package parser import "../ast" import "../source" import "../symbol" import "../token" import "../types" import "base:intrinsics" import "core:fmt" import "core:strconv" import "core:strings" import "core:unicode/utf8" Parser :: struct { tokens: ^token.Stream, source_file: ^source.Source, diagnostics: ^source.Diagnostics, module: ast.Module, pkg: ast.Package_Id, file: ast.File_Id, cursor: int, delimiter_depth: int, // Suppresses `Name { ... }` struct-literal parsing at delimiter depth 0 so a // control-flow condition like `if foo { ... }` does not swallow the block as a // struct literal. Nested `(`/`[`/call-arg contexts (delimiter_depth > 0) still // allow struct literals. no_struct_literal: bool, } MAX_EXPRESSION_NESTING :: 256 token_text :: proc(parser: ^Parser, tok: token.Token) -> string { if tok.span.end < tok.span.start || int(tok.span.end) > len(parser.source_file.text) { return "" } return parser.source_file.text[int(tok.span.start):int(tok.span.end)] } span_from :: proc(first, last: source.Span) -> source.Span { return source.Span{file=first.file, start=first.start, end=last.end} } current :: proc(parser: ^Parser) -> token.Token { return parser.tokens.items[min(parser.cursor, len(parser.tokens.items)-1)] } previous :: proc(parser: ^Parser) -> token.Token { return parser.tokens.items[max(parser.cursor-1, 0)] } advance :: proc(parser: ^Parser) -> token.Token { result := current(parser) if result.kind != .Eof { parser.cursor += 1 } return result } allow :: proc(parser: ^Parser, kind: token.Kind) -> (token.Token, bool) { if current(parser).kind == kind { return advance(parser), true } return current(parser), false } skip_newlines :: proc(parser: ^Parser) { for current(parser).kind == .Newline { advance(parser) } } add_expr :: proc(parser: ^Parser, expr: ast.Expr) -> ast.Expr_Id { id := ast.expr_id(len(parser.module.exprs)) append(&parser.module.exprs, expr) return id } invalid_expr :: proc(parser: ^Parser, span: source.Span, message: string) -> ast.Expr_Id { id := source.add(parser.diagnostics, span, message) return add_expr(parser, ast.Expr{ kind=.Invalid, span=span, left=ast.INVALID_EXPR, right=ast.INVALID_EXPR, diagnostic=id, }) } is_type_token :: proc(kind: token.Kind) -> bool { #partial switch kind { case .Keyword_Int, .Keyword_I8, .Keyword_I16, .Keyword_I32, .Keyword_I64, .Keyword_U8, .Keyword_U16, .Keyword_U32, .Keyword_U64, .Keyword_Isize, .Keyword_Usize, .Keyword_F32, .Keyword_F64, .Keyword_C_Char, .Keyword_C_Schar, .Keyword_C_Uchar, .Keyword_C_Short, .Keyword_C_Ushort, .Keyword_C_Int, .Keyword_C_Uint, .Keyword_C_Long, .Keyword_C_Ulong, .Keyword_C_Longlong, .Keyword_C_Ulonglong, .Keyword_C_Float, .Keyword_C_Double, .Keyword_C_Longdouble, .Keyword_Void, .Keyword_Bool, .Keyword_C_Func, .Identifier, .Question, .At, .Star, .Left_Bracket: return true } return false } decode_character :: proc(parser: ^Parser, tok: token.Token) -> (u64, bool) { text := token_text(parser, tok) if len(text) < 3 { return 0, false } contents := text[1:len(text)-1] if len(contents) == 2 && contents[0] == '\\' { switch contents[1] { case '0': return 0, true case 'n': return '\n', true case 'r': return '\r', true case 't': return '\t', true case '\\': return '\\', true case '\'': return '\'', true } return 0, false } value, width := utf8.decode_rune_in_string(contents) return u64(value), width == len(contents) } parse_type_constant :: proc(parser: ^Parser) -> (u64, bool) { negative := false if _, ok := allow(parser, .Minus); ok { negative = true } tok := current(parser) if tok.kind == .Integer { advance(parser) value, ok := parse_integer_magnitude(token_text(parser, tok)) if !ok { return 0, false } if negative { return transmute(u64)-i64(value), true } return value, true } if !negative && tok.kind == .Character { advance(parser) return decode_character(parser, tok) } source.add(parser.diagnostics, tok.span, "expected an integer or character constant") return 0, false } parse_type :: proc(parser: ^Parser) -> ast.Type_Syntax { tok := current(parser) if tok.kind == .Question { advance(parser) child := parse_type(parser) return types.intern(&parser.module.type_store, types.Node{kind=.Optional, child=child}) } if tok.kind == .At || tok.kind == .Star { many := tok.kind == .Star advance(parser) _, mutable := allow(parser, .Keyword_Mut) child := parse_type(parser) return types.intern(&parser.module.type_store, types.Node{ kind=.Pointer, child=child, mutable=mutable, many=many, }) } if tok.kind == .Left_Bracket { advance(parser) node := types.Node{} if _, ok := allow(parser, .Right_Bracket); ok { node.kind = .Slice } else if _, ok := allow(parser, .Star); ok { node.kind = .Pointer node.many = true node.has_sentinel = true if _, ok = allow(parser, .Semicolon); !ok { source.add(parser.diagnostics, current(parser).span, "expected ';' after '*' in sentinel pointer type") } node.sentinel, _ = parse_type_constant(parser) if _, ok = allow(parser, .Right_Bracket); !ok { source.add(parser.diagnostics, current(parser).span, "expected ']' after sentinel pointer type") } } else if _, ok := allow(parser, .Semicolon); ok { node.kind = .Slice node.has_sentinel = true node.sentinel, _ = parse_type_constant(parser) if _, ok = allow(parser, .Right_Bracket); !ok { source.add(parser.diagnostics, current(parser).span, "expected ']' after sentinel slice type") } } else { node.kind = .Array if _, ok := allow(parser, .Underscore); ok { node.inferred_count = true } else { count, ok := parse_type_constant(parser) if ok { node.count = count } } if _, ok := allow(parser, .Semicolon); ok { node.has_sentinel = true node.sentinel, _ = parse_type_constant(parser) } if _, ok := allow(parser, .Right_Bracket); !ok { source.add(parser.diagnostics, current(parser).span, "expected ']' after array type") } } _, node.mutable = allow(parser, .Keyword_Mut) node.child = parse_type(parser) return types.intern(&parser.module.type_store, node) } #partial switch tok.kind { case .Keyword_Int: advance(parser) return types.INT case .Keyword_I8: advance(parser) return types.I8 case .Keyword_I16: advance(parser) return types.I16 case .Keyword_I32: advance(parser) return types.I32 case .Keyword_I64: advance(parser) return types.I64 case .Keyword_U8: advance(parser) return types.U8 case .Keyword_U16: advance(parser) return types.U16 case .Keyword_U32: advance(parser) return types.U32 case .Keyword_U64: advance(parser) return types.U64 case .Keyword_Isize: advance(parser) return types.ISIZE case .Keyword_Usize: advance(parser) return types.USIZE case .Keyword_F32: advance(parser) return types.F32 case .Keyword_F64: advance(parser) return types.F64 case .Keyword_C_Char: advance(parser) return types.C_CHAR case .Keyword_C_Schar: advance(parser) return types.C_SCHAR case .Keyword_C_Uchar: advance(parser) return types.C_UCHAR case .Keyword_C_Short: advance(parser) return types.C_SHORT case .Keyword_C_Ushort: advance(parser) return types.C_USHORT case .Keyword_C_Int: advance(parser) return types.C_INT case .Keyword_C_Uint: advance(parser) return types.C_UINT case .Keyword_C_Long: advance(parser) return types.C_LONG case .Keyword_C_Ulong: advance(parser) return types.C_ULONG case .Keyword_C_Longlong: advance(parser) return types.C_LONGLONG case .Keyword_C_Ulonglong: advance(parser) return types.C_ULONGLONG case .Keyword_C_Float: advance(parser) return types.C_FLOAT case .Keyword_C_Double: advance(parser) return types.C_DOUBLE case .Keyword_C_Longdouble: advance(parser) return types.C_LONGDOUBLE case .Keyword_Void: advance(parser) return types.VOID case .Keyword_Bool: advance(parser) return types.BOOL case .Keyword_C_Func: advance(parser) if _, ok := allow(parser, .Left_Paren); !ok { source.add(parser.diagnostics, current(parser).span, "expected '(' after c_func type") return types.INVALID } params, variadic := parse_params(parser) if _, ok := allow(parser, .Right_Paren); !ok { source.add(parser.diagnostics, current(parser).span, "expected ')' after function type parameters") } result := parse_type(parser) param_types := make([]types.Type, len(params), parser.module.allocator) for param, index in params { param_types[index] = param.type } function_type := types.function(&parser.module.type_store, param_types, result, true, variadic) delete(param_types, parser.module.allocator) delete(params, parser.module.allocator) return function_type case .Identifier: first := advance(parser) name := first qualifier := symbol.INVALID if _, ok := allow(parser, .Dot); ok { qualifier = first.symbol if current(parser).kind != .Identifier { source.add(parser.diagnostics, current(parser).span, "expected a type name after '.'") return types.INVALID } name = advance(parser) } return types.named( &parser.module.type_store, u32(parser.pkg), u32(name.symbol), u32(qualifier), u32(parser.file), ) } source.add(parser.diagnostics, tok.span, "expected a type") return types.INVALID } skip_parenthesized :: proc(parser: ^Parser) -> source.Span { start := current(parser) depth := 0 end := start for current(parser).kind != .Eof { tok := advance(parser) end = tok if tok.kind == .Left_Paren { depth += 1 } else if tok.kind == .Right_Paren { depth -= 1 if depth == 0 { break } } } return span_from(start.span, end.span) } parse_call_args :: proc(parser: ^Parser, nesting: int) -> ([]ast.Expr_Id, token.Token) { left_paren := advance(parser) parser.delimiter_depth += 1 defer parser.delimiter_depth -= 1 args: [dynamic]ast.Expr_Id args.allocator = parser.module.allocator skip_newlines(parser) for current(parser).kind != .Right_Paren && current(parser).kind != .Eof { append(&args, parse_expression_bp(parser, 0, nesting+1)) skip_newlines(parser) if _, ok := allow(parser, .Comma); ok { skip_newlines(parser) continue } break } right_paren, ok := allow(parser, .Right_Paren) if !ok { source.add(parser.diagnostics, current(parser).span, "expected ')' after call arguments") right_paren = left_paren } return args[:], right_paren } parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Token, nesting: int) -> ast.Expr_Id { if nesting >= MAX_EXPRESSION_NESTING { span := skip_parenthesized(parser) return invalid_expr(parser, span, "expression nesting exceeds 256 levels") } args, right_paren := parse_call_args(parser, nesting) return add_expr(parser, ast.Expr{ kind=.Call, span=source.Span{file=name.span.file, start=first.span.start, end=right_paren.span.end}, qualifier=qualifier, name=name.symbol, args=args[:], left=ast.INVALID_EXPR, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) } parse_array_literal :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id { start := advance(parser) parser.delimiter_depth += 1 defer parser.delimiter_depth -= 1 args: [dynamic]ast.Expr_Id args.allocator = parser.module.allocator skip_newlines(parser) for current(parser).kind != .Right_Bracket && current(parser).kind != .Eof { append(&args, parse_expression_bp(parser, 0, nesting+1)) skip_newlines(parser) if _, ok := allow(parser, .Comma); ok { skip_newlines(parser) continue } break } end, ok := allow(parser, .Right_Bracket) if !ok { source.add(parser.diagnostics, current(parser).span, "expected ']' after array literal") end = start } return add_expr(parser, ast.Expr{ kind=.Array, span=span_from(start.span, end.span), args=args[:], left=ast.INVALID_EXPR, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) } parse_struct_literal :: proc( parser: ^Parser, qualifier: symbol.Id, first, name: token.Token, nesting: int, ) -> ast.Expr_Id { left_brace := advance(parser) parser.delimiter_depth += 1 defer parser.delimiter_depth -= 1 args: [dynamic]ast.Expr_Id args.allocator = parser.module.allocator skip_newlines(parser) for current(parser).kind != .Right_Brace && current(parser).kind != .Eof { field := current(parser) if field.kind != .Identifier { source.add(parser.diagnostics, field.span, "expected a keyed struct field initializer") break } advance(parser) if _, ok := allow(parser, .Equal); !ok { source.add(parser.diagnostics, current(parser).span, "expected '=' after struct field name") } skip_newlines(parser) value := parse_expression_bp(parser, 0, nesting+1) append(&args, add_expr(parser, ast.Expr{ kind=.Keyed, span=span_from(field.span, parser.module.exprs[value].span), name=field.symbol, left=value, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, })) skip_newlines(parser) if _, ok := allow(parser, .Comma); ok { skip_newlines(parser) continue } break } right_brace, ok := allow(parser, .Right_Brace) if !ok { source.add(parser.diagnostics, current(parser).span, "expected '}' after struct literal") right_brace = left_brace } return add_expr(parser, ast.Expr{ kind=.Struct_Literal, span=source.Span{file=name.span.file, start=first.span.start, end=right_brace.span.end}, qualifier=qualifier, name=name.symbol, args=args[:], left=ast.INVALID_EXPR, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) } parse_integer_magnitude :: proc(text: string) -> (u64, bool) { value: u64 for byte in transmute([]byte)text { if byte < '0' || byte > '9' { return 0, false } next, overflow := intrinsics.overflow_mul(value, u64(10)) if overflow { return 0, false } value, overflow = intrinsics.overflow_add(next, u64(byte-'0')) if overflow { return 0, false } } return value, len(text) > 0 } parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id { tok := current(parser) #partial switch tok.kind { case .Integer: advance(parser) value, ok := parse_integer_magnitude(token_text(parser, tok)) if !ok { return invalid_expr(parser, tok.span, "integer literal magnitude does not fit in u64") } return add_expr(parser, ast.Expr{ kind=.Integer, span=tok.span, integer=value, left=ast.INVALID_EXPR, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) case .Character: advance(parser) value, ok := decode_character(parser, tok) if !ok { return invalid_expr(parser, tok.span, "character literal must contain one Unicode code point") } return add_expr(parser, ast.Expr{ kind=.Integer, span=tok.span, integer=value, left=ast.INVALID_EXPR, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) case .Float: advance(parser) value, ok := strconv.parse_f64(token_text(parser, tok)) if !ok { return invalid_expr(parser, tok.span, "invalid floating-point literal") } return add_expr(parser, ast.Expr{ kind=.Float, span=tok.span, integer=transmute(u64)value, left=ast.INVALID_EXPR, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) case .String: advance(parser) value := decode_import_path(parser, tok) id := u64(len(parser.module.strings)) append(&parser.module.strings, value) return add_expr(parser, ast.Expr{ kind=.String, span=tok.span, integer=id, left=ast.INVALID_EXPR, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) case .Keyword_None: advance(parser) return add_expr(parser, ast.Expr{ kind=.None, span=tok.span, left=ast.INVALID_EXPR, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) case .Keyword_True, .Keyword_False: advance(parser) return add_expr(parser, ast.Expr{ kind=.Bool, span=tok.span, integer=1 if tok.kind == .Keyword_True else 0, left=ast.INVALID_EXPR, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) case .Left_Bracket: return parse_array_literal(parser, nesting) case .Identifier: first := advance(parser) name := first qualifier := symbol.INVALID if _, ok := allow(parser, .Dot); ok { if current(parser).kind != .Identifier { return invalid_expr(parser, current(parser).span, "expected a package member after '.'") } qualifier = first.symbol name = advance(parser) } if current(parser).kind == .Left_Paren { return parse_call(parser, qualifier, first, name, nesting) } if current(parser).kind == .Left_Brace && !(parser.no_struct_literal && parser.delimiter_depth == 0) { return parse_struct_literal(parser, qualifier, first, name, nesting) } return add_expr(parser, ast.Expr{ kind=.Name, span=span_from(first.span, name.span), qualifier=qualifier, name=name.symbol, left=ast.INVALID_EXPR, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) case .Underscore: advance(parser) return invalid_expr(parser, tok.span, "'_' is a write-only sink and cannot be read") case .Left_Paren: if nesting >= MAX_EXPRESSION_NESTING { span := skip_parenthesized(parser) return invalid_expr(parser, span, "expression nesting exceeds 256 levels") } advance(parser) parser.delimiter_depth += 1 defer parser.delimiter_depth -= 1 skip_newlines(parser) expr := parse_expression_bp(parser, 0, nesting+1) skip_newlines(parser) if _, ok := allow(parser, .Right_Paren); !ok { source.add(parser.diagnostics, current(parser).span, "expected ')'") } return expr case .Invalid: advance(parser) return add_expr(parser, ast.Expr{ kind=.Invalid, span=tok.span, left=ast.INVALID_EXPR, right=ast.INVALID_EXPR, diagnostic=tok.diagnostic, }) } if tok.kind != .Newline && tok.kind != .Right_Brace && tok.kind != .Eof { advance(parser) } return invalid_expr(parser, tok.span, "expected an expression") } infix_binding_power :: proc(kind: token.Kind) -> (left, right: int, ok: bool) { #partial switch kind { case .Keyword_Orelse: return 2, 3, true case .Keyword_Or: return 4, 5, true case .Keyword_And: return 6, 7, true case .Equal_Equal, .Bang_Equal, .Less, .Less_Equal, .Greater, .Greater_Equal: return 8, 9, true case .Plus: return 10, 11, true } return 0, 0, false } infix_expr_kind :: proc(kind: token.Kind) -> ast.Expr_Kind { #partial switch kind { case .Keyword_Orelse: return .Orelse case .Keyword_Or: return .Or case .Keyword_And: return .And case .Equal_Equal: return .Eq case .Bang_Equal: return .Ne case .Less: return .Lt case .Less_Equal: return .Le case .Greater: return .Gt case .Greater_Equal: return .Ge case: return .Add } } prefix_binding_power :: proc(kind: token.Kind) -> (right: int, ok: bool) { #partial switch kind { case .Minus, .Ampersand, .Bang: return 20, true } return 0, false } parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int) -> ast.Expr_Id { if nesting > MAX_EXPRESSION_NESTING { tok := current(parser) if tok.kind != .Newline && tok.kind != .Right_Brace && tok.kind != .Eof { advance(parser) } return invalid_expr(parser, tok.span, "expression nesting exceeds 256 levels") } left := ast.INVALID_EXPR if right_power, ok := prefix_binding_power(current(parser).kind); ok { operator := advance(parser) if parser.delimiter_depth > 0 { skip_newlines(parser) } operand := parse_expression_bp(parser, right_power, nesting+1) operand_expr := parser.module.exprs[operand] prefix_kind := ast.Expr_Kind.Negate #partial switch operator.kind { case .Ampersand: prefix_kind = .Address case .Bang: prefix_kind = .Not } left = add_expr(parser, ast.Expr{ kind=prefix_kind, span=span_from(operator.span, operand_expr.span), left=operand, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) } else { left = parse_primary(parser, nesting) } if parser.delimiter_depth > 0 { skip_newlines(parser) } for { if current(parser).kind == .Caret || current(parser).kind == .Question { operator := advance(parser) left_expr := parser.module.exprs[left] left = add_expr(parser, ast.Expr{ kind=.Deref if operator.kind == .Caret else .Unwrap, span=span_from(left_expr.span, operator.span), left=left, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) continue } if current(parser).kind == .Dot { advance(parser) field := current(parser) if field.kind != .Identifier { left = invalid_expr(parser, field.span, "expected a field name after '.'") continue } advance(parser) left_expr := parser.module.exprs[left] left = add_expr(parser, ast.Expr{ kind=.Field, span=span_from(left_expr.span, field.span), name=field.symbol, left=left, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) continue } if current(parser).kind == .Left_Bracket { start_token := advance(parser) parser.delimiter_depth += 1 skip_newlines(parser) start_expr := ast.INVALID_EXPR end_expr := ast.INVALID_EXPR slicing := false if _, ok := allow(parser, .Range); ok { slicing = true } else { start_expr = parse_expression_bp(parser, 0, nesting+1) skip_newlines(parser) if _, ok := allow(parser, .Range); ok { slicing = true } } skip_newlines(parser) if slicing && current(parser).kind != .Right_Bracket { end_expr = parse_expression_bp(parser, 0, nesting+1) skip_newlines(parser) } end_token, ok := allow(parser, .Right_Bracket) if !ok { source.add(parser.diagnostics, current(parser).span, "expected ']' after index or slice") end_token = start_token } parser.delimiter_depth -= 1 left_expr := parser.module.exprs[left] if slicing { args := make([]ast.Expr_Id, 2, parser.module.allocator) args[0] = start_expr args[1] = end_expr left = add_expr(parser, ast.Expr{ kind=.Slice, span=span_from(left_expr.span, end_token.span), args=args, left=left, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) } else { left = add_expr(parser, ast.Expr{ kind=.Index, span=span_from(left_expr.span, end_token.span), left=left, right=start_expr, diagnostic=source.INVALID_DIAGNOSTIC, }) } continue } if current(parser).kind == .Left_Paren { if nesting >= MAX_EXPRESSION_NESTING { span := skip_parenthesized(parser) left = invalid_expr(parser, span, "expression nesting exceeds 256 levels") continue } left_expr := parser.module.exprs[left] args, right_paren := parse_call_args(parser, nesting) left = add_expr(parser, ast.Expr{ kind=.Call, span=span_from(left_expr.span, right_paren.span), args=args, left=left, right=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) continue } left_power, right_power, ok := infix_binding_power(current(parser).kind) if !ok || left_power < minimum_binding_power { break } operator := advance(parser) skip_newlines(parser) right := parse_expression_bp(parser, right_power, nesting+1) left_expr := parser.module.exprs[left] right_expr := parser.module.exprs[right] left = add_expr(parser, ast.Expr{ kind=infix_expr_kind(operator.kind), span=span_from(left_expr.span, right_expr.span), left=left, right=right, diagnostic=source.INVALID_DIAGNOSTIC, }) if parser.delimiter_depth > 0 { skip_newlines(parser) } } return left } parse_expression :: proc(parser: ^Parser) -> ast.Expr_Id { return parse_expression_bp(parser, 0, 0) } finish_statement :: proc(parser: ^Parser, allow_closing_brace := false) -> source.Diagnostic_Id { if current(parser).kind == .Newline { skip_newlines(parser) return source.INVALID_DIAGNOSTIC } if current(parser).kind == .Eof || allow_closing_brace && current(parser).kind == .Right_Brace { return source.INVALID_DIAGNOSTIC } diagnostic := source.add( parser.diagnostics, current(parser).span, "completed statements must be followed by a newline", ) for current(parser).kind != .Newline && current(parser).kind != .Right_Brace && current(parser).kind != .Eof { advance(parser) } skip_newlines(parser) return diagnostic } parse_return :: proc(parser: ^Parser) -> ast.Stmt_Id { start := advance(parser) skip_newlines(parser) if current(parser).kind == .Underscore { end := advance(parser) id := ast.stmt_id(len(parser.module.statements)) append(&parser.module.statements, ast.Stmt{ kind=.Return, span=span_from(start.span, end.span), name=end.symbol, expr=ast.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) return id } expr := parse_expression(parser) id := ast.stmt_id(len(parser.module.statements)) append(&parser.module.statements, ast.Stmt{ kind=.Return, span=span_from(start.span, parser.module.exprs[expr].span), expr=expr, diagnostic=source.INVALID_DIAGNOSTIC, }) return id } starts_declared_type :: proc(parser: ^Parser) -> bool { if current(parser).kind != .Left_Bracket { return is_type_token(current(parser).kind) } depth := 0 cursor := parser.cursor for cursor < len(parser.tokens.items) { kind := parser.tokens.items[cursor].kind if kind == .Left_Bracket { depth += 1 } else if kind == .Right_Bracket { depth -= 1 if depth == 0 { cursor += 1 break } } cursor += 1 } if cursor < len(parser.tokens.items) && parser.tokens.items[cursor].kind == .Keyword_Mut { cursor += 1 } return cursor < len(parser.tokens.items) && is_type_token(parser.tokens.items[cursor].kind) } parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id { if current(parser).kind == .Keyword_Return { return parse_return(parser) } if current(parser).kind == .Keyword_If { return parse_if(parser) } if current(parser).kind == .Keyword_While { return parse_while(parser) } if current(parser).kind == .Identifier || current(parser).kind == .Underscore { start_cursor := parser.cursor name := advance(parser) type_syntax := types.INVALID had_type := false if starts_declared_type(parser) { type_syntax = parse_type(parser) had_type = true } operator := current(parser) if operator.kind == .Colon_Colon || operator.kind == .Equal { advance(parser) skip_newlines(parser) expr := parse_expression(parser) kind := ast.Stmt_Kind.Assignment immutable := false if operator.kind == .Colon_Colon || had_type { kind = .Declaration immutable = operator.kind == .Colon_Colon } id := ast.stmt_id(len(parser.module.statements)) append(&parser.module.statements, ast.Stmt{ kind=kind, span=span_from(name.span, parser.module.exprs[expr].span), name=name.symbol, type=type_syntax, immutable=immutable, target=ast.INVALID_EXPR, expr=expr, diagnostic=source.INVALID_DIAGNOSTIC, }) return id } parser.cursor = start_cursor } expr := parse_expression(parser) if _, ok := allow(parser, .Equal); ok { skip_newlines(parser) value := parse_expression(parser) id := ast.stmt_id(len(parser.module.statements)) append(&parser.module.statements, ast.Stmt{ kind=.Assignment, span=span_from(parser.module.exprs[expr].span, parser.module.exprs[value].span), target=expr, expr=value, diagnostic=source.INVALID_DIAGNOSTIC, }) return id } id := ast.stmt_id(len(parser.module.statements)) append(&parser.module.statements, ast.Stmt{ kind=.Expression, span=parser.module.exprs[expr].span, expr=expr, diagnostic=source.INVALID_DIAGNOSTIC, }) return id } parse_params :: proc(parser: ^Parser) -> ([]ast.Param, bool) { params: [dynamic]ast.Param params.allocator = parser.module.allocator variadic := false skip_newlines(parser) for current(parser).kind != .Right_Paren && current(parser).kind != .Eof { if current(parser).kind == .Ellipsis { marker := advance(parser) if variadic { source.add(parser.diagnostics, marker.span, "duplicate variadic marker") } variadic = true skip_newlines(parser) if _, ok := allow(parser, .Comma); ok { skip_newlines(parser) } if current(parser).kind != .Right_Paren { source.add(parser.diagnostics, current(parser).span, "variadic marker must be the final parameter") } continue } names: [dynamic]token.Token names.allocator = parser.module.allocator for { if current(parser).kind != .Identifier { source.add(parser.diagnostics, current(parser).span, "expected parameter name") break } append(&names, advance(parser)) if is_type_token(current(parser).kind) { break } if _, ok := allow(parser, .Comma); !ok { source.add(parser.diagnostics, current(parser).span, "expected ',' or parameter type") break } skip_newlines(parser) } type_syntax := parse_type(parser) for name in names { append(¶ms, ast.Param{name=name.symbol, span=name.span, type=type_syntax}) } delete(names) skip_newlines(parser) if _, ok := allow(parser, .Comma); ok { skip_newlines(parser) continue } break } return params[:], variadic } // parse_block parses a brace-delimited statement sequence `{ ... }`, consuming // both braces, and returns the contained statement ids. Shared by function // bodies and control-flow blocks. parse_block :: proc(parser: ^Parser) -> []ast.Stmt_Id { body: [dynamic]ast.Stmt_Id body.allocator = parser.module.allocator if _, ok := allow(parser, .Left_Brace); !ok { source.add(parser.diagnostics, current(parser).span, "expected '{' to open a block") return body[:] } skip_newlines(parser) for current(parser).kind != .Right_Brace && current(parser).kind != .Eof { append(&body, parse_statement(parser)) if diagnostic := finish_statement(parser, true); diagnostic != source.INVALID_DIAGNOSTIC { statement_id := ast.stmt_id(len(parser.module.statements)) append(&parser.module.statements, ast.Stmt{ kind=.Invalid, span=current(parser).span, expr=ast.INVALID_EXPR, diagnostic=diagnostic, }) append(&body, statement_id) } } if _, ok := allow(parser, .Right_Brace); !ok { source.add(parser.diagnostics, current(parser).span, "expected '}' to close a block") } return body[:] } parse_if :: proc(parser: ^Parser) -> ast.Stmt_Id { start := advance(parser) // consume 'if' skip_newlines(parser) saved := parser.no_struct_literal parser.no_struct_literal = true condition := parse_expression(parser) parser.no_struct_literal = saved binding := symbol.INVALID if _, ok := allow(parser, .Pipe); ok { name_tok, name_ok := allow(parser, .Identifier) if name_ok { binding = name_tok.symbol } else { source.add(parser.diagnostics, current(parser).span, "expected a binding name after '|'") } if _, close_ok := allow(parser, .Pipe); !close_ok { source.add(parser.diagnostics, current(parser).span, "expected '|' to close the unwrap binding") } } skip_newlines(parser) then_body := parse_block(parser) else_body: []ast.Stmt_Id = nil saved_cursor := parser.cursor skip_newlines(parser) if current(parser).kind == .Keyword_Else { advance(parser) skip_newlines(parser) if current(parser).kind == .Keyword_If { nested := parse_if(parser) single := make([]ast.Stmt_Id, 1, parser.module.allocator) single[0] = nested else_body = single } else { else_body = parse_block(parser) } } else { parser.cursor = saved_cursor } id := ast.stmt_id(len(parser.module.statements)) append(&parser.module.statements, ast.Stmt{ kind=.If, span=span_from(start.span, previous(parser).span), name=binding, expr=condition, body=then_body, else_body=else_body, diagnostic=source.INVALID_DIAGNOSTIC, }) return id } parse_while_update :: proc(parser: ^Parser) -> ast.Stmt_Id { parenthesized := false if _, ok := allow(parser, .Left_Paren); ok { parenthesized = true parser.delimiter_depth += 1 skip_newlines(parser) } update := ast.INVALID_STMT if current(parser).kind == .Left_Brace || current(parser).kind == .Right_Paren || current(parser).kind == .Eof { diagnostic := source.add( parser.diagnostics, current(parser).span, "expected a while update statement after ':'", ) update = ast.stmt_id(len(parser.module.statements)) append(&parser.module.statements, ast.Stmt{ kind=.Invalid, span=current(parser).span, expr=ast.INVALID_EXPR, update=ast.INVALID_STMT, diagnostic=diagnostic, }) } else { saved := parser.no_struct_literal parser.no_struct_literal = true update = parse_statement(parser) parser.no_struct_literal = saved statement := &parser.module.statements[update] switch statement.kind { case .Assignment, .Expression: case .Invalid, .Declaration, .Return, .If, .While: diagnostic := source.add( parser.diagnostics, statement.span, "while update must be an assignment, sink, or expression statement", ) statement.kind = .Invalid statement.diagnostic = diagnostic } } if parenthesized { skip_newlines(parser) if _, ok := allow(parser, .Right_Paren); !ok { source.add(parser.diagnostics, current(parser).span, "expected ')' after while update") for current(parser).kind != .Right_Paren && current(parser).kind != .Left_Brace && current(parser).kind != .Newline && current(parser).kind != .Eof { advance(parser) } _, _ = allow(parser, .Right_Paren) } parser.delimiter_depth -= 1 } return update } parse_while :: proc(parser: ^Parser) -> ast.Stmt_Id { start := advance(parser) // consume 'while' skip_newlines(parser) saved := parser.no_struct_literal parser.no_struct_literal = true condition := parse_expression(parser) parser.no_struct_literal = saved skip_newlines(parser) update := ast.INVALID_STMT if _, ok := allow(parser, .Colon); ok { skip_newlines(parser) update = parse_while_update(parser) skip_newlines(parser) } body := parse_block(parser) id := ast.stmt_id(len(parser.module.statements)) append(&parser.module.statements, ast.Stmt{ kind=.While, span=span_from(start.span, previous(parser).span), expr=condition, body=body, update=update, diagnostic=source.INVALID_DIAGNOSTIC, }) return id } parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) { advance(parser) if _, ok := allow(parser, .Left_Paren); !ok { source.add(parser.diagnostics, current(parser).span, "expected '(' after 'func'") } params, variadic := parse_params(parser) if _, ok := allow(parser, .Right_Paren); !ok { source.add(parser.diagnostics, current(parser).span, "expected ')' after parameters") } skip_newlines(parser) result := parse_type(parser) end := previous(parser) ended_by_newline := current(parser).kind == .Newline if current(parser).kind == .Newline { skip_newlines(parser) } if current(parser).kind != .Left_Brace { if !ended_by_newline && current(parser).kind != .Eof { _ = finish_statement(parser) } _ = ast.function_id(len(parser.module.functions)) append(&parser.module.functions, ast.Function{ span=span_from(name.span, end.span), name=name.symbol, pkg=parser.pkg, file=parser.file, c_abi=c_abi, has_body=false, variadic=variadic, params=params, result=result, diagnostic=source.INVALID_DIAGNOSTIC, }) return } body := parse_block(parser) end = previous(parser) _ = ast.function_id(len(parser.module.functions)) append(&parser.module.functions, ast.Function{ span=span_from(name.span, end.span), name=name.symbol, pkg=parser.pkg, file=parser.file, c_abi=c_abi, has_body=true, variadic=variadic, params=params, result=result, body=body, diagnostic=source.INVALID_DIAGNOSTIC, }) } parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool) { start := advance(parser) id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol)) ended_by_newline := current(parser).kind == .Newline skip_newlines(parser) if current(parser).kind != .Left_Brace { if !c_layout { source.add(parser.diagnostics, start.span, "native struct declarations require a body") } if !types.define_struct(&parser.module.type_store, id, nil, c_layout, true) { source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name)) } if !ended_by_newline { _ = finish_statement(parser) } return } advance(parser) fields: [dynamic]types.Field fields.allocator = parser.module.allocator defer delete(fields) skip_newlines(parser) for current(parser).kind != .Right_Brace && current(parser).kind != .Eof { if current(parser).kind != .Identifier { source.add(parser.diagnostics, current(parser).span, "expected a struct field name") for current(parser).kind != .Newline && current(parser).kind != .Right_Brace && current(parser).kind != .Eof { advance(parser) } skip_newlines(parser) continue } field_name := advance(parser) field_type := parse_type(parser) append(&fields, types.Field{name=u32(field_name.symbol), type=field_type}) if _, ok := allow(parser, .Comma); ok { skip_newlines(parser) continue } _ = finish_statement(parser, true) } if _, ok := allow(parser, .Right_Brace); !ok { source.add(parser.diagnostics, current(parser).span, "expected '}' after struct fields") } if !types.define_struct(&parser.module.type_store, id, fields[:], c_layout, false) { source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name)) } _ = finish_statement(parser) } decode_import_path :: proc(parser: ^Parser, tok: token.Token) -> string { text := token_text(parser, tok) if len(text) < 2 { return fmt.aprintf("", allocator=parser.module.allocator) } builder := strings.builder_make(parser.module.allocator) defer strings.builder_destroy(&builder) for index := 1; index < len(text)-1; index += 1 { value := text[index] if value == '\\' && index+1 < len(text)-1 { index += 1 value = text[index] switch value { case 'n': value = '\n' case 'r': value = '\r' case 't': value = '\t' case '0': value = 0 case: } } strings.write_byte(&builder, value) } return fmt.aprintf("%s", strings.to_string(builder), allocator=parser.module.allocator) } parse_import :: proc(parser: ^Parser, alias: token.Token, start: token.Token) { skip_newlines(parser) path_token := current(parser) if path_token.kind != .String { source.add(parser.diagnostics, path_token.span, "expected an import path string") if path_token.kind != .Newline && path_token.kind != .Eof { advance(parser) } _ = ast.import_id(len(parser.module.imports)) append(&parser.module.imports, ast.Import{ span=start.span, alias=alias.symbol, pkg=parser.pkg, file=parser.file, target=ast.INVALID_PACKAGE, valid=false, diagnostic=source.INVALID_DIAGNOSTIC, }) _ = finish_statement(parser) return } advance(parser) _ = ast.import_id(len(parser.module.imports)) append(&parser.module.imports, ast.Import{ span=span_from(start.span, path_token.span), alias=alias.symbol, path=decode_import_path(parser, path_token), pkg=parser.pkg, file=parser.file, target=ast.INVALID_PACKAGE, valid=true, diagnostic=source.INVALID_DIAGNOSTIC, }) _ = finish_statement(parser) } parse_top_level :: proc(parser: ^Parser) { if current(parser).kind == .Keyword_Import { start := advance(parser) parse_import(parser, token.Token{}, start) return } if current(parser).kind != .Identifier { source.add(parser.diagnostics, current(parser).span, "expected a top-level declaration") for current(parser).kind != .Newline && current(parser).kind != .Eof { advance(parser) } _ = finish_statement(parser) return } name := advance(parser) if current(parser).kind == .Colon_Colon { saved := parser.cursor advance(parser) skip_newlines(parser) if current(parser).kind == .Keyword_Import { start := advance(parser) parse_import(parser, name, start) return } parser.cursor = saved } type_syntax := types.INVALID if is_type_token(current(parser).kind) { type_syntax = parse_type(parser) } operator := current(parser) if operator.kind != .Colon_Colon && operator.kind != .Equal { source.add(parser.diagnostics, operator.span, "expected '::' or '=' after top-level name") _ = finish_statement(parser) return } advance(parser) skip_newlines(parser) if operator.kind == .Colon_Colon && (current(parser).kind == .Keyword_Func || current(parser).kind == .Keyword_C_Func) { c_abi := current(parser).kind == .Keyword_C_Func parse_function(parser, name, c_abi) return } if operator.kind == .Colon_Colon && (current(parser).kind == .Keyword_Struct || current(parser).kind == .Keyword_C_Struct) { parse_struct(parser, name, current(parser).kind == .Keyword_C_Struct) return } expr := parse_expression(parser) _ = ast.global_id(len(parser.module.globals)) append(&parser.module.globals, ast.Global{ span=span_from(name.span, parser.module.exprs[expr].span), name=name.symbol, pkg=parser.pkg, file=parser.file, type=type_syntax, immutable=operator.kind == .Colon_Colon, expr=expr, diagnostic=source.INVALID_DIAGNOSTIC, }) _ = finish_statement(parser) } parse :: proc( stream: ^token.Stream, source_file: ^source.Source, diagnostics: ^source.Diagnostics, allocator := context.allocator, ) -> ast.Module { parser := Parser{ tokens=stream, source_file=source_file, diagnostics=diagnostics, module=ast.init_module(allocator), } skip_newlines(&parser) for current(&parser).kind != .Eof { parse_top_level(&parser) skip_newlines(&parser) } return parser.module } parse_into :: proc( stream: ^token.Stream, source_file: ^source.Source, diagnostics: ^source.Diagnostics, module: ^ast.Module, pkg: ast.Package_Id, file: ast.File_Id, ) { parser := Parser{ tokens=stream, source_file=source_file, diagnostics=diagnostics, module=module^, pkg=pkg, file=file, } skip_newlines(&parser) for current(&parser).kind != .Eof { parse_top_level(&parser) skip_newlines(&parser) } module^ = parser.module }