Added "case-of" control structure

This commit is contained in:
Twan van Laarhoven
2020-05-09 18:38:25 +02:00
parent 0a0dd72e36
commit 420c329bc4
5 changed files with 98 additions and 24 deletions
+50 -8
View File
@@ -381,11 +381,12 @@ enum Precedence
, PREC_NONE
};
/// Type of expressions. Broadly: LHS/exprssion/statement
/// Type of expressions. Broadly: LHS/expression/statement
/// Lower is more restrictive
enum ExprType
{ EXPR_VAR // A single variable, which could be converted to the left hand side of an assignment
, EXPR_STATEMENT // A 'statement', i.e. an expression that doesn't produce a result, and shouldn't be the last one in a block
, EXPR_OTHER
, EXPR_STATEMENT // A 'statement', i.e. an expression that doesn't produce a result, and shouldn't be the last one in a block
, EXPR_FAILED
};
@@ -508,10 +509,10 @@ ExprType parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
} else if (token == _("if")) {
// if AAA then BBB else CCC
parseOper(input, script, PREC_AND); // AAA
unsigned jmpElse = script.addInstruction(I_JUMP_IF_NOT); // jnz lbl_else
Addr jmpElse = script.addInstruction(I_JUMP_IF_NOT); // jnz lbl_else
expectToken(input, _("then")); // then
ExprType type1 = parseOper(input, script, PREC_SET); // BBB
unsigned jmpEnd = script.addInstruction(I_JUMP); // jump lbl_end
Addr jmpEnd = script.addInstruction(I_JUMP); // jump lbl_end
script.comeFrom(jmpElse); // lbl_else:
ExprType type2 = EXPR_STATEMENT;
if (input.peek() == _("else")) { // else
@@ -520,8 +521,49 @@ ExprType parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
} else {
script.addInstruction(I_PUSH_CONST, script_nil);
}
script.comeFrom(jmpEnd); // lbl_end:
script.comeFrom(jmpEnd); // lbl_end:
return type1 == EXPR_STATEMENT || type2 == EXPR_STATEMENT ? EXPR_STATEMENT : EXPR_OTHER;
} else if (token == _("case")) {
// case AAA of BBB: CCC[,] DDD: EEE ... else FFF
parseOper(input, script, PREC_AND);
expectToken(input, _("of"));
vector<Addr> jmpsEnd;
bool wasElse = false;
ExprType type = EXPR_OTHER;
Token t = input.peek();
while (t != TOK_RPAREN && t != TOK_EOF) {
Addr jmp;
if (input.peek() == _("else")) {
// else:
wasElse = true;
input.read();
expectToken(input, _(":"));
} else {
parseOper(input, script, PREC_AND);
expectToken(input, _(":"));
script.addInstruction(I_DUP, 1);
script.addInstruction(I_BINARY, I_EQ);
jmp = script.addInstruction(I_JUMP_IF_NOT);
}
script.addInstruction(I_POP);
ExprType type1 = parseOper(input, script, PREC_SET);
type = max(type, type1);
if (wasElse) {
break;
} else {
if (input.peek() == _(",")) input.read(); // optional comma
jmpsEnd.push_back(script.addInstruction(I_JUMP));
script.comeFrom(jmp);
}
t = input.peek();
}
if (!wasElse) {
type = max(type, EXPR_STATEMENT);
script.addInstruction(I_POP);
script.addInstruction(I_PUSH_CONST, script_nil);
}
for (Addr jmp : jmpsEnd) script.comeFrom(jmp);
return type;
} else if (token == _("for")) {
// the loop body should have a net stack effect of 0, but the entire expression of +1
// solution: add all results from the body, start with nil
@@ -565,7 +607,7 @@ ExprType parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
script.addInstruction(I_BINARY, I_ITERATOR_R); // iterator_range
}
script.addInstruction(I_PUSH_CONST, script_nil); // push nil
unsigned lblStart = script.addInstruction(with_key
Addr lblStart = script.addInstruction(with_key
? I_LOOP_WITH_KEY // lbl_start: loop_with_key lbl_end
: I_LOOP); // lbl_start: loop lbl_end
expectToken(input, _("do")); // do
@@ -713,7 +755,7 @@ ExprType parseOper(TokenIterator& input, Script& script, Precedence minPrec, Ins
// I_JUMP_SC_AND after # if top==false then goto after else pop
// YYY
// after:
unsigned jmpSC = script.addInstruction(I_JUMP_SC_AND);
Addr jmpSC = script.addInstruction(I_JUMP_SC_AND);
parseOper(input, script, PREC_CMP);
script.comeFrom(jmpSC);
}
@@ -725,7 +767,7 @@ ExprType parseOper(TokenIterator& input, Script& script, Precedence minPrec, Ins
parseOper(input, script, PREC_ADD, I_BINARY, I_OR_ELSE);
} else {
// short-circuiting or
unsigned jmpSC = script.addInstruction(I_JUMP_SC_OR);
Addr jmpSC = script.addInstruction(I_JUMP_SC_OR);
parseOper(input, script, PREC_CMP);
script.comeFrom(jmpSC);
}
+17 -13
View File
@@ -97,7 +97,7 @@ ScriptValueP Script::dependencies(Context& ctx, const Dependency& dep) const {
static const unsigned int INVALID_ADDRESS = 0x03FFFFFF;
unsigned int Script::addInstruction(InstructionType t) {
Addr Script::addInstruction(InstructionType t) {
assert( t == I_JUMP
|| t == I_JUMP_IF_NOT
|| t == I_JUMP_SC_AND
@@ -107,7 +107,7 @@ unsigned int Script::addInstruction(InstructionType t) {
|| t == I_POP);
Instruction i = {t, {INVALID_ADDRESS}};
instructions.push_back(i);
return getLabel() - 1;
return Addr{getLabel().addr - 1};
}
void Script::addInstruction(InstructionType t, unsigned int d) {
// Don't optimize ...I_PUSH_CONST x; I_MEMBER... to I_MEMBER_C
@@ -121,6 +121,9 @@ void Script::addInstruction(InstructionType t, unsigned int d) {
Instruction i = {t, {d}};
instructions.push_back(i);
}
void Script::addInstruction(InstructionType t, Addr d) {
addInstruction(t, d.addr);
}
void Script::addInstruction(InstructionType t, const ScriptValueP& c) {
constants.push_back(c);
Instruction i = {t, {(unsigned int)constants.size() - 1}};
@@ -132,19 +135,19 @@ void Script::addInstruction(InstructionType t, const String& s) {
instructions.push_back(i);
}
void Script::comeFrom(unsigned int pos) {
assert( instructions.at(pos).instr == I_JUMP
|| instructions.at(pos).instr == I_JUMP_IF_NOT
|| instructions.at(pos).instr == I_JUMP_SC_AND
|| instructions.at(pos).instr == I_JUMP_SC_OR
|| instructions.at(pos).instr == I_LOOP
|| instructions.at(pos).instr == I_LOOP_WITH_KEY);
assert( instructions.at(pos).data == INVALID_ADDRESS );
instructions.at(pos).data = (unsigned int)instructions.size();
void Script::comeFrom(Addr pos) {
assert( instructions.at(pos.addr).instr == I_JUMP
|| instructions.at(pos.addr).instr == I_JUMP_IF_NOT
|| instructions.at(pos.addr).instr == I_JUMP_SC_AND
|| instructions.at(pos.addr).instr == I_JUMP_SC_OR
|| instructions.at(pos.addr).instr == I_LOOP
|| instructions.at(pos.addr).instr == I_LOOP_WITH_KEY);
assert( instructions.at(pos.addr).data == INVALID_ADDRESS );
instructions.at(pos.addr).data = (unsigned int)instructions.size();
}
unsigned int Script::getLabel() const {
return (unsigned int)instructions.size();
Addr Script::getLabel() const {
return Addr{ (unsigned int)instructions.size() };
}
#ifdef _DEBUG // debugging
@@ -223,6 +226,7 @@ String Script::dumpInstr(unsigned int pos, Instruction i) const {
switch (i.instr) {
case I_PUSH_CONST: case I_MEMBER_C: // const
ret += _("\t") + constants[i.data]->typeName();
ret += _("\t") + constants[i.data]->toCode();
break;
case I_JUMP: case I_JUMP_IF_NOT: case I_JUMP_SC_AND: case I_JUMP_SC_OR:
case I_LOOP: case I_LOOP_WITH_KEY:
+10 -3
View File
@@ -110,6 +110,11 @@ struct Instruction {
};
};
/// An address/position in a script
struct Addr {
unsigned int addr;
};
// ----------------------------------------------------------------------------- : Variables
// for faster lookup from code
@@ -170,9 +175,11 @@ public:
ScriptValueP dependencies(Context& ctx, const Dependency&) const override;
/// Add a jump instruction, later comeFrom should be called on the returned value
unsigned int addInstruction(InstructionType t);
Addr addInstruction(InstructionType t);
/// Add an instruction with integer data
void addInstruction(InstructionType t, unsigned int d);
/// Add an instruction with integer data
void addInstruction(InstructionType t, Addr d);
/// Add an instruction with constant data
void addInstruction(InstructionType t, const ScriptValueP& c);
/// Add an instruction with string data
@@ -182,9 +189,9 @@ public:
/** The instruction at pos must be a jumping instruction, it is changed so the current position
* 'comes from' pos (in addition to other control flow).
*/
void comeFrom(unsigned int pos);
void comeFrom(Addr pos);
/// Get the current instruction position
unsigned int getLabel() const;
Addr getLabel() const;
/// Get access to the vector of instructions
inline vector<Instruction>& getInstructions() { return instructions; }