From 5bb57116c05d57f3faa64719da0690bdc8c37eed Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 26 Aug 2019 14:26:58 -0300 Subject: [PATCH 1/7] update readme --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3f2d823..b8657f5 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,26 @@ 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 + - theres no `for` yet + - `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) ## how -``` + - step 1: dab + - step 2: ``` git clone https://gitdab.com/luna/vig.git cd vig zig build install --prefix ~/.local/ From 3cd19e6515a53a2b18f9788edbd40af3f71104dd Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 26 Aug 2019 14:30:13 -0300 Subject: [PATCH 2/7] add todo to readme --- README.md | 11 +++++++++-- examples/hello.v | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b8657f5..f51db53 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,6 @@ negatively charged towards ## variations - - theres no `for` yet - - `for` is split between `for` and `loop` because my fucking god i cant stand having *four* different variations of `for` to parse. @@ -25,6 +23,15 @@ negatively charged towards 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 diff --git a/examples/hello.v b/examples/hello.v index 9c1f047..28df0e2 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,5 @@ fn main(a int) int { p.x = 69 - println(egg.scramble(3).with(cheddar)) + println(a.b(3).c(d)) } From b382c136ec6f4bab8a1496ef1e1f22fa19301990 Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 26 Aug 2019 14:46:00 -0300 Subject: [PATCH 3/7] parser: make parseExpr return Expr instead of Node --- src/parser.zig | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/parser.zig b/src/parser.zig index d3fa8bf..535783d 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -129,6 +129,8 @@ pub const Parser = struct { return false; } + // TODO maybe move helper functions to ast_helper.zig? + fn mkFnDecl( self: *Parser, name: Token, @@ -411,17 +413,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, }); } @@ -522,7 +527,7 @@ pub const Parser = struct { 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(); @@ -550,7 +555,7 @@ pub const Parser = struct { if (self.check(.LeftBrace)) { body = try self.parseStmtBlock(); } else { - expr = (try self.parseExpr()).Expr; + expr = try self.parseExpr(); body = try self.parseStmtBlock(); } @@ -559,7 +564,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,20 +572,19 @@ 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 { @@ -784,13 +788,13 @@ 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); } } @@ -817,7 +821,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 +849,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 From 1c8eda7305eb0c9ff8003c9c919a546188802990 Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 26 Aug 2019 14:48:53 -0300 Subject: [PATCH 4/7] ast: remove Expr Node (lives under Stmt) --- src/ast.zig | 2 -- src/ast_printer.zig | 6 ------ src/parser.zig | 6 ------ 3 files changed, 14 deletions(-) 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 535783d..8ded3a2 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -162,12 +162,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 }; From c3f0b4b4d5cbebe2bb0b4027a40c527f9e19e64d Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 26 Aug 2019 14:59:14 -0300 Subject: [PATCH 5/7] parser: simplify parseBlock --- src/parser.zig | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/parser.zig b/src/parser.zig index 8ded3a2..0f3ffe6 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -462,31 +462,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 { @@ -495,27 +491,19 @@ 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; } From 9e32ff9e16bda64d82b7138a506a0833cfa9d761 Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 26 Aug 2019 15:19:31 -0300 Subject: [PATCH 6/7] parser: split into finishAssignment --- src/parser.zig | 69 ++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/src/parser.zig b/src/parser.zig index 0f3ffe6..d00465f 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -533,7 +533,7 @@ 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 { @@ -570,7 +570,7 @@ pub const Parser = struct { } 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 = @@ -578,53 +578,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(); From 9eaa71cbd6953b0913fdb0154e57a3504b510e11 Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 26 Aug 2019 15:29:59 -0300 Subject: [PATCH 7/7] remove some debug calls --- examples/hello.v | 2 ++ src/parser.zig | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/examples/hello.v b/examples/hello.v index 28df0e2..561bd45 100644 --- a/examples/hello.v +++ b/examples/hello.v @@ -49,4 +49,6 @@ fn main(a int) int { p.x = 69 println(a.b(3).c(d)) + + v()()() } diff --git a/src/parser.zig b/src/parser.zig index d00465f..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; @@ -727,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); @@ -749,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 { @@ -778,7 +780,6 @@ pub const Parser = struct { } } - // TODO shouldnt consume() return the current token, not nextToken? var paren = try self.consume(.RightParen, "Expected ')' after arguments"); return self.mkCall(callee, paren, args); @@ -843,7 +844,6 @@ pub const Parser = struct { }; _ = try self.nextToken(); - return expr; } };