diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/evaluator.zig | 361 | ||||
| -rw-r--r-- | src/main.zig | 65 |
2 files changed, 15 insertions, 411 deletions
diff --git a/src/evaluator.zig b/src/evaluator.zig deleted file mode 100644 index 5d97eed..0000000 --- a/src/evaluator.zig +++ /dev/null @@ -1,361 +0,0 @@ -const std = @import("std"); -const parser = @import("parser.zig"); - -const EvaluatorError = error{ - EvaluationError, - OutOfMemory, -}; - -const VariableType = enum { NUMBER, BOOLEAN, STRING, FUNCTION_DEFINITION }; - -const Variable = union(VariableType) { - NUMBER: i64, - BOOLEAN: bool, - STRING: []const u8, - FUNCTION_DEFINITION: *parser.Node, -}; - -pub const Evaluator = struct { - ast: ?*parser.Node, - environment: *Environment, - - arena: std.mem.Allocator, - - pub fn init(arena_allocator: std.mem.Allocator) !*Evaluator { - const evaluator = try arena_allocator.create(Evaluator); - evaluator.* = .{ - .ast = null, - .environment = try Environment.init(arena_allocator), - .arena = arena_allocator, - }; - return evaluator; - } - - pub fn evaluate_ast(self: *Evaluator, ast: *parser.Node) !i64 { - errdefer std.debug.print("Error evaluating AST\n", .{}); - std.debug.assert(ast.* == parser.Node.PROGRAM); - - const program = ast.PROGRAM; - - for (program.statements) |stmt| { - _ = try self.evaluate_statement(stmt); - } - - const main = self.environment.get_variable("main") orelse return EvaluatorError.EvaluationError; - const ret = try self.evaluate_function_definition(main.FUNCTION_DEFINITION, &[_]*parser.Node{}); - return ret.?.NUMBER; - } - - 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); - - switch (statement.STATEMENT.statement.*) { - .ASSIGNMENT_STATEMENT => |*assignment_statement| { - try self.evaluate_assignment_statement(@ptrCast(assignment_statement)); - return null; - }, - .FUNCTION_CALL_STATEMENT => |*function_call_statement| { - _ = try self.evaluate_function_call_statement(@ptrCast(function_call_statement)); - return null; - }, - .IF_STATEMENT => |*if_statement| { - return try self.evaluate_if_statement(@ptrCast(if_statement)); - }, - .WHILE_STATEMENT => |*while_statement| { - return try self.evaluate_while_statement(@ptrCast(while_statement)); - }, - .RETURN_STATEMENT => |*return_statement| return try self.evaluate_return_statement(@ptrCast(return_statement)), - else => unreachable, - } - - return null; - } - - fn evaluate_assignment_statement(self: *Evaluator, node: *parser.Node) !void { - errdefer std.debug.print("Error evaluating assignment statement\n", .{}); - std.debug.assert(node.* == parser.Node.ASSIGNMENT_STATEMENT); - - const assignment_statement = node.ASSIGNMENT_STATEMENT; - - //TODO: We should lowercase keys no? - if (assignment_statement.is_declaration) { - try self.environment.add_variable(assignment_statement.name, null); - } - - if (!self.environment.contains_variable(assignment_statement.name)) { - std.debug.print("Variable not found: {s}\n", .{assignment_statement.name}); - return EvaluatorError.EvaluationError; - } - - 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!?*Variable { - errdefer std.debug.print("Error evaluating function call statement\n", .{}); - std.debug.assert(node.* == parser.Node.FUNCTION_CALL_STATEMENT); - - const function_call_statement = node.FUNCTION_CALL_STATEMENT; - - switch (function_call_statement.expression.*) { - .FUNCTION_DEFINITION => |*function_definition| { - return try self.evaluate_function_definition(@ptrCast(function_definition), function_call_statement.arguments); - }, - .PRIMARY_EXPRESSION => |*primary_expression| { - std.debug.assert(primary_expression.* == .IDENTIFIER); - - // Print function implementation - if (std.mem.eql(u8, function_call_statement.expression.PRIMARY_EXPRESSION.IDENTIFIER.name, "print") or std.mem.eql(u8, function_call_statement.expression.PRIMARY_EXPRESSION.IDENTIFIER.name, "printb")) { - 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 null; - } - - const function_definition = self.environment.get_variable(primary_expression.IDENTIFIER.name) orelse return EvaluatorError.EvaluationError; - return self.evaluate_function_definition(function_definition.FUNCTION_DEFINITION, function_call_statement.arguments); - }, - else => unreachable, - } - } - - 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); - - const if_statement = node.IF_STATEMENT; - - const if_condition_val = try self.get_expression_value(if_statement.condition); - - 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_while_statement(self: *Evaluator, node: *parser.Node) !?*Variable { - errdefer std.debug.print("Error evaluating while statement\n", .{}); - std.debug.assert(node.* == parser.Node.WHILE_STATEMENT); - - const while_statement = node.WHILE_STATEMENT; - - while (true) { - const while_condition_val = try self.get_expression_value(while_statement.condition); - std.debug.assert(while_condition_val.?.* == .BOOLEAN); - - if (!while_condition_val.?.BOOLEAN) return null; - - if (try self.evaluate_block_statements(while_statement.statements)) |ret| return ret; - } - - return null; - } - - 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.?; - } - - 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) 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); - if (x.addition) return try self.create_variable(.{ .NUMBER = lhs.NUMBER + rhs.NUMBER }); - return try self.create_variable(.{ .NUMBER = lhs.NUMBER - rhs.NUMBER }); - }, - .MULTIPLICATIVE_EXPRESSION => |x| { - 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); - switch (x.typ) { - .MUL => return try self.create_variable(.{ .NUMBER = lhs.NUMBER * rhs.NUMBER }), - .DIV => return try self.create_variable(.{ .NUMBER = @divFloor(lhs.NUMBER, rhs.NUMBER) }), - .MOD => return try self.create_variable(.{ .NUMBER = @rem(lhs.NUMBER, rhs.NUMBER) }), - } - }, - .UNARY_EXPRESSION => |x| { - const val = try self.get_expression_value(x.expression) orelse return EvaluatorError.EvaluationError; - switch (x.typ) { - .NOT => { - std.debug.assert(val.* == .BOOLEAN); - return try self.create_variable(.{ .BOOLEAN = !val.BOOLEAN }); - }, - .MINUS => { - std.debug.assert(val.* == .NUMBER); - return try self.create_variable(.{ .NUMBER = -val.NUMBER }); - }, - else => unreachable, - } - }, - .EQUALITY_EXPRESSION => |x| { - 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); //TODO: Generic - switch (x.typ) { - .EQ => return try self.create_variable(.{ .BOOLEAN = (lhs.NUMBER == rhs.NUMBER) }), - .GT => return try self.create_variable(.{ .BOOLEAN = (lhs.NUMBER > rhs.NUMBER) }), - .LT => return try self.create_variable(.{ .BOOLEAN = (lhs.NUMBER < rhs.NUMBER) }), - } - }, - .PRIMARY_EXPRESSION => |x| { - switch (x) { - .NUMBER => |number| return self.create_variable(.{ .NUMBER = number.value }), - .BOOLEAN => |b| return self.create_variable(.{ .BOOLEAN = b.value }), - .STRING => |s| return self.create_variable(.{ .STRING = s.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; - }, - } - }, - // 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!?*Variable { - errdefer std.debug.print("Error evaluating function definition\n", .{}); - std.debug.assert(node.* == parser.Node.FUNCTION_DEFINITION); - - const function_definition = node.*.FUNCTION_DEFINITION; - - 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.get_expression_value(argument)); - } - - if (try self.evaluate_block_statements(function_definition.statements)) |ret| return ret; - - // We should never get here as there should be a return statement - return EvaluatorError.EvaluationError; - } - - fn evaluate_block_statements(self: *Evaluator, statements: []*parser.Node) !?*Variable { - var i: usize = 0; - while (i < statements.len) : (i += 1) { - const stmt = statements[i]; - const res = try self.evaluate_statement(stmt); - if (res != null) return res.?; - } - return null; - } - - fn create_variable(self: *Evaluator, variable_value: Variable) !*Variable { - const variable = try self.arena.create(Variable); - variable.* = variable_value; - return variable; - } -}; - -const Scope = struct { - variables: std.StringHashMap(?*Variable), -}; - -const Environment = struct { - scope_stack: std.ArrayList(*Scope), - - arena: std.mem.Allocator, - - fn init(arena_allocator: std.mem.Allocator) !*Environment { - const self = try arena_allocator.create(Environment); - - self.* = .{ - .scope_stack = std.ArrayList(*Scope).init(arena_allocator), - .arena = arena_allocator, - }; - - // Create global scope - try self.create_scope(); - - return self; - } - - fn create_scope(self: *Environment) !void { - const scope = try self.arena.create(Scope); - scope.* = .{ - .variables = std.StringHashMap(?*Variable).init(self.arena), - }; - try self.scope_stack.append(scope); - } - - fn drop_scope(self: *Environment) void { - _ = self.scope_stack.pop(); - } - - fn add_variable(self: *Environment, name: []const u8, variable: ?*Variable) !void { - try self.scope_stack.getLast().variables.put(name, variable); - } - - fn get_variable(self: *Environment, name: []const u8) ?*Variable { - 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; - } - return null; - } - - fn contains_variable(self: *Environment, name: []const u8) bool { - var i = self.scope_stack.items.len; - while (i > 0) { - i -= 1; - const scope = self.scope_stack.items[i]; - if (scope.variables.contains(name)) return true; - } - return false; - } -}; - -test "simple" { - const ast = &parser.Node{ .PROGRAM = .{ .statements = @constCast(&[_]*parser.Node{ @constCast(&parser.Node{ .STATEMENT = .{ .statement = @constCast(&parser.Node{ .VARIABLE_STATEMENT = .{ - .is_declaration = true, - .name = @constCast("i"), - .expression = @constCast(&parser.Node{ .EXPRESSION = .{ - .NUMBER = .{ .value = 2 }, - } }), - } }) } }), @constCast(&parser.Node{ .STATEMENT = .{ .statement = @constCast(&parser.Node{ - .VARIABLE_STATEMENT = .{ - .is_declaration = false, - .name = @constCast("i"), - .expression = @constCast(&parser.Node{ .EXPRESSION = .{ - .NUMBER = .{ .value = 2 }, - } }), - }, - }) } }) }) } }; - - var evaluator = try Evaluator.init(std.testing.allocator); - defer evaluator.deinit(); - - const evaluation_result = try evaluator.evaluate_ast(@constCast(ast)); - - try std.testing.expectEqual(0, evaluation_result); -} diff --git a/src/main.zig b/src/main.zig index 3ac7f39..5ddd750 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,13 +1,9 @@ const std = @import("std"); const tokenizer = @import("tokenizer.zig"); const parser = @import("parser.zig"); -const evaluator = @import("evaluator.zig"); const codegen = @import("codegen.zig"); pub fn main() !void { - const stdout = std.io.getStdOut().writer(); - const stdin = std.io.getStdIn().reader(); - const pathLen = std.mem.len(std.os.argv[1]); const path = std.os.argv[1][0..pathLen]; @@ -21,48 +17,22 @@ pub fn main() !void { var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); - const source_evaluator = try evaluator.Evaluator.init(arena.allocator()); - - if (std.mem.eql(u8, path, "-i")) { - while (true) { - try stdout.print("> ", .{}); - - const buf = try stdin.readUntilDelimiterAlloc(allocator, '\n', 1024); - defer allocator.free(buf); - - process_buf(buf, allocator, arena.allocator(), source_evaluator, null) catch |err| { - try stdout.print("Error processing line: {any}\n", .{err}); - }; - } - } else { - std.debug.print("Tokenizing! {s}\n", .{path}); - const file = try std.fs.cwd().openFile(path, .{}); - const buf = try file.readToEndAlloc(allocator, 1 * 1024 * 1024); - defer allocator.free(buf); - if (std.os.argv.len < 3) { - try process_buf( - buf, - allocator, - arena.allocator(), - source_evaluator, - null, - ); - } else { - const source_codegen = try codegen.CodeGen.init(arena.allocator()); - defer source_codegen.deinit(); - try process_buf( - buf, - allocator, - arena.allocator(), - source_evaluator, - source_codegen, - ); - source_codegen.compile(); - } - } + std.debug.print("Tokenizing! {s}\n", .{path}); + const file = try std.fs.cwd().openFile(path, .{}); + const buf = try file.readToEndAlloc(allocator, 1 * 1024 * 1024); + defer allocator.free(buf); + const source_codegen = try codegen.CodeGen.init(arena.allocator()); + defer source_codegen.deinit(); + try process_buf( + buf, + allocator, + arena.allocator(), + source_codegen, + ); + source_codegen.compile(); } -fn process_buf(buf: []u8, allocator: std.mem.Allocator, arena: std.mem.Allocator, source_evaluator: *evaluator.Evaluator, source_codegen: ?*codegen.CodeGen) !void { +fn process_buf(buf: []u8, allocator: std.mem.Allocator, arena: std.mem.Allocator, source_codegen: ?*codegen.CodeGen) !void { std.debug.print("Buf:\n{s}\n", .{buf}); var token_list = std.ArrayList(tokenizer.Token).init(allocator); @@ -78,12 +48,7 @@ fn process_buf(buf: []u8, allocator: std.mem.Allocator, arena: std.mem.Allocator const ast = try source_parser.parse(); std.debug.print("AST: {any}\n", .{ast}); - if (source_codegen != null) { - try source_codegen.?.generate(ast); - } else { - const result = try source_evaluator.evaluate_ast(ast); - std.debug.print("Evaluation result: {any}\n", .{result}); - } + try source_codegen.?.generate(ast); } test { |