mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 15:43:01 +00:00 
			
		
		
		
	Add proto field to tables to allow prototypal inheritance.
This commit is contained in:
		| @@ -114,14 +114,17 @@ | ||||
|         ret) | ||||
|  }) | ||||
|  | ||||
| (defn onerr [t e] | ||||
|  (print (string t " error: " e))) | ||||
| # Compile time | ||||
|  | ||||
| (var *read* nil) | ||||
| (var *onvalue* identity) | ||||
| (var *env* _env) | ||||
|  | ||||
| (def require-loading @{}) | ||||
|  | ||||
| (defn onerr [t e] | ||||
|  (print (string t " error: " e))) | ||||
|  | ||||
| (defn char-stream [getchunk ondone] | ||||
|  (fiber (fn [parent] | ||||
|    (def buf @"") | ||||
| @@ -197,7 +200,7 @@ | ||||
|   (def wrapper (fiber (fn [] | ||||
|     (while *read* | ||||
|       (def source (*read*)) | ||||
|       (def res (compile source _env)) | ||||
|       (def res (compile source *env*)) | ||||
|       (if (= (type res) :function) | ||||
|         (*onvalue* (res)) | ||||
|         (onerr "compile" (get res :error))))))) | ||||
|   | ||||
| @@ -47,6 +47,9 @@ static const DstReg cfuns[] = { | ||||
|     {"buffer", dst_core_buffer}, | ||||
|     {"gensym", dst_core_gensym}, | ||||
|     {"get", dst_core_get}, | ||||
|     {"rawget", dst_core_rawget}, | ||||
|     {"getproto", dst_core_getproto}, | ||||
|     {"setproto", dst_core_setproto}, | ||||
|     {"put", dst_core_put}, | ||||
|     {"length", dst_core_length}, | ||||
|     {"gccollect", dst_core_gccollect}, | ||||
|   | ||||
| @@ -172,6 +172,33 @@ int dst_core_get(DstArgs args) { | ||||
|     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) { | ||||
|     const char *status = ""; | ||||
|     if (args.n != 1) return dst_throw(args, "expected 1 argument"); | ||||
|   | ||||
| @@ -109,10 +109,15 @@ static void dst_mark_array(DstArray *array) { | ||||
| } | ||||
|  | ||||
| static void dst_mark_table(DstTable *table) { | ||||
|     recur: /* Manual tail recursion */ | ||||
|     if (dst_gc_reachable(table)) | ||||
|         return; | ||||
|     dst_gc_mark(table); | ||||
|     dst_mark_kvs(table->data, table->capacity); | ||||
|     if (table->proto) { | ||||
|         table = table->proto; | ||||
|         goto recur; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void dst_mark_struct(const DstKV *st) { | ||||
|   | ||||
| @@ -43,6 +43,7 @@ DstTable *dst_table_init(DstTable *table, int32_t capacity) { | ||||
|     } | ||||
|     table->count = 0; | ||||
|     table->deleted = 0; | ||||
|     table->proto = NULL; | ||||
|     return table; | ||||
| } | ||||
|  | ||||
| @@ -114,8 +115,25 @@ static void dst_table_rehash(DstTable *t, int32_t size) { | ||||
|     free(olddata); | ||||
| } | ||||
|  | ||||
| /* Get a value out of the object */ | ||||
| /* Get a value out of the table */ | ||||
| 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); | ||||
|     if (NULL != bucket && !dst_checktype(bucket->key, DST_NIL)) | ||||
|         return bucket->value; | ||||
|   | ||||
| @@ -120,6 +120,7 @@ DstTable *dst_table(int32_t capacity); | ||||
| DstTable *dst_table_init(DstTable *table, int32_t capacity); | ||||
| void dst_table_deinit(DstTable *table); | ||||
| Dst dst_table_get(DstTable *t, Dst key); | ||||
| Dst dst_table_rawget(DstTable *t, Dst key); | ||||
| Dst dst_table_remove(DstTable *t, Dst key); | ||||
| void dst_table_put(DstTable *t, Dst key, Dst value); | ||||
| const DstKV *dst_table_next(DstTable *t, const DstKV *kv); | ||||
|   | ||||
| @@ -120,6 +120,9 @@ extern "C" { | ||||
|  * ands crashing (the parser). Instead, error out. */ | ||||
| #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. | ||||
|  * If this is not defined, fiber stacks can grow without limit (until memory | ||||
|  * runs out) */ | ||||
|   | ||||
| @@ -90,6 +90,9 @@ int dst_core_buffer(DstArgs args); | ||||
| int dst_core_gensym(DstArgs args); | ||||
| int dst_core_length(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_current(DstArgs args); | ||||
| int dst_core_put(DstArgs args); | ||||
|   | ||||
| @@ -359,6 +359,7 @@ struct DstBuffer { | ||||
| /* A mutable associative data type. Backed by a hashtable. */ | ||||
| struct DstTable { | ||||
|     DstKV *data; | ||||
|     DstTable *proto; | ||||
|     int32_t count; | ||||
|     int32_t capacity; | ||||
|     int32_t deleted; | ||||
|   | ||||
							
								
								
									
										38
									
								
								thoughts.md
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								thoughts.md
									
									
									
									
									
								
							| @@ -2,38 +2,6 @@ | ||||
|  | ||||
| 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 | ||||
|   is reasonably useful, it complicates the implementation of macros and other source | ||||
|   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. | ||||
|  | ||||
| - 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 | ||||
|   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 | ||||
| @@ -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 | ||||
|   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 (?). | ||||
|   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 | ||||
| @@ -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 | ||||
|   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. | ||||
|  | ||||
| - Actually make a debugger. While the VM changes to enable debugging are relatively | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose