diff --git a/src/pk_compiler.c b/src/pk_compiler.c index 90269ef..b2aa1a7 100644 --- a/src/pk_compiler.c +++ b/src/pk_compiler.c @@ -202,8 +202,6 @@ static _Keyword _keywords[] = { /*****************************************************************************/ // Precedence parsing references: -// https://en.wikipedia.org/wiki/Shunting-yard_algorithm -// http://mathcenter.oxford.emory.edu/site/cs171/shuntingYardAlgorithm/ // http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/ typedef enum { @@ -1311,7 +1309,7 @@ static int emitByte(Compiler* compiler, int byte); static int emitShort(Compiler* compiler, int arg); static void emitLoopJump(Compiler* compiler); -static void emitAssignment(Compiler* compiler, TokenType assignment); +static void emitAssignedOp(Compiler* compiler, TokenType assignment); static void emitFunctionEnd(Compiler* compiler); static void patchJump(Compiler* compiler, int addr_index); @@ -1437,34 +1435,62 @@ static GrammarRule* getRule(TokenType type) { return &(rules[(int)type]); } -// Emit variable store. -static void emitStoreVariable(Compiler* compiler, int index, bool global) { - if (global) { - emitOpcode(compiler, OP_STORE_GLOBAL); - emitByte(compiler, index); +// FIXME: +// This function is used by the import system, remove this function (and move +// it to emitStoreName()) after import system refactored. +// +// Store the value at the stack top to the global at the [index]. +static void emitStoreGlobal(Compiler* compiler, int index) { + emitOpcode(compiler, OP_STORE_GLOBAL); + emitByte(compiler, index); +} - } else { - if (index < 9) { //< 0..8 locals have single opcode. - emitOpcode(compiler, (Opcode)(OP_STORE_LOCAL_0 + index)); - } else { - emitOpcode(compiler, OP_STORE_LOCAL_N); +// Emit opcode to push the named value at the [index] in it's array. +static void emitPushName(Compiler* compiler, NameDefnType type, int index) { + switch (type) { + case NAME_NOT_DEFINED: + UNREACHABLE(); + + case NAME_LOCAL_VAR: + ASSERT(index >= 0, OOPS); + if (index < 9) { //< 0..8 locals have single opcode. + emitOpcode(compiler, (Opcode)(OP_PUSH_LOCAL_0 + index)); + } else { + emitOpcode(compiler, OP_PUSH_LOCAL_N); + emitByte(compiler, index); + } + return; + case NAME_GLOBAL_VAR: + emitOpcode(compiler, OP_PUSH_GLOBAL); emitByte(compiler, index); - } + return; + case NAME_BUILTIN: + emitOpcode(compiler, OP_PUSH_BUILTIN_FN); + emitByte(compiler, index); + return; } } -static void emitPushVariable(Compiler* compiler, int index, bool global) { - if (global) { - emitOpcode(compiler, OP_PUSH_GLOBAL); - emitByte(compiler, index); +// Emit opcode to store the stack top value to the named value at the [index] +// in it's array. +static void emitStoreName(Compiler* compiler, NameDefnType type, int index) { + switch (type) { + case NAME_NOT_DEFINED: + case NAME_BUILTIN: + UNREACHABLE(); - } else { - if (index < 9) { //< 0..8 locals have single opcode. - emitOpcode(compiler, (Opcode)(OP_PUSH_LOCAL_0 + index)); - } else { - emitOpcode(compiler, OP_PUSH_LOCAL_N); - emitByte(compiler, index); - } + case NAME_LOCAL_VAR: + ASSERT(index >= 0, OOPS); + if (index < 9) { //< 0..8 locals have single opcode. + emitOpcode(compiler, (Opcode)(OP_STORE_LOCAL_0 + index)); + } else { + emitOpcode(compiler, OP_STORE_LOCAL_N); + emitByte(compiler, index); + } + return; + case NAME_GLOBAL_VAR: + emitStoreGlobal(compiler, index); + return; } } @@ -1545,39 +1571,82 @@ static void exprName(Compiler* compiler) { int line = compiler->parser.previous.line; NameSearchResult result = compilerSearchName(compiler, start, length); - if (result.type == NAME_NOT_DEFINED) { - if (compiler->l_value && match(compiler, TK_EQ)) { - skipNewLines(compiler); + if (compiler->l_value && matchAssignment(compiler)) { + TokenType assignment = compiler->parser.previous.type; + skipNewLines(compiler); - int index = compilerAddVariable(compiler, start, length, line); + // Type of the name that's being assigned. Could only be local, global + // or an upvalue. + NameDefnType name_type = result.type; + int index = result.index; // Index of the name in it's array. + + // Will be set to true if the name is a new local. + bool new_local = false; + + if (assignment == TK_EQ) { // name = (expr); + + // Assignment to builtin functions will override the name and it'll + // become a local or global variable. Note that if the names is a global + // which hasent defined yet we treat that as a local (no global keyword + // like python does) and it's recommented to define all the globals + // before entering a local scope. + + if (result.type == NAME_NOT_DEFINED || result.type == NAME_BUILTIN) { + name_type = (compiler->scope_depth == DEPTH_GLOBAL) + ? NAME_GLOBAL_VAR + : NAME_LOCAL_VAR; + index = compilerAddVariable(compiler, start, length, line); + + // We cannot set `compiler->new_local = true;` here since there is an + // expression after the assignment pending. We'll update it once the + // expression is compiled. + if (name_type == NAME_LOCAL_VAR) { + new_local = true; + } + } // Compile the assigned value. compileExpression(compiler); - // Store the value to the variable. - if (compiler->scope_depth == DEPTH_GLOBAL) { - emitStoreVariable(compiler, index, true); - - } else { - // This will prevent the assignment from being popped out from the - // stack since the assigned value itself is the local and not a temp. - compiler->new_local = true; - - // Ensure the local variable's index is equals to the stack top index. - // If the compiler has errors, we cannot and don't have to assert. - ASSERT(compiler->parser.has_errors || - (compiler->func->stack_size - 1) == index, OOPS); - - // We don't need to call emitStoreVariable (which emit STORE_LOCAL) - // because the local is already at it's location in the stack, we just - // don't pop it. + } else { // name += / -= / *= ... = (expr); + if (result.type == NAME_NOT_DEFINED) { + parseError(compiler, "Name '%.*s' is not defined.", length, start); } - } else { - // The name could be a global value which hasn't been defined at this - // point. We add an implicit forward declaration and once this expression - // executed the value could be initialized only if the expression is at - // a local depth. + // Push the named value. + emitPushName(compiler, name_type, index); + + // Compile the RHS of the assigned operation. + compileExpression(compiler); + + // Do the arithmatic operation of the assignment. + emitAssignedOp(compiler, assignment); + } + + // If it's a new local we don't have to store it, it's already at it's + // stack slot. + if (new_local) { + // This will prevent the assignment from being popped out from the + // stack since the assigned value itself is the local and not a temp. + compiler->new_local = true; + + // Ensure the local variable's index is equals to the stack top index. + // If the compiler has errors, we cannot and don't have to assert. + ASSERT(compiler->parser.has_errors || + (compiler->func->stack_size - 1) == index, OOPS); + } else { + // The assigned value or the result of the operator will be at the top of + // the stack by now. Store it. + emitStoreName(compiler, name_type, index); + } + + } else { // Just the name and no assignment followed by. + + // The name could be a global value which hasn't been defined at this + // point. We add an implicit forward declaration and once this expression + // executed the value could be initialized only if the expression is at + // a local depth. + if (result.type == NAME_NOT_DEFINED) { if (compiler->scope_depth == DEPTH_GLOBAL) { parseError(compiler, "Name '%.*s' is not defined.", length, start); } else { @@ -1585,45 +1654,11 @@ static void exprName(Compiler* compiler) { int index = emitByte(compiler, 0xff); compilerAddForward(compiler, index, _FN, start, length, line); } - } - - } else { - - switch (result.type) { - case NAME_LOCAL_VAR: - case NAME_GLOBAL_VAR: { - const bool is_global = result.type == NAME_GLOBAL_VAR; - - if (compiler->l_value && matchAssignment(compiler)) { - skipNewLines(compiler); - - TokenType assignment = compiler->parser.previous.type; - if (assignment != TK_EQ) { - emitPushVariable(compiler, result.index, is_global); - compileExpression(compiler); - emitAssignment(compiler, assignment); - - } else { - compileExpression(compiler); - } - - emitStoreVariable(compiler, result.index, is_global); - - } else { - emitPushVariable(compiler, result.index, is_global); - } - break; - } - - case NAME_BUILTIN: - emitOpcode(compiler, OP_PUSH_BUILTIN_FN); - emitByte(compiler, result.index); - break; - - case NAME_NOT_DEFINED: - UNREACHABLE(); // Case already handled. + } else { + emitPushName(compiler, result.type, result.index); } } + } // Compiling (expr a) or (expr b) @@ -1784,14 +1819,14 @@ static void exprAttrib(Compiler* compiler) { name, length); if (compiler->l_value && matchAssignment(compiler)) { + TokenType assignment = compiler->parser.previous.type; skipNewLines(compiler); - TokenType assignment = compiler->parser.previous.type; if (assignment != TK_EQ) { emitOpcode(compiler, OP_GET_ATTRIB_KEEP); emitShort(compiler, index); compileExpression(compiler); - emitAssignment(compiler, assignment); + emitAssignedOp(compiler, assignment); } else { compileExpression(compiler); } @@ -1810,13 +1845,13 @@ static void exprSubscript(Compiler* compiler) { consume(compiler, TK_RBRACKET, "Expected ']' after subscription ends."); if (compiler->l_value && matchAssignment(compiler)) { + TokenType assignment = compiler->parser.previous.type; skipNewLines(compiler); - TokenType assignment = compiler->parser.previous.type; if (assignment != TK_EQ) { emitOpcode(compiler, OP_GET_SUBSCRIPT_KEEP); compileExpression(compiler); - emitAssignment(compiler, assignment); + emitAssignedOp(compiler, assignment); } else { compileExpression(compiler); @@ -2054,7 +2089,7 @@ static void emitLoopJump(Compiler* compiler) { emitShort(compiler, offset); } -static void emitAssignment(Compiler* compiler, TokenType assignment) { +static void emitAssignedOp(Compiler* compiler, TokenType assignment) { switch (assignment) { case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break; case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break; @@ -2518,7 +2553,7 @@ static void compilerImportSingleEntry(Compiler* compiler, emitShort(compiler, name_index); int index = compilerImportName(compiler, line, name, length); - if (index != -1) emitStoreVariable(compiler, index, true); + if (index != -1) emitStoreGlobal(compiler, index); emitOpcode(compiler, OP_POP); } @@ -2588,7 +2623,7 @@ static void compileFromImport(Compiler* compiler) { // Get the variable to bind the imported symbol, if we already have a // variable with that name override it, otherwise use a new variable. int var_index = compilerImportName(compiler, line, name, length); - if (var_index != -1) emitStoreVariable(compiler, var_index, true); + if (var_index != -1) emitStoreGlobal(compiler, var_index); emitOpcode(compiler, OP_POP); } while (match(compiler, TK_COMMA) && (skipNewLines(compiler), true)); @@ -2649,7 +2684,7 @@ static void compileRegularImport(Compiler* compiler) { } if (var_index != -1) { - emitStoreVariable(compiler, var_index, true); + emitStoreGlobal(compiler, var_index); emitOpcode(compiler, OP_POP); } else {