1210 lines
30 KiB
C
1210 lines
30 KiB
C
#include"parse.h"
|
|
|
|
#include"vm.h"
|
|
#include<alloca.h>
|
|
|
|
#define i_key LInst
|
|
#include"stc/vec.h"
|
|
|
|
#define i_key int32_t
|
|
#include"stc/vec.h"
|
|
|
|
#define i_key LFunc
|
|
#include"stc/vec.h"
|
|
|
|
typedef struct ScopeItem {
|
|
Token name;
|
|
int vreg;
|
|
|
|
struct ScopeItem *next;
|
|
} ScopeItem;
|
|
|
|
typedef struct Scope {
|
|
struct Scope *parent;
|
|
ScopeItem *items;
|
|
} Scope;
|
|
|
|
ScopeItem *scope_get(Scope *scope, const char *name) {
|
|
for(ScopeItem *si = scope->items; si; si = si->next) {
|
|
if(!strcmp(si->name.text, name)) {
|
|
return si;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ScopeItem *scope_find(Scope *scope, const char *name) {
|
|
while(scope) {
|
|
ScopeItem *si = scope_get(scope, name);
|
|
if(si) {
|
|
return si;
|
|
}
|
|
scope = scope->parent;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void scope_set_direct(Scope *scope, ScopeItem *new) {
|
|
new->next = scope->items;
|
|
scope->items = new;
|
|
}
|
|
|
|
typedef struct {
|
|
vec_LInst instrs;
|
|
int used_vregs[256];
|
|
} Chunk;
|
|
|
|
typedef struct {
|
|
size_t i;
|
|
size_t sz;
|
|
Token *tokens;
|
|
|
|
LTable *environment;
|
|
|
|
Scope *scope;
|
|
|
|
vec_LFunc unit_functions;
|
|
|
|
size_t abyss_size;
|
|
uint8_t *abyss;
|
|
|
|
Chunk current_chunk;
|
|
} Parser;
|
|
|
|
typedef enum ExprKind {
|
|
EX_LOCAL,
|
|
EX_GLOBAL,
|
|
EX_INT,
|
|
EX_FLOAT,
|
|
EX_BOOL,
|
|
EX_ADD,
|
|
EX_SUB,
|
|
EX_MUL,
|
|
EX_DIV,
|
|
EX_IDIV,
|
|
EX_MOD,
|
|
EX_POW,
|
|
EX_BAND,
|
|
EX_BOR,
|
|
EX_BXOR,
|
|
EX_AND,
|
|
EX_OR,
|
|
EX_EQ,
|
|
EX_NEQ,
|
|
EX_TBL_LIT,
|
|
EX_FUNC_LIT,
|
|
EX_DOT,
|
|
EX_INDEX,
|
|
EX_CALL,
|
|
EX_STR,
|
|
EX_LENGTH,
|
|
} ExprKind;
|
|
typedef struct Expr {
|
|
ExprKind kind;
|
|
struct Expr *next_to_die;
|
|
union {
|
|
struct {
|
|
struct Expr *A;
|
|
union {
|
|
struct Expr *B;
|
|
Token B_tok;
|
|
};
|
|
};
|
|
Token name;
|
|
intmax_t i;
|
|
double f;
|
|
bool b;
|
|
struct {
|
|
uint8_t sub_count;
|
|
struct Expr *subs[];
|
|
};
|
|
struct {
|
|
size_t table_first_token;
|
|
size_t table_last_token;
|
|
};
|
|
};
|
|
} Expr;
|
|
|
|
size_t abyss_insert(Parser *P, uint8_t *data, size_t len) {
|
|
size_t idx = P->abyss_size;
|
|
|
|
P->abyss = realloc(P->abyss, P->abyss_size += len);
|
|
|
|
if(data) {
|
|
memcpy(P->abyss + idx, data, len);
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
Token peek(Parser *P, int offset) {
|
|
if(P->i + offset >= P->sz) {
|
|
Token t = P->tokens[P->sz - 1];
|
|
t.type = TOK_EOF;
|
|
return t;
|
|
}
|
|
return P->tokens[P->i + offset];
|
|
}
|
|
|
|
Token get(Parser *P) {
|
|
Token t = peek(P, 0);
|
|
P->i++;
|
|
return t;
|
|
}
|
|
|
|
Token expect(Parser *P, TokenType type) {
|
|
Token t = get(P);
|
|
assert(t.type == type);
|
|
return t;
|
|
}
|
|
|
|
bool maybe(Parser *P, TokenType type) {
|
|
if(peek(P, 0).type == type) {
|
|
P->i++;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int find_vreg(Parser *P) {
|
|
for(int i = 255; i >= 0; i--) {
|
|
if(!P->current_chunk.used_vregs[i]) {
|
|
return i;
|
|
}
|
|
}
|
|
assert(false);
|
|
}
|
|
|
|
void alloc_vreg(Parser *P, int vreg) {
|
|
P->current_chunk.used_vregs[vreg]++;
|
|
}
|
|
|
|
void free_vreg(Parser *P, int vreg) {
|
|
P->current_chunk.used_vregs[vreg]--;
|
|
assert(P->current_chunk.used_vregs[vreg] >= 0 && "Cannot free unused vreg");
|
|
}
|
|
|
|
static void scope_kill(Parser *P) {
|
|
Scope *parent = P->scope->parent;
|
|
|
|
for(ScopeItem *si = P->scope->items; si;) {
|
|
ScopeItem *next = si->next;
|
|
free(si);
|
|
si = next;
|
|
}
|
|
free(P->scope);
|
|
|
|
P->scope = parent;
|
|
}
|
|
|
|
void parse_chunk(Parser *P);
|
|
|
|
int parse_functiondef(Parser *P, bool can_be_local) {
|
|
size_t old_idx = P->i;
|
|
|
|
if(!(peek(P, 0).type == TOK_FUNCTION || (can_be_local && peek(P, 0).type == TOK_LOCAL && peek(P, 1).type == TOK_FUNCTION))) {
|
|
goto err;
|
|
}
|
|
|
|
bool is_local = maybe(P, TOK_LOCAL);
|
|
|
|
expect(P, TOK_FUNCTION);
|
|
|
|
Token name = {};
|
|
if(peek(P, 0).type == TOK_NAME) {
|
|
name = get(P);
|
|
}
|
|
|
|
if(!maybe(P, TOK_PAREN_L)) {
|
|
goto err;
|
|
}
|
|
|
|
Chunk old_chunk = P->current_chunk;
|
|
P->current_chunk = (Chunk) {};
|
|
|
|
Scope *new_scope = calloc(1, sizeof(*new_scope));
|
|
new_scope->parent = P->scope;
|
|
P->scope = new_scope;
|
|
|
|
size_t arg_count = 0;
|
|
if(!maybe(P, TOK_PAREN_R)) {
|
|
while(1) {
|
|
if(peek(P, 0).type != TOK_NAME) {
|
|
goto err;
|
|
}
|
|
|
|
ScopeItem *si = calloc(1, sizeof(*si));
|
|
si->name = expect(P, TOK_NAME);
|
|
si->vreg = arg_count++;
|
|
scope_set_direct(P->scope, si);
|
|
|
|
if(maybe(P, TOK_PAREN_R)) {
|
|
break;
|
|
} else {
|
|
if(peek(P, 0).type != TOK_COMMA) {
|
|
goto err;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
parse_chunk(P);
|
|
if(P->current_chunk.instrs.data[P->current_chunk.instrs.size - 1].opcode != L_RET) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_RET, .argb = {0}});
|
|
}
|
|
|
|
expect(P, TOK_END);
|
|
|
|
assert(P->unit_functions.size > 0);
|
|
|
|
LFunc lf = {};
|
|
lf.unit = P->unit_functions.data[0].unit;
|
|
lf.is_native = false;
|
|
lf.lua_instrs = P->current_chunk.instrs.data;
|
|
lf.env = P->environment;
|
|
vec_LFunc_push(&P->unit_functions, lf);
|
|
|
|
size_t function_idx = P->unit_functions.size - 1;
|
|
|
|
P->current_chunk = old_chunk;
|
|
scope_kill(P);
|
|
|
|
int vreg = find_vreg(P);
|
|
assert(vreg != -1);
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETFUNC, .a = vreg, .bc = function_idx});
|
|
|
|
if(name.text) {
|
|
if(is_local) {
|
|
alloc_vreg(P, vreg);
|
|
|
|
ScopeItem *si = calloc(1, sizeof(*si));
|
|
si->name = name;
|
|
si->vreg = vreg;
|
|
|
|
scope_set_direct(P->scope, si);
|
|
} else {
|
|
size_t abyss_idx = abyss_insert(P, NULL, sizeof(uint16_t) + strlen(name.text));
|
|
*(uint16_t*) &P->abyss[abyss_idx] = strlen(name.text);
|
|
memcpy(&P->abyss[abyss_idx + sizeof(uint16_t)], name.text, strlen(name.text));
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETGLOBAL, .a = vreg, .bc = abyss_idx});
|
|
}
|
|
}
|
|
|
|
return vreg;
|
|
err:
|
|
P->i = old_idx;
|
|
return -1;
|
|
}
|
|
|
|
Expr *desc_exp(Parser *P);
|
|
Expr *desc_subexp(Parser *P, int priority);
|
|
void emit_expr(Parser *P, int assigned_vreg, Expr *expr);
|
|
|
|
bool parse_functioncall(Parser *P) {
|
|
size_t old_idx = P->i;
|
|
|
|
Expr *ex = desc_exp(P);
|
|
|
|
if(ex->kind != EX_CALL) {
|
|
goto err;
|
|
}
|
|
|
|
emit_expr(P, find_vreg(P), ex);
|
|
|
|
return true;
|
|
err:
|
|
P->i = old_idx;
|
|
return false;
|
|
}
|
|
|
|
vec_Token parse_namelist(Parser *P) {
|
|
vec_Token v = {};
|
|
|
|
vec_Token_push(&v, expect(P, TOK_NAME));
|
|
|
|
while(maybe(P, TOK_COMMA)) {
|
|
vec_Token_push(&v, expect(P, TOK_NAME));
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
static Expr *last_desc = NULL;
|
|
static Expr *mark_for_death(Expr *e) {
|
|
e->next_to_die = last_desc;
|
|
last_desc = e;
|
|
return e;
|
|
}
|
|
static Expr *new_expr(size_t space) {
|
|
Expr *e = calloc(1, sizeof(*e) + space);
|
|
mark_for_death(e);
|
|
return e;
|
|
}
|
|
Expr *desc_subexp(Parser *P, int priority) {
|
|
if(priority == 0) {
|
|
Expr *a = desc_subexp(P, priority + 1);
|
|
|
|
while(maybe(P, TOK_DOUBLE_EQUAL) || maybe(P, TOK_NOT_EQUAL)) {
|
|
Token op = peek(P, -1);
|
|
|
|
Expr *b = desc_subexp(P, priority + 1);
|
|
|
|
Expr *opex = new_expr(0);
|
|
opex->A = a;
|
|
opex->B = b;
|
|
|
|
if(op.type == TOK_DOUBLE_EQUAL) {
|
|
opex->kind = EX_EQ;
|
|
} else if(op.type == TOK_NOT_EQUAL) {
|
|
opex->kind = EX_NEQ;
|
|
}
|
|
|
|
a = opex;
|
|
}
|
|
|
|
return a;
|
|
} else if(priority == 1) {
|
|
Expr *a = desc_subexp(P, priority + 1);
|
|
|
|
while(peek(P, 0).type == TOK_PLUS || peek(P, 0).type == TOK_MINUS) {
|
|
Token op = get(P);
|
|
|
|
Expr *b = desc_subexp(P, priority + 1);
|
|
|
|
Expr *opex = new_expr(0);
|
|
opex->A = a;
|
|
opex->B = b;
|
|
|
|
if(op.type == TOK_PLUS) {
|
|
opex->kind = EX_ADD;
|
|
} else {
|
|
opex->kind = EX_SUB;
|
|
}
|
|
|
|
a = opex;
|
|
}
|
|
|
|
return a;
|
|
} else if(priority == 2) {
|
|
Expr *a = desc_subexp(P, priority + 1);
|
|
|
|
while(peek(P, 0).type == TOK_MUL || peek(P, 0).type == TOK_DIV || peek(P, 0).type == TOK_IDIV || peek(P, 0).type == TOK_PERCENT) {
|
|
Token op = get(P);
|
|
|
|
Expr *b = desc_subexp(P, priority + 1);
|
|
|
|
Expr *opex = new_expr(0);
|
|
opex->A = a;
|
|
opex->B = b;
|
|
|
|
if(op.type == TOK_MUL) {
|
|
opex->kind = EX_MUL;
|
|
} else if(op.type == TOK_DIV) {
|
|
opex->kind = EX_DIV;
|
|
} else if(op.type == TOK_IDIV) {
|
|
opex->kind = EX_IDIV;
|
|
} else if(op.type == TOK_PERCENT) {
|
|
opex->kind = EX_MOD;
|
|
}
|
|
|
|
a = opex;
|
|
}
|
|
|
|
return a;
|
|
} else if(priority == 3) {
|
|
Expr *e = NULL;
|
|
|
|
if(maybe(P, TOK_SHARP)) {
|
|
e = new_expr(0);
|
|
e->kind = EX_LENGTH;
|
|
e->A = desc_subexp(P, priority);
|
|
} else if(maybe(P, TOK_TRUE)) {
|
|
e = new_expr(0);
|
|
e->kind = EX_BOOL;
|
|
e->b = true;
|
|
} else if(maybe(P, TOK_FALSE)) {
|
|
e = new_expr(0);
|
|
e->kind = EX_BOOL;
|
|
e->b = false;
|
|
} else if(maybe(P, TOK_NUMBER)) {
|
|
P->i--;
|
|
|
|
Token num = expect(P, TOK_NUMBER);
|
|
long i = strtol(num.text, NULL, 10);
|
|
|
|
e = new_expr(0);
|
|
e->kind = EX_INT;
|
|
e->i = i;
|
|
} else if(maybe(P, TOK_NAME)) {
|
|
P->i--;
|
|
|
|
Token name = expect(P, TOK_NAME);
|
|
|
|
ScopeItem *si = scope_find(P->scope, name.text);
|
|
|
|
e = new_expr(0);
|
|
e->kind = si ? EX_LOCAL : EX_GLOBAL;
|
|
e->name = name;
|
|
} else if(maybe(P, TOK_STRING)) {
|
|
P->i--;
|
|
|
|
Token str = expect(P, TOK_STRING);
|
|
|
|
e = new_expr(0);
|
|
e->kind = EX_STR;
|
|
e->name = str;
|
|
} else if(maybe(P, TOK_FUNCTION)) {
|
|
e = new_expr(0);
|
|
e->kind = EX_FUNC_LIT;
|
|
e->table_first_token = P->i - 1;
|
|
|
|
size_t depth = 1;
|
|
while(1) {
|
|
Token t = get(P);
|
|
if(t.type == TOK_THEN || t.type == TOK_DO) {
|
|
depth++;
|
|
} else if(t.type == TOK_END) {
|
|
depth--;
|
|
if(depth == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
e->table_last_token = P->i - 1;
|
|
} else if(maybe(P, TOK_SQUIGGLY_L)) {
|
|
e = new_expr(0);
|
|
e->kind = EX_TBL_LIT;
|
|
e->table_first_token = P->i - 1;
|
|
|
|
size_t depth = 1;
|
|
while(1) {
|
|
Token t = get(P);
|
|
if(t.type == TOK_SQUIGGLY_L) {
|
|
depth++;
|
|
} else if(t.type == TOK_SQUIGGLY_R) {
|
|
depth--;
|
|
if(depth == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
e->table_last_token = P->i - 1;
|
|
}
|
|
|
|
if(e) {
|
|
while(maybe(P, TOK_PAREN_L) || maybe(P, TOK_DOT) || maybe(P, TOK_SQUAREN_L)) {
|
|
if(peek(P, -1).type == TOK_PAREN_L) {
|
|
Expr *call = new_expr(sizeof(Expr*) + sizeof(Expr*) * 32);
|
|
call->kind = EX_CALL;
|
|
call->sub_count = 1;
|
|
call->subs[0] = e;
|
|
|
|
if(!maybe(P, TOK_PAREN_R)) {
|
|
while(1) {
|
|
call->subs[call->sub_count++] = desc_exp(P);
|
|
|
|
if(maybe(P, TOK_PAREN_R)) {
|
|
break;
|
|
} else {
|
|
expect(P, TOK_COMMA);
|
|
}
|
|
}
|
|
}
|
|
|
|
e = call;
|
|
} else if(peek(P, -1).type == TOK_DOT) {
|
|
Expr *dot = new_expr(0);
|
|
dot->kind = EX_DOT;
|
|
dot->A = e;
|
|
dot->B_tok = expect(P, TOK_NAME);
|
|
|
|
e = dot;
|
|
} else if(peek(P, -1).type == TOK_SQUAREN_L) {
|
|
Expr *index = new_expr(0);
|
|
index->kind = EX_INDEX;
|
|
index->A = e;
|
|
index->B = desc_exp(P);
|
|
|
|
expect(P, TOK_SQUAREN_R);
|
|
|
|
e = index;
|
|
}
|
|
}
|
|
|
|
return e;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
Expr *desc_exp(Parser *P) {
|
|
return desc_subexp(P, 0);
|
|
}
|
|
|
|
/*void parse_exp_priority(Parser *P, int assigned_vreg, int priority) {
|
|
if(priority == 0) {
|
|
parse_exp_priority(P, assigned_vreg, priority + 1);
|
|
|
|
while(peek(P, 0).type == TOK_DOUBLE_EQUAL) {
|
|
int vreg = find_vreg(P);
|
|
|
|
Token op = get(P);
|
|
|
|
alloc_vreg(P, vreg);
|
|
parse_exp_priority(P, vreg, priority + 1);
|
|
free_vreg(P, vreg);
|
|
|
|
if(op.type == TOK_DOUBLE_EQUAL) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_COND_EQ, .a = assigned_vreg, .b = assigned_vreg, .c = vreg});
|
|
}
|
|
}
|
|
} else if(priority == 1) {
|
|
parse_exp_priority(P, assigned_vreg, priority + 1);
|
|
|
|
while(peek(P, 0).type == TOK_MUL || peek(P, 0).type == TOK_DIV || peek(P, 0).type == TOK_PERCENT || peek(P, 0).type == TOK_IDIV) {
|
|
int vreg = find_vreg(P);
|
|
|
|
Token op = get(P);
|
|
|
|
alloc_vreg(P, vreg);
|
|
parse_exp_priority(P, vreg, priority + 1);
|
|
free_vreg(P, vreg);
|
|
|
|
if(op.type == TOK_MUL) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_MUL, .a = assigned_vreg, .b = assigned_vreg, .c = vreg});
|
|
} else if(op.type == TOK_DIV) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_DIV, .a = assigned_vreg, .b = assigned_vreg, .c = vreg});
|
|
} else if(op.type == TOK_PERCENT) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_MOD, .a = assigned_vreg, .b = assigned_vreg, .c = vreg});
|
|
} else {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_IDIV, .a = assigned_vreg, .b = assigned_vreg, .c = vreg});
|
|
}
|
|
}
|
|
} else if(priority == 2) {
|
|
parse_exp_priority(P, assigned_vreg, priority + 1);
|
|
|
|
while(peek(P, 0).type == TOK_PLUS || peek(P, 0).type == TOK_MINUS) {
|
|
int vreg = find_vreg(P);
|
|
|
|
Token op = get(P);
|
|
|
|
alloc_vreg(P, vreg);
|
|
parse_exp_priority(P, vreg, priority + 1);
|
|
free_vreg(P, vreg);
|
|
|
|
if(op.type == TOK_PLUS) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_ADD, .a = assigned_vreg, .b = assigned_vreg, .c = vreg});
|
|
} else {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SUB, .a = assigned_vreg, .b = assigned_vreg, .c = vreg});
|
|
}
|
|
}
|
|
} else if(priority == 3) {
|
|
if(maybe(P, TOK_TRUE)) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETBOOL, .a = assigned_vreg, .b = true});
|
|
} else if(maybe(P, TOK_FALSE)) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETBOOL, .a = assigned_vreg, .b = false});
|
|
} else if(maybe(P, TOK_NIL)) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETNIL, .a = assigned_vreg});
|
|
} else if(maybe(P, TOK_SQUIGGLY_L)) {
|
|
expect(P, TOK_SQUIGGLY_R);
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETTABLE, .a = assigned_vreg, .bc = 8});
|
|
} else if(peek(P, 0).type == TOK_NAME) {
|
|
Token name = expect(P, TOK_NAME);
|
|
|
|
ScopeItem *si = scope_find(P->scope, name.text);
|
|
|
|
if(!si) {
|
|
size_t abyss_idx = abyss_insert(P, NULL, sizeof(uint16_t) + strlen(name.text));
|
|
*(uint16_t*) &P->abyss[abyss_idx] = strlen(name.text);
|
|
memcpy(&P->abyss[abyss_idx + sizeof(uint16_t)], name.text, strlen(name.text));
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_GETGLOBAL, .a = assigned_vreg, .b = abyss_idx});
|
|
} else {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_MOVE, .a = assigned_vreg, .b = si->vreg});
|
|
}
|
|
} else if(peek(P, 0).type == TOK_NUMBER) {
|
|
Token num = expect(P, TOK_NUMBER);
|
|
|
|
long i = strtol(num.text, NULL, 10);
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETINT16, .a = assigned_vreg, .bc = i});
|
|
} else if(peek(P, 0).type == TOK_STRING) {
|
|
Token str = expect(P, TOK_STRING);
|
|
|
|
size_t abyss_idx = abyss_insert(P, NULL, sizeof(uint16_t) + strlen(str.text));
|
|
*(uint16_t*) &P->abyss[abyss_idx] = strlen(str.text);
|
|
memcpy(&P->abyss[abyss_idx + sizeof(uint16_t)], str.text, strlen(str.text));
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETSTR, .a = assigned_vreg, .bc = abyss_idx});
|
|
} else {
|
|
assert(false);
|
|
}
|
|
}
|
|
}*/
|
|
|
|
void emit_expr(Parser *P, int assigned_vreg, Expr *expr) {
|
|
if(expr->kind == EX_LOCAL) {
|
|
ScopeItem *si = scope_find(P->scope, expr->name.text);
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_MOVE, .a = assigned_vreg, .b = si->vreg});
|
|
} else if(expr->kind == EX_GLOBAL) {
|
|
size_t abyss_idx = abyss_insert(P, NULL, sizeof(uint16_t) + strlen(expr->name.text));
|
|
*(uint16_t*) &P->abyss[abyss_idx] = strlen(expr->name.text);
|
|
memcpy(&P->abyss[abyss_idx + sizeof(uint16_t)], expr->name.text, strlen(expr->name.text));
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_GETGLOBAL, .a = assigned_vreg, .b = abyss_idx});
|
|
} else if(expr->kind == EX_INT) {
|
|
if(expr->i <= 32767 && expr->i >= -32768) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETINT16, .a = assigned_vreg, .bc = expr->i});
|
|
} else {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETINT32, .a = assigned_vreg, .bc = abyss_insert(P, (uint8_t*) &expr->i, 4)});
|
|
}
|
|
} else if(expr->kind == EX_FLOAT) {
|
|
assert(false);
|
|
} else if(expr->kind == EX_BOOL) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETBOOL, .a = assigned_vreg, .bc = expr->b});
|
|
} else if(expr->kind == EX_STR) {
|
|
size_t abyss_idx = abyss_insert(P, NULL, sizeof(uint16_t) + strlen(expr->name.text));
|
|
*(uint16_t*) &P->abyss[abyss_idx] = strlen(expr->name.text);
|
|
memcpy(&P->abyss[abyss_idx + sizeof(uint16_t)], expr->name.text, strlen(expr->name.text));
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETSTR, .a = assigned_vreg, .bc = abyss_idx});
|
|
} else if(expr->kind == EX_CALL) {
|
|
int vreg = find_vreg(P);
|
|
assert(vreg != -1);
|
|
|
|
emit_expr(P, vreg, expr->subs[0]);
|
|
alloc_vreg(P, vreg);
|
|
|
|
uint8_t buf[258] = {};
|
|
|
|
uint8_t *ret_vreg = buf + 0;
|
|
uint8_t *arg_count = buf + 1;
|
|
uint8_t *args = buf + 2;
|
|
|
|
for(size_t i = 1; i < expr->sub_count; i++) {
|
|
int av = find_vreg(P);
|
|
emit_expr(P, av, expr->subs[i]);
|
|
args[(*arg_count)++] = av;
|
|
alloc_vreg(P, av);
|
|
}
|
|
|
|
*ret_vreg = find_vreg(P);
|
|
|
|
free_vreg(P, vreg);
|
|
for(int i = 0; i < *arg_count; i++) {
|
|
free_vreg(P, args[i]);
|
|
}
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_CALL, .a = vreg, .bc = abyss_insert(P, buf, 2 + *arg_count)});
|
|
} else if(expr->kind == EX_FUNC_LIT) {
|
|
size_t old_idx = P->i;
|
|
|
|
P->i = expr->table_first_token;
|
|
|
|
expect(P, TOK_FUNCTION);
|
|
|
|
expect(P, TOK_PAREN_L);
|
|
|
|
Chunk old_chunk = P->current_chunk;
|
|
P->current_chunk = (Chunk) {};
|
|
|
|
Scope *new_scope = calloc(1, sizeof(*new_scope));
|
|
new_scope->parent = P->scope;
|
|
P->scope = new_scope;
|
|
|
|
size_t arg_count = 0;
|
|
if(!maybe(P, TOK_PAREN_R)) {
|
|
while(1) {
|
|
expect(P, TOK_NAME);
|
|
|
|
P->i--;
|
|
|
|
ScopeItem *si = calloc(1, sizeof(*si));
|
|
si->name = expect(P, TOK_NAME);
|
|
si->vreg = arg_count++;
|
|
scope_set_direct(P->scope, si);
|
|
|
|
if(maybe(P, TOK_PAREN_R)) {
|
|
break;
|
|
} else {
|
|
expect(P, TOK_COMMA);
|
|
}
|
|
}
|
|
}
|
|
|
|
parse_chunk(P);
|
|
if(P->current_chunk.instrs.data[P->current_chunk.instrs.size - 1].opcode != L_RET) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_RET, .argb = {0}});
|
|
}
|
|
|
|
expect(P, TOK_END);
|
|
|
|
assert(P->unit_functions.size > 0);
|
|
|
|
LFunc lf = {};
|
|
lf.unit = P->unit_functions.data[0].unit;
|
|
lf.is_native = false;
|
|
lf.lua_instrs = P->current_chunk.instrs.data;
|
|
lf.env = P->environment;
|
|
vec_LFunc_push(&P->unit_functions, lf);
|
|
|
|
size_t function_idx = P->unit_functions.size - 1;
|
|
|
|
P->current_chunk = old_chunk;
|
|
scope_kill(P);
|
|
|
|
P->i = old_idx;
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETFUNC, .a = assigned_vreg, .bc = function_idx});
|
|
} else if(expr->kind == EX_TBL_LIT) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETTABLE, .a = assigned_vreg, .bc = 16});
|
|
|
|
size_t old_idx = P->i;
|
|
P->i = expr->table_first_token;
|
|
|
|
expect(P, TOK_SQUIGGLY_L);
|
|
|
|
alloc_vreg(P, assigned_vreg);
|
|
|
|
if(!maybe(P, TOK_SQUIGGLY_R)) {
|
|
while(1) {
|
|
int keyv = find_vreg(P);
|
|
assert(keyv != -1);
|
|
if(peek(P, 0).type == TOK_NAME) {
|
|
Token name = get(P);
|
|
|
|
size_t abyss_idx = abyss_insert(P, NULL, sizeof(uint16_t) + strlen(name.text));
|
|
*(uint16_t*) &P->abyss[abyss_idx] = strlen(name.text);
|
|
memcpy(&P->abyss[abyss_idx + sizeof(uint16_t)], name.text, strlen(name.text));
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETSTR, .a = keyv, .bc = abyss_idx});
|
|
} else {
|
|
expect(P, TOK_SQUAREN_L);
|
|
emit_expr(P, keyv, desc_exp(P));
|
|
expect(P, TOK_SQUAREN_R);
|
|
}
|
|
|
|
expect(P, TOK_EQUAL);
|
|
|
|
alloc_vreg(P, keyv);
|
|
int valv = find_vreg(P);
|
|
emit_expr(P, valv, desc_exp(P));
|
|
free_vreg(P, keyv);
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETFIELD, .a = assigned_vreg, .b = keyv, .c = valv});
|
|
|
|
if(maybe(P, TOK_SQUIGGLY_R)) {
|
|
break;
|
|
} else {
|
|
expect(P, TOK_COMMA);
|
|
}
|
|
}
|
|
}
|
|
|
|
free_vreg(P, assigned_vreg);
|
|
|
|
P->i = old_idx;
|
|
} else if(expr->kind == EX_DOT) {
|
|
Token field = expr->B_tok;
|
|
|
|
emit_expr(P, assigned_vreg, expr->A);
|
|
alloc_vreg(P, assigned_vreg);
|
|
|
|
size_t abyss_idx = abyss_insert(P, NULL, sizeof(uint16_t) + strlen(field.text));
|
|
*(uint16_t*) &P->abyss[abyss_idx] = strlen(field.text);
|
|
memcpy(&P->abyss[abyss_idx + sizeof(uint16_t)], field.text, strlen(field.text));
|
|
|
|
int keyv = find_vreg(P);
|
|
assert(keyv != -1);
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETSTR, .a = keyv, .bc = abyss_idx});
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_GETFIELD, .a = assigned_vreg, .b = assigned_vreg, .c = keyv});
|
|
|
|
free_vreg(P, assigned_vreg);
|
|
} else if(expr->kind == EX_INDEX) {
|
|
emit_expr(P, assigned_vreg, expr->A);
|
|
|
|
alloc_vreg(P, assigned_vreg);
|
|
int keyv = find_vreg(P);
|
|
emit_expr(P, keyv, expr->B);
|
|
free_vreg(P, assigned_vreg);
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_GETFIELD, .a = assigned_vreg, .b = assigned_vreg, .c = keyv});
|
|
} else if(expr->kind == EX_LENGTH) {
|
|
emit_expr(P, assigned_vreg, expr->A);
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_LEN, .a = assigned_vreg, .b = assigned_vreg, .c = 0});
|
|
} else {
|
|
assert(expr->kind == EX_ADD || expr->kind == EX_SUB || expr->kind == EX_MUL || expr->kind == EX_DIV || expr->kind == EX_IDIV || expr->kind == EX_MOD || expr->kind == EX_POW || expr->kind == EX_BAND || expr->kind == EX_BOR || expr->kind == EX_BXOR || expr->kind == EX_AND || expr->kind == EX_OR || expr->kind == EX_EQ || expr->kind == EX_NEQ);
|
|
|
|
assert(expr->A);
|
|
assert(expr->B);
|
|
|
|
emit_expr(P, assigned_vreg, expr->A);
|
|
|
|
alloc_vreg(P, assigned_vreg);
|
|
int b_vreg = find_vreg(P);
|
|
emit_expr(P, b_vreg, expr->B);
|
|
free_vreg(P, assigned_vreg);
|
|
|
|
uint8_t opcode;
|
|
switch(expr->kind) {
|
|
case EX_ADD: opcode = L_ADD; break;
|
|
case EX_SUB: opcode = L_SUB; break;
|
|
case EX_MUL: opcode = L_MUL; break;
|
|
case EX_DIV: opcode = L_DIV; break;
|
|
case EX_IDIV: opcode = L_IDIV; break;
|
|
case EX_MOD: opcode = L_MOD; break;
|
|
case EX_POW: opcode = L_POW; break;
|
|
case EX_EQ: opcode = L_COND_EQ; break;
|
|
case EX_NEQ: opcode = L_COND_NEQ; break;
|
|
default: assert(false);
|
|
}
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = opcode, .a = assigned_vreg, .b = assigned_vreg, .c = b_vreg});
|
|
}
|
|
}
|
|
|
|
int parse_exp(Parser *P, int assigned_vreg) {
|
|
bool owned = false;
|
|
if(assigned_vreg == -1) {
|
|
owned = true;
|
|
|
|
assigned_vreg = find_vreg(P);
|
|
alloc_vreg(P, assigned_vreg);
|
|
}
|
|
|
|
emit_expr(P, assigned_vreg, desc_exp(P));
|
|
|
|
if(owned) {
|
|
free_vreg(P, assigned_vreg);
|
|
}
|
|
|
|
return assigned_vreg;
|
|
}
|
|
|
|
bool parse_assignment(Parser *P) {
|
|
size_t old_idx = P->i;
|
|
|
|
const size_t max_exprs = 8;
|
|
|
|
Expr *lhs[max_exprs];
|
|
size_t lhsi = 0;
|
|
|
|
Expr *rhs[max_exprs];
|
|
int rhsv[max_exprs];
|
|
size_t rhsi = 0;
|
|
|
|
while(1) {
|
|
lhs[lhsi++] = desc_exp(P);
|
|
|
|
if(!lhs[lhsi - 1]) {
|
|
goto err;
|
|
}
|
|
|
|
if(lhs[lhsi - 1]->kind != EX_LOCAL && lhs[lhsi - 1]->kind != EX_GLOBAL && lhs[lhsi - 1]->kind != EX_DOT && lhs[lhsi - 1]->kind != EX_INDEX) {
|
|
goto err;
|
|
}
|
|
|
|
if(maybe(P, TOK_EQUAL)) {
|
|
break;
|
|
} else {
|
|
expect(P, TOK_COMMA);
|
|
}
|
|
}
|
|
|
|
rhs[rhsi++] = desc_exp(P);
|
|
|
|
while(1) {
|
|
if(!maybe(P, TOK_COMMA)) {
|
|
break;
|
|
}
|
|
|
|
rhs[rhsi++] = desc_exp(P);
|
|
}
|
|
|
|
for(size_t i = 0; i < rhsi; i++) {
|
|
rhsv[i] = find_vreg(P);
|
|
emit_expr(P, rhsv[i], rhs[i]);
|
|
alloc_vreg(P, rhsv[i]);
|
|
}
|
|
|
|
assert(rhsi == lhsi);
|
|
|
|
for(size_t i = 0; i < lhsi; i++) {
|
|
if(lhs[i]->kind == EX_LOCAL) {
|
|
ScopeItem *si = scope_find(P->scope, lhs[i]->name.text);
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_MOVE, .a = si->vreg, .b = rhsv[i]});
|
|
} else if(lhs[i]->kind == EX_GLOBAL) {
|
|
size_t abyss_idx = abyss_insert(P, NULL, sizeof(uint16_t) + strlen(lhs[i]->name.text));
|
|
*(uint16_t*) &P->abyss[abyss_idx] = strlen(lhs[i]->name.text);
|
|
memcpy(&P->abyss[abyss_idx + sizeof(uint16_t)], lhs[i]->name.text, strlen(lhs[i]->name.text));
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETGLOBAL, .a = rhsv[i], .bc = abyss_idx});
|
|
} else if(lhs[i]->kind == EX_INDEX) {
|
|
int lhsv = find_vreg(P);
|
|
assert(lhsv != -1);
|
|
emit_expr(P, lhsv, lhs[i]->A);
|
|
alloc_vreg(P, lhsv);
|
|
|
|
int keyv = find_vreg(P);
|
|
assert(keyv != -1);
|
|
emit_expr(P, keyv, lhs[i]->B);
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETFIELD, .a = lhsv, .b = keyv, .c = rhsv[i]});
|
|
|
|
free_vreg(P, lhsv);
|
|
} else if(lhs[i]->kind == EX_DOT) {
|
|
int lhsv = find_vreg(P);
|
|
assert(lhsv != -1);
|
|
emit_expr(P, lhsv, lhs[i]->A);
|
|
alloc_vreg(P, lhsv);
|
|
|
|
Token field = lhs[i]->B_tok;
|
|
|
|
size_t abyss_idx = abyss_insert(P, NULL, sizeof(uint16_t) + strlen(field.text));
|
|
*(uint16_t*) &P->abyss[abyss_idx] = strlen(field.text);
|
|
memcpy(&P->abyss[abyss_idx + sizeof(uint16_t)], field.text, strlen(field.text));
|
|
|
|
int keyv = find_vreg(P);
|
|
assert(keyv != -1);
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETSTR, .a = keyv, .bc = abyss_idx});
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETFIELD, .a = lhsv, .b = keyv, .c = rhsv[i]});
|
|
|
|
free_vreg(P, lhsv);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
for(size_t i = 0; i < rhsi; i++) {
|
|
free_vreg(P, rhsv[i]);
|
|
}
|
|
|
|
return true;
|
|
err:
|
|
P->i = old_idx;
|
|
return false;
|
|
}
|
|
|
|
void parse_chunk(Parser *P);
|
|
|
|
bool parse_stat(Parser *P) {
|
|
if(parse_functiondef(P, true) != -1) {
|
|
} else if(maybe(P, TOK_LOCAL)) {
|
|
vec_Token names = parse_namelist(P);
|
|
|
|
int i = 0;
|
|
|
|
if(maybe(P, TOK_EQUAL)) {
|
|
do {
|
|
int vreg = parse_exp(P, -1);
|
|
|
|
if(i < names.size) {
|
|
alloc_vreg(P, vreg);
|
|
|
|
ScopeItem *si = calloc(1, sizeof(*si));
|
|
*si = (ScopeItem) {.name = names.data[i], .vreg = vreg};
|
|
|
|
scope_set_direct(P->scope, si);
|
|
}
|
|
|
|
i++;
|
|
} while(maybe(P, TOK_COMMA));
|
|
}
|
|
|
|
for(; i < names.size; i++) {
|
|
int vreg = find_vreg(P);
|
|
alloc_vreg(P, vreg);
|
|
|
|
ScopeItem *si = calloc(1, sizeof(*si));
|
|
*si = (ScopeItem) {.name = names.data[i], .vreg = vreg};
|
|
|
|
scope_set_direct(P->scope, si);
|
|
}
|
|
|
|
vec_Token_drop(&names);
|
|
} else if(maybe(P, TOK_IF)) {
|
|
int vreg = parse_exp(P, -1);
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_JNOTCOND, .a = vreg, .bc = 0});
|
|
size_t jump2else = P->current_chunk.instrs.size - 1;
|
|
|
|
expect(P, TOK_THEN);
|
|
|
|
parse_chunk(P);
|
|
|
|
bool isElse = maybe(P, TOK_ELSE);
|
|
|
|
size_t jump2end;
|
|
if(isElse) {
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_JUMP, .a = 0, .bc = 0});
|
|
jump2end = P->current_chunk.instrs.size - 1;
|
|
}
|
|
|
|
P->current_chunk.instrs.data[jump2else].bc = P->current_chunk.instrs.size - 1 - jump2else;
|
|
|
|
if(isElse) {
|
|
parse_chunk(P);
|
|
P->current_chunk.instrs.data[jump2end].bc = P->current_chunk.instrs.size - 1 - jump2end;
|
|
}
|
|
|
|
expect(P, TOK_END);
|
|
} else if(maybe(P, TOK_WHILE)) {
|
|
Expr *condition = desc_exp(P);
|
|
|
|
int vreg = find_vreg(P);
|
|
assert(vreg != -1);
|
|
|
|
size_t start_idx = P->current_chunk.instrs.size;
|
|
|
|
emit_expr(P, vreg, condition);
|
|
|
|
size_t jump2end = P->current_chunk.instrs.size;
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_JNOTCOND, .a = vreg, .bc = 0});
|
|
|
|
Scope *new_scope = calloc(1, sizeof(*new_scope));
|
|
new_scope->parent = P->scope;
|
|
|
|
expect(P, TOK_DO);
|
|
parse_chunk(P);
|
|
expect(P, TOK_END);
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_JUMP, .a = 0, .bc = start_idx - P->current_chunk.instrs.size - 1});
|
|
|
|
P->current_chunk.instrs.data[jump2end].bc = P->current_chunk.instrs.size - 1 - jump2end;
|
|
|
|
scope_kill(P);
|
|
} else if(maybe(P, TOK_FOR)) {
|
|
if(peek(P, 0).type == TOK_NAME && peek(P, 1).type == TOK_EQUAL) {
|
|
// Range loop
|
|
|
|
Token name = expect(P, TOK_NAME);
|
|
|
|
expect(P, TOK_EQUAL);
|
|
|
|
int fromVreg = parse_exp(P, -1);
|
|
alloc_vreg(P, fromVreg);
|
|
|
|
expect(P, TOK_COMMA);
|
|
|
|
int toVreg = parse_exp(P, -1);
|
|
alloc_vreg(P, toVreg);
|
|
|
|
int stepVreg;
|
|
if(maybe(P, TOK_COMMA)) {
|
|
stepVreg = parse_exp(P, -1);
|
|
} else {
|
|
stepVreg = find_vreg(P);
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SETINT16, .a = stepVreg, .bc = 1});
|
|
}
|
|
alloc_vreg(P, stepVreg);
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_SUB, .a = fromVreg, .b = fromVreg, .c = stepVreg});
|
|
|
|
expect(P, TOK_DO);
|
|
|
|
Scope *new_scope = calloc(1, sizeof(*new_scope));
|
|
new_scope->parent = P->scope;
|
|
|
|
ScopeItem *si = calloc(1, sizeof(*si));
|
|
si->name = name;
|
|
si->vreg = fromVreg;
|
|
scope_set_direct(new_scope, si);
|
|
|
|
P->scope = new_scope;
|
|
|
|
size_t start_idx = P->current_chunk.instrs.size;
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_ADD, .a = fromVreg, .b = fromVreg, .c = stepVreg});
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_ADVANCETEST, .a = fromVreg, .b = toVreg, .c = stepVreg});
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_JUMP, .a = 0, .bc = 0});
|
|
size_t jump2end = P->current_chunk.instrs.size - 1;
|
|
|
|
parse_chunk(P);
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_JUMP, .a = 0, .bc = start_idx - P->current_chunk.instrs.size - 1});
|
|
|
|
P->current_chunk.instrs.data[jump2end].bc = P->current_chunk.instrs.size - 1 - jump2end;
|
|
|
|
scope_kill(P);
|
|
|
|
expect(P, TOK_END);
|
|
|
|
free_vreg(P, fromVreg);
|
|
free_vreg(P, toVreg);
|
|
free_vreg(P, stepVreg);
|
|
} else {
|
|
assert(0 && "Iterator for loop not yet supported");
|
|
}
|
|
} else if(maybe(P, TOK_RETURN)) {
|
|
int vreg = parse_exp(P, -1);
|
|
|
|
vec_LInst_push(&P->current_chunk.instrs, (LInst) {.opcode = L_RET, .argb = {vreg}});
|
|
|
|
return false;
|
|
} else if(peek(P, 0).type == TOK_END || peek(P, 0).type == TOK_ELSE || peek(P, 0).type == TOK_ELSEIF || peek(P, 0).type == TOK_EOF) {
|
|
return false;
|
|
} else if(parse_functioncall(P)) {
|
|
} else if(parse_assignment(P)) {
|
|
} else {
|
|
assert(false);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void parse_chunk(Parser *P) {
|
|
while(parse_stat(P));
|
|
}
|
|
|
|
LUnit *lparse(size_t sz, Token *tokens, LTable *environment) {
|
|
LUnit *unit = calloc(1, sizeof(*unit));
|
|
|
|
Parser P = {
|
|
.i = 0,
|
|
.sz = sz,
|
|
.tokens = tokens,
|
|
|
|
.environment = environment,
|
|
|
|
.unit_functions = {},
|
|
|
|
.scope = calloc(1, sizeof(Scope)),
|
|
};
|
|
|
|
LFunc func = {};
|
|
func.unit = unit;
|
|
func.is_native = false;
|
|
func.upvalue_count = 0;
|
|
func.env = environment;
|
|
vec_LFunc_push(&P.unit_functions, func);
|
|
|
|
parse_chunk(&P);
|
|
if(P.current_chunk.instrs.data[P.current_chunk.instrs.size - 1].opcode != L_RET) {
|
|
vec_LInst_push(&P.current_chunk.instrs, (LInst) {.opcode = L_RET, .argb = {0}});
|
|
}
|
|
|
|
P.unit_functions.data[0].lua_instrs = P.current_chunk.instrs.data;
|
|
|
|
unit->abyss = P.abyss;
|
|
unit->func_count = 1;
|
|
unit->funcs = P.unit_functions.data;
|
|
|
|
for(Expr *e = last_desc; e;) {
|
|
Expr *n = e->next_to_die;
|
|
free(e);
|
|
e = n;
|
|
}
|
|
last_desc = NULL;
|
|
|
|
return unit;
|
|
}
|