Commit 4e8e1589 authored by twanvl's avatar twanvl

Added some more classes for ScriptErrors;

Added split_text function, which is the 'opposite' of break_text;
Script code "f\n(stuff)" now parses as "f;stuff" instead of a function call, "f(stuff)";
Fixed bug in cursor movement, which caused the closing > of a tag at end of input to be overwritten.
parent 81dcfb00
...@@ -74,7 +74,7 @@ ScriptValueP Context::eval(const Script& script, bool useScope) { ...@@ -74,7 +74,7 @@ ScriptValueP Context::eval(const Script& script, bool useScope) {
// Get a variable // Get a variable
case I_GET_VAR: { case I_GET_VAR: {
ScriptValueP value = variables[i.data].value; ScriptValueP value = variables[i.data].value;
if (!value) throw ScriptError(_("Variable not set: ") + variable_to_string((Variable)i.data)); if (!value) throw ScriptErrorNoVariable(variable_to_string((Variable)i.data));
stack.push_back(value); stack.push_back(value);
break; break;
} }
...@@ -124,7 +124,7 @@ ScriptValueP Context::eval(const Script& script, bool useScope) { ...@@ -124,7 +124,7 @@ ScriptValueP Context::eval(const Script& script, bool useScope) {
// Function call // Function call
case I_CALL: { case I_CALL: {
// new scope // new scope
size_t scope = openScope(); LocalScope local_scope(*this);
// prepare arguments // prepare arguments
for (unsigned int j = 0 ; j < i.data ; ++j) { for (unsigned int j = 0 ; j < i.data ; ++j) {
setVariable((Variable)instr[i.data - j - 1].data, stack.back()); setVariable((Variable)instr[i.data - j - 1].data, stack.back());
...@@ -135,7 +135,6 @@ ScriptValueP Context::eval(const Script& script, bool useScope) { ...@@ -135,7 +135,6 @@ ScriptValueP Context::eval(const Script& script, bool useScope) {
// get function and call // get function and call
stack.back() = stack.back()->eval(*this); stack.back() = stack.back()->eval(*this);
} catch (const Error& e) { } catch (const Error& e) {
closeScope(scope);
// try to determine what named function was called // try to determine what named function was called
// the instructions for this look like: // the instructions for this look like:
// I_GET_VAR name of function // I_GET_VAR name of function
...@@ -152,8 +151,6 @@ ScriptValueP Context::eval(const Script& script, bool useScope) { ...@@ -152,8 +151,6 @@ ScriptValueP Context::eval(const Script& script, bool useScope) {
throw e; // rethrow throw e; // rethrow
} }
} }
// restore scope
closeScope(scope);
break; break;
} }
...@@ -241,7 +238,7 @@ void Context::setVariable(Variable name, const ScriptValueP& value) { ...@@ -241,7 +238,7 @@ void Context::setVariable(Variable name, const ScriptValueP& value) {
ScriptValueP Context::getVariable(const String& name) { ScriptValueP Context::getVariable(const String& name) {
ScriptValueP value = variables[string_to_variable(name)].value; ScriptValueP value = variables[string_to_variable(name)].value;
if (!value) throw ScriptError(_("Variable not set: ") + name); if (!value) throw ScriptErrorNoVariable(name);
return value; return value;
} }
...@@ -250,7 +247,7 @@ ScriptValueP Context::getVariableOpt(const String& name) { ...@@ -250,7 +247,7 @@ ScriptValueP Context::getVariableOpt(const String& name) {
} }
ScriptValueP Context::getVariable(Variable var) { ScriptValueP Context::getVariable(Variable var) {
if (variables[var].value) return variables[var].value; if (variables[var].value) return variables[var].value;
throw ScriptError(_("Variable not set: ") + variable_to_string(var)); throw ScriptErrorNoVariable(variable_to_string(var));
} }
ScriptValueP Context::getVariableInScopeOpt(Variable var) { ScriptValueP Context::getVariableInScopeOpt(Variable var) {
if (variables[var].level == level) return variables[var].value; if (variables[var].level == level) return variables[var].value;
......
...@@ -110,14 +110,14 @@ SCRIPT_FUNCTION(to_int) { ...@@ -110,14 +110,14 @@ SCRIPT_FUNCTION(to_int) {
} else if (str.empty()) { } else if (str.empty()) {
result = 0; result = 0;
} else { } else {
return delayError(_ERROR_3_("can't convert value", str, input->typeName(), _TYPE_("integer"))); return delay_error(ScriptErrorConversion(str, input->typeName(), _TYPE_("integer")));
} }
} else { } else {
result = (int)*input; result = (int)*input;
} }
SCRIPT_RETURN(result); SCRIPT_RETURN(result);
} catch (const ScriptError& e) { } catch (const ScriptError& e) {
return new_intrusive1<ScriptDelayedError>(e); return delay_error(e);
} }
} }
...@@ -136,14 +136,14 @@ SCRIPT_FUNCTION(to_real) { ...@@ -136,14 +136,14 @@ SCRIPT_FUNCTION(to_real) {
if (str.empty()) { if (str.empty()) {
result = 0.0; result = 0.0;
} else if (!str.ToDouble(&result)) { } else if (!str.ToDouble(&result)) {
return delayError(_ERROR_3_("can't convert value", str, input->typeName(), _TYPE_("double"))); return delay_error(ScriptErrorConversion(str, input->typeName(), _TYPE_("double")));
} }
} else { } else {
result = (double)*input; result = (double)*input;
} }
SCRIPT_RETURN(result); SCRIPT_RETURN(result);
} catch (const ScriptError& e) { } catch (const ScriptError& e) {
return new_intrusive1<ScriptDelayedError>(e); return delay_error(e);
} }
} }
...@@ -170,11 +170,11 @@ SCRIPT_FUNCTION(to_number) { ...@@ -170,11 +170,11 @@ SCRIPT_FUNCTION(to_number) {
} else if (str.empty()) { } else if (str.empty()) {
SCRIPT_RETURN(0); SCRIPT_RETURN(0);
} else { } else {
return delayError(_ERROR_3_("can't convert value", str, input->typeName(), _TYPE_("double"))); return delay_error(ScriptErrorConversion(str, input->typeName(), _TYPE_("double")));
} }
} }
} catch (const ScriptError& e) { } catch (const ScriptError& e) {
return new_intrusive1<ScriptDelayedError>(e); return delay_error(e);
} }
} }
...@@ -190,7 +190,7 @@ SCRIPT_FUNCTION(to_boolean) { ...@@ -190,7 +190,7 @@ SCRIPT_FUNCTION(to_boolean) {
} }
SCRIPT_RETURN(result); SCRIPT_RETURN(result);
} catch (const ScriptError& e) { } catch (const ScriptError& e) {
return new_intrusive1<ScriptDelayedError>(e); return delay_error(e);
} }
} }
...@@ -199,7 +199,7 @@ SCRIPT_FUNCTION(to_color) { ...@@ -199,7 +199,7 @@ SCRIPT_FUNCTION(to_color) {
SCRIPT_PARAM_C(AColor, input); SCRIPT_PARAM_C(AColor, input);
SCRIPT_RETURN(input); SCRIPT_RETURN(input);
} catch (const ScriptError& e) { } catch (const ScriptError& e) {
return new_intrusive1<ScriptDelayedError>(e); return delay_error(e);
} }
} }
......
...@@ -124,7 +124,7 @@ SCRIPT_FUNCTION(symbol_variation) { ...@@ -124,7 +124,7 @@ SCRIPT_FUNCTION(symbol_variation) {
if (value) { if (value) {
filename = value->filename; filename = value->filename;
} else if (valueO) { } else if (valueO) {
throw ScriptError(_ERROR_2_("can't convert", valueO->typeName(), _TYPE_("symbol" ))); throw ScriptErrorConversion(valueO->typeName(), _TYPE_("symbol" ));
} else { } else {
filename = from_script<String>(symbol); filename = from_script<String>(symbol);
} }
......
...@@ -192,6 +192,38 @@ SCRIPT_FUNCTION_SIMPLIFY_CLOSURE(break_text) { ...@@ -192,6 +192,38 @@ SCRIPT_FUNCTION_SIMPLIFY_CLOSURE(break_text) {
return ScriptValueP(); return ScriptValueP();
} }
// ----------------------------------------------------------------------------- : Rules : regex split
SCRIPT_FUNCTION_WITH_SIMPLIFY(split_text) {
SCRIPT_PARAM_C(String, input);
SCRIPT_PARAM_C(ScriptRegexP, match);
SCRIPT_PARAM_DEFAULT_N(bool, _("include empty"), include_empty, true);
ScriptCustomCollectionP ret(new ScriptCustomCollection);
// find all matches
while (match->regex.Matches(input)) {
// match, append to result
size_t start, len;
bool ok = match->regex.GetMatch(&start, &len, 0);
assert(ok);
if (include_empty || start > 0) {
ret->value.push_back(to_script(input.substr(0,start)));
}
input = input.substr(start + len); // everything after the match
}
if (include_empty || !input.empty()) {
ret->value.push_back(to_script(input));
}
return ret;
}
SCRIPT_FUNCTION_SIMPLIFY_CLOSURE(split_text) {
FOR_EACH(b, closure.bindings) {
if (b.first == SCRIPT_VAR_match) {
b.second = regex_from_script(b.second); // pre-compile
}
}
return ScriptValueP();
}
// ----------------------------------------------------------------------------- : Rules : regex match // ----------------------------------------------------------------------------- : Rules : regex match
SCRIPT_FUNCTION_WITH_SIMPLIFY(match) { SCRIPT_FUNCTION_WITH_SIMPLIFY(match) {
...@@ -214,6 +246,7 @@ void init_script_regex_functions(Context& ctx) { ...@@ -214,6 +246,7 @@ void init_script_regex_functions(Context& ctx) {
ctx.setVariable(_("replace"), script_replace); ctx.setVariable(_("replace"), script_replace);
ctx.setVariable(_("filter text"), script_filter_text); ctx.setVariable(_("filter text"), script_filter_text);
ctx.setVariable(_("break text"), script_break_text); ctx.setVariable(_("break text"), script_break_text);
ctx.setVariable(_("split text"), script_split_text);
ctx.setVariable(_("match"), script_match); ctx.setVariable(_("match"), script_match);
ctx.setVariable(_("replace rule"), new_intrusive1<ScriptRule>(script_replace)); ctx.setVariable(_("replace rule"), new_intrusive1<ScriptRule>(script_replace));
ctx.setVariable(_("filter rule"), new_intrusive1<ScriptRule>(script_filter_text)); ctx.setVariable(_("filter rule"), new_intrusive1<ScriptRule>(script_filter_text));
......
...@@ -625,7 +625,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -625,7 +625,7 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
void parseOper(TokenIterator& input, Script& script, Precedence minPrec, InstructionType closeWith, int closeWithData) { void parseOper(TokenIterator& input, Script& script, Precedence minPrec, InstructionType closeWith, int closeWithData) {
size_t added = script.getInstructions().size(); // number of instructions added size_t added = script.getInstructions().size(); // number of instructions added
parseExpr(input, script, minPrec); // first argument parseExpr(input, script, minPrec); // first argument
added -= script.getInstructions().size(); added = script.getInstructions().size() - added;
// read any operators after an expression // read any operators after an expression
// EBNF: expr = expr | expr oper expr // EBNF: expr = expr | expr oper expr
// without left recursion: expr = expr (oper expr)* // without left recursion: expr = expr (oper expr)*
...@@ -649,7 +649,7 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc ...@@ -649,7 +649,7 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
// We made a mistake, the part before the := should be a variable name, // We made a mistake, the part before the := should be a variable name,
// not an expression. Remove that instruction. // not an expression. Remove that instruction.
Instruction& instr = script.getInstructions().back(); Instruction& instr = script.getInstructions().back();
if (added == 1 && instr.instr != I_GET_VAR) { if (added != 1 || instr.instr != I_GET_VAR) {
input.add_error(_("Can only assign to variables")); input.add_error(_("Can only assign to variables"));
} }
script.getInstructions().pop_back(); script.getInstructions().pop_back();
...@@ -695,9 +695,9 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc ...@@ -695,9 +695,9 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
} else { } else {
input.expected(_("name")); input.expected(_("name"));
} }
} else if (minPrec <= PREC_FUN && token==_("[")) { // get member by expr } else if (minPrec <= PREC_FUN && token==_("[") && !token.newline) { // get member by expr
size_t before = script.getInstructions().size(); size_t before = script.getInstructions().size();
parseOper(input, script, PREC_ALL); parseOper(input, script, PREC_SET);
if (script.getInstructions().size() == before + 1 && script.getInstructions().back().instr == I_PUSH_CONST) { if (script.getInstructions().size() == before + 1 && script.getInstructions().back().instr == I_PUSH_CONST) {
// optimize: // optimize:
// PUSH_CONST x // PUSH_CONST x
...@@ -709,7 +709,7 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc ...@@ -709,7 +709,7 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
script.addInstruction(I_BINARY, I_MEMBER); script.addInstruction(I_BINARY, I_MEMBER);
} }
expectToken(input, _("]"), &token); expectToken(input, _("]"), &token);
} else if (minPrec <= PREC_FUN && token==_("(")) { } else if (minPrec <= PREC_FUN && token==_("(") && !token.newline) {
// function call, read arguments // function call, read arguments
vector<Variable> arguments; vector<Variable> arguments;
parseCallArguments(input, script, arguments); parseCallArguments(input, script, arguments);
......
...@@ -79,9 +79,12 @@ class ScriptDelayedError : public ScriptValue { ...@@ -79,9 +79,12 @@ class ScriptDelayedError : public ScriptValue {
ScriptError error; // the error message ScriptError error; // the error message
}; };
inline ScriptValueP delayError(const String& m) { inline ScriptValueP delay_error(const String& m) {
return new_intrusive1<ScriptDelayedError>(ScriptError(m)); return new_intrusive1<ScriptDelayedError>(ScriptError(m));
} }
inline ScriptValueP delay_error(const ScriptError& error) {
return new_intrusive1<ScriptDelayedError>(error);
}
// ----------------------------------------------------------------------------- : Iterators // ----------------------------------------------------------------------------- : Iterators
...@@ -93,6 +96,7 @@ struct ScriptIterator : public ScriptValue { ...@@ -93,6 +96,7 @@ struct ScriptIterator : public ScriptValue {
/// Return the next item for this iterator, or ScriptValueP() if there is no such item /// Return the next item for this iterator, or ScriptValueP() if there is no such item
virtual ScriptValueP next(ScriptValueP* key_out = nullptr) = 0; virtual ScriptValueP next(ScriptValueP* key_out = nullptr) = 0;
virtual ScriptValueP makeIterator(const ScriptValueP& thisP) const;
}; };
// make an iterator over a range // make an iterator over a range
...@@ -161,7 +165,7 @@ ScriptValueP get_member(const map<String,V>& m, const String& name) { ...@@ -161,7 +165,7 @@ ScriptValueP get_member(const map<String,V>& m, const String& name) {
if (it != m.end()) { if (it != m.end()) {
return to_script(it->second); return to_script(it->second);
} else { } else {
return delayError(_ERROR_2_("has no member", _TYPE_("collection"), name)); return delay_error(ScriptErrorNoMember(_TYPE_("collection"), name));
} }
} }
...@@ -171,7 +175,7 @@ ScriptValueP get_member(const IndexMap<K,V>& m, const String& name) { ...@@ -171,7 +175,7 @@ ScriptValueP get_member(const IndexMap<K,V>& m, const String& name) {
if (it != m.end()) { if (it != m.end()) {
return to_script(*it); return to_script(*it);
} else { } else {
return delayError(_ERROR_2_("has no member", _TYPE_("collection"), name)); return delay_error(ScriptErrorNoMember(_TYPE_("collection"), name));
} }
} }
...@@ -389,7 +393,7 @@ inline ScriptValueP to_script(const Defaultable<T>& v) { return to_script(v()); ...@@ -389,7 +393,7 @@ inline ScriptValueP to_script(const Defaultable<T>& v) { return to_script(v());
template <typename T> inline T from_script (const ScriptValueP& value) { template <typename T> inline T from_script (const ScriptValueP& value) {
ScriptObject<T>* o = dynamic_cast<ScriptObject<T>*>(value.get()); ScriptObject<T>* o = dynamic_cast<ScriptObject<T>*>(value.get());
if (!o) { if (!o) {
throw ScriptError(_ERROR_2_("can't convert", value->typeName(), _TYPE_("object" ))); throw ScriptErrorConversion(value->typeName(), _TYPE_("object" ));
} }
return o->getValue(); return o->getValue();
} }
......
...@@ -19,16 +19,16 @@ DECLARE_TYPEOF_COLLECTION(pair<Variable COMMA ScriptValueP>); ...@@ -19,16 +19,16 @@ DECLARE_TYPEOF_COLLECTION(pair<Variable COMMA ScriptValueP>);
// ----------------------------------------------------------------------------- : ScriptValue // ----------------------------------------------------------------------------- : ScriptValue
// Base cases // Base cases
ScriptValue::operator String() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("string" ))); } ScriptValue::operator String() const { throw ScriptErrorConversion(typeName(), _TYPE_("string" )); }
ScriptValue::operator int() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("integer" ))); } ScriptValue::operator int() const { throw ScriptErrorConversion(typeName(), _TYPE_("integer" )); }
ScriptValue::operator bool() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("boolean" ))); } ScriptValue::operator bool() const { throw ScriptErrorConversion(typeName(), _TYPE_("boolean" )); }
ScriptValue::operator double() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("double" ))); } ScriptValue::operator double() const { throw ScriptErrorConversion(typeName(), _TYPE_("double" )); }
ScriptValue::operator AColor() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("color" ))); } ScriptValue::operator AColor() const { throw ScriptErrorConversion(typeName(), _TYPE_("color" )); }
ScriptValueP ScriptValue::eval(Context&) const { return delayError(_ERROR_2_("can't convert", typeName(), _TYPE_("function"))); } ScriptValueP ScriptValue::eval(Context&) const { return delay_error(ScriptErrorConversion(typeName(), _TYPE_("function"))); }
ScriptValueP ScriptValue::next(ScriptValueP* key_out) { throw InternalError(_("Can't convert from ")+typeName()+_(" to iterator")); } ScriptValueP ScriptValue::next(ScriptValueP* key_out) { throw InternalError(_("Can't convert from ")+typeName()+_(" to iterator")); }
ScriptValueP ScriptValue::makeIterator(const ScriptValueP&) const { return delayError(_ERROR_2_("can't convert", typeName(), _TYPE_("collection"))); } ScriptValueP ScriptValue::makeIterator(const ScriptValueP&) const { return delay_error(ScriptErrorConversion(typeName(), _TYPE_("collection"))); }
int ScriptValue::itemCount() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("collection"))); } int ScriptValue::itemCount() const { throw ScriptErrorConversion(typeName(), _TYPE_("collection")); }
GeneratedImageP ScriptValue::toImage(const ScriptValueP&) const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("image" ))); } GeneratedImageP ScriptValue::toImage(const ScriptValueP&) const { throw ScriptErrorConversion(typeName(), _TYPE_("image" )); }
String ScriptValue::toCode() const { return *this; } String ScriptValue::toCode() const { return *this; }
CompareWhat ScriptValue::compareAs(String& compare_str, void const*& compare_ptr) const { CompareWhat ScriptValue::compareAs(String& compare_str, void const*& compare_ptr) const {
compare_str = toCode(); compare_str = toCode();
...@@ -39,11 +39,11 @@ ScriptValueP ScriptValue::getMember(const String& name) const { ...@@ -39,11 +39,11 @@ ScriptValueP ScriptValue::getMember(const String& name) const {
if (name.ToLong(&index)) { if (name.ToLong(&index)) {
return getIndex(index); return getIndex(index);
} else { } else {
return delayError(_ERROR_2_("has no member", typeName(), name)); return delay_error(ScriptErrorNoMember(typeName(), name));
} }
} }
ScriptValueP ScriptValue::getIndex(int index) const { ScriptValueP ScriptValue::getIndex(int index) const {
return delayError(_ERROR_2_("has no member", typeName(), String()<<index)); return delay_error(ScriptErrorNoMember(typeName(), String()<<index));
} }
...@@ -122,6 +122,7 @@ ScriptValueP ScriptDelayedError::makeIterator(const ScriptValueP& thisP) const ...@@ -122,6 +122,7 @@ ScriptValueP ScriptDelayedError::makeIterator(const ScriptValueP& thisP) const
ScriptType ScriptIterator::type() const { return SCRIPT_ITERATOR; } ScriptType ScriptIterator::type() const { return SCRIPT_ITERATOR; }
String ScriptIterator::typeName() const { return _("iterator"); } String ScriptIterator::typeName() const { return _("iterator"); }
CompareWhat ScriptIterator::compareAs(String&, void const*&) const { return COMPARE_NO; } CompareWhat ScriptIterator::compareAs(String&, void const*&) const { return COMPARE_NO; }
ScriptValueP ScriptIterator::makeIterator(const ScriptValueP& thisP) const { return thisP; }
// Iterator over a range of integers // Iterator over a range of integers
class ScriptRangeIterator : public ScriptIterator { class ScriptRangeIterator : public ScriptIterator {
...@@ -248,7 +249,7 @@ class ScriptString : public ScriptValue { ...@@ -248,7 +249,7 @@ class ScriptString : public ScriptValue {
if (value.ToDouble(&d)) { if (value.ToDouble(&d)) {
return d; return d;
} else { } else {
throw ScriptError(_ERROR_3_("can't convert value", value, typeName(), _TYPE_("double"))); throw ScriptErrorConversion(value, typeName(), _TYPE_("double"));
} }
} }
virtual operator int() const { virtual operator int() const {
...@@ -256,7 +257,7 @@ class ScriptString : public ScriptValue { ...@@ -256,7 +257,7 @@ class ScriptString : public ScriptValue {
if (value.ToLong(&l)) { if (value.ToLong(&l)) {
return l; return l;
} else { } else {
throw ScriptError(_ERROR_3_("can't convert value", value, typeName(), _TYPE_("integer"))); throw ScriptErrorConversion(value, typeName(), _TYPE_("integer"));
} }
} }
virtual operator bool() const { virtual operator bool() const {
...@@ -265,13 +266,13 @@ class ScriptString : public ScriptValue { ...@@ -265,13 +266,13 @@ class ScriptString : public ScriptValue {
} else if (value == _("no") || value == _("false") || value.empty()) { } else if (value == _("no") || value == _("false") || value.empty()) {
return false; return false;
} else { } else {
throw ScriptError(_ERROR_3_("can't convert value", value, typeName(), _TYPE_("boolean"))); throw ScriptErrorConversion(value, typeName(), _TYPE_("boolean"));
} }
} }
virtual operator AColor() const { virtual operator AColor() const {
AColor c = parse_acolor(value); AColor c = parse_acolor(value);
if (!c.Ok()) { if (!c.Ok()) {
throw ScriptError(_ERROR_3_("can't convert value", value, typeName(), _TYPE_("color"))); throw ScriptErrorConversion(value, typeName(), _TYPE_("color"));
} }
return c; return c;
} }
...@@ -285,7 +286,7 @@ class ScriptString : public ScriptValue { ...@@ -285,7 +286,7 @@ class ScriptString : public ScriptValue {
if (name.ToLong(&index) && index >= 0 && (size_t)index < value.size()) { if (name.ToLong(&index) && index >= 0 && (size_t)index < value.size()) {
return to_script(String(1,value[index])); return to_script(String(1,value[index]));
} else { } else {
return delayError(_ERROR_2_("has no member value", value, name)); return delay_error(_ERROR_2_("has no member value", value, name));
} }
} }
private: private:
......
...@@ -108,6 +108,28 @@ class ScriptError : public Error { ...@@ -108,6 +108,28 @@ class ScriptError : public Error {
inline ScriptError(const String& str) : Error(str) {} inline ScriptError(const String& str) : Error(str) {}
}; };
/// "Variable not set"
class ScriptErrorNoVariable : public ScriptError {
public:
inline ScriptErrorNoVariable(const String& var) : ScriptError(_("Variable not set: ") + var) {}
};
/// "Can't convert from A to B"
class ScriptErrorConversion : public ScriptError {
public:
inline ScriptErrorConversion(const String& a, const String& b)
: ScriptError(_ERROR_2_("can't convert", a, b)) {}
inline ScriptErrorConversion(const String& value, const String& a, const String& b)
: ScriptError(_ERROR_3_("can't convert value", value, a, b)) {}
};
/// "A has no member B"
class ScriptErrorNoMember : public ScriptError {
public:
inline ScriptErrorNoMember(const String& type, const String& member)
: ScriptError(_ERROR_2_("has no member", type, member)) {}
};
// ----------------------------------------------------------------------------- : Error handling // ----------------------------------------------------------------------------- : Error handling
/// Should errors be written to stdout? /// Should errors be written to stdout?
......
...@@ -307,9 +307,12 @@ void cursor_to_index_range(const String& str, size_t cursor, size_t& start, size ...@@ -307,9 +307,12 @@ void cursor_to_index_range(const String& str, size_t cursor, size_t& start, size
if (cur == cursor) start = i; if (cur == cursor) start = i;
} }
} }
if (cur < cursor) {
start = end = size;
} else {
end = min(i, size); end = min(i, size);
if (cur < cursor) start = end = size; }
if (end <= start) end = start + 1; end = max(end, start + 1); // always start < end, since there are always valid cursor positions
} }
size_t cursor_to_index(const String& str, size_t cursor, Movement dir) { size_t cursor_to_index(const String& str, size_t cursor, Movement dir) {
......
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