2018-01-12 15:41:27 +00:00
|
|
|
/*
|
2018-05-18 03:41:20 +00:00
|
|
|
* Copyright (c) 2018 Calvin Rose
|
2018-01-12 15:41:27 +00:00
|
|
|
*
|
|
|
|
* 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 <dst/dst.h>
|
|
|
|
#include "compile.h"
|
2018-07-04 03:07:35 +00:00
|
|
|
#include "util.h"
|
|
|
|
#include "vector.h"
|
2018-07-01 15:52:15 +00:00
|
|
|
#include "emit.h"
|
2018-01-12 15:41:27 +00:00
|
|
|
|
2018-07-12 01:29:39 +00:00
|
|
|
static DstSlot dstc_quote(DstFopts opts, int32_t argn, const Dst *argv) {
|
2018-01-12 15:41:27 +00:00
|
|
|
if (argn != 1) {
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_cerror(opts.compiler, "expected 1 argument");
|
2018-01-27 20:15:09 +00:00
|
|
|
return dstc_cslot(dst_wrap_nil());
|
|
|
|
}
|
|
|
|
return dstc_cslot(argv[0]);
|
|
|
|
}
|
|
|
|
|
2018-07-01 15:52:15 +00:00
|
|
|
/* Preform destructuring. Be careful to
|
2018-07-04 03:07:35 +00:00
|
|
|
* keep the order registers are freed.
|
|
|
|
* Returns if the slot 'right' can be freed. */
|
|
|
|
static int destructure(DstCompiler *c,
|
2018-07-01 15:52:15 +00:00
|
|
|
Dst left,
|
|
|
|
DstSlot right,
|
2018-07-04 03:07:35 +00:00
|
|
|
int (*leaf)(DstCompiler *c,
|
2018-02-06 06:25:48 +00:00
|
|
|
const uint8_t *sym,
|
|
|
|
DstSlot s,
|
2018-03-12 06:06:51 +00:00
|
|
|
DstTable *attr),
|
|
|
|
DstTable *attr) {
|
2018-02-06 06:25:48 +00:00
|
|
|
switch (dst_type(left)) {
|
|
|
|
default:
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_cerror(c, "unexpected type in destructuring");
|
2018-07-04 03:07:35 +00:00
|
|
|
return 1;
|
2018-02-06 06:25:48 +00:00
|
|
|
case DST_SYMBOL:
|
|
|
|
/* Leaf, assign right to left */
|
2018-07-04 03:07:35 +00:00
|
|
|
return leaf(c, dst_unwrap_symbol(left), right, attr);
|
2018-02-06 06:25:48 +00:00
|
|
|
case DST_TUPLE:
|
|
|
|
case DST_ARRAY:
|
|
|
|
{
|
2018-07-04 03:07:35 +00:00
|
|
|
int32_t i, len;
|
2018-06-17 17:55:02 +00:00
|
|
|
const Dst *values;
|
2018-07-04 17:15:52 +00:00
|
|
|
dst_indexed_view(left, &values, &len);
|
2018-02-06 06:25:48 +00:00
|
|
|
for (i = 0; i < len; i++) {
|
2018-07-04 03:07:35 +00:00
|
|
|
DstSlot nextright = dstc_farslot(c);
|
2018-06-17 17:55:02 +00:00
|
|
|
Dst subval = values[i];
|
2018-02-06 06:25:48 +00:00
|
|
|
if (i < 0x100) {
|
2018-07-04 03:07:35 +00:00
|
|
|
dstc_emit_ssu(c, DOP_GET_INDEX, nextright, right, (uint8_t) i, 1);
|
2018-02-06 06:25:48 +00:00
|
|
|
} else {
|
2018-07-04 03:07:35 +00:00
|
|
|
DstSlot k = dstc_cslot(dst_wrap_integer(i));
|
|
|
|
dstc_emit_sss(c, DOP_GET, nextright, right, k, 1);
|
2018-02-06 06:25:48 +00:00
|
|
|
}
|
2018-07-04 03:07:35 +00:00
|
|
|
if (destructure(c, subval, nextright, leaf, attr))
|
|
|
|
dstc_freeslot(c, nextright);
|
2018-02-06 06:25:48 +00:00
|
|
|
}
|
|
|
|
}
|
2018-07-04 03:07:35 +00:00
|
|
|
return 1;
|
2018-02-06 07:02:28 +00:00
|
|
|
case DST_TABLE:
|
|
|
|
case DST_STRUCT:
|
|
|
|
{
|
2018-07-04 17:15:52 +00:00
|
|
|
const DstKV *kvs = NULL;
|
|
|
|
int32_t i, cap, len;
|
|
|
|
dst_dictionary_view(left, &kvs, &len, &cap);
|
|
|
|
for (i = 0; i < cap; i++) {
|
|
|
|
if (dst_checktype(kvs[i].key, DST_NIL)) continue;
|
2018-07-04 03:07:35 +00:00
|
|
|
DstSlot nextright = dstc_farslot(c);
|
2018-07-04 17:15:52 +00:00
|
|
|
DstSlot k = dstc_value(dstc_fopts_default(c), kvs[i].key);
|
2018-07-04 03:07:35 +00:00
|
|
|
dstc_emit_sss(c, DOP_GET, nextright, right, k, 1);
|
2018-07-04 17:15:52 +00:00
|
|
|
if (destructure(c, kvs[i].value, nextright, leaf, attr))
|
2018-07-04 03:07:35 +00:00
|
|
|
dstc_freeslot(c, nextright);
|
2018-02-06 07:02:28 +00:00
|
|
|
}
|
|
|
|
}
|
2018-07-04 03:07:35 +00:00
|
|
|
return 1;
|
2018-02-06 06:25:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-12 01:29:39 +00:00
|
|
|
static DstSlot dstc_varset(DstFopts opts, int32_t argn, const Dst *argv) {
|
2018-01-17 04:18:45 +00:00
|
|
|
DstFopts subopts = dstc_fopts_default(opts.compiler);
|
2018-01-13 21:14:40 +00:00
|
|
|
DstSlot ret, dest;
|
2018-01-17 04:18:45 +00:00
|
|
|
Dst head;
|
2018-01-12 15:41:27 +00:00
|
|
|
if (argn != 2) {
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_cerror(opts.compiler, "expected 2 arguments");
|
2018-01-12 15:41:27 +00:00
|
|
|
return dstc_cslot(dst_wrap_nil());
|
|
|
|
}
|
2018-06-29 03:36:31 +00:00
|
|
|
head = argv[0];
|
2018-01-17 04:18:45 +00:00
|
|
|
if (!dst_checktype(head, DST_SYMBOL)) {
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_cerror(opts.compiler, "expected symbol");
|
2018-01-12 15:41:27 +00:00
|
|
|
return dstc_cslot(dst_wrap_nil());
|
|
|
|
}
|
2018-06-29 03:36:31 +00:00
|
|
|
dest = dstc_resolve(opts.compiler, dst_unwrap_symbol(head));
|
2018-01-13 21:14:40 +00:00
|
|
|
if (!(dest.flags & DST_SLOT_MUTABLE)) {
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_cerror(opts.compiler, "cannot set constant");
|
2018-01-13 21:14:40 +00:00
|
|
|
return dstc_cslot(dst_wrap_nil());
|
|
|
|
}
|
|
|
|
subopts.flags = DST_FOPTS_HINT;
|
|
|
|
subopts.hint = dest;
|
2018-01-17 04:18:45 +00:00
|
|
|
ret = dstc_value(subopts, argv[1]);
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_copy(opts.compiler, dest, ret);
|
2018-01-13 21:14:40 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add attributes to a global def or var table */
|
2018-03-12 06:06:51 +00:00
|
|
|
static DstTable *handleattr(DstCompiler *c, int32_t argn, const Dst *argv) {
|
2018-01-13 21:14:40 +00:00
|
|
|
int32_t i;
|
2018-03-12 06:06:51 +00:00
|
|
|
DstTable *tab = dst_table(2);
|
2018-01-13 21:14:40 +00:00
|
|
|
for (i = 1; i < argn - 1; i++) {
|
2018-06-29 03:36:31 +00:00
|
|
|
Dst attr = argv[i];
|
2018-01-13 21:14:40 +00:00
|
|
|
switch (dst_type(attr)) {
|
|
|
|
default:
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_cerror(c, "could not add metadata to binding");
|
2018-03-12 06:06:51 +00:00
|
|
|
break;
|
2018-01-13 21:14:40 +00:00
|
|
|
case DST_SYMBOL:
|
|
|
|
dst_table_put(tab, attr, dst_wrap_true());
|
|
|
|
break;
|
|
|
|
case DST_STRING:
|
|
|
|
dst_table_put(tab, dst_csymbolv("doc"), attr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-03-12 06:06:51 +00:00
|
|
|
return tab;
|
2018-01-13 21:14:40 +00:00
|
|
|
}
|
|
|
|
|
2018-06-29 03:36:31 +00:00
|
|
|
static DstSlot dohead(DstCompiler *c, DstFopts opts, Dst *head, int32_t argn, const Dst *argv) {
|
2018-01-17 04:18:45 +00:00
|
|
|
DstFopts subopts = dstc_fopts_default(c);
|
2018-01-13 21:14:40 +00:00
|
|
|
DstSlot ret;
|
|
|
|
if (argn < 2) {
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_cerror(c, "expected at least 2 arguments");
|
2018-01-13 21:14:40 +00:00
|
|
|
return dstc_cslot(dst_wrap_nil());
|
|
|
|
}
|
2018-06-29 03:36:31 +00:00
|
|
|
*head = argv[0];
|
2018-01-21 19:39:32 +00:00
|
|
|
subopts.flags = opts.flags & ~(DST_FOPTS_TAIL | DST_FOPTS_DROP);
|
2018-01-17 04:18:45 +00:00
|
|
|
subopts.hint = opts.hint;
|
|
|
|
ret = dstc_value(subopts, argv[argn - 1]);
|
2018-01-21 19:39:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Def or var a symbol in a local scope */
|
2018-07-04 03:07:35 +00:00
|
|
|
static int namelocal(DstCompiler *c, const uint8_t *head, int32_t flags, DstSlot ret) {
|
|
|
|
int isUnnamedRegister = !(ret.flags & DST_SLOT_NAMED) &&
|
|
|
|
ret.index > 0 &&
|
|
|
|
ret.envindex >= 0;
|
|
|
|
if (!isUnnamedRegister) {
|
2018-01-21 19:39:32 +00:00
|
|
|
/* Slot is not able to be named */
|
2018-07-04 03:07:35 +00:00
|
|
|
DstSlot localslot = dstc_farslot(c);
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_copy(c, localslot, ret);
|
2018-01-21 19:39:32 +00:00
|
|
|
ret = localslot;
|
|
|
|
}
|
2018-02-03 22:22:04 +00:00
|
|
|
ret.flags |= flags;
|
2018-07-04 03:07:35 +00:00
|
|
|
dstc_nameslot(c, head, ret);
|
|
|
|
return !isUnnamedRegister;
|
2018-01-21 19:39:32 +00:00
|
|
|
}
|
|
|
|
|
2018-07-04 03:07:35 +00:00
|
|
|
static int varleaf(
|
2018-02-06 06:25:48 +00:00
|
|
|
DstCompiler *c,
|
|
|
|
const uint8_t *sym,
|
|
|
|
DstSlot s,
|
2018-03-12 06:06:51 +00:00
|
|
|
DstTable *attr) {
|
2018-07-01 15:52:15 +00:00
|
|
|
if (c->scope->flags & DST_SCOPE_TOP) {
|
2018-01-12 15:41:27 +00:00
|
|
|
/* Global var, generate var */
|
2018-07-01 15:52:15 +00:00
|
|
|
DstSlot refslot;
|
2018-01-12 15:41:27 +00:00
|
|
|
DstTable *reftab = dst_table(1);
|
2018-03-12 06:06:51 +00:00
|
|
|
reftab->proto = attr;
|
2018-01-12 15:41:27 +00:00
|
|
|
DstArray *ref = dst_array(1);
|
|
|
|
dst_array_push(ref, dst_wrap_nil());
|
2018-03-18 18:01:58 +00:00
|
|
|
dst_table_put(reftab, dst_csymbolv(":ref"), dst_wrap_array(ref));
|
2018-02-06 06:25:48 +00:00
|
|
|
dst_table_put(c->env, dst_wrap_symbol(sym), dst_wrap_table(reftab));
|
2018-01-12 15:41:27 +00:00
|
|
|
refslot = dstc_cslot(dst_wrap_array(ref));
|
2018-07-04 03:07:35 +00:00
|
|
|
dstc_emit_ssu(c, DOP_PUT_INDEX, refslot, s, 0, 0);
|
|
|
|
return 1;
|
2018-01-12 15:41:27 +00:00
|
|
|
} else {
|
2018-07-04 03:07:35 +00:00
|
|
|
return namelocal(c, sym, DST_SLOT_MUTABLE, s);
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-12 01:29:39 +00:00
|
|
|
static DstSlot dstc_var(DstFopts opts, int32_t argn, const Dst *argv) {
|
2018-01-12 15:41:27 +00:00
|
|
|
DstCompiler *c = opts.compiler;
|
2018-01-17 04:18:45 +00:00
|
|
|
Dst head;
|
2018-06-29 03:36:31 +00:00
|
|
|
DstSlot ret = dohead(c, opts, &head, argn, argv);
|
2018-07-04 17:15:52 +00:00
|
|
|
if (c->result.status == DST_COMPILE_ERROR)
|
|
|
|
return dstc_cslot(dst_wrap_nil());
|
2018-07-04 03:07:35 +00:00
|
|
|
if (destructure(c, argv[0], ret, varleaf, handleattr(c, argn, argv)))
|
|
|
|
dstc_freeslot(c, ret);
|
2018-02-06 06:25:48 +00:00
|
|
|
return dstc_cslot(dst_wrap_nil());
|
|
|
|
}
|
|
|
|
|
2018-07-04 03:07:35 +00:00
|
|
|
static int defleaf(
|
2018-02-06 06:25:48 +00:00
|
|
|
DstCompiler *c,
|
|
|
|
const uint8_t *sym,
|
|
|
|
DstSlot s,
|
2018-03-12 06:06:51 +00:00
|
|
|
DstTable *attr) {
|
2018-07-01 15:52:15 +00:00
|
|
|
if (c->scope->flags & DST_SCOPE_TOP) {
|
2018-01-13 21:14:40 +00:00
|
|
|
DstTable *tab = dst_table(2);
|
2018-03-12 06:06:51 +00:00
|
|
|
tab->proto = attr;
|
2018-03-18 18:01:58 +00:00
|
|
|
DstSlot valsym = dstc_cslot(dst_csymbolv(":value"));
|
2018-01-13 21:14:40 +00:00
|
|
|
DstSlot tabslot = dstc_cslot(dst_wrap_table(tab));
|
|
|
|
|
2018-01-12 15:41:27 +00:00
|
|
|
/* Add env entry to env */
|
2018-02-06 06:25:48 +00:00
|
|
|
dst_table_put(c->env, dst_wrap_symbol(sym), dst_wrap_table(tab));
|
2018-01-13 21:14:40 +00:00
|
|
|
|
|
|
|
/* Put value in table when evaulated */
|
2018-07-04 03:07:35 +00:00
|
|
|
dstc_emit_sss(c, DOP_PUT, tabslot, valsym, s, 0);
|
|
|
|
return 1;
|
2018-01-12 15:41:27 +00:00
|
|
|
} else {
|
2018-07-04 03:07:35 +00:00
|
|
|
return namelocal(c, sym, 0, s);
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
2018-02-06 06:25:48 +00:00
|
|
|
}
|
|
|
|
|
2018-07-12 01:29:39 +00:00
|
|
|
static DstSlot dstc_def(DstFopts opts, int32_t argn, const Dst *argv) {
|
2018-02-06 06:25:48 +00:00
|
|
|
DstCompiler *c = opts.compiler;
|
|
|
|
Dst head;
|
|
|
|
opts.flags &= ~DST_FOPTS_HINT;
|
2018-06-29 03:36:31 +00:00
|
|
|
DstSlot ret = dohead(c, opts, &head, argn, argv);
|
2018-07-04 17:15:52 +00:00
|
|
|
if (c->result.status == DST_COMPILE_ERROR)
|
|
|
|
return dstc_cslot(dst_wrap_nil());
|
2018-07-04 03:07:35 +00:00
|
|
|
if (destructure(c, argv[0], ret, defleaf, handleattr(c, argn, argv)))
|
|
|
|
dstc_freeslot(c, ret);
|
2018-02-06 06:25:48 +00:00
|
|
|
return dstc_cslot(dst_wrap_nil());
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* :condition
|
|
|
|
* ...
|
|
|
|
* jump-if-not condition :right
|
|
|
|
* :left
|
|
|
|
* ...
|
|
|
|
* jump done (only if not tail)
|
|
|
|
* :right
|
|
|
|
* ...
|
|
|
|
* :done
|
|
|
|
*/
|
2018-07-12 01:29:39 +00:00
|
|
|
static DstSlot dstc_if(DstFopts opts, int32_t argn, const Dst *argv) {
|
2018-01-12 15:41:27 +00:00
|
|
|
DstCompiler *c = opts.compiler;
|
2018-07-01 15:52:15 +00:00
|
|
|
int32_t labelr, labeljr, labeld, labeljd;
|
2018-01-17 04:18:45 +00:00
|
|
|
DstFopts condopts, bodyopts;
|
2018-01-12 15:41:27 +00:00
|
|
|
DstSlot cond, left, right, target;
|
2018-01-17 04:18:45 +00:00
|
|
|
Dst truebody, falsebody;
|
2018-07-12 01:29:39 +00:00
|
|
|
DstScope condscope, tempscope;
|
2018-01-12 15:41:27 +00:00
|
|
|
const int tail = opts.flags & DST_FOPTS_TAIL;
|
|
|
|
const int drop = opts.flags & DST_FOPTS_DROP;
|
|
|
|
|
|
|
|
if (argn < 2 || argn > 3) {
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_cerror(c, "expected 2 or 3 arguments to if");
|
2018-01-12 15:41:27 +00:00
|
|
|
return dstc_cslot(dst_wrap_nil());
|
|
|
|
}
|
|
|
|
|
2018-01-17 04:18:45 +00:00
|
|
|
/* Get the bodies of the if expression */
|
|
|
|
truebody = argv[1];
|
|
|
|
falsebody = argn > 2 ? argv[2] : dst_wrap_nil();
|
|
|
|
|
2018-01-12 15:41:27 +00:00
|
|
|
/* Get options */
|
2018-01-17 04:18:45 +00:00
|
|
|
condopts = dstc_fopts_default(c);
|
|
|
|
bodyopts = opts;
|
2018-01-12 15:41:27 +00:00
|
|
|
|
2018-07-12 01:29:39 +00:00
|
|
|
/* Set target for compilation */
|
|
|
|
target = (drop || tail)
|
|
|
|
? dstc_cslot(dst_wrap_nil())
|
|
|
|
: dstc_gettarget(opts);
|
|
|
|
|
2018-01-12 15:41:27 +00:00
|
|
|
/* Compile condition */
|
2018-07-12 01:29:39 +00:00
|
|
|
dstc_scope(&condscope, c, 0, "if");
|
2018-01-17 04:18:45 +00:00
|
|
|
cond = dstc_value(condopts, argv[0]);
|
2018-01-12 15:41:27 +00:00
|
|
|
|
|
|
|
/* Check constant condition. */
|
|
|
|
/* TODO: Use type info for more short circuits */
|
2018-01-21 19:39:32 +00:00
|
|
|
if (cond.flags & DST_SLOT_CONSTANT) {
|
2018-01-17 04:18:45 +00:00
|
|
|
if (!dst_truthy(cond.constant)) {
|
|
|
|
/* Swap the true and false bodies */
|
|
|
|
Dst temp = falsebody;
|
|
|
|
falsebody = truebody;
|
|
|
|
truebody = temp;
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
2018-07-01 15:52:15 +00:00
|
|
|
dstc_scope(&tempscope, c, 0, "if-body");
|
2018-01-17 04:18:45 +00:00
|
|
|
target = dstc_value(bodyopts, truebody);
|
2018-01-12 15:41:27 +00:00
|
|
|
dstc_popscope(c);
|
2018-07-12 01:29:39 +00:00
|
|
|
dstc_popscope(c);
|
2018-01-17 04:18:45 +00:00
|
|
|
dstc_throwaway(bodyopts, falsebody);
|
2018-01-12 15:41:27 +00:00
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compile jump to right */
|
2018-07-04 03:07:35 +00:00
|
|
|
labeljr = dstc_emit_si(c, DOP_JUMP_IF_NOT, cond, 0, 0);
|
2018-01-12 15:41:27 +00:00
|
|
|
|
|
|
|
/* Condition left body */
|
2018-07-01 15:52:15 +00:00
|
|
|
dstc_scope(&tempscope, c, 0, "if-true");
|
2018-01-17 04:18:45 +00:00
|
|
|
left = dstc_value(bodyopts, truebody);
|
2018-06-29 03:36:31 +00:00
|
|
|
if (!drop && !tail) dstc_copy(c, target, left);
|
2018-01-12 15:41:27 +00:00
|
|
|
dstc_popscope(c);
|
|
|
|
|
|
|
|
/* Compile jump to done */
|
|
|
|
labeljd = dst_v_count(c->buffer);
|
2018-06-29 03:36:31 +00:00
|
|
|
if (!tail) dstc_emit(c, DOP_JUMP);
|
2018-01-12 15:41:27 +00:00
|
|
|
|
|
|
|
/* Compile right body */
|
|
|
|
labelr = dst_v_count(c->buffer);
|
2018-07-01 15:52:15 +00:00
|
|
|
dstc_scope(&tempscope, c, 0, "if-false");
|
2018-01-17 04:18:45 +00:00
|
|
|
right = dstc_value(bodyopts, falsebody);
|
2018-06-29 03:36:31 +00:00
|
|
|
if (!drop && !tail) dstc_copy(c, target, right);
|
2018-01-12 15:41:27 +00:00
|
|
|
dstc_popscope(c);
|
|
|
|
|
2018-07-12 01:29:39 +00:00
|
|
|
/* Pop main scope */
|
|
|
|
dstc_popscope(c);
|
|
|
|
|
2018-01-12 15:41:27 +00:00
|
|
|
/* Write jumps - only add jump lengths if jump actually emitted */
|
|
|
|
labeld = dst_v_count(c->buffer);
|
|
|
|
c->buffer[labeljr] |= (labelr - labeljr) << 16;
|
|
|
|
if (!tail) c->buffer[labeljd] |= (labeld - labeljd) << 8;
|
|
|
|
|
|
|
|
if (tail) target.flags |= DST_SLOT_RETURNED;
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compile a do form. Do forms execute their body sequentially and
|
|
|
|
* evaluate to the last expression in the body. */
|
2018-07-12 01:29:39 +00:00
|
|
|
static DstSlot dstc_do(DstFopts opts, int32_t argn, const Dst *argv) {
|
2018-01-12 15:41:27 +00:00
|
|
|
int32_t i;
|
2018-03-19 18:51:18 +00:00
|
|
|
DstSlot ret = dstc_cslot(dst_wrap_nil());
|
2018-01-17 04:18:45 +00:00
|
|
|
DstCompiler *c = opts.compiler;
|
|
|
|
DstFopts subopts = dstc_fopts_default(c);
|
2018-07-01 15:52:15 +00:00
|
|
|
DstScope tempscope;
|
|
|
|
dstc_scope(&tempscope, c, 0, "do");
|
2018-01-12 15:41:27 +00:00
|
|
|
for (i = 0; i < argn; i++) {
|
|
|
|
if (i != argn - 1) {
|
|
|
|
subopts.flags = DST_FOPTS_DROP;
|
2018-03-19 18:51:18 +00:00
|
|
|
} else {
|
|
|
|
subopts = opts;
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
2018-01-17 04:18:45 +00:00
|
|
|
ret = dstc_value(subopts, argv[i]);
|
2018-01-12 15:41:27 +00:00
|
|
|
if (i != argn - 1) {
|
2018-01-17 04:18:45 +00:00
|
|
|
dstc_freeslot(c, ret);
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
}
|
2018-01-17 04:18:45 +00:00
|
|
|
dstc_popscope_keepslot(c, ret);
|
2018-01-12 15:41:27 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-07-12 01:29:39 +00:00
|
|
|
/* Add a funcdef to the top most function scope */
|
|
|
|
static int32_t dstc_addfuncdef(DstCompiler *c, DstFuncDef *def) {
|
|
|
|
DstScope *scope = c->scope;
|
|
|
|
while (scope) {
|
|
|
|
if (scope->flags & DST_SCOPE_FUNCTION)
|
|
|
|
break;
|
|
|
|
scope = scope->parent;
|
|
|
|
}
|
|
|
|
dst_assert(scope, "could not add funcdef");
|
|
|
|
dst_v_push(scope->defs, def);
|
|
|
|
return dst_v_count(scope->defs) - 1;
|
|
|
|
}
|
|
|
|
|
2018-01-12 15:41:27 +00:00
|
|
|
/*
|
|
|
|
* :whiletop
|
|
|
|
* ...
|
|
|
|
* :condition
|
|
|
|
* jump-if-not cond :done
|
|
|
|
* ...
|
|
|
|
* jump :whiletop
|
|
|
|
* :done
|
|
|
|
*/
|
2018-07-12 01:29:39 +00:00
|
|
|
static DstSlot dstc_while(DstFopts opts, int32_t argn, const Dst *argv) {
|
2018-01-12 15:41:27 +00:00
|
|
|
DstCompiler *c = opts.compiler;
|
|
|
|
DstSlot cond;
|
2018-01-17 04:18:45 +00:00
|
|
|
DstFopts subopts = dstc_fopts_default(c);
|
2018-07-01 15:52:15 +00:00
|
|
|
DstScope tempscope;
|
|
|
|
int32_t labelwt, labeld, labeljt, labelc, i;
|
2018-01-12 15:41:27 +00:00
|
|
|
int infinite = 0;
|
|
|
|
|
|
|
|
if (argn < 2) {
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_cerror(c, "expected at least 2 arguments");
|
2018-01-12 15:41:27 +00:00
|
|
|
return dstc_cslot(dst_wrap_nil());
|
|
|
|
}
|
|
|
|
|
|
|
|
labelwt = dst_v_count(c->buffer);
|
|
|
|
|
2018-07-12 01:29:39 +00:00
|
|
|
dstc_scope(&tempscope, c, 0, "while");
|
|
|
|
|
2018-01-12 15:41:27 +00:00
|
|
|
/* Compile condition */
|
2018-01-17 04:18:45 +00:00
|
|
|
cond = dstc_value(subopts, argv[0]);
|
2018-01-12 15:41:27 +00:00
|
|
|
|
|
|
|
/* Check for constant condition */
|
|
|
|
if (cond.flags & DST_SLOT_CONSTANT) {
|
|
|
|
/* Loop never executes */
|
|
|
|
if (!dst_truthy(cond.constant)) {
|
2018-07-12 01:29:39 +00:00
|
|
|
dstc_popscope(c);
|
2018-01-12 15:41:27 +00:00
|
|
|
return dstc_cslot(dst_wrap_nil());
|
|
|
|
}
|
|
|
|
/* Infinite loop */
|
|
|
|
infinite = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Infinite loop does not need to check condition */
|
2018-07-12 01:29:39 +00:00
|
|
|
labelc = infinite
|
|
|
|
? 0
|
|
|
|
: dstc_emit_si(c, DOP_JUMP_IF_NOT, cond, 0, 0);
|
2018-01-12 15:41:27 +00:00
|
|
|
|
|
|
|
/* Compile body */
|
|
|
|
for (i = 1; i < argn; i++) {
|
|
|
|
subopts.flags = DST_FOPTS_DROP;
|
2018-01-17 04:18:45 +00:00
|
|
|
dstc_freeslot(c, dstc_value(subopts, argv[i]));
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
|
2018-07-12 01:29:39 +00:00
|
|
|
/* Check if closure created in while scope. If so,
|
|
|
|
* recompile in a function scope. */
|
|
|
|
if (tempscope.flags & DST_SCOPE_CLOSURE) {
|
|
|
|
tempscope.flags |= DST_SCOPE_UNUSED;
|
|
|
|
dstc_popscope(c);
|
|
|
|
dst_v__cnt(c->buffer) = labelwt;
|
|
|
|
dst_v__cnt(c->mapbuffer) = labelwt;
|
|
|
|
|
|
|
|
dstc_scope(&tempscope, c, DST_SCOPE_FUNCTION, "while-iife");
|
|
|
|
|
|
|
|
/* Recompile in the function scope */
|
|
|
|
cond = dstc_value(subopts, argv[0]);
|
|
|
|
if (!(cond.flags & DST_SLOT_CONSTANT)) {
|
|
|
|
/* If not an infinte loop, return nil when condition false */
|
|
|
|
dstc_emit_si(c, DOP_JUMP_IF, cond, 2, 0);
|
|
|
|
dstc_emit(c, DOP_RETURN_NIL);
|
|
|
|
}
|
|
|
|
for (i = 1; i < argn; i++) {
|
|
|
|
subopts.flags = DST_FOPTS_DROP;
|
|
|
|
dstc_freeslot(c, dstc_value(subopts, argv[i]));
|
|
|
|
}
|
|
|
|
/* But now add tail recursion */
|
|
|
|
int32_t tempself = dstc_regalloc_temp(&tempscope.ra, DSTC_REGTEMP_0);
|
|
|
|
dstc_emit(c, DOP_LOAD_SELF | (tempself << 8));
|
|
|
|
dstc_emit(c, DOP_TAILCALL | (tempself << 8));
|
|
|
|
/* Compile function */
|
|
|
|
DstFuncDef *def = dstc_pop_funcdef(c);
|
2018-07-12 02:18:24 +00:00
|
|
|
def->name = dst_cstring("_while");
|
2018-07-12 01:29:39 +00:00
|
|
|
int32_t defindex = dstc_addfuncdef(c, def);
|
|
|
|
/* And then load the closure and call it. */
|
|
|
|
int32_t cloreg = dstc_regalloc_temp(&c->scope->ra, DSTC_REGTEMP_0);
|
|
|
|
dstc_emit(c, DOP_CLOSURE | (cloreg << 8) | (defindex << 16));
|
|
|
|
dstc_emit(c, DOP_CALL | (cloreg << 8) | (cloreg << 16));
|
|
|
|
dstc_regalloc_free(&c->scope->ra, cloreg);
|
2018-07-12 02:18:24 +00:00
|
|
|
c->scope->flags |= DST_SCOPE_CLOSURE;
|
2018-07-12 01:29:39 +00:00
|
|
|
return dstc_cslot(dst_wrap_nil());
|
|
|
|
}
|
|
|
|
|
2018-01-12 15:41:27 +00:00
|
|
|
/* Compile jump to whiletop */
|
|
|
|
labeljt = dst_v_count(c->buffer);
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_emit(c, DOP_JUMP);
|
2018-01-12 15:41:27 +00:00
|
|
|
|
|
|
|
/* Calculate jumps */
|
|
|
|
labeld = dst_v_count(c->buffer);
|
|
|
|
if (!infinite) c->buffer[labelc] |= (labeld - labelc) << 16;
|
|
|
|
c->buffer[labeljt] |= (labelwt - labeljt) << 8;
|
|
|
|
|
|
|
|
/* Pop scope and return nil slot */
|
2018-01-17 04:18:45 +00:00
|
|
|
dstc_popscope(c);
|
2018-01-12 15:41:27 +00:00
|
|
|
|
|
|
|
return dstc_cslot(dst_wrap_nil());
|
|
|
|
}
|
|
|
|
|
2018-07-12 01:29:39 +00:00
|
|
|
static DstSlot dstc_fn(DstFopts opts, int32_t argn, const Dst *argv) {
|
2018-01-12 15:41:27 +00:00
|
|
|
DstCompiler *c = opts.compiler;
|
|
|
|
DstFuncDef *def;
|
|
|
|
DstSlot ret;
|
2018-01-17 04:18:45 +00:00
|
|
|
Dst head, paramv;
|
2018-07-01 15:52:15 +00:00
|
|
|
DstScope fnscope;
|
2018-07-04 03:07:35 +00:00
|
|
|
int32_t paramcount, argi, parami, arity, defindex;
|
2018-01-17 04:18:45 +00:00
|
|
|
DstFopts subopts = dstc_fopts_default(c);
|
2018-01-12 15:41:27 +00:00
|
|
|
const Dst *params;
|
2018-07-01 15:52:15 +00:00
|
|
|
const char *errmsg = NULL;
|
2018-01-12 15:41:27 +00:00
|
|
|
int varargs = 0;
|
2018-01-19 17:37:37 +00:00
|
|
|
int selfref = 0;
|
2018-01-12 15:41:27 +00:00
|
|
|
|
2018-07-01 15:52:15 +00:00
|
|
|
/* Begin function */
|
2018-07-12 02:18:24 +00:00
|
|
|
c->scope->flags |= DST_SCOPE_CLOSURE;
|
2018-07-01 15:52:15 +00:00
|
|
|
dstc_scope(&fnscope, c, DST_SCOPE_FUNCTION, "function");
|
|
|
|
|
2018-01-12 15:41:27 +00:00
|
|
|
if (argn < 2) {
|
2018-07-01 15:52:15 +00:00
|
|
|
errmsg = "expected at least 2 arguments to function literal";
|
|
|
|
goto error;
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Read function parameters */
|
|
|
|
parami = 0;
|
|
|
|
arity = 0;
|
2018-06-29 03:36:31 +00:00
|
|
|
head = argv[0];
|
2018-01-19 17:37:37 +00:00
|
|
|
if (dst_checktype(head, DST_SYMBOL)) {
|
|
|
|
selfref = 1;
|
|
|
|
parami = 1;
|
|
|
|
}
|
2018-01-12 15:41:27 +00:00
|
|
|
if (parami >= argn) {
|
2018-07-01 15:52:15 +00:00
|
|
|
errmsg = "expected function parameters";
|
|
|
|
goto error;
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
2018-06-29 03:36:31 +00:00
|
|
|
paramv = argv[parami];
|
2018-07-04 17:15:52 +00:00
|
|
|
if (dst_indexed_view(paramv, ¶ms, ¶mcount)) {
|
2018-01-12 15:41:27 +00:00
|
|
|
int32_t i;
|
|
|
|
for (i = 0; i < paramcount; i++) {
|
2018-06-29 03:36:31 +00:00
|
|
|
Dst param = params[i];
|
2018-01-17 04:18:45 +00:00
|
|
|
if (dst_checktype(param, DST_SYMBOL)) {
|
2018-01-12 15:41:27 +00:00
|
|
|
/* Check for varargs */
|
2018-01-17 04:18:45 +00:00
|
|
|
if (0 == dst_cstrcmp(dst_unwrap_symbol(param), "&")) {
|
2018-01-12 15:41:27 +00:00
|
|
|
if (i != paramcount - 2) {
|
2018-07-01 15:52:15 +00:00
|
|
|
errmsg = "variable argument symbol in unexpected location";
|
|
|
|
goto error;
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
varargs = 1;
|
|
|
|
arity--;
|
|
|
|
continue;
|
|
|
|
}
|
2018-07-01 15:52:15 +00:00
|
|
|
dstc_nameslot(c, dst_unwrap_symbol(param), dstc_farslot(c));
|
2018-01-12 15:41:27 +00:00
|
|
|
} else {
|
2018-07-01 15:52:15 +00:00
|
|
|
destructure(c, param, dstc_farslot(c), defleaf, NULL);
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
2018-03-12 06:06:51 +00:00
|
|
|
arity++;
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
} else {
|
2018-07-01 15:52:15 +00:00
|
|
|
errmsg = "expected function parameters";
|
|
|
|
goto error;
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 17:37:37 +00:00
|
|
|
/* Check for self ref */
|
|
|
|
if (selfref) {
|
2018-07-01 15:52:15 +00:00
|
|
|
DstSlot slot = dstc_farslot(c);
|
2018-01-19 17:37:37 +00:00
|
|
|
slot.flags = DST_SLOT_NAMED | DST_FUNCTION;
|
2018-07-04 03:07:35 +00:00
|
|
|
dstc_emit_s(c, DOP_LOAD_SELF, slot, 1);
|
2018-01-19 17:37:37 +00:00
|
|
|
dstc_nameslot(c, dst_unwrap_symbol(head), slot);
|
|
|
|
}
|
|
|
|
|
2018-01-12 15:41:27 +00:00
|
|
|
/* Compile function body */
|
2018-03-12 06:06:51 +00:00
|
|
|
if (parami + 1 == argn) {
|
2018-06-29 03:36:31 +00:00
|
|
|
dstc_emit(c, DOP_RETURN_NIL);
|
2018-03-12 06:06:51 +00:00
|
|
|
} else for (argi = parami + 1; argi < argn; argi++) {
|
2018-07-01 15:52:15 +00:00
|
|
|
subopts.flags = (argi == (argn - 1)) ? DST_FOPTS_TAIL : DST_FOPTS_DROP;
|
2018-07-04 03:07:35 +00:00
|
|
|
dstc_value(subopts, argv[argi]);
|
2018-07-04 17:15:52 +00:00
|
|
|
if (c->result.status == DST_COMPILE_ERROR)
|
2018-07-01 15:52:15 +00:00
|
|
|
goto error2;
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
2018-03-23 13:18:04 +00:00
|
|
|
|
2018-01-12 15:41:27 +00:00
|
|
|
/* Build function */
|
|
|
|
def = dstc_pop_funcdef(c);
|
|
|
|
def->arity = arity;
|
|
|
|
if (varargs) def->flags |= DST_FUNCDEF_FLAG_VARARG;
|
2018-03-22 00:53:39 +00:00
|
|
|
if (selfref) def->name = dst_unwrap_symbol(head);
|
2018-01-12 15:41:27 +00:00
|
|
|
defindex = dstc_addfuncdef(c, def);
|
|
|
|
|
2018-01-12 21:25:24 +00:00
|
|
|
/* Ensure enough slots for vararg function. */
|
|
|
|
if (arity + varargs > def->slotcount) def->slotcount = arity + varargs;
|
|
|
|
|
2018-01-12 15:41:27 +00:00
|
|
|
/* Instantiate closure */
|
2018-01-16 04:31:39 +00:00
|
|
|
ret = dstc_gettarget(opts);
|
2018-07-04 03:07:35 +00:00
|
|
|
dstc_emit_su(c, DOP_CLOSURE, ret, defindex, 1);
|
2018-01-12 15:41:27 +00:00
|
|
|
return ret;
|
2018-07-01 15:52:15 +00:00
|
|
|
|
|
|
|
error:
|
|
|
|
dstc_cerror(c, errmsg);
|
|
|
|
error2:
|
|
|
|
dstc_popscope(c);
|
|
|
|
return dstc_cslot(dst_wrap_nil());
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
|
2018-03-23 13:18:04 +00:00
|
|
|
/* Keep in lexicographic order */
|
2018-01-12 15:41:27 +00:00
|
|
|
static const DstSpecial dstc_specials[] = {
|
2018-03-16 22:15:34 +00:00
|
|
|
{":=", dstc_varset},
|
2018-01-12 15:41:27 +00:00
|
|
|
{"def", dstc_def},
|
|
|
|
{"do", dstc_do},
|
|
|
|
{"fn", dstc_fn},
|
|
|
|
{"if", dstc_if},
|
|
|
|
{"quote", dstc_quote},
|
|
|
|
{"var", dstc_var},
|
|
|
|
{"while", dstc_while}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Find a special */
|
|
|
|
const DstSpecial *dstc_special(const uint8_t *name) {
|
|
|
|
return dst_strbinsearch(
|
|
|
|
&dstc_specials,
|
|
|
|
sizeof(dstc_specials)/sizeof(DstSpecial),
|
|
|
|
sizeof(DstSpecial),
|
|
|
|
name);
|
|
|
|
}
|
|
|
|
|