Commit 0d629421c9
Changed files (2)
std
zig
std/zig/parse2.zig
@@ -1,2670 +0,0 @@
-const std = @import("../std.zig");
-const assert = std.debug.assert;
-const Allocator = std.mem.Allocator;
-const ast = std.zig.ast;
-const Node = ast.Node;
-const Tree = ast.Tree;
-const Error = ast.Error;
-const TokenIndex = ast.TokenIndex;
-const Token = std.zig.Token;
-const TokenIterator = Tree.TokenList.Iterator;
-
-pub fn parse(allocator: *Allocator, source: []const u8) !Tree {
- var tree_arena = std.heap.ArenaAllocator.init(allocator);
- errdefer tree_arena.deinit();
- const arena = &tree_arena.allocator;
-
- var token_list = Tree.TokenList.init(arena);
- var tokenizer = std.zig.Tokenizer.init(source);
- while (true) {
- const tree_token = try token_list.addOne();
- tree_token.* = tokenizer.next();
- if (tree_token.id == .Eof) break;
- }
- var it = token_list.iterator(0);
-
- while (it.peek().?.id == .LineComment) _ = it.next();
-
- var tree = Tree{
- .source = source,
- .root_node = undefined,
- .tokens = token_list,
- .errors = Tree.ErrorList.init(arena),
- // TODO: Remove (not used/needed anywhere)
- .arena_allocator = tree_arena,
- };
-
- tree.root_node = try parseRoot(&tree.arena_allocator.allocator, &it, &tree);
-
- return tree;
-}
-
-// Root <- skip ContainerMembers eof
-fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node.Root {
- const node = try arena.create(Node.Root);
- node.* = Node.Root{
- .base = Node{ .id = .Root },
- .decls = undefined,
- .doc_comments = null,
- .shebang = null,
- .eof_token = undefined,
- };
- node.decls = (try parseContainerMembers(arena, it, tree, .Keyword_struct)) orelse return node;
- node.eof_token = eatToken(it, .Eof) orelse unreachable;
- return node;
-}
-
-// ContainerMembers
-// <- TestDecl ContainerMembers
-// / TopLevelComptime ContainerMembers
-// / KEYWORD_pub? TopLevelDecl ContainerMembers
-// / KEYWORD_pub? ContainerField COMMA ContainerMembers
-// / KEYWORD_pub? ContainerField
-// /
-fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree, kind: Token.Id) !?Node.Root.DeclList {
- var list = Node.Root.DeclList.init(arena);
-
- while (true) {
- if (try parseTestDecl(arena, it, tree)) |node| {
- try list.push(node);
- continue;
- }
-
- if (try parseTopLevelComptime(arena, it, tree)) |node| {
- try list.push(node);
- continue;
- }
-
- const visibility_token = eatToken(it, .Keyword_pub);
-
- if (try parseTopLevelDecl(arena, it, tree, visibility_token)) |node| {
- try list.push(node);
- continue;
- }
-
- if (try parseContainerField(arena, it, tree, kind)) |node| {
- if (node.cast(Node.StructField)) |struct_field| struct_field.visib_token = visibility_token;
- try list.push(node);
- if (eatToken(it, .Comma)) |_| continue else break;
- }
-
- // Dangling pub
- if (visibility_token != null) {
- try tree.errors.push(Error{
- .ExpectedPubItem = Error.ExpectedPubItem{ .token = it.peek().?.start },
- });
- return null;
- }
-
- break;
- }
-
- return list;
-}
-
-// TestDecl <- KEYWORD_test STRINGLITERAL Block
-fn parseTestDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const test_token = eatToken(it, .Keyword_test) orelse return null;
- const name_node = (try expectNode(arena, it, tree, parseStringLiteral, Error{
- .ExpectedStringLiteral = Error.ExpectedStringLiteral{ .token = it.peek().?.start },
- })) orelse return null;
- const block_node = (try expectNode(
- arena,
- it,
- tree,
- parseBlock,
- Error{ .ExpectedLBrace = Error.ExpectedLBrace{ .token = it.peek().?.start } },
- )) orelse return null;
-
- const test_node = try arena.create(Node.TestDecl);
- test_node.* = Node.TestDecl{
- .base = Node{ .id = .TestDecl },
- .doc_comments = null,
- .test_token = test_token,
- .name = name_node,
- .body_node = block_node,
- };
- return &test_node.base;
-}
-
-// TopLevelComptime <- KEYWORD_comptime BlockExpr
-fn parseTopLevelComptime(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const tok = eatToken(it, .Keyword_comptime) orelse return null;
- const block_node = (try expectNode(arena, it, tree, parseBlockExpr, Error{
- .ExpectedLabelOrLBrace = Error.ExpectedLabelOrLBrace{ .token = it.peek().?.start },
- })) orelse return null;
-
- const comptime_node = try arena.create(Node.Comptime);
- comptime_node.* = Node.Comptime{
- .base = Node{ .id = .Comptime },
- .doc_comments = null,
- .comptime_token = tok,
- .expr = block_node,
- };
- return &comptime_node.base;
-}
-
-// TopLevelDecl
-// <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block)
-// / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl
-// / KEYWORD_use Expr SEMICOLON
-fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree, vis: ?TokenIndex) !?*Node {
- const export_token = eatToken(it, .Keyword_export);
- const extern_token = if (export_token == null) eatToken(it, .Keyword_extern) else null;
- const lib_name = if (extern_token != null) try parseStringLiteral(arena, it, tree) else null;
- const inline_token = if (extern_token == null) eatToken(it, .Keyword_inline) else null;
-
- if (try parseFnProto(arena, it, tree)) |node| {
- const fn_node = node.cast(Node.FnProto).?;
-
- fn_node.*.visib_token = vis;
- fn_node.*.extern_export_inline_token = export_token orelse extern_token orelse inline_token;
- fn_node.*.lib_name = lib_name;
-
- if (eatToken(it, .Semicolon)) |_| return node;
- if (try parseBlock(arena, it, tree)) |body_node| {
- fn_node.body_node = body_node;
- return node;
- }
-
- try tree.errors.push(Error{
- .ExpectedSemiOrLBrace = Error.ExpectedSemiOrLBrace{ .token = it.peek().?.start },
- });
- return null;
- }
-
- if (inline_token != null) return null;
-
- const thread_local_token = eatToken(it, .Keyword_threadlocal);
-
- if (try parseVarDecl(arena, it, tree)) |node| {
- var var_decl = node.cast(Node.VarDecl).?;
- var_decl.*.doc_comments = null;
- var_decl.*.visib_token = vis;
- var_decl.*.thread_local_token = thread_local_token;
- var_decl.*.comptime_token = null;
- var_decl.*.extern_export_token = export_token orelse extern_token;
- var_decl.*.lib_name = lib_name;
- return node;
- }
-
- const use_node = (try parseUse(arena, it, tree)) orelse return null;
- const expr_node = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- const semicolon_token = (try expectToken(it, tree, .Semicolon)) orelse return null;
- const use_node_raw = use_node.cast(Node.Use).?;
- use_node_raw.*.visib_token = vis;
- use_node_raw.*.expr = expr_node;
- use_node_raw.*.semicolon_token = semicolon_token;
-
- return use_node;
-}
-
-// FnProto <- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)
-fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const cc = (try parseFnCC(arena, it, tree)) orelse return null; // null on parse error
- const fn_token = eatToken(it, .Keyword_fn) orelse return null;
- const name_token = eatToken(it, .Identifier);
- const lparen = (try expectToken(it, tree, .LParen)) orelse return null;
- const params = try parseParamDeclList(arena, it, tree);
- const rparen = (try expectToken(it, tree, .RParen)) orelse return null;
- const alignment_node = try parseByteAlign(arena, it, tree);
- const section_expr = try parseLinkSection(arena, it, tree);
- const exclamation_token = eatToken(it, .Bang);
-
- const return_type_expr = blk: {
- if (eatToken(it, .Keyword_var)) |var_token| {
- const node = try arena.create(Node.VarType);
- node.* = Node.VarType{
- .base = Node{ .id = .VarType },
- .token = var_token,
- };
- }
- break :blk (try expectNode(arena, it, tree, parseTypeExpr, Error{
- .ExpectedReturnType = Error.ExpectedReturnType{ .token = it.peek().?.start },
- })) orelse return null;
- };
-
- // TODO: Based on this rule, `!var` is an acceptable return type, but there is no usage
- // or coverage of that yet. The grammar also does not include `Keyword_var` as a choice
- // for PrimaryTypeExpr, but the iterative stage2 parser treats it as one, which actually
- // makes more sense given the return type rule above. Clarify this with @Hejsil.
- // Alternative rule, if `var` were to be included in PrimaryTypeExpr (I think):
- //
- // - FnProto <- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)
- // + FnProto <- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? TypeExpr
- const return_type = if (exclamation_token != null)
- Node.FnProto.ReturnType{
- .InferErrorSet = return_type_expr,
- }
- else
- Node.FnProto.ReturnType{
- .Explicit = return_type_expr,
- };
-
- const fn_proto_node = try arena.create(Node.FnProto);
- fn_proto_node.* = Node.FnProto{
- .base = Node{ .id = .FnProto },
- .doc_comments = null,
- .visib_token = null,
- .fn_token = fn_token,
- .name_token = name_token,
- .params = params,
- .return_type = return_type,
- .var_args_token = undefined, // TODO ?TokenIndex
- .extern_export_inline_token = null,
- .cc_token = null,
- .async_attr = null,
- .body_node = null,
- .lib_name = null,
- .align_expr = null,
- .section_expr = section_expr,
- };
-
- switch (cc) {
- .CC => |token| fn_proto_node.cc_token = token,
- .Extern => |token| fn_proto_node.extern_export_inline_token = token,
- .Async => |node| fn_proto_node.async_attr = node,
- .None => {},
- }
-
- return &fn_proto_node.base;
-}
-
-// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
-fn parseVarDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const mut_token = eatToken(it, .Keyword_const) orelse
- eatToken(it, .Keyword_var) orelse
- return null;
-
- const name_token = (try expectToken(it, tree, .Identifier)) orelse return null;
- const type_node = blk: {
- if (eatToken(it, .Colon)) |_| {
- break :blk (try expectNode(arena, it, tree, parseTypeExpr, Error{
- .ExpectedTypeExpr = Error.ExpectedTypeExpr{ .token = it.peek().?.start },
- })) orelse return null;
- } else break :blk null;
- };
- const align_node = try parseByteAlign(arena, it, tree);
- const section_node = try parseLinkSection(arena, it, tree);
- const eq_token = eatToken(it, .Equal);
- const init_node = blk: {
- if (eq_token) |_| {
- break :blk (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- } else break :blk null;
- };
- const semicolon_token = (try expectToken(it, tree, .Semicolon)) orelse return null;
-
- const node = try arena.create(Node.VarDecl);
- node.* = Node.VarDecl{
- .base = Node{ .id = .VarDecl },
- .mut_token = mut_token,
- .name_token = name_token,
- .eq_token = eq_token orelse 0,
- .type_node = type_node,
- .align_node = align_node,
- .section_node = section_node,
- .init_node = init_node,
- .semicolon_token = semicolon_token,
- // set by caller
- .doc_comments = null,
- .visib_token = null,
- .thread_local_token = null,
- .comptime_token = null,
- .extern_export_token = null,
- .lib_name = null,
- };
-
- return &node.base;
-}
-
-// ContainerField <- IDENTIFIER (COLON TypeExpr)? (EQUAL Expr)?
-fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree, kind: Token.Id) !?*Node {
- const name_token = eatToken(it, .Identifier) orelse return null;
-
- const type_expr = blk: {
- if (eatToken(it, .Colon)) |_| {
- break :blk (try expectNode(arena, it, tree, parseTypeExpr, Error{
- .ExpectedTypeExpr = Error.ExpectedTypeExpr{ .token = it.peek().?.start },
- })) orelse return null;
- } else break :blk null;
- };
-
- // TODO: supply default value to struct field when ast.Node.StructField supports it
- const default_value = blk: {
- if (eatToken(it, .Equal)) |_| {
- break :blk (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- }));
- } else break :blk null;
- };
-
- switch (kind) {
- .Keyword_struct => {
- const node = try arena.create(Node.StructField);
- node.* = Node.StructField{
- .base = Node{ .id = .StructField },
- .name_token = name_token,
- .type_expr = type_expr orelse undefined,
- .doc_comments = null,
- .visib_token = null,
- };
- return &node.base;
- },
- .Keyword_union => {
- const node = try arena.create(Node.UnionTag);
- node.* = Node.UnionTag{
- .base = Node{ .id = .UnionTag },
- .doc_comments = null,
- .name_token = name_token,
- .type_expr = type_expr orelse undefined,
- .value_expr = default_value,
- };
- return &node.base;
- },
- .Keyword_enum => {
- const node = try arena.create(Node.EnumTag);
- node.* = Node.EnumTag{
- .base = Node{ .id = .EnumTag },
- .doc_comments = null,
- .name_token = name_token,
- .value = default_value,
- };
- return &node.base;
- },
- else => unreachable,
- }
-}
-
-// Statement
-// <- KEYWORD_comptime? VarDecl
-// / KEYWORD_comptime BlockExprStatement
-// / KEYWORD_suspend (SEMICOLON / BlockExprStatement)
-// / KEYWORD_defer BlockExprStatement
-// / KEYWORD_errdefer BlockExprStatement
-// / IfStatement
-// / LabeledStatement
-// / SwitchExpr
-// / AssignExpr SEMICOLON
-fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const comptime_token = eatToken(it, .Keyword_comptime);
-
- const var_decl_node = try parseVarDecl(arena, it, tree);
- if (var_decl_node) |node| {
- const var_decl = node.cast(Node.VarDecl).?;
- var_decl.comptime_token = comptime_token;
- return node;
- }
-
- if (comptime_token) |token| {
- const block_expr = (try expectNode(arena, it, tree, parseBlockExprStatement, Error{
- .ExpectedBlockOrAssignment = Error.ExpectedBlockOrAssignment{ .token = it.peek().?.start },
- })) orelse return null;
-
- const node = try arena.create(Node.Comptime);
- node.* = Node.Comptime{
- .base = Node{ .id = .Comptime },
- .doc_comments = null,
- .comptime_token = token,
- .expr = block_expr,
- };
- return &node.base;
- }
-
- if (eatToken(it, .Keyword_suspend)) |suspend_token| {
- const semicolon = eatToken(it, .Semicolon);
-
- const body_node = if (semicolon == null) blk: {
- break :blk (try expectNode(arena, it, tree, parseBlockExprStatement, Error{
- // TODO: expected block or expression
- .ExpectedBlockOrAssignment = Error.ExpectedBlockOrAssignment{ .token = it.peek().?.start },
- })) orelse return null;
- } else null;
-
- const node = try arena.create(Node.Suspend);
- node.* = Node.Suspend{
- .base = Node{ .id = .Suspend },
- .suspend_token = suspend_token,
- .body = body_node,
- };
- return &node.base;
- }
-
- const defer_token = eatToken(it, .Keyword_defer) orelse eatToken(it, .Keyword_errdefer);
- if (defer_token) |token| {
- const expr_node = (try expectNode(arena, it, tree, parseBlockExprStatement, Error{
- // TODO: expected block or expression
- .ExpectedBlockOrAssignment = Error.ExpectedBlockOrAssignment{ .token = it.peek().?.start },
- })) orelse return null;
- const node = try arena.create(Node.Defer);
- node.* = Node.Defer{
- .base = Node{ .id = .Defer },
- .defer_token = token,
- .expr = expr_node,
- };
- return &node.base;
- }
-
- if (try parseIfStatement(arena, it, tree)) |node| return node;
- if (try parseLabeledStatement(arena, it, tree)) |node| return node;
- if (try parseSwitchExpr(arena, it, tree)) |node| return node;
- if (try parseAssignExpr(arena, it, tree)) |node| {
- _ = (try expectToken(it, tree, .Semicolon)) orelse return null;
- return node;
- }
-
- return null;
-}
-
-// IfStatement
-// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
-// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
-fn parseIfStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const if_node = if (try parseIfPrefix(arena, it, tree)) |node| node.cast(Node.If).? else return null;
- const block_expr = (try parseBlockExpr(arena, it, tree));
- const assign_expr = if (block_expr == null) blk: {
- break :blk (try parseAssignExpr(arena, it, tree)) orelse null;
- } else null;
- const semicolon = if (assign_expr != null) eatToken(it, .Semicolon) else null;
-
- const else_node = if (semicolon != null) blk: {
- const else_token = eatToken(it, .Keyword_else) orelse break :blk null;
- const payload = try parsePayload(arena, it, tree);
- const else_body = (try expectNode(arena, it, tree, parseStatement, Error{
- .InvalidToken = Error.InvalidToken{ .token = it.peek().?.start },
- })) orelse return null;
-
- const node = try arena.create(Node.Else);
- node.* = Node.Else{
- .base = Node{ .id = .Else },
- .else_token = else_token,
- .payload = payload,
- .body = else_body,
- };
-
- break :blk node;
- } else null;
-
- if (block_expr) |body| {
- if_node.body = body;
- if_node.@"else" = else_node;
- return &if_node.base;
- }
-
- if (assign_expr) |body| {
- if_node.body = body;
- if (semicolon != null) return &if_node.base;
- if (else_node != null) {
- if_node.@"else" = else_node;
- return &if_node.base;
- }
- try tree.errors.push(Error{
- .ExpectedSemiOrElse = Error.ExpectedSemiOrElse{ .token = it.peek().?.start },
- });
- }
-
- return null;
-}
-
-// LabeledStatement <- BlockLabel? (Block / LoopStatement)
-fn parseLabeledStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) anyerror!?*Node {
- const label_token = parseBlockLabel(arena, it, tree);
-
- if (try parseBlock(arena, it, tree)) |node| {
- node.cast(Node.Block).?.label = label_token;
- return node;
- }
-
- if (try parseLoopStatement(arena, it, tree)) |node| {
- if (node.cast(Node.For)) |for_node| {
- for_node.label = label_token;
- } else if (node.cast(Node.While)) |while_node| {
- while_node.label = label_token;
- } else unreachable;
- return node;
- }
-
- return null;
-}
-
-// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
-fn parseLoopStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const inline_token = eatToken(it, .Keyword_inline);
-
- if (try parseForStatement(arena, it, tree)) |node| {
- node.cast(Node.For).?.inline_token = inline_token;
- return node;
- }
-
- if (try parseWhileStatement(arena, it, tree)) |node| {
- node.cast(Node.While).?.inline_token = inline_token;
- return node;
- }
-
- return null;
-}
-
-// ForStatement
-// <- ForPrefix BlockExpr ( KEYWORD_else Statement )?
-// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )
-fn parseForStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const node = (try parseForPrefix(arena, it, tree)) orelse return null;
- const for_prefix = node.cast(Node.For).?;
-
- if (try parseBlockExpr(arena, it, tree)) |block_expr_node| {
- for_prefix.body = block_expr_node;
-
- if (eatToken(it, .Keyword_else)) |else_token| {
- const statement_node = (try expectNode(arena, it, tree, parseStatement, Error{
- .InvalidToken = Error.InvalidToken{ .token = it.peek().?.start },
- })) orelse return null;
-
- const else_node = try arena.create(Node.Else);
- else_node.* = Node.Else{
- .base = Node{ .id = .Else },
- .else_token = else_token,
- .payload = null,
- .body = statement_node,
- };
- for_prefix.@"else" = else_node;
-
- return node;
- }
-
- return node;
- }
-
- if (try parseAssignExpr(arena, it, tree)) |assign_expr| {
- for_prefix.body = assign_expr;
-
- if (eatToken(it, .Semicolon) != null) return node;
-
- if (eatToken(it, .Keyword_else)) |else_token| {
- const statement_node = (try expectNode(arena, it, tree, parseStatement, Error{
- .ExpectedStatement = Error.ExpectedStatement{ .token = it.peek().?.start },
- })) orelse return null;
-
- const else_node = try arena.create(Node.Else);
- else_node.* = Node.Else{
- .base = Node{ .id = .Else },
- .else_token = else_token,
- .payload = null,
- .body = statement_node,
- };
- for_prefix.@"else" = else_node;
- return node;
- }
-
- try tree.errors.push(Error{
- .ExpectedSemiOrElse = Error.ExpectedSemiOrElse{ .token = it.peek().?.start },
- });
- return null;
- }
-
- return null;
-}
-
-// WhileStatement
-// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
-// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
-fn parseWhileStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const node = (try parseWhilePrefix(arena, it, tree)) orelse return null;
- const while_prefix = node.cast(Node.While).?;
-
- if (try parseBlockExpr(arena, it, tree)) |block_expr_node| {
- while_prefix.body = block_expr_node;
-
- if (eatToken(it, .Keyword_else)) |else_token| {
- const payload = try parsePayload(arena, it, tree);
-
- const statement_node = (try expectNode(arena, it, tree, parseStatement, Error{
- .InvalidToken = Error.InvalidToken{ .token = it.peek().?.start },
- })) orelse return null;
-
- const else_node = try arena.create(Node.Else);
- else_node.* = Node.Else{
- .base = Node{ .id = .Else },
- .else_token = else_token,
- .payload = payload,
- .body = statement_node,
- };
- while_prefix.@"else" = else_node;
-
- return node;
- }
-
- return node;
- }
-
- if (try parseAssignExpr(arena, it, tree)) |assign_expr_node| {
- while_prefix.body = assign_expr_node;
-
- if (eatToken(it, .Semicolon) != null) return node;
-
- if (eatToken(it, .Keyword_else)) |else_token| {
- const payload = try parsePayload(arena, it, tree);
-
- const statement_node = (try expectNode(arena, it, tree, parseStatement, Error{
- .ExpectedStatement = Error.ExpectedStatement{ .token = it.peek().?.start },
- })) orelse return null;
-
- const else_node = try arena.create(Node.Else);
- else_node.* = Node.Else{
- .base = Node{ .id = .Else },
- .else_token = else_token,
- .payload = payload,
- .body = statement_node,
- };
- while_prefix.@"else" = else_node;
- return node;
- }
-
- try tree.errors.push(Error{
- .ExpectedSemiOrElse = Error.ExpectedSemiOrElse{ .token = it.peek().?.start },
- });
- return null;
- }
-
- return null;
-}
-
-// BlockExprStatement
-// <- BlockExpr
-// / AssignExpr SEMICOLON
-fn parseBlockExprStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- if (try parseBlockExpr(arena, it, tree)) |node| return node;
- if (try parseAssignExpr(arena, it, tree)) |node| {
- _ = (try expectToken(it, tree, .Semicolon)) orelse return null;
- return node;
- }
- return null;
-}
-
-// BlockExpr <- BlockLabel? Block
-fn parseBlockExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) anyerror!?*Node {
- const label_token = parseBlockLabel(arena, it, tree) orelse return null;
- const block_node = (parseBlock(arena, it, tree) catch return error.TodoFixRecursion) orelse return null;
- block_node.cast(Node.Block).?.label = label_token;
- return block_node;
-}
-
-// AssignExpr <- Expr (AssignOp Expr)?
-fn parseAssignExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return parseBinOpExpr(arena, it, tree, parseAssignOp, parseExpr, .Once);
-}
-
-// Expr <- KEYWORD_try* BoolOrExpr
-fn parseExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return parsePrefixOpExpr(arena, it, tree, parseTry, parseBoolOrExpr);
-}
-
-// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
-fn parseBoolOrExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return parseBinOpExpr(
- arena,
- it,
- tree,
- SimpleBinOpParser(.Keyword_or, Node.InfixOp.Op.BoolOr).parse,
- parseBoolAndExpr,
- .Infinitely,
- );
-}
-
-// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)*
-fn parseBoolAndExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return parseBinOpExpr(
- arena,
- it,
- tree,
- SimpleBinOpParser(.Keyword_and, Node.InfixOp.Op.BoolAnd).parse,
- parseCompareExpr,
- .Infinitely,
- );
-}
-
-// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?
-fn parseCompareExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return parseBinOpExpr(arena, it, tree, parseCompareOp, parseBitwiseExpr, .Once);
- // TODO: stage1 supplies BinOpChainInf, not Once, but grammar uses `?`
-}
-
-// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*
-fn parseBitwiseExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return parseBinOpExpr(arena, it, tree, parseBitwiseOp, parseBitShiftExpr, .Infinitely);
-}
-
-// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)*
-fn parseBitShiftExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return parseBinOpExpr(arena, it, tree, parseBitShiftOp, parseAdditionExpr, .Infinitely);
-}
-
-// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)*
-fn parseAdditionExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return parseBinOpExpr(arena, it, tree, parseAdditionOp, parseMultiplyExpr, .Infinitely);
-}
-
-// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)*
-fn parseMultiplyExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return parseBinOpExpr(arena, it, tree, parseMultiplyOp, parsePrefixExpr, .Infinitely);
-}
-
-// PrefixExpr <- PrefixOp* PrimaryExpr
-fn parsePrefixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return parsePrefixOpExpr(arena, it, tree, parsePrefixOp, parsePrimaryExpr);
-}
-
-// PrimaryExpr
-// <- AsmExpr
-// / IfExpr
-// / KEYWORD_break BreakLabel? Expr?
-// / KEYWORD_cancel Expr
-// / KEYWORD_comptime Expr
-// / KEYWORD_continue BreakLabel?
-// / KEYWORD_resume Expr
-// / KEYWORD_return Expr?
-// / BlockLabel? LoopExpr
-// / Block
-// / CurlySuffixExpr
-fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- // TODO: enum literal not represented in grammar: https://github.com/ziglang/zig/issues/2235
- if (try parseEnumLiteral(arena, it, tree)) |node| return node;
- if (try parseAsmExpr(arena, it, tree)) |node| return node;
- if (try parseIfExpr(arena, it, tree)) |node| return node;
-
- if (eatToken(it, .Keyword_break)) |token| {
- const label = parseBreakLabel(arena, it, tree);
- const expr_node = try parseExpr(arena, it, tree);
- const node = try arena.create(Node.ControlFlowExpression);
- node.* = Node.ControlFlowExpression{
- .base = Node{ .id = .ControlFlowExpression },
- .ltoken = token,
- .kind = Node.ControlFlowExpression.Kind{ .Break = null }, // TODO: what goes here?
- .rhs = expr_node,
- };
- return &node.base;
- }
-
- if (eatToken(it, .Keyword_cancel)) |token| {
- const expr_node = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- const node = try arena.create(Node.PrefixOp);
- node.* = Node.PrefixOp{
- .base = Node{ .id = .PrefixOp },
- .op_token = token,
- .op = Node.PrefixOp.Op.Cancel,
- .rhs = expr_node,
- };
- return &node.base;
- }
-
- if (eatToken(it, .Keyword_comptime)) |token| {
- const expr_node = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- const node = try arena.create(Node.Comptime);
- node.* = Node.Comptime{
- .base = Node{ .id = .Comptime },
- .doc_comments = null,
- .comptime_token = token,
- .expr = expr_node,
- };
- return &node.base;
- }
-
- if (eatToken(it, .Keyword_continue)) |token| {
- const label = parseBreakLabel(arena, it, tree);
- const node = try arena.create(Node.ControlFlowExpression);
- node.* = Node.ControlFlowExpression{
- .base = Node{ .id = .ControlFlowExpression },
- .ltoken = token,
- .kind = Node.ControlFlowExpression.Kind{ .Continue = null }, // TODO: what goes here?
- .rhs = null,
- };
- return &node.base;
- }
-
- if (eatToken(it, .Keyword_resume)) |token| {
- const expr_node = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- const node = try arena.create(Node.PrefixOp);
- node.* = Node.PrefixOp{
- .base = Node{ .id = .PrefixOp },
- .op_token = token,
- .op = Node.PrefixOp.Op.Resume,
- .rhs = expr_node,
- };
- return &node.base;
- }
-
- if (eatToken(it, .Keyword_return)) |token| {
- const expr_node = try parseExpr(arena, it, tree);
- const node = try arena.create(Node.ControlFlowExpression);
- node.* = Node.ControlFlowExpression{
- .base = Node{ .id = .ControlFlowExpression },
- .ltoken = token,
- .kind = Node.ControlFlowExpression.Kind.Return,
- .rhs = expr_node,
- };
- return &node.base;
- }
-
- const label = parseBlockLabel(arena, it, tree);
- if (try parseLoopExpr(arena, it, tree)) |node| {
- if (node.cast(Node.For)) |for_node| {
- for_node.label = label;
- } else if (node.cast(Node.While)) |while_node| {
- while_node.label = label;
- } else unreachable;
- return node;
- }
-
- if (try parseBlock(arena, it, tree)) |node| return node;
- if (try parseCurlySuffixExpr(arena, it, tree)) |node| return node;
-
- return null;
-}
-
-// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)?
-fn parseIfExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const if_node = (try parseIfPrefix(arena, it, tree)) orelse return null;
- const expr_node = (try parseExpr(arena, it, tree)) orelse return null;
-
- const else_node = if (eatToken(it, .Keyword_else)) |else_token| blk: {
- const payload = try parsePayload(arena, it, tree);
- const else_expr = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
-
- const node = try arena.create(Node.Else);
- node.* = Node.Else{
- .base = Node{ .id = .Else },
- .else_token = else_token,
- .payload = payload,
- .body = else_expr,
- };
-
- break :blk node;
- } else null;
-
- const node = if_node.cast(Node.If).?;
- node.*.body = expr_node;
- node.*.@"else" = else_node;
-
- return &node.base;
-}
-
-// Block <- LBRACE Statement* RBRACE
-fn parseBlock(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const lbrace = eatToken(it, .LBrace) orelse return null;
-
- var statements = Node.Block.StatementList.init(arena);
- while (true) {
- const statement = (try parseStatement(arena, it, tree)) orelse break;
- try statements.push(statement);
- }
-
- const rbrace = (try expectToken(it, tree, .RBrace)) orelse return null;
-
- const block_node = try arena.create(Node.Block);
- block_node.* = Node.Block{
- .base = Node{ .id = .Block },
- .label = null, // set by caller
- .lbrace = lbrace,
- .statements = statements,
- .rbrace = rbrace,
- };
-
- return &block_node.base;
-}
-
-// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)
-fn parseLoopExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const inline_token = eatToken(it, .Keyword_inline);
-
- if (try parseForExpr(arena, it, tree)) |node| {
- node.cast(Node.For).?.inline_token = inline_token;
- return node;
- }
-
- if (try parseWhileExpr(arena, it, tree)) |node| {
- node.cast(Node.While).?.inline_token = inline_token;
- return node;
- }
-
- // TODO: error?
-
- return null;
-}
-
-// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
-fn parseForExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const node = (try parseForPrefix(arena, it, tree)) orelse return null;
-
- const body_node = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- node.cast(Node.For).?.body = body_node;
-
- if (eatToken(it, .Keyword_else)) |else_token| {
- const body = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
-
- const else_node = try arena.create(Node.Else);
- else_node.* = Node.Else{
- .base = Node{ .id = .Else },
- .else_token = else_token,
- .payload = null,
- .body = body,
- };
-
- node.cast(Node.For).?.@"else" = else_node;
- }
-
- return node;
-}
-
-// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
-fn parseWhileExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const node = (try parseWhilePrefix(arena, it, tree)) orelse return null;
-
- const body_node = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- node.cast(Node.For).?.body = body_node;
-
- if (eatToken(it, .Keyword_else)) |else_token| {
- const payload = try parsePayload(arena, it, tree);
- const body = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
-
- const else_node = try arena.create(Node.Else);
- else_node.* = Node.Else{
- .base = Node{ .id = .Else },
- .else_token = else_token,
- .payload = null,
- .body = body,
- };
-
- node.cast(Node.While).?.@"else" = else_node;
- }
-
- return node;
-}
-
-// CurlySuffixExpr <- TypeExpr InitList?
-fn parseCurlySuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const type_expr = (try parseTypeExpr(arena, it, tree)) orelse return null;
- const init_list = (try parseInitList(arena, it, tree)) orelse return type_expr;
- init_list.cast(Node.SuffixOp).?.lhs = type_expr;
- return init_list;
-}
-
-// InitList
-// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
-// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
-// / LBRACE RBRACE
-fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const lbrace = eatToken(it, .LBrace) orelse return null;
- var init_list = Node.SuffixOp.Op.InitList.init(arena);
- const node = try arena.create(Node.SuffixOp);
- node.* = Node.SuffixOp{
- .base = Node{ .id = .SuffixOp },
- .lhs = undefined, // set by caller
- .op = Node.SuffixOp.Op{ .StructInitializer = init_list },
- .rtoken = undefined, // set below
- };
-
- if (try parseFieldInit(arena, it, tree)) |field_init| {
- try init_list.push(field_init);
- while (eatToken(it, .Comma)) |_| {
- const next = (try parseFieldInit(arena, it, tree)) orelse break;
- try init_list.push(next);
- }
- } else if (try parseExpr(arena, it, tree)) |expr| {
- try init_list.push(expr);
- while (eatToken(it, .Comma)) |_| {
- const next = (try parseExpr(arena, it, tree)) orelse break;
- try init_list.push(next);
- }
- }
-
- node.rtoken = (try expectToken(it, tree, .RBrace)) orelse return null;
- return &node.base;
-}
-
-// TypeExpr <- PrefixTypeOp* ErrorUnionExpr
-fn parseTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return parsePrefixOpExpr(arena, it, tree, parsePrefixTypeOp, parseErrorUnionExpr);
-}
-
-// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
-fn parseErrorUnionExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const suffix_expr = (try parseSuffixExpr(arena, it, tree)) orelse return null;
-
- if (eatToken(it, .Bang)) |bang| {
- const type_expr = (try expectNode(arena, it, tree, parseTypeExpr, Error{
- .ExpectedTypeExpr = Error.ExpectedTypeExpr{ .token = it.peek().?.start },
- })) orelse return null;
- const op_node = try arena.create(Node.InfixOp);
- op_node.* = Node.InfixOp{
- .base = Node{ .id = .InfixOp },
- .op_token = bang,
- .lhs = suffix_expr,
- .op = Node.InfixOp.Op.ErrorUnion,
- .rhs = type_expr,
- };
- return &op_node.base;
- }
-
- return suffix_expr;
-}
-
-// SuffixExpr
-// <- AsyncPrefix PrimaryTypeExpr SuffixOp* FnCallArguments
-// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
-fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- if (try parseAsyncPrefix(arena, it, tree)) |async_node| {
- // TODO: Implement hack for parsing `async fn ...` in ast_parse_suffix_expr
- var child = (try expectNode(arena, it, tree, parsePrimaryTypeExpr, Error{
- // TODO: different error?
- .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr{ .token = it.peek().?.start },
- })) orelse return null;
-
- while (try parseSuffixOp(arena, it, tree)) |suffix| {
- // TODO: all of this, maybe
- switch (suffix.cast(Node.SuffixOp).?.op) {
- .Call => |op| {},
- .ArrayAccess => |op| {},
- .Slice => |op| {},
- .ArrayInitializer => |op| {},
- .StructInitializer => |op| {},
- .Deref => |op| {},
- .UnwrapOptional => |op| {},
- }
- child = suffix;
- }
-
- const params = (try parseFnCallArguments(arena, it, tree)) orelse {
- try tree.errors.push(Error{
- .ExpectedParamList = Error.ExpectedParamList{ .token = it.peek().?.start },
- });
- return null;
- };
-
- const node = try arena.create(Node.SuffixOp);
- node.* = Node.SuffixOp{
- .base = Node{ .id = .SuffixOp },
- .lhs = child,
- .op = Node.SuffixOp.Op{
- .Call = Node.SuffixOp.Op.Call{
- .params = params,
- .async_attr = async_node.cast(Node.AsyncAttribute).?,
- },
- },
- .rtoken = undefined, // TODO TokenIndex ehhhhhh????
- };
- return &node.base;
- }
-
- if (try parsePrimaryTypeExpr(arena, it, tree)) |expr| {
- var res = expr;
-
- while (true) {
- if (try parseSuffixOp(arena, it, tree)) |suffix| {
- suffix.cast(Node.SuffixOp).?.lhs = res;
- res = suffix;
- continue;
- } else if (try parseFnCallArguments(arena, it, tree)) |params| {
- const call = try arena.create(Node.SuffixOp);
- call.* = Node.SuffixOp{
- .base = Node{ .id = .SuffixOp },
- .lhs = res,
- .op = Node.SuffixOp.Op{
- .Call = Node.SuffixOp.Op.Call{
- .params = params,
- .async_attr = null,
- },
- },
- .rtoken = undefined, // TODO: TokenIndex HMMMMM.
- };
- res = &call.base;
- continue;
- }
- break;
- }
- // TODO
- return res;
- }
-
- return null;
-}
-
-// PrimaryTypeExpr
-// <- BUILTINIDENTIFIER FnCallArguments
-// / CHAR_LITERAL
-// / ContainerDecl
-// / ErrorSetDecl
-// / FLOAT
-// / FnProto
-// / GroupedExpr
-// / LabeledTypeExpr
-// / IDENTIFIER
-// / IfTypeExpr
-// / INTEGER
-// / KEYWORD_anyerror
-// / KEYWORD_comptime TypeExpr
-// / KEYWORD_error DOT IDENTIFIER
-// / KEYWORD_false
-// / KEYWORD_null
-// / KEYWORD_promise
-// / KEYWORD_true
-// / KEYWORD_undefined
-// / KEYWORD_unreachable
-// / STRINGLITERAL
-// / SwitchExpr
-fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- // TODO: @[a-zA-Z_][a-zA-Z0-9]* (builtin identifier)
- if (eatToken(it, .CharLiteral)) |token| {
- const node = try arena.create(Node.CharLiteral);
- node.* = Node.CharLiteral{
- .base = Node{ .id = .CharLiteral },
- .token = token,
- };
- return &node.base;
- }
-
- if (try parseContainerDecl(arena, it, tree)) |node| return node;
- if (try parseErrorSetDecl(arena, it, tree)) |node| return node;
- if (try parseFloatLiteral(arena, it, tree)) |node| return node;
- if (try parseFnProto(arena, it, tree)) |node| return node;
- if (try parseGroupedExpr(arena, it, tree)) |node| return node;
- if (try parseLabeledTypeExpr(arena, it, tree)) |node| return node;
- if (try parseIdentifier(arena, it, tree)) |node| return node;
- if (try parseIfTypeExpr(arena, it, tree)) |node| return node;
- if (try parseIntegerLiteral(arena, it, tree)) |node| return node;
- if (eatToken(it, .Keyword_anyerror)) |token| return createLiteral(arena, Node.ErrorType, token);
- if (eatToken(it, .Keyword_comptime)) |token| {
- const expr = (try parseTypeExpr(arena, it, tree)) orelse return null;
- const node = try arena.create(Node.Comptime);
- node.* = Node.Comptime{
- .base = Node{ .id = .Comptime },
- .doc_comments = null,
- .comptime_token = token,
- .expr = expr,
- };
- return &node.base;
- }
- if (eatToken(it, .Keyword_error)) |token| {
- const period = (try expectToken(it, tree, .Period)) orelse return null;
- const identifier = (try expectNode(arena, it, tree, parseIdentifier, Error{
- .ExpectedIdentifier = Error.ExpectedIdentifier{ .token = it.peek().?.start },
- })) orelse return null;
- const global_error_set = try createLiteral(arena, Node.ErrorType, token);
- const node = try arena.create(Node.InfixOp);
- node.* = Node.InfixOp{
- .base = Node{ .id = .InfixOp },
- .op_token = period,
- .lhs = global_error_set,
- .op = Node.InfixOp.Op.Period,
- .rhs = identifier,
- };
- return &node.base;
- }
- if (eatToken(it, .Keyword_false)) |token| return createLiteral(arena, Node.BoolLiteral, token);
- if (eatToken(it, .Keyword_null)) |token| return createLiteral(arena, Node.NullLiteral, token);
- if (eatToken(it, .Keyword_promise)) |token| {
- const node = try arena.create(Node.PromiseType);
- node.* = Node.PromiseType{
- .base = Node{ .id = .PromiseType },
- .promise_token = token,
- .result = null,
- };
- return &node.base;
- }
- if (eatToken(it, .Keyword_true)) |token| return createLiteral(arena, Node.BoolLiteral, token);
- if (eatToken(it, .Keyword_undefined)) |token| return createLiteral(arena, Node.UndefinedLiteral, token);
- if (eatToken(it, .Keyword_unreachable)) |token| return createLiteral(arena, Node.Unreachable, token);
- if (try parseStringLiteral(arena, it, tree)) |node| return node;
- if (try parseSwitchExpr(arena, it, tree)) |node| return node;
-
- return null;
-}
-
-// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
-fn parseContainerDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const layout_token = eatToken(it, .Keyword_extern) orelse
- eatToken(it, .Keyword_packed);
-
- const node = (try parseContainerDeclAuto(arena, it, tree)) orelse return null;
- node.cast(Node.ContainerDecl).?.*.layout_token = layout_token;
- return node;
-}
-
-// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE
-fn parseErrorSetDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const error_token = eatToken(it, .Keyword_error) orelse return null;
- _ = (try expectToken(it, tree, .LBrace)) orelse return null;
- const decls = try parseIdentifierList(arena, it, tree);
- const rbrace = (try expectToken(it, tree, .RBrace)) orelse return null;
-
- const node = try arena.create(Node.ErrorSetDecl);
- node.* = Node.ErrorSetDecl{
- .base = Node{ .id = .ErrorSetDecl },
- .error_token = error_token,
- .decls = decls,
- .rbrace_token = rbrace,
- };
- return &node.base;
-}
-
-// GroupedExpr <- LPAREN Expr RPAREN
-fn parseGroupedExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const lparen = eatToken(it, .LParen) orelse return null;
- const expr = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- const rparen = (try expectToken(it, tree, .RParen)) orelse return null;
-
- const node = try arena.create(Node.GroupedExpression);
- node.* = Node.GroupedExpression{
- .base = Node{ .id = .GroupedExpression },
- .lparen = lparen,
- .expr = expr,
- .rparen = rparen,
- };
- return &node.base;
-}
-
-// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
-fn parseIfTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const node = (try parseIfPrefix(arena, it, tree)) orelse return null;
- const type_expr = (try expectNode(arena, it, tree, parseTypeExpr, Error{
- .ExpectedTypeExpr = Error.ExpectedTypeExpr{ .token = it.peek().?.start },
- })) orelse return null;
-
- const if_prefix = node.cast(Node.If).?;
- if_prefix.body = type_expr;
-
- if (eatToken(it, .Keyword_else)) |else_token| {
- const payload = (try parsePayload(arena, it, tree)) orelse return null;
- const else_body = (try expectNode(arena, it, tree, parseTypeExpr, Error{
- .ExpectedTypeExpr = Error.ExpectedTypeExpr{ .token = it.peek().?.start },
- })) orelse return null;
-
- const else_node = try arena.create(Node.Else);
- else_node.* = Node.Else{
- .base = Node{ .id = .Else },
- .else_token = else_token,
- .payload = payload,
- .body = else_body,
- };
- if_prefix.@"else" = else_node;
- }
-
- return node;
-}
-
-// LabeledTypeExpr
-// <- BlockLabel Block
-// / BlockLabel? LoopTypeExpr
-fn parseLabeledTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const label = parseBlockLabel(arena, it, tree);
-
- if (label) |token| {
- if (try parseBlock(arena, it, tree)) |node| {
- node.cast(Node.Block).?.label = token;
- return node;
- }
- }
-
- const node = (try parseLoopTypeExpr(arena, it, tree)) orelse return null;
- switch (node.id) {
- .For => node.cast(Node.For).?.label = label,
- .While => node.cast(Node.While).?.label = label,
- else => unreachable,
- }
- return node;
-}
-
-// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
-fn parseLoopTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const inline_token = eatToken(it, .Keyword_inline);
-
- if (try parseForTypeExpr(arena, it, tree)) |node| {
- node.cast(Node.For).?.inline_token = inline_token;
- return node;
- }
-
- if (try parseWhileTypeExpr(arena, it, tree)) |node| {
- node.cast(Node.While).?.inline_token = inline_token;
- return node;
- }
-
- return null;
-}
-
-// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?
-fn parseForTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const node = (try parseForPrefix(arena, it, tree)) orelse return null;
- const for_prefix = node.cast(Node.For).?;
-
- const type_expr = (try expectNode(arena, it, tree, parseTypeExpr, Error{
- .ExpectedTypeExpr = Error.ExpectedTypeExpr{ .token = it.peek().?.start },
- })) orelse return null;
- for_prefix.body = type_expr;
-
- if (eatToken(it, .Keyword_else)) |else_token| {
- const else_expr = (try expectNode(arena, it, tree, parseTypeExpr, Error{
- .ExpectedTypeExpr = Error.ExpectedTypeExpr{ .token = it.peek().?.start },
- })) orelse return null;
-
- const else_node = try arena.create(Node.Else);
- else_node.* = Node.Else{
- .base = Node{ .id = .Else },
- .else_token = else_token,
- .payload = null,
- .body = else_expr,
- };
-
- for_prefix.@"else" = else_node;
- }
-
- return node;
-}
-
-// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
-fn parseWhileTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const node = (try parseWhilePrefix(arena, it, tree)) orelse return null;
- const while_prefix = node.cast(Node.While).?;
-
- const type_expr = (try expectNode(arena, it, tree, parseTypeExpr, Error{
- .ExpectedTypeExpr = Error.ExpectedTypeExpr{ .token = it.peek().?.start },
- })) orelse return null;
- while_prefix.body = type_expr;
-
- if (eatToken(it, .Keyword_else)) |else_token| {
- const payload = try parsePayload(arena, it, tree);
-
- const else_expr = (try expectNode(arena, it, tree, parseTypeExpr, Error{
- .ExpectedTypeExpr = Error.ExpectedTypeExpr{ .token = it.peek().?.start },
- })) orelse return null;
-
- const else_node = try arena.create(Node.Else);
- else_node.* = Node.Else{
- .base = Node{ .id = .Else },
- .else_token = else_token,
- .payload = null,
- .body = else_expr,
- };
-
- while_prefix.@"else" = else_node;
- }
-
- return node;
-}
-
-// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
-fn parseSwitchExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const switch_token = eatToken(it, .Keyword_switch) orelse return null;
- _ = (try expectToken(it, tree, .LParen)) orelse return null;
- const expr_node = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- _ = (try expectToken(it, tree, .RParen)) orelse return null;
- _ = (try expectToken(it, tree, .LBrace)) orelse return null;
- const cases = try parseSwitchProngList(arena, it, tree);
- const rbrace = (try expectToken(it, tree, .RBrace)) orelse return null;
-
- const node = try arena.create(Node.Switch);
- node.* = Node.Switch{
- .base = Node{ .id = .Switch },
- .switch_token = switch_token,
- .expr = expr_node,
- .cases = cases,
- .rbrace = rbrace,
- };
- return &node.base;
-}
-
-// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN
-fn parseAsmExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const asm_token = eatToken(it, .Keyword_asm) orelse return null;
- const volatile_token = eatToken(it, .Keyword_volatile);
- _ = (try expectToken(it, tree, .LParen)) orelse return null;
- const asm_output = try parseAsmOutput(arena, it, tree);
- const rparen = (try expectToken(it, tree, .RParen)) orelse return null;
-
- const node = try arena.create(Node.Asm);
- node.* = Node.Asm{
- .base = Node{ .id = .Asm },
- .asm_token = asm_token,
- .volatile_token = volatile_token,
- .template = undefined, //TODO
- .outputs = undefined, // asm_output, // TODO
- .inputs = undefined, // TODO
- .clobbers = undefined, // TODO
- .rparen = rparen,
- };
- return &node.base;
-}
-
-// TODO: enum literal not represented in grammar: https://github.com/ziglang/zig/issues/2235
-fn parseEnumLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const dot = eatToken(it, .Period) orelse return null;
- const name = (try expectToken(it, tree, .Identifier)) orelse return null;
-
- const node = try arena.create(Node.EnumLiteral);
- node.* = Node.EnumLiteral{
- .base = undefined, // TODO: ??
- .dot = dot,
- .name = name,
- };
- return &node.base;
-}
-
-// AsmOutput <- COLON AsmOutputList AsmInput?
-fn parseAsmOutput(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return error.NotImplemented; // TODO
-}
-
-// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
-fn parseAsmOutputItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return error.NotImplemented; // TODO
-}
-
-// AsmInput <- COLON AsmInputList AsmClobbers?
-fn parseAsmInput(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return error.NotImplemented; // TODO
-}
-
-// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
-fn parseAsmInputItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return error.NotImplemented; // TODO
-}
-
-// AsmClobbers <- COLON StringList
-// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL?
-fn parseAsmClobbers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?Node.Asm.ClobberList {
- _ = eatToken(it, .Colon) orelse return null;
- return try ListParser(Node.Asm.ClobberList, parseStringLiteral).parse(arena, it, tree);
-}
-
-// BreakLabel <- COLON IDENTIFIER
-fn parseBreakLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree) ?TokenIndex {
- _ = eatToken(it, .Colon) orelse return null;
- return eatToken(it, .Identifier);
-}
-
-// BlockLabel <- IDENTIFIER COLON
-fn parseBlockLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree) ?TokenIndex {
- const token = eatToken(it, .Identifier) orelse return null;
- _ = eatToken(it, .Colon) orelse return null;
- return token;
-}
-
-// FieldInit <- DOT IDENTIFIER EQUAL Expr
-fn parseFieldInit(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const period_token = eatToken(it, .Period) orelse return null;
- const name_token = (try expectToken(it, tree, .Identifier)) orelse return null;
- const eq_token = (try expectToken(it, tree, .Equal)) orelse return null;
- const expr_node = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
-
- const node = try arena.create(Node.FieldInitializer);
- node.* = Node.FieldInitializer{
- .base = Node{ .id = .FieldInitializer },
- .period_token = period_token,
- .name_token = name_token,
- .expr = expr_node,
- };
- return &node.base;
-}
-
-// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
-fn parseWhileContinueExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- _ = eatToken(it, .Colon) orelse return null;
- _ = (try expectToken(it, tree, .LParen)) orelse return null;
- const node = (try expectNode(arena, it, tree, parseAssignExpr, Error{
- .ExpectedExprOrAssignment = Error.ExpectedExprOrAssignment{ .token = it.peek().?.start },
- })) orelse return null;
- _ = (try expectToken(it, tree, .RParen)) orelse return null;
- return node;
-}
-
-// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN
-fn parseLinkSection(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- _ = eatToken(it, .Keyword_linksection) orelse return null;
- _ = (try expectToken(it, tree, .LParen)) orelse return null;
- const expr_node = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- _ = (try expectToken(it, tree, .RParen)) orelse return null;
- return expr_node;
-}
-
-// FnCC
-// <- KEYWORD_nakedcc
-// / KEYWORD_stdcallcc
-// / KEYWORD_extern
-// / KEYWORD_async (LARROW TypeExpr RARROW)?
-fn parseFnCC(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?FnCC {
- if (eatToken(it, .Keyword_nakedcc)) |token| return FnCC{ .CC = token };
- if (eatToken(it, .Keyword_stdcallcc)) |token| return FnCC{ .CC = token };
- if (eatToken(it, .Keyword_extern)) |token| return FnCC{ .Extern = token };
- if (eatToken(it, .Keyword_async)) |token| {
- const node = try arena.create(Node.AsyncAttribute);
- node.* = Node.AsyncAttribute{
- .base = Node{ .id = .AsyncAttribute },
- .async_token = token,
- .allocator_type = null,
- .rangle_bracket = null,
- };
- if (eatToken(it, .AngleBracketLeft)) |_| {
- const type_expr = (try expectNode(arena, it, tree, parseTypeExpr, Error{
- .ExpectedTypeExpr = Error.ExpectedTypeExpr{ .token = it.peek().?.start },
- })) orelse return null;
- const rarrow = (try expectToken(it, tree, .AngleBracketRight)) orelse return null;
- node.allocator_type = type_expr;
- node.rangle_bracket = rarrow;
- }
- return FnCC{ .Async = node };
- }
- return FnCC{ .None = {} };
-}
-
-const FnCC = union(enum) {
- CC: TokenIndex,
- Extern: TokenIndex,
- Async: *Node.AsyncAttribute,
- None,
-};
-
-// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
-fn parseParamDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const noalias_token = eatToken(it, .Keyword_noalias);
- const comptime_token = if (noalias_token == null) eatToken(it, .Keyword_comptime) else null;
- const name_token = blk: {
- const identifier = eatToken(it, .Identifier) orelse break :blk null;
- if (eatToken(it, .Colon) != null) break :blk identifier;
- _ = rewindTokenIterator(it); // ParamType may also be an identifier
- break :blk null;
- };
- const param_type = (try parseParamType(arena, it, tree)) orelse return null;
-
- switch (param_type) {
- .None => {
- if (name_token != null)
- try tree.errors.push(Error{
- .ExpectedParamType = Error.ExpectedParamType{ .token = it.peek().?.start },
- });
- return null;
- },
- else => {},
- }
-
- const param_decl = try arena.create(Node.ParamDecl);
- param_decl.* = Node.ParamDecl{
- .base = Node{ .id = .ParamDecl },
- .doc_comments = null,
- .comptime_token = comptime_token,
- .noalias_token = noalias_token,
- .name_token = name_token,
- .type_node = undefined, // TODO: ok that this remains undefined when ... is found?
- .var_args_token = null,
- };
- switch (param_type) {
- .VarType => |node| param_decl.type_node = node,
- .TypeExpr => |node| param_decl.type_node = node,
- .VarArgs => |token| param_decl.var_args_token = token,
- else => unreachable,
- }
- return ¶m_decl.base;
-}
-
-// ParamType
-// <- KEYWORD_var
-// / DOT3
-// / TypeExpr
-fn parseParamType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?ParamType {
- if (eatToken(it, .Keyword_var)) |token| {
- const node = try arena.create(Node.VarType);
- node.* = Node.VarType{
- .base = Node{ .id = .VarType },
- .token = token,
- };
- return ParamType{ .VarType = &node.base };
- }
- if (eatToken(it, .Ellipsis3)) |token| return ParamType{ .VarArgs = token };
- if (try parseTypeExpr(arena, it, tree)) |node| return ParamType{ .TypeExpr = node };
- return null;
-}
-
-const ParamType = union(enum) {
- VarType: *Node,
- VarArgs: TokenIndex,
- TypeExpr: *Node,
- None,
-};
-
-// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?
-fn parseIfPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const if_token = eatToken(it, .Keyword_if) orelse return null;
- _ = (try expectToken(it, tree, .LParen)) orelse return null;
- const condition = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- _ = (try expectToken(it, tree, .RParen)) orelse return null;
- const payload = try parsePtrPayload(arena, it, tree);
-
- const node = try arena.create(Node.If);
- node.* = Node.If{
- .base = Node{ .id = .If },
- .if_token = if_token,
- .condition = condition,
- .payload = payload,
- .body = undefined, // set by caller
- .@"else" = null,
- };
- return &node.base;
-}
-
-// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
-fn parseWhilePrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const while_token = eatToken(it, .Keyword_while) orelse return null;
-
- _ = (try expectToken(it, tree, .LParen)) orelse return null;
- const condition = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- _ = (try expectToken(it, tree, .RParen)) orelse return null;
-
- const payload = try parsePtrPayload(arena, it, tree);
- const continue_expr = try parseWhileContinueExpr(arena, it, tree);
-
- const node = try arena.create(Node.While);
- node.* = Node.While{
- .base = Node{ .id = .While },
- .label = null,
- .inline_token = null,
- .while_token = while_token,
- .condition = condition,
- .payload = payload,
- .continue_expr = continue_expr,
- .body = undefined, // set by caller
- .@"else" = null,
- };
- return &node.base;
-}
-
-// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
-fn parseForPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const for_token = eatToken(it, .Keyword_for) orelse return null;
-
- _ = (try expectToken(it, tree, .LParen)) orelse return null;
- const array_expr = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- _ = (try expectToken(it, tree, .RParen)) orelse return null;
-
- const payload = (try expectNode(arena, it, tree, parsePtrIndexPayload, Error{
- // TODO
- .InvalidToken = Error.InvalidToken{ .token = it.peek().?.start },
- })) orelse return null;
-
- const node = try arena.create(Node.For);
- node.* = Node.For{
- .base = Node{ .id = .For },
- .label = null,
- .inline_token = null,
- .for_token = for_token,
- .array_expr = array_expr,
- .payload = payload, // TODO: why is this field optional?
- .body = undefined, // set by caller
- .@"else" = null,
- };
- return &node.base;
-}
-
-// Payload <- PIPE IDENTIFIER PIPE
-fn parsePayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const lpipe = eatToken(it, .Pipe) orelse return null;
- const identifier = (try expectNode(arena, it, tree, parseIdentifier, Error{
- .ExpectedIdentifier = Error.ExpectedIdentifier{ .token = it.peek().?.start },
- })) orelse return null;
- const rpipe = (try expectToken(it, tree, .Pipe)) orelse return null;
-
- const node = try arena.create(Node.Payload);
- node.* = Node.Payload{
- .base = Node{ .id = .Payload },
- .lpipe = lpipe,
- .error_symbol = identifier,
- .rpipe = rpipe,
- };
- return &node.base;
-}
-
-// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE
-fn parsePtrPayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const lpipe = eatToken(it, .Pipe) orelse return null;
- const asterisk = eatToken(it, .Asterisk);
- const identifier = (try expectNode(arena, it, tree, parseIdentifier, Error{
- .ExpectedIdentifier = Error.ExpectedIdentifier{ .token = it.peek().?.start },
- })) orelse return null;
- const rpipe = (try expectToken(it, tree, .Pipe)) orelse return null;
-
- const node = try arena.create(Node.PointerPayload);
- node.* = Node.PointerPayload{
- .base = Node{ .id = .PointerPayload },
- .lpipe = lpipe,
- .ptr_token = asterisk,
- .value_symbol = identifier,
- .rpipe = rpipe,
- };
- return &node.base;
-}
-
-// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE
-fn parsePtrIndexPayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const lpipe = eatToken(it, .Pipe) orelse return null;
- const asterisk = eatToken(it, .Asterisk);
- const index = blk: {
- if (eatToken(it, .Asterisk) == null) break :blk null;
- break :blk (try expectNode(arena, it, tree, parseIdentifier, Error{
- .ExpectedIdentifier = Error.ExpectedIdentifier{ .token = it.peek().?.start },
- })) orelse return null;
- };
- const identifier = (try expectNode(arena, it, tree, parseIdentifier, Error{
- .ExpectedIdentifier = Error.ExpectedIdentifier{ .token = it.peek().?.start },
- })) orelse return null;
- const rpipe = (try expectToken(it, tree, .Pipe)) orelse return null;
-
- const node = try arena.create(Node.PointerIndexPayload);
- node.* = Node.PointerIndexPayload{
- .base = Node{ .id = .PointerIndexPayload },
- .lpipe = lpipe,
- .ptr_token = asterisk,
- .value_symbol = identifier,
- .index_symbol = index,
- .rpipe = rpipe,
- };
- return &node.base;
-}
-
-// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
-fn parseSwitchProng(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const node = (try parseSwitchCase(arena, it, tree)) orelse return null;
- const arrow = (try expectToken(it, tree, .EqualAngleBracketRight)) orelse return null;
- const payload = try parsePtrPayload(arena, it, tree);
- const expr = (try expectNode(arena, it, tree, parseAssignExpr, Error{
- .ExpectedExprOrAssignment = Error.ExpectedExprOrAssignment{ .token = it.peek().?.start },
- })) orelse return null;
-
- const switch_case = node.cast(Node.SwitchCase).?;
- switch_case.arrow_token = arrow;
- switch_case.payload = payload;
- switch_case.expr = expr;
-
- return node;
-}
-
-// SwitchCase
-// <- SwitchItem (COMMA SwitchItem)* COMMA?
-// / KEYWORD_else
-fn parseSwitchCase(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- var list = Node.SwitchCase.ItemList.init(arena);
-
- if (try parseSwitchItem(arena, it, tree)) |first_item| {
- try list.push(first_item);
- while (eatToken(it, .Comma) != null) {
- const next_item = (try parseSwitchItem(arena, it, tree)) orelse break;
- try list.push(next_item);
- }
- } else if (eatToken(it, .Keyword_else)) |else_token| {
- const else_node = try arena.create(Node.SwitchElse);
- else_node.* = Node.SwitchElse{
- .base = Node{ .id = .SwitchElse },
- .token = else_token,
- };
- try list.push(&else_node.base);
- } else return null;
-
- const node = try arena.create(Node.SwitchCase);
- node.* = Node.SwitchCase{
- .base = Node{ .id = .SwitchCase },
- .items = list,
- .arrow_token = undefined, // set by caller
- .payload = null,
- .expr = undefined, // set by caller
- };
- return &node.base;
-}
-
-// SwitchItem <- Expr (DOT3 Expr)?
-fn parseSwitchItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const expr = (try parseExpr(arena, it, tree)) orelse return null;
- if (eatToken(it, .Ellipsis3)) |token| {
- const range_end = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
-
- const node = try arena.create(Node.InfixOp);
- node.* = Node.InfixOp{
- .base = Node{ .id = .InfixOp },
- .op_token = token,
- .lhs = expr,
- .op = Node.InfixOp.Op{ .Range = {} },
- .rhs = range_end,
- };
- return &node.base;
- }
- return expr;
-}
-
-// AssignOp
-// <- ASTERISKEQUAL
-// / SLASHEQUAL
-// / PERCENTEQUAL
-// / PLUSEQUAL
-// / MINUSEQUAL
-// / LARROW2EQUAL
-// / RARROW2EQUAL
-// / AMPERSANDEQUAL
-// / CARETEQUAL
-// / PIPEEQUAL
-// / ASTERISKPERCENTEQUAL
-// / PLUSPERCENTEQUAL
-// / MINUSPERCENTEQUAL
-// / EQUAL
-fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return error.NotImplemented; // TODO
-}
-
-// CompareOp
-// <- EQUALEQUAL
-// / EXCLAMATIONMARKEQUAL
-// / LARROW
-// / RARROW
-// / LARROWEQUAL
-// / RARROWEQUAL
-fn parseCompareOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const ops = Node.InfixOp.Op;
-
- const token = nextNonCommentToken(it);
- const op = switch (token.ptr.id) {
- .EqualEqual => ops{ .EqualEqual = {} },
- .BangEqual => ops{ .BangEqual = {} },
- .AngleBracketLeft => ops{ .LessThan = {} },
- .AngleBracketRight => ops{ .GreaterThan = {} },
- .AngleBracketLeftEqual => ops{ .LessOrEqual = {} },
- .AngleBracketRightEqual => ops{ .GreaterOrEqual = {} },
- else => {
- _ = rewindTokenIterator(it);
- return null;
- },
- };
-
- return try createInfixOp(arena, it.index, op);
-}
-
-// BitwiseOp
-// <- AMPERSAND
-// / CARET
-// / PIPE
-// / KEYWORD_orelse
-// / KEYWORD_catch Payload?
-fn parseBitwiseOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const ops = Node.InfixOp.Op;
-
- const token = nextNonCommentToken(it);
- const op = switch (token.ptr.id) {
- .Ampersand => ops{ .BitAnd = {} },
- .Caret => ops{ .BitXor = {} },
- .Pipe => ops{ .BitOr = {} },
- .Keyword_orelse => ops{ .UnwrapOptional = {} },
- .Keyword_catch => ops{ .Catch = try parsePayload(arena, it, tree) },
- else => {
- _ = rewindTokenIterator(it);
- return null;
- },
- };
-
- return try createInfixOp(arena, it.index, op);
-}
-
-// BitShiftOp
-// <- LARROW2
-// / RARROW2
-fn parseBitShiftOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const ops = Node.InfixOp.Op;
-
- const token = nextNonCommentToken(it);
- const op = switch (token.ptr.id) {
- .AngleBracketAngleBracketLeft => ops{ .BitShiftLeft = {} },
- .AngleBracketAngleBracketRight => ops{ .BitShiftRight = {} },
- else => {
- _ = rewindTokenIterator(it);
- return null;
- },
- };
-
- return try createInfixOp(arena, it.index, op);
-}
-
-// AdditionOp
-// <- PLUS
-// / MINUS
-// / PLUS2
-// / PLUSPERCENT
-// / MINUSPERCENT
-fn parseAdditionOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const ops = Node.InfixOp.Op;
-
- const token = nextNonCommentToken(it);
- const op = switch (token.ptr.id) {
- .Plus => ops{ .Add = {} },
- .Minus => ops{ .Sub = {} },
- .PlusPlus => ops{ .ArrayCat = {} },
- .PlusPercent => ops{ .AddWrap = {} },
- .MinusPercent => ops{ .SubWrap = {} },
- else => {
- _ = rewindTokenIterator(it);
- return null;
- },
- };
-
- return try createInfixOp(arena, it.index, op);
-}
-
-// MultiplyOp
-// <- PIPE2
-// / ASTERISK
-// / SLASH
-// / PERCENT
-// / ASTERISK2
-// / ASTERISKPERCENT
-fn parseMultiplyOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const ops = Node.InfixOp.Op;
-
- const token = nextNonCommentToken(it);
- const op = switch (token.ptr.id) {
- .PipePipe => ops{ .BoolOr = {} },
- .Asterisk => ops{ .Mult = {} },
- .Slash => ops{ .Div = {} },
- .Percent => ops{ .Mod = {} },
- .AsteriskAsterisk => ops{ .ArrayMult = {} },
- .AsteriskPercent => ops{ .MultWrap = {} },
- else => {
- _ = rewindTokenIterator(it);
- return null;
- },
- };
-
- return try createInfixOp(arena, it.index, op);
-}
-
-// PrefixOp
-// <- EXCLAMATIONMARK
-// / MINUS
-// / TILDE
-// / MINUSPERCENT
-// / AMPERSAND
-// / KEYWORD_try
-// / KEYWORD_await
-fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const ops = Node.PrefixOp.Op;
-
- const token = nextNonCommentToken(it);
- const op = switch (token.ptr.id) {
- .Bang => ops{ .BoolNot = {} },
- .Minus => ops{ .Negation = {} },
- .Tilde => ops{ .BitNot = {} },
- .MinusPercent => ops{ .NegationWrap = {} },
- .Ampersand => ops{ .AddressOf = {} },
- .Keyword_try => ops{ .Try = {} },
- .Keyword_await => ops{ .Await = {} },
- else => {
- _ = rewindTokenIterator(it);
- return null;
- },
- };
-
- const node = try arena.create(Node.PrefixOp);
- node.* = Node.PrefixOp{
- .base = Node{ .id = .PrefixOp },
- .op_token = it.index,
- .op = op,
- .rhs = undefined,
- };
- return &node.base;
-}
-
-// TODO: last choice allows for `*const volatile volatile const`, `*align(4) align(8) align(4)` etc.
-// PrefixTypeOp
-// <- QUESTIONMARK
-// / KEYWORD_promise MINUSRARROW
-// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)*
-// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)*
-fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- if (eatToken(it, .QuestionMark)) |token| {
- const node = try arena.create(Node.PrefixOp);
- node.* = Node.PrefixOp{
- .base = Node{ .id = .PrefixOp },
- .op_token = token,
- .op = Node.PrefixOp.Op.OptionalType,
- .rhs = undefined, // set by caller
- };
- return &node.base;
- }
-
- if (eatToken(it, .Keyword_promise)) |token| {
- const arrow = (try expectToken(it, tree, .Arrow)) orelse return null;
- const node = try arena.create(Node.PromiseType);
- node.* = Node.PromiseType{
- .base = Node{ .id = .PromiseType },
- .promise_token = token,
- .result = null,
- };
- return &node.base;
- }
-
- if (try parseArrayTypeStart(arena, it, tree)) |node| {
- // TODO: Set node.rhs
- while (true) {
- if (try parseByteAlign(arena, it, tree)) |byte_align| {
- // TODO
- continue;
- }
-
- if (eatToken(it, .Keyword_const)) |const_token| {
- // TODO
- continue;
- }
-
- if (eatToken(it, .Keyword_volatile)) |volatile_token| {
- // TODO
- continue;
- }
-
- break;
- }
- // return null;
- return error.NotImplemented;
- }
-
- if (try parsePtrTypeStart(arena, it, tree)) |node| {
- while (true) {
- // TODO: allowzero
- if (eatToken(it, .Keyword_align)) |align_token| {
- const lparen = (try expectToken(it, tree, .LParen)) orelse return null;
- const expr_node = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
-
- // Optional bit range
- const bit_range = if (eatToken(it, .Colon)) |_| bit_range_value: {
- const range_start = (try expectNode(arena, it, tree, parseIntegerLiteral, Error{
- .ExpectedIntegerLiteral = Error.ExpectedIntegerLiteral{ .token = it.peek().?.start },
- })) orelse return null;
- _ = (try expectToken(it, tree, .Colon)) orelse return null;
- const range_end = (try expectNode(arena, it, tree, parseIntegerLiteral, Error{
- .ExpectedIntegerLiteral = Error.ExpectedIntegerLiteral{ .token = it.peek().?.start },
- })) orelse return null;
-
- break :bit_range_value Node.PrefixOp.PtrInfo.Align.BitRange{
- .start = range_start,
- .end = range_end,
- };
- } else null;
- _ = (try expectToken(it, tree, .RParen)) orelse return null;
-
- node.cast(Node.PrefixOp).?.op.PtrType.align_info = Node.PrefixOp.PtrInfo.Align{
- .node = expr_node,
- .bit_range = bit_range,
- };
-
- continue;
- } else if (eatToken(it, .Keyword_const)) |const_token| ptr_info_value: {
- node.cast(Node.PrefixOp).?.op.PtrType.const_token = const_token;
- continue;
- } else if (eatToken(it, .Keyword_volatile)) |volatile_token| {
- node.cast(Node.PrefixOp).?.op.PtrType.volatile_token = volatile_token;
- continue;
- }
- break;
- }
- }
-
- return null;
-}
-
-// SuffixOp
-// <- LBRACKET Expr (DOT2 Expr?)? RBRACKET
-// / DOT IDENTIFIER
-// / DOTASTERISK
-// / DOTQUESTIONMARK
-fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- if (eatToken(it, .LBracket)) |_| {
- const expr_node = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- const dots = eatToken(it, .Ellipsis2);
- const expr = if (dots) |_| try parseExpr(arena, it, tree) else null;
- _ = (try expectToken(it, tree, .RBracket)) orelse return null;
- return error.NotImplemented; // TODO
- }
-
- if (eatToken(it, .Period)) |_| {
- const identifier = (try expectToken(it, tree, .Identifier)) orelse return null;
- return error.NotImplemented; // TODO
- }
-
- if (eatToken(it, .Period)) |period| {
- if (eatToken(it, .Asterisk)) |asterisk| {
- const node = try arena.create(Node.SuffixOp);
- node.* = Node.SuffixOp{
- .base = Node{ .id = .SuffixOp },
- .lhs = undefined, // TODO
- .op = Node.SuffixOp.Op.Deref,
- .rtoken = undefined, // TODO
- };
- return &node.base;
- }
- if (eatToken(it, .QuestionMark)) |question_mark| {
- const node = try arena.create(Node.SuffixOp);
- node.* = Node.SuffixOp{
- .base = Node{ .id = .SuffixOp },
- .lhs = undefined, // TODO
- .op = Node.SuffixOp.Op.UnwrapOptional,
- .rtoken = undefined, // TODO
- };
- return &node.base;
- }
- try tree.errors.push(Error{
- .ExpectedDerefOrUnwrap = Error.ExpectedDerefOrUnwrap{ .token = it.peek().?.start },
- });
- return null;
- }
-
- return null;
-}
-
-// AsyncPrefix <- KEYWORD_async (LARROW PrefixExpr RARROW)?
-fn parseAsyncPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const async_token = eatToken(it, .Keyword_async) orelse return null;
- var rangle_bracket: ?TokenIndex = null;
- const expr_node = if (eatToken(it, .AngleBracketLeft)) |_| blk: {
- const prefix_expr = (try expectNode(arena, it, tree, parsePrefixExpr, Error{
- .ExpectedPrefixExpr = Error.ExpectedPrefixExpr{ .token = it.peek().?.start },
- })) orelse return null;
- rangle_bracket = (try expectToken(it, tree, .AngleBracketRight)) orelse return null;
- break :blk prefix_expr;
- } else null;
-
- const node = try arena.create(Node.AsyncAttribute);
- node.* = Node.AsyncAttribute{
- .base = Node{ .id = .AsyncAttribute },
- .async_token = async_token,
- .allocator_type = expr_node,
- .rangle_bracket = rangle_bracket,
- };
- return &node.base;
-}
-
-// FnCallArguments <- LPAREN ExprList RPAREN
-// ExprList <- (Expr COMMA)* Expr?
-fn parseFnCallArguments(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?Node.SuffixOp.Op.Call.ParamList {
- if (eatToken(it, .LParen) == null) return null;
- const list = try ListParser(Node.SuffixOp.Op.Call.ParamList, parseExpr).parse(arena, it, tree);
- _ = (try expectToken(it, tree, .RParen)) orelse return null;
- return list;
-}
-
-// ArrayTypeStart <- LBRACKET Expr? RBRACKET
-fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const lbracket = eatToken(it, .LBracket) orelse return null;
- const expr = (try parseExpr(arena, it, tree)) orelse return null;
- const rbracket = (try expectToken(it, tree, .RBracket)) orelse return null;
-
- const node = try arena.create(Node.PrefixOp);
- node.* = Node.PrefixOp{
- .base = Node{ .id = .PrefixOp },
- .op_token = lbracket,
- .op = Node.PrefixOp.Op{ .ArrayType = expr },
- .rhs = undefined, // set by caller
- };
- return &node.base;
-}
-
-// PtrTypeStart
-// <- ASTERISK
-// / ASTERISK2
-// / PTRUNKNOWN
-// / PTRC
-fn parsePtrTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const token = eatToken(it, .Asterisk) orelse
- eatToken(it, .AsteriskAsterisk) orelse
- eatToken(it, .BracketStarBracket) orelse
- eatToken(it, .BracketStarCBracket) orelse
- null;
-
- if (token) |op_token| {
- const node = try arena.create(Node.PrefixOp);
- node.* = Node.PrefixOp{
- .base = Node{ .id = .PrefixOp },
- .op_token = op_token,
- .op = Node.PrefixOp.Op{
- .PtrType = Node.PrefixOp.PtrInfo{
- .allowzero_token = null,
- .align_info = null,
- .const_token = null,
- .volatile_token = null,
- },
- },
- .rhs = undefined, // set by caller
- };
- return &node.base;
- } else return null;
- // TODO: zig fmt allows expression body of `if` on its own line, but forces the expression
- // body of an `else if` to be all on the same line
-}
-
-// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE
-fn parseContainerDeclAuto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const node = (try parseContainerDeclType(arena, it, tree)) orelse return null;
- const lbrace = (try expectToken(it, tree, .LBrace)) orelse return null;
- const kind = it.list.at(node.cast(Node.ContainerDecl).?.kind_token).id;
- const members = (try parseContainerMembers(arena, it, tree, kind)) orelse return null;
- const rbrace = (try expectToken(it, tree, .RBrace)) orelse return null;
-
- const decl_type = node.cast(Node.ContainerDecl).?;
- decl_type.fields_and_decls = members;
- decl_type.lbrace_token = lbrace;
- decl_type.rbrace_token = rbrace;
-
- return node;
-}
-
-// ContainerDeclType
-// <- (KEYWORD_struct / KEYWORD_enum) (LPAREN Expr RPAREN)?
-// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
-fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const container_type = eatToken(it, .Keyword_struct) orelse eatToken(it, .Keyword_enum);
- if (container_type) |kind_token| {
- // TODO: https://github.com/ziglang/zig/issues/2330
- const init_arg_expr = if (eatToken(it, .LParen) != null) blk: {
- const expr = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- _ = (try expectToken(it, tree, .RParen)) orelse return null;
- break :blk Node.ContainerDecl.InitArg{ .Type = expr };
- } else Node.ContainerDecl.InitArg{ .None = {} };
-
- const node = try arena.create(Node.ContainerDecl);
- node.* = Node.ContainerDecl{
- .base = Node{ .id = .ContainerDecl },
- .layout_token = null,
- .kind_token = kind_token,
- .init_arg_expr = init_arg_expr,
- .fields_and_decls = undefined, // set by caller
- .lbrace_token = undefined, // set by caller
- .rbrace_token = undefined, // set by caller
- };
- return &node.base;
- }
-
- if (eatToken(it, .Keyword_union)) |kind_token| {
- const init_arg_expr = if (eatToken(it, .LParen) != null) set_init_arg: {
- if (eatToken(it, .Keyword_enum) != null) {
- const enum_expr = if (eatToken(it, .LParen) != null) set_enum_expr: {
- const expr = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- _ = (try expectToken(it, tree, .RParen)) orelse return null;
- break :set_enum_expr expr;
- } else null;
- break :set_init_arg Node.ContainerDecl.InitArg{ .Enum = enum_expr };
- }
-
- const expr = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- _ = (try expectToken(it, tree, .RParen)) orelse return null;
- break :set_init_arg Node.ContainerDecl.InitArg{ .Type = expr };
- } else Node.ContainerDecl.InitArg{ .None = {} };
-
- const node = try arena.create(Node.ContainerDecl);
- node.* = Node.ContainerDecl{
- .base = Node{ .id = .ContainerDecl },
- .layout_token = null,
- .kind_token = kind_token,
- .init_arg_expr = init_arg_expr,
- .fields_and_decls = undefined, // set by caller
- .lbrace_token = undefined, // set by caller
- .rbrace_token = undefined, // set by caller
- };
- return &node.base;
- }
-
- return null;
-}
-
-// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN
-fn parseByteAlign(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const align_token = eatToken(it, .Keyword_align) orelse return null;
- _ = (try expectToken(it, tree, .LParen)) orelse return null;
- const align_expr = (try expectNode(arena, it, tree, parseExpr, Error{
- .ExpectedExpr = Error.ExpectedExpr{ .token = it.peek().?.start },
- })) orelse return null;
- _ = (try expectToken(it, tree, .RParen)) orelse return null;
-
- const node = try arena.create(Node.PrefixOp);
- node.* = Node.PrefixOp{
- .base = Node{ .id = .PrefixOp },
- .op_token = align_token,
- .op = Node.PrefixOp.Op{
- .PtrType = Node.PrefixOp.PtrInfo{
- .allowzero_token = null,
- .align_info = Node.PrefixOp.PtrInfo.Align{
- .node = align_expr,
- .bit_range = null,
- },
- .const_token = null,
- .volatile_token = null,
- },
- },
- .rhs = undefined, // set by caller
- };
-
- return &node.base;
-}
-
-// IdentifierList <- (IDENTIFIER COMMA)* IDENTIFIER?
-fn parseIdentifierList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !Node.ErrorSetDecl.DeclList {
- // ErrorSetDecl.DeclList is used since ErrorSetDecl is the only caller of this function.
- return try ListParser(Node.ErrorSetDecl.DeclList, parseIdentifier).parse(arena, it, tree);
-}
-
-// SwitchProngList <- (SwitchProng COMMA)* SwitchProng?
-fn parseSwitchProngList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !Node.Switch.CaseList {
- return try ListParser(Node.Switch.CaseList, parseSwitchProng).parse(arena, it, tree);
-}
-
-// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem?
-fn parseAsmOutputList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return try ListParser(Node.Asm.OutputList, parseAsmOutputItem).parse(arena, it, tree);
-}
-
-// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem?
-fn parseAsmInputList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- return try ListParser(Node.Asm.InputList, parseAsmInputItem).parse(arena, it, tree);
-}
-
-// ParamDeclList <- (ParamDecl COMMA)* ParamDecl?
-fn parseParamDeclList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !Node.FnProto.ParamList {
- return try ListParser(Node.FnProto.ParamList, parseParamDecl).parse(arena, it, tree);
-}
-
-// TODO: don't use anyerror
-const ParseFn = fn (*Allocator, *TokenIterator, *Tree) anyerror!?*Node;
-
-// Helper parsers not included in the grammar
-
-fn parseIdentifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const token = eatToken(it, .Identifier) orelse return null;
- const node = try arena.create(Node.Identifier);
- node.* = Node.Identifier{
- .base = Node{ .id = .Identifier },
- .token = token,
- };
- return &node.base;
-}
-
-fn createLiteral(arena: *Allocator, comptime T: type, token: TokenIndex) !*Node {
- const result = try arena.create(T);
- result.* = T{
- .base = Node{ .id = Node.typeToId(T) },
- .token = token,
- };
- return &result.base;
-}
-
-// string literal or multiline string literal
-fn parseStringLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- if (eatToken(it, .StringLiteral)) |token| {
- const node = try arena.create(Node.StringLiteral);
- node.* = Node.StringLiteral{
- .base = Node{ .id = .StringLiteral },
- .token = token,
- };
- return &node.base;
- }
-
- if (eatToken(it, .MultilineStringLiteralLine)) |first_line| {
- const node = try arena.create(Node.MultilineStringLiteral);
- node.* = Node.MultilineStringLiteral{
- .base = Node{ .id = .MultilineStringLiteral },
- .lines = Node.MultilineStringLiteral.LineList.init(arena),
- };
- try node.lines.push(first_line);
- while (eatToken(it, .MultilineStringLiteralLine)) |line|
- try node.lines.push(line);
-
- return &node.base;
- }
-
- return null;
-}
-
-fn parseIntegerLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const token = eatToken(it, .IntegerLiteral) orelse return null;
- const node = try arena.create(Node.IntegerLiteral);
- node.* = Node.IntegerLiteral{
- .base = Node{ .id = .IntegerLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn parseFloatLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const token = eatToken(it, .FloatLiteral) orelse return null;
- const node = try arena.create(Node.FloatLiteral);
- node.* = Node.FloatLiteral{
- .base = Node{ .id = .FloatLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn parseTry(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const token = eatToken(it, .Keyword_try) orelse return null;
- const node = try arena.create(Node.PrefixOp);
- node.* = Node.PrefixOp{
- .base = Node{ .id = .PrefixOp },
- .op_token = token,
- .op = Node.PrefixOp.Op.Try,
- .rhs = undefined, // set by caller
- };
- return &node.base;
-}
-
-fn parseUse(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const token = eatToken(it, .Keyword_use) orelse return null;
- const node = try arena.create(Node.Use);
- node.* = Node.Use{
- .base = Node{ .id = .PrefixOp },
- .doc_comments = null,
- .visib_token = null,
- .use_token = token,
- .expr = undefined,
- .semicolon_token = undefined,
- };
- return &node.base;
-}
-
-// Op* Child
-fn parsePrefixOpExpr(
- arena: *Allocator,
- it: *TokenIterator,
- tree: *Tree,
- opParseFn: ParseFn,
- childParseFn: ParseFn,
-) !?*Node {
- if (try opParseFn(arena, it, tree)) |op| {
- const child = (try expectNode(
- arena,
- it,
- tree,
- childParseFn,
- Error{
- .InvalidToken = Error.InvalidToken{ .token = it.peek().?.start },
- },
- )) orelse return null;
- op.cast(Node.PrefixOp).?.rhs = child;
- return op;
- }
- return childParseFn(arena, it, tree);
-}
-
-// Child (Op Child)(*/?)
-fn parseBinOpExpr(
- arena: *Allocator,
- it: *TokenIterator,
- tree: *Tree,
- opParseFn: ParseFn,
- childParseFn: ParseFn,
- chain: BinOpChain,
-) !?*Node {
- var res = (try childParseFn(arena, it, tree)) orelse return null;
-
- while (try opParseFn(arena, it, tree)) |node| {
- const right = (try expectNode(arena, it, tree, childParseFn, Error{
- .InvalidToken = Error.InvalidToken{ .token = it.peek().?.start },
- })) orelse return null;
- const left = res;
- res = node;
-
- const op = node.cast(Node.InfixOp).?;
- op.*.lhs = left;
- op.*.rhs = right;
-
- switch (chain) {
- .Once => break,
- .Infinitely => continue,
- }
- }
-
- return res;
-}
-
-fn SimpleBinOpParser(token: Token.Id, op: Node.InfixOp.Op) type {
- return struct {
- pub fn parse(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const op_token = eatToken(it, token) orelse return null;
- const node = try arena.create(Node.InfixOp);
- node.* = Node.InfixOp{
- .base = Node{ .id = .InfixOp },
- .op_token = op_token,
- .lhs = undefined, // set by caller
- .op = op,
- .rhs = undefined, // set by caller
- };
- return &node.base;
- }
- };
-}
-
-const BinOpChain = enum {
- Once,
- Infinitely,
-};
-
-fn createInfixOp(arena: *Allocator, index: TokenIndex, op: Node.InfixOp.Op) !*Node {
- const node = try arena.create(Node.InfixOp);
- node.* = Node.InfixOp{
- .base = Node{ .id = .InfixOp },
- .op_token = index,
- .lhs = undefined,
- .op = op,
- .rhs = undefined,
- };
- return &node.base;
-}
-
-fn eatToken(it: *TokenIterator, id: Token.Id) ?TokenIndex {
- return if (it.peek().?.id == id) nextNonCommentToken(it).index else null;
-}
-
-fn expectToken(it: *TokenIterator, tree: *Tree, id: Token.Id) !?TokenIndex {
- const token = nextNonCommentToken(it);
- if (token.ptr.id != id) {
- try tree.errors.push(Error{
- .ExpectedToken = Error.ExpectedToken{ .token = token.index, .expected_id = id },
- });
- return null;
- }
- return token.index;
-}
-
-fn nextNonCommentToken(it: *TokenIterator) AnnotatedToken {
- const result = AnnotatedToken{
- .index = it.index,
- .ptr = it.next().?,
- };
- assert(result.ptr.id != .LineComment);
-
- while (true) {
- const next_tok = it.peek() orelse return result;
- if (next_tok.id != .LineComment) return result;
- _ = it.next();
- }
-}
-
-fn rewindTokenIterator(it: *TokenIterator) void {
- while (true) {
- const prev_tok = it.prev() orelse return;
- if (prev_tok.id == .LineComment) continue;
- return;
- }
-}
-
-const AnnotatedToken = struct {
- index: TokenIndex,
- ptr: *Token,
-};
-
-fn expectNode(
- arena: *Allocator,
- it: *TokenIterator,
- tree: *Tree,
- parseFn: ParseFn,
- err: Error, // if parsing fails
-) !?*Node {
- const node = try parseFn(arena, it, tree);
- if (node == null) try tree.errors.push(err);
- return node;
-}
-
-fn ListParser(comptime L: type, comptime nodeParseFn: var) type {
- return struct {
- pub fn parse(arena: *Allocator, it: *TokenIterator, tree: *Tree) !L {
- var list = L.init(arena);
- while (try nodeParseFn(arena, it, tree)) |node| {
- try list.push(node);
- if (eatToken(it, .Colon) == null) break;
- }
- return list;
- }
- };
-}
-
-test "std.zig.parser" {
- _ = @import("parser_test.zig");
-}
CMakeLists.txt
@@ -723,7 +723,6 @@ set(ZIG_STD_FILES
"zig.zig"
"zig/ast.zig"
"zig/parse.zig"
- "zig/parse2.zig"
"zig/parse_string_literal.zig"
"zig/render.zig"
"zig/tokenizer.zig"