vp2py 3.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vp2py-3.0.0/PKG-INFO ADDED
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.3
2
+ Name: vp2py
3
+ Version: 3.0.0
4
+ Summary: An implementation of a Varphi-to-Python compiler.
5
+ Author: Varphi
6
+ Author-email: Varphi <support@varphi-lang.com>
7
+ Requires-Dist: typer
8
+ Requires-Dist: varphi-devkit>=3.0.0
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+
12
+ # The Varphi-to-Python Compiler
13
+ This is the source code repository for a compiler that converts the [Varphi programming language](https://varphi-lang.com/) into Python.
vp2py-3.0.0/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # The Varphi-to-Python Compiler
2
+ This is the source code repository for a compiler that converts the [Varphi programming language](https://varphi-lang.com/) into Python.
@@ -0,0 +1,28 @@
1
+ [project]
2
+ name = "vp2py"
3
+ version = "3.0.0"
4
+ description = "An implementation of a Varphi-to-Python compiler."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = [
8
+ "typer",
9
+ "varphi-devkit>=3.0.0",
10
+ ]
11
+
12
+ [[project.authors]]
13
+ name = "Varphi"
14
+ email = "support@varphi-lang.com"
15
+
16
+ [project.scripts]
17
+ vp2py = "vp2py.cli:main"
18
+
19
+ [build-system]
20
+ requires = ["uv_build>=0.9.18,<0.10.0"]
21
+ build-backend = "uv_build"
22
+
23
+ [dependency-groups]
24
+ dev = [
25
+ "black",
26
+ "pytest",
27
+ "vermin",
28
+ ]
@@ -0,0 +1,27 @@
1
+ [project]
2
+ name = "vp2py"
3
+ version = "3.0.0"
4
+ description = "An implementation of a Varphi-to-Python compiler."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Varphi", email = "support@varphi-lang.com" }
8
+ ]
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "typer",
12
+ "varphi-devkit>=3.0.0",
13
+ ]
14
+
15
+ [project.scripts]
16
+ vp2py = "vp2py.cli:main"
17
+
18
+ [build-system]
19
+ requires = ["uv_build>=0.9.18,<0.10.0"]
20
+ build-backend = "uv_build"
21
+
22
+ [dependency-groups]
23
+ dev = [
24
+ "black",
25
+ "pytest",
26
+ "vermin"
27
+ ]
@@ -0,0 +1,3 @@
1
+ from .compiler import VarphiToPythonCompiler
2
+
3
+ __all__ = ["VarphiToPythonCompiler"]
@@ -0,0 +1,38 @@
1
+ from pathlib import Path
2
+ import typer
3
+
4
+
5
+ def vp2py(
6
+ input_file: Path = typer.Argument(
7
+ ...,
8
+ exists=True,
9
+ file_okay=True,
10
+ dir_okay=False,
11
+ readable=True,
12
+ resolve_path=True,
13
+ help="Path to input Varphi source file",
14
+ ),
15
+ debug: bool = typer.Option(
16
+ False,
17
+ "--debug/--no-debug",
18
+ help="Compile with debugging capabilities",
19
+ ),
20
+ ):
21
+ """Compile a Varphi source code file to Python"""
22
+ from .compiler import VarphiToPythonCompiler
23
+
24
+ compiler = VarphiToPythonCompiler()
25
+ if debug:
26
+ compiler.toggle_debug()
27
+
28
+ # Read file, compile, and print result to stdout
29
+ result = compiler.compile(input_file.read_text(encoding="utf-8"))
30
+ typer.echo(result)
31
+
32
+
33
+ def main():
34
+ typer.run(vp2py)
35
+
36
+
37
+ if __name__ == "__main__":
38
+ main()
@@ -0,0 +1,110 @@
1
+ from varphi_devkit import (
2
+ VarphiCompiler,
3
+ BuiltinSymbol,
4
+ Variable,
5
+ Direction,
6
+ Character,
7
+ ReadWriteTupleElement,
8
+ )
9
+
10
+ TEMPLATE = """\
11
+ from vp2py.lib import State, main
12
+ from varphi_devkit import BuiltinSymbol, Direction, Variable, Character, VarphiTransition
13
+
14
+ # --- State Registry ---
15
+ state_registry = {{
16
+ {state_registry_entries}
17
+ }}
18
+
19
+ # --- Transition Definitions ---
20
+ {instruction_definitions}
21
+
22
+ # --- Runtime Setup ---
23
+ initial_state = state_registry['{initial_state}']
24
+ k = {num_tapes}
25
+
26
+ if __name__ == "__main__":
27
+ import argparse
28
+ parser = argparse.ArgumentParser(description="Varphi Turing Machine Runtime")
29
+ parser.add_argument("--debug", action="store_true", default={debug_mode}, help="Enable step-by-step debug mode")
30
+ parser.add_argument("-b", "--blank-char", type=str, default="_", help="Character representing a BLANK cell in input/output (Default: _)")
31
+ args = parser.parse_args()
32
+
33
+ # Pass the state_registry to main!
34
+ main(k, initial_state, state_registry, args.debug, args.blank_char)
35
+ """
36
+
37
+
38
+ class VarphiToPythonCompiler(VarphiCompiler):
39
+ debug_mode: bool
40
+
41
+ def __init__(self):
42
+ super().__init__()
43
+ self.debug_mode = False
44
+
45
+ def toggle_debug(self):
46
+ self.debug_mode = not self.debug_mode
47
+
48
+ def _format_symbol(self, s: ReadWriteTupleElement) -> str:
49
+ """Converts devkit symbol objects to valid strings to inject into the compiled code."""
50
+ if s == BuiltinSymbol.BLANK:
51
+ return "BuiltinSymbol.BLANK"
52
+ if isinstance(s, Variable):
53
+ return f"Variable({s.id})"
54
+ if isinstance(s, Character):
55
+ return f"Character({repr(s.value)})"
56
+ raise ValueError(f"Unknown symbol type: {type(s)}")
57
+
58
+ def _format_direction(self, d: Direction) -> str:
59
+ """Converts devkit direction enums to valid Python code strings."""
60
+ return f"Direction.{d.name}"
61
+
62
+ def _generate_compiled_program(self) -> str:
63
+ # Use the devkit's pre-discovered states directly
64
+ all_states = self.states
65
+
66
+ # Build the dictionary mapping strings to the State objects directly to avoid keyword collisions
67
+ registry_entries = "\n".join(
68
+ f" '{name}': State('{name}')," for name in all_states
69
+ )
70
+
71
+ # Map the pre-sorted IR transitions to VarphiTransition instantiations
72
+ instructions_code = []
73
+ for transitions in self.ir.values():
74
+ for t in transitions:
75
+ read_str = "(" + ", ".join(
76
+ self._format_symbol(s) for s in t.read_symbols
77
+ )
78
+ read_str += ",)" if len(t.read_symbols) == 1 else ")"
79
+
80
+ write_str = "(" + ", ".join(
81
+ self._format_symbol(s) for s in t.write_symbols
82
+ )
83
+ write_str += ",)" if len(t.write_symbols) == 1 else ")"
84
+
85
+ shift_str = "(" + ", ".join(
86
+ self._format_direction(d) for d in t.shift_directions
87
+ )
88
+ shift_str += ",)" if len(t.shift_directions) == 1 else ")"
89
+
90
+ code = (
91
+ f"state_registry['{t.current_state}'].add_transition(\n"
92
+ f" VarphiTransition(\n"
93
+ f" current_state='{t.current_state}',\n"
94
+ f" read_symbols={read_str},\n"
95
+ f" next_state='{t.next_state}',\n"
96
+ f" write_symbols={write_str},\n"
97
+ f" shift_directions={shift_str},\n"
98
+ f" line_number={t.line_number}\n"
99
+ f" )\n"
100
+ f")"
101
+ )
102
+ instructions_code.append(code)
103
+
104
+ return TEMPLATE.format(
105
+ state_registry_entries=registry_entries,
106
+ instruction_definitions="\n\n".join(instructions_code),
107
+ initial_state=self.initial_state,
108
+ num_tapes=self._tape_count,
109
+ debug_mode=self.debug_mode,
110
+ )
@@ -0,0 +1,12 @@
1
+ from .model import State, Tape, Head, TuringMachine
2
+ from .functions import main
3
+ from .exceptions import VarphiRuntimeError
4
+
5
+ __all__ = [
6
+ "State",
7
+ "Tape",
8
+ "Head",
9
+ "TuringMachine",
10
+ "main",
11
+ "VarphiRuntimeError",
12
+ ]
@@ -0,0 +1,2 @@
1
+ class VarphiRuntimeError(Exception):
2
+ pass
@@ -0,0 +1,44 @@
1
+ import sys
2
+ from .io import VarphiIO, DebugView
3
+ from .model import State, TuringMachine
4
+
5
+
6
+ def main(
7
+ k: int,
8
+ initial_state: State,
9
+ state_registry: dict,
10
+ debug: bool,
11
+ blank_char: str = "_",
12
+ ) -> None:
13
+ io = VarphiIO.from_stdin(blank_char)
14
+ tm = TuringMachine(k, io.tapes, initial_state, state_registry)
15
+ time_complexity = 0
16
+ SEPARATOR = "—" * 60
17
+
18
+ while tm.peek():
19
+ if debug:
20
+ print(f"\n{SEPARATOR}", file=sys.stderr)
21
+ print(
22
+ f"STEP {time_complexity + 1} [State: {tm.state.name}]", file=sys.stderr
23
+ )
24
+ print(f"{SEPARATOR}", file=sys.stderr)
25
+ print(DebugView(tm, blank_char), file=sys.stderr)
26
+ try:
27
+ input("\n>> Press ENTER to step forward...")
28
+ except (KeyboardInterrupt, EOFError):
29
+ print("\nInterrupted.", file=sys.stderr)
30
+ return
31
+
32
+ tm.step()
33
+ time_complexity += 1
34
+
35
+ if sys.stdout.isatty():
36
+ print(f"\n{SEPARATOR}", file=sys.stderr)
37
+ print(f"HALTED at state '{tm.state.name}'", file=sys.stderr)
38
+ print(f"Time taken: {time_complexity} steps", file=sys.stderr)
39
+ print(
40
+ f"Space used: {sum(head.space_complexity() for head in tm.heads)} cells",
41
+ file=sys.stderr,
42
+ )
43
+
44
+ VarphiIO(tm.tapes).print(blank_char)
@@ -0,0 +1,111 @@
1
+ from __future__ import annotations
2
+ import sys
3
+ from dataclasses import dataclass
4
+ from typing import Tuple
5
+ from .model import Tape, TuringMachine
6
+ from .exceptions import VarphiRuntimeError
7
+ from varphi_devkit import BuiltinSymbol, Character
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class VarphiIO:
12
+ tapes: Tuple[Tape, ...]
13
+
14
+ @staticmethod
15
+ def from_stdin(blank_char: str) -> VarphiIO:
16
+ if sys.stdin.isatty():
17
+ print("Number of input tapes: ", end="", file=sys.stderr, flush=True)
18
+
19
+ header = sys.stdin.readline()
20
+ if not header:
21
+ return VarphiIO(tuple())
22
+
23
+ try:
24
+ num_tapes = int(header.strip())
25
+ except ValueError:
26
+ raise VarphiRuntimeError(
27
+ f'Runtime Error: Expected an integer for number of tapes, got "{header.strip()}".'
28
+ )
29
+ if num_tapes <= 0:
30
+ raise VarphiRuntimeError(
31
+ f'Runtime Error: Expected a positive number of tapes, got "{num_tapes}".'
32
+ )
33
+
34
+ tapes = []
35
+ for i in range(num_tapes):
36
+ if sys.stdin.isatty():
37
+ print(f"Tape {i + 1}: ", end="", file=sys.stderr, flush=True)
38
+
39
+ line = sys.stdin.readline().strip("\r\n")
40
+
41
+ parsed_tape = []
42
+ for char in line:
43
+ if char == blank_char:
44
+ parsed_tape.append(BuiltinSymbol.BLANK)
45
+ else:
46
+ parsed_tape.append(Character(char))
47
+
48
+ tapes.append(Tape(parsed_tape))
49
+
50
+ return VarphiIO(tuple(tapes))
51
+
52
+ def print(self, blank_char: str) -> None:
53
+ show_labels = sys.stdout.isatty()
54
+ if show_labels:
55
+ print("Number of tapes: ", end="", file=sys.stderr, flush=True)
56
+
57
+ print(len(self.tapes))
58
+
59
+ for i, tape in enumerate(self.tapes):
60
+ if show_labels:
61
+ print(f"Tape {i + 1}: ", end="", file=sys.stderr, flush=True)
62
+ print(tape.to_string(blank_char))
63
+
64
+
65
+ @dataclass
66
+ class DebugView:
67
+ machine: TuringMachine
68
+ blank_char: str
69
+
70
+ def __str__(self) -> str:
71
+ line_number = (
72
+ self.machine._next_transition.line_number
73
+ if self.machine._next_transition
74
+ else "?"
75
+ )
76
+ lines = [f"State: {self.machine.state.name} (Line {line_number})"]
77
+
78
+ max_radius = 0
79
+ for head in self.machine.heads:
80
+ if not head.tape._tape:
81
+ continue
82
+
83
+ dist_left = (
84
+ abs(head.index - head.tape._min_idx)
85
+ if head.tape._min_idx is not None
86
+ else 0
87
+ )
88
+ dist_right = (
89
+ abs(head.index - head.tape._max_idx)
90
+ if head.tape._max_idx is not None
91
+ else 0
92
+ )
93
+ max_radius = max(max_radius, dist_left, dist_right)
94
+
95
+ for i, head in enumerate(self.machine.heads):
96
+ start = head.index - max_radius
97
+ end = head.index + max_radius
98
+
99
+ chars = []
100
+ for idx in range(start, end + 1):
101
+ val = head.tape._tape.get(idx, BuiltinSymbol.BLANK)
102
+ val_str = self.blank_char if val == BuiltinSymbol.BLANK else val.value
103
+
104
+ if idx == head.index:
105
+ chars.append(f"[{val_str}]")
106
+ else:
107
+ chars.append(val_str)
108
+
109
+ lines.append(f"Tape {i + 1}: {''.join(chars)}")
110
+
111
+ return "\n".join(lines)
@@ -0,0 +1,274 @@
1
+ from __future__ import annotations
2
+ from typing import Dict, Tuple, Optional, Set
3
+ import random
4
+ from collections import defaultdict
5
+
6
+ from varphi_devkit import (
7
+ BuiltinSymbol,
8
+ Direction,
9
+ Variable,
10
+ Character,
11
+ ReadWriteTupleElement,
12
+ VarphiTransition,
13
+ )
14
+
15
+
16
+ class State:
17
+ """A state in the Turing machine."""
18
+
19
+ name: str
20
+ transitions: list[VarphiTransition]
21
+
22
+ def __init__(self, name: str) -> None:
23
+ """Initialize a new state."""
24
+ self.name = name
25
+ self.transitions = []
26
+
27
+ def add_transition(self, transition: VarphiTransition) -> None:
28
+ """Append a VarphiTransition to the state."""
29
+ self.transitions.append(transition)
30
+
31
+ def get_transition(
32
+ self, tape_readings: tuple[Character | BuiltinSymbol, ...]
33
+ ) -> Optional[Tuple[VarphiTransition, Dict[Variable, Character | BuiltinSymbol]]]:
34
+ """
35
+ Evaluate current tape readings against the state's transitions to find the highest-specificity match.
36
+
37
+ Args:
38
+ tape_readings: A tuple of the actual characters currently read by the machine's heads.
39
+
40
+ Returns:
41
+ A tuple containing the winning VarphiTransition and its variable bindings, or None if the machine halts as a result of this situation.
42
+ """
43
+ candidates = []
44
+ best_specificity = None
45
+
46
+ for transition in self.transitions:
47
+ bindings = self._check_match(transition.read_symbols, tape_readings)
48
+
49
+ if bindings is not None:
50
+ # The transition rule applies
51
+ current_specificity = transition.specificity
52
+
53
+ if best_specificity is None:
54
+ best_specificity = current_specificity
55
+
56
+ # Once the best (lowest) specificity is set, we will only consider
57
+ # scores that are the same as that scores
58
+ # Since the transitions have been sorted by the devkit by specificity, we can
59
+ # stop collecting transitions once we see one that has a higher specificity than the
60
+ # best specificity
61
+ elif current_specificity > best_specificity:
62
+ break
63
+
64
+ candidates.append((transition, bindings))
65
+
66
+ if not candidates:
67
+ # No transition rules apply, so the machine will halt
68
+ return None
69
+
70
+ # Resolve nondeterministic ties
71
+ return random.choice(candidates)
72
+
73
+ def _check_match(
74
+ self,
75
+ pattern: tuple[ReadWriteTupleElement, ...],
76
+ readings: tuple[Character | BuiltinSymbol, ...],
77
+ ) -> Optional[Dict[Variable, Character | BuiltinSymbol]]:
78
+ """
79
+ Verify if a given pattern matches the current tape readings and extract variable bindings.
80
+
81
+ Returns the variable bindings (or an empty dictionary if there aren't variables) if the pattern applies,
82
+ otherwise returns None
83
+ """
84
+ bindings = {}
85
+
86
+ for pattern_symbol, read_symbol in zip(pattern, readings):
87
+ if isinstance(pattern_symbol, Variable):
88
+ if pattern_symbol not in bindings:
89
+ # Bind the variable to the read symbol
90
+ bindings[pattern_symbol] = read_symbol
91
+ elif bindings[pattern_symbol] != read_symbol:
92
+ # We've bound the variable previously, but there's a mismatch
93
+ # This rule cannot possibly apply
94
+ return None
95
+ elif pattern_symbol != read_symbol:
96
+ # The pattern and read symbols don't match up, so the rule doesn't apply
97
+ return None
98
+ # If we're here, the rule applies. Return the variable bindings
99
+ return bindings
100
+
101
+ def __repr__(self) -> str:
102
+ return f"State({self.name})"
103
+
104
+
105
+ class Tape:
106
+ """Represents a two-way infinite tape of the Turing machine."""
107
+
108
+ _tape: defaultdict[int, Character | BuiltinSymbol]
109
+ _min_idx: Optional[int]
110
+ _max_idx: Optional[int]
111
+
112
+ def __init__(self, initial_values: list[Character | BuiltinSymbol]) -> None:
113
+ self._tape = defaultdict(lambda: BuiltinSymbol.BLANK)
114
+ self._min_idx = None
115
+ self._max_idx = None
116
+
117
+ for i, val in enumerate(initial_values):
118
+ if val != BuiltinSymbol.BLANK:
119
+ self[i] = val
120
+
121
+ def __getitem__(self, index: int) -> Character | BuiltinSymbol:
122
+ self._update_bounds(index)
123
+ return self._tape[index]
124
+
125
+ def __setitem__(self, index: int, value: Character | BuiltinSymbol) -> None:
126
+ self._update_bounds(index)
127
+ self._tape[index] = value
128
+
129
+ def _update_bounds(self, index: int) -> None:
130
+ if self._min_idx is None or self._max_idx is None:
131
+ self._min_idx = index
132
+ self._max_idx = index
133
+ else:
134
+ self._max_idx = max(self._max_idx, index)
135
+ self._min_idx = min(self._min_idx, index)
136
+
137
+ def to_string(self, blank_char: str) -> str:
138
+ if self._min_idx is None or self._max_idx is None:
139
+ return ""
140
+
141
+ min_i, max_i = self._min_idx, self._max_idx
142
+
143
+ while max_i >= min_i and self._tape[max_i] == BuiltinSymbol.BLANK:
144
+ max_i -= 1
145
+ while min_i <= max_i and self._tape[min_i] == BuiltinSymbol.BLANK:
146
+ min_i += 1
147
+
148
+ if min_i > max_i:
149
+ return ""
150
+
151
+ return "".join(
152
+ blank_char if self._tape[i] == BuiltinSymbol.BLANK else self._tape[i].value
153
+ for i in range(min_i, max_i + 1)
154
+ )
155
+
156
+ @property
157
+ def is_empty(self) -> bool:
158
+ return self._min_idx is None
159
+
160
+
161
+ class Head:
162
+ """Represents a read/write head positioned on a specific tape."""
163
+
164
+ tape: Tape
165
+ index: int
166
+ user_input_cell_range: Optional[tuple[int, int]]
167
+ new_accessed_cells: Set[int]
168
+
169
+ def __init__(self, tape: Tape) -> None:
170
+ self.tape = tape
171
+ self.index = 0
172
+ self.new_accessed_cells = set()
173
+
174
+ if tape.is_empty:
175
+ self.user_input_cell_range = None
176
+ else:
177
+ self.user_input_cell_range = (tape._min_idx, tape._max_idx)
178
+
179
+ def move(self, direction: Direction) -> None:
180
+ if direction == Direction.LEFT:
181
+ self.index -= 1
182
+ elif direction == Direction.RIGHT:
183
+ self.index += 1
184
+
185
+ def read(self) -> Character | BuiltinSymbol:
186
+ self._check_access()
187
+ return self.tape[self.index]
188
+
189
+ def write(self, value: Character | BuiltinSymbol) -> None:
190
+ self._check_access()
191
+ self.tape[self.index] = value
192
+
193
+ def _check_access(self):
194
+ if self.user_input_cell_range is None:
195
+ self.new_accessed_cells.add(self.index)
196
+ else:
197
+ start, end = self.user_input_cell_range
198
+ if self.index < start or self.index > end:
199
+ self.new_accessed_cells.add(self.index)
200
+
201
+ def space_complexity(self) -> int:
202
+ return len(self.new_accessed_cells)
203
+
204
+
205
+ class TuringMachine:
206
+ tapes: tuple[Tape, ...]
207
+ heads: tuple[Head, ...]
208
+ state: State
209
+ state_registry: dict[str, State]
210
+ _next_transition: Optional[VarphiTransition]
211
+ _current_bindings: dict[Variable, Character | BuiltinSymbol]
212
+
213
+ def __init__(
214
+ self,
215
+ k: int,
216
+ tapes: tuple[Tape, ...],
217
+ initial_state: State,
218
+ state_registry: dict[str, State],
219
+ ) -> None:
220
+ """
221
+ Initialize the machine.
222
+
223
+ Args:
224
+ k: The required number of tapes for this machine configuration.
225
+ tapes: A tuple of initial tapes provided by user input. Missing tapes are generated as blanks.
226
+ initial_state: The entry state of the machine.
227
+ state_registry: A dictionary mapping state names to live State objects.
228
+ """
229
+ self.tapes = tapes # Keep all tapes so they are printed at the end
230
+ missing_count = k - len(self.tapes)
231
+ if missing_count > 0:
232
+ self.tapes += tuple(Tape([]) for _ in range(missing_count))
233
+
234
+ # We only make heads for the first k tapes (we safely ignore excess tapes during execution)
235
+ self.heads = tuple(Head(t) for t in self.tapes[:k])
236
+ self.state = initial_state
237
+ self.state_registry = state_registry
238
+ self._next_transition = None
239
+ self._current_bindings = {}
240
+
241
+ def peek(self) -> bool:
242
+ """
243
+ Preview the next transition by evaluating current tape readings against the state.
244
+ This will select and "arm" the machine with the next transition, but won't execute it
245
+ Return True if a next transition has been selected, False if the machine will halt
246
+ """
247
+ reads = tuple(h.read() for h in self.heads)
248
+ result = self.state.get_transition(reads)
249
+
250
+ if result is None:
251
+ # No transition applies; halt
252
+ self._next_transition = None
253
+ self._current_bindings = {}
254
+ return False
255
+
256
+ self._next_transition, self._current_bindings = result
257
+ return True
258
+
259
+ def step(self) -> None:
260
+ """Execute the transition locked in by the previous call to peek()."""
261
+ transition = self._next_transition
262
+ bindings = self._current_bindings
263
+
264
+ if transition is None:
265
+ return
266
+
267
+ # Perform the string-to-object dictionary lookup here
268
+ self.state = self.state_registry[transition.next_state]
269
+
270
+ for i, head in enumerate(self.heads):
271
+ sym = transition.write_symbols[i]
272
+ val_to_write = bindings[sym] if isinstance(sym, Variable) else sym
273
+ head.write(val_to_write)
274
+ head.move(transition.shift_directions[i])