mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
1989 lines
44 KiB
V
1989 lines
44 KiB
V
// 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
|
||
expr Expr
|
||
has_expr bool
|
||
pos token.Position
|
||
typ_pos token.Position
|
||
pub mut:
|
||
typ Type
|
||
comments []Comment
|
||
}
|
||
|
||
pub struct GlobalDecl {
|
||
pub:
|
||
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 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'
|
||
}
|
||
|
||
/*
|
||
// 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
|
||
}
|