Skip to content

Commit

Permalink
Added class definition and function calls to new parser.
Browse files Browse the repository at this point in the history
Fixed bugs in compiler.
  • Loading branch information
hg0428 committed Apr 18, 2024
1 parent 08e0389 commit 42a9745
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 43 deletions.
2 changes: 1 addition & 1 deletion Aardvark Compiler/Data.adk
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ let Keywords = set{
'static',
'include',
'await',
'async',
'pause-until',
'yield',
'let', # Statements
'as',
Expand Down
164 changes: 122 additions & 42 deletions Aardvark Compiler/new-Parser.adk
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ static class Parser as this {
this.eat()
ast_node = {
type: 'VariableAccess',
value: current_token.value,
name: current_token.value,
position: current_token.position
}
}
Expand Down Expand Up @@ -257,6 +257,8 @@ static class Parser as this {
}

while ast_node?false {
if eat_line_breaks
this.eat_line_breaks()
# side-by-side multiply (i.e. 2x)
if this.compare("Identifier") & this.peek().start.column == ast_node.position.end.column + 1 {
let token = this.eat()
Expand Down Expand Up @@ -352,8 +354,20 @@ static class Parser as this {
left = (CHECK(this.parse_primary(require, exclude, is_deconstruction))) if level < 0 else (CHECK(this.parse_expression(level - 1, require, exclude, eat_line_breaks, is_deconstruction)));
return left
}
function parse_statement(require=false, is_deconstruction=false) {
function parse_statement(require=false, is_deconstruction=false, mode="normal") {
this.eat_line_breaks()
if mode == "class_scope" {
if this.compare('Delimiter', "$")
return this.parse_function_definition("class_scope")
else if this.compare("Keyword", "function")
return this.parse_function_definition("class_scope")
else if this.compare("Keyword", "class")
return this.parse_class_definition()
else if this.compare("Keyword", "let")
return this.parse_assignment()
else
return this.Throw("SyntaxError", "Not valid in class scope", position=this.peek().position)
}
if this.compare('Keyword') match this.peek().value {
case 'return'
return this.parse_return_statement()
Expand Down Expand Up @@ -381,9 +395,6 @@ static class Parser as this {
}
}
function parse this.parse_program()
function parse_function_call(ast_node) {

}
function parse_property_access(ast_node) {
let dot = this.eat()
this.eat_line_breaks()
Expand Down Expand Up @@ -458,6 +469,7 @@ static class Parser as this {
value = CHECK(this.parse_expression(require=true, eat_line_breaks=true))
} else # Otherwise, the value is a variable access to the key value
value = key.variable
# Should we allow `?` in the key to only be added if it has a value?
pairs.add({
type: "KeyValuePair",
key: key.variable,
Expand Down Expand Up @@ -653,7 +665,8 @@ static class Parser as this {
function parse_function_definition(mode="normal", found_static=false) {
let is_static = found_static
let is_private = false
let first_keyword
let parameters = []
let first_keyword, label, name, return_type, body, parameter_expansion, function_keyword
if this.compare("Keyword", "static") {
first_keyword = this.eat()
is_static = true
Expand All @@ -670,18 +683,26 @@ static class Parser as this {
first_keyword = keyword
is_static = true
}
let function_keyword
if mode == "class_scope" & this.compare("Delimiter", "$")
function_keyword = this.eat()
else if mode != "extending"
# the function keyword is required unless we're in a class scope or extending
if !["extending", "class_scope"].contains(mode)
function_keyword = CHECK(this.eat("Keyword", "function"))
else # Otherwise it's optional
function_keyword = this.maybe_eat("Keyword", "function")

if mode == "class_scope" & this.compare("Delimiter", "$") {
function_keyword = this.eat()
let token = CHECK(this.eat("Identifier"))
name = {
type: "VariableAccess",
name: '$' + token.value,
position: token.position
}
}

if (!first_keyword? & function_keyword?)
first_keyword = function_keyword

let name, type, body
let parameters = []
let parameter_expansion
if this.compare("Identifier") {
if !(name?) & this.compare("Identifier") {
let token = this.eat()
name = {
type: "VariableAccess",
Expand Down Expand Up @@ -712,14 +733,18 @@ static class Parser as this {
if this.compare("Delimiter", ",") this.eat()
let closing_parenthesis = this.eat("Delimiter", ")")
}
# Parse labels
if this.maybe_eat("Keyword", "as")
label = CHECK(this.eat("Identifier"))

# Parse return type
if this.compare("Operator", "->") {
let operator = this.eat()
if (!first_keyword?)
first_keyword = operator
type = CHECK(this.parse_primary(require=true))
if !this.is_valid_type(type) {
return this.Throw("SyntaxError", "Invalid syntax.", position=type.position, here_message= $"Expected type.")
return_type = CHECK(this.parse_primary(require=true))
if !this.is_valid_type(return_type) {
return this.Throw("SyntaxError", "Invalid syntax.", position=return_type.position, here_message= $"Expected type.")
}
}
body = CHECK(this.parse_statement_or_scope_body())
Expand All @@ -728,18 +753,19 @@ static class Parser as this {
name: name?,
parameters,
parameter_expansion: parameter_expansion?,
type: type?,
body: body,
is_static: is_static,
is_private: is_private,
return_type: return_type?,
body,
label: label?,
is_static,
is_private,
position: {
start: first_keyword.position.start if first_keyword? else body.position.start,
end: body.position.end
}

}
}
function parse_statement_or_scope_body() {
function parse_statement_or_scope_body(mode="normal") {
let body
if this.compare("Delimiter", "{") {
let previous_store_errors_state = this.store_errors
Expand All @@ -751,7 +777,7 @@ static class Parser as this {
this.pos = previous_position
this.error_stack.(-1).Throw()
this.error_stack = this.error_stack.slice(0, -1)
extension = CHECK(this.parse_scope_body())
extension = CHECK(this.parse_scope_body(true, mode))
}
} else {
body = CHECK(this.parse_statement(require=true))
Expand Down Expand Up @@ -785,7 +811,7 @@ static class Parser as this {
}
}
function parse_class_definition() {
let name, body
let name, label
let is_static = false
let is_private = false
if this.maybe_eat("Keyword", "static")
Expand All @@ -795,7 +821,29 @@ static class Parser as this {
if this.maybe_eat("Keyword", "static")
is_static = true
let class_keyword = CHECK(this.eat("Keyword", "class"))
name = this.maybe_eat("Identifier")
if this.compare("Identifier") {
let token = this.eat()
name = {
type: "VariableAccess",
name: token.value,
position: token.position
}
}
if this.maybe_eat("Keyword", "as")
label = CHECK(this.eat("Identifier"))
let body = CHECK(this.parse_scope_body(true, "class_scope"))
return {
type: "ClassDefinition",
name: name?,
body,
label: label?,
is_static,
is_private,
position: {
start: class_keyword.position.start,
end: body.position.end
}
}
# TODO
}
function parse_extending_statement() {
Expand Down Expand Up @@ -826,7 +874,7 @@ static class Parser as this {
let identifier = this.eat()
object = {
type: "VariableAccess",
value: identifier.value,
name: identifier.value,
position: identifier.position
}
if this.compare('Delimiter', '.') & this.peek().start.column == ast_node.position.end.column + 1 {
Expand Down Expand Up @@ -880,12 +928,43 @@ static class Parser as this {
}
}
}
function parse_function_call() {
function parse_function_call(ast_node) {
let opening_parenthesis = CHECK(this.eat("Delimiter", "("))
let parameters = []
let arguments = []
let keyword_arguments = []
# let
# TODO
while !this.compare("Delimiter", ")") {
if arguments.length > 0 | keyword_arguments.length > 0
CHECK(this.eat("Delimiter", ","))
let value = CHECK(this.parse_expression(exclude=['Reassignment']))
if this.maybe_eat("Operator", "=") {
let key = value
if !this.is_valid_variable_definition(key)
return this.Throw("SyntaxError", $"Invalid syntax.", position=first.position, here_message= $"Expected parameter name.")
let value = CHECK(this.parse_expression())
keyword_arguments.add({
type: "KeyValuePair",
key: key,
value,
position: {
start: key.position.start,
end: value.position.end
}
})
} else
arguments.add(value)
}
if this.compare("Delimiter", ",") this.eat()
let closing_parenthesis = this.eat("Delimiter", ")")
return {
type: "FunctionCall",
object: ast_node,
arguments,
keyword_arguments,
position: {
start: opening_parenthesis.position.start,
end: closing_parenthesis.position.end
}
}
}
function parse_reassignment(ast_node) {
if !this.is_valid_variable_definition(ast_node) {
Expand Down Expand Up @@ -916,7 +995,7 @@ static class Parser as this {
}
}
}
function parse_scope_body(enclosing_curly_braces=true) {
function parse_scope_body(enclosing_curly_braces=true, mode="normal") {
let beginning_token = this.peek()
let opening_curly_brace, closing_curly_brace
if enclosing_curly_braces
Expand All @@ -928,10 +1007,10 @@ static class Parser as this {
if this.is_EOF() | (enclosing_curly_braces & this.compare("Delimiter", "}")) break;
let statement
while !(statement? | this.is_EOF()) {
statement = this.parse_statement(true)
statement = this.parse_statement(true, false, mode)
if type_of(statement) == ADK_Error {
while true {
CHECK(this.eat(keep_track=true))
this.eat(keep_track=true)
if this.compare("LineBreak") | this.is_EOF() break
}
this.eat_line_breaks()
Expand Down Expand Up @@ -986,6 +1065,7 @@ let parser_tests = {
empty_scope: "\n\n",
property_access: "x.y.z",
function_definition: "private static function x(Number x, y, {Number a, b, c:d}, [String a, b, c], this.y, ...args) -> Number {\n\n}",
simple_function_definition: "function x {}",
if_else_statement: "if condition {\n\n x\n\n\n } else if condition { \ny\n } \n\nelse { z }",
object_type: "let {Number x, String y} x",
optional_type: "let String? x",
Expand All @@ -1000,15 +1080,19 @@ let parser_tests = {
operator_extending: "extending +(Number x, String y) -> String {}",
reassignment: "x = y = z = 5",
template_string: "$'Hello, {name}!'",
multiline_property_access: "x.\n y.\n z"
multiline_property_access: "x.\n y.\n z",
class_definition: "private static class x as this {\n\n$constructor() {}\n\nfunction $constructor() {}\nfunction method() {}\nlet x = 5\n let y}",
empty_class: "class {\n\n\n}",
function_call: "x(x, y, ...z, a=b, c=d, {e, f:g})"
}
let error_tests = {
multiple_decimal_points: "5.4.3",
multiple_underscore: "5_4_3",
assign_value: "let 5 = x",
else_without_if: "else { x }",
anonymous_function_extension: "extending function {}",
many_errors: "5.4.3\n5_4_3\nlet 5 = x\n\nelse {}\n\n"
many_errors: "5.4.3\n5_4_3\nlet 5 = x\n\nelse {}\n\n",
expression_in_class_scope: "class x {\nstdout.log(5 * 5)\n}"
}
function test(String code) {
let lexer = Lexer(false, false)
Expand All @@ -1028,21 +1112,17 @@ function test(String code) {
return false
return AST
}
# stdout.log(test(parser_tests.empty_object))
# let ultimate = ""

stdout.log(test(error_tests.expression_in_class_scope))
for test_name:code in parser_tests {
# ultimate += code + "\n"
let passed = test(code)
# stdout.log(passed)
if passed
stdout.log("PASSED:", test_name)
else
stdout.log("FAILED:", test_name)
}
# let passed = test(ultimate)
# if passed
# stdout.log("PASSED: ULTIMATE")
# else
# stdout.log("FAILED: ULTIMATE")

# for test_name:code in error_tests {
# test(code)
Expand Down

0 comments on commit 42a9745

Please sign in to comment.