diff options
Diffstat (limited to 'v_windows/v/old/vlib/v/ast/ast.v')
-rw-r--r-- | v_windows/v/old/vlib/v/ast/ast.v | 2020 |
1 files changed, 2020 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/v/ast/ast.v b/v_windows/v/old/vlib/v/ast/ast.v new file mode 100644 index 0000000..b2e1e3b --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/ast.v @@ -0,0 +1,2020 @@ +// 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 + +// NB: when you add a new Expr or Stmt type with a .pos field, remember to update +// the .position() token.Position methods too. +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 +} + +// `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` + 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 root is SelectorExpr { + // TODO: remove this line + selector_expr := root as SelectorExpr + root = selector_expr.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 + 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 + 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 + // + 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. <int, string> + 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 +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_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 + stmts []Stmt // right side + pos token.Position + is_else bool + post_comments []Comment // comments below ´... }´ + branch_pos token.Position // for checker errors about invalid branches +pub mut: + 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 <openssl/rand.h> # please install openssl // comment' + kind string // : 'include' + main string // : '<openssl/rand.h>' + 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_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 +} + +// NB: &string(x) gets parsed as PrefixExpr{ right: CastExpr{...} } +// TODO: that is very likely a parsing bug. It should get parsed as just +// CastExpr{...}, where .typname is '&string' instead. +// The current situation leads to special cases in vfmt and cgen +// (see prefix_expr_cast_expr in fmt.v, and .is_amp in cgen.v) +// .in_prexpr is also needed because of that, because the checker needs to +// show warnings about the deprecated C->V conversions `string(x)` and +// `string(x,y)`, while skipping the real pointer casts like `&string(x)`. +// 2021/07/17: TODO: since 6edfb2c, the above is fixed at the parser level, +// we need to remove the hacks/special cases in vfmt and the checker too. +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: + 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 = map{ + 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 = map{ + 8: map{ + 'r#b': 16 + } + 16: map{ + 'r#w': 16 + } + 32: map{ + 'r#d': 16 + } + 64: map{ + 'r#': 16 + 'mm#': 16 + 'cr#': 16 + 'dr#': 16 + } + 80: map{ + 'st#': 16 + } + 128: map{ + 'xmm#': 32 + } + 256: map{ + 'ymm#': 32 + } + 512: map{ + '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 = map{ + 'r#': 16 + } +) + +pub const ( + riscv_no_number_register_list = ['zero', 'ra', 'sp', 'gp', 'tp'] + riscv_with_number_register_list = map{ + '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 + } + 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 + } + } + CTempVar { + return token.Position{} + } + // 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 a give 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 + } + } +} |