diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin new file mode 100644 index 000000000..b4f1ba2af --- /dev/null +++ b/core/odin/ast/ast.odin @@ -0,0 +1,607 @@ +package odin_ast + +import "core:odin/token" + +Proc_Tag :: enum { + Bounds_Check, + No_Bounds_Check, + Require_Results, + No_Context, +} +Proc_Tags :: distinct bit_set[Proc_Tag; u32]; + +Proc_Inlining :: enum u32 { + None = 0, + Inline = 1, + No_Inline = 2, +} + +Proc_Calling_Convention :: enum i32 { + Invalid = 0, + Odin, + Contextless, + C_Decl, + Std_Call, + Fast_Call, + + Foreign_Block_Default = -1, +} + + +Comment_Group :: struct { + list: []token.Token, +} + +Node :: struct { + pos: token.Pos, + end: token.Pos, + derived: any, +} + + +Expr :: struct { + using expr_base: Node, +} +Stmt :: struct { + using stmt_base: Node, +} +Decl :: struct { + using decl_base: Stmt, +} + +// Expressions + +Bad_Expr :: struct { + using node: Expr, +} + +Ident :: struct { + using node: Expr, + name: string, +} + +Implicit :: struct { + using node: Expr, + tok: token.Token, +} + + +Undef :: struct { + using node: Expr, + tok: token.Kind, +} + +Basic_Lit :: struct { + using node: Expr, + tok: token.Token, +} + +Basic_Directive :: struct { + using node: Expr, + tok: token.Token, + name: string, +} + +Ellipsis :: struct { + using node: Expr, + tok: token.Kind, + expr: ^Expr, +} + +Proc_Lit :: struct { + using node: Expr, + type: ^Proc_Type, + body: ^Stmt, + tags: Proc_Tags, + inlining: Proc_Inlining, +} + +Comp_Lit :: struct { + using node: Expr, + type: ^Expr, + open: token.Pos, + elems: []^Expr, + close: token.Pos, +} + + +Tag_Expr :: struct { + using node: Expr, + op: token.Token, + name: string, + expr: ^Expr, +} + +Unary_Expr :: struct { + using node: Expr, + op: token.Token, + expr: ^Expr, +} + +Binary_Expr :: struct { + using node: Expr, + left: ^Expr, + op: token.Token, + right: ^Expr, +} + +Paren_Expr :: struct { + using node: Expr, + open: token.Pos, + expr: ^Expr, + close: token.Pos, +} + +Selector_Expr :: struct { + using node: Expr, + expr: ^Expr, + field: ^Ident, +} + + +Index_Expr :: struct { + using node: Expr, + expr: ^Expr, + open: token.Pos, + index: ^Expr, + close: token.Pos, +} + +Deref_Expr :: struct { + using node: Expr, + expr: ^Expr, + op: token.Token, +} + +Slice_Expr :: struct { + using node: Expr, + expr: ^Expr, + open: token.Pos, + low: ^Expr, + interval: token.Token, + high: ^Expr, + close: token.Pos, +} + +Call_Expr :: struct { + using node: Expr, + inlining: Proc_Inlining, + expr: ^Expr, + open: token.Pos, + args: []^Expr, + ellipsis: token.Token, + close: token.Pos, +} + +Field_Value :: struct { + using node: Expr, + field: ^Expr, + sep: token.Pos, + value: ^Expr, +} + +Ternary_Expr :: struct { + using node: Expr, + cond: ^Expr, + op1: token.Token, + x: ^Expr, + op2: token.Token, + y: ^Expr, +} + +Type_Assertion :: struct { + using node: Expr, + expr: ^Expr, + dot: token.Pos, + open: token.Pos, + type: ^Expr, + close: token.Pos, +} + +Type_Cast :: struct { + using node: Expr, + tok: token.Token, + open: token.Pos, + type: ^Expr, + close: token.Pos, + expr: ^Expr, +} + +Auto_Cast :: struct { + using node: Expr, + op: token.Token, + expr: ^Expr, +} + + + + +// Statements + +Bad_Stmt :: struct { + using node: Stmt, +} + +Empty_Stmt :: struct { + using node: Stmt, + semicolon: token.Pos, // Position of the following ';' +} + +Expr_Stmt :: struct { + using node: Stmt, + expr: ^Expr, +} + +Tag_Stmt :: struct { + using node: Stmt, + op: token.Token, + name: string, + stmt: ^Stmt, +} + +Assign_Stmt :: struct { + using node: Stmt, + lhs: []^Expr, + op: token.Token, + rhs: []^Expr, +} + + +Block_Stmt :: struct { + using node: Stmt, + label: ^Expr, + open: token.Pos, + stmts: []^Stmt, + close: token.Pos, +} + +If_Stmt :: struct { + using node: Stmt, + label: ^Expr, + if_pos: token.Pos, + init: ^Stmt, + cond: ^Expr, + body: ^Stmt, + else_stmt: ^Stmt, +} + +When_Stmt :: struct { + using node: Stmt, + when_pos: token.Pos, + cond: ^Expr, + body: ^Stmt, + else_stmt: ^Stmt, +} + +Return_Stmt :: struct { + using node: Stmt, + results: []^Expr, +} + +Defer_Stmt :: struct { + using node: Stmt, + stmt: ^Stmt, +} + +For_Stmt :: struct { + using node: Stmt, + label: ^Expr, + for_pos: token.Pos, + init: ^Stmt, + cond: ^Expr, + post: ^Stmt, + body: ^Stmt, +} + +Range_Stmt :: struct { + using node: Stmt, + label: ^Expr, + for_pos: token.Pos, + val0: ^Expr, + val1: ^Expr, + in_pos: token.Pos, + expr: ^Expr, + body: ^Stmt, +} + + +Case_Clause :: struct { + using node: Stmt, + case_pos: token.Pos, + list: []^Expr, + terminator: token.Token, + body: []^Stmt, +} + +Switch_Stmt :: struct { + using node: Stmt, + label: ^Expr, + switch_pos: token.Pos, + init: ^Stmt, + cond: ^Expr, + body: ^Stmt, + complete: bool, +} + +Type_Switch_Stmt :: struct { + using node: Stmt, + label: ^Expr, + switch_pos: token.Pos, + tag: ^Stmt, + expr: ^Expr, + body: ^Stmt, + complete: bool, +} + +Branch_Stmt :: struct { + using node: Stmt, + tok: token.Token, + label: ^Ident, +} + +Using_Stmt :: struct { + using node: Stmt, + list: []^Expr, +} + + +// Declarations + +Bad_Decl :: struct { + using node: Decl, +} + +Value_Decl :: struct { + using node: Decl, + docs: ^Comment_Group, + attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily + names: []^Expr, + type: ^Expr, + values: []^Expr, + comment: ^Comment_Group, + is_static: bool, + is_using: bool, + is_mutable: bool, +} + +Package_Decl :: struct { + using node: Decl, + docs: ^Comment_Group, + token: token.Token, + name: string, + comment: ^Comment_Group, +} + +Import_Decl :: struct { + using node: Decl, + docs: ^Comment_Group, + is_using: bool, + import_tok: token.Token, + name: token.Token, + relpath: token.Token, + fullpath: string, + comment: ^Comment_Group, +} + +Foreign_Block_Decl :: struct { + using node: Decl, + docs: ^Comment_Group, + attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily + tok: token.Token, + foreign_library: ^Expr, + body: ^Stmt, +} + +Foreign_Import_Decl :: struct { + using node: Decl, + docs: ^Comment_Group, + foreign_tok: token.Token, + import_tok: token.Token, + name: ^Ident, + collection_name: string, + fullpaths: []string, + comment: ^Comment_Group, +} + + + +// Other things +unparen_expr :: proc(expr: ^Expr) -> ^Expr { + if expr == nil { + return nil; + } + for { + e, ok := expr.derived.(Paren_Expr); + if !ok do break; + expr = e.expr; + } + return expr; +} + +Field_Flag :: enum { + Ellipsis, + Using, + No_Alias, + C_Vararg, + Auto_Cast, + In, + Results, + Default_Parameters, + Typeid_Token, +} + +Field_Flags :: distinct bit_set[Field_Flag]; + +Field_Flags_Struct :: Field_Flags{ + Field_Flag.Using, +}; +Field_Flags_Record_Poly_Params :: Field_Flags{ + Field_Flag.Typeid_Token, +}; +Field_Flags_Signature :: Field_Flags{ + Field_Flag.Ellipsis, + Field_Flag.Using, + Field_Flag.No_Alias, + Field_Flag.C_Vararg, + Field_Flag.Auto_Cast, + Field_Flag.Default_Parameters, +}; + +Field_Flags_Signature_Params :: Field_Flags_Signature | {Field_Flag.Typeid_Token}; +Field_Flags_Signature_Results :: Field_Flags_Signature; + + +Proc_Group :: struct { + using node: Expr, + tok: token.Token, + open: token.Pos, + args: []^Expr, + close: token.Pos, +} + +Attribute :: struct { + using node: Node, + tok: token.Kind, + open: token.Pos, + elems: []^Expr, + close: token.Pos, +} + +Field :: struct { + using node: Node, + docs: ^Comment_Group, + names: []^Expr, // Could be polymorphic + type: ^Expr, + default_value: ^Expr, + flags: Field_Flags, + comment: ^Comment_Group, +} + +Field_List :: struct { + using node: Node, + open: token.Pos, + list: []^Field, + close: token.Pos, +} + + +// Types +Typeid_Type :: struct { + using node: Expr, + tok: token.Kind, + specialization: ^Expr, +} + +Helper_Type :: struct { + using node: Expr, + tok: token.Kind, + type: ^Expr, +} + +Distinct_Type :: struct { + using node: Expr, + tok: token.Kind, + type: ^Expr, +} + +Opaque_Type :: struct { + using node: Expr, + tok: token.Kind, + type: ^Expr, +} + +Poly_Type :: struct { + using node: Expr, + dollar: token.Pos, + type: ^Ident, + specialization: ^Expr, +} + +Proc_Type :: struct { + using node: Expr, + tok: token.Token, + calling_convention: Proc_Calling_Convention, + params: ^Field_List, + arrow: token.Pos, + results: ^Field_List, + tags: Proc_Tags, + generic: bool, + diverging: bool, +} + +Pointer_Type :: struct { + using node: Expr, + pointer: token.Pos, + elem: ^Expr, +} + +Array_Type :: struct { + using node: Expr, + open: token.Pos, + len: ^Expr, // Ellipsis node for [?]T arrray types, nil for slice types + close: token.Pos, + elem: ^Expr, +} + +Dynamic_Array_Type :: struct { + using node: Expr, + open: token.Pos, + dynamic_pos: token.Pos, + close: token.Pos, + elem: ^Expr, +} + +Struct_Type :: struct { + using node: Expr, + tok_pos: token.Pos, + poly_params: ^Field_List, + align: ^Expr, + is_packed: bool, + is_raw_union: bool, + fields: ^Field_List, + name_count: int, +} + +Union_Type :: struct { + using node: Expr, + tok_pos: token.Pos, + poly_params: ^Field_List, + align: ^Expr, + variants: []^Expr, +} + +Enum_Type :: struct { + using node: Expr, + tok_pos: token.Pos, + base_type: ^Expr, + open: token.Pos, + fields: []^Expr, + close: token.Pos, + + is_using: bool, +} + +Bit_Field_Type :: struct { + using node: Expr, + tok_pos: token.Pos, + align: ^Expr, + open: token.Pos, + fields: []^Field_Value, // Field_Value with ':' rather than '=' + close: token.Pos, +} + +Bit_Set_Type :: struct { + using node: Expr, + tok_pos: token.Pos, + open: token.Pos, + elem: ^Expr, + underlying: ^Expr, + close: token.Pos, +} + +Map_Type :: struct { + using node: Expr, + tok_pos: token.Pos, + key: ^Expr, + value: ^Expr, +} diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin new file mode 100644 index 000000000..d04a627d7 --- /dev/null +++ b/core/odin/ast/clone.odin @@ -0,0 +1,313 @@ +package odin_ast + +import "core:mem" +import "core:fmt" +import "core:odin/token" + +new :: proc($T: typeid, pos, end: token.Pos) -> ^T { + n := mem.new(T); + n.pos = pos; + n.end = end; + n.derived = n^; + base: ^Node = n; // dummy check + return n; +} + +clone :: proc{ + clone_node, + clone_expr, + clone_stmt, + clone_decl, + clone_array, + clone_dynamic_array, +}; + +clone_array :: proc(array: $A/[]^$T) -> A { + if len(array) == 0 { + return nil; + } + res := make(A, len(array)); + for elem, i in array { + res[i] = auto_cast clone(elem); + } + return res; +} + +clone_dynamic_array :: proc(array: $A/[dynamic]^$T) -> A { + if len(array) == 0 { + return nil; + } + res := make(A, len(array)); + for elem, i in array { + res[i] = auto_cast clone(elem); + } + return res; +} + +clone_expr :: proc(node: ^Expr) -> ^Expr { + return cast(^Expr)clone_node(node); +} +clone_stmt :: proc(node: ^Stmt) -> ^Stmt { + return cast(^Stmt)clone_node(node); +} +clone_decl :: proc(node: ^Decl) -> ^Decl { + return cast(^Decl)clone_node(node); +} +clone_node :: proc(node: ^Node) -> ^Node { + if node == nil { + return nil; + } + + size := size_of(Node); + align := align_of(Node); + ti := type_info_of(node.derived.id); + if ti != nil { + size = ti.size; + align = ti.align; + } + + res := cast(^Node)mem.alloc(size, align); + src: rawptr = node; + if node.derived != nil { + src = node.derived.data; + } + mem.copy(res, src, size); + res.derived.data = rawptr(res); + + switch n in node.derived { + case Bad_Expr: + case Ident: + case Implicit: + case Undef: + case Basic_Lit: + + case Ellipsis: + r := cast(^Ellipsis)res; + r.expr = clone(r.expr); + case Proc_Lit: + r := cast(^Proc_Lit)res; + r.type = auto_cast clone(r.type); + r.body = clone(r.body); + case Comp_Lit: + r := cast(^Comp_Lit)res; + r.type = clone(r.type); + r.elems = clone(r.elems); + + case Tag_Expr: + r := cast(^Tag_Expr)res; + r.expr = clone(r.expr); + case Unary_Expr: + r := cast(^Unary_Expr)res; + r.expr = clone(r.expr); + case Binary_Expr: + r := cast(^Binary_Expr)res; + r.left = clone(r.left); + r.right = clone(r.right); + case Paren_Expr: + r := cast(^Paren_Expr)res; + r.expr = clone(r.expr); + case Selector_Expr: + r := cast(^Selector_Expr)res; + r.expr = clone(r.expr); + r.field = auto_cast clone(r.field); + case Index_Expr: + r := cast(^Index_Expr)res; + r.expr = clone(r.expr); + r.index = clone(r.index); + case Deref_Expr: + r := cast(^Deref_Expr)res; + r.expr = clone(r.expr); + case Slice_Expr: + r := cast(^Slice_Expr)res; + r.expr = clone(r.expr); + r.low = clone(r.low); + r.high = clone(r.high); + case Call_Expr: + r := cast(^Call_Expr)res; + r.expr = clone(r.expr); + r.args = clone(r.args); + case Field_Value: + r := cast(^Field_Value)res; + r.field = clone(r.field); + r.value = clone(r.value); + case Ternary_Expr: + r := cast(^Ternary_Expr)res; + r.cond = clone(r.cond); + r.x = clone(r.x); + r.y = clone(r.y); + case Type_Assertion: + r := cast(^Type_Assertion)res; + r.expr = clone(r.expr); + r.type = clone(r.type); + case Type_Cast: + r := cast(^Type_Cast)res; + r.type = clone(r.type); + r.expr = clone(r.expr); + case Auto_Cast: + r := cast(^Auto_Cast)res; + r.expr = clone(r.expr); + + case Bad_Stmt: + case Empty_Stmt: + case Expr_Stmt: + r := cast(^Expr_Stmt)res; + r.expr = clone(r.expr); + case Tag_Stmt: + r := cast(^Expr_Stmt)res; + r.expr = clone(r.expr); + + case Assign_Stmt: + r := cast(^Assign_Stmt)res; + r.lhs = clone(r.lhs); + r.rhs = clone(r.rhs); + case Block_Stmt: + r := cast(^Block_Stmt)res; + r.label = auto_cast clone(r.label); + r.stmts = clone(r.stmts); + case If_Stmt: + r := cast(^If_Stmt)res; + r.label = auto_cast clone(r.label); + r.init = clone(r.init); + r.cond = clone(r.cond); + r.body = clone(r.body); + r.else_stmt = clone(r.else_stmt); + case When_Stmt: + r := cast(^When_Stmt)res; + r.cond = clone(r.cond); + r.body = clone(r.body); + r.else_stmt = clone(r.else_stmt); + case Return_Stmt: + r := cast(^Return_Stmt)res; + r.results = clone(r.results); + case Defer_Stmt: + r := cast(^Defer_Stmt)res; + r.stmt = clone(r.stmt); + case For_Stmt: + r := cast(^For_Stmt)res; + r.label = auto_cast clone(r.label); + r.init = clone(r.init); + r.cond = clone(r.cond); + r.post = clone(r.post); + r.body = clone(r.body); + case Range_Stmt: + r := cast(^Range_Stmt)res; + r.label = auto_cast clone(r.label); + r.val0 = clone(r.val0); + r.val1 = clone(r.val1); + r.expr = clone(r.expr); + r.body = clone(r.body); + case Case_Clause: + r := cast(^Case_Clause)res; + r.list = clone(r.list); + r.body = clone(r.body); + case Switch_Stmt: + r := cast(^Switch_Stmt)res; + r.label = auto_cast clone(r.label); + r.init = clone(r.init); + r.cond = clone(r.cond); + r.body = clone(r.body); + case Type_Switch_Stmt: + r := cast(^Type_Switch_Stmt)res; + r.label = auto_cast clone(r.label); + r.tag = clone(r.tag); + r.expr = clone(r.expr); + r.body = clone(r.body); + case Branch_Stmt: + r := cast(^Branch_Stmt)res; + r.label = auto_cast clone(r.label); + case Using_Stmt: + r := cast(^Using_Stmt)res; + r.list = clone(r.list); + case Bad_Decl: + case Value_Decl: + r := cast(^Value_Decl)res; + r.attributes = clone(r.attributes); + r.names = clone(r.names); + r.type = clone(r.type); + r.values = clone(r.values); + case Package_Decl: + case Import_Decl: + case Foreign_Block_Decl: + r := cast(^Foreign_Block_Decl)res; + r.attributes = clone(r.attributes); + r.foreign_library = clone(r.foreign_library); + r.body = clone(r.body); + case Foreign_Import_Decl: + r := cast(^Foreign_Import_Decl)res; + r.name = auto_cast clone(r.name); + case Proc_Group: + r := cast(^Proc_Group)res; + r.args = clone(r.args); + case Attribute: + r := cast(^Attribute)res; + r.elems = clone(r.elems); + case Field: + r := cast(^Field)res; + r.names = clone(r.names); + r.type = clone(r.type); + r.default_value = clone(r.default_value); + case Field_List: + r := cast(^Field_List)res; + r.list = clone(r.list); + case Typeid_Type: + r := cast(^Typeid_Type)res; + r.specialization = clone(r.specialization); + case Helper_Type: + r := cast(^Helper_Type)res; + r.type = clone(r.type); + case Distinct_Type: + r := cast(^Distinct_Type)res; + r.type = clone(r.type); + case Opaque_Type: + r := cast(^Opaque_Type)res; + r.type = clone(r.type); + case Poly_Type: + r := cast(^Poly_Type)res; + r.type = auto_cast clone(r.type); + r.specialization = clone(r.specialization); + case Proc_Type: + r := cast(^Proc_Type)res; + r.params = auto_cast clone(r.params); + r.results = auto_cast clone(r.results); + case Pointer_Type: + r := cast(^Pointer_Type)res; + r.elem = clone(r.elem); + case Array_Type: + r := cast(^Array_Type)res; + r.len = clone(r.len); + r.elem = clone(r.elem); + case Dynamic_Array_Type: + r := cast(^Dynamic_Array_Type)res; + r.elem = clone(r.elem); + case Struct_Type: + r := cast(^Struct_Type)res; + r.poly_params = auto_cast clone(r.poly_params); + r.align = clone(r.align); + r.fields = auto_cast clone(r.fields); + case Union_Type: + r := cast(^Union_Type)res; + r.poly_params = auto_cast clone(r.poly_params); + r.align = clone(r.align); + r.variants = clone(r.variants); + case Enum_Type: + r := cast(^Enum_Type)res; + r.base_type = clone(r.base_type); + r.fields = clone(r.fields); + case Bit_Field_Type: + r := cast(^Bit_Field_Type)res; + r.fields = clone(r.fields); + case Bit_Set_Type: + r := cast(^Bit_Set_Type)res; + r.elem = clone(r.elem); + r.underlying = clone(r.underlying); + case Map_Type: + r := cast(^Map_Type)res; + r.key = clone(r.key); + r.value = clone(r.value); + + case: + fmt.panicf("Unhandled node kind: %T", n); + } + + return res; +} diff --git a/core/odin/ast/file.odin b/core/odin/ast/file.odin new file mode 100644 index 000000000..9d7550f45 --- /dev/null +++ b/core/odin/ast/file.odin @@ -0,0 +1,40 @@ +package odin_ast + +import "core:odin/token" + +Package_Kind :: enum { + Normal, + Runtime, + Init, +} + +Package :: struct { + kind: Package_Kind, + id: int, + name: string, + fullpath: string, + files: []^File, + + user_data: rawptr, +} + +File :: struct { + id: int, + pkg: ^Package, + + fullpath: string, + src: []byte, + + pkg_decl: ^Package_Decl, + pkg_token: token.Token, + pkg_name: string, + + decls: [dynamic]^Stmt, + imports: [dynamic]^Import_Decl, + directive_count: int, + + comments: [dynamic]^Comment_Group, + + syntax_warning_count: int, + syntax_error_count: int, +} diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin new file mode 100644 index 000000000..ddfdd8daf --- /dev/null +++ b/core/odin/parser/parser.odin @@ -0,0 +1,2776 @@ +package odin_parser + +import "core:odin/ast" +import "core:odin/token" +import "core:odin/tokenizer" + +import "core:strings" +import "core:fmt" + +Warning_Handler :: #type proc(pos: token.Pos, fmt: string, args: ..any); +Error_Handler :: #type proc(pos: token.Pos, fmt: string, args: ..any); + +Parser :: struct { + file: ^ast.File, + tok: tokenizer.Tokenizer, + + warn: Warning_Handler, + err: Error_Handler, + + prev_tok: token.Token, + curr_tok: token.Token, + + // >= 0: In Expression + // < 0: In Control Clause + // NOTE(bill): Used to prevent type literals in control clauses + expr_level: int, + allow_range: bool, // NOTE(bill): Ranges are only allowed in certain cases + allow_in_expr: bool, // NOTE(bill): in expression are only allowed in certain cases + in_foreign_block: bool, + allow_type: bool, + + lead_comment: ^ast.Comment_Group, + line_comment: ^ast.Comment_Group, + + curr_proc: ^ast.Node, + + error_count: int, +} + +Stmt_Allow_Flag :: enum { + In, + Label, +} +Stmt_Allow_Flags :: distinct bit_set[Stmt_Allow_Flag]; + + +Import_Decl_Kind :: enum { + Standard, + Using, +} + + + +default_warning_handler :: proc(pos: token.Pos, msg: string, args: ..any) { + fmt.printf_err("%s(%d:%d): Warning: ", pos.file, pos.line, pos.column); + fmt.printf_err(msg, ..args); + fmt.printf_err("\n"); +} +default_error_handler :: proc(pos: token.Pos, msg: string, args: ..any) { + fmt.printf_err("%s(%d:%d): ", pos.file, pos.line, pos.column); + fmt.printf_err(msg, ..args); + fmt.printf_err("\n"); +} + +warn :: proc(p: ^Parser, pos: token.Pos, msg: string, args: ..any) { + if p.warn != nil { + p.warn(pos, msg, ..args); + } + p.file.syntax_warning_count += 1; +} + +error :: proc(p: ^Parser, pos: token.Pos, msg: string, args: ..any) { + if p.err != nil { + p.err(pos, msg, ..args); + } + p.file.syntax_error_count += 1; + p.error_count += 1; +} + + +end_pos :: proc(tok: token.Token) -> token.Pos { + // TODO(bill): Correct this for multiline tokens (comments) + pos := tok.pos; + pos.offset += len(tok.text); + pos.column += len(tok.text); + return pos; +} + +parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { + zero_parser: { + p.prev_tok = {}; + p.curr_tok = {}; + p.expr_level = 0; + p.allow_range = false; + p.allow_in_expr = false; + p.in_foreign_block = false; + p.allow_type = false; + p.lead_comment = nil; + p.line_comment = nil; + } + + p.file = file; + tokenizer.init(&p.tok, file.src, file.fullpath); + if p.tok.ch <= 0 { + return true; + } + + + advance_token(p); + consume_comment_groups(p, p.prev_tok); + + docs := p.lead_comment; + + p.file.pkg_token = expect_token(p, token.Package); + if p.file.pkg_token.kind != token.Package { + return false; + } + + pkg_name := expect_token_after(p, token.Ident, "package"); + if pkg_name.kind == token.Ident { + if is_blank_ident(pkg_name) { + error(p, pkg_name.pos, "invalid package name '_'"); + } + // TODO(bill): Reserved package names + } + p.file.pkg_name = pkg_name.text; + + pd := ast.new(ast.Package_Decl, pkg_name.pos, end_pos(p.prev_tok)); + pd.docs = docs; + pd.token = p.file.pkg_token; + pd.name = pkg_name.text; + pd.comment = p.line_comment; + p.file.pkg_decl = pd; + + expect_semicolon(p, pd); + + if p.file.syntax_error_count > 0 { + return false; + } + + p.file.decls = make([dynamic]^ast.Stmt); + + for p.curr_tok.kind != token.EOF { + stmt := parse_stmt(p); + if stmt != nil { + if _, ok := stmt.derived.(ast.Empty_Stmt); !ok { + append(&p.file.decls, stmt); + if es, ok := stmt.derived.(ast.Expr_Stmt); ok && es.expr != nil { + if _, ok := es.expr.derived.(ast.Proc_Lit); ok { + error(p, stmt.pos, "procedure literal evaluated but not used"); + } + } + } + } + } + + return true; +} + + +next_token0 :: proc(p: ^Parser) -> bool { + p.curr_tok = tokenizer.scan(&p.tok); + if p.curr_tok.kind == token.EOF { + // error(p, p.curr_tok.pos, "token is EOF"); + return false; + } + return true; +} + +consume_comment :: proc(p: ^Parser) -> (tok: token.Token, end_line: int) { + tok = p.curr_tok; + assert(tok.kind == token.Comment); + end_line = tok.pos.line; + + if tok.text[1] == '*' { + for c in tok.text { + if c == '\n' { + end_line += 1; + } + } + } + + end := next_token0(p); + if p.curr_tok.pos.line > tok.pos.line { + end_line += 1; + } + + return; +} + +consume_comment_group :: proc(p: ^Parser, n: int) -> (comments: ^ast.Comment_Group, end_line: int) { + list: [dynamic]token.Token; + end_line = p.curr_tok.pos.line; + for p.curr_tok.kind == token.Comment && + p.curr_tok.pos.line <= end_line+n { + comment: token.Token; + comment, end_line = consume_comment(p); + append(&list, comment); + } + + if len(list) > 0 { + comments = new(ast.Comment_Group); + comments.list = list[:]; + append(&p.file.comments, comments); + } + + return; +} + +consume_comment_groups :: proc(p: ^Parser, prev: token.Token) { + if p.curr_tok.kind == token.Comment { + comment: ^ast.Comment_Group; + end_line := 0; + + if p.curr_tok.pos.line == prev.pos.line { + comment, end_line = consume_comment_group(p, 0); + if p.curr_tok.pos.line != end_line || p.curr_tok.kind == token.EOF { + p.line_comment = comment; + } + } + + end_line = -1; + for p.curr_tok.kind == token.Comment { + comment, end_line = consume_comment_group(p, 1); + } + if end_line+1 == p.curr_tok.pos.line || end_line < 0 { + p.lead_comment = comment; + } + + assert(p.curr_tok.kind != token.Comment); + } +} + +advance_token :: proc(p: ^Parser) -> token.Token { + p.lead_comment = nil; + p.line_comment = nil; + p.prev_tok = p.curr_tok; + prev := p.prev_tok; + + if next_token0(p) { + consume_comment_groups(p, prev); + } + return prev; +} + +expect_token :: proc(p: ^Parser, kind: token.Kind) -> token.Token { + prev := p.curr_tok; + if prev.kind != kind { + e := token.to_string(kind); + g := token.to_string(prev.kind); + error(p, prev.pos, "expected '%s', got '%s'", e, g); + if prev.kind == token.EOF { + // TODO(bill): Handle catastropic errors? + } + } + advance_token(p); + return prev; +} + +expect_token_after :: proc(p: ^Parser, kind: token.Kind, msg: string) -> token.Token { + prev := p.curr_tok; + if prev.kind != kind { + e := token.to_string(kind); + g := token.to_string(prev.kind); + error(p, prev.pos, "expected '%s' after %s, got '%s'", e, msg, g); + } + advance_token(p); + return prev; +} + +expect_operator :: proc(p: ^Parser) -> token.Token { + prev := p.curr_tok; + if !token.is_operator(prev.kind) { + g := token.to_string(prev.kind); + error(p, prev.pos, "expected an operator, got '%s'", g); + } + advance_token(p); + return prev; +} + +allow_token :: proc(p: ^Parser, kind: token.Kind) -> bool { + if p.curr_tok.kind == kind { + advance_token(p); + return true; + } + return false; +} + + +is_blank_ident :: proc{ + is_blank_ident_string, + is_blank_ident_token, + is_blank_ident_node, +}; +is_blank_ident_string :: inline proc(str: string) -> bool { + return str == "_"; +} +is_blank_ident_token :: inline proc(tok: token.Token) -> bool { + if tok.kind == token.Ident { + return is_blank_ident_string(tok.text); + } + return false; +} +is_blank_ident_node :: inline proc(node: ^ast.Node) -> bool { + if ident, ok := node.derived.(ast.Ident); ok { + return is_blank_ident(ident.name); + } + return true; +} + + +is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { + if node == nil { + return false; + } + switch n in node.derived { + case ast.Empty_Stmt, ast.Block_Stmt: + return true; + + case ast.If_Stmt, ast.When_Stmt, + ast.For_Stmt, ast.Range_Stmt, + ast.Switch_Stmt, ast.Type_Switch_Stmt: + return true; + + case ast.Helper_Type: + return is_semicolon_optional_for_node(p, n.type); + case ast.Distinct_Type: + return is_semicolon_optional_for_node(p, n.type); + case ast.Pointer_Type: + return is_semicolon_optional_for_node(p, n.elem); + case ast.Struct_Type, ast.Union_Type, ast.Enum_Type, ast.Bit_Field_Type: + return true; + case ast.Proc_Lit: + return true; + + case ast.Package_Decl, ast.Import_Decl, ast.Foreign_Import_Decl: + return true; + + case ast.Foreign_Block_Decl: + return is_semicolon_optional_for_node(p, n.body); + + case ast.Value_Decl: + if n.is_mutable { + return false; + } + if len(n.values) > 0 { + return is_semicolon_optional_for_node(p, n.values[len(n.values)-1]); + } + } + + return false; +} + + +expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool { + if allow_token(p, token.Semicolon) { + return true; + } + + prev := p.prev_tok; + if prev.kind == token.Semicolon { + return true; + } + + if p.curr_tok.kind == token.EOF { + return true; + } + + if node != nil { + if prev.pos.line != p.curr_tok.pos.line { + if is_semicolon_optional_for_node(p, node) { + return true; + } + } else { + switch p.curr_tok.kind { + case token.Close_Brace: + case token.Close_Paren: + case token.Else: + return true; + } + } + } + + error(p, prev.pos, "expected ';', got %s", token.to_string(prev.kind)); + return false; +} + +new_blank_ident :: proc(p: ^Parser, pos: token.Pos) -> ^ast.Ident { + tok: token.Token; + tok.pos = pos; + i := ast.new(ast.Ident, pos, end_pos(tok)); + i.name = "_"; + return i; +} + +parse_ident :: proc(p: ^Parser) -> ^ast.Ident { + tok := p.curr_tok; + pos := tok.pos; + name := "_"; + if tok.kind == token.Ident { + name = tok.text; + advance_token(p); + } else { + expect_token(p, token.Ident); + } + i := ast.new(ast.Ident, pos, end_pos(tok)); + i.name = name; + return i; +} + +parse_stmt_list :: proc(p: ^Parser) -> []^ast.Stmt { + list: [dynamic]^ast.Stmt; + for p.curr_tok.kind != token.Case && + p.curr_tok.kind != token.Close_Brace && + p.curr_tok.kind != token.EOF { + stmt := parse_stmt(p); + if stmt != nil { + if _, ok := stmt.derived.(ast.Empty_Stmt); !ok { + append(&list, stmt); + if es, ok := stmt.derived.(ast.Expr_Stmt); ok && es.expr != nil { + if _, ok := es.expr.derived.(ast.Proc_Lit); ok { + error(p, stmt.pos, "procedure literal evaluated but not used"); + } + } + } + } + } + return list[:]; +} + +parse_block_stmt :: proc(p: ^Parser, is_when: bool) -> ^ast.Stmt { + if !is_when && p.curr_proc == nil { + error(p, p.curr_tok.pos, "you cannot use a block statement in the file scope"); + } + return parse_body(p); +} + +parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt { + tok := expect_token(p, token.When); + + cond: ^ast.Expr; + body: ^ast.Stmt; + else_stmt: ^ast.Stmt; + + prev_level := p.expr_level; + p.expr_level = -1; + cond = parse_expr(p, false); + p.expr_level = prev_level; + + if cond == nil { + error(p, p.curr_tok.pos, "expected a condition for when statement"); + } + if allow_token(p, token.Do) { + body = convert_stmt_to_body(p, parse_stmt(p)); + } else { + body = parse_block_stmt(p, true); + } + + if allow_token(p, token.Else) { + switch p.curr_tok.kind { + case token.When: + else_stmt = parse_when_stmt(p); + case token.Open_Brace: + else_stmt = parse_block_stmt(p, true); + case token.Do: + expect_token(p, token.Do); + else_stmt = convert_stmt_to_body(p, parse_stmt(p)); + case: + error(p, p.curr_tok.pos, "expected when statement block statement"); + else_stmt = ast.new(ast.Bad_Stmt, p.curr_tok.pos, end_pos(p.curr_tok)); + } + } + + end := body.end; + if else_stmt != nil { + end = else_stmt.end; + } + when_stmt := ast.new(ast.When_Stmt, tok.pos, end); + when_stmt.when_pos = tok.pos; + when_stmt.cond = cond; + when_stmt.body = body; + when_stmt.else_stmt = else_stmt; + return when_stmt; +} + +convert_stmt_to_expr :: proc(p: ^Parser, stmt: ^ast.Stmt, kind: string) -> ^ast.Expr { + if stmt == nil { + return nil; + } + if es, ok := stmt.derived.(ast.Expr_Stmt); ok { + return es.expr; + } + error(p, stmt.pos, "expected %s, found a simple statement", kind); + return ast.new(ast.Bad_Expr, p.curr_tok.pos, end_pos(p.curr_tok)); +} + +parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { + tok := expect_token(p, token.If); + + init: ^ast.Stmt; + cond: ^ast.Expr; + body: ^ast.Stmt; + else_stmt: ^ast.Stmt; + + prev_level := p.expr_level; + p.expr_level = -1; + prev_allow_in_expr := p.allow_in_expr; + p.allow_in_expr = true; + if allow_token(p, token.Semicolon) { + cond = parse_expr(p, false); + } else { + init = parse_simple_stmt(p, nil); + if allow_token(p, token.Semicolon) { + cond = parse_expr(p, false); + } else { + cond = convert_stmt_to_expr(p, init, "boolean expression"); + init = nil; + } + } + + p.expr_level = prev_level; + p.allow_in_expr = prev_allow_in_expr; + + if cond == nil { + error(p, p.curr_tok.pos, "expected a condition for if statement"); + + } + if allow_token(p, token.Do) { + body = convert_stmt_to_body(p, parse_stmt(p)); + } else { + body = parse_block_stmt(p, false); + } + + if allow_token(p, token.Else) { + switch p.curr_tok.kind { + case token.If: + else_stmt = parse_when_stmt(p); + case token.Open_Brace: + else_stmt = parse_block_stmt(p, false); + case token.Do: + expect_token(p, token.Do); + else_stmt = convert_stmt_to_body(p, parse_stmt(p)); + case: + error(p, p.curr_tok.pos, "expected if statement block statement"); + else_stmt = ast.new(ast.Bad_Stmt, p.curr_tok.pos, end_pos(p.curr_tok)); + } + } + + end := body.end; + if else_stmt != nil { + end = else_stmt.end; + } + if_stmt := ast.new(ast.If_Stmt, tok.pos, end); + if_stmt.if_pos = tok.pos; + if_stmt.init = init; + if_stmt.cond = cond; + if_stmt.body = body; + if_stmt.else_stmt = else_stmt; + return if_stmt; +} + +parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { + if p.curr_proc == nil { + error(p, p.curr_tok.pos, "you cannot use a for statement in the file scope"); + } + + tok := expect_token(p, token.For); + + init: ^ast.Stmt; + cond: ^ast.Stmt; + post: ^ast.Stmt; + body: ^ast.Stmt; + is_range := false; + + if p.curr_tok.kind != token.Open_Brace && p.curr_tok.kind != token.Do { + prev_level := p.expr_level; + defer p.expr_level = prev_level; + p.expr_level = -1; + + if p.curr_tok.kind == token.In { + in_tok := expect_token(p, token.In); + rhs: ^ast.Expr; + + prev_allow_range := p.allow_range; + p.allow_range = true; + rhs = parse_expr(p, false); + p.allow_range = prev_allow_range; + + if allow_token(p, token.Do) { + body = convert_stmt_to_body(p, parse_stmt(p)); + } else { + body = parse_body(p); + } + + range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end); + range_stmt.for_pos = tok.pos; + range_stmt.in_pos = in_tok.pos; + range_stmt.expr = rhs; + range_stmt.body = body; + return range_stmt; + } + + if p.curr_tok.kind != token.Semicolon { + cond = parse_simple_stmt(p, {Stmt_Allow_Flag.In}); + if as, ok := cond.derived.(ast.Assign_Stmt); ok && as.op.kind == token.In { + is_range = true; + } + } + + if !is_range && allow_token(p, token.Semicolon) { + init = cond; + cond = nil; + if p.curr_tok.kind != token.Semicolon { + cond = parse_simple_stmt(p, nil); + } + expect_semicolon(p, cond); + if p.curr_tok.kind != token.Open_Brace && p.curr_tok.kind != token.Do { + post = parse_simple_stmt(p, nil); + } + } + } + + if allow_token(p, token.Do) { + body = convert_stmt_to_body(p, parse_stmt(p)); + } else { + body = parse_body(p); + } + + + if is_range { + assign_stmt := cond.derived.(ast.Assign_Stmt); + val0, val1: ^ast.Expr; + + switch len(assign_stmt.lhs) { + case 1: + val0 = assign_stmt.lhs[0]; + case 2: + val0 = assign_stmt.lhs[0]; + val1 = assign_stmt.lhs[1]; + case: + error(p, cond.pos, "expected either 1 or 2 identifiers"); + return ast.new(ast.Bad_Stmt, tok.pos, body.end); + } + + rhs: ^ast.Expr; + if len(assign_stmt.rhs) > 0 { + rhs = assign_stmt.rhs[0]; + } + + range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end); + range_stmt.for_pos = tok.pos; + range_stmt.val0 = val0; + range_stmt.val1 = val1; + range_stmt.in_pos = assign_stmt.op.pos; + range_stmt.expr = rhs; + range_stmt.body = body; + return range_stmt; + } + + cond_expr := convert_stmt_to_expr(p, cond, "boolean expression"); + for_stmt := ast.new(ast.For_Stmt, tok.pos, body.end); + for_stmt.for_pos = tok.pos; + for_stmt.init = init; + for_stmt.cond = cond_expr; + for_stmt.post = post; + for_stmt.body = body; + return for_stmt; +} + +parse_case_clause :: proc(p: ^Parser, is_type_switch: bool) -> ^ast.Case_Clause { + tok := expect_token(p, token.Case); + + list: []^ast.Expr; + + if p.curr_tok.kind != token.Colon { + prev_allow_range, prev_allow_in_expr := p.allow_range, p.allow_in_expr; + defer p.allow_range, p.allow_in_expr = prev_allow_range, prev_allow_in_expr; + p.allow_range, p.allow_in_expr = !is_type_switch, !is_type_switch; + + list = parse_rhs_expr_list(p); + } + + terminator := expect_token(p, token.Colon); + + stmts := parse_stmt_list(p); + + cc := ast.new(ast.Case_Clause, tok.pos, end_pos(p.prev_tok)); + cc.list = list; + cc.terminator = terminator; + cc.body = stmts; + return cc; +} + +parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt { + tok := expect_token(p, token.Switch); + + init: ^ast.Stmt; + tag: ^ast.Stmt; + is_type_switch := false; + clauses: [dynamic]^ast.Stmt; + + if p.curr_tok.kind != token.Open_Brace { + prev_level := p.expr_level; + defer p.expr_level = prev_level; + p.expr_level = -1; + + if p.curr_tok.kind == token.In { + in_tok := expect_token(p, token.In); + is_type_switch = true; + + lhs := make([]^ast.Expr, 1); + rhs := make([]^ast.Expr, 1); + lhs[0] = new_blank_ident(p, tok.pos); + rhs[0] = parse_expr(p, true); + + as := ast.new(ast.Assign_Stmt, tok.pos, rhs[0].end); + as.lhs = lhs; + as.op = in_tok; + as.rhs = rhs; + tag = as; + } else { + tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In}); + if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == token.In { + is_type_switch = true; + } else if allow_token(p, token.Semicolon) { + init = tag; + tag = nil; + if p.curr_tok.kind != token.Open_Brace { + tag = parse_simple_stmt(p, nil); + } + } + } + } + + + open := expect_token(p, token.Open_Brace); + + for p.curr_tok.kind == token.Case { + clause := parse_case_clause(p, is_type_switch); + append(&clauses, clause); + } + + close := expect_token(p, token.Close_Brace); + + body := ast.new(ast.Block_Stmt, open.pos, end_pos(close)); + body.stmts = clauses[:]; + + if is_type_switch { + ts := ast.new(ast.Type_Switch_Stmt, tok.pos, body.end); + ts.tag = tag; + ts.body = body; + return ts; + } else { + cond := convert_stmt_to_expr(p, tag, "switch expression"); + ts := ast.new(ast.Switch_Stmt, tok.pos, body.end); + ts.init = init; + ts.cond = cond; + ts.body = body; + return ts; + } +} + +parse_attribute :: proc(p: ^Parser, tok: token.Token, open_kind, close_kind: token.Kind) -> ^ast.Stmt { + elems: [dynamic]^ast.Expr; + + open := expect_token(p, open_kind); + p.expr_level += 1; + for p.curr_tok.kind != close_kind && + p.curr_tok.kind != token.EOF { + elem: ^ast.Expr; + elem = parse_ident(p); + if p.curr_tok.kind == token.Eq { + eq := expect_token(p, token.Eq); + value := parse_value(p); + fv := ast.new(ast.Field_Value, elem.pos, value.end); + fv.field = elem; + fv.sep = eq.pos; + fv.value = value; + + elem = fv; + } + append(&elems, elem); + + if !allow_token(p, token.Comma) { + break; + } + } + + p.expr_level -= 1; + close := expect_token_after(p, close_kind, "attribute"); + + attribute := ast.new(ast.Attribute, tok.pos, end_pos(close)); + attribute.tok = tok.kind; + attribute.open = open.pos; + attribute.elems = elems[:]; + attribute.close = close.pos; + + decl := parse_stmt(p); + switch d in &decl.derived { + case ast.Value_Decl: + append(&d.attributes, attribute); + case ast.Foreign_Block_Decl: + append(&d.attributes, attribute); + case: + error(p, decl.pos, "expected a value or foreign declaration after an attribute"); + free(attribute); + delete(elems); + } + return decl; + +} + +parse_foreign_block_decl :: proc(p: ^Parser) -> ^ast.Stmt { + decl := parse_stmt(p); + switch in decl.derived { + case ast.Empty_Stmt, ast.Bad_Stmt, ast.Bad_Decl: + // Ignore + return nil; + case ast.When_Stmt, ast.Value_Decl: + return decl; + } + + error(p, decl.pos, "foreign blocks only allow procedure and variable declarations"); + + return nil; + +} + +parse_foreign_block :: proc(p: ^Parser, tok: token.Token) -> ^ast.Foreign_Block_Decl { + docs := p.lead_comment; + + foreign_library: ^ast.Expr; + switch p.curr_tok.kind { + case token.Export: + i := ast.new(ast.Implicit, tok.pos, end_pos(tok)); + i.tok = expect_token(p, token.Export); + foreign_library = i; + case token.Open_Brace: + i := ast.new(ast.Ident, tok.pos, end_pos(tok)); + i.name = "_"; + foreign_library = i; + case: + foreign_library = parse_ident(p); + } + + decls: [dynamic]^ast.Stmt; + + prev_in_foreign_block := p.in_foreign_block; + defer p.in_foreign_block = prev_in_foreign_block; + p.in_foreign_block = true; + + open := expect_token(p, token.Open_Brace); + for p.curr_tok.kind != token.Close_Brace && p.curr_tok.kind != token.EOF { + decl := parse_foreign_block_decl(p); + if decl != nil { + append(&decls, decl); + } + } + close := expect_token(p, token.Close_Brace); + + body := ast.new(ast.Block_Stmt, open.pos, end_pos(close)); + body.open = open.pos; + body.stmts = decls[:]; + body.close = close.pos; + + decl := ast.new(ast.Foreign_Block_Decl, tok.pos, body.end); + decl.docs = docs; + decl.tok = tok; + decl.foreign_library = foreign_library; + decl.body = body; + return decl; +} + + +parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl { + docs := p.lead_comment; + tok := expect_token(p, token.Foreign); + + switch p.curr_tok.kind { + case token.Export, token.Ident, token.Open_Brace: + return parse_foreign_block(p, tok); + + case token.Import: + import_tok := expect_token(p, token.Import); + name: ^ast.Ident; + if p.curr_tok.kind == token.Ident { + name = parse_ident(p); + } + + if name != nil && is_blank_ident(name) { + error(p, name.pos, "illegal foreign import name: '_'"); + } + + fullpaths: [dynamic]string; + if allow_token(p, token.Open_Brace) { + for p.curr_tok.kind != token.Close_Brace && + p.curr_tok.kind != token.EOF { + path := expect_token(p, token.String); + append(&fullpaths, path.text); + + if !allow_token(p, token.Comma) { + break; + } + } + expect_token(p, token.Close_Brace); + } else { + path := expect_token(p, token.String); + reserve(&fullpaths, 1); + append(&fullpaths, path.text); + } + + if len(fullpaths) == 0 { + error(p, import_tok.pos, "foreign import without any paths"); + } + + decl := ast.new(ast.Foreign_Import_Decl, tok.pos, end_pos(p.prev_tok)); + decl.docs = docs; + decl.foreign_tok = tok; + decl.import_tok = import_tok; + decl.name = name; + decl.fullpaths = fullpaths[:]; + expect_semicolon(p, decl); + decl.comment = p.line_comment; + return decl; + } + + error(p, tok.pos, "invalid foreign declaration"); + return ast.new(ast.Bad_Decl, tok.pos, end_pos(tok)); +} + + +parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { + tok := p.curr_tok; + switch tok.kind { + // Operands + case token.Context, // Also allows for 'context = ' + token.Inline, token.No_Inline, + token.Ident, + token.Integer, token.Float, token.Imag, + token.Rune, token.String, + token.Open_Paren, + token.Pointer, + // Unary Expressions + token.Add, token.Sub, token.Xor, token.Not, token.And: + s := parse_simple_stmt(p, {Stmt_Allow_Flag.Label}); + expect_semicolon(p, s); + return s; + + + case token.Import: return parse_import_decl(p); + case token.Foreign: return parse_foreign_decl(p); + case token.If: return parse_if_stmt(p); + case token.When: return parse_when_stmt(p); + case token.For: return parse_for_stmt(p); + case token.Switch: return parse_switch_stmt(p); + + case token.Defer: + tok := advance_token(p); + stmt := parse_stmt(p); + switch s in stmt.derived { + case ast.Empty_Stmt: + error(p, s.pos, "empty statement after defer (e.g. ';')"); + case ast.Defer_Stmt: + error(p, s.pos, "you cannot defer a defer statement"); + stmt = s.stmt; + case ast.Return_Stmt: + error(p, s.pos, "you cannot defer a return statement"); + } + ds := ast.new(ast.Defer_Stmt, tok.pos, stmt.end); + ds.stmt = stmt; + return ds; + + case token.Return: + tok := advance_token(p); + + if p.expr_level > 0 { + error(p, tok.pos, "you cannot use a return statement within an expression"); + } + + results: [dynamic]^ast.Expr; + for p.curr_tok.kind != token.Semicolon { + result := parse_expr(p, false); + append(&results, result); + if p.curr_tok.kind != token.Comma || + p.curr_tok.kind == token.EOF { + break; + } + advance_token(p); + } + + end := end_pos(tok); + if len(results) > 0 { + end = results[len(results)-1].pos; + } + + rs := ast.new(ast.Return_Stmt, tok.pos, end); + rs.results = results[:]; + return rs; + + case token.Break, token.Continue, token.Fallthrough: + tok := advance_token(p); + label: ^ast.Expr; + if tok.kind != token.Fallthrough && p.curr_tok.kind == token.Ident { + label = parse_ident(p); + } + end := label != nil ? label.end : end_pos(tok); + s := ast.new(ast.Branch_Stmt, tok.pos, end); + expect_semicolon(p, s); + return s; + + case token.Static: + docs := p.lead_comment; + tok := expect_token(p, token.Static); + + list := parse_lhs_expr_list(p); + if len(list) == 0 { + error(p, tok.pos, "illegal use of 'static' statement"); + expect_semicolon(p, nil); + return ast.new(ast.Bad_Stmt, tok.pos, end_pos(p.prev_tok)); + } + + expect_token_after(p, token.Colon, "identifier list"); + decl := parse_value_decl(p, list, docs); + if decl != nil do switch d in &decl.derived { + case ast.Value_Decl: + if d.is_mutable { + d.is_static = true; + } else { + error(p, tok.pos, "'static' may only be currently used with variable declarations"); + } + case: + error(p, tok.pos, "illegal use of 'static' statement"); + } + + error(p, tok.pos, "illegal use of 'static' statement"); + if decl != nil { + return decl; + } + + return ast.new(ast.Bad_Stmt, tok.pos, end_pos(p.prev_tok)); + + case token.Using: + docs := p.lead_comment; + tok := expect_token(p, token.Using); + + if p.curr_tok.kind == token.Import { + return parse_import_decl(p, Import_Decl_Kind.Using); + } + + list := parse_lhs_expr_list(p); + if len(list) == 0 { + error(p, tok.pos, "illegal use of 'using' statement"); + expect_semicolon(p, nil); + return ast.new(ast.Bad_Stmt, tok.pos, end_pos(p.prev_tok)); + } + + if p.curr_tok.kind != token.Colon { + end := list[len(list)-1]; + expect_semicolon(p, end); + us := ast.new(ast.Using_Stmt, tok.pos, end.end); + us.list = list; + return us; + } + expect_token_after(p, token.Colon, "identifier list"); + decl := parse_value_decl(p, list, docs); + if decl != nil do switch d in &decl.derived { + case ast.Value_Decl: + d.is_using = true; + return decl; + } + + error(p, tok.pos, "illegal use of 'using' statement"); + return ast.new(ast.Bad_Stmt, tok.pos, end_pos(p.prev_tok)); + + case token.At: + tok := advance_token(p); + return parse_attribute(p, tok, token.Open_Paren, token.Close_Paren); + + case token.Hash: + tok := expect_token(p, token.Hash); + tag := expect_token(p, token.Ident); + name := tag.text; + + switch name { + case "bounds_check", "no_bounds_check": + // TODO(bill): Handle stmt state flags + return parse_stmt(p); + case "complete": + stmt := parse_stmt(p); + switch s in &stmt.derived { + case ast.Switch_Stmt: s.complete = true; + case ast.Type_Switch_Stmt: s.complete = true; + case: error(p, stmt.pos, "#complete can only be applied to a swtich statement"); + } + return stmt; + case "assert": + bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(tag)); + bd.tok = tok; + bd.name = name; + ce := parse_call_expr(p, bd); + es := ast.new(ast.Expr_Stmt, ce.pos, ce.end); + es.expr = ce; + return es; + case "include": + error(p, tag.pos, "#include is not a valid import declaration kind. Did you meant 'import'?"); + return ast.new(ast.Bad_Stmt, tok.pos, end_pos(tag)); + case: + stmt := parse_stmt(p); + te := ast.new(ast.Tag_Stmt, tok.pos, stmt.pos); + te.op = tok; + te.name = name; + te.stmt = stmt; + return te; + } + case token.Open_Brace: + return parse_block_stmt(p, false); + + case token.Semicolon: + s := ast.new(ast.Empty_Stmt, tok.pos, end_pos(tok)); + advance_token(p); + return s; + } + + advance_token(p); + error(p, tok.pos, "expected a statement, got %s", token.to_string(tok.kind)); + s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok)); + return s; +} + + +token_precedence :: proc(p: ^Parser, kind: token.Kind) -> int { + switch kind { + case token.Question: + return 1; + case token.Ellipsis: + if !p.allow_range { + return 0; + } + return 2; + case token.Cmp_Or: + return 3; + case token.Cmp_And: + return 4; + case token.Cmp_Eq, token.Not_Eq, + token.Lt, token.Gt, + token.Lt_Eq, token.Gt_Eq: + return 5; + case token.In, token.Notin: + if p.expr_level >= 0 || p.allow_in_expr { + return 6; + } + return 0; + case token.Add, token.Sub, token.Or, token.Xor: + return 7; + case token.Mul, token.Quo, + token.Mod, token.Mod_Mod, + token.And, token.And_Not, + token.Shl, token.Shr: + return 8; + } + return 0; +} + +parse_type_or_ident :: proc(p: ^Parser) -> ^ast.Expr { + prev_allow_type := p.allow_type; + prev_expr_level := p.expr_level; + defer { + p.allow_type = prev_allow_type; + p.expr_level = prev_expr_level; + } + + p.allow_type = true; + p.expr_level = -1; + + lhs := true; + return parse_atom_expr(p, parse_operand(p, lhs), lhs); +} +parse_type :: proc(p: ^Parser) -> ^ast.Expr { + type := parse_type_or_ident(p); + if type == nil { + error(p, p.curr_tok.pos, "expected a type"); + return ast.new(ast.Bad_Expr, p.curr_tok.pos, end_pos(p.curr_tok)); + } + return type; +} + +parse_body :: proc(p: ^Parser) -> ^ast.Block_Stmt { + prev_expr_level := p.expr_level; + defer p.expr_level = prev_expr_level; + + p.expr_level = 0; + open := expect_token(p, token.Open_Brace); + stmts := parse_stmt_list(p); + close := expect_token(p, token.Close_Brace); + + bs := ast.new(ast.Block_Stmt, open.pos, end_pos(close)); + bs.open = open.pos; + bs.stmts = stmts; + bs.close = close.pos; + return bs; +} + +convert_stmt_to_body :: proc(p: ^Parser, stmt: ^ast.Stmt) -> ^ast.Stmt { + switch s in stmt.derived { + case ast.Block_Stmt: + error(p, stmt.pos, "expected a normal statement rather than a block statement"); + return stmt; + case ast.Empty_Stmt: + error(p, stmt.pos, "expected a non-empty statement"); + } + + bs := ast.new(ast.Block_Stmt, stmt.pos, stmt.end); + bs.open = stmt.pos; + bs.stmts = make([]^ast.Stmt, 1); + bs.stmts[0] = stmt; + bs.close = stmt.end; + return bs; +} + +new_ast_field :: proc(names: []^ast.Expr, type: ^ast.Expr, default_value: ^ast.Expr) -> ^ast.Field { + pos, end: token.Pos; + + if len(names) > 0 { + pos = names[0].pos; + if default_value != nil { + end = default_value.end; + } else if type != nil { + end = type.end; + } else { + end = names[len(names)-1].pos; + } + } else { + if type != nil { + pos = type.pos; + } else if default_value != nil { + pos = default_value.pos; + } + + if default_value != nil { + end = default_value.end; + } else if type != nil { + end = type.end; + } + } + + field := ast.new(ast.Field, pos, end); + field.names = names; + field.type = type; + field.default_value = default_value; + return field; +} + + +Field_Prefix :: enum { + Invalid, + Unknown, + + Using, + No_Alias, + C_Vararg, + In, + Auto_Cast, +} + +Field_Prefixes :: distinct bit_set[Field_Prefix]; + +Expr_And_Flags :: struct { + expr: ^ast.Expr, + flags: ast.Field_Flags, +} + +convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags, allow_poly_names: bool) -> []^ast.Expr { + idents := make([dynamic]^ast.Expr, 0, len(list)); + + for ident, i in list { + if !ignore_flags { + if i != 0 { + error(p, ident.expr.pos, "illegal use of prefixes in parameter list"); + } + } + + id: ^ast.Expr = ident.expr; + + switch n in ident.expr.derived { + case ast.Ident: + case ast.Bad_Expr: + case ast.Poly_Type: + if allow_poly_names { + if n.specialization == nil { + break; + } else { + error(p, ident.expr.pos, "expected a polymorphic identifier without an specialization"); + } + } else { + error(p, ident.expr.pos, "expected a non-polymorphic identifier"); + } + case: + error(p, ident.expr.pos, "expected an identifier"); + id = ast.new(ast.Ident, ident.expr.pos, ident.expr.end); + } + + append(&idents, id); + } + + return idents[:]; +} + +is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix { + using Field_Prefix; + switch p.curr_tok.kind { + case token.EOF: + return Invalid; + case token.Using: + advance_token(p); + return Using; + case token.In: + advance_token(p); + return In; + case token.Auto_Cast: + advance_token(p); + return Auto_Cast; + case token.Hash: + advance_token(p); + defer advance_token(p); + switch p.curr_tok.kind { + case token.Ident: + switch p.curr_tok.text { + case "no_alias": + return No_Alias; + case "c_vararg": + return C_Vararg; + } + } + return Unknown; + } + return Invalid; +} + +parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags { + counts: [len(Field_Prefix)]int; + + for { + kind := is_token_field_prefix(p); + if kind == Field_Prefix.Invalid { + break; + } + + if kind == Field_Prefix.Unknown { + error(p, p.curr_tok.pos, "unknown prefix kind '#%s'", p.curr_tok.text); + continue; + } + + counts[kind] += 1; + } + + flags: ast.Field_Flags; + + for kind in Field_Prefix { + count := counts[kind]; + using Field_Prefix; + #complete switch kind { + case Invalid, Unknown: // Ignore + case Using: + if count > 1 do error(p, p.curr_tok.pos, "multiple 'using' in this field list"); + if count > 0 do flags |= {ast.Field_Flag.Using}; + case No_Alias: + if count > 1 do error(p, p.curr_tok.pos, "multiple '#no_alias' in this field list"); + if count > 0 do flags |= {ast.Field_Flag.No_Alias}; + case C_Vararg: + if count > 1 do error(p, p.curr_tok.pos, "multiple '#c_vararg' in this field list"); + if count > 0 do flags |= {ast.Field_Flag.C_Vararg}; + case In: + if count > 1 do error(p, p.curr_tok.pos, "multiple 'in' in this field list"); + if count > 0 do flags |= {ast.Field_Flag.In}; + case Auto_Cast: + if count > 1 do error(p, p.curr_tok.pos, "multiple 'auto_cast' in this field list"); + if count > 0 do flags |= {ast.Field_Flag.Auto_Cast}; + } + } + + return flags; +} + +check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, set_flags: ast.Field_Flags) -> ast.Field_Flags { + if name_count > 1 && ast.Field_Flag.Using in set_flags { + error(p, p.curr_tok.pos, "cannot apply 'using' to more than one of the same type"); + set_flags &~= {ast.Field_Flag.Using}; + } + + for flag in ast.Field_Flag { + if flag notin allowed_flags && flag in set_flags { + using ast.Field_Flag; + #complete switch flag { + case Using: + error(p, p.curr_tok.pos, "'using' is not allowed within this field list"); + case No_Alias: + error(p, p.curr_tok.pos, "'#no_alias' is not allowed within this field list"); + case C_Vararg: + error(p, p.curr_tok.pos, "'#c_vararg' is not allowed within this field list"); + case Auto_Cast: + error(p, p.curr_tok.pos, "'auto_cast' is not allowed within this field list"); + case In: + error(p, p.curr_tok.pos, "'in' is not allowed within this field list"); + case Ellipsis, Results, Default_Parameters, Typeid_Token: + panic("Impossible prefixes"); + } + set_flags &~= {flag}; + } + } + + if ast.Field_Flag.Using in allowed_flags && ast.Field_Flag.Using in set_flags { + set_flags &~= {ast.Field_Flag.Using}; + } + + return set_flags; +} + +parse_var_type :: proc(p: ^Parser, flags: ast.Field_Flags) -> ^ast.Expr { + if ast.Field_Flag.Ellipsis in flags && p.curr_tok.kind == token.Ellipsis { + tok := advance_token(p); + type := parse_type_or_ident(p); + if type == nil { + error(p, tok.pos, "variadic field missing type after '..'"); + type = ast.new(ast.Bad_Expr, tok.pos, end_pos(tok)); + } + e := ast.new(ast.Ellipsis, type.pos, type.end); + e.expr = type; + return e; + } + type: ^ast.Expr; + if ast.Field_Flag.Typeid_Token in flags && p.curr_tok.kind == token.Typeid { + tok := expect_token(p, token.Typeid); + specialization: ^ast.Expr; + end := tok.pos; + if allow_token(p, token.Quo) { + specialization = parse_type(p); + end = specialization.end; + } + + ti := ast.new(ast.Typeid_Type, tok.pos, end); + ti.tok = tok.kind; + ti.specialization = specialization; + type = ti; + } else { + type = parse_type(p); + } + + return type; +} + +check_procedure_name_list :: proc(p: ^Parser, names: []^ast.Expr) -> bool { + if len(names) == 0 { + return false; + } + + _, first_is_polymorphic := names[0].derived.(ast.Poly_Type); + any_polymorphic_names := first_is_polymorphic; + + for i := 1; i < len(names); i += 1 { + name := names[i]; + + if first_is_polymorphic { + if _, ok := name.derived.(ast.Poly_Type); ok { + any_polymorphic_names = true; + } else { + error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers"); + return any_polymorphic_names; + } + } else { + if _, ok := name.derived.(ast.Poly_Type); ok { + any_polymorphic_names = true; + error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers"); + return any_polymorphic_names; + } else { + // Okay + } + } + } + + return any_polymorphic_names; +} + +parse_ident_list :: proc(p: ^Parser, allow_poly_names: bool) -> []^ast.Expr { + list: [dynamic]^ast.Expr; + + for { + if allow_poly_names && p.curr_tok.kind == token.Dollar { + tok := expect_token(p, token.Dollar); + ident := parse_ident(p); + poly_name := ast.new(ast.Poly_Type, tok.pos, ident.end); + poly_name.type = ident; + append(&list, poly_name); + } else { + ident := parse_ident(p); + append(&list, ident); + } + if p.curr_tok.kind != token.Comma || + p.curr_tok.kind == token.EOF { + break; + } + advance_token(p); + } + + return list[:]; +} + + + +parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Field_Flags) -> (field_list: ^ast.Field_List, total_name_count: int) { + handle_field :: proc(p: ^Parser, + seen_ellipsis: ^bool, fields: ^[dynamic]^ast.Field, + docs: ^ast.Comment_Group, + names: []^ast.Expr, + allowed_flags, set_flags: ast.Field_Flags + ) -> bool { + + expect_field_separator :: proc(p: ^Parser, param: ^ast.Expr) -> bool { + tok := p.curr_tok; + if allow_token(p, token.Comma) { + return true; + } + if allow_token(p, token.Semicolon) { + error(p, tok.pos, "expected a comma, got a semicolon"); + return true; + } + return false; + } + is_type_ellipsis :: proc(type: ^ast.Expr) -> bool { + if type == nil do return false; + _, ok := type.derived.(ast.Ellipsis); + return ok; + } + + any_polymorphic_names := check_procedure_name_list(p, names); + set_flags = check_field_flag_prefixes(p, len(names), allowed_flags, set_flags); + + type: ^ast.Expr; + default_value: ^ast.Expr; + + expect_token_after(p, token.Colon, "field list"); + if p.curr_tok.kind != token.Eq { + type = parse_var_type(p, allowed_flags); + tt := ast.unparen_expr(type); + if !any_polymorphic_names { + if ti, ok := type.derived.(ast.Typeid_Type); ok && ti.specialization != nil { + error(p, type.pos, "specialization of typeid is not allowed without polymorphic names"); + } + } + } + + if allow_token(p, token.Eq) { + default_value = parse_expr(p, false); + if ast.Field_Flag.Default_Parameters notin allowed_flags { + error(p, p.curr_tok.pos, "default parameters are only allowed for procedures"); + default_value = nil; + } + } + + if default_value != nil && len(names) > 1 { + error(p, p.curr_tok.pos, "default parameters can only be applied to single values"); + } + + if allowed_flags == ast.Field_Flags_Struct && default_value != nil { + error(p, default_value.pos, "default parameters are not allowed for structs"); + default_value = nil; + } + + if is_type_ellipsis(type) { + if seen_ellipsis^ do error(p, type.pos, "extra variadic parameter after ellipsis"); + seen_ellipsis^ = true; + if len(names) != 1 { + error(p, type.pos, "variadic parameters can only have one field name"); + } + } else if seen_ellipsis^ && default_value == nil { + error(p, p.curr_tok.pos, "extra parameter after ellipsis without a default value"); + } + + ok := expect_field_separator(p, type); + + field := new_ast_field(names, type, default_value); + field.docs = docs; + field.flags = set_flags; + field.comment = p.line_comment; + append(fields, field); + + return ok; + } + + + start_tok := p.curr_tok; + + docs := p.lead_comment; + + fields: [dynamic]^ast.Field; + + list: [dynamic]Expr_And_Flags; + defer delete(list); + + seen_ellipsis := false; + + allow_typeid_token := ast.Field_Flag.Typeid_Token in allowed_flags; + allow_poly_names := allow_typeid_token; + + for p.curr_tok.kind != follow && + p.curr_tok.kind != token.Colon && + p.curr_tok.kind != token.EOF { + prefix_flags := parse_field_prefixes(p); + param := parse_var_type(p, allowed_flags & {ast.Field_Flag.Typeid_Token, ast.Field_Flag.Ellipsis}); + if _, ok := param.derived.(ast.Ellipsis); ok { + if seen_ellipsis { + error(p, param.pos, "extra variadic parameter after ellipsis"); + } + seen_ellipsis = true; + } else if seen_ellipsis { + error(p, param.pos, "extra parameter after ellipsis"); + } + + eaf := Expr_And_Flags{param, prefix_flags}; + append(&list, eaf); + if !allow_token(p, token.Comma) { + break; + } + } + + if p.curr_tok.kind != token.Colon { + for eaf in list { + type := eaf.expr; + tok: token.Token; + tok.pos = type.pos; + if ast.Field_Flag.Results notin allowed_flags { + tok.text = "_"; + } + + names := make([]^ast.Expr, 1); + names[0] = ast.new(ast.Ident, tok.pos, end_pos(tok)); + names[0].derived.(ast.Ident).name = tok.text; + + flags := check_field_flag_prefixes(p, len(list), allowed_flags, eaf.flags); + + field := new_ast_field(names, type, nil); + field.docs = docs; + field.flags = flags; + field.comment = p.line_comment; + append(&fields, field); + } + } else { + names := convert_to_ident_list(p, list[:], true, allow_poly_names); + if len(names) == 0 { + error(p, p.curr_tok.pos, "empty field declaration"); + } + + set_flags: ast.Field_Flags; + if len(list) > 0 { + set_flags = list[0].flags; + } + total_name_count += len(names); + handle_field(p, &seen_ellipsis, &fields, docs, names, allowed_flags, set_flags); + + for p.curr_tok.kind != follow && p.curr_tok.kind != token.EOF { + docs := p.lead_comment; + set_flags := parse_field_prefixes(p); + names := parse_ident_list(p, allow_poly_names); + + total_name_count += len(names); + ok := handle_field(p, &seen_ellipsis, &fields, docs, names, allowed_flags, set_flags); + if !ok { + break; + } + } + } + + field_list = ast.new(ast.Field_List, start_tok.pos, p.curr_tok.pos); + field_list.list = fields[:]; + return; +} + + +parse_results :: proc(p: ^Parser) -> (list: ^ast.Field_List, diverging: bool) { + if !allow_token(p, token.Arrow_Right) { + return; + } + + if allow_token(p, token.Not) { + diverging = true; + return; + } + + prev_level := p.expr_level; + defer p.expr_level = prev_level; + + if p.curr_tok.kind != token.Open_Paren { + type := parse_type(p); + field := new_ast_field(nil, type, nil); + + list = ast.new(ast.Field_List, field.pos, field.end); + list.list = make([]^ast.Field, 1); + list.list[0] = field; + return; + } + + expect_token(p, token.Open_Paren); + list, _ = parse_field_list(p, token.Close_Paren, ast.Field_Flags_Signature_Results); + expect_token_after(p, token.Close_Paren, "parameter list"); + return; +} + + +string_to_calling_convention :: proc(s: string) -> ast.Proc_Calling_Convention { + using ast.Proc_Calling_Convention; + switch s { + case "odin": + return Odin; + case "contextless": + return Contextless; + case "cdecl", "c": + return C_Decl; + case "stdcall", "std": + return Std_Call; + case "fast", "fastcall": + return Fast_Call; + } + return Invalid; +} + +parse_proc_type :: proc(p: ^Parser, tok: token.Token) -> ^ast.Proc_Type { + cc := ast.Proc_Calling_Convention.Invalid; + if p.curr_tok.kind == token.String { + str := expect_token(p, token.String); + cc = string_to_calling_convention(str.text); + if cc == ast.Proc_Calling_Convention.Invalid { + error(p, str.pos, "unknown calling convention '%s'", str.text); + } + } + + if cc == ast.Proc_Calling_Convention.Invalid { + if p.in_foreign_block { + cc = ast.Proc_Calling_Convention.Foreign_Block_Default; + } else { + cc = ast.Proc_Calling_Convention.Odin; + } + } + + expect_token(p, token.Open_Paren); + params, _ := parse_field_list(p, token.Close_Paren, ast.Field_Flags_Signature_Params); + expect_token(p, token.Close_Paren); + results, diverging := parse_results(p); + + is_generic := false; + + loop: for param in params.list { + if param.type != nil { + + } + } + + end := end_pos(p.prev_tok); + pt := ast.new(ast.Proc_Type, tok.pos, end); + pt.tok = tok; + pt.calling_convention = cc; + pt.params = params; + pt.results = results; + pt.diverging = diverging; + pt.generic = is_generic; + return pt; +} + +check_poly_params_for_type :: proc(p: ^Parser, poly_params: ^ast.Field_List, tok: token.Token) { + +} + + + +parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { + switch p.curr_tok.kind { + case token.Ident: + return parse_ident(p); + + case token.Undef: + tok := expect_token(p, token.Undef); + undef := ast.new(ast.Undef, tok.pos, end_pos(tok)); + undef.tok = tok.kind; + return undef; + + case token.Context: + tok := expect_token(p, token.Context); + ctx := ast.new(ast.Implicit, tok.pos, end_pos(tok)); + ctx.tok = tok; + return ctx; + + case token.Integer, token.Float, token.Imag, + token.Rune, token.String: + tok := advance_token(p); + bl := ast.new(ast.Basic_Lit, tok.pos, end_pos(tok)); + bl.tok = tok; + return bl; + + + case token.Size_Of, token.Align_Of, token.Offset_Of: + tok := advance_token(p); + expr := ast.new(ast.Implicit, tok.pos, end_pos(tok)); + expr.tok = tok; + return parse_call_expr(p, expr); + + case token.Open_Brace: + if !lhs { + return parse_literal_value(p, nil); + } + + case token.Open_Paren: + open := expect_token(p, token.Open_Paren); + p.expr_level += 1; + expr := parse_expr(p, false); + p.expr_level -= 1; + close := expect_token(p, token.Close_Paren); + + pe := ast.new(ast.Paren_Expr, open.pos, end_pos(close)); + pe.open = open.pos; + pe.expr = expr; + pe.close = close.pos; + return pe; + + case token.Distinct: + tok := advance_token(p); + type := parse_type(p); + dt := ast.new(ast.Distinct_Type, tok.pos, type.end); + dt.tok = tok.kind; + dt.type = type; + return dt; + + case token.Opaque: + tok := advance_token(p); + type := parse_type(p); + ot := ast.new(ast.Opaque_Type, tok.pos, type.end); + ot.tok = tok.kind; + ot.type = type; + return ot; + case token.Hash: + tok := expect_token(p, token.Hash); + name := expect_token(p, token.Ident); + switch name.text { + case "type": + type := parse_type(p); + hp := ast.new(ast.Helper_Type, tok.pos, type.end); + hp.tok = tok.kind; + hp.type = type; + return hp; + + case "file", "line", "procedure", "caller_location": + bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name)); + bd.tok = tok; + bd.name = name.text; + return bd; + case "location", "assert", "defined": + bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name)); + bd.tok = tok; + bd.name = name.text; + return parse_call_expr(p, bd); + case: + expr := parse_expr(p, lhs); + te := ast.new(ast.Tag_Expr, tok.pos, expr.pos); + te.op = tok; + te.name = name.text; + te.expr = expr; + return te; + } + + case token.Inline, token.No_Inline: + tok := advance_token(p); + + case token.Proc: + tok := expect_token(p, token.Proc); + + if p.curr_tok.kind == token.Open_Brace { + open := expect_token(p, token.Open_Brace); + + args: [dynamic]^ast.Expr; + + for p.curr_tok.kind != token.Close_Brace && + p.curr_tok.kind != token.EOF { + elem := parse_expr(p, false); + append(&args, elem); + + if !allow_token(p, token.Comma) { + break; + } + } + + close := expect_token(p, token.Close_Brace); + + if len(args) == 0 { + error(p, tok.pos, "expected at least 1 argument in procedure group"); + } + + pg := ast.new(ast.Proc_Group, tok.pos, end_pos(close)); + pg.tok = tok; + pg.open = open.pos; + pg.args = args[:]; + pg.close = close.pos; + return pg; + } + + type := parse_proc_type(p, tok); + + if p.allow_type && p.expr_level < 0 { + return type; + } + body: ^ast.Stmt; + + if allow_token(p, token.Undef) { + // Okay + } else if p.curr_tok.kind == token.Open_Brace { + prev_proc := p.curr_proc; + p.curr_proc = type; + body = parse_body(p); + p.curr_proc = prev_proc; + } else if allow_token(p, token.Do) { + prev_proc := p.curr_proc; + p.curr_proc = type; + body = convert_stmt_to_body(p, parse_stmt(p)); + p.curr_proc = prev_proc; + } else { + return type; + } + + pl := ast.new(ast.Proc_Lit, tok.pos, end_pos(p.prev_tok)); + pl.type = type; + pl.body = body; + return pl; + + case token.Dollar: + tok := advance_token(p); + type := parse_ident(p); + end := type.end; + + specialization: ^ast.Expr; + if allow_token(p, token.Quo) { + specialization = parse_type(p); + end = specialization.pos; + } + pt := ast.new(ast.Poly_Type, tok.pos, end); + pt.dollar = tok.pos; + pt.type = type; + pt.specialization = specialization; + return pt; + + case token.Typeid: + tok := advance_token(p); + ti := ast.new(ast.Typeid_Type, tok.pos, end_pos(tok)); + ti.tok = tok.kind; + ti.specialization = nil; + return ti; + + case token.Type_Of: + tok := advance_token(p); + i := ast.new(ast.Implicit, tok.pos, end_pos(tok)); + i.tok = tok; + type: ^ast.Expr = parse_call_expr(p, i); + for p.curr_tok.kind == token.Period { + tok := advance_token(p); + + field := parse_ident(p); + sel := ast.new(ast.Selector_Expr, tok.pos, field.end); + sel.expr = type; + sel.field = field; + + type = sel; + } + + return type; + + + case token.Pointer: + tok := expect_token(p, token.Pointer); + elem := parse_type(p); + ptr := ast.new(ast.Pointer_Type, tok.pos, elem.end); + ptr.elem = elem; + return ptr; + + case token.Open_Bracket: + open := expect_token(p, token.Open_Bracket); + count: ^ast.Expr; + if p.curr_tok.kind == token.Question { + tok := expect_token(p, token.Question); + q := ast.new(ast.Unary_Expr, tok.pos, end_pos(tok)); + q.op = tok; + count = q; + } else if p.curr_tok.kind == token.Dynamic { + tok := expect_token(p, token.Dynamic); + close := expect_token(p, token.Close_Bracket); + elem := parse_type(p); + da := ast.new(ast.Dynamic_Array_Type, open.pos, elem.end); + da.open = open.pos; + da.dynamic_pos = tok.pos; + da.close = close.pos; + da.elem = elem; + + return da; + } else if p.curr_tok.kind != token.Close_Bracket { + p.expr_level += 1; + count = parse_expr(p, false); + p.expr_level -= 1; + } + close := expect_token(p, token.Close_Bracket); + elem := parse_type(p); + at := ast.new(ast.Array_Type, open.pos, elem.end); + at.open = open.pos; + at.len = count; + at.close = close.pos; + at.elem = elem; + return at; + + case token.Map: + tok := expect_token(p, token.Map); + open := expect_token(p, token.Open_Bracket); + key := parse_type(p); + close := expect_token(p, token.Close_Bracket); + value := parse_type(p); + + mt := ast.new(ast.Map_Type, tok.pos, value.end); + mt.tok_pos = tok.pos; + mt.key = key; + mt.value = value; + return mt; + + case token.Struct: + tok := expect_token(p, token.Struct); + + poly_params: ^ast.Field_List; + align: ^ast.Expr; + is_packed: bool; + is_raw_union: bool; + fields: ^ast.Field_List; + name_count: int; + + if allow_token(p, token.Open_Paren) { + param_count: int; + poly_params, param_count = parse_field_list(p, token.Close_Paren, ast.Field_Flags_Record_Poly_Params); + if param_count == 0 { + error(p, poly_params.pos, "expected at least 1 polymorphic parameter"); + poly_params = nil; + } + expect_token_after(p, token.Close_Paren, "parameter list"); + check_poly_params_for_type(p, poly_params, tok); + } + + prev_level := p.expr_level; + p.expr_level = -1; + for allow_token(p, token.Hash) { + tag := expect_token_after(p, token.Ident, "#"); + switch tag.text { + case "packed": + if is_packed do error(p, tag.pos, "duplicate struct tag '#%s'", tag.text); + is_packed = true; + case "align": + if align != nil do error(p, tag.pos, "duplicate struct tag '#%s'", tag.text); + align = parse_expr(p, true); + case "raw_union": + if is_raw_union do error(p, tag.pos, "duplicate struct tag '#%s'", tag.text); + is_raw_union = true; + case: + error(p, tag.pos, "invalid struct tag '#%s", tag.text); + } + } + p.expr_level = prev_level; + + if is_raw_union && is_packed { + is_packed = false; + error(p, tok.pos, "'#raw_union' cannot also be '#packed"); + } + + open := expect_token(p, token.Open_Brace); + fields, name_count = parse_field_list(p, token.Close_Brace, ast.Field_Flags_Struct); + close := expect_token(p, token.Close_Brace); + + st := ast.new(ast.Struct_Type, tok.pos, end_pos(close)); + st.poly_params = poly_params; + st.align = align; + st.is_packed = is_packed; + st.is_raw_union = is_raw_union; + st.fields = fields; + st.name_count = name_count; + return st; + + case token.Union: + tok := expect_token(p, token.Union); + poly_params: ^ast.Field_List; + align: ^ast.Expr; + + if allow_token(p, token.Open_Paren) { + param_count: int; + poly_params, param_count = parse_field_list(p, token.Close_Paren, ast.Field_Flags_Record_Poly_Params); + if param_count == 0 { + error(p, poly_params.pos, "expected at least 1 polymorphic parameter"); + poly_params = nil; + } + expect_token_after(p, token.Close_Paren, "parameter list"); + check_poly_params_for_type(p, poly_params, tok); + } + + prev_level := p.expr_level; + p.expr_level = -1; + for allow_token(p, token.Hash) { + tag := expect_token_after(p, token.Ident, "#"); + switch tag.text { + case "align": + if align != nil do error(p, tag.pos, "duplicate union tag '#%s'", tag.text); + align = parse_expr(p, true); + case: + error(p, tag.pos, "invalid union tag '#%s", tag.text); + } + } + p.expr_level = prev_level; + + variants: [dynamic]^ast.Expr; + + open := expect_token_after(p, token.Open_Brace, "union"); + + for p.curr_tok.kind != token.Close_Brace && p.curr_tok.kind != token.EOF { + type := parse_type(p); + if _, ok := type.derived.(ast.Bad_Expr); !ok { + append(&variants, type); + } + if !allow_token(p, token.Comma) { + break; + } + } + + close := expect_token(p, token.Close_Brace); + + ut := ast.new(ast.Union_Type, tok.pos, end_pos(close)); + ut.poly_params = poly_params; + ut.variants = variants[:]; + ut.align = align; + + return ut; + + case token.Enum: + tok := expect_token(p, token.Enum); + base_type: ^ast.Expr; + if p.curr_tok.kind != token.Open_Brace { + base_type = parse_type(p); + } + open := expect_token(p, token.Open_Brace); + fields := parse_elem_list(p); + close := expect_token(p, token.Close_Brace); + + et := ast.new(ast.Enum_Type, tok.pos, end_pos(close)); + et.base_type = base_type; + et.open = open.pos; + et.fields = fields; + et.close = close.pos; + return et; + + case token.Bit_Field: + tok := expect_token(p, token.Bit_Field); + + fields: [dynamic]^ast.Field_Value; + align: ^ast.Expr; + + prev_level := p.expr_level; + p.expr_level = -1; + + for allow_token(p, token.Hash) { + tag := expect_token_after(p, token.Ident, "#"); + switch tag.text { + case "align": + if align != nil { + error(p, tag.pos, "duplicate bit_field tag '#%s", tag.text); + } + align = parse_expr(p, true); + case: + error(p, tag.pos, "invalid bit_field tag '#%s", tag.text); + } + } + + p.expr_level = prev_level; + + open := expect_token_after(p, token.Open_Brace, "bit_field"); + + for p.curr_tok.kind != token.Close_Brace && p.curr_tok.kind != token.EOF { + name := parse_ident(p); + colon := expect_token(p, token.Colon); + value := parse_expr(p, true); + + fv := ast.new(ast.Field_Value, name.pos, value.end); + fv.field = name; + fv.sep = colon.pos; + fv.value = value; + append(&fields, fv); + + if !allow_token(p, token.Comma) { + break; + } + } + + close := expect_token(p, token.Close_Brace); + + bft := ast.new(ast.Bit_Field_Type, tok.pos, end_pos(close)); + bft.tok_pos = tok.pos; + bft.open = open.pos; + bft.fields = fields[:]; + bft.close = close.pos; + bft.align = align; + + return bft; + + case token.Bit_Set: + tok := expect_token(p, token.Bit_Set); + open := expect_token(p, token.Open_Bracket); + elem, underlying: ^ast.Expr; + + prev_allow_range := p.allow_range; + p.allow_range = true; + elem = parse_expr(p, false); + p.allow_range = prev_allow_range; + + if allow_token(p, token.Semicolon) { + underlying = parse_type(p); + } + + + close := expect_token(p, token.Close_Bracket); + + bst := ast.new(ast.Bit_Set_Type, tok.pos, end_pos(close)); + bst.tok_pos = tok.pos; + bst.open = open.pos; + bst.elem = elem; + bst.underlying = underlying; + bst.close = close.pos; + return bst; + + } + + return nil; +} + +is_literal_type :: proc(expr: ^ast.Expr) -> bool { + expr = ast.unparen_expr(expr); + if expr == nil { + return false; + } + switch _ in expr.derived { + case ast.Bad_Expr, + ast.Ident, + ast.Selector_Expr, + ast.Array_Type, + ast.Struct_Type, + ast.Union_Type, + ast.Enum_Type, + ast.Dynamic_Array_Type, + ast.Map_Type, + ast.Bit_Field_Type, + ast.Bit_Set_Type, + ast.Call_Expr: + return true; + } + return false; +} + +parse_value :: proc(p: ^Parser) -> ^ast.Expr { + if p.curr_tok.kind == token.Open_Brace { + return parse_literal_value(p, nil); + } + return parse_expr(p, false); +} + +parse_elem_list :: proc(p: ^Parser) -> []^ast.Expr { + elems: [dynamic]^ast.Expr; + + for p.curr_tok.kind != token.Close_Brace && p.curr_tok.kind != token.EOF { + elem := parse_value(p); + if p.curr_tok.kind == token.Eq { + eq := expect_token(p, token.Eq); + value := parse_value(p); + + fv := ast.new(ast.Field_Value, elem.pos, value.end); + fv.field = elem; + fv.sep = eq.pos; + fv.value = value; + + elem = fv; + } + + append(&elems, elem); + + if !allow_token(p, token.Comma) { + break; + } + } + + return elems[:]; +} + +parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit { + elems: []^ast.Expr; + open := expect_token(p, token.Open_Brace); + p.expr_level += 1; + if p.curr_tok.kind != token.Close_Brace { + elems = parse_elem_list(p); + } + p.expr_level -= 1; + + close := expect_token_after(p, token.Close_Brace, "compound literal"); + + pos := type != nil ? type.pos : open.pos; + lit := ast.new(ast.Comp_Lit, pos, end_pos(close)); + lit.type = type; + lit.open = open.pos; + lit.elems = elems; + lit.close = close.pos; + return lit; +} + +parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr { + args: [dynamic]^ast.Expr; + + ellipsis: token.Token; + + p.expr_level += 1; + open := expect_token(p, token.Open_Paren); + + for p.curr_tok.kind != token.Close_Paren && + p.curr_tok.kind != token.EOF && + ellipsis.pos.line == 0 { + + if p.curr_tok.kind == token.Comma { + error(p, p.curr_tok.pos, "expected an expression not ,"); + } else if p.curr_tok.kind == token.Eq { + error(p, p.curr_tok.pos, "expected an expression not ="); + } + + prefix_ellipsis := false; + if p.curr_tok.kind == token.Ellipsis { + prefix_ellipsis = true; + ellipsis = expect_token(p, token.Ellipsis); + } + + arg := parse_expr(p, false); + if p.curr_tok.kind == token.Eq { + eq := expect_token(p, token.Eq); + + if prefix_ellipsis { + error(p, ellipsis.pos, "'..' must be applied to value rather than a field name"); + } + + value := parse_value(p); + fv := ast.new(ast.Field_Value, arg.pos, value.end); + fv.field = arg; + fv.sep = eq.pos; + fv.value = value; + + arg = fv; + } + + append(&args, arg); + + if !allow_token(p, token.Comma) { + break; + } + } + + close := expect_token_after(p, token.Close_Paren, "argument list"); + p.expr_level -= 1; + + ce := ast.new(ast.Call_Expr, operand.pos, end_pos(close)); + ce.expr = operand; + ce.open = open.pos; + ce.args = args[:]; + ce.ellipsis = ellipsis; + ce.close = close.pos; + + return ce; +} + + +parse_atom_expr :: proc(p: ^Parser, operand: ^ast.Expr, lhs: bool) -> ^ast.Expr { + if operand == nil { + if p.allow_type do return nil; + error(p, p.curr_tok.pos, "expected an operand"); + be := ast.new(ast.Bad_Expr, p.curr_tok.pos, end_pos(p.curr_tok)); + advance_token(p); + operand = be; + } + + loop := true; + for loop { + switch p.curr_tok.kind { + case: + loop = false; + + case token.Open_Paren: + operand = parse_call_expr(p, operand); + + case token.Open_Bracket: + prev_allow_range := p.allow_range; + defer p.allow_range = prev_allow_range; + p.allow_range = false; + + indicies: [2]^ast.Expr; + interval: token.Token; + is_slice_op := false; + + p.expr_level += 1; + open := expect_token(p, token.Open_Bracket); + + if p.curr_tok.kind != token.Colon { + indicies[0] = parse_expr(p, false); + } + + if p.curr_tok.kind == token.Colon { + interval = advance_token(p); + is_slice_op = true; + if (p.curr_tok.kind != token.Close_Bracket && p.curr_tok.kind != token.EOF) { + indicies[1] = parse_expr(p, false); + } + } + + close := expect_token(p, token.Close_Bracket); + p.expr_level -= 1; + + if is_slice_op { + se := ast.new(ast.Slice_Expr, operand.pos, end_pos(close)); + se.expr = operand; + se.open = open.pos; + se.low = indicies[0]; + se.interval = interval; + se.high = indicies[1]; + se.close = close.pos; + + operand = se; + } else { + ie := ast.new(ast.Index_Expr, operand.pos, end_pos(close)); + ie.expr = operand; + ie.open = open.pos; + ie.index = indicies[0]; + ie.close = close.pos; + + operand = ie; + } + + + case token.Period: + tok := expect_token(p, token.Period); + switch p.curr_tok.kind { + case token.Ident: + field := parse_ident(p); + + sel := ast.new(ast.Selector_Expr, operand.pos, field.end); + sel.expr = operand; + sel.field = field; + + operand = sel; + + case token.Open_Paren: + open := expect_token(p, token.Open_Paren); + type := parse_type(p); + close := expect_token(p, token.Close_Paren); + + ta := ast.new(ast.Type_Assertion, operand.pos, end_pos(close)); + ta.expr = operand; + ta.open = open.pos; + ta.type = type; + ta.close = close.pos; + + operand = ta; + + case: + error(p, p.curr_tok.pos, "expected a selector"); + advance_token(p); + operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok)); + } + + case token.Pointer: + op := expect_token(p, token.Pointer); + deref := ast.new(ast.Deref_Expr, operand.pos, end_pos(op)); + deref.expr = operand; + deref.op = op; + + operand = deref; + + case token.Open_Brace: + if !lhs && is_literal_type(operand) && p.expr_level >= 0 { + operand = parse_literal_value(p, operand); + } else { + loop = false; + } + + } + + lhs = false; + } + + return operand; + +} + +parse_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { + return parse_binary_expr(p, lhs, 0+1); +} +parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { + switch p.curr_tok.kind { + case token.Transmute, token.Cast: + tok := advance_token(p); + open := expect_token(p, token.Open_Paren); + type := parse_type(p); + close := expect_token(p, token.Close_Paren); + expr := parse_unary_expr(p, lhs); + + tc := ast.new(ast.Type_Cast, tok.pos, expr.end); + tc.tok = tok; + tc.open = open.pos; + tc.type = type; + tc.close = close.pos; + tc.expr = expr; + return tc; + + case token.Auto_Cast: + op := advance_token(p); + expr := parse_unary_expr(p, lhs); + + ac := ast.new(ast.Auto_Cast, op.pos, expr.end); + ac.op = op; + ac.expr = expr; + return ac; + + case token.Add, token.Sub, + token.Not, token.Xor, + token.And: + op := advance_token(p); + expr := parse_unary_expr(p, lhs); + + ue := ast.new(ast.Unary_Expr, op.pos, expr.end); + ue.op = op; + ue.expr = expr; + return ue; + + } + return parse_atom_expr(p, parse_operand(p, lhs), lhs); +} +parse_binary_expr :: proc(p: ^Parser, lhs: bool, prec_in: int) -> ^ast.Expr { + expr := parse_unary_expr(p, lhs); + for prec := token_precedence(p, p.curr_tok.kind); prec >= prec_in; prec -= 1 { + for { + op := p.curr_tok; + op_prec := token_precedence(p, op.kind); + if op_prec != prec { + break; + } + expect_operator(p); + + if op.kind == token.Question { + cond := expr; + x := parse_expr(p, lhs); + colon := expect_token(p, token.Colon); + y := parse_expr(p, lhs); + te := ast.new(ast.Ternary_Expr, expr.pos, end_pos(p.prev_tok)); + te.cond = cond; + te.op1 = op; + te.x = x; + te.op2 = colon; + te.y = y; + + expr = te; + } else { + right := parse_binary_expr(p, false, prec+1); + if right == nil { + error(p, op.pos, "expected expression on the right-hand side of the binary operator"); + } + be := ast.new(ast.Binary_Expr, expr.pos, end_pos(p.prev_tok)); + be.left = expr; + be.op = op; + be.right = right; + + expr = be; + } + } + } + + return expr; +} + + +parse_expr_list :: proc(p: ^Parser, lhs: bool) -> ([]^ast.Expr) { + list: [dynamic]^ast.Expr; + for { + expr := parse_expr(p, lhs); + append(&list, expr); + if p.curr_tok.kind != token.Comma || p.curr_tok.kind == token.EOF { + break; + } + advance_token(p); + } + + return list[:]; +} +parse_lhs_expr_list :: proc(p: ^Parser) -> []^ast.Expr { + return parse_expr_list(p, true); +} +parse_rhs_expr_list :: proc(p: ^Parser) -> []^ast.Expr { + return parse_expr_list(p, false); +} + +parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt { + start_tok := p.curr_tok; + docs := p.lead_comment; + + lhs := parse_lhs_expr_list(p); + op := p.curr_tok; + switch { + case token.is_assignment_operator(op.kind): + // if p.curr_proc == nil { + // error(p, p.curr_tok.pos, "simple statements are not allowed at the file scope"); + // return ast.new(ast.Bad_Stmt, start_tok.pos, end_pos(p.curr_tok)); + // } + advance_token(p); + rhs := parse_rhs_expr_list(p); + if len(rhs) == 0 { + error(p, p.curr_tok.pos, "no right-hand side in assignment statement"); + return ast.new(ast.Bad_Stmt, start_tok.pos, end_pos(p.curr_tok)); + } + stmt := ast.new(ast.Assign_Stmt, lhs[0].pos, rhs[len(rhs)-1].end); + stmt.lhs = lhs; + stmt.op = op; + stmt.rhs = rhs; + return stmt; + + case op.kind == token.In: + if Stmt_Allow_Flag.In in flags { + allow_token(p, token.In); + prev_allow_range := p.allow_range; + p.allow_range = true; + expr := parse_expr(p, false); + p.allow_range = prev_allow_range; + + rhs := make([]^ast.Expr, 1); + rhs[0] = expr; + + stmt := ast.new(ast.Assign_Stmt, lhs[0].pos, rhs[len(rhs)-1].end); + stmt.lhs = lhs; + stmt.op = op; + stmt.rhs = rhs; + return stmt; + } + case op.kind == token.Colon: + expect_token_after(p, token.Colon, "identifier list"); + if Stmt_Allow_Flag.Label in flags && len(lhs) == 1 { + switch p.curr_tok.kind { + case token.Open_Brace, token.If, token.For, token.Switch: + label := lhs[0]; + stmt := parse_stmt(p); + + if stmt != nil do switch n in &stmt.derived { + case ast.Block_Stmt: n.label = label; + case ast.If_Stmt: n.label = label; + case ast.For_Stmt: n.label = label; + case ast.Switch_Stmt: n.label = label; + case ast.Type_Switch_Stmt: n.label = label; + } + + return stmt; + } + } + + return parse_value_decl(p, lhs, docs); + } + + if len(lhs) > 1 { + error(p, op.pos, "expected 1 expression, got %d", len(lhs)); + return ast.new(ast.Bad_Stmt, start_tok.pos, end_pos(p.curr_tok)); + } + + es := ast.new(ast.Expr_Stmt, lhs[0].pos, lhs[0].end); + es.expr = lhs[0]; + return es; +} + +parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Group) -> ^ast.Decl { + is_mutable := true; + + values: []^ast.Expr; + type := parse_type_or_ident(p); + + switch p.curr_tok.kind { + case token.Eq, token.Colon: + sep := advance_token(p); + is_mutable = sep.kind != token.Colon; + + values = parse_rhs_expr_list(p); + if len(values) > len(names) { + error(p, p.curr_tok.pos, "too many values on the right-hand side of the declaration"); + } else if len(values) < len(names) && !is_mutable { + error(p, p.curr_tok.pos, "all constant declarations must be defined"); + } else if len(values) == 0 { + error(p, p.curr_tok.pos, "expected an expression for this declaration"); + } + } + + if is_mutable { + if type == nil && len(values) == 0 { + error(p, p.curr_tok.pos, "missing variable type or initialization"); + return ast.new(ast.Bad_Decl, names[0].pos, end_pos(p.curr_tok)); + } + } else { + if type == nil && len(values) == 0 && len(names) > 0 { + error(p, p.curr_tok.pos, "missing constant value"); + return ast.new(ast.Bad_Decl, names[0].pos, end_pos(p.curr_tok)); + } + } + + if p.expr_level >= 0 { + end: ^ast.Expr; + if !is_mutable && len(values) > 0 { + end = values[len(values)-1]; + } + if p.curr_tok.kind == token.Close_Brace && + p.curr_tok.pos.line == p.prev_tok.pos.line { + + } else { + expect_semicolon(p, end); + } + } + + if p.curr_proc == nil { + if len(values) > 0 && len(names) != len(values) { + error(p, values[0].pos, "expected %d expressions on the right-hand side, got %d", len(names), len(values)); + } + } + + decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(p.prev_tok)); + decl.docs = docs; + decl.names = names; + decl.type = type; + decl.values = values; + decl.is_mutable = is_mutable; + return decl; +} + + +parse_import_decl :: proc(p: ^Parser, kind := Import_Decl_Kind.Standard) -> ^ast.Import_Decl { + docs := p.lead_comment; + tok := expect_token(p, token.Import); + + import_name: token.Token; + is_using := kind != Import_Decl_Kind.Standard; + + switch p.curr_tok.kind { + case token.Ident: + import_name = advance_token(p); + case: + import_name.pos = p.curr_tok.pos; + } + + if !is_using && is_blank_ident(import_name) { + error(p, import_name.pos, "illegal import name: '_'"); + } + + path := expect_token_after(p, token.String, "import"); + + decl := ast.new(ast.Import_Decl, tok.pos, end_pos(path)); + decl.docs = docs; + decl.is_using = is_using; + decl.import_tok = tok; + decl.name = import_name; + decl.relpath = path; + decl.fullpath = path.text; + + if p.curr_proc != nil { + error(p, decl.pos, "import declarations cannot be used within a procedure, it must be done at the file scope"); + } else { + append(&p.file.imports, decl); + } + expect_semicolon(p, decl); + decl.comment = p.line_comment; + + return decl; +}