From 4f3f594b822667699e46799a6b720da389350696 Mon Sep 17 00:00:00 2001 From: CPunch Date: Mon, 14 Dec 2020 14:38:46 -0600 Subject: [PATCH] added multiple return value support to the parser --- src/cdebug.c | 2 +- src/cparse.c | 104 ++++++++++++++++++++++++++++++++++++--------------- src/cvm.c | 23 +++++------- 3 files changed, 84 insertions(+), 45 deletions(-) diff --git a/src/cdebug.c b/src/cdebug.c index 3dcfcaa..caa9043 100644 --- a/src/cdebug.c +++ b/src/cdebug.c @@ -175,7 +175,7 @@ int disasmInstr(CChunk *chunk, int offset, int indent) { case OP_INCOBJECT: return u8u16OperandInstruction("OP_INCOBJECT", chunk, offset); case OP_RETURN: - return simpleInstruction("OP_RETURN", offset); + return u8OperandInstruction("OP_RETURN", chunk, offset); default: printf("Unknown opcode! [%d]\n", i); exit(0); diff --git a/src/cparse.c b/src/cparse.c index 13676b4..1ea6451 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -36,6 +36,7 @@ typedef struct CCompilerState { int scopeDepth; int pushedValues; int savedPushed; + int expectedValues; struct CCompilerState* enclosing; } CCompilerState; @@ -75,7 +76,7 @@ typedef struct { static void parsePrecedence(CParseState*, Precedence); static void variable(CParseState *pstate, bool canAssign); -static void expression(CParseState*); +static int expression(CParseState *pstate, int needed, bool forceNeeded); static void statement(CParseState *pstate); static void declaration(CParseState *pstate); static void function(CParseState *pstate, FunctionType type); @@ -94,6 +95,7 @@ static void initCompilerState(CParseState* pstate, CCompilerState *ccstate, Func ccstate->scopeDepth = 0; ccstate->pushedValues = 0; ccstate->savedPushed = 0; + ccstate->expectedValues = 0; ccstate->type = type; ccstate->function = cosmoO_newFunction(pstate->state); ccstate->function->module = pstate->module; @@ -340,7 +342,7 @@ static int parseArguments(CParseState *pstate) { // there are args to parse! if (!check(pstate, TOKEN_RIGHT_PAREN)) { do { - expression(pstate); + expression(pstate, 1, true); args++; } while(match(pstate, TOKEN_COMMA)); } @@ -434,7 +436,7 @@ static void binary(CParseState *pstate, bool canAssign) { } static void group(CParseState *pstate, bool canAssign) { - expression(pstate); + expression(pstate, 1, true); consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')'"); } @@ -469,7 +471,7 @@ static void namedVariable(CParseState *pstate, CToken name, bool canAssign, bool if (canAssign && match(pstate, TOKEN_EQUAL)) { // setter - expression(pstate); + expression(pstate, 1, true); _etterOP(pstate, opSet, arg); valuePopped(pstate, 1); } else if (canIncrement && match(pstate, TOKEN_PLUS_PLUS)) { // i++ @@ -549,8 +551,8 @@ static void call_(CParseState *pstate, bool canAssign) { valuePopped(pstate, argCount + 1); // all of these values will be popped off the stack when returned (+1 for the function) writeu8(pstate, OP_CALL); writeu8(pstate, argCount); - writeu8(pstate, 1); // TODO - valuePushed(pstate, 1); + writeu8(pstate, pstate->compiler->expectedValues); // TODO + valuePushed(pstate, pstate->compiler->expectedValues); } static void object(CParseState *pstate, bool canAssign) { @@ -560,12 +562,12 @@ static void object(CParseState *pstate, bool canAssign) { if (!match(pstate, TOKEN_RIGHT_BRACE)) { do { // parse the key first - expression(pstate); // should parse until ':' + expression(pstate, 1, true); // should parse until ':' consume(pstate, TOKEN_COLON, "Expected ':' to mark end of key and start of value!"); // now, parse the value (until comma) - expression(pstate); + expression(pstate, 1, true); // "pop" the 2 values valuePopped(pstate, 2); @@ -587,7 +589,7 @@ static void dot(CParseState *pstate, bool canAssign) { if (canAssign && match(pstate, TOKEN_EQUAL)) { writeu8(pstate, OP_LOADCONST); writeu16(pstate, name); - expression(pstate); + expression(pstate, 1, true); writeu8(pstate, OP_SETOBJECT); valuePopped(pstate, 2); // pops key, value & object } else if (match(pstate, TOKEN_PLUS_PLUS)) { // increment the field @@ -604,8 +606,9 @@ static void dot(CParseState *pstate, bool canAssign) { uint8_t args = parseArguments(pstate); writeu8(pstate, OP_INVOKE); writeu8(pstate, args); - writeu8(pstate, 1); // TODO - valuePopped(pstate, args); // pops the function & the object but pushes a result + writeu8(pstate, pstate->compiler->expectedValues); // TODO + valuePopped(pstate, args+1); // args + function + valuePushed(pstate, pstate->compiler->expectedValues); } else { writeu8(pstate, OP_LOADCONST); writeu16(pstate, name); @@ -615,11 +618,11 @@ static void dot(CParseState *pstate, bool canAssign) { } static void _index(CParseState *pstate, bool canAssign) { - expression(pstate); + expression(pstate, 1, true); consume(pstate, TOKEN_RIGHT_BRACKET, "Expected ']' to end index."); if (canAssign && match(pstate, TOKEN_EQUAL)) { - expression(pstate); + expression(pstate, 1, true); writeu8(pstate, OP_NEWINDEX); valuePopped(pstate, 2); // pops key, value & object } else if (match(pstate, TOKEN_PLUS_PLUS)) { // increment the field @@ -903,28 +906,44 @@ static void block(CParseState *pstate) { consume(pstate, TOKEN_END, "'end' expected to end block.'"); } -static void varDeclaration(CParseState *pstate, bool forceLocal) { - uint16_t global = parseVariable(pstate, "Expected identifer!", forceLocal); +static void varDeclaration(CParseState *pstate, bool forceLocal, int expectedValues) { + uint16_t ident = parseVariable(pstate, "Expected identifer!", forceLocal); + expectedValues++; if (match(pstate, TOKEN_EQUAL)) { // assigning a variable - valuePopped(pstate, 1); // we are expecting a value // consume all the ',' do { - expression(pstate); + valuePopped(pstate, 1); + int pushed = expression(pstate, expectedValues, false); + valuePushed(pstate, 1); + expectedValues -= pushed; + + if (expectedValues < 0) { // these values need to be thrown away + writePop(pstate, -expectedValues); + valuePopped(pstate, -expectedValues); + expectedValues = 1; + } } while (match(pstate, TOKEN_COMMA)); - valuePushed(pstate, 1); + // for any expected value we didn't get + while (expectedValues-- > 0) { + valuePushed(pstate, 1); + writeu8(pstate, OP_NIL); + } + + } else if (match(pstate, TOKEN_COMMA)) { + varDeclaration(pstate, forceLocal, expectedValues); } else { writeu8(pstate, OP_NIL); valuePushed(pstate, 1); } - defineVariable(pstate, global, forceLocal); + defineVariable(pstate, ident, forceLocal); } static void ifStatement(CParseState *pstate) { - expression(pstate); + expression(pstate, 1, true); consume(pstate, TOKEN_THEN, "Expect 'then' after expression."); int jump = writeJmp(pstate, OP_PEJMP); @@ -967,7 +986,7 @@ static void ifStatement(CParseState *pstate) { static void whileStatement(CParseState *pstate) { int jumpLocation = getChunk(pstate)->count; - expression(pstate); + expression(pstate, 1, true); consume(pstate, TOKEN_DO, "expected 'do' after conditional expression."); @@ -1047,12 +1066,20 @@ static void returnStatement(CParseState *pstate) { if (blockFollow(pstate->current)) { // does this return have a value writeu8(pstate, OP_NIL); writeu8(pstate, OP_RETURN); + writeu8(pstate, 1); return; } - expression(pstate); + // grab return values + int rvalues = 0; + do { + expression(pstate, 1, true); + rvalues++; + } while (match(pstate, TOKEN_COMMA)); + writeu8(pstate, OP_RETURN); - valuePopped(pstate, 1); + writeu8(pstate, rvalues); + valuePopped(pstate, rvalues); } static void localFunction(CParseState *pstate) { @@ -1080,7 +1107,7 @@ static void forLoop(CParseState *pstate) { // parse conditional int exitJmp = -1; if (!match(pstate, TOKEN_EOS)) { - expression(pstate); + expression(pstate, 1, true); consume(pstate, TOKEN_EOS, "Expected ';' after conditional"); exitJmp = writeJmp(pstate, OP_PEJMP); @@ -1092,9 +1119,7 @@ static void forLoop(CParseState *pstate) { int bodyJmp = writeJmp(pstate, OP_JMP); int iteratorStart = getChunk(pstate)->count; - int savedPushed = pstate->compiler->pushedValues; - expression(pstate); - alignStack(pstate, savedPushed); + expression(pstate, 0, true); consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')' after iterator"); writeJmpBack(pstate, loopStart); @@ -1128,21 +1153,36 @@ static void synchronize(CParseState *pstate) { } } -static void expression(CParseState *pstate) { +static int expression(CParseState *pstate, int needed, bool forceNeeded) { + int lastExpected = pstate->compiler->expectedValues; + int saved = pstate->compiler->pushedValues + needed; + pstate->compiler->expectedValues = needed; + parsePrecedence(pstate, PREC_ASSIGNMENT); + + if (pstate->compiler->pushedValues > saved) { + writePop(pstate, pstate->compiler->pushedValues - saved); + valuePopped(pstate, pstate->compiler->pushedValues - saved); + } else if (forceNeeded && pstate->compiler->pushedValues < saved) { + error(pstate, "Missing expression!"); + } + + pstate->compiler->expectedValues = lastExpected; + + return pstate->compiler->pushedValues - (saved - needed); } static void expressionStatement(CParseState *pstate) { pstate->compiler->savedPushed = pstate->compiler->pushedValues; if (match(pstate, TOKEN_VAR)) { - varDeclaration(pstate, false); + varDeclaration(pstate, false, 0); } else if (match(pstate, TOKEN_LOCAL)) { // force declare a local if (match(pstate, TOKEN_FUNCTION)) localFunction(pstate); // force local a function else - varDeclaration(pstate, true); // force local a variable + varDeclaration(pstate, true, 0); // force local a variable } else if (match(pstate, TOKEN_IF)) { ifStatement(pstate); } else if (match(pstate, TOKEN_DO)) { @@ -1160,7 +1200,8 @@ static void expressionStatement(CParseState *pstate) { } else if (match(pstate, TOKEN_RETURN)) { returnStatement(pstate); } else { - expression(pstate); + // we don't need/want any values on the stack, so call expression with 0 values needed + expression(pstate, 0, false); } // realign the stack @@ -1183,6 +1224,7 @@ static CObjFunction *endCompiler(CParseState *pstate) { popLocals(pstate, pstate->compiler->scopeDepth); // remove the locals from other scopes writeu8(pstate, OP_NIL); writeu8(pstate, OP_RETURN); + writeu8(pstate, 1); // update pstate to next compiler state CCompilerState *cachedCCState = pstate->compiler; diff --git a/src/cvm.c b/src/cvm.c index b770fd3..f221257 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -116,7 +116,7 @@ static inline void callCFunction(CState *state, CosmoCFunction cfunc, int args, cosmoM_unfreezeGC(state); // remember where the return values are - CValue* results = cosmoV_getTop(state, 0); + CValue* results = cosmoV_getTop(state, nres-1); state->top = savedBase + offset; // set stack @@ -148,7 +148,7 @@ bool call(CState *state, CObjClosure *closure, int args, int nresults, int offse return false; // remember where the return values are - CValue* results = cosmoV_getTop(state, 0); + CValue* results = cosmoV_getTop(state, nres-1); // pop the callframe and return results :) popCallFrame(state, offset); @@ -167,15 +167,15 @@ bool call(CState *state, CObjClosure *closure, int args, int nresults, int offse return true; } -bool invokeMethod(CState* state, CObjObject *obj, CValue func, int args, int nresults) { +bool invokeMethod(CState* state, CObjObject *obj, CValue func, int args, int nresults, int offset) { // first, set the first argument to the object StkPtr temp = cosmoV_getTop(state, args); *temp = cosmoV_newObj(obj); if (IS_CFUNCTION(func)) { - callCFunction(state, cosmoV_readCFunction(func), args+1, nresults, 1); + callCFunction(state, cosmoV_readCFunction(func), args+1, nresults, offset); } else if (IS_CLOSURE(func)) { - call(state, cosmoV_readClosure(func), args+1, nresults, 1); // offset = 1 so our stack is properly reset + call(state, cosmoV_readClosure(func), args+1, nresults, offset); // offset = 1 so our stack is properly reset } else { cosmoV_error(state, "Cannot invoke non-function type %s!", cosmoV_typeStr(func)); } @@ -202,7 +202,7 @@ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) { } case COBJ_METHOD: { CObjMethod *method = (CObjMethod*)cosmoV_readObj(*val); - invokeMethod(state, method->obj, method->func, args, nresults); + invokeMethod(state, method->obj, method->func, args, nresults, 1); break; } case COBJ_OBJECT: { // object is being instantiated, making another object @@ -213,7 +213,7 @@ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) { // check if they defined an initalizer if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) { - invokeMethod(state, newObj, ret, args, nresults); + invokeMethod(state, newObj, ret, args, nresults, 1); } else { // no default initalizer if (args != 0) { @@ -549,11 +549,7 @@ int cosmoV_execute(CState *state) { cosmoO_getObject(state, object, *key, &val); // we use cosmoO_getObject instead of the cosmoV_getObject wrapper so we get the raw value from the object instead of the CObjMethod wrapper // now invoke the method! - invokeMethod(state, object, val, args, nres); - - // moves return value & resets stack (key now points to the stack location of our return value) - *temp = *key; - state->top = key; + invokeMethod(state, object, val, args, nres, 0); break; } case OP_ADD: { // pop 2 values off the stack & try to add them together @@ -770,7 +766,8 @@ int cosmoV_execute(CState *state) { case OP_FALSE: cosmoV_pushBoolean(state, false); break; case OP_NIL: cosmoV_pushValue(state, cosmoV_newNil()); break; case OP_RETURN: { - return 1; + uint8_t res = READBYTE(); + return res; } default: CERROR("unknown opcode!");