Add standard stack manipulation functions
diff --git a/AstNode.h b/AstNode.h
index b10872a..1cf66d4 100644
--- a/AstNode.h
+++ b/AstNode.h
@@ -7,7 +7,7 @@
public:
using TokenBase::TokenBase;
- explicit AstNode(QString function, QList<AstNode> args);
+ AstNode(QString function, QList<AstNode> args);
enum
{
diff --git a/Evaluator.cpp b/Evaluator.cpp
index b52b843..5177fe5 100644
--- a/Evaluator.cpp
+++ b/Evaluator.cpp
@@ -2,6 +2,7 @@
#include "Function.h"
#include "Matcher.h"
#include "VarContext.h"
+#include "PPrint.h"
#include <QDebug>
@@ -38,6 +39,40 @@
Evaluator::Evaluator()
{
+ Function buryFn("Br");
+ buryFn.addNativeSentence("s.Name '=' e.Expr", [this](VarContext args)
+ {
+ Token name = args.singleVar("Name");
+ if (name.type() != Token::IDENT)
+ rtError("Invalid argument", "First argument to <Br> must be an identifier, received " + pprint(name));
+
+ bury(name.name(), args.expressionVar("Expr"));
+
+ return QList<Token>();
+ });
+ addFunction(buryFn);
+
+ Function digFn("Dg");
+ digFn.addNativeSentence("s.Name", [this](VarContext args)
+ {
+ Token name = args.singleVar("Name");
+ if (name.type() != Token::IDENT)
+ rtError("Invalid argument", "First argument to <Dg> must be an identifier, received " + pprint(name));
+
+ return dig(name.name());
+ });
+ addFunction(digFn);
+
+ Function copyFn("Cp");
+ copyFn.addNativeSentence("s.Name", [this](VarContext args)
+ {
+ Token name = args.singleVar("Name");
+ if (name.type() != Token::IDENT)
+ rtError("Invalid argument", "First argument to <Cp> must be an identifier, received " + pprint(name));
+
+ return copy(name.name());
+ });
+ addFunction(copyFn);
}
void Evaluator::addFunction(Function func)
@@ -132,7 +167,7 @@
if (sentence.isExternal())
{
- return RuntimeResult(sentence.externResult(args));
+ return RuntimeResult(sentence.externResult(res));
}
QList<Token> final;
@@ -148,5 +183,52 @@
return RuntimeResult(final);
}
- return RuntimeResult("Function " + name + " had no matching sentences for input");
+ return RuntimeResult("Function " + name + " had no matching sentences for input");
+}
+
+QList<Token> Evaluator::dig(QString name)
+{
+ if (_vars.contains(name))
+ {
+ QList<Token> value = _vars[name].pop();
+
+ if (_vars[name].empty())
+ {
+ _vars.remove(name);
+ }
+
+ return value;
+ }
+ else
+ {
+ return {};
+ }
+}
+
+QList<Token> Evaluator::copy(QString name)
+{
+ if (_vars.contains(name))
+ {
+ return _vars[name].last();
+ }
+ else
+ {
+ return {};
+ }
+}
+
+void Evaluator::bury(QString name, QList<Token> expression)
+{
+ if (!_vars.contains(name))
+ {
+ _vars[name] = QStack<QList<Token>>();
+ }
+
+ _vars[name].push(expression);
+}
+
+void rtError(QString brief, QString details)
+{
+ eout("Runtime Error: " + brief);
+ eout(details);
}
diff --git a/Evaluator.h b/Evaluator.h
index 5cc7851..afda18f 100644
--- a/Evaluator.h
+++ b/Evaluator.h
@@ -1,6 +1,7 @@
#pragma once
#include <QMap>
+#include <QStack>
#include "Token.h"
#include "AstNode.h"
@@ -36,4 +37,12 @@
private:
QMap<QString, Function> _functions;
+ QMap<QString, QStack<QList<Token>>> _vars;
+
+protected:
+ QList<Token> dig(QString name);
+ QList<Token> copy(QString name);
+ void bury(QString name, QList<Token> expression);
};
+
+void rtError(QString brief, QString details);
diff --git a/Function.cpp b/Function.cpp
index 39ac957..f28841f 100644
--- a/Function.cpp
+++ b/Function.cpp
@@ -36,9 +36,9 @@
return _native != nullptr;
}
-QList<Token> Sentence::externResult(QList<Token> args) const
+QList<Token> Sentence::externResult(MatchResult args) const
{
- return _native(std::move(args));
+ return _native(args.context);
}
QList<Token> Sentence::pattern() const
diff --git a/Function.h b/Function.h
index 2cb862f..ca90113 100644
--- a/Function.h
+++ b/Function.h
@@ -2,8 +2,10 @@
#include "Token.h"
#include "AstNode.h"
+#include "VarContext.h"
+#include "Matcher.h"
-using SentenceResultFn = std::function<QList<Token> (QList<Token>)>;
+using SentenceResultFn = std::function<QList<Token> (VarContext)>;
class Sentence {
public:
@@ -13,7 +15,7 @@
Sentence(QList<Token> pattern, SentenceResultFn result);
bool isExternal() const;
- QList<Token> externResult(QList<Token> args) const;
+ QList<Token> externResult(MatchResult args) const;
QList<Token> pattern() const;
QList<AstNode> result() const;
diff --git a/PPrint.cpp b/PPrint.cpp
index 986c38c..de43582 100644
--- a/PPrint.cpp
+++ b/PPrint.cpp
@@ -24,3 +24,8 @@
return val.message() + " at " + val.pos() + "\n" + highlighted;
}
+
+void eout(QString string)
+{
+ QTextStream(stderr) << string << '\n';
+}
diff --git a/PPrint.h b/PPrint.h
index 5212334..6298d6b 100644
--- a/PPrint.h
+++ b/PPrint.h
@@ -10,11 +10,11 @@
template <typename T>
QString pprint(T val);
-template <>
-QString pprint<Token>(Token val);
+//template <>
+//QString pprint<Token>(Token val);
-template <>
-QString pprint<AstNode>(AstNode val);
+//template <>
+//QString pprint<AstNode>(AstNode val);
template <typename T>
QString pprint(QList<T> val)
@@ -36,3 +36,4 @@
QString pprint(ParseResult val, const Parser &parser);
void sout(QString string);
+void eout(QString string);
diff --git a/Parser.cpp b/Parser.cpp
index 41f6727..6ce62fa 100644
--- a/Parser.cpp
+++ b/Parser.cpp
@@ -196,19 +196,27 @@
template <typename T>
ParseResult Parser::parseMany(QList<T> *list)
{
- QList<T> nodes;
+ QList<T> nodes, string;
T next;
- ParseResult ret;
+ ParseResult ret, stringRet;
- while ((ret = parseOne(&next)))
+ while ((ret = parseOne(&next)) || (stringRet = parseString(&string)))
{
- nodes.append(next);
+ if (ret)
+ nodes.append(next);
+ else if (stringRet)
+ nodes.append(string);
+
+ // So that we can check if anything was incomplete recently at the end
+ ret = stringRet = false;
}
*list = nodes;
if (ret.status() == ParseResult::INCOMPLETE)
return ret;
+ else if (stringRet.status() == ParseResult::INCOMPLETE)
+ return ret;
else
return true;
}
@@ -423,3 +431,47 @@
{
return QString::number(line) + ":" + QString::number(lineOffset);
}
+
+template <typename T>
+ParseResult Parser::parseString(QList<T> *list)
+{
+ skip();
+
+ ParsePos pos = save();
+
+ if (peek() != '\'')
+ return false;
+
+ get();
+
+ list->clear();
+
+ while (peek() != 0 && peek() != '\'')
+ {
+ QChar c = get();
+ if (c == '\\')
+ {
+ QChar next = get();
+ QString conversions = "''n\nt\tr\r";
+
+ for (int i = 0; i < conversions.size(); i += 2)
+ {
+ if (next == conversions[i])
+ list->append(T(conversions[i + 1]));
+ }
+ }
+ else
+ {
+ list->append(T(c));
+ }
+ }
+
+ if (get() == 0)
+ {
+ ParseResult ret(ParseResult::INCOMPLETE, "Expected ' before end of input", save());
+ reset(pos);
+ return ret;
+ }
+
+ return true;
+}
diff --git a/Parser.h b/Parser.h
index 5d87a2b..102a3c3 100644
--- a/Parser.h
+++ b/Parser.h
@@ -76,6 +76,9 @@
ParseResult parseMany(QList<T> *list);
template <typename T>
+ ParseResult parseString(QList<T> *list);
+
+ template <typename T>
ParseResult parseOne(T *node);
ParseResult parseSentence(Sentence *sentence);
diff --git a/StdLib.cpp b/StdLib.cpp
index 0ad7887..5d6bf6a 100644
--- a/StdLib.cpp
+++ b/StdLib.cpp
@@ -3,15 +3,17 @@
StdLib::StdLib()
{
- _print.addNativeSentence("e.Expr", [](QList<Token> args)
+ _print.addNativeSentence("e.Expr", [](VarContext args)
{
- sout(pprint(args));
- return args;
+ auto expr = args.expressionVar("Expr");
+ sout(pprint(args.expressionVar("Expr")));
+ return expr;
});
- _prout.addNativeSentence("e.Expr", [](QList<Token> args)
+ _prout.addNativeSentence("e.Expr", [](VarContext args)
{
- sout(pprint(std::move(args)));
+ auto expr = args.expressionVar("Expr");
+ sout(pprint(expr));
return QList<Token>();
});
}
diff --git a/VarContext.h b/VarContext.h
index 5e580b3..6e5ea13 100644
--- a/VarContext.h
+++ b/VarContext.h
@@ -4,7 +4,8 @@
#include <QDebug>
#include "Token.h"
-class VarContext {
+class VarContext
+{
public:
VarContext() = default;
VarContext(VarContext const &other) noexcept;
@@ -19,7 +20,8 @@
friend QDebug &operator <<(QDebug &debug, const VarContext &ctx);
private:
- struct Var {
+ struct Var
+ {
Var() = default;
char t = 0;
@@ -30,4 +32,4 @@
QMap<QString, Var> _vars;
};
-QDebug &operator <<(QDebug &debug, const VarContext &ctx);
\ No newline at end of file
+QDebug &operator <<(QDebug &debug, const VarContext &ctx);
diff --git a/main.cpp b/main.cpp
index ce9f435..7f1a3ad 100644
--- a/main.cpp
+++ b/main.cpp
@@ -83,7 +83,7 @@
}
end:
- qDebug() << "\033[36mEvaluate\033[0m" << function << expression << "->" << result;
+ qDebug() << "\033[36mEvaluate\033[0m" << function << expression << "->" << pprint(result);
}
void testEval(QString function, QString expression, QList<Token> expected)
@@ -91,6 +91,11 @@
testEval(function, expression, pprint(expected));
}
+void testEval(QString expression, QString expected)
+{
+ testEval("", expression, expected);
+}
+
void testMatch(const QString &test, bool shouldBe, const MatchResult &result)
{
if (result.success != shouldBe)
@@ -236,6 +241,8 @@
testParseAst("123", {AstNode("123", 10)});
testParseAst("12 00", {AstNode::fromInteger(12), AstNode::fromInteger(0)});
testParseAst("s.A s.B", {AstNode('s', "A"), AstNode('s', "B")});
+ testParseAst("a '=' b", {AstNode('a'), AstNode('='), AstNode('b')});
+ testParseAst("'This\\'<>#$@#^%\\n' 'another $3543543'");
}
void testAllFunctionDefs()
@@ -248,6 +255,8 @@
{
testEval("First {s.A e.Rest = s.A;}", "<First hello>", "h");
testEval("Number { = 123; }", "<Number>", QList<Token>{Token::fromInteger(123)});
+ testEval("<Br Name '=' Jim> <Dg Name>", "Jim");
+ testEval("<Br A '=' hello> <Br A '=' 'good bye'> <Dg A> <Dg A>", "'good byehello'");
}
int main(int argc, char *argv[])