js: comptime&assert improvements, more `byte` and `strings.Builder` methods ported (#12096)

pull/12102/head
playX 2021-10-07 15:55:47 +03:00 committed by GitHub
parent 42359d8915
commit 33a1006cc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 209 additions and 41 deletions

View File

@ -78,3 +78,21 @@ fn js_stacktrace() string {
return stacktrace return stacktrace
} }
// Check for nil value
pub fn isnil(val voidptr) bool {
res := false
// This one is kinda weird. In C and native backend we can cast booleans and integers to pointers
// so we just check *for* all possible NULL-like values here.
#val = val.valueOf()
#res.val = val === null || val === undefined || val === false || val === 0 || val === BigInt(0)
return res
}
pub fn (f float_literal) str() string {
res := ''
#res.str += f.valueOf()
return res
}

View File

@ -7,14 +7,6 @@ pub fn (b byte) is_space() bool {
return result return result
} }
pub fn (c byte) is_letter() bool {
result := false
#result.val = (c.val >= `a`.charCodeAt() && c.val <= `z`.charCodeAt()) || (c.val >= `A`.charCodeAt() && c.val <= `Z`.charCodeAt())
return result
}
pub fn (c byte) str() string { pub fn (c byte) str() string {
res := '' res := ''
#res.str = c.val.toString() #res.str = c.val.toString()
@ -37,3 +29,42 @@ pub fn (c byte) repeat(count int) string {
return res return res
} }
pub fn (c byte) is_digit() bool {
return c >= `0` && c <= `9`
}
// is_hex_digit returns `true` if the byte is either in range 0-9, a-f or A-F and `false` otherwise.
// Example: assert byte(`F`) == true
[inline]
pub fn (c byte) is_hex_digit() bool {
return c.is_digit() || (c >= `a` && c <= `f`) || (c >= `A` && c <= `F`)
}
// is_oct_digit returns `true` if the byte is in range 0-7 and `false` otherwise.
// Example: assert byte(`7`) == true
[inline]
pub fn (c byte) is_oct_digit() bool {
return c >= `0` && c <= `7`
}
// is_bin_digit returns `true` if the byte is a binary digit (0 or 1) and `false` otherwise.
// Example: assert byte(`0`) == true
[inline]
pub fn (c byte) is_bin_digit() bool {
return c == `0` || c == `1`
}
// is_letter returns `true` if the byte is in range a-z or A-Z and `false` otherwise.
// Example: assert byte(`V`) == true
[inline]
pub fn (c byte) is_letter() bool {
return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`)
}
// is_alnum returns `true` if the byte is in range a-z, A-Z, 0-9 and `false` otherwise.
// Example: assert byte(`V`) == true
[inline]
pub fn (c byte) is_alnum() bool {
return c.is_letter() || c.is_digit()
}

View File

@ -23,35 +23,35 @@ pub fn (i u16) str() string {
pub fn (i int) str() string { pub fn (i int) str() string {
mut res := '' mut res := ''
#res = new string( i ) #res = new string( i+'' )
return res return res
} }
pub fn (i i64) str() string { pub fn (i i64) str() string {
mut res := '' mut res := ''
#res = new string( i ) #res = new string( i + '')
return res return res
} }
pub fn (i u32) str() string { pub fn (i u32) str() string {
mut res := '' mut res := ''
#res = new string( i ) #res = new string( i + '')
return res return res
} }
pub fn (i u64) str() string { pub fn (i u64) str() string {
mut res := '' mut res := ''
#res = new string( i ) #res = new string( i + '')
return res return res
} }
pub fn (i bool) str() string { pub fn (i bool) str() string {
mut res := '' mut res := ''
#res = new string( i ) #res = new string( i + '')
return res return res
} }

View File

@ -232,6 +232,13 @@ pub fn (s string) u64() u64 {
return u64(JS.parseInt(s)) return u64(JS.parseInt(s))
} }
pub fn (s string) byte() u64 {
res := byte(0)
#res.val = byte(JS.parseInt(s))
return res
}
// trim_right strips any of the characters given in `cutset` from the right of the string. // trim_right strips any of the characters given in `cutset` from the right of the string.
// Example: assert ' Hello V d'.trim_right(' d') == ' Hello V' // Example: assert ' Hello V d'.trim_right(' d') == ' Hello V'
pub fn (s string) trim_right(cutset string) string { pub fn (s string) trim_right(cutset string) string {
@ -897,3 +904,6 @@ pub fn (s []string) join(sep string) string {
} }
return res return res
} }
// There's no better way to find length of JS String in bytes.
#Object.defineProperty(string.prototype,"len", { get: function() {return new int(new TextEncoder().encode(this.str).length);}, set: function(l) {/* ignore */ } });

View File

@ -359,11 +359,10 @@ fn test_reassign() {
assert b == 'hi!' assert b == 'hi!'
} }
/*
fn test_runes() { fn test_runes() {
s := 'привет' s := 'привет'
println(s.len) println(s.len)
//assert s.len == 12 // assert s.len == 12
s2 := 'privet' s2 := 'privet'
assert s2.len == 6 assert s2.len == 6
u := s.runes() u := s.runes()
@ -386,7 +385,7 @@ fn test_runes() {
last := u[u.len - 1] last := u[u.len - 1]
assert first.str().len == 2 assert first.str().len == 2
assert last.str().len == 2 assert last.str().len == 2
}*/ }
fn test_contains() { fn test_contains() {
s := 'view.v' s := 'view.v'
@ -420,7 +419,6 @@ fn test_arr_contains() {
assert ints.contains(2) assert ints.contains(2)
} }
/*
fn test_to_num() { fn test_to_num() {
s := '7' s := '7'
assert s.int() == 7 assert s.int() == 7
@ -434,7 +432,7 @@ fn test_to_num() {
big := '93993993939322' big := '93993993939322'
assert big.u64() == 93993993939322 assert big.u64() == 93993993939322
assert big.i64() == 93993993939322 assert big.i64() == 93993993939322
}*/ }
/* /*
fn test_inter_format_string() { fn test_inter_format_string() {
@ -473,7 +471,6 @@ fn test_hash() {
assert s5.hash() % ((1 << 20) - 1) == s.hash() % ((1 << 20) - 1) assert s5.hash() % ((1 << 20) - 1) == s.hash() % ((1 << 20) - 1)
assert s5.hash() % ((1 << 20) - 1) == 592861 assert s5.hash() % ((1 << 20) - 1) == 592861
}*/ }*/
fn test_trim() { fn test_trim() {
assert 'banana'.trim('bna') == '' assert 'banana'.trim('bna') == ''
assert 'abc'.trim('ac') == 'b' assert 'abc'.trim('ac') == 'b'

View File

@ -97,3 +97,25 @@ pub fn (mut b Builder) write_runes(runes []rune) {
b << res.bytes() b << res.bytes()
} }
} }
// after(6) returns 'world'
// buf == 'hello world'
pub fn (mut b Builder) after(n int) string {
if n >= b.len {
return ''
}
x := b.slice(n, b.len)
return x.bytestr()
}
// last_n(5) returns 'world'
// buf == 'hello world'
pub fn (b &Builder) last_n(n int) string {
if n >= b.len {
return ''
}
x := b.slice(b.len - n, b.len)
return x.bytestr()
}

View File

@ -272,7 +272,7 @@ fn (mut g JsGen) comp_if_to_ifdef(name string, is_comptime_optional bool) ?strin
return 'false' return 'false'
} }
'no_bounds_checking' { 'no_bounds_checking' {
return 'CUSTOM_DEFINE_no_bounds_checking' return 'checkDefine("CUSTOM_DEFINE_no_bounds_checking")'
} }
'freestanding' { 'freestanding' {
return '_VFREESTANDING' return '_VFREESTANDING'
@ -301,7 +301,7 @@ fn (mut g JsGen) comp_if_to_ifdef(name string, is_comptime_optional bool) ?strin
else { else {
if is_comptime_optional if is_comptime_optional
|| (g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all) { || (g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all) {
return 'CUSTOM_DEFINE_$name' return 'checkDefine("CUSTOM_DEFINE_$name")'
} }
return error('bad os ifdef name "$name"') // should never happen, caught in the checker return error('bad os ifdef name "$name"') // should never happen, caught in the checker
} }

View File

@ -221,6 +221,7 @@ fn (mut g JsGen) method_call(node ast.CallExpr) {
name = g.generic_fn_name(node.concrete_types, name, false) name = g.generic_fn_name(node.concrete_types, name, false)
g.write('${name}(') g.write('${name}(')
g.expr(it.left) g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write(',') g.write(',')
for i, arg in it.args { for i, arg in it.args {
g.expr(arg.expr) g.expr(arg.expr)

View File

@ -144,7 +144,6 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
if g.file.mod.name == 'builtin' && !g.generated_builtin { if g.file.mod.name == 'builtin' && !g.generated_builtin {
g.gen_builtin_type_defs() g.gen_builtin_type_defs()
g.writeln('Object.defineProperty(array.prototype,"len", { get: function() {return new int(this.arr.arr.length);}, set: function(l) { this.arr.arr.length = l.valueOf(); } }); ') g.writeln('Object.defineProperty(array.prototype,"len", { get: function() {return new int(this.arr.arr.length);}, set: function(l) { this.arr.arr.length = l.valueOf(); } }); ')
g.writeln('Object.defineProperty(string.prototype,"len", { get: function() {return new int(this.str.length);}, set: function(l) {/* ignore */ } }); ')
g.writeln('Object.defineProperty(map.prototype,"len", { get: function() {return new int(this.map.size);}, set: function(l) { this.map.size = l.valueOf(); } }); ') g.writeln('Object.defineProperty(map.prototype,"len", { get: function() {return new int(this.map.size);}, set: function(l) { this.map.size = l.valueOf(); } }); ')
g.writeln('Object.defineProperty(array.prototype,"length", { get: function() {return new int(this.arr.arr.length);}, set: function(l) { this.arr.arr.length = l.valueOf(); } }); ') g.writeln('Object.defineProperty(array.prototype,"length", { get: function() {return new int(this.arr.arr.length);}, set: function(l) { this.arr.arr.length = l.valueOf(); } }); ')
g.generated_builtin = true g.generated_builtin = true
@ -410,19 +409,14 @@ pub fn (mut g JsGen) init() {
g.definitions.writeln('const \$os = {') g.definitions.writeln('const \$os = {')
g.definitions.writeln(' endianess: "LE",') g.definitions.writeln(' endianess: "LE",')
g.definitions.writeln('}') g.definitions.writeln('}')
} else { } else {
g.definitions.writeln('const \$os = require("os");') g.definitions.writeln('const \$os = require("os");')
g.definitions.writeln('const \$process = process;') g.definitions.writeln('const \$process = process;')
} }
g.definitions.writeln('function alias(value) { return value; } ') g.definitions.writeln('function checkDefine(key) {')
g.definitions.writeln('\tif (globalThis.hasOwnProperty(key)) { return !!globalThis[key]; } return false;')
g.definitions.writeln('function \$v_fmt(value) { let res = ""; g.definitions.writeln('}')
if (Object.getPrototypeOf(s).hasOwnProperty("str") && typeof s.str == "function") res = s.str().str
else res = s.toString()
return res
} ')
} }
pub fn (g JsGen) hashes() string { pub fn (g JsGen) hashes() string {
@ -848,7 +842,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
// TODO // TODO
} }
ast.CTempVar { ast.CTempVar {
g.write('/* ast.CTempVar: node.name */') g.write('$node.name')
} }
ast.DumpExpr { ast.DumpExpr {
g.write('/* ast.DumpExpr: $node.expr */') g.write('/* ast.DumpExpr: $node.expr */')
@ -1008,6 +1002,60 @@ fn (mut g JsGen) expr(node ast.Expr) {
} }
} }
struct UnsupportedAssertCtempTransform {
msg string
code int
}
const unsupported_ctemp_assert_transform = IError(UnsupportedAssertCtempTransform{})
fn (mut g JsGen) assert_subexpression_to_ctemp(expr ast.Expr, expr_type ast.Type) ?ast.Expr {
match expr {
ast.CallExpr {
return g.new_ctemp_var_then_gen(expr, expr_type)
}
ast.ParExpr {
if expr.expr is ast.CallExpr {
return g.new_ctemp_var_then_gen(expr.expr, expr_type)
}
}
ast.SelectorExpr {
if expr.expr is ast.CallExpr {
sym := g.table.get_final_type_symbol(g.unwrap_generic(expr.expr.return_type))
if sym.kind == .struct_ {
if (sym.info as ast.Struct).is_union {
return js.unsupported_ctemp_assert_transform
}
}
return g.new_ctemp_var_then_gen(expr, expr_type)
}
}
else {}
}
return js.unsupported_ctemp_assert_transform
}
fn (mut g JsGen) new_ctemp_var(expr ast.Expr, expr_type ast.Type) ast.CTempVar {
return ast.CTempVar{
name: g.new_tmp_var()
typ: expr_type
is_ptr: expr_type.is_ptr()
orig: expr
}
}
fn (mut g JsGen) new_ctemp_var_then_gen(expr ast.Expr, expr_type ast.Type) ast.CTempVar {
x := g.new_ctemp_var(expr, expr_type)
g.gen_ctemp_var(x)
return x
}
fn (mut g JsGen) gen_ctemp_var(tvar ast.CTempVar) {
g.write('let $tvar.name = ')
g.expr(tvar.orig)
g.writeln(';')
}
fn (mut g JsGen) gen_assert_metainfo(node ast.AssertStmt) string { fn (mut g JsGen) gen_assert_metainfo(node ast.AssertStmt) string {
mod_path := g.file.path mod_path := g.file.path
fn_name := g.fn_decl.name fn_name := g.fn_decl.name
@ -1029,12 +1077,12 @@ fn (mut g JsGen) gen_assert_metainfo(node ast.AssertStmt) string {
g.writeln('\t${metaname}.op = new string("$expr_op_str");') g.writeln('\t${metaname}.op = new string("$expr_op_str");')
g.writeln('\t${metaname}.llabel = new string("$expr_left_str");') g.writeln('\t${metaname}.llabel = new string("$expr_left_str");')
g.writeln('\t${metaname}.rlabel = new string("$expr_right_str");') g.writeln('\t${metaname}.rlabel = new string("$expr_right_str");')
g.write('\t${metaname}.lvalue = new string("') g.write('\t${metaname}.lvalue = ')
g.gen_assert_single_expr(node.expr.left, node.expr.left_type) g.gen_assert_single_expr(node.expr.left, node.expr.left_type)
g.writeln('");') g.writeln(';')
g.write('\t${metaname}.rvalue = new string("') g.write('\t${metaname}.rvalue = ')
g.gen_assert_single_expr(node.expr.right, node.expr.right_type) g.gen_assert_single_expr(node.expr.right, node.expr.right_type)
g.writeln('");') g.writeln(';')
} }
ast.CallExpr { ast.CallExpr {
g.writeln('\t${metaname}.op = new string("call");') g.writeln('\t${metaname}.op = new string("call");')
@ -1049,28 +1097,69 @@ fn (mut g JsGen) gen_assert_single_expr(expr ast.Expr, typ ast.Type) {
unknown_value := '*unknown value*' unknown_value := '*unknown value*'
match expr { match expr {
ast.CastExpr, ast.IfExpr, ast.IndexExpr, ast.MatchExpr { ast.CastExpr, ast.IfExpr, ast.IndexExpr, ast.MatchExpr {
g.write(unknown_value) g.write('new string("$unknown_value")')
} }
ast.PrefixExpr { ast.PrefixExpr {
g.write(unknown_value) if expr.right is ast.CastExpr {
// TODO: remove this check;
// vlib/builtin/map_test.v (a map of &int, set to &int(0)) fails
// without special casing ast.CastExpr here
g.write('new string("$unknown_value")')
} else {
g.gen_expr_to_string(expr, typ)
}
} }
ast.TypeNode { ast.TypeNode {
sym := g.table.get_type_symbol(g.unwrap_generic(typ)) sym := g.table.get_type_symbol(g.unwrap_generic(typ))
g.write('$sym.name') g.write('new string("$sym.name"')
} }
else { else {
g.write(unknown_value) mut should_clone := true
if typ == ast.string_type && expr is ast.StringLiteral {
should_clone = false
}
if expr is ast.CTempVar {
if expr.orig is ast.CallExpr {
should_clone = false
if expr.orig.or_block.kind == .propagate {
should_clone = true
}
if expr.orig.is_method && expr.orig.args.len == 0
&& expr.orig.name == 'type_name' {
should_clone = true
}
}
}
if should_clone {
g.write('string_clone(')
}
g.gen_expr_to_string(expr, typ)
if should_clone {
g.write(')')
}
} }
} }
// g.writeln(' /* typeof: ' + expr.type_name() + ' type: ' + typ.str() + ' */ ') // g.writeln(' /* typeof: ' + expr.type_name() + ' type: ' + typ.str() + ' */ ')
} }
// TODO // TODO
fn (mut g JsGen) gen_assert_stmt(a ast.AssertStmt) { fn (mut g JsGen) gen_assert_stmt(orig_node ast.AssertStmt) {
if !a.is_used { mut node := orig_node
if !node.is_used {
return return
} }
g.writeln('// assert') g.writeln('// assert')
if mut node.expr is ast.InfixExpr {
if subst_expr := g.assert_subexpression_to_ctemp(node.expr.left, node.expr.left_type) {
node.expr.left = subst_expr
}
if subst_expr := g.assert_subexpression_to_ctemp(node.expr.right, node.expr.right_type) {
node.expr.right = subst_expr
}
}
mut a := node
g.write('if( ') g.write('if( ')
g.expr(a.expr) g.expr(a.expr)
g.write('.valueOf() ) {') g.write('.valueOf() ) {')