diff options
author | Baitinq <manuelpalenzuelamerino@gmail.com> | 2025-01-19 13:55:58 +0100 |
---|---|---|
committer | Baitinq <manuelpalenzuelamerino@gmail.com> | 2025-01-19 14:09:32 +0100 |
commit | 538b822e837c42f29f5285198a39cfb601685620 (patch) | |
tree | a2ed9a4817f770d141af7b59e210439534d28ee0 | |
parent | Example: Get fibonacci example working (diff) | |
download | interpreter-538b822e837c42f29f5285198a39cfb601685620.tar.gz interpreter-538b822e837c42f29f5285198a39cfb601685620.tar.bz2 interpreter-538b822e837c42f29f5285198a39cfb601685620.zip |
Feature: Add support for boolean values
-rw-r--r-- | examples/9.src | 10 | ||||
-rw-r--r-- | grammar.ebnf | 2 | ||||
-rw-r--r-- | src/evaluator.zig | 65 | ||||
-rw-r--r-- | src/parser.zig | 10 | ||||
-rw-r--r-- | src/tokenizer.zig | 4 |
5 files changed, 57 insertions, 34 deletions
diff --git a/examples/9.src b/examples/9.src new file mode 100644 index 0000000..d21ae71 --- /dev/null +++ b/examples/9.src @@ -0,0 +1,10 @@ +let main = () => { + let i = true; + + if i { + print(i); + return 1; + }; + + return 0; +}; diff --git a/grammar.ebnf b/grammar.ebnf index 86710a2..eee1103 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -16,7 +16,7 @@ Expression ::= AdditiveExpression | FunctionDefinition AdditiveExpression ::= PrimaryExpression (("+" | "-") AdditiveExpression) -PrimaryExpression ::= NUMBER | IDENTIFIER | FunctionCallStatement +PrimaryExpression ::= NUMBER | BOOLEAN | IDENTIFIER | FunctionCallStatement FunctionDefinition ::= LPAREN FunctionParamters? RPAREN ARROW LBRACE Statement* ReturnStatement SEMICOLON RBRACE diff --git a/src/evaluator.zig b/src/evaluator.zig index 2fc5441..39c7235 100644 --- a/src/evaluator.zig +++ b/src/evaluator.zig @@ -6,10 +6,11 @@ const EvaluatorError = error{ OutOfMemory, }; -const VariableType = enum { NUMBER, FUNCTION_DEFINITION }; +const VariableType = enum { NUMBER, BOOLEAN, FUNCTION_DEFINITION }; const Variable = union(VariableType) { NUMBER: i64, + BOOLEAN: bool, FUNCTION_DEFINITION: *parser.Node, }; @@ -41,10 +42,11 @@ pub const Evaluator = struct { } const main = self.environment.get_variable("main") orelse return EvaluatorError.EvaluationError; - return try self.evaluate_function_definition(main.FUNCTION_DEFINITION, &[_]*parser.Node{}); + const ret = try self.evaluate_function_definition(main.FUNCTION_DEFINITION, &[_]*parser.Node{}); + return ret.?.NUMBER; } - fn evaluate_statement(self: *Evaluator, statement: *parser.Node) EvaluatorError!?i64 { + fn evaluate_statement(self: *Evaluator, statement: *parser.Node) EvaluatorError!?*Variable { errdefer std.debug.print("Error evaluating statement\n", .{}); std.debug.assert(statement.* == parser.Node.STATEMENT); @@ -83,11 +85,11 @@ pub const Evaluator = struct { return EvaluatorError.EvaluationError; } - const variable = try self.create_variable(assignment_statement.expression); + const variable = try self.get_expression_value(assignment_statement.expression); try self.environment.add_variable(assignment_statement.name, variable); } - fn evaluate_function_call_statement(self: *Evaluator, node: *parser.Node) EvaluatorError!i64 { + fn evaluate_function_call_statement(self: *Evaluator, node: *parser.Node) EvaluatorError!?*Variable { errdefer std.debug.print("Error evaluating function call statement\n", .{}); std.debug.assert(node.* == parser.Node.FUNCTION_CALL_STATEMENT); @@ -97,7 +99,7 @@ pub const Evaluator = struct { if (std.mem.eql(u8, function_call_statement.name, "print")) { std.debug.assert(function_call_statement.arguments.len == 1); std.debug.print("PRINT: {any}\n", .{try self.get_expression_value(function_call_statement.arguments[0])}); - return 0; + return null; } const function_definition = self.environment.get_variable(function_call_statement.name) orelse return EvaluatorError.EvaluationError; @@ -105,7 +107,7 @@ pub const Evaluator = struct { return self.evaluate_function_definition(function_definition.FUNCTION_DEFINITION, function_call_statement.arguments); } - fn evaluate_if_statement(self: *Evaluator, node: *parser.Node) !?i64 { + fn evaluate_if_statement(self: *Evaluator, node: *parser.Node) !?*Variable { errdefer std.debug.print("Error evaluating if statement\n", .{}); std.debug.assert(node.* == parser.Node.IF_STATEMENT); @@ -113,54 +115,63 @@ pub const Evaluator = struct { const if_condition_val = try self.get_expression_value(if_statement.condition); - if (if_condition_val != 0) return null; + std.debug.assert(if_condition_val.?.* == .BOOLEAN); + + if (!if_condition_val.?.BOOLEAN) return null; if (try self.evaluate_block_statements(if_statement.statements)) |ret| return ret; return null; } - fn evaluate_return_statement(self: *Evaluator, return_statement: *parser.Node) !i64 { + fn evaluate_return_statement(self: *Evaluator, return_statement: *parser.Node) !*Variable { errdefer std.debug.print("Error evaluating return statement\n", .{}); std.debug.assert(return_statement.* == parser.Node.RETURN_STATEMENT); const return_value = try self.get_expression_value(return_statement.RETURN_STATEMENT.expression); - return return_value; + return return_value.?; } - fn get_expression_value(self: *Evaluator, node: *parser.Node) !i64 { + fn get_expression_value(self: *Evaluator, node: *parser.Node) !?*Variable { errdefer std.debug.print("Error getting statement value\n", .{}); switch (node.*) { .ADDITIVE_EXPRESSION => |x| { - const lhs = try self.get_expression_value(x.lhs); - const rhs = try self.get_expression_value(x.rhs); - if (x.addition) return lhs + rhs; - return lhs - rhs; + const lhs = try self.get_expression_value(x.lhs) orelse return EvaluatorError.EvaluationError; + const rhs = try self.get_expression_value(x.rhs) orelse return EvaluatorError.EvaluationError; + std.debug.assert(lhs.* == .NUMBER and rhs.* == .NUMBER); + var res: i64 = undefined; + if (x.addition) res = lhs.NUMBER + rhs.NUMBER; + res = lhs.NUMBER - rhs.NUMBER; + return try self.create_variable(.{ .NUMBER = res }); }, .PRIMARY_EXPRESSION => |x| { switch (x) { - .NUMBER => |number| return number.value, + .NUMBER => |number| return self.create_variable(.{ .NUMBER = number.value }), + .BOOLEAN => |b| return self.create_variable(.{ .BOOLEAN = b.value }), .IDENTIFIER => |identifier| { const val = self.environment.get_variable(identifier.name) orelse { std.debug.print("Identifier {any} not found\n", .{identifier.name}); return EvaluatorError.EvaluationError; }; - return val.NUMBER; + return val; }, else => unreachable, } }, // I don't like having 2 places where we evaluate functions .FUNCTION_CALL_STATEMENT => return try self.evaluate_function_call_statement(node), + .FUNCTION_DEFINITION => return try self.create_variable(.{ + .FUNCTION_DEFINITION = node, + }), else => unreachable, } } - fn evaluate_function_definition(self: *Evaluator, node: *parser.Node, arguments: []*parser.Node) EvaluatorError!i64 { + fn evaluate_function_definition(self: *Evaluator, node: *parser.Node, arguments: []*parser.Node) EvaluatorError!?*Variable { errdefer std.debug.print("Error evaluating function definition\n", .{}); std.debug.assert(node.* == parser.Node.FUNCTION_DEFINITION); @@ -176,7 +187,7 @@ pub const Evaluator = struct { 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)); + try self.environment.add_variable(parameter.PRIMARY_EXPRESSION.IDENTIFIER.name, try self.get_expression_value(argument)); } if (try self.evaluate_block_statements(function_definition.statements)) |ret| return ret; @@ -185,7 +196,7 @@ pub const Evaluator = struct { return EvaluatorError.EvaluationError; } - fn evaluate_block_statements(self: *Evaluator, statements: []*parser.Node) !?i64 { + fn evaluate_block_statements(self: *Evaluator, statements: []*parser.Node) !?*Variable { var i: usize = 0; while (i < statements.len) : (i += 1) { const stmt = statements[i]; @@ -195,19 +206,9 @@ pub const Evaluator = struct { return null; } - fn create_variable(self: *Evaluator, node: *parser.Node) !*Variable { + fn create_variable(self: *Evaluator, variable_value: Variable) !*Variable { const variable = try self.allocator.create(Variable); - if (node.* == parser.Node.FUNCTION_DEFINITION) { - variable.* = .{ - .FUNCTION_DEFINITION = node, - }; - } else { - const val = try self.get_expression_value(node); - variable.* = .{ - .NUMBER = val, - }; - } - + variable.* = variable_value; return variable; } }; diff --git a/src/parser.zig b/src/parser.zig index 36f7437..ac66e5d 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -56,6 +56,9 @@ pub const Node = union(NodeType) { NUMBER: struct { value: i64, }, + BOOLEAN: struct { + value: bool, + }, IDENTIFIER: struct { name: []const u8, }, @@ -237,7 +240,7 @@ pub const Parser = struct { return lhs; } - // PrimaryExpression ::= NUMBER | IDENTIFIER | FunctionCallStatement + // PrimaryExpression ::= NUMBER | BOOLEAN | IDENTIFIER | FunctionCallStatement fn parse_primary_expression(self: *Parser) ParserError!*Node { errdefer if (!self.try_context) std.debug.print("Error parsing primary expression\n", .{}); @@ -253,6 +256,11 @@ pub const Parser = struct { }, }, }), + .BOOLEAN => |boolean_token| try self.create_node(.{ + .PRIMARY_EXPRESSION = .{ .BOOLEAN = .{ + .value = boolean_token, + } }, + }), .IDENTIFIER => |identifier_token| try self.create_node(.{ .PRIMARY_EXPRESSION = .{ .IDENTIFIER = .{ diff --git a/src/tokenizer.zig b/src/tokenizer.zig index e395835..6765ea9 100644 --- a/src/tokenizer.zig +++ b/src/tokenizer.zig @@ -16,6 +16,7 @@ pub const TokenType = enum { // Literals NUMBER, + BOOLEAN, // Operators EQUALS, @@ -38,6 +39,7 @@ pub const Token = union(TokenType) { ARROW: void, IDENTIFIER: []u8, NUMBER: i64, + BOOLEAN: bool, EQUALS: void, PLUS: void, MINUS: void, @@ -84,6 +86,8 @@ pub const Tokenizer = struct { if (std.mem.eql(u8, string, "let")) return Token{ .LET = void{} }; if (std.mem.eql(u8, string, "if")) return Token{ .IF = void{} }; if (std.mem.eql(u8, string, "return")) return Token{ .RETURN = void{} }; + if (std.mem.eql(u8, string, "true")) return Token{ .BOOLEAN = true }; + if (std.mem.eql(u8, string, "false")) return Token{ .BOOLEAN = false }; if (std.fmt.parseInt(i32, string, 10) catch null) |i| return Token{ .NUMBER = i }; |