# HG changeset patch # User Windel Bouwman # Date 1390144184 -3600 # Node ID 84d67cce67b7e578928c3d59fb192a7d8b53aa55 # Parent 8d07a4254f04a6760e253b2e28f89c3a18efbcc2 Working burg diff -r 8d07a4254f04 -r 84d67cce67b7 python/arm.pb --- a/python/arm.pb Sat Jan 18 18:58:43 2014 +0100 +++ b/python/arm.pb Sun Jan 19 16:09:44 2014 +0100 @@ -4,17 +4,17 @@ %% -stmt: ASGNI(disp, reg) 1 (. .) +stmt: ASGNI(disp, reg) 1 (. print('ASGNI') .) stmt: reg 0 (. .) -reg: ADDI(reg, rc) 1 (. self.emit(Add, src=[$1], dst=[$c], other=[$2]) .) +reg: ADDI(reg, rc) 1 (. print('ADDI(reg, rc)') .) reg: CVCI(INDIRC(disp)) 1 (. .) reg: IOI 0 (. .) reg: disp 1 (. .) disp: ADDI(reg, con) 1 (. .) -disp: ADDRLP 0 (. .) +disp: ADDRLP 0 (. print('ADDRLP') .) rc: con 0 (. .) rc: reg 0 (. .) -con: CNSTI 0 (. .) +con: CNSTI 0 (. print('CNSTI') .) con: IOI 0 (. .) diff -r 8d07a4254f04 -r 84d67cce67b7 python/pyburg.py --- a/python/pyburg.py Sat Jan 18 18:58:43 2014 +0100 +++ b/python/pyburg.py Sun Jan 19 16:09:44 2014 +0100 @@ -23,6 +23,12 @@ The 'a' in the example above indicates an open connection to a next tree pattern. + In the example above 'reg' is a non-terminal. ADDI32 is a terminal. non-terminals + cannot have child nodes. A special case occurs in this case: + reg -> rc + where 'rc' is a non-terminal. This is an example of a chain rule. Chain rules + can be used to allow several variants of non-terminals. + The generated matcher uses dynamic programming to find the best match of the tree. This strategy consists of two steps: - label: During this phase the given tree is traversed in a bottom up way. @@ -76,6 +82,8 @@ class Rule: + """ A rewrite rule. Specifies a tree that can be rewritten into a result + at a specific cost """ def __init__(self, non_term, tree, cost, template): self.non_term = non_term self.tree = tree @@ -99,7 +107,7 @@ class Nonterm(Symbol): def __init__(self, name): super().__init__(name) - self.rules = [] + self.chain_rules = [] class BurgSystem: @@ -116,7 +124,13 @@ non_terminals = property(lambda s: s.symType(Nonterm)) def add_rule(self, non_term, tree, cost, template): + template = template[2:-2].strip() + if not template: + template = 'pass' rule = Rule(non_term, tree, cost, template) + if len(tree.children) == 0 and tree.name not in self.terminals: + print('chain:', rule) + self.non_term(tree.name).chain_rules.append(rule) self.non_term(rule.non_term) self.rules.append(rule) rule.nr = len(self.rules) @@ -126,7 +140,7 @@ raise BurgError('Cannot redefine terminal') if not self.goal: self.goal = name - self.install(name, Nonterm) + return self.install(name, Nonterm) def tree(self, name, *args): return Tree(name, *args) @@ -135,9 +149,9 @@ assert type(name) is str if name in self.symbols: assert type(self.symbols[name]) is t - return self.symbols[name] else: self.symbols[name] = t(name) + return self.symbols[name] def add_terminal(self, terminal): self.install(terminal, Term) @@ -169,23 +183,33 @@ self.print('from tree import Tree, BaseMatcher, State') self.print() self.print('class Matcher(BaseMatcher):') - self.print(' def __init__(self):') - self.print(' self.kid_functions = {}') - self.print(' self.nts_map = {}') + self.print(' def __init__(self):') + self.print(' self.kid_functions = {}') + self.print(' self.nts_map = {}') + self.print(' self.pat_f = {}') for rule in self.system.rules: kids, dummy = self.compute_kids(rule.tree, 't') + rule.num_nts = len(dummy) lf = 'lambda t: [{}]'.format(', '.join(kids), rule) - self.print(' # {}'.format(rule)) - self.print(' self.kid_functions[{}] = {}'.format(rule.nr, lf)) - self.print(' self.nts_map[{}] = {}'.format(rule.nr, dummy)) - self.print('') + pf = 'self.P{}'.format(rule.nr) + self.print(' # {}: {}'.format(rule.nr, rule)) + self.print(' self.kid_functions[{}] = {}'.format(rule.nr, lf)) + self.print(' self.nts_map[{}] = {}'.format(rule.nr, dummy)) + self.print(' self.pat_f[{}] = {}'.format(rule.nr, pf)) + self.print() + for rule in self.system.rules: + if rule.num_nts > 0: + args = ', ' + ', '.join('nt{}'.format(x) for x in range(rule.num_nts)) + else: + args = '' + self.print(' def P{}(self{}):'.format(rule.nr, args)) + self.print(' {}'.format(rule.template)) self.emit_state() - self.print() - self.print(' def gen(self, tree):') - self.print(' self.burm_label(tree)') - self.print(' if not tree.state.has_goal("{}"):'.format(self.system.goal)) - self.print(' raise Exception("Tree not covered")') - self.print(' self.apply_rules(tree, "{}")'.format(self.system.goal)) + self.print(' def gen(self, tree):') + self.print(' self.burm_label(tree)') + self.print(' if not tree.state.has_goal("{}"):'.format(self.system.goal)) + self.print(' raise Exception("Tree not covered")') + self.print(' self.apply_rules(tree, "{}")'.format(self.system.goal)) def emit_record(self, rule, state_var): # TODO: check for rules fullfilled (by not using 999999) @@ -193,11 +217,14 @@ self.print(' kids = self.kids(tree, {})'.format(rule.nr)) self.print(' c = sum(x.state.get_cost(y) for x, y in zip(kids, nts)) + {}'.format(rule.cost)) self.print(' tree.state.set_cost("{}", c, {})'.format(rule.non_term, rule.nr)) + for cr in self.system.symbols[rule.non_term].chain_rules: + self.print(' # Chain rule: {}'.format(cr)) + self.print(' tree.state.set_cost("{}", c + {}, {})'.format(cr.non_term, cr.cost, cr.nr)) def emit_state(self): """ Emit a function that assigns a new state to a node """ - self.print(' def burm_state(self, tree):') - self.print(' tree.state = State()') + self.print(' def burm_state(self, tree):') + self.print(' tree.state = State()') for term in self.system.terminals: self.emitcase(term) self.print() @@ -206,15 +233,9 @@ rules = [rule for rule in self.system.rules if rule.tree.name == term] for rule in rules: condition = self.emittest(rule.tree, 'tree') - self.print(' if {}:'.format(condition)) + self.print(' if {}:'.format(condition)) self.emit_record(rule, 'state') - def emit_closure(self): - for non_terminal in self.system.non_terminals: - self.print('def closure_{}(self, c):'.format(non_terminal)) - self.print(' pass') - self.print() - def compute_kids(self, t, root_name): """ Compute of a pattern the blanks that must be provided from below in the tree """ if t.name in self.system.non_terminals: diff -r 8d07a4254f04 -r 84d67cce67b7 python/tree.py --- a/python/tree.py Sat Jan 18 18:58:43 2014 +0100 +++ b/python/tree.py Sun Jan 19 16:09:44 2014 +0100 @@ -14,15 +14,19 @@ class State: + """ State used to label tree nodes """ def __init__(self): self.labels = {} + def has_goal(self, goal): return goal in self.labels + def get_cost(self, goal): - if not self.has_goal(goal): return 999999 return self.labels[goal][0] + def get_rule(self, goal): return self.labels[goal][1] + def set_cost(self, goal, cost, rule): if self.has_goal(goal): if self.get_cost(goal) > cost: @@ -30,7 +34,9 @@ else: self.labels[goal] = (cost, rule) + class BaseMatcher: + """ Base class for matcher objects. """ def kids(self, tree, rule): return self.kid_functions[rule](tree) @@ -38,12 +44,13 @@ return self.nts_map[rule] def burm_label(self, tree): + """ Label all nodes in the tree bottom up """ for c in tree.children: self.burm_label(c) self.burm_state(tree) def apply_rules(self, tree, goal): - print(tree.state.get_rule(goal)) rule = tree.state.get_rule(goal) - for kid_tree, kid_goal in zip(self.kids(tree, rule), self.nts(rule)): - self.apply_rules(kid_tree, kid_goal) + results = [self.apply_rules(kid_tree, kid_goal) + for kid_tree, kid_goal in zip(self.kids(tree, rule), self.nts(rule))] + self.pat_f[rule](*results)