From 7b756ad092d76f7b1610667de5aebfb747e2d0f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Reuh=20Fildadut?= Date: Sat, 10 Sep 2022 17:59:22 +0900 Subject: [PATCH] Update README, bump version --- LANGUAGE.md | 35 +++++++++++++-------- README.md | 2 ++ TUTORIAL.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++ anselme.lua | 6 ++-- notes.txt | 29 +++++++++++++----- parser/common.lua | 11 ++++++- test/run.lua | 2 +- 7 files changed, 137 insertions(+), 25 deletions(-) create mode 100644 TUTORIAL.md diff --git a/LANGUAGE.md b/LANGUAGE.md index 7886e3c..38227ee 100644 --- a/LANGUAGE.md +++ b/LANGUAGE.md @@ -248,7 +248,7 @@ Functions can return a value using a [return line](#lines-that-can-t-have-childr Functions always have the following variables defined in its namespace by default: `👁️`: number, number of times the function was executed before -`🔖`: funcion reference, last reached checkpoint. `nil` if no checkpoint reached. +`🔖`: function reference, last reached checkpoint. `nil` if no checkpoint reached. * `§`: checkpoint. Followed by an [identifier](#identifiers), then eventually an [alias](#aliases). Define a checkpoint. Also define a new namespace for its children. @@ -630,9 +630,9 @@ Default types are: * `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. Can be called as if it was the original function using `func ref!` and `func ref(args)`. +* `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. Can get the referenced variable value using `var ref!`. +* `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]'. @@ -676,11 +676,17 @@ These can be used to represent some caracters in string and other text elements * `\"` for `"` * `\n` for a newline * `\t` for a tabulation -* `\{` for `{` -* `\[` for `[` +* `\{` and `\}` for `{` and `}` +* `\[` and `\]` for `[` and `]` * `\~` for `~` * `\#` for `#` * `\$` for `$` +* `\(` for `(` +* `\>` for `>` +* `\%` for `%` +* `\§` for `§` +* `\@` for `@` +* `\:` for `:` #### Truethness @@ -941,8 +947,9 @@ _*_ _//_ _/_ _%_ _::_ -_ !_ _^_ -_._ _!_ +_!_ &_ +_._ ``` A series of operators with the same priority are evaluated left-to-right. @@ -1009,15 +1016,19 @@ This only works on strings: `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`: returns a function reference to the given function. +`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. ##### Variable references -`&var`: returns a variable reference to the given variable. +`&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 reference variable. +`a!`: returns the value associated with the referenced variable. + +`a`: returns the value associated with the referenced variable. ##### Various @@ -1031,9 +1042,9 @@ This only works on strings: `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 (or reference to a subfunction) named `b` in the referenced function namespace. 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). +`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 (or reference to a subfunction) named `b` in the object, or, if the object does not contain it, its base class. +`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 `()`. @@ -1091,7 +1102,7 @@ This only works on strings: #### Built-in variables -Variables for default types (each is associated to a string of the internal variable type name): `nil`, `number`, `string`, `list`, `pair`, `function reference`, `variable reference`. +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`. diff --git a/README.md b/README.md index ca5810d..71b73fc 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,8 @@ Othertimes we don't: TODO: stupidly complex script +See [TUTORIAL.md](TUTORIAL.md) for a short introduction (not yet done). + Reference ------------------ diff --git a/TUTORIAL.md b/TUTORIAL.md new file mode 100644 index 0000000..772c146 --- /dev/null +++ b/TUTORIAL.md @@ -0,0 +1,77 @@ +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 b354251..d5c6473 100644 --- a/anselme.lua +++ b/anselme.lua @@ -54,14 +54,14 @@ local anselme = { -- * `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 versions = { - save = 1, - language = 22, + save = 2, + language = 23, api = 5 }, --- General version number. -- -- It is incremented at each update. - version = 23, + version = 24, --- Currently running [interpreter](#interpreters). -- `nil` if no interpreter running. running = nil diff --git a/notes.txt b/notes.txt index a5f7436..e187d8a 100644 --- a/notes.txt +++ b/notes.txt @@ -43,11 +43,24 @@ 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) + :foo:bar = "key" + :map = {bar=42} + + i.e. just remove the special case of interpreting identifiers as strings in pairs. But then might need to distinguish from the named argument syntax in function calls. + +TODO: type system is not super nice to use. + UPDATE: tried to implement a static type system, ain't worth it. Would require major refactoring to go full static with good type inference. The language is supposed to allow to not have to worry about low level stuff, so no deal if the type inference isn't good. Thank you multiple dispatch for making everything complicated (i still love you though)... Well, if I ever reimplement Anselme, let's thank the Julia devs for doing the hard work: https://docs.julialang.org/en/v1/devdocs/inference/ + +TODO: some sensible way to capture text event in string litterals (string interpolation/subtexts)? -> 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... TODO: the function decorator feels a bit glued-on to the current syntax @@ -65,7 +78,7 @@ TODO: fn/checkpoint/tag: maybe consider them a regular func call that takes chil :a = $(args) stuff - how are children passed on? overloading? + 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 @@ -83,18 +96,18 @@ TODO: fn/checkpoint/tag: maybe consider them a regular func call that takes chil TODO: perform seen/reached/etc default variable not in interpreter but using parse-time macro TODO: no function call without () (reference to fn instead?). Fn could be stored in save but replaced with new versions on code reload... -> how to track these references? - if we make fn first class this means anonymous fn, is it ok? + if we make fn first class this means anonymous fn, is it ok? -> no way to track anonymous fn, so no. 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 +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 variables (:a::number=5) and functions return ($ f()::number) -* enforce some restrictions on type (assume they're constant?) +* 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 diff --git a/parser/common.lua b/parser/common.lua index ab3a6ff..ca5a252 100644 --- a/parser/common.lua +++ b/parser/common.lua @@ -62,13 +62,22 @@ common = { ["\\t"] = "\t", -- string interpolation ["\\{"] = "{", + ["\\}"] = "}", -- subtext ["\\["] = "[", + ["\\]"] = "]", -- end of text line expressions ["\\~"] = "~", ["\\#"] = "#", -- decorators - ["\\$"] = "$" + ["\\$"] = "$", + -- line types + ["\\("] = "(", + ["\\>"] = ">", + ["\\%"] = "%", + ["\\§"] = "§", + ["\\@"] = "@", + ["\\:"] = ":", }, -- list of possible injections and their associated name in vm.state.inject injections = { diff --git a/test/run.lua b/test/run.lua index 6a86a48..a3008f5 100644 --- a/test/run.lua +++ b/test/run.lua @@ -268,6 +268,6 @@ else if args["write-all"] then print("Wrote test results.") else - print(("%s/%s tests success."):format(success, total)) + print(("%s/%s tests passed."):format(success, total)) end end