blob: 9a276f0a42e91177184a8ae9f32410284d9b3e2c [file] [log] [blame]
swissChili7babd922021-12-02 22:46:48 -08001#include <QCoreApplication>
swissChili923bd532021-12-08 22:48:58 -08002#include <QCommandLineParser>
swissChili7babd922021-12-02 22:46:48 -08003#include <QDebug>
4
swissChili3e98c062021-12-04 22:07:38 -08005#include "Matcher.h"
6#include "Token.h"
swissChilic71acc62021-12-07 08:03:37 -08007#include "AstNode.h"
8#include "Parser.h"
swissChili07d325f2021-12-08 20:02:05 -08009#include "Evaluator.h"
10#include "VarContext.h"
swissChili923bd532021-12-08 22:48:58 -080011#include "Repl.h"
swissChili9dddbf72021-12-08 23:03:25 -080012#include "PPrint.h"
swissChili3e98c062021-12-04 22:07:38 -080013
14int g_numFailed = 0;
15
swissChili07d325f2021-12-08 20:02:05 -080016void testEval(QString function, QString expression, QString expected)
17{
18 Evaluator eval;
19 Parser funcParser(function),
20 exprParser(expression),
21 resParser(expected);
22
23 Function func;
24
25 QList<AstNode> expr = exprParser.parseMany<AstNode>();
26 QList<Token> res = resParser.parseMany<Token>();
27
28 QList<Token> result;
29
30 exprParser.skip();
31 resParser.skip();
32 while (funcParser.parseFunctionDefinition(&func))
33 {
34 eval.addFunction(func);
35 }
36
37 funcParser.skip();
38
39 if (!exprParser.atEnd() || !resParser.atEnd() || !funcParser.atEnd())
40 {
41 g_numFailed++;
42 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
43 qDebug() << "Failed to fully parse expression, function or result";
44 qDebug() << function << expression << expected;
45
46 goto end;
47 }
48
49 for (const AstNode &node : expr)
50 {
51 RuntimeResult rr = eval.evaluate(node, VarContext());
52
53 if (!rr.success())
54 {
55 g_numFailed++;
56 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
57 qDebug() << "Runtime error while evaluating" << node;
58 qDebug() << rr;
59
60 goto end;
61 }
62
63 result.append(rr.result());
64 }
65
66 if (result != res)
67 {
68 g_numFailed++;
69 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
70 qDebug() << "Expected result" << res;
71 qDebug() << "Got" << result;
72 }
73
74end:
75 qDebug() << "\033[36mEvaluate\033[0m" << function << expression << "->" << result;
76}
77
swissChilic71acc62021-12-07 08:03:37 -080078void testMatch(const QString &test, bool shouldBe, const MatchResult &result)
79{
80 if (result.success != shouldBe)
81 {
swissChili3e98c062021-12-04 22:07:38 -080082 g_numFailed++;
swissChilic71acc62021-12-07 08:03:37 -080083 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChilid17b5a12021-12-05 20:46:42 -080084 qDebug() << "with context" << result.context;
swissChili3e98c062021-12-04 22:07:38 -080085 }
86
swissChilic71acc62021-12-07 08:03:37 -080087 qDebug() << "\033[36mMatchResult\033[0m" << test << result.success;
swissChilid17b5a12021-12-05 20:46:42 -080088
swissChilic71acc62021-12-07 08:03:37 -080089 if (result.success != shouldBe)
90 {
swissChilid17b5a12021-12-05 20:46:42 -080091 qDebug() << "";
92 }
swissChili3e98c062021-12-04 22:07:38 -080093}
94
swissChili682e7bc2021-12-07 09:04:54 -080095void testMatch(QString data, QString pattern, bool shouldBe = true)
96{
97 Parser dataParser(data),
98 patternParser(pattern);
99
swissChili07d325f2021-12-08 20:02:05 -0800100 testMatch(pattern + " = " + data, shouldBe,
101 match(dataParser.parseMany<Token>(), patternParser.parseMany<Token>(), VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800102}
103
swissChili9dddbf72021-12-08 23:03:25 -0800104void testParseAst(QString string, QList<AstNode> equals = {})
swissChilic71acc62021-12-07 08:03:37 -0800105{
106 Parser parser{string};
107
swissChili682e7bc2021-12-07 09:04:54 -0800108 QList<AstNode> result = parser.parseMany<AstNode>();
swissChilic71acc62021-12-07 08:03:37 -0800109
swissChili9dddbf72021-12-08 23:03:25 -0800110 if (!equals.empty() && result != equals)
111 {
112 g_numFailed++;
113 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
114 qDebug() << "Expected" << pprint(equals);
115 }
116
117 qDebug() << "\033[36mParse\033[0m" << string << pprint(result);
swissChilic71acc62021-12-07 08:03:37 -0800118}
119
swissChili8a581c62021-12-07 13:29:21 -0800120void testParseFunc(QString string)
121{
122 Parser parser{string};
123
124 Function func;
125
126 if (!parser.parseFunctionDefinition(&func))
127 {
128 g_numFailed++;
129 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
130 qDebug() << string;
131 }
132 else
133 {
134 qDebug() << "\033[36mFunction\033[0m";
135 qDebug().noquote() << func;
136 }
137}
138
swissChilic71acc62021-12-07 08:03:37 -0800139int testResults()
140{
141 if (g_numFailed == 0)
142 {
143 qDebug() << "\033[32mALL TESTS SUCCEEDED\033[0m";
144 }
145 else
146 {
147 qDebug().nospace() << "\033[31m" << g_numFailed << " TESTS FAILED\033[0m";
swissChili3e98c062021-12-04 22:07:38 -0800148 }
149
150 return g_numFailed;
151}
152
swissChilic71acc62021-12-07 08:03:37 -0800153void testAllMatches()
154{
155 testMatch("a = a", true, match({Token('a')}, {Token('a')}, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800156
swissChilic71acc62021-12-07 08:03:37 -0800157 testMatch("s.a = y", true, match({Token('y')}, {Token('s', "a")}, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800158
swissChili3e98c062021-12-04 22:07:38 -0800159 LTok sameTwo = {Token('s', "a"), Token('s', "a")};
swissChilic71acc62021-12-07 08:03:37 -0800160 testMatch("s.a s.a = aa", true, match({Token('a'), Token('a')}, sameTwo, VarContext()));
161 testMatch("s.a s.a = ab", false, match({Token('a'), Token('b')}, sameTwo, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800162
163 LTok sameStartEnd = {
swissChilic71acc62021-12-07 08:03:37 -0800164 Token('s', "a"),
165 Token('e', "middle"),
166 Token('s', "a")};
167 testMatch("s.a e.middle s.a = aea", true,
168 match({Token('a'), Token('e'), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800169
swissChilic71acc62021-12-07 08:03:37 -0800170 testMatch("s.a e.middle s.a = aef Hi a", true,
171 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800172
swissChilic71acc62021-12-07 08:03:37 -0800173 testMatch("s.a e.middle s.a = aef Hi c", false,
174 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('c')}, sameStartEnd, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800175
176 LTok parenthesized = {
swissChilic71acc62021-12-07 08:03:37 -0800177 Token(LTok({Token('s', "a")})),
178 Token('e', "Middle"),
179 Token('s', "a"),
swissChilid17b5a12021-12-05 20:46:42 -0800180 };
181 LTok parenTest1 = {
swissChilic71acc62021-12-07 08:03:37 -0800182 Token(LTok({Token('y')})),
183 Token('f'),
184 Token("MiddleStuff"),
185 Token('y')};
swissChilid17b5a12021-12-05 20:46:42 -0800186
swissChilic71acc62021-12-07 08:03:37 -0800187 testMatch("(s.a) e.Middle s.a = (y)f MiddleStuff y", true,
swissChilid17b5a12021-12-05 20:46:42 -0800188 match(parenTest1, parenthesized, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800189 // testMatch("(y)f Middle-stuff y", "(s.a) e.Middle s.a");
swissChilid17b5a12021-12-05 20:46:42 -0800190
swissChili682e7bc2021-12-07 09:04:54 -0800191 testMatch("(a)", "(a)");
swissChili07d325f2021-12-08 20:02:05 -0800192 testMatch("hello", "s.A e.Rest");
swissChili9dddbf72021-12-08 23:03:25 -0800193 testMatch("123", "123");
194 testMatch("(123)", "t.a");
195 testMatch("(123)", "(s.a)");
swissChili3e98c062021-12-04 22:07:38 -0800196}
197
swissChilic71acc62021-12-07 08:03:37 -0800198void testAllParses()
199{
swissChili682e7bc2021-12-07 09:04:54 -0800200 testParseAst("all symbols");
201 testParseAst("Identifier symbols Identifier");
202 testParseAst("s.A");
203 testParseAst("(s.A) Variable-quoted");
204 testParseAst("<Func-name a b (c)>");
205 testParseAst("<Prout hi>");
206 testParseAst("(Prout hi)");
207 testParseAst("(<Prout hi>)");
208 testParseAst("<If T Then (<Prout hi>) Else (<Prout sorry>)>");
209 testParseAst("(s.a) e.Middle s.a");
swissChili8a581c62021-12-07 13:29:21 -0800210 testParseAst("Hello; Goodbye");
211 testParseAst("Key = Value");
swissChili9dddbf72021-12-08 23:03:25 -0800212 testParseAst("123", {AstNode("123", 10)});
213 testParseAst("12 00", {AstNode("12", 10), AstNode("0", 10)});
swissChili8a581c62021-12-07 13:29:21 -0800214}
215
216void testAllFunctionDefs()
217{
218 testParseFunc("Test { = HI; }");
swissChili923bd532021-12-08 22:48:58 -0800219 testParseFunc("Palindrome { = T; s.A = T; s.A e.Middle s.A = <Palindrome e.Middle>; e.Ignored = F; } ");
swissChili07d325f2021-12-08 20:02:05 -0800220}
221
222void testAllEvals()
223{
224 testEval("First {s.A e.Rest = s.A;}", "<First hello>", "h");
swissChilic71acc62021-12-07 08:03:37 -0800225}
226
227int main(int argc, char *argv[])
228{
swissChili7babd922021-12-02 22:46:48 -0800229 QCoreApplication a(argc, argv);
swissChili923bd532021-12-08 22:48:58 -0800230 QCoreApplication::setApplicationName("REFAL");
231 QCoreApplication::setApplicationVersion("1.0-a1");
swissChili3e98c062021-12-04 22:07:38 -0800232
swissChili923bd532021-12-08 22:48:58 -0800233 QCommandLineParser cli;
234 cli.setApplicationDescription("REFAL interpreter");
235 cli.addHelpOption();
236 cli.addVersionOption();
swissChili3e98c062021-12-04 22:07:38 -0800237
swissChili923bd532021-12-08 22:48:58 -0800238 QCommandLineOption testOption(QStringList{"t", "test"}, "Run test suite");
239 cli.addOption(testOption);
240
241 cli.process(a);
242
243 if (cli.isSet(testOption))
244 {
245 testAllMatches();
246 qDebug() << "";
247 testAllParses();
248 qDebug() << "";
249 testAllFunctionDefs();
250 qDebug() << "";
251 testAllEvals();
252
253 qDebug() << "";
254
255 return testResults();
256 }
257 else
258 {
259 Repl repl;
260 repl.start();
261 }
swissChili7babd922021-12-02 22:46:48 -0800262}