最後活躍 2 days ago

The simplest Nova implementation I've got (so far).

june's Avatar june 已修改 2 days ago. 還原成這個修訂版本

2 files changed, 24 insertions, 7 deletions

search.nv

@@ -1,4 +1,5 @@
1 1 +
2 + `Push a bunch of facts.`
2 3 0 b
3 4 1 a 1 b 1 c
4 5 1 d 1 e 1 f
@@ -6,16 +7,19 @@
6 7 1 a
7 8 .
8 9
10 + `Did we find what we're looking for?`
9 11 ? 0 $a 1 $a
10 12 - 0
11 13 + 2 $a
12 14 .
13 15
16 + `Put unrelated items to the side.`
14 17 ? 0 $a 1 $b
15 18 - 1
16 19 + 3 $b
17 20 .
18 21
22 + `We couldn't find what we were looking for.`
19 23 ? 0 $a
20 24 - 0
21 25 + 4 $a

simplest-nova.py

@@ -5,9 +5,9 @@ from enum import Enum
5 5
6 6 class State(Enum):
7 7 # States
8 - INSERT = 0
9 - MATCH = 1
10 - REMOVE = 2
8 + INSERT = 0
9 + MATCH = 1
10 + REMOVE = 2
11 11
12 12 # Substates
13 13 FETCH = 3
@@ -30,8 +30,15 @@ class Machine:
30 30 for hook in hooks:
31 31 hook(self)
32 32 def seek(self, instruction):
33 - while self.cursor < self.length and self.code[self.cursor] != instruction:
34 - self.cursor += 1
33 + while self.cursor < self.length:
34 + if self.code[self.cursor] == '`' and instruction != '`':
35 + self.cursor += 1
36 + self.seek('`')
37 + self.cursor += 1
38 + elif self.code[self.cursor] == instruction:
39 + break
40 + else:
41 + self.cursor += 1
35 42 def branch(self):
36 43 self.clear()
37 44 self.seek('.')
@@ -105,13 +112,19 @@ class Machine:
105 112 if self.state is State.INSERT:
106 113 return self.insert(instruction)
107 114 return self.match(instruction)
115 + def comment(self):
116 + self.cursor += 1
117 + self.seek('`')
118 + self.cursor += 1
119 + return self.cursor >= self.length
108 120 def step(self):
109 121 instructions = {
110 122 '+': Machine.transition(State.INSERT),
111 123 '?': Machine.transition(State.MATCH),
112 124 '-': Machine.transition(State.REMOVE),
113 125 '$': Machine.variable,
114 - '.': Machine.reset
126 + '.': Machine.reset,
127 + '`': Machine.comment
115 128 }
116 129 if self.cursor >= self.length:
117 130 return True
@@ -146,4 +159,4 @@ def main(name, arguments):
146 159 Machine(code).run().print()
147 160
148 161 if __name__ == "__main__":
149 - main(sys.argv[0], sys.argv[1:])
162 + main(sys.argv[0], sys.argv[1:])

june's Avatar june 已修改 3 days ago. 還原成這個修訂版本

1 file changed, 2 insertions, 2 deletions

simplest-nova.py

@@ -11,7 +11,7 @@ class State(Enum):
11 11
12 12 # Substates
13 13 FETCH = 3
14 - LITERAL = 4
14 + LITERAL = 4
15 15 VARIABLE = 5
16 16
17 17 class Machine:
@@ -146,4 +146,4 @@ def main(name, arguments):
146 146 Machine(code).run().print()
147 147
148 148 if __name__ == "__main__":
149 - main(sys.argv[0], sys.argv[1:])
149 + main(sys.argv[0], sys.argv[1:])

june's Avatar june 已修改 3 days ago. 還原成這個修訂版本

1 file changed, 22 insertions

search.nv(檔案已創建)

@@ -0,0 +1,22 @@
1 + +
2 + 0 b
3 + 1 a 1 b 1 c
4 + 1 d 1 e 1 f
5 + 1 e 1 d 1 c
6 + 1 a
7 + .
8 +
9 + ? 0 $a 1 $a
10 + - 0
11 + + 2 $a
12 + .
13 +
14 + ? 0 $a 1 $b
15 + - 1
16 + + 3 $b
17 + .
18 +
19 + ? 0 $a
20 + - 0
21 + + 4 $a
22 + .

june's Avatar june 已修改 3 days ago. 還原成這個修訂版本

1 file changed, 125 insertions, 110 deletions

simplest-nova.py

