Skip to content
This repository has been archived by the owner on Feb 13, 2021. It is now read-only.

Commit

Permalink
feat: add contparse
Browse files Browse the repository at this point in the history
  • Loading branch information
makenowjust committed Sep 17, 2020
1 parent de0e6c9 commit 72fed25
Show file tree
Hide file tree
Showing 10 changed files with 917 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
This repository contains the following libraries:

- [`parsers-common`](modules/parsers-common): Common utilities for parser combinator.
- [`parsers-contparse`](modules/parsers-contparse): A parser combinator library with continuation passing style.
- [`parsers-funcparse`](modules/parsers-funcparse): A basic functional parser combinator library.
- [`parsers-inlineparse`](modules/parsers-inlineparse): A faster parser combinator library with mutable state & inline expansion.
- [`parsers-stackparse`](modules/parsers-stackparse): A stackless parser combinator library.
Expand Down
13 changes: 11 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ ThisBuild / scalafixDependencies += "com.github.vovapolu" %% "scaluzzi" % "0.1.1
lazy val root = project
.in(file("."))
.settings(publish / skip := true)
.aggregate(bench, common, funcparse, inlineparse, stackparse)
.aggregate(bench, common, contparse, funcparse, inlineparse, stackparse)

lazy val bench = project
.in(file("modules/bench"))
Expand All @@ -45,7 +45,7 @@ lazy val bench = project
libraryDependencies += "io.monix" %% "minitest" % "2.8.2" % Test,
testFrameworks += new TestFramework("minitest.runner.Framework")
)
.dependsOn(funcparse, inlineparse, stackparse)
.dependsOn(contparse, funcparse, inlineparse, stackparse)
.enablePlugins(JmhPlugin)

def moduleSettings(moduleName: String) =
Expand All @@ -66,6 +66,15 @@ lazy val common = project
.in(file("modules/parsers-common"))
.settings(moduleSettings("common"))

lazy val contparse = project
.in(file("modules/parsers-contparse"))
.settings(
moduleSettings("contparse"),
// Dependencies:
libraryDependencies += "com.lihaoyi" %% "sourcecode" % "0.2.1"
)
.dependsOn(common)

lazy val funcparse = project
.in(file("modules/parsers-funcparse"))
.settings(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ class Bench {
def measureAtto(): atto.ParseResult[JSON] =
AttoJSONParser.parse(Bench.source)

@Benchmark
def measureContparse(): contparse.Parsed[JSON] =
ContparseJSONParser.parse(Bench.source)

@Benchmark
def measureFastparse(): fastparse.Parsed[JSON] =
FastparseJSONParser.parse(Bench.source)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package codes.quine.labo.parsers
package bench

import scala.annotation.switch

import contparse._
import JSON._

object ContparseJSONParser {
val space: P[Unit] = CharsWhileIn(" \r\n", 0)

val json: P[JSON] =
P((number | string | `null` | `true` | `false` | array | `object`) ~ space)

val entry: P[JSON] = space ~ json ~ End

val digits: P[Unit] = CharsWhileIn("0123456789")
val exponent: P[Unit] = CharIn("eE") ~ CharIn("+-").? ~ digits
val fractional: P[Unit] = "." ~ digits
val integral: P[Unit] = "0" | CharIn("123456789") ~ digits.?

val number: P[JSON] =
(CharIn("+-").? ~ integral ~ fractional.? ~ exponent.?).!.map(s => JSONNumber(s.toDouble)).named("<number>")

val `null`: P[JSON] = "null" ~ Pass(JSONNull)
val `true`: P[JSON] = "true" ~ Pass(JSONBoolean(true))
val `false`: P[JSON] = "false" ~ Pass(JSONBoolean(false))

val hexDigit: P[Unit] = CharIn("0123456789abcdefABCDEF")
val unicodeEscape: P[Char] = "u" ~ hexDigit.count(4).!.map(s => Integer.parseInt(s, 16).toChar)
val simpleEscape: P[Char] =
CharIn("\"\\/bfnrt").!.map(s =>
(s.charAt(0): @switch) match {
case 'b' => '\b'
case 'f' => '\f'
case 'n' => '\n'
case 'r' => '\r'
case 't' => '\t'
case c => c
}
)
val escape: P[Char] = "\\" ~/ (simpleEscape | unicodeEscape)
val stringContent: P[String] = CharsWhile(c => c != '"' && c != '\\').! | escape.map(String.valueOf(_))
val key: P[String] = ("\"" ~/ stringContent.rep.map(_.mkString) ~ "\"").named("<string>")
val string: P[JSON] = key.map(JSONString(_))

val arrayContent: P[Seq[JSON]] =
(json ~ ("," ~ space ~/ json).rep).?.map {
case None => Seq.empty
case Some((x, xs)) => x +: xs
}
val array: P[JSON] = "[" ~ space ~/ arrayContent.map(JSONArray(_)) ~ "]"

val pair: P[(String, JSON)] = key ~ space ~ ":" ~ space ~ json
val objectContent: P[Seq[(String, JSON)]] =
(pair ~ ("," ~ space ~/ pair).rep).?.map {
case None => Seq.empty
case Some((k, v, xs)) => (k, v) +: xs
}
val `object`: P[JSON] = "{" ~ space ~/ objectContent.map(JSONObject(_)) ~ "}"

def parse(input: String): Parsed[JSON] =
contparse.parse(input, entry)
}
3 changes: 3 additions & 0 deletions modules/parsers-contparse/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# parsers-contparse

> A parser combinator library with continuation passing style.
Loading

0 comments on commit 72fed25

Please sign in to comment.