-
-/* compiler/patternmatcher/patternmatcher.cpp: implementation of the Faust
- rewriting engine */
-
-#include "tlib.hh"
-#include "list.hh"
-#include "boxes.hh"
-#include "ppbox.hh"
-#include "eval.hh"
-#include "patternmatcher.hh"
-
-using namespace std;
-#include <vector>
-#include <list>
-#include <set>
-#include <utility>
-
-/* Uncomment for debugging output. */
-//#define DEBUG
-
-/* Additional Tree deconstruction operations. */
-
-/* Check for cons (nonempty list) nodes. */
-
-static inline bool isCons(Tree x, Tree& h, Tree& t)
-{
- if (isList(x)) {
- h = hd(x); t = tl(x);
- return true;
- } else
- return false;
-}
-
-/* Deconstruct a (BDA) op pattern (YO). */
-
-static inline bool isBoxPatternOp(Tree box, Node& n, Tree& t1, Tree& t2)
-{
- if ( isBoxPar(box, t1, t2) ||
- isBoxSeq(box, t1, t2) ||
- isBoxSplit(box, t1, t2) ||
- isBoxMerge(box, t1, t2) ||
- isBoxHGroup(box, t1, t2) ||
- isBoxVGroup(box, t1, t2) ||
- isBoxTGroup(box, t1, t2) ||
- isBoxRec(box, t1, t2) )
- {
- n = box->node();
- return true;
- } else {
- return false;
- }
-}
-
-/* TA data structures. */
-
-/* subterm paths */
-
-typedef vector<int> Path;
-
-/* Subterm at given path in given term tree. */
-
-static Tree subtree(Tree X, int i, const Path& p)
-{
- int n = p.size();
- Node op(0);
- Tree x0, x1;
- if (i < n && isBoxPatternOp(X, op, x0, x1))
- return subtree((p[i]==0)?x0:x1, i+1, p);
- else
- return X;
-}
-
-/* rule markers */
-
-struct Rule {
- int r; // rule number
- Tree id; // matched variable (NULL if none)
- Path p; // subterm path indicating where variable value is found
-
- Rule(int _r, Tree _id) : r(_r), id(_id), p(Path()) {}
- Rule(int _r, Tree _id, const Path& _p) : r(_r), id(_id), p(_p) {}
- Rule(const Rule& rule) : r(rule.r), id(rule.id), p(rule.p) {}
-
- Rule& operator = (const Rule& rule)
- { r = rule.r; id = rule.id; p = rule.p; return *this; }
-
- bool operator == (const Rule& rule) const
- { return r == rule.r; }
- bool operator < (const Rule& rule) const
- { return r < rule.r; }
-
-#ifdef DEBUG
- ostream& print(ostream& fout) const;
-#endif
-};
-
-struct State;
-
-/* transitions */
-
-struct Trans {
- Tree x; // symbol or constant (NULL for variable)
- Node n; // operator symbol (if arity>0)
- int arity; // symbol arity
- State *state; // successor state
-
- Trans(Tree _x);
- Trans(const Node& _n, int _arity);
- Trans(const Trans& trans);
- ~Trans();
-
- Trans& operator = (const Trans& trans);
-
- bool is_var_trans() const { return arity == 0 && x == NULL; }
- bool is_cst_trans(Tree &_x) const { _x = x; return arity == 0 && x != NULL; }
- bool is_op_trans(Node &_n) const { _n = n; return arity > 0; }
-
- bool operator == (const Trans& trans) const
- { return arity == trans.arity && x == trans.x && n == trans.n; }
- bool operator < (const Trans& trans) const
- { return (arity < trans.arity) ? 1 :
- (arity > trans.arity) ? 0 :
- (arity == 0) ? (x < trans.x) :
- (n.getSym() < trans.n.getSym()); }
-
-#ifdef DEBUG
- ostream& print(ostream& fout) const;
-#endif
-};
-
-/* states */
-
-struct State {
- int s; // state number
- bool match_num; // whether state has a transition on a numeric constant
- list<Rule> rules; // rule markers
- list<Trans> trans; // transitions (1st transition is on variable if available)
- State() :
- s(0), match_num(false), rules(list<Rule>()), trans(list<Trans>()) {}
- State(const State& state) :
- s(state.s), match_num(state.match_num),
- rules(state.rules), trans(state.trans) {}
-
- State& operator = (const State& state)
- { s = state.s; match_num = state.match_num;
- rules = state.rules; trans = state.trans;
- return *this;
- }
-
-#ifdef DEBUG
- ostream& print(ostream& fout) const;
-#endif
-};
-
-// these need to come here so that the storage size of struct State is known
-
-Trans::Trans(Tree _x) :
- x(_x), n(0), arity(0), state(new State)
-{
-}
-
-Trans::Trans(const Node& _n, int _arity) :
- x(NULL), n(_n), arity(_arity), state(new State)
-{
-}
-
-Trans::Trans(const Trans& trans) :
- x(trans.x), n(trans.n), arity(trans.arity)
-{
- state = new State(*trans.state);
-}
-
-Trans::~Trans()
-{
- delete state;
-}
-
-Trans& Trans::operator = (const Trans& trans)
-{
- x = trans.x; n = trans.n; arity = trans.arity;
- state = new State(*trans.state);
- return *this;
-}
-
-/* the automaton */
-
-struct Automaton {
- vector<State*> state;
- vector<Tree> rhs;
-
- Automaton() : state(vector<State*>()), rhs(vector<Tree>()), s(0) {}
-
- // number of rules
- int n_rules() { return rhs.size(); }
- // markers of rules still active in state s
- const list<Rule>& rules(int s) { return state[s]->rules; }
- // transitions in state s
- const list<Trans>& trans(int s) { return state[s]->trans; }
- // is s a final state?
- bool final(int s) { return trans(s).empty(); }
-
- // assign state numbers and build the state table
- int s;
- void build(State *st);
-
-#ifdef DEBUG
- ostream& print(ostream& fout) const;
-#endif
-};
-
-void Automaton::build(State *st)
-{
- state.push_back(st);
- st->s = s++;
- list<Trans>::const_iterator t;
- for (t = st->trans.begin(); t != st->trans.end(); t++) {
- Tree x;
- double f;
- int i;
- if (t->is_cst_trans(x) &&
- (isBoxInt(x, &i) || isBoxReal(x, &f)))
- st->match_num = true;
- build(t->state);
- }
-}
-
-/* Debugging output. */
-
-#ifdef DEBUG
-inline ostream& operator << (ostream& s, const Rule& x)
-{ return x.print(s); }
-inline ostream& operator << (ostream& s, const Trans& x)
-{ return x.print(s); }
-inline ostream& operator << (ostream& s, const State& x)
-{ return x.print(s); }
-inline ostream& operator << (ostream& s, const Automaton& x)
-{ return x.print(s); }
-
-ostream& Rule::print(ostream& fout) const
-{
- if (id != NULL)
- fout << "#" << r << "(" << *id << ")";
- else
- fout << "#" << r;
- return fout;
-}
-
-ostream& Trans::print(ostream& fout) const
-{
- if (arity > 0)
- fout << "\top " << n << ": state " << state->s << endl;
- else if (x == NULL)
- fout << "\tvar _: state " << state->s << endl;
- else
- fout << "\tcst " << *x << ": state " << state->s << endl;
- return fout;
-}
-
-ostream& State::print(ostream& fout) const
-{
- fout << "state " << s << ":";
- list<Rule>::const_iterator r;
- for (r = rules.begin(); r != rules.end(); r++)
- fout << " " << *r;
- fout << endl;
- list<Trans>::const_iterator t;
- for (t = trans.begin(); t != trans.end(); t++)
- fout << *t;
- return fout;
-}
-
-ostream& Automaton::print(ostream& fout) const
-{
- int i, n = rhs.size();
- for (i = 0; i < n; i++)
- fout << "rule #" << i << ": " << *rhs[i] << endl;
- n = state.size();
- for (i = 0; i < n; i++)
- fout << *state[i];
- return fout;
-}
-#endif
-
-/* Construction algorithm for the pattern matching automaton.
- *
- * We employ the incremental technique described in Graef: Left-To-Right Tree
- * Pattern Matching, Proc. RTA 1991, Springer 1991 (LNCS 488) to construct a
- * tree automaton (TA) for the given patterns. The basic outline of the
- * technique is as follows. Initially, the automaton is empty. From each
- * pattern we produce a trie (considering the pattern as a string of variable
- * and function symbols and constants). This trie is then merged with the TA
- * obtained so far. The latter process is similar to merging two deterministic
- * finite automata, but it also takes into account the variables (see the
- * merge_state() routine below).
- */
-
-/* Construct a trie from a term tree. Takes the "start" and returns the "end"
- state of the (sub-)trie. */
-
-static State *make_state(State *state, int r, Tree x, Path& p)
-{
- Tree id, x0, x1;
- Node op(0);
- if (isBoxPatternVar(x, id)) {
- /* variable */
- Rule rule(r, id, p);
- state->rules.push_back(rule);
- Trans trans(NULL);
- state->trans.push_back(trans);
- return state->trans.begin()->state;
- } else if (isBoxPatternOp(x, op, x0, x1)) {
- /* composite pattern */
- Rule rule(r, NULL);
- state->rules.push_back(rule);
- Trans trans(op, 2);
- state->trans.push_back(trans);
- State *next = state->trans.begin()->state;
- p.push_back(0);
- next = make_state(next, r, x0, p);
- p.pop_back();
- p.push_back(1);
- next = make_state(next, r, x1, p);
- p.pop_back();
- return next;
- } else {
- /* constant */
- Rule rule(r, NULL);
- state->rules.push_back(rule);
- Trans trans(x);
- state->trans.push_back(trans);
- return state->trans.begin()->state;
- }
-}
-
-/* Take a copy of a state and prefix it with n variable transitions. */
-
-static State *make_var_state(int n, State *state)
-{
- if (n <= 0)
- return new State(*state);
- list<Rule>rules = state->rules;
- list<Rule>::iterator r;
- for (r = rules.begin(); r != rules.end(); r++) {
- r->id = NULL; r->p = Path();
- }
- State *prefix = new State, *current = prefix;
- while (n-- > 0) {
- current->rules = rules;
- Trans trans(NULL);
- current->trans.push_back(trans);
- current = current->trans.begin()->state;
- }
- *current = *state;
- return prefix;
-}
-
-/* Merge two tree automata. Merges the tree automaton rooted at state2 into
- the automaton rooted at state1. We assume that state2 is in "trie" form,
- i.e., each state has at most one transition, which is always guaranteed
- here and simplifies the algorithm. */
-
-static void merge_state(State *state1, State *state2);
-
-static void inline merge_rules(list<Rule>& rules1, list<Rule>& rules2)
-{
- list<Rule> cprules2 = rules2;
- rules1.merge(cprules2);
-}
-
-static void merge_trans_var(list<Trans>& trans, State *state)
-{
- if (!trans.begin()->is_var_trans()) {
- /* If we don't have a variable transition in this state yet then create a
- new one. */
- Trans tr(NULL);
- trans.push_front(tr);
- }
- list<Trans>::const_iterator t;
- Tree x;
- Node op(0);
- for (t = trans.begin(); t != trans.end(); t++) {
- if (t->is_var_trans())
- merge_state(t->state, state);
- else if (t->is_cst_trans(x)) {
- /* add the completion of the given state for a constant */
- merge_state(t->state, state);
- } else if (t->is_op_trans(op)) {
- /* add the completion of the given state for an arity>0 op */
- State *state1 = make_var_state(t->arity, state);
- merge_state(t->state, state1);
- delete state1;
- }
- }
-}
-
-static void merge_trans_cst(list<Trans>& trans, Tree x, State *state)
-{
- list<Trans>::iterator t0 = trans.begin(), t1 = t0, t;
- Tree x1;
- if (t0->is_var_trans()) t1++;
- for (t = t1; t != trans.end(); t++) {
- if (t->is_cst_trans(x1)) {
- if (x == x1) {
- merge_state(t->state, state);
- return;
- } else if (x < x1)
- break;
- }
- }
- /* no matching transition has been found; add a new one */
- Trans tr(x);
- trans.insert(t, tr); t--;
- State *state1 = t->state;
- *state1 = *state;
- if (t1 != t0) {
- /* if we have a variable transition in the current state, we also need to
- merge its completion for the current symbol/constant */
- merge_state(state1, t0->state);
- }
-}
-
-static void merge_trans_op(list<Trans>& trans, const Node& op,
- int arity, State *state)
-{
- /* analogous to merge_trans_cst above, but handles the arity>0 case */
- list<Trans>::iterator t0 = trans.begin(), t1 = t0, t;
- Node op1(0);
- if (t0->is_var_trans()) t1++;
- for (t = t1; t != trans.end(); t++) {
- if (t->is_op_trans(op1)) {
- if (op == op1) {
- merge_state(t->state, state);
- return;
- } else if (op.getSym() < op1.getSym())
- break;
- }
- }
- Trans tr(op, arity);
- trans.insert(t, tr); t--;
- State *state1 = t->state;
- *state1 = *state;
- if (t1 != t0) {
- State *state2 = make_var_state(arity, t0->state);
- merge_state(state1, state2);
- delete state2;
- }
-}
-
-static void merge_trans(list<Trans>& trans1, list<Trans>& trans2)
-{
- Tree x;
- Node op(0);
- if (trans2.empty())
- ;
- else if (trans1.empty()) {
- list<Trans> cptrans2 = trans2;
- /* append a copy of trans2 to trans1 */
- trans1.splice(trans1.end(), cptrans2);
- } else if (trans2.begin()->is_var_trans())
- /* merge a variable transition */
- merge_trans_var(trans1, trans2.begin()->state);
- else if (trans2.begin()->is_cst_trans(x))
- /* merge a constant transition */
- merge_trans_cst(trans1, x, trans2.begin()->state);
- else if (trans2.begin()->is_op_trans(op))
- /* merge a BDA op transition */
- merge_trans_op(trans1, op, trans2.begin()->arity, trans2.begin()->state);
-}
-
-static void merge_state(State *state1, State *state2)
-{
- merge_rules(state1->rules, state2->rules);
- merge_trans(state1->trans, state2->trans);
-}
-
-/* Take the rules of a BoxCase expression and return a pointer to the
- corresponding TA automaton (interface operation). */
-
-Automaton *make_pattern_matcher(Tree R)
-/* Tree R encodes the rules of a case box expressions as a Tree object, as
- follows:
-
- Rules ::= cons Rule (cons Rule ... nil)
- Rule ::= cons Lhs Rhs
- Lhs ::= cons Pattern (cons Pattern ... nil)
- Pattern ::= Tree (parameter pattern)
- Rhs ::= Tree
-
- NOTE: The lists of rules and patterns are actually delivered in reverse
- order by the parser, so we have to reverse them on the fly. */
-{
- Automaton *A = new Automaton;
- int n = len(R), r = n;
- State *start = new State;
- Tree rule, rest;
- vector<Tree> rules(n, (Tree)NULL);
- vector< vector<Tree> > testpats(n);
- while (isCons(R, rule, rest)) {
- rules[--r] = rule;
- R = rest;
- }
- for (r = 0; r < n; r++) {
- Tree lhs, rhs;
- if (isCons(rules[r], lhs, rhs)) {
- Tree pat, rest;
- int m = len(lhs), i = m;
- vector<Tree> pats(len(lhs), (Tree)NULL);
- State *state0 = new State, *state = state0;
- A->rhs.push_back(rhs);
- while (isCons(lhs, pat, rest)) {
- pats[--i] = pat;
- lhs = rest;
- }
- testpats[r] = pats;
- for (i = 0; i < m; i++) {
- Path p;
- state = make_state(state, r, pats[i], p);
- }
- Rule rule(r, NULL);
- state->rules.push_back(rule);
- merge_state(start, state0);
- delete state0;
- }
- }
- A->build(start);
- /* Check for shadowed rules. Note that because of potential nonlinearities
- it is *not* enough to just check the rule lists of final states and
- determine whether they have multiple matched rules. */
- for (r = 0; r < n; r++) {
- int s = 0, m = testpats[r].size();
- Tree C;
- vector<Tree> E(n, nil);
- /* try to match the lhs of rule #r */
- for (int i = 0; i < m; i++) {
- s = apply_pattern_matcher(A, s, testpats[r][i], C, E);
- if (s < 0) break;
- }
- if (A->final(s)) {
- list<Rule>::const_iterator ru;
- for (ru = A->rules(s).begin(); ru != A->rules(s).end(); ru++)
- if (!isBoxError(E[ru->r]))
- if (ru->r < r) {
- /* Lhs of rule #r matched a higher-priority rule, so rule #r may
- be shadowed. */
- Tree lhs1, rhs1, lhs2, rhs2;
- if (isCons(rules[ru->r], lhs1, rhs1) && isCons(rules[r], lhs2, rhs2)) {
- cerr << "WARNING : shadowed pattern-matching rule: "
- << boxpp(reverse(lhs2)) << " => " << boxpp(rhs2) << ";"
- << " previous rule was: "
- << boxpp(reverse(lhs1)) << " => " << boxpp(rhs1) << ";"
- << endl;
- } else {
- cerr << "INTERNAL ERROR : " << __FILE__ << ":" << __LINE__ << endl;
- exit(1);
- }
- } else if (ru->r >= r)
- break;
- }
- }
-#ifdef DEBUG
- cerr << "automaton " << A << endl << *A << "end automaton" << endl;
-#endif
- return A;
-}
-
-/* Helper type to represent variable substitutions which are recorded during
- matching. Each variable is associated with the path pointing at the subterm
- of the argument where the substitution of the matched variable is to be
- found. */
-
-struct Assoc {
- Tree id;
- Path p;
- Assoc(Tree _id, const Path& _p) : id(_id), p(_p) {}
-};
-typedef list<Assoc> Subst;
-
-/* add all substitutions for a given state */
-
-static void add_subst(vector<Subst>& subst, Automaton *A, int s)
-{
- list<Rule> rules = A->rules(s);
- list<Rule>::const_iterator r;
- for (r = rules.begin(); r != rules.end(); r++)
- if (r->id != NULL)
- subst[r->r].push_back(Assoc(r->id, r->p));
-}
-
-/* Process a given term tree X starting from state s, modify variable
- substitutions accordingly. Returns the resulting state, or -1 if no
- match. This does all the grunt work of matching. */
-
-static int apply_pattern_matcher_internal(Automaton *A, int s, Tree X,
- vector<Subst>& subst)
-{
- /* FIXME: rewrite this non-recursively? */
- if (s >= 0) {
- list<Trans>::const_iterator t;
- if (A->state[s]->match_num)
- /* simplify possible numeric argument on the fly */
- X = simplifyPattern(X);
- /* first check for applicable non-variable transitions */
- for (t = A->trans(s).begin(); t != A->trans(s).end(); t++) {
- Tree x;
- Node op(0), op1(0);
- if (t->is_var_trans())
- continue;
- else if (t->is_cst_trans(x)) {
- if (X==x) {
- /* transition on constant */
-#ifdef DEBUG
- cerr << "state " << s << ", " << *x << ": goto state " << t->state->s << endl;
-#endif
- add_subst(subst, A, s);
- s = t->state->s;
- return s;
- }
- } else if (t->is_op_trans(op)) {
- Tree x0, x1;
- if (isBoxPatternOp(X, op1, x0, x1) && op == op1) {
- /* transition on operation symbol */
-#ifdef DEBUG
- cerr << "state " << s << ", " << op << ": goto state " << t->state->s << endl;
-#endif
- add_subst(subst, A, s);
- s = t->state->s;
- if (s >= 0)
- s = apply_pattern_matcher_internal(A, s, x0, subst);
- if (s >= 0)
- s = apply_pattern_matcher_internal(A, s, x1, subst);
- return s;
- }
- }
- }
- /* check for variable transition (is always first in the list of
- transitions) */
- t = A->trans(s).begin();
- if (t->is_var_trans()) {
-#ifdef DEBUG
- cerr << "state " << s << ", _: goto state " << t->state->s << endl;
-#endif
- add_subst(subst, A, s);
- s = t->state->s;
- } else {
-#ifdef DEBUG
- cerr << "state " << s << ", *** match failed ***" << endl;
-#endif
- s = -1;
- }
- }
- return s;
-}
-
-/* Apply the pattern matcher to a single argument, starting from a given state
- (interface operation). Returns the resulting state, modifies the variable
- bindings E accordingly, and sets C to the resulting closure if a final
- state is reached. Result will be -1 to indicate a matching failure, and C
- will be set to nil if no final state has been reached yet. */
-
-int apply_pattern_matcher(Automaton *A, // automaton
- int s, // start state
- Tree X, // arg to be matched
- Tree& C, // output closure (if any)
- vector<Tree>& E) // modified output environments
-{
- int n = A->n_rules();
- vector<Subst> subst(n, Subst());
- /* perform matching, record variable substitutions */
-#ifdef DEBUG
- cerr << "automaton " << A << ", state " << s << ", start match on arg: " << *X << endl;
-#endif
- s = apply_pattern_matcher_internal(A, s, X, subst);
- C = nil;
- if (s < 0)
- /* failed match */
- return s;
- /* process variable substitutions */
- list<Rule>::const_iterator r;
- for (r = A->rules(s).begin(); r != A->rules(s).end(); r++) {
- // all rules still active in state s
- if (!isBoxError(E[r->r])) { // and still viable
- Subst::const_iterator assoc;
- for (assoc = subst[r->r].begin(); assoc != subst[r->r].end(); assoc++) {
- Tree Z, Z1 = subtree(X, 0, assoc->p);
- if (searchIdDef(assoc->id, Z, E[r->r])) {
- if (Z != Z1) {
- /* failed nonlinearity, add to the set of nonviable rules */
-#ifdef DEBUG
- cerr << "state " << s << ", rule #" << r->r << ": " <<
- *assoc->id << " := " << *Z1 << " *** failed *** old value: " <<
- *Z << endl;
-#endif
- E[r->r] = boxError();
- }
- } else {
- /* bind a variable for the current rule */
-#ifdef DEBUG
- cerr << "state " << s << ", rule #" << r->r << ": " <<
- *assoc->id << " := " << *Z1 << endl;
-#endif
- E[r->r] = pushValueDef(assoc->id, Z1, E[r->r]);
- }
- }
- }
- }
- if (A->final(s)) {
- /* if in a final state then return the right-hand side together with the
- corresponding variable environment */
- for (r = A->rules(s).begin(); r != A->rules(s).end(); r++) // all rules matched in state s
- if (!isBoxError(E[r->r])) { // and still viable
- /* return the rhs of the matched rule */
- C = closure(A->rhs[r->r], nil, nil, E[r->r]);
-#ifdef DEBUG
- cerr << "state " << s << ", complete match yields rhs #" << r->r <<
- ": " << *A->rhs[r->r] << endl;
-#endif
- return s;
- }
- /* if none of the rules were matched then declare a failed match */
-#ifdef DEBUG
- cerr << "state " << s << ", *** match failed ***" << endl;
-#endif
- return -1;
- }
-#ifdef DEBUG
- cerr << "state " << s << ", successful incomplete match" << endl;
-#endif
- return s;
-}