diff --git a/README.md b/README.md index f51db53..3f2d823 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # vig -a [v] parser in zig, also a shitpost taken too far +a [v] parser in zig [v]: https://vlang.io @@ -9,33 +9,20 @@ for more epic adventures) ## why - - because i want to learn parsers and what best to do it with a language i'm +because i want to learn parsers and what best to do it with a language i'm negatively charged towards - - theres an ast now it looks pretty - - i finally understand recursive descent parsers ## variations - `for` is split between `for` and `loop` because my fucking god i cant stand - having *four* different variations of `for` to parse. + having *four* different variations of `for`. - struct initialization is with `Struct.{}`, not `Struct{}`, to remove parsing - ambiguities (`if a {}` and `a{}`, v solves that with case, e.g structs Must - Be Properly Titled and i can't bother with that) - -## todo - - - no `for` yet - - no methods yet (`fn (v Type) blah() blah {...}`) - - do we really want a type system - - do we really want to output c - - do we really want to output llvm - - do we really want to output x86 + ambiguities ## how - - step 1: dab - - step 2: ``` +``` git clone https://gitdab.com/luna/vig.git cd vig zig build install --prefix ~/.local/ diff --git a/examples/hello.v b/examples/hello.v index 561bd45..9c1f047 100644 --- a/examples/hello.v +++ b/examples/hello.v @@ -37,7 +37,7 @@ fn main(a int) int { println('skirts') } - test('asd', 1, 2, 3) + cock_and_ball_torture('cbt', 1, 2, 3) return 23 @@ -48,7 +48,5 @@ fn main(a int) int { p.x = 69 - println(a.b(3).c(d)) - - v()()() + println(egg.scramble(3).with(cheddar)) } diff --git a/src/ast.zig b/src/ast.zig index 0ac53d6..c872fd2 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -14,6 +14,7 @@ pub const NodeType = enum { ConstDecl, Struct, Block, + Expr, Stmt, }; @@ -237,6 +238,7 @@ pub const Node = union(NodeType) { Block: StmtList, + Expr: *Expr, Stmt: *Stmt, pub fn mkRoot(allocator: *std.mem.Allocator) !*Node { diff --git a/src/ast_printer.zig b/src/ast_printer.zig index b386b32..668e687 100644 --- a/src/ast_printer.zig +++ b/src/ast_printer.zig @@ -70,6 +70,12 @@ pub fn printNode(node: *Node, ident: usize) void { } }, + .Expr => |expr| { + printIdent(ident); + printExpr(expr); + std.debug.warn("\n"); + }, + .Stmt => |stmt| { printIdent(ident); printStmt(ident, stmt); diff --git a/src/parser.zig b/src/parser.zig index 0a3007e..d3fa8bf 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -88,8 +88,6 @@ pub const Parser = struct { return token; } - /// Consume the current token type, then walk to the next token. - /// Returns the consumed token. fn consume(self: *Parser, ttype: TokenType, comptime msg: []const u8) !Token { if (self.check(ttype)) { var tok = self.peek(); @@ -101,15 +99,16 @@ pub const Parser = struct { return Result.CompileError; } - /// Consume the current token. Gives default error messages fn consumeSingle(self: *Parser, ttype: TokenType) !Token { + std.debug.warn("consume {}..?", ttype); + if (self.check(ttype)) { var cur = self.peek(); _ = try self.nextToken(); + std.debug.warn(" now has {}\n", self.peek()); return cur; } - // TODO maybe this could be entirely comptime? var buf_main: [1000]u8 = undefined; var buf = try std.fmt.bufPrint( buf_main[0..], @@ -117,12 +116,11 @@ pub const Parser = struct { ttype, self.peek().ttype, ); - try self.tokenError(self.peek(), buf); + return Result.CompileError; } - /// check() against multiple tokens fn compareAnyOf(self: *@This(), ttypes: []TokenType) bool { for (ttypes) |ttype| { if (self.check(ttype)) return true; @@ -131,8 +129,6 @@ pub const Parser = struct { return false; } - // TODO maybe move helper functions to ast_helper.zig? - fn mkFnDecl( self: *Parser, name: Token, @@ -164,6 +160,12 @@ pub const Parser = struct { return node; } + fn mkExpr(self: *Parser, expr: *Expr) !*ast.Node { + var node = try self.allocator.create(Node); + node.* = Node{ .Expr = expr }; + return node; + } + fn mkStmt(self: *Parser, stmt: *Stmt) !*ast.Node { var node = try self.allocator.create(Node); node.* = Node{ .Stmt = stmt }; @@ -409,20 +411,17 @@ pub const Parser = struct { errdefer consts.deinit(); _ = try self.consumeSingle(.Const); + _ = try self.consumeSingle(.LeftParen); while (self.peek().ttype != .RightParen) { const const_name = try self.consumeSingle(.Identifier); _ = try self.consumeSingle(.Equal); - // const declarations dont have type, a future type system must - // check the output type of the expression and assign it to the - // const later on. - var expr = try self.parseExpr(); try consts.append(ast.SingleConst{ .name = const_name, - .expr = expr, + .expr = expr.Expr, }); } @@ -464,27 +463,31 @@ pub const Parser = struct { .Struct => try self.parseStructDecl(), else => |ttype| blk: { - self.doError("expected Fn, Const, Struct, got {}\n", ttype); + self.doError("(basic) expected fn/const, got {}\n", ttype); return Result.CompileError; }, }; } - fn parseBlockInternal(self: *@This(), comptime T: type) !T { - var stmts = T.init(self.allocator); + fn parseBlock(self: *@This()) !*Node { + var stmts = ast.StmtList.init(self.allocator); errdefer stmts.deinit(); _ = try self.consumeSingle(.LeftBrace); while (self.peek().ttype != .RightBrace) { - var stmt = try self.parseStmt(); + var stmt = try self.parseDecl(); printer.printNode(try self.mkStmt(stmt), 0); try stmts.append(stmt); } _ = try self.consumeSingle(.RightBrace); - return stmts; + return try self.mkBlock(stmts); + } + + fn parseDecl(self: *@This()) !*Stmt { + return try self.parseStmt(); } fn parseStmt(self: *@This()) anyerror!*Stmt { @@ -493,25 +496,33 @@ pub const Parser = struct { .Loop => try self.parseLoop(), .Println => try self.parsePrintln(), .Return => try self.parseReturn(), + + // TODO make newlines tokens and consume newline? else => try self.parseStmtExpr(), }; } - /// Parse a list of statements. - fn parseBlock(self: *@This()) !*Node { - var stmts = try self.parseBlockInternal(ast.StmtList); - return try self.mkBlock(stmts); - } - - /// parse blocks inside statements + /// Copy of parseBlock for blocks in statements fn parseStmtBlock(self: *@This()) !ast.Block { - var block = try self.parseBlockInternal(ast.Block); + var block = ast.Block.init(self.allocator); + errdefer block.deinit(); + + _ = try self.consumeSingle(.LeftBrace); + + while (self.peek().ttype != .RightBrace) { + var stmt = try self.parseDecl(); + printer.printNode(try self.mkStmt(stmt), 0); + try block.append(stmt); + } + + _ = try self.consumeSingle(.RightBrace); + return block; } fn parseIfStmt(self: *@This()) !*Stmt { _ = try self.consumeSingle(.If); - var condition = try self.parseExpr(); + var condition = (try self.parseExpr()).Expr; const then_branch = try self.parseStmtBlock(); @@ -535,11 +546,11 @@ pub const Parser = struct { var expr: ?*Expr = null; var body: ast.Block = undefined; - // 'loop {' = infinite loop + // infinite loop if (self.check(.LeftBrace)) { body = try self.parseStmtBlock(); } else { - expr = try self.parseExpr(); + expr = (try self.parseExpr()).Expr; body = try self.parseStmtBlock(); } @@ -548,7 +559,7 @@ pub const Parser = struct { fn parseReturn(self: *@This()) !*Stmt { const tok = try self.consumeSingle(.Return); - const expr = try self.parseExpr(); + const expr = (try self.parseExpr()).Expr; return try Stmt.mkReturn(self.allocator, tok, expr); } @@ -556,23 +567,24 @@ pub const Parser = struct { _ = try self.consumeSingle(.Println); _ = try self.consumeSingle(.LeftParen); - var expr = try self.parseExpr(); + var expr = (try self.parseExpr()).Expr; _ = try self.consumeSingle(.RightParen); return try Stmt.mkPrintln(self.allocator, expr); } fn parseStmtExpr(self: *@This()) !*Stmt { - var expr = try self.parseExpr(); + var expr = (try self.parseExpr()).Expr; return try self.mkStmtExpr(expr); } - fn parseExpr(self: *@This()) anyerror!*Expr { - return try self.parseAssignment(); + fn parseExpr(self: *@This()) anyerror!*Node { + var expr: *Expr = try self.parseAssignment(); + return self.mkExpr(expr); } fn parseAssignment(self: *@This()) anyerror!*Expr { - // there can be two assignments coming out of this function: + // there can be two types coming out of this function: // - a mutable/immutable variable declaration with := // - an assignment to a variable with = @@ -580,50 +592,53 @@ pub const Parser = struct { // of this is an Expr, we wrap variable assignments in an Expr as well. var mutable: bool = false; + std.debug.warn("start assignment pass with cur={}\n", self.peek()); + if (self.check(.Mut)) { _ = try self.consumeSingle(.Mut); mutable = true; } var expr = try self.parseOr(); + std.debug.warn("lvalue: {}, cur: {}\n", expr, self.peek()); + + var value: *Expr = undefined; + + var op: Token = undefined; if (self.check(.ColonEqual) or self.check(.Equal)) { - return try self.finishAssignment(expr, mutable); + op = self.peek(); + _ = try self.nextToken(); + value = try self.parseAssignment(); + + switch (expr.*) { + .Variable => { + switch (op.ttype) { + .ColonEqual => return try self.mkVarDecl(expr.Variable, value, mutable), + .Equal => return try self.mkAssign(expr.Variable, value), + else => unreachable, + } + }, + + .Get => |get| { + if (op.ttype == .ColonEqual) { + self.doError("can not initialize struct field"); + return Result.CompileError; + } + + return try self.mkSet(get.struc, get.name, value); + }, + + else => |expr_typ| { + self.doError("Invalid assignment target {}", expr_typ); + return Result.CompileError; + }, + } } return expr; } - fn finishAssignment(self: *@This(), expr: *Expr, mutable: bool) !*Expr { - var op = self.peek(); - _ = try self.nextToken(); - var value = try self.parseAssignment(); - - switch (expr.*) { - .Variable => { - switch (op.ttype) { - .ColonEqual => return try self.mkVarDecl(expr.Variable, value, mutable), - .Equal => return try self.mkAssign(expr.Variable, value), - else => unreachable, - } - }, - - .Get => |get| { - if (op.ttype == .ColonEqual) { - self.doError("can not initialize struct field"); - return Result.CompileError; - } - - return try self.mkSet(get.struc, get.name, value); - }, - - else => |expr_typ| { - self.doError("Invalid assignment target {}", expr_typ); - return Result.CompileError; - }, - } - } - fn parseOr(self: *@This()) !*Expr { var expr = try self.parseAnd(); @@ -729,14 +744,18 @@ pub const Parser = struct { return expr; } - /// Parse either: - /// - A function call - /// - A struct initialization (Point.{...}) - /// - A struct Get expression (p.x) fn parseCall(self: *@This()) !*Expr { + // we parse a primary expression instead of consuming a .Identifier + // since parseCall is connected to the rest of the parser. doing + // identifiers would break the rest of the rules that want primaries. + + // nothing stops us from ensuring expr is a Variable though ;) var expr = try self.parsePrimary(); while (true) { + std.debug.warn("maybe fncall / struct: {}\n", self.peek().ttype); + printer.printExpr(expr); + if (self.check(.LeftParen)) { _ = try self.consumeSingle(.LeftParen); expr = try self.finishCall(expr); @@ -747,11 +766,7 @@ pub const Parser = struct { _ = try self.consumeSingle(.LeftBrace); expr = try self.finishStructVal(expr); } else { - var name = try self.consume( - .Identifier, - "Expect property name after '.'", - ); - + var name = try self.consume(.Identifier, "Expect property name after '.'"); expr = try self.mkGet(expr, name); } } else { @@ -769,17 +784,18 @@ pub const Parser = struct { if (!self.check(.RightParen)) { // emulating do-while really badly - var arg = try self.parseExpr(); + var arg = (try self.parseExpr()).Expr; try args.append(arg); while (self.check(.Comma)) { _ = try self.consumeSingle(.Comma); - arg = try self.parseExpr(); + arg = (try self.parseExpr()).Expr; try args.append(arg); } } + // TODO shouldnt consume() return the current token, not nextToken? var paren = try self.consume(.RightParen, "Expected ')' after arguments"); return self.mkCall(callee, paren, args); @@ -801,7 +817,7 @@ pub const Parser = struct { // TODO check .Comma for the quick initialization {val,val,val} _ = try self.consumeSingle(.Colon); - const field_value = try self.parseExpr(); + const field_value = (try self.parseExpr()).Expr; try inits.append(ast.StructInit{ .field = field_name, @@ -829,7 +845,7 @@ pub const Parser = struct { .LeftParen => blk: { _ = try self.nextToken(); - var expr = try self.parseExpr(); + var expr = (try self.parseExpr()).Expr; _ = try self.consume(.RightParen, "Expected ')' after expression"); // for groupings, we don't want to skip tokens as we already @@ -844,6 +860,7 @@ pub const Parser = struct { }; _ = try self.nextToken(); + return expr; } };