blob: 5e00583ca9783be3a79b52f2d9168c9ce69da572 [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"
swissChilif8c74f02022-04-16 18:47:16 -070011#ifdef INCLUDE_CLI
swissChili923bd532021-12-08 22:48:58 -080012#include "Repl.h"
swissChilif8c74f02022-04-16 18:47:16 -070013#endif
swissChili9dddbf72021-12-08 23:03:25 -080014#include "PPrint.h"
swissChili3e98c062021-12-04 22:07:38 -080015
swissChili23958ca2022-02-21 19:23:34 -080016#ifdef NO_IDE
17using Application = QCoreApplication;
18#else
19#include "ide/IdeMain.h"
20#endif
21
swissChili3e98c062021-12-04 22:07:38 -080022int g_numFailed = 0;
23
swissChili1060c0e2021-12-09 09:46:42 -080024
swissChili07d325f2021-12-08 20:02:05 -080025void testEval(QString function, QString expression, QString expected)
26{
swissChili732628e2022-02-25 10:35:56 -080027 Evaluator eval;
28 Parser funcParser(function),
29 exprParser(expression),
30 resParser(expected);
swissChili07d325f2021-12-08 20:02:05 -080031
swissChili732628e2022-02-25 10:35:56 -080032 Function func;
swissChili07d325f2021-12-08 20:02:05 -080033
swissChili732628e2022-02-25 10:35:56 -080034 QList<AstNode> expr;
35 ParseResult exprRet = exprParser.parseMany<AstNode>(&expr);
36 QList<Token> res;
37 ParseResult resRet = resParser.parseMany<Token>(&res);
38 ParseResult funcRet;
swissChili07d325f2021-12-08 20:02:05 -080039
swissChili732628e2022-02-25 10:35:56 -080040 QList<Token> result;
swissChili07d325f2021-12-08 20:02:05 -080041
swissChili732628e2022-02-25 10:35:56 -080042 exprParser.skip();
43 resParser.skip();
44 while ((funcRet = funcParser.parseFunctionDefinition(&func)))
45 {
46 eval.addFunction(func);
47 }
swissChili07d325f2021-12-08 20:02:05 -080048
swissChili732628e2022-02-25 10:35:56 -080049 funcParser.skip();
swissChili07d325f2021-12-08 20:02:05 -080050
swissChili732628e2022-02-25 10:35:56 -080051 if (!exprRet || !resRet || funcRet.status() == ParseResult::INCOMPLETE)
52 {
swissChili07d325f2021-12-08 20:02:05 -080053 g_numFailed++;
54 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChili732628e2022-02-25 10:35:56 -080055 qDebug() << "Failed to fully parse expression, function or result";
swissChili323883d2022-02-20 16:35:23 -080056 qDebug() << "Function:";
57 sout(pprint(funcRet, funcParser));
58
59 qDebug() << "Expression:";
60 sout(pprint(exprRet, exprParser));
61
62 qDebug() << "Result:";
63 sout(pprint(resRet, resParser));
swissChili07d325f2021-12-08 20:02:05 -080064
swissChili732628e2022-02-25 10:35:56 -080065 goto end;
66 }
swissChili07d325f2021-12-08 20:02:05 -080067
swissChili732628e2022-02-25 10:35:56 -080068 for (const AstNode &node : expr)
69 {
70 RuntimeResult rr = eval.evaluate(node, VarContext());
swissChili07d325f2021-12-08 20:02:05 -080071
swissChili732628e2022-02-25 10:35:56 -080072 if (!rr.success())
73 {
74 g_numFailed++;
75 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
76 qDebug() << "Runtime error while evaluating" << node;
77 qDebug() << rr;
swissChili07d325f2021-12-08 20:02:05 -080078
swissChili732628e2022-02-25 10:35:56 -080079 goto end;
80 }
swissChili07d325f2021-12-08 20:02:05 -080081
swissChili732628e2022-02-25 10:35:56 -080082 result.append(rr.result());
83 }
swissChili07d325f2021-12-08 20:02:05 -080084
swissChili732628e2022-02-25 10:35:56 -080085 if (result != res)
86 {
87 g_numFailed++;
88 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
89 qDebug() << "Expected result" << res;
90 qDebug() << "Got" << result;
91 }
swissChili07d325f2021-12-08 20:02:05 -080092
93end:
swissChili918557c2022-02-20 20:16:34 -080094 qDebug() << "\033[36mEvaluate\033[0m" << function << expression << "->" << pprint(result);
swissChili07d325f2021-12-08 20:02:05 -080095}
96
swissChili1060c0e2021-12-09 09:46:42 -080097void testEval(QString function, QString expression, QList<Token> expected)
98{
swissChili732628e2022-02-25 10:35:56 -080099 testEval(function, expression, pprint(expected));
swissChili1060c0e2021-12-09 09:46:42 -0800100}
101
swissChili918557c2022-02-20 20:16:34 -0800102void testEval(QString expression, QString expected)
103{
104 testEval("", expression, expected);
105}
106
swissChilic71acc62021-12-07 08:03:37 -0800107void testMatch(const QString &test, bool shouldBe, const MatchResult &result)
108{
109 if (result.success != shouldBe)
110 {
swissChili3e98c062021-12-04 22:07:38 -0800111 g_numFailed++;
swissChilic71acc62021-12-07 08:03:37 -0800112 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChilid17b5a12021-12-05 20:46:42 -0800113 qDebug() << "with context" << result.context;
swissChili3e98c062021-12-04 22:07:38 -0800114 }
115
swissChilic71acc62021-12-07 08:03:37 -0800116 qDebug() << "\033[36mMatchResult\033[0m" << test << result.success;
swissChilid17b5a12021-12-05 20:46:42 -0800117
swissChilic71acc62021-12-07 08:03:37 -0800118 if (result.success != shouldBe)
119 {
swissChilid17b5a12021-12-05 20:46:42 -0800120 qDebug() << "";
121 }
swissChili3e98c062021-12-04 22:07:38 -0800122}
123
swissChili682e7bc2021-12-07 09:04:54 -0800124void testMatch(QString data, QString pattern, bool shouldBe = true)
125{
126 Parser dataParser(data),
127 patternParser(pattern);
128
swissChili732628e2022-02-25 10:35:56 -0800129 QList<Token> parsedData, parsedPattern;
130
131 dataParser.parseMany<Token>(&parsedData);
132 patternParser.parseMany<Token>(&parsedPattern);
swissChili847a78c2021-12-09 17:44:52 -0800133
swissChili07d325f2021-12-08 20:02:05 -0800134 testMatch(pattern + " = " + data, shouldBe,
swissChili732628e2022-02-25 10:35:56 -0800135 match(parsedData, parsedPattern, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800136}
137
swissChili9dddbf72021-12-08 23:03:25 -0800138void testParseAst(QString string, QList<AstNode> equals = {})
swissChilic71acc62021-12-07 08:03:37 -0800139{
140 Parser parser{string};
141
swissChili847a78c2021-12-09 17:44:52 -0800142 QList<AstNode> result;
swissChili732628e2022-02-25 10:35:56 -0800143 parser.parseMany<AstNode>(&result);
swissChilic71acc62021-12-07 08:03:37 -0800144
swissChili732628e2022-02-25 10:35:56 -0800145 if (!equals.empty() && result != equals)
146 {
swissChili9dddbf72021-12-08 23:03:25 -0800147 g_numFailed++;
148 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
149 qDebug() << "Expected" << pprint(equals);
swissChili732628e2022-02-25 10:35:56 -0800150 }
swissChili9dddbf72021-12-08 23:03:25 -0800151
152 qDebug() << "\033[36mParse\033[0m" << string << pprint(result);
swissChilic71acc62021-12-07 08:03:37 -0800153}
154
swissChili8a581c62021-12-07 13:29:21 -0800155void testParseFunc(QString string)
156{
157 Parser parser{string};
swissChili732628e2022-02-25 10:35:56 -0800158 ParseResult ret;
swissChili8a581c62021-12-07 13:29:21 -0800159
160 Function func;
161
swissChili847a78c2021-12-09 17:44:52 -0800162 if (!(ret = parser.parseFunctionDefinition(&func)))
swissChili8a581c62021-12-07 13:29:21 -0800163 {
164 g_numFailed++;
165 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChili323883d2022-02-20 16:35:23 -0800166 sout(pprint(ret, parser));
swissChili8a581c62021-12-07 13:29:21 -0800167 qDebug() << string;
168 }
169 else
170 {
171 qDebug() << "\033[36mFunction\033[0m";
172 qDebug().noquote() << func;
173 }
174}
175
swissChilic71acc62021-12-07 08:03:37 -0800176int testResults()
177{
178 if (g_numFailed == 0)
179 {
180 qDebug() << "\033[32mALL TESTS SUCCEEDED\033[0m";
181 }
182 else
183 {
184 qDebug().nospace() << "\033[31m" << g_numFailed << " TESTS FAILED\033[0m";
swissChili3e98c062021-12-04 22:07:38 -0800185 }
186
187 return g_numFailed;
188}
189
swissChilic71acc62021-12-07 08:03:37 -0800190void testAllMatches()
191{
192 testMatch("a = a", true, match({Token('a')}, {Token('a')}, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800193
swissChilic71acc62021-12-07 08:03:37 -0800194 testMatch("s.a = y", true, match({Token('y')}, {Token('s', "a")}, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800195
swissChili3e98c062021-12-04 22:07:38 -0800196 LTok sameTwo = {Token('s', "a"), Token('s', "a")};
swissChilic71acc62021-12-07 08:03:37 -0800197 testMatch("s.a s.a = aa", true, match({Token('a'), Token('a')}, sameTwo, VarContext()));
198 testMatch("s.a s.a = ab", false, match({Token('a'), Token('b')}, sameTwo, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800199
200 LTok sameStartEnd = {
swissChilic71acc62021-12-07 08:03:37 -0800201 Token('s', "a"),
202 Token('e', "middle"),
203 Token('s', "a")};
204 testMatch("s.a e.middle s.a = aea", true,
swissChili732628e2022-02-25 10:35:56 -0800205 match({Token('a'), Token('e'), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800206
swissChilic71acc62021-12-07 08:03:37 -0800207 testMatch("s.a e.middle s.a = aef Hi a", true,
swissChili732628e2022-02-25 10:35:56 -0800208 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800209
swissChilic71acc62021-12-07 08:03:37 -0800210 testMatch("s.a e.middle s.a = aef Hi c", false,
swissChili732628e2022-02-25 10:35:56 -0800211 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('c')}, sameStartEnd, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800212
213 LTok parenthesized = {
swissChilic71acc62021-12-07 08:03:37 -0800214 Token(LTok({Token('s', "a")})),
215 Token('e', "Middle"),
216 Token('s', "a"),
swissChilid17b5a12021-12-05 20:46:42 -0800217 };
218 LTok parenTest1 = {
swissChilic71acc62021-12-07 08:03:37 -0800219 Token(LTok({Token('y')})),
220 Token('f'),
221 Token("MiddleStuff"),
222 Token('y')};
swissChilid17b5a12021-12-05 20:46:42 -0800223
swissChilic71acc62021-12-07 08:03:37 -0800224 testMatch("(s.a) e.Middle s.a = (y)f MiddleStuff y", true,
swissChili732628e2022-02-25 10:35:56 -0800225 match(parenTest1, parenthesized, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800226 // testMatch("(y)f Middle-stuff y", "(s.a) e.Middle s.a");
swissChilid17b5a12021-12-05 20:46:42 -0800227
swissChili682e7bc2021-12-07 09:04:54 -0800228 testMatch("(a)", "(a)");
swissChili732628e2022-02-25 10:35:56 -0800229 testMatch("hello", "s.A e.Rest");
230 testMatch("123", "123");
231 testMatch("(123)", "t.a");
232 testMatch("(123)", "(s.a)");
swissChili3e98c062021-12-04 22:07:38 -0800233}
234
swissChilic71acc62021-12-07 08:03:37 -0800235void testAllParses()
236{
swissChili682e7bc2021-12-07 09:04:54 -0800237 testParseAst("all symbols");
238 testParseAst("Identifier symbols Identifier");
239 testParseAst("s.A");
240 testParseAst("(s.A) Variable-quoted");
241 testParseAst("<Func-name a b (c)>");
242 testParseAst("<Prout hi>");
243 testParseAst("(Prout hi)");
244 testParseAst("(<Prout hi>)");
245 testParseAst("<If T Then (<Prout hi>) Else (<Prout sorry>)>");
246 testParseAst("(s.a) e.Middle s.a");
swissChili8a581c62021-12-07 13:29:21 -0800247 testParseAst("Hello; Goodbye");
248 testParseAst("Key = Value");
swissChili732628e2022-02-25 10:35:56 -0800249 testParseAst("123", {AstNode("123", 10)});
250 testParseAst("12 00", {AstNode::fromInteger(12), AstNode::fromInteger(0)});
swissChili323883d2022-02-20 16:35:23 -0800251 testParseAst("s.A s.B", {AstNode('s', "A"), AstNode('s', "B")});
swissChili918557c2022-02-20 20:16:34 -0800252 testParseAst("a '=' b", {AstNode('a'), AstNode('='), AstNode('b')});
253 testParseAst("'This\\'<>#$@#^%\\n' 'another $3543543'");
swissChili8a581c62021-12-07 13:29:21 -0800254}
255
256void testAllFunctionDefs()
257{
258 testParseFunc("Test { = HI; }");
swissChili923bd532021-12-08 22:48:58 -0800259 testParseFunc("Palindrome { = T; s.A = T; s.A e.Middle s.A = <Palindrome e.Middle>; e.Ignored = F; } ");
swissChili07d325f2021-12-08 20:02:05 -0800260}
261
262void testAllEvals()
263{
swissChili732628e2022-02-25 10:35:56 -0800264 testEval("First {s.A e.Rest = s.A;}", "<First hello>", "h");
265 testEval("Number { = 123; }", "<Number>", QList<Token>{Token::fromInteger(123)});
swissChili918557c2022-02-20 20:16:34 -0800266 testEval("<Br Name '=' Jim> <Dg Name>", "Jim");
267 testEval("<Br A '=' hello> <Br A '=' 'good bye'> <Dg A> <Dg A>", "'good byehello'");
swissChilic71acc62021-12-07 08:03:37 -0800268}
269
270int main(int argc, char *argv[])
271{
swissChilie386bc72022-02-24 21:31:31 -0800272 QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
swissChilid2af6ad2022-04-16 14:42:17 -0700273#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
274 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
275#endif
swissChilie386bc72022-02-24 21:31:31 -0800276
swissChili23958ca2022-02-21 19:23:34 -0800277 Application a(argc, argv);
278 a.setApplicationName("REFAL");
279 a.setApplicationVersion("1.0-a1");
swissChili3e98c062021-12-04 22:07:38 -0800280
swissChili732628e2022-02-25 10:35:56 -0800281 QCommandLineParser cli;
282 cli.setApplicationDescription("REFAL interpreter");
283 cli.addHelpOption();
284 cli.addVersionOption();
swissChili3e98c062021-12-04 22:07:38 -0800285
swissChili323883d2022-02-20 16:35:23 -0800286 cli.addPositionalArgument("script", "REFAL script to run");
287
swissChili732628e2022-02-25 10:35:56 -0800288 QCommandLineOption testOption(QStringList{"t", "test"}, "Run test suite.");
289 cli.addOption(testOption);
swissChili23958ca2022-02-21 19:23:34 -0800290 QCommandLineOption replOption(QStringList({"r", "repl"}), "Start CLI REPL");
291 cli.addOption(replOption);
swissChili923bd532021-12-08 22:48:58 -0800292
swissChili732628e2022-02-25 10:35:56 -0800293 cli.process(a);
swissChili923bd532021-12-08 22:48:58 -0800294
swissChili323883d2022-02-20 16:35:23 -0800295 if (cli.positionalArguments().length() > 0)
296 {
297 qDebug() << "Running script" << cli.positionalArguments()[0];
298 }
299 else if (cli.isSet(testOption))
swissChili732628e2022-02-25 10:35:56 -0800300 {
301 testAllMatches();
302 qDebug() << "";
303 testAllParses();
304 qDebug() << "";
305 testAllFunctionDefs();
306 qDebug() << "";
307 testAllEvals();
swissChili923bd532021-12-08 22:48:58 -0800308
swissChili732628e2022-02-25 10:35:56 -0800309 qDebug() << "";
swissChili923bd532021-12-08 22:48:58 -0800310
swissChili732628e2022-02-25 10:35:56 -0800311 return testResults();
312 }
swissChilif8c74f02022-04-16 18:47:16 -0700313#ifdef INCLUDE_CLI
swissChili23958ca2022-02-21 19:23:34 -0800314 else if (cli.isSet(replOption))
swissChili732628e2022-02-25 10:35:56 -0800315 {
316 Repl repl;
317 repl.start();
318 }
swissChilif8c74f02022-04-16 18:47:16 -0700319#endif
swissChili23958ca2022-02-21 19:23:34 -0800320 else
321 {
322 return ideMain(&a);
323 }
swissChili7babd922021-12-02 22:46:48 -0800324}