{"id":27761029,"url":"https://github.com/tsoding/tula","last_synced_at":"2025-06-18T14:32:48.888Z","repository":{"id":234269865,"uuid":"788547754","full_name":"tsoding/tula","owner":"tsoding","description":"Turing Language","archived":false,"fork":false,"pushed_at":"2024-07-29T18:53:58.000Z","size":187,"stargazers_count":191,"open_issues_count":1,"forks_count":4,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-04-29T12:19:51.977Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tsoding.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-04-18T16:19:58.000Z","updated_at":"2025-04-29T07:28:20.000Z","dependencies_parsed_at":"2024-05-15T18:20:29.716Z","dependency_job_id":"87279ae8-11c0-4a88-8b9d-aa287f9c5162","html_url":"https://github.com/tsoding/tula","commit_stats":null,"previous_names":["tsoding/tula"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tsoding/tula","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsoding%2Ftula","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsoding%2Ftula/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsoding%2Ftula/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsoding%2Ftula/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tsoding","download_url":"https://codeload.github.com/tsoding/tula/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsoding%2Ftula/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260570092,"owners_count":23029622,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-29T12:19:49.318Z","updated_at":"2025-06-18T14:32:43.864Z","avatar_url":"https://github.com/tsoding.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tula\n\nTula (**Tu**ring **La**nguage) is an [Esoteric Programming Language](https://en.wikipedia.org/wiki/Esoteric_programming_language) based on [Turing Machine](https://en.wikipedia.org/wiki/Turing_machine) and extended with [Set Theory](https://en.wikipedia.org/wiki/Set_theory).\n\n## Install\n\n1. Install [Rust Compiler](https://www.rust-lang.org/learn/get-started)\n2. Clone this repo:\n```console\n$ git clone https://github.com/tsoding/tula \u0026\u0026 cd tula\n```\n3. Install the compiler to `~/.cargo/bin/` (it should be already in your `$PATH` after rustup installation)\n```console\n$ cargo install --path .\n```\n4. Run some examples:\n```console\n$ tula run ./examples/05-rule110.tula\n```\n5. Expand the example into a form without Universal Quantifiers and Sets:\n```console\n$ tula expand ./examples/05-rule110.tula\n```\n\n## Test\n\nThe project is using [rere.py](https://github.com/tsoding/rere.py) for testing the behavior of the compiler:\n\n1. To run the tests:\n```console\n$ ./rere.py replay ./tests.list\n```\n\n2. To record the new behavior:\n```console\n$ ./rere.py record ./tests.list\n```\n\n## Base Syntax\n\nThe program consist of sequence of rules:\n\n```js\ncase \u003cState\u003e \u003cRead\u003e \u003cWrite\u003e \u003cStep\u003e \u003cNext\u003e\ncase \u003cState\u003e \u003cRead\u003e \u003cWrite\u003e \u003cStep\u003e \u003cNext\u003e\ncase \u003cState\u003e \u003cRead\u003e \u003cWrite\u003e \u003cStep\u003e \u003cNext\u003e\n...\n```\n\nEach rule starts with the keyword `case` and 5 expressions:\n\n- `\u003cState\u003e` - The current state of the Machine,\n- `\u003cRead\u003e` - What the Machine reads on the Tape,\n- `\u003cWrite\u003e` - What the Machine should write on the Tape,\n- `\u003cStep\u003e` - Where the Head of the Machine must step (`\u003c-` left, `-\u003e` right or `.` stand),\n- `\u003cNext\u003e` - What is the next state of the Machine.\n\n### Example\n\nSimple program that increments a binary number (least significant bits come first):\n\n```js\n// When in the state `Inc` and read `0`, replace it with `1` move the head\n// to the right and switch to the state `Halt` which halts the program.\ncase Inc 0 1 -\u003e Halt\n\n// When in the state `Inc` and read `1`, replace it with `0` move the head\n// to the right and keep staying in the state `Inc` effectively looping over\n// the tape\ncase Inc 1 0 -\u003e Inc\n\n// Start the program from the state `Inc` with the tape `{ 1 1 0 1 }` and print\n// the tape on each state change\ntrace Inc { 1 1 0 1 }\n```\n\nThe trace of the execution of the above program:\n\n```\nInc: 1 1 0 1\n     ^\nInc: 0 1 0 1\n       ^\nInc: 0 0 0 1\n         ^\nHalt: 0 0 1 1\n            ^\n```\n\nYou can actually have several `trace` statements within a single file\nthat start at different states with different tapes. All of them are\ngoing to be executed sequentually.\n\nIf you need to start the head from a different position provide two\ntape sequences. First one is going to be treated as everything to the\nleft of the head, the second one is everything to the right:\n```\ntrace Loop { a b c } { 1 1 1 0 }\n//                     ^\n//                     The head starts here\ncase Loop 1 0 -\u003e Loop\n```\n\nThe trace of the above program\n\n```\nLoop: a b c 1 1 1 0\n            ^\nLoop: a b c 0 1 1 0\n              ^\nLoop: a b c 0 0 1 0\n                ^\nLoop: a b c 0 0 0 0\n                  ^\n```\n\n## Compound Expressions\n\nInstead of using just Symbols you can actually use Compound Expressions. The syntax of a Compond Expression is similar to [S-expressions](https://en.wikipedia.org/wiki/S-expression) but without the pair syntax. Just Symbols and Lists:\n\n```ebnf\n\u003cexpr\u003e ::= \u003csymbol\u003e\n\u003cexpr\u003e ::= \"(\" *\u003cexpr\u003e \")\"\n```\n\nHere is a simple example that iterates the Tape of Pairs of Numbers and swaps each pair until it reaches the delimiter `\u0026`:\n\n```js\ncase Swap (1 2) (2 1) -\u003e Swap\ncase Swap (2 3) (3 2) -\u003e Swap\ncase Swap (3 4) (4 3) -\u003e Swap\n\ntrace Swap { (1 2) (2 3) (3 4) \u0026 }\n```\n\nTrace of the above program:\n\n```\nSwap: (1 2) (2 3) (3 4) \u0026\n      ^~~~~\nSwap: (2 1) (2 3) (3 4) \u0026\n            ^~~~~\nSwap: (2 1) (3 2) (3 4) \u0026\n                  ^~~~~\nSwap: (2 1) (3 2) (4 3) \u0026\n                        ^\n```\n\nThe Compound Expressions don't really add that much to the Language by themselves. We could've written the above program like this and end up with basically this same result:\n\n```js\ncase Swap 1_2 2_1 -\u003e Swap\ncase Swap 2_3 3_2 -\u003e Swap\ncase Swap 3_4 4_3 -\u003e Swap\n\ntrace Swap { 1_2 2_3 3_4 \u0026 }\n```\n\nWhat they actually do is emphasize that the Symbols may contain additional information and enable use with extrating this information by using Sets and Universal Quantification.\n\n## Sets and Universal Quantification\n\nTula supports defining Sets (which are collections of Compound Expressions) and using [Universal Quantification](https://en.wikipedia.org/wiki/Universal_quantification) on those Sets to generate Rules automatically.\n\n```js\nlet Set { a b c }\nfor n in Set case S n 0 -\u003e S\n```\n\nThe above program will expand to\n\n```js\ncase S a 0 -\u003e S\ncase S b 0 -\u003e S\ncase S c 0 -\u003e S\n```\n\nNote that `for n in Set` quantifier is applied only to a single statement that follows it. To apply the quantifier to a block of statements use curly braces:\n\n```js\nlet Set { a b c }\nfor n in Set {\n    case S n 0 -\u003e S\n    case I n 1 -\u003e I\n}\n```\n\nThe above expands to:\n\n```js\ncase S a 0 -\u003e S\ncase I a 1 -\u003e I\ncase S b 0 -\u003e S\ncase I b 1 -\u003e I\ncase S c 0 -\u003e S\ncase I c 1 -\u003e I\n```\n\nYou can also nest the Quantifiers:\n\n```js\nlet Set { a b c }\nfor n in Set\nfor m in Set\ncase (S n) m 0 -\u003e S\n```\n\nThe above expands to this:\n\n```js\ncase (S a) a 0 -\u003e S\ncase (S a) b 0 -\u003e S\ncase (S a) c 0 -\u003e S\ncase (S b) a 0 -\u003e S\ncase (S b) b 0 -\u003e S\ncase (S b) c 0 -\u003e S\ncase (S c) a 0 -\u003e S\ncase (S c) b 0 -\u003e S\ncase (S c) c 0 -\u003e S\n```\n\nNested Quantifiers that iterate over the same set can be collapsed like so:\n\n```js\nlet Set { a b c }\nfor n m in Set\ncase (S n) m 0 -\u003e S\n```\n\n### Example\n\nHere is the example that iterates the Tape of Pairs of Numbers again but using Sets, Universal Quantifiers:\n\n```js\nlet Numbers { 1 2 3 4 }\n\n// For each `a` and `b` from the set of Numbers when in the state `Swap` and read `(a b)`\n// replace it with `(b a)` move the head to the right and stay in the `Swap` state loop\n// over the entire tape until you encounter something else\nfor a b in Numbers\ncase Swap (a b) (b a) -\u003e Swap\n\n// When in the state `Swap` and read `\u0026`, keep it as `\u0026` move the head to the right and `Halt`\ncase Swap \u0026 \u0026 -\u003e Halt\n\n// Execute and trace the program starting from state `Swap` with the tape that contains a\n// bunch of pairs of numbers.\ntrace Swap { (1 2) (2 3) (3 4) \u0026 }\n```\n\nThe trace of the above program:\n\n```\nSwap: (1 2) (2 3) (3 4) \u0026\n      ^~~~~\nSwap: (2 1) (2 3) (3 4) \u0026\n            ^~~~~\nSwap: (2 1) (3 2) (3 4) \u0026\n                  ^~~~~\nSwap: (2 1) (3 2) (4 3) \u0026\n                        ^\nHalt: (2 1) (3 2) (4 3) \u0026 \u0026\n                          ^\n```\n\nThe tape is infinite to the left and right filled with the first and last symbols correspondingly. In the example above the tape is filled with `\u0026` to the right, which is clearly indicated by the last trace output.\n\n## Anonymous Sets\n\nIt is not necessary to define the Sets upfront with the `let` keyword. You can use them directly in Universal Quantifiers:\n\n```js\nfor n in { a b c } {\n    case S n 0 -\u003e S\n}\n```\n\n## Set operations\n\nYou can combine the Sets with Union and Difference operations (`+` and `-` infix operators correspondingly)\n\n```js\nlet Numbers { 69 420 }\nlet Emoji { 😳 🍆 🔥 💯 }\n\n// For any Emoji or Numbers except 🍆 replace it with 🦀.\n// This effectively makes the program stop at 🍆 'cause there is no case for it.\nfor e in Numbers + Emoji - { 🍆 } {\n    case Crab e 🦀 -\u003e Crab\n}\n\ntrace Crab { 🔥 😳 69 420 🍆 }\n```\n\nThe trace of the above program\n\n```\nCrab: 🔥 😳 69 420 🍆\n      ^~\nCrab: 🦀 😳 69 420 🍆\n         ^~\nCrab: 🦀 🦀 69 420 🍆\n            ^~\nCrab: 🦀 🦀 🦀 420 🍆\n               ^~~\nCrab: 🦀 🦀 🦀 🦀 🍆\n                  ^~\n```\n\nThis kind of Set Expressions are also allowed in the Set Definitions:\n\n```js\nlet Numbers { 69 420 }\nlet Emoji { 😳 🍆 🔥 💯 }\nlet Anything_But_Eggplant ( Numbers + Emoji - { 🍆 } )  // Parenthesis for clarity\nlet Anything_But_Eggplant Numbers + Emoji - { 🍆 }  // Also works without parenthesis\n```\n\n### Cartesian Products\n\nOne special operation on the Sets allows you to create [Cartesian Products](https://en.wikipedia.org/wiki/Cartesian_product) of them. Here is how you can Skip all Pairs of Numbers without using nested Universal Quantifiers:\n\n```js\nlet Number { 1 2 3 4 }\nlet Pair Number * Number\n\nfor _ in Pair\ncase Skip _ _ -\u003e Skip\n\ntrace Skip { (1 2) (2 3) (3 4) \u0026 }\n```\n\nThe trace of the above program:\n\n```\nSkip: (1 2) (2 3) (3 4) \u0026\n      ^~~~~\nSkip: (1 2) (2 3) (3 4) \u0026\n            ^~~~~\nSkip: (1 2) (2 3) (3 4) \u0026\n                  ^~~~~\nSkip: (1 2) (2 3) (3 4) \u0026\n                        ^\n```\n\n## \"Magical\" Sets\n\nTula supports a special \"magical\" set `Integer` that is infinite (actually not, it's `i32`, but you get the point):\n\n```js\nfor a b in Integer\ncase Swap (a b) (b a) -\u003e Swap\n\ncase Swap \u0026 \u0026 -\u003e Halt\n\ntrace Swap { (69 420) (1337 7331) (42 37) \u0026 }\n```\n\nThe trace of the above program:\n\n```\nSwap: (69 420) (1337 7331) (42 37) \u0026\n      ^~~~~~~~\nSwap: (420 69) (1337 7331) (42 37) \u0026\n               ^~~~~~~~~~~\nSwap: (420 69) (7331 1337) (42 37) \u0026\n                           ^~~~~~~\nSwap: (420 69) (7331 1337) (37 42) \u0026\n                                   ^\nHalt: (420 69) (7331 1337) (37 42) \u0026 \u0026\n                                     ^\n```\n\nIt is actually impossible to expand the example because `Integer` is\njust too big. But the Interpreter still prints the trace\ninstantaneously because internally it does not actually generate any\ncases. It treats the Sets as Types and performs an efficient Type\nChecking and Pattern Matching to infer the `\u003cWrite\u003e`, `\u003cStep\u003e` and\n`\u003cNext\u003e` based on the current state of the Machine.\n\nYou can use `Integer` in Set Expressions:\n\n```js\n// For any Integer except 5 specifically keep moving to the right\nfor n in Integer - { 5 }\ncase Until_Five n n -\u003e Until_Five\ntrace Until_Five { 1 2 3 4 5 6 7 8 }\n```\n\nTrace of the above program\n\n```\nUntil_Five: 1 2 3 4 5 6 7 8\n            ^\nUntil_Five: 1 2 3 4 5 6 7 8\n              ^\nUntil_Five: 1 2 3 4 5 6 7 8\n                ^\nUntil_Five: 1 2 3 4 5 6 7 8\n                  ^\nUntil_Five: 1 2 3 4 5 6 7 8\n                    ^\n```\n\nThis specifically makes the program halt at `5` because it does not have a case for it.\n\nAdditional Magical Sets include:\n- Real - Set of Real Numbers (corresponds to f32 in Rust)\n- String - Set of Strings (symbols wrapped in single quotes `'`)\n\n## Eval Expressions (EEs)\n\nOnce you've got two Integers the most logical thing to do would be to sum them up:\n\n```js\ntrace Sum { (1 2) . }\nfor a b in Integer\ncase Sum (a b) [a + b] . Halt\n```\n\nThe trace of the above program:\n\n```\nSum: (1 2) .\n     ^~~~~\nHalt: 3 .\n      ^\n```\n\nIn the program above `[a + b]` is an Eval Expression (EE). It is a kind of a Compound Expression. Evaluating the Eval Expressions in the Compound Expressions is called Forcing them.\n\nBellow is a program that fills up the Tape with Fibonacci numbers up until a delimiter `\u0026`:\n\n```js\nfor a in Integer\ncase Fib a a -\u003e (Fib a)\n\nfor a b in Integer {\n    case (Fib a)   b b       -\u003e (Fib a b)\n    case (Fib a b) 0 [a + b] .  (Fib b)\n}\n\ntrace Fib { 0 1 0 0 0 0 \u0026 }\n```\n\nThe trace of the above program:\n\n```\nFib: 0 1 0 0 0 0 \u0026\n     ^\n(Fib 0): 0 1 0 0 0 0 \u0026\n           ^\n(Fib 0 1): 0 1 0 0 0 0 \u0026\n               ^\n(Fib 1): 0 1 1 0 0 0 \u0026\n             ^\n(Fib 1 1): 0 1 1 0 0 0 \u0026\n                 ^\n(Fib 1): 0 1 1 2 0 0 \u0026\n               ^\n(Fib 1 2): 0 1 1 2 0 0 \u0026\n                   ^\n(Fib 2): 0 1 1 2 3 0 \u0026\n                 ^\n(Fib 2 3): 0 1 1 2 3 0 \u0026\n                     ^\n(Fib 3): 0 1 1 2 3 5 \u0026\n                   ^\n(Fib 3 5): 0 1 1 2 3 5 \u0026\n                       ^\n```\n\nThe syntax of an EE is `\"[\" \u003cexpr\u003e \u003cexpr\u003e \u003cexpr\u003e \"]\"` where `\u003cexpr\u003e` is a Compound Expression.\n- First `\u003cexpr\u003e` is the Left-Hand Side operand.\n- Second `\u003cexpr\u003e` is the operator.\n- Third `\u003cexpr\u003e` is the Right-Hand Side operand.\n\nMaking operands Compound Expressions allows for nesting like this `[[a % 15] == 0]`. Such EEs are Forced Recursively starting from the Inner ones.\n\nSince the operator is also a Compound Expression it is possible to substitute them as well:\n\n```js\nlet Op { + - }\n\nfor a b in Integer\nfor op in Op\ncase Eval (a b op) [a op b] -\u003e Eval\n\ntrace Eval { (34 35 +) (500 80 -) \u0026 }\n```\n\nThe trace of the above program:\n\n```\nEval: (34 35 +) (500 80 -) \u0026\n      ^~~~~~~~~\nEval: 69 (500 80 -) \u0026\n         ^~~~~~~~~~\nEval: 69 420 \u0026\n             ^\n```\n\n- Supported Integer operations: `+`, `-`, `*`, `/`, `%`, `\u003c`, `\u003c=`, `\u003e`, `\u003e=`, `==`, `!=`.\n- Supported Real operations: `+`, `-`, `*`, `/`, `%`, `\u003c`, `\u003c=`, `\u003e`, `\u003e=`, `==`, `!=`.\n- Supported String operations: `+`, `\u003c`, `\u003c=`, `\u003e`, `\u003e=`, `==`, `!=`.\n- Supported Boolean operations: `\u0026\u0026`, `||`, `==`, `!=` (Boolean is either symbol `true` or symbol `false`).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsoding%2Ftula","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftsoding%2Ftula","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsoding%2Ftula/lists"}