// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. module ast import v.token import v.errors import v.pref pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral | CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | ComptimeSelector | ConcatExpr | DumpExpr | EmptyExpr | EnumVal | FloatLiteral | GoExpr | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | IsRefType | Likely | LockExpr | MapInit | MatchExpr | NodeError | None | OffsetOf | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit | TypeNode | TypeOf | UnsafeExpr pub type Stmt = AsmStmt | AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt | EmptyStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | NodeError | Return | SqlStmt | StructDecl | TypeDecl pub type ScopeObject = AsmRegister | ConstField | GlobalField | Var // TODO: replace Param pub type Node = CallArg | ConstField | EmptyNode | EnumField | Expr | File | GlobalField | IfBranch | MatchBranch | NodeError | Param | ScopeObject | SelectBranch | Stmt | StructField | StructInitField pub struct TypeNode { pub: typ Type pos token.Position } pub struct EmptyExpr { x int } pub fn empty_expr() Expr { return EmptyExpr{} } pub struct EmptyStmt { pub: pos token.Position } pub fn empty_stmt() Stmt { return EmptyStmt{} } pub struct EmptyNode { x int } pub fn empty_node() Node { return EmptyNode{} } // `{stmts}` or `unsafe {stmts}` pub struct Block { pub: stmts []Stmt is_unsafe bool pos token.Position } // | IncDecStmt k // Stand-alone expression in a statement list. pub struct ExprStmt { pub: expr Expr pos token.Position comments []Comment pub mut: is_expr bool typ Type } pub struct IntegerLiteral { pub: val string pos token.Position } pub struct FloatLiteral { pub: val string pos token.Position } pub struct StringLiteral { pub: val string is_raw bool language Language pos token.Position } // 'name: $name' pub struct StringInterLiteral { pub: vals []string exprs []Expr fwidths []int precisions []int pluss []bool fills []bool fmt_poss []token.Position pos token.Position pub mut: expr_types []Type fmts []byte need_fmts []bool // an explicit non-default fmt required, e.g. `x` } pub struct CharLiteral { pub: val string pos token.Position } pub struct BoolLiteral { pub: val bool pos token.Position } pub enum GenericKindField { unknown name typ } // `foo.bar` pub struct SelectorExpr { pub: pos token.Position field_name string is_mut bool // is used for the case `if mut ident.selector is MyType {`, it indicates if the root ident is mutable mut_pos token.Position next_token token.Kind pub mut: expr Expr // expr.field_name expr_type Type // type of `Foo` in `Foo.bar` typ Type // type of the entire thing (`Foo.bar`) name_type Type // T in `T.name` or typeof in `typeof(expr).name` gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown scope &Scope from_embed_type Type // holds the type of the embed that the method is called from } // root_ident returns the origin ident where the selector started. pub fn (e &SelectorExpr) root_ident() ?Ident { mut root := e.expr for mut root is SelectorExpr { root = root.expr } if root is Ident { return root as Ident } return none } // module declaration pub struct Module { pub: name string // encoding.base64 short_name string // base64 attrs []Attr pos token.Position name_pos token.Position // `name` in import name is_skipped bool // module main can be skipped in single file programs } pub struct StructField { pub: pos token.Position type_pos token.Position comments []Comment has_default_expr bool attrs []Attr is_pub bool default_val string is_mut bool is_global bool pub mut: default_expr Expr default_expr_typ Type name string typ Type } /* pub struct Field { pub: name string pos token.Position pub mut: typ Type } */ // const field in const declaration group pub struct ConstField { pub: mod string name string expr Expr // the value expr of field; everything after `=` is_pub bool pos token.Position pub mut: typ Type // the type of the const field, it can be any type in V comments []Comment // comments before current const field // the comptime_expr_value field is filled by the checker, when it has enough // info to evaluate the constant at compile time comptime_expr_value ComptTimeConstValue = empty_comptime_const_expr() } // const declaration pub struct ConstDecl { pub: is_pub bool pos token.Position pub mut: fields []ConstField // all the const fields in the `const (...)` block end_comments []Comment // comments that after last const field is_block bool // const() block } pub struct StructDecl { pub: pos token.Position name string generic_types []Type is_pub bool // _pos fields for vfmt mut_pos int // mut: pub_pos int // pub: pub_mut_pos int // pub mut: global_pos int // __global: module_pos int // module: language Language is_union bool attrs []Attr end_comments []Comment embeds []Embed pub mut: fields []StructField } pub struct Embed { pub: typ Type pos token.Position comments []Comment } pub struct InterfaceEmbedding { pub: name string typ Type pos token.Position comments []Comment } pub struct InterfaceDecl { pub: name string typ Type name_pos token.Position language Language field_names []string is_pub bool mut_pos int // mut: pos token.Position pre_comments []Comment generic_types []Type pub mut: methods []FnDecl fields []StructField // ifaces []InterfaceEmbedding are_ifaces_expanded bool } pub struct StructInitField { pub: pos token.Position name_pos token.Position comments []Comment next_comments []Comment pub mut: expr Expr name string typ Type expected_type Type parent_type Type } pub struct StructInitEmbed { pub: expr Expr pos token.Position comments []Comment next_comments []Comment pub mut: name string typ Type expected_type Type } pub struct StructInit { pub: pos token.Position name_pos token.Position is_short bool pub mut: unresolved bool pre_comments []Comment typ Type update_expr Expr update_expr_type Type update_expr_comments []Comment has_update_expr bool fields []StructInitField embeds []StructInitEmbed } // import statement pub struct Import { pub: mod string // the module name of the import alias string // the `x` in `import xxx as x` pos token.Position mod_pos token.Position alias_pos token.Position syms_pos token.Position pub mut: syms []ImportSymbol // the list of symbols in `import {symbol1, symbol2}` comments []Comment next_comments []Comment } // import symbol,for import {symbol} syntax pub struct ImportSymbol { pub: pos token.Position name string } // anonymous function pub struct AnonFn { pub mut: decl FnDecl inherited_vars []Param typ Type // the type of anonymous fn. Both .typ and .decl.name are auto generated has_gen bool // has been generated } // function or method declaration pub struct FnDecl { pub: name string mod string is_deprecated bool is_pub bool is_variadic bool is_anon bool is_noreturn bool // true, when [noreturn] is used on a fn is_manualfree bool // true, when [manualfree] is used on a fn is_main bool // true for `fn main()` is_test bool // true for `fn test_abcde` is_conditional bool // true for `[if abc] fn abc(){}` is_exported bool // true for `[export: 'exact_C_name']` is_keep_alive bool // passed memory must not be freed (by GC) before function returns is_unsafe bool // true, when [unsafe] is used on a fn receiver StructField // TODO this is not a struct field receiver_pos token.Position // `(u User)` in `fn (u User) name()` position is_method bool method_type_pos token.Position // `User` in ` fn (u User)` position method_idx int rec_mut bool // is receiver mutable rec_share ShareType language Language // V, C, JS file_mode Language // whether *the file*, where a function was a '.c.v', '.js.v' etc. no_body bool // just a definition `fn C.malloc()` is_builtin bool // this function is defined in builtin/strconv body_pos token.Position // function bodys position file string generic_names []string is_direct_arr bool // direct array access attrs []Attr ctdefine_idx int = -1 // the index in fn.attrs of `[if xyz]`, when such attribute exists pub mut: params []Param stmts []Stmt defer_stmts []DeferStmt return_type Type return_type_pos token.Position // `string` in `fn (u User) name() string` position has_return bool should_be_skipped bool // comments []Comment // comments *after* the header, but *before* `{`; used for InterfaceDecl next_comments []Comment // coments that are one line after the decl; used for InterfaceDecl // source_file &File = 0 scope &Scope label_names []string pos token.Position // function declaration position } // break, continue pub struct BranchStmt { pub: kind token.Kind label string pos token.Position } // function or method call expr pub struct CallExpr { pub: pos token.Position name_pos token.Position mod string pub mut: name string // left.name() is_method bool is_field bool // temp hack, remove ASAP when re-impl CallExpr / Selector (joe) is_keep_alive bool // GC must not free arguments before fn returns is_noreturn bool // whether the function/method is marked as [noreturn] args []CallArg expected_arg_types []Type language Language or_block OrExpr left Expr // `user` in `user.register()` left_type Type // type of `user` receiver_type Type // User return_type Type should_be_skipped bool concrete_types []Type // concrete types, e.g. concrete_list_pos token.Position free_receiver bool // true if the receiver expression needs to be freed scope &Scope from_embed_type Type // holds the type of the embed that the method is called from comments []Comment } /* pub struct AutofreeArgVar { name string idx int } */ // function call argument: `f(callarg)` pub struct CallArg { pub: is_mut bool share ShareType comments []Comment pub mut: expr Expr typ Type is_tmp_autofree bool // this tells cgen that a tmp variable has to be used for the arg expression in order to free it after the call pos token.Position // tmp_name string // for autofree } // function return statement pub struct Return { pub: pos token.Position comments []Comment pub mut: exprs []Expr types []Type } /* pub enum Expr { Binary(InfixExpr) If(IfExpr) Integer(IntegerExpr) } */ /* pub struct Stmt { pos int //end int } */ pub struct Var { pub: name string expr Expr share ShareType is_mut bool is_autofree_tmp bool is_arg bool // fn args should not be autofreed is_auto_deref bool is_inherited bool pub mut: typ Type orig_type Type // original sumtype type; 0 if it's not a sumtype smartcasts []Type // nested sum types require nested smart casting, for that a list of types is needed // TODO: move this to a real docs site later // 10 <- original type (orig_type) // [11, 12, 13] <- cast order (smartcasts) // 12 <- the current casted type (typ) pos token.Position is_used bool is_changed bool // to detect mutable vars that are never changed // // (for setting the position after the or block for autofree) is_or bool // `x := foo() or { ... }` is_tmp bool // for tmp for loop vars, so that autofree can skip them is_auto_heap bool // value whoes address goes out of scope is_stack_obj bool // may be pointer to stack value (`mut` or `&` arg and not [heap] struct) } // used for smartcasting only // struct fields change type in scopes pub struct ScopeStructField { pub: struct_type Type // type of struct name string pos token.Position typ Type smartcasts []Type // nested sum types require nested smart casting, for that a list of types is needed orig_type Type // original sumtype type; 0 if it's not a sumtype // TODO: move this to a real docs site later // 10 <- original type (orig_type) // [11, 12, 13] <- cast order (smartcasts) // 12 <- the current casted type (typ) } pub struct GlobalField { pub: name string has_expr bool pos token.Position typ_pos token.Position pub mut: expr Expr typ Type comments []Comment } pub struct GlobalDecl { pub: mod string pos token.Position is_block bool // __global() block pub mut: fields []GlobalField end_comments []Comment } pub struct EmbeddedFile { pub: rpath string // used in the source code, as an ID/key to the embed apath string // absolute path during compilation to the resource } // Each V source file is represented by one File structure. // When the V compiler runs, the parser will fill an []File. // That array is then passed to V's checker. [heap] pub struct File { pub: nr_lines int // number of source code lines in the file (including newlines and comments) nr_bytes int // number of processed source code bytes mod Module // the module of the source file (from `module xyz` at the top) global_scope &Scope is_test bool // true for _test.v files pub mut: path string // absolute path of the source file - '/projects/v/file.v' path_base string // file name - 'file.v' (useful for tracing) scope &Scope stmts []Stmt // all the statements in the source file imports []Import // all the imports auto_imports []string // imports that were implicitely added embedded_files []EmbeddedFile // list of files to embed in the binary imported_symbols map[string]string // used for `import {symbol}`, it maps symbol => module.symbol errors []errors.Error // all the checker errors in the file warnings []errors.Warning // all the checker warnings in the file notices []errors.Notice // all the checker notices in the file generic_fns []&FnDecl global_labels []string // from `asm { .globl labelname }` } [unsafe] pub fn (f &File) free() { unsafe { f.path.free() f.path_base.free() f.scope.free() f.stmts.free() f.imports.free() f.auto_imports.free() f.embedded_files.free() f.imported_symbols.free() f.errors.free() f.warnings.free() f.notices.free() f.global_labels.free() } } pub struct IdentFn { pub mut: typ Type } // TODO: (joe) remove completely, use ident.obj // instead which points to the scope object pub struct IdentVar { pub mut: typ Type is_mut bool is_static bool is_volatile bool is_optional bool share ShareType } pub type IdentInfo = IdentFn | IdentVar pub enum IdentKind { unresolved blank_ident variable constant global function } // A single identifier pub struct Ident { pub: language Language tok_kind token.Kind pos token.Position mut_pos token.Position comptime bool pub mut: scope &Scope obj ScopeObject mod string name string kind IdentKind info IdentInfo is_mut bool } pub fn (i &Ident) var_info() IdentVar { match mut i.info { IdentVar { return i.info } else { // return IdentVar{} panic('Ident.var_info(): info is not IdentVar variant') } } } // left op right // See: token.Kind.is_infix pub struct InfixExpr { pub: op token.Kind pos token.Position is_stmt bool pub mut: left Expr right Expr left_type Type right_type Type auto_locked string or_block OrExpr } // ++, -- pub struct PostfixExpr { pub: op token.Kind expr Expr pos token.Position pub mut: auto_locked string } // See: token.Kind.is_prefix pub struct PrefixExpr { pub: op token.Kind pos token.Position pub mut: right_type Type right Expr or_block OrExpr is_option bool // IfGuard } pub struct IndexExpr { pub: pos token.Position index Expr // [0], RangeExpr [start..end] or map[key] or_expr OrExpr pub mut: left Expr left_type Type // array, map, fixed array is_setter bool is_map bool is_array bool is_farray bool is_option bool // IfGuard } pub struct IfExpr { pub: is_comptime bool tok_kind token.Kind left Expr // `a` in `a := if ...` pos token.Position post_comments []Comment pub mut: branches []IfBranch // includes all `else if` branches is_expr bool typ Type has_else bool // implements bool // comptime $if implements interface } pub struct IfBranch { pub: cond Expr pos token.Position body_pos token.Position comments []Comment pub mut: pkg_exist bool stmts []Stmt scope &Scope } pub struct UnsafeExpr { pub: expr Expr pos token.Position } pub struct LockExpr { pub: stmts []Stmt is_rlock []bool pos token.Position pub mut: lockeds []Expr // `x`, `y.z` in `lock x, y.z {` comments []Comment is_expr bool typ Type scope &Scope } pub struct MatchExpr { pub: tok_kind token.Kind cond Expr branches []MatchBranch pos token.Position comments []Comment // comments before the first branch pub mut: is_expr bool // returns a value return_type Type cond_type Type // type of `x` in `match x {` expected_type Type // for debugging only is_sum_type bool } pub struct MatchBranch { pub: ecmnts [][]Comment // inline comments for each left side expr pos token.Position is_else bool post_comments []Comment // comments below ´... }´ branch_pos token.Position // for checker errors about invalid branches pub mut: stmts []Stmt // right side exprs []Expr // left side scope &Scope } pub struct SelectExpr { pub: branches []SelectBranch pos token.Position has_exception bool pub mut: is_expr bool // returns a value expected_type Type // for debugging only } pub struct SelectBranch { pub: stmt Stmt // `a := <-ch` or `ch <- a` stmts []Stmt // right side pos token.Position comment Comment // comment above `select {` is_else bool is_timeout bool post_comments []Comment } pub enum CompForKind { methods fields attributes } pub struct CompFor { pub: val_var string stmts []Stmt kind CompForKind pos token.Position typ_pos token.Position pub mut: // expr Expr typ Type } pub struct ForStmt { pub: cond Expr stmts []Stmt is_inf bool // `for {}` pos token.Position pub mut: label string // `label: for {` scope &Scope } pub struct ForInStmt { pub: key_var string val_var string cond Expr is_range bool high Expr // `10` in `for i in 0..10 {` stmts []Stmt pos token.Position val_is_mut bool // `for mut val in vals {` means that modifying `val` will modify the array // and the array cannot be indexed inside the loop pub mut: key_type Type val_type Type cond_type Type kind Kind // array/map/string label string // `label: for {` scope &Scope } pub struct ForCStmt { pub: init Stmt // i := 0; has_init bool cond Expr // i < 10; has_cond bool inc Stmt // i++; i += 2 has_inc bool is_multi bool // for a,b := 0,1; a < 10; a,b = a+b, a {...} stmts []Stmt pos token.Position pub mut: label string // `label: for {` scope &Scope } // #include, #define etc pub struct HashStmt { pub: mod string pos token.Position source_file string pub mut: val string // example: 'include # please install openssl // comment' kind string // : 'include' main string // : '' msg string // : 'please install openssl' ct_conds []Expr // *all* comptime conditions, that must be true, for the hash to be processed // ct_conds is filled by the checker, based on the current nesting of `$if cond1 {}` blocks } /* // filter(), map(), sort() pub struct Lambda { pub: name string } */ // variable assign statement pub struct AssignStmt { pub: op token.Kind // include: =,:=,+=,-=,*=,/= and so on; for a list of all the assign operators, see vlib/token/token.v pos token.Position comments []Comment end_comments []Comment pub mut: right []Expr left []Expr left_types []Type right_types []Type is_static bool // for translated code only is_volatile bool // for disabling variable access optimisations (needed for hardware drivers) is_simple bool // `x+=2` in `for x:=1; ; x+=2` has_cross_var bool } // `expr as Ident` pub struct AsCast { pub: expr Expr // from expr: `expr` in `expr as Ident` typ Type // to type pos token.Position pub mut: expr_type Type // from type } // an enum value, like OS.macos or .macos pub struct EnumVal { pub: enum_name string val string mod string // for full path `mod_Enum_val` pos token.Position pub mut: typ Type } // enum field in enum declaration pub struct EnumField { pub: name string pos token.Position comments []Comment // comment after Enumfield in the same line next_comments []Comment // comments between current EnumField and next EnumField expr Expr // the value of current EnumField; 123 in `ename = 123` has_expr bool // true, when .expr has a value } // enum declaration pub struct EnumDecl { pub: name string is_pub bool is_flag bool // true when the enum has [flag] tag,for bit field enum is_multi_allowed bool // true when the enum has [_allow_multiple_values] tag comments []Comment // comments before the first EnumField fields []EnumField // all the enum fields attrs []Attr // attributes of enum declaration pos token.Position } pub struct AliasTypeDecl { pub: name string is_pub bool parent_type Type pos token.Position type_pos token.Position comments []Comment } // New implementation of sum types pub struct SumTypeDecl { pub: name string is_pub bool pos token.Position comments []Comment typ Type generic_types []Type pub mut: variants []TypeNode } pub struct FnTypeDecl { pub: name string is_pub bool typ Type pos token.Position type_pos token.Position comments []Comment } // TODO: handle this differently // v1 excludes non current os ifdefs so // the defer's never get added in the first place pub struct DeferStmt { pub: stmts []Stmt pos token.Position pub mut: defer_vars []Ident ifdef string idx_in_fn int = -1 // index in FnDecl.defer_stmts } // `(3+4)` pub struct ParExpr { pub: expr Expr pos token.Position } pub struct GoExpr { pub: pos token.Position pub mut: call_expr CallExpr is_expr bool } pub struct GotoLabel { pub: name string pos token.Position } pub struct GotoStmt { pub: name string pos token.Position } pub struct ArrayInit { pub: pos token.Position // `[]` in []Type{} position elem_type_pos token.Position // `Type` in []Type{} position exprs []Expr // `[expr, expr]` or `[expr]Type{}` for fixed array ecmnts [][]Comment // optional iembed comments after each expr pre_cmnts []Comment is_fixed bool has_val bool // fixed size literal `[expr, expr]!` mod string len_expr Expr // len: expr cap_expr Expr // cap: expr default_expr Expr // init: expr has_len bool has_cap bool has_default bool pub mut: expr_types []Type // [Dog, Cat] // also used for interface_types elem_type Type // element type typ Type // array type } pub struct ArrayDecompose { pub: expr Expr pos token.Position pub mut: expr_type Type arg_type Type } pub struct ChanInit { pub: pos token.Position cap_expr Expr has_cap bool pub mut: typ Type elem_type Type } pub struct MapInit { pub: pos token.Position keys []Expr vals []Expr comments [][]Comment // comments after key-value pairs pre_cmnts []Comment // comments before the first key-value pair pub mut: typ Type key_type Type value_type Type } // s[10..20] pub struct RangeExpr { pub: low Expr high Expr has_high bool has_low bool pos token.Position } pub struct CastExpr { pub: arg Expr // `n` in `string(buf, n)` pub mut: typ Type // `string` expr Expr // `buf` in `string(buf, n)` and `&Type(buf)` typname string // `&Type` in `&Type(buf)` expr_type Type // `byteptr`, the type of the `buf` expression has_arg bool // true for `string(buf, n)`, false for `&Type(buf)` pos token.Position } pub struct AsmStmt { pub: arch pref.Arch is_basic bool is_volatile bool is_goto bool clobbered []AsmClobbered pos token.Position pub mut: templates []AsmTemplate scope &Scope output []AsmIO input []AsmIO global_labels []string // labels defined in assembly block, exported with `.globl` local_labels []string // local to the assembly block } pub struct AsmTemplate { pub mut: name string is_label bool // `example_label:` is_directive bool // .globl assembly_function args []AsmArg comments []Comment pos token.Position } // [eax+5] | j | displacement literal (e.g. 123 in [rax + 123] ) | eax | true | `a` | 0.594 | 123 | label_name pub type AsmArg = AsmAddressing | AsmAlias | AsmDisp | AsmRegister | BoolLiteral | CharLiteral | FloatLiteral | IntegerLiteral | string pub struct AsmRegister { pub mut: name string // eax or r12d etc. typ Type size int } pub struct AsmDisp { pub: val string pos token.Position } pub struct AsmAlias { pub: pos token.Position pub mut: name string // a } pub struct AsmAddressing { pub: scale int = -1 // 1, 2, 4, or 8 literal mode AddressingMode pos token.Position pub mut: segment string // fs: displacement AsmArg // 8, 16 or 32 bit literal value base AsmArg // gpr index AsmArg // gpr } // adressing modes: pub enum AddressingMode { invalid displacement // displacement base // base base_plus_displacement // base + displacement index_times_scale_plus_displacement // (index ∗ scale) + displacement base_plus_index_plus_displacement // base + (index ∗ scale) + displacement base_plus_index_times_scale_plus_displacement // base + index + displacement rip_plus_displacement // rip + displacement } pub struct AsmClobbered { pub mut: reg AsmRegister comments []Comment } // : [alias_a] '=r' (a) // this is a comment pub struct AsmIO { pub: alias string // [alias_a] constraint string // '=r' TODO: allow all backends to easily use this with a struct expr Expr // (a) comments []Comment // // this is a comment typ Type pos token.Position } pub const ( // reference: https://en.wikipedia.org/wiki/X86#/media/File:Table_of_x86_Registers_svg.svg // map register size -> register name x86_no_number_register_list = { 8: ['al', 'ah', 'bl', 'bh', 'cl', 'ch', 'dl', 'dh', 'bpl', 'sil', 'dil', 'spl'] 16: ['ax', 'bx', 'cx', 'dx', 'bp', 'si', 'di', 'sp', /* segment registers */ 'cs', 'ss', 'ds', 'es', 'fs', 'gs', 'flags', 'ip', /* task registers */ 'gdtr', 'idtr', 'tr', 'ldtr', // CSR register 'msw', /* FP core registers */ 'cw', 'sw', 'tw', 'fp_ip', 'fp_dp', 'fp_cs', 'fp_ds', 'fp_opc'] 32: [ 'eax', 'ebx', 'ecx', 'edx', 'ebp', 'esi', 'edi', 'esp', 'eflags', 'eip', /* CSR register */ 'mxcsr' /* 32-bit FP core registers 'fp_dp', 'fp_ip' (TODO: why are there duplicates?) */, ] 64: ['rax', 'rbx', 'rcx', 'rdx', 'rbp', 'rsi', 'rdi', 'rsp', 'rflags', 'rip'] } // no comments because maps do not support comments // r#*: gp registers added in 64-bit extensions, can only be from 8-15 actually // *mm#: vector/simd registors // st#: floating point numbers // cr#: control/status registers // dr#: debug registers x86_with_number_register_list = { 8: { 'r#b': 16 } 16: { 'r#w': 16 } 32: { 'r#d': 16 } 64: { 'r#': 16 'mm#': 16 'cr#': 16 'dr#': 16 } 80: { 'st#': 16 } 128: { 'xmm#': 32 } 256: { 'ymm#': 32 } 512: { 'zmm#': 32 } } ) // TODO: saved priviled registers for arm pub const ( arm_no_number_register_list = ['fp' /* aka r11 */, /* not instruction pointer: */ 'ip' /* aka r12 */, 'sp' /* aka r13 */, 'lr' /* aka r14 */, /* this is instruction pointer ('program counter'): */ 'pc' /* aka r15 */, ] // 'cpsr' and 'apsr' are special flags registers, but cannot be referred to directly arm_with_number_register_list = { 'r#': 16 } ) pub const ( riscv_no_number_register_list = ['zero', 'ra', 'sp', 'gp', 'tp'] riscv_with_number_register_list = { 'x#': 32 't#': 3 's#': 12 'a#': 8 } ) pub struct AssertStmt { pub: pos token.Position pub mut: expr Expr is_used bool // asserts are used in _test.v files, as well as in non -prod builds of all files } // `if [x := opt()] {` pub struct IfGuardExpr { pub: var_name string pos token.Position pub mut: expr Expr expr_type Type } pub enum OrKind { absent block propagate } // `or { ... }` pub struct OrExpr { pub: stmts []Stmt kind OrKind pos token.Position } /* // `or { ... }` pub struct OrExpr2 { pub: call_expr CallExpr stmts []Stmt // inside `or { }` kind OrKind pos token.Position } */ // deprecated pub struct Assoc { pub: var_name string fields []string exprs []Expr pos token.Position pub mut: typ Type scope &Scope } pub struct SizeOf { pub: is_type bool expr Expr // checker uses this to set typ pos token.Position pub mut: typ Type } pub struct IsRefType { pub: is_type bool expr Expr // checker uses this to set typ pos token.Position pub mut: typ Type } pub struct OffsetOf { pub: struct_type Type field string pos token.Position } pub struct Likely { pub: expr Expr pos token.Position is_likely bool // false for _unlikely_ } pub struct TypeOf { pub: expr Expr pos token.Position pub mut: expr_type Type } pub struct DumpExpr { pub: expr Expr pos token.Position pub mut: expr_type Type cname string // filled in the checker } pub struct Comment { pub: text string is_multi bool // true only for /* comment */, that use many lines is_inline bool // true for all /* comment */ comments pos token.Position } pub struct ConcatExpr { pub: vals []Expr pos token.Position pub mut: return_type Type } // @FN, @STRUCT, @MOD etc. See full list in token.valid_at_tokens pub struct AtExpr { pub: name string pos token.Position kind token.AtKind pub mut: val string } pub struct ComptimeSelector { pub: has_parens bool // if $() is used, for vfmt left Expr field_expr Expr pos token.Position pub mut: left_type Type typ Type } pub struct ComptimeCall { pub: pos token.Position has_parens bool // if $() is used, for vfmt method_name string method_pos token.Position scope &Scope left Expr args_var string // is_vweb bool vweb_tmpl File // is_embed bool embed_file EmbeddedFile // is_env bool env_pos token.Position // is_pkgconfig bool pub mut: sym TypeSymbol result_type Type env_value string args []CallArg } pub struct None { pub: pos token.Position } pub enum SqlStmtKind { insert update delete create drop } pub struct SqlStmt { pub: pos token.Position db_expr Expr // `db` in `sql db {` pub mut: lines []SqlStmtLine } pub struct SqlStmtLine { pub: kind SqlStmtKind pos token.Position where_expr Expr update_exprs []Expr // for `update` pub mut: object_var_name string // `user` updated_columns []string // for `update set x=y` table_expr TypeNode fields []StructField sub_structs map[int]SqlStmtLine } pub struct SqlExpr { pub: typ Type is_count bool db_expr Expr // `db` in `sql db {` has_where bool has_offset bool offset_expr Expr has_order bool order_expr Expr has_desc bool is_array bool pos token.Position has_limit bool limit_expr Expr pub mut: where_expr Expr table_expr TypeNode fields []StructField sub_structs map[int]SqlExpr } pub struct NodeError { pub: idx int // index for referencing the related File error pos token.Position } [inline] pub fn (expr Expr) is_blank_ident() bool { match expr { Ident { return expr.kind == .blank_ident } else { return false } } } pub fn (expr Expr) position() token.Position { // all uncommented have to be implemented // NB: please do not print here. the language server will hang // as it uses STDIO primarly to communicate ~Ned match expr { AnonFn { return expr.decl.pos } CTempVar, EmptyExpr { // println('compiler bug, unhandled EmptyExpr position()') return token.Position{} } NodeError, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, ComptimeCall, ComptimeSelector, EnumVal, DumpExpr, FloatLiteral, GoExpr, Ident, IfExpr, IntegerLiteral, IsRefType, Likely, LockExpr, MapInit, MatchExpr, None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit, TypeNode, TypeOf, UnsafeExpr { return expr.pos } IndexExpr { if expr.or_expr.kind != .absent { return expr.or_expr.pos } return expr.pos } IfGuardExpr { return expr.expr.position() } InfixExpr { left_pos := expr.left.position() right_pos := expr.right.position() return token.Position{ line_nr: expr.pos.line_nr pos: left_pos.pos len: right_pos.pos - left_pos.pos + right_pos.len col: left_pos.col last_line: right_pos.last_line } } // Please, do NOT use else{} here. // This match is exhaustive *on purpose*, to help force // maintaining/implementing proper .pos fields. } } pub fn (expr Expr) is_lvalue() bool { match expr { Ident { return true } CTempVar { return true } IndexExpr { return expr.left.is_lvalue() } SelectorExpr { return expr.expr.is_lvalue() } ParExpr { return expr.expr.is_lvalue() } // for var := &{...(*pointer_var)} PrefixExpr { return expr.right.is_lvalue() } else {} } return false } pub fn (expr Expr) is_expr() bool { match expr { IfExpr { return expr.is_expr } LockExpr { return expr.is_expr } MatchExpr { return expr.is_expr } SelectExpr { return expr.is_expr } else {} } return true } pub fn (expr Expr) is_lit() bool { return match expr { BoolLiteral, CharLiteral, StringLiteral, IntegerLiteral { true } else { false } } } pub fn (expr Expr) is_auto_deref_var() bool { match expr { Ident { if expr.obj is Var { if expr.obj.is_auto_deref { return true } } } PrefixExpr { if expr.op == .amp && expr.right.is_auto_deref_var() { return true } } else {} } return false } // returns if an expression can be used in `lock x, y.z {` pub fn (e &Expr) is_lockable() bool { match e { Ident { return true } SelectorExpr { return e.expr.is_lockable() } else { return false } } } // check if stmt can be an expression in C pub fn (stmt Stmt) check_c_expr() ? { match stmt { AssignStmt { return } ExprStmt { if stmt.expr.is_expr() { return } return error('unsupported statement (`$stmt.expr.type_name()`)') } else {} } return error('unsupported statement (`$stmt.type_name()`)') } // CTempVar is used in cgen only, to hold nodes for temporary variables pub struct CTempVar { pub: name string // the name of the C temporary variable; used by g.expr(x) orig Expr // the original expression, which produced the C temp variable; used by x.str() typ Type // the type of the original expression is_ptr bool // whether the type is a pointer } pub fn (node Node) position() token.Position { match node { NodeError { return token.Position{} } EmptyNode { return token.Position{} } Stmt { mut pos := node.pos if node is Import { for sym in node.syms { pos = pos.extend(sym.pos) } } else if node is TypeDecl { match node { FnTypeDecl, AliasTypeDecl { pos = pos.extend(node.type_pos) } SumTypeDecl { for variant in node.variants { pos = pos.extend(variant.pos) } } } } if node is AssignStmt { return pos.extend(node.right.last().position()) } if node is AssertStmt { return pos.extend(node.expr.position()) } return pos } Expr { return node.position() } StructField { return node.pos.extend(node.type_pos) } MatchBranch, SelectBranch, EnumField, ConstField, StructInitField, GlobalField, CallArg { return node.pos } Param { return node.pos.extend(node.type_pos) } IfBranch { return node.pos.extend(node.body_pos) } ScopeObject { match node { ConstField, GlobalField, Var { return node.pos } AsmRegister { return token.Position{ len: -1 line_nr: -1 pos: -1 last_line: -1 col: -1 } } } } File { mut pos := token.Position{} if node.stmts.len > 0 { first_pos := node.stmts.first().pos last_pos := node.stmts.last().pos pos = first_pos.extend_with_last_line(last_pos, last_pos.line_nr) } return pos } } } pub fn (node Node) children() []Node { mut children := []Node{} if node is Expr { match node { StringInterLiteral, Assoc, ArrayInit { return node.exprs.map(Node(it)) } SelectorExpr, PostfixExpr, UnsafeExpr, AsCast, ParExpr, IfGuardExpr, SizeOf, Likely, TypeOf, ArrayDecompose { children << node.expr } LockExpr, OrExpr { return node.stmts.map(Node(it)) } StructInit { return node.fields.map(Node(it)) } AnonFn { children << Stmt(node.decl) } CallExpr { children << node.left children << node.args.map(Node(it)) children << Expr(node.or_block) } InfixExpr { children << node.left children << node.right } PrefixExpr { children << node.right } IndexExpr { children << node.left children << node.index } IfExpr { children << node.left children << node.branches.map(Node(it)) } MatchExpr { children << node.cond children << node.branches.map(Node(it)) } SelectExpr { return node.branches.map(Node(it)) } ChanInit { children << node.cap_expr } MapInit { children << node.keys.map(Node(it)) children << node.vals.map(Node(it)) } RangeExpr { children << node.low children << node.high } CastExpr { children << node.expr children << node.arg } ConcatExpr { return node.vals.map(Node(it)) } ComptimeCall, ComptimeSelector { children << node.left } else {} } } else if node is Stmt { match node { Block, DeferStmt, ForCStmt, ForInStmt, ForStmt, CompFor { return node.stmts.map(Node(it)) } ExprStmt, AssertStmt { children << node.expr } InterfaceDecl { children << node.methods.map(Node(Stmt(it))) children << node.fields.map(Node(it)) } AssignStmt { children << node.left.map(Node(it)) children << node.right.map(Node(it)) } Return { return node.exprs.map(Node(it)) } // NB: these four decl nodes cannot be merged as one branch StructDecl { return node.fields.map(Node(it)) } GlobalDecl { return node.fields.map(Node(it)) } ConstDecl { return node.fields.map(Node(it)) } EnumDecl { return node.fields.map(Node(it)) } FnDecl { if node.is_method { children << Node(node.receiver) } children << node.params.map(Node(it)) children << node.stmts.map(Node(it)) } TypeDecl { if node is SumTypeDecl { children << node.variants.map(Node(Expr(it))) } } else {} } } else if node is ScopeObject { match node { GlobalField, ConstField, Var { children << node.expr } AsmRegister {} } } else { match node { GlobalField, ConstField, EnumField, StructInitField, CallArg { children << node.expr } SelectBranch { children << node.stmt children << node.stmts.map(Node(it)) } IfBranch, File { return node.stmts.map(Node(it)) } MatchBranch { children << node.stmts.map(Node(it)) children << node.exprs.map(Node(it)) } else {} } } return children } // helper for dealing with `m[k1][k2][k3][k3] = value` pub fn (mut lx IndexExpr) recursive_mapset_is_setter(val bool) { lx.is_setter = val if mut lx.left is IndexExpr { if lx.left.is_map { lx.left.recursive_mapset_is_setter(val) } } } // return all the registers for the given architecture pub fn all_registers(mut t Table, arch pref.Arch) map[string]ScopeObject { mut res := map[string]ScopeObject{} match arch { .amd64, .i386 { for bit_size, array in ast.x86_no_number_register_list { for name in array { res[name] = AsmRegister{ name: name typ: t.bitsize_to_type(bit_size) size: bit_size } } } for bit_size, array in ast.x86_with_number_register_list { for name, max_num in array { for i in 0 .. max_num { hash_index := name.index('#') or { panic('all_registers: no hashtag found') } assembled_name := '${name[..hash_index]}$i${name[hash_index + 1..]}' res[assembled_name] = AsmRegister{ name: assembled_name typ: t.bitsize_to_type(bit_size) size: bit_size } } } } } .arm32 { arm32 := gen_all_registers(mut t, ast.arm_no_number_register_list, ast.arm_with_number_register_list, 32) for k, v in arm32 { res[k] = v } } .arm64 { arm64 := gen_all_registers(mut t, ast.arm_no_number_register_list, ast.arm_with_number_register_list, 64) for k, v in arm64 { res[k] = v } } .rv32 { rv32 := gen_all_registers(mut t, ast.riscv_no_number_register_list, ast.riscv_with_number_register_list, 32) for k, v in rv32 { res[k] = v } } .rv64 { rv64 := gen_all_registers(mut t, ast.riscv_no_number_register_list, ast.riscv_with_number_register_list, 64) for k, v in rv64 { res[k] = v } } else { // TODO panic('all_registers: unhandled arch') } } return res } // only for arm and riscv because x86 has different sized registers fn gen_all_registers(mut t Table, without_numbers []string, with_numbers map[string]int, bit_size int) map[string]ScopeObject { mut res := map[string]ScopeObject{} for name in without_numbers { res[name] = AsmRegister{ name: name typ: t.bitsize_to_type(bit_size) size: bit_size } } for name, max_num in with_numbers { for i in 0 .. max_num { hash_index := name.index('#') or { panic('all_registers: no hashtag found') } assembled_name := '${name[..hash_index]}$i${name[hash_index + 1..]}' res[assembled_name] = AsmRegister{ name: assembled_name typ: t.bitsize_to_type(bit_size) size: bit_size } } } return res } // is `expr` a literal, i.e. it does not depend on any other declarations (C compile time constant) pub fn (expr Expr) is_literal() bool { match expr { BoolLiteral, CharLiteral, FloatLiteral, IntegerLiteral { return true } PrefixExpr { return expr.right.is_literal() } InfixExpr { return expr.left.is_literal() && expr.right.is_literal() } ParExpr { return expr.expr.is_literal() } CastExpr { return !expr.has_arg && expr.expr.is_literal() && (expr.typ.is_ptr() || expr.typ.is_pointer() || expr.typ in [i8_type, i16_type, int_type, i64_type, byte_type, u8_type, u16_type, u32_type, u64_type, f32_type, f64_type, char_type, bool_type, rune_type]) } SizeOf, IsRefType { return expr.is_type || expr.expr.is_literal() } else { return false } } }