aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jakob@odersky.com>2018-04-08 14:31:06 -0700
committerJakob Odersky <jakob@odersky.com>2018-04-08 14:31:06 -0700
commitf1fe50cba0c07d27f109a727c480b02639d140e0 (patch)
tree258c5de20c6eb8bb2ff46fd623fa8fb7b76daebf
parentc0d91d66fb7cf06fb7d2fff7ba3218b7a4dc5e49 (diff)
downloadyamlesque-f1fe50cba0c07d27f109a727c480b02639d140e0.tar.gz
yamlesque-f1fe50cba0c07d27f109a727c480b02639d140e0.tar.bz2
yamlesque-f1fe50cba0c07d27f109a727c480b02639d140e0.zip
Add tests
-rw-r--r--yamlesque/src/main/scala/YamlParser.scala (renamed from yamlesque/src/main/scala/parser.scala)154
-rw-r--r--yamlesque/src/main/scala/package.scala7
-rw-r--r--yamlesque/src/main/scala/yamlValues.scala2
-rw-r--r--yamlesque/src/test/scala/ParserTests.scala262
4 files changed, 289 insertions, 136 deletions
diff --git a/yamlesque/src/main/scala/parser.scala b/yamlesque/src/main/scala/YamlParser.scala
index c912330..71849f1 100644
--- a/yamlesque/src/main/scala/parser.scala
+++ b/yamlesque/src/main/scala/YamlParser.scala
@@ -3,7 +3,7 @@ package yamlesque
import annotation.{switch, tailrec}
import scala.collection.mutable.ListBuffer
-object Parser {
+object YamlParser extends (Iterator[Char] => YamlValue) {
sealed trait TokenKind
object TokenKind {
@@ -166,98 +166,94 @@ object Parser {
init()
}
- object Parser {
+ def parse(tokens: Iterator[Token]): YamlValue = {
+ var token0 = tokens.next()
+ var token1 = tokens.next()
- class ParseException(val message: String) extends Exception(message)
-
- def run(tokens: Iterator[Token]): YamlValue = {
- var token0 = tokens.next()
- var token1 = tokens.next()
-
- def readNext(): Unit = {
- token0 = token1
- token1 = tokens.next()
- }
+ def readNext(): Unit = {
+ token0 = token1
+ token1 = tokens.next()
+ }
- def fatal(message: String, token: Token) = {
- val completeMessage =
- s"parse error at line ${token.line}, column ${token.col}: $message"
- throw new ParseException(completeMessage)
- }
+ def fatal(message: String, token: Token) = {
+ val completeMessage =
+ s"parse error at line ${token.line}, column ${token.col}: $message"
+ throw new ParseException(completeMessage)
+ }
- def wrongKind(found: Token, required: TokenKind*) = {
- fatal(
- s"token kind not allowed at this position\n" +
- s" found: ${found.kind}\n" +
- s" required: ${required.mkString(" or ")}\n" +
- " " * found.col + found.value + "\n" +
- " " * found.col + "^",
- found
- )
- }
+ def wrongKind(found: Token, required: TokenKind*) = {
+ fatal(
+ s"token kind not allowed at this position\n" +
+ s" found: ${found.kind}\n" +
+ s" required: ${required.mkString(" or ")}\n" +
+ " " * found.col + found.value + "\n" +
+ " " * found.col + "^",
+ found
+ )
+ }
- def nextSequence() = {
- val startCol = token0.col
- val items = new ListBuffer[YamlValue]
- while (startCol <= token0.col && token0.kind != EOF) {
- token0.kind match {
- case ITEM =>
- readNext()
- items += nextBlock(startCol)
- case _ => wrongKind(token0, ITEM)
- }
+ def nextSequence() = {
+ val startCol = token0.col
+ val items = new ListBuffer[YamlValue]
+ while (startCol <= token0.col && token0.kind != EOF) {
+ token0.kind match {
+ case ITEM =>
+ readNext()
+ items += nextBlock(startCol)
+ case _ => wrongKind(token0, ITEM)
}
- YamlSequence(items.toVector)
}
+ YamlSequence(items.toVector)
+ }
- def nextMapping() = {
- val startCol = token0.col
- val fields = new ListBuffer[(String, YamlValue)]
- while (startCol <= token0.col && token0.kind != EOF) {
- token0.kind match {
- case IDENTIFIER =>
- val key = token0.value
- readNext()
- token0.kind match {
- case MAPPING =>
- readNext()
- val value = nextBlock(startCol)
- fields += key -> value
- case _ => wrongKind(token0, MAPPING)
- }
+ def nextMapping() = {
+ val startCol = token0.col
+ val fields = new ListBuffer[(String, YamlValue)]
+ while (startCol <= token0.col && token0.kind != EOF) {
+ token0.kind match {
+ case IDENTIFIER =>
+ val key = token0.value
+ readNext()
+ token0.kind match {
+ case MAPPING =>
+ readNext()
+ val value = nextBlock(startCol)
+ fields += key -> value
+ case _ => wrongKind(token0, MAPPING)
+ }
- case _ => wrongKind(token0, IDENTIFIER)
- }
+ case _ => wrongKind(token0, IDENTIFIER)
}
- YamlMapping(fields.toMap)
}
+ YamlMapping(fields.toMap)
+ }
- def nextBlock(startCol: Int): YamlValue = {
- if (token0.col < startCol) {
- YamlScalar.Empty
- } else {
- token0.kind match {
- case IDENTIFIER =>
- if (token1.kind == MAPPING && token0.line == token1.line) {
- nextMapping()
- } else {
- val y = YamlScalar(token0.value)
- readNext()
- y
- }
- case ITEM =>
- nextSequence()
- case EOF => YamlScalar.Empty
- case _ => wrongKind(token0, IDENTIFIER, ITEM)
- }
+ def nextBlock(startCol: Int): YamlValue = {
+ if (token0.col < startCol) {
+ YamlScalar.Empty
+ } else {
+ token0.kind match {
+ case IDENTIFIER =>
+ if (token1.kind == MAPPING && token0.line == token1.line) {
+ nextMapping()
+ } else {
+ val y = YamlScalar(token0.value)
+ readNext()
+ y
+ }
+ case ITEM =>
+ nextSequence()
+ case EOF => YamlScalar.Empty
+ case _ => wrongKind(token0, IDENTIFIER, ITEM)
}
}
-
- nextBlock(0)
}
- }
- def parse(data: String): YamlValue = {
- Parser.run(new Scanner(data.toIterator))
+ nextBlock(0)
}
+
+ def apply(data: Iterator[Char]): YamlValue = parse(new Scanner(data))
+
}
+
+class ParseException(val message: String) extends Exception(message)
diff --git a/yamlesque/src/main/scala/package.scala b/yamlesque/src/main/scala/package.scala
new file mode 100644
index 0000000..fdb05d0
--- /dev/null
+++ b/yamlesque/src/main/scala/package.scala
@@ -0,0 +1,7 @@
+package yamlesque
+
+object `package` {
+ implicit class RichString(val str: String) extends AnyVal {
+ def parseYaml: YamlValue = YamlParser(str.toIterator)
+ }
+}
diff --git a/yamlesque/src/main/scala/yamlValues.scala b/yamlesque/src/main/scala/yamlValues.scala
index 3bc4f36..afe8e0b 100644
--- a/yamlesque/src/main/scala/yamlValues.scala
+++ b/yamlesque/src/main/scala/yamlValues.scala
@@ -4,7 +4,7 @@ sealed trait YamlValue {
def print: String = YamlValue.DefaultPrinter(this)
}
object YamlValue {
- val DefaultPrinter = new YamlPrinter(true)
+ val DefaultPrinter = new YamlPrinter(compact = true)
}
case class YamlMapping(fields: Map[String, YamlValue]) extends YamlValue
diff --git a/yamlesque/src/test/scala/ParserTests.scala b/yamlesque/src/test/scala/ParserTests.scala
index 9965bcd..b93b159 100644
--- a/yamlesque/src/test/scala/ParserTests.scala
+++ b/yamlesque/src/test/scala/ParserTests.scala
@@ -4,67 +4,217 @@ import utest._
object ParserTests extends TestSuite {
- val yaml = YamlMapping(
- "key1" -> YamlScalar("value1"),
- "key2" -> YamlMapping(
- "key1" -> YamlScalar("value1"),
- "key2" -> YamlScalar("value1"),
- "key3" -> YamlSequence(
- YamlScalar("a1"),
+ val tests = Tests {
+ "parse empty string" - {
+ "".parseYaml ==> YamlScalar.Empty
+ }
+ "parse simple scalar" - {
+ "hello".parseYaml ==> YamlScalar("hello")
+ }
+ "parse scalar with space" - {
+ "hello world".parseYaml ==> YamlScalar("hello world")
+ }
+ "parse scalar with a colon" - {
+ "hello:world".parseYaml ==> YamlScalar("hello:world")
+ }
+ "parse scalar with a minus" - {
+ "hello-world".parseYaml ==> YamlScalar("hello-world")
+ }
+ "parse scalar starting with a colon" - {
+ ":hello world".parseYaml ==> YamlScalar(":hello world")
+ }
+ "parse scalar starting with a minus" - {
+ "-hello world".parseYaml ==> YamlScalar("-hello world")
+ }
+ "parse empty list" - {
+ "-".parseYaml ==> YamlSequence(YamlScalar.Empty)
+ }
+ "parse a simple list" - {
+ "-\n a\n-\n b\n-\n c".parseYaml ==> YamlSequence(YamlScalar("a"),
+ YamlScalar("b"),
+ YamlScalar("c"))
+ }
+ "parse a simple compact list" - {
+ "- a\n- b\n - c".parseYaml ==> YamlSequence(YamlScalar("a"),
+ YamlScalar("b"),
+ YamlScalar("c"))
+ }
+ "fail to parse a list with a non-item token" - {
+ val e = intercept[ParseException] {
+ "- a\n- b\n -c".parseYaml // -c is missing a space between '-' and 'c'
+ }
+ assert(e.message.contains("token kind"))
+ }
+ "parse a nested list" - {
+ val ls =
+ s"""|- a0
+ |- b0
+ |-
+ | - a1
+ | - b1
+ | -
+ | - a2
+ | - b2
+ |- c0
+ |- - a1
+ | - b1
+ |- - - - a4
+ |""".stripMargin
+ val result = YamlSequence(
+ YamlScalar("a0"),
+ YamlScalar("b0"),
YamlSequence(
YamlScalar("a1"),
- YamlScalar("a2"),
- YamlScalar("a3")
+ YamlScalar("b1"),
+ YamlSequence(
+ YamlScalar("a2"),
+ YamlScalar("b2")
+ )
),
- YamlScalar("a3"),
- YamlMapping(
- "a1" -> YamlScalar("b"),
- "a2" -> YamlScalar("b"),
- "a3" -> YamlScalar("b"),
- "a4" -> YamlScalar("b")
+ YamlScalar("c0"),
+ YamlSequence(
+ YamlScalar("a1"),
+ YamlScalar("b1")
),
- YamlScalar("a4"),
- YamlScalar("a4")
- ),
- "key4" -> YamlScalar("value1"),
- "key5" -> YamlScalar("value1"),
- "key6" -> YamlScalar("value1")
- ),
- "key3" -> YamlScalar("value3")
- )
-
- val string =
- s"""|
- |key1: value1
- |key2:
- | key4: value1
- | key5: value1
- | key1: value1
- | key2: value1
- | key6: value1
- | key3:
- | - a1
- | -
- | - a1
- | - a2
- | - a3
- | - a3
- | -
- | a1: b
- | a2: b
- | a3: b
- | a4: b
- | - a4
- | - a4
- |key3: value3
- |""".stripMargin
-
- val tests = Tests{
- "parse" - {
- // assert(Parser.parse(string) == yaml)
+ YamlSequence(
+ YamlSequence(
+ YamlSequence(
+ YamlScalar("a4")
+ )
+ )
+ )
+ )
+ ls.parseYaml ==> result
+ }
+ "parse a simple mapping" - {
+ "a:\n b".parseYaml ==> YamlMapping("a" -> YamlScalar("b"))
+ }
+ "parse a double mapping" - {
+ "a:\n b\nc:\n d".parseYaml ==> YamlMapping(
+ "a" -> YamlScalar("b"),
+ "c" -> YamlScalar("d")
+ )
+ }
+ "parse a simple compact mapping" - {
+ "a: b".parseYaml ==> YamlMapping("a" -> YamlScalar("b"))
+ }
+ "parse a double compact mapping" - {
+ "a: b\nc: d".parseYaml ==> YamlMapping(
+ "a" -> YamlScalar("b"),
+ "c" -> YamlScalar("d")
+ )
+ }
+ "parse a simple mapping without a value" - {
+ "a:\n".parseYaml ==> YamlMapping(
+ "a" -> YamlScalar("")
+ )
}
- "printandparse" - {
- //assert(Parser.parse(yaml.print) == yaml)
+ "parse a mapping without a value" - {
+ "k1: v1\nk2:\nk3: v3".parseYaml ==> YamlMapping(
+ "k1" -> YamlScalar("v1"),
+ "k2" -> YamlScalar.Empty,
+ "k3" -> YamlScalar("v3")
+ )
+ }
+ "parse a nested mapping" - {
+ val m =
+ s"""|k1:
+ | k11: a
+ | k12: b
+ |k2:
+ | k21:
+ | k31:
+ | k41: a
+ | k22:
+ | b
+ |k3: a
+ |k4: k41: k42: k43: a
+ |""".stripMargin
+ m.parseYaml ==> YamlMapping(
+ "k1" -> YamlMapping(
+ "k11" -> YamlScalar("a"),
+ "k12" -> YamlScalar("b")
+ ),
+ "k2" -> YamlMapping(
+ "k21" -> YamlMapping(
+ "k31" -> YamlMapping(
+ "k41" -> YamlScalar("a")
+ )
+ ),
+ "k22" -> YamlScalar("b")
+ ),
+ "k3" -> YamlScalar("a"),
+ "k4" -> YamlMapping(
+ "k41" -> YamlMapping(
+ "k42" -> YamlMapping(
+ "k43" -> YamlScalar("a")
+ )
+ )
+ )
+ )
+ }
+ "maps and sequences" - {
+ val yaml = YamlMapping(
+ "key1" -> YamlScalar("value1"),
+ "key2" -> YamlMapping(
+ "key1" -> YamlScalar("value1"),
+ "key2" -> YamlScalar("value1"),
+ "key3" -> YamlSequence(
+ YamlScalar("a1"),
+ YamlSequence(
+ YamlScalar("a1"),
+ YamlScalar("a2"),
+ YamlScalar("a3")
+ ),
+ YamlScalar("a3"),
+ YamlMapping(
+ "a1" -> YamlScalar("b"),
+ "a2" -> YamlScalar("b"),
+ "a3" -> YamlScalar("b"),
+ "a4" -> YamlScalar("b")
+ ),
+ YamlScalar("a4"),
+ YamlScalar("a4")
+ ),
+ "key4" -> YamlScalar("value1"),
+ "key5" -> YamlScalar("value1"),
+ "key6" -> YamlScalar("value1")
+ ),
+ "key3" -> YamlScalar("value3")
+ )
+
+ val string =
+ s"""|
+ |key1: value1
+ |key2:
+ | key4:
+ | value1
+ | key5: value1
+ | key1: value1
+ | key2: value1
+ | key6: value1
+ | key3:
+ | - a1
+ | -
+ | - a1
+ | - a2
+ | - a3
+ | - a3
+ | -
+ | a1: b
+ | a2: b
+ | a3: b
+ | a4: b
+ | - a4
+ | - a4
+ |key3: value3
+ |""".stripMargin
+ "parse" - {
+ string.parseYaml ==> yaml
+ }
+ "print and parse" - {
+ yaml.print.parseYaml ==> yaml
+ }
}
}