package uk.ac.man.cs.lethe.internal.fol.parsing

import scala.util.parsing.combinator.syntactical._
import scala.util.parsing.combinator.RegexParsers

import uk.ac.man.cs.lethe.internal.fol.datatypes._

object FOLParser extends RegexParsers { 

  def parseFormula(input: String): Formula = parseAll(formula, input) match { 
    case Success(c, _) => c
    case Failure(m, remaining) => throw new RuntimeException(m+"\n"+remaining.pos.longString)
    case Error(m, remaining) => throw new RuntimeException(m+"\n"+remaining.pos.longString)
  }

  def parseClause(input: String): Clause = parseAll(clause, input) match { 
    case Success(c, _) => c
    case Failure(m, remaining) => throw new RuntimeException(m+"\n"+remaining.pos.longString)
    case Error(m, remaining) => throw new RuntimeException(m+"\n"+remaining.pos.longString)
  }

  val PREDICATE_NAME = """\w+"""r
  val VARIABLE = """_?[xyzuvw]\d*"""r
  val CONSTANT = """[abcde]\d*"""r
  val FUNCTION_NAME = """_?[fgh]\d*"""r

  def formula: Parser[Formula] = 

    pLiteral | negation | conjunction | disjunction | implication | equivalence | universalQuantification | existentialQuantification


  def clause: Parser[Clause] = 
    repsep(literal, "||") ^^ { case literals: List[Literal] => Clause(Set()++literals)}

  def literal: Parser[Literal] = pLiteral | nLiteral

  def pLiteral: Parser[Literal] = 
    PREDICATE_NAME ~ "(" ~ repsep(term, ",") <~ ")" ^^ { case predName ~ _ ~ args => Literal(true, GenericPredicate(predName, args))}

  def nLiteral: Parser[Literal] = 
    "¬" ~ PREDICATE_NAME ~ "(" ~ repsep(term, ",") <~ ")" ^^ { case _ ~ predName ~ _ ~ args => Literal(false, GenericPredicate(predName, args))}

  def term: Parser[Term] = 
    ( variable | CONSTANT ^^ { c => Constant(c)} | function )

  def variable: Parser[Variable] = 
    VARIABLE ^^ { v => Variable(v)}

  def function: Parser[Function] =
    FUNCTION_NAME ~ "(" ~ repsep(term, ",") <~ ")" ^^ { case funcName ~ _ ~ args => Function(funcName, args) }

  def negation: Parser[Formula] =
    "¬" ~ formula ^^ { case _ ~ f => Negation(f)}

  def conjunction: Parser[Formula] = 
    "(" ~> repsep(formula, "&&") <~ ")" ^^ { (formula: List[Formula]) => Conjunction(formula.toSet) }

  def disjunction: Parser[Formula] =
    "(" ~> repsep(formula, "||") <~ ")" ^^ { (formula: List[Formula]) => Disjunction(formula.toSet) }

  def implication: Parser[Formula] = 
    "(" ~> formula ~ "->" ~ formula <~ ")" ^^ { case f1 ~ _ ~ f2 => Implication(f1, f2) }

  def equivalence: Parser[Formula] =
    "(" ~> formula ~ "<->" ~ formula <~ ")" ^^ { case f1 ~ _ ~ f2 => Equivalence(f1, f2) }

  def universalQuantification: Parser[Formula] = 
    "A" ~> variable ~ "." ~ formula ^^ { case v ~ _ ~ f => UniversalQuantification(v, f) }

  def existentialQuantification: Parser[Formula] = 
    "E" ~> variable ~ "." ~ formula ^^ { case v ~ _ ~ f => ExistentialQuantification(v, f) }
}  
