diff --git a/README.md b/README.md index 3f2d823..f51db53 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # vig -a [v] parser in zig +a [v] parser in zig, also a shitpost taken too far [v]: https://vlang.io @@ -9,20 +9,33 @@ 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`. + having *four* different variations of `for` to parse. - struct initialization is with `Struct.{}`, not `Struct{}`, to remove parsing - ambiguities + 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 ## 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 9c1f047..561bd45 100644 --- a/examples/hello.v +++ b/examples/hello.v @@ -37,7 +37,7 @@ fn main(a int) int { println('skirts') } - cock_and_ball_torture('cbt', 1, 2, 3) + test('asd', 1, 2, 3) return 23 @@ -48,5 +48,7 @@ fn main(a int) int { p.x = 69 - println(egg.scramble(3).with(cheddar)) + println(a.b(3).c(d)) + + v()()() } diff --git a/src/ast.zig b/src/ast.zig index c872fd2..0ac53d6 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -14,7 +14,6 @@ pub const NodeType = enum { ConstDecl, Struct, Block, - Expr, Stmt, }; @@ -238,7 +237,6 @@ 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 668e687..b386b32 100644 --- a/src/ast_printer.zig +++ b/src/ast_printer.zig @@ -70,12 +70,6 @@ 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 d3fa8bf..0a3007e 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -88,6 +88,8 @@ 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(); @@ -99,16 +101,15 @@ 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..], @@ -116,11 +117,12 @@ pub const Parser = struct { ttype, self.peek().ttype, ); - try self.tokenError(self.peek(), buf); + 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; @@ -129,6 +131,8 @@ pub const Parser = struct { return false; } + // TODO maybe move helper functions to ast_helper.zig? + fn mkFnDecl( self: *Parser, name: Token, @@ -160,12 +164,6 @@ 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 }; @@ -411,17 +409,20 @@ 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, }); } @@ -463,31 +464,27 @@ pub const Parser = struct { .Struct => try self.parseStructDecl(), else => |ttype| blk: { - self.doError("(basic) expected fn/const, got {}\n", ttype); + self.doError("expected Fn, Const, Struct, got {}\n", ttype); return Result.CompileError; }, }; } - fn parseBlock(self: *@This()) !*Node { - var stmts = ast.StmtList.init(self.allocator); + fn parseBlockInternal(self: *@This(), comptime T: type) !T { + var stmts = T.init(self.allocator); errdefer stmts.deinit(); _ = try self.consumeSingle(.LeftBrace); while (self.peek().ttype != .RightBrace) { - var stmt = try self.parseDecl(); + var stmt = try self.parseStmt(); printer.printNode(try self.mkStmt(stmt), 0); try stmts.append(stmt); } _ = try self.consumeSingle(.RightBrace); - return try self.mkBlock(stmts); - } - - fn parseDecl(self: *@This()) !*Stmt { - return try self.parseStmt(); + return stmts; } fn parseStmt(self: *@This()) anyerror!*Stmt { @@ -496,33 +493,25 @@ 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(), }; } - /// Copy of parseBlock for blocks in statements + /// 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 fn parseStmtBlock(self: *@This()) !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); - + var block = try self.parseBlockInternal(ast.Block); return block; } fn parseIfStmt(self: *@This()) !*Stmt { _ = try self.consumeSingle(.If); - var condition = (try self.parseExpr()).Expr; + var condition = try self.parseExpr(); const then_branch = try self.parseStmtBlock(); @@ -546,11 +535,11 @@ pub const Parser = struct { var expr: ?*Expr = null; var body: ast.Block = undefined; - // infinite loop + // 'loop {' = infinite loop if (self.check(.LeftBrace)) { body = try self.parseStmtBlock(); } else { - expr = (try self.parseExpr()).Expr; + expr = try self.parseExpr(); body = try self.parseStmtBlock(); } @@ -559,7 +548,7 @@ pub const Parser = struct { fn parseReturn(self: *@This()) !*Stmt { const tok = try self.consumeSingle(.Return); - const expr = (try self.parseExpr()).Expr; + const expr = try self.parseExpr(); return try Stmt.mkReturn(self.allocator, tok, expr); } @@ -567,24 +556,23 @@ pub const Parser = struct { _ = try self.consumeSingle(.Println); _ = try self.consumeSingle(.LeftParen); - var expr = (try self.parseExpr()).Expr; + var expr = try self.parseExpr(); _ = try self.consumeSingle(.RightParen); return try Stmt.mkPrintln(self.allocator, expr); } fn parseStmtExpr(self: *@This()) !*Stmt { - var expr = (try self.parseExpr()).Expr; + var expr = try self.parseExpr(); return try self.mkStmtExpr(expr); } - fn parseExpr(self: *@This()) anyerror!*Node { - var expr: *Expr = try self.parseAssignment(); - return self.mkExpr(expr); + fn parseExpr(self: *@This()) anyerror!*Expr { + return try self.parseAssignment(); } fn parseAssignment(self: *@This()) anyerror!*Expr { - // there can be two types coming out of this function: + // there can be two assignments coming out of this function: // - a mutable/immutable variable declaration with := // - an assignment to a variable with = @@ -592,53 +580,50 @@ 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)) { - 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 try self.finishAssignment(expr, mutable); } 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(); @@ -744,18 +729,14 @@ 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); @@ -766,7 +747,11 @@ 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 { @@ -784,18 +769,17 @@ pub const Parser = struct { if (!self.check(.RightParen)) { // emulating do-while really badly - var arg = (try self.parseExpr()).Expr; + var arg = try self.parseExpr(); try args.append(arg); while (self.check(.Comma)) { _ = try self.consumeSingle(.Comma); - arg = (try self.parseExpr()).Expr; + arg = try self.parseExpr(); 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); @@ -817,7 +801,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()).Expr; + const field_value = try self.parseExpr(); try inits.append(ast.StructInit{ .field = field_name, @@ -845,7 +829,7 @@ pub const Parser = struct { .LeftParen => blk: { _ = try self.nextToken(); - var expr = (try self.parseExpr()).Expr; + var expr = try self.parseExpr(); _ = try self.consume(.RightParen, "Expected ')' after expression"); // for groupings, we don't want to skip tokens as we already @@ -860,7 +844,6 @@ pub const Parser = struct { }; _ = try self.nextToken(); - return expr; } };