diff --git a/src/data/export_template.hpp b/src/data/export_template.hpp index 8dcca443..e4f15efd 100644 --- a/src/data/export_template.hpp +++ b/src/data/export_template.hpp @@ -43,12 +43,12 @@ class ExportTemplate : public Packaged { /// Information that can be used by export functions struct ExportInfo { - SetP set; ///< The set that is being exported - ExportTemplateP export_template; ///< The export template used - String directory_relative; ///< The directory for storing extra files (or "" if !export->create_directory) - /// This is just the directory name - String directory_absolute; ///< The absolute path of the directory - std::set exported_images; ///< Images (from symbol font) already exported + SetP set; ///< The set that is being exported + ExportTemplateP export_template; ///< The export template used + String directory_relative; ///< The directory for storing extra files (or "" if !export->create_directory) + /// This is just the directory name + String directory_absolute; ///< The absolute path of the directory + map exported_images; ///< Images (from symbol font) already exported, and their size }; DECLARE_DYNAMIC_ARG(ExportInfo*, export_info); diff --git a/src/script/context.cpp b/src/script/context.cpp index 9ed4e0de..81ae6575 100644 --- a/src/script/context.cpp +++ b/src/script/context.cpp @@ -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; diff --git a/src/script/functions/export.cpp b/src/script/functions/export.cpp index fdce2908..d477db8e 100644 --- a/src/script/functions/export.cpp +++ b/src/script/functions/export.cpp @@ -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 += _("") + html_escape(sym.text) + _(""); - if (ei.exported_images.insert(filename).second) { + map::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 += _("") + html_escape(sym.text)
+		     +  _(""); } 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()); } diff --git a/src/script/parser.cpp b/src/script/parser.cpp index 5910f3e4..a5283ecc 100644 --- a/src/script/parser.cpp +++ b/src/script/parser.cpp @@ -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 diff --git a/src/script/script.cpp b/src/script/script.cpp index 5de6d521..6362133b 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -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; +} \ No newline at end of file diff --git a/src/script/script.hpp b/src/script/script.hpp index 738ba4f4..07ac54c7 100644 --- a/src/script/script.hpp +++ b/src/script/script.hpp @@ -153,6 +153,13 @@ class Script : public ScriptValue { /// Constant values that can be referred to from the script vector 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; }; diff --git a/src/script/script_manager.cpp b/src/script/script_manager.cpp index 9de5e6b5..973eac54 100644 --- a/src/script/script_manager.cpp +++ b/src/script/script_manager.cpp @@ -220,7 +220,7 @@ void SetScriptManager::updateStyles(Context& ctx, const IndexMap& 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& 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