diff options
Diffstat (limited to 'yamlesque/src/main/scala/YamlParser.scala')
-rw-r--r-- | yamlesque/src/main/scala/YamlParser.scala | 258 |
1 files changed, 0 insertions, 258 deletions
diff --git a/yamlesque/src/main/scala/YamlParser.scala b/yamlesque/src/main/scala/YamlParser.scala deleted file mode 100644 index f7a0f9b..0000000 --- a/yamlesque/src/main/scala/YamlParser.scala +++ /dev/null @@ -1,258 +0,0 @@ -package yamlesque - -import annotation.{switch, tailrec} -import scala.collection.mutable.ListBuffer - -object YamlParser extends (Iterator[Char] => YamlValue) { - - sealed trait TokenKind - object TokenKind { - case object EOF extends TokenKind - case object BAD extends TokenKind - case object DOCSTART extends TokenKind - case object DOCEND extends TokenKind - case object MAPPING extends TokenKind - case object ITEM extends TokenKind - case object IDENTIFIER extends TokenKind - case object COMMENT extends TokenKind - } - import TokenKind._ - - case class Token(val kind: TokenKind, value: String = "") { - var line: Int = 0 - var col: Int = 0 - def setPos(line: Int, col: Int): this.type = { - this.col = col - this.line = line - this - } - override def toString() = { - s"($line, $col): " + super.toString - } - } - - object Chars { - final val LF = '\u000A' - final val CR = '\u000D' - final val SU = '\u001A' - - @inline def isSpace(ch: Char): Boolean = ch match { - case ' ' | '\t' => true - case _ => false - } - - @inline def isBlank(ch: Char): Boolean = ch match { - case ' ' | '\t' | CR | LF | SU => true - case _ => false - } - } - - class Scanner(chars: Iterator[Char]) extends Iterator[Token] { - import Chars._ - - private var ch0: Char = 0 - private var ch1: Char = 0 - private var ch2: Char = 0 - private var pos: Long = 0 - private var line: Int = 0 - private var col: Int = 0 - - private def skipChar(): Unit = { - val ch: Char = if (chars.hasNext) { - chars.next() - } else { - SU - } - pos += 1 - col += 1 - ch0 = ch1 - ch1 = ch2 - ch2 = ch - } - private def skipChars(n: Int): Unit = { - var i = 0 - while (i < n) { skipChar(); i += 1 } - } - def init() = { - skipChars(3) - pos = 0 - col = 0 - line = 0 - } - - private var buffer = new StringBuilder() - private def putChar(): Unit = { - buffer.append(ch0) - skipChars(1) - } - private def tokenValue(): String = { - val str = buffer.result() - buffer.clear() - str - } - - private var token: Token = Token(BAD, "not yet initialized") - - @tailrec private def fetchToken(): Unit = { - ch0 match { - case ':' if isBlank(ch1) => - token = Token(MAPPING).setPos(line, col) - skipChars(1) - case '-' if isBlank(ch1) => - token = Token(ITEM).setPos(line, col) - skipChars(1) - case '-' if ch1 == '-' && ch2 == '-' => - token = Token(DOCSTART).setPos(line, col) - skipChars(3) - case '.' if ch1 == '.' && ch2 == '.' => - token = Token(DOCEND).setPos(line, col) - skipChars(3) - case '#' => - val l = line - val c = col - skipChars(1) - while (ch0 != LF && ch0 != SU) { - putChar() - } - token = Token(COMMENT, tokenValue()).setPos(l, c) - buffer.clear() - case c if isSpace(c) => - skipChars(1) - fetchToken() - case LF => - skipChars(1) - col = 0 - line += 1 - fetchToken() - case CR => - skipChars(1) - if (ch0 == LF) { - skipChars(1) - } - col = 0 - line += 1 - fetchToken() - case SU => - token = Token(EOF).setPos(line, col) - skipChars(1) - case _ => fetchScalar() - } - } - - private def fetchScalar(): Unit = { - val l = line - val c = col - @tailrec def fetchRest(): Unit = ch0 match { - case ':' if isBlank(ch1) => - token = Token(IDENTIFIER, tokenValue()) - case LF => - token = Token(IDENTIFIER, tokenValue()) - case SU => - token = Token(IDENTIFIER, tokenValue()) - case c => - putChar() - fetchRest() - } - fetchRest() - token.setPos(l, c) - } - - override def hasNext: Boolean = true - override def next(): Token = { - fetchToken() - token - } - init() - } - - def parse(tokens: Iterator[Token]): YamlValue = { - var token0 = tokens.next() - var 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 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 + 1) - case _ => wrongKind(token0, ITEM) - } - } - 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 + 1) - fields += key -> value - case _ => wrongKind(token0, MAPPING) - } - - case _ => wrongKind(token0, IDENTIFIER) - } - } - YamlMapping(fields.toMap) - } - - def nextBlock(startCol: Int): YamlValue = { - if (token0.col < startCol) { - YamlEmpty - } 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 => YamlEmpty - case _ => wrongKind(token0, IDENTIFIER, ITEM) - } - } - } - - nextBlock(0) - } - - def apply(data: Iterator[Char]): YamlValue = parse(new Scanner(data)) - -} - -class ParseException(val message: String) extends Exception(message) |