Commit fcd75a64 authored by twanvl's avatar twanvl

Added 'assert' pseudo function so I can run some tests of the script code

parent 7b4024a9
Function: assert
--Usage--
> assert(condition)
Check that the condition is @true@. Shows a warning message if it is not.
Note: @assert@ is a special built-in keyword, so that the error message can include the condition.
--Parameters--
! Parameter Type Description
| @input@ [[type:boolean]] Condition to check.
--Examples--
> assert(1 + 1 == 2) == nil # nothing happens
> assert(1 * 1 == 2) == nil # An error message is shown
...@@ -87,3 +87,4 @@ These functions are built into the program, other [[type:function]]s can be defi ...@@ -87,3 +87,4 @@ These functions are built into the program, other [[type:function]]s can be defi
! Other functions <<< ! Other functions <<<
| [[fun:trace]] Output a message for debugging purposes. | [[fun:trace]] Output a message for debugging purposes.
| [[fun:assert]] Check a condition for debugging purposes.
...@@ -201,7 +201,14 @@ void Context::setVariable(const String& name, const ScriptValueP& value) { ...@@ -201,7 +201,14 @@ void Context::setVariable(const String& name, const ScriptValueP& value) {
setVariable(string_to_variable(name), value); setVariable(string_to_variable(name), value);
} }
#ifdef _DEBUG
extern vector<String> variable_names;
#endif
void Context::setVariable(Variable name, const ScriptValueP& value) { void Context::setVariable(Variable name, const ScriptValueP& value) {
#ifdef _DEBUG
assert((size_t)name < variable_names.size());
#endif
VariableValue& var = variables[name]; VariableValue& var = variables[name];
if (var.level < level) { if (var.level < level) {
// keep shadow copy // keep shadow copy
......
...@@ -31,6 +31,12 @@ SCRIPT_FUNCTION(trace) { ...@@ -31,6 +31,12 @@ SCRIPT_FUNCTION(trace) {
SCRIPT_RETURN(input); SCRIPT_RETURN(input);
} }
SCRIPT_FUNCTION(warning) {
SCRIPT_PARAM_C(String, input);
handle_warning(input, true);
return script_nil;
}
// ----------------------------------------------------------------------------- : String stuff // ----------------------------------------------------------------------------- : String stuff
// convert a string to upper case // convert a string to upper case
......
...@@ -22,6 +22,8 @@ DECLARE_TYPEOF_COLLECTION(Variable); ...@@ -22,6 +22,8 @@ DECLARE_TYPEOF_COLLECTION(Variable);
String read_utf8_line(wxInputStream& input, bool eat_bom = true, bool until_eof = false); String read_utf8_line(wxInputStream& input, bool eat_bom = true, bool until_eof = false);
extern ScriptValueP script_warning;
// ----------------------------------------------------------------------------- : Tokenizing : class // ----------------------------------------------------------------------------- : Tokenizing : class
enum TokenType enum TokenType
...@@ -75,6 +77,11 @@ class TokenIterator { ...@@ -75,6 +77,11 @@ class TokenIterator {
*/ */
void putBack(); void putBack();
/// Get a section of source code
String getSourceCode(size_t start, size_t end);
/// Get the current line number
int getLineNumber();
private: private:
String input; String input;
size_t pos; size_t pos;
...@@ -318,6 +325,15 @@ void TokenIterator::expected(const String& expected, const Token* opening) { ...@@ -318,6 +325,15 @@ void TokenIterator::expected(const String& expected, const Token* opening) {
} }
String TokenIterator::getSourceCode(size_t start, size_t end) {
start = min(start, input.size());
end = min(end, input.size());
return input.substr(start, end-start);
}
int TokenIterator::getLineNumber() {
return line_number(peek(0).pos, input);
}
// ----------------------------------------------------------------------------- : Parsing // ----------------------------------------------------------------------------- : Parsing
...@@ -450,24 +466,20 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -450,24 +466,20 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
script.addInstruction(I_PUSH_CONST, script_nil); // universal constant : nil script.addInstruction(I_PUSH_CONST, script_nil); // universal constant : nil
} else if (token == _("if")) { } else if (token == _("if")) {
// if AAA then BBB else CCC // if AAA then BBB else CCC
unsigned int jmpElse, jmpEnd; parseOper(input, script, PREC_AND); // AAA
parseOper(input, script, PREC_AND); // AAA unsigned jmpElse = script.addInstruction(I_JUMP_IF_NOT); // jnz lbl_else
jmpElse = script.getLabel(); // jmp_else: expectToken(input, _("then")); // then
script.addInstruction(I_JUMP_IF_NOT, INVALID_ADDRESS); // jnz lbl_else parseOper(input, script, PREC_SET); // BBB
expectToken(input, _("then")); // then unsigned jmpEnd = script.addInstruction(I_JUMP); // jump lbl_end
parseOper(input, script, PREC_SET); // BBB script.comeFrom(jmpElse); // lbl_else:
jmpEnd = script.getLabel(); // jmp_end: if (input.peek() == _("else")) { //else
script.addInstruction(I_JUMP, INVALID_ADDRESS); // jump lbl_end
script.comeFrom(jmpElse); // lbl_else:
if (input.peek() == _("else")) { // 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:
} else if (token == _("for")) { } else if (token == _("for")) {
unsigned int lblStart;
// 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
if (input.peek() == _("each")) { if (input.peek() == _("each")) {
...@@ -481,8 +493,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -481,8 +493,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
parseOper(input, script, PREC_AND); // BBB parseOper(input, script, PREC_AND); // BBB
script.addInstruction(I_UNARY, I_ITERATOR_C); // iterator_collection script.addInstruction(I_UNARY, I_ITERATOR_C); // iterator_collection
script.addInstruction(I_PUSH_CONST, script_nil); // push nil script.addInstruction(I_PUSH_CONST, script_nil); // push nil
lblStart = script.getLabel(); // lbl_start: unsigned lblStart = script.addInstruction(I_LOOP); // lbl_start: loop lbl_end
script.addInstruction(I_LOOP, INVALID_ADDRESS); // loop
expectToken(input, _("do")); // do expectToken(input, _("do")); // do
script.addInstruction(I_SET_VAR, script.addInstruction(I_SET_VAR,
string_to_variable(name.value));// set name string_to_variable(name.value));// set name
...@@ -499,8 +510,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -499,8 +510,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
parseOper(input, script, PREC_AND); // CCC parseOper(input, script, PREC_AND); // CCC
script.addInstruction(I_BINARY, I_ITERATOR_R); // iterator_range script.addInstruction(I_BINARY, I_ITERATOR_R); // iterator_range
script.addInstruction(I_PUSH_CONST, script_nil); // push nil script.addInstruction(I_PUSH_CONST, script_nil); // push nil
lblStart = script.getLabel(); // lbl_start: unsigned lblStart = script.addInstruction(I_LOOP); // lbl_start: loop lbl_end
script.addInstruction(I_LOOP, INVALID_ADDRESS); // loop
expectToken(input, _("do")); // do expectToken(input, _("do")); // do
script.addInstruction(I_SET_VAR, script.addInstruction(I_SET_VAR,
string_to_variable(name.value));// set name string_to_variable(name.value));// set name
...@@ -542,6 +552,25 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -542,6 +552,25 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
script.addInstruction(I_BINARY, op); script.addInstruction(I_BINARY, op);
} }
expectToken(input, _(")"), &token); expectToken(input, _(")"), &token);
} else if (token == _("assert")) {
// assert(condition)
expectToken(input, _("("));
size_t start = input.peek().pos;
int line = input.getLineNumber();
parseOper(input, script, PREC_ALL); // condition
size_t end = input.peek().pos;
String message = String::Format(_("Assertion failure on line %d: "), line) + input.getSourceCode(start,end);
expectToken(input, _(")"), &token);
// compile into: if condition then nil else warning("condition")
unsigned jmpElse = script.addInstruction(I_JUMP_IF_NOT); // jnz lbl_else
script.addInstruction(I_PUSH_CONST, script_nil); // push nil
unsigned jmpEnd = script.addInstruction(I_JUMP); // jump lbl_end
script.comeFrom(jmpElse); // lbl_else:
script.addInstruction(I_PUSH_CONST, script_warning); // push warning
script.addInstruction(I_PUSH_CONST, message); // push "condition"
script.addInstruction(I_CALL, 1); // call
script.addInstruction(I_NOP, SCRIPT_VAR_input); // (input:)
script.comeFrom(jmpEnd); // lbl_end:
} else { } else {
// variable // variable
Variable var = string_to_variable(token.value); Variable var = string_to_variable(token.value);
......
...@@ -86,10 +86,14 @@ ScriptValueP Script::eval(Context& ctx) const { ...@@ -86,10 +86,14 @@ ScriptValueP Script::eval(Context& ctx) const {
ScriptValueP Script::dependencies(Context& ctx, const Dependency& dep) const { ScriptValueP Script::dependencies(Context& ctx, const Dependency& dep) const {
return ctx.dependencies(dep, *this); return ctx.dependencies(dep, *this);
} }
void Script::addInstruction(InstructionType t) { static const unsigned int INVALID_ADDRESS = 0x03FFFFFF;
Instruction i = {t, {0}};
unsigned int Script::addInstruction(InstructionType t) {
assert( t == I_JUMP || t == I_JUMP_IF_NOT || t == I_LOOP);
Instruction i = {t, {INVALID_ADDRESS}};
instructions.push_back(i); instructions.push_back(i);
return getLabel() - 1;
} }
void Script::addInstruction(InstructionType t, unsigned int d) { void Script::addInstruction(InstructionType t, unsigned int d) {
// Don't optimize ...I_PUSH_CONST x; I_MEMBER... to I_MEMBER_C // Don't optimize ...I_PUSH_CONST x; I_MEMBER... to I_MEMBER_C
...@@ -198,8 +202,7 @@ String Script::dumpInstr(unsigned int pos, Instruction i) const { ...@@ -198,8 +202,7 @@ String Script::dumpInstr(unsigned int pos, Instruction i) const {
// arg // arg
switch (i.instr) { switch (i.instr) {
case I_PUSH_CONST: case I_MEMBER_C: // const case I_PUSH_CONST: case I_MEMBER_C: // const
ret += _("\t") + constants[i.data]->toString(); ret += _("\t") + constants[i.data]->typeName();
ret += _("\t(") + constants[i.data]->typeName() + _(")");
break; break;
case I_JUMP: case I_JUMP_IF_NOT: case I_LOOP: case I_MAKE_OBJECT: case I_CALL: case I_CLOSURE: // int case I_JUMP: case I_JUMP_IF_NOT: case I_LOOP: case I_MAKE_OBJECT: case I_CALL: case I_CLOSURE: // int
ret += String::Format(_("\t%d"), i.data); ret += String::Format(_("\t%d"), i.data);
......
...@@ -103,7 +103,6 @@ struct Instruction { ...@@ -103,7 +103,6 @@ struct Instruction {
QuaternaryInstructionType instr4 : 27; QuaternaryInstructionType instr4 : 27;
}; };
}; };
static const unsigned int INVALID_ADDRESS = 0x03FFFFFF;
// ----------------------------------------------------------------------------- : Variables // ----------------------------------------------------------------------------- : Variables
...@@ -157,8 +156,8 @@ class Script : public ScriptValue { ...@@ -157,8 +156,8 @@ class Script : public ScriptValue {
virtual ScriptValueP eval(Context& ctx) const; virtual ScriptValueP eval(Context& ctx) const;
virtual ScriptValueP dependencies(Context& ctx, const Dependency&) const; virtual ScriptValueP dependencies(Context& ctx, const Dependency&) const;
/// Add an instruction with no data /// Add a jump instruction, later comeFrom should be called on the returned value
void addInstruction(InstructionType t); unsigned int addInstruction(InstructionType t);
/// Add an instruction with integer data /// Add an instruction with integer data
void addInstruction(InstructionType t, unsigned int d); void addInstruction(InstructionType t, unsigned int d);
/// Add an instruction with constant data /// Add an instruction with constant data
...@@ -169,7 +168,6 @@ class Script : public ScriptValue { ...@@ -169,7 +168,6 @@ class Script : public ScriptValue {
/// Update an instruction to point to the current position /// Update an instruction to point to the current position
/** The instruction at pos must be a jumping instruction, it is changed so the current position /** 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). * 'comes from' pos (in addition to other control flow).
* The position must be a label just before the instruction to be updated.
*/ */
void comeFrom(unsigned int pos); void comeFrom(unsigned int pos);
/// Get the current instruction position /// Get the current instruction position
......
...@@ -69,7 +69,9 @@ $built_in_functions = array( ...@@ -69,7 +69,9 @@ $built_in_functions = array(
'copy_file' =>'', 'copy_file' =>'',
'write_text_file' =>'', 'write_text_file' =>'',
'write_image_file' =>'', 'write_image_file' =>'',
// other
'trace' =>'', 'trace' =>'',
'assert' =>'',
); );
......
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