ast: merge `IfExpr` and `CompIf` (#6011)
parent
98e6d25b42
commit
ff92c3409d
|
@ -8,9 +8,8 @@ import time
|
||||||
#flag -lpthread
|
#flag -lpthread
|
||||||
$if macos {
|
$if macos {
|
||||||
#include <dispatch/dispatch.h>
|
#include <dispatch/dispatch.h>
|
||||||
} $else {
|
|
||||||
#include <semaphore.h>
|
|
||||||
}
|
}
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
// [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
|
// [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
|
||||||
[ref_only]
|
[ref_only]
|
||||||
|
|
|
@ -16,18 +16,13 @@ pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | C
|
||||||
SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type |
|
SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type |
|
||||||
TypeOf | UnsafeExpr
|
TypeOf | UnsafeExpr
|
||||||
|
|
||||||
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | CompIf | ConstDecl |
|
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt |
|
||||||
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl |
|
EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
|
||||||
GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | Return |
|
GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | Return | SqlStmt |
|
||||||
SqlStmt | StructDecl | TypeDecl
|
StructDecl | TypeDecl
|
||||||
|
|
||||||
pub type ScopeObject = ConstField | GlobalDecl | Var
|
pub type ScopeObject = ConstField | GlobalDecl | Var
|
||||||
|
|
||||||
// pub type Type = StructType | ArrayType
|
|
||||||
// pub struct StructType {
|
|
||||||
// fields []Field
|
|
||||||
// }
|
|
||||||
// pub struct ArrayType {}
|
|
||||||
pub struct Type {
|
pub struct Type {
|
||||||
pub:
|
pub:
|
||||||
typ table.Type
|
typ table.Type
|
||||||
|
@ -468,6 +463,7 @@ pub mut:
|
||||||
|
|
||||||
pub struct IfExpr {
|
pub struct IfExpr {
|
||||||
pub:
|
pub:
|
||||||
|
is_comptime bool
|
||||||
tok_kind token.Kind
|
tok_kind token.Kind
|
||||||
left Expr // `a` in `a := if ...`
|
left Expr // `a` in `a := if ...`
|
||||||
pos token.Position
|
pos token.Position
|
||||||
|
@ -482,13 +478,13 @@ pub mut:
|
||||||
pub struct IfBranch {
|
pub struct IfBranch {
|
||||||
pub:
|
pub:
|
||||||
cond Expr
|
cond Expr
|
||||||
stmts []Stmt
|
|
||||||
pos token.Position
|
pos token.Position
|
||||||
body_pos token.Position
|
body_pos token.Position
|
||||||
comments []Comment
|
comments []Comment
|
||||||
left_as_name string // `name` in `if cond is SumType as name`
|
left_as_name string // `name` in `if cond is SumType as name`
|
||||||
mut_name bool // `if mut name is`
|
mut_name bool // `if mut name is`
|
||||||
pub mut:
|
pub mut:
|
||||||
|
stmts []Stmt
|
||||||
smartcast bool // true when cond is `x is SumType`, set in checker.if_expr
|
smartcast bool // true when cond is `x is SumType`, set in checker.if_expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,42 +553,6 @@ pub:
|
||||||
post_comments []Comment
|
post_comments []Comment
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
CompIf.is_opt:
|
|
||||||
`$if xyz? {}` => this compile time `if` is optional,
|
|
||||||
and .is_opt reflects the presence of ? at the end.
|
|
||||||
When .is_opt is true, the code should compile, even
|
|
||||||
if `xyz` is NOT defined.
|
|
||||||
If .is_opt is false, then when `xyz` is not defined,
|
|
||||||
the compilation will fail.
|
|
||||||
|
|
||||||
`$if method.type is string {}` will produce CompIf with:
|
|
||||||
.is_typecheck true,
|
|
||||||
.tchk_expr: method.type
|
|
||||||
.tchk_type: string
|
|
||||||
.tchk_match: true on each iteration, having a string `method.type`
|
|
||||||
*/
|
|
||||||
pub enum CompIfKind {
|
|
||||||
platform
|
|
||||||
typecheck
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CompIf {
|
|
||||||
pub:
|
|
||||||
val string
|
|
||||||
stmts []Stmt
|
|
||||||
is_not bool
|
|
||||||
kind CompIfKind
|
|
||||||
tchk_expr Expr
|
|
||||||
tchk_type table.Type
|
|
||||||
pos token.Position
|
|
||||||
pub mut:
|
|
||||||
tchk_match bool
|
|
||||||
is_opt bool
|
|
||||||
has_else bool
|
|
||||||
else_stmts []Stmt
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum CompForKind {
|
pub enum CompForKind {
|
||||||
methods
|
methods
|
||||||
fields
|
fields
|
||||||
|
@ -1134,7 +1094,6 @@ pub fn (stmt Stmt) position() token.Position {
|
||||||
// BranchStmt {
|
// BranchStmt {
|
||||||
// }
|
// }
|
||||||
*/
|
*/
|
||||||
CompIf { return stmt.pos }
|
|
||||||
ConstDecl { return stmt.pos }
|
ConstDecl { return stmt.pos }
|
||||||
/*
|
/*
|
||||||
// DeferStmt {
|
// DeferStmt {
|
||||||
|
|
|
@ -58,9 +58,8 @@ pub fn (mut b Builder) compile_c() {
|
||||||
// println(files)
|
// println(files)
|
||||||
}
|
}
|
||||||
$if windows {
|
$if windows {
|
||||||
b.pref.ccompiler = b.find_win_cc() or {
|
b.find_win_cc() or { verror(no_compiler_error) }
|
||||||
panic(no_compiler_error)
|
// TODO Probably extend this to other OS's?
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// v1 compiler files
|
// v1 compiler files
|
||||||
// v.add_v_files_to_compile()
|
// v.add_v_files_to_compile()
|
||||||
|
|
|
@ -42,7 +42,7 @@ const (
|
||||||
fn todo() {
|
fn todo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (v &Builder) find_win_cc() ?string {
|
fn (mut v Builder) find_win_cc() ? {
|
||||||
$if !windows {
|
$if !windows {
|
||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
@ -58,15 +58,19 @@ fn (v &Builder) find_win_cc() ?string {
|
||||||
thirdparty_tcc := os.join_path(vpath, 'thirdparty', 'tcc', 'tcc.exe')
|
thirdparty_tcc := os.join_path(vpath, 'thirdparty', 'tcc', 'tcc.exe')
|
||||||
os.exec('$thirdparty_tcc -v') or {
|
os.exec('$thirdparty_tcc -v') or {
|
||||||
if v.pref.is_verbose {
|
if v.pref.is_verbose {
|
||||||
println('No C compiler found')
|
println('tcc not found')
|
||||||
}
|
}
|
||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
return thirdparty_tcc
|
v.pref.ccompiler = thirdparty_tcc
|
||||||
|
v.pref.ccompiler_type = .tinyc
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return 'msvc'
|
v.pref.ccompiler = 'msvc'
|
||||||
|
v.pref.ccompiler_type = .msvc
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return v.pref.ccompiler
|
v.pref.ccompiler_type = pref.cc_from_string(v.pref.ccompiler)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut v Builder) cc() {
|
fn (mut v Builder) cc() {
|
||||||
|
|
|
@ -84,18 +84,19 @@ fn find_windows_kit_root(host_arch string) ?WindowsKit {
|
||||||
path := 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots'
|
path := 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots'
|
||||||
rc := C.RegOpenKeyEx(hkey_local_machine, path.to_wide(), 0, key_query_value | key_wow64_32key |
|
rc := C.RegOpenKeyEx(hkey_local_machine, path.to_wide(), 0, key_query_value | key_wow64_32key |
|
||||||
key_enumerate_sub_keys, &root_key)
|
key_enumerate_sub_keys, &root_key)
|
||||||
defer {
|
// TODO: Fix defer inside ifs
|
||||||
C.RegCloseKey(root_key)
|
// defer {
|
||||||
}
|
// C.RegCloseKey(root_key)
|
||||||
|
// }
|
||||||
if rc != 0 {
|
if rc != 0 {
|
||||||
return error('Unable to open root key')
|
return error('Unable to open root key')
|
||||||
}
|
}
|
||||||
// Try and find win10 kit
|
// Try and find win10 kit
|
||||||
kit_root := find_windows_kit_internal(root_key, ['KitsRoot10', 'KitsRoot81']) or {
|
kit_root := find_windows_kit_internal(root_key, ['KitsRoot10', 'KitsRoot81']) or {
|
||||||
|
C.RegCloseKey(root_key)
|
||||||
return error('Unable to find a windows kit')
|
return error('Unable to find a windows kit')
|
||||||
}
|
}
|
||||||
kit_lib := kit_root + 'Lib'
|
kit_lib := kit_root + 'Lib'
|
||||||
// println(kit_lib)
|
|
||||||
files := os.ls(kit_lib)?
|
files := os.ls(kit_lib)?
|
||||||
mut highest_path := ''
|
mut highest_path := ''
|
||||||
mut highest_int := 0
|
mut highest_int := 0
|
||||||
|
@ -109,7 +110,7 @@ fn find_windows_kit_root(host_arch string) ?WindowsKit {
|
||||||
}
|
}
|
||||||
kit_lib_highest := kit_lib + '\\$highest_path'
|
kit_lib_highest := kit_lib + '\\$highest_path'
|
||||||
kit_include_highest := kit_lib_highest.replace('Lib', 'Include')
|
kit_include_highest := kit_lib_highest.replace('Lib', 'Include')
|
||||||
// println('$kit_lib_highest $kit_include_highest')
|
C.RegCloseKey(root_key)
|
||||||
return WindowsKit{
|
return WindowsKit{
|
||||||
um_lib_path: kit_lib_highest + '\\um\\$host_arch'
|
um_lib_path: kit_lib_highest + '\\um\\$host_arch'
|
||||||
ucrt_lib_path: kit_lib_highest + '\\ucrt\\$host_arch'
|
ucrt_lib_path: kit_lib_highest + '\\ucrt\\$host_arch'
|
||||||
|
|
|
@ -16,6 +16,16 @@ const (
|
||||||
enum_max = 0x7FFFFFFF
|
enum_max = 0x7FFFFFFF
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
valid_comp_if_os = ['windows', 'ios', 'mac', 'macos', 'mach', 'darwin', 'hpux', 'gnu',
|
||||||
|
'qnx', 'linux', 'freebsd', 'openbsd', 'netbsd', 'bsd', 'dragonfly', 'android', 'solaris', 'haiku',
|
||||||
|
'linux_or_macos',
|
||||||
|
]
|
||||||
|
valid_comp_if_compilers = ['gcc', 'tinyc', 'clang', 'mingw', 'msvc', 'cplusplus']
|
||||||
|
valid_comp_if_platforms = ['amd64', 'aarch64', 'x64', 'x32', 'little_endian', 'big_endian']
|
||||||
|
valid_comp_if_other = ['js', 'debug', 'test', 'glibc', 'prealloc', 'no_bounds_checking']
|
||||||
|
)
|
||||||
|
|
||||||
pub struct Checker {
|
pub struct Checker {
|
||||||
pub mut:
|
pub mut:
|
||||||
table &table.Table
|
table &table.Table
|
||||||
|
@ -40,6 +50,7 @@ pub mut:
|
||||||
mod string // current module name
|
mod string // current module name
|
||||||
is_builtin_mod bool // are we in `builtin`?
|
is_builtin_mod bool // are we in `builtin`?
|
||||||
inside_unsafe bool
|
inside_unsafe bool
|
||||||
|
skip_flags bool // should `#flag` and `#include` be skipped
|
||||||
cur_generic_type table.Type
|
cur_generic_type table.Type
|
||||||
mut:
|
mut:
|
||||||
expr_level int // to avoid infinit recursion segfaults due to compiler bugs
|
expr_level int // to avoid infinit recursion segfaults due to compiler bugs
|
||||||
|
@ -2078,12 +2089,14 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
// c.expected_type = table.void_type
|
// c.expected_type = table.void_type
|
||||||
match mut node {
|
match mut node {
|
||||||
ast.AssertStmt {
|
ast.AssertStmt {
|
||||||
|
cur_exp_typ := c.expected_type
|
||||||
assert_type := c.expr(node.expr)
|
assert_type := c.expr(node.expr)
|
||||||
if assert_type != table.bool_type_idx {
|
if assert_type != table.bool_type_idx {
|
||||||
atype_name := c.table.get_type_symbol(assert_type).name
|
atype_name := c.table.get_type_symbol(assert_type).name
|
||||||
c.error('assert can be used only with `bool` expressions, but found `$atype_name` instead',
|
c.error('assert can be used only with `bool` expressions, but found `$atype_name` instead',
|
||||||
node.pos)
|
node.pos)
|
||||||
}
|
}
|
||||||
|
c.expected_type = cur_exp_typ
|
||||||
}
|
}
|
||||||
ast.AssignStmt {
|
ast.AssignStmt {
|
||||||
c.assign_stmt(mut node)
|
c.assign_stmt(mut node)
|
||||||
|
@ -2107,17 +2120,6 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
// node.typ = c.expr(node.expr)
|
// node.typ = c.expr(node.expr)
|
||||||
c.stmts(node.stmts)
|
c.stmts(node.stmts)
|
||||||
}
|
}
|
||||||
ast.CompIf {
|
|
||||||
c.stmts(node.stmts)
|
|
||||||
if node.has_else {
|
|
||||||
c.stmts(node.else_stmts)
|
|
||||||
}
|
|
||||||
mut stmts := node.stmts.clone()
|
|
||||||
stmts << node.else_stmts
|
|
||||||
if has_return := c.has_return(stmts) {
|
|
||||||
c.returns = has_return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast.ConstDecl {
|
ast.ConstDecl {
|
||||||
mut field_names := []string{}
|
mut field_names := []string{}
|
||||||
mut field_order := []int{}
|
mut field_order := []int{}
|
||||||
|
@ -2303,6 +2305,9 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
|
fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
|
||||||
|
if c.skip_flags {
|
||||||
|
return
|
||||||
|
}
|
||||||
if c.pref.backend == .js {
|
if c.pref.backend == .js {
|
||||||
if !c.file.path.ends_with('.js.v') {
|
if !c.file.path.ends_with('.js.v') {
|
||||||
c.error('Hash statements are only allowed in backend specific files such "x.js.v"',
|
c.error('Hash statements are only allowed in backend specific files such "x.js.v"',
|
||||||
|
@ -3123,33 +3128,38 @@ pub fn (mut c Checker) unsafe_expr(mut node ast.UnsafeExpr) table.Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
||||||
mut expr_required := false
|
is_ct := node.is_comptime
|
||||||
if c.expected_type != table.void_type {
|
if_kind := if is_ct { '\$if' } else { 'if' }
|
||||||
// sym := c.table.get_type_symbol(c.expected_type)
|
expr_required := c.expected_type != table.void_type
|
||||||
// println('$c.file.path $node.pos.line_nr IF is expr: checker exp type = ' + sym.source_name)
|
|
||||||
expr_required = true
|
|
||||||
}
|
|
||||||
former_expected_type := c.expected_type
|
former_expected_type := c.expected_type
|
||||||
node.typ = table.void_type
|
node.typ = table.void_type
|
||||||
mut require_return := false
|
mut require_return := false
|
||||||
mut branch_without_return := false
|
mut branch_without_return := false
|
||||||
for i, branch in node.branches {
|
mut should_skip := false // Whether the current branch should be skipped
|
||||||
|
mut found_branch := false // Whether a matching branch was found- skip the rest
|
||||||
|
for i in 0 .. node.branches.len {
|
||||||
|
mut branch := node.branches[i]
|
||||||
if branch.cond is ast.ParExpr {
|
if branch.cond is ast.ParExpr {
|
||||||
c.error('unnecessary `()` in an if condition. use `if expr {` instead of `if (expr) {`.',
|
c.error('unnecessary `()` in `$if_kind` condition, use `$if_kind expr {` instead of `$if_kind (expr) {`.',
|
||||||
branch.pos)
|
branch.pos)
|
||||||
}
|
}
|
||||||
if !node.has_else || i < node.branches.len - 1 {
|
if !node.has_else || i < node.branches.len - 1 {
|
||||||
// check condition type is boolean
|
if is_ct {
|
||||||
cond_typ := c.expr(branch.cond)
|
should_skip = c.comp_if_branch(branch.cond, branch.pos)
|
||||||
if cond_typ.idx() !in [table.bool_type_idx, table.void_type_idx] && !c.pref.translated {
|
} else {
|
||||||
// void types are skipped, because they mean the var was initialized incorrectly
|
// check condition type is boolean
|
||||||
// (via missing function etc)
|
cond_typ := c.expr(branch.cond)
|
||||||
typ_sym := c.table.get_type_symbol(cond_typ)
|
if cond_typ.idx() !in [table.bool_type_idx, table.void_type_idx] && !c.pref.translated {
|
||||||
c.error('non-bool type `$typ_sym.source_name` used as if condition', branch.pos)
|
// void types are skipped, because they mean the var was initialized incorrectly
|
||||||
|
// (via missing function etc)
|
||||||
|
typ_sym := c.table.get_type_symbol(cond_typ)
|
||||||
|
c.error('non-bool type `$typ_sym.source_name` used as if condition',
|
||||||
|
branch.pos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// smartcast sumtypes and interfaces when using `is`
|
// smartcast sumtypes and interfaces when using `is`
|
||||||
if branch.cond is ast.InfixExpr {
|
if !is_ct && branch.cond is ast.InfixExpr {
|
||||||
infix := branch.cond as ast.InfixExpr
|
infix := branch.cond as ast.InfixExpr
|
||||||
if infix.op == .key_is &&
|
if infix.op == .key_is &&
|
||||||
(infix.left is ast.Ident || infix.left is ast.SelectorExpr) && infix.right is ast.Type {
|
(infix.left is ast.Ident || infix.left is ast.SelectorExpr) && infix.right is ast.Type {
|
||||||
|
@ -3185,7 +3195,25 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.stmts(branch.stmts)
|
if is_ct { // Skip checking if needed
|
||||||
|
cur_skip_flags := c.skip_flags
|
||||||
|
if found_branch {
|
||||||
|
c.skip_flags = true
|
||||||
|
} else if should_skip {
|
||||||
|
c.skip_flags = true
|
||||||
|
should_skip = false // Reset the value of `should_skip` for the next branch
|
||||||
|
} else {
|
||||||
|
found_branch = true // If a branch wasn't skipped, the rest must be
|
||||||
|
}
|
||||||
|
if !c.skip_flags || c.pref.output_cross_c {
|
||||||
|
c.stmts(branch.stmts)
|
||||||
|
} else {
|
||||||
|
node.branches[i].stmts = []
|
||||||
|
}
|
||||||
|
c.skip_flags = cur_skip_flags
|
||||||
|
} else {
|
||||||
|
c.stmts(branch.stmts)
|
||||||
|
}
|
||||||
if expr_required {
|
if expr_required {
|
||||||
if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt {
|
if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt {
|
||||||
mut last_expr := branch.stmts[branch.stmts.len - 1] as ast.ExprStmt
|
mut last_expr := branch.stmts[branch.stmts.len - 1] as ast.ExprStmt
|
||||||
|
@ -3227,10 +3255,11 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
||||||
node.pos)
|
node.pos)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.error('`if` expression requires an expression as the last statement of every branch',
|
c.error('`$if_kind` expression requires an expression as the last statement of every branch',
|
||||||
branch.pos)
|
branch.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Also check for returns inside a comp.if's statements, even if its contents aren't parsed
|
||||||
if has_return := c.has_return(branch.stmts) {
|
if has_return := c.has_return(branch.stmts) {
|
||||||
if has_return {
|
if has_return {
|
||||||
require_return = true
|
require_return = true
|
||||||
|
@ -3253,21 +3282,99 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
||||||
}
|
}
|
||||||
if expr_required {
|
if expr_required {
|
||||||
if !node.has_else {
|
if !node.has_else {
|
||||||
c.error('`if` expression needs `else` clause', node.pos)
|
d := if is_ct { '$' } else { '' }
|
||||||
|
c.error('`$if_kind` expression needs `${d}else` clause', node.pos)
|
||||||
}
|
}
|
||||||
return node.typ
|
return node.typ
|
||||||
}
|
}
|
||||||
return table.bool_type
|
return table.bool_type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// comp_if_branch checks the condition of a compile-time `if` branch. It returns a `bool` that
|
||||||
|
// saying whether that branch's contents should be skipped (targets a different os for example)
|
||||||
|
fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool {
|
||||||
|
// TODO: better error messages here
|
||||||
|
match cond {
|
||||||
|
ast.ParExpr {
|
||||||
|
return c.comp_if_branch(cond.expr, pos)
|
||||||
|
}
|
||||||
|
ast.PrefixExpr {
|
||||||
|
if cond.op != .not {
|
||||||
|
c.error('invalid `\$if` condition', cond.pos)
|
||||||
|
}
|
||||||
|
return !c.comp_if_branch(cond.right, cond.pos)
|
||||||
|
}
|
||||||
|
ast.PostfixExpr {
|
||||||
|
if cond.op != .question {
|
||||||
|
c.error('invalid \$if postfix operator', cond.pos)
|
||||||
|
} else if cond.expr is ast.Ident as ident {
|
||||||
|
return ident.name !in c.pref.compile_defines_all
|
||||||
|
} else {
|
||||||
|
c.error('invalid `\$if` condition', cond.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast.InfixExpr {
|
||||||
|
match cond.op {
|
||||||
|
.and {
|
||||||
|
l := c.comp_if_branch(cond.left, cond.pos)
|
||||||
|
r := c.comp_if_branch(cond.right, cond.pos)
|
||||||
|
return l && r
|
||||||
|
}
|
||||||
|
.logical_or {
|
||||||
|
l := c.comp_if_branch(cond.left, cond.pos)
|
||||||
|
r := c.comp_if_branch(cond.right, cond.pos)
|
||||||
|
return l || r
|
||||||
|
}
|
||||||
|
.key_is, .not_is {
|
||||||
|
// $if method.@type is string
|
||||||
|
// TODO better checks here, will be done in comp. for PR
|
||||||
|
if cond.left !is ast.SelectorExpr || cond.right !is ast.Type {
|
||||||
|
c.error('invalid `\$if` condition', cond.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eq, .ne {
|
||||||
|
// $if method.args.len == 1
|
||||||
|
// TODO better checks here, will be done in comp. for PR
|
||||||
|
if cond.left !is ast.SelectorExpr || cond.right !is ast.IntegerLiteral {
|
||||||
|
c.error('invalid `\$if` condition', cond.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
c.error('invalid `\$if` condition', cond.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast.Ident {
|
||||||
|
if cond.name in valid_comp_if_os {
|
||||||
|
return cond.name != c.pref.os.str().to_lower() // TODO hack
|
||||||
|
} else if cond.name in valid_comp_if_compilers {
|
||||||
|
return pref.cc_from_string(cond.name) != c.pref.ccompiler_type
|
||||||
|
} else if cond.name in valid_comp_if_platforms {
|
||||||
|
return false // TODO
|
||||||
|
} else if cond.name in valid_comp_if_other {
|
||||||
|
// TODO: This should probably be moved
|
||||||
|
match cond.name as name {
|
||||||
|
'js' { return c.pref.backend != .js }
|
||||||
|
'debug' { return !c.pref.is_debug }
|
||||||
|
'test' { return !c.pref.is_test }
|
||||||
|
'glibc' { return false } // TODO
|
||||||
|
'prealloc' { return !c.pref.prealloc }
|
||||||
|
'no_bounds_checking' { return cond.name !in c.pref.compile_defines_all }
|
||||||
|
else { return false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
c.error('invalid `\$if` condition', pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
fn (c &Checker) has_return(stmts []ast.Stmt) ?bool {
|
fn (c &Checker) has_return(stmts []ast.Stmt) ?bool {
|
||||||
// complexity means either more match or ifs
|
// complexity means either more match or ifs
|
||||||
mut has_complexity := false
|
mut has_complexity := false
|
||||||
for s in stmts {
|
for s in stmts {
|
||||||
if s is ast.CompIf {
|
|
||||||
has_complexity = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if s is ast.ExprStmt {
|
if s is ast.ExprStmt {
|
||||||
if s.expr is ast.IfExpr || s.expr is ast.MatchExpr {
|
if s.expr is ast.IfExpr || s.expr is ast.MatchExpr {
|
||||||
has_complexity = true
|
has_complexity = true
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
vlib/v/checker/tests/unnecessary_parenthesis.vv:2:2: error: unnecessary `()` in an if condition. use `if expr {` instead of `if (expr) {`.
|
vlib/v/checker/tests/unnecessary_parenthesis.vv:2:2: error: unnecessary `()` in `if` condition, use `if expr {` instead of `if (expr) {`.
|
||||||
1 | fn main() {
|
1 | fn main() {
|
||||||
2 | if (1 == 1) {
|
2 | if (1 == 1) {
|
||||||
| ~~~~~~~~~~~
|
| ~~~~~~~~~~~
|
||||||
3 | println('yeay')
|
3 | println('yeay')
|
||||||
4 | } else if (1 == 2) {
|
4 | } else if (1 == 2) {
|
||||||
vlib/v/checker/tests/unnecessary_parenthesis.vv:4:4: error: unnecessary `()` in an if condition. use `if expr {` instead of `if (expr) {`.
|
vlib/v/checker/tests/unnecessary_parenthesis.vv:4:4: error: unnecessary `()` in `if` condition, use `if expr {` instead of `if (expr) {`.
|
||||||
2 | if (1 == 1) {
|
2 | if (1 == 1) {
|
||||||
3 | println('yeay')
|
3 | println('yeay')
|
||||||
4 | } else if (1 == 2) {
|
4 | } else if (1 == 2) {
|
||||||
| ~~~~~~~~~~~~~~~~
|
| ~~~~~~~~~~~~~~~~
|
||||||
5 | println("oh no :'(")
|
5 | println("oh no :'(")
|
||||||
6 | } else if (1 == 3) {
|
6 | } else if (1 == 3) {
|
||||||
vlib/v/checker/tests/unnecessary_parenthesis.vv:6:4: error: unnecessary `()` in an if condition. use `if expr {` instead of `if (expr) {`.
|
vlib/v/checker/tests/unnecessary_parenthesis.vv:6:4: error: unnecessary `()` in `if` condition, use `if expr {` instead of `if (expr) {`.
|
||||||
4 | } else if (1 == 2) {
|
4 | } else if (1 == 2) {
|
||||||
5 | println("oh no :'(")
|
5 | println("oh no :'(")
|
||||||
6 | } else if (1 == 3) {
|
6 | } else if (1 == 3) {
|
||||||
|
|
|
@ -9,7 +9,8 @@ import benchmark
|
||||||
|
|
||||||
const (
|
const (
|
||||||
skip_files = [
|
skip_files = [
|
||||||
'nonexisting'
|
'vlib/v/checker/tests/return_missing_comp_if.vv'
|
||||||
|
'vlib/v/checker/tests/return_missing_comp_if_nested.vv'
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -307,28 +307,6 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
|
||||||
f.stmts(it.stmts)
|
f.stmts(it.stmts)
|
||||||
f.writeln('}')
|
f.writeln('}')
|
||||||
}
|
}
|
||||||
ast.CompIf {
|
|
||||||
inversion := if it.is_not { '!' } else { '' }
|
|
||||||
is_opt := if it.is_opt { ' ?' } else { '' }
|
|
||||||
mut typecheck := ''
|
|
||||||
if it.kind == .typecheck {
|
|
||||||
typ := f.no_cur_mod(f.table.type_to_str(it.tchk_type))
|
|
||||||
typecheck = ' is $typ'
|
|
||||||
f.write('\$if $inversion')
|
|
||||||
f.expr(it.tchk_expr)
|
|
||||||
f.write(is_opt)
|
|
||||||
f.write(typecheck)
|
|
||||||
f.writeln(' {')
|
|
||||||
} else {
|
|
||||||
f.writeln('\$if $inversion$it.val$is_opt {')
|
|
||||||
}
|
|
||||||
f.stmts(it.stmts)
|
|
||||||
if it.has_else {
|
|
||||||
f.writeln('} \$else {')
|
|
||||||
f.stmts(it.else_stmts)
|
|
||||||
}
|
|
||||||
f.writeln('}')
|
|
||||||
}
|
|
||||||
ast.ConstDecl {
|
ast.ConstDecl {
|
||||||
f.const_decl(it)
|
f.const_decl(it)
|
||||||
}
|
}
|
||||||
|
@ -942,7 +920,12 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
||||||
}
|
}
|
||||||
ast.PostfixExpr {
|
ast.PostfixExpr {
|
||||||
f.expr(node.expr)
|
f.expr(node.expr)
|
||||||
f.write(node.op.str())
|
// `$if foo ?`
|
||||||
|
if node.op == .question {
|
||||||
|
f.write(' ?')
|
||||||
|
} else {
|
||||||
|
f.write('$node.op')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast.PrefixExpr {
|
ast.PrefixExpr {
|
||||||
f.write(node.op.str())
|
f.write(node.op.str())
|
||||||
|
@ -1361,6 +1344,7 @@ pub fn (mut f Fmt) infix_expr(node ast.InfixExpr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut f Fmt) if_expr(it ast.IfExpr) {
|
pub fn (mut f Fmt) if_expr(it ast.IfExpr) {
|
||||||
|
dollar := if it.is_comptime { '$' } else { '' }
|
||||||
single_line := it.branches.len == 2 && it.has_else && it.branches[0].stmts.len == 1 &&
|
single_line := it.branches.len == 2 && it.has_else && it.branches[0].stmts.len == 1 &&
|
||||||
it.branches[1].stmts.len == 1 &&
|
it.branches[1].stmts.len == 1 &&
|
||||||
(it.is_expr || f.is_assign)
|
(it.is_expr || f.is_assign)
|
||||||
|
@ -1386,10 +1370,10 @@ pub fn (mut f Fmt) if_expr(it ast.IfExpr) {
|
||||||
} else {
|
} else {
|
||||||
f.write('} ')
|
f.write('} ')
|
||||||
}
|
}
|
||||||
f.write('else ')
|
f.write('${dollar}else ')
|
||||||
}
|
}
|
||||||
if i < it.branches.len - 1 || !it.has_else {
|
if i < it.branches.len - 1 || !it.has_else {
|
||||||
f.write('if ')
|
f.write('${dollar}if ')
|
||||||
if branch.mut_name {
|
if branch.mut_name {
|
||||||
f.write('mut ')
|
f.write('mut ')
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ pub fn (stmt Stmt) position() Position {
|
||||||
// }
|
// }
|
||||||
*/
|
*/
|
||||||
Comment { return stmt.pos }
|
Comment { return stmt.pos }
|
||||||
CompIf { return stmt.pos }
|
|
||||||
ConstDecl { return stmt.pos }
|
ConstDecl { return stmt.pos }
|
||||||
/*
|
/*
|
||||||
// DeferStmt {
|
// DeferStmt {
|
||||||
|
|
|
@ -777,10 +777,6 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
ast.CompFor {
|
ast.CompFor {
|
||||||
g.comp_for(node)
|
g.comp_for(node)
|
||||||
}
|
}
|
||||||
ast.CompIf {
|
|
||||||
g.write_v_source_line_info(node.pos)
|
|
||||||
g.comp_if(node)
|
|
||||||
}
|
|
||||||
ast.DeferStmt {
|
ast.DeferStmt {
|
||||||
mut defer_stmt := *node
|
mut defer_stmt := *node
|
||||||
defer_stmt.ifdef = g.defer_ifdef
|
defer_stmt.ifdef = g.defer_ifdef
|
||||||
|
@ -2821,6 +2817,10 @@ fn (mut g Gen) concat_expr(node ast.ConcatExpr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) if_expr(node ast.IfExpr) {
|
fn (mut g Gen) if_expr(node ast.IfExpr) {
|
||||||
|
if node.is_comptime {
|
||||||
|
g.comp_if(node)
|
||||||
|
return
|
||||||
|
}
|
||||||
if node.is_expr || g.inside_ternary != 0 {
|
if node.is_expr || g.inside_ternary != 0 {
|
||||||
g.inside_ternary++
|
g.inside_ternary++
|
||||||
g.write('(')
|
g.write('(')
|
||||||
|
|
|
@ -101,67 +101,94 @@ fn cgen_attrs(attrs []table.Attr) []string {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) comp_if(mut it ast.CompIf) {
|
fn (mut g Gen) comp_if(node ast.IfExpr) {
|
||||||
if it.stmts.len == 0 && it.else_stmts.len == 0 {
|
line := if node.is_expr {
|
||||||
return
|
stmt_str := g.go_before_stmt(0)
|
||||||
}
|
g.write(tabs[g.indent])
|
||||||
if it.kind == .typecheck {
|
stmt_str.trim_space()
|
||||||
mut comptime_var_type := table.Type(0)
|
} else { '' }
|
||||||
mut name := ''
|
for i, branch in node.branches {
|
||||||
if it.tchk_expr is ast.SelectorExpr {
|
start_pos := g.out.len
|
||||||
se := it.tchk_expr as ast.SelectorExpr
|
if i == node.branches.len - 1 && node.has_else {
|
||||||
name = '${se.expr}.$se.field_name'
|
g.writeln('#else')
|
||||||
comptime_var_type = g.comptime_var_type_map[name]
|
} else {
|
||||||
|
if i == 0 {
|
||||||
|
g.write('#if ')
|
||||||
|
} else {
|
||||||
|
g.write('#elif ')
|
||||||
|
}
|
||||||
|
g.comp_if_expr(branch.cond)
|
||||||
|
g.writeln('')
|
||||||
}
|
}
|
||||||
// if comptime_var_type == 0 {
|
expr_str := g.out.last_n(g.out.len - start_pos).trim_space()
|
||||||
// $if trace_gen ? {
|
g.defer_ifdef = expr_str
|
||||||
// eprintln('Known compile time types: ')
|
if node.is_expr {
|
||||||
// eprintln(g.comptime_var_type_map.str())
|
len := branch.stmts.len
|
||||||
// }
|
if len > 0 {
|
||||||
// // verror('the compile time type of `$it.tchk_expr.str()` is unknown')
|
last := branch.stmts[len - 1] as ast.ExprStmt
|
||||||
// return
|
if len > 1 {
|
||||||
// }
|
tmp := g.new_tmp_var()
|
||||||
it_type_name := g.table.get_type_name(it.tchk_type)
|
styp := g.typ(last.typ)
|
||||||
should_write := (comptime_var_type == it.tchk_type && !it.is_not) ||
|
g.indent++
|
||||||
(comptime_var_type != it.tchk_type && it.is_not)
|
g.writeln('$styp $tmp;')
|
||||||
if should_write {
|
g.writeln('{')
|
||||||
inversion := if it.is_not { '!' } else { '' }
|
g.stmts(branch.stmts[0 .. len - 1])
|
||||||
g.writeln('/* \$if $name ${inversion}is $it_type_name */ {')
|
g.write('\t$tmp = ')
|
||||||
g.stmts(it.stmts)
|
g.stmt(last)
|
||||||
g.writeln('}')
|
g.writeln('}')
|
||||||
} else if it.has_else {
|
g.indent--
|
||||||
g.writeln('/* \$else */ {')
|
g.writeln('$line $tmp;')
|
||||||
g.stmts(it.else_stmts)
|
} else {
|
||||||
g.writeln('}')
|
g.write('$line ')
|
||||||
|
g.stmt(last)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Only wrap the contents in {} if we're inside a function, not on the top level scope
|
||||||
|
should_create_scope := g.fn_decl != 0
|
||||||
|
if should_create_scope { g.writeln('{') }
|
||||||
|
g.stmts(branch.stmts)
|
||||||
|
if should_create_scope { g.writeln('}') }
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
ifdef := g.comp_if_to_ifdef(it.val, it.is_opt)
|
|
||||||
g.empty_line = false
|
|
||||||
if it.is_not {
|
|
||||||
g.writeln('// \$if !$it.val {\n#ifndef ' + ifdef)
|
|
||||||
} else {
|
|
||||||
g.writeln('// \$if $it.val {\n#ifdef ' + ifdef)
|
|
||||||
}
|
|
||||||
// NOTE: g.defer_ifdef is needed for defers called witin an ifdef
|
|
||||||
// in v1 this code would be completely excluded
|
|
||||||
g.defer_ifdef = if it.is_not { '#ifndef ' + ifdef } else { '#ifdef ' + ifdef }
|
|
||||||
// println('comp if stmts $g.file.path:$it.pos.line_nr')
|
|
||||||
g.indent--
|
|
||||||
g.stmts(it.stmts)
|
|
||||||
g.indent++
|
|
||||||
g.defer_ifdef = ''
|
|
||||||
if it.has_else {
|
|
||||||
g.empty_line = false
|
|
||||||
g.writeln('#else')
|
|
||||||
g.defer_ifdef = if it.is_not { '#ifdef ' + ifdef } else { '#ifndef ' + ifdef }
|
|
||||||
g.indent--
|
|
||||||
g.stmts(it.else_stmts)
|
|
||||||
g.indent++
|
|
||||||
g.defer_ifdef = ''
|
g.defer_ifdef = ''
|
||||||
}
|
}
|
||||||
g.empty_line = false
|
if node.is_expr { g.write('#endif') } else { g.writeln('#endif') }
|
||||||
g.writeln('#endif\n// } $it.val')
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) comp_if_expr(cond ast.Expr) {
|
||||||
|
match cond {
|
||||||
|
ast.ParExpr {
|
||||||
|
g.write('(')
|
||||||
|
g.comp_if_expr(cond.expr)
|
||||||
|
g.write(')')
|
||||||
|
} ast.PrefixExpr {
|
||||||
|
g.write(cond.op.str())
|
||||||
|
g.comp_if_expr(cond.right)
|
||||||
|
} ast.PostfixExpr {
|
||||||
|
ifdef := g.comp_if_to_ifdef((cond.expr as ast.Ident).name, true)
|
||||||
|
g.write('defined($ifdef)')
|
||||||
|
} ast.InfixExpr {
|
||||||
|
match cond.op {
|
||||||
|
.and, .logical_or {
|
||||||
|
g.comp_if_expr(cond.left)
|
||||||
|
g.write(' $cond.op ')
|
||||||
|
g.comp_if_expr(cond.right)
|
||||||
|
}
|
||||||
|
.key_is, .not_is {
|
||||||
|
se := cond.left as ast.SelectorExpr
|
||||||
|
name := '${se.expr}.$se.field_name'
|
||||||
|
exp_type := g.comptime_var_type_map[name]
|
||||||
|
got_type := (cond.right as ast.Type).typ
|
||||||
|
g.write('$exp_type == $got_type')
|
||||||
|
} .eq, .ne {
|
||||||
|
// TODO Implement `$if method.args.len == 1`
|
||||||
|
} else {}
|
||||||
|
}
|
||||||
|
} ast.Ident {
|
||||||
|
ifdef := g.comp_if_to_ifdef(cond.name, false)
|
||||||
|
g.write('defined($ifdef)')
|
||||||
|
} else {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) comp_for(node ast.CompFor) {
|
fn (mut g Gen) comp_for(node ast.CompFor) {
|
||||||
|
|
|
@ -445,9 +445,6 @@ fn (mut g JsGen) stmt(node ast.Stmt) {
|
||||||
g.gen_branch_stmt(node)
|
g.gen_branch_stmt(node)
|
||||||
}
|
}
|
||||||
ast.CompFor {}
|
ast.CompFor {}
|
||||||
ast.CompIf {
|
|
||||||
// skip: JS has no compile time if
|
|
||||||
}
|
|
||||||
ast.ConstDecl {
|
ast.ConstDecl {
|
||||||
g.gen_const_decl(node)
|
g.gen_const_decl(node)
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,152 +132,6 @@ fn (mut p Parser) comp_for() ast.CompFor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut p Parser) comp_if() ast.Stmt {
|
|
||||||
pos := p.tok.position()
|
|
||||||
p.next()
|
|
||||||
// if p.tok.kind == .name && p.tok.lit == 'vweb' {
|
|
||||||
// return p.vweb()
|
|
||||||
// }
|
|
||||||
p.check(.key_if)
|
|
||||||
mut is_not := p.tok.kind == .not
|
|
||||||
inversion_pos := p.tok.position()
|
|
||||||
if is_not {
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
//
|
|
||||||
name_pos_start := p.tok.position()
|
|
||||||
mut val := ''
|
|
||||||
mut tchk_expr := ast.Expr{}
|
|
||||||
if p.peek_tok.kind == .dot {
|
|
||||||
vname := p.parse_ident(.v)
|
|
||||||
cobj := p.scope.find(vname.name) or {
|
|
||||||
p.error_with_pos('unknown variable `$vname.name`', name_pos_start)
|
|
||||||
return ast.Stmt{}
|
|
||||||
}
|
|
||||||
if cobj is ast.Var {
|
|
||||||
tchk_expr = p.dot_expr(vname)
|
|
||||||
val = vname.name
|
|
||||||
if tchk_expr is ast.SelectorExpr as tchk_expr2 {
|
|
||||||
if p.tok.kind == .lsbr && tchk_expr2.field_name == 'args' {
|
|
||||||
tchk_expr = p.index_expr(tchk_expr)
|
|
||||||
if p.tok.kind == .dot && p.peek_tok.lit == 'Type' {
|
|
||||||
tchk_expr = p.dot_expr(tchk_expr)
|
|
||||||
} else {
|
|
||||||
p.error_with_pos('only the `Type` field is supported for arguments',
|
|
||||||
p.peek_tok.position())
|
|
||||||
}
|
|
||||||
} else if tchk_expr2.field_name !in ['Type', 'ReturnType'] {
|
|
||||||
p.error_with_pos('only the `Type` and `ReturnType` fields are supported for now',
|
|
||||||
name_pos_start)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
p.error_with_pos('`$vname.name` is not a variable', name_pos_start)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val = p.check_name()
|
|
||||||
}
|
|
||||||
name_pos := name_pos_start.extend(p.tok.position())
|
|
||||||
//
|
|
||||||
mut stmts := []ast.Stmt{}
|
|
||||||
mut skip_os := false
|
|
||||||
mut skip_cc := false
|
|
||||||
if val in supported_platforms {
|
|
||||||
os := os_from_string(val)
|
|
||||||
if (!is_not && os != p.pref.os) || (is_not && os == p.pref.os) {
|
|
||||||
skip_os = true
|
|
||||||
}
|
|
||||||
} else if val in supported_ccompilers {
|
|
||||||
if p.pref.ccompiler.len == 2 && p.pref.ccompiler == 'cc' {
|
|
||||||
// we just do not know, so we can not skip:
|
|
||||||
skip_cc = false
|
|
||||||
}else {
|
|
||||||
cc := cc_from_string(val)
|
|
||||||
user_cc := cc_from_string(p.pref.ccompiler)
|
|
||||||
if (!is_not && cc != user_cc) || (is_not && cc == user_cc) {
|
|
||||||
skip_cc = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mut skip := skip_os || skip_cc
|
|
||||||
// `$if os {` or `$if compiler {` for a different target, skip everything inside
|
|
||||||
// to avoid compilation errors (like including <windows.h> or calling WinAPI fns
|
|
||||||
// on non-Windows systems)
|
|
||||||
if !p.pref.is_fmt && !p.pref.output_cross_c && skip {
|
|
||||||
p.check(.lcbr)
|
|
||||||
// p.warn('skipping $if $val os=$os p.pref.os=$p.pref.os')
|
|
||||||
mut stack := 1
|
|
||||||
for {
|
|
||||||
if p.tok.kind == .key_return {
|
|
||||||
p.returns = true
|
|
||||||
}
|
|
||||||
if p.tok.kind == .lcbr {
|
|
||||||
stack++
|
|
||||||
} else if p.tok.kind == .rcbr {
|
|
||||||
stack--
|
|
||||||
}
|
|
||||||
if p.tok.kind == .eof {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if stack <= 0 && p.tok.kind == .rcbr {
|
|
||||||
// p.warn('exiting $stack')
|
|
||||||
p.next()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
skip = false
|
|
||||||
}
|
|
||||||
mut is_opt := false
|
|
||||||
mut is_typecheck := false
|
|
||||||
mut tchk_type := table.Type(0)
|
|
||||||
if p.tok.kind == .question {
|
|
||||||
p.next()
|
|
||||||
is_opt = true
|
|
||||||
} else if p.tok.kind in [.key_is, .not_is] {
|
|
||||||
typecheck_inversion := p.tok.kind == .not_is
|
|
||||||
p.next()
|
|
||||||
tchk_type = p.parse_type()
|
|
||||||
is_typecheck = true
|
|
||||||
if is_not {
|
|
||||||
name := p.table.get_type_name(tchk_type)
|
|
||||||
p.error_with_pos('use `\$if $tchk_expr !is $name {`, not `\$if !$tchk_expr is $name {`',
|
|
||||||
inversion_pos)
|
|
||||||
}
|
|
||||||
is_not = typecheck_inversion
|
|
||||||
}
|
|
||||||
if !skip {
|
|
||||||
stmts = p.parse_block()
|
|
||||||
}
|
|
||||||
if !is_typecheck && val.len == 0 {
|
|
||||||
p.error_with_pos('Only `\$if compvarname.field is type {}` is supported', name_pos)
|
|
||||||
}
|
|
||||||
if is_typecheck {
|
|
||||||
match tchk_expr {
|
|
||||||
ast.SelectorExpr {}
|
|
||||||
else { p.error_with_pos('Only compvarname.field is supported', name_pos) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mut node := ast.CompIf{
|
|
||||||
is_not: is_not
|
|
||||||
is_opt: is_opt
|
|
||||||
kind: if is_typecheck { ast.CompIfKind.typecheck } else { ast.CompIfKind.platform }
|
|
||||||
pos: pos
|
|
||||||
val: val
|
|
||||||
tchk_type: tchk_type
|
|
||||||
tchk_expr: tchk_expr
|
|
||||||
stmts: stmts
|
|
||||||
}
|
|
||||||
if p.tok.kind == .dollar && p.peek_tok.kind == .key_else {
|
|
||||||
p.next()
|
|
||||||
p.next()
|
|
||||||
node.has_else = true
|
|
||||||
node.else_stmts = p.parse_block()
|
|
||||||
}
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO import warning bug
|
// TODO import warning bug
|
||||||
const (
|
const (
|
||||||
todo_delete_me = pref.OS.linux
|
todo_delete_me = pref.OS.linux
|
||||||
|
@ -339,30 +193,6 @@ fn os_from_string(os string) pref.OS {
|
||||||
return .linux
|
return .linux
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to convert string names to CC enum
|
|
||||||
pub fn cc_from_string(cc_str string) pref.CompilerType {
|
|
||||||
if cc_str.len == 0 {
|
|
||||||
return .gcc
|
|
||||||
}
|
|
||||||
cc := cc_str.replace('\\', '/').split('/').last().all_before('.')
|
|
||||||
if 'tcc' in cc {
|
|
||||||
return .tinyc
|
|
||||||
}
|
|
||||||
if 'tinyc' in cc {
|
|
||||||
return .tinyc
|
|
||||||
}
|
|
||||||
if 'clang' in cc {
|
|
||||||
return .clang
|
|
||||||
}
|
|
||||||
if 'mingw' in cc {
|
|
||||||
return .mingw
|
|
||||||
}
|
|
||||||
if 'msvc' in cc {
|
|
||||||
return .msvc
|
|
||||||
}
|
|
||||||
return .gcc
|
|
||||||
}
|
|
||||||
|
|
||||||
// `app.$action()` (`action` is a string)
|
// `app.$action()` (`action` is a string)
|
||||||
// `typ` is `App` in this example
|
// `typ` is `App` in this example
|
||||||
// fn (mut p Parser) comptime_method_call(typ table.Type) ast.ComptimeCall {
|
// fn (mut p Parser) comptime_method_call(typ table.Type) ast.ComptimeCall {
|
||||||
|
|
|
@ -7,20 +7,28 @@ import v.ast
|
||||||
import v.table
|
import v.table
|
||||||
import v.token
|
import v.token
|
||||||
|
|
||||||
fn (mut p Parser) if_expr() ast.IfExpr {
|
fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
|
||||||
was_inside_if_expr := p.inside_if_expr
|
was_inside_if_expr := p.inside_if_expr
|
||||||
|
was_inside_ct_if_expr := p.inside_ct_if_expr
|
||||||
defer {
|
defer {
|
||||||
p.inside_if_expr = was_inside_if_expr
|
p.inside_if_expr = was_inside_if_expr
|
||||||
|
p.inside_ct_if_expr = was_inside_ct_if_expr
|
||||||
}
|
}
|
||||||
p.inside_if_expr = true
|
p.inside_if_expr = true
|
||||||
pos := p.tok.position()
|
pos := if is_comptime {
|
||||||
|
p.inside_ct_if_expr = true
|
||||||
|
p.next() // `$`
|
||||||
|
p.prev_tok.position().extend(p.tok.position())
|
||||||
|
} else {
|
||||||
|
p.tok.position()
|
||||||
|
}
|
||||||
mut branches := []ast.IfBranch{}
|
mut branches := []ast.IfBranch{}
|
||||||
mut has_else := false
|
mut has_else := false
|
||||||
mut comments := []ast.Comment{}
|
mut comments := []ast.Comment{}
|
||||||
mut prev_guard := false
|
mut prev_guard := false
|
||||||
for p.tok.kind in [.key_if, .key_else] {
|
for p.tok.kind in [.key_if, .key_else] {
|
||||||
p.inside_if = true
|
p.inside_if = true
|
||||||
start_pos := p.tok.position()
|
start_pos := if is_comptime { p.prev_tok.position().extend(p.tok.position()) } else { p.tok.position() }
|
||||||
if p.tok.kind == .key_else {
|
if p.tok.kind == .key_else {
|
||||||
comments << p.eat_comments()
|
comments << p.eat_comments()
|
||||||
p.check(.key_else)
|
p.check(.key_else)
|
||||||
|
@ -59,6 +67,7 @@ fn (mut p Parser) if_expr() ast.IfExpr {
|
||||||
comments = []
|
comments = []
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if is_comptime { p.check(.dollar) }
|
||||||
}
|
}
|
||||||
// `if` or `else if`
|
// `if` or `else if`
|
||||||
p.check(.key_if)
|
p.check(.key_if)
|
||||||
|
@ -73,7 +82,7 @@ fn (mut p Parser) if_expr() ast.IfExpr {
|
||||||
mut cond := ast.Expr{}
|
mut cond := ast.Expr{}
|
||||||
mut is_guard := false
|
mut is_guard := false
|
||||||
// `if x := opt() {`
|
// `if x := opt() {`
|
||||||
if p.peek_tok.kind == .decl_assign {
|
if !is_comptime && p.peek_tok.kind == .decl_assign {
|
||||||
p.open_scope()
|
p.open_scope()
|
||||||
is_guard = true
|
is_guard = true
|
||||||
var_pos := p.tok.position()
|
var_pos := p.tok.position()
|
||||||
|
@ -102,7 +111,9 @@ fn (mut p Parser) if_expr() ast.IfExpr {
|
||||||
// if sum is T
|
// if sum is T
|
||||||
is_is_cast := infix.op == .key_is
|
is_is_cast := infix.op == .key_is
|
||||||
is_ident := infix.left is ast.Ident
|
is_ident := infix.left is ast.Ident
|
||||||
left_as_name = if is_is_cast && p.tok.kind == .key_as {
|
left_as_name = if is_comptime {
|
||||||
|
''
|
||||||
|
} else if is_is_cast && p.tok.kind == .key_as {
|
||||||
p.next()
|
p.next()
|
||||||
p.check_name()
|
p.check_name()
|
||||||
} else if is_ident {
|
} else if is_ident {
|
||||||
|
@ -129,11 +140,20 @@ fn (mut p Parser) if_expr() ast.IfExpr {
|
||||||
mut_name: mut_name
|
mut_name: mut_name
|
||||||
}
|
}
|
||||||
comments = p.eat_comments()
|
comments = p.eat_comments()
|
||||||
|
if is_comptime {
|
||||||
|
if p.tok.kind == .key_else {
|
||||||
|
p.error('use `\$else` instead of `else` in compile-time `if` branches')
|
||||||
|
}
|
||||||
|
if p.peek_tok.kind == .key_else {
|
||||||
|
p.check(.dollar)
|
||||||
|
}
|
||||||
|
}
|
||||||
if p.tok.kind != .key_else {
|
if p.tok.kind != .key_else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ast.IfExpr{
|
return ast.IfExpr{
|
||||||
|
is_comptime: is_comptime
|
||||||
branches: branches
|
branches: branches
|
||||||
post_comments: comments
|
post_comments: comments
|
||||||
pos: pos
|
pos: pos
|
||||||
|
|
|
@ -29,6 +29,7 @@ mut:
|
||||||
language table.Language
|
language table.Language
|
||||||
inside_if bool
|
inside_if bool
|
||||||
inside_if_expr bool
|
inside_if_expr bool
|
||||||
|
inside_ct_if_expr bool
|
||||||
inside_or_expr bool
|
inside_or_expr bool
|
||||||
inside_for bool
|
inside_for bool
|
||||||
inside_fn bool
|
inside_fn bool
|
||||||
|
@ -462,7 +463,9 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
|
||||||
return p.struct_decl()
|
return p.struct_decl()
|
||||||
}
|
}
|
||||||
.dollar {
|
.dollar {
|
||||||
return p.comp_if()
|
return ast.ExprStmt{
|
||||||
|
expr: p.if_expr(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.hash {
|
.hash {
|
||||||
return p.hash()
|
return p.hash()
|
||||||
|
@ -599,7 +602,9 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
||||||
}
|
}
|
||||||
.dollar {
|
.dollar {
|
||||||
if p.peek_tok.kind == .key_if {
|
if p.peek_tok.kind == .key_if {
|
||||||
return p.comp_if()
|
return ast.ExprStmt{
|
||||||
|
expr: p.if_expr(true)
|
||||||
|
}
|
||||||
} else if p.peek_tok.kind == .key_for {
|
} else if p.peek_tok.kind == .key_for {
|
||||||
return p.comp_for()
|
return p.comp_for()
|
||||||
} else if p.peek_tok.kind == .name {
|
} else if p.peek_tok.kind == .name {
|
||||||
|
|
|
@ -47,10 +47,14 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
||||||
node = p.enum_val()
|
node = p.enum_val()
|
||||||
}
|
}
|
||||||
.dollar {
|
.dollar {
|
||||||
if p.peek_tok.kind == .name {
|
match p.peek_tok.kind {
|
||||||
return p.vweb()
|
.name {
|
||||||
} else {
|
return p.vweb()
|
||||||
p.error('unexpected $')
|
} .key_if {
|
||||||
|
return p.if_expr(true)
|
||||||
|
} else {
|
||||||
|
p.error('unexpected $')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.chartoken {
|
.chartoken {
|
||||||
|
@ -89,7 +93,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.key_if {
|
.key_if {
|
||||||
node = p.if_expr()
|
node = p.if_expr(false)
|
||||||
}
|
}
|
||||||
.key_unsafe {
|
.key_unsafe {
|
||||||
p.next()
|
p.next()
|
||||||
|
@ -191,25 +195,34 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
||||||
p.check(.rcbr)
|
p.check(.rcbr)
|
||||||
}
|
}
|
||||||
.key_fn {
|
.key_fn {
|
||||||
// Anonymous function
|
if p.expecting_type {
|
||||||
node = p.anon_fn()
|
// Anonymous function type
|
||||||
// its a call
|
start_pos := p.tok.position()
|
||||||
// NOTE: this could be moved to just before the pratt loop
|
return ast.Type{
|
||||||
// then anything can be a call, eg. `index[2]()` or `struct.field()`
|
typ: p.parse_type()
|
||||||
// but this would take a bit of modification
|
pos: start_pos.extend(p.prev_tok.position())
|
||||||
if p.tok.kind == .lpar {
|
|
||||||
p.next()
|
|
||||||
pos := p.tok.position()
|
|
||||||
args := p.call_args()
|
|
||||||
p.check(.rpar)
|
|
||||||
node = ast.CallExpr{
|
|
||||||
name: 'anon'
|
|
||||||
left: node
|
|
||||||
args: args
|
|
||||||
pos: pos
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Anonymous function
|
||||||
|
node = p.anon_fn()
|
||||||
|
// its a call
|
||||||
|
// NOTE: this could be moved to just before the pratt loop
|
||||||
|
// then anything can be a call, eg. `index[2]()` or `struct.field()`
|
||||||
|
// but this would take a bit of modification
|
||||||
|
if p.tok.kind == .lpar {
|
||||||
|
p.next()
|
||||||
|
pos := p.tok.position()
|
||||||
|
args := p.call_args()
|
||||||
|
p.check(.rpar)
|
||||||
|
node = ast.CallExpr{
|
||||||
|
name: 'anon'
|
||||||
|
left: node
|
||||||
|
args: args
|
||||||
|
pos: pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node
|
||||||
}
|
}
|
||||||
return node
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
p.error('expr(): bad token `$p.tok.kind.str()`')
|
p.error('expr(): bad token `$p.tok.kind.str()`')
|
||||||
|
@ -262,7 +275,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
||||||
if p.tok.kind == .key_as && p.inside_if {
|
if p.tok.kind == .key_as && p.inside_if {
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
} else if p.tok.kind in [.inc, .dec] {
|
} else if p.tok.kind in [.inc, .dec] || (p.tok.kind == .question && p.inside_ct_if_expr) {
|
||||||
// Postfix
|
// Postfix
|
||||||
// detect `f(x++)`, `a[x++]`
|
// detect `f(x++)`, `a[x++]`
|
||||||
if p.peek_tok.kind in [.rpar, .rsbr] &&
|
if p.peek_tok.kind in [.rpar, .rsbr] &&
|
||||||
|
@ -294,10 +307,12 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr {
|
||||||
pos := p.tok.position()
|
pos := p.tok.position()
|
||||||
p.next()
|
p.next()
|
||||||
mut right := ast.Expr{}
|
mut right := ast.Expr{}
|
||||||
|
prev_expecting_type := p.expecting_type
|
||||||
if op in [.key_is, .not_is] {
|
if op in [.key_is, .not_is] {
|
||||||
p.expecting_type = true
|
p.expecting_type = true
|
||||||
}
|
}
|
||||||
right = p.expr(precedence)
|
right = p.expr(precedence)
|
||||||
|
p.expecting_type = prev_expecting_type
|
||||||
if p.pref.is_vet && op in [.key_in, .not_in] &&
|
if p.pref.is_vet && op in [.key_in, .not_in] &&
|
||||||
right is ast.ArrayInit && (right as ast.ArrayInit).exprs.len == 1 {
|
right is ast.ArrayInit && (right as ast.ArrayInit).exprs.len == 1 {
|
||||||
p.vet_error('Use `var == value` instead of `var in [value]`', pos.line_nr)
|
p.vet_error('Use `var == value` instead of `var in [value]`', pos.line_nr)
|
||||||
|
|
|
@ -59,6 +59,7 @@ pub fn (mut p Preferences) fill_with_defaults() {
|
||||||
if p.ccompiler == '' {
|
if p.ccompiler == '' {
|
||||||
p.ccompiler = default_c_compiler()
|
p.ccompiler = default_c_compiler()
|
||||||
}
|
}
|
||||||
|
p.ccompiler_type = cc_from_string(p.ccompiler)
|
||||||
p.is_test = p.path.ends_with('_test.v')
|
p.is_test = p.path.ends_with('_test.v')
|
||||||
p.is_vsh = p.path.ends_with('.vsh')
|
p.is_vsh = p.path.ends_with('.vsh')
|
||||||
p.is_script = p.is_vsh || p.path.ends_with('.v') || p.path.ends_with('.vv')
|
p.is_script = p.is_vsh || p.path.ends_with('.v') || p.path.ends_with('.vv')
|
||||||
|
|
|
@ -32,11 +32,12 @@ pub enum Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum CompilerType {
|
pub enum CompilerType {
|
||||||
|
gcc
|
||||||
tinyc
|
tinyc
|
||||||
clang
|
clang
|
||||||
mingw
|
mingw
|
||||||
msvc
|
msvc
|
||||||
gcc
|
cplusplus
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -81,7 +82,8 @@ pub mut:
|
||||||
// For example, passing -cflags -Os will cause the C compiler to optimize the generated binaries for size.
|
// For example, passing -cflags -Os will cause the C compiler to optimize the generated binaries for size.
|
||||||
// You could pass several -cflags XXX arguments. They will be merged with each other.
|
// You could pass several -cflags XXX arguments. They will be merged with each other.
|
||||||
// You can also quote several options at the same time: -cflags '-Os -fno-inline-small-functions'.
|
// You can also quote several options at the same time: -cflags '-Os -fno-inline-small-functions'.
|
||||||
ccompiler string // the name of the used C compiler
|
ccompiler string // the name of the C compiler used
|
||||||
|
ccompiler_type CompilerType // the type of the C compiler used
|
||||||
third_party_option string
|
third_party_option string
|
||||||
building_v bool
|
building_v bool
|
||||||
autofree bool
|
autofree bool
|
||||||
|
@ -376,6 +378,19 @@ pub fn backend_from_string(s string) ?Backend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to convert string names to CC enum
|
||||||
|
pub fn cc_from_string(cc_str string) CompilerType {
|
||||||
|
if cc_str.len == 0 { return .gcc } // TODO
|
||||||
|
cc := cc_str.replace('\\', '/').split('/').last().all_before('.')
|
||||||
|
if '++' in cc { return .cplusplus }
|
||||||
|
if 'tcc' in cc { return .tinyc }
|
||||||
|
if 'tinyc' in cc { return .tinyc }
|
||||||
|
if 'clang' in cc { return .clang }
|
||||||
|
if 'mingw' in cc { return .mingw }
|
||||||
|
if 'msvc' in cc { return .msvc }
|
||||||
|
return .gcc
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_define(mut prefs Preferences, define string) {
|
fn parse_define(mut prefs Preferences, define string) {
|
||||||
define_parts := define.split('=')
|
define_parts := define.split('=')
|
||||||
if define_parts.len == 1 {
|
if define_parts.len == 1 {
|
||||||
|
|
|
@ -329,9 +329,10 @@ pub fn build_precedences() []Precedence {
|
||||||
|
|
||||||
p[Kind.lsbr] = .index
|
p[Kind.lsbr] = .index
|
||||||
p[Kind.dot] = .call
|
p[Kind.dot] = .call
|
||||||
// `++` | `--`
|
// `++` | `--` | `?`
|
||||||
p[Kind.inc] = .postfix
|
p[Kind.inc] = .postfix
|
||||||
p[Kind.dec] = .postfix
|
p[Kind.dec] = .postfix
|
||||||
|
p[Kind.question] = .postfix
|
||||||
// `*` | `/` | `%` | `<<` | `>>` | `&`
|
// `*` | `/` | `%` | `<<` | `>>` | `&`
|
||||||
p[Kind.mul] = .product
|
p[Kind.mul] = .product
|
||||||
p[Kind.div] = .product
|
p[Kind.div] = .product
|
||||||
|
|
Loading…
Reference in New Issue