Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions src/main/scala/com/phasmidsoftware/parse/CellParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package com.phasmidsoftware.parse

import com.phasmidsoftware.table._
import com.phasmidsoftware.util.{FP, Reflection}

import scala.reflect.ClassTag
import scala.util.control.NonFatal
import scala.util.{Failure, Success, Try}
Expand Down Expand Up @@ -580,11 +581,7 @@ trait CellParsers {
}
}

/**
* This companion object comprises CellParser[T] objects which represent conversions that are fixed,
* i.e. they don't depend on some other parameter such as the formatter in DateTime conversions.
*/
object CellParsers
object StdCellParsers extends CellParsers

/**
* CONSIDER: do we really need this exception?
Expand Down
19 changes: 8 additions & 11 deletions src/main/scala/com/phasmidsoftware/parse/Parseable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@

package com.phasmidsoftware.parse

import java.io.File
import java.net.URL

import org.joda.time.LocalDate

import java.io.File
import java.net.URL
import scala.annotation.implicitNotFound
import scala.util.parsing.combinator.JavaTokenParsers
import scala.util.{Failure, Success, Try}
Expand Down Expand Up @@ -175,17 +174,15 @@ object Parseable {
* @param w the String to parse.
* @return a Try[StringList].
*/
def split(w: String): Try[StringList] = parser.parseAll(parser.list, w) match {
case parser.Success(ws: StringList, _) => Success(ws)
case parser.Failure(msg, _) => Failure(ParseLogicException(s"cannot split string '$w': $msg"))
case parser.Error(msg, _) => Failure(ParseLogicException(s"cannot split string '$w': $msg"))
def split(w: String): Try[StringList] = ListParser.parseAll(ListParser.list, w) match {
case ListParser.Success(ws: StringList, _) => Success(ws)
case ListParser.Failure(msg, _) => Failure(ParseLogicException(s"cannot split string '$w': $msg"))
case ListParser.Error(msg, _) => Failure(ParseLogicException(s"cannot split string '$w': $msg"))
case _ => Failure(ParseLogicException(s"cannot split string '$w'"))
}

private def lift[T](f: String => T): String => Try[T] = w => Try(f(w))

private val parser = new ListParser()

private def parseAndRecover[T](w: String)(f: String => Try[T])(msg: String => String): Try[T] =
f(w).recoverWith {
case x: IllegalArgumentException => Failure(if (w.nonEmpty) InvalidParseException(msg(w), x) else BlankException(x))
Expand Down Expand Up @@ -236,10 +233,10 @@ object ParseableOption {
}

/**
* This class is a parser of lists.
* This object is a parser of lists.
* A list is considered to be enclosed by {} and separated by commas.
*/
class ListParser() extends JavaTokenParsers {
object ListParser extends JavaTokenParsers {

lazy val list: Parser[StringList] = "{" ~> strings <~ "}" | singleton

Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/com/phasmidsoftware/parse/TableParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ case class RawTableParser(override protected val predicate: Try[RawRow] => Boole

type Row = RawRow

implicit val stringSeqParser: CellParser[RawRow] = new CellParsers {}.cellParserSeq
implicit val stringSeqParser: CellParser[RawRow] = StdCellParsers.cellParserSeq

val rowParser: RowParser[Row, String] = StandardRowParser[RawRow]

Expand Down
100 changes: 44 additions & 56 deletions src/test/scala/com/phasmidsoftware/parse/CellParsersSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class CellParsersSpec extends flatspec.AnyFlatSpec with should.Matchers {
}

it should "parse optional non-empty String" in {
val parser = new CellParsers {}.cellParserOptionNonEmptyString
val parser = StdCellParsers.cellParserOptionNonEmptyString
parser.convertString("Hello") shouldBe Success(Some("Hello"))
parser.convertString("") shouldBe Success(None)
}
Expand All @@ -195,13 +195,13 @@ class CellParsersSpec extends flatspec.AnyFlatSpec with should.Matchers {
case class Int1(x: Int) extends IsInt
case class Int2(x: Int) extends IsInt
case class MyInt(s: Int, z: IsInt)
val cellParsers = new CellParsers {}
implicit val int1ColumnHelper: ColumnHelper[Int1] = cellParsers.columnHelper()
implicit val int2ColumnHelper: ColumnHelper[Int2] = cellParsers.columnHelper()
implicit val myIntColumnHelper: ColumnHelper[MyInt] = cellParsers.columnHelper()
implicit val int1Parser: CellParser[IsInt] = cellParsers.cellParser1(Int1)
implicit val int2Parser: CellParser[IsInt] = cellParsers.cellParser1(Int2)
implicit val parser: CellParser[MyInt] = cellParsers.cellParser2Conditional(MyInt.apply, Map(1 -> int1Parser, 2 -> int2Parser))
val p = StdCellParsers
implicit val int1ColumnHelper: ColumnHelper[Int1] = p.columnHelper()
implicit val int2ColumnHelper: ColumnHelper[Int2] = p.columnHelper()
implicit val myIntColumnHelper: ColumnHelper[MyInt] = p.columnHelper()
implicit val int1Parser: CellParser[IsInt] = p.cellParser1(Int1)
implicit val int2Parser: CellParser[IsInt] = p.cellParser1(Int2)
implicit val parser: CellParser[MyInt] = p.cellParser2Conditional(MyInt.apply, Map(1 -> int1Parser, 2 -> int2Parser))

RowValues(Row(Seq("1", "2"), Header.create("s", "z"), 0)).convertTo[MyInt].get.z shouldBe Int1(2)
RowValues(Row(Seq("2", "2"), Header.create("s", "z"), 1)).convertTo[MyInt].get.z shouldBe Int2(2)
Expand All @@ -211,157 +211,145 @@ class CellParsersSpec extends flatspec.AnyFlatSpec with should.Matchers {

it should "parse with cellParser1" in {
case class T(a: Int)
val cellParsers = new CellParsers {}
implicit val columnHelper: ColumnHelper[T] = cellParsers.columnHelper()
val parser: CellParser[T] = cellParsers.cellParser1(T)
implicit val columnHelper: ColumnHelper[T] = StdCellParsers.columnHelper()
val parser: CellParser[T] = StdCellParsers.cellParser1(T)
parser.toString shouldBe s"MultiCellParser: cellParser1 for ${classOf[T].getName}"
val header = Header.create("a")
val row = Row(Seq("1"), header, 0)
parser.parse(None, row, header) shouldBe Success(T(1))
val badParser: CellParser[T] = cellParsers.cellParser1(T, Seq("a", "b"))
val badParser: CellParser[T] = StdCellParsers.cellParser1(T, Seq("a", "b"))
badParser.parse(None, row, header) should matchPattern { case Failure(_) => }
}

it should "parse with cellParser2" in {
case class T(a: Int, b: Double)
val cellParsers = new CellParsers {}
implicit val columnHelper: ColumnHelper[T] = cellParsers.columnHelper()
val parser: CellParser[T] = cellParsers.cellParser2(T)
implicit val columnHelper: ColumnHelper[T] = StdCellParsers.columnHelper()
val parser: CellParser[T] = StdCellParsers.cellParser2(T)
parser.toString shouldBe s"MultiCellParser: cellParser2 for ${classOf[T].getName}"
val header = Header.create("a", "b")
val row = Row(Seq("1", "2"), header, 0)
parser.parse(None, row, header) shouldBe Success(T(1, 2.0))
val badParser: CellParser[T] = cellParsers.cellParser2(T, Seq("a"))
val badParser: CellParser[T] = StdCellParsers.cellParser2(T, Seq("a"))
badParser.parse(None, row, header) should matchPattern { case Failure(_) => }
}

it should "parse with cellParser3" in {
case class T(a: Int, b: Double, c: Boolean)
val cellParsers = new CellParsers {}
implicit val columnHelper: ColumnHelper[T] = cellParsers.columnHelper()
val parser: CellParser[T] = cellParsers.cellParser3(T)
implicit val columnHelper: ColumnHelper[T] = StdCellParsers.columnHelper()
val parser: CellParser[T] = StdCellParsers.cellParser3(T)
parser.toString shouldBe s"MultiCellParser: cellParser3 for ${classOf[T].getName}"
val header = Header.create("a", "b", "c")
val row = Row(Seq("1", "2", "true"), header, 0)
parser.parse(None, row, header) shouldBe Success(T(1, 2.0, c = true))
val badParser: CellParser[T] = cellParsers.cellParser3(T, Seq("a", "b"))
val badParser: CellParser[T] = StdCellParsers.cellParser3(T, Seq("a", "b"))
badParser.parse(None, row, header) should matchPattern { case Failure(_) => }
}

it should "parse with cellParser4" in {
case class T(a: Int, b: Double, c: String, d: Boolean)
val cellParsers = new CellParsers {}
implicit val columnHelper: ColumnHelper[T] = cellParsers.columnHelper()
val parser: CellParser[T] = cellParsers.cellParser4(T)
implicit val columnHelper: ColumnHelper[T] = StdCellParsers.columnHelper()
val parser: CellParser[T] = StdCellParsers.cellParser4(T)
parser.toString shouldBe s"MultiCellParser: cellParser4 for ${classOf[T].getName}"
val header = Header.create("a", "b", "c", "d")
val row = Row(Seq("1", "2", "x", "true"), header, 0)
parser.parse(None, row, header) shouldBe Success(T(1, 2.0, "x", d = true))
val badParser: CellParser[T] = cellParsers.cellParser4(T, Seq("a", "b", "c"))
val badParser: CellParser[T] = StdCellParsers.cellParser4(T, Seq("a", "b", "c"))
badParser.parse(None, row, header) should matchPattern { case Failure(_) => }
}

it should "parse with cellParser5" in {
case class T(a: Int, b: Double, c: String, d: Short, e: Boolean)
val cellParsers = new CellParsers {}
implicit val columnHelper: ColumnHelper[T] = cellParsers.columnHelper()
val parser: CellParser[T] = cellParsers.cellParser5(T)
implicit val columnHelper: ColumnHelper[T] = StdCellParsers.columnHelper()
val parser: CellParser[T] = StdCellParsers.cellParser5(T)
parser.toString shouldBe s"MultiCellParser: cellParser5 for ${classOf[T].getName}"
val header = Header.create("a", "b", "c", "d", "e")
val row = Row(Seq("1", "2", "x", "128", "true"), header, 0)
parser.parse(None, row, header) shouldBe Success(T(1, 2.0, "x", 128, e = true))
val badParser: CellParser[T] = cellParsers.cellParser5(T, Seq("a", "b", "c"))
val badParser: CellParser[T] = StdCellParsers.cellParser5(T, Seq("a", "b", "c"))
badParser.parse(None, row, header) should matchPattern { case Failure(_) => }
}

it should "parse with cellParser6" in {
case class T(a: Int, b: Double, c: String, d: Short, e: Byte, f: Boolean)
val cellParsers = new CellParsers {}
implicit val columnHelper: ColumnHelper[T] = cellParsers.columnHelper()
val parser: CellParser[T] = cellParsers.cellParser6(T)
implicit val columnHelper: ColumnHelper[T] = StdCellParsers.columnHelper()
val parser: CellParser[T] = StdCellParsers.cellParser6(T)
parser.toString shouldBe s"MultiCellParser: cellParser6 for ${classOf[T].getName}"
val header = Header.create("a", "b", "c", "d", "e", "f")
val row = Row(Seq("1", "2", "x", "128", "7", "true"), header, 0)
parser.parse(None, row, header) shouldBe Success(T(1, 2.0, "x", 128, 7, f = true))
val badParser: CellParser[T] = cellParsers.cellParser6(T, Seq("a", "b", "c"))
val badParser: CellParser[T] = StdCellParsers.cellParser6(T, Seq("a", "b", "c"))
badParser.parse(None, row, header) should matchPattern { case Failure(_) => }
}

it should "parse with cellParser7" in {
case class T(a: Int, b: Double, c: String, d: Short, e: Byte, f: Float, g: Boolean)
val cellParsers = new CellParsers {}
implicit val columnHelper: ColumnHelper[T] = cellParsers.columnHelper()
val parser: CellParser[T] = cellParsers.cellParser7(T)
implicit val columnHelper: ColumnHelper[T] = StdCellParsers.columnHelper()
val parser: CellParser[T] = StdCellParsers.cellParser7(T)
parser.toString shouldBe s"MultiCellParser: cellParser7 for ${classOf[T].getName}"
val header = Header.create("a", "b", "c", "d", "e", "f", "g")
val row = Row(Seq("1", "2", "x", "128", "7", "3.14", "true"), header, 0)
parser.parse(None, row, header) shouldBe Success(T(1, 2.0, "x", 128, 7, 3.14f, g = true))
val badParser: CellParser[T] = cellParsers.cellParser7(T, Seq("a", "b", "c"))
val badParser: CellParser[T] = StdCellParsers.cellParser7(T, Seq("a", "b", "c"))
badParser.parse(None, row, header) should matchPattern { case Failure(_) => }
}

it should "parse with cellParser8" in {
case class T(a: Int, b: Double, c: String, d: Short, e: Byte, f: Float, g: BigInt, h: Boolean)
val cellParsers = new CellParsers {}
implicit val columnHelper: ColumnHelper[T] = cellParsers.columnHelper()
val parser: CellParser[T] = cellParsers.cellParser8(T)
implicit val columnHelper: ColumnHelper[T] = StdCellParsers.columnHelper()
val parser: CellParser[T] = StdCellParsers.cellParser8(T)
parser.toString shouldBe s"MultiCellParser: cellParser8 for ${classOf[T].getName}"
val header = Header.create("a", "b", "c", "d", "e", "f", "g", "h")
val row = Row(Seq("1", "2", "x", "128", "7", "3.14", "1000000", "true"), header, 0)
parser.parse(None, row, header) shouldBe Success(T(1, 2.0, "x", 128, 7, 3.14f, BigInt(1000000), h = true))
val badParser: CellParser[T] = cellParsers.cellParser8(T, Seq("a", "b", "c"))
val badParser: CellParser[T] = StdCellParsers.cellParser8(T, Seq("a", "b", "c"))
badParser.parse(None, row, header) should matchPattern { case Failure(_) => }
}

it should "parse with cellParser9" in {
case class T(a: Int, b: Double, c: String, d: Short, e: Byte, f: Float, g: BigInt, h: BigDecimal, i: Boolean)
val cellParsers = new CellParsers {}
implicit val columnHelper: ColumnHelper[T] = cellParsers.columnHelper()
val parser: CellParser[T] = cellParsers.cellParser9(T)
implicit val columnHelper: ColumnHelper[T] = StdCellParsers.columnHelper()
val parser: CellParser[T] = StdCellParsers.cellParser9(T)
parser.toString shouldBe s"MultiCellParser: cellParser9 for ${classOf[T].getName}"
val header = Header.create("a", "b", "c", "d", "e", "f", "g", "h", "i")
val row = Row(Seq("1", "2", "x", "128", "7", "3.14", "1000000", "3.1415927", "true"), header, 0)
parser.parse(None, row, header) shouldBe Success(T(1, 2.0, "x", 128, 7, 3.14f, BigInt(1000000), BigDecimal(3.1415927), i = true))
val badParser: CellParser[T] = cellParsers.cellParser9(T, Seq("a", "b", "c"))
val badParser: CellParser[T] = StdCellParsers.cellParser9(T, Seq("a", "b", "c"))
badParser.parse(None, row, header) should matchPattern { case Failure(_) => }
}

it should "parse with cellParser10" in {
case class T(a: Int, b: Double, c: String, d: Short, e: Byte, f: Float, g: BigInt, h: BigDecimal, i: Int, j: Boolean)
val cellParsers = new CellParsers {}
implicit val columnHelper: ColumnHelper[T] = cellParsers.columnHelper()
val parser: CellParser[T] = cellParsers.cellParser10(T)
implicit val columnHelper: ColumnHelper[T] = StdCellParsers.columnHelper()
val parser: CellParser[T] = StdCellParsers.cellParser10(T)
parser.toString shouldBe s"MultiCellParser: cellParser10 for ${classOf[T].getName}"
val header = Header.create("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")
val row = Row(Seq("1", "2", "x", "128", "7", "3.14", "1000000", "3.1415927", "99", "true"), header, 0)
parser.parse(None, row, header) shouldBe Success(T(1, 2.0, "x", 128, 7, 3.14f, BigInt(1000000), BigDecimal(3.1415927), 99, j = true))
val badParser: CellParser[T] = cellParsers.cellParser10(T, Seq("a", "b", "c"))
val badParser: CellParser[T] = StdCellParsers.cellParser10(T, Seq("a", "b", "c"))
badParser.parse(None, row, header) should matchPattern { case Failure(_) => }
}

it should "parse with cellParser11" in {
case class T(a: Int, b: Double, c: String, d: Short, e: Byte, f: Float, g: BigInt, h: BigDecimal, i: Int, j: Char, k: Boolean)
val cellParsers = new CellParsers {}
implicit val columnHelper: ColumnHelper[T] = cellParsers.columnHelper()
val parser: CellParser[T] = cellParsers.cellParser11(T)
implicit val columnHelper: ColumnHelper[T] = StdCellParsers.columnHelper()
val parser: CellParser[T] = StdCellParsers.cellParser11(T)
parser.toString shouldBe s"MultiCellParser: cellParser11 for ${classOf[T].getName}"
val header = Header.create("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k")
val row = Row(Seq("1", "2", "x", "128", "7", "3.14", "1000000", "3.1415927", "99", "a", "true"), header, 0)
parser.parse(None, row, header) shouldBe Success(T(1, 2.0, "x", 128, 7, 3.14f, BigInt(1000000), BigDecimal(3.1415927), 99, 'a', k = true))
val badParser: CellParser[T] = cellParsers.cellParser11(T, Seq("a", "b", "c"))
val badParser: CellParser[T] = StdCellParsers.cellParser11(T, Seq("a", "b", "c"))
badParser.parse(None, row, header) should matchPattern { case Failure(_) => }
}

it should "parse with cellParser12" in {
case class T(a: Int, b: Double, c: String, d: Short, e: Byte, f: Float, g: BigInt, h: BigDecimal, i: Int, j: Char, k: Double, l: Boolean)
val cellParsers = new CellParsers {}
implicit val columnHelper: ColumnHelper[T] = cellParsers.columnHelper()
val parser: CellParser[T] = cellParsers.cellParser12(T)
implicit val columnHelper: ColumnHelper[T] = StdCellParsers.columnHelper()
val parser: CellParser[T] = StdCellParsers.cellParser12(T)
parser.toString shouldBe s"MultiCellParser: cellParser12 for ${classOf[T].getName}"
val header = Header.create("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l")
val row = Row(Seq("1", "2", "x", "128", "7", "3.14", "1000000", "3.1415927", "99", "a", "3.1415927", "true"), header, 0)
parser.parse(None, row, header) shouldBe Success(T(1, 2.0, "x", 128, 7, 3.14f, BigInt(1000000), BigDecimal(3.1415927), 99, 'a', 3.1415927, l = true))
val badParser: CellParser[T] = cellParsers.cellParser12(T, Seq("a", "b", "c"))
val badParser: CellParser[T] = StdCellParsers.cellParser12(T, Seq("a", "b", "c"))
badParser.parse(None, row, header) should matchPattern { case Failure(_) => }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class ParseableSpec extends flatspec.AnyFlatSpec with should.Matchers {

behavior of "ListParser"
it should "parse a list" in {
val p = new ListParser
val p = ListParser
p.parseAll(p.list, "{1,2,3}") should matchPattern { case p.Success(_, _) => }
p.parseAll(p.list, "{1,2-3,3}") should matchPattern { case p.Success(_, _) => }
p.parseAll(p.list, "1") should matchPattern { case p.Success(_, _) => }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,13 @@ class TableParserSpec extends flatspec.AnyFlatSpec with should.Matchers {

object IntPair {

class IntPairParser extends JavaTokenParsers {
object IntPairParser extends JavaTokenParsers {
def pair: Parser[(Int, Int)] = wholeNumber ~ wholeNumber ^^ { case x ~ y => (x.toInt, y.toInt) }
}

val intPairParser = new IntPairParser

trait IntPairRowParser extends StringParser[IntPair] {
def parse(indexedString: (String, Int))(header: Header): Try[IntPair] = intPairParser.parseAll(intPairParser.pair, indexedString._1) match {
case intPairParser.Success((x: Int, y: Int), _) => Success(IntPair(x, y))
def parse(indexedString: (String, Int))(header: Header): Try[IntPair] = IntPairParser.parseAll(IntPairParser.pair, indexedString._1) match {
case IntPairParser.Success((x: Int, y: Int), _) => Success(IntPair(x, y))
case _ => Failure(TableException(s"unable to parse ${indexedString._1}"))
}

Expand Down
8 changes: 3 additions & 5 deletions src/test/scala/com/phasmidsoftware/table/TableSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@ class TableSpec extends flatspec.AnyFlatSpec with should.Matchers {

object IntPair {

class IntPairParser extends JavaTokenParsers {
object IntPairParser extends JavaTokenParsers {
lazy val pair: Parser[(Int, Int)] = wholeNumber ~ wholeNumber ^^ { case x ~ y => (x.toInt, y.toInt) }
}

val intPairParser = new IntPairParser

trait IntPairRowParser extends StringParser[IntPair] {
def parse(indexedString: (String, Int))(header: Header): Try[IntPair] = intPairParser.parseAll(intPairParser.pair, indexedString._1) match {
case intPairParser.Success((x, y), _) => Success(IntPair(x, y))
def parse(indexedString: (String, Int))(header: Header): Try[IntPair] = IntPairParser.parseAll(IntPairParser.pair, indexedString._1) match {
case IntPairParser.Success((x, y), _) => Success(IntPair(x, y))
case _ => Failure(TableException(s"unable to parse ${indexedString._1}"))
}

Expand Down