added multiple return value support to the parser

This commit is contained in:
CPunch 2020-12-14 14:38:46 -06:00
parent 48ceca1834
commit 4f3f594b82
3 changed files with 84 additions and 45 deletions

View File

@ -175,7 +175,7 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
case OP_INCOBJECT: case OP_INCOBJECT:
return u8u16OperandInstruction("OP_INCOBJECT", chunk, offset); return u8u16OperandInstruction("OP_INCOBJECT", chunk, offset);
case OP_RETURN: case OP_RETURN:
return simpleInstruction("OP_RETURN", offset); return u8OperandInstruction("OP_RETURN", chunk, offset);
default: default:
printf("Unknown opcode! [%d]\n", i); printf("Unknown opcode! [%d]\n", i);
exit(0); exit(0);

View File

@ -36,6 +36,7 @@ typedef struct CCompilerState {
int scopeDepth; int scopeDepth;
int pushedValues; int pushedValues;
int savedPushed; int savedPushed;
int expectedValues;
struct CCompilerState* enclosing; struct CCompilerState* enclosing;
} CCompilerState; } CCompilerState;
@ -75,7 +76,7 @@ typedef struct {
static void parsePrecedence(CParseState*, Precedence); static void parsePrecedence(CParseState*, Precedence);
static void variable(CParseState *pstate, bool canAssign); 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 statement(CParseState *pstate);
static void declaration(CParseState *pstate); static void declaration(CParseState *pstate);
static void function(CParseState *pstate, FunctionType type); static void function(CParseState *pstate, FunctionType type);
@ -94,6 +95,7 @@ static void initCompilerState(CParseState* pstate, CCompilerState *ccstate, Func
ccstate->scopeDepth = 0; ccstate->scopeDepth = 0;
ccstate->pushedValues = 0; ccstate->pushedValues = 0;
ccstate->savedPushed = 0; ccstate->savedPushed = 0;
ccstate->expectedValues = 0;
ccstate->type = type; ccstate->type = type;
ccstate->function = cosmoO_newFunction(pstate->state); ccstate->function = cosmoO_newFunction(pstate->state);
ccstate->function->module = pstate->module; ccstate->function->module = pstate->module;
@ -340,7 +342,7 @@ static int parseArguments(CParseState *pstate) {
// there are args to parse! // there are args to parse!
if (!check(pstate, TOKEN_RIGHT_PAREN)) { if (!check(pstate, TOKEN_RIGHT_PAREN)) {
do { do {
expression(pstate); expression(pstate, 1, true);
args++; args++;
} while(match(pstate, TOKEN_COMMA)); } while(match(pstate, TOKEN_COMMA));
} }
@ -434,7 +436,7 @@ static void binary(CParseState *pstate, bool canAssign) {
} }
static void group(CParseState *pstate, bool canAssign) { static void group(CParseState *pstate, bool canAssign) {
expression(pstate); expression(pstate, 1, true);
consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')'"); 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)) { if (canAssign && match(pstate, TOKEN_EQUAL)) {
// setter // setter
expression(pstate); expression(pstate, 1, true);
_etterOP(pstate, opSet, arg); _etterOP(pstate, opSet, arg);
valuePopped(pstate, 1); valuePopped(pstate, 1);
} else if (canIncrement && match(pstate, TOKEN_PLUS_PLUS)) { // i++ } 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) 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, OP_CALL);
writeu8(pstate, argCount); writeu8(pstate, argCount);
writeu8(pstate, 1); // TODO writeu8(pstate, pstate->compiler->expectedValues); // TODO
valuePushed(pstate, 1); valuePushed(pstate, pstate->compiler->expectedValues);
} }
static void object(CParseState *pstate, bool canAssign) { static void object(CParseState *pstate, bool canAssign) {
@ -560,12 +562,12 @@ static void object(CParseState *pstate, bool canAssign) {
if (!match(pstate, TOKEN_RIGHT_BRACE)) { if (!match(pstate, TOKEN_RIGHT_BRACE)) {
do { do {
// parse the key first // 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!"); consume(pstate, TOKEN_COLON, "Expected ':' to mark end of key and start of value!");
// now, parse the value (until comma) // now, parse the value (until comma)
expression(pstate); expression(pstate, 1, true);
// "pop" the 2 values // "pop" the 2 values
valuePopped(pstate, 2); valuePopped(pstate, 2);
@ -587,7 +589,7 @@ static void dot(CParseState *pstate, bool canAssign) {
if (canAssign && match(pstate, TOKEN_EQUAL)) { if (canAssign && match(pstate, TOKEN_EQUAL)) {
writeu8(pstate, OP_LOADCONST); writeu8(pstate, OP_LOADCONST);
writeu16(pstate, name); writeu16(pstate, name);
expression(pstate); expression(pstate, 1, true);
writeu8(pstate, OP_SETOBJECT); writeu8(pstate, OP_SETOBJECT);
valuePopped(pstate, 2); // pops key, value & object valuePopped(pstate, 2); // pops key, value & object
} else if (match(pstate, TOKEN_PLUS_PLUS)) { // increment the field } 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); uint8_t args = parseArguments(pstate);
writeu8(pstate, OP_INVOKE); writeu8(pstate, OP_INVOKE);
writeu8(pstate, args); writeu8(pstate, args);
writeu8(pstate, 1); // TODO writeu8(pstate, pstate->compiler->expectedValues); // TODO
valuePopped(pstate, args); // pops the function & the object but pushes a result valuePopped(pstate, args+1); // args + function
valuePushed(pstate, pstate->compiler->expectedValues);
} else { } else {
writeu8(pstate, OP_LOADCONST); writeu8(pstate, OP_LOADCONST);
writeu16(pstate, name); writeu16(pstate, name);
@ -615,11 +618,11 @@ static void dot(CParseState *pstate, bool canAssign) {
} }
static void _index(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."); consume(pstate, TOKEN_RIGHT_BRACKET, "Expected ']' to end index.");
if (canAssign && match(pstate, TOKEN_EQUAL)) { if (canAssign && match(pstate, TOKEN_EQUAL)) {
expression(pstate); expression(pstate, 1, true);
writeu8(pstate, OP_NEWINDEX); writeu8(pstate, OP_NEWINDEX);
valuePopped(pstate, 2); // pops key, value & object valuePopped(pstate, 2); // pops key, value & object
} else if (match(pstate, TOKEN_PLUS_PLUS)) { // increment the field } 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.'"); consume(pstate, TOKEN_END, "'end' expected to end block.'");
} }
static void varDeclaration(CParseState *pstate, bool forceLocal) { static void varDeclaration(CParseState *pstate, bool forceLocal, int expectedValues) {
uint16_t global = parseVariable(pstate, "Expected identifer!", forceLocal); uint16_t ident = parseVariable(pstate, "Expected identifer!", forceLocal);
expectedValues++;
if (match(pstate, TOKEN_EQUAL)) { // assigning a variable if (match(pstate, TOKEN_EQUAL)) { // assigning a variable
valuePopped(pstate, 1); // we are expecting a value
// consume all the ',' // consume all the ','
do { 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)); } 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 { } else {
writeu8(pstate, OP_NIL); writeu8(pstate, OP_NIL);
valuePushed(pstate, 1); valuePushed(pstate, 1);
} }
defineVariable(pstate, global, forceLocal); defineVariable(pstate, ident, forceLocal);
} }
static void ifStatement(CParseState *pstate) { static void ifStatement(CParseState *pstate) {
expression(pstate); expression(pstate, 1, true);
consume(pstate, TOKEN_THEN, "Expect 'then' after expression."); consume(pstate, TOKEN_THEN, "Expect 'then' after expression.");
int jump = writeJmp(pstate, OP_PEJMP); int jump = writeJmp(pstate, OP_PEJMP);
@ -967,7 +986,7 @@ static void ifStatement(CParseState *pstate) {
static void whileStatement(CParseState *pstate) { static void whileStatement(CParseState *pstate) {
int jumpLocation = getChunk(pstate)->count; int jumpLocation = getChunk(pstate)->count;
expression(pstate); expression(pstate, 1, true);
consume(pstate, TOKEN_DO, "expected 'do' after conditional expression."); 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 if (blockFollow(pstate->current)) { // does this return have a value
writeu8(pstate, OP_NIL); writeu8(pstate, OP_NIL);
writeu8(pstate, OP_RETURN); writeu8(pstate, OP_RETURN);
writeu8(pstate, 1);
return; return;
} }
expression(pstate); // grab return values
int rvalues = 0;
do {
expression(pstate, 1, true);
rvalues++;
} while (match(pstate, TOKEN_COMMA));
writeu8(pstate, OP_RETURN); writeu8(pstate, OP_RETURN);
valuePopped(pstate, 1); writeu8(pstate, rvalues);
valuePopped(pstate, rvalues);
} }
static void localFunction(CParseState *pstate) { static void localFunction(CParseState *pstate) {
@ -1080,7 +1107,7 @@ static void forLoop(CParseState *pstate) {
// parse conditional // parse conditional
int exitJmp = -1; int exitJmp = -1;
if (!match(pstate, TOKEN_EOS)) { if (!match(pstate, TOKEN_EOS)) {
expression(pstate); expression(pstate, 1, true);
consume(pstate, TOKEN_EOS, "Expected ';' after conditional"); consume(pstate, TOKEN_EOS, "Expected ';' after conditional");
exitJmp = writeJmp(pstate, OP_PEJMP); exitJmp = writeJmp(pstate, OP_PEJMP);
@ -1092,9 +1119,7 @@ static void forLoop(CParseState *pstate) {
int bodyJmp = writeJmp(pstate, OP_JMP); int bodyJmp = writeJmp(pstate, OP_JMP);
int iteratorStart = getChunk(pstate)->count; int iteratorStart = getChunk(pstate)->count;
int savedPushed = pstate->compiler->pushedValues; expression(pstate, 0, true);
expression(pstate);
alignStack(pstate, savedPushed);
consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')' after iterator"); consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')' after iterator");
writeJmpBack(pstate, loopStart); 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); 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) { static void expressionStatement(CParseState *pstate) {
pstate->compiler->savedPushed = pstate->compiler->pushedValues; pstate->compiler->savedPushed = pstate->compiler->pushedValues;
if (match(pstate, TOKEN_VAR)) { if (match(pstate, TOKEN_VAR)) {
varDeclaration(pstate, false); varDeclaration(pstate, false, 0);
} else if (match(pstate, TOKEN_LOCAL)) { } else if (match(pstate, TOKEN_LOCAL)) {
// force declare a local // force declare a local
if (match(pstate, TOKEN_FUNCTION)) if (match(pstate, TOKEN_FUNCTION))
localFunction(pstate); // force local a function localFunction(pstate); // force local a function
else else
varDeclaration(pstate, true); // force local a variable varDeclaration(pstate, true, 0); // force local a variable
} else if (match(pstate, TOKEN_IF)) { } else if (match(pstate, TOKEN_IF)) {
ifStatement(pstate); ifStatement(pstate);
} else if (match(pstate, TOKEN_DO)) { } else if (match(pstate, TOKEN_DO)) {
@ -1160,7 +1200,8 @@ static void expressionStatement(CParseState *pstate) {
} else if (match(pstate, TOKEN_RETURN)) { } else if (match(pstate, TOKEN_RETURN)) {
returnStatement(pstate); returnStatement(pstate);
} else { } 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 // realign the stack
@ -1183,6 +1224,7 @@ static CObjFunction *endCompiler(CParseState *pstate) {
popLocals(pstate, pstate->compiler->scopeDepth); // remove the locals from other scopes popLocals(pstate, pstate->compiler->scopeDepth); // remove the locals from other scopes
writeu8(pstate, OP_NIL); writeu8(pstate, OP_NIL);
writeu8(pstate, OP_RETURN); writeu8(pstate, OP_RETURN);
writeu8(pstate, 1);
// update pstate to next compiler state // update pstate to next compiler state
CCompilerState *cachedCCState = pstate->compiler; CCompilerState *cachedCCState = pstate->compiler;

View File

@ -116,7 +116,7 @@ static inline void callCFunction(CState *state, CosmoCFunction cfunc, int args,
cosmoM_unfreezeGC(state); cosmoM_unfreezeGC(state);
// remember where the return values are // 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 state->top = savedBase + offset; // set stack
@ -148,7 +148,7 @@ bool call(CState *state, CObjClosure *closure, int args, int nresults, int offse
return false; return false;
// remember where the return values are // 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 :) // pop the callframe and return results :)
popCallFrame(state, offset); popCallFrame(state, offset);
@ -167,15 +167,15 @@ bool call(CState *state, CObjClosure *closure, int args, int nresults, int offse
return true; 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 // first, set the first argument to the object
StkPtr temp = cosmoV_getTop(state, args); StkPtr temp = cosmoV_getTop(state, args);
*temp = cosmoV_newObj(obj); *temp = cosmoV_newObj(obj);
if (IS_CFUNCTION(func)) { 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)) { } 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 { } else {
cosmoV_error(state, "Cannot invoke non-function type %s!", cosmoV_typeStr(func)); 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: { case COBJ_METHOD: {
CObjMethod *method = (CObjMethod*)cosmoV_readObj(*val); CObjMethod *method = (CObjMethod*)cosmoV_readObj(*val);
invokeMethod(state, method->obj, method->func, args, nresults); invokeMethod(state, method->obj, method->func, args, nresults, 1);
break; break;
} }
case COBJ_OBJECT: { // object is being instantiated, making another object 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 // check if they defined an initalizer
if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) { if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) {
invokeMethod(state, newObj, ret, args, nresults); invokeMethod(state, newObj, ret, args, nresults, 1);
} else { } else {
// no default initalizer // no default initalizer
if (args != 0) { 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 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! // now invoke the method!
invokeMethod(state, object, val, args, nres); invokeMethod(state, object, val, args, nres, 0);
// moves return value & resets stack (key now points to the stack location of our return value)
*temp = *key;
state->top = key;
break; break;
} }
case OP_ADD: { // pop 2 values off the stack & try to add them together 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_FALSE: cosmoV_pushBoolean(state, false); break;
case OP_NIL: cosmoV_pushValue(state, cosmoV_newNil()); break; case OP_NIL: cosmoV_pushValue(state, cosmoV_newNil()); break;
case OP_RETURN: { case OP_RETURN: {
return 1; uint8_t res = READBYTE();
return res;
} }
default: default:
CERROR("unknown opcode!"); CERROR("unknown opcode!");