summaryrefslogtreecommitdiff
path: root/runtime/runtime.py
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/runtime.py')
-rw-r--r--runtime/runtime.py96
1 files changed, 96 insertions, 0 deletions
diff --git a/runtime/runtime.py b/runtime/runtime.py
new file mode 100644
index 0000000..a65596e
--- /dev/null
+++ b/runtime/runtime.py
@@ -0,0 +1,96 @@
+"""Methods for parsing and executing TripletStructures according to their rules.
+"""
+# pylint: disable=import-error,no-name-in-module
+from runtime.cpp_structure import CPPStructure
+from runtime.production_rule import ProductionRule
+from runtime.interactive import TSREPL
+from runtime.matcher import OneOffMatcher
+
+class TSRuntime:
+ """A runtime for interpreting and executing triplet structures.
+ """
+ def __init__(self, ts):
+ """Initializes a new TSRuntime.
+ """
+ ts.commit(False)
+ self.ts = ts
+ # The Solver handles the 'dirty work' of actually finding matches to
+ # rule implicants.
+ self.solver = CPPStructure(self.ts)
+ self.extract_rules()
+ ts.commit(False)
+
+ def interactive(self):
+ """Begins an interactive runtime session.
+ """
+ repl = TSREPL(self)
+ repl.run()
+
+ def extract_rules(self):
+ """Populates self.rules and deletes the structure nodes.
+
+ NOTE: This function removes all rule-related nodes from the structure
+ after parsing them into ProductionRules. This is a relatively cheap and
+ easy way to ensure we don't have to worry about rules applying to other
+ rules, etc. In the long run, though, it would be nice to support
+ reflective rules.
+ """
+ ts = self.ts
+
+ self.rules = []
+ self.rules_by_name = dict()
+
+ rule_nodes = set(fact[1] for fact in ts.lookup(None, None, "/RULE"))
+ for rule_node in sorted(rule_nodes):
+ rule = ProductionRule(self, rule_node)
+ self.rules.append(rule)
+ self.rules_by_name[rule.name] = rule
+
+ avoid_nodes = set(avoid_node for rule in self.rules
+ for avoid_node in rule.all_nodes)
+ for avoid_node in avoid_nodes:
+ ts[avoid_node].remove_with_facts()
+ for node in ts.nodes.copy():
+ if not node.startswith("/:"):
+ ts[node].remove()
+
+ def matcher_propose(self, matcher):
+ """Propose TSDeltas based on a Matcher.
+
+ Yields (assignment, delta) pairs, where delta = assignment.produce().
+ """
+ assert self.ts.is_clean()
+
+ for assignment in matcher.assignments():
+ assignment = assignment.apply()
+ delta = self.ts.commit(commit_if_clean=True)
+
+ if delta:
+ yield (assignment, delta)
+ if self.ts.path[-1] is delta:
+ self.ts.rollback(-1)
+
+ def propose(self, rule, partial=None):
+ """Propose TSDeltas based on the rules.
+
+ Yields (assignment, delta) pairs, where delta = assignment.produce().
+ """
+ rule = self.rules_by_name[rule]
+ partial = partial or dict()
+ matcher = OneOffMatcher(self, rule, partial or dict())
+ yield from self.matcher_propose(matcher)
+
+ def propose_all(self, rules=None):
+ """Helper to yield proposals from multiple ProductionRules.
+ """
+ if rules is None:
+ rules = [rule.name for rule in self.rules]
+ for rule in rules:
+ yield from self.propose(rule)
+
+ def get_rule(self, name):
+ """Returns the ProductionRule associated with @name.
+
+ In the structure, there is some fact (A, @node, "/RULE").
+ """
+ return next(rule for rule in self.rules if rule.name == name)
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback