| author | haftmann | 
| Wed, 10 Feb 2010 15:52:12 +0100 | |
| changeset 35097 | 4554bb2abfa3 | 
| parent 33957 | e9afca2118d4 | 
| child 38328 | 36afb56ec49e | 
| permissions | -rw-r--r-- | 
| 18 | 1  | 
(* Title: Pure/Syntax/ast.ML  | 
| 0 | 2  | 
Author: Markus Wenzel, TU Muenchen  | 
3  | 
||
| 18 | 4  | 
Abstract syntax trees, translation rules, matching and normalization of asts.  | 
| 0 | 5  | 
*)  | 
6  | 
||
| 18 | 7  | 
signature AST0 =  | 
| 16609 | 8  | 
sig  | 
| 0 | 9  | 
datatype ast =  | 
10  | 
Constant of string |  | 
|
11  | 
Variable of string |  | 
|
12  | 
Appl of ast list  | 
|
| 258 | 13  | 
exception AST of string * ast list  | 
| 16609 | 14  | 
end;  | 
| 258 | 15  | 
|
16  | 
signature AST1 =  | 
|
| 16609 | 17  | 
sig  | 
| 258 | 18  | 
include AST0  | 
| 0 | 19  | 
val mk_appl: ast -> ast list -> ast  | 
| 18 | 20  | 
val str_of_ast: ast -> string  | 
21  | 
val pretty_ast: ast -> Pretty.T  | 
|
| 258 | 22  | 
val pretty_rule: ast * ast -> Pretty.T  | 
| 10913 | 23  | 
val fold_ast: string -> ast list -> ast  | 
24  | 
val fold_ast_p: string -> ast list * ast -> ast  | 
|
25  | 
val unfold_ast: string -> ast -> ast list  | 
|
26  | 
val unfold_ast_p: string -> ast -> ast list * ast  | 
|
| 32738 | 27  | 
val trace_ast: bool Unsynchronized.ref  | 
28  | 
val stat_ast: bool Unsynchronized.ref  | 
|
| 16609 | 29  | 
end;  | 
| 18 | 30  | 
|
31  | 
signature AST =  | 
|
| 16609 | 32  | 
sig  | 
| 258 | 33  | 
include AST1  | 
| 0 | 34  | 
val head_of_rule: ast * ast -> string  | 
35  | 
val rule_error: ast * ast -> string option  | 
|
| 
9372
 
7834e56e2277
AST translation rules no longer require constant head on LHS;
 
wenzelm 
parents: 
8997 
diff
changeset
 | 
36  | 
val normalize: bool -> bool -> (string -> (ast * ast) list) -> ast -> ast  | 
| 
 
7834e56e2277
AST translation rules no longer require constant head on LHS;
 
wenzelm 
parents: 
8997 
diff
changeset
 | 
37  | 
val normalize_ast: (string -> (ast * ast) list) -> ast -> ast  | 
| 16609 | 38  | 
end;  | 
| 0 | 39  | 
|
| 1506 | 40  | 
structure Ast : AST =  | 
| 0 | 41  | 
struct  | 
42  | 
||
| 18 | 43  | 
(** abstract syntax trees **)  | 
| 0 | 44  | 
|
45  | 
(*asts come in two flavours:  | 
|
| 18 | 46  | 
- ordinary asts representing terms and typs: Variables are (often) treated  | 
47  | 
like Constants;  | 
|
| 0 | 48  | 
- patterns used as lhs and rhs in rules: Variables are placeholders for  | 
49  | 
proper asts*)  | 
|
50  | 
||
51  | 
datatype ast =  | 
|
| 18 | 52  | 
Constant of string | (*"not", "_abs", "fun"*)  | 
53  | 
Variable of string | (*x, ?x, 'a, ?'a*)  | 
|
54  | 
  Appl of ast list;       (*(f x y z), ("fun" 'a 'b), ("_abs" x t)*)
 | 
