Skip to content

Commit

Permalink
Merge 80a84f9 into f01de5c
Browse files Browse the repository at this point in the history
  • Loading branch information
gerdreiss committed Feb 23, 2017
2 parents f01de5c + 80a84f9 commit 196b716
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 15 deletions.
32 changes: 18 additions & 14 deletions base/src/main/scala/co/uproot/abandon/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ object AbandonLexer extends StdLexical with ImplicitConversions {
}

def eol = elem("eol", _ == '\n')
override def comment = ';' ~> rep(chrExcept(EofCh, '\n')) ^^ { case chars => chars.mkString }
override def comment = ';' ~> rep(chrExcept(EofCh, '\n', ';')) ^^ { case chars => chars.mkString }

/** A string is a collection of zero or more Unicode characters, wrapped in
* double quotes, using backslash escapes (cf. http://www.json.org/).
Expand Down Expand Up @@ -124,18 +124,19 @@ class AbandonParser(inputPathOpt: Option[String]) extends StandardTokenParsers w
private lazy val number: PackratParser[BigDecimal] = accept("number", { case lexical.NumericLit(n) => BigDecimal(n) })
private lazy val eol = accept("<eol>", { case lexical.EOL => })
private lazy val comment = accept("<comment>", { case lexical.CommentToken(c) => c })
private lazy val anyEol = ((comment?) ~ eol)
private lazy val comments = comment*
private lazy val anyEol = comments ~ eol
private lazy val allButEOL: PackratParser[String] = accept("any", {
case t: lexical.Token if !t.isInstanceOf[lexical.EOL.type] && !t.isInstanceOf[lexical.ErrorToken] => t.chars
})
private lazy val allUntilEOL = (allButEOL+) ^^ { case tokens => tokens.mkString("") }
private lazy val stringOrAllUntilEOL = stringLit | allUntilEOL

private lazy val fragSeparators = anyEol*
private def line[T](p: Parser[T]): Parser[T] = p <~ (((comment?) ~ eol)*)
private def line[T](p: Parser[T]): Parser[T] = p <~ ((comments ~ eol)*)

// End of line comment
private def eolComment = (comment?) <~ eol
// End of line comments
private def eolComments = comments <~ eol

lazy val abandon: Parser[Scope] = abandon(None)
def abandon(parentScopeOpt: Option[Scope]): Parser[Scope] = phrase(frags) ^^ { case entries => ParserHelper.fixupScopeParents(Scope(entries, None), parentScopeOpt) }
Expand Down Expand Up @@ -233,23 +234,26 @@ class AbandonParser(inputPathOpt: Option[String]) extends StandardTokenParsers w
}
private lazy val comparisonExpr: PackratParser[String] = ((">" | "<" | "=") ~ "=" ^^ {case (o1 ~ o2) => o1 + o2}) | ">" | "<"

private lazy val compactTxFrag = (currentPosition ~ ("." ~> dateExpr ~ accountName ~ numericExpr ~ eolComment) ^^ {
case pos ~ (date ~ accountName ~ amount ~ optComment) =>
Transaction(pos, date, List(Post(accountName, Option(amount), optComment)), None, None, Nil)
})
private lazy val compactTxFrag = currentPosition ~ ("." ~> dateExpr ~ accountName ~ numericExpr ~ eolComments) ^^ {
case pos ~ (date ~ accountName ~ amount ~ comments) =>
Transaction(pos, date, List(Post(accountName, Option(amount), comments.headOption)), None, None,
if (comments.isEmpty) Nil else comments.tail)
}

private lazy val txFrag = currentPosition ~ (((dateExpr ~ (annotation?) ~ (payee?)) <~ eol) ~ (eolComment*) ~ (post+)) ^^ {
case pos ~ (date ~ annotationOpt ~ optPayee ~ optComment ~ posts) =>
private lazy val txFrag = currentPosition ~ (((dateExpr ~ (annotation?) ~ (payee?)) <~ eol) ~ (eolComments*) ~ (postAndRestComments+)) ^^ {
case pos ~ (date ~ annotationOpt ~ optPayee ~ comments ~ postsAndComments) =>
val annotationStrOpt = annotationOpt.map(_.mkString(""))
Transaction(pos, date, posts, annotationStrOpt, optPayee, optComment.flatten)
val (ps, cs) = postsAndComments.unzip
Transaction(pos, date, ps, annotationStrOpt, optPayee, (comments ++ cs).flatten)
}

private lazy val annotation = (("(" ~> (ident | numericLit)+) <~ ")")

private lazy val payee = ((allButEOL)+) ^^ { case x => x.mkString(" ") }

private lazy val post: PackratParser[Post] = (accountName ~ opt(numericExpr) ~ eolComment) ^^ {
case name ~ amount ~ commentOpt => Post(name, amount, commentOpt)
private lazy val postAndRestComments: PackratParser[(Post, List[String])] = (accountName ~ opt(numericExpr) ~ eolComments) ^^ {
case name ~ amount ~ comments => (Post(name, amount, comments.headOption),
if (comments.isEmpty) Nil else comments.tail)
}

lazy val dateExpr = dateFrag | isoDateFrag
Expand Down
110 changes: 109 additions & 1 deletion base/src/test/scala/co/uproot/abandon/ParserTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class ParserTest extends FlatSpec with Matchers with Inside {
def unaryNegExpr(e1: Expr, pos: Option[InputPosition] = None) = {
UnaryNegExpr(e1)(pos)
}

it should "parse a simple transaction" in {
implicit val testInput = """
2013/1/2
Expand Down Expand Up @@ -587,4 +587,112 @@ class ParserTest extends FlatSpec with Matchers with Inside {
}
}
}

it should "parse one comment each before and after a post" in {
implicit val testInput =
"""
2013/1/2
; Part 1
Expense -200 ; Part 2
Cash
"""

val parseResult = parser.abandon(scanner(testInput))

inside(parseResult) {
case parser.Success(result, _) =>
inside(result.entries) {
case List(txnGroup) =>
inside(txnGroup) {
case Transaction(_, _, posts, None, None, comment1 :: Nil) =>
comment1 should be (" Part 1")
inside(posts) {
case List(Post(_, _, Some(postComment)), Post(_, _, None)) =>
postComment should be (" Part 2")
}
}
}
}
}

it should "parse multiple comments in a line after a post" in {
implicit val testInput =
"""
2013/1/2
Expense -200 ; Part 2 ; Part 1
Cash
"""

val parseResult = parser.abandon(scanner(testInput))

inside(parseResult) {
case parser.Success(result, _) =>
inside(result.entries) {
case List(txnGroup) =>
inside(txnGroup) {
case Transaction(_, _, posts, None, None, comment1 :: Nil) =>
comment1 should be (" Part 1")
inside(posts) {
case List(Post(_, _, Some(postComment)), Post(_, _, None)) =>
postComment should be (" Part 2 ")
}
}
}
}
}

it should "parse one comment before and multiple comments in a line after a post" in {
implicit val testInput =
"""
2013/1/2
; Part 1
Expense -200 ; Part 3 ; Part 2
Cash
"""

val parseResult = parser.abandon(scanner(testInput))

inside(parseResult) {
case parser.Success(result, _) =>
inside(result.entries) {
case List(txnGroup) =>
inside(txnGroup) {
case Transaction(_, _, posts, None, None, comment1 :: comment2 :: Nil) =>
comment1 should be (" Part 1")
comment2 should be (" Part 2")
inside(posts) {
case List(Post(_, _, Some(postComment)), Post(_, _, None)) =>
postComment should be (" Part 3 ")
}
}
}
}
}

it should "parse posts with no comments" in {
implicit val testInput =
"""
2013/1/2
Expense -200
Cash
"""

val parseResult = parser.abandon(scanner(testInput))

inside(parseResult) {
case parser.Success(result, _) =>
inside(result.entries) {
case List(txnGroup) =>
inside(txnGroup) {
case Transaction(_, _, posts, None, None, comments) =>
comments should be (Nil)
inside(posts) {
case List(Post(_, _, post1Comment), Post(_, _, post2Comment)) =>
post1Comment should be (None)
post2Comment should be (None)
}
}
}
}
}
}
18 changes: 18 additions & 0 deletions tests/sclT0007-comments/c01.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
inputs += c01.ledger

reportOptions = {
isRight = [Liability, Capital, Income]
}

reports += {
title = "Balance Sheet"
type = balance
outFiles = [balSheet.txt, "-"]
accountMatch = ["^Assets.*", "^Liability.*", "^Capital.*"]
}

reports += {
title = "Profit and Loss"
type = balance
accountMatch = ["^Income.*", "^Expenses.*"]
}
38 changes: 38 additions & 0 deletions tests/sclT0007-comments/c01.ledger
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
; March 2012
2012/3/1 OpeningBalance
Capital:Owner 6,000
Assets:Current:BankAccount -5,000
Assets:Fixed:Computer -1,000

; Oct 2012
2012/Oct/5
Expenses:Bank_Charges -200
Assets:Current:BankAccount

; Jan 2013
2013/Jan/15 (89000) Alice Enterprises
; For Office Stationary
Expenses:Consumables -400
Assets:Current:BankAccount


2013/1/29 (89001) Bob Traders
; For 14" monitor ; Dell
Assets:Fixed:Computer -2000 ; Dell WS
Assets:Current:BankAccount

2013/Feb/14 Persistent Helpers
; Loan
Liability:Loan 5,000
Assets:Current:BankAccount

2013/Feb/16 (80005) Deepika
; January Salary
Expenses:Salaries -2,000
Assets:Current:BankAccount

2013/1/4
; Invoice a ; Part 1
Assets:Current:Sundry_Debtors:Vikings -25,000
Income:Receipts

0 comments on commit 196b716

Please sign in to comment.