diff --git a/src/lambda/AST.scala b/src/lambda/AST.scala index 42ae209..6b3af2b 100644 --- a/src/lambda/AST.scala +++ b/src/lambda/AST.scala @@ -1,6 +1,8 @@ package lambda -sealed trait Expr +import scala.util.parsing.input.Positional + +sealed trait Expr extends Positional case class Lambda(arg: Var, body: Expr) extends Expr diff --git a/src/lambda/LambdaParser.scala b/src/lambda/LambdaParser.scala index 5344656..08d1e7f 100644 --- a/src/lambda/LambdaParser.scala +++ b/src/lambda/LambdaParser.scala @@ -12,11 +12,11 @@ class LambdaParser extends StdTokenParsers with PackratParsers { type P[+T] = PackratParser[T] lazy val expr: P[Expr] = application | notApp lazy val notApp = variable | parens | lambda - lazy val lambda: P[Lambda] = ("λ" | "\\") ~> variable ~ "." ~ expr ^^ - { case v ~ "." ~ e => Lambda(v, e) } - lazy val application: P[Apply] = expr ~ notApp ^^ - { case left ~ right => Apply(left, right) } - lazy val variable: P[Var] = ident ^^ Var.apply + lazy val lambda: P[Lambda] = positioned(("λ" | "\\") ~> variable ~ "." ~ expr ^^ + { case v ~ "." ~ e => Lambda(v, e) }) + lazy val application: P[Apply] = positioned(expr ~ notApp ^^ + { case left ~ right => Apply(left, right) }) + lazy val variable: P[Var] = positioned(ident ^^ Var.apply) lazy val parens: P[Expr] = "(" ~> expr <~ ")" def parse(str: String): ParseResult[Expr] = { diff --git a/src/lambda/LambdaREPL.scala b/src/lambda/LambdaREPL.scala index abb7400..e6f94cc 100644 --- a/src/lambda/LambdaREPL.scala +++ b/src/lambda/LambdaREPL.scala @@ -13,7 +13,15 @@ object LambdaREPL { val exprSrc = readLine("λ> ") import parser.{Success, NoSuccess} parser.parse(exprSrc) match { - case Success(expr, _) => println(pretty(eval(bind(expr)))) + case Success(expr, _) => + val bound = bind(expr) + if (bind.messages.isEmpty) + println(pretty(eval(bound))) + else { + for (m <- bind.messages) + println(m.pos.longString + m.msg) + bind.messages.clear() + } case err: NoSuccess => println(err) } } diff --git a/src/lambda/Message.scala b/src/lambda/Message.scala new file mode 100644 index 0000000..ac746a2 --- /dev/null +++ b/src/lambda/Message.scala @@ -0,0 +1,5 @@ +package lambda + +import scala.util.parsing.input.Position + +case class Message(pos: Position, msg: String) \ No newline at end of file diff --git a/src/lambda/Scope.scala b/src/lambda/Scope.scala index 12167ad..c2d1714 100644 --- a/src/lambda/Scope.scala +++ b/src/lambda/Scope.scala @@ -1,5 +1,7 @@ package lambda +import scala.collection.mutable.ListBuffer + object Scope { var id = 0 def nextId = { val i = id; id += 1; i } @@ -19,6 +21,7 @@ class Scope(val parent: Option[Scope], val boundNames: Set[String]) { } class Binder() { + val messages = ListBuffer[Message]() def apply(term: Expr) = bind(term, Scope.TOP) @@ -29,7 +32,9 @@ class Binder() { case v @ Var(name, _) => (parent closestBinding name) match { case Some(scope) => v.copy(scope = scope) - case None => sys.error("Undefined variable: " + name) + case None => + messages += Message(v.pos, "Unbound variable: " + name) + v } case Apply(fun, arg) => Apply(bind(fun, parent), bind(arg, parent))