// Implementation of a Turing Maching in Souffle

.type Alphabet <: symbol
.type State <: symbol
.type Direction <: symbol

//
// Tape of the Turing Machine
//
// A triple (pos, symbol, step) enumerates all symbols of the
// tape at the i-th position in the j-th computational step.
//

.decl Tape(position:number, symbol:Alphabet, step:number)
.output Tape()

// write new symbol from a successful transition.
Tape(position, newSymbol, step + 1) :-
   Tape(position, oldSymbol, step),
   Configuration(position, currentState, step),
   Transition(currentState, _, oldSymbol, newSymbol, _).

// copy tape symbol from previous step at previously written
// position, if not altered by current state transition.
Tape(position, symbol, step + 1) :-
   Tape(position, symbol, step),
   Configuration(writePosition, _, step),
   position != writePosition.

// add blank symbol to the left of the tape, if the position has not been
// written before.
Tape(position, "b", step + 1) :-
   Configuration(position, _, step + 1),
   TapeLimits(step, left, _),
   position < left.

// add blank symbol to the right of the tape, if the position
// has not been written before.
Tape(position, "b", step + 1) :-
   Configuration(position, _, step + 1),
   TapeLimits(step, _, right),
   position > right.

//
// Configuration of the Turing Machine
//
// The configuration defines the current tape position,
// the current state, and the computational step.
//

.decl Configuration(position: number, state:State, step:number)
.output Configuration()

Configuration(position - 1, nextState, step + 1) :-
   Tape(position, oldSymbol, step),
   Configuration(position, currentState, step),
   Transition(currentState, nextState, oldSymbol, _, "L").

Configuration(position + 1, nextState, step + 1) :-
   Tape(position, oldSymbol, step),
   Configuration(position, currentState, step),
   Transition(currentState, nextState, oldSymbol, _, "R").

//
// TapeLimits of Turing Machine
//
// The left and right-most position already written positions
// on the tape. This relation is necessary for producing blank
// symbols for new positions on the tape.
//
.decl TapeLimits(step:number, left:number, right:number)
.output TapeLimits()

// tape limit has not changed
TapeLimits(step + 1, left, right) :-
   TapeLimits(step, left, right),
   Configuration(position, _, step + 1),
   left <= position,
   position <= right.

// tape limit has changed to the left
TapeLimits(step + 1, position, right) :-
   TapeLimits(step, left, right),
   Configuration(position, _, step + 1),
   position < left.

// tape limit has changed to the right
TapeLimits(step + 1, left, position) :-
   TapeLimits(step, left, right),
   Configuration(position, _, step + 1),
   right < position.

//
// Partial Transition Function of Turing Machine
//
// Defines the transition from one state to another,
// the symbols that is necesary to transit, the new symbol that
// is written, and in which direction the turing machine is moving.
// Note that the turing machine stops if no new state can be found.
//

.decl Transition(src:State, dest:State, read:Alphabet, write:Alphabet, d:Direction)

Transition("S0", "S0", "x", "y", "R").
Transition("S0", "S1", "y", "x", "L").
Transition("S1", "S1", "y", "x", "L").

//
// Initial State of the tape
//

// Tape: 001

Tape(0, "x", 0). // first position on tape set to symbol "x"
Tape(1, "x", 0). // second position on tape set to symbol "x"
Tape(2, "y", 0). // third position on tape set to symbol "y"

// Limits of tape for initial state
TapeLimits(0, 0, 2).

// Initial configuration of turing machine
Configuration(0, "S0", 0).
