uCBQN/src/vm.h
2021-06-16 03:03:31 +03:00

178 lines
5.6 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
enum {
PUSH = 0, // N; push object from objs[N]
VARO = 1, // N; push variable with name strs[N]
VARM = 2, // N; push mutable variable with name strs[N]
ARRO = 3, // N; create a vector of top N items
ARRM = 4, // N; create a mutable vector of top N items
FN1C = 5, // monadic function call ⟨…,x,f ⟩ → F x
FN2C = 6, // dyadic function call ⟨…,x,f,w⟩ → w F x
OP1D = 7, // derive 1-modifier to function; ⟨…, _m,f⟩ → (f _m)
OP2D = 8, // derive 2-modifier to function; ⟨…,g,_m,f⟩ → (f _m_ g)
TR2D = 9, // derive 2-train aka atop; ⟨…, g,f⟩ → (f g)
TR3D = 10, // derive 3-train aka fork; ⟨…,h,g,f⟩ → (f g h)
SETN = 11, // set new; _ ←_; ⟨…,x, mut⟩ → mut←x
SETU = 12, // set upd; _ ↩_; ⟨…,x, mut⟩ → mut↩x
SETM = 13, // set mod; _ F↩_; ⟨…,x,F,mut⟩ → mut F↩x
POPS = 14, // pop object from stack
DFND = 15, // N; push dfns[N], derived to current scope
FN1O = 16, // optional monadic call (FN1C but checks for · at 𝕩)
FN2O = 17, // optional dyadic call (FN2C but checks for · at 𝕩 & 𝕨)
CHKV = 18, // throw error if top of stack is ·
TR3O = 19, // TR3D but creates an atop if F is ·
OP2H = 20, // derive 2-modifier to 1-modifier ⟨…,g,_m_⟩ → (_m_ g)
LOCO = 21, // N0,N1; push variable at depth N0 and position N1
LOCM = 22, // N0,N1; push mutable variable at depth N0 and position N1
VFYM = 23, // push a mutable version of ToS that fails if set to a non-equal value (for header assignment)
SETH = 24, // set header; acts like SETN, but it doesn't push to stack, and, instead of erroring in cases it would, it skips to the next body
RETN = 25, // returns top of stack
FLDO = 26, // N; get field nameList[N] from ToS
FLDM = 27, // N; get mutable field nameList[N] from ToS
NSPM = 28, // N; replace ToS with one with a namespace field alias N
RETD = 29, // return a namespace of exported items
SYSV = 30, // N; get system function N
LOCU = 31, // N0,N1; like LOCO but overrides the slot with bi_optOut
EXTO, EXTM, EXTU, // alternate versions of LOC_ for extended variables
ADDI, ADDU, // separate PUSH for refcounting needed/not needed (stores the object inline as 2 u32s, instead of reading from `objs`)
FN1Ci, FN1Oi, FN2Ci, FN2Oi, // FN__ alternatives that don't take the function from the stack, but instead as an 2×u32 immediate in the bytecode
BC_SIZE
};
typedef struct Comp Comp;
typedef struct BlBlocks BlBlocks;
typedef struct Block Block;
typedef struct Body Body;
typedef struct Scope Scope;
typedef struct ScopeExt ScopeExt;
typedef struct Comp {
struct Value;
B bc;
B src;
B indices;
HArr* objs;
u32 blockAm;
} Comp;
struct BlBlocks {
struct Value;
u32 am;
Block* a[];
};
struct Block {
struct Value;
bool imm;
u8 ty;
Body* body;
};
typedef struct NSDesc NSDesc;
struct Body {
struct Value;
Comp* comp;
BlBlocks* blocks;
// B* objs;
u32* bc; // pointer in an owned I32Arr
i32* map; // pointer in an owned I32Arr
u8* nvm; // either NULL or a pointer to machine code otherwise
u32 maxStack;
u16 maxPSC;
u16 varAm;
NSDesc* nsDesc;
B nvmRefs;
i32 varIDs[];
};
struct ScopeExt {
struct Value;
u16 varAm;
B vars[]; // vars has length varAm*2; position varAm and onwards are corresponding names to variables at regular indexes
};
struct Scope {
struct Value;
Scope* psc;
Body* body; // last place where code was executed in this scope; also used for variable name resolution, so can only safely be replaced if varAm==0
u16 varAm;
ScopeExt* ext; // will probably be NULL
B vars[];
};
Block* bqn_comp(B str, B path, B args);
Block* bqn_compSc(B str, B path, B args, Scope* sc, bool repl);
Block* compile(B bcq, B objs, B blocksq, B indices, B tokenInfo, B src, Scope* sc);
Scope* m_scope(Body* body, Scope* psc, u16 varAm);
B evalBC(Body* b, Scope* sc); // doesn't consume; executes bytecode of the body directly in the scope
u32* nextBC(u32* p);
i32 stackDiff(u32* p);
i32 stackConsumed(u32* p);
i32 stackAdded(u32* p);
char* nameBC(u32* p);
typedef struct FunBlock { struct Fun; Scope* sc; Block* bl; } FunBlock;
typedef struct Md1Block { struct Md1; Scope* sc; Block* bl; } Md1Block;
typedef struct Md2Block { struct Md2; Scope* sc; Block* bl; } Md2Block;
// all don't consume anything
B m_funBlock(Block* bl, Scope* psc); // may return evaluated result, whatever
B m_md1Block(Block* bl, Scope* psc);
B m_md2Block(Block* bl, Scope* psc);
typedef struct Env {
Scope* sc;
union { u32* bcL; i32 bcV; };
} Env;
extern Env* envCurr;
extern Env* envStart;
extern Env* envEnd;
static inline void pushEnv(Scope* sc, u32* bc) {
if (envCurr==envEnd) thrM("Stack overflow");
envCurr->sc = sc;
envCurr->bcL = bc;
envCurr++;
}
static inline void popEnv() {
assert(envCurr>envStart);
envCurr--;
}
void vm_pst(Env* s, Env* e);
void vm_pstLive();
void vm_printPos(Comp* comp, i32 bcPos, i64 pos);
typedef struct FldAlias {
struct Value;
B obj;
i32 p;
} FldAlias;
NOINLINE B v_getR(Scope* pscs[], B s);
static B v_get(Scope* pscs[], B s) { // get value representing s, replacing with bi_optOut; doesn't consume
if (RARE(!isVar(s))) return v_getR(pscs, s);
Scope* sc = pscs[(u16)(s.u>>32)];
B r = sc->vars[(u32)s.u];
sc->vars[(u32)s.u] = bi_optOut;
return r;
}
NOINLINE void v_setR(Scope* pscs[], B s, B x, bool upd);
static void v_set(Scope* pscs[], B s, B x, bool upd) { // doesn't consume
if (RARE(!isVar(s))) return v_setR(pscs, s, x, upd);;
Scope* sc = pscs[(u16)(s.u>>32)];
B prev = sc->vars[(u32)s.u];
if (upd) {
if (prev.u==bi_noVar.u) thrM("↩: Updating undefined variable");
dec(prev);
}
sc->vars[(u32)s.u] = inc(x);
}