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
}
// 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
}
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()
}

View File

@ -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
}

View File

@ -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 */ } });

View File

@ -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'

View File

@ -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()
}

View File

@ -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
}

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)
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)

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 {
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() ) {')