MST expression
This commit is contained in:
parent
047af8c172
commit
2751cee926
@ -13,4 +13,9 @@ kotlin.sourceSets {
|
|||||||
implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha-3")
|
implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha-3")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
jvmMain{
|
||||||
|
dependencies{
|
||||||
|
implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
62
kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt
Normal file
62
kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package scientifik.kmath.ast
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.NumericAlgebra
|
||||||
|
import scientifik.kmath.operations.RealField
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Mathematical Syntax Tree node for mathematical expressions
|
||||||
|
*/
|
||||||
|
sealed class MST {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node containing unparsed string
|
||||||
|
*/
|
||||||
|
data class Singular(val value: String) : MST()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node containing a number
|
||||||
|
*/
|
||||||
|
data class Numeric(val value: Number) : MST()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node containing an unary operation
|
||||||
|
*/
|
||||||
|
data class Unary(val operation: String, val value: MST) : MST() {
|
||||||
|
companion object {
|
||||||
|
const val ABS_OPERATION = "abs"
|
||||||
|
//TODO add operations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node containing binary operation
|
||||||
|
*/
|
||||||
|
data class Binary(val operation: String, val left: MST, val right: MST) : MST() {
|
||||||
|
companion object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO add a function with positional arguments
|
||||||
|
|
||||||
|
//TODO add a function with named arguments
|
||||||
|
|
||||||
|
fun <T> NumericAlgebra<T>.evaluate(node: MST): T {
|
||||||
|
return when (node) {
|
||||||
|
is MST.Numeric -> number(node.value)
|
||||||
|
is MST.Singular -> raw(node.value)
|
||||||
|
is MST.Unary -> unaryOperation(node.operation, evaluate(node.value))
|
||||||
|
is MST.Binary -> when {
|
||||||
|
node.left is MST.Numeric && node.right is MST.Numeric -> {
|
||||||
|
val number = RealField.binaryOperation(
|
||||||
|
node.operation,
|
||||||
|
node.left.value.toDouble(),
|
||||||
|
node.right.value.toDouble()
|
||||||
|
)
|
||||||
|
number(number)
|
||||||
|
}
|
||||||
|
node.left is MST.Numeric -> leftSideNumberOperation(node.operation, node.left.value, evaluate(node.right))
|
||||||
|
node.right is MST.Numeric -> rightSideNumberOperation(node.operation, evaluate(node.left), node.right.value)
|
||||||
|
else -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package scientifik.kmath.ast
|
||||||
|
|
||||||
|
import scientifik.kmath.expressions.Expression
|
||||||
|
import scientifik.kmath.operations.NumericAlgebra
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The expression evaluates MST on-flight
|
||||||
|
*/
|
||||||
|
class MSTExpression<T>(val algebra: NumericAlgebra<T>, val mst: MST) : Expression<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Substitute algebra raw value
|
||||||
|
*/
|
||||||
|
private inner class InnerAlgebra(val arguments: Map<String, T>) : NumericAlgebra<T> by algebra {
|
||||||
|
override fun raw(value: String): T = arguments[value] ?: super.raw(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invoke(arguments: Map<String, T>): T = InnerAlgebra(arguments).evaluate(mst)
|
||||||
|
}
|
@ -1,61 +0,0 @@
|
|||||||
package scientifik.kmath.ast
|
|
||||||
|
|
||||||
import scientifik.kmath.operations.NumericAlgebra
|
|
||||||
import scientifik.kmath.operations.RealField
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A syntax tree node for mathematical expressions
|
|
||||||
*/
|
|
||||||
sealed class MathSyntaxTree
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A node containing unparsed string
|
|
||||||
*/
|
|
||||||
data class SingularNode(val value: String) : MathSyntaxTree()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A node containing a number
|
|
||||||
*/
|
|
||||||
data class NumberNode(val value: Number) : MathSyntaxTree()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A node containing an unary operation
|
|
||||||
*/
|
|
||||||
data class UnaryNode(val operation: String, val value: MathSyntaxTree) : MathSyntaxTree() {
|
|
||||||
companion object {
|
|
||||||
const val ABS_OPERATION = "abs"
|
|
||||||
//TODO add operations
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A node containing binary operation
|
|
||||||
*/
|
|
||||||
data class BinaryNode(val operation: String, val left: MathSyntaxTree, val right: MathSyntaxTree) : MathSyntaxTree() {
|
|
||||||
companion object
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO add a function with positional arguments
|
|
||||||
|
|
||||||
//TODO add a function with named arguments
|
|
||||||
|
|
||||||
fun <T> NumericAlgebra<T>.compile(node: MathSyntaxTree): T {
|
|
||||||
return when (node) {
|
|
||||||
is NumberNode -> number(node.value)
|
|
||||||
is SingularNode -> raw(node.value)
|
|
||||||
is UnaryNode -> unaryOperation(node.operation, compile(node.value))
|
|
||||||
is BinaryNode -> when {
|
|
||||||
node.left is NumberNode && node.right is NumberNode -> {
|
|
||||||
val number = RealField.binaryOperation(
|
|
||||||
node.operation,
|
|
||||||
node.left.value.toDouble(),
|
|
||||||
node.right.value.toDouble()
|
|
||||||
)
|
|
||||||
number(number)
|
|
||||||
}
|
|
||||||
node.left is NumberNode -> leftSideNumberOperation(node.operation, node.left.value, compile(node.right))
|
|
||||||
node.right is NumberNode -> rightSideNumberOperation(node.operation, compile(node.left), node.right.value)
|
|
||||||
else -> binaryOperation(node.operation, compile(node.left), compile(node.right))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,4 +20,4 @@ inline fun <reified T : Any, A : Algebra<T>> A.asm(
|
|||||||
block: AsmExpressionAlgebra<T, A>.() -> AsmExpression<T>
|
block: AsmExpressionAlgebra<T, A>.() -> AsmExpression<T>
|
||||||
): Expression<T> = TODO()
|
): Expression<T> = TODO()
|
||||||
|
|
||||||
inline fun <reified T : Any, A : Algebra<T>> A.asm(ast: MathSyntaxTree): Expression<T> = asm { compile(ast) }
|
inline fun <reified T : Any, A : Algebra<T>> A.asm(ast: MST): Expression<T> = asm { evaluate(ast) }
|
56
kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt
Normal file
56
kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package scientifik.kmath.ast
|
||||||
|
|
||||||
|
import com.github.h0tk3y.betterParse.combinators.*
|
||||||
|
import com.github.h0tk3y.betterParse.grammar.Grammar
|
||||||
|
import com.github.h0tk3y.betterParse.grammar.parseToEnd
|
||||||
|
import com.github.h0tk3y.betterParse.grammar.parser
|
||||||
|
import com.github.h0tk3y.betterParse.grammar.tryParseToEnd
|
||||||
|
import com.github.h0tk3y.betterParse.parser.ParseResult
|
||||||
|
import com.github.h0tk3y.betterParse.parser.Parser
|
||||||
|
import scientifik.kmath.operations.FieldOperations
|
||||||
|
import scientifik.kmath.operations.PowerOperations
|
||||||
|
import scientifik.kmath.operations.RingOperations
|
||||||
|
import scientifik.kmath.operations.SpaceOperations
|
||||||
|
|
||||||
|
private object ArithmeticsEvaluator : Grammar<MST>() {
|
||||||
|
val num by token("-?[\\d.]+(?:[eE]-?\\d+)?")
|
||||||
|
val lpar by token("\\(")
|
||||||
|
val rpar by token("\\)")
|
||||||
|
val mul by token("\\*")
|
||||||
|
val pow by token("\\^")
|
||||||
|
val div by token("/")
|
||||||
|
val minus by token("-")
|
||||||
|
val plus by token("\\+")
|
||||||
|
val ws by token("\\s+", ignore = true)
|
||||||
|
|
||||||
|
val number: Parser<MST> by num use { MST.Numeric(text.toDouble()) }
|
||||||
|
|
||||||
|
val term: Parser<MST> by number or
|
||||||
|
(skip(minus) and parser(this::term) map { MST.Unary(SpaceOperations.MINUS_OPERATION, it) }) or
|
||||||
|
(skip(lpar) and parser(this::rootParser) and skip(rpar))
|
||||||
|
|
||||||
|
val powChain by leftAssociative(term, pow) { a, _, b ->
|
||||||
|
MST.Binary(PowerOperations.POW_OPERATION, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
val divMulChain: Parser<MST> by leftAssociative(powChain, div or mul use { type }) { a, op, b ->
|
||||||
|
if (op == div) {
|
||||||
|
MST.Binary(FieldOperations.DIV_OPERATION, a, b)
|
||||||
|
} else {
|
||||||
|
MST.Binary(RingOperations.TIMES_OPERATION, a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val subSumChain: Parser<MST> by leftAssociative(divMulChain, plus or minus use { type }) { a, op, b ->
|
||||||
|
if (op == plus) {
|
||||||
|
MST.Binary(SpaceOperations.PLUS_OPERATION, a, b)
|
||||||
|
} else {
|
||||||
|
MST.Binary(SpaceOperations.MINUS_OPERATION, a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val rootParser: Parser<MST> by subSumChain
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.tryParseMath(): ParseResult<MST> = ArithmeticsEvaluator.tryParseToEnd(this)
|
||||||
|
fun String.parseMath(): MST = ArithmeticsEvaluator.parseToEnd(this)
|
@ -0,0 +1,17 @@
|
|||||||
|
package scietifik.kmath.ast
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import scientifik.kmath.ast.evaluate
|
||||||
|
import scientifik.kmath.ast.parseMath
|
||||||
|
import scientifik.kmath.operations.Complex
|
||||||
|
import scientifik.kmath.operations.ComplexField
|
||||||
|
|
||||||
|
internal class ParserTest{
|
||||||
|
@Test
|
||||||
|
fun parsedExpression(){
|
||||||
|
val mst = "2+2*(2+2)".parseMath()
|
||||||
|
val res = ComplexField.evaluate(mst)
|
||||||
|
assertEquals(Complex(10.0,0.0), res)
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,12 @@ import scientifik.kmath.operations.Algebra
|
|||||||
*/
|
*/
|
||||||
interface Expression<T> {
|
interface Expression<T> {
|
||||||
operator fun invoke(arguments: Map<String, T>): T
|
operator fun invoke(arguments: Map<String, T>): T
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
operator fun <T> invoke(block: (Map<String, T>) -> T): Expression<T> = object : Expression<T> {
|
||||||
|
override fun invoke(arguments: Map<String, T>): T = block(arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun <T> Expression<T>.invoke(vararg pairs: Pair<String, T>): T = invoke(mapOf(*pairs))
|
operator fun <T> Expression<T>.invoke(vararg pairs: Pair<String, T>): T = invoke(mapOf(*pairs))
|
||||||
|
@ -66,8 +66,7 @@ open class FunctionalExpressionField<T>(
|
|||||||
val field: Field<T>
|
val field: Field<T>
|
||||||
) : Field<Expression<T>>, ExpressionAlgebra<T, Expression<T>>, FunctionalExpressionSpace<T>(field) {
|
) : Field<Expression<T>>, ExpressionAlgebra<T, Expression<T>>, FunctionalExpressionSpace<T>(field) {
|
||||||
|
|
||||||
override val one: Expression<T>
|
override val one: Expression<T> = ConstantExpression(this.field.one)
|
||||||
get() = const(this.field.one)
|
|
||||||
|
|
||||||
fun const(value: Double): Expression<T> = const(field.run { one * value })
|
fun const(value: Double): Expression<T> = const(field.run { one * value })
|
||||||
|
|
||||||
@ -80,4 +79,16 @@ open class FunctionalExpressionField<T>(
|
|||||||
|
|
||||||
operator fun T.times(arg: Expression<T>) = arg * this
|
operator fun T.times(arg: Expression<T>) = arg * this
|
||||||
operator fun T.div(arg: Expression<T>) = arg / this
|
operator fun T.div(arg: Expression<T>) = arg / this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a functional expression on this [Space]
|
||||||
|
*/
|
||||||
|
fun <T> Space<T>.buildExpression(block: FunctionalExpressionSpace<T>.() -> Expression<T>): Expression<T> =
|
||||||
|
FunctionalExpressionSpace(this).run(block)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a functional expression on this [Field]
|
||||||
|
*/
|
||||||
|
fun <T> Field<T>.buildExpression(block: FunctionalExpressionField<T>.() -> Expression<T>): Expression<T> =
|
||||||
|
FunctionalExpressionField(this).run(block)
|
@ -50,6 +50,12 @@ object ComplexField : ExtendedField<Complex> {
|
|||||||
operator fun Complex.minus(d: Double) = add(this, -d.toComplex())
|
operator fun Complex.minus(d: Double) = add(this, -d.toComplex())
|
||||||
|
|
||||||
operator fun Double.times(c: Complex) = Complex(c.re * this, c.im * this)
|
operator fun Double.times(c: Complex) = Complex(c.re * this, c.im * this)
|
||||||
|
|
||||||
|
override fun raw(value: String): Complex = if (value == "i") {
|
||||||
|
i
|
||||||
|
} else {
|
||||||
|
super.raw(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user