summary refs log tree commit diff
diff options
context:
space:
mode:
authorBaitinq <manuelpalenzuelamerino@gmail.com>2025-01-12 17:43:50 +0100
committerBaitinq <manuelpalenzuelamerino@gmail.com>2025-01-12 17:47:51 +0100
commitb3344abfba762b22af1ac80cdf2801ff6eaf3696 (patch)
tree355cf53c0efcd5853b0e4c0755670b42453dec23
parentEvaluator: Fix bug when variables are initialized with the same identifier as... (diff)
downloadinterpreter-b3344abfba762b22af1ac80cdf2801ff6eaf3696.tar.gz
interpreter-b3344abfba762b22af1ac80cdf2801ff6eaf3696.tar.bz2
interpreter-b3344abfba762b22af1ac80cdf2801ff6eaf3696.zip
Add support for sum operator
-rw-r--r--examples/3.src6
-rw-r--r--grammar.ebnf2
-rw-r--r--src/evaluator.zig6
-rw-r--r--src/parser.zig71
-rw-r--r--src/tokenizer.zig67
-rw-r--r--todo/0.src5
6 files changed, 99 insertions, 58 deletions
diff --git a/examples/3.src b/examples/3.src
index 736ab61..042edea 100644
--- a/examples/3.src
+++ b/examples/3.src
@@ -1,5 +1,3 @@
-let ten = 10;
+let sixteen = 10 + 2 + 4;
 
-let twelve = ten + 2;
-
-print(twelve);
+print(sixteen);
diff --git a/grammar.ebnf b/grammar.ebnf
index 1423e74..1ac139e 100644
--- a/grammar.ebnf
+++ b/grammar.ebnf
@@ -6,4 +6,4 @@ VariableStatement ::= ("let" IDENTIFIER | IDENTIFIER) EQUALS Expression
 
 PrintStatement :== PRINT LPAREN Expression RPAREN
 
-Expression :== NUMBER | IDENTIFIER
+Expression :== NUMBER | IDENTIFIER | Expression + Expression
diff --git a/src/evaluator.zig b/src/evaluator.zig
index f8bb052..ef83463 100644
--- a/src/evaluator.zig
+++ b/src/evaluator.zig
@@ -93,6 +93,12 @@ pub const Evaluator = struct {
 
                 return try self.get_expression_value(expression orelse return EvaluatorError.EvaluationError);
             },
+            .BINARY => |operation| {
+                //TODO: For now, this just represents sum
+                const lhs = try self.get_expression_value(operation.lhs);
+                const rhs = try self.get_expression_value(operation.rhs);
+                return lhs + rhs;
+            },
         };
     }
 };
diff --git a/src/parser.zig b/src/parser.zig
index 9e27d65..e8fcd81 100644
--- a/src/parser.zig
+++ b/src/parser.zig
@@ -14,24 +14,35 @@ const NodeType = enum {
     EXPRESSION,
 };
 
