js: comptime&assert improvements, more `byte` and `strings.Builder` methods ported (#12096)
parent
42359d8915
commit
33a1006cc5
|
@ -78,3 +78,21 @@ fn js_stacktrace() string {
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -7,14 +7,6 @@ pub fn (b byte) is_space() bool {
|
|||
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 {
|
||||
res := ''
|
||||
#res.str = c.val.toString()
|
||||
|
@ -37,3 +29,42 @@ pub fn (c byte) repeat(count int) string {
|
|||
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -23,35 +23,35 @@ pub fn (i u16) str() string {
|
|||
|
||||
pub fn (i int) str() string {
|
||||
mut res := ''
|
||||
#res = new string( i )
|
||||
#res = new string( i+'' )
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (i i64) str() string {
|
||||
mut res := ''
|
||||
#res = new string( i )
|
||||
#res = new string( i + '')
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (i u32) str() string {
|
||||
mut res := ''
|
||||
#res = new string( i )
|
||||
#res = new string( i + '')
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (i u64) str() string {
|
||||
mut res := ''
|
||||
#res = new string( i )
|
||||
#res = new string( i + '')
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (i bool) str() string {
|
||||
mut res := ''
|
||||
#res = new string( i )
|
||||
#res = new string( i + '')
|
||||
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -232,6 +232,13 @@ pub fn (s string) u64() u64 {
|
|||
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.
|
||||
// Example: assert ' Hello V d'.trim_right(' d') == ' Hello V'
|
||||
pub fn (s string) trim_right(cutset string) string {
|
||||
|
@ -897,3 +904,6 @@ pub fn (s []string) join(sep string) string {
|
|||
}
|
||||
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 */ } });
|
||||
|
|
|
@ -359,11 +359,10 @@ fn test_reassign() {
|
|||
assert b == 'hi!'
|
||||
}
|
||||
|
||||
/*
|
||||
fn test_runes() {
|
||||
s := 'привет'
|
||||
println(s.len)
|
||||
//assert s.len == 12
|
||||
// assert s.len == 12
|
||||
s2 := 'privet'
|
||||
assert s2.len == 6
|
||||
u := s.runes()
|
||||
|
@ -386,7 +385,7 @@ fn test_runes() {
|
|||
last := u[u.len - 1]
|
||||
assert first.str().len == 2
|
||||
assert last.str().len == 2
|
||||
}*/
|
||||
}
|
||||
|
||||
fn test_contains() {
|
||||
s := 'view.v'
|
||||
|
@ -420,7 +419,6 @@ fn test_arr_contains() {
|
|||
assert ints.contains(2)
|
||||
}
|
||||
|
||||
/*
|
||||
fn test_to_num() {
|
||||
s := '7'
|
||||
assert s.int() == 7
|
||||
|
@ -434,7 +432,7 @@ fn test_to_num() {
|
|||
big := '93993993939322'
|
||||
assert big.u64() == 93993993939322
|
||||
assert big.i64() == 93993993939322
|
||||
}*/
|
||||
}
|
||||
|
||||
/*
|
||||
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) == 592861
|
||||
}*/
|
||||
|
||||
fn test_trim() {
|
||||
assert 'banana'.trim('bna') == ''
|
||||
assert 'abc'.trim('ac') == 'b'
|
||||
|
|
|
@ -97,3 +97,25 @@ pub fn (mut b Builder) write_runes(runes []rune) {
|
|||
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()
|
||||
}
|
||||
|
|
|
@ -272,7 +272,7 @@ fn (mut g JsGen) comp_if_to_ifdef(name string, is_comptime_optional bool) ?strin
|
|||
return 'false'
|
||||
}
|
||||
'no_bounds_checking' {
|
||||
return 'CUSTOM_DEFINE_no_bounds_checking'
|
||||
return 'checkDefine("CUSTOM_DEFINE_no_bounds_checking")'
|
||||
}
|
||||
'freestanding' {
|
||||
return '_VFREESTANDING'
|
||||
|
@ -301,7 +301,7 @@ fn (mut g JsGen) comp_if_to_ifdef(name string, is_comptime_optional bool) ?strin
|
|||
else {
|
||||
if is_comptime_optional
|
||||
|| (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
|
||||
}
|
||||
|
|
|
@ -221,6 +221,7 @@ fn (mut g JsGen) method_call(node ast.CallExpr) {
|
|||
name = g.generic_fn_name(node.concrete_types, name, false)
|
||||
g.write('${name}(')
|
||||
g.expr(it.left)
|
||||
g.gen_deref_ptr(it.left_type)
|
||||
g.write(',')
|
||||
for i, arg in it.args {
|
||||
g.expr(arg.expr)
|
||||
|
|
|
@ -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 {
|
||||
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(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(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
|
||||
|
@ -410,19 +409,14 @@ pub fn (mut g JsGen) init() {
|
|||
|
||||
g.definitions.writeln('const \$os = {')
|
||||
g.definitions.writeln(' endianess: "LE",')
|
||||
|
||||
g.definitions.writeln('}')
|
||||
} else {
|
||||
g.definitions.writeln('const \$os = require("os");')
|
||||
g.definitions.writeln('const \$process = process;')
|
||||
}
|
||||
g.definitions.writeln('function alias(value) { return value; } ')
|
||||
|
||||
g.definitions.writeln('function \$v_fmt(value) { let res = "";
|
||||
if (Object.getPrototypeOf(s).hasOwnProperty("str") && typeof s.str == "function") res = s.str().str
|
||||
else res = s.toString()
|
||||
return res
|
||||
} ')
|
||||
g.definitions.writeln('function checkDefine(key) {')
|
||||
g.definitions.writeln('\tif (globalThis.hasOwnProperty(key)) { return !!globalThis[key]; } return false;')
|
||||
g.definitions.writeln('}')
|
||||
}
|
||||
|
||||
pub fn (g JsGen) hashes() string {
|
||||
|
@ -848,7 +842,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
|
|||
// TODO
|
||||
}
|
||||
ast.CTempVar {
|
||||
g.write('/* ast.CTempVar: node.name */')
|
||||
g.write('$node.name')
|
||||
}
|
||||
ast.DumpExpr {
|
||||
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 {
|
||||
mod_path := g.file.path
|
||||
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}.llabel = new string("$expr_left_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.writeln('");')
|
||||
g.write('\t${metaname}.rvalue = new string("')
|
||||
g.writeln(';')
|
||||
g.write('\t${metaname}.rvalue = ')
|
||||
g.gen_assert_single_expr(node.expr.right, node.expr.right_type)
|
||||
g.writeln('");')
|
||||
g.writeln(';')
|
||||
}
|
||||
ast.CallExpr {
|
||||
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*'
|
||||
match expr {
|
||||
ast.CastExpr, ast.IfExpr, ast.IndexExpr, ast.MatchExpr {
|
||||
g.write(unknown_value)
|
||||
g.write('new string("$unknown_value")')
|
||||
}
|
||||
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 {
|
||||
sym := g.table.get_type_symbol(g.unwrap_generic(typ))
|
||||
g.write('$sym.name')
|
||||
g.write('new string("$sym.name"')
|
||||
}
|
||||
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() + ' */ ')
|
||||
}
|
||||
|
||||
// TODO
|
||||
fn (mut g JsGen) gen_assert_stmt(a ast.AssertStmt) {
|
||||
if !a.is_used {
|
||||
fn (mut g JsGen) gen_assert_stmt(orig_node ast.AssertStmt) {
|
||||
mut node := orig_node
|
||||
if !node.is_used {
|
||||
return
|
||||
}
|
||||
|
||||
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.expr(a.expr)
|
||||
g.write('.valueOf() ) {')
|
||||
|
|
Loading…
Reference in New Issue