From fe351b5ca427a7f3fe64f0622377115995104af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Reuh=20Fildadut?= Date: Sun, 17 Dec 2023 17:15:16 +0100 Subject: [PATCH] Anselme v2.0.0-alpha rewrite Woke up and felt like changing a couple things. It's actually been worked on for a while, little at a time... The goal was to make the language and implementation much simpler. Well I don't know if it really ended up being simpler but it sure is more robust. Main changes: * proper first class functions and closures supports! proper scoping rules! no more namespace shenanigans! * everything is an expression, no more statements! make the implementation both simpler and more complex, but it's much more consistent now! the syntax has massively changed as a result though. * much more organized and easy to modify codebase: one file for each AST node, no more random fields or behavior set by some random node exceptionally, everything should now follow the same API defined in ast.abstract.Node Every foundational feature should be implemented right now. The vast majority of things that were possible in v2 are possible now; some things aren't, but that's usually because v2 is a bit more sane. The main missing things before a proper release are tests and documentation. There's a few other things that might be implemented later, see the ideas.md file. --- LANGUAGE.md | 1133 ----------------- LICENSE | 5 - README.md | 76 -- TUTORIAL.md | 77 -- anselme.lua | 846 +----------- anselme.md | 322 ----- ast/ArgumentTuple.lua | 207 +++ ast/Assignment.lua | 37 + ast/AttachBlock.lua | 49 + ast/Block.lua | 80 ++ ast/Boolean.lua | 28 + ast/Branched.lua | 59 + ast/Call.lua | 86 ++ ast/Choice.lua | 52 + ast/Closure.lua | 58 + ast/Definition.lua | 60 + ast/Environment.lua | 214 ++++ ast/Flush.lua | 28 + ast/Function.lua | 78 ++ ast/FunctionParameter.lua | 45 + ast/Identifier.lua | 44 + ast/List.lua | 81 ++ ast/LuaFunction.lua | 63 + ast/Nil.lua | 20 + ast/Number.lua | 25 + ast/Overload.lua | 62 + ast/Pair.lua | 25 + ast/ParameterTuple.lua | 67 + ast/Quote.lua | 34 + ast/Resumable.lua | 60 + ast/ResumeParentFunction.lua | 44 + ast/Return.lua | 33 + ast/ReturnBoundary.lua | 37 + ast/String.lua | 34 + ast/StringInterpolation.lua | 53 + ast/Struct.lua | 121 ++ ast/Symbol.lua | 78 ++ ast/Table.lua | 85 ++ ast/Text.lua | 36 + ast/TextInterpolation.lua | 59 + ast/Tuple.lua | 66 + ast/Typed.lua | 24 + ast/abstract/AutoCall.lua | 8 + ast/abstract/Event.lua | 22 + ast/abstract/Node.lua | 282 ++++ ast/abstract/Overloadable.lua | 27 + ast/abstract/Runtime.lua | 12 + ast/init.lua | 13 + class.lua | 169 +++ common.lua | 91 -- common/init.lua | 70 + common/to_anselme.lua | 26 + doc/api.md | 263 ++++ doc/api.md.template | 12 + doc/gendocs.lua | 86 ++ doc/language.md | 1 + doc/tutorial.md | 1 + ideas.md | 127 ++ init.lua | 1 - interpreter/common.lua | 699 ---------- interpreter/expression.lua | 586 --------- interpreter/interpreter.lua | 234 ---- lib/ansicolors.lua | 100 ++ lib/binser.lua | 753 +++++++++++ notes.txt | 109 -- parser/Source.lua | 38 + parser/code_to_tree.lua | 65 + parser/common.lua | 342 ----- parser/expression.lua | 558 -------- parser/expression/comment.lua | 57 + .../contextual/function_parameter.lua | 40 + .../function_parameter_no_default.lua | 7 + .../expression/contextual/parameter_tuple.lua | 49 + .../expression/primary/block_identifier.lua | 16 + .../primary/function_definition.lua | 205 +++ parser/expression/primary/identifier.lua | 40 + parser/expression/primary/init.lua | 42 + parser/expression/primary/number.lua | 19 + parser/expression/primary/parenthesis.lua | 31 + parser/expression/primary/prefix/else.lua | 8 + parser/expression/primary/prefix/function.lua | 35 + parser/expression/primary/prefix/mutable.lua | 9 + parser/expression/primary/prefix/negation.lua | 9 + parser/expression/primary/prefix/not.lua | 9 + parser/expression/primary/prefix/prefix.lua | 34 + .../primary/prefix/prefix_quote_right.lua | 11 + parser/expression/primary/prefix/return.lua | 15 + .../expression/primary/prefix/semicolon.lua | 13 + parser/expression/primary/primary.lua | 33 + parser/expression/primary/string.lua | 78 ++ parser/expression/primary/struct.lua | 17 + parser/expression/primary/symbol.lua | 40 + parser/expression/primary/text.lua | 24 + parser/expression/primary/tuple.lua | 41 + .../expression/secondary/infix/addition.lua | 9 + parser/expression/secondary/infix/and.lua | 9 + .../expression/secondary/infix/assignment.lua | 23 + .../secondary/infix/assignment_call.lua | 24 + .../secondary/infix/assignment_with_infix.lua | 35 + parser/expression/secondary/infix/call.lua | 28 + parser/expression/secondary/infix/choice.lua | 17 + .../expression/secondary/infix/definition.lua | 22 + .../expression/secondary/infix/different.lua | 9 + .../expression/secondary/infix/division.lua | 9 + parser/expression/secondary/infix/equal.lua | 9 + .../expression/secondary/infix/exponent.lua | 9 + parser/expression/secondary/infix/greater.lua | 9 + .../secondary/infix/greater_equal.lua | 9 + parser/expression/secondary/infix/if.lua | 9 + .../infix/implicit_multiplication.lua | 23 + parser/expression/secondary/infix/index.lua | 19 + parser/expression/secondary/infix/infix.lua | 34 + .../secondary/infix/infix_or_suffix.lua | 32 + .../secondary/infix/infix_quote_both.lua | 12 + .../secondary/infix/infix_quote_right.lua | 11 + .../secondary/infix/integer_division.lua | 9 + parser/expression/secondary/infix/lower.lua | 9 + .../secondary/infix/lower_equal.lua | 9 + parser/expression/secondary/infix/modulo.lua | 9 + .../secondary/infix/multiplication.lua | 9 + parser/expression/secondary/infix/or.lua | 9 + parser/expression/secondary/infix/pair.lua | 9 + .../expression/secondary/infix/semicolon.lua | 9 + .../secondary/infix/substraction.lua | 9 + parser/expression/secondary/infix/tag.lua | 9 + parser/expression/secondary/infix/tuple.lua | 36 + .../expression/secondary/infix/type_check.lua | 9 + parser/expression/secondary/infix/while.lua | 9 + parser/expression/secondary/init.lua | 78 ++ parser/expression/secondary/secondary.lua | 34 + parser/expression/secondary/suffix/call.lua | 40 + .../secondary/suffix/exclamation_call.lua | 15 + .../expression/secondary/suffix/semicolon.lua | 9 + parser/expression/secondary/suffix/suffix.lua | 31 + parser/expression/to_ast.lua | 50 + parser/init.lua | 13 + parser/postparser.lua | 110 -- parser/preparser.lua | 569 --------- parser/tree_to_ast.lua | 52 + readme.md | 3 + state/ScopeStack.lua | 159 +++ state/State.lua | 229 ++++ state/event_manager.lua | 53 + state/resumable_manager.lua | 70 + state/tag_manager.lua | 38 + stdlib/base.lua | 60 + stdlib/boolean.lua | 42 + stdlib/boot.ans | 2 + stdlib/bootscript.lua | 16 - stdlib/checkpoint.lua | 10 + stdlib/closure.lua | 38 + stdlib/conditionals.lua | 64 + stdlib/functions.lua | 480 ------- stdlib/init.lua | 36 + stdlib/languages/enUS.lua | 6 - stdlib/languages/frFR.lua | 33 - stdlib/number.lua | 49 + stdlib/string.lua | 6 + stdlib/structures.lua | 87 ++ stdlib/tag.lua | 27 + stdlib/text.lua | 24 + stdlib/type_check.lua | 20 + stdlib/types.lua | 412 ------ test/inspect.lua | 334 ----- test/run.lua | 278 ---- test/ser.lua | 143 --- test/tests/anonymous function.ans | 21 - test/tests/anonymous function.lua | 81 -- test/tests/argument alias.ans | 4 - test/tests/argument alias.lua | 22 - test/tests/binary operator overload.ans | 11 - test/tests/binary operator overload.lua | 30 - test/tests/binop assignement.ans | 7 - test/tests/binop assignement.lua | 22 - test/tests/checkpoint change.ans | 33 - test/tests/checkpoint change.lua | 193 --- .../checkpoint merging mutable value.ans | 26 - .../checkpoint merging mutable value.lua | 68 - test/tests/checkpoint merging variable.ans | 26 - test/tests/checkpoint merging variable.lua | 68 - test/tests/checkpoint reached seen.ans | 8 - test/tests/checkpoint reached seen.lua | 66 - test/tests/choice block.ans | 11 - test/tests/choice block.lua | 50 - test/tests/choice function.ans | 11 - test/tests/choice function.lua | 40 - ...e line interpolation with choice event.ans | 10 - ...e line interpolation with choice event.lua | 47 - ...ce line interpolation with event flush.ans | 11 - ...ce line interpolation with event flush.lua | 55 - ...ice line interpolation with text event.ans | 18 - ...ice line interpolation with text event.lua | 72 -- test/tests/choice preserve tags.ans | 16 - test/tests/choice preserve tags.lua | 73 -- test/tests/choice simple.ans | 5 - test/tests/choice simple.lua | 28 - test/tests/choice with decorators.ans | 35 - test/tests/choice with decorators.lua | 132 -- test/tests/comment block.ans | 7 - test/tests/comment block.lua | 6 - test/tests/comment.ans | 1 - test/tests/comment.lua | 6 - test/tests/commit.ans | 20 - test/tests/commit.lua | 54 - test/tests/condition decorator.ans | 3 - test/tests/condition decorator.lua | 19 - test/tests/condition else false.ans | 6 - test/tests/condition else false.lua | 14 - test/tests/condition else true.ans | 6 - test/tests/condition else true.lua | 14 - test/tests/condition elseif false.ans | 8 - test/tests/condition elseif false.lua | 14 - test/tests/condition elseif true.ans | 8 - test/tests/condition elseif true.lua | 14 - test/tests/condition false.ans | 4 - test/tests/condition false.lua | 6 - test/tests/condition operator.ans | 6 - test/tests/condition operator.lua | 35 - test/tests/condition true.ans | 4 - test/tests/condition true.lua | 14 - test/tests/constant object attribute.ans | 8 - test/tests/constant object attribute.lua | 14 - test/tests/constant object.ans | 8 - test/tests/constant object.lua | 14 - test/tests/constant values variable.ans | 15 - test/tests/constant values variable.lua | 40 - test/tests/constant values.ans | 15 - test/tests/constant values.lua | 40 - test/tests/constant variable list.ans | 7 - test/tests/constant variable list.lua | 14 - test/tests/constant variable.ans | 7 - test/tests/constant variable.lua | 14 - .../constrained variable assignement.ans | 11 - .../constrained variable assignement.lua | 22 - .../tests/constrained variable definition.ans | 7 - .../tests/constrained variable definition.lua | 14 - test/tests/custom event.ans | 3 - test/tests/custom event.lua | 21 - test/tests/custom text formatting.ans | 11 - test/tests/custom text formatting.lua | 14 - test/tests/define override function.ans | 3 - test/tests/define override function.lua | 6 - test/tests/define override variable.ans | 3 - test/tests/define override variable.lua | 6 - test/tests/define override.ans | 5 - test/tests/define override.lua | 6 - test/tests/define.ans | 1 - test/tests/define.lua | 6 - test/tests/equality operator.ans | 29 - test/tests/equality operator.lua | 149 --- test/tests/flush.ans | 8 - test/tests/flush.lua | 40 - test/tests/function alias.ans | 9 - test/tests/function alias.lua | 38 - test/tests/function arg vararg.ans | 5 - test/tests/function arg vararg.lua | 19 - test/tests/function arg.ans | 4 - test/tests/function arg.lua | 14 - test/tests/function args arity check fail.ans | 4 - test/tests/function args arity check fail.lua | 6 - test/tests/function args vararg empty.ans | 5 - test/tests/function args vararg empty.lua | 23 - test/tests/function args vararg.ans | 5 - test/tests/function args vararg.lua | 23 - test/tests/function args.ans | 4 - test/tests/function args.lua | 18 - test/tests/function assignement.ans | 22 - test/tests/function assignement.lua | 54 - test/tests/function conflict.ans | 5 - test/tests/function conflict.lua | 6 - .../function custom type dispatch error.ans | 17 - .../function custom type dispatch error.lua | 42 - test/tests/function custom type dispatch.ans | 15 - test/tests/function custom type dispatch.lua | 42 - test/tests/function cycle.ans | 18 - test/tests/function cycle.lua | 46 - test/tests/function name dispatch.ans | 9 - test/tests/function name dispatch.lua | 22 - test/tests/function next.ans | 18 - test/tests/function next.lua | 46 - test/tests/function no conflict.ans | 5 - test/tests/function no conflict.lua | 6 - test/tests/function random.ans | 18 - test/tests/function random.lua | 46 - .../function reference call explicit call.ans | 12 - .../function reference call explicit call.lua | 40 - test/tests/function reference call.ans | 27 - test/tests/function reference call.lua | 123 -- test/tests/function reference chain call.ans | 13 - test/tests/function reference chain call.lua | 30 - ...nction reference dot operator function.ans | 11 - ...nction reference dot operator function.lua | 30 - .../tests/function reference dot operator.ans | 10 - .../tests/function reference dot operator.lua | 22 - .../function return exit function nested.ans | 9 - .../function return exit function nested.lua | 19 - test/tests/function return exit function.ans | 6 - test/tests/function return exit function.lua | 14 - test/tests/function return nested.ans | 7 - test/tests/function return nested.lua | 19 - test/tests/function return.ans | 4 - test/tests/function return.lua | 14 - test/tests/function scope wrong.ans | 4 - test/tests/function scope wrong.lua | 6 - test/tests/function scope.ans | 4 - test/tests/function scope.lua | 18 - test/tests/function scoped mutable.ans | 40 - test/tests/function scoped mutable.lua | 566 -------- test/tests/function scoped nested.ans | 44 - test/tests/function scoped nested.lua | 230 ---- test/tests/function scoped recursive.ans | 19 - test/tests/function scoped recursive.lua | 278 ---- test/tests/function scoped.ans | 29 - test/tests/function scoped.lua | 70 - test/tests/function selection.ans | 12 - test/tests/function selection.lua | 22 - ...nction separate variable from variants.ans | 10 - ...nction separate variable from variants.lua | 18 - .../tests/function type dispatch ambigous.ans | 7 - .../tests/function type dispatch ambigous.lua | 6 - .../function type dispatch with default.ans | 26 - .../function type dispatch with default.lua | 73 -- test/tests/function type dispatch.ans | 9 - test/tests/function type dispatch.lua | 22 - test/tests/function ufcs arg.ans | 6 - test/tests/function ufcs arg.lua | 22 - test/tests/function ufcs args.ans | 4 - test/tests/function ufcs args.lua | 18 - test/tests/function vararg empty.ans | 4 - test/tests/function vararg empty.lua | 14 - test/tests/function vararg.ans | 4 - test/tests/function vararg.lua | 14 - test/tests/function.ans | 4 - test/tests/function.lua | 14 - ...immediately run function explicit call.ans | 4 - ...immediately run function explicit call.lua | 30 - ...immediately run function implicit call.ans | 10 - ...immediately run function implicit call.lua | 49 - test/tests/immediately run function scope.ans | 2 - test/tests/immediately run function scope.lua | 18 - test/tests/immediately run variable.ans | 4 - test/tests/immediately run variable.lua | 19 - test/tests/implicit call of references.ans | 61 - test/tests/implicit call of references.lua | 285 ----- test/tests/implicit multiplication.ans | 11 - test/tests/implicit multiplication.lua | 71 -- .../interrupt callback nested paragraph.ans | 19 - .../interrupt callback nested paragraph.lua | 37 - test/tests/interrupt callback nested.ans | 20 - test/tests/interrupt callback nested.lua | 32 - test/tests/interrupt callback.ans | 18 - test/tests/interrupt callback.lua | 32 - test/tests/interrupt no callback.ans | 18 - test/tests/interrupt no callback.lua | 20 - test/tests/lazy boolean operators.ans | 23 - test/tests/lazy boolean operators.lua | 162 --- test/tests/list assignement.ans | 21 - test/tests/list assignement.lua | 78 -- test/tests/list index.ans | 11 - test/tests/list index.lua | 68 - test/tests/loop decorator.ans | 5 - test/tests/loop decorator.lua | 117 -- test/tests/map assignement.ans | 20 - test/tests/map assignement.lua | 78 -- test/tests/map index accross checkpoints.ans | 29 - test/tests/map index accross checkpoints.lua | 92 -- test/tests/map index.ans | 7 - test/tests/map index.lua | 45 - test/tests/merge nested mutable bis.ans | 17 - test/tests/merge nested mutable bis.lua | 21 - test/tests/merge nested mutable error bis.ans | 19 - test/tests/merge nested mutable error bis.lua | 21 - test/tests/merge nested mutable error.ans | 19 - test/tests/merge nested mutable error.lua | 21 - test/tests/merge nested mutable.ans | 17 - test/tests/merge nested mutable.lua | 21 - test/tests/named arguments.ans | 4 - test/tests/named arguments.lua | 30 - test/tests/named varag.ans | 10 - test/tests/named varag.lua | 14 - ...amespace operator arbitrary expression.ans | 6 - ...amespace operator arbitrary expression.lua | 14 - test/tests/nested conditions.ans | 19 - test/tests/nested conditions.lua | 27 - test/tests/nested flush.ans | 19 - test/tests/nested flush.lua | 69 - test/tests/object comparaison.ans | 23 - test/tests/object comparaison.lua | 78 -- test/tests/object constructor.ans | 15 - test/tests/object constructor.lua | 57 - test/tests/object several.ans | 26 - test/tests/object several.lua | 192 --- test/tests/object simple.ans | 17 - test/tests/object simple.lua | 98 -- test/tests/optional arguments.ans | 4 - test/tests/optional arguments.lua | 30 - test/tests/pair operator.ans | 5 - test/tests/pair operator.lua | 22 - test/tests/paragraph alias.ans | 4 - test/tests/paragraph alias.lua | 22 - test/tests/paragraph run force.ans | 14 - test/tests/paragraph run force.lua | 55 - test/tests/paragraph run from.ans | 14 - test/tests/paragraph run from.lua | 60 - test/tests/paragraph run.ans | 14 - test/tests/paragraph run.lua | 60 - test/tests/paragraph.ans | 3 - test/tests/paragraph.lua | 14 - test/tests/remove duplicate spaces.ans | 3 - test/tests/remove duplicate spaces.lua | 28 - test/tests/remove trailing spaces.ans | 5 - test/tests/remove trailing spaces.lua | 34 - test/tests/resume from nested paragraph.ans | 26 - test/tests/resume from nested paragraph.lua | 135 -- .../resume from paragraph restore tags.ans | 17 - .../resume from paragraph restore tags.lua | 85 -- ...sume from paragraph with nested choice.ans | 77 -- ...sume from paragraph with nested choice.lua | 268 ---- ...e from paragraph with nested condition.ans | 21 - ...e from paragraph with nested condition.lua | 38 - test/tests/return children.ans | 13 - test/tests/return children.lua | 37 - test/tests/return in choice.ans | 10 - test/tests/return in choice.lua | 30 - .../scope checkpoint mutable bis error.ans | 49 - .../scope checkpoint mutable bis error.lua | 120 -- test/tests/scope checkpoint mutable bis.ans | 40 - test/tests/scope checkpoint mutable bis.lua | 206 --- test/tests/scope checkpoint mutable error.ans | 38 - test/tests/scope checkpoint mutable error.lua | 99 -- .../scope checkpoint mutable ter error.ans | 51 - .../scope checkpoint mutable ter error.lua | 120 -- test/tests/scope checkpoint mutable ter.ans | 42 - test/tests/scope checkpoint mutable ter.lua | 206 --- test/tests/scope checkpoint mutable.ans | 29 - test/tests/scope checkpoint mutable.lua | 131 -- test/tests/seen checkpoint resume.ans | 12 - test/tests/seen checkpoint resume.lua | 45 - test/tests/string escaping.ans | 9 - test/tests/string escaping.lua | 91 -- test/tests/subtext.ans | 12 - test/tests/subtext.lua | 86 -- test/tests/tag decorator nested.ans | 4 - test/tests/tag decorator nested.lua | 22 - test/tests/tag decorator.ans | 3 - test/tests/tag decorator.lua | 22 - test/tests/tag empty.ans | 4 - test/tests/tag empty.lua | 19 - test/tests/tag operator.ans | 7 - test/tests/tag operator.lua | 42 - test/tests/tag.ans | 4 - test/tests/tag.lua | 22 - test/tests/text block.ans | 2 - test/tests/text block.lua | 19 - test/tests/text break.ans | 3 - test/tests/text break.lua | 22 - test/tests/text buffer with tags.ans | 8 - test/tests/text buffer with tags.lua | 41 - test/tests/text buffer.ans | 8 - test/tests/text buffer.lua | 34 - test/tests/text escaping.ans | 11 - test/tests/text escaping.lua | 54 - test/tests/text format.ans | 3 - test/tests/text format.lua | 18 - ...t line interpolation with choice event.ans | 16 - ...t line interpolation with choice event.lua | 82 -- ...xt line interpolation with event flush.ans | 13 - ...xt line interpolation with event flush.lua | 50 - ...ext line interpolation with text event.ans | 10 - ...ext line interpolation with text event.lua | 44 - test/tests/text.ans | 1 - test/tests/text.lua | 14 - test/tests/unary operator overload.ans | 11 - test/tests/unary operator overload.lua | 30 - test/tests/unseen line.ans | 7 - test/tests/unseen line.lua | 34 - test/tests/variable alias.ans | 3 - test/tests/variable alias.lua | 22 - test/tests/variable reference get value.ans | 5 - test/tests/variable reference get value.lua | 14 - test/tests/while loop else.ans | 19 - test/tests/while loop else.lua | 82 -- test/tests/while loop.ans | 16 - test/tests/while loop.lua | 150 --- 484 files changed, 7099 insertions(+), 18084 deletions(-) delete mode 100644 LANGUAGE.md delete mode 100644 LICENSE delete mode 100644 README.md delete mode 100644 TUTORIAL.md delete mode 100644 anselme.md create mode 100644 ast/ArgumentTuple.lua create mode 100644 ast/Assignment.lua create mode 100644 ast/AttachBlock.lua create mode 100644 ast/Block.lua create mode 100644 ast/Boolean.lua create mode 100644 ast/Branched.lua create mode 100644 ast/Call.lua create mode 100644 ast/Choice.lua create mode 100644 ast/Closure.lua create mode 100644 ast/Definition.lua create mode 100644 ast/Environment.lua create mode 100644 ast/Flush.lua create mode 100644 ast/Function.lua create mode 100644 ast/FunctionParameter.lua create mode 100644 ast/Identifier.lua create mode 100644 ast/List.lua create mode 100644 ast/LuaFunction.lua create mode 100644 ast/Nil.lua create mode 100644 ast/Number.lua create mode 100644 ast/Overload.lua create mode 100644 ast/Pair.lua create mode 100644 ast/ParameterTuple.lua create mode 100644 ast/Quote.lua create mode 100644 ast/Resumable.lua create mode 100644 ast/ResumeParentFunction.lua create mode 100644 ast/Return.lua create mode 100644 ast/ReturnBoundary.lua create mode 100644 ast/String.lua create mode 100644 ast/StringInterpolation.lua create mode 100644 ast/Struct.lua create mode 100644 ast/Symbol.lua create mode 100644 ast/Table.lua create mode 100644 ast/Text.lua create mode 100644 ast/TextInterpolation.lua create mode 100644 ast/Tuple.lua create mode 100644 ast/Typed.lua create mode 100644 ast/abstract/AutoCall.lua create mode 100644 ast/abstract/Event.lua create mode 100644 ast/abstract/Node.lua create mode 100644 ast/abstract/Overloadable.lua create mode 100644 ast/abstract/Runtime.lua create mode 100644 ast/init.lua create mode 100644 class.lua delete mode 100644 common.lua create mode 100644 common/init.lua create mode 100644 common/to_anselme.lua create mode 100644 doc/api.md create mode 100644 doc/api.md.template create mode 100644 doc/gendocs.lua create mode 100644 doc/language.md create mode 100644 doc/tutorial.md create mode 100644 ideas.md delete mode 100644 init.lua delete mode 100644 interpreter/common.lua delete mode 100644 interpreter/expression.lua delete mode 100644 interpreter/interpreter.lua create mode 100644 lib/ansicolors.lua create mode 100644 lib/binser.lua delete mode 100644 notes.txt create mode 100644 parser/Source.lua create mode 100644 parser/code_to_tree.lua delete mode 100644 parser/common.lua delete mode 100644 parser/expression.lua create mode 100644 parser/expression/comment.lua create mode 100644 parser/expression/contextual/function_parameter.lua create mode 100644 parser/expression/contextual/function_parameter_no_default.lua create mode 100644 parser/expression/contextual/parameter_tuple.lua create mode 100644 parser/expression/primary/block_identifier.lua create mode 100644 parser/expression/primary/function_definition.lua create mode 100644 parser/expression/primary/identifier.lua create mode 100644 parser/expression/primary/init.lua create mode 100644 parser/expression/primary/number.lua create mode 100644 parser/expression/primary/parenthesis.lua create mode 100644 parser/expression/primary/prefix/else.lua create mode 100644 parser/expression/primary/prefix/function.lua create mode 100644 parser/expression/primary/prefix/mutable.lua create mode 100644 parser/expression/primary/prefix/negation.lua create mode 100644 parser/expression/primary/prefix/not.lua create mode 100644 parser/expression/primary/prefix/prefix.lua create mode 100644 parser/expression/primary/prefix/prefix_quote_right.lua create mode 100644 parser/expression/primary/prefix/return.lua create mode 100644 parser/expression/primary/prefix/semicolon.lua create mode 100644 parser/expression/primary/primary.lua create mode 100644 parser/expression/primary/string.lua create mode 100644 parser/expression/primary/struct.lua create mode 100644 parser/expression/primary/symbol.lua create mode 100644 parser/expression/primary/text.lua create mode 100644 parser/expression/primary/tuple.lua create mode 100644 parser/expression/secondary/infix/addition.lua create mode 100644 parser/expression/secondary/infix/and.lua create mode 100644 parser/expression/secondary/infix/assignment.lua create mode 100644 parser/expression/secondary/infix/assignment_call.lua create mode 100644 parser/expression/secondary/infix/assignment_with_infix.lua create mode 100644 parser/expression/secondary/infix/call.lua create mode 100644 parser/expression/secondary/infix/choice.lua create mode 100644 parser/expression/secondary/infix/definition.lua create mode 100644 parser/expression/secondary/infix/different.lua create mode 100644 parser/expression/secondary/infix/division.lua create mode 100644 parser/expression/secondary/infix/equal.lua create mode 100644 parser/expression/secondary/infix/exponent.lua create mode 100644 parser/expression/secondary/infix/greater.lua create mode 100644 parser/expression/secondary/infix/greater_equal.lua create mode 100644 parser/expression/secondary/infix/if.lua create mode 100644 parser/expression/secondary/infix/implicit_multiplication.lua create mode 100644 parser/expression/secondary/infix/index.lua create mode 100644 parser/expression/secondary/infix/infix.lua create mode 100644 parser/expression/secondary/infix/infix_or_suffix.lua create mode 100644 parser/expression/secondary/infix/infix_quote_both.lua create mode 100644 parser/expression/secondary/infix/infix_quote_right.lua create mode 100644 parser/expression/secondary/infix/integer_division.lua create mode 100644 parser/expression/secondary/infix/lower.lua create mode 100644 parser/expression/secondary/infix/lower_equal.lua create mode 100644 parser/expression/secondary/infix/modulo.lua create mode 100644 parser/expression/secondary/infix/multiplication.lua create mode 100644 parser/expression/secondary/infix/or.lua create mode 100644 parser/expression/secondary/infix/pair.lua create mode 100644 parser/expression/secondary/infix/semicolon.lua create mode 100644 parser/expression/secondary/infix/substraction.lua create mode 100644 parser/expression/secondary/infix/tag.lua create mode 100644 parser/expression/secondary/infix/tuple.lua create mode 100644 parser/expression/secondary/infix/type_check.lua create mode 100644 parser/expression/secondary/infix/while.lua create mode 100644 parser/expression/secondary/init.lua create mode 100644 parser/expression/secondary/secondary.lua create mode 100644 parser/expression/secondary/suffix/call.lua create mode 100644 parser/expression/secondary/suffix/exclamation_call.lua create mode 100644 parser/expression/secondary/suffix/semicolon.lua create mode 100644 parser/expression/secondary/suffix/suffix.lua create mode 100644 parser/expression/to_ast.lua create mode 100644 parser/init.lua delete mode 100644 parser/postparser.lua delete mode 100644 parser/preparser.lua create mode 100644 parser/tree_to_ast.lua create mode 100644 readme.md create mode 100644 state/ScopeStack.lua create mode 100644 state/State.lua create mode 100644 state/event_manager.lua create mode 100644 state/resumable_manager.lua create mode 100644 state/tag_manager.lua create mode 100644 stdlib/base.lua create mode 100644 stdlib/boolean.lua create mode 100644 stdlib/boot.ans delete mode 100644 stdlib/bootscript.lua create mode 100644 stdlib/checkpoint.lua create mode 100644 stdlib/closure.lua create mode 100644 stdlib/conditionals.lua delete mode 100644 stdlib/functions.lua create mode 100644 stdlib/init.lua delete mode 100644 stdlib/languages/enUS.lua delete mode 100644 stdlib/languages/frFR.lua create mode 100644 stdlib/number.lua create mode 100644 stdlib/string.lua create mode 100644 stdlib/structures.lua create mode 100644 stdlib/tag.lua create mode 100644 stdlib/text.lua create mode 100644 stdlib/type_check.lua delete mode 100644 stdlib/types.lua delete mode 100644 test/inspect.lua delete mode 100644 test/run.lua delete mode 100644 test/ser.lua delete mode 100644 test/tests/anonymous function.ans delete mode 100644 test/tests/anonymous function.lua delete mode 100644 test/tests/argument alias.ans delete mode 100644 test/tests/argument alias.lua delete mode 100644 test/tests/binary operator overload.ans delete mode 100644 test/tests/binary operator overload.lua delete mode 100644 test/tests/binop assignement.ans delete mode 100644 test/tests/binop assignement.lua delete mode 100644 test/tests/checkpoint change.ans delete mode 100644 test/tests/checkpoint change.lua delete mode 100644 test/tests/checkpoint merging mutable value.ans delete mode 100644 test/tests/checkpoint merging mutable value.lua delete mode 100644 test/tests/checkpoint merging variable.ans delete mode 100644 test/tests/checkpoint merging variable.lua delete mode 100644 test/tests/checkpoint reached seen.ans delete mode 100644 test/tests/checkpoint reached seen.lua delete mode 100644 test/tests/choice block.ans delete mode 100644 test/tests/choice block.lua delete mode 100644 test/tests/choice function.ans delete mode 100644 test/tests/choice function.lua delete mode 100644 test/tests/choice line interpolation with choice event.ans delete mode 100644 test/tests/choice line interpolation with choice event.lua delete mode 100644 test/tests/choice line interpolation with event flush.ans delete mode 100644 test/tests/choice line interpolation with event flush.lua delete mode 100644 test/tests/choice line interpolation with text event.ans delete mode 100644 test/tests/choice line interpolation with text event.lua delete mode 100644 test/tests/choice preserve tags.ans delete mode 100644 test/tests/choice preserve tags.lua delete mode 100644 test/tests/choice simple.ans delete mode 100644 test/tests/choice simple.lua delete mode 100644 test/tests/choice with decorators.ans delete mode 100644 test/tests/choice with decorators.lua delete mode 100644 test/tests/comment block.ans delete mode 100644 test/tests/comment block.lua delete mode 100644 test/tests/comment.ans delete mode 100644 test/tests/comment.lua delete mode 100644 test/tests/commit.ans delete mode 100644 test/tests/commit.lua delete mode 100644 test/tests/condition decorator.ans delete mode 100644 test/tests/condition decorator.lua delete mode 100644 test/tests/condition else false.ans delete mode 100644 test/tests/condition else false.lua delete mode 100644 test/tests/condition else true.ans delete mode 100644 test/tests/condition else true.lua delete mode 100644 test/tests/condition elseif false.ans delete mode 100644 test/tests/condition elseif false.lua delete mode 100644 test/tests/condition elseif true.ans delete mode 100644 test/tests/condition elseif true.lua delete mode 100644 test/tests/condition false.ans delete mode 100644 test/tests/condition false.lua delete mode 100644 test/tests/condition operator.ans delete mode 100644 test/tests/condition operator.lua delete mode 100644 test/tests/condition true.ans delete mode 100644 test/tests/condition true.lua delete mode 100644 test/tests/constant object attribute.ans delete mode 100644 test/tests/constant object attribute.lua delete mode 100644 test/tests/constant object.ans delete mode 100644 test/tests/constant object.lua delete mode 100644 test/tests/constant values variable.ans delete mode 100644 test/tests/constant values variable.lua delete mode 100644 test/tests/constant values.ans delete mode 100644 test/tests/constant values.lua delete mode 100644 test/tests/constant variable list.ans delete mode 100644 test/tests/constant variable list.lua delete mode 100644 test/tests/constant variable.ans delete mode 100644 test/tests/constant variable.lua delete mode 100644 test/tests/constrained variable assignement.ans delete mode 100644 test/tests/constrained variable assignement.lua delete mode 100644 test/tests/constrained variable definition.ans delete mode 100644 test/tests/constrained variable definition.lua delete mode 100644 test/tests/custom event.ans delete mode 100644 test/tests/custom event.lua delete mode 100644 test/tests/custom text formatting.ans delete mode 100644 test/tests/custom text formatting.lua delete mode 100644 test/tests/define override function.ans delete mode 100644 test/tests/define override function.lua delete mode 100644 test/tests/define override variable.ans delete mode 100644 test/tests/define override variable.lua delete mode 100644 test/tests/define override.ans delete mode 100644 test/tests/define override.lua delete mode 100644 test/tests/define.ans delete mode 100644 test/tests/define.lua delete mode 100644 test/tests/equality operator.ans delete mode 100644 test/tests/equality operator.lua delete mode 100644 test/tests/flush.ans delete mode 100644 test/tests/flush.lua delete mode 100644 test/tests/function alias.ans delete mode 100644 test/tests/function alias.lua delete mode 100644 test/tests/function arg vararg.ans delete mode 100644 test/tests/function arg vararg.lua delete mode 100644 test/tests/function arg.ans delete mode 100644 test/tests/function arg.lua delete mode 100644 test/tests/function args arity check fail.ans delete mode 100644 test/tests/function args arity check fail.lua delete mode 100644 test/tests/function args vararg empty.ans delete mode 100644 test/tests/function args vararg empty.lua delete mode 100644 test/tests/function args vararg.ans delete mode 100644 test/tests/function args vararg.lua delete mode 100644 test/tests/function args.ans delete mode 100644 test/tests/function args.lua delete mode 100644 test/tests/function assignement.ans delete mode 100644 test/tests/function assignement.lua delete mode 100644 test/tests/function conflict.ans delete mode 100644 test/tests/function conflict.lua delete mode 100644 test/tests/function custom type dispatch error.ans delete mode 100644 test/tests/function custom type dispatch error.lua delete mode 100644 test/tests/function custom type dispatch.ans delete mode 100644 test/tests/function custom type dispatch.lua delete mode 100644 test/tests/function cycle.ans delete mode 100644 test/tests/function cycle.lua delete mode 100644 test/tests/function name dispatch.ans delete mode 100644 test/tests/function name dispatch.lua delete mode 100644 test/tests/function next.ans delete mode 100644 test/tests/function next.lua delete mode 100644 test/tests/function no conflict.ans delete mode 100644 test/tests/function no conflict.lua delete mode 100644 test/tests/function random.ans delete mode 100644 test/tests/function random.lua delete mode 100644 test/tests/function reference call explicit call.ans delete mode 100644 test/tests/function reference call explicit call.lua delete mode 100644 test/tests/function reference call.ans delete mode 100644 test/tests/function reference call.lua delete mode 100644 test/tests/function reference chain call.ans delete mode 100644 test/tests/function reference chain call.lua delete mode 100644 test/tests/function reference dot operator function.ans delete mode 100644 test/tests/function reference dot operator function.lua delete mode 100644 test/tests/function reference dot operator.ans delete mode 100644 test/tests/function reference dot operator.lua delete mode 100644 test/tests/function return exit function nested.ans delete mode 100644 test/tests/function return exit function nested.lua delete mode 100644 test/tests/function return exit function.ans delete mode 100644 test/tests/function return exit function.lua delete mode 100644 test/tests/function return nested.ans delete mode 100644 test/tests/function return nested.lua delete mode 100644 test/tests/function return.ans delete mode 100644 test/tests/function return.lua delete mode 100644 test/tests/function scope wrong.ans delete mode 100644 test/tests/function scope wrong.lua delete mode 100644 test/tests/function scope.ans delete mode 100644 test/tests/function scope.lua delete mode 100644 test/tests/function scoped mutable.ans delete mode 100644 test/tests/function scoped mutable.lua delete mode 100644 test/tests/function scoped nested.ans delete mode 100644 test/tests/function scoped nested.lua delete mode 100644 test/tests/function scoped recursive.ans delete mode 100644 test/tests/function scoped recursive.lua delete mode 100644 test/tests/function scoped.ans delete mode 100644 test/tests/function scoped.lua delete mode 100644 test/tests/function selection.ans delete mode 100644 test/tests/function selection.lua delete mode 100644 test/tests/function separate variable from variants.ans delete mode 100644 test/tests/function separate variable from variants.lua delete mode 100644 test/tests/function type dispatch ambigous.ans delete mode 100644 test/tests/function type dispatch ambigous.lua delete mode 100644 test/tests/function type dispatch with default.ans delete mode 100644 test/tests/function type dispatch with default.lua delete mode 100644 test/tests/function type dispatch.ans delete mode 100644 test/tests/function type dispatch.lua delete mode 100644 test/tests/function ufcs arg.ans delete mode 100644 test/tests/function ufcs arg.lua delete mode 100644 test/tests/function ufcs args.ans delete mode 100644 test/tests/function ufcs args.lua delete mode 100644 test/tests/function vararg empty.ans delete mode 100644 test/tests/function vararg empty.lua delete mode 100644 test/tests/function vararg.ans delete mode 100644 test/tests/function vararg.lua delete mode 100644 test/tests/function.ans delete mode 100644 test/tests/function.lua delete mode 100644 test/tests/immediately run function explicit call.ans delete mode 100644 test/tests/immediately run function explicit call.lua delete mode 100644 test/tests/immediately run function implicit call.ans delete mode 100644 test/tests/immediately run function implicit call.lua delete mode 100644 test/tests/immediately run function scope.ans delete mode 100644 test/tests/immediately run function scope.lua delete mode 100644 test/tests/immediately run variable.ans delete mode 100644 test/tests/immediately run variable.lua delete mode 100644 test/tests/implicit call of references.ans delete mode 100644 test/tests/implicit call of references.lua delete mode 100644 test/tests/implicit multiplication.ans delete mode 100644 test/tests/implicit multiplication.lua delete mode 100644 test/tests/interrupt callback nested paragraph.ans delete mode 100644 test/tests/interrupt callback nested paragraph.lua delete mode 100644 test/tests/interrupt callback nested.ans delete mode 100644 test/tests/interrupt callback nested.lua delete mode 100644 test/tests/interrupt callback.ans delete mode 100644 test/tests/interrupt callback.lua delete mode 100644 test/tests/interrupt no callback.ans delete mode 100644 test/tests/interrupt no callback.lua delete mode 100644 test/tests/lazy boolean operators.ans delete mode 100644 test/tests/lazy boolean operators.lua delete mode 100644 test/tests/list assignement.ans delete mode 100644 test/tests/list assignement.lua delete mode 100644 test/tests/list index.ans delete mode 100644 test/tests/list index.lua delete mode 100644 test/tests/loop decorator.ans delete mode 100644 test/tests/loop decorator.lua delete mode 100644 test/tests/map assignement.ans delete mode 100644 test/tests/map assignement.lua delete mode 100644 test/tests/map index accross checkpoints.ans delete mode 100644 test/tests/map index accross checkpoints.lua delete mode 100644 test/tests/map index.ans delete mode 100644 test/tests/map index.lua delete mode 100644 test/tests/merge nested mutable bis.ans delete mode 100644 test/tests/merge nested mutable bis.lua delete mode 100644 test/tests/merge nested mutable error bis.ans delete mode 100644 test/tests/merge nested mutable error bis.lua delete mode 100644 test/tests/merge nested mutable error.ans delete mode 100644 test/tests/merge nested mutable error.lua delete mode 100644 test/tests/merge nested mutable.ans delete mode 100644 test/tests/merge nested mutable.lua delete mode 100644 test/tests/named arguments.ans delete mode 100644 test/tests/named arguments.lua delete mode 100644 test/tests/named varag.ans delete mode 100644 test/tests/named varag.lua delete mode 100644 test/tests/namespace operator arbitrary expression.ans delete mode 100644 test/tests/namespace operator arbitrary expression.lua delete mode 100644 test/tests/nested conditions.ans delete mode 100644 test/tests/nested conditions.lua delete mode 100644 test/tests/nested flush.ans delete mode 100644 test/tests/nested flush.lua delete mode 100644 test/tests/object comparaison.ans delete mode 100644 test/tests/object comparaison.lua delete mode 100644 test/tests/object constructor.ans delete mode 100644 test/tests/object constructor.lua delete mode 100644 test/tests/object several.ans delete mode 100644 test/tests/object several.lua delete mode 100644 test/tests/object simple.ans delete mode 100644 test/tests/object simple.lua delete mode 100644 test/tests/optional arguments.ans delete mode 100644 test/tests/optional arguments.lua delete mode 100644 test/tests/pair operator.ans delete mode 100644 test/tests/pair operator.lua delete mode 100644 test/tests/paragraph alias.ans delete mode 100644 test/tests/paragraph alias.lua delete mode 100644 test/tests/paragraph run force.ans delete mode 100644 test/tests/paragraph run force.lua delete mode 100644 test/tests/paragraph run from.ans delete mode 100644 test/tests/paragraph run from.lua delete mode 100644 test/tests/paragraph run.ans delete mode 100644 test/tests/paragraph run.lua delete mode 100644 test/tests/paragraph.ans delete mode 100644 test/tests/paragraph.lua delete mode 100644 test/tests/remove duplicate spaces.ans delete mode 100644 test/tests/remove duplicate spaces.lua delete mode 100644 test/tests/remove trailing spaces.ans delete mode 100644 test/tests/remove trailing spaces.lua delete mode 100644 test/tests/resume from nested paragraph.ans delete mode 100644 test/tests/resume from nested paragraph.lua delete mode 100644 test/tests/resume from paragraph restore tags.ans delete mode 100644 test/tests/resume from paragraph restore tags.lua delete mode 100644 test/tests/resume from paragraph with nested choice.ans delete mode 100644 test/tests/resume from paragraph with nested choice.lua delete mode 100644 test/tests/resume from paragraph with nested condition.ans delete mode 100644 test/tests/resume from paragraph with nested condition.lua delete mode 100644 test/tests/return children.ans delete mode 100644 test/tests/return children.lua delete mode 100644 test/tests/return in choice.ans delete mode 100644 test/tests/return in choice.lua delete mode 100644 test/tests/scope checkpoint mutable bis error.ans delete mode 100644 test/tests/scope checkpoint mutable bis error.lua delete mode 100644 test/tests/scope checkpoint mutable bis.ans delete mode 100644 test/tests/scope checkpoint mutable bis.lua delete mode 100644 test/tests/scope checkpoint mutable error.ans delete mode 100644 test/tests/scope checkpoint mutable error.lua delete mode 100644 test/tests/scope checkpoint mutable ter error.ans delete mode 100644 test/tests/scope checkpoint mutable ter error.lua delete mode 100644 test/tests/scope checkpoint mutable ter.ans delete mode 100644 test/tests/scope checkpoint mutable ter.lua delete mode 100644 test/tests/scope checkpoint mutable.ans delete mode 100644 test/tests/scope checkpoint mutable.lua delete mode 100644 test/tests/seen checkpoint resume.ans delete mode 100644 test/tests/seen checkpoint resume.lua delete mode 100644 test/tests/string escaping.ans delete mode 100644 test/tests/string escaping.lua delete mode 100644 test/tests/subtext.ans delete mode 100644 test/tests/subtext.lua delete mode 100644 test/tests/tag decorator nested.ans delete mode 100644 test/tests/tag decorator nested.lua delete mode 100644 test/tests/tag decorator.ans delete mode 100644 test/tests/tag decorator.lua delete mode 100644 test/tests/tag empty.ans delete mode 100644 test/tests/tag empty.lua delete mode 100644 test/tests/tag operator.ans delete mode 100644 test/tests/tag operator.lua delete mode 100644 test/tests/tag.ans delete mode 100644 test/tests/tag.lua delete mode 100644 test/tests/text block.ans delete mode 100644 test/tests/text block.lua delete mode 100644 test/tests/text break.ans delete mode 100644 test/tests/text break.lua delete mode 100644 test/tests/text buffer with tags.ans delete mode 100644 test/tests/text buffer with tags.lua delete mode 100644 test/tests/text buffer.ans delete mode 100644 test/tests/text buffer.lua delete mode 100644 test/tests/text escaping.ans delete mode 100644 test/tests/text escaping.lua delete mode 100644 test/tests/text format.ans delete mode 100644 test/tests/text format.lua delete mode 100644 test/tests/text line interpolation with choice event.ans delete mode 100644 test/tests/text line interpolation with choice event.lua delete mode 100644 test/tests/text line interpolation with event flush.ans delete mode 100644 test/tests/text line interpolation with event flush.lua delete mode 100644 test/tests/text line interpolation with text event.ans delete mode 100644 test/tests/text line interpolation with text event.lua delete mode 100644 test/tests/text.ans delete mode 100644 test/tests/text.lua delete mode 100644 test/tests/unary operator overload.ans delete mode 100644 test/tests/unary operator overload.lua delete mode 100644 test/tests/unseen line.ans delete mode 100644 test/tests/unseen line.lua delete mode 100644 test/tests/variable alias.ans delete mode 100644 test/tests/variable alias.lua delete mode 100644 test/tests/variable reference get value.ans delete mode 100644 test/tests/variable reference get value.lua delete mode 100644 test/tests/while loop else.ans delete mode 100644 test/tests/while loop else.lua delete mode 100644 test/tests/while loop.ans delete mode 100644 test/tests/while loop.lua diff --git a/LANGUAGE.md b/LANGUAGE.md deleted file mode 100644 index 8246c2a..0000000 --- a/LANGUAGE.md +++ /dev/null @@ -1,1133 +0,0 @@ -Anselme language reference -========================== - -### Main structure - -Anselme will read a bunch of different scripts files and execute them afterward. Like you would expect of... any? scripting language. We use the `.ans` file extension for files. - -Scripts are read line per line, from top to bottom. Some lines can have children; children lines are indented with some sort of whitespace, whether it's tabs or space (as long as it's consistent), like in Python. - -``` -> Parent line. - Child line. - - > Another child line. - But this one have a child. Grand-child. - -Another line. - - random line whith indentation which makes no sense at all. -``` - -#### Checkpoints - -When executing a piece of Anselme code, your scripts will not directly modify the global state (i.e. the values of variables used by every script), but only locally, in its associated interpreter instance. Meaning that if you change a variable, its value will only be set in the local state, so the new value will be accessible from the current interpreter but not other interpreters, at least until the next checkpoint. Similarly, the first time your script reads a variable its value at this time is kept into the local state so it can not be affected by other scripts, at least until the next checkpoint. - -Right after reaching a checkpoint line, Anselme will merge the local state with the global one, i.e., make every change accessible to other scripts, and get checkpointed changes from other scripts. - -``` -:$ main - :var = 5 - - ~ var := 2 - - before: {var}==2, because the value has been changed in the current execution context - - (But if we run the script "parallel" in parallel at this point, it will still think var==5) - - :! foo - But the variable will be merged with the global state on a checkpoint - - after: {var}==2, still, as expected - - (And if we run the script "parallel" in parallel at this point, it will now think var==2) - -:$ parallel - parallel: {main.var} - -~ main - -(note: if two scripts try to modify the same value at the same time, one of them will win, but which one is undefined/a surprise) -``` - -The purpose of this system is both to allow several scripts to run at the same time with an easy way to avoid interferences, and to make sure the global state is always in a consistent (and not in the middle of a calculation): since scripts can be interrupted at any time, when it is interrupted, anything that was changed between the last checkpoint and the interruption will be discarded. If you're a RDBMS person, that's more-or-less equivalent to a transaction with a repeatable read isolation level (without any sort of locking or lost update protection though). - -When running the script again, it will resume correctly at the last reached checkpoint. See [function calls](#function-calls) for more details on how to call/resume a function. - -Checkpoints are set per function, and are expected to be defined inside functions only. - -State merging also happens after a checkpoint has been manually called or resumed from. - -### Lines types - -There's different types of lines, depending on their first character(s) (after indentation). - -* `(`: comment line. Everything following this is ignored. Doesn't even check for children indentation and syntax correctness, so have fun. - -``` -(FANCY COMMENT YEAH) - so many - things - to say - here -``` - -#### Text lines: - -Lines that can append data to the event buffer and emit text or choice events. - -* `>`: write a choice into the [event buffer](#event-buffer). Followed by arbitrary text. Support [text interpolation](#text-interpolation); if a text event is created during the text interpolation, it is added to the choice text content instead of the global event buffer. Support [escape codes](#escape-codes). Empty choices are discarded. - -``` -:$ f - Third choice - -> First choice -> Second choice -> Last choice -> {f} -``` - -If an unescaped `~`, `~?` or `#` appears in the line, the associated operator is applied to the line (see [operators](#operators)), using the previous text as the left argument and everything that follows as the right argument expression. - -``` -(Conditionnaly executing a line) -:$ fn - > show this choice only once ~ 👁️ - -(Tagging a single line) -> tagged # 42 - not tagged -``` - -* regular text, i.e. any line that doesn't start with a special line type character: write some text into the [event buffer](#event-buffer). Support [text interpolation](#text-interpolation). Support [escape codes](#escape-codes). Don't accept children lines. - -``` -Hello, -this is some text. - -And this is more text, in a different event. -``` - -If an unescaped `~`, `~?` or `#` appears in the line, the associated operator is applied to the line (see [operators](#operators)), using the previous text as the left argument and everything that follows as the right argument expression. - -``` -(Conditionnaly executing a line) -:$ fn - run this line only once ~ 👁️ - -(Tagging a single line) -tagged # 42 -``` - -* empty line: flush the event buffer, i.e., if there are any pending lines of text or choices, send them to your game. See [Event buffer](#event-buffer). This line always keep the same identation as the last non-empty line, so you don't need to put invisible whitespace on an empty-looking line. Is also automatically added at the end of a file. Don't accept children lines. - -#### Expression lines: - -Lines that evaluate an [expression](#expressions) and do something with the result. - -* `~`: condition line. Can be followed by an [expression](#expressions); otherwise the expression `1` is assumed. If the expression evaluates to [true](#truethness), run its children. Without children, this line is typically use to simply run an expression. - -* `~~`: else-condition. Same as a condition line, but is only run if the last condition or else-condition line (in the same indentation block) was false (regardless of line distance). - -``` -~ 1 - This is true -~~ - This is never run. - - -~ 0 - This is never run. -~~ 1 == 0 - This neither. -~~ - This is. -``` - -* `~?`: while loop line. Works like `~` condition lines, but if the expression evaluates to true and after it ran its children, will reevaluate the expression again and repeat the previous logic until the expression eventually evaluates to false. - -``` -(Count to 10:) -:i = 1 -~? i < 10 - {i} - - ~ i += 1 - -(else-conditions can be used if the expression has never evalated to true in the loop:) -~ i := 5 -~? i < 2 - Never happens. -~~ - This is run. -``` - -* `#`: tag line. Can be followed by an [expression](#expressions); otherwise nil expression is assumed. The results of the [expression](#expressions) will be wrapped in a map and added to the tags send along with any `choice` or `text` event sent from its children. Can be nested. - -``` -# color="red" - Text tagged with a red color - - # "blink" - Tagged with a red color and blink. -``` - -* `@`: return line. Can be followed by an [expression](#expressions); otherwise nil expression is assumed. Exit the current function and returns the expression's value. - -``` -:$ hey - @5 - -{hey} = 5 -``` - -If this line has children, they will be ran _after_ evaluating the returned expression but _before_ exiting the current function. If the children return a value, it is used instead. - -``` -(Returns 0 and print 5) -:$ fn - :i=0 - - @i - ~ i:=5 - {i} - -(Returns 3) -:$ g - @0 - @3 -``` - -Please note that Anselme will discard returns values sent from within a choice block. Returns inside choice block still have the expected behaviour of stopping the execution of the choice block. - -This is the case because choice blocks are not ran right as they are read, but only at the next event flush (i.e. empty line). This means that if there is no flush in the function itself, the choice will be ran *after* the function has already been executed and returning a value at this point makes no sense: - -``` -:$ f - > a - @1 - @2 - -(f will return 2 since the choice is run after the @2 line) -~ f == 2 - - Yes. - -(Choice block is actually ran right before the "Yes" line, when the choice event is flushed.) -``` - -#### Definition lines: - -Definition lines are used to define variables, constants, functions, checkpoints, and objects. Definition lines always start with `:`. - -For every definition line type, it is possible to make it so it is immediately ran after definition by inserting a `~` after the initial `:`: - -``` -:~ var = &fn - -:~$ loop - This text is run immediately. - > Loop - @loop - > Exit -``` - -is equivalent to - -``` -:var = &fn -~ var - -:$ loop - This text is run immediately. - > Loop - @loop - > Exit -~ loop -``` - -* `:$`: function definition line. Followed by an [identifier](#identifiers), then eventually an [alias](#aliases), and eventually a parameter list. Define a function using its children as function body. Also define a new namespace for its children (using the function name if it has no arguments, or a unique name otherwise). - -The function body is not executed when the line is reached; it must be explicitely called in an expression. See [expressions](#function-calls) to see the different ways of calling a function. - -A parameter list can be optionally given after the identifier. Parameter names are identifiers, with eventually an alias (after a `:`) and a default value (after a `=`), and then a type constraint (after a `::`). It is enclosed with paranthesis and contain a comma-separated list of identifiers: - -``` -:$ f(a, b: alias for b, c="default for c", d: alias for d = "default for d") - first argument: {a} - second argument: {b} - third argument: {c} - fourth argument: {d} - -:$ f(a::string, b: alias for b::string, c::alias="default for c"::string) - same -``` - -Functions can also have a variable number of arguments. By adding `...` after the last argument identifier, it will be considered a variable length argument ("vararg"), and will contain a list of every extraneous argument. - -``` -:$ f(a, b...) - {b} - -(will print [1]) -~ f("discarded", 1) - -(will print [1,2,3]) -~ f("discarded", 1, 2, 3) - -(will print []) -~ f("discarded") -``` - -When a parameter list is given (or just empty parentheses `()`), the function is considered `scoped` - this means that any variable defined in it will only be defined in a call to the function and can only be accessed from this specific call: - -``` -(Non-scoped function: usual behaviour, variables are accessible from everywhere and always.) -:$ f - :a = 1 - ~ a += 1 - -{f.a} is 1 - -~ f -{f.a} is 2 - -(Scoped function: can't access g.a from outside the function) -:$ g() - :a = 1 - {a} - ~ a += 1 - -(Each time the function is called, it has access to its own version of g.a, and don't share it - so this display 1 both times:) -~ g - -~ g -``` - -This is basically the behaviour you'd expect from functions in most other programming languages, and what you would use in Anselme any time you don't care about storing the function variables or want the exact same initial function variables each time you call the function (e.g. recursion). Scoped variables can not be persistent, and are not affected by checkpointing. - -Functions with the same name can be defined, as long as they have a different arguments. Functions will be selected based on the number of arguments given, their name and their type constraint: - -``` -:$ f(a, b) - a - -:$ f(x) - b - -:$ f(x::string) - c - -(will print a) -~ f(1,2) - -(will print b) -~ f(1) - -(will print c) -~ f("hello") -``` - -Every operator, except assignement operators, `|`, `&`, `,`, `~?`, `~` and `#` can also be use as a function name in order to overload the operator: - -``` -(binary operator names: _op_) -(prefix unary operator: op_) -(suffix unary operator: _op) -:$ _/_(a::string, b::string) - @"{a}/{b}" -``` - -After the parameter list, you may also write `:=` followed by an identifier, and eventually an alias. This defines an assignement function, which will be called when assigning a value to the function: - -``` -:x = "value" -:$ f() - @x -:$ f() := v - @x := v - -value = {f} - -~ f() := "other" - -other = {f} -``` - -Functions can return a value using a [return line](#lines-that-can-t-have-children). - -Functions always have the following variables defined in its namespace by default: - -`👁️`: number, number of times the function was executed/resumed before (incremented when reaching the end of the function or a return line) -`🔖`: function reference, last reached checkpoint. `()` (nil) if no checkpoint reached. (updated when reaching a checkpoint or directly executing a checkpoint) - -These variables are persistent, unless the function is scoped. - -* `:!`: checkpoint definition. Followed by an [identifier](#identifiers), then eventually an [alias](#aliases). Define a checkpoint. Also define a new namespace for its children. - -Checkpoints share most of their behavior with functions, with several exceptions. Like functions, the body is not executed when the line is reached; it must either be explicitely called in an expression or executed when resuming the parent function (see checkpoint behaviour below). Can be called in an expression. See [expressions](#checkpoint-calls) to see the different ways of calling a checkpoint manually. - -The local interpreter state will be merged with the global state when the line is reached. See [checkpoints](#checkpoints). - -When executing the parent function after this checkpoint has been reached (using the paranthesis-less function call syntax), the function will resume from this checkpoint, and the checkpoint's children will be run. This is meant to be used as a way to restart the conversation from this point after it was interrupted, providing necessary context. - -``` -:$ inane dialog - Hello George. Nice weather we're having today? - :! interrupted - What was I saying? Ah yes, the weather... - (further dialog here) -``` - -Checkpoints always have the following variable defined in its namespace by default: - -`👁️`: number, number of times the checkpoint was executed/resumed before (incremented when reaching the end of the function or a return line) -`🏁`: number, number of times the checkpoint was reached before (incremented when reaching the checkpoint line; not incremented when resuming from/executing the checkpoint directly) - -These variables are persistent. - -* `:%`: class definition. Followed by an [identifier](#identifiers), then eventually an [alias](#aliases). Define a class. Also define a new namespace for its children. - -Classes share most of their behavior with functions, with a few exceptions. Classes can not take arguments or be scoped; and when called, if the function does not return a value or returns `()` (nil), it will returns a new object instead based on this class. The object can be used to access variables ("attributes") defined in the class, but if one of these attributes is modified on the object it will not change the value in the base class but only in the object. - -Objects can therefore be used to create independant data structures that can contain any variable defined in the base class, inspired by object-oriented programming. - -``` -:% class - :a = 1 - -:object = class - -~ object.a := 3 - -Is 3: {object.a} -Is 1: {class.a} -``` - -Note that the new object returned by the class is also automatically given an annotation that is a reference to the class. This can be used to define methods/function that operate only on objects based on this specific class. - -``` -:% class - :a = 1 - -:$ show(object::&class) - a = {object.a} - -:object = class - -~ object!show -``` - -Classes have the same default variable defined as functions. - -* `:`: variable declaration. Followed by an [identifier](#identifiers) (with eventually an [alias](#aliases)), a `=` and an [expression](#expressions). Defines a variable with a default value and this identifier in the current [namespace]("identifiers"). The expression is not evaluated instantly, but the first time the variable is used. Don't accept children lines. - -``` -:foo = 42 -:bar : alias = 12 -``` - -* `::`: constant declaration. Work the same way as a variable declaration, but the variable can't be reassigned after their declaration and first evaluation, and their value is marked as constant (i.e. can not be modified even it is of a mutable type). - -``` -::foo = 42 -``` - -* `:@`: persistent variable declaration. Work the same way as a variable declaration, but the variable will be stored in the save file, and if we loaded a save file its value will be retrieved from the save file instead of from the expression's result. - -``` -:@foo = 42 -:@bar : alias = 12 -``` - -### Text interpolation - -Text and choice lines allow for arbitrary text. Expression can be evaluated and inserted into the text as the line is executed by enclosing the [expression](#expressions) into brackets. The expressions are evaluated in the same order as the reading direction. - -The expression is automatically wrapped in a call to `{}(expr)`. You can overload `{}` to change its behaviour for custom types; main intended use is to provide some pretty-printing function. - -Note that events can be sent from the interpolated expression as usual. So you may not want to send a choice event or flush the event buffer from, for example, an interpolated expression in a text line, as your text line will be cut in two with the flush or choice between the two halves. - -Text interpolated in choices have the special property of capturing text events into the choice text. - -``` -:a = 5 - -Value of a: {a} - -(in text and choices, text events created from an interpolated expression are included in the final text) -:$ f - wor - (the returned value comes after) - @"ld." - -(Will print "Hello world.") -Hello {f} - -> Hello {f} - -(keep in mind that events are also sent as usual in places that would usually not send event and thus can't really handle them in a sensible manner) -(for example in text litterals: this will send a "wor" text event and put "ld." in b) -:b = "{f}" -``` - -Text interpolation in text and choices lines also support subtexts: this will process text in squares brackets `[]` in the same way as a regular text line. - -``` -Hello [world]. - -(Typically used to tag part of a line in a compact manner) -Hello [world#5] - -> Hello [world#5] -``` - -### Events - -Anselme need to give back control to the game at some point. This is done through events: the interpreter regularly yield its coroutine and returns a bunch of data to your game. This is the "event", it is what we call whatever Anselme sends back to your game. - -Each event is composed of two elements: a type (string; `text`, `choice`, `return` or `error` by default, custom types can also be defined) and associated data; the data associated with each event depends on its type. For the default events this data is: - -* `text` (text to display) is a list of text elements, each with a `text` field, containing the text contents, and a `tags` field, containing the tags associated with this text. -* `choice` (choices to choose from) is a list of choices. Each of these choice is a list of text elements like for the `text` event. -* `return` (when the script ends) is the returned value. -* `error` (when there is an error) is the error message. - -#### Event buffer - -For some event types (`text` and `choice`), Anselme does not immediately sends the event as soon as they are available but appends them to a buffer of events that will be sent to your game on the next event flush line (empty line): this is the "event buffer". - -``` -Some text. -Another text. - -(the above flush line will cause Anselme to send two text events containing the two previous lines) -Text in another event. -``` - -Beyond technical reasons, the event buffer serves as a way to group together several lines. For example, choice A and B will be sent to the game at the same time and can therefore be assumed to be part of the same "choice block", as opposed to choice C wich will be sent alone: - -``` -> Choice A -> Choice B - -> Choice C -``` - -In practise, this is mostly useful to provide some choice or text from another function: - -``` -:$ reusable choice - > Reusable choice - -> Choice A -~ reusable choice -> Choice C -``` - -Besides empty lines, Anselme will also automatically flush events when the current event type change (when reaching a choice line with a text event in the event buffer, or vice versa), so your game only has to handle a single event of a single type at a time. For example, this will send a text event, flush it, and then buffer a choice event: - -``` -Text -> Choice -``` - -By default, some processing is done on the event buffer before sending it to your game. You can disable these by disabling the associated features flages using `vm:disable` (see #api-reference). - -* strip trailing spaces: will remove any space characters at the end of the text (for text event), or at the end of each choice (for choice event). - -``` -(There is a space between the text and the tag expression that would be included in the text event otherwise.) -Some text # tag -``` - -* strip duplicate spaces: will remove any duplicated space characters between each element that constitute the text (for text event), or for each choice (for choice event). - -``` -(There is a space between the text and the tag expression; but there is a space as well after the text interpolation in the last line. The two spaces are converted into a single space (the space will belong to the first text element, i.e. the "text " element).) -:$ f - text # tag - -Some {text} here. -``` - -TODO: check if spacing rules are language-specific and move this to language files if appropriate - -### Identifiers - -Valid identifiers must be at least 1 characters long and can contain anything except the characters ``~`^+-=<>/[]*{}|\_!?,;:()"@&$#%`` (that is, every special character on a US keyboard except '). They can contain spaces. They can not start with a number. - -When defining an identifier (using a function, checkpoint or variable delcaration line), it will be defined into the current namespace (defined by the parent function/checkpoint). When evaluating an expression, Anselme will look for variables into the current line's namespace, then go up a level if it isn't found, and so on. Note that the namespace of functions with arguments are not accessible from outside the function. - -In practise, this means you have to use the "genealogy" of the variable to refer to it from a line not in the same namespace: - -``` -:$ fn1 - (everything here is in the fn1 namespace) - :$ fn2 - (fn1.fn2 namespace) - :var2 = 42 - Var2 = 42: {var2} - - Var2 = not found: {var2} - Var2 = 42: {fn2.var2} - - :var1 = 1 - -Var2 = 42: {fn1.fn2.var2} - -:var1 = 2 - -Var1 in the current namespace = 1: {var1} -Var1 in the fn1 namespace = 2: {fn1.var1} - -(Weird, but valid, and also the reason I'm not talking of scoping:) -~ fn1.var1 == 3 -``` - -#### Aliases - -When defining identifiers (in variables, functions, checkpoint or class definitions), they can be followed by a colon and another identifier. This identifier can be used as a new way to access the identifier (i.e., an alias). - -``` -:name: alias = 42 - -{name} is the same as {alias} -``` - -Note that alias have priority over normal identifiers; if both an identifier and an alias have the same name, the alias will be used. - -The main purpose of aliases is translation. When saving the state of your game's script, Anselme will store the name of the persistent variables and their contents, and require the name to be the same when loading the save later, in order to correctly restore their values. - -This behaviour is fine if you only have one language; but if you want to translate your game, this means the translations will need to keep using the original, untranslated persistent variables and functions names if it wants to be compatible with saves in differents languages. Which is not very practical or nice to read. - -Anselme's solution is to keep the original name in the translated script file, but alias them with a translated name. This way, the translated script can be written withou constantly switching languages: - -``` -(in the original, english script) -:@player name = "John Pizzapone" - -Hi {player name}! - -(in a translated, french script) -:@player name : nom du joueur = "John Pizzapone" - -Salut {nom du joueur} ! -``` - -Variables that are defined automatically by Anselme (`👁️`, `🔖` and `🏁` in checkpoints and functions) can be automatically aliased using `vm:setaliases("👁️alias", "🔖alias", 🏁alias")`. See [API](#api-reference). - -### Expressions - -Besides lines, plenty of things in Anselme take expressions, which allow various operations on values and variables. - -Note that these are *not* Lua expressions. - -#### Types - -Default types are: - -* `nil`: nil. Can be defined using empty parantheses `()`. - -* `number`: a number (double). Can be defined using the forms `42`, `.42`, `42.42`. - -* `string`: a string. Can be defined between double quotes `"string"`. Support [text interpolation](#text-interpolation). Support [escape codes](#escape-codes). - -* `pair`: a couple of values. Types can be mixed. Can be defined using equal sign `"key"=5` or a colon `"key":5`. Pairs named by a string that is also a valid identifier can be created using the `key=5` shorthand syntax; `key` will not be interpreted as the variable `key` but the string `"key"` (if `key` is a variable and you want to force the use of its value as a key instead of the string `"key"`, you can either wrap it in parentheses or use the colon syntax). - -* `annotated`: a couple of values. Types can be mixed. Can be defined using colon `expr::type`. The second value is used in type constraints, this is intended to be use to give a custom type to a value. - -* `function reference`: reference to one or more function(s) with a given name. Can be defined using `&function name`, which will create a reference to every function with this name accessible from the current namespace. Will behave as if it was the original function (can be called using `func ref`, `func ref!` or `func ref(args)`). - -* `variable reference`: reference to a single variable with a given name. Can be defined using `&variable name`, which will create a reference to the closest variable with this name accessible from the current namespace. Will behave as if it was the original variable, returning the value when called (value can be retrieved using `var ref!` or simply `var ref`). - -* `list`: a list of values. Mutable. Types can be mixed. Can be defined between square brackets and use comma as a separator '[1,2,3,4]'. - -* `map`: a map of keys-values. Mutable. Types can be mixed. Can be defined between curly braces and use comma as a separator, using pairs to define the key-values pairs (otherwise the numeric position of the element is used as a key). '{1=1,2=2,3,4="heh",foo="bar"}'. - -* `object`: an object/record. Mutable. Can be created by calling a class function. - -* `event buffer`: an [event buffer](#event-buffer). Can be created using `%[text]`, where text is interpreted the same way as a text line (so you can perform interpolation, subtexts, add tags, etc.). Note that this only allows for text and flush events; if the text emit choices and custom events an error will be raised. - -Every type is immutable, except `list`, `map` and `object`. - -How conversions are handled from Anselme to Lua: - -* `nil` -> `nil` - -* `number` -> `number` - -* `string` -> `string` - -* `pair` -> `table`, with a single key-value pair. - -* `annotated` -> the annotated value (the annotation is stripped) - -* `list` -> `table` (purely sequential table). - -* `map` -> `table` (will map each key to a key in the Lua table). - -* `object` -> `table` (containing each object and class property and its value) - -* `event buffer` -> `table` (a list of events where each event is a list of two elements (event type and its eventual data), like `{ { "text", text = { { tags = {...}, text = "hello" } } }, { "flush" }, ... }`) - -How conservions are handled from Lua to Anselme: - -* `nil` -> `nil` - -* `number` -> `number` - -* `string` -> `string` - -* `table` -> `list` or `map`. Converted to a list if the table is purely sequential, otherwise returns a map; e.g. `{1,2,key="value",3}` -> `{1=1,2=2,3=3,key="value"}` and `{1,2,3}` -> [1,2,3] - -* `boolean` -> `number`, 0 for false, 1 for true. - -#### Escape codes - -These can be used to represent some character in string and other text elements that would otherwise be difficult to express due to conflicts with Anselme syntax; for example to avoid a character at the start of a text line to be interpreted as another line type. - -* `\n` for a newline -* `\t` for a tabulation -* `\\` for `\` -* `\"` for `"` to escape string delimiters -* `\{` and `\}` for `{` and `}` to escape text interpolation -* `\[` and `\]` for `[` and `]` to escape subtexts -* and, in general, for any character X we can get it by prefixing it with an `\`: `\X` for `X` - -#### Truethness - -Only `0` and `nil` are false. Everything else is considered true. - -#### Equality - -Anselme consider two objects to be equal if they can *always* be used interchangeably. - -In practice, this results in two main cases for equality tests: - -* immutable values (strings, numbers, constants, ...). They are compared by recursively making sure all of their values and structure are equal. - -``` -(All of the following if true) -~ 5 == 5 -~ "foo" == "foo" -~ constant([1,2,3]) == constant([1,2,3]) -``` - -* mutable values (list, map, objects that are not constant). They are compared by reference, i.e. they are only considered equal if they are not distinct objects, even if they contain the same values and structure. - -``` -:a = [1,2,3] -:b = a -:c = [1,2,3] - -(True:) -~ a == b - -(False:) -~ a == c - -(a and c are not interchangeable as they are two distinct lists; if we do:) -~ a(1) = 42 -(This will change the first value of both a and b, but not c.) -``` - -#### Refering to an identifier - -Any defined identifier can be accessed from an expression by using its name; the identifier will be first searched in the current namespace, then go up until it finds it as described in [identifiers](#identifiers). - -What will happen then depends on what the identifier refer to: see [function calls](#function-calls) for functions and [checkpoint calls](#checkpoint-calls) for checkpoints. - -For variables, the identifier will returns the value of the variable when evaluated. - -When the identifier is preceeded by another expression directly (without any operator between the two), Anselme consider this to be an implicit multiplication. This behave in the same way as if there was a `*` operator between the expression and identifier, but has a priority just higher than explicit multiplication. - -``` -:x = 3 - -{2x} = {2*x} = 6 - -(Priority is made slighly higher to avoid parentheses in this kind of operation:) -{1/2x} = {1/(2*x)} = 1/6 -``` - -#### Function calls - -The simplest way to call a function is simply to use its name. If the function has no arguments, parantheses are optional, or can be replaced with a `!`: - -``` -:$ f - called - -~ f -(equivalent to) -~ f! - -:$ f(a) - called with {a} - -~ f("an argument") -``` - -Please note, however, that if the function contains checkpoints, these two syntaxes behave differently. Without parantheses, the function will resume from the last reached checkpoint; with parantheses, the function always restart from its beginning: - -``` -:$ f - a - :! checkpoint - b - c - -No checkpoint reached, will write "a" and "c": -~ f - -Checkpoint is now reached, will write "b" and "c": -~ f - -Force no checkpoint, will write "a" and "c": -~ f() - -``` - -Functions with arguments can also be called with a "method-like" syntax using the `!` operator (though Anselme has no concept of classes and methods): - -``` -:$ f(a) - called with {a} - -"an argument"!f - -:$ f(a, b) - called with {a} and {b} - -"an argument"!f("another argument") -``` - -If the function has a return value, any of these calls will of course return the value. - -``` -:$ f - @"text" - -this is text: {f} -``` - -Functions can also have default arguments. Defaults values can be any expression and are re-evaluated each time the function is called: - -``` -:$ f(a, b=1) - @a+b - -{f(1)} = 2 - -:$ g(a, b=a) - @a+b - -{g(1)} = 2 -{g(2)} = 4 -``` - -Arguments can also be passed by naming them instead of their position. These syntaxes can be mixed: - -``` -:$ f(a, b, c) - @a + b + c - -{f(1,2,3)} = {f(c=3,b=2,a=1)} = {f(1,2,c=3)} -``` - -Anselme actually treat argument maps are regular maps; named arguments are actually pairs and positional arguments are implicitely converted to pairs with their position as a key. Arguments are evaluated left-to-right. The call will error if one of the keys in the map is not a string or number. - -This means that pairs can't be passed directly as arguments to a function (as they will be considered named arguments). If you want to use pairs, always wrap them in a list. - -Functions can have a variable number of arguments. Additional arguments are added in a list: - -``` -:$ f(a, b...) - {a} - - {b} - -{f(1, 2, 3, 4, 5)} - -(Will print:) - 1 - [2,3,4,5] -``` - -Anselme use dynamic dispatch, meaning the correct function is selected at runtime. The correct function is selected based on number of arguments, argument names, and argument type constraint. The function with the most specific arguments will be selected. If several functions match, an error is thrown. - -``` -:$ fn(x::number, y) - a - -:$ fn(x::number) - b - -:$ fn(a::string) - c - -:$ fn(x::number, y::number) - c - -a = {fn(5, "s")} - -b = {fn("s")} - -c = {fn(5)} - -d = {fn(5, 2)} - -:$ g(x) - -:$ g(x, a="t") - -error, can't select unique function: {g(5)} -``` - -Note that types constraints are expected to be constant and are evaluated only once. Default values, however, are evaluated each time the function is called (and the user didn't explicitely give an argument that would replace this default). - -#### Checkpoint calls - -Most of the time, you should'nt need to call checkpoints yourself - they will be automatically be set as the active checkpoint when the interperter reach their line, and they will be automatically called when resuming its parent function. - -But in the cases when you want to manually set the current checkpoint, you can call it with a similar syntax to paranthesis-less function calls: - -``` -:$ f - a - :! checkpoint - b - c - -Set the current checkpoint to "checkpoint" and force run the function starting from this checkpoint, will write "b" and "c": -~ f.checkpoint - -Will correctly resumes from the last set checkpoint, and write "b" and "c": -~ f -f! can also be used for the exact same result. - -Function can always be restarted from the begining using parantheses: -~ f() -``` - -You can also only execute the checkpoints' children code only by using a parantheses-syntax: - -``` -:$ f - a - :! checkpoint - b - c - -Set the current checkpoint to "checkpoint" and run this checkpoint only, will only write "b": -~ f.checkpoint() - -And will resume from the checkpoint like before: -~ f -``` - -Method style calling is also possible, like with functions. - -Checkpoints merge variables after being called (either manually or automatically from resuming a function). See [checkpoints](#checkpoints). The merge always happen after the checkpoint's child block has been ran. - -Please also be aware that when resuming from a checkpoint, Anselme will try to restore the interpreter state as if the function was correctly executed from the start up to this checkpoint. This includes: - -* if the checkpoint is in a condition block, it will assume the condition was true (but will not re-evaluate it) -* if the checkpoint is in a choice block, it will assume this choice was selected (but will not re-evaluate any of the choices from the same choice group) -* will try to re-add every tag from parent lines; this require Anselme to re-evaluate every tag lines that are a parent of the checkpoint in the function. Be careful if your tag expressions have side-effects. - -##### Operator priority - -From lowest to highest priority: - -``` -_;_ _; -_:=_ _+=_ _-=_ _//=_ _/=_ _*=_ _%=_ _^=_ -_,_ $_ -_~?_ _~_ _#_ -_=_ -_|_ _&_ -_!=_ _==_ _>=_ _<=_ _<_ _>_ -_+_ _-_ -_*_ _//_ _/_ _%_ -_::_ --_ !_ -_^_ -_!_ -&_ -_._ -``` - -A series of operators with the same priority are evaluated left-to-right. - -The function called for a binary operator op is named `_op_`, for a prefix unary operator `op_`, for a suffix unary operator `_op`. Theses names are used to defined new operator behaviour; see function line. - -#### Operators - -Built-in operators: - -##### Assignement - -`a := b`: evaluate b, assign its value to identifier `a`. Returns the new value. - -`a(index) := b`: evaluate b, assign its value to element of specific index in list/map `a`. Element is searched using the same method as list/map index operator `a(b)`; in the case of list, also allows to add a new element to the list by giving `len(a)+1` as the index. In the case of a map, if b is nil `()`, deletes the key-value pair from the map. Returns the new value. - -`a.b := v`: if a is a function reference or an object, modify the b variable in the reference function or object. - -`a += b`: evaluate b, assign its the current value of a `+` the value of b to a. Returns the new value. - -`-=`, `*=`, `/=`, `//=`, `%=`, `^=`: same with other arithmetic operators. - -##### Comparaison - -`a == b`: returns `1` if a and b are considered equal, `0` otherwise - -`a != b`: returns `1` if a and b are not equal, `0` otherwise - -These only work on numbers: - -`a > b`: returns `1` if a is greater than b are different, `0` otherwise - -`<`, `>=`, `<=`: same with lower, greater or equal, lower or equal - -##### Arithmetic - -These only work on numbers. - -`a + b`: evaluate a and b, returns their sum. - -`-`, `*`, `/`, `//`, `^`, `%`: same for substraction, multiplication, division, integer division, exponentiation, modulo - -`-a`: evaluate a, returns its opposite - -This only works on strings: - -`a + b`: evaluate a and b, concatenate them. - -##### Logic operators - -`!a`: evaluate a, returns `0` if it is true, `1` otherwise - -`a & b`: and operator, lazy - -`a | b`: or operator, lazy - -`a ~ b`: evaluates b, if true evaluates a and returns it, otherwise returns nil (lazy). - -`a ~? b`: evaluates b, if true evaluates a then reevaluate b and loop this until b is false; returns a list containing all successive values that a returned. - -##### Functions and function references - -`fn(args)`: call the function, checkpoint or function reference with the given arguments. - -`fn!`: call the function, checkpoint or function reference without arguments. Can leads to different behaviour that the syntax with parantheses; see [function calls](#function-calls). - -`fn`: call the function, checkpoint or function reference without arguments. Can leads to different behaviour that the other syntaxes; see [function calls](#function-calls). - -`&fn`: returns a function reference to the given function. If it is already a reference, returns the same reference. - -`a!fn(args)`: call the function or function reference with the variable as first argument. Parantheses are optional. - -`$x`: returns a reference to an anonymous function that returns the expression `x`. Note that the returned reference *can not* be persisted. - -`$(parameters)x`: returns a reference to an anonymous function that returns the expression `x` and takes some parameters (same syntax as function definition lines). Note that the returned reference *can not* be persisted. - -##### Variable references - -`&var`: returns a variable reference to the given variable. If it is already a reference, returns the same reference. - -`a!`: returns the value associated with the referenced variable. - -`a`: returns the value associated with the referenced variable. - -##### Various - -`a ; b`: evaluate a, discard its result, then evaluate b. Returns the result of b. - -`a;`: evaluate a, discard its result, returns nil. - -`a = b`: evaluate a and b, returns a new pair with a as key and b as value. If a is an identifier, will interpret it as a string (and not a variable; you can wrap a in parentheses if you want to use the value associated with variable a instead, or just use the alternative pair operator `a:b` which does not have this behavior). - -`a : b`: evaluate a and b, returns a new pair with a as key and b as value. Unlike the `a=b` operator, will evaluate a as and will get the value associated with the identifier if a is a variable identifier. Can be used for example to benefit from variable aliases and have translatable keys. - -`a :: b`: evaluate a and b, returns a new annotated value with a as value and b as the annotation. This annotation will be checked in type constraints. - -`a # b`: evaluates b, then evaluates a with b added to the active tags (wrap b in a map and merges it with the current tag map). Returns a. - -`a.b`: if a is a function reference, returns the first found variable named `b` in the referenced function namespace; or if `b` is a subfunction in the referenced function, will call it (you can use the usual ways to call functions and gives arguments as well: `a.b!` or `a.b(x, y, ...)`). When overloading this operator, if `b` is an identifier, the operator will interpret it as a string (instead of returning the evaluated value of the variable eventually associated to the identifier). - -`object.b`: if object is an object, returns the first found variable named `b` in the object, or, if the object does not contain it, found in its base class. If `b` is a subfunction in the base class, will call it (arguments can also be given using the usual syntax). - -`list(b)`: evaluate b (number), returns the value with this index in the list. Use 1-based indexing. If a negative value is given, will look from the end of the list (`-1` is the last element, `-2` the one before, etc.). Error on invalid index. Operator is named `()`. - -`map(b)`: evaluate b, returns the value with this key in the map. If the key is not present in the map, returns `nil`. - -`{}(v)`: function called when formatting a value in a text interpolation for printing. - -#### Built-in functions - -##### Pair methods - -`name(pair)`: returns the name (first element) of a pair - -`value(pair)`: returns the value (second element) of a pair - -##### List methods - -`len(list)`: returns length of the list - -`insert(list[, position], value)`: insert a value at position (by default, the end of the list) - -`remove(list, [position])`: remove the list element at position (by default, the end of the list) - -`find(list, value)`: returns the index of the first element equal to value in the list; returns 0 if no such element found. - -##### Sequential execution - -`cycle(...)`: given function/checkpoint references as arguments, will execute them in the order given each time the function is ran; e.g., `cycle(&a, &b)` will execute a on the first execution, then b, then a again, etc. - -`next(...)`: same as cycle, but will not cycle; once the end of sequence is reached, will keep executing the last element. - -`random(...)`: same arguments as before, but execute a random element at every execution. - -##### String methods - -`len(string)`: returns length of the string in UTF-8 characters - -##### Various - -`alias(ref::reference, alias::string)`: define an alias `alias` for the variable or function referenced by `ref`. Expect fully qualified names for the alias. - -`rand([m[, n]])`: when called whitout arguments, returns a random float in [0,1). Otherwise, returns a random number in [m,n]; m=1 if not given. - -`floor(x)`: returns the largest integral value less than or equal x - -`ceil(x)`: returns the smallest integral value more than or equal x - -`round(x, increment=1)`: returns the number, rounder to the nearest increment - -`error(str)`: throw an error with the specified message - -`annotation(v::annotated)`: returns v's annotation - -`unannotated(v)`: return v, eventual annotations removed - -`type(v)`: return v's type - -`is a(v, type or annotation)`: check if v is of a certain type or annotation - -`constant(v)`: create a constant copy of v and returns it. The resulting value is immutable, even if it contains mutable types (will raise an error if you try to change it). - -#### Built-in variables - -Variables for default types (each is associated to a string of the internal variable type name): `nil`, `number`, `string`, `list`, `map`, `pair`, `function reference`, `variable reference`. - -The π constant is also defined in `pi`. - -#### Built-in language scripts - -Anselme provides some scripts that define translated aliases for built-in variables and functions. Currently `enUS` (English) and `frFR` (French) are provided. - -See the `stdlib/languages` for details on each language. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 07b4981..0000000 --- a/LICENSE +++ /dev/null @@ -1,5 +0,0 @@ -Copyright 2019-2022 Étienne "Reuh" Fildadut - -Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 71b73fc..0000000 --- a/README.md +++ /dev/null @@ -1,76 +0,0 @@ -Anselme -======= - -The overengineered dialog scripting system in pure Lua. - -Whatever is on the master branch should work fine. **Documentation and language are still WIP and will change. I am using this in a project and modify it as my needs change.** Breaking changes are documented in commit messages. - -Purpose -------- - -Once upon a time, I wanted to do a game with a branching story. I could store the current state in a bunch of variables and just write everything like the rest of my game's code. But no, that would be *too simple*. I briefly looked at [ink](https://github.com/inkle/ink), which looked nice but lacked some features I felt like I needed. Mainly, I wanted something more language independant and less linear. Also, I wasn't a fan of the syntax. And I'm a weirdo who make their game in Lua *and* likes making their own scripting languages (by the way, if you like Lua but not my weird idiosyncratic language, there's actually some other options ([Erogodic](https://github.com/oniietzschan/erogodic) looks nice)). - -So, here we go. Let's make a new scripting language. - -Anselme ended up with some features that are actually quite useful compared to the alternatives: - -* allows for concurently running scripts (a conversation bores you? why not start another at the same time!) -* allows for script interuption with gracious fallback (so you can *finally* make that NPC shut up mid-sentence) -* a mostly consistent and easy to read syntax based around lines and whitespace -* easily extensible (at least from Lua ;)) - -And most stuff you'd expect from such a language: - -* easy text writing, can integrate expressions into text, can assign tags to (part of) lines -* choices that lead to differents paths -* variables, functions, arbitrary expressions (not Lua, it's its own thing) -* can pause the interpreter when needed -* can save and restore state - -And things that are halfway there but *should* be there eventually (i.e., TODO): - -* language independant; scripts should (hopefully) be easily localizable into any language (it's possible, but doesn't provide any batteries for this right now). - Defaults variables use emoji and then it's expected to alias them; works but not the most satisfying solution. -* a good documentation (need to work on consistent naming of Anselme concepts, a step by step tutorial) - -Things that Anselme is not: - -* a game engine. It's very specific to dialogs and text, so unless you make a text game you will need to do a lot of other stuff. -* a language based on Lua. It's imperative and arrays start at 1 but there's not much else in common. -* a high-performance language. No, really, I didn't even try to make anything fast, so don't use Anselme to compute primes. - -Example -------- - -Sometimes we need some simplicity: - -``` -HELLO SIR, HOW ARE YOU TODAY -> why are you yelling - I LIKE TO - > Well that's stupid. - I DO NOT LIKE YOU SIR. -> I AM FINE AND YOU - I AM FINE THANK YOU - - LOVELY WEATHER WE'RE HAVING, AREN'T WE? - > Sure is! - YEAH. YEAH. - > I've seen better. - NOT NICE. - -WELL, GOOD BYE. -``` - -Othertimes we don't: - -TODO: stupidly complex script - -See [TUTORIAL.md](TUTORIAL.md) for a short introduction (not yet done). - -Reference ------------------- - -See [LANGUAGE.md](LANGUAGE.md) for a reference of the language. - -See [anselme.md](anselme.md) for the Lua API's documentation. diff --git a/TUTORIAL.md b/TUTORIAL.md deleted file mode 100644 index 772c146..0000000 --- a/TUTORIAL.md +++ /dev/null @@ -1,77 +0,0 @@ -Anselme short tutorial -====================== - -This document is a work-in-progress and currently mostly useless. - -Level 1: basics ---------------- - -Basics of Anselme. Should be enough to make a "choose-your-own-adventure" type script using the test game runner. - -### Text - -Writing simple text. - -### Choices - -Defining multiple choices. - -### Basic functions - -Defining functions without arguments and switching to them. - -### Variables and conditions - -Variable & constant definition, simple conditions and expressions. - -Level 2: intermediate ---------------------- - -More advanced features that you would likely need if you intend to integrate Anselme into your game. - -### Checkpoints - -Purpose and how they work. - -### Tags - -Tag lines, subtexts. - -### Events and adding Anselme to your game - -Events buffer, basic Lua API. - -### Other line types - -Loops, comments. - -Level 3: advanced ------------------ - -If you want to make full use of Anselme's features for your game, or just want to flex about a language nobody's heard of in your CV. This part will assume previous programming knowledge. - -### Advanced functions - -Arguments, scopes, return values. - -### Advanced expressions - -Variable types, operators, built-in functions. This part is going to be long... - -#### General rules of an expression - -#### Operators - -#### Main types - -#### Sequences and maps - -#### Objects - -#### References - -#### Function dispatch - -### Translation - -Aliases, what can be translated, what is saved. diff --git a/anselme.lua b/anselme.lua index b3bd91a..04977a4 100644 --- a/anselme.lua +++ b/anselme.lua @@ -1,794 +1,80 @@ ---- anselme main module +--- The main module. ---- Anselme Lua API reference --- --- We actively support LuaJIT and Lua 5.4. Lua 5.1, 5.2 and 5.3 *should* work but I don't always test against them. --- --- This documentation is generated from the main module file `anselme.lua` using `ldoc --ext md anselme.lua`. --- --- Example usage: +-- Naming conventions: +-- * Classes +-- * everything_else +-- * (note: "classes" that are not meat to be instancied and are just here to benefit from inheritance fall into everything_else, e.g. parsing classes) + +--- Usage: -- ```lua --- local anselme = require("anselme") -- load main module +-- local anselme = require("anselme") -- --- local vm = anselme() -- create new VM --- vm:loadgame("game") -- load some scripts, etc. --- local interpreter = vm:rungame() -- create a new interpreter using what was loaded with :loadgame +-- -- create a new state +-- local state = anselme.new() +-- state:load_stdlib() -- --- -- simple function to convert text event data into a string --- -- in your game you may want to handle tags, here we ignore them for simplicity --- local function format_text(text) --- local r = "" --- for _, l in ipairs(t) do --- r = r .. l.text --- end --- return r +-- -- read an anselme script file +-- local f = assert(io.open("script.ans")) +-- local script = anselme.parse(f:read("*a"), "script.ans") +-- f:close() +-- +-- -- load the script in a new branch +-- local run_state = state:branch() +-- run_state:run(script) +-- +-- -- run the script +-- while run_state:active() do +-- local e, data = run_state:step() +-- if e == "text" then +-- for _, l in ipairs(data) do +-- print(l:format(run_state)) +-- end +-- elseif e == "choice" then +-- for i, l in ipairs(data) do +-- print(("%s> %s"):format(i, l:format(run_state))) +-- end +-- local choice = tonumber(io.read("*l")) +-- data:choose(choice) +-- elseif e == "return" then +-- run_state:merge() +-- elseif e == "error" then +-- error(data) +-- end -- end --- --- -- event loop --- repeat --- local event, data = interpreter:step() -- progress script until next event --- if event == "text" then --- print(format_text(d)) --- elseif event == "choice" then --- for j, choice in ipairs(d) do --- print(j.."> "..format_text(choice)) --- end --- interpreter:choose(io.read()) --- elseif event == "error" then --- error(data) --- end --- until t == "return" or t == "error" -- ``` --- --- Calling the Anselme main module will create a return a new [VM](#vms). --- --- The main module also contain a few fields: --- --- @type anselme -local anselme = { - --- Anselme version information table. - -- - -- Contains version informations as number (higher means more recent) of Anselme divied in a few categories: - -- - -- * `save`, which is incremented at each update which may break save compatibility - -- * `language`, which is incremented at each update which may break script file compatibility - -- * `api`, which is incremented at each update which may break Lua API compatibility + +local parser = require("parser") +local State = require("state.State") +require("ast.abstract.Node"):_i_hate_cycles() + +return { + --- Global version string. Follow semver. + version = "2.0.0-alpha", + + --- Table containing per-category version numbers. Incremented by one for any change that may break compatibility. versions = { - save = 2, - language = 25, - api = 6 + --- Version number for languages and standard library changes. + language = 27, + --- Version number for save/AST format changes. + save = 4, + --- Version number for Lua API changes. + api = 8 }, - --- General version number. - -- - -- It is incremented at each update. - version = 26, - --- Currently running [interpreter](#interpreters). - -- `nil` if no interpreter running. - running = nil -} -package.loaded[...] = anselme --- load libs -local anselme_root = (...):gsub("anselme$", "") -local preparse = require(anselme_root.."parser.preparser") -local postparse = require(anselme_root.."parser.postparser") -local expression = require(anselme_root.."parser.expression") -local eval = require(anselme_root.."interpreter.expression") -local injections = require(anselme_root.."parser.common").injections -local run_line = require(anselme_root.."interpreter.interpreter").run_line -local run = require(anselme_root.."interpreter.interpreter").run -local to_lua = require(anselme_root.."interpreter.common").to_lua -local merge_state = require(anselme_root.."interpreter.common").merge_state -local stdfuncs = require(anselme_root.."stdlib.functions") -local bootscript = require(anselme_root.."stdlib.bootscript") -local copy = require(anselme_root.."common").copy -local should_be_persisted = require(anselme_root.."interpreter.common").should_be_persisted -local check_persistable = require(anselme_root.."interpreter.common").check_persistable - --- wrappers for love.filesystem / luafilesystem -local function list_directory(path) - local t = {} - if love then - t = love.filesystem.getDirectoryItems(path) - else - local lfs = require("lfs") - for item in lfs.dir(path) do - table.insert(t, item) - end - end - return t -end -local function is_directory(path) - if love then - return not not love.filesystem.getInfo(path, "directory") - else - local lfs = require("lfs") - return lfs.attributes(path, "mode") == "directory" - end -end -local function is_file(path) - if love then - return not not love.filesystem.getInfo(path, "file") - else - local lfs = require("lfs") - return lfs.attributes(path, "mode") == "file" - end -end - ---- Interpreters --- --- An interpreter is in charge of running Anselme code and is spawned from a [VM](#vms). --- Several interpreters from the same VM can run at the same time. --- --- Typically, you would have a interpreter for each script that need at the same time, for example one for every NPC --- that is currently talking. --- --- Each interpreter can only run one script at a time, and will run it sequentially. --- You can advance in the script by calling the `:step` method, which will run the script until an event is sent (for example some text needs to be displayed), --- which will pause the whole interpreter until `:step` is called again. --- --- @type interpreter -local interpreter_methods = { - --- interpreter state - -- for internal use, you shouldn't touch this - -- @local - state = nil, - --- [VM](#vms) this interpreter belongs to. - vm = nil, - --- String, type of the event that stopped the interpreter (`nil` if interpreter is still running). - end_event = nil, - - --- Run the interpreter until the next event. - -- Returns event type (string), data (any). + --- Parse a `code` string and return the generated AST. -- - -- Will merge changed variables on successful script end. + -- `source` is an optional string; it will be used as the code source name in error messages. -- - -- If event is `"return"` or `"error"`, the interpreter can not be stepped further and should be discarded. - -- - -- Default event types and their associated data: - -- * `text`: text to display, data is a list of text elements, each with a `text` field, containing the text contents, and a `tags` field, containing the tags associated with this text - -- * `choice`: choices to choose from, data is a list of choices Each of these choice is a list of text elements like for the `text` event - -- * `return`: when the script ends, data is the returned value (`nil` if nothing returned) - -- * `error`: when there is an error, data is the error message. - -- - -- See [LANGUAGE.md](LANGUAGE.md) for more details on events. - step = function(self) - -- check status - if self.end_event then - return "error", ("interpreter can't be restarted after receiving a %s event"):format(self.end_event) - end - if coroutine.status(self.state.interpreter.coroutine) ~= "suspended" then - return "error", ("can't step interpreter because it has already finished or is already running (coroutine status: %s)"):format(coroutine.status(self.state.interpreter.coroutine)) - end - -- handle interrupt - if self.state.interpreter.interrupt then - local expr = self.state.interpreter.interrupt - if expr == true then - return "return", "" -- nothing to do after interrupt - else - local line = self.state.interpreter.running_line - local namespace = self:current_namespace() - -- replace state with interrupted state - local exp, err = expression(expr, self.state.interpreter.global_state, namespace or "", "interpreter:interrupt") - if not exp then return "error", ("%s; during interrupt %q at %s"):format(err, expr, line and line.source or "unknown") end - local r, e = self.vm:run(exp) - if not r then return "error", e end - self.state = r.state - end - end - -- run - local previous = anselme.running - anselme.running = self - local success, event, data = coroutine.resume(self.state.interpreter.coroutine) - anselme.running = previous - if not success then event, data = "error", event end - if event == "return" then merge_state(self.state) end - if event == "return" or event == "error" then self.end_event = event end - return event, data + -- Usage: + -- ```lua + -- local ast = anselme.parse("1 + 2", "test") + -- ast:eval() + -- ``` + parse = function(code, source) + return parser(code, source) end, - - --- Select a choice. - -- `i` is the index (number) of the choice in the choice list (from the choice event's data). - -- - -- The choice will be selected on the next interpreter step. - -- - -- Returns this interpreter. - choose = function(self, i) - self.state.interpreter.choice_selected = tonumber(i) - return self - end, - - --- Interrupt (abort the currently running script) the interpreter on the next step, executing an expression (string, if specified) in the current namespace instead. - -- - -- Returns this interpreter. - interrupt = function(self, expr) - self.state.interpreter.interrupt = expr or true - return self - end, - - --- Returns the namespace (string) the last ran line belongs to. - current_namespace = function(self) - local line = self.state.interpreter.running_line - local namespace = "" - if line then - local cur_line = line - namespace = cur_line.namespace - while not namespace do - local block = cur_line.parent_block - if not block.parent_line then break end -- reached root - cur_line = block.parent_line - namespace = cur_line.namespace - end - end - return namespace - end, - - --- Run an expression (string) or block, optionally in a specific namespace (string, will use root namespace if not specified). - -- This may trigger events and must be called from within the interpreter coroutine (i.e. from a function called from a running script). - -- - -- No automatic merge if this change the interpreter state, merge is done once we reach end of script in a call to `:step` as usual. - -- - -- Returns the returned value (nil if nothing returned). - run = function(self, expr, namespace) - -- check status - if coroutine.status(self.state.interpreter.coroutine) ~= "running" then - error("run must be called from whithin the interpreter coroutine") - end - -- parse - local err - if type(expr) ~= "table" then expr, err = expression(tostring(expr), self.state.interpreter.global_state, namespace or "", "interpreter:run") end - if not expr then coroutine.yield("error", err) end - -- run - local r, e - if expr.type == "block" then - r, e = run(self.state, expr) - else - r, e = eval(self.state, expr) - end - if not r then coroutine.yield("error", e) end - if self.state.interpreter.current_event then -- flush final events - local rf, re = run_line(self.state, { type = "flush events" }) - if re then coroutine.yield("error", re) end - if rf then r = rf end - end - return to_lua(r, self.state) - end, - --- Evaluate an expression (string) or block, optionally in a specific namespace (string, will use root namespace if not specified). - -- The expression can't yield events. - -- Can be called from outside the interpreter coroutine. Will create a new coroutine that operate on this interpreter state. - -- - -- No automatic merge if this change the interpreter state, merge is done once we reach end of script in a call to `:step` as usual. - -- - -- Returns the returned value in case of success (nil if nothing returned). - -- - -- Returns nil, error message in case of error. - eval = function(self, expr, namespace) - if self.end_event then - return "error", ("interpreter can't be restarted after receiving a %s event"):format(self.end_event) - end - -- parse - local err - if type(expr) ~= "table" then expr, err = expression(tostring(expr), self.state.interpreter.global_state, namespace or "", "interpreter:eval") end - if not expr then return nil, err end - -- run - local co = coroutine.create(function() - local r, e - if expr.type == "block" then - r, e = run(self.state, expr) - else - r, e = eval(self.state, expr) - end - if not r then return "error", e end - return "return", r - end) - local previous = anselme.running - anselme.running = self - local success, event, data = coroutine.resume(co) - anselme.running = previous - if not success then - return nil, event - elseif event == "error" then - self.end_event = "error" - return nil, data - elseif event ~= "return" then - return nil, ("evaluated expression generated an %q event; at %s"):format(event, self.state.interpreter.running_line.source) - else - return to_lua(data, self.state) - end + --- Return a new [State](#state). + new = function() + return State:new() end, } -interpreter_methods.__index = interpreter_methods - ---- VMs --- --- A VM stores the state required to run Anselme scripts. Each VM is completely independant from each other. --- --- @type vm -local vm_mt = { - --- anselme state - -- for internal use, you shouldn't touch this - -- @local - state = nil, - - --- loaded game state - -- for internal use, you shouldn't touch this - -- @local - game = nil, - - --- Wrapper for loading a whole set of scripts (a "game"). - -- Should be preferred to other loading functions if possible as this sets all the common options on its own. - -- - -- Requires LÖVE or LuaFileSystem. - -- - -- Will load from the directory given by `path` (string), in order: - -- * `config.ans`, which will be executed in the "config" namespace and may contains various optional configuration options: - -- * `anselme version`: number, version of the anselme language this game was made for - -- * `game version`: any, version information of the game. Can be used to perform eventual migration of save with an old version in the main file. - -- Always included in saved variables. - -- * `language`: string, built-in language file to load - -- * `inject directory`: string, directory that may contain "function start.ans", "checkpoint end.ans", etc. which content will be used to setup - -- the custom code injection methods (see vm:setinjection) - -- * `global directory`: string, path of global script directory. Every script file and subdirectory in the path will be loaded in the global namespace. - -- * `start expression`: string, expression that will be ran when starting the game - -- * files in the global directory, if defined in config.ans - -- * every other file in the path and subdirectories, using their path as namespace (i.e., contents of path/world1/john.ans will be defined in a function world1.john) - -- - -- Returns this VM in case of success. - -- - -- Returns nil, error message in case of error. - loadgame = function(self, path) - if self.game then error("game already loaded") end - -- load config - if is_file(path.."/config.ans") then - local s, e = self:loadfile(path.."/config.ans", "config") - if not s then return s, e end - s, e = self:eval("config") - if e then return s, e end - end - -- get config - self.game = { - anselme_version = self:eval("config.anselme version"), - game_version = self:eval("config.game version"), - language = self:eval("config.language"), - inject_directory = self:eval("config.inject directory"), - global_directory = self:eval("config.global directory"), - start_expression = self:eval("config.start expression") - } - -- check language version - if self.game.anselme_version and self.game.anselme_version ~= anselme.versions.language then - return nil, ("trying to load game made for Anselme language %s, but currently using version %s"):format(self.game.anselme_version, anselme.versions.language) - end - -- load language - if self.game.language then - local s, e = self:loadlanguage(self.game.language) - if not s then return s, e end - end - -- load injections - if self.game.inject_directory then - for inject, ninject in pairs(injections) do - local f = io.open(path.."/"..self.game.inject_directory.."/"..inject..".ans", "r") - if f then - self.state.inject[ninject] = f:read("*a") - f:close() - end - end - end - -- load global scripts - for _, item in ipairs(list_directory(path.."/"..self.game.global_directory)) do - if item:match("[^%.]") then - local p = path.."/"..self.game.global_directory.."/"..item - local s, e - if is_directory(p) then - s, e = self:loaddirectory(p) - elseif item:match("%.ans$") then - s, e = self:loadfile(p) - end - if not s then return s, e end - end - end - -- load other files - for _, item in ipairs(list_directory(path)) do - if item:match("[^%.]") and - item ~= "config.ans" and - item ~= self.game.global_directory and - item ~= self.game.inject_directory - then - local p = path.."/"..item - local s, e - if is_directory(p) then - s, e = self:loaddirectory(p, item) - elseif item:match("%.ans$") then - s, e = self:loadfile(p, item:gsub("%.ans$", "")) - end - if not s then return s, e end - end - end - return self - end, - --- Return a interpreter which runs the game start expression (if given). - -- - -- Returns interpreter in case of success. - -- - -- Returns nil, error message in case of error. - rungame = function(self) - if not self.game then error("no game loaded") end - if self.game.start_expression then - return self:run(self.game.start_expression) - else - return self:run("()") - end - end, - - --- Load code from a string. - -- Similar to Lua's code loading functions. - -- - -- Compared to their Lua equivalents, these also take an optional `name` argument (default="") that set the namespace to load the code in. Will define a new function is specified; otherwise, code will be parsed but not executable from an expression (as it is not named). - -- - -- Returns parsed block in case of success. - -- - -- Returns nil, error message in case of error. - loadstring = function(self, str, name, source) - local s, e = preparse(self.state, str, name or "", source) - if not s then return s, e end - return s - end, - --- Load code from a file. - -- See `vm:loadstring`. - loadfile = function(self, path, name) - local content - if love then - local e - content, e = love.filesystem.read(path) - if not content then return content, e end - else - local f, e = io.open(path, "r") - if not f then return f, e end - content = f:read("*a") - f:close() - end - local s, err = self:loadstring(content, name, path) - if not s then return s, err end - return s - end, - -- Load every file in a directory, using filename (without .ans extension) as its namespace. - -- - -- Requires LÖVE or LuaFileSystem. - -- - -- Returns this VM in case of success. - -- - -- Returns nil, error message in case of error. - loaddirectory = function(self, path, name) - if not name then name = "" end - name = name == "" and "" or name.."." - for _, item in ipairs(list_directory(path)) do - if item:match("[^%.]") then - local p = path.."/"..item - local s, e - if is_directory(p) then - s, e = self:loaddirectory(p, name..item) - elseif item:match("%.ans$") then - s, e = self:loadfile(p, name..item:gsub("%.ans$", "")) - end - if not s then return s, e end - end - end - return self - end, - - --- Set aliases for built-in variables 👁️, 🔖 and 🏁 that will be defined on every new checkpoint and function. - -- This does not affect variables that were defined before this function was called. - -- Set to nil for no alias. - -- - -- Returns this VM. - setaliases = function(self, seen, checkpoint, reached) - self.state.builtin_aliases["👁️"] = seen - self.state.builtin_aliases["🔖"] = checkpoint - self.state.builtin_aliases["🏁"] = reached - return self - end, - --- Set some code that will be injected at specific places in all code loaded after this is called. - -- Can typically be used to define variables for every function like 👁️, setting some value on every function resume, etc. - -- - -- Possible inject types: - -- * `"function start"`: injected at the start of every non-scoped function - -- * `"function end"`: injected at the end of every non-scoped function - -- * `"function return"`: injected at the end of each return's children that is contained in a non-scoped function - -- * `"checkpoint start"`: injected at the start of every checkpoint - -- * `"checkpoint end"`: injected at the end of every checkpoint - -- * `"class start"`: injected at the start of every class - -- * `"class end"`: injected at the end of every class - -- * `"scoped function start"`: injected at the start of every scoped function - -- * `"scoped function end"`: injected at the end of every scoped function - -- * `"scoped function return"`: injected at the end of each return's children that is contained in a scoped function - -- - -- Set `code` to nil to disable the inject. - -- - -- Returns this VM. - setinjection = function(self, inject, code) - assert(injections[inject], ("unknown injection type %q"):format(inject)) - self.state.inject[injections[inject]] = code - return self - end, - - --- Load and execute a built-in language file. - -- - -- The language file may optionally contain the special variables: - -- * alias 👁️: string, default alias for 👁️ - -- * alias 🏁: string, default alias for 🏁 - -- * alias 🔖: string, default alias for 🔖 - -- - -- Returns this VM in case of success. - -- - -- Returns nil, error message in case of error. - loadlanguage = function(self, lang) - local namespace = "anselme.languages."..lang - -- execute language file - local code = require(anselme_root.."stdlib.languages."..lang) - local s, e = self:loadstring(code, namespace, lang) - if not s then return s, e end - s, e = self:eval(namespace) - if e then return s, e end - -- set aliases for built-in variables - local seen_alias = self:eval(namespace..".alias 👁️") - local checkpoint_alias = self:eval(namespace..".alias 🔖") - local reached_alias = self:eval(namespace..".alias 🏁") - self:setaliases(seen_alias, checkpoint_alias, reached_alias) - return self - end, - - --- Define functions from Lua. - -- - -- * `signature`: string, full signature of the function - -- * `fn`: function (Lua function or table, see examples in `stdlib/functions.lua`) - -- - -- Alternatively, can also take a table as a sole argument to load several functions: { ["signature"] = fn, ... } - -- - -- Returns this VM. - loadfunction = function(self, signature, fn) - if type(signature) == "table" then - for k, v in pairs(signature) do - local s, e = self:loadfunction(k, v) - if not s then return nil, e end - end - else - if type(fn) == "function" then fn = { value = fn } end - self.state.link_next_function_definition_to_lua_function = fn - local s, e = self:loadstring(":$"..signature, "", "lua") - if not s then return nil, e end - assert(self.state.link_next_function_definition_to_lua_function == nil, "unexpected error while defining lua function") - return self - end - return self - end, - - --- Save/load script state - -- - -- Only saves persistent variables' full names and values. - -- Make sure to not change persistent variables names, class name, class attribute names, checkpoint names and functions names between a - -- save and a load (alias can of course be changed), as Anselme will not be able to match them to the old names stored in the save file. - -- - -- If a variable is stored in the save file but is not marked as persistent in the current scripts (e.g. if you updated the Anselme scripts to - -- remove the persistence), it will not be loaded. - -- - -- Loading should be done after loading all the game scripts (otherwise you will get "variable already defined" errors). - -- - -- Returns this VM. - load = function(self, data) - assert(anselme.versions.save == data.anselme.versions.save, ("trying to load data from an incompatible version of Anselme; save was done using save version %s but current version is %s"):format(data.anselme.versions.save, anselme.versions.save)) - for k, v in pairs(data.variables) do - if self.state.variable_metadata[k] then - if self.state.variable_metadata[k].persistent then - self.state.variables[k] = v - end - else - self.state.variables[k] = v -- non-existent variable: keep it in case there was a mistake, it's not going to affect anything anyway - end - end - return self - end, - --- Save script state. - -- See `vm:load`. - -- - -- Returns save data in case of success. - -- - -- Returns nil, error message in case of error. - save = function(self) - local vars = {} - for k, v in pairs(self.state.variables) do - if should_be_persisted(self.state, k, v) then - local s, e = check_persistable(v) - if not s then return nil, ("%s; while saving variable %s"):format(e, k) end - vars[k] = v - end - end - return { - anselme = { - versions = anselme.versions, - version = anselme.version - }, - variables = vars - } - end, - - --- Perform parsing that needs to be done after loading code. - -- This is automatically ran before starting an interpreter, but you may want to execute it before if you want to check for parsing error manually. - -- - -- Returns self in case of success. - -- - -- Returns nil, error message in case of error. - postload = function(self) - if #self.state.queued_lines > 0 then - local r, e = postparse(self.state) - if not r then return nil, e end - end - return self - end, - - --- Enable feature flags. - -- Available flags: - -- * `"strip trailing spaces"`: remove trailing spaces from choice and text events (enabled by default) - -- * `"strip duplicate spaces"`: remove duplicated spaces between text elements from choice and text events (enabled by default) - -- - -- Returns this VM. - enable = function(self, ...) - for _, flag in ipairs{...} do - self.state.feature_flags[flag] = true - end - return self - end, - --- Disable features flags. - -- Returns this VM. - disable = function(self, ...) - for _, flag in ipairs{...} do - self.state.feature_flags[flag] = nil - end - return self - end, - - --- Run code. - -- Will merge state after successful execution - -- - -- * `expr`: expression to evaluate (string or parsed expression), or a block to run - -- * `namespace`(default=""): namespace to evaluate the expression in - -- * `tags`(default={}): defaults tags when evaluating the expression (Lua value) - -- - -- Return interpreter in case of success. - -- - -- Returns nil, error message in case of error. - run = function(self, expr, namespace, tags) - local s, e = self:postload() - if not s then return s, e end - -- - local err - if type(expr) ~= "table" then expr, err = expression(tostring(expr), self.state, namespace or "", "vm:run") end - if not expr then return expr, err end - -- interpreter state - local interpreter - interpreter = { - state = { - inject = self.state.inject, - feature_flags = self.state.feature_flags, - builtin_aliases = self.state.builtin_aliases, - aliases = setmetatable({}, { __index = self.state.aliases }), - functions = self.state.functions, -- no need for a cache as we can't define or modify any function from the interpreter for now - variable_metadata = self.state.variable_metadata, -- no cache as metadata are expected to be constant - variables = setmetatable({}, { - __index = function(variables, k) - local cache = getmetatable(variables).cache - if cache[k] == nil then - cache[k] = copy(self.state.variables[k], getmetatable(variables).copy_cache) - end - return cache[k] - end, - -- variables that keep current state and should be cleared at each checkpoint - cache = {}, -- cache of previously read values (copies), to get repeatable reads & handle mutable types without changing global state - modified_tables = {}, -- list of modified tables (copies) that should be merged with global state on next checkpoint - copy_cache = {}, -- table of [original table] = copied table. Automatically filled by copy(). - -- keep track of scoped variables in scoped functions [fn line] = {{scoped variables}, next scope, ...} - -- (scoped variables aren't merged on checkpoint, shouldn't be cleared at checkpoints) - -- (only stores scoped variables that have been reassigned at some point (i.e. every accessed one since they start as undefined)) - scoped = {} - }), - interpreter = { - -- constant - global_state = self.state, - coroutine = coroutine.create(function() return "return", interpreter:run(expr, namespace) end), - -- status - running_line = nil, - -- choice event - choice_selected = nil, - -- skip next choices until next event change (to skip currently running choice block when resuming from a checkpoint) - skip_choices_until_flush = nil, - -- active event buffer stack - event_buffer_stack = {}, - -- current event waiting to be sent - current_event = nil, - -- interrupt - interrupt = nil, - -- tag stack - tags = {}, - -- default tags for everything in this interpreter (Lua values) - base_lua_tags = tags, - }, - }, - vm = self - } - return setmetatable(interpreter, interpreter_methods) - end, - --- Evaluate code. - -- Behave like `:run`, except the expression can not emit events and will return the result of the expression directly. - -- Merge state after sucessful execution automatically like `:run`. - -- - -- * `expr`: expression to evaluate (string or parsed expression), or a block to evaluate - -- * `namespace`(default=""): namespace to evaluate the expression in - -- * `tags`(default={}): defaults tags when evaluating the expression (Lua value) - -- - -- Return value in case of success (nil if nothing returned). - -- - -- Returns nil, error message in case of error. - eval = function(self, expr, namespace, tags) - local interpreter, err = self:run("()", namespace, tags) - if not interpreter then return interpreter, err end - local r, e = interpreter:eval(expr, namespace) - if e then return r, e end - assert(interpreter:step() == "return", "evaluated expression can not emit events") -- trigger merge / end-of-script things - return r - end -} -vm_mt.__index = vm_mt - --- return anselme module -return setmetatable(anselme, { - __call = function() - -- global state - local state = { - inject = { - -- function_start = "code block...", ... - }, - feature_flags = { - ["strip trailing spaces"] = true, - ["strip duplicate spaces"] = true - }, - builtin_aliases = { - -- ["👁️"] = "seen", - -- ["🔖"] = "checkpoint", - -- ["🏁"] = "reached" - }, - aliases = { - -- ["bonjour.salutation"] = "hello.greeting", ... - }, - functions = { - -- ["script.fn"] = { - -- { - -- function or checkpoint table - -- }, ... - -- }, ... - }, - variable_metadata = { - -- foo = { constant = true, persistent = true, constraint = constraint, ... }, ... - }, - variables = { - -- foo = { - -- type = "number", - -- value = 42 - -- }, ... - }, - queued_lines = { - -- { line = line, namespace = "foo" }, ... - }, - link_next_function_definition_to_lua_function = nil -- temporarly set to tell the preparser to link a anselme function definition with a lua function - } - local vm = setmetatable({ state = state }, vm_mt) - -- bootscript - local boot = assert(vm:loadstring(bootscript, "", "boot script")) - local _, e = vm:eval(boot) - if e then error(e) end - -- lua-defined functions - assert(vm:loadfunction(stdfuncs.lua)) - -- anselme-defined functions - local ansfunc = assert(vm:loadstring(stdfuncs.anselme, "", "built-in functions")) - _, e = vm:eval(ansfunc) - if e then return error(e) end - return vm - end -}) diff --git a/anselme.md b/anselme.md deleted file mode 100644 index c8f6b54..0000000 --- a/anselme.md +++ /dev/null @@ -1,322 +0,0 @@ -## Anselme Lua API reference - - We actively support LuaJIT and Lua 5.4. Lua 5.1, 5.2 and 5.3 *should* work but I don't always test against them. - - This documentation is generated from the main module file `anselme.lua` using `ldoc --ext md anselme.lua`. - - Example usage: - ```lua - local anselme = require("anselme") -- load main module - - local vm = anselme() -- create new VM - vm:loadgame("game") -- load some scripts, etc. - local interpreter = vm:rungame() -- create a new interpreter using what was loaded with :loadgame - - -- simple function to convert text event data into a string - -- in your game you may want to handle tags, here we ignore them for simplicity - local function format_text(text) - local r = "" - for _, l in ipairs(t) do - r = r .. l.text - end - return r - end - - -- event loop - repeat - local event, data = interpreter:step() -- progress script until next event - if event == "text" then - print(format_text(d)) - elseif event == "choice" then - for j, choice in ipairs(d) do - print(j.."> "..format_text(choice)) - end - interpreter:choose(io.read()) - elseif event == "error" then - error(data) - end - until t == "return" or t == "error" - ``` - - Calling the Anselme main module will create a return a new [VM](#vms). - - The main module also contain a few fields: - - -### anselme.versions - -Anselme version information table. - - Contains version informations as number (higher means more recent) of Anselme divied in a few categories: - - * `save`, which is incremented at each update which may break save compatibility - * `language`, which is incremented at each update which may break script file compatibility - * `api`, which is incremented at each update which may break Lua API compatibility - -### anselme.version - -General version number. - - It is incremented at each update. - -### anselme.running - -Currently running [interpreter](#interpreters). - `nil` if no interpreter running. - -## Interpreters - - An interpreter is in charge of running Anselme code and is spawned from a [VM](#vms). - Several interpreters from the same VM can run at the same time. - - Typically, you would have a interpreter for each script that need at the same time, for example one for every NPC - that is currently talking. - - Each interpreter can only run one script at a time, and will run it sequentially. - You can advance in the script by calling the `:step` method, which will run the script until an event is sent (for example some text needs to be displayed), - which will pause the whole interpreter until `:step` is called again. - - -### interpreter.vm - -[VM](#vms) this interpreter belongs to. - -### interpreter.end_event - -String, type of the event that stopped the interpreter (`nil` if interpreter is still running). - -### interpreter:step () - -Run the interpreter until the next event. - Returns event type (string), data (any). - - Will merge changed variables on successful script end. - - If event is `"return"` or `"error"`, the interpreter can not be stepped further and should be discarded. - - Default event types and their associated data: - * `text`: text to display, data is a list of text elements, each with a `text` field, containing the text contents, and a `tags` field, containing the tags associated with this text - * `choice`: choices to choose from, data is a list of choices Each of these choice is a list of text elements like for the `text` event - * `return`: when the script ends, data is the returned value (`nil` if nothing returned) - * `error`: when there is an error, data is the error message. - - See [LANGUAGE.md](LANGUAGE.md) for more details on events. - -### interpreter:choose (i) - -Select a choice. - `i` is the index (number) of the choice in the choice list (from the choice event's data). - - The choice will be selected on the next interpreter step. - - Returns this interpreter. - -### interpreter:interrupt (expr) - -Interrupt (abort the currently running script) the interpreter on the next step, executing an expression (string, if specified) in the current namespace instead. - - Returns this interpreter. - -### interpreter:current_namespace () - -Returns the namespace (string) the last ran line belongs to. - -### interpreter:run (expr, namespace) - -Run an expression (string) or block, optionally in a specific namespace (string, will use root namespace if not specified). - This may trigger events and must be called from within the interpreter coroutine (i.e. from a function called from a running script). - - No automatic merge if this change the interpreter state, merge is done once we reach end of script in a call to `:step` as usual. - - Returns the returned value (nil if nothing returned). - -### interpreter:eval (expr, namespace) - -Evaluate an expression (string) or block, optionally in a specific namespace (string, will use root namespace if not specified). - The expression can't yield events. - Can be called from outside the interpreter coroutine. Will create a new coroutine that operate on this interpreter state. - - No automatic merge if this change the interpreter state, merge is done once we reach end of script in a call to `:step` as usual. - - Returns the returned value in case of success (nil if nothing returned). - - Returns nil, error message in case of error. - -## VMs - - A VM stores the state required to run Anselme scripts. Each VM is completely independant from each other. - - -### vm:loadgame (path) - -Wrapper for loading a whole set of scripts (a "game"). - Should be preferred to other loading functions if possible as this sets all the common options on its own. - - Requires LÖVE or LuaFileSystem. - - Will load from the directory given by `path` (string), in order: - * `config.ans`, which will be executed in the "config" namespace and may contains various optional configuration options: - * `anselme version`: number, version of the anselme language this game was made for - * `game version`: any, version information of the game. Can be used to perform eventual migration of save with an old version in the main file. - Always included in saved variables. - * `language`: string, built-in language file to load - * `inject directory`: string, directory that may contain "function start.ans", "checkpoint end.ans", etc. which content will be used to setup - the custom code injection methods (see vm:setinjection) - * `global directory`: string, path of global script directory. Every script file and subdirectory in the path will be loaded in the global namespace. - * `start expression`: string, expression that will be ran when starting the game - * files in the global directory, if defined in config.ans - * every other file in the path and subdirectories, using their path as namespace (i.e., contents of path/world1/john.ans will be defined in a function world1.john) - - Returns this VM in case of success. - - Returns nil, error message in case of error. - -### vm:rungame () - -Return a interpreter which runs the game start expression (if given). - - Returns interpreter in case of success. - - Returns nil, error message in case of error. - -### vm:loadstring (str, name, source) - -Load code from a string. - Similar to Lua's code loading functions. - - Compared to their Lua equivalents, these also take an optional `name` argument (default="") that set the namespace to load the code in. Will define a new function is specified; otherwise, code will be parsed but not executable from an expression (as it is not named). - - Returns parsed block in case of success. - - Returns nil, error message in case of error. - -### vm:loadfile (path, name) - -Load code from a file. - See `vm:loadstring`. - -### vm:setaliases (seen, checkpoint, reached) - -Set aliases for built-in variables 👁️, 🔖 and 🏁 that will be defined on every new checkpoint and function. - This does not affect variables that were defined before this function was called. - Set to nil for no alias. - - Returns this VM. - -### vm:setinjection (inject, code) - -Set some code that will be injected at specific places in all code loaded after this is called. - Can typically be used to define variables for every function like 👁️, setting some value on every function resume, etc. - - Possible inject types: - * `"function start"`: injected at the start of every non-scoped function - * `"function end"`: injected at the end of every non-scoped function - * `"function return"`: injected at the end of each return's children that is contained in a non-scoped function - * `"checkpoint start"`: injected at the start of every checkpoint - * `"checkpoint end"`: injected at the end of every checkpoint - * `"class start"`: injected at the start of every class - * `"class end"`: injected at the end of every class - * `"scoped function start"`: injected at the start of every scoped function - * `"scoped function end"`: injected at the end of every scoped function - * `"scoped function return"`: injected at the end of each return's children that is contained in a scoped function - - Set `code` to nil to disable the inject. - - Returns this VM. - -### vm:loadlanguage (lang) - -Load and execute a built-in language file. - - The language file may optionally contain the special variables: - * alias 👁️: string, default alias for 👁️ - * alias 🏁: string, default alias for 🏁 - * alias 🔖: string, default alias for 🔖 - - Returns this VM in case of success. - - Returns nil, error message in case of error. - -### vm:loadfunction (signature, fn) - -Define functions from Lua. - - * `signature`: string, full signature of the function - * `fn`: function (Lua function or table, see examples in `stdlib/functions.lua`) - - Returns this VM. - -### vm:load (data) - -Save/load script state - - Only saves persistent variables' full names and values. - Make sure to not change persistent variables names, class name, class attribute names, checkpoint names and functions names between a - save and a load (alias can of course be changed), as Anselme will not be able to match them to the old names stored in the save file. - - If a variable is stored in the save file but is not marked as persistent in the current scripts (e.g. if you updated the Anselme scripts to - remove the persistence), it will not be loaded. - - Loading should be done after loading all the game scripts (otherwise you will get "variable already defined" errors). - - Returns this VM. - -### vm:save () - -Save script state. - See `vm:load`. - - Returns save data in case of success. - - Returns nil, error message in case of error. - -### vm:postload () - -Perform parsing that needs to be done after loading code. - This is automatically ran before starting an interpreter, but you may want to execute it before if you want to check for parsing error manually. - - Returns self in case of success. - - Returns nil, error message in case of error. - -### vm:enable (...) - -Enable feature flags. - Available flags: - * `"strip trailing spaces"`: remove trailing spaces from choice and text events (enabled by default) - * `"strip duplicate spaces"`: remove duplicated spaces between text elements from choice and text events (enabled by default) - - Returns this VM. - -### vm:disable (...) - -Disable features flags. - Returns this VM. - -### vm:run (expr, namespace, tags) - -Run code. - Will merge state after successful execution - - * `expr`: expression to evaluate (string or parsed expression), or a block to run - * `namespace`(default=""): namespace to evaluate the expression in - * `tags`(default={}): defaults tags when evaluating the expression (Lua value) - - Return interpreter in case of success. - - Returns nil, error message in case of error. - -### vm:eval (expr, namespace, tags) - -Evaluate code. - Behave like `:run`, except the expression can not emit events and will return the result of the expression directly. - Merge state after sucessful execution automatically like `:run`. - - * `expr`: expression to evaluate (string or parsed expression), or a block to evaluate - * `namespace`(default=""): namespace to evaluate the expression in - * `tags`(default={}): defaults tags when evaluating the expression (Lua value) - - Return value in case of success (nil if nothing returned). - - Returns nil, error message in case of error. - diff --git a/ast/ArgumentTuple.lua b/ast/ArgumentTuple.lua new file mode 100644 index 0000000..c242743 --- /dev/null +++ b/ast/ArgumentTuple.lua @@ -0,0 +1,207 @@ +local ast = require("ast") +local Identifier, Number + +local operator_priority = require("common").operator_priority + +local ArgumentTuple +ArgumentTuple = ast.abstract.Node { + type = "argument tuple", + + list = nil, -- list of expr + named = nil, -- { [string] = expr, ... } + assignment = nil, -- expr + arity = 0, + + init = function(self, ...) + self.list = { ... } + self.named = {} + self.arity = #self.list + end, + insert_positional = function(self, position, val) -- only for construction + local l = {} + for k, v in pairs(self.list) do + if k >= position then l[k+1] = v + else l[k] = v end + end + l[position] = val + self.list = l + self.arity = self.arity + 1 + end, + set_positional = function(self, position, val) -- only for construction + assert(not self.list[position]) + self.list[position] = val + self.arity = self.arity + 1 + end, + set_named = function(self, identifier, val) -- only for construction + local name = identifier.name + assert(not self.named[name]) + self.named[name] = val + self.arity = self.arity + 1 + end, + set_assignment = function(self, val) -- only for construction + assert(not self.assignment) + self.assignment = val + self.arity = self.arity + 1 + self.format_priority = operator_priority["_=_"] + end, + + _format = function(self, state, priority, ...) + local l = {} + for _, e in pairs(self.list) do + table.insert(l, e:format(state, operator_priority["_,_"], ...)) + end + for n, e in pairs(self.named) do + table.insert(l, n.."="..e:format_right(state, operator_priority["_=_"], ...)) + end + local s = ("(%s)"):format(table.concat(l, ", ")) + if self.assignment then + s = s .. (" = %s"):format(self.assignment:format_right(state, operator_priority["_=_"], ...)) + end + return s + end, + + traverse = function(self, fn, ...) + for _, e in pairs(self.list) do + fn(e, ...) + end + for _, e in pairs(self.named) do + fn(e, ...) + end + if self.assignment then + fn(self.assignment, ...) + end + end, + + -- need to redefine hash to include a table.sort as pairs() in :traverse is non-deterministic + -- as well as doesn't account for named arguments names + _hash = function(self) + local t = {} + for _, e in pairs(self.list) do + table.insert(t, e:hash()) + end + for n, e in pairs(self.named) do + table.insert(t, ("%s=%s"):format(n, e:hash())) + end + if self.assignment then + table.insert(t, self.assignment:hash()) + end + table.sort(t) + return ("%s<%s>"):format(self.type, table.concat(t, ";")) + end, + + _eval = function(self, state) + local r = ArgumentTuple:new() + for i, e in pairs(self.list) do + r:set_positional(i, e:eval(state)) + end + for n, e in pairs(self.named) do + r:set_named(Identifier:new(n), e:eval(state)) + end + if self.assignment then + r:set_assignment(self.assignment:eval(state)) + end + return r + end, + + with_first_argument = function(self, first) + local r = ArgumentTuple:new() + r:set_positional(1, first) + for i, e in pairs(self.list) do + r:set_positional(i+1, e) + end + for n, e in pairs(self.named) do + r:set_named(Identifier:new(n), e) + end + if self.assignment then + r:set_assignment(self.assignment) + end + return r + end, + + -- return specificity (>=0), secondary specificity (>=0) + -- return false, failure message + match_parameter_tuple = function(self, state, params) + -- basic arity checks + if self.arity > params.max_arity or self.arity < params.min_arity then + if params.min_arity == params.max_arity then + return false, ("expected %s arguments, received %s"):format(params.min_arity, self.arity) + else + return false, ("expected between %s and %s arguments, received %s"):format(params.min_arity, params.max_arity, self.arity) + end + end + if params.assignment and not self.assignment then + return false, "expected an assignment argument" + end + -- search for parameter -> argument match + local specificity = 0 + local used_list = {} + local used_named = {} + local used_assignment = false + for i, param in ipairs(params.list) do + -- search in args + local arg + if self.list[i] then + used_list[i] = true + arg = self.list[i] + elseif self.named[param.identifier.name] then + used_named[param.identifier.name] = true + arg = self.named[param.identifier.name] + elseif i == params.max_arity and params.assignment and self.assignment then + used_assignment = true + arg = self.assignment + elseif param.default then + arg = param.default + end + -- not found + if not arg then return false, ("missing parameter %s"):format(param.identifier:format(state)) end + -- type check + if param.type_check then + local r = param.type_check:call(state, ArgumentTuple:new(arg)) + if not r:truthy() then return false, ("type check failure for parameter %s in function %s"):format(param.identifier:format(state), params:format(state)) end + if Number:is(r) then + specificity = specificity + r.number + else + specificity = specificity + 1 + end + end + end + -- check for unused arguments + for i in pairs(self.list) do + if not used_list[i] then + return false, ("%sth positional argument is unused"):format(i) + end + end + for n in pairs(self.named) do + if not used_named[n] then + return false, ("named argument %s is unused"):format(n) + end + end + if self.assignment and not used_assignment then + return false, "assignment argument is unused" + end + -- everything is A-ok + return specificity, params.eval_depth + end, + + -- assume :match_parameter_tuple was already called and returned true + bind_parameter_tuple = function(self, state, params) + for i, arg in ipairs(params.list) do + if self.list[i] then + state.scope:define(arg.identifier:to_symbol(), self.list[i]) + elseif self.named[arg.identifier.name] then + state.scope:define(arg.identifier:to_symbol(), self.named[arg.identifier.name]) + elseif i == params.max_arity and params.assignment then + state.scope:define(arg.identifier:to_symbol(), self.assignment) + elseif arg.default then + state.scope:define(arg.identifier:to_symbol(), arg.default:eval(state)) + else + error(("no argument matching parameter %q"):format(arg.identifier.name)) + end + end + end +} + +package.loaded[...] = ArgumentTuple +Identifier, Number = ast.Identifier, ast.Number + +return ArgumentTuple diff --git a/ast/Assignment.lua b/ast/Assignment.lua new file mode 100644 index 0000000..0a6f23b --- /dev/null +++ b/ast/Assignment.lua @@ -0,0 +1,37 @@ +local ast = require("ast") +local Nil + +local operator_priority = require("common").operator_priority + +local Assignment = ast.abstract.Node { + type = "assignment", + + identifier = nil, + expression = nil, + format_priority = operator_priority["_=_"], + + init = function(self, identifier, expression) + self.identifier = identifier + self.expression = expression + end, + + _format = function(self, ...) + return self.identifier:format(...).." = "..self.expression:format_right(...) + end, + + traverse = function(self, fn, ...) + fn(self.identifier, ...) + fn(self.expression, ...) + end, + + _eval = function(self, state) + local val = self.expression:eval(state) + state.scope:set(self.identifier, val) + return Nil:new() + end, +} + +package.loaded[...] = Assignment +Nil = ast.Nil + +return Assignment diff --git a/ast/AttachBlock.lua b/ast/AttachBlock.lua new file mode 100644 index 0000000..a61a582 --- /dev/null +++ b/ast/AttachBlock.lua @@ -0,0 +1,49 @@ +local ast = require("ast") +local Identifier, Quote + +local attached_block_identifier + +local AttachBlock = ast.abstract.Node { + type = "attach block", + + expression = nil, + block = nil, + + init = function(self, expression, block) + self.expression = expression + self.block = block + self.format_priority = self.expression.format_priority + end, + + _format = function(self, state, priority, indentation, ...) + return self.expression:format(state, priority, indentation, ...).."\n\t"..self.block:format(state, priority, indentation + 1, ...) + end, + + traverse = function(self, fn, ...) + fn(self.expression, ...) + fn(self.block, ...) + end, + + _eval = function(self, state) + state.scope:push_partial(attached_block_identifier) + state.scope:define(attached_block_identifier:to_symbol(), Quote:new(self.block)) -- _ is always wrapped in a Call when it appears + local exp = self.expression:eval(state) + state.scope:pop() + + return exp + end, + + _prepare = function(self, state) + state.scope:push_partial(attached_block_identifier) + state.scope:define(attached_block_identifier:to_symbol(), Quote:new(self.block)) + self.expression:prepare(state) + state.scope:pop() + end +} + +package.loaded[...] = AttachBlock +Identifier, Quote = ast.Identifier, ast.Quote + +attached_block_identifier = Identifier:new("_") + +return AttachBlock diff --git a/ast/Block.lua b/ast/Block.lua new file mode 100644 index 0000000..76ea90f --- /dev/null +++ b/ast/Block.lua @@ -0,0 +1,80 @@ +local ast = require("ast") +local Nil, Return, AutoCall, ArgumentTuple, Flush + +local Block = ast.abstract.Node { + type = "block", + + expressions = {}, + + init = function(self) + self.expressions = {} + end, + add = function(self, expression) -- only for construction + table.insert(self.expressions, expression) + end, + + _format = function(self, state, prio, ...) + local l = {} + for _, e in ipairs(self.expressions) do + if Flush:is(e) then + table.insert(l, (e:format(state, 0, ...):gsub("\n$", ""))) + else + table.insert(l, e:format(state, 0, ...)) + end + end + return table.concat(l, "\n") + end, + + traverse = function(self, fn, ...) + for _, e in ipairs(self.expressions) do + fn(e, ...) + end + end, + + _eval = function(self, state) + local r + state.scope:push() + if self:resuming(state) then + local resuming = self:get_resume_data(state) + local resumed = false + for _, e in ipairs(self.expressions) do + if e == resuming then resumed = true end + if resumed then + r = e:eval(state) + if AutoCall:issub(r) then + r = r:call(state, ArgumentTuple:new()) + end + if Return:is(r) then + break -- pass on to parent block until we reach a function boundary + end + end + end + else + for _, e in ipairs(self.expressions) do + self:set_resume_data(state, e) + r = e:eval(state) + if AutoCall:issub(r) then + r = r:call(state, ArgumentTuple:new()) + end + if Return:is(r) then + break -- pass on to parent block until we reach a function boundary + end + end + end + state.scope:pop() + return r or Nil:new() + end, + + _prepare = function(self, state) + state.scope:push() + for _, e in ipairs(self.expressions) do + e:prepare(state) + end + state.scope:pop() + end +} + +package.loaded[...] = Block +Nil, Return, AutoCall, ArgumentTuple, Flush = ast.Nil, ast.Return, ast.abstract.AutoCall, ast.ArgumentTuple, ast.Flush + +return Block diff --git a/ast/Boolean.lua b/ast/Boolean.lua new file mode 100644 index 0000000..25fa100 --- /dev/null +++ b/ast/Boolean.lua @@ -0,0 +1,28 @@ +local ast = require("ast") + +return ast.abstract.Node { + type = "boolean", + _evaluated = true, -- no evaluation needed + + value = nil, + + init = function(self, val) + self.value = val + end, + + _hash = function(self) + return ("boolean<%q>"):format(self.value) + end, + + _format = function(self) + return tostring(self.value) + end, + + to_lua = function(self, state) + return self.value + end, + + truthy = function(self) + return self.value + end +} diff --git a/ast/Branched.lua b/ast/Branched.lua new file mode 100644 index 0000000..5aeb43f --- /dev/null +++ b/ast/Branched.lua @@ -0,0 +1,59 @@ +-- branched: associate to each branch a different value +-- used to handle mutability. probably the only mutable node you'll ever need! it's literally perfect! +-- note: all values here are expected to be already evaluated + +local ast = require("ast") + +local Branched = ast.abstract.Runtime { + type = "branched", + mutable = true, + + value = nil, -- { [branch name] = value, ... } + + init = function(self, state, value) + self.value = {} + self:set(state, value) + end, + + in_branch = function(self, state) + return not not self.value[state.branch_id] + end, + get = function(self, state) + return self.value[state.branch_id] or self.value[state.source_branch_id] + end, + set = function(self, state, value) + self.value[state.branch_id] = value + end, + _merge = function(self, state, cache) + local val = self.value[state.branch_id] + if val then + self.value[state.source_branch_id] = val + self.value[state.branch_id] = nil + val:merge(state, cache) + end + end, + + _format = function(self, state, ...) + if state then + return self:get(state):format(state, ...) + else + local t = {} + for b, v in pairs(self.value) do + table.insert(t, ("%s→%s"):format(b, v)) + end + return "<"..table.concat(t, ", ")..">" + end + end, + + traverse = function(self, fn, ...) + for _, v in pairs(self.value) do + fn(v, ...) + end + end, + + _eval = function(self, state) + return self:get(state) + end +} + +return Branched diff --git a/ast/Call.lua b/ast/Call.lua new file mode 100644 index 0000000..3f9ac0e --- /dev/null +++ b/ast/Call.lua @@ -0,0 +1,86 @@ +local ast = require("ast") +local Identifier + +local regular_operators = require("common").regular_operators +local operator_priority = require("common").operator_priority + +local function reverse(t, fmt) + for _, v in ipairs(t) do t[fmt:format(v[1])] = v[2] end + return t +end +local infix = reverse(regular_operators.infixes, "_%s_") +local prefix = reverse(regular_operators.prefixes, "%s_") +local suffix = reverse(regular_operators.suffixes, "_%s") + +local Call + +Call = ast.abstract.Node { + type = "call", + + func = nil, + arguments = nil, -- ArgumentTuple + format_priority = infix["_!"], -- often overwritten in :init + + init = function(self, func, arguments) + self.func = func + self.arguments = arguments + + -- get priority: operators + if Identifier:is(self.func) then + local name, arity = self.func.name, self.arguments.arity + if infix[name] and arity == 2 then + self.format_priority = infix[name] + elseif prefix[name] and arity == 1 then + self.format_priority = prefix[name] + elseif suffix[name] and arity == 1 then + self.format_priority = suffix[name] + end + end + if self.arguments.assignment then + self.format_priority = operator_priority["_=_"] + end + end, + + _format = function(self, ...) + if self.arguments.arity == 0 then + if Identifier:is(self.func) and self.func.name == "_" then + return "_" -- the _ identifier is automatically re-wrapped in a Call when it appears + end + local func = self.func:format(...) + return func.."!" + else + if Identifier:is(self.func) then + local name, arity = self.func.name, self.arguments.arity + if infix[name] and arity == 2 then + local left = self.arguments.list[1]:format(...) + local right = self.arguments.list[2]:format_right(...) + return ("%s %s %s"):format(left, name:match("^_(.*)_$"), right) + elseif prefix[name] and arity == 1 then + local right = self.arguments.list[1]:format_right(...) + return ("%s%s"):format(name:match("^(.*)_$"), right) + elseif suffix[name] and arity == 1 then + local left = self.arguments.list[1]:format(...) + return ("%s%s"):format(left, name:match("^_(.*)$")) + end + end + return self.func:format(...)..self.arguments:format(...) -- no need for format_right, we already handle the assignment priority here + end + end, + + traverse = function(self, fn, ...) + fn(self.func, ...) + fn(self.arguments, ...) + end, + + _eval = function(self, state) + local func = self.func:eval(state) + local arguments = self.arguments:eval(state) + + return func:call(state, arguments) + end +} + +package.loaded[...] = Call +Identifier = ast.Identifier + +return Call diff --git a/ast/Choice.lua b/ast/Choice.lua new file mode 100644 index 0000000..5bb8f22 --- /dev/null +++ b/ast/Choice.lua @@ -0,0 +1,52 @@ +local ast = require("ast") +local ArgumentTuple + +local operator_priority = require("common").operator_priority + +local Choice +Choice = ast.abstract.Runtime { + type = "choice", + + text = nil, + func = nil, + format_priority = operator_priority["_|>_"], + + init = function(self, text, func) + self.text = text + self.func = func + end, + + traverse = function(self, fn, ...) + fn(self.text, ...) + fn(self.func, ...) + end, + + _format = function(self, ...) + return ("%s |> %s"):format(self.text:format(...), self.func:format_right(...)) + end, + + build_event_data = function(self, state, event_buffer) + local l = { + _selected = nil, + choose = function(self, choice) + self._selected = choice + end + } + for _, c in event_buffer:iter(state) do + table.insert(l, c.text) + end + return l + end, + post_flush_callback = function(self, state, event_buffer, data) + local choice = data._selected + assert(choice, "no choice made") + assert(choice > 0 and choice <= event_buffer:len(state), "choice out of bounds") + + event_buffer:get(state, choice).func:call(state, ArgumentTuple:new()) + end +} + +package.loaded[...] = Choice +ArgumentTuple = ast.ArgumentTuple + +return Choice diff --git a/ast/Closure.lua b/ast/Closure.lua new file mode 100644 index 0000000..435ac72 --- /dev/null +++ b/ast/Closure.lua @@ -0,0 +1,58 @@ +-- note: functions only appear in non-evaluated nodes! once evaluated, they always become closures + +local ast = require("ast") +local Overloadable, Runtime = ast.abstract.Overloadable, ast.abstract.Runtime +local Definition + +local Closure +Closure = Runtime(Overloadable) { + type = "closure", + + func = nil, -- Function + scope = nil, -- Environment + exported_scope = nil, -- Environment + + init = function(self, func, state) + self.func = func + self.scope = state.scope:capture() + + -- layer a new export layer on top of captured/current scope + state.scope:push_export() + self.exported_scope = state.scope:capture() + + -- pre-define exports + for sym, exp in pairs(self.func.exports) do + Definition:new(sym, exp):eval(state) + end + + state.scope:pop() + end, + + _format = function(self, ...) + return self.func:format(...) + end, + + traverse = function(self, fn, ...) + fn(self.func, ...) + fn(self.scope, ...) + fn(self.exported_scope, ...) + end, + + compatible_with_arguments = function(self, state, args) + return args:match_parameter_tuple(state, self.func.parameters) + end, + format_parameters = function(self, state) + return self.func.parameters:format(state) + end, + call_compatible = function(self, state, args) + state.scope:push(self.exported_scope) + local exp = self.func:call_compatible(state, args) + state.scope:pop() + return exp + end, +} + +package.loaded[...] = Closure +Definition = ast.Definition + +return Closure diff --git a/ast/Definition.lua b/ast/Definition.lua new file mode 100644 index 0000000..d69e320 --- /dev/null +++ b/ast/Definition.lua @@ -0,0 +1,60 @@ +local ast = require("ast") +local Nil, Overloadable + +local operator_priority = require("common").operator_priority + +local Definition = ast.abstract.Node { + type = "definition", + + symbol = nil, + expression = nil, + format_priority = operator_priority["_=_"], + + init = function(self, symbol, expression) + self.symbol = symbol + self.expression = expression + end, + + _format = function(self, ...) + return self.symbol:format(...).." = "..self.expression:format_right(...) + end, + + traverse = function(self, fn, ...) + fn(self.symbol, ...) + fn(self.expression, ...) + end, + + _eval = function(self, state) + if self.symbol.exported and state.scope:defined_in_current(self.symbol) then + return Nil:new() -- export vars: can reuse existing defining + end + + local symbol = self.symbol:eval(state) + local val = self.expression:eval(state) + + if Overloadable:issub(val) then + state.scope:define_overloadable(symbol, val) + else + state.scope:define(symbol, val) + end + + return Nil:new() + end, + + _prepare = function(self, state) + local symbol, val = self.symbol, self.expression + symbol:prepare(state) + val:prepare(state) + + if Overloadable:issub(val) then + state.scope:define_overloadable(symbol, val) + else + state.scope:define(symbol, val) + end + end +} + +package.loaded[...] = Definition +Nil, Overloadable = ast.Nil, ast.abstract.Overloadable + +return Definition diff --git a/ast/Environment.lua b/ast/Environment.lua new file mode 100644 index 0000000..adf8d63 --- /dev/null +++ b/ast/Environment.lua @@ -0,0 +1,214 @@ +local ast = require("ast") + +local operator_priority = require("common").operator_priority + +local Branched, ArgumentTuple, Overload, Overloadable, Table + +local VariableMetadata = ast.abstract.Runtime { + type = "variable metadata", + + symbol = nil, + branched = nil, + format_priority = operator_priority["_=_"], + + init = function(self, state, symbol, value) + self.symbol = symbol + self.branched = Branched:new(state, value) + end, + get = function(self, state) + return self.branched:get(state) + end, + set = function(self, state, value) + assert(not self.symbol.constant, ("trying to change the value of constant %s"):format(self.symbol.string)) + if self.symbol.type_check then + local r = self.symbol.type_check:call(state, ArgumentTuple:new(value)) + if not r:truthy() then error(("type check failure for %s; %s does not satisfy %s"):format(self.symbol.string, value, self.symbol.type_check)) end + end + self.branched:set(state, value) + end, + + _format = function(self, ...) + return ("%s=%s"):format(self.symbol:format(...), self.branched:format(...)) + end, + traverse = function(self, fn, ...) + fn(self.symbol, ...) + fn(self.branched, ...) + end, + + _merge = function(self, state, cache) + if not self.symbol.confined_to_branch then + self.branched:merge(state, cache) + end + end +} + +local Environment = ast.abstract.Runtime { + type = "environment", + + parent = nil, -- environment or nil + variables = nil, -- Table of { {identifier} = variable metadata, ... } + + partial = nil, -- { [name string] = true, ... } + export = nil, -- bool + + init = function(self, state, parent, partial_names, is_export) + self.variables = Table:new(state) + self.parent = parent + self.partial = partial_names + self.export = is_export + end, + + traverse = function(self, fn, ...) + if self.parent then + fn(self.parent, ...) + end + fn(self.variables, ...) + end, + _format = function(self, state) + return "" + end, + + -- define new variable in the environment + define = function(self, state, symbol, exp) + local name = symbol.string + if self:defined_in_current(state, symbol) then + error(name.." is already defined in the current scope") + end + if (self.partial and not self.partial[name]) + or (self.export ~= symbol.exported) then + return self.parent:define(state, symbol, exp) + end + self.variables:set(state, symbol:to_identifier(), VariableMetadata:new(state, symbol, exp)) + end, + -- define or redefine new overloadable variable in current environment, inheriting existing overload variants from (parent) scopes + define_overloadable = function(self, state, symbol, exp) + assert(Overloadable:issub(exp), "trying to add an non-overloadable value to an overload") + + local identifier = symbol:to_identifier() + + -- add overload variants already defined in current or parent scope + if self:defined(state, identifier) then + local val = self:get(state, identifier) + if Overload:is(val) then + exp = Overload:new(exp) + for _, v in ipairs(val.list) do + exp:insert(v) + end + elseif Overloadable:issub(val) then + exp = Overload:new(exp, val) + elseif self:defined_in_current(state, symbol) then + error(("can't add an overload variant to non-overloadable variable %s defined in the same scope"):format(identifier)) + end + end + + -- update/define in current scope + if self:defined_in_current(state, symbol) then + self:set(state, identifier, exp) + else + self:define(state, symbol, exp) + end + end, + + -- returns bool if variable defined in current or parent environment + defined = function(self, state, identifier) + if self.variables:has(state, identifier) then + return true + elseif self.parent then + return self.parent:defined(state, identifier) + end + return false + end, + -- returns bool if variable defined in current environment layer + -- (note: by current layer, we mean the closest one where the variable is able to exist - if it is exported, the closest export layer, etc.) + defined_in_current = function(self, state, symbol) + local name = symbol.string + if self.variables:has(state, symbol:to_identifier()) then + return true + elseif (self.partial and not self.partial[name]) + or (self.export ~= symbol.exported) then + return self.parent:defined_in_current(state, symbol) + end + return false + end, + -- return bool if variable is defined in the current environment only - won't search in parent event for exported & partial names + defined_in_current_strict = function(self, state, identifier) + return self.variables:has(state, identifier) + end, + + -- get variable in current or parent scope, with metadata + _get_variable = function(self, state, identifier) + if self:defined(state, identifier) then + if self.variables:has(state, identifier) then + return self.variables:get(state, identifier) + elseif self.parent then + return self.parent:_get_variable(state, identifier) + end + else + error(("identifier %q is undefined in branch %s"):format(identifier.name, state.branch_id), 0) + end + end, + -- get variable value in current or parent environment + get = function(self, state, identifier) + return self:_get_variable(state, identifier):get(state) + end, + -- set variable value in current or parent environment + set = function(self, state, identifier, val) + return self:_get_variable(state, identifier):set(state, val) + end, + + -- returns a list {[symbol]=val,...} of all persistent variables in the current strict layer + list_persistent = function(self, state) + assert(self.export, "not an export scope layer") + local r = {} + for _, vm in self.variables:iter(state) do + if vm.symbol.persistent then + r[vm.symbol] = vm:get(state) + end + end + return r + end, + -- returns a list {[symbol]=val,...} of all exported variables in the current strict layer + list_exported = function(self, state) + assert(self.export, "not an export scope layer") + local r = {} + for _, vm in self.variables:iter(state) do + if vm.symbol.exported then + r[vm.symbol] = vm:get(state) + end + end + return r + end, + -- return the depth of the environmenet, i.e. the number of parents + depth = function(self) + local d = 0 + local e = self + while e.parent do + e = e.parent + d = d + 1 + end + return d + end, + + _debug_state = function(self, state, filter, t, level) + level = level or 0 + t = t or {} + + local indentation = string.rep(" ", level) + table.insert(t, ("%s> %s %s scope"):format(indentation, self.export and "exported" or "", self.partial and "partial" or "")) + + for name, var in self.variables:iter(state) do + if name.name:match(filter) then + table.insert(t, ("%s| %s = %s"):format(indentation, name, var:get(state))) + end + end + if self.parent then + self.parent:_debug_state(state, filter, t, level+1) + end + return t + end, +} + +package.loaded[...] = Environment +Branched, ArgumentTuple, Overload, Overloadable, Table = ast.Branched, ast.ArgumentTuple, ast.Overload, ast.abstract.Overloadable, ast.Table + +return Environment diff --git a/ast/Flush.lua b/ast/Flush.lua new file mode 100644 index 0000000..5fefa04 --- /dev/null +++ b/ast/Flush.lua @@ -0,0 +1,28 @@ +local ast = require("ast") +local Nil + +local event_manager = require("state.event_manager") + +local Flush = ast.abstract.Node { + type = "flush", + + init = function(self) end, + + _hash = function(self) + return "flush" + end, + + _format = function(self) + return "\n" + end, + + _eval = function(self, state) + event_manager:flush(state) + return Nil:new() + end +} + +package.loaded[...] = Flush +Nil = ast.Nil + +return Flush diff --git a/ast/Function.lua b/ast/Function.lua new file mode 100644 index 0000000..63132b8 --- /dev/null +++ b/ast/Function.lua @@ -0,0 +1,78 @@ +-- note: functions only appear in non-evaluated nodes! once evaluated, they always become closures + +local ast = require("ast") +local Overloadable = ast.abstract.Overloadable +local Closure, ReturnBoundary + +local operator_priority = require("common").operator_priority + +local Function +Function = Overloadable { + type = "function", + + parameters = nil, -- ParameterTuple + expression = nil, + format_priority = operator_priority["$_"], + + exports = nil, -- { [sym] = exp, ... }, exctracted from expression during :prepare + + init = function(self, parameters, expression, exports) + self.parameters = parameters + self.expression = ReturnBoundary:new(expression) + self.exports = exports or {} + end, + + _format = function(self, ...) + if self.parameters.assignment then + return "$"..self.parameters:format(...).."; "..self.expression:format_right(...) + else + return "$"..self.parameters:format(...).." "..self.expression:format_right(...) + end + end, + + traverse = function(self, fn, ...) + fn(self.parameters, ...) + fn(self.expression, ...) + end, + + compatible_with_arguments = function(self, state, args) + return args:match_parameter_tuple(state, self.parameters) + end, + format_parameters = function(self, state) + return self.parameters:format(state) + end, + call_compatible = function(self, state, args) + state.scope:push() + args:bind_parameter_tuple(state, self.parameters) + + local exp = self.expression:eval_resumable(state) + + state.scope:pop() + + -- reminder: don't do any additionnal processing here as that won't be executed when resuming self.expression + -- instead wrap it in some additional node, like our friend ReturnBoundary + + return exp + end, + + _eval = function(self, state) + return Closure:new(Function:new(self.parameters:eval(state), self.expression, self.exports), state) + end, + + _prepare = function(self, state) + state.scope:push_export() -- recreate scope context that will be created by closure + + state.scope:push() + self.parameters:prepare(state) + self.expression:prepare(state) + state.scope:pop() + + self.exports = state.scope:capture():list_exported(state) + state.scope:pop() + end, +} + +package.loaded[...] = Function +Closure, ReturnBoundary = ast.Closure, ast.ReturnBoundary + +return Function diff --git a/ast/FunctionParameter.lua b/ast/FunctionParameter.lua new file mode 100644 index 0000000..c5244bb --- /dev/null +++ b/ast/FunctionParameter.lua @@ -0,0 +1,45 @@ +local ast = require("ast") +local operator_priority = require("common").operator_priority + +local FunctionParameter +FunctionParameter = ast.abstract.Node { + type = "function parameter", + + identifier = nil, + default = nil, -- can be nil + type_check = nil, -- can be nil + + init = function(self, identifier, default, type_check) + self.identifier = identifier + self.default = default + self.type_check = type_check + if default then + self.format_priority = operator_priority["_=_"] + elseif type_check then -- type_check has higher prio than assignment in any case + self.format_priority = operator_priority["_::_"] + end + end, + + _format = function(self, state, prio, ...) + local s = self.identifier:format(state, prio, ...) + if self.type_check then + s = s .. "::" .. self.type_check:format_right(state, operator_priority["_::_"], ...) + end + if self.default then + s = s .. "=" .. self.default:format_right(state, operator_priority["_=_"], ...) + end + return s + end, + + traverse = function(self, fn, ...) + fn(self.identifier, ...) + if self.default then fn(self.default, ...) end + if self.type_check then fn(self.type_check, ...) end + end, + + _eval = function(self, state) + return FunctionParameter:new(self.identifier, self.default, self.type_check and self.type_check:eval(state)) + end +} + +return FunctionParameter diff --git a/ast/Identifier.lua b/ast/Identifier.lua new file mode 100644 index 0000000..22bd505 --- /dev/null +++ b/ast/Identifier.lua @@ -0,0 +1,44 @@ +local ast = require("ast") +local Symbol, String + +local Identifier +Identifier = ast.abstract.Node { + type = "identifier", + + name = nil, + + init = function(self, name) + self.name = name + end, + + _hash = function(self) + return ("identifier<%q>"):format(self.name) + end, + + _format = function(self) + return self.name + end, + + _eval = function(self, state) + return state.scope:get(self) + end, + + to_string = function(self) + return String:new(self.name) + end, + to_symbol = function(self, modifiers) + return Symbol:new(self.name, modifiers) + end, + + _prepare = function(self, state) + if state.scope:defined(self) then + state.scope:get(self):prepare(state) + end + end +} + +package.loaded[...] = Identifier + +Symbol, String = ast.Symbol, ast.String + +return Identifier diff --git a/ast/List.lua b/ast/List.lua new file mode 100644 index 0000000..a422cdd --- /dev/null +++ b/ast/List.lua @@ -0,0 +1,81 @@ +local ast = require("ast") +local Branched, Tuple + +local operator_priority = require("common").operator_priority + +local List +List = ast.abstract.Runtime { + type = "list", + + format_priority = operator_priority["*_"], + + -- note: yeah technically this isn't mutable, only .branched is + + -- note: this a Branched of Tuple, and we *will* forcefully mutate the tuples, so make sure to not disseminate any reference to them outside the List + -- unless you want rumors about mutable tuples to spread + branched = nil, + + init = function(self, state, from_tuple) + from_tuple = from_tuple or Tuple:new() + self.branched = Branched:new(state, from_tuple:copy()) + end, + + _format = function(self, ...) + return "*"..self.branched:format_right(...) + end, + + traverse = function(self, fn, ...) + fn(self.branched, ...) + end, + + -- List is always created from an evaluated Tuple, so no need to _eval here + + -- create copy of the list in branch if not here + -- do this before any mutation + -- return the tuple for the current branch + _prepare_branch = function(self, state) + if not self.branched:in_branch(state) then + self.branched:set(state, self.branched:get(state):copy()) + end + return self.branched:get(state) + end, + + len = function(self, state) + return #self.branched:get(state).list + end, + iter = function(self, state) + return ipairs(self.branched:get(state).list) + end, + get = function(self, state, index) + local list = self.branched:get(state) + if index < 0 then index = #list.list + 1 + index end + if index > #list.list or index == 0 then error("list index out of bounds") end + return list.list[index] + end, + set = function(self, state, index, val) + local list = self:_prepare_branch(state) + if index < 0 then index = #list.list + 1 + index end + if index > #list.list or index == 0 then error("list index out of bounds") end + list.list[index] = val + end, + insert = function(self, state, val) + local l = self:_prepare_branch(state) + table.insert(l.list, val) + end, + remove = function(self, state) + local l = self:_prepare_branch(state) + table.remove(l.list) + end, + + to_tuple = function(self, state) + return self.branched:get(state):copy() + end, + to_lua = function(self, state) + return self.branched:get(state):to_lua(state) + end, +} + +package.loaded[...] = List +Branched, Tuple = ast.Branched, ast.Tuple + +return List diff --git a/ast/LuaFunction.lua b/ast/LuaFunction.lua new file mode 100644 index 0000000..4392289 --- /dev/null +++ b/ast/LuaFunction.lua @@ -0,0 +1,63 @@ +local ast = require("ast") +local Overloadable = ast.abstract.Overloadable + +local operator_priority = require("common").operator_priority + +local LuaFunction +LuaFunction = ast.abstract.Runtime(Overloadable) { + type = "lua function", + + parameters = nil, -- ParameterTuple + func = nil, -- lua function + format_priority = operator_priority["$_"], + + init = function(self, parameters, func) + self.parameters = parameters + self.func = func + end, + + traverse = function(self, fn, ...) + fn(self.parameters, ...) + end, + + _format = function(self, ...) + if self.parameters.assignment then + return "$"..self.parameters:format(...).."; " + else + return "$"..self.parameters:format(...).." " + end + end, + + compatible_with_arguments = function(self, state, args) + return args:match_parameter_tuple(state, self.parameters) + end, + format_parameters = function(self, state) + return self.parameters:format(state) + end, + call_compatible = function(self, state, args) + local lua_args = { state } + + state.scope:push() + + args:bind_parameter_tuple(state, self.parameters) + for _, param in ipairs(self.parameters.list) do + table.insert(lua_args, state.scope:get(param.identifier)) + end + + state.scope:pop() + + local r = self.func(table.unpack(lua_args)) + assert(r, "lua function returned no value") + return r + end, + + _eval = function(self, state) + return LuaFunction:new(self.parameters:eval(state), self.func) + end, + + to_lua = function(self, state) + return self.func + end, +} + +return LuaFunction diff --git a/ast/Nil.lua b/ast/Nil.lua new file mode 100644 index 0000000..ef447d2 --- /dev/null +++ b/ast/Nil.lua @@ -0,0 +1,20 @@ +local ast = require("ast") + +return ast.abstract.Node { + type = "nil", + _evaluated = true, -- no evaluation needed + + init = function(self) end, + + _hash = function(self) + return "nil" + end, + + _format = function(self) + return "()" + end, + + to_lua = function(self, state) return nil end, + + truthy = function(self) return false end +} diff --git a/ast/Number.lua b/ast/Number.lua new file mode 100644 index 0000000..902f065 --- /dev/null +++ b/ast/Number.lua @@ -0,0 +1,25 @@ +local ast = require("ast") + +local Number +Number = ast.abstract.Node { + type = "number", + _evaluated = true, -- no evaluation needed + + number = nil, + + init = function(self, number) + self.number = number + end, + + _hash = function(self) + return ("number<%s>"):format(self.number) + end, + + _format = function(self) + return tostring(self.number) + end, + + to_lua = function(self, state) return self.number end, +} + +return Number diff --git a/ast/Overload.lua b/ast/Overload.lua new file mode 100644 index 0000000..1a9b2bf --- /dev/null +++ b/ast/Overload.lua @@ -0,0 +1,62 @@ +local ast = require("ast") + +local Overload +Overload = ast.abstract.Node { + type = "overload", + _evaluated = true, + + list = nil, + + init = function(self, ...) + self.list = { ... } + end, + insert = function(self, val) -- only for construction + table.insert(self.list, val) + end, + + _format = function(self, ...) + local s = "overload<" + for i, e in ipairs(self.list) do + s = s .. e:format(...) + if i < #self.list then s = s .. ", " end + end + return s..">" + end, + + traverse = function(self, fn, ...) + for _, e in ipairs(self.list) do + fn(e, ...) + end + end, + + call = function(self, state, args) + local failure = {} -- list of failure messages (kept until we find the first success) + local success, success_specificity, success_secondary_specificity = nil, -1, -1 + -- some might think that iterating a list for every function call is a terrible idea, but that list has a fixed number of elements, so big O notation says suck it up + for _, fn in ipairs(self.list) do + local specificity, secondary_specificity = fn:compatible_with_arguments(state, args) + if specificity then + if specificity > success_specificity then + success, success_specificity, success_secondary_specificity = fn, specificity, secondary_specificity + elseif specificity == success_specificity then + if secondary_specificity > success_secondary_specificity then + success, success_specificity, success_secondary_specificity = fn, specificity, secondary_specificity + elseif secondary_specificity == success_secondary_specificity then + error(("more than one function match %s, matching functions were at least (specificity %s.%s):\n\t• %s\n\t• %s"):format(args:format(state), specificity, secondary_specificity, fn:format_parameters(state), success:format_parameters(state)), 0) + end + end + -- no need to add error message for less specific function since we already should have at least one success + elseif not success then + table.insert(failure, fn:format_parameters(state) .. ": " .. secondary_specificity) + end + end + if success then + return success:call_compatible(state, args) + else + -- error + error(("no function match %s, possible functions were:\n\t• %s"):format(args:format(state), table.concat(failure, "\n\t• ")), 0) + end + end +} + +return Overload diff --git a/ast/Pair.lua b/ast/Pair.lua new file mode 100644 index 0000000..ed8e1ce --- /dev/null +++ b/ast/Pair.lua @@ -0,0 +1,25 @@ +local ast = require("ast") + +local operator_priority = require("common").operator_priority + +return ast.abstract.Runtime { + type = "pair", + + name = nil, + value = nil, + format_priority = operator_priority["_:_"], + + init = function(self, name, value) + self.name = name + self.value = value + end, + + traverse = function(self, fn, ...) + fn(self.name, ...) + fn(self.value, ...) + end, + + _format = function(self, ...) + return ("%s:%s"):format(self.name:format(...), self.value:format(...)) + end, +} diff --git a/ast/ParameterTuple.lua b/ast/ParameterTuple.lua new file mode 100644 index 0000000..be6a749 --- /dev/null +++ b/ast/ParameterTuple.lua @@ -0,0 +1,67 @@ +local ast = require("ast") + +local operator_priority = require("common").operator_priority + +local ParameterTuple +ParameterTuple = ast.abstract.Node { + type = "parameter tuple", + + assignment = false, + list = nil, + min_arity = 0, + max_arity = 0, + + eval_depth = 0, -- scope deth where this parametertuple was evaluated, used as secondary specificity + + init = function(self, ...) + self.list = {...} + end, + insert = function(self, val) -- only for construction + assert(not self.assignment, "can't add new parameters after assignment parameter was added") + table.insert(self.list, val) + self.max_arity = self.max_arity + 1 + if not val.default then + self.min_arity = self.min_arity + 1 + end + end, + insert_assignment = function(self, val) -- only for construction + self:insert(val) + self.assignment = true + self.format_priority = operator_priority["_=_"] + end, + + _format = function(self, state, prio, ...) + local l = {} + for i, e in ipairs(self.list) do + if i < self.max_arity or not self.assignment then + table.insert(l, e:format(state, operator_priority["_,_"], ...)) + end + end + local s = ("(%s)"):format(table.concat(l, ", ")) + if self.assignment then + s = s .. (" = %s"):format(self.list[#self.list]:format_right(state, operator_priority["_=_"], ...)) + end + return s + end, + + traverse = function(self, fn, ...) + for _, e in ipairs(self.list) do + fn(e, ...) + end + end, + + _eval = function(self, state) + local r = ParameterTuple:new() + for i, param in ipairs(self.list) do + if i < self.max_arity or not self.assignment then + r:insert(param:eval(state)) + else + r:insert_assignment(param:eval(state)) + end + end + r.eval_depth = state.scope:depth() + return r + end +} + +return ParameterTuple diff --git a/ast/Quote.lua b/ast/Quote.lua new file mode 100644 index 0000000..f4ca356 --- /dev/null +++ b/ast/Quote.lua @@ -0,0 +1,34 @@ +-- prevent an expression from being immediately evaluated, and instead only evaluate it when the node is explicitely called +-- it can be used to evaluate the expression on demand, as if the quote call AST was simply replaced by the unevaluated associated expression AST (like a macro) +-- keep in mind that this thus bypass any scoping rule, closure, etc. +-- +-- used for infix operators where the evaluation of the right term depends of the left one (lazy boolean operators, conditionals, etc.) + +local ast = require("ast") + +local Quote +Quote = ast.abstract.Node { + type = "quote", + + expression = nil, + + init = function(self, expression) + self.expression = expression + self.format_priority = expression.format_priority + end, + + _format = function(self, ...) + return self.expression:format(...) -- Quote is generated transparently by operators + end, + + traverse = function(self, fn, ...) + fn(self.expression, ...) + end, + + call = function(self, state, args) + assert(args.arity == 0, "Quote! does not accept arguments") + return self.expression:eval(state) + end +} + +return Quote diff --git a/ast/Resumable.lua b/ast/Resumable.lua new file mode 100644 index 0000000..b9235a9 --- /dev/null +++ b/ast/Resumable.lua @@ -0,0 +1,60 @@ +local ast = require("ast") +local Table + +local resumable_manager + +local Resumable +Resumable = ast.abstract.Runtime { + type = "resumable", + + resuming = false, + + expression = nil, + scope = nil, + data = nil, + + init = function(self, state, expression, scope, data) + self.expression = expression + self.scope = scope + self.data = data or Table:new(state) + end, + + _format = function(self) + return "" + end, + + traverse = function(self, fn, ...) + fn(self.expression, ...) + fn(self.data, ...) + fn(self.scope, ...) + end, + + -- returns a copy with the data copied + capture = function(self, state) + return Resumable:new(state, self.expression, self.scope, self.data:copy(state)) + end, + + -- resume from this resumable + call = function(self, state, args) + assert(args.arity == 0, "Resumable! does not accept arguments") + + state.scope:push(self.scope) + + local resuming = self:capture(state) + resuming.resuming = true + resumable_manager:push(state, resuming) + local r = self.expression:eval(state) + resumable_manager:pop(state) + + state.scope:pop() + + return r + end, +} + +package.loaded[...] = Resumable +Table = ast.Table + +resumable_manager = require("state.resumable_manager") + +return Resumable diff --git a/ast/ResumeParentFunction.lua b/ast/ResumeParentFunction.lua new file mode 100644 index 0000000..1ec5fb9 --- /dev/null +++ b/ast/ResumeParentFunction.lua @@ -0,0 +1,44 @@ +-- intended to be wrapped in a Function, so that when resuming from the function, will keep resuming to where the function was called from +-- used in Choices to resume back from where the event was flushed +-- note: when resuming, the return value will be discarded, instead returning what the parent function will return + +local ast = require("ast") +local ArgumentTuple + +local resumable_manager + +local ResumeParentFunction = ast.abstract.Node { + type = "resume parent function", + + expression = nil, + + init = function(self, expression) + self.expression = expression + self.format_priority = expression.format_priority + end, + + _format = function(self, ...) + return self.expression:format(...) + end, + + traverse = function(self, fn, ...) + fn(self.expression, ...) + end, + + _eval = function(self, state) + if resumable_manager:resuming(state, self) then + self.expression:eval(state) + return resumable_manager:get_data(state, self):call(state, ArgumentTuple:new()) + else + resumable_manager:set_data(state, self, resumable_manager:capture(state, 1)) + return self.expression:eval(state) + end + end +} + +package.loaded[...] = ResumeParentFunction +ArgumentTuple = ast.ArgumentTuple + +resumable_manager = require("state.resumable_manager") + +return ResumeParentFunction diff --git a/ast/Return.lua b/ast/Return.lua new file mode 100644 index 0000000..b3a29af --- /dev/null +++ b/ast/Return.lua @@ -0,0 +1,33 @@ +local ast = require("ast") + +local operator_priority = require("common").operator_priority + +local Return +Return = ast.abstract.Node { + type = "return", + + expression = nil, + format_priority = operator_priority["@_"], + + init = function(self, expression) + self.expression = expression + end, + + _format = function(self, ...) + return ("@%s"):format(self.expression:format_right(...)) + end, + + traverse = function(self, fn, ...) + fn(self.expression, ...) + end, + + _eval = function(self, state) + return Return:new(self.expression:eval(state)) + end, + + to_lua = function(self, state) + return self.expression:to_lua(state) + end +} + +return Return diff --git a/ast/ReturnBoundary.lua b/ast/ReturnBoundary.lua new file mode 100644 index 0000000..0c338ca --- /dev/null +++ b/ast/ReturnBoundary.lua @@ -0,0 +1,37 @@ +-- used stop propagating Return when leaving functions + +local ast = require("ast") +local Return + +local ReturnBoundary = ast.abstract.Node { + type = "return boundary", + + expression = nil, + + init = function(self, expression) + self.expression = expression + self.format_priority = self.expression.format_priority + end, + + _format = function(self, ...) + return self.expression:format(...) + end, + + traverse = function(self, fn, ...) + fn(self.expression, ...) + end, + + _eval = function(self, state) + local exp = self.expression:eval(state) + if Return:is(exp) then + return exp.expression + else + return exp + end + end +} + +package.loaded[...] = ReturnBoundary +Return = ast.Return + +return ReturnBoundary diff --git a/ast/String.lua b/ast/String.lua new file mode 100644 index 0000000..db1287f --- /dev/null +++ b/ast/String.lua @@ -0,0 +1,34 @@ +local ast = require("ast") +local Identifier + +local String = ast.abstract.Node { + type = "string", + _evaluated = true, -- no evaluation needed + + string = nil, + + init = function(self, str) + self.string = str + end, + + _hash = function(self) + return ("string<%q>"):format(self.string) + end, + + _format = function(self) + return ("%q"):format(self.string) + end, + + to_lua = function(self, state) + return self.string + end, + + to_identifier = function(self) + return Identifier:new(self.string) + end +} + +package.loaded[...] = String +Identifier = ast.Identifier + +return String diff --git a/ast/StringInterpolation.lua b/ast/StringInterpolation.lua new file mode 100644 index 0000000..fb5c20f --- /dev/null +++ b/ast/StringInterpolation.lua @@ -0,0 +1,53 @@ +local ast = require("ast") +local String + +local StringInterpolation = ast.abstract.Node { + type = "string interpolation", + + list = nil, + + init = function(self, ...) + self.list = {...} + end, + insert = function(self, val) -- only for construction + table.insert(self.list, val) + end, + + traverse = function(self, fn, ...) + for _, e in ipairs(self.list) do + fn(e, ...) + end + end, + + _format = function(self, ...) + local l = {} + for _, e in ipairs(self.list) do + if String:is(e) then + local t = e.string:gsub("\\", "\\\\"):gsub("\n", "\\n"):gsub("\t", "\\t"):gsub("\"", "\\\"") + table.insert(l, t) + else + table.insert(l, ("{%s}"):format(e:format(...))) + end + end + return ("\"%s\""):format(table.concat(l)) + end, + + _eval = function(self, state) + local t = {} + for _, e in ipairs(self.list) do + local r = e:eval(state) + if String:is(r) then + r = r.string + else + r = r:format(state) + end + table.insert(t, r) + end + return String:new(table.concat(t)) + end +} + +package.loaded[...] = StringInterpolation +String = ast.String + +return StringInterpolation diff --git a/ast/Struct.lua b/ast/Struct.lua new file mode 100644 index 0000000..3083497 --- /dev/null +++ b/ast/Struct.lua @@ -0,0 +1,121 @@ +local ast = require("ast") +local Pair, Number, Nil + +local operator_priority = require("common").operator_priority + +local Struct + +local TupleToStruct = ast.abstract.Node { + type = "tuple to struct", + + tuple = nil, + + init = function(self, tuple) + self.tuple = tuple + end, + traverse = function(self, fn, ...) + fn(self.tuple, ...) + end, + + _format = function(self, ...) + return self.tuple:format(...):gsub("^%[", "{"):gsub("%]$", "}") + end, + + _eval = function(self, state) + local t = Struct:new() + for i, e in ipairs(self.tuple.list) do + if Pair:is(e) then + t:set(e.name, e.value) + else + t:set(Number:new(i), e) + end + end + return t + end +} + +Struct = ast.abstract.Runtime { + type = "struct", + + table = nil, + + init = function(self) + self.table = {} + end, + set = function(self, key, value) -- only for construction + self.table[key:hash()] = { key, value } + end, + include = function(self, other) -- only for construction + for _, e in pairs(other.table) do + self:set(e[1], e[2]) + end + end, + + copy = function(self) + local s = Struct:new() + for _, e in pairs(self.table) do + s:set(e[1], e[2]) + end + return s + end, + + -- build from (non-evaluated) tuple + -- results needs to be evaluated + from_tuple = function(self, tuple) + return TupleToStruct:new(tuple) + end, + + _format = function(self, state, prio, ...) + local l = {} + for _, e in pairs(self.table) do + -- _:_ has higher priority than _,_ + table.insert(l, e[1]:format(state, operator_priority["_:_"], ...)..":"..e[2]:format_right(state, operator_priority["_:_"], ...)) + end + return ("{%s}"):format(table.concat(l, ", ")) + end, + + traverse = function(self, fn, ...) + for _, e in pairs(self.table) do + fn(e[1], ...) + fn(e[2], ...) + end + end, + + -- need to redefine hash to include a table.sort as pairs() in :traverse is non-deterministic + _hash = function(self) + local t = {} + for _, e in pairs(self.table) do + table.insert(t, ("%s;%s"):format(e[1]:hash(), e[2]:hash())) + end + table.sort(t) + return ("%s<%s>"):format(self.type, table.concat(t, ";")) + end, + + -- regarding eval: Struct is built from TupleToStruct function call which already eval, so every Struct should be fully evaluated + + to_lua = function(self, state) + local l = {} + for _, e in ipairs(self.table) do + l[e[1]:to_lua(state)] = e[2]:to_lua(state) + end + return l + end, + + get = function(self, key) + local hash = key:hash() + if self.table[hash] then + return self.table[hash][2] + else + return Nil:new() + end + end, + has = function(self, key) + local hash = key:hash() + return not not self.table[hash] + end +} + +package.loaded[...] = Struct +Pair, Number, Nil = ast.Pair, ast.Number, ast.Nil + +return Struct diff --git a/ast/Symbol.lua b/ast/Symbol.lua new file mode 100644 index 0000000..a64d20a --- /dev/null +++ b/ast/Symbol.lua @@ -0,0 +1,78 @@ +local ast = require("ast") +local Identifier, String + +local operator_priority = require("common").operator_priority + +local Symbol +Symbol = ast.abstract.Node { + type = "symbol", + + string = nil, + constant = nil, -- bool + type_check = nil, -- exp + + exported = nil, -- bool + persistent = nil, -- bool, imply exported + + confined_to_branch = nil, -- bool + + init = function(self, str, modifiers) + modifiers = modifiers or {} + self.string = str + self.constant = modifiers.constant + self.persistent = modifiers.persistent + self.type_check = modifiers.type_check + self.confined_to_branch = modifiers.confined_to_branch + self.exported = modifiers.exported or modifiers.persistent + if self.type_check then + self.format_priority = operator_priority["_::_"] + end + end, + + _eval = function(self, state) + return Symbol:new(self.string, { + constant = self.constant, + persistent = self.persistent, + type_check = self.type_check and self.type_check:eval(state), + confined_to_branch = self.confined_to_branch, + exported = self.exported + }) + end, + + _hash = function(self) + return ("symbol<%q>"):format(self.string) + end, + + _format = function(self, state, prio, ...) + local s = ":" + if self.constant then + s = s .. ":" + end + if self.persistent then + s = s .. "&" + end + if self.exported then + s = s .. "@" + end + s = s .. self.string + if self.type_check then + s = s .. "::" .. self.type_check:format_right(state, operator_priority["_::_"], ...) + end + return s + end, + + to_lua = function(self, state) + return self.string + end, + to_identifier = function(self) + return Identifier:new(self.string) + end, + to_string = function(self) + return String:new(self.string) + end +} + +package.loaded[...] = Symbol +Identifier, String = ast.Identifier, ast.String + +return Symbol diff --git a/ast/Table.lua b/ast/Table.lua new file mode 100644 index 0000000..99ded8e --- /dev/null +++ b/ast/Table.lua @@ -0,0 +1,85 @@ +local ast = require("ast") +local Branched, Struct, Nil = ast.Branched, ast.Struct, ast.Nil + +local operator_priority = require("common").operator_priority + +local Table +Table = ast.abstract.Runtime { + type = "table", + + format_priority = operator_priority["*_"], + + -- note: technically this isn't mutable, only .branched is + + -- note: this a Branched of Struct, and we *will* forcefully mutate the tuples, so make sure to not disseminate any reference to them outside the Table + -- unless you want rumors about mutable structs to spread + branched = nil, + + init = function(self, state, from_struct) + from_struct = from_struct or Struct:new() + self.branched = Branched:new(state, from_struct:copy()) + end, + + _format = function(self, ...) + return "*"..self.branched:format_right(...) + end, + + traverse = function(self, fn, ...) + fn(self.branched, ...) + end, + + -- Table is always created from an evaluated Struct, so no need to _eval here + + -- create copy of the table in branch if not here + -- do this before any mutation + -- return the struct for the current branch + _prepare_branch = function(self, state) + if not self.branched:in_branch(state) then + self.branched:set(state, self.branched:get(state):copy()) + end + return self.branched:get(state) + end, + + get = function(self, state, key) + local s = self.branched:get(state) + return s:get(key) + end, + set = function(self, state, key, val) + local s = self:_prepare_branch(state) + local hash = key:hash() + if Nil:is(val) then + s.table[hash] = nil + else + s.table[hash] = { key, val } + end + end, + has = function(self, state, key) + local s = self.branched:get(state) + return s:has(key) + end, + iter = function(self, state) + local t, h = self.branched:get(state).table, nil + return function() + local e + h, e = next(t, h) + if h == nil then return nil + else return e[1], e[2] + end + end + end, + + to_struct = function(self, state) + return self.branched:get(state):copy() + end, + to_lua = function(self, state) + return self.branched:get(state):to_lua(state) + end, + copy = function(self, state) + return Table:new(state, self:to_struct(state)) + end +} + +package.loaded[...] = Table +Branched, Struct, Nil = ast.Branched, ast.Struct, ast.Nil + +return Table diff --git a/ast/Text.lua b/ast/Text.lua new file mode 100644 index 0000000..c29df8c --- /dev/null +++ b/ast/Text.lua @@ -0,0 +1,36 @@ +local ast = require("ast") +local AutoCall, Event, Runtime = ast.abstract.AutoCall, ast.abstract.Event, ast.abstract.Runtime + +return Runtime(AutoCall, Event) { + type = "text", + + list = nil, -- { { String, tag Table }, ... } + + init = function(self) + self.list = {} + end, + insert = function(self, str, tags) -- only for construction + table.insert(self.list, { str, tags }) + end, + + traverse = function(self, fn, ...) + for _, e in ipairs(self.list) do + fn(e[1], ...) + fn(e[2], ...) + end + end, + + _format = function(self, ...) + local t = {} + for _, e in ipairs(self.list) do + table.insert(t, ("%s%s"):format(e[2]:format(...), e[1]:format(...))) + end + return ("| %s |"):format(table.concat(t, " ")) + end, + + -- Text comes from TextInterpolation which already evals the contents + + to_event_data = function(self) + return self + end +} diff --git a/ast/TextInterpolation.lua b/ast/TextInterpolation.lua new file mode 100644 index 0000000..a3fe062 --- /dev/null +++ b/ast/TextInterpolation.lua @@ -0,0 +1,59 @@ +local ast = require("ast") +local Text, String + +local tag_manager = require("state.tag_manager") + +local TextInterpolation = ast.abstract.Node { + type = "text interpolation", + + list = nil, + + init = function(self, ...) + self.list = {...} + end, + insert = function(self, val) -- only for construction + table.insert(self.list, val) + end, + + traverse = function(self, fn, ...) + for _, e in ipairs(self.list) do + fn(e, ...) + end + end, + + _format = function(self, ...) + local l = {} + for _, e in ipairs(self.list) do + if String:is(e) then + local t = e.string:gsub("\\", "\\\\"):gsub("\n", "\\n"):gsub("\t", "\\t"):gsub("\"", "\\\"") + table.insert(l, t) + else + table.insert(l, ("{%s}"):format(e:format(...))) + end + end + return ("| %s|"):format(table.concat(l)) + end, + + _eval = function(self, state) + local t = Text:new() + local tags = tag_manager:get(state) + for _, e in ipairs(self.list) do + local r = e:eval(state) + if String:is(r) then + t:insert(r, tags) + elseif Text:is(r) then + for _, v in ipairs(r.list) do + t:insert(v[1], v[2]) + end + else + t:insert(String:new(r:format(state)), tags) + end + end + return t + end, +} + +package.loaded[...] = TextInterpolation +Text, String = ast.Text, ast.String + +return TextInterpolation diff --git a/ast/Tuple.lua b/ast/Tuple.lua new file mode 100644 index 0000000..da43753 --- /dev/null +++ b/ast/Tuple.lua @@ -0,0 +1,66 @@ +local ast = require("ast") + +local operator_priority = require("common").operator_priority + +local Tuple +Tuple = ast.abstract.Node { + type = "tuple", + explicit = true, -- false for implicitely created tuples, e.g. 1,2,3 without the brackets [] + + list = nil, + + init = function(self, ...) + self.list = { ... } + end, + insert = function(self, val) -- only for construction + table.insert(self.list, val) + end, + + _format = function(self, state, prio, ...) + local l = {} + for _, e in ipairs(self.list) do + table.insert(l, e:format(state, operator_priority["_,_"], ...)) + end + return ("[%s]"):format(table.concat(l, ", ")) + end, + + traverse = function(self, fn, ...) + for _, e in ipairs(self.list) do + fn(e, ...) + end + end, + + _eval = function(self, state) + local t = Tuple:new() + for _, e in ipairs(self.list) do + t:insert(e:eval(state)) + end + if not self.explicit then + t.explicit = false + end + return t + end, + copy = function(self) + local t = Tuple:new() + for _, e in ipairs(self.list) do + t:insert(e) + end + return t + end, + + to_lua = function(self, state) + local l = {} + for _, e in ipairs(self.list) do + table.insert(l, e:to_lua(state)) + end + return l + end, + + get = function(self, index) + if index < 0 then index = #self.list + 1 + index end + if index > #self.list or index == 0 then error("tuple index out of bounds") end + return self.list[index] + end +} + +return Tuple diff --git a/ast/Typed.lua b/ast/Typed.lua new file mode 100644 index 0000000..d6af880 --- /dev/null +++ b/ast/Typed.lua @@ -0,0 +1,24 @@ +local ast = require("ast") + +local operator_priority = require("common").operator_priority + +return ast.abstract.Runtime { + type = "typed", + + expression = nil, + type_expression = nil, + + init = function(self, type, expression) + self.type_expression = type + self.expression = expression + end, + + _format = function(self, state, prio, ...) + return ("type(%s, %s)"):format(self.type_expression:format(state, operator_priority["_,_"], ...), self.expression:format_right(state, operator_priority["_,_"], ...)) + end, + + traverse = function(self, fn, ...) + fn(self.type_expression, ...) + fn(self.expression, ...) + end +} diff --git a/ast/abstract/AutoCall.lua b/ast/abstract/AutoCall.lua new file mode 100644 index 0000000..a9c824a --- /dev/null +++ b/ast/abstract/AutoCall.lua @@ -0,0 +1,8 @@ +-- called automatically when returned by one of the expression in a block + +local ast = require("ast") + +return ast.abstract.Node { + type = "auto call", + init = false +} diff --git a/ast/abstract/Event.lua b/ast/abstract/Event.lua new file mode 100644 index 0000000..fe2778c --- /dev/null +++ b/ast/abstract/Event.lua @@ -0,0 +1,22 @@ +-- for nodes that can be written to the event buffer + +local ast = require("ast") + +return ast.abstract.Node { + type = "event", + init = false, + + -- returns value that will be yielded by the whole event buffer data on flush + -- by default a list of what is returned by :to_event_data for each event of the buffer + build_event_data = function(self, state, event_buffer) + local l = {} + for _, event in event_buffer:iter(state) do + table.insert(l, event:to_event_data(state)) + end + return l + end, + to_event_data = function(self, state) error("unimplemented") end, + + -- post_flush_callback(self, state, event_buffer, event_data) + post_flush_callback = false +} diff --git a/ast/abstract/Node.lua b/ast/abstract/Node.lua new file mode 100644 index 0000000..d68043a --- /dev/null +++ b/ast/abstract/Node.lua @@ -0,0 +1,282 @@ +local class = require("class") +local fmt = require("common").fmt +local binser = require("lib.binser") + +-- NODES SHOULD BE IMMUTABLE AFTER CREATION IF POSSIBLE! +-- i don't think i actually rely on this behavior for anything but it makes me feel better about life in general +-- (well, unless node.mutable == true, in which case go ahead and break my little heart) +-- UPDATE: i actually assumed nodes to be immutable by default in a lot of places now, thank you past me, it did indeed make me feel better about life in general + +-- reminder: when requiring AST nodes somewhere, try to do it at the end of the file. and if you need to require something in this file, do it in the :_i_hate_cycles method. +-- i've had enough headaches with cyclics references and nodes required several times... + +local uuid = require("common").uuid + +local State, Runtime +local resumable_manager + +local custom_call_identifier + +local context_max_length = 50 +local function cutoff_text(str) + if str:match("\n") or utf8.len(str) > context_max_length then + local cut_pos = math.min((str:match("()\n") or math.huge)-1, (utf8.offset(str, context_max_length, 1) or math.huge)-1) + str = str:sub(1, cut_pos) .. "…" + end + return str +end +local function format_error(state, node, message) + local ctx = cutoff_text(node:format(state)) -- get some context code around error + return fmt("%{red}%s%{reset}\n\t↳ from %{underline}%s%{reset} in %s: %{dim}%s", message, node.source, node.type, ctx) +end + +-- traverse helpers +local traverse +traverse = { + set_source = function(self, source) + self:set_source(source) + end, + prepare = function(self, state) + self:prepare(state) + end, + merge = function(self, state, cache) + self:merge(state, cache) + end, + hash = function(self, t) + table.insert(t, self:hash()) + end +} + +local Node +Node = class { + type = "node", + source = "?", + mutable = false, + + -- abstract class + -- must be redefined + init = false, + + -- set the source of this node and its children (unless a source is already set) + -- to be preferably used during construction only + set_source = function(self, source) + local str_source = tostring(source) + if self.source == "?" then + self.source = str_source + self:traverse(traverse.set_source, str_source) + end + return self + end, + + -- call function callback with args ... on the children Nodes of this Node + -- by default, assumes no children Nodes + -- you will want to redefine this for nodes with children nodes + -- (note: when calling, remember that cycles are common place in the AST, so stay safe use a cache) + traverse = function(self, callback, ...) end, + + -- returns new AST + -- whatever this function returned is assumed to be already evaluated + -- the actual evaluation is done in _eval + eval = function(self, state) + if self._evaluated then return self end + local s, r = pcall(self._eval, self, state) + if s then + r._evaluated = true + return r + else + error(format_error(state, self, r), 0) + end + end, + _evaluated = false, -- if true, node is assumed to be already evaluated and :eval will be the identity function + -- evaluate this node and return the result + -- by default assume the node can't be evaluated further and return itself; redefine for everything else, probably + -- THIS SHOULD NOT MUTATE THE CURRENT NODE; create and return a new Node instead! (even if node is mutable) + _eval = function(self, state) + return self + end, + + -- prepare the AST after parsing and before evaluation + -- this behave like a cached :traverse through the AST, except this keeps track of the scope stack + -- i.e. when :prepare is called on a node, it should be in a similar scope stack context as will be when it will be evaluated + -- used to predefine exported variables and other compile-time variable handling + -- note: the state here is a temporary state only used during the prepare step + -- the actual preparation is done in _prepare + -- (this can mutate the node as needed and is automatically called after each parse) + prepare = function(self, state) + assert(not Runtime:issub(self), ("can't prepare a %s node that should only exist at runtime"):format(self.type)) + state = state or State:new() + if self._prepared then return end + local s, r = pcall(self._prepare, self, state) + if s then + self._prepared = true + else + error(format_error(state, self, r), 0) + end + end, + _prepared = false, -- indicate that the node was prepared and :prepare should nop + -- prepare this node. can mutate the node (considered to be part of construction). + _prepare = function(self, state) + self:traverse(traverse.prepare, state) + end, + + -- same as eval, but make the evaluated expression as a resume boundary + -- i.e. if a checkpoint is defined somewhere in this eval, it will start back from this node eval when resuming + eval_resumable = function(self, state) + return resumable_manager:eval(state, self) + end, + -- set the current resume data for this node + -- (relevant inside :eval) + set_resume_data = function(self, state, data) + resumable_manager:set_data(state, self, data) + end, + -- get the current resume data for this node + get_resume_data = function(self, state) + return resumable_manager:get_data(state, self) + end, + -- returns true if the current node is in a resuming state + -- (relevant inside :eval) + resuming = function(self, state) + return resumable_manager:resuming(state, self) + end, + + -- return result AST + -- arg is a ArgumentTuple node (already evaluated) + -- redefine if relevant + call = function(self, state, arg) + if state.scope:defined(custom_call_identifier) then + local custom_call = custom_call_identifier:eval(state) + return custom_call:call(state, arg:with_first_argument(self)) + else + error("trying to call a "..self.type..": "..self:format(state)) + end + end, + + -- merge any changes back into the main branch + -- cache is a table indicating nodes when the merge has already been triggered { [node] = true, ... } + -- (just give an empty table on the initial call) + -- redefine :_merge if needed, not this + merge = function(self, state, cache) + if not cache[self] then + cache[self] = true + self:_merge(state, cache) + self:traverse(traverse.merge, state, cache) + end + end, + _merge = function(self, state, cache) end, + + -- return string that uniquely represent this node + -- the actual hash is computed in :_hash, don't redefine :hash directly + -- note: if the node is mutable, this will return a UUID instead of calling :_hash + hash = function(self) + if not self._hash_cache then + if self.mutable then + self._hash_cache = uuid() + else + self._hash_cache = self:_hash() + end + end + return self._hash_cache + end, + _hash_cache = nil, -- cached hash + -- return string that uniquely represent this node + -- by default, build a "node type" representation using :traverse + -- you may want to redefine this for base types and other nodes with discriminating info that's not in children nodes. + -- also beware if :traverse uses pairs() or any other non-deterministic function, it'd be nice if this was properly bijective... + -- (no need to redefine for mutable nodes, since an uuid is used instead) + _hash = function(self) + local t = {} + self:traverse(traverse.hash, t) + return ("%s<%s>"):format(self.type, table.concat(t, ";")) + end, + + -- return a pretty string representation of the node. + -- for non-runtime nodes (what was generated by a parse without any evaluation), this should return valid Anselme code that is functionnally equivalent to the parsed code. note that it currently does not preserve comment. + -- redefine _format, not this - note that _format is a mandary method for all nodes. + -- state is optional and should only be relevant for runtime nodes; if specified, only show what is relevant for the current branch. + -- indentation_level and parent_priority are optional value that respectively keep track in nester :format calls of the indentation level (number) and parent operator priority (number); if the node has a strictly lower priority than the parent node, parentheses will be added + -- also remember that execution is done left-to-right, so in case of priority equality, all is fine if the term appear left of the operator, but parentheses will need to be added if the term is right of the operator - so make sure to call :format_right for such cases + -- (:format is not cached as even immutable nodes may contain mutable children) + format = function(self, state, parent_priority, indentation_level) + indentation_level = indentation_level or 0 + parent_priority = parent_priority or 0 + + local s = self:_format(state, self.format_priority, indentation_level) + + if self.format_priority < parent_priority then + s = ("(%s)"):format(s) + end + + local indentation = ("\t"):rep(indentation_level) + s = s:gsub("\n", "\n"..indentation) + + return s + end, + -- same as :format, but should be called only for nodes right of the current operator + format_right = function(self, state, parent_priority, indentation_level) + indentation_level = indentation_level or 0 + parent_priority = parent_priority or 0 + + local s = self:_format(state, self.format_priority, indentation_level) + + if self.format_priority <= parent_priority then + s = ("(%s)"):format(s) + end + + local indentation = (" "):rep(indentation_level) + s = indentation..s:gsub("\n", "\n"..indentation) + + return s + end, + -- redefine this to provide a custom :format. returns a string. + _format = function(self, state, self_priority, identation) + error("format not implemented for "..self.type) + end, + -- priority of the node that will be used in :format to add eventually needed parentheses. + -- should not be modified after object construction! + format_priority = math.huge, -- by default, assumes primary node, i.e. never wrap in parentheses + + -- return Lua value + -- this should probably be only called on a Node that is already evaluated + -- redefine if you want, probably only for nodes that are already evaluated + to_lua = function(self, state) + error("cannot convert "..self.type.." to a Lua value") + end, + + -- returns truthiness of node + -- redefine for false stuff + truthy = function(self) + return true + end, + + -- register the node for serialization on creation + __created = function(self) + if self.init then -- only call on non-abstract node + binser.register(self, self.type) + end + end, + + __tostring = function(self) return self:format() end, + + -- Node is required by every other AST node, some of which exist in cyclic require loops. + -- Delaying the requires in each node after it is defined is enough to fix it, but not for abstract Nodes, since because we are subclassing each node from + -- them, we need them to be available BEFORE the Node is defined. But Node require several other modules, which themselves require some other AST... + -- The worst thing with this kind of require loop combined with our existing cycle band-aids is that Lua won't error, it will just execute the first node to subclass from Node twice. Which is annoying since now we have several, technically distinct classes representing the same node frolicking around. + -- Thus, any require here that may require other Nodes shall be done here. This method is called in anselme.lua after everything else is required. + _i_hate_cycles = function(self) + local ast = require("ast") + custom_call_identifier = ast.Identifier:new("_!") + Runtime = ast.abstract.Runtime + + State = require("state.State") + resumable_manager = require("state.resumable_manager") + end, + + _debug_traverse = function(self, level) + level = level or 0 + local t = {} + self:traverse(function(v) table.insert(t, v:_debug_ast(level+1)) end) + return ("%s%s:\n%s"):format((" "):rep(level), self.type, table.concat(t, "\n")) + end, +} + +return Node diff --git a/ast/abstract/Overloadable.lua b/ast/abstract/Overloadable.lua new file mode 100644 index 0000000..7b19d08 --- /dev/null +++ b/ast/abstract/Overloadable.lua @@ -0,0 +1,27 @@ +local ast = require("ast") + +return ast.abstract.Node { + type = "overloadable", + init = false, + + -- return specificity (number>=0), secondary specificity (number >=0) + -- return false, failure message (string) + compatible_with_arguments = function(self, state, args) + error("not implemented for "..self.type) + end, + -- same as :call, but assumes :compatible_with_arguments was checked before the call + call_compatible = function(self, state, args) + error("not implemented for "..self.type) + end, + + -- return string + format_parameters = function(self, state) + return self:format(state) + end, + + -- default for :call + call = function(self, state, args) + assert(self:compatible_with_arguments(state, args)) + return self:call_compatible(state, args) + end +} diff --git a/ast/abstract/Runtime.lua b/ast/abstract/Runtime.lua new file mode 100644 index 0000000..a4823cd --- /dev/null +++ b/ast/abstract/Runtime.lua @@ -0,0 +1,12 @@ +-- indicate a Runtime node: it should not exist in the AST generated by the parser but only as a result of an evaluation or call +-- is assumed to be already evaluated and prepared (will actually error on prepare) + +local ast = require("ast") + +return ast.abstract.Node { + type = "runtime", + init = false, + + _evaluated = true, + _prepared = true +} diff --git a/ast/init.lua b/ast/init.lua new file mode 100644 index 0000000..cb07dab --- /dev/null +++ b/ast/init.lua @@ -0,0 +1,13 @@ +return setmetatable({ + abstract = setmetatable({}, { + __index = function(self, key) + self[key] = require("ast.abstract."..key) + return self[key] + end + }) +}, { + __index = function(self, key) + self[key] = require("ast."..key) + return self[key] + end +}) diff --git a/class.lua b/class.lua new file mode 100644 index 0000000..e6aa342 --- /dev/null +++ b/class.lua @@ -0,0 +1,169 @@ +--- classtoi v2: finding a sweet spot between classtoi-light and classtoi-heavy +-- aka getlost v2 +-- +-- usage: +-- +-- local class = require("class") +-- local Vehicle = class { +-- type = "vehicle", -- class name, optional +-- +-- stability_threshold = 3, -- class variable, also availabe in instances +-- wheel_count = nil, -- doesn't do anything, but i like to keep track of variables that will need to be defined later in a subclass or a constructor +-- +-- init = false, -- abstract class, can't be instanciated +-- +-- is_stable = function(self) -- method, available both in class and instances +-- return self.wheel_count > self.stability_threshold +-- end +-- } +-- +-- local Car = Vehicle { -- subclassing by calling the parent class; multiple inheritance possible by either chaining calls or passing several tables as arguments +-- type = "car", +-- wheel_count = 4, +-- color = nil, +-- init = function(self, color) -- constructor +-- self.color = color +-- end +-- } +-- local car = Car:new("red") -- instancing +-- print(car:is_stable(), car.color) -- true, "red" +-- +-- the default class returned by require("class") contains a few other default methods that will be inherited by all subclasses +-- see line 99 and further for details & documentation +-- +-- design philosophy: +-- do not add feature until we need it +-- what we want to be fast: instance creation, class & instance method call & property acces +-- do not care: class creation +-- +-- and if you're wondering, no i'm not using either classtoi-heavy nor classtoi-light in any current project anymore. + +--# helper functions #-- + +-- tostring that ignore __tostring methamethod +local function rawtostring(v) + local mt = getmetatable(v) + setmetatable(v, nil) + local str = tostring(v) + setmetatable(v, mt) + return str +end + +-- deep table copy, preserve metatable +local function copy(t, cache) + if cache == nil then cache = {} end + if cache[t] then return cache[t] end + local r = {} + cache[t] = r + for k, v in pairs(t) do + r[k] = type(v) == "table" and copy(v, cache) or v + end + return setmetatable(r, getmetatable(t)) +end + +-- add val to set +local function add_to_set(set, val) + if not set[val] then + table.insert(set, val) + set[val] = true + end +end + +--# class creation logic #-- +local new_class, class_mt + +new_class = function(...) + local class = {} + local include = {...} + for i=1, #include do + local parent = include[i] + parent = parent.__included ~= nil and parent:__included(class) or parent + for k, v in pairs(parent) do + class[k] = v + end + end + class.__index = class + setmetatable(class, class_mt) + return class.__created ~= nil and class:__created() or class +end + +class_mt = { + __call = new_class, + __tostring = function(self) + local name = self.type and ("class %q"):format(self.type) or "class" + return rawtostring(self):gsub("^table", name) + end +} +class_mt.__index = class_mt + +--# base class and its contents #-- +-- feel free to redefine these as needed in your own classes; all of these are also optional and can be deleted. +return new_class { + --- instanciate. arguments are passed to the (eventual) constructor :init. + -- behavior undefined when called on an object. + -- set to false to make class non-instanciable (will give unhelpful error on instanciation attempt). + -- obj = class:new(...) + new = function(self, ...) + local obj = setmetatable({}, self) + return obj.init ~= nil and obj:init(...) or obj + end, + --- constructor. arguments are passed from :new. if :init returns a value, it will be returned by :new instead of the self object. + -- set to false to make class abstract (will give unhelpful error on instanciation attempt), redefine in subclass to make non-abstract again. + -- init = function(self, ...) content... end + init = nil, + --- check if the object is an instance of this class. + -- class:is(obj) + -- obj:is(class) + is = function(self, other) -- class:is(obj) + if getmetatable(self) == class_mt then + return getmetatable(other) == self + else + return other:is(self) + end + end, + --- check if the object is an instance of this class or of a class that inherited this class. + -- parentclass:issub(obj) + -- parentclass:issub(class) + -- obj:issub(parentclass) + issub = function(self, other) + if getmetatable(self) == class_mt then + return other.__parents and other.__parents[self] or self:is(other) + else + return other:issub(self) + end + end, + --- check if self is a class + -- class:isclass() + isclass = function(self) + return getmetatable(self) == class_mt + end, + --- called when included in a new class. if it returns a value, it will be used as the included table instead of the self table. + -- default function tracks parent classes and is needed for :issub to work, and returns a deep copy of the included table. + __included = function(self, into) + -- add to parents + if not into.__parents then + into.__parents = {} + end + local __parents = self.__parents + if __parents then + for i=1, #__parents do + add_to_set(into.__parents, __parents[i]) + end + end + add_to_set(into.__parents, self) + -- create copied table + local copied = copy(self) + copied.__parents = nil -- prevent __parents being overwritten + return copied + end, + -- automatically created by __included and needed for :issub to work + -- list and set of classes that are parents of this class: { parent_a, [parent_a] = true, parent_b, [parent_b] = true, ... } + __parents = nil, + --- called on the class when it is created. if it returns a value, it will be returned as the new class instead of the self class. + __created = nil, + --- pretty printing. type is used as the name of the class. + type = "object", + __tostring = function(self) + return rawtostring(self):gsub("^table", self.type) + end +} diff --git a/common.lua b/common.lua deleted file mode 100644 index c43a2d6..0000000 --- a/common.lua +++ /dev/null @@ -1,91 +0,0 @@ -local common - ---- replace values recursively in table t according to to_replace ([old table] = new table) --- already_replaced is a temporary table to avoid infinite loop & duplicate processing, no need to give it -local function replace_in_table(t, to_replace, already_replaced) - already_replaced = already_replaced or {} - already_replaced[t] = true - for k, v in pairs(t) do - if to_replace[v] then - t[k] = to_replace[v] - elseif type(v) == "table" and not already_replaced[v] then - replace_in_table(v, to_replace, already_replaced) - end - end -end - -common = { - --- recursively copy a table (key & values), handle cyclic references, no metatable - -- cache is table with copied tables [original table] = copied value, will create temporary table if argument is omitted - copy = function(t, cache) - if type(t) == "table" then - cache = cache or {} - if cache[t] then - return cache[t] - else - local c = {} - cache[t] = c - for k, v in pairs(t) do - c[common.copy(k, cache)] = common.copy(v, cache) - end - return c - end - else - return t - end - end, - --- given a table t from which some copy was issued, the copy cache, and a list of tables from the copied version, - -- put theses copied tables in t in place of their original values, preserving references to non-modified values - replace_with_copied_values = function(t, cache, copied_to_replace) - -- reverse copy cache - local ehcac = {} - for k, v in pairs(cache) do ehcac[v] = k end - -- build table of [original table] = replacement copied table - local to_replace = {} - for _, v in ipairs(copied_to_replace) do - local original = ehcac[v] - if original then -- table doesn't have an original value if it's a new table... - to_replace[original] = v - end - end - -- fix references to not-modified tables in modified values - local not_modified = {} - for original, modified in pairs(cache) do - if not to_replace[original] then - not_modified[modified] = original - end - end - for _, m in ipairs(copied_to_replace) do - replace_in_table(m, not_modified) - end - -- replace in t - replace_in_table(t, to_replace) - end, - --- given a table t issued from some copy, the copy cache, and a list of tables from the copied version, - -- put the original tables that are not in the list in t in place of their copied values - fix_not_modified_references = function(t, cache, copied_to_replace) - -- reverse copy cache - local ehcac = {} - for k, v in pairs(cache) do ehcac[v] = k end - -- build table of [original table] = replacement copied table - local to_replace = {} - for _, v in ipairs(copied_to_replace) do - local original = ehcac[v] - if original then -- table doesn't have an original value if it's a new table... - to_replace[original] = v - end - end - -- fix references to not-modified tables in t - local not_modified = {} - for original, modified in pairs(cache) do - if not to_replace[original] then - not_modified[modified] = original - end - end - replace_in_table(t, not_modified) - end -} - -package.loaded[...] = common - -return common diff --git a/common/init.lua b/common/init.lua new file mode 100644 index 0000000..24812c5 --- /dev/null +++ b/common/init.lua @@ -0,0 +1,70 @@ +local escape_cache = {} +local ansicolors = require("lib.ansicolors") + +local common = { + -- escape text to be used as an exact pattern + escape = function(str) + if not escape_cache[str] then + escape_cache[str] = str:gsub("[^%w]", "%%%1") + end + return escape_cache[str] + end, + --- transform an identifier into a clean version (trim each part) + trim = function(str) + return str:match("^%s*(.-)%s*$") + end, + fmt = function(str, ...) + return ansicolors(str):format(...) + end, + uuid = function() + return ("xxxxxxxx-xxxx-4xxx-Nxxx-xxxxxxxxxxxx") -- version 4 + :gsub("N", math.random(0x8, 0xb)) -- variant 1 + :gsub("x", function() return ("%x"):format(math.random(0x0, 0xf)) end) -- random hexadecimal digit + end, + -- list of operators and their priority that are handled through regular function calls & can be overloaded/etc. by the user + regular_operators = { + prefixes = { + { "~", 3.5 }, -- just below _~_ so else-if (~ condition ~ expression) parses as (~ (condition ~ expression)) + { "!", 11 }, + { "-", 11 }, + { "*", 11 }, + }, + suffixes = { + { ";", 1 }, + { "!", 12 } + }, + infixes = { + { ";", 1 }, + { "#", 2 }, + { "~", 4 }, { "~?", 4 }, + { "|>", 5 }, { "&", 5 }, { "|", 5 }, + { "==", 7 }, { "!=", 7 }, { ">=", 7 }, { "<=", 7 }, { "<", 7 }, { ">", 7 }, + { "+", 8 }, { "-", 8 }, + { "//", 9 }, { "/", 9 }, { "*", 9 }, { "%", 9 }, + { "^", 10 }, + { "::", 11 }, + { ".", 14 }, + { ":", 5 } + } + }, + -- list of all operators and their priority + operator_priority = { + [";_"] = 1, + ["$_"] = 1, + ["@_"] = 2, + ["_,_"] = 2, + ["_=_"] = 3, + ["_!_"] = 12, + ["_()"] = 13 + -- generated at run-time for regular operators + } +} + +local function store_priority(t, fmt) + for _, v in ipairs(t) do common.operator_priority[fmt:format(v[1])] = v[2] end +end +store_priority(common.regular_operators.infixes, "_%s_") +store_priority(common.regular_operators.prefixes, "%s_") +store_priority(common.regular_operators.suffixes, "_%s") + +return common diff --git a/common/to_anselme.lua b/common/to_anselme.lua new file mode 100644 index 0000000..c941cb8 --- /dev/null +++ b/common/to_anselme.lua @@ -0,0 +1,26 @@ +local ast = require("ast") +local Number, Struct, String, Nil, Boolean + +local function to_anselme(val) + if type(val) == "number" then + return Number:new(val) + elseif type(val) == "table" then + local s = Struct:new() + for k, v in pairs(val) do + s:set(to_anselme(k), to_anselme(v)) + end + return s + elseif type(val) == "string" then + return String:new(val) + elseif type(val) == "nil" then + return Nil:new() + elseif type(val) == "boolean" then + return Boolean:new(val) + else + error("can't convert "..type(val).." to an Anselme value") + end +end + +Number, Struct, String, Nil, Boolean = ast.Number, ast.Struct, ast.String, ast.Nil, ast.Boolean + +return to_anselme diff --git a/doc/api.md b/doc/api.md new file mode 100644 index 0000000..9770691 --- /dev/null +++ b/doc/api.md @@ -0,0 +1,263 @@ +This document describes how to use the main Anselme modules. This is generated automatically from the source files. + +Note that this file only describes the `anselme` and `state.State` modules, which are only a selection of what I consider to be the "public API" of Anselme that I will try to keep stable. +If you need more advanced control on Anselme, feel free to look into the other source files to find more; the most useful functions should all be reasonably commented. + +# anselme + +The main module. + +Usage: +```lua +local anselme = require("anselme") + +-- create a new state +local state = anselme.new() +state:load_stdlib() + +-- read an anselme script file +local f = assert(io.open("script.ans")) +local script = anselme.parse(f:read("*a"), "script.ans") +f:close() + +-- load the script in a new branch +local run_state = state:branch() +run_state:run(script) + +-- run the script +while run_state:active() do + local e, data = run_state:step() + if e == "text" then + for _, l in ipairs(data) do + print(l:format(run_state)) + end + elseif e == "choice" then + for i, l in ipairs(data) do + print(("%s> %s"):format(i, l:format(run_state))) + end + local choice = tonumber(io.read("*l")) + data:choose(choice) + elseif e == "return" then + run_state:merge() + elseif e == "error" then + error(data) + end +end +``` + +### .version + +Global version string. Follow semver. + +_defined at line 52 of [anselme.lua](../anselme.lua):_ `version = "2.0.0-alpha",` + +### .versions + +Table containing per-category version numbers. Incremented by one for any change that may break compatibility. + +_defined at line 55 of [anselme.lua](../anselme.lua):_ `versions = {` + +#### .language + +Version number for languages and standard library changes. + +_defined at line 57 of [anselme.lua](../anselme.lua):_ `language = 27,` + +#### .save + +Version number for save/AST format changes. + +_defined at line 59 of [anselme.lua](../anselme.lua):_ `save = 4,` + +#### .api + +Version number for Lua API changes. + +_defined at line 61 of [anselme.lua](../anselme.lua):_ `api = 8` + +### .parse (code, source) + +Parse a `code` string and return the generated AST. + +`source` is an optional string; it will be used as the code source name in error messages. + +Usage: +```lua +local ast = anselme.parse("1 + 2", "test") +ast:eval() +``` + +_defined at line 73 of [anselme.lua](../anselme.lua):_ `parse = function(code, source)` + +### .new () + +Return a new [State](#state). + +_defined at line 77 of [anselme.lua](../anselme.lua):_ `new = function()` + + +--- +_file generated at 2023-12-21T20:56:31Z_ + +# State + +Contains all state relative to an Anselme interpreter. Each State is fully independant from each other. +Each State can run a single script at a time, and variable changes are isolated between each State (see [branching](#branching-and-merging)). + +### :load_stdlib () + +Load standard library. +You will probably want to call this on every State right after creation. + +_defined at line 40 of [state/State.lua](../state/State.lua):_ `load_stdlib = function(self)` + +## Branching and merging + +### .branch_id + +Name of the branch associated to this State. + +_defined at line 47 of [state/State.lua](../state/State.lua):_ `branch_id = "main",` + +### .source_branch_id + +Name of the branch this State was branched from. + +_defined at line 49 of [state/State.lua](../state/State.lua):_ `source_branch_id = "main",` + +### :branch () + +Return a new branch of this State. + +Branches act as indepent copies of this State where any change will not be reflected in the source State until it is merged back into the source branch. +Note: probably makes the most sense to create branches from the main State only. + +_defined at line 55 of [state/State.lua](../state/State.lua):_ `branch = function(self)` + +### :merge () + +Merge everything that was changed in this branch back into the main State branch. + +Recommendation: only merge if you know that the state of the variables is consistent, for example at the end of the script, checkpoints, ... +If your script errored or was interrupted at an unknown point in the script, you might be in the middle of a calculation and variables won't contain +values you want to merge. + +_defined at line 64 of [state/State.lua](../state/State.lua):_ `merge = function(self)` + +## Variable definition + +### :define (name, value, func, raw_mode) + +Define a value in the global scope, converting it from Lua to Anselme if needed. + +* for lua functions: `define("name", "(x, y, z=5)", function(x, y, z) ... end)`, where arguments and return values of the function are automatically converted between anselme and lua values +* for other lua values: `define("name", value)` +* for anselme AST: `define("name", value)` + +`name` can be prefixed with symbol modifiers, for example ":name" for a constant variable. + +If `raw_mode` is true, no anselme-to/from-lua conversion will be performed in the function. +The function will receive the state followed by AST nodes as arguments, and is expected to return an AST node. + +_defined at line 82 of [state/State.lua](../state/State.lua):_ `define = function(self, name, value, func, raw_mode)` + +### :define_local (name, value, func, raw_mode) + +Same as `:define`, but define the expression in the current scope. + +_defined at line 88 of [state/State.lua](../state/State.lua):_ `define_local = function(self, name, value, func, raw_mode)` + +For anything more advanced, you can directly access the current scope stack stored in `state.scope`. +See [state/ScopeStack.lua](../state/ScopeStack.lua) for details; the documentation is not as polished as this file but you should still be able to find your way around. + +## Saving and loading persistent variables + +### :save () + +Return a serialized (string) representation of all global persistent variables in this State. + +This can be loaded back later using `:load`. + +_defined at line 100 of [state/State.lua](../state/State.lua):_ `save = function(self)` + +### :load (save) + +Load a string generated by `:save`. + +Variables that do not exist currently in the global scope will be defined, those that do will be overwritten with the loaded data. + +_defined at line 107 of [state/State.lua](../state/State.lua):_ `load = function(self, save)` + +## Current script state + +### :active () + +Indicate if a script is currently loaded in this branch. + +_defined at line 127 of [state/State.lua](../state/State.lua):_ `active = function(self)` + +### :state () + +Returns `"running`" if a script is currently loaded and running (i.e. this was called from the script). + +Returns `"active"` if a script is loaded but not currently running (i.e. the script has not started or is waiting on an event). + +Returns `"inactive"` if no script is loaded. + +_defined at line 135 of [state/State.lua](../state/State.lua):_ `state = function(self)` + +### :run (code, source) + +Load a script in this branch. It will become the active script. + +`code` is the code string or AST to run, `source` is the source name string to show in errors (optional). + +Note that this will only load the script; execution will only start by using the `:step` method. Will error if a script is already active in this State. + +_defined at line 147 of [state/State.lua](../state/State.lua):_ `run = function(self, code, source)` + +### :step () + +When a script is active, will resume running it until the next event. + +Will error if no script is active. + +Returns `event type string, event data`. + +_defined at line 160 of [state/State.lua](../state/State.lua):_ `step = function(self)` + +### :interrupt (code, source) + +Stops the currently active script. + +Will error if no script is active. + +If `code` is given, the script will not be disabled but instead will be immediately replaced with this new script. +The new script will then be started on the next `:step` and will preserve the current scope. This can be used to trigger an exit function or similar in the active script. + +_defined at line 178 of [state/State.lua](../state/State.lua):_ `interrupt = function(self, code, source)` + +### :eval (code, source) + +Evaluate an expression in the global scope. + +This can be called from outside a running script, but an error will be triggered the expression raise any event other than return. + +* returns AST in case of success. Run `:to_lua(state)` on it to convert to a Lua value. +* returns `nil, error message` in case of error. + +_defined at line 199 of [state/State.lua](../state/State.lua):_ `eval = function(self, code, source)` + +### :eval_local (code, source) + +Same as `:eval`, but evaluate the expression in the current scope. + +_defined at line 206 of [state/State.lua](../state/State.lua):_ `eval_local = function(self, code, source)` + +If you want to perform more advanced manipulation of the resulting AST nodes, look at the `ast` modules. +In particular, every Node inherits the methods from [ast.abstract.Node](../ast/abstract/Node.lua). +Otherwise, each Node has its own module file defined in the [ast/](../ast) directory. + + +--- +_file generated at 2023-12-21T20:56:31Z_ diff --git a/doc/api.md.template b/doc/api.md.template new file mode 100644 index 0000000..6624c80 --- /dev/null +++ b/doc/api.md.template @@ -0,0 +1,12 @@ +This document describes how to use the main Anselme modules. This is generated automatically from the source files. + +Note that this file only describes the `anselme` and `state.State` modules, which are only a selection of what I consider to be the "public API" of Anselme that I will try to keep stable. +If you need more advanced control on Anselme, feel free to look into the other source files to find more; the most useful functions should all be reasonably commented. + +# anselme + +{{anselme.lua}} + +# State + +{{state/State.lua}} diff --git a/doc/gendocs.lua b/doc/gendocs.lua new file mode 100644 index 0000000..60a0eda --- /dev/null +++ b/doc/gendocs.lua @@ -0,0 +1,86 @@ +-- LDoc doesn't like me so I don't like LDoc. +-- Behold! A documentation generator that doesn't try to be smart! +-- Call this from the root anselme directory: `lua doc/gendocs.lua` + +local files = { + "doc/api.md" +} +local source_link_prefix = "../" +local base_header_level = 2 + +local title_extractors = { + -- methods + { "(.-)%s*=%s*function%s*%(%s*self%s*%)", ":%1 ()" }, + { "(.-)%s*=%s*function%s*%(%s*self%s*%,%s*(.-)%)", ":%1 (%2)" }, + + -- functions + { "(.-)%s*=%s*function%s*%((.-)%)", ".%1 (%2)" }, + + -- fields + { "(.-)%s*=", ".%1" }, +} +local function extract_block_title(line) + local title = line + for _, ext in ipairs(title_extractors) do + if line:match(ext[1]) then + title = line:gsub(("^%s.-$"):format(ext[1]), ext[2]) + break + end + end + return title +end + +local function process(content) + return content:gsub("{{(.-)}}", function(lua_file) + local f = io.open(lua_file, "r") + local c = f:read("*a") + f:close() + + local output = {} + + local comment_block + local line_no = 1 + for line in c:gmatch("[^\n]*") do + if line:match("^%s*%-%-%-") then + comment_block = {} + table.insert(comment_block, (line:match("^%s*%-%-%-%s?(.-)$"))) + elseif comment_block then + if line:match("^%s*%-%-") then + table.insert(comment_block, (line:match("^%s*%-%-%s?(.-)$"))) + else + if line:match("[^%s]") then + local ident, code = line:match("^(%s*)(.-)$") + table.insert(comment_block, 1, ("%s %s\n"):format( + ("#"):rep(base_header_level+utf8.len(ident)), + extract_block_title(code) + )) + table.insert(comment_block, ("\n_defined at line %s of [%s](%s):_ `%s`"):format(line_no, lua_file, source_link_prefix..lua_file, code)) + end + table.insert(comment_block, "") + table.insert(output, table.concat(comment_block, "\n")) + comment_block = nil + end + end + line_no = line_no + 1 + end + + table.insert(output, ("\n---\n_file generated at %s_"):format(os.date("!%Y-%m-%dT%H:%M:%SZ"))) + + return table.concat(output, "\n") + end) +end + +local function generate_file(input, output) + local f = io.open(input, "r") + local content = f:read("*a") + f:close() + + local out = process(content, output) + f = io.open(output, "w") + f:write(out) + f:close() +end + +for _, path in ipairs(files) do + generate_file(path..".template", path) +end diff --git a/doc/language.md b/doc/language.md new file mode 100644 index 0000000..30404ce --- /dev/null +++ b/doc/language.md @@ -0,0 +1 @@ +TODO \ No newline at end of file diff --git a/doc/tutorial.md b/doc/tutorial.md new file mode 100644 index 0000000..30404ce --- /dev/null +++ b/doc/tutorial.md @@ -0,0 +1 @@ +TODO \ No newline at end of file diff --git a/ideas.md b/ideas.md new file mode 100644 index 0000000..0fb3c09 --- /dev/null +++ b/ideas.md @@ -0,0 +1,127 @@ +Various ideas and things that may or may not be done. It's like GitHub issues, but I don't have to leave my text editor or connect to the scary Internet. + +Loosely ordered by willingness to implement. + +--- + +Documentation: +* language reference +* tutorial + +--- + +Write tests. Kinda mandatory actually, while I've tried to improve and do it much better than Anselme v1 there's still plenty interweaved moving parts here. Not sure how much better I can do with the same design requirements tbh. See anselme v1 tests to get a base library of tests. + +--- + +Make requires relative. Currently Anselme expect its directory to be properly somewhere in package.path. +Also improve compatibility with Lua 5.3 and LuaJIT (I don't think we should support anything other than 5.4, 5.3 and LuaJIT). + +--- + +Translation. TODO Design + +Translation model: +- for text, choices: text+line+file as id, translation (either text or function) +- for strings, assets, ...: ? translatable string ? +- for variable names: ? +- for stdlib: ? + +--- + +Persistence "issue": Storing a closure stores it whole environment, which includes all the stdlib. Technically it works, but that's a lot of useless information. Would need to track which variable is used (should be doable in prepare) and prune the closure. +Or register all functions as ressources in binser - that makes kinda sense, they're immutable, and their signature should be unique. Would need to track which functions are safe to skip / can be reloaded from somewhere on load. + +--- + +Redesign the Node hierarchy to avoid cycles. + +--- + +Standard library. + +* Text manipulation would make sense, but that would require a full UTF-8/Unicode support library like https://github.com/starwing/luautf8. +* Something to load other files. Maybe not load it by default to let the calling game sandbox Anselme. +* Implement the useful functions from Anselme v1. +* Checkpoint management. +* Overloadable :format for custom types. + +--- + +Server API. + +To be able to use Anselme in another language, it would be nice to be able to access it over some form of IPC. + +No need to bother with networking I think. Just do some stdin/stdout handling, maybe use something like JSON-RPC: https://www.jsonrpc.org/specification (reminder: will need to add some metadata to specify content length, not aware of any streaming json lib in pure Lua - here's a rxi seal of quality library btw: https://github.com/rxi/json.lua). Or just make our own protocol around JSON. +Issue: how to represent Anselme values? they will probably contain cycles, needs to access their methods, etc. +Probably wise to look into how other do it. LSP: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/ + +--- + +Syntax modifications: + +* on the subject of assignments: + - multiple assignments: + + :a, :b = 5, 6 + a, b = list!($(l) l[3], l[6]) + + Easy by interpreting the left operand as a List. + + - regular operator assignments: + Could interpret the left operand as a string when it is an identifier, like how _._ works. + Would feel good to have less nodes. But because we can doesn't mean we should. Also Assignment is reused in a few other places. + +--- + +Reduce the number of AST node types ; try to merge similar node and make simpler individuals nodes if possible by composing them. +Won't help with performance but make me feel better, and easier to extend. Anselme should be more minimal is possible. + +--- + +Static analysis tools. + +To draw a graph of branches, keep track of used variables and prune the unused ones from the Environments, pre-filter Overloads, etc. + +--- + +Multiline expressions. + +* add the ability to escape newlines + Issue: need a way to correctly track line numbers, the current parser assumes one expression = one source +* allow some expressions to run over several lines (the ones that expect a closing token, like paren/list/structs) + Issue: the line and expression parsing is completely separate + +--- + +Performance: + +* the most terribly great choice is the overload with parameter filtering. + Assuming the filter functions are pure seems reasonable, so caching could be done. + Could also hardcode some shortcut paths for the simple type equality check case. + Or track function/expression purity and cache/precompute the results. Not sure how to do that with multiple dispatch though. + (note for future reference: once a function is first evaluated into a closure, its parameters are fixed, including the type check overloads) +* the recursive AST interpreter is also pretty meh, could do a bytecode VM. + This one seems like a lot more work. + Could also compile to Lua and let LuaJIT deal with it. Or WASM, that sounds trendy. + +Then again, performance has never been a goal of Anselme. + +--- + +Macros. + +Could be implemented by creating functions to build AST nodes from Anselme that can also take quotes as arguments. +That should be easy, but I don't remember why I wanted macros in the first place, so until I want them again, shrug. + +--- + +High concept ideas / stuff that sounds cool but maybe not worth it. + +* Instead of using a bunch of sigils as operators, accept fancy unicode caracters. + Easy to parse, but harder to write. + Could implement a formatter/linter/whatever this is called these days and have Anselme recompile the AST into a nice, properly Unicodified output. + Issue: the parser may be performing some transformations on the AST that would make the output an uncanny valley copy of the original. Also we need to preserve comments. +* Files are so 2000; instead put everything in a big single file and use a custom editor to edit it. + Imagine selecting an identifier, and then it zooms in and show the AST associated to it. Nested indefinitely. Feels very futuristic, so probably worth it. +* Frankly the event buffer system still feel pretty janky, but I don't have any better idea for now. diff --git a/init.lua b/init.lua deleted file mode 100644 index 82118e2..0000000 --- a/init.lua +++ /dev/null @@ -1 +0,0 @@ -return require((...)..".anselme") \ No newline at end of file diff --git a/interpreter/common.lua b/interpreter/common.lua deleted file mode 100644 index 8ab1a5d..0000000 --- a/interpreter/common.lua +++ /dev/null @@ -1,699 +0,0 @@ -local atypes, ltypes -local eval, run_block -local replace_with_copied_values, fix_not_modified_references -local common -local identifier_pattern -local copy - -local function random_identifier() - local r = "" - for _=1, 16 do -- that's like 10^31 possibilities, ought to be enough for anyone - r = r .. string.char(math.random(32, 126)) - end - return r -end - -common = { - --- merge interpreter state with global state - merge_state = function(state) - local mt = getmetatable(state.variables) - -- store current scoped variables before merging them - for fn in pairs(mt.scoped) do - common.scope:store_last_scope(state, fn) - end - -- merge alias state - local global = state.interpreter.global_state - for alias, fqm in pairs(state.aliases) do - global.aliases[alias] = fqm - state.aliases[alias] = nil - end - -- merge modified mutable varables - local copy_cache, modified_tables = mt.copy_cache, mt.modified_tables - replace_with_copied_values(global.variables, copy_cache, modified_tables) - mt.copy_cache = {} - mt.modified_tables = {} - mt.cache = {} - -- merge modified re-assigned variables - for var, value in pairs(state.variables) do - if var:match("^"..identifier_pattern.."$") then -- skip scoped variables - global.variables[var] = value - state.variables[var] = nil - end - end - -- scoping: since merging means we will re-copy every variable from global state again, we need to simulate this - -- behaviour for scoped variables (to have consistent references for mutables values in particular), including - -- scopes that aren't currently active - fix_not_modified_references(mt.scoped, copy_cache, modified_tables) -- replace not modified values in scope with original before re-copying to keep consistent references - for _, scopes in pairs(mt.scoped) do - for _, scope in ipairs(scopes) do - for var, value in pairs(scope) do - -- pretend the value for this scope is the global value so the cache system perform the new copy from it - local old_var = global.variables[var] - global.variables[var] = value - state.variables[var] = nil - scope[var] = state.variables[var] - mt.cache[var] = nil - global.variables[var] = old_var - end - end - end - -- restore last scopes - for fn in pairs(mt.scoped) do - common.scope:set_last_scope(state, fn) - end - end, - --- checks if the value is compatible with the variable's (eventual) constraint - -- returns depth, or math.huge if no constraint - -- returns nil, err - check_constraint = function(state, fqm, val) - local constraint = state.variable_metadata[fqm].constraint - if constraint then - if not constraint.value then - local v, e = eval(state, constraint.pending) - if not v then - return nil, ("%s; while evaluating constraint for variable %q"):format(e, fqm) - end - constraint.value = v - end - local depth = common.is_of_type(val, constraint.value) - if not depth then - return nil, ("constraint check failed") - end - return depth - end - return math.huge - end, - --- checks if the variable is mutable - -- returns true - -- returns nil, mutation illegal message - check_mutable = function(state, fqm) - if state.variable_metadata[fqm].constant then - return nil, ("can't change the value of a constant %q"):format(fqm) - end - return true - end, - --- mark a value as constant, recursively affecting all the potentially mutable subvalues - mark_constant = function(v) - return assert(common.traverse(v, function(v) - if v.hash_id then v.hash_id = nil end -- no longer need to compare by id - end, "mark_constant")) - end, - -- traverse v and all the subvalues it contains - -- callback(v) is called on every value after traversing its subvalues - -- if pertype_callback is given, will then call the associated callback(v) in the type table for each value - -- both those callbacks can either returns nil (success) or nil, err (error) - -- returns true - -- return nil, error - traverse = function(v, callback, pertype_callback) - if atypes[v.type] and atypes[v.type].traverse then - local r, e = atypes[v.type].traverse(v.value, callback, pertype_callback) - if not r then return nil, e end - r, e = callback(v) - if e then return nil, e end - if pertype_callback and atypes[v.type][pertype_callback] then - r, e = atypes[v.type][pertype_callback](v) - if e then return nil, e end - end - return true - else - error(("don't know how to traverse type %s"):format(v.type)) - end - end, - --- checks if the value can be persisted - -- returns true - -- returns nil, persist illegal message - check_persistable = function(v) - return common.traverse(v, function(v) - if v.nonpersistent then - return nil, ("can't put a non persistable %s into a persistent variable"):format(v.type) - end - end) - end, - --- returns a variable's value, evaluating a pending expression if neccessary - -- if you're sure the variable has already been evaluated, use state.variables[fqm] directly - -- return var - -- return nil, err - get_variable = function(state, fqm) - local var = state.variables[fqm] - if var.type == "pending definition" then - -- evaluate - local v, e = eval(state, var.value.expression) - if not v then - return nil, ("%s; while evaluating default value for variable %q defined at %s"):format(e, fqm, var.value.source) - end - -- make constant if variable is constant - if state.variable_metadata[fqm].constant then - v = copy(v) - common.mark_constant(v) - end - -- set variable - local s, err = common.set_variable(state, fqm, v, state.variable_metadata[fqm].constant) - if not s then return nil, err end - return v - else - return var - end - end, - --- set the value of a variable - -- returns true - -- returns nil, err - set_variable = function(state, name, val, bypass_constant_check) - if val.type ~= "pending definition" then - -- check constant - if not bypass_constant_check then - local s, e = common.check_mutable(state, name) - if not s then - return nil, ("%s; while assigning value to variable %q"):format(e, name) - end - end - -- check persistence - if state.variable_metadata[name].persistent then - local s, e = common.check_persistable(val) - if not s then - return nil, ("%s; while assigning value to variable %q"):format(e, name) - end - end - -- check constraint - local s, e = common.check_constraint(state, name, val) - if not s then - return nil, ("%s; while assigning value to variable %q"):format(e, name) - end - end - state.variables[name] = val - return true - end, - --- handle scoped function - scope = { - init_scope = function(self, state, fn) - local scoped = getmetatable(state.variables).scoped - if not fn.scoped then error("trying to initialize the scope stack for a non-scoped function") end - if not scoped[fn] then scoped[fn] = {} end - -- check scoped variables - for _, name in ipairs(fn.scoped) do - -- put fresh variable from global state in scope - local val = state.interpreter.global_state.variables[name] - if val.type ~= "undefined argument" and val.type ~= "pending definition" then -- only possibilities for scoped variable, and they're immutable - error("invalid scoped variable") - end - end - end, - --- push a new scope for this function - push = function(self, state, fn) - local scoped = getmetatable(state.variables).scoped - self:init_scope(state, fn) - -- preserve current values in last scope - self:store_last_scope(state, fn) - -- add scope - local fn_scope = {} - table.insert(scoped[fn], fn_scope) - self:set_last_scope(state, fn) - end, - --- pop the last scope for this function - pop = function(self, state, fn) - local scoped = getmetatable(state.variables).scoped - if not scoped[fn] then error("trying to pop a scope without any pushed scope") end - -- remove current scope - table.remove(scoped[fn]) - -- restore last scope - self:set_last_scope(state, fn) - -- if the stack is empty, - -- we could remove mt.scoped[fn] I guess, but I don't think there's going to be a million different functions in a single game so should be ok - -- (anselme's performance is already bad enough, let's not create tables at each function call...) - end, - --- store the current values of the scoped variables in the last scope of this function - store_last_scope = function(self, state, fn) - local scopes = getmetatable(state.variables).scoped[fn] - local last_scope = scopes[#scopes] - if last_scope then - for _, name in pairs(fn.scoped) do - local val = rawget(state.variables, name) - if val then - last_scope[name] = val - end - end - end - end, - --- set scopped variables to previous scope - set_last_scope = function(self, state, fn) - local scopes = getmetatable(state.variables).scoped[fn] - for _, name in ipairs(fn.scoped) do - state.variables[name] = nil - end - local last_scope = scopes[#scopes] - if last_scope then - for name, val in pairs(last_scope) do - state.variables[name] = val - end - end - end - }, - --- mark a table as modified, so it will be merged on the next checkpoint if it appears somewhere in a value - mark_as_modified = function(state, v) - local modified = getmetatable(state.variables).modified_tables - table.insert(modified, v) - end, - --- returns true if a variable should be persisted on save - -- will exclude: variable that have not been evaluated yet and non-persistent variable - -- this will by consequence excludes variable in scoped variables (can be neither persistent not evaluated into global state), constants (can not be persistent), internal anselme variables (not marked persistent), etc. - -- You may want to check afterwards with check_persistable to check if the value can actually be persisted. - should_be_persisted = function(state, name, value) - return value.type ~= "pending definition" and state.variable_metadata[name].persistent - end, - --- check truthyness of an anselme value - truthy = function(val) - if val.type == "number" then - return val.value ~= 0 - elseif val.type == "nil" then - return false - else - return true - end - end, - --- compare two anselme values for equality. - -- for immutable values or constants: compare by value - -- for mutable values: compare by reference - compare = function(a, b) - if a.type ~= b.type or a.constant ~= b.constant then - return false - end - if a.type == "pair" or a.type == "annotated" then - return common.compare(a.value[1], b.value[1]) and common.compare(a.value[2], b.value[2]) - elseif a.type == "function reference" then - if #a.value ~= #b.value then - return false - end - for _, aname in ipairs(a.value) do - local found = false - for _, bname in ipairs(b.value) do - if aname == bname then - found = true - break - end - end - if not found then - return false - end - end - return true - -- mutable types: need to be constant - elseif a.constant and a.type == "list" then - if #a.value ~= #b.value then - return false - end - for i, v in ipairs(a.value) do - if not common.compare(v, b.value[i]) then - return false - end - end - return true - elseif a.constant and a.type == "map" then - return common.hash(a) == common.hash(b) - elseif a.constant and a.type == "object" then - if a.value.class ~= b.value.class then - return false - end - -- check every attribute redefined in a and b - -- NOTE: comparaison will fail if an attribute has been redefined in only one of the object, even if it was set to the same value as the original class attribute - local compared = {} - for name, v in pairs(a.value.attributes) do - compared[name] = true - if not b.value.attributes[name] or not common.compare(v, b.value.attributes[name]) then - return false - end - end - for name, v in pairs(b.value.attributes) do - if not compared[name] then - if not a.value.attributes[name] or not common.compare(v, a.value.attributes[name]) then - return false - end - end - end - return true - -- the rest - else - return a.value == b.value - end - end, - --- format a anselme value to something printable - -- does not call custom {}() functions, only built-in ones, so it should not be able to fail - -- str: if success - -- nil, err: if error - format = function(val) - if atypes[val.type] and atypes[val.type].format then - return atypes[val.type].format(val.value) - else - return nil, ("no formatter for type %q"):format(val.type) - end - end, - --- compute a hash for a value. - -- A hash is a Lua string such as, given two values, they are considered equal by Anselme if and only if their hash are considered equal by Lua. - -- Will generate random identifiers for mutable values (equality test by reference) in order for the identifier to stay the same accross checkpoints and - -- other potential variable copies. - -- str: if success - -- nil, err: if error - hash = function(val) - if atypes[val.type] and atypes[val.type].hash then - if atypes[val.type].mutable and not val.constant then - if not val.hash_id then val.hash_id = random_identifier() end - return ("mut(%s)"):format(val.hash_id) - else - return atypes[val.type].hash(val.value) - end - else - return nil, ("no hasher for type %q"):format(val.type) - end - end, - --- recompute all the hases in a map. - -- str: if success - -- nil, err: if error - update_hashes = function(map) - for k, v in pairs(map.value) do - local hash, e = common.hash(v[1]) - if not hash then return nil, e end - map[k] = nil - map[hash] = v - end - end, - --- convert anselme value to lua - -- lua value: if success (may be nil!) - -- nil, err: if error - to_lua = function(val, state) - if atypes[val.type] and atypes[val.type].to_lua then - return atypes[val.type].to_lua(val.value, state) - else - return nil, ("no Lua exporter for type %q"):format(val.type) - end - end, - --- convert lua value to anselme - -- anselme value: if success - -- nil, err: if error - from_lua = function(val) - if ltypes[type(val)] and ltypes[type(val)].to_anselme then - return ltypes[type(val)].to_anselme(val) - else - return nil, ("no Lua importer for type %q"):format(type(val)) - end - end, - --- evaluate a text AST into a single Lua string - -- string: if success - -- nil, err: if error - eval_text = function(state, text) - local l = {} - local s, e = common.eval_text_callback(state, text, function(str) table.insert(l, str) end) - if not s then return nil, e end - return table.concat(l) - end, - --- same as eval_text, but instead of building a Lua string, call callback for every evaluated part of the text - -- callback returns nil, err in case of error - -- true: if success - -- nil, err: if error - eval_text_callback = function(state, text, callback) - for _, item in ipairs(text) do - if type(item) == "string" then - callback(item) - else - local v, e = eval(state, item) - if not v then return v, e end - v, e = common.format(v) - if not v then return v, e end - if v ~= "" then - local r, err = callback(v) - if err then return r, err end - end - end - end - return true - end, - --- check if an anselme value is of a certain type or annotation - -- specificity(number): if var is of type type. lower is more specific - -- false: if not - is_of_type = function(var, type) - local depth = 1 - -- var has a custom annotation - if var.type == "annotated" then - -- special case: if we just want to see if a value is annotated - if type.type == "string" and type.value == "annotated" then - return depth - end - -- check annotation - local var_type = var.value[2] - while true do - if common.compare(var_type, type) then -- same type - return depth - elseif var_type.type == "annotated" then -- compare parent type - depth = depth + 1 - var_type = var_type.value[2] - else -- no parent, fall back on base type - depth = depth + 1 - var = var.value[1] - break - end - end - end - -- var has a base type - return type.type == "string" and type.value == var.type and depth - end, - -- return a pretty printable type value for var - pretty_type = function(var) - if var.type == "annotated" then - return common.format(var.value[2]) - else - return var.type - end - end, - --- tag management - tags = { - --- push new tags on top of the stack, from Anselme values. val is expected to be a map. - push = function(self, state, val) - local new = { type = "map", value = {} } - -- copy - local last = self:current(state) - for k, v in pairs(last.value) do new.value[k] = v end - -- append new values - for k, v in pairs(val.value) do new.value[k] = v end - -- add - table.insert(state.interpreter.tags, new) - end, - --- same but do not merge with last stack item - push_no_merge = function(self, state, val) - table.insert(state.interpreter.tags, val) - end, - -- pop tag table on top of the stack - pop = function(self, state) - table.remove(state.interpreter.tags) - end, - --- return current lua tags table - current = function(self, state) - return state.interpreter.tags[#state.interpreter.tags] or { type = "map", value = {} } - end, - --- returns length of tags stack - len = function(self, state) - return #state.interpreter.tags - end, - --- pop item until we reached desired stack length - -- so in case there's a possibility to mess up the stack somehow, it will restore the stack to a good state - trim = function(self, state, len) - while #state.interpreter.tags > len do - self:pop(state) - end - end - }, - --- event buffer management - -- i.e. only for text and choice events - events = { - --- add a new element to the last event in the current buffer - -- will create new event if needed - append = function(self, state, type, data) - local buffer = self:current_buffer(state) - local last = buffer[#buffer] - if not last or last.type ~= type then - last = { type = type, value = {} } - table.insert(buffer, last) - end - table.insert(last.value, data) - end, - - --- new events will be collected in this event buffer (any table) until the next pop - -- this is handled by a stack so nesting is allowed - push_buffer = function(self, state, buffer) - table.insert(state.interpreter.event_buffer_stack, buffer) - end, - --- stop capturing events of a certain type. - -- must be called after a push_buffer - pop_buffer = function(self, state) - table.remove(state.interpreter.event_buffer_stack) - end, - --- returns the current buffer value - current_buffer = function(self, state) - return state.interpreter.event_buffer_stack[#state.interpreter.event_buffer_stack] - end, - - -- flush event buffer if it's neccessary to push an event of the given type - -- returns true in case of success - -- returns nil, err in case of error - make_space_for = function(self, state, type) - if #state.interpreter.event_buffer_stack == 0 and state.interpreter.current_event and state.interpreter.current_event.type ~= type then - return self:manual_flush(state) - end - return true - end, - - --- write all the data in a buffer into the current buffer, or to the game is no buffer is currently set - write_buffer = function(self, state, buffer) - for _, event in ipairs(buffer) do - if #state.interpreter.event_buffer_stack == 0 then - if event.type == "flush" then - local r, e = self:manual_flush(state) - if not r then return r, e end - elseif state.interpreter.current_event then - if state.interpreter.current_event.type == event.type then - for _, v in ipairs(event.value) do - table.insert(state.interpreter.current_event.value, v) - end - else - local r, e = self:manual_flush(state) - if not r then return r, e end - state.interpreter.current_event = event - end - else - state.interpreter.current_event = event - end - else - local current_buffer = self:current_buffer(state) - table.insert(current_buffer, event) - end - end - return true - end, - - --- same as manual_flush but add the flush to the current buffer if one is set instead of directly to the game - flush = function(self, state) - if #state.interpreter.event_buffer_stack == 0 then - return self:manual_flush(state) - else - local current_buffer = self:current_buffer(state) - table.insert(current_buffer, { type = "flush" }) - return true - end - end, - - --- flush events and send them to the game if possible - -- returns true in case of success - -- returns nil, err in case of error - manual_flush = function(self, state) - while state.interpreter.current_event do - local event = state.interpreter.current_event - state.interpreter.current_event = nil - state.interpreter.skip_choices_until_flush = nil - - local type = event.type - local buffer - - local choices - -- copy & process text buffer - if type == "text" then - buffer = common.post_process_text(state, event.value) - -- copy & process choice buffer - elseif type == "choice" then - -- copy & process choice text content into buffer, and needed private state into choices for each choice - buffer = {} - choices = {} - for _, c in ipairs(event.value) do - table.insert(buffer, common.post_process_text(state, c)) - table.insert(choices, c._state) - end - -- discard empty choices - for i=#buffer, 1, -1 do - if #buffer[i] == 0 then - table.remove(buffer, i) - table.remove(choices, i) - end - end - -- nervermind - if #choices == 0 then - return true - end - end - - -- yield event - coroutine.yield(type, buffer) - - -- run choice - if type == "choice" then - local sel = state.interpreter.choice_selected - state.interpreter.choice_selected = nil - if not sel or sel < 1 or sel > #choices then - return nil, "invalid choice" - else - local choice = choices[sel] - -- execute in expected tag & event capture state - local capture_state = state.interpreter.event_capture_stack - state.interpreter.event_capture_stack = {} - common.tags:push_no_merge(state, choice.tags) - local _, e = run_block(state, choice.block) - common.tags:pop(state) - state.interpreter.event_capture_stack = capture_state - if e then return nil, e end - -- we discard return value from choice block as the execution is delayed until an event flush - -- and we don't want to stop the execution of another function unexpectedly - end - end - end - return true - end - }, - --- copy some text & process it to be suited to be sent to Lua in an event - post_process_text = function(state, text) - local r = {} - -- copy into r & convert tags to lua - for _, t in ipairs(text) do - local tags = common.to_lua(t.tags, state) - if state.interpreter.base_lua_tags then - for k, v in pairs(state.interpreter.base_lua_tags) do - if tags[k] == nil then tags[k] = v end - end - end - table.insert(r, { - text = t.text, - tags = tags - }) - end - -- remove trailing spaces - if state.feature_flags["strip trailing spaces"] then - local final = r[#r] - if final then - final.text = final.text:match("^(.-) *$") - if final.text == "" then - table.remove(r) - end - end - end - -- remove duplicate spaces - if state.feature_flags["strip duplicate spaces"] then - for i=1, #r-1 do - local a, b = r[i], r[i+1] - local na = #a.text:match(" *$") - local nb = #b.text:match("^ *") - if na > 0 and nb > 0 then -- remove duplicated spaces from second element first - b.text = b.text:match("^ *(.-)$") - end - if na > 1 then - a.text = a.text:match("^(.- ) *$") - end - end - end - return r - end -} - -package.loaded[...] = common -local types = require((...):gsub("interpreter%.common$", "stdlib.types")) -atypes, ltypes = types.anselme, types.lua -eval = require((...):gsub("common$", "expression")) -run_block = require((...):gsub("common$", "interpreter")).run_block -local acommon = require((...):gsub("interpreter%.common$", "common")) -replace_with_copied_values, fix_not_modified_references = acommon.replace_with_copied_values, acommon.fix_not_modified_references -identifier_pattern = require((...):gsub("interpreter%.common$", "parser.common")).identifier_pattern -copy = require((...):gsub("interpreter%.common$", "common")).copy - -return common diff --git a/interpreter/expression.lua b/interpreter/expression.lua deleted file mode 100644 index 9d80d24..0000000 --- a/interpreter/expression.lua +++ /dev/null @@ -1,586 +0,0 @@ -local expression -local to_lua, from_lua, eval_text, truthy, format, pretty_type, get_variable, tags, eval_text_callback, events, flatten_list, set_variable, scope, check_constraint, hash - -local run - -local unpack = table.unpack or unpack - ---- evaluate an expression --- returns evaluated value (table) if success --- returns nil, error if error -local function eval(state, exp) - -- nil - if exp.type == "nil" then - return { - type = "nil", - value = nil - } - -- number - elseif exp.type == "number" then - return { - type = "number", - value = exp.value - } - -- string - elseif exp.type == "string" then - local t, e = eval_text(state, exp.text) - if not t then return nil, e end - return { - type = "string", - value = t - } - -- text buffer - elseif exp.type == "text buffer" then - -- eval text expression - local v, e = eval(state, exp.text) - if not v then return v, e end - local l = v.type == "list" and v.value or { v } - -- write resulting buffers (plural if loop in text expression) into a single result buffer - local buffer = {} - for _, item in ipairs(l) do - if item.type == "event buffer" then - for _, event in ipairs(item.value) do - if event.type ~= "text" and event.type ~= "flush" then - return nil, ("event %q can't be captured in a text buffer"):format(event.type) - end - table.insert(buffer, event) - end - end - end - return { - type = "event buffer", - value = buffer - } - -- parentheses - elseif exp.type == "parentheses" then - return eval(state, exp.expression) - -- list defined in brackets - elseif exp.type == "list brackets" then - if exp.expression then - local v, e = eval(state, exp.expression) - if not v then return nil, e end - if exp.expression.type == "list" then - return v - -- contained a single element, wrap in list manually - else - return { - type = "list", - value = { v } - } - end - else - return { - type = "list", - value = {} - } - end - -- map defined in brackets - elseif exp.type == "map brackets" then - -- get constructing list - local list, e = eval(state, { type = "list brackets", expression = exp.expression }) - if not list then return nil, e end - -- make map - local map = {} - for i, v in ipairs(list.value) do - local key, value - if v.type == "pair" then - key = v.value[1] - value = v.value[2] - else - key = { type = "number", value = i } - value = v - end - local h, err = hash(key) - if not h then return nil, err end - map[h] = { key, value } - end - return { - type = "map", - value = map - } - -- list defined using , operator - elseif exp.type == "list" then - local flat = flatten_list(exp) - local l = {} - for _, ast in ipairs(flat) do - local v, e = eval(state, ast) - if not v then return nil, e end - table.insert(l, v) - end - return { - type = "list", - value = l - } - -- assignment - elseif exp.type == ":=" then - if exp.left.type == "variable" then - local name = exp.left.name - local val, vale = eval(state, exp.right) - if not val then return nil, vale end - local s, e = set_variable(state, name, val) - if not s then return nil, e end - return val - else - return nil, ("don't know how to perform assignment on %s expression"):format(exp.left.type) - end - -- lazy boolean operators - elseif exp.type == "&" then - local left, lefte = eval(state, exp.left) - if not left then return nil, lefte end - if truthy(left) then - local right, righte = eval(state, exp.right) - if not right then return nil, righte end - if truthy(right) then - return { - type = "number", - value = 1 - } - end - end - return { - type = "number", - value = 0 - } - elseif exp.type == "|" then - local left, lefte = eval(state, exp.left) - if not left then return nil, lefte end - if truthy(left) then - return { - type = "number", - value = 1 - } - end - local right, righte = eval(state, exp.right) - if not right then return nil, righte end - return { - type = "number", - value = truthy(right) and 1 or 0 - } - -- conditional - elseif exp.type == "~" then - local right, righte = eval(state, exp.right) - if not right then return nil, righte end - if truthy(right) then - local left, lefte = eval(state, exp.left) - if not left then return nil, lefte end - return left - end - return { - type = "nil", - value = nil - } - -- while loop - elseif exp.type == "~?" then - local right, righte = eval(state, exp.right) - if not right then return nil, righte end - local l = {} - while truthy(right) do - local left, lefte = eval(state, exp.left) - if not left then return nil, lefte end - table.insert(l, left) - -- next iteration - right, righte = eval(state, exp.right) - if not right then return nil, righte end - end - return { - type = "list", - value = l - } - -- tag - elseif exp.type == "#" then - local right, righte = eval(state, { type = "map brackets", expression = exp.right }) - if not right then return nil, righte end - tags:push(state, right) - local left, lefte = eval(state, exp.left) - tags:pop(state) - if not left then return nil, lefte end - return left - -- variable - elseif exp.type == "variable" then - return get_variable(state, exp.name) - -- references - elseif exp.type == "function reference" then - return { - type = "function reference", - value = exp.names - } - elseif exp.type == "variable reference" then - -- check if variable is already a reference - local v, e = eval(state, exp.expression) - if not v then return nil, e end - if v.type == "function reference" or v.type == "variable reference" then - return v - else - return { type = "variable reference", value = exp.name } - end - elseif exp.type == "implicit call if reference" then - local v, e = eval(state, exp.expression) - if not v then return nil, e end - if v.type == "function reference" or v.type == "variable reference" then - exp.variant.argument.expression.value = v - return eval(state, exp.variant) - else - return v - end - -- function - elseif exp.type == "function call" then - -- eval args: map brackets - local args = {} - local last_contiguous_positional = 0 - if exp.argument then - local arg, arge = eval(state, exp.argument) - if not arg then return nil, arge end - -- map into args table - for _, v in pairs(arg.value) do - if v[1].type == "string" or v[1].type == "number" then - args[v[1].value] = v[2] - else - return nil, ("unexpected key of type %s in argument map; keys must be string or number"):format(v[1].type) - end - end - -- get length of contiguous positional arguments (#args may not be always be equal depending on implementation...) - for i, _ in ipairs(args) do - last_contiguous_positional = i - end - end - -- function reference: call the referenced function - local variants = exp.variants - local paren_call = exp.paren_call - if args[1] and args[1].type == "function reference" and (exp.called_name == "()" or exp.called_name == "_!") then - -- remove func ref as first arg - local refv = args[1].value - table.remove(args, 1) - -- set paren_call for _! - if exp.called_name == "_!" then - paren_call = false - end - -- get variants of the referenced function - variants = {} - for _, ffqm in ipairs(refv) do - for _, variant in ipairs(state.functions[ffqm]) do - table.insert(variants, variant) - end - end - end - -- eval assignment arg - local assignment - if exp.assignment then - local arge - assignment, arge = eval(state, exp.assignment) - if not assignment then return nil, arge end - end - -- try to select a function - local tried_function_error_messages = {} - local selected_variant = { depths = { assignment = nil }, variant = nil, args_to_set = nil } - for _, fn in ipairs(variants) do - if fn.type ~= "function" then - return nil, ("unknown function type %q"):format(fn.type) - -- functions - else - if not fn.assignment or exp.assignment then - local ok = true - -- get and set args - local variant_args = {} - local used_args = {} - local depths = { assignment = nil } - for j, param in ipairs(fn.params) do - local val - -- named - if param.alias and args[param.alias] then - val = args[param.alias] - used_args[param.alias] = true - elseif args[param.name] then - val = args[param.name] - used_args[param.name] = true - -- vararg - elseif param.vararg then - val = { type = "list", value = {} } - for k=j, last_contiguous_positional do - table.insert(val.value, args[k]) - used_args[k] = true - end - -- positional - elseif args[j] then - val = args[j] - used_args[j] = true - end - if val then - -- check type constraint - local depth, err = check_constraint(state, param.full_name, val) - if not depth then - ok = false - local v = state.variable_metadata[param.full_name].constraint.value - table.insert(tried_function_error_messages, ("%s: argument %s is not of expected type %s"):format(fn.pretty_signature, param.name, format(v) or v)) - break - end - depths[j] = depth - -- set - variant_args[param.full_name] = val - -- default: evaluate once function is selected - -- there's no need to type check because the type constraint is already the default value's type, because of syntax - elseif param.default then - variant_args[param.full_name] = { type = "pending definition", value = { expression = param.default, source = fn.source } } - else - ok = false - table.insert(tried_function_error_messages, ("%s: missing mandatory argument %q in function %q call"):format(fn.pretty_signature, param.name, fn.name)) - break - end - end - -- check for unused arguments - if ok then - for key, arg in pairs(args) do - if not used_args[key] then - ok = false - if arg.type == "pair" and arg.value[1].type == "string" then - table.insert(tried_function_error_messages, ("%s: unexpected %s argument"):format(fn.pretty_signature, arg.value[1].value)) - else - table.insert(tried_function_error_messages, ("%s: unexpected argument in position %s"):format(fn.pretty_signature, i)) - end - break - end - end - end - -- assignment arg - if ok and exp.assignment then - -- check type constraint - local param = fn.assignment - local depth, err = check_constraint(state, param.full_name, assignment) - if not depth then - ok = false - local v = state.variable_metadata[param.full_name].constraint.value - table.insert(tried_function_error_messages, ("%s: argument %s is not of expected type %s"):format(fn.pretty_signature, param.name, format(v) or v)) - end - depths.assignment = depth - -- set - variant_args[param.full_name] = assignment - end - if ok then - if not selected_variant.variant then - selected_variant.depths = depths - selected_variant.variant = fn - selected_variant.args_to_set = variant_args - else - -- check specificity order - local lower - for j, d in ipairs(depths) do - local current_depth = selected_variant.depths[j] or math.huge -- not every arg may be set on every variant (varargs) - if d < current_depth then -- stricly lower, i.e. more specific function - lower = true - break - elseif d > current_depth then -- stricly greater, i.e. less specific function - lower = false - break - end - end - if lower == nil and exp.assignment then -- use assignment if still ambigous - local current_depth = selected_variant.depths.assignment - if depths.assignment < current_depth then -- stricly lower, i.e. more specific function - lower = true - elseif depths.assignment > current_depth then -- stricly greater, i.e. less specific function - lower = false - end - end - if lower then - selected_variant.depths = depths - selected_variant.variant = fn - selected_variant.args_to_set = variant_args - elseif lower == nil then -- equal, ambigous dispatch - return nil, ("function call %q is ambigous; may be at least either:\n\t%s\n\t%s"):format(exp.called_name, fn.pretty_signature, selected_variant.variant.pretty_signature) - end - end - end - end - end - end - -- function successfully selected: run - if selected_variant.variant then - local fn = selected_variant.variant - if fn.type ~= "function" then - return nil, ("unknown function type %q"):format(fn.type) - -- checkpoint: no args and can resume execution - elseif fn.subtype == "checkpoint" then - -- set current checkpoint - local s, e = set_variable(state, fn.parent_resumable.namespace.."🔖", { - type = "function reference", - value = { fn.name } - }) - if not s then return nil, e end - -- run checkpoint content, eventually resuming - local r, e = run(state, fn.child, not paren_call) - if not r then return nil, e end - return r - -- other functions - else - local ret - -- push scope - -- NOTE: if error happens between here and scope:pop, will leave the stack a mess - -- should not be an issue since an interpreter is supposed to be discarded after an error, but should change this if we ever - -- add some excepetion handling in anselme at some point - if fn.scoped then - scope:push(state, fn) - end - -- set arguments - for name, val in pairs(selected_variant.args_to_set) do - local s, e = set_variable(state, name, val) - if not s then return nil, e end - end - -- get function vars - local checkpoint, checkpointe - if fn.resumable then - checkpoint, checkpointe = get_variable(state, fn.namespace.."🔖") - if not checkpoint then return nil, checkpointe end - end - -- execute lua functions - -- I guess we could technically skip getting & updating the seen and checkpoints vars since they can't be used from Anselme - -- but it's also kinda fun to known how many time a function was ran - if fn.lua_function then - local lua_fn = fn.lua_function - -- get args - local final_args = {} - for j, param in ipairs(fn.params) do - local v, e = get_variable(state, param.full_name) - if not v then return nil, e end - final_args[j] = v - end - if fn.assignment then - local v, e = get_variable(state, fn.assignment.full_name) - if not v then return nil, e end - final_args[#final_args+1] = v - end - -- execute function - -- raw mode: pass raw anselme values to the Lua function; support return nil, err in case of error - if lua_fn.mode == "raw" then - local r, e = lua_fn.value(unpack(final_args)) - if r then - ret = r - else - return nil, ("%s; in Lua function %q"):format(e or "raw function returned nil and no error message", exp.called_name) - end - -- unannotated raw mode: same as raw, but strips custom annotations from the arguments - elseif lua_fn.mode == "unannotated raw" then - -- extract value from custom types - for i, arg in ipairs(final_args) do - if arg.type == "annotated" then - final_args[i] = arg.value[1] - end - end - local r, e = lua_fn.value(unpack(final_args)) - if r then - ret = r - else - return nil, ("%s; in Lua function %q"):format(e or "unannotated raw function returned nil and no error message", exp.called_name) - end - -- normal mode: convert args to Lua and convert back Lua value to Anselme - elseif lua_fn.mode == nil then - local l_lua = {} - for _, v in ipairs(final_args) do - local lv, e = to_lua(v, state) - if e then return nil, e end - table.insert(l_lua, lv) - end - local r, e - if _VERSION == "Lua 5.1" and not jit then -- PUC Lua 5.1 doesn't allow yield from a pcall - r, e = true, lua_fn.value(unpack(l_lua)) - else - r, e = pcall(lua_fn.value, unpack(l_lua)) -- pcall to produce a more informative error message (instead of full coroutine crash) - end - if r then - ret = from_lua(e) - else - return nil, ("%s; in Lua function %q"):format(e, exp.called_name) - end - else - return nil, ("unknown Lua function mode %q"):format(lua_fn.mode) - end - -- execute anselme functions - else - local e - -- eval function from start - if paren_call or not fn.resumable or checkpoint.type == "nil" then - ret, e = run(state, fn.child) - -- resume at last checkpoint - else - local expr, err = expression(checkpoint.value[1], state, fn.namespace, "resume from checkpoint") - if not expr then return nil, err end - ret, e = eval(state, expr) - end - if not ret then return nil, e end - end - -- for classes: build resulting object - if fn.subtype == "class" and ret and ret.type == "nil" then - ret = { - type = "annotated", - value = { - { - type = "object", - value = { - class = fn.name, - attributes = {} - } - }, - { - type = "function reference", - value = { fn.name } - } - } - } - end - -- pop scope - if fn.scoped then - scope:pop(state, fn) - end - -- return value - if not ret then return nil, ("function %q didn't return a value"):format(exp.called_name) end - return ret - end - end - -- no matching function found - local args_txt = {} - for key, arg in pairs(args) do - local s = "" - if type(key) == "string" or (type(key) == "number" and key > last_contiguous_positional) then - s = s .. ("%s="):format(key) - end - s = s .. pretty_type(arg) - table.insert(args_txt, s) - end - local called_name = ("%s(%s)"):format(exp.called_name, table.concat(args_txt, ", ")) - if assignment then - called_name = called_name .. " := " .. pretty_type(assignment) - end - return nil, ("no compatible function found for call to %s; potential candidates were:\n\t%s"):format(called_name, table.concat(tried_function_error_messages, "\n\t")) - -- event buffer (internal type, only issued from a text or choice line) - elseif exp.type == "text" then - local l = {} - events:push_buffer(state, l) - local current_tags = tags:current(state) - local v, e = eval_text_callback(state, exp.text, function(text) - events:append(state, "text", { text = text, tags = current_tags }) - end) - events:pop_buffer(state) - if not v then return nil, e end - return { - type = "event buffer", - value = l - } - elseif exp.type == "nonpersistent" then - local v, e = eval(state, exp.expression) - if not v then return nil, e end - v.nonpersistent = true - return v - -- pass the value along (internal type, used for variable reference implicit calls) - elseif exp.type == "value passthrough" then - return exp.value - else - return nil, ("unknown expression %q"):format(tostring(exp.type)) - end -end - -package.loaded[...] = eval -run = require((...):gsub("expression$", "interpreter")).run -expression = require((...):gsub("interpreter%.expression$", "parser.expression")) -flatten_list = require((...):gsub("interpreter%.expression$", "parser.common")).flatten_list -local common = require((...):gsub("expression$", "common")) -to_lua, from_lua, eval_text, truthy, format, pretty_type, get_variable, tags, eval_text_callback, events, set_variable, scope, check_constraint, hash = common.to_lua, common.from_lua, common.eval_text, common.truthy, common.format, common.pretty_type, common.get_variable, common.tags, common.eval_text_callback, common.events, common.set_variable, common.scope, common.check_constraint, common.hash - -return eval diff --git a/interpreter/interpreter.lua b/interpreter/interpreter.lua deleted file mode 100644 index 6f7013e..0000000 --- a/interpreter/interpreter.lua +++ /dev/null @@ -1,234 +0,0 @@ -local eval -local truthy, merge_state, escape, get_variable, tags, events, set_variable -local run_line, run_block - --- returns var in case of success and there is a return --- return nil in case of success and there is no return --- return nil, err in case of error -run_line = function(state, line) - -- store line - state.interpreter.running_line = line - -- line types - if line.type == "condition" then - line.parent_block.last_condition_success = nil - local v, e = eval(state, line.expression) - if not v then return nil, ("%s; at %s"):format(e, line.source) end - if truthy(v) then - line.parent_block.last_condition_success = true - v, e = run_block(state, line.child) - if e then return nil, e end - if v then return v end - end - elseif line.type == "else-condition" then - if not line.parent_block.last_condition_success then - local v, e = eval(state, line.expression) - if not v then return nil, ("%s; at %s"):format(e, line.source) end - if truthy(v) then - line.parent_block.last_condition_success = true - v, e = run_block(state, line.child) - if e then return nil, e end - if v then return v end - end - end - elseif line.type == "while" then - line.parent_block.last_condition_success = nil - local v, e = eval(state, line.expression) - if not v then return nil, ("%s; at %s"):format(e, line.source) end - while truthy(v) do - line.parent_block.last_condition_success = true - v, e = run_block(state, line.child) - if e then return nil, e end - if v then return v end - -- next iteration - v, e = eval(state, line.expression) - if not v then return nil, ("%s; at %s"):format(e, line.source) end - end - elseif line.type == "choice" then - local v, e = events:make_space_for(state, "choice") - if not v then return nil, ("%s; in automatic event flush at %s"):format(e, line.source) end - v, e = eval(state, line.text) - if not v then return nil, ("%s; at %s"):format(e, line.source) end - local l = v.type == "list" and v.value or { v } - -- convert text events to choices - for _, item in ipairs(l) do - if item.type == "event buffer" then - local current_tags = tags:current(state) - local choice_block_state = { tags = current_tags, block = line.child } - local final_buffer = {} - for _, event in ipairs(item.value) do - if event.type == "text" then - -- create new choice block if needed - local last_choice_block = final_buffer[#final_buffer] - if not last_choice_block or last_choice_block.type ~= "choice" then - last_choice_block = { type = "choice", value = {} } - table.insert(final_buffer, last_choice_block) - end - -- create new choice item in choice block if needed - local last_choice = last_choice_block.value[#last_choice_block.value] - if not last_choice then - last_choice = { _state = choice_block_state } - table.insert(last_choice_block.value, last_choice) - end - -- add text to last choice item - for _, txt in ipairs(event.value) do - table.insert(last_choice, txt) - end - else - table.insert(final_buffer, event) - end - end - local iv, ie = events:write_buffer(state, final_buffer) - if not iv then return nil, ("%s; at %s"):format(ie, line.source) end - end - end - elseif line.type == "tag" then - local v, e = eval(state, line.expression) - if not v then return nil, ("%s; at %s"):format(e, line.source) end - tags:push(state, v) - v, e = run_block(state, line.child) - tags:pop(state) - if e then return nil, e end - if v then return v end - elseif line.type == "return" then - local v, e = eval(state, line.expression) - if not v then return nil, ("%s; at %s"):format(e, line.source) end - local cv, ce = run_block(state, line.child) - if ce then return nil, ce end - if cv then return cv end - return v - elseif line.type == "text" then - local v, e = events:make_space_for(state, "text") -- do this before any evaluation start - if not v then return nil, ("%s; in automatic event flush at %s"):format(e, line.source) end - v, e = eval(state, line.text) - if not v then return nil, ("%s; at %s"):format(e, line.source) end - local l = v.type == "list" and v.value or { v } - for _, item in ipairs(l) do - if item.type == "event buffer" then - local iv, ie = events:write_buffer(state, item.value) - if not iv then return nil, ("%s; at %s"):format(ie, line.source) end - end - end - elseif line.type == "flush events" then - local v, e = events:flush(state) - if not v then return nil, ("%s; in event flush at %s"):format(e, line.source) end - elseif line.type == "function" and line.subtype == "checkpoint" then - local reached, reachede = get_variable(state, line.namespace.."🏁") - if not reached then return nil, reachede end - local s, e = set_variable(state, line.namespace.."🏁", { - type = "number", - value = reached.value + 1 - }) - if not s then return nil, e end - s, e = set_variable(state, line.parent_resumable.namespace.."🔖", { - type = "function reference", - value = { line.name } - }) - if not s then return nil, e end - merge_state(state) - else - return nil, ("unknown line type %q; at %s"):format(line.type, line.source) - end -end - --- returns var in case of success and there is a return --- return nil in case of success and there is no return --- return nil, err in case of error -run_block = function(state, block, resume_from_there, i, j) - i = i or 1 - local max = math.min(#block, j or math.huge) - while i <= max do - local line = block[i] - local skip = false - -- skip current choice block if enabled - if state.interpreter.skip_choices_until_flush and line.type == "choice" then - skip = true - end - -- run line - if not skip then - local v, e = run_line(state, line) - if e then return nil, e end - if v then return v end - end - i = i + 1 - end - -- if we reach the end of a checkpoint block (we are resuming execution from a checkpoint), merge state - if block.parent_line and block.parent_line.type == "function" and block.parent_line.subtype == "checkpoint" then - merge_state(state) - end - -- go up hierarchy if asked to resume - -- will stop at resumable function boundary - -- if parent is a choice, will ignore choices that belong to the same block (like the whole block was executed naturally from a higher parent) - -- if parent if a condition, will mark it as a success (skipping following else-conditions) (for the same reasons as for choices) - -- if parent pushed a tag, will pop it (tags from parents are added to the stack in run()) - if resume_from_there and block.parent_line and not block.parent_line.resumable then - local parent_line = block.parent_line - if parent_line.type == "choice" then - state.interpreter.skip_choices_until_flush = true - elseif parent_line.type == "condition" or parent_line.type == "else-condition" then - parent_line.parent_block.last_condition_success = true - end - if parent_line.type == "tag" then - tags:pop(state) - end - local v, e = run_block(state, parent_line.parent_block, resume_from_there, parent_line.parent_position+1) - if e then return nil, e end - if v then return v end - end -end - --- returns var in case of success --- return nil, err in case of error -local function run(state, block, resume_from_there, i, j) - -- restore tags from parents when resuming - local tags_len = tags:len(state) - if resume_from_there then - local tags_to_add = {} - -- go up in hierarchy in ascending order until resumable function boundary - local parent_line = block.parent_line - while parent_line and not parent_line.resumable do - if parent_line.type == "tag" then - local v, e = eval(state, parent_line.expression) - if not v then return nil, ("%s; at %s"):format(e, parent_line.source) end - table.insert(tags_to_add, v) - end - parent_line = parent_line.parent_block.parent_line - end - -- re-add tag in desceding order - for k=#tags_to_add, 1, -1 do - tags:push(state, tags_to_add[k]) - end - end - -- run - local v, e = run_block(state, block, resume_from_there, i, j) - -- return to previous tag state - -- when resuming is done, tag stack pop when exiting the tag block - -- stray elements may be left on the stack if there is a return before we go up all the tag blocks, so we trim them - if resume_from_there then - tags:trim(state, tags_len) - end - -- return - if e then return nil, e end - if v then - return v - else - -- default no return value - return { - type = "nil", - value = nil - } - end -end - -local interpreter = { - run = run, - run_block = run_block, - run_line = run_line -} - -package.loaded[...] = interpreter -eval = require((...):gsub("interpreter$", "expression")) -local common = require((...):gsub("interpreter$", "common")) -truthy, merge_state, tags, get_variable, events, set_variable = common.truthy, common.merge_state, common.tags, common.get_variable, common.events, common.set_variable -escape = require((...):gsub("interpreter%.interpreter$", "parser.common")).escape - -return interpreter diff --git a/lib/ansicolors.lua b/lib/ansicolors.lua new file mode 100644 index 0000000..f474952 --- /dev/null +++ b/lib/ansicolors.lua @@ -0,0 +1,100 @@ +-- ansicolors.lua v1.0.2 (2012-08) + +-- Copyright (c) 2009 Rob Hoelz +-- Copyright (c) 2011 Enrique García Cota +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. + + +-- support detection +local function isWindows() + return type(package) == 'table' and type(package.config) == 'string' and package.config:sub(1,1) == '\\' +end + +local supported = not isWindows() +if isWindows() then supported = os.getenv("ANSICON") end + +local keys = { + -- reset + reset = 0, + + -- misc + bright = 1, + dim = 2, + underline = 4, + blink = 5, + reverse = 7, + hidden = 8, + + -- foreground colors + black = 30, + red = 31, + green = 32, + yellow = 33, + blue = 34, + magenta = 35, + cyan = 36, + white = 37, + + -- background colors + blackbg = 40, + redbg = 41, + greenbg = 42, + yellowbg = 43, + bluebg = 44, + magentabg = 45, + cyanbg = 46, + whitebg = 47 +} + +local escapeString = string.char(27) .. '[%dm' +local function escapeNumber(number) + return escapeString:format(number) +end + +local function escapeKeys(str) + + if not supported then return "" end + + local buffer = {} + local number + for word in str:gmatch("%w+") do + number = keys[word] + assert(number, "Unknown key: " .. word) + table.insert(buffer, escapeNumber(number) ) + end + + return table.concat(buffer) +end + +local function replaceCodes(str) + str = string.gsub(str,"(%%{(.-)})", function(_, str) return escapeKeys(str) end ) + return str +end + +-- public + +local function ansicolors( str ) + str = tostring(str or '') + + return replaceCodes('%{reset}' .. str .. '%{reset}') +end + + +return setmetatable({noReset = replaceCodes}, {__call = function (_, str) return ansicolors (str) end}) diff --git a/lib/binser.lua b/lib/binser.lua new file mode 100644 index 0000000..a427398 --- /dev/null +++ b/lib/binser.lua @@ -0,0 +1,753 @@ +-- binser.lua + +--[[ +Copyright (c) 2016-2019 Calvin Rose + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local assert = assert +local error = error +local select = select +local pairs = pairs +local getmetatable = getmetatable +local setmetatable = setmetatable +local type = type +local loadstring = loadstring or load +local concat = table.concat +local char = string.char +local byte = string.byte +local format = string.format +local sub = string.sub +local dump = string.dump +local floor = math.floor +local frexp = math.frexp +local unpack = unpack or table.unpack +local huge = math.huge + +-- Lua 5.3 frexp polyfill +-- From https://github.com/excessive/cpml/blob/master/modules/utils.lua +if not frexp then + local log, abs, floor = math.log, math.abs, math.floor + local log2 = log(2) + frexp = function(x) + if x == 0 then return 0, 0 end + local e = floor(log(abs(x)) / log2 + 1) + return x / 2 ^ e, e + end +end + +local function pack(...) + return {...}, select("#", ...) +end + +local function not_array_index(x, len) + return type(x) ~= "number" or x < 1 or x > len or x ~= floor(x) +end + +local function type_check(x, tp, name) + assert(type(x) == tp, + format("Expected parameter %q to be of type %q.", name, tp)) +end + +local bigIntSupport = false +local isInteger +if math.type then -- Detect Lua 5.3 + local mtype = math.type + bigIntSupport = loadstring[[ + local char = string.char + return function(n) + local nn = n < 0 and -(n + 1) or n + local b1 = nn // 0x100000000000000 + local b2 = nn // 0x1000000000000 % 0x100 + local b3 = nn // 0x10000000000 % 0x100 + local b4 = nn // 0x100000000 % 0x100 + local b5 = nn // 0x1000000 % 0x100 + local b6 = nn // 0x10000 % 0x100 + local b7 = nn // 0x100 % 0x100 + local b8 = nn % 0x100 + if n < 0 then + b1, b2, b3, b4 = 0xFF - b1, 0xFF - b2, 0xFF - b3, 0xFF - b4 + b5, b6, b7, b8 = 0xFF - b5, 0xFF - b6, 0xFF - b7, 0xFF - b8 + end + return char(212, b1, b2, b3, b4, b5, b6, b7, b8) + end]]() + isInteger = function(x) + return mtype(x) == 'integer' + end +else + isInteger = function(x) + return floor(x) == x + end +end + +-- Copyright (C) 2012-2015 Francois Perrad. +-- number serialization code modified from https://github.com/fperrad/lua-MessagePack +-- Encode a number as a big-endian ieee-754 double, big-endian signed 64 bit integer, or a small integer +local function number_to_str(n) + if isInteger(n) then -- int + if n <= 100 and n >= -27 then -- 1 byte, 7 bits of data + return char(n + 27) + elseif n <= 8191 and n >= -8192 then -- 2 bytes, 14 bits of data + n = n + 8192 + return char(128 + (floor(n / 0x100) % 0x100), n % 0x100) + elseif bigIntSupport then + return bigIntSupport(n) + end + end + local sign = 0 + if n < 0.0 then + sign = 0x80 + n = -n + end + local m, e = frexp(n) -- mantissa, exponent + if m ~= m then + return char(203, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + elseif m == huge then + if sign == 0 then + return char(203, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + else + return char(203, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + end + elseif m == 0.0 and e == 0 then + return char(0xCB, sign, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + end + e = e + 0x3FE + if e < 1 then -- denormalized numbers + m = m * 2 ^ (52 + e) + e = 0 + else + m = (m * 2 - 1) * 2 ^ 52 + end + return char(203, + sign + floor(e / 0x10), + (e % 0x10) * 0x10 + floor(m / 0x1000000000000), + floor(m / 0x10000000000) % 0x100, + floor(m / 0x100000000) % 0x100, + floor(m / 0x1000000) % 0x100, + floor(m / 0x10000) % 0x100, + floor(m / 0x100) % 0x100, + m % 0x100) +end + +-- Copyright (C) 2012-2015 Francois Perrad. +-- number deserialization code also modified from https://github.com/fperrad/lua-MessagePack +local function number_from_str(str, index) + local b = byte(str, index) + if not b then error("Expected more bytes of input.") end + if b < 128 then + return b - 27, index + 1 + elseif b < 192 then + local b2 = byte(str, index + 1) + if not b2 then error("Expected more bytes of input.") end + return b2 + 0x100 * (b - 128) - 8192, index + 2 + end + local b1, b2, b3, b4, b5, b6, b7, b8 = byte(str, index + 1, index + 8) + if (not b1) or (not b2) or (not b3) or (not b4) or + (not b5) or (not b6) or (not b7) or (not b8) then + error("Expected more bytes of input.") + end + if b == 212 then + local flip = b1 >= 128 + if flip then -- negative + b1, b2, b3, b4 = 0xFF - b1, 0xFF - b2, 0xFF - b3, 0xFF - b4 + b5, b6, b7, b8 = 0xFF - b5, 0xFF - b6, 0xFF - b7, 0xFF - b8 + end + local n = ((((((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) * + 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8 + if flip then + return (-n) - 1, index + 9 + else + return n, index + 9 + end + end + if b ~= 203 then + error("Expected number") + end + local sign = b1 > 0x7F and -1 or 1 + local e = (b1 % 0x80) * 0x10 + floor(b2 / 0x10) + local m = ((((((b2 % 0x10) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8 + local n + if e == 0 then + if m == 0 then + n = sign * 0.0 + else + n = sign * (m / 2 ^ 52) * 2 ^ -1022 + end + elseif e == 0x7FF then + if m == 0 then + n = sign * huge + else + n = 0 + end + else + n = sign * (1.0 + m / 2 ^ 52) * 2 ^ (e - 0x3FF) + end + return n, index + 9 +end + + +local function newbinser() + + -- unique table key for getting next value + local NEXT = {} + local CTORSTACK = {} + + -- NIL = 202 + -- FLOAT = 203 + -- TRUE = 204 + -- FALSE = 205 + -- STRING = 206 + -- TABLE = 207 + -- REFERENCE = 208 + -- CONSTRUCTOR = 209 + -- FUNCTION = 210 + -- RESOURCE = 211 + -- INT64 = 212 + -- TABLE WITH META = 213 + + local mts = {} + local ids = {} + local serializers = {} + local deserializers = {} + local resources = {} + local resources_by_name = {} + local types = {} + + types["nil"] = function(x, visited, accum) + accum[#accum + 1] = "\202" + end + + function types.number(x, visited, accum) + accum[#accum + 1] = number_to_str(x) + end + + function types.boolean(x, visited, accum) + accum[#accum + 1] = x and "\204" or "\205" + end + + function types.string(x, visited, accum) + local alen = #accum + if visited[x] then + accum[alen + 1] = "\208" + accum[alen + 2] = number_to_str(visited[x]) + else + visited[x] = visited[NEXT] + visited[NEXT] = visited[NEXT] + 1 + accum[alen + 1] = "\206" + accum[alen + 2] = number_to_str(#x) + accum[alen + 3] = x + end + end + + local function check_custom_type(x, visited, accum) + local res = resources[x] + if res then + accum[#accum + 1] = "\211" + types[type(res)](res, visited, accum) + return true + end + local mt = getmetatable(x) + local id = mt and ids[mt] + if id then + local constructing = visited[CTORSTACK] + if constructing[x] then + error("Infinite loop in constructor.") + end + constructing[x] = true + accum[#accum + 1] = "\209" + types[type(id)](id, visited, accum) + local args, len = pack(serializers[id](x)) + accum[#accum + 1] = number_to_str(len) + for i = 1, len do + local arg = args[i] + types[type(arg)](arg, visited, accum) + end + visited[x] = visited[NEXT] + visited[NEXT] = visited[NEXT] + 1 + -- We finished constructing + constructing[x] = nil + return true + end + end + + function types.userdata(x, visited, accum) + if visited[x] then + accum[#accum + 1] = "\208" + accum[#accum + 1] = number_to_str(visited[x]) + else + if check_custom_type(x, visited, accum) then return end + error("Cannot serialize this userdata.") + end + end + + function types.table(x, visited, accum) + if visited[x] then + accum[#accum + 1] = "\208" + accum[#accum + 1] = number_to_str(visited[x]) + else + if check_custom_type(x, visited, accum) then return end + visited[x] = visited[NEXT] + visited[NEXT] = visited[NEXT] + 1 + local xlen = #x + local mt = getmetatable(x) + if mt then + accum[#accum + 1] = "\213" + types.table(mt, visited, accum) + else + accum[#accum + 1] = "\207" + end + accum[#accum + 1] = number_to_str(xlen) + for i = 1, xlen do + local v = x[i] + types[type(v)](v, visited, accum) + end + local key_count = 0 + for k in pairs(x) do + if not_array_index(k, xlen) then + key_count = key_count + 1 + end + end + accum[#accum + 1] = number_to_str(key_count) + for k, v in pairs(x) do + if not_array_index(k, xlen) then + types[type(k)](k, visited, accum) + types[type(v)](v, visited, accum) + end + end + end + end + + types["function"] = function(x, visited, accum) + if visited[x] then + accum[#accum + 1] = "\208" + accum[#accum + 1] = number_to_str(visited[x]) + else + if check_custom_type(x, visited, accum) then return end + visited[x] = visited[NEXT] + visited[NEXT] = visited[NEXT] + 1 + local str = dump(x) + accum[#accum + 1] = "\210" + accum[#accum + 1] = number_to_str(#str) + accum[#accum + 1] = str + end + end + + types.cdata = function(x, visited, accum) + if visited[x] then + accum[#accum + 1] = "\208" + accum[#accum + 1] = number_to_str(visited[x]) + else + if check_custom_type(x, visited, #accum) then return end + error("Cannot serialize this cdata.") + end + end + + types.thread = function() error("Cannot serialize threads.") end + + local function deserialize_value(str, index, visited) + local t = byte(str, index) + if not t then return nil, index end + if t < 128 then + return t - 27, index + 1 + elseif t < 192 then + local b2 = byte(str, index + 1) + if not b2 then error("Expected more bytes of input.") end + return b2 + 0x100 * (t - 128) - 8192, index + 2 + elseif t == 202 then + return nil, index + 1 + elseif t == 203 or t == 212 then + return number_from_str(str, index) + elseif t == 204 then + return true, index + 1 + elseif t == 205 then + return false, index + 1 + elseif t == 206 then + local length, dataindex = number_from_str(str, index + 1) + local nextindex = dataindex + length + if not (length >= 0) then error("Bad string length") end + if #str < nextindex - 1 then error("Expected more bytes of string") end + local substr = sub(str, dataindex, nextindex - 1) + visited[#visited + 1] = substr + return substr, nextindex + elseif t == 207 or t == 213 then + local mt, count, nextindex + local ret = {} + visited[#visited + 1] = ret + nextindex = index + 1 + if t == 213 then + mt, nextindex = deserialize_value(str, nextindex, visited) + if type(mt) ~= "table" then error("Expected table metatable") end + end + count, nextindex = number_from_str(str, nextindex) + for i = 1, count do + local oldindex = nextindex + ret[i], nextindex = deserialize_value(str, nextindex, visited) + if nextindex == oldindex then error("Expected more bytes of input.") end + end + count, nextindex = number_from_str(str, nextindex) + for i = 1, count do + local k, v + local oldindex = nextindex + k, nextindex = deserialize_value(str, nextindex, visited) + if nextindex == oldindex then error("Expected more bytes of input.") end + oldindex = nextindex + v, nextindex = deserialize_value(str, nextindex, visited) + if nextindex == oldindex then error("Expected more bytes of input.") end + if k == nil then error("Can't have nil table keys") end + ret[k] = v + end + if mt then setmetatable(ret, mt) end + return ret, nextindex + elseif t == 208 then + local ref, nextindex = number_from_str(str, index + 1) + return visited[ref], nextindex + elseif t == 209 then + local count + local name, nextindex = deserialize_value(str, index + 1, visited) + count, nextindex = number_from_str(str, nextindex) + local args = {} + for i = 1, count do + local oldindex = nextindex + args[i], nextindex = deserialize_value(str, nextindex, visited) + if nextindex == oldindex then error("Expected more bytes of input.") end + end + if not name or not deserializers[name] then + error(("Cannot deserialize class '%s'"):format(tostring(name))) + end + local ret = deserializers[name](unpack(args)) + visited[#visited + 1] = ret + return ret, nextindex + elseif t == 210 then + local length, dataindex = number_from_str(str, index + 1) + local nextindex = dataindex + length + if not (length >= 0) then error("Bad string length") end + if #str < nextindex - 1 then error("Expected more bytes of string") end + local ret = loadstring(sub(str, dataindex, nextindex - 1)) + visited[#visited + 1] = ret + return ret, nextindex + elseif t == 211 then + local resname, nextindex = deserialize_value(str, index + 1, visited) + if resname == nil then error("Got nil resource name") end + local res = resources_by_name[resname] + if res == nil then + error(("No resources found for name '%s'"):format(tostring(resname))) + end + return res, nextindex + else + error("Could not deserialize type byte " .. t .. ".") + end + end + + local function serialize(...) + local visited = {[NEXT] = 1, [CTORSTACK] = {}} + local accum = {} + for i = 1, select("#", ...) do + local x = select(i, ...) + types[type(x)](x, visited, accum) + end + return concat(accum) + end + + local function make_file_writer(file) + return setmetatable({}, { + __newindex = function(_, _, v) + file:write(v) + end + }) + end + + local function serialize_to_file(path, mode, ...) + local file, err = io.open(path, mode) + assert(file, err) + local visited = {[NEXT] = 1, [CTORSTACK] = {}} + local accum = make_file_writer(file) + for i = 1, select("#", ...) do + local x = select(i, ...) + types[type(x)](x, visited, accum) + end + -- flush the writer + file:flush() + file:close() + end + + local function writeFile(path, ...) + return serialize_to_file(path, "wb", ...) + end + + local function appendFile(path, ...) + return serialize_to_file(path, "ab", ...) + end + + local function deserialize(str, index) + assert(type(str) == "string", "Expected string to deserialize.") + local vals = {} + index = index or 1 + local visited = {} + local len = 0 + local val + while true do + local nextindex + val, nextindex = deserialize_value(str, index, visited) + if nextindex > index then + len = len + 1 + vals[len] = val + index = nextindex + else + break + end + end + return vals, len + end + + local function deserializeN(str, n, index) + assert(type(str) == "string", "Expected string to deserialize.") + n = n or 1 + assert(type(n) == "number", "Expected a number for parameter n.") + assert(n > 0 and floor(n) == n, "N must be a poitive integer.") + local vals = {} + index = index or 1 + local visited = {} + local len = 0 + local val + while len < n do + local nextindex + val, nextindex = deserialize_value(str, index, visited) + if nextindex > index then + len = len + 1 + vals[len] = val + index = nextindex + else + break + end + end + vals[len + 1] = index + return unpack(vals, 1, n + 1) + end + + local function readFile(path) + local file, err = io.open(path, "rb") + assert(file, err) + local str = file:read("*all") + file:close() + return deserialize(str) + end + + -- Resources + + local function registerResource(resource, name) + type_check(name, "string", "name") + assert(not resources[resource], + "Resource already registered.") + assert(not resources_by_name[name], + format("Resource %q already exists.", name)) + resources_by_name[name] = resource + resources[resource] = name + return resource + end + + local function unregisterResource(name) + type_check(name, "string", "name") + assert(resources_by_name[name], format("Resource %q does not exist.", name)) + local resource = resources_by_name[name] + resources_by_name[name] = nil + resources[resource] = nil + return resource + end + + -- Templating + + local function normalize_template(template) + local ret = {} + for i = 1, #template do + ret[i] = template[i] + end + local non_array_part = {} + -- The non-array part of the template (nested templates) have to be deterministic, so they are sorted. + -- This means that inherently non deterministicly sortable keys (tables, functions) should NOT be used + -- in templates. Looking for way around this. + for k in pairs(template) do + if not_array_index(k, #template) then + non_array_part[#non_array_part + 1] = k + end + end + table.sort(non_array_part) + for i = 1, #non_array_part do + local name = non_array_part[i] + ret[#ret + 1] = {name, normalize_template(template[name])} + end + return ret + end + + local function templatepart_serialize(part, argaccum, x, len) + local extras = {} + local extracount = 0 + for k, v in pairs(x) do + extras[k] = v + extracount = extracount + 1 + end + for i = 1, #part do + local name + if type(part[i]) == "table" then + name = part[i][1] + len = templatepart_serialize(part[i][2], argaccum, x[name], len) + else + name = part[i] + len = len + 1 + argaccum[len] = x[part[i]] + end + if extras[name] ~= nil then + extracount = extracount - 1 + extras[name] = nil + end + end + if extracount > 0 then + argaccum[len + 1] = extras + else + argaccum[len + 1] = nil + end + return len + 1 + end + + local function templatepart_deserialize(ret, part, values, vindex) + for i = 1, #part do + local name = part[i] + if type(name) == "table" then + local newret = {} + ret[name[1]] = newret + vindex = templatepart_deserialize(newret, name[2], values, vindex) + else + ret[name] = values[vindex] + vindex = vindex + 1 + end + end + local extras = values[vindex] + if extras then + for k, v in pairs(extras) do + ret[k] = v + end + end + return vindex + 1 + end + + local function template_serializer_and_deserializer(metatable, template) + return function(x) + local argaccum = {} + local len = templatepart_serialize(template, argaccum, x, 0) + return unpack(argaccum, 1, len) + end, function(...) + local ret = {} + local args = {...} + templatepart_deserialize(ret, template, args, 1) + return setmetatable(ret, metatable) + end + end + + -- Used to serialize classes withh custom serializers and deserializers. + -- If no _serialize or _deserialize (or no _template) value is found in the + -- metatable, then the metatable is registered as a resources. + local function register(metatable, name, serialize, deserialize) + if type(metatable) == "table" then + name = name or metatable.name + serialize = serialize or metatable._serialize + deserialize = deserialize or metatable._deserialize + if (not serialize) or (not deserialize) then + if metatable._template then + -- Register as template + local t = normalize_template(metatable._template) + serialize, deserialize = template_serializer_and_deserializer(metatable, t) + else + -- Register the metatable as a resource. This is semantically + -- similar and more flexible (handles cycles). + registerResource(metatable, name) + return + end + end + elseif type(metatable) == "string" then + name = name or metatable + end + type_check(name, "string", "name") + type_check(serialize, "function", "serialize") + type_check(deserialize, "function", "deserialize") + assert((not ids[metatable]) and (not resources[metatable]), + "Metatable already registered.") + assert((not mts[name]) and (not resources_by_name[name]), + ("Name %q already registered."):format(name)) + mts[name] = metatable + ids[metatable] = name + serializers[name] = serialize + deserializers[name] = deserialize + return metatable + end + + local function unregister(item) + local name, metatable + if type(item) == "string" then -- assume name + name, metatable = item, mts[item] + else -- assume metatable + name, metatable = ids[item], item + end + type_check(name, "string", "name") + mts[name] = nil + if (metatable) then + resources[metatable] = nil + ids[metatable] = nil + end + serializers[name] = nil + deserializers[name] = nil + resources_by_name[name] = nil; + return metatable + end + + local function registerClass(class, name) + name = name or class.name + if class.__instanceDict then -- middleclass + register(class.__instanceDict, name) + else -- assume 30log or similar library + register(class, name) + end + return class + end + + return { + VERSION = "0.0-8", + -- aliases + s = serialize, + d = deserialize, + dn = deserializeN, + r = readFile, + w = writeFile, + a = appendFile, + + serialize = serialize, + deserialize = deserialize, + deserializeN = deserializeN, + readFile = readFile, + writeFile = writeFile, + appendFile = appendFile, + register = register, + unregister = unregister, + registerResource = registerResource, + unregisterResource = unregisterResource, + registerClass = registerClass, + + newbinser = newbinser + } +end + +return newbinser() diff --git a/notes.txt b/notes.txt deleted file mode 100644 index 71006ac..0000000 --- a/notes.txt +++ /dev/null @@ -1,109 +0,0 @@ -# Symbol selection - -Anselme favor symbols over keywords, as it make translation easier. - -We prefer to use symbols available on a standard US keyboard as it often is the lowest common denominator. - -As we want to be able to write identifiers with little restriction, we try to only use symbols which are unlikely to appear naturally in a name. - -Considering Anselme is aimed to people with a light programming introduction, are safe to use for syntax purposes: - -* Diacritics (should be safe when used on their own): ~`^ -* Usual mathematical symbols (should be safe to use): +-=<>/ -* Unusual punctuation / main use is already programming (should be safe to use): []*{}|\_ -* Usual punctuation used to separate parts of a sentence (should be safe to use): !?.,;:() -* Signs (could be used in a name, but also common programming symbols): @&$#% -* Usual punctuation (could be used in a name): '" - -In the end, we decided to reserve all of those except '. - -Using other unicode symbols may be also alright, but there also should be a way to only use these symbols. - -Reserved symbols that are still not used in expressions: `\_?@$ - -Reserved symbols that are still not used as a line type: `^+-= meh, this means we can capture choice events, and choice events contain their code block, and we don't want to store code blocks in the save file (as code can be updated/translated/whatever) - ignoring choice events, we might be able to use subtexts in string litterals; using the same code for text lines and text litterals? we would lose tags... - -> no. would break ability to switch between two translations of the script as the save would contain the text events from the previous language. - -TODO: simplify language, it is much too complicated. Less line types? (var def, func, checkpoint, tag). Rewrite some ad hoc syntax using the expression system? - -TODO: fn/checkpoint/tag: maybe consider them a regular func call that takes children as arg; can keep compatibility using $/§ as shortcut for the actual call. - would allow more flexibility esp. for tags... - a func def would be: - - :a = $ - stuff - - but then args? - - :a = $(args) - stuff - - how are children passed on? overloading? -> means a code block would be passed as a value, how to avoid it ending up in the save file? - - if not possible, at least make checkpoint or fn defined using the other or some superset... -> checkpoints defined using fn - - OK for tag though: pushtag/poptag fn: - - # color:red - a - - translate to something like - - ~ tag.push(color:red) - a - ~ tag.pop() - -TODO: make language simple enough to be able to reimplement it in, say, nim. Especially the AST interpreter (we could precompile a lot of stuff...) - -TODO: test reacheability of script paths + visualization of different branches the script can take. For one of those overarching story visualization thingy. - -TODO: redisign the checkpoint system to work better when used with parallel scripts (if both change the same variable, will be overwritten); not sure how to do that, would need some complicated conflict resolution code or something like CRDT... - -TODO: redisign a static type checking system -If we want to go full gradual typing, it would help to: -* add type anotation+type check to functions return ($ f()::number) -> there's a lot of function calls, so probably checked at compiling only -* enforce some restrictions on type (assume they're constant/sublanguage, not full expressions) -* make some tuple/list distinction (homogenous/heterogenous types) as right now index operations are a type roulette. Or type annotate lists using some parametric type. -Advantages: -* can be used for better static variant selection; if everything is type annotated, selection could be restricted to a single function -Disadvantages: -* idk if it's worth the trouble -* could do something like `$ ()(l::list(?), i::number)::?`, but then can't return nil on not found... - -TODO: write a translation guide/simplify translation process - -TODO: make injection nicer. Some decorator-like syntax? to select specific functions to inject to - -TODO: allow multiple aliases for a single identifier? - -TODO: closures. Especially for when returning a subfunction from a scoped variable. diff --git a/parser/Source.lua b/parser/Source.lua new file mode 100644 index 0000000..b74df20 --- /dev/null +++ b/parser/Source.lua @@ -0,0 +1,38 @@ +local class = require("class") + +local Source +Source = class { + name = "?", + line = -1, + position = -1, + + init = function(self, name, line, position) + self.name = name + self.line = line + self.position = position + end, + increment = function(self, n, ...) + self.position = self.position + n + end, + count = function(self, capture, ...) + self:increment(utf8.len(capture)) + return capture, ... + end, + consume = function(self, capture, ...) + self:increment(utf8.len(capture)) + return ... + end, + + clone = function(self) + return Source:new(self.name, self.line, self.position) + end, + set = function(self, other) + self.name, self.line, self.position = other.name, other.line, other.position + end, + + __tostring = function(self) + return ("%s:%s:%s"):format(self.name, self.line, self.position) + end +} + +return Source diff --git a/parser/code_to_tree.lua b/parser/code_to_tree.lua new file mode 100644 index 0000000..ad220ba --- /dev/null +++ b/parser/code_to_tree.lua @@ -0,0 +1,65 @@ +--- transform raw code string into a nested tree of lines + +local Source = require("parser.Source") + +local function indented_to_tree(indented) + local tree = {} + local current_parent = tree + local current_level = 0 + local last_line_empty = nil + + for _, l in ipairs(indented) do + -- indentation of empty line is determined using the next line + -- (consecutive empty lines are merged into one) + if l.content == "" then + last_line_empty = l + else + -- raise indentation + if l.level > current_level then + if #current_parent == 0 then -- can't add children to nil + error(("invalid indentation; at %s"):format(l.source)) + end + current_parent = current_parent[#current_parent] + current_level = l.level + -- lower indentation + elseif l.level < current_level then + current_parent = tree + current_level = 0 + while current_level < l.level do -- find correct level starting back from the root + current_parent = current_parent[#current_parent] + current_level = current_parent[1].level + end + if current_level ~= l.level then + error(("invalid indentation; at %s"):format(l.source)) + end + end + -- add line + if last_line_empty then + last_line_empty.level = current_level + table.insert(current_parent, last_line_empty) + last_line_empty = nil + end + table.insert(current_parent, l) + end + end + + return tree +end + +local function code_to_indented(code, source_name) + local indented = {} + + local i = 1 + for line in (code.."\n"):gmatch("(.-)\n") do + local indent, rem = line:match("^(%s*)(.-)$") + local indent_len = utf8.len(indent) + table.insert(indented, { level = indent_len, content = rem, source = Source:new(source_name, i, 1+indent_len) }) + i = i + 1 + end + + return indented +end + +return function(code, source_name) + return indented_to_tree(code_to_indented(code, source_name or "?")) +end diff --git a/parser/common.lua b/parser/common.lua deleted file mode 100644 index c424575..0000000 --- a/parser/common.lua +++ /dev/null @@ -1,342 +0,0 @@ -local expression - -local escapeCache = {} - -local common - ---- rewrite name to use defined aliases (under namespace only) --- namespace should not contain aliases --- returns the final fqm -local replace_aliases = function(aliases, namespace, name) - local name_list = common.split(name) - local prefix = namespace - for i=1, #name_list, 1 do -- search alias for each part of the fqm - local n = ("%s%s%s"):format(prefix, prefix == "" and "" or ".", name_list[i]) - if aliases[n] then - prefix = aliases[n] - else - prefix = n - end - end - return prefix -end - -local disallowed_set = ("~`^+-=<>/[]*{}|\\_!?,;:()\"@&$#%"):gsub("[^%w]", "%%%1") - -common = { - --- valid identifier pattern - identifier_pattern = "%s*[^0-9%s"..disallowed_set.."][^"..disallowed_set.."]*", - -- names allowed for a function that aren't valid identifiers, mainly for overloading operators - special_functions_names = { - -- operators not included here and why: - -- * assignment operators (:=, +=, -=, //=, /=, *=, %=, ^=): handled with its own syntax (function assignment) - -- * list operator (,): is used when calling every functions, sounds like more trouble than it's worth - -- * |, &, ~? and ~ operators: are lazy and don't behave like regular functions - -- * # operator: need to set tag state _before_ evaluating the left arg - - -- prefix unop - "-_", "!_", - "&_", - -- binop - "_;_", - "_=_", "_:_", - "_!=_", "_==_", "_>=_", "_<=_", "_<_", "_>_", - "_+_", "_-_", - "_*_", "_//_", "_/_", "_%_", - "_::_", - "_^_", - "_!_", - "_._", - -- suffix unop - "_;", - "_!", - -- special - "()", - "{}" - }, - -- escapement code and their value in strings - -- only includes the "special" escape codes, as the generic \. -> . is handled by default in parse_text - -- I don't think there's a point in supporting form feed, carriage return, and other printer and terminal related codes - string_escapes = { - ["\\\\"] = "\\", - ["\\n"] = "\n", - ["\\t"] = "\t", - }, - -- list of possible injections and their associated name in vm.state.inject - injections = { - ["function start"] = "function_start", ["function end"] = "function_end", ["function return"] = "function_return", - ["scoped function start"] = "scoped_function_start", ["scoped function end"] = "scoped_function_end", ["scoped function return"] = "scoped_function_return", - ["checkpoint start"] = "checkpoint_start", ["checkpoint end"] = "checkpoint_end", - ["class start"] = "class_start", ["class end"] = "class_end" - }, - --- escape a string to be used as an exact match pattern - escape = function(str) - if not escapeCache[str] then - escapeCache[str] = str:gsub("[^%w]", "%%%1") - end - return escapeCache[str] - end, - --- trim a string by removing whitespace at the start and end - trim = function(str) - return str:match("^%s*(.-)%s*$") - end, - --- split a string separated by . - split = function(str) - local address = {} - for name in (str.."."):gmatch("(.-)%.") do - table.insert(address, name) - end - return address - end, - --- find a variable/function in a list, going up through the namespace hierarchy - -- will apply aliases - -- returns value, fqm in case of success - -- returns nil, err in case of error - find = function(aliases, list, namespace, name) - if namespace ~= "" then - local ns = common.split(namespace:gsub("%.$", "")) - for i=#ns, 1, -1 do - local current_namespace = table.concat(ns, ".", 1, i) - local fqm = replace_aliases(aliases, current_namespace, name) - if list[fqm] then - return list[fqm], fqm - end - end - end - -- root namespace - name = replace_aliases(aliases, "", name) - if list[name] then - return list[name], name - end - return nil, ("can't find %q in namespace %s"):format(name, namespace) - end, - --- same as find, but return a list of every encoutered possibility - -- returns a list of fqm - find_all = function(aliases, list, namespace, name) - local l = {} - if namespace ~= "" then - local ns = common.split(namespace:gsub("%.$", "")) - for i=#ns, 1, -1 do - local current_namespace = table.concat(ns, ".", 1, i) - local fqm = replace_aliases(aliases, current_namespace, name) - if list[fqm] then - table.insert(l, fqm) - end - end - end - -- root namespace - name = replace_aliases(aliases, "", name) - if list[name] then - table.insert(l, name) - end - return l - end, - --- transform an identifier into a clean version (trim each part) - format_identifier = function(identifier) - local r = identifier:gsub("[^%.]+", function(str) - return common.trim(str) - end) - return r - end, - --- flatten a nested list expression into a list of expressions - flatten_list = function(list, t) - t = t or {} - if list.type == "list" then - table.insert(t, 1, list.right) - common.flatten_list(list.left, t) - else - table.insert(t, 1, list) - end - return t - end, - -- parse interpolated expressions in a text - -- type sets the type of the returned expression (text is in text field) - -- allow_subtext (bool) to enable or not [subtext] support - -- if allow_binops is given, if one of the caracters of allow_binops appear unescaped in the text, it will interpreter a binary operator expression - -- * returns an expression with given type (string by default) and as a value a list of strings and expressions (text elements) - -- * if allow_binops is given, also returns remaining string (if the right expression stop before the end of the text) - -- * nil, err: in case of error - parse_text = function(text, state, namespace, type, allow_binops, allow_subtext, in_subtext) - local l = {} - local text_exp = { type = type, text = l } - local delimiters = "" - if allow_binops then - delimiters = allow_binops - end - if allow_subtext then - delimiters = delimiters .. "%[" - end - if in_subtext then - delimiters = delimiters .. "%]" - end - while text:match(("[^{%s]+"):format(delimiters)) do - local t, r = text:match(("^([^{%s]*)(.-)$"):format(delimiters)) - -- text - if t ~= "" then - -- handle \{ and binop escape: skip to next { until it's not escaped - while t:match("\\$") and r:match(("^[{%s]"):format(delimiters)) do - local t2, r2 = r:match(("^([{%s][^{%s]*)(.-)$"):format(delimiters, delimiters)) - t = t .. t2 -- don't need to remove \ as it will be stripped with other escapes codes 3 lines later - r = r2 - end - -- replace escape codes - local escaped = t:gsub("\\.", function(escape) - return common.string_escapes[escape] or escape:match("^\\(.)$") - end) - table.insert(l, escaped) - end - -- expr - if r:match("^{") then - local exp, rem = expression(r:gsub("^{", ""), state, namespace, "interpolated expression") - if not exp then return nil, rem end - if not rem:match("^%s*}") then return nil, ("expected closing } at end of expression before %q"):format(rem) end - -- wrap in format() call - local variant, err = common.find_function(state, namespace, "{}", { type = "parentheses", expression = exp }, true) - if not variant then return variant, err end - -- add to text - table.insert(l, variant) - text = rem:match("^%s*}(.*)$") - -- start subtext - elseif allow_subtext and r:match("^%[") then - local exp, rem = common.parse_text(r:gsub("^%[", ""), state, namespace, "text", "#~", allow_subtext, true) - if not exp then return nil, rem end - if not rem:match("^%]") then return nil, ("expected closing ] at end of subtext before %q"):format(rem) end - -- add to text - table.insert(l, exp) - text = rem:match("^%](.*)$") - -- end subtext - elseif in_subtext and r:match("^%]") then - if allow_binops then - return text_exp, r - else - return text_exp - end - -- binop expression at the end of the text - elseif allow_binops and r:match(("^[%s]"):format(allow_binops)) then - local exp, rem = expression(r, state, namespace, "text binop suffix", nil, text_exp) - if not exp then return nil, rem end - return exp, rem - elseif r == "" then - break - else - error(("unexpected %q at end of text or string"):format(r)) - end - end - if allow_binops then - return text_exp, "" - else - return text_exp - end - end, - -- find a list of compatible function variants from a fully qualified name - -- this functions does not guarantee that the returned variants are fully compatible with the given arguments and only performs a pre-selection without the ones which definitely aren't - -- * list of compatible variants: if success - -- * nil, err: if error - find_function_variant_from_fqm = function(fqm, state, arg) - local err = ("compatible function %q variant not found"):format(fqm) - local func = state.functions[fqm] - local args = arg and common.flatten_list(arg) or {} - local variants = {} - for _, variant in ipairs(func) do - local ok = true - -- arity check - -- note: because named args can't be predicted in advance (pairs need to be evaluated), this arity check isn't enough to guarantee a compatible arity - -- (e.g., if there's 3 required args but only provide 3 optional arg in a call, will pass) - local min, max = variant.arity[1], variant.arity[2] - if #args < min or #args > max then - if min == max then - err = ("function %q expected %s arguments but received %s"):format(fqm, min, #args) - else - err = ("function %q expected between %s and %s arguments but received %s"):format(fqm, min, max, #args) - end - ok = false - end - -- done - if ok then - table.insert(variants, variant) - end - end - if #variants > 0 then - return variants - else - return nil, err - end - end, - --- same as find_function_variant_from_fqm, but will search every function from the current namespace and up using find - -- returns directly a function expression in case of success - -- return nil, err otherwise - find_function = function(state, namespace, name, arg, paren_call, implicit_call) - local l = common.find_all(state.aliases, state.functions, namespace, name) - return common.find_function_from_list(state, namespace, name, l, arg, paren_call, implicit_call) - end, - --- same as find_function, but take a list of already found ffqm instead of searching - find_function_from_list = function(state, namespace, name, names, arg, paren_call, implicit_call) - local variants = {} - local err = ("compatible function %q variant not found"):format(name) - local l = common.find_all(state.aliases, state.functions, namespace, name) - for _, ffqm in ipairs(l) do - local found - found, err = common.find_function_variant_from_fqm(ffqm, state, arg) - if found then - for _, v in ipairs(found) do - table.insert(variants, v) - end - end - end - if #variants > 0 then - return { - type = "function call", - called_name = name, -- name of the called function - paren_call = paren_call, -- was call with parantheses? - implicit_call = implicit_call, -- was call implicitely (no ! or parentheses)? - variants = variants, -- list of potential variants - argument = { -- wrap everything in a list literal to simplify later things (otherwise may be nil, single value, list constructor) - type = "map brackets", - expression = arg - } - } - else - return nil, err -- returns last error - end - end, - -- returns the function's signature text - signature = function(fn) - if fn.signature then return fn.signature end - local signature - local function make_param_signature(p) - local sig = p.name - if p.vararg then - sig = sig .. "..." - end - if p.alias then - sig = sig .. ":" .. p.alias - end - if p.type_constraint then - sig = sig .. "::" .. p.type_constraint - end - if p.default then - sig = sig .. "=" .. p.default - end - return sig - end - local arg_sig = {} - for j, p in ipairs(fn.params) do - arg_sig[j] = make_param_signature(p) - end - if fn.assignment then - signature = ("%s(%s) := %s"):format(fn.name, table.concat(arg_sig, ", "), make_param_signature(fn.assignment)) - else - signature = ("%s(%s)"):format(fn.name, table.concat(arg_sig, ", ")) - end - return signature - end, - -- same as signature, format the signature for displaying to the user and add some debug information - pretty_signature = function(fn) - return ("%s (at %s)"):format(common.signature(fn), fn.source) - end, -} - -package.loaded[...] = common -expression = require((...):gsub("common$", "expression")) - -return common diff --git a/parser/expression.lua b/parser/expression.lua deleted file mode 100644 index b59ee1a..0000000 --- a/parser/expression.lua +++ /dev/null @@ -1,558 +0,0 @@ -local identifier_pattern, format_identifier, find, escape, find_function, parse_text, find_all, split, find_function_from_list, preparse - ---- binop priority -local binops_prio = { - [1] = { ";" }, - [2] = { ":=", "+=", "-=", "//=", "/=", "*=", "%=", "^=" }, - [3] = { "," }, - [4] = { "~?", "~", "#" }, - [5] = { "=", ":" }, - [6] = { "|", "&" }, - [7] = { "!=", "==", ">=", "<=", "<", ">" }, - [8] = { "+", "-" }, - [9] = { "*", "//", "/", "%" }, - [10] = { "::" }, - [11] = {}, -- unary operators - [12] = { "^" }, - [13] = { "!" }, - [14] = {}, - [15] = { "." } -} -local call_priority = 13 -- note: higher priority operators will have to deal with potential functions expressions -local implicit_call_priority = 12.5 -- just below call priority so explicit calls automatically take precedence -local pair_priority = 5 -local implicit_multiply_priority = 9.5 -- just above / so 1/2x gives 1/(2x) --- unop priority -local prefix_unops_prio = { - [1] = {}, - [2] = {}, - [3] = { "$" }, - [4] = {}, - [5] = {}, - [6] = {}, - [7] = {}, - [8] = {}, - [9] = {}, - [10] = {}, - [11] = { "-", "!" }, - [12] = {}, - [13] = {}, - [14] = { "&" }, - [15] = {} -} -local suffix_unops_prio = { - [1] = { ";" }, - [2] = {}, - [3] = {}, - [4] = {}, - [5] = {}, - [6] = {}, - [7] = {}, - [8] = {}, - [9] = {}, - [10] = {}, - [11] = {}, - [12] = {}, - [13] = { "!" }, - [14] = {}, - [15] = {} -} - -local function get_text_in_litteral(s, start_pos) - local d, r - -- find end of string - start_pos = start_pos or 2 - local i = start_pos - while true do - local skip - skip = s:match("^[^%\\\"]-%b{}()", i) -- skip interpolated expressions - if skip then i = skip end - skip = s:match("^[^%\\\"]-\\.()", i) -- skip escape codes (need to skip every escape code in order to correctly parse \\": the " is not escaped) - if skip then i = skip end - if not skip then -- nothing skipped - local end_pos = s:match("^[^%\"]-\"()", i) -- search final double quote - if end_pos then - d, r = s:sub(start_pos, end_pos-2), s:sub(end_pos) - break - else - return nil, ("expected \" to finish string near %q"):format(s:sub(i)) - end - end - end - return d, r -end -local function random_identifier_alpha() - local r = "" - for _=1, 18 do -- that's live 10^30 possibilities, ought to be enough for anyone - if math.random(1, 2) == 1 then - r = r .. string.char(math.random(65, 90)) - else - r = r .. string.char(math.random(97, 122)) - end - end - return r -end - ---- parse an expression --- return expr, remaining if success --- returns nil, err if error -local function expression(s, state, namespace, source, current_priority, operating_on) - s = s:match("^%s*(.*)$") - current_priority = current_priority or 0 - if not operating_on then - -- number - if s:match("^%d*%.%d+") or s:match("^%d+") then - local d, r = s:match("^(%d*%.%d+)(.*)$") - if not d then - d, r = s:match("^(%d+)(.*)$") - end - return expression(r, state, namespace, source, current_priority, { - type = "number", - value = tonumber(d) - }) - -- string - elseif s:match("^%\"") then - local d, r = get_text_in_litteral(s) - local l, e = parse_text(d, state, namespace, "string") -- parse interpolated expressions - if not l then return l, e end - return expression(r, state, namespace, source, current_priority, l) - -- text buffer - elseif s:match("^%%%[") then - local text = s:match("^%%(.*)$") - local v, r = parse_text(text, state, namespace, "text", "#~", true) - if not v then return nil, r end - return expression(r, state, namespace, source, current_priority, { - type = "text buffer", - text = v - }) - -- paranthesis - elseif s:match("^%b()") then - local content, r = s:match("^(%b())(.*)$") - content = content:gsub("^%(", ""):gsub("%)$", "") - local exp - if content:match("[^%s]") then - local r_paren - exp, r_paren = expression(content, state, namespace, source) - if not exp then return nil, "invalid expression inside parentheses: "..r_paren end - if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of parenthesis expression"):format(r_paren) end - else - exp = { type = "nil", value = nil } - end - return expression(r, state, namespace, source, current_priority, { - type = "parentheses", - expression = exp - }) - -- list parenthesis - elseif s:match("^%b[]") then - local content, r = s:match("^(%b[])(.*)$") - content = content:gsub("^%[", ""):gsub("%]$", "") - local exp - if content:match("[^%s]") then - local r_paren - exp, r_paren = expression(content, state, namespace, source) - if not exp then return nil, "invalid expression inside list parentheses: "..r_paren end - if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of list parenthesis expression"):format(r_paren) end - end - return expression(r, state, namespace, source, current_priority, { - type = "list brackets", - expression = exp - }) - -- map parenthesis - elseif s:match("^%b{}") then - local content, r = s:match("^(%b{})(.*)$") - content = content:gsub("^%{", ""):gsub("%}$", "") - local exp - if content:match("[^%s]") then - local r_paren - exp, r_paren = expression(content, state, namespace, source) - if not exp then return nil, "invalid expression inside map parentheses: "..r_paren end - if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of map parenthesis expression"):format(r_paren) end - end - return expression(r, state, namespace, source, current_priority, { - type = "map brackets", - expression = exp - }) - -- identifier - elseif s:match("^"..identifier_pattern) then - local name, r = s:match("^("..identifier_pattern..")(.-)$") - name = format_identifier(name) - -- string:value pair shorthand using = - if r:match("^=[^=]") and pair_priority > current_priority then - local val - val, r = expression(r:match("^=(.*)$"), state, namespace, source, pair_priority) - if not val then return val, r end - local args = { - type = "list", - left = { - type = "string", - text = { name } - }, - right = val - } - -- find compatible variant - local variant, err = find_function(state, namespace, "_=_", args, true) - if not variant then return variant, err end - return expression(r, state, namespace, source, current_priority, variant) - end - -- variables - -- if name isn't a valid variable, suffix call: detect if a prefix is valid variable, suffix _._ call is handled in the binop section below - local nl = split(name) - for i=#nl, 1, -1 do - local name_prefix = table.concat(nl, ".", 1, i) - local var, vfqm = find(state.aliases, state.variables, namespace, name_prefix) - if var then - if i < #nl then - r = "."..table.concat(nl, ".", i+1, #nl)..r - end - return expression(r, state, namespace, source, current_priority, { - type = "variable", - name = vfqm - }) - end - end - -- functions. This is a temporary expression that will either be transformed into a reference by the &_ operator, or an (implicit) function call otherwise. - for i=#nl, 1, -1 do - local name_prefix = table.concat(nl, ".", 1, i) - local lfnqm = find_all(state.aliases, state.functions, namespace, name_prefix) - if #lfnqm > 0 then - if i < #nl then - r = "."..table.concat(nl, ".", i+1, #nl)..r - end - return expression(r, state, namespace, source, current_priority, { - type = "potential function", - called_name = name, - names = lfnqm - }) - end - end - return nil, ("can't find function or variable named %q in namespace %q"):format(name, namespace) - end - -- prefix unops - for prio, oplist in ipairs(prefix_unops_prio) do - for _, op in ipairs(oplist) do - local escaped = escape(op) - if s:match("^"..escaped) then - local sright = s:match("^"..escaped.."(.*)$") - -- function and variable reference - if op == "&" then - local right, r = expression(sright, state, namespace, source, prio) - if not right then return nil, ("invalid expression after unop %q: %s"):format(op, r) end - if right.type == "potential function" then - return expression(r, state, namespace, source, current_priority, { - type = "function reference", - names = right.names - }) - elseif right.type == "variable" then - return expression(r, state, namespace, source, current_priority, { - type = "variable reference", - name = right.name, - expression = right - }) - else - -- find variant - local variant, err = find_function(state, namespace, op.."_", right, true) - if not variant then return variant, err end - return expression(r, state, namespace, source, current_priority, variant) - end - -- anonymous function - elseif op == "$" then - -- get eventual arguments - local params = "()" - if sright:match("^%b()") then - params, sright = sright:match("^(%b())(.*)$") - end - -- define function - local fn_name = ("%s🥸%s"):format(namespace, random_identifier_alpha()) - local s, e = preparse(state, (":$%s%s\n\t@%s"):format(fn_name, params, fn_name), "", source) - if not s then return nil, e end - local fn = state.functions[fn_name][1] - -- parse return expression - local right, r = expression(sright, state, fn.namespace, source, prio) - if not right then return nil, ("invalid expression after unop %q: %s"):format(op, r) end - -- put expression in return line - for _, c in ipairs(fn.child) do - if c.type == "return" and c.expression == fn_name then - c.expression = right - end - end - -- return reference to created function - return expression(r, state, namespace, source, current_priority, { - type = "nonpersistent", - expression = { - type = "function reference", - names = { fn_name } - } - }) - -- normal prefix unop - else - local right, r = expression(sright, state, namespace, source, prio) - if not right then return nil, ("invalid expression after unop %q: %s"):format(op, r) end - -- find variant - local variant, err = find_function(state, namespace, op.."_", right, true) - if not variant then return variant, err end - return expression(r, state, namespace, source, current_priority, variant) - end - end - end - end - return nil, ("no valid expression before %q"):format(s) - else - -- transform potential function/variable calls into actual calls automatically - -- need to do this before every other operator, since once the code finds the next operator it won't go back to check if this applied and assume it - -- didn't skip anything since it didn't see any other operator before, even if it's actually higher priority... - -- the problems of an implicit operator I guess - if implicit_call_priority > current_priority then - -- implicit call of a function. Unlike for variables, can't be cancelled since there's not any other value this could return, we don't - -- have first class functions here... - if operating_on.type == "potential function" then - local args, paren_call, implicit_call - local r = s - if r:match("^%b()") then - paren_call = true - local content, rem = r:match("^(%b())(.*)$") - content = content:gsub("^%(", ""):gsub("%)$", "") - r = rem - -- get arguments - if content:match("[^%s]") then - local err - args, err = expression(content, state, namespace, source) - if not args then return args, err end - if err:match("[^%s]") then return nil, ("unexpected %q at end of argument list"):format(err) end - end - else -- implicit call; will be changed if there happens to be a ! after in the suffix operator code - implicit_call = true - end - -- find compatible variant - local variant, err = find_function_from_list(state, namespace, operating_on.called_name, operating_on.names, args, paren_call, implicit_call) - if not variant then return variant, err end - return expression(r, state, namespace, source, current_priority, variant) - -- implicit call on variable reference. Might be canceled afterwards due to finding a higher priority operator. - elseif operating_on.type == "variable" or (operating_on.type == "function call" and operating_on.called_name == "_._") then - local implicit_call_variant, err = find_function(state, namespace, "_!", { type = "value passthrough" }, false, true) - if not implicit_call_variant then return implicit_call_variant, err end - return expression(s, state, namespace, source, current_priority, { - type = "implicit call if reference", - variant = implicit_call_variant, - expression = operating_on - }) - end - end - -- binop - for prio, oplist in ipairs(binops_prio) do - if prio > current_priority then - -- cancel implicit call operator if we are handling a binop of higher priority - -- see comment a bit above on why the priority handling is stupid for implicit operators - local operating_on = operating_on - if prio > implicit_call_priority and operating_on.type == "implicit call if reference" then - operating_on = operating_on.expression - end - for _, op in ipairs(oplist) do - local escaped = escape(op) - if s:match("^"..escaped) then - local sright = s:match("^"..escaped.."(.*)$") - -- suffix call - if op == "!" and sright:match("^"..identifier_pattern) then - local name, r = sright:match("^("..identifier_pattern..")(.-)$") - name = format_identifier(name) - local args, paren_call - if r:match("^%b()") then - paren_call = true - local content, rem = r:match("^(%b())(.*)$") - content = content:gsub("^%(", ""):gsub("%)$", "") - r = rem - -- get arguments - if content:match("[^%s]") then - local err - args, err = expression(content, state, namespace, source) - if not args then return args, err end - if err:match("[^%s]") then return nil, ("unexpected %q at end of argument map"):format(err) end - end - end - -- add first argument - if not args then - args = operating_on - else - if args.type == "list" then -- insert as first element - local first_list = args - while first_list.left.type == "list" do - first_list = first_list.left - end - first_list.left = { - type = "list", - left = operating_on, - right = first_list.left - } - else - args = { - type = "list", - left = operating_on, - right = args - } - end - end - -- find compatible variant - local variant, err = find_function(state, namespace, name, args, paren_call) - if not variant then return variant, err end - return expression(r, state, namespace, source, current_priority, variant) - -- namespace - elseif op == "." and sright:match("^"..identifier_pattern) then - local name, r = sright:match("^("..identifier_pattern..")(.-)$") - name = format_identifier(name) - -- find variant - local args = { - type = "list", - left = operating_on, - right = { type = "string", text = { name } } - } - local variant, err = find_function(state, namespace, "_._", args, true) - if not variant then return variant, err end - return expression(r, state, namespace, source, current_priority, variant) - -- other binops - else - local right, r = expression(sright, state, namespace, source, prio) - if right then - -- list constructor (can't do this through a function call since we need to build a list for its arguments) - if op == "," then - return expression(r, state, namespace, source, current_priority, { - type = "list", - left = operating_on, - right = right - }) - -- special binops - elseif op == ":=" or op == "+=" or op == "-=" or op == "//=" or op == "/=" or op == "*=" or op == "%=" or op == "^=" then - -- cancel implicit call on right variable - if operating_on.type == "implicit call if reference" then - operating_on = operating_on.expression - end - -- rewrite assignment + arithmetic operators into a normal assignment - if op ~= ":=" then - local args = { - type = "list", - left = operating_on, - right = right - } - local variant, err = find_function(state, namespace, "_"..op:match("^(.*)%=$").."_", args, true) - if not variant then return variant, err end - right = variant - end - -- assign to a function - if operating_on.type == "function call" then - -- remove non-assignment functions - for i=#operating_on.variants, 1, -1 do - if not operating_on.variants[i].assignment then - table.remove(operating_on.variants, i) - end - end - if #operating_on.variants == 0 then - return nil, ("trying to perform assignment on function %s with no compatible assignment variant"):format(operating_on.called_name) - end - -- rewrite function to perform assignment - operating_on.assignment = right - return expression(r, state, namespace, source, current_priority, operating_on) - elseif operating_on.type ~= "variable" then - return nil, ("trying to perform assignment on a %s expression"):format(operating_on.type) - end - -- assign to a variable - return expression(r, state, namespace, source, current_priority, { - type = ":=", - left = operating_on, - right = right - }) - elseif op == "&" or op == "|" or op == "~?" or op == "~" or op == "#" then - return expression(r, state, namespace, source, current_priority, { - type = op, - left = operating_on, - right = right - }) - -- normal binop - else - -- find variant - local args = { - type = "list", - left = operating_on, - right = right - } - local variant, err = find_function(state, namespace, "_"..op.."_", args, true) - if not variant then return variant, err end - return expression(r, state, namespace, source, current_priority, variant) - end - end - end - end - end - end - end - -- suffix unop - for prio, oplist in ipairs(suffix_unops_prio) do - if prio > current_priority then - -- cancel implit call operator if we are handling an operator of higher priority - -- see comment a bit above on why the priority handling is stupid for implicit operators - local operating_on = operating_on - if prio > implicit_call_priority and operating_on.type == "implicit call if reference" then - operating_on = operating_on.expression - end - for _, op in ipairs(oplist) do - local escaped = escape(op) - if s:match("^"..escaped) then - local r = s:match("^"..escaped.."(.*)$") - -- remove ! after a previously-assumed implicit function call - if op == "!" and operating_on.type == "function call" and operating_on.implicit_call then - operating_on.implicit_call = false - return expression(r, state, namespace, source, current_priority, operating_on) - -- normal suffix unop - else - local variant, err = find_function(state, namespace, "_"..op, operating_on, true) - if not variant then return variant, err end - return expression(r, state, namespace, source, current_priority, variant) - end - end - end - end - end - -- index / call - if call_priority > current_priority and s:match("^%b()") then - if operating_on.type == "implicit call if reference" then - operating_on = operating_on.expression -- replaced with current call - end - local args = operating_on - local content, r = s:match("^(%b())(.*)$") - content = content:gsub("^%(", ""):gsub("%)$", "") - -- get arguments - if content:match("[^%s]") then - local right, r_paren = expression(content, state, namespace, source) - if not right then return right, r_paren end - if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of index/call expression"):format(r_paren) end - args = { type = "list", left = args, right = right } - end - local variant, err = find_function(state, namespace, "()", args, true) - if not variant then return variant, err end - return expression(r, state, namespace, source, current_priority, variant) - end - -- implicit multiplication - if implicit_multiply_priority > current_priority then - if s:match("^"..identifier_pattern) then - local right, r = expression(s, state, namespace, source, implicit_multiply_priority) - if right then - local args = { - type = "list", - left = operating_on, - right = right - } - local variant, err = find_function(state, namespace, "_*_", args, true) - if not variant then return variant, err end - return expression(r, state, namespace, source, current_priority, variant) - end - end - end - -- nothing to operate - return operating_on, s - end -end - -package.loaded[...] = expression -local common = require((...):gsub("expression$", "common")) -identifier_pattern, format_identifier, find, escape, find_function, parse_text, find_all, split, find_function_from_list = common.identifier_pattern, common.format_identifier, common.find, common.escape, common.find_function, common.parse_text, common.find_all, common.split, common.find_function_from_list - -preparse = require((...):gsub("expression$", "preparser")) - -return expression diff --git a/parser/expression/comment.lua b/parser/expression/comment.lua new file mode 100644 index 0000000..cdc55fb --- /dev/null +++ b/parser/expression/comment.lua @@ -0,0 +1,57 @@ +local primary = require("parser.expression.primary.primary") + +local comment +comment = primary { + match = function(self, str) + return str:match("^%(%(") + end, + parse = function(self, source, str, limit_pattern) + local rem = source:consume(str:match("^(%(%()(.*)$")) + + local content_list = {} + while not rem:match("^%)%)") do + local content + content, rem = rem:match("^([^%(%)]*)(.-)$") + + -- cut the text prematurely at limit_pattern if relevant + if limit_pattern and content:match(limit_pattern) then + local pos = content:match("()"..limit_pattern) -- limit_pattern can contain $, so can't directly extract with captures + content, rem = source:count(content:sub(1, pos-1)), ("))%s%s"):format(content:sub(pos), rem) + source:increment(-2) + else + source:count(content) + end + + table.insert(content_list, content) + + -- nested comment + if rem:match("^%(%(") then + local subcomment + subcomment, rem = comment:parse(source, rem, limit_pattern) + + table.insert(content_list, "((") + for _, c in ipairs(subcomment) do table.insert(content_list, c) end + table.insert(content_list, "))") + -- no end token after the comment + elseif not rem:match("^%)%)") then + -- single ) or (, keep on commentin' + if rem:match("^[%)%(]") then + local s + s, rem = source:count(rem:match("^([%)%(])(.-)$")) + table.insert(content_list, s) + -- anything other than end-of-line + elseif rem:match("[^%s]") then + error(("unexpected %q at end of comment"):format(rem), 0) + -- consumed everything until end-of-line, close your eyes and imagine the text has been closed + else + rem = rem .. "))" + end + end + end + rem = source:consume(rem:match("^(%)%))(.*)$")) + + return table.concat(content_list, ""), rem + end +} + +return comment diff --git a/parser/expression/contextual/function_parameter.lua b/parser/expression/contextual/function_parameter.lua new file mode 100644 index 0000000..0394a7a --- /dev/null +++ b/parser/expression/contextual/function_parameter.lua @@ -0,0 +1,40 @@ +local primary = require("parser.expression.primary.primary") +local identifier = require("parser.expression.primary.identifier") +local expression_to_ast = require("parser.expression.to_ast") + +local ast = require("ast") +local FunctionParameter = ast.FunctionParameter + +local operator_priority = require("common").operator_priority +local assignment_priority = operator_priority["_=_"] +local type_check_priority = operator_priority["_::_"] + +return primary { + match = function(self, str) + return identifier:match(str) + end, + parse = function(self, source, str, limit_pattern, no_default_value) + local source_param = source:clone() + + -- name + local ident, rem = identifier:parse(source, str) + + -- type check + local type_check + if rem:match("^%s*::") then + local scheck = source:consume(rem:match("^(%s*::%s*)(.*)$")) + type_check, rem = expression_to_ast(source, scheck, limit_pattern, type_check_priority) + end + + -- default value + local default + if not no_default_value then + if rem:match("^%s*=") then + local sdefault = source:consume(rem:match("^(%s*=%s*)(.*)$")) + default, rem = expression_to_ast(source, sdefault, limit_pattern, assignment_priority) + end + end + + return FunctionParameter:new(ident, default, type_check):set_source(source_param), rem + end +} diff --git a/parser/expression/contextual/function_parameter_no_default.lua b/parser/expression/contextual/function_parameter_no_default.lua new file mode 100644 index 0000000..8651fef --- /dev/null +++ b/parser/expression/contextual/function_parameter_no_default.lua @@ -0,0 +1,7 @@ +local function_parameter = require("parser.expression.contextual.function_parameter") + +return function_parameter { + parse = function(self, source, str, limit_pattern) + return function_parameter:parse(source, str, limit_pattern, true) + end +} diff --git a/parser/expression/contextual/parameter_tuple.lua b/parser/expression/contextual/parameter_tuple.lua new file mode 100644 index 0000000..e2cd824 --- /dev/null +++ b/parser/expression/contextual/parameter_tuple.lua @@ -0,0 +1,49 @@ +local primary = require("parser.expression.primary.primary") +local function_parameter = require("parser.expression.contextual.function_parameter") +local function_parameter_no_default = require("parser.expression.contextual.function_parameter_no_default") + +local ast = require("ast") +local ParameterTuple = ast.ParameterTuple + +return primary { + match = function(self, str) + return str:match("^%(") + end, + parse = function(self, source, str, limit_pattern) + local source_start = source:clone() + local parameters = ParameterTuple:new() + local rem = source:consume(str:match("^(%()(.*)$")) + + -- i would LOVE to reuse the regular list parsing code for this, but unfortunately the list parsing code + -- itself depends on this and expect this to be available quite early, and it's ANNOYING + while not rem:match("^%s*%)") do + -- parameter + local func_param + func_param, rem = function_parameter:expect(source, rem, limit_pattern) + + -- next! comma separator + if not rem:match("^%s*%)") then + if not rem:match("^%s*,") then + error(("unexpected %q at end of argument list"):format(rem), 0) + end + rem = source:consume(rem:match("^(%s*,)(.*)$")) + end + + -- add + parameters:insert(func_param) + end + rem = rem:match("^%s*%)(.*)$") + + -- assigment param + if rem:match("^%s*=") then + rem = source:consume(rem:match("^(%s*=%s*)(.*)$")) + + local func_param + func_param, rem = function_parameter_no_default:expect(source, rem, limit_pattern) + + parameters:insert_assignment(func_param) + end + + return parameters:set_source(source_start), rem + end +} diff --git a/parser/expression/primary/block_identifier.lua b/parser/expression/primary/block_identifier.lua new file mode 100644 index 0000000..6986b0a --- /dev/null +++ b/parser/expression/primary/block_identifier.lua @@ -0,0 +1,16 @@ +local primary = require("parser.expression.primary.primary") + +local ast = require("ast") +local Identifier, Call, ArgumentTuple = ast.Identifier, ast.Call, ast.ArgumentTuple + +return primary { + match = function(self, str) + return str:match("^_") + end, + + parse = function(self, source, str) + local source_start = source:clone() + local rem = source:consume(str:match("^(_)(.-)$")) + return Call:new(Identifier:new("_"), ArgumentTuple:new()):set_source(source_start), rem + end +} diff --git a/parser/expression/primary/function_definition.lua b/parser/expression/primary/function_definition.lua new file mode 100644 index 0000000..e9c57c7 --- /dev/null +++ b/parser/expression/primary/function_definition.lua @@ -0,0 +1,205 @@ +local primary = require("parser.expression.primary.primary") +local function_parameter_no_default = require("parser.expression.contextual.function_parameter_no_default") +local parameter_tuple = require("parser.expression.contextual.parameter_tuple") +local identifier = require("parser.expression.primary.identifier") + +local expression_to_ast = require("parser.expression.to_ast") +local escape = require("common").escape + +local ast = require("ast") +local Symbol, Definition, Function, ParameterTuple = ast.Symbol, ast.Definition, ast.Function, ast.ParameterTuple + +local regular_operators = require("common").regular_operators +local prefixes = regular_operators.prefixes +local suffixes = regular_operators.suffixes +local infixes = regular_operators.infixes + +local operator_priority = require("common").operator_priority + +-- same as function_parameter_no_default, but allow wrapping in (evenetual) parentheses +-- in order to solve some priotity issues (_._ has higher priority than _::_, leading to not being possible to overload it with type filtering without parentheses) +local function_parameter_maybe_parenthesis = function_parameter_no_default { + match = function(self, str) + if str:match("^%(") then + return function_parameter_no_default:match(str:match("^%((.*)$")) + else + return function_parameter_no_default:match(str) + end + end, + parse = function(self, source, str, limit_pattern) + if str:match("^%(") then + str = source:consume(str:match("^(%()(.*)$")) + + local exp, rem = function_parameter_no_default:parse(source, str, limit_pattern) + + if not rem:match("^%s*%)") then error(("unexpected %q at end of parenthesis"):format(rem), 0) end + rem = source:consume(rem:match("^(%s*%))(.-)$")) + + return exp, rem + else + return function_parameter_no_default:parse(source, str, limit_pattern) + end + end +} + + +-- signature type 1: unary prefix +-- :$-parameter exp +-- returns symbol, parameter_tuple, rem if success +-- return nil otherwise +local function search_prefix_signature(modifiers, source, str, limit_pattern) + for _, pfx in ipairs(prefixes) do + local prefix = pfx[1] + local prefix_pattern = "%s*"..escape(prefix).."%s*" + if str:match("^"..prefix_pattern) then + -- operator name + local rem = source:consume(str:match("^("..prefix_pattern..")(.*)$")) + local symbol = Symbol:new(prefix.."_", modifiers):set_source(source:clone():increment(-1)) + + -- parameters + local parameter + parameter, rem = function_parameter_maybe_parenthesis:expect(source, rem, limit_pattern) + + local parameters = ParameterTuple:new() + parameters:insert(parameter) + + return symbol, parameters, rem + end + end +end + +-- signature type 2: binary infix +-- should be checked before suffix signature +-- :$parameterA + parameterB exp +-- returns symbol, parameter_tuple, rem if success +-- return nil otherwise +local function search_infix_signature(modifiers, source, str, limit_pattern) + if function_parameter_maybe_parenthesis:match(str) then + local src = source:clone() -- operate on clone source since search success is not yet guaranteed + local parameter_a, rem = function_parameter_maybe_parenthesis:parse(src, str, limit_pattern) + + local parameters = ParameterTuple:new() + parameters:insert(parameter_a) + + for _, ifx in ipairs(infixes) do + local infix = ifx[1] + local infix_pattern = "%s*"..escape(infix).."%s*" + if rem:match("^"..infix_pattern) then + -- operator name + rem = src:consume(rem:match("^("..infix_pattern..")(.*)$")) + local symbol = Symbol:new("_"..infix.."_", modifiers):set_source(src:clone():increment(-1)) + + -- parameters + if function_parameter_maybe_parenthesis:match(rem) then + local parameter_b + parameter_b, rem = function_parameter_maybe_parenthesis:parse(src, rem, limit_pattern) + + parameters:insert(parameter_b) + + source:set(src) + return symbol, parameters, rem + else + return + end + end + end + end +end + +-- signature type 3: unary suffix +-- :$parameter! exp +-- returns symbol, parameter_tuple, rem if success +-- return nil otherwise +local function search_suffix_signature(modifiers, source, str, limit_pattern) + if function_parameter_maybe_parenthesis:match(str) then + local src = source:clone() -- operate on clone source since search success is not yet guaranteed + local parameter_a, rem = function_parameter_maybe_parenthesis:parse(src, str, limit_pattern) + + local parameters = ParameterTuple:new() + parameters:insert(parameter_a) + + for _, sfx in ipairs(suffixes) do + local suffix = sfx[1] + local suffix_pattern = "%s*"..escape(suffix).."%s*" + if rem:match("^"..suffix_pattern) then + -- operator name + rem = src:count(rem:match("^("..suffix_pattern..")(.*)$")) + local symbol = Symbol:new("_"..suffix, modifiers):set_source(src:clone():increment(-1)) + + source:set(src) + return symbol, parameters, rem + end + end + end +end + +-- signature type 4: regular function +-- :$identifier(parameter_tuple, ...) exp +-- returns symbol, parameter_tuple, rem if success +-- return nil otherwise +local function search_function_signature(modifiers, source, str, limit_pattern) + if identifier:match(str) then + local name_source = source:clone() + local name, rem = identifier:parse(source, str, limit_pattern) + + -- name + local symbol = name:to_symbol(modifiers):set_source(name_source) + + -- parse eventual parameters + local parameters + if parameter_tuple:match(rem) then + parameters, rem = parameter_tuple:parse(source, rem) + else + parameters = ParameterTuple:new() + end + + return symbol, parameters, rem + end +end + +return primary { + match = function(self, str) + return str:match("^%::?[&@]?%$") + end, + + parse = function(self, source, str, limit_pattern) + local source_start = source:clone() + local mod_const, mod_exported, rem = source:consume(str:match("^(%:(:?)([&@]?)%$)(.-)$")) + + -- get modifiers + local constant, exported, persistent + if mod_const == ":" then constant = true end + if mod_exported == "@" then exported = true + elseif mod_exported == "&" then persistent = true end + local modifiers = { constant = constant, exported = exported, persistent = persistent } + + -- search for a valid signature + local symbol, parameters + local s, p, r = search_prefix_signature(modifiers, source, rem, limit_pattern) + if s then symbol, parameters, rem = s, p, r + else + s, p, r = search_infix_signature(modifiers, source, rem, limit_pattern) + if s then symbol, parameters, rem = s, p, r + else + s, p, r = search_suffix_signature(modifiers, source, rem, limit_pattern) + if s then symbol, parameters, rem = s, p, r + else + s, p, r = search_function_signature(modifiers, source, rem, limit_pattern) + if s then symbol, parameters, rem = s, p, r end + end + end + end + + -- done + if symbol then + -- parse expression + local right + s, right, rem = pcall(expression_to_ast, source, rem, limit_pattern, operator_priority["$_"]) + if not s then error(("invalid expression after unop %q: %s"):format(self.operator, right), 0) end + + -- return function + local fn = Function:new(parameters, right):set_source(source_start) + return Definition:new(symbol, fn):set_source(source_start), rem + end + end +} diff --git a/parser/expression/primary/identifier.lua b/parser/expression/primary/identifier.lua new file mode 100644 index 0000000..feff777 --- /dev/null +++ b/parser/expression/primary/identifier.lua @@ -0,0 +1,40 @@ +local primary = require("parser.expression.primary.primary") + +local Identifier = require("ast.Identifier") + +local disallowed_set = (".~`^+-=<>/[]*{}|\\_!?,;:()\"@&$#%"):gsub("[^%w]", "%%%1") +local identifier_pattern = "%s*[^0-9%s'"..disallowed_set.."][^"..disallowed_set.."]*" + +local common = require("common") +local trim, escape = common.trim, common.escape + +-- for operator identifiers +local regular_operators = require("common").regular_operators +local operators = {} +for _, prefix in ipairs(regular_operators.prefixes) do table.insert(operators, prefix[1].."_") end +for _, infix in ipairs(regular_operators.infixes) do table.insert(operators, "_"..infix[1].."_") end +for _, suffix in ipairs(regular_operators.suffixes) do table.insert(operators, "_"..suffix[1]) end + +-- all valid identifier patterns +local identifier_patterns = { identifier_pattern } +for _, operator in ipairs(operators) do table.insert(identifier_patterns, "%s*"..escape(operator)) end + +return primary { + match = function(self, str) + for _, pat in ipairs(identifier_patterns) do + if str:match("^"..pat) then return true end + end + return false + end, + + parse = function(self, source, str) + for _, pat in ipairs(identifier_patterns) do + if str:match("^"..pat) then + local start_source = source:clone() + local name, rem = source:count(str:match("^("..pat..")(.-)$")) + name = trim(name) + return Identifier:new(name):set_source(start_source), rem + end + end + end +} diff --git a/parser/expression/primary/init.lua b/parser/expression/primary/init.lua new file mode 100644 index 0000000..7655a75 --- /dev/null +++ b/parser/expression/primary/init.lua @@ -0,0 +1,42 @@ +--- try to parse a primary expression + +local function r(name) + return require("parser.expression.primary."..name), nil +end + +local primaries = { + r("number"), + r("string"), + r("text"), + r("parenthesis"), + r("function_definition"), + r("symbol"), + r("identifier"), + r("block_identifier"), + r("tuple"), + r("struct"), + + -- prefixes + -- 1 + r("prefix.semicolon"), + r("prefix.function"), + -- 2 + r("prefix.return"), + -- 3.5 + r("prefix.else"), + -- 11 + r("prefix.negation"), + r("prefix.not"), + r("prefix.mutable"), +} + +return { + -- returns exp, rem if expression found + -- returns nil if no expression found + search = function(self, source, str, limit_pattern) + for _, primary in ipairs(primaries) do + local exp, rem = primary:search(source, str, limit_pattern) + if exp then return exp, rem end + end + end +} diff --git a/parser/expression/primary/number.lua b/parser/expression/primary/number.lua new file mode 100644 index 0000000..2cbf94e --- /dev/null +++ b/parser/expression/primary/number.lua @@ -0,0 +1,19 @@ +local primary = require("parser.expression.primary.primary") + +local Number = require("ast.Number") + +return primary { + match = function(self, str) + return str:match("^%d*%.%d+") or str:match("^%d+") + end, + parse = function(self, source, str) + local start_source = source:clone() + local d, r = str:match("^(%d*%.%d+)(.*)$") + if not d then + d, r = source:count(str:match("^(%d+)(.*)$")) + else + source:count(d) + end + return Number:new(tonumber(d)):set_source(start_source), r + end +} diff --git a/parser/expression/primary/parenthesis.lua b/parser/expression/primary/parenthesis.lua new file mode 100644 index 0000000..36b6ec3 --- /dev/null +++ b/parser/expression/primary/parenthesis.lua @@ -0,0 +1,31 @@ +-- either parentheses or nil () + +local primary = require("parser.expression.primary.primary") + +local ast = require("ast") +local Nil = ast.Nil + +local expression_to_ast = require("parser.expression.to_ast") + +return primary { + match = function(self, str) + return str:match("^%(") + end, + parse = function(self, source, str) + local start_source = source:clone() + local rem = source:consume(str:match("^(%()(.*)$")) + + local exp + if rem:match("^%s*%)") then + exp = Nil:new() + else + local s + s, exp, rem = pcall(expression_to_ast, source, rem, "%)") + if not s then error("invalid expression inside parentheses: "..exp, 0) end + if not rem:match("^%s*%)") then error(("unexpected %q at end of parenthesis"):format(rem), 0) end + end + rem = source:consume(rem:match("^(%s*%))(.*)$")) + + return exp:set_source(start_source), rem + end +} diff --git a/parser/expression/primary/prefix/else.lua b/parser/expression/primary/prefix/else.lua new file mode 100644 index 0000000..3f0fc74 --- /dev/null +++ b/parser/expression/primary/prefix/else.lua @@ -0,0 +1,8 @@ +local prefix_quote_right = require("parser.expression.primary.prefix.prefix_quote_right") +local operator_priority = require("common").operator_priority + +return prefix_quote_right { + operator = "~", + identifier = "~_", + priority = operator_priority["~_"] +} diff --git a/parser/expression/primary/prefix/function.lua b/parser/expression/primary/prefix/function.lua new file mode 100644 index 0000000..a9a41e0 --- /dev/null +++ b/parser/expression/primary/prefix/function.lua @@ -0,0 +1,35 @@ +local prefix = require("parser.expression.primary.prefix.prefix") +local parameter_tuple = require("parser.expression.contextual.parameter_tuple") +local escape = require("common").escape +local expression_to_ast = require("parser.expression.to_ast") + +local ast = require("ast") +local Function, ParameterTuple = ast.Function, ast.ParameterTuple + +local operator_priority = require("common").operator_priority + +return prefix { + operator = "$", + priority = operator_priority["$_"], + + parse = function(self, source, str, limit_pattern) + local source_start = source:clone() + local escaped = escape(self.operator) + local rem = source:consume(str:match("^("..escaped..")(.*)$")) + + -- parse eventual parameters + local parameters + if parameter_tuple:match(rem) then + parameters, rem = parameter_tuple:parse(source, rem) + else + parameters = ParameterTuple:new() + end + + -- parse expression + local s, right + s, right, rem = pcall(expression_to_ast, source, rem, limit_pattern, self.priority) + if not s then error(("invalid expression after unop %q: %s"):format(self.operator, right), 0) end + + return Function:new(parameters, right):set_source(source_start), rem + end +} diff --git a/parser/expression/primary/prefix/mutable.lua b/parser/expression/primary/prefix/mutable.lua new file mode 100644 index 0000000..03b84e1 --- /dev/null +++ b/parser/expression/primary/prefix/mutable.lua @@ -0,0 +1,9 @@ +local prefix = require("parser.expression.primary.prefix.prefix") + +local operator_priority = require("common").operator_priority + +return prefix { + operator = "*", + identifier = "*_", + priority = operator_priority["*_"] +} diff --git a/parser/expression/primary/prefix/negation.lua b/parser/expression/primary/prefix/negation.lua new file mode 100644 index 0000000..5cccd74 --- /dev/null +++ b/parser/expression/primary/prefix/negation.lua @@ -0,0 +1,9 @@ +local prefix = require("parser.expression.primary.prefix.prefix") + +local operator_priority = require("common").operator_priority + +return prefix { + operator = "-", + identifier = "-_", + priority = operator_priority["-_"] +} diff --git a/parser/expression/primary/prefix/not.lua b/parser/expression/primary/prefix/not.lua new file mode 100644 index 0000000..f7ba921 --- /dev/null +++ b/parser/expression/primary/prefix/not.lua @@ -0,0 +1,9 @@ +local prefix = require("parser.expression.primary.prefix.prefix") + +local operator_priority = require("common").operator_priority + +return prefix { + operator = "!", + identifier = "!_", + priority = operator_priority["!_"] +} diff --git a/parser/expression/primary/prefix/prefix.lua b/parser/expression/primary/prefix/prefix.lua new file mode 100644 index 0000000..69bfbd4 --- /dev/null +++ b/parser/expression/primary/prefix/prefix.lua @@ -0,0 +1,34 @@ +-- unary prefix operators, for example: the - in -5 + +local primary = require("parser.expression.primary.primary") +local escape = require("common").escape +local expression_to_ast = require("parser.expression.to_ast") + +local ast = require("ast") +local Call, Identifier, ArgumentTuple = ast.Call, ast.Identifier, ast.ArgumentTuple + +return primary { + operator = nil, + identifier = nil, + priority = nil, + + match = function(self, str) + local escaped = escape(self.operator) + return str:match("^"..escaped) + end, + + parse = function(self, source, str, limit_pattern) + local source_start = source:clone() + local escaped = escape(self.operator) + + local sright = source:consume(str:match("^("..escaped..")(.*)$")) + local s, right, rem = pcall(expression_to_ast, source, sright, limit_pattern, self.priority) + if not s then error(("invalid expression after prefix operator %q: %s"):format(self.operator, right), 0) end + + return self:build_ast(right):set_source(source_start), rem + end, + + build_ast = function(self, right) + return Call:new(Identifier:new(self.identifier), ArgumentTuple:new(right)) + end +} diff --git a/parser/expression/primary/prefix/prefix_quote_right.lua b/parser/expression/primary/prefix/prefix_quote_right.lua new file mode 100644 index 0000000..4792108 --- /dev/null +++ b/parser/expression/primary/prefix/prefix_quote_right.lua @@ -0,0 +1,11 @@ +local prefix = require("parser.expression.primary.prefix.prefix") + +local ast = require("ast") +local Call, Identifier, ArgumentTuple, Quote = ast.Call, ast.Identifier, ast.ArgumentTuple, ast.Quote + +return prefix { + build_ast = function(self, right) + right = Quote:new(right) + return Call:new(Identifier:new(self.identifier), ArgumentTuple:new(right)) + end +} diff --git a/parser/expression/primary/prefix/return.lua b/parser/expression/primary/prefix/return.lua new file mode 100644 index 0000000..0c34e8c --- /dev/null +++ b/parser/expression/primary/prefix/return.lua @@ -0,0 +1,15 @@ +local prefix = require("parser.expression.primary.prefix.prefix") + +local ast = require("ast") +local Return = ast.Return + +local operator_priority = require("common").operator_priority + +return prefix { + operator = "@", + priority = operator_priority["@_"], + + build_ast = function(self, right) + return Return:new(right) + end +} diff --git a/parser/expression/primary/prefix/semicolon.lua b/parser/expression/primary/prefix/semicolon.lua new file mode 100644 index 0000000..fe73a5e --- /dev/null +++ b/parser/expression/primary/prefix/semicolon.lua @@ -0,0 +1,13 @@ +local prefix = require("parser.expression.primary.prefix.prefix") + +local operator_priority = require("common").operator_priority + +return prefix { + operator = ";", + identifier = ";_", + priority = operator_priority[";_"], + + build_ast = function(self, right) + return right + end +} diff --git a/parser/expression/primary/primary.lua b/parser/expression/primary/primary.lua new file mode 100644 index 0000000..a8502f3 --- /dev/null +++ b/parser/expression/primary/primary.lua @@ -0,0 +1,33 @@ +local class = require("class") + +return class { + new = false, -- static class + + -- returns exp, rem if expression found + -- returns nil if no expression found + search = function(self, source, str, limit_pattern) + if not self:match(str) then + return nil + end + return self:parse(source, str, limit_pattern) + end, + -- return bool + -- (not needed if you redefined :search) + match = function(self, str) + return false + end, + -- return AST, rem + -- (not needed if you redefined :search) + parse = function(self, source, str, limit_pattern) + error("unimplemented") + end, + + -- class helpers -- + + -- return AST, rem + expect = function(self, source, str, limit_pattern) + local exp, rem = self:search(source, str, limit_pattern) + if not exp then error(("expected %s but got %s"):format(self.type, str)) end + return exp, rem + end +} diff --git a/parser/expression/primary/string.lua b/parser/expression/primary/string.lua new file mode 100644 index 0000000..6d38f9e --- /dev/null +++ b/parser/expression/primary/string.lua @@ -0,0 +1,78 @@ +-- note: this is reused in primary.text, hence all the configurable fields + +local primary = require("parser.expression.primary.primary") + +local StringInterpolation = require("ast.StringInterpolation") + +local ast = require("ast") +local String = ast.String + +local expression_to_ast = require("parser.expression.to_ast") + +local escape = require("common").escape + +local escape_code = { + ["n"] = "\n", + ["t"] = "\t", + -- everything else is identity by default +} + +return primary { + type = "string", -- interpolation type - used for errors + start_pattern = "\"", -- pattern that start the string interpolation + stop_char = "\"", -- character that stops the string interpolation - must be a single character! + + allow_implicit_stop = false, -- set to true to allow the string to be closed implicitely when reaching the end of the expression or limit_pattern + + interpolation = StringInterpolation, + + match = function(self, str) + return str:match("^"..self.start_pattern) + end, + parse = function(self, source, str, limit_pattern) + local interpolation = self.interpolation:new() + + local stop_pattern = escape(self.stop_char) + local start_source = source:clone() + local rem = source:consume(str:match("^("..self.start_pattern..")(.-)$")) + + while not rem:match("^"..stop_pattern) do + local text_source = source:clone() + local text + text, rem = rem:match("^([^%{%\\"..stop_pattern.."]*)(.-)$") -- get all text until something potentially happens + + -- cut the text prematurely at limit_pattern if relevant + if self.allow_implicit_stop and limit_pattern and text:match(limit_pattern) then + local pos = text:match("()"..limit_pattern) -- limit_pattern can contain $, so can't directly extract with captures + text, rem = source:count(text:sub(1, pos-1)), ("%s%s%s"):format(self.stop_char, text:sub(pos), rem) + source:increment(-1) + else + source:count(text) + end + + interpolation:insert(String:new(text):set_source(text_source)) + + if rem:match("^%{") then + local ok, exp + ok, exp, rem = pcall(expression_to_ast, source, source:consume(rem:match("^(%{)(.*)$")), "%}") + if not ok then error("invalid expression inside interpolation: "..exp, 0) end + if not rem:match("^%s*%}") then error(("unexpected %q at end of interpolation"):format(rem), 0) end + rem = source:consume(rem:match("^(%s*%})(.*)$")) + interpolation:insert(exp) + elseif rem:match("^\\") then + text, rem = source:consume(rem:match("^(\\(.))(.*)$")) + interpolation:insert(String:new(escape_code[text] or text)) + elseif not rem:match("^"..stop_pattern) then + if not self.allow_implicit_stop or rem:match("[^%s]") then + error(("unexpected %q at end of "..self.type):format(rem), 0) + -- consumed everything until end-of-line, implicit stop allowed, close your eyes and imagine the text has been closed + else + rem = rem .. self.stop_char + end + end + end + rem = source:consume(rem:match("^("..stop_pattern..")(.*)$")) + + return interpolation:set_source(start_source), rem + end +} diff --git a/parser/expression/primary/struct.lua b/parser/expression/primary/struct.lua new file mode 100644 index 0000000..1bc6371 --- /dev/null +++ b/parser/expression/primary/struct.lua @@ -0,0 +1,17 @@ +local primary = require("parser.expression.primary.primary") +local tuple = require("parser.expression.primary.tuple") + +local ast = require("ast") +local Struct = ast.Struct + +return primary { + match = function(self, str) + return str:match("^%{") + end, + + parse = function(self, source, str) + local l, rem = tuple:parse_tuple(source, str, "{", '}') + + return Struct:from_tuple(l), rem + end +} diff --git a/parser/expression/primary/symbol.lua b/parser/expression/primary/symbol.lua new file mode 100644 index 0000000..05a40aa --- /dev/null +++ b/parser/expression/primary/symbol.lua @@ -0,0 +1,40 @@ +local primary = require("parser.expression.primary.primary") +local type_check = require("parser.expression.secondary.infix.type_check") + +local identifier = require("parser.expression.primary.identifier") + +local ast = require("ast") +local Nil = ast.Nil + +return primary { + match = function(self, str) + if str:match("^%::?[&@]?") then + return identifier:match(str:match("^%::?[&@]?(.-)$")) + end + return false + end, + + parse = function(self, source, str) + local mod_const, mod_export, rem = source:consume(str:match("^(%:(:?)([&@]?))(.-)$")) + local constant, persistent, type_check_exp, exported + + -- get modifier + if mod_const == ":" then constant = true end + if mod_export == "&" then persistent = true + elseif mod_export == "@" then exported = true end + + -- name + local ident + ident, rem = identifier:parse(source, rem) + + -- type check + local nil_val = Nil:new() + if type_check:match(rem, 0, nil_val) then + local exp + exp, rem = type_check:parse(source, rem, nil, 0, nil_val) + type_check_exp = exp.arguments.list[2] + end + + return ident:to_symbol{ constant = constant, persistent = persistent, exported = exported, type_check = type_check_exp }:set_source(source), rem + end +} diff --git a/parser/expression/primary/text.lua b/parser/expression/primary/text.lua new file mode 100644 index 0000000..a1c2d41 --- /dev/null +++ b/parser/expression/primary/text.lua @@ -0,0 +1,24 @@ +local string = require("parser.expression.primary.string") + +local ast = require("ast") +local TextInterpolation = ast.TextInterpolation + +return string { + type = "text", + start_pattern = "|%s?", + stop_char = "|", + allow_implicit_stop = true, + interpolation = TextInterpolation, + + parse = function(self, source, str, limit_pattern) + local interpolation, rem = string.parse(self, source, str, limit_pattern) + + -- restore | when chaining with a choice operator + if rem:match("^>") then + rem = "|" .. rem + source:increment(-1) + end + + return interpolation, rem + end +} diff --git a/parser/expression/primary/tuple.lua b/parser/expression/primary/tuple.lua new file mode 100644 index 0000000..7ff0159 --- /dev/null +++ b/parser/expression/primary/tuple.lua @@ -0,0 +1,41 @@ +local primary = require("parser.expression.primary.primary") + +local ast = require("ast") +local Tuple = ast.Tuple + +local expression_to_ast = require("parser.expression.to_ast") + +local escape = require("common").escape + +return primary { + match = function(self, str) + return str:match("^%[") + end, + + parse = function(self, source, str) + return self:parse_tuple(source, str, "[", "]") + end, + + parse_tuple = function(self, source, str, start_char, end_char) + local start_source = source:clone() + local rem = source:consume(str:match("^("..escape(start_char)..")(.*)$")) + local end_match = escape(end_char) + + local l + if not rem:match("^%s*"..end_match) then + local s + s, l, rem = pcall(expression_to_ast, source, rem, end_match) + if not s then error("invalid expression in list: "..l, 0) end + end + + if not Tuple:is(l) or l.explicit then l = Tuple:new(l) end -- single or no element + + if not rem:match("^%s*"..end_match) then + error(("unexpected %q at end of list"):format(rem), 0) + end + rem = source:consume(rem:match("^(%s*"..end_match..")(.*)$")) + + l.explicit = true + return l:set_source(start_source), rem + end, +} diff --git a/parser/expression/secondary/infix/addition.lua b/parser/expression/secondary/infix/addition.lua new file mode 100644 index 0000000..54c1b62 --- /dev/null +++ b/parser/expression/secondary/infix/addition.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = "+", + identifier = "_+_", + priority = operator_priority["_+_"] +} diff --git a/parser/expression/secondary/infix/and.lua b/parser/expression/secondary/infix/and.lua new file mode 100644 index 0000000..4baf657 --- /dev/null +++ b/parser/expression/secondary/infix/and.lua @@ -0,0 +1,9 @@ +local infix_quote_right = require("parser.expression.secondary.infix.infix_quote_right") + +local operator_priority = require("common").operator_priority + +return infix_quote_right { + operator = "&", + identifier = "_&_", + priority = operator_priority["_&_"] +} diff --git a/parser/expression/secondary/infix/assignment.lua b/parser/expression/secondary/infix/assignment.lua new file mode 100644 index 0000000..5b01467 --- /dev/null +++ b/parser/expression/secondary/infix/assignment.lua @@ -0,0 +1,23 @@ +local infix = require("parser.expression.secondary.infix.infix") +local escape = require("common").escape + +local operator_priority = require("common").operator_priority + +local ast = require("ast") +local Identifier, Assignment = ast.Identifier, ast.Assignment + +return infix { + operator = "=", + identifier = "_=_", + priority = operator_priority["_=_"], + + -- return bool + match = function(self, str, current_priority, primary) + local escaped = escape(self.operator) + return self.priority > current_priority and str:match("^"..escaped) and Identifier:is(primary) + end, + + build_ast = function(self, left, right) + return Assignment:new(left, right) + end +} diff --git a/parser/expression/secondary/infix/assignment_call.lua b/parser/expression/secondary/infix/assignment_call.lua new file mode 100644 index 0000000..8501e3a --- /dev/null +++ b/parser/expression/secondary/infix/assignment_call.lua @@ -0,0 +1,24 @@ +local infix = require("parser.expression.secondary.infix.infix") +local escape = require("common").escape + +local operator_priority = require("common").operator_priority + +local ast = require("ast") +local Call = ast.Call + +return infix { + operator = "=", + identifier = "_=_", + priority = operator_priority["_=_"], + + -- return bool + match = function(self, str, current_priority, primary) + local escaped = escape(self.operator) + return self.priority > current_priority and str:match("^"..escaped) and Call:is(primary) + end, + + build_ast = function(self, left, right) + left.arguments:set_assignment(right) + return Call:new(left.func, left.arguments) -- recreate Call since we modified left.arguments + end, +} diff --git a/parser/expression/secondary/infix/assignment_with_infix.lua b/parser/expression/secondary/infix/assignment_with_infix.lua new file mode 100644 index 0000000..5a897cb --- /dev/null +++ b/parser/expression/secondary/infix/assignment_with_infix.lua @@ -0,0 +1,35 @@ +local ast = require("ast") +local Call, Identifier, ArgumentTuple = ast.Call, ast.Identifier, ast.ArgumentTuple + +local assignment = require("parser.expression.secondary.infix.assignment") +local assignment_call = require("parser.expression.secondary.infix.assignment_call") + +local infixes = require("common").regular_operators.infixes + +local generated = {} + +for _, infix in ipairs(infixes) do + local operator = infix[1].."=" + local identifier = "_=_" + local infix_identifier = "_"..infix[1].."_" + + table.insert(generated, assignment { + operator = operator, + identifier = identifier, + build_ast = function(self, left, right) + right = Call:new(Identifier:new(infix_identifier), ArgumentTuple:new(left, right)) + return assignment.build_ast(self, left, right) + end + }) + + table.insert(generated, assignment_call { + operator = operator, + identifier = identifier, + build_ast = function(self, left, right) + right = Call:new(Identifier:new(infix_identifier), ArgumentTuple:new(left, right)) + return assignment_call.build_ast(self, left, right) + end + }) +end + +return generated diff --git a/parser/expression/secondary/infix/call.lua b/parser/expression/secondary/infix/call.lua new file mode 100644 index 0000000..400aece --- /dev/null +++ b/parser/expression/secondary/infix/call.lua @@ -0,0 +1,28 @@ +local infix = require("parser.expression.secondary.infix.infix") +local escape = require("common").escape +local identifier = require("parser.expression.primary.identifier") + +local operator_priority = require("common").operator_priority + +local ast = require("ast") +local Call, ArgumentTuple = ast.Call, ast.ArgumentTuple + +return infix { + operator = "!", + identifier = "_!_", + priority = operator_priority["_!_"], + + match = function(self, str, current_priority, primary) + local escaped = escape(self.operator) + return self.priority > current_priority and str:match("^"..escaped) and identifier:match(str:match("^"..escaped.."%s*(.-)$")) + end, + + build_ast = function(self, left, right) + if Call:is(right) then + right.arguments:insert_positional(1, left) + return right + else + return Call:new(right, ArgumentTuple:new(left)) + end + end +} diff --git a/parser/expression/secondary/infix/choice.lua b/parser/expression/secondary/infix/choice.lua new file mode 100644 index 0000000..5590eee --- /dev/null +++ b/parser/expression/secondary/infix/choice.lua @@ -0,0 +1,17 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +local ast = require("ast") +local Call, Identifier, ArgumentTuple, ResumeParentFunction, ParameterTuple, Function = ast.Call, ast.Identifier, ast.ArgumentTuple, ast.ResumeParentFunction, ast.ParameterTuple, ast.Function + +return infix { + operator = "|>", + identifier = "_|>_", + priority = operator_priority["_|>_"], + + build_ast = function(self, left, right) + right = Function:new(ParameterTuple:new(), ResumeParentFunction:new(right)) + return Call:new(Identifier:new(self.identifier), ArgumentTuple:new(left, right)) + end +} diff --git a/parser/expression/secondary/infix/definition.lua b/parser/expression/secondary/infix/definition.lua new file mode 100644 index 0000000..3d89334 --- /dev/null +++ b/parser/expression/secondary/infix/definition.lua @@ -0,0 +1,22 @@ +local infix = require("parser.expression.secondary.infix.infix") +local escape = require("common").escape + +local operator_priority = require("common").operator_priority + +local ast = require("ast") +local Definition, Symbol = ast.Definition, ast.Symbol + +return infix { + operator = "=", + identifier = "_=_", + priority = operator_priority["_=_"], + + match = function(self, str, current_priority, primary) + local escaped = escape(self.operator) + return self.priority > current_priority and str:match("^"..escaped) and Symbol:is(primary) + end, + + build_ast = function(self, left, right) + return Definition:new(left, right) + end +} diff --git a/parser/expression/secondary/infix/different.lua b/parser/expression/secondary/infix/different.lua new file mode 100644 index 0000000..cd837a0 --- /dev/null +++ b/parser/expression/secondary/infix/different.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = "!=", + identifier = "_!=_", + priority = operator_priority["_!=_"] +} diff --git a/parser/expression/secondary/infix/division.lua b/parser/expression/secondary/infix/division.lua new file mode 100644 index 0000000..777401b --- /dev/null +++ b/parser/expression/secondary/infix/division.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = "/", + identifier = "_/_", + priority = operator_priority["_/_"] +} diff --git a/parser/expression/secondary/infix/equal.lua b/parser/expression/secondary/infix/equal.lua new file mode 100644 index 0000000..969a75f --- /dev/null +++ b/parser/expression/secondary/infix/equal.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = "==", + identifier = "_==_", + priority = operator_priority["_==_"] +} diff --git a/parser/expression/secondary/infix/exponent.lua b/parser/expression/secondary/infix/exponent.lua new file mode 100644 index 0000000..bbc1606 --- /dev/null +++ b/parser/expression/secondary/infix/exponent.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = "^", + identifier = "_^_", + priority = operator_priority["_^_"] +} diff --git a/parser/expression/secondary/infix/greater.lua b/parser/expression/secondary/infix/greater.lua new file mode 100644 index 0000000..8bafc4e --- /dev/null +++ b/parser/expression/secondary/infix/greater.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = ">", + identifier = "_>_", + priority = operator_priority["_>_"] +} diff --git a/parser/expression/secondary/infix/greater_equal.lua b/parser/expression/secondary/infix/greater_equal.lua new file mode 100644 index 0000000..cee293d --- /dev/null +++ b/parser/expression/secondary/infix/greater_equal.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = ">=", + identifier = "_>=_", + priority = operator_priority["_>=_"] +} diff --git a/parser/expression/secondary/infix/if.lua b/parser/expression/secondary/infix/if.lua new file mode 100644 index 0000000..d9e9934 --- /dev/null +++ b/parser/expression/secondary/infix/if.lua @@ -0,0 +1,9 @@ +local infix_quote_right = require("parser.expression.secondary.infix.infix_quote_right") + +local operator_priority = require("common").operator_priority + +return infix_quote_right { + operator = "~", + identifier = "_~_", + priority = operator_priority["_~_"] +} diff --git a/parser/expression/secondary/infix/implicit_multiplication.lua b/parser/expression/secondary/infix/implicit_multiplication.lua new file mode 100644 index 0000000..d0f20f1 --- /dev/null +++ b/parser/expression/secondary/infix/implicit_multiplication.lua @@ -0,0 +1,23 @@ +local infix = require("parser.expression.secondary.infix.infix") +local identifier = require("parser.expression.primary.identifier") + +local operator_priority = require("common").operator_priority + +local ast = require("ast") +local Call, Identifier, ArgumentTuple = ast.Call, ast.Identifier, ast.ArgumentTuple + +return infix { + operator = "*", + identifier = "_*_", + priority = operator_priority["_*_"]+.5, -- just above / so 1/2x gives 1/(2x) + + match = function(self, str, current_priority, primary) + return self.priority > current_priority and identifier:match(str) + end, + + parse = function(self, source, str, limit_pattern, current_priority, primary) + local start_source = source:clone() + local right, rem = identifier:parse(source, str, limit_pattern) + return Call:new(Identifier:new(self.identifier), ArgumentTuple:new(primary, right)):set_source(start_source), rem + end +} diff --git a/parser/expression/secondary/infix/index.lua b/parser/expression/secondary/infix/index.lua new file mode 100644 index 0000000..2f484e4 --- /dev/null +++ b/parser/expression/secondary/infix/index.lua @@ -0,0 +1,19 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +local ast = require("ast") +local Call, Identifier, ArgumentTuple = ast.Call, ast.Identifier, ast.ArgumentTuple + +return infix { + operator = ".", + identifier = "_._", + priority = operator_priority["_._"], + + build_ast = function(self, left, right) + if Identifier:is(right) then + right = right:to_string() + end + return Call:new(Identifier:new(self.identifier), ArgumentTuple:new(left, right)) + end +} diff --git a/parser/expression/secondary/infix/infix.lua b/parser/expression/secondary/infix/infix.lua new file mode 100644 index 0000000..8734aa1 --- /dev/null +++ b/parser/expression/secondary/infix/infix.lua @@ -0,0 +1,34 @@ +local secondary = require("parser.expression.secondary.secondary") +local escape = require("common").escape +local expression_to_ast = require("parser.expression.to_ast") + +local ast = require("ast") +local Call, Identifier, ArgumentTuple = ast.Call, ast.Identifier, ast.ArgumentTuple + +return secondary { + operator = nil, + identifier = nil, + priority = nil, + + -- return bool + match = function(self, str, current_priority, primary) + local escaped = escape(self.operator) + return self.priority > current_priority and str:match("^"..escaped) + end, + + -- return AST, rem + parse = function(self, source, str, limit_pattern, current_priority, primary) + local start_source = source:clone() + local escaped = escape(self.operator) + + local sright = source:consume(str:match("^("..escaped..")(.*)$")) + local s, right, rem = pcall(expression_to_ast, source, sright, limit_pattern, self.priority) + if not s then error(("invalid expression after binary operator %q: %s"):format(self.operator, right), 0) end + + return self:build_ast(primary, right):set_source(start_source), rem + end, + + build_ast = function(self, left, right) + return Call:new(Identifier:new(self.identifier), ArgumentTuple:new(left, right)) + end +} diff --git a/parser/expression/secondary/infix/infix_or_suffix.lua b/parser/expression/secondary/infix/infix_or_suffix.lua new file mode 100644 index 0000000..de6246a --- /dev/null +++ b/parser/expression/secondary/infix/infix_or_suffix.lua @@ -0,0 +1,32 @@ +-- same as infix, but skip if no valid expression after the operator instead of erroring +-- useful for operators that are both valid as infix and as suffix + +local infix = require("parser.expression.secondary.infix.infix") +local escape = require("common").escape +local expression_to_ast = require("parser.expression.to_ast") + +return infix { + -- returns exp, rem if expression found + -- returns nil if no expression found + search = function(self, source, str, limit_pattern, current_priority, operating_on_primary) + if not self:match(str, current_priority, operating_on_primary) then + return nil + end + return self:maybe_parse(source, str, limit_pattern, current_priority, operating_on_primary) + end, + + parse = function() error("no guaranteed parse for this operator") end, + + -- return AST, rem + -- return nil + maybe_parse = function(self, source, str, limit_pattern, current_priority, primary) + local start_source = source:clone() + local escaped = escape(self.operator) + + local sright = source:consume(str:match("^("..escaped..")(.*)$")) + local s, right, rem = pcall(expression_to_ast, source, sright, limit_pattern, self.priority) + if not s then return nil end + + return self:build_ast(primary, right):set_source(start_source), rem + end, +} diff --git a/parser/expression/secondary/infix/infix_quote_both.lua b/parser/expression/secondary/infix/infix_quote_both.lua new file mode 100644 index 0000000..2a97e66 --- /dev/null +++ b/parser/expression/secondary/infix/infix_quote_both.lua @@ -0,0 +1,12 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local ast = require("ast") +local Call, Identifier, ArgumentTuple, Quote = ast.Call, ast.Identifier, ast.ArgumentTuple, ast.Quote + +return infix { + build_ast = function(self, left, right) + left = Quote:new(left) + right = Quote:new(right) + return Call:new(Identifier:new(self.identifier), ArgumentTuple:new(left, right)) + end +} diff --git a/parser/expression/secondary/infix/infix_quote_right.lua b/parser/expression/secondary/infix/infix_quote_right.lua new file mode 100644 index 0000000..c743547 --- /dev/null +++ b/parser/expression/secondary/infix/infix_quote_right.lua @@ -0,0 +1,11 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local ast = require("ast") +local Call, Identifier, ArgumentTuple, Quote = ast.Call, ast.Identifier, ast.ArgumentTuple, ast.Quote + +return infix { + build_ast = function(self, left, right) + right = Quote:new(right) + return Call:new(Identifier:new(self.identifier), ArgumentTuple:new(left, right)) + end +} diff --git a/parser/expression/secondary/infix/integer_division.lua b/parser/expression/secondary/infix/integer_division.lua new file mode 100644 index 0000000..426814b --- /dev/null +++ b/parser/expression/secondary/infix/integer_division.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = "//", + identifier = "_//_", + priority = operator_priority["_//_"] +} diff --git a/parser/expression/secondary/infix/lower.lua b/parser/expression/secondary/infix/lower.lua new file mode 100644 index 0000000..a9d323c --- /dev/null +++ b/parser/expression/secondary/infix/lower.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = "<", + identifier = "_<_", + priority = operator_priority["_<_"] +} diff --git a/parser/expression/secondary/infix/lower_equal.lua b/parser/expression/secondary/infix/lower_equal.lua new file mode 100644 index 0000000..c951236 --- /dev/null +++ b/parser/expression/secondary/infix/lower_equal.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = "<=", + identifier = "_<=_", + priority = operator_priority["_<=_"] +} diff --git a/parser/expression/secondary/infix/modulo.lua b/parser/expression/secondary/infix/modulo.lua new file mode 100644 index 0000000..26bafbc --- /dev/null +++ b/parser/expression/secondary/infix/modulo.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = "%", + identifier = "_%_", + priority = operator_priority["_%_"] +} diff --git a/parser/expression/secondary/infix/multiplication.lua b/parser/expression/secondary/infix/multiplication.lua new file mode 100644 index 0000000..d71b4ec --- /dev/null +++ b/parser/expression/secondary/infix/multiplication.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = "*", + identifier = "_*_", + priority = operator_priority["_*_"] +} diff --git a/parser/expression/secondary/infix/or.lua b/parser/expression/secondary/infix/or.lua new file mode 100644 index 0000000..c113eed --- /dev/null +++ b/parser/expression/secondary/infix/or.lua @@ -0,0 +1,9 @@ +local infix_quote_right = require("parser.expression.secondary.infix.infix_quote_right") + +local operator_priority = require("common").operator_priority + +return infix_quote_right { + operator = "|", + identifier = "_|_", + priority = operator_priority["_|_"] +} diff --git a/parser/expression/secondary/infix/pair.lua b/parser/expression/secondary/infix/pair.lua new file mode 100644 index 0000000..64b63dc --- /dev/null +++ b/parser/expression/secondary/infix/pair.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = ":", + identifier = "_:_", + priority = operator_priority["_:_"] +} diff --git a/parser/expression/secondary/infix/semicolon.lua b/parser/expression/secondary/infix/semicolon.lua new file mode 100644 index 0000000..05cb73c --- /dev/null +++ b/parser/expression/secondary/infix/semicolon.lua @@ -0,0 +1,9 @@ +local infix_or_suffix = require("parser.expression.secondary.infix.infix_or_suffix") + +local operator_priority = require("common").operator_priority + +return infix_or_suffix { + operator = ";", + identifier = "_;_", + priority = operator_priority["_;_"] +} diff --git a/parser/expression/secondary/infix/substraction.lua b/parser/expression/secondary/infix/substraction.lua new file mode 100644 index 0000000..374e2cd --- /dev/null +++ b/parser/expression/secondary/infix/substraction.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = "-", + identifier = "_-_", + priority = operator_priority["_-_"] +} diff --git a/parser/expression/secondary/infix/tag.lua b/parser/expression/secondary/infix/tag.lua new file mode 100644 index 0000000..bb2a822 --- /dev/null +++ b/parser/expression/secondary/infix/tag.lua @@ -0,0 +1,9 @@ +local infix_quote_right = require("parser.expression.secondary.infix.infix_quote_right") + +local operator_priority = require("common").operator_priority + +return infix_quote_right { + operator = "#", + identifier = "_#_", + priority = operator_priority["_#_"] +} diff --git a/parser/expression/secondary/infix/tuple.lua b/parser/expression/secondary/infix/tuple.lua new file mode 100644 index 0000000..9f9c564 --- /dev/null +++ b/parser/expression/secondary/infix/tuple.lua @@ -0,0 +1,36 @@ +local infix = require("parser.expression.secondary.infix.infix") +local escape = require("common").escape +local expression_to_ast = require("parser.expression.to_ast") + +local operator_priority = require("common").operator_priority + +local ast = require("ast") +local Tuple = ast.Tuple + +return infix { + operator = ",", + identifier = "_,_", + priority = operator_priority["_,_"], + + -- reminder: this :parse method is also called from primary.list as an helper to build list bracket litterals + parse = function(self, source, str, limit_pattern, current_priority, primary) + local start_source = source:clone() + local l = Tuple:new() + l:insert(primary) + + local escaped = escape(self.operator) + local rem = str + while rem:match("^%s*"..escaped) do + rem = source:consume(rem:match("^(%s*"..escaped..")(.*)$")) + + local s, right + s, right, rem = pcall(expression_to_ast, source, rem, limit_pattern, self.priority) + if not s then error(("invalid expression after binop %q: %s"):format(self.operator, right), 0) end + + l:insert(right) + end + + l.explicit = false + return l:set_source(start_source), rem + end +} diff --git a/parser/expression/secondary/infix/type_check.lua b/parser/expression/secondary/infix/type_check.lua new file mode 100644 index 0000000..965045a --- /dev/null +++ b/parser/expression/secondary/infix/type_check.lua @@ -0,0 +1,9 @@ +local infix = require("parser.expression.secondary.infix.infix") + +local operator_priority = require("common").operator_priority + +return infix { + operator = "::", + identifier = "_::_", + priority = operator_priority["_::_"] +} diff --git a/parser/expression/secondary/infix/while.lua b/parser/expression/secondary/infix/while.lua new file mode 100644 index 0000000..748008f --- /dev/null +++ b/parser/expression/secondary/infix/while.lua @@ -0,0 +1,9 @@ +local infix_quote_both = require("parser.expression.secondary.infix.infix_quote_both") + +local operator_priority = require("common").operator_priority + +return infix_quote_both { + operator = "~?", + identifier = "_~?_", + priority = operator_priority["_~?_"] +} diff --git a/parser/expression/secondary/init.lua b/parser/expression/secondary/init.lua new file mode 100644 index 0000000..f43cfbd --- /dev/null +++ b/parser/expression/secondary/init.lua @@ -0,0 +1,78 @@ +--- try to parse a secondary expression + +local function r(name) + return require("parser.expression.secondary."..name), nil +end + +local secondaries = { + -- binary infix operators + -- 1 + r("infix.semicolon"), + -- 2 + r("infix.tuple"), + r("infix.tag"), + -- 4 + r("infix.while"), + r("infix.if"), + -- 6 + r("infix.choice"), + r("infix.and"), + r("infix.or"), + -- 7 + r("infix.equal"), + r("infix.different"), + r("infix.greater_equal"), + r("infix.lower_equal"), + r("infix.greater"), + r("infix.lower"), + -- 8 + r("infix.addition"), + r("infix.substraction"), + -- 9 + r("infix.multiplication"), + r("infix.integer_division"), + r("infix.division"), + r("infix.modulo"), + -- 9.5 + r("infix.implicit_multiplication"), + -- 10 + r("infix.exponent"), + -- 11 + r("infix.type_check"), + -- 12 + r("infix.call"), + -- 14 + r("infix.index"), + -- 3 + r("infix.assignment"), -- deported after equal + r("infix.assignment_call"), + r("infix.definition"), + -- 5 + r("infix.pair"), -- deported after type_check + + -- unary suffix operators + -- 1 + r("suffix.semicolon"), + -- 12 + r("suffix.exclamation_call"), + -- 13 + r("suffix.call"), +} + +-- add generated assignement+infix operator combos, before the rest +local assignment_operators = r("infix.assignment_with_infix") +for i, op in ipairs(assignment_operators) do + table.insert(secondaries, i, op) +end + +return { + -- returns exp, rem if expression found + -- returns nil if no expression found + -- returns nil, err if error + search = function(self, source, str, limit_pattern, current_priority, primary) + for _, secondary in ipairs(secondaries) do + local exp, rem = secondary:search(source, str, limit_pattern, current_priority, primary) + if exp then return exp, rem end + end + end +} diff --git a/parser/expression/secondary/secondary.lua b/parser/expression/secondary/secondary.lua new file mode 100644 index 0000000..c058cb2 --- /dev/null +++ b/parser/expression/secondary/secondary.lua @@ -0,0 +1,34 @@ +local class = require("class") + +return class { + new = false, -- static class + + -- returns exp, rem if expression found + -- returns nil if no expression found + search = function(self, source, str, limit_pattern, current_priority, operating_on_primary) + if not self:match(str, current_priority, operating_on_primary) then + return nil + end + return self:parse(source, str, limit_pattern, current_priority, operating_on_primary) + end, + -- return bool + -- (not needed if you redefined :search) + match = function(self, str, current_priority, operating_on_primary) + return false + end, + -- return AST, rem + -- (not needed if you redefined :search) + -- assumes that :match was checked before, and can not return nil (may error though) + parse = function(self, source, str, limit_pattern, current_priority, operating_on_primary) + error("unimplemented") + end, + + -- class helpers -- + + -- return AST, rem + expect = function(self, source, str, limit_pattern, current_priority, operating_on_primary) + local exp, rem = self:search(source, str, limit_pattern, current_priority, operating_on_primary) + if not exp then error(("expected %s but got %s"):format(self.type, str)) end + return exp, rem + end +} diff --git a/parser/expression/secondary/suffix/call.lua b/parser/expression/secondary/suffix/call.lua new file mode 100644 index 0000000..d456a82 --- /dev/null +++ b/parser/expression/secondary/suffix/call.lua @@ -0,0 +1,40 @@ +-- index/call + +local secondary = require("parser.expression.secondary.secondary") +local parenthesis = require("parser.expression.primary.parenthesis") + +local operator_priority = require("common").operator_priority + +local ast = require("ast") +local Call, ArgumentTuple, Tuple, Assignment, Nil = ast.Call, ast.ArgumentTuple, ast.Tuple, ast.Assignment, ast.Nil + +return secondary { + priority = operator_priority["_()"], + + match = function(self, str, current_priority, primary) + return self.priority > current_priority and parenthesis:match(str) + end, + + parse = function(self, source, str, limit_pattern, current_priority, primary) + local start_source = source:clone() + local args = ArgumentTuple:new() + + local exp, rem = parenthesis:parse(source, str, limit_pattern) + + if Nil:is(exp) then + exp = Tuple:new() + elseif not Tuple:is(exp) or exp.explicit then -- single argument + exp = Tuple:new(exp) + end + + for i, v in ipairs(exp.list) do + if Assignment:is(v) then + args:set_named(v.identifier, v.expression) + else + args:set_positional(i, v) + end + end + + return Call:new(primary, args):set_source(start_source), rem + end +} diff --git a/parser/expression/secondary/suffix/exclamation_call.lua b/parser/expression/secondary/suffix/exclamation_call.lua new file mode 100644 index 0000000..377426f --- /dev/null +++ b/parser/expression/secondary/suffix/exclamation_call.lua @@ -0,0 +1,15 @@ +local suffix = require("parser.expression.secondary.suffix.suffix") + +local operator_priority = require("common").operator_priority + +local ast = require("ast") +local Call, ArgumentTuple = ast.Call, ast.ArgumentTuple + +return suffix { + operator = "!", + priority = operator_priority["_!"], + + build_ast = function(self, left) + return Call:new(left, ArgumentTuple:new()) + end +} diff --git a/parser/expression/secondary/suffix/semicolon.lua b/parser/expression/secondary/suffix/semicolon.lua new file mode 100644 index 0000000..c962687 --- /dev/null +++ b/parser/expression/secondary/suffix/semicolon.lua @@ -0,0 +1,9 @@ +local suffix = require("parser.expression.secondary.suffix.suffix") + +local operator_priority = require("common").operator_priority + +return suffix { + operator = ";", + identifier = "_;", + priority = operator_priority["_;"] +} diff --git a/parser/expression/secondary/suffix/suffix.lua b/parser/expression/secondary/suffix/suffix.lua new file mode 100644 index 0000000..9bb67e6 --- /dev/null +++ b/parser/expression/secondary/suffix/suffix.lua @@ -0,0 +1,31 @@ +-- unary suffix operators, for example the ! in func! + +local secondary = require("parser.expression.secondary.secondary") +local escape = require("common").escape + +local ast = require("ast") +local Call, Identifier, ArgumentTuple = ast.Call, ast.Identifier, ast.ArgumentTuple + +return secondary { + operator = nil, + identifier = nil, + priority = nil, + + match = function(self, str, current_priority, primary) + local escaped = escape(self.operator) + return self.priority > current_priority and str:match("^"..escaped) + end, + + parse = function(self, source, str, limit_pattern, current_priority, primary) + local start_source = source:clone() + local escaped = escape(self.operator) + + local rem = source:consume(str:match("^("..escaped..")(.*)$")) + + return self:build_ast(primary):set_source(start_source), rem + end, + + build_ast = function(self, left) + return Call:new(Identifier:new(self.identifier), ArgumentTuple:new(left)) + end +} diff --git a/parser/expression/to_ast.lua b/parser/expression/to_ast.lua new file mode 100644 index 0000000..f3d0a9f --- /dev/null +++ b/parser/expression/to_ast.lua @@ -0,0 +1,50 @@ +--- transform an expression string into raw AST + +local primary, secondary + +local comment = require("parser.expression.comment") + +-- parse an expression, starting from a secondary element operating on operating_on_primary +-- returns expr, remaining +local function from_secondary(source, s, limit_pattern, current_priority, operating_on_primary) + s = source:consume(s:match("^(%s*)(.*)$")) + current_priority = current_priority or 0 + -- if there is a comment, restart the parsing after the comment ends + local c, c_rem = comment:search(source, s, limit_pattern) + if c then return from_secondary(source, c_rem, limit_pattern, current_priority, operating_on_primary) end + -- secondary elements + local exp, rem = secondary:search(source, s, limit_pattern, current_priority, operating_on_primary) + if exp then return from_secondary(source, rem, limit_pattern, current_priority, exp) end + -- nothing to apply on primary + return operating_on_primary, s +end + +--- parse an expression +-- current_priority: only elements of strictly higher priority will be parser +-- limit_pattern: set to a string pattern that will trigger the end of elements that would otherwise consume everything until end-of-line (pattern is not consumed) +-- fallback_exp: if no primary expression can be found, will return this instead. Used to avoid raising an error where an empty or comment-only expression is allowed. +-- return expr, remaining +local function expression_to_ast(source, s, limit_pattern, current_priority, fallback_exp) + s = source:consume(s:match("^(%s*)(.*)$")) + current_priority = current_priority or 0 + -- if there is a comment, restart the parsing after the comment ends + local c, c_rem = comment:search(source, s, limit_pattern) + if c then return expression_to_ast(source, c_rem, limit_pattern, current_priority, fallback_exp) end + -- primary elements + local exp, rem = primary:search(source, s, limit_pattern) + if exp then return from_secondary(source, rem, limit_pattern, current_priority, exp) end + -- no valid primary expression + if fallback_exp then return fallback_exp, s end + error(("no valid expression before %q"):format(s), 0) +end + +package.loaded[...] = expression_to_ast + +primary = require("parser.expression.primary") +secondary = require("parser.expression.secondary") + +-- return expr, remaining +return function(source, s, limit_pattern, current_priority, operating_on_primary, fallback_exp) + if operating_on_primary then return from_secondary(source, s, limit_pattern, current_priority, operating_on_primary) + else return expression_to_ast(source, s, limit_pattern, current_priority, fallback_exp) end +end diff --git a/parser/init.lua b/parser/init.lua new file mode 100644 index 0000000..2938087 --- /dev/null +++ b/parser/init.lua @@ -0,0 +1,13 @@ +local code_to_tree = require("parser.code_to_tree") +local tree_to_ast = require("parser.tree_to_ast") + +-- parse code (string) with the associated source (Source) +-- the returned AST tree is stateless and can be stored/evaluated/etc as you please +return function(code, source) + local tree = code_to_tree(code, source) + local block = tree_to_ast(tree) + + block:prepare() + + return block +end diff --git a/parser/postparser.lua b/parser/postparser.lua deleted file mode 100644 index 2a89ba9..0000000 --- a/parser/postparser.lua +++ /dev/null @@ -1,110 +0,0 @@ -local expression -local parse_text - --- * true: if success --- * nil, error: in case of error -local function parse(state) - -- expression parsing - for i=#state.queued_lines, 1, -1 do - local l = state.queued_lines[i] - local line, namespace = l.line, l.namespace - -- default arguments and type constraints - if line.type == "function" then - for _, param in ipairs(line.params) do - -- get type constraints - if param.type_constraint then - local type_exp, rem = expression(param.type_constraint, state, namespace, line.source) - if not type_exp then return nil, ("in type constraint, %s; at %s"):format(rem, line.source) end - if rem:match("[^%s]") then - return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param.full_name, rem, line.source) - end - state.variable_metadata[param.full_name].constraint = { pending = type_exp } - end - -- get default value - if param.default then - local default_exp, rem = expression(param.default, state, namespace, line.source) - if not default_exp then return nil, ("in default value, %s; at %s"):format(rem, line.source) end - if rem:match("[^%s]") then - return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param.full_name, rem, line.source) - end - param.default = default_exp - -- extract type constraint from default value - if default_exp.type == "function call" and default_exp.called_name == "_::_" then - state.variable_metadata[param.full_name].constraint = { pending = default_exp.argument.expression.right } - end - end - end - -- assignment argument - if line.assignment and line.assignment.type_constraint then - local type_exp, rem = expression(line.assignment.type_constraint, state, namespace, line.source) - if not type_exp then return nil, ("in type constraint, %s; at %s"):format(rem, line.source) end - if rem:match("[^%s]") then - return nil, ("unexpected characters after parameter %q: %q; at %s"):format(line.assignment.full_name, rem, line.source) - end - state.variable_metadata[line.assignment.full_name].constraint = { pending = type_exp } - end - -- get list of scoped variables - -- (note includes every variables in the namespace of subnamespace, so subfunctions are scoped alongside this function) - if line.scoped then - line.scoped = {} - for name in pairs(state.variables) do - if name:sub(1, #namespace) == namespace then - if state.variable_metadata[name].persistent then return nil, ("variable %q can not be persistent as it is in a scoped function"):format(name) end - table.insert(line.scoped, name) - end - end - end - -- get list of properties - -- (unlike scoped, does not includes subnamespaces) - if line.properties then - line.properties = {} - for name in pairs(state.variables) do - if name:sub(1, #namespace) == namespace and not name:sub(#namespace+1):match("%.") then - table.insert(line.properties, name) - end - end - end - end - -- expressions - if line.expression and type(line.expression) == "string" then - local exp, rem = expression(line.expression, state, namespace, line.source) - if not exp then return nil, ("%s; at %s"):format(rem, line.source) end - if rem:match("[^%s]") then return nil, ("expected end of expression before %q; at %s"):format(rem, line.source) end - line.expression = exp - -- variable pending definition: expression will be evaluated when variable is needed - if line.type == "definition" then - state.variables[line.name].value.expression = line.expression - -- parse constraints - if line.constraint then - local type_exp, rem2 = expression(line.constraint, state, namespace, line.source) - if not type_exp then return nil, ("in type constraint, %s; at %s"):format(rem2, line.source) end - if rem2:match("[^%s]") then - return nil, ("unexpected characters after variable %q: %q; at %s"):format(line.name, rem2, line.source) - end - state.variable_metadata[line.name].constraint = { pending = type_exp } - end - end - end - -- text (text & choice lines) - if line.text then - local txt, err = parse_text(line.text, state, namespace, "text", "#~", true) - if not txt then return nil, ("%s; at %s"):format(err, line.source) end - if err:match("[^%s]") then return nil, ("expected end of expression in end-of-text expression before %q"):format(err) end - line.text = txt - end - table.remove(state.queued_lines, i) - end - if #state.queued_lines > 0 then -- lines were added during post-parsing, process these - return parse(state) - else - return true - end -end - -package.loaded[...] = parse -expression = require((...):gsub("postparser$", "expression")) -local common = require((...):gsub("postparser$", "common")) -parse_text = common.parse_text - ---- postparse shit: parse expressions and do variable existence and type checking -return parse diff --git a/parser/preparser.lua b/parser/preparser.lua deleted file mode 100644 index 051be01..0000000 --- a/parser/preparser.lua +++ /dev/null @@ -1,569 +0,0 @@ -local format_identifier, identifier_pattern, escape, special_functions_names, pretty_signature, signature, copy, injections - -local parse_indented - ---- try to define an alias using rem, the text that follows the identifier --- returns true, new_rem, alias_name in case of success --- returns true, rem in case of no alias and no error --- returns nil, err in case of alias and error -local function maybe_alias(rem, fqm, namespace, line, state) - local alias - if rem:match("^%:[^%:%=]") then - local param_content = rem:sub(2) - alias, rem = param_content:match("^("..identifier_pattern..")(.-)$") - if not alias then return nil, ("expected an identifier in alias, but got %q; at %s"):format(param_content, line.source) end - alias = format_identifier(alias) - -- format alias - local aliasfqm = ("%s%s"):format(namespace, alias) - -- define alias - if state.aliases[aliasfqm] ~= nil and state.aliases[aliasfqm] ~= fqm then - return nil, ("trying to define alias %q for %q, but already exist and refer to %q; at %s"):format(aliasfqm, fqm, state.aliases[aliasfqm], line.source) - end - state.aliases[aliasfqm] = fqm - end - return true, rem, alias -end - ---- inject lines defined for the injection that match parent_function type and inject_type in inject_in starting from index inject_at -local function inject(state, parent_function, inject_type, inject_in, inject_at) - inject_at = inject_at or #inject_in+1 - local prefix - if parent_function.subtype == "checkpoint" then - prefix = "checkpoint" - elseif parent_function.subtype == "class" then - prefix = "class" - elseif parent_function.scoped then - prefix = "scoped_function" - else - prefix = "function" - end - local ninject = ("%s_%s"):format(prefix, inject_type) - if state.inject[ninject] then - for i, ll in ipairs(state.inject[ninject]) do - table.insert(inject_in, inject_at+i-1, copy(ll)) - end - end -end - ---- parse a single line into AST --- * ast: if success --- * nil, error: in case of error -local function parse_line(line, state, namespace, parent_resumable, in_scoped) - local l = line.content - local r = { - source = line.source - } - -- else-condition, condition & while - if l:match("^~[~%?]?") then - if l:match("^~~") then - r.type = "else-condition" - elseif l:match("^~%?") then - r.type = "while" - else - r.type = "condition" - end - r.child = true - local expr = l:match("^~[~%?]?(.*)$") - if expr:match("[^%s]") then - r.expression = expr - else - r.expression = "1" - end - -- choice - elseif l:match("^>") then - r.type = "choice" - r.child = true - r.text = l:match("^>%s*(.-)$") - -- definition - elseif l:match("^:") then - local lr = l:match("^:(.*)$") - -- immediately run variable - local run_immediately = false - if lr:match("^~") then - lr = lr:match("^~(.*)$") - run_immediately = true - end - -- function & checkpoint - if lr:match("^%$") or lr:match("^%!") or lr:match("^%%") then -- § is a 2-bytes caracter, DO NOT USE LUA PATTERN OPERATORS as they operate on single bytes - r.type = "function" - r.child = true - -- subtype options - local allow_params = true - local allow_assign = true - local keep_in_ast = false - if lr:match("^%$") then - r.subtype = "function" - r.resumable = true - elseif lr:match("^%%") then - r.subtype = "class" - r.resumable = true - r.properties = true - allow_params = false - allow_assign = false - elseif lr:match("^%!") then - r.subtype = "checkpoint" - allow_params = false - allow_assign = false - keep_in_ast = true - if not parent_resumable then return nil, ("checkpoint definition line is not in a function; at %s"):format(line.source) end - r.parent_resumable = parent_resumable -- store parent resumable function and run checkpoint when line is read - else - error("unknown function line type") - end - -- don't keep function node in block AST - if not keep_in_ast then - r.remove_from_block_ast = true - end - -- lua function - if r.subtype == "function" and state.global_state.link_next_function_definition_to_lua_function then - r.lua_function = state.global_state.link_next_function_definition_to_lua_function - state.global_state.link_next_function_definition_to_lua_function = nil - end - -- get identifier - local lc = lr:match("^[%$%%%!](.-)$") - local identifier, rem = lc:match("^("..identifier_pattern..")(.-)$") - if not identifier then - for _, name in ipairs(special_functions_names) do - identifier, rem = lc:match("^(%s*"..escape(name).."%s*)(.-)$") - if identifier then break end - end - end - if not identifier then - return nil, ("no valid identifier in function definition line %q; at %s"):format(lc, line.source) - end - -- format identifier - local fqm = ("%s%s"):format(namespace, format_identifier(identifier)) - local func_namespace = fqm .. "." - -- get alias - local ok_alias - ok_alias, rem = maybe_alias(rem, fqm, namespace, line, state) - if not ok_alias then return ok_alias, rem end - -- anything else are argument, isolate function it its own namespace - -- (to not mix its args and variables with the main variant) - if rem:match("[^%s]") then - func_namespace = ("%s(%s)."):format(fqm, tostring(r)) - r.private_namespace = true - end - -- define function - if state.variables[fqm] then return nil, ("trying to define %s %s, but a variable with the same name exists; at %s"):format(r.type, fqm, line.source) end - r.namespace = func_namespace - r.name = fqm - -- get params - r.params = {} - if allow_params and rem:match("^%b()") then - r.scoped = true - local content - content, rem = rem:match("^(%b())%s*(.*)$") - content = content:gsub("^%(", ""):gsub("%)$", "") - for param in content:gmatch("[^%,]+") do - -- get identifier - local param_identifier, param_rem = param:match("^("..identifier_pattern..")(.-)$") - if not param_identifier then return nil, ("no valid identifier in function parameter %q; at %s"):format(param, line.source) end - param_identifier = format_identifier(param_identifier) - -- format identifier - local param_fqm = ("%s%s"):format(func_namespace, param_identifier) - -- get alias - local ok_param_alias, param_alias - ok_param_alias, param_rem, param_alias = maybe_alias(param_rem, param_fqm, func_namespace, line, state) - if not ok_param_alias then return ok_param_alias, param_rem end - -- get potential type constraints and default value - local type_constraint, default - if param_rem:match("^::") then - type_constraint = param_rem:match("^::(.*)$") - elseif param_rem:match("^=") then - default = param_rem:match("^=(.*)$") - elseif param_rem:match("[^%s]") then - return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param_fqm, param_rem, line.source) - end - -- add parameter - table.insert(r.params, { name = param_identifier, alias = param_alias, full_name = param_fqm, type_constraint = type_constraint, default = default, vararg = nil }) - end - end - -- get assignment param - if allow_assign and rem:match("^%:%=") then - local param = rem:match("^%:%=(.*)$") - -- get identifier - local param_identifier, param_rem = param:match("^("..identifier_pattern..")(.-)$") - if not param_identifier then return nil, ("no valid identifier in function parameter %q; at %s"):format(param, line.source) end - param_identifier = format_identifier(param_identifier) - -- format identifier - local param_fqm = ("%s%s"):format(func_namespace, param_identifier) - -- get alias - local ok_param_alias, param_alias - ok_param_alias, param_rem, param_alias = maybe_alias(param_rem, param_fqm, func_namespace, line, state) - if not ok_param_alias then return ok_param_alias, param_rem end - -- get potential type constraint - local type_constraint - if param_rem:match("^::") then - type_constraint = param_rem:match("^::(.*)$") - elseif param_rem:match("[^%s]") then - return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param_fqm, param_rem, line.source) - end - -- add parameter - r.assignment = { name = param_identifier, alias = param_alias, full_name = param_fqm, type_constraint = type_constraint, default = nil, vararg = nil } - elseif rem:match("[^%s]") then - return nil, ("expected end-of-line at end of function definition line, but got %q; at %s"):format(rem, line.source) - end - -- calculate arity - local minarity, maxarity = #r.params, #r.params - for _, param in ipairs(r.params) do -- params with default values - if param.default then - minarity = minarity - 1 - end - end - -- varargs - if maxarity > 0 and r.params[maxarity].full_name:match("%.%.%.$") then - r.params[maxarity].name = r.params[maxarity].name:match("^(.*)%.%.%.$") - r.params[maxarity].full_name = r.params[maxarity].full_name:match("^(.*)%.%.%.$") - r.params[maxarity].vararg = true - minarity = minarity - 1 - maxarity = math.huge - end - r.arity = { minarity, maxarity } - r.signature = signature(r) - r.pretty_signature = pretty_signature(r) - -- check for signature conflict with functions with the same fqm - if state.functions[fqm] then - for _, variant in ipairs(state.functions[fqm]) do - if r.signature == variant.signature then - return nil, ("trying to define %s %s, but another function with same signature %s exists; at %s"):format(r.type, r.pretty_signature, variant.pretty_signature, line.source) - end - end - end - -- define variables - if not line.children then line.children = {} end - local scoped = in_scoped or r.scoped - -- define 👁️ variable - local seen_alias = state.global_state.builtin_aliases["👁️"] - table.insert(line.children, 1, { content = (":%s👁️%s=0"):format(scoped and "" or "@", seen_alias and ":"..seen_alias or ""), source = line.source }) - -- define 🔖 variable - if r.resumable then - local checkpoint_alias = state.global_state.builtin_aliases["🔖"] - table.insert(line.children, 1, { content = (":%s🔖%s=()"):format(scoped and "" or "@", checkpoint_alias and ":"..checkpoint_alias or ""), source = line.source }) - end - -- define 🏁 variable - if r.subtype == "checkpoint" then - local reached_alias = state.global_state.builtin_aliases["🏁"] - table.insert(line.children, 1, { content = (":%s🏁%s=0"):format(scoped and "" or "@", reached_alias and ":"..reached_alias or ""), source = line.source }) - end - -- custom code injection - inject(state, r, "start", line.children, 2) - inject(state, r, "end", line.children) - -- update 👁️ variable - table.insert(line.children, { content = "~👁️+=1", source = line.source }) - -- define args - for _, param in ipairs(r.params) do - if not state.variables[param.full_name] then - state.variables[param.full_name] = { - type = "undefined argument", - value = nil - } - state.variable_metadata[param.full_name] = {} - else - return nil, ("trying to define parameter %q, but a variable with the same name exists; at %s"):format(param.full_name, line.source) - end - end - if r.assignment then - if not state.variables[r.assignment.full_name] then - state.variables[r.assignment.full_name] = { - type = "undefined argument", - value = nil - } - state.variable_metadata[r.assignment.full_name] = {} - else - return nil, ("trying to define parameter %q, but a variable with the same name exists; at %s"):format(r.assignment.full_name, line.source) - end - end - -- define new function, no other variant yet - if not state.functions[fqm] then - state.functions[fqm] = { r } - -- overloading - else - table.insert(state.functions[fqm], r) - end - -- variable and constants - else - r.type = "definition" - r.remove_from_block_ast = true - local rem = lr - -- check if constant - if rem:match("^:") then - rem = rem:match("^:(.*)$") - r.constant = true - elseif rem:match("^@") then - rem = rem:match("^@(.*)$") - r.persistent = true - end - -- get identifier - local identifier - identifier, rem = rem:match("^("..identifier_pattern..")(.-)$") - if not identifier then return nil, ("no valid identifier at start of definition line %q; at %s"):format(l, line.source) end - -- format identifier - local fqm = ("%s%s"):format(namespace, format_identifier(identifier)) - -- get alias - local ok_alias - ok_alias, rem = maybe_alias(rem, fqm, namespace, line, state) - if not ok_alias then return ok_alias, rem end - -- type constraint - if rem:match("^::(.-)=") then - r.constraint, rem = rem:match("^::%s*(.-)%s*(=.*)$") - end - -- get expression - local exp = rem:match("^=(.*)$") - if not exp then return nil, ("expected \"= expression\" after %q in definition line; at %s"):format(rem, line.source) end - -- define identifier - if state.functions[fqm] then return nil, ("trying to define variable %q, but a function with the same name exists; at %s"):format(fqm, line.source) end - if state.variables[fqm] then - if state.variables[fqm].type == "pending definition" then - return nil, ("trying to define variable %q but it is already defined at %s; at %s"):format(fqm, state.variables[fqm].value.source, line.source) - else - return nil, ("trying to define variable %q but it is already defined; at %s"):format(fqm, line.source) - end - end - r.name = fqm - r.expression = exp - state.variables[fqm] = { type = "pending definition", value = { expression = nil, source = r.source } } - state.variable_metadata[fqm] = {} - if r.constant then state.variable_metadata[fqm].constant = true end - if r.persistent then state.variable_metadata[fqm].persistent = true end - end - -- add expression line after to perform the immediate execution - if run_immediately then - line.line_after = { content = "~ "..r.name, source = line.source } - end - -- tag - elseif l:match("^%#") then - r.type = "tag" - r.child = true - local expr = l:match("^%#(.*)$") - r.expression = ("{%s}"):format(expr) - -- return - elseif l:match("^%@") then - if not parent_resumable then return nil, ("return line is not in a function; at %s"):format(line.source) end - r.type = "return" - r.child = true - local expr = l:match("^%@(.*)$") - if expr:match("[^%s]") then - r.expression = expr - else - r.expression = "()" - end - -- custom code injection - if not line.children then line.children = {} end - inject(state, parent_resumable, "return", line.children) - -- update 👁️ variable - table.insert(line.children, { content = "~👁️+=1", source = line.source }) - -- text - elseif l:match("[^%s]") then - r.type = "text" - r.text = l - -- flush events - else - r.type = "flush events" - end - if not r.type then return nil, ("unknown line %s type"):format(line.source) end - return r -end - ---- parse an indented into final AST --- * block: in case of success --- * nil, err: in case of error -local function parse_block(indented, state, namespace, parent_resumable, in_scoped) - local block = { type = "block" } - for i, l in ipairs(indented) do - -- parsable line - local ast, err = parse_line(l, state, namespace, parent_resumable, in_scoped) - if err then return nil, err end - -- add to block AST - if not ast.remove_from_block_ast then - ast.parent_block = block - -- add ast node - ast.parent_position = #block+1 - table.insert(block, ast) - end - -- add child - if ast.child then ast.child = { type = "block", parent_line = ast } end - -- queue in expression evalution - table.insert(state.queued_lines, { namespace = ast.namespace or namespace, line = ast }) - - -- indented block - if l.children then - if not ast.child then - return nil, ("line %s (%s) can't have children"):format(ast.source, ast.type) - else - local r, e = parse_block(l.children, state, ast.namespace or namespace, (ast.type == "function" and ast.resumable) and ast or parent_resumable, (ast.type == "function" and ast.scoped) or in_scoped) - if not r then return r, e end - r.parent_line = ast - ast.child = r - end - end - - -- insert line after - if l.line_after then - table.insert(indented, i+1, l.line_after) - end - end - return block -end - --- returns new_indented -local function transform_indented(indented) - local i = 1 - while i <= #indented do - local l = indented[i] - -- comment - if l.content:match("^%(") then - table.remove(indented, i) - else - i = i + 1 - -- indented block - if l.children then - transform_indented(l.children) - end - end - end - return indented -end - ---- returns the nested list of lines {content="", line=1, children={lines...} or nil}, parsing indentation --- multiple empty lines are merged --- * list, last line, insert_empty_line: in case of success --- * nil, err: in case of error -local function parse_indent(lines, source, i, indentLevel, insert_empty_line) - i = i or 1 - indentLevel = indentLevel or 0 - local indented = {} - while i <= #lines do - if lines[i]:match("[^%s]") then - local indent, line = lines[i]:match("^(%s*)(.*)$") - if #indent == indentLevel then - if insert_empty_line then - table.insert(indented, { content = "", source = ("%s:%s"):format(source, insert_empty_line) }) - insert_empty_line = false - end - table.insert(indented, { content = line, source = ("%s:%s"):format(source, i) }) - elseif #indent > indentLevel then - if #indented == 0 then - return nil, ("unexpected indentation; at %s:%s"):format(source, i) - else - local t - t, i, insert_empty_line = parse_indent(lines, source, i, #indent, insert_empty_line) - if not t then return nil, i end - indented[#indented].children = t - end - else - return indented, i-1, insert_empty_line - end - elseif not insert_empty_line then - insert_empty_line = i - end - i = i + 1 - end - return indented, i-1, insert_empty_line -end - ---- return the list of raw lines of s -local function parse_lines(s) - local lines = {} - for l in (s.."\n"):gmatch("(.-)\n") do - table.insert(lines, l) - end - return lines -end - ---- make indented from intial string --- * list: in case of success --- * nil, err: in case of error -parse_indented = function(s, fnname, source) - source = source or fnname - -- parse lines - local lines = parse_lines(s) - local indented, e = parse_indent(lines, source) - if not indented then return nil, e end - -- wrap in named function if neccessary - if fnname ~= nil and fnname ~= "" then - if not fnname:match("^"..identifier_pattern.."$") then - return nil, ("invalid function name %q"):format(fnname) - end - indented = { - { content = ":$ "..fnname, source = ("%s:%s"):format(source, 0), children = indented }, - } - end - -- transform ast - indented = transform_indented(indented) - return indented -end - ---- preparse shit: create AST structure, define variables and functions, but don't parse expression or perform any type checking --- (wait for other files to be parsed before doing this with postparse) --- * block: in case of success --- * nil, err: in case of error -local function parse(state, s, name, source) - -- get indented - local indented, e = parse_indented(s, name, source) - if not indented then return nil, e end - -- build state proxy - local state_proxy = { - inject = {}, - aliases = setmetatable({}, { __index = state.aliases }), - variable_metadata = setmetatable({}, { __index = state.variable_metadata }), - variables = setmetatable({}, { __index = state.aliases }), - functions = setmetatable({}, { - __index = function(self, key) - if state.functions[key] then - local t = {} -- need to copy to allow ipairs over variants - for k, v in ipairs(state.functions[key]) do - t[k] = v - end - self[key] = t - return t - end - return nil - end - }), - queued_lines = {}, - global_state = state - } - -- parse injects - for tinject, ninject in pairs(injections) do - if state.inject[ninject] then - local inject_indented, err = parse_indented(state.inject[ninject], nil, "injected "..tinject) - if not inject_indented then return nil, err end - state_proxy.inject[ninject] = inject_indented - end - end - -- parse - local root, err = parse_block(indented, state_proxy, "") - if not root then return nil, err end - -- merge back state proxy into global state - for k,v in pairs(state_proxy.aliases) do - state.aliases[k] = v - end - for k,v in pairs(state_proxy.variable_metadata) do - state.variable_metadata[k] = v - end - for k,v in pairs(state_proxy.variables) do - state.variables[k] = v - end - for k,v in pairs(state_proxy.functions) do - if not state.functions[k] then - state.functions[k] = v - else - for i,w in ipairs(v) do - state.functions[k][i] = w - end - end - end - for _,l in ipairs(state_proxy.queued_lines) do - table.insert(state.queued_lines, l) - end - -- return block - return root -end - -package.loaded[...] = parse -local common = require((...):gsub("preparser$", "common")) -format_identifier, identifier_pattern, escape, special_functions_names, pretty_signature, signature, injections = common.format_identifier, common.identifier_pattern, common.escape, common.special_functions_names, common.pretty_signature, common.signature, common.injections -copy = require((...):gsub("parser%.preparser$", "common")).copy - -return parse diff --git a/parser/tree_to_ast.lua b/parser/tree_to_ast.lua new file mode 100644 index 0000000..44412b6 --- /dev/null +++ b/parser/tree_to_ast.lua @@ -0,0 +1,52 @@ +--- transform a tree of lines into raw AST + +local tree_to_block + +local ast = require("ast") +local Block, Flush, AttachBlock + +local expression_to_ast = require("parser.expression.to_ast") + +-- wrapper for expression_to_ast to check that there is no crap remaining after the expression has been parsed +-- return AST +local function expect_end(exp, rem) + if rem:match("[^%s]") then + error(("expected end of expression before %q"):format(rem)) + end + return exp +end +local function expect_end_block(exp, rem) + if rem:match("[^%s]") and not rem:match("^ ?_$") then + error(("expected end of expression before %q"):format(rem)) + end + return exp +end + +-- return AST +local function line_to_expression(content, tree) + if #tree > 0 then + local child_block = tree_to_block(tree) + return AttachBlock:new(expect_end_block(expression_to_ast(tree.source:clone(), content.." _", " _$")), child_block):set_source(tree.source) + else + return expect_end(expression_to_ast(tree.source:clone(), content, nil, nil, nil, Flush:new())):set_source(tree.source) + end +end + +-- return AST (Block) +tree_to_block = function(tree) + local block = Block:new() + + for _, l in ipairs(tree) do + local s, expression = pcall(line_to_expression, l.content, l) + if not s then error(("%s; at %s"):format(expression, l.source), 0) end + + block:add(expression) + end + + return block +end + +package.loaded[...] = tree_to_block +Block, Flush, AttachBlock = ast.Block, ast.Flush, ast.AttachBlock + +return tree_to_block diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..2dbc974 --- /dev/null +++ b/readme.md @@ -0,0 +1,3 @@ +# Anselme + +The overengineered dialog scripting system in pure Lua. diff --git a/state/ScopeStack.lua b/state/ScopeStack.lua new file mode 100644 index 0000000..90639c8 --- /dev/null +++ b/state/ScopeStack.lua @@ -0,0 +1,159 @@ +-- The current scope stack. One scope stack per State branch. +-- Only the scope currently on top of the stack will be used by the running script. + +local class = require("class") +local ast = require("ast") +local to_anselme = require("common.to_anselme") + +local LuaFunction, Environment, Node + +local parameter_tuple = require("parser.expression.contextual.parameter_tuple") +local symbol = require("parser.expression.primary.symbol") + +local ScopeStack = class { + state = nil, + + stack = nil, -- stack of Environment + current = nil, -- Environment + + initial_size = nil, -- number, size of the stack at creation (i.e. the "global" scopes only) + + init = function(self, state, branch_from_state) + self.state = state + self.stack = {} + if branch_from_state then + -- load existing environments from branched from state instead of creating new ones + for _, env in ipairs(branch_from_state.scope.stack) do + self:push(env) + end + else + self:push_export() -- root scope is the global scope, stuff can be exported there + self:push() -- for non-exported variables + end + self.initial_size = #self.stack + end, + + -- store all changed variables back into the main branch + merge = function(self) + local cache = {} + for _, env in ipairs(self.stack) do + env:merge(self.state, cache) + end + end, + + -- helper to define stuff from lua easily in the current scope + -- for lua functions: define_lua("name", "(x, y, z=5)", function(x, y, z) ... end), where arguments and return values of the function are automatically converted between anselme and lua values + -- for other lua values: define_lua("name", value) + -- for anselme AST: define_lua("name", value) + -- name can be prefixed with symbol modifiers, for example ":name" for a constant variable + -- if `raw_mode` is true, no anselme-to/from-lua conversion will be performed in the function + -- the function will receive the state followed by AST nodes as arguments, and is expected to return an AST node + define_lua = function(self, name, value, func, raw_mode) + local source = require("parser.Source"):new() + local sym = symbol:parse(source, (":%s"):format(name)) + if func then + local parameters = parameter_tuple:parse(source, value) + if not raw_mode then + local original_func = func + func = function(state, ...) + local lua_args = {} + for _, arg in ipairs{...} do + table.insert(lua_args, arg:to_lua(state)) + end + return to_anselme(original_func(table.unpack(lua_args))) + end + end + self:define_overloadable(sym, LuaFunction:new(parameters, func):eval(self.state)) + elseif Node:is(value) then + self:define(sym, value) + else + self:define(sym, to_anselme(value)) + end + end, + + -- methods that call the associated method from the current scope, see ast.Environment for details + define = function(self, symbol, exp) self.current:define(self.state, symbol, exp) end, + define_overloadable = function(self, symbol, exp) return self.current:define_overloadable(self.state, symbol, exp) end, + defined = function(self, identifier) return self.current:defined(self.state, identifier) end, + defined_in_current = function(self, symbol) return self.current:defined_in_current(self.state, symbol) end, + set = function(self, identifier, exp) self.current:set(self.state, identifier, exp) end, + get = function(self, identifier) return self.current:get(self.state, identifier) end, + depth = function(self) return self.current:depth() end, + + -- push new scope + -- if environment is given, it will be used instead of creating a new children of the current environment + push = function(self, environment) + local env + if environment then + env = environment + else + env = Environment:new(self.state, self.current) + end + table.insert(self.stack, env) + self.current = env + end, + -- push a partial layer on the current scope + -- this is used to shadow or temporarly define specific variable in the current scope + -- a partial layer is considered to be part of the current scope when looking up and defining variables, and any + -- other variable will still be defined in the current scope + -- ... is a list of identifiers + -- (still use :pop to pop it though) + push_partial = function(self, ...) + local is_partial = {} + for _, id in ipairs{...} do is_partial[id.name] = true end + local env = Environment:new(self.state, self.current, is_partial) + self:push(env) + end, + -- push an export layer on the current scope + -- this is where any exported variable defined on this or children scope will end up being defined + -- note: non-exported variables will not be defined in such a layer, make sure to add a normal layer on top as needed + -- still need to be :pop'd + push_export = function(self) + local env = Environment:new(self.state, self.current, nil, true) + self:push(env) + end, + -- push the global scope on top of the stack + push_global = function(self) + self:push(self.stack[self.initial_size]) + end, + -- pop current scope + pop = function(self) + table.remove(self.stack) + self.current = self.stack[#self.stack] + assert(self.current, "popped the root scope!") + end, + -- get the size of the stack + size = function(self) + return #self.stack + end, + -- pop all the scopes until only n are left (by default, only keep the global scopes) + reset = function(self, n) + n = n or self.initial_size + while self.stack[n+1] do + self:pop() + end + end, + + -- return current environment, to use with :push later (mostly for closures) + -- reminder: scopes are mutable + capture = function(self) + return self.current + end, + + -- return a table { [symbol] = value } of persistent variables defined on the root scope on this branch + list_persistent_global = function(self) + local env = self.stack[1] + return env:list_persistent(self.state) + end, + + _debug_state = function(self, filter) + filter = filter or "" + local s = "current branch id: "..self.state.branch_id.."\n" + return s .. table.concat(self.current:_debug_state(self.state, filter), "\n") + end +} + +package.loaded[...] = ScopeStack +LuaFunction, Environment, Node = ast.LuaFunction, ast.Environment, ast.abstract.Node + +return ScopeStack diff --git a/state/State.lua b/state/State.lua new file mode 100644 index 0000000..609a686 --- /dev/null +++ b/state/State.lua @@ -0,0 +1,229 @@ +--- Contains all state relative to an Anselme interpreter. Each State is fully independant from each other. +-- Each State can run a single script at a time, and variable changes are isolated between each State (see [branching](#branching-and-merging)). + +local class = require("class") +local ScopeStack = require("state.ScopeStack") +local tag_manager = require("state.tag_manager") +local event_manager = require("state.event_manager") +local resumable_manager = require("state.resumable_manager") +local uuid = require("common").uuid +local parser = require("parser") +local binser = require("lib.binser") +local anselme + +local State +State = class { + type = "anselme state", + + init = function(self, branch_from) + -- create a new branch from an existing state + -- note: the existing state must not currently have an active script + if branch_from then + self.branch_id = uuid() + self.source_branch_id = branch_from.branch_id + self.scope = ScopeStack:new(self, branch_from) + + event_manager:reset(self) -- events are isolated per branch + resumable_manager:reset(self) -- resumable stack is isolated per branch + -- create new empty state + else + self.scope = ScopeStack:new(self) + + event_manager:setup(self) + tag_manager:setup(self) + resumable_manager:setup(self) + end + end, + + --- Load standard library. + -- You will probably want to call this on every State right after creation. + load_stdlib = function(self) + require("stdlib")(self) + end, + + ---## Branching and merging + + --- Name of the branch associated to this State. + branch_id = "main", + --- Name of the branch this State was branched from. + source_branch_id = "main", + + --- Return a new branch of this State. + -- + -- Branches act as indepent copies of this State where any change will not be reflected in the source State until it is merged back into the source branch. + -- Note: probably makes the most sense to create branches from the main State only. + branch = function(self) + assert(not self:active(), "can't branch while a script is active") + return State:new(self) + end, + --- Merge everything that was changed in this branch back into the main State branch. + -- + -- Recommendation: only merge if you know that the state of the variables is consistent, for example at the end of the script, checkpoints, ... + -- If your script errored or was interrupted at an unknown point in the script, you might be in the middle of a calculation and variables won't contain + -- values you want to merge. + merge = function(self) + self.scope:merge() + end, + + ---## Variable definition + + scope = nil, -- ScopeStack associated with the State. Contains *all* scopes related to this State. + + --- Define a value in the global scope, converting it from Lua to Anselme if needed. + -- + -- * for lua functions: `define("name", "(x, y, z=5)", function(x, y, z) ... end)`, where arguments and return values of the function are automatically converted between anselme and lua values + -- * for other lua values: `define("name", value)` + -- * for anselme AST: `define("name", value)` + -- + -- `name` can be prefixed with symbol modifiers, for example ":name" for a constant variable. + -- + -- If `raw_mode` is true, no anselme-to/from-lua conversion will be performed in the function. + -- The function will receive the state followed by AST nodes as arguments, and is expected to return an AST node. + define = function(self, name, value, func, raw_mode) + self.scope:push_global() + self:define_local(name, value, func, raw_mode) + self.scope:pop() + end, + --- Same as `:define`, but define the expression in the current scope. + define_local = function(self, name, value, func, raw_mode) + self.scope:define_lua(name, value, func, raw_mode) + end, + + --- For anything more advanced, you can directly access the current scope stack stored in `state.scope`. + -- See [state/ScopeStack.lua](../state/ScopeStack.lua) for details; the documentation is not as polished as this file but you should still be able to find your way around. + + ---## Saving and loading persistent variables + + --- Return a serialized (string) representation of all global persistent variables in this State. + -- + -- This can be loaded back later using `:load`. + save = function(self) + local list = self.scope:list_persistent_global() + return binser.serialize(anselme.versions.save, list) + end, + --- Load a string generated by `:save`. + -- + -- Variables that do not exist currently in the global scope will be defined, those that do will be overwritten with the loaded data. + load = function(self, save) + local version, list = binser.deserializeN(save, 2) + if version ~= anselme.versions.save then print("Loading a save file generated by a different Anselme version, things may break!") end + self.scope:push_global() + for sym, val in pairs(list) do + if self.scope:defined_in_current(sym) then + self.scope:set(sym:to_identifier(), val) + else + self.scope:define(sym, val) + end + end + self.scope:pop() + end, + + ---## Current script state + + -- Currently active script + _coroutine = nil, + + --- Indicate if a script is currently loaded in this branch. + active = function(self) + return not not self._coroutine + end, + --- Returns `"running`" if a script is currently loaded and running (i.e. this was called from the script). + -- + -- Returns `"active"` if a script is loaded but not currently running (i.e. the script has not started or is waiting on an event). + -- + -- Returns `"inactive"` if no script is loaded. + state = function(self) + if self:active() then + return coroutine.status(self._coroutine) == "running" and "running" or "active" + else + return "inactive" + end + end, + --- Load a script in this branch. It will become the active script. + -- + -- `code` is the code string or AST to run, `source` is the source name string to show in errors (optional). + -- + -- Note that this will only load the script; execution will only start by using the `:step` method. Will error if a script is already active in this State. + run = function(self, code, source) + assert(not self:active(), "a script is already active") + self._coroutine = coroutine.create(function() + local r = assert(self:eval_local(code, source)) + event_manager:final_flush(self) + return "return", r + end) + end, + --- When a script is active, will resume running it until the next event. + -- + -- Will error if no script is active. + -- + -- Returns `event type string, event data`. + step = function(self) + assert(self:active(), "trying to step but no script is currently active") + local success, type, data = coroutine.resume(self._coroutine) + if not success then + self.scope:reset() + type, data = "error", type + end + if coroutine.status(self._coroutine) == "dead" then + self._coroutine = nil + end + return type, data + end, + --- Stops the currently active script. + -- + -- Will error if no script is active. + -- + -- If `code` is given, the script will not be disabled but instead will be immediately replaced with this new script. + -- The new script will then be started on the next `:step` and will preserve the current scope. This can be used to trigger an exit function or similar in the active script. + interrupt = function(self, code, source) + assert(self:active(), "trying to interrupt but no script is currently active") + if code then + self._coroutine = coroutine.create(function() + local r = assert(self:eval_local(code, source)) + event_manager:final_flush(self) + self.scope:reset() -- scope stack is probably messed up after the switch + return "return", r + end) + else + self.scope:reset() + self._coroutine = nil + end + end, + + --- Evaluate an expression in the global scope. + -- + -- This can be called from outside a running script, but an error will be triggered the expression raise any event other than return. + -- + -- * returns AST in case of success. Run `:to_lua(state)` on it to convert to a Lua value. + -- * returns `nil, error message` in case of error. + eval = function(self, code, source) + self.scope:push_global() + local r, e = self:eval_local(code, source) + self.scope:pop() + return r, e + end, + --- Same as `:eval`, but evaluate the expression in the current scope. + eval_local = function(self, code, source) + if type(code) == "string" then code = parser(code, source) end + local stack_size = self.scope:size() + local s, e = pcall(code.eval, code, self) + if not s then + self.scope:reset(stack_size) + return nil, e + else + return e + end + end, + --- If you want to perform more advanced manipulation of the resulting AST nodes, look at the `ast` modules. + -- In particular, every Node inherits the methods from [ast.abstract.Node](../ast/abstract/Node.lua). + -- Otherwise, each Node has its own module file defined in the [ast/](../ast) directory. + + __tostring = function(self) + return ("anselme state, branch %s, %s"):format(self.branch_id, self:state()) + end +} + +package.loaded[...] = State +anselme = require("anselme") + +return State diff --git a/state/event_manager.lua b/state/event_manager.lua new file mode 100644 index 0000000..cf6dda9 --- /dev/null +++ b/state/event_manager.lua @@ -0,0 +1,53 @@ +local class = require("class") + +local ast = require("ast") +local Nil, String, List, Identifier = ast.Nil, ast.String, ast.List, ast.Identifier + +-- list of event data +local event_buffer_identifier = Identifier:new("_event_buffer") +local event_buffer_symbol = event_buffer_identifier:to_symbol{ confined_to_branch = true } -- per-branch, global variables + +-- type of currently buffered event +local last_event_type_identifier = Identifier:new("_last_event_type") +local last_event_type_symbol = last_event_type_identifier:to_symbol{ confined_to_branch = true } + +return class { + init = false, + + setup = function(self, state) + state.scope:define(event_buffer_symbol, List:new(state)) + state.scope:define(last_event_type_symbol, Nil:new()) + end, + reset = function(self, state) + state.scope:set(event_buffer_identifier, List:new(state)) + state.scope:set(last_event_type_identifier, Nil:new()) + end, + + write = function(self, state, event) + local current_type = state.scope:get(last_event_type_identifier):to_lua(state) + if current_type ~= nil and current_type ~= event.type then + self:flush(state) + end + state.scope:set(last_event_type_identifier, String:new(event.type)) + state.scope:get(event_buffer_identifier):insert(state, event) + end, + + flush = function(self, state) + local last_type = state.scope:get(last_event_type_identifier):to_lua(state) + if last_type then + local last_buffer = state.scope:get(event_buffer_identifier) + local event_president = last_buffer:get(state, 1) -- elected representative of all concerned events + -- yield event data + local data = event_president:build_event_data(state, last_buffer) + coroutine.yield(last_type, data) + -- clear room for the future + state.scope:set(last_event_type_identifier, Nil:new()) + state.scope:set(event_buffer_identifier, List:new(state)) + -- post callback + if event_president.post_flush_callback then event_president:post_flush_callback(state, last_buffer, data) end + end + end, + final_flush = function(self, state) + while state.scope:get(last_event_type_identifier):to_lua(state) do self:flush(state) end + end +} diff --git a/state/resumable_manager.lua b/state/resumable_manager.lua new file mode 100644 index 0000000..424be53 --- /dev/null +++ b/state/resumable_manager.lua @@ -0,0 +1,70 @@ +local class = require("class") + +local ast = require("ast") +local Resumable, Nil, List, Identifier + +-- stack of resumable contexts +local resumable_stack_identifier, resumable_stack_symbol + +local resumable_manager = class { + init = false, + + setup = function(self, state) + state.scope:define(resumable_stack_symbol, List:new(state)) + self:push(state, Resumable:new(state, Nil:new(), state.scope:capture())) + end, + reset = function(self, state) + state.scope:set(resumable_stack_identifier, List:new(state)) + self:push(state, Resumable:new(state, Nil:new(), state.scope:capture())) + end, + + push = function(self, state, resumable) + local stack = state.scope:get(resumable_stack_identifier) + stack:insert(state, resumable) + end, + pop = function(self, state) + local stack = state.scope:get(resumable_stack_identifier) + stack:remove(state) + end, + _get = function(self, state) + return state.scope:get(resumable_stack_identifier):get(state, -1) + end, + + -- returns the Resumable object that resumes from this point + -- level indicate which function to resume: level=0 means resume the current function, level=1 the parent function (resume from the call to the current function in the parent function), etc. + capture = function(self, state, level) + level = level or 0 + return state.scope:get(resumable_stack_identifier):get(state, -1-level):capture(state) + end, + + eval = function(self, state, exp) + self:push(state, Resumable:new(state, exp, state.scope:capture())) + local r = exp:eval(state) + self:pop(state) + return r + end, + + set_data = function(self, state, node, data) + self:_get(state).data:set(state, node, data) + end, + get_data = function(self, state, node) + return self:_get(state).data:get(state, node) + end, + resuming = function(self, state, node) + local resumable = self:_get(state) + if node then + return resumable.resuming and resumable.data:has(state, node) + else + return resumable.resuming + end + end +} + +package.loaded[...] = resumable_manager + +Resumable, Nil, List, Identifier = ast.Resumable, ast.Nil, ast.List, ast.Identifier + +resumable_stack_identifier = Identifier:new("_resumable_stack") +resumable_stack_symbol = resumable_stack_identifier:to_symbol{ confined_to_branch = true } -- per-branch, global variables + +return resumable_manager diff --git a/state/tag_manager.lua b/state/tag_manager.lua new file mode 100644 index 0000000..a5988ef --- /dev/null +++ b/state/tag_manager.lua @@ -0,0 +1,38 @@ +local class = require("class") + +local ast = require("ast") +local Struct, Identifier + +local tag_identifier, tag_symbol + +local tag_manager = class { + init = false, + + setup = function(self, state) + state.scope:define(tag_symbol, Struct:new()) + end, + + push = function(self, state, tbl) + local new_strct = Struct:new() + new_strct:include(self:get(state)) + new_strct:include(tbl) + + state.scope:push_partial(tag_identifier) + state.scope:define(tag_symbol, new_strct) + end, + pop = function(self, state) + state.scope:pop() + end, + + get = function(self, state) + return state.scope:get(tag_identifier) + end +} + +package.loaded[...] = tag_manager +Struct, Identifier = ast.Struct, ast.Identifier + +tag_identifier = Identifier:new("_tags") +tag_symbol = tag_identifier:to_symbol() + +return tag_manager diff --git a/stdlib/base.lua b/stdlib/base.lua new file mode 100644 index 0000000..d7e2019 --- /dev/null +++ b/stdlib/base.lua @@ -0,0 +1,60 @@ +local ast = require("ast") +local Pair, ArgumentTuple, Nil, String, Typed, Boolean = ast.Pair, ast.ArgumentTuple, ast.Nil, ast.String, ast.Typed, ast.Boolean + +return { + { "_;_", "(left, right)", function(state, left, right) return right end }, + { "_;", "(left)", function(state, left) return Nil:new() end }, + { "_:_", "(name, value)", function(state, a, b) return Pair:new(a,b) end }, + { + "_::_", "(value, check)", + function(state, value, check) + local r = check:call(state, ArgumentTuple:new(value)) + if r:truthy() then + return value + else + error(("type check failure: %s does not satisfy %s"):format(value:format(state), check:format(state)), 0) + end + end + }, + { + "print", "(a)", + function(state, a) + print(a:format(state)) + return Nil:new() + end + }, + { + "hash", "(a)", + function(state, a) + return String:new(a:hash()) + end + }, + { + "type", "(value)", + function(state, v) + if v.type == "typed" then + return v.type_expression + else + return String:new(v.type) + end + end + }, + { + "value", "(value)", + function(state, v) + if v.type == "typed" then + return v.expression + else + return v + end + end + }, + { + "type", "(type, value)", + function(state, t, v) + return Typed:new(t, v) + end + }, + { "true", Boolean:new(true) }, + { "false", Boolean:new(false) }, +} diff --git a/stdlib/boolean.lua b/stdlib/boolean.lua new file mode 100644 index 0000000..9500f4e --- /dev/null +++ b/stdlib/boolean.lua @@ -0,0 +1,42 @@ +local ast = require("ast") +local Boolean, ArgumentTuple = ast.Boolean, ast.ArgumentTuple + +return { + { + "_==_", "(a, b)", + function(state, a, b) + if a.mutable ~= b.mutable then return Boolean:new(false) + elseif a.mutable then + return Boolean:new(a == b) + else + return Boolean:new(a:hash() == b:hash()) + end + end + }, + { + "!_", "(a)", + function(state, a) + return Boolean:new(not a:truthy()) + end + }, + { + "_&_", "(left, right)", + function(state, left, right) + if left:truthy() then + return right:call(state, ArgumentTuple:new()) + else + return left + end + end + }, + { + "_|_", "(left, right)", + function(state, left, right) + if left:truthy() then + return left + else + return right:call(state, ArgumentTuple:new()) + end + end + }, +} diff --git a/stdlib/boot.ans b/stdlib/boot.ans new file mode 100644 index 0000000..4115747 --- /dev/null +++ b/stdlib/boot.ans @@ -0,0 +1,2 @@ +:@$is(t) $(x) x!type == t +:@$equal(x) $(y) x == y diff --git a/stdlib/bootscript.lua b/stdlib/bootscript.lua deleted file mode 100644 index d6d0d4a..0000000 --- a/stdlib/bootscript.lua +++ /dev/null @@ -1,16 +0,0 @@ --- Script run when creating a VM -return [[ -(Built-in type definition) -::nil="nil" -::number="number" -::string="string" -::list="list" -::map="map" -::pair="pair" -::function reference="function reference" -::variable reference="variable reference" -::object="object" -::annotated="annotated" - -::pi=3.1415926535898 -]] diff --git a/stdlib/checkpoint.lua b/stdlib/checkpoint.lua new file mode 100644 index 0000000..605daac --- /dev/null +++ b/stdlib/checkpoint.lua @@ -0,0 +1,10 @@ +local resumable_manager = require("state.resumable_manager") + +return { + { + "new checkpoint", "(level::number=0)", + function(state, level) + return resumable_manager:capture(state, level.number) + end + } +} diff --git a/stdlib/closure.lua b/stdlib/closure.lua new file mode 100644 index 0000000..97b1662 --- /dev/null +++ b/stdlib/closure.lua @@ -0,0 +1,38 @@ +local ast = require("ast") +local Nil, Boolean, Definition = ast.Nil, ast.Boolean, ast.Definition + +return { + { + "defined", "(c::closure, s::string)", + function(state, c, s) + return Boolean:new(c.exported_scope:defined_in_current_strict(state, s:to_identifier())) + end + }, + { + "_._", "(c::closure, s::string)", + function(state, c, s) + local identifier = s:to_identifier() + assert(c.exported_scope:defined_in_current_strict(state, identifier), ("no exported variable %q defined in closure"):format(s.string)) + return c.exported_scope:get(state, identifier) + end + }, + { + "_._", "(c::closure, s::string) = v", + function(state, c, s, v) + local identifier = s:to_identifier() + assert(c.exported_scope:defined_in_current_strict(state, identifier), ("no exported variable %q defined in closure"):format(s.string)) + c.exported_scope:set(state, identifier, v) + return Nil:new() + end + }, + { + "_._", "(c::closure, s::symbol) = v", + function(state, c, s, v) + assert(s.exported, "can't define a non-exported variable from the outside of the closure") + state.scope:push(c.exported_scope) + local r = Definition:new(s, v):eval(state) + state.scope:pop() + return r + end + } +} diff --git a/stdlib/conditionals.lua b/stdlib/conditionals.lua new file mode 100644 index 0000000..37bc11b --- /dev/null +++ b/stdlib/conditionals.lua @@ -0,0 +1,64 @@ +local ast = require("ast") +local ArgumentTuple, Nil, Boolean, Identifier = ast.ArgumentTuple, ast.Nil, ast.Boolean, ast.Identifier + +local if_identifier = Identifier:new("_if_status") +local if_symbol = if_identifier:to_symbol() + +local function ensure_if_variable(state) + if not state.scope:defined_in_current(if_symbol) then + state.scope:define(if_symbol, Boolean:new(false)) + end +end +local function set_if_variable(state, bool) + state.scope:set(if_identifier, Boolean:new(bool)) +end +local function last_if_success(state) + return state.scope:get(if_identifier):truthy() +end + +return { + { + "_~_", "(condition, expression)", function(state, condition, expression) + ensure_if_variable(state) + if condition:truthy() then + set_if_variable(state, true) + return expression:call(state, ArgumentTuple:new()) + else + set_if_variable(state, false) + return Nil:new() + end + end + }, + { + "~_", "(expression)", + function(state, expression) + ensure_if_variable(state) + if last_if_success(state) then + return Nil:new() + else + set_if_variable(state, true) + return expression:call(state, ArgumentTuple:new()) + end + end + }, + + { + "_~?_", "(condition, expression)", + function(state, condition, expression) + ensure_if_variable(state) + local cond = condition:call(state, ArgumentTuple:new()) + local r + if cond:truthy() then + set_if_variable(state, true) + else + set_if_variable(state, false) + return Nil:new() + end + while cond:truthy() do + r = expression:call(state, ArgumentTuple:new()) + cond = condition:call(state, ArgumentTuple:new()) + end + return r + end + }, +} diff --git a/stdlib/functions.lua b/stdlib/functions.lua deleted file mode 100644 index e8b5be1..0000000 --- a/stdlib/functions.lua +++ /dev/null @@ -1,480 +0,0 @@ -local truthy, anselme, compare, is_of_type, identifier_pattern, format_identifier, find, get_variable, mark_as_modified, set_variable, check_mutable, copy, mark_constant, hash - -local lua_functions -lua_functions = { - -- discard left - ["_;_(a, b)"] = { - mode = "raw", - value = function(a, b) return b end - }, - ["_;(a)"] = { - mode = "raw", - value = function(a) return { type = "nil", value = nil } end - }, - -- comparaison - ["_==_(a, b)"] = { - mode = "raw", - value = function(a, b) - return { - type = "number", - value = compare(a, b) and 1 or 0 - } - end - }, - ["_!=_(a, b)"] = { - mode = "raw", - value = function(a, b) - return { - type = "number", - value = compare(a, b) and 0 or 1 - } - end - }, - ["_>_(a::number, b::number)"] = function(a, b) return a > b end, - ["_<_(a::number, b::number)"] = function(a, b) return a < b end, - ["_>=_(a::number, b::number)"] = function(a, b) return a >= b end, - ["_<=_(a::number, b::number)"] = function(a, b) return a <= b end, - -- arithmetic - ["_+_(a::number, b::number)"] = function(a, b) return a + b end, - ["_+_(a::string, b::string)"] = function(a, b) return a .. b end, - ["_-_(a::number, b::number)"] = function(a, b) return a - b end, - ["-_(a::number)"] = function(a) return -a end, - ["_*_(a::number, b::number)"] = function(a, b) return a * b end, - ["_/_(a::number, b::number)"] = function(a, b) return a / b end, - ["_//_(a::number, b::number)"] = function(a, b) return math.floor(a / b) end, - ["_%_(a::number, b::number)"] = function(a, b) return a % b end, - ["_^_(a::number, b::number)"] = function(a, b) return a ^ b end, - -- boolean - ["!_(a)"] = { - mode = "raw", - value = function(a) - return { - type = "number", - value = truthy(a) and 0 or 1 - } - end - }, - -- pair - ["_=_(a, b)"] = { - mode = "raw", - value = function(a, b) - return { - type = "pair", - value = { a, b } - } - end - }, - ["_:_(a, b)"] = { - mode = "raw", - value = function(a, b) - return { - type = "pair", - value = { a, b } - } - end - }, - -- annotate - ["_::_(a, b)"] = { - mode = "raw", - value = function(a, b) - return { - type = "annotated", - value = { a, b } - } - end - }, - -- namespace - ["_._(r::function reference, name::string)"] = { - mode = "unannotated raw", - value = function(r, n) - local state = anselme.running.state - local rval = r.value - local name = n.value - for _, ffqm in ipairs(rval) do - local var, vfqm = find(state.aliases, state.interpreter.global_state.variables, "", ffqm.."."..name) - if var then - return get_variable(state, vfqm) - end - end - for _, ffqm in ipairs(rval) do - local fn, fnfqm = find(state.aliases, state.functions, "", ffqm.."."..name) - if fn then - return { - type = "function reference", - value = { fnfqm } - } - end - end - return nil, ("can't find variable %q in function reference (searched in namespaces: %s)"):format(name, table.concat(rval, ", ")) - end - }, - ["_._(r::function reference, name::string) := v"] = { - mode = "unannotated raw", - value = function(r, n, v) - local state = anselme.running.state - local rval = r.value - local name = n.value - for _, ffqm in ipairs(rval) do - local var, vfqm = find(state.aliases, state.interpreter.global_state.variables, "", ffqm.."."..name) - if var then - local s, e = set_variable(state, vfqm, v) - if not s then return nil, e end - return v - end - end - return nil, ("can't find variable %q in function reference (searched in namespaces: %s)"):format(name, table.concat(rval, ", ")) - end - }, - ["_._(r::object, name::string)"] = { - mode = "unannotated raw", - value = function(r, n) - local state = anselme.running.state - local obj = r.value - local name = n.value - -- attribute already present in object - local var = find(state.aliases, obj.attributes, "", obj.class.."."..name) - if var then return var end - -- search for attribute in base class - local cvar, cvfqm = find(state.aliases, state.interpreter.global_state.variables, "", obj.class.."."..name) - if cvar then return get_variable(state, cvfqm) end - -- search for method in base class - local fn, fnfqm = find(state.aliases, state.functions, "", obj.class.."."..name) - if fn then - return { - type = "function reference", - value = { fnfqm } - } - end - return nil, ("can't find attribute %q in object"):format(name) - end - }, - ["_._(r::object, name::string) := v"] = { - mode = "unannotated raw", - value = function(r, n, v) - local state = anselme.running.state - local obj = r.value - local name = n.value - -- check constant state - if r.constant then - return nil, "can't change the value of an attribute of a constant object" - end - -- attribute already present in object - local var, vfqm = find(state.aliases, obj.attributes, "", obj.class.."."..name) - if var then - if not check_mutable(state, vfqm) then return nil, "can't change the value of a constant attribute" end - obj.attributes[vfqm] = v - mark_as_modified(anselme.running.state, obj.attributes) - return v - end - -- search for attribute in base class - local cvar, cvfqm = find(state.aliases, state.interpreter.global_state.variables, "", obj.class.."."..name) - if cvar then - if not check_mutable(state, cvfqm) then return nil, "can't change the value of a constant attribute" end - obj.attributes[cvfqm] = v - mark_as_modified(anselme.running.state, obj.attributes) - return v - end - return nil, ("can't find attribute %q in object"):format(name) - end - }, - -- index - ["()(l::list, i::number)"] = { - mode = "unannotated raw", - value = function(l, i) - local index = i.value - if index < 0 then index = #l.value + 1 + index end - if index > #l.value or index == 0 then return nil, "list index out of bounds" end - return l.value[index] or { type = "nil", value = nil } - end - }, - ["()(l::map, k)"] = { - mode = "raw", - value = function(l, i) - local lv = l.type == "annotated" and l.value[1] or l - local h, err = hash(i) - if not h then return nil, err end - local v = lv.value[h] - if v then - return v[2] - else - return { type = "nil", value = nil } - end - end - }, - -- index assignment - ["()(l::list, i::number) := v"] = { - mode = "raw", - value = function(l, i, v) - local lv = l.type == "annotated" and l.value[1] or l - local iv = i.type == "annotated" and i.value[1] or i - if lv.constant then return nil, "can't change the contents of a constant list" end - local index = iv.value - if index < 0 then index = #lv.value + 1 + index end - if index > #lv.value + 1 or index == 0 then return nil, "list assignment index out of bounds" end - lv.value[index] = v - mark_as_modified(anselme.running.state, lv.value) - return v - end - }, - ["()(l::map, k) := v::nil"] = { - mode = "raw", - value = function(l, k, v) - local lv = l.type == "annotated" and l.value[1] or l - if lv.constant then return nil, "can't change the contents of a constant map" end - local h, err = hash(k) - if not h then return nil, err end - lv.value[h] = nil - mark_as_modified(anselme.running.state, lv.value) - return v - end - }, - ["()(l::map, k) := v"] = { - mode = "raw", - value = function(l, k, v) - local lv = l.type == "annotated" and l.value[1] or l - if lv.constant then return nil, "can't change the contents of a constant map" end - local h, err = hash(k) - if not h then return nil, err end - lv.value[h] = { k, v } - mark_as_modified(anselme.running.state, lv.value) - return v - end - }, - ["()(fn::function reference, l...)"] = { - -- bypassed, this case is manually handled in the expression interpreter - }, - ["_!(fn::function reference)"] = { - -- bypassed, this case is manually handled in the expression interpreter - }, - ["_!(fn::variable reference)"] = { - mode = "unannotated raw", - value = function(v) - return get_variable(anselme.running.state, v.value) - end - }, - ["&_(v::variable reference)"] = { - mode = "unannotated raw", - value = function(v) return v end - }, - ["&_(fn::function reference)"] = { - mode = "unannotated raw", - value = function(v) return v end - }, - -- format - ["{}(v)"] = { - mode = "raw", - value = function(v) - return v - end - }, - -- alias - ["alias(ref::function reference, alias::string)"] = { - mode = "unannotated raw", - value = function(ref, alias) - -- check identifiers - alias = alias.value - local aliasfqm = alias:match("^"..identifier_pattern.."$") - if not aliasfqm then error(("%q is not a valid identifier for an alias"):format(alias)) end - aliasfqm = format_identifier(aliasfqm) - -- define alias - for _, fnfqm in ipairs(ref.value) do - local aliases = anselme.running.state.aliases - if aliases[aliasfqm] ~= nil and aliases[aliasfqm] ~= fnfqm then - error(("trying to define alias %q for %q, but already exist and refer to %q"):format(aliasfqm, fnfqm, aliases[alias])) - end - aliases[aliasfqm] = fnfqm - end - return { type = "nil" } - end - }, - ["alias(ref::variable reference, alias::string)"] = { - mode = "unannotated raw", - value = function(ref, alias) - -- check identifiers - alias = alias.value - local aliasfqm = alias:match("^"..identifier_pattern.."$") - if not aliasfqm then error(("%q is not a valid identifier for an alias"):format(alias)) end - aliasfqm = format_identifier(aliasfqm) - -- define alias - local aliases = anselme.running.state.aliases - if aliases[aliasfqm] ~= nil and aliases[aliasfqm] ~= ref.value then - error(("trying to define alias %q for %q, but already exist and refer to %q"):format(aliasfqm, ref.value, aliases[alias])) - end - aliases[aliasfqm] = ref.value - return { type = "nil" } - end - }, - -- pair methods - ["name(p::pair)"] = { - mode = "unannotated raw", - value = function(a) - return a.value[1] - end - }, - ["value(p::pair)"] = { - mode = "unannotated raw", - value = function(a) - return a.value[2] - end - }, - -- list methods - ["len(l::list)"] = { - mode = "unannotated raw", -- raw to count pairs in the list - value = function(a) - return { - type = "number", - value = #a.value - } - end - }, - ["insert(l::list, v)"] = { - mode = "raw", - value = function(l, v) - local lv = l.type == "annotated" and l.value[1] or l - if lv.constant then return nil, "can't insert values into a constant list" end - table.insert(lv.value, v) - mark_as_modified(anselme.running.state, lv.value) - return l - end - }, - ["insert(l::list, i::number, v)"] = { - mode = "raw", - value = function(l, i, v) - local lv = l.type == "annotated" and l.value[1] or l - local iv = i.type == "annotated" and i.value[1] or i - if lv.constant then return nil, "can't insert values into a constant list" end - table.insert(lv.value, iv.value, v) - mark_as_modified(anselme.running.state, lv.value) - return l - end - }, - ["remove(l::list)"] = { - mode = "unannotated raw", - value = function(l) - if l.constant then return nil, "can't remove values from a constant list" end - mark_as_modified(anselme.running.state, l.value) - return table.remove(l.value) - end - }, - ["remove(l::list, i::number)"] = { - mode = "unannotated raw", - value = function(l, i) - if l.constant then return nil, "can't remove values from a constant list" end - mark_as_modified(anselme.running.state, l.value) - return table.remove(l.value, i.value) - end - }, - ["find(l::list, v)"] = { - mode = "raw", - value = function(l, v) - local lv = l.type == "annotated" and l.value[1] or l - for i, x in ipairs(lv.value) do - if compare(x, v) then - return i - end - end - return { type = "number", value = 0 } - end - }, - -- string - ["len(s::string)"] = function(s) - return require("utf8").len(s) - end, - -- other methods - ["error(m::string)"] = function(m) error(m, 0) end, - ["rand()"] = function() return math.random() end, - ["rand(a::number)"] = function(a) return math.random(a) end, - ["rand(a::number, b::number)"] = function(a, b) return math.random(a, b) end, - ["floor(a::number)"] = function(a) return math.floor(a) end, - ["ceil(a::number)"] = function(a) return math.ceil(a) end, - ["round(a::number, increment=1::number)"] = function(a, increment) - local n = a / increment - if n >= 0 then - return math.floor(n + 0.5) * increment - else - return math.ceil(n - 0.5) * increment - end - end, - ["unannotated(v)"] = { - mode = "raw", - value = function(v) - if v.type == "annotated" then - return v.value[1] - else - return v - end - end - }, - ["type(v)"] = { - mode = "unannotated raw", - value = function(v) - return { - type = "string", - value = v.type - } - end - }, - ["annotation(v::annotated)"] = { - mode = "raw", - value = function(v) - return v.value[2] - end - }, - ["is a(v, t)"] = { - mode = "raw", - value = function(v, t) - return { - type = "number", - value = is_of_type(v, t) or 0 - } - end - }, - ["constant(v)"] = { - mode = "raw", - value = function(v) - local c = copy(v) - mark_constant(c) - return c - end - } -} - -local anselme_functions = [[ -:$ random(l...) - ~ l(rand(1, l!len))! - -:$ next(l...) - :j = 0 - ~? j += 1; j < len(l) & l(j).👁️ != 0 - ~ l(j)! - -:$ cycle(l...) - :f = l(1) - :j = 1 - ~? j += 1; j <= len(l) & !((f := l(j); 1) ~ l(j).👁️ < f.👁️) - ~ f! - -:$ concat(l::list, separator=""::string) - :r = "" - :j = 0 - ~? j += 1; j <= len(l) - ~ r += "{l(j)}" - ~ j < len(l) - ~ r += separator - @r -]] - -local functions = { - lua = lua_functions, - anselme = anselme_functions -} - -package.loaded[...] = functions -local icommon = require((...):gsub("stdlib%.functions$", "interpreter.common")) -truthy, compare, is_of_type, get_variable, mark_as_modified, set_variable, check_mutable, mark_constant, hash = icommon.truthy, icommon.compare, icommon.is_of_type, icommon.get_variable, icommon.mark_as_modified, icommon.set_variable, icommon.check_mutable, icommon.mark_constant, icommon.hash -local pcommon = require((...):gsub("stdlib%.functions$", "parser.common")) -identifier_pattern, format_identifier, find = pcommon.identifier_pattern, pcommon.format_identifier, pcommon.find -anselme = require((...):gsub("stdlib%.functions$", "anselme")) -copy = require((...):gsub("stdlib%.functions$", "common")).copy - -return functions diff --git a/stdlib/init.lua b/stdlib/init.lua new file mode 100644 index 0000000..1f02a12 --- /dev/null +++ b/stdlib/init.lua @@ -0,0 +1,36 @@ +local parser = require("parser") + +local function define_lua(state, list) + for _, fn in ipairs(list) do + state.scope:define_lua(fn[1], fn[2], fn[3], true) + end +end +local function load(state, l) + for _, m in ipairs(l) do + define_lua(state, require("stdlib."..m)) + end +end + +return function(main_state) + load(main_state, { + "boolean", + "tag", + "conditionals", + "base", + "type_check" + }) + + local f = assert(io.open("stdlib/boot.ans")) + local boot = parser(f:read("*a"), "boot.ans") + f:close() + boot:eval(main_state) + + load(main_state, { + "number", + "string", + "text", + "structures", + "closure", + "checkpoint" + }) +end diff --git a/stdlib/languages/enUS.lua b/stdlib/languages/enUS.lua deleted file mode 100644 index dc1aecc..0000000 --- a/stdlib/languages/enUS.lua +++ /dev/null @@ -1,6 +0,0 @@ -return [[ -(Built-in variables) -::alias 👁️ = "seen" -::alias 🔖 = "checkpoint" -::alias 🏁 = "reached" -]] diff --git a/stdlib/languages/frFR.lua b/stdlib/languages/frFR.lua deleted file mode 100644 index 38559c6..0000000 --- a/stdlib/languages/frFR.lua +++ /dev/null @@ -1,33 +0,0 @@ -return [[ -(Types) -~ &nil!alias("nul") -~ &number!alias("nombre") -~ &string!alias("texte") -~ &list!alias("liste") -~ &map!alias("dictionnaire") -~ &pair!alias("paire") -~ &function reference!alias("réference de fonction") -~ &variable reference!alias("réference de variable") -~ &object!alias("objet") -~ &annotated!alias("annoté") - -(Built-in functions) -~ &name!alias("nom") -~ &value!alias("valeur") -~ &len!alias("longueur") -~ &insert!alias("ajouter") -~ &remove!alias("retirer") -~ &find!alias("trouver") -~ &error!alias("erreur") -~ &rand!alias("aléa") -~ &is a!alias("est un") -~ &unannotated!alias("non annoté") -~ &cycle!alias("cycler") -~ &random!alias("aléatoire") -~ &next!alias("séquence") - -(Built-in variables) -::alias 👁️ = "vu" -::alias 🔖 = "checkpoint" -::alias 🏁 = "atteint" -]] diff --git a/stdlib/number.lua b/stdlib/number.lua new file mode 100644 index 0000000..cea03eb --- /dev/null +++ b/stdlib/number.lua @@ -0,0 +1,49 @@ +local ast = require("ast") +local Boolean, Number = ast.Boolean, ast.Number + +return { + { + "_<_", "(a::number, b::number)", + function(state, a, b) + if a.number < b.number then return b + else return Boolean:new(false) + end + end + }, + { "_<_", "(a::equal(false), b::number)", function(state, a, b) return Boolean:new(false) end }, + { + "_<=_", "(a::number, b::number)", + function(state, a, b) + if a.number <= b.number then return b + else return Boolean:new(false) + end + end + }, + { "_<=_", "(a::equal(false), b::number)", function(state, a, b) return Boolean:new(false) end }, + { + "_>_", "(a::number, b::number)", + function(state, a, b) + if a.number > b.number then return b + else return Boolean:new(false) + end + end + }, + { "_>_", "(a::equal(false), b::number)", function(state, a, b) return Boolean:new(false) end }, + { + "_>=_", "(a::number, b::number)", + function(state, a, b) + if a.number >= b.number then return b + else return Boolean:new(false) + end + end + }, + { "_>=_", "(a::equal(false), b::number)", function(state, a, b) return Boolean:new(false) end }, + { "_+_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number + b.number) end }, + { "_-_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number - b.number) end }, + { "_*_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number * b.number) end }, + { "_/_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number / b.number) end }, + { "_//_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number // b.number) end }, + { "_%_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number % b.number) end }, + { "_^_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number ^ b.number) end }, + { "-_", "(a::number)", function(state, a) return Number:new(-a.number) end }, +} diff --git a/stdlib/string.lua b/stdlib/string.lua new file mode 100644 index 0000000..6855146 --- /dev/null +++ b/stdlib/string.lua @@ -0,0 +1,6 @@ +local ast = require("ast") +local String = ast.String + +return { + { "_+_", "(a::string, b::string)", function(state, a, b) return String:new(a.string .. b.string) end } +} diff --git a/stdlib/structures.lua b/stdlib/structures.lua new file mode 100644 index 0000000..f0d4c6b --- /dev/null +++ b/stdlib/structures.lua @@ -0,0 +1,87 @@ +local ast = require("ast") +local Nil, List, Table, Number = ast.Nil, ast.List, ast.Table, ast.Number + +return { + -- tuple + { + "*_", "(t::tuple)", + function(state, tuple) + return List:new(state, tuple) + end + }, + { + "_!", "(l::tuple, i::number)", + function(state, l, i) + return l:get(i.number) + end + }, + + -- list + { + "_!", "(l::list, i::number)", + function(state, l, i) + return l:get(state, i.number) + end + }, + { + "_!", "(l::list, i::number) = value", + function(state, l, i, v) + l:set(state, i.number, v) + return Nil:new() + end + }, + { + "insert", "(l::list, value)", + function(state, l, v) + l:insert(state, v) + return Nil:new() + end + }, + { + "len", "(l::list)", + function(state, l) + return Number:new(l:len(state)) + end + }, + { + "to tuple", "(l::list)", + function(state, l) + return l:to_tuple(state) + end + }, + + -- struct + { + "*_", "(s::struct)", + function(state, struct) + return Table:new(state, struct) + end + }, + { + "_!", "(s::struct, key)", + function(state, s, k) + return s:get(k) + end + }, + + -- table + { + "_!", "(t::table, key)", + function(state, t, key) + return t:get(state, key) + end + }, + { + "_!", "(t::table, key) = value", + function(state, t, key, value) + t:set(state, key, value) + return Nil:new() + end + }, + { + "to struct", "(t::table)", + function(state, t) + return t:to_struct(state) + end + }, +} diff --git a/stdlib/tag.lua b/stdlib/tag.lua new file mode 100644 index 0000000..ee2da7f --- /dev/null +++ b/stdlib/tag.lua @@ -0,0 +1,27 @@ +local ast = require("ast") +local Tuple, Table, Struct, ArgumentTuple = ast.Tuple, ast.Table, ast.Struct, ast.ArgumentTuple + +local tag_manager = require("state.tag_manager") + +return { + { + "_#_", "(tags, expression)", + function(state, tags, expression) + local tags_struct + if Tuple:is(tags) and not tags.explicit then + tags_struct = Struct:from_tuple(tags):eval(state) + elseif Struct:is(tags) then + tags_struct = tags + elseif Table:is(tags) then + tags_struct = tags:to_struct(state) + else + tags_struct = Struct:from_tuple(Tuple:new(tags)):eval(state) + end + + tag_manager:push(state, tags_struct) + local v = expression:call(state, ArgumentTuple:new()) + tag_manager:pop(state) + return v + end + } +} diff --git a/stdlib/text.lua b/stdlib/text.lua new file mode 100644 index 0000000..b5a4552 --- /dev/null +++ b/stdlib/text.lua @@ -0,0 +1,24 @@ +local ast = require("ast") +local Nil, Choice = ast.Nil, ast.Choice + +local event_manager = require("state.event_manager") + +return { + -- text + { + "_!", "(txt::text)", + function(state, text) + event_manager:write(state, text) + return Nil:new() + end + }, + + -- choice + { + "_|>_", "(txt::text, fn)", + function(state, text, func) + event_manager:write(state, Choice:new(text, func)) + return Nil:new() + end + }, +} diff --git a/stdlib/type_check.lua b/stdlib/type_check.lua new file mode 100644 index 0000000..ad6a436 --- /dev/null +++ b/stdlib/type_check.lua @@ -0,0 +1,20 @@ +local ast = require("ast") +local Boolean = ast.Boolean + +return { + { "number", "(x)", function(state, x) return Boolean:new(x.type == "number") end }, + { "string", "(x)", function(state, x) return Boolean:new(x.type == "string") end }, + { "boolean", "(x)", function(state, x) return Boolean:new(x.type == "boolean") end }, + { "symbol", "(x)", function(state, x) return Boolean:new(x.type == "symbol") end }, + + { "text", "(x)", function(state, x) return Boolean:new(x.type == "text") end }, + + { "tuple", "(x)", function(state, x) return Boolean:new(x.type == "tuple") end }, + { "list", "(x)", function(state, x) return Boolean:new(x.type == "list") end }, + { "struct", "(x)", function(state, x) return Boolean:new(x.type == "struct") end }, + { "table", "(x)", function(state, x) return Boolean:new(x.type == "table") end }, + + { "closure", "(x)", function(state, x) return Boolean:new(x.type == "closure") end }, + { "overload", "(x)", function(state, x) return Boolean:new(x.type == "overload") end }, + { "function", "(x)", function(state, x) return Boolean:new(x.type == "overload" or x.type == "closure" or x.type == "funciton" or x.type == "lua function") end }, +} diff --git a/stdlib/types.lua b/stdlib/types.lua deleted file mode 100644 index 5d9b9cb..0000000 --- a/stdlib/types.lua +++ /dev/null @@ -1,412 +0,0 @@ -local format, to_lua, from_lua, events, anselme, escape, hash, update_hashes, get_variable, find_function_variant_from_fqm, post_process_text, traverse - -local types = {} -types.lua = { - ["nil"] = { - to_anselme = function(val) - return { - type = "nil", - value = nil - } - end - }, - boolean = { - to_anselme = function(val) - return { - type = "number", - value = val and 1 or 0 - } - end - }, - number = { - to_anselme = function(val) - return { - type = "number", - value = val - } - end - }, - string = { - to_anselme = function(val) - return { - type = "string", - value = val - } - end - }, - table = { - to_anselme = function(val) - local is_map = false - local l = {} - local m = {} - for _, v in ipairs(val) do - local r, e = from_lua(v) - if not r then return r, e end - table.insert(l, r) - end - for k, v in pairs(val) do - if not l[k] then - is_map = true - local kv, ke = from_lua(k) - if not k then return k, ke end - local vv, ve = from_lua(v) - if not v then return v, ve end - local h, err = hash(kv) - if not h then return nil, err end - m[h] = { kv, vv } - end - end - if is_map then - for i, v in ipairs(l) do - local key = { type = "number", value = i } - local h, err = hash(key) - if not h then return nil, err end - m[h] = { key, v } - end - return { - type = "map", - value = m - } - else - return { - type = "list", - value = l - } - end - end - } -} - -types.anselme = { - ["nil"] = { - format = function() - return "" - end, - to_lua = function() - return nil - end, - hash = function() - return "nil()" - end, - traverse = function() return true end, - }, - number = { - format = function(val) - return tostring(val) - end, - to_lua = function(val) - return val - end, - hash = function(val) - return ("n(%s)"):format(val) - end, - traverse = function() return true end, - }, - string = { - format = function(val) - return tostring(val) - end, - to_lua = function(val) - return val - end, - hash = function(val) - return ("s(%s)"):format(val) - end, - traverse = function() return true end, - }, - pair = { - format = function(val) - local k, ke = format(val[1]) - if not k then return k, ke end - local v, ve = format(val[2]) - if not v then return v, ve end - return ("%s=%s"):format(k, v) - end, - to_lua = function(val, state) - local k, ke = to_lua(val[1], state) - if ke then return nil, ke end - local v, ve = to_lua(val[2], state) - if ve then return nil, ve end - return { [k] = v } - end, - hash = function(val) - local k, ke = hash(val[1]) - if not k then return k, ke end - local v, ve = hash(val[2]) - if not v then return v, ve end - return ("p(%s=%s)"):format(k, v) - end, - traverse = function(val, callback, pertype_callback) - local k, ke = traverse(val[1], callback, pertype_callback) - if not k then return k, ke end - local v, ve = traverse(val[2], callback, pertype_callback) - if not v then return v, ve end - return true - end, - }, - annotated = { - format = function(val) - local k, ke = format(val[1]) - if not k then return k, ke end - local v, ve = format(val[2]) - if not v then return v, ve end - return ("%s::%s"):format(k, v) - end, - to_lua = function(val, state) - local k, ke = to_lua(val[1], state) - if ke then return nil, ke end - return k - end, - hash = function(val) - local k, ke = hash(val[1]) - if not k then return k, ke end - local v, ve = hash(val[2]) - if not v then return v, ve end - return ("a(%s::%s)"):format(k, v) - end, - traverse = function(val, callback, pertype_callback) - local k, ke = traverse(val[1], callback, pertype_callback) - if not k then return k, ke end - local v, ve = traverse(val[2], callback, pertype_callback) - if not v then return v, ve end - return true - end, - }, - list = { - mutable = true, - format = function(val) - local l = {} - for _, v in ipairs(val) do - local s, e = format(v) - if not s then return s, e end - table.insert(l, s) - end - return ("[%s]"):format(table.concat(l, ", ")) - end, - to_lua = function(val, state) - local l = {} - for _, v in ipairs(val) do - local s, e = to_lua(v, state) - if e then return nil, e end - table.insert(l, s) - end - return l - end, - hash = function(val) - local l = {} - for _, v in ipairs(val) do - local s, e = hash(v) - if not s then return s, e end - table.insert(l, s) - end - return ("l(%s)"):format(table.concat(l, ",")) - end, - traverse = function(val, callback, pertype_callback) - for _, item in ipairs(val) do - local s, e = traverse(item, callback, pertype_callback) - if not s then return s, e end - end - return true - end, - mark_constant = function(v) - v.constant = true - end, - }, - map = { - mutable = true, - format = function(val) - local l = {} - for _, v in pairs(val) do - local ks, ke = format(v[1]) - if not ks then return ks, ke end - local vs, ve = format(v[2]) - if not vs then return vs, ve end - table.insert(l, ("%s=%s"):format(ks, vs)) - end - table.sort(l) - return ("{%s}"):format(table.concat(l, ", ")) - end, - to_lua = function(val, state) - local l = {} - for _, v in pairs(val) do - local kl, ke = to_lua(v[1], state) - if ke then return nil, ke end - local xl, xe = to_lua(v[2], state) - if xe then return nil, xe end - l[kl] = xl - end - return l - end, - hash = function(val) - local l = {} - for _, v in pairs(val) do - local ks, ke = hash(v[1]) - if not ks then return ks, ke end - local vs, ve = hash(v[2]) - if not vs then return vs, ve end - table.insert(l, ("%s=%s"):format(ks, vs)) - end - table.sort(l) - return ("m(%s)"):format(table.concat(l, ",")) - end, - traverse = function(val, callback, pertype_callback) - for _, v in pairs(val) do - local ks, ke = traverse(v[1], callback, pertype_callback) - if not ks then return ks, ke end - local vs, ve = traverse(v[2], callback, pertype_callback) - if not vs then return vs, ve end - end - return true - end, - mark_constant = function(v) - v.constant = true - update_hashes(v) - end, - }, - object = { - mutable = true, - format = function(val) - local attributes = {} - for name, v in pairs(val.attributes) do - table.insert(attributes, ("%s=%s"):format(name:gsub("^"..escape(val.class)..".", ""), format(v))) - end - if #attributes > 0 then - table.sort(attributes) - return ("%%%s(%s)"):format(val.class, table.concat(attributes, ", ")) - else - return ("%%%s"):format(val.class) - end - end, - to_lua = function(val, state) - local r = {} - local namespacePattern = "^"..escape(val.class).."%." - -- set object properties - for name, v in pairs(val.attributes) do - local var, err = to_lua(v, state) - if err then return nil, err end - r[name:gsub(namespacePattern, "")] = var - end - -- set class properties - local class, err = find_function_variant_from_fqm(val.class, state, nil) - if not class then return nil, err end - assert(#class == 1 and class[1].subtype == "class") - class = class[1] - for _, prop in ipairs(class.properties) do - if not val.attributes[prop] then - local var - var, err = get_variable(state, prop) - if not var then return nil, err end - var, err = to_lua(var, state) - if err then return nil, err end - r[prop:gsub(namespacePattern, "")] = var - end - end - return r - end, - hash = function(val) - local attributes = {} - for name, v in pairs(val.attributes) do - table.insert(attributes, ("%s=%s"):format(name:gsub("^"..escape(val.class)..".", ""), format(v))) - end - table.sort(attributes) - return ("%%(%s;%s)"):format(val.class, table.concat(attributes, ",")) - end, - traverse = function(v, callback, pertype_callback) - for _, attrib in pairs(v.attributes) do - local s, e = traverse(attrib, callback, pertype_callback) - if not s then return s, e end - end - return true - end, - mark_constant = function(v) - v.constant = true - end, - }, - ["function reference"] = { - format = function(val) - if #val > 1 then - return ("&(%s)"):format(table.concat(val, ", ")) - else - return ("&%s"):format(table.concat(val, ", ")) - end - end, - to_lua = nil, - hash = function(val) - return ("&f(%s)"):format(table.concat(val, ", ")) - end, - traverse = function() return true end, - }, - ["variable reference"] = { - format = function(val) - return ("&%s"):format(val) - end, - to_lua = nil, - hash = function(val) - return ("&v(%s)"):format(val) - end, - traverse = function() return true end, - }, - -- event buffer: can only be used outside of Anselme internals for text & flush events (through text buffers) - ["event buffer"] = { - format = function(val) -- triggered from subtexts - local v, e = events:write_buffer(anselme.running.state, val) - if not v then return v, e end - return "" - end, - to_lua = function(val, state) - local r = {} - for _, event in ipairs(val) do - if event.type == "text" then - table.insert(r, { "text", post_process_text(state, event.value) }) - elseif event.type == "flush" then - table.insert(r, { "flush" }) - else - return nil, ("event %q in event buffer can't be converted to a Lua value"):format(event.type) - end - end - return r - end, - hash = function(val) - local l = {} - for _, event in ipairs(val) do - if event.type == "text" then - local text = {} - for _, t in ipairs(event.value) do - local str = ("s(%s)"):format(t.text) - local tags, e = hash(t.tags) - if not tags then return nil, e end - table.insert(text, ("%s#%s"):format(str, tags)) - end - table.insert(l, ("text(%s)"):format(table.concat(text, ","))) - elseif event.type == "flush" then - table.insert(l, "flush") - else - return nil, ("event %q in event buffer cannot be hashed"):format(event.type) - end - end - return ("eb(%s)"):format(table.concat(l, ",")) - end, - traverse = function(val, callback, pertype_callback) - for _, event in ipairs(val) do - if event.type == "text" then - for _, t in ipairs(event.value) do - local s, e = traverse(t.tags, callback, pertype_callback) - if not s then return s, e end - end - elseif event.type ~= "flush" then - return nil, ("event %q in event buffer cannot be traversed"):format(event.type) - end - end - return true - end, - }, -} - -package.loaded[...] = types -local common = require((...):gsub("stdlib%.types$", "interpreter.common")) -format, to_lua, from_lua, events, hash, update_hashes, get_variable, post_process_text, traverse = common.format, common.to_lua, common.from_lua, common.events, common.hash, common.update_hashes, common.get_variable, common.post_process_text, common.traverse -anselme = require((...):gsub("stdlib%.types$", "anselme")) -local pcommon = require((...):gsub("stdlib%.types$", "parser.common")) -escape, find_function_variant_from_fqm = pcommon.escape, pcommon.find_function_variant_from_fqm - -return types diff --git a/test/inspect.lua b/test/inspect.lua deleted file mode 100644 index e2e3806..0000000 --- a/test/inspect.lua +++ /dev/null @@ -1,334 +0,0 @@ -local inspect ={ - _VERSION = 'inspect.lua 3.1.0', - _URL = 'http://github.com/kikito/inspect.lua', - _DESCRIPTION = 'human-readable representations of tables', - _LICENSE = [[ - MIT LICENSE - - Copyright (c) 2013 Enrique García Cota - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ]] -} - -local tostring = tostring - -inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end}) -inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end}) - -local function rawpairs(t) - return next, t, nil -end - --- Apostrophizes the string if it has quotes, but not aphostrophes --- Otherwise, it returns a regular quoted string -local function smartQuote(str) - if str:match('"') and not str:match("'") then - return "'" .. str .. "'" - end - return '"' .. str:gsub('"', '\\"') .. '"' -end - --- \a => '\\a', \0 => '\\0', 31 => '\31' -local shortControlCharEscapes = { - ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", - ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v" -} -local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031 -for i=0, 31 do - local ch = string.char(i) - if not shortControlCharEscapes[ch] then - shortControlCharEscapes[ch] = "\\"..i - longControlCharEscapes[ch] = string.format("\\%03d", i) - end -end - -local function escape(str) - return (str:gsub("\\", "\\\\") - :gsub("(%c)%f[0-9]", longControlCharEscapes) - :gsub("%c", shortControlCharEscapes)) -end - -local function isIdentifier(str) - return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" ) -end - -local function isSequenceKey(k, sequenceLength) - return type(k) == 'number' - and 1 <= k - and k <= sequenceLength - and math.floor(k) == k -end - -local defaultTypeOrders = { - ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4, - ['function'] = 5, ['userdata'] = 6, ['thread'] = 7 -} - -local function sortKeys(a, b) - local ta, tb = type(a), type(b) - - -- strings and numbers are sorted numerically/alphabetically - if ta == tb and (ta == 'string' or ta == 'number') then return a < b end - - local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb] - -- Two default types are compared according to the defaultTypeOrders table - if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb] - elseif dta then return true -- default types before custom ones - elseif dtb then return false -- custom types after default ones - end - - -- custom types are sorted out alphabetically - return ta < tb -end - --- For implementation reasons, the behavior of rawlen & # is "undefined" when --- tables aren't pure sequences. So we implement our own # operator. -local function getSequenceLength(t) - local len = 1 - local v = rawget(t,len) - while v ~= nil do - len = len + 1 - v = rawget(t,len) - end - return len - 1 -end - -local function getNonSequentialKeys(t) - local keys, keysLength = {}, 0 - local sequenceLength = getSequenceLength(t) - for k,_ in rawpairs(t) do - if not isSequenceKey(k, sequenceLength) then - keysLength = keysLength + 1 - keys[keysLength] = k - end - end - table.sort(keys, sortKeys) - return keys, keysLength, sequenceLength -end - -local function countTableAppearances(t, tableAppearances) - tableAppearances = tableAppearances or {} - - if type(t) == 'table' then - if not tableAppearances[t] then - tableAppearances[t] = 1 - for k,v in rawpairs(t) do - countTableAppearances(k, tableAppearances) - countTableAppearances(v, tableAppearances) - end - countTableAppearances(getmetatable(t), tableAppearances) - else - tableAppearances[t] = tableAppearances[t] + 1 - end - end - - return tableAppearances -end - -local copySequence = function(s) - local copy, len = {}, #s - for i=1, len do copy[i] = s[i] end - return copy, len -end - -local function makePath(path, ...) - local keys = {...} - local newPath, len = copySequence(path) - for i=1, #keys do - newPath[len + i] = keys[i] - end - return newPath -end - -local function processRecursive(process, item, path, visited) - if item == nil then return nil end - if visited[item] then return visited[item] end - - local processed = process(item, path) - if type(processed) == 'table' then - local processedCopy = {} - visited[item] = processedCopy - local processedKey - - for k,v in rawpairs(processed) do - processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) - if processedKey ~= nil then - processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) - end - end - - local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) - if type(mt) ~= 'table' then mt = nil end -- ignore not nil/table __metatable field - setmetatable(processedCopy, mt) - processed = processedCopy - end - return processed -end - - - -------------------------------------------------------------------- - -local Inspector = {} -local Inspector_mt = {__index = Inspector} - -function Inspector:puts(...) - local args = {...} - local buffer = self.buffer - local len = #buffer - for i=1, #args do - len = len + 1 - buffer[len] = args[i] - end -end - -function Inspector:down(f) - self.level = self.level + 1 - f() - self.level = self.level - 1 -end - -function Inspector:tabify() - self:puts(self.newline, string.rep(self.indent, self.level)) -end - -function Inspector:alreadyVisited(v) - return self.ids[v] ~= nil -end - -function Inspector:getId(v) - local id = self.ids[v] - if not id then - local tv = type(v) - id = (self.maxIds[tv] or 0) + 1 - self.maxIds[tv] = id - self.ids[v] = id - end - return tostring(id) -end - -function Inspector:putKey(k) - if isIdentifier(k) then return self:puts(k) end - self:puts("[") - self:putValue(k) - self:puts("]") -end - -function Inspector:putTable(t) - if t == inspect.KEY or t == inspect.METATABLE then - self:puts(tostring(t)) - elseif self:alreadyVisited(t) then - self:puts('') - elseif self.level >= self.depth then - self:puts('{...}') - else - if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end - - local nonSequentialKeys, nonSequentialKeysLength, sequenceLength = getNonSequentialKeys(t) - local mt = getmetatable(t) - - self:puts('{') - self:down(function() - local count = 0 - for i=1, sequenceLength do - if count > 0 then self:puts(',') end - self:puts(' ') - self:putValue(t[i]) - count = count + 1 - end - - for i=1, nonSequentialKeysLength do - local k = nonSequentialKeys[i] - if count > 0 then self:puts(',') end - self:tabify() - self:putKey(k) - self:puts(' = ') - self:putValue(t[k]) - count = count + 1 - end - - if type(mt) == 'table' then - if count > 0 then self:puts(',') end - self:tabify() - self:puts(' = ') - self:putValue(mt) - end - end) - - if nonSequentialKeysLength > 0 or type(mt) == 'table' then -- result is multi-lined. Justify closing } - self:tabify() - elseif sequenceLength > 0 then -- array tables have one extra space before closing } - self:puts(' ') - end - - self:puts('}') - end -end - -function Inspector:putValue(v) - local tv = type(v) - - if tv == 'string' then - self:puts(smartQuote(escape(v))) - elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or - tv == 'cdata' or tv == 'ctype' then - self:puts(tostring(v)) - elseif tv == 'table' then - self:putTable(v) - else - self:puts('<', tv, ' ', self:getId(v), '>') - end -end - -------------------------------------------------------------------- - -function inspect.inspect(root, options) - options = options or {} - - local depth = options.depth or math.huge - local newline = options.newline or '\n' - local indent = options.indent or ' ' - local process = options.process - - if process then - root = processRecursive(process, root, {}, {}) - end - - local inspector = setmetatable({ - depth = depth, - level = 0, - buffer = {}, - ids = {}, - maxIds = {}, - newline = newline, - indent = indent, - tableAppearances = countTableAppearances(root) - }, Inspector_mt) - - inspector:putValue(root) - - return table.concat(inspector.buffer) -end - -setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end }) - -return inspect - diff --git a/test/run.lua b/test/run.lua deleted file mode 100644 index 7acb9fc..0000000 --- a/test/run.lua +++ /dev/null @@ -1,278 +0,0 @@ -local lfs = require("lfs") -local anselme = require("anselme") -local ser = require("test.ser") -local inspect = require("test.inspect") - -local function format_text(t) - local r = "" - for _, l in ipairs(t) do - -- format tags display - local tags = "" - for k, v in pairs(l.tags) do - tags = tags .. ("[%q]=%q"):format(k, v) - end - -- build text - if tags ~= "" then - r = r .. ("[%s]%s"):format(tags, l.text) - else - r = r .. l.text - end - end - return r -end - -local function compare(a, b) - if type(a) == "table" and type(b) == "table" then - for k, v in pairs(a) do - if not compare(v, b[k]) then - return false - end - end - for k, v in pairs(b) do - if not compare(v, a[k]) then - return false - end - end - return true - else - return a == b - end -end - -local function write_result(filebase, result) - local o = assert(io.open(filebase..".lua", "w")) - o:write(ser(result)) - o:write("\n--[[\n") - for _, v in ipairs(result) do - o:write(inspect(v):gsub("]]", "] ]").."\n") -- professional-level bandaid when ]] appear in the output - end - o:write("]]--") - o:close() -end - --- parse args -local args = {} -local i=1 -while i <= #arg do - if arg[i+1] and not arg[i+1]:match("^%-%-") then - args[arg[i]:gsub("^%-%-", "")] = arg[i+1] - i = i + 2 - else - args[arg[i]:gsub("^%-%-", "")] = true - i = i + 1 - end -end - -if args.help then - print("Anselme test runner. Usage:") - print(" no arguments: perform included test suite") - print(" --script filename: test a script interactively") - print(" --game directory: test a game interactively") - print(" --help: display this message") - print("") - print("For test suite mode:") - print(" --filter pattern: only perform tests matching pattern") - print(" --write-all: rewrite all expected test results with current output") - print(" --write-new: write expected test results with current output for test that do not already have a saved expected output") - print(" --write-error: rewrite expected test results with current output for test with invalid output") - print(" --silent: silent output") - print("") - print("For script or game mode:") - print(" --lang code: load a language file") - print(" --save: print save data at the end of the script") - os.exit() -end - --- test script -if args.script or args.game then - local vm = anselme() - if args.lang then - assert(vm:loadlanguage(args.lang)) - end - local state, err - if args.script then - state, err = vm:loadfile(args.script, "script") - else - state, err = vm:loadgame(args.game) - end - if state then - local istate, e - if args.script then - istate, e = vm:run("script") - elseif args.game then - istate, e = vm:rungame() - end - if not istate then - print("error", e) - else - repeat - local t, d = istate:step() - if t == "text" then - print(format_text(d)) - elseif t == "choice" then - for j, choice in ipairs(d) do - print(j.."> "..format_text(choice)) - end - istate:choose(io.read()) - elseif t == "error" then - print(t, d) - else - print(t, inspect(d)) - end - until t == "return" or t == "error" - end - else - print("error", err) - end - if args.save then - local s, e = vm:save() - if s then - print(inspect(s)) - else - print(("Error while saving: %s"):format(e)) - end - end - --- test mode -else - -- list tests - local files = {} - for item in lfs.dir("test/tests/") do - if item:match("%.ans$") and item:match(args.filter or "") then - table.insert(files, "test/tests/"..item) - end - end - table.sort(files) - - -- run tests - local total, success = #files, 0 - for _, file in ipairs(files) do - local filebase = file:match("^(.*)%.ans$") - local namespace = filebase:match("([^/]*)$") - -- simple random to get the same result across lua versions - local prev = 0 - local function badrandom(a, b) - prev = (4241 * prev + 11) % 6997 - return a + prev % (b-a+1) - end - function math.random(a, b) - if not a and not b then - return badrandom(0, 999) / 1000 - elseif not b then - return badrandom(1, a) - else - return badrandom(a, b) - end - end - -- load vm - local vm = anselme() - vm:setaliases("seen", "checkpoint", "reached") - vm:loadfunction { - -- custom event test - ["wait(time::number)"] = { - value = function(duration) - coroutine.yield("wait", duration) - end - }, - -- run another function in parallel - ["run(name::string)"] = { - value = function(str) - local istate, e = anselme.running.vm:run(str, anselme.running:current_namespace()) - if not istate then coroutine.yield("error", e) end - local event, data = istate:step() - coroutine.yield(event, data) - end - }, - -- manual choice - ["choose(choice::number)"] = { - value = function(c) - anselme.running:choose(c) - end - }, - -- manual interrupt - ["interrupt(name::string)"] = { - value = function(str) - anselme.running:interrupt(str) - coroutine.yield("wait", 0) - end - }, - ["interrupt()"] = { - value = function() - anselme.running:interrupt() - coroutine.yield("wait", 0) - end - } - } - local state, err = vm:loadfile(file, namespace) - - local result = {} - if state then - local istate, e = vm:run(namespace) - if not istate then - table.insert(result, { "error", e }) - else - repeat - local t, d = istate:step() - table.insert(result, { t, d }) - until t == "return" or t == "error" - - local postrun = vm:eval(namespace..".post run") - if postrun then - istate, e = vm:run(namespace.."."..postrun) - if not istate then - table.insert(result, { "error", e }) - else - repeat - local t, d = istate:step() - table.insert(result, { t, d }) - until t == "return" or t == "error" - end - end - end - else - table.insert(result, { "error", err }) - end - - if args["write-all"] then - write_result(filebase, result) - else - local o, e = loadfile(filebase..".lua") - if o then - local output = o() - if not compare(result, output) then - if not args.silent then - print("> "..namespace) - print(inspect(result)) - print("is not equal to") - print(inspect(output)) - print("") - end - if args["write-error"] then - write_result(filebase, result) - print("Rewritten result file for "..filebase) - success = success + 1 - end - else - success = success + 1 - end - else - if args["write-new"] and e:match("No such file") then - write_result(filebase, result) - print("Written result file for "..filebase) - success = success + 1 - elseif not args.silent then - print("> "..namespace) - print(e) - print("result was:") - print(inspect(result)) - print("") - end - end - end - end - if args["write-all"] then - print("Wrote test results.") - else - print(("%s/%s tests passed."):format(success, total)) - end -end diff --git a/test/ser.lua b/test/ser.lua deleted file mode 100644 index a056a11..0000000 --- a/test/ser.lua +++ /dev/null @@ -1,143 +0,0 @@ ---[[ -Copyright (c) 2011,2013 Robin Wellner - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -]] - -local pairs, ipairs, tostring, type, concat, dump, floor, format = pairs, ipairs, tostring, type, table.concat, string.dump, math.floor, string.format - -local function getchr(c) - return "\\" .. c:byte() -end - -local function make_safe(text) - return ("%q"):format(text):gsub('\n', 'n'):gsub("[\128-\255]", getchr) -end - -local oddvals = {[tostring(1/0)] = '1/0', [tostring(-1/0)] = '-1/0', [tostring(-(0/0))] = '-(0/0)', [tostring(0/0)] = '0/0'} -local function write(t, memo, rev_memo) - local ty = type(t) - if ty == 'number' then - t = format("%.17g", t) - return oddvals[t] or t - elseif ty == 'boolean' or ty == 'nil' then - return tostring(t) - elseif ty == 'string' then - return make_safe(t) - elseif ty == 'table' or ty == 'function' then - if not memo[t] then - local index = #rev_memo + 1 - memo[t] = index - rev_memo[index] = t - end - return '_[' .. memo[t] .. ']' - else - error("Trying to serialize unsupported type " .. ty) - end -end - -local kw = {['and'] = true, ['break'] = true, ['do'] = true, ['else'] = true, - ['elseif'] = true, ['end'] = true, ['false'] = true, ['for'] = true, - ['function'] = true, ['goto'] = true, ['if'] = true, ['in'] = true, - ['local'] = true, ['nil'] = true, ['not'] = true, ['or'] = true, - ['repeat'] = true, ['return'] = true, ['then'] = true, ['true'] = true, - ['until'] = true, ['while'] = true} -local function write_key_value_pair(k, v, memo, rev_memo, name) - if type(k) == 'string' and k:match '^[_%a][_%w]*$' and not kw[k] then - return (name and name .. '.' or '') .. k ..'=' .. write(v, memo, rev_memo) - else - return (name or '') .. '[' .. write(k, memo, rev_memo) .. ']=' .. write(v, memo, rev_memo) - end -end - --- fun fact: this function is not perfect --- it has a few false positives sometimes --- but no false negatives, so that's good -local function is_cyclic(memo, sub, super) - local m = memo[sub] - local p = memo[super] - return m and p and m < p -end - -local function write_table_ex(t, memo, rev_memo, srefs, name) - if type(t) == 'function' then - return '_[' .. name .. ']=loadstring' .. make_safe(dump(t)) - end - local m = {} - local mi = 1 - for i = 1, #t do -- don't use ipairs here, we need the gaps - local v = t[i] - if v == t or is_cyclic(memo, v, t) then - srefs[#srefs + 1] = {name, i, v} - m[mi] = 'nil' - mi = mi + 1 - else - m[mi] = write(v, memo, rev_memo) - mi = mi + 1 - end - end - for k,v in pairs(t) do - if type(k) ~= 'number' or floor(k) ~= k or k < 1 or k > #t then - if v == t or k == t or is_cyclic(memo, v, t) or is_cyclic(memo, k, t) then - srefs[#srefs + 1] = {name, k, v} - else - m[mi] = write_key_value_pair(k, v, memo, rev_memo) - mi = mi + 1 - end - end - end - return '_[' .. name .. ']={' .. concat(m, ',') .. '}' -end - -return function(t) - local memo = {[t] = 0} - local rev_memo = {[0] = t} - local srefs = {} - local result = {} - - -- phase 1: recursively descend the table structure - local n = 0 - while rev_memo[n] do - result[n + 1] = write_table_ex(rev_memo[n], memo, rev_memo, srefs, n) - n = n + 1 - end - - -- phase 2: reverse order - for i = 1, n*.5 do - local j = n - i + 1 - result[i], result[j] = result[j], result[i] - end - - -- phase 3: add all the tricky cyclic stuff - for i, v in ipairs(srefs) do - n = n + 1 - result[n] = write_key_value_pair(v[2], v[3], memo, rev_memo, '_[' .. v[1] .. ']') - end - - -- phase 4: add something about returning the main table - if result[n]:sub(1, 5) == '_[0]=' then - result[n] = 'return ' .. result[n]:sub(6) - else - result[n + 1] = 'return _[0]' - end - - -- phase 5: just concatenate everything - result = concat(result, '\n') - return n > 1 and 'local _={}\n' .. result or result -end diff --git a/test/tests/anonymous function.ans b/test/tests/anonymous function.ans deleted file mode 100644 index aa1e0eb..0000000 --- a/test/tests/anonymous function.ans +++ /dev/null @@ -1,21 +0,0 @@ -:f = $(x)x*x - -:$g(x) - @x*x - -{f(5)} = {g(5)} - -{f(2)} = {g(2)} - -:y = 5 -:h = $(x)x*x+y - -{h(3)} == 14 - -~ y := 7 - -{h(5)} == 32 - -:i = $y*y - -{i} == 49 diff --git a/test/tests/anonymous function.lua b/test/tests/anonymous function.lua deleted file mode 100644 index 0300588..0000000 --- a/test/tests/anonymous function.lua +++ /dev/null @@ -1,81 +0,0 @@ -local _={} -_[35]={} -_[34]={} -_[33]={} -_[32]={} -_[31]={} -_[30]={} -_[29]={} -_[28]={} -_[27]={} -_[26]={} -_[25]={} -_[24]={} -_[23]={tags=_[35],text=" == 49"} -_[22]={tags=_[34],text="49"} -_[21]={tags=_[33],text=" == 32"} -_[20]={tags=_[32],text="32"} -_[19]={tags=_[31],text=" == 14"} -_[18]={tags=_[30],text="14"} -_[17]={tags=_[29],text="4"} -_[16]={tags=_[28],text=" = "} -_[15]={tags=_[27],text="4"} -_[14]={tags=_[26],text="25"} -_[13]={tags=_[25],text=" = "} -_[12]={tags=_[24],text="25"} -_[11]={_[22],_[23]} -_[10]={_[20],_[21]} -_[9]={_[18],_[19]} -_[8]={_[15],_[16],_[17]} -_[7]={_[12],_[13],_[14]} -_[6]={"return"} -_[5]={"text",_[11]} -_[4]={"text",_[10]} -_[3]={"text",_[9]} -_[2]={"text",_[8]} -_[1]={"text",_[7]} -return {_[1],_[2],_[3],_[4],_[5],_[6]} ---[[ -{ "text", { { - tags = {}, - text = "25" - }, { - tags = {}, - text = " = " - }, { - tags = {}, - text = "25" - } } } -{ "text", { { - tags = {}, - text = "4" - }, { - tags = {}, - text = " = " - }, { - tags = {}, - text = "4" - } } } -{ "text", { { - tags = {}, - text = "14" - }, { - tags = {}, - text = " == 14" - } } } -{ "text", { { - tags = {}, - text = "32" - }, { - tags = {}, - text = " == 32" - } } } -{ "text", { { - tags = {}, - text = "49" - }, { - tags = {}, - text = " == 49" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/argument alias.ans b/test/tests/argument alias.ans deleted file mode 100644 index de6f9a9..0000000 --- a/test/tests/argument alias.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ f(str: foo) - @str + foo - -{f("bi")} = {f(foo="bi")} diff --git a/test/tests/argument alias.lua b/test/tests/argument alias.lua deleted file mode 100644 index 13b38f4..0000000 --- a/test/tests/argument alias.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[7]={} -_[6]={tags=_[7],text="bibi"} -_[5]={tags=_[7],text=" = "} -_[4]={tags=_[7],text="bibi"} -_[3]={_[4],_[5],_[6]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "bibi" - }, { - tags =
, - text = " = " - }, { - tags =
, - text = "bibi" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/binary operator overload.ans b/test/tests/binary operator overload.ans deleted file mode 100644 index 2fe7581..0000000 --- a/test/tests/binary operator overload.ans +++ /dev/null @@ -1,11 +0,0 @@ -:$ _-_(a, b) - @"generic minus" - -:$ _-_(a::string, b::string) - @a + " minus " + b - -{2-5} - -{"heh"-"lol"} - -{[]-[]} diff --git a/test/tests/binary operator overload.lua b/test/tests/binary operator overload.lua deleted file mode 100644 index 991726f..0000000 --- a/test/tests/binary operator overload.lua +++ /dev/null @@ -1,30 +0,0 @@ -local _={} -_[13]={} -_[12]={} -_[11]={} -_[10]={tags=_[13],text="generic minus"} -_[9]={tags=_[12],text="heh minus lol"} -_[8]={tags=_[11],text="-3"} -_[7]={_[10]} -_[6]={_[9]} -_[5]={_[8]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "-3" - } } } -{ "text", { { - tags = {}, - text = "heh minus lol" - } } } -{ "text", { { - tags = {}, - text = "generic minus" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/binop assignement.ans b/test/tests/binop assignement.ans deleted file mode 100644 index 6fbeacd..0000000 --- a/test/tests/binop assignement.ans +++ /dev/null @@ -1,7 +0,0 @@ -:c = 1 - -{c} - -~ c += 2 - -{c} \ No newline at end of file diff --git a/test/tests/binop assignement.lua b/test/tests/binop assignement.lua deleted file mode 100644 index 95ace50..0000000 --- a/test/tests/binop assignement.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[9]={} -_[8]={} -_[7]={tags=_[9],text="3"} -_[6]={tags=_[8],text="1"} -_[5]={_[7]} -_[4]={_[6]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/checkpoint change.ans b/test/tests/checkpoint change.ans deleted file mode 100644 index 1fdbfe9..0000000 --- a/test/tests/checkpoint change.ans +++ /dev/null @@ -1,33 +0,0 @@ -:$ f - x - :! p - a - - :! q - b - - c - - d - -From start: -~ f - -From p checkpoint: -~ f - -From q checkpoint: -~ f - -From q checkpoint again: -~ f - -Force p checkpoint: -~ f.p() - -From q again: -~ f - -Go to p again by setting checkpoint manually: -~ f.checkpoint := &f.p -~ f diff --git a/test/tests/checkpoint change.lua b/test/tests/checkpoint change.lua deleted file mode 100644 index 6eea480..0000000 --- a/test/tests/checkpoint change.lua +++ /dev/null @@ -1,193 +0,0 @@ -local _={} -_[91]={} -_[90]={} -_[89]={} -_[88]={} -_[87]={} -_[86]={} -_[85]={} -_[84]={} -_[83]={} -_[82]={} -_[81]={} -_[80]={} -_[79]={} -_[78]={} -_[77]={} -_[76]={} -_[75]={} -_[74]={} -_[73]={} -_[72]={} -_[71]={} -_[70]={} -_[69]={} -_[68]={} -_[67]={} -_[66]={} -_[65]={tags=_[91],text="d"} -_[64]={tags=_[90],text="c"} -_[63]={tags=_[89],text="a"} -_[62]={tags=_[88],text="Go to p again by setting checkpoint manually:"} -_[61]={tags=_[87],text="d"} -_[60]={tags=_[86],text="c"} -_[59]={tags=_[85],text="b"} -_[58]={tags=_[84],text="From q again:"} -_[57]={tags=_[83],text="c"} -_[56]={tags=_[82],text="a"} -_[55]={tags=_[81],text="Force p checkpoint:"} -_[54]={tags=_[80],text="d"} -_[53]={tags=_[79],text="c"} -_[52]={tags=_[78],text="b"} -_[51]={tags=_[77],text="From q checkpoint again:"} -_[50]={tags=_[76],text="d"} -_[49]={tags=_[75],text="c"} -_[48]={tags=_[74],text="b"} -_[47]={tags=_[73],text="From q checkpoint:"} -_[46]={tags=_[72],text="d"} -_[45]={tags=_[71],text="c"} -_[44]={tags=_[70],text="a"} -_[43]={tags=_[69],text="From p checkpoint:"} -_[42]={tags=_[68],text="d"} -_[41]={tags=_[67],text="x"} -_[40]={tags=_[66],text="From start:"} -_[39]={_[65]} -_[38]={_[64]} -_[37]={_[62],_[63]} -_[36]={_[61]} -_[35]={_[60]} -_[34]={_[58],_[59]} -_[33]={_[57]} -_[32]={_[55],_[56]} -_[31]={_[54]} -_[30]={_[53]} -_[29]={_[51],_[52]} -_[28]={_[50]} -_[27]={_[49]} -_[26]={_[47],_[48]} -_[25]={_[46]} -_[24]={_[45]} -_[23]={_[43],_[44]} -_[22]={_[42]} -_[21]={_[40],_[41]} -_[20]={"return"} -_[19]={"text",_[39]} -_[18]={"text",_[38]} -_[17]={"text",_[37]} -_[16]={"text",_[36]} -_[15]={"text",_[35]} -_[14]={"text",_[34]} -_[13]={"text",_[33]} -_[12]={"text",_[32]} -_[11]={"text",_[31]} -_[10]={"text",_[30]} -_[9]={"text",_[29]} -_[8]={"text",_[28]} -_[7]={"text",_[27]} -_[6]={"text",_[26]} -_[5]={"text",_[25]} -_[4]={"text",_[24]} -_[3]={"text",_[23]} -_[2]={"text",_[22]} -_[1]={"text",_[21]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15],_[16],_[17],_[18],_[19],_[20]} ---[[ -{ "text", { { - tags = {}, - text = "From start:" - }, { - tags = {}, - text = "x" - } } } -{ "text", { { - tags = {}, - text = "d" - } } } -{ "text", { { - tags = {}, - text = "From p checkpoint:" - }, { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "d" - } } } -{ "text", { { - tags = {}, - text = "From q checkpoint:" - }, { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "d" - } } } -{ "text", { { - tags = {}, - text = "From q checkpoint again:" - }, { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "d" - } } } -{ "text", { { - tags = {}, - text = "Force p checkpoint:" - }, { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "From q again:" - }, { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "d" - } } } -{ "text", { { - tags = {}, - text = "Go to p again by setting checkpoint manually:" - }, { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "d" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/checkpoint merging mutable value.ans b/test/tests/checkpoint merging mutable value.ans deleted file mode 100644 index 3a793fc..0000000 --- a/test/tests/checkpoint merging mutable value.ans +++ /dev/null @@ -1,26 +0,0 @@ -:post run = "after error" - -:l = [1,2] - -1,2: {l} - -~ l!insert(3) - -1,2,3: {l} - -:! a - -~ l!insert(4) - -1,2,3,4: {l} - -:! b - -~ l!insert(5) - -1,2,3,4,5: {l} - -~ error("cancel merge") - -:$ after error - 1,2,3,4: {l} diff --git a/test/tests/checkpoint merging mutable value.lua b/test/tests/checkpoint merging mutable value.lua deleted file mode 100644 index 076460b..0000000 --- a/test/tests/checkpoint merging mutable value.lua +++ /dev/null @@ -1,68 +0,0 @@ -local _={} -_[27]={} -_[26]={} -_[25]={} -_[24]={} -_[23]={} -_[22]={tags=_[27],text="[1, 2, 3, 4]"} -_[21]={tags=_[27],text="1,2,3,4: "} -_[20]={tags=_[26],text="[1, 2, 3, 4, 5]"} -_[19]={tags=_[26],text="1,2,3,4,5: "} -_[18]={tags=_[25],text="[1, 2, 3, 4]"} -_[17]={tags=_[25],text="1,2,3,4: "} -_[16]={tags=_[24],text="[1, 2, 3]"} -_[15]={tags=_[24],text="1,2,3: "} -_[14]={tags=_[23],text="[1, 2]"} -_[13]={tags=_[23],text="1,2: "} -_[12]={_[21],_[22]} -_[11]={_[19],_[20]} -_[10]={_[17],_[18]} -_[9]={_[15],_[16]} -_[8]={_[13],_[14]} -_[7]={"return"} -_[6]={"text",_[12]} -_[5]={"error","cancel merge; in Lua function \"error\"; at test/tests/checkpoint merging mutable value.ans:23"} -_[4]={"text",_[11]} -_[3]={"text",_[10]} -_[2]={"text",_[9]} -_[1]={"text",_[8]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7]} ---[[ -{ "text", { { - tags = <1>{}, - text = "1,2: " - }, { - tags =
, - text = "[1, 2]" - } } } -{ "text", { { - tags = <1>{}, - text = "1,2,3: " - }, { - tags =
, - text = "[1, 2, 3]" - } } } -{ "text", { { - tags = <1>{}, - text = "1,2,3,4: " - }, { - tags =
, - text = "[1, 2, 3, 4]" - } } } -{ "text", { { - tags = <1>{}, - text = "1,2,3,4,5: " - }, { - tags =
, - text = "[1, 2, 3, 4, 5]" - } } } -{ "error", 'cancel merge; in Lua function "error"; at test/tests/checkpoint merging mutable value.ans:23' } -{ "text", { { - tags = <1>{}, - text = "1,2,3,4: " - }, { - tags =
, - text = "[1, 2, 3, 4]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/checkpoint merging variable.ans b/test/tests/checkpoint merging variable.ans deleted file mode 100644 index 037fd58..0000000 --- a/test/tests/checkpoint merging variable.ans +++ /dev/null @@ -1,26 +0,0 @@ -:post run = "after error" - -:l = 1 - -1: {l} - -~ l := 2 - -2: {l} - -:! a - -~ l := 3 - -3: {l} - -:! b - -~ l := 4 - -4: {l} - -~ error("cancel merge") - -:$ after error - 3: {l} diff --git a/test/tests/checkpoint merging variable.lua b/test/tests/checkpoint merging variable.lua deleted file mode 100644 index c471455..0000000 --- a/test/tests/checkpoint merging variable.lua +++ /dev/null @@ -1,68 +0,0 @@ -local _={} -_[27]={} -_[26]={} -_[25]={} -_[24]={} -_[23]={} -_[22]={tags=_[27],text="3"} -_[21]={tags=_[27],text="3: "} -_[20]={tags=_[26],text="4"} -_[19]={tags=_[26],text="4: "} -_[18]={tags=_[25],text="3"} -_[17]={tags=_[25],text="3: "} -_[16]={tags=_[24],text="2"} -_[15]={tags=_[24],text="2: "} -_[14]={tags=_[23],text="1"} -_[13]={tags=_[23],text="1: "} -_[12]={_[21],_[22]} -_[11]={_[19],_[20]} -_[10]={_[17],_[18]} -_[9]={_[15],_[16]} -_[8]={_[13],_[14]} -_[7]={"return"} -_[6]={"text",_[12]} -_[5]={"error","cancel merge; in Lua function \"error\"; at test/tests/checkpoint merging variable.ans:23"} -_[4]={"text",_[11]} -_[3]={"text",_[10]} -_[2]={"text",_[9]} -_[1]={"text",_[8]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7]} ---[[ -{ "text", { { - tags = <1>{}, - text = "1: " - }, { - tags =
, - text = "1" - } } } -{ "text", { { - tags = <1>{}, - text = "2: " - }, { - tags =
, - text = "2" - } } } -{ "text", { { - tags = <1>{}, - text = "3: " - }, { - tags =
, - text = "3" - } } } -{ "text", { { - tags = <1>{}, - text = "4: " - }, { - tags =
, - text = "4" - } } } -{ "error", 'cancel merge; in Lua function "error"; at test/tests/checkpoint merging variable.ans:23' } -{ "text", { { - tags = <1>{}, - text = "3: " - }, { - tags =
, - text = "3" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/checkpoint reached seen.ans b/test/tests/checkpoint reached seen.ans deleted file mode 100644 index 3619559..0000000 --- a/test/tests/checkpoint reached seen.ans +++ /dev/null @@ -1,8 +0,0 @@ -:! p - seen! - -Seen: {p.seen} - -Reached: {p.reached} - -@p ~ !p.seen diff --git a/test/tests/checkpoint reached seen.lua b/test/tests/checkpoint reached seen.lua deleted file mode 100644 index 6093d12..0000000 --- a/test/tests/checkpoint reached seen.lua +++ /dev/null @@ -1,66 +0,0 @@ -local _={} -_[29]={} -_[28]={} -_[27]={} -_[26]={} -_[25]={} -_[24]={} -_[23]={} -_[22]={} -_[21]={} -_[20]={text="1",tags=_[29]} -_[19]={text="Reached: ",tags=_[28]} -_[18]={text="1",tags=_[27]} -_[17]={text="Seen: ",tags=_[26]} -_[16]={text="seen!",tags=_[25]} -_[15]={text="1",tags=_[24]} -_[14]={text="Reached: ",tags=_[23]} -_[13]={text="0",tags=_[22]} -_[12]={text="Seen: ",tags=_[21]} -_[11]={_[19],_[20]} -_[10]={_[17],_[18]} -_[9]={_[16]} -_[8]={_[14],_[15]} -_[7]={_[12],_[13]} -_[6]={"return"} -_[5]={"text",_[11]} -_[4]={"text",_[10]} -_[3]={"text",_[9]} -_[2]={"text",_[8]} -_[1]={"text",_[7]} -return {_[1],_[2],_[3],_[4],_[5],_[6]} ---[[ -{ "text", { { - tags = {}, - text = "Seen: " - }, { - tags = {}, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "Reached: " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "seen!" - } } } -{ "text", { { - tags = {}, - text = "Seen: " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "Reached: " - }, { - tags = {}, - text = "1" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/choice block.ans b/test/tests/choice block.ans deleted file mode 100644 index b65818e..0000000 --- a/test/tests/choice block.ans +++ /dev/null @@ -1,11 +0,0 @@ -> ye - no -> ne - ok -~ choose(2) - -> ho - plop -> oh - plup -~ choose(1) \ No newline at end of file diff --git a/test/tests/choice block.lua b/test/tests/choice block.lua deleted file mode 100644 index 7edb258..0000000 --- a/test/tests/choice block.lua +++ /dev/null @@ -1,50 +0,0 @@ -local _={} -_[23]={} -_[22]={} -_[21]={} -_[20]={tags=_[23],text="oh"} -_[19]={tags=_[21],text="ho"} -_[18]={} -_[17]={tags=_[18],text="ne"} -_[16]={tags=_[22],text="ye"} -_[15]={tags=_[21],text="plop"} -_[14]={_[20]} -_[13]={_[19]} -_[12]={tags=_[18],text="ok"} -_[11]={_[17]} -_[10]={_[16]} -_[9]={_[15]} -_[8]={_[13],_[14]} -_[7]={_[12]} -_[6]={_[10],_[11]} -_[5]={"return"} -_[4]={"text",_[9]} -_[3]={"choice",_[8]} -_[2]={"text",_[7]} -_[1]={"choice",_[6]} -return {_[1],_[2],_[3],_[4],_[5]} ---[[ -{ "choice", { { { - tags = {}, - text = "ye" - } }, { { - tags = {}, - text = "ne" - } } } } -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "choice", { { { - tags = {}, - text = "ho" - } }, { { - tags = {}, - text = "oh" - } } } } -{ "text", { { - tags = {}, - text = "plop" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/choice function.ans b/test/tests/choice function.ans deleted file mode 100644 index e4e3fcc..0000000 --- a/test/tests/choice function.ans +++ /dev/null @@ -1,11 +0,0 @@ -:$ f - > neol - nah - -> ho - plop -~ f -> oh - ok -~ f -~ choose(3) \ No newline at end of file diff --git a/test/tests/choice function.lua b/test/tests/choice function.lua deleted file mode 100644 index b965c9c..0000000 --- a/test/tests/choice function.lua +++ /dev/null @@ -1,40 +0,0 @@ -local _={} -_[18]={} -_[17]={} -_[16]={} -_[15]={} -_[14]={tags=_[18],text="neol"} -_[13]={tags=_[15],text="oh"} -_[12]={tags=_[17],text="neol"} -_[11]={tags=_[16],text="ho"} -_[10]={tags=_[15],text="ok"} -_[9]={_[14]} -_[8]={_[13]} -_[7]={_[12]} -_[6]={_[11]} -_[5]={_[10]} -_[4]={_[6],_[7],_[8],_[9]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"choice",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "choice", { { { - tags = {}, - text = "ho" - } }, { { - tags = {}, - text = "neol" - } }, { { - tags = {}, - text = "oh" - } }, { { - tags = {}, - text = "neol" - } } } } -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/choice line interpolation with choice event.ans b/test/tests/choice line interpolation with choice event.ans deleted file mode 100644 index 3ba50b9..0000000 --- a/test/tests/choice line interpolation with choice event.ans +++ /dev/null @@ -1,10 +0,0 @@ -~ choose(1) -> Press {jump button} to jump. - ok -> No - ko - -:$ jump button - A # 1 - > Suprise choice! - @"JOIN" diff --git a/test/tests/choice line interpolation with choice event.lua b/test/tests/choice line interpolation with choice event.lua deleted file mode 100644 index f7f45e6..0000000 --- a/test/tests/choice line interpolation with choice event.lua +++ /dev/null @@ -1,47 +0,0 @@ -local _={} -_[19]={} -_[18]={} -_[17]={1} -_[16]={} -_[15]={text="No",tags=_[19]} -_[14]={text=" to jump.",tags=_[16]} -_[13]={text="JOIN",tags=_[16]} -_[12]={text="Suprise choice!",tags=_[18]} -_[11]={text="A",tags=_[17]} -_[10]={text="Press ",tags=_[16]} -_[9]={text="ok",tags=_[16]} -_[8]={_[15]} -_[7]={_[12],_[13],_[14]} -_[6]={_[10],_[11]} -_[5]={_[9]} -_[4]={_[6],_[7],_[8]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"choice",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "choice", { { { - tags = <1>{}, - text = "Press " - }, { - tags = { 1 }, - text = "A" - } }, { { - tags = {}, - text = "Suprise choice!" - }, { - tags =
, - text = "JOIN" - }, { - tags =
, - text = " to jump." - } }, { { - tags = {}, - text = "No" - } } } } -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/choice line interpolation with event flush.ans b/test/tests/choice line interpolation with event flush.ans deleted file mode 100644 index bf239c4..0000000 --- a/test/tests/choice line interpolation with event flush.ans +++ /dev/null @@ -1,11 +0,0 @@ -~ choose(1) -> Press {jump button} to jump. - ok - ~ choose(1) -> No - ko - -:$ jump button - A # 1 - - @"SPLIT" diff --git a/test/tests/choice line interpolation with event flush.lua b/test/tests/choice line interpolation with event flush.lua deleted file mode 100644 index 22cc45a..0000000 --- a/test/tests/choice line interpolation with event flush.lua +++ /dev/null @@ -1,55 +0,0 @@ -local _={} -_[22]={} -_[21]={1} -_[20]={tags=_[22],text="No"} -_[19]={text=" to jump."} -_[18]={text="SPLIT"} -_[17]={} -_[16]={tags=_[21],text="A"} -_[15]={tags=_[17],text="Press "} -_[14]={tags=_[17],text="ok"} -_[13]={_[20]} -_[12]={_[18],_[19]} -_[11]={tags=_[17],text="ok"} -_[10]={_[15],_[16]} -_[9]={_[14]} -_[8]={_[12],_[13]} -_[7]={_[11]} -_[6]={_[10]} -_[5]={"return"} -_[4]={"text",_[9]} -_[3]={"choice",_[8]} -_[2]={"text",_[7]} -_[1]={"choice",_[6]} -_[0]={_[1],_[2],_[3],_[4],_[5]} -_[18].tags=_[17] -_[19].tags=_[17] -return _[0] ---[[ -{ "choice", { { { - tags = {}, - text = "Press " - }, { - tags = { 1 }, - text = "A" - } } } } -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "choice", { { { - tags = <1>{}, - text = "SPLIT" - }, { - tags =
, - text = " to jump." - } }, { { - tags = {}, - text = "No" - } } } } -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/choice line interpolation with text event.ans b/test/tests/choice line interpolation with text event.ans deleted file mode 100644 index d149de8..0000000 --- a/test/tests/choice line interpolation with text event.ans +++ /dev/null @@ -1,18 +0,0 @@ -> Press {jump button} to jump. - ok -> No - ko -~ choose(1) - -:$ jump button - A # 1 - -> Other - ko -> Use {move axis} to move. - ok -~ choose(2) - -:$ move axis - left # 1 - @" joystick" diff --git a/test/tests/choice line interpolation with text event.lua b/test/tests/choice line interpolation with text event.lua deleted file mode 100644 index 12204c3..0000000 --- a/test/tests/choice line interpolation with text event.lua +++ /dev/null @@ -1,72 +0,0 @@ -local _={} -_[30]={1} -_[29]={} -_[28]={} -_[27]={1} -_[26]={} -_[25]={text=" to move.",tags=_[26]} -_[24]={text="joystick",tags=_[26]} -_[23]={text="left ",tags=_[30]} -_[22]={text="Use ",tags=_[26]} -_[21]={text="Other",tags=_[29]} -_[20]={} -_[19]={text="No",tags=_[28]} -_[18]={text="to jump.",tags=_[20]} -_[17]={text="A ",tags=_[27]} -_[16]={text="Press ",tags=_[20]} -_[15]={text="ok",tags=_[26]} -_[14]={_[22],_[23],_[24],_[25]} -_[13]={_[21]} -_[12]={text="ok",tags=_[20]} -_[11]={_[19]} -_[10]={_[16],_[17],_[18]} -_[9]={_[15]} -_[8]={_[13],_[14]} -_[7]={_[12]} -_[6]={_[10],_[11]} -_[5]={"return"} -_[4]={"text",_[9]} -_[3]={"choice",_[8]} -_[2]={"text",_[7]} -_[1]={"choice",_[6]} -return {_[1],_[2],_[3],_[4],_[5]} ---[[ -{ "choice", { { { - tags = <1>{}, - text = "Press " - }, { - tags = { 1 }, - text = "A " - }, { - tags =
, - text = "to jump." - } }, { { - tags = {}, - text = "No" - } } } } -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "choice", { { { - tags = {}, - text = "Other" - } }, { { - tags = <1>{}, - text = "Use " - }, { - tags = { 1 }, - text = "left " - }, { - tags =
, - text = "joystick" - }, { - tags =
, - text = " to move." - } } } } -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/choice preserve tags.ans b/test/tests/choice preserve tags.ans deleted file mode 100644 index 491541a..0000000 --- a/test/tests/choice preserve tags.ans +++ /dev/null @@ -1,16 +0,0 @@ -:$ f - # 42 - > a - b - -~ f -> c -~ choose(1) - -# "k"="v" - ~ f - > d - ~ choose(1) - e - -f diff --git a/test/tests/choice preserve tags.lua b/test/tests/choice preserve tags.lua deleted file mode 100644 index 90cff95..0000000 --- a/test/tests/choice preserve tags.lua +++ /dev/null @@ -1,73 +0,0 @@ -local _={} -_[30]={} -_[29]={} -_[28]={k="v"} -_[27]={42,k="v"} -_[26]={tags=_[28],text="d"} -_[25]={tags=_[27],text="a"} -_[24]={42} -_[23]={tags=_[30],text="c"} -_[22]={tags=_[24],text="a"} -_[21]={tags=_[29],text="f"} -_[20]={tags=_[28],text="e"} -_[19]={tags=_[27],text="b"} -_[18]={_[26]} -_[17]={_[25]} -_[16]={tags=_[24],text="b"} -_[15]={_[23]} -_[14]={_[22]} -_[13]={_[21]} -_[12]={_[20]} -_[11]={_[19]} -_[10]={_[17],_[18]} -_[9]={_[16]} -_[8]={_[14],_[15]} -_[7]={"return"} -_[6]={"text",_[13]} -_[5]={"text",_[12]} -_[4]={"text",_[11]} -_[3]={"choice",_[10]} -_[2]={"text",_[9]} -_[1]={"choice",_[8]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7]} ---[[ -{ "choice", { { { - tags = { 42 }, - text = "a" - } }, { { - tags = {}, - text = "c" - } } } } -{ "text", { { - tags = { 42 }, - text = "b" - } } } -{ "choice", { { { - tags = { 42, - k = "v" - }, - text = "a" - } }, { { - tags = { - k = "v" - }, - text = "d" - } } } } -{ "text", { { - tags = { 42, - k = "v" - }, - text = "b" - } } } -{ "text", { { - tags = { - k = "v" - }, - text = "e" - } } } -{ "text", { { - tags = {}, - text = "f" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/choice simple.ans b/test/tests/choice simple.ans deleted file mode 100644 index 085090a..0000000 --- a/test/tests/choice simple.ans +++ /dev/null @@ -1,5 +0,0 @@ -> ye - no -> ne - ok -~ choose(2) diff --git a/test/tests/choice simple.lua b/test/tests/choice simple.lua deleted file mode 100644 index 88e67b6..0000000 --- a/test/tests/choice simple.lua +++ /dev/null @@ -1,28 +0,0 @@ -local _={} -_[12]={} -_[11]={} -_[10]={tags=_[11],text="ne"} -_[9]={tags=_[12],text="ye"} -_[8]={tags=_[11],text="ok"} -_[7]={_[10]} -_[6]={_[9]} -_[5]={_[8]} -_[4]={_[6],_[7]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"choice",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "choice", { { { - tags = {}, - text = "ye" - } }, { { - tags = {}, - text = "ne" - } } } } -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/choice with decorators.ans b/test/tests/choice with decorators.ans deleted file mode 100644 index 7c62089..0000000 --- a/test/tests/choice with decorators.ans +++ /dev/null @@ -1,35 +0,0 @@ -> a ~ 1 - -> a -> b - -> b -~ choose(1) - -> a ~ 1 - -> a -> b - -> b -~ choose(2) - -> a ~ 0 - -> a -> b - -> b -~ choose(1) - -> a - -> a -> b # 25 - -> b -~ choose(2) - -> a ~ 0 # 12 - -> a -> b # 3 - -> b -~ choose(1) - -> a ~ 1 # 12 - -> a -> b # 3 - -> b -~ choose(1) diff --git a/test/tests/choice with decorators.lua b/test/tests/choice with decorators.lua deleted file mode 100644 index f841139..0000000 --- a/test/tests/choice with decorators.lua +++ /dev/null @@ -1,132 +0,0 @@ -local _={} -_[67]={3} -_[66]={12} -_[65]={3} -_[64]={25} -_[63]={} -_[62]={} -_[61]={} -_[60]={} -_[59]={} -_[58]={} -_[57]={} -_[56]={text="b",tags=_[67]} -_[55]={text="a",tags=_[66]} -_[54]={} -_[53]={text="b",tags=_[65]} -_[52]={} -_[51]={text="b",tags=_[64]} -_[50]={text="a",tags=_[63]} -_[49]={} -_[48]={text="b",tags=_[62]} -_[47]={} -_[46]={text="b",tags=_[61]} -_[45]={text="a",tags=_[60]} -_[44]={} -_[43]={text="b",tags=_[59]} -_[42]={text="a",tags=_[58]} -_[41]={text="-> a",tags=_[57]} -_[40]={_[56]} -_[39]={_[55]} -_[38]={text="-> b",tags=_[54]} -_[37]={_[53]} -_[36]={text="-> b",tags=_[52]} -_[35]={_[51]} -_[34]={_[50]} -_[33]={text="-> b",tags=_[49]} -_[32]={_[48]} -_[31]={text="-> b",tags=_[47]} -_[30]={_[46]} -_[29]={_[45]} -_[28]={text="-> a",tags=_[44]} -_[27]={_[43]} -_[26]={_[42]} -_[25]={_[41]} -_[24]={_[39],_[40]} -_[23]={_[38]} -_[22]={_[37]} -_[21]={_[36]} -_[20]={_[34],_[35]} -_[19]={_[33]} -_[18]={_[32]} -_[17]={_[31]} -_[16]={_[29],_[30]} -_[15]={_[28]} -_[14]={_[26],_[27]} -_[13]={"return"} -_[12]={"text",_[25]} -_[11]={"choice",_[24]} -_[10]={"text",_[23]} -_[9]={"choice",_[22]} -_[8]={"text",_[21]} -_[7]={"choice",_[20]} -_[6]={"text",_[19]} -_[5]={"choice",_[18]} -_[4]={"text",_[17]} -_[3]={"choice",_[16]} -_[2]={"text",_[15]} -_[1]={"choice",_[14]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13]} ---[[ -{ "choice", { { { - tags = {}, - text = "a" - } }, { { - tags = {}, - text = "b" - } } } } -{ "text", { { - tags = {}, - text = "-> a" - } } } -{ "choice", { { { - tags = {}, - text = "a" - } }, { { - tags = {}, - text = "b" - } } } } -{ "text", { { - tags = {}, - text = "-> b" - } } } -{ "choice", { { { - tags = {}, - text = "b" - } } } } -{ "text", { { - tags = {}, - text = "-> b" - } } } -{ "choice", { { { - tags = {}, - text = "a" - } }, { { - tags = { 25 }, - text = "b" - } } } } -{ "text", { { - tags = {}, - text = "-> b" - } } } -{ "choice", { { { - tags = { 3 }, - text = "b" - } } } } -{ "text", { { - tags = {}, - text = "-> b" - } } } -{ "choice", { { { - tags = { 12 }, - text = "a" - } }, { { - tags = { 3 }, - text = "b" - } } } } -{ "text", { { - tags = {}, - text = "-> a" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/comment block.ans b/test/tests/comment block.ans deleted file mode 100644 index 4172e97..0000000 --- a/test/tests/comment block.ans +++ /dev/null @@ -1,7 +0,0 @@ -(hey couic + 5) - other stuff - - CHAZOUM - OO - - k diff --git a/test/tests/comment block.lua b/test/tests/comment block.lua deleted file mode 100644 index 142a8c2..0000000 --- a/test/tests/comment block.lua +++ /dev/null @@ -1,6 +0,0 @@ -local _={} -_[1]={"return"} -return {_[1]} ---[[ -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/comment.ans b/test/tests/comment.ans deleted file mode 100644 index 0700c0e..0000000 --- a/test/tests/comment.ans +++ /dev/null @@ -1 +0,0 @@ -(hey couic + 5) diff --git a/test/tests/comment.lua b/test/tests/comment.lua deleted file mode 100644 index 142a8c2..0000000 --- a/test/tests/comment.lua +++ /dev/null @@ -1,6 +0,0 @@ -local _={} -_[1]={"return"} -return {_[1]} ---[[ -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/commit.ans b/test/tests/commit.ans deleted file mode 100644 index 919fbb3..0000000 --- a/test/tests/commit.ans +++ /dev/null @@ -1,20 +0,0 @@ -:$ bar - :var=5 - - ~ var := 2 - - before: {var} - - ~ run("parallel") - - :! foo - checkpoint - - after: {var} - - ~ run("parallel") - -:$ parallel - parallel: {bar.var} - -~ bar diff --git a/test/tests/commit.lua b/test/tests/commit.lua deleted file mode 100644 index 3311efc..0000000 --- a/test/tests/commit.lua +++ /dev/null @@ -1,54 +0,0 @@ -local _={} -_[21]={} -_[20]={} -_[19]={} -_[18]={} -_[17]={tags=_[21],text="2"} -_[16]={tags=_[21],text="parallel: "} -_[15]={tags=_[20],text="2"} -_[14]={tags=_[20],text="after: "} -_[13]={tags=_[19],text="5"} -_[12]={tags=_[19],text="parallel: "} -_[11]={tags=_[18],text="2"} -_[10]={tags=_[18],text="before: "} -_[9]={_[16],_[17]} -_[8]={_[14],_[15]} -_[7]={_[12],_[13]} -_[6]={_[10],_[11]} -_[5]={"return"} -_[4]={"text",_[9]} -_[3]={"text",_[8]} -_[2]={"text",_[7]} -_[1]={"text",_[6]} -return {_[1],_[2],_[3],_[4],_[5]} ---[[ -{ "text", { { - tags = <1>{}, - text = "before: " - }, { - tags =
, - text = "2" - } } } -{ "text", { { - tags = <1>{}, - text = "parallel: " - }, { - tags =
, - text = "5" - } } } -{ "text", { { - tags = <1>{}, - text = "after: " - }, { - tags =
, - text = "2" - } } } -{ "text", { { - tags = <1>{}, - text = "parallel: " - }, { - tags =
, - text = "2" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/condition decorator.ans b/test/tests/condition decorator.ans deleted file mode 100644 index f40e95b..0000000 --- a/test/tests/condition decorator.ans +++ /dev/null @@ -1,3 +0,0 @@ -ko ~ 0 -ok ~ 1 -ok bis ~ 1 \ No newline at end of file diff --git a/test/tests/condition decorator.lua b/test/tests/condition decorator.lua deleted file mode 100644 index 26ecb53..0000000 --- a/test/tests/condition decorator.lua +++ /dev/null @@ -1,19 +0,0 @@ -local _={} -_[7]={} -_[6]={} -_[5]={text="ok bis",tags=_[7]} -_[4]={text="ok ",tags=_[6]} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "ok " - }, { - tags = {}, - text = "ok bis" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/condition else false.ans b/test/tests/condition else false.ans deleted file mode 100644 index 1f68306..0000000 --- a/test/tests/condition else false.ans +++ /dev/null @@ -1,6 +0,0 @@ -:a = 5 - -~ a == 2 - ko -~~ - ok diff --git a/test/tests/condition else false.lua b/test/tests/condition else false.lua deleted file mode 100644 index efc9631..0000000 --- a/test/tests/condition else false.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="ok"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/condition else true.ans b/test/tests/condition else true.ans deleted file mode 100644 index 9c2b2ad..0000000 --- a/test/tests/condition else true.ans +++ /dev/null @@ -1,6 +0,0 @@ -:a = 5 - -~ a == 5 - ok -~~ - ko diff --git a/test/tests/condition else true.lua b/test/tests/condition else true.lua deleted file mode 100644 index efc9631..0000000 --- a/test/tests/condition else true.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="ok"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/condition elseif false.ans b/test/tests/condition elseif false.ans deleted file mode 100644 index f87d64e..0000000 --- a/test/tests/condition elseif false.ans +++ /dev/null @@ -1,8 +0,0 @@ -:a = 5 - -~ a == 2 - ko -~~ 0 - ko -~~ - ok diff --git a/test/tests/condition elseif false.lua b/test/tests/condition elseif false.lua deleted file mode 100644 index efc9631..0000000 --- a/test/tests/condition elseif false.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="ok"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/condition elseif true.ans b/test/tests/condition elseif true.ans deleted file mode 100644 index 709bd65..0000000 --- a/test/tests/condition elseif true.ans +++ /dev/null @@ -1,8 +0,0 @@ -:a = 5 - -~ a == 2 - ko -~~ 1 - ok -~~ - ko diff --git a/test/tests/condition elseif true.lua b/test/tests/condition elseif true.lua deleted file mode 100644 index efc9631..0000000 --- a/test/tests/condition elseif true.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="ok"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/condition false.ans b/test/tests/condition false.ans deleted file mode 100644 index 6794221..0000000 --- a/test/tests/condition false.ans +++ /dev/null @@ -1,4 +0,0 @@ -:a = 5 - -~ a == 2 - ko diff --git a/test/tests/condition false.lua b/test/tests/condition false.lua deleted file mode 100644 index 142a8c2..0000000 --- a/test/tests/condition false.lua +++ /dev/null @@ -1,6 +0,0 @@ -local _={} -_[1]={"return"} -return {_[1]} ---[[ -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/condition operator.ans b/test/tests/condition operator.ans deleted file mode 100644 index 7a980bb..0000000 --- a/test/tests/condition operator.ans +++ /dev/null @@ -1,6 +0,0 @@ -:$ f - b - -a {f ~ 5} c - -a {f ~ 0} c diff --git a/test/tests/condition operator.lua b/test/tests/condition operator.lua deleted file mode 100644 index 8a65413..0000000 --- a/test/tests/condition operator.lua +++ /dev/null @@ -1,35 +0,0 @@ -local _={} -_[13]={} -_[12]={} -_[11]={} -_[10]={tags=_[13],text="c"} -_[9]={tags=_[13],text="a "} -_[8]={tags=_[11],text=" c"} -_[7]={tags=_[12],text="b"} -_[6]={tags=_[11],text="a "} -_[5]={_[9],_[10]} -_[4]={_[6],_[7],_[8]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = <1>{}, - text = "a " - }, { - tags = {}, - text = "b" - }, { - tags =
, - text = " c" - } } } -{ "text", { { - tags = <1>{}, - text = "a " - }, { - tags =
, - text = "c" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/condition true.ans b/test/tests/condition true.ans deleted file mode 100644 index 07a9e4c..0000000 --- a/test/tests/condition true.ans +++ /dev/null @@ -1,4 +0,0 @@ -:a = 5 - -~ a == 5 - ok diff --git a/test/tests/condition true.lua b/test/tests/condition true.lua deleted file mode 100644 index efc9631..0000000 --- a/test/tests/condition true.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="ok"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/constant object attribute.ans b/test/tests/constant object attribute.ans deleted file mode 100644 index 3225098..0000000 --- a/test/tests/constant object attribute.ans +++ /dev/null @@ -1,8 +0,0 @@ -:% obj - ::a = 12 - -:x = obj() - -{x.a} - -{x.a := 52} diff --git a/test/tests/constant object attribute.lua b/test/tests/constant object attribute.lua deleted file mode 100644 index ce7f8b8..0000000 --- a/test/tests/constant object attribute.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="12"} -_[3]={_[4]} -_[2]={"error","can't change the value of a constant attribute; in Lua function \"_._\"; at test/tests/constant object attribute.ans:8"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "12" - } } } -{ "error", "can't change the value of a constant attribute; in Lua function \"_._\"; at test/tests/constant object attribute.ans:8" } -]]-- \ No newline at end of file diff --git a/test/tests/constant object.ans b/test/tests/constant object.ans deleted file mode 100644 index 4d212b1..0000000 --- a/test/tests/constant object.ans +++ /dev/null @@ -1,8 +0,0 @@ -:% obj - :a = 12 - -::x = obj() - -{x.a} - -{x.a := 52} diff --git a/test/tests/constant object.lua b/test/tests/constant object.lua deleted file mode 100644 index 0c4c1b3..0000000 --- a/test/tests/constant object.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="12"} -_[3]={_[4]} -_[2]={"error","can't change the value of an attribute of a constant object; in Lua function \"_._\"; at test/tests/constant object.ans:8"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "12" - } } } -{ "error", "can't change the value of an attribute of a constant object; in Lua function \"_._\"; at test/tests/constant object.ans:8" } -]]-- \ No newline at end of file diff --git a/test/tests/constant values variable.ans b/test/tests/constant values variable.ans deleted file mode 100644 index d8a5f84..0000000 --- a/test/tests/constant values variable.ans +++ /dev/null @@ -1,15 +0,0 @@ -:l = [1,2,3] - -::x = l - -{l} -{x} - ------ - -~ l!remove() - -{l} -{x} - -~ x!remove() diff --git a/test/tests/constant values variable.lua b/test/tests/constant values variable.lua deleted file mode 100644 index 8b388cf..0000000 --- a/test/tests/constant values variable.lua +++ /dev/null @@ -1,40 +0,0 @@ -local _={} -_[17]={} -_[16]={} -_[15]={} -_[14]={} -_[13]={} -_[12]={text="[1, 2, 3]",tags=_[17]} -_[11]={text="[1, 2]",tags=_[16]} -_[10]={text="-----",tags=_[15]} -_[9]={text="[1, 2, 3]",tags=_[14]} -_[8]={text="[1, 2, 3]",tags=_[13]} -_[7]={_[11],_[12]} -_[6]={_[10]} -_[5]={_[8],_[9]} -_[4]={"error","can't remove values from a constant list; in Lua function \"remove\"; at test/tests/constant values variable.ans:15"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "[1, 2, 3]" - }, { - tags = {}, - text = "[1, 2, 3]" - } } } -{ "text", { { - tags = {}, - text = "-----" - } } } -{ "text", { { - tags = {}, - text = "[1, 2]" - }, { - tags = {}, - text = "[1, 2, 3]" - } } } -{ "error", "can't remove values from a constant list; in Lua function \"remove\"; at test/tests/constant values variable.ans:15" } -]]-- \ No newline at end of file diff --git a/test/tests/constant values.ans b/test/tests/constant values.ans deleted file mode 100644 index 662999d..0000000 --- a/test/tests/constant values.ans +++ /dev/null @@ -1,15 +0,0 @@ -:l = [1,2,3] - -:x = constant(l) - -{l} -{x} - ------ - -~ l!remove() - -{l} -{x} - -~ x!remove() diff --git a/test/tests/constant values.lua b/test/tests/constant values.lua deleted file mode 100644 index b950bb2..0000000 --- a/test/tests/constant values.lua +++ /dev/null @@ -1,40 +0,0 @@ -local _={} -_[17]={} -_[16]={} -_[15]={} -_[14]={} -_[13]={} -_[12]={text="[1, 2, 3]",tags=_[17]} -_[11]={text="[1, 2]",tags=_[16]} -_[10]={text="-----",tags=_[15]} -_[9]={text="[1, 2, 3]",tags=_[14]} -_[8]={text="[1, 2, 3]",tags=_[13]} -_[7]={_[11],_[12]} -_[6]={_[10]} -_[5]={_[8],_[9]} -_[4]={"error","can't remove values from a constant list; in Lua function \"remove\"; at test/tests/constant values.ans:15"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "[1, 2, 3]" - }, { - tags = {}, - text = "[1, 2, 3]" - } } } -{ "text", { { - tags = {}, - text = "-----" - } } } -{ "text", { { - tags = {}, - text = "[1, 2]" - }, { - tags = {}, - text = "[1, 2, 3]" - } } } -{ "error", "can't remove values from a constant list; in Lua function \"remove\"; at test/tests/constant values.ans:15" } -]]-- \ No newline at end of file diff --git a/test/tests/constant variable list.ans b/test/tests/constant variable list.ans deleted file mode 100644 index 1233ba1..0000000 --- a/test/tests/constant variable list.ans +++ /dev/null @@ -1,7 +0,0 @@ -::a = [3] - -{a} - -~ a!insert(52) - -{a} diff --git a/test/tests/constant variable list.lua b/test/tests/constant variable list.lua deleted file mode 100644 index e86534d..0000000 --- a/test/tests/constant variable list.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="[3]"} -_[3]={_[4]} -_[2]={"error","can't insert values into a constant list; in Lua function \"insert\"; at test/tests/constant variable list.ans:5"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "[3]" - } } } -{ "error", "can't insert values into a constant list; in Lua function \"insert\"; at test/tests/constant variable list.ans:5" } -]]-- \ No newline at end of file diff --git a/test/tests/constant variable.ans b/test/tests/constant variable.ans deleted file mode 100644 index 2b8c8d6..0000000 --- a/test/tests/constant variable.ans +++ /dev/null @@ -1,7 +0,0 @@ -::a = 3 - -{a} - -~ a := 52 - -{a} diff --git a/test/tests/constant variable.lua b/test/tests/constant variable.lua deleted file mode 100644 index 887f62c..0000000 --- a/test/tests/constant variable.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="3"} -_[3]={_[4]} -_[2]={"error","can't change the value of a constant \"constant variable.a\"; while assigning value to variable \"constant variable.a\"; at test/tests/constant variable.ans:5"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "3" - } } } -{ "error", "can't change the value of a constant \"constant variable.a\"; while assigning value to variable \"constant variable.a\"; at test/tests/constant variable.ans:5" } -]]-- \ No newline at end of file diff --git a/test/tests/constrained variable assignement.ans b/test/tests/constrained variable assignement.ans deleted file mode 100644 index f5f6184..0000000 --- a/test/tests/constrained variable assignement.ans +++ /dev/null @@ -1,11 +0,0 @@ -:weigh::"kg" = 5::"kg" - -{weigh} - -~ weigh := 12::"kg" - -{weigh} - -~ weigh := 32 - -{weigh} diff --git a/test/tests/constrained variable assignement.lua b/test/tests/constrained variable assignement.lua deleted file mode 100644 index 80fd71f..0000000 --- a/test/tests/constrained variable assignement.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[9]={} -_[8]={} -_[7]={text="12::kg",tags=_[9]} -_[6]={text="5::kg",tags=_[8]} -_[5]={_[7]} -_[4]={_[6]} -_[3]={"error","constraint check failed; while assigning value to variable \"constrained variable assignement.weigh\"; at test/tests/constrained variable assignement.ans:9"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "5::kg" - } } } -{ "text", { { - tags = {}, - text = "12::kg" - } } } -{ "error", 'constraint check failed; while assigning value to variable "constrained variable assignement.weigh"; at test/tests/constrained variable assignement.ans:9' } -]]-- \ No newline at end of file diff --git a/test/tests/constrained variable definition.ans b/test/tests/constrained variable definition.ans deleted file mode 100644 index bdc7607..0000000 --- a/test/tests/constrained variable definition.ans +++ /dev/null @@ -1,7 +0,0 @@ -:weigh::"kg" = 5::"kg" - -{weigh} - -:not weigh::"kg" = 12 - -{not weigh} diff --git a/test/tests/constrained variable definition.lua b/test/tests/constrained variable definition.lua deleted file mode 100644 index 032b58a..0000000 --- a/test/tests/constrained variable definition.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={text="5::kg",tags=_[5]} -_[3]={_[4]} -_[2]={"error","constraint check failed; while assigning value to variable \"constrained variable definition.not weigh\"; at test/tests/constrained variable definition.ans:7"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "5::kg" - } } } -{ "error", 'constraint check failed; while assigning value to variable "constrained variable definition.not weigh"; at test/tests/constrained variable definition.ans:7' } -]]-- \ No newline at end of file diff --git a/test/tests/custom event.ans b/test/tests/custom event.ans deleted file mode 100644 index e3fbd8d..0000000 --- a/test/tests/custom event.ans +++ /dev/null @@ -1,3 +0,0 @@ -ah -~ wait(5) -ho diff --git a/test/tests/custom event.lua b/test/tests/custom event.lua deleted file mode 100644 index 2ca82b5..0000000 --- a/test/tests/custom event.lua +++ /dev/null @@ -1,21 +0,0 @@ -local _={} -_[8]={} -_[7]={} -_[6]={tags=_[8],text="ho"} -_[5]={tags=_[7],text="ah"} -_[4]={_[5],_[6]} -_[3]={"return"} -_[2]={"text",_[4]} -_[1]={"wait",5} -return {_[1],_[2],_[3]} ---[[ -{ "wait", 5 } -{ "text", { { - tags = {}, - text = "ah" - }, { - tags = {}, - text = "ho" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/custom text formatting.ans b/test/tests/custom text formatting.ans deleted file mode 100644 index 9059ab0..0000000 --- a/test/tests/custom text formatting.ans +++ /dev/null @@ -1,11 +0,0 @@ -:person = "personne" - -:$ Person(name, age) - @{name=name, age=age}::person - -:abject = Person("Darmanin", 38) - -{abject} - -:$ {}(p::person) - @"Name: {p("name")}\nAge: {p("age")}" diff --git a/test/tests/custom text formatting.lua b/test/tests/custom text formatting.lua deleted file mode 100644 index 7a5be31..0000000 --- a/test/tests/custom text formatting.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="Name: Darmanin\nAge: 38"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "Name: Darmanin\nAge: 38" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/define override function.ans b/test/tests/define override function.ans deleted file mode 100644 index 79efc81..0000000 --- a/test/tests/define override function.ans +++ /dev/null @@ -1,3 +0,0 @@ -:$ a - -:a = 2 diff --git a/test/tests/define override function.lua b/test/tests/define override function.lua deleted file mode 100644 index 7f7e3da..0000000 --- a/test/tests/define override function.lua +++ /dev/null @@ -1,6 +0,0 @@ -local _={} -_[1]={"error","trying to define variable \"define override function.a\", but a function with the same name exists; at test/tests/define override function.ans:3"} -return {_[1]} ---[[ -{ "error", 'trying to define variable "define override function.a", but a function with the same name exists; at test/tests/define override function.ans:3' } -]]-- \ No newline at end of file diff --git a/test/tests/define override variable.ans b/test/tests/define override variable.ans deleted file mode 100644 index 0e23022..0000000 --- a/test/tests/define override variable.ans +++ /dev/null @@ -1,3 +0,0 @@ -:a = 2 - -:$ a diff --git a/test/tests/define override variable.lua b/test/tests/define override variable.lua deleted file mode 100644 index 3aa0713..0000000 --- a/test/tests/define override variable.lua +++ /dev/null @@ -1,6 +0,0 @@ -local _={} -_[1]={"error","trying to define function define override variable.a, but a variable with the same name exists; at test/tests/define override variable.ans:3"} -return {_[1]} ---[[ -{ "error", "trying to define function define override variable.a, but a variable with the same name exists; at test/tests/define override variable.ans:3" } -]]-- \ No newline at end of file diff --git a/test/tests/define override.ans b/test/tests/define override.ans deleted file mode 100644 index 1fdefc8..0000000 --- a/test/tests/define override.ans +++ /dev/null @@ -1,5 +0,0 @@ -:a = 5 - -:a = 2 - -a: {a} diff --git a/test/tests/define override.lua b/test/tests/define override.lua deleted file mode 100644 index 0a6ae31..0000000 --- a/test/tests/define override.lua +++ /dev/null @@ -1,6 +0,0 @@ -local _={} -_[1]={"error","trying to define variable \"define override.a\" but it is already defined at test/tests/define override.ans:1; at test/tests/define override.ans:3"} -return {_[1]} ---[[ -{ "error", 'trying to define variable "define override.a" but it is already defined at test/tests/define override.ans:1; at test/tests/define override.ans:3' } -]]-- \ No newline at end of file diff --git a/test/tests/define.ans b/test/tests/define.ans deleted file mode 100644 index db1999d..0000000 --- a/test/tests/define.ans +++ /dev/null @@ -1 +0,0 @@ -:a = 5 diff --git a/test/tests/define.lua b/test/tests/define.lua deleted file mode 100644 index 142a8c2..0000000 --- a/test/tests/define.lua +++ /dev/null @@ -1,6 +0,0 @@ -local _={} -_[1]={"return"} -return {_[1]} ---[[ -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/equality operator.ans b/test/tests/equality operator.ans deleted file mode 100644 index 30f746a..0000000 --- a/test/tests/equality operator.ans +++ /dev/null @@ -1,29 +0,0 @@ -::a = [1=2] - -0 = {a == [5=2]!constant} - -0 = {a == [1=3]!constant} - -1 = {a == [1=2]!constant} - -::b = [1,2,3] - -0 = {b == a} - -0 = {b == []!constant} - -0 = {b == [3,1,2]!constant} - -0 = {b == [1,2,3,4]!constant} - -1 = {b == [1,2,3]!constant} - -:c = [1,2,3] - -0 = {c == b} - -1 = {c!constant == b} - -::d = [1,2,3] - -1 = {d == b} diff --git a/test/tests/equality operator.lua b/test/tests/equality operator.lua deleted file mode 100644 index 17f5f10..0000000 --- a/test/tests/equality operator.lua +++ /dev/null @@ -1,149 +0,0 @@ -local _={} -_[67]={} -_[66]={} -_[65]={} -_[64]={} -_[63]={} -_[62]={} -_[61]={} -_[60]={} -_[59]={} -_[58]={} -_[57]={} -_[56]={} -_[55]={} -_[54]={} -_[53]={} -_[52]={} -_[51]={} -_[50]={} -_[49]={} -_[48]={} -_[47]={} -_[46]={} -_[45]={tags=_[67],text="1"} -_[44]={tags=_[66],text="1 = "} -_[43]={tags=_[65],text="1"} -_[42]={tags=_[64],text="1 = "} -_[41]={tags=_[63],text="0"} -_[40]={tags=_[62],text="0 = "} -_[39]={tags=_[61],text="1"} -_[38]={tags=_[60],text="1 = "} -_[37]={tags=_[59],text="0"} -_[36]={tags=_[58],text="0 = "} -_[35]={tags=_[57],text="0"} -_[34]={tags=_[56],text="0 = "} -_[33]={tags=_[55],text="0"} -_[32]={tags=_[54],text="0 = "} -_[31]={tags=_[53],text="0"} -_[30]={tags=_[52],text="0 = "} -_[29]={tags=_[51],text="1"} -_[28]={tags=_[50],text="1 = "} -_[27]={tags=_[49],text="0"} -_[26]={tags=_[48],text="0 = "} -_[25]={tags=_[47],text="0"} -_[24]={tags=_[46],text="0 = "} -_[23]={_[44],_[45]} -_[22]={_[42],_[43]} -_[21]={_[40],_[41]} -_[20]={_[38],_[39]} -_[19]={_[36],_[37]} -_[18]={_[34],_[35]} -_[17]={_[32],_[33]} -_[16]={_[30],_[31]} -_[15]={_[28],_[29]} -_[14]={_[26],_[27]} -_[13]={_[24],_[25]} -_[12]={"return"} -_[11]={"text",_[23]} -_[10]={"text",_[22]} -_[9]={"text",_[21]} -_[8]={"text",_[20]} -_[7]={"text",_[19]} -_[6]={"text",_[18]} -_[5]={"text",_[17]} -_[4]={"text",_[16]} -_[3]={"text",_[15]} -_[2]={"text",_[14]} -_[1]={"text",_[13]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12]} ---[[ -{ "text", { { - tags = {}, - text = "0 = " - }, { - tags = {}, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "0 = " - }, { - tags = {}, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "1 = " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "0 = " - }, { - tags = {}, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "0 = " - }, { - tags = {}, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "0 = " - }, { - tags = {}, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "0 = " - }, { - tags = {}, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "1 = " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "0 = " - }, { - tags = {}, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "1 = " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "1 = " - }, { - tags = {}, - text = "1" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/flush.ans b/test/tests/flush.ans deleted file mode 100644 index a2c72c9..0000000 --- a/test/tests/flush.ans +++ /dev/null @@ -1,8 +0,0 @@ -a - -> b -~ choose(1) - -c -> d -~ choose(1) diff --git a/test/tests/flush.lua b/test/tests/flush.lua deleted file mode 100644 index 2f7da9f..0000000 --- a/test/tests/flush.lua +++ /dev/null @@ -1,40 +0,0 @@ -local _={} -_[19]={} -_[18]={} -_[17]={tags=_[19],text="d"} -_[16]={} -_[15]={tags=_[18],text="b"} -_[14]={} -_[13]={_[17]} -_[12]={tags=_[16],text="c"} -_[11]={_[15]} -_[10]={tags=_[14],text="a"} -_[9]={_[13]} -_[8]={_[12]} -_[7]={_[11]} -_[6]={_[10]} -_[5]={"return"} -_[4]={"choice",_[9]} -_[3]={"text",_[8]} -_[2]={"choice",_[7]} -_[1]={"text",_[6]} -return {_[1],_[2],_[3],_[4],_[5]} ---[[ -{ "text", { { - tags = {}, - text = "a" - } } } -{ "choice", { { { - tags = {}, - text = "b" - } } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "choice", { { { - tags = {}, - text = "d" - } } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function alias.ans b/test/tests/function alias.ans deleted file mode 100644 index ab5fa6c..0000000 --- a/test/tests/function alias.ans +++ /dev/null @@ -1,9 +0,0 @@ -:$ f : test - @"ok" - -{f} = {test} - -:$ g : bis(a) - @a - -{g("ye")} = {bis("ye")} diff --git a/test/tests/function alias.lua b/test/tests/function alias.lua deleted file mode 100644 index aca155f..0000000 --- a/test/tests/function alias.lua +++ /dev/null @@ -1,38 +0,0 @@ -local _={} -_[13]={} -_[12]={} -_[11]={tags=_[13],text="ye"} -_[10]={tags=_[13],text=" = "} -_[9]={tags=_[13],text="ye"} -_[8]={tags=_[12],text="ok"} -_[7]={tags=_[12],text=" = "} -_[6]={tags=_[12],text="ok"} -_[5]={_[9],_[10],_[11]} -_[4]={_[6],_[7],_[8]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = <1>{}, - text = "ok" - }, { - tags =
, - text = " = " - }, { - tags =
, - text = "ok" - } } } -{ "text", { { - tags = <1>{}, - text = "ye" - }, { - tags =
, - text = " = " - }, { - tags =
, - text = "ye" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function arg vararg.ans b/test/tests/function arg vararg.ans deleted file mode 100644 index 72fd74b..0000000 --- a/test/tests/function arg vararg.ans +++ /dev/null @@ -1,5 +0,0 @@ -:$ f(a, l...) - {a} - {l} - -~ f("ok", "o", "k") diff --git a/test/tests/function arg vararg.lua b/test/tests/function arg vararg.lua deleted file mode 100644 index 6ccb0a5..0000000 --- a/test/tests/function arg vararg.lua +++ /dev/null @@ -1,19 +0,0 @@ -local _={} -_[7]={} -_[6]={} -_[5]={tags=_[7],text="[o, k]"} -_[4]={tags=_[6],text="ok"} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "ok" - }, { - tags = {}, - text = "[o, k]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function arg.ans b/test/tests/function arg.ans deleted file mode 100644 index c102c17..0000000 --- a/test/tests/function arg.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ f(a) - {a} - -~ f("ok") diff --git a/test/tests/function arg.lua b/test/tests/function arg.lua deleted file mode 100644 index efc9631..0000000 --- a/test/tests/function arg.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="ok"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function args arity check fail.ans b/test/tests/function args arity check fail.ans deleted file mode 100644 index c41ac2b..0000000 --- a/test/tests/function args arity check fail.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ f(a, b) - {a}{b} - -~ f("ok") diff --git a/test/tests/function args arity check fail.lua b/test/tests/function args arity check fail.lua deleted file mode 100644 index 5623842..0000000 --- a/test/tests/function args arity check fail.lua +++ /dev/null @@ -1,6 +0,0 @@ -local _={} -_[1]={"error","function \"function args arity check fail.f\" expected 2 arguments but received 1; at test/tests/function args arity check fail.ans:4"} -return {_[1]} ---[[ -{ "error", 'function "function args arity check fail.f" expected 2 arguments but received 1; at test/tests/function args arity check fail.ans:4' } -]]-- \ No newline at end of file diff --git a/test/tests/function args vararg empty.ans b/test/tests/function args vararg empty.ans deleted file mode 100644 index ecdc920..0000000 --- a/test/tests/function args vararg empty.ans +++ /dev/null @@ -1,5 +0,0 @@ -:$ f(a, b, l...) - {a}{b} - {l} - -~ f("o","k") diff --git a/test/tests/function args vararg empty.lua b/test/tests/function args vararg empty.lua deleted file mode 100644 index 013ecc1..0000000 --- a/test/tests/function args vararg empty.lua +++ /dev/null @@ -1,23 +0,0 @@ -local _={} -_[8]={} -_[7]={} -_[6]={tags=_[8],text="[]"} -_[5]={tags=_[7],text="k"} -_[4]={tags=_[7],text="o"} -_[3]={_[4],_[5],_[6]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "o" - }, { - tags =
, - text = "k" - }, { - tags = {}, - text = "[]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function args vararg.ans b/test/tests/function args vararg.ans deleted file mode 100644 index ae02ba7..0000000 --- a/test/tests/function args vararg.ans +++ /dev/null @@ -1,5 +0,0 @@ -:$ f(a, b, l...) - {a}{b} - {l} - -~ f("o", "k", "o", "k") diff --git a/test/tests/function args vararg.lua b/test/tests/function args vararg.lua deleted file mode 100644 index 4c56d4e..0000000 --- a/test/tests/function args vararg.lua +++ /dev/null @@ -1,23 +0,0 @@ -local _={} -_[8]={} -_[7]={} -_[6]={tags=_[8],text="[o, k]"} -_[5]={tags=_[7],text="k"} -_[4]={tags=_[7],text="o"} -_[3]={_[4],_[5],_[6]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "o" - }, { - tags =
, - text = "k" - }, { - tags = {}, - text = "[o, k]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function args.ans b/test/tests/function args.ans deleted file mode 100644 index 736cf22..0000000 --- a/test/tests/function args.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ f(a, b) - {a}{b} - -~ f("o", "k") diff --git a/test/tests/function args.lua b/test/tests/function args.lua deleted file mode 100644 index 5c81938..0000000 --- a/test/tests/function args.lua +++ /dev/null @@ -1,18 +0,0 @@ -local _={} -_[6]={} -_[5]={tags=_[6],text="k"} -_[4]={tags=_[6],text="o"} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "o" - }, { - tags =
, - text = "k" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function assignement.ans b/test/tests/function assignement.ans deleted file mode 100644 index 3efecc4..0000000 --- a/test/tests/function assignement.ans +++ /dev/null @@ -1,22 +0,0 @@ -:a = 5 - -:$ f(p) - @a - -:$ f(p) := v - v={v} - ~ a := v - -:$ f(p) := v::string - v2={v} - ~ a := p - -{f(a)} - -~ f(3) := 50 - -{f(a)} - -~ f(3) := "ok" - -{f(a)} diff --git a/test/tests/function assignement.lua b/test/tests/function assignement.lua deleted file mode 100644 index 190c344..0000000 --- a/test/tests/function assignement.lua +++ /dev/null @@ -1,54 +0,0 @@ -local _={} -_[23]={} -_[22]={} -_[21]={} -_[20]={} -_[19]={} -_[18]={tags=_[23],text="3"} -_[17]={tags=_[22],text="ok"} -_[16]={tags=_[22],text="v2="} -_[15]={tags=_[21],text="50"} -_[14]={tags=_[20],text="50"} -_[13]={tags=_[20],text="v="} -_[12]={tags=_[19],text="5"} -_[11]={_[18]} -_[10]={_[16],_[17]} -_[9]={_[15]} -_[8]={_[13],_[14]} -_[7]={_[12]} -_[6]={"return"} -_[5]={"text",_[11]} -_[4]={"text",_[10]} -_[3]={"text",_[9]} -_[2]={"text",_[8]} -_[1]={"text",_[7]} -return {_[1],_[2],_[3],_[4],_[5],_[6]} ---[[ -{ "text", { { - tags = {}, - text = "5" - } } } -{ "text", { { - tags = <1>{}, - text = "v=" - }, { - tags =
, - text = "50" - } } } -{ "text", { { - tags = {}, - text = "50" - } } } -{ "text", { { - tags = <1>{}, - text = "v2=" - }, { - tags =
, - text = "ok" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function conflict.ans b/test/tests/function conflict.ans deleted file mode 100644 index bd9f873..0000000 --- a/test/tests/function conflict.ans +++ /dev/null @@ -1,5 +0,0 @@ -:$ f(a, b) - -:$ f(x) - -:$ f(a, b) diff --git a/test/tests/function conflict.lua b/test/tests/function conflict.lua deleted file mode 100644 index 27482ad..0000000 --- a/test/tests/function conflict.lua +++ /dev/null @@ -1,6 +0,0 @@ -local _={} -_[1]={"error","trying to define function function conflict.f(a, b) (at test/tests/function conflict.ans:5), but another function with same signature function conflict.f(a, b) (at test/tests/function conflict.ans:1) exists; at test/tests/function conflict.ans:5"} -return {_[1]} ---[[ -{ "error", "trying to define function function conflict.f(a, b) (at test/tests/function conflict.ans:5), but another function with same signature function conflict.f(a, b) (at test/tests/function conflict.ans:1) exists; at test/tests/function conflict.ans:5" } -]]-- \ No newline at end of file diff --git a/test/tests/function custom type dispatch error.ans b/test/tests/function custom type dispatch error.ans deleted file mode 100644 index c0b3a6f..0000000 --- a/test/tests/function custom type dispatch error.ans +++ /dev/null @@ -1,17 +0,0 @@ -:name="name"::string -:french name="french"::name -:esperanto name="esperanto"::name - -:$ a(name::string) - {name} is english or generic - -:$ a(name:nom::french name) - {nom} is french - -~ a("bob"::name) - -~ a("pierre"::french name) - -~ a("idk"::esperanto name) - -~ a(5) diff --git a/test/tests/function custom type dispatch error.lua b/test/tests/function custom type dispatch error.lua deleted file mode 100644 index 3e0853f..0000000 --- a/test/tests/function custom type dispatch error.lua +++ /dev/null @@ -1,42 +0,0 @@ -local _={} -_[16]={} -_[15]={} -_[14]={} -_[13]={tags=_[16],text=" is english or generic"} -_[12]={tags=_[16],text="idk::esperanto::name::string"} -_[11]={tags=_[15],text=" is french"} -_[10]={tags=_[15],text="pierre::french::name::string"} -_[9]={tags=_[14],text=" is english or generic"} -_[8]={tags=_[14],text="bob::name::string"} -_[7]={_[12],_[13]} -_[6]={_[10],_[11]} -_[5]={_[8],_[9]} -_[4]={"error","no compatible function found for call to a(number); potential candidates were:\n\9function custom type dispatch error.a(name::string) (at test/tests/function custom type dispatch error.ans:5): argument name is not of expected type string\n\9function custom type dispatch error.a(name:nom::french name) (at test/tests/function custom type dispatch error.ans:8): argument name is not of expected type french::name::string; at test/tests/function custom type dispatch error.ans:17"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = <1>{}, - text = "bob::name::string" - }, { - tags =
, - text = " is english or generic" - } } } -{ "text", { { - tags = <1>{}, - text = "pierre::french::name::string" - }, { - tags =
, - text = " is french" - } } } -{ "text", { { - tags = <1>{}, - text = "idk::esperanto::name::string" - }, { - tags =
, - text = " is english or generic" - } } } -{ "error", "no compatible function found for call to a(number); potential candidates were:\n\tfunction custom type dispatch error.a(name::string) (at test/tests/function custom type dispatch error.ans:5): argument name is not of expected type string\n\tfunction custom type dispatch error.a(name:nom::french name) (at test/tests/function custom type dispatch error.ans:8): argument name is not of expected type french::name::string; at test/tests/function custom type dispatch error.ans:17" } -]]-- \ No newline at end of file diff --git a/test/tests/function custom type dispatch.ans b/test/tests/function custom type dispatch.ans deleted file mode 100644 index c627143..0000000 --- a/test/tests/function custom type dispatch.ans +++ /dev/null @@ -1,15 +0,0 @@ -:name="name"::string -:french name="french"::name -:esperanto name="esperanto"::name - -:$ a(name) - {name} is english or generic - -:$ a(name:nom::french name) - {nom} is french - -~ a("bob"::name) - -~ a("pierre"::french name) - -~ a("idk"::esperanto name) diff --git a/test/tests/function custom type dispatch.lua b/test/tests/function custom type dispatch.lua deleted file mode 100644 index 30ddd52..0000000 --- a/test/tests/function custom type dispatch.lua +++ /dev/null @@ -1,42 +0,0 @@ -local _={} -_[16]={} -_[15]={} -_[14]={} -_[13]={tags=_[16],text=" is english or generic"} -_[12]={tags=_[16],text="idk::esperanto::name::string"} -_[11]={tags=_[15],text=" is french"} -_[10]={tags=_[15],text="pierre::french::name::string"} -_[9]={tags=_[14],text=" is english or generic"} -_[8]={tags=_[14],text="bob::name::string"} -_[7]={_[12],_[13]} -_[6]={_[10],_[11]} -_[5]={_[8],_[9]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = <1>{}, - text = "bob::name::string" - }, { - tags =
, - text = " is english or generic" - } } } -{ "text", { { - tags = <1>{}, - text = "pierre::french::name::string" - }, { - tags =
, - text = " is french" - } } } -{ "text", { { - tags = <1>{}, - text = "idk::esperanto::name::string" - }, { - tags =
, - text = " is english or generic" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function cycle.ans b/test/tests/function cycle.ans deleted file mode 100644 index 5aae6f2..0000000 --- a/test/tests/function cycle.ans +++ /dev/null @@ -1,18 +0,0 @@ -:$ f - :$ a - a - :$ b - b - :$ c - c - ~ cycle(&a,&b,&c) - -~ f - -~ f - -~ f - -~ f - -~ f diff --git a/test/tests/function cycle.lua b/test/tests/function cycle.lua deleted file mode 100644 index dd54c30..0000000 --- a/test/tests/function cycle.lua +++ /dev/null @@ -1,46 +0,0 @@ -local _={} -_[21]={} -_[20]={} -_[19]={} -_[18]={} -_[17]={} -_[16]={tags=_[21],text="b"} -_[15]={tags=_[20],text="a"} -_[14]={tags=_[19],text="c"} -_[13]={tags=_[18],text="b"} -_[12]={tags=_[17],text="a"} -_[11]={_[16]} -_[10]={_[15]} -_[9]={_[14]} -_[8]={_[13]} -_[7]={_[12]} -_[6]={"return"} -_[5]={"text",_[11]} -_[4]={"text",_[10]} -_[3]={"text",_[9]} -_[2]={"text",_[8]} -_[1]={"text",_[7]} -return {_[1],_[2],_[3],_[4],_[5],_[6]} ---[[ -{ "text", { { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "b" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function name dispatch.ans b/test/tests/function name dispatch.ans deleted file mode 100644 index 643b56b..0000000 --- a/test/tests/function name dispatch.ans +++ /dev/null @@ -1,9 +0,0 @@ -:$ fn(x) - x - -:$ fn(a) - a - -~ fn(a=5) - -~ fn(x=5) diff --git a/test/tests/function name dispatch.lua b/test/tests/function name dispatch.lua deleted file mode 100644 index df94781..0000000 --- a/test/tests/function name dispatch.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[9]={} -_[8]={} -_[7]={tags=_[9],text="x"} -_[6]={tags=_[8],text="a"} -_[5]={_[7]} -_[4]={_[6]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "x" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function next.ans b/test/tests/function next.ans deleted file mode 100644 index c4eb9c3..0000000 --- a/test/tests/function next.ans +++ /dev/null @@ -1,18 +0,0 @@ -:$ f - :$ a - a - :$ b - b - :$ c - c - ~ next(&a, &b, &c) - -~ f - -~ f - -~ f - -~ f - -~ f \ No newline at end of file diff --git a/test/tests/function next.lua b/test/tests/function next.lua deleted file mode 100644 index 9d13f0f..0000000 --- a/test/tests/function next.lua +++ /dev/null @@ -1,46 +0,0 @@ -local _={} -_[21]={} -_[20]={} -_[19]={} -_[18]={} -_[17]={} -_[16]={tags=_[21],text="c"} -_[15]={tags=_[20],text="c"} -_[14]={tags=_[19],text="c"} -_[13]={tags=_[18],text="b"} -_[12]={tags=_[17],text="a"} -_[11]={_[16]} -_[10]={_[15]} -_[9]={_[14]} -_[8]={_[13]} -_[7]={_[12]} -_[6]={"return"} -_[5]={"text",_[11]} -_[4]={"text",_[10]} -_[3]={"text",_[9]} -_[2]={"text",_[8]} -_[1]={"text",_[7]} -return {_[1],_[2],_[3],_[4],_[5],_[6]} ---[[ -{ "text", { { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function no conflict.ans b/test/tests/function no conflict.ans deleted file mode 100644 index ea799ab..0000000 --- a/test/tests/function no conflict.ans +++ /dev/null @@ -1,5 +0,0 @@ -:$ f(a, b) - -:$ f(x) - -:$ f(u, v) diff --git a/test/tests/function no conflict.lua b/test/tests/function no conflict.lua deleted file mode 100644 index 142a8c2..0000000 --- a/test/tests/function no conflict.lua +++ /dev/null @@ -1,6 +0,0 @@ -local _={} -_[1]={"return"} -return {_[1]} ---[[ -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function random.ans b/test/tests/function random.ans deleted file mode 100644 index a788abf..0000000 --- a/test/tests/function random.ans +++ /dev/null @@ -1,18 +0,0 @@ -:$ f - :$ a - a - :$ b - b - :$ c - c - ~ random(&a,&b,&c) - -~ f - -~ f - -~ f - -~ f - -~ f diff --git a/test/tests/function random.lua b/test/tests/function random.lua deleted file mode 100644 index cce621d..0000000 --- a/test/tests/function random.lua +++ /dev/null @@ -1,46 +0,0 @@ -local _={} -_[21]={} -_[20]={} -_[19]={} -_[18]={} -_[17]={} -_[16]={tags=_[21],text="a"} -_[15]={tags=_[20],text="c"} -_[14]={tags=_[19],text="b"} -_[13]={tags=_[18],text="a"} -_[12]={tags=_[17],text="c"} -_[11]={_[16]} -_[10]={_[15]} -_[9]={_[14]} -_[8]={_[13]} -_[7]={_[12]} -_[6]={"return"} -_[5]={"text",_[11]} -_[4]={"text",_[10]} -_[3]={"text",_[9]} -_[2]={"text",_[8]} -_[1]={"text",_[7]} -return {_[1],_[2],_[3],_[4],_[5],_[6]} ---[[ -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "a" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function reference call explicit call.ans b/test/tests/function reference call explicit call.ans deleted file mode 100644 index 6ee1ce4..0000000 --- a/test/tests/function reference call explicit call.ans +++ /dev/null @@ -1,12 +0,0 @@ -:$ fn - 1 - :! c - 2 - -:c = &fn - -~ c! - -~ c() - -~ c! diff --git a/test/tests/function reference call explicit call.lua b/test/tests/function reference call explicit call.lua deleted file mode 100644 index 399cf6e..0000000 --- a/test/tests/function reference call explicit call.lua +++ /dev/null @@ -1,40 +0,0 @@ -local _={} -_[17]={} -_[16]={} -_[15]={} -_[14]={} -_[13]={} -_[12]={text="2",tags=_[17]} -_[11]={text="2",tags=_[16]} -_[10]={text="1",tags=_[15]} -_[9]={text="2",tags=_[14]} -_[8]={text="1",tags=_[13]} -_[7]={_[12]} -_[6]={_[10],_[11]} -_[5]={_[8],_[9]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "1" - }, { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "1" - }, { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "2" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function reference call.ans b/test/tests/function reference call.ans deleted file mode 100644 index a7d108d..0000000 --- a/test/tests/function reference call.ans +++ /dev/null @@ -1,27 +0,0 @@ -:$ f(x) - @x+2 - -:$ g(x) - @x+3 - -:a = &f - -:b = &g - -3: {f(1)} - -3: {a(1)} - -5: {g(2)} - -4: {a(2)} - -4: {b(1)} - -5: {f(3)} - -13: {b(10)} - -7: {a(5)} - -8: {g(5)} diff --git a/test/tests/function reference call.lua b/test/tests/function reference call.lua deleted file mode 100644 index 98f117f..0000000 --- a/test/tests/function reference call.lua +++ /dev/null @@ -1,123 +0,0 @@ -local _={} -_[55]={} -_[54]={} -_[53]={} -_[52]={} -_[51]={} -_[50]={} -_[49]={} -_[48]={} -_[47]={} -_[46]={} -_[45]={} -_[44]={} -_[43]={} -_[42]={} -_[41]={} -_[40]={} -_[39]={} -_[38]={} -_[37]={tags=_[55],text="8"} -_[36]={tags=_[54],text="8: "} -_[35]={tags=_[53],text="7"} -_[34]={tags=_[52],text="7: "} -_[33]={tags=_[51],text="13"} -_[32]={tags=_[50],text="13: "} -_[31]={tags=_[49],text="5"} -_[30]={tags=_[48],text="5: "} -_[29]={tags=_[47],text="4"} -_[28]={tags=_[46],text="4: "} -_[27]={tags=_[45],text="4"} -_[26]={tags=_[44],text="4: "} -_[25]={tags=_[43],text="5"} -_[24]={tags=_[42],text="5: "} -_[23]={tags=_[41],text="3"} -_[22]={tags=_[40],text="3: "} -_[21]={tags=_[39],text="3"} -_[20]={tags=_[38],text="3: "} -_[19]={_[36],_[37]} -_[18]={_[34],_[35]} -_[17]={_[32],_[33]} -_[16]={_[30],_[31]} -_[15]={_[28],_[29]} -_[14]={_[26],_[27]} -_[13]={_[24],_[25]} -_[12]={_[22],_[23]} -_[11]={_[20],_[21]} -_[10]={"return"} -_[9]={"text",_[19]} -_[8]={"text",_[18]} -_[7]={"text",_[17]} -_[6]={"text",_[16]} -_[5]={"text",_[15]} -_[4]={"text",_[14]} -_[3]={"text",_[13]} -_[2]={"text",_[12]} -_[1]={"text",_[11]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10]} ---[[ -{ "text", { { - tags = {}, - text = "3: " - }, { - tags = {}, - text = "3" - } } } -{ "text", { { - tags = {}, - text = "3: " - }, { - tags = {}, - text = "3" - } } } -{ "text", { { - tags = {}, - text = "5: " - }, { - tags = {}, - text = "5" - } } } -{ "text", { { - tags = {}, - text = "4: " - }, { - tags = {}, - text = "4" - } } } -{ "text", { { - tags = {}, - text = "4: " - }, { - tags = {}, - text = "4" - } } } -{ "text", { { - tags = {}, - text = "5: " - }, { - tags = {}, - text = "5" - } } } -{ "text", { { - tags = {}, - text = "13: " - }, { - tags = {}, - text = "13" - } } } -{ "text", { { - tags = {}, - text = "7: " - }, { - tags = {}, - text = "7" - } } } -{ "text", { { - tags = {}, - text = "8: " - }, { - tags = {}, - text = "8" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function reference chain call.ans b/test/tests/function reference chain call.ans deleted file mode 100644 index 0a1af4b..0000000 --- a/test/tests/function reference chain call.ans +++ /dev/null @@ -1,13 +0,0 @@ -:$ fn - @1 - -:$ add(x) - @x+2 - -:c = &fn - -{c!} - -{fn!add} - -{c!!add} diff --git a/test/tests/function reference chain call.lua b/test/tests/function reference chain call.lua deleted file mode 100644 index 79b4a0f..0000000 --- a/test/tests/function reference chain call.lua +++ /dev/null @@ -1,30 +0,0 @@ -local _={} -_[13]={} -_[12]={} -_[11]={} -_[10]={text="3",tags=_[13]} -_[9]={text="3",tags=_[12]} -_[8]={text="1",tags=_[11]} -_[7]={_[10]} -_[6]={_[9]} -_[5]={_[8]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function reference dot operator function.ans b/test/tests/function reference dot operator function.ans deleted file mode 100644 index 1bcda95..0000000 --- a/test/tests/function reference dot operator function.ans +++ /dev/null @@ -1,11 +0,0 @@ -:$ f - :$ a - @12 - -:x = &f - -{&x.a} - -{x.a} - -{x.a!} diff --git a/test/tests/function reference dot operator function.lua b/test/tests/function reference dot operator function.lua deleted file mode 100644 index d051424..0000000 --- a/test/tests/function reference dot operator function.lua +++ /dev/null @@ -1,30 +0,0 @@ -local _={} -_[13]={} -_[12]={} -_[11]={} -_[10]={tags=_[13],text="12"} -_[9]={tags=_[12],text="12"} -_[8]={tags=_[11],text="&function reference dot operator function.f.a"} -_[7]={_[10]} -_[6]={_[9]} -_[5]={_[8]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "&function reference dot operator function.f.a" - } } } -{ "text", { { - tags = {}, - text = "12" - } } } -{ "text", { { - tags = {}, - text = "12" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function reference dot operator.ans b/test/tests/function reference dot operator.ans deleted file mode 100644 index e4f040b..0000000 --- a/test/tests/function reference dot operator.ans +++ /dev/null @@ -1,10 +0,0 @@ -:$ f - :a = 12 - -:x = &f - -{x.a} - -~ x.a := 52 - -{x.a} diff --git a/test/tests/function reference dot operator.lua b/test/tests/function reference dot operator.lua deleted file mode 100644 index b442a53..0000000 --- a/test/tests/function reference dot operator.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[9]={} -_[8]={} -_[7]={tags=_[9],text="52"} -_[6]={tags=_[8],text="12"} -_[5]={_[7]} -_[4]={_[6]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "12" - } } } -{ "text", { { - tags = {}, - text = "52" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function return exit function nested.ans b/test/tests/function return exit function nested.ans deleted file mode 100644 index c0fae57..0000000 --- a/test/tests/function return exit function nested.ans +++ /dev/null @@ -1,9 +0,0 @@ -:$ hey - :! foo - @2 - @3 - @5 - u - -{hey} -{hey.foo} \ No newline at end of file diff --git a/test/tests/function return exit function nested.lua b/test/tests/function return exit function nested.lua deleted file mode 100644 index f07424a..0000000 --- a/test/tests/function return exit function nested.lua +++ /dev/null @@ -1,19 +0,0 @@ -local _={} -_[7]={} -_[6]={} -_[5]={tags=_[7],text="2"} -_[4]={tags=_[6],text="5"} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "5" - }, { - tags = {}, - text = "2" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function return exit function.ans b/test/tests/function return exit function.ans deleted file mode 100644 index c1da341..0000000 --- a/test/tests/function return exit function.ans +++ /dev/null @@ -1,6 +0,0 @@ -:$ hey - @5 - a - @2 - -{hey} \ No newline at end of file diff --git a/test/tests/function return exit function.lua b/test/tests/function return exit function.lua deleted file mode 100644 index 6296c24..0000000 --- a/test/tests/function return exit function.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="5"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "5" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function return nested.ans b/test/tests/function return nested.ans deleted file mode 100644 index 28ba74f..0000000 --- a/test/tests/function return nested.ans +++ /dev/null @@ -1,7 +0,0 @@ -:$ hey - :! foo - @2 - @5 - -{hey} -{hey.foo} \ No newline at end of file diff --git a/test/tests/function return nested.lua b/test/tests/function return nested.lua deleted file mode 100644 index f07424a..0000000 --- a/test/tests/function return nested.lua +++ /dev/null @@ -1,19 +0,0 @@ -local _={} -_[7]={} -_[6]={} -_[5]={tags=_[7],text="2"} -_[4]={tags=_[6],text="5"} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "5" - }, { - tags = {}, - text = "2" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function return.ans b/test/tests/function return.ans deleted file mode 100644 index 90619ef..0000000 --- a/test/tests/function return.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ hey - @5 - -{hey} \ No newline at end of file diff --git a/test/tests/function return.lua b/test/tests/function return.lua deleted file mode 100644 index 6296c24..0000000 --- a/test/tests/function return.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="5"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "5" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function scope wrong.ans b/test/tests/function scope wrong.ans deleted file mode 100644 index d3a4e28..0000000 --- a/test/tests/function scope wrong.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ a - :b = 5 - -a: {b} diff --git a/test/tests/function scope wrong.lua b/test/tests/function scope wrong.lua deleted file mode 100644 index cdbda48..0000000 --- a/test/tests/function scope wrong.lua +++ /dev/null @@ -1,6 +0,0 @@ -local _={} -_[1]={"error","can't find function or variable named \"b\" in namespace \"function scope wrong.\"; at test/tests/function scope wrong.ans:4"} -return {_[1]} ---[[ -{ "error", "can't find function or variable named \"b\" in namespace \"function scope wrong.\"; at test/tests/function scope wrong.ans:4" } -]]-- \ No newline at end of file diff --git a/test/tests/function scope.ans b/test/tests/function scope.ans deleted file mode 100644 index 7378c49..0000000 --- a/test/tests/function scope.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ a - :b = 5 - -a: {a.b} diff --git a/test/tests/function scope.lua b/test/tests/function scope.lua deleted file mode 100644 index 4055219..0000000 --- a/test/tests/function scope.lua +++ /dev/null @@ -1,18 +0,0 @@ -local _={} -_[6]={} -_[5]={tags=_[6],text="5"} -_[4]={tags=_[6],text="a: "} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "a: " - }, { - tags =
, - text = "5" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function scoped mutable.ans b/test/tests/function scoped mutable.ans deleted file mode 100644 index 219ba84..0000000 --- a/test/tests/function scoped mutable.ans +++ /dev/null @@ -1,40 +0,0 @@ -:n = 0 - -:$ f(c=1) - :a = [] - - start: {a} - - ~ a!insert(c) - - ~ n += 1 - - before recursion {c}: {a} - - ~ n < 5 - ~ f(c+1) - - after recursion {c}: {a} - -new list each time: - -~ f - -:$ g(c=1, a=[]) - start: {a} - - ~ a!insert(c) - - ~ n += 1 - - before recursion {c}: {a} - - ~ n < 5 - ~ g(c+1, a) - - after recursion {c}: {a} - -pass list: - -~ n := 0 -~ g diff --git a/test/tests/function scoped mutable.lua b/test/tests/function scoped mutable.lua deleted file mode 100644 index 8503a69..0000000 --- a/test/tests/function scoped mutable.lua +++ /dev/null @@ -1,566 +0,0 @@ -local _={} -_[249]={} -_[248]={} -_[247]={} -_[246]={} -_[245]={} -_[244]={} -_[243]={} -_[242]={} -_[241]={} -_[240]={} -_[239]={} -_[238]={} -_[237]={} -_[236]={} -_[235]={} -_[234]={} -_[233]={} -_[232]={} -_[231]={} -_[230]={} -_[229]={} -_[228]={} -_[227]={} -_[226]={} -_[225]={} -_[224]={} -_[223]={} -_[222]={} -_[221]={} -_[220]={} -_[219]={} -_[218]={} -_[217]={} -_[216]={} -_[215]={} -_[214]={} -_[213]={} -_[212]={} -_[211]={} -_[210]={} -_[209]={} -_[208]={} -_[207]={} -_[206]={} -_[205]={} -_[204]={} -_[203]={} -_[202]={} -_[201]={} -_[200]={} -_[199]={} -_[198]={} -_[197]={} -_[196]={} -_[195]={} -_[194]={} -_[193]={} -_[192]={} -_[191]={} -_[190]={} -_[189]={} -_[188]={} -_[187]={} -_[186]={} -_[185]={} -_[184]={} -_[183]={} -_[182]={} -_[181]={} -_[180]={} -_[179]={} -_[178]={} -_[177]={} -_[176]={} -_[175]={} -_[174]={} -_[173]={} -_[172]={} -_[171]={} -_[170]={} -_[169]={} -_[168]={} -_[167]={} -_[166]={} -_[165]={} -_[164]={} -_[163]={} -_[162]={} -_[161]={} -_[160]={} -_[159]={} -_[158]={} -_[157]={} -_[156]={} -_[155]={tags=_[249],text="[1, 2, 3, 4, 5]"} -_[154]={tags=_[248],text=": "} -_[153]={tags=_[247],text="1"} -_[152]={tags=_[246],text="after recursion "} -_[151]={tags=_[245],text="[1, 2, 3, 4, 5]"} -_[150]={tags=_[244],text=": "} -_[149]={tags=_[243],text="2"} -_[148]={tags=_[242],text="after recursion "} -_[147]={tags=_[241],text="[1, 2, 3, 4, 5]"} -_[146]={tags=_[240],text=": "} -_[145]={tags=_[239],text="3"} -_[144]={tags=_[238],text="after recursion "} -_[143]={tags=_[237],text="[1, 2, 3, 4, 5]"} -_[142]={tags=_[236],text=": "} -_[141]={tags=_[235],text="4"} -_[140]={tags=_[234],text="after recursion "} -_[139]={tags=_[233],text="[1, 2, 3, 4, 5]"} -_[138]={tags=_[232],text=": "} -_[137]={tags=_[231],text="5"} -_[136]={tags=_[230],text="before recursion "} -_[135]={tags=_[229],text="[1, 2, 3, 4]"} -_[134]={tags=_[228],text="start: "} -_[133]={tags=_[227],text="[1, 2, 3, 4]"} -_[132]={tags=_[226],text=": "} -_[131]={tags=_[225],text="4"} -_[130]={tags=_[224],text="before recursion "} -_[129]={tags=_[223],text="[1, 2, 3]"} -_[128]={tags=_[222],text="start: "} -_[127]={tags=_[221],text="[1, 2, 3]"} -_[126]={tags=_[220],text=": "} -_[125]={tags=_[219],text="3"} -_[124]={tags=_[218],text="before recursion "} -_[123]={tags=_[217],text="[1, 2]"} -_[122]={tags=_[216],text="start: "} -_[121]={tags=_[215],text="[1, 2]"} -_[120]={tags=_[214],text=": "} -_[119]={tags=_[213],text="2"} -_[118]={tags=_[212],text="before recursion "} -_[117]={tags=_[211],text="[1]"} -_[116]={tags=_[210],text="start: "} -_[115]={tags=_[209],text="[1]"} -_[114]={tags=_[208],text=": "} -_[113]={tags=_[207],text="1"} -_[112]={tags=_[206],text="before recursion "} -_[111]={tags=_[205],text="[]"} -_[110]={tags=_[204],text="start: "} -_[109]={tags=_[203],text="pass list:"} -_[108]={tags=_[202],text="[1]"} -_[107]={tags=_[201],text=": "} -_[106]={tags=_[200],text="1"} -_[105]={tags=_[199],text="after recursion "} -_[104]={tags=_[198],text="[2]"} -_[103]={tags=_[197],text=": "} -_[102]={tags=_[196],text="2"} -_[101]={tags=_[195],text="after recursion "} -_[100]={tags=_[194],text="[3]"} -_[99]={tags=_[193],text=": "} -_[98]={tags=_[192],text="3"} -_[97]={tags=_[191],text="after recursion "} -_[96]={tags=_[190],text="[4]"} -_[95]={tags=_[189],text=": "} -_[94]={tags=_[188],text="4"} -_[93]={tags=_[187],text="after recursion "} -_[92]={tags=_[186],text="[5]"} -_[91]={tags=_[185],text=": "} -_[90]={tags=_[184],text="5"} -_[89]={tags=_[183],text="before recursion "} -_[88]={tags=_[182],text="[]"} -_[87]={tags=_[181],text="start: "} -_[86]={tags=_[180],text="[4]"} -_[85]={tags=_[179],text=": "} -_[84]={tags=_[178],text="4"} -_[83]={tags=_[177],text="before recursion "} -_[82]={tags=_[176],text="[]"} -_[81]={tags=_[175],text="start: "} -_[80]={tags=_[174],text="[3]"} -_[79]={tags=_[173],text=": "} -_[78]={tags=_[172],text="3"} -_[77]={tags=_[171],text="before recursion "} -_[76]={tags=_[170],text="[]"} -_[75]={tags=_[169],text="start: "} -_[74]={tags=_[168],text="[2]"} -_[73]={tags=_[167],text=": "} -_[72]={tags=_[166],text="2"} -_[71]={tags=_[165],text="before recursion "} -_[70]={tags=_[164],text="[]"} -_[69]={tags=_[163],text="start: "} -_[68]={tags=_[162],text="[1]"} -_[67]={tags=_[161],text=": "} -_[66]={tags=_[160],text="1"} -_[65]={tags=_[159],text="before recursion "} -_[64]={tags=_[158],text="[]"} -_[63]={tags=_[157],text="start: "} -_[62]={tags=_[156],text="new list each time:"} -_[61]={_[152],_[153],_[154],_[155]} -_[60]={_[148],_[149],_[150],_[151]} -_[59]={_[144],_[145],_[146],_[147]} -_[58]={_[140],_[141],_[142],_[143]} -_[57]={_[136],_[137],_[138],_[139]} -_[56]={_[134],_[135]} -_[55]={_[130],_[131],_[132],_[133]} -_[54]={_[128],_[129]} -_[53]={_[124],_[125],_[126],_[127]} -_[52]={_[122],_[123]} -_[51]={_[118],_[119],_[120],_[121]} -_[50]={_[116],_[117]} -_[49]={_[112],_[113],_[114],_[115]} -_[48]={_[110],_[111]} -_[47]={_[109]} -_[46]={_[105],_[106],_[107],_[108]} -_[45]={_[101],_[102],_[103],_[104]} -_[44]={_[97],_[98],_[99],_[100]} -_[43]={_[93],_[94],_[95],_[96]} -_[42]={_[89],_[90],_[91],_[92]} -_[41]={_[87],_[88]} -_[40]={_[83],_[84],_[85],_[86]} -_[39]={_[81],_[82]} -_[38]={_[77],_[78],_[79],_[80]} -_[37]={_[75],_[76]} -_[36]={_[71],_[72],_[73],_[74]} -_[35]={_[69],_[70]} -_[34]={_[65],_[66],_[67],_[68]} -_[33]={_[63],_[64]} -_[32]={_[62]} -_[31]={"return"} -_[30]={"text",_[61]} -_[29]={"text",_[60]} -_[28]={"text",_[59]} -_[27]={"text",_[58]} -_[26]={"text",_[57]} -_[25]={"text",_[56]} -_[24]={"text",_[55]} -_[23]={"text",_[54]} -_[22]={"text",_[53]} -_[21]={"text",_[52]} -_[20]={"text",_[51]} -_[19]={"text",_[50]} -_[18]={"text",_[49]} -_[17]={"text",_[48]} -_[16]={"text",_[47]} -_[15]={"text",_[46]} -_[14]={"text",_[45]} -_[13]={"text",_[44]} -_[12]={"text",_[43]} -_[11]={"text",_[42]} -_[10]={"text",_[41]} -_[9]={"text",_[40]} -_[8]={"text",_[39]} -_[7]={"text",_[38]} -_[6]={"text",_[37]} -_[5]={"text",_[36]} -_[4]={"text",_[35]} -_[3]={"text",_[34]} -_[2]={"text",_[33]} -_[1]={"text",_[32]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15],_[16],_[17],_[18],_[19],_[20],_[21],_[22],_[23],_[24],_[25],_[26],_[27],_[28],_[29],_[30],_[31]} ---[[ -{ "text", { { - tags = {}, - text = "new list each time:" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "[]" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "1" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[1]" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "[]" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "2" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[2]" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "[]" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "3" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[3]" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "[]" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "4" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[4]" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "[]" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "5" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[5]" - } } } -{ "text", { { - tags = {}, - text = "after recursion " - }, { - tags = {}, - text = "4" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[4]" - } } } -{ "text", { { - tags = {}, - text = "after recursion " - }, { - tags = {}, - text = "3" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[3]" - } } } -{ "text", { { - tags = {}, - text = "after recursion " - }, { - tags = {}, - text = "2" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[2]" - } } } -{ "text", { { - tags = {}, - text = "after recursion " - }, { - tags = {}, - text = "1" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[1]" - } } } -{ "text", { { - tags = {}, - text = "pass list:" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "[]" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "1" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[1]" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "[1]" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "2" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[1, 2]" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "[1, 2]" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "3" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[1, 2, 3]" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "[1, 2, 3]" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "4" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[1, 2, 3, 4]" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "[1, 2, 3, 4]" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "5" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[1, 2, 3, 4, 5]" - } } } -{ "text", { { - tags = {}, - text = "after recursion " - }, { - tags = {}, - text = "4" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[1, 2, 3, 4, 5]" - } } } -{ "text", { { - tags = {}, - text = "after recursion " - }, { - tags = {}, - text = "3" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[1, 2, 3, 4, 5]" - } } } -{ "text", { { - tags = {}, - text = "after recursion " - }, { - tags = {}, - text = "2" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[1, 2, 3, 4, 5]" - } } } -{ "text", { { - tags = {}, - text = "after recursion " - }, { - tags = {}, - text = "1" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "[1, 2, 3, 4, 5]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function scoped nested.ans b/test/tests/function scoped nested.ans deleted file mode 100644 index 7b7990a..0000000 --- a/test/tests/function scoped nested.ans +++ /dev/null @@ -1,44 +0,0 @@ -:$ f() - :a = 1 - - {a} - - ~ a := a + 1 - - :$ g - :a = 1 - - {a} - - ~ a := a + 1 - - :$ h() - :a = 1 - - {a} - - ~ a := a + 1 - - \> depth 2, unscoped: - - ~ g - - ~ g - - ~ g - - \> depth 2, scoped: - - ~ h - - ~ h - - ~ h - -depth 1: - -~ f - -~ f - -~ f diff --git a/test/tests/function scoped nested.lua b/test/tests/function scoped nested.lua deleted file mode 100644 index 0de588b..0000000 --- a/test/tests/function scoped nested.lua +++ /dev/null @@ -1,230 +0,0 @@ -local _={} -_[113]={} -_[112]={} -_[111]={} -_[110]={} -_[109]={} -_[108]={} -_[107]={} -_[106]={} -_[105]={} -_[104]={} -_[103]={} -_[102]={} -_[101]={} -_[100]={} -_[99]={} -_[98]={} -_[97]={} -_[96]={} -_[95]={} -_[94]={} -_[93]={} -_[92]={} -_[91]={} -_[90]={} -_[89]={} -_[88]={} -_[87]={} -_[86]={} -_[85]={tags=_[113],text="1"} -_[84]={tags=_[112],text="1"} -_[83]={tags=_[111],text="1"} -_[82]={tags=_[110],text="> depth 2, scoped:"} -_[81]={tags=_[109],text="3"} -_[80]={tags=_[108],text="2"} -_[79]={tags=_[107],text="1"} -_[78]={tags=_[106],text="> depth 2, unscoped:"} -_[77]={tags=_[105],text="1"} -_[76]={tags=_[104],text="1"} -_[75]={tags=_[103],text="1"} -_[74]={tags=_[102],text="1"} -_[73]={tags=_[101],text="> depth 2, scoped:"} -_[72]={tags=_[100],text="3"} -_[71]={tags=_[99],text="2"} -_[70]={tags=_[98],text="1"} -_[69]={tags=_[97],text="> depth 2, unscoped:"} -_[68]={tags=_[96],text="1"} -_[67]={tags=_[95],text="1"} -_[66]={tags=_[94],text="1"} -_[65]={tags=_[93],text="1"} -_[64]={tags=_[92],text="> depth 2, scoped:"} -_[63]={tags=_[91],text="3"} -_[62]={tags=_[90],text="2"} -_[61]={tags=_[89],text="1"} -_[60]={tags=_[88],text="> depth 2, unscoped:"} -_[59]={tags=_[87],text="1"} -_[58]={tags=_[86],text="depth 1:"} -_[57]={_[85]} -_[56]={_[84]} -_[55]={_[83]} -_[54]={_[82]} -_[53]={_[81]} -_[52]={_[80]} -_[51]={_[79]} -_[50]={_[78]} -_[49]={_[77]} -_[48]={_[76]} -_[47]={_[75]} -_[46]={_[74]} -_[45]={_[73]} -_[44]={_[72]} -_[43]={_[71]} -_[42]={_[70]} -_[41]={_[69]} -_[40]={_[68]} -_[39]={_[67]} -_[38]={_[66]} -_[37]={_[65]} -_[36]={_[64]} -_[35]={_[63]} -_[34]={_[62]} -_[33]={_[61]} -_[32]={_[60]} -_[31]={_[59]} -_[30]={_[58]} -_[29]={"return"} -_[28]={"text",_[57]} -_[27]={"text",_[56]} -_[26]={"text",_[55]} -_[25]={"text",_[54]} -_[24]={"text",_[53]} -_[23]={"text",_[52]} -_[22]={"text",_[51]} -_[21]={"text",_[50]} -_[20]={"text",_[49]} -_[19]={"text",_[48]} -_[18]={"text",_[47]} -_[17]={"text",_[46]} -_[16]={"text",_[45]} -_[15]={"text",_[44]} -_[14]={"text",_[43]} -_[13]={"text",_[42]} -_[12]={"text",_[41]} -_[11]={"text",_[40]} -_[10]={"text",_[39]} -_[9]={"text",_[38]} -_[8]={"text",_[37]} -_[7]={"text",_[36]} -_[6]={"text",_[35]} -_[5]={"text",_[34]} -_[4]={"text",_[33]} -_[3]={"text",_[32]} -_[2]={"text",_[31]} -_[1]={"text",_[30]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15],_[16],_[17],_[18],_[19],_[20],_[21],_[22],_[23],_[24],_[25],_[26],_[27],_[28],_[29]} ---[[ -{ "text", { { - tags = {}, - text = "depth 1:" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "> depth 2, unscoped:" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "text", { { - tags = {}, - text = "> depth 2, scoped:" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "> depth 2, unscoped:" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "text", { { - tags = {}, - text = "> depth 2, scoped:" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "> depth 2, unscoped:" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "text", { { - tags = {}, - text = "> depth 2, scoped:" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function scoped recursive.ans b/test/tests/function scoped recursive.ans deleted file mode 100644 index 6a409ee..0000000 --- a/test/tests/function scoped recursive.ans +++ /dev/null @@ -1,19 +0,0 @@ -:n = 0 - -:$ f(c=1) - :a = 1 - - start: {a} - - ~ a := a + 1 - - ~ n += 1 - - before recursion {c}: {a} - - ~ n < 5 - ~ f(c+1) - - after recursion {c}: {a} - -~ f diff --git a/test/tests/function scoped recursive.lua b/test/tests/function scoped recursive.lua deleted file mode 100644 index 8887049..0000000 --- a/test/tests/function scoped recursive.lua +++ /dev/null @@ -1,278 +0,0 @@ -local _={} -_[121]={} -_[120]={} -_[119]={} -_[118]={} -_[117]={} -_[116]={} -_[115]={} -_[114]={} -_[113]={} -_[112]={} -_[111]={} -_[110]={} -_[109]={} -_[108]={} -_[107]={} -_[106]={} -_[105]={} -_[104]={} -_[103]={} -_[102]={} -_[101]={} -_[100]={} -_[99]={} -_[98]={} -_[97]={} -_[96]={} -_[95]={} -_[94]={} -_[93]={} -_[92]={} -_[91]={} -_[90]={} -_[89]={} -_[88]={} -_[87]={} -_[86]={} -_[85]={} -_[84]={} -_[83]={} -_[82]={} -_[81]={} -_[80]={} -_[79]={} -_[78]={} -_[77]={} -_[76]={} -_[75]={tags=_[121],text="2"} -_[74]={tags=_[120],text=": "} -_[73]={tags=_[119],text="1"} -_[72]={tags=_[118],text="after recursion "} -_[71]={tags=_[117],text="2"} -_[70]={tags=_[116],text=": "} -_[69]={tags=_[115],text="2"} -_[68]={tags=_[114],text="after recursion "} -_[67]={tags=_[113],text="2"} -_[66]={tags=_[112],text=": "} -_[65]={tags=_[111],text="3"} -_[64]={tags=_[110],text="after recursion "} -_[63]={tags=_[109],text="2"} -_[62]={tags=_[108],text=": "} -_[61]={tags=_[107],text="4"} -_[60]={tags=_[106],text="after recursion "} -_[59]={tags=_[105],text="2"} -_[58]={tags=_[104],text=": "} -_[57]={tags=_[103],text="5"} -_[56]={tags=_[102],text="before recursion "} -_[55]={tags=_[101],text="1"} -_[54]={tags=_[100],text="start: "} -_[53]={tags=_[99],text="2"} -_[52]={tags=_[98],text=": "} -_[51]={tags=_[97],text="4"} -_[50]={tags=_[96],text="before recursion "} -_[49]={tags=_[95],text="1"} -_[48]={tags=_[94],text="start: "} -_[47]={tags=_[93],text="2"} -_[46]={tags=_[92],text=": "} -_[45]={tags=_[91],text="3"} -_[44]={tags=_[90],text="before recursion "} -_[43]={tags=_[89],text="1"} -_[42]={tags=_[88],text="start: "} -_[41]={tags=_[87],text="2"} -_[40]={tags=_[86],text=": "} -_[39]={tags=_[85],text="2"} -_[38]={tags=_[84],text="before recursion "} -_[37]={tags=_[83],text="1"} -_[36]={tags=_[82],text="start: "} -_[35]={tags=_[81],text="2"} -_[34]={tags=_[80],text=": "} -_[33]={tags=_[79],text="1"} -_[32]={tags=_[78],text="before recursion "} -_[31]={tags=_[77],text="1"} -_[30]={tags=_[76],text="start: "} -_[29]={_[72],_[73],_[74],_[75]} -_[28]={_[68],_[69],_[70],_[71]} -_[27]={_[64],_[65],_[66],_[67]} -_[26]={_[60],_[61],_[62],_[63]} -_[25]={_[56],_[57],_[58],_[59]} -_[24]={_[54],_[55]} -_[23]={_[50],_[51],_[52],_[53]} -_[22]={_[48],_[49]} -_[21]={_[44],_[45],_[46],_[47]} -_[20]={_[42],_[43]} -_[19]={_[38],_[39],_[40],_[41]} -_[18]={_[36],_[37]} -_[17]={_[32],_[33],_[34],_[35]} -_[16]={_[30],_[31]} -_[15]={"return"} -_[14]={"text",_[29]} -_[13]={"text",_[28]} -_[12]={"text",_[27]} -_[11]={"text",_[26]} -_[10]={"text",_[25]} -_[9]={"text",_[24]} -_[8]={"text",_[23]} -_[7]={"text",_[22]} -_[6]={"text",_[21]} -_[5]={"text",_[20]} -_[4]={"text",_[19]} -_[3]={"text",_[18]} -_[2]={"text",_[17]} -_[1]={"text",_[16]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15]} ---[[ -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "1" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "2" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "3" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "4" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "start: " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "before recursion " - }, { - tags = {}, - text = "5" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "after recursion " - }, { - tags = {}, - text = "4" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "after recursion " - }, { - tags = {}, - text = "3" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "after recursion " - }, { - tags = {}, - text = "2" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "after recursion " - }, { - tags = {}, - text = "1" - }, { - tags = {}, - text = ": " - }, { - tags = {}, - text = "2" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function scoped.ans b/test/tests/function scoped.ans deleted file mode 100644 index d61e4bf..0000000 --- a/test/tests/function scoped.ans +++ /dev/null @@ -1,29 +0,0 @@ -:$ f() - :a = 1 - - {a} - - ~ a := a + 1 - -:$ g - :a = 1 - - {a} - - ~ a := a + 1 - -scoped: - -~ f - -~ f - -~ f - -unscoped: - -~ g - -~ g - -~ g \ No newline at end of file diff --git a/test/tests/function scoped.lua b/test/tests/function scoped.lua deleted file mode 100644 index 7ceb049..0000000 --- a/test/tests/function scoped.lua +++ /dev/null @@ -1,70 +0,0 @@ -local _={} -_[33]={} -_[32]={} -_[31]={} -_[30]={} -_[29]={} -_[28]={} -_[27]={} -_[26]={} -_[25]={text="3",tags=_[33]} -_[24]={text="2",tags=_[32]} -_[23]={text="1",tags=_[31]} -_[22]={text="unscoped:",tags=_[30]} -_[21]={text="1",tags=_[29]} -_[20]={text="1",tags=_[28]} -_[19]={text="1",tags=_[27]} -_[18]={text="scoped:",tags=_[26]} -_[17]={_[25]} -_[16]={_[24]} -_[15]={_[23]} -_[14]={_[22]} -_[13]={_[21]} -_[12]={_[20]} -_[11]={_[19]} -_[10]={_[18]} -_[9]={"return"} -_[8]={"text",_[17]} -_[7]={"text",_[16]} -_[6]={"text",_[15]} -_[5]={"text",_[14]} -_[4]={"text",_[13]} -_[3]={"text",_[12]} -_[2]={"text",_[11]} -_[1]={"text",_[10]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9]} ---[[ -{ "text", { { - tags = {}, - text = "scoped:" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "unscoped:" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function selection.ans b/test/tests/function selection.ans deleted file mode 100644 index 04ce4c6..0000000 --- a/test/tests/function selection.ans +++ /dev/null @@ -1,12 +0,0 @@ -:$ a(x::number) - @x + 2 - -:$ x - :$ a(x::string) - @x + "heh" - - {a("plop")} - - {a(2)} - -~ x diff --git a/test/tests/function selection.lua b/test/tests/function selection.lua deleted file mode 100644 index e39f511..0000000 --- a/test/tests/function selection.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[9]={} -_[8]={} -_[7]={tags=_[9],text="4"} -_[6]={tags=_[8],text="plopheh"} -_[5]={_[7]} -_[4]={_[6]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "plopheh" - } } } -{ "text", { { - tags = {}, - text = "4" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function separate variable from variants.ans b/test/tests/function separate variable from variants.ans deleted file mode 100644 index 9b656e8..0000000 --- a/test/tests/function separate variable from variants.ans +++ /dev/null @@ -1,10 +0,0 @@ -:$ f - :a = 2 - -:$ f(x) - :a = 5 - -:$ f(b) - :a = 10 - -{f.a} = 2 diff --git a/test/tests/function separate variable from variants.lua b/test/tests/function separate variable from variants.lua deleted file mode 100644 index 6be60bf..0000000 --- a/test/tests/function separate variable from variants.lua +++ /dev/null @@ -1,18 +0,0 @@ -local _={} -_[6]={} -_[5]={tags=_[6],text=" = 2"} -_[4]={tags=_[6],text="2"} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "2" - }, { - tags =
, - text = " = 2" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function type dispatch ambigous.ans b/test/tests/function type dispatch ambigous.ans deleted file mode 100644 index 52ffba8..0000000 --- a/test/tests/function type dispatch ambigous.ans +++ /dev/null @@ -1,7 +0,0 @@ -:$ fn(x::number) - x - -:$ fn(a::number) - a - -~ fn(5) diff --git a/test/tests/function type dispatch ambigous.lua b/test/tests/function type dispatch ambigous.lua deleted file mode 100644 index 8fca88b..0000000 --- a/test/tests/function type dispatch ambigous.lua +++ /dev/null @@ -1,6 +0,0 @@ -local _={} -_[1]={"error","function call \"fn\" is ambigous; may be at least either:\n\9function type dispatch ambigous.fn(a::number) (at test/tests/function type dispatch ambigous.ans:4)\n\9function type dispatch ambigous.fn(x::number) (at test/tests/function type dispatch ambigous.ans:1); at test/tests/function type dispatch ambigous.ans:7"} -return {_[1]} ---[[ -{ "error", 'function call "fn" is ambigous; may be at least either:\n\tfunction type dispatch ambigous.fn(a::number) (at test/tests/function type dispatch ambigous.ans:4)\n\tfunction type dispatch ambigous.fn(x::number) (at test/tests/function type dispatch ambigous.ans:1); at test/tests/function type dispatch ambigous.ans:7' } -]]-- \ No newline at end of file diff --git a/test/tests/function type dispatch with default.ans b/test/tests/function type dispatch with default.ans deleted file mode 100644 index 48c460a..0000000 --- a/test/tests/function type dispatch with default.ans +++ /dev/null @@ -1,26 +0,0 @@ -:$ fn(x::number) - x - -:$ fn(a="o"::string) - a - -~ fn("s") - -~ fn(5) - -~ fn() - -:$ g(n="s", a=5::number) - @"gn" - -:$ g(n="s", a="lol"::string) - @"gs" - -{g(n="k", a="l")} -{g(n="k", a=1)} -{g(n="k", "l")} -{g(n="k", 1)} -{g("k", "l")} -{g("k", 1)} -{g("k", a="l")} -{g("k", a=1)} diff --git a/test/tests/function type dispatch with default.lua b/test/tests/function type dispatch with default.lua deleted file mode 100644 index 4ea3755..0000000 --- a/test/tests/function type dispatch with default.lua +++ /dev/null @@ -1,73 +0,0 @@ -local _={} -_[31]={} -_[30]={} -_[29]={} -_[28]={} -_[27]={} -_[26]={} -_[25]={} -_[24]={} -_[23]={} -_[22]={} -_[21]={} -_[20]={tags=_[31],text="gn"} -_[19]={tags=_[30],text="gs"} -_[18]={tags=_[29],text="gn"} -_[17]={tags=_[28],text="gs"} -_[16]={tags=_[27],text="gn"} -_[15]={tags=_[26],text="gs"} -_[14]={tags=_[25],text="gn"} -_[13]={tags=_[24],text="gs"} -_[12]={tags=_[23],text="a"} -_[11]={tags=_[22],text="x"} -_[10]={tags=_[21],text="a"} -_[9]={_[13],_[14],_[15],_[16],_[17],_[18],_[19],_[20]} -_[8]={_[12]} -_[7]={_[11]} -_[6]={_[10]} -_[5]={"return"} -_[4]={"text",_[9]} -_[3]={"text",_[8]} -_[2]={"text",_[7]} -_[1]={"text",_[6]} -return {_[1],_[2],_[3],_[4],_[5]} ---[[ -{ "text", { { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "x" - } } } -{ "text", { { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "gs" - }, { - tags = {}, - text = "gn" - }, { - tags = {}, - text = "gs" - }, { - tags = {}, - text = "gn" - }, { - tags = {}, - text = "gs" - }, { - tags = {}, - text = "gn" - }, { - tags = {}, - text = "gs" - }, { - tags = {}, - text = "gn" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function type dispatch.ans b/test/tests/function type dispatch.ans deleted file mode 100644 index 315f56c..0000000 --- a/test/tests/function type dispatch.ans +++ /dev/null @@ -1,9 +0,0 @@ -:$ fn(x::number) - x - -:$ fn(a::string) - a - -~ fn("s") - -~ fn(5) diff --git a/test/tests/function type dispatch.lua b/test/tests/function type dispatch.lua deleted file mode 100644 index df94781..0000000 --- a/test/tests/function type dispatch.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[9]={} -_[8]={} -_[7]={tags=_[9],text="x"} -_[6]={tags=_[8],text="a"} -_[5]={_[7]} -_[4]={_[6]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "x" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function ufcs arg.ans b/test/tests/function ufcs arg.ans deleted file mode 100644 index 530e957..0000000 --- a/test/tests/function ufcs arg.ans +++ /dev/null @@ -1,6 +0,0 @@ -:$ f(a) - {a} - -~ "ok"!f - -~ "ok"!f() diff --git a/test/tests/function ufcs arg.lua b/test/tests/function ufcs arg.lua deleted file mode 100644 index 229c5dc..0000000 --- a/test/tests/function ufcs arg.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[9]={} -_[8]={} -_[7]={tags=_[9],text="ok"} -_[6]={tags=_[8],text="ok"} -_[5]={_[7]} -_[4]={_[6]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function ufcs args.ans b/test/tests/function ufcs args.ans deleted file mode 100644 index 896c6c7..0000000 --- a/test/tests/function ufcs args.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ f(a, b) - {a}{b} - -~ "o"!f("k") diff --git a/test/tests/function ufcs args.lua b/test/tests/function ufcs args.lua deleted file mode 100644 index 5c81938..0000000 --- a/test/tests/function ufcs args.lua +++ /dev/null @@ -1,18 +0,0 @@ -local _={} -_[6]={} -_[5]={tags=_[6],text="k"} -_[4]={tags=_[6],text="o"} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "o" - }, { - tags =
, - text = "k" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function vararg empty.ans b/test/tests/function vararg empty.ans deleted file mode 100644 index f521e78..0000000 --- a/test/tests/function vararg empty.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ f(l...) - {l} - -~ f() diff --git a/test/tests/function vararg empty.lua b/test/tests/function vararg empty.lua deleted file mode 100644 index d9a3d85..0000000 --- a/test/tests/function vararg empty.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="[]"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "[]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function vararg.ans b/test/tests/function vararg.ans deleted file mode 100644 index b7aa5da..0000000 --- a/test/tests/function vararg.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ f(l...) - {l} - -~ f("o", "k") diff --git a/test/tests/function vararg.lua b/test/tests/function vararg.lua deleted file mode 100644 index 41da986..0000000 --- a/test/tests/function vararg.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="[o, k]"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "[o, k]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/function.ans b/test/tests/function.ans deleted file mode 100644 index 3276153..0000000 --- a/test/tests/function.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ f - ok - -~ f diff --git a/test/tests/function.lua b/test/tests/function.lua deleted file mode 100644 index efc9631..0000000 --- a/test/tests/function.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="ok"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/immediately run function explicit call.ans b/test/tests/immediately run function explicit call.ans deleted file mode 100644 index a9bebc7..0000000 --- a/test/tests/immediately run function explicit call.ans +++ /dev/null @@ -1,4 +0,0 @@ -:~$ a - a.👁️: {a.👁️} - -~ a() \ No newline at end of file diff --git a/test/tests/immediately run function explicit call.lua b/test/tests/immediately run function explicit call.lua deleted file mode 100644 index f021ef0..0000000 --- a/test/tests/immediately run function explicit call.lua +++ /dev/null @@ -1,30 +0,0 @@ -local _={} -_[11]={} -_[10]={} -_[9]={tags=_[11],text="1"} -_[8]={tags=_[11],text="a.\240\159\145\129\239\184\143: "} -_[7]={tags=_[10],text="0"} -_[6]={tags=_[10],text="a.\240\159\145\129\239\184\143: "} -_[5]={_[8],_[9]} -_[4]={_[6],_[7]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = <1>{}, - text = "a.👁️: " - }, { - tags =
, - text = "0" - } } } -{ "text", { { - tags = <1>{}, - text = "a.👁️: " - }, { - tags =
, - text = "1" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/immediately run function implicit call.ans b/test/tests/immediately run function implicit call.ans deleted file mode 100644 index 57482a7..0000000 --- a/test/tests/immediately run function implicit call.ans +++ /dev/null @@ -1,10 +0,0 @@ -:$ f - ko - :~$ a - a.👁️: {a.👁️} - ok - -~ f.a - -In function: -~ f diff --git a/test/tests/immediately run function implicit call.lua b/test/tests/immediately run function implicit call.lua deleted file mode 100644 index 1837bbf..0000000 --- a/test/tests/immediately run function implicit call.lua +++ /dev/null @@ -1,49 +0,0 @@ -local _={} -_[18]={} -_[17]={} -_[16]={} -_[15]={} -_[14]={} -_[13]={text="ok",tags=_[18]} -_[12]={text=" ",tags=_[17]} -_[11]={text="1",tags=_[17]} -_[10]={text="a.\240\159\145\129\239\184\143: ",tags=_[17]} -_[9]={text="ko",tags=_[16]} -_[8]={text="In function:",tags=_[15]} -_[7]={text="0",tags=_[14]} -_[6]={text="a.\240\159\145\129\239\184\143: ",tags=_[14]} -_[5]={_[8],_[9],_[10],_[11],_[12],_[13]} -_[4]={_[6],_[7]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = <1>{}, - text = "a.👁️: " - }, { - tags =
, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "In function:" - }, { - tags = {}, - text = "ko" - }, { - tags = <1>{}, - text = "a.👁️: " - }, { - tags =
, - text = "1" - }, { - tags =
, - text = " " - }, { - tags = {}, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/immediately run function scope.ans b/test/tests/immediately run function scope.ans deleted file mode 100644 index 47ea1f3..0000000 --- a/test/tests/immediately run function scope.ans +++ /dev/null @@ -1,2 +0,0 @@ -:~$ a - a.👁️: {a.👁️} diff --git a/test/tests/immediately run function scope.lua b/test/tests/immediately run function scope.lua deleted file mode 100644 index 5d7e948..0000000 --- a/test/tests/immediately run function scope.lua +++ /dev/null @@ -1,18 +0,0 @@ -local _={} -_[6]={} -_[5]={tags=_[6],text="0"} -_[4]={tags=_[6],text="a.\240\159\145\129\239\184\143: "} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "a.👁️: " - }, { - tags =
, - text = "0" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/immediately run variable.ans b/test/tests/immediately run variable.ans deleted file mode 100644 index 067d9fe..0000000 --- a/test/tests/immediately run variable.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ a - a.👁️: {a.👁️} - -:~ b = &a diff --git a/test/tests/immediately run variable.lua b/test/tests/immediately run variable.lua deleted file mode 100644 index 0273939..0000000 --- a/test/tests/immediately run variable.lua +++ /dev/null @@ -1,19 +0,0 @@ -local _={} -_[7]={} -_[6]={} -_[5]={tags=_[7],text="0"} -_[4]={tags=_[6],text="a.\240\159\145\129\239\184\143: "} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "a.👁️: " - }, { - tags = {}, - text = "0" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/implicit call of references.ans b/test/tests/implicit call of references.ans deleted file mode 100644 index fa04b23..0000000 --- a/test/tests/implicit call of references.ans +++ /dev/null @@ -1,61 +0,0 @@ -Function with argument: - -:$ f(x) - -{&f} - -Function without argument: - -:$ a - lol - :$ b - hihi - -a: {a} - -&a: {&a} - -:ref = &a - -&ref: {&ref} - -&&&ref: {&&&ref} - -ref: {ref} - -ref.b: {ref.b} - -ref.b!: {ref.b!} - -ref.b(): {ref.b()} - -&ref.b: {&ref.b} - -&ref.b!: {&ref.b!} - -&ref.b(): {&ref.b()} - -&ref!: {&ref!} - -Objects: - -:% A - :$ b - KK - @1 - -&A: {&A} - -:ref A = &A - -ref A: {ref A} - -&ref A: {&ref A} - -A.b: {A.b} - -&A.b: {&A.b} - -\(&A).b: {(&A).b} - -&(&A).b: {&(&A).b} diff --git a/test/tests/implicit call of references.lua b/test/tests/implicit call of references.lua deleted file mode 100644 index 6e4716c..0000000 --- a/test/tests/implicit call of references.lua +++ /dev/null @@ -1,285 +0,0 @@ -local _={} -_[131]={} -_[130]={} -_[129]={} -_[128]={} -_[127]={} -_[126]={} -_[125]={} -_[124]={} -_[123]={} -_[122]={} -_[121]={} -_[120]={} -_[119]={} -_[118]={} -_[117]={} -_[116]={} -_[115]={} -_[114]={} -_[113]={} -_[112]={} -_[111]={} -_[110]={} -_[109]={} -_[108]={} -_[107]={} -_[106]={} -_[105]={} -_[104]={} -_[103]={} -_[102]={} -_[101]={} -_[100]={} -_[99]={} -_[98]={} -_[97]={} -_[96]={} -_[95]={} -_[94]={} -_[93]={} -_[92]={} -_[91]={} -_[90]={} -_[89]={tags=_[131],text="&implicit call of references.A.b"} -_[88]={tags=_[130],text="&(&A).b: "} -_[87]={tags=_[129],text="KK"} -_[86]={tags=_[128],text="(&A).b: "} -_[85]={tags=_[127],text="&implicit call of references.A.b"} -_[84]={tags=_[126],text="&A.b: "} -_[83]={tags=_[125],text="KK"} -_[82]={tags=_[124],text="A.b: "} -_[81]={tags=_[123],text="&implicit call of references.A"} -_[80]={tags=_[122],text="&ref A: "} -_[79]={tags=_[121],text="1"} -_[78]={tags=_[120],text="ref A: "} -_[77]={tags=_[119],text="&implicit call of references.A"} -_[76]={tags=_[118],text="&A: "} -_[75]={tags=_[117],text="Objects:"} -_[74]={tags=_[116],text="lol"} -_[73]={tags=_[115],text="&ref!: "} -_[72]={tags=_[114],text="hihi"} -_[71]={tags=_[113],text="&ref.b(): "} -_[70]={tags=_[112],text="hihi"} -_[69]={tags=_[111],text="&ref.b!: "} -_[68]={tags=_[110],text="&implicit call of references.a.b"} -_[67]={tags=_[109],text="&ref.b: "} -_[66]={tags=_[108],text="hihi"} -_[65]={tags=_[107],text="ref.b(): "} -_[64]={tags=_[106],text="hihi"} -_[63]={tags=_[105],text="ref.b!: "} -_[62]={tags=_[104],text="hihi"} -_[61]={tags=_[103],text="ref.b: "} -_[60]={tags=_[102],text="lol"} -_[59]={tags=_[101],text="ref: "} -_[58]={tags=_[100],text="&implicit call of references.a"} -_[57]={tags=_[99],text="&&&ref: "} -_[56]={tags=_[98],text="&implicit call of references.a"} -_[55]={tags=_[97],text="&ref: "} -_[54]={tags=_[96],text="&implicit call of references.a"} -_[53]={tags=_[95],text="&a: "} -_[52]={tags=_[94],text="lol"} -_[51]={tags=_[93],text="a: "} -_[50]={tags=_[92],text="Function without argument:"} -_[49]={tags=_[91],text="&implicit call of references.f"} -_[48]={tags=_[90],text="Function with argument:"} -_[47]={_[88],_[89]} -_[46]={_[86],_[87]} -_[45]={_[84],_[85]} -_[44]={_[82],_[83]} -_[43]={_[80],_[81]} -_[42]={_[78],_[79]} -_[41]={_[76],_[77]} -_[40]={_[75]} -_[39]={_[73],_[74]} -_[38]={_[71],_[72]} -_[37]={_[69],_[70]} -_[36]={_[67],_[68]} -_[35]={_[65],_[66]} -_[34]={_[63],_[64]} -_[33]={_[61],_[62]} -_[32]={_[59],_[60]} -_[31]={_[57],_[58]} -_[30]={_[55],_[56]} -_[29]={_[53],_[54]} -_[28]={_[51],_[52]} -_[27]={_[50]} -_[26]={_[49]} -_[25]={_[48]} -_[24]={"return"} -_[23]={"text",_[47]} -_[22]={"text",_[46]} -_[21]={"text",_[45]} -_[20]={"text",_[44]} -_[19]={"text",_[43]} -_[18]={"text",_[42]} -_[17]={"text",_[41]} -_[16]={"text",_[40]} -_[15]={"text",_[39]} -_[14]={"text",_[38]} -_[13]={"text",_[37]} -_[12]={"text",_[36]} -_[11]={"text",_[35]} -_[10]={"text",_[34]} -_[9]={"text",_[33]} -_[8]={"text",_[32]} -_[7]={"text",_[31]} -_[6]={"text",_[30]} -_[5]={"text",_[29]} -_[4]={"text",_[28]} -_[3]={"text",_[27]} -_[2]={"text",_[26]} -_[1]={"text",_[25]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15],_[16],_[17],_[18],_[19],_[20],_[21],_[22],_[23],_[24]} ---[[ -{ "text", { { - tags = {}, - text = "Function with argument:" - } } } -{ "text", { { - tags = {}, - text = "&implicit call of references.f" - } } } -{ "text", { { - tags = {}, - text = "Function without argument:" - } } } -{ "text", { { - tags = {}, - text = "a: " - }, { - tags = {}, - text = "lol" - } } } -{ "text", { { - tags = {}, - text = "&a: " - }, { - tags = {}, - text = "&implicit call of references.a" - } } } -{ "text", { { - tags = {}, - text = "&ref: " - }, { - tags = {}, - text = "&implicit call of references.a" - } } } -{ "text", { { - tags = {}, - text = "&&&ref: " - }, { - tags = {}, - text = "&implicit call of references.a" - } } } -{ "text", { { - tags = {}, - text = "ref: " - }, { - tags = {}, - text = "lol" - } } } -{ "text", { { - tags = {}, - text = "ref.b: " - }, { - tags = {}, - text = "hihi" - } } } -{ "text", { { - tags = {}, - text = "ref.b!: " - }, { - tags = {}, - text = "hihi" - } } } -{ "text", { { - tags = {}, - text = "ref.b(): " - }, { - tags = {}, - text = "hihi" - } } } -{ "text", { { - tags = {}, - text = "&ref.b: " - }, { - tags = {}, - text = "&implicit call of references.a.b" - } } } -{ "text", { { - tags = {}, - text = "&ref.b!: " - }, { - tags = {}, - text = "hihi" - } } } -{ "text", { { - tags = {}, - text = "&ref.b(): " - }, { - tags = {}, - text = "hihi" - } } } -{ "text", { { - tags = {}, - text = "&ref!: " - }, { - tags = {}, - text = "lol" - } } } -{ "text", { { - tags = {}, - text = "Objects:" - } } } -{ "text", { { - tags = {}, - text = "&A: " - }, { - tags = {}, - text = "&implicit call of references.A" - } } } -{ "text", { { - tags = {}, - text = "ref A: " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "&ref A: " - }, { - tags = {}, - text = "&implicit call of references.A" - } } } -{ "text", { { - tags = {}, - text = "A.b: " - }, { - tags = {}, - text = "KK" - } } } -{ "text", { { - tags = {}, - text = "&A.b: " - }, { - tags = {}, - text = "&implicit call of references.A.b" - } } } -{ "text", { { - tags = {}, - text = "(&A).b: " - }, { - tags = {}, - text = "KK" - } } } -{ "text", { { - tags = {}, - text = "&(&A).b: " - }, { - tags = {}, - text = "&implicit call of references.A.b" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/implicit multiplication.ans b/test/tests/implicit multiplication.ans deleted file mode 100644 index 478b7da..0000000 --- a/test/tests/implicit multiplication.ans +++ /dev/null @@ -1,11 +0,0 @@ -:x = 4 - -{2x} = 8 - -{(2+1)x} = 12 - -{2pi} = 2pi - -{1/2x} = 0.125 - -{(1/2)x+.1} = 2.1 diff --git a/test/tests/implicit multiplication.lua b/test/tests/implicit multiplication.lua deleted file mode 100644 index d49bed3..0000000 --- a/test/tests/implicit multiplication.lua +++ /dev/null @@ -1,71 +0,0 @@ -local _={} -_[31]={} -_[30]={} -_[29]={} -_[28]={} -_[27]={} -_[26]={} -_[25]={} -_[24]={} -_[23]={} -_[22]={} -_[21]={text=" = 2.1",tags=_[31]} -_[20]={text="2.1",tags=_[30]} -_[19]={text=" = 0.125",tags=_[29]} -_[18]={text="0.125",tags=_[28]} -_[17]={text=" = 2pi",tags=_[27]} -_[16]={text="6.2831853071796",tags=_[26]} -_[15]={text=" = 12",tags=_[25]} -_[14]={text="12",tags=_[24]} -_[13]={text=" = 8",tags=_[23]} -_[12]={text="8",tags=_[22]} -_[11]={_[20],_[21]} -_[10]={_[18],_[19]} -_[9]={_[16],_[17]} -_[8]={_[14],_[15]} -_[7]={_[12],_[13]} -_[6]={"return"} -_[5]={"text",_[11]} -_[4]={"text",_[10]} -_[3]={"text",_[9]} -_[2]={"text",_[8]} -_[1]={"text",_[7]} -return {_[1],_[2],_[3],_[4],_[5],_[6]} ---[[ -{ "text", { { - tags = {}, - text = "8" - }, { - tags = {}, - text = " = 8" - } } } -{ "text", { { - tags = {}, - text = "12" - }, { - tags = {}, - text = " = 12" - } } } -{ "text", { { - tags = {}, - text = "6.2831853071796" - }, { - tags = {}, - text = " = 2pi" - } } } -{ "text", { { - tags = {}, - text = "0.125" - }, { - tags = {}, - text = " = 0.125" - } } } -{ "text", { { - tags = {}, - text = "2.1" - }, { - tags = {}, - text = " = 2.1" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/interrupt callback nested paragraph.ans b/test/tests/interrupt callback nested paragraph.ans deleted file mode 100644 index f945e45..0000000 --- a/test/tests/interrupt callback nested paragraph.ans +++ /dev/null @@ -1,19 +0,0 @@ -:$ oh - :! leave - in interrupt: {bar.var} - no - :$ bar - :var = 5 - - ~ var := 2 - - before: {var} - - ~ interrupt("leave") - - :! foo - checkpoint - - after: {var} - -~ oh.bar diff --git a/test/tests/interrupt callback nested paragraph.lua b/test/tests/interrupt callback nested paragraph.lua deleted file mode 100644 index ad68679..0000000 --- a/test/tests/interrupt callback nested paragraph.lua +++ /dev/null @@ -1,37 +0,0 @@ -local _={} -_[14]={} -_[13]={} -_[12]={} -_[11]={tags=_[14],text="no"} -_[10]={tags=_[13],text="5"} -_[9]={tags=_[13],text="in interrupt: "} -_[8]={tags=_[12],text="2"} -_[7]={tags=_[12],text="before: "} -_[6]={_[9],_[10],_[11]} -_[5]={_[7],_[8]} -_[4]={"return"} -_[3]={"text",_[6]} -_[2]={"wait",0} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = <1>{}, - text = "before: " - }, { - tags =
, - text = "2" - } } } -{ "wait", 0 } -{ "text", { { - tags = <1>{}, - text = "in interrupt: " - }, { - tags =
, - text = "5" - }, { - tags = {}, - text = "no" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/interrupt callback nested.ans b/test/tests/interrupt callback nested.ans deleted file mode 100644 index bbf13ac..0000000 --- a/test/tests/interrupt callback nested.ans +++ /dev/null @@ -1,20 +0,0 @@ -:$ leave - in interrupt: {oh.bar.var} - -:$ oh - no - :$ bar - :var = 5 - - ~ var := 2 - - before: {var} - - ~ interrupt("leave") - - :! foo - checkpoint - - after: {var} - -~ oh.bar diff --git a/test/tests/interrupt callback nested.lua b/test/tests/interrupt callback nested.lua deleted file mode 100644 index 97d6750..0000000 --- a/test/tests/interrupt callback nested.lua +++ /dev/null @@ -1,32 +0,0 @@ -local _={} -_[12]={} -_[11]={} -_[10]={tags=_[12],text="5"} -_[9]={tags=_[12],text="in interrupt: "} -_[8]={tags=_[11],text="2"} -_[7]={tags=_[11],text="before: "} -_[6]={_[9],_[10]} -_[5]={_[7],_[8]} -_[4]={"return"} -_[3]={"text",_[6]} -_[2]={"wait",0} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = <1>{}, - text = "before: " - }, { - tags =
, - text = "2" - } } } -{ "wait", 0 } -{ "text", { { - tags = <1>{}, - text = "in interrupt: " - }, { - tags =
, - text = "5" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/interrupt callback.ans b/test/tests/interrupt callback.ans deleted file mode 100644 index 609755f..0000000 --- a/test/tests/interrupt callback.ans +++ /dev/null @@ -1,18 +0,0 @@ -:$ bar - :var = 5 - - ~ var := 2 - - :$ leave - in interrupt: {var} - - before: {var} - - ~ interrupt("leave") - - :! foo - checkpoint - - after: {var} - -~ bar diff --git a/test/tests/interrupt callback.lua b/test/tests/interrupt callback.lua deleted file mode 100644 index 97d6750..0000000 --- a/test/tests/interrupt callback.lua +++ /dev/null @@ -1,32 +0,0 @@ -local _={} -_[12]={} -_[11]={} -_[10]={tags=_[12],text="5"} -_[9]={tags=_[12],text="in interrupt: "} -_[8]={tags=_[11],text="2"} -_[7]={tags=_[11],text="before: "} -_[6]={_[9],_[10]} -_[5]={_[7],_[8]} -_[4]={"return"} -_[3]={"text",_[6]} -_[2]={"wait",0} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = <1>{}, - text = "before: " - }, { - tags =
, - text = "2" - } } } -{ "wait", 0 } -{ "text", { { - tags = <1>{}, - text = "in interrupt: " - }, { - tags =
, - text = "5" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/interrupt no callback.ans b/test/tests/interrupt no callback.ans deleted file mode 100644 index de97996..0000000 --- a/test/tests/interrupt no callback.ans +++ /dev/null @@ -1,18 +0,0 @@ -:$ bar - :var = 5 - - ~ var := 2 - - :$ leave - in interrupt: {var} - - before: {var} - - ~ interrupt() - - :! foo - checkpoint - - after: {var} - -~ bar diff --git a/test/tests/interrupt no callback.lua b/test/tests/interrupt no callback.lua deleted file mode 100644 index d1c20dd..0000000 --- a/test/tests/interrupt no callback.lua +++ /dev/null @@ -1,20 +0,0 @@ -local _={} -_[7]={} -_[6]={tags=_[7],text="2"} -_[5]={tags=_[7],text="before: "} -_[4]={_[5],_[6]} -_[3]={"return",""} -_[2]={"wait",0} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = <1>{}, - text = "before: " - }, { - tags =
, - text = "2" - } } } -{ "wait", 0 } -{ "return", "" } -]]-- \ No newline at end of file diff --git a/test/tests/lazy boolean operators.ans b/test/tests/lazy boolean operators.ans deleted file mode 100644 index ec1d138..0000000 --- a/test/tests/lazy boolean operators.ans +++ /dev/null @@ -1,23 +0,0 @@ -:$ a - a - @1 - -:$ b - b - @0 - -{a & b} = a b 0 - -{b & a} = b 0 - -{a & a} = a a 1 - -{b & b} = b 0 - -{a | b} = a 1 - -{b | a} = b a 1 - -{a | a} = a 1 - -{b | b} = b b 0 diff --git a/test/tests/lazy boolean operators.lua b/test/tests/lazy boolean operators.lua deleted file mode 100644 index faee7ea..0000000 --- a/test/tests/lazy boolean operators.lua +++ /dev/null @@ -1,162 +0,0 @@ -local _={} -_[65]={} -_[64]={} -_[63]={} -_[62]={} -_[61]={} -_[60]={} -_[59]={} -_[58]={} -_[57]={} -_[56]={} -_[55]={} -_[54]={} -_[53]={} -_[52]={} -_[51]={} -_[50]={} -_[49]={} -_[48]={} -_[47]={} -_[46]={} -_[45]={tags=_[65],text=" = b b 0"} -_[44]={tags=_[65],text="0"} -_[43]={tags=_[64],text="b"} -_[42]={tags=_[63],text="b"} -_[41]={tags=_[62],text=" = a 1"} -_[40]={tags=_[62],text="1"} -_[39]={tags=_[61],text="a"} -_[38]={tags=_[60],text=" = b a 1"} -_[37]={tags=_[60],text="1"} -_[36]={tags=_[59],text="a"} -_[35]={tags=_[58],text="b"} -_[34]={tags=_[57],text=" = a 1"} -_[33]={tags=_[57],text="1"} -_[32]={tags=_[56],text="a"} -_[31]={tags=_[55],text=" = b 0"} -_[30]={tags=_[55],text="0"} -_[29]={tags=_[54],text="b"} -_[28]={tags=_[53],text=" = a a 1"} -_[27]={tags=_[53],text="1"} -_[26]={tags=_[52],text="a"} -_[25]={tags=_[51],text="a"} -_[24]={tags=_[50],text=" = b 0"} -_[23]={tags=_[50],text="0"} -_[22]={tags=_[49],text="b"} -_[21]={tags=_[48],text=" = a b 0"} -_[20]={tags=_[48],text="0"} -_[19]={tags=_[47],text="b"} -_[18]={tags=_[46],text="a"} -_[17]={_[42],_[43],_[44],_[45]} -_[16]={_[39],_[40],_[41]} -_[15]={_[35],_[36],_[37],_[38]} -_[14]={_[32],_[33],_[34]} -_[13]={_[29],_[30],_[31]} -_[12]={_[25],_[26],_[27],_[28]} -_[11]={_[22],_[23],_[24]} -_[10]={_[18],_[19],_[20],_[21]} -_[9]={"return"} -_[8]={"text",_[17]} -_[7]={"text",_[16]} -_[6]={"text",_[15]} -_[5]={"text",_[14]} -_[4]={"text",_[13]} -_[3]={"text",_[12]} -_[2]={"text",_[11]} -_[1]={"text",_[10]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9]} ---[[ -{ "text", { { - tags = {}, - text = "a" - }, { - tags = {}, - text = "b" - }, { - tags = <1>{}, - text = "0" - }, { - tags =
, - text = " = a b 0" - } } } -{ "text", { { - tags = {}, - text = "b" - }, { - tags = <1>{}, - text = "0" - }, { - tags =
, - text = " = b 0" - } } } -{ "text", { { - tags = {}, - text = "a" - }, { - tags = {}, - text = "a" - }, { - tags = <1>{}, - text = "1" - }, { - tags =
, - text = " = a a 1" - } } } -{ "text", { { - tags = {}, - text = "b" - }, { - tags = <1>{}, - text = "0" - }, { - tags =
, - text = " = b 0" - } } } -{ "text", { { - tags = {}, - text = "a" - }, { - tags = <1>{}, - text = "1" - }, { - tags =
, - text = " = a 1" - } } } -{ "text", { { - tags = {}, - text = "b" - }, { - tags = {}, - text = "a" - }, { - tags = <1>{}, - text = "1" - }, { - tags =
, - text = " = b a 1" - } } } -{ "text", { { - tags = {}, - text = "a" - }, { - tags = <1>{}, - text = "1" - }, { - tags =
, - text = " = a 1" - } } } -{ "text", { { - tags = {}, - text = "b" - }, { - tags = {}, - text = "b" - }, { - tags = <1>{}, - text = "0" - }, { - tags =
, - text = " = b b 0" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/list assignement.ans b/test/tests/list assignement.ans deleted file mode 100644 index ea5f2a0..0000000 --- a/test/tests/list assignement.ans +++ /dev/null @@ -1,21 +0,0 @@ -:x = [1,2] - -{x} - -{x(1) := 3} - -{x} - -{x(2) := 5} - -{x} - -{x(-1) := 12} - -{x} - -{x(3) := 99} - -{x} - -{x(5) := 0} diff --git a/test/tests/list assignement.lua b/test/tests/list assignement.lua deleted file mode 100644 index 0350235..0000000 --- a/test/tests/list assignement.lua +++ /dev/null @@ -1,78 +0,0 @@ -local _={} -_[37]={} -_[36]={} -_[35]={} -_[34]={} -_[33]={} -_[32]={} -_[31]={} -_[30]={} -_[29]={} -_[28]={tags=_[37],text="[3, 12, 99]"} -_[27]={tags=_[36],text="99"} -_[26]={tags=_[35],text="[3, 12]"} -_[25]={tags=_[34],text="12"} -_[24]={tags=_[33],text="[3, 5]"} -_[23]={tags=_[32],text="5"} -_[22]={tags=_[31],text="[3, 2]"} -_[21]={tags=_[30],text="3"} -_[20]={tags=_[29],text="[1, 2]"} -_[19]={_[28]} -_[18]={_[27]} -_[17]={_[26]} -_[16]={_[25]} -_[15]={_[24]} -_[14]={_[23]} -_[13]={_[22]} -_[12]={_[21]} -_[11]={_[20]} -_[10]={"error","list assignment index out of bounds; in Lua function \"()\"; at test/tests/list assignement.ans:21"} -_[9]={"text",_[19]} -_[8]={"text",_[18]} -_[7]={"text",_[17]} -_[6]={"text",_[16]} -_[5]={"text",_[15]} -_[4]={"text",_[14]} -_[3]={"text",_[13]} -_[2]={"text",_[12]} -_[1]={"text",_[11]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10]} ---[[ -{ "text", { { - tags = {}, - text = "[1, 2]" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "text", { { - tags = {}, - text = "[3, 2]" - } } } -{ "text", { { - tags = {}, - text = "5" - } } } -{ "text", { { - tags = {}, - text = "[3, 5]" - } } } -{ "text", { { - tags = {}, - text = "12" - } } } -{ "text", { { - tags = {}, - text = "[3, 12]" - } } } -{ "text", { { - tags = {}, - text = "99" - } } } -{ "text", { { - tags = {}, - text = "[3, 12, 99]" - } } } -{ "error", 'list assignment index out of bounds; in Lua function "()"; at test/tests/list assignement.ans:21' } -]]-- \ No newline at end of file diff --git a/test/tests/list index.ans b/test/tests/list index.ans deleted file mode 100644 index 8b3f4a9..0000000 --- a/test/tests/list index.ans +++ /dev/null @@ -1,11 +0,0 @@ -:x = [1,2,3] - -{x} - -{x(1)} == {x(-3)} - -{x(2)} == {x(-2)} - -{x(3)} == {x(-1)} - -{x(-4)} diff --git a/test/tests/list index.lua b/test/tests/list index.lua deleted file mode 100644 index 4b4139f..0000000 --- a/test/tests/list index.lua +++ /dev/null @@ -1,68 +0,0 @@ -local _={} -_[29]={} -_[28]={} -_[27]={} -_[26]={} -_[25]={} -_[24]={} -_[23]={} -_[22]={} -_[21]={} -_[20]={} -_[19]={text="3",tags=_[29]} -_[18]={text=" == ",tags=_[28]} -_[17]={text="3",tags=_[27]} -_[16]={text="2",tags=_[26]} -_[15]={text=" == ",tags=_[25]} -_[14]={text="2",tags=_[24]} -_[13]={text="1",tags=_[23]} -_[12]={text=" == ",tags=_[22]} -_[11]={text="1",tags=_[21]} -_[10]={text="[1, 2, 3]",tags=_[20]} -_[9]={_[17],_[18],_[19]} -_[8]={_[14],_[15],_[16]} -_[7]={_[11],_[12],_[13]} -_[6]={_[10]} -_[5]={"error","list index out of bounds; in Lua function \"()\"; at test/tests/list index.ans:11"} -_[4]={"text",_[9]} -_[3]={"text",_[8]} -_[2]={"text",_[7]} -_[1]={"text",_[6]} -return {_[1],_[2],_[3],_[4],_[5]} ---[[ -{ "text", { { - tags = {}, - text = "[1, 2, 3]" - } } } -{ "text", { { - tags = {}, - text = "1" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "2" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "3" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "3" - } } } -{ "error", 'list index out of bounds; in Lua function "()"; at test/tests/list index.ans:11' } -]]-- \ No newline at end of file diff --git a/test/tests/loop decorator.ans b/test/tests/loop decorator.ans deleted file mode 100644 index c932b8a..0000000 --- a/test/tests/loop decorator.ans +++ /dev/null @@ -1,5 +0,0 @@ -:i = 0 - -{i}\n~? (i += 1; i <= 10) - -{i} diff --git a/test/tests/loop decorator.lua b/test/tests/loop decorator.lua deleted file mode 100644 index 6309837..0000000 --- a/test/tests/loop decorator.lua +++ /dev/null @@ -1,117 +0,0 @@ -local _={} -_[47]={} -_[46]={} -_[45]={} -_[44]={} -_[43]={} -_[42]={} -_[41]={} -_[40]={} -_[39]={} -_[38]={} -_[37]={} -_[36]={} -_[35]={} -_[34]={} -_[33]={} -_[32]={} -_[31]={} -_[30]={} -_[29]={} -_[28]={} -_[27]={} -_[26]={text="11",tags=_[47]} -_[25]={text="\n",tags=_[46]} -_[24]={text="10",tags=_[45]} -_[23]={text="\n",tags=_[44]} -_[22]={text="9",tags=_[43]} -_[21]={text="\n",tags=_[42]} -_[20]={text="8",tags=_[41]} -_[19]={text="\n",tags=_[40]} -_[18]={text="7",tags=_[39]} -_[17]={text="\n",tags=_[38]} -_[16]={text="6",tags=_[37]} -_[15]={text="\n",tags=_[36]} -_[14]={text="5",tags=_[35]} -_[13]={text="\n",tags=_[34]} -_[12]={text="4",tags=_[33]} -_[11]={text="\n",tags=_[32]} -_[10]={text="3",tags=_[31]} -_[9]={text="\n",tags=_[30]} -_[8]={text="2",tags=_[29]} -_[7]={text="\n",tags=_[28]} -_[6]={text="1",tags=_[27]} -_[5]={_[26]} -_[4]={_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15],_[16],_[17],_[18],_[19],_[20],_[21],_[22],_[23],_[24],_[25]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "1" - }, { - tags = {}, - text = "\n" - }, { - tags = {}, - text = "2" - }, { - tags = {}, - text = "\n" - }, { - tags = {}, - text = "3" - }, { - tags = {}, - text = "\n" - }, { - tags = {}, - text = "4" - }, { - tags = {}, - text = "\n" - }, { - tags = {}, - text = "5" - }, { - tags = {}, - text = "\n" - }, { - tags = {}, - text = "6" - }, { - tags = {}, - text = "\n" - }, { - tags = {}, - text = "7" - }, { - tags = {}, - text = "\n" - }, { - tags = {}, - text = "8" - }, { - tags = {}, - text = "\n" - }, { - tags = {}, - text = "9" - }, { - tags = {}, - text = "\n" - }, { - tags = {}, - text = "10" - }, { - tags = {}, - text = "\n" - } } } -{ "text", { { - tags = {}, - text = "11" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/map assignement.ans b/test/tests/map assignement.ans deleted file mode 100644 index 12b3ef7..0000000 --- a/test/tests/map assignement.ans +++ /dev/null @@ -1,20 +0,0 @@ -:x = {1,2} - -{x} - -{x(1) := 3} - -{x} - -{x("foo") := "a"} - -{x} - -{x("bar") := "b"} - -{x} - -{x("foo") := "c"} - -{x} - diff --git a/test/tests/map assignement.lua b/test/tests/map assignement.lua deleted file mode 100644 index d28ce6e..0000000 --- a/test/tests/map assignement.lua +++ /dev/null @@ -1,78 +0,0 @@ -local _={} -_[37]={} -_[36]={} -_[35]={} -_[34]={} -_[33]={} -_[32]={} -_[31]={} -_[30]={} -_[29]={} -_[28]={text="{1=3, 2=2, bar=b, foo=c}",tags=_[37]} -_[27]={text="c",tags=_[36]} -_[26]={text="{1=3, 2=2, bar=b, foo=a}",tags=_[35]} -_[25]={text="b",tags=_[34]} -_[24]={text="{1=3, 2=2, foo=a}",tags=_[33]} -_[23]={text="a",tags=_[32]} -_[22]={text="{1=3, 2=2}",tags=_[31]} -_[21]={text="3",tags=_[30]} -_[20]={text="{1=1, 2=2}",tags=_[29]} -_[19]={_[28]} -_[18]={_[27]} -_[17]={_[26]} -_[16]={_[25]} -_[15]={_[24]} -_[14]={_[23]} -_[13]={_[22]} -_[12]={_[21]} -_[11]={_[20]} -_[10]={"return"} -_[9]={"text",_[19]} -_[8]={"text",_[18]} -_[7]={"text",_[17]} -_[6]={"text",_[16]} -_[5]={"text",_[15]} -_[4]={"text",_[14]} -_[3]={"text",_[13]} -_[2]={"text",_[12]} -_[1]={"text",_[11]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10]} ---[[ -{ "text", { { - tags = {}, - text = "{1=1, 2=2}" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "text", { { - tags = {}, - text = "{1=3, 2=2}" - } } } -{ "text", { { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "{1=3, 2=2, foo=a}" - } } } -{ "text", { { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "{1=3, 2=2, bar=b, foo=a}" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "{1=3, 2=2, bar=b, foo=c}" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/map index accross checkpoints.ans b/test/tests/map index accross checkpoints.ans deleted file mode 100644 index 29961fd..0000000 --- a/test/tests/map index accross checkpoints.ans +++ /dev/null @@ -1,29 +0,0 @@ -:x = {4} - -x={x} - -:a = {1,2,3,(x)=4} - -:c = a - -1={a==c} - -a(x)={a(x)} - -:! ch a - -a(x)={a(x)} - -:! ch b - -~ x(2) := 3 - -a(x)={a(x)} - -:! ch c - -a(x)={a(x)} - -~ x:={4} - -no={a(x)} diff --git a/test/tests/map index accross checkpoints.lua b/test/tests/map index accross checkpoints.lua deleted file mode 100644 index a3596bc..0000000 --- a/test/tests/map index accross checkpoints.lua +++ /dev/null @@ -1,92 +0,0 @@ -local _={} -_[41]={} -_[40]={} -_[39]={} -_[38]={} -_[37]={} -_[36]={} -_[35]={} -_[34]={} -_[33]={} -_[32]={} -_[31]={} -_[30]={} -_[29]={} -_[28]={tags=_[41],text="no="} -_[27]={tags=_[40],text="4"} -_[26]={tags=_[39],text="a(x)="} -_[25]={tags=_[38],text="4"} -_[24]={tags=_[37],text="a(x)="} -_[23]={tags=_[36],text="4"} -_[22]={tags=_[35],text="a(x)="} -_[21]={tags=_[34],text="4"} -_[20]={tags=_[33],text="a(x)="} -_[19]={tags=_[32],text="1"} -_[18]={tags=_[31],text="1="} -_[17]={tags=_[30],text="{1=4}"} -_[16]={tags=_[29],text="x="} -_[15]={_[28]} -_[14]={_[26],_[27]} -_[13]={_[24],_[25]} -_[12]={_[22],_[23]} -_[11]={_[20],_[21]} -_[10]={_[18],_[19]} -_[9]={_[16],_[17]} -_[8]={"return"} -_[7]={"text",_[15]} -_[6]={"text",_[14]} -_[5]={"text",_[13]} -_[4]={"text",_[12]} -_[3]={"text",_[11]} -_[2]={"text",_[10]} -_[1]={"text",_[9]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8]} ---[[ -{ "text", { { - tags = {}, - text = "x=" - }, { - tags = {}, - text = "{1=4}" - } } } -{ "text", { { - tags = {}, - text = "1=" - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "a(x)=" - }, { - tags = {}, - text = "4" - } } } -{ "text", { { - tags = {}, - text = "a(x)=" - }, { - tags = {}, - text = "4" - } } } -{ "text", { { - tags = {}, - text = "a(x)=" - }, { - tags = {}, - text = "4" - } } } -{ "text", { { - tags = {}, - text = "a(x)=" - }, { - tags = {}, - text = "4" - } } } -{ "text", { { - tags = {}, - text = "no=" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/map index.ans b/test/tests/map index.ans deleted file mode 100644 index c2caa83..0000000 --- a/test/tests/map index.ans +++ /dev/null @@ -1,7 +0,0 @@ -:t = {ahah=23,k=23,12} - -{t} == \{3=12, ahah=23, k=23} - -{t(3)} == 12 - -{t("ahah")} == 23 diff --git a/test/tests/map index.lua b/test/tests/map index.lua deleted file mode 100644 index 48f28f8..0000000 --- a/test/tests/map index.lua +++ /dev/null @@ -1,45 +0,0 @@ -local _={} -_[19]={} -_[18]={} -_[17]={} -_[16]={} -_[15]={} -_[14]={} -_[13]={tags=_[19],text=" == 23"} -_[12]={tags=_[18],text="23"} -_[11]={tags=_[17],text=" == 12"} -_[10]={tags=_[16],text="12"} -_[9]={tags=_[15],text=" == {3=12, ahah=23, k=23}"} -_[8]={tags=_[14],text="{3=12, ahah=23, k=23}"} -_[7]={_[12],_[13]} -_[6]={_[10],_[11]} -_[5]={_[8],_[9]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "{3=12, ahah=23, k=23}" - }, { - tags = {}, - text = " == {3=12, ahah=23, k=23}" - } } } -{ "text", { { - tags = {}, - text = "12" - }, { - tags = {}, - text = " == 12" - } } } -{ "text", { { - tags = {}, - text = "23" - }, { - tags = {}, - text = " == 23" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/merge nested mutable bis.ans b/test/tests/merge nested mutable bis.ans deleted file mode 100644 index 6503334..0000000 --- a/test/tests/merge nested mutable bis.ans +++ /dev/null @@ -1,17 +0,0 @@ -:post run = "check" - -:a = [1] -:b = [2] - -~ a!insert(b) - -:! c - -~ b!insert(3) - -:! d - -~ b!insert(4) - -:$ check - \[1,\[2,3,4]]: {a} diff --git a/test/tests/merge nested mutable bis.lua b/test/tests/merge nested mutable bis.lua deleted file mode 100644 index e5ac112..0000000 --- a/test/tests/merge nested mutable bis.lua +++ /dev/null @@ -1,21 +0,0 @@ -local _={} -_[8]={} -_[7]={} -_[6]={text="[1, [2, 3, 4]]",tags=_[8]} -_[5]={text="[1,[2,3,4]]: ",tags=_[7]} -_[4]={_[5],_[6]} -_[3]={"return"} -_[2]={"text",_[4]} -_[1]={"return"} -return {_[1],_[2],_[3]} ---[[ -{ "return" } -{ "text", { { - tags = {}, - text = "[1,[2,3,4] ]: " - }, { - tags = {}, - text = "[1, [2, 3, 4] ]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/merge nested mutable error bis.ans b/test/tests/merge nested mutable error bis.ans deleted file mode 100644 index 24a95ef..0000000 --- a/test/tests/merge nested mutable error bis.ans +++ /dev/null @@ -1,19 +0,0 @@ -:post run = "check" - -:a = [1] -:b = [2] - -~ a!insert(b) - -:! c - -~ b!insert(3) - -:! d - -~ b!insert(4) - -~ error("abort") - -:$ check - \[1,\[2,3]]: {a} diff --git a/test/tests/merge nested mutable error bis.lua b/test/tests/merge nested mutable error bis.lua deleted file mode 100644 index eb95ba6..0000000 --- a/test/tests/merge nested mutable error bis.lua +++ /dev/null @@ -1,21 +0,0 @@ -local _={} -_[8]={} -_[7]={} -_[6]={text="[1, [2, 3]]",tags=_[8]} -_[5]={text="[1,[2,3]]: ",tags=_[7]} -_[4]={_[5],_[6]} -_[3]={"return"} -_[2]={"text",_[4]} -_[1]={"error","abort; in Lua function \"error\"; at test/tests/merge nested mutable error bis.ans:16"} -return {_[1],_[2],_[3]} ---[[ -{ "error", 'abort; in Lua function "error"; at test/tests/merge nested mutable error bis.ans:16' } -{ "text", { { - tags = {}, - text = "[1,[2,3] ]: " - }, { - tags = {}, - text = "[1, [2, 3] ]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/merge nested mutable error.ans b/test/tests/merge nested mutable error.ans deleted file mode 100644 index 973168c..0000000 --- a/test/tests/merge nested mutable error.ans +++ /dev/null @@ -1,19 +0,0 @@ -:post run = "check" - -:a = [1] -:b = [2] - -~ a!insert(b) - -:! c - -~ b!insert(3) - -:! d - -~ a!insert(4) - -~ error("abort") - -:$ check - \[1,\[2,3]]: {a} diff --git a/test/tests/merge nested mutable error.lua b/test/tests/merge nested mutable error.lua deleted file mode 100644 index 21a94d3..0000000 --- a/test/tests/merge nested mutable error.lua +++ /dev/null @@ -1,21 +0,0 @@ -local _={} -_[8]={} -_[7]={} -_[6]={text="[1, [2, 3]]",tags=_[8]} -_[5]={text="[1,[2,3]]: ",tags=_[7]} -_[4]={_[5],_[6]} -_[3]={"return"} -_[2]={"text",_[4]} -_[1]={"error","abort; in Lua function \"error\"; at test/tests/merge nested mutable error.ans:16"} -return {_[1],_[2],_[3]} ---[[ -{ "error", 'abort; in Lua function "error"; at test/tests/merge nested mutable error.ans:16' } -{ "text", { { - tags = {}, - text = "[1,[2,3] ]: " - }, { - tags = {}, - text = "[1, [2, 3] ]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/merge nested mutable.ans b/test/tests/merge nested mutable.ans deleted file mode 100644 index 2efcba6..0000000 --- a/test/tests/merge nested mutable.ans +++ /dev/null @@ -1,17 +0,0 @@ -:post run = "check" - -:a = [1] -:b = [2] - -~ a!insert(b) - -:! c - -~ b!insert(3) - -:! d - -~ a!insert(4) - -:$ check - \[1,\[2,3],4]: {a} diff --git a/test/tests/merge nested mutable.lua b/test/tests/merge nested mutable.lua deleted file mode 100644 index 641a55a..0000000 --- a/test/tests/merge nested mutable.lua +++ /dev/null @@ -1,21 +0,0 @@ -local _={} -_[8]={} -_[7]={} -_[6]={text="[1, [2, 3], 4]",tags=_[8]} -_[5]={text="[1,[2,3],4]: ",tags=_[7]} -_[4]={_[5],_[6]} -_[3]={"return"} -_[2]={"text",_[4]} -_[1]={"return"} -return {_[1],_[2],_[3]} ---[[ -{ "return" } -{ "text", { { - tags = {}, - text = "[1,[2,3],4]: " - }, { - tags = {}, - text = "[1, [2, 3], 4]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/named arguments.ans b/test/tests/named arguments.ans deleted file mode 100644 index ee0ef59..0000000 --- a/test/tests/named arguments.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ f(a, b, c) - @a + b + c - -{f("a", "b", "c")} = {f(a="a", b="b", c="c")} = {f(c="c", a="a", b="b")} diff --git a/test/tests/named arguments.lua b/test/tests/named arguments.lua deleted file mode 100644 index 6b5010f..0000000 --- a/test/tests/named arguments.lua +++ /dev/null @@ -1,30 +0,0 @@ -local _={} -_[9]={} -_[8]={tags=_[9],text="abc"} -_[7]={tags=_[9],text=" = "} -_[6]={tags=_[9],text="abc"} -_[5]={tags=_[9],text=" = "} -_[4]={tags=_[9],text="abc"} -_[3]={_[4],_[5],_[6],_[7],_[8]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "abc" - }, { - tags =
, - text = " = " - }, { - tags =
, - text = "abc" - }, { - tags =
, - text = " = " - }, { - tags =
, - text = "abc" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/named varag.ans b/test/tests/named varag.ans deleted file mode 100644 index 02bd0c0..0000000 --- a/test/tests/named varag.ans +++ /dev/null @@ -1,10 +0,0 @@ -:$ f(l...) - ~ l!len - :a = 0 - ~ a := l(1) - ~ l!remove(1) - @a + f(l=l) - ~~ - @0 - -{f(1,2,3,4,5)} diff --git a/test/tests/named varag.lua b/test/tests/named varag.lua deleted file mode 100644 index 339f3e3..0000000 --- a/test/tests/named varag.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="15"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "15" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/namespace operator arbitrary expression.ans b/test/tests/namespace operator arbitrary expression.ans deleted file mode 100644 index 9503464..0000000 --- a/test/tests/namespace operator arbitrary expression.ans +++ /dev/null @@ -1,6 +0,0 @@ -:$ f - :x = 5 - -:a = &f - -{a.("x")} diff --git a/test/tests/namespace operator arbitrary expression.lua b/test/tests/namespace operator arbitrary expression.lua deleted file mode 100644 index 917ecac..0000000 --- a/test/tests/namespace operator arbitrary expression.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={text="5",tags=_[5]} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "5" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/nested conditions.ans b/test/tests/nested conditions.ans deleted file mode 100644 index f9fd285..0000000 --- a/test/tests/nested conditions.ans +++ /dev/null @@ -1,19 +0,0 @@ -~ 1 - yes - ~ 0 - no -~~ - nope - ~ 1 - hai - ~ 0 - still no -~~ - nein - -~ 0 - nah -~~ - ye - ~~ - da diff --git a/test/tests/nested conditions.lua b/test/tests/nested conditions.lua deleted file mode 100644 index 56ca0b5..0000000 --- a/test/tests/nested conditions.lua +++ /dev/null @@ -1,27 +0,0 @@ -local _={} -_[11]={} -_[10]={} -_[9]={} -_[8]={tags=_[11],text="da"} -_[7]={tags=_[10],text="ye"} -_[6]={tags=_[9],text="yes"} -_[5]={_[7],_[8]} -_[4]={_[6]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "yes" - } } } -{ "text", { { - tags = {}, - text = "ye" - }, { - tags = {}, - text = "da" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/nested flush.ans b/test/tests/nested flush.ans deleted file mode 100644 index 31e94e4..0000000 --- a/test/tests/nested flush.ans +++ /dev/null @@ -1,19 +0,0 @@ -~ 1 - a - -b - -~ 1 - c -d - -~ 1 - e - -> f -~ choose(1) - -~ 1 - g -> h -~ choose(1) diff --git a/test/tests/nested flush.lua b/test/tests/nested flush.lua deleted file mode 100644 index 098fb60..0000000 --- a/test/tests/nested flush.lua +++ /dev/null @@ -1,69 +0,0 @@ -local _={} -_[33]={} -_[32]={} -_[31]={tags=_[33],text="h"} -_[30]={} -_[29]={tags=_[32],text="f"} -_[28]={} -_[27]={} -_[26]={} -_[25]={} -_[24]={} -_[23]={_[31]} -_[22]={tags=_[30],text="g"} -_[21]={_[29]} -_[20]={tags=_[28],text="e"} -_[19]={tags=_[27],text="d"} -_[18]={tags=_[26],text="c"} -_[17]={tags=_[25],text="b"} -_[16]={tags=_[24],text="a"} -_[15]={_[23]} -_[14]={_[22]} -_[13]={_[21]} -_[12]={_[20]} -_[11]={_[18],_[19]} -_[10]={_[17]} -_[9]={_[16]} -_[8]={"return"} -_[7]={"choice",_[15]} -_[6]={"text",_[14]} -_[5]={"choice",_[13]} -_[4]={"text",_[12]} -_[3]={"text",_[11]} -_[2]={"text",_[10]} -_[1]={"text",_[9]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8]} ---[[ -{ "text", { { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "c" - }, { - tags = {}, - text = "d" - } } } -{ "text", { { - tags = {}, - text = "e" - } } } -{ "choice", { { { - tags = {}, - text = "f" - } } } } -{ "text", { { - tags = {}, - text = "g" - } } } -{ "choice", { { { - tags = {}, - text = "h" - } } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/object comparaison.ans b/test/tests/object comparaison.ans deleted file mode 100644 index 7551505..0000000 --- a/test/tests/object comparaison.ans +++ /dev/null @@ -1,23 +0,0 @@ -:% class - :a:b = "foo" - :c = "bar" - -:o = class -:a = class -::b = class - -0 = {o == a} -0 = {o == b} -1 = {o!constant == b} - -~ o.b := "haha" - -0 = {o!constant == b} - -~ a.b := "haha" - -1 = {o!constant == a!constant} - -~ o.b := "foo" - -0 = {o!constant == b} diff --git a/test/tests/object comparaison.lua b/test/tests/object comparaison.lua deleted file mode 100644 index 94b32f7..0000000 --- a/test/tests/object comparaison.lua +++ /dev/null @@ -1,78 +0,0 @@ -local _={} -_[33]={} -_[32]={} -_[31]={} -_[30]={} -_[29]={} -_[28]={} -_[27]={} -_[26]={} -_[25]={} -_[24]={} -_[23]={} -_[22]={} -_[21]={tags=_[33],text="0"} -_[20]={tags=_[32],text="0 = "} -_[19]={tags=_[31],text="1"} -_[18]={tags=_[30],text="1 = "} -_[17]={tags=_[29],text="0"} -_[16]={tags=_[28],text="0 = "} -_[15]={tags=_[27],text="1"} -_[14]={tags=_[26],text="1 = "} -_[13]={tags=_[25],text="0"} -_[12]={tags=_[24],text="0 = "} -_[11]={tags=_[23],text="0"} -_[10]={tags=_[22],text="0 = "} -_[9]={_[20],_[21]} -_[8]={_[18],_[19]} -_[7]={_[16],_[17]} -_[6]={_[10],_[11],_[12],_[13],_[14],_[15]} -_[5]={"return"} -_[4]={"text",_[9]} -_[3]={"text",_[8]} -_[2]={"text",_[7]} -_[1]={"text",_[6]} -return {_[1],_[2],_[3],_[4],_[5]} ---[[ -{ "text", { { - tags = {}, - text = "0 = " - }, { - tags = {}, - text = "0" - }, { - tags = {}, - text = "0 = " - }, { - tags = {}, - text = "0" - }, { - tags = {}, - text = "1 = " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "0 = " - }, { - tags = {}, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "1 = " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "0 = " - }, { - tags = {}, - text = "0" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/object constructor.ans b/test/tests/object constructor.ans deleted file mode 100644 index c3b5fa4..0000000 --- a/test/tests/object constructor.ans +++ /dev/null @@ -1,15 +0,0 @@ -:% class - :a:b = "foo" - :c = "bar" - -:$ new(o::&class, x) - ~ o.c := x - @o - -:o = class -:p = class!new("hoho") - -{o}, {p} - -{o.c} == {class.c} -{p.c} != {class.c} diff --git a/test/tests/object constructor.lua b/test/tests/object constructor.lua deleted file mode 100644 index 669eb7c..0000000 --- a/test/tests/object constructor.lua +++ /dev/null @@ -1,57 +0,0 @@ -local _={} -_[23]={} -_[22]={} -_[21]={} -_[20]={} -_[19]={} -_[18]={} -_[17]={} -_[16]={} -_[15]={} -_[14]={tags=_[23],text="bar"} -_[13]={tags=_[22],text=" != "} -_[12]={tags=_[21],text="hoho"} -_[11]={tags=_[20],text="bar"} -_[10]={tags=_[19],text=" == "} -_[9]={tags=_[18],text="bar"} -_[8]={tags=_[17],text="%object constructor.class(c=hoho)::&object constructor.class"} -_[7]={tags=_[16],text=", "} -_[6]={tags=_[15],text="%object constructor.class::&object constructor.class"} -_[5]={_[9],_[10],_[11],_[12],_[13],_[14]} -_[4]={_[6],_[7],_[8]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "%object constructor.class::&object constructor.class" - }, { - tags = {}, - text = ", " - }, { - tags = {}, - text = "%object constructor.class(c=hoho)::&object constructor.class" - } } } -{ "text", { { - tags = {}, - text = "bar" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "bar" - }, { - tags = {}, - text = "hoho" - }, { - tags = {}, - text = " != " - }, { - tags = {}, - text = "bar" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/object several.ans b/test/tests/object several.ans deleted file mode 100644 index a72231b..0000000 --- a/test/tests/object several.ans +++ /dev/null @@ -1,26 +0,0 @@ -:% class - :a:b = "foo" - :c = "bar" - -:o = class -:p = class - -{o}, {p} - -{o.a} == {class.a} -{o.b} == {class.b} - -{p.a} == {class.a} -{p.b} == {class.b} - -~ o.b := "haha" - -{o.a} != {class.a} -{o.b} != {class.b} - -{p.a} == {class.a} -{p.b} == {class.b} - -{o.c} == {class.c} - -{p.c} == {class.c} diff --git a/test/tests/object several.lua b/test/tests/object several.lua deleted file mode 100644 index bbe8ba5..0000000 --- a/test/tests/object several.lua +++ /dev/null @@ -1,192 +0,0 @@ -local _={} -_[81]={} -_[80]={} -_[79]={} -_[78]={} -_[77]={} -_[76]={} -_[75]={} -_[74]={} -_[73]={} -_[72]={} -_[71]={} -_[70]={} -_[69]={} -_[68]={} -_[67]={} -_[66]={} -_[65]={} -_[64]={} -_[63]={} -_[62]={} -_[61]={} -_[60]={} -_[59]={} -_[58]={} -_[57]={} -_[56]={} -_[55]={} -_[54]={} -_[53]={} -_[52]={} -_[51]={} -_[50]={} -_[49]={} -_[48]={tags=_[81],text="bar"} -_[47]={tags=_[80],text=" == "} -_[46]={tags=_[79],text="bar"} -_[45]={tags=_[78],text="bar"} -_[44]={tags=_[77],text=" == "} -_[43]={tags=_[76],text="bar"} -_[42]={tags=_[75],text="foo"} -_[41]={tags=_[74],text=" == "} -_[40]={tags=_[73],text="foo"} -_[39]={tags=_[72],text="foo"} -_[38]={tags=_[71],text=" == "} -_[37]={tags=_[70],text="foo"} -_[36]={tags=_[69],text="foo"} -_[35]={tags=_[68],text=" != "} -_[34]={tags=_[67],text="haha"} -_[33]={tags=_[66],text="foo"} -_[32]={tags=_[65],text=" != "} -_[31]={tags=_[64],text="haha"} -_[30]={tags=_[63],text="foo"} -_[29]={tags=_[62],text=" == "} -_[28]={tags=_[61],text="foo"} -_[27]={tags=_[60],text="foo"} -_[26]={tags=_[59],text=" == "} -_[25]={tags=_[58],text="foo"} -_[24]={tags=_[57],text="foo"} -_[23]={tags=_[56],text=" == "} -_[22]={tags=_[55],text="foo"} -_[21]={tags=_[54],text="foo"} -_[20]={tags=_[53],text=" == "} -_[19]={tags=_[52],text="foo"} -_[18]={tags=_[51],text="%object several.class::&object several.class"} -_[17]={tags=_[50],text=", "} -_[16]={tags=_[49],text="%object several.class::&object several.class"} -_[15]={_[46],_[47],_[48]} -_[14]={_[43],_[44],_[45]} -_[13]={_[37],_[38],_[39],_[40],_[41],_[42]} -_[12]={_[31],_[32],_[33],_[34],_[35],_[36]} -_[11]={_[25],_[26],_[27],_[28],_[29],_[30]} -_[10]={_[19],_[20],_[21],_[22],_[23],_[24]} -_[9]={_[16],_[17],_[18]} -_[8]={"return"} -_[7]={"text",_[15]} -_[6]={"text",_[14]} -_[5]={"text",_[13]} -_[4]={"text",_[12]} -_[3]={"text",_[11]} -_[2]={"text",_[10]} -_[1]={"text",_[9]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8]} ---[[ -{ "text", { { - tags = {}, - text = "%object several.class::&object several.class" - }, { - tags = {}, - text = ", " - }, { - tags = {}, - text = "%object several.class::&object several.class" - } } } -{ "text", { { - tags = {}, - text = "foo" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "foo" - }, { - tags = {}, - text = "foo" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "foo" - } } } -{ "text", { { - tags = {}, - text = "foo" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "foo" - }, { - tags = {}, - text = "foo" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "foo" - } } } -{ "text", { { - tags = {}, - text = "haha" - }, { - tags = {}, - text = " != " - }, { - tags = {}, - text = "foo" - }, { - tags = {}, - text = "haha" - }, { - tags = {}, - text = " != " - }, { - tags = {}, - text = "foo" - } } } -{ "text", { { - tags = {}, - text = "foo" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "foo" - }, { - tags = {}, - text = "foo" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "foo" - } } } -{ "text", { { - tags = {}, - text = "bar" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "bar" - } } } -{ "text", { { - tags = {}, - text = "bar" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "bar" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/object simple.ans b/test/tests/object simple.ans deleted file mode 100644 index 081a3c2..0000000 --- a/test/tests/object simple.ans +++ /dev/null @@ -1,17 +0,0 @@ -:% class - :a:b = "foo" - :c = "bar" - -:o = class - -{o} - -{o.a} == {class.a} -{o.b} == {class.b} - -~ o.b := "haha" - -{o.a} != {class.a} -{o.b} != {class.b} - -{o.c} == {class.c} diff --git a/test/tests/object simple.lua b/test/tests/object simple.lua deleted file mode 100644 index 7da5fb8..0000000 --- a/test/tests/object simple.lua +++ /dev/null @@ -1,98 +0,0 @@ -local _={} -_[41]={} -_[40]={} -_[39]={} -_[38]={} -_[37]={} -_[36]={} -_[35]={} -_[34]={} -_[33]={} -_[32]={} -_[31]={} -_[30]={} -_[29]={} -_[28]={} -_[27]={} -_[26]={} -_[25]={tags=_[41],text="bar"} -_[24]={tags=_[40],text=" == "} -_[23]={tags=_[39],text="bar"} -_[22]={tags=_[38],text="foo"} -_[21]={tags=_[37],text=" != "} -_[20]={tags=_[36],text="haha"} -_[19]={tags=_[35],text="foo"} -_[18]={tags=_[34],text=" != "} -_[17]={tags=_[33],text="haha"} -_[16]={tags=_[32],text="foo"} -_[15]={tags=_[31],text=" == "} -_[14]={tags=_[30],text="foo"} -_[13]={tags=_[29],text="foo"} -_[12]={tags=_[28],text=" == "} -_[11]={tags=_[27],text="foo"} -_[10]={tags=_[26],text="%object simple.class::&object simple.class"} -_[9]={_[23],_[24],_[25]} -_[8]={_[17],_[18],_[19],_[20],_[21],_[22]} -_[7]={_[11],_[12],_[13],_[14],_[15],_[16]} -_[6]={_[10]} -_[5]={"return"} -_[4]={"text",_[9]} -_[3]={"text",_[8]} -_[2]={"text",_[7]} -_[1]={"text",_[6]} -return {_[1],_[2],_[3],_[4],_[5]} ---[[ -{ "text", { { - tags = {}, - text = "%object simple.class::&object simple.class" - } } } -{ "text", { { - tags = {}, - text = "foo" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "foo" - }, { - tags = {}, - text = "foo" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "foo" - } } } -{ "text", { { - tags = {}, - text = "haha" - }, { - tags = {}, - text = " != " - }, { - tags = {}, - text = "foo" - }, { - tags = {}, - text = "haha" - }, { - tags = {}, - text = " != " - }, { - tags = {}, - text = "foo" - } } } -{ "text", { { - tags = {}, - text = "bar" - }, { - tags = {}, - text = " == " - }, { - tags = {}, - text = "bar" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/optional arguments.ans b/test/tests/optional arguments.ans deleted file mode 100644 index 4dde1c0..0000000 --- a/test/tests/optional arguments.ans +++ /dev/null @@ -1,4 +0,0 @@ -:$ f(a, b, c="c") - @a + b + c - -{f("a", "b")} = {f("a", "b", "c")} = {f(b="b", a="a")} diff --git a/test/tests/optional arguments.lua b/test/tests/optional arguments.lua deleted file mode 100644 index 6b5010f..0000000 --- a/test/tests/optional arguments.lua +++ /dev/null @@ -1,30 +0,0 @@ -local _={} -_[9]={} -_[8]={tags=_[9],text="abc"} -_[7]={tags=_[9],text=" = "} -_[6]={tags=_[9],text="abc"} -_[5]={tags=_[9],text=" = "} -_[4]={tags=_[9],text="abc"} -_[3]={_[4],_[5],_[6],_[7],_[8]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "abc" - }, { - tags =
, - text = " = " - }, { - tags =
, - text = "abc" - }, { - tags =
, - text = " = " - }, { - tags =
, - text = "abc" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/pair operator.ans b/test/tests/pair operator.ans deleted file mode 100644 index 68cc034..0000000 --- a/test/tests/pair operator.ans +++ /dev/null @@ -1,5 +0,0 @@ -:name = "test" - -{[name=1, 3="p", (name)="foo", "ho"="ah", name=name]} - -{["name":1, 3:"p", name:"foo", "ho":"ah", "name":name]} diff --git a/test/tests/pair operator.lua b/test/tests/pair operator.lua deleted file mode 100644 index 4b2af01..0000000 --- a/test/tests/pair operator.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[9]={} -_[8]={} -_[7]={tags=_[9],text="[name=1, 3=p, test=foo, ho=ah, name=test]"} -_[6]={tags=_[8],text="[name=1, 3=p, test=foo, ho=ah, name=test]"} -_[5]={_[7]} -_[4]={_[6]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "[name=1, 3=p, test=foo, ho=ah, name=test]" - } } } -{ "text", { { - tags = {}, - text = "[name=1, 3=p, test=foo, ho=ah, name=test]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/paragraph alias.ans b/test/tests/paragraph alias.ans deleted file mode 100644 index 6dafa7d..0000000 --- a/test/tests/paragraph alias.ans +++ /dev/null @@ -1,4 +0,0 @@ -:! f : test - @"ok" - -{f} = {test} diff --git a/test/tests/paragraph alias.lua b/test/tests/paragraph alias.lua deleted file mode 100644 index c88d990..0000000 --- a/test/tests/paragraph alias.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[7]={} -_[6]={tags=_[7],text="ok"} -_[5]={tags=_[7],text=" = "} -_[4]={tags=_[7],text="ok"} -_[3]={_[4],_[5],_[6]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "ok" - }, { - tags =
, - text = " = " - }, { - tags =
, - text = "ok" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/paragraph run force.ans b/test/tests/paragraph run force.ans deleted file mode 100644 index de2106c..0000000 --- a/test/tests/paragraph run force.ans +++ /dev/null @@ -1,14 +0,0 @@ -:$ f - x - :! p - a - b - -Force run checkpoint: -~ f.p() - -From checkpoint: -~ f - -Force no checkpoint: -~ f() diff --git a/test/tests/paragraph run force.lua b/test/tests/paragraph run force.lua deleted file mode 100644 index f8b432b..0000000 --- a/test/tests/paragraph run force.lua +++ /dev/null @@ -1,55 +0,0 @@ -local _={} -_[23]={} -_[22]={} -_[21]={} -_[20]={} -_[19]={} -_[18]={} -_[17]={} -_[16]={} -_[15]={tags=_[23],text="b"} -_[14]={tags=_[22],text="x"} -_[13]={tags=_[21],text="Force no checkpoint:"} -_[12]={tags=_[20],text="b"} -_[11]={tags=_[19],text="a"} -_[10]={tags=_[18],text="From checkpoint:"} -_[9]={tags=_[17],text="a"} -_[8]={tags=_[16],text="Force run checkpoint:"} -_[7]={_[13],_[14],_[15]} -_[6]={_[10],_[11],_[12]} -_[5]={_[8],_[9]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "Force run checkpoint:" - }, { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "From checkpoint:" - }, { - tags = {}, - text = "a" - }, { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "Force no checkpoint:" - }, { - tags = {}, - text = "x" - }, { - tags = {}, - text = "b" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/paragraph run from.ans b/test/tests/paragraph run from.ans deleted file mode 100644 index 72cc7a8..0000000 --- a/test/tests/paragraph run from.ans +++ /dev/null @@ -1,14 +0,0 @@ -:$ f - x - :! p - a - b - -Force run from checkpoint: -~ f.p - -From checkpoint: -~ f - -Force no checkpoint: -~ f() diff --git a/test/tests/paragraph run from.lua b/test/tests/paragraph run from.lua deleted file mode 100644 index 9a38458..0000000 --- a/test/tests/paragraph run from.lua +++ /dev/null @@ -1,60 +0,0 @@ -local _={} -_[25]={} -_[24]={} -_[23]={} -_[22]={} -_[21]={} -_[20]={} -_[19]={} -_[18]={} -_[17]={} -_[16]={tags=_[25],text="b"} -_[15]={tags=_[24],text="x"} -_[14]={tags=_[23],text="Force no checkpoint:"} -_[13]={tags=_[22],text="b"} -_[12]={tags=_[21],text="a"} -_[11]={tags=_[20],text="From checkpoint:"} -_[10]={tags=_[19],text="b"} -_[9]={tags=_[18],text="a"} -_[8]={tags=_[17],text="Force run from checkpoint:"} -_[7]={_[14],_[15],_[16]} -_[6]={_[11],_[12],_[13]} -_[5]={_[8],_[9],_[10]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "Force run from checkpoint:" - }, { - tags = {}, - text = "a" - }, { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "From checkpoint:" - }, { - tags = {}, - text = "a" - }, { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "Force no checkpoint:" - }, { - tags = {}, - text = "x" - }, { - tags = {}, - text = "b" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/paragraph run.ans b/test/tests/paragraph run.ans deleted file mode 100644 index cc68f38..0000000 --- a/test/tests/paragraph run.ans +++ /dev/null @@ -1,14 +0,0 @@ -:$ f - x - :! p - a - b - -No checkpoint: -~ f - -From checkpoint: -~ f - -Force no checkpoint: -~ f() diff --git a/test/tests/paragraph run.lua b/test/tests/paragraph run.lua deleted file mode 100644 index e74ba4a..0000000 --- a/test/tests/paragraph run.lua +++ /dev/null @@ -1,60 +0,0 @@ -local _={} -_[25]={} -_[24]={} -_[23]={} -_[22]={} -_[21]={} -_[20]={} -_[19]={} -_[18]={} -_[17]={} -_[16]={tags=_[25],text="b"} -_[15]={tags=_[24],text="x"} -_[14]={tags=_[23],text="Force no checkpoint:"} -_[13]={tags=_[22],text="b"} -_[12]={tags=_[21],text="a"} -_[11]={tags=_[20],text="From checkpoint:"} -_[10]={tags=_[19],text="b"} -_[9]={tags=_[18],text="x"} -_[8]={tags=_[17],text="No checkpoint:"} -_[7]={_[14],_[15],_[16]} -_[6]={_[11],_[12],_[13]} -_[5]={_[8],_[9],_[10]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "No checkpoint:" - }, { - tags = {}, - text = "x" - }, { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "From checkpoint:" - }, { - tags = {}, - text = "a" - }, { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "Force no checkpoint:" - }, { - tags = {}, - text = "x" - }, { - tags = {}, - text = "b" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/paragraph.ans b/test/tests/paragraph.ans deleted file mode 100644 index 8808a29..0000000 --- a/test/tests/paragraph.ans +++ /dev/null @@ -1,3 +0,0 @@ -:! p - a -b \ No newline at end of file diff --git a/test/tests/paragraph.lua b/test/tests/paragraph.lua deleted file mode 100644 index da0e7fa..0000000 --- a/test/tests/paragraph.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="b"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "b" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/remove duplicate spaces.ans b/test/tests/remove duplicate spaces.ans deleted file mode 100644 index f1e7aca..0000000 --- a/test/tests/remove duplicate spaces.ans +++ /dev/null @@ -1,3 +0,0 @@ -a -b -{" "}c diff --git a/test/tests/remove duplicate spaces.lua b/test/tests/remove duplicate spaces.lua deleted file mode 100644 index 34066f7..0000000 --- a/test/tests/remove duplicate spaces.lua +++ /dev/null @@ -1,28 +0,0 @@ -local _={} -_[10]={} -_[9]={} -_[8]={} -_[7]={text="c",tags=_[10]} -_[6]={text="",tags=_[10]} -_[5]={text="b ",tags=_[9]} -_[4]={text="a ",tags=_[8]} -_[3]={_[4],_[5],_[6],_[7]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "a " - }, { - tags = {}, - text = "b " - }, { - tags = <1>{}, - text = "" - }, { - tags =
, - text = "c" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/remove trailing spaces.ans b/test/tests/remove trailing spaces.ans deleted file mode 100644 index 3698665..0000000 --- a/test/tests/remove trailing spaces.ans +++ /dev/null @@ -1,5 +0,0 @@ -a - -b - -{" "}c diff --git a/test/tests/remove trailing spaces.lua b/test/tests/remove trailing spaces.lua deleted file mode 100644 index 64be7a2..0000000 --- a/test/tests/remove trailing spaces.lua +++ /dev/null @@ -1,34 +0,0 @@ -local _={} -_[14]={} -_[13]={} -_[12]={} -_[11]={tags=_[14],text="c"} -_[10]={tags=_[14],text=" "} -_[9]={tags=_[13],text="b"} -_[8]={tags=_[12],text="a"} -_[7]={_[10],_[11]} -_[6]={_[9]} -_[5]={_[8]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = <1>{}, - text = " " - }, { - tags =
, - text = "c" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/resume from nested paragraph.ans b/test/tests/resume from nested paragraph.ans deleted file mode 100644 index f2fad80..0000000 --- a/test/tests/resume from nested paragraph.ans +++ /dev/null @@ -1,26 +0,0 @@ -:$ f - x - :! p - a - - :! q - b - - c - - d - -From start: -~ f - -From p checkpoint: -~ f - -From q checkpoint: -~ f - -From q checkpoint again: -~ f - -Force p checkpoint: -~ f.p() diff --git a/test/tests/resume from nested paragraph.lua b/test/tests/resume from nested paragraph.lua deleted file mode 100644 index b3de8e6..0000000 --- a/test/tests/resume from nested paragraph.lua +++ /dev/null @@ -1,135 +0,0 @@ -local _={} -_[63]={} -_[62]={} -_[61]={} -_[60]={} -_[59]={} -_[58]={} -_[57]={} -_[56]={} -_[55]={} -_[54]={} -_[53]={} -_[52]={} -_[51]={} -_[50]={} -_[49]={} -_[48]={} -_[47]={} -_[46]={} -_[45]={tags=_[63],text="c"} -_[44]={tags=_[62],text="a"} -_[43]={tags=_[61],text="Force p checkpoint:"} -_[42]={tags=_[60],text="d"} -_[41]={tags=_[59],text="c"} -_[40]={tags=_[58],text="b"} -_[39]={tags=_[57],text="From q checkpoint again:"} -_[38]={tags=_[56],text="d"} -_[37]={tags=_[55],text="c"} -_[36]={tags=_[54],text="b"} -_[35]={tags=_[53],text="From q checkpoint:"} -_[34]={tags=_[52],text="d"} -_[33]={tags=_[51],text="c"} -_[32]={tags=_[50],text="a"} -_[31]={tags=_[49],text="From p checkpoint:"} -_[30]={tags=_[48],text="d"} -_[29]={tags=_[47],text="x"} -_[28]={tags=_[46],text="From start:"} -_[27]={_[45]} -_[26]={_[43],_[44]} -_[25]={_[42]} -_[24]={_[41]} -_[23]={_[39],_[40]} -_[22]={_[38]} -_[21]={_[37]} -_[20]={_[35],_[36]} -_[19]={_[34]} -_[18]={_[33]} -_[17]={_[31],_[32]} -_[16]={_[30]} -_[15]={_[28],_[29]} -_[14]={"return"} -_[13]={"text",_[27]} -_[12]={"text",_[26]} -_[11]={"text",_[25]} -_[10]={"text",_[24]} -_[9]={"text",_[23]} -_[8]={"text",_[22]} -_[7]={"text",_[21]} -_[6]={"text",_[20]} -_[5]={"text",_[19]} -_[4]={"text",_[18]} -_[3]={"text",_[17]} -_[2]={"text",_[16]} -_[1]={"text",_[15]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14]} ---[[ -{ "text", { { - tags = {}, - text = "From start:" - }, { - tags = {}, - text = "x" - } } } -{ "text", { { - tags = {}, - text = "d" - } } } -{ "text", { { - tags = {}, - text = "From p checkpoint:" - }, { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "d" - } } } -{ "text", { { - tags = {}, - text = "From q checkpoint:" - }, { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "d" - } } } -{ "text", { { - tags = {}, - text = "From q checkpoint again:" - }, { - tags = {}, - text = "b" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "text", { { - tags = {}, - text = "d" - } } } -{ "text", { { - tags = {}, - text = "Force p checkpoint:" - }, { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "c" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/resume from paragraph restore tags.ans b/test/tests/resume from paragraph restore tags.ans deleted file mode 100644 index 06d3965..0000000 --- a/test/tests/resume from paragraph restore tags.ans +++ /dev/null @@ -1,17 +0,0 @@ -:$ f - # "a"="a" - a - ~ 1 # "x"="x" - # "b"="b" - :! p - b # "c"="c" - - c - - d - - e - -~ f - -~ f diff --git a/test/tests/resume from paragraph restore tags.lua b/test/tests/resume from paragraph restore tags.lua deleted file mode 100644 index 65570e3..0000000 --- a/test/tests/resume from paragraph restore tags.lua +++ /dev/null @@ -1,85 +0,0 @@ -local _={} -_[32]={} -_[31]={a="a"} -_[30]={a="a",b="b"} -_[29]={a="a",c="c",b="b"} -_[28]={} -_[27]={a="a",b="b"} -_[26]={a="a"} -_[25]={tags=_[32],text="e"} -_[24]={tags=_[31],text="d"} -_[23]={tags=_[30],text="c"} -_[22]={tags=_[29],text="b"} -_[21]={tags=_[28],text="e"} -_[20]={tags=_[26],text="d"} -_[19]={tags=_[27],text="c"} -_[18]={tags=_[26],text="a"} -_[17]={_[25]} -_[16]={_[24]} -_[15]={_[23]} -_[14]={_[22]} -_[13]={_[21]} -_[12]={_[20]} -_[11]={_[19]} -_[10]={_[18]} -_[9]={"return"} -_[8]={"text",_[17]} -_[7]={"text",_[16]} -_[6]={"text",_[15]} -_[5]={"text",_[14]} -_[4]={"text",_[13]} -_[3]={"text",_[12]} -_[2]={"text",_[11]} -_[1]={"text",_[10]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9]} ---[[ -{ "text", { { - tags = { - a = "a" - }, - text = "a" - } } } -{ "text", { { - tags = { - a = "a", - b = "b" - }, - text = "c" - } } } -{ "text", { { - tags = { - a = "a" - }, - text = "d" - } } } -{ "text", { { - tags = {}, - text = "e" - } } } -{ "text", { { - tags = { - a = "a", - b = "b", - c = "c" - }, - text = "b" - } } } -{ "text", { { - tags = { - a = "a", - b = "b" - }, - text = "c" - } } } -{ "text", { { - tags = { - a = "a" - }, - text = "d" - } } } -{ "text", { { - tags = {}, - text = "e" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/resume from paragraph with nested choice.ans b/test/tests/resume from paragraph with nested choice.ans deleted file mode 100644 index 98014c2..0000000 --- a/test/tests/resume from paragraph with nested choice.ans +++ /dev/null @@ -1,77 +0,0 @@ -:$ f - > a - -> a - :! p - > aa - -> aa - > ab - -> ab - > b - -> b - ~ choose(2) - - > c - -> c - ~ choose(1) - -~ f - -~ f.p - -:$ g - > a - -> a - :! p - > aa - -> aa - > ab - -> ab - > b - -> b - ~ choose(2) - autoflush - > c - -> c - ~ choose(1) - -~ g - -~ g.p - -:$ h - ~ 1 - > a - -> a - :! p - > aa - -> aa - > ab - -> ab - ~ choose(1) - > b - -> b - > c - -> c - ~ choose(1) - -~ h - -~ h.p - -:$ i - > a - -> a - :! p - > aa - -> aa - > ab - -> ab - > b - -> b - ~ 1 - > c - -> c - -~ i - -~ i.p diff --git a/test/tests/resume from paragraph with nested choice.lua b/test/tests/resume from paragraph with nested choice.lua deleted file mode 100644 index 937f343..0000000 --- a/test/tests/resume from paragraph with nested choice.lua +++ /dev/null @@ -1,268 +0,0 @@ -local _={} -_[130]={} -_[129]={} -_[128]={} -_[127]={} -_[126]={} -_[125]={} -_[124]={} -_[123]={} -_[122]={} -_[121]={} -_[120]={text="c",tags=_[130]} -_[119]={text="b",tags=_[129]} -_[118]={text="a",tags=_[128]} -_[117]={} -_[116]={text="ab",tags=_[127]} -_[115]={text="aa",tags=_[117]} -_[114]={text="ab"} -_[113]={text="aa"} -_[112]={} -_[111]={text="c",tags=_[126]} -_[110]={text="b",tags=_[125]} -_[109]={text="a",tags=_[112]} -_[108]={} -_[107]={text="c",tags=_[108]} -_[106]={} -_[105]={} -_[104]={text="ab",tags=_[105]} -_[103]={text="aa",tags=_[124]} -_[102]={} -_[101]={text="c",tags=_[102]} -_[100]={} -_[99]={} -_[98]={text="b",tags=_[99]} -_[97]={text="a",tags=_[123]} -_[96]={} -_[95]={text="c",tags=_[96]} -_[94]={} -_[93]={text="ab",tags=_[94]} -_[92]={text="aa",tags=_[122]} -_[91]={} -_[90]={text="c",tags=_[91]} -_[89]={} -_[88]={text="b",tags=_[89]} -_[87]={text="a",tags=_[121]} -_[86]={_[120]} -_[85]={_[119]} -_[84]={_[118]} -_[83]={text="-> aa",tags=_[117]} -_[82]={_[116]} -_[81]={_[115]} -_[80]={text="-> aa",tags=_[112]} -_[79]={_[114]} -_[78]={_[113]} -_[77]={text="-> a",tags=_[112]} -_[76]={_[111]} -_[75]={_[110]} -_[74]={_[109]} -_[73]={text="-> c",tags=_[108]} -_[72]={_[107]} -_[71]={text="autoflush",tags=_[106]} -_[70]={text="-> ab",tags=_[105]} -_[69]={_[104]} -_[68]={_[103]} -_[67]={text="-> c",tags=_[102]} -_[66]={_[101]} -_[65]={text="autoflush",tags=_[100]} -_[64]={text="-> b",tags=_[99]} -_[63]={_[98]} -_[62]={_[97]} -_[61]={text="-> c",tags=_[96]} -_[60]={_[95]} -_[59]={text="-> ab",tags=_[94]} -_[58]={_[93]} -_[57]={_[92]} -_[56]={text="-> c",tags=_[91]} -_[55]={_[90]} -_[54]={text="-> b",tags=_[89]} -_[53]={_[88]} -_[52]={_[87]} -_[51]={_[84],_[85],_[86]} -_[50]={_[83]} -_[49]={_[81],_[82]} -_[48]={_[80]} -_[47]={_[78],_[79]} -_[46]={_[77]} -_[45]={_[74],_[75],_[76]} -_[44]={_[73]} -_[43]={_[72]} -_[42]={_[71]} -_[41]={_[70]} -_[40]={_[68],_[69]} -_[39]={_[67]} -_[38]={_[66]} -_[37]={_[65]} -_[36]={_[64]} -_[35]={_[62],_[63]} -_[34]={_[61]} -_[33]={_[60]} -_[32]={_[59]} -_[31]={_[57],_[58]} -_[30]={_[56]} -_[29]={_[55]} -_[28]={_[54]} -_[27]={_[52],_[53]} -_[26]={"error","invalid choice; in event flush at test/tests/resume from paragraph with nested choice.ans:76"} -_[25]={"choice",_[51]} -_[24]={"text",_[50]} -_[23]={"choice",_[49]} -_[22]={"text",_[48]} -_[21]={"choice",_[47]} -_[20]={"text",_[46]} -_[19]={"choice",_[45]} -_[18]={"text",_[44]} -_[17]={"choice",_[43]} -_[16]={"text",_[42]} -_[15]={"text",_[41]} -_[14]={"choice",_[40]} -_[13]={"text",_[39]} -_[12]={"choice",_[38]} -_[11]={"text",_[37]} -_[10]={"text",_[36]} -_[9]={"choice",_[35]} -_[8]={"text",_[34]} -_[7]={"choice",_[33]} -_[6]={"text",_[32]} -_[5]={"choice",_[31]} -_[4]={"text",_[30]} -_[3]={"choice",_[29]} -_[2]={"text",_[28]} -_[1]={"choice",_[27]} -_[0]={_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15],_[16],_[17],_[18],_[19],_[20],_[21],_[22],_[23],_[24],_[25],_[26]} -_[113].tags=_[112] -_[114].tags=_[112] -return _[0] ---[[ -{ "choice", { { { - tags = {}, - text = "a" - } }, { { - tags = {}, - text = "b" - } } } } -{ "text", { { - tags = {}, - text = "-> b" - } } } -{ "choice", { { { - tags = {}, - text = "c" - } } } } -{ "text", { { - tags = {}, - text = "-> c" - } } } -{ "choice", { { { - tags = {}, - text = "aa" - } }, { { - tags = {}, - text = "ab" - } } } } -{ "text", { { - tags = {}, - text = "-> ab" - } } } -{ "choice", { { { - tags = {}, - text = "c" - } } } } -{ "text", { { - tags = {}, - text = "-> c" - } } } -{ "choice", { { { - tags = {}, - text = "a" - } }, { { - tags = {}, - text = "b" - } } } } -{ "text", { { - tags = {}, - text = "-> b" - } } } -{ "text", { { - tags = {}, - text = "autoflush" - } } } -{ "choice", { { { - tags = {}, - text = "c" - } } } } -{ "text", { { - tags = {}, - text = "-> c" - } } } -{ "choice", { { { - tags = {}, - text = "aa" - } }, { { - tags = {}, - text = "ab" - } } } } -{ "text", { { - tags = {}, - text = "-> ab" - } } } -{ "text", { { - tags = {}, - text = "autoflush" - } } } -{ "choice", { { { - tags = {}, - text = "c" - } } } } -{ "text", { { - tags = {}, - text = "-> c" - } } } -{ "choice", { { { - tags = {}, - text = "a" - } }, { { - tags = {}, - text = "b" - } }, { { - tags = {}, - text = "c" - } } } } -{ "text", { { - tags = {}, - text = "-> a" - } } } -{ "choice", { { { - tags = <1>{}, - text = "aa" - } }, { { - tags =
, - text = "ab" - } } } } -{ "text", { { - tags = {}, - text = "-> aa" - } } } -{ "choice", { { { - tags = {}, - text = "aa" - } }, { { - tags = {}, - text = "ab" - } } } } -{ "text", { { - tags = {}, - text = "-> aa" - } } } -{ "choice", { { { - tags = {}, - text = "a" - } }, { { - tags = {}, - text = "b" - } }, { { - tags = {}, - text = "c" - } } } } -{ "error", "invalid choice; in event flush at test/tests/resume from paragraph with nested choice.ans:76" } -]]-- \ No newline at end of file diff --git a/test/tests/resume from paragraph with nested condition.ans b/test/tests/resume from paragraph with nested condition.ans deleted file mode 100644 index 54d9c87..0000000 --- a/test/tests/resume from paragraph with nested condition.ans +++ /dev/null @@ -1,21 +0,0 @@ -:$ f - ~ 1 - :! p - x - ~~ - y - -~ f - -~ f.p - -:$ g - ~ 0 - :! p - x - ~~ - y - -~ g - -~ g.p diff --git a/test/tests/resume from paragraph with nested condition.lua b/test/tests/resume from paragraph with nested condition.lua deleted file mode 100644 index 70b7f88..0000000 --- a/test/tests/resume from paragraph with nested condition.lua +++ /dev/null @@ -1,38 +0,0 @@ -local _={} -_[17]={} -_[16]={} -_[15]={} -_[14]={} -_[13]={tags=_[17],text="x"} -_[12]={tags=_[16],text="y"} -_[11]={tags=_[15],text="x"} -_[10]={tags=_[14],text="x"} -_[9]={_[13]} -_[8]={_[12]} -_[7]={_[11]} -_[6]={_[10]} -_[5]={"return"} -_[4]={"text",_[9]} -_[3]={"text",_[8]} -_[2]={"text",_[7]} -_[1]={"text",_[6]} -return {_[1],_[2],_[3],_[4],_[5]} ---[[ -{ "text", { { - tags = {}, - text = "x" - } } } -{ "text", { { - tags = {}, - text = "x" - } } } -{ "text", { { - tags = {}, - text = "y" - } } } -{ "text", { { - tags = {}, - text = "x" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/return children.ans b/test/tests/return children.ans deleted file mode 100644 index 621bce1..0000000 --- a/test/tests/return children.ans +++ /dev/null @@ -1,13 +0,0 @@ -:$ fn - :i=0 - @i - ~ i:=5 - {i} - -{fn} = 50 - -:$ g - @0 - @3 - -{g} = 3 diff --git a/test/tests/return children.lua b/test/tests/return children.lua deleted file mode 100644 index b3ab770..0000000 --- a/test/tests/return children.lua +++ /dev/null @@ -1,37 +0,0 @@ -local _={} -_[15]={} -_[14]={} -_[13]={} -_[12]={} -_[11]={} -_[10]={text=" = 3",tags=_[15]} -_[9]={text="3",tags=_[14]} -_[8]={text=" = 50",tags=_[13]} -_[7]={text="0",tags=_[12]} -_[6]={text="5",tags=_[11]} -_[5]={_[9],_[10]} -_[4]={_[6],_[7],_[8]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "5" - }, { - tags = {}, - text = "0" - }, { - tags = {}, - text = " = 50" - } } } -{ "text", { { - tags = {}, - text = "3" - }, { - tags = {}, - text = " = 3" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/return in choice.ans b/test/tests/return in choice.ans deleted file mode 100644 index b75041c..0000000 --- a/test/tests/return in choice.ans +++ /dev/null @@ -1,10 +0,0 @@ -:$ f - > a - x - @1 - y - @2 - -~ f == 2 - ~ choose(1) - Yes. diff --git a/test/tests/return in choice.lua b/test/tests/return in choice.lua deleted file mode 100644 index 38b91af..0000000 --- a/test/tests/return in choice.lua +++ /dev/null @@ -1,30 +0,0 @@ -local _={} -_[13]={} -_[12]={} -_[11]={tags=_[12],text="a"} -_[10]={tags=_[13],text="Yes."} -_[9]={tags=_[12],text="x"} -_[8]={_[11]} -_[7]={_[10]} -_[6]={_[9]} -_[5]={_[8]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"choice",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "choice", { { { - tags = {}, - text = "a" - } } } } -{ "text", { { - tags = {}, - text = "x" - } } } -{ "text", { { - tags = {}, - text = "Yes." - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/scope checkpoint mutable bis error.ans b/test/tests/scope checkpoint mutable bis error.ans deleted file mode 100644 index da436ea..0000000 --- a/test/tests/scope checkpoint mutable bis error.ans +++ /dev/null @@ -1,49 +0,0 @@ -:post run = "check" - -:x = [99] - -:l = [1,x] - -:n = 0 - -:$ f(t) - ~ t!insert(len(l)+1) - - f1: {l} {t} - - CHECK - :! c - - ~ n < 1 - REC - - ~ n += 1 - ~ f(t) - - END REC - - f2: {l} - - CHECK 2 - :! d - - ~ t!insert(len(t)+1) - - ~ t(2)!insert(len(l)+1) - - ~ error("t") - - f3: {l} {t} - -~ f(l) - -FINAL - -l: {l} - -x: {x} - -:$ check - AFTER ERROR - - l: {l} \ No newline at end of file diff --git a/test/tests/scope checkpoint mutable bis error.lua b/test/tests/scope checkpoint mutable bis error.lua deleted file mode 100644 index 6353c8f..0000000 --- a/test/tests/scope checkpoint mutable bis error.lua +++ /dev/null @@ -1,120 +0,0 @@ -local _={} -_[54]={} -_[53]={} -_[52]={} -_[51]={} -_[50]={} -_[49]={} -_[48]={} -_[47]={} -_[46]={} -_[45]={} -_[44]={} -_[43]={} -_[42]={} -_[41]={} -_[40]={} -_[39]={} -_[38]={} -_[37]={text="[1, [99], 3, 4]",tags=_[54]} -_[36]={text="l: ",tags=_[53]} -_[35]={text="AFTER ERROR",tags=_[52]} -_[34]={text="CHECK 2",tags=_[51]} -_[33]={text="[1, [99], 3, 4]",tags=_[50]} -_[32]={text="f2: ",tags=_[49]} -_[31]={text="CHECK",tags=_[48]} -_[30]={text="[1, [99], 3, 4]",tags=_[47]} -_[29]={text=" ",tags=_[46]} -_[28]={text="[1, [99], 3, 4]",tags=_[45]} -_[27]={text="f1: ",tags=_[44]} -_[26]={text="REC",tags=_[43]} -_[25]={text="CHECK",tags=_[42]} -_[24]={text="[1, [99], 3]",tags=_[41]} -_[23]={text=" ",tags=_[40]} -_[22]={text="[1, [99], 3]",tags=_[39]} -_[21]={text="f1: ",tags=_[38]} -_[20]={_[36],_[37]} -_[19]={_[35]} -_[18]={_[34]} -_[17]={_[32],_[33]} -_[16]={_[31]} -_[15]={_[27],_[28],_[29],_[30]} -_[14]={_[26]} -_[13]={_[25]} -_[12]={_[21],_[22],_[23],_[24]} -_[11]={"return"} -_[10]={"text",_[20]} -_[9]={"text",_[19]} -_[8]={"error","t; in Lua function \"error\"; at test/tests/scope checkpoint mutable bis error.ans:34; at test/tests/scope checkpoint mutable bis error.ans:21; at test/tests/scope checkpoint mutable bis error.ans:38"} -_[7]={"text",_[18]} -_[6]={"text",_[17]} -_[5]={"text",_[16]} -_[4]={"text",_[15]} -_[3]={"text",_[14]} -_[2]={"text",_[13]} -_[1]={"text",_[12]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11]} ---[[ -{ "text", { { - tags = {}, - text = "f1: " - }, { - tags = {}, - text = "[1, [99], 3]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, [99], 3]" - } } } -{ "text", { { - tags = {}, - text = "CHECK" - } } } -{ "text", { { - tags = {}, - text = "REC" - } } } -{ "text", { { - tags = {}, - text = "f1: " - }, { - tags = {}, - text = "[1, [99], 3, 4]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, [99], 3, 4]" - } } } -{ "text", { { - tags = {}, - text = "CHECK" - } } } -{ "text", { { - tags = {}, - text = "f2: " - }, { - tags = {}, - text = "[1, [99], 3, 4]" - } } } -{ "text", { { - tags = {}, - text = "CHECK 2" - } } } -{ "error", 't; in Lua function "error"; at test/tests/scope checkpoint mutable bis error.ans:34; at test/tests/scope checkpoint mutable bis error.ans:21; at test/tests/scope checkpoint mutable bis error.ans:38' } -{ "text", { { - tags = {}, - text = "AFTER ERROR" - } } } -{ "text", { { - tags = {}, - text = "l: " - }, { - tags = {}, - text = "[1, [99], 3, 4]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/scope checkpoint mutable bis.ans b/test/tests/scope checkpoint mutable bis.ans deleted file mode 100644 index 1476b84..0000000 --- a/test/tests/scope checkpoint mutable bis.ans +++ /dev/null @@ -1,40 +0,0 @@ -:x = [99] - -:l = [1,x] - -:n = 0 - -:$ f(t) - ~ t!insert(len(l)+1) - - f1: {l} {t} - - CHECK - :! c - - ~ n < 1 - REC - - ~ n += 1 - ~ f(t) - - END REC - - f2: {l} - - CHECK 2 - :! d - - ~ t!insert(len(t)+1) - - ~ t(2)!insert(len(l)+1) - - f3: {l} {t} - -~ f(l) - -FINAL - -l: {l} - -x: {x} diff --git a/test/tests/scope checkpoint mutable bis.lua b/test/tests/scope checkpoint mutable bis.lua deleted file mode 100644 index 7391703..0000000 --- a/test/tests/scope checkpoint mutable bis.lua +++ /dev/null @@ -1,206 +0,0 @@ -local _={} -_[93]={} -_[92]={} -_[91]={} -_[90]={} -_[89]={} -_[88]={} -_[87]={} -_[86]={} -_[85]={} -_[84]={} -_[83]={} -_[82]={} -_[81]={} -_[80]={} -_[79]={} -_[78]={} -_[77]={} -_[76]={} -_[75]={} -_[74]={} -_[73]={} -_[72]={} -_[71]={} -_[70]={} -_[69]={} -_[68]={} -_[67]={} -_[66]={} -_[65]={} -_[64]={} -_[63]={} -_[62]={tags=_[93],text="[99, 6, 7]"} -_[61]={tags=_[92],text="x: "} -_[60]={tags=_[91],text="[1, [99, 6, 7], 3, 4, 5, 6]"} -_[59]={tags=_[90],text="l: "} -_[58]={tags=_[89],text="FINAL"} -_[57]={tags=_[88],text="[1, [99, 6, 7], 3, 4, 5, 6]"} -_[56]={tags=_[87],text=" "} -_[55]={tags=_[86],text="[1, [99, 6, 7], 3, 4, 5, 6]"} -_[54]={tags=_[85],text="f3: "} -_[53]={tags=_[84],text="CHECK 2"} -_[52]={tags=_[83],text="[1, [99, 6], 3, 4, 5]"} -_[51]={tags=_[82],text="f2: "} -_[50]={tags=_[81],text="END REC"} -_[49]={tags=_[80],text="[1, [99, 6], 3, 4, 5]"} -_[48]={tags=_[79],text=" "} -_[47]={tags=_[78],text="[1, [99, 6], 3, 4, 5]"} -_[46]={tags=_[77],text="f3: "} -_[45]={tags=_[76],text="CHECK 2"} -_[44]={tags=_[75],text="[1, [99], 3, 4]"} -_[43]={tags=_[74],text="f2: "} -_[42]={tags=_[73],text="CHECK"} -_[41]={tags=_[72],text="[1, [99], 3, 4]"} -_[40]={tags=_[71],text=" "} -_[39]={tags=_[70],text="[1, [99], 3, 4]"} -_[38]={tags=_[69],text="f1: "} -_[37]={tags=_[68],text="REC"} -_[36]={tags=_[67],text="CHECK"} -_[35]={tags=_[66],text="[1, [99], 3]"} -_[34]={tags=_[65],text=" "} -_[33]={tags=_[64],text="[1, [99], 3]"} -_[32]={tags=_[63],text="f1: "} -_[31]={_[61],_[62]} -_[30]={_[59],_[60]} -_[29]={_[58]} -_[28]={_[54],_[55],_[56],_[57]} -_[27]={_[53]} -_[26]={_[51],_[52]} -_[25]={_[50]} -_[24]={_[46],_[47],_[48],_[49]} -_[23]={_[45]} -_[22]={_[43],_[44]} -_[21]={_[42]} -_[20]={_[38],_[39],_[40],_[41]} -_[19]={_[37]} -_[18]={_[36]} -_[17]={_[32],_[33],_[34],_[35]} -_[16]={"return"} -_[15]={"text",_[31]} -_[14]={"text",_[30]} -_[13]={"text",_[29]} -_[12]={"text",_[28]} -_[11]={"text",_[27]} -_[10]={"text",_[26]} -_[9]={"text",_[25]} -_[8]={"text",_[24]} -_[7]={"text",_[23]} -_[6]={"text",_[22]} -_[5]={"text",_[21]} -_[4]={"text",_[20]} -_[3]={"text",_[19]} -_[2]={"text",_[18]} -_[1]={"text",_[17]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15],_[16]} ---[[ -{ "text", { { - tags = {}, - text = "f1: " - }, { - tags = {}, - text = "[1, [99], 3]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, [99], 3]" - } } } -{ "text", { { - tags = {}, - text = "CHECK" - } } } -{ "text", { { - tags = {}, - text = "REC" - } } } -{ "text", { { - tags = {}, - text = "f1: " - }, { - tags = {}, - text = "[1, [99], 3, 4]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, [99], 3, 4]" - } } } -{ "text", { { - tags = {}, - text = "CHECK" - } } } -{ "text", { { - tags = {}, - text = "f2: " - }, { - tags = {}, - text = "[1, [99], 3, 4]" - } } } -{ "text", { { - tags = {}, - text = "CHECK 2" - } } } -{ "text", { { - tags = {}, - text = "f3: " - }, { - tags = {}, - text = "[1, [99, 6], 3, 4, 5]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, [99, 6], 3, 4, 5]" - } } } -{ "text", { { - tags = {}, - text = "END REC" - } } } -{ "text", { { - tags = {}, - text = "f2: " - }, { - tags = {}, - text = "[1, [99, 6], 3, 4, 5]" - } } } -{ "text", { { - tags = {}, - text = "CHECK 2" - } } } -{ "text", { { - tags = {}, - text = "f3: " - }, { - tags = {}, - text = "[1, [99, 6, 7], 3, 4, 5, 6]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, [99, 6, 7], 3, 4, 5, 6]" - } } } -{ "text", { { - tags = {}, - text = "FINAL" - } } } -{ "text", { { - tags = {}, - text = "l: " - }, { - tags = {}, - text = "[1, [99, 6, 7], 3, 4, 5, 6]" - } } } -{ "text", { { - tags = {}, - text = "x: " - }, { - tags = {}, - text = "[99, 6, 7]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/scope checkpoint mutable error.ans b/test/tests/scope checkpoint mutable error.ans deleted file mode 100644 index 1fb6c4e..0000000 --- a/test/tests/scope checkpoint mutable error.ans +++ /dev/null @@ -1,38 +0,0 @@ -:post run = "check" - -:l = [1] - -:n = 0 - -:$ f(t) - ~ t!insert(len(l)+1) - - f1: {l} {t} - - CHECK - :! c - - ~ n < 1 - REC - - ~ n += 1 - ~ f(t) - - END REC - - ~ t!insert(len(t)+1) - - ~ error("t") - - f2: {l} - -~ f(l) - -FINAL - -l: {l} - -:$ check - AFTER ERROR - - l: {l} \ No newline at end of file diff --git a/test/tests/scope checkpoint mutable error.lua b/test/tests/scope checkpoint mutable error.lua deleted file mode 100644 index 577ea2f..0000000 --- a/test/tests/scope checkpoint mutable error.lua +++ /dev/null @@ -1,99 +0,0 @@ -local _={} -_[44]={} -_[43]={} -_[42]={} -_[41]={} -_[40]={} -_[39]={} -_[38]={} -_[37]={} -_[36]={} -_[35]={} -_[34]={} -_[33]={} -_[32]={} -_[31]={} -_[30]={tags=_[44],text="[1, 2, 3]"} -_[29]={tags=_[43],text="l: "} -_[28]={tags=_[42],text="AFTER ERROR"} -_[27]={tags=_[41],text="CHECK"} -_[26]={tags=_[40],text="[1, 2, 3]"} -_[25]={tags=_[39],text=" "} -_[24]={tags=_[38],text="[1, 2, 3]"} -_[23]={tags=_[37],text="f1: "} -_[22]={tags=_[36],text="REC"} -_[21]={tags=_[35],text="CHECK"} -_[20]={tags=_[34],text="[1, 2]"} -_[19]={tags=_[33],text=" "} -_[18]={tags=_[32],text="[1, 2]"} -_[17]={tags=_[31],text="f1: "} -_[16]={_[29],_[30]} -_[15]={_[28]} -_[14]={_[27]} -_[13]={_[23],_[24],_[25],_[26]} -_[12]={_[22]} -_[11]={_[21]} -_[10]={_[17],_[18],_[19],_[20]} -_[9]={"return"} -_[8]={"text",_[16]} -_[7]={"text",_[15]} -_[6]={"error","t; in Lua function \"error\"; at test/tests/scope checkpoint mutable error.ans:25; at test/tests/scope checkpoint mutable error.ans:19; at test/tests/scope checkpoint mutable error.ans:29"} -_[5]={"text",_[14]} -_[4]={"text",_[13]} -_[3]={"text",_[12]} -_[2]={"text",_[11]} -_[1]={"text",_[10]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9]} ---[[ -{ "text", { { - tags = {}, - text = "f1: " - }, { - tags = {}, - text = "[1, 2]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, 2]" - } } } -{ "text", { { - tags = {}, - text = "CHECK" - } } } -{ "text", { { - tags = {}, - text = "REC" - } } } -{ "text", { { - tags = {}, - text = "f1: " - }, { - tags = {}, - text = "[1, 2, 3]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, 2, 3]" - } } } -{ "text", { { - tags = {}, - text = "CHECK" - } } } -{ "error", 't; in Lua function "error"; at test/tests/scope checkpoint mutable error.ans:25; at test/tests/scope checkpoint mutable error.ans:19; at test/tests/scope checkpoint mutable error.ans:29' } -{ "text", { { - tags = {}, - text = "AFTER ERROR" - } } } -{ "text", { { - tags = {}, - text = "l: " - }, { - tags = {}, - text = "[1, 2, 3]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/scope checkpoint mutable ter error.ans b/test/tests/scope checkpoint mutable ter error.ans deleted file mode 100644 index 560a7c3..0000000 --- a/test/tests/scope checkpoint mutable ter error.ans +++ /dev/null @@ -1,51 +0,0 @@ -:post run = "check" - -:x = [99] - -:l = [1,x] - -:n = 0 - -:$ f(t) - ~ t!insert(len(l)+1) - - f1: {l} {t} - - CHECK - :! c - - ~ n < 1 - REC - - ~ n += 1 - ~ f(t) - - END REC - - ~ x!insert(12) - - f2: {l} - - CHECK 2 - :! d - - ~ t!insert(len(t)+1) - - ~ t(2)!insert(len(l)+1) - - ~ error("t") - - f3: {l} {t} - -~ f(l) - -FINAL - -l: {l} - -x: {x} - -:$ check - AFTER ERROR - - l: {l} \ No newline at end of file diff --git a/test/tests/scope checkpoint mutable ter error.lua b/test/tests/scope checkpoint mutable ter error.lua deleted file mode 100644 index fe49431..0000000 --- a/test/tests/scope checkpoint mutable ter error.lua +++ /dev/null @@ -1,120 +0,0 @@ -local _={} -_[54]={} -_[53]={} -_[52]={} -_[51]={} -_[50]={} -_[49]={} -_[48]={} -_[47]={} -_[46]={} -_[45]={} -_[44]={} -_[43]={} -_[42]={} -_[41]={} -_[40]={} -_[39]={} -_[38]={} -_[37]={text="[1, [99, 12], 3, 4]",tags=_[54]} -_[36]={text="l: ",tags=_[53]} -_[35]={text="AFTER ERROR",tags=_[52]} -_[34]={text="CHECK 2",tags=_[51]} -_[33]={text="[1, [99, 12], 3, 4]",tags=_[50]} -_[32]={text="f2: ",tags=_[49]} -_[31]={text="CHECK",tags=_[48]} -_[30]={text="[1, [99], 3, 4]",tags=_[47]} -_[29]={text=" ",tags=_[46]} -_[28]={text="[1, [99], 3, 4]",tags=_[45]} -_[27]={text="f1: ",tags=_[44]} -_[26]={text="REC",tags=_[43]} -_[25]={text="CHECK",tags=_[42]} -_[24]={text="[1, [99], 3]",tags=_[41]} -_[23]={text=" ",tags=_[40]} -_[22]={text="[1, [99], 3]",tags=_[39]} -_[21]={text="f1: ",tags=_[38]} -_[20]={_[36],_[37]} -_[19]={_[35]} -_[18]={_[34]} -_[17]={_[32],_[33]} -_[16]={_[31]} -_[15]={_[27],_[28],_[29],_[30]} -_[14]={_[26]} -_[13]={_[25]} -_[12]={_[21],_[22],_[23],_[24]} -_[11]={"return"} -_[10]={"text",_[20]} -_[9]={"text",_[19]} -_[8]={"error","t; in Lua function \"error\"; at test/tests/scope checkpoint mutable ter error.ans:36; at test/tests/scope checkpoint mutable ter error.ans:21; at test/tests/scope checkpoint mutable ter error.ans:40"} -_[7]={"text",_[18]} -_[6]={"text",_[17]} -_[5]={"text",_[16]} -_[4]={"text",_[15]} -_[3]={"text",_[14]} -_[2]={"text",_[13]} -_[1]={"text",_[12]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11]} ---[[ -{ "text", { { - tags = {}, - text = "f1: " - }, { - tags = {}, - text = "[1, [99], 3]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, [99], 3]" - } } } -{ "text", { { - tags = {}, - text = "CHECK" - } } } -{ "text", { { - tags = {}, - text = "REC" - } } } -{ "text", { { - tags = {}, - text = "f1: " - }, { - tags = {}, - text = "[1, [99], 3, 4]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, [99], 3, 4]" - } } } -{ "text", { { - tags = {}, - text = "CHECK" - } } } -{ "text", { { - tags = {}, - text = "f2: " - }, { - tags = {}, - text = "[1, [99, 12], 3, 4]" - } } } -{ "text", { { - tags = {}, - text = "CHECK 2" - } } } -{ "error", 't; in Lua function "error"; at test/tests/scope checkpoint mutable ter error.ans:36; at test/tests/scope checkpoint mutable ter error.ans:21; at test/tests/scope checkpoint mutable ter error.ans:40' } -{ "text", { { - tags = {}, - text = "AFTER ERROR" - } } } -{ "text", { { - tags = {}, - text = "l: " - }, { - tags = {}, - text = "[1, [99, 12], 3, 4]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/scope checkpoint mutable ter.ans b/test/tests/scope checkpoint mutable ter.ans deleted file mode 100644 index 4ca598b..0000000 --- a/test/tests/scope checkpoint mutable ter.ans +++ /dev/null @@ -1,42 +0,0 @@ -:x = [99] - -:l = [1,x] - -:n = 0 - -:$ f(t) - ~ t!insert(len(l)+1) - - f1: {l} {t} - - CHECK - :! c - - ~ n < 1 - REC - - ~ n += 1 - ~ f(t) - - END REC - - ~ x!insert(12) - - f2: {l} - - CHECK 2 - :! d - - ~ t!insert(len(t)+1) - - ~ t(2)!insert(len(l)+1) - - f3: {l} {t} - -~ f(l) - -FINAL - -l: {l} - -x: {x} diff --git a/test/tests/scope checkpoint mutable ter.lua b/test/tests/scope checkpoint mutable ter.lua deleted file mode 100644 index 9ea5706..0000000 --- a/test/tests/scope checkpoint mutable ter.lua +++ /dev/null @@ -1,206 +0,0 @@ -local _={} -_[93]={} -_[92]={} -_[91]={} -_[90]={} -_[89]={} -_[88]={} -_[87]={} -_[86]={} -_[85]={} -_[84]={} -_[83]={} -_[82]={} -_[81]={} -_[80]={} -_[79]={} -_[78]={} -_[77]={} -_[76]={} -_[75]={} -_[74]={} -_[73]={} -_[72]={} -_[71]={} -_[70]={} -_[69]={} -_[68]={} -_[67]={} -_[66]={} -_[65]={} -_[64]={} -_[63]={} -_[62]={tags=_[93],text="[99, 12, 6, 12, 7]"} -_[61]={tags=_[92],text="x: "} -_[60]={tags=_[91],text="[1, [99, 12, 6, 12, 7], 3, 4, 5, 6]"} -_[59]={tags=_[90],text="l: "} -_[58]={tags=_[89],text="FINAL"} -_[57]={tags=_[88],text="[1, [99, 12, 6, 12, 7], 3, 4, 5, 6]"} -_[56]={tags=_[87],text=" "} -_[55]={tags=_[86],text="[1, [99, 12, 6, 12, 7], 3, 4, 5, 6]"} -_[54]={tags=_[85],text="f3: "} -_[53]={tags=_[84],text="CHECK 2"} -_[52]={tags=_[83],text="[1, [99, 12, 6, 12], 3, 4, 5]"} -_[51]={tags=_[82],text="f2: "} -_[50]={tags=_[81],text="END REC"} -_[49]={tags=_[80],text="[1, [99, 12, 6], 3, 4, 5]"} -_[48]={tags=_[79],text=" "} -_[47]={tags=_[78],text="[1, [99, 12, 6], 3, 4, 5]"} -_[46]={tags=_[77],text="f3: "} -_[45]={tags=_[76],text="CHECK 2"} -_[44]={tags=_[75],text="[1, [99, 12], 3, 4]"} -_[43]={tags=_[74],text="f2: "} -_[42]={tags=_[73],text="CHECK"} -_[41]={tags=_[72],text="[1, [99], 3, 4]"} -_[40]={tags=_[71],text=" "} -_[39]={tags=_[70],text="[1, [99], 3, 4]"} -_[38]={tags=_[69],text="f1: "} -_[37]={tags=_[68],text="REC"} -_[36]={tags=_[67],text="CHECK"} -_[35]={tags=_[66],text="[1, [99], 3]"} -_[34]={tags=_[65],text=" "} -_[33]={tags=_[64],text="[1, [99], 3]"} -_[32]={tags=_[63],text="f1: "} -_[31]={_[61],_[62]} -_[30]={_[59],_[60]} -_[29]={_[58]} -_[28]={_[54],_[55],_[56],_[57]} -_[27]={_[53]} -_[26]={_[51],_[52]} -_[25]={_[50]} -_[24]={_[46],_[47],_[48],_[49]} -_[23]={_[45]} -_[22]={_[43],_[44]} -_[21]={_[42]} -_[20]={_[38],_[39],_[40],_[41]} -_[19]={_[37]} -_[18]={_[36]} -_[17]={_[32],_[33],_[34],_[35]} -_[16]={"return"} -_[15]={"text",_[31]} -_[14]={"text",_[30]} -_[13]={"text",_[29]} -_[12]={"text",_[28]} -_[11]={"text",_[27]} -_[10]={"text",_[26]} -_[9]={"text",_[25]} -_[8]={"text",_[24]} -_[7]={"text",_[23]} -_[6]={"text",_[22]} -_[5]={"text",_[21]} -_[4]={"text",_[20]} -_[3]={"text",_[19]} -_[2]={"text",_[18]} -_[1]={"text",_[17]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15],_[16]} ---[[ -{ "text", { { - tags = {}, - text = "f1: " - }, { - tags = {}, - text = "[1, [99], 3]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, [99], 3]" - } } } -{ "text", { { - tags = {}, - text = "CHECK" - } } } -{ "text", { { - tags = {}, - text = "REC" - } } } -{ "text", { { - tags = {}, - text = "f1: " - }, { - tags = {}, - text = "[1, [99], 3, 4]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, [99], 3, 4]" - } } } -{ "text", { { - tags = {}, - text = "CHECK" - } } } -{ "text", { { - tags = {}, - text = "f2: " - }, { - tags = {}, - text = "[1, [99, 12], 3, 4]" - } } } -{ "text", { { - tags = {}, - text = "CHECK 2" - } } } -{ "text", { { - tags = {}, - text = "f3: " - }, { - tags = {}, - text = "[1, [99, 12, 6], 3, 4, 5]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, [99, 12, 6], 3, 4, 5]" - } } } -{ "text", { { - tags = {}, - text = "END REC" - } } } -{ "text", { { - tags = {}, - text = "f2: " - }, { - tags = {}, - text = "[1, [99, 12, 6, 12], 3, 4, 5]" - } } } -{ "text", { { - tags = {}, - text = "CHECK 2" - } } } -{ "text", { { - tags = {}, - text = "f3: " - }, { - tags = {}, - text = "[1, [99, 12, 6, 12, 7], 3, 4, 5, 6]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, [99, 12, 6, 12, 7], 3, 4, 5, 6]" - } } } -{ "text", { { - tags = {}, - text = "FINAL" - } } } -{ "text", { { - tags = {}, - text = "l: " - }, { - tags = {}, - text = "[1, [99, 12, 6, 12, 7], 3, 4, 5, 6]" - } } } -{ "text", { { - tags = {}, - text = "x: " - }, { - tags = {}, - text = "[99, 12, 6, 12, 7]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/scope checkpoint mutable.ans b/test/tests/scope checkpoint mutable.ans deleted file mode 100644 index dde76a8..0000000 --- a/test/tests/scope checkpoint mutable.ans +++ /dev/null @@ -1,29 +0,0 @@ -:l = [1] - -:n = 0 - -:$ f(t) - ~ t!insert(len(l)+1) - - f1: {l} {t} - - CHECK - :! c - - ~ n < 1 - REC - - ~ n += 1 - ~ f(t) - - END REC - - ~ t!insert(len(t)+1) - - f2: {l} - -~ f(l) - -FINAL - -l: {l} \ No newline at end of file diff --git a/test/tests/scope checkpoint mutable.lua b/test/tests/scope checkpoint mutable.lua deleted file mode 100644 index 687ac03..0000000 --- a/test/tests/scope checkpoint mutable.lua +++ /dev/null @@ -1,131 +0,0 @@ -local _={} -_[59]={} -_[58]={} -_[57]={} -_[56]={} -_[55]={} -_[54]={} -_[53]={} -_[52]={} -_[51]={} -_[50]={} -_[49]={} -_[48]={} -_[47]={} -_[46]={} -_[45]={} -_[44]={} -_[43]={} -_[42]={} -_[41]={} -_[40]={text="[1, 2, 3, 4, 5]",tags=_[59]} -_[39]={text="l: ",tags=_[58]} -_[38]={text="FINAL",tags=_[57]} -_[37]={text="[1, 2, 3, 4, 5]",tags=_[56]} -_[36]={text="f2: ",tags=_[55]} -_[35]={text="END REC",tags=_[54]} -_[34]={text="[1, 2, 3, 4]",tags=_[53]} -_[33]={text="f2: ",tags=_[52]} -_[32]={text="CHECK",tags=_[51]} -_[31]={text="[1, 2, 3]",tags=_[50]} -_[30]={text=" ",tags=_[49]} -_[29]={text="[1, 2, 3]",tags=_[48]} -_[28]={text="f1: ",tags=_[47]} -_[27]={text="REC",tags=_[46]} -_[26]={text="CHECK",tags=_[45]} -_[25]={text="[1, 2]",tags=_[44]} -_[24]={text=" ",tags=_[43]} -_[23]={text="[1, 2]",tags=_[42]} -_[22]={text="f1: ",tags=_[41]} -_[21]={_[39],_[40]} -_[20]={_[38]} -_[19]={_[36],_[37]} -_[18]={_[35]} -_[17]={_[33],_[34]} -_[16]={_[32]} -_[15]={_[28],_[29],_[30],_[31]} -_[14]={_[27]} -_[13]={_[26]} -_[12]={_[22],_[23],_[24],_[25]} -_[11]={"return"} -_[10]={"text",_[21]} -_[9]={"text",_[20]} -_[8]={"text",_[19]} -_[7]={"text",_[18]} -_[6]={"text",_[17]} -_[5]={"text",_[16]} -_[4]={"text",_[15]} -_[3]={"text",_[14]} -_[2]={"text",_[13]} -_[1]={"text",_[12]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11]} ---[[ -{ "text", { { - tags = {}, - text = "f1: " - }, { - tags = {}, - text = "[1, 2]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, 2]" - } } } -{ "text", { { - tags = {}, - text = "CHECK" - } } } -{ "text", { { - tags = {}, - text = "REC" - } } } -{ "text", { { - tags = {}, - text = "f1: " - }, { - tags = {}, - text = "[1, 2, 3]" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "[1, 2, 3]" - } } } -{ "text", { { - tags = {}, - text = "CHECK" - } } } -{ "text", { { - tags = {}, - text = "f2: " - }, { - tags = {}, - text = "[1, 2, 3, 4]" - } } } -{ "text", { { - tags = {}, - text = "END REC" - } } } -{ "text", { { - tags = {}, - text = "f2: " - }, { - tags = {}, - text = "[1, 2, 3, 4, 5]" - } } } -{ "text", { { - tags = {}, - text = "FINAL" - } } } -{ "text", { { - tags = {}, - text = "l: " - }, { - tags = {}, - text = "[1, 2, 3, 4, 5]" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/seen checkpoint resume.ans b/test/tests/seen checkpoint resume.ans deleted file mode 100644 index 5ee996b..0000000 --- a/test/tests/seen checkpoint resume.ans +++ /dev/null @@ -1,12 +0,0 @@ -:$ fn - {👁️} - - :! a - - a: {👁️} - -~ fn.a - -~ fn.a - -~ fn.a diff --git a/test/tests/seen checkpoint resume.lua b/test/tests/seen checkpoint resume.lua deleted file mode 100644 index 5bc9aba..0000000 --- a/test/tests/seen checkpoint resume.lua +++ /dev/null @@ -1,45 +0,0 @@ -local _={} -_[19]={} -_[18]={} -_[17]={} -_[16]={} -_[15]={} -_[14]={} -_[13]={text="2",tags=_[19]} -_[12]={text="a: ",tags=_[18]} -_[11]={text="1",tags=_[17]} -_[10]={text="a: ",tags=_[16]} -_[9]={text="0",tags=_[15]} -_[8]={text="a: ",tags=_[14]} -_[7]={_[12],_[13]} -_[6]={_[10],_[11]} -_[5]={_[8],_[9]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "a: " - }, { - tags = {}, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "a: " - }, { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "a: " - }, { - tags = {}, - text = "2" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/string escaping.ans b/test/tests/string escaping.ans deleted file mode 100644 index 20179e1..0000000 --- a/test/tests/string escaping.ans +++ /dev/null @@ -1,9 +0,0 @@ -expression {"{"a"}"} - -quote {"\""} - -other codes {"\n"} {"\\"} {"\t"} \{braces} - -{"escaping expressions {"a"+"bc"} and \{stuff} \\ and quotes \""} - -generic symbol {"\£"} diff --git a/test/tests/string escaping.lua b/test/tests/string escaping.lua deleted file mode 100644 index 83c8aec..0000000 --- a/test/tests/string escaping.lua +++ /dev/null @@ -1,91 +0,0 @@ -local _={} -_[39]={} -_[38]={} -_[37]={} -_[36]={} -_[35]={} -_[34]={} -_[33]={} -_[32]={} -_[31]={} -_[30]={} -_[29]={} -_[28]={} -_[27]={} -_[26]={} -_[25]={tags=_[39],text="\194\163"} -_[24]={tags=_[38],text="generic symbol "} -_[23]={tags=_[37],text="escaping expressions abc and {stuff} \\ and quotes \""} -_[22]={tags=_[36],text=" {braces}"} -_[21]={tags=_[35],text="\9"} -_[20]={tags=_[34],text=" "} -_[19]={tags=_[33],text="\\"} -_[18]={tags=_[32],text=" "} -_[17]={tags=_[31],text="\n"} -_[16]={tags=_[30],text="other codes "} -_[15]={tags=_[29],text="\""} -_[14]={tags=_[28],text="quote "} -_[13]={tags=_[27],text="a"} -_[12]={tags=_[26],text="expression "} -_[11]={_[24],_[25]} -_[10]={_[23]} -_[9]={_[16],_[17],_[18],_[19],_[20],_[21],_[22]} -_[8]={_[14],_[15]} -_[7]={_[12],_[13]} -_[6]={"return"} -_[5]={"text",_[11]} -_[4]={"text",_[10]} -_[3]={"text",_[9]} -_[2]={"text",_[8]} -_[1]={"text",_[7]} -return {_[1],_[2],_[3],_[4],_[5],_[6]} ---[[ -{ "text", { { - tags = {}, - text = "expression " - }, { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "quote " - }, { - tags = {}, - text = '"' - } } } -{ "text", { { - tags = {}, - text = "other codes " - }, { - tags = {}, - text = "\n" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "\\" - }, { - tags = {}, - text = " " - }, { - tags = {}, - text = "\t" - }, { - tags = {}, - text = " {braces}" - } } } -{ "text", { { - tags = {}, - text = 'escaping expressions abc and {stuff} \\ and quotes "' - } } } -{ "text", { { - tags = {}, - text = "generic symbol " - }, { - tags = {}, - text = "£" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/subtext.ans b/test/tests/subtext.ans deleted file mode 100644 index afdc802..0000000 --- a/test/tests/subtext.ans +++ /dev/null @@ -1,12 +0,0 @@ -:$ button - A # 2=2 - -Press -A # 5 -to jump. - -Press [A#5] to jump. - -Press [{button}#1] to jump. - -Press [-[button#3=3]-#1] to jump. diff --git a/test/tests/subtext.lua b/test/tests/subtext.lua deleted file mode 100644 index d96e354..0000000 --- a/test/tests/subtext.lua +++ /dev/null @@ -1,86 +0,0 @@ -local _={} -_[33]={1,[3]=3} -_[32]={1} -_[31]={} -_[30]={1,2} -_[29]={} -_[28]={5} -_[27]={} -_[26]={} -_[25]={5} -_[24]={} -_[23]={tags=_[31],text=" to jump."} -_[22]={tags=_[32],text="-"} -_[21]={tags=_[33],text="button"} -_[20]={tags=_[32],text="-"} -_[19]={tags=_[31],text="Press "} -_[18]={tags=_[29],text="to jump."} -_[17]={tags=_[30],text="A "} -_[16]={tags=_[29],text="Press "} -_[15]={tags=_[27],text=" to jump."} -_[14]={tags=_[28],text="A"} -_[13]={tags=_[27],text="Press "} -_[12]={tags=_[26],text="to jump."} -_[11]={tags=_[25],text="A "} -_[10]={tags=_[24],text="Press "} -_[9]={_[19],_[20],_[21],_[22],_[23]} -_[8]={_[16],_[17],_[18]} -_[7]={_[13],_[14],_[15]} -_[6]={_[10],_[11],_[12]} -_[5]={"return"} -_[4]={"text",_[9]} -_[3]={"text",_[8]} -_[2]={"text",_[7]} -_[1]={"text",_[6]} -return {_[1],_[2],_[3],_[4],_[5]} ---[[ -{ "text", { { - tags = {}, - text = "Press " - }, { - tags = { 5 }, - text = "A " - }, { - tags = {}, - text = "to jump." - } } } -{ "text", { { - tags = <1>{}, - text = "Press " - }, { - tags = { 5 }, - text = "A" - }, { - tags =
, - text = " to jump." - } } } -{ "text", { { - tags = <1>{}, - text = "Press " - }, { - tags = { 1, 2 }, - text = "A " - }, { - tags =
, - text = "to jump." - } } } -{ "text", { { - tags = <1>{}, - text = "Press " - }, { - tags = <2>{ 1 }, - text = "-" - }, { - tags = { 1, - [3] = 3 - }, - text = "button" - }, { - tags =
, - text = "-" - }, { - tags =
, - text = " to jump." - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/tag decorator nested.ans b/test/tests/tag decorator nested.ans deleted file mode 100644 index 8195149..0000000 --- a/test/tests/tag decorator nested.ans +++ /dev/null @@ -1,4 +0,0 @@ -# 1 - foo - ~ 1 # b=[1,2] - bar ~ 1 # a=[2,3] diff --git a/test/tests/tag decorator nested.lua b/test/tests/tag decorator nested.lua deleted file mode 100644 index 17f75be..0000000 --- a/test/tests/tag decorator nested.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[8]={2,3} -_[7]={1,a=_[8]} -_[6]={1} -_[5]={tags=_[7],text="bar"} -_[4]={tags=_[6],text="foo"} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = { 1 }, - text = "foo" - }, { - tags = { 1, - a = { 2, 3 } - }, - text = "bar" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/tag decorator.ans b/test/tests/tag decorator.ans deleted file mode 100644 index 572cc46..0000000 --- a/test/tests/tag decorator.ans +++ /dev/null @@ -1,3 +0,0 @@ -# 1 - foo - bar # a=[2,3] diff --git a/test/tests/tag decorator.lua b/test/tests/tag decorator.lua deleted file mode 100644 index 17f75be..0000000 --- a/test/tests/tag decorator.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[8]={2,3} -_[7]={1,a=_[8]} -_[6]={1} -_[5]={tags=_[7],text="bar"} -_[4]={tags=_[6],text="foo"} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = { 1 }, - text = "foo" - }, { - tags = { 1, - a = { 2, 3 } - }, - text = "bar" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/tag empty.ans b/test/tests/tag empty.ans deleted file mode 100644 index d0ad67a..0000000 --- a/test/tests/tag empty.ans +++ /dev/null @@ -1,4 +0,0 @@ -# 1 - foo - # - bar diff --git a/test/tests/tag empty.lua b/test/tests/tag empty.lua deleted file mode 100644 index 9e952f9..0000000 --- a/test/tests/tag empty.lua +++ /dev/null @@ -1,19 +0,0 @@ -local _={} -_[7]={1} -_[6]={1} -_[5]={tags=_[7],text="bar"} -_[4]={tags=_[6],text="foo"} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = { 1 }, - text = "foo" - }, { - tags = { 1 }, - text = "bar" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/tag operator.ans b/test/tests/tag operator.ans deleted file mode 100644 index 862c6e3..0000000 --- a/test/tests/tag operator.ans +++ /dev/null @@ -1,7 +0,0 @@ -:$ f - b - -a {f # 5} c - -# 2=2 - a {f # 5} c diff --git a/test/tests/tag operator.lua b/test/tests/tag operator.lua deleted file mode 100644 index fac882b..0000000 --- a/test/tests/tag operator.lua +++ /dev/null @@ -1,42 +0,0 @@ -local _={} -_[15]={5,2} -_[14]={[2]=2} -_[13]={5} -_[12]={} -_[11]={tags=_[14],text=" c"} -_[10]={tags=_[15],text="b"} -_[9]={tags=_[14],text="a "} -_[8]={tags=_[12],text=" c"} -_[7]={tags=_[13],text="b"} -_[6]={tags=_[12],text="a "} -_[5]={_[9],_[10],_[11]} -_[4]={_[6],_[7],_[8]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = <1>{}, - text = "a " - }, { - tags = { 5 }, - text = "b" - }, { - tags =
, - text = " c" - } } } -{ "text", { { - tags = <1>{ - [2] = 2 - }, - text = "a " - }, { - tags = { 5, 2 }, - text = "b" - }, { - tags =
, - text = " c" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/tag.ans b/test/tests/tag.ans deleted file mode 100644 index 2a4853d..0000000 --- a/test/tests/tag.ans +++ /dev/null @@ -1,4 +0,0 @@ -# 1 - foo - # a=[2,3] - bar \ No newline at end of file diff --git a/test/tests/tag.lua b/test/tests/tag.lua deleted file mode 100644 index 17f75be..0000000 --- a/test/tests/tag.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[8]={2,3} -_[7]={1,a=_[8]} -_[6]={1} -_[5]={tags=_[7],text="bar"} -_[4]={tags=_[6],text="foo"} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = { 1 }, - text = "foo" - }, { - tags = { 1, - a = { 2, 3 } - }, - text = "bar" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/text block.ans b/test/tests/text block.ans deleted file mode 100644 index 3c52dc7..0000000 --- a/test/tests/text block.ans +++ /dev/null @@ -1,2 +0,0 @@ -a -b c \ No newline at end of file diff --git a/test/tests/text block.lua b/test/tests/text block.lua deleted file mode 100644 index 92312d6..0000000 --- a/test/tests/text block.lua +++ /dev/null @@ -1,19 +0,0 @@ -local _={} -_[7]={} -_[6]={} -_[5]={tags=_[7],text="b c"} -_[4]={tags=_[6],text="a"} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "a" - }, { - tags = {}, - text = "b c" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/text break.ans b/test/tests/text break.ans deleted file mode 100644 index 24583da..0000000 --- a/test/tests/text break.ans +++ /dev/null @@ -1,3 +0,0 @@ -a - -b c \ No newline at end of file diff --git a/test/tests/text break.lua b/test/tests/text break.lua deleted file mode 100644 index e8036e3..0000000 --- a/test/tests/text break.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[9]={} -_[8]={} -_[7]={tags=_[9],text="b c"} -_[6]={tags=_[8],text="a"} -_[5]={_[7]} -_[4]={_[6]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = {}, - text = "a" - } } } -{ "text", { { - tags = {}, - text = "b c" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/text buffer with tags.ans b/test/tests/text buffer with tags.ans deleted file mode 100644 index 1e69662..0000000 --- a/test/tests/text buffer with tags.ans +++ /dev/null @@ -1,8 +0,0 @@ -:$ f - lol # 1 - - d - -:a = %[a {f} [t # 2] b] - -@a diff --git a/test/tests/text buffer with tags.lua b/test/tests/text buffer with tags.lua deleted file mode 100644 index 717e9e5..0000000 --- a/test/tests/text buffer with tags.lua +++ /dev/null @@ -1,41 +0,0 @@ -local _={} -_[21]={} -_[20]={2} -_[19]={} -_[18]={1} -_[17]={} -_[16]={text="b",tags=_[21]} -_[15]={text="t ",tags=_[20]} -_[14]={text="d",tags=_[19]} -_[13]={text="lol",tags=_[18]} -_[12]={text="a",tags=_[17]} -_[11]={_[15],_[16]} -_[10]={_[14]} -_[9]={_[13]} -_[8]={_[12]} -_[7]={"text",_[11]} -_[6]={"text",_[10]} -_[5]={"flush"} -_[4]={"text",_[9]} -_[3]={"text",_[8]} -_[2]={_[3],_[4],_[5],_[6],_[7]} -_[1]={"return",_[2]} -return {_[1]} ---[[ -{ "return", { { "text", { { - tags = {}, - text = "a" - } } }, { "text", { { - tags = { 1 }, - text = "lol" - } } }, { "flush" }, { "text", { { - tags = {}, - text = "d" - } } }, { "text", { { - tags = { 2 }, - text = "t " - }, { - tags = {}, - text = "b" - } } } } } -]]-- \ No newline at end of file diff --git a/test/tests/text buffer.ans b/test/tests/text buffer.ans deleted file mode 100644 index e27078c..0000000 --- a/test/tests/text buffer.ans +++ /dev/null @@ -1,8 +0,0 @@ -:$ f - lol - - d - -:a = %[a {f} b] - -@a diff --git a/test/tests/text buffer.lua b/test/tests/text buffer.lua deleted file mode 100644 index 7cd1090..0000000 --- a/test/tests/text buffer.lua +++ /dev/null @@ -1,34 +0,0 @@ -local _={} -_[17]={} -_[16]={} -_[15]={} -_[14]={} -_[13]={tags=_[17],text=" b"} -_[12]={tags=_[16],text="d"} -_[11]={tags=_[15],text="lol"} -_[10]={tags=_[14],text="a"} -_[9]={_[12],_[13]} -_[8]={_[11]} -_[7]={_[10]} -_[6]={"text",_[9]} -_[5]={"flush"} -_[4]={"text",_[8]} -_[3]={"text",_[7]} -_[2]={_[3],_[4],_[5],_[6]} -_[1]={"return",_[2]} -return {_[1]} ---[[ -{ "return", { { "text", { { - tags = {}, - text = "a" - } } }, { "text", { { - tags = {}, - text = "lol" - } } }, { "flush" }, { "text", { { - tags = {}, - text = "d" - }, { - tags = {}, - text = " b" - } } } } } -]]-- \ No newline at end of file diff --git a/test/tests/text escaping.ans b/test/tests/text escaping.ans deleted file mode 100644 index 701bb13..0000000 --- a/test/tests/text escaping.ans +++ /dev/null @@ -1,11 +0,0 @@ -expression \{a} - -quote \" - -other codes \n \\ \t - -decorators \# tag \~ condition \$ fn - -sub \[text] - -generic symbol \£ diff --git a/test/tests/text escaping.lua b/test/tests/text escaping.lua deleted file mode 100644 index d1e20e2..0000000 --- a/test/tests/text escaping.lua +++ /dev/null @@ -1,54 +0,0 @@ -local _={} -_[25]={} -_[24]={} -_[23]={} -_[22]={} -_[21]={} -_[20]={} -_[19]={tags=_[25],text="generic symbol \194\163"} -_[18]={tags=_[24],text="sub [text]"} -_[17]={tags=_[23],text="decorators # tag ~ condition $ fn"} -_[16]={tags=_[22],text="other codes \n \\ \9"} -_[15]={tags=_[21],text="quote \""} -_[14]={tags=_[20],text="expression {a}"} -_[13]={_[19]} -_[12]={_[18]} -_[11]={_[17]} -_[10]={_[16]} -_[9]={_[15]} -_[8]={_[14]} -_[7]={"return"} -_[6]={"text",_[13]} -_[5]={"text",_[12]} -_[4]={"text",_[11]} -_[3]={"text",_[10]} -_[2]={"text",_[9]} -_[1]={"text",_[8]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7]} ---[[ -{ "text", { { - tags = {}, - text = "expression {a}" - } } } -{ "text", { { - tags = {}, - text = 'quote "' - } } } -{ "text", { { - tags = {}, - text = "other codes \n \\ \t" - } } } -{ "text", { { - tags = {}, - text = "decorators # tag ~ condition $ fn" - } } } -{ "text", { { - tags = {}, - text = "sub [text]" - } } } -{ "text", { { - tags = {}, - text = "generic symbol £" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/text format.ans b/test/tests/text format.ans deleted file mode 100644 index 9eca55a..0000000 --- a/test/tests/text format.ans +++ /dev/null @@ -1,3 +0,0 @@ -:a = 5 - -a: {a} diff --git a/test/tests/text format.lua b/test/tests/text format.lua deleted file mode 100644 index 4055219..0000000 --- a/test/tests/text format.lua +++ /dev/null @@ -1,18 +0,0 @@ -local _={} -_[6]={} -_[5]={tags=_[6],text="5"} -_[4]={tags=_[6],text="a: "} -_[3]={_[4],_[5]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "a: " - }, { - tags =
, - text = "5" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/text line interpolation with choice event.ans b/test/tests/text line interpolation with choice event.ans deleted file mode 100644 index 08afbb8..0000000 --- a/test/tests/text line interpolation with choice event.ans +++ /dev/null @@ -1,16 +0,0 @@ -Press {jump button} to jump. - -:$ jump button - A # 1 - ~ choose(1) - > Surprise choice! - ok - -Use {move axis} to move. - -:$ move axis - left # 1 - ~ choose(1) - > Surprise choice! - ok2 - @" joystick" diff --git a/test/tests/text line interpolation with choice event.lua b/test/tests/text line interpolation with choice event.lua deleted file mode 100644 index 95f3f32..0000000 --- a/test/tests/text line interpolation with choice event.lua +++ /dev/null @@ -1,82 +0,0 @@ -local _={} -_[36]={} -_[35]={tags=_[36],text="Surprise choice!"} -_[34]={1} -_[33]={} -_[32]={} -_[31]={tags=_[32],text="Surprise choice!"} -_[30]={1} -_[29]={} -_[28]={tags=_[33],text=" to move."} -_[27]={tags=_[33],text=" joystick"} -_[26]={tags=_[36],text="ok2"} -_[25]={_[35]} -_[24]={tags=_[34],text="left"} -_[23]={tags=_[33],text="Use "} -_[22]={tags=_[29],text=" to jump."} -_[21]={tags=_[32],text="ok"} -_[20]={_[31]} -_[19]={tags=_[30],text="A"} -_[18]={tags=_[29],text="Press "} -_[17]={_[27],_[28]} -_[16]={_[26]} -_[15]={_[25]} -_[14]={_[23],_[24]} -_[13]={_[22]} -_[12]={_[21]} -_[11]={_[20]} -_[10]={_[18],_[19]} -_[9]={"return"} -_[8]={"text",_[17]} -_[7]={"text",_[16]} -_[6]={"choice",_[15]} -_[5]={"text",_[14]} -_[4]={"text",_[13]} -_[3]={"text",_[12]} -_[2]={"choice",_[11]} -_[1]={"text",_[10]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9]} ---[[ -{ "text", { { - tags = {}, - text = "Press " - }, { - tags = { 1 }, - text = "A" - } } } -{ "choice", { { { - tags = {}, - text = "Surprise choice!" - } } } } -{ "text", { { - tags = {}, - text = "ok" - } } } -{ "text", { { - tags = {}, - text = " to jump." - } } } -{ "text", { { - tags = {}, - text = "Use " - }, { - tags = { 1 }, - text = "left" - } } } -{ "choice", { { { - tags = {}, - text = "Surprise choice!" - } } } } -{ "text", { { - tags = {}, - text = "ok2" - } } } -{ "text", { { - tags = <1>{}, - text = " joystick" - }, { - tags =
, - text = " to move." - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/text line interpolation with event flush.ans b/test/tests/text line interpolation with event flush.ans deleted file mode 100644 index 4af72d0..0000000 --- a/test/tests/text line interpolation with event flush.ans +++ /dev/null @@ -1,13 +0,0 @@ -Press {jump button} to jump. - -:$ jump button - A # 1 - - @ - -Use {move axis} to move. - -:$ move axis - left # 1 - - @" joystick" diff --git a/test/tests/text line interpolation with event flush.lua b/test/tests/text line interpolation with event flush.lua deleted file mode 100644 index e5700b2..0000000 --- a/test/tests/text line interpolation with event flush.lua +++ /dev/null @@ -1,50 +0,0 @@ -local _={} -_[20]={1} -_[19]={} -_[18]={1} -_[17]={} -_[16]={text=" to move.",tags=_[19]} -_[15]={text=" joystick",tags=_[19]} -_[14]={text="left",tags=_[20]} -_[13]={text="Use ",tags=_[19]} -_[12]={text=" to jump.",tags=_[17]} -_[11]={text="A",tags=_[18]} -_[10]={text="Press ",tags=_[17]} -_[9]={_[15],_[16]} -_[8]={_[13],_[14]} -_[7]={_[12]} -_[6]={_[10],_[11]} -_[5]={"return"} -_[4]={"text",_[9]} -_[3]={"text",_[8]} -_[2]={"text",_[7]} -_[1]={"text",_[6]} -return {_[1],_[2],_[3],_[4],_[5]} ---[[ -{ "text", { { - tags = {}, - text = "Press " - }, { - tags = { 1 }, - text = "A" - } } } -{ "text", { { - tags = {}, - text = " to jump." - } } } -{ "text", { { - tags = {}, - text = "Use " - }, { - tags = { 1 }, - text = "left" - } } } -{ "text", { { - tags = <1>{}, - text = " joystick" - }, { - tags =
, - text = " to move." - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/text line interpolation with text event.ans b/test/tests/text line interpolation with text event.ans deleted file mode 100644 index 7103768..0000000 --- a/test/tests/text line interpolation with text event.ans +++ /dev/null @@ -1,10 +0,0 @@ -Press {jump button} to jump. - -:$ jump button - A # 1 - -Use {move axis} to move. - -:$ move axis - left # 1 - @" joystick" diff --git a/test/tests/text line interpolation with text event.lua b/test/tests/text line interpolation with text event.lua deleted file mode 100644 index c208c91..0000000 --- a/test/tests/text line interpolation with text event.lua +++ /dev/null @@ -1,44 +0,0 @@ -local _={} -_[16]={1} -_[15]={} -_[14]={1} -_[13]={} -_[12]={text=" to move.",tags=_[15]} -_[11]={text="joystick",tags=_[15]} -_[10]={text="left ",tags=_[16]} -_[9]={text="Use ",tags=_[15]} -_[8]={text="to jump.",tags=_[13]} -_[7]={text="A ",tags=_[14]} -_[6]={text="Press ",tags=_[13]} -_[5]={_[9],_[10],_[11],_[12]} -_[4]={_[6],_[7],_[8]} -_[3]={"return"} -_[2]={"text",_[5]} -_[1]={"text",_[4]} -return {_[1],_[2],_[3]} ---[[ -{ "text", { { - tags = <1>{}, - text = "Press " - }, { - tags = { 1 }, - text = "A " - }, { - tags =
, - text = "to jump." - } } } -{ "text", { { - tags = <1>{}, - text = "Use " - }, { - tags = { 1 }, - text = "left " - }, { - tags =
, - text = "joystick" - }, { - tags =
, - text = " to move." - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/text.ans b/test/tests/text.ans deleted file mode 100644 index 2e65efe..0000000 --- a/test/tests/text.ans +++ /dev/null @@ -1 +0,0 @@ -a \ No newline at end of file diff --git a/test/tests/text.lua b/test/tests/text.lua deleted file mode 100644 index fe44ce0..0000000 --- a/test/tests/text.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="a"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "a" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/unary operator overload.ans b/test/tests/unary operator overload.ans deleted file mode 100644 index 50e25d3..0000000 --- a/test/tests/unary operator overload.ans +++ /dev/null @@ -1,11 +0,0 @@ -:$ -_(f) - @"generic minus" - -:$ -_(f::string) - @"minus "+f - -{-5} - -{-"lol"} - -{-[]} diff --git a/test/tests/unary operator overload.lua b/test/tests/unary operator overload.lua deleted file mode 100644 index 3797b53..0000000 --- a/test/tests/unary operator overload.lua +++ /dev/null @@ -1,30 +0,0 @@ -local _={} -_[13]={} -_[12]={} -_[11]={} -_[10]={tags=_[13],text="generic minus"} -_[9]={tags=_[12],text="minus lol"} -_[8]={tags=_[11],text="-5"} -_[7]={_[10]} -_[6]={_[9]} -_[5]={_[8]} -_[4]={"return"} -_[3]={"text",_[7]} -_[2]={"text",_[6]} -_[1]={"text",_[5]} -return {_[1],_[2],_[3],_[4]} ---[[ -{ "text", { { - tags = {}, - text = "-5" - } } } -{ "text", { { - tags = {}, - text = "minus lol" - } } } -{ "text", { { - tags = {}, - text = "generic minus" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/unseen line.ans b/test/tests/unseen line.ans deleted file mode 100644 index 913add7..0000000 --- a/test/tests/unseen line.ans +++ /dev/null @@ -1,7 +0,0 @@ -:$ x - a - seen only once ~ !seen - b - -~ x -~ x \ No newline at end of file diff --git a/test/tests/unseen line.lua b/test/tests/unseen line.lua deleted file mode 100644 index ed6f69c..0000000 --- a/test/tests/unseen line.lua +++ /dev/null @@ -1,34 +0,0 @@ -local _={} -_[13]={} -_[12]={} -_[11]={} -_[10]={} -_[9]={} -_[8]={text="b",tags=_[13]} -_[7]={text="a",tags=_[12]} -_[6]={text="b",tags=_[11]} -_[5]={text="seen only once ",tags=_[10]} -_[4]={text="a",tags=_[9]} -_[3]={_[4],_[5],_[6],_[7],_[8]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "a" - }, { - tags = {}, - text = "seen only once " - }, { - tags = {}, - text = "b" - }, { - tags = {}, - text = "a" - }, { - tags = {}, - text = "b" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/variable alias.ans b/test/tests/variable alias.ans deleted file mode 100644 index 36f80a4..0000000 --- a/test/tests/variable alias.ans +++ /dev/null @@ -1,3 +0,0 @@ -:a : b = 42 - -{a} = {b} diff --git a/test/tests/variable alias.lua b/test/tests/variable alias.lua deleted file mode 100644 index 794e4c4..0000000 --- a/test/tests/variable alias.lua +++ /dev/null @@ -1,22 +0,0 @@ -local _={} -_[7]={} -_[6]={tags=_[7],text="42"} -_[5]={tags=_[7],text=" = "} -_[4]={tags=_[7],text="42"} -_[3]={_[4],_[5],_[6]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = <1>{}, - text = "42" - }, { - tags =
, - text = " = " - }, { - tags =
, - text = "42" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/variable reference get value.ans b/test/tests/variable reference get value.ans deleted file mode 100644 index 5727af7..0000000 --- a/test/tests/variable reference get value.ans +++ /dev/null @@ -1,5 +0,0 @@ -:x = 52 - -:y = &x - -{y!} diff --git a/test/tests/variable reference get value.lua b/test/tests/variable reference get value.lua deleted file mode 100644 index 172747d..0000000 --- a/test/tests/variable reference get value.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _={} -_[5]={} -_[4]={tags=_[5],text="52"} -_[3]={_[4]} -_[2]={"return"} -_[1]={"text",_[3]} -return {_[1],_[2]} ---[[ -{ "text", { { - tags = {}, - text = "52" - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/while loop else.ans b/test/tests/while loop else.ans deleted file mode 100644 index 5ef53f4..0000000 --- a/test/tests/while loop else.ans +++ /dev/null @@ -1,19 +0,0 @@ -:i = 1 - -Start with i={i}: - -~? i < 5 - {i} - - ~ i += 1 -~~ - Loop not ran. - -Start with i={i}: - -~? i < 5 - {i} - - ~ i += 1 -~~ - Loop not ran. \ No newline at end of file diff --git a/test/tests/while loop else.lua b/test/tests/while loop else.lua deleted file mode 100644 index 55f6864..0000000 --- a/test/tests/while loop else.lua +++ /dev/null @@ -1,82 +0,0 @@ -local _={} -_[37]={} -_[36]={} -_[35]={} -_[34]={} -_[33]={} -_[32]={} -_[31]={} -_[30]={} -_[29]={} -_[28]={} -_[27]={} -_[26]={text="Loop not ran.",tags=_[37]} -_[25]={text=":",tags=_[36]} -_[24]={text="5",tags=_[35]} -_[23]={text="Start with i=",tags=_[34]} -_[22]={text="4",tags=_[33]} -_[21]={text="3",tags=_[32]} -_[20]={text="2",tags=_[31]} -_[19]={text="1",tags=_[30]} -_[18]={text=":",tags=_[29]} -_[17]={text="1",tags=_[28]} -_[16]={text="Start with i=",tags=_[27]} -_[15]={_[26]} -_[14]={_[23],_[24],_[25]} -_[13]={_[22]} -_[12]={_[21]} -_[11]={_[20]} -_[10]={_[19]} -_[9]={_[16],_[17],_[18]} -_[8]={"return"} -_[7]={"text",_[15]} -_[6]={"text",_[14]} -_[5]={"text",_[13]} -_[4]={"text",_[12]} -_[3]={"text",_[11]} -_[2]={"text",_[10]} -_[1]={"text",_[9]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8]} ---[[ -{ "text", { { - tags = {}, - text = "Start with i=" - }, { - tags = {}, - text = "1" - }, { - tags = {}, - text = ":" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "text", { { - tags = {}, - text = "4" - } } } -{ "text", { { - tags = {}, - text = "Start with i=" - }, { - tags = {}, - text = "5" - }, { - tags = {}, - text = ":" - } } } -{ "text", { { - tags = {}, - text = "Loop not ran." - } } } -{ "return" } -]]-- \ No newline at end of file diff --git a/test/tests/while loop.ans b/test/tests/while loop.ans deleted file mode 100644 index 1932001..0000000 --- a/test/tests/while loop.ans +++ /dev/null @@ -1,16 +0,0 @@ -:i = 0 -~? i <= 10 - {i} - - ~ i += 1 - -return in loop: - -~ i := 0 -~? i <= 10 - {i} - - ~ i == 5 - @ - - ~ i += 1 diff --git a/test/tests/while loop.lua b/test/tests/while loop.lua deleted file mode 100644 index 07bfb82..0000000 --- a/test/tests/while loop.lua +++ /dev/null @@ -1,150 +0,0 @@ -local _={} -_[73]={} -_[72]={} -_[71]={} -_[70]={} -_[69]={} -_[68]={} -_[67]={} -_[66]={} -_[65]={} -_[64]={} -_[63]={} -_[62]={} -_[61]={} -_[60]={} -_[59]={} -_[58]={} -_[57]={} -_[56]={} -_[55]={text="5",tags=_[73]} -_[54]={text="4",tags=_[72]} -_[53]={text="3",tags=_[71]} -_[52]={text="2",tags=_[70]} -_[51]={text="1",tags=_[69]} -_[50]={text="0",tags=_[68]} -_[49]={text="return in loop:",tags=_[67]} -_[48]={text="10",tags=_[66]} -_[47]={text="9",tags=_[65]} -_[46]={text="8",tags=_[64]} -_[45]={text="7",tags=_[63]} -_[44]={text="6",tags=_[62]} -_[43]={text="5",tags=_[61]} -_[42]={text="4",tags=_[60]} -_[41]={text="3",tags=_[59]} -_[40]={text="2",tags=_[58]} -_[39]={text="1",tags=_[57]} -_[38]={text="0",tags=_[56]} -_[37]={_[55]} -_[36]={_[54]} -_[35]={_[53]} -_[34]={_[52]} -_[33]={_[51]} -_[32]={_[50]} -_[31]={_[49]} -_[30]={_[48]} -_[29]={_[47]} -_[28]={_[46]} -_[27]={_[45]} -_[26]={_[44]} -_[25]={_[43]} -_[24]={_[42]} -_[23]={_[41]} -_[22]={_[40]} -_[21]={_[39]} -_[20]={_[38]} -_[19]={"return"} -_[18]={"text",_[37]} -_[17]={"text",_[36]} -_[16]={"text",_[35]} -_[15]={"text",_[34]} -_[14]={"text",_[33]} -_[13]={"text",_[32]} -_[12]={"text",_[31]} -_[11]={"text",_[30]} -_[10]={"text",_[29]} -_[9]={"text",_[28]} -_[8]={"text",_[27]} -_[7]={"text",_[26]} -_[6]={"text",_[25]} -_[5]={"text",_[24]} -_[4]={"text",_[23]} -_[3]={"text",_[22]} -_[2]={"text",_[21]} -_[1]={"text",_[20]} -return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15],_[16],_[17],_[18],_[19]} ---[[ -{ "text", { { - tags = {}, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "text", { { - tags = {}, - text = "4" - } } } -{ "text", { { - tags = {}, - text = "5" - } } } -{ "text", { { - tags = {}, - text = "6" - } } } -{ "text", { { - tags = {}, - text = "7" - } } } -{ "text", { { - tags = {}, - text = "8" - } } } -{ "text", { { - tags = {}, - text = "9" - } } } -{ "text", { { - tags = {}, - text = "10" - } } } -{ "text", { { - tags = {}, - text = "return in loop:" - } } } -{ "text", { { - tags = {}, - text = "0" - } } } -{ "text", { { - tags = {}, - text = "1" - } } } -{ "text", { { - tags = {}, - text = "2" - } } } -{ "text", { { - tags = {}, - text = "3" - } } } -{ "text", { { - tags = {}, - text = "4" - } } } -{ "text", { { - tags = {}, - text = "5" - } } } -{ "return" } -]]-- \ No newline at end of file