|
| 0 | 55  | 
|
56  | 
||
57  | 
(*the list of subasts of an Appl node has to contain at least 2 elements, i.e.  | 
|
58  | 
there are no empty asts or nullary applications; use mk_appl for convenience*)  | 
|
59  | 
||
| 18 | 60  | 
fun mk_appl f [] = f  | 
61  | 
| mk_appl f args = Appl (f :: args);  | 
|
| 0 | 62  | 
|
63  | 
||
64  | 
(*exception for system errors involving asts*)  | 
|
65  | 
||
66  | 
exception AST of string * ast list;  | 
|
67  | 
||
68  | 
||
69  | 
||
| 18 | 70  | 
(** print asts in a LISP-like style **)  | 
71  | 
||
| 0 | 72  | 
fun str_of_ast (Constant a) = quote a  | 
73  | 
| str_of_ast (Variable x) = x  | 
|
74  | 
  | str_of_ast (Appl asts) = "(" ^ (space_implode " " (map str_of_ast asts)) ^ ")";
 | 
|
75  | 
||
| 14599 | 76  | 
fun pretty_ast (Constant a) = Pretty.quote (Pretty.str a)  | 
| 18 | 77  | 
| pretty_ast (Variable x) = Pretty.str x  | 
78  | 
| pretty_ast (Appl asts) =  | 
|
| 513 | 79  | 
      Pretty.enclose "(" ")" (Pretty.breaks (map pretty_ast asts));
 | 
| 18 | 80  | 
|
81  | 
fun pretty_rule (lhs, rhs) =  | 
|
| 235 | 82  | 
Pretty.block [pretty_ast lhs, Pretty.str " ->", Pretty.brk 2, pretty_ast rhs];  | 
| 18 | 83  | 
|
84  | 
||
| 0 | 85  | 
(* head_of_ast, head_of_rule *)  | 
86  | 
||
| 
9372
 
7834e56e2277
AST translation rules no longer require constant head on LHS;
 
wenzelm 
parents: 
8997 
diff
changeset
 | 
87  | 
fun head_of_ast (Constant a) = a  | 
| 
 
7834e56e2277
AST translation rules no longer require constant head on LHS;
 
wenzelm 
parents: 
8997 
diff
changeset
 | 
88  | 
| head_of_ast (Appl (Constant a :: _)) = a  | 
| 
 
7834e56e2277
AST translation rules no longer require constant head on LHS;
 
wenzelm 
parents: 
8997 
diff
changeset
 | 
89  | 
| head_of_ast _ = "";  | 
| 0 | 90  | 
|
| 
9372
 
7834e56e2277
AST translation rules no longer require constant head on LHS;
 
wenzelm 
parents: 
8997 
diff
changeset
 | 
