From 96b73acad7467bce9b516289f4dc7e5944218318 Mon Sep 17 00:00:00 2001 From: Leah Lundqvist Date: Tue, 24 Nov 2020 22:52:23 +0100 Subject: [PATCH] jsgen: optional "or blocks" (#6938) --- vlib/builtin/js/builtin.js.v | 6 ++++ vlib/builtin/js/builtin.v | 4 +-- vlib/v/gen/js/js.v | 50 ++++++++++++++++++++++++++++++---- vlib/v/gen/js/tests/js.v | 15 ++++++++++ vlib/v/gen/js/tests/optional.v | 31 +++++++++++++++++++++ 5 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 vlib/builtin/js/builtin.js.v create mode 100644 vlib/v/gen/js/tests/optional.v diff --git a/vlib/builtin/js/builtin.js.v b/vlib/builtin/js/builtin.js.v new file mode 100644 index 0000000000..d76346601e --- /dev/null +++ b/vlib/builtin/js/builtin.js.v @@ -0,0 +1,6 @@ +module builtin + +// used to generate JS throw statements. +pub fn js_throw(s any) { + #throw (s instanceof Error ? s : new Error(s)) +} diff --git a/vlib/builtin/js/builtin.v b/vlib/builtin/js/builtin.v index a86d32ff5f..1cac2f3cef 100644 --- a/vlib/builtin/js/builtin.v +++ b/vlib/builtin/js/builtin.v @@ -35,13 +35,13 @@ pub fn eprint(s any) { // a 'real' way to exit in the browser. pub fn exit(c int) { JS.process.exit(c) + js_throw('exit($c)') } pub fn unwrap(opt any) any { o := &Option(opt) if o.not_ok { - panic(o.error) - return o.error + js_throw(o.error) } return opt } diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index feca3cd070..6aa63df590 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -15,7 +15,7 @@ const ( 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield'] tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t', - '\t\t\t\t\t\t\t\t', + '\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t' ] ) @@ -1011,6 +1011,10 @@ fn (mut g JsGen) gen_go_stmt(node ast.GoStmt) { receiver_sym := g.table.get_type_symbol(node.call_expr.receiver_type) name = receiver_sym.name + '.' + name } + // todo: please add a name feild without the mod name for ast.CallExpr + if name.starts_with('$node.call_expr.mod\.') { + name = name[node.call_expr.mod.len+1..] + } g.writeln('await new Promise(function(resolve){') g.inc_indent() g.write('${name}(') @@ -1048,7 +1052,7 @@ fn (mut g JsGen) gen_interface_decl(it ast.InterfaceDecl) { fn (mut g JsGen) gen_return_stmt(it ast.Return) { if it.exprs.len == 0 { // Returns nothing - g.write('return;') + g.writeln('return;') return } g.write('return ') @@ -1173,7 +1177,11 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { } call_return_is_optional := it.return_type.has_flag(.optional) if call_return_is_optional { - g.write('builtin.unwrap(') + g.writeln('(function(){') + g.inc_indent() + g.writeln('try {') + g.inc_indent() + g.write('return builtin.unwrap(') } g.expr(it.left) if it.is_method { // foo.bar.baz() @@ -1224,10 +1232,40 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { g.write(', ') } } + // end method call + g.write(')') if call_return_is_optional { - g.write('))') - } else { - g.write(')') + // 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('})()') } } diff --git a/vlib/v/gen/js/tests/js.v b/vlib/v/gen/js/tests/js.v index f4e1d2c3fb..e927ecd441 100644 --- a/vlib/v/gen/js/tests/js.v +++ b/vlib/v/gen/js/tests/js.v @@ -59,8 +59,12 @@ fn main() { } for i, x in 'hello' { } + mut evens := []int{} for x in 1 .. 10 { + y := error_if_even(x) or { x + 1 } + evens << y } + println(evens) arr := [1, 2, 3, 4, 5] for i in arr { } @@ -81,6 +85,7 @@ fn main() { println(message) }) hl.raw_js_log() + propagation() or { println(err) } } fn anon_consumer(greeting string, anon fn (message string)) { @@ -120,3 +125,13 @@ fn (it Companies) method() int { } return 0 } + +fn error_if_even(num int) ?int { + if num % 2 == 0 { return error('number is even') } + return num +} + +fn propagation() ? { + println('Propagation test:') + return error('"Task failed successfully" - Windows XP') +} diff --git a/vlib/v/gen/js/tests/optional.v b/vlib/v/gen/js/tests/optional.v new file mode 100644 index 0000000000..1bcd78dcda --- /dev/null +++ b/vlib/v/gen/js/tests/optional.v @@ -0,0 +1,31 @@ +module main + +fn main() { + try_propagation() or { println("captured: $err") } +} + +fn try_propagation() ? { + try_numbers()? +} + +fn try_numbers() ? { + for x in 1 .. 10 { + y := error_if_even(x) or { x + 1 } + println("$x rounded to $y") + error_if_prime(y)? + } +} + +fn error_if_even(num int) ?int { + if num % 2 == 0 { return error('number is even') } + return num +} + +fn error_if_prime(num int) ?int { + for i in 2..num { + if num % i == 0 { + return error('$num is prime') + } + } + return num +}