Skip to content

Commit

Permalink
Merge pull request #4 from tindzk/table-arrays
Browse files Browse the repository at this point in the history
Add support for table arrays
  • Loading branch information
jvican committed Nov 13, 2017
2 parents dfb7a97 + da6d754 commit f6052c7
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 15 deletions.
44 changes: 35 additions & 9 deletions src/main/scala/stoml/TomlParser.scala
Expand Up @@ -63,6 +63,15 @@ object Toml extends TomlSymbol with Common {
def apply(ls: Key, ps: Seq[Pair]): Table =
Table(ls -> (ps map (Pair.unapply(_).get) toMap))
}

case class TableArray(elem: (Key, Map[String, Elem])) extends Node

object TableArray {
def apply(ls: Key, ps: Seq[Pair]): TableArray =
TableArray(ls -> (ps map (Pair.unapply(_).get) toMap))
}

case class TableArrayItems(elem: List[TableArray]) extends Node
}

trait ParserUtil { this: TomlSymbol =>
Expand Down Expand Up @@ -173,21 +182,29 @@ trait TomlParser extends ParserUtil with TomlSymbol {
P(validKey.rep(min = 1, sep = WS0.? ~ "." ~ WS0.?))
val tableDef: Parser[Seq[String]] =
P("[" ~ WS0.? ~ tableIds ~ WS0.? ~ "]")
val tableArrayDef: Parser[Seq[String]] =
P("[[" ~ WS.? ~ tableIds ~ WS.? ~ "]]")

val table: Parser[Table] =
P(WS ~ tableDef ~ WS ~ pair.rep(sep = WS)).map(t =>
Table(t._1.map(Str.cleanStr).mkString("."), t._2))
P(WS ~ tableDef ~ WS ~ pair.rep(sep = WS)).map { case (a, b) =>
Table(a.map(Str.cleanStr).mkString("."), b)
}
val tableArray: Parser[TableArray] =
P(WS ~ tableArrayDef ~ WS ~ pair.rep(sep = WS)).map { case (a, b) =>
TableArray(a.map(Str.cleanStr).mkString("."), b)
}

lazy val elem: Parser[Elem] = P {
WS ~ (string | boolean | double | integer | array | date) ~ WS
}

lazy val node: Parser[Node] = P(WS ~ (pair | table) ~ WS)
lazy val node: Parser[Node] = P(WS ~ (pair | table | tableArray) ~ WS)
lazy val nodes: Parser[Seq[Node]] = P(node.rep(min = 1, sep = WS) ~ End)
}

trait TomlParserApi extends TomlParser with Common {

import stoml.Toml.{Node, Table, Pair}
import stoml.Toml.{Node, Table, Pair, TableArray, TableArrayItems}

case class TomlContent(map: Map[Key, Node]) {
def lookup(k: Key): Option[Node] = map.get(k)
Expand All @@ -199,11 +216,20 @@ trait TomlParserApi extends TomlParser with Common {

object TomlContent {
def apply(s: Seq[Node]): TomlContent = TomlContent {
s.foldLeft(Map.empty[Key, Node])((m, e) =>
e match {
case t: Table => m + (t.elem._1 -> t)
case p: Pair => m + (p.elem._1 -> p)
})
s.foldLeft(Map.empty[Key, Node]) { (m, e) =>
val value = e match {
case t: Table => t.elem._1 -> t
case t: TableArray =>
t.elem._1 -> (m.get(t.elem._1) match {
case Some(TableArrayItems(items)) =>
TableArrayItems(items :+ t)
case _ => TableArrayItems(List(t))
})
case p: Pair => p.elem._1 -> p
}

m + value
}
}
}

Expand Down
36 changes: 36 additions & 0 deletions src/test/scala/api/TomlParserApiSpec.scala
Expand Up @@ -4,6 +4,7 @@ import fastparse.core.Parsed.{Failure, Success}
import org.scalatest.{FunSpec, Matchers}

class TomlParserApiSpec extends FunSpec with Matchers {
import stoml.Toml._
import stoml.TomlParserApi._

val smallFileTest =
Expand Down Expand Up @@ -43,5 +44,40 @@ class TomlParserApiSpec extends FunSpec with Matchers {
fail("`toToml` has not parsed correctly the file")
}
}

it("should parse parse table arrays") {
val array =
"""
|[[products]]
|name = "Hammer"
|sku = 738594937
|colour = "blue"
|
|[[products]]
|name = "Nail"
|sku = 284758393
|colour = "grey"
""".stripMargin

parseToml(array) match {
case Success(v, _) =>
val p = v.lookup("products")
println(p)
assert(p.contains(TableArrayItems(List(
TableArray("products", List(
Pair("name" -> Str("Hammer")),
Pair("sku" -> Integer(738594937)),
Pair("colour" -> Str("blue"))
)),
TableArray("products", List(
Pair("name" -> Str("Nail")),
Pair("sku" -> Integer(284758393)),
Pair("colour" -> Str("grey"))
))
))))

case f: Failure[_, _] => fail()
}
}
}
}
25 changes: 19 additions & 6 deletions src/test/scala/stoml/ArrayTomlSpec.scala
Expand Up @@ -11,16 +11,14 @@ trait ArrayTomlGen {
with StringTomlGen
with NumbersTomlGen =>

val openChars = List("[", "[\n")
val seps = List(",\n", ",")
val closeChars = List("]", "\n]")

def arrayFormat(s: Seq[_], fs: (String, String, String)): String =
fs._1 + (s mkString fs._2) + fs._3

import Gen.{nonEmptyListOf, oneOf}

def arrayGen = for {
def arrayGen(openChars: List[String],
seps: List[String],
closeChars: List[String]) = for {
ts <- oneOf(validStrGen, validDoubleGen, validLongGen)
elems <- nonEmptyListOf(ts)
c1 <- oneOf(openChars)
Expand All @@ -36,7 +34,22 @@ class ArrayTomlSpec extends PropSpec
with TestParserUtil {

property("parse arrays") {
forAll(arrayGen) {
val openChars = List("[", "[\n")
val seps = List(",\n", ",")
val closeChars = List("]", "\n]")

forAll(arrayGen(openChars, seps, closeChars)) {
s: String =>
shouldBeSuccess(elem.parse(s))
}
}

property("parse table arrays") {
val openChars = List("[[", "[[\n")
val seps = List(",\n", ",")
val closeChars = List("]]", "\n]]")

forAll(arrayGen(openChars, seps, closeChars)) {
s: String =>
shouldBeSuccess(elem.parse(s))
}
Expand Down

0 comments on commit f6052c7

Please sign in to comment.