diff --git a/quartz/std.qz b/quartz/std.qz index 3e0a8a52..ec107388 100644 --- a/quartz/std.qz +++ b/quartz/std.qz @@ -1512,6 +1512,11 @@ module derive { indent = " ".repeat(depth * 4); } + let indent_prev = " "; + if pretty { + indent_prev = " ".repeat((depth - 1) * 4); + } + let rep = reflection::get_type_rep(t); if rep.name.equal("string") { return format(`"{}"`, t as string); @@ -1573,9 +1578,14 @@ module derive { let tag_name = rep.fields.at(tag); let v = (t as ptr[any]).at(1); - s = s.concat(format(" {}: {} }", tag_name, derive::to_string_internal(v, pretty, depth + 1))); + s = s.concat(format( + "{}{}: {}", + indent, + tag_name, + derive::to_string_internal(v, pretty, depth + 1), + )); - return s; + return s.concat("{}{}}".format(sep, indent_prev)); } let s = format("{} {{}", rep.name, sep); @@ -1589,7 +1599,7 @@ module derive { s = s.concat(format("{}{}: {}", indent, f, derive::to_string_internal(v, pretty, depth + 1))); } - return s.concat(" }"); + return s.concat("{}{}}".format(sep, indent_prev)); } fun to_string(t: any): string { diff --git a/quartz/typecheck.qz b/quartz/typecheck.qz index 5d5db99c..31c3c342 100644 --- a/quartz/typecheck.qz +++ b/quartz/typecheck.qz @@ -37,6 +37,7 @@ struct Typechecker { }], current_function_name: string?, captured: vec[string], + env: map[string, LType], } module Typechecker { @@ -370,6 +371,7 @@ module Typechecker { }]](), current_function_name: nil, captured: make[vec[string]](), + env: make[map[string, LType]](), }; } @@ -527,7 +529,7 @@ module Typechecker { }, ); - self.function(d.t_func!, make[map[string, LType]]()).try; + self.function(d.t_func!).try; self.globals.insert( self.path_to(d.t_func!.name.data), @@ -642,12 +644,13 @@ module Typechecker { return _ or error::new(format("unimplemented: decl, {}", d.to_string())); } - fun function(self, f: Function, locals_init: map[string, LType]): Type or error { - let locals = self.locals; - self.locals = make[map[string, LType]](); - for k in locals_init.list_keys() { - self.locals.insert(k, locals_init.(k)); + fun function(self, f: Function): Type or error { + let prev_locals = self.locals; + let prev_env = self.env; + for l in self.locals.list_keys() { + self.env.insert(l, self.locals.at(l)); } + self.locals = make[map[string, LType]](); self.captured = make[vec[string]](); @@ -678,28 +681,9 @@ module Typechecker { self.result_type = f.result_type?; self.block(f.body).try; - let captured_local = make[map[string, bool]](); - for c in self.captured { - captured_local.insert(c, true); - } - for p in f.params { - captured_local.insert(p.name, false); - } - for l in self.locals.list_keys() { - if !locals_init.has(l) { - captured_local.insert(l, false); - } - } - - self.captured = make[vec[string]](); - for c in captured_local.list_keys() { - if captured_local.(c) { - self.captured.push(c); - } - } - - self.locals = locals; self.current_function_name = nil; + self.locals = prev_locals; + self.env = prev_env; let ps = make[vec[Type]](); for param in f.params { @@ -1049,7 +1033,6 @@ module Typechecker { return _ or error::new("unimplemented: binop, {}".format(expr.to_string())); } else if expr.t_ident != nil { if self.locals.has(expr.t_ident!.name) { - self.captured.push(expr.t_ident!.name); let t = self.locals.at(expr.t_ident!.name); self.set_search_node_type(t.data, lexpr.location); self.set_search_node_definition(self.current_path, t.location, lexpr.location); @@ -1057,6 +1040,15 @@ module Typechecker { return t.data; } + if self.env.has(expr.t_ident!.name) { + self.captured.push(expr.t_ident!.name); + let t = self.env.at(expr.t_ident!.name); + self.set_search_node_type(t.data, lexpr.location); + self.set_search_node_definition(self.current_path, t.location, lexpr.location); + self.set_completion(t.data, lexpr.location).try; + + return t.data; + } let result or _err = self.resolve_path(Path::new(expr.t_ident!.name)); if result != nil { @@ -1782,9 +1774,10 @@ module Typechecker { lexpr.location.start!.to_string(), ); - let prev = self.current_function_name; - let func_type = self.function(expr.t_closure!.func, self.locals).try; - self.current_function_name = prev; + let prev_name = self.current_function_name; + let prev_captured = self.captured; + let func_type = self.function(expr.t_closure!.func).try; + self.current_function_name = prev_name; let captures = make[vec[struct { name: string, @@ -1793,11 +1786,16 @@ module Typechecker { for c in self.captured { captures.push(struct { name: c, - type_: self.locals.(c).data, + type_: self.env.(c).data, }); } expr.t_closure!.captures = captures; + // merge with previous captures + for p in prev_captured { + self.captured.push(p); + } + return Type { t_closure: struct { params: func_type.t_func!.params, diff --git a/tests/cases/closures/test10.qz b/tests/cases/closures/test10.qz index 0d8b39e0..8d994eae 100644 --- a/tests/cases/closures/test10.qz +++ b/tests/cases/closures/test10.qz @@ -1,5 +1,6 @@ -fun f(a: i32, p: any) { +fun f(a: i32, p: fun[(i32), nil]) { println("{}", a.to_string()); + p(7); } fun main() { @@ -10,11 +11,13 @@ fun main() { fun (resp_id: i32) { f( a, - fun () { - panic("Image failed to load"); + fun (arg: i32) { + println("called with {}", arg.to_string()); }, ); }, ); + + println("ok"); } diff --git a/tests/cases/closures/test10.stdout b/tests/cases/closures/test10.stdout new file mode 100644 index 00000000..854cc68f --- /dev/null +++ b/tests/cases/closures/test10.stdout @@ -0,0 +1,4 @@ +5 +10 +called with 7 +ok diff --git a/tests/cases/closures/test11.qz b/tests/cases/closures/test11.qz new file mode 100644 index 00000000..76a9a3b4 --- /dev/null +++ b/tests/cases/closures/test11.qz @@ -0,0 +1,45 @@ +fun js_fetch(url: string, call: fun[(i32), nil]) { + call(10); +} + +fun js_image_set_onload(image: any, call: fun[(), nil]) { + call(); +} + +fun js_image_set_onerror(image: any, call: fun[(), nil]) { + call(); +} + +fun js_context_draw_image(context: string, image: i32, x: i32, y: i32) { + println(context); + println("{}", image.to_string()); + println("{}", x.to_string()); + println("{}", y.to_string()); +} + +fun main() { + let rhb = 200; + let context = "context"; + let image = "image"; + + js_fetch( + "assets/rhb.json", + fun (resp_id: i32) { + js_image_set_onload( + rhb, + fun () { + js_context_draw_image(context, rhb, 0, 0); + }, + ); + js_image_set_onerror( + image, + fun () { + println("Image failed to load"); + }, + ); + }, + ); + + println("ok"); +} + diff --git a/tests/cases/closures/test11.stdout b/tests/cases/closures/test11.stdout new file mode 100644 index 00000000..99cbd813 --- /dev/null +++ b/tests/cases/closures/test11.stdout @@ -0,0 +1,6 @@ +context +200 +0 +0 +Image failed to load +ok diff --git a/tests/cases/closures/test4.qz b/tests/cases/closures/test4.qz index 89fddf24..bb872e17 100644 --- a/tests/cases/closures/test4.qz +++ b/tests/cases/closures/test4.qz @@ -3,7 +3,6 @@ fun main() { let outer = fun () { let x = "outer"; let inner = fun () { - let x = "inner"; println(x); }; inner();