From ed63f09b57e31dc15852a3a690e61cb6b8c08491 Mon Sep 17 00:00:00 2001 From: cozis Date: Wed, 6 Apr 2022 13:22:31 +0200 Subject: [PATCH] added tuples and multiple return values --- examples/json.noja | 86 +++++++------------- examples/makegarbage.noja | 2 +- examples/return.noja | 8 ++ src/builtins/basic.c | 164 +++++++++++++++++++++++++++---------- src/builtins/files.c | 6 +- src/builtins/math.c | 31 ++++--- src/common/executable.c | 14 ++-- src/compiler/ASTi.h | 1 + src/compiler/compile.c | 165 +++++++++++++++++++++++++++----------- src/compiler/parse.c | 74 ++++++++++------- src/main.c | 9 ++- src/objects/objects.c | 9 +-- src/objects/objects.h | 14 ++-- src/runtime/o_func.c | 10 +-- src/runtime/o_nfunc.c | 17 ++-- src/runtime/runtime.c | 77 +++++++++++------- src/runtime/runtime.h | 7 +- 17 files changed, 434 insertions(+), 260 deletions(-) create mode 100644 examples/return.noja diff --git a/examples/json.noja b/examples/json.noja index 8efd9e5..266c2e4 100644 --- a/examples/json.noja +++ b/examples/json.noja @@ -37,10 +37,8 @@ fun parse(str) { skip(ctx, isSpace); - if ended(ctx): { - print('Source only contains whitespace'); - return none; - } + if ended(ctx): + return none, 'Source only contains whitespace'; return parseAny(ctx); } @@ -53,10 +51,8 @@ fun parseArray(ctx) { skip(ctx, isSpace); - if ended(ctx): { - error("Source ended inside an array"); - return none; - } + if ended(ctx): + return none, "Source ended inside an array"; arr = []; @@ -76,27 +72,21 @@ fun parseArray(ctx) { skip(ctx, isSpace); - if ended(ctx): { - error("Source ended inside an array"); - return none; - } + if ended(ctx): + return none, "Source ended inside an array"; if current(ctx) == ']': break; - if current(ctx) != ',': { - error("Bad character inside array"); - return none; - } + if current(ctx) != ',': + return none, "Bad character inside array"; next(ctx); # Skip ','. skip(ctx, isSpace); - if ended(ctx): { - error("Source ended inside an array"); - return none; - } + if ended(ctx): + return none, "Source ended inside an array"; } next(ctx); @@ -111,10 +101,8 @@ fun parseObject(ctx) { skip(ctx, isSpace); - if ended(ctx): { - error("Source ended inside an object"); - return none; - } + if ended(ctx): + return none, "Source ended inside an object"; obj = {}; @@ -125,10 +113,8 @@ fun parseObject(ctx) { while true: { - if current(ctx) != '"': { - error("Bad character where a string was expected"); - return none; - } + if current(ctx) != '"': + return none, "Bad character where a string was expected"; key = parseString(ctx); @@ -137,10 +123,8 @@ fun parseObject(ctx) { skip(ctx, isSpace); - if current(ctx) != ':': { - error("Bad character where ':' was expected"); - return none; - } + if current(ctx) != ':': + return none, "Bad character where ':' was expected"; next(ctx); # Skip ':'. @@ -155,27 +139,21 @@ fun parseObject(ctx) { skip(ctx, isSpace); - if ended(ctx): { - error("Source ended inside an arrays"); - return none; - } + if ended(ctx): + return none, "Source ended inside an arrays"; if current(ctx) == '}': break; - if current(ctx) != ',': { - error("Bad character inside array"); - return none; - } + if current(ctx) != ',': + return none, "Bad character inside array"; next(ctx); # Skip ','. skip(ctx, isSpace); - if ended(ctx): { - error("Source ended inside an array"); - return none; - } + if ended(ctx): + return none, "Source ended inside an array"; } next(ctx); @@ -198,10 +176,8 @@ fun parseString(ctx) { next(ctx); } - if ended(ctx): { - error("Source ended inside a string"); - return none; - } + if ended(ctx): + return none, "Source ended inside a string"; next(ctx); # Skip the '"'. return buff; @@ -228,15 +204,11 @@ fun parseNumber(ctx) { next(ctx); - if ended(ctx): { - error("Source string ended unexpectedly after dot"); - return none; - } + if ended(ctx): + return none, "Source string ended unexpectedly after dot"; - if not isDigit(current(ctx)): { - error("Got something other than a digit after dot"); - return none; - } + if not isDigit(current(ctx)): + return none, "Got something other than a digit after dot"; fact = 1; while not ended(ctx): { @@ -253,8 +225,6 @@ fun parseNumber(ctx) { fun parseAny(ctx) { - print('Parsing value\n'); - assert(not ended(ctx)); if current(ctx) == '[': diff --git a/examples/makegarbage.noja b/examples/makegarbage.noja index fdb9f4d..7b791ac 100644 --- a/examples/makegarbage.noja +++ b/examples/makegarbage.noja @@ -3,7 +3,7 @@ print('Start\n'); i = 0; -n = 1000; +n = 10000000; while i < n: { diff --git a/examples/return.noja b/examples/return.noja new file mode 100644 index 0000000..9305d3d --- /dev/null +++ b/examples/return.noja @@ -0,0 +1,8 @@ + +fun hello() + return 1, "hello"; + +a, b = hello(); + +print('a = ', a, '\n'); +print('b = ', b, '\n'); \ No newline at end of file diff --git a/src/builtins/basic.c b/src/builtins/basic.c index b8092c9..3434f29 100644 --- a/src/builtins/basic.c +++ b/src/builtins/basic.c @@ -37,24 +37,32 @@ #include "math.h" #include "../utils/utf8.h" -static Object *bin_print(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +static int bin_print(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { + (void) runtime; + (void) rets; + (void) maxretc; + (void) error; + for(int i = 0; i < (int) argc; i += 1) Object_Print(argv[i], stdout); - - return Object_NewNone(Runtime_GetHeap(runtime), error); + return 0; } -static Object *bin_type(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +static int bin_type(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { assert(argc == 1); (void) runtime; (void) error; - return (Object*) argv[0]->type; + + if(maxretc == 0) + return 0; + rets[0] = (Object*) argv[0]->type; + return 1; } -static Object *bin_unicode(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +static int bin_unicode(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { (void) runtime; (void) error; @@ -66,29 +74,37 @@ static Object *bin_unicode(Runtime *runtime, Object **argv, unsigned int argc, E if(!Object_IsString(argv[0])) { Error_Report(error, 0, "Argument #%d is not a string", 1); - return NULL; + return -1; } const char *string; int n; string = Object_ToString(argv[0],&n,Runtime_GetHeap(runtime),error); if (string == NULL) - return NULL; + return -1; if(n == 0) { Error_Report(error, 0, "Argument #%d is an empty string", 1); - return NULL; + return -1; } int k = utf8_sequence_to_utf32_codepoint(string,n,&ret); assert(k >= 0); - - return Object_FromInt(ret,Runtime_GetHeap(runtime),error); + + Object *temp = Object_FromInt(ret,Runtime_GetHeap(runtime),error); + + if(temp == NULL) + return -1; + + if(maxretc == 0) + return 0; + rets[0] = temp; + return 1; } -static Object *bin_chr(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +static int bin_chr(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { assert(argc == 1); @@ -97,7 +113,7 @@ static Object *bin_chr(Runtime *runtime, Object **argv, unsigned int argc, Error if(!Object_IsInt(argv[0])) { Error_Report(error, 0, "Argument #%d is not an integer", 1); - return NULL; + return -1; } @@ -106,7 +122,7 @@ static Object *bin_chr(Runtime *runtime, Object **argv, unsigned int argc, Error int value = Object_ToInt(argv[0],error); if(error->occurred) - return NULL; + return -1; int k = utf8_sequence_from_utf32_codepoint(buff,sizeof(buff),value); @@ -114,25 +130,41 @@ static Object *bin_chr(Runtime *runtime, Object **argv, unsigned int argc, Error if(k<0) { Error_Report(error, 0, "Argument #%d is not valid utf-32", 1); - return NULL; + return -1; } - return Object_FromString(buff,k,Runtime_GetHeap(runtime),error); + Object *temp = Object_FromString(buff,k,Runtime_GetHeap(runtime),error); + + if(temp == NULL) + return -1; + + if(maxretc == 0) + return 0; + rets[0] = temp; + return 1; } -static Object *bin_count(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +static int bin_count(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { assert(argc == 1); int n = Object_Count(argv[0], error); if(error->occurred) - return NULL; + return -1; - return Object_FromInt(n, Runtime_GetHeap(runtime), error); + Object *temp = Object_FromInt(n, Runtime_GetHeap(runtime), error); + + if(temp == NULL) + return -1; + + if(maxretc == 0) + return 0; + rets[0] = temp; + return 1; } -static Object *bin_input(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +static int bin_input(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { (void) argv; @@ -158,7 +190,7 @@ static Object *bin_input(Runtime *runtime, Object **argv, unsigned int argc, Err { if(str != maybe) free(str); Error_Report(error, 1, "No memory"); - return NULL; + return -1; } str = tmp; @@ -175,23 +207,36 @@ static Object *bin_input(Runtime *runtime, Object **argv, unsigned int argc, Err if(str != maybe) free(str); - return res; + if(res == NULL) + return -1; + + if(maxretc == 0) + return 0; + rets[0] = res; + return 1; } -static Object *bin_assert(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +static int bin_assert(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { + (void) runtime; + (void) rets; + (void) maxretc; + for(unsigned int i = 0; i < argc; i += 1) if(!Object_ToBool(argv[i], error)) { if(!error->occurred) Error_Report(error, 0, "Assertion failed"); - return NULL; + return -1; } - return Object_NewNone(Runtime_GetHeap(runtime), error); + return 0; } -static Object *bin_error(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +static int bin_error(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { + (void) rets; + (void) maxretc; + assert(argc == 1); int length; @@ -200,13 +245,13 @@ static Object *bin_error(Runtime *runtime, Object **argv, unsigned int argc, Err string = Object_ToString(argv[0], &length, Runtime_GetHeap(runtime), error); if(string == NULL) - return NULL; + return -1; Error_Report(error, 0, "%s", string); - return NULL; + return -1; } -static Object *bin_strcat(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +static int bin_strcat(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { unsigned int total_count = 0; @@ -215,13 +260,13 @@ static Object *bin_strcat(Runtime *runtime, Object **argv, unsigned int argc, Er if(!Object_IsString(argv[i])) { Error_Report(error, 0, "Argument #%d is not a string", i+1); - return NULL; + return -1; } total_count += Object_Count(argv[i], error); if(error->occurred) - return NULL; + return -1; } char starting[128]; @@ -234,7 +279,7 @@ static Object *bin_strcat(Runtime *runtime, Object **argv, unsigned int argc, Er if(buffer == NULL) { Error_Report(error, 1, "No memory"); - return NULL; + return -1; } } @@ -260,35 +305,58 @@ static Object *bin_strcat(Runtime *runtime, Object **argv, unsigned int argc, Er done: if(starting != buffer) free(buffer); - return result; + + if(result == NULL) + return -1; + + if(maxretc == 0) + return 0; + rets[0] = result; + return 1; } -static Object *bin_newBuffer(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +static int bin_newBuffer(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { assert(argc == 1); long long int size = Object_ToInt(argv[0], error); if(error->occurred == 1) - return NULL; + return -1; - return Object_NewBuffer(size, Runtime_GetHeap(runtime), error); + Object *temp = Object_NewBuffer(size, Runtime_GetHeap(runtime), error); + + if(temp == NULL) + return -1; + + if(maxretc == 0) + return 0; + rets[0] = temp; + return 1; } -static Object *bin_sliceBuffer(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +static int bin_sliceBuffer(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { assert(argc == 3); long long int offset = Object_ToInt(argv[1], error); - if(error->occurred == 1) return NULL; + if(error->occurred == 1) return -1; long long int length = Object_ToInt(argv[2], error); - if(error->occurred == 1) return NULL; + if(error->occurred == 1) return -1; - return Object_SliceBuffer(argv[0], offset, length, Runtime_GetHeap(runtime), error); + Object *temp = Object_SliceBuffer(argv[0], offset, length, Runtime_GetHeap(runtime), error); + + if(temp == NULL) + return -1; + + if(maxretc == 0) + return 0; + rets[0] = temp; + return 1; } -static Object *bin_bufferToString(Runtime *runtime, Object **argv, unsigned int argc, Error *error) +static int bin_bufferToString(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) { assert(argc == 1); @@ -298,14 +366,22 @@ static Object *bin_bufferToString(Runtime *runtime, Object **argv, unsigned int buffaddr = Object_GetBufferAddrAndSize(argv[0], &buffsize, error); if(error->occurred) - return NULL; + return -1; - return Object_FromString(buffaddr, buffsize, Runtime_GetHeap(runtime), error); + Object *temp = Object_FromString(buffaddr, buffsize, Runtime_GetHeap(runtime), error); + + if(temp == NULL) + return -1; + + if(maxretc == 0) + return 0; + rets[0] = temp; + return 1; } const StaticMapSlot bins_basic[] = { { "math", SM_SMAP, .as_smap = bins_math, }, - { "files", SM_SMAP, .as_smap = bins_files, }, +// { "files", SM_SMAP, .as_smap = bins_files, }, // { "net", SM_SMAP, .as_smap = bins_net, }, { "newBuffer", SM_FUNCT, .as_funct = bin_newBuffer, .argc = 1 }, diff --git a/src/builtins/files.c b/src/builtins/files.c index 15638a2..5c38932 100644 --- a/src/builtins/files.c +++ b/src/builtins/files.c @@ -27,7 +27,8 @@ ** | with The Noja Interpreter. If not, see . | ** +--------------------------------------------------------------------------+ */ - +#warning "Commented out whole file" +/* #include #include #include "files.h" @@ -305,4 +306,5 @@ const StaticMapSlot bins_files[] = { { "read", SM_FUNCT, .as_funct = bin_read, .argc = 3, }, { "write", SM_FUNCT, .as_funct = bin_write, .argc = 3, }, { NULL, SM_END, {}, {} }, -}; \ No newline at end of file +}; +*/ \ No newline at end of file diff --git a/src/builtins/math.c b/src/builtins/math.c index 211f745..56b01ff 100644 --- a/src/builtins/math.c +++ b/src/builtins/math.c @@ -33,7 +33,7 @@ #include "math.h" #define WRAP_FUNC(name) \ - static Object *bin_ ## name(Runtime *runtime, Object **argv, unsigned int argc, Error *error) \ + static int bin_ ## name(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) \ { \ assert(argc == 1); \ \ @@ -42,45 +42,56 @@ double v = Object_ToFloat(argv[0], error); \ \ if(error->occurred) \ - return NULL; \ + return -1; \ \ - return Object_FromFloat(name(v), Runtime_GetHeap(runtime), error); \ + if(maxretc > 0) \ + { \ + rets[0] = Object_FromFloat(name(v), Runtime_GetHeap(runtime), error); \ + if(rets[0] == NULL) \ + return -1; \ + return 1; \ + } \ + return 0; \ } \ else \ { \ Error_Report(error, 0, "Expected first argument to be a float, but it's a %s", Object_GetName(argv[0]));\ - return NULL; \ + return -1; \ } \ } #define WRAP_FUNC_2(name) \ - static Object *bin_ ## name(Runtime *runtime, Object **argv, unsigned int argc, Error *error) \ + static int bin_ ## name(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error) \ { \ assert(argc == 2); \ \ if(!Object_IsFloat(argv[0])) \ { \ Error_Report(error, 0, "Expected first argument to be a float, but it's a %s", Object_GetName(argv[0]));\ - return NULL; \ + return -1; \ } \ \ if(!Object_IsFloat(argv[1])) \ { \ Error_Report(error, 0, "Expected second argument to be a float, but it's a %s", Object_GetName(argv[1]));\ - return NULL; \ + return -1; \ } \ \ double v1 = Object_ToFloat(argv[0], error); \ \ if(error->occurred) \ - return NULL; \ + return -1; \ \ double v2 = Object_ToFloat(argv[1], error); \ \ if(error->occurred) \ - return NULL; \ + return -1; \ \ - return Object_FromFloat(name(v1, v2), Runtime_GetHeap(runtime), error); \ + Object *res = Object_FromFloat(name(v1, v2), Runtime_GetHeap(runtime), error); \ + if(res == NULL) return -1; \ + if(maxretc == 0) return 0; \ + rets[0] = res; \ + return 1; \ } WRAP_FUNC(ceil) diff --git a/src/common/executable.c b/src/common/executable.c index 6cbabd9..98d8a9b 100644 --- a/src/common/executable.c +++ b/src/common/executable.c @@ -90,7 +90,7 @@ static const InstrInfo instr_table[] = { [OPCODE_ASS] = {"ASS", 1, (OperandType[]) {OPTP_STRING}}, [OPCODE_POP] = {"POP", 1, (OperandType[]) {OPTP_INT}}, - [OPCODE_CALL] = {"CALL", 1, (OperandType[]) {OPTP_INT}}, + [OPCODE_CALL] = {"CALL", 2, (OperandType[]) {OPTP_INT, OPTP_INT}}, [OPCODE_SELECT] = {"SELECT", 0, NULL}, [OPCODE_INSERT] = {"INSERT", 0, NULL}, [OPCODE_INSERT2] = {"INSERT2", 0, NULL}, @@ -105,7 +105,7 @@ static const InstrInfo instr_table[] = { [OPCODE_PUSHLST] = {"PUSHLST", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_PUSHMAP] = {"PUSHMAP", 1, (OperandType[]) {OPTP_INT}}, - [OPCODE_RETURN] = {"RETURN", 0, NULL}, + [OPCODE_RETURN] = {"RETURN", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_JUMPIFNOTANDPOP] = {"JUMPIFNOTANDPOP", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_JUMPIFANDPOP] = {"JUMPIFANDPOP", 1, (OperandType[]) {OPTP_INT}}, @@ -299,7 +299,7 @@ Executable *ExeBuilder_Finalize(ExeBuilder *exeb, Error *error) if(exeb->promc > 0) { - Error_Report(error, 0, "There are still %d unfulfilled promises", exeb->promc); + Error_Report(error, 1, "There are still %d unfulfilled promises", exeb->promc); return 0; } @@ -371,7 +371,7 @@ _Bool ExeBuilder_Append(ExeBuilder *exeb, Error *error, Opcode opcode, Operand * if(opc != info->opcount) { // ERROR: Too many operands were provided. - Error_Report(error, 0, + Error_Report(error, 1, "Instruction %s expects %d operands, but %d were provided", info->name, info->opcount, opc); return 0; @@ -409,13 +409,13 @@ _Bool ExeBuilder_Append(ExeBuilder *exeb, Error *error, Opcode opcode, Operand * if(expected_type == OPTP_STRING) { - Error_Report(error, 0, "Promise values can't be provided as string operands"); + Error_Report(error, 1, "Promise values can't be provided as string operands"); return 0; } if(Promise_Size(opv[i].as_promise) != operand_type_sizes[expected_type]) { - Error_Report(error, 0, + Error_Report(error, 1, "Provided promise has a value size of %d, " "but since %s %s was expected, the promise " "size must be %d", @@ -429,7 +429,7 @@ _Bool ExeBuilder_Append(ExeBuilder *exeb, Error *error, Opcode opcode, Operand * else if(expected_type != provided_type) { // ERROR: Wrong operand type provided. - Error_Report(error, 0, + Error_Report(error, 1, "Instruction %s expects %s %s as operand %d, but %s %s was provided instead", info->name, operand_type_arts[expected_type], diff --git a/src/compiler/ASTi.h b/src/compiler/ASTi.h index 98ad2c7..b20b073 100644 --- a/src/compiler/ASTi.h +++ b/src/compiler/ASTi.h @@ -68,6 +68,7 @@ typedef enum { EXPR_INT, EXPR_MAP, EXPR_CALL, + EXPR_PAIR, EXPR_LIST, EXPR_NONE, EXPR_TRUE, diff --git a/src/compiler/compile.c b/src/compiler/compile.c index be247dc..6151cb2 100644 --- a/src/compiler/compile.c +++ b/src/compiler/compile.c @@ -50,6 +50,8 @@ #include "compile.h" #include "ASTi.h" +static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Promise *break_dest, Error *error); + static Opcode exprkind_to_opcode(ExprKind kind) { switch(kind) @@ -75,6 +77,43 @@ static Opcode exprkind_to_opcode(ExprKind kind) } } +static _Bool emit_instr_for_funccall(ExeBuilder *exeb, CallExprNode *expr, Promise *break_dest, int returns, Error *error) +{ + Node *arg = expr->argv; + + while(arg) + { + if(!emit_instr_for_node(exeb, arg, break_dest, error)) + return 0; + + arg = arg->next; + } + + if(!emit_instr_for_node(exeb, expr->func, break_dest, error)) + return 0; + + Operand ops[2]; + ops[0] = (Operand) { .type = OPTP_INT, .as_int = expr->argc }; + ops[1] = (Operand) { .type = OPTP_INT, .as_int = returns }; + return ExeBuilder_Append(exeb, error, OPCODE_CALL, ops, 2, expr->base.base.offset, expr->base.base.length); +} + +static _Bool flatten_tuple_tree(ExprNode *root, ExprNode **tuple, int max, int *count, Error *error) +{ + if(root->kind == EXPR_PAIR) + return flatten_tuple_tree((ExprNode*) ((OperExprNode*) root)->head, tuple, max, count, error) && + flatten_tuple_tree((ExprNode*) ((OperExprNode*) root)->head->next, tuple, max, count, error); + + if(max == *count) + { + Error_Report(error, 0, "Static buffer is too small"); + return 0; + } + + tuple[(*count)++] = root; + return 1; +} + static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Promise *break_dest, Error *error) { assert(node != NULL); @@ -86,6 +125,10 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Promise *break_de ExprNode *expr = (ExprNode*) node; switch(expr->kind) { + case EXPR_PAIR: + Error_Report(error, 0, "Tuple outside of assignment or return statement"); + return 0; + case EXPR_NOT: case EXPR_POS: case EXPR_NEG: @@ -123,35 +166,75 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Promise *break_de lop = oper->head; rop = lop->next; - if(!emit_instr_for_node(exeb, rop, break_dest, error)) + ExprNode *tuple[32]; + int count = 0; + + if(!flatten_tuple_tree((ExprNode*) lop, tuple, sizeof(tuple)/sizeof(tuple[0]), &count, error)) return 0; - if(((ExprNode*) lop)->kind == EXPR_IDENT) + assert(count > 0); + + if(count == 1) /* No tuple. */ { - const char *name = ((IdentExprNode*) lop)->val; - - Operand op = { .type = OPTP_STRING, .as_string = name }; - if(!ExeBuilder_Append(exeb, error, OPCODE_ASS, &op, 1, node->offset, node->length)) - return 0; - } - else if(((ExprNode*) lop)->kind == EXPR_SELECT) - { - Node *idx = ((IndexSelectionExprNode*) lop)->idx; - Node *set = ((IndexSelectionExprNode*) lop)->set; - - if(!emit_instr_for_node(exeb, set, break_dest, error)) - return 0; - - if(!emit_instr_for_node(exeb, idx, break_dest, error)) - return 0; - - if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT2, NULL, 0, node->offset, node->length)) + if(!emit_instr_for_node(exeb, rop, break_dest, error)) return 0; } else { - Error_Report(error, 0, "Assignment left operand can't be assigned to"); - return 0; + if(((ExprNode*) rop)->kind == EXPR_CALL) + { + if(!emit_instr_for_funccall(exeb, (CallExprNode*) rop, break_dest, count, error)) + return 0; + } + else + { + Error_Report(error, 0, "Assigning to %d variables only 1 value", count); + return 0; + } + } + + for(int i = 0; i < count; i += 1) + { + ExprNode *tuple_item = tuple[count-i-1]; + switch(tuple_item->kind) + { + case EXPR_IDENT: + { + const char *name = ((IdentExprNode*) tuple_item)->val; + + Operand op = { .type = OPTP_STRING, .as_string = name }; + if(!ExeBuilder_Append(exeb, error, OPCODE_ASS, &op, 1, tuple_item->base.offset, tuple_item->base.length)) + return 0; + break; + } + + case EXPR_SELECT: + { + Node *idx = ((IndexSelectionExprNode*) tuple_item)->idx; + Node *set = ((IndexSelectionExprNode*) tuple_item)->set; + + if(!emit_instr_for_node(exeb, set, break_dest, error)) + return 0; + + if(!emit_instr_for_node(exeb, idx, break_dest, error)) + return 0; + + if(!ExeBuilder_Append(exeb, error, OPCODE_INSERT2, NULL, 0, tuple_item->base.offset, tuple_item->base.length)) + return 0; + break; + } + + default: + Error_Report(error, 0, "Assigning to something that it can't be assigned to"); + return 0; + } + + if(i+1 < count) + { + Operand op = (Operand) { .type = OPTP_INT, .as_int = 1 }; + if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, node->offset, 0)) + return 0; + } } return 1; @@ -253,25 +336,7 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Promise *break_de } case EXPR_CALL: - { - CallExprNode *p = (CallExprNode*) expr; - - Node *arg = p->argv; - - while(arg) - { - if(!emit_instr_for_node(exeb, arg, break_dest, error)) - return 0; - - arg = arg->next; - } - - if(!emit_instr_for_node(exeb, p->func, break_dest, 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); - } + return emit_instr_for_funccall(exeb, (CallExprNode*) expr, break_dest, 1, error); case EXPR_SELECT: { @@ -526,10 +591,18 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Promise *break_de { ReturnNode *ret = (ReturnNode*) node; - if(!emit_instr_for_node(exeb, ret->val, break_dest, error)) + ExprNode *tuple[32]; + int count = 0; + + if(!flatten_tuple_tree((ExprNode*) ret->val, tuple, sizeof(tuple)/sizeof(tuple[0]), &count, error)) return 0; - if(!ExeBuilder_Append(exeb, error, OPCODE_RETURN, NULL, 0, ret->base.offset, ret->base.length)) + for(int i = 0; i < count; i += 1) + if(!emit_instr_for_node(exeb, (Node*) tuple[i], break_dest, error)) + return 0; + + Operand op = (Operand) { .type = OPTP_INT, .as_int = count }; + if(!ExeBuilder_Append(exeb, error, OPCODE_RETURN, &op, 1, ret->base.offset, ret->base.length)) return 0; return 1; @@ -608,7 +681,8 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Promise *break_de // 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)) + Operand op = (Operand) { .type = OPTP_INT, .as_int = 0 }; + if(!ExeBuilder_Append(exeb, error, OPCODE_RETURN, &op, 1, func->body->offset, 0)) return 0; } @@ -672,7 +746,8 @@ Executable *compile(AST *ast, BPAlloc *alloc, Error *error) if(!emit_instr_for_node(exeb, ast->root, NULL, error)) return 0; - if(ExeBuilder_Append(exeb, error, OPCODE_RETURN, NULL, 0, Source_GetSize(ast->src), 0)) + Operand op = (Operand) { .type = OPTP_INT, .as_int = 0 }; + if(ExeBuilder_Append(exeb, error, OPCODE_RETURN, &op, 1, Source_GetSize(ast->src), 0)) { exe = ExeBuilder_Finalize(exeb, error); diff --git a/src/compiler/parse.c b/src/compiler/parse.c index 9e15754..16034ec 100644 --- a/src/compiler/parse.c +++ b/src/compiler/parse.c @@ -83,6 +83,7 @@ typedef enum { TLCBRK = '{', TRCBRK = '}', TDOT = '.', + TCOMMA = ',', TDONE = 256, TINT, @@ -126,7 +127,7 @@ typedef struct { } Context; static Node *parse_statement(Context *ctx); -static Node *parse_expression(Context *ctx); +static Node *parse_expression(Context *ctx, _Bool allow_toplev_tuples); static Node *parse_expression_statement(Context *ctx); static Node *parse_ifelse_statement(Context *ctx); static Node *parse_compound_statement(Context *ctx, TokenKind end); @@ -141,7 +142,8 @@ static inline _Bool isoper(char c) return c == '+' || c == '-' || c == '*' || c == '/' || c == '<' || c == '>' || - c == '!' || c == '='; + c == '!' || c == '=' || + c == ','; } /* Symbol: tokenize @@ -311,17 +313,18 @@ static Token *tokenize(Source *src, BPAlloc *alloc, Error *error) int size; const char *text; } optable[] = { - { TADD, 1, "+" }, - { TSUB, 1, "-" }, - { TMUL, 1, "*" }, - { TDIV, 1, "/" }, - { TEQL, 2, "==" }, - { TNQL, 2, "!=" }, - { TLSS, 1, "<" }, - { TLEQ, 2, "<=" }, - { TGRT, 1, ">" }, - { TGEQ, 2, ">=" }, - { TASS, 1, "=" }, + { TADD, 1, "+" }, + { TSUB, 1, "-" }, + { TMUL, 1, "*" }, + { TDIV, 1, "/" }, + { TEQL, 2, "==" }, + { TNQL, 2, "!=" }, + { TLSS, 1, "<" }, + { TLEQ, 2, "<=" }, + { TGRT, 1, ">" }, + { TGEQ, 2, ">=" }, + { TASS, 1, "=" }, + { TCOMMA, 1, "," }, }; _Bool found = 0; @@ -598,11 +601,19 @@ static Node *parse_statement(Context *ctx) next(ctx); // Consume the "return" keyword. - Node *val = parse_expression_statement(ctx); + Node *val = parse_expression(ctx, 1); if(val == NULL) return NULL; + if(current(ctx) != ';') + { + Error_Report(ctx->error, 0, "Got token \"%.*s\" where \";\" was expected", ctx->token->length, ctx->src + ctx->token->offset); + return NULL; + } + + next(ctx); // Consume the ';'. + ReturnNode *node = BPAlloc_Malloc(ctx->alloc, sizeof(ReturnNode)); if(node == NULL) @@ -638,7 +649,7 @@ static Node *parse_expression_statement(Context *ctx) { assert(ctx != NULL); - Node *expr = parse_expression(ctx); + Node *expr = parse_expression(ctx, 1); if(expr == NULL) return NULL; @@ -829,7 +840,7 @@ static Node *parse_list_primary_expression(Context *ctx) while(1) { // Parse. - Node *item = parse_expression(ctx); + Node *item = parse_expression(ctx, 0); if(item == NULL) return NULL; @@ -940,7 +951,7 @@ static Node *parse_map_primary_expression(Context *ctx) } else { - key = parse_expression(ctx); + key = parse_expression(ctx, 0); } if(key == NULL) @@ -961,7 +972,7 @@ static Node *parse_map_primary_expression(Context *ctx) next(ctx); // Parse. - Node *item = parse_expression(ctx); + Node *item = parse_expression(ctx, 0); if(item == NULL) return NULL; @@ -1100,7 +1111,7 @@ static Node *parse_primary_expresion(Context *ctx) { next(ctx); // Consume the '('. - Node *node = parse_expression(ctx); + Node *node = parse_expression(ctx, 1); if(node == NULL) return NULL; @@ -1419,7 +1430,7 @@ static Node *parse_postfix_expression(Context *ctx) while(1) { // Parse. - Node *arg = parse_expression(ctx); + Node *arg = parse_expression(ctx, 0); if(arg == NULL) return NULL; @@ -1552,7 +1563,7 @@ static inline _Bool isbinop(Token *tok) tok->kind == TLEQ || tok->kind == TGEQ || tok->kind == TEQL || tok->kind == TNQL || tok->kind == TKWAND || tok->kind == TKWOR || - tok->kind == '='; + tok->kind == '=' || tok->kind == ','; } static inline _Bool isrightassoc(Token *tok) @@ -1592,6 +1603,9 @@ static inline int precedenceof(Token *tok) case '*': case '/': return 5; + + case ',': + return 6; default: return -100000000; @@ -1601,12 +1615,15 @@ static inline int precedenceof(Token *tok) return -100000000; } -static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec) +static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec, _Bool allow_toplev_tuples) { while(isbinop(ctx->token) && precedenceof(ctx->token) >= min_prec) { Token *op = ctx->token; + if(op->kind == ',' && allow_toplev_tuples == 0) + break; + next(ctx); Node *right_expr = parse_prefix_expression(ctx); @@ -1616,7 +1633,7 @@ static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec) while(isbinop(ctx->token) && (precedenceof(ctx->token) > precedenceof(op) || (precedenceof(ctx->token) == precedenceof(op) && isrightassoc(ctx->token)))) { - right_expr = parse_expression_2(ctx, right_expr, precedenceof(op) + 1); + right_expr = parse_expression_2(ctx, right_expr, precedenceof(op) + 1, allow_toplev_tuples); if(right_expr == NULL) return NULL; @@ -1652,6 +1669,7 @@ static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec) case TKWAND: temp->base.kind = EXPR_AND; break; case TKWOR: temp->base.kind = EXPR_OR; break; case '=': temp->base.kind = EXPR_ASS; break; + case ',': temp->base.kind = EXPR_PAIR; break; default: UNREACHABLE; @@ -1670,7 +1688,7 @@ static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec) return left_expr; } -static Node *parse_expression(Context *ctx) +static Node *parse_expression(Context *ctx, _Bool allow_toplev_tuples) { Node *left_expr = parse_prefix_expression(ctx); @@ -1680,7 +1698,7 @@ static Node *parse_expression(Context *ctx) if(done(ctx)) return left_expr; - return parse_expression_2(ctx, left_expr, -1000000000); + return parse_expression_2(ctx, left_expr, -1000000000, allow_toplev_tuples); } static Node *parse_ifelse_statement(Context *ctx) @@ -1704,7 +1722,7 @@ static Node *parse_ifelse_statement(Context *ctx) next(ctx); // Consume the "if" keyword. - Node *condition = parse_expression(ctx); + Node *condition = parse_expression(ctx, 1); if(condition == NULL) return NULL; @@ -1987,7 +2005,7 @@ static Node *parse_while_statement(Context *ctx) next(ctx); // Consume the "while" keyword. - Node *condition = parse_expression(ctx); + Node *condition = parse_expression(ctx, 1); if(condition == NULL) return NULL; @@ -2073,7 +2091,7 @@ static Node *parse_dowhile_statement(Context *ctx) next(ctx); // Consume the "while" keyword. - Node *condition = parse_expression(ctx); + Node *condition = parse_expression(ctx, 1); if(condition == NULL) return NULL; diff --git a/src/main.c b/src/main.c index 27c202b..1b9bff6 100644 --- a/src/main.c +++ b/src/main.c @@ -159,12 +159,15 @@ static _Bool interpret(Source *src) Runtime_SetBuiltins(runt, bins); - Object *o = run(runt, (Error*) &error, exe, 0, NULL, NULL, 0); + Object *rets[8]; + unsigned int maxretc = sizeof(rets)/sizeof(rets[0]); + + int retc = run(runt, (Error*) &error, exe, 0, NULL, NULL, 0, rets, maxretc); // NOTE: The pointer to the builtins object is invalidated // now because it may be moved by the garbage collector. - if(o == NULL) + if(retc < 0) { print_error("Runtime", (Error*) &error); @@ -179,7 +182,7 @@ static _Bool interpret(Source *src) Runtime_Free(runt); Executable_Free(exe); - return o != NULL; + return retc > -1; } static _Bool disassemble(Source *src) diff --git a/src/objects/objects.c b/src/objects/objects.c index 5fbf1e4..1981938 100644 --- a/src/objects/objects.c +++ b/src/objects/objects.c @@ -120,10 +120,9 @@ Object *Object_Copy(Object *obj, Heap *heap, Error *err) return type->copy(obj, heap, err); } -Object *Object_Call(Object *obj, Object **argv, unsigned int argc, Heap *heap, Error *err) +int Object_Call(Object *obj, Object **argv, unsigned int argc, Object **rets, unsigned int maxrets, Heap *heap, Error *err) { - assert(err); - assert(obj); + assert(err != NULL && obj != NULL); const TypeObject *type = Object_GetType(obj); assert(type); @@ -131,10 +130,10 @@ Object *Object_Call(Object *obj, Object **argv, unsigned int argc, Heap *heap, E if(type->call == NULL) { Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__); - return NULL; + return -1; } - return type->call(obj, argv, argc, heap, err); + return type->call(obj, argv, argc, rets, maxrets, heap, err); } void Object_Print(Object *obj, FILE *fp) diff --git a/src/objects/objects.h b/src/objects/objects.h index ba792fc..9a78b4a 100644 --- a/src/objects/objects.h +++ b/src/objects/objects.h @@ -66,12 +66,12 @@ struct TypeObject { unsigned int size; AtomicType atomic; - _Bool (*init)(Object *self, Error *err); - _Bool (*free)(Object *self, Error *err); - int (*hash)(Object *self); - Object* (*copy)(Object *self, Heap *heap, Error *err); - Object* (*call)(Object *self, Object **argv, unsigned int argc, Heap *heap, Error *err); - void (*print)(Object *self, FILE *fp); + _Bool (*init)(Object *self, Error *err); + _Bool (*free)(Object *self, Error *err); + int (*hash)(Object *self); + Object* (*copy)(Object *self, Heap *heap, Error *err); + int (*call)(Object *self, Object **argv, unsigned int argc, Object **rets, unsigned int maxrets, Heap *heap, Error *err); + void (*print)(Object *self, FILE *fp); unsigned int (*deepsize)(const Object *self); // Collections. @@ -123,7 +123,7 @@ unsigned int Object_GetDeepSize(const Object *obj, Error *err); void *Object_GetBufferAddrAndSize(Object *obj, int *size, Error *error); int Object_Hash (Object *obj); Object* Object_Copy (Object *obj, Heap *heap, Error *err); -Object* Object_Call (Object *obj, Object **argv, unsigned int argc, Heap *heap, Error *err); +int Object_Call (Object *obj, Object **argv, unsigned int argc, Object **rets, unsigned int maxrets, Heap *heap, Error *err); void Object_Print (Object *obj, FILE *fp); Object* Object_Select(Object *coll, Object *key, Heap *heap, Error *err); Object* Object_Delete(Object *coll, Object *key, Heap *heap, Error *err); diff --git a/src/runtime/o_func.c b/src/runtime/o_func.c index 486b094..5037fda 100644 --- a/src/runtime/o_func.c +++ b/src/runtime/o_func.c @@ -57,7 +57,7 @@ static void walk(Object *self, void (*callback)(Object **referer, void *userp), callback(&func->closure, userp); } -static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap, Error *error) +static int call(Object *self, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Heap *heap, Error *error) { assert(self != NULL && heap != NULL && error != NULL); @@ -88,7 +88,7 @@ static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap, if(argv2 == NULL) { Error_Report(error, 1, "No memory"); - return NULL; + return -1; } // Copy the provided arguments. @@ -101,21 +101,21 @@ static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap, argv2[i] = Object_NewNone(heap, error); if(argv2[i] == NULL) - return 0; + return -1; } } else // The right amount of arguments was provided. argv2 = argv; - Object *result = run(func->runtime, error, func->exe, func->index, func->closure, argv2, expected_argc); + int retc = run(func->runtime, error, func->exe, func->index, func->closure, argv2, expected_argc, rets, maxretc); // NOTE: Every object reference is invalidated from here. if(argv2 != argv) free(argv2); - return result; + return retc; } static TypeObject t_func = { diff --git a/src/runtime/o_nfunc.c b/src/runtime/o_nfunc.c index 623704c..540f245 100644 --- a/src/runtime/o_nfunc.c +++ b/src/runtime/o_nfunc.c @@ -43,11 +43,11 @@ typedef struct { Object base; Runtime *runtime; - Object *(*callback)(Runtime *runtime, Object **argv, unsigned int argc, Error *error); + int (*callback)(Runtime *runtime, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Error *error); int argc; } NativeFunctionObject; -static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap, Error *error) +static int call(Object *self, Object **argv, unsigned int argc, Object **rets, unsigned int maxretc, Heap *heap, Error *error) { assert(self != NULL); assert(heap != NULL); @@ -87,7 +87,7 @@ static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap, if(argv2 == NULL) { Error_Report(error, 1, "No memory"); - return NULL; + return -1; } // Copy the provided arguments. @@ -102,27 +102,24 @@ static Object *call(Object *self, Object **argv, unsigned int argc, Heap *heap, if(argv2[i] == NULL) { free(argv2); - return NULL; + return -1; } } } else UNREACHABLE; assert(func->callback != NULL); - Object *result = func->callback(func->runtime, argv2, argc2, error); + int retc = func->callback(func->runtime, argv2, argc2, rets, maxretc, error); // NOTE: Since the callback may have executed some bytecode, a GC // cycle may have been triggered, therefore we must assume // every object reference that was locally saved is invalidated // from here (the returned object is good tho). - 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; + return retc; } static TypeObject t_nfunc = { @@ -156,7 +153,7 @@ static TypeObject t_nfunc = { * The newly created object. If an error occurred, NULL is returned * and information about the error is stored in the [error] argument. */ -Object *Object_FromNativeFunction(Runtime *runtime, Object *(*callback)(Runtime*, Object**, unsigned int, Error*), int argc, Heap *heap, Error *error) +Object *Object_FromNativeFunction(Runtime *runtime, int (*callback)(Runtime*, Object**, unsigned int, Object**, unsigned int, Error*), int argc, Heap *heap, Error *error) { assert(callback != NULL); diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index 4683ee7..28bfa8b 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -77,7 +77,7 @@ int Runtime_GetCurrentIndex(Runtime *runtime) Executable *Runtime_GetCurrentExecutable(Runtime *runtime) { if(runtime->depth == 0) - return NULL; + return NULL; else return runtime->frame->exe; } @@ -805,11 +805,13 @@ static _Bool step(Runtime *runtime, Error *error) case OPCODE_CALL: { - assert(opc == 1); + assert(opc == 2); assert(ops[0].type == OPTP_INT); + assert(ops[1].type == OPTP_INT); int argc = ops[0].as_int; - assert(argc >= 0); + int retc = ops[1].as_int; + assert(argc >= 0 && retc > 0); if(runtime->frame->used < argc + 1) { @@ -839,19 +841,32 @@ static _Bool step(Runtime *runtime, Error *error) (void) Runtime_Pop(runtime, error, argc+1); assert(error->occurred == 0); - Object *obj = Object_Call(callable, argv, argc, runtime->heap, error); - // NOTE: Every local object reference is invalidated from here. - - if(obj == NULL) - { - assert(error->occurred != 0); + Object *rets[8]; + unsigned int maxrets = sizeof(rets)/sizeof(rets[0]); + + int num_rets = Object_Call(callable, argv, argc, rets, maxrets, runtime->heap, error); + + if(num_rets < 0) return 0; - } + + // NOTE: Every local object reference is invalidated from here. assert(error->occurred == 0); - if(!Runtime_Push(runtime, error, obj)) - return 0; + for(int g = 0; g < MIN(num_rets, retc); g += 1) + if(!Runtime_Push(runtime, error, rets[g])) + return 0; + + for(int g = 0; g < retc - num_rets; g += 1) + { + Object *temp = Object_NewNone(Runtime_GetHeap(runtime), error); + + if(temp == NULL) + return NULL; + + if(!Runtime_Push(runtime, error, temp)) + return 0; + } return 1; } @@ -1126,7 +1141,14 @@ static _Bool step(Runtime *runtime, Error *error) } case OPCODE_RETURN: - return 0; + { + assert(opc == 1); + assert(ops[0].type == OPTP_INT); + int retc = ops[0].as_int; + assert(retc >= 0); + assert(retc == runtime->frame->used); + return 0; + } case OPCODE_JUMP: assert(opc == 1); @@ -1233,7 +1255,7 @@ static _Bool collect(Runtime *runtime, Error *error) return Heap_StopCollection(runtime->heap); } -Object *run(Runtime *runtime, Error *error, Executable *exe, int index, Object *closure, Object **argv, int argc) +int run(Runtime *runtime, Error *error, Executable *exe, int index, Object *closure, Object **argv, int argc, Object **rets, int maxretc) { assert(runtime != NULL); assert(error != NULL); @@ -1244,7 +1266,7 @@ Object *run(Runtime *runtime, Error *error, Executable *exe, int index, Object * if(runtime->depth == MAX_FRAMES) { Error_Report(error, 1, "Maximum nested call limit of %d was reached", MAX_FRAMES); - return NULL; + return -1; } assert(runtime->depth < MAX_FRAMES); @@ -1260,12 +1282,12 @@ Object *run(Runtime *runtime, Error *error, Executable *exe, int index, Object * frame.used = 0; if(frame.locals == NULL) - return NULL; + return -1; if(frame.exe == NULL) { Error_Report(error, 1, "Failed to copy executable"); - return NULL; + return -1; } // Add the frame to the runtime. @@ -1275,7 +1297,7 @@ Object *run(Runtime *runtime, Error *error, Executable *exe, int index, Object * } // This is what the function will return. - Object *result = NULL; + int retc = -1; // Push the initial values of the frame. for(int i = 0; i < argc; i += 1) @@ -1317,19 +1339,12 @@ Object *run(Runtime *runtime, Error *error, Executable *exe, int index, Object * // If an error occurred, we want to return NULL. if(error->occurred == 0) { - // If the step function left something - // on the stack, we return that. If it - // didn't, we return some other default - // value like "none". - if(frame.used == 0) + retc = MIN(frame.used, maxretc); + + for(int i = 0; i < retc; i += 1) { - // Nothing to return on the stack. Set to none. - result = Object_NewNone(runtime->heap, error); - } - else - { - result = Stack_Top(runtime->stack, 0); - assert(result != NULL); + rets[i] = Stack_Top(runtime->stack, i - retc + 1); + assert(rets[i] != NULL); } } @@ -1348,5 +1363,5 @@ cleanup: Executable_Free(frame.exe); } - return result; + return retc; } \ No newline at end of file diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 174af81..c62ee43 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -54,7 +54,7 @@ Executable *Runtime_GetCurrentExecutable(Runtime *runtime); Snapshot *Snapshot_New(Runtime *runtime); void Snapshot_Free(Snapshot *snapshot); void Snapshot_Print(Snapshot *snapshot, FILE *fp); -Object *run(Runtime *runtime, Error *error, Executable *exe, int index, Object *closure, Object **argv, int argc); +int run(Runtime *runtime, Error *error, Executable *exe, int index, Object *closure, Object **argv, int argc, Object **rets, int maxretc); typedef enum { SM_END, @@ -79,7 +79,7 @@ struct StaticMapSlot { _Bool as_bool; long long int as_int; double as_float; - Object *(*as_funct)(Runtime*, Object**, unsigned int, Error*); + int (*as_funct)(Runtime*, Object**, unsigned int, Object**, unsigned int, Error*); TypeObject *as_type; }; union { int argc; int length; }; @@ -87,8 +87,7 @@ struct StaticMapSlot { Object *Object_NewStaticMap(const StaticMapSlot *slots, Runtime *runt, Error *error); Object *Object_FromNojaFunction(Runtime *runtime, Executable *exe, int index, int argc, Object *closure, Heap *heap, Error *error); -Object *Object_FromNativeFunction(Runtime *runtime, Object *(*callback)(Runtime*, Object**, unsigned int, Error*), int argc, Heap *heap, Error *error); - +Object *Object_FromNativeFunction(Runtime *runtime, int (*callback)(Runtime*, Object**, unsigned int, Object**, unsigned int, Error*), int argc, Heap *heap, Error *error); typedef struct { Error base; Runtime *runtime;