blob: 32f8e6fbd48f945b66f5d3aee6fc4668bd4061b4 [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
swissChili23958ca2022-02-21 19:23:34 -080014#ifdef NO_IDE
15using Application = QCoreApplication;
16#else
17#include "ide/IdeMain.h"
18#endif
19
swissChili3e98c062021-12-04 22:07:38 -080020int g_numFailed = 0;
21
swissChili1060c0e2021-12-09 09:46:42 -080022
swissChili07d325f2021-12-08 20:02:05 -080023void testEval(QString function, QString expression, QString expected)
24{
25 Evaluator eval;
26 Parser funcParser(function),
27 exprParser(expression),
28 resParser(expected);
29
30 Function func;
31
swissChili847a78c2021-12-09 17:44:52 -080032 QList<AstNode> expr;
33 ParseResult exprRet = exprParser.parseMany<AstNode>(&expr);
34 QList<Token> res;
35 ParseResult resRet = resParser.parseMany<Token>(&res);
36 ParseResult funcRet;
swissChili07d325f2021-12-08 20:02:05 -080037
38 QList<Token> result;
39
40 exprParser.skip();
41 resParser.skip();
swissChili847a78c2021-12-09 17:44:52 -080042 while ((funcRet = funcParser.parseFunctionDefinition(&func)))
swissChili07d325f2021-12-08 20:02:05 -080043 {
44 eval.addFunction(func);
45 }
46
47 funcParser.skip();
48
swissChili847a78c2021-12-09 17:44:52 -080049 if (!exprRet || !resRet || funcRet.status() == ParseResult::INCOMPLETE)
swissChili07d325f2021-12-08 20:02:05 -080050 {
51 g_numFailed++;
52 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
53 qDebug() << "Failed to fully parse expression, function or result";
swissChili323883d2022-02-20 16:35:23 -080054 qDebug() << "Function:";
55 sout(pprint(funcRet, funcParser));
56
57 qDebug() << "Expression:";
58 sout(pprint(exprRet, exprParser));
59
60 qDebug() << "Result:";
61 sout(pprint(resRet, resParser));
swissChili07d325f2021-12-08 20:02:05 -080062
63 goto end;
64 }
65
66 for (const AstNode &node : expr)
67 {
68 RuntimeResult rr = eval.evaluate(node, VarContext());
69
70 if (!rr.success())
71 {
72 g_numFailed++;
73 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
74 qDebug() << "Runtime error while evaluating" << node;
75 qDebug() << rr;
76
77 goto end;
78 }
79
80 result.append(rr.result());
81 }
82
83 if (result != res)
84 {
85 g_numFailed++;
86 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
87 qDebug() << "Expected result" << res;
88 qDebug() << "Got" << result;
89 }
90
91end:
swissChili918557c2022-02-20 20:16:34 -080092 qDebug() << "\033[36mEvaluate\033[0m" << function << expression << "->" << pprint(result);
swissChili07d325f2021-12-08 20:02:05 -080093}
94
swissChili1060c0e2021-12-09 09:46:42 -080095void testEval(QString function, QString expression, QList<Token> expected)
96{
97 testEval(function, expression, pprint(expected));
98}
99
swissChili918557c2022-02-20 20:16:34 -0800100void testEval(QString expression, QString expected)
101{
102 testEval("", expression, expected);
103}
104
swissChilic71acc62021-12-07 08:03:37 -0800105void testMatch(const QString &test, bool shouldBe, const MatchResult &result)
106{
107 if (result.success != shouldBe)
108 {
swissChili3e98c062021-12-04 22:07:38 -0800109 g_numFailed++;
swissChilic71acc62021-12-07 08:03:37 -0800110 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChilid17b5a12021-12-05 20:46:42 -0800111 qDebug() << "with context" << result.context;
swissChili3e98c062021-12-04 22:07:38 -0800112 }
113
swissChilic71acc62021-12-07 08:03:37 -0800114 qDebug() << "\033[36mMatchResult\033[0m" << test << result.success;
swissChilid17b5a12021-12-05 20:46:42 -0800115
swissChilic71acc62021-12-07 08:03:37 -0800116 if (result.success != shouldBe)
117 {
swissChilid17b5a12021-12-05 20:46:42 -0800118 qDebug() << "";
119 }
swissChili3e98c062021-12-04 22:07:38 -0800120}
121
swissChili682e7bc2021-12-07 09:04:54 -0800122void testMatch(QString data, QString pattern, bool shouldBe = true)
123{
124 Parser dataParser(data),
125 patternParser(pattern);
126
swissChili847a78c2021-12-09 17:44:52 -0800127 QList<Token> parsedData, parsedPattern;
128
129 dataParser.parseMany<Token>(&parsedData);
130 patternParser.parseMany<Token>(&parsedPattern);
131
swissChili07d325f2021-12-08 20:02:05 -0800132 testMatch(pattern + " = " + data, shouldBe,
swissChili847a78c2021-12-09 17:44:52 -0800133 match(parsedData, parsedPattern, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800134}
135
swissChili9dddbf72021-12-08 23:03:25 -0800136void testParseAst(QString string, QList<AstNode> equals = {})
swissChilic71acc62021-12-07 08:03:37 -0800137{
138 Parser parser{string};
139
swissChili847a78c2021-12-09 17:44:52 -0800140 QList<AstNode> result;
141 parser.parseMany<AstNode>(&result);
swissChilic71acc62021-12-07 08:03:37 -0800142
swissChili9dddbf72021-12-08 23:03:25 -0800143 if (!equals.empty() && result != equals)
144 {
145 g_numFailed++;
146 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
147 qDebug() << "Expected" << pprint(equals);
148 }
149
150 qDebug() << "\033[36mParse\033[0m" << string << pprint(result);
swissChilic71acc62021-12-07 08:03:37 -0800151}
152
swissChili8a581c62021-12-07 13:29:21 -0800153void testParseFunc(QString string)
154{
155 Parser parser{string};
swissChili847a78c2021-12-09 17:44:52 -0800156 ParseResult ret;
swissChili8a581c62021-12-07 13:29:21 -0800157
158 Function func;
159
swissChili847a78c2021-12-09 17:44:52 -0800160 if (!(ret = parser.parseFunctionDefinition(&func)))
swissChili8a581c62021-12-07 13:29:21 -0800161 {
162 g_numFailed++;
163 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChili323883d2022-02-20 16:35:23 -0800164 sout(pprint(ret, parser));
swissChili8a581c62021-12-07 13:29:21 -0800165 qDebug() << string;
166 }
167 else
168 {
169 qDebug() << "\033[36mFunction\033[0m";
170 qDebug().noquote() << func;
171 }
172}
173
swissChilic71acc62021-12-07 08:03:37 -0800174int testResults()
175{
176 if (g_numFailed == 0)
177 {
178 qDebug() << "\033[32mALL TESTS SUCCEEDED\033[0m";
179 }
180 else
181 {
182 qDebug().nospace() << "\033[31m" << g_numFailed << " TESTS FAILED\033[0m";
swissChili3e98c062021-12-04 22:07:38 -0800183 }
184
185 return g_numFailed;
186}
187
swissChilic71acc62021-12-07 08:03:37 -0800188void testAllMatches()
189{
190 testMatch("a = a", true, match({Token('a')}, {Token('a')}, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800191
swissChilic71acc62021-12-07 08:03:37 -0800192 testMatch("s.a = y", true, match({Token('y')}, {Token('s', "a")}, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800193
swissChili3e98c062021-12-04 22:07:38 -0800194 LTok sameTwo = {Token('s', "a"), Token('s', "a")};
swissChilic71acc62021-12-07 08:03:37 -0800195 testMatch("s.a s.a = aa", true, match({Token('a'), Token('a')}, sameTwo, VarContext()));
196 testMatch("s.a s.a = ab", false, match({Token('a'), Token('b')}, sameTwo, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800197
198 LTok sameStartEnd = {
swissChilic71acc62021-12-07 08:03:37 -0800199 Token('s', "a"),
200 Token('e', "middle"),
201 Token('s', "a")};
202 testMatch("s.a e.middle s.a = aea", true,
203 match({Token('a'), Token('e'), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800204
swissChilic71acc62021-12-07 08:03:37 -0800205 testMatch("s.a e.middle s.a = aef Hi a", true,
206 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800207
swissChilic71acc62021-12-07 08:03:37 -0800208 testMatch("s.a e.middle s.a = aef Hi c", false,
209 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('c')}, sameStartEnd, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800210
211 LTok parenthesized = {
swissChilic71acc62021-12-07 08:03:37 -0800212 Token(LTok({Token('s', "a")})),
213 Token('e', "Middle"),
214 Token('s', "a"),
swissChilid17b5a12021-12-05 20:46:42 -0800215 };
216 LTok parenTest1 = {
swissChilic71acc62021-12-07 08:03:37 -0800217 Token(LTok({Token('y')})),
218 Token('f'),
219 Token("MiddleStuff"),
220 Token('y')};
swissChilid17b5a12021-12-05 20:46:42 -0800221
swissChilic71acc62021-12-07 08:03:37 -0800222 testMatch("(s.a) e.Middle s.a = (y)f MiddleStuff y", true,
swissChilid17b5a12021-12-05 20:46:42 -0800223 match(parenTest1, parenthesized, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800224 // testMatch("(y)f Middle-stuff y", "(s.a) e.Middle s.a");
swissChilid17b5a12021-12-05 20:46:42 -0800225
swissChili682e7bc2021-12-07 09:04:54 -0800226 testMatch("(a)", "(a)");
swissChili07d325f2021-12-08 20:02:05 -0800227 testMatch("hello", "s.A e.Rest");
swissChili9dddbf72021-12-08 23:03:25 -0800228 testMatch("123", "123");
229 testMatch("(123)", "t.a");
230 testMatch("(123)", "(s.a)");
swissChili3e98c062021-12-04 22:07:38 -0800231}
232
swissChilic71acc62021-12-07 08:03:37 -0800233void testAllParses()
234{
swissChili682e7bc2021-12-07 09:04:54 -0800235 testParseAst("all symbols");
236 testParseAst("Identifier symbols Identifier");
237 testParseAst("s.A");
238 testParseAst("(s.A) Variable-quoted");
239 testParseAst("<Func-name a b (c)>");
240 testParseAst("<Prout hi>");
241 testParseAst("(Prout hi)");
242 testParseAst("(<Prout hi>)");
243 testParseAst("<If T Then (<Prout hi>) Else (<Prout sorry>)>");
244 testParseAst("(s.a) e.Middle s.a");
swissChili8a581c62021-12-07 13:29:21 -0800245 testParseAst("Hello; Goodbye");
246 testParseAst("Key = Value");
swissChili9dddbf72021-12-08 23:03:25 -0800247 testParseAst("123", {AstNode("123", 10)});
swissChili1060c0e2021-12-09 09:46:42 -0800248 testParseAst("12 00", {AstNode::fromInteger(12), AstNode::fromInteger(0)});
swissChili323883d2022-02-20 16:35:23 -0800249 testParseAst("s.A s.B", {AstNode('s', "A"), AstNode('s', "B")});
swissChili918557c2022-02-20 20:16:34 -0800250 testParseAst("a '=' b", {AstNode('a'), AstNode('='), AstNode('b')});
251 testParseAst("'This\\'<>#$@#^%\\n' 'another $3543543'");
swissChili8a581c62021-12-07 13:29:21 -0800252}
253
254void testAllFunctionDefs()
255{
256 testParseFunc("Test { = HI; }");
swissChili923bd532021-12-08 22:48:58 -0800257 testParseFunc("Palindrome { = T; s.A = T; s.A e.Middle s.A = <Palindrome e.Middle>; e.Ignored = F; } ");
swissChili07d325f2021-12-08 20:02:05 -0800258}
259
260void testAllEvals()
261{
262 testEval("First {s.A e.Rest = s.A;}", "<First hello>", "h");
swissChili1060c0e2021-12-09 09:46:42 -0800263 testEval("Number { = 123; }", "<Number>", QList<Token>{Token::fromInteger(123)});
swissChili918557c2022-02-20 20:16:34 -0800264 testEval("<Br Name '=' Jim> <Dg Name>", "Jim");
265 testEval("<Br A '=' hello> <Br A '=' 'good bye'> <Dg A> <Dg A>", "'good byehello'");
swissChilic71acc62021-12-07 08:03:37 -0800266}
267
268int main(int argc, char *argv[])
269{
swissChili23958ca2022-02-21 19:23:34 -0800270 Application a(argc, argv);
271 a.setApplicationName("REFAL");
272 a.setApplicationVersion("1.0-a1");
swissChili3e98c062021-12-04 22:07:38 -0800273
swissChili923bd532021-12-08 22:48:58 -0800274 QCommandLineParser cli;
275 cli.setApplicationDescription("REFAL interpreter");
276 cli.addHelpOption();
277 cli.addVersionOption();
swissChili3e98c062021-12-04 22:07:38 -0800278
swissChili323883d2022-02-20 16:35:23 -0800279 cli.addPositionalArgument("script", "REFAL script to run");
280
swissChili1060c0e2021-12-09 09:46:42 -0800281 QCommandLineOption testOption(QStringList{"t", "test"}, "Run test suite.");
swissChili923bd532021-12-08 22:48:58 -0800282 cli.addOption(testOption);
swissChili23958ca2022-02-21 19:23:34 -0800283 QCommandLineOption replOption(QStringList({"r", "repl"}), "Start CLI REPL");
284 cli.addOption(replOption);
swissChili923bd532021-12-08 22:48:58 -0800285
286 cli.process(a);
287
swissChili323883d2022-02-20 16:35:23 -0800288 if (cli.positionalArguments().length() > 0)
289 {
290 qDebug() << "Running script" << cli.positionalArguments()[0];
291 }
292 else if (cli.isSet(testOption))
swissChili923bd532021-12-08 22:48:58 -0800293 {
294 testAllMatches();
295 qDebug() << "";
296 testAllParses();
297 qDebug() << "";
298 testAllFunctionDefs();
299 qDebug() << "";
300 testAllEvals();
301
302 qDebug() << "";
303
304 return testResults();
305 }
swissChili23958ca2022-02-21 19:23:34 -0800306 else if (cli.isSet(replOption))
swissChili923bd532021-12-08 22:48:58 -0800307 {
308 Repl repl;
309 repl.start();
310 }
swissChili23958ca2022-02-21 19:23:34 -0800311 else
312 {
313 return ideMain(&a);
314 }
swissChili7babd922021-12-02 22:46:48 -0800315}