]> Nishi Git Mirror - gwion.git/commitdiff
:art: Introduce effects
authorJérémie Astor <fennecdjay@gmail.com>
Wed, 21 Apr 2021 21:09:17 +0000 (23:09 +0200)
committerJérémie Astor <fennecdjay@gmail.com>
Wed, 21 Apr 2021 21:09:17 +0000 (23:09 +0200)
16 files changed:
ast
include/emit.h
include/opcode.h
include/vm.h
opcode.txt
src/clean.c
src/emit/emit.c
src/env/context.c
src/lib/lib_func.c
src/lib/object.c
src/parse/check.c
src/parse/scan1.c
src/parse/scan2.c
src/vm/vm.c
src/vm/vm_code.c
src/vm/vm_shred.c

diff --git a/ast b/ast
index 54f6475d7a9686929d553695180bb6bd0be262bf..22dd7634ccd580fa2265cd554dbbaf572a8489be 160000 (submodule)
--- a/ast
+++ b/ast
@@ -1 +1 @@
-Subproject commit 54f6475d7a9686929d553695180bb6bd0be262bf
+Subproject commit 22dd7634ccd580fa2265cd554dbbaf572a8489be
index 7ec3ea490cb63f9e82038acbce44a9dffca5e570..a9afcd1adf047a5f6f80c46d38bb509d95328e24 100644 (file)
@@ -5,6 +5,8 @@ typedef struct Frame_ {
   size_t curr_offset;
   struct Vector_ stack;
   struct Vector_ defer;
+  struct Map_    handlers;
+  m_uint try_top;
 } Frame;
 
 typedef struct Code_ {
index 79e4492892a5b87a9192676341dca9bd84fd2011..77410da206c69c6137219906de6d92ade7603e06 100644 (file)
@@ -183,6 +183,9 @@ enum {
   eGackType,
   eGackEnd,
   eGack,
+  eTryIni,
+  eTryEnd,
+  eHandleEffect,
   eNoOp,
   eEOC,
   eUnroll2,
@@ -372,6 +375,9 @@ enum {
 #define  GackType             (f_instr)eGackType
 #define  GackEnd              (f_instr)eGackEnd
 #define  Gack                 (f_instr)eGack
+#define  TryIni               (f_instr)eTryIni
+#define  TryEnd               (f_instr)eTryEnd
+#define  HandleEffect         (f_instr)eHandleEffect
 #define  NoOp                 (f_instr)eNoOp
 #define  EOC                  (f_instr)eEOC
 #define  Unroll2              (f_instr)eUnroll2
index b1214386cab69d0359d615de117758eacdbba440..1911d73ed790e1a911b00683bf511923bf7f555e 100644 (file)
@@ -22,6 +22,7 @@ struct VM_Code_ {
     void* memoize;
     Closure *closure;
   };
+  struct Map_ handlers;
   m_str name;
   uint16_t ref;
   ae_flag flag;
@@ -52,6 +53,7 @@ struct ShredInfo_ {
   Vector args;
   MemPool mp;
   VM_Code orig;
+  struct Map_ frame;
 };
 
 struct ShredTick_ {
index 6457e216728e12544f474993979a997cc1960d8c..a1ac49401c357f56ae142d5fb81d8aee78d56500 100644 (file)
@@ -180,6 +180,9 @@ GcEnd
 GackType
 GackEnd
 Gack
+TryIni
+TryEnd
+HandleEffect
 NoOp
 EOC
 Unroll2
index 92651251e3e11c184c3fcf23af41cd80d0227888..628b1b8395a9abeccc205acf9751f6d055efda86 100644 (file)
@@ -221,6 +221,20 @@ ANN static void clean_case_list(Clean *a, Stmt_List b) {
     clean_case_list(a, b->next);
 }
 
+ANN static void clean_handler_list(Clean *a, Handler_List b) {
+  ++a->scope;
+  clean_stmt(a, b->stmt);
+  --a->scope;
+  if(b->next)
+    clean_handler_list(a, b->next);
+}
+ANN static void clean_stmt_try(Clean *a, Stmt_Try b) {
+  ++a->scope;
+  clean_stmt(a, b->stmt);
+  --a->scope;
+  clean_handler_list(a, b->handler);
+}
+
 ANN static void clean_stmt_match(Clean *a, Stmt_Match b) {
   ++a->scope;
   clean_exp(a, b->cond);
@@ -248,6 +262,7 @@ ANN static void clean_dummy(Clean *a NUSED, void *b NUSED) {}
 #define clean_stmt_pp clean_dummy
 #define clean_stmt_break clean_dummy
 #define clean_stmt_continue clean_dummy
+#define clean_stmt_resume clean_dummy
 
 DECL_STMT_FUNC(clean, void, Clean*)
 ANN static void clean_stmt(Clean *a, Stmt b) {
index fc97bf8fcd8e71412fc73bfa4342d733362ce724..64cb26334119ee2be3ac913c1cab50c86cb9a2bf 100644 (file)
@@ -57,6 +57,8 @@ ANN static void free_frame(MemPool p, Frame* a) {
       mp_free(p, Local, (Local*)vector_at(&a->stack, i - 1));
   vector_release(&a->stack);
   vector_release(&a->defer);
+  if(a->handlers.ptr)
+    map_release(&a->handlers);
   mp_free(p, Frame, a);
 }
 
@@ -1887,6 +1889,52 @@ ANN static m_bool emit_union_def(const Emitter emit NUSED, const Union_Def udef)
   return GW_OK;
 }
 
+
+// add a Goto. later the index is set to the ont of the happy path
+// maybe this and the function above can use the same machinery as returns or breaks
+ANN static inline void emit_try_goto(const restrict Emitter emit, const Vector v) {
+  const Instr instr = emit_add_instr(emit, Goto);
+  vector_add(v, (m_uint)instr);
+}
+
+// set Goto indexes the one of the happy path
+ANN static inline void try_goto_indexes(const Vector v, const m_uint pc) {
+  for(m_uint i = 0; i < vector_size(v); i++) {
+    const Instr instr = (Instr)vector_at(v, i);
+    instr->m_val = pc;
+  }
+}
+
+ANN static inline m_bool emit_handler_list(const restrict Emitter emit, const Handler_List handler,
+          const Vector v) {
+  const Instr instr = emit_add_instr(emit, HandleEffect);
+  instr->m_val2 = (m_uint)handler->xid;
+  CHECK_BB(scoped_stmt(emit, handler->stmt, 1))
+  if(handler->next)
+    CHECK_BB(emit_handler_list(emit, handler->next, v))
+  emit_try_goto(emit, v);
+  instr->m_val = emit_code_size(emit);
+  return GW_OK;
+}
+
+ANN static inline m_bool emit_stmt_try(const restrict Emitter emit, const Stmt_Try stmt) {
+  const m_uint top = emit->code->frame->try_top;
+  emit->code->frame->try_top = emit_code_size(emit);
+  (void)emit_add_instr(emit, TryIni);
+  struct Vector_ v; // store Gotos to the happy path
+  vector_init(&v);
+  CHECK_BB(scoped_stmt(emit, stmt->stmt, 1))
+  emit_try_goto(emit, &v);
+  if(!emit->code->frame->handlers.ptr)
+    map_init(&emit->code->frame->handlers);
+  CHECK_BB(emit_handler_list(emit, stmt->handler, &v))
+  try_goto_indexes(&v, emit_code_size(emit));
+  vector_release(&v);
+  emit->code->frame->try_top = top;
+  (void)emit_add_instr(emit, TryEnd);
+  return GW_OK;
+}
+
 ANN static m_bool emit_stmt_exp(const Emitter emit, const struct Stmt_Exp_* exp) {
   return exp->val ? emit_exp(emit, exp->val) : GW_OK;
 }
@@ -2071,6 +2119,12 @@ ANN static m_bool emit_stmt_defer(const Emitter emit, const struct Stmt_Defer_*
   return GW_OK;
 }
 
+ANN static m_bool emit_stmt_resume(const Emitter emit, const struct Stmt_Index_* stmt NUSED) {
+  const Instr instr = emit_add_instr(emit, Goto);
+  instr->m_val = emit->code->frame->try_top;
+  return GW_OK;
+}
+
 #define emit_stmt_while emit_stmt_flow
 #define emit_stmt_until emit_stmt_flow
 DECL_STMT_FUNC(emit, m_bool , Emitter)
index a3b1dbe73c1b440a5a9f35d34a53d32cafd1726a..3670f4420be0e69897565876fc848012f475de44 100644 (file)
@@ -22,6 +22,7 @@ ANN2(2) Context new_context(MemPool p, const Ast ast, const m_str str) {
 ANN void load_context(const Context context, const Env env) {
   context_addref((env->context = context));
   vector_add(&env->scope->nspc_stack, (vtype)env->curr);
+  env->name = context->name;
   context->nspc->parent = env->curr;
   env->curr = context->nspc;
 }
index 5b61b8d93230cbb00e5457431084f0cf5d09bbbd..ac0d16b475f4c9944086d42325260631d0b3f4dc 100644 (file)
@@ -66,7 +66,7 @@ ANN static void _fptr_tmpl_push(const Env env, const Func f) {
 }
 
 ANN static m_bool fptr_tmpl_push(const Env env, struct FptrInfo *info) {
-  if(safe_tflag(info->rhs->value_ref->from->owner_class, tflag_tmpl))
+//  if(safe_tflag(info->rhs->value_ref->from->owner_class, tflag_tmpl))
   if(!info->rhs->def->base->tmpl)
     return GW_OK;
   nspc_push_type(env->gwion->mp, env->curr);
@@ -169,7 +169,8 @@ ANN static Type fptr_type(const Env env, struct FptrInfo *info) {
 
 ANN static m_bool _check_lambda(const Env env, Exp_Lambda *l, const Func_Def def) {
 //if(l->def->base->func)return GW_OK;
-  if(safe_tflag(def->base->func->value_ref->from->owner_class, tflag_tmpl))
+  const bool is_tmpl = safe_tflag(def->base->func->value_ref->from->owner_class, tflag_tmpl);
+  if(is_tmpl)
     template_push_types(env, def->base->func->value_ref->from->owner_class->info->cdef->base.tmpl);
   Arg_List base = def->base->args, arg = l->def->base->args;
   while(base && arg) {
@@ -177,15 +178,15 @@ ANN static m_bool _check_lambda(const Env env, Exp_Lambda *l, const Func_Def def
     base = base->next;
     arg = arg->next;
   }
+  l->def->base->td = type2td(env->gwion, known_type(env, def->base->td), exp_self(l)->pos);
+  if(is_tmpl)
+    nspc_pop_type(env->gwion->mp, env->curr);
   if(base || arg) // beware, error between pops
     ERR_B(exp_self(l)->pos, _("argument number does not match for lambda"))
   l->def->base->flag = def->base->flag;
 //  if(GET_FLAG(def->base, global) && !l->owner && def->base->func->value_ref->from->owner_class)
     UNSET_FLAG(l->def->base, global);
-  l->def->base->td = type2td(env->gwion, known_type(env, def->base->td), exp_self(l)->pos);
   l->def->base->values = env->curr->info->value;
-  if(safe_tflag(def->base->func->value_ref->from->owner_class, tflag_tmpl))
-    nspc_pop_type(env->gwion->mp, env->curr);
   const m_uint scope = env->scope->depth;
 //  if(GET_FLAG(def->base, global) && !l->owner && def->base->func->value_ref->from->owner_class)
 //env_push(env, NULL, env->context->nspc);
index f8e48f264874783dc3b2b8e2ddfc3c324b75a68b..ed2567199b83796a3343b93a306cfd235abae2ec 100644 (file)
@@ -20,6 +20,7 @@
 
 #undef insert_symbol
 ANN void exception(const VM_Shred shred, const m_str c) {
+//handle(shred);
   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);
index 20965cc88bee8382fc170675cc2f527ec74ebf13..92cf2cff7f1d233582e442168136a0f1bd3cf69c 100644 (file)
@@ -1215,6 +1215,18 @@ ANN static m_bool case_loop(const Env env, const Stmt_Match stmt) {
   return GW_OK;
 }
 
+
+ANN static inline m_bool check_handler_list(const restrict Env env, const Handler_List handler) {
+  if(handler->next)
+    CHECK_BB(check_handler_list(env, handler->next))
+  RET_NSPC(check_stmt(env, handler->stmt))
+}
+
+ANN static inline m_bool check_stmt_try(const restrict Env env, const Stmt_Try stmt) {
+  CHECK_BB(check_handler_list(env, stmt->handler))
+  RET_NSPC(check_stmt(env, stmt->stmt))
+}
+
 ANN static m_bool _check_stmt_match(const Env env, const Stmt_Match stmt) {
   CHECK_OB(check_exp(env, stmt->cond))
   MATCH_INI(env->scope)
@@ -1246,6 +1258,7 @@ ANN static m_bool check_stmt_defer(const Env env, const Stmt_Defer stmt) {
   return check_stmt(env, stmt->stmt);
 }
 
+#define check_stmt_resume dummy_func
 DECL_STMT_FUNC(check, m_bool , Env)
 
 ANN m_bool check_stmt(const Env env, const Stmt stmt) {
index b27270e2e4cd399b91f7f167d3d3b9feb4ee82a8..126a36a2ddf4eef76292e91dd16456387ba159cf 100644 (file)
@@ -283,6 +283,18 @@ ANN static inline m_bool scan1_stmt_match(const restrict Env env, const Stmt_Mat
   RET_NSPC(_scan1_stmt_match(env, stmt))
 }
 
+
+ANN static inline m_bool scan1_handler_list(const restrict Env env, const Handler_List handler) {
+  if(handler->next)
+    CHECK_BB(scan1_handler_list(env, handler->next))
+  RET_NSPC(scan1_stmt(env, handler->stmt))
+}
+
+ANN static inline m_bool scan1_stmt_try(const restrict Env env, const Stmt_Try stmt) {
+  CHECK_BB(scan1_handler_list(env, stmt->handler))
+  RET_NSPC(scan1_stmt(env, stmt->stmt))
+}
+
 ANN static inline m_bool stmt_each_defined(const restrict Env env, const Stmt_Each stmt) {
   if(nspc_lookup_value1(env->curr, stmt->sym))
     ERR_B(stmt_self(stmt)->pos, _("foreach value '%s' is already defined"), s_name(stmt->sym))
@@ -454,9 +466,10 @@ ANN m_bool scan1_union_def(const Env env, const Union_Def udef) {
 
 #define scan1_stmt_while    scan1_stmt_flow
 #define scan1_stmt_until    scan1_stmt_flow
-#define scan1_stmt_continue (void*)dummy_func
-#define scan1_stmt_break    (void*)dummy_func
+#define scan1_stmt_continue dummy_func
+#define scan1_stmt_break    dummy_func
 #define scan1_stmt_return   scan1_stmt_exp
+#define scan1_stmt_resume   dummy_func
 
 ANN static m_bool scan1_stmt_pp(const Env env, const Stmt_PP stmt) {
   if(stmt->pp_type == ae_pp_include)
index 9fa8591946dc6b05a65c6dd6bd1c1d77c61079ab..3dd396d0cf3dbbd82345270205096e0f88fb0d30 100644 (file)
@@ -201,6 +201,17 @@ ANN static inline m_bool _scan2_stmt_match(const restrict Env env, const Stmt_Ma
   return GW_OK;
 }
 
+ANN static inline m_bool scan2_handler_list(const restrict Env env, const Handler_List handler) {
+  if(handler->next)
+    CHECK_BB(scan2_handler_list(env, handler->next))
+  RET_NSPC(scan2_stmt(env, handler->stmt))
+}
+
+ANN static inline m_bool scan2_stmt_try(const restrict Env env, const Stmt_Try stmt) {
+  CHECK_BB(scan2_handler_list(env, stmt->handler))
+  RET_NSPC(scan2_stmt(env, stmt->stmt))
+}
+
 ANN static inline m_bool scan2_stmt_match(const restrict Env env, const Stmt_Match stmt) {
   CHECK_BB(scan2_exp(env, stmt->cond))
   RET_NSPC(_scan2_stmt_match(env, stmt))
@@ -247,9 +258,10 @@ ANN m_bool scan2_union_def(const Env env NUSED, const Union_Def udef) {
 
 #define scan2_stmt_while    scan2_stmt_flow
 #define scan2_stmt_until    scan2_stmt_flow
-#define scan2_stmt_continue (void*)dummy_func
-#define scan2_stmt_break    (void*)dummy_func
+#define scan2_stmt_continue dummy_func
+#define scan2_stmt_break    dummy_func
 #define scan2_stmt_return   scan2_stmt_exp
+#define scan2_stmt_resume   dummy_func
 
 ANN static m_bool scan2_stmt_pp(const Env env, const Stmt_PP stmt) {
   if(stmt->pp_type == ae_pp_include)
index d11c566ce1554e77bbb3e45db973a44bb915edc6..18a641f84df4b128726d25f54b0bb6ffe866d2a6 100644 (file)
@@ -144,7 +144,60 @@ ANN static VM_Shred init_fork_shred(const VM_Shred shred, const VM_Code code, co
   return ME(o);
 }
 
-#define TEST0(t, pos) if(!*(t*)(reg-pos)){ shred->pc = PC; exception(shred, "ZeroDivideException"); break; }
+ANN static bool unwind(VM_Shred shred, const Symbol effect) {
+  // there is an handler
+  if(!map_size(&shred->info->frame))
+    return true;
+  if(shred->code->handlers.ptr) {
+    const m_uint start = VKEY(&shred->info->frame, VLEN(&shred->info->frame) - 1);
+    if(start > shred->pc)
+      return true;
+    const Map m = &shred->code->handlers;
+    m_uint pc = 0;
+    for(m_uint i = 0; i < map_size(m); i++) {
+      if(start > shred->pc)
+        break;
+      if(start < shred->pc && VKEY(m, i) > shred->pc) {
+         const m_uint next = VKEY(m, i);
+         const Instr instr = (Instr)vector_at(shred->code->instr, next + 1);
+         if(!instr->m_val2 || (Symbol)instr->m_val2 == effect) {
+           pc = next + 1;
+           break;
+         }
+      }
+    }
+    if(!pc) // outside of a try statement
+      return true;
+    shred->reg = (m_bit*)VVAL(&shred->info->frame, VLEN(&shred->info->frame) - 1);
+    shredule(shred->tick->shreduler, shred, 0);
+    shred->pc = pc;//VKEY(m, i);
+    return false;
+  }
+  // there might be no more stack to unwind
+  map_remove(&shred->info->frame, VLEN(&shred->info->frame)-1);
+  if(shred->mem == (m_bit*)shred + sizeof(struct VM_Shred_) + SIZEOF_REG)
+    return true;
+  // literally unwind
+  shred->pc   = *(m_uint*) (shred->mem - SZ_INT * 2);
+  shred->code = *(VM_Code*)(shred->mem - SZ_INT * 3);
+  shred->mem -= (*(m_uint*)(shred->mem - SZ_INT * 4) + SZ_INT * 4);
+  return unwind(shred, effect);
+}
+
+ANN void handle(VM_Shred shred, const Symbol xid) {
+  // remove from the shreduler
+  // TODO: get shred->mem and shred->reg offsets
+  shreduler_remove(shred->tick->shreduler, shred, false);
+  // do the unwiding
+  if(unwind(shred, xid)) {
+    vm_shred_exit(shred);
+    puts(s_name(xid));
+  }
+  // I guess the VM could have *trace mode*
+  // which would happen here from the top
+}
+
+#define TEST0(t, pos) if(!*(t*)(reg-pos)){ shred->pc = PC; handle(shred, insert_symbol(vm->gwion->st, "ZeroDivideException")); break; }
 
 #define ADVANCE() byte += BYTECODE_SZ;
 
@@ -331,7 +384,7 @@ ANN void vm_run(const VM* vm) { // lgtm [cpp/use-of-goto]
     &&upvalueint, &&upvaluefloat, &&upvalueother, &&upvalueaddr,
     &&dotfunc,
     &&gcini, &&gcadd, &&gcend,
-    &&gacktype, &&gackend, &&gack, &&noop, &&eoc, &&unroll2, &&other, &&regpushimm
+    &&gacktype, &&gackend, &&gack, &&try_ini, &&try_end, &&handleeffect, &&noop, &&eoc, &&unroll2, &&other, &&regpushimm
   };
   const Shreduler s = vm->shreduler;
   register VM_Shred shred;
@@ -952,6 +1005,15 @@ gack:
   VM_OUT
   gack(shred, VAL);
   goto in;
+try_ini:
+  if(!shred->info->frame.ptr) // ???
+    map_init(&shred->info->frame);
+  map_set(&shred->info->frame, PC, (m_uint)shred->reg);
+  DISPATCH();
+try_end:
+  map_remove(&shred->info->frame, VLEN(&shred->info->frame)-1);
+handleeffect:
+// this should check the *xid* of the exception
 noop:
   DISPATCH();
 other:
index d1b29f1164097aa2e6236252991b9b678fd129fa..aed65b8a4bd49cf33c78f40c75a2ebd1d4854a9f 100644 (file)
@@ -33,6 +33,8 @@ ANN void free_vmcode(VM_Code a, Gwion gwion) {
       }
       free_code_instr(a->instr, gwion);
     }
+    if(a->handlers.ptr)
+      map_release(&a->handlers);
     free_vector(gwion->mp, a->instr);
   }
   free_mstr(gwion->mp, a->name);
@@ -42,7 +44,8 @@ ANN void free_vmcode(VM_Code a, Gwion gwion) {
 static inline uint isgoto(const unsigned opcode) {
   return opcode == eGoto || opcode == eArrayTop ||
       opcode == eBranchEqInt || opcode == eBranchNeqInt ||
-      opcode == eBranchEqFloat || opcode == eBranchNeqFloat;
+      opcode == eBranchEqFloat || opcode == eBranchNeqFloat ||
+      opcode == eHandleEffect;
 }
 
 static inline void setpc(const m_bit *data, const m_uint i) {
@@ -130,7 +133,13 @@ ANN static m_bit* tobytecode(MemPool p, const VM_Code code) {
           if(instr->m_val <= vector_at(&nop, pc))
             break;
         }
-        *(m_uint*)(data + SZ_INT) = instr->m_val > pc ? instr->m_val - pc : 0;
+        const m_uint new_pc = instr->m_val > pc ? instr->m_val - pc : 0;
+        if(instr->opcode == eHandleEffect) {
+            if(!code->handlers.ptr)
+              map_init(&code->handlers);
+            map_set(&code->handlers, j, new_pc);
+        }
+        *(m_uint*)(data + SZ_INT) = new_pc;
       }
       setpc(data, j);
       ++j;
index 0864d7dd5b9c99179f9e3c579873badcd86a6003..8695a2a482965c0c78320abfe89cb91e9b4b2f54 100644 (file)
@@ -45,6 +45,8 @@ void free_vm_shred(VM_Shred shred) {
   for(m_uint i = vector_size(&shred->gc) + 1; --i;)
     release((M_Object)vector_at(&shred->gc, i - 1), shred);
   vector_release(&shred->gc);
+  if(shred->info->frame.ptr)
+    map_release(&shred->info->frame);
   vmcode_remref(shred->info->orig, shred->info->vm->gwion);
   const MemPool mp = shred->info->mp;
   mp_free(mp, ShredTick, shred->tick);