diff --git a/README.md b/README.md index 4239ab2..34586d7 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,132 @@ # Karslet +[![Release](https://jitpack.io/v/com.github.mikaelhg/kotlin-peg-dsl.svg)] +(https://jitpack.io/#com.github.mikaelhg/kotlin-peg-dsl) + A Kotlin domain specific language (DSL) and library for easily creating recursive descent parsers in the [parsing expression grammar (PEG)][1] tradition. A work in progress. +## Usage + +```kotlin +import java.nio.CharBuffer + +import io.mikael.karslet.Karslet + +data class PxKeyword(val keyword: String, val language: String?, val specifiers: List?) + +data class PxValue(val numberValue: Long?, val stringValue: String?, val listValue: List?) + +typealias PxRow = Pair + +class Usage { + + private fun stringOrList() = Karslet.sequence> { + + character('"') + val first = characters(min = 0) { it != '"' } + character('"') + + val last = zeroOrMore> { + val state = mutableListOf() + beforeAttempt { state.clear() } + + character(',') + character('"') + val current = characters(min = 0) { it != '"' } + character('"') + + onIteration { state += current.value() } + onSuccess { state } + } + + onSuccess { listOf(first.value()) + last.value() } + } + + private fun keywordLanguage() = Karslet.optional { + + var state: String? = null + beforeAttempt { state = null } + + character('[') + val lang = characters(min = 2, max = 2) { it.isLetter() } + character(']') + + onIteration { state = lang.value() } + onSuccess { state } + } + + private fun keywordSpecifiers() = Karslet.optional?> { + + var state: List? = null + beforeAttempt { state = null } + + character('(') + val strings = include(stringOrList()) + character(')') + + onIteration { state = strings.value() } + onSuccess { state } + } + + private fun pxKeyword() = Karslet.sequence { + val kw = characters(min = 1) { it !in arrayOf('[', '(', '=') } + val lang = include(keywordLanguage()) + val spec = include(keywordSpecifiers()) + + onSuccess { PxKeyword(kw.value(), lang.value(), spec.value()) } + } + + private fun pxValue() = Karslet.choice { + val strings = sequence?> { + val x = include(stringOrList()) + character(';') + onSuccess { x.value() } + } + val numbers = sequence { + val x = characters(min = 1) { it.isDigit() } + character(';') + onSuccess { x.value().toLongOrNull() } + } + val letters = sequence { + val x = characters(min = 1) { it.isLetterOrDigit() } + character(';') + onSuccess { x.value() } + } + onSuccess { PxValue(numbers.value(), letters.value(), strings.value()) } + } + + @Test + fun pxParserDemo() { + + val parser = Karslet.oneOrMore> { + val state = mutableListOf() + + val row = sequence { + val k = include(pxKeyword()) + character('=') + val v = include(pxValue()) + whitespace() + + onSuccess { PxRow(k.value(), v.value()) } + } + + beforeAttempt { state.clear() } + onIteration { state += row.value() } + onSuccess { state } + } + + TestData.rows.forEach { row -> + val success = parser.parse(CharBuffer.wrap(row)) + println("$success ${parser.value()}") + } + + } + +} + +``` + [1]: https://en.wikipedia.org/wiki/Parsing_expression_grammar diff --git a/build.gradle.kts b/build.gradle.kts index c3f88ae..412945e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } group = "io.mikael.karslet" -version = "0.0.1-SNAPSHOT" +version = "0.1.0" java { toolchain { @@ -19,7 +19,7 @@ repositories { dependencies { implementation(enforcedPlatform(kotlin("bom"))) - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + implementation(kotlin("stdlib-jdk8")) testImplementation(enforcedPlatform("org.junit:junit-bom:5.7.2")) testImplementation("org.junit.jupiter:junit-jupiter-api") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") @@ -34,7 +34,6 @@ tasks.withType { tasks.withType { kotlinOptions { - freeCompilerArgs = listOf("-Xjsr305=strict") jvmTarget = "11" } } diff --git a/src/main/kotlin/io/mikael/karslet/Karslet.kt b/src/main/kotlin/io/mikael/karslet/Karslet.kt index 9e50b0c..b63ab6a 100644 --- a/src/main/kotlin/io/mikael/karslet/Karslet.kt +++ b/src/main/kotlin/io/mikael/karslet/Karslet.kt @@ -8,6 +8,30 @@ const val MAX_REPEATS = Integer.MAX_VALUE /** * Karslet is a Parsing Expression Grammar (PEG) domain specific language (DSL) library. + * + * ```kotlin + * val rule = Karslet.sequence> { + * character('"') + * val first = characters(min = 0) { it != '"' } + * character('"') + * + * val last = zeroOrMore> { + * val state = mutableListOf() + * beforeAttempt { state.clear() } + * + * character(',') + * character('"') + * val current = characters(min = 0) { it != '"' } + * character('"') + * + * onIteration { state += current.value() } + * onSuccess { state } + * + * } + + * onSuccess { listOf(first.value()) + last.value() } + * } + * ``` */ object Karslet { diff --git a/src/test/kotlin/io/mikael/karslet/tests.kt b/src/test/kotlin/io/mikael/karslet/tests.kt index 008e31b..4de2947 100644 --- a/src/test/kotlin/io/mikael/karslet/tests.kt +++ b/src/test/kotlin/io/mikael/karslet/tests.kt @@ -89,7 +89,7 @@ class Demos { @Test fun pxParserDemo() { - val parser = Karslet.zeroOrMore> { + val parser = Karslet.oneOrMore> { val state = mutableListOf() val row = sequence {