137 lines
3.9 KiB
Scala
137 lines
3.9 KiB
Scala
package fos
|
|
|
|
import scala.util.parsing.combinator.syntactical.StandardTokenParsers
|
|
import scala.util.parsing.input.*
|
|
|
|
/** This object implements a parser and evaluator for the NB
|
|
* language of booleans and numbers found in Chapter 3 of
|
|
* the TAPL book.
|
|
*/
|
|
object Arithmetic extends StandardTokenParsers {
|
|
lexical.reserved ++= List("true", "false", "if", "then", "else", "succ", "pred", "iszero")
|
|
|
|
import lexical.NumericLit
|
|
|
|
/** term ::= 'true'
|
|
| 'false'
|
|
| 'if' term 'then' term 'else' term
|
|
| numericLit
|
|
| 'succ' term
|
|
| 'pred' term
|
|
| 'iszero' term
|
|
*/
|
|
|
|
def NumericRec(c:Int,s:Term): (Int,Term) = {
|
|
if(c<=0){
|
|
(0,s)
|
|
}else{
|
|
NumericRec(c-1,Succ(s))
|
|
}
|
|
}
|
|
|
|
def term: Parser[Term] = {
|
|
"true" ^^^ True |
|
|
"false" ^^^ False |
|
|
("if" ~> term) ~ ("then" ~> term) ~ ("else" ~> term) ^^ { case cond ~ t1 ~ t2 => If(cond, t1, t2) } |
|
|
numericLit ^^ { case v if (v.toInt >=0) => NumericRec(v.toInt,Zero)._2} |
|
|
("succ" ~> term) ^^ { Succ(_) } |
|
|
("pred" ~> term) ^^ { Pred(_) } |
|
|
("iszero" ~> term) ^^ { IsZero(_) } |
|
|
failure("Unknown Term")
|
|
}
|
|
|
|
case class NoReductionPossible(t: Term) extends Exception(t.toString)
|
|
|
|
/** Return a list of at most n terms, each being one step of reduction. */
|
|
def path(t: Term, n: Int = 64): List[Term] =
|
|
if (n <= 0) Nil
|
|
else
|
|
t :: {
|
|
try {
|
|
path(reduce(t), n - 1)
|
|
} catch {
|
|
case NoReductionPossible(t1) =>
|
|
Nil
|
|
}
|
|
}
|
|
|
|
def isNumeric(t: Term): Boolean = t match {
|
|
case Zero => true
|
|
case Pred(v) => isNumeric(v)
|
|
case Succ(v) => isNumeric(v)
|
|
case _ => false;
|
|
}
|
|
/** Perform one step of reduction, when possible.
|
|
* If reduction is not possible NoReductionPossible exception
|
|
* with corresponding irreducible term should be thrown.
|
|
*/
|
|
def reduce(t: Term): Term = t match {
|
|
case If(cond, t1, t2) => if(cond == True) t1 else if (cond == False) t2 else If(reduce(cond), t1, t2)
|
|
case IsZero(z) => z match {
|
|
case Zero => True
|
|
case Succ(_) => False
|
|
case other => IsZero(reduce(other))
|
|
}
|
|
case Succ(s) => s match {
|
|
case Zero => throw NoReductionPossible(Zero)
|
|
case other => Succ(reduce(other))
|
|
}
|
|
case Pred(p) => p match {
|
|
case Zero => Zero
|
|
case Succ(nv) if (isNumeric(nv)) => nv
|
|
case other => Pred(reduce(other))
|
|
}
|
|
case other => throw NoReductionPossible(other)
|
|
}
|
|
|
|
case class TermIsStuck(t: Term) extends Exception(t.toString)
|
|
|
|
/** Perform big step evaluation (result is always a value.)
|
|
* If evaluation is not possible TermIsStuck exception with
|
|
* corresponding inner irreducible term should be thrown.
|
|
*/
|
|
def eval(t: Term): Term = t match {
|
|
case True => True
|
|
case False => False
|
|
case If(cond, t1, t2) => eval(cond) match {
|
|
case True => eval(t1)
|
|
case False => eval(t2)
|
|
case _ => throw TermIsStuck(t)
|
|
}
|
|
case Zero => Zero
|
|
case Succ(v) => eval(v) match {
|
|
case Zero => Zero
|
|
case Pred(nv) => eval(nv)
|
|
case _ => throw TermIsStuck(t)
|
|
}
|
|
case Pred(v) => eval(v) match {
|
|
case Zero => Zero
|
|
case Succ(nv) => eval(nv)
|
|
case _ => throw TermIsStuck(t)
|
|
}
|
|
case IsZero(v) => eval(v) match {
|
|
case Zero => True
|
|
case Succ(nv) => False
|
|
case _ => throw TermIsStuck(t)
|
|
}
|
|
}
|
|
|
|
def main(args: Array[String]): Unit = {
|
|
val stdin = new java.io.BufferedReader(new java.io.InputStreamReader(System.in))
|
|
val tokens = new lexical.Scanner(stdin.readLine())
|
|
phrase(term)(tokens) match {
|
|
case Success(trees, _) =>
|
|
for (t <- path(trees))
|
|
println(t)
|
|
try {
|
|
print("Big step: ")
|
|
println(eval(trees))
|
|
} catch {
|
|
case TermIsStuck(t) => println("Stuck term: " + t)
|
|
}
|
|
case e =>
|
|
println(e)
|
|
}
|
|
}
|
|
}
|