@@ -11,124 +11,139 @@ class State(Enum):
11 11
12 12 # Substates
13 13 FETCH = 3
14 - LABEL = 4
14 + LITERAL = 4
15 15 VARIABLE = 5
16 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()
17 + class Machine:
18 + def __init__(self, code, hooks=[]):
19 + self.code = code
20 + self.literals = "0123456789abcdefABCDEF"
21 + self.length = len(code)
22 + self.cursor = 0
23 + self.stack = 0
24 + self.state = State.INSERT
25 + self.substate = State.FETCH
26 + self.stacks = [[] for _ in range(0x10)]
27 + self.offsets = [0] * 0x10
28 + self.variables = {}
29 + self.hooks = hooks
30 + for hook in hooks:
31 + hook(self)
32 + def seek(self, instruction):
33 + while self.cursor < self.length and self.code[self.cursor] != instruction:
34 + self.cursor += 1
35 + def branch(self):
36 + self.clear()
37 + self.seek('.')
38 + def transition(new):
39 + def instruction(self):
40 + self.state = new
41 + self.substate = State.FETCH
42 + self.cursor += 1
43 + return self.cursor >= self.length
44 + return instruction
45 + def variable(self):
46 + if self.substate is State.LITERAL:
47 + self.substate = State.VARIABLE
48 + self.cursor += 1
49 + return self.cursor >= self.length
50 + def clear(self):
51 + self.variables.clear()
52 + for stack in range(0x10):
53 + self.offsets[stack] = 0
54 + def reset(self):
55 + self.clear()
56 + self.cursor = 0
57 + self.state = State.INSERT
58 + self.substate = State.FETCH
59 + self.seek('?')
60 + for hook in self.hooks:
61 + hook(self)
62 + def number(self, instruction):
63 + try:
64 + return int(instruction, 16)
65 + except:
66 + return None
67 + def insert(self, instruction):
68 + if self.substate is State.LITERAL:
69 + self.stacks[self.stack].append(instruction)
70 + else:
71 + if instruction in self.variables:
72 + self.stacks[self.stack].append(self.variables[instruction])
73 + else:
74 + self.stacks[self.stack].append(0)
75 + self.substate = State.FETCH
76 + self.cursor += 1
77 + return self.cursor >= self.length
78 + def match(self, instruction):
79 + depth = len(self.stacks[self.stack])
80 + offset = self.offsets[self.stack]
81 + if depth == 0 or offset >= depth:
82 + self.branch()
83 + else:
84 + if self.substate is State.LITERAL:
85 + if self.stacks[stack][-1 - offset] != instruction:
86 + self.branch()
87 + elif self.substate is State.VARIABLE:
88 + if instruction in self.variables:
89 + if self.variables[instruction] != self.stacks[self.stack][-1 - offset]:
90 + self.branch()
63 91 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])))
92 + self.variables[instruction] = self.stacks[self.stack][-1 - offset]
93 + self.substate = State.FETCH
94 + self.cursor += 1
95 + return self.cursor >= self.length
96 + def literal(self, instruction):
97 + if self.substate is State.FETCH:
98 + if self.state is State.REMOVE:
99 + self.stacks[instruction].pop()
100 + else:
101 + self.stack = instruction
102 + self.substate = State.LITERAL
103 + self.cursor += 1
104 + return self.cursor >= self.length
105 + if self.state is State.INSERT:
106 + return self.insert(instruction)
107 + return self.match(instruction)
108 + def step(self):
109 + instructions = {
110 + '+': Machine.transition(State.INSERT),
111 + '?': Machine.transition(State.MATCH),
112 + '-': Machine.transition(State.REMOVE),
113 + '$': Machine.variable,
114 + '.': Machine.reset
115 + }
116 + if self.cursor >= self.length:
117 + return True
118 + instruction = self.code[self.cursor]
119 + if instruction in instructions:
120 + return instructions[instruction](self)
121 + elif instruction in self.literals:
122 + return self.literal(self.number(instruction))
123 + self.cursor += 1
124 + return False
125 + def run(self):
126 + halt = False
127 + while not halt:
128 + halt = self.step()
129 + return self
130 + def print(self):
131 + for index, stack in enumerate(self.stacks):
132 + if len(stack) != 0:
133 + print(str(index) + ':', " ".join([str(element) for element in stack]))
122 134
123 135 def main(name, arguments):
124 - rules = ""
136 + code = ""
125 137 if len(arguments) == 0:
126 - print("usage:", name, + "<files>...")
138 + print("usage:", name, "<files...>")
127 139 return
128 140 for argument in arguments:
141 + if argument == '-':
142 + code += input()
143 + continue
129 144 with open(argument) as file:
130 - rules += file.read()
131 - pretty(run(rules))
145 + code += file.read()
146 + Machine(code).run().print()
132 147
133 148 if __name__ == "__main__":
134 - main(sys.argv[0], sys.argv[1:])
149 + main(sys.argv[0], sys.argv[1:])

june's Avatar june 已修改 4 days ago. 還原成這個修訂版本

1 file changed, 134 insertions

simplest-nova.py(檔案已創建)

@@ -0,0 +1,134 @@
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:])
上一頁 下一頁