-pub const Node = union(NodeType) { PROGRAM: struct {
-    statements: []*Node,
-}, STATEMENT: struct {
-    statement: *Node,
-}, VARIABLE_STATEMENT: struct {
-    is_declaration: bool,
-    name: []const u8,
-    expression: *Node,
-}, PRINT_STATEMENT: struct {
-    expression: *Node,
-}, EXPRESSION: union(enum) {
-    NUMBER: struct {
-        value: i64,
+pub const Node = union(NodeType) {
+    PROGRAM: struct {
+        statements: []*Node,
     },
-    IDENTIFIER: struct {
+    STATEMENT: struct {
+        statement: *Node,
+    },
+    VARIABLE_STATEMENT: struct {
+        is_declaration: bool,
         name: []const u8,
+        expression: *Node,
+    },
+    PRINT_STATEMENT: struct {
+        expression: *Node,
+    },
+    EXPRESSION: union(enum) {
+        NUMBER: struct {
+            value: i64,
+        },
+        IDENTIFIER: struct {
+            name: []const u8,
+        },
+        BINARY: struct {
+            //TODO: For now, this just represents sum
+            lhs: *Node,
+            rhs: *Node,
+        },
     },
-} };
+};
 
 pub const Parser = struct {
     tokens: []tokenizer.Token,
@@ -88,12 +99,10 @@ pub const Parser = struct {
     // VariableStatement ::= ("let" IDENTIFIER | IDENTIFIER) EQUALS Expression
     fn parse_variable_statement(self: *Parser) ParserError!*Node {
         errdefer std.debug.print("Error parsing variable statement\n", .{});
-        const token = self.peek_token() orelse return ParserError.ParsingError;
 
         var is_declaration: bool = false;
-        if (token == .LET) {
+        if (self.match_token(.LET)) {
             is_declaration = true;
-            _ = self.consume_token() orelse return ParserError.ParsingError;
         }
 
         const identifier = try self.accept_token(tokenizer.TokenType.IDENTIFIER);
@@ -129,12 +138,12 @@ pub const Parser = struct {
         });
     }
 
-    // Expression :== NUMBER | IDENTIFIER
+    // Expression :== NUMBER | IDENTIFIER | Expression + Expression
     fn parse_expression(self: *Parser) ParserError!*Node {
         errdefer std.debug.print("Error parsing expression\n", .{});
         const token = self.consume_token() orelse return ParserError.ParsingError;
 
-        return switch (token) {
+        const lhs = try switch (token) {
             .NUMBER => |number_token| self.create_node(.{
                 .EXPRESSION = .{
                     .NUMBER = .{
@@ -149,8 +158,19 @@ pub const Parser = struct {
                     },
                 },
             }),
-            else => return ParserError.ParsingError,
+            else => unreachable,
         };
+
+        while (self.match_token(tokenizer.TokenType.PLUS)) {
+            const rhs = try self.parse_expression();
+
+            return self.create_node(.{ .EXPRESSION = .{ .BINARY = .{
+                .lhs = lhs,
+                .rhs = rhs,
+            } } });
+        }
+
+        return lhs;
     }
 
     fn accept_token(self: *Parser, expected_token: tokenizer.TokenType) ParserError!tokenizer.Token {
@@ -162,6 +182,15 @@ pub const Parser = struct {
         return self.consume_token() orelse unreachable;
     }
 
+    fn match_token(self: *Parser, token: tokenizer.TokenType) bool {
+        const curr_token = self.peek_token() orelse return false;
+        if (curr_token == token) {
+            _ = self.consume_token();
+            return true;
+        }
+        return false;
+    }
+
     fn consume_token(self: *Parser) ?tokenizer.Token {
         if (self.offset >= self.tokens.len) return null;
 
diff --git a/src/tokenizer.zig b/src/tokenizer.zig
index 4353ebb..20f80cc 100644
--- a/src/tokenizer.zig
+++ b/src/tokenizer.zig
@@ -4,6 +4,39 @@ const TokenizerError = error{
     TokenizingError,
 };
 
+pub const TokenType = enum {
+    // Keywords
+    LET,
+    PRINT,
+
+    // Identifiers
+    IDENTIFIER,
+
+    // Literals
+    NUMBER,
+
+    // Operators
+    EQUALS,
+    PLUS,
+
+    // Punctuation
+    SEMICOLON,
+    LPAREN,
+    RPAREN,
+};
+
+pub const Token = union(TokenType) {
+    LET: void,
+    PRINT: void,
+    IDENTIFIER: []u8,
+    NUMBER: i64,
+    EQUALS: void,
+    PLUS: void,
+    SEMICOLON: void,
+    LPAREN: void,
+    RPAREN: void,
+};
+
 pub const Tokenizer = struct {
     buf: []u8,
     offset: u32,
@@ -24,6 +57,7 @@ pub const Tokenizer = struct {
         if (c == '(') return Token{ .LPAREN = void{} };
         if (c == ')') return Token{ .RPAREN = void{} };
         if (c == '=') return Token{ .EQUALS = void{} };
+        if (c == '+') return Token{ .PLUS = void{} };
 
         const string = self.consume_string();
         if (string.len == 0) return TokenizerError.TokenizingError;
@@ -60,37 +94,6 @@ pub const Tokenizer = struct {
     }
 };
 
-pub const TokenType = enum {
-    // Keywords
-    LET,
-    PRINT,
-
-    // Identifiers
-    IDENTIFIER,
-
-    // Literals
-    NUMBER,
-
-    // Operators
-    EQUALS,
-
-    // Punctuation
-    SEMICOLON,
-    LPAREN,
-    RPAREN,
-};
-
-pub const Token = union(TokenType) {
-    LET: void,
-    PRINT: void,
-    IDENTIFIER: []u8,
-    NUMBER: i64,
-    EQUALS: void,
-    SEMICOLON: void,
-    LPAREN: void,
-    RPAREN: void,
-};
-
 test "simple" {
     const tests = [_]struct {
         buf: []u8,
@@ -132,7 +135,7 @@ test "simple" {
         defer token_list.deinit();
 
         var tokenizer = try Tokenizer.init(t.buf);
-        while (tokenizer.next()) |token| {
+        while (try tokenizer.next()) |token| {
             try token_list.append(token);
         }
         try std.testing.expectEqualDeep(t.tokens, token_list.items);
diff --git a/todo/0.src b/todo/0.src
new file mode 100644
index 0000000..2784a86
--- /dev/null
+++ b/todo/0.src
@@ -0,0 +1,5 @@
+let x = 1;
+
+x = x;
+
+print(x);