Commit ae47bd3e authored by twanvl's avatar twanvl

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.
parent cb9ea8ca
......@@ -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<String> 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<String,wxSize> exported_images; ///< Images (from symbol font) already exported, and their size
};
DECLARE_DYNAMIC_ARG(ExportInfo*, export_info);
......
......@@ -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
......
......@@ -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
......@@ -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
......
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