v/vlib/v/gen/js/comptime.v

318 lines
6.7 KiB
V

module js
import v.ast
import v.pref
fn (mut g JsGen) comptime_if(node ast.IfExpr) {
if !node.is_expr && !node.has_else && node.branches.len == 1 {
if node.branches[0].stmts.len == 0 {
// empty ifdef; result of target OS != conditional => skip
return
}
}
for i, branch in node.branches {
if i == node.branches.len - 1 && node.has_else {
g.writeln('else')
} else {
if i == 0 {
g.write('if (')
} else {
g.write('else if (')
}
g.comptime_if_cond(branch.cond, branch.pkg_exist)
g.writeln(')')
}
if node.is_expr {
print('$branch.stmts')
len := branch.stmts.len
if len > 0 {
last := branch.stmts[len - 1] as ast.ExprStmt
if len > 1 {
tmp := g.new_tmp_var()
g.inc_indent()
g.writeln('let $tmp;')
g.writeln('{')
g.stmts(branch.stmts[0..len - 1])
g.write('\t$tmp = ')
g.stmt(last)
g.writeln('}')
g.dec_indent()
g.writeln('$tmp;')
} else {
g.stmt(last)
}
}
} else {
g.writeln('{')
g.stmts(branch.stmts)
g.writeln('}')
}
}
}
/*
// returning `false` means the statements inside the $if can be skipped
*/
// returns the value of the bool comptime expression
fn (mut g JsGen) comptime_if_cond(cond ast.Expr, pkg_exist bool) bool {
match cond {
ast.BoolLiteral {
g.expr(cond)
return true
}
ast.ParExpr {
g.write('(')
is_cond_true := g.comptime_if_cond(cond.expr, pkg_exist)
g.write(')')
return is_cond_true
}
ast.PrefixExpr {
g.write(cond.op.str())
return g.comptime_if_cond(cond.right, pkg_exist)
}
ast.PostfixExpr {
ifdef := g.comptime_if_to_ifdef((cond.expr as ast.Ident).name, true) or {
verror(err.msg())
return false
}
g.write('$ifdef')
return true
}
ast.InfixExpr {
match cond.op {
.and, .logical_or {
l := g.comptime_if_cond(cond.left, pkg_exist)
g.write(' $cond.op ')
r := g.comptime_if_cond(cond.right, pkg_exist)
return if cond.op == .and { l && r } else { l || r }
}
.key_is, .not_is {
left := cond.left
mut name := ''
mut exp_type := ast.Type(0)
got_type := (cond.right as ast.TypeNode).typ
// Handle `$if x is Interface {`
// mut matches_interface := 'false'
if left is ast.TypeNode && cond.right is ast.TypeNode
&& g.table.sym(got_type).kind == .interface_ {
// `$if Foo is Interface {`
interface_sym := g.table.sym(got_type)
if interface_sym.info is ast.Interface {
// q := g.table.sym(interface_sym.info.types[0])
checked_type := g.unwrap_generic(left.typ)
// TODO PERF this check is run twice (also in the checker)
// store the result in a field
is_true := g.table.does_type_implement_interface(checked_type,
got_type)
// true // exp_type in interface_sym.info.types
if cond.op == .key_is {
if is_true {
g.write('1')
} else {
g.write('0')
}
return is_true
} else if cond.op == .not_is {
if is_true {
g.write('0')
} else {
g.write('1')
}
return !is_true
}
// matches_interface = '/*iface:$got_type $exp_type*/ true'
//}
}
} else if left is ast.SelectorExpr {
name = '${left.expr}.$left.field_name'
exp_type = g.comptime_var_type_map[name]
} else if left is ast.TypeNode {
// this is only allowed for generics currently, otherwise blocked by checker
exp_type = g.unwrap_generic(left.typ)
}
if cond.op == .key_is {
g.write('$exp_type == $got_type')
return exp_type == got_type
} else {
g.write('$exp_type != $got_type')
return exp_type != got_type
}
}
.eq, .ne {
// TODO Implement `$if method.args.len == 1`
g.write('1')
return true
}
else {
return true
}
}
}
ast.Ident {
ifdef := g.comptime_if_to_ifdef(cond.name, false) or { 'true' } // handled in checker
g.write('$ifdef')
return true
}
ast.ComptimeCall {
g.write('$pkg_exist')
return true
}
else {
// should be unreachable, but just in case
g.write('1')
return true
}
}
}
fn (mut g JsGen) comptime_if_to_ifdef(name string, is_comptime_optional bool) ?string {
match name {
// platforms/os-es:
'windows' {
return '(\$process.platform == "windows")'
}
'ios' {
return '(\$process.platform == "darwin")'
}
'macos' {
return '(\$process.platform == "darwin")'
}
'mach' {
return '(\$process.platform == "darwin")'
}
'darwin' {
return '(\$process.platform == "darwin")'
}
'linux' {
return '(\$process.platform == "linux")'
}
'freebsd' {
return '(\$process.platform == "freebsd")'
}
'openbsd' {
return '(\$process.platform == "openbsd")'
}
'bsd' {
return '(\$process.platform == "freebsd" || (\$process.platform == "openbsd"))'
}
'android' {
return '(\$process.platform == "android")'
}
'solaris' {
return '(\$process.platform == "sunos")'
}
'js_node' {
if g.pref.backend == .js_node {
return 'true'
} else {
return 'false'
}
}
'js_freestanding' {
if g.pref.backend == .js_freestanding {
return 'true'
} else {
return 'false'
}
}
'js_browser' {
if g.pref.backend == .js_browser {
return 'true'
} else {
return 'false'
}
}
'es5' {
if g.pref.output_es5 {
return 'true'
} else {
return 'false'
}
}
//
'js' {
return 'true'
}
// compilers:
'gcc' {
return 'false'
}
'tinyc' {
return 'false'
}
'clang' {
return 'false'
}
'mingw' {
return 'false'
}
'msvc' {
return 'false'
}
'cplusplus' {
return 'false'
}
// other:
'threads' {
return 'false'
}
'gcboehm' {
return 'false'
}
// todo(playX): these should return true or false depending on CLI options
'debug' {
return 'false'
}
'prod' {
return 'false'
}
'test' {
return 'false'
}
'glibc' {
return 'false'
}
'prealloc' {
return 'false'
}
'no_bounds_checking' {
return 'checkDefine("CUSTOM_DEFINE_no_bounds_checking")'
}
'freestanding' {
return '_VFREESTANDING'
}
// architectures:
'amd64' {
return '(\$process.arch == "x64")'
}
'aarch64', 'arm64' {
return '(\$process.arch == "arm64)'
}
// bitness:
'x64' {
return '(\$process.arch == "x64")'
}
'x32' {
return '(\$process.arch == "x32")'
}
// endianness:
'little_endian' {
return '(\$os.endianess == "LE")'
}
'big_endian' {
return '(\$os.endianess == "BE")'
}
else {
if is_comptime_optional
|| (g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all) {
return 'checkDefine("CUSTOM_DEFINE_$name")'
}
return error('bad os ifdef name "$name"') // should never happen, caught in the checker
}
}
return none
}