diff --git a/examples/if.sub b/examples/if.sub new file mode 100644 index 0000000..01d3d48 --- /dev/null +++ b/examples/if.sub @@ -0,0 +1,22 @@ +fn main() void { + print("Helloworld", "maman", "Harold", "Reese",); + fn_call("I like to play with people"); + var sub: i32 = "Bonjour"; + if "bonjour" { + print("in if statement"); + foo("in foo statement"); + if "maman" { + bar("foo", "bar", "baz",); + var inner_var: Foo = "bonsoir"; + } elseif "else_expr" { + fn_else("in_fn_else",); + } else { + print("without condition"); + } + } elseif best("else_if_expr") { + bazz("in bazz"); + fuzz("in bazz"); + } else { + var else_inner: f32 = "for now, expr can just be strings"; + } +} diff --git a/src/ast.zig b/src/ast.zig index 61ec513..6d8834b 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -1,6 +1,7 @@ const std = @import("std"); const panic = std.debug.panic; +const assert = std.debug.assert; //////////// Program Block @@ -13,7 +14,7 @@ pub const Ast = struct { pub fn print(ast: Ast) void { for (ast.decls.items) |decl| { - decl.print(); + decl.print(0); std.debug.print("\n\n", .{}); } } @@ -43,9 +44,9 @@ pub const ProgramDecl = union(ProgramDeclTag) { } }; } - pub fn print(self: Self) void { + pub fn print(self: Self, nindent: usize) void { switch (self) { - .fn_decl => |fn_decl| fn_decl.print(), + .fn_decl => |fn_decl| fn_decl.print(nindent), } } }; @@ -58,20 +59,20 @@ const FnDecl = struct { body: std.ArrayList(Stmt), return_type: []const u8, - pub fn print(self: Self) void { + pub fn print(self: Self, indent: usize) void { + print_nindent(indent); std.debug.print("fn {s}(", .{self.name}); for (self.args.items) |arg| { std.debug.print("{s}, ", .{arg}); } std.debug.print(") {s} {{\n", .{self.return_type}); - const indent = 1; for (self.body.items) |stmt| { - print_nindent(indent); - stmt.print(); + stmt.print(indent + 1); } + print_nindent(indent); std.debug.print("}}", .{}); } }; @@ -147,6 +148,7 @@ const VarDeclExpr = struct { const StmtTag = enum { assign, no_assign, + if_, }; //////////// Stmt structs @@ -155,6 +157,7 @@ pub const Stmt = union(StmtTag) { assign: AssignStmt, no_assign: NoAssignStmt, + if_: IfStmt, pub fn create_assign(var_: ?[]const u8, value: Expr) Stmt { return .{ .assign = .{ @@ -163,12 +166,29 @@ pub const Stmt = union(StmtTag) { } }; } - pub fn print(self: Self) void { + pub fn create_if( + if_eval: Expr, + if_body: std.ArrayList(Stmt), + elseif_evals: std.ArrayList(Expr), + elseif_thens: std.ArrayList(std.ArrayList(Stmt)), + else_then: ?std.ArrayList(Stmt), + ) Stmt { + return .{ .if_ = .{ + .if_eval = if_eval, + .if_body = if_body, + .elseif_evals = elseif_evals, + .elseif_thens = elseif_thens, + .else_then = else_then, + } }; + } + + pub fn print(self: Self, indent: usize) void { switch (self) { - .assign => |assign| assign.print(), - .no_assign => |no_assign| no_assign.print(), + .assign => |assign| assign.print(indent), + .no_assign => |no_assign| no_assign.print(indent), + .if_ => |if_| if_.print(indent), } - std.debug.print(";\n", .{}); + std.debug.print("\n", .{}); } }; @@ -179,9 +199,11 @@ const AssignStmt = struct { type_: []const u8, value: Expr, - pub fn print(self: Self) void { + pub fn print(self: Self, indent: usize) void { + print_nindent(indent); std.debug.print("var {s}: {s} = ", .{ self.var_, self.type_ }); self.value.print(); + std.debug.print(";", .{}); } }; @@ -189,13 +211,66 @@ const NoAssignStmt = struct { const Self = @This(); value: Expr, - pub fn print(self: Self) void { + pub fn print(self: Self, indent: usize) void { + print_nindent(indent); self.value.print(); + std.debug.print(";", .{}); + } +}; + +const IfStmt = struct { + const Self = @This(); + + if_eval: Expr, + if_body: std.ArrayList(Stmt), + + elseif_evals: std.ArrayList(Expr), + elseif_thens: std.ArrayList(std.ArrayList(Stmt)), + + else_then: ?std.ArrayList(Stmt), + + pub fn print(self: Self, indent: usize) void { + print_nindent(indent); + std.debug.print("if ", .{}); + self.if_eval.print(); + std.debug.print(" {{\n", .{}); + for (self.if_body.items) |stmt| { + stmt.print(indent + 1); + } + print_nindent(indent); + std.debug.print("}}", .{}); + + assert(self.elseif_evals.items.len == self.elseif_thens.items.len); + for (0..self.elseif_evals.items.len) |i| { + const eval = self.elseif_evals.items[i]; + const then = self.elseif_thens.items[i]; + + std.debug.print(" elseif ", .{}); + eval.print(); + std.debug.print(" {{\n", .{}); + + for (then.items) |then_stmt| { + then_stmt.print(indent + 1); + } + print_nindent(indent); + std.debug.print("}}", .{}); + } + + if (self.else_then != null) { + const else_then = self.else_then.?; + + std.debug.print(" else {{\n", .{}); + for (else_then.items) |then_stmt| { + then_stmt.print(indent + 1); + } + print_nindent(indent); + std.debug.print("}}", .{}); + } } }; fn print_nindent(n: usize) void { for (0..n) |_| { - std.debug.print(" ", .{}); + std.debug.print(" ", .{}); } } diff --git a/src/main.zig b/src/main.zig index 734ccfb..af46282 100644 --- a/src/main.zig +++ b/src/main.zig @@ -9,13 +9,17 @@ const Parser = parser_pkg.Parser; const LIMIT = 1024 * 10; +const assert = std.debug.assert; + pub fn main(init: std.process.Init) !void { - _ = ast_pkg; - const io = init.io; const alloc = std.heap.page_allocator; + const args = try init.minimal.args.toSlice(alloc); + assert(args.len == 2); + const io = init.io; + const current_dir = std.Io.Dir.cwd(); - const file_path = "examples/helloworld.sub"; + const file_path = args[1]; const content: []const u8 = try current_dir.readFileAlloc( io, file_path, @@ -25,7 +29,7 @@ pub fn main(init: std.process.Init) !void { var l = Lexer.init( content, - "main.sub", + file_path, ); var parser = Parser.init(&l, alloc); const ast = try parser.parse(); diff --git a/src/parser.zig b/src/parser.zig index 6bcea42..b2bc2b8 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -80,19 +80,25 @@ pub const Parser = struct { while (l.token != .cbrace) { const stmt = try parse_stmt(l, alloc); try body.append(alloc, stmt); - l.eat(.semicolon); + // l.eat(.semicolon); } l.eat(.cbrace); return ProgramDecl.create_fn(id, args, body, return_type); } - fn parse_stmt(l: *Lexer, alloc: Allocator) !Stmt { + fn parse_stmt(l: *Lexer, alloc: Allocator) error{OutOfMemory}!Stmt { if (l.token == .var_) { // var declaration - return parse_var_decl_stmt(l, alloc); + const stmt = parse_var_decl_stmt(l, alloc); + l.eat(.semicolon); + return stmt; + } else if (l.token == .if_) { + return parse_if_stmt(l, alloc); } else { - return parse_no_var_decl_stmt(l, alloc); + const stmt = parse_no_var_decl_stmt(l, alloc); + l.eat(.semicolon); + return stmt; } } @@ -119,6 +125,56 @@ pub const Parser = struct { } }; } + fn parse_if_stmt(l: *Lexer, alloc: Allocator) !Stmt { + l.eat(.if_); + const if_eval = try parse_expr(l, alloc); + var if_body: std.ArrayList(Stmt) = .empty; + l.eat(.obrace); + while (l.token != .cbrace) { + const stmt = try parse_stmt(l, alloc); + try if_body.append(alloc, stmt); + } + l.eat(.cbrace); + var elseif_evals: std.ArrayList(Expr) = .empty; + var elseif_thens: std.ArrayList(std.ArrayList(Stmt)) = .empty; + + while (l.token == .elseif) { + l.eat(.elseif); + const eval = try parse_expr(l, alloc); + + var then: std.ArrayList(Stmt) = .empty; + l.eat(.obrace); + while (l.token != .cbrace) { + const stmt = try parse_stmt(l, alloc); + try then.append(alloc, stmt); + } + l.eat(.cbrace); + try elseif_evals.append(alloc, eval); + try elseif_thens.append(alloc, then); + } + + var else_then: ?std.ArrayList(Stmt) = null; + + if (l.token == .else_) { + l.eat(.else_); + l.eat(.obrace); + else_then = .empty; + while (l.token != .cbrace) { + const stmt = try parse_stmt(l, alloc); + try else_then.?.append(alloc, stmt); + } + l.eat(.cbrace); + } + + return Stmt.create_if( + if_eval, + if_body, + elseif_evals, + elseif_thens, + else_then, + ); + } + fn parse_expr(l: *Lexer, alloc: Allocator) error{OutOfMemory}!Expr { if (l.token == .id) { const next_l = l.nextl(); @@ -129,7 +185,6 @@ pub const Parser = struct { } else if (l.token == .str) { return parse_str(l); } - panic("parse_expr panics with {}, name: {s}", .{ l.token, l.name.as_str(l.content) }); }