diff options
author | Manuel Palenzuela Merino <manuel.palenzuela@datadoghq.com> | 2025-01-18 20:19:01 +0100 |
---|---|---|
committer | Manuel Palenzuela Merino <manuel.palenzuela@datadoghq.com> | 2025-01-18 20:20:07 +0100 |
commit | 731437da05b96ced14b5b063051cfd99c81e452a (patch) | |
tree | 95c7dfd47bdd59d4ed31db07ffd8fb7b15b7bc92 | |
parent | Evaluator: create and drop scopes when entering/leaving functions (diff) | |
download | interpreter-731437da05b96ced14b5b063051cfd99c81e452a.tar.gz interpreter-731437da05b96ced14b5b063051cfd99c81e452a.tar.bz2 interpreter-731437da05b96ced14b5b063051cfd99c81e452a.zip |
Evaluator: implement support for function arguments
-rw-r--r-- | examples/6.src | 10 | ||||
-rw-r--r-- | src/evaluator.zig | 57 | ||||
-rw-r--r-- | src/parser.zig | 72 |
3 files changed, 75 insertions, 64 deletions
diff --git a/examples/6.src b/examples/6.src index 573ee6a..1d8b8d8 100644 --- a/examples/6.src +++ b/examples/6.src @@ -1,8 +1,10 @@ -let print_input = (input) => { - print(input); - return input; +let print_input = (input_a, input_b) => { + print(input_a); + print(input_b); + return input_a + input_b; }; let main = () => { - return print_input(7); + let i = print_input(1,4); + return print_input(7, 2) + i; }; diff --git a/src/evaluator.zig b/src/evaluator.zig index 95aecd1..f4a56d8 100644 --- a/src/evaluator.zig +++ b/src/evaluator.zig @@ -41,7 +41,7 @@ pub const Evaluator = struct { } const main = self.environment.get_variable("main") orelse return EvaluatorError.EvaluationError; - return try self.evaluate_function_definition(main.FUNCTION_DEFINITION); + return try self.evaluate_function_definition(main.FUNCTION_DEFINITION, &[_]*parser.Node{}); } fn evaluate_statement(self: *Evaluator, statement: *parser.Node) EvaluatorError!void { @@ -71,21 +71,11 @@ pub const Evaluator = struct { return EvaluatorError.EvaluationError; } - var variable: *Variable = undefined; - if (assignment_statement.expression.* == parser.Node.FUNCTION_DEFINITION) { - variable = try self.create_variable(.{ - .FUNCTION_DEFINITION = assignment_statement.expression, - }); - } else { - const val = try self.get_expression_value(assignment_statement.expression); - variable = try self.create_variable(.{ - .NUMBER = val, - }); - } + const variable = try self.create_variable(assignment_statement.expression); try self.environment.add_variable(assignment_statement.name, variable); } - fn evaluate_function_call_statement(self: *Evaluator, node: *parser.Node) !i64 { + fn evaluate_function_call_statement(self: *Evaluator, node: *parser.Node) EvaluatorError!i64 { errdefer std.debug.print("Error evaluating function call statement\n", .{}); std.debug.assert(node.* == parser.Node.FUNCTION_CALL_STATEMENT); @@ -98,9 +88,9 @@ pub const Evaluator = struct { return 0; } - const val = self.environment.get_variable(function_call_statement.name) orelse return EvaluatorError.EvaluationError; + const function_definition = self.environment.get_variable(function_call_statement.name) orelse return EvaluatorError.EvaluationError; - return self.evaluate_function_definition(val.FUNCTION_DEFINITION); //TODO: Pass arguments to this + return self.evaluate_function_definition(function_definition.FUNCTION_DEFINITION, function_call_statement.arguments); } fn evaluate_return_statement(self: *Evaluator, return_statement: *parser.Node) !i64 { @@ -136,17 +126,13 @@ pub const Evaluator = struct { } }, // I don't like having 2 places where we evaluate functions - .FUNCTION_CALL_STATEMENT => |x| { - const func = self.environment.get_variable(x.name) orelse return EvaluatorError.EvaluationError; - - return try self.evaluate_function_definition(func.FUNCTION_DEFINITION); - }, + .FUNCTION_CALL_STATEMENT => return try self.evaluate_function_call_statement(node), else => unreachable, } } - fn evaluate_function_definition(self: *Evaluator, node: *parser.Node) EvaluatorError!i64 { + fn evaluate_function_definition(self: *Evaluator, node: *parser.Node, arguments: []*parser.Node) EvaluatorError!i64 { errdefer std.debug.print("Error evaluating function definition\n", .{}); std.debug.assert(node.* == parser.Node.FUNCTION_DEFINITION); @@ -155,7 +141,17 @@ pub const Evaluator = struct { try self.environment.create_scope(); defer self.environment.drop_scope(); + std.debug.assert(function_definition.parameters.len == arguments.len); + var i: usize = 0; + while (i < arguments.len) : (i += 1) { + const parameter = function_definition.parameters[i]; + const argument = arguments[i]; + + try self.environment.add_variable(parameter.PRIMARY_EXPRESSION.IDENTIFIER.name, try self.create_variable(argument)); + } + + i = 0; while (i < function_definition.statements.len - 1) { const stmt = function_definition.statements[i]; try self.evaluate_statement(stmt); @@ -166,9 +162,19 @@ pub const Evaluator = struct { return try self.evaluate_return_statement(return_stmt); } - fn create_variable(self: *Evaluator, variable_value: Variable) !*Variable { + fn create_variable(self: *Evaluator, node: *parser.Node) !*Variable { const variable = try self.allocator.create(Variable); - variable.* = variable_value; + if (node.* == parser.Node.FUNCTION_DEFINITION) { + variable.* = .{ + .FUNCTION_DEFINITION = node, + }; + } else { + const val = try self.get_expression_value(node); + variable.* = .{ + .NUMBER = val, + }; + } + return variable; } }; @@ -215,8 +221,9 @@ const Environment = struct { } fn get_variable(self: *Environment, name: []const u8) ?*Variable { - var i = self.scope_stack.items.len - 1; - while (i >= 0) : (i -= 1) { + var i = self.scope_stack.items.len; + while (i > 0) { + i -= 1; const scope = self.scope_stack.items[i]; if (scope.variables.get(name)) |v| return v; } diff --git a/src/parser.zig b/src/parser.zig index e9a2908..cf7f7d3 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -108,7 +108,7 @@ pub const Parser = struct { const statement = self.accept_parse(parse_function_call_statement) orelse try self.parse_assignment_statement(); - _ = try self.accept_token(tokenizer.TokenType.SEMICOLON); + _ = try self.require_token(tokenizer.TokenType.SEMICOLON); return self.create_node(.{ .STATEMENT = .{ @@ -122,13 +122,13 @@ pub const Parser = struct { errdefer if (!self.try_context) std.debug.print("Error parsing assignment statement\n", .{}); var is_declaration: bool = false; - if (self.match_token(.LET) != null) { + if (self.accept_token(.LET) != null) { is_declaration = true; } - const identifier = try self.accept_token(tokenizer.TokenType.IDENTIFIER); + const identifier = try self.require_token(tokenizer.TokenType.IDENTIFIER); - _ = try self.accept_token(tokenizer.TokenType.EQUALS); + _ = try self.require_token(tokenizer.TokenType.EQUALS); const expression = try self.parse_expression(); @@ -145,13 +145,13 @@ pub const Parser = struct { fn parse_function_call_statement(self: *Parser) ParserError!*Node { errdefer if (!self.try_context) std.debug.print("Error parsing function call statement\n", .{}); - const identifier = try self.accept_token(tokenizer.TokenType.IDENTIFIER); + const identifier = try self.require_token(tokenizer.TokenType.IDENTIFIER); - _ = try self.accept_token(tokenizer.TokenType.LPAREN); + _ = try self.require_token(tokenizer.TokenType.LPAREN); const arguments = try self.parse_function_arguments(); - _ = try self.accept_token(tokenizer.TokenType.RPAREN); + _ = try self.require_token(tokenizer.TokenType.RPAREN); return self.create_node(.{ .FUNCTION_CALL_STATEMENT = .{ .name = try self.allocator.dupe(u8, identifier.IDENTIFIER), @@ -162,18 +162,19 @@ pub const Parser = struct { // FunctionArguments ::= Expression ("," Expression)* fn parse_function_arguments(self: *Parser) ParserError![]*Node { errdefer if (!self.try_context) std.debug.print("Error parsing function arguments\n", .{}); - var nodeList = std.ArrayList(*Node).init(self.allocator); + var node_list = std.ArrayList(*Node).init(self.allocator); var first = true; - while (self.accept_parse(parse_expression)) |a| { + while (true) { if (!first) { - _ = try self.accept_token(tokenizer.TokenType.COMMA); + _ = self.accept_token(tokenizer.TokenType.COMMA); } first = false; - try nodeList.append(a); + const expr = self.accept_parse(parse_expression) orelse return node_list.items; + try node_list.append(expr); } - return nodeList.items; + return node_list.items; } // Expression ::= AdditiveExpression | FunctionDefinition @@ -191,7 +192,7 @@ pub const Parser = struct { const lhs = try self.parse_primary_expression(); - if (self.match_token(tokenizer.TokenType.PLUS) != null) { + if (self.accept_token(tokenizer.TokenType.PLUS) != null) { const rhs = try self.parse_additive_expression(); return self.create_node(.{ .ADDITIVE_EXPRESSION = .{ .lhs = lhs, @@ -233,14 +234,14 @@ pub const Parser = struct { fn parse_function_definition(self: *Parser) ParserError!*Node { errdefer if (!self.try_context) std.debug.print("Error parsing function definition\n", .{}); - _ = try self.accept_token(tokenizer.TokenType.LPAREN); + _ = try self.require_token(tokenizer.TokenType.LPAREN); const parameters = try self.parse_function_parameters(); - _ = try self.accept_token(tokenizer.TokenType.RPAREN); + _ = try self.require_token(tokenizer.TokenType.RPAREN); - _ = try self.accept_token(tokenizer.TokenType.ARROW); - _ = try self.accept_token(tokenizer.TokenType.LBRACE); + _ = try self.require_token(tokenizer.TokenType.ARROW); + _ = try self.require_token(tokenizer.TokenType.LBRACE); var nodes = std.ArrayList(*Node).init(self.allocator); while (self.accept_parse(parse_statement)) |expression| { @@ -249,7 +250,7 @@ pub const Parser = struct { try nodes.append(try self.parse_return_statement()); - _ = try self.accept_token(tokenizer.TokenType.RBRACE); + _ = try self.require_token(tokenizer.TokenType.RBRACE); return self.create_node(.{ .FUNCTION_DEFINITION = .{ .statements = nodes.items, @@ -260,35 +261,36 @@ pub const Parser = struct { // FunctionParameters ::= IDENTIFIER ("," IDENTIFIER)* fn parse_function_parameters(self: *Parser) ParserError![]*Node { errdefer if (!self.try_context) std.debug.print("Error parsing function parameters\n", .{}); - var nodeList = std.ArrayList(*Node).init(self.allocator); + var node_list = std.ArrayList(*Node).init(self.allocator); var first = true; - while (self.match_token(tokenizer.TokenType.IDENTIFIER)) |a| { + while (true) { if (!first) { - _ = try self.accept_token(tokenizer.TokenType.COMMA); + _ = self.accept_token(tokenizer.TokenType.COMMA); } first = false; + const ident = self.accept_token(tokenizer.TokenType.IDENTIFIER) orelse return node_list.items; - try nodeList.append(try self.create_node(.{ + try node_list.append(try self.create_node(.{ .PRIMARY_EXPRESSION = .{ .IDENTIFIER = .{ - .name = try self.allocator.dupe(u8, a.IDENTIFIER), + .name = try self.allocator.dupe(u8, ident.IDENTIFIER), }, }, })); } - return nodeList.items; + return node_list.items; } // ReturnStatement :== RETURN Expression fn parse_return_statement(self: *Parser) ParserError!*Node { errdefer if (!self.try_context) std.debug.print("Error parsing return statement\n", .{}); - _ = try self.accept_token(tokenizer.TokenType.RETURN); + _ = try self.require_token(tokenizer.TokenType.RETURN); const expression = try self.parse_expression(); - _ = try self.accept_token(tokenizer.TokenType.SEMICOLON); //TODO: I dont like this + _ = try self.require_token(tokenizer.TokenType.SEMICOLON); //TODO: I dont like this return self.create_node(.{ .RETURN_STATEMENT = .{ @@ -308,7 +310,15 @@ pub const Parser = struct { return node; } - fn accept_token(self: *Parser, expected_token: tokenizer.TokenType) ParserError!tokenizer.Token { + fn accept_token(self: *Parser, token: tokenizer.TokenType) ?tokenizer.Token { + const curr_token = self.peek_token() orelse return null; + if (curr_token == token) { + return self.consume_token(); + } + return null; + } + + fn require_token(self: *Parser, expected_token: tokenizer.TokenType) ParserError!tokenizer.Token { errdefer if (!self.try_context) std.debug.print("Error accepting token: {any}\n", .{expected_token}); const token = self.peek_token() orelse return ParserError.ParsingError; @@ -320,14 +330,6 @@ pub const Parser = struct { return self.consume_token() orelse unreachable; } - fn match_token(self: *Parser, token: tokenizer.TokenType) ?tokenizer.Token { - const curr_token = self.peek_token() orelse return null; - if (curr_token == token) { - return self.consume_token(); - } - return null; - } - fn consume_token(self: *Parser) ?tokenizer.Token { if (self.offset >= self.tokens.len) return null; |