#programming-language #automata #language #turing #symbols

bin+lib amethyst_geode

Turing Machine Programming Language Interpreter

3 stable releases

new 1.0.2 May 17, 2025

#138 in Programming languages

Download history 166/week @ 2025-05-12

166 downloads per month

MPL-2.0 license

455KB
3.5K SLoC

Rust 2K SLoC // 0.0% comments Haskell 1.5K SLoC // 0.1% comments C 62 SLoC // 0.1% comments

Amethyst

Amethyst is a programming language where you write everything using Turing machines. Its goal is to help grow the understanding of this popular and powerful computational model.

Installation

If you have cargo, you can install the binary directly from crates.io

cargo install amethyst_geode

There are also precompiled binaries available for Windows, macOS, and Linux.

Running a .myst file

geode run code/add.myst -input 110+01 -o -limit 500 -tape

Or you can specify the compilation flags in a separate file:

geode r code/simple.myst -c config/example.txt -start verify

Run configuration flags

Flag Shorthand Argument Default
-input -i initial tape content @
-output -o false
-tape -t false
-bound -b max number of cells none
-iterations / -limit -iter / -l max number of steps 1200
-debug -d false
-config -c path/to/config.txt

Wildcard symbol

As a quality of life feature, amethyst supports the wildcard pattern _ in transitions. For a state, when a transition has a wildcard as the read symbol, all tape symbols that do not have an already matching transition will match the wildcard and the cell will be rewriten with the write symbol. When both the reading and writing symbols are wildcards, the cell will not be rewriten for all tape symbol that match. This functionality heavily helps with code readability and **avoiding repetition**.

Arrow states

Arrow states are states that do nothing but move to another state. The following notation: state1 -> state2; is syntactic sugar for state1 { _ / _, N -> state2}. This pattern appeared often when working with components, especially in the case of multiple macros where one final state simply lead into the next input state.

Components

Components are a way to reuse code. Syntactically, they look like function arguments for the automata, but they are static copies of the specified turing machines.

E.g. Let's say we have written a turing machine that adds two numbers together and we would like to reuse this code. We can add this 'add' automaton as a component to the 'main' automaton like so automaton main(add a){...}. We can consider the 'a' component of a static turing machine with the blueprint of 'add'. We use 'a' as a black box, concerning ourselves only with its input a.initial_state and output a.accept_states & a.reject_states.

Each component's initial state is exposed to the parent turing machine. Final states (accepting and rejecting) of the component automata have to be rewriten in order to continue the execution of the parent machine.

Macros

Macros are a way to reduce boilerplate code, whether that be a very common and useful machine or a machine with many repetitive states.

E.g. To move exactly 8 cells to the right we would need 8 states with each state having the only job of making a right move and going to the next state.

These are not functions with variables, they are just syntactic sugar that gets expanded into a specific turing machine at compile time. Each macro-machine will have one initial state (input) and two final states (accept and reject). Some macros (such as move or shift) do not use the reject state since there's no way they can fail.

The following macros are present so far:

  • complement (automaton)
    • accepts only if the automaton rejects and vice versa
  • intersect (automaton, automaton)
    • takes two or more automata and accepts the input only if all of the automata accept said input
  • reunion (automaton, automaton, automaton)
    • takes two or more automata and accepts the input only if any of the automata accept said input
    • the three macros above are not useful in terms of output, but can be used for recognizing formal languages
  • chain (automaton, automaton, automaton)
    • takes two or more automata and chains them like so:
      • macro input state -> first automaton's initial state
      • nth automaton's accept state -> (n+1)th automaton's initial state
      • last automaton's accept state -> macro accept state
      • all automata reject states -> macro reject state
  • repeat (7, automaton)
    • equivalent with a chain of 7 automata of the same type
  • move (moveSymbol, number)
    • moves the head left or right a number of positions
  • override (moveSymbol, number, tapeSymbol)
    • a move that also replaces the tape symbols it encounters with the given symbol
  • place (string)
    • override to the right with a string of tape symbols, useful for writing simple output
  • shift (moveSymbol, number)
    • a right shift inserts a number of cells to the right of the current position (all of the cells to the right of the head shift right)
    • a left shift deletes a number of cells to the left of the current position (all of the cells to the right of the head shift left)

Example

hello world example

geode run code/hello.myst -input WORLD -debug -output -tape

You can find more code examples here

Dependencies

~0–7MB
~33K SLoC