Porting to Python 3 and adding a few tests.
This commit is contained in:
parent
dc4e10aaff
commit
599b1a12d4
27
README.md
27
README.md
@ -11,28 +11,25 @@ the memory pointer. Have a look at the instruction set:
|
||||
|
||||
Instruction set:
|
||||
|
||||
```
|
||||
Lx : Move memory pointer left x chars (defaults to 1).
|
||||
Rx : Move memory pointer right x chars (defaults to 1).
|
||||
|
||||
Nx : Goto the next char that is x with the instruction pointer.
|
||||
Px : Goto the prev char that is x with the instruction pointer.
|
||||
O : Output current value under memory pointer.
|
||||
I : Write from stdin to the current memory pointer.
|
||||
K : Decrement the value under the current memory pointer.
|
||||
H : Increment the value under the current memory pointer.
|
||||
Jx : Jump with the instruction pointer to the previous 'x' char if the value under the current memory pointer != 0. (Jumps to the beginning if no 'x' char is found)
|
||||
```
|
||||
- Lx: `left`: Move memory pointer on the left for `x` chars (defaults to `1`).
|
||||
- Rx: `right`: Move memory pointer on the right for `x` chars (defaults to `1`).
|
||||
- Nx: `next`: Forward Jump: Move execution pointer to the first found `x` on the left of its current position.
|
||||
- Px: `prev`: Backward Jump: Move execution pointer to the first found `x` on the right of its current position.
|
||||
- O: `output`: Copy the current value under memory pointer to stdout.
|
||||
- I: `input`: Copy from stdin to the current memory pointer.
|
||||
- K: Decrement the value under the current memory pointer.
|
||||
- H: Increment the value under the current memory pointer.
|
||||
- Jx: `jump` If the value under the current memory pointer is not `0` (0x30): Move the instruction pointer to the previous `x` instruction (like `Px`). (Jumps to the beginning if no `x` char is found).
|
||||
|
||||
The Hello World:
|
||||
|
||||
R9R9R9R4OROROROROROROROROROROROHello World.
|
||||
python horrlang.py 'R9R9R9R4OROROROROROROROROROROROHello World.'
|
||||
|
||||
Or:
|
||||
|
||||
Hello World0KORJK
|
||||
python horrlang.py 'Hello World0KORJK'
|
||||
|
||||
Writing a serie of P should like:
|
||||
Writing a infinite serie of P:
|
||||
|
||||
R5OP5P or R3OP3
|
||||
|
||||
|
98
horrlang.py
98
horrlang.py
@ -1,32 +1,17 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Horrlang Interpreter."""
|
||||
|
||||
import sys
|
||||
|
||||
class InfiniteArray:
|
||||
def __init__(self, values=None):
|
||||
self.chunks = {}
|
||||
if values is not None:
|
||||
for key, value in enumerate(values):
|
||||
self[key] = value
|
||||
|
||||
def get_chunk(self, index):
|
||||
if index not in self.chunks:
|
||||
self.chunks[index] = [0 for i in xrange(4096)]
|
||||
return self.chunks[index]
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.get_chunk(index / 4096)[index % 4096]
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
self.get_chunk(index / 4096)[index % 4096] = value
|
||||
import io
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class Machine:
|
||||
def __init__(self, code, read, write, max_cycles=None):
|
||||
def __init__(self, code, read=sys.stdin.read, write=sys.stdout.write, max_cycles=None):
|
||||
self.read = read
|
||||
self.write = write
|
||||
self.mem = InfiniteArray([ord(char) for char in code])
|
||||
self.mem = defaultdict(int)
|
||||
self.mem.update({i: ord(char) for i, char in enumerate(code)})
|
||||
self.exc_ptr = 0
|
||||
self.mem_ptr = 0
|
||||
self.max_cycles = max_cycles
|
||||
@ -41,34 +26,37 @@ class Machine:
|
||||
|
||||
def get_integer(self):
|
||||
parameter = 0
|
||||
while self.mem[self.exc_ptr + 1] >= 0x30 \
|
||||
and self.mem[self.exc_ptr + 1] <= 0x39:
|
||||
while self.mem[self.exc_ptr + 1] >= 0x30 and self.mem[self.exc_ptr + 1] <= 0x39:
|
||||
self.exc_ptr += 1
|
||||
if not self.cycle():
|
||||
return parameter
|
||||
parameter = parameter * 10 + (self.mem[self.exc_ptr] \
|
||||
- ord('0'))
|
||||
parameter = parameter * 10 + (self.mem[self.exc_ptr] - ord("0"))
|
||||
if parameter == 0:
|
||||
parameter = 1
|
||||
return parameter
|
||||
|
||||
def L(self):
|
||||
"""Move memory pointer to the left."""
|
||||
self.mem_ptr -= self.get_integer()
|
||||
if self.mem_ptr < 0:
|
||||
self.mem_ptr = 0
|
||||
|
||||
def R(self):
|
||||
"""Move memory pointer to the right."""
|
||||
self.mem_ptr += self.get_integer()
|
||||
|
||||
def O(self):
|
||||
"""Output."""
|
||||
char = self.mem[self.mem_ptr]
|
||||
if char > 0 and char < 256:
|
||||
if 0 < char < 256:
|
||||
self.write(chr(char))
|
||||
|
||||
def I(self):
|
||||
"""Input."""
|
||||
self.mem[self.mem_ptr] = ord(self.read(1))
|
||||
|
||||
def N(self):
|
||||
"""Forward jump."""
|
||||
self.exc_ptr += 1
|
||||
search = self.mem[self.exc_ptr]
|
||||
while self.mem[self.exc_ptr] != search:
|
||||
@ -77,6 +65,7 @@ class Machine:
|
||||
return
|
||||
|
||||
def P(self):
|
||||
"""Backward jump."""
|
||||
search = self.mem[self.exc_ptr + 1]
|
||||
self.exc_ptr -= 1
|
||||
while self.mem[self.exc_ptr] != search:
|
||||
@ -85,7 +74,8 @@ class Machine:
|
||||
return
|
||||
|
||||
def J(self):
|
||||
if self.mem[self.mem_ptr] != ord('0'):
|
||||
"""Conditional jump."""
|
||||
if self.mem[self.mem_ptr] != ord("0"):
|
||||
search = self.mem[self.exc_ptr + 1]
|
||||
self.exc_ptr -= 1
|
||||
while self.mem[self.exc_ptr] != search:
|
||||
@ -94,26 +84,70 @@ class Machine:
|
||||
return
|
||||
|
||||
def H(self):
|
||||
"""Increment."""
|
||||
self.mem[self.mem_ptr] = self.mem[self.mem_ptr] + 1
|
||||
|
||||
def K(self):
|
||||
"""Decrement."""
|
||||
self.mem[self.mem_ptr] = self.mem[self.mem_ptr] - 1
|
||||
|
||||
def _debug(self):
|
||||
print(f"# Cycle {self.cycles}\n")
|
||||
for i, value in sorted(self.mem.items()):
|
||||
marker = ""
|
||||
if i == self.exc_ptr:
|
||||
marker += "← exc ptr"
|
||||
control = chr(self.mem[self.exc_ptr])
|
||||
if hasattr(self, control):
|
||||
marker += " (" + getattr(self, control).__doc__ + ")"
|
||||
if i == self.mem_ptr:
|
||||
marker += "← mem ptr"
|
||||
printable = ""
|
||||
if chr(value).isprintable():
|
||||
printable = f"{chr(value)}"
|
||||
print(f"{i:3d} | {value:5d} | {printable:5s}", marker)
|
||||
print()
|
||||
|
||||
def run(self, debug=False):
|
||||
while True:
|
||||
if self.mem[self.exc_ptr] == 0:
|
||||
return
|
||||
char = self.mem[self.exc_ptr]
|
||||
if char > 0 and char < 256:
|
||||
if 0 < char < 256:
|
||||
control = chr(char)
|
||||
if debug:
|
||||
print control
|
||||
self._debug()
|
||||
if hasattr(self, control):
|
||||
getattr(self, control)()
|
||||
self.exc_ptr += 1
|
||||
if not self.cycle():
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
Machine(sys.argv[1], sys.stdin.read, sys.stdout.write,
|
||||
int(sys.argv[2]) if len(sys.argv) > 2 else None).run()
|
||||
|
||||
def eval(instructions, input="", max_cycles=None):
|
||||
"""Convenience, non-interactive wrapper around HorrlangMachine."""
|
||||
stdin = io.StringIO(input)
|
||||
stdout = io.StringIO()
|
||||
machine = Machine(instructions, stdin.read, stdout.write, max_cycles=max_cycles)
|
||||
machine.run()
|
||||
return stdout.getvalue()
|
||||
|
||||
|
||||
import argparse
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("code", type=str)
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
parser.add_argument("--max-cycles", type=int, default=None)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
Machine(args.code, max_cycles=args.max_cycles,).run(debug=args.debug)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
21
test_horrlang.py
Normal file
21
test_horrlang.py
Normal file
@ -0,0 +1,21 @@
|
||||
from horrlang import eval
|
||||
|
||||
|
||||
def test_hello_world():
|
||||
assert eval("R9R9R9R4OROROROROROROROROROROROHello World.") == "Hello World."
|
||||
|
||||
|
||||
def test_short_hello_world():
|
||||
assert eval("Hello World0KORJK") == "Hello World"
|
||||
|
||||
|
||||
def test_from_readme():
|
||||
assert eval("R5OP5P", max_cycles=100) == "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP"
|
||||
|
||||
|
||||
def test_from_readme_short():
|
||||
assert eval("R3OP3", max_cycles=100) == "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP"
|
||||
|
||||
|
||||
def test_123456789():
|
||||
assert eval("R9R3:ROHLKJ:91") == "123456789"
|
Loading…
Reference in New Issue
Block a user