All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gapt.formats.llk.LLKImporter.scala Maven / Gradle / Ivy

The newest version!
package gapt.formats.llk

import gapt.proofs.HOLSequent
import gapt.proofs.RichFormulaSequent
import gapt.formats.llk.ast.LambdaAST
import gapt.expr.formula.hol._
import gapt.expr._
import gapt.expr.BetaReduction._
import gapt.proofs.lk._

import scala.annotation.tailrec
import EquationVerifier._
import gapt.expr.formula.All
import gapt.expr.formula.And
import gapt.expr.formula.Atom
import gapt.expr.formula.Eq
import gapt.expr.formula.Ex
import gapt.expr.formula.Formula
import gapt.expr.formula.Imp
import gapt.expr.formula.Neg
import gapt.expr.formula.Or
import gapt.expr.formula.fol.FOLAtom
import gapt.expr.formula.hol.HOLFunction
import gapt.expr.subst.Substitution
import gapt.expr.ty.To
import gapt.expr.ty.Ty
import gapt.expr.util.freeVariables
import gapt.expr.util.syntacticMatching
import gapt.proofs.context.update.Definition
import gapt.proofs.lk.rules.AndLeftRule
import gapt.proofs.lk.rules.AndRightRule
import gapt.proofs.lk.rules.CutRule
import gapt.proofs.lk.rules.ConversionLeftRule
import gapt.proofs.lk.rules.ConversionRightRule
import gapt.proofs.lk.rules.EqualityLeftRule
import gapt.proofs.lk.rules.EqualityRightRule
import gapt.proofs.lk.rules.ExistsLeftRule
import gapt.proofs.lk.rules.ExistsRightRule
import gapt.proofs.lk.rules.ForallLeftRule
import gapt.proofs.lk.rules.ForallRightRule
import gapt.proofs.lk.rules.ImpLeftRule
import gapt.proofs.lk.rules.ImpRightRule
import gapt.proofs.lk.rules.LogicalAxiom
import gapt.proofs.lk.rules.NegLeftRule
import gapt.proofs.lk.rules.NegRightRule
import gapt.proofs.lk.rules.OrLeftRule
import gapt.proofs.lk.rules.OrRightRule
import gapt.proofs.lk.rules.ProofLink
import gapt.proofs.lk.rules.WeakeningLeftRule
import gapt.proofs.lk.rules.WeakeningRightRule
import gapt.proofs.lk.rules.macros.ContractionMacroRule
import gapt.proofs.lk.rules.macros.WeakeningMacroRule
import gapt.proofs.lk.util.AtomicExpansion
import gapt.proofs.lk.util.solvePropositional
import gapt.utils.Logger

private object LLKLogger extends Logger("llk"); import LLKLogger._

object LLKFormatter {
  /* formats a sequent */
  def f(fs: HOLSequent): String = {
    " " + fs.antecedent.map(toLLKString.apply).mkString(", ") + " :- " +
      fs.succedent.map(toLLKString.apply).mkString(", ") + " "
  }

  def f(s: Substitution): String = {
    s.map.map(x => f(x._1) + " -> " + f(x._2)).mkString("{", ",", "}")
  }

  def f(e: Expr): String = " " + toLLKString(e) + " "
}

/**
 * This implements the second parsing pass, converting hlk Tokens to an LK Proof. HybridLatexParser inherits from
 * TokenToLKConverter to have a common interface, but the code here is only dependent on the AST. It uses the ASTtoHOL
 * object to create Exprs from hol ASTs.
 */
trait TokenToLKConverter {
  import LLKFormatter._

  /* Extracts type declarations from the tokens and creates a function to create atomic terms by name */
  def createNaming(r: List[Token]): (String => Expr) = {
    val ctypes: List[(String, Ty)] = r.flatMap(_ match {
      case TToken("CONST", names, t) => names map ((_, t))
      case _                         => Nil
    })
    val constmap = Map[String, Ty]() ++ ctypes

    val vtypes: List[(String, Ty)] = r.flatMap(_ match {
      case TToken("VAR", names, t) => names map ((_, t))
      case _                       => Nil
    })
    val varmap = Map[String, Ty]() ++ vtypes

    { (s: String) =>
      {
        if (varmap contains s) {
          Var(s, varmap(s))
        } else if (constmap contains s) {
          Const(s, constmap(s))
        } else
          throw new HybridLatexParserException("no type declaration for symbol " + s)
      }
    }
  }

  def printRules(r: List[Token]): List[HOLSequent] = {
    val rules = r.filter(_ match { case RToken(_, _, _, _, _) => true; case _ => false })
    val naming = createNaming(r)

    var l = List[HOLSequent]()

    for (case RToken(name, argname, antecedent, succedent, _) <- rules) {
      val ant = antecedent.map(x => c(LLKFormulaParser.ASTtoHOL(naming, x)))
      val suc = succedent.map(x => c(LLKFormulaParser.ASTtoHOL(naming, x)))
      val fs = HOLSequent(ant, suc)
      // println(name + ": "+fs)
      l = fs :: l
    }

    l.reverse
  }

  /* The main entry point to the proof parser. The list of tokens l is taken apart into subproofs (noticable by the
     CONTINUEWITH rule). Then the subproofs are ordered by dependency and constructed in this order*/
  def createLKProof(l: List[Token]): ExtendedProofDatabase = {
    // seperate rule tokens from type declaration tokens
    val (rtokens, tatokens) = l.partition {
      case RToken(_, _, _, _, _) => true;
      case _                     => false;
    }.asInstanceOf[(List[RToken], List[Token])] // need to cast because partition returns Tokens
    val (ttokens, atokens) = tatokens.partition {
      case TToken(_, _, _)        => true;
      case t @ AToken(_, _, _, _) => false;
      case t: Token => throw new Exception(
          "Severe error: rule tokens were already filtered out, but rule " + t + " still contained!"
        )
    }.asInstanceOf[(List[TToken], List[AToken])] // need to cast because partition returns Tokens
    // println("creating naming!")
    val naming = createNaming(ttokens)
    // println("creating axioms!")
    val unclosedaxioms = createAxioms(naming, atokens)
    // println("closing axioms!")
    val axioms = unclosedaxioms map (x => (x._1, universalClosure(x._2)))
    // println(axioms)
    // println("creating definitions!")
    val llk_definitions = createDefinitions(naming, atokens, axioms)

    // println(definitions)
    // seperate inferences for the different (sub)proofs
    val (last, rm) = rtokens.foldLeft(List[RToken](), Map[Formula, List[RToken]]())((current, token) => {
      token match {
        case RToken("CONTINUEWITH", Some(name), a, s, _) =>
          // put proof under name into map, continue with empty rulelist
          try {
            val nformula = c(LLKFormulaParser.ASTtoHOL(naming, name))
            (Nil, current._2 + ((nformula, current._1.reverse)))
          } catch {
            case e: Exception => throw new HybridLatexParserException(
                "Error in parsing CONTINUEWITH{" + name + "}{" + a + "}{" + s"}: " + e.getMessage,
                e
              )
          }
        case RToken("CONTINUEWITH", _, _, _, _) =>
          throw new HybridLatexParserException("The CONTINUEWITH statement needs a name giving the argument!")
        case RToken("COMMENT", _, _, _, _) =>
          // filter comments
          current
        case _ =>
          // apend rule to current list
          (token :: current._1, current._2)
      }
    })

    require(
      last.isEmpty,
      "There are some rules left after parsing the list, use CONTINUEFROM to give proof a name! " + last
    )

    // dependncy analysis
    val dependecies = getDependecyMap(naming, rm)
    val ordering: List[Formula] = getOrdering(dependecies)

    // proof completion in dependency order
    val proofs = ordering.foldLeft(Map[Formula, LKProof]())((proofs_done, f) => {
      // println("Processing (sub)proof "+this.f(f))
      val f_proof: LKProof = completeProof(f, proofs_done, naming, rm(f), axioms, llk_definitions)
      proofs_done + ((f, f_proof))
    })
    ExtendedProofDatabase(
      proofs,
      axioms,
      llk_definitions.map(d =>
        llkDefinitionToLKDefinition(d._1, d._2).toTuple
      )
    )
  }

