adding compilation of if-else statements
This commit is contained in:
+23
-5
@@ -48,12 +48,15 @@ static const InstrInfo instr_table[] = {
|
||||
[OPCODE_MUL] = {"MUL", 2, 0, NULL},
|
||||
[OPCODE_DIV] = {"DIV", 2, 0, NULL},
|
||||
|
||||
[OPCODE_PUSHI] = {"PUSHI", 2, 1, (OperandType[]) {OPTP_INT}},
|
||||
[OPCODE_PUSHF] = {"PUSHF", 2, 1, (OperandType[]) {OPTP_FLOAT}},
|
||||
[OPCODE_PUSHS] = {"PUSHS", 2, 1, (OperandType[]) {OPTP_STRING}},
|
||||
[OPCODE_PUSHV] = {"PUSHV", 2, 1, (OperandType[]) {OPTP_STRING}},
|
||||
[OPCODE_PUSHI] = {"PUSHI", 1, 1, (OperandType[]) {OPTP_INT}},
|
||||
[OPCODE_PUSHF] = {"PUSHF", 1, 1, (OperandType[]) {OPTP_FLOAT}},
|
||||
[OPCODE_PUSHS] = {"PUSHS", 1, 1, (OperandType[]) {OPTP_STRING}},
|
||||
[OPCODE_PUSHV] = {"PUSHV", 1, 1, (OperandType[]) {OPTP_STRING}},
|
||||
|
||||
[OPCODE_RETURN] = {"RETURN", 0, 0, NULL},
|
||||
|
||||
[OPCODE_JUMPIFNOTANDPOP] = {"JUMPIFNOTANDPOP", 1, 1, (OperandType[]) {OPTP_INT}},
|
||||
[OPCODE_JUMP] = {"JUMP", 0, 1, (OperandType[]) {OPTP_INT}},
|
||||
};
|
||||
|
||||
static const char *operand_type_names[] = {
|
||||
@@ -362,4 +365,19 @@ _Bool ExeBuilder_Append(ExeBuilder *exeb, Error *error, Opcode opcode, Operand *
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
BPAlloc *ExeBuilder_GetAlloc(ExeBuilder *exeb)
|
||||
{
|
||||
return BucketList_GetAlloc(exeb->code);
|
||||
}
|
||||
|
||||
int ExeBuilder_InstrCount(ExeBuilder *exeb)
|
||||
{
|
||||
int raw_size = BucketList_Size(exeb->code);
|
||||
|
||||
assert(raw_size % sizeof(Instruction) == 0);
|
||||
|
||||
return raw_size / sizeof(Instruction);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ typedef enum {
|
||||
OPCODE_PUSHS,
|
||||
OPCODE_PUSHV,
|
||||
OPCODE_RETURN,
|
||||
OPCODE_JUMPIFNOTANDPOP,
|
||||
OPCODE_JUMP,
|
||||
} Opcode;
|
||||
|
||||
typedef struct xExecutable Executable;
|
||||
@@ -47,4 +49,6 @@ Source *Executable_GetSource(Executable *exe);
|
||||
ExeBuilder *ExeBuilder_New(BPAlloc *alloc);
|
||||
_Bool ExeBuilder_Append(ExeBuilder *exeb, Error *error, Opcode opcode, Operand *opv, int opc, int off, int len);
|
||||
Executable *ExeBuilder_Finalize(ExeBuilder *exeb, Error *error);
|
||||
BPAlloc *ExeBuilder_GetAlloc(ExeBuilder *exeb);
|
||||
int ExeBuilder_InstrCount(ExeBuilder *exeb);
|
||||
#endif
|
||||
+71
-2
@@ -42,7 +42,8 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
||||
OperExprNode *oper = (OperExprNode*) expr;
|
||||
|
||||
for(Node *operand = oper->head; operand; operand = operand->next)
|
||||
emit_instr_for_node(exeb, operand, error);
|
||||
if(!emit_instr_for_node(exeb, operand, error))
|
||||
return 0;
|
||||
|
||||
return ExeBuilder_Append(exeb, error,
|
||||
exprkind_to_opcode(expr->kind),
|
||||
@@ -84,10 +85,78 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
||||
break;
|
||||
}
|
||||
|
||||
case NODE_IFELSE:
|
||||
{
|
||||
IfElseNode *ifelse = (IfElseNode*) node;
|
||||
|
||||
if(!emit_instr_for_node(exeb, ifelse->condition, error))
|
||||
return 0;
|
||||
|
||||
if(ifelse->false_branch)
|
||||
{
|
||||
Promise *else_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
Promise *done_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
|
||||
if(else_offset == NULL || done_offset == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Operand op = { .type = OPTP_PROMISE, .as_promise = else_offset };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &op, 1, node->offset, node->length))
|
||||
return 0;
|
||||
|
||||
if(!emit_instr_for_node(exeb, ifelse->true_branch, error))
|
||||
return 0;
|
||||
|
||||
op = (Operand) { .type = OPTP_PROMISE, .as_promise = done_offset };
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMP, &op, 1, node->offset, node->length))
|
||||
return 0;
|
||||
|
||||
long long int temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(else_offset, &temp, sizeof(temp));
|
||||
|
||||
if(!emit_instr_for_node(exeb, ifelse->false_branch, error))
|
||||
return 0;
|
||||
|
||||
temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(done_offset, &temp, sizeof(temp));
|
||||
|
||||
Promise_Free(else_offset);
|
||||
Promise_Free(done_offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
Promise *done_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
|
||||
|
||||
if(done_offset == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &(Operand) { .type = OPTP_PROMISE, .as_promise = done_offset }, 1, node->offset, node->length))
|
||||
return 0;
|
||||
|
||||
if(!emit_instr_for_node(exeb, ifelse->true_branch, error))
|
||||
return 0;
|
||||
|
||||
long long int temp = ExeBuilder_InstrCount(exeb);
|
||||
Promise_Resolve(done_offset, &temp, sizeof(temp));
|
||||
|
||||
Promise_Free(done_offset);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
UNREACHABLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Executable *compile(AST *ast, BPAlloc *alloc, Error *error)
|
||||
|
||||
+30
-14
@@ -369,7 +369,7 @@ static Node *parse_string_primary_expression(Context *ctx)
|
||||
assert(ctx != NULL);
|
||||
assert(ctx->token->kind == TSTRING);
|
||||
|
||||
if(current(ctx) == TDONE)
|
||||
if(done(ctx))
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Source ended where a string literal was expected");
|
||||
return NULL;
|
||||
@@ -493,6 +493,12 @@ static Node *parse_primary_expresion(Context *ctx)
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
|
||||
if(done(ctx))
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Source ended where a primary expression was expected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch(current(ctx))
|
||||
{
|
||||
case '+':
|
||||
@@ -532,7 +538,7 @@ static Node *parse_primary_expresion(Context *ctx)
|
||||
if(node == NULL)
|
||||
return NULL;
|
||||
|
||||
if(current(ctx) == TDONE)
|
||||
if(done(ctx))
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Source ended before \")\", after sub-expression");
|
||||
return NULL;
|
||||
@@ -823,34 +829,44 @@ static Node *parse_ifelse_statement(Context *ctx)
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
assert(ctx->token != NULL);
|
||||
assert(ctx->token->kind == TKWIF);
|
||||
|
||||
ctx->token = ctx->token->next;
|
||||
|
||||
if(ctx->token->kind == TDONE)
|
||||
if(done(ctx))
|
||||
{
|
||||
// ERROR: Source ended after if keyword.
|
||||
// An expression was expected.
|
||||
Error_Report(ctx->error, 0, "Source ended after if keyword. An expression was expected");
|
||||
Error_Report(ctx->error, 0, "Source ended where an if-else statement was expected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(current(ctx) != TKWIF)
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where an if-else statement was expected", ctx->token->length, ctx->src + ctx->token->offset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Token *if_token = current_token(ctx);
|
||||
assert(if_token != NULL);
|
||||
|
||||
next(ctx); // Consume the "if" keyword.
|
||||
|
||||
Node *condition = parse_expression(ctx);
|
||||
if(condition == NULL) return NULL;
|
||||
|
||||
if(condition == NULL)
|
||||
return NULL;
|
||||
|
||||
Node *true_branch = parse_statement(ctx);
|
||||
if(condition == NULL) return NULL;
|
||||
|
||||
if(condition == NULL)
|
||||
return NULL;
|
||||
|
||||
Node *false_branch = NULL;
|
||||
|
||||
if(ctx->token->kind == TKWELSE)
|
||||
{
|
||||
// Consume the "else" token.
|
||||
ctx->token = ctx->token->next;
|
||||
next(ctx); // Consume the "else" token.
|
||||
|
||||
false_branch = parse_statement(ctx);
|
||||
if(false_branch == NULL) return NULL;
|
||||
|
||||
if(false_branch == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
IfElseNode *ifelse;
|
||||
|
||||
@@ -126,6 +126,27 @@ static xj_value *convert_node(Node *node, xj_error *error, xj_alloc **alloc)
|
||||
}
|
||||
break;
|
||||
|
||||
case NODE_IFELSE:
|
||||
{
|
||||
IfElseNode *ifelse = (IfElseNode*) node;
|
||||
|
||||
xj_value *condition = convert_node(ifelse->condition, error, alloc);
|
||||
if(condition == NULL) return NULL;
|
||||
|
||||
xj_value *true_branch = convert_node(ifelse->true_branch, error, alloc);
|
||||
if(true_branch == NULL) return NULL;
|
||||
|
||||
xj_value *false_branch = convert_node(ifelse->false_branch, error, alloc);
|
||||
if(false_branch == NULL) return NULL;
|
||||
|
||||
return xj_decodef(error, alloc,
|
||||
"{"
|
||||
"\t\"node-kind\": \"if-else\", \n"
|
||||
"\t\"condition\": %v, \n"
|
||||
"\t\"true-branch\": %v, \n"
|
||||
"\t\"false-branch\": %v\n"
|
||||
"}", condition, true_branch, false_branch);
|
||||
}
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "objects.h"
|
||||
|
||||
static _Bool to_bool(Object *obj, Error *err);
|
||||
|
||||
static const Type t_int = {
|
||||
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||
.name = "int",
|
||||
.size = sizeof (Object),
|
||||
.atomic = ATMTP_BOOL,
|
||||
.to_bool = to_bool,
|
||||
};
|
||||
|
||||
static Object the_true_object = {
|
||||
.type = t_bool,
|
||||
.flags = Object_STATIC,
|
||||
}
|
||||
|
||||
static Object the_false_object = {
|
||||
.type = t_bool,
|
||||
.flags = Object_STATIC,
|
||||
}
|
||||
|
||||
static _Bool to_bool(Object *obj, Error *err)
|
||||
{
|
||||
assert(obj);
|
||||
assert(err);
|
||||
assert(Object_GetType(obj) == &t_bool);
|
||||
return obj == &the_true_object;
|
||||
}
|
||||
|
||||
Object *Object_FromBool(_Bool val, Heap *heap, Error *error)
|
||||
{
|
||||
(void) heap;
|
||||
(void) error;
|
||||
return val ? &the_true_object : &the_false_object;
|
||||
}
|
||||
@@ -218,6 +218,13 @@ _Bool Object_IsInt(Object *obj)
|
||||
return obj->type->atomic == ATMTP_INT;
|
||||
}
|
||||
|
||||
_Bool Object_IsBool(Object *obj)
|
||||
{
|
||||
assert(obj != NULL);
|
||||
assert(obj->type != NULL);
|
||||
return obj->type->atomic == ATMTP_BOOL;
|
||||
}
|
||||
|
||||
_Bool Object_IsFloat(Object *obj)
|
||||
{
|
||||
assert(obj != NULL);
|
||||
@@ -248,6 +255,29 @@ long long int Object_ToInt(Object *obj, Error *err)
|
||||
return type->to_int(obj, err);
|
||||
}
|
||||
|
||||
_Bool Object_ToBool(Object *obj, Error *err)
|
||||
{
|
||||
assert(err);
|
||||
assert(obj);
|
||||
|
||||
if(!Object_IsBool(obj))
|
||||
{
|
||||
Error_Report(err, 0, "Object is not a boolean");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Type *type = Object_GetType(obj);
|
||||
assert(type);
|
||||
|
||||
if(type->to_bool == NULL)
|
||||
{
|
||||
Error_Report(err, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return type->to_bool(obj, err);
|
||||
}
|
||||
|
||||
double Object_ToFloat(Object *obj, Error *err)
|
||||
{
|
||||
assert(err);
|
||||
|
||||
@@ -15,6 +15,7 @@ struct Object {
|
||||
typedef enum {
|
||||
ATMTP_NOTATOMIC = 0,
|
||||
ATMTP_INT,
|
||||
ATMTP_BOOL,
|
||||
ATMTP_FLOAT,
|
||||
} AtomicType;
|
||||
|
||||
@@ -47,6 +48,7 @@ struct Type {
|
||||
// Some.
|
||||
union {
|
||||
long long int (*to_int)(Object *self, Error *err);
|
||||
_Bool (*to_bool)(Object *self, Error *err);
|
||||
double (*to_float)(Object *self, Error *err);
|
||||
char *(*to_string)(Object *self, int *size, Heap *heap, Error *err);
|
||||
};
|
||||
@@ -81,13 +83,16 @@ Object* Object_NewMap(int num, Heap *heap, Error *error);
|
||||
Object* Object_NewNone(Heap *heap, Error *error);
|
||||
|
||||
Object* Object_FromInt (long long int val, Heap *heap, Error *error);
|
||||
Object* Object_FromBool (_Bool val, Heap *heap, Error *error);
|
||||
Object* Object_FromFloat (double val, Heap *heap, Error *error);
|
||||
Object* Object_FromString(const char *str, int len, Heap *heap, Error *error);
|
||||
|
||||
_Bool Object_IsInt (Object *obj);
|
||||
_Bool Object_IsBool (Object *obj);
|
||||
_Bool Object_IsFloat(Object *obj);
|
||||
|
||||
long long int Object_ToInt (Object *obj, Error *err);
|
||||
_Bool Object_ToBool (Object *obj, Error *err);
|
||||
double Object_ToFloat(Object *obj, Error *err);
|
||||
|
||||
_Bool Object_Compare(Object *obj1, Object *obj2, Error *error);
|
||||
|
||||
@@ -433,6 +433,40 @@ static _Bool step(Runtime *runtime, Error *error)
|
||||
case OPCODE_RETURN:
|
||||
return 0;
|
||||
|
||||
case OPCODE_JUMP:
|
||||
assert(opc == 1);
|
||||
assert(ops[0].type == OPTP_INT);
|
||||
runtime->frame->index = ops[0].as_int;
|
||||
return 1;
|
||||
|
||||
case OPCODE_JUMPIFNOTANDPOP:
|
||||
{
|
||||
assert(opc == 1);
|
||||
assert(ops[0].type == OPTP_INT);
|
||||
|
||||
long long int target = ops[0].as_int;
|
||||
|
||||
if(runtime->frame->used == 0)
|
||||
{
|
||||
Error_Report(error, 1, "Frame doesn't have enough items on the stack to execute JUMPIFNOTANDPOP");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Object *top = Stack_Top(runtime->stack, 0);
|
||||
assert(top != NULL);
|
||||
|
||||
if(!Object_IsBool(top))
|
||||
{
|
||||
Error_Report(error, 1, "JUMPIFNOTANDPOP expected a boolean on the stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(Object_ToBool(top, error)) // This can't fail because we know it's a bool.
|
||||
runtime->frame->index = target;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
return 0;
|
||||
|
||||
@@ -174,4 +174,9 @@ void BucketList_Copy(BucketList *blist, void *dest, int len)
|
||||
copied += copying;
|
||||
bucket = bucket->next;
|
||||
}
|
||||
}
|
||||
|
||||
BPAlloc *BucketList_GetAlloc(BucketList *blist)
|
||||
{
|
||||
return blist->alloc;
|
||||
}
|
||||
@@ -7,4 +7,5 @@ int BucketList_Size(BucketList *blist);
|
||||
void BucketList_Copy(BucketList *blist, void *dest, int len);
|
||||
_Bool BucketList_Append (BucketList *blist, const void *data, int size);
|
||||
void *BucketList_Append2(BucketList *blist, const void *data, int size);
|
||||
BPAlloc *BucketList_GetAlloc(BucketList *blist);
|
||||
#endif
|
||||
+1
-1
@@ -41,7 +41,7 @@ unsigned int Promise_Size(Promise *promise)
|
||||
return promise->size;
|
||||
}
|
||||
|
||||
void Promise_Delete(Promise *promise)
|
||||
void Promise_Free(Promise *promise)
|
||||
{
|
||||
assert(promise->set == 1);
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
typedef struct xPromise Promise;
|
||||
Promise *Promise_New(BPAlloc *alloc, int size);
|
||||
unsigned int Promise_Size(Promise *promise);
|
||||
void Promise_Delete(Promise *promise);
|
||||
void Promise_Free(Promise *promise);
|
||||
void Promise_Resolve(Promise *promise, const void *data, int size);
|
||||
_Bool Promise_Subscribe(Promise *promise, void *dest);
|
||||
_Bool Promise_Subscribe2(Promise *promise, void *dest, void *userp, void (*callback)(void*));
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ Source *Source_FromFile(const char *file, Error *error)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(fseek(fp, 0, SEEK_END))
|
||||
if(fseek(fp, 0, SEEK_SET))
|
||||
{
|
||||
Error_Report(error, 1, "Call to fseek failed (%s, errno = %d)", strerror(errno), errno);
|
||||
fclose(fp);
|
||||
|
||||
Reference in New Issue
Block a user