added function calls, native function objects and runtime builtins
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
USING_VALGRIND=0
|
USING_VALGRIND=0
|
||||||
|
|
||||||
FLAGS="-L3p/libs/ -I3p/include/ -Wall -Wextra -g -DUSING_VALGRIND=$USING_VALGRIND -DTRACE_SOURCE_COPIES=$TRACE_SOURCE_COPIES"
|
FLAGS="-L3p/libs/ -I3p/include/ -Wall -Wextra -g -DUSING_VALGRIND=$USING_VALGRIND"
|
||||||
|
|
||||||
mkdir temp
|
mkdir temp
|
||||||
|
|
||||||
@@ -34,6 +34,8 @@ gcc -c src/common/executable.c -o temp/common/executable.o $FLAGS
|
|||||||
mkdir temp/runtime
|
mkdir temp/runtime
|
||||||
gcc -c src/runtime/runtime_error.c -o temp/runtime/runtime_error.o $FLAGS
|
gcc -c src/runtime/runtime_error.c -o temp/runtime/runtime_error.o $FLAGS
|
||||||
gcc -c src/runtime/runtime.c -o temp/runtime/runtime.o $FLAGS
|
gcc -c src/runtime/runtime.c -o temp/runtime/runtime.o $FLAGS
|
||||||
|
gcc -c src/runtime/builtins.c -o temp/runtime/builtins.o $FLAGS
|
||||||
|
gcc -c src/runtime/o_nfunc.c -o temp/runtime/o_nfunc.o $FLAGS
|
||||||
gcc -c src/runtime/o_func.c -o temp/runtime/o_func.o $FLAGS
|
gcc -c src/runtime/o_func.c -o temp/runtime/o_func.o $FLAGS
|
||||||
|
|
||||||
rm -rf build
|
rm -rf build
|
||||||
@@ -76,6 +78,8 @@ gcc src/main.c src/debug.c \
|
|||||||
temp/objects/o_string.o \
|
temp/objects/o_string.o \
|
||||||
temp/runtime/runtime.o \
|
temp/runtime/runtime.o \
|
||||||
temp/runtime/runtime_error.o \
|
temp/runtime/runtime_error.o \
|
||||||
|
temp/runtime/builtins.o \
|
||||||
|
temp/runtime/o_nfunc.o \
|
||||||
temp/runtime/o_func.o \
|
temp/runtime/o_func.o \
|
||||||
temp/common/executable.o \
|
temp/common/executable.o \
|
||||||
-o build/noja $FLAGS -Lbuild/ -lnoja-compile -lnoja-objects -lxjson
|
-o build/noja $FLAGS -Lbuild/ -lnoja-compile -lnoja-objects -lxjson
|
||||||
|
|||||||
+2
-4
@@ -1,7 +1,5 @@
|
|||||||
|
|
||||||
fun hello()
|
fun hello()
|
||||||
{
|
print('hel\nlo', 2);
|
||||||
print;('hello');
|
|
||||||
}
|
|
||||||
|
|
||||||
return hello;
|
return hello();
|
||||||
@@ -49,7 +49,7 @@ static const InstrInfo instr_table[] = {
|
|||||||
[OPCODE_DIV] = {"DIV", 0, NULL},
|
[OPCODE_DIV] = {"DIV", 0, NULL},
|
||||||
[OPCODE_ASS] = {"ASS", 1, (OperandType[]) {OPTP_STRING}},
|
[OPCODE_ASS] = {"ASS", 1, (OperandType[]) {OPTP_STRING}},
|
||||||
[OPCODE_POP] = {"POP", 1, (OperandType[]) {OPTP_INT}},
|
[OPCODE_POP] = {"POP", 1, (OperandType[]) {OPTP_INT}},
|
||||||
|
[OPCODE_CALL] = {"CALL", 1, (OperandType[]) {OPTP_INT}},
|
||||||
[OPCODE_PUSHINT] = {"PUSHINT", 1, (OperandType[]) {OPTP_INT}},
|
[OPCODE_PUSHINT] = {"PUSHINT", 1, (OperandType[]) {OPTP_INT}},
|
||||||
[OPCODE_PUSHFLT] = {"PUSHFLT", 1, (OperandType[]) {OPTP_FLOAT}},
|
[OPCODE_PUSHFLT] = {"PUSHFLT", 1, (OperandType[]) {OPTP_FLOAT}},
|
||||||
[OPCODE_PUSHSTR] = {"PUSHSTR", 1, (OperandType[]) {OPTP_STRING}},
|
[OPCODE_PUSHSTR] = {"PUSHSTR", 1, (OperandType[]) {OPTP_STRING}},
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ typedef enum {
|
|||||||
OPCODE_DIV,
|
OPCODE_DIV,
|
||||||
OPCODE_ASS,
|
OPCODE_ASS,
|
||||||
OPCODE_POP,
|
OPCODE_POP,
|
||||||
|
OPCODE_CALL,
|
||||||
OPCODE_PUSHINT,
|
OPCODE_PUSHINT,
|
||||||
OPCODE_PUSHFLT,
|
OPCODE_PUSHFLT,
|
||||||
OPCODE_PUSHSTR,
|
OPCODE_PUSHSTR,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ typedef enum {
|
|||||||
EXPR_DIV,
|
EXPR_DIV,
|
||||||
EXPR_ASS,
|
EXPR_ASS,
|
||||||
EXPR_INT,
|
EXPR_INT,
|
||||||
|
EXPR_CALL,
|
||||||
EXPR_NONE,
|
EXPR_NONE,
|
||||||
EXPR_TRUE,
|
EXPR_TRUE,
|
||||||
EXPR_FALSE,
|
EXPR_FALSE,
|
||||||
@@ -79,6 +80,13 @@ typedef struct {
|
|||||||
int count;
|
int count;
|
||||||
} OperExprNode;
|
} OperExprNode;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ExprNode base;
|
||||||
|
Node *func;
|
||||||
|
Node *argv;
|
||||||
|
int argc;
|
||||||
|
} CallExprNode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Node base;
|
Node base;
|
||||||
Node *condition;
|
Node *condition;
|
||||||
|
|||||||
@@ -101,6 +101,27 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
return ExeBuilder_Append(exeb, error, OPCODE_PUSHVAR, &op, 1, node->offset, node->length);
|
return ExeBuilder_Append(exeb, error, OPCODE_PUSHVAR, &op, 1, node->offset, node->length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case EXPR_CALL:
|
||||||
|
{
|
||||||
|
CallExprNode *p = (CallExprNode*) expr;
|
||||||
|
|
||||||
|
Node *arg = p->argv;
|
||||||
|
|
||||||
|
while(arg)
|
||||||
|
{
|
||||||
|
if(!emit_instr_for_node(exeb, arg, error))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
arg = arg->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!emit_instr_for_node(exeb, p->func, error))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Operand op = { .type = OPTP_INT, .as_int = p->argc };
|
||||||
|
return ExeBuilder_Append(exeb, error, OPCODE_CALL, &op, 1, node->offset, node->length);
|
||||||
|
}
|
||||||
|
|
||||||
case EXPR_NONE:
|
case EXPR_NONE:
|
||||||
return ExeBuilder_Append(exeb, error, OPCODE_PUSHNNE, NULL, 0, node->offset, node->length);
|
return ExeBuilder_Append(exeb, error, OPCODE_PUSHNNE, NULL, 0, node->offset, node->length);
|
||||||
|
|
||||||
@@ -310,6 +331,11 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
|||||||
|
|
||||||
if(!emit_instr_for_node(exeb, func->body, error))
|
if(!emit_instr_for_node(exeb, func->body, error))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
// Write a return instruction, just
|
||||||
|
// in case it didn't already return.
|
||||||
|
if(!ExeBuilder_Append(exeb, error, OPCODE_RETURN, NULL, 0, func->body->offset, 0))
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the first index after the function code.
|
// This is the first index after the function code.
|
||||||
|
|||||||
+95
-2
@@ -53,6 +53,7 @@ static Node *parse_expression_statement(Context *ctx);
|
|||||||
static Node *parse_ifelse_statement(Context *ctx);
|
static Node *parse_ifelse_statement(Context *ctx);
|
||||||
static Node *parse_compound_statement(Context *ctx, TokenKind end);
|
static Node *parse_compound_statement(Context *ctx, TokenKind end);
|
||||||
static Node *parse_function_definition(Context *ctx);
|
static Node *parse_function_definition(Context *ctx);
|
||||||
|
static Node *parse_postfix_expression(Context *ctx);
|
||||||
|
|
||||||
static inline _Bool isoper(char c)
|
static inline _Bool isoper(char c)
|
||||||
{
|
{
|
||||||
@@ -859,6 +860,98 @@ static Node *parse_primary_expresion(Context *ctx)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Node *parse_postfix_expression(Context *ctx)
|
||||||
|
{
|
||||||
|
assert(ctx != NULL);
|
||||||
|
|
||||||
|
Node *node = parse_primary_expresion(ctx);
|
||||||
|
|
||||||
|
if(node == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
switch(current(ctx))
|
||||||
|
{
|
||||||
|
case '(':
|
||||||
|
{
|
||||||
|
int offset = current_token(ctx)->offset;
|
||||||
|
|
||||||
|
Node *argv = NULL;
|
||||||
|
int argc = 0;
|
||||||
|
|
||||||
|
next(ctx); // Skip the '('.
|
||||||
|
|
||||||
|
if(current(ctx) != ')')
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
// Parse.
|
||||||
|
Node *arg = parse_expression(ctx);
|
||||||
|
|
||||||
|
if(arg == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// Append.
|
||||||
|
arg->next = argv;
|
||||||
|
argv = arg;
|
||||||
|
argc += 1;
|
||||||
|
|
||||||
|
// Get ',' or ')'.
|
||||||
|
|
||||||
|
if(current(ctx) == ')')
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(current(ctx) != ',')
|
||||||
|
{
|
||||||
|
if(current(ctx) == TDONE)
|
||||||
|
Error_Report(ctx->error, 0, "Source ended inside a function argument list");
|
||||||
|
else
|
||||||
|
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where ',' or ')' were expected", ctx->token->length, ctx->src + ctx->token->offset);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(ctx); // Skip the ','.
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = current_token(ctx)->offset
|
||||||
|
+ current_token(ctx)->length
|
||||||
|
- offset;
|
||||||
|
|
||||||
|
next(ctx); // Skip the ')'.
|
||||||
|
|
||||||
|
CallExprNode *call;
|
||||||
|
{
|
||||||
|
call = BPAlloc_Malloc(ctx->alloc, sizeof(CallExprNode));
|
||||||
|
|
||||||
|
if(call == NULL)
|
||||||
|
{
|
||||||
|
Error_Report(ctx->error, 1, "No memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
call->base.base.kind = NODE_EXPR;
|
||||||
|
call->base.base.next = NULL;
|
||||||
|
call->base.base.offset = offset;
|
||||||
|
call->base.base.length = length;
|
||||||
|
call->base.kind = EXPR_CALL;
|
||||||
|
call->func = node;
|
||||||
|
call->argv = argv;
|
||||||
|
call->argc = argc;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = (Node*) call;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto done;
|
||||||
|
} // End switch.
|
||||||
|
} // End loop.
|
||||||
|
done:
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
static inline _Bool isbinop(Token *tok)
|
static inline _Bool isbinop(Token *tok)
|
||||||
{
|
{
|
||||||
assert(tok != NULL);
|
assert(tok != NULL);
|
||||||
@@ -911,7 +1004,7 @@ static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec)
|
|||||||
ctx->token = ctx->token->next;
|
ctx->token = ctx->token->next;
|
||||||
assert(ctx->token != NULL);
|
assert(ctx->token != NULL);
|
||||||
|
|
||||||
Node *right_expr = parse_primary_expresion(ctx);
|
Node *right_expr = parse_postfix_expression(ctx);
|
||||||
|
|
||||||
if(right_expr == NULL)
|
if(right_expr == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -966,7 +1059,7 @@ static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec)
|
|||||||
|
|
||||||
static Node *parse_expression(Context *ctx)
|
static Node *parse_expression(Context *ctx)
|
||||||
{
|
{
|
||||||
Node *left_expr = parse_primary_expresion(ctx);
|
Node *left_expr = parse_postfix_expression(ctx);
|
||||||
|
|
||||||
if(left_expr == NULL)
|
if(left_expr == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
+12
-3
@@ -220,9 +220,19 @@ int main(int argc, char **argv)
|
|||||||
RuntimeError error;
|
RuntimeError error;
|
||||||
RuntimeError_Init(&error, runtime);
|
RuntimeError_Init(&error, runtime);
|
||||||
|
|
||||||
|
if(!add_builtins(runtime, (Error*) &error))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Couldn't load builtins: %s\n", error.base.message);
|
||||||
|
Debug_Free(dbg);
|
||||||
|
Source_Free(src);
|
||||||
|
Executable_Free(exe);
|
||||||
|
RuntimeError_Free(&error);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
Object *result = run(runtime, (Error*) &error, exe, 0, NULL, 0);
|
Object *result = run(runtime, (Error*) &error, exe, 0, NULL, 0);
|
||||||
|
|
||||||
if(result == NULL || !Object_Print(result, stderr, (Error*) &error))
|
if(result == NULL)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "RUNTIME ERROR: %s.\n", error.base.message);
|
fprintf(stderr, "RUNTIME ERROR: %s.\n", error.base.message);
|
||||||
|
|
||||||
@@ -238,8 +248,7 @@ int main(int argc, char **argv)
|
|||||||
Runtime_Free(runtime);
|
Runtime_Free(runtime);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
Runtime_Free(runtime);
|
Runtime_Free(runtime);
|
||||||
Debug_Free(dbg);
|
Debug_Free(dbg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include "runtime.h"
|
||||||
|
|
||||||
|
static Object *bin_print(Runtime *runtime, Object **argv, unsigned int argc, Error *error)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < (int) argc; i += 1)
|
||||||
|
Object_Print(argv[i], stdout, error);
|
||||||
|
|
||||||
|
return Object_NewNone(Runtime_GetHeap(runtime), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
int argc;
|
||||||
|
Object *(*callback)(Runtime*, Object**, unsigned int, Error*);
|
||||||
|
} BuiltinNativeFunctions;
|
||||||
|
|
||||||
|
BuiltinNativeFunctions builtins[] = {
|
||||||
|
{"print", -1, bin_print},
|
||||||
|
};
|
||||||
|
|
||||||
|
_Bool add_builtins(Runtime *runtime, Error *error)
|
||||||
|
{
|
||||||
|
Heap *heap = Runtime_GetHeap(runtime);
|
||||||
|
assert(heap != NULL);
|
||||||
|
|
||||||
|
Object *dest = Runtime_GetBuiltins(runtime, error);
|
||||||
|
|
||||||
|
if(dest == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < (int) (sizeof(builtins) / sizeof(builtins[0])); i += 1)
|
||||||
|
{
|
||||||
|
Object *name = Object_FromString(builtins[i].name, -1, heap, error);
|
||||||
|
|
||||||
|
if(name == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Object *func = Object_FromNativeFunction(runtime, builtins[i].callback, builtins[i].argc, heap, error);
|
||||||
|
|
||||||
|
if(func == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if(!Object_Insert(dest, name, func, heap, error))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "../utils/defs.h"
|
||||||
|
#include "../objects/objects.h"
|
||||||
|
#include "runtime.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Object base;
|
||||||
|
Runtime *runtime;
|
||||||
|
Object *(*callback)(Runtime *runtime, Object **argv, unsigned int argc, Error *error);
|
||||||
|
int argc;
|
||||||
|
} NativeFunctionObject;
|
||||||
|
|
||||||
|
static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap, Error *error);
|
||||||
|
|
||||||
|
static const Type t_nfunc = {
|
||||||
|
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
|
||||||
|
.name = "native function",
|
||||||
|
.size = sizeof (NativeFunctionObject),
|
||||||
|
.call = call,
|
||||||
|
};
|
||||||
|
|
||||||
|
static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap, Error *error)
|
||||||
|
{
|
||||||
|
assert(self != NULL);
|
||||||
|
assert(heap != NULL);
|
||||||
|
assert(error != NULL);
|
||||||
|
|
||||||
|
NativeFunctionObject *func = (NativeFunctionObject*) self;
|
||||||
|
|
||||||
|
// If the function isn't variadic, make sure
|
||||||
|
// the right amount of arguments is provided.
|
||||||
|
|
||||||
|
Object **argv2;
|
||||||
|
int argc2;
|
||||||
|
|
||||||
|
int expected_argc = func->argc;
|
||||||
|
|
||||||
|
if(expected_argc < 0 || expected_argc == (int) argc)
|
||||||
|
{
|
||||||
|
// The function is variadic or the right
|
||||||
|
// amount of arguments was provided.
|
||||||
|
argv2 = argv;
|
||||||
|
argc2 = argc;
|
||||||
|
}
|
||||||
|
else if(expected_argc < (int) argc)
|
||||||
|
{
|
||||||
|
// Nothing to be done. By using
|
||||||
|
// the right argc the additional
|
||||||
|
// arguments are ignored implicitly.
|
||||||
|
argv2 = argv;
|
||||||
|
argc2 = expected_argc;
|
||||||
|
}
|
||||||
|
else if(expected_argc > (int) argc)
|
||||||
|
{
|
||||||
|
// Some arguments are missing.
|
||||||
|
argv2 = malloc(sizeof(Object*) * expected_argc);
|
||||||
|
|
||||||
|
if(argv2 == NULL)
|
||||||
|
{
|
||||||
|
Error_Report(error, 1, "No memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the provided arguments.
|
||||||
|
for(int i = 0; i < (int) argc; i += 1)
|
||||||
|
argv2[i] = argv[i];
|
||||||
|
|
||||||
|
// Set the unspecified arguments to none.
|
||||||
|
for(int i = argc; i < expected_argc; i += 1)
|
||||||
|
{
|
||||||
|
argv2[i] = Object_NewNone(heap, error);
|
||||||
|
|
||||||
|
if(argv2[i] == NULL)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else UNREACHABLE;
|
||||||
|
|
||||||
|
assert(func->callback != NULL);
|
||||||
|
Object *result = func->callback(func->runtime, argv2, argc2, error);
|
||||||
|
|
||||||
|
if(result == NULL && error->occurred == 0)
|
||||||
|
Error_Report(error, 1, "Native callback returned NULL but didn't report errors");
|
||||||
|
|
||||||
|
if(argv2 != argv)
|
||||||
|
free(argv2);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object *Object_FromNativeFunction(Runtime *runtime, Object *(*callback)(Runtime*, Object**, unsigned int, Error*), int argc, Heap *heap, Error *error)
|
||||||
|
{
|
||||||
|
assert(callback != NULL);
|
||||||
|
assert(heap != NULL);
|
||||||
|
assert(error != NULL);
|
||||||
|
|
||||||
|
NativeFunctionObject *func = (NativeFunctionObject*) Heap_Malloc(heap, &t_nfunc, error);
|
||||||
|
|
||||||
|
if(func == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
func->runtime = runtime;
|
||||||
|
func->callback = callback;
|
||||||
|
func->argc = argc;
|
||||||
|
|
||||||
|
return (Object*) func;
|
||||||
|
}
|
||||||
+77
-6
@@ -17,12 +17,18 @@ struct Frame {
|
|||||||
struct xRuntime {
|
struct xRuntime {
|
||||||
void *callback_userp;
|
void *callback_userp;
|
||||||
_Bool (*callback_addr)(Runtime*, void*);
|
_Bool (*callback_addr)(Runtime*, void*);
|
||||||
|
Object *builtins;
|
||||||
int depth;
|
int depth;
|
||||||
Frame *frame;
|
Frame *frame;
|
||||||
Stack *stack;
|
Stack *stack;
|
||||||
Heap *heap;
|
Heap *heap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Heap *Runtime_GetHeap(Runtime *runtime)
|
||||||
|
{
|
||||||
|
return runtime->heap;
|
||||||
|
}
|
||||||
|
|
||||||
int Runtime_GetCurrentIndex(Runtime *runtime)
|
int Runtime_GetCurrentIndex(Runtime *runtime)
|
||||||
{
|
{
|
||||||
if(runtime->depth == 0)
|
if(runtime->depth == 0)
|
||||||
@@ -73,8 +79,10 @@ Runtime *Runtime_New(int stack_size, int heap_size, void *callback_userp, _Bool
|
|||||||
|
|
||||||
runtime->callback_userp = callback_userp;
|
runtime->callback_userp = callback_userp;
|
||||||
runtime->callback_addr = callback_addr;
|
runtime->callback_addr = callback_addr;
|
||||||
|
runtime->builtins = NULL;
|
||||||
runtime->frame = NULL;
|
runtime->frame = NULL;
|
||||||
runtime->depth = 0;
|
runtime->depth = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return runtime;
|
return runtime;
|
||||||
@@ -87,6 +95,14 @@ void Runtime_Free(Runtime *runtime)
|
|||||||
free(runtime);
|
free(runtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object *Runtime_GetBuiltins(Runtime *runtime, Error *error)
|
||||||
|
{
|
||||||
|
if(runtime->builtins == NULL)
|
||||||
|
runtime->builtins = Object_NewMap(-1, runtime->heap, error);
|
||||||
|
|
||||||
|
return runtime->builtins;
|
||||||
|
}
|
||||||
|
|
||||||
_Bool Runtime_Push(Runtime *runtime, Error *error, Object *obj)
|
_Bool Runtime_Push(Runtime *runtime, Error *error, Object *obj)
|
||||||
{
|
{
|
||||||
assert(runtime != NULL);
|
assert(runtime != NULL);
|
||||||
@@ -418,6 +434,51 @@ static _Bool step(Runtime *runtime, Error *error)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OPCODE_CALL:
|
||||||
|
{
|
||||||
|
assert(opc == 1);
|
||||||
|
assert(ops[0].type == OPTP_INT);
|
||||||
|
|
||||||
|
int argc = ops[0].as_int;
|
||||||
|
assert(argc >= 0);
|
||||||
|
|
||||||
|
if(runtime->frame->used < argc + 1)
|
||||||
|
{
|
||||||
|
Error_Report(error, 1, "Frame doesn't own enough objects to execute call");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object *callable = Stack_Top(runtime->stack, 0);
|
||||||
|
assert(callable != NULL);
|
||||||
|
|
||||||
|
Object *argv[8];
|
||||||
|
|
||||||
|
int max_argc = sizeof(argv) / sizeof(argv[0]);
|
||||||
|
if(argc > max_argc)
|
||||||
|
{
|
||||||
|
Error_Report(error, 1, "Static buffer only allows function calls with up to %d arguments", max_argc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < argc; i += 1)
|
||||||
|
{
|
||||||
|
argv[i] = Stack_Top(runtime->stack, -(i+1));
|
||||||
|
assert(argv[i] != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) Runtime_Pop(runtime, error, argc+1);
|
||||||
|
assert(error->occurred == 0);
|
||||||
|
|
||||||
|
Object *obj = Object_Call(callable, argv, argc, runtime->heap, error);
|
||||||
|
|
||||||
|
if(obj == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(!Runtime_Push(runtime, error, obj))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
case OPCODE_PUSHINT:
|
case OPCODE_PUSHINT:
|
||||||
{
|
{
|
||||||
assert(opc == 1);
|
assert(opc == 1);
|
||||||
@@ -475,13 +536,23 @@ static _Bool step(Runtime *runtime, Error *error)
|
|||||||
|
|
||||||
Object *obj = Object_Select(runtime->frame->vars, key, runtime->heap, error);
|
Object *obj = Object_Select(runtime->frame->vars, key, runtime->heap, error);
|
||||||
|
|
||||||
if(obj == NULL)
|
if(obj == NULL && error->occurred == 0)
|
||||||
{
|
{
|
||||||
if(error->occurred == 0)
|
// Variable not defined locally.
|
||||||
// There's no such variable.
|
|
||||||
Error_Report(error, 1, "Reference to undefined variable \"%s\"", ops[0].as_string);
|
if(runtime->builtins)
|
||||||
return 0;
|
obj = Object_Select(runtime->builtins, key, runtime->heap, error);
|
||||||
|
|
||||||
|
if(obj == NULL)
|
||||||
|
{
|
||||||
|
if(error->occurred == 0)
|
||||||
|
// There's no such variable.
|
||||||
|
Error_Report(error, 1, "Reference to undefined variable \"%s\"", ops[0].as_string);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else if(obj == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if(!Runtime_Push(runtime, error, obj))
|
if(!Runtime_Push(runtime, error, obj))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -538,7 +609,7 @@ static _Bool step(Runtime *runtime, Error *error)
|
|||||||
assert(ops[1].type == OPTP_INT);
|
assert(ops[1].type == OPTP_INT);
|
||||||
|
|
||||||
Object *obj = Object_FromNojaFunction(runtime, runtime->frame->exe, ops[0].as_int, ops[1].as_int, runtime->heap, error);
|
Object *obj = Object_FromNojaFunction(runtime, runtime->frame->exe, ops[0].as_int, ops[1].as_int, runtime->heap, error);
|
||||||
|
|
||||||
if(obj == NULL)
|
if(obj == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ Runtime* Runtime_New(int stack_size, int heap_size, void *callback_userp, _Bool
|
|||||||
void Runtime_Free(Runtime *runtime);
|
void Runtime_Free(Runtime *runtime);
|
||||||
_Bool Runtime_Pop(Runtime *runtime, Error *error, unsigned int n);
|
_Bool Runtime_Pop(Runtime *runtime, Error *error, unsigned int n);
|
||||||
_Bool Runtime_Push(Runtime *runtime, Error *error, Object *obj);
|
_Bool Runtime_Push(Runtime *runtime, Error *error, Object *obj);
|
||||||
|
Heap* Runtime_GetHeap(Runtime *runtime);
|
||||||
|
Object* Runtime_GetBuiltins(Runtime *runtime, Error *error);
|
||||||
int Runtime_GetCurrentIndex(Runtime *runtime);
|
int Runtime_GetCurrentIndex(Runtime *runtime);
|
||||||
Executable *Runtime_GetCurrentExecutable(Runtime *runtime);
|
Executable *Runtime_GetCurrentExecutable(Runtime *runtime);
|
||||||
|
|
||||||
@@ -18,7 +20,9 @@ Snapshot *Snapshot_New(Runtime *runtime);
|
|||||||
void Snapshot_Free(Snapshot *snapshot);
|
void Snapshot_Free(Snapshot *snapshot);
|
||||||
void Snapshot_Print(Snapshot *snapshot, FILE *fp);
|
void Snapshot_Print(Snapshot *snapshot, FILE *fp);
|
||||||
|
|
||||||
|
_Bool add_builtins(Runtime *runtime, Error *error);
|
||||||
Object *run(Runtime *runtime, Error *error, Executable *exe, int index, Object **argv, int argc);
|
Object *run(Runtime *runtime, Error *error, Executable *exe, int index, Object **argv, int argc);
|
||||||
|
|
||||||
Object *Object_FromNojaFunction(Runtime *runtime, Executable *exe, int index, int argc, Heap *heap, Error *error);
|
Object* Object_FromNativeFunction(Runtime *runtime, Object *(*callback)(Runtime*, Object**, unsigned int, Error*), int argc, Heap *heap, Error *error);
|
||||||
|
Object* Object_FromNojaFunction(Runtime *runtime, Executable *exe, int index, int argc, Heap *heap, Error *error);
|
||||||
#endif
|
#endif
|
||||||
Reference in New Issue
Block a user