diff --git a/src/ast.zig b/src/ast.zig new file mode 100644 index 0000000..1ed62f4 --- /dev/null +++ b/src/ast.zig @@ -0,0 +1,19 @@ +pub const AstNodeType = enum { + Program, +}; + +pub const AstNode = union(AstNodeType) { + Program: []AstNode, +}; + +pub fn printNode(stdout: var, node: AstNode) anyerror!void { + switch (node) { + .Program => |children| try printNodes(stdout, children), + } +} + +fn printNodes(stdout: var, nodes: []AstNode) anyerror!void { + for (nodes) |node| { + try printNode(stdout, node); + } +} diff --git a/src/main.zig b/src/main.zig index e538343..b9ca981 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,6 +1,6 @@ const std = @import("std"); -const scanners = @import("scanner.zig"); +const runners = @import("runner.zig"); const Allocator = std.mem.Allocator; @@ -9,30 +9,14 @@ pub const Result = error{ CompileError, }; +pub const StdOut = *std.io.OutStream(std.fs.File.WriteError); + fn run(allocator: *Allocator, data: []u8) !void { var stdout_file = try std.io.getStdOut(); const stdout = &stdout_file.outStream().stream; - var scanner = scanners.Scanner.init(allocator, data); - - while (true) { - var tok_opt = scanner.nextToken() catch |err| { - try stdout.print( - "error at '{}': {}\n", - scanner.currentLexeme(), - err, - ); - - return Result.CompileError; - }; - - if (tok_opt) |tok| { - if (tok.ttype == .EOF) break; - try stdout.print("{x}\n", tok); - } - } - - return Result.Ok; + var runner = runners.Runner.init(allocator, stdout); + return runner.execute(data); } fn runFile(allocator: *Allocator, path: []const u8) !void { diff --git a/src/parser.zig b/src/parser.zig new file mode 100644 index 0000000..e57d0ec --- /dev/null +++ b/src/parser.zig @@ -0,0 +1,72 @@ +const std = @import("std"); +const scanners = @import("scanner.zig"); +const main = @import("main.zig"); +const ast = @import("ast.zig"); +const tokens = @import("tokens.zig"); + +const Allocator = std.mem.Allocator; +const Scanner = scanners.Scanner; +const AstNode = ast.AstNode; +const Token = tokens.Token; +const TokenType = tokens.TokenType; +const Result = main.Result; + +pub const Parser = struct { + allocator: *Allocator, + scanner: *Scanner, + current: Token = undefined, + root: AstNode = undefined, + + pub fn init(allocator: *Allocator, scanner: *Scanner) Parser { + return Parser{ .allocator = allocator, .scanner = scanner }; + } + + fn doError(self: *Parser, comptime fmt: []const u8, args: ...) !void { + std.debug.warn("parser error at line {}\n\t", self.scanner.line); + std.debug.warn(fmt, args); + std.debug.warn("\n"); + + return Result.CompileError; + } + + fn advance(self: *Parser) !void { + var tok_opt = try self.scanner.nextToken(); + if (tok_opt) |tok| { + self.current = tok; + } + } + + fn accept(self: *Parser, ttype: TokenType) !bool { + if (self.current.ttype == ttype) { + try self.advance(); + return true; + } else { + return false; + } + } + + fn expect(self: *Parser, ttype: TokenType) !void { + if (!try self.accept(ttype)) { + try self.doError("expected {x}, got {}", ttype, self.current.ttype); + } + } + + fn program(self: *Parser) !void { + try self.advance(); + try self.advance(); + try self.advance(); + try self.advance(); + + try self.expect(.EOF); + } + + pub fn parse(self: *Parser) !AstNode { + self.root = AstNode{ + .Program = try self.allocator.alloc(AstNode, 0), + }; + + try self.program(); + + return self.root; + } +}; diff --git a/src/runner.zig b/src/runner.zig new file mode 100644 index 0000000..61c0af1 --- /dev/null +++ b/src/runner.zig @@ -0,0 +1,48 @@ +const std = @import("std"); +const scanners = @import("scanner.zig"); +const parsers = @import("parser.zig"); +const main = @import("main.zig"); +const ast = @import("ast.zig"); + +const Allocator = std.mem.Allocator; +const Result = main.Result; +const Parser = parsers.Parser; + +pub const Runner = struct { + allocator: *Allocator, + stdout: main.StdOut, + + pub fn init(allocator: *Allocator, stdout: main.StdOut) Runner { + return Runner{ .allocator = allocator, .stdout = stdout }; + } + + fn testScanner(self: *Runner, scanner: *scanners.Scanner) !void { + while (true) { + var tok_opt = scanner.nextToken() catch |err| { + try self.stdout.print( + "error at '{}': {}\n", + scanner.currentLexeme(), + err, + ); + + return Result.CompileError; + }; + + if (tok_opt) |tok| { + if (tok.ttype == .EOF) break; + try self.stdout.print("{x}\n", tok); + } + } + } + + pub fn execute(self: *Runner, code: []u8) !void { + var scanner = scanners.Scanner.init(self.allocator, code); + try self.testScanner(&scanner); + + scanner = scanners.Scanner.init(self.allocator, code); + var parser = Parser.init(self.allocator, &scanner); + var tree = try parser.parse(); + try ast.printNode(self.stdout, tree); + return Result.Ok; + } +};