1 /* THIS IS A PARTIAL TRANSLATION FROM Isabelle2016-1 ...
2 Title: Pure/Syntax/ast.ML
3 Author: Markus Wenzel, TU Muenchen
5 Abstract syntax trees, translation rules, matching and normalization of asts.
7 package isac.gui.mawen.syntax
9 import isac.gui.mawen.scalaterm.Util
10 import isac.gui.mawen.syntax.isabelle.XLibrary
12 import scala.collection.immutable.TreeMap
16 sealed abstract class Ast
17 case class Constant(name: String) extends Ast // operator
18 case class Variable(name: String) extends Ast
19 case class Appl(name: List[Ast]) extends Ast // invariant: List[Ast].length > 1
21 /*the list of subasts of an Appl node has to contain at least 2 elements, i.e.
22 there are no empty asts or nullary applications; use mk_appl for convenience*/
23 def mk_appl(f : Ast, args : List[Ast]) : Ast =
26 case _ => Appl(f :: args)
28 def is_atom(a : Ast) = a match {
29 case Constant(_) => true
30 case Variable(_) => true
34 def prefix_string_of(ast : Ast) : String = ast match {
35 case Constant(str) => XSyntax.isa_ast(str)
36 case Variable(str) => str
38 names.map { x => prefix_string_of(x) }.mkString(" ")
42 //the root "_list" has been dropped in to_str (how handle lists of lists ?)
43 def to_list(args: Ast, prior: Int, l_r: String): String = args match {
44 case Variable(v) => to_str(Variable(v), 0, "none")
45 case Appl(List(Constant("_args"), a, arg)) =>
46 to_str(a, 0, "none") + ", " + to_list(arg, 0, "none")
47 case ast => to_str(ast, 0, "none") //the last element
49 def to_tuple(ast: Ast, prior: Int, l_r: String): String = ast match {
50 case Appl(List(Constant("_tuple"), a1, a2)) =>
51 "(" + to_str(a1, 0, "none") + to_tuple(a2, 0, "none")
52 case Appl(List(Constant("_tuple_args"), a1, a2)) =>
53 ", " + to_str(a1, 0, "none") + to_tuple(a2, 0, "none")
54 case Appl(List(Constant("_tuple_arg"), arg)) =>
55 ", " + to_str(arg, 0, "none") + ")"
56 case ast => to_str(ast, 0, "none") //the last element
58 // set parentheses according to priority and to left/right associativity
59 def to_binop(str: String, a1: Ast, a2: Ast, prior: Int, l_r: String): String = { val pri = XSyntax.isa_prior(str)
61 XSyntax.isa_fix(str) match {
62 case "infixl" => XLibrary.enclose("(", ")", to_str(a1, pri, "left") + XSyntax.isa_math(str) + to_str(a2, pri, "right"))
63 case "infixr" => XLibrary.enclose("(", ")", to_str(a1, pri, "left") + XSyntax.isa_math(str) + to_str(a2, pri, "right"))
64 case "prefix" => XLibrary.enclose("(", ")", XSyntax.isa_math(str) + to_str(a1, pri, "left") + to_str(a2, pri, "right"))}
66 XSyntax.isa_fix(str) match {
68 if (prior == pri & l_r == "right") {
69 XLibrary.enclose("(", ")", to_str(a1, pri, "left") + XSyntax.isa_math(str) + to_str(a2, pri, "right"))
71 to_str(a1, pri, "left") + XSyntax.isa_math(str) + to_str(a2, pri, "right")
75 if (prior == pri & l_r == "left") {
76 XLibrary.enclose("(", ")", to_str(a1, pri, "left") + XSyntax.isa_math(str) + to_str(a2, pri, "right"))
78 to_str(a1, pri, "left") + XSyntax.isa_math(str) + to_str(a2, pri, "right")
81 case "prefix" => XSyntax.isa_math(str) + to_str(a1, pri, "none") + " " + to_str(a2, pri, "none")}
84 /* transform an Ast into a String, which corresponds to Isabelle's
85 * pretty_printing within Isac's Knowledge, restricted for prototyping.
87 def to_str (ast: Ast, prior: Int, l_r: String): String = ast match {
88 case Constant("GAP") => "#"
89 case Constant(str) => XSyntax.isa_math(str)
90 case Variable(str) => XLibrary.cut_long_ident(str)
92 case Appl(List(Variable(str), a)) => //ONLY univariate functions expected
93 if (Set("Probl" /*prelim.test*/, "Problem").contains(str))
94 XSyntax.isa_math(str) + to_tuple(a, 0, "none")
96 to_str(Variable(str), 0, "none") + XLibrary.enclose(" (", ")", to_str(a, 0, "none"))
98 case Appl(List(Constant("CURSOR"), a)) => to_str(a, prior, "none")
99 case Appl(List(a, Constant("CURSOR"))) => to_str(a, prior, "none")
100 case Appl(List(Constant("_list"), a)) => XLibrary.enclose("[", "]", to_list(a, prior, "none"))
101 case Appl(List(Constant("_tuple"), a1, a2)) => to_tuple(Appl(List(Constant("_tuple"), a1, a2)), prior, "none")
102 case Appl(List(Constant(str), a)) if str.startsWith("BOX") => to_str(a, prior, "none")
103 case Appl(List(Constant(str), a)) => {
104 val uniop = XSyntax.isa_math(str)
105 if (Set("Diff ", "Integrate ", "solve ").contains(uniop)) uniop + to_tuple(a, 0, "none")
106 else if (prior == 99 & XSyntax.isa_prior(str) == 99) //FIXME: handles "d_d x (! sin(_) !)" as special case
107 XLibrary.enclose("(", ")", uniop + XLibrary.enclose("(", ")", to_str(a, 0, "none")))
109 uniop + XLibrary.enclose("(", ")", to_str(a, 0, "none"))
112 case Appl(List(Constant(str), a)) if str.startsWith("BOX") => to_str(a, prior, "none")
113 case Appl(List(Constant(str), a1, a2)) => to_binop(str, a1, a2, prior, l_r)
115 case Appl(asts) => XLibrary.strs_to_str(asts.map(x => {
116 val str = Ast.to_str(x, 0, "none"); if (is_atom(x)) str else XLibrary.enclose("(", ")", str)
119 // infix: "left" or "right" associativity ---------vvvv
120 def math_string_of(t: Ast): String = to_str(t, 0, "none")
123 fun string_of (Ast.Constant str) = "Constant" ^ enclose "(" ")" (quote str)
124 | string_of (Ast.Variable str) = "Variable" ^ enclose "(" ")" (quote str)
125 | string_of (Ast.Appl asts) = "Appl " ^ enclose "[" "]" (commas (map string_of asts))
127 def raw_string_of (ast: Ast): String = ast match {
128 case Constant(str) => "Constant" + XLibrary.enclose("(", ")", XLibrary.quote(str))
129 case Variable(str) => "Variable" + XLibrary.enclose("(", ")", XLibrary.quote(str))
131 "Appl" + XLibrary.enclose("(List(", "))", XLibrary.commas(asts.map ( x => raw_string_of(x) )))
135 * translation from Isabelle2016-1 ~~/src/Pure/Syntax/ast.ML
136 * "match" is a Scala keyword
138 (* match = fn: ast -> ast -> (ast Symtab.table * ast list) option*)
142 (* mtch = fn: ast -> ast -> ast Symtab.table -> ast Symtab.table *)
143 fun mtch (Constant a) (Constant b) env =
144 if a = b then env else raise NO_MATCH
145 | mtch (Variable a) (Constant b) env =
146 if a = b then env else raise NO_MATCH
147 | mtch ast (Variable x) env = Symtab.update (x, ast) env
148 (*update: key * 'a -> 'a table -> 'a table*)
149 | mtch (Appl asts) (Appl pats) env = mtch_lst asts pats env
150 | mtch _ _ _ = raise NO_MATCH
151 (* mtch_lst = fn: ast list -> ast list -> ast Symtab.table -> ast Symtab.table *)
152 and mtch_lst (ast :: asts) (pat :: pats) env =
153 mtch_lst asts pats (mtch ast pat env)
154 | mtch_lst [] [] env = env
155 | mtch_lst _ _ _ = raise NO_MATCH;
156 (* ast * (ast list) *)
159 (Appl asts, Appl pats) =>
160 let val a = length asts and p = length pats in
161 if a > p then (Appl (take p asts), drop p asts)
166 (* (ast Symtab.table * ast list) *)
167 SOME (mtch head pat Symtab.empty, args ) handle NO_MATCH => NONE
170 def matches (ast_pat: Tuple2[Ast, Ast]) : Option[(TreeMap[String, Ast], List[Ast])] = {
172 case class NO_MATCH() extends Exception()
174 def mtch (ast_pat: Tuple2[Ast, Ast], env: TreeMap[String, Ast])
175 : TreeMap[String, Ast] = ast_pat match {
176 case ((Ast.Constant(a), Ast.Constant(b))) =>
177 if (a == b) env else throw new NO_MATCH
178 case ((Ast.Variable(a), Ast.Constant(b))) =>
179 if (a == b) env else throw new NO_MATCH
180 case ((ast, Ast.Variable(x))) => env + ((x, ast))
181 case ((Appl(asts), Appl(pats))) => mtch_lst ((asts, pats), env)
182 case (_) => throw new NO_MATCH
184 def mtch_lst (asts_pats: Tuple2[List[Ast], List[Ast]], env: TreeMap[String, Ast])
185 : TreeMap[String, Ast] = asts_pats match {
186 case ((ast :: asts, pat :: pats)) =>
187 mtch_lst ((asts, pats), mtch ((ast, pat), env))
188 case ((List(), List())) => env
189 case (_) => throw new NO_MATCH
191 val (head, args) = ast_pat match {
192 case ((Appl(asts), Appl(pats))) => {
195 if (a > p) {(Ast.Appl(asts.take(p)), asts.drop(p))}
196 else (ast_pat._1, List())
198 case ((_,_)) => (ast_pat._1, List())
201 Some (mtch ((head, ast_pat._2), new TreeMap()), args)
203 case c: NO_MATCH => None
208 translation from Isabelle2016-1 ~~/src/Pure/Syntax/ast.ML
210 (* = fn: Proof.context -> (string -> (ast * ast) list) -> ast -> ast*)
211 fun normalize ctxt get_rules pre_ast =
213 val trace = Config.get ctxt trace;
214 val stats = Config.get ctxt stats;
216 val passes = Unsynchronized.ref 0;
217 val failed_matches = Unsynchronized.ref 0;
218 val changes = Unsynchronized.ref 0;
220 (* = fn: ast Symtab.table -> ast -> ast*)
221 fun subst _ (ast as Constant _) = ast
222 | subst env (Variable x) = the (Symtab.lookup env x)
223 | subst env (Appl asts) = Appl (map (subst env) asts);
225 (* = fn: (ast * ast) list -> ast -> ast option*)
226 fun try_rules ((lhs, rhs) :: pats) ast =
227 (case match ast lhs of
229 (Unsynchronized.inc changes; SOME (mk_appl (subst env rhs) args))
230 | NONE => (Unsynchronized.inc failed_matches; try_rules pats ast))
231 | try_rules [] _ = NONE;
233 (* = fn: ast -> ast option *)
234 val try_headless_rules = try_rules (get_rules "");
236 (* = fn: ast -> string -> ast option *)
238 (case try_rules (get_rules a) ast of
239 NONE => try_headless_rules ast
242 (* = fn: ast -> ast option *)
243 fun rewrite (ast as Constant a) = try ast a
244 | rewrite (ast as Variable a) = try ast a
245 | rewrite (ast as Appl (Constant a :: _)) = try ast a
246 | rewrite (ast as Appl (Variable a :: _)) = try ast a
247 | rewrite ast = try_headless_rules ast;
249 (* = fn: ast -> ast -> unit *)
250 fun rewrote old_ast new_ast =
251 if trace then tracing (message "rewrote:" (pretty_rule (old_ast, new_ast)))
254 (* = fn: ast -> ast *)
257 SOME new_ast => (rewrote ast new_ast; norm_root new_ast)
260 (* = fn: ast -> ast *)
262 (case norm_root ast of
265 val old_changes = ! changes;
266 val new_ast = Appl (map norm sub_asts);
268 if old_changes = ! changes then new_ast else norm_root new_ast
270 | atomic_ast => atomic_ast);
272 (* = fn: ast -> ast *)
275 val old_changes = ! changes;
276 val new_ast = norm ast;
278 Unsynchronized.inc passes;
279 if old_changes = ! changes then new_ast else normal new_ast
282 val _ = if trace then tracing (message "pre:" (pretty_ast pre_ast)) else ();
283 val post_ast = normal pre_ast;
285 if trace orelse stats then
286 tracing (message "post:" (pretty_ast post_ast) ^ "\nnormalize: " ^
287 string_of_int (! passes) ^ " passes, " ^
288 string_of_int (! changes) ^ " changes, " ^
289 string_of_int (! failed_matches) ^ " matches failed")
293 * The translation to Scala drops the (logical) context and simplifies Symtab.table.
294 * In case Scala code is copied, the respective source is indicated.
296 //~~/src/Pure/PIDE/document.scala
297 // private def fail[A]: A = throw new State.Fail(this)
298 // def the_version(id: Document_ID.Version): Version = versions.getOrElse(id, fail)
299 private def fail[A]: A = throw new Exception()
301 def normalize(get_rules: String => List[Tuple2[Ast, Ast]], pre_ast: Ast) = {
302 //def get_rules(key: String) = XSyntax.print_rules(key) //used BEFORE made local to normalize
304 var failed_matches = 0
307 def subst(env: TreeMap[String, Ast]) (ast: Ast) : Ast = ast match {
308 case Constant(x) => Constant(x)
309 case Variable(x) => env.getOrElse(x, fail)
310 case Appl(asts) => Appl(asts.map(x => subst(env)(x)))
312 def try_rules(rules: List[Tuple2[Ast, Ast]], ast: Ast): Option[Ast] = rules match {
313 case ((lhs, rhs) :: pats) => {
314 matches(ast, lhs) match { //matches <- match ..Scala keyword
315 case Some((env, args)) => {
316 changes = changes + 1
317 Some(mk_appl(subst(env)(rhs), args))
320 failed_matches = failed_matches + 1
327 def try_headless_rules(ast: Ast) = try_rules(get_rules(""), ast)
329 // try is a Scala keyword
330 def xtry(ast: Ast, a: String): Option[Ast] =
331 try_rules (get_rules(a), ast) match {
332 case None => try_headless_rules(ast)
335 def rewrite(ast: Ast) = ast match {
336 case Constant(a) => xtry(ast, a)
337 case Variable(a) => xtry(ast, a)
338 case Appl (Constant(a) :: _) => xtry(ast, a)
339 case Appl (Variable(a) :: _) => xtry(ast, a)
340 case a => try_headless_rules(a)
342 def rewrote (old_ast: Ast, new_ast: Ast) = {
343 println("###scala rewrote:")
344 println("###scala old_ast: " + Ast.raw_string_of(old_ast))
345 println("###scala new_ast: " + Ast.raw_string_of(new_ast))
347 def norm_root(ast: Ast): Ast =
349 case Some(new_ast) => {
350 //rewrote (ast, new_ast)
354 def norm(ast: Ast): Ast =
355 norm_root(ast) match {
356 case Appl(sub_asts) => {
357 val old_changes = changes
358 val new_ast = Appl(sub_asts.map(x => norm(x)))
359 if (old_changes == changes)
364 case atomic_ast => atomic_ast
366 def normal(ast: Ast): Ast = {
367 val old_changes = changes
368 val new_ast = norm(ast)
369 if (old_changes == changes)
377 //\\=================================================================//
379 case class AST_NO_KEY(message: String = "") extends Exception(message)
381 def upd_list (asts: List[Ast], i: Integer, ast: Ast) = asts.updated(i - 1, ast)
383 /* add ast to ast <code>upd</code> at a position given by <code>key</code>.
384 * This is the only function modifying an immutable ast,
385 * because "remove" means: add Constant("GAP") at <code>key</code>
387 def add (key: List[Int], ad: Ast, ast: Ast): Ast = ast match {
390 Appl(upd_list(asts, key.head, ad))
393 throw new AST_NO_KEY("Index out of range of list")
395 val uu = add(key.tail, ad, asts(key.head -1))
396 Appl(upd_list(asts, key.head, uu)) }}}
397 case const_var => const_var
399 // get a sub-term at key of an ast.
400 def get_value (key: List[Int], ast: Ast): Ast = ast match {
402 if (key.isEmpty) Appl(asts)
403 else if (asts.length < key.head - 1)
404 throw new AST_NO_KEY("NOT existent key=" + key)
405 else get_value (key.tail, asts(key.head - 1))
407 if (key.isEmpty) const_var
408 else throw new AST_NO_KEY("NOT existent key=" + key)