blob: ce9f4352c8a8a000944e8396498dee19a3263c83 [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:
86 qDebug() << "\033[36mEvaluate\033[0m" << function << expression << "->" << result;
87}
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
swissChilic71acc62021-12-07 08:03:37 -080094void testMatch(const QString &test, bool shouldBe, const MatchResult &result)
95{
96 if (result.success != shouldBe)
97 {
swissChili3e98c062021-12-04 22:07:38 -080098 g_numFailed++;
swissChilic71acc62021-12-07 08:03:37 -080099 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChilid17b5a12021-12-05 20:46:42 -0800100 qDebug() << "with context" << result.context;
swissChili3e98c062021-12-04 22:07:38 -0800101 }
102
swissChilic71acc62021-12-07 08:03:37 -0800103 qDebug() << "\033[36mMatchResult\033[0m" << test << result.success;
swissChilid17b5a12021-12-05 20:46:42 -0800104
swissChilic71acc62021-12-07 08:03:37 -0800105 if (result.success != shouldBe)
106 {
swissChilid17b5a12021-12-05 20:46:42 -0800107 qDebug() << "";
108 }
swissChili3e98c062021-12-04 22:07:38 -0800109}
110
swissChili682e7bc2021-12-07 09:04:54 -0800111void testMatch(QString data, QString pattern, bool shouldBe = true)
112{
113 Parser dataParser(data),
114 patternParser(pattern);
115
swissChili847a78c2021-12-09 17:44:52 -0800116 QList<Token> parsedData, parsedPattern;
117
118 dataParser.parseMany<Token>(&parsedData);
119 patternParser.parseMany<Token>(&parsedPattern);
120
swissChili07d325f2021-12-08 20:02:05 -0800121 testMatch(pattern + " = " + data, shouldBe,
swissChili847a78c2021-12-09 17:44:52 -0800122 match(parsedData, parsedPattern, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800123}
124
swissChili9dddbf72021-12-08 23:03:25 -0800125void testParseAst(QString string, QList<AstNode> equals = {})
swissChilic71acc62021-12-07 08:03:37 -0800126{
127 Parser parser{string};
128
swissChili847a78c2021-12-09 17:44:52 -0800129 QList<AstNode> result;
130 parser.parseMany<AstNode>(&result);
swissChilic71acc62021-12-07 08:03:37 -0800131
swissChili9dddbf72021-12-08 23:03:25 -0800132 if (!equals.empty() && result != equals)
133 {
134 g_numFailed++;
135 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
136 qDebug() << "Expected" << pprint(equals);
137 }
138
139 qDebug() << "\033[36mParse\033[0m" << string << pprint(result);
swissChilic71acc62021-12-07 08:03:37 -0800140}
141
swissChili8a581c62021-12-07 13:29:21 -0800142void testParseFunc(QString string)
143{
144 Parser parser{string};
swissChili847a78c2021-12-09 17:44:52 -0800145 ParseResult ret;
swissChili8a581c62021-12-07 13:29:21 -0800146
147 Function func;
148
swissChili847a78c2021-12-09 17:44:52 -0800149 if (!(ret = parser.parseFunctionDefinition(&func)))
swissChili8a581c62021-12-07 13:29:21 -0800150 {
151 g_numFailed++;
152 qDebug() << "\n\033[31mTEST FAILS:\033[0m";
swissChili323883d2022-02-20 16:35:23 -0800153 sout(pprint(ret, parser));
swissChili8a581c62021-12-07 13:29:21 -0800154 qDebug() << string;
155 }
156 else
157 {
158 qDebug() << "\033[36mFunction\033[0m";
159 qDebug().noquote() << func;
160 }
161}
162
swissChilic71acc62021-12-07 08:03:37 -0800163int testResults()
164{
165 if (g_numFailed == 0)
166 {
167 qDebug() << "\033[32mALL TESTS SUCCEEDED\033[0m";
168 }
169 else
170 {
171 qDebug().nospace() << "\033[31m" << g_numFailed << " TESTS FAILED\033[0m";
swissChili3e98c062021-12-04 22:07:38 -0800172 }
173
174 return g_numFailed;
175}
176
swissChilic71acc62021-12-07 08:03:37 -0800177void testAllMatches()
178{
179 testMatch("a = a", true, match({Token('a')}, {Token('a')}, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800180
swissChilic71acc62021-12-07 08:03:37 -0800181 testMatch("s.a = y", true, match({Token('y')}, {Token('s', "a")}, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800182
swissChili3e98c062021-12-04 22:07:38 -0800183 LTok sameTwo = {Token('s', "a"), Token('s', "a")};
swissChilic71acc62021-12-07 08:03:37 -0800184 testMatch("s.a s.a = aa", true, match({Token('a'), Token('a')}, sameTwo, VarContext()));
185 testMatch("s.a s.a = ab", false, match({Token('a'), Token('b')}, sameTwo, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800186
187 LTok sameStartEnd = {
swissChilic71acc62021-12-07 08:03:37 -0800188 Token('s', "a"),
189 Token('e', "middle"),
190 Token('s', "a")};
191 testMatch("s.a e.middle s.a = aea", true,
192 match({Token('a'), Token('e'), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800193
swissChilic71acc62021-12-07 08:03:37 -0800194 testMatch("s.a e.middle s.a = aef Hi a", true,
195 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('a')}, sameStartEnd, VarContext()));
swissChili3e98c062021-12-04 22:07:38 -0800196
swissChilic71acc62021-12-07 08:03:37 -0800197 testMatch("s.a e.middle s.a = aef Hi c", false,
198 match({Token('a'), Token('e'), Token('f'), Token("Hi"), Token('c')}, sameStartEnd, VarContext()));
swissChilid17b5a12021-12-05 20:46:42 -0800199
200 LTok parenthesized = {
swissChilic71acc62021-12-07 08:03:37 -0800201 Token(LTok({Token('s', "a")})),
202 Token('e', "Middle"),
203 Token('s', "a"),
swissChilid17b5a12021-12-05 20:46:42 -0800204 };
205 LTok parenTest1 = {
swissChilic71acc62021-12-07 08:03:37 -0800206 Token(LTok({Token('y')})),
207 Token('f'),
208 Token("MiddleStuff"),
209 Token('y')};
swissChilid17b5a12021-12-05 20:46:42 -0800210
swissChilic71acc62021-12-07 08:03:37 -0800211 testMatch("(s.a) e.Middle s.a = (y)f MiddleStuff y", true,
swissChilid17b5a12021-12-05 20:46:42 -0800212 match(parenTest1, parenthesized, VarContext()));
swissChili682e7bc2021-12-07 09:04:54 -0800213 // testMatch("(y)f Middle-stuff y", "(s.a) e.Middle s.a");
swissChilid17b5a12021-12-05 20:46:42 -0800214
swissChili682e7bc2021-12-07 09:04:54 -0800215 testMatch("(a)", "(a)");
swissChili07d325f2021-12-08 20:02:05 -0800216 testMatch("hello", "s.A e.Rest");
swissChili9dddbf72021-12-08 23:03:25 -0800217 testMatch("123", "123");
218 testMatch("(123)", "t.a");
219 testMatch("(123)", "(s.a)");
swissChili3e98c062021-12-04 22:07:38 -0800220}
221
swissChilic71acc62021-12-07 08:03:37 -0800222void testAllParses()
223{
swissChili682e7bc2021-12-07 09:04:54 -0800224 testParseAst("all symbols");
225 testParseAst("Identifier symbols Identifier");
226 testParseAst("s.A");
227 testParseAst("(s.A) Variable-quoted");
228 testParseAst("<Func-name a b (c)>");
229 testParseAst("<Prout hi>");
230 testParseAst("(Prout hi)");
231 testParseAst("(<Prout hi>)");
232 testParseAst("<If T Then (<Prout hi>) Else (<Prout sorry>)>");
233 testParseAst("(s.a) e.Middle s.a");
swissChili8a581c62021-12-07 13:29:21 -0800234 testParseAst("Hello; Goodbye");
235 testParseAst("Key = Value");
swissChili9dddbf72021-12-08 23:03:25 -0800236 testParseAst("123", {AstNode("123", 10)});
swissChili1060c0e2021-12-09 09:46:42 -0800237 testParseAst("12 00", {AstNode::fromInteger(12), AstNode::fromInteger(0)});
swissChili323883d2022-02-20 16:35:23 -0800238 testParseAst("s.A s.B", {AstNode('s', "A"), AstNode('s', "B")});
swissChili8a581c62021-12-07 13:29:21 -0800239}
240
241void testAllFunctionDefs()
242{
243 testParseFunc("Test { = HI; }");
swissChili923bd532021-12-08 22:48:58 -0800244 testParseFunc("Palindrome { = T; s.A = T; s.A e.Middle s.A = <Palindrome e.Middle>; e.Ignored = F; } ");
swissChili07d325f2021-12-08 20:02:05 -0800245}
246
247void testAllEvals()
248{
249 testEval("First {s.A e.Rest = s.A;}", "<First hello>", "h");
swissChili1060c0e2021-12-09 09:46:42 -0800250 testEval("Number { = 123; }", "<Number>", QList<Token>{Token::fromInteger(123)});
swissChilic71acc62021-12-07 08:03:37 -0800251}
252
253int main(int argc, char *argv[])
254{
swissChili7babd922021-12-02 22:46:48 -0800255 QCoreApplication a(argc, argv);
swissChili923bd532021-12-08 22:48:58 -0800256 QCoreApplication::setApplicationName("REFAL");
257 QCoreApplication::setApplicationVersion("1.0-a1");
swissChili3e98c062021-12-04 22:07:38 -0800258
swissChili923bd532021-12-08 22:48:58 -0800259 QCommandLineParser cli;
260 cli.setApplicationDescription("REFAL interpreter");
261 cli.addHelpOption();
262 cli.addVersionOption();
swissChili3e98c062021-12-04 22:07:38 -0800263
swissChili323883d2022-02-20 16:35:23 -0800264 cli.addPositionalArgument("script", "REFAL script to run");
265
swissChili1060c0e2021-12-09 09:46:42 -0800266 QCommandLineOption testOption(QStringList{"t", "test"}, "Run test suite.");
swissChili923bd532021-12-08 22:48:58 -0800267 cli.addOption(testOption);
268
269 cli.process(a);
270
swissChili323883d2022-02-20 16:35:23 -0800271 if (cli.positionalArguments().length() > 0)
272 {
273 qDebug() << "Running script" << cli.positionalArguments()[0];
274 }
275 else if (cli.isSet(testOption))
swissChili923bd532021-12-08 22:48:58 -0800276 {
277 testAllMatches();
278 qDebug() << "";
279 testAllParses();
280 qDebug() << "";
281 testAllFunctionDefs();
282 qDebug() << "";
283 testAllEvals();
284
285 qDebug() << "";
286
287 return testResults();
288 }
289 else
290 {
291 Repl repl;
292 repl.start();
293 }
swissChili7babd922021-12-02 22:46:48 -0800294}