diff --git a/.gitignore b/.gitignore index c40d955..f2d8a4c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ **/*.log **/*.out **/*.synctex.gz -**/*.xdv \ No newline at end of file +**/*.xdv +tests/*.output \ No newline at end of file diff --git a/documentation/plthy.pdf b/documentation/plthy.pdf index abb02c2..46b1109 100644 Binary files a/documentation/plthy.pdf and b/documentation/plthy.pdf differ diff --git a/documentation/plthy.tex b/documentation/plthy.tex index 05d9301..a6da828 100644 --- a/documentation/plthy.tex +++ b/documentation/plthy.tex @@ -1,5 +1,11 @@ \documentclass[a4paper]{paper} \usepackage[margin=2.5cm]{geometry} +\usepackage{enumitem} + +\def\threedigits#1{% +\ifnum#1<100 0\fi +\ifnum#1<10 0\fi +\number#1} \begin{document} \begin{center} @@ -16,20 +22,33 @@ \textit{statement} & $\rightarrow$ & \textit{statement} \texttt{because} \textit{expression} \\ \hline \textit{statement} & $\rightarrow$ & \texttt{until} \textit{expression} \textit{statement} \\ \hline \textit{statement} & $\rightarrow$ & \textit{expression} \texttt{->} \textbf{id} \\ \hline - \textit{statement} & $\rightarrow$ & \texttt{define} \textbf{function} \texttt{<} \textbf{numeral} \texttt{>} \texttt{as} \textit{statement} \\ \hline + \textit{statement} & $\rightarrow$ & \texttt{define} \textbf{function} \texttt{<} \textbf{int} \texttt{>} \texttt{as} \textit{statement} \\ \hline \textit{statement} & $\rightarrow$ & \texttt{return} \textit{expression} \\ \hline\hline \textit{command} & $\rightarrow$ & \textbf{builtin} \texttt{<} \textit{expressions} \texttt{>} \\ \hline \textit{command} & $\rightarrow$ & \texttt{"} \textbf{function} \texttt{" <} \textit{expressions} \texttt{>} \\ \hline\hline \textit{expressions} & $\rightarrow$ & \\ \hline \textit{expressions} & $\rightarrow$ & \textit{expression} \texttt{;} \textit{expressions} \\ \hline\hline \textit{expression} & $\rightarrow$ & \textbf{string} \\ \hline - \textit{expression} & $\rightarrow$ & \textbf{numeral} \\ \hline + \textit{expression} & $\rightarrow$ & \textbf{int} \\ \hline + \textit{expression} & $\rightarrow$ & \textbf{float} \\ \hline \textit{expression} & $\rightarrow$ & \textbf{boolean} \\ \hline \textit{expression} & $\rightarrow$ & \texttt{\{} \textit{expressions} \texttt{\}} \\ \hline + \textit{expression} & $\rightarrow$ & \textit{expression} \texttt{\{} \textit{expression} \texttt{\}} \\ \hline \textit{expression} & $\rightarrow$ & \texttt{(} \textit{expression} \texttt{)} \\ \hline \textit{expression} & $\rightarrow$ & \textit{expression} \textbf{binop} \textit{expression} \\ \hline \textit{expression} & $\rightarrow$ & \texttt{variable} \textbf{id}\\ \hline \textit{expression} & $\rightarrow$ & \textit{statement} \\ \hline \end{tabular} \end{center} + + \section{Errors} + \begin{enumerate}[label={\textbf{E\protect\threedigits{\theenumi}:}}, leftmargin = *] + \item No greeting + \item No valediction + \item Unexpected token + \item Random compiler error + \item Unknown builtin + \item Wrong number of arguments for builtin + \item Unknown binop + \end{enumerate} \end{document} \ No newline at end of file diff --git a/examples/guessing_game.plthy b/examples/guessing_game.plthy new file mode 100644 index 0000000..687cd9d --- /dev/null +++ b/examples/guessing_game.plthy @@ -0,0 +1,10 @@ +hello| +$do random<1;128;> -> n| +$0 -> guess| +until variable guess = variable n [ + $do input<'guess: ';> -> guess| + do print<'too high';> if variable guess > variable n| + do print<'too low';> if variable guess < variable n| + do print<'correct!';> if variable guess = variable n| +]| +goodbye| \ No newline at end of file diff --git a/plthy b/plthy index d036cea..89afb42 100755 --- a/plthy +++ b/plthy @@ -26,8 +26,6 @@ def main(): parser = Parser() tokens = lexer.lex(program_text) - # for t in tokens: - # print(t) program = parser.parse(tokens) diff --git a/plthy_impl/ast_nodes.py b/plthy_impl/ast_nodes.py index acc2a45..aaf28c6 100644 --- a/plthy_impl/ast_nodes.py +++ b/plthy_impl/ast_nodes.py @@ -16,10 +16,16 @@ class Exp(BaseBox): def eval(self, vtable, ftable): return vtable, ftable, None -class ExpNumeral(Exp): + def __repr__(self) -> str: + return "exp()" + +class ExpInt(Exp): def __init__(self, value: int): self.value = value + def __repr__(self) -> str: + return f"exp_int({self.value})" + def eval(self, vtable, ftable): return vtable, ftable, self.value @@ -27,6 +33,9 @@ class ExpString(Exp): def __init__(self, value: str): self.value = value + def __repr__(self) -> str: + return f"exp_string({self.value})" + def eval(self, vtable, ftable): return vtable, ftable, self.value @@ -34,6 +43,9 @@ class ExpVariable(Exp): def __init__(self, variable_id: str): self.variable_name = variable_id + def __repr__(self) -> str: + return f"exp_variable({self.variable_name})" + def eval(self, vtable, ftable): return vtable, ftable, vtable[self.variable_name] @@ -43,34 +55,81 @@ class ExpABinop(Exp): self.exp1 = exp1 self.exp2 = exp2 + def __repr__(self) -> str: + return f"exp_a_binop({self.op}, {self.exp1}, {self.exp2})" + def eval(self, vtable, ftable): + vtable, ftable, r1 = self.exp1.eval(vtable, ftable) + if isinstance(r1,str): + if r1.isdigit(): + r1 = int(r1) + else: + r1 = float(r1) + vtable, ftable, r2 = self.exp2.eval(vtable, ftable) + if isinstance(r2,str): + if r2.isdigit(): + r2 = int(r2) + else: + r2 = float(r2) if self.op == "+": - vtable, ftable, r1 = self.exp1.eval(vtable, ftable) - vtable, ftable, r2 = self.exp2.eval(vtable, ftable) return vtable, ftable, r1+r2 elif self.op == "-": - vtable, ftable, r1 = self.exp1.eval(vtable, ftable) - vtable, ftable, r2 = self.exp2.eval(vtable, ftable) return vtable, ftable, r1-r2 elif self.op == "*": - vtable, ftable, r1 = self.exp1.eval(vtable, ftable) - vtable, ftable, r2 = self.exp2.eval(vtable, ftable) return vtable, ftable, r1*r2 elif self.op == "/": - vtable, ftable, r1 = self.exp1.eval(vtable, ftable) - vtable, ftable, r2 = self.exp2.eval(vtable, ftable) return vtable, ftable, r1/r2 elif self.op == "=": - vtable, ftable, r1 = self.exp1.eval(vtable, ftable) - vtable, ftable, r2 = self.exp2.eval(vtable, ftable) return vtable, ftable, r1 == r2 + elif self.op == ">": + return vtable, ftable, r1 > r2 + elif self.op == "<": + return vtable, ftable, r1 < r2 else: - raise Exception(f"Binop {self.op} not implemented") + print(f"E007: Unknown binop {self.op}") + exit() + +class ExpList(Exp): + def __init__(self, expressions: list[Exp]): + self.list = expressions + + def __repr__(self) -> str: + return f"exp_list([{rep_join(self.list)}])" + + def eval(self, vtable, ftable): + results = [] + for element in self.list: + vtable, ftable, result = element.eval(vtable, ftable) + results.append(result) + + return vtable, ftable, results + +class ExpIndex(Exp): + def __init__(self, items: ExpList, index: Exp): + self.list = items + self.index = index + + def __repr__(self) -> str: + return f"exp_index({self.list}, {self.index})" + + def eval(self, vtable, ftable): + vtable, ftable, elements = self.list.eval(vtable, ftable) + vtable, ftable, index = self.index.eval(vtable, ftable) + + if len(elements) > index: + result = elements[index] + else: + result = None + + return vtable, ftable, result class ExpArg(Exp): def __init__(self, arg: int): self.arg = arg + def __repr__(self) -> str: + return f"exp_arg({self.arg})" + def eval(self, vtable, ftable): n = vtable["#ARGS"] - self.arg @@ -80,11 +139,17 @@ class Command(BaseBox): def eval(self, vtable, ftable): return vtable, ftable, None + def __repr__(self) -> str: + return f"command()" + class Builtin(Command): def __init__(self, builtin: str, args: list[Exp]): self.builtin = builtin self.args = args + def __repr__(self) -> str: + return f"builtin({self.builtin}, {self.args})" + def eval(self, vtable, ftable): if self.builtin == "print": prints = [] @@ -94,14 +159,41 @@ class Builtin(Command): print(" ".join(prints)) - return vtable, ftable, None + return vtable, ftable, " ".join(prints) + elif self.builtin == "input": + if not self.args == []: + prints = [] + for arg in self.args: + vtable, ftable, result = arg.eval(vtable, ftable) + prints.append(str(result)) + + print(" ".join(prints), end="") + else: + print("> ", end="") + + result = input() + return vtable, ftable, result + elif self.builtin == "random": + if not len(self.args) == 2: + print(f"E006: Wrong number of arguments for builtin '{self.builtin}' ({len(self.args)})") + exit() + + vtable, ftable, r1 = self.args[0].eval(vtable, ftable) + vtable, ftable, r2 = self.args[1].eval(vtable, ftable) + + r = random.randint(r1, r2) + + return vtable, ftable, r else: - raise Exception(f"Unknown builtin {self.builtin}") + raise Exception(f"E005: Unknown builtin {self.builtin}") class Do(BaseBox): def __init__(self, command: Command): self.command = command + def __repr__(self) -> str: + return f"do({self.command})" + def eval(self, vtable, ftable): return self.command.eval(vtable, ftable) @@ -109,17 +201,23 @@ class Statement(BaseBox): def eval(self, vtable, ftable): return vtable, ftable, None + def __repr__(self) -> str: + return f"statement()" + class StatementSet(Statement): def __init__(self, expression: Exp, variable: str): self.expression = expression self.variable_name = variable + def __repr__(self) -> str: + return f"set({self.expression}, {self.variable_name})" + def eval(self, vtable, ftable): vtable, ftable, result = self.expression.eval( vtable, ftable ) vtable[self.variable_name] = result - return vtable, ftable, None + return vtable, ftable, result class StatementDefine(Statement): def __init__(self, function_name : str, parameters: int, statement: Statement): @@ -127,14 +225,20 @@ class StatementDefine(Statement): self.statement = statement self.parameters = parameters + def __repr__(self) -> str: + return f"define({self.function_name}, {self.statement}, {self.parameters})" + def eval(self, vtable, ftable): ftable[self.function_name] = (self.parameters, self.statement) - return vtable, ftable, None + return vtable, ftable, (self.parameters, self.statement) class StatementReturn(Statement): def __init__(self, expression: Exp): self.value = expression + def __repr__(self) -> str: + return f"return({self.value})" + def eval(self, vtable, ftable): vtable, ftable, result = self.value.eval(vtable, ftable) vtable["#return"] = result @@ -144,6 +248,9 @@ class Scope(Statement): def __init__(self, statements: list[Statement]): self.statements = statements + def __repr__(self) -> str: + return f"scope([{rep_join(self.statements)}])" + def eval(self, vtable, ftable): result = None for statement in self.statements: @@ -160,6 +267,9 @@ class Call(Statement): self.function_name = function_name self.arguments = arguments + def __repr__(self) -> str: + return f"call({self.function_name}, {self.arguments})" + def eval(self, vtable, ftable): assert len(self.arguments) == ftable[self.function_name][0] @@ -189,29 +299,61 @@ class StatementIf(Statement): self.statement = statement self.condition = condition + def __repr__(self) -> str: + return f"if({self.statement}, {self.condition})" + def eval(self, vtable, ftable): vtable, ftable, result = self.condition.eval(vtable, ftable) if result: return self.statement.eval(vtable, ftable) else: - return vtable, ftable, None + return vtable, ftable, result + +class StatementUntil(Statement): + def __init__(self, statement: Statement, condition): + self.statement = statement + self.condition = condition + + def __repr__(self) -> str: + return f"until({self.statement}, {self.condition})" + + def eval(self, vtable, ftable): + while True: + vtable, ftable, result = self.condition.eval(vtable, ftable) + if not result: + vtable, ftable, _ = self.statement.eval(vtable, ftable) + if "#return" in vtable: + result = vtable["#return"] + del vtable["#return"] + return vtable, ftable, result + else: + return vtable, ftable, result class Maybe(BaseBox): def __init__(self, statement : Statement): self.statement = statement + def __repr__(self) -> str: + return f"maybe({self.statement})" + def eval(self, vtable, ftable): if random.randint(0,1) == 1: return self.statement.eval(vtable,ftable) else: - return vtable, ftable, None + return vtable, ftable, False class Program(BaseBox): def __init__(self, statements: list[Statement]) -> None: self.statements = statements + random.seed(str(self)) + r = random.randint(1,20) + if r == 1: + print("E004: Random compiler error") + exit() + random.seed() def __repr__(self) -> str: - statements_string = f"statements([{rep_join(self.statements)}])" + statements_string = f"program([{rep_join(self.statements)}])" return statements_string def eval(self, *_): diff --git a/plthy_impl/lexer.py b/plthy_impl/lexer.py index e68666e..f2d2a98 100644 --- a/plthy_impl/lexer.py +++ b/plthy_impl/lexer.py @@ -9,7 +9,7 @@ KEYWORD_TOKENS = [("KEYWORD_"+i.upper(), i) for i in [ "maybe", "do", "if", - "because", +# "because", "until", "define", "as", @@ -19,18 +19,21 @@ KEYWORD_TOKENS = [("KEYWORD_"+i.upper(), i) for i in [ ]] BUILTIN_TOKENS = [("BUILTIN", i) for i in [ - "print" + "print", + "input", + "random" ]] DATA_TOKENS = [ ("DATA_STRING", r"\'.*?\'"), - ("DATA_NUMERAL", r"\d+(\.\d+)?") + ("DATA_INT", r"\d+"), + ("DATA_FLOAT", r"\d+(\.\d+)") ] SYMBOL_TOKENS = [ ("SYMBOL_SET", r"\-\>"), - ("SYMBOL_LPARENS", r"\("), - ("SYMBOL_RPARENS", r"\)"), +# ("SYMBOL_LPARENS", r"\("), +# ("SYMBOL_RPARENS", r"\)"), ("SYMBOL_LBRACKET", r"\["), ("SYMBOL_RBRACKET", r"\]"), ("SYMBOL_LCURL", r"\{"), @@ -39,8 +42,8 @@ SYMBOL_TOKENS = [ ("SYMBOL_MINUS", r"\-"), ("SYMBOL_TIMES", r"\*"), ("SYMBOL_DIVIDE", r"\/"), - ("SYMBOL_COMMA", r"\,"), - ("SYMBOL_COLON", r"\:"), +# ("SYMBOL_COMMA", r"\,"), +# ("SYMBOL_COLON", r"\:"), ("SYMBOL_SEMICOLON", r"\;"), ("SYMBOL_PIPE", r"\|"), ("SYMBOL_QUOTE", r"\""), diff --git a/plthy_impl/parser.py b/plthy_impl/parser.py index 908c6db..9674cd2 100644 --- a/plthy_impl/parser.py +++ b/plthy_impl/parser.py @@ -9,11 +9,11 @@ class Parser(): [i[0] for i in ALL_TOKENS], precedence=[ ('left', ["KEYWORD_MAYBE", "KEYWORD_RETURN"]), - ('left', ["KEYWORD_IF", "KEYWORD_DEFINE", "KEYWORD_AS"]), + ('left', ["KEYWORD_IF", "KEYWORD_UNTIL", "KEYWORD_DEFINE", "KEYWORD_AS"]), ('left', ["KEYWORD_DO", "BUILTIN"]), ('left', ["SYMBOL_EQUALS", "SYMBOL_SET"]), ('left', ["SYMBOL_PLUS", "SYMBOL_MINUS"]), - ('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE"]) + ('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE", "SYMBOL_LT","SYMBOL_GT"]) ] ) @@ -49,7 +49,11 @@ class Parser(): def statement_if(tokens): return ast_nodes.StatementIf(tokens[0], tokens[2]) - @self.pg.production('statement : KEYWORD_DEFINE ID SYMBOL_LT DATA_NUMERAL SYMBOL_GT KEYWORD_AS statement', precedence="KEYWORD_DEFINE") + @self.pg.production('statement : KEYWORD_UNTIL expression statement') + def statement_until(tokens): + return ast_nodes.StatementUntil(tokens[2],tokens[1]) + + @self.pg.production('statement : KEYWORD_DEFINE ID SYMBOL_LT DATA_INT SYMBOL_GT KEYWORD_AS statement', precedence="KEYWORD_DEFINE") def statement_define(tokens): return ast_nodes.StatementDefine(tokens[1].value, int(tokens[3].value), tokens[6]) @@ -80,9 +84,13 @@ class Parser(): return [tokens[0]] + tokens[2] ## expression ## - @self.pg.production('expression : DATA_NUMERAL') - def exp_numeral(tokens): - return ast_nodes.ExpNumeral(float(tokens[0].value)) + @self.pg.production('expression : DATA_INT') + def exp_int(tokens): + return ast_nodes.ExpInt(int(tokens[0].value)) + + @self.pg.production('expression : DATA_FLOAT') + def exp_int(tokens): + return ast_nodes.ExpInt(float(tokens[0].value)) @self.pg.production('expression : DATA_STRING') def exp_string(tokens): @@ -101,6 +109,8 @@ class Parser(): @self.pg.production('expression : expression SYMBOL_TIMES expression') @self.pg.production('expression : expression SYMBOL_DIVIDE expression') @self.pg.production('expression : expression SYMBOL_EQUALS expression') + @self.pg.production('expression : expression SYMBOL_LT expression') + @self.pg.production('expression : expression SYMBOL_GT expression') def exp_a_binop(tokens): return ast_nodes.ExpABinop(tokens[1].value,tokens[0],tokens[2]) @@ -108,10 +118,24 @@ class Parser(): def exp_arg(tokens): return ast_nodes.ExpArg(int(tokens[1].value[1:])) + @self.pg.production('expression : SYMBOL_LCURL expressions SYMBOL_RCURL') + def exp_list(tokens): + return ast_nodes.ExpList(tokens[1]) + + @self.pg.production('expression : expression SYMBOL_LCURL expression SYMBOL_RCURL') + def exp_index(tokens): + return ast_nodes.ExpIndex(tokens[0],tokens[2]) + ## Error Handling ## @self.pg.error def error_handle(token): - raise Exception(token.name, token.value, token.source_pos) + if token.source_pos is None: + print("E002: No valediction") + elif token.source_pos.lineno == 1 and token.source_pos.colno == 1: + print("E001: No greeting") + else: + print(f"E003: Unexpected token '{token.value}' ({token.name}) at line {token.source_pos.lineno}, column {token.source_pos.colno}.") + exit() ## Finish ## parser = self.pg.build() diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..2d04c0f --- /dev/null +++ b/test.sh @@ -0,0 +1,28 @@ +#! /usr/bin/bash + +for f in ./tests/*.plthy; do + filename=$(basename $f .plthy) + python ./plthy -i "tests/$filename.plthy" > "tests/$filename.output" + + printf '%-20s' "${filename/_/" "} " + + if test -f "tests/$filename.expected1"; then + match_found=0 + for g in ./tests/$filename.expected*; do + if cmp -s "$g" "tests/$filename.output"; then + echo "✓" + match_found=1 + break + fi + done + if [ $match_found -eq 0 ]; then + echo "X" + fi + else + if cmp -s "tests/$filename.expected" "tests/$filename.output"; then + echo "✓" + else + echo "X" + fi + fi +done diff --git a/tests/01_none.expected b/tests/01_none.expected new file mode 100644 index 0000000..e69de29 diff --git a/tests/none.plthy b/tests/01_none.plthy similarity index 100% rename from tests/none.plthy rename to tests/01_none.plthy diff --git a/tests/02_variable.expected b/tests/02_variable.expected new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/tests/02_variable.expected @@ -0,0 +1 @@ +2 diff --git a/tests/variable.plthy b/tests/02_variable.plthy similarity index 100% rename from tests/variable.plthy rename to tests/02_variable.plthy diff --git a/tests/03_math.expected b/tests/03_math.expected new file mode 100644 index 0000000..017ab29 --- /dev/null +++ b/tests/03_math.expected @@ -0,0 +1 @@ +4 2.0 2 diff --git a/tests/math.plthy b/tests/03_math.plthy similarity index 100% rename from tests/math.plthy rename to tests/03_math.plthy diff --git a/tests/04_scope.expected b/tests/04_scope.expected new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/tests/04_scope.expected @@ -0,0 +1 @@ +5 diff --git a/tests/scope.plthy b/tests/04_scope.plthy similarity index 100% rename from tests/scope.plthy rename to tests/04_scope.plthy diff --git a/tests/05_maybe.expected1 b/tests/05_maybe.expected1 new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/05_maybe.expected1 @@ -0,0 +1 @@ +1 diff --git a/tests/05_maybe.expected2 b/tests/05_maybe.expected2 new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/tests/05_maybe.expected2 @@ -0,0 +1 @@ +2 diff --git a/tests/maybe.plthy b/tests/05_maybe.plthy similarity index 100% rename from tests/maybe.plthy rename to tests/05_maybe.plthy diff --git a/tests/06_if.expected b/tests/06_if.expected new file mode 100644 index 0000000..6178079 --- /dev/null +++ b/tests/06_if.expected @@ -0,0 +1 @@ +b diff --git a/tests/if.plthy b/tests/06_if.plthy similarity index 100% rename from tests/if.plthy rename to tests/06_if.plthy diff --git a/tests/07_function.expected b/tests/07_function.expected new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/tests/07_function.expected @@ -0,0 +1 @@ +5 diff --git a/tests/function.plthy b/tests/07_function.plthy similarity index 100% rename from tests/function.plthy rename to tests/07_function.plthy diff --git a/tests/08_precedence.expected b/tests/08_precedence.expected new file mode 100644 index 0000000..b7dfd93 --- /dev/null +++ b/tests/08_precedence.expected @@ -0,0 +1,3 @@ +7 +5 +5 6 diff --git a/tests/precedence.plthy b/tests/08_precedence.plthy similarity index 66% rename from tests/precedence.plthy rename to tests/08_precedence.plthy index 34c6ade..203cca0 100644 --- a/tests/precedence.plthy +++ b/tests/08_precedence.plthy @@ -3,4 +3,6 @@ $ 1 + 2 * 3 -> x| do print| $ 5 -> y if variable x = 7| do print| +$$5 -> z + 1 -> a| +do print| goodbye| \ No newline at end of file diff --git a/tests/09_fib.expected b/tests/09_fib.expected new file mode 100644 index 0000000..ca8664f --- /dev/null +++ b/tests/09_fib.expected @@ -0,0 +1,5 @@ +1 +1 +2 +8 +55 diff --git a/tests/fib.plthy b/tests/09_fib.plthy similarity index 100% rename from tests/fib.plthy rename to tests/09_fib.plthy diff --git a/tests/10_E001.expected b/tests/10_E001.expected new file mode 100644 index 0000000..8c81fec --- /dev/null +++ b/tests/10_E001.expected @@ -0,0 +1 @@ +E001: No greeting diff --git a/tests/10_E001.plthy b/tests/10_E001.plthy new file mode 100644 index 0000000..15bcb54 --- /dev/null +++ b/tests/10_E001.plthy @@ -0,0 +1,2 @@ +$2 -> x| +goodbye| \ No newline at end of file diff --git a/tests/11_E002.expected b/tests/11_E002.expected new file mode 100644 index 0000000..a9cf250 --- /dev/null +++ b/tests/11_E002.expected @@ -0,0 +1 @@ +E002: No valediction diff --git a/tests/11_E002.plthy b/tests/11_E002.plthy new file mode 100644 index 0000000..7a71af5 --- /dev/null +++ b/tests/11_E002.plthy @@ -0,0 +1,2 @@ +hello| +$2 -> x| \ No newline at end of file diff --git a/tests/12_list.expected b/tests/12_list.expected new file mode 100644 index 0000000..58a437d --- /dev/null +++ b/tests/12_list.expected @@ -0,0 +1,3 @@ +[1, 2, 3] +3 +2 diff --git a/tests/12_list.plthy b/tests/12_list.plthy new file mode 100644 index 0000000..e491c73 --- /dev/null +++ b/tests/12_list.plthy @@ -0,0 +1,6 @@ +hello| +${1;2;3;} -> x| +do print| +${1;1+1;do print<3;>;} -> y| +do print| +goodbye| \ No newline at end of file