Skip to content

Commit

Permalink
record position for every expression and use it in errors
Browse files Browse the repository at this point in the history
closes gh-102
  • Loading branch information
hrj committed Sep 23, 2016
1 parent 7923635 commit 6d8508d
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 63 deletions.
29 changes: 15 additions & 14 deletions base/src/main/scala/co/uproot/abandon/Ast.scala
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ case class AccountDeclaration(name: AccountName, details: Map[String, Expr]) ext
case class IncludeDirective(fileName: String) extends ASTEntry

sealed abstract class Expr {
val pos: Option[InputPosition]
def prettyPrint = toString
def getRefs: Seq[Ref]
}
Expand All @@ -162,46 +163,46 @@ trait LiteralValue[T] {
def getRefs = Nil
}

case class BooleanLiteralExpr(val value: Boolean) extends Expr with LiteralValue[Boolean]
case class StringLiteralExpr(val value: String) extends Expr with LiteralValue[String]
case class BooleanLiteralExpr(val value: Boolean)(val pos: Option[InputPosition]) extends Expr with LiteralValue[Boolean]
case class StringLiteralExpr(val value: String)(val pos: Option[InputPosition]) extends Expr with LiteralValue[String]

case class NumericLiteralExpr(val value: BigDecimal) extends Expr with LiteralValue[BigDecimal] {
case class NumericLiteralExpr(val value: BigDecimal)(val pos: Option[InputPosition]) extends Expr with LiteralValue[BigDecimal] {
override def prettyPrint = value.toString
}

case class FunctionExpr(val name: String, val arguments: Seq[Expr]) extends Expr {
case class FunctionExpr(val name: String, val arguments: Seq[Expr], val pos: Option[InputPosition]) extends Expr {
override def prettyPrint = "%s(%s)" format (name, arguments.map(_.prettyPrint).mkString(", "))
def getRefs = Ref(name, arguments.length) +: arguments.flatMap(_.getRefs)
def getRefs = Ref(name, arguments.length, pos) +: arguments.flatMap(_.getRefs)
}

case class IdentifierExpr(val name: String) extends Expr {
case class IdentifierExpr(val name: String)(val pos: Option[InputPosition]) extends Expr {
override def prettyPrint = name
def getRefs = Seq(Ref(name, 0))
def getRefs = Seq(Ref(name, 0, pos))
}

sealed abstract class BinaryExpr(op1: Expr, op2: Expr, opChar: String, operation: (BigDecimal, BigDecimal) => BigDecimal) extends Expr {
override def prettyPrint = op1.prettyPrint + " " + opChar + " " + op2.prettyPrint
def getRefs = op1.getRefs ++ op2.getRefs
}

case class AddExpr(val op1: Expr, val op2: Expr) extends BinaryExpr(op1, op2, "+", _ + _)
case class AddExpr(val op1: Expr, val op2: Expr)(val pos: Option[InputPosition]) extends BinaryExpr(op1, op2, "+", _ + _)

case class SubExpr(val op1: Expr, val op2: Expr) extends BinaryExpr(op1, op2, "-", _ - _)
case class SubExpr(val op1: Expr, val op2: Expr)(val pos: Option[InputPosition]) extends BinaryExpr(op1, op2, "-", _ - _)

case class MulExpr(val op1: Expr, val op2: Expr) extends BinaryExpr(op1, op2, "*", _ * _)
case class MulExpr(val op1: Expr, val op2: Expr)(val pos: Option[InputPosition]) extends BinaryExpr(op1, op2, "*", _ * _)

case class DivExpr(val op1: Expr, val op2: Expr) extends BinaryExpr(op1, op2, "/", _ / _)
case class DivExpr(val op1: Expr, val op2: Expr)(val pos: Option[InputPosition]) extends BinaryExpr(op1, op2, "/", _ / _)

case class UnaryNegExpr(val op: Expr) extends Expr {
case class UnaryNegExpr(val op: Expr)(val pos: Option[InputPosition]) extends Expr {
override def prettyPrint = " -(" + op.prettyPrint + ")"
def getRefs = op.getRefs
}

case class ConditionExpr(val e1: Expr, val op: String, val e2: Expr) extends Expr {
case class ConditionExpr(val e1: Expr, val op: String, val e2: Expr)(val pos: Option[InputPosition]) extends Expr {
def getRefs = e1.getRefs ++ e2.getRefs
}

case class IfExpr(val cond: Expr, val op1: Expr, val op2: Expr) extends Expr {
case class IfExpr(val cond: Expr, val op1: Expr, val op2: Expr)(val pos: Option[InputPosition]) extends Expr {
def getRefs = cond.getRefs ++ op1.getRefs ++ op2.getRefs
}

Expand Down
45 changes: 27 additions & 18 deletions base/src/main/scala/co/uproot/abandon/Eval.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package co.uproot.abandon

import com.sun.org.apache.xalan.internal.xsltc.compiler.LiteralExpr

case class Ref(name: String, argCount: Int)
case class Ref(name: String, argCount: Int, pos: Option[InputPosition])

object EvaluationContext {
private def ensureUnique(defs: Seq[Definition]):Unit = {
Expand All @@ -25,7 +25,7 @@ class EvaluationContext(scope: Scope, localDefs: Seq[Definition]) {
d.rhs.getRefs foreach { ref =>
if (!defined.isDefinedAt(ref.name)) {
if (!d.params.contains(ref.name)) {
throw new InputPosError("Definition not found: " + ref.name, d.pos)
throw mkInputError("Couldn't resolve reference: " + ref.name, ref.pos)
}
} else {
if (defined(ref.name).params.length != ref.argCount) {
Expand All @@ -42,7 +42,16 @@ class EvaluationContext(scope: Scope, localDefs: Seq[Definition]) {

def isImmediatelyEvaluable(name: String) = true

def getValue(name: String, params: Seq[Expr]) = {
private def mkInputError(msg: String, posOpt: Option[InputPosition]):RuntimeException = {
posOpt match {
case Some(pos) =>
new InputPosError(msg, pos)
case None =>
new InputError(msg)
}
}

def getValue(name: String, params: Seq[Expr], e: Expr) = {
defined.get(name) match {
case Some(d) =>
val d = defined(name)
Expand All @@ -54,16 +63,16 @@ class EvaluationContext(scope: Scope, localDefs: Seq[Definition]) {
// println("evaluated", name, params, result)
result
case None =>
// TODO: show input position
throw new InputError("Definition not found: " + name)
throw mkInputError("Couldn't resolve reference: " + name, e.pos)
}
}

def evaluate[T](e: Expr)(implicit m: Manifest[T]):T = {
// TODO: Expression can also have a position, and we can throw InputPosError
evaluateInternal(e) match {
case t:T => t
case x => throw new InputError("Expected type: " + m + " but expression evaluated to: " + x.getClass)
case x =>
throw mkInputError("Expected type: " + m + " but expression evaluated to: " + x.getClass, e.pos)
}
}

Expand All @@ -73,24 +82,24 @@ class EvaluationContext(scope: Scope, localDefs: Seq[Definition]) {

private def evaluateInternal(e:Expr):Expr = {
e match {
case AddExpr(e1, e2) => NumericLiteralExpr(evaluateBD(e1) + evaluateBD(e2))
case SubExpr(e1, e2) => NumericLiteralExpr(evaluateBD(e1) - evaluateBD(e2))
case MulExpr(e1, e2) => NumericLiteralExpr(evaluateBD(e1) * evaluateBD(e2))
case DivExpr(e1, e2) => NumericLiteralExpr(evaluateBD(e1) / evaluateBD(e2))
case UnaryNegExpr(e1) => NumericLiteralExpr(-evaluateBD(e1))
case AddExpr(e1, e2) => NumericLiteralExpr(evaluateBD(e1) + evaluateBD(e2))(None)
case SubExpr(e1, e2) => NumericLiteralExpr(evaluateBD(e1) - evaluateBD(e2))(None)
case MulExpr(e1, e2) => NumericLiteralExpr(evaluateBD(e1) * evaluateBD(e2))(None)
case DivExpr(e1, e2) => NumericLiteralExpr(evaluateBD(e1) / evaluateBD(e2))(None)
case UnaryNegExpr(e1) => NumericLiteralExpr(-evaluateBD(e1))(None)
case le:LiteralValue[_] => le
case IdentifierExpr(name) => getValue(name, Nil)
case FunctionExpr(name, arguments) => getValue(name, arguments.map(evaluateInternal(_)))
case IdentifierExpr(name) => getValue(name, Nil, e)
case FunctionExpr(name, arguments, _) => getValue(name, arguments.map(evaluateInternal(_)), e)
case IfExpr(cond, e1, e2) => evaluateInternal(if(evaluateBoolean(cond)) e1 else e2)
case ConditionExpr(e1, op, e2) => {
val r1 = evaluateBD(e1)
val r2 = evaluateBD(e2)
op match {
case ">" => BooleanLiteralExpr(r1 > r2)
case ">=" => BooleanLiteralExpr(r1 >= r2)
case "<" => BooleanLiteralExpr(r1 < r2)
case "<=" => BooleanLiteralExpr(r1 <= r2)
case "==" => BooleanLiteralExpr(r1 == r2)
case ">" => BooleanLiteralExpr(r1 > r2)(None)
case ">=" => BooleanLiteralExpr(r1 >= r2)(None)
case "<" => BooleanLiteralExpr(r1 < r2)(None)
case "<=" => BooleanLiteralExpr(r1 <= r2)(None)
case "==" => BooleanLiteralExpr(r1 == r2)(None)
}
}
}
Expand Down
47 changes: 29 additions & 18 deletions base/src/main/scala/co/uproot/abandon/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class AbandonParser(inputPathOpt: Option[String]) extends StandardTokenParsers w
case name ~ details => AccountDeclaration(name, details)
}
private lazy val accountDefDetails = keyValuePairs
private lazy val keyValuePairSingleton = (ident) ^^ { case k => (k, BooleanLiteralExpr(true)) }
private lazy val keyValuePairSingleton = (currentPosition ~ ident) ^^ { case pos ~ k => (k, BooleanLiteralExpr(true)(Some(pos))) }
private lazy val keyValuePairBoolean = ((ident <~ ":") ~ booleanExpr) ^^ { case k ~ v => (k, v) }
private lazy val keyValuePair = keyValuePairSingleton | keyValuePairBoolean
private lazy val keyValuePairs = ((keyValuePair <~ anyEol)*) ^^ { case s => s.toMap }
Expand All @@ -176,50 +176,61 @@ class AbandonParser(inputPathOpt: Option[String]) extends StandardTokenParsers w
private lazy val expression: PackratParser[Expr] = (numericExpr ||| booleanExpr ||| stringExpr)

private def mkFunctionExpr(exprParser: Parser[Expr]) = {
val zeroArgFunctionExpr = (ident ^^ { case x => IdentifierExpr(x) })
val multiArgFunctionExpr = (((ident <~ "(") ~ rep1sep(exprParser, ",")) <~ ")") ^^ { case x ~ es => FunctionExpr(x, es) }
val zeroArgFunctionExpr = (currentPosition ~ ident ^^ { case pos ~ x => IdentifierExpr(x)(Some(pos)) })
val multiArgFunctionExpr = currentPosition ~ (((ident <~ "(") ~ rep1sep(exprParser, ",")) <~ ")") ^^ {
case pos ~ (x ~ es) => FunctionExpr(x, es, Some(pos))
}
multiArgFunctionExpr ||| zeroArgFunctionExpr
}

private lazy val functionExpr = mkFunctionExpr(expression)

lazy val numericParser: Parser[Expr] = phrase(numericExpr)

private lazy val numericLiteralExpr: PackratParser[Expr] = (number ^^ { case n => NumericLiteralExpr(n) })
private lazy val numericLiteralExpr: PackratParser[Expr] = (currentPosition ~ number ^^ { case pos ~ n => NumericLiteralExpr(n)(Some(pos)) })
private lazy val numericLiteralFirstExpr: PackratParser[Expr] = (numericLiteralExpr | numericExpr)
private lazy val unaryPosExpr: PackratParser[Expr] = ("+" ~> numericLiteralFirstExpr)
private lazy val unaryNegExpr: PackratParser[UnaryNegExpr] = ("-" ~> numericLiteralFirstExpr) ^^ { case expr => UnaryNegExpr(expr) }
private lazy val unaryNegExpr: PackratParser[UnaryNegExpr] = currentPosition ~ ("-" ~> numericLiteralFirstExpr) ^^ {
case pos ~ expr => UnaryNegExpr(expr)(Some(pos))
}
private lazy val parenthesizedExpr: PackratParser[Expr] = (("(" ~> numericExpr) <~ ")") ^^ { case expr => expr }

private lazy val ternaryIfExpr: PackratParser[Expr] = (booleanExpr <~ "?") ~ (expression <~ ":") ~ expression ^^ { case be ~ e1 ~ e2 => IfExpr(be, e1, e2) }
private lazy val ternaryIfExpr: PackratParser[Expr] = currentPosition ~ (booleanExpr <~ "?") ~ (expression <~ ":") ~ expression ^^ {
case pos ~ be ~ e1 ~ e2 => IfExpr(be, e1, e2)(Some(pos))
}

private def mkExpr(op: String, e1: Expr, e2: Expr) = {
private def mkExpr(op: String, e1: Expr, e2: Expr, pos: InputPosition) = {
op match {
case "+" => AddExpr(e1, e2)
case "-" => SubExpr(e1, e2)
case "*" => MulExpr(e1, e2)
case "/" => DivExpr(e1, e2)
case "+" => AddExpr(e1, e2)(Some(pos))
case "-" => SubExpr(e1, e2)(Some(pos))
case "*" => MulExpr(e1, e2)(Some(pos))
case "/" => DivExpr(e1, e2)(Some(pos))
}
}

private lazy val numericExpr: PackratParser[Expr] =
(term ~ termFrag) ^^ {
case t1 ~ ts => ts.foldLeft(t1) { case (acc, op ~ t2) => mkExpr(op, acc, t2) }
currentPosition ~ (term ~ termFrag) ^^ {
case pos ~ (t1 ~ ts) => ts.foldLeft(t1) { case (acc, op ~ t2) => mkExpr(op, acc, t2, pos) }
}

private lazy val term: PackratParser[Expr] =
factor ~ factorFrag ^^ {
case t1 ~ ts => ts.foldLeft(t1) { case (acc, op ~ t2) => mkExpr(op, acc, t2) }
currentPosition ~ factor ~ factorFrag ^^ {
case pos ~ t1 ~ ts => ts.foldLeft(t1) { case (acc, op ~ t2) => mkExpr(op, acc, t2, pos) }
}

private lazy val termFrag: PackratParser[Seq[String ~ Expr]] = (("+" | "-") ~ term)*
private lazy val factor: PackratParser[Expr] = ternaryIfExpr | functionExpr | numericLiteralExpr | parenthesizedExpr | unaryPosExpr | unaryNegExpr
private lazy val factorFrag: PackratParser[Seq[String ~ Expr]] = (("*" | "/") ~ factor)*

private lazy val stringExpr: PackratParser[Expr] = (stringLit ^^ StringLiteralExpr) | functionExpr
private lazy val stringLitExpr = (currentPosition ~ stringLit) ^^ {case pos ~ lit => StringLiteralExpr(lit)(Some(pos))}
private lazy val stringExpr: PackratParser[Expr] = stringLitExpr | functionExpr
private lazy val booleanExpr: PackratParser[Expr] = conditionExpr | booleanLiteralExpr | functionExpr
private lazy val booleanLiteralExpr = (trueKeyword ^^^ BooleanLiteralExpr(true)) | (falseKeyword ^^^ BooleanLiteralExpr(false))
private lazy val conditionExpr = (numericExpr ~ comparisonExpr ~ numericExpr) ^^ { case (e1 ~ op ~ e2) => ConditionExpr(e1, op, e2)}
private lazy val trueExpr = currentPosition ~ trueKeyword ^^ { case pos ~ lit => BooleanLiteralExpr(true)(Some(pos))}
private lazy val falseExpr = currentPosition ~ falseKeyword ^^ { case pos ~ lit => BooleanLiteralExpr(false)(Some(pos))}
private lazy val booleanLiteralExpr = trueExpr | falseExpr
private lazy val conditionExpr = currentPosition ~ (numericExpr ~ comparisonExpr ~ numericExpr) ^^ {
case pos ~ (e1 ~ op ~ e2) => ConditionExpr(e1, op, e2)(Some(pos))
}
private lazy val comparisonExpr: PackratParser[String] = ((">" | "<" | "=") ~ "=" ^^ {case (o1 ~ o2) => o1 + o2}) | ">" | "<"

private lazy val compactTxFrag = (currentPosition ~ ("." ~> dateExpr ~ accountName ~ numericExpr ~ eolComment) ^^ {
Expand Down
2 changes: 1 addition & 1 deletion base/src/main/scala/co/uproot/abandon/Process.scala
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ object Processor {
if (!(txTotal equals Zero)) {
txScope.definitions.find { d => d.name equals "defaultAccount" } match {
case Some(defaultAccountDef) => {
val defaultAccount = evaluationContext.evaluateString(FunctionExpr("defaultAccount", Nil))
val defaultAccount = evaluationContext.evaluateString(FunctionExpr("defaultAccount", Nil, Some(tx.pos)))
val fullDefaultAccount = transformAlias(AccountName(defaultAccount.split(":")))
val delta = -txTotal
txTotal += delta
Expand Down
Loading

0 comments on commit 6d8508d

Please sign in to comment.