blob: 765e3800d39e83ed418b9f7125cf16809800df90 [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"
swissChili3e98c062021-12-04 22:07:38 -080012
13int g_numFailed = 0;
14
swissChili07d325f2021-12-08 20:02:05 -080015void testEval(QString function, QString expression, QString expected)
16{
17 Evaluator eval;
18 Parser funcParser(function),
19 exprParser(expression),
20 resParser(expected);
21
22 Function func;
23
24 QList<AstNode> expr = exprParser.parseMany<AstNode>();
25 QList<Token> res = resParser.parseMany<Token>();
26
27 QList<Token> result;
28
29 exprParser.skip();
30 resParser.skip();
31 while (funcParser.parseFunctionDefinition(&func))
32 {
33 eval.addFunction(func);
34 }
35
36 funcParser.skip();
37
38 if (!exprParser.atEnd() || !resParser.atEnd() || !funcParser.atEnd())
39 {
40 g_numFailed++;
41 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
42 qDebug() << "Failed to fully parse expression, function or result";
43 qDebug() << function << expression << expected;
44
45 goto end;
46 }
47
48 for (const AstNode &node : expr)
49 {
50 RuntimeResult rr = eval.evaluate(node, VarContext());
51
52 if (!rr.success())
53 {
54 g_numFailed++;
55 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
56 qDebug() << "Runtime error while evaluating" << node;
57 qDebug() << rr;
58
59 goto end;
60 }
61
62 result.append(rr.result());
63 }
64
65 if (result != res)
66 {
67 g_numFailed++;
68 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
69 qDebug() << "Expected result" << res;
70 qDebug() << "Got" << result;
71 }
72
73end:
74 qDebug() << "\033[36mEvaluate\033[0m" << function << expression << "->" << result;
75}
76
swissChilic71acc62021-12-07 08:03:37 -080077void testMatch(const QString &test, bool shouldBe, const MatchResult &result)
78{
79 if (result.success != shouldBe)
80 {
swissChili3e98c062021-12-04 22:07:38 -080081 g_numFailed++;
swissChilic71acc62021-12-07 08:03:37 -080082 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChilid17b5a12021-12-05 20:46:42 -080083 qDebug() << "with context" << result.context;
swissChili3e98c062021-12-04 22:07:38 -080084 }
85
swissChilic71acc62021-12-07 08:03:37 -080086 qDebug() << "\033[36mMatchResult\033[0m" << test << result.success;
swissChilid17b5a12021-12-05 20:46:42 -080087
swissChilic71acc62021-12-07 08:03:37 -080088 if (result.success != shouldBe)
89 {
swissChilid17b5a12021-12-05 20:46:42 -080090 qDebug() << "";
91 }
swissChili3e98c062021-12-04 22:07:38 -080092}
93
swissChili682e7bc2021-12-07 09:04:54 -080094void testMatch(QString data, QString pattern, bool shouldBe = true)
95{
96 Parser dataParser(data),
97 patternParser(pattern);
98
swissChili07d325f2021-12-08 20:02:05 -080099 testMatch(pattern + " = " + data, shouldBe,
100 match(dataParser.parseMany<Token>(), patternParser.parseMany<Token>(), VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800101}
102
103void testParseAst(QString string)
swissChilic71acc62021-12-07 08:03:37 -0800104{
105 Parser parser{string};
106
swissChili682e7bc2021-12-07 09:04:54 -0800107 QList<AstNode> result = parser.parseMany<AstNode>();
swissChilic71acc62021-12-07 08:03:37 -0800108
109 qDebug() << "\033[36mParse\033[0m" << string << result;
110}
111
swissChili8a581c62021-12-07 13:29:21 -0800112void testParseFunc(QString string)
113{
114 Parser parser{string};
115
116 Function func;
117
118 if (!parser.parseFunctionDefinition(&func))
119 {
120 g_numFailed++;
121 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
122 qDebug() << string;
123 }
124 else
125 {
126 qDebug() << "\033[36mFunction\033[0m";
127 qDebug().noquote() << func;
128 }
129}
130
swissChilic71acc62021-12-07 08:03:37 -0800131int testResults()
132{
133 if (g_numFailed == 0)
134 {
135 qDebug() << "\033[32mALL TESTS SUCCEEDED\033[0m";
136 }
137 else
138 {
139 qDebug().nospace() << "\033[31m" << g_numFailed << " TESTS FAILED\033[0m";
swissChili3e98c062021-12-04 22:07:38 -0800140 }
141
142 return g_numFailed;
143}
144
swissChilic71acc62021-12-07 08:03:37 -0800145void testAllMatches()
146{
147 testMatch("a = a", true, match({Token('a')}, {Token('a')}, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800148
swissChilic71acc62021-12-07 08:03:37 -0800149 testMatch("s.a = y", true, match({Token('y')}, {Token('s', "a")}, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800150
swissChili3e98c062021-12-04 22:07:38 -0800151 LTok sameTwo = {Token('s', "a"), Token('s', "a")};
swissChilic71acc62021-12-07 08:03:37 -0800152 testMatch("s.a s.a = aa", true, match({Token('a'), Token('a')}, sameTwo, VarContext()));
153 testMatch("s.a s.a = ab", false, match({Token('a'), Token('b')}, sameTwo, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800154
155 LTok sameStartEnd = {
swissChilic71acc62021-12-07 08:03:37 -0800156 Token('s', "a"),
157 Token('e', "middle"),
158 Token('s', "a")};
159 testMatch("s.a e.middle s.a = aea", true,
160 match({Token('a'), Token('e'), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800161
swissChilic71acc62021-12-07 08:03:37 -0800162 testMatch("s.a e.middle s.a = aef Hi a", true,
163 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800164
swissChilic71acc62021-12-07 08:03:37 -0800165 testMatch("s.a e.middle s.a = aef Hi c", false,
166 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('c')}, sameStartEnd, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800167
168 LTok parenthesized = {
swissChilic71acc62021-12-07 08:03:37 -0800169 Token(LTok({Token('s', "a")})),
170 Token('e', "Middle"),
171 Token('s', "a"),
swissChilid17b5a12021-12-05 20:46:42 -0800172 };
173 LTok parenTest1 = {
swissChilic71acc62021-12-07 08:03:37 -0800174 Token(LTok({Token('y')})),
175 Token('f'),
176 Token("MiddleStuff"),
177 Token('y')};
swissChilid17b5a12021-12-05 20:46:42 -0800178
swissChilic71acc62021-12-07 08:03:37 -0800179 testMatch("(s.a) e.Middle s.a = (y)f MiddleStuff y", true,
swissChilid17b5a12021-12-05 20:46:42 -0800180 match(parenTest1, parenthesized, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800181 // testMatch("(y)f Middle-stuff y", "(s.a) e.Middle s.a");
swissChilid17b5a12021-12-05 20:46:42 -0800182
swissChili682e7bc2021-12-07 09:04:54 -0800183 testMatch("(a)", "(a)");
swissChili07d325f2021-12-08 20:02:05 -0800184 testMatch("hello", "s.A e.Rest");
swissChili3e98c062021-12-04 22:07:38 -0800185}
186
swissChilic71acc62021-12-07 08:03:37 -0800187void testAllParses()
188{
swissChili682e7bc2021-12-07 09:04:54 -0800189 testParseAst("all symbols");
190 testParseAst("Identifier symbols Identifier");
191 testParseAst("s.A");
192 testParseAst("(s.A) Variable-quoted");
193 testParseAst("<Func-name a b (c)>");
194 testParseAst("<Prout hi>");
195 testParseAst("(Prout hi)");
196 testParseAst("(<Prout hi>)");
197 testParseAst("<If T Then (<Prout hi>) Else (<Prout sorry>)>");
198 testParseAst("(s.a) e.Middle s.a");
swissChili8a581c62021-12-07 13:29:21 -0800199 testParseAst("Hello; Goodbye");
200 testParseAst("Key = Value");
201}
202
203void testAllFunctionDefs()
204{
205 testParseFunc("Test { = HI; }");
swissChili923bd532021-12-08 22:48:58 -0800206 testParseFunc("Palindrome { = T; s.A = T; s.A e.Middle s.A = <Palindrome e.Middle>; e.Ignored = F; } ");
swissChili07d325f2021-12-08 20:02:05 -0800207}
208
209void testAllEvals()
210{
211 testEval("First {s.A e.Rest = s.A;}", "<First hello>", "h");
swissChilic71acc62021-12-07 08:03:37 -0800212}
213
214int main(int argc, char *argv[])
215{
swissChili7babd922021-12-02 22:46:48 -0800216 QCoreApplication a(argc, argv);
swissChili923bd532021-12-08 22:48:58 -0800217 QCoreApplication::setApplicationName("REFAL");
218 QCoreApplication::setApplicationVersion("1.0-a1");
swissChili3e98c062021-12-04 22:07:38 -0800219
swissChili923bd532021-12-08 22:48:58 -0800220 QCommandLineParser cli;
221 cli.setApplicationDescription("REFAL interpreter");
222 cli.addHelpOption();
223 cli.addVersionOption();
swissChili3e98c062021-12-04 22:07:38 -0800224
swissChili923bd532021-12-08 22:48:58 -0800225 QCommandLineOption testOption(QStringList{"t", "test"}, "Run test suite");
226 cli.addOption(testOption);
227
228 cli.process(a);
229
230 if (cli.isSet(testOption))
231 {
232 testAllMatches();
233 qDebug() << "";
234 testAllParses();
235 qDebug() << "";
236 testAllFunctionDefs();
237 qDebug() << "";
238 testAllEvals();
239
240 qDebug() << "";
241
242 return testResults();
243 }
244 else
245 {
246 Repl repl;
247 repl.start();
248 }
swissChili7babd922021-12-02 22:46:48 -0800249}