[orx-expression-evaluator] Switch to antlr-kotlin for parser generation and make it a common kotlin module

This commit is contained in:
Edwin Jakobs
2024-01-05 12:40:59 +01:00
parent a407824bae
commit 456596aba7
21 changed files with 101 additions and 63 deletions

View File

@@ -19,6 +19,7 @@ libfreenect = "0.5.7-1.5.9"
librealsense = "2.53.1-1.5.9"
gson = "2.10.1"
antlr = "4.13.1"
antlrKotlin = "0.1.0-RC14"
tensorflow = "0.5.0"
minim = "2.2.2"
netty = "4.1.92.Final"
@@ -76,6 +77,7 @@ javaosc-core = { group = "com.illposed.osc", name = "javaosc-core", version.ref
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
antlr-core = { group = "org.antlr", name = "antlr4", version.ref = "antlr" }
antlr-runtime = { group = "org.antlr", name = "antlr4-runtime", version.ref = "antlr" }
antlr-kotlin-runtime = { group = "com.strumenta", name = "antlr-kotlin-runtime", version.ref = "antlrKotlin" }
jupiter-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junitJupiter" }
jupiter-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junitJupiter" }
@@ -87,7 +89,7 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi
nebula-release = { id = "nebula.release", version.ref = "nebulaRelease" }
gradle-nexus-publish = { id = "io.github.gradle-nexus.publish-plugin", version.ref = "gradleNexusPublish" }
kotest-multiplatform = { id = "io.kotest.multiplatform", version.ref = "kotest" }
antlr-kotlin = { id = "com.strumenta.antlr-kotlin", version.ref = "antlrKotlin" }
[bundles]
jupiter = ["jupiter-api", "jupiter-engine"]

View File

@@ -0,0 +1,66 @@
import com.strumenta.antlrkotlin.gradle.AntlrKotlinTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
org.openrndr.extra.convention.`kotlin-multiplatform`
alias(libs.plugins.antlr.kotlin)
}
val generateKotlinGrammarSource = tasks.register<AntlrKotlinTask>("generateKotlinGrammarSource") {
dependsOn("cleanGenerateKotlinGrammarSource")
// ANTLR .g4 files are under {example-project}/antlr
// Only include *.g4 files. This allows tools (e.g., IDE plugins)
// to generate temporary files inside the base path
source = fileTree(layout.projectDirectory.dir("src/commonMain/antlr")) {
include("**/*.g4")
}
// We want the generated source files to have this package name
val pkgName = "org.openrndr.expressions.parser"
packageName = pkgName
// We want visitors alongside listeners.
// The Kotlin target language is implicit, as is the file encoding (UTF-8)
arguments = listOf("-visitor")
// Generated files are outputted inside build/generatedAntlr/{package-name}
val outDir = "generatedAntlr/${pkgName.replace(".", "/")}"
outputDirectory = layout.buildDirectory.dir(outDir).get().asFile
}
tasks.withType<KotlinCompile> {
kotlinOptions.freeCompilerArgs = listOf("-opt-in=kotlin.RequiresOptIn")
}
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation(libs.antlr.kotlin.runtime)
implementation(libs.openrndr.application)
implementation(libs.openrndr.math)
implementation(libs.kotlin.coroutines)
implementation(project(":orx-property-watchers"))
implementation(project(":orx-noise"))
}
kotlin {
srcDir(layout.buildDirectory.dir("generatedAntlr"))
}
}
val jvmDemo by getting {
dependencies {
implementation(project(":orx-jvm:orx-gui"))
}
}
val jvmTest by getting {
dependencies {
implementation(libs.kluent)
}
}
}
}
tasks.withType<KotlinCompile> {
dependsOn(generateKotlinGrammarSource)
}

View File

