diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 230fe26568..17a19e6213 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -283,13 +283,13 @@ pub const NodeUse = struct { pub const NodeErrorSetDecl = struct { base: Node, error_token: Token, - decls: ArrayList(&NodeIdentifier), + decls: ArrayList(&Node), rbrace_token: Token, pub fn iterate(self: &NodeErrorSetDecl, index: usize) ?&Node { var i = index; - if (i < self.decls.len) return &self.decls.at(i).base; + if (i < self.decls.len) return self.decls.at(i); i -= self.decls.len; return null; @@ -676,13 +676,13 @@ pub const NodeComptime = struct { pub const NodePayload = struct { base: Node, lpipe: Token, - error_symbol: &NodeIdentifier, + error_symbol: &Node, rpipe: Token, pub fn iterate(self: &NodePayload, index: usize) ?&Node { var i = index; - if (i < 1) return &self.error_symbol.base; + if (i < 1) return self.error_symbol; i -= 1; return null; @@ -700,14 +700,14 @@ pub const NodePayload = struct { pub const NodePointerPayload = struct { base: Node, lpipe: Token, - is_ptr: bool, - value_symbol: &NodeIdentifier, + ptr_token: ?Token, + value_symbol: &Node, rpipe: Token, pub fn iterate(self: &NodePointerPayload, index: usize) ?&Node { var i = index; - if (i < 1) return &self.value_symbol.base; + if (i < 1) return self.value_symbol; i -= 1; return null; @@ -725,19 +725,19 @@ pub const NodePointerPayload = struct { pub const NodePointerIndexPayload = struct { base: Node, lpipe: Token, - is_ptr: bool, - value_symbol: &NodeIdentifier, - index_symbol: ?&NodeIdentifier, + ptr_token: ?Token, + value_symbol: &Node, + index_symbol: ?&Node, rpipe: Token, pub fn iterate(self: &NodePointerIndexPayload, index: usize) ?&Node { var i = index; - if (i < 1) return &self.value_symbol.base; + if (i < 1) return self.value_symbol; i -= 1; if (self.index_symbol) |index_symbol| { - if (i < 1) return &index_symbol.base; + if (i < 1) return index_symbol; i -= 1; } @@ -756,14 +756,14 @@ pub const NodePointerIndexPayload = struct { pub const NodeElse = struct { base: Node, else_token: Token, - payload: ?&NodePayload, + payload: ?&Node, body: &Node, pub fn iterate(self: &NodeElse, index: usize) ?&Node { var i = index; if (self.payload) |payload| { - if (i < 1) return &payload.base; + if (i < 1) return payload; i -= 1; } @@ -813,7 +813,7 @@ pub const NodeSwitch = struct { pub const NodeSwitchCase = struct { base: Node, items: ArrayList(&Node), - payload: ?&NodePointerPayload, + payload: ?&Node, expr: &Node, pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node { @@ -823,7 +823,7 @@ pub const NodeSwitchCase = struct { i -= self.items.len; if (self.payload) |payload| { - if (i < 1) return &payload.base; + if (i < 1) return payload; i -= 1; } @@ -865,7 +865,7 @@ pub const NodeWhile = struct { inline_token: ?Token, while_token: Token, condition: &Node, - payload: ?&NodePointerPayload, + payload: ?&Node, continue_expr: ?&Node, body: &Node, @"else": ?&NodeElse, @@ -877,7 +877,7 @@ pub const NodeWhile = struct { i -= 1; if (self.payload) |payload| { - if (i < 1) return &payload.base; + if (i < 1) return payload; i -= 1; } @@ -924,7 +924,7 @@ pub const NodeFor = struct { inline_token: ?Token, for_token: Token, array_expr: &Node, - payload: ?&NodePointerIndexPayload, + payload: ?&Node, body: &Node, @"else": ?&NodeElse, @@ -935,7 +935,7 @@ pub const NodeFor = struct { i -= 1; if (self.payload) |payload| { - if (i < 1) return &payload.base; + if (i < 1) return payload; i -= 1; } @@ -975,7 +975,7 @@ pub const NodeIf = struct { base: Node, if_token: Token, condition: &Node, - payload: ?&NodePointerPayload, + payload: ?&Node, body: &Node, @"else": ?&NodeElse, @@ -986,7 +986,7 @@ pub const NodeIf = struct { i -= 1; if (self.payload) |payload| { - if (i < 1) return &payload.base; + if (i < 1) return payload; i -= 1; } @@ -1048,7 +1048,7 @@ pub const NodeInfixOp = struct { BitXor, BoolAnd, BoolOr, - Catch: ?&NodePayload, + Catch: ?&Node, Div, EqualEqual, ErrorUnion, @@ -1076,7 +1076,7 @@ pub const NodeInfixOp = struct { switch (self.op) { InfixOp.Catch => |maybe_payload| { if (maybe_payload) |payload| { - if (i < 1) return &payload.base; + if (i < 1) return payload; i -= 1; } }, @@ -1344,14 +1344,30 @@ pub const NodeControlFlowExpression = struct { rhs: ?&Node, const Kind = union(enum) { - Break: ?Token, - Continue: ?Token, + Break: ?&Node, + Continue: ?&Node, Return, }; pub fn iterate(self: &NodeControlFlowExpression, index: usize) ?&Node { var i = index; + switch (self.kind) { + Kind.Break => |maybe_label| { + if (maybe_label) |label| { + if (i < 1) return label; + i -= 1; + } + }, + Kind.Continue => |maybe_label| { + if (maybe_label) |label| { + if (i < 1) return label; + i -= 1; + } + }, + Kind.Return => {}, + } + if (self.rhs) |rhs| { if (i < 1) return rhs; i -= 1; @@ -1370,14 +1386,14 @@ pub const NodeControlFlowExpression = struct { } switch (self.kind) { - Kind.Break => |maybe_blk_token| { - if (maybe_blk_token) |blk_token| { - return blk_token; + Kind.Break => |maybe_label| { + if (maybe_label) |label| { + return label.lastToken(); } }, - Kind.Continue => |maybe_blk_token| { - if (maybe_blk_token) |blk_token| { - return blk_token; + Kind.Continue => |maybe_label| { + if (maybe_label) |label| { + return label.lastToken(); } }, Kind.Return => return self.ltoken, @@ -1390,14 +1406,14 @@ pub const NodeControlFlowExpression = struct { pub const NodeSuspend = struct { base: Node, suspend_token: Token, - payload: ?&NodePayload, + payload: ?&Node, body: ?&Node, pub fn iterate(self: &NodeSuspend, index: usize) ?&Node { var i = index; if (self.payload) |payload| { - if (i < 1) return &payload.base; + if (i < 1) return payload; i -= 1; } @@ -1605,7 +1621,7 @@ pub const NodeThisLiteral = struct { pub const NodeAsmOutput = struct { base: Node, - symbolic_name: &NodeIdentifier, + symbolic_name: &Node, constraint: &Node, kind: Kind, @@ -1617,7 +1633,7 @@ pub const NodeAsmOutput = struct { pub fn iterate(self: &NodeAsmOutput, index: usize) ?&Node { var i = index; - if (i < 1) return &self.symbolic_name.base; + if (i < 1) return self.symbolic_name; i -= 1; if (i < 1) return self.constraint; @@ -1651,14 +1667,14 @@ pub const NodeAsmOutput = struct { pub const NodeAsmInput = struct { base: Node, - symbolic_name: &NodeIdentifier, + symbolic_name: &Node, constraint: &Node, expr: &Node, pub fn iterate(self: &NodeAsmInput, index: usize) ?&Node { var i = index; - if (i < 1) return &self.symbolic_name.base; + if (i < 1) return self.symbolic_name; i -= 1; if (i < 1) return self.constraint; @@ -1682,7 +1698,7 @@ pub const NodeAsmInput = struct { pub const NodeAsm = struct { base: Node, asm_token: Token, - is_volatile: bool, + volatile_token: ?Token, template: &Node, //tokens: ArrayList(AsmToken), outputs: ArrayList(&NodeAsmOutput), diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 1aa299b49a..c0708581ea 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -59,29 +59,29 @@ pub const Parser = struct { lib_name: ?&ast.Node, }; - const ContainerExternCtx = struct { - dest_ptr: DestPtr, - ltoken: Token, - layout: ast.NodeContainerDecl.Layout, + const VarDeclCtx = struct { + mut_token: Token, + visib_token: ?Token, + comptime_token: ?Token, + extern_export_token: ?Token, + lib_name: ?&ast.Node, + list: &ArrayList(&ast.Node), }; - const DestPtr = union(enum) { - Field: &&ast.Node, - NullableField: &?&ast.Node, + const TopLevelExternOrFieldCtx = struct { + visib_token: Token, + container_decl: &ast.NodeContainerDecl, + }; - pub fn store(self: &const DestPtr, value: &ast.Node) void { - switch (*self) { - DestPtr.Field => |ptr| *ptr = value, - DestPtr.NullableField => |ptr| *ptr = value, - } - } + const ExternTypeCtx = struct { + opt_ctx: OptionalCtx, + extern_token: Token, + }; - pub fn get(self: &const DestPtr) &ast.Node { - switch (*self) { - DestPtr.Field => |ptr| return *ptr, - DestPtr.NullableField => |ptr| return ??*ptr, - } - } + const ContainerKindCtx = struct { + opt_ctx: OptionalCtx, + ltoken: Token, + layout: ast.NodeContainerDecl.Layout, }; const ExpectTokenSave = struct { @@ -89,13 +89,9 @@ pub const Parser = struct { ptr: &Token, }; - const RevertState = struct { - parser: Parser, - tokenizer: Tokenizer, - - // We expect, that if something is optional, then there is a field, - // that needs to be set to null, when we revert. - ptr: &?&ast.Node, + const OptionalTokenSave = struct { + id: Token.Id, + ptr: &?Token, }; const ExprListCtx = struct { @@ -104,11 +100,6 @@ pub const Parser = struct { ptr: &Token, }; - const ElseCtx = struct { - payload: ?DestPtr, - body: DestPtr, - }; - fn ListSave(comptime T: type) type { return struct { list: &ArrayList(T), @@ -116,120 +107,196 @@ pub const Parser = struct { }; } + const MaybeLabeledExpressionCtx = struct { + label: Token, + opt_ctx: OptionalCtx, + }; + const LabelCtx = struct { label: ?Token, - dest_ptr: DestPtr, + opt_ctx: OptionalCtx, }; const InlineCtx = struct { label: ?Token, inline_token: ?Token, - dest_ptr: DestPtr, + opt_ctx: OptionalCtx, }; const LoopCtx = struct { label: ?Token, inline_token: ?Token, loop_token: Token, - dest_ptr: DestPtr, + opt_ctx: OptionalCtx, }; const AsyncEndCtx = struct { - dest_ptr: DestPtr, + ctx: OptionalCtx, attribute: &ast.NodeAsyncAttribute, }; + const ErrorTypeOrSetDeclCtx = struct { + opt_ctx: OptionalCtx, + error_token: Token, + }; + + const ParamDeclEndCtx = struct { + fn_proto: &ast.NodeFnProto, + param_decl: &ast.NodeParamDecl, + }; + + const ComptimeStatementCtx = struct { + comptime_token: Token, + block: &ast.NodeBlock, + }; + + const OptionalCtx = union(enum) { + Optional: &?&ast.Node, + RequiredNull: &?&ast.Node, + Required: &&ast.Node, + + pub fn store(self: &const OptionalCtx, value: &ast.Node) void { + switch (*self) { + OptionalCtx.Optional => |ptr| *ptr = value, + OptionalCtx.RequiredNull => |ptr| *ptr = value, + OptionalCtx.Required => |ptr| *ptr = value, + } + } + + pub fn get(self: &const OptionalCtx) ?&ast.Node { + switch (*self) { + OptionalCtx.Optional => |ptr| return *ptr, + OptionalCtx.RequiredNull => |ptr| return ??*ptr, + OptionalCtx.Required => |ptr| return *ptr, + } + } + + pub fn toRequired(self: &const OptionalCtx) OptionalCtx { + switch (*self) { + OptionalCtx.Optional => |ptr| { + return OptionalCtx { .RequiredNull = ptr }; + }, + OptionalCtx.RequiredNull => |ptr| return *self, + OptionalCtx.Required => |ptr| return *self, + } + } + }; + const State = union(enum) { TopLevel, TopLevelExtern: TopLevelDeclCtx, TopLevelLibname: TopLevelDeclCtx, TopLevelDecl: TopLevelDeclCtx, - ContainerExtern: ContainerExternCtx, + TopLevelExternOrField: TopLevelExternOrFieldCtx, + + ContainerKind: ContainerKindCtx, + ContainerInitArgStart: &ast.NodeContainerDecl, + ContainerInitArg: &ast.NodeContainerDecl, ContainerDecl: &ast.NodeContainerDecl, - SliceOrArrayAccess: &ast.NodeSuffixOp, - AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo, - VarDecl: &ast.NodeVarDecl, + + VarDecl: VarDeclCtx, VarDeclAlign: &ast.NodeVarDecl, VarDeclEq: &ast.NodeVarDecl, - IfToken: @TagType(Token.Id), - IfTokenSave: ExpectTokenSave, - ExpectToken: @TagType(Token.Id), - ExpectTokenSave: ExpectTokenSave, + + FnDef: &ast.NodeFnProto, FnProto: &ast.NodeFnProto, FnProtoAlign: &ast.NodeFnProto, FnProtoReturnType: &ast.NodeFnProto, + ParamDecl: &ast.NodeFnProto, - ParamDeclComma, - FnDef: &ast.NodeFnProto, + ParamDeclAliasOrComptime: &ast.NodeParamDecl, + ParamDeclName: &ast.NodeParamDecl, + ParamDeclEnd: ParamDeclEndCtx, + ParamDeclComma: &ast.NodeFnProto, + + MaybeLabeledExpression: MaybeLabeledExpressionCtx, LabeledExpression: LabelCtx, Inline: InlineCtx, While: LoopCtx, - For: LoopCtx, - Block: &ast.NodeBlock, - Else: &?&ast.NodeElse, WhileContinueExpr: &?&ast.Node, + For: LoopCtx, + Else: &?&ast.NodeElse, + + Block: &ast.NodeBlock, Statement: &ast.NodeBlock, - Semicolon: &const &const ast.Node, + ComptimeStatement: ComptimeStatementCtx, + Semicolon: &&ast.Node, + AsmOutputItems: &ArrayList(&ast.NodeAsmOutput), + AsmOutputReturnOrType: &ast.NodeAsmOutput, AsmInputItems: &ArrayList(&ast.NodeAsmInput), AsmClopperItems: &ArrayList(&ast.Node), + ExprListItemOrEnd: ExprListCtx, ExprListCommaOrEnd: ExprListCtx, FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer), FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer), FieldListCommaOrEnd: &ast.NodeContainerDecl, - IdentifierListItemOrEnd: ListSave(&ast.NodeIdentifier), - IdentifierListCommaOrEnd: ListSave(&ast.NodeIdentifier), + IdentifierListItemOrEnd: ListSave(&ast.Node), + IdentifierListCommaOrEnd: ListSave(&ast.Node), SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase), - SuspendBody: &ast.NodeSuspend, - AsyncEnd: AsyncEndCtx, - Payload: &?&ast.NodePayload, - PointerPayload: &?&ast.NodePointerPayload, - PointerIndexPayload: &?&ast.NodePointerIndexPayload, SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase), + SwitchCaseFirstItem: &ArrayList(&ast.Node), SwitchCaseItem: &ArrayList(&ast.Node), SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node), - /// A state that can be appended before any other State. If an error occures, - /// the parser will first try looking for the closest optional state. If an - /// optional state is found, the parser will revert to the state it was in - /// when the optional was added. This will polute the arena allocator with - /// "leaked" nodes. TODO: Figure out if it's nessesary to handle leaked nodes. - Optional: RevertState, + SuspendBody: &ast.NodeSuspend, + AsyncAllocator: &ast.NodeAsyncAttribute, + AsyncEnd: AsyncEndCtx, - Expression: DestPtr, - RangeExpressionBegin: DestPtr, - RangeExpressionEnd: DestPtr, - AssignmentExpressionBegin: DestPtr, - AssignmentExpressionEnd: DestPtr, - UnwrapExpressionBegin: DestPtr, - UnwrapExpressionEnd: DestPtr, - BoolOrExpressionBegin: DestPtr, - BoolOrExpressionEnd: DestPtr, - BoolAndExpressionBegin: DestPtr, - BoolAndExpressionEnd: DestPtr, - ComparisonExpressionBegin: DestPtr, - ComparisonExpressionEnd: DestPtr, - BinaryOrExpressionBegin: DestPtr, - BinaryOrExpressionEnd: DestPtr, - BinaryXorExpressionBegin: DestPtr, - BinaryXorExpressionEnd: DestPtr, - BinaryAndExpressionBegin: DestPtr, - BinaryAndExpressionEnd: DestPtr, - BitShiftExpressionBegin: DestPtr, - BitShiftExpressionEnd: DestPtr, - AdditionExpressionBegin: DestPtr, - AdditionExpressionEnd: DestPtr, - MultiplyExpressionBegin: DestPtr, - MultiplyExpressionEnd: DestPtr, - CurlySuffixExpressionBegin: DestPtr, - CurlySuffixExpressionEnd: DestPtr, - TypeExprBegin: DestPtr, - TypeExprEnd: DestPtr, - PrefixOpExpression: DestPtr, - SuffixOpExpressionBegin: DestPtr, - SuffixOpExpressionEnd: DestPtr, - PrimaryExpression: DestPtr, + ExternType: ExternTypeCtx, + SliceOrArrayAccess: &ast.NodeSuffixOp, + SliceOrArrayType: &ast.NodePrefixOp, + AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo, + + Payload: OptionalCtx, + PointerPayload: OptionalCtx, + PointerIndexPayload: OptionalCtx, + + Expression: OptionalCtx, + RangeExpressionBegin: OptionalCtx, + RangeExpressionEnd: OptionalCtx, + AssignmentExpressionBegin: OptionalCtx, + AssignmentExpressionEnd: OptionalCtx, + UnwrapExpressionBegin: OptionalCtx, + UnwrapExpressionEnd: OptionalCtx, + BoolOrExpressionBegin: OptionalCtx, + BoolOrExpressionEnd: OptionalCtx, + BoolAndExpressionBegin: OptionalCtx, + BoolAndExpressionEnd: OptionalCtx, + ComparisonExpressionBegin: OptionalCtx, + ComparisonExpressionEnd: OptionalCtx, + BinaryOrExpressionBegin: OptionalCtx, + BinaryOrExpressionEnd: OptionalCtx, + BinaryXorExpressionBegin: OptionalCtx, + BinaryXorExpressionEnd: OptionalCtx, + BinaryAndExpressionBegin: OptionalCtx, + BinaryAndExpressionEnd: OptionalCtx, + BitShiftExpressionBegin: OptionalCtx, + BitShiftExpressionEnd: OptionalCtx, + AdditionExpressionBegin: OptionalCtx, + AdditionExpressionEnd: OptionalCtx, + MultiplyExpressionBegin: OptionalCtx, + MultiplyExpressionEnd: OptionalCtx, + CurlySuffixExpressionBegin: OptionalCtx, + CurlySuffixExpressionEnd: OptionalCtx, + TypeExprBegin: OptionalCtx, + TypeExprEnd: OptionalCtx, + PrefixOpExpression: OptionalCtx, + SuffixOpExpressionBegin: OptionalCtx, + SuffixOpExpressionEnd: OptionalCtx, + PrimaryExpression: OptionalCtx, + + ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx, + StringLiteral: OptionalCtx, + Identifier: OptionalCtx, + + + IfToken: @TagType(Token.Id), + IfTokenSave: ExpectTokenSave, + ExpectToken: @TagType(Token.Id), + ExpectTokenSave: ExpectTokenSave, + OptionalTokenSave: OptionalTokenSave, }; /// Returns an AST tree, allocated with the parser's allocator. @@ -302,31 +369,31 @@ pub const Parser = struct { Token.Id.Keyword_test => { stack.append(State.TopLevel) catch unreachable; - const name_token = self.getNextToken(); - const name = (try self.parseStringLiteral(arena, name_token)) ?? { - try self.parseError(&stack, name_token, "expected string literal, found {}", @tagName(name_token.id)); - continue; - }; - const lbrace = (try self.expectToken(&stack, Token.Id.LBrace)) ?? continue; - const block = try self.createNode(arena, ast.NodeBlock, ast.NodeBlock { .base = undefined, .label = null, - .lbrace = lbrace, + .lbrace = undefined, .statements = ArrayList(&ast.Node).init(arena), .rbrace = undefined, } ); - _ = try self.createAttachNode(arena, &root_node.decls, ast.NodeTestDecl, + const test_node = try self.createAttachNode(arena, &root_node.decls, ast.NodeTestDecl, ast.NodeTestDecl { .base = undefined, .test_token = token, - .name = name, + .name = undefined, .body_node = &block.base, } ); stack.append(State { .Block = block }) catch unreachable; + try stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.LBrace, + .ptr = &block.rbrace, + } + }); + try stack.append(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } }); continue; }, Token.Id.Eof => { @@ -346,15 +413,30 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_comptime => { + const block = try self.createNode(arena, ast.NodeBlock, + ast.NodeBlock { + .base = undefined, + .label = null, + .lbrace = undefined, + .statements = ArrayList(&ast.Node).init(arena), + .rbrace = undefined, + } + ); const node = try self.createAttachNode(arena, &root_node.decls, ast.NodeComptime, ast.NodeComptime { .base = undefined, .comptime_token = token, - .expr = undefined, + .expr = &block.base, } ); stack.append(State.TopLevel) catch unreachable; - try stack.append(State { .Expression = DestPtr { .Field = &node.expr } }); + try stack.append(State { .Block = block }); + try stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.LBrace, + .ptr = &block.rbrace, + } + }); continue; }, else => { @@ -404,7 +486,6 @@ pub const Parser = struct { } } }, - State.TopLevelLibname => |ctx| { const lib_name = blk: { const lib_name_token = self.getNextToken(); @@ -422,15 +503,14 @@ pub const Parser = struct { .lib_name = lib_name, }, }) catch unreachable; + continue; }, - State.TopLevelDecl => |ctx| { const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_use => { if (ctx.extern_export_inline_token != null) { - try self.parseError(&stack, token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id)); - continue; + return self.parseError(token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id)); } const node = try self.createAttachNode(arena, ctx.decls, ast.NodeUse, @@ -447,44 +527,36 @@ pub const Parser = struct { .ptr = &node.semicolon_token, } }) catch unreachable; - try stack.append(State { .Expression = DestPtr { .Field = &node.expr } }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); continue; }, Token.Id.Keyword_var, Token.Id.Keyword_const => { if (ctx.extern_export_inline_token) |extern_export_inline_token| { if (extern_export_inline_token.id == Token.Id.Keyword_inline) { - try self.parseError(&stack, token, "Invalid token {}", @tagName(extern_export_inline_token.id)); - continue; + return self.parseError(token, "Invalid token {}", @tagName(extern_export_inline_token.id)); } } - const var_decl_node = try self.createAttachNode(arena, ctx.decls, ast.NodeVarDecl, - ast.NodeVarDecl { - .base = undefined, + stack.append(State { + .VarDecl = VarDeclCtx { .visib_token = ctx.visib_token, - .mut_token = token, + .lib_name = ctx.lib_name, .comptime_token = null, .extern_export_token = ctx.extern_export_inline_token, - .type_node = null, - .align_node = null, - .init_node = null, - .lib_name = ctx.lib_name, - // initialized later - .name_token = undefined, - .eq_token = undefined, - .semicolon_token = undefined, + .mut_token = token, + .list = ctx.decls } - ); - stack.append(State { .VarDecl = var_decl_node }) catch unreachable; + }) catch unreachable; continue; }, - Token.Id.Keyword_fn => { + Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, + Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.NodeFnProto, ast.NodeFnProto { .base = undefined, .visib_token = ctx.visib_token, .name_token = null, - .fn_token = token, + .fn_token = undefined, .params = ArrayList(&ast.Node).init(arena), .return_type = undefined, .var_args_token = null, @@ -498,148 +570,84 @@ pub const Parser = struct { ); stack.append(State { .FnDef = fn_proto }) catch unreachable; try stack.append(State { .FnProto = fn_proto }); - continue; - }, - Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.NodeFnProto, - ast.NodeFnProto { - .base = undefined, - .visib_token = ctx.visib_token, - .name_token = null, - .fn_token = undefined, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = ctx.extern_export_inline_token, - .cc_token = token, - .async_attr = null, - .body_node = null, - .lib_name = ctx.lib_name, - .align_expr = null, - } - ); - stack.append(State { .FnDef = fn_proto }) catch unreachable; - try stack.append(State { .FnProto = fn_proto }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } - }); - continue; - }, - Token.Id.Keyword_async => { - const async_node = try self.createNode(arena, ast.NodeAsyncAttribute, - ast.NodeAsyncAttribute { - .base = undefined, - .async_token = token, - .allocator_type = null, - .rangle_bracket = null, - } - ); - const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.NodeFnProto, - ast.NodeFnProto { - .base = undefined, - .visib_token = ctx.visib_token, - .name_token = null, - .fn_token = undefined, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = ctx.extern_export_inline_token, - .cc_token = null, - .async_attr = async_node, - .body_node = null, - .lib_name = ctx.lib_name, - .align_expr = null, - } - ); - stack.append(State { .FnDef = fn_proto }) catch unreachable; - try stack.append(State { .FnProto = fn_proto }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } - }); + switch (token.id) { + Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { + fn_proto.cc_token = token; + try stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + } + }); + continue; + }, + Token.Id.Keyword_async => { + const async_node = try self.createNode(arena, ast.NodeAsyncAttribute, + ast.NodeAsyncAttribute { + .base = undefined, + .async_token = token, + .allocator_type = null, + .rangle_bracket = null, + } + ); + fn_proto.async_attr = async_node; - const langle_bracket = self.getNextToken(); - if (langle_bracket.id != Token.Id.AngleBracketLeft) { - self.putBackToken(langle_bracket); - continue; + try stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + } + }); + try stack.append(State { .AsyncAllocator = async_node }); + continue; + }, + Token.Id.Keyword_fn => { + fn_proto.fn_token = token; + continue; + }, + else => unreachable, } - - async_node.rangle_bracket = Token(undefined); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.AngleBracketRight, - .ptr = &??async_node.rangle_bracket, - } - }); - try stack.append(State { .TypeExprBegin = DestPtr { .NullableField = &async_node.allocator_type } }); - continue; }, else => { - try self.parseError(&stack, token, "expected variable declaration or function, found {}", @tagName(token.id)); - continue; + return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id)); }, } }, - State.VarDecl => |var_decl| { - stack.append(State { .VarDeclAlign = var_decl }) catch unreachable; - try stack.append(State { .TypeExprBegin = DestPtr {.NullableField = &var_decl.type_node} }); - try stack.append(State { .IfToken = Token.Id.Colon }); + State.TopLevelExternOrField => |ctx| { + if (self.eatToken(Token.Id.Identifier)) |identifier| { + std.debug.assert(ctx.container_decl.kind == ast.NodeContainerDecl.Kind.Struct); + const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.NodeStructField, + ast.NodeStructField { + .base = undefined, + .visib_token = ctx.visib_token, + .name_token = identifier, + .type_expr = undefined, + } + ); + + stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } }); + try stack.append(State { .ExpectToken = Token.Id.Colon }); + continue; + } + + stack.append(State{ .ContainerDecl = ctx.container_decl }) catch unreachable; try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Identifier, - .ptr = &var_decl.name_token, + .TopLevelExtern = TopLevelDeclCtx { + .decls = &ctx.container_decl.fields_and_decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = null, + .lib_name = null, } }); continue; }, - State.VarDeclAlign => |var_decl| { - stack.append(State { .VarDeclEq = var_decl }) catch unreachable; - const next_token = self.getNextToken(); - if (next_token.id == Token.Id.Keyword_align) { - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = DestPtr{.NullableField = &var_decl.align_node} }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - continue; - } - self.putBackToken(next_token); - continue; - }, - State.VarDeclEq => |var_decl| { + State.ContainerKind => |ctx| { const token = self.getNextToken(); - switch (token.id) { - Token.Id.Equal => { - var_decl.eq_token = token; - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Semicolon, - .ptr = &var_decl.semicolon_token, - }, - }) catch unreachable; - try stack.append(State { .Expression = DestPtr {.NullableField = &var_decl.init_node} }); - continue; - }, - Token.Id.Semicolon => { - var_decl.semicolon_token = token; - continue; - }, - else => { - try self.parseError(&stack, token, "expected '=' or ';', found {}", @tagName(token.id)); - continue; - } - } - }, - - State.ContainerExtern => |ctx| { - const token = self.getNextToken(); - const node = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeContainerDecl, + const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeContainerDecl, ast.NodeContainerDecl { .base = undefined, .ltoken = ctx.ltoken, @@ -649,15 +657,14 @@ pub const Parser = struct { Token.Id.Keyword_union => ast.NodeContainerDecl.Kind.Union, Token.Id.Keyword_enum => ast.NodeContainerDecl.Kind.Enum, else => { - try self.parseError(&stack, token, "expected {}, {} or {}, found {}", + return self.parseError(token, "expected {}, {} or {}, found {}", @tagName(Token.Id.Keyword_struct), @tagName(Token.Id.Keyword_union), @tagName(Token.Id.Keyword_enum), @tagName(token.id)); - continue; }, }, - .init_arg_expr = undefined, + .init_arg_expr = ast.NodeContainerDecl.InitArg.None, .fields_and_decls = ArrayList(&ast.Node).init(arena), .rbrace_token = undefined, } @@ -665,37 +672,36 @@ pub const Parser = struct { stack.append(State { .ContainerDecl = node }) catch unreachable; try stack.append(State { .ExpectToken = Token.Id.LBrace }); + try stack.append(State { .ContainerInitArgStart = node }); + continue; + }, - const lparen = self.getNextToken(); - if (lparen.id != Token.Id.LParen) { - self.putBackToken(lparen); - node.init_arg_expr = ast.NodeContainerDecl.InitArg.None; + State.ContainerInitArgStart => |container_decl| { + if (self.eatToken(Token.Id.LParen) == null) { continue; } - try stack.append(State { .ExpectToken = Token.Id.RParen }); + stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + try stack.append(State { .ContainerInitArg = container_decl }); + continue; + }, + State.ContainerInitArg => |container_decl| { const init_arg_token = self.getNextToken(); switch (init_arg_token.id) { Token.Id.Keyword_enum => { - node.init_arg_expr = ast.NodeContainerDecl.InitArg.Enum; + container_decl.init_arg_expr = ast.NodeContainerDecl.InitArg.Enum; }, else => { self.putBackToken(init_arg_token); - node.init_arg_expr = ast.NodeContainerDecl.InitArg { .Type = undefined }; - try stack.append(State { - .Expression = DestPtr { - .Field = &node.init_arg_expr.Type - } - }); + container_decl.init_arg_expr = ast.NodeContainerDecl.InitArg { .Type = undefined }; + stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; }, } continue; }, - State.ContainerDecl => |container_decl| { const token = self.getNextToken(); - switch (token.id) { Token.Id.Identifier => { switch (container_decl.kind) { @@ -710,7 +716,7 @@ pub const Parser = struct { ); stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - try stack.append(State { .Expression = DestPtr { .Field = &node.type_expr } }); + try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } }); try stack.append(State { .ExpectToken = Token.Id.Colon }); continue; }, @@ -724,14 +730,8 @@ pub const Parser = struct { ); stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - - const next = self.getNextToken(); - if (next.id != Token.Id.Colon) { - self.putBackToken(next); - continue; - } - - try stack.append(State { .Expression = DestPtr { .NullableField = &node.type_expr } }); + try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } }); + try stack.append(State { .IfToken = Token.Id.Colon }); continue; }, ast.NodeContainerDecl.Kind.Enum => { @@ -744,52 +744,36 @@ pub const Parser = struct { ); stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - - const next = self.getNextToken(); - if (next.id != Token.Id.Equal) { - self.putBackToken(next); - continue; - } - - try stack.append(State { .Expression = DestPtr { .NullableField = &node.value } }); + try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &node.value } }); + try stack.append(State { .IfToken = Token.Id.Equal }); continue; }, } }, Token.Id.Keyword_pub => { - if (self.eatToken(Token.Id.Identifier)) |identifier| { - switch (container_decl.kind) { - ast.NodeContainerDecl.Kind.Struct => { - const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeStructField, - ast.NodeStructField { - .base = undefined, - .visib_token = token, - .name_token = identifier, - .type_expr = undefined, - } - ); - - stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - try stack.append(State { .Expression = DestPtr { .Field = &node.type_expr } }); - try stack.append(State { .ExpectToken = Token.Id.Colon }); - continue; - }, - else => { - self.putBackToken(identifier); - } + switch (container_decl.kind) { + ast.NodeContainerDecl.Kind.Struct => { + try stack.append(State { + .TopLevelExternOrField = TopLevelExternOrFieldCtx { + .visib_token = token, + .container_decl = container_decl, + } + }); + continue; + }, + else => { + stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.append(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &container_decl.fields_and_decls, + .visib_token = token, + .extern_export_inline_token = null, + .lib_name = null, + } + }); + continue; } } - - stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = token, - .extern_export_inline_token = null, - .lib_name = null, - } - }); - continue; }, Token.Id.Keyword_export => { stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; @@ -823,1609 +807,75 @@ pub const Parser = struct { } }, - State.ExpectToken => |token_id| { - _ = (try self.expectToken(&stack, token_id)) ?? continue; - continue; - }, - State.ExpectTokenSave => |expect_token_save| { - *expect_token_save.ptr = (try self.expectToken(&stack, expect_token_save.id)) ?? continue; - continue; - }, - - State.IfToken => |token_id| { - const token = self.getNextToken(); - if (@TagType(Token.Id)(token.id) != token_id) { - self.putBackToken(token); - _ = stack.pop(); - continue; - } - continue; - }, - - State.IfTokenSave => |if_token_save| { - const token = self.getNextToken(); - if (@TagType(Token.Id)(token.id) != if_token_save.id) { - self.putBackToken(token); - _ = stack.pop(); - continue; - } - - *if_token_save.ptr = token; - continue; - }, - - State.Optional => { }, - - State.Expression => |dest_ptr| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_return => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeControlFlowExpression, - ast.NodeControlFlowExpression { - .base = undefined, - .ltoken = token, - .kind = ast.NodeControlFlowExpression.Kind.Return, - .rhs = undefined, - } - ); - - // TODO: Find another way to do optional expressions - stack.append(State { - .Optional = RevertState { - .parser = *self, - .tokenizer = *self.tokenizer, - .ptr = &node.rhs, - } - }) catch unreachable; - try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } }); - continue; - }, - Token.Id.Keyword_break, Token.Id.Keyword_continue => { - const label = blk: { - const colon = self.getNextToken(); - if (colon.id != Token.Id.Colon) { - self.putBackToken(colon); - break :blk null; - } - - break :blk (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue; - }; - - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeControlFlowExpression, - ast.NodeControlFlowExpression { - .base = undefined, - .ltoken = token, - .kind = switch (token.id) { - Token.Id.Keyword_break => ast.NodeControlFlowExpression.Kind { .Break = label }, - Token.Id.Keyword_continue => ast.NodeControlFlowExpression.Kind { .Continue = label }, - else => unreachable, - }, - .rhs = undefined, - } - ); - - // TODO: Find another way to do optional expressions - stack.append(State { - .Optional = RevertState { - .parser = *self, - .tokenizer = *self.tokenizer, - .ptr = &node.rhs, - } - }) catch unreachable; - try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } }); - continue; - }, - Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp, - ast.NodePrefixOp { - .base = undefined, - .op_token = token, - .op = switch (token.id) { - Token.Id.Keyword_try => ast.NodePrefixOp.PrefixOp { .Try = void{} }, - Token.Id.Keyword_cancel => ast.NodePrefixOp.PrefixOp { .Cancel = void{} }, - Token.Id.Keyword_resume => ast.NodePrefixOp.PrefixOp { .Resume = void{} }, - else => unreachable, - }, - .rhs = undefined, - } - ); - - stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable; - continue; - }, - else => { - if (!try self.parseBlockExpr(&stack, arena, dest_ptr, token)) { - self.putBackToken(token); - stack.append(State { .UnwrapExpressionBegin = dest_ptr }) catch unreachable; - } - continue; - } - } - }, - - State.RangeExpressionBegin => |dest_ptr| { - stack.append(State { .RangeExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .Expression = dest_ptr }); - continue; - }, - - State.RangeExpressionEnd => |dest_ptr| { - if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = ellipsis3, - .op = ast.NodeInfixOp.InfixOp.Range, - .rhs = undefined, - } - ); - stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable; - } - - continue; - }, - - State.AssignmentExpressionBegin => |dest_ptr| { - stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .Expression = dest_ptr }); - continue; - }, - - State.AssignmentExpressionEnd => |dest_ptr| { - const token = self.getNextToken(); - if (tokenIdToAssignment(token.id)) |ass_id| { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = ass_id, - .rhs = undefined, - } - ); - stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; - } - }, - - State.UnwrapExpressionBegin => |dest_ptr| { - stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .BoolOrExpressionBegin = dest_ptr }); - continue; - }, - - State.UnwrapExpressionEnd => |dest_ptr| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_catch, Token.Id.QuestionMarkQuestionMark => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = switch (token.id) { - Token.Id.Keyword_catch => ast.NodeInfixOp.InfixOp { .Catch = null }, - Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp { .UnwrapMaybe = void{} }, - else => unreachable, - }, - .rhs = undefined, - } - ); - - stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }); - - if (node.op == ast.NodeInfixOp.InfixOp.Catch) { - try stack.append(State { .Payload = &node.op.Catch }); - } - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, - - State.BoolOrExpressionBegin => |dest_ptr| { - stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .BoolAndExpressionBegin = dest_ptr }); - continue; - }, - - State.BoolOrExpressionEnd => |dest_ptr| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_or => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = ast.NodeInfixOp.InfixOp.BoolOr, - .rhs = undefined, - } - ); - stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .BoolAndExpressionBegin = DestPtr { .Field = &node.rhs } }); - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, - - State.BoolAndExpressionBegin => |dest_ptr| { - stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .ComparisonExpressionBegin = dest_ptr }); - continue; - }, - - State.BoolAndExpressionEnd => |dest_ptr| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_and => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = ast.NodeInfixOp.InfixOp.BoolAnd, - .rhs = undefined, - } - ); - stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .ComparisonExpressionBegin = DestPtr { .Field = &node.rhs } }); - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, - - State.ComparisonExpressionBegin => |dest_ptr| { - stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .BinaryOrExpressionBegin = dest_ptr }); - continue; - }, - - State.ComparisonExpressionEnd => |dest_ptr| { - const token = self.getNextToken(); - if (tokenIdToComparison(token.id)) |comp_id| { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = comp_id, - .rhs = undefined, - } - ); - stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .BinaryOrExpressionBegin = DestPtr { .Field = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; - } - }, - - State.BinaryOrExpressionBegin => |dest_ptr| { - stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .BinaryXorExpressionBegin = dest_ptr }); - continue; - }, - - State.BinaryOrExpressionEnd => |dest_ptr| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Pipe => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = ast.NodeInfixOp.InfixOp.BitOr, - .rhs = undefined, - } - ); - stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .BinaryXorExpressionBegin = DestPtr { .Field = &node.rhs } }); - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, - - State.BinaryXorExpressionBegin => |dest_ptr| { - stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .BinaryAndExpressionBegin = dest_ptr }); - continue; - }, - - State.BinaryXorExpressionEnd => |dest_ptr| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Caret => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = ast.NodeInfixOp.InfixOp.BitXor, - .rhs = undefined, - } - ); - stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .BinaryAndExpressionBegin = DestPtr { .Field = &node.rhs } }); - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, - - State.BinaryAndExpressionBegin => |dest_ptr| { - stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .BitShiftExpressionBegin = dest_ptr }); - continue; - }, - - State.BinaryAndExpressionEnd => |dest_ptr| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Ampersand => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = ast.NodeInfixOp.InfixOp.BitAnd, - .rhs = undefined, - } - ); - stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .BitShiftExpressionBegin = DestPtr { .Field = &node.rhs } }); - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, - - State.BitShiftExpressionBegin => |dest_ptr| { - stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .AdditionExpressionBegin = dest_ptr }); - continue; - }, - - State.BitShiftExpressionEnd => |dest_ptr| { - const token = self.getNextToken(); - if (tokenIdToBitShift(token.id)) |bitshift_id| { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = bitshift_id, - .rhs = undefined, - } - ); - stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .AdditionExpressionBegin = DestPtr { .Field = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; - } - }, - - State.AdditionExpressionBegin => |dest_ptr| { - stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .MultiplyExpressionBegin = dest_ptr }); - continue; - }, - - State.AdditionExpressionEnd => |dest_ptr| { - const token = self.getNextToken(); - if (tokenIdToAddition(token.id)) |add_id| { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = add_id, - .rhs = undefined, - } - ); - stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .MultiplyExpressionBegin = DestPtr { .Field = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; - } - }, - - State.MultiplyExpressionBegin => |dest_ptr| { - stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .CurlySuffixExpressionBegin = dest_ptr }); - continue; - }, - - State.MultiplyExpressionEnd => |dest_ptr| { - const token = self.getNextToken(); - if (tokenIdToMultiply(token.id)) |mult_id| { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = mult_id, - .rhs = undefined, - } - ); - stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .CurlySuffixExpressionBegin = DestPtr { .Field = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; - } - }, - - State.CurlySuffixExpressionBegin => |dest_ptr| { - stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .TypeExprBegin = dest_ptr }); - continue; - }, - - State.CurlySuffixExpressionEnd => |dest_ptr| { - if (self.eatToken(Token.Id.LBrace) == null) { - continue; - } - - if (self.isPeekToken(Token.Id.Period)) { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp, - ast.NodeSuffixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op = ast.NodeSuffixOp.SuffixOp { - .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena), - }, - .rtoken = undefined, - } - ); - stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { - .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) { - .list = &node.op.StructInitializer, - .ptr = &node.rtoken, - } - }); - continue; - } else { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp, - ast.NodeSuffixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op = ast.NodeSuffixOp.SuffixOp { - .ArrayInitializer = ArrayList(&ast.Node).init(arena), - }, - .rtoken = undefined, - } - ); - stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.op.ArrayInitializer, - .end = Token.Id.RBrace, - .ptr = &node.rtoken, - } - }); - continue; - } - }, - - State.TypeExprBegin => |dest_ptr| { - stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable; - try stack.append(State { .PrefixOpExpression = dest_ptr }); - continue; - }, - - State.TypeExprEnd => |dest_ptr| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Bang => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = ast.NodeInfixOp.InfixOp.ErrorUnion, - .rhs = undefined, - } - ); - stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable; - try stack.append(State { .PrefixOpExpression = DestPtr { .Field = &node.rhs } }); - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, - - State.PrefixOpExpression => |dest_ptr| { - const token = self.getNextToken(); - if (tokenIdToPrefixOp(token.id)) |prefix_id| { - var node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp, - ast.NodePrefixOp { - .base = undefined, - .op_token = token, - .op = prefix_id, - .rhs = undefined, - } - ); - - if (token.id == Token.Id.AsteriskAsterisk) { - const child = try self.createNode(arena, ast.NodePrefixOp, - ast.NodePrefixOp { - .base = undefined, - .op_token = token, - .op = prefix_id, - .rhs = undefined, - } - ); - node.rhs = &child.base; - node = child; - } - - stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable; - if (node.op == ast.NodePrefixOp.PrefixOp.AddrOf) { - try stack.append(State { .AddrOfModifiers = &node.op.AddrOf }); - } - continue; - } else { - self.putBackToken(token); - stack.append(State { .SuffixOpExpressionBegin = dest_ptr }) catch unreachable; - continue; - } - }, - - State.SuffixOpExpressionBegin => |dest_ptr| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_async => { - const async_node = try self.createNode(arena, ast.NodeAsyncAttribute, - ast.NodeAsyncAttribute { - .base = undefined, - .async_token = token, - .allocator_type = null, - .rangle_bracket = null, - } - ); - stack.append(State { - .AsyncEnd = AsyncEndCtx { - .dest_ptr = dest_ptr, - .attribute = async_node, - } - }) catch unreachable; - try stack.append(State { .SuffixOpExpressionEnd = dest_ptr }); - try stack.append(State { .PrimaryExpression = dest_ptr }); - - const langle_bracket = self.getNextToken(); - if (langle_bracket.id != Token.Id.AngleBracketLeft) { - self.putBackToken(langle_bracket); - continue; - } - - async_node.rangle_bracket = Token(undefined); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.AngleBracketRight, - .ptr = &??async_node.rangle_bracket, - } - }); - try stack.append(State { .TypeExprBegin = DestPtr { .NullableField = &async_node.allocator_type } }); - continue; - }, - else => { - self.putBackToken(token); - stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .PrimaryExpression = dest_ptr }); - continue; - } - } - }, - - State.SuffixOpExpressionEnd => |dest_ptr| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.LParen => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp, - ast.NodeSuffixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op = ast.NodeSuffixOp.SuffixOp { - .Call = ast.NodeSuffixOp.CallInfo { - .params = ArrayList(&ast.Node).init(arena), - .async_attr = null, - } - }, - .rtoken = undefined, - } - ); - stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.op.Call.params, - .end = Token.Id.RParen, - .ptr = &node.rtoken, - } - }); - continue; - }, - Token.Id.LBracket => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp, - ast.NodeSuffixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op = ast.NodeSuffixOp.SuffixOp { - .ArrayAccess = undefined, - }, - .rtoken = undefined - } - ); - stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { .SliceOrArrayAccess = node }); - try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayAccess }}); - continue; - }, - Token.Id.Period => { - const identifier = try self.createLiteral(arena, ast.NodeIdentifier, Token(undefined)); - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp, - ast.NodeInfixOp { - .base = undefined, - .lhs = dest_ptr.get(), - .op_token = token, - .op = ast.NodeInfixOp.InfixOp.Period, - .rhs = &identifier.base, - } - ); - stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable; - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Identifier, - .ptr = &identifier.token - } - }); - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, - - State.PrimaryExpression => |dest_ptr| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.IntegerLiteral => { - dest_ptr.store(&(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base); - continue; - }, - Token.Id.FloatLiteral => { - dest_ptr.store(&(try self.createLiteral(arena, ast.NodeFloatLiteral, token)).base); - continue; - }, - Token.Id.CharLiteral => { - dest_ptr.store(&(try self.createLiteral(arena, ast.NodeCharLiteral, token)).base); - continue; - }, - Token.Id.Keyword_undefined => { - dest_ptr.store(&(try self.createLiteral(arena, ast.NodeUndefinedLiteral, token)).base); - continue; - }, - Token.Id.Keyword_true, Token.Id.Keyword_false => { - dest_ptr.store(&(try self.createLiteral(arena, ast.NodeBoolLiteral, token)).base); - continue; - }, - Token.Id.Keyword_null => { - dest_ptr.store(&(try self.createLiteral(arena, ast.NodeNullLiteral, token)).base); - continue; - }, - Token.Id.Keyword_this => { - dest_ptr.store(&(try self.createLiteral(arena, ast.NodeThisLiteral, token)).base); - continue; - }, - Token.Id.Keyword_var => { - dest_ptr.store(&(try self.createLiteral(arena, ast.NodeVarType, token)).base); - continue; - }, - Token.Id.Keyword_unreachable => { - dest_ptr.store(&(try self.createLiteral(arena, ast.NodeUnreachable, token)).base); - continue; - }, - Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { - dest_ptr.store((try self.parseStringLiteral(arena, token)) ?? unreachable); - }, - Token.Id.LParen => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeGroupedExpression, - ast.NodeGroupedExpression { - .base = undefined, - .lparen = token, - .expr = undefined, - .rparen = undefined, - } - ); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RParen, - .ptr = &node.rparen, - } - }) catch unreachable; - try stack.append(State { .Expression = DestPtr { .Field = &node.expr } }); - continue; - }, - Token.Id.Builtin => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeBuiltinCall, - ast.NodeBuiltinCall { - .base = undefined, - .builtin_token = token, - .params = ArrayList(&ast.Node).init(arena), - .rparen_token = undefined, - } - ); - stack.append(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.params, - .end = Token.Id.RParen, - .ptr = &node.rparen_token, - } - }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.LParen, }); - continue; - }, - Token.Id.LBracket => { - const rbracket_token = self.getNextToken(); - if (rbracket_token.id == Token.Id.RBracket) { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp, - ast.NodePrefixOp { - .base = undefined, - .op_token = token, - .op = ast.NodePrefixOp.PrefixOp{ - .SliceType = ast.NodePrefixOp.AddrOfInfo { - .align_expr = null, - .bit_offset_start_token = null, - .bit_offset_end_token = null, - .const_token = null, - .volatile_token = null, - } - }, - .rhs = undefined, - } - ); - dest_ptr.store(&node.base); - stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable; - try stack.append(State { .AddrOfModifiers = &node.op.SliceType }); - continue; - } - - self.putBackToken(rbracket_token); - - const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp, - ast.NodePrefixOp { - .base = undefined, - .op_token = token, - .op = ast.NodePrefixOp.PrefixOp{ - .ArrayType = undefined, - }, - .rhs = undefined, - } - ); - stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.RBracket }); - try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayType } }); - - }, - Token.Id.Keyword_error => { - if (self.eatToken(Token.Id.LBrace) == null) { - dest_ptr.store(&(try self.createLiteral(arena, ast.NodeErrorType, token)).base); - continue; - } - - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeErrorSetDecl, - ast.NodeErrorSetDecl { - .base = undefined, - .error_token = token, - .decls = ArrayList(&ast.NodeIdentifier).init(arena), - .rbrace_token = undefined, - } - ); - - stack.append(State { - .IdentifierListItemOrEnd = ListSave(&ast.NodeIdentifier) { - .list = &node.decls, - .ptr = &node.rbrace_token, - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_packed => { - stack.append(State { - .ContainerExtern = ContainerExternCtx { - .dest_ptr = dest_ptr, - .ltoken = token, - .layout = ast.NodeContainerDecl.Layout.Packed, - }, - }) catch unreachable; - }, - Token.Id.Keyword_extern => { - const next = self.getNextToken(); - if (next.id == Token.Id.Keyword_fn) { - const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto, - ast.NodeFnProto { - .base = undefined, - .visib_token = null, - .name_token = null, - .fn_token = next, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = token, - .cc_token = null, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - } - ); - stack.append(State { .FnProto = fn_proto }) catch unreachable; - continue; - } - - self.putBackToken(next); - stack.append(State { - .ContainerExtern = ContainerExternCtx { - .dest_ptr = dest_ptr, - .ltoken = token, - .layout = ast.NodeContainerDecl.Layout.Extern, - }, - }) catch unreachable; - }, - Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => { - self.putBackToken(token); - stack.append(State { - .ContainerExtern = ContainerExternCtx { - .dest_ptr = dest_ptr, - .ltoken = token, - .layout = ast.NodeContainerDecl.Layout.Auto, - }, - }) catch unreachable; - }, - Token.Id.Identifier => { - const next = self.getNextToken(); - if (next.id != Token.Id.Colon) { - self.putBackToken(next); - dest_ptr.store(&(try self.createLiteral(arena, ast.NodeIdentifier, token)).base); - continue; - } - - stack.append(State { - .LabeledExpression = LabelCtx { - .label = token, - .dest_ptr = dest_ptr - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_fn => { - const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto, - ast.NodeFnProto { - .base = undefined, - .visib_token = null, - .name_token = null, - .fn_token = token, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = null, - .cc_token = null, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - } - ); - stack.append(State { .FnProto = fn_proto }) catch unreachable; - continue; - }, - Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_token = (try self.expectToken(&stack, Token.Id.Keyword_fn)) ?? continue; - const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto, - ast.NodeFnProto { - .base = undefined, - .visib_token = null, - .name_token = null, - .fn_token = fn_token, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = null, - .cc_token = token, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - } - ); - stack.append(State { .FnProto = fn_proto }) catch unreachable; - continue; - }, - Token.Id.Keyword_asm => { - const is_volatile = blk: { - const volatile_token = self.getNextToken(); - if (volatile_token.id != Token.Id.Keyword_volatile) { - self.putBackToken(volatile_token); - break :blk false; - } - break :blk true; - }; - _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue; - - const template_token = self.getNextToken(); - const template = (try self.parseStringLiteral(arena, template_token)) ?? { - try self.parseError(&stack, template_token, "expected string literal, found {}", @tagName(template_token.id)); - continue; - }; - // TODO parse template - - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeAsm, - ast.NodeAsm { - .base = undefined, - .asm_token = token, - .is_volatile = is_volatile, - .template = template, - //.tokens = ArrayList(ast.NodeAsm.AsmToken).init(arena), - .outputs = ArrayList(&ast.NodeAsmOutput).init(arena), - .inputs = ArrayList(&ast.NodeAsmInput).init(arena), - .cloppers = ArrayList(&ast.Node).init(arena), - .rparen = undefined, - } - ); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RParen, - .ptr = &node.rparen, - } - }) catch unreachable; - try stack.append(State { .AsmClopperItems = &node.cloppers }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .AsmInputItems = &node.inputs }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .AsmOutputItems = &node.outputs }); - try stack.append(State { .IfToken = Token.Id.Colon }); - }, - Token.Id.Keyword_inline => { - stack.append(State { - .Inline = InlineCtx { - .label = null, - .inline_token = token, - .dest_ptr = dest_ptr, - } - }) catch unreachable; - continue; - }, - else => { - if (!try self.parseBlockExpr(&stack, arena, dest_ptr, token)) { - try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id)); - } - continue; - } - } - }, - - State.SliceOrArrayAccess => |node| { - var token = self.getNextToken(); - - switch (token.id) { - Token.Id.Ellipsis2 => { - const start = node.op.ArrayAccess; - node.op = ast.NodeSuffixOp.SuffixOp { - .Slice = ast.NodeSuffixOp.SliceRange { - .start = start, - .end = undefined, - } - }; - - const rbracket_token = self.getNextToken(); - if (rbracket_token.id != Token.Id.RBracket) { - self.putBackToken(rbracket_token); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RBracket, - .ptr = &node.rtoken, - } - }) catch unreachable; - try stack.append(State { .Expression = DestPtr { .NullableField = &node.op.Slice.end } }); - } else { - node.rtoken = rbracket_token; - } - continue; - }, - Token.Id.RBracket => { - node.rtoken = token; - continue; - }, - else => { - try self.parseError(&stack, token, "expected ']' or '..', found {}", @tagName(token.id)); - continue; - } - } - }, - - - State.AsmOutputItems => |items| { - const lbracket = self.getNextToken(); - if (lbracket.id != Token.Id.LBracket) { - self.putBackToken(lbracket); - continue; - } - - stack.append(State { .AsmOutputItems = items }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.Comma }); - - const symbolic_name = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue; - _ = (try self.expectToken(&stack, Token.Id.RBracket)) ?? continue; - - const constraint_token = self.getNextToken(); - const constraint = (try self.parseStringLiteral(arena, constraint_token)) ?? { - try self.parseError(&stack, constraint_token, "expected string literal, found {}", @tagName(constraint_token.id)); - continue; - }; - - _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue; - try stack.append(State { .ExpectToken = Token.Id.RParen }); - - const node = try self.createNode(arena, ast.NodeAsmOutput, - ast.NodeAsmOutput { + State.VarDecl => |ctx| { + const var_decl = try self.createAttachNode(arena, ctx.list, ast.NodeVarDecl, + ast.NodeVarDecl { .base = undefined, - .symbolic_name = try self.createLiteral(arena, ast.NodeIdentifier, symbolic_name), - .constraint = constraint, - .kind = undefined, - } - ); - try items.append(node); - - const symbol_or_arrow = self.getNextToken(); - switch (symbol_or_arrow.id) { - Token.Id.Identifier => { - node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.NodeIdentifier, symbol_or_arrow) }; - }, - Token.Id.Arrow => { - node.kind = ast.NodeAsmOutput.Kind { .Return = undefined }; - try stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.kind.Return } }); - }, - else => { - try self.parseError(&stack, symbol_or_arrow, "expected '->' or {}, found {}", - @tagName(Token.Id.Identifier), - @tagName(symbol_or_arrow.id)); - continue; - }, - } - }, - - State.AsmInputItems => |items| { - const lbracket = self.getNextToken(); - if (lbracket.id != Token.Id.LBracket) { - self.putBackToken(lbracket); - continue; - } - - stack.append(State { .AsmInputItems = items }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.Comma }); - - const symbolic_name = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue; - _ = (try self.expectToken(&stack, Token.Id.RBracket)) ?? continue; - - const constraint_token = self.getNextToken(); - const constraint = (try self.parseStringLiteral(arena, constraint_token)) ?? { - try self.parseError(&stack, constraint_token, "expected string literal, found {}", @tagName(constraint_token.id)); - continue; - }; - - _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue; - try stack.append(State { .ExpectToken = Token.Id.RParen }); - - const node = try self.createNode(arena, ast.NodeAsmInput, - ast.NodeAsmInput { - .base = undefined, - .symbolic_name = try self.createLiteral(arena, ast.NodeIdentifier, symbolic_name), - .constraint = constraint, - .expr = undefined, - } - ); - try items.append(node); - try stack.append(State { .Expression = DestPtr { .Field = &node.expr } }); - }, - - State.AsmClopperItems => |items| { - const string_token = self.getNextToken(); - const string = (try self.parseStringLiteral(arena, string_token)) ?? { - self.putBackToken(string_token); - continue; - }; - try items.append(string); - - stack.append(State { .AsmClopperItems = items }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.Comma }); - }, - - State.ExprListItemOrEnd => |list_state| { - var token = self.getNextToken(); - - const IdTag = @TagType(Token.Id); - if (IdTag(list_state.end) == token.id) { - *list_state.ptr = token; - continue; - } - - self.putBackToken(token); - stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable; - try stack.append(State { .Expression = DestPtr{ .Field = try list_state.list.addOne() } }); - }, - - State.FieldInitListItemOrEnd => |list_state| { - if (self.eatToken(Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; - continue; - } - - const node = try self.createNode(arena, ast.NodeFieldInitializer, - ast.NodeFieldInitializer { - .base = undefined, - .period_token = undefined, + .visib_token = ctx.visib_token, + .mut_token = ctx.mut_token, + .comptime_token = ctx.comptime_token, + .extern_export_token = ctx.extern_export_token, + .type_node = null, + .align_node = null, + .init_node = null, + .lib_name = ctx.lib_name, + // initialized later .name_token = undefined, - .expr = undefined, + .eq_token = undefined, + .semicolon_token = undefined, } ); - try list_state.list.append(node); - stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; - try stack.append(State { .Expression = DestPtr{.Field = &node.expr} }); - try stack.append(State { .ExpectToken = Token.Id.Equal }); + stack.append(State { .VarDeclAlign = var_decl }) catch unreachable; + try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); + try stack.append(State { .IfToken = Token.Id.Colon }); try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Identifier, - .ptr = &node.name_token, + .ptr = &var_decl.name_token, } }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Period, - .ptr = &node.period_token, - } - }); - }, - - State.IdentifierListItemOrEnd => |list_state| { - if (self.eatToken(Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; - continue; - } - - const node = try self.createLiteral(arena, ast.NodeIdentifier, Token(undefined)); - try list_state.list.append(node); - - stack.append(State { .IdentifierListCommaOrEnd = list_state }) catch unreachable; - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Identifier, - .ptr = &node.token, - } - }); - }, - - State.SwitchCaseOrEnd => |list_state| { - if (self.eatToken(Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; - continue; - } - - const node = try self.createNode(arena, ast.NodeSwitchCase, - ast.NodeSwitchCase { - .base = undefined, - .items = ArrayList(&ast.Node).init(arena), - .payload = null, - .expr = undefined, - } - ); - try list_state.list.append(node); - stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable; - try stack.append(State { .AssignmentExpressionBegin = DestPtr{ .Field = &node.expr } }); - try stack.append(State { .PointerPayload = &node.payload }); - - const maybe_else = self.getNextToken(); - if (maybe_else.id == Token.Id.Keyword_else) { - const else_node = try self.createAttachNode(arena, &node.items, ast.NodeSwitchElse, - ast.NodeSwitchElse { - .base = undefined, - .token = maybe_else, - } - ); - try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); - continue; - } else { - self.putBackToken(maybe_else); - try stack.append(State { .SwitchCaseItem = &node.items }); - continue; - } - }, - - State.SwitchCaseItem => |case_items| { - stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; - try stack.append(State { .RangeExpressionBegin = DestPtr{ .Field = try case_items.addOne() } }); - }, - - State.ExprListCommaOrEnd => |list_state| { - try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .ExprListItemOrEnd = list_state }); continue; }, - - State.FieldInitListCommaOrEnd => |list_state| { - try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .FieldInitListItemOrEnd = list_state }); - continue; - }, - - State.FieldListCommaOrEnd => |container_decl| { - try self.commaOrEnd(&stack, Token.Id.RBrace, &container_decl.rbrace_token, - State { .ContainerDecl = container_decl }); - continue; - }, - - State.IdentifierListCommaOrEnd => |list_state| { - try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .IdentifierListItemOrEnd = list_state }); - continue; - }, - - State.SwitchCaseCommaOrEnd => |list_state| { - try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .SwitchCaseOrEnd = list_state }); - continue; - }, - - State.SwitchCaseItemCommaOrEnd => |case_items| { - try self.commaOrEnd(&stack, Token.Id.EqualAngleBracketRight, null, State { .SwitchCaseItem = case_items }); - continue; - }, - - State.Else => |dest| { - const else_token = self.getNextToken(); - if (else_token.id != Token.Id.Keyword_else) { - self.putBackToken(else_token); - continue; - } - - const node = try self.createNode(arena, ast.NodeElse, - ast.NodeElse { - .base = undefined, - .else_token = else_token, - .payload = null, - .body = undefined, - } - ); - *dest = node; - - stack.append(State { .Expression = DestPtr { .Field = &node.body } }) catch unreachable; - try stack.append(State { .Payload = &node.payload }); - }, - - State.WhileContinueExpr => |dest| { - const colon = self.getNextToken(); - if (colon.id != Token.Id.Colon) { - self.putBackToken(colon); - continue; - } - - _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue; - stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = dest } }); - }, - - State.SuspendBody => |suspend_node| { - if (suspend_node.payload != null) { - try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = &suspend_node.body } }); - } - continue; - }, - - State.AsyncEnd => |ctx| { - const node = ctx.dest_ptr.get(); - - switch (node.id) { - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", node); - fn_proto.async_attr = ctx.attribute; - }, - ast.Node.Id.SuffixOp => { - const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node); - if (suffix_op.op == ast.NodeSuffixOp.SuffixOp.Call) { - suffix_op.op.Call.async_attr = ctx.attribute; - continue; - } - - try self.parseError(&stack, node.firstToken(), "expected call or fn proto, found {}.", - @tagName(suffix_op.op)); - continue; - }, - else => { - try self.parseError(&stack, node.firstToken(), "expected call or fn proto, found {}.", - @tagName(node.id)); - continue; - } - } - }, - - State.Payload => |dest| { - const lpipe = self.getNextToken(); - if (lpipe.id != Token.Id.Pipe) { - self.putBackToken(lpipe); - continue; - } - - const error_symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue; - const rpipe = (try self.expectToken(&stack, Token.Id.Pipe)) ?? continue; - *dest = try self.createNode(arena, ast.NodePayload, - ast.NodePayload { - .base = undefined, - .lpipe = lpipe, - .error_symbol = try self.createLiteral(arena, ast.NodeIdentifier, error_symbol), - .rpipe = rpipe - } - ); - }, - - State.PointerPayload => |dest| { - const lpipe = self.getNextToken(); - if (lpipe.id != Token.Id.Pipe) { - self.putBackToken(lpipe); - continue; - } - - const is_ptr = blk: { - const asterik = self.getNextToken(); - if (asterik.id == Token.Id.Asterisk) { - break :blk true; - } else { - self.putBackToken(asterik); - break :blk false; - } - }; - - const value_symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue; - const rpipe = (try self.expectToken(&stack, Token.Id.Pipe)) ?? continue; - *dest = try self.createNode(arena, ast.NodePointerPayload, - ast.NodePointerPayload { - .base = undefined, - .lpipe = lpipe, - .is_ptr = is_ptr, - .value_symbol = try self.createLiteral(arena, ast.NodeIdentifier, value_symbol), - .rpipe = rpipe - } - ); - }, - - State.PointerIndexPayload => |dest| { - const lpipe = self.getNextToken(); - if (lpipe.id != Token.Id.Pipe) { - self.putBackToken(lpipe); - continue; - } - - const is_ptr = blk: { - const asterik = self.getNextToken(); - if (asterik.id == Token.Id.Asterisk) { - break :blk true; - } else { - self.putBackToken(asterik); - break :blk false; - } - }; - - const value_symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue; - const index_symbol = blk: { - const comma = self.getNextToken(); - if (comma.id != Token.Id.Comma) { - self.putBackToken(comma); - break :blk null; - } - - const symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue; - break :blk try self.createLiteral(arena, ast.NodeIdentifier, symbol); - }; - - const rpipe = (try self.expectToken(&stack, Token.Id.Pipe)) ?? continue; - *dest = try self.createNode(arena, ast.NodePointerIndexPayload, - ast.NodePointerIndexPayload { - .base = undefined, - .lpipe = lpipe, - .is_ptr = is_ptr, - .value_symbol = try self.createLiteral(arena, ast.NodeIdentifier, value_symbol), - .index_symbol = index_symbol, - .rpipe = rpipe - } - ); - }, - - State.AddrOfModifiers => |addr_of_info| { - var token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_align => { - stack.append(state) catch unreachable; - if (addr_of_info.align_expr != null) { - try self.parseError(&stack, token, "multiple align qualifiers"); - continue; - } - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_info.align_expr} }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - Token.Id.Keyword_const => { - stack.append(state) catch unreachable; - if (addr_of_info.const_token != null) { - try self.parseError(&stack, token, "duplicate qualifier: const"); - continue; - } - addr_of_info.const_token = token; - continue; - }, - Token.Id.Keyword_volatile => { - stack.append(state) catch unreachable; - if (addr_of_info.volatile_token != null) { - try self.parseError(&stack, token, "duplicate qualifier: volatile"); - continue; - } - addr_of_info.volatile_token = token; - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, - - State.FnProto => |fn_proto| { - stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable; - try stack.append(State { .ParamDecl = fn_proto }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); + State.VarDeclAlign => |var_decl| { + stack.append(State { .VarDeclEq = var_decl }) catch unreachable; const next_token = self.getNextToken(); - if (next_token.id == Token.Id.Identifier) { - fn_proto.name_token = next_token; + if (next_token.id == Token.Id.Keyword_align) { + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); continue; } + self.putBackToken(next_token); continue; }, - - State.FnProtoAlign => |fn_proto| { - stack.append(State { .FnProtoReturnType = fn_proto }) catch unreachable; - - if (self.eatToken(Token.Id.Keyword_align)) |align_token| { - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = DestPtr { .NullableField = &fn_proto.align_expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - } - - continue; - }, - - State.FnProtoReturnType => |fn_proto| { + State.VarDeclEq => |var_decl| { const token = self.getNextToken(); switch (token.id) { - Token.Id.Bang => { - fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined }; + Token.Id.Equal => { + var_decl.eq_token = token; stack.append(State { - .TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.InferErrorSet}, + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Semicolon, + .ptr = &var_decl.semicolon_token, + }, }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } }); + continue; + }, + Token.Id.Semicolon => { + var_decl.semicolon_token = token; continue; }, else => { - // TODO: this is a special case. Remove this when #760 is fixed - if (token.id == Token.Id.Keyword_error) { - if (self.isPeekToken(Token.Id.LBrace)) { - fn_proto.return_type = ast.NodeFnProto.ReturnType { - .Explicit = &(try self.createLiteral(arena, ast.NodeErrorType, token)).base - }; - continue; - } - } - - self.putBackToken(token); - fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined }; - stack.append(State { - .TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.Explicit}, - }) catch unreachable; - continue; - }, - } - }, - - State.ParamDecl => |fn_proto| { - if (self.eatToken(Token.Id.RParen)) |_| { - continue; - } - const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.NodeParamDecl, - ast.NodeParamDecl { - .base = undefined, - .comptime_token = null, - .noalias_token = null, - .name_token = null, - .type_node = undefined, - .var_args_token = null, - }, - ); - if (self.eatToken(Token.Id.Keyword_comptime)) |comptime_token| { - param_decl.comptime_token = comptime_token; - } else if (self.eatToken(Token.Id.Keyword_noalias)) |noalias_token| { - param_decl.noalias_token = noalias_token; - } - if (self.eatToken(Token.Id.Identifier)) |identifier| { - if (self.eatToken(Token.Id.Colon)) |_| { - param_decl.name_token = identifier; - } else { - self.putBackToken(identifier); + return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id)); } } - if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| { - param_decl.var_args_token = ellipsis3; - stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - continue; - } - - stack.append(State { .ParamDecl = fn_proto }) catch unreachable; - try stack.append(State.ParamDeclComma); - try stack.append(State { - .TypeExprBegin = DestPtr {.Field = ¶m_decl.type_node} - }); - continue; }, - State.ParamDeclComma => { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.RParen => { - _ = stack.pop(); // pop off the ParamDecl - continue; - }, - Token.Id.Comma => continue, - else => { - try self.parseError(&stack, token, "expected ',' or ')', found {}", @tagName(token.id)); - continue; - }, - } - }, State.FnDef => |fn_proto| { const token = self.getNextToken(); @@ -2446,17 +896,144 @@ pub const Parser = struct { }, Token.Id.Semicolon => continue, else => { - try self.parseError(&stack, token, "expected ';' or '{{', found {}", @tagName(token.id)); + return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id)); + }, + } + }, + State.FnProto => |fn_proto| { + stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable; + try stack.append(State { .ParamDecl = fn_proto }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); + + if (self.eatToken(Token.Id.Identifier)) |name_token| { + fn_proto.name_token = name_token; + } + continue; + }, + State.FnProtoAlign => |fn_proto| { + stack.append(State { .FnProtoReturnType = fn_proto }) catch unreachable; + + if (self.eatToken(Token.Id.Keyword_align)) |align_token| { + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); + } + continue; + }, + State.FnProtoReturnType => |fn_proto| { + const token = self.getNextToken(); + switch (token.id) { + Token.Id.Bang => { + fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined }; + stack.append(State { + .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet }, + }) catch unreachable; + continue; + }, + else => { + // TODO: this is a special case. Remove this when #760 is fixed + if (token.id == Token.Id.Keyword_error) { + if (self.isPeekToken(Token.Id.LBrace)) { + fn_proto.return_type = ast.NodeFnProto.ReturnType { + .Explicit = &(try self.createLiteral(arena, ast.NodeErrorType, token)).base + }; + continue; + } + } + + self.putBackToken(token); + fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined }; + stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; continue; }, } }, + + State.ParamDecl => |fn_proto| { + if (self.eatToken(Token.Id.RParen)) |_| { + continue; + } + const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.NodeParamDecl, + ast.NodeParamDecl { + .base = undefined, + .comptime_token = null, + .noalias_token = null, + .name_token = null, + .type_node = undefined, + .var_args_token = null, + }, + ); + + stack.append(State { + .ParamDeclEnd = ParamDeclEndCtx { + .param_decl = param_decl, + .fn_proto = fn_proto, + } + }) catch unreachable; + try stack.append(State { .ParamDeclName = param_decl }); + try stack.append(State { .ParamDeclAliasOrComptime = param_decl }); + continue; + }, + State.ParamDeclAliasOrComptime => |param_decl| { + if (self.eatToken(Token.Id.Keyword_comptime)) |comptime_token| { + param_decl.comptime_token = comptime_token; + } else if (self.eatToken(Token.Id.Keyword_noalias)) |noalias_token| { + param_decl.noalias_token = noalias_token; + } + continue; + }, + State.ParamDeclName => |param_decl| { + // TODO: Here, we eat two tokens in one state. This means that we can't have + // comments between these two tokens. + if (self.eatToken(Token.Id.Identifier)) |ident_token| { + if (self.eatToken(Token.Id.Colon)) |_| { + param_decl.name_token = ident_token; + } else { + self.putBackToken(ident_token); + } + } + continue; + }, + State.ParamDeclEnd => |ctx| { + if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| { + ctx.param_decl.var_args_token = ellipsis3; + stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + continue; + } + + try stack.append(State { .ParamDeclComma = ctx.fn_proto }); + try stack.append(State { + .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node } + }); + continue; + }, + State.ParamDeclComma => |fn_proto| { + if ((try self.expectCommaOrEnd(Token.Id.RParen)) == null) { + stack.append(State { .ParamDecl = fn_proto }) catch unreachable; + } + continue; + }, + + State.MaybeLabeledExpression => |ctx| { + if (self.eatToken(Token.Id.Colon)) |_| { + stack.append(State { + .LabeledExpression = LabelCtx { + .label = ctx.label, + .opt_ctx = ctx.opt_ctx, + } + }) catch unreachable; + continue; + } + + _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.NodeIdentifier, ctx.label); + continue; + }, State.LabeledExpression => |ctx| { const token = self.getNextToken(); switch (token.id) { Token.Id.LBrace => { - const block = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeBlock, + const block = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeBlock, ast.NodeBlock { .base = undefined, .label = ctx.label, @@ -2474,7 +1051,7 @@ pub const Parser = struct { .label = ctx.label, .inline_token = null, .loop_token = token, - .dest_ptr = ctx.dest_ptr, + .opt_ctx = ctx.opt_ctx.toRequired(), } }) catch unreachable; continue; @@ -2485,7 +1062,7 @@ pub const Parser = struct { .label = ctx.label, .inline_token = null, .loop_token = token, - .dest_ptr = ctx.dest_ptr, + .opt_ctx = ctx.opt_ctx.toRequired(), } }) catch unreachable; continue; @@ -2495,18 +1072,21 @@ pub const Parser = struct { .Inline = InlineCtx { .label = ctx.label, .inline_token = token, - .dest_ptr = ctx.dest_ptr, + .opt_ctx = ctx.opt_ctx.toRequired(), } }) catch unreachable; continue; }, else => { - try self.parseError(&stack, token, "expected 'while', 'for', 'inline' or '{{', found {}", @tagName(token.id)); + if (ctx.opt_ctx != OptionalCtx.Optional) { + return self.parseError(token, "expected 'while', 'for', 'inline' or '{{', found {}", @tagName(token.id)); + } + + self.putBackToken(token); continue; }, } }, - State.Inline => |ctx| { const token = self.getNextToken(); switch (token.id) { @@ -2516,7 +1096,7 @@ pub const Parser = struct { .inline_token = ctx.inline_token, .label = ctx.label, .loop_token = token, - .dest_ptr = ctx.dest_ptr, + .opt_ctx = ctx.opt_ctx.toRequired(), } }) catch unreachable; continue; @@ -2527,20 +1107,23 @@ pub const Parser = struct { .inline_token = ctx.inline_token, .label = ctx.label, .loop_token = token, - .dest_ptr = ctx.dest_ptr, + .opt_ctx = ctx.opt_ctx.toRequired(), } }) catch unreachable; continue; }, else => { - try self.parseError(&stack, token, "expected 'while' or 'for', found {}", @tagName(token.id)); + if (ctx.opt_ctx != OptionalCtx.Optional) { + return self.parseError(token, "expected 'while' or 'for', found {}", @tagName(token.id)); + } + + self.putBackToken(token); continue; }, } }, - State.While => |ctx| { - const node = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeWhile, + const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeWhile, ast.NodeWhile { .base = undefined, .label = ctx.label, @@ -2554,16 +1137,23 @@ pub const Parser = struct { } ); stack.append(State { .Else = &node.@"else" }) catch unreachable; - try stack.append(State { .Expression = DestPtr { .Field = &node.body } }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); try stack.append(State { .WhileContinueExpr = &node.continue_expr }); - try stack.append(State { .PointerPayload = &node.payload }); + try stack.append(State { .IfToken = Token.Id.Colon }); + try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = DestPtr { .Field = &node.condition } }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } }); try stack.append(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + State.WhileContinueExpr => |dest| { + stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); + continue; }, - State.For => |ctx| { - const node = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeFor, + const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeFor, ast.NodeFor { .base = undefined, .label = ctx.label, @@ -2576,12 +1166,33 @@ pub const Parser = struct { } ); stack.append(State { .Else = &node.@"else" }) catch unreachable; - try stack.append(State { .Expression = DestPtr { .Field = &node.body } }); - try stack.append(State { .PointerIndexPayload = &node.payload }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.append(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } }); try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = DestPtr { .Field = &node.array_expr } }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.array_expr } }); try stack.append(State { .ExpectToken = Token.Id.LParen }); + continue; }, + State.Else => |dest| { + if (self.eatToken(Token.Id.Keyword_else)) |else_token| { + const node = try self.createNode(arena, ast.NodeElse, + ast.NodeElse { + .base = undefined, + .else_token = else_token, + .payload = null, + .body = undefined, + } + ); + *dest = node; + + stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable; + try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + continue; + } else { + continue; + } + }, + State.Block => |block| { const token = self.getNextToken(); @@ -2598,67 +1209,37 @@ pub const Parser = struct { }, } }, - State.Statement => |block| { - const next = self.getNextToken(); - switch (next.id) { + const token = self.getNextToken(); + switch (token.id) { Token.Id.Keyword_comptime => { - const mut_token = self.getNextToken(); - if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) { - const var_decl = try self.createAttachNode(arena, &block.statements, ast.NodeVarDecl, - ast.NodeVarDecl { - .base = undefined, - .visib_token = null, - .mut_token = mut_token, - .comptime_token = next, - .extern_export_token = null, - .type_node = null, - .align_node = null, - .init_node = null, - .lib_name = null, - // initialized later - .name_token = undefined, - .eq_token = undefined, - .semicolon_token = undefined, - } - ); - stack.append(State { .VarDecl = var_decl }) catch unreachable; - continue; - } else { - self.putBackToken(mut_token); - self.putBackToken(next); - const statememt = try block.statements.addOne(); - stack.append(State { .Semicolon = statememt }) catch unreachable; - try stack.append(State { .Expression = DestPtr{.Field = statememt } }); - } + stack.append(State { + .ComptimeStatement = ComptimeStatementCtx { + .comptime_token = token, + .block = block, + } + }) catch unreachable; + continue; }, Token.Id.Keyword_var, Token.Id.Keyword_const => { - const var_decl = try self.createAttachNode(arena, &block.statements, ast.NodeVarDecl, - ast.NodeVarDecl { - .base = undefined, + stack.append(State { + .VarDecl = VarDeclCtx { .visib_token = null, - .mut_token = next, .comptime_token = null, .extern_export_token = null, - .type_node = null, - .align_node = null, - .init_node = null, .lib_name = null, - // initialized later - .name_token = undefined, - .eq_token = undefined, - .semicolon_token = undefined, + .mut_token = token, + .list = &block.statements, } - ); - stack.append(State { .VarDecl = var_decl }) catch unreachable; + }) catch unreachable; continue; }, Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { const node = try self.createAttachNode(arena, &block.statements, ast.NodeDefer, ast.NodeDefer { .base = undefined, - .defer_token = next, - .kind = switch (next.id) { + .defer_token = token, + .kind = switch (token.id) { Token.Id.Keyword_defer => ast.NodeDefer.Kind.Unconditional, Token.Id.Keyword_errdefer => ast.NodeDefer.Kind.Error, else => unreachable, @@ -2666,8 +1247,8 @@ pub const Parser = struct { .expr = undefined, } ); - stack.append(State { .Semicolon = &node.base }) catch unreachable; - try stack.append(State { .AssignmentExpressionBegin = DestPtr{.Field = &node.expr } }); + stack.append(State { .Semicolon = &&node.base }) catch unreachable; + try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); continue; }, Token.Id.LBrace => { @@ -2675,7 +1256,7 @@ pub const Parser = struct { ast.NodeBlock { .base = undefined, .label = null, - .lbrace = next, + .lbrace = token, .statements = ArrayList(&ast.Node).init(arena), .rbrace = undefined, } @@ -2684,22 +1265,1487 @@ pub const Parser = struct { continue; }, else => { - self.putBackToken(next); + self.putBackToken(token); const statememt = try block.statements.addOne(); stack.append(State { .Semicolon = statememt }) catch unreachable; - try stack.append(State { .AssignmentExpressionBegin = DestPtr{.Field = statememt } }); + try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statememt } }); + continue; + } + } + }, + State.ComptimeStatement => |ctx| { + const token = self.getNextToken(); + switch (token.id) { + Token.Id.Keyword_var, Token.Id.Keyword_const => { + stack.append(State { + .VarDecl = VarDeclCtx { + .visib_token = null, + .comptime_token = ctx.comptime_token, + .extern_export_token = null, + .lib_name = null, + .mut_token = token, + .list = &ctx.block.statements, + } + }) catch unreachable; + continue; + }, + else => { + self.putBackToken(token); + self.putBackToken(ctx.comptime_token); + const statememt = try ctx.block.statements.addOne(); + stack.append(State { .Semicolon = statememt }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = statememt } }); continue; } } - }, - State.Semicolon => |node_ptr| { const node = *node_ptr; if (requireSemiColon(node)) { - _ = (try self.expectToken(&stack, Token.Id.Semicolon)) ?? continue; + stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; + continue; } - } + continue; + }, + + + State.AsmOutputItems => |items| { + const lbracket = self.getNextToken(); + if (lbracket.id != Token.Id.LBracket) { + self.putBackToken(lbracket); + continue; + } + + const node = try self.createNode(arena, ast.NodeAsmOutput, + ast.NodeAsmOutput { + .base = undefined, + .symbolic_name = undefined, + .constraint = undefined, + .kind = undefined, + } + ); + try items.append(node); + + stack.append(State { .AsmOutputItems = items }) catch unreachable; + try stack.append(State { .IfToken = Token.Id.Comma }); + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .AsmOutputReturnOrType = node }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); + try stack.append(State { .ExpectToken = Token.Id.RBracket }); + try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + continue; + }, + State.AsmOutputReturnOrType => |node| { + const token = self.getNextToken(); + switch (token.id) { + Token.Id.Identifier => { + node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.NodeIdentifier, token) }; + continue; + }, + Token.Id.Arrow => { + node.kind = ast.NodeAsmOutput.Kind { .Return = undefined }; + try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } }); + continue; + }, + else => { + return self.parseError(token, "expected '->' or {}, found {}", + @tagName(Token.Id.Identifier), + @tagName(token.id)); + }, + } + }, + State.AsmInputItems => |items| { + const lbracket = self.getNextToken(); + if (lbracket.id != Token.Id.LBracket) { + self.putBackToken(lbracket); + continue; + } + + const node = try self.createNode(arena, ast.NodeAsmInput, + ast.NodeAsmInput { + .base = undefined, + .symbolic_name = undefined, + .constraint = undefined, + .expr = undefined, + } + ); + try items.append(node); + + stack.append(State { .AsmInputItems = items }) catch unreachable; + try stack.append(State { .IfToken = Token.Id.Comma }); + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); + try stack.append(State { .ExpectToken = Token.Id.RBracket }); + try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + continue; + }, + State.AsmClopperItems => |items| { + stack.append(State { .AsmClopperItems = items }) catch unreachable; + try stack.append(State { .IfToken = Token.Id.Comma }); + try stack.append(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } }); + continue; + }, + + + State.ExprListItemOrEnd => |list_state| { + if (self.eatToken(list_state.end)) |token| { + *list_state.ptr = token; + continue; + } + + stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } }); + continue; + }, + State.ExprListCommaOrEnd => |list_state| { + if (try self.expectCommaOrEnd(list_state.end)) |end| { + *list_state.ptr = end; + continue; + } else { + stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable; + continue; + } + }, + State.FieldInitListItemOrEnd => |list_state| { + if (self.eatToken(Token.Id.RBrace)) |rbrace| { + *list_state.ptr = rbrace; + continue; + } + + const node = try self.createNode(arena, ast.NodeFieldInitializer, + ast.NodeFieldInitializer { + .base = undefined, + .period_token = undefined, + .name_token = undefined, + .expr = undefined, + } + ); + try list_state.list.append(node); + + stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } }); + try stack.append(State { .ExpectToken = Token.Id.Equal }); + try stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Identifier, + .ptr = &node.name_token, + } + }); + try stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Period, + .ptr = &node.period_token, + } + }); + continue; + }, + State.FieldInitListCommaOrEnd => |list_state| { + if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { + *list_state.ptr = end; + continue; + } else { + stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable; + continue; + } + }, + State.FieldListCommaOrEnd => |container_decl| { + if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { + container_decl.rbrace_token = end; + continue; + } else { + stack.append(State { .ContainerDecl = container_decl }) catch unreachable; + continue; + } + }, + State.IdentifierListItemOrEnd => |list_state| { + if (self.eatToken(Token.Id.RBrace)) |rbrace| { + *list_state.ptr = rbrace; + continue; + } + + stack.append(State { .IdentifierListCommaOrEnd = list_state }) catch unreachable; + try stack.append(State { .Identifier = OptionalCtx { .Required = try list_state.list.addOne() } }); + continue; + }, + State.IdentifierListCommaOrEnd => |list_state| { + if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { + *list_state.ptr = end; + continue; + } else { + stack.append(State { .IdentifierListItemOrEnd = list_state }) catch unreachable; + continue; + } + }, + State.SwitchCaseOrEnd => |list_state| { + if (self.eatToken(Token.Id.RBrace)) |rbrace| { + *list_state.ptr = rbrace; + continue; + } + + const node = try self.createNode(arena, ast.NodeSwitchCase, + ast.NodeSwitchCase { + .base = undefined, + .items = ArrayList(&ast.Node).init(arena), + .payload = null, + .expr = undefined, + } + ); + try list_state.list.append(node); + stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable; + try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); + try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.append(State { .SwitchCaseFirstItem = &node.items }); + continue; + }, + State.SwitchCaseCommaOrEnd => |list_state| { + if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { + *list_state.ptr = end; + continue; + } else { + stack.append(State { .SwitchCaseOrEnd = list_state }) catch unreachable; + continue; + } + }, + State.SwitchCaseFirstItem => |case_items| { + const token = self.getNextToken(); + if (token.id == Token.Id.Keyword_else) { + const else_node = try self.createAttachNode(arena, case_items, ast.NodeSwitchElse, + ast.NodeSwitchElse { + .base = undefined, + .token = token, + } + ); + try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); + continue; + } else { + self.putBackToken(token); + try stack.append(State { .SwitchCaseItem = case_items }); + continue; + } + }, + State.SwitchCaseItem => |case_items| { + stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; + try stack.append(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } }); + }, + State.SwitchCaseItemCommaOrEnd => |case_items| { + if ((try self.expectCommaOrEnd(Token.Id.EqualAngleBracketRight)) == null) { + stack.append(State { .SwitchCaseItem = case_items }) catch unreachable; + } + continue; + }, + + + State.SuspendBody => |suspend_node| { + if (suspend_node.payload != null) { + try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } }); + } + continue; + }, + State.AsyncAllocator => |async_node| { + if (self.eatToken(Token.Id.AngleBracketLeft) == null) { + continue; + } + + async_node.rangle_bracket = Token(undefined); + try stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.AngleBracketRight, + .ptr = &??async_node.rangle_bracket, + } + }); + try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } }); + continue; + }, + State.AsyncEnd => |ctx| { + const node = ctx.ctx.get() ?? continue; + + switch (node.id) { + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", node); + fn_proto.async_attr = ctx.attribute; + continue; + }, + ast.Node.Id.SuffixOp => { + const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node); + if (suffix_op.op == ast.NodeSuffixOp.SuffixOp.Call) { + suffix_op.op.Call.async_attr = ctx.attribute; + continue; + } + + return self.parseError(node.firstToken(), "expected {}, found {}.", + @tagName(ast.NodeSuffixOp.SuffixOp.Call), + @tagName(suffix_op.op)); + }, + else => { + return self.parseError(node.firstToken(), "expected {} or {}, found {}.", + @tagName(ast.NodeSuffixOp.SuffixOp.Call), + @tagName(ast.Node.Id.FnProto), + @tagName(node.id)); + } + } + }, + + + State.ExternType => |ctx| { + if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| { + const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeFnProto, + ast.NodeFnProto { + .base = undefined, + .visib_token = null, + .name_token = null, + .fn_token = fn_token, + .params = ArrayList(&ast.Node).init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = ctx.extern_token, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + } + ); + stack.append(State { .FnProto = fn_proto }) catch unreachable; + continue; + } + + stack.append(State { + .ContainerKind = ContainerKindCtx { + .opt_ctx = ctx.opt_ctx, + .ltoken = ctx.extern_token, + .layout = ast.NodeContainerDecl.Layout.Extern, + }, + }) catch unreachable; + continue; + }, + State.SliceOrArrayAccess => |node| { + var token = self.getNextToken(); + switch (token.id) { + Token.Id.Ellipsis2 => { + const start = node.op.ArrayAccess; + node.op = ast.NodeSuffixOp.SuffixOp { + .Slice = ast.NodeSuffixOp.SliceRange { + .start = start, + .end = null, + } + }; + + stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.RBracket, + .ptr = &node.rtoken, + } + }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } }); + continue; + }, + Token.Id.RBracket => { + node.rtoken = token; + continue; + }, + else => { + return self.parseError(token, "expected ']' or '..', found {}", @tagName(token.id)); + } + } + }, + State.SliceOrArrayType => |node| { + if (self.eatToken(Token.Id.RBracket)) |_| { + node.op = ast.NodePrefixOp.PrefixOp { + .SliceType = ast.NodePrefixOp.AddrOfInfo { + .align_expr = null, + .bit_offset_start_token = null, + .bit_offset_end_token = null, + .const_token = null, + .volatile_token = null, + } + }; + stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + try stack.append(State { .AddrOfModifiers = &node.op.SliceType }); + continue; + } + + node.op = ast.NodePrefixOp.PrefixOp { .ArrayType = undefined }; + stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + try stack.append(State { .ExpectToken = Token.Id.RBracket }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } }); + continue; + }, + State.AddrOfModifiers => |addr_of_info| { + var token = self.getNextToken(); + switch (token.id) { + Token.Id.Keyword_align => { + stack.append(state) catch unreachable; + if (addr_of_info.align_expr != null) { + return self.parseError(token, "multiple align qualifiers"); + } + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + Token.Id.Keyword_const => { + stack.append(state) catch 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 => { + stack.append(state) catch 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); + continue; + }, + } + }, + + + State.Payload => |opt_ctx| { + const token = self.getNextToken(); + if (token.id != Token.Id.Pipe) { + if (opt_ctx != OptionalCtx.Optional) { + return self.parseError(token, "expected {}, found {}.", + @tagName(Token.Id.Pipe), + @tagName(token.id)); + } + + self.putBackToken(token); + continue; + } + + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePayload, + ast.NodePayload { + .base = undefined, + .lpipe = token, + .error_symbol = undefined, + .rpipe = undefined + } + ); + + stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } + }) catch unreachable; + try stack.append(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } }); + continue; + }, + State.PointerPayload => |opt_ctx| { + const token = self.getNextToken(); + if (token.id != Token.Id.Pipe) { + if (opt_ctx != OptionalCtx.Optional) { + return self.parseError(token, "expected {}, found {}.", + @tagName(Token.Id.Pipe), + @tagName(token.id)); + } + + self.putBackToken(token); + continue; + } + + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePointerPayload, + ast.NodePointerPayload { + .base = undefined, + .lpipe = token, + .ptr_token = null, + .value_symbol = undefined, + .rpipe = undefined + } + ); + + stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } + }) catch unreachable; + try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); + try stack.append(State { + .OptionalTokenSave = OptionalTokenSave { + .id = Token.Id.Asterisk, + .ptr = &node.ptr_token, + } + }); + continue; + }, + State.PointerIndexPayload => |opt_ctx| { + const token = self.getNextToken(); + if (token.id != Token.Id.Pipe) { + if (opt_ctx != OptionalCtx.Optional) { + return self.parseError(token, "expected {}, found {}.", + @tagName(Token.Id.Pipe), + @tagName(token.id)); + } + + self.putBackToken(token); + continue; + } + + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePointerIndexPayload, + ast.NodePointerIndexPayload { + .base = undefined, + .lpipe = token, + .ptr_token = null, + .value_symbol = undefined, + .index_symbol = null, + .rpipe = undefined + } + ); + + stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } + }) catch unreachable; + try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } }); + try stack.append(State { .IfToken = Token.Id.Comma }); + try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); + try stack.append(State { + .OptionalTokenSave = OptionalTokenSave { + .id = Token.Id.Asterisk, + .ptr = &node.ptr_token, + } + }); + continue; + }, + + + State.Expression => |opt_ctx| { + const token = self.getNextToken(); + switch (token.id) { + Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeControlFlowExpression, + ast.NodeControlFlowExpression { + .base = undefined, + .ltoken = token, + .kind = undefined, + .rhs = null, + } + ); + + stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable; + + switch (token.id) { + Token.Id.Keyword_break => { + node.kind = ast.NodeControlFlowExpression.Kind { .Break = null }; + try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } }); + try stack.append(State { .IfToken = Token.Id.Colon }); + }, + Token.Id.Keyword_continue => { + node.kind = ast.NodeControlFlowExpression.Kind { .Continue = null }; + try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } }); + try stack.append(State { .IfToken = Token.Id.Colon }); + }, + Token.Id.Keyword_return => { + node.kind = ast.NodeControlFlowExpression.Kind.Return; + }, + else => unreachable, + } + continue; + }, + Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp, + ast.NodePrefixOp { + .base = undefined, + .op_token = token, + .op = switch (token.id) { + Token.Id.Keyword_try => ast.NodePrefixOp.PrefixOp { .Try = void{} }, + Token.Id.Keyword_cancel => ast.NodePrefixOp.PrefixOp { .Cancel = void{} }, + Token.Id.Keyword_resume => ast.NodePrefixOp.PrefixOp { .Resume = void{} }, + else => unreachable, + }, + .rhs = undefined, + } + ); + + stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + continue; + }, + else => { + if (!try self.parseBlockExpr(&stack, arena, opt_ctx, token)) { + self.putBackToken(token); + stack.append(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable; + } + continue; + } + } + }, + State.RangeExpressionBegin => |opt_ctx| { + stack.append(State { .RangeExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .Expression = opt_ctx }); + continue; + }, + State.RangeExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = ellipsis3, + .op = ast.NodeInfixOp.InfixOp.Range, + .rhs = undefined, + } + ); + stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + continue; + } + }, + State.AssignmentExpressionBegin => |opt_ctx| { + stack.append(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .Expression = opt_ctx }); + continue; + }, + + State.AssignmentExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token = self.getNextToken(); + if (tokenIdToAssignment(token.id)) |ass_id| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token, + .op = ass_id, + .rhs = undefined, + } + ); + stack.append(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + self.putBackToken(token); + continue; + } + }, + + State.UnwrapExpressionBegin => |opt_ctx| { + stack.append(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .BoolOrExpressionBegin = opt_ctx }); + continue; + }, + + State.UnwrapExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token = self.getNextToken(); + if (tokenIdToUnwrapExpr(token.id)) |unwrap_id| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token, + .op = unwrap_id, + .rhs = undefined, + } + ); + + stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); + + if (node.op == ast.NodeInfixOp.InfixOp.Catch) { + try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } }); + } + continue; + } else { + self.putBackToken(token); + continue; + } + }, + + State.BoolOrExpressionBegin => |opt_ctx| { + stack.append(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .BoolAndExpressionBegin = opt_ctx }); + continue; + }, + + State.BoolOrExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (self.eatToken(Token.Id.Keyword_or)) |or_token| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = or_token, + .op = ast.NodeInfixOp.InfixOp.BoolOr, + .rhs = undefined, + } + ); + stack.append(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, + + State.BoolAndExpressionBegin => |opt_ctx| { + stack.append(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .ComparisonExpressionBegin = opt_ctx }); + continue; + }, + + State.BoolAndExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (self.eatToken(Token.Id.Keyword_and)) |and_token| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = and_token, + .op = ast.NodeInfixOp.InfixOp.BoolAnd, + .rhs = undefined, + } + ); + stack.append(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, + + State.ComparisonExpressionBegin => |opt_ctx| { + stack.append(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .BinaryOrExpressionBegin = opt_ctx }); + continue; + }, + + State.ComparisonExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token = self.getNextToken(); + if (tokenIdToComparison(token.id)) |comp_id| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token, + .op = comp_id, + .rhs = undefined, + } + ); + stack.append(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + self.putBackToken(token); + continue; + } + }, + + State.BinaryOrExpressionBegin => |opt_ctx| { + stack.append(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .BinaryXorExpressionBegin = opt_ctx }); + continue; + }, + + State.BinaryOrExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (self.eatToken(Token.Id.Pipe)) |pipe| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = pipe, + .op = ast.NodeInfixOp.InfixOp.BitOr, + .rhs = undefined, + } + ); + stack.append(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, + + State.BinaryXorExpressionBegin => |opt_ctx| { + stack.append(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .BinaryAndExpressionBegin = opt_ctx }); + continue; + }, + + State.BinaryXorExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (self.eatToken(Token.Id.Caret)) |caret| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = caret, + .op = ast.NodeInfixOp.InfixOp.BitXor, + .rhs = undefined, + } + ); + stack.append(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, + + State.BinaryAndExpressionBegin => |opt_ctx| { + stack.append(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .BitShiftExpressionBegin = opt_ctx }); + continue; + }, + + State.BinaryAndExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (self.eatToken(Token.Id.Ampersand)) |ampersand| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = ampersand, + .op = ast.NodeInfixOp.InfixOp.BitAnd, + .rhs = undefined, + } + ); + stack.append(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, + + State.BitShiftExpressionBegin => |opt_ctx| { + stack.append(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .AdditionExpressionBegin = opt_ctx }); + continue; + }, + + State.BitShiftExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token = self.getNextToken(); + if (tokenIdToBitShift(token.id)) |bitshift_id| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token, + .op = bitshift_id, + .rhs = undefined, + } + ); + stack.append(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + self.putBackToken(token); + continue; + } + }, + + State.AdditionExpressionBegin => |opt_ctx| { + stack.append(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .MultiplyExpressionBegin = opt_ctx }); + continue; + }, + + State.AdditionExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token = self.getNextToken(); + if (tokenIdToAddition(token.id)) |add_id| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token, + .op = add_id, + .rhs = undefined, + } + ); + stack.append(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + self.putBackToken(token); + continue; + } + }, + + State.MultiplyExpressionBegin => |opt_ctx| { + stack.append(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .CurlySuffixExpressionBegin = opt_ctx }); + continue; + }, + + State.MultiplyExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token = self.getNextToken(); + if (tokenIdToMultiply(token.id)) |mult_id| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token, + .op = mult_id, + .rhs = undefined, + } + ); + stack.append(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + self.putBackToken(token); + continue; + } + }, + + State.CurlySuffixExpressionBegin => |opt_ctx| { + stack.append(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .IfToken = Token.Id.LBrace }); + try stack.append(State { .TypeExprBegin = opt_ctx }); + continue; + }, + + State.CurlySuffixExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (self.isPeekToken(Token.Id.Period)) { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp, + ast.NodeSuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.NodeSuffixOp.SuffixOp { + .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena), + }, + .rtoken = undefined, + } + ); + stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .IfToken = Token.Id.LBrace }); + try stack.append(State { + .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) { + .list = &node.op.StructInitializer, + .ptr = &node.rtoken, + } + }); + continue; + } + + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp, + ast.NodeSuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.NodeSuffixOp.SuffixOp { + .ArrayInitializer = ArrayList(&ast.Node).init(arena), + }, + .rtoken = undefined, + } + ); + stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .IfToken = Token.Id.LBrace }); + try stack.append(State { + .ExprListItemOrEnd = ExprListCtx { + .list = &node.op.ArrayInitializer, + .end = Token.Id.RBrace, + .ptr = &node.rtoken, + } + }); + continue; + }, + + State.TypeExprBegin => |opt_ctx| { + stack.append(State { .TypeExprEnd = opt_ctx }) catch unreachable; + try stack.append(State { .PrefixOpExpression = opt_ctx }); + continue; + }, + + State.TypeExprEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (self.eatToken(Token.Id.Bang)) |bang| { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = bang, + .op = ast.NodeInfixOp.InfixOp.ErrorUnion, + .rhs = undefined, + } + ); + stack.append(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, + + State.PrefixOpExpression => |opt_ctx| { + const token = self.getNextToken(); + if (tokenIdToPrefixOp(token.id)) |prefix_id| { + var node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp, + ast.NodePrefixOp { + .base = undefined, + .op_token = token, + .op = prefix_id, + .rhs = undefined, + } + ); + + // Treat '**' token as two derefs + if (token.id == Token.Id.AsteriskAsterisk) { + const child = try self.createNode(arena, ast.NodePrefixOp, + ast.NodePrefixOp { + .base = undefined, + .op_token = token, + .op = prefix_id, + .rhs = undefined, + } + ); + node.rhs = &child.base; + node = child; + } + + stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + if (node.op == ast.NodePrefixOp.PrefixOp.AddrOf) { + try stack.append(State { .AddrOfModifiers = &node.op.AddrOf }); + } + continue; + } else { + self.putBackToken(token); + stack.append(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable; + continue; + } + }, + + State.SuffixOpExpressionBegin => |opt_ctx| { + if (self.eatToken(Token.Id.Keyword_async)) |async_token| { + const async_node = try self.createNode(arena, ast.NodeAsyncAttribute, + ast.NodeAsyncAttribute { + .base = undefined, + .async_token = async_token, + .allocator_type = null, + .rangle_bracket = null, + } + ); + stack.append(State { + .AsyncEnd = AsyncEndCtx { + .ctx = opt_ctx, + .attribute = async_node, + } + }) catch unreachable; + try stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }); + try stack.append(State { .PrimaryExpression = opt_ctx.toRequired() }); + try stack.append(State { .AsyncAllocator = async_node }); + continue; + } + + stack.append(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .PrimaryExpression = opt_ctx }); + continue; + }, + + State.SuffixOpExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token = self.getNextToken(); + switch (token.id) { + Token.Id.LParen => { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp, + ast.NodeSuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.NodeSuffixOp.SuffixOp { + .Call = ast.NodeSuffixOp.CallInfo { + .params = ArrayList(&ast.Node).init(arena), + .async_attr = null, + } + }, + .rtoken = undefined, + } + ); + stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { + .ExprListItemOrEnd = ExprListCtx { + .list = &node.op.Call.params, + .end = Token.Id.RParen, + .ptr = &node.rtoken, + } + }); + continue; + }, + Token.Id.LBracket => { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp, + ast.NodeSuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.NodeSuffixOp.SuffixOp { + .ArrayAccess = undefined, + }, + .rtoken = undefined + } + ); + stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .SliceOrArrayAccess = node }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }}); + continue; + }, + Token.Id.Period => { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, + ast.NodeInfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token, + .op = ast.NodeInfixOp.InfixOp.Period, + .rhs = undefined, + } + ); + stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .Identifier = OptionalCtx { .Required = &node.rhs } }); + continue; + }, + else => { + self.putBackToken(token); + continue; + }, + } + }, + + State.PrimaryExpression => |opt_ctx| { + const token = self.getNextToken(); + switch (token.id) { + Token.Id.IntegerLiteral => { + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeStringLiteral, token); + continue; + }, + Token.Id.FloatLiteral => { + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeFloatLiteral, token); + continue; + }, + Token.Id.CharLiteral => { + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeCharLiteral, token); + continue; + }, + Token.Id.Keyword_undefined => { + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeUndefinedLiteral, token); + continue; + }, + Token.Id.Keyword_true, Token.Id.Keyword_false => { + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeBoolLiteral, token); + continue; + }, + Token.Id.Keyword_null => { + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeNullLiteral, token); + continue; + }, + Token.Id.Keyword_this => { + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeThisLiteral, token); + continue; + }, + Token.Id.Keyword_var => { + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeVarType, token); + continue; + }, + Token.Id.Keyword_unreachable => { + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeUnreachable, token); + continue; + }, + Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { + opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable); + continue; + }, + Token.Id.LParen => { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeGroupedExpression, + ast.NodeGroupedExpression { + .base = undefined, + .lparen = token, + .expr = undefined, + .rparen = undefined, + } + ); + stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.RParen, + .ptr = &node.rparen, + } + }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); + continue; + }, + Token.Id.Builtin => { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeBuiltinCall, + ast.NodeBuiltinCall { + .base = undefined, + .builtin_token = token, + .params = ArrayList(&ast.Node).init(arena), + .rparen_token = undefined, + } + ); + stack.append(State { + .ExprListItemOrEnd = ExprListCtx { + .list = &node.params, + .end = Token.Id.RParen, + .ptr = &node.rparen_token, + } + }) catch unreachable; + try stack.append(State { .ExpectToken = Token.Id.LParen, }); + continue; + }, + Token.Id.LBracket => { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp, + ast.NodePrefixOp { + .base = undefined, + .op_token = token, + .op = undefined, + .rhs = undefined, + } + ); + stack.append(State { .SliceOrArrayType = node }) catch unreachable; + continue; + }, + Token.Id.Keyword_error => { + stack.append(State { + .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx { + .error_token = token, + .opt_ctx = opt_ctx + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_packed => { + stack.append(State { + .ContainerKind = ContainerKindCtx { + .opt_ctx = opt_ctx, + .ltoken = token, + .layout = ast.NodeContainerDecl.Layout.Packed, + }, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_extern => { + stack.append(State { + .ExternType = ExternTypeCtx { + .opt_ctx = opt_ctx, + .extern_token = token, + }, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => { + self.putBackToken(token); + stack.append(State { + .ContainerKind = ContainerKindCtx { + .opt_ctx = opt_ctx, + .ltoken = token, + .layout = ast.NodeContainerDecl.Layout.Auto, + }, + }) catch unreachable; + continue; + }, + Token.Id.Identifier => { + stack.append(State { + .MaybeLabeledExpression = MaybeLabeledExpressionCtx { + .label = token, + .opt_ctx = opt_ctx + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_fn => { + const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto, + ast.NodeFnProto { + .base = undefined, + .visib_token = null, + .name_token = null, + .fn_token = token, + .params = ArrayList(&ast.Node).init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = null, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + } + ); + stack.append(State { .FnProto = fn_proto }) catch unreachable; + continue; + }, + Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { + const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto, + ast.NodeFnProto { + .base = undefined, + .visib_token = null, + .name_token = null, + .fn_token = undefined, + .params = ArrayList(&ast.Node).init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = null, + .cc_token = token, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + } + ); + stack.append(State { .FnProto = fn_proto }) catch unreachable; + try stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token + } + }); + continue; + }, + Token.Id.Keyword_asm => { + const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeAsm, + ast.NodeAsm { + .base = undefined, + .asm_token = token, + .volatile_token = null, + .template = undefined, + //.tokens = ArrayList(ast.NodeAsm.AsmToken).init(arena), + .outputs = ArrayList(&ast.NodeAsmOutput).init(arena), + .inputs = ArrayList(&ast.NodeAsmInput).init(arena), + .cloppers = ArrayList(&ast.Node).init(arena), + .rparen = undefined, + } + ); + stack.append(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.RParen, + .ptr = &node.rparen, + } + }) catch unreachable; + try stack.append(State { .AsmClopperItems = &node.cloppers }); + try stack.append(State { .IfToken = Token.Id.Colon }); + try stack.append(State { .AsmInputItems = &node.inputs }); + try stack.append(State { .IfToken = Token.Id.Colon }); + try stack.append(State { .AsmOutputItems = &node.outputs }); + try stack.append(State { .IfToken = Token.Id.Colon }); + try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.template } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State { + .OptionalTokenSave = OptionalTokenSave { + .id = Token.Id.Keyword_volatile, + .ptr = &node.volatile_token, + } + }); + }, + Token.Id.Keyword_inline => { + stack.append(State { + .Inline = InlineCtx { + .label = null, + .inline_token = token, + .opt_ctx = opt_ctx, + } + }) catch unreachable; + continue; + }, + else => { + if (!try self.parseBlockExpr(&stack, arena, opt_ctx, token)) { + self.putBackToken(token); + if (opt_ctx != OptionalCtx.Optional) { + return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)); + } + } + continue; + } + } + }, + + + State.ErrorTypeOrSetDecl => |ctx| { + if (self.eatToken(Token.Id.LBrace) == null) { + _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.NodeErrorType, ctx.error_token); + continue; + } + + const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeErrorSetDecl, + ast.NodeErrorSetDecl { + .base = undefined, + .error_token = ctx.error_token, + .decls = ArrayList(&ast.Node).init(arena), + .rbrace_token = undefined, + } + ); + + stack.append(State { + .IdentifierListItemOrEnd = ListSave(&ast.Node) { + .list = &node.decls, + .ptr = &node.rbrace_token, + } + }) catch unreachable; + continue; + }, + State.StringLiteral => |opt_ctx| { + const token = self.getNextToken(); + opt_ctx.store( + (try self.parseStringLiteral(arena, token)) ?? { + self.putBackToken(token); + if (opt_ctx != OptionalCtx.Optional) { + return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)); + } + + continue; + } + ); + }, + State.Identifier => |opt_ctx| { + if (self.eatToken(Token.Id.Identifier)) |ident_token| { + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeIdentifier, ident_token); + continue; + } + + if (opt_ctx != OptionalCtx.Optional) { + const token = self.getNextToken(); + return self.parseError(token, "expected identifier, found {}", @tagName(token.id)); + } + }, + + + State.ExpectToken => |token_id| { + _ = try self.expectToken(token_id); + continue; + }, + State.ExpectTokenSave => |expect_token_save| { + *expect_token_save.ptr = try self.expectToken(expect_token_save.id); + continue; + }, + State.IfToken => |token_id| { + if (self.eatToken(token_id)) |_| { + continue; + } + + _ = stack.pop(); + continue; + }, + State.IfTokenSave => |if_token_save| { + if (self.eatToken(if_token_save.id)) |token| { + *if_token_save.ptr = token; + continue; + } + + _ = stack.pop(); + continue; + }, + State.OptionalTokenSave => |optional_token_save| { + if (self.eatToken(optional_token_save.id)) |token| { + *optional_token_save.ptr = token; + continue; + } + + continue; + }, } } } @@ -2807,10 +2853,10 @@ pub const Parser = struct { } } - fn parseBlockExpr(self: &Parser, stack: &ArrayList(State), arena: &mem.Allocator, dest_ptr: &const DestPtr, token: &const Token) !bool { + fn parseBlockExpr(self: &Parser, stack: &ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx, token: &const Token) !bool { switch (token.id) { Token.Id.Keyword_suspend => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuspend, + const node = try self.createToCtxNode(arena, ctx, ast.NodeSuspend, ast.NodeSuspend { .base = undefined, .suspend_token = *token, @@ -2820,11 +2866,11 @@ pub const Parser = struct { ); stack.append(State { .SuspendBody = node }) catch unreachable; - try stack.append(State { .Payload = &node.payload }); + try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); return true; }, Token.Id.Keyword_if => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeIf, + const node = try self.createToCtxNode(arena, ctx, ast.NodeIf, ast.NodeIf { .base = undefined, .if_token = *token, @@ -2836,10 +2882,10 @@ pub const Parser = struct { ); stack.append(State { .Else = &node.@"else" }) catch unreachable; - try stack.append(State { .Expression = DestPtr { .Field = &node.body } }); - try stack.append(State { .PointerPayload = &node.payload }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = DestPtr { .Field = &node.condition } }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } }); try stack.append(State { .ExpectToken = Token.Id.LParen }); return true; }, @@ -2849,7 +2895,7 @@ pub const Parser = struct { .label = null, .inline_token = null, .loop_token = *token, - .dest_ptr = *dest_ptr, + .opt_ctx = *ctx, } }) catch unreachable; return true; @@ -2860,13 +2906,13 @@ pub const Parser = struct { .label = null, .inline_token = null, .loop_token = *token, - .dest_ptr = *dest_ptr, + .opt_ctx = *ctx, } }) catch unreachable; return true; }, Token.Id.Keyword_switch => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSwitch, + const node = try self.createToCtxNode(arena, ctx, ast.NodeSwitch, ast.NodeSwitch { .base = undefined, .switch_token = *token, @@ -2884,23 +2930,23 @@ pub const Parser = struct { }) catch unreachable; try stack.append(State { .ExpectToken = Token.Id.LBrace }); try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = DestPtr { .Field = &node.expr } }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); try stack.append(State { .ExpectToken = Token.Id.LParen }); return true; }, Token.Id.Keyword_comptime => { - const node = try self.createToDestNode(arena, dest_ptr, ast.NodeComptime, + const node = try self.createToCtxNode(arena, ctx, ast.NodeComptime, ast.NodeComptime { .base = undefined, .comptime_token = *token, .expr = undefined, } ); - try stack.append(State { .Expression = DestPtr { .Field = &node.expr } }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); return true; }, Token.Id.LBrace => { - const block = try self.createToDestNode(arena, dest_ptr, ast.NodeBlock, + const block = try self.createToCtxNode(arena, ctx, ast.NodeBlock, ast.NodeBlock { .base = undefined, .label = null, @@ -2918,22 +2964,16 @@ pub const Parser = struct { } } - fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, maybe_ptr: ?&Token, state_after_comma: &const State) !void { + fn expectCommaOrEnd(self: &Parser, end: @TagType(Token.Id)) !?Token { var token = self.getNextToken(); switch (token.id) { - Token.Id.Comma => { - stack.append(state_after_comma) catch unreachable; - }, + Token.Id.Comma => return null, else => { - const IdTag = @TagType(Token.Id); - if (IdTag(*end) == token.id) { - if (maybe_ptr) |ptr| { - *ptr = token; - } - return; + if (end == token.id) { + return token; } - try self.parseError(stack, token, "expected ',' or {}, found {}", @tagName(*end), @tagName(token.id)); + return self.parseError(token, "expected ',' or {}, found {}", @tagName(end), @tagName(token.id)); }, } } @@ -2960,8 +3000,16 @@ pub const Parser = struct { }; } - fn tokenIdToComparison(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp { - return switch (*id) { + fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + return switch (id) { + Token.Id.Keyword_catch => ast.NodeInfixOp.InfixOp { .Catch = null }, + Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp { .UnwrapMaybe = void{} }, + else => null, + }; + } + + fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + return switch (id) { Token.Id.BangEqual => ast.NodeInfixOp.InfixOp { .BangEqual = void{} }, Token.Id.EqualEqual => ast.NodeInfixOp.InfixOp { .EqualEqual = void{} }, Token.Id.AngleBracketLeft => ast.NodeInfixOp.InfixOp { .LessThan = void{} }, @@ -2972,16 +3020,16 @@ pub const Parser = struct { }; } - fn tokenIdToBitShift(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp { - return switch (*id) { + fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + return switch (id) { Token.Id.AngleBracketAngleBracketLeft => ast.NodeInfixOp.InfixOp { .BitShiftLeft = void{} }, Token.Id.AngleBracketAngleBracketRight => ast.NodeInfixOp.InfixOp { .BitShiftRight = void{} }, else => null, }; } - fn tokenIdToAddition(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp { - return switch (*id) { + fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + return switch (id) { Token.Id.Minus => ast.NodeInfixOp.InfixOp { .Sub = void{} }, Token.Id.MinusPercent => ast.NodeInfixOp.InfixOp { .SubWrap = void{} }, Token.Id.Plus => ast.NodeInfixOp.InfixOp { .Add = void{} }, @@ -2991,8 +3039,8 @@ pub const Parser = struct { }; } - fn tokenIdToMultiply(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp { - return switch (*id) { + fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + return switch (id) { Token.Id.Slash => ast.NodeInfixOp.InfixOp { .Div = void{} }, Token.Id.Asterisk => ast.NodeInfixOp.InfixOp { .Mult = void{} }, Token.Id.AsteriskAsterisk => ast.NodeInfixOp.InfixOp { .ArrayMult = void{} }, @@ -3003,8 +3051,8 @@ pub const Parser = struct { }; } - fn tokenIdToPrefixOp(id: &const Token.Id) ?ast.NodePrefixOp.PrefixOp { - return switch (*id) { + fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.NodePrefixOp.PrefixOp { + return switch (id) { Token.Id.Bang => ast.NodePrefixOp.PrefixOp { .BoolNot = void{} }, Token.Id.Tilde => ast.NodePrefixOp.PrefixOp { .BitNot = void{} }, Token.Id.Minus => ast.NodePrefixOp.PrefixOp { .Negation = void{} }, @@ -3049,9 +3097,9 @@ pub const Parser = struct { return node; } - fn createToDestNode(self: &Parser, arena: &mem.Allocator, dest_ptr: &const DestPtr, comptime T: type, init_to: &const T) !&T { + fn createToCtxNode(self: &Parser, arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T { const node = try self.createNode(arena, T, init_to); - dest_ptr.store(&node.base); + opt_ctx.store(&node.base); return node; } @@ -3065,51 +3113,38 @@ pub const Parser = struct { ); } - fn parseError(self: &Parser, stack: &ArrayList(State), token: &const Token, comptime fmt: []const u8, args: ...) !void { - // Before reporting an error. We pop the stack to see if our state was optional - self.revertIfOptional(stack) catch { - const loc = self.tokenizer.getTokenLocation(0, token); - warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args); - warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]); - { - var i: usize = 0; - while (i < loc.column) : (i += 1) { - warn(" "); - } - } - { - const caret_count = token.end - token.start; - var i: usize = 0; - while (i < caret_count) : (i += 1) { - warn("~"); - } - } - warn("\n"); - return error.ParseError; - }; + fn createToCtxLiteral(self: &Parser, arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token: &const Token) !&T { + const node = try self.createLiteral(arena, T, token); + opt_ctx.store(&node.base); + + return node; } - fn revertIfOptional(self: &Parser, stack: &ArrayList(State)) !void { - while (stack.popOrNull()) |state| { - switch (state) { - State.Optional => |revert| { - *self = revert.parser; - *self.tokenizer = revert.tokenizer; - *revert.ptr = null; - return; - }, - else => { } + fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) { + const loc = self.tokenizer.getTokenLocation(0, token); + warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args); + warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]); + { + var i: usize = 0; + while (i < loc.column) : (i += 1) { + warn(" "); } } - - return error.NoOptionalStateFound; + { + const caret_count = token.end - token.start; + var i: usize = 0; + while (i < caret_count) : (i += 1) { + warn("~"); + } + } + warn("\n"); + return error.ParseError; } - fn expectToken(self: &Parser, stack: &ArrayList(State), id: @TagType(Token.Id)) !?Token { + fn expectToken(self: &Parser, id: @TagType(Token.Id)) !Token { const token = self.getNextToken(); if (token.id != id) { - try self.parseError(stack, token, "expected {}, found {}", @tagName(id), @tagName(token.id)); - return null; + return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id)); } return token; } @@ -3424,7 +3459,7 @@ pub const Parser = struct { } if (suspend_node.payload) |payload| { - try stack.append(RenderState { .Expression = &payload.base }); + try stack.append(RenderState { .Expression = payload }); try stack.append(RenderState { .Text = " " }); } }, @@ -3435,7 +3470,7 @@ pub const Parser = struct { if (prefix_op_node.op == ast.NodeInfixOp.InfixOp.Catch) { if (prefix_op_node.op.Catch) |payload| { try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = &payload.base }); + try stack.append(RenderState { .Expression = payload }); } try stack.append(RenderState { .Text = " catch " }); } else { @@ -3624,17 +3659,25 @@ pub const Parser = struct { }, ast.Node.Id.ControlFlowExpression => { const flow_expr = @fieldParentPtr(ast.NodeControlFlowExpression, "base", base); + + if (flow_expr.rhs) |rhs| { + try stack.append(RenderState { .Expression = rhs }); + try stack.append(RenderState { .Text = " " }); + } + switch (flow_expr.kind) { - ast.NodeControlFlowExpression.Kind.Break => |maybe_blk_token| { + ast.NodeControlFlowExpression.Kind.Break => |maybe_label| { try stream.print("break"); - if (maybe_blk_token) |blk_token| { - try stream.print(" :{}", self.tokenizer.getTokenSlice(blk_token)); + if (maybe_label) |label| { + try stream.print(" :"); + try stack.append(RenderState { .Expression = label }); } }, - ast.NodeControlFlowExpression.Kind.Continue => |maybe_blk_token| { + ast.NodeControlFlowExpression.Kind.Continue => |maybe_label| { try stream.print("continue"); - if (maybe_blk_token) |blk_token| { - try stream.print(" :{}", self.tokenizer.getTokenSlice(blk_token)); + if (maybe_label) |label| { + try stream.print(" :"); + try stack.append(RenderState { .Expression = label }); } }, ast.NodeControlFlowExpression.Kind.Return => { @@ -3642,25 +3685,20 @@ pub const Parser = struct { }, } - - if (flow_expr.rhs) |rhs| { - try stream.print(" "); - try stack.append(RenderState { .Expression = rhs }); - } }, ast.Node.Id.Payload => { const payload = @fieldParentPtr(ast.NodePayload, "base", base); try stack.append(RenderState { .Text = "|"}); - try stack.append(RenderState { .Expression = &payload.error_symbol.base }); + try stack.append(RenderState { .Expression = payload.error_symbol }); try stack.append(RenderState { .Text = "|"}); }, ast.Node.Id.PointerPayload => { const payload = @fieldParentPtr(ast.NodePointerPayload, "base", base); try stack.append(RenderState { .Text = "|"}); - try stack.append(RenderState { .Expression = &payload.value_symbol.base }); + try stack.append(RenderState { .Expression = payload.value_symbol }); - if (payload.is_ptr) { - try stack.append(RenderState { .Text = "*"}); + if (payload.ptr_token) |ptr_token| { + try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(ptr_token) }); } try stack.append(RenderState { .Text = "|"}); @@ -3670,14 +3708,14 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "|"}); if (payload.index_symbol) |index_symbol| { - try stack.append(RenderState { .Expression = &index_symbol.base }); + try stack.append(RenderState { .Expression = index_symbol }); try stack.append(RenderState { .Text = ", "}); } - try stack.append(RenderState { .Expression = &payload.value_symbol.base }); + try stack.append(RenderState { .Expression = payload.value_symbol }); - if (payload.is_ptr) { - try stack.append(RenderState { .Text = "*"}); + if (payload.ptr_token) |ptr_token| { + try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(ptr_token) }); } try stack.append(RenderState { .Text = "|"}); @@ -3809,7 +3847,7 @@ pub const Parser = struct { i -= 1; const node = decls[i]; try stack.append(RenderState { .Text = "," }); - try stack.append(RenderState { .Expression = &node.base}); + try stack.append(RenderState { .Expression = node }); try stack.append(RenderState.PrintIndent); try stack.append(RenderState { .Text = blk: { @@ -3961,7 +3999,7 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = switch_case.expr }); if (switch_case.payload) |payload| { try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = &payload.base }); + try stack.append(RenderState { .Expression = payload }); } try stack.append(RenderState { .Text = " => "}); @@ -4003,7 +4041,7 @@ pub const Parser = struct { if (else_node.payload) |payload| { try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = &payload.base }); + try stack.append(RenderState { .Expression = payload }); } }, ast.Node.Id.While => { @@ -4048,7 +4086,7 @@ pub const Parser = struct { } if (while_node.payload) |payload| { - try stack.append(RenderState { .Expression = &payload.base }); + try stack.append(RenderState { .Expression = payload }); try stack.append(RenderState { .Text = " " }); } @@ -4091,7 +4129,7 @@ pub const Parser = struct { } if (for_node.payload) |payload| { - try stack.append(RenderState { .Expression = &payload.base }); + try stack.append(RenderState { .Expression = payload }); try stack.append(RenderState { .Text = " " }); } @@ -4124,7 +4162,7 @@ pub const Parser = struct { if (@"else".payload) |payload| { try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = &payload.base }); + try stack.append(RenderState { .Expression = payload }); } try stack.append(RenderState { .Text = " " }); @@ -4138,7 +4176,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = " " }); if (if_node.payload) |payload| { - try stack.append(RenderState { .Expression = &payload.base }); + try stack.append(RenderState { .Expression = payload }); try stack.append(RenderState { .Text = " " }); } @@ -4150,8 +4188,8 @@ pub const Parser = struct { const asm_node = @fieldParentPtr(ast.NodeAsm, "base", base); try stream.print("{} ", self.tokenizer.getTokenSlice(asm_node.asm_token)); - if (asm_node.is_volatile) { - try stream.write("volatile "); + if (asm_node.volatile_token) |volatile_token| { + try stream.print("{} ", self.tokenizer.getTokenSlice(volatile_token)); } try stack.append(RenderState { .Indent = indent }); @@ -4241,7 +4279,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = " ("}); try stack.append(RenderState { .Expression = asm_input.constraint }); try stack.append(RenderState { .Text = "] "}); - try stack.append(RenderState { .Expression = &asm_input.symbolic_name.base}); + try stack.append(RenderState { .Expression = asm_input.symbolic_name }); try stack.append(RenderState { .Text = "["}); }, ast.Node.Id.AsmOutput => { @@ -4260,7 +4298,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = " ("}); try stack.append(RenderState { .Expression = asm_output.constraint }); try stack.append(RenderState { .Text = "] "}); - try stack.append(RenderState { .Expression = &asm_output.symbolic_name.base}); + try stack.append(RenderState { .Expression = asm_output.symbolic_name }); try stack.append(RenderState { .Text = "["}); },