blob: 42cc0a43c240afad90683035e88e591465b6806b [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{
swissChili732628e2022-02-25 10:35:56 -080025 Evaluator eval;
26 Parser funcParser(function),
27 exprParser(expression),
28 resParser(expected);
swissChili07d325f2021-12-08 20:02:05 -080029
swissChili732628e2022-02-25 10:35:56 -080030 Function func;
swissChili07d325f2021-12-08 20:02:05 -080031
swissChili732628e2022-02-25 10:35:56 -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
swissChili732628e2022-02-25 10:35:56 -080038 QList<Token> result;
swissChili07d325f2021-12-08 20:02:05 -080039
swissChili732628e2022-02-25 10:35:56 -080040 exprParser.skip();
41 resParser.skip();
42 while ((funcRet = funcParser.parseFunctionDefinition(&func)))
43 {
44 eval.addFunction(func);
45 }
swissChili07d325f2021-12-08 20:02:05 -080046
swissChili732628e2022-02-25 10:35:56 -080047 funcParser.skip();
swissChili07d325f2021-12-08 20:02:05 -080048
swissChili732628e2022-02-25 10:35:56 -080049 if (!exprRet || !resRet || funcRet.status() == ParseResult::INCOMPLETE)
50 {
swissChili07d325f2021-12-08 20:02:05 -080051 g_numFailed++;
52 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChili732628e2022-02-25 10:35:56 -080053 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
swissChili732628e2022-02-25 10:35:56 -080063 goto end;
64 }
swissChili07d325f2021-12-08 20:02:05 -080065
swissChili732628e2022-02-25 10:35:56 -080066 for (const AstNode &node : expr)
67 {
68 RuntimeResult rr = eval.evaluate(node, VarContext());
swissChili07d325f2021-12-08 20:02:05 -080069
swissChili732628e2022-02-25 10:35:56 -080070 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;
swissChili07d325f2021-12-08 20:02:05 -080076
swissChili732628e2022-02-25 10:35:56 -080077 goto end;
78 }
swissChili07d325f2021-12-08 20:02:05 -080079
swissChili732628e2022-02-25 10:35:56 -080080 result.append(rr.result());
81 }
swissChili07d325f2021-12-08 20:02:05 -080082
swissChili732628e2022-02-25 10:35:56 -080083 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 }
swissChili07d325f2021-12-08 20:02:05 -080090
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{
swissChili732628e2022-02-25 10:35:56 -080097 testEval(function, expression, pprint(expected));
swissChili1060c0e2021-12-09 09:46:42 -080098}
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
swissChili732628e2022-02-25 10:35:56 -0800127 QList<Token> parsedData, parsedPattern;
128
129 dataParser.parseMany<Token>(&parsedData);
130 patternParser.parseMany<Token>(&parsedPattern);
swissChili847a78c2021-12-09 17:44:52 -0800131
swissChili07d325f2021-12-08 20:02:05 -0800132 testMatch(pattern + " = " + data, shouldBe,
swissChili732628e2022-02-25 10:35:56 -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;
swissChili732628e2022-02-25 10:35:56 -0800141 parser.parseMany<AstNode>(&result);
swissChilic71acc62021-12-07 08:03:37 -0800142
swissChili732628e2022-02-25 10:35:56 -0800143 if (!equals.empty() && result != equals)
144 {
swissChili9dddbf72021-12-08 23:03:25 -0800145 g_numFailed++;
146 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
147 qDebug() << "Expected" << pprint(equals);
swissChili732628e2022-02-25 10:35:56 -0800148 }
swissChili9dddbf72021-12-08 23:03:25 -0800149
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};
swissChili732628e2022-02-25 10:35:56 -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,
swissChili732628e2022-02-25 10:35:56 -0800203 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,
swissChili732628e2022-02-25 10:35:56 -0800206 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,
swissChili732628e2022-02-25 10:35:56 -0800209 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,
swissChili732628e2022-02-25 10:35:56 -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)");
swissChili732628e2022-02-25 10:35:56 -0800227 testMatch("hello", "s.A e.Rest");
228 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");
swissChili732628e2022-02-25 10:35:56 -0800247 testParseAst("123", {AstNode("123", 10)});
248 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{
swissChili732628e2022-02-25 10:35:56 -0800262 testEval("First {s.A e.Rest = s.A;}", "<First hello>", "h");
263 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{
swissChilie386bc72022-02-24 21:31:31 -0800270 QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
swissChilid2af6ad2022-04-16 14:42:17 -0700271#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
272 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
273#endif
swissChilie386bc72022-02-24 21:31:31 -0800274
swissChili23958ca2022-02-21 19:23:34 -0800275 Application a(argc, argv);
276 a.setApplicationName("REFAL");
277 a.setApplicationVersion("1.0-a1");
swissChili3e98c062021-12-04 22:07:38 -0800278
swissChili732628e2022-02-25 10:35:56 -0800279 QCommandLineParser cli;
280 cli.setApplicationDescription("REFAL interpreter");
281 cli.addHelpOption();
282 cli.addVersionOption();
swissChili3e98c062021-12-04 22:07:38 -0800283
swissChili323883d2022-02-20 16:35:23 -0800284 cli.addPositionalArgument("script", "REFAL script to run");
285
swissChili732628e2022-02-25 10:35:56 -0800286 QCommandLineOption testOption(QStringList{"t", "test"}, "Run test suite.");
287 cli.addOption(testOption);
swissChili23958ca2022-02-21 19:23:34 -0800288 QCommandLineOption replOption(QStringList({"r", "repl"}), "Start CLI REPL");
289 cli.addOption(replOption);
swissChili923bd532021-12-08 22:48:58 -0800290
swissChili732628e2022-02-25 10:35:56 -0800291 cli.process(a);
swissChili923bd532021-12-08 22:48:58 -0800292
swissChili323883d2022-02-20 16:35:23 -0800293 if (cli.positionalArguments().length() > 0)
294 {
295 qDebug() << "Running script" << cli.positionalArguments()[0];
296 }
297 else if (cli.isSet(testOption))
swissChili732628e2022-02-25 10:35:56 -0800298 {
299 testAllMatches();
300 qDebug() << "";
301 testAllParses();
302 qDebug() << "";
303 testAllFunctionDefs();
304 qDebug() << "";
305 testAllEvals();
swissChili923bd532021-12-08 22:48:58 -0800306
swissChili732628e2022-02-25 10:35:56 -0800307 qDebug() << "";
swissChili923bd532021-12-08 22:48:58 -0800308
swissChili732628e2022-02-25 10:35:56 -0800309 return testResults();
310 }
swissChili23958ca2022-02-21 19:23:34 -0800311 else if (cli.isSet(replOption))
swissChili732628e2022-02-25 10:35:56 -0800312 {
313 Repl repl;
314 repl.start();
315 }
swissChili23958ca2022-02-21 19:23:34 -0800316 else
317 {
318 return ideMain(&a);
319 }
swissChili7babd922021-12-02 22:46:48 -0800320}