mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-12 13:37:00 -04:00
improved error reporting for the keyword editor
git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@260 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -60,7 +60,7 @@ menu:
|
|||||||
set info tab: &Set Information F6
|
set info tab: &Set Information F6
|
||||||
style tab: St&yle F7
|
style tab: St&yle F7
|
||||||
keywords tab: &Keywords F8
|
keywords tab: &Keywords F8
|
||||||
stats tab: S&tatistics F8
|
stats tab: S&tatistics F9
|
||||||
|
|
||||||
help: &Help
|
help: &Help
|
||||||
index: &Index... F1
|
index: &Index... F1
|
||||||
@@ -331,6 +331,9 @@ label:
|
|||||||
mode: Mode
|
mode: Mode
|
||||||
uses: Uses
|
uses: Uses
|
||||||
reminder: Reminder text
|
reminder: Reminder text
|
||||||
|
standard keyword:
|
||||||
|
This is a standard %s keyword, you can not edit it.
|
||||||
|
If you make a copy of the keyword your copy will take precedent.
|
||||||
|
|
||||||
# Open dialogs
|
# Open dialogs
|
||||||
all files All files
|
all files All files
|
||||||
|
|||||||
@@ -84,27 +84,30 @@ void KeywordReminderTextValue::store() {
|
|||||||
retrieve();
|
retrieve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Re-highlight
|
// new value
|
||||||
String new_value = untag(value);
|
String new_value = untag(value);
|
||||||
highlight(new_value);
|
|
||||||
// Try to parse the script
|
// Try to parse the script
|
||||||
try {
|
vector<ScriptParseError> parse_errors;
|
||||||
ScriptP new_script = parse(new_value, true);
|
ScriptP new_script = parse(new_value, true, parse_errors);
|
||||||
|
if (parse_errors.empty()) {
|
||||||
// parsed okay, assign
|
// parsed okay, assign
|
||||||
errors.clear();
|
errors.clear();
|
||||||
keyword.reminder.getScriptP() = new_script;
|
keyword.reminder.getScriptP() = new_script;
|
||||||
keyword.reminder.getUnparsed() = new_value;
|
keyword.reminder.getUnparsed() = new_value;
|
||||||
} catch (const Error& e) {
|
} else {
|
||||||
// parse errors, report
|
// parse errors, report
|
||||||
errors = e.what(); // TODO
|
errors = ScriptParseErrors(parse_errors).what();
|
||||||
}
|
}
|
||||||
|
// re-highlight input, show errors
|
||||||
|
highlight(new_value, parse_errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeywordReminderTextValue::retrieve() {
|
void KeywordReminderTextValue::retrieve() {
|
||||||
highlight(*underlying);
|
vector<ScriptParseError> no_errors;
|
||||||
|
highlight(*underlying, no_errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeywordReminderTextValue::highlight(const String& code) {
|
void KeywordReminderTextValue::highlight(const String& code, const vector<ScriptParseError>& errors) {
|
||||||
// Add tags to indicate code / syntax highlight
|
// Add tags to indicate code / syntax highlight
|
||||||
// i.e. bla {if code "x" } bla
|
// i.e. bla {if code "x" } bla
|
||||||
// becomes:
|
// becomes:
|
||||||
@@ -112,7 +115,24 @@ void KeywordReminderTextValue::highlight(const String& code) {
|
|||||||
String new_value;
|
String new_value;
|
||||||
int in_brace = 0;
|
int in_brace = 0;
|
||||||
bool in_string = true;
|
bool in_string = true;
|
||||||
|
vector<ScriptParseError>::const_iterator error = errors.begin();
|
||||||
for (size_t pos = 0 ; pos < code.size() ; ) {
|
for (size_t pos = 0 ; pos < code.size() ; ) {
|
||||||
|
// error underlining
|
||||||
|
while (error != errors.end() && error->start == error->end) ++error;
|
||||||
|
if (error != errors.end()) {
|
||||||
|
if (error->start == pos) {
|
||||||
|
new_value += _("<error>");
|
||||||
|
}
|
||||||
|
if (error->end == pos) {
|
||||||
|
++error;
|
||||||
|
if (error == errors.end() || error->start > pos) {
|
||||||
|
new_value += _("</error>");
|
||||||
|
} else {
|
||||||
|
// immediatly open again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// process a character
|
||||||
Char c = code.GetChar(pos);
|
Char c = code.GetChar(pos);
|
||||||
if (c == _('<')) {
|
if (c == _('<')) {
|
||||||
new_value += _('\1'); // escape
|
new_value += _('\1'); // escape
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include <util/prec.hpp>
|
#include <util/prec.hpp>
|
||||||
#include <util/action_stack.hpp>
|
#include <util/action_stack.hpp>
|
||||||
|
#include <util/error.hpp>
|
||||||
#include <data/field/text.hpp>
|
#include <data/field/text.hpp>
|
||||||
|
|
||||||
class Set;
|
class Set;
|
||||||
@@ -93,7 +94,7 @@ class KeywordReminderTextValue : public KeywordTextValue {
|
|||||||
virtual void retrieve();
|
virtual void retrieve();
|
||||||
|
|
||||||
/// Syntax highlight, and store in value
|
/// Syntax highlight, and store in value
|
||||||
void highlight(const String& code);
|
void highlight(const String& code, const vector<ScriptParseError>& errors);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : EOF
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
|||||||
@@ -33,3 +33,23 @@ void AtomTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, co
|
|||||||
}
|
}
|
||||||
CompoundTextElement::draw(dc, scale, rect, xs, what, start, end);
|
CompoundTextElement::draw(dc, scale, rect, xs, what, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : ErrorTextElement
|
||||||
|
|
||||||
|
void ErrorTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const {
|
||||||
|
// Draw wavy underline
|
||||||
|
dc.SetPen(*wxRED_PEN);
|
||||||
|
RealPoint pos = rect.bottomLeft() - dc.trInvS(RealSize(0,2));
|
||||||
|
RealSize dx(dc.trInvS(2), 0), dy(0, dc.trInvS(1));
|
||||||
|
while (pos.x + 1 < rect.right()) {
|
||||||
|
dc.DrawLine(pos - dy, pos + dx + dy);
|
||||||
|
pos += dx;
|
||||||
|
dy = -dy;
|
||||||
|
}
|
||||||
|
if (pos.x < rect.right()) {
|
||||||
|
// final piece
|
||||||
|
dc.DrawLine(pos - dy, pos + dx / 2);
|
||||||
|
}
|
||||||
|
// Draw the contents
|
||||||
|
CompoundTextElement::draw(dc, scale, rect, xs, what, start, end);
|
||||||
|
}
|
||||||
@@ -73,18 +73,19 @@ struct TextElementsFromString {
|
|||||||
// What formatting is enabled?
|
// What formatting is enabled?
|
||||||
int bold, italic, symbol;
|
int bold, italic, symbol;
|
||||||
int soft, kwpph, param, line;
|
int soft, kwpph, param, line;
|
||||||
int code, code_kw, code_string, param_ref;
|
int code, code_kw, code_string, param_ref, error;
|
||||||
int param_id;
|
int param_id;
|
||||||
bool bracket;
|
bool bracket;
|
||||||
|
|
||||||
TextElementsFromString()
|
TextElementsFromString()
|
||||||
: bold(0), italic(0), symbol(0), soft(0), kwpph(0), param(0), line(0)
|
: bold(0), italic(0), symbol(0), soft(0), kwpph(0), param(0), line(0)
|
||||||
, code(0), code_kw(0), code_string(0), param_ref(0)
|
, code(0), code_kw(0), code_string(0), param_ref(0), error(0)
|
||||||
, param_id(0), bracket(false) {}
|
, param_id(0), bracket(false) {}
|
||||||
|
|
||||||
// read TextElements from a string
|
// read TextElements from a string
|
||||||
void fromString(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
|
void fromString(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
|
||||||
te.elements.clear();
|
te.elements.clear();
|
||||||
|
end = min(end, text.size());
|
||||||
// for each character...
|
// for each character...
|
||||||
for (size_t pos = start ; pos < end ; ) {
|
for (size_t pos = start ; pos < end ; ) {
|
||||||
Char c = text.GetChar(pos);
|
Char c = text.GetChar(pos);
|
||||||
@@ -126,11 +127,18 @@ struct TextElementsFromString {
|
|||||||
else if (is_substr(text, tag_start, _("</line"))) line -= 1;
|
else if (is_substr(text, tag_start, _("</line"))) line -= 1;
|
||||||
else if (is_substr(text, tag_start, _("<atom"))) {
|
else if (is_substr(text, tag_start, _("<atom"))) {
|
||||||
// 'atomic' indicator
|
// 'atomic' indicator
|
||||||
size_t end = match_close_tag(text, tag_start);
|
size_t end_tag = min(end, match_close_tag(text, tag_start));
|
||||||
shared_ptr<AtomTextElement> e(new AtomTextElement(text, pos, end));
|
shared_ptr<AtomTextElement> e(new AtomTextElement(text, pos, end_tag));
|
||||||
fromString(e->elements, text, pos, end, style, ctx);
|
fromString(e->elements, text, pos, end_tag, style, ctx);
|
||||||
te.elements.push_back(e);
|
te.elements.push_back(e);
|
||||||
pos = skip_tag(text, end);
|
pos = skip_tag(text, end_tag);
|
||||||
|
} else if (is_substr(text, tag_start, _( "<error"))) {
|
||||||
|
// error indicator
|
||||||
|
size_t end_tag = min(end, match_close_tag(text, tag_start));
|
||||||
|
shared_ptr<ErrorTextElement> e(new ErrorTextElement(text, pos, end_tag));
|
||||||
|
fromString(e->elements, text, pos, end_tag, style, ctx);
|
||||||
|
te.elements.push_back(e);
|
||||||
|
pos = skip_tag(text, end_tag);
|
||||||
} else {
|
} else {
|
||||||
// ignore other tags
|
// ignore other tags
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,13 @@ class AtomTextElement : public CompoundTextElement {
|
|||||||
virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A TextElement drawn using a red wavy underline
|
||||||
|
class ErrorTextElement : public CompoundTextElement {
|
||||||
|
public:
|
||||||
|
ErrorTextElement(const String& text, size_t start ,size_t end) : CompoundTextElement(text, start, end) {}
|
||||||
|
|
||||||
|
virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||||
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Other text elements
|
// ----------------------------------------------------------------------------- : Other text elements
|
||||||
/*
|
/*
|
||||||
|
|||||||
+85
-30
@@ -51,10 +51,11 @@ enum OpenBrace
|
|||||||
, BRACE_PAREN // (, [, {
|
, BRACE_PAREN // (, [, {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Iterator over a string, one token at a time
|
/// Iterator over a string, one token at a time.
|
||||||
|
/** Also stores errors found when tokenizing or parsing */
|
||||||
class TokenIterator {
|
class TokenIterator {
|
||||||
public:
|
public:
|
||||||
TokenIterator(const String& str, bool string_mode);
|
TokenIterator(const String& str, bool string_mode, vector<ScriptParseError>& errors);
|
||||||
|
|
||||||
/// Peek at the next token, doesn't move to the one after that
|
/// Peek at the next token, doesn't move to the one after that
|
||||||
/** Can peek further forward by using higher values of offset.
|
/** Can peek further forward by using higher values of offset.
|
||||||
@@ -87,6 +88,14 @@ class TokenIterator {
|
|||||||
void readToken();
|
void readToken();
|
||||||
/// Read the next token which is a string (after the opening ")
|
/// Read the next token which is a string (after the opening ")
|
||||||
void readStringToken();
|
void readStringToken();
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// All errors found
|
||||||
|
vector<ScriptParseError>& errors;
|
||||||
|
/// Add an error message
|
||||||
|
void add_error(const String& message);
|
||||||
|
/// Expected some token instead of what was found
|
||||||
|
void expected(const String& exp);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Characters
|
// ----------------------------------------------------------------------------- : Characters
|
||||||
@@ -102,10 +111,11 @@ bool isLongOper(const String& s) { return s==_(":=") || s==_("==") || s==_("!=")
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Tokenizing
|
// ----------------------------------------------------------------------------- : Tokenizing
|
||||||
|
|
||||||
TokenIterator::TokenIterator(const String& str, bool string_mode)
|
TokenIterator::TokenIterator(const String& str, bool string_mode, vector<ScriptParseError>& errors)
|
||||||
: input(str)
|
: input(str)
|
||||||
, pos(0)
|
, pos(0)
|
||||||
, newline(false)
|
, newline(false)
|
||||||
|
, errors(errors)
|
||||||
{
|
{
|
||||||
if (string_mode) {
|
if (string_mode) {
|
||||||
open_braces.push(BRACE_STRING_MODE);
|
open_braces.push(BRACE_STRING_MODE);
|
||||||
@@ -221,7 +231,8 @@ void TokenIterator::readToken() {
|
|||||||
// comment untill end of line
|
// comment untill end of line
|
||||||
while (pos < input.size() && input[pos] != _('\n')) ++pos;
|
while (pos < input.size() && input[pos] != _('\n')) ++pos;
|
||||||
} else {
|
} else {
|
||||||
throw ScriptParseError(_("Unknown character in script: '") + String(1,c) + _("'"));
|
add_error(_("Unknown character in script: '") + String(1,c) + _("'"));
|
||||||
|
// just skip the character
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +245,10 @@ void TokenIterator::readStringToken() {
|
|||||||
addToken(TOK_STRING, str);
|
addToken(TOK_STRING, str);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
throw ScriptParseError(_("Unexpected end of input in string constant"));
|
add_error(_("Unexpected end of input in string constant"));
|
||||||
|
// fix up
|
||||||
|
addToken(TOK_STRING, str);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Char c = input.GetChar(pos++);
|
Char c = input.GetChar(pos++);
|
||||||
@@ -246,7 +260,12 @@ void TokenIterator::readStringToken() {
|
|||||||
return;
|
return;
|
||||||
} else if (c == _('\\')) {
|
} else if (c == _('\\')) {
|
||||||
// escape
|
// escape
|
||||||
if (pos >= input.size()) throw ScriptParseError(_("Unexpected end of input in string constant"));
|
if (pos >= input.size()) {
|
||||||
|
add_error(_("Unexpected end of input in string constant"));
|
||||||
|
// fix up
|
||||||
|
addToken(TOK_STRING, str);
|
||||||
|
return;
|
||||||
|
}
|
||||||
c = input.GetChar(pos++);
|
c = input.GetChar(pos++);
|
||||||
if (c == _('n')) str += _('\n');
|
if (c == _('n')) str += _('\n');
|
||||||
else if (c == _('<')) str += _('\1'); // escape for <
|
else if (c == _('<')) str += _('\1'); // escape for <
|
||||||
@@ -264,8 +283,20 @@ void TokenIterator::readStringToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TokenIterator::add_error(const String& message) {
|
||||||
|
if (!errors.empty() && errors.back().start == pos) return; // already an error here
|
||||||
|
errors.push_back(ScriptParseError(pos, message));
|
||||||
|
}
|
||||||
|
void TokenIterator::expected(const String& expected) {
|
||||||
|
size_t error_pos = pos - peek(0).value.size();
|
||||||
|
if (!errors.empty() && errors.back().start == pos) return; // already an error here
|
||||||
|
errors.push_back(ScriptParseError(error_pos, expected, peek(0).value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Parsing
|
// ----------------------------------------------------------------------------- : Parsing
|
||||||
|
|
||||||
|
|
||||||
/// Precedence levels for parsing, higher = tighter
|
/// Precedence levels for parsing, higher = tighter
|
||||||
enum Precedence
|
enum Precedence
|
||||||
{ PREC_ALL
|
{ PREC_ALL
|
||||||
@@ -300,22 +331,43 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec);
|
|||||||
*/
|
*/
|
||||||
void parseOper(TokenIterator& input, Script& script, Precedence minPrec, InstructionType closeWith = I_NOP, int closeWithData = 0);
|
void parseOper(TokenIterator& input, Script& script, Precedence minPrec, InstructionType closeWith = I_NOP, int closeWithData = 0);
|
||||||
|
|
||||||
ScriptP parse(const String& s, bool string_mode) {
|
|
||||||
TokenIterator input(s, string_mode);
|
ScriptP parse(const String& s, bool string_mode, vector<ScriptParseError>& errors_out) {
|
||||||
|
errors_out.clear();
|
||||||
|
// parse
|
||||||
|
TokenIterator input(s, string_mode, errors_out);
|
||||||
ScriptP script(new Script);
|
ScriptP script(new Script);
|
||||||
parseOper(input, *script, PREC_ALL, I_RET);
|
parseOper(input, *script, PREC_ALL, I_RET);
|
||||||
if (input.peek() != TOK_EOF) {
|
Token eof = input.read();
|
||||||
throw ScriptParseError(_("end of input"), input.peek().value);
|
if (eof != TOK_EOF) {
|
||||||
} else {
|
input.expected(_("end of input"));
|
||||||
|
}
|
||||||
|
// were there errors?
|
||||||
|
if (errors_out.empty()) {
|
||||||
return script;
|
return script;
|
||||||
|
} else {
|
||||||
|
return ScriptP();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expect a token, throws if it is not found
|
ScriptP parse(const String& s, bool string_mode) {
|
||||||
void expectToken(TokenIterator& input, const Char* expect) {
|
vector<ScriptParseError> errors;
|
||||||
|
ScriptP script = parse(s, string_mode, errors);
|
||||||
|
if (!errors.empty()) {
|
||||||
|
throw ScriptParseErrors(errors);
|
||||||
|
}
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Expect a token, adds an error if it is not found
|
||||||
|
bool expectToken(TokenIterator& input, const Char* expect, const Char* name_in_error = nullptr) {
|
||||||
Token token = input.read();
|
Token token = input.read();
|
||||||
if (token != expect) {
|
if (token == expect) {
|
||||||
throw ScriptParseError(expect, token.value);
|
return true;
|
||||||
|
} else {
|
||||||
|
input.expected(name_in_error ? name_in_error : expect);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,7 +419,9 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
|
|||||||
// for each AAA in BBB do CCC
|
// for each AAA in BBB do CCC
|
||||||
input.read(); // each
|
input.read(); // each
|
||||||
Token name = input.read(); // AAA
|
Token name = input.read(); // AAA
|
||||||
if (name != TOK_NAME) throw ScriptParseError(_("name"), name.value);
|
if (name != TOK_NAME) {
|
||||||
|
input.expected(_("name"));
|
||||||
|
}
|
||||||
expectToken(input, _("in")); // in
|
expectToken(input, _("in")); // in
|
||||||
parseOper(input, script, PREC_SET); // BBB
|
parseOper(input, script, PREC_SET); // BBB
|
||||||
script.addInstruction(I_UNARY, I_ITERATOR_C); // iterator_collection
|
script.addInstruction(I_UNARY, I_ITERATOR_C); // iterator_collection
|
||||||
@@ -428,7 +482,8 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
|
|||||||
} else if (token == TOK_STRING) {
|
} else if (token == TOK_STRING) {
|
||||||
script.addInstruction(I_PUSH_CONST, to_script(token.value));
|
script.addInstruction(I_PUSH_CONST, to_script(token.value));
|
||||||
} else {
|
} else {
|
||||||
throw ScriptParseError(_("Unexpected token '") + token.value + _("'"));
|
input.expected(_("expression"));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -459,11 +514,10 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
|
|||||||
// not an expression. Remove that instruction.
|
// not an expression. Remove that instruction.
|
||||||
Instruction instr = script.getInstructions().back();
|
Instruction instr = script.getInstructions().back();
|
||||||
if (instr.instr != I_GET_VAR) {
|
if (instr.instr != I_GET_VAR) {
|
||||||
throw ScriptParseError(_("Can only assign to variables"));
|
input.add_error(_("Can only assign to variables"));
|
||||||
} else {
|
|
||||||
script.getInstructions().pop_back();
|
|
||||||
parseOper(input, script, PREC_SET, I_SET_VAR, instr.data);
|
|
||||||
}
|
}
|
||||||
|
script.getInstructions().pop_back();
|
||||||
|
parseOper(input, script, PREC_SET, I_SET_VAR, instr.data);
|
||||||
}
|
}
|
||||||
else if (minPrec <= PREC_AND && token==_("and")) parseOper(input, script, PREC_CMP, I_BINARY, I_AND);
|
else if (minPrec <= PREC_AND && token==_("and")) parseOper(input, script, PREC_CMP, I_BINARY, I_AND);
|
||||||
else if (minPrec <= PREC_AND && token==_("or" )) parseOper(input, script, PREC_CMP, I_BINARY, I_OR);
|
else if (minPrec <= PREC_AND && token==_("or" )) parseOper(input, script, PREC_CMP, I_BINARY, I_OR);
|
||||||
@@ -484,7 +538,7 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
|
|||||||
if (token == TOK_NAME || token == TOK_INT || token == TOK_DOUBLE || token == TOK_STRING) {
|
if (token == TOK_NAME || token == TOK_INT || token == TOK_DOUBLE || token == TOK_STRING) {
|
||||||
script.addInstruction(I_MEMBER_C, token.value);
|
script.addInstruction(I_MEMBER_C, token.value);
|
||||||
} else {
|
} else {
|
||||||
throw ScriptParseError(_("name"), input.peek().value);
|
input.expected(_("name"));
|
||||||
}
|
}
|
||||||
} else if (minPrec <= PREC_FUN && token==_("[")) { // get member by expr
|
} else if (minPrec <= PREC_FUN && token==_("[")) { // get member by expr
|
||||||
parseOper(input, script, PREC_ALL, I_BINARY, I_MEMBER);
|
parseOper(input, script, PREC_ALL, I_BINARY, I_MEMBER);
|
||||||
@@ -528,14 +582,15 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
|
|||||||
} else {
|
} else {
|
||||||
parseOper(input, script, PREC_ALL, I_BINARY, I_ADD); // e
|
parseOper(input, script, PREC_ALL, I_BINARY, I_ADD); // e
|
||||||
}
|
}
|
||||||
expectToken(input, _("}\""));
|
if (expectToken(input, _("}\""), _("}"))) {
|
||||||
parseOper(input, script, PREC_NONE); // y
|
parseOper(input, script, PREC_NONE); // y
|
||||||
// optimize: e + "" -> e
|
// optimize: e + "" -> e
|
||||||
i = script.getInstructions().back();
|
i = script.getInstructions().back();
|
||||||
if (i.instr == I_PUSH_CONST && script.getConstants()[i.data]->toString().empty()) {
|
if (i.instr == I_PUSH_CONST && script.getConstants()[i.data]->toString().empty()) {
|
||||||
script.getInstructions().pop_back();
|
script.getInstructions().pop_back();
|
||||||
} else {
|
} else {
|
||||||
script.addInstruction(I_BINARY, I_ADD);
|
script.addInstruction(I_BINARY, I_ADD);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (minPrec <= PREC_NEWLINE && token.newline) {
|
} else if (minPrec <= PREC_NEWLINE && token.newline) {
|
||||||
// newline functions as ;
|
// newline functions as ;
|
||||||
|
|||||||
+13
-1
@@ -10,13 +10,25 @@
|
|||||||
// ----------------------------------------------------------------------------- : Includes
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
#include <util/prec.hpp>
|
#include <util/prec.hpp>
|
||||||
|
#include <util/error.hpp>
|
||||||
#include <script/script.hpp>
|
#include <script/script.hpp>
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Parser
|
// ----------------------------------------------------------------------------- : Parser
|
||||||
|
|
||||||
/// Parse a String to a Script
|
/// Parse a String to a Script
|
||||||
/** If string_mode then s is interpreted as a string,
|
/** If string_mode then s is interpreted as a string,
|
||||||
* escaping to script mode can be done with {}
|
* escaping to script mode can be done with {}.
|
||||||
|
*
|
||||||
|
* Errors are stored in the output vector.
|
||||||
|
* If there are errors, the result is a null pointer
|
||||||
|
*/
|
||||||
|
ScriptP parse(const String& s, bool string_mode, vector<ScriptParseError>& errors_out);
|
||||||
|
|
||||||
|
/// Parse a String to a Script
|
||||||
|
/** If string_mode then s is interpreted as a string,
|
||||||
|
* escaping to script mode can be done with {}.
|
||||||
|
*
|
||||||
|
* If an error is encountered, an exception is thrown.
|
||||||
*/
|
*/
|
||||||
ScriptP parse(const String& s, bool string_mode = false);
|
ScriptP parse(const String& s, bool string_mode = false);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include <util/error.hpp>
|
#include <util/error.hpp>
|
||||||
|
|
||||||
|
DECLARE_TYPEOF_COLLECTION(ScriptParseError);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Error types
|
// ----------------------------------------------------------------------------- : Error types
|
||||||
|
|
||||||
Error::Error(const String& message)
|
Error::Error(const String& message)
|
||||||
@@ -20,6 +22,32 @@ String Error::what() const {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Parse errors
|
||||||
|
|
||||||
|
ScriptParseError::ScriptParseError(size_t pos, const String& error)
|
||||||
|
: start(pos), end(pos)
|
||||||
|
, ParseError(error)
|
||||||
|
{}
|
||||||
|
ScriptParseError::ScriptParseError(size_t pos, const String& exp, const String& found)
|
||||||
|
: start(pos), end(pos + found.size())
|
||||||
|
, ParseError(_("Expected '") + exp + _("' instead of '") + found + _("'"))
|
||||||
|
{}
|
||||||
|
String ScriptParseError::what() const {
|
||||||
|
return String(_("(")) << (int)start << _("): ") << Error::what();
|
||||||
|
}
|
||||||
|
|
||||||
|
String concat(const vector<ScriptParseError>& errors) {
|
||||||
|
String total;
|
||||||
|
FOR_EACH_CONST(e, errors) {
|
||||||
|
if (!total.empty()) total += _("\n");
|
||||||
|
total += e.what();
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
ScriptParseErrors::ScriptParseErrors(const vector<ScriptParseError>& errors)
|
||||||
|
: ParseError(concat(errors))
|
||||||
|
{}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Error handling
|
// ----------------------------------------------------------------------------- : Error handling
|
||||||
|
|
||||||
// Errors for which a message box was already shown
|
// Errors for which a message box was already shown
|
||||||
|
|||||||
+12
-3
@@ -75,9 +75,18 @@ class FileParseError : public ParseError {
|
|||||||
/// Parse error in a script
|
/// Parse error in a script
|
||||||
class ScriptParseError : public ParseError {
|
class ScriptParseError : public ParseError {
|
||||||
public:
|
public:
|
||||||
inline ScriptParseError(const String& str) : ParseError(str) {}
|
ScriptParseError(size_t pos, const String& str);
|
||||||
inline ScriptParseError(const String& exp, const String& found)
|
ScriptParseError(size_t pos, const String& expected, const String& found);
|
||||||
: ParseError(_("Expected '") + exp + _("' instead of '") + found + _("'")) {}
|
/// Position of the error
|
||||||
|
size_t start, end;
|
||||||
|
/// Return the error message
|
||||||
|
virtual String what() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Multiple parse errors in a script
|
||||||
|
class ScriptParseErrors : public ParseError {
|
||||||
|
public:
|
||||||
|
ScriptParseErrors(const vector<ScriptParseError>& errors);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Script errors
|
// ----------------------------------------------------------------------------- : Script errors
|
||||||
|
|||||||
Reference in New Issue
Block a user