91  | 
fun head_of_rule (lhs, _) = head_of_ast lhs;  | 
| 0 | 92  | 
|
93  | 
||
94  | 
||
| 18 | 95  | 
(** check translation rules **)  | 
| 0 | 96  | 
|
| 32784 | 97  | 
fun rule_error (lhs, rhs) =  | 
| 0 | 98  | 
let  | 
| 19486 | 99  | 
fun add_vars (Constant _) = I  | 
100  | 
| add_vars (Variable x) = cons x  | 
|
101  | 
| add_vars (Appl asts) = fold add_vars asts;  | 
|
| 0 | 102  | 
|
| 19486 | 103  | 
val lvars = add_vars lhs [];  | 
104  | 
val rvars = add_vars rhs [];  | 
|
| 0 | 105  | 
in  | 
| 19486 | 106  | 
if has_duplicates (op =) lvars then SOME "duplicate vars in lhs"  | 
| 33038 | 107  | 
else if not (subset (op =) (rvars, lvars)) then SOME "rhs contains extra variables"  | 
| 15531 | 108  | 
else NONE  | 
| 0 | 109  | 
end;  | 
110  | 
||
111  | 
||
112  | 
||
| 18 | 113  | 
(** ast translation utilities **)  | 
| 0 | 114  | 
|
115  | 
(* fold asts *)  | 
|
116  | 
||
117  | 
fun fold_ast _ [] = raise Match  | 
|
118  | 
| fold_ast _ [y] = y  | 
|
119  | 
| fold_ast c (x :: xs) = Appl [Constant c, x, fold_ast c xs];  | 
|
120  | 
||
| 19473 | 121  | 
fun fold_ast_p c = uncurry (fold_rev (fn x => fn xs => Appl [Constant c, x, xs]));  | 
| 0 | 122  | 
|
123  | 
||
124  | 
(* unfold asts *)  | 
|
125  | 
||
126  | 
fun unfold_ast c (y as Appl [Constant c', x, xs]) =  | 
|
| 16609 | 127  | 
if c = c' then x :: unfold_ast c xs else [y]  | 
| 0 | 128  | 
| unfold_ast _ y = [y];  | 
129  | 
||
130  | 
fun unfold_ast_p c (y as Appl [Constant c', x, xs]) =  | 
|
| 18 | 131  | 
if c = c' then apfst (cons x) (unfold_ast_p c xs)  | 
| 0 | 132  | 
else ([], y)  | 
133  | 
| unfold_ast_p _ y = ([], y);  | 
|
134  | 
||
135  | 
||
| 16609 | 136  | 
|
| 0 | 137  | 
(** normalization of asts **)  | 
138  | 
||
139  | 
(* match *)  | 
|
140  | 
||
141  | 
fun match ast pat =  | 
|
142  | 
let  | 
|
143  | 
exception NO_MATCH;  | 
|
144  | 
||
| 
1127
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
145  | 
fun mtch (Constant a) (Constant b) env =  | 
| 0 | 146  | 
if a = b then env else raise NO_MATCH  | 
| 
1127
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
147  | 
| mtch (Variable a) (Constant b) env =  | 
| 0 | 148  | 
if a = b then env else raise NO_MATCH  | 
| 17412 | 149  | 
| mtch ast (Variable x) env = Symtab.update (x, ast) env  | 
| 
1127
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
150  | 
| mtch (Appl asts) (Appl pats) env = mtch_lst asts pats env  | 
| 
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
151  | 
| mtch _ _ _ = raise NO_MATCH  | 
| 
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
152  | 
and mtch_lst (ast :: asts) (pat :: pats) env =  | 
| 
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
153  | 
mtch_lst asts pats (mtch ast pat env)  | 
| 
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
154  | 
| mtch_lst [] [] env = env  | 
| 
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
155  | 
| mtch_lst _ _ _ = raise NO_MATCH;  | 
| 
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
156  | 
|
| 
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
157  | 
val (head, args) =  | 
| 
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
158  | 
(case (ast, pat) of  | 
| 
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
159  | 
(Appl asts, Appl pats) =>  | 
| 
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
160  | 
let val a = length asts and p = length pats in  | 
| 33957 | 161  | 
if a > p then (Appl (take p asts), drop p asts)  | 
| 
1127
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
162  | 
else (ast, [])  | 
| 
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
163  | 
end  | 
| 
 
42ec82147d83
changed macro expander such that patterns also match prefixes of appls;
 
wenzelm 
parents: 
922 
diff
changeset
 | 
164  | 
| _ => (ast, []));  | 
| 0 | 165  | 
in  | 
| 15531 | 166  | 
SOME (mtch head pat Symtab.empty, args) handle NO_MATCH => NONE  | 
| 0 | 167  | 
end;  | 
168  | 
||
169  | 
||
| 18 | 170  | 
(* normalize *)  | 
| 0 | 171  | 
|
172  | 
(*the normalizer works yoyo-like: top-down, bottom-up, top-down, ...*)  | 
|
173  | 
||
| 18 | 174  | 
fun normalize trace stat get_rules pre_ast =  | 
| 0 | 175  | 
let  | 
| 32738 | 176  | 
val passes = Unsynchronized.ref 0;  | 
177  | 
val failed_matches = Unsynchronized.ref 0;  | 
|
178  | 
val changes = Unsynchronized.ref 0;  | 
|
| 0 | 179  | 
|
| 18 | 180  | 
fun subst _ (ast as Constant _) = ast  | 
| 17412 | 181  | 
| subst env (Variable x) = the (Symtab.lookup env x)  | 
| 0 | 182  | 
| subst env (Appl asts) = Appl (map (subst env) asts);  | 
183  | 
||
| 11733 | 184  | 
fun try_rules ((lhs, rhs) :: pats) ast =  | 
| 0 | 185  | 
(case match ast lhs of  | 
| 15531 | 186  | 
SOME (env, args) =>  | 
| 32738 | 187  | 
(Unsynchronized.inc changes; SOME (mk_appl (subst env rhs) args))  | 
188  | 
| NONE => (Unsynchronized.inc failed_matches; try_rules pats ast))  | 
|
| 15531 | 189  | 
| try_rules [] _ = NONE;  | 
| 11733 | 190  | 
val try_headless_rules = try_rules (get_rules "");  | 
| 0 | 191  | 
|
| 11733 | 192  | 
fun try ast a =  | 
193  | 
(case try_rules (get_rules a) ast of  | 
|
| 15531 | 194  | 
NONE => try_headless_rules ast  | 
| 11733 | 195  | 
| some => some);  | 
| 0 | 196  | 
|
197  | 
fun rewrite (ast as Constant a) = try ast a  | 
|
198  | 
| rewrite (ast as Variable a) = try ast a  | 
|
199  | 
| rewrite (ast as Appl (Constant a :: _)) = try ast a  | 
|
200  | 
| rewrite (ast as Appl (Variable a :: _)) = try ast a  | 
|
| 11733 | 201  | 
| rewrite ast = try_headless_rules ast;  | 
| 0 | 202  | 
|
203  | 
fun rewrote old_ast new_ast =  | 
|
| 21962 | 204  | 
if trace then  | 
205  | 
        tracing ("rewrote: " ^ str_of_ast old_ast ^ "  ->  " ^ str_of_ast new_ast)
 | 
|
206  | 
else ();  | 
|
| 0 | 207  | 
|
208  | 
fun norm_root ast =  | 
|
209  | 
(case rewrite ast of  | 
|
| 15531 | 210  | 
SOME new_ast => (rewrote ast new_ast; norm_root new_ast)  | 
211  | 
| NONE => ast);  | 
|
| 0 | 212  | 
|
213  | 
fun norm ast =  | 
|
214  | 
(case norm_root ast of  | 
|
215  | 
Appl sub_asts =>  | 
|
216  | 
let  | 
|
217  | 
val old_changes = ! changes;  | 
|
218  | 
val new_ast = Appl (map norm sub_asts);  | 
|
219  | 
in  | 
|
220  | 
if old_changes = ! changes then new_ast else norm_root new_ast  | 
|
221  | 
end  | 
|
222  | 
| atomic_ast => atomic_ast);  | 
|
223  | 
||
224  | 
fun normal ast =  | 
|
225  | 
let  | 
|
226  | 
val old_changes = ! changes;  | 
|
227  | 
val new_ast = norm ast;  | 
|
228  | 
in  | 
|
| 32738 | 229  | 
Unsynchronized.inc passes;  | 
| 0 | 230  | 
if old_changes = ! changes then new_ast else normal new_ast  | 
231  | 
end;  | 
|
232  | 
||
233  | 
||
| 21962 | 234  | 
    val _ = if trace then tracing ("pre: " ^ str_of_ast pre_ast) else ();
 | 
| 
9372
 
7834e56e2277
AST translation rules no longer require constant head on LHS;
 
wenzelm 
parents: 
8997 
diff
changeset
 | 
235  | 
val post_ast = normal pre_ast;  | 
| 21962 | 236  | 
val _ =  | 
237  | 
if trace orelse stat then  | 
|
238  | 
        tracing ("post: " ^ str_of_ast post_ast ^ "\nnormalize: " ^
 | 
|
239  | 
string_of_int (! passes) ^ " passes, " ^  | 
|
240  | 
string_of_int (! changes) ^ " changes, " ^  | 
|
241  | 
string_of_int (! failed_matches) ^ " matches failed")  | 
|
242  | 
else ();  | 
|
243  | 
in post_ast end;  | 
|
| 0 | 244  | 
|
245  | 
||
| 18 | 246  | 
(* normalize_ast *)  | 
247  | 
||
| 32738 | 248  | 
val trace_ast = Unsynchronized.ref false;  | 
249  | 
val stat_ast = Unsynchronized.ref false;  | 
|
| 12262 | 250  | 
|
| 18 | 251  | 
fun normalize_ast get_rules ast =  | 
| 8997 | 252  | 
normalize (! trace_ast) (! stat_ast) get_rules ast;  | 
| 18 | 253  | 
|
| 1506 | 254  | 
end;  |