hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit d6819ca19bed2b349a3356efbaa5bac943ee020b
parent 1e82efdec742a39d80d179076236a677c32e7124
Author: Drew DeVault <sir@cmpwn.com>
Date:   Thu,  2 Sep 2021 13:58:38 +0200

hare::unit: implement return expressions

This also:
- Implements them in cmd/hare
- Fixes termination semantics for compound

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mcmd/harec/gen.ha | 30++++++++++++++++++++++--------
Mhare/unit/context.ha | 1+
Mhare/unit/expr.ha | 5++++-
Mhare/unit/process.ha | 53++++++++++++++++++++++++++++++++++++++++++++++++++---
4 files changed, 77 insertions(+), 12 deletions(-)

diff --git a/cmd/harec/gen.ha b/cmd/harec/gen.ha @@ -32,8 +32,9 @@ fn gen(store: *types::typestore, unit: *unit::unit) void = { fn gen_func(ctx: *context, decl: *unit::decl) void = { // TODO: const fndecl = &decl.decl as *unit::decl_func; const fndecl = decl.decl as unit::decl_func; - if (fndecl.body == null) { - return; // Prototype + const body = match (fndecl.body) { + null => return, // Prototype + expr: *unit::expr => expr, }; const fntype = fndecl.prototype.repr as types::func; assert(fntype.flags == 0); @@ -65,12 +66,13 @@ fn gen_func(ctx: *context, decl: *unit::decl) void = { fmt::fprintln(ctx.out, mklabel(ctx, "body"))!; - // TODO: We needn't write our own return if the user returned themselves - const rval = gen_expr(ctx, fndecl.body: *unit::expr); - if (has_rval) { - emit(ctx, void, qinstr::RET, rval); - } else { - emit(ctx, void, qinstr::RET); + const rval = gen_expr(ctx, body); + if (!body.terminates) { + if (has_rval) { + emit(ctx, void, qinstr::RET, rval); + } else { + emit(ctx, void, qinstr::RET); + }; }; io::write(os::stdout, bufio::buffer(ctx.out))!; @@ -81,6 +83,7 @@ fn gen_expr(ctx: *context, expr: *unit::expr) value = { return match (expr.expr) { unit::compound_expr => gen_expr_compound(ctx, expr), unit::constant_expr => gen_expr_const(ctx, expr), + unit::return_expr => gen_expr_return(ctx, expr), }; }; @@ -109,3 +112,14 @@ fn gen_expr_const(ctx: *context, expr: *unit::expr) value = { _type = expr.result, }; }; + +fn gen_expr_return(ctx: *context, expr: *unit::expr) value = { + const rexpr = expr.expr as unit::return_expr; + match (rexpr) { + null => emit(ctx, void, qinstr::RET), + expr: *unit::expr => { + emit(ctx, void, qinstr::RET, gen_expr(ctx, expr)); + }, + }; + return vvoid; +}; diff --git a/hare/unit/context.ha b/hare/unit/context.ha @@ -3,4 +3,5 @@ use hare::types; type context = struct { store: *types::typestore, scope: *scope, + fntype: *types::func, }; diff --git a/hare/unit/expr.ha b/hare/unit/expr.ha @@ -8,7 +8,7 @@ export type expr = struct { end: lex::location, result: const *types::_type, terminates: bool, - expr: (compound_expr | constant_expr | void), + expr: (compound_expr | constant_expr | return_expr), }; // A compound expression, i.e. { ... } @@ -16,3 +16,6 @@ export type compound_expr = []*expr; // The value of a constant expression. export type constant_expr = ast::value; // TODO: composite types + +// A return expression, i.e. return <value> +export type return_expr = nullable *expr; diff --git a/hare/unit/process.ha b/hare/unit/process.ha @@ -48,6 +48,7 @@ fn process_func( assert(fntype.variadism == types::variadism::NONE); // TODO assert(len(fntype.params) == 0); // TODO + ctx.fntype = &fntype; const body: nullable *expr = match (afndecl.body) { abody: ast::expr => process_expr(ctx, &abody)?, void => null, @@ -95,7 +96,7 @@ fn process_expr( ast::size_expr => abort(), // TODO ast::offset_expr => abort(), // TODO ast::propagate_expr => abort(), // TODO - ast::return_expr => abort(), // TODO + ast::return_expr => process_return(ctx, expr), ast::slice_expr => abort(), // TODO ast::switch_expr => abort(), // TODO ast::unarithm_expr => abort(), // TODO @@ -105,8 +106,9 @@ fn process_compound(ctx: *context, aexpr: *ast::expr) (*expr | error) = { const compound = aexpr.expr as ast::compound_expr; const scope = scope_push(ctx, scope_class::COMPOUND); - let exprs: compound_expr = []; - for (let i = 0z; i < len(compound.exprs); i += 1) { + let exprs: compound_expr = alloc([], len(compound.exprs)); + let i = 0z; + for (i < len(compound.exprs); i += 1) { append(exprs, process_expr(ctx, compound.exprs[i])?); }; @@ -116,6 +118,8 @@ fn process_compound(ctx: *context, aexpr: *ast::expr) (*expr | error) = { end = aexpr.end, result = &types::builtin_void, // TODO: Pick result type expr = exprs, + terminates = exprs[i - 1].terminates, + ... }); }; @@ -128,6 +132,12 @@ fn process_compound(ctx: *context, aexpr: *ast::expr) (*expr | error) = { assert(expr.result.repr as types::builtin == types::builtin::VOID); const compound = expr.expr as compound_expr; assert(len(compound) == 3); + + const aexpr = parse_expr("{ return; }"); + defer ast::expr_free(aexpr); + const expr = process_compound(&ctx, aexpr)!; + assert(expr.terminates); + // TODO: test yields }; @@ -216,3 +226,40 @@ fn process_constant(ctx: *context, aexpr: *ast::expr) (*expr | error) = { }; }; }; + +fn process_return(ctx: *context, aexpr: *ast::expr) (*expr | error) = { + const ret = aexpr.expr as ast::return_expr; + const rval = match (ret) { + null => null, + aexpr: *ast::expr => process_expr(ctx, aexpr)?, + }; + // TODO: assert(types::assignable(ctx.fntype.result, rval.type)); + return alloc(expr { + start = aexpr.start, + end = aexpr.end, + terminates = true, + result = &types::builtin_void, + expr = rval: return_expr, + }); +}; + +@test fn _return() void = { + const ctx = mktestctx(); + defer freetestctx(&ctx); + const aexpr = parse_expr("return;"); + defer ast::expr_free(aexpr); + const expr = process_return(&ctx, aexpr)!; + assert(expr.terminates); + assert(expr.result.repr as types::builtin == types::builtin::VOID); + const rval = expr.expr as return_expr; + assert(rval == null); + + const aexpr = parse_expr("return 10;"); + defer ast::expr_free(aexpr); + const expr = process_return(&ctx, aexpr)!; + assert(expr.terminates); + assert(expr.result.repr as types::builtin == types::builtin::VOID); + const rval = expr.expr as return_expr; + assert(rval != null); + assert((rval: *expr).expr is constant_expr); +};