Commit 7ce6b2c6 authored by twanvl's avatar twanvl

warn about functions ending with a statement instead of a return value

parent 7104e9a7
...@@ -373,13 +373,23 @@ enum Precedence ...@@ -373,13 +373,23 @@ enum Precedence
, PREC_NONE , PREC_NONE
}; };
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 shouldn't be the last one
, EXPR_OTHER
, EXPR_FAILED
};
/// Parse an expression /// Parse an expression
/** @param input Read tokens from the input /** @param input Read tokens from the input
* @param scrip Add resulting instructions to the script * @param scrip Add resulting instructions to the script
* @param min_prec Minimum precedence level for operators * @param min_prec Minimum precedence level for operators
*
* @returns the type of expression
*
* NOTE: The net stack effect of an expression should be +1 * NOTE: The net stack effect of an expression should be +1
*/ */
void parseExpr(TokenIterator& input, Script& script, Precedence min_prec); ExprType parseExpr(TokenIterator& input, Script& script, Precedence min_prec);
/// Parse an expression, possibly with operators applied. Optionally adds an instruction at the end. /// Parse an expression, possibly with operators applied. Optionally adds an instruction at the end.
/** @param input Read tokens from the input /** @param input Read tokens from the input
...@@ -389,7 +399,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence min_prec); ...@@ -389,7 +399,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence min_prec);
* @param close_with_data Data for the instruction at the end * @param close_with_data Data for the instruction at the end
* NOTE: The net stack effect of an expression should be +1 * NOTE: The net stack effect of an expression should be +1
*/ */
void parseOper(TokenIterator& input, Script& script, Precedence min_prec, InstructionType close_with = I_NOP, int close_with_data = 0); ExprType parseOper(TokenIterator& input, Script& script, Precedence min_prec, InstructionType close_with = I_NOP, int close_with_data = 0);
/// Parse call arguments, "(...)" /// Parse call arguments, "(...)"
void parseCallArguments(TokenIterator& input, Script& script, vector<Variable>& arguments); void parseCallArguments(TokenIterator& input, Script& script, vector<Variable>& arguments);
...@@ -434,9 +444,7 @@ bool expectToken(TokenIterator& input, const Char* expect, const Token* opening ...@@ -434,9 +444,7 @@ bool expectToken(TokenIterator& input, const Char* expect, const Token* opening
} }
} }
void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ExprType parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
// usually loop only once, unless we encounter newlines
while (true) {
Token token = input.read(); Token token = input.read();
if (token == _("(")) { if (token == _("(")) {
// Parentheses = grouping for precedence of expressions // Parentheses = grouping for precedence of expressions
...@@ -445,7 +453,10 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -445,7 +453,10 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
} else if (token == _("{")) { } else if (token == _("{")) {
// {} = function block. Parse a new Script // {} = function block. Parse a new Script
intrusive_ptr<Script> subScript(new Script); intrusive_ptr<Script> subScript(new Script);
parseOper(input, *subScript, PREC_ALL); ExprType t = parseOper(input, *subScript, PREC_ALL);
if (t == EXPR_STATEMENT) {
input.add_error(_("Warning: last statement of a function should be an expression, i.e. it should return a result in all cases."));
}
expectToken(input, _("}"), &token); expectToken(input, _("}"), &token);
script.addInstruction(I_PUSH_CONST, subScript); script.addInstruction(I_PUSH_CONST, subScript);
} else if (token == _("[")) { } else if (token == _("[")) {
...@@ -492,13 +503,15 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -492,13 +503,15 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
parseOper(input, script, PREC_SET); // BBB parseOper(input, script, PREC_SET); // BBB
unsigned jmpEnd = script.addInstruction(I_JUMP); // jump lbl_end unsigned jmpEnd = script.addInstruction(I_JUMP); // jump lbl_end
script.comeFrom(jmpElse); // lbl_else: script.comeFrom(jmpElse); // lbl_else:
if (input.peek() == _("else")) { //else bool has_else = input.peek() == _("else"); //else
if (has_else) {
input.read(); input.read();
parseOper(input, script, PREC_SET); // CCC parseOper(input, script, PREC_SET); // CCC
} else { } else {
script.addInstruction(I_PUSH_CONST, script_nil); script.addInstruction(I_PUSH_CONST, script_nil);
} }
script.comeFrom(jmpEnd); // lbl_end: script.comeFrom(jmpEnd); // lbl_end:
return has_else ? EXPR_OTHER : EXPR_STATEMENT;
} else if (token == _("for")) { } else if (token == _("for")) {
// the loop body should have a net stack effect of 0, but the entire expression of +1 // 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 // solution: add all results from the body, start with nil
...@@ -616,10 +629,12 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -616,10 +629,12 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
script.addInstruction(I_NOP, SCRIPT_VAR_condition); // (condition:) script.addInstruction(I_NOP, SCRIPT_VAR_condition); // (condition:)
script.addInstruction(I_NOP, SCRIPT_VAR_input); // (input:) script.addInstruction(I_NOP, SCRIPT_VAR_input); // (input:)
} }
return EXPR_STATEMENT;
} else { } else {
// variable // variable
Variable var = string_to_variable(token.value); Variable var = string_to_variable(token.value);
script.addInstruction(I_GET_VAR, var); script.addInstruction(I_GET_VAR, var);
return EXPR_VAR;
} }
} else if (token == TOK_INT) { } else if (token == TOK_INT) {
long l = 0; long l = 0;
...@@ -634,17 +649,15 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -634,17 +649,15 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
} else if (token == TOK_STRING) { } else if (token == TOK_STRING) {
script.addInstruction(I_PUSH_CONST, to_script(token.value)); script.addInstruction(I_PUSH_CONST, to_script(token.value));
} else { } else {
script.addInstruction(I_PUSH_CONST, script_nil);
input.expected(_("expression")); input.expected(_("expression"));
return; return EXPR_FAILED;
}
break;
} }
return EXPR_OTHER;
} }
void parseOper(TokenIterator& input, Script& script, Precedence minPrec, InstructionType closeWith, int closeWithData) { ExprType parseOper(TokenIterator& input, Script& script, Precedence minPrec, InstructionType closeWith, int closeWithData) {
size_t added = script.getInstructions().size(); // number of instructions added ExprType type = parseExpr(input, script, minPrec); // first argument
parseExpr(input, script, minPrec); // first argument
added = script.getInstructions().size() - added;
// read any operators after an expression // read any operators after an expression
// EBNF: expr = expr | expr oper expr // EBNF: expr = expr | expr oper expr
// without left recursion: expr = expr (oper expr)* // without left recursion: expr = expr (oper expr)*
...@@ -656,6 +669,7 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc ...@@ -656,6 +669,7 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
input.putBack(); input.putBack();
break; break;
} }
if (minPrec <= PREC_SEQ && token==_(";")) { if (minPrec <= PREC_SEQ && token==_(";")) {
Token next = input.peek(1); Token next = input.peek(1);
if (next == TOK_RPAREN || next == TOK_EOF) { if (next == TOK_RPAREN || next == TOK_EOF) {
...@@ -663,12 +677,12 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc ...@@ -663,12 +677,12 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
break; break;
} }
script.addInstruction(I_POP); // discard result of first expression script.addInstruction(I_POP); // discard result of first expression
parseOper(input, script, PREC_SET); type = parseOper(input, script, PREC_SET);
} else if (minPrec <= PREC_SET && token==_(":=")) { } else if (minPrec <= PREC_SET && token==_(":=")) {
// We made a mistake, the part before the := should be a variable name, // We made a mistake, the part before the := should be a variable name,
// not an expression. Remove that instruction. // not an expression. Remove that instruction.
Instruction& instr = script.getInstructions().back(); Instruction& instr = script.getInstructions().back();
if (added != 1 || instr.instr != I_GET_VAR) { if (type != EXPR_VAR || instr.instr != I_GET_VAR) {
input.add_error(_("Can only assign to variables")); input.add_error(_("Can only assign to variables"));
} }
script.getInstructions().pop_back(); script.getInstructions().pop_back();
...@@ -790,16 +804,19 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc ...@@ -790,16 +804,19 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
// only if we don't match another token! // only if we don't match another token!
input.putBack(); input.putBack();
script.addInstruction(I_POP); script.addInstruction(I_POP);
parseOper(input, script, PREC_SET); type = parseOper(input, script, PREC_SET);
} else { } else {
input.putBack(); input.putBack();
break; break;
} }
if (type == EXPR_VAR) type = EXPR_OTHER; // var only applies to single variables, not to things with operators
} }
// add closing instruction // add closing instruction
if (closeWith != I_NOP) { if (closeWith != I_NOP) {
script.addInstruction(closeWith, closeWithData); script.addInstruction(closeWith, closeWithData);
} }
return type;
} }
void parseCallArguments(TokenIterator& input, Script& script, vector<Variable>& arguments) { void parseCallArguments(TokenIterator& input, Script& script, vector<Variable>& arguments) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment