simplest-nova.py
· 4.4 KiB · Python
Raw
Playground
#!/usr/bin/env python3
import sys
from enum import Enum
class State(Enum):
# States
INSERT = 0
MATCH = 1
REMOVE = 2
# Substates
FETCH = 3
LABEL = 4
VARIABLE = 5
def number(instruction):
try:
return int(instruction, 16)
except:
return None
def seek(cursor, code, instruction):
while cursor < len(code) and code[cursor] != instruction:
cursor += 1
return cursor
def step(machine, code):
state, substate, cursor, label, stacks, offsets, variables = machine
if cursor >= len(code):
return (False, machine)
instruction = code[cursor]
if instruction == '+':
state = State.INSERT
substate = State.FETCH
cursor += 1
elif instruction == '?':
state = State.MATCH
substate = State.FETCH
cursor += 1
elif instruction == '-':
state = State.REMOVE
substate = State.FETCH
cursor += 1
elif instruction == '.':
state = State.INSERT
substate = State.FETCH
variables.clear()
for offset in range(0xF):
offsets[offset] = 0
cursor = seek(0, code, '?')
elif instruction == '$':
if substate is State.LABEL:
substate = State.VARIABLE
cursor += 1
else:
# Gremlins live here.
data = number(instruction)
if data is not None:
if substate is State.FETCH:
if state is State.REMOVE:
stacks[data].pop()
else:
label = data
substate = State.LABEL
elif substate is State.LABEL:
if state is State.INSERT:
stacks[label].append(data)
elif state is State.MATCH:
if len(stacks[label]) == 0 or offsets[label] >= len(stacks[label]) or stacks[label][-1 - offsets[label]] != data:
cursor = seek(cursor, code, '.')
variables.clear()
for offset in range(0xF):
offsets[offset] = 0
else:
offsets[label] += 1
substate = State.FETCH
elif substate is State.VARIABLE:
if state is State.INSERT:
if data in variables:
stacks[label].append(variables[data])
else:
stacks[label].append(0)
elif state is State.MATCH:
if len(stacks[label]) == 0 or offsets[label] > len(stacks[label]):
cursor = seek(cursor, code, '.')
variables.clear()
for offset in range(0xF):
offsets[offset] = 0
else:
if data in variables:
if stacks[label][-1 - offsets[label]] != variables[data]:
cursor = seek(cursor, code, '.')
variables.clear()
for offset in range(0xF):
offsets[offset] = 0
else:
offsets[label] += 1
else:
variables[data] = stacks[label][-1 - offsets[label]]
offsets[label] += 1
substate = State.FETCH
cursor += 1
return (cursor >= len(code), (state, substate, cursor, label, stacks, offsets, variables))
def run(code, machine=None):
if not machine:
stacks = [[] for _ in range(0x10)]
offsets = [0] * 0x10
machine = (State.INSERT, State.FETCH, 0, 0, stacks, offsets, {})
halt = False
while not halt:
halt, machine = step(machine, code)
return machine
def pretty(machine):
state, substate, cursor, label, stacks, offsets, variables = machine
for index, stack in enumerate(stacks):
if len(stack) == 0:
continue
print(str(index) + ':', " ".join(reversed([str(element) for element in stack])))
def main(name, arguments):
rules = ""
if len(arguments) == 0:
print("usage:", name, + "<files>...")
return
for argument in arguments:
with open(argument) as file:
rules += file.read()
pretty(run(rules))
if __name__ == "__main__":
main(sys.argv[0], sys.argv[1:])
| 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | import sys |
| 4 | from enum import Enum |
| 5 | |
| 6 | class State(Enum): |
| 7 | # States |
| 8 | INSERT = 0 |
| 9 | MATCH = 1 |
| 10 | REMOVE = 2 |
| 11 | |
| 12 | # Substates |
| 13 | FETCH = 3 |
| 14 | LABEL = 4 |
| 15 | VARIABLE = 5 |
| 16 | |
| 17 | def number(instruction): |
| 18 | try: |
| 19 | return int(instruction, 16) |
| 20 | except: |
| 21 | return None |
| 22 | |
| 23 | def seek(cursor, code, instruction): |
| 24 | while cursor < len(code) and code[cursor] != instruction: |
| 25 | cursor += 1 |
| 26 | return cursor |
| 27 | |
| 28 | def step(machine, code): |
| 29 | state, substate, cursor, label, stacks, offsets, variables = machine |
| 30 | if cursor >= len(code): |
| 31 | return (False, machine) |
| 32 | instruction = code[cursor] |
| 33 | if instruction == '+': |
| 34 | state = State.INSERT |
| 35 | substate = State.FETCH |
| 36 | cursor += 1 |
| 37 | elif instruction == '?': |
| 38 | state = State.MATCH |
| 39 | substate = State.FETCH |
| 40 | cursor += 1 |
| 41 | elif instruction == '-': |
| 42 | state = State.REMOVE |
| 43 | substate = State.FETCH |
| 44 | cursor += 1 |
| 45 | elif instruction == '.': |
| 46 | state = State.INSERT |
| 47 | substate = State.FETCH |
| 48 | variables.clear() |
| 49 | for offset in range(0xF): |
| 50 | offsets[offset] = 0 |
| 51 | cursor = seek(0, code, '?') |
| 52 | elif instruction == '$': |
| 53 | if substate is State.LABEL: |
| 54 | substate = State.VARIABLE |
| 55 | cursor += 1 |
| 56 | else: |
| 57 | # Gremlins live here. |
| 58 | data = number(instruction) |
| 59 | if data is not None: |
| 60 | if substate is State.FETCH: |
| 61 | if state is State.REMOVE: |
| 62 | stacks[data].pop() |
| 63 | else: |
| 64 | label = data |
| 65 | substate = State.LABEL |
| 66 | elif substate is State.LABEL: |
| 67 | if state is State.INSERT: |
| 68 | stacks[label].append(data) |
| 69 | elif state is State.MATCH: |
| 70 | if len(stacks[label]) == 0 or offsets[label] >= len(stacks[label]) or stacks[label][-1 - offsets[label]] != data: |
| 71 | cursor = seek(cursor, code, '.') |
| 72 | variables.clear() |
| 73 | for offset in range(0xF): |
| 74 | offsets[offset] = 0 |
| 75 | else: |
| 76 | offsets[label] += 1 |
| 77 | substate = State.FETCH |
| 78 | elif substate is State.VARIABLE: |
| 79 | if state is State.INSERT: |
| 80 | if data in variables: |
| 81 | stacks[label].append(variables[data]) |
| 82 | else: |
| 83 | stacks[label].append(0) |
| 84 | elif state is State.MATCH: |
| 85 | if len(stacks[label]) == 0 or offsets[label] > len(stacks[label]): |
| 86 | cursor = seek(cursor, code, '.') |
| 87 | variables.clear() |
| 88 | for offset in range(0xF): |
| 89 | offsets[offset] = 0 |
| 90 | else: |
| 91 | if data in variables: |
| 92 | if stacks[label][-1 - offsets[label]] != variables[data]: |
| 93 | cursor = seek(cursor, code, '.') |
| 94 | variables.clear() |
| 95 | for offset in range(0xF): |
| 96 | offsets[offset] = 0 |
| 97 | else: |
| 98 | offsets[label] += 1 |
| 99 | else: |
| 100 | variables[data] = stacks[label][-1 - offsets[label]] |
| 101 | offsets[label] += 1 |
| 102 | substate = State.FETCH |
| 103 | cursor += 1 |
| 104 | return (cursor >= len(code), (state, substate, cursor, label, stacks, offsets, variables)) |
| 105 | |
| 106 | def run(code, machine=None): |
| 107 | if not machine: |
| 108 | stacks = [[] for _ in range(0x10)] |
| 109 | offsets = [0] * 0x10 |
| 110 | machine = (State.INSERT, State.FETCH, 0, 0, stacks, offsets, {}) |
| 111 | halt = False |
| 112 | while not halt: |
| 113 | halt, machine = step(machine, code) |
| 114 | return machine |
| 115 | |
| 116 | def pretty(machine): |
| 117 | state, substate, cursor, label, stacks, offsets, variables = machine |
| 118 | for index, stack in enumerate(stacks): |
| 119 | if len(stack) == 0: |
| 120 | continue |
| 121 | print(str(index) + ':', " ".join(reversed([str(element) for element in stack]))) |
| 122 | |
| 123 | def main(name, arguments): |
| 124 | rules = "" |
| 125 | if len(arguments) == 0: |
| 126 | print("usage:", name, + "<files>...") |
| 127 | return |
| 128 | for argument in arguments: |
| 129 | with open(argument) as file: |
| 130 | rules += file.read() |
| 131 | pretty(run(rules)) |
| 132 | |
| 133 | if __name__ == "__main__": |
| 134 | main(sys.argv[0], sys.argv[1:]) |
| 135 |