Skip to content

Commit

Permalink
Merge pull request #158 from myuon/more-closures
Browse files Browse the repository at this point in the history
  • Loading branch information
myuon authored Oct 10, 2023
2 parents bd19dc5 + 26e3454 commit 54a431b
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 26 deletions.
18 changes: 18 additions & 0 deletions quartz/ir.qz
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,24 @@ module IrTermWriter {
self.end();

self.end();
} else if term.t_dynamic_call != nil {
self.start();
self.write("dynamic-call");
self.write(term.t_dynamic_call!.callee_type.to_string());
self.expression(term.t_dynamic_call!.callee_id);
for arg in term.t_dynamic_call!.args {
self.expression(arg);
}
self.end();
} else if term.t_funcref_table != nil {
self.start();
self.write("funcref-table");
for func in term.t_funcref_table!.functions {
self.write(func);
}
self.end();
} else {
return panic("unknown term: {}", derive::to_string(term));
}
}
}
Expand Down
133 changes: 107 additions & 26 deletions quartz/ir_code_gen.qz
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ module InternalTypeRep {
);
} else if t.t_externref != nil {
return InternalTypeRep::from_name("externref", make[vec[InternalTypeRep]]());
} else if t.t_func != nil {
return InternalTypeRep::from_name("func", make[vec[InternalTypeRep]]());
}

return panic("unreachable, {}".format(t.to_string()));
Expand Down Expand Up @@ -419,6 +421,10 @@ module IrCodeGenerator {
"slice",
make[vec[InternalTypeRep]](InternalTypeRep::from_name("TypeRep", make[vec[InternalTypeRep]]())),
));
self.type_reps.get_or_insert(InternalTypeRep::from_name(
"func",
make[vec[InternalTypeRep]](),
));

