Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lexer & Parser: Adding support for decorator #558

Merged
merged 1 commit into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions projects/compiler/src/lexer.abra
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub enum TokenKind {
Match
Type
Enum
Decorator
Return(subsequentNewline: Bool)
Readonly
Import
Expand Down Expand Up @@ -111,6 +112,7 @@ pub enum TokenKind {
TokenKind.Match => "match"
TokenKind.Type => "type"
TokenKind.Enum => "enum"
TokenKind.Decorator => "decorator"
TokenKind.Return => "return"
TokenKind.Readonly => "readonly"
TokenKind.Import => "import"
Expand Down Expand Up @@ -664,6 +666,7 @@ pub type Lexer {
"match" => TokenKind.Match
"type" => TokenKind.Type
"enum" => TokenKind.Enum
"decorator" => TokenKind.Decorator
"return" => {
val sawNewline = self._skipWhitespace()
TokenKind.Return(sawNewline)
Expand Down
11 changes: 7 additions & 4 deletions projects/compiler/src/parser.abra
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ type TypeField {
pub type TypeDeclarationNode {
pub decorators: DecoratorNode[]
pub pubToken: Token?
pub isDecorator: Bool
pub name: Label
pub typeParams: Label[]
pub fields: TypeField[]
Expand Down Expand Up @@ -496,7 +497,7 @@ pub type Parser {
try self._parseDecorator()

val nextToken = try self._expectPeek()
val expected = [TokenKind.Val, TokenKind.Var, TokenKind.Func, TokenKind.Type, TokenKind.Enum, TokenKind.At, TokenKind.Pub]
val expected = [TokenKind.Val, TokenKind.Var, TokenKind.Func, TokenKind.Type, TokenKind.Enum, TokenKind.Decorator, TokenKind.At, TokenKind.Pub]
if !expected.contains(nextToken.kind) {
return Err(ParseError(position: nextToken.position, kind: ParseErrorKind.ExpectedToken(expected, nextToken.kind)))
}
Expand All @@ -508,7 +509,7 @@ pub type Parser {
self._advance() // consume 'pub' token

val nextToken = try self._expectPeek()
val expected = [TokenKind.Val, TokenKind.Var, TokenKind.Func, TokenKind.Type, TokenKind.Enum]
val expected = [TokenKind.Val, TokenKind.Var, TokenKind.Func, TokenKind.Type, TokenKind.Enum, TokenKind.Decorator]
if !expected.contains(nextToken.kind) {
return Err(ParseError(position: nextToken.position, kind: ParseErrorKind.ExpectedToken(expected, nextToken.kind)))
}
Expand All @@ -518,8 +519,9 @@ pub type Parser {
TokenKind.Val => self._parseBindingDeclaration(mutable: false)
TokenKind.Var => self._parseBindingDeclaration(mutable: true)
TokenKind.Func => self._parseFunctionDeclaration()
TokenKind.Type => self._parseTypeDeclaration()
TokenKind.Type => self._parseTypeDeclaration(isDecorator: false)
TokenKind.Enum => self._parseEnumDeclaration()
TokenKind.Decorator => self._parseTypeDeclaration(isDecorator: true)
TokenKind.While => self._parseWhileLoop()
TokenKind.For => self._parseForLoop()
TokenKind.Break => self._parseBreak()
Expand Down Expand Up @@ -703,7 +705,7 @@ pub type Parser {
Ok(AstNode(token: token, kind: AstNodeKind.FunctionDeclaration(node)))
}

func _parseTypeDeclaration(self): Result<AstNode, ParseError> {
func _parseTypeDeclaration(self, isDecorator: Bool): Result<AstNode, ParseError> {
val decorators = self._seenDecorators
self._seenDecorators = []

Expand Down Expand Up @@ -762,6 +764,7 @@ pub type Parser {
val node = TypeDeclarationNode(
decorators: decorators,
pubToken: pubToken,
isDecorator: isDecorator,
name: typeName,
typeParams: typeParams,
fields: fields,
Expand Down
14 changes: 9 additions & 5 deletions projects/compiler/src/test_utils.abra
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func printTokenKindAsJson(kind: TokenKind, indentLevelStart: Int, currentIndentL
TokenKind.Match => println("$fieldsIndent\"name\": \"Match\"")
TokenKind.Type => println("$fieldsIndent\"name\": \"Type\"")
TokenKind.Enum => println("$fieldsIndent\"name\": \"Enum\"")
TokenKind.Decorator => println("$fieldsIndent\"name\": \"Decorator\"")
TokenKind.Return => println("$fieldsIndent\"name\": \"Return\"")
TokenKind.Readonly => println("$fieldsIndent\"name\": \"Readonly\"")
TokenKind.Import => println("$fieldsIndent\"name\": \"Import\"")
Expand Down Expand Up @@ -263,21 +264,21 @@ func printTypeIdentifierAsJson(typeIdentifier: TypeIdentifier, indentLevelStart:
print("$endIndent}")
}

func printDecoratorNodeAsJson(decorator: DecoratorNode, indentLevelStart: Int, currentIndentLevel: Int) {
func printDecoratorNodeAsJson(dec: DecoratorNode, indentLevelStart: Int, currentIndentLevel: Int) {
val startIndent = " ".repeat(indentLevelStart)
val fieldsIndent = " ".repeat(currentIndentLevel + 1)
val endIndent = " ".repeat(currentIndentLevel)

print("$startIndent{\n$fieldsIndent\"name\": ")
printLabelAsJson(decorator.name)
printLabelAsJson(dec.name)

if decorator.arguments.isEmpty() {
if dec.arguments.isEmpty() {
println(",\n$fieldsIndent\"arguments\": []")
} else {
println(",\n$fieldsIndent\"arguments\": [")
val pathIndent = " ".repeat(currentIndentLevel + 2)
val pathsIndent = " ".repeat(currentIndentLevel + 3)
for arg, idx in decorator.arguments {
for arg, idx in dec.arguments {
print("$pathIndent{\n$pathsIndent\"label\": ")
if arg.label |label| {
printLabelAsJson(label)
Expand All @@ -287,7 +288,7 @@ func printDecoratorNodeAsJson(decorator: DecoratorNode, indentLevelStart: Int, c
}
print("$pathsIndent\"value\": ")
printAstNodeAsJson(arg.value, 0, currentIndentLevel + 3)
val comma = if idx != decorator.arguments.length - 1 "," else ""
val comma = if idx != dec.arguments.length - 1 "," else ""
println("\n$pathIndent}$comma")
}
println("$fieldsIndent]")
Expand Down Expand Up @@ -930,6 +931,9 @@ func printAstNodeKindAsJson(kind: AstNodeKind, indentLevelStart: Int, currentInd
} else {
println("$fieldsIndent\"pubToken\": null,")
}
if node.isDecorator {
println("$fieldsIndent\"isDecorator\": true,")
}
print("$fieldsIndent\"typeName\": ")
printLabelAsJson(node.name)

Expand Down
2 changes: 1 addition & 1 deletion projects/compiler/test/lexer/keywords.abra
Original file line number Diff line number Diff line change
@@ -1 +1 @@
true false val var if else func while break for in type enum self match readonly import from as try continue pub
true false val var if else func while break for in type enum self match readonly import from as try continue pub decorator
6 changes: 6 additions & 0 deletions projects/compiler/test/lexer/keywords.out.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,11 @@
"kind": {
"name": "Pub"
}
},
{
"position": [1, 114],
"kind": {
"name": "Decorator"
}
}
]
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Error at %FILE_NAME%:2:1
Unexpected token 'identifier', expected one of 'val', 'var', 'func', 'type', 'enum', '@', 'pub':
Unexpected token 'identifier', expected one of 'val', 'var', 'func', 'type', 'enum', 'decorator', '@', 'pub':
| abc()
^
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Error at %FILE_NAME%:2:1
Unexpected token 'while', expected one of 'val', 'var', 'func', 'type', 'enum', '@', 'pub':
Unexpected token 'while', expected one of 'val', 'var', 'func', 'type', 'enum', 'decorator', '@', 'pub':
| while true { }
^
60 changes: 60 additions & 0 deletions projects/compiler/test/parser/decoratordecl.abra
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
decorator Foo { }
decorator Foo<A, B> { }
decorator Foo<A> { a: A, b: Bool }
decorator Foo {
a: Int
b: Bool = true
}

decorator FooBar123 {
func foo(self, b: String) {}
}

decorator FooBar123 {
a: Int

func foo(self, b: String): X<Y> = 123
func bar(self, b = true) { self.a + b }
}

decorator FooBar123 {
a: Int

func foo(self, b: String): X<Y> = 123

@Foo
func bar(self, b = true) { self.a + b }
}

decorator Outer {
a: Int

func foo(self, b: String): X<Y> { 123 }

type InnerType {
a: Int

func bar(self, b = true) { self.a + b }
}

enum InnerEnum {
A(a: Int)

func bar(self, b = true) { self.a + b }
}
}

@Bar("a")
pub decorator Outer {
a: Int

@Bar("b")
func foo(self, b: String): X<Y> { 123 }

@Bar("c")
type Inner { a: Int }
}

pub decorator Outer2 {
pub a: Int
}
Loading