From fbae5890ccb88e336f3be48478a0a85c990581a5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Astor?= Date: Sun, 14 Mar 2021 16:58:21 +0100 Subject: [PATCH] :art: libprettyerr and contracts --- Makefile | 8 ++++++-- ast | 2 +- src/clean.c | 5 +++++ src/emit/emit.c | 5 ++++- src/gwion.c | 17 +++++++++------- src/import/import_tdef.c | 2 +- src/lib/engine.c | 8 ++++++++ src/lib/object.c | 2 +- src/lib/ref.c | 3 ++- src/parse/check.c | 37 +++++++++++++++++++++++++++++++++- src/parse/scan0.c | 2 +- src/parse/scan1.c | 4 +++- src/parse/scan2.c | 6 +++++- tests/error/func_code_error.gw | 6 +++++- tests/error/stray.gw | 2 +- 15 files changed, 89 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 15ae537e..0a9ee058 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,7 @@ test_dir += examples CFLAGS += -Iutil/include -Iast/include -Ilibcmdapp/src -D_GNU_SOURCE +CFLAGS += -Iast/libprettyerr/src # add commit hash to version for now CFLAGS += -DGWION_VERSION=\"$(shell git log -n1 --format="%h")\" @@ -47,8 +48,8 @@ endif CFLAGS += -DGWION_BUILTIN -_GWLIBS = util/libgwion_util.a ast/libgwion_ast.a libcmdapp/libcmdapp.a lib${PRG}.a util/libtermcolor/libtermcolor.a -GWLIBS = lib${PRG}.a libcmdapp/libcmdapp.a ast/libgwion_ast.a util/libgwion_util.a util/libtermcolor/libtermcolor.a +_GWLIBS = util/libgwion_util.a ast/libgwion_ast.a libcmdapp/libcmdapp.a lib${PRG}.a util/libtermcolor/libtermcolor.a ast/libprettyerr/libprettyerr.a +GWLIBS = lib${PRG}.a libcmdapp/libcmdapp.a ast/libgwion_ast.a ast/libprettyerr/libprettyerr.a util/libgwion_util.a util/libtermcolor/libtermcolor.a _LDFLAGS = ${GWLIBS} ${LDFLAGS} all: options-show ${_GWLIBS} src/main.o @@ -76,6 +77,9 @@ ast/libgwion_ast.a: libcmdapp/libcmdapp.a: @+${MAKE} -s -C libcmdapp static +ast/libprettyerr/libprettyerr.a: + @+CFLAGS=-I${PWD}/util/libtermcolor/src ${MAKE} -s -C ast/libprettyerr static + ast: ast/libgwion_ast.a @(info build ast) diff --git a/ast b/ast index a6f47836..d5f0d5d1 160000 --- a/ast +++ b/ast @@ -1 +1 @@ -Subproject commit a6f47836656ec77d15a9ad1914b05779692bdaba +Subproject commit d5f0d5d1df9b07c116da954771d566af9aa2ad10 diff --git a/src/clean.c b/src/clean.c index 148e412a..72ddac6f 100644 --- a/src/clean.c +++ b/src/clean.c @@ -332,6 +332,11 @@ ANN static void clean_fptr_def(Clean *a, Fptr_Def b) { ANN static void clean_type_def(Clean *a, Type_Def b) { if(b->ext) clean_type_decl(a, b->ext); + if(b->when) { + clean_exp(a, b->when); + if(b->when_def) + clean_func_def(a, b->when_def); + } if(b->tmpl) clean_tmpl(a, b->tmpl); } diff --git a/src/emit/emit.c b/src/emit/emit.c index e618d20b..52a4cb21 100644 --- a/src/emit/emit.c +++ b/src/emit/emit.c @@ -1504,7 +1504,8 @@ ANN2(1) /*static */m_bool emit_exp(const Emitter emit, /* const */Exp e) { CHECK_BB(emit_implicit_cast(emit, exp, exp->cast_to)) if(isa(e->type, emit->gwion->type[et_object]) > 0 && (e->cast_to ? isa(e->cast_to, emit->gwion->type[et_object]) > 0 : 1) && - e->exp_type == ae_exp_decl && GET_FLAG(e->d.exp_decl.td, late) && exp_getuse(e) && !exp_getvar(e)) { + e->exp_type == ae_exp_decl && GET_FLAG(e->d.exp_decl.td, late) && exp_getuse(e) && !exp_getvar(e) && GET_FLAG(e->d.exp_decl.list->self->value, late)) { +// e->exp_type == ae_exp_decl && !exp_getvar(e)) { const Instr instr = emit_add_instr(emit, GWOP_EXCEPT); instr->m_val = -SZ_INT; } @@ -1862,6 +1863,8 @@ ANN static m_bool emit_stmt_loop(const Emitter emit, const Stmt_Loop stmt) { } ANN static m_bool emit_type_def(const Emitter emit, const Type_Def tdef) { + if(tdef->when) + CHECK_BB(emit_func_def(emit, tdef->when_def)) return (!is_fptr(emit->gwion, tdef->type) && tdef->type->info->cdef) ? emit_class_def(emit, tdef->type->info->cdef) : GW_OK; } diff --git a/src/gwion.c b/src/gwion.c index 48e2f180..a360c819 100644 --- a/src/gwion.c +++ b/src/gwion.c @@ -140,18 +140,21 @@ ANN void gwion_end(const Gwion gwion) { ANN static void env_header(const Env env) { if(env->class_def) - gw_err(_("in class: '%s'\n"), env->class_def->name); - if(env->func) - gw_err(_("in function: '%s'\n"), env->func->name); + gwerr_secondary("in class:", env->name, env->class_def->info->cdef->pos); + if(env->func && env->func != (Func)1 && env->func->def) + gwerr_secondary("in function:", env->name, env->func->def->pos); } ANN static void env_xxx(const Env env, const loc_t pos, const m_str fmt, va_list arg) { #ifndef __FUZZING__ + va_list tmpa; + va_copy(tmpa, arg); + const int size = vsnprintf(NULL, 0, fmt, tmpa); + va_end(tmpa); + char c[size + 1]; + vsprintf(c, fmt, arg); + gwerr_basic(c, NULL, NULL, env->name, pos, 0); env_header(env); - loc_header(pos, env->name); - vfprintf(stderr, fmt, arg); - fprintf(stderr, "\n"); - loc_err(pos, env->name); #endif } diff --git a/src/import/import_tdef.c b/src/import/import_tdef.c index f0712a16..9df5ea6d 100644 --- a/src/import/import_tdef.c +++ b/src/import/import_tdef.c @@ -27,7 +27,7 @@ ANN Type gwi_typedef_end(const Gwi gwi, const ae_flag flag) { CHECK_BO(ck_ok(gwi, ck_tdef)) Type_Decl *td = gwi->ck->td; td->flag |= flag; - const Type_Def tdef = new_type_def(gwi->gwion->mp, td, gwi->ck->sym); + const Type_Def tdef = new_type_def(gwi->gwion->mp, td, gwi->ck->sym, gwi->loc); if(gwi->ck->tmpl) tdef->tmpl = gwi_tmpl(gwi); gwi->ck->td = NULL; diff --git a/src/lib/engine.c b/src/lib/engine.c index 14b2a7fe..1d26b25b 100644 --- a/src/lib/engine.c +++ b/src/lib/engine.c @@ -73,6 +73,11 @@ static OP_CHECK(opck_basic_ctor) { ERR_N(exp_self(call)->pos, _("can't call a non-callable value")) } +static INSTR(PredicateCheck) { + if(!*(m_uint*)REG(-SZ_INT)) + Except(shred, "predicate failed"); +} + ANN static m_bool import_core_libs(const Gwi gwi) { const Type t_class = gwi_mk_type(gwi, "@Class", SZ_INT, NULL); set_tflag(t_class, tflag_infer); @@ -108,6 +113,9 @@ ANN static m_bool import_core_libs(const Gwi gwi) { struct SpecialId_ spid = { .type=t_now, .exec=RegPushNow, .is_const=1 }; gwi_specialid(gwi, "now", &spid); + struct SpecialId_ predicate = { .type=t_void, .exec=PredicateCheck, .is_const=1 }; + gwi_specialid(gwi, "@predicate", &predicate); + const Type t_compound = gwi_mk_type(gwi, "@Compound", SZ_INT, NULL); GWI_BB(gwi_gack(gwi, t_compound, gack_compound)) GWI_BB(gwi_set_global_type(gwi, t_compound, et_compound)) diff --git a/src/lib/object.c b/src/lib/object.c index afd7dffc..f8e48f26 100644 --- a/src/lib/object.c +++ b/src/lib/object.c @@ -20,7 +20,7 @@ #undef insert_symbol ANN void exception(const VM_Shred shred, const m_str c) { - gw_err("%s: shred[id=%" UINT_F ":%s], PC=[%" UINT_F "]\n", + gw_err("{+}%s{0}: shred[{-}id=%" UINT_F "{0}:%s], PC=[{-}%" UINT_F "{0}]\n", c, shred->tick->xid, shred->info->name, shred->pc - 1); vm_shred_exit(shred); } diff --git a/src/lib/ref.c b/src/lib/ref.c index c3702df6..237266de 100644 --- a/src/lib/ref.c +++ b/src/lib/ref.c @@ -21,7 +21,8 @@ static m_bool ref_access(const Env env, const Exp e) { const m_str access = exp_access(e); if(!access) return GW_OK; - ERR_B(e->pos, _("operand is %s"), access); + env_err(env, e->pos, _("operand is %s"), access ); + return GW_ERROR; } static OP_CHECK(opck_implicit_similar) { diff --git a/src/parse/check.c b/src/parse/check.c index 977842ec..c8b6643a 100644 --- a/src/parse/check.c +++ b/src/parse/check.c @@ -861,6 +861,41 @@ ANN static Type check_exp_dot(const Env env, Exp_Dot* member) { } ANN m_bool check_type_def(const Env env, const Type_Def tdef) { + if(tdef->when) { + const Var_Decl decl = new_var_decl(env->gwion->mp, insert_symbol("self"), NULL, tdef->when->pos); + const Arg_List args = new_arg_list(env->gwion->mp, cpy_type_decl(env->gwion->mp, tdef->ext), decl, NULL); + Func_Base *fb = new_func_base(env->gwion->mp, type2td(env->gwion, tdef->type, tdef->when->pos), + insert_symbol("@implicit"), args, ae_flag_none); + set_fbflag(fb, fbflag_op); + const Exp helper = new_prim_id(env->gwion->mp, insert_symbol("@predicate"), tdef->when->pos); + tdef->when->next = helper; + const Stmt stmt = new_stmt_exp(env->gwion->mp, ae_stmt_exp, cpy_exp(env->gwion->mp, tdef->when), tdef->when->pos);// copy exp + const Stmt_List body = new_stmt_list(env->gwion->mp, stmt, NULL);//ret_list); + const Stmt code = new_stmt_code(env->gwion->mp, body, tdef->when->pos); + const Func_Def fdef = new_func_def(env->gwion->mp, fb, code, tdef->when->pos); + CHECK_BB(traverse_func_def(env, fdef)) + const Exp predicate = stmt->d.stmt_exp.val; + if(isa(predicate->type, env->gwion->type[et_bool]) < 0) { + char explain[strlen(predicate->type->name) + 7]; + sprintf(explain, "found `{/+}%s{0}`", predicate->type->name); + gwerr_basic("Invalid `{/+}when{0}` predicate expression type", explain, "use `{/+}bool{0}`", + env->name, tdef->when->pos, 0); + char from[strlen(tdef->type->name) + 39]; + sprintf(from, "in `{/+}%s{0}` definition", tdef->type->name); + gwerr_secondary(from, env->name, tdef->pos); + if(env->context) + env->context->error++; + return GW_ERROR; + } + // we handle the return after, so that we don't get *cant' use implicit casting while defining it* + const Exp ret_id = new_prim_id(env->gwion->mp, insert_symbol("self"), tdef->when->pos); + ret_id->d.prim.value = new_value(env->gwion->mp, tdef->type, "self"); + const Stmt ret = new_stmt_exp(env->gwion->mp, ae_stmt_return, ret_id, tdef->when->pos); + const Stmt_List ret_list = new_stmt_list(env->gwion->mp, ret, NULL); + ret_id->type = tdef->type; + body->next = ret_list; + tdef->when_def = fdef; + } return (!is_fptr(env->gwion, tdef->type) && tdef->type->info->cdef) ? check_class_def(env, tdef->type->info->cdef) : GW_OK; } @@ -931,7 +966,7 @@ ANN static inline m_bool for_empty(const Env env, const Stmt_For stmt) { if(!stmt->c2 || !stmt->c2->d.stmt_exp.val) ERR_B(stmt_self(stmt)->pos, _("empty for loop condition..." "...(note: explicitly use 'true' if it's the intent)" - "...(e.g., 'for(; true;){ /*...*/ }')")) + "...(e.g., 'for(; true;){{ /*...*/ }')")) return GW_OK; } diff --git a/src/parse/scan0.c b/src/parse/scan0.c index 134c6425..606b66c6 100644 --- a/src/parse/scan0.c +++ b/src/parse/scan0.c @@ -162,7 +162,7 @@ ANN m_bool scan0_type_def(const Env env, const Type_Def tdef) { CHECK_BB(typedef_complex(env, tdef, base)) } else typedef_fptr(env, tdef, base); - if(!tdef->distinct) + if(!tdef->distinct && !tdef->when) scan0_implicit_similar(env, tdef->type, base); set_tflag(tdef->type, tflag_typedef); return GW_OK; diff --git a/src/parse/scan1.c b/src/parse/scan1.c index d1e5411d..6c209490 100644 --- a/src/parse/scan1.c +++ b/src/parse/scan1.c @@ -403,6 +403,8 @@ ANN m_bool scan1_fptr_def(const Env env, const Fptr_Def fptr) { ANN m_bool scan1_type_def(const Env env, const Type_Def tdef) { if(!tdef->type) tdef->type = nspc_lookup_type0(env->curr, tdef->xid); + if(tdef->when) + CHECK_BB(scan1_exp(env, tdef->when)) return (!is_fptr(env->gwion, tdef->type) && tdef->type->info->cdef) ? scan1_cdef(env, tdef->type) : GW_OK; } @@ -533,7 +535,7 @@ ANN static m_bool scan1_fdef_args(const Env env, Arg_List list) { do { Nspc nspc = env->curr; do if(nspc_lookup_value0(nspc, list->var_decl->xid)) - ERR_B(list->var_decl->pos, _("argument '%s' shadows a previuosly defined variable"), + ERR_B(list->var_decl->pos, _("argument '%s' shadows a previously defined variable"), s_name(list->var_decl->xid)) while((nspc = nspc->parent)); } while((list = list->next)); diff --git a/src/parse/scan2.c b/src/parse/scan2.c index 837e5091..ea0a5cc9 100644 --- a/src/parse/scan2.c +++ b/src/parse/scan2.c @@ -89,8 +89,12 @@ ANN m_bool scan2_fptr_def(const Env env NUSED, const Fptr_Def fptr) { return GW_OK; } +ANN static m_bool scan2_func_def_op(const Env env, const Func_Def f); ANN m_bool scan2_type_def(const Env env, const Type_Def tdef) { - if(!tdef->type->info->cdef) return GW_OK; + if(tdef->when) + CHECK_BB(scan2_exp(env, tdef->when)) + if(!tdef->type->info->cdef) + return GW_OK; return (!is_fptr(env->gwion, tdef->type) && tdef->type->info->cdef) ? scan2_class_def(env, tdef->type->info->cdef) : GW_OK; } diff --git a/tests/error/func_code_error.gw b/tests/error/func_code_error.gw index 229933d4..11262c4b 100644 --- a/tests/error/func_code_error.gw +++ b/tests/error/func_code_error.gw @@ -1,2 +1,6 @@ #! [contains] in function: -fun void test() { <<< b >>>; } +class C { + fun void test() { + <<< b >>>; + } +} diff --git a/tests/error/stray.gw b/tests/error/stray.gw index ca84fac1..aac695b5 100644 --- a/tests/error/stray.gw +++ b/tests/error/stray.gw @@ -1,2 +1,2 @@ #! [contains somewhere] stray in program -**µ +1**µ -- 2.43.0