1
0
mirror of https://github.com/janet-lang/janet synced 2024-06-14 09:26:49 +00:00
janet/src/compiler/specials.c
bakpakin 4f74d57359 Refactor stl to corelib and stl. Corelib is part of vm, stl
is part of dst language. Add bootstrapping code directly into stl.
Stl is now logically grouped with compiler.
2018-01-29 23:38:49 -05:00

526 lines
17 KiB
C

/*
* 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 <dst/dst.h>
#include <dst/dstcorelib.h>
#include <dst/dstcompile.h>
#include <dst/dstparse.h>
#include "compile.h"
#include <headerlibs/strbinsearch.h>
#include <headerlibs/vector.h>
DstSlot dstc_quote(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
if (argn != 1) {
dstc_cerror(opts.compiler, ast, "expected 1 argument");
return dstc_cslot(dst_wrap_nil());
}
return dstc_cslot(dst_ast_unwrap(argv[0]));
}
DstSlot dstc_astquote(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
if (argn != 1) {
dstc_cerror(opts.compiler, ast, "expected 1 argument");
return dstc_cslot(dst_wrap_nil());
}
return dstc_cslot(argv[0]);
}
DstSlot dstc_varset(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
DstFopts subopts = dstc_fopts_default(opts.compiler);
DstSlot ret, dest;
Dst head;
if (argn != 2) {
dstc_cerror(opts.compiler, ast, "expected 2 arguments");
return dstc_cslot(dst_wrap_nil());
}
head = dst_ast_unwrap(argv[0]);
if (!dst_checktype(head, DST_SYMBOL)) {
dstc_cerror(opts.compiler, ast, "expected symbol");
return dstc_cslot(dst_wrap_nil());
}
dest = dstc_resolve(opts.compiler, ast, dst_unwrap_symbol(head));
if (!(dest.flags & DST_SLOT_MUTABLE)) {
dstc_cerror(opts.compiler, ast, "cannot set constant");
return dstc_cslot(dst_wrap_nil());
}
subopts.flags = DST_FOPTS_HINT;
subopts.hint = dest;
ret = dstc_value(subopts, argv[1]);
dstc_copy(opts.compiler, ast, dest, ret);
return ret;
}
/* Add attributes to a global def or var table */
static void handleattr(DstCompiler *c, int32_t argn, const Dst *argv, DstTable *tab) {
int32_t i;
for (i = 1; i < argn - 1; i++) {
Dst attr = dst_ast_unwrap1(argv[i]);
switch (dst_type(attr)) {
default:
dstc_cerror(c, dst_ast_node(argv[i]), "could not add metadata to binding");
return;
case DST_SYMBOL:
dst_table_put(tab, attr, dst_wrap_true());
break;
case DST_STRING:
dst_table_put(tab, dst_csymbolv("doc"), attr);
break;
}
}
}
static DstSlot dohead(DstCompiler *c, DstFopts opts, DstAst *ast, Dst *head, int32_t argn, const Dst *argv) {
DstFopts subopts = dstc_fopts_default(c);
DstSlot ret;
if (argn < 2) {
dstc_cerror(c, ast, "expected at least 2 arguments");
return dstc_cslot(dst_wrap_nil());
}
*head = dst_ast_unwrap1(argv[0]);
if (!dst_checktype(*head, DST_SYMBOL)) {
dstc_cerror(c, dst_ast_node(argv[0]), "expected symbol");
return dstc_cslot(dst_wrap_nil());
}
subopts.flags = opts.flags & ~(DST_FOPTS_TAIL | DST_FOPTS_DROP);
subopts.hint = opts.hint;
ret = dstc_value(subopts, argv[argn - 1]);
return ret;
}
/* Def or var a symbol in a local scope */
static DstSlot namelocal(DstCompiler *c, DstAst *ast, Dst head, int32_t flags, DstSlot ret) {
/* Non root scope, bring to local slot */
if (ret.flags & DST_SLOT_NAMED ||
ret.envindex != 0 ||
ret.index < 0 ||
ret.index > 0xFF) {
/* Slot is not able to be named */
DstSlot localslot;
localslot.index = dstc_lsloti(c);
/* infer type? */
localslot.flags = flags;
localslot.envindex = 0;
localslot.constant = dst_wrap_nil();
dstc_copy(c, ast, localslot, ret);
ret = localslot;
}
dstc_nameslot(c, dst_unwrap_symbol(head), ret);
ret.flags |= DST_SLOT_NAMED;
return ret;
}
DstSlot dstc_var(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
DstCompiler *c = opts.compiler;
Dst head;
DstSlot ret = dohead(c, opts, ast, &head, argn, argv);
if (dstc_iserr(&opts)) return dstc_cslot(dst_wrap_nil());
if (dst_v_last(c->scopes).flags & DST_SCOPE_TOP) {
DstSlot refslot, refarrayslot;
/* Global var, generate var */
DstTable *reftab = dst_table(1);
DstArray *ref = dst_array(1);
dst_array_push(ref, dst_wrap_nil());
dst_table_put(reftab, dst_csymbolv("ref"), dst_wrap_array(ref));
handleattr(c, argn, argv, reftab);
dst_table_put(c->env, head, dst_wrap_table(reftab));
refslot = dstc_cslot(dst_wrap_array(ref));
refarrayslot = refslot;
refslot.flags |= DST_SLOT_REF | DST_SLOT_NAMED | DST_SLOT_MUTABLE;
/* Generate code to set ref */
int32_t refarrayindex = dstc_preread(c, ast, 0xFF, 1, refarrayslot);
int32_t retindex = dstc_preread(c, ast, 0xFF, 2, ret);
dstc_emit(c, ast,
(retindex << 16) |
(refarrayindex << 8) |
DOP_PUT_INDEX);
dstc_postread(c, refarrayslot, refarrayindex);
dstc_postread(c, ret, retindex);
ret = refslot;
} else {
ret = namelocal(c, ast, head, DST_SLOT_NAMED | DST_SLOT_MUTABLE, ret);
}
return ret;
}
DstSlot dstc_def(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
DstCompiler *c = opts.compiler;
Dst head;
opts.flags &= ~DST_FOPTS_HINT;
DstSlot ret = dohead(c, opts, ast, &head, argn, argv);
if (dstc_iserr(&opts)) return dstc_cslot(dst_wrap_nil());
if (dst_v_last(c->scopes).flags & DST_SCOPE_TOP) {
DstTable *tab = dst_table(2);
int32_t tableindex, valsymindex, valueindex;
DstSlot valsym = dstc_cslot(dst_csymbolv("value"));
DstSlot tabslot = dstc_cslot(dst_wrap_table(tab));
/* Add env entry to env */
handleattr(c, argn, argv, tab);
dst_table_put(c->env, head, dst_wrap_table(tab));
/* Put value in table when evaulated */
tableindex = dstc_preread(c, ast, 0xFF, 1, tabslot);
valsymindex = dstc_preread(c, ast, 0xFF, 2, valsym);
valueindex = dstc_preread(c, ast, 0xFF, 3, ret);
dstc_emit(c, ast,
(valueindex << 24) |
(valsymindex << 16) |
(tableindex << 8) |
DOP_PUT);
dstc_postread(c, tabslot, tableindex);
dstc_postread(c, valsym, valsymindex);
dstc_postread(c, ret, valueindex);
} else {
ret = namelocal(c, ast, head, DST_SLOT_NAMED, ret);
}
return ret;
}
/*
* :condition
* ...
* jump-if-not condition :right
* :left
* ...
* jump done (only if not tail)
* :right
* ...
* :done
*/
DstSlot dstc_if(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
DstCompiler *c = opts.compiler;
int32_t labelr, labeljr, labeld, labeljd, condlocal;
DstFopts condopts, bodyopts;
DstSlot cond, left, right, target;
Dst truebody, falsebody;
const int tail = opts.flags & DST_FOPTS_TAIL;
const int drop = opts.flags & DST_FOPTS_DROP;
if (argn < 2 || argn > 3) {
dstc_cerror(c, ast, "expected 2 or 3 arguments to if");
return dstc_cslot(dst_wrap_nil());
}
/* Get the bodies of the if expression */
truebody = argv[1];
falsebody = argn > 2 ? argv[2] : dst_wrap_nil();
/* Get options */
condopts = dstc_fopts_default(c);
bodyopts = opts;
/* Compile condition */
cond = dstc_value(condopts, argv[0]);
/* Check constant condition. */
/* TODO: Use type info for more short circuits */
if (cond.flags & DST_SLOT_CONSTANT) {
if (!dst_truthy(cond.constant)) {
/* Swap the true and false bodies */
Dst temp = falsebody;
falsebody = truebody;
truebody = temp;
}
dstc_scope(c, 0);
target = dstc_value(bodyopts, truebody);
dstc_popscope(c);
dstc_throwaway(bodyopts, falsebody);
return target;
}
/* Set target for compilation */
target = (!drop && !tail)
? dstc_gettarget(opts)
: dstc_cslot(dst_wrap_nil());
/* Compile jump to right */
condlocal = dstc_preread(c, ast, 0xFF, 1, cond);
labeljr = dst_v_count(c->buffer);
dstc_emit(c, ast, DOP_JUMP_IF_NOT | (condlocal << 8));
dstc_postread(c, cond, condlocal);
dstc_freeslot(c, cond);
/* Condition left body */
dstc_scope(c, 0);
left = dstc_value(bodyopts, truebody);
if (!drop && !tail) dstc_copy(c, ast, target, left);
dstc_popscope(c);
/* Compile jump to done */
labeljd = dst_v_count(c->buffer);
if (!tail) dstc_emit(c, ast, DOP_JUMP);
/* Compile right body */
labelr = dst_v_count(c->buffer);
dstc_scope(c, 0);
right = dstc_value(bodyopts, falsebody);
if (!drop && !tail) dstc_copy(c, ast, target, right);
dstc_popscope(c);
/* 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. */
DstSlot dstc_do(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
int32_t i;
DstSlot ret;
DstCompiler *c = opts.compiler;
DstFopts subopts = dstc_fopts_default(c);
(void) ast;
dstc_scope(c, 0);
for (i = 0; i < argn; i++) {
if (i != argn - 1) {
subopts.flags = DST_FOPTS_DROP;
} else if (opts.flags & DST_FOPTS_TAIL) {
subopts.flags = DST_FOPTS_TAIL;
}
ret = dstc_value(subopts, argv[i]);
if (i != argn - 1) {
dstc_freeslot(c, ret);
}
}
dstc_popscope_keepslot(c, ret);
return ret;
}
/*
* :whiletop
* ...
* :condition
* jump-if-not cond :done
* ...
* jump :whiletop
* :done
*/
DstSlot dstc_while(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
DstCompiler *c = opts.compiler;
DstSlot cond;
DstFopts subopts = dstc_fopts_default(c);
int32_t condlocal, labelwt, labeld, labeljt, labelc, i;
int infinite = 0;
if (argn < 2) {
dstc_cerror(c, ast, "expected at least 2 arguments");
return dstc_cslot(dst_wrap_nil());
}
labelwt = dst_v_count(c->buffer);
/* Compile condition */
cond = dstc_value(subopts, argv[0]);
/* Check for constant condition */
if (cond.flags & DST_SLOT_CONSTANT) {
/* Loop never executes */
if (!dst_truthy(cond.constant)) {
return dstc_cslot(dst_wrap_nil());
}
/* Infinite loop */
infinite = 1;
}
dstc_scope(c, 0);
/* Infinite loop does not need to check condition */
if (!infinite) {
condlocal = dstc_preread(c, ast, 0xFF, 1, cond);
labelc = dst_v_count(c->buffer);
dstc_emit(c, ast, DOP_JUMP_IF_NOT | (condlocal << 8));
dstc_postread(c, cond, condlocal);
}
/* Compile body */
for (i = 1; i < argn; i++) {
subopts.flags = DST_FOPTS_DROP;
dstc_freeslot(c, dstc_value(subopts, argv[i]));
}
/* Compile jump to whiletop */
labeljt = dst_v_count(c->buffer);
dstc_emit(c, ast, DOP_JUMP);
/* 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 */
dstc_popscope(c);
return dstc_cslot(dst_wrap_nil());
}
/* Add a funcdef to the top most function scope */
static int32_t dstc_addfuncdef(DstCompiler *c, DstFuncDef *def) {
DstScope *scope = &dst_v_last(c->scopes);
while (scope >= c->scopes) {
if (scope->flags & DST_SCOPE_FUNCTION)
break;
scope--;
}
dst_assert(scope >= c->scopes, "could not add funcdef");
dst_v_push(scope->defs, def);
return dst_v_count(scope->defs) - 1;
}
DstSlot dstc_fn(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
DstCompiler *c = opts.compiler;
DstFuncDef *def;
DstSlot ret;
Dst head, paramv;
int32_t paramcount, argi, parami, arity, localslot, defindex;
DstFopts subopts = dstc_fopts_default(c);
const Dst *params;
int varargs = 0;
int selfref = 0;
if (argn < 2) {
dstc_cerror(c, ast, "expected at least 2 arguments to function literal");
return dstc_cslot(dst_wrap_nil());
}
/* Begin function */
dstc_scope(c, DST_SCOPE_FUNCTION);
/* Read function parameters */
parami = 0;
arity = 0;
head = dst_ast_unwrap1(argv[0]);
if (dst_checktype(head, DST_SYMBOL)) {
selfref = 1;
parami = 1;
}
if (parami >= argn) {
dstc_cerror(c, dst_ast_node(argv[0]), "expected function parameters");
return dstc_cslot(dst_wrap_nil());
}
paramv = dst_ast_unwrap(argv[parami]);
if (dst_seq_view(paramv, &params, &paramcount)) {
int32_t i;
for (i = 0; i < paramcount; i++) {
Dst param = dst_ast_unwrap1(params[i]);
if (dst_checktype(param, DST_SYMBOL)) {
DstSlot slot;
/* Check for varargs */
if (0 == dst_cstrcmp(dst_unwrap_symbol(param), "&")) {
if (i != paramcount - 2) {
dstc_cerror(c, dst_ast_node(params[i]), "variable argument symbol in unexpected location");
return dstc_cslot(dst_wrap_nil());
}
varargs = 1;
arity--;
continue;
}
slot.flags = DST_SLOT_NAMED;
slot.envindex = 0;
slot.constant = dst_wrap_nil();
slot.index = dstc_lsloti(c);
dstc_nameslot(c, dst_unwrap_symbol(param), slot);
arity++;
} else {
dstc_cerror(c, dst_ast_node(params[i]), "expected symbol as function parameter");
return dstc_cslot(dst_wrap_nil());
}
}
} else {
dstc_cerror(c, ast, "expected function parameters");
return dstc_cslot(dst_wrap_nil());
}
/* Check for self ref */
if (selfref) {
DstSlot slot;
slot.envindex = 0;
slot.flags = DST_SLOT_NAMED | DST_FUNCTION;
slot.constant = dst_wrap_nil();
slot.index = dstc_lsloti(c);
dstc_emit(c, ast, (slot.index << 8) | DOP_LOAD_SELF);
dstc_nameslot(c, dst_unwrap_symbol(head), slot);
}
/* Compile function body */
for (argi = parami + 1; argi < argn; argi++) {
DstSlot s;
subopts.flags = argi == (argn - 1) ? DST_FOPTS_TAIL : DST_FOPTS_DROP;
s = dstc_value(subopts, argv[argi]);
dstc_freeslot(c, s);
if (dstc_iserr(&opts)) return dstc_cslot(dst_wrap_nil());
}
/* Build function */
def = dstc_pop_funcdef(c);
def->arity = arity;
if (varargs) def->flags |= DST_FUNCDEF_FLAG_VARARG;
defindex = dstc_addfuncdef(c, def);
/* Ensure enough slots for vararg function. */
if (arity + varargs > def->slotcount) def->slotcount = arity + varargs;
/* Instantiate closure */
ret = dstc_gettarget(opts);
localslot = ret.index > 0xF0 ? 0xF1 : ret.index;
dstc_emit(c, ast,
(defindex << 16) |
(localslot << 8) |
DOP_CLOSURE);
if (ret.index != localslot) {
dstc_emit(c, ast,
(ret.index << 16) |
(localslot << 8) |
DOP_MOVE_FAR);
}
return ret;
}
/* Keep in lexographic order */
static const DstSpecial dstc_specials[] = {
{"ast-quote", dstc_astquote},
{"def", dstc_def},
{"do", dstc_do},
{"fn", dstc_fn},
{"if", dstc_if},
{"quote", dstc_quote},
{"var", dstc_var},
{"varset!", dstc_varset},
{"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);
}