  /* Creates the subproof proofname from a list of rules. Uses the naming function to create basic term and
   *  uses the proofs map to look up subproofs referenced by CONTINUEWITH and INSTLEMMA. This means, the calls
   *  to completeProof have to be ordered s.t. dependent proofs are already contained in the map.*/
  def completeProof(
      proofname: Formula,
      proofs: Map[Formula, LKProof],
      naming: String => Expr,
      rules: List[RToken],
      axioms: Map[Formula, Formula],
      definitions: Map[Expr, Expr]
  ): LKProof = {
    var proofstack: List[LKProof] = List[LKProof]()
    for (rt @ RToken(name, auxterm, antecedent, succedent, sub) <- rules) {
      // println("Processing rule: "+name)
      val ant = antecedent.map(x => c(LLKFormulaParser.ASTtoHOL(naming, x)))
      val suc = succedent.map(x => c(LLKFormulaParser.ASTtoHOL(naming, x)))
      val fs = HOLSequent(ant, suc)
      val nLine = sys.props("line.separator")
      name match {
        case "AX" =>
          val expaxiom = (ant, suc) match {
            case _ if ant == suc => // this is a workaround for the more restricted lk introduction rules
              AtomicExpansion(ant.head) // TODO: remove atomic expansion when it's not necessary anymore
            case _ =>
              ProofLink(FOLAtom("llkAxiom"), HOLSequent(ant, suc))
          }
          proofstack = expaxiom :: proofstack
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "TAUTCOMPLETION" =>
          require(
            ant.size == 1,
            "Tautological Axiom Completion needs exactly one formula in the antecedent, not " + ant.mkString(",")
          )
          require(
            suc.size == 1,
            "Tautological Axiom Completion needs exactly one formula in the succedent, not "
              + suc.mkString(",")
          )
          require(
            ant.head == suc.head,
            "Tautological Axiom Completion can only expand sequents of the form F :- F, not " +
              HOLSequent(ant, suc)
          )
          val rule = AtomicExpansion(ant.head)
          proofstack = rule :: proofstack
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "AUTOPROP" =>
          try {
            val Right(rule) = solvePropositional(HOLSequent(ant, suc)): @unchecked
            proofstack = rule :: proofstack
            require(
              proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
              "Error creating rule! Expected sequent: " + f(fs) +
                " got " + f(proofstack.head.endSequent) + " instead!"
            )
          } catch {
            case e: Exception =>
              throw new HybridLatexParserException(
                "Autopropositional failed with the message: " + e.getMessage + nLine + "Stack Trace:" + nLine +
                  e.getStackTrace.mkString("", nLine, nLine),
                e
              )
          }
        // --- quantifier rules ---
        case "ALLL" =>
          proofstack = handleWeakQuantifier(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "EXR" =>
          proofstack = handleWeakQuantifier(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "ALLR" =>
          proofstack = handleStrongQuantifier(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "EXL" =>
          proofstack = handleStrongQuantifier(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "ANDR" =>
          proofstack = handleBinaryLogicalOperator(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "ORL" =>
          proofstack = handleBinaryLogicalOperator(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "IMPL" =>
          proofstack = handleBinaryLogicalOperator(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        // --- unary rules ---
        case "ORR" =>
          proofstack = handleUnaryLogicalOperator(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "ANDL" =>
          proofstack = handleUnaryLogicalOperator(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "IMPR" =>
          proofstack = handleUnaryLogicalOperator(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        // --- negation rules ---
        case "NEGL" =>
          proofstack = handleNegation(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "NEGR" =>
          proofstack = handleNegation(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )

        // --- equational rules ---
        case "EQL" =>
          proofstack = handleEquality(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "EQR" =>
          proofstack = handleEquality(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )

        // --- definition rules ---
        case "DEF" =>
          proofstack = handleDefinitions(proofstack, name, fs, auxterm, naming, rt, definitions)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )

        // --- structural rules ---
        case "CONTRL" =>
          proofstack = handleContraction(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "CONTRR" =>
          proofstack = handleContraction(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "WEAKL" =>
          proofstack = handleWeakening(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "WEAKR" =>
          proofstack = handleWeakening(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "CUT" =>
          proofstack = handleCut(proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )

        // --- macro rules ---
        case "EQAXIOM" =>
          proofstack = handleEQAxiom(proofstack, name, fs, auxterm, sub, naming, rt, axioms, definitions)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )

        case "INSTAXIOM" =>
          proofstack = handleInstAxiom(proofstack, name, fs, auxterm, sub, naming, rt, axioms, definitions)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )

        case "CONTINUEFROM" =>
          proofstack = handleLink(proofs, proofstack, name, fs, auxterm, naming, rt)
          require(
            proofstack.nonEmpty && proofstack.head.endSequent.multiSetEquals(fs),
            "Error creating rule! Expected sequent: " + f(fs) +
              " got " + f(proofstack.head.endSequent) + " instead!"
          )
        case "CONTINUEWITH" => ;
        case "COMMENT"      => ;
        case _              => throw new HybridLatexParserException("Rule type " + name + " not yet implemented!")
      }

    }

    require(proofstack.nonEmpty, "Error creating proof: Proof stack may not be empty!")
    proofstack.head
  }

  /* Takes care of weak quantifiers. */
  def handleWeakQuantifier(current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], naming: (String) => Expr, rt: RToken): List[LKProof] = {
    require(current_proof.nonEmpty, "Imbalanced proof tree in application of " + ruletype + " with es: " + fs)
    val oldproof :: rest = current_proof: @unchecked

    val (mainsequent, auxsequent, _) = filterContext(oldproof.endSequent, fs)
    require(
      auxsequent.formulas.size == 1,
      "Exactly one auxiliary formula in weak quantifier rule required (no autocontraction allowed)! " + auxsequent
    )
    val (main, aux) = ruletype match {
      // TODO: if you use ALLL instead of ALLR, you might get an index out of bounds exception!
      case "ALLL" => (mainsequent.antecedent.head, auxsequent.antecedent.head)
      case "EXR"  => (mainsequent.succedent.head, auxsequent.succedent.head)
    }

    def inferTerm(x: Var, holf: Formula): Expr = {
      syntacticMatching(holf, aux) match {
        case Some(sub) =>
          val s: Expr = sub.map.getOrElse(x, x)
          // in case the variable was projected away, we use the identity function
          if (auxterm.nonEmpty) {
            // try to use user provided term
            val t: Expr = LLKFormulaParser.ASTtoHOL(naming, auxterm.get)
            if (s == t) {
              // println("Remark: automatically inferred the auxiliaray term " + f(t) + " in formula "+f(f))
              t
            } else {
              debug("Preferring user specified term " + f(t) + " over inferred term " + f(s) + ".")
              t
            }
          } else {
            // no user provided term
            s
          }

        case None =>
          //          println("Remark: Could not infer substitution term, using user specified one!")
          LLKFormulaParser.ASTtoHOL(
            naming,
            auxterm.getOrElse(
              throw new HybridLatexParserException("No substitution term found, please specify! " + rt)
            )
          )
      }
    }

    main match {
      case All(x, f) =>
        require(ruletype == "ALLL", "Main formula " + main + " can not be used in a forall left rule!")
        val term = inferTerm(x, f)
        val rule = ForallLeftRule(oldproof, main, term)
        rule :: rest

      case Ex(x, f) =>
        inferTerm(x, f)
        require(ruletype == "EXR", "Main formula " + main + " can not be used in a exists right rule!")
        val term = inferTerm(x, f)
        val rule = ExistsRightRule(oldproof, main, term)
        rule :: rest
    }
  }

  def handleStrongQuantifier(current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], naming: (String) => Expr, rt: RToken): List[LKProof] = {
    require(current_proof.nonEmpty, "Imbalanced proof tree in application of " + ruletype + " with es: " + fs)
    val oldproof = current_proof.head
    val (mainsequent, auxsequent, _) = filterContext(oldproof.endSequent, fs)
    require(
      auxsequent.formulas.size == 1,
      "Exactly one auxiliary formula in strong quantifier rule required! " + auxsequent
    )
    val (main, aux) = ruletype match {
      case "EXL"  => (mainsequent.antecedent.head, auxsequent.antecedent.head)
      case "ALLR" => (mainsequent.succedent.head, auxsequent.succedent.head)
    }

    def inferTerm(x: Var, f: Formula): Expr = {
      syntacticMatching(f, aux) match {
        case Some(sub) =>
          val s: Expr = sub.map.getOrElse(x, x) // in case the term was projected away we try the identity function
          if (auxterm.nonEmpty) {
            // try to use user provided term
            val t: Expr = LLKFormulaParser.ASTtoHOL(naming, auxterm.get)
            if (s == t) {
              //              println("Remark: automatically inferred the auxiliaray term in rule " + rt + ".")
              require(
                t.isInstanceOf[Var],
                "Strong quantifier rule needs an eigenvariable as argument, but " + t + " is not!"
              )
              t
            } else {
              debug("Preferring user specified term " + t + " over inferred term " + s + ".")
              require(
                t.isInstanceOf[Var],
                "Strong quantifier rule needs an eigenvariable as argument, but " + t + " is not!"
              )
              t
            }
          } else {
            // no user provided term
            // require(s.isInstanceOf[Var],
            //  "Strong quantifier rule needs an eigenvariable as argument, but "+s+" is not!")
            s
          }

        case None =>
          // automatic mode failed
          debug("Remark: Could not infer substitution term, using user specified one!")
          val t = LLKFormulaParser.ASTtoHOL(
            naming,
            auxterm.getOrElse(
              throw new HybridLatexParserException("No substitution term found, please specify! " + rt)
            )
          )
          require(
            t.isInstanceOf[Var],
            "Strong quantifier rule needs an eigenvariable as argument, but " + t + " is not!"
          )
          t
      }
    }

    main match {
      case All(x, f) =>
        val v = inferTerm(x, f)
        require(ruletype == "ALLR", "Main formula " + main + " can not be used in a forall right rule!")
        val rule = ForallRightRule(oldproof, main, v.asInstanceOf[Var])
        rule :: current_proof.tail

      case Ex(x, f) =>
        val v = inferTerm(x, f)
        require(ruletype == "EXL", "Main formula " + main + " can not be used in a exists left rule!")
        val rule = ExistsLeftRule(oldproof, main, v.asInstanceOf[Var])
        rule :: current_proof.tail
    }
  }

  def handleBinaryLogicalOperator(current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], naming: (String) => Expr, rt: RToken): List[LKProof] = {
    require(current_proof.size > 1, "Imbalanced proof tree in application of " + ruletype + " with es: " + fs)
    val rightproof :: leftproof :: stack = current_proof: @unchecked

    val (mainsequent, auxsequent, context) = filterContext(leftproof.endSequent, rightproof.endSequent, fs)

    try {
      ruletype match {
        case "ANDR" =>
          mainsequent.succedent.head match {
            case And(l, r) =>
              require(
                auxsequent.succedent.contains(l),
                "Left branch formula " + l + " not found in auxiliary formulas" + f(auxsequent)
              )
              require(
                auxsequent.succedent.contains(r),
                "Right branch formula " + r + " not found in auxiliary formulas!" + f(auxsequent)
              )
              require(
                leftproof.endSequent.succedent.contains(l),
                "Left branch formula " + l + " not found in auxiliary formulas " + leftproof.endSequent
              )
              require(
                rightproof.endSequent.succedent.contains(r),
                "Right branch formula " + r + " not found in auxiliary formulas!" + rightproof.endSequent
              )
              val inf = AndRightRule(leftproof, l, rightproof, r)
              val contr = ContractionMacroRule(inf, fs)
              contr :: stack
            case _ => throw new HybridLatexParserException(
                "Main formula of a conjunction right rule must have conjuntion as outermost operator!"
              )
          }

        case "ORL" =>
          mainsequent.antecedent.head match {
            case Or(l, r) =>
              require(
                auxsequent.antecedent.contains(l),
                "Left branch formula " + l + " not found in auxiliary formulas " + f(auxsequent)
              )
              require(
                auxsequent.antecedent.contains(r),
                "Right branch formula " + r + " not found in auxiliary formulas!" + f(auxsequent)
              )
              require(
                leftproof.endSequent.antecedent.contains(l),
                "Left branch formula " + l + " not found in auxiliary formulas " + leftproof.endSequent
              )
              require(
                rightproof.endSequent.antecedent.contains(r),
                "Right branch formula " + r + " not found in auxiliary formulas!" + rightproof.endSequent
              )
              val inf = OrLeftRule(leftproof, l, rightproof, r)
              val contr = ContractionMacroRule(inf, fs)
              contr :: stack
            case _ => throw new HybridLatexParserException(
                "Main formula of a disjunction left rule must have disjunction as outermost operator!"
              )
          }

        case "IMPL" =>
          mainsequent.antecedent.head match {
            case Imp(l, r) =>
              require(
                auxsequent.succedent.contains(l),
                "Left branch formula " + l + " not found in auxiliary formulas " + f(auxsequent)
              )
              require(
                auxsequent.antecedent.contains(r),
                "Right branch formula " + r + " not found in auxiliary formulas!" + f(auxsequent)
              )
              require(
                leftproof.endSequent.succedent.contains(l),
                "Left branch formula " + l + " not found in auxiliary formulas " + leftproof.endSequent
              )
              require(
                rightproof.endSequent.antecedent.contains(r),
                "Right branch formula " + r + " not found in auxiliary formulas!" + rightproof.endSequent
              )
              val inf = ImpLeftRule(leftproof, l, rightproof, r)
              val contr = ContractionMacroRule(inf, fs)
              contr :: stack
            case _ => throw new HybridLatexParserException(
                "Main formula of a implication left rule must have implication as outermost operator!"
              )
          }
      }
    } catch {
      case e: Exception => throw new HybridLatexParserException(
          "Error in handling binary rule with end-sequent: " + f(fs) + sys.props("line.separator") +
            "Problem: " + e.getMessage,
          e
        )
    }
  }

  def handleUnaryLogicalOperator(current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], naming: (String) => Expr, rt: RToken): List[LKProof] = {
    require(current_proof.nonEmpty, "Imbalanced proof tree in application of " + ruletype + " with es: " + fs)
    val top :: stack = current_proof: @unchecked

    val (mainsequent, auxsequent, context) = filterContext(top.endSequent, fs)

    ruletype match {
      case "ORR" =>
        mainsequent.succedent.head match {
          case or @ Or(l, r) =>
            val inf = (auxsequent.succedent.contains(l), auxsequent.succedent.contains(r)) match {
              case (false, false) =>
                throw new Exception("Neither " + l + " nor " + r + " found in auxiliary formulas" + f(auxsequent))
              case (false, true) =>
                OrRightRule(WeakeningRightRule(top, l), or)
              case (true, false) =>
                OrRightRule(WeakeningRightRule(top, r), or)
              case (true, true) =>
                OrRightRule(top, or)
            }

            val contr = ContractionMacroRule(inf, fs, strict = true)
            contr :: stack
          case _ => throw new HybridLatexParserException(
              "Main formula of a disjunction right rule must have conjunction as outermost operator!"
            )
        }

      case "ANDL" =>
        mainsequent.antecedent.head match {
          case and @ And(l, r) =>
            val inf = (auxsequent.antecedent.contains(l), auxsequent.antecedent.contains(r)) match {
              case (false, false) =>
                throw new Exception("Neither " + l + " nor " + r + " found in auxiliary formulas" + f(auxsequent))
              case (false, true) =>
                AndLeftRule(WeakeningLeftRule(top, l), and)
              case (true, false) =>
                AndLeftRule(WeakeningLeftRule(top, r), and)
              case (true, true) =>
                AndLeftRule(top, and)
            }

            val contr = ContractionMacroRule(inf, fs, strict = true)
            contr :: stack
          case _ => throw new HybridLatexParserException(
              "Main formula of a conjunction left rule must have disjunction as outermost operator!"
            )
        }

      case "IMPR" =>
        mainsequent.succedent.head match {
          case Imp(l, r) =>
            require(
              auxsequent.antecedent.contains(l),
              "Left branch formula " + l + " not found in auxiliary formulas " + f(auxsequent)
            )
            require(
              auxsequent.succedent.contains(r),
              "Right branch formula " + r + " not found in auxiliary formulas!" + f(auxsequent)
            )
            require(
              top.endSequent.antecedent.contains(l),
              "Left branch formula " + l + " not found in auxiliary formulas " + top.endSequent
            )
            require(
              top.endSequent.succedent.contains(r),
              "Right branch formula " + r + " not found in auxiliary formulas!" + top.endSequent
            )
            val inf = ImpRightRule(top, l, r)
            val contr = ContractionMacroRule(inf, fs)
            contr :: stack
          case _ => throw new HybridLatexParserException(
              "Main formula of a implication right rule must have implication as outermost operator!"
            )
        }
    }
  }

  def handleNegation(current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], naming: (String) => Expr, rt: RToken): List[LKProof] = {
    require(current_proof.nonEmpty, "Imbalanced proof tree in application of " + ruletype + " with es: " + fs)
    val top :: stack = current_proof: @unchecked
    val (main, aux, context) = filterContext(top.endSequent, fs)

    val left = main.antecedent.foldLeft(top)((intermediate, f) => {
      f match {
        case Neg(g) =>
          NegLeftRule(intermediate, g)
        case _ =>
          throw new HybridLatexParserException(
            "Trying to apply the negation rule on formula " + f +
              " without negation as outermost symbol on " + top.endSequent + " to get " + fs
          )
      }
    })
    val right = main.succedent.foldLeft(left)((intermediate, f) => {
      f match {
        case Neg(g) =>
          NegRightRule(intermediate, g)
        case _ =>
          throw new HybridLatexParserException(
            "Trying to apply the negation rule on formula " + f +
              " without negation as outermost symbol on " + top.endSequent + " to get " + fs
          )
      }
    })

    val contr = ContractionMacroRule(right, fs, strict = false)

    require(
      contr.endSequent multiSetEquals fs,
      "Could not create target sequent " + fs + " by a series of negations from " + top.endSequent +
        " but got " + contr.endSequent + " instead!"
    )
    contr :: stack
  }

  // TODO: refactor, a lot of code is already done in the rule constructors now
  def handleEquality(current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], naming: (String) => Expr, rt: RToken): List[LKProof] = {
    require(current_proof.size > 1, "Imbalanced proof tree in application of " + ruletype + " with es: " + fs)
    val rightproof :: leftproof :: stack = current_proof: @unchecked

    // In the case the main formula is the same as an auxiliariy formula, filterContext cannot infer the main formula
    // we doe this now by hand
    def eqfilter(x: Formula): Boolean = x match { case Eq(s, t) => true; case _ => false }
    def canReplace(s: Expr, t: Expr, exp1: Expr, exp2: Expr): Boolean = {
      (checkReplacement(s, t, exp1, exp2), checkReplacement(t, s, exp1, exp2)) match {
        case (EqualModuloEquality(_), _) => true
        case (_, EqualModuloEquality(_)) => true
        case _                           => false
      }
    }

    val lefteqs = leftproof.endSequent.succedent.filter(eqfilter)
    ruletype match {
      case "EQL" =>
        // we find all candidates, i.e. equations e: s=t in the left parent and pair it with possible formulas f
        // from the right parent together with possible main formulas in the conclusion s.t. f[s] = main[t] or
        // f[t] = main[s]
        val righteqsante = rightproof.endSequent.antecedent
        val candidates = for (
          case e @ Eq(s, t) <- lefteqs;
          f <- righteqsante;
          main <- fs.antecedent;
          if canReplace(s, t, f, main)
        ) yield { (s, t, e, f, main) }

        require(
          candidates.nonEmpty,
          "For an eq:l rule, could not find a possible replacement of an equation from "
            + lefteqs.map(f(_)).mkString("(", ",", ")") + " in " +
            fs.antecedent.map(f(_)).mkString("(", ",", ")")
        )

        // now we try to create an inference from each candidate
        val inferences: Seq[LKProof] = candidates.flatMap(args => {
          val (s, t, eq, f, main) = args
          val l1 = {
            // we check if we can paramodulate s=t ...
            checkReplacement(s, t, f, main) match {
              case EqualModuloEquality(_) =>
                val rule = CutRule(leftproof, eq, EqualityLeftRule(WeakeningLeftRule(rightproof, eq), eq, f, main), eq)
                try {
                  ContractionMacroRule(rule, fs) :: Nil
                } catch {
                  case e: Exception => Nil
                }
              case _ =>
                Nil
            }
          }
          val l2 = {
            // if not we try t=s....
            checkReplacement(t, s, f, main) match {
              case EqualModuloEquality(_) =>
                val rule = CutRule(leftproof, eq, EqualityLeftRule(WeakeningLeftRule(rightproof, eq), eq, f, main), eq)
                try {
                  ContractionMacroRule(rule, fs, strict = false) :: Nil
                } catch {
                  case e: Exception => Nil
                }
              case _ =>
                Nil
            }
          }
          l1 ++ l2
        })

        require(
          inferences.nonEmpty,
          "Could not infer an eq:l rule from left parent " + f(leftproof.endSequent)
            + " and " + f(rightproof.endSequent) + " to infer " + f(fs)
        )
        if (inferences.size > 1)
          debug("WARNING: Inference to create eq:l rule is not uniquely specified from left parent "
            + leftproof.endSequent + " and " + rightproof.endSequent + " to infer " + fs)

        inferences.head :: stack

      case "EQR" =>
        // we find all candidates, i.e. equations e: s=t in the left parent and pair it with possible formulas f
        // from the right parent together with possible main formulas in the conclusion
        val righteqssucc = rightproof.endSequent.succedent
        val candidates = for (
          case e @ Eq(s, t) <- lefteqs;
          f <- righteqssucc;
          main <- fs.succedent;
          if canReplace(s, t, f, main)
        ) yield { (s, t, e, f, main) }

        require(
          candidates.nonEmpty,
          "For an eq:r rule, could not find a possible replacement of an equation from "
            + lefteqs.map(f(_)).mkString("(", ",", ")") + " in " +
            fs.succedent.map(f(_)).mkString("(", ",", ")")
        )

        // now we try to create an inference from each candidate
        val inferences: Seq[LKProof] = candidates.flatMap(args => {
          val (s, t, eq, f, main) = args

          val l1 = {
            // print("Trying"+f(s)+"="+f(t)+" in "+f(main))
            // we check if we can paramodulate s=t ...
            checkReplacement(s, t, f, main) match {
              case EqualModuloEquality(_) =>
                // println("found!")
                val rule = CutRule(leftproof, eq, EqualityRightRule(WeakeningLeftRule(rightproof, eq), eq, f, main), eq)
                try {
                  ContractionMacroRule(rule, fs, strict = false) :: Nil
                } catch {
                  case e: Exception => Nil
                }
              case _ =>
                Nil
            }
          }

          val l2 = {
            // if not, we try t=s....
            // print("Trying"+f(t)+"="+f(s)+" in "+f(main))
            checkReplacement(t, s, f, main) match {
              case EqualModuloEquality(_) =>
                // println("found!")
                val rule = CutRule(leftproof, eq, EqualityRightRule(WeakeningLeftRule(rightproof, eq), eq, f, main), eq)
                try {
                  ContractionMacroRule(rule, fs, strict = false) :: Nil
                } catch {
                  case e: Exception => Nil
                }

              case _ =>
                Nil
            }
          }

          l1 ++ l2
        })

        require(
          inferences.nonEmpty,
          "Could not infer an eq:r rule from left parent " + f(leftproof.endSequent)
            + " and " + f(rightproof.endSequent) + " to infer " + f(fs)
        )
        if (inferences.size > 1)
          debug("WARNING: Inference to create eq:r rule is not uniquely specified from left parent "
            + leftproof.endSequent + " and " + rightproof.endSequent + " to infer " + fs)

        inferences.head :: stack

      case _ => throw new Exception("Expected equational rule but got rule name: " + ruletype)
    }
  }

  // TODO: integrate which definitions were used into the proof
  def handleDefinitions(current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], naming: (String) => Expr, rt: RToken, llk_definitions: Map[Expr, Expr]): List[LKProof] = {
    require(current_proof.nonEmpty, "Imbalanced proof tree in application of " + ruletype + " with es: " + fs)
    val parent :: stack = current_proof: @unchecked
    val (mainsequent, auxsequent, context) = filterContext(parent.endSequent, fs)
    require(
      auxsequent.formulas.size == 1,
      "Definition rules expect exactly one auxiliary formula, not " + f(auxsequent)
    )
    require(
      (auxsequent.antecedent.size == mainsequent.antecedent.size) &&
        (auxsequent.succedent.size == mainsequent.succedent.size),
      "The definition needs to be on the same side!"
    )

    val definitions = llk_definitions.toList.map(llkd => llkDefinitionToLKDefinition(llkd._1, llkd._2))

    (auxsequent, mainsequent) match {
      case (HOLSequent(Vector(), Vector(aux)), HOLSequent(Vector(), Vector(main))) =>
        // try each definition to infer the main formula
        val rule = definitions.dropWhile(d =>
          try {
            ConversionRightRule(parent, aux, main);
            false
          } catch { case e: Exception => true }
        ) match {
          case d :: _ => ConversionRightRule(parent, aux, main)
          case _ =>
            throw new HybridLatexParserException(
              "Couldn't find a matching definition to infer " + f(main) + " from " + f(aux)
            )
        }
        rule :: stack
      case (HOLSequent(Vector(aux), Vector()), HOLSequent(Vector(main), Vector())) =>
        // try each definition to infer the main formula
        val rule = definitions.dropWhile(d =>
          try {
            ConversionLeftRule(parent, aux, main); false
          } catch { case e: Exception => true }
        ) match {
          case d :: _ => ConversionLeftRule(parent, aux, main)
          case _ =>
            throw new HybridLatexParserException(
              "Couldn't find a matching definition to infer " + f(main) + " from " + f(aux)
            )
        }
        rule :: stack
      case _ =>
        throw new HybridLatexParserException(
          "Error in creation of definition rule, can not infer " + f(mainsequent) + " from " + f(auxsequent)
        )
    }
  }

  /*   =================== STRUCTURAL RULES =============================    */
  def handleContraction(current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], naming: (String) => Expr, rt: RToken): List[LKProof] = {
    require(current_proof.nonEmpty, "Imbalanced proof tree in application of " + ruletype + " with es: " + fs)
    val parentproof :: stack = current_proof: @unchecked
    val inf = ContractionMacroRule(parentproof, fs, strict = false)
    inf :: stack
  }

  def handleWeakening(current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], naming: (String) => Expr, rt: RToken): List[LKProof] = {
    require(current_proof.nonEmpty, "Imbalanced proof tree in application of " + ruletype + " with es: " + fs)
    val parentproof :: stack = current_proof: @unchecked
    // val inf = weaken(parentproof, fs)
    val inf = WeakeningMacroRule(parentproof, fs)
    inf :: stack
  }

  def handleCut(current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], naming: (String) => Expr, rt: RToken): List[LKProof] = {
    require(current_proof.size > 1, "Imbalanced proof tree in application of " + ruletype + " with es: " + fs)
    val rightproof :: leftproof :: stack = current_proof: @unchecked

    val auxsequent = (leftproof.endSequent ++ rightproof.endSequent) diff fs
    require(
      auxsequent.antecedent.size == 1 && auxsequent.succedent.size == 1,
      "Need exactly one formula in the antecedent and in the succedent of the parents!" + f(auxsequent)
    )
    require(
      auxsequent.antecedent.head == auxsequent.succedent.head,
      "Cut formula right (" + auxsequent.antecedent.head +
        ") is not equal to cut formula left (" + auxsequent.succedent.head + ")"
    )
    val cutformula = auxsequent.antecedent.head
    require(
      leftproof.endSequent.succedent contains cutformula,
      "Cut formula " + cutformula + " must occur in succedent of " + leftproof.endSequent
    )
    require(
      rightproof.endSequent.antecedent contains cutformula,
      "Cut formula " + cutformula + " must occur in antecedent of " + rightproof.endSequent
    )
    val inf = CutRule(leftproof, rightproof, cutformula)
    require(
      inf.endSequent multiSetEquals fs,
      "Inferred sequent " + inf.endSequent + " is what was not expected: " + fs
    )
    inf :: stack
  }

  def handleLink(proofs: Map[Formula, LKProof], current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], naming: (String) => Expr, rt: RToken): List[LKProof] = {
    require(auxterm.isDefined, "Can not refer to a subproof(CONTINUEFROM): Need a proof name to link to!")
    val link = LLKFormulaParser.ASTtoHOL(naming, auxterm.get)
    val ps: List[LKProof] = proofs.toList.flatMap(x => {
      syntacticMatching(x._1, link) match {
        case None => Nil
        case Some(sub) =>
          sub(x._2) :: Nil
      }
    })

    require(
      ps.nonEmpty,
      "None of the proofs in " +
        proofs.keys.mkString("(", ",", ")") + " matches proof link " + link
    )
    require(
      ps.head.endSequent.multiSetEquals(fs),
      "LINK to " + f(link) + " must give " + f(fs) + " but gives " + f(ps.head.endSequent)
    )

    ps.head :: current_proof

  }

  val axformula = Atom(Const("AX", To), Nil)

  def createSubstitution(naming: String => Expr, astlist: List[(ast.Var, LambdaAST)]): Substitution = {
    val terms: List[(Var, Expr)] = astlist.foldLeft(List[(Var, Expr)]())((list, p) => {
      (
        LLKFormulaParser.ASTtoHOL(naming, p._1).asInstanceOf[Var],
        LLKFormulaParser.ASTtoHOL(naming, p._2)
      ) :: list
    })
    Substitution(terms.reverse)
  }

  /* =============== Macro Rules ============================ */

  val axioms_prove_sequent = HOLSequent(List(Atom(Const("AX", To), Nil)), Nil)
  def normalize(exp: Expr) = betaNormalize(exp)

  def handleEQAxiom(current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], subterm: List[(ast.Var, LambdaAST)], naming: (String) => Expr, rt: RToken, axioms: Map[Formula, Formula], definitions: Map[Expr, Expr]): List[LKProof] = {
    require(current_proof.nonEmpty, "Imbalanced proof tree in application of " + ruletype + " with es: " + fs)
    val oldproof :: rest = current_proof: @unchecked
    require(auxterm.isDefined, "Error creating an equational axiom rule: Need instantiation annotation!")
    val auxf = c(LLKFormulaParser.ASTtoHOLnormalized(naming, auxterm.get))

    val sub = createSubstitution(naming, subterm)

    val axs = axioms.toList
    val candidates = axs.flatMap(s => {
      val (name, ax1) = s
      val (_, ax2) = stripUniversalQuantifiers(ax1)
      val ax = normalize(sub(ax2))
      // println("Trying:"+f(ax)+" against "+f(auxf))
      val r1 = syntacticMatching(ax, auxf) match {
        case Some(sub) if sub(ax) syntaxEquals auxf => (name, ax1, sub) :: Nil
        case Some(sub) =>
          val sub2 = Substitution(sub.map.filter(x => x._1 != x._2))
          if (sub2(ax) syntaxEquals auxf)
            (name, ax1, sub2) :: Nil
          else
            Nil
        case None => Nil
      }

      if (sub(ax) syntaxEquals auxf) {
        debug("User specified sub works!" + f(sub))
        (name, ax1, sub) :: r1
      } else r1
    })

    require(candidates.nonEmpty, "Could not find equational axiom for " + f(auxf))
    if (candidates.size > 1)
      debug("Warning: Axiom not uniquely specified, possible candidates: " +
        candidates.map(x => f(x._1) + " " + x._2).mkString(","))
    val (name, axiom, sub2) = candidates.head
    // definitions map (x => if (x._1 syntaxEquals(axformula)) println(x._1 +" -> "+x._2))
    val axiomconjunction = c(definitions(axformula))
    val defs = definitions.toList.map(x => llkDefinitionToLKDefinition(x._1, x._2))
    val (_, axproof) = getAxiomLookupProof(name, axiom, auxf, axiomconjunction, LogicalAxiom(auxf), sub2, defs)
    val Some(axdef) = defs.find(_.what == Const("AX", To)): @unchecked
    val axrule = ConversionLeftRule(axproof, axiomconjunction, axformula)

    val Eq(s, t) = auxf: @unchecked

    val auxsequent = oldproof.endSequent diff fs
    val mainsequent = fs diff (oldproof.endSequent ++ axioms_prove_sequent)
    require(mainsequent.formulas.size == 1, "Exactly one main formula required, not " + f(mainsequent))
    require(auxsequent.formulas.size == 1, "Excatly one auxiliary formula needed in parent, not " + f(auxsequent))
    val newproof = (auxsequent: @unchecked) match {
      case HOLSequent(Vector(), Vector(formula)) =>
        require(
          mainsequent.antecedent.isEmpty && mainsequent.succedent.size == 1,
          "Auxformula and main formula in eqaxiom rule need to be on the same side of the sequent, not "
            + f(mainsequent) + " and " + f(auxsequent)
        )
        val HOLSequent(Vector(), Vector(main)) = mainsequent
        CutRule(axrule, auxf, EqualityRightRule(WeakeningLeftRule(oldproof, auxf), auxf, formula, main), auxf)

      case HOLSequent(Vector(formula), Vector()) =>
        require(
          mainsequent.antecedent.size == 1 && mainsequent.succedent.isEmpty,
          "Auxformula and main formula in eqaxiom rule need to be on the same side of the sequent, not "
            + f(mainsequent) + " and " + f(auxsequent)
        )
        val HOLSequent(Vector(main), Vector()) = mainsequent
        CutRule(axrule, auxf, EqualityLeftRule(WeakeningLeftRule(oldproof, auxf), auxf, formula, main), auxf)
    }

    val cproof = ContractionMacroRule(newproof, fs, strict = false)

    cproof :: rest
  }

  def handleInstAxiom(current_proof: List[LKProof], ruletype: String, fs: HOLSequent, auxterm: Option[LambdaAST], subterm: List[(ast.Var, LambdaAST)], naming: (String) => Expr, rt: RToken, axioms: Map[Formula, Formula], definitions: Map[Expr, Expr]): List[LKProof] = {
    require(current_proof.nonEmpty, "Imbalanced proof tree in application of " + ruletype + " with es: " + fs)
    val oldproof :: rest = current_proof: @unchecked
    // require(auxterm.isDefined, "Error creating an stantiate axiom rule: Need instantiation annotation!")
    // val auxf = c(LLKFormulaParser.ASTtoHOL(naming, auxterm.get))
    val auxsequent = oldproof.endSequent diff fs
    val mainsequent = fs diff (oldproof.endSequent ++ axioms_prove_sequent)

    require(
      mainsequent.formulas.isEmpty,
      "The instantiate axiom rule should not have a main formula, we got: " + f(mainsequent)
    )
    require(
      auxsequent.antecedent.size == 1 && auxsequent.succedent.isEmpty,
      "Auxformula formula in inst axiom rule need to be on the lh side of the sequent, not " + f(auxsequent)
    )

    val HOLSequent(Vector(auxf_), Vector()) = auxsequent
    val auxf = c(normalize(auxf_))

    // println("auxf="+f(auxf))

    val sub = createSubstitution(naming, subterm)
    // println("sub="+f(sub))
    // println(sub)

    val axs = axioms.toList
    val candidates = axs.flatMap(s => {
      val (name, ax1) = s
      val (_, ax2) = stripUniversalQuantifiers(ax1)
      val ax = betaNormalize(sub(ax2))
      // println("Trying: "+ f(ax))
      val r1 = syntacticMatching(ax, auxf) match {
        case Some(sub) if sub(ax) syntaxEquals auxf => (name, ax1, sub) :: Nil
        case Some(sub2) =>
          val sub = Substitution(sub2.map.filterNot(x => x._1 == x._2))
          if (sub(ax) syntaxEquals auxf) {
            (name, ax1, sub) :: Nil
          } else {
            info("wrong sub found!" + f(sub(ax)) + " for " + f(auxf) + " sub=" + f(sub) + " ax=" + f(ax))
            Nil
          };

        //        case Some(sub) => (ax,sub)::Nil
        case None => Nil
      }
      if (sub(ax) syntaxEquals auxf) {
        debug("User specified sub works!" + f(sub))
        (name, ax1, sub) :: r1
      } else r1

    })

    require(candidates.nonEmpty, "Could not find instance axiom for " + f(auxf))
    if (candidates.size > 1)
      debug("Warning: Axiom not uniquely specified, possible candidates: " +
        candidates.map(x => f(x._1) + " " + x._2).mkString(","))
    val (name, axiom, sub2) = candidates.head
    // definitions map (x => if (x._1 syntaxEquals(axformula)) println(x._1 +" -> "+x._2))
    val axiomconjunction = c(definitions(axformula))
    val defs = definitions.toList.map(x => llkDefinitionToLKDefinition(x._1, x._2))
    val (_, axproof) = getAxiomLookupProof(name, axiom, auxf, axiomconjunction, oldproof, sub2, defs)
    val Some(axdef) = defs.find(_.what == Const("AX", To)): @unchecked
    val axrule = ConversionLeftRule(axproof, axiomconjunction, axformula)
    ContractionMacroRule(axrule, fs, strict = false) :: rest
  }

  /* given a map of elements to lists of dependant elements (e.g. a node's children in a graph),
   calculate a list l where
   * for every element a occurring before an element b in l we know that a does not depend on b.
   * Throws an exception, if the dependency graph contains cycles. */
  def getOrdering[T](pm: Map[T, List[T]]): List[T] = {
    val (leaves, nonleaves) = pm.partition(el => el._2.isEmpty)
    require(leaves.nonEmpty, "Circular dependency detected: " + pm)
    val leaflist = leaves.keySet.toList
    if (nonleaves.isEmpty) {
      leaflist
    } else {
      // remove leaves from the graph
      val rest = nonleaves map (el => (el._1, el._2 filterNot (leaflist contains _)))
      leaflist ++ getOrdering(rest)
    }
  }

  /* Extracts a map of dependencies between subproofs from a mapping of proof names to the
   token lists representing them. */
  def getDependecyMap(naming: String => Expr, pm: Map[Formula, List[RToken]]): Map[Formula, List[Formula]] = {
    val proofnames = pm.keySet.toList
    // only keep continuefrom tokens in the lists, map to the formulas in
    // the proofnames against which the dependencies matches
    pm.map(element =>
      (
        element._1,
        element._2.flatMap(_ match {
          case RToken("CONTINUEFROM", Some(f), _, _, _) =>
            // find the matching proofs
            proofnames.filter(p =>
              syntacticMatching(p, c(LLKFormulaParser.ASTtoHOL(naming, f))).isDefined
            ) match {
              // and check if the dependency is ok
              case Nil =>
                throw new HybridLatexParserException("Could not find a matching dependency for proof " + f
                  + " in: " + proofnames.mkString(","))
              case l @ List(d) =>
                l
              case _ =>
                throw new HybridLatexParserException("Found more than one matching dependency for proof " + f
                  + " in: " + proofnames.mkString(",") + " but proof links need to be unique!")
            }

          case RToken("INSTLEMMA", Some(f), _, _, _) =>
            // find the matching proofs
            proofnames.filter(p =>
              syntacticMatching(p, c(LLKFormulaParser.ASTtoHOL(naming, f))).isDefined
            ) match {
              // and check if the dependency is ok
              case Nil =>
                throw new HybridLatexParserException("Could not find a matching dependency for proof " + f
                  + " in: " + proofnames.mkString(","))
              case l @ List(d) =>
                l
              case _ =>
                throw new HybridLatexParserException("Found more than one matching dependency for proof " + f
                  + " in: " + proofnames.mkString(",") + " but proof links need to be unique!")
            }

          case RToken("CONTINUEFROM", _, _, _, _) =>
            throw new HybridLatexParserException("The CONTINUEFROM statement needs a proof as an argument!")
          case RToken("INSTLEMMA", _, _, _, _) =>
            throw new HybridLatexParserException("The INSTLEMMA statement needs a proof as an argument!")
          case _ => Nil
        })
      )
    )
  }

  /* remove common context from sequent (fs_old) and inferred sequent (fs_new).
   * return a triple: (sequent with main formula,
   *                   sequent with auxiliary and uncontracted formulas,
   *                   context sequent) */
  def filterContext(fs_old: HOLSequent, fs_new: HOLSequent): (HOLSequent, HOLSequent, HOLSequent) = {
    val ndiff = fs_new diff fs_old
    val odiff = fs_old diff fs_new

    val csequent = fs_new diff ndiff

    try {
      require(
        ndiff.formulas.length == 1,
        "We want exactly one primary formula, not: " + f(ndiff) +
          " in " + f(fs_new)
      )
      require(
        odiff.formulas.nonEmpty,
        "We want at least one auxiliary formula, not: " + f(odiff) +
          " in " + f(fs_old)
      )
    } catch {
      case e: Exception => throw new HybridLatexParserException(e.getMessage, e)
    }
    (ndiff, odiff, csequent)
  }

  /* creates a map from axiom names to the corresponding fsequents from a list of axiom tokens,
   * supposed to be passed on to EQAXIOM and INSTAXIOM rules */
  def createAxioms(naming: String => Expr, l: List[AToken]): Map[Formula, Formula] = {
    l.filter(_.rule == "AXIOMDEC").foldLeft(Map[Formula, Formula]())((map, token) => {
      val AToken(rulename, aname, antecedent, succedent) = token
      require(aname.nonEmpty, "Axiom declaration " + token + " needs a name!")
      val aformula: Formula = c(LLKFormulaParser.ASTtoHOL(naming, aname.get))
      val ant = antecedent.map(x => c(LLKFormulaParser.ASTtoHOL(naming, x)))
      val suc = succedent.map(x => c(LLKFormulaParser.ASTtoHOL(naming, x)))
      val fs = HOLSequent(ant, suc)
      require(ant.isEmpty && suc.size == 1, "Axiom declarations need to be one positive formula, not " + fs)
      map + ((aformula, fs.succedent.head))
    })
  }

  /* creates a binary conjunction tree from a list of formulas */
  def createConjunctions(l: List[Formula]): Formula = createConjunctions_(l) match {
    case List(conj)   => conj
    case l @ (_ :: _) => createConjunctions(l)
    case Nil          => throw new HybridLatexParserException("Could not create conjunction of empty list!")
  }
  /* for a list of formulas of length n return a list of formulas And(i_1.i_2), etc of size n/2 */
  def createConjunctions_(l: List[Formula]): List[Formula] = l match {
    case x :: y :: rest => And(x, y) :: createConjunctions_(rest)
    case _              => l
  }

  /* Checks if what is contained as formula inside a nesting of neg, and, or, imp. Used for a lookup in an
    axiom conjunction */
  private def formula_contains_atom(f: Formula, what: Formula): Boolean = f match {
    case Neg(x)    => formula_contains_atom(x, what)
    case And(x, y) => formula_contains_atom(x, what) || formula_contains_atom(y, what)
    case Imp(x, y) => formula_contains_atom(x, what) || formula_contains_atom(y, what)
    case Or(x, y)  => formula_contains_atom(x, what) || formula_contains_atom(y, what)
    case _         => f == what
  }

  def llkDefinitionToLKDefinition(exp: Expr, to: Expr) = exp match {
    case Apps(c @ Const(_, _, _), args) if args.forall({ case Var(_, _) => true; case _ => false }) =>
      val vs = args.map({ case v @ Var(_, _) => v })
      Definition(c, Abs.Block(vs, to))
    case _ =>
      throw new Exception(s"Can not convert LLK Definition $exp -> $to to LK Definition!")
  }

  /* Creates the definition map from a list of ATokens. Also adds a defintion for all the axioms. */
  def createDefinitions(naming: String => Expr, l: List[AToken], axioms: Map[Formula, Formula]): Map[Expr, Expr] = {
    val preddefs = l.filter(_.rule == "PREDDEF").foldLeft(Map[Expr, Expr]())((map, token) => {
      val (left, right) = token match {
        case AToken(_, _, List(left), List(right)) => (left, right)
        case _ => throw new HybridLatexParserException(
            "Only one formula allowed as parameters in predicate definition declaration, got " + token
          )
      }

      val lformula = c(LLKFormulaParser.ASTtoHOL(naming, left))
      val rformula = c(LLKFormulaParser.ASTtoHOL(naming, right))

      lformula match {
        case Atom(_, _) =>
          require(
            freeVariables(lformula) == freeVariables(rformula),
            "Definition formulas " + lformula + " and " + rformula + " do not have the same set of free variables!" +
              sys.props("line.separator") + freeVariables(lformula) + sys.props("line.separator") +
              freeVariables(rformula)
          )
          map + ((lformula, rformula))
        case _ => throw new HybridLatexParserException("Left hand side of a definition must be an atom, but is " +
            lformula)
      }

    })

    val fundefs = l.filter(_.rule == "FUNDEF").foldLeft(preddefs)((map, token) => {
      val (left, right) = token match {
        case AToken(_, _, List(left), List(right)) => (left, right)
        case _ => throw new HybridLatexParserException(
            "Only one formula allowed as parameters in function definition declaration, got " + token
          )
      }

      val lexpression = LLKFormulaParser.ASTtoHOL(naming, left)
      val rexpression = LLKFormulaParser.ASTtoHOL(naming, right)

      (lexpression, rexpression) match {
        case (f1 @ HOLFunction(_, _), f2 @ HOLFunction(_, _)) =>
          require(
            f1.ty == f2.ty,
            "The types of defined formulas and definition must match, but are: " + f1.ty + " and " + f2.ty
          )
          require(
            freeVariables(lexpression) == freeVariables(rexpression),
            "Definition function " + lexpression + " and " + rexpression +
              " do not have the same set of free variables!"
          )
          map + ((lexpression, rexpression))
        case _ => throw new HybridLatexParserException(
            "Left hand side of a definition must be an atom, but is " + lexpression
          )
      }

    })

    // TODO: check name clashes
    val defs_withaxioms = axioms.foldLeft(fundefs)((map, pair) => map + pair)

    if (axioms.nonEmpty) {
      val axiomformula = createConjunctions(axioms.keys.toList)
      defs_withaxioms + ((axformula, axiomformula))
    } else
      defs_withaxioms

  }

  /* remove common context from 2 sequents (fs_old1, fs_old2) and inferred sequent (fs_new).
   * return a triple: (sequent with main formula,
   *                   sequent with auxiliary and uncontracted formulas,
   *                   context sequent) */
  def filterContext(fs_old1: HOLSequent, fs_old2: HOLSequent, fs_new: HOLSequent): (HOLSequent, HOLSequent, HOLSequent) =
    filterContext(fs_old1 ++ fs_old2, fs_new)

  /* removes univarsal quantifiers from f and returns the list of quantified variables together
   * with the stripped formula */
  def stripUniversalQuantifiers(f: Formula): (List[Var], Formula) = stripUniversalQuantifiers(f, Nil)
  @tailrec
  private def stripUniversalQuantifiers(f: Formula, acc: List[Var]): (List[Var], Formula) = f match {
    case All(x, f_) => stripUniversalQuantifiers(f_, x.asInstanceOf[Var] :: acc)
    case _          => (acc.reverse, f)
  }

  /* Checked cast of Expr to Formula which gives a nicer error message */
  private def c(e: Expr): Formula =
    e match {
      case formula: Formula => formula
      case _                => throw new Exception("Could not convert " + e + " to a HOL Formula!")
    }

  def getAxiomLookupProof(name: Formula, axiom: Formula, instance: Formula, axiomconj: Formula, axiomproof: LKProof, sub: Substitution, definitions: List[Definition]): (Formula, LKProof) = {
    axiomconj match {
      case Atom(c @ Const(n, To, _), List()) =>
        val pi = proveInstanceFrom(axiom, instance, sub, axiomproof)
        val d = definitions.find(_.what == c).getOrElse(
          throw new Exception(
            s"could not find a definition for $c in ${definitions.map(_.what).sortBy(_.name)}"
          )
        )
        (axiomconj, ConversionLeftRule(pi, axiom, axiomconj))

      case And(x, y) if formula_contains_atom(x, name) =>
        val (aux, uproof) = getAxiomLookupProof(name, axiom, instance, x, axiomproof, sub, definitions)
        (axiomconj, AndLeftRule(WeakeningLeftRule(uproof, y), aux, y))

      case And(x, y) if formula_contains_atom(y, name) =>
        val (aux, uproof) = getAxiomLookupProof(name, axiom, instance, y, axiomproof, sub, definitions)
        (axiomconj, AndLeftRule(WeakeningLeftRule(uproof, x), x, aux))

      case _ =>
        throw new Exception("Could not create a proof for inference AX :- " +
          instance + sys.props("line.separator") + " " + name + " does not occur in " + axiomconj)
    }
  }

  // TODO:move this code to an appropriate place
  def proveInstance(axiom: Formula, instance: Formula, sub: Substitution): LKProof = {
    // val (qs,body) = stripUniversalQuantifiers(axiom)
    proveInstance_(axiom, instance, sub, LogicalAxiom(instance))._2
  }

  def proveInstanceFrom(axiom: Formula, instance: Formula, sub: Substitution, uproof: LKProof): LKProof = {
    // val (qs,body) = stripUniversalQuantifiers(axiom)
    proveInstance_(axiom, instance, sub, uproof)._2
  }
  def proveInstance_(axiom: Formula, instance: Formula, sub: Substitution, axiomproof: LKProof): (Formula, LKProof) = {
    //    println("Prove instance with sub "+f(sub))
    axiom match {
      case All(v, s) =>
        val (aux, uproof) = proveInstance_(s, instance, sub, axiomproof)
        (c(sub(axiom)), ForallLeftRule(uproof, c(sub(axiom)), sub.map(v)))
      case f if normalize(sub(f)) syntaxEquals instance =>
        (instance, axiomproof)
      case _ => throw new Exception(
          "Implementation error! Could not de++ " + f(axiom) + " subterm=" + sub(axiom) + " need=" + f(instance)
        )
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy