const std = @import("std"); const Allocator = std.mem.Allocator; const zexa = @import("./root.zig"); const Value = zexa.lang.Value; const Scope = zexa.lang.Scope; const Number = zexa.lang.Number; const Environment = zexa.env.Environment; const lexer = @import("./lexer.zig"); const Tokenizer = lexer.Tokenizer; const Token = lexer.Token; const TokenValue = lexer.TokenValue; const LineInfo = lexer.LineInfo; const util = @import("./util.zig"); const SliceIterator = util.SliceIterator; const StringBuilder = util.StringBuilder; pub const Parser = struct { const Self = @This(); allocator: Allocator, tokenizer: *Tokenizer, env: *Environment, ast: std.ArrayList(*Value), line: LineInfo = .{ .line = 0, .column = 0 }, pub const ParseError = error{ UnexpectedToken, UnexpectedEOF, UnclosedList, }; pub fn init(allocator: Allocator, tokenizer: *Tokenizer, env: *Environment) !Self { return .{ .allocator = allocator, .tokenizer = tokenizer, .env = env, .ast = try std.ArrayList(*Value).initCapacity(allocator, 1000), }; } pub fn deinit(self: *Self) void { self.ast.deinit(self.allocator); } pub fn parse(self: *Self) !void { var iter = SliceIterator(Token).init(self.tokenizer.tokens.items); while (iter.next()) |tok| { try self.ast.append(self.allocator, try self.parseExpr(tok, &iter)); } } pub fn parseExpr(self: *Self, token: Token, tokens: *SliceIterator(Token)) !*Value { const scope = &self.env.primary_scope; self.line = token.line; switch (token.value) { .num => |num| { const v = try std.fmt.parseFloat(Number, num); return try Value.initFromAny(scope, v); }, .sym => |sym| return try Value.initSymbol(scope, sym), .str => |str| return try Value.initString(scope, str), .quote => { const ntok = tokens.next() orelse return ParseError.UnexpectedEOF; return try Value.initFromAny( scope, try self.parseExpr(ntok, tokens), ); }, .lparen => { const list = try Value.initCons( scope, try Value.initNil(scope), try Value.initNil(scope), ); var first = true; if (tokens.peek()) |ptok| { switch (ptok.value) { .rparen => { _ = tokens.next(); return list; }, else => {}, } } while (tokens.next()) |ntok| { switch (ntok.value) { .rparen => { break; }, else => { const val = try self.parseExpr(ntok, tokens); if (first) { list.expr.cons.car = val; first = false; } else { try list.append(scope, val); } }, } } return list; }, .rparen => return ParseError.UnclosedList, } } }; test "parser" { var tokenizer = try Tokenizer.init(std.testing.allocator, @embedFile("./test/test1.zexa")); defer tokenizer.deinit(); try tokenizer.lex(); var env = try Environment.init(std.testing.allocator); defer env.deinit(); try zexa.corelib.loadCoreLibrary(&env.primary_scope); var parser = try Parser.init(std.testing.allocator, &tokenizer, &env); try parser.parse(); defer parser.deinit(); _ = try env.primary_scope.evalAst(parser.ast.items); } test "test" { var tokenizer = try Tokenizer.init(std.testing.allocator, "(lambda () (print hey))"); defer tokenizer.deinit(); try tokenizer.lex(); var env = try Environment.init(std.testing.allocator); defer env.deinit(); var parser = try Parser.init(std.testing.allocator, &tokenizer, &env); defer parser.deinit(); try parser.parse(); for (parser.ast.items) |v| { std.debug.print("value: {f}\n", .{v}); } }