1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-28 19:19:53 +00:00

Add proto field to tables to allow prototypal inheritance.

This commit is contained in:
Calvin Rose 2018-03-10 13:34:46 -05:00
parent 0c3b0673ff
commit 0b6ac1698c
10 changed files with 71 additions and 39 deletions

View File

@ -114,14 +114,17 @@
ret) ret)
}) })
(defn onerr [t e] # Compile time
(print (string t " error: " e)))
(var *read* nil) (var *read* nil)
(var *onvalue* identity) (var *onvalue* identity)
(var *env* _env)
(def require-loading @{}) (def require-loading @{})
(defn onerr [t e]
(print (string t " error: " e)))
(defn char-stream [getchunk ondone] (defn char-stream [getchunk ondone]
(fiber (fn [parent] (fiber (fn [parent]
(def buf @"") (def buf @"")
@ -197,7 +200,7 @@
(def wrapper (fiber (fn [] (def wrapper (fiber (fn []
(while *read* (while *read*
(def source (*read*)) (def source (*read*))
(def res (compile source _env)) (def res (compile source *env*))
(if (= (type res) :function) (if (= (type res) :function)
(*onvalue* (res)) (*onvalue* (res))
(onerr "compile" (get res :error))))))) (onerr "compile" (get res :error)))))))

View File

@ -47,6 +47,9 @@ static const DstReg cfuns[] = {
{"buffer", dst_core_buffer}, {"buffer", dst_core_buffer},
{"gensym", dst_core_gensym}, {"gensym", dst_core_gensym},
{"get", dst_core_get}, {"get", dst_core_get},
{"rawget", dst_core_rawget},
{"getproto", dst_core_getproto},
{"setproto", dst_core_setproto},
{"put", dst_core_put}, {"put", dst_core_put},
{"length", dst_core_length}, {"length", dst_core_length},
{"gccollect", dst_core_gccollect}, {"gccollect", dst_core_gccollect},

View File

@ -172,6 +172,33 @@ int dst_core_get(DstArgs args) {
return dst_return(args, ds); return dst_return(args, ds);
} }
int dst_core_rawget(DstArgs args) {
if (args.n != 2) return dst_throw(args, "expected 2 arguments");
if (!dst_checktype(args.v[0], DST_TABLE)) return dst_throw(args, "expected table");
return dst_return(args, dst_table_rawget(dst_unwrap_table(args.v[0]), args.v[1]));
}
int dst_core_getproto(DstArgs args) {
DstTable *t;
if (args.n != 1) return dst_throw(args, "expected 1 argument");
if (!dst_checktype(args.v[0], DST_TABLE)) return dst_throw(args, "expected table");
t = dst_unwrap_table(args.v[0]);
return dst_return(args, t->proto
? dst_wrap_table(t->proto)
: dst_wrap_nil());
}
int dst_core_setproto(DstArgs args) {
if (args.n != 2) return dst_throw(args, "expected 2 arguments");
if (!dst_checktype(args.v[0], DST_TABLE)) return dst_throw(args, "expected table");
if (!dst_checktype(args.v[1], DST_TABLE) && !dst_checktype(args.v[1], DST_NIL))
return dst_throw(args, "expected table");
dst_unwrap_table(args.v[0])->proto = dst_checktype(args.v[1], DST_TABLE)
? dst_unwrap_table(args.v[1])
: NULL;
return dst_return(args, args.v[0]);
}
int dst_core_fiber_status(DstArgs args) { int dst_core_fiber_status(DstArgs args) {
const char *status = ""; const char *status = "";
if (args.n != 1) return dst_throw(args, "expected 1 argument"); if (args.n != 1) return dst_throw(args, "expected 1 argument");

View File

@ -109,10 +109,15 @@ static void dst_mark_array(DstArray *array) {
} }
static void dst_mark_table(DstTable *table) { static void dst_mark_table(DstTable *table) {
recur: /* Manual tail recursion */
if (dst_gc_reachable(table)) if (dst_gc_reachable(table))
return; return;
dst_gc_mark(table); dst_gc_mark(table);
dst_mark_kvs(table->data, table->capacity); dst_mark_kvs(table->data, table->capacity);
if (table->proto) {
table = table->proto;
goto recur;
}
} }
static void dst_mark_struct(const DstKV *st) { static void dst_mark_struct(const DstKV *st) {

View File

@ -43,6 +43,7 @@ DstTable *dst_table_init(DstTable *table, int32_t capacity) {
} }
table->count = 0; table->count = 0;
table->deleted = 0; table->deleted = 0;
table->proto = NULL;
return table; return table;
} }
@ -114,8 +115,25 @@ static void dst_table_rehash(DstTable *t, int32_t size) {
free(olddata); free(olddata);
} }
/* Get a value out of the object */ /* Get a value out of the table */
Dst dst_table_get(DstTable *t, Dst key) { Dst dst_table_get(DstTable *t, Dst key) {
DstKV *bucket = dst_table_find(t, key);
if (NULL != bucket && !dst_checktype(bucket->key, DST_NIL))
return bucket->value;
/* Check prototypes */
{
int i;
for (i = DST_MAX_PROTO_DEPTH, t = t->proto; t && i; t = t->proto, --i) {
bucket = dst_table_find(t, key);
if (NULL != bucket && !dst_checktype(bucket->key, DST_NIL))
return bucket->value;
}
}
return dst_wrap_nil();
}
/* Get a value out of the table. Don't check prototype tables. */
Dst dst_table_rawget(DstTable *t, Dst key) {
DstKV *bucket = dst_table_find(t, key); DstKV *bucket = dst_table_find(t, key);
if (NULL != bucket && !dst_checktype(bucket->key, DST_NIL)) if (NULL != bucket && !dst_checktype(bucket->key, DST_NIL))
return bucket->value; return bucket->value;

View File

@ -120,6 +120,7 @@ DstTable *dst_table(int32_t capacity);
DstTable *dst_table_init(DstTable *table, int32_t capacity); DstTable *dst_table_init(DstTable *table, int32_t capacity);
void dst_table_deinit(DstTable *table); void dst_table_deinit(DstTable *table);
Dst dst_table_get(DstTable *t, Dst key); Dst dst_table_get(DstTable *t, Dst key);
Dst dst_table_rawget(DstTable *t, Dst key);
Dst dst_table_remove(DstTable *t, Dst key); Dst dst_table_remove(DstTable *t, Dst key);
void dst_table_put(DstTable *t, Dst key, Dst value); void dst_table_put(DstTable *t, Dst key, Dst value);
const DstKV *dst_table_next(DstTable *t, const DstKV *kv); const DstKV *dst_table_next(DstTable *t, const DstKV *kv);

View File

@ -120,6 +120,9 @@ extern "C" {
* ands crashing (the parser). Instead, error out. */ * ands crashing (the parser). Instead, error out. */
#define DST_RECURSION_GUARD 1024 #define DST_RECURSION_GUARD 1024
/* Maximum depth to follow table prototypes before giving up and returning nil. */
#define DST_MAX_PROTO_DEPTH 200
/* Define max stack size for stacks before raising a stack overflow error. /* Define max stack size for stacks before raising a stack overflow error.
* If this is not defined, fiber stacks can grow without limit (until memory * If this is not defined, fiber stacks can grow without limit (until memory
* runs out) */ * runs out) */

View File

@ -90,6 +90,9 @@ int dst_core_buffer(DstArgs args);
int dst_core_gensym(DstArgs args); int dst_core_gensym(DstArgs args);
int dst_core_length(DstArgs args); int dst_core_length(DstArgs args);
int dst_core_get(DstArgs args); int dst_core_get(DstArgs args);
int dst_core_rawget(DstArgs args);
int dst_core_getproto(DstArgs args);
int dst_core_setproto(DstArgs args);
int dst_core_fiber_status(DstArgs args); int dst_core_fiber_status(DstArgs args);
int dst_core_fiber_current(DstArgs args); int dst_core_fiber_current(DstArgs args);
int dst_core_put(DstArgs args); int dst_core_put(DstArgs args);

View File

@ -359,6 +359,7 @@ struct DstBuffer {
/* A mutable associative data type. Backed by a hashtable. */ /* A mutable associative data type. Backed by a hashtable. */
struct DstTable { struct DstTable {
DstKV *data; DstKV *data;
DstTable *proto;
int32_t count; int32_t count;
int32_t capacity; int32_t capacity;
int32_t deleted; int32_t deleted;

View File

@ -2,38 +2,6 @@
A collection of thoughts and todo tasks for the project. A collection of thoughts and todo tasks for the project.
- Allow entrances into the VM to track the size of the stack when they entered, and return
when the stack is less that. This would make calling dst functions from C feasible (
The programmer would still have to ensure no GC violations).
Instead, we can just keep allocating new Fibers when we call a dst function from C. A pool
of fibers would mostly mitigate the overhead of allocation. (going with this).
We can now call into dst from C without suspending the entire garbage collector. A separate
function does exactly that.
- Make unknown instruction in vm trap and put current fiber in a new state, 'debug'.
This could allow implementation of a debugger. Since opcodes are encoded in one byte,
we can use the most significant bit (0x80) to set breakpoints in code, assuming all valid
opcodes are in the range [0, 127]. The debugger could simply set the MSB of the opcode for each
instruction that was marked. This would allow debugging with 0 overhead.
We could also add a debugger instruction, much like JavaScripts debugger; statement very easily.
Lastly, to make continuation after a breakpoint easier, stopping on the first instruction
could be optional. This could be as simple as selecting the first 7 bits of the instructions
instead of the usual 8 for the very instruction executed after entering the vm loop.
What exactly should happen on a trapped instruction is another issue. It would be preferable
for the runtime to be able to handle a trap in dst, but allow nested fibers to not capture
debugging signals unless needed.
Fiber's currently propagate all states to their direct parent, but perhaps each fiber
could have a mask for different signals - error, debug, return. So a single fiber could
say capture returns, error, but not debug. Possibly like try - catch in other languages, where
we only catch certain kinds of errors. The default fiber would be to only mask debug, so a single fiber
could wrap an entire running application for debugging.
- Remove the concept of 'Ast node'. While providing fine-grained source mapping is - Remove the concept of 'Ast node'. While providing fine-grained source mapping is
is reasonably useful, it complicates the implementation of macros and other source is reasonably useful, it complicates the implementation of macros and other source
transforming operations. Instead, we can key collection types (which have the unique transforming operations. Instead, we can key collection types (which have the unique
@ -53,7 +21,7 @@ A collection of thoughts and todo tasks for the project.
definitions generated with this one form. definitions generated with this one form.
- Serialization and deserialization of all datatypes. This would allow loading of bytecode - Serialization and deserialization of all datatypes. This would allow loading of bytecode
without needing the compiler present. However, loading C functions is currently problamatic. without needing the compiler present. However, loading C functions is currently problematic.
C functions could perhaps be wrapped in data structures that contain some meta information C functions could perhaps be wrapped in data structures that contain some meta information
about them, say home module and types. This could also allow some automated type checking for about them, say home module and types. This could also allow some automated type checking for
C functions rather than writing it manually. Some slight overhead could perhaps be compensated C functions rather than writing it manually. Some slight overhead could perhaps be compensated
@ -65,7 +33,7 @@ A collection of thoughts and todo tasks for the project.
values and data structure style values. For example, simply adding special keys as fields values and data structure style values. For example, simply adding special keys as fields
would make plain a table or struct possibly become object-like if the write keys are added. would make plain a table or struct possibly become object-like if the write keys are added.
A Lua like solution would be a metatables. It would perhaps make sense to only allow A Lua like solution would be metatables. It would perhaps make sense to only allow
metatables on tables, as object like behavior seems to makes most sense on mutable data (?). metatables on tables, as object like behavior seems to makes most sense on mutable data (?).
For example, metatables on a struct could allow non-pure behavior unless they were extremely For example, metatables on a struct could allow non-pure behavior unless they were extremely
limited, or purity was enforced somehow. This could break expectations of a struct to behave limited, or purity was enforced somehow. This could break expectations of a struct to behave
@ -73,7 +41,7 @@ A collection of thoughts and todo tasks for the project.
Also, it might make sense that the metatable of a value would actually be a struct, so Also, it might make sense that the metatable of a value would actually be a struct, so
a metastruct. Implementations of Lua (LuaJIT) do not allow (or do not acknowledge) a metastruct. Implementations of Lua (LuaJIT) do not allow (or do not acknowledge)
changing certain values of a metatables after it is set, such as __gc, for performance changing certain values of a metatables after it is set, such as gc, for performance
reasons. reasons.
- Actually make a debugger. While the VM changes to enable debugging are relatively - Actually make a debugger. While the VM changes to enable debugging are relatively