summary refs log tree commit diff
diff options
context:
space:
mode:
authorManuel Palenzuela Merino <manuel.palenzuela@datadoghq.com>2025-01-18 20:19:01 +0100
committerManuel Palenzuela Merino <manuel.palenzuela@datadoghq.com>2025-01-18 20:20:07 +0100
commit731437da05b96ced14b5b063051cfd99c81e452a (patch)
tree95c7dfd47bdd59d4ed31db07ffd8fb7b15b7bc92
parentEvaluator: create and drop scopes when entering/leaving functions (diff)
downloadinterpreter-731437da05b96ced14b5b063051cfd99c81e452a.tar.gz
interpreter-731437da05b96ced14b5b063051cfd99c81e452a.tar.bz2
interpreter-731437da05b96ced14b5b063051cfd99c81e452a.zip
Evaluator: implement support for function arguments
-rw-r--r--examples/6.src10
-rw-r--r--src/evaluator.zig57
-rw-r--r--src/parser.zig72
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;