v.gen.js: fix method calls and other codegen parts, rand module compiles (#11205)

pull/11229/head
playX 2021-08-18 11:33:37 +03:00 committed by GitHub
parent c51f83efba
commit 0121c8b4fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 301 additions and 130 deletions

View File

@ -84,11 +84,11 @@ fn (a &array) set_len(i int) {
}
pub fn (mut a array) sort_with_compare(compare voidptr) {
#a.arr.sort(compare)
#a.val.arr.sort(compare)
}
pub fn (mut a array) sort() {
#a.arr.sort($sortComparator)
#a.val.arr.sort($sortComparator)
}
pub fn (a array) index(v string) int {
@ -110,16 +110,16 @@ pub fn (a array) slice(start int, end int) array {
}
pub fn (mut a array) insert(i int, val voidptr) {
#a.arr.splice(i,0,val)
#a.val.arr.splice(i,0,val)
}
pub fn (mut a array) insert_many(i int, val voidptr, size int) {
#a.arr.splice(i,0,...val.slice(0,+size))
#a.val.arr.splice(i,0,...val.slice(0,+size))
}
pub fn (mut a array) join(separator string) string {
mut res := ''
#res = new builtin.string(a.arr.join(separator +''));
#res = new builtin.string(a.val.arr.join(separator +''));
return res
}
@ -164,7 +164,7 @@ pub fn (mut a array) delete(i int) {
// delete_many deletes `size` elements beginning with index `i`
pub fn (mut a array) delete_many(i int, size int) {
#a.arr.splice(i.valueOf(),size.valueOf())
#a.val.arr.splice(i.valueOf(),size.valueOf())
}
// prepend prepends one value to the array.
@ -186,7 +186,7 @@ pub fn (a array) reverse() array {
}
pub fn (mut a array) reverse_in_place() {
#a.arr.reverse()
#a.val.arr.reverse()
}
#array.prototype.$includes = function (elem) { return this.arr.find(function(e) { return vEq(elem,e); }) !== undefined;}
@ -195,7 +195,7 @@ pub fn (mut a array) reverse_in_place() {
// resulting in a single output value.
pub fn (a array) reduce(iter fn (int, int) int, accum_start int) int {
mut accum_ := accum_start
#for (let i of a) {
#for (let i = 0;i < a.arr.length;i++) {
#accum_ = iter(accum_, a.arr[i])
#}
@ -204,7 +204,7 @@ pub fn (a array) reduce(iter fn (int, int) int, accum_start int) int {
pub fn (mut a array) pop() voidptr {
mut res := voidptr(0)
#res = a.arr.pop()
#res = a.val.arr.pop()
return res
}
@ -237,5 +237,5 @@ pub fn (a array) contains(key voidptr) bool {
// delete_last effectively removes last element of an array.
pub fn (mut a array) delete_last() {
#a.arr.pop();
#a.val.arr.pop();
}

View File

@ -4,3 +4,35 @@ module builtin
pub fn js_throw(s any) {
#throw (s instanceof Error ? s : new Error(s))
}
pub fn println(s any) {
$if js_freestanding {
#print(s.toString())
} $else {
#console.log(s.toString())
}
}
pub fn print(s any) {
$if js_node {
#$process.stdout.write(s.toString())
} $else {
panic('Cannot `print` in a browser, use `println` instead')
}
}
pub fn eprintln(s any) {
$if js_freestanding {
#print(s.toString())
} $else {
#console.error(s.toString())
}
}
pub fn eprint(s any) {
$if js_node {
#$process.stderr.write(s.toString())
} $else {
panic('Cannot `eprint` in a browser, use `println` instead')
}
}

View File

@ -6,34 +6,6 @@ module builtin
fn (a any) toString()
pub fn println(s any) {
// Quickfix to properly print basic types
// TODO: Add proper detection code for this
JS.console.log(s.toString())
}
pub fn print(s any) {
// TODO
// $if js.node {
JS.process.stdout.write(s.toString())
// } $else {
// panic('Cannot `print` in a browser, use `println` instead')
// }
}
pub fn eprintln(s any) {
JS.console.error(s.toString())
}
pub fn eprint(s any) {
// TODO
// $if js.node {
JS.process.stderr.write(s.toString())
// } $else {
// panic('Cannot `eprint` in a browser, use `eprintln` instead')
// }
}
// Exits the process in node, and halts execution in the browser
// because `process.exit` is undefined. Workaround for not having
// a 'real' way to exit in the browser.

View File

@ -0,0 +1,13 @@
module config
import rand.seed
// PRNGConfigStruct is a configuration struct for creating a new instance of the default RNG.
// Note that the RNGs may have a different number of u32s required for seeding. The default
// generator WyRand used 64 bits, ie. 2 u32s so that is the default. In case your desired generator
// uses a different number of u32s, use the `seed.time_seed_array()` method with the correct
// number of u32s.
pub struct PRNGConfigStruct {
pub:
seed_ []u32 = seed.time_seed_array(2)
}

View File

@ -3,18 +3,9 @@
// that can be found in the LICENSE file.
module rand
import rand.seed
import rand.config
import rand.wyrand
// PRNGConfigStruct is a configuration struct for creating a new instance of the default RNG.
// Note that the RNGs may have a different number of u32s required for seeding. The default
// generator WyRand used 64 bits, ie. 2 u32s so that is the default. In case your desired generator
// uses a different number of u32s, use the `seed.time_seed_array()` method with the correct
// number of u32s.
pub struct PRNGConfigStruct {
seed_ []u32 = seed.time_seed_array(2)
}
// PRNG is a common interface for all PRNGs that can be used seamlessly with the rand
// modules's API. It defines all the methods that a PRNG (in the vlib or custom made) must
// implement in order to ensure that _all_ functions can be used with the generator.
@ -52,7 +43,7 @@ fn init() {
}
// new_default returns a new instance of the default RNG. If the seed is not provided, the current time will be used to seed the instance.
pub fn new_default(config PRNGConfigStruct) &PRNG {
pub fn new_default(config config.PRNGConfigStruct) &PRNG {
mut rng := &wyrand.WyRandRNG{}
rng.seed(config.seed_)
return rng

View File

@ -0,0 +1,24 @@
module time
// parse returns time from a date string.
//
// TODO(playX): JS Date expects iso8061 format of strings and other formats
// are implementation dependant, we probably want to implement parsing in JS.
pub fn parse(s string) Time {
mut res := Time{}
#let date = new Date(s.str)
#res.year.val = date.getFullYear()
#res.month.val = date.getMonth()
#res.day.val = date.getDay()
#res.hour.val = date.getHours()
#res.minute.val = date.getMinutes()
#res.second.val = date.getSeconds()
#res.microsecond.val = date.getMilliseconds() * 1000
#res.unix.val = (date.getTime() / 1000).toFixed(0)
return res
}
pub fn parse_iso8601(s string) ?Time {
return parse(s)
}

View File

@ -304,8 +304,8 @@ fn (mut g JsGen) gen_builtin_type_defs() {
g.gen_builtin_prototype(
typ_name: typ_name
default_value: 'new Number(0)'
constructor: 'this.val = val | 0'
value_of: 'this.val | 0'
constructor: 'this.val = Number(val)'
value_of: 'Number(this.val)'
to_string: 'this.valueOf().toString()'
eq: 'this.valueOf() === other.valueOf()'
to_jsval: '+this'

View File

@ -308,7 +308,7 @@ pub fn (mut g JsGen) init() {
g.definitions.writeln('"use strict";')
g.definitions.writeln('')
g.definitions.writeln('var \$global = (new Function("return this"))();')
g.definitions.writeln('function \$ref(value) { this.val = value; } ')
g.definitions.writeln('function \$ref(value) { if (value instanceof \$ref) { return value; } this.val = value; } ')
g.definitions.writeln('\$ref.prototype.valueOf = function() { return this.val; } ')
if g.pref.backend != .js_node {
g.definitions.writeln('const \$process = {')
@ -736,17 +736,16 @@ fn (mut g JsGen) expr(node ast.Expr) {
}
ast.PrefixExpr {
if node.op in [.amp, .mul] {
// C pointers/references: ignore them
if node.op == .amp {
if !node.right_type.is_pointer() {
// kind of weird way to handle references but it allows us to access type methods easily.
g.write('(function(x) {')
g.write(' return { val: x, __proto__: Object.getPrototypeOf(x), valueOf: function() { return this.val; } }})( ')
g.expr(node.right)
g.write(')')
} else {
g.expr(node.right)
}
// if !node.right_type.is_pointer() {
// kind of weird way to handle references but it allows us to access type methods easily.
g.write('(function(x) {')
g.write(' return { val: x, __proto__: Object.getPrototypeOf(x), valueOf: function() { return this.val; } }})( ')
g.expr(node.right)
g.write(')')
//} else {
// g.expr(node.right)
// }
} else {
g.write('(')
g.expr(node.right)
@ -1150,14 +1149,22 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) {
if args.len > 0 {
g.write(', ')
}
g.write('${it.params[0].name} = this')
if it.params[0].is_mut {
g.write('${it.params[0].name} = new \$ref(this)')
} else {
g.write('${it.params[0].name} = this')
}
}
g.writeln(') {')
for i, arg in args {
is_varg := i == args.len - 1 && it.is_variadic
name := g.js_name(arg.name)
if is_varg {
name := g.js_name(arg.name)
g.writeln('$name = new array($name);')
} else {
if arg.typ.is_ptr() || arg.is_mut {
g.writeln('$name = new \$ref($name)')
}
}
}
@ -1514,7 +1521,141 @@ fn (mut g JsGen) gen_array_init_values(exprs []ast.Expr) {
g.write(']')
}
fn (mut g JsGen) gen_method_call(it ast.CallExpr) bool {
g.call_stack << it
mut name := g.js_name(it.name)
call_return_is_optional := it.return_type.has_flag(.optional)
if call_return_is_optional {
g.writeln('(function(){')
g.inc_indent()
g.writeln('try {')
g.inc_indent()
g.write('return builtin.unwrap(')
}
sym := g.table.get_type_symbol(it.receiver_type)
if sym.kind == .array {
if sym.kind == .array && it.name in ['map', 'filter'] {
g.expr(it.left)
mut ltyp := it.left_type
for ltyp.is_ptr() {
g.write('.val')
ltyp = ltyp.deref()
}
g.write('.')
// Prevent 'it' from getting shadowed inside the match
node := it
g.write(it.name)
g.write('(')
expr := node.args[0].expr
match expr {
ast.AnonFn {
g.gen_fn_decl(expr.decl)
g.write(')')
return true
}
ast.Ident {
if expr.kind == .function {
g.write(g.js_name(expr.name))
g.write(')')
return true
} else if expr.kind == .variable {
v_sym := g.table.get_type_symbol(expr.var_info().typ)
if v_sym.kind == .function {
g.write(g.js_name(expr.name))
g.write(')')
return true
}
}
}
else {}
}
g.write('it => ')
g.expr(node.args[0].expr)
g.write(')')
return true
}
left_sym := g.table.get_type_symbol(it.left_type)
if left_sym.kind == .array {
if it.name in special_array_methods {
g.expr(it.left)
mut ltyp := it.left_type
for ltyp.is_ptr() {
g.write('.val')
ltyp = ltyp.deref()
}
g.write('.')
g.gen_array_method_call(it)
return true
}
}
}
// interfaces require dynamic dispatch. To obtain method table we use getPrototypeOf
g.write('Object.getPrototypeOf(')
g.expr(it.left)
mut ltyp := it.left_type
for ltyp.is_ptr() {
g.write('.val')
ltyp = ltyp.deref()
}
g.write(').$name .call(')
g.expr(it.left)
g.write(',')
for i, arg in it.args {
g.expr(arg.expr)
if i != it.args.len - 1 {
g.write(', ')
}
}
// end method call
g.write(')')
if call_return_is_optional {
// end unwrap
g.writeln(')')
g.dec_indent()
// begin catch block
g.writeln('} catch(err) {')
g.inc_indent()
// gen or block contents
match it.or_block.kind {
.block {
if it.or_block.stmts.len > 1 {
g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1])
}
g.write('return ')
g.stmt(it.or_block.stmts.last())
}
.propagate {
panicstr := '`optional not set (\${err})`'
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
g.writeln('return builtin.panic($panicstr)')
} else {
g.writeln('builtin.js_throw(err)')
}
}
else {}
}
// end catch
g.dec_indent()
g.writeln('}')
// end anon fn
g.dec_indent()
g.write('})()')
}
g.call_stack.delete_last()
return true
}
fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
if it.is_method {
if g.gen_method_call(it) {
return
}
}
g.call_stack << it
mut name := g.js_name(it.name)
call_return_is_optional := it.return_type.has_flag(.optional)
@ -1525,59 +1666,13 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
g.inc_indent()
g.write('return builtin.unwrap(')
}
g.expr(it.left)
if it.is_method { // foo.bar.baz()
sym := g.table.get_type_symbol(it.receiver_type)
g.write('.')
if sym.kind == .array && it.name in ['map', 'filter'] {
// Prevent 'it' from getting shadowed inside the match
node := it
g.write(it.name)
g.write('(')
expr := node.args[0].expr
match expr {
ast.AnonFn {
g.gen_fn_decl(expr.decl)
g.write(')')
return
}
ast.Ident {
if expr.kind == .function {
g.write(g.js_name(expr.name))
g.write(')')
return
} else if expr.kind == .variable {
v_sym := g.table.get_type_symbol(expr.var_info().typ)
if v_sym.kind == .function {
g.write(g.js_name(expr.name))
g.write(')')
return
}
}
}
else {}
}
g.write('it => ')
g.expr(node.args[0].expr)
g.write(')')
return
}
left_sym := g.table.get_type_symbol(it.left_type)
if left_sym.kind == .array {
node := it
if node.name in special_array_methods {
g.gen_array_method_call(it)
return
}
}
} else {
if name in g.builtin_fns {
g.write('builtin.')
}
if name in g.builtin_fns {
g.write('builtin.')
}
g.write('${name}(')
for i, arg in it.args {
g.expr(arg.expr)
@ -1782,6 +1877,14 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) {
}
}
fn (mut g JsGen) gen_deref_ptr(ty ast.Type) {
mut t := ty
for t.is_ptr() {
g.write('.val')
t = t.deref()
}
}
fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
l_sym := g.table.get_type_symbol(it.left_type)
r_sym := g.table.get_type_symbol(it.right_type)
@ -1804,10 +1907,12 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
g.cast_stack << greater_typ
g.write('BigInt((')
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write(').\$toJS())')
g.write(' $it.op ')
g.write('BigInt((')
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
g.write(').\$toJS())')
g.cast_stack.delete_last()
g.write(')')
@ -1820,30 +1925,41 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
has_operator_overloading := g.table.type_has_method(l_sym, '==')
if has_operator_overloading {
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write('.eq(')
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
g.write(')')
// Shallow equatables
} else if l_sym.kind in js.shallow_equatables && r_sym.kind in js.shallow_equatables {
// wrap left expr in parens so binary operations will work correctly.
g.write('(')
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write(')')
g.write('.eq(')
g.cast_stack << int(l_sym.kind)
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
g.cast_stack.delete_last()
g.write(')')
} else {
g.write('vEq(')
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write(', ')
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
g.write(')')
}
} else if l_sym.kind == .array && it.op == .left_shift { // arr << 1
g.write('Array.prototype.push.call(')
g.expr(it.left)
mut ltyp := it.left_type
for ltyp.is_ptr() {
g.write('.val')
ltyp = ltyp.deref()
}
g.write('.arr,')
array_info := l_sym.info as ast.Array
// arr << [1, 2]
@ -1854,6 +1970,12 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
g.write(')')
} else if r_sym.kind in [.array, .map, .string] && it.op in [.key_in, .not_in] {
g.expr(it.right)
mut ltyp := it.right_type
for ltyp.is_ptr() {
g.write('.val')
ltyp = ltyp.deref()
}
if r_sym.kind == .map {
g.write('.map.has(')
} else if r_sym.kind == .string {
@ -1868,6 +1990,7 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
g.write(')')
} else if it.op in [.key_is, .not_is] { // foo is Foo
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write(' instanceof ')
g.write(g.typ(it.right_type))
} else if it.op in [.lt, .gt, .ge, .le] && g.table.type_has_method(l_sym, '<')
@ -1877,19 +2000,24 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
}
if it.op in [.lt, .ge] {
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write('.\$lt (')
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
g.write(')')
} else {
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
g.write('.\$lt (')
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write(')')
}
} else {
has_operator_overloading := g.table.type_has_method(l_sym, it.op.str())
if has_operator_overloading {
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
name := match it.op.str() {
'+' {
'\$add'
@ -1913,6 +2041,7 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
}
g.write('.$name (')
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
g.write(')')
} else {
mut greater_typ := 0
@ -1933,10 +2062,11 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
}
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write(' $it.op ')
g.expr(it.right)
g.gen_deref_ptr(it.right_type)
if is_arithmetic {
g.cast_stack.delete_last()
@ -2033,8 +2163,10 @@ fn (mut g JsGen) gen_map_init_expr(it ast.MapInit) {
fn (mut g JsGen) gen_selector_expr(it ast.SelectorExpr) {
g.expr(it.expr)
if it.expr_type.is_ptr() {
g.write('.valueOf()')
mut ltyp := it.expr_type
for ltyp.is_ptr() {
g.write('.val')
ltyp = ltyp.deref()
}
g.write('.$it.field_name')
}
@ -2078,7 +2210,8 @@ fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) {
}
fn (mut g JsGen) gen_string_literal(it ast.StringLiteral) {
text := it.val.replace("'", "\\'")
mut text := it.val.replace("'", "'")
text = text.replace('"', '\\"')
should_cast := !(g.cast_stack.len > 0 && g.cast_stack.last() == ast.string_type_idx)
if true || should_cast {
if g.file.mod.name == 'builtin' {
@ -2086,7 +2219,7 @@ fn (mut g JsGen) gen_string_literal(it ast.StringLiteral) {
}
g.write('string(')
}
g.write("'$text'")
g.write("\"$text\"")
if true || should_cast {
g.write(')')
}
@ -2166,6 +2299,9 @@ fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) {
g.cast_stack << it.typ
typ := g.typ(it.typ)
if !is_literal {
if it.typ.is_ptr() {
g.write('new \$ref(')
}
if typ !in js.v_types || g.ns.name == 'builtin' {
g.write('new ')
}
@ -2177,6 +2313,9 @@ fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) {
}
if !is_literal {
g.write(')')
if it.typ.is_ptr() {
g.write(')')
}
}
g.cast_stack.delete_last()
}

View File

@ -60,16 +60,16 @@ true
0
1
1.1
[1, 2, 3, 4]
[1, 5, 6, 2, 3, 4]
[[1, 2], 3, 4]
[[1, 2], [5, 6], 3, 4]
0
1
1
0
1
1.1
[1, 2, 3, 4]
[5, 6, 1, 2, 3, 4]
[[1, 2], 3, 4]
[[5, 6], [1, 2], 3, 4]
5
true
1.1
@ -152,11 +152,11 @@ true
true
true
true
0
0
0
0
0
15
20
14
-6
-7
[2, 4, 6]
[is, awesome]
[2, 3, 4, 6, 8, 9, 10]
@ -278,9 +278,9 @@ a
123
123
[[1, 2, 3]]
[[[1, 2, 3]]]
[[1, 2, 3]]
[[1, 2, 3]]
[[1, 2, 3]]
[[[1, 2, 3]]]
true
true
true