mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-06 04:37:47 +08:00
Merge pull request #195 from ThakeeNathees/expr-name
exprName re-defined
This commit is contained in:
commit
9a8f3365ad
@ -202,8 +202,6 @@ static _Keyword _keywords[] = {
|
|||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
// Precedence parsing references:
|
// 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/
|
// http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -1311,7 +1309,7 @@ static int emitByte(Compiler* compiler, int byte);
|
|||||||
static int emitShort(Compiler* compiler, int arg);
|
static int emitShort(Compiler* compiler, int arg);
|
||||||
|
|
||||||
static void emitLoopJump(Compiler* compiler);
|
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 emitFunctionEnd(Compiler* compiler);
|
||||||
|
|
||||||
static void patchJump(Compiler* compiler, int addr_index);
|
static void patchJump(Compiler* compiler, int addr_index);
|
||||||
@ -1437,34 +1435,62 @@ static GrammarRule* getRule(TokenType type) {
|
|||||||
return &(rules[(int)type]);
|
return &(rules[(int)type]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit variable store.
|
// FIXME:
|
||||||
static void emitStoreVariable(Compiler* compiler, int index, bool global) {
|
// This function is used by the import system, remove this function (and move
|
||||||
if (global) {
|
// it to emitStoreName()) after import system refactored.
|
||||||
emitOpcode(compiler, OP_STORE_GLOBAL);
|
//
|
||||||
emitByte(compiler, index);
|
// 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 {
|
// Emit opcode to push the named value at the [index] in it's array.
|
||||||
if (index < 9) { //< 0..8 locals have single opcode.
|
static void emitPushName(Compiler* compiler, NameDefnType type, int index) {
|
||||||
emitOpcode(compiler, (Opcode)(OP_STORE_LOCAL_0 + index));
|
switch (type) {
|
||||||
} else {
|
case NAME_NOT_DEFINED:
|
||||||
emitOpcode(compiler, OP_STORE_LOCAL_N);
|
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);
|
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) {
|
// Emit opcode to store the stack top value to the named value at the [index]
|
||||||
if (global) {
|
// in it's array.
|
||||||
emitOpcode(compiler, OP_PUSH_GLOBAL);
|
static void emitStoreName(Compiler* compiler, NameDefnType type, int index) {
|
||||||
emitByte(compiler, index);
|
switch (type) {
|
||||||
|
case NAME_NOT_DEFINED:
|
||||||
|
case NAME_BUILTIN:
|
||||||
|
UNREACHABLE();
|
||||||
|
|
||||||
} else {
|
case NAME_LOCAL_VAR:
|
||||||
if (index < 9) { //< 0..8 locals have single opcode.
|
ASSERT(index >= 0, OOPS);
|
||||||
emitOpcode(compiler, (Opcode)(OP_PUSH_LOCAL_0 + index));
|
if (index < 9) { //< 0..8 locals have single opcode.
|
||||||
} else {
|
emitOpcode(compiler, (Opcode)(OP_STORE_LOCAL_0 + index));
|
||||||
emitOpcode(compiler, OP_PUSH_LOCAL_N);
|
} else {
|
||||||
emitByte(compiler, index);
|
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;
|
int line = compiler->parser.previous.line;
|
||||||
NameSearchResult result = compilerSearchName(compiler, start, length);
|
NameSearchResult result = compilerSearchName(compiler, start, length);
|
||||||
|
|
||||||
if (result.type == NAME_NOT_DEFINED) {
|
if (compiler->l_value && matchAssignment(compiler)) {
|
||||||
if (compiler->l_value && match(compiler, TK_EQ)) {
|
TokenType assignment = compiler->parser.previous.type;
|
||||||
skipNewLines(compiler);
|
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.
|
// Compile the assigned value.
|
||||||
compileExpression(compiler);
|
compileExpression(compiler);
|
||||||
|
|
||||||
// Store the value to the variable.
|
} else { // name += / -= / *= ... = (expr);
|
||||||
if (compiler->scope_depth == DEPTH_GLOBAL) {
|
if (result.type == NAME_NOT_DEFINED) {
|
||||||
emitStoreVariable(compiler, index, true);
|
parseError(compiler, "Name '%.*s' is not defined.", length, start);
|
||||||
|
|
||||||
} 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 {
|
|
||||||
|
|
||||||
// The name could be a global value which hasn't been defined at this
|
// Push the named value.
|
||||||
// point. We add an implicit forward declaration and once this expression
|
emitPushName(compiler, name_type, index);
|
||||||
// executed the value could be initialized only if the expression is at
|
|
||||||
// a local depth.
|
// 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) {
|
if (compiler->scope_depth == DEPTH_GLOBAL) {
|
||||||
parseError(compiler, "Name '%.*s' is not defined.", length, start);
|
parseError(compiler, "Name '%.*s' is not defined.", length, start);
|
||||||
} else {
|
} else {
|
||||||
@ -1585,45 +1654,11 @@ static void exprName(Compiler* compiler) {
|
|||||||
int index = emitByte(compiler, 0xff);
|
int index = emitByte(compiler, 0xff);
|
||||||
compilerAddForward(compiler, index, _FN, start, length, line);
|
compilerAddForward(compiler, index, _FN, start, length, line);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
emitPushName(compiler, result.type, result.index);
|
||||||
} 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.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compiling (expr a) or (expr b)
|
// Compiling (expr a) or (expr b)
|
||||||
@ -1784,14 +1819,14 @@ static void exprAttrib(Compiler* compiler) {
|
|||||||
name, length);
|
name, length);
|
||||||
|
|
||||||
if (compiler->l_value && matchAssignment(compiler)) {
|
if (compiler->l_value && matchAssignment(compiler)) {
|
||||||
|
TokenType assignment = compiler->parser.previous.type;
|
||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
|
|
||||||
TokenType assignment = compiler->parser.previous.type;
|
|
||||||
if (assignment != TK_EQ) {
|
if (assignment != TK_EQ) {
|
||||||
emitOpcode(compiler, OP_GET_ATTRIB_KEEP);
|
emitOpcode(compiler, OP_GET_ATTRIB_KEEP);
|
||||||
emitShort(compiler, index);
|
emitShort(compiler, index);
|
||||||
compileExpression(compiler);
|
compileExpression(compiler);
|
||||||
emitAssignment(compiler, assignment);
|
emitAssignedOp(compiler, assignment);
|
||||||
} else {
|
} else {
|
||||||
compileExpression(compiler);
|
compileExpression(compiler);
|
||||||
}
|
}
|
||||||
@ -1810,13 +1845,13 @@ static void exprSubscript(Compiler* compiler) {
|
|||||||
consume(compiler, TK_RBRACKET, "Expected ']' after subscription ends.");
|
consume(compiler, TK_RBRACKET, "Expected ']' after subscription ends.");
|
||||||
|
|
||||||
if (compiler->l_value && matchAssignment(compiler)) {
|
if (compiler->l_value && matchAssignment(compiler)) {
|
||||||
|
TokenType assignment = compiler->parser.previous.type;
|
||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
|
|
||||||
TokenType assignment = compiler->parser.previous.type;
|
|
||||||
if (assignment != TK_EQ) {
|
if (assignment != TK_EQ) {
|
||||||
emitOpcode(compiler, OP_GET_SUBSCRIPT_KEEP);
|
emitOpcode(compiler, OP_GET_SUBSCRIPT_KEEP);
|
||||||
compileExpression(compiler);
|
compileExpression(compiler);
|
||||||
emitAssignment(compiler, assignment);
|
emitAssignedOp(compiler, assignment);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
compileExpression(compiler);
|
compileExpression(compiler);
|
||||||
@ -2054,7 +2089,7 @@ static void emitLoopJump(Compiler* compiler) {
|
|||||||
emitShort(compiler, offset);
|
emitShort(compiler, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void emitAssignment(Compiler* compiler, TokenType assignment) {
|
static void emitAssignedOp(Compiler* compiler, TokenType assignment) {
|
||||||
switch (assignment) {
|
switch (assignment) {
|
||||||
case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break;
|
case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break;
|
||||||
case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break;
|
case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break;
|
||||||
@ -2518,7 +2553,7 @@ static void compilerImportSingleEntry(Compiler* compiler,
|
|||||||
emitShort(compiler, name_index);
|
emitShort(compiler, name_index);
|
||||||
|
|
||||||
int index = compilerImportName(compiler, line, name, length);
|
int index = compilerImportName(compiler, line, name, length);
|
||||||
if (index != -1) emitStoreVariable(compiler, index, true);
|
if (index != -1) emitStoreGlobal(compiler, index);
|
||||||
emitOpcode(compiler, OP_POP);
|
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
|
// Get the variable to bind the imported symbol, if we already have a
|
||||||
// variable with that name override it, otherwise use a new variable.
|
// variable with that name override it, otherwise use a new variable.
|
||||||
int var_index = compilerImportName(compiler, line, name, length);
|
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);
|
emitOpcode(compiler, OP_POP);
|
||||||
|
|
||||||
} while (match(compiler, TK_COMMA) && (skipNewLines(compiler), true));
|
} while (match(compiler, TK_COMMA) && (skipNewLines(compiler), true));
|
||||||
@ -2649,7 +2684,7 @@ static void compileRegularImport(Compiler* compiler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (var_index != -1) {
|
if (var_index != -1) {
|
||||||
emitStoreVariable(compiler, var_index, true);
|
emitStoreGlobal(compiler, var_index);
|
||||||
emitOpcode(compiler, OP_POP);
|
emitOpcode(compiler, OP_POP);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user