{"id":25477003,"url":"https://github.com/notkamui/keval","last_synced_at":"2025-11-06T03:30:20.869Z","repository":{"id":45332055,"uuid":"333832523","full_name":"notKamui/Keval","owner":"notKamui","description":"A Kotlin mini library for math expression string evaluation","archived":false,"fork":false,"pushed_at":"2024-06-08T16:56:03.000Z","size":722,"stargazers_count":121,"open_issues_count":0,"forks_count":11,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-06-08T17:58:10.115Z","etag":null,"topics":["abstract-syntax-tree","java-library","kotlin","kotlin-library","library","math","mathematical-expressions","string-eval","string-manipulation"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/notKamui.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-28T17:25:00.000Z","updated_at":"2024-06-08T17:33:59.000Z","dependencies_parsed_at":"2024-05-29T14:54:32.097Z","dependency_job_id":null,"html_url":"https://github.com/notKamui/Keval","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notKamui%2FKeval","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notKamui%2FKeval/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notKamui%2FKeval/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notKamui%2FKeval/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/notKamui","download_url":"https://codeload.github.com/notKamui/Keval/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239479026,"owners_count":19645656,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abstract-syntax-tree","java-library","kotlin","kotlin-library","library","math","mathematical-expressions","string-eval","string-manipulation"],"created_at":"2025-02-18T13:29:17.522Z","updated_at":"2025-11-06T03:30:20.805Z","avatar_url":"https://github.com/notKamui.png","language":"Kotlin","readme":"# Keval\n\n***A Kotlin Multiplatform mini library for string evaluation***\n\n[![Kotlin](https://img.shields.io/badge/Kotlin-1.9.22-7f52ff.svg)](https://kotlinlang.org)\n[![Maven Central](https://img.shields.io/maven-central/v/com.notkamui.libs/keval.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/com.notkamui.libs/keval)\n\n(You may need to watch out using it:\nhaving to evaluate a string into a number is more often than not a code smell)\n\n## Import\n\nYou can import Keval directly with the jar files, or using your favorite dependency manager with the Maven Central\nrepository:\n\nMaven\n\n```XML\n\u003cdependencies\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003ecom.notkamui.libs\u003c/groupId\u003e\n    \u003cartifactId\u003ekeval\u003c/artifactId\u003e\n    \u003cversion\u003e1.1.1\u003c/version\u003e\n  \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\nGradle (here with KotlinDSL)\n\n```Kotlin\nrepositories {\n  mavenCentral()\n}\n\ndependencies {\n  implementation(\"com.notkamui.libs:keval:1.1.1\")\n}\n```\n\n(In case you're using it with another language than Kotlin -- i.e. Java --, make sure you include kotlin stdlib too)\n\n## Usage\n\nKeval can evaluate a mathematical expression as a `String` into a `Double` value. It is customizable in the sense that\none can add new binary and unary operators, functions and constants.\n\nThe base settings of Keval already include sensible defaults for the most common mathematical operations.\n\nKeval has support for all classic binary operators:\n\n- Subtraction `-`\n- Addition `+`\n- Multiplication `*`\n- Division `/`\n- Exponent `^`\n- Remainder (mod) `%`\n\nKeval has support for all classic unary operators:\n- Negation/Opposition `-` (prefix)\n- Identity `+` (prefix) (basically does nothing)\n- Factorial `!` (postfix)\n\nKeval has support for functions of variable arity:\n\n- Negate/Oppose `neg(expr)` (where 'expr' is an expression)\n- Absolute `abs(expr)` (where 'expr' is an expression)\n- Square root `sqrt(expr)` (where 'expr' is an expression)\n- Cube root `cbrt(expr)` (where 'expr' is an expression)\n- Exponential `exp(expr)` (where 'expr' is an expression)\n- Natural logarithm `ln(expr)` (where 'expr' is an expression)\n- Base 10 logarithm `log10(expr)` (where 'expr' is an expression)\n- Base 2 logarithm `log2(expr)` (where 'expr' is an expression)\n- Sine `sin(expr)` (where 'expr' is an expression)\n- Cosine `cos(expr)` (where 'expr' is an expression)\n- Tangent `tan(expr)` (where 'expr' is an expression)\n- Arcsine `asin(expr)` (where 'expr' is an expression)\n- Arccosine `acos(expr)` (where 'expr' is an expression)\n- Arctangent `atan(expr)` (where 'expr' is an expression)\n- Ceiling `ceil(expr)` (where 'expr' is an expression)\n- Floor `floor(expr)` (where 'expr' is an expression)\n- Round `round(expr)` (where 'expr' is an expression)\n\nKeval has support for constants, it has two built-in constant:\n\n- π `PI`\n- *e* `e` (Euler's number)\n\nYou can optionally add as many operators, functions or constants to Keval, as long as you define every field\nproperly, with a DSL (Domain Specific Language):\n\n- A **binary operator** is defined by:\n  - its **symbol** (a `Char` that is NOT a digit, nor a letter, nor an underscore)\n  - its **precedence**/priority level (a positive `Int`)\n  - its **associativity** (a `Boolean` true if left associative, false otherwise)\n  - its **implementation** (a function `(Double, Double) -\u003e Double`)\n- A **unary operator** is defined by:\n  - its **symbol** (a `Char` that is NOT a digit, nor a letter, nor an underscore)\n  - whether it is **prefix** (a `Boolean`)\n  - its **implementation** (a function `(Double) -\u003e Double`)\n- A **function** is defined by:\n  - its **name** (a non-empty `String` identifier, that doesn't start with a digit, and only contains letters, digits or\n    underscores)\n  - its **arity**/number of arguments (a positive (or 0) `Int` or null if the function can take any number of arguments,\n    also called a *variadic function*)\n  - its **implementation** (a function `(DoubleArray) -\u003e Double`)\n- A **constant** is defined by:\n  - its **name** (a non-empty `String` identifier, that doesn't start with a digit, and only contains letters, digits or\n    underscores)\n  - its **value** (a `Double`)\n\nKeval will use the built-in operators, function and constants if you choose not to define any new resource ; but if you\nchoose to do so, you need to include them manually. You may also choose to use Keval as an extension function.\n\n\u003e Please note that adding a new resource with a name that already exists will overwrite the previous one, except in the\n\u003e case of operators, where one symbol can represent both a binary and a unary operator. For example, it is possible to\n\u003e define a binary operator `-` and a unary operator `-` at the same time.\n\nYou can use it in several ways:\n\n```Kotlin\n\nKeval.eval(\"(3+4)(2/8 * 5) % PI\") // uses default resources\n\n\"(3+4)(2/8 * 5) % PI\".keval() // extension ; uses default resources\n\nKeval.create { // builder instance\n    includeDefault() // this function includes the built-in resources\n    \n    binaryOperator { // this function adds a binary operator ; you can call it several times\n        symbol = ';'\n        precedence = 3\n        isLeftAssociative = true\n        implementation = { a, b -\u003e a.pow(2) + b.pow(2) }\n    }\n    \n    unaryOperator { // this function adds a unary operator ; you can call it several times\n        symbol = '#'\n        isPrefix = false\n        implementation = { arg -\u003e (1..arg.toInt()).fold(0.0) { acc, i -\u003e acc + i } }\n    }\n  \n    function { // this function adds a function ; you can call it several times\n        name = \"max\"\n        arity = 2\n        implementation = { args -\u003e max(args[0], args[1]) }\n    }\n  \n    function { // this function adds a variadic aggregation (no arity) ; you can call it several times\n        name = \"sum\"\n        implementation = { args -\u003e args.sum() }\n    }\n  \n    constant { // this function adds a constant ; you can call it several times\n        name = \"PHI\"\n        value = 1.618\n    }\n}.eval(\"2*max(2, 3) ; 4# + PHI^2\")\n\n\"2*max(2, 3) ; 4# + PHI^2\".keval { // builder instance + extension\n    includeDefault()\n  \n    binaryOperator {\n        symbol = ';'\n        precedence = 3\n        isLeftAssociative = true\n        implementation = { a, b -\u003e a.pow(2) + b.pow(2) }\n    }\n    \n    unaryOperator {\n        symbol = '#'\n        isPrefix = false\n        implementation = { arg -\u003e (1..arg.toInt()).fold(0.0) { acc, i -\u003e acc + i } }\n    }\n  \n    function {\n        name = \"max\"\n        arity = 2\n        implementation = { args -\u003e max(args[0], args[1]) }\n    }\n  \n    function {\n        name = \"sum\"\n        implementation = { args -\u003e args.sum() }\n    }\n  \n    constant {\n        name = \"PHI\"\n        value = 1.618\n    }\n}\n```\n\nThe advantage of using `Keval.create` is that you may keep an instance of it in a variable so that you can call as\nmany `eval` as you need.\n\nIn concordance with creating a Keval instance, you can also add resources like this:\n\n```Kotlin\nval kvl = Keval().create {}\n    .withDefault() // includes default resources // it is unnecessary here since Keval() with no DSL already does it\n    .withBinaryOperator( // includes a new binary operator\n        ';', // symbol\n        3, // precedence\n        true // isLeftAssociative\n    ) { a, b -\u003e a.pow(2) + b.pow(2) } // implementation\n    .withUnaryOperator( // includes a new unary operator\n        '#', // symbol\n        false, // isPrefix\n    ) { arg -\u003e (1..arg.toInt()).fold(0.0) { acc, i -\u003e acc + i } } // implementation \n    .withFunction( // includes a new function\n        \"max\", // name\n        2 // arity\n    ) { max(it[0], it[1]) } // implementation\n    .withFunction( // includes a new variadic function\n        \"sum\", // name\n    ) { it.sum() } // implementation\n    .withConstant( // includes a new constant\n        \"PHI\", // name\n        1.618 // value\n    )\n\nkvl.eval(\"2*max(2, 3) ; 4# + PHI^2\")\n```\n\nThis can be combined with creating an instance with a DSL (i.e. `Keval.create`).\n***This is an especially useful syntax for Java users, since DSLs generally don't translate well over it.***\n\nCreating a resource with a name that already exists will overwrite the previous one.\n\nKeval assumes products/multiplications, and as such, the * symbol/name ***cannot*** be overwritten, and is the only\noperator to\n***always*** be present in the resource set of a Keval instance:\n\n```Kotlin\n\"(2+3)(6+4)\".keval() == \"(2+3)*(6+4)\".keval()\n```\n\nIn addition, the symbols `(`,`)`,`,` are reserved and trying to create operator using one of those symbols will result with an exception.\n\n## Error Handling\n\nIn case of an error, Keval will throw one of several `KevalException`s:\n\n- `KevalZeroDivisionException` in the case a zero division occurs\n- `KevalInvalidArgumentException` in the case a operator or function is called with an invalid argument (i.e. a negative number\n  for a factorial)\n- `KevalInvalidExpressionException` if the expression is invalid, with the following properties:\n  - `expression` contains the fully sanitized expression\n  - `position` is an estimate of the position of the error\n- `KevalInvalidSymbolException` if the expression contains an invalid operator, with the following properties:\n  - `invalidSymbol` contains the actual invalid operator\n  - `expression` contains the fully sanitized expression\n  - `position` is an estimate of the position of the error\n- `KevalDSLException` if, in the DSL, one of the field is either not set, or doesn't follow its restrictions (defined\n  above)\n\n`KevalZeroDivisionException` and `KevalInvalidArgumentException` are instantiable so that you can throw them when\nimplementing a custom operator/function.\n\n## Future Plans\n\n- Support for variables (will produce a `DoubleArray` instead of a single `Double`)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotkamui%2Fkeval","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnotkamui%2Fkeval","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotkamui%2Fkeval/lists"}