From 412d40d09f8913aaf84fbffdaee4027ddd98548c Mon Sep 17 00:00:00 2001 From: bakpakin Date: Fri, 24 Nov 2017 23:17:04 -0500 Subject: [PATCH] Work on interpreter. adding more opcodes and syscalls. --- contrib/vim/ftdetect/dst.vim | 1 + contrib/vim/ftplugin/dst.vim | 25 ++ contrib/vim/indent/dst.vim | 17 ++ contrib/vim/syntax/dst.vim | 69 +++++ core/asm.c | 57 +++-- core/opcodes.h | 12 +- core/parse.c | 2 +- core/syscalls.c | 89 ++++++- core/table.c | 3 +- core/util.c | 11 - core/value.c | 179 +++++++++++++ core/vm.c | 199 ++++++++------ dsts/compile.dsts | 0 unittests/sample.dsts => dsts/example.dsts | 2 +- dsts/minimal.dsts | 24 ++ junkyard/ds.c | 285 --------------------- unittests/asm_test.c | 2 +- 17 files changed, 569 insertions(+), 408 deletions(-) create mode 100644 contrib/vim/ftdetect/dst.vim create mode 100644 contrib/vim/ftplugin/dst.vim create mode 100644 contrib/vim/indent/dst.vim create mode 100644 contrib/vim/syntax/dst.vim create mode 100644 dsts/compile.dsts rename unittests/sample.dsts => dsts/example.dsts (99%) create mode 100644 dsts/minimal.dsts delete mode 100644 junkyard/ds.c diff --git a/contrib/vim/ftdetect/dst.vim b/contrib/vim/ftdetect/dst.vim new file mode 100644 index 00000000..038d2136 --- /dev/null +++ b/contrib/vim/ftdetect/dst.vim @@ -0,0 +1 @@ +autocmd BufRead,BufNewFile *.dst,*.dsts setlocal filetype=dst diff --git a/contrib/vim/ftplugin/dst.vim b/contrib/vim/ftplugin/dst.vim new file mode 100644 index 00000000..7ecdbb90 --- /dev/null +++ b/contrib/vim/ftplugin/dst.vim @@ -0,0 +1,25 @@ +" Vim filetype plugin file +" Language: DST +" Maintainer: Calvin Rose + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:cpo_save = &cpo +set cpo&vim + +setlocal iskeyword+=?,-,*,!,+,/,=,<,>,.,:,$ + +" There will be false positives, but this is better than missing the whole set +" of user-defined def* definitions. +setlocal define=\\v[(/]def(ault)@!\\S* + +" Remove 't' from 'formatoptions' to avoid auto-wrapping code. +setlocal formatoptions-=t + +setlocal comments=n:# +setlocal commentstring=#\ %s + +let &cpo = s:cpo_save diff --git a/contrib/vim/indent/dst.vim b/contrib/vim/indent/dst.vim new file mode 100644 index 00000000..b424b02c --- /dev/null +++ b/contrib/vim/indent/dst.vim @@ -0,0 +1,17 @@ +" Vim filetype plugin file +" Language: DST +" Maintainer: Calvin Rose + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +let s:cpo_save = &cpo +set cpo&vim + +setlocal noautoindent nosmartindent +setlocal softtabstop=2 shiftwidth=2 expandtab +setlocal indentkeys=!,o,O + +let &cpo = s:cpo_save diff --git a/contrib/vim/syntax/dst.vim b/contrib/vim/syntax/dst.vim new file mode 100644 index 00000000..8b5c13e1 --- /dev/null +++ b/contrib/vim/syntax/dst.vim @@ -0,0 +1,69 @@ +" Vim syntax file +" Language: DST +" Maintainer: Calvin Rose + +if exists("b:current_syntax") + finish +endif + +let s:cpo_sav = &cpo +set cpo&vim + +if has("folding") && exists("g:dst_fold") && g:dst_fold > 0 + setlocal foldmethod=syntax +endif + +syntax keyword DstCommentTodo contained FIXME XXX TODO FIXME: XXX: TODO: + +" DST comments +syn match DstComment "#.*$" contains=DstCommentTodo,@Spell + +syntax match DstStringEscape '\v\\%([\\btnfr"])' contained +syntax region DstString matchgroup=DstStringDelimiter start=/"/ skip=/\\\\\|\\"/ end=/"/ contains=DstStringEscape,@Spell + +" Dst Symbols +syn match DstSymbol '\v<%([a-zA-Z!$&*_+=|<.>?-])+%([a-zA-Z0-9!#$%&*_+=|'<.>/?-])*>' + +" DST numbers +syn match DstReal '\v<[-+]?%(\d+|\d+\.\d*)%([eE][-+]?\d+)?>' +syn match DstInteger '\v<[-+]?%(\d+)>' + +syn match DstConstant 'nil' +syn match DstConstant 'true' +syn match DstConstant 'false' + +" Dst Keywords +syn match DstKeyword '\v<:[a-zA-Z0-9_\-]*>' + +syntax match DstQuote "'" + +" -*- TOP CLUSTER -*- +syntax cluster DstTop contains=@Spell,DstComment,DstConstant,DstQuote,DstKeyword,DstSymbol,DstInteger,DstReal,DstString,DstTuple,DstArray,DstTable,DstStruct + +syntax region DstTuple matchgroup=DstParen start="(" end=")" contains=@DstTop fold +syntax region DstArray matchgroup=DstParen start="\[" end="]" contains=@DstTop fold +syntax region DstTable matchgroup=DstParen start="{" end="}" contains=@DstTop fold +syntax region DstStruct matchgroup=DstParen start="@{" end="}" contains=@DstTop fold + +" Highlight superfluous closing parens, brackets and braces. +syntax match DstError "]\|}\|)" + +syntax sync fromstart + +" Highlighting +hi def link DstComment Comment +hi def link DstSymbol Identifier +hi def link DstInteger Number +hi def link DstReal Type +hi def link DstConstant Constant +hi def link DstKeyword Keyword +hi def link DstString String +hi def link DstStringDelimiter String + +hi def link DstQuote SpecialChar +hi def link DstParen Delimiter + +let b:current_syntax = "dst" + +let &cpo = s:cpo_sav +unlet! s:cpo_sav diff --git a/core/asm.c b/core/asm.c index f3aaeb3e..4975e3c3 100644 --- a/core/asm.c +++ b/core/asm.c @@ -72,6 +72,7 @@ enum DstInstructionType { DIT_SU, /* Unsigned */ DIT_SSS, DIT_SSI, + DIT_SSU, DIT_SES, DIT_SC }; @@ -117,9 +118,6 @@ static const DstInstructionDef dst_ops[] = { {"bitxor", DIT_SSS, DOP_BXOR}, {"call", DIT_SS, DOP_CALL}, {"closure", DIT_SC, DOP_CLOSURE}, - {"coerce-integer", DIT_SS, DOP_COERCE_INTEGER}, - {"coerce-real", DIT_SS, DOP_COERCE_REAL}, - {"coerce-string", DIT_SS, DOP_COERCE_STRING}, {"compare", DIT_SSS, DOP_COMPARE}, {"divide", DIT_SSS, DOP_DIVIDE}, {"divide-immediate", DIT_SSI, DOP_DIVIDE_IMMEDIATE}, @@ -127,9 +125,13 @@ static const DstInstructionDef dst_ops[] = { {"divide-real", DIT_SSS, DOP_DIVIDE_REAL}, {"equals", DIT_SSS, DOP_EQUALS}, {"error", DIT_S, DOP_ERROR}, + {"get", DIT_SSS, DOP_GET}, + {"get-index", DIT_SSU, DOP_GET_INDEX}, {"greater-than", DIT_SSS, DOP_GREATER_THAN}, {"jump", DIT_L, DOP_JUMP}, {"jump-if", DIT_SL, DOP_JUMP_IF}, + {"jump-if-not", DIT_SL, DOP_JUMP_IF_NOT}, + {"less-than", DIT_SSS, DOP_LESS_THAN}, {"load-boolean", DIT_S, DOP_LOAD_BOOLEAN}, {"load-constant", DIT_SC, DOP_LOAD_CONSTANT}, {"load-integer", DIT_SI, DOP_LOAD_INTEGER}, @@ -143,9 +145,11 @@ static const DstInstructionDef dst_ops[] = { {"multiply-real", DIT_SSS, DOP_MULTIPLY_REAL}, {"noop", DIT_0, DOP_NOOP}, {"push", DIT_S, DOP_PUSH}, + {"push-array", DIT_S, DOP_PUSH_ARRAY}, {"push2", DIT_SS, DOP_PUSH_2}, {"push3", DIT_SSS, DOP_PUSH_3}, - {"push-array", DIT_S, DOP_PUSH_ARRAY}, + {"put", DIT_SSS, DOP_PUT}, + {"put-index", DIT_SSU, DOP_PUT_INDEX}, {"return", DIT_S, DOP_RETURN}, {"return-nil", DIT_0, DOP_RETURN_NIL}, {"set-upvalue", DIT_SES, DOP_SET_UPVALUE}, @@ -240,6 +244,7 @@ static void dst_asm_errorv(DstAssembler *a, const uint8_t *m) { /* Parse an argument to an assembly instruction, and return the result as an * integer. This integer will need to be trimmed and bound checked. */ static int64_t doarg_1(DstAssembler *a, DstOpArgType argtype, DstValue x) { + int64_t ret = -1; DstTable *c; switch (argtype) { case DST_OAT_SLOT: @@ -264,44 +269,58 @@ static int64_t doarg_1(DstAssembler *a, DstOpArgType argtype, DstValue x) { } switch (x.type) { default: + goto error; break; case DST_INTEGER: - return x.as.integer; + ret = x.as.integer; + break; case DST_TUPLE: { if (argtype == DST_OAT_TYPE) { - int64_t result = 0; + ret = 0; uint32_t i = 0; for (i = 0; i < dst_tuple_length(x.as.tuple); i++) { - result |= doarg_1(a, DST_OAT_SIMPLETYPE, x.as.tuple[i]); + ret |= doarg_1(a, DST_OAT_SIMPLETYPE, x.as.tuple[i]); } - return result; + } else { + goto error; } break; } case DST_SYMBOL: + case DST_STRING: { + x.type = DST_SYMBOL; if (NULL != c) { DstValue result = dst_table_get(c, x); if (result.type == DST_INTEGER) { - if (argtype == DST_OAT_LABEL) - return result.as.integer - a->bytecode_count; - return result.as.integer; + if (argtype == DST_OAT_LABEL) { + ret = result.as.integer - a->bytecode_count; + } else { + ret = result.as.integer; + } } else { dst_asm_errorv(a, dst_formatc("unknown name %q", x)); } } else if (argtype == DST_OAT_TYPE || argtype == DST_OAT_SIMPLETYPE) { int index = strsearch(x.as.string, dst_type_names); if (index != -1) { - return (int64_t) index; + ret = (int64_t) index; } else { dst_asm_errorv(a, dst_formatc("unknown type %q", x)); } + } else { + goto error; } break; } } - dst_asm_errorv(a, dst_formatc("unexpected type %t parsing instruction argument", x.type)); + if (argtype == DST_OAT_SLOT && ret >= a->def->slotcount) + a->def->slotcount = (uint32_t) ret + 1; + return ret; + + error: + dst_asm_errorv(a, dst_formatc("error parsing instruction argument %v", x)); return 0; } @@ -395,12 +414,13 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, break; } case DIT_SSI: + case DIT_SSU: { if (dst_tuple_length(argt) != 4) dst_asm_error(a, "expected 3 arguments: (op, slot, slot, integer)"); instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); instr |= doarg(a, DST_OAT_SLOT, 2, 1, 0, argt[2]); - instr |= doarg(a, DST_OAT_INTEGER, 3, 1, 1, argt[3]); + instr |= doarg(a, DST_OAT_INTEGER, 3, 1, idef->type == DIT_SSI, argt[3]); break; } case DIT_SES: @@ -410,7 +430,7 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, if (dst_tuple_length(argt) != 4) dst_asm_error(a, "expected 3 arguments: (op, slot, environment, envslot)"); instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - env = doarg(a, DST_OAT_ENVIRONMENT, 2, 1, 0, argt[2]); + env = doarg(a, DST_OAT_ENVIRONMENT, 0, 1, 0, argt[2]); instr |= env << 16; for (env += 1; env > 0; env--) { b = b->parent; @@ -519,7 +539,6 @@ static DstAssembleResult dst_asm1(DstAssembler *parent, DstAssembleOptions opts) /* Create slot aliases */ x = dst_struct_get(st, dst_wrap_symbol(dst_cstring("slots"))); if (dst_seq_view(x, &arr, &count)) { - def->slotcount = count; for (i = 0; i < count; i++) { DstValue v = arr[i]; if (v.type == DST_TUPLE) { @@ -535,13 +554,8 @@ static DstAssembleResult dst_asm1(DstAssembler *parent, DstAssembleOptions opts) dst_asm_error(&a, "slot names must be symbols or tuple of symbols"); } } - } else if (x.type == DST_INTEGER) { - def->slotcount = (uint32_t) x.as.integer; - } else { - dst_asm_error(&a, "slots must be specified"); } - /* Create environment aliases */ x = dst_struct_get(st, dst_wrap_symbol(dst_cstring("environments"))); if (dst_seq_view(x, &arr, &count)) { @@ -596,6 +610,7 @@ static DstAssembleResult dst_asm1(DstAssembler *parent, DstAssembleOptions opts) for (i = 0; i < count; ++i) { DstValue instr = arr[i]; if (instr.type == DST_STRING) { + instr.type = DST_SYMBOL; dst_table_put(&a.labels, instr, dst_wrap_integer(blength)); } else if (instr.type == DST_TUPLE) { blength++; diff --git a/core/opcodes.h b/core/opcodes.h index e2187c99..a029b183 100644 --- a/core/opcodes.h +++ b/core/opcodes.h @@ -30,9 +30,6 @@ enum DstOpCode { DOP_TYPECHECK, DOP_RETURN, DOP_RETURN_NIL, - DOP_COERCE_INTEGER, - DOP_COERCE_REAL, - DOP_COERCE_STRING, DOP_ADD_INTEGER, DOP_ADD_IMMEDIATE, DOP_ADD_REAL, @@ -61,7 +58,9 @@ enum DstOpCode { DOP_MOVE, DOP_JUMP, DOP_JUMP_IF, + DOP_JUMP_IF_NOT, DOP_GREATER_THAN, + DOP_LESS_THAN, DOP_EQUALS, DOP_COMPARE, DOP_LOAD_NIL, @@ -79,7 +78,12 @@ enum DstOpCode { DOP_TAILCALL, DOP_SYSCALL, DOP_LOAD_SYSCALL, - DOP_TRANSFER + DOP_TRANSFER, + DOP_GET, + DOP_PUT, + DOP_GET_INDEX, + DOP_PUT_INDEX, + DOP_LENGTH }; #endif diff --git a/core/parse.c b/core/parse.c index ad0af931..b8cfee0b 100644 --- a/core/parse.c +++ b/core/parse.c @@ -141,7 +141,7 @@ static int is_symbol_char(uint8_t c) { if (c >= '0' && c <= ':') return 1; if (c >= '<' && c <= '@') return 1; if (c >= '*' && c <= '/') return 1; - if (c >= '#' && c <= '&') return 1; + if (c >= '$' && c <= '&') return 1; if (c == '_') return 1; if (c == '^') return 1; if (c == '!') return 1; diff --git a/core/syscalls.c b/core/syscalls.c index 60d05ded..ad90c676 100644 --- a/core/syscalls.c +++ b/core/syscalls.c @@ -24,7 +24,7 @@ #include #include -int dst_print(DstValue *argv, uint32_t argn) { +int dst_sys_print(DstValue *argv, uint32_t argn) { uint32_t i; for (i = 0; i < argn; ++i) { uint32_t j, len; @@ -38,6 +38,91 @@ int dst_print(DstValue *argv, uint32_t argn) { return 0; } +int dst_sys_asm(DstValue *argv, uint32_t argn) { + DstAssembleOptions opts; + DstAssembleResult res; + if (argn < 1) { + dst_vm_fiber->ret = dst_cstringv("expected assembly source"); + return 1; + } + opts.source = argv[0]; + opts.parsemap = argn >= 2 ? argv[1] : dst_wrap_nil(); + opts.flags = 0; + res = dst_asm(opts); + if (res.status == DST_ASSEMBLE_OK) { + dst_vm_fiber->ret = dst_wrap_function(dst_asm_func(res)); + return 0; + } else { + dst_vm_fiber->ret = dst_wrap_string(res.result.error); + return 1; + } +} + +int dst_sys_tuple(DstValue *argv, uint32_t argn) { + dst_vm_fiber->ret = dst_wrap_tuple(dst_tuple_n(argv, argn)); + return 0; +} + +int dst_sys_array(DstValue *argv, uint32_t argn) { + DstArray *array = dst_array(argn); + array->count = argn; + memcpy(array->data, argv, argn * sizeof(DstValue)); + dst_vm_fiber->ret = dst_wrap_array(array); + return 0; +} + +int dst_sys_table(DstValue *argv, uint32_t argn) { + uint32_t i; + DstTable *table = dst_table(argn/2); + if (argn & 1) { + dst_vm_fiber->ret = dst_cstringv("expected even number of arguments"); + return 1; + } + for (i = 0; i < argn; i += 2) { + dst_table_put(table, argv[i], argv[i + 1]); + } + dst_vm_fiber->ret = dst_wrap_table(table); + return 0; +} + +int dst_sys_struct(DstValue *argv, uint32_t argn) { + uint32_t i; + DstValue *st = dst_struct_begin(argn/2); + if (argn & 1) { + dst_vm_fiber->ret = dst_cstringv("expected even number of arguments"); + return 1; + } + for (i = 0; i < argn; i += 2) { + dst_struct_put(st, argv[i], argv[i + 1]); + } + dst_vm_fiber->ret = dst_wrap_struct(dst_struct_end(st)); + return 0; +} + +int dst_sys_get(DstValue *argv, uint32_t argn) { + uint32_t i; + if (argn < 2) { + dst_vm_fiber->ret = dst_cstringv("expected at least 1 argument"); + return 1; + } + DstValue ds = argv[0]; + for (i = 1; i < argn; i++) { + const char *err = dst_try_get(ds, argv[i], &ds); + if (NULL != err) { + dst_vm_fiber->ret = dst_cstringv(err); + return 1; + } + } + dst_vm_fiber->ret = ds; + return 0; +} + DstCFunction dst_vm_syscalls[256] = { - dst_print + dst_sys_print, + dst_sys_asm, + dst_sys_tuple, + dst_sys_array, + dst_sys_struct, + dst_sys_table, + NULL }; diff --git a/core/table.c b/core/table.c index c375577a..33ec9982 100644 --- a/core/table.c +++ b/core/table.c @@ -174,8 +174,7 @@ DstValue dst_table_next(DstTable *t, DstValue key) { /* Convert table to struct */ const DstValue *dst_table_to_struct(DstTable *t) { uint32_t i; - const DstValue *st; - st = dst_struct_begin(t->count); + DstValue *st = dst_struct_begin(t->count); for (i = 0; i < t->capacity; i++) { if (t->data[i].type != DST_NIL) dst_struct_put(st, t->data[i], t->data[i + 1]); diff --git a/core/util.c b/core/util.c index 2c4bf823..f04f2685 100644 --- a/core/util.c +++ b/core/util.c @@ -102,14 +102,3 @@ double dst_integer_to_real(int64_t integer) { /* TODO - consider c undefined behavior */ return (double) integer; } - -/* Convert an index used by the capi to an absolute index */ -uint32_t dst_startrange(int64_t index, uint32_t modulo) { - if (index < 0) index += modulo; - return ((index >= 0 && index < modulo)) ? ((uint32_t) index) : 0; -} - -/* Convert an index used by the capi to an absolute index */ -uint32_t dst_endrange(int64_t index, uint32_t modulo) { - return dst_startrange(index, modulo + 1); -} diff --git a/core/value.c b/core/value.c index c213f1eb..476f3f5f 100644 --- a/core/value.c +++ b/core/value.c @@ -192,3 +192,182 @@ int dst_compare(DstValue x, DstValue y) { } return 1; } + +/* Get a value out af an associated data structure. + * Returns possible c error message, and NULL for no error. The + * useful return value is written to out on success */ +const char *dst_try_get(DstValue ds, DstValue key, DstValue *out) { + int64_t index; + DstValue ret; + switch (ds.type) { + case DST_ARRAY: + if (key.type != DST_INTEGER) return "expected integer key"; + index = key.as.integer; + if (index < 0 || index >= ds.as.array->count) + return "invalid array access"; + ret = ds.as.array->data[index]; + break; + case DST_TUPLE: + if (key.type != DST_INTEGER) return "expected integer key"; + index = key.as.integer; + if (index < 0 || index >= dst_tuple_length(ds.as.tuple)) + return "invalid tuple access"; + ret = ds.as.tuple[index]; + break; + case DST_BUFFER: + if (key.type != DST_INTEGER) return "expected integer key"; + index = key.as.integer; + if (index < 0 || index >= ds.as.buffer->count) + return "invalid buffer access"; + ret.type = DST_INTEGER; + ret.as.integer = ds.as.buffer->data[index]; + break; + case DST_STRING: + case DST_SYMBOL: + if (key.type != DST_INTEGER) return "expected integer key"; + index = key.as.integer; + if (index < 0 || index >= dst_string_length(ds.as.string)) + return "invalid string access"; + ret.type = DST_INTEGER; + ret.as.integer = ds.as.string[index]; + break; + case DST_STRUCT: + ret = dst_struct_get(ds.as.st, key); + break; + case DST_TABLE: + ret = dst_table_get(ds.as.table, key); + break; + default: + return "cannot get"; + } + *out = ret; + return NULL; +} + +/* Set a value in an associative data structure. Returns possible + * error message, and NULL if no error. */ +const char *dst_try_put(DstValue ds, DstValue key, DstValue value) { + int64_t index; + switch (ds.type) { + case DST_ARRAY: + if (key.type != DST_INTEGER) return "expected integer key"; + index = key.as.integer; + if (index < 0 || index >= ds.as.array->count) + return "invalid array access"; + ds.as.array->data[index] = value; + break; + case DST_BUFFER: + if (key.type != DST_INTEGER) return "expected integer key"; + index = key.as.integer; + if (value.type != DST_INTEGER) return "expected integer value"; + if (index < 0 || index >= ds.as.buffer->count) + return "invalid buffer access"; + ds.as.buffer->data[index] = (uint8_t) value.as.integer; + break; + case DST_TABLE: + dst_table_put(ds.as.table, key, value); + break; + default: + return "cannot set"; + } + return NULL; +} + +/* Get the next key in an associative data structure. Used for iterating through an + * associative data structure. */ +const char *dst_try_next(DstValue ds, DstValue key, DstValue *out) { + switch(ds.type) { + default: + return "expected table or struct"; + case DST_TABLE: + *out = dst_table_next(ds.as.table, key); + return NULL; + case DST_STRUCT: + *out = dst_struct_next(ds.as.st, key); + return NULL; + } +} + +/* Get the length of an object. Returns errors for invalid types */ +uint32_t dst_length(DstValue x) { + switch (x.type) { + default: + return 0; + case DST_STRING: + return dst_string_length(x.as.string); + case DST_ARRAY: + return x.as.array->count; + case DST_BUFFER: + return x.as.buffer->count; + case DST_TUPLE: + return dst_tuple_length(x.as.tuple); + case DST_STRUCT: + return dst_struct_length(x.as.st); + case DST_TABLE: + return x.as.table->count; + } +} + +/* Get the capacity of an object. Returns 0 for invalid types */ +uint32_t dst_capacity(DstValue x) { + switch (x.type) { + default: + return 0; + case DST_STRING: + return dst_string_length(x.as.string); + case DST_ARRAY: + return x.as.array->capacity; + case DST_BUFFER: + return x.as.buffer->capacity; + case DST_TUPLE: + return dst_tuple_length(x.as.tuple); + case DST_STRUCT: + return dst_struct_length(x.as.st); + case DST_TABLE: + return x.as.table->capacity; + } +} + +/* Index into a data structure. Returns nil for out of bounds or invliad data structure */ +DstValue dst_getindex(DstValue ds, uint32_t index) { + switch (ds.type) { + default: + return dst_wrap_nil(); + case DST_STRING: + if (index >= dst_string_length(ds.as.string)) return dst_wrap_nil(); + return dst_wrap_integer(ds.as.string[index]); + case DST_ARRAY: + if (index >= ds.as.array->count) return dst_wrap_nil(); + return ds.as.array->data[index]; + case DST_BUFFER: + if (index >= ds.as.buffer->count) return dst_wrap_nil(); + return dst_wrap_integer(ds.as.buffer->data[index]); + case DST_TUPLE: + if (index >= dst_tuple_length(ds.as.tuple)) return dst_wrap_nil(); + return ds.as.tuple[index]; + } +} + +/* Set an index in a linear data structure. Does nothing if data structure + * is invalid */ +void dst_setindex(DstValue ds, DstValue value, uint32_t index) { + switch (ds.type) { + default: + return; + case DST_ARRAY: + if (index >= ds.as.array->count) { + dst_array_ensure(ds.as.array, 2 * index); + ds.as.array->count = index + 1; + } + ds.as.array->data[index] = value; + return; + case DST_BUFFER: + if (value.type != DST_INTEGER) return; + if (index >= ds.as.buffer->count) { + dst_buffer_ensure(ds.as.buffer, 2 * index); + ds.as.buffer->count = index + 1; + } + ds.as.buffer->data[index] = value.as.integer; + return; + } +} diff --git a/core/vm.c b/core/vm.c index b53caaaa..5ee1eecc 100644 --- a/core/vm.c +++ b/core/vm.c @@ -26,6 +26,29 @@ /* VM State */ DstFiber *dst_vm_fiber; +/* Helper to ensure proper fiber is activated after returning */ +static int dst_update_fiber() { + if (dst_vm_fiber->frame == 0) { + dst_vm_fiber->status = DST_FIBER_DEAD; + } + while (dst_vm_fiber->status == DST_FIBER_DEAD || + dst_vm_fiber->status == DST_FIBER_ERROR) { + if (NULL != dst_vm_fiber->parent) { + dst_vm_fiber = dst_vm_fiber->parent; + if (dst_vm_fiber->status == DST_FIBER_ALIVE) { + /* If the parent thread is still alive, + we are inside a cfunction */ + return 1; + } + } else { + /* The root thread has termiated */ + return 1; + } + } + dst_vm_fiber->status = DST_FIBER_ALIVE; + return 0; +} + /* Start running the VM from where it left off. */ int dst_continue() { @@ -122,37 +145,6 @@ int dst_continue() { dst_vm_fiber->ret.type = DST_NIL; goto vm_return; - case DOP_COERCE_INTEGER: - { - DstValue input = stack[oparg(2, 0xFFFF)]; - if (input.type == DST_INTEGER) { - stack[oparg(1, 0xFF)] = input; - } else if (input.type == DST_REAL) { - stack[oparg(1, 0xFF)] = dst_wrap_integer(dst_real_to_integer(input.as.real)); - } else { - vm_throw("expected number"); - } - continue; - } - - case DOP_COERCE_REAL: - { - DstValue input = stack[oparg(2, 0xFFFF)]; - if (input.type == DST_INTEGER) { - stack[oparg(1, 0xFF)] = dst_wrap_real(dst_integer_to_real(input.as.integer)); - } else if (input.type == DST_REAL) { - stack[oparg(1, 0xFF)] = input; - } else { - vm_throw("expected number"); - } - continue; - } - - case DOP_COERCE_STRING: - stack[oparg(1, 0xFF)] = dst_wrap_string(dst_to_string(stack[oparg(2, 0xFFFF)])); - pc++; - break; - case DOP_ADD_INTEGER: vm_binop_integer(+); @@ -269,7 +261,7 @@ int dst_continue() { case DOP_SHIFT_RIGHT_IMMEDIATE: stack[oparg(1, 0xFF)] = dst_wrap_integer( - stack[oparg(2, 0xFF)].as.uinteger >> oparg(3, 0xFF) + (int64_t)(stack[oparg(2, 0xFF)].as.uinteger >> oparg(3, 0xFF)) ); pc++; continue; @@ -277,6 +269,13 @@ int dst_continue() { case DOP_SHIFT_LEFT: vm_binop_integer(<<); + case DOP_SHIFT_LEFT_IMMEDIATE: + stack[oparg(1, 0xFF)] = dst_wrap_integer( + stack[oparg(2, 0xFF)].as.integer << oparg(3, 0xFF) + ); + pc++; + continue; + case DOP_MOVE: stack[oparg(1, 0xFF)] = stack[oparg(2, 0xFFFF)]; pc++; @@ -294,11 +293,28 @@ int dst_continue() { } continue; + case DOP_JUMP_IF_NOT: + if (dst_truthy(stack[oparg(1, 0xFF)])) { + pc++; + } else { + pc += (*(int32_t *)pc) >> 16; + } + continue; + + case DOP_LESS_THAN: + stack[oparg(1, 0xFF)].type = DST_BOOLEAN; + stack[oparg(1, 0xFF)].as.boolean = dst_compare( + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)] + ) < 0; + pc++; + continue; + case DOP_GREATER_THAN: stack[oparg(1, 0xFF)].type = DST_BOOLEAN; stack[oparg(1, 0xFF)].as.boolean = dst_compare( stack[oparg(2, 0xFF)], - stack[oparg(3 ,0xFF)] + stack[oparg(3, 0xFF)] ) > 0; pc++; continue; @@ -307,7 +323,7 @@ int dst_continue() { stack[oparg(1, 0xFF)].type = DST_BOOLEAN; stack[oparg(1, 0xFF)].as.boolean = dst_equals( stack[oparg(2, 0xFF)], - stack[oparg(3 ,0xFF)] + stack[oparg(3, 0xFF)] ); pc++; continue; @@ -316,7 +332,7 @@ int dst_continue() { stack[oparg(1, 0xFF)].type = DST_INTEGER; stack[oparg(1, 0xFF)].as.integer = dst_compare( stack[oparg(2, 0xFF)], - stack[oparg(3 ,0xFF)] + stack[oparg(3, 0xFF)] ); pc++; continue; @@ -454,14 +470,13 @@ int dst_continue() { break; } else if (callee.type == DST_CFUNCTION) { dst_fiber_cframe(dst_vm_fiber); - stack = dst_vm_fiber->data + dst_vm_fiber->frame; dst_vm_fiber->ret.type = DST_NIL; - if (callee.as.cfunction(stack, dst_vm_fiber->frametop - dst_vm_fiber->frame)) { - dst_fiber_popframe(dst_vm_fiber); + if (callee.as.cfunction( + dst_vm_fiber->data + dst_vm_fiber->frame, + dst_vm_fiber->frametop - dst_vm_fiber->frame)) { goto vm_error; } else { - dst_fiber_popframe(dst_vm_fiber); - goto vm_return; + goto vm_return_cfunc; } } else { vm_throw("cannot call non-function type"); @@ -480,14 +495,13 @@ int dst_continue() { break; } else if (callee.type == DST_CFUNCTION) { dst_fiber_cframe_tail(dst_vm_fiber); - stack = dst_vm_fiber->data + dst_vm_fiber->frame; dst_vm_fiber->ret.type = DST_NIL; - if (callee.as.cfunction(stack, dst_vm_fiber->frametop - dst_vm_fiber->frame)) { - dst_fiber_popframe(dst_vm_fiber); + if (callee.as.cfunction( + dst_vm_fiber->data + dst_vm_fiber->frame, + dst_vm_fiber->frametop - dst_vm_fiber->frame)) { goto vm_error; } else { - dst_fiber_popframe(dst_vm_fiber); - goto vm_return; + goto vm_return_cfunc; } } else { vm_throw("expected function"); @@ -500,14 +514,12 @@ int dst_continue() { DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; vm_assert(NULL != f, "invalid syscall"); dst_fiber_cframe(dst_vm_fiber); - stack = dst_vm_fiber->data + dst_vm_fiber->frame; dst_vm_fiber->ret.type = DST_NIL; - if (f(stack, dst_vm_fiber->frametop - dst_vm_fiber->frame)) { - dst_fiber_popframe(dst_vm_fiber); + if (f(dst_vm_fiber->data + dst_vm_fiber->frame, + dst_vm_fiber->frametop - dst_vm_fiber->frame)) { goto vm_error; } else { - dst_fiber_popframe(dst_vm_fiber); - goto vm_return; + goto vm_return_cfunc; } continue; } @@ -548,28 +560,66 @@ int dst_continue() { continue; } + case DOP_PUT: + { + const char *err = dst_try_put( + stack[oparg(1, 0xFF)], + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)]); + if (NULL != err) { + vm_throw(err); + } + ++pc; + } + continue; + + case DOP_PUT_INDEX: + dst_setindex( + stack[oparg(1, 0xFF)], + stack[oparg(3, 0xFF)], + oparg(3, 0xFF)); + ++pc; + continue; + + case DOP_GET: + { + const char *err = dst_try_get( + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)], + stack + oparg(1, 0xFF)); + if (NULL != err) { + vm_throw(err); + } + ++pc; + } + continue; + + case DOP_GET_INDEX: + stack[oparg(1, 0xFF)] = dst_getindex( + stack[oparg(2, 0xFF)], + oparg(3, 0xFF)); + ++pc; + continue; + + /* Return from c function. Simpler than retuning from dst function */ + vm_return_cfunc: + { + DstValue ret = dst_vm_fiber->ret; + dst_fiber_popframe(dst_vm_fiber); + if (dst_update_fiber()) + return 0; + stack[oparg(1, 0xFF)] = ret; + pc++; + continue; + } + /* Handle returning from stack frame. Expect return value in fiber->ret */ vm_return: { DstValue ret = dst_vm_fiber->ret; dst_fiber_popframe(dst_vm_fiber); - while (!dst_vm_fiber->frame || - dst_vm_fiber->status == DST_FIBER_DEAD || - dst_vm_fiber->status == DST_FIBER_ERROR) { - dst_vm_fiber->status = DST_FIBER_DEAD; - if (NULL != dst_vm_fiber->parent) { - dst_vm_fiber = dst_vm_fiber->parent; - if (dst_vm_fiber->status == DST_FIBER_ALIVE) { - /* If the parent thread is still alive, - we are inside a cfunction */ - return 0; - } - stack = dst_vm_fiber->data + dst_vm_fiber->frame; - } else { - return 0; - } - } - dst_vm_fiber->status = DST_FIBER_ALIVE; + if (dst_update_fiber()) + return 0; stack = dst_vm_fiber->data + dst_vm_fiber->frame; pc = dst_stack_frame(stack)->pc; stack[oparg(1, 0xFF)] = ret; @@ -582,19 +632,8 @@ int dst_continue() { { DstValue ret = dst_vm_fiber->ret; dst_vm_fiber->status = DST_FIBER_ERROR; - while (!dst_vm_fiber->frame || - dst_vm_fiber->status == DST_FIBER_DEAD || - dst_vm_fiber->status == DST_FIBER_ERROR) { - if (dst_vm_fiber->parent == NULL) - return 1; - dst_vm_fiber = dst_vm_fiber->parent; - if (dst_vm_fiber->status == DST_FIBER_ALIVE) { - /* If the parent thread is still alive, - we are inside a cfunction */ - return 1; - } - } - dst_vm_fiber->status = DST_FIBER_ALIVE; + if (dst_update_fiber()) + return 1; stack = dst_vm_fiber->data + dst_vm_fiber->frame; pc = dst_stack_frame(stack)->pc; stack[oparg(1, 0xFF)] = ret; diff --git a/dsts/compile.dsts b/dsts/compile.dsts new file mode 100644 index 00000000..e69de29b diff --git a/unittests/sample.dsts b/dsts/example.dsts similarity index 99% rename from unittests/sample.dsts rename to dsts/example.dsts index 31ce835b..6645beb4 100644 --- a/unittests/sample.dsts +++ b/dsts/example.dsts @@ -51,7 +51,7 @@ # from their number # Literal FuncEnvs and Functions may be possible later constants [ - "hello" + :hello (def bork 123) (def bip 456) '(1 2 3) diff --git a/dsts/minimal.dsts b/dsts/minimal.dsts new file mode 100644 index 00000000..6f47ebe3 --- /dev/null +++ b/dsts/minimal.dsts @@ -0,0 +1,24 @@ +{ + bytecode [ + (load-integer 0 10000) + (load-integer 1 0) + (load-constant 3 lookup) + + :label + (equals 2 1 0) + (jump-if 2 :done) + (push 0) + (shift-right-immediate 0 0 1) + (syscall 2 0) + (get 2 3 0) + (push3 2 3 0) + (syscall 2 0) + (jump :label) + + :done + (return-nil) + ] + constants [ + (def lookup "0123456789abcdef") + ] +} diff --git a/junkyard/ds.c b/junkyard/ds.c deleted file mode 100644 index 79fc64d7..00000000 --- a/junkyard/ds.c +++ /dev/null @@ -1,285 +0,0 @@ -/* -* Copyright (c) 2017 Calvin Rose -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to -* deal in the Software without restriction, including without limitation the -* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -* sell copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -* IN THE SOFTWARE. -*/ - -#include "cache.h" -#include - -/* Get a value out af an associated data structure. - * Returns possible c error message, and NULL for no error. The - * useful return value is written to out on success */ -const char *dst_value_get(DstValue ds, DstValue key, DstValue *out) { - int64_t index; - DstValue ret; - switch (ds.type) { - case DST_ARRAY: - if (key.type != DST_INTEGER) return "expected integer key"; - index = dst_startrange(key.as.integer, ds.as.array->count); - if (index < 0) return "invalid array access"; - ret = ds.as.array->data[index]; - break; - case DST_TUPLE: - if (key.type != DST_INTEGER) return "expected integer key"; - index = dst_startrange(key.as.integer, dst_tuple_length(ds.as.tuple)); - if (index < 0) return "invalid tuple access"; - ret = ds.as.tuple[index]; - break; - case DST_BUFFER: - if (key.type != DST_INTEGER) return "expected integer key"; - index = dst_startrange(key.as.integer, ds.as.buffer->count); - if (index < 0) return "invalid buffer access"; - ret.type = DST_INTEGER; - ret.as.integer = ds.as.buffer->data[index]; - break; - case DST_STRING: - case DST_SYMBOL: - if (key.type != DST_INTEGER) return "expected integer key"; - index = dst_startrange(key.as.integer, dst_string_length(ds.as.string)); - if (index < 0) return "invalid string access"; - ret.type = DST_INTEGER; - ret.as.integer = ds.as.string[index]; - break; - case DST_STRUCT: - ret = dst_struct_get(ds.as.st, key); - break; - case DST_TABLE: - ret = dst_table_get(ds.as.table, key); - break; - default: - return "cannot get"; - } - *out = ret; - return NULL; -} - -int dst_get(Dst *vm) { - DstValue ds = dst_popv(vm); - DstValue key = dst_popv(vm); - DstValue ret; - const char *err = dst_value_get(ds, key, &ret); - if (err) { - vm->flags = 1; - dst_cstring(vm, err); - } else { - dst_pushv(vm, ret); - } -} - -/* Set a value in an associative data structure. Returns possible - * error message, and NULL if no error. */ -const char *dst_value_set(Dst *vm, DstValue ds, DstValue key, DstValue value) { - int64_t index; - switch (ds.type) { - case DST_ARRAY: - if (key.type != DST_INTEGER) return "expected integer key"; - index = dst_startrange(key.as.integer, ds.as.array->count); - if (index < 0) return "invalid array access"; - ds.as.array->data[index] = value; - break; - case DST_BUFFER: - if (key.type != DST_INTEGER) return "expected integer key"; - if (value.type != DST_INTEGER) return "expected integer value"; - index = dst_startrange(key.as.integer, ds.as.buffer->count); - if (index < 0) return "invalid buffer access"; - ds.as.buffer->data[index] = (uint8_t) value.as.integer; - break; - case DST_TABLE: - dst_table_put(vm, ds.as.table, key, value); - break; - default: - return "cannot set"; - } - return NULL; -} - -void dst_set(Dst *vm) { - DstValue ds = dst_popv(vm); - DstValue key = dst_popv(vm); - DstValue value = dst_popv(vm); - const char *err = dst_value_set(vm, ds, key, value); - if (err) { - vm->flags = 1; - vm->ret = dst_string_cv(vm, err); - } -} - -/* Get the next key in an associative data structure. Used for iterating through an - * associative data structure. */ -int dst_next(Dst *vm) { - DstValue dsv = dst_popv(vm); - DstValue keyv = dst_popv(vm); - DstValue ret = keyv; - switch(dsv.type) { - default: - dst_cerr(vm, "expected table or struct"); - return 0; - case DST_TABLE: - ret = dst_table_next(dsv.as.table, keyv); - break; - case DST_STRUCT: - ret = dst_struct_next(dsv.as.st, keyv); - break; - } - dst_pushv(vm, ret); - return ret.type != DST_NIL; -} - -/* Ensure capacity in a datastructure */ -void dst_ensure(Dst *vm, int64_t index, uint32_t capacity) { - DstValue x = dst_getv(vm, index); - switch (x.type) { - default: - dst_cerr(vm, "could not ensure capacity"); - break; - case DST_ARRAY: - dst_array_ensure_(vm, x.as.array, capacity); - break; - case DST_BUFFER: - dst_buffer_ensure_(vm, x.as.buffer, capacity); - break; - } -} - -/* Get the length of an object. Returns errors for invalid types */ -uint32_t dst_length(Dst *vm, int64_t index) { - DstValue x = dst_getv(vm, index); - uint32_t length; - switch (x.type) { - default: - dst_cerr(vm, "cannot get length"); - return 0; - case DST_STRING: - length = dst_string_length(x.as.string); - break; - case DST_ARRAY: - length = x.as.array->count; - break; - case DST_BUFFER: - length = x.as.buffer->count; - break; - case DST_TUPLE: - length = dst_tuple_length(x.as.tuple); - break; - case DST_STRUCT: - length = dst_struct_length(x.as.st); - break; - case DST_TABLE: - length = x.as.table->count; - break; - } - return length; -} - -/* Get the capacity of an object. Returns errors for invalid types */ -uint32_t dst_capacity(Dst *vm, int64_t index) { - DstValue x = dst_getv(vm, index); - uint32_t cap; - switch (x.type) { - default: - dst_cerr(vm, "cannot get capacity"); - return 0; - case DST_STRING: - cap = dst_string_length(x.as.string); - break; - case DST_ARRAY: - cap = x.as.array->capacity; - break; - case DST_BUFFER: - cap = x.as.buffer->capacity; - break; - case DST_TUPLE: - cap = dst_tuple_length(x.as.tuple); - break; - case DST_STRUCT: - cap = dst_struct_length(x.as.st); - break; - case DST_TABLE: - cap = x.as.table->capacity; - break; - } - return cap; -} - -/* Sequence functions */ -const char *dst_getindex_value(DstValue ds, uint32_t index, DstValue *out) { - switch (ds.type) { - default: - return "expected sequence type"; - case DST_STRING: - if (index >= dst_string_length(ds.as.string)) return "index out of bounds"; - *out = dst_wrap_integer(ds.as.string[index]); - break; - case DST_ARRAY: - if (index >= ds.as.array->count) return "index out of bounds"; - *out = ds.as.array->data[index]; - break; - case DST_BUFFER: - if (index >= ds.as.buffer->count) return "index out of bounds"; - *out = dst_wrap_integer(ds.as.buffer->data[index]); - break; - case DST_TUPLE: - if (index >= dst_tuple_length(ds.as.tuple)) return "index out of bounds"; - *out = ds.as.tuple[index]; - break; - } - return NULL; -} -void dst_getindex(Dst *vm, uint32_t index) { - DstValue out; - DstValue ds = dst_popv(vm); - const char *e = dst_getindex_value(ds, index, &out); - if (e == NULL) { - dst_pushv(vm, out); - } else { - dst_cstring(vm, e); - vm->flags = 1; - } -} - -const char *dst_setindex_value(Dst *vm, DstValue ds, uint32_t index, DstValue value) { - switch (ds.type) { - default: - return "expected mutable sequence type"; - case DST_ARRAY: - if (index >= ds.as.array->count) { - dst_array_ensure_(vm, ds.as.array, index + 1); - } - ds.as.array->data[index] = value; - break; - case DST_BUFFER: - if (value.type != DST_INTEGER) return "expected integer type"; - if (index >= ds.as.buffer->count) { - dst_buffer_ensure_(vm, ds.as.buffer, index + 1); - } - ds.as.buffer->data[index] = value.as.integer; - break; - } - return NULL; -} -void dst_setindex(Dst *vm, uint32_t index) { - DstValue ds = dst_popv(vm); - DstValue value = dst_popv(vm); - const char *e = dst_setindex_value(vm, ds, index, value); - if (e != NULL) { - dst_cstring(vm, e); - vm->flags = 1; - } -} diff --git a/unittests/asm_test.c b/unittests/asm_test.c index ed8d9bd1..644af480 100644 --- a/unittests/asm_test.c +++ b/unittests/asm_test.c @@ -7,7 +7,7 @@ int main() { DstAssembleResult ares; DstFunction *func; - FILE *f = fopen("./unittests/sample.dsts", "rb"); + FILE *f = fopen("./dsts/minimal.dsts", "rb"); fseek(f, 0, SEEK_END); long fsize = ftell(f); fseek(f, 0, SEEK_SET); //same as rewind(f);