// Register declared types
for k in self.globals.list_keys() {
Expand Down Expand Up @@ -1915,15 +1921,25 @@ module IrCodeGenerator {
t_load: struct {
type_: IrType::new(c.type_),
address: IrTerm {
t_ident: "env",
},
offset: IrCodeGenerator::wrap_mult_sizeof(
IrType::new(c.type_),
IrTerm {
t_i32: i,
t_load: struct {
type_: IrType {
t_address: true,
},
address: IrTerm {
t_ident: "env",
},
offset: IrCodeGenerator::wrap_mult_sizeof(
IrType::new(c.type_),
IrTerm {
t_i32: i,
},
),
},
),
},
},
offset: IrTerm {
t_i32: 0,
},
}
};
}
}
Expand Down Expand Up @@ -3097,22 +3113,16 @@ module IrCodeGenerator {
term: IrTerm,
}]]();
for c in expr.t_closure!.captures {
captures_ir.push(IrType::new(c.type_));
captures_ir.push(IrType {
t_address: true,
});
values.push(struct {
label: nil,
type_: IrType {
t_address: true,
},
term: IrTerm {
t_load: struct {
type_: IrType::new(c.type_),
address: IrTerm {
t_ident: c.name,
},
offset: IrTerm {
t_i32: 0,
},
},
t_ident: c.name,
},
});
}
Expand All @@ -3130,16 +3140,59 @@ module IrCodeGenerator {
let symbol = self.path_to(expr.t_closure!.func.name.data);
let function_id = self.functions.get_or_insert(symbol);

elements.push(IrTerm {
t_i32: function_id,
});
elements.push(self.generate_array("struct", make[vec[IrType]](
IrType {
t_i32: true,
},
IrType {
t_address: true,
},
), make[vec[struct {
label: string?,
type_: IrType,
term: IrTerm,
}]](
struct {
label: "function"?,
type_: IrType {
t_i32: true,
},
term: IrTerm {
t_i32: function_id,
},
},
struct {
label: "env"?,
type_: IrType {
t_address: true,
},
term: IrTerm {
t_ident: "env",
},
},
)).try);

return IrTerm {
t_seq: struct {
terms: elements,
},
};
} else if expr.t_closure_call != nil {
let elements = make[vec[IrTerm]]();

let var_name = "__closure__{}".format(self.counter.to_string());
self.counter = self.counter + 1;

elements.push(IrTerm {
t_let: IrLet {
name: var_name,
type_: IrType {
t_address: true,
},
value: self.expression(expr.t_closure_call!.callee).try,
},
});

let callee_type = expr.t_closure_call!.callee_type;
callee_type.t_closure!.params.push(Type {
t_any: true,
Expand All @@ -3150,16 +3203,44 @@ module IrCodeGenerator {
terms.push(self.expression(arg).try);
}

terms.push(IrTerm {
t_ident: "env",
});
terms.push(self.generate_array_at(
Type {
t_ptr: Type {
t_omit: true,
},
},
IrTerm {
t_ident: var_name,
},
IrTerm {
t_i32: 1,
},
).try);

return IrTerm {
elements.push(IrTerm {
t_dynamic_call: struct {
callee_type: IrType::new(callee_type),
callee_id: self.expression(expr.t_closure_call!.callee).try,
callee_id: self.generate_array_at(
Type {
t_ptr: Type {
t_omit: true,
},
},
IrTerm {
t_ident: var_name,
},
IrTerm {
t_i32: 0,
},
).try,
args: terms,
},
});

return IrTerm {
t_seq: struct {
terms: elements,
},
};
}

Expand Down
35 changes: 35 additions & 0 deletions quartz/parser.qz
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,41 @@ module Parser {
},
location: token.location,
};
} else if token.lexeme == "fun" {
let start_token = self.expect("lbracket").try;
let params = make[vec[Type]]();
self.expect("lparen").try;
while !self.peek().try.lexeme.equal("rparen") {
let t = self.type_().try;
params.push(t.data);

if self.peek().try.lexeme.equal("comma") {
self.consume().try;
} else {
break;
}
}
self.expect("rparen").try;

let result = Type {
t_nil: true,
};
if self.peek().try.lexeme.equal("comma") {
self.consume().try;
result = self.type_().try.data;
}

let end_token = self.expect("rbracket").try;

return LType {
data: Type {
t_closure: struct {
params: params,
result: result,
},
},
location: self.span_location(start_token, end_token),
};
} else {
return _ or error::new(format("Expected type, got {}", token.to_string()));
}
Expand Down
18 changes: 18 additions & 0 deletions quartz/typecheck.qz
Original file line number Diff line number Diff line change
Expand Up @@ -2296,6 +2296,24 @@ fun unify(type1: Type, type2: Type): Type or error {
},
};
}
if type1.t_closure != nil && type2.t_closure != nil {
if type1.t_closure!.params.length != type2.t_closure!.params.length {
return _ or error::new("unification failed: ".concat(type1.t_closure!.params.length.to_string()).concat(
" != ",
).concat(type2.t_closure!.params.length.to_string()));
}

for i in 0..type1.t_closure!.params.length {
let rs1 = type1.t_closure!.params.at(i);
let rs2 = type2.t_closure!.params.at(i);

unify(rs1, rs2).try;
}

unify(type1.t_closure!.result, type2.t_closure!.result).try;

return type1;
}

return _ or error::new(
"unification failed: ".concat(type1.to_string()).concat(" != ").concat(type2.to_string()),
Expand Down
17 changes: 17 additions & 0 deletions tests/cases/closures/test3.qz
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
let g: fun[(i32), i32]? = nil;

fun define_closure() {
let a = 10;

g = fun(x: i32): i32 {
return x + a;
}?;
}

fun main() {
define_closure();

assert_eq(g!(7), 17);

println("ok");
}
1 change: 1 addition & 0 deletions tests/cases/closures/test3.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ok
1 change: 1 addition & 0 deletions tests/cases/errors_parser/test3.qz
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
struct i

0 comments on commit 54a431b

Please sign in to comment.