Skip to content

Commit a3a86e3

Browse files
committed
Tidying
1 parent e66d9f8 commit a3a86e3

3 files changed

Lines changed: 41 additions & 50 deletions

File tree

lib/laundromat/cfg.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,17 @@ def build(clause: list[Directive | str], current: Node, condition: Condition):
4848
raise InvalidNode(directive)
4949

5050
case Special.Hash:
51-
for index, clause in enumerate(
52-
directive.clauses[:-1] if directive.default_token else directive.clauses):
53-
build(clause, current, Equal(Special.Hash, index))
54-
55-
default = Less(index + 1, index + 1, Special.Hash)
5651
if directive.default_token:
57-
build(directive.clauses[-1], current, default)
52+
for index, clause in enumerate(directive.clauses[:-1]):
53+
build(clause, current, Equal(Special.Hash, index))
54+
55+
build(directive.clauses[-1], current, Less(index + 1, index + 1, Special.Hash))
5856

5957
else:
60-
self.add_edge(current, current.closing, condition=default)
58+
for index, clause in enumerate(directive.clauses):
59+
build(clause, current, Equal(Special.Hash, index))
60+
61+
self.add_edge(current, current.closing, condition=Less(index + 1, index + 1, Special.Hash))
6162

6263
case n if 0 <= n < len(directive.clauses):
6364
build(directive.clauses[n], current, Condition())
@@ -93,7 +94,7 @@ def build(clause: list[Directive | str], current: Node, condition: Condition):
9394
buffer.extend(clauses.pop() + [directive.default_token])
9495

9596
for section in clauses[:-1]:
96-
buffer.extend(section + [Directive("~;", [])])
97+
buffer.extend(section + [Directive(";", [])])
9798

9899
# TODO: Actually handle justification
99100
build(buffer + clauses[-1], current, Condition())
@@ -124,8 +125,8 @@ def build(clause: list[Directive | str], current: Node, condition: Condition):
124125
self.add_edge(current, CFG.CRASH)
125126
return
126127

127-
case '*' if directive.get_param(0, 0) == Special.V:
128-
self.add_edge(current, CFG.CRASH)
128+
case '%' | '&' | '|' | '~' | '*' if Special.V in directive.params:
129+
self.crash_on(current)
129130
return
130131

131132
case _:
@@ -185,7 +186,7 @@ def __str__(self) -> str:
185186
case nil, non_nil:
186187
return program + f"~#[{nil}~:;{non_nil}"
187188

188-
case "[":
189+
case "[" if current.directive.get_param(0) == Special.Hash:
189190
cases = {}
190191
for child in self[current]:
191192
# Terrible no good very bad
@@ -215,13 +216,14 @@ def __str__(self) -> str:
215216

216217
return program
217218

218-
def add_crash(self, u: Node):
219-
self.add_edge(u, v := Node(Directive("?", [])))
220-
self.add_edge(v, CFG.CRASH)
221-
222219
def add_edge(self, u: Node, v: Node, **attrs):
223220
super().add_edge(u, v, condition=attrs.get("condition", Condition()), back=attrs.get("back", False))
224221

222+
def crash_on(self, node: Node):
223+
self.remove_edges_from([*self.edges(node)])
224+
self.add_edge(node, crash := Node(Directive("?", [])))
225+
self.add_edge(crash, CFG.CRASH)
226+
225227
def descendants(self, node: Node) -> set[Node]:
226228
return nx.descendants(self, node) | {node}
227229

@@ -282,6 +284,9 @@ def subgraph(self, nodes) -> 'CFG':
282284
subgraph.__class__ = CFG
283285
return subgraph
284286

287+
def terminates_from(self, node: Node) -> bool:
288+
return bool({CFG.CRASH, CFG.END} & self.descendants(node))
289+
285290
def update_pointers(self):
286291
# TODO: Use the logger
287292
for node in self:

lib/laundromat/node.py

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -155,22 +155,9 @@ def step(self, node: 'Node') -> 'Pointer':
155155
case n:
156156
return +(self + n)
157157

158-
case '[' if node.directive.at_sign:
159-
return self + 1
160-
161-
case '[' if node.directive.colon:
162-
return self + 1
163-
164-
case '[' | '<':
165-
return self
166-
167158
case '{':
168159
return self.copy(from_start=Range(0, inf), from_end=Range(0, inf))
169160

