mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-13 05:57:00 -04:00
Added 'assert' pseudo function so I can run some tests of the script code
git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@988 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
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
|
|||||||
|
|
||||||
! 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) {
|
|||||||
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) {
|
|||||||
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
|
||||||
|
|||||||
+46
-17
@@ -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 {
|
|||||||
*/
|
*/
|
||||||
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) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
// 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 {
|
|||||||
QuaternaryInstructionType instr4 : 27;
|
QuaternaryInstructionType instr4 : 27;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
static const unsigned int INVALID_ADDRESS = 0x03FFFFFF;
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Variables
|
// ----------------------------------------------------------------------------- : Variables
|
||||||
|
|
||||||
@@ -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 {
|
|||||||
/// 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(
|
|||||||
'copy_file' =>'',
|
'copy_file' =>'',
|
||||||
'write_text_file' =>'',
|
'write_text_file' =>'',
|
||||||
'write_image_file' =>'',
|
'write_image_file' =>'',
|
||||||
|
// other
|
||||||
'trace' =>'',
|
'trace' =>'',
|
||||||
|
'assert' =>'',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user