2 /* compiler/patternmatcher/patternmatcher.cpp: implementation of the Faust
10 #include "patternmatcher.hh"
18 /* Uncomment for debugging output. */
21 /* Additional Tree deconstruction operations. */
23 /* Check for cons (nonempty list) nodes. */
25 static inline bool isCons(Tree x
, Tree
& h
, Tree
& t
)
34 /* Deconstruct a (BDA) op pattern (YO). */
36 static inline bool isBoxPatternOp(Tree box
, Node
& n
, Tree
& t1
, Tree
& t2
)
38 if ( isBoxPar(box
, t1
, t2
) ||
39 isBoxSeq(box
, t1
, t2
) ||
40 isBoxSplit(box
, t1
, t2
) ||
41 isBoxMerge(box
, t1
, t2
) ||
42 isBoxHGroup(box
, t1
, t2
) ||
43 isBoxVGroup(box
, t1
, t2
) ||
44 isBoxTGroup(box
, t1
, t2
) ||
45 isBoxRec(box
, t1
, t2
) )
54 /* TA data structures. */
58 typedef vector
<int> Path
;
60 /* Subterm at given path in given term tree. */
62 static Tree
subtree(Tree X
, int i
, const Path
& p
)
67 if (i
< n
&& isBoxPatternOp(X
, op
, x0
, x1
))
68 return subtree((p
[i
]==0)?x0
:x1
, i
+1, p
);
77 Tree id
; // matched variable (NULL if none)
78 Path p
; // subterm path indicating where variable value is found
80 Rule(int _r
, Tree _id
) : r(_r
), id(_id
), p(Path()) {}
81 Rule(int _r
, Tree _id
, const Path
& _p
) : r(_r
), id(_id
), p(_p
) {}
82 Rule(const Rule
& rule
) : r(rule
.r
), id(rule
.id
), p(rule
.p
) {}
84 Rule
& operator = (const Rule
& rule
)
85 { r
= rule
.r
; id
= rule
.id
; p
= rule
.p
; return *this; }
87 bool operator == (const Rule
& rule
) const
88 { return r
== rule
.r
; }
89 bool operator < (const Rule
& rule
) const
90 { return r
< rule
.r
; }
93 ostream
& print(ostream
& fout
) const;
102 Tree x
; // symbol or constant (NULL for variable)
103 Node n
; // operator symbol (if arity>0)
104 int arity
; // symbol arity
105 State
*state
; // successor state
108 Trans(const Node
& _n
, int _arity
);
109 Trans(const Trans
& trans
);
112 Trans
& operator = (const Trans
& trans
);
114 bool is_var_trans() const { return arity
== 0 && x
== NULL
; }
115 bool is_cst_trans(Tree
&_x
) const { _x
= x
; return arity
== 0 && x
!= NULL
; }
116 bool is_op_trans(Node
&_n
) const { _n
= n
; return arity
> 0; }
118 bool operator == (const Trans
& trans
) const
119 { return arity
== trans
.arity
&& x
== trans
.x
&& n
== trans
.n
; }
120 bool operator < (const Trans
& trans
) const
121 { return (arity
< trans
.arity
) ? 1 :
122 (arity
> trans
.arity
) ? 0 :
123 (arity
== 0) ? (x
< trans
.x
) :
124 (n
.getSym() < trans
.n
.getSym()); }
127 ostream
& print(ostream
& fout
) const;
134 int s
; // state number
135 bool match_num
; // whether state has a transition on a numeric constant
136 list
<Rule
> rules
; // rule markers
137 list
<Trans
> trans
; // transitions (1st transition is on variable if available)
139 s(0), match_num(false), rules(list
<Rule
>()), trans(list
<Trans
>()) {}
140 State(const State
& state
) :
141 s(state
.s
), match_num(state
.match_num
),
142 rules(state
.rules
), trans(state
.trans
) {}
144 State
& operator = (const State
& state
)
145 { s
= state
.s
; match_num
= state
.match_num
;
146 rules
= state
.rules
; trans
= state
.trans
;
151 ostream
& print(ostream
& fout
) const;
155 // these need to come here so that the storage size of struct State is known
157 Trans::Trans(Tree _x
) :
158 x(_x
), n(0), arity(0), state(new State
)
162 Trans::Trans(const Node
& _n
, int _arity
) :
163 x(NULL
), n(_n
), arity(_arity
), state(new State
)
167 Trans::Trans(const Trans
& trans
) :
168 x(trans
.x
), n(trans
.n
), arity(trans
.arity
)
170 state
= new State(*trans
.state
);
178 Trans
& Trans::operator = (const Trans
& trans
)
180 x
= trans
.x
; n
= trans
.n
; arity
= trans
.arity
;
181 state
= new State(*trans
.state
);
188 vector
<State
*> state
;
191 Automaton() : state(vector
<State
*>()), rhs(vector
<Tree
>()), s(0) {}
194 int n_rules() { return rhs
.size(); }
195 // markers of rules still active in state s
196 const list
<Rule
>& rules(int s
) { return state
[s
]->rules
; }
197 // transitions in state s
198 const list
<Trans
>& trans(int s
) { return state
[s
]->trans
; }
199 // is s a final state?
200 bool final(int s
) { return trans(s
).empty(); }
202 // assign state numbers and build the state table
204 void build(State
*st
);
207 ostream
& print(ostream
& fout
) const;
211 void Automaton::build(State
*st
)
215 list
<Trans
>::const_iterator t
;
216 for (t
= st
->trans
.begin(); t
!= st
->trans
.end(); t
++) {
220 if (t
->is_cst_trans(x
) &&
221 (isBoxInt(x
, &i
) || isBoxReal(x
, &f
)))
222 st
->match_num
= true;
227 /* Debugging output. */
230 inline ostream
& operator << (ostream
& s
, const Rule
& x
)
231 { return x
.print(s
); }
232 inline ostream
& operator << (ostream
& s
, const Trans
& x
)
233 { return x
.print(s
); }
234 inline ostream
& operator << (ostream
& s
, const State
& x
)
235 { return x
.print(s
); }
236 inline ostream
& operator << (ostream
& s
, const Automaton
& x
)
237 { return x
.print(s
); }
239 ostream
& Rule::print(ostream
& fout
) const
242 fout
<< "#" << r
<< "(" << *id
<< ")";
248 ostream
& Trans::print(ostream
& fout
) const
251 fout
<< "\top " << n
<< ": state " << state
->s
<< endl
;
253 fout
<< "\tvar _: state " << state
->s
<< endl
;
255 fout
<< "\tcst " << *x
<< ": state " << state
->s
<< endl
;
259 ostream
& State::print(ostream
& fout
) const
261 fout
<< "state " << s
<< ":";
262 list
<Rule
>::const_iterator r
;
263 for (r
= rules
.begin(); r
!= rules
.end(); r
++)
266 list
<Trans
>::const_iterator t
;
267 for (t
= trans
.begin(); t
!= trans
.end(); t
++)
272 ostream
& Automaton::print(ostream
& fout
) const
274 int i
, n
= rhs
.size();
275 for (i
= 0; i
< n
; i
++)
276 fout
<< "rule #" << i
<< ": " << *rhs
[i
] << endl
;
278 for (i
= 0; i
< n
; i
++)
284 /* Construction algorithm for the pattern matching automaton.
286 * We employ the incremental technique described in Graef: Left-To-Right Tree
287 * Pattern Matching, Proc. RTA 1991, Springer 1991 (LNCS 488) to construct a
288 * tree automaton (TA) for the given patterns. The basic outline of the
289 * technique is as follows. Initially, the automaton is empty. From each
290 * pattern we produce a trie (considering the pattern as a string of variable
291 * and function symbols and constants). This trie is then merged with the TA
292 * obtained so far. The latter process is similar to merging two deterministic
293 * finite automata, but it also takes into account the variables (see the
294 * merge_state() routine below).
297 /* Construct a trie from a term tree. Takes the "start" and returns the "end"
298 state of the (sub-)trie. */
300 static State
*make_state(State
*state
, int r
, Tree x
, Path
& p
)
304 if (isBoxPatternVar(x
, id
)) {
307 state
->rules
.push_back(rule
);
309 state
->trans
.push_back(trans
);
310 return state
->trans
.begin()->state
;
311 } else if (isBoxPatternOp(x
, op
, x0
, x1
)) {
312 /* composite pattern */
314 state
->rules
.push_back(rule
);
316 state
->trans
.push_back(trans
);
317 State
*next
= state
->trans
.begin()->state
;
319 next
= make_state(next
, r
, x0
, p
);
322 next
= make_state(next
, r
, x1
, p
);
328 state
->rules
.push_back(rule
);
330 state
->trans
.push_back(trans
);
331 return state
->trans
.begin()->state
;
335 /* Take a copy of a state and prefix it with n variable transitions. */
337 static State
*make_var_state(int n
, State
*state
)
340 return new State(*state
);
341 list
<Rule
>rules
= state
->rules
;
342 list
<Rule
>::iterator r
;
343 for (r
= rules
.begin(); r
!= rules
.end(); r
++) {
344 r
->id
= NULL
; r
->p
= Path();
346 State
*prefix
= new State
, *current
= prefix
;
348 current
->rules
= rules
;
350 current
->trans
.push_back(trans
);
351 current
= current
->trans
.begin()->state
;
357 /* Merge two tree automata. Merges the tree automaton rooted at state2 into
358 the automaton rooted at state1. We assume that state2 is in "trie" form,
359 i.e., each state has at most one transition, which is always guaranteed
360 here and simplifies the algorithm. */
362 static void merge_state(State
*state1
, State
*state2
);
364 static void inline merge_rules(list
<Rule
>& rules1
, list
<Rule
>& rules2
)
366 list
<Rule
> cprules2
= rules2
;
367 rules1
.merge(cprules2
);
370 static void merge_trans_var(list
<Trans
>& trans
, State
*state
)
372 if (!trans
.begin()->is_var_trans()) {
373 /* If we don't have a variable transition in this state yet then create a
376 trans
.push_front(tr
);
378 list
<Trans
>::const_iterator t
;
381 for (t
= trans
.begin(); t
!= trans
.end(); t
++) {
382 if (t
->is_var_trans())
383 merge_state(t
->state
, state
);
384 else if (t
->is_cst_trans(x
)) {
385 /* add the completion of the given state for a constant */
386 merge_state(t
->state
, state
);
387 } else if (t
->is_op_trans(op
)) {
388 /* add the completion of the given state for an arity>0 op */
389 State
*state1
= make_var_state(t
->arity
, state
);
390 merge_state(t
->state
, state1
);
396 static void merge_trans_cst(list
<Trans
>& trans
, Tree x
, State
*state
)
398 list
<Trans
>::iterator t0
= trans
.begin(), t1
= t0
, t
;
400 if (t0
->is_var_trans()) t1
++;
401 for (t
= t1
; t
!= trans
.end(); t
++) {
402 if (t
->is_cst_trans(x1
)) {
404 merge_state(t
->state
, state
);
410 /* no matching transition has been found; add a new one */
412 trans
.insert(t
, tr
); t
--;
413 State
*state1
= t
->state
;
416 /* if we have a variable transition in the current state, we also need to
417 merge its completion for the current symbol/constant */
418 merge_state(state1
, t0
->state
);
422 static void merge_trans_op(list
<Trans
>& trans
, const Node
& op
,
423 int arity
, State
*state
)
425 /* analogous to merge_trans_cst above, but handles the arity>0 case */
426 list
<Trans
>::iterator t0
= trans
.begin(), t1
= t0
, t
;
428 if (t0
->is_var_trans()) t1
++;
429 for (t
= t1
; t
!= trans
.end(); t
++) {
430 if (t
->is_op_trans(op1
)) {
432 merge_state(t
->state
, state
);
434 } else if (op
.getSym() < op1
.getSym())
439 trans
.insert(t
, tr
); t
--;
440 State
*state1
= t
->state
;
443 State
*state2
= make_var_state(arity
, t0
->state
);
444 merge_state(state1
, state2
);
449 static void merge_trans(list
<Trans
>& trans1
, list
<Trans
>& trans2
)
455 else if (trans1
.empty()) {
456 list
<Trans
> cptrans2
= trans2
;
457 /* append a copy of trans2 to trans1 */
458 trans1
.splice(trans1
.end(), cptrans2
);
459 } else if (trans2
.begin()->is_var_trans())
460 /* merge a variable transition */
461 merge_trans_var(trans1
, trans2
.begin()->state
);
462 else if (trans2
.begin()->is_cst_trans(x
))
463 /* merge a constant transition */
464 merge_trans_cst(trans1
, x
, trans2
.begin()->state
);
465 else if (trans2
.begin()->is_op_trans(op
))
466 /* merge a BDA op transition */
467 merge_trans_op(trans1
, op
, trans2
.begin()->arity
, trans2
.begin()->state
);
470 static void merge_state(State
*state1
, State
*state2
)
472 merge_rules(state1
->rules
, state2
->rules
);
473 merge_trans(state1
->trans
, state2
->trans
);
476 /* Take the rules of a BoxCase expression and return a pointer to the
477 corresponding TA automaton (interface operation). */
479 Automaton
*make_pattern_matcher(Tree R
)
480 /* Tree R encodes the rules of a case box expressions as a Tree object, as
483 Rules ::= cons Rule (cons Rule ... nil)
484 Rule ::= cons Lhs Rhs
485 Lhs ::= cons Pattern (cons Pattern ... nil)
486 Pattern ::= Tree (parameter pattern)
489 NOTE: The lists of rules and patterns are actually delivered in reverse
490 order by the parser, so we have to reverse them on the fly. */
492 Automaton
*A
= new Automaton
;
493 int n
= len(R
), r
= n
;
494 State
*start
= new State
;
496 vector
<Tree
> rules(n
, (Tree
)NULL
);
497 vector
< vector
<Tree
> > testpats(n
);
498 while (isCons(R
, rule
, rest
)) {
502 for (r
= 0; r
< n
; r
++) {
504 if (isCons(rules
[r
], lhs
, rhs
)) {
506 int m
= len(lhs
), i
= m
;
507 vector
<Tree
> pats(len(lhs
), (Tree
)NULL
);
508 State
*state0
= new State
, *state
= state0
;
509 A
->rhs
.push_back(rhs
);
510 while (isCons(lhs
, pat
, rest
)) {
515 for (i
= 0; i
< m
; i
++) {
517 state
= make_state(state
, r
, pats
[i
], p
);
520 state
->rules
.push_back(rule
);
521 merge_state(start
, state0
);
526 /* Check for shadowed rules. Note that because of potential nonlinearities
527 it is *not* enough to just check the rule lists of final states and
528 determine whether they have multiple matched rules. */
529 for (r
= 0; r
< n
; r
++) {
530 int s
= 0, m
= testpats
[r
].size();
532 vector
<Tree
> E(n
, nil
);
533 /* try to match the lhs of rule #r */
534 for (int i
= 0; i
< m
; i
++) {
535 s
= apply_pattern_matcher(A
, s
, testpats
[r
][i
], C
, E
);
539 list
<Rule
>::const_iterator ru
;
540 for (ru
= A
->rules(s
).begin(); ru
!= A
->rules(s
).end(); ru
++)
541 if (!isBoxError(E
[ru
->r
]))
543 /* Lhs of rule #r matched a higher-priority rule, so rule #r may
545 Tree lhs1
, rhs1
, lhs2
, rhs2
;
546 if (isCons(rules
[ru
->r
], lhs1
, rhs1
) && isCons(rules
[r
], lhs2
, rhs2
)) {
547 cerr
<< "WARNING : shadowed pattern-matching rule: "
548 << boxpp(reverse(lhs2
)) << " => " << boxpp(rhs2
) << ";"
549 << " previous rule was: "
550 << boxpp(reverse(lhs1
)) << " => " << boxpp(rhs1
) << ";"
553 cerr
<< "INTERNAL ERROR : " << __FILE__
<< ":" << __LINE__
<< endl
;
556 } else if (ru
->r
>= r
)
561 cerr
<< "automaton " << A
<< endl
<< *A
<< "end automaton" << endl
;
566 /* Helper type to represent variable substitutions which are recorded during
567 matching. Each variable is associated with the path pointing at the subterm
568 of the argument where the substitution of the matched variable is to be
574 Assoc(Tree _id
, const Path
& _p
) : id(_id
), p(_p
) {}
576 typedef list
<Assoc
> Subst
;
578 /* add all substitutions for a given state */
580 static void add_subst(vector
<Subst
>& subst
, Automaton
*A
, int s
)
582 list
<Rule
> rules
= A
->rules(s
);
583 list
<Rule
>::const_iterator r
;
584 for (r
= rules
.begin(); r
!= rules
.end(); r
++)
586 subst
[r
->r
].push_back(Assoc(r
->id
, r
->p
));
589 /* Process a given term tree X starting from state s, modify variable
590 substitutions accordingly. Returns the resulting state, or -1 if no
591 match. This does all the grunt work of matching. */
593 static int apply_pattern_matcher_internal(Automaton
*A
, int s
, Tree X
,
594 vector
<Subst
>& subst
)
596 /* FIXME: rewrite this non-recursively? */
598 list
<Trans
>::const_iterator t
;
599 if (A
->state
[s
]->match_num
)
600 /* simplify possible numeric argument on the fly */
601 X
= simplifyPattern(X
);
602 /* first check for applicable non-variable transitions */
603 for (t
= A
->trans(s
).begin(); t
!= A
->trans(s
).end(); t
++) {
606 if (t
->is_var_trans())
608 else if (t
->is_cst_trans(x
)) {
610 /* transition on constant */
612 cerr
<< "state " << s
<< ", " << *x
<< ": goto state " << t
->state
->s
<< endl
;
614 add_subst(subst
, A
, s
);
618 } else if (t
->is_op_trans(op
)) {
620 if (isBoxPatternOp(X
, op1
, x0
, x1
) && op
== op1
) {
621 /* transition on operation symbol */
623 cerr
<< "state " << s
<< ", " << op
<< ": goto state " << t
->state
->s
<< endl
;
625 add_subst(subst
, A
, s
);
628 s
= apply_pattern_matcher_internal(A
, s
, x0
, subst
);
630 s
= apply_pattern_matcher_internal(A
, s
, x1
, subst
);
635 /* check for variable transition (is always first in the list of
637 t
= A
->trans(s
).begin();
638 if (t
->is_var_trans()) {
640 cerr
<< "state " << s
<< ", _: goto state " << t
->state
->s
<< endl
;
642 add_subst(subst
, A
, s
);
646 cerr
<< "state " << s
<< ", *** match failed ***" << endl
;
654 /* Apply the pattern matcher to a single argument, starting from a given state
655 (interface operation). Returns the resulting state, modifies the variable
656 bindings E accordingly, and sets C to the resulting closure if a final
657 state is reached. Result will be -1 to indicate a matching failure, and C
658 will be set to nil if no final state has been reached yet. */
660 int apply_pattern_matcher(Automaton
*A
, // automaton
661 int s
, // start state
662 Tree X
, // arg to be matched
663 Tree
& C
, // output closure (if any)
664 vector
<Tree
>& E
) // modified output environments
666 int n
= A
->n_rules();
667 vector
<Subst
> subst(n
, Subst());
668 /* perform matching, record variable substitutions */
670 cerr
<< "automaton " << A
<< ", state " << s
<< ", start match on arg: " << *X
<< endl
;
672 s
= apply_pattern_matcher_internal(A
, s
, X
, subst
);
677 /* process variable substitutions */
678 list
<Rule
>::const_iterator r
;
679 for (r
= A
->rules(s
).begin(); r
!= A
->rules(s
).end(); r
++) {
680 // all rules still active in state s
681 if (!isBoxError(E
[r
->r
])) { // and still viable
682 Subst::const_iterator assoc
;
683 for (assoc
= subst
[r
->r
].begin(); assoc
!= subst
[r
->r
].end(); assoc
++) {
684 Tree Z
, Z1
= subtree(X
, 0, assoc
->p
);
685 if (searchIdDef(assoc
->id
, Z
, E
[r
->r
])) {
687 /* failed nonlinearity, add to the set of nonviable rules */
689 cerr
<< "state " << s
<< ", rule #" << r
->r
<< ": " <<
690 *assoc
->id
<< " := " << *Z1
<< " *** failed *** old value: " <<
693 E
[r
->r
] = boxError();
696 /* bind a variable for the current rule */
698 cerr
<< "state " << s
<< ", rule #" << r
->r
<< ": " <<
699 *assoc
->id
<< " := " << *Z1
<< endl
;
701 E
[r
->r
] = pushValueDef(assoc
->id
, Z1
, E
[r
->r
]);
707 /* if in a final state then return the right-hand side together with the
708 corresponding variable environment */
709 for (r
= A
->rules(s
).begin(); r
!= A
->rules(s
).end(); r
++) // all rules matched in state s
710 if (!isBoxError(E
[r
->r
])) { // and still viable
711 /* return the rhs of the matched rule */
712 C
= closure(A
->rhs
[r
->r
], nil
, nil
, E
[r
->r
]);
714 cerr
<< "state " << s
<< ", complete match yields rhs #" << r
->r
<<
715 ": " << *A
->rhs
[r
->r
] << endl
;
719 /* if none of the rules were matched then declare a failed match */
721 cerr
<< "state " << s
<< ", *** match failed ***" << endl
;
726 cerr
<< "state " << s
<< ", successful incomplete match" << endl
;