Add detailed parse errors
diff --git a/Parser.cpp b/Parser.cpp
index 145972f..9b8f264 100644
--- a/Parser.cpp
+++ b/Parser.cpp
@@ -2,6 +2,41 @@
#include <QDebug>
+ParseResult::ParseResult(bool okay)
+ : ParseResult((int)okay)
+{}
+
+ParseResult::ParseResult(int status, ParsePos pos)
+ : ParseResult(status, "", pos)
+{}
+
+ParseResult::ParseResult(int status, QString message, ParsePos pos)
+{
+ _status = status;
+ _message = message;
+ _pos = pos;
+}
+
+ParseResult::operator bool() const
+{
+ return _status == COMPLETE;
+}
+
+ParsePos ParseResult::pos() const
+{
+ return _pos;
+}
+
+QString ParseResult::message() const
+{
+ return _message;
+}
+
+int ParseResult::status() const
+{
+ return _status;
+}
+
Parser::Parser(QString input)
{
_input = input;
@@ -17,7 +52,19 @@
QChar Parser::get()
{
- return _input[_pos++];
+ QChar c = _input[_pos++];
+
+ if (c == '\n')
+ {
+ _line++;
+ _offset = 0;
+ }
+ else
+ {
+ _offset++;
+ }
+
+ return c;
}
bool Parser::atEnd()
@@ -31,8 +78,20 @@
get();
}
+ParsePos Parser::save() const
+{
+ return ParsePos{_line, _pos, _offset};
+}
+
+void Parser::reset(ParsePos pos)
+{
+ _line = pos.line;
+ _pos = pos.pos;
+ _offset = pos.lineOffset;
+}
+
template <typename T>
-bool Parser::parseSymbol(T *node)
+ParseResult Parser::parseSymbol(T *node)
{
skip();
@@ -46,7 +105,7 @@
}
template <typename T>
-bool Parser::parseIdentifier(T *node)
+ParseResult Parser::parseIdentifier(T *node)
{
skip();
@@ -67,7 +126,7 @@
}
template <typename T>
-bool Parser::parseNumber(T *node)
+ParseResult Parser::parseNumber(T *node)
{
skip();
@@ -86,11 +145,11 @@
}
template <typename T>
-bool Parser::parseVariable(T *node)
+ParseResult Parser::parseVariable(T *node)
{
skip();
- int pos = _pos;
+ ParsePos pos = save();
if (peek() == 's' || peek() == 'e' || peek() == 't')
{
@@ -112,47 +171,68 @@
*node = T(type, QString(nameNode.symbol()));
return true;
}
+ else
+ {
+ ParseResult ret(ParseResult::INCOMPLETE,
+ "Expected identifier or symbol after . in variable",
+ pos);
+
+ reset(pos);
+
+ return ret;
+ }
}
}
- _pos = pos;
+ reset(pos);
return false;
}
template <typename T>
-QList<T> Parser::parseMany()
+ParseResult Parser::parseMany(QList<T> *list)
{
- QList<T > nodes;
+ QList<T> nodes;
T next;
+ ParseResult ret;
- while (parseOne(&next))
+ while ((ret = parseOne(&next)))
{
+ // qDebug() << "parseMany one" << next;
nodes.append(next);
}
- return nodes;
+ *list = nodes;
+
+ if (ret.status() == ParseResult::INCOMPLETE)
+ return ret;
+ else
+ return true;
}
template <typename T>
-bool Parser::parseParens(T *node)
+ParseResult Parser::parseParens(T *node)
{
skip();
- int pos = _pos;
+ ParsePos pos = save();
if (peek() != '(')
return false;
get();
- QList<T> many = parseMany<T>();
+ QList<T> many;
+ ParseResult ret = parseMany(&many);
+
*node = T(many);
skip();
if (peek() != ')')
{
- _pos = pos;
- return false;
+ ret = ParseResult(ParseResult::INCOMPLETE, "Expected ) in parenthesized list", save());
+ reset(pos);
+
+ return ret;
}
get();
@@ -160,11 +240,12 @@
return true;
}
-bool Parser::parseFunction(AstNode *node)
+ParseResult Parser::parseFunction(AstNode *node)
{
skip();
- int pos = _pos;
+ ParsePos pos = save();
+ ParseResult ret;
if (peek() != '<')
return false;
@@ -172,20 +253,29 @@
get();
AstNode head;
- if (!parseIdentifier(&head))
+ if (!(ret = parseIdentifier(&head)))
{
- _pos = pos;
- return false;
+ reset(pos);
+ return ret;
}
- QList<AstNode> body = parseMany<AstNode>();
+ QList<AstNode> body;
+ ret = parseMany(&body);
+
+ if (!ret)
+ {
+ reset(pos);
+ return ret;
+ }
+
*node = AstNode(head.name(), body);
skip();
if (peek() != '>')
{
- _pos = pos;
- return false;
+ ret = ParseResult(ParseResult::INCOMPLETE, "Expected >", save());
+ reset(pos);
+ return ret;
}
get();
@@ -194,7 +284,7 @@
}
template <>
-bool Parser::parseOne<Token>(Token *node)
+ParseResult Parser::parseOne<Token>(Token *node)
{
return parseVariable(node) ||
parseNumber(node) ||
@@ -204,7 +294,7 @@
}
template <>
-bool Parser::parseOne<AstNode>(AstNode *node)
+ParseResult Parser::parseOne<AstNode>(AstNode *node)
{
return parseFunction(node) ||
parseVariable(node) ||
@@ -214,43 +304,74 @@
parseParens(node);
}
-bool Parser::parseSentence(Sentence *sentence)
+ParseResult Parser::parseSentence(Sentence *sentence)
{
- int pos = _pos;
+ ParsePos pos = save();
- QList<Token> pattern = parseMany<Token>();
+ qDebug() << "Parsing sentence" << peek();
+
+ if (peek() == '}')
+ {
+ return false;
+ }
+
+ QList<Token> pattern;
+ ParseResult ret = parseMany(&pattern);
+
+ if (!ret)
+ {
+ qDebug() << "Many failed" << ret.message();
+ reset(pos);
+ return ret;
+ }
skip();
+ qDebug() << "will we get an =?" << peek();
+
if (get() != '=')
{
- _pos = pos;
- return false;
+ ret = ParseResult(ParseResult::INCOMPLETE, "Expected = in sentence", save());
+ reset(pos);
+ return ret;
}
- QList<AstNode> result = parseMany<AstNode>();
+ QList<AstNode> result;
+ ret = parseMany(&result);
+
+ if (!ret)
+ {
+ qDebug() << "sentence parseMany returned" << ret.message();
+ reset(pos);
+ return ret;
+ }
skip();
- if (get() != ';')
+ qDebug() << "end of sentence" << peek();
+
+ if (peek() != '}' && get() != ';')
{
- _pos = pos;
- return false;
+ ret = ParseResult(ParseResult::INCOMPLETE, "Expected ; or } after sentence", save());
+ reset(pos);
+ return ret;
}
*sentence = Sentence(pattern, result);
+
return true;
}
-bool Parser::parseFunctionDefinition(Function *function)
+ParseResult Parser::parseFunctionDefinition(Function *function)
{
- int pos = _pos;
+ ParsePos pos = save();
+ ParseResult ret;
Token identifier;
- if (!parseIdentifier(&identifier))
+ if (!(ret = parseIdentifier(&identifier)))
{
- _pos = pos;
- return false;
+ reset(pos);
+ return ret;
}
QString name = identifier.name();
@@ -260,23 +381,33 @@
if (get() != '{')
{
- _pos = pos;
- return false;
+ reset(pos);
+ return false;
}
Sentence sentence;
- while (parseSentence(&sentence))
+ while ((ret = parseSentence(&sentence)))
{
func.addSentence(sentence);
skip();
}
+ if (ret.status() == ParseResult::INCOMPLETE)
+ {
+ qDebug() << "Function incomplete";
+ reset(pos);
+ return ret;
+ }
+
if (get() != '}')
{
- _pos = pos;
- return false;
+ ret = ParseResult(ParseResult::INCOMPLETE, "Expected } at end of function");
+ reset(pos);
+ return ret;
}
+ qDebug() << "Function parsing succeeded";
+
*function = func;
return true;
}
diff --git a/Parser.h b/Parser.h
index 1426989..f65e787 100644
--- a/Parser.h
+++ b/Parser.h
@@ -6,6 +6,40 @@
#include "AstNode.h"
#include "Function.h"
+struct ParsePos
+{
+ int line = 0,
+ pos = 0,
+ lineOffset = 0;
+};
+
+class ParseResult
+{
+public:
+ ParseResult() = default;
+ ParseResult(bool okay);
+ explicit ParseResult(int status, ParsePos pos = {});
+ ParseResult(int status, QString message, ParsePos pos = {});
+
+ enum
+ {
+ NO_MATCH,
+ COMPLETE,
+ INCOMPLETE,
+ };
+
+ operator bool() const;
+
+ ParsePos pos() const;
+ QString message() const;
+ int status() const;
+
+private:
+ int _status = COMPLETE;
+ QString _message = "";
+ ParsePos _pos;
+};
+
class Parser
{
public:
@@ -18,38 +52,44 @@
void skip();
template <typename T>
- bool parseSymbol(T *node);
+ ParseResult parseSymbol(T *node);
template <typename T>
- bool parseIdentifier(T *node);
+ ParseResult parseIdentifier(T *node);
template <typename T>
- bool parseNumber(T *node);
+ ParseResult parseNumber(T *node);
template <typename T>
- bool parseVariable(T *node);
+ ParseResult parseVariable(T *node);
template <typename T>
- bool parseParens(T *node);
+ ParseResult parseParens(T *node);
- bool parseFunction(AstNode *node);
+ ParseResult parseFunction(AstNode *node);
template <typename T>
- QList<T> parseMany();
+ ParseResult parseMany(QList<T> *list);
template <typename T>
- bool parseOne(T *node);
+ ParseResult parseOne(T *node);
- bool parseSentence(Sentence *sentence);
- bool parseFunctionDefinition(Function *function);
+ ParseResult parseSentence(Sentence *sentence);
+ ParseResult parseFunctionDefinition(Function *function);
+
+ ParsePos save() const;
+ void reset(ParsePos pos);
private:
int _pos = 0;
+ int _line = 1;
+ int _offset = 1;
+
QString _input;
};
template <>
-bool Parser::parseOne<Token>(Token *node);
+ParseResult Parser::parseOne<Token>(Token *node);
template <>
-bool Parser::parseOne<AstNode>(AstNode *node);
+ParseResult Parser::parseOne<AstNode>(AstNode *node);
diff --git a/Repl.cpp b/Repl.cpp
index 6137c19..0bddf3d 100644
--- a/Repl.cpp
+++ b/Repl.cpp
@@ -127,7 +127,9 @@
return true;
}
- *expr = parser.parseMany<AstNode>();
+ if (!parser.parseMany(expr))
+ return false;
+
parser.skip();
return parser.atEnd();
diff --git a/main.cpp b/main.cpp
index 12f6dea..8e29b11 100644
--- a/main.cpp
+++ b/main.cpp
@@ -23,26 +23,31 @@
Function func;
- QList<AstNode> expr = exprParser.parseMany<AstNode>();
- QList<Token> res = resParser.parseMany<Token>();
+ QList<AstNode> expr;
+ ParseResult exprRet = exprParser.parseMany<AstNode>(&expr);
+ QList<Token> res;
+ ParseResult resRet = resParser.parseMany<Token>(&res);
+ ParseResult funcRet;
QList<Token> result;
exprParser.skip();
resParser.skip();
- while (funcParser.parseFunctionDefinition(&func))
+ while ((funcRet = funcParser.parseFunctionDefinition(&func)))
{
eval.addFunction(func);
}
funcParser.skip();
- if (!exprParser.atEnd() || !resParser.atEnd() || !funcParser.atEnd())
+ if (!exprRet || !resRet || funcRet.status() == ParseResult::INCOMPLETE)
{
g_numFailed++;
qDebug() << "\n\033[31mTEST FAILS:\033[0m";
qDebug() << "Failed to fully parse expression, function or result";
qDebug() << function << expression << expected;
+ qDebug() << funcRet.message() << exprRet.message() << resRet.message();
+ qDebug() << funcRet << exprRet << resRet;
goto end;
}
@@ -103,15 +108,21 @@
Parser dataParser(data),
patternParser(pattern);
+ QList<Token> parsedData, parsedPattern;
+
+ dataParser.parseMany<Token>(&parsedData);
+ patternParser.parseMany<Token>(&parsedPattern);
+
testMatch(pattern + " = " + data, shouldBe,
- match(dataParser.parseMany<Token>(), patternParser.parseMany<Token>(), VarContext()));
+ match(parsedData, parsedPattern, VarContext()));
}
void testParseAst(QString string, QList<AstNode> equals = {})
{
Parser parser{string};
- QList<AstNode> result = parser.parseMany<AstNode>();
+ QList<AstNode> result;
+ parser.parseMany<AstNode>(&result);
if (!equals.empty() && result != equals)
{
@@ -126,13 +137,15 @@
void testParseFunc(QString string)
{
Parser parser{string};
+ ParseResult ret;
Function func;
- if (!parser.parseFunctionDefinition(&func))
+ if (!(ret = parser.parseFunctionDefinition(&func)))
{
g_numFailed++;
qDebug() << "\n\033[31mTEST FAILS:\033[0m";
+ qDebug() << ret.message();
qDebug() << string;
}
else