Skip to content

Commit

Permalink
feat(): implement parser
Browse files Browse the repository at this point in the history
OK, actually copy-paste from earlier gist and polish it a bit ;-)
  • Loading branch information
silmeth committed Oct 13, 2017
1 parent 01bbaba commit 227717a
Showing 1 changed file with 81 additions and 0 deletions.
81 changes: 81 additions & 0 deletions src/main/kotlin/com/github/silmeth/json/SimpleJsonGrammar.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.github.silmeth.json

import com.github.h0tk3y.betterParse.combinators.*
import com.github.h0tk3y.betterParse.grammar.Grammar
import com.github.h0tk3y.betterParse.grammar.parser
import com.github.h0tk3y.betterParse.parser.Parser

object SimpleJsonGrammar : Grammar<Any?>() {
// the regex "[^\\"]*(\\["nrtbf\\][^\\"]*)*" matches:
// " – opening double quote,
// [^\\"]* – any number of not escaped characters, nor double quotes
// (
// \\["nrtbf\\] – backslash followed by special character (\", \n, \r, \\, etc.)
// [^\\"]* – and any number of non-special characters
// )* – repeating as a group any number of times
// " – closing double quote
private val stringLiteral by token("\"[^\\\\\"]*(\\\\[\"nrtbf\\\\][^\\\\\"]*)*\"")

private val whiteSpace by token("\\s+", ignore = true)

// Punctuation and parentheses
private val comma by token(",")
private val colon by token(":")
private val openingBrace by token("\\{")
private val closingBrace by token("\\}")
private val openingBracket by token("\\[")
private val closingBracket by token("\\]")

// Keywords
private val nullToken by token("\\bnull\\b")
private val trueToken by token("\\btrue\\b")
private val falseToken by token("\\bfalse\\b")

// Signs used by numbers
private val integer by token("\\d+")
private val dot by token("\\.")
private val exponent by token("[eE]")
private val minus by token("-")

// Json literal values
private val jsonNull: Parser<Any?> = nullToken asJust null
private val jsonBool: Parser<Boolean> = (trueToken asJust true) or (falseToken asJust false)
private val string: Parser<String> = (stringLiteral use { text.substring(1, text.lastIndex) })
.map { it.replace("\\\"", "\"")
.replace("\\n", "\n")
.replace("\\r", "\r")
.replace("\\t", "\t")
.replace("\\b", "\b")
.replace("\\f", "\u000C")
.replace("\\\\", "\\") }
// Json number literals
private val exponentPart = -exponent and integer
private val floatingPointPart = -dot and optional(integer)
private val onlyFloatingPart = -dot and integer
private val positiveNumber: Parser<Double> = ((integer and optional(floatingPointPart))
.map { (int, floatPart) ->
int.text + (floatPart?.let { ".${it.text}" } ?: "")
} or
(onlyFloatingPart map { ".${it.text}" }) and
optional(exponentPart map { "e${it.text}" }))
.map { (p1, p2) ->
(p1 + (p2 ?: "")).toDouble()
}

private val number: Parser<Double> = (optional(minus) and positiveNumber)
.map { (m, num) -> if (m != null) -num else num }

private val jsonPrimitiveValue: Parser<Any?> = jsonNull or jsonBool or string or number
private val jsonObject: Parser<Map<String, Any?>> = (skip(openingBrace) and
separated(string and skip(colon) and parser(this::jsonValue), comma, true) and
skip(closingBrace))
.map {
it.terms.map { (key, v) -> Pair(key, v) }.toMap()
}
private val jsonArray: Parser<List<Any?>> = (skip(openingBracket) and
separated(parser(this::jsonValue), comma, true) and
skip(closingBracket))
.map { it.terms }
private val jsonValue: Parser<Any?> = jsonPrimitiveValue or jsonObject or jsonArray
override val rootParser = jsonValue
}

0 comments on commit 227717a

Please sign in to comment.