diff --git a/src-self-hosted/ast.zig b/src-self-hosted/ast.zig index 40da01481c..5fd836b950 100644 --- a/src-self-hosted/ast.zig +++ b/src-self-hosted/ast.zig @@ -13,9 +13,9 @@ pub const Node = struct { Identifier, FnProto, ParamDecl, - AddrOfExpr, Block, - Return, + InfixOp, + PrefixOp, IntegerLiteral, FloatLiteral, }; @@ -27,9 +27,9 @@ pub const Node = struct { Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index), Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index), Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index), - Id.AddrOfExpr => @fieldParentPtr(NodeAddrOfExpr, "base", base).iterate(index), Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index), - Id.Return => @fieldParentPtr(NodeReturn, "base", base).iterate(index), + Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index), + Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index), Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index), Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index), }; @@ -42,9 +42,9 @@ pub const Node = struct { Id.Identifier => allocator.destroy(@fieldParentPtr(NodeIdentifier, "base", base)), Id.FnProto => allocator.destroy(@fieldParentPtr(NodeFnProto, "base", base)), Id.ParamDecl => allocator.destroy(@fieldParentPtr(NodeParamDecl, "base", base)), - Id.AddrOfExpr => allocator.destroy(@fieldParentPtr(NodeAddrOfExpr, "base", base)), Id.Block => allocator.destroy(@fieldParentPtr(NodeBlock, "base", base)), - Id.Return => allocator.destroy(@fieldParentPtr(NodeReturn, "base", base)), + Id.InfixOp => allocator.destroy(@fieldParentPtr(NodeInfixOp, "base", base)), + Id.PrefixOp => allocator.destroy(@fieldParentPtr(NodePrefixOp, "base", base)), Id.IntegerLiteral => allocator.destroy(@fieldParentPtr(NodeIntegerLiteral, "base", base)), Id.FloatLiteral => allocator.destroy(@fieldParentPtr(NodeFloatLiteral, "base", base)), }; @@ -170,31 +170,6 @@ pub const NodeParamDecl = struct { } }; -pub const NodeAddrOfExpr = struct { - base: Node, - op_token: Token, - align_expr: ?&Node, - bit_offset_start_token: ?Token, - bit_offset_end_token: ?Token, - const_token: ?Token, - volatile_token: ?Token, - op_expr: &Node, - - pub fn iterate(self: &NodeAddrOfExpr, index: usize) -> ?&Node { - var i = index; - - if (self.align_expr) |align_expr| { - if (i < 1) return align_expr; - i -= 1; - } - - if (i < 1) return self.op_expr; - i -= 1; - - return null; - } -}; - pub const NodeBlock = struct { base: Node, begin_token: Token, @@ -211,15 +186,68 @@ pub const NodeBlock = struct { } }; -pub const NodeReturn = struct { +pub const NodeInfixOp = struct { base: Node, - return_token: Token, - expr: &Node, + op_token: Token, + lhs: &Node, + op: InfixOp, + rhs: &Node, - pub fn iterate(self: &NodeReturn, index: usize) -> ?&Node { + const InfixOp = enum { + EqualEqual, + BangEqual, + }; + + pub fn iterate(self: &NodeInfixOp, index: usize) -> ?&Node { var i = index; - if (i < 1) return self.expr; + if (i < 1) return self.lhs; + i -= 1; + + switch (self.op) { + InfixOp.EqualEqual => {}, + InfixOp.BangEqual => {}, + } + + if (i < 1) return self.rhs; + i -= 1; + + return null; + } +}; + +pub const NodePrefixOp = struct { + base: Node, + op_token: Token, + op: PrefixOp, + rhs: &Node, + + const PrefixOp = union(enum) { + Return, + AddrOf: AddrOfInfo, + }; + const AddrOfInfo = struct { + align_expr: ?&Node, + bit_offset_start_token: ?Token, + bit_offset_end_token: ?Token, + const_token: ?Token, + volatile_token: ?Token, + }; + + pub fn iterate(self: &NodePrefixOp, index: usize) -> ?&Node { + var i = index; + + switch (self.op) { + PrefixOp.Return => {}, + PrefixOp.AddrOf => |addr_of_info| { + if (addr_of_info.align_expr) |align_expr| { + if (i < 1) return align_expr; + i -= 1; + } + }, + } + + if (i < 1) return self.rhs; i -= 1; return null; diff --git a/src-self-hosted/parser.zig b/src-self-hosted/parser.zig index 09e6ab8180..b34603f131 100644 --- a/src-self-hosted/parser.zig +++ b/src-self-hosted/parser.zig @@ -68,7 +68,12 @@ pub const Parser = struct { TopLevelExtern: ?Token, TopLevelDecl: TopLevelDeclCtx, Expression: DestPtr, - AddrOfModifiers: &ast.NodeAddrOfExpr, + ExpectOperand, + Operand: &ast.Node, + AfterOperand, + InfixOp: &ast.NodeInfixOp, + PrefixOp: &ast.NodePrefixOp, + AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo, TypeExpr: DestPtr, VarDecl: &ast.NodeVarDecl, VarDeclAlign: &ast.NodeVarDecl, @@ -265,63 +270,140 @@ pub const Parser = struct { _ = %return self.eatToken(token_id); continue; }, + State.Expression => |dest_ptr| { + // save the dest_ptr for later + stack.append(state) %% unreachable; + %return stack.append(State.ExpectOperand); + continue; + }, + State.ExpectOperand => { + // we'll either get an operand (like 1 or x), + // or a prefix operator (like ~ or return). const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_return => { - const return_node = %return self.createAttachReturn(dest_ptr, token); - stack.append(State {.Expression = DestPtr {.Field = &return_node.expr} }) %% unreachable; - continue; - }, - Token.Id.Identifier => { - _ = %return self.createAttachIdentifier(dest_ptr, token); - continue; - }, - Token.Id.IntegerLiteral => { - _ = %return self.createAttachIntegerLiteral(dest_ptr, token); - continue; - }, - Token.Id.FloatLiteral => { - _ = %return self.createAttachFloatLiteral(dest_ptr, token); + %return stack.append(State { .PrefixOp = %return self.createPrefixOp(token, + ast.NodePrefixOp.PrefixOp.Return) }); + %return stack.append(State.ExpectOperand); continue; }, Token.Id.Ampersand => { - const addr_of_expr = %return self.createAttachAddrOfExpr(dest_ptr, token); - stack.append(State { .AddrOfModifiers = addr_of_expr }) %% unreachable; + const prefix_op = %return self.createPrefixOp(token, ast.NodePrefixOp.PrefixOp{ + .AddrOf = ast.NodePrefixOp.AddrOfInfo { + .align_expr = null, + .bit_offset_start_token = null, + .bit_offset_end_token = null, + .const_token = null, + .volatile_token = null, + } + }); + %return stack.append(State { .PrefixOp = prefix_op }); + %return stack.append(State.ExpectOperand); + %return stack.append(State { .AddrOfModifiers = &prefix_op.op.AddrOf }); + continue; + }, + Token.Id.Identifier => { + %return stack.append(State { + .Operand = &(%return self.createIdentifier(token)).base + }); + %return stack.append(State.AfterOperand); + continue; + }, + Token.Id.IntegerLiteral => { + %return stack.append(State { + .Operand = &(%return self.createIntegerLiteral(token)).base + }); + %return stack.append(State.AfterOperand); + continue; + }, + Token.Id.FloatLiteral => { + %return stack.append(State { + .Operand = &(%return self.createFloatLiteral(token)).base + }); + %return stack.append(State.AfterOperand); continue; }, else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)), } }, - State.AddrOfModifiers => |addr_of_expr| { + State.AfterOperand => { + // we'll either get an infix operator (like != or ^), + // or a postfix operator (like () or {}), + // otherwise this expression is done (like on a ; or else). + var token = self.getNextToken(); + switch (token.id) { + Token.Id.EqualEqual => { + %return stack.append(State { + .InfixOp = %return self.createInfixOp(token, ast.NodeInfixOp.InfixOp.EqualEqual) + }); + %return stack.append(State.ExpectOperand); + continue; + }, + Token.Id.BangEqual => { + %return stack.append(State { + .InfixOp = %return self.createInfixOp(token, ast.NodeInfixOp.InfixOp.BangEqual) + }); + %return stack.append(State.ExpectOperand); + continue; + }, + else => { + // no postfix/infix operator after this operand. + self.putBackToken(token); + // reduce the stack + var expression: &ast.Node = stack.pop().Operand; + while (true) { + switch (stack.pop()) { + State.Expression => |dest_ptr| { + // we're done + %return dest_ptr.store(expression); + break; + }, + State.InfixOp => |infix_op| { + infix_op.rhs = expression; + infix_op.lhs = stack.pop().Operand; + expression = &infix_op.base; + continue; + }, + State.PrefixOp => |prefix_op| { + prefix_op.rhs = expression; + expression = &prefix_op.base; + continue; + }, + else => unreachable, + } + } + continue; + }, + } + }, + + State.AddrOfModifiers => |addr_of_info| { var token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_align => { - stack.append(State { .AddrOfModifiers = addr_of_expr }) %% unreachable; - if (addr_of_expr.align_expr != null) return self.parseError(token, "multiple align qualifiers"); + stack.append(state) %% unreachable; + if (addr_of_info.align_expr != null) return self.parseError(token, "multiple align qualifiers"); _ = %return self.eatToken(Token.Id.LParen); %return stack.append(State { .ExpectToken = Token.Id.RParen }); - %return stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_expr.align_expr} }); + %return stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_info.align_expr} }); continue; }, Token.Id.Keyword_const => { - if (addr_of_expr.const_token != null) return self.parseError(token, "duplicate qualifier: const"); - addr_of_expr.const_token = token; - stack.append(State { .AddrOfModifiers = addr_of_expr }) %% unreachable; + stack.append(state) %% unreachable; + if (addr_of_info.const_token != null) return self.parseError(token, "duplicate qualifier: const"); + addr_of_info.const_token = token; continue; }, Token.Id.Keyword_volatile => { - if (addr_of_expr.volatile_token != null) return self.parseError(token, "duplicate qualifier: volatile"); - addr_of_expr.volatile_token = token; - stack.append(State { .AddrOfModifiers = addr_of_expr }) %% unreachable; + stack.append(state) %% unreachable; + if (addr_of_info.volatile_token != null) return self.parseError(token, "duplicate qualifier: volatile"); + addr_of_info.volatile_token = token; continue; }, else => { self.putBackToken(token); - stack.append(State { - .Expression = DestPtr { .Field = &addr_of_expr.op_expr}, - }) %% unreachable; continue; }, } @@ -482,8 +564,14 @@ pub const Parser = struct { %return stack.append(State { .Expression = DestPtr{.List = &block.statements} }); continue; }, + + // These are data, not control flow. + State.InfixOp => unreachable, + State.PrefixOp => unreachable, + State.Operand => unreachable, } - unreachable; + @import("std").debug.panic("{}", @tagName(state)); + //unreachable; } } @@ -560,23 +648,6 @@ pub const Parser = struct { return node; } - fn createAddrOfExpr(self: &Parser, op_token: &const Token) -> %&ast.NodeAddrOfExpr { - const node = %return self.allocator.create(ast.NodeAddrOfExpr); - %defer self.allocator.destroy(node); - - *node = ast.NodeAddrOfExpr { - .base = ast.Node {.id = ast.Node.Id.AddrOfExpr}, - .align_expr = null, - .op_token = *op_token, - .bit_offset_start_token = null, - .bit_offset_end_token = null, - .const_token = null, - .volatile_token = null, - .op_expr = undefined, - }; - return node; - } - fn createBlock(self: &Parser, begin_token: &const Token) -> %&ast.NodeBlock { const node = %return self.allocator.create(ast.NodeBlock); %defer self.allocator.destroy(node); @@ -590,14 +661,29 @@ pub const Parser = struct { return node; } - fn createReturn(self: &Parser, return_token: &const Token) -> %&ast.NodeReturn { - const node = %return self.allocator.create(ast.NodeReturn); + fn createInfixOp(self: &Parser, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) -> %&ast.NodeInfixOp { + const node = %return self.allocator.create(ast.NodeInfixOp); %defer self.allocator.destroy(node); - *node = ast.NodeReturn { - .base = ast.Node {.id = ast.Node.Id.Return}, - .return_token = *return_token, - .expr = undefined, + *node = ast.NodeInfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp}, + .op_token = *op_token, + .lhs = undefined, + .op = *op, + .rhs = undefined, + }; + return node; + } + + fn createPrefixOp(self: &Parser, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) -> %&ast.NodePrefixOp { + const node = %return self.allocator.create(ast.NodePrefixOp); + %defer self.allocator.destroy(node); + + *node = ast.NodePrefixOp { + .base = ast.Node {.id = ast.Node.Id.PrefixOp}, + .op_token = *op_token, + .op = *op, + .rhs = undefined, }; return node; } @@ -635,20 +721,6 @@ pub const Parser = struct { return node; } - fn createAttachFloatLiteral(self: &Parser, dest_ptr: &const DestPtr, token: &const Token) -> %&ast.NodeFloatLiteral { - const node = %return self.createFloatLiteral(token); - %defer self.allocator.destroy(node); - %return dest_ptr.store(&node.base); - return node; - } - - fn createAttachIntegerLiteral(self: &Parser, dest_ptr: &const DestPtr, token: &const Token) -> %&ast.NodeIntegerLiteral { - const node = %return self.createIntegerLiteral(token); - %defer self.allocator.destroy(node); - %return dest_ptr.store(&node.base); - return node; - } - fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) -> %&ast.NodeIdentifier { const node = %return self.createIdentifier(name_token); %defer self.allocator.destroy(node); @@ -656,20 +728,6 @@ pub const Parser = struct { return node; } - fn createAttachReturn(self: &Parser, dest_ptr: &const DestPtr, return_token: &const Token) -> %&ast.NodeReturn { - const node = %return self.createReturn(return_token); - %defer self.allocator.destroy(node); - %return dest_ptr.store(&node.base); - return node; - } - - fn createAttachAddrOfExpr(self: &Parser, dest_ptr: &const DestPtr, op_token: &const Token) -> %&ast.NodeAddrOfExpr { - const node = %return self.createAddrOfExpr(op_token); - %defer self.allocator.destroy(node); - %return dest_ptr.store(&node.base); - return node; - } - fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) -> %&ast.NodeParamDecl { const node = %return self.createParamDecl(); %defer self.allocator.destroy(node); @@ -783,7 +841,6 @@ pub const Parser = struct { ParamDecl: &ast.Node, Text: []const u8, Expression: &ast.Node, - AddrOfExprBit: &ast.NodeAddrOfExpr, VarDecl: &ast.NodeVarDecl, Statement: &ast.Node, PrintIndent, @@ -912,17 +969,6 @@ pub const Parser = struct { const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base); %return stream.print("{}", self.tokenizer.getTokenSlice(identifier.name_token)); }, - ast.Node.Id.AddrOfExpr => { - const addr_of_expr = @fieldParentPtr(ast.NodeAddrOfExpr, "base", base); - %return stream.print("{}", self.tokenizer.getTokenSlice(addr_of_expr.op_token)); - %return stack.append(RenderState { .AddrOfExprBit = addr_of_expr}); - - if (addr_of_expr.align_expr) |align_expr| { - %return stream.print("align("); - %return stack.append(RenderState { .Text = ") "}); - %return stack.append(RenderState { .Expression = align_expr}); - } - }, ast.Node.Id.Block => { const block = @fieldParentPtr(ast.NodeBlock, "base", base); %return stream.write("{"); @@ -940,10 +986,43 @@ pub const Parser = struct { %return stack.append(RenderState { .Text = "\n" }); } }, - ast.Node.Id.Return => { - const return_node = @fieldParentPtr(ast.NodeReturn, "base", base); - %return stream.write("return "); - %return stack.append(RenderState { .Expression = return_node.expr }); + ast.Node.Id.InfixOp => { + const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base); + %return stack.append(RenderState { .Expression = prefix_op_node.rhs }); + switch (prefix_op_node.op) { + ast.NodeInfixOp.InfixOp.EqualEqual => { + %return stack.append(RenderState { .Text = " == "}); + }, + ast.NodeInfixOp.InfixOp.BangEqual => { + %return stack.append(RenderState { .Text = " != "}); + }, + else => unreachable, + } + %return stack.append(RenderState { .Expression = prefix_op_node.lhs }); + }, + ast.Node.Id.PrefixOp => { + const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base); + %return stack.append(RenderState { .Expression = prefix_op_node.rhs }); + switch (prefix_op_node.op) { + ast.NodePrefixOp.PrefixOp.Return => { + %return stream.write("return "); + }, + ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| { + %return stream.write("&"); + if (addr_of_info.volatile_token != null) { + %return stack.append(RenderState { .Text = "volatile "}); + } + if (addr_of_info.const_token != null) { + %return stack.append(RenderState { .Text = "const "}); + } + if (addr_of_info.align_expr) |align_expr| { + %return stream.print("align("); + %return stack.append(RenderState { .Text = ") "}); + %return stack.append(RenderState { .Expression = align_expr}); + } + }, + else => unreachable, + } }, ast.Node.Id.IntegerLiteral => { const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base); @@ -955,21 +1034,6 @@ pub const Parser = struct { }, else => unreachable, }, - RenderState.AddrOfExprBit => |addr_of_expr| { - if (addr_of_expr.bit_offset_start_token) |bit_offset_start_token| { - %return stream.print("{} ", self.tokenizer.getTokenSlice(bit_offset_start_token)); - } - if (addr_of_expr.bit_offset_end_token) |bit_offset_end_token| { - %return stream.print("{} ", self.tokenizer.getTokenSlice(bit_offset_end_token)); - } - if (addr_of_expr.const_token) |const_token| { - %return stream.print("{} ", self.tokenizer.getTokenSlice(const_token)); - } - if (addr_of_expr.volatile_token) |volatile_token| { - %return stream.print("{} ", self.tokenizer.getTokenSlice(volatile_token)); - } - %return stack.append(RenderState { .Expression = addr_of_expr.op_expr}); - }, RenderState.FnProtoRParen => |fn_proto| { %return stream.print(")"); if (fn_proto.align_expr != null) { @@ -1128,4 +1192,12 @@ test "zig fmt" { \\extern fn f3(s: &align(1) const volatile u8) -> c_int; \\ ); + + testCanonical( + \\fn f1(a: bool, b: bool) -> bool { + \\ a != b; + \\ return a == b; + \\} + \\ + ); } diff --git a/src-self-hosted/tokenizer.zig b/src-self-hosted/tokenizer.zig index 5d32a83af7..570d41fa7e 100644 --- a/src-self-hosted/tokenizer.zig +++ b/src-self-hosted/tokenizer.zig @@ -71,7 +71,10 @@ pub const Token = struct { StringLiteral: StrLitKind, Eof, Builtin, + Bang, Equal, + EqualEqual, + BangEqual, LParen, RParen, Semicolon, @@ -187,6 +190,8 @@ pub const Tokenizer = struct { C, StringLiteral, StringLiteralBackslash, + Equal, + Bang, Minus, Slash, LineComment, @@ -232,9 +237,10 @@ pub const Tokenizer = struct { result.id = Token.Id.Builtin; }, '=' => { - result.id = Token.Id.Equal; - self.index += 1; - break; + state = State.Equal; + }, + '!' => { + state = State.Bang; }, '(' => { result.id = Token.Id.LParen; @@ -356,6 +362,30 @@ pub const Tokenizer = struct { }, }, + State.Bang => switch (c) { + '=' => { + result.id = Token.Id.BangEqual; + self.index += 1; + break; + }, + else => { + result.id = Token.Id.Bang; + break; + }, + }, + + State.Equal => switch (c) { + '=' => { + result.id = Token.Id.EqualEqual; + self.index += 1; + break; + }, + else => { + result.id = Token.Id.Equal; + break; + }, + }, + State.Minus => switch (c) { '>' => { result.id = Token.Id.Arrow;