1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00
v/vlib/v/ast/ast.v

2457 lines
56 KiB
V
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 2019-2023 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
| ComptimeType
| ConcatExpr
| DumpExpr
| EmptyExpr
| EnumVal
| FloatLiteral
| GoExpr
| Ident
| IfExpr
| IfGuardExpr
| IndexExpr
| InfixExpr
| IntegerLiteral
| IsRefType
| Likely
| LockExpr
| MapInit
| MatchExpr
| Nil
| NodeError
| None
| OffsetOf
| OrExpr
| ParExpr
| PostfixExpr
| PrefixExpr
| RangeExpr
| SelectExpr
| SelectorExpr
| SizeOf
| SpawnExpr
| SqlExpr
| StringInterLiteral
| StringLiteral
| StructInit
| TypeNode
| TypeOf
| UnsafeExpr
pub type Stmt = AsmStmt
| AssertStmt
| AssignStmt
| Block
| BranchStmt
| ComptimeFor
| 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:
pos token.Pos
pub mut:
typ Type
end_comments []Comment // comments that after current type node
}
pub enum ComptimeTypeKind {
map_
int
float
struct_
iface
array
sum_type
enum_
alias
function
option
}
pub struct ComptimeType {
pub:
kind ComptimeTypeKind
pos token.Pos
}
pub fn (cty ComptimeType) str() string {
return match cty.kind {
.map_ { '\$map' }
.int { '\$int' }
.float { '\$float' }
.struct_ { '\$struct' }
.iface { '\$interface' }
.array { '\$array' }
.sum_type { '\$sumtype' }
.enum_ { '\$enum' }
.alias { '\$alias' }
.function { '\$function' }
.option { '\$option' }
}
}
pub type EmptyExpr = u8
pub struct EmptyStmt {
pub:
pos token.Pos
}
pub struct EmptyNode {
pub:
pos token.Pos
}
pub const (
empty_expr = Expr(EmptyExpr(0))
empty_stmt = Stmt(EmptyStmt{})
empty_node = Node(EmptyNode{})
)
// `{stmts}` or `unsafe {stmts}`
pub struct Block {
pub:
is_unsafe bool
pos token.Pos
pub mut:
stmts []Stmt
}
// | IncDecStmt k
// Stand-alone expression in a statement list.
pub struct ExprStmt {
pub:
pos token.Pos
comments []Comment
pub mut:
expr Expr
is_expr bool
typ Type
}
pub struct IntegerLiteral {
pub:
val string
pos token.Pos
}
pub struct FloatLiteral {
pub:
val string
pos token.Pos
}
[minify]
pub struct StringLiteral {
pub:
val string
is_raw bool
language Language
pos token.Pos
}
// 'name: $name'
pub struct StringInterLiteral {
pub:
vals []string
fwidths []int
precisions []int
pluss []bool
fills []bool
fmt_poss []token.Pos
pos token.Pos
pub mut:
exprs []Expr
expr_types []Type
fmts []u8
need_fmts []bool // an explicit non-default fmt required, e.g. `x`
}
pub struct CharLiteral {
pub:
val string
pos token.Pos
}
pub struct BoolLiteral {
pub:
val bool
pos token.Pos
}
pub struct Nil {
pub:
pos token.Pos
}
pub enum GenericKindField {
unknown
name
typ
}
// `foo.bar`
[minify]
pub struct SelectorExpr {
pub:
pos token.Pos
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.Pos
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`
or_block OrExpr
gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown
scope &Scope = unsafe { nil }
from_embed_types []Type // holds the type of the embed that the method is called from
has_hidden_receiver bool
}
// 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 mut root is Ident {
return root
}
return none
}
// module declaration
pub struct Module {
pub:
name string // encoding.base64
short_name string // base64
attrs []Attr
pos token.Pos
name_pos token.Pos // `name` in import name
is_skipped bool // module main can be skipped in single file programs
}
[minify]
pub struct StructField {
pub:
pos token.Pos
type_pos token.Pos
option_pos token.Pos
comments []Comment
i int
has_default_expr bool
attrs []Attr
is_pub bool
default_val string
is_mut bool
is_global bool
is_volatile bool
is_deprecated bool
pub mut:
is_recursive bool
default_expr Expr
default_expr_typ Type
name string
typ Type
unaliased_typ Type
anon_struct_decl StructDecl // only if the field is an anonymous struct
}
pub fn (f &StructField) equals(o &StructField) bool {
// TODO: f.is_mut == o.is_mut was removed here to allow read only access
// to (mut/not mut), but otherwise equal fields; some other new checks are needed:
// - if node is declared mut, and we mutate node.stmts, all stmts fields must be mutable
// - same goes for pub and global, if we call the field from another module
return f.name == o.name && f.typ == o.typ && f.is_pub == o.is_pub && f.is_global == o.is_global
}
// const field in const declaration group
pub struct ConstField {
pub:
mod string
name string
is_pub bool
is_markused bool // an explicit `[markused]` tag; the const will NOT be removed by `-skip-unused`, no matter what
pos token.Pos
pub mut:
expr Expr // the value expr of field; everything after `=`
typ Type // the type of the const field, it can be any type in V
comments []Comment // comments before current const field
end_comments []Comment // comments that after 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
[minify]
pub struct ConstDecl {
pub:
is_pub bool
pos token.Pos
attrs []Attr // tags like `[markused]`, valid for all the consts in the list
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
}
[minify]
pub struct StructDecl {
pub:
pos token.Pos
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.Pos
comments []Comment
}
pub struct InterfaceEmbedding {
pub:
name string
typ Type
pos token.Pos
comments []Comment
}
[minify]
pub struct InterfaceDecl {
pub:
name string
typ Type
name_pos token.Pos
language Language
field_names []string
is_pub bool
mut_pos int // mut:
pos token.Pos
pre_comments []Comment
generic_types []Type
attrs []Attr
pub mut:
methods []FnDecl
fields []StructField
embeds []InterfaceEmbedding
are_embeds_expanded bool
}
// `field1: val1`
pub struct StructInitField {
pub:
pos token.Pos
name_pos token.Pos
comments []Comment
next_comments []Comment
pub mut:
expr Expr // `val1`
name string // 'field1'
typ Type // the type of this field
expected_type Type
parent_type Type
}
// `s := Foo{
// ...a
// field1: 'hello'
// }`
[minify]
pub struct StructInit {
pub:
pos token.Pos
name_pos token.Pos
no_keys bool // `Foo{val1, val2}`
is_short_syntax bool // `foo(field1: val1, field2: val2)`
is_anon bool // `x: struct{ foo: bar }`
pub mut:
unresolved bool
pre_comments []Comment
typ_str string // 'Foo'
typ Type // the type of this struct
update_expr Expr // `a` in `...a`
update_expr_type Type
update_expr_pos token.Pos
update_expr_comments []Comment
is_update_embed bool
has_update_expr bool // has `...a`
init_fields []StructInitField
generic_types []Type
}
pub enum StructInitKind {
normal
short_syntax
anon
}
// 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.Pos
mod_pos token.Pos
alias_pos token.Pos
syms_pos token.Pos
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.Pos
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 map[string]bool // has been generated
}
// function or method declaration
[minify]
pub struct FnDecl {
pub:
name string // 'math.bits.normalize'
short_name string // 'normalize'
mod string // 'math.bits'
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() {}`, false for `fn test_abc(x int) {}`, or for fns that do not start with test_
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
is_markused bool // true, when an explicit `[markused]` tag was put on a fn; `-skip-unused` will not remove that fn
is_file_translated bool // true, when the file it resides in is `[translated]`
receiver StructField // TODO this is not a struct field
receiver_pos token.Pos // `(u User)` in `fn (u User) name()` position
is_method bool
is_static_type_method bool // true for `fn Foo.bar() {}`
method_type_pos token.Pos // `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.Pos // 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:
idx int // index in an external container; can be used to refer to the function in a more efficient way, just by its integer index
params []Param
stmts []Stmt
defer_stmts []DeferStmt
return_type Type
return_type_pos token.Pos // `string` in `fn (u User) name() string` position
has_return bool
should_be_skipped bool // true, when -skip-unused could not find any usages of that function, starting from main + other known used functions
ninstances int // 0 for generic functions with no concrete instances
has_await bool // 'true' if this function uses JS.await
comments []Comment // comments *after* the header, but *before* `{`; used for InterfaceDecl
end_comments []Comment // comments *after* header declarations. E.g.: `fn C.C_func(x int) int // Comment`
next_comments []Comment // comments that are one line after the decl; used for InterfaceDecl
source_file &File = unsafe { nil }
scope &Scope = unsafe { nil }
label_names []string
pos token.Pos // function declaration position
}
pub fn (f &FnDecl) new_method_with_receiver_type(new_type Type) FnDecl {
unsafe {
mut new_method := f
new_method.params = f.params.clone()
for i in 1 .. new_method.params.len {
if new_method.params[i].typ == new_method.params[0].typ {
new_method.params[i].typ = new_type
}
}
new_method.params[0].typ = new_type
return *new_method
}
}
[minify]
pub struct Fn {
pub:
is_variadic bool
language Language
is_pub bool
is_ctor_new bool // `[use_new] fn JS.Array.prototype.constructor()`
is_deprecated bool // `[deprecated] fn abc(){}`
is_noreturn bool // `[noreturn] fn abc(){}`
is_unsafe bool // `[unsafe] fn abc(){}`
is_placeholder bool
is_main bool // `fn main(){}`
is_test bool // `fn test_abc(){}`
is_keep_alive bool // passed memory must not be freed (by GC) before function returns
is_method bool // true for `fn (x T) name()`, and for interface declarations (which are also for methods)
is_static_type_method bool // true for `fn Foo.bar() {}`
no_body bool // a pure declaration like `fn abc(x int)`; used in .vh files, C./JS. fns.
is_file_translated bool // true, when the file it resides in is `[translated]`
mod string
file string
file_mode Language
pos token.Pos
return_type_pos token.Pos
pub mut:
return_type Type
receiver_type Type // != 0, when .is_method == true
name string
params []Param
source_fn voidptr // set in the checker, while processing fn declarations // TODO get rid of voidptr
usages int
generic_names []string
dep_names []string // globals or consts dependent names
attrs []Attr // all fn attributes
is_conditional bool // true for `[if abc]fn(){}`
ctdefine_idx int // the index of the attribute, containing the compile time define [if mytag]
}
fn (f &Fn) method_equals(o &Fn) bool {
return f.params[1..].equals(o.params[1..]) && f.return_type == o.return_type
&& f.is_variadic == o.is_variadic && f.language == o.language
&& f.generic_names == o.generic_names && f.is_pub == o.is_pub && f.mod == o.mod
&& f.name == o.name
}
[minify]
pub struct Param {
pub:
pos token.Pos
name string
is_mut bool
is_shared bool
is_atomic bool
is_auto_rec bool
type_pos token.Pos
is_hidden bool // interface first arg
pub mut:
typ Type
}
pub fn (p &Param) specifier() string {
if p.is_shared {
return 'shared'
} else if p.is_atomic {
return 'atomic'
} else if p.is_mut {
return 'mut'
} else {
return ''
}
}
pub fn (f &Fn) new_method_with_receiver_type(new_type Type) Fn {
unsafe {
mut new_method := f
new_method.params = f.params.clone()
for i in 1 .. new_method.params.len {
if new_method.params[i].typ == new_method.params[0].typ {
new_method.params[i].typ = new_type
}
}
new_method.params[0].typ = new_type
return *new_method
}
}
fn (p &Param) equals(o &Param) bool {
return p.name == o.name && p.is_mut == o.is_mut && p.typ == o.typ && p.is_hidden == o.is_hidden
}
fn (p []Param) equals(o []Param) bool {
if p.len != o.len {
return false
}
for i in 0 .. p.len {
if !p[i].equals(o[i]) {
return false
}
}
return true
}
// break, continue
[minify]
pub struct BranchStmt {
pub:
kind token.Kind
label string
pos token.Pos
}
// function or method call expr
[minify]
pub struct CallExpr {
pub:
pos token.Pos
name_pos token.Pos
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_fn_var bool // fn variable, `a := fn() {}`, then: `a()`
is_fn_a_const bool // fn const, `const c = abc`, where `abc` is a function, then: `c()`
is_keep_alive bool // GC must not free arguments before fn returns
is_noreturn bool // whether the function/method is marked as [noreturn]
is_ctor_new bool // if JS ctor calls requires `new` before call, marked as `[use_new]` in V
is_file_translated bool // true, when the file it resides in is `[translated]`
args []CallArg
expected_arg_types []Type
comptime_ret_val bool
language Language
or_block OrExpr
left Expr // `user` in `user.register()`
left_type Type // type of `user`
receiver_type Type // User
return_type Type
fn_var_type Type // the fn type, when `is_fn_a_const` or `is_fn_var` is true
const_name string // the fully qualified name of the const, i.e. `main.c`, given `const c = abc`, and callexpr: `c()`
should_be_skipped bool // true for calls to `[if someflag?]` functions, when there is no `-d someflag`
concrete_types []Type // concrete types, e.g. <int, string>
concrete_list_pos token.Pos
raw_concrete_types []Type
free_receiver bool // true if the receiver expression needs to be freed
scope &Scope = unsafe { nil }
from_embed_types []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)`
[minify]
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.Pos
// tmp_name string // for autofree
}
// function return statement
pub struct Return {
pub:
pos token.Pos
comments []Comment
pub mut:
exprs []Expr
types []Type
}
pub enum ComptimeVarKind {
no_comptime // it is not a comptime var
key_var // map key from `for k,v in t.$(field.name)`
value_var // map value from `for k,v in t.$(field.name)`
field_var // comptime field var `a := t.$(field.name)`
generic_param // generic fn parameter
}
[minify]
pub struct Var {
pub:
name string
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
has_inherited bool
pub mut:
expr Expr
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.Pos
is_used bool // whether the local variable was used in other expressions
is_changed bool // to detect mutable vars that are never changed
ct_type_var ComptimeVarKind // comptime variable type
// (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 whose 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
[minify]
pub struct ScopeStructField {
pub:
struct_type Type // type of struct
name string
pos token.Pos
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)
}
[minify]
pub struct GlobalField {
pub:
name string
has_expr bool
pos token.Pos
typ_pos token.Pos
is_markused bool // an explicit `[markused]` tag; the global will NOT be removed by `-skip-unused`
is_volatile bool
pub mut:
expr Expr
typ Type
comments []Comment
}
pub struct GlobalDecl {
pub:
mod string
pos token.Pos
is_block bool // __global() block
attrs []Attr // tags like `[markused]`, valid for all the globals in the list
pub mut:
fields []GlobalField
end_comments []Comment
}
[minify]
pub struct EmbeddedFile {
pub:
compression_type string
pub mut:
rpath string // used in the source code, as an ID/key to the embed
apath string // absolute path during compilation to the resource
// these are set by gen_embed_file_init in v/gen/c/embed
is_compressed bool
bytes []u8
len int
}
// 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 = unsafe { nil }
is_test bool // true for _test.v files
is_generated bool // true for `[generated] module xyz` files; turn off notices
is_translated bool // true for `[translated] module xyz` files; turn off some checks
pub mut:
idx int // index in an external container; can be used to refer to the file in a more efficient way, just by its integer index
path string // absolute path of the source file - '/projects/v/file.v'
path_base string // file name - 'file.v' (useful for tracing)
scope &Scope = unsafe { nil }
stmts []Stmt // all the statements in the source file
imports []Import // all the imports
auto_imports []string // imports that were implicitly 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 }`
template_paths []string // all the .html/.md files that were processed with $tmpl
}
[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
[minify]
pub struct IdentVar {
pub mut:
typ Type
is_mut bool
is_static bool
is_volatile bool
is_option bool
share ShareType
}
pub type IdentInfo = IdentFn | IdentVar
pub enum IdentKind {
unresolved
blank_ident
variable
constant
global
function
}
// A single identifier
[minify]
pub struct Ident {
pub:
language Language
tok_kind token.Kind
pos token.Pos
mut_pos token.Pos
comptime bool
pub mut:
scope &Scope = unsafe { nil }
obj ScopeObject
mod string
name string
kind IdentKind
info IdentInfo
is_mut bool // if mut *token* is before name. Use `is_mut()` to lookup mut variable
or_expr OrExpr
concrete_types []Type
}
pub fn (i &Ident) is_auto_heap() bool {
match i.obj {
Var {
return i.obj.is_auto_heap
}
else {
return false
}
}
}
pub fn (i &Ident) is_mut() bool {
match i.obj {
Var {
return i.obj.is_mut
}
ConstField {
return false
}
AsmRegister, GlobalField {
return true
}
}
}
pub fn (i &Ident) var_info() IdentVar {
match i.info {
IdentVar {
return i.info
}
else {
panic('Ident.var_info(): info is not IdentVar variant')
}
}
}
// left op right
// See: token.Kind.is_infix
[minify]
pub struct InfixExpr {
pub:
op token.Kind
pos token.Pos
is_stmt bool
pub mut:
left Expr
right Expr
left_type Type
right_type Type
promoted_type Type = void_type
auto_locked string
or_block OrExpr
ct_left_value_evaled bool
ct_left_value ComptTimeConstValue = empty_comptime_const_expr()
ct_right_value_evaled bool
ct_right_value ComptTimeConstValue = empty_comptime_const_expr()
before_op_comments []Comment
after_op_comments []Comment
}
// ++, --
pub struct PostfixExpr {
pub:
op token.Kind
pos token.Pos
is_c2v_prefix bool // for `--x` (`x--$`), only for translated code until c2v can handle it
pub mut:
expr Expr
typ Type
auto_locked string
}
// See: token.Kind.is_prefix
[minify]
pub struct PrefixExpr {
pub:
op token.Kind
pos token.Pos
pub mut:
right_type Type
right Expr
or_block OrExpr
is_option bool // IfGuard
}
[minify]
pub struct IndexExpr {
pub:
pos token.Pos
pub mut:
index Expr // [0], RangeExpr [start..end] or map[key]
or_expr OrExpr
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
is_direct bool // Set if the underlying memory can be safely accessed
is_gated bool // #[] gated array
}
[minify]
pub struct IfExpr {
pub:
is_comptime bool
tok_kind token.Kind
pos token.Pos
post_comments []Comment
pub mut:
left Expr // `a` in `a := if ...`
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:
pos token.Pos
body_pos token.Pos
comments []Comment
pub mut:
cond Expr
pkg_exist bool
stmts []Stmt
scope &Scope = unsafe { nil }
}
pub struct UnsafeExpr {
pub:
pos token.Pos
pub mut:
expr Expr
}
pub struct LockExpr {
pub:
is_rlock []bool
pos token.Pos
pub mut:
stmts []Stmt
lockeds []Expr // `x`, `y.z` in `lock x, y.z {`
comments []Comment
is_expr bool
typ Type
scope &Scope = unsafe { nil }
}
[minify]
pub struct MatchExpr {
pub:
tok_kind token.Kind
pos token.Pos
comments []Comment // comments before the first branch
pub mut:
cond Expr
branches []MatchBranch
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.Pos
is_else bool
post_comments []Comment // comments below ´... }´
branch_pos token.Pos // for checker errors about invalid branches
pub mut:
stmts []Stmt // right side
exprs []Expr // left side
scope &Scope = unsafe { nil }
}
pub struct SelectExpr {
pub:
branches []SelectBranch
pos token.Pos
has_exception bool
pub mut:
is_expr bool // returns a value
expected_type Type // for debugging only
}
[minify]
pub struct SelectBranch {
pub:
pos token.Pos
comment Comment // comment above `select {`
is_else bool
is_timeout bool
post_comments []Comment
pub mut:
stmt Stmt // `a := <-ch` or `ch <- a`
stmts []Stmt // right side
}
pub enum ComptimeForKind {
methods
fields
attributes
values
}
pub struct ComptimeFor {
pub:
val_var string
kind ComptimeForKind
pos token.Pos
typ_pos token.Pos
pub mut:
stmts []Stmt
typ Type
}
pub struct ForStmt {
pub:
is_inf bool // `for {}`
pos token.Pos
comments []Comment
pub mut:
cond Expr
stmts []Stmt
label string // `label: for {`
scope &Scope = unsafe { nil }
}
[minify]
pub struct ForInStmt {
pub:
key_var string
val_var string
is_range bool
pos token.Pos
comments []Comment
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:
val_is_ref bool // `for val in &arr {` means that value of `val` will be the reference of the value in `arr`
cond Expr
key_type Type
val_type Type
cond_type Type
high Expr // `10` in `for i in 0..10 {`
high_type Type
kind Kind // array/map/string
label string // `label: for {`
scope &Scope = unsafe { nil }
stmts []Stmt
}
pub struct ForCStmt {
pub:
has_init bool
has_cond bool
has_inc bool
is_multi bool // for a,b := 0,1; a < 10; a,b = a+b, a {...}
pos token.Pos
comments []Comment
pub mut:
init Stmt // i := 0;
cond Expr // i < 10;
inc Stmt // i++; i += 2
stmts []Stmt
label string // `label: for {`
scope &Scope = unsafe { nil }
}
// #include, #define etc
pub struct HashStmt {
pub:
mod string
pos token.Pos
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
[minify]
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.Pos
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:
typ Type // to type
pos token.Pos
pub mut:
expr Expr // from expr: `expr` in `expr as Ident`
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.Pos
pub mut:
typ Type
}
// enum field in enum declaration
pub struct EnumField {
pub:
name string
pos token.Pos
comments []Comment // comment after Enumfield in the same line
next_comments []Comment // comments between current EnumField and next EnumField
has_expr bool // true, when .expr has a value
attrs []Attr
pub mut:
expr Expr // the value of current EnumField; 123 in `ename = 123`
}
// enum declaration
[minify]
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
typ Type // the default is `int`; can be changed by `enum Big as u64 { a = 5 }`
typ_pos token.Pos
pos token.Pos
}
pub struct AliasTypeDecl {
pub:
name string
is_pub bool
typ Type
parent_type Type
pos token.Pos
type_pos token.Pos
comments []Comment
}
// SumTypeDecl is the ast node for `type MySumType = string | int`
pub struct SumTypeDecl {
pub:
name string
is_pub bool
pos token.Pos
name_pos token.Pos
typ Type
generic_types []Type
attrs []Attr // attributes of type declaration
pub mut:
variants []TypeNode
}
pub struct FnTypeDecl {
pub:
name string
is_pub bool
typ Type
pos token.Pos
type_pos token.Pos
comments []Comment
generic_types []Type
attrs []Attr // attributes of type declaration
}
// TODO: handle this differently
// v1 excludes non current os ifdefs so
// the defer's never get added in the first place
[minify]
pub struct DeferStmt {
pub:
pos token.Pos
pub mut:
stmts []Stmt
defer_vars []Ident
ifdef string
idx_in_fn int = -1 // index in FnDecl.defer_stmts
}
// `(3+4)`
pub struct ParExpr {
pub:
pos token.Pos
pub mut:
expr Expr
}
[minify]
pub struct GoExpr {
pub:
pos token.Pos
pub mut:
call_expr CallExpr
is_expr bool
}
[minify]
pub struct SpawnExpr {
pub:
pos token.Pos
pub mut:
call_expr CallExpr
is_expr bool
}
pub struct GotoLabel {
pub:
name string
pos token.Pos
pub mut:
is_used bool
}
pub struct GotoStmt {
pub:
name string
pos token.Pos
}
[minify]
pub struct ArrayInit {
pub:
pos token.Pos // `[]` in []Type{} position
elem_type_pos token.Pos // `Type` in []Type{} position
ecmnts [][]Comment // optional iembed comments after each expr
pre_cmnts []Comment
is_fixed bool
has_val bool // fixed size literal `[expr, expr]!`
mod string
has_len bool
has_cap bool
has_default bool
has_index bool // true if temp variable index is used
pub mut:
exprs []Expr // `[expr, expr]` or `[expr]Type{}` for fixed array
len_expr Expr // len: expr
cap_expr Expr // cap: expr
default_expr Expr // init: expr
expr_types []Type // [Dog, Cat] // also used for interface_types
elem_type Type // element type
default_type Type // default value type
typ Type // array type
}
pub struct ArrayDecompose {
pub:
pos token.Pos
pub mut:
expr Expr
expr_type Type
arg_type Type
}
pub struct ChanInit {
pub:
pos token.Pos
elem_type_pos token.Pos
has_cap bool
pub mut:
cap_expr Expr
typ Type
elem_type Type
}
[minify]
pub struct MapInit {
pub:
pos token.Pos
comments [][]Comment // comments after key-value pairs
pre_cmnts []Comment // comments before the first key-value pair
pub mut:
keys []Expr
vals []Expr
val_types []Type
typ Type
key_type Type
value_type Type
}
// s[10..20]
[minify]
pub struct RangeExpr {
pub:
has_high bool
has_low bool
pos token.Pos
is_gated bool // #[] gated array
pub mut:
low Expr
high Expr
typ Type // filled in by checker; the type of `0...1` is `int` for example, while `a`...`z` is `rune` etc
}
[minify]
pub struct CastExpr {
pub mut:
arg Expr // `n` in `string(buf, n)`
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.Pos
}
[minify]
pub struct AsmStmt {
pub:
arch pref.Arch
is_basic bool
is_volatile bool
is_goto bool
clobbered []AsmClobbered
pos token.Pos
pub mut:
templates []AsmTemplate
scope &Scope = unsafe { nil }
output []AsmIO
input []AsmIO
global_labels []string // labels defined in assembly block, exported with `.globl`
local_labels []string // local to the assembly block
}
[minify]
pub struct AsmTemplate {
pub mut:
name string
is_label bool // `example_label:`
is_directive bool // .globl assembly_function
args []AsmArg
comments []Comment
pos token.Pos
}
// [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.Pos
}
pub struct AsmAlias {
pub:
pos token.Pos
pub mut:
name string // a
}
pub struct AsmAddressing {
pub:
scale int = -1 // 1, 2, 4, or 8 literal
mode AddressingMode
pos token.Pos
pub mut:
segment string // fs:
displacement AsmArg // 8, 16 or 32 bit literal value
base AsmArg // gpr
index AsmArg // gpr
}
// addressing 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
comments []Comment // // this is a comment
typ Type
pos token.Pos
pub mut:
expr Expr // (a)
}
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 registers
// 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
}
)
// `assert a == 0, 'a is zero'`
[minify]
pub struct AssertStmt {
pub:
pos token.Pos
extra_pos token.Pos
pub mut:
expr Expr // `a == 0`
extra Expr // `'a is zero'`
is_used bool // asserts are used in _test.v files, as well as in non -prod builds of all files
}
pub struct IfGuardVar {
pub mut:
name string
is_mut bool
pos token.Pos
}
// `if x := opt() {`
pub struct IfGuardExpr {
pub:
vars []IfGuardVar
pub mut:
expr Expr // `opt()`
expr_type Type // type of `opt()`
}
pub enum OrKind {
absent
block
propagate_option
propagate_result
}
// `or { ... }`
pub struct OrExpr {
pub:
kind OrKind
pos token.Pos
pub mut:
stmts []Stmt
}
/*
// `or { ... }`
pub struct OrExpr2 {
pub:
call_expr CallExpr
stmts []Stmt // inside `or { }`
kind OrKind
pos token.Pos
}
*/
// deprecated
[minify]
pub struct Assoc {
pub:
var_name string
fields []string
pos token.Pos
pub mut:
exprs []Expr
typ Type
scope &Scope = unsafe { nil }
}
pub struct SizeOf {
pub:
guessed_type bool // a legacy `sizeof( GuessedType )` => a deprecation notice, suggesting `v fmt -w .` => `sizeof[ Type ]()`
is_type bool
pos token.Pos
pub mut:
expr Expr // checker uses this to set typ, when !is_type
typ Type
}
pub struct IsRefType {
pub:
guessed_type bool // a legacy `isreftype( GuessedType )` => a deprecation notice, suggesting `v fmt -w .` => `isreftype[ Type ]()`
is_type bool
pos token.Pos
pub mut:
expr Expr // checker uses this to set typ, when !is_type
typ Type
}
[minify]
pub struct OffsetOf {
pub:
struct_type Type
field string
pos token.Pos
}
pub struct Likely {
pub:
pos token.Pos
is_likely bool // false for _unlikely_
pub mut:
expr Expr
}
[minify]
pub struct TypeOf {
pub:
is_type bool
pos token.Pos
pub mut:
expr Expr // checker uses this to set typ, when !is_type
typ Type
}
[minify]
pub struct DumpExpr {
pub:
pos token.Pos
pub mut:
expr Expr
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.Pos
}
pub struct ConcatExpr {
pub:
vals []Expr
pos token.Pos
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.Pos
kind token.AtKind
pub mut:
val string
}
[minify]
pub struct ComptimeSelector {
pub:
has_parens bool // if $() is used, for vfmt
pos token.Pos
pub mut:
left Expr
left_type Type
field_expr Expr
typ Type
}
[minify]
pub struct ComptimeCall {
pub:
pos token.Pos
has_parens bool // if $() is used, for vfmt
method_name string
method_pos token.Pos
scope &Scope = unsafe { nil }
is_vweb bool
is_embed bool
is_env bool
env_pos token.Pos
is_pkgconfig bool
pub mut:
vweb_tmpl File
left Expr
left_type Type
result_type Type
env_value string
args_var string
args []CallArg
embed_file EmbeddedFile
or_block OrExpr
}
pub struct None {
pub:
pos token.Pos
}
pub enum SqlStmtKind {
insert
update
delete
create
drop
}
pub struct SqlStmt {
pub:
pos token.Pos
pub mut:
lines []SqlStmtLine
db_expr Expr // `db` in `sql db {`
or_expr OrExpr
db_expr_type Type // the type of the `db` in `sql db {`
}
pub struct SqlStmtLine {
pub:
kind SqlStmtKind
pos token.Pos
// is_generated indicates a statement is generated by ORM for complex queries with related tables.
is_generated bool
scope &Scope = unsafe { nil }
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
where_expr Expr
update_exprs []Expr // for `update`
}
pub struct SqlExpr {
pub:
is_count bool
has_where bool
has_order bool
has_limit bool
has_offset bool
has_desc bool
is_array bool
// is_generated indicates a statement is generated by ORM for complex queries with related tables.
is_generated bool
pos token.Pos
pub mut:
typ Type
db_expr Expr // `db` in `sql db {`
where_expr Expr
order_expr Expr
limit_expr Expr
offset_expr Expr
table_expr TypeNode
fields []StructField
sub_structs map[int]SqlExpr
or_expr OrExpr
}
pub struct NodeError {
pub:
idx int // index for referencing the related File error
pos token.Pos
}
[inline]
pub fn (expr Expr) is_blank_ident() bool {
if expr is Ident {
return expr.kind == .blank_ident
}
return false
}
pub fn (expr Expr) pos() token.Pos {
// all uncommented have to be implemented
// Note: please do not print here. the language server will hang
// as it uses STDIO primarily to communicate ~Ned
match expr {
AnonFn {
return expr.decl.pos
}
CTempVar, EmptyExpr {
// println('compiler bug, unhandled EmptyExpr pos()')
return token.Pos{}
}
NodeError, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr,
CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, ComptimeCall, ComptimeSelector,
EnumVal, DumpExpr, FloatLiteral, GoExpr, SpawnExpr, 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, ComptimeType,
Nil {
return expr.pos
}
IndexExpr {
if expr.or_expr.kind != .absent {
return expr.or_expr.pos
}
return expr.pos
}
IfGuardExpr {
return expr.expr.pos()
}
InfixExpr {
left_pos := expr.left.pos()
right_pos := expr.right.pos()
return token.Pos{
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 {
return match expr {
Ident, CTempVar { true }
IndexExpr { expr.left.is_lvalue() }
SelectorExpr { expr.expr.is_lvalue() }
ParExpr { expr.expr.is_lvalue() } // for var := &{...(*pointer_var)}
PrefixExpr { expr.right.is_lvalue() }
ComptimeSelector { expr.field_expr.is_lvalue() }
else { false }
}
}
pub fn (expr Expr) is_expr() bool {
return match expr {
IfExpr, LockExpr, MatchExpr, SelectExpr { expr.is_expr }
else { true }
}
}
pub fn (expr Expr) get_pure_type() Type {
match expr {
BoolLiteral { return bool_type }
CharLiteral { return char_type }
FloatLiteral { return f64_type }
StringLiteral { return string_type }
IntegerLiteral { return i64_type }
else { return void_type }
}
}
pub fn (expr Expr) is_pure_literal() bool {
return match expr {
BoolLiteral, CharLiteral, FloatLiteral, 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 {
return match e {
Ident { true }
SelectorExpr { e.expr.is_lockable() }
else { false }
}
}
// 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)
typ Type // the type of the original expression
is_ptr bool // whether the type is a pointer
pub mut:
orig Expr // the original expression, which produced the C temp variable; used by x.str()
}
pub fn (node Node) pos() token.Pos {
match node {
NodeError {
return token.Pos{}
}
EmptyNode {
return token.Pos{}
}
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().pos())
}
if node is AssertStmt {
return pos.extend(node.expr.pos())
}
return pos
}
Expr {
return node.pos()
}
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.Pos{
len: -1
line_nr: -1
pos: -1
last_line: -1
col: -1
}
}
}
}
File {
mut pos := token.Pos{}
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.init_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, ComptimeFor {
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))
}
// Note: 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)
}
}
}
pub fn (mut lx IndexExpr) recursive_arraymap_set_is_setter() {
lx.is_setter = true
if mut lx.left is IndexExpr {
lx.left.recursive_arraymap_set_is_setter()
} else if mut lx.left is SelectorExpr {
if mut lx.left.expr is IndexExpr {
lx.left.expr.recursive_arraymap_set_is_setter()
}
}
}
// 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, StringLiteral, StringInterLiteral {
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_any_kind_of_pointer()
|| expr.typ in [i8_type, i16_type, int_type, i64_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
}
}
}
pub fn (e Expr) is_nil() bool {
if e is Nil {
return true
}
if e is UnsafeExpr {
if e.expr is Nil {
return true
}
}
return false
}
pub fn type_can_start_with_token(tok &token.Token) bool {
match tok.kind {
.name {
return (tok.lit.len > 0 && tok.lit[0].is_capital())
|| builtin_type_names_matcher.matches(tok.lit)
}
// Note: return type (T1, T2) should be handled elsewhere
.amp, .key_fn, .lsbr, .question {
return true
}
else {}
}
return false
}