@@ -1,6 +1,7 @@
package org.openrndr.extra.expressions
import org.antlr.v4.runtime.tree.ParseTreeWalker
import org.antlr.v4.kotlinruntime.tree.ParseTreeWalker
/**
* Compile a (Double)->Double function from an expression string

View File

@@ -1,11 +1,13 @@
package org.openrndr.extra.expressions
import KeyLangLexer
import KeyLangParser
import KeyLangParserBaseListener
import org.antlr.v4.runtime.*
import org.antlr.v4.runtime.tree.ParseTreeWalker
import org.antlr.v4.runtime.tree.TerminalNode
import org.antlr.v4.kotlinruntime.*
import org.antlr.v4.kotlinruntime.tree.ParseTreeWalker
import org.antlr.v4.kotlinruntime.tree.TerminalNode
import org.openrndr.expressions.parser.KeyLangLexer
import org.openrndr.expressions.parser.KeyLangParser
import org.openrndr.expressions.parser.KeyLangParserBaseListener
import org.openrndr.extra.noise.uniform
import org.openrndr.math.*
@@ -84,11 +86,11 @@ internal class ExpressionListener(
val right = doubleStack.pop()
val left = doubleStack.pop()
val result = when (val operator = ctx.operator?.type) {
KeyLangParser.PLUS -> left + right
KeyLangParser.MINUS -> left - right
KeyLangParser.ASTERISK -> left * right
KeyLangParser.DIVISION -> left / right
KeyLangParser.PERCENTAGE -> mod(left, right)
KeyLangLexer.Tokens.PLUS.id -> left + right
KeyLangParser.Tokens.MINUS.id -> left - right
KeyLangParser.Tokens.ASTERISK.id -> left * right
KeyLangParser.Tokens.DIVISION.id -> left / right
KeyLangParser.Tokens.PERCENTAGE.id -> mod(left, right)
else -> error("operator '$operator' not implemented")
}
doubleStack.push(result)
@@ -103,10 +105,10 @@ internal class ExpressionListener(
val left = doubleStack.pop()
val right = doubleStack.pop()
val result = when (val operator = ctx.operator?.type) {
KeyLangParser.PLUS -> left + right
KeyLangParser.MINUS -> right - left
KeyLangParser.ASTERISK -> left * right
KeyLangParser.DIVISION -> left / right
KeyLangParser.Tokens.PLUS.id -> left + right
KeyLangParser.Tokens.MINUS.id -> right - left
KeyLangParser.Tokens.ASTERISK.id -> left * right
KeyLangParser.Tokens.DIVISION.id -> left / right
else -> error("operator '$operator' not implemented")
}
doubleStack.push(result)
@@ -245,13 +247,13 @@ internal class ExpressionListener(
override fun visitTerminal(node: TerminalNode) {
val type = node.symbol?.type
if (type == KeyLangParser.INTLIT) {
if (type == KeyLangParser.Tokens.INTLIT.id) {
doubleStack.push(node.text.toDouble())
}
if (type == KeyLangParser.DECLIT) {
if (type == KeyLangParser.Tokens.DECLIT.id) {
doubleStack.push(node.text.toDouble())
}
if (type == KeyLangParser.ID) {
if (type == KeyLangParser.Tokens.ID.id) {
val name = node.text.replace("`", "")
@Suppress("DIVISION_BY_ZERO")
when (val idType = idTypeStack.pop()) {
@@ -409,11 +411,11 @@ fun evaluateExpression(
parser.removeErrorListeners()
parser.addErrorListener(object : BaseErrorListener() {
override fun syntaxError(
recognizer: Recognizer<*, *>?,
recognizer: Recognizer<*, *>,
offendingSymbol: Any?,
line: Int,
charPositionInLine: Int,
msg: String?,
msg: String,
e: RecognitionException?
) {
throw ExpressionException("parser error in expression: '$expression'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]")
@@ -440,11 +442,11 @@ fun compileExpression(
parser.removeErrorListeners()
parser.addErrorListener(object : BaseErrorListener() {
override fun syntaxError(
recognizer: Recognizer<*, *>?,
recognizer: Recognizer<*, *>,
offendingSymbol: Any?,
line: Int,
charPositionInLine: Int,
msg: String?,
msg: String,
e: RecognitionException?
) {
throw ExpressionException("parser error in expression: '$expression'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]")
@@ -470,11 +472,11 @@ internal fun expressionRoot(expression: String): KeyLangParser.KeyLangFileContex
parser.removeErrorListeners()
parser.addErrorListener(object : BaseErrorListener() {
override fun syntaxError(
recognizer: Recognizer<*, *>?,
recognizer: Recognizer<*, *>,
offendingSymbol: Any?,
line: Int,
charPositionInLine: Int,
msg: String?,
msg: String,
e: RecognitionException?
) {
throw ExpressionException("parser error in expression: '$expression'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]")

View File

@@ -1,33 +0,0 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
org.openrndr.extra.convention.`kotlin-jvm`
antlr
}
tasks.generateGrammarSource {
maxHeapSize = "64m"
arguments.addAll(listOf("-visitor", "-long-messages"))
}
tasks.withType<KotlinCompile> {
kotlinOptions.freeCompilerArgs = listOf("-opt-in=kotlin.RequiresOptIn")
}
dependencies {
antlr(libs.antlr.core)
implementation(libs.antlr.runtime)
implementation(libs.openrndr.application)
implementation(libs.openrndr.math)
implementation(libs.kotlin.coroutines)
implementation(project(":orx-property-watchers"))
implementation(project(":orx-noise"))
testImplementation(libs.kluent)
demoImplementation(project(":orx-jvm:orx-gui"))
}
tasks.getByName("compileKotlin").dependsOn("generateGrammarSource")
tasks.getByName("compileDemoKotlin").dependsOn("generateDemoGrammarSource")
tasks.getByName("compileTestKotlin").dependsOn("generateTestGrammarSource")
tasks.getByName("sourcesJar").dependsOn("generateGrammarSource")

View File

@@ -15,7 +15,7 @@ dependencies {
implementation(libs.kotlin.reflect)
implementation(project(":orx-noise"))
implementation(project(":orx-easing"))
api(project(":orx-jvm:orx-expression-evaluator"))
api(project(":orx-expression-evaluator"))
demoImplementation(project(":orx-jvm:orx-panel"))
testImplementation(libs.kluent)
}

View File

@@ -14,7 +14,7 @@ tasks.test {
}
dependencies {
implementation(project(":orx-jvm:orx-expression-evaluator"))
implementation(project(":orx-expression-evaluator"))
implementation(libs.openrndr.application)
implementation(libs.openrndr.math)
implementation(libs.kotlin.coroutines)

View File

@@ -27,7 +27,7 @@ include(
"orx-jvm:orx-dnk3",
"orx-easing",
"orx-envelopes",
"orx-jvm:orx-expression-evaluator",
"orx-expression-evaluator",
"orx-jvm:orx-file-watcher",
"orx-parameters",
"orx-fx",