170-
case ']' | '>' | '}':
171-
return self
172-
173-
# TODO: Spell out more cases
174161
case _:
175162
return self + node.consumes
176163

@@ -194,6 +181,10 @@ def check(self, pointer: Pointer) -> bool:
194181
def enforce(self, pointer: Pointer) -> Pointer:
195182
return pointer
196183

184+
@property
185+
def queries_tape(self) -> bool:
186+
return False
187+
197188

198189
@dataclass(frozen=True)
199190
class Nil(Condition):
@@ -238,7 +229,7 @@ def check(self, pointer: Pointer) -> bool:
238229
return b is not None if self.negated else b is None
239230

240231
case params if Special.V in params:
241-
return pointer
232+
return True
242233

243234
case [Special.Hash, Special.Hash]:
244235
return not self.negated
@@ -257,6 +248,10 @@ def enforce(self, pointer: Pointer) -> Pointer:
257248
case _:
258249
return pointer
259250

251+
@property
252+
def queries_tape(self) -> bool:
253+
return Special.V in [self.a, self.b]
254+
260255

261256
@dataclass(frozen=True)
262257
class Less(Condition):
@@ -283,7 +278,7 @@ def check(self, pointer: Pointer) -> bool:
283278
return all(x is None for x in params) != self.negated
284279

285280
case params if Special.V in params:
286-
return pointer
281+
return True
287282

288283
case [Special.Hash, Special.Hash, Special.Hash]:
289284
return not self.negated
@@ -330,6 +325,10 @@ def enforce(self, pointer: Pointer) -> Pointer:
330325
case _:
331326
return pointer
332327

328+
@property
329+
def queries_tape(self) -> bool:
330+
return Special.V in [self.a, self.b, self.c]
331+
333332

334333
class Control(StrEnum):
335334
Start = "S"
@@ -387,11 +386,8 @@ def consumes(self) -> Range:
387386
case '/':
388387
return Range.only(1)
389388

390-
case '[' if self.directive.at_sign:
391-
return Range(0, 1)
392-
393-
case '[' if self.directive.colon:
394-
return Range.only(1)
389+
case '[' if self.directive.at_sign or self.directive.colon:
390+
return Range(1)
395391

396392
case '[' | ']' | '<' | '>':
397393
return Range.only(0)

lib/laundromat/optimizer.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import matplotlib.pyplot as plt
2-
import networkx as nx
3-
41
from lib.laundromat.cfg import *
52

63

@@ -16,28 +13,21 @@ def simplify(cfg: CFG) -> CFG:
1613

1714
# Crash past the end of the tape
1815
if node.pointer.from_end < Range(0, inf):
19-
cfg.remove_edge(node, child)
20-
cfg.add_crash(node)
16+
cfg.crash_on(node)
2117
print(f"Crashed at {node}")
2218

2319
# Crash infinite loops
24-
if node.kind == "{" and not {CFG.END, CFG.CRASH} & nx.descendants(cfg, child):
20+
if node.kind == "{" and not cfg.terminates_from(child):
21+
cfg.crash_on(child)
2522
print("Found an infinite loop!")
2623

27-
cfg.remove_edges_from([*cfg.edges(child)])
28-
cfg.add_crash(child)
29-
30-
# Skip
31-
if not condition:
32-
continue
33-
3424
# Check reachability
3525
if not condition.check(node.pointer) or child.directive == Control.UB:
3626
cfg.remove_edge(node, child)
3727
print(f"Removed {node} -> {child}")
3828

3929
# Simplify condition
40-
elif condition.enforce(node.pointer) == node.pointer:
30+
elif condition.enforce(node.pointer) == node.pointer and not condition.queries_tape:
4131
cfg[node][child]["condition"] = Condition()
4232
print(f"Simplified {node} -> {child}")
4333

@@ -54,7 +44,7 @@ def simplify(cfg: CFG) -> CFG:
5444
return cfg
5545

5646

57-
graph = CFG("""~@{~#[1~;2~;3~]~}""")
47+
graph = CFG("""~1[comment~]""")
5848
graph.draw(size=12)
5949

6050
simplified = simplify(graph)

0 commit comments

Comments
 (0)