blob: 8e29b1120a3cd83a722f8c96ee422f643987465a [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
swissChili1060c0e2021-12-09 09:46:42 -080016
swissChili07d325f2021-12-08 20:02:05 -080017void testEval(QString function, QString expression, QString expected)
18{
19 Evaluator eval;
20 Parser funcParser(function),
21 exprParser(expression),
22 resParser(expected);
23
24 Function func;
25
swissChili847a78c2021-12-09 17:44:52 -080026 QList<AstNode> expr;
27 ParseResult exprRet = exprParser.parseMany<AstNode>(&expr);
28 QList<Token> res;
29 ParseResult resRet = resParser.parseMany<Token>(&res);
30 ParseResult funcRet;
swissChili07d325f2021-12-08 20:02:05 -080031
32 QList<Token> result;
33
34 exprParser.skip();
35 resParser.skip();
swissChili847a78c2021-12-09 17:44:52 -080036 while ((funcRet = funcParser.parseFunctionDefinition(&func)))
swissChili07d325f2021-12-08 20:02:05 -080037 {
38 eval.addFunction(func);
39 }
40
41 funcParser.skip();
42
swissChili847a78c2021-12-09 17:44:52 -080043 if (!exprRet || !resRet || funcRet.status() == ParseResult::INCOMPLETE)
swissChili07d325f2021-12-08 20:02:05 -080044 {
45 g_numFailed++;
46 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
47 qDebug() << "Failed to fully parse expression, function or result";
48 qDebug() << function << expression << expected;
swissChili847a78c2021-12-09 17:44:52 -080049 qDebug() << funcRet.message() << exprRet.message() << resRet.message();
50 qDebug() << funcRet << exprRet << resRet;
swissChili07d325f2021-12-08 20:02:05 -080051
52 goto end;
53 }
54
55 for (const AstNode &node : expr)
56 {
57 RuntimeResult rr = eval.evaluate(node, VarContext());
58
59 if (!rr.success())
60 {
61 g_numFailed++;
62 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
63 qDebug() << "Runtime error while evaluating" << node;
64 qDebug() << rr;
65
66 goto end;
67 }
68
69 result.append(rr.result());
70 }
71
72 if (result != res)
73 {
74 g_numFailed++;
75 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
76 qDebug() << "Expected result" << res;
77 qDebug() << "Got" << result;
78 }
79
80end:
81 qDebug() << "\033[36mEvaluate\033[0m" << function << expression << "->" << result;
82}
83
swissChili1060c0e2021-12-09 09:46:42 -080084void testEval(QString function, QString expression, QList<Token> expected)
85{
86 testEval(function, expression, pprint(expected));
87}
88
swissChilic71acc62021-12-07 08:03:37 -080089void testMatch(const QString &test, bool shouldBe, const MatchResult &result)
90{
91 if (result.success != shouldBe)
92 {
swissChili3e98c062021-12-04 22:07:38 -080093 g_numFailed++;
swissChilic71acc62021-12-07 08:03:37 -080094 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChilid17b5a12021-12-05 20:46:42 -080095 qDebug() << "with context" << result.context;
swissChili3e98c062021-12-04 22:07:38 -080096 }
97
swissChilic71acc62021-12-07 08:03:37 -080098 qDebug() << "\033[36mMatchResult\033[0m" << test << result.success;
swissChilid17b5a12021-12-05 20:46:42 -080099
swissChilic71acc62021-12-07 08:03:37 -0800100 if (result.success != shouldBe)
101 {
swissChilid17b5a12021-12-05 20:46:42 -0800102 qDebug() << "";
103 }
swissChili3e98c062021-12-04 22:07:38 -0800104}
105
swissChili682e7bc2021-12-07 09:04:54 -0800106void testMatch(QString data, QString pattern, bool shouldBe = true)
107{
108 Parser dataParser(data),
109 patternParser(pattern);
110
swissChili847a78c2021-12-09 17:44:52 -0800111 QList<Token> parsedData, parsedPattern;
112
113 dataParser.parseMany<Token>(&parsedData);
114 patternParser.parseMany<Token>(&parsedPattern);
115
swissChili07d325f2021-12-08 20:02:05 -0800116 testMatch(pattern + " = " + data, shouldBe,
swissChili847a78c2021-12-09 17:44:52 -0800117 match(parsedData, parsedPattern, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800118}
119
swissChili9dddbf72021-12-08 23:03:25 -0800120void testParseAst(QString string, QList<AstNode> equals = {})
swissChilic71acc62021-12-07 08:03:37 -0800121{
122 Parser parser{string};
123
swissChili847a78c2021-12-09 17:44:52 -0800124 QList<AstNode> result;
125 parser.parseMany<AstNode>(&result);
swissChilic71acc62021-12-07 08:03:37 -0800126
swissChili9dddbf72021-12-08 23:03:25 -0800127 if (!equals.empty() && result != equals)
128 {
129 g_numFailed++;
130 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
131 qDebug() << "Expected" << pprint(equals);
132 }
133
134 qDebug() << "\033[36mParse\033[0m" << string << pprint(result);
swissChilic71acc62021-12-07 08:03:37 -0800135}
136
swissChili8a581c62021-12-07 13:29:21 -0800137void testParseFunc(QString string)
138{
139 Parser parser{string};
swissChili847a78c2021-12-09 17:44:52 -0800140 ParseResult ret;
swissChili8a581c62021-12-07 13:29:21 -0800141
142 Function func;
143
swissChili847a78c2021-12-09 17:44:52 -0800144 if (!(ret = parser.parseFunctionDefinition(&func)))
swissChili8a581c62021-12-07 13:29:21 -0800145 {
146 g_numFailed++;
147 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChili847a78c2021-12-09 17:44:52 -0800148 qDebug() << ret.message();
swissChili8a581c62021-12-07 13:29:21 -0800149 qDebug() << string;
150 }
151 else
152 {
153 qDebug() << "\033[36mFunction\033[0m";
154 qDebug().noquote() << func;
155 }
156}
157
swissChilic71acc62021-12-07 08:03:37 -0800158int testResults()
159{
160 if (g_numFailed == 0)
161 {
162 qDebug() << "\033[32mALL TESTS SUCCEEDED\033[0m";
163 }
164 else
165 {
166 qDebug().nospace() << "\033[31m" << g_numFailed << " TESTS FAILED\033[0m";
swissChili3e98c062021-12-04 22:07:38 -0800167 }
168
169 return g_numFailed;
170}
171
swissChilic71acc62021-12-07 08:03:37 -0800172void testAllMatches()
173{
174 testMatch("a = a", true, match({Token('a')}, {Token('a')}, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800175
swissChilic71acc62021-12-07 08:03:37 -0800176 testMatch("s.a = y", true, match({Token('y')}, {Token('s', "a")}, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800177
swissChili3e98c062021-12-04 22:07:38 -0800178 LTok sameTwo = {Token('s', "a"), Token('s', "a")};
swissChilic71acc62021-12-07 08:03:37 -0800179 testMatch("s.a s.a = aa", true, match({Token('a'), Token('a')}, sameTwo, VarContext()));
180 testMatch("s.a s.a = ab", false, match({Token('a'), Token('b')}, sameTwo, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800181
182 LTok sameStartEnd = {
swissChilic71acc62021-12-07 08:03:37 -0800183 Token('s', "a"),
184 Token('e', "middle"),
185 Token('s', "a")};
186 testMatch("s.a e.middle s.a = aea", true,
187 match({Token('a'), Token('e'), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800188
swissChilic71acc62021-12-07 08:03:37 -0800189 testMatch("s.a e.middle s.a = aef Hi a", true,
190 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800191
swissChilic71acc62021-12-07 08:03:37 -0800192 testMatch("s.a e.middle s.a = aef Hi c", false,
193 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('c')}, sameStartEnd, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800194
195 LTok parenthesized = {
swissChilic71acc62021-12-07 08:03:37 -0800196 Token(LTok({Token('s', "a")})),
197 Token('e', "Middle"),
198 Token('s', "a"),
swissChilid17b5a12021-12-05 20:46:42 -0800199 };
200 LTok parenTest1 = {
swissChilic71acc62021-12-07 08:03:37 -0800201 Token(LTok({Token('y')})),
202 Token('f'),
203 Token("MiddleStuff"),
204 Token('y')};
swissChilid17b5a12021-12-05 20:46:42 -0800205
swissChilic71acc62021-12-07 08:03:37 -0800206 testMatch("(s.a) e.Middle s.a = (y)f MiddleStuff y", true,
swissChilid17b5a12021-12-05 20:46:42 -0800207 match(parenTest1, parenthesized, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800208 // testMatch("(y)f Middle-stuff y", "(s.a) e.Middle s.a");
swissChilid17b5a12021-12-05 20:46:42 -0800209
swissChili682e7bc2021-12-07 09:04:54 -0800210 testMatch("(a)", "(a)");
swissChili07d325f2021-12-08 20:02:05 -0800211 testMatch("hello", "s.A e.Rest");
swissChili9dddbf72021-12-08 23:03:25 -0800212 testMatch("123", "123");
213 testMatch("(123)", "t.a");
214 testMatch("(123)", "(s.a)");
swissChili3e98c062021-12-04 22:07:38 -0800215}
216
swissChilic71acc62021-12-07 08:03:37 -0800217void testAllParses()
218{
swissChili682e7bc2021-12-07 09:04:54 -0800219 testParseAst("all symbols");
220 testParseAst("Identifier symbols Identifier");
221 testParseAst("s.A");
222 testParseAst("(s.A) Variable-quoted");
223 testParseAst("<Func-name a b (c)>");
224 testParseAst("<Prout hi>");
225 testParseAst("(Prout hi)");
226 testParseAst("(<Prout hi>)");
227 testParseAst("<If T Then (<Prout hi>) Else (<Prout sorry>)>");
228 testParseAst("(s.a) e.Middle s.a");
swissChili8a581c62021-12-07 13:29:21 -0800229 testParseAst("Hello; Goodbye");
230 testParseAst("Key = Value");
swissChili9dddbf72021-12-08 23:03:25 -0800231 testParseAst("123", {AstNode("123", 10)});
swissChili1060c0e2021-12-09 09:46:42 -0800232 testParseAst("12 00", {AstNode::fromInteger(12), AstNode::fromInteger(0)});
swissChili8a581c62021-12-07 13:29:21 -0800233}
234
235void testAllFunctionDefs()
236{
237 testParseFunc("Test { = HI; }");
swissChili923bd532021-12-08 22:48:58 -0800238 testParseFunc("Palindrome { = T; s.A = T; s.A e.Middle s.A = <Palindrome e.Middle>; e.Ignored = F; } ");
swissChili07d325f2021-12-08 20:02:05 -0800239}
240
241void testAllEvals()
242{
243 testEval("First {s.A e.Rest = s.A;}", "<First hello>", "h");
swissChili1060c0e2021-12-09 09:46:42 -0800244 testEval("Number { = 123; }", "<Number>", QList<Token>{Token::fromInteger(123)});
swissChilic71acc62021-12-07 08:03:37 -0800245}
246
247int main(int argc, char *argv[])
248{
swissChili7babd922021-12-02 22:46:48 -0800249 QCoreApplication a(argc, argv);
swissChili923bd532021-12-08 22:48:58 -0800250 QCoreApplication::setApplicationName("REFAL");
251 QCoreApplication::setApplicationVersion("1.0-a1");
swissChili3e98c062021-12-04 22:07:38 -0800252
swissChili923bd532021-12-08 22:48:58 -0800253 QCommandLineParser cli;
254 cli.setApplicationDescription("REFAL interpreter");
255 cli.addHelpOption();
256 cli.addVersionOption();
swissChili3e98c062021-12-04 22:07:38 -0800257
swissChili1060c0e2021-12-09 09:46:42 -0800258 QCommandLineOption testOption(QStringList{"t", "test"}, "Run test suite.");
swissChili923bd532021-12-08 22:48:58 -0800259 cli.addOption(testOption);
260
261 cli.process(a);
262
263 if (cli.isSet(testOption))
264 {
265 testAllMatches();
266 qDebug() << "";
267 testAllParses();
268 qDebug() << "";
269 testAllFunctionDefs();
270 qDebug() << "";
271 testAllEvals();
272
273 qDebug() << "";
274
275 return testResults();
276 }
277 else
278 {
279 Repl repl;
280 repl.start();
281 }
swissChili7babd922021-12-02 22:46:48 -0800282}