isac-java/src/java/isac/gui/mawen/syntax/ast.scala
author mmahringer <s1520454056@students.fh-hagenberg.at>
Wed, 02 Aug 2017 10:56:36 +0200
changeset 5192 2183a5d68d6f
parent 5190 d3fa6cda3137
parent 5191 bcc8361629d7
child 5193 dfdf1b9349bb
permissions -rw-r--r--
merged
     1 /*  THIS IS A PARTIAL TRANSLATION FROM Isabelle2016-1 ...
     2     Title:      Pure/Syntax/ast.ML
     3     Author:     Markus Wenzel, TU Muenchen
     4 
     5 Abstract syntax trees, translation rules, matching and normalization of asts.
     6 */
     7 package isac.gui.mawen.syntax
     8 
     9 import isac.gui.mawen.scalaterm.Util
    10 import isac.gui.mawen.syntax.isabelle.XLibrary
    11 
    12 import scala.collection.immutable.TreeMap
    13 
    14 object Ast {
    15   
    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
    20   
    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 =
    24   args match {
    25     case Nil => f
    26     case _ => Appl(f :: args)
    27   }
    28   def is_atom(a : Ast) = a match {
    29     case Constant(_) => true
    30     case Variable(_) => true
    31     case Appl(_) => false
    32   }
    33   
    34   def prefix_string_of(ast : Ast) : String = ast match {
    35     case Constant(str) =>  XSyntax.isa_ast(str)
    36     case Variable(str) =>  str
    37     case Appl(names) => {
    38       names.map { x => prefix_string_of(x) }.mkString(" ")
    39     }
    40   }
    41 
    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
    48   }
    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
    57   }
    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)
    60     if (prior > pri) {
    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"))}
    65     }else{
    66        XSyntax.isa_fix(str) match {
    67        case "infixl" => {
    68          if (prior == pri & l_r == "right") {
    69            XLibrary.enclose("(", ")", to_str(a1, pri, "left") + XSyntax.isa_math(str) + to_str(a2, pri, "right"))
    70          }else{
    71            to_str(a1, pri, "left") + XSyntax.isa_math(str) + to_str(a2, pri, "right")
    72          }
    73        }
    74        case "infixr" => {
    75          if (prior == pri & l_r == "left") {
    76            XLibrary.enclose("(", ")", to_str(a1, pri, "left") + XSyntax.isa_math(str) + to_str(a2, pri, "right"))
    77          }else{
    78            to_str(a1, pri, "left") + XSyntax.isa_math(str) + to_str(a2, pri, "right")
    79          }
    80        }
    81        case "prefix" => XSyntax.isa_math(str) + to_str(a1, pri, "none") + " " + to_str(a2, pri, "none")}
    82     } }
    83   
    84   /* transform an Ast into a String, which corresponds to Isabelle's 
    85    * pretty_printing within Isac's Knowledge, restricted for prototyping.
    86    */
    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)
    91     
    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")
    95       else
    96         to_str(Variable(str), 0, "none") + XLibrary.enclose(" (", ")", to_str(a, 0, "none"))
    97 
    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")))
   108       else
   109         uniop + XLibrary.enclose("(", ")", to_str(a, 0, "none"))
   110       }
   111 
   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)
   114 
   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)
   117       }))
   118   }    
   119   // infix: "left" or "right" associativity ---------vvvv
   120   def math_string_of(t: Ast): String = to_str(t, 0, "none")
   121   
   122   /*
   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))
   126    */
   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))
   130     case Appl(asts) =>
   131       "Appl" + XLibrary.enclose("(List(", "))", XLibrary.commas(asts.map ( x => raw_string_of(x) )))
   132   }
   133   
   134   /*
   135    * translation from Isabelle2016-1 ~~/src/Pure/Syntax/ast.ML
   136    * "match" is a Scala keyword
   137   
   138   (*  match = fn: ast -> ast -> (ast Symtab.table * ast list) option*)
   139   fun match ast pat =
   140     let
   141       exception NO_MATCH;
   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) *)
   157       val (head, args) =
   158         (case (ast, pat) of
   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)
   162               else (ast, [])
   163             end
   164         | _ => (ast, []));
   165     in
   166         (* (ast Symtab.table          * ast list) *)
   167       SOME (mtch head pat Symtab.empty, args    ) handle NO_MATCH => NONE
   168     end;
   169    */
   170   def matches (ast_pat: Tuple2[Ast, Ast]) : Option[(TreeMap[String, Ast], List[Ast])] = {
   171 
   172     case class NO_MATCH() extends Exception()
   173     
   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
   183     }
   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      
   190     }
   191     val (head, args) = ast_pat match {
   192       case ((Appl(asts), Appl(pats))) => {
   193         val a = asts.length
   194         val p = pats.length
   195         if (a > p) {(Ast.Appl(asts.take(p)), asts.drop(p))}
   196         else (ast_pat._1, List())
   197       }
   198       case ((_,_)) => (ast_pat._1, List())
   199     }
   200     try {
   201       Some (mtch ((head, ast_pat._2), new TreeMap()), args)
   202     } catch {
   203       case c: NO_MATCH => None
   204     }
   205   }
   206   
   207   /*
   208   translation from Isabelle2016-1 ~~/src/Pure/Syntax/ast.ML
   209    *
   210   (*      = fn: Proof.context -> (string -> (ast * ast) list) -> ast -> ast*)
   211   fun normalize ctxt get_rules pre_ast =
   212     let
   213       val trace = Config.get ctxt trace;
   214       val stats = Config.get ctxt stats;
   215   
   216       val passes = Unsynchronized.ref 0;
   217       val failed_matches = Unsynchronized.ref 0;
   218       val changes = Unsynchronized.ref 0;
   219       
   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);
   224         
   225       (*      = fn: (ast * ast) list -> ast -> ast option*)
   226       fun try_rules ((lhs, rhs) :: pats) ast =
   227             (case match ast lhs of
   228               SOME (env, args) =>
   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;
   232    
   233       (* = fn: ast -> ast option *)
   234       val try_headless_rules = try_rules (get_rules "");
   235   
   236       (* = fn: ast -> string -> ast option *)
   237       fun try ast a =
   238         (case try_rules (get_rules a) ast of
   239           NONE => try_headless_rules ast
   240         | some => some);
   241   
   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;
   248   
   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)))
   252         else ();
   253   
   254       (* = fn: ast -> ast *)
   255       fun norm_root ast =
   256         (case rewrite ast of
   257           SOME new_ast => (rewrote ast new_ast; norm_root new_ast)
   258         | NONE => ast);
   259   
   260       (* = fn: ast -> ast *)
   261       fun norm ast =
   262         (case norm_root ast of
   263           Appl sub_asts =>
   264             let
   265               val old_changes = ! changes;
   266               val new_ast = Appl (map norm sub_asts);
   267             in
   268               if old_changes = ! changes then new_ast else norm_root new_ast
   269             end
   270         | atomic_ast => atomic_ast);
   271   
   272       (* = fn: ast -> ast *)
   273       fun normal ast =
   274         let
   275           val old_changes = ! changes;
   276           val new_ast = norm ast;
   277         in
   278           Unsynchronized.inc passes;
   279           if old_changes = ! changes then new_ast else normal new_ast
   280         end;
   281         
   282       val _ = if trace then tracing (message "pre:" (pretty_ast pre_ast)) else ();
   283       val post_ast = normal pre_ast;
   284       val _ =
   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")
   290         else ();
   291     in post_ast end;
   292   * 
   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.
   295   */
   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()
   300   
   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
   303   
   304     var failed_matches = 0
   305     var changes = 0
   306   
   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)))
   311     }
   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))
   318           }
   319           case None => {
   320             failed_matches = failed_matches + 1
   321             try_rules(pats, ast)
   322           }
   323         }
   324       }
   325       case Nil => { None }
   326     }    
   327     def try_headless_rules(ast: Ast) = try_rules(get_rules(""), ast)
   328   
   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)
   333         case some => some
   334       }
   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)
   341     }
   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))
   346     }
   347     def norm_root(ast: Ast): Ast =
   348       rewrite(ast) match {
   349         case Some(new_ast) => {
   350           //rewrote (ast, new_ast)
   351           norm_root(new_ast)}
   352         case None => ast
   353     }
   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)
   360             new_ast
   361           else
   362             norm_root(new_ast)
   363         }
   364         case atomic_ast => atomic_ast
   365       }
   366     def normal(ast: Ast): Ast = {
   367       val old_changes = changes
   368       val new_ast = norm(ast)
   369       if (old_changes == changes)
   370         new_ast
   371       else
   372         normal(new_ast)
   373     }
   374     normal(pre_ast)
   375   } //def normalize
   376   
   377   //\\=================================================================//
   378   
   379   case class AST_NO_KEY(message: String = "") extends Exception(message)
   380   
   381   def upd_list (asts: List[Ast], i: Integer, ast: Ast) = asts.updated(i - 1, ast)
   382   
   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>
   386    */
   387   def add (key: List[Int], ad: Ast, ast: Ast): Ast = ast match {
   388     case Appl(asts) => {
   389       if (key.length == 1)
   390         Appl(upd_list(asts, key.head, ad))
   391       else {
   392         if (key.isEmpty)
   393           throw new AST_NO_KEY("Index out of range of list")
   394         else {
   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
   398   }
   399   // get a sub-term at key of an ast.
   400   def get_value (key: List[Int], ast: Ast): Ast = ast match {
   401     case Appl(asts) =>
   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))
   406     case const_var => 
   407       if (key.isEmpty) const_var 
   408       else throw new AST_NO_KEY("NOT existent key=" + key)
   409   }
   410 
   411 }
   412 
   413