blob: 7f1a3ad808617e151d85d7b9969a657ab6e69351 [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";
swissChili323883d2022-02-20 16:35:23 -080048 qDebug() << "Function:";
49 sout(pprint(funcRet, funcParser));
50
51 qDebug() << "Expression:";
52 sout(pprint(exprRet, exprParser));
53
54 qDebug() << "Result:";
55 sout(pprint(resRet, resParser));
swissChili07d325f2021-12-08 20:02:05 -080056
57 goto end;
58 }
59
60 for (const AstNode &node : expr)
61 {
62 RuntimeResult rr = eval.evaluate(node, VarContext());
63
64 if (!rr.success())
65 {
66 g_numFailed++;
67 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
68 qDebug() << "Runtime error while evaluating" << node;
69 qDebug() << rr;
70
71 goto end;
72 }
73
74 result.append(rr.result());
75 }
76
77 if (result != res)
78 {
79 g_numFailed++;
80 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
81 qDebug() << "Expected result" << res;
82 qDebug() << "Got" << result;
83 }
84
85end:
swissChili918557c2022-02-20 20:16:34 -080086 qDebug() << "\033[36mEvaluate\033[0m" << function << expression << "->" << pprint(result);
swissChili07d325f2021-12-08 20:02:05 -080087}
88
swissChili1060c0e2021-12-09 09:46:42 -080089void testEval(QString function, QString expression, QList<Token> expected)
90{
91 testEval(function, expression, pprint(expected));
92}
93
swissChili918557c2022-02-20 20:16:34 -080094void testEval(QString expression, QString expected)
95{
96 testEval("", expression, expected);
97}
98
swissChilic71acc62021-12-07 08:03:37 -080099void testMatch(const QString &test, bool shouldBe, const MatchResult &result)
100{
101 if (result.success != shouldBe)
102 {
swissChili3e98c062021-12-04 22:07:38 -0800103 g_numFailed++;
swissChilic71acc62021-12-07 08:03:37 -0800104 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChilid17b5a12021-12-05 20:46:42 -0800105 qDebug() << "with context" << result.context;
swissChili3e98c062021-12-04 22:07:38 -0800106 }
107
swissChilic71acc62021-12-07 08:03:37 -0800108 qDebug() << "\033[36mMatchResult\033[0m" << test << result.success;
swissChilid17b5a12021-12-05 20:46:42 -0800109
swissChilic71acc62021-12-07 08:03:37 -0800110 if (result.success != shouldBe)
111 {
swissChilid17b5a12021-12-05 20:46:42 -0800112 qDebug() << "";
113 }
swissChili3e98c062021-12-04 22:07:38 -0800114}
115
swissChili682e7bc2021-12-07 09:04:54 -0800116void testMatch(QString data, QString pattern, bool shouldBe = true)
117{
118 Parser dataParser(data),
119 patternParser(pattern);
120
swissChili847a78c2021-12-09 17:44:52 -0800121 QList<Token> parsedData, parsedPattern;
122
123 dataParser.parseMany<Token>(&parsedData);
124 patternParser.parseMany<Token>(&parsedPattern);
125
swissChili07d325f2021-12-08 20:02:05 -0800126 testMatch(pattern + " = " + data, shouldBe,
swissChili847a78c2021-12-09 17:44:52 -0800127 match(parsedData, parsedPattern, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800128}
129
swissChili9dddbf72021-12-08 23:03:25 -0800130void testParseAst(QString string, QList<AstNode> equals = {})
swissChilic71acc62021-12-07 08:03:37 -0800131{
132 Parser parser{string};
133
swissChili847a78c2021-12-09 17:44:52 -0800134 QList<AstNode> result;
135 parser.parseMany<AstNode>(&result);
swissChilic71acc62021-12-07 08:03:37 -0800136
swissChili9dddbf72021-12-08 23:03:25 -0800137 if (!equals.empty() && result != equals)
138 {
139 g_numFailed++;
140 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
141 qDebug() << "Expected" << pprint(equals);
142 }
143
144 qDebug() << "\033[36mParse\033[0m" << string << pprint(result);
swissChilic71acc62021-12-07 08:03:37 -0800145}
146
swissChili8a581c62021-12-07 13:29:21 -0800147void testParseFunc(QString string)
148{
149 Parser parser{string};
swissChili847a78c2021-12-09 17:44:52 -0800150 ParseResult ret;
swissChili8a581c62021-12-07 13:29:21 -0800151
152 Function func;
153
swissChili847a78c2021-12-09 17:44:52 -0800154 if (!(ret = parser.parseFunctionDefinition(&func)))
swissChili8a581c62021-12-07 13:29:21 -0800155 {
156 g_numFailed++;
157 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChili323883d2022-02-20 16:35:23 -0800158 sout(pprint(ret, parser));
swissChili8a581c62021-12-07 13:29:21 -0800159 qDebug() << string;
160 }
161 else
162 {
163 qDebug() << "\033[36mFunction\033[0m";
164 qDebug().noquote() << func;
165 }
166}
167
swissChilic71acc62021-12-07 08:03:37 -0800168int testResults()
169{
170 if (g_numFailed == 0)
171 {
172 qDebug() << "\033[32mALL TESTS SUCCEEDED\033[0m";
173 }
174 else
175 {
176 qDebug().nospace() << "\033[31m" << g_numFailed << " TESTS FAILED\033[0m";
swissChili3e98c062021-12-04 22:07:38 -0800177 }
178
179 return g_numFailed;
180}
181
swissChilic71acc62021-12-07 08:03:37 -0800182void testAllMatches()
183{
184 testMatch("a = a", true, match({Token('a')}, {Token('a')}, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800185
swissChilic71acc62021-12-07 08:03:37 -0800186 testMatch("s.a = y", true, match({Token('y')}, {Token('s', "a")}, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800187
swissChili3e98c062021-12-04 22:07:38 -0800188 LTok sameTwo = {Token('s', "a"), Token('s', "a")};
swissChilic71acc62021-12-07 08:03:37 -0800189 testMatch("s.a s.a = aa", true, match({Token('a'), Token('a')}, sameTwo, VarContext()));
190 testMatch("s.a s.a = ab", false, match({Token('a'), Token('b')}, sameTwo, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800191
192 LTok sameStartEnd = {
swissChilic71acc62021-12-07 08:03:37 -0800193 Token('s', "a"),
194 Token('e', "middle"),
195 Token('s', "a")};
196 testMatch("s.a e.middle s.a = aea", true,
197 match({Token('a'), Token('e'), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800198
swissChilic71acc62021-12-07 08:03:37 -0800199 testMatch("s.a e.middle s.a = aef Hi a", true,
200 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800201
swissChilic71acc62021-12-07 08:03:37 -0800202 testMatch("s.a e.middle s.a = aef Hi c", false,
203 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('c')}, sameStartEnd, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800204
205 LTok parenthesized = {
swissChilic71acc62021-12-07 08:03:37 -0800206 Token(LTok({Token('s', "a")})),
207 Token('e', "Middle"),
208 Token('s', "a"),
swissChilid17b5a12021-12-05 20:46:42 -0800209 };
210 LTok parenTest1 = {
swissChilic71acc62021-12-07 08:03:37 -0800211 Token(LTok({Token('y')})),
212 Token('f'),
213 Token("MiddleStuff"),
214 Token('y')};
swissChilid17b5a12021-12-05 20:46:42 -0800215
swissChilic71acc62021-12-07 08:03:37 -0800216 testMatch("(s.a) e.Middle s.a = (y)f MiddleStuff y", true,
swissChilid17b5a12021-12-05 20:46:42 -0800217 match(parenTest1, parenthesized, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800218 // testMatch("(y)f Middle-stuff y", "(s.a) e.Middle s.a");
swissChilid17b5a12021-12-05 20:46:42 -0800219
swissChili682e7bc2021-12-07 09:04:54 -0800220 testMatch("(a)", "(a)");
swissChili07d325f2021-12-08 20:02:05 -0800221 testMatch("hello", "s.A e.Rest");
swissChili9dddbf72021-12-08 23:03:25 -0800222 testMatch("123", "123");
223 testMatch("(123)", "t.a");
224 testMatch("(123)", "(s.a)");
swissChili3e98c062021-12-04 22:07:38 -0800225}
226
swissChilic71acc62021-12-07 08:03:37 -0800227void testAllParses()
228{
swissChili682e7bc2021-12-07 09:04:54 -0800229 testParseAst("all symbols");
230 testParseAst("Identifier symbols Identifier");
231 testParseAst("s.A");
232 testParseAst("(s.A) Variable-quoted");
233 testParseAst("<Func-name a b (c)>");
234 testParseAst("<Prout hi>");
235 testParseAst("(Prout hi)");
236 testParseAst("(<Prout hi>)");
237 testParseAst("<If T Then (<Prout hi>) Else (<Prout sorry>)>");
238 testParseAst("(s.a) e.Middle s.a");
swissChili8a581c62021-12-07 13:29:21 -0800239 testParseAst("Hello; Goodbye");
240 testParseAst("Key = Value");
swissChili9dddbf72021-12-08 23:03:25 -0800241 testParseAst("123", {AstNode("123", 10)});
swissChili1060c0e2021-12-09 09:46:42 -0800242 testParseAst("12 00", {AstNode::fromInteger(12), AstNode::fromInteger(0)});
swissChili323883d2022-02-20 16:35:23 -0800243 testParseAst("s.A s.B", {AstNode('s', "A"), AstNode('s', "B")});
swissChili918557c2022-02-20 20:16:34 -0800244 testParseAst("a '=' b", {AstNode('a'), AstNode('='), AstNode('b')});
245 testParseAst("'This\\'<>#$@#^%\\n' 'another $3543543'");
swissChili8a581c62021-12-07 13:29:21 -0800246}
247
248void testAllFunctionDefs()
249{
250 testParseFunc("Test { = HI; }");
swissChili923bd532021-12-08 22:48:58 -0800251 testParseFunc("Palindrome { = T; s.A = T; s.A e.Middle s.A = <Palindrome e.Middle>; e.Ignored = F; } ");
swissChili07d325f2021-12-08 20:02:05 -0800252}
253
254void testAllEvals()
255{
256 testEval("First {s.A e.Rest = s.A;}", "<First hello>", "h");
swissChili1060c0e2021-12-09 09:46:42 -0800257 testEval("Number { = 123; }", "<Number>", QList<Token>{Token::fromInteger(123)});
swissChili918557c2022-02-20 20:16:34 -0800258 testEval("<Br Name '=' Jim> <Dg Name>", "Jim");
259 testEval("<Br A '=' hello> <Br A '=' 'good bye'> <Dg A> <Dg A>", "'good byehello'");
swissChilic71acc62021-12-07 08:03:37 -0800260}
261
262int main(int argc, char *argv[])
263{
swissChili7babd922021-12-02 22:46:48 -0800264 QCoreApplication a(argc, argv);
swissChili923bd532021-12-08 22:48:58 -0800265 QCoreApplication::setApplicationName("REFAL");
266 QCoreApplication::setApplicationVersion("1.0-a1");
swissChili3e98c062021-12-04 22:07:38 -0800267
swissChili923bd532021-12-08 22:48:58 -0800268 QCommandLineParser cli;
269 cli.setApplicationDescription("REFAL interpreter");
270 cli.addHelpOption();
271 cli.addVersionOption();
swissChili3e98c062021-12-04 22:07:38 -0800272
swissChili323883d2022-02-20 16:35:23 -0800273 cli.addPositionalArgument("script", "REFAL script to run");
274
swissChili1060c0e2021-12-09 09:46:42 -0800275 QCommandLineOption testOption(QStringList{"t", "test"}, "Run test suite.");
swissChili923bd532021-12-08 22:48:58 -0800276 cli.addOption(testOption);
277
278 cli.process(a);
279
swissChili323883d2022-02-20 16:35:23 -0800280 if (cli.positionalArguments().length() > 0)
281 {
282 qDebug() << "Running script" << cli.positionalArguments()[0];
283 }
284 else if (cli.isSet(testOption))
swissChili923bd532021-12-08 22:48:58 -0800285 {
286 testAllMatches();
287 qDebug() << "";
288 testAllParses();
289 qDebug() << "";
290 testAllFunctionDefs();
291 qDebug() << "";
292 testAllEvals();
293
294 qDebug() << "";
295
296 return testResults();
297 }
298 else
299 {
300 Repl repl;
301 repl.start();
302 }
swissChili7babd922021-12-02 22:46:48 -0800303}