blob: 12f6deacea2c5bc7bf01a7dfbd17c700c219356d [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
26 QList<AstNode> expr = exprParser.parseMany<AstNode>();
27 QList<Token> res = resParser.parseMany<Token>();
28
29 QList<Token> result;
30
31 exprParser.skip();
32 resParser.skip();
33 while (funcParser.parseFunctionDefinition(&func))
34 {
35 eval.addFunction(func);
36 }
37
38 funcParser.skip();
39
40 if (!exprParser.atEnd() || !resParser.atEnd() || !funcParser.atEnd())
41 {
42 g_numFailed++;
43 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
44 qDebug() << "Failed to fully parse expression, function or result";
45 qDebug() << function << expression << expected;
46
47 goto end;
48 }
49
50 for (const AstNode &node : expr)
51 {
52 RuntimeResult rr = eval.evaluate(node, VarContext());
53
54 if (!rr.success())
55 {
56 g_numFailed++;
57 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
58 qDebug() << "Runtime error while evaluating" << node;
59 qDebug() << rr;
60
61 goto end;
62 }
63
64 result.append(rr.result());
65 }
66
67 if (result != res)
68 {
69 g_numFailed++;
70 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
71 qDebug() << "Expected result" << res;
72 qDebug() << "Got" << result;
73 }
74
75end:
76 qDebug() << "\033[36mEvaluate\033[0m" << function << expression << "->" << result;
77}
78
swissChili1060c0e2021-12-09 09:46:42 -080079void testEval(QString function, QString expression, QList<Token> expected)
80{
81 testEval(function, expression, pprint(expected));
82}
83
swissChilic71acc62021-12-07 08:03:37 -080084void testMatch(const QString &test, bool shouldBe, const MatchResult &result)
85{
86 if (result.success != shouldBe)
87 {
swissChili3e98c062021-12-04 22:07:38 -080088 g_numFailed++;
swissChilic71acc62021-12-07 08:03:37 -080089 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChilid17b5a12021-12-05 20:46:42 -080090 qDebug() << "with context" << result.context;
swissChili3e98c062021-12-04 22:07:38 -080091 }
92
swissChilic71acc62021-12-07 08:03:37 -080093 qDebug() << "\033[36mMatchResult\033[0m" << test << result.success;
swissChilid17b5a12021-12-05 20:46:42 -080094
swissChilic71acc62021-12-07 08:03:37 -080095 if (result.success != shouldBe)
96 {
swissChilid17b5a12021-12-05 20:46:42 -080097 qDebug() << "";
98 }
swissChili3e98c062021-12-04 22:07:38 -080099}
100
swissChili682e7bc2021-12-07 09:04:54 -0800101void testMatch(QString data, QString pattern, bool shouldBe = true)
102{
103 Parser dataParser(data),
104 patternParser(pattern);
105
swissChili07d325f2021-12-08 20:02:05 -0800106 testMatch(pattern + " = " + data, shouldBe,
107 match(dataParser.parseMany<Token>(), patternParser.parseMany<Token>(), VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800108}
109
swissChili9dddbf72021-12-08 23:03:25 -0800110void testParseAst(QString string, QList<AstNode> equals = {})
swissChilic71acc62021-12-07 08:03:37 -0800111{
112 Parser parser{string};
113
swissChili682e7bc2021-12-07 09:04:54 -0800114 QList<AstNode> result = parser.parseMany<AstNode>();
swissChilic71acc62021-12-07 08:03:37 -0800115
swissChili9dddbf72021-12-08 23:03:25 -0800116 if (!equals.empty() && result != equals)
117 {
118 g_numFailed++;
119 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
120 qDebug() << "Expected" << pprint(equals);
121 }
122
123 qDebug() << "\033[36mParse\033[0m" << string << pprint(result);
swissChilic71acc62021-12-07 08:03:37 -0800124}
125
swissChili8a581c62021-12-07 13:29:21 -0800126void testParseFunc(QString string)
127{
128 Parser parser{string};
129
130 Function func;
131
132 if (!parser.parseFunctionDefinition(&func))
133 {
134 g_numFailed++;
135 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
136 qDebug() << string;
137 }
138 else
139 {
140 qDebug() << "\033[36mFunction\033[0m";
141 qDebug().noquote() << func;
142 }
143}
144
swissChilic71acc62021-12-07 08:03:37 -0800145int testResults()
146{
147 if (g_numFailed == 0)
148 {
149 qDebug() << "\033[32mALL TESTS SUCCEEDED\033[0m";
150 }
151 else
152 {
153 qDebug().nospace() << "\033[31m" << g_numFailed << " TESTS FAILED\033[0m";
swissChili3e98c062021-12-04 22:07:38 -0800154 }
155
156 return g_numFailed;
157}
158
swissChilic71acc62021-12-07 08:03:37 -0800159void testAllMatches()
160{
161 testMatch("a = a", true, match({Token('a')}, {Token('a')}, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800162
swissChilic71acc62021-12-07 08:03:37 -0800163 testMatch("s.a = y", true, match({Token('y')}, {Token('s', "a")}, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800164
swissChili3e98c062021-12-04 22:07:38 -0800165 LTok sameTwo = {Token('s', "a"), Token('s', "a")};
swissChilic71acc62021-12-07 08:03:37 -0800166 testMatch("s.a s.a = aa", true, match({Token('a'), Token('a')}, sameTwo, VarContext()));
167 testMatch("s.a s.a = ab", false, match({Token('a'), Token('b')}, sameTwo, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800168
169 LTok sameStartEnd = {
swissChilic71acc62021-12-07 08:03:37 -0800170 Token('s', "a"),
171 Token('e', "middle"),
172 Token('s', "a")};
173 testMatch("s.a e.middle s.a = aea", true,
174 match({Token('a'), Token('e'), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800175
swissChilic71acc62021-12-07 08:03:37 -0800176 testMatch("s.a e.middle s.a = aef Hi a", true,
177 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800178
swissChilic71acc62021-12-07 08:03:37 -0800179 testMatch("s.a e.middle s.a = aef Hi c", false,
180 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('c')}, sameStartEnd, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800181
182 LTok parenthesized = {
swissChilic71acc62021-12-07 08:03:37 -0800183 Token(LTok({Token('s', "a")})),
184 Token('e', "Middle"),
185 Token('s', "a"),
swissChilid17b5a12021-12-05 20:46:42 -0800186 };
187 LTok parenTest1 = {
swissChilic71acc62021-12-07 08:03:37 -0800188 Token(LTok({Token('y')})),
189 Token('f'),
190 Token("MiddleStuff"),
191 Token('y')};
swissChilid17b5a12021-12-05 20:46:42 -0800192
swissChilic71acc62021-12-07 08:03:37 -0800193 testMatch("(s.a) e.Middle s.a = (y)f MiddleStuff y", true,
swissChilid17b5a12021-12-05 20:46:42 -0800194 match(parenTest1, parenthesized, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800195 // testMatch("(y)f Middle-stuff y", "(s.a) e.Middle s.a");
swissChilid17b5a12021-12-05 20:46:42 -0800196
swissChili682e7bc2021-12-07 09:04:54 -0800197 testMatch("(a)", "(a)");
swissChili07d325f2021-12-08 20:02:05 -0800198 testMatch("hello", "s.A e.Rest");
swissChili9dddbf72021-12-08 23:03:25 -0800199 testMatch("123", "123");
200 testMatch("(123)", "t.a");
201 testMatch("(123)", "(s.a)");
swissChili3e98c062021-12-04 22:07:38 -0800202}
203
swissChilic71acc62021-12-07 08:03:37 -0800204void testAllParses()
205{
swissChili682e7bc2021-12-07 09:04:54 -0800206 testParseAst("all symbols");
207 testParseAst("Identifier symbols Identifier");
208 testParseAst("s.A");
209 testParseAst("(s.A) Variable-quoted");
210 testParseAst("<Func-name a b (c)>");
211 testParseAst("<Prout hi>");
212 testParseAst("(Prout hi)");
213 testParseAst("(<Prout hi>)");
214 testParseAst("<If T Then (<Prout hi>) Else (<Prout sorry>)>");
215 testParseAst("(s.a) e.Middle s.a");
swissChili8a581c62021-12-07 13:29:21 -0800216 testParseAst("Hello; Goodbye");
217 testParseAst("Key = Value");
swissChili9dddbf72021-12-08 23:03:25 -0800218 testParseAst("123", {AstNode("123", 10)});
swissChili1060c0e2021-12-09 09:46:42 -0800219 testParseAst("12 00", {AstNode::fromInteger(12), AstNode::fromInteger(0)});
swissChili8a581c62021-12-07 13:29:21 -0800220}
221
222void testAllFunctionDefs()
223{
224 testParseFunc("Test { = HI; }");
swissChili923bd532021-12-08 22:48:58 -0800225 testParseFunc("Palindrome { = T; s.A = T; s.A e.Middle s.A = <Palindrome e.Middle>; e.Ignored = F; } ");
swissChili07d325f2021-12-08 20:02:05 -0800226}
227
228void testAllEvals()
229{
230 testEval("First {s.A e.Rest = s.A;}", "<First hello>", "h");
swissChili1060c0e2021-12-09 09:46:42 -0800231 testEval("Number { = 123; }", "<Number>", QList<Token>{Token::fromInteger(123)});
swissChilic71acc62021-12-07 08:03:37 -0800232}
233
234int main(int argc, char *argv[])
235{
swissChili7babd922021-12-02 22:46:48 -0800236 QCoreApplication a(argc, argv);
swissChili923bd532021-12-08 22:48:58 -0800237 QCoreApplication::setApplicationName("REFAL");
238 QCoreApplication::setApplicationVersion("1.0-a1");
swissChili3e98c062021-12-04 22:07:38 -0800239
swissChili923bd532021-12-08 22:48:58 -0800240 QCommandLineParser cli;
241 cli.setApplicationDescription("REFAL interpreter");
242 cli.addHelpOption();
243 cli.addVersionOption();
swissChili3e98c062021-12-04 22:07:38 -0800244
swissChili1060c0e2021-12-09 09:46:42 -0800245 QCommandLineOption testOption(QStringList{"t", "test"}, "Run test suite.");
swissChili923bd532021-12-08 22:48:58 -0800246 cli.addOption(testOption);
247
248 cli.process(a);
249
250 if (cli.isSet(testOption))
251 {
252 testAllMatches();
253 qDebug() << "";
254 testAllParses();
255 qDebug() << "";
256 testAllFunctionDefs();
257 qDebug() << "";
258 testAllEvals();
259
260 qDebug() << "";
261
262 return testResults();
263 }
264 else
265 {
266 Repl repl;
267 repl.start();
268 }
swissChili7babd922021-12-02 22:46:48 -0800269}