mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 13:06:59 -04:00
Added "width= and height=" to symbols exported to html;
Added a backtrace function to the script evaluator, error messages now include a 'stack trace' of functions that were called. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@446 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
+21
-2
@@ -116,8 +116,27 @@ ScriptValueP Context::eval(const Script& script, bool useScope) {
|
||||
stack.pop_back();
|
||||
}
|
||||
instr += i.data; // skip arguments
|
||||
// get function and call
|
||||
stack.back() = stack.back()->eval(*this);
|
||||
try {
|
||||
// get function and call
|
||||
stack.back() = stack.back()->eval(*this);
|
||||
} catch (const Error& e) {
|
||||
// try to determine what named function was called
|
||||
// the instructions for this look like:
|
||||
// I_GET_VAR name of function
|
||||
// *code* arguments
|
||||
// I_CALL number of arguments = i.data
|
||||
// I_NOP * n arg names
|
||||
// next <--- instruction pointer points here
|
||||
// skip the stack effect of the arguments themselfs
|
||||
const Instruction* instr_bt = script.backtraceSkip(instr - i.data - 2, i.data);
|
||||
// have we have reached the name
|
||||
if (instr_bt && instr_bt->instr == I_GET_VAR) {
|
||||
// this is a valid instruction, it is I_GET_VAR
|
||||
throw ScriptError(e.what() + _("\n in function: ") + variable_to_string(instr_bt->data));
|
||||
} else {
|
||||
throw e; // rethrow
|
||||
}
|
||||
}
|
||||
// restore scope
|
||||
closeScope(scope);
|
||||
break;
|
||||
|
||||
@@ -169,15 +169,19 @@ String symbols_to_html(const String& str, SymbolFont& symbol_font, double size)
|
||||
String html;
|
||||
FOR_EACH(sym, symbols) {
|
||||
String filename = symbol_font.name() + _("-") + clean_filename(sym.text) + _(".png");
|
||||
html += _("<img src='") + filename + _("' alt='") + html_escape(sym.text) + _("'>");
|
||||
if (ei.exported_images.insert(filename).second) {
|
||||
map<String,wxSize>::iterator it = ei.exported_images.find(filename);
|
||||
if (it == ei.exported_images.end()) {
|
||||
// save symbol image
|
||||
Image img = symbol_font.getImage(size, sym);
|
||||
wxFileName fn;
|
||||
fn.SetPath(ei.directory_absolute);
|
||||
fn.SetFullName(filename);
|
||||
img.SaveFile(fn.GetFullPath());
|
||||
it = ei.exported_images.insert(make_pair(filename, wxSize(img.GetWidth(), img.GetHeight()))).first;
|
||||
}
|
||||
html += _("<img src='") + filename + _("' alt='") + html_escape(sym.text)
|
||||
+ _("' width='") + (String() << it->second.x)
|
||||
+ _("' height='") + (String() << it->second.y) + _("'>");
|
||||
}
|
||||
return html;
|
||||
}
|
||||
@@ -386,7 +390,7 @@ SCRIPT_FUNCTION(write_image_file) {
|
||||
wxFileName fn;
|
||||
fn.SetPath(ei.directory_absolute);
|
||||
fn.SetFullName(file);
|
||||
if (!ei.exported_images.insert(fn.GetFullName()).second) {
|
||||
if (ei.exported_images.find(fn.GetFullName()) != ei.exported_images.end()) {
|
||||
SCRIPT_RETURN(fn.GetFullName()); // already written an image with this name
|
||||
}
|
||||
// get image
|
||||
@@ -404,6 +408,7 @@ SCRIPT_FUNCTION(write_image_file) {
|
||||
if (!image.Ok()) throw Error(_("Unable to generate image for file ") + file);
|
||||
// write
|
||||
image.SaveFile(fn.GetFullPath());
|
||||
ei.exported_images.insert(make_pair(fn.GetFullName(), wxSize(image.GetWidth(), image.GetHeight())));
|
||||
SCRIPT_RETURN(fn.GetFullName());
|
||||
}
|
||||
|
||||
|
||||
@@ -426,11 +426,11 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
|
||||
unsigned int jmpElse, jmpEnd;
|
||||
parseOper(input, script, PREC_AND); // AAA
|
||||
jmpElse = script.getLabel(); // jmp_else:
|
||||
script.addInstruction(I_JUMP_IF_NOT, 0xFFFF); // jnz lbl_else
|
||||
script.addInstruction(I_JUMP_IF_NOT, 0xFFFFFFFF); // jnz lbl_else
|
||||
expectToken(input, _("then")); // then
|
||||
parseOper(input, script, PREC_SET); // BBB
|
||||
jmpEnd = script.getLabel(); // jmp_end:
|
||||
script.addInstruction(I_JUMP, 0xFFFF); // jump lbl_end
|
||||
script.addInstruction(I_JUMP, 0xFFFFFFFF); // jump lbl_end
|
||||
script.comeFrom(jmpElse); // lbl_else:
|
||||
if (input.peek() == _("else")) { // else
|
||||
input.read();
|
||||
@@ -455,7 +455,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
|
||||
script.addInstruction(I_UNARY, I_ITERATOR_C); // iterator_collection
|
||||
script.addInstruction(I_PUSH_CONST, script_nil); // push nil
|
||||
lblStart = script.getLabel(); // lbl_start:
|
||||
script.addInstruction(I_LOOP, 0xFFFF); // loop
|
||||
script.addInstruction(I_LOOP, 0xFFFFFFFF); // loop
|
||||
expectToken(input, _("do")); // do
|
||||
script.addInstruction(I_SET_VAR,
|
||||
string_to_variable(name.value));// set name
|
||||
@@ -473,7 +473,7 @@ void 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
|
||||
lblStart = script.getLabel(); // lbl_start:
|
||||
script.addInstruction(I_LOOP, 0xFFFF); // loop
|
||||
script.addInstruction(I_LOOP, 0xFFFFFFFF); // loop
|
||||
expectToken(input, _("do")); // do
|
||||
script.addInstruction(I_SET_VAR,
|
||||
string_to_variable(name.value));// set name
|
||||
|
||||
+73
-2
@@ -40,9 +40,9 @@ Variable string_to_variable(const String& s) {
|
||||
*/
|
||||
String variable_to_string(Variable v) {
|
||||
FOR_EACH(vi, variables) {
|
||||
if (vi.second == v) return vi.first;
|
||||
if (vi.second == v) return replace_all(vi.first, _(" "), _("_"));
|
||||
}
|
||||
throw ScriptError(String(_("Variable not found: ")) << v);
|
||||
throw InternalError(String(_("Variable not found: ")) << v);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Script
|
||||
@@ -173,3 +173,74 @@ String Script::dumpInstr(unsigned int pos, Instruction i) const {
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Backtracing
|
||||
|
||||
const Instruction* Script::backtraceSkip(const Instruction* instr, int to_skip) const {
|
||||
for (;instr >= &instructions[0] &&
|
||||
(to_skip || // we have something to skip
|
||||
instr >= &instructions[1] && (instr-1)->instr == I_JUMP // always look inside a jump
|
||||
) ; --instr) {
|
||||
// skip an instruction
|
||||
switch (instr->instr) {
|
||||
case I_PUSH_CONST:
|
||||
case I_GET_VAR:
|
||||
to_skip -= 1; break; // nett stack effect +1
|
||||
case I_POP:
|
||||
case I_BINARY:
|
||||
to_skip += 1; break; // nett stack effect 1-2 == -1
|
||||
case I_TERNARY:
|
||||
to_skip += 2; break; // nett stack effect 1-3 == -1
|
||||
case I_CALL:
|
||||
to_skip += instr->data; // arguments of call
|
||||
break;
|
||||
case I_MAKE_OBJECT:
|
||||
to_skip += 2 * instr->data - 1;
|
||||
break;
|
||||
case I_JUMP: {
|
||||
// jumps outputed by the parser are always backwards
|
||||
// and there will be a way not to take this jump
|
||||
// the part in between will have no significant stack effect
|
||||
assert(&instructions[instr->data] < instr);
|
||||
unsigned int after_jump = instr + 1 - &instructions[0];
|
||||
for (--instr ; instr >= &instructions[0] ; --instr) {
|
||||
if (instr->instr == I_LOOP && instr->data == after_jump) {
|
||||
// code looks like
|
||||
// 1 (nettstack+1) iterator
|
||||
// 2 (nettstack+1) accumulator (usually push nil)
|
||||
// loop:
|
||||
// 3 I_LOOP end
|
||||
// 4 (netstack+0)
|
||||
// 5 I_JUMP loop
|
||||
// end:
|
||||
// we have not handled anything for this loop, current position is 2,
|
||||
// we need to skip two things (iterator+accumulator) instead of one
|
||||
to_skip += 1;
|
||||
break;
|
||||
} else if (instr->instr == I_JUMP_IF_NOT && instr->data == after_jump) {
|
||||
// code looks like
|
||||
// 1 (nettstack+1)
|
||||
// 2 I_JUMP_IF_NOT else
|
||||
// 3 (nettstack+1)
|
||||
// 4 I_JUMP end
|
||||
// else:
|
||||
// 5 (nettstack+1)
|
||||
// end:
|
||||
// we have already handled 4..5, current position is 2,
|
||||
// we need to skip an additional item for 1
|
||||
to_skip += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++instr; // compensate for the -- in the outer loop
|
||||
break;
|
||||
}
|
||||
case I_RET: case I_JUMP_IF_NOT: case I_LOOP:
|
||||
return nullptr; // give up
|
||||
default:
|
||||
break; // nett stack effect 0
|
||||
}
|
||||
}
|
||||
return instr >= &instructions[0] ? instr : nullptr;
|
||||
}
|
||||
@@ -153,6 +153,13 @@ class Script : public ScriptValue {
|
||||
/// Constant values that can be referred to from the script
|
||||
vector<ScriptValueP> constants;
|
||||
|
||||
/// Do a backtrace for error messages.
|
||||
/** Starting from instr, move backwards until the nett stack effect
|
||||
* of the skipped instructions is equal to_skip.
|
||||
* If the backtrace fails, returns nullptr
|
||||
*/
|
||||
const Instruction* backtraceSkip(const Instruction* instr, int to_skip) const;
|
||||
|
||||
friend class Context;
|
||||
};
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ void SetScriptManager::updateStyles(Context& ctx, const IndexMap<FieldP,StyleP>&
|
||||
s->tellListeners();
|
||||
}
|
||||
} catch (const ScriptError& e) {
|
||||
throw ScriptError(e.what() + _("\nWhile updating styles for '") + s->fieldP->name + _("'"));
|
||||
throw ScriptError(e.what() + _("\n while updating styles for '") + s->fieldP->name + _("'"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,7 +258,7 @@ void SetScriptManager::updateAll() {
|
||||
try {
|
||||
v->update(ctx);
|
||||
} catch (const ScriptError& e) {
|
||||
throw ScriptError(e.what() + _("\nWhile updating set value '") + v->fieldP->name + _("'"));
|
||||
throw ScriptError(e.what() + _("\n while updating set value '") + v->fieldP->name + _("'"));
|
||||
}
|
||||
}
|
||||
// update card data of all cards
|
||||
@@ -268,7 +268,7 @@ void SetScriptManager::updateAll() {
|
||||
try {
|
||||
v->update(ctx);
|
||||
} catch (const ScriptError& e) {
|
||||
throw ScriptError(e.what() + _("\nWhile updating card value '") + v->fieldP->name + _("'"));
|
||||
throw ScriptError(e.what() + _("\n while updating card value '") + v->fieldP->name + _("'"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,7 +302,7 @@ void SetScriptManager::updateToUpdate(const ToUpdate& u, deque<ToUpdate>& to_upd
|
||||
try {
|
||||
changes = u.value->update(ctx);
|
||||
} catch (const ScriptError& e) {
|
||||
throw ScriptError(e.what() + _("\nWhile updating value '") + u.value->fieldP->name + _("'"));
|
||||
throw ScriptError(e.what() + _("\n while updating value '") + u.value->fieldP->name + _("'"));
|
||||
}
|
||||
if (changes) {
|
||||
// changed, send event
|
||||
|
||||
Reference in New Issue
Block a user