├── .github ├── dependabot.yml └── workflows │ ├── Build.yml │ ├── coverage.yml │ ├── rust-clippy.yml │ └── rust.yml ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── SECURITY.md ├── benches └── execute_expression.rs └── src ├── context.rs ├── define.rs ├── descriptor.rs ├── error.rs ├── function.rs ├── init.rs ├── keyword.rs ├── lib.rs ├── operator.rs ├── parser.rs ├── token.rs ├── tokenizer.rs └── value.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/Build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - "*" 6 | pull_request: 7 | env: 8 | RUST_BACKTRACE: 1 9 | 10 | jobs: 11 | linux: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | version: 16 | - stable 17 | target: 18 | - i686-unknown-linux-gnu 19 | - i686-unknown-linux-musl 20 | - x86_64-unknown-linux-gnu 21 | - x86_64-unknown-linux-musl 22 | - aarch64-unknown-linux-gnu 23 | fail-fast: false 24 | steps: 25 | - uses: actions/checkout@v3 26 | - uses: dtolnay/rust-toolchain@master 27 | with: 28 | toolchain: ${{ matrix.version }} 29 | components: rustfmt 30 | - name: cache 31 | uses: Swatinem/rust-cache@v2 32 | - name: test 33 | run: cargo test 34 | - name: ignored test 35 | run: cargo test -- --ignored || true 36 | if: matrix.version == 'nightly' 37 | - uses: actions/upload-artifact@v3 38 | if: failure() 39 | with: 40 | name: linux ${{ matrix.version }} 41 | retention-days: 3 42 | path: | 43 | tests/data/ 44 | !tests/data/**/*.rs 45 | - name: check formatting 46 | run: cargo fmt -- --check 47 | windows: 48 | runs-on: windows-latest 49 | strategy: 50 | matrix: 51 | version: 52 | - stable 53 | target: 54 | - x86_64-pc-windows-gnu 55 | - x86_64-pc-windows-msvc 56 | fail-fast: false 57 | steps: 58 | - uses: actions/checkout@v3 59 | - uses: dtolnay/rust-toolchain@master 60 | with: 61 | toolchain: ${{ matrix.version }} 62 | - name: cache 63 | uses: Swatinem/rust-cache@v2 64 | - name: test 65 | run: cargo test 66 | - uses: actions/upload-artifact@v3 67 | if: failure() 68 | with: 69 | name: windows ${{ matrix.version }} 70 | retention-days: 3 71 | path: | 72 | tests/data/ 73 | !tests/data/**/*.rs 74 | mac: 75 | runs-on: macos-latest 76 | strategy: 77 | matrix: 78 | version: 79 | - stable 80 | target: 81 | - x86_64-apple-darwin 82 | fail-fast: false 83 | steps: 84 | - uses: actions/checkout@v3 85 | - uses: dtolnay/rust-toolchain@master 86 | with: 87 | toolchain: ${{ matrix.version }} 88 | - name: cache 89 | uses: Swatinem/rust-cache@v2 90 | - name: test 91 | run: cargo test 92 | - uses: actions/upload-artifact@v3 93 | #if: failure() 94 | with: 95 | name: mac ${{ matrix.version }} 96 | retention-days: 3 97 | path: | 98 | tests/data/ 99 | !tests/data/**/*.rs 100 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: coverage 2 | 3 | on: [push] 4 | jobs: 5 | test: 6 | name: coverage 7 | runs-on: ubuntu-latest 8 | container: 9 | image: xd009642/tarpaulin 10 | options: --security-opt seccomp=unconfined 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v3 14 | 15 | - name: Generate code coverage 16 | run: | 17 | cargo tarpaulin --verbose --all-features --workspace --timeout 120 --out Xml 18 | 19 | - name: Upload to codecov.io 20 | uses: codecov/codecov-action@v2 21 | with: 22 | token: ${{secrets.CODECOV_TOKEN}} # not required for public repos 23 | fail_ci_if_error: true 24 | -------------------------------------------------------------------------------- /.github/workflows/rust-clippy.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # rust-clippy is a tool that runs a bunch of lints to catch common 6 | # mistakes in your Rust code and help improve your Rust code. 7 | # More details at https://github.com/rust-lang/rust-clippy 8 | # and https://rust-lang.github.io/rust-clippy/ 9 | 10 | name: rust-clippy analyze 11 | 12 | on: 13 | push: 14 | branches: [ "master" ] 15 | pull_request: 16 | # The branches below must be a subset of the branches above 17 | branches: [ "master" ] 18 | schedule: 19 | - cron: '15 17 * * 0' 20 | 21 | jobs: 22 | rust-clippy-analyze: 23 | name: Run rust-clippy analyzing 24 | runs-on: ubuntu-latest 25 | permissions: 26 | contents: read 27 | security-events: write 28 | actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status 29 | steps: 30 | - name: Checkout code 31 | uses: actions/checkout@v2 32 | 33 | - name: Install Rust toolchain 34 | uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1 35 | with: 36 | profile: minimal 37 | toolchain: stable 38 | components: clippy 39 | override: true 40 | 41 | - name: Install required cargo 42 | run: cargo install clippy-sarif sarif-fmt 43 | 44 | - name: Run rust-clippy 45 | run: 46 | cargo clippy 47 | --all-features 48 | --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt 49 | continue-on-error: true 50 | 51 | - name: Upload analysis results to GitHub 52 | uses: github/codeql-action/upload-sarif@v2 53 | with: 54 | sarif_file: rust-clippy-results.sarif 55 | wait-for-processing: true 56 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.devcontainer 3 | cobertura.xml 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug unit tests in library 'expression_executor'", 11 | "cargo": { 12 | "args": [ 13 | "test", 14 | "--no-run", 15 | "--lib", 16 | "--package=expression_executor" 17 | ], 18 | "filter": { 19 | "name": "expression_executor", 20 | "kind": "lib" 21 | } 22 | }, 23 | "args": [], 24 | "cwd": "${workspaceFolder}" 25 | }, 26 | { 27 | "type": "lldb", 28 | "request": "launch", 29 | "name": "Debug executable 'expression_executor'", 30 | "cargo": { 31 | "args": [ 32 | "build", 33 | "--bin=expression_executor", 34 | "--package=expression_executor" 35 | ], 36 | "filter": { 37 | "name": "expression_executor", 38 | "kind": "bin" 39 | } 40 | }, 41 | "args": [], 42 | "cwd": "${workspaceFolder}" 43 | }, 44 | { 45 | "type": "lldb", 46 | "request": "launch", 47 | "name": "Debug unit tests in executable 'expression_executor'", 48 | "cargo": { 49 | "args": [ 50 | "test", 51 | "--no-run", 52 | "--bin=expression_executor", 53 | "--package=expression_executor" 54 | ], 55 | "filter": { 56 | "name": "expression_executor", 57 | "kind": "bin" 58 | } 59 | }, 60 | "args": [], 61 | "cwd": "${workspaceFolder}" 62 | } 63 | ] 64 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.linkedProjects": [ 3 | "./Cargo.toml" 4 | ] 5 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "ahash" 18 | version = "0.8.3" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" 21 | dependencies = [ 22 | "cfg-if", 23 | "once_cell", 24 | "version_check", 25 | ] 26 | 27 | [[package]] 28 | name = "aho-corasick" 29 | version = "1.0.4" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" 32 | dependencies = [ 33 | "memchr", 34 | ] 35 | 36 | [[package]] 37 | name = "anes" 38 | version = "0.1.6" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 41 | 42 | [[package]] 43 | name = "anstyle" 44 | version = "1.0.4" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" 47 | 48 | [[package]] 49 | name = "arrayvec" 50 | version = "0.7.2" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" 53 | 54 | [[package]] 55 | name = "autocfg" 56 | version = "1.1.0" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 59 | 60 | [[package]] 61 | name = "bitflags" 62 | version = "2.4.1" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" 65 | 66 | [[package]] 67 | name = "bitvec" 68 | version = "1.0.1" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" 71 | dependencies = [ 72 | "funty", 73 | "radium", 74 | "tap", 75 | "wyz", 76 | ] 77 | 78 | [[package]] 79 | name = "borsh" 80 | version = "0.10.2" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "40f9ca3698b2e4cb7c15571db0abc5551dca417a21ae8140460b50309bb2cc62" 83 | dependencies = [ 84 | "borsh-derive", 85 | "hashbrown 0.13.2", 86 | ] 87 | 88 | [[package]] 89 | name = "borsh-derive" 90 | version = "0.10.2" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "598b3eacc6db9c3ee57b22707ad8f6a8d2f6d442bfe24ffeb8cbb70ca59e6a35" 93 | dependencies = [ 94 | "borsh-derive-internal", 95 | "borsh-schema-derive-internal", 96 | "proc-macro-crate", 97 | "proc-macro2", 98 | "syn 1.0.109", 99 | ] 100 | 101 | [[package]] 102 | name = "borsh-derive-internal" 103 | version = "0.10.2" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "186b734fa1c9f6743e90c95d7233c9faab6360d1a96d4ffa19d9cfd1e9350f8a" 106 | dependencies = [ 107 | "proc-macro2", 108 | "quote", 109 | "syn 1.0.109", 110 | ] 111 | 112 | [[package]] 113 | name = "borsh-schema-derive-internal" 114 | version = "0.10.2" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "99b7ff1008316626f485991b960ade129253d4034014616b94f309a15366cc49" 117 | dependencies = [ 118 | "proc-macro2", 119 | "quote", 120 | "syn 1.0.109", 121 | ] 122 | 123 | [[package]] 124 | name = "bumpalo" 125 | version = "3.14.0" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" 128 | 129 | [[package]] 130 | name = "bytecheck" 131 | version = "0.6.11" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" 134 | dependencies = [ 135 | "bytecheck_derive", 136 | "ptr_meta", 137 | "simdutf8", 138 | ] 139 | 140 | [[package]] 141 | name = "bytecheck_derive" 142 | version = "0.6.11" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" 145 | dependencies = [ 146 | "proc-macro2", 147 | "quote", 148 | "syn 1.0.109", 149 | ] 150 | 151 | [[package]] 152 | name = "byteorder" 153 | version = "1.4.3" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 156 | 157 | [[package]] 158 | name = "bytes" 159 | version = "1.4.0" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 162 | 163 | [[package]] 164 | name = "cast" 165 | version = "0.3.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 168 | 169 | [[package]] 170 | name = "cfg-if" 171 | version = "1.0.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 174 | 175 | [[package]] 176 | name = "ciborium" 177 | version = "0.2.1" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" 180 | dependencies = [ 181 | "ciborium-io", 182 | "ciborium-ll", 183 | "serde", 184 | ] 185 | 186 | [[package]] 187 | name = "ciborium-io" 188 | version = "0.2.1" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" 191 | 192 | [[package]] 193 | name = "ciborium-ll" 194 | version = "0.2.1" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" 197 | dependencies = [ 198 | "ciborium-io", 199 | "half", 200 | ] 201 | 202 | [[package]] 203 | name = "clap" 204 | version = "4.4.11" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" 207 | dependencies = [ 208 | "clap_builder", 209 | ] 210 | 211 | [[package]] 212 | name = "clap_builder" 213 | version = "4.4.11" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" 216 | dependencies = [ 217 | "anstyle", 218 | "clap_lex", 219 | ] 220 | 221 | [[package]] 222 | name = "clap_lex" 223 | version = "0.6.0" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" 226 | 227 | [[package]] 228 | name = "criterion" 229 | version = "0.5.1" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" 232 | dependencies = [ 233 | "anes", 234 | "cast", 235 | "ciborium", 236 | "clap", 237 | "criterion-plot", 238 | "is-terminal", 239 | "itertools", 240 | "num-traits", 241 | "once_cell", 242 | "oorandom", 243 | "plotters", 244 | "rayon", 245 | "regex", 246 | "serde", 247 | "serde_derive", 248 | "serde_json", 249 | "tinytemplate", 250 | "walkdir", 251 | ] 252 | 253 | [[package]] 254 | name = "criterion-plot" 255 | version = "0.5.0" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 258 | dependencies = [ 259 | "cast", 260 | "itertools", 261 | ] 262 | 263 | [[package]] 264 | name = "crossbeam-deque" 265 | version = "0.8.4" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" 268 | dependencies = [ 269 | "cfg-if", 270 | "crossbeam-epoch", 271 | "crossbeam-utils", 272 | ] 273 | 274 | [[package]] 275 | name = "crossbeam-epoch" 276 | version = "0.9.16" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" 279 | dependencies = [ 280 | "autocfg", 281 | "cfg-if", 282 | "crossbeam-utils", 283 | "memoffset", 284 | ] 285 | 286 | [[package]] 287 | name = "crossbeam-utils" 288 | version = "0.8.17" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" 291 | dependencies = [ 292 | "cfg-if", 293 | ] 294 | 295 | [[package]] 296 | name = "either" 297 | version = "1.9.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 300 | 301 | [[package]] 302 | name = "errno" 303 | version = "0.3.8" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 306 | dependencies = [ 307 | "libc", 308 | "windows-sys 0.52.0", 309 | ] 310 | 311 | [[package]] 312 | name = "expression_engine" 313 | version = "0.7.0" 314 | dependencies = [ 315 | "criterion", 316 | "once_cell", 317 | "rstest", 318 | "rust_decimal", 319 | ] 320 | 321 | [[package]] 322 | name = "funty" 323 | version = "2.0.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" 326 | 327 | [[package]] 328 | name = "futures" 329 | version = "0.3.28" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" 332 | dependencies = [ 333 | "futures-channel", 334 | "futures-core", 335 | "futures-executor", 336 | "futures-io", 337 | "futures-sink", 338 | "futures-task", 339 | "futures-util", 340 | ] 341 | 342 | [[package]] 343 | name = "futures-channel" 344 | version = "0.3.28" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" 347 | dependencies = [ 348 | "futures-core", 349 | "futures-sink", 350 | ] 351 | 352 | [[package]] 353 | name = "futures-core" 354 | version = "0.3.28" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" 357 | 358 | [[package]] 359 | name = "futures-executor" 360 | version = "0.3.28" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" 363 | dependencies = [ 364 | "futures-core", 365 | "futures-task", 366 | "futures-util", 367 | ] 368 | 369 | [[package]] 370 | name = "futures-io" 371 | version = "0.3.28" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" 374 | 375 | [[package]] 376 | name = "futures-macro" 377 | version = "0.3.28" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" 380 | dependencies = [ 381 | "proc-macro2", 382 | "quote", 383 | "syn 2.0.12", 384 | ] 385 | 386 | [[package]] 387 | name = "futures-sink" 388 | version = "0.3.28" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" 391 | 392 | [[package]] 393 | name = "futures-task" 394 | version = "0.3.28" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" 397 | 398 | [[package]] 399 | name = "futures-timer" 400 | version = "3.0.2" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" 403 | 404 | [[package]] 405 | name = "futures-util" 406 | version = "0.3.28" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" 409 | dependencies = [ 410 | "futures-channel", 411 | "futures-core", 412 | "futures-io", 413 | "futures-macro", 414 | "futures-sink", 415 | "futures-task", 416 | "memchr", 417 | "pin-project-lite", 418 | "pin-utils", 419 | "slab", 420 | ] 421 | 422 | [[package]] 423 | name = "getrandom" 424 | version = "0.2.8" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 427 | dependencies = [ 428 | "cfg-if", 429 | "libc", 430 | "wasi", 431 | ] 432 | 433 | [[package]] 434 | name = "glob" 435 | version = "0.3.1" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 438 | 439 | [[package]] 440 | name = "half" 441 | version = "1.8.2" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" 444 | 445 | [[package]] 446 | name = "hashbrown" 447 | version = "0.12.3" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 450 | dependencies = [ 451 | "ahash 0.7.6", 452 | ] 453 | 454 | [[package]] 455 | name = "hashbrown" 456 | version = "0.13.2" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" 459 | dependencies = [ 460 | "ahash 0.8.3", 461 | ] 462 | 463 | [[package]] 464 | name = "hermit-abi" 465 | version = "0.3.3" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" 468 | 469 | [[package]] 470 | name = "is-terminal" 471 | version = "0.4.9" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" 474 | dependencies = [ 475 | "hermit-abi", 476 | "rustix", 477 | "windows-sys 0.48.0", 478 | ] 479 | 480 | [[package]] 481 | name = "itertools" 482 | version = "0.10.5" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 485 | dependencies = [ 486 | "either", 487 | ] 488 | 489 | [[package]] 490 | name = "itoa" 491 | version = "1.0.6" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 494 | 495 | [[package]] 496 | name = "js-sys" 497 | version = "0.3.66" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" 500 | dependencies = [ 501 | "wasm-bindgen", 502 | ] 503 | 504 | [[package]] 505 | name = "libc" 506 | version = "0.2.151" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" 509 | 510 | [[package]] 511 | name = "linux-raw-sys" 512 | version = "0.4.12" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" 515 | 516 | [[package]] 517 | name = "log" 518 | version = "0.4.20" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 521 | 522 | [[package]] 523 | name = "memchr" 524 | version = "2.5.0" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 527 | 528 | [[package]] 529 | name = "memoffset" 530 | version = "0.9.0" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" 533 | dependencies = [ 534 | "autocfg", 535 | ] 536 | 537 | [[package]] 538 | name = "num-traits" 539 | version = "0.2.15" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 542 | dependencies = [ 543 | "autocfg", 544 | ] 545 | 546 | [[package]] 547 | name = "once_cell" 548 | version = "1.18.0" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 551 | 552 | [[package]] 553 | name = "oorandom" 554 | version = "11.1.3" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" 557 | 558 | [[package]] 559 | name = "pin-project-lite" 560 | version = "0.2.12" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" 563 | 564 | [[package]] 565 | name = "pin-utils" 566 | version = "0.1.0" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 569 | 570 | [[package]] 571 | name = "plotters" 572 | version = "0.3.5" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" 575 | dependencies = [ 576 | "num-traits", 577 | "plotters-backend", 578 | "plotters-svg", 579 | "wasm-bindgen", 580 | "web-sys", 581 | ] 582 | 583 | [[package]] 584 | name = "plotters-backend" 585 | version = "0.3.5" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" 588 | 589 | [[package]] 590 | name = "plotters-svg" 591 | version = "0.3.5" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" 594 | dependencies = [ 595 | "plotters-backend", 596 | ] 597 | 598 | [[package]] 599 | name = "ppv-lite86" 600 | version = "0.2.17" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 603 | 604 | [[package]] 605 | name = "proc-macro-crate" 606 | version = "0.1.5" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" 609 | dependencies = [ 610 | "toml", 611 | ] 612 | 613 | [[package]] 614 | name = "proc-macro2" 615 | version = "1.0.52" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" 618 | dependencies = [ 619 | "unicode-ident", 620 | ] 621 | 622 | [[package]] 623 | name = "ptr_meta" 624 | version = "0.1.4" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" 627 | dependencies = [ 628 | "ptr_meta_derive", 629 | ] 630 | 631 | [[package]] 632 | name = "ptr_meta_derive" 633 | version = "0.1.4" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" 636 | dependencies = [ 637 | "proc-macro2", 638 | "quote", 639 | "syn 1.0.109", 640 | ] 641 | 642 | [[package]] 643 | name = "quote" 644 | version = "1.0.26" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 647 | dependencies = [ 648 | "proc-macro2", 649 | ] 650 | 651 | [[package]] 652 | name = "radium" 653 | version = "0.7.0" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" 656 | 657 | [[package]] 658 | name = "rand" 659 | version = "0.8.5" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 662 | dependencies = [ 663 | "libc", 664 | "rand_chacha", 665 | "rand_core", 666 | ] 667 | 668 | [[package]] 669 | name = "rand_chacha" 670 | version = "0.3.1" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 673 | dependencies = [ 674 | "ppv-lite86", 675 | "rand_core", 676 | ] 677 | 678 | [[package]] 679 | name = "rand_core" 680 | version = "0.6.4" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 683 | dependencies = [ 684 | "getrandom", 685 | ] 686 | 687 | [[package]] 688 | name = "rayon" 689 | version = "1.8.0" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" 692 | dependencies = [ 693 | "either", 694 | "rayon-core", 695 | ] 696 | 697 | [[package]] 698 | name = "rayon-core" 699 | version = "1.12.0" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" 702 | dependencies = [ 703 | "crossbeam-deque", 704 | "crossbeam-utils", 705 | ] 706 | 707 | [[package]] 708 | name = "regex" 709 | version = "1.9.3" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" 712 | dependencies = [ 713 | "aho-corasick", 714 | "memchr", 715 | "regex-automata", 716 | "regex-syntax", 717 | ] 718 | 719 | [[package]] 720 | name = "regex-automata" 721 | version = "0.3.6" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" 724 | dependencies = [ 725 | "aho-corasick", 726 | "memchr", 727 | "regex-syntax", 728 | ] 729 | 730 | [[package]] 731 | name = "regex-syntax" 732 | version = "0.7.4" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" 735 | 736 | [[package]] 737 | name = "relative-path" 738 | version = "1.8.0" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "4bf2521270932c3c7bed1a59151222bd7643c79310f2916f01925e1e16255698" 741 | 742 | [[package]] 743 | name = "rend" 744 | version = "0.4.0" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" 747 | dependencies = [ 748 | "bytecheck", 749 | ] 750 | 751 | [[package]] 752 | name = "rkyv" 753 | version = "0.7.42" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" 756 | dependencies = [ 757 | "bitvec", 758 | "bytecheck", 759 | "hashbrown 0.12.3", 760 | "ptr_meta", 761 | "rend", 762 | "rkyv_derive", 763 | "seahash", 764 | "tinyvec", 765 | "uuid", 766 | ] 767 | 768 | [[package]] 769 | name = "rkyv_derive" 770 | version = "0.7.42" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" 773 | dependencies = [ 774 | "proc-macro2", 775 | "quote", 776 | "syn 1.0.109", 777 | ] 778 | 779 | [[package]] 780 | name = "rstest" 781 | version = "0.18.2" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" 784 | dependencies = [ 785 | "futures", 786 | "futures-timer", 787 | "rstest_macros", 788 | "rustc_version", 789 | ] 790 | 791 | [[package]] 792 | name = "rstest_macros" 793 | version = "0.18.2" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" 796 | dependencies = [ 797 | "cfg-if", 798 | "glob", 799 | "proc-macro2", 800 | "quote", 801 | "regex", 802 | "relative-path", 803 | "rustc_version", 804 | "syn 2.0.12", 805 | "unicode-ident", 806 | ] 807 | 808 | [[package]] 809 | name = "rust_decimal" 810 | version = "1.31.0" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "4a2ab0025103a60ecaaf3abf24db1db240a4e1c15837090d2c32f625ac98abea" 813 | dependencies = [ 814 | "arrayvec", 815 | "borsh", 816 | "byteorder", 817 | "bytes", 818 | "num-traits", 819 | "rand", 820 | "rkyv", 821 | "serde", 822 | "serde_json", 823 | ] 824 | 825 | [[package]] 826 | name = "rustc_version" 827 | version = "0.4.0" 828 | source = "registry+https://github.com/rust-lang/crates.io-index" 829 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 830 | dependencies = [ 831 | "semver", 832 | ] 833 | 834 | [[package]] 835 | name = "rustix" 836 | version = "0.38.28" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" 839 | dependencies = [ 840 | "bitflags", 841 | "errno", 842 | "libc", 843 | "linux-raw-sys", 844 | "windows-sys 0.52.0", 845 | ] 846 | 847 | [[package]] 848 | name = "ryu" 849 | version = "1.0.13" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 852 | 853 | [[package]] 854 | name = "same-file" 855 | version = "1.0.6" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 858 | dependencies = [ 859 | "winapi-util", 860 | ] 861 | 862 | [[package]] 863 | name = "seahash" 864 | version = "4.1.0" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" 867 | 868 | [[package]] 869 | name = "semver" 870 | version = "1.0.18" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" 873 | 874 | [[package]] 875 | name = "serde" 876 | version = "1.0.157" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "707de5fcf5df2b5788fca98dd7eab490bc2fd9b7ef1404defc462833b83f25ca" 879 | dependencies = [ 880 | "serde_derive", 881 | ] 882 | 883 | [[package]] 884 | name = "serde_derive" 885 | version = "1.0.157" 886 | source = "registry+https://github.com/rust-lang/crates.io-index" 887 | checksum = "78997f4555c22a7971214540c4a661291970619afd56de19f77e0de86296e1e5" 888 | dependencies = [ 889 | "proc-macro2", 890 | "quote", 891 | "syn 2.0.12", 892 | ] 893 | 894 | [[package]] 895 | name = "serde_json" 896 | version = "1.0.94" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" 899 | dependencies = [ 900 | "itoa", 901 | "ryu", 902 | "serde", 903 | ] 904 | 905 | [[package]] 906 | name = "simdutf8" 907 | version = "0.1.4" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" 910 | 911 | [[package]] 912 | name = "slab" 913 | version = "0.4.8" 914 | source = "registry+https://github.com/rust-lang/crates.io-index" 915 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" 916 | dependencies = [ 917 | "autocfg", 918 | ] 919 | 920 | [[package]] 921 | name = "syn" 922 | version = "1.0.109" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 925 | dependencies = [ 926 | "proc-macro2", 927 | "quote", 928 | "unicode-ident", 929 | ] 930 | 931 | [[package]] 932 | name = "syn" 933 | version = "2.0.12" 934 | source = "registry+https://github.com/rust-lang/crates.io-index" 935 | checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927" 936 | dependencies = [ 937 | "proc-macro2", 938 | "quote", 939 | "unicode-ident", 940 | ] 941 | 942 | [[package]] 943 | name = "tap" 944 | version = "1.0.1" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 947 | 948 | [[package]] 949 | name = "tinytemplate" 950 | version = "1.2.1" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 953 | dependencies = [ 954 | "serde", 955 | "serde_json", 956 | ] 957 | 958 | [[package]] 959 | name = "tinyvec" 960 | version = "1.6.0" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 963 | dependencies = [ 964 | "tinyvec_macros", 965 | ] 966 | 967 | [[package]] 968 | name = "tinyvec_macros" 969 | version = "0.1.1" 970 | source = "registry+https://github.com/rust-lang/crates.io-index" 971 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 972 | 973 | [[package]] 974 | name = "toml" 975 | version = "0.5.11" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" 978 | dependencies = [ 979 | "serde", 980 | ] 981 | 982 | [[package]] 983 | name = "unicode-ident" 984 | version = "1.0.8" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 987 | 988 | [[package]] 989 | name = "uuid" 990 | version = "1.4.1" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" 993 | 994 | [[package]] 995 | name = "version_check" 996 | version = "0.9.4" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 999 | 1000 | [[package]] 1001 | name = "walkdir" 1002 | version = "2.4.0" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" 1005 | dependencies = [ 1006 | "same-file", 1007 | "winapi-util", 1008 | ] 1009 | 1010 | [[package]] 1011 | name = "wasi" 1012 | version = "0.11.0+wasi-snapshot-preview1" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1015 | 1016 | [[package]] 1017 | name = "wasm-bindgen" 1018 | version = "0.2.89" 1019 | source = "registry+https://github.com/rust-lang/crates.io-index" 1020 | checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" 1021 | dependencies = [ 1022 | "cfg-if", 1023 | "wasm-bindgen-macro", 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "wasm-bindgen-backend" 1028 | version = "0.2.89" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" 1031 | dependencies = [ 1032 | "bumpalo", 1033 | "log", 1034 | "once_cell", 1035 | "proc-macro2", 1036 | "quote", 1037 | "syn 2.0.12", 1038 | "wasm-bindgen-shared", 1039 | ] 1040 | 1041 | [[package]] 1042 | name = "wasm-bindgen-macro" 1043 | version = "0.2.89" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" 1046 | dependencies = [ 1047 | "quote", 1048 | "wasm-bindgen-macro-support", 1049 | ] 1050 | 1051 | [[package]] 1052 | name = "wasm-bindgen-macro-support" 1053 | version = "0.2.89" 1054 | source = "registry+https://github.com/rust-lang/crates.io-index" 1055 | checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" 1056 | dependencies = [ 1057 | "proc-macro2", 1058 | "quote", 1059 | "syn 2.0.12", 1060 | "wasm-bindgen-backend", 1061 | "wasm-bindgen-shared", 1062 | ] 1063 | 1064 | [[package]] 1065 | name = "wasm-bindgen-shared" 1066 | version = "0.2.89" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" 1069 | 1070 | [[package]] 1071 | name = "web-sys" 1072 | version = "0.3.66" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" 1075 | dependencies = [ 1076 | "js-sys", 1077 | "wasm-bindgen", 1078 | ] 1079 | 1080 | [[package]] 1081 | name = "winapi" 1082 | version = "0.3.9" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1085 | dependencies = [ 1086 | "winapi-i686-pc-windows-gnu", 1087 | "winapi-x86_64-pc-windows-gnu", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "winapi-i686-pc-windows-gnu" 1092 | version = "0.4.0" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1095 | 1096 | [[package]] 1097 | name = "winapi-util" 1098 | version = "0.1.6" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 1101 | dependencies = [ 1102 | "winapi", 1103 | ] 1104 | 1105 | [[package]] 1106 | name = "winapi-x86_64-pc-windows-gnu" 1107 | version = "0.4.0" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1110 | 1111 | [[package]] 1112 | name = "windows-sys" 1113 | version = "0.48.0" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1116 | dependencies = [ 1117 | "windows-targets 0.48.5", 1118 | ] 1119 | 1120 | [[package]] 1121 | name = "windows-sys" 1122 | version = "0.52.0" 1123 | source = "registry+https://github.com/rust-lang/crates.io-index" 1124 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1125 | dependencies = [ 1126 | "windows-targets 0.52.0", 1127 | ] 1128 | 1129 | [[package]] 1130 | name = "windows-targets" 1131 | version = "0.48.5" 1132 | source = "registry+https://github.com/rust-lang/crates.io-index" 1133 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1134 | dependencies = [ 1135 | "windows_aarch64_gnullvm 0.48.5", 1136 | "windows_aarch64_msvc 0.48.5", 1137 | "windows_i686_gnu 0.48.5", 1138 | "windows_i686_msvc 0.48.5", 1139 | "windows_x86_64_gnu 0.48.5", 1140 | "windows_x86_64_gnullvm 0.48.5", 1141 | "windows_x86_64_msvc 0.48.5", 1142 | ] 1143 | 1144 | [[package]] 1145 | name = "windows-targets" 1146 | version = "0.52.0" 1147 | source = "registry+https://github.com/rust-lang/crates.io-index" 1148 | checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" 1149 | dependencies = [ 1150 | "windows_aarch64_gnullvm 0.52.0", 1151 | "windows_aarch64_msvc 0.52.0", 1152 | "windows_i686_gnu 0.52.0", 1153 | "windows_i686_msvc 0.52.0", 1154 | "windows_x86_64_gnu 0.52.0", 1155 | "windows_x86_64_gnullvm 0.52.0", 1156 | "windows_x86_64_msvc 0.52.0", 1157 | ] 1158 | 1159 | [[package]] 1160 | name = "windows_aarch64_gnullvm" 1161 | version = "0.48.5" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1164 | 1165 | [[package]] 1166 | name = "windows_aarch64_gnullvm" 1167 | version = "0.52.0" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" 1170 | 1171 | [[package]] 1172 | name = "windows_aarch64_msvc" 1173 | version = "0.48.5" 1174 | source = "registry+https://github.com/rust-lang/crates.io-index" 1175 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1176 | 1177 | [[package]] 1178 | name = "windows_aarch64_msvc" 1179 | version = "0.52.0" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" 1182 | 1183 | [[package]] 1184 | name = "windows_i686_gnu" 1185 | version = "0.48.5" 1186 | source = "registry+https://github.com/rust-lang/crates.io-index" 1187 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1188 | 1189 | [[package]] 1190 | name = "windows_i686_gnu" 1191 | version = "0.52.0" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" 1194 | 1195 | [[package]] 1196 | name = "windows_i686_msvc" 1197 | version = "0.48.5" 1198 | source = "registry+https://github.com/rust-lang/crates.io-index" 1199 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 1200 | 1201 | [[package]] 1202 | name = "windows_i686_msvc" 1203 | version = "0.52.0" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" 1206 | 1207 | [[package]] 1208 | name = "windows_x86_64_gnu" 1209 | version = "0.48.5" 1210 | source = "registry+https://github.com/rust-lang/crates.io-index" 1211 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 1212 | 1213 | [[package]] 1214 | name = "windows_x86_64_gnu" 1215 | version = "0.52.0" 1216 | source = "registry+https://github.com/rust-lang/crates.io-index" 1217 | checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" 1218 | 1219 | [[package]] 1220 | name = "windows_x86_64_gnullvm" 1221 | version = "0.48.5" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1224 | 1225 | [[package]] 1226 | name = "windows_x86_64_gnullvm" 1227 | version = "0.52.0" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" 1230 | 1231 | [[package]] 1232 | name = "windows_x86_64_msvc" 1233 | version = "0.48.5" 1234 | source = "registry+https://github.com/rust-lang/crates.io-index" 1235 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1236 | 1237 | [[package]] 1238 | name = "windows_x86_64_msvc" 1239 | version = "0.52.0" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" 1242 | 1243 | [[package]] 1244 | name = "wyz" 1245 | version = "0.5.1" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" 1248 | dependencies = [ 1249 | "tap", 1250 | ] 1251 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "expression_engine" 3 | version = "0.7.0" 4 | edition = "2021" 5 | description = "An expression engine written in pure rust" 6 | license = "Apache-2.0" 7 | homepage = "https://github.com/ashyanSpada/expression_engine_rs" 8 | keywords = [ 9 | "rule-engine", 10 | "expression-engine", 11 | "math-engine", 12 | "expressionengine", 13 | "ruleengine" 14 | ] 15 | repository = "https://github.com/ashyanSpada/expression_engine_rs" 16 | documentation = "https://docs.rs/expression_engine/latest/expression_engine/" 17 | 18 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 19 | 20 | [dependencies] 21 | rust_decimal = "1.31.0" 22 | once_cell = "1.18.0" 23 | 24 | [dev-dependencies] 25 | rstest = "0.18.2" 26 | criterion = {version="0.5.1", features=["html_reports"]} 27 | 28 | [[bench]] 29 | name = "execute_expression" 30 | harness = false 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # expression_engine 2 | [![Build Status](https://github.com/ashyanSpada/expression_engine_rs/workflows/Build/badge.svg)](https://github.com/ashyanSpada/expression_engine_rs/actions) 3 | [![Latest Version](https://img.shields.io/crates/v/expression_engine.svg)](https://crates.io/crates/expression_engine) 4 | [![License:Apache-2.0](https://img.shields.io/badge/License-Apache2.0-yellow.svg)](https://opensource.org/licenses/Apache-2.0) 5 | [![codecov](https://codecov.io/gh/ashyanSpada/expression_engine_rs/graph/badge.svg?token=H5GNNRVUZQ)](https://codecov.io/gh/ashyanSpada/expression_engine_rs) 6 | 7 | ## Introduction 8 | 9 | Expression engine is a library written in pure Rust which provides an engine to compile and execute expressions. An expression indicates a string-like sentence that can be executed with some contexts and return a value (mostly, but not limited to, boolean, string and number). 10 | 11 | Expression engine aims to provide an engine for users that can execute complex logics using configurations without recompiling. It's a proper alternative as the basis to build business rule engines. 12 | 13 | ## Usage 14 | 15 | Calling the engine is simple. At first, define the expression you want to execute. Secondly, create a context to cache the pre-defined inner functions and variables. And then, register the variables and functions to the context. Finally, call the execute function with the expression and context to get the executing result. 16 | 17 | ```rust 18 | use expression_engine::{create_context, execute, Value}; 19 | let input = "c = 5+3; c+=10+f; c"; 20 | let ctx = create_context!( 21 | "d" => 2, 22 | "b" => true, 23 | "f" => Arc::new(|params| Ok(Value::from(3))) 24 | ); 25 | let ans = execute(input, ctx).unwrap(); 26 | assert_eq!(ans, Value::from(21)) 27 | ``` 28 | 29 | ## Features 30 | 31 | + Easy to Use (three lines at least) 32 | + Abundant Types and Expressions (Five fundamental types and seven kinds of expressions) 33 | + Pre-defined Operators Support (Common boolean, numeric and string operators) 34 | + Support function and operators registration 35 | + Support operator redirection 36 | 37 | ## Definition 38 | 39 | ### Expression 40 | 41 | ``` 42 | Syntax 43 | Expression(;Expression)* 44 | 45 | Expression: 46 | ( 47 | LiteralExpression 48 | | UnaryExpression 49 | | BinaryExpression 50 | | TernaryExpression 51 | | FunctionExpression 52 | | ReferenceExpression 53 | | ListExpression 54 | | MapExpression 55 | | NoneExpression 56 | 57 | ) 58 | 59 | ``` 60 | 61 | ### LiteralExpression 62 | 63 | ``` 64 | Syntax 65 | LiteralExpression: 66 | (LITERAL_NUMBER|LITERAL_BOOL|LITERAL_STRING) 67 | 68 | ``` 69 | 70 | A literal expression is an expression consisting of only one single token instead of a sequence of tokens. Here are 3 kinds of literal expresions, respectively, the LITERAL_NUMBER, the LITERAL_BOOL, and the LITERAL_STRING. 71 | 72 | #### LITERAL_NUMBER 73 | 74 | ```rust 75 | fn is_digit_char(ch: char) -> bool { 76 | return '0' <= ch && ch <= '9' || ch == '.' || ch == '-' || ch == 'e' || ch == 'E' || ch == '+'; 77 | } 78 | ``` 79 | 80 | Continuous chars with patterns as above will be parsed to a number. 81 | 82 | #### LITERAL_BOOL 83 | 84 | The `false` and `False` will be parsed to the bool value **false**, while the `true` and `True` will be decoded to the bool value **true**. 85 | 86 | #### LITERAL_STRING 87 | 88 | A sequence of characters that starts with " and ends with " or starts with ' and ends with ' will be decoded as a LITERAL_STRING. 89 | 90 | ### UnaryExpression 91 | 92 | ``` 93 | Syntax 94 | UnaryExpression: 95 | Operator Operand 96 | 97 | Operand: 98 | Expression 99 | ``` 100 | 101 | A unary expression is consisted of an operand and a unary operator. All the unary operators have the same precedence and the right-to-left associativity. 102 | 103 | | UnaryOp | Desc | 104 | | ------- | ------------------------- | 105 | | ! | Logical negation operator | 106 | | not | Logical negation operator | 107 | 108 | ### BinaryExpression 109 | 110 | ``` 111 | Syntax 112 | BinaryExpression: 113 | Lhs Operator Rhs 114 | 115 | Lhs: 116 | Expresssion 117 | 118 | Rhs: 119 | Expression 120 | 121 | ``` 122 | 123 | A binary expression contains two operands separated by an operator. All the binary operators have right-to-left associativity while their precedences may be not the same. The supported binary operators are as below: 124 | 125 | | Operator | Precedence | Desc | 126 | | --------- | ---------- | ---- | 127 | | \|= | 20 | | 128 | | <<= | 20 | | 129 | | = | 20 | | 130 | | += | 20 | | 131 | | ^= | 20 | | 132 | | /= | 20 | | 133 | | &= | 20 | | 134 | | >>= | 20 | | 135 | | %= | 20 | | 136 | | -= | 20 | | 137 | | *= | 20 | | 138 | | \|\| | 40 | | 139 | | && | 50 | | 140 | | > | 60 | | 141 | | >= | 60 | | 142 | | <= | 60 | | 143 | | != | 60 | | 144 | | < | 60 | | 145 | | == | 60 | | 146 | | \| | 70 | | 147 | | ^ | 80 | | 148 | | & | 90 | | 149 | | >> | 100 | | 150 | | << | 100 | | 151 | | + | 110 | | 152 | | - | 110 | | 153 | | * | 120 | | 154 | | % | 120 | | 155 | | / | 120 | | 156 | | beginWith | 200 | | 157 | | endWith | 200 | | 158 | 159 | ### TernaryExpression 160 | 161 | ``` 162 | Syntax 163 | TernaryExpression: 164 | Condition ? Lhs : Rhs 165 | 166 | Condition: 167 | Expression 168 | 169 | Lhs: 170 | Expression 171 | 172 | Rhs: 173 | Expression 174 | ``` 175 | 176 | The ternary expression is composed of three parts, respectively the Condition, the Lhs and the Rhs. If the result of the Condition is true, then return to Lhs. Otherwise the result of Rhs is returned. 177 | 178 | ### FunctionExpression 179 | 180 | ``` 181 | Syntax 182 | FunctionExpression: 183 | func(FunctionParams?) 184 | 185 | FunctionParams: 186 | Expression(,Expression)* 187 | 188 | 189 | ``` 190 | 191 | The function name with the params which are a sequence of expressions separated by comma consist the function expression. 192 | 193 | ### ReferenceExpression 194 | 195 | The reference expression is either a variable or a function with no params. 196 | 197 | ### ListExpression 198 | 199 | ``` 200 | Syntax 201 | ListExpression: 202 | [ListElements?] 203 | 204 | ListElements: 205 | Expression(,Expression)* 206 | ``` 207 | 208 | The list expression starts with the open bracket and ends with the close bracket. Its params are a list of expressions. 209 | 210 | ### MapExpression 211 | 212 | ``` 213 | Syntax 214 | MapExpression: 215 | {MapElements?} 216 | 217 | MapElements: 218 | KeyElement:ValueElement(,KeyElement:ValueElement)* 219 | 220 | KeyElement: 221 | Expression 222 | 223 | ValueElement: 224 | Expression 225 | ``` 226 | 227 | The map expression begins with the open brace and ends with the close brace with a sequence of k, v pair where both the k and v are expressions. 228 | 229 | ### NoneExpression 230 | 231 | ``` 232 | Syntax 233 | NoneExpression: 234 | None 235 | 236 | ``` 237 | 238 | The return value of the NoneExpression is `None`. 239 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /benches/execute_expression.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use expression_engine::{create_context, execute, parse_expression, Value}; 3 | 4 | fn bench_execute_expression(c: &mut Criterion) { 5 | let input = "c = 5+3; c+=10+f; c"; 6 | c.bench_function("execute_expression", |b| { 7 | b.iter(|| { 8 | execute( 9 | input, 10 | create_context!( 11 | "d" => 2, 12 | "b" => true, 13 | "f" => Arc::new(|_| Ok(Value::from(3))) 14 | ), 15 | ) 16 | }) 17 | }); 18 | } 19 | 20 | fn bench_parse_expression(c: &mut Criterion) { 21 | let input = "c = 5+3; c+=10+f; c"; 22 | c.bench_function("parse_expression", |b| b.iter(|| parse_expression(input))); 23 | } 24 | 25 | criterion_group!(benches, bench_execute_expression, bench_parse_expression); 26 | criterion_main!(benches); 27 | -------------------------------------------------------------------------------- /src/context.rs: -------------------------------------------------------------------------------- 1 | use crate::define::Result; 2 | use crate::function::InnerFunction; 3 | use crate::value::Value; 4 | use core::clone::Clone; 5 | use std::collections::HashMap; 6 | use std::sync::{Arc, Mutex}; 7 | 8 | #[derive(Clone)] 9 | pub enum ContextValue { 10 | Variable(Value), 11 | Function(Arc), 12 | } 13 | 14 | pub struct Context(pub Arc>>); 15 | 16 | impl Context { 17 | pub fn new() -> Self { 18 | Context(Arc::new(Mutex::new(HashMap::new()))) 19 | } 20 | 21 | pub fn set_func(&mut self, name: &str, func: Arc) { 22 | self.set(name, ContextValue::Function(func.clone())); 23 | } 24 | 25 | pub fn set_variable(&mut self, name: &str, value: Value) { 26 | self.set(name, ContextValue::Variable(value)); 27 | } 28 | 29 | pub fn set(&mut self, name: &str, v: ContextValue) { 30 | self.0.lock().unwrap().insert(name.to_string(), v); 31 | } 32 | 33 | pub fn get_func(&self, name: &str) -> Option> { 34 | let value = self.get(name)?; 35 | match value { 36 | ContextValue::Function(func) => Some(func.clone()), 37 | ContextValue::Variable(_) => None, 38 | } 39 | } 40 | 41 | pub fn get_variable(&self, name: &str) -> Option { 42 | let value = self.get(name)?; 43 | match value { 44 | ContextValue::Variable(v) => Some(v.clone()), 45 | ContextValue::Function(_) => None, 46 | } 47 | } 48 | 49 | pub fn get(&self, name: &str) -> Option { 50 | let binding = self.0.lock().unwrap(); 51 | let value = binding.get(name)?; 52 | Some(value.clone()) 53 | } 54 | 55 | pub fn value(&self, name: &str) -> Result { 56 | let binding = self.0.lock().unwrap(); 57 | if binding.get(name).is_none() { 58 | return Ok(Value::None); 59 | } 60 | let value = binding.get(name).unwrap(); 61 | match value { 62 | ContextValue::Variable(v) => Ok(v.clone()), 63 | ContextValue::Function(func) => func(Vec::new()), 64 | } 65 | } 66 | } 67 | 68 | /// 69 | ///```rust 70 | /// use expression_engine::create_context; 71 | /// use expression_engine::Value; 72 | /// let a = create_context!("d" => 3.5, "c" => Arc::new(|params| { 73 | /// Ok(Value::from(3)) 74 | /// })); 75 | ///``` 76 | /// 77 | /// 78 | #[macro_export] 79 | macro_rules! create_context { 80 | (($ctx:expr) $k:expr => Arc::new($($v:tt)*), $($tt:tt)*) => {{ 81 | $ctx.set_func($k, Arc::new($($v)*)); 82 | $crate::create_context!(($ctx) $($tt)*); 83 | }}; 84 | 85 | (($ctx:expr) $k:expr => $v:expr, $($tt:tt)*) => {{ 86 | $ctx.set_variable($k, Value::from($v)); 87 | $crate::create_context!(($ctx) $($tt)*); 88 | }}; 89 | 90 | (($ctx:expr) $k:expr => Arc::new($($v:tt)*)) => {{ 91 | $ctx.set_func($k, Arc::new($($v)*)); 92 | }}; 93 | 94 | (($ctx:expr) $k:expr => $v:expr) => {{ 95 | $ctx.set_variable($k, Value::from($v)); 96 | }}; 97 | 98 | (($ctx:expr)) => {}; 99 | 100 | ($($tt:tt)*) => {{ 101 | use std::sync::Arc; 102 | let mut ctx = $crate::Context::new(); 103 | $crate::create_context!((&mut ctx) $($tt)*); 104 | ctx 105 | }}; 106 | } 107 | -------------------------------------------------------------------------------- /src/define.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use core::result; 3 | 4 | pub type Result = result::Result; 5 | -------------------------------------------------------------------------------- /src/descriptor.rs: -------------------------------------------------------------------------------- 1 | use once_cell::sync::OnceCell; 2 | use std::collections::HashMap; 3 | use std::sync::{Arc, Mutex}; 4 | 5 | #[derive(Hash, Eq, PartialEq)] 6 | enum DescriptorKey { 7 | UNARY(String), 8 | BINARY(String), 9 | POSTFIX(String), 10 | TERNARY, 11 | FUNCTION(String), 12 | REFERENCE(String), 13 | LIST, 14 | MAP, 15 | CHAIN, 16 | } 17 | 18 | #[derive(Clone)] 19 | enum Descriptor { 20 | UNARY(Arc), 21 | BINARY(Arc), 22 | POSTFIX(Arc), 23 | TERNARY(Arc), 24 | FUNCTION(Arc), 25 | REFERENCE(Arc), 26 | LIST(Arc), 27 | MAP(Arc), 28 | CHAIN(Arc), 29 | } 30 | 31 | type UnaryDescriptor = dyn Fn(String, String) -> String + Send + Sync + 'static; 32 | type BinaryDescriptor = dyn Fn(String, String, String) -> String + Send + Sync + 'static; 33 | type PostfixDescriptor = dyn Fn(String, String) -> String + Send + Sync + 'static; 34 | type TernaryDescriptor = dyn Fn(String, String, String) -> String + Send + Sync + 'static; 35 | type FunctionDescriptor = dyn Fn(String, Vec) -> String + Send + Sync + 'static; 36 | type ReferenceDescriptor = dyn Fn(String) -> String + Send + Sync + 'static; 37 | type ListDescriptor = dyn Fn(Vec) -> String + Send + Sync + 'static; 38 | type MapDescriptor = dyn Fn(Vec<(String, String)>) -> String + Send + Sync + 'static; 39 | type ChainDescriptor = dyn Fn(Vec) -> String + Send + Sync + 'static; 40 | 41 | pub struct DescriptorManager { 42 | store: &'static Mutex>, 43 | } 44 | 45 | impl DescriptorManager { 46 | pub fn new() -> Self { 47 | static STORE: OnceCell>> = OnceCell::new(); 48 | let store = STORE.get_or_init(|| Mutex::new(HashMap::new())); 49 | DescriptorManager { store } 50 | } 51 | 52 | fn set(&mut self, key: DescriptorKey, value: Descriptor) { 53 | let mut binding = self.store.lock().unwrap(); 54 | binding.insert(key, value); 55 | } 56 | 57 | fn get(&self, key: DescriptorKey) -> Option { 58 | let binding = self.store.lock().unwrap(); 59 | let value = binding.get(&key); 60 | if value.is_none() { 61 | return None; 62 | } 63 | Some(value.unwrap().clone()) 64 | } 65 | 66 | pub fn set_unary_descriptor(&mut self, op: String, descriptor: Arc) { 67 | let key = DescriptorKey::UNARY(op); 68 | let value = Descriptor::UNARY(descriptor); 69 | self.set(key, value) 70 | } 71 | 72 | pub fn get_unary_descriptor(&self, op: String) -> Arc { 73 | let key = DescriptorKey::UNARY(op); 74 | let v = self.get(key); 75 | if v.is_none() { 76 | return Arc::new(default_unary_descriptor); 77 | } 78 | match v.unwrap() { 79 | Descriptor::UNARY(f) => f.clone(), 80 | _ => Arc::new(default_unary_descriptor), 81 | } 82 | } 83 | 84 | pub fn set_binary_descriptor(&mut self, op: String, descriptor: Arc) { 85 | let key = DescriptorKey::BINARY(op); 86 | let value = Descriptor::BINARY(descriptor); 87 | self.set(key, value) 88 | } 89 | 90 | pub fn get_binary_descriptor(&self, op: String) -> Arc { 91 | let key = DescriptorKey::UNARY(op); 92 | let v = self.get(key); 93 | if v.is_none() { 94 | return Arc::new(default_binary_descriptor); 95 | } 96 | match v.unwrap() { 97 | Descriptor::BINARY(f) => f.clone(), 98 | _ => Arc::new(default_binary_descriptor), 99 | } 100 | } 101 | 102 | pub fn set_postfix_descriptor(&mut self, op: String, descriptor: Arc) { 103 | let key = DescriptorKey::POSTFIX(op); 104 | let value = Descriptor::POSTFIX(descriptor); 105 | self.set(key, value) 106 | } 107 | 108 | pub fn get_postfix_descriptor(&self, op: String) -> Arc { 109 | let key = DescriptorKey::POSTFIX(op); 110 | let v = self.get(key); 111 | if v.is_none() { 112 | return Arc::new(default_postfix_descriptor); 113 | } 114 | match v.unwrap() { 115 | Descriptor::POSTFIX(f) => f.clone(), 116 | _ => Arc::new(default_unary_descriptor), 117 | } 118 | } 119 | 120 | pub fn set_ternary_descriptor(&mut self, descriptor: Arc) { 121 | let key = DescriptorKey::TERNARY; 122 | let value = Descriptor::TERNARY(descriptor); 123 | self.set(key, value) 124 | } 125 | 126 | pub fn get_ternary_descriptor(&self) -> Arc { 127 | let key = DescriptorKey::TERNARY; 128 | let v = self.get(key); 129 | if v.is_none() { 130 | return Arc::new(default_ternary_descriptor); 131 | } 132 | match v.unwrap() { 133 | Descriptor::TERNARY(f) => f.clone(), 134 | _ => Arc::new(default_binary_descriptor), 135 | } 136 | } 137 | 138 | pub fn set_function_descriptor(&mut self, name: String, descriptor: Arc) { 139 | let key = DescriptorKey::FUNCTION(name); 140 | let value = Descriptor::FUNCTION(descriptor); 141 | self.set(key, value) 142 | } 143 | 144 | pub fn get_function_descriptor(&self, name: String) -> Arc { 145 | let key = DescriptorKey::FUNCTION(name); 146 | let v = self.get(key); 147 | if v.is_none() { 148 | return Arc::new(default_function_descriptor); 149 | } 150 | match v.unwrap() { 151 | Descriptor::FUNCTION(f) => f.clone(), 152 | _ => Arc::new(default_function_descriptor), 153 | } 154 | } 155 | 156 | pub fn set_reference_descriptor(&mut self, name: String, descriptor: Arc) { 157 | let key = DescriptorKey::REFERENCE(name); 158 | let value = Descriptor::REFERENCE(descriptor); 159 | self.set(key, value) 160 | } 161 | 162 | pub fn get_reference_descriptor(&self, name: String) -> Arc { 163 | let key = DescriptorKey::REFERENCE(name); 164 | let v = self.get(key); 165 | if v.is_none() { 166 | return Arc::new(default_reference_descriptor); 167 | } 168 | match v.unwrap() { 169 | Descriptor::REFERENCE(f) => f.clone(), 170 | _ => Arc::new(default_reference_descriptor), 171 | } 172 | } 173 | 174 | pub fn set_list_descriptor(&mut self, descriptor: Arc) { 175 | let key = DescriptorKey::LIST; 176 | let value = Descriptor::LIST(descriptor); 177 | self.set(key, value) 178 | } 179 | 180 | pub fn get_list_descriptor(&self) -> Arc { 181 | let key = DescriptorKey::LIST; 182 | let v = self.get(key); 183 | if v.is_none() { 184 | return Arc::new(default_list_descriptor); 185 | } 186 | match v.unwrap() { 187 | Descriptor::LIST(f) => f.clone(), 188 | _ => Arc::new(default_list_descriptor), 189 | } 190 | } 191 | 192 | pub fn set_map_descriptor(&mut self, descriptor: Arc) { 193 | let key = DescriptorKey::MAP; 194 | let value = Descriptor::MAP(descriptor); 195 | self.set(key, value) 196 | } 197 | 198 | pub fn get_map_descriptor(&self) -> Arc { 199 | let key = DescriptorKey::MAP; 200 | let v = self.get(key); 201 | if v.is_none() { 202 | return Arc::new(default_map_descriptor); 203 | } 204 | match v.unwrap() { 205 | Descriptor::MAP(f) => f.clone(), 206 | _ => Arc::new(default_map_descriptor), 207 | } 208 | } 209 | 210 | pub fn set_chain_descriptor(&mut self, descriptor: Arc) { 211 | let key = DescriptorKey::CHAIN; 212 | let value = Descriptor::CHAIN(descriptor); 213 | self.set(key, value) 214 | } 215 | 216 | pub fn get_chain_descriptor(&self) -> Arc { 217 | let key = DescriptorKey::CHAIN; 218 | let v = self.get(key); 219 | if v.is_none() { 220 | return Arc::new(default_chain_descriptor); 221 | } 222 | match v.unwrap() { 223 | Descriptor::CHAIN(f) => f.clone(), 224 | _ => Arc::new(default_chain_descriptor), 225 | } 226 | } 227 | } 228 | 229 | fn default_unary_descriptor(op: String, rhs: String) -> String { 230 | op + &rhs 231 | } 232 | 233 | fn default_binary_descriptor(op: String, lhs: String, rhs: String) -> String { 234 | lhs + &op + &rhs 235 | } 236 | 237 | fn default_postfix_descriptor(lhs: String, op: String) -> String { 238 | lhs + &op 239 | } 240 | 241 | fn default_ternary_descriptor(condition: String, lhs: String, rhs: String) -> String { 242 | condition + "?" + &lhs + ":" + &rhs 243 | } 244 | 245 | fn default_function_descriptor(name: String, params: Vec) -> String { 246 | name + "(" + ¶ms.join(",") + ")" 247 | } 248 | 249 | fn default_reference_descriptor(name: String) -> String { 250 | name 251 | } 252 | 253 | fn default_list_descriptor(params: Vec) -> String { 254 | "[".to_string() + ¶ms.join(",") + "]" 255 | } 256 | 257 | fn default_map_descriptor(m: Vec<(String, String)>) -> String { 258 | let mut tmp = Vec::new(); 259 | for (k, v) in m { 260 | tmp.push(k + ":" + &v) 261 | } 262 | "{".to_string() + &tmp.join(",") + "}" 263 | } 264 | 265 | fn default_chain_descriptor(params: Vec) -> String { 266 | params.join(";") 267 | } 268 | 269 | #[cfg(test)] 270 | mod tests { 271 | use super::default_binary_descriptor; 272 | use super::default_chain_descriptor; 273 | use super::default_function_descriptor; 274 | use super::default_list_descriptor; 275 | use super::default_map_descriptor; 276 | use super::default_postfix_descriptor; 277 | use super::default_reference_descriptor; 278 | use super::default_ternary_descriptor; 279 | use super::default_unary_descriptor; 280 | use super::DescriptorManager; 281 | use std::sync::Arc; 282 | 283 | #[test] 284 | fn test_register() { 285 | DescriptorManager::new() 286 | .set_binary_descriptor("haha".to_string(), Arc::new(default_binary_descriptor)); 287 | DescriptorManager::new().set_chain_descriptor(Arc::new(default_chain_descriptor)); 288 | DescriptorManager::new() 289 | .set_function_descriptor("haha".to_string(), Arc::new(default_function_descriptor)); 290 | DescriptorManager::new().set_list_descriptor(Arc::new(default_list_descriptor)); 291 | DescriptorManager::new().set_map_descriptor(Arc::new(default_map_descriptor)); 292 | DescriptorManager::new() 293 | .set_reference_descriptor("haha".to_string(), Arc::new(default_reference_descriptor)); 294 | DescriptorManager::new().set_ternary_descriptor(Arc::new(default_ternary_descriptor)); 295 | DescriptorManager::new() 296 | .set_unary_descriptor("haha".to_string(), Arc::new(default_unary_descriptor)); 297 | DescriptorManager::new() 298 | .set_postfix_descriptor("haha".to_string(), Arc::new(default_postfix_descriptor)) 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Debug)] 4 | pub enum Error { 5 | InvalidNumber(String), 6 | UnexpectedEOF(usize), 7 | UnterminatedString(usize), 8 | InvalidBool(usize), 9 | NotSupportedChar(usize, char), 10 | ReferenceNotExist(String), 11 | FunctionNotExist(String), 12 | NotSupportedOp(String), 13 | InfixOpNotRegistered(String), 14 | PrefixOpNotRegistered(String), 15 | InnerFunctionNotRegistered(String), 16 | ShouldBeNumber(), 17 | ShouldBeBool(), 18 | ShouldBeList(), 19 | ShouldBeMap(), 20 | ParamInvalid(), 21 | ShouldBeString(), 22 | InvalidTernaryExprNeedColon(), 23 | ExpectedOpNotExist(String), 24 | WrongContextValueType(), 25 | UnexpectedToken(), 26 | NotReferenceExpr, 27 | NoOpenDelim, 28 | NoCloseDelim, 29 | InvalidOp(String), 30 | InvalidInteger, 31 | InvalidFloat, 32 | ExpectBinOpToken, 33 | } 34 | 35 | #[cfg(not(tarpaulin_include))] 36 | impl fmt::Display for Error { 37 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 38 | use Error::*; 39 | match self { 40 | InvalidNumber(s) => write!(f, "invalid number: {}", s), 41 | UnexpectedEOF(start) => write!(f, "unexpected eof: {}", start), 42 | UnterminatedString(start) => write!(f, "unterminated string: {}", start), 43 | InvalidBool(start) => write!(f, "invalid bool: {}", start), 44 | NotSupportedChar(start, ch) => write!(f, "not supported char: {}, {}", start, ch), 45 | ReferenceNotExist(name) => write!(f, "reference not exist: {}", name), 46 | FunctionNotExist(name) => write!(f, "function not exist: {}", name), 47 | NotSupportedOp(op) => write!(f, "not supported op: {}", op), 48 | InfixOpNotRegistered(op) => write!(f, "binary op not registered: {}", op), 49 | PrefixOpNotRegistered(op) => write!(f, "unary op not registered: {}", op), 50 | InnerFunctionNotRegistered(name) => { 51 | write!(f, "inner function not registered: {}", name) 52 | } 53 | ShouldBeNumber() => write!(f, "should be number"), 54 | ShouldBeBool() => write!(f, "should be bool"), 55 | ShouldBeList() => write!(f, "should be list"), 56 | ShouldBeMap() => write!(f, "should be map"), 57 | InvalidTernaryExprNeedColon() => write!(f, "invalid ternary expr needs colon"), 58 | ExpectedOpNotExist(op) => write!(f, "expected op:{} not exist", op.clone()), 59 | ParamInvalid() => write!(f, "param invalid"), 60 | ShouldBeString() => write!(f, "should be string"), 61 | WrongContextValueType() => write!(f, "wrong context value type"), 62 | UnexpectedToken() => write!(f, "unexpected token"), 63 | NotReferenceExpr => write!(f, "not reference expr"), 64 | NoOpenDelim => write!(f, "no open delim"), 65 | NoCloseDelim => write!(f, "no close delim"), 66 | InvalidOp(op) => write!(f, "invalid op {}", op), 67 | InvalidInteger => write!(f, "invalid integer"), 68 | InvalidFloat => write!(f, "invalid float"), 69 | ExpectBinOpToken => write!(f, "expect bin op token"), 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/function.rs: -------------------------------------------------------------------------------- 1 | use crate::define::Result; 2 | use crate::error::Error; 3 | use crate::value::Value; 4 | use once_cell::sync::OnceCell; 5 | use rust_decimal::Decimal; 6 | use std::collections::HashMap; 7 | use std::sync::{Arc, Mutex}; 8 | 9 | pub type InnerFunction = dyn Fn(Vec) -> Result + Send + Sync + 'static; 10 | 11 | pub struct InnerFunctionManager { 12 | pub store: &'static Mutex>>, 13 | } 14 | 15 | impl InnerFunctionManager { 16 | pub fn new() -> Self { 17 | static STORE: OnceCell>>> = OnceCell::new(); 18 | let store = STORE.get_or_init(|| Mutex::new(HashMap::new())); 19 | InnerFunctionManager { store: store } 20 | } 21 | 22 | pub fn init(&mut self) { 23 | self.register( 24 | "min", 25 | Arc::new(|params| { 26 | let mut min = None; 27 | for param in params.into_iter() { 28 | let num = param.decimal()?; 29 | if min.is_none() || num < min.unwrap() { 30 | min = Some(num); 31 | } 32 | } 33 | Ok(Value::Number(min.unwrap())) 34 | }), 35 | ); 36 | 37 | self.register( 38 | "max", 39 | Arc::new(|params| { 40 | let mut max = None; 41 | for param in params.into_iter() { 42 | let num = param.decimal()?; 43 | if max.is_none() || num > max.unwrap() { 44 | max = Some(num); 45 | } 46 | } 47 | Ok(Value::Number(max.unwrap())) 48 | }), 49 | ); 50 | 51 | self.register( 52 | "sum", 53 | Arc::new(|params| { 54 | let mut ans = Decimal::ZERO; 55 | for param in params.into_iter() { 56 | ans += param.decimal()?; 57 | } 58 | Ok(Value::Number(ans)) 59 | }), 60 | ); 61 | 62 | self.register( 63 | "mul", 64 | Arc::new(|params| { 65 | let mut ans = Decimal::ONE; 66 | for param in params.into_iter() { 67 | ans *= param.decimal()?; 68 | } 69 | Ok(Value::Number(ans)) 70 | }), 71 | ); 72 | } 73 | 74 | pub fn register(&mut self, name: &str, f: Arc) { 75 | self.store.lock().unwrap().insert(name.to_string(), f); 76 | } 77 | 78 | pub fn get(&self, name: &str) -> Result> { 79 | let binding = self.store.lock().unwrap(); 80 | let ans = binding.get(name); 81 | if ans.is_none() { 82 | return Err(Error::InnerFunctionNotRegistered(String::from(name))); 83 | } 84 | Ok(ans.unwrap().clone()) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/init.rs: -------------------------------------------------------------------------------- 1 | use crate::function::InnerFunctionManager; 2 | use crate::operator::{InfixOpManager, PostfixOpManager, PrefixOpManager}; 3 | use once_cell::sync::OnceCell; 4 | 5 | pub fn init() { 6 | static INITED: OnceCell<()> = OnceCell::new(); 7 | INITED.get_or_init(|| { 8 | PrefixOpManager::new().init(); 9 | InfixOpManager::new().init(); 10 | PostfixOpManager::new().init(); 11 | InnerFunctionManager::new().init(); 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /src/keyword.rs: -------------------------------------------------------------------------------- 1 | use crate::operator::{InfixOpManager, PostfixOpManager, PrefixOpManager}; 2 | 3 | pub fn is_prefix_op(op: &str) -> bool { 4 | PrefixOpManager::new().exist(op) 5 | } 6 | 7 | pub fn is_infix_op(op: &str) -> bool { 8 | InfixOpManager::new().exist(op) 9 | } 10 | 11 | pub fn is_postfix_op(op: &str) -> bool { 12 | PostfixOpManager::new().exist(op) 13 | } 14 | 15 | pub fn is_ternary_op(op: &str) -> bool { 16 | op == "?" || op == ":" 17 | } 18 | 19 | pub fn is_op(op: &str) -> bool { 20 | is_prefix_op(op) || is_infix_op(op) || is_postfix_op(op) || is_ternary_op(op) 21 | } 22 | 23 | pub fn is_not(op: &str) -> bool { 24 | op == "not" 25 | } 26 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Expression engine is a library written in pure Rust which provides an engine to compile and execute expressions. 2 | //! An expression indicates a string-like sentence that can be executed with some contexts and return a value (mostly, but not limited to, boolean, string and number). 3 | //! Expression engine aims to provide an engine for users that can execute complex logics using configurations without recompiling. 4 | //! It's a proper alternative as the basis to build business rule engines. 5 | //! ## Features 6 | 7 | //! + Easy to Use (three lines at least) 8 | //! + Abundant Types and Expressions (Five fundamental types and seven kinds of expressions) 9 | //! + Pre-defined Operators Support (Common boolean, numeric and string operators) 10 | //! + Support function and operators registration 11 | //! + Support operator redirection 12 | mod define; 13 | mod error; 14 | mod parser; 15 | #[macro_use] 16 | mod function; 17 | mod keyword; 18 | mod operator; 19 | mod token; 20 | mod tokenizer; 21 | #[macro_use] 22 | mod value; 23 | mod context; 24 | mod descriptor; 25 | mod init; 26 | use std::sync::Arc; 27 | 28 | /// ## Usage 29 | /// 30 | /// Calling the engine is simple. At first, define the expression you want to execute. Secondly, create a context to cache the pre-defined inner functions and variables. And then, register the variables and functions to the context. Finally, call the execute function with the expression and context to get the executing result. 31 | /// 32 | /// ``` rust 33 | /// use expression_engine::{create_context, execute, Value}; 34 | /// let input = "c = 5+3; c+=10+f; c"; 35 | /// let ctx = create_context!( 36 | /// "d" => 2, 37 | /// "b" => true, 38 | /// "f" => Arc::new(|params| Ok(Value::from(3))) 39 | /// ); 40 | /// let ans = execute(input, ctx).unwrap(); 41 | /// assert_eq!(ans, Value::from(21)) 42 | /// ``` 43 | pub fn execute(expr: &str, mut ctx: context::Context) -> Result { 44 | parse_expression(expr)?.exec(&mut ctx) 45 | } 46 | 47 | /// ## Usage 48 | /// 49 | /// You can easily parse a string into ExprAST via this method. 50 | /// 51 | /// ``` rust 52 | /// use expression_engine::parse_expression; 53 | /// let input = "a + 3*2+test()+[1,2,3,'haha']"; 54 | /// let ast = parse_expression(input); 55 | /// assert!(ast.is_ok()); 56 | /// ``` 57 | pub fn parse_expression(expr: &str) -> Result { 58 | init(); 59 | parser::Parser::new(expr)?.parse_stmt() 60 | } 61 | 62 | /// ## Usage 63 | /// 64 | /// You can register some inner functions in advance via this method 65 | /// 66 | /// ``` rust 67 | /// use std::sync::Arc; 68 | /// use expression_engine::{register_function, create_context, execute, Value}; 69 | /// register_function("test", Arc::new(|_| return Ok(Value::from("test")))); 70 | /// let input = "test()"; 71 | /// let ans = execute(input, create_context!()); 72 | /// assert!(ans.is_ok()); 73 | /// assert_eq!(ans.unwrap(), Value::from("test")); 74 | /// ``` 75 | pub fn register_function(name: &str, handler: Arc) { 76 | use crate::function::InnerFunctionManager; 77 | init(); 78 | InnerFunctionManager::new().register(name, handler); 79 | } 80 | 81 | /// ## Usage 82 | /// 83 | /// You can register some prefix operators in advance via this method 84 | /// 85 | /// ``` rust 86 | /// use std::sync::Arc; 87 | /// use expression_engine::{register_prefix_op, create_context, execute, Value}; 88 | /// register_prefix_op( 89 | /// "+++", 90 | /// Arc::new(|v| { 91 | /// let mut tmp = v.integer()?; 92 | /// tmp += 3; 93 | /// Ok(Value::from(tmp)) 94 | /// }), 95 | /// ); 96 | /// let input = "+++11"; 97 | /// let ans = execute(input, create_context!()); 98 | /// assert!(ans.is_ok()); 99 | /// assert_eq!(ans.unwrap(), Value::from(14)); 100 | /// ``` 101 | pub fn register_prefix_op(op: &str, handler: Arc) { 102 | use crate::operator::PrefixOpManager; 103 | init(); 104 | PrefixOpManager::new().register(op, handler); 105 | } 106 | 107 | /// ## Usage 108 | /// 109 | /// You can register some postfix operators in advance via this method 110 | /// 111 | /// ``` rust 112 | /// use std::sync::Arc; 113 | /// use expression_engine::{register_postfix_op, create_context, execute, Value}; 114 | /// register_postfix_op( 115 | /// "---", 116 | /// Arc::new(|v| { 117 | /// let mut tmp = v.integer()?; 118 | /// tmp -= 3; 119 | /// Ok(Value::from(tmp)) 120 | /// }), 121 | /// ); 122 | /// let input = "100---"; 123 | /// let ans = execute(input, create_context!()); 124 | /// assert!(ans.is_ok()); 125 | /// assert_eq!(ans.unwrap(), Value::from(97)); 126 | /// ``` 127 | pub fn register_postfix_op(op: &str, handler: Arc) { 128 | use crate::operator::PostfixOpManager; 129 | init(); 130 | PostfixOpManager::new().register(op, handler); 131 | } 132 | 133 | /// ## Usage 134 | /// 135 | /// You can register some infix operators in advance via this method 136 | /// 137 | /// ``` rust 138 | /// use std::sync::Arc; 139 | /// use expression_engine::{register_infix_op, create_context, execute, Value, InfixOpAssociativity, InfixOpType,}; 140 | /// register_infix_op( 141 | /// "---", 142 | /// 100, 143 | /// InfixOpType::CALC, 144 | /// InfixOpAssociativity::RIGHT, 145 | /// Arc::new(|left, right| Ok(Value::from(left.integer()? - right.integer()?))), 146 | /// ); 147 | /// let input = "100---55---44"; 148 | /// let ans = execute(input, create_context!()); 149 | /// assert!(ans.is_ok()); 150 | /// assert_eq!(ans.unwrap(), Value::from(89)); 151 | /// ``` 152 | pub fn register_infix_op( 153 | op: &str, 154 | precedence: i32, 155 | op_type: InfixOpType, 156 | associativity: InfixOpAssociativity, 157 | handler: Arc, 158 | ) { 159 | use crate::operator::InfixOpManager; 160 | init(); 161 | InfixOpManager::new().register(op, precedence, op_type, associativity, handler); 162 | } 163 | 164 | fn init() { 165 | use crate::init::init; 166 | init(); 167 | } 168 | 169 | pub type Value = value::Value; 170 | pub type Context = context::Context; 171 | pub type Result = define::Result; 172 | pub type ExprAST<'a> = parser::ExprAST<'a>; 173 | pub type InfixOpType = operator::InfixOpType; 174 | pub type InfixOpAssociativity = operator::InfixOpAssociativity; 175 | 176 | #[cfg(test)] 177 | mod tests { 178 | use crate::{ 179 | create_context, execute, parse_expression, register_function, register_infix_op, 180 | register_postfix_op, register_prefix_op, InfixOpAssociativity, InfixOpType, Value, 181 | }; 182 | use std::sync::Arc; 183 | #[test] 184 | fn test_execute() { 185 | let input = "c = 5+3; c+=10+f; c"; 186 | let ctx = create_context!( 187 | "d" => 2, 188 | "b" => true, 189 | "f" => Arc::new(|_| Ok(Value::from(3))) 190 | ); 191 | let ans = execute(input, ctx).unwrap(); 192 | assert_eq!(ans, 21.into()) 193 | } 194 | 195 | #[test] 196 | fn test_parse_expression() { 197 | let input = "a + 3*2+test()+[1,2,3,'haha']"; 198 | assert!(parse_expression(input).is_ok()); 199 | } 200 | 201 | #[test] 202 | fn test_register_function() { 203 | register_function("test", Arc::new(|_| return Ok(Value::from("test")))); 204 | let input = "test()"; 205 | let ans = execute(input, create_context!()); 206 | assert!(ans.is_ok()); 207 | assert_eq!(ans.unwrap(), Value::from("test")); 208 | } 209 | 210 | #[test] 211 | fn test_register_prefix_op() { 212 | register_prefix_op( 213 | "+++", 214 | Arc::new(|v| { 215 | let mut tmp = v.integer()?; 216 | tmp += 3; 217 | Ok(Value::from(tmp)) 218 | }), 219 | ); 220 | let input = "+++11"; 221 | let ans = execute(input, create_context!()); 222 | assert!(ans.is_ok()); 223 | assert_eq!(ans.unwrap(), Value::from(14)); 224 | } 225 | 226 | #[test] 227 | fn test_register_postfix_op() { 228 | register_postfix_op( 229 | "---", 230 | Arc::new(|v| { 231 | let mut tmp = v.integer()?; 232 | tmp -= 3; 233 | Ok(Value::from(tmp)) 234 | }), 235 | ); 236 | let input = "100---"; 237 | let ans = execute(input, create_context!()); 238 | assert!(ans.is_ok()); 239 | assert_eq!(ans.unwrap(), Value::from(97)); 240 | } 241 | 242 | #[test] 243 | fn test_register_infix_op() { 244 | register_infix_op( 245 | "right_minus", 246 | 100, 247 | InfixOpType::CALC, 248 | InfixOpAssociativity::RIGHT, 249 | Arc::new(|left, right| Ok(Value::from(left.integer()? - right.integer()?))), 250 | ); 251 | let input = "100 right_minus 55 right_minus 44"; 252 | let ans = execute(input, create_context!()); 253 | match &ans { 254 | Ok(_) => {} 255 | Err(e) => print!("err is {}", e), 256 | } 257 | assert!(ans.is_ok()); 258 | assert_eq!(ans.unwrap(), Value::from(89)); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /src/operator.rs: -------------------------------------------------------------------------------- 1 | use crate::define::Result; 2 | use crate::error::Error; 3 | use crate::value::Value; 4 | use once_cell::sync::OnceCell; 5 | use rust_decimal::prelude::FromPrimitive; 6 | use rust_decimal::Decimal; 7 | use std::collections::HashMap; 8 | use std::sync::{Arc, Mutex}; 9 | 10 | pub type InfixOpFunc = dyn Fn(Value, Value) -> Result + Send + Sync + 'static; 11 | 12 | pub type PrefixOpFunc = dyn Fn(Value) -> Result + Send + Sync + 'static; 13 | 14 | pub type PostfixOpFunc = dyn Fn(Value) -> Result + Send + Sync + 'static; 15 | 16 | #[derive(Clone)] 17 | pub enum InfixOpType { 18 | CALC, 19 | SETTER, 20 | } 21 | 22 | #[derive(Clone, PartialEq)] 23 | pub enum InfixOpAssociativity { 24 | LEFT, 25 | RIGHT, 26 | } 27 | 28 | #[derive(Clone)] 29 | pub struct InfixOpConfig( 30 | pub i32, 31 | pub InfixOpType, 32 | pub InfixOpAssociativity, 33 | pub Arc, 34 | ); 35 | 36 | pub struct InfixOpManager { 37 | store: &'static Mutex>, 38 | } 39 | 40 | pub struct PrefixOpManager { 41 | store: &'static Mutex>>, 42 | } 43 | 44 | pub struct PostfixOpManager { 45 | store: &'static Mutex>>, 46 | } 47 | 48 | impl InfixOpManager { 49 | pub fn new() -> Self { 50 | static STORE: OnceCell>> = OnceCell::new(); 51 | let store = STORE.get_or_init(|| Mutex::new(HashMap::new())); 52 | InfixOpManager { store: store } 53 | } 54 | 55 | pub fn init(&mut self) { 56 | use InfixOpAssociativity::*; 57 | use InfixOpType::*; 58 | self.register("=", 20, SETTER, RIGHT, Arc::new(|_, right| Ok(right))); 59 | 60 | for op in vec!["+=", "-=", "*=", "/=", "%="] { 61 | self.register( 62 | op, 63 | 20, 64 | SETTER, 65 | RIGHT, 66 | Arc::new(move |left, right| { 67 | let (mut a, b) = (left.decimal()?, right.decimal()?); 68 | match op { 69 | "+=" => a += b, 70 | "-=" => a -= b, 71 | "*=" => a *= b, 72 | "/=" => a /= b, 73 | "%=" => a %= b, 74 | _ => (), 75 | } 76 | Ok(Value::Number(a)) 77 | }), 78 | ); 79 | } 80 | 81 | for op in vec!["<<=", ">>=", "&=", "^=", "|="] { 82 | self.register( 83 | op, 84 | 20, 85 | SETTER, 86 | RIGHT, 87 | Arc::new(move |left, right| { 88 | let (mut a, b) = (left.integer()?, right.integer()?); 89 | match op { 90 | "<<=" => a <<= b, 91 | ">>=" => a >>= b, 92 | "&=" => a &= b, 93 | "^=" => a ^= b, 94 | "|=" => a |= b, 95 | _ => (), 96 | } 97 | Ok(Value::from(a)) 98 | }), 99 | ); 100 | } 101 | 102 | for (op, precedence) in vec![("||", 40), ("&&", 50)] { 103 | self.register( 104 | op, 105 | precedence, 106 | CALC, 107 | LEFT, 108 | Arc::new(move |left, right| { 109 | let (mut a, b) = (left.bool()?, right.bool()?); 110 | match op { 111 | "||" => a = a || b, 112 | "&&" => a = a && b, 113 | _ => (), 114 | } 115 | Ok(Value::from(a)) 116 | }), 117 | ); 118 | } 119 | 120 | for op in vec!["<", "<=", ">", ">="] { 121 | self.register( 122 | op, 123 | 60, 124 | CALC, 125 | LEFT, 126 | Arc::new(move |left, right| { 127 | let (a, b) = (left.decimal()?, right.decimal()?); 128 | let mut value = false; 129 | match op { 130 | "<" => value = a < b, 131 | "<=" => value = a <= b, 132 | ">" => value = a > b, 133 | ">=" => value = a >= b, 134 | _ => (), 135 | } 136 | Ok(Value::from(value)) 137 | }), 138 | ); 139 | } 140 | 141 | for op in vec!["==", "!="] { 142 | self.register( 143 | op, 144 | 60, 145 | CALC, 146 | LEFT, 147 | Arc::new(move |left, right| { 148 | let mut value = false; 149 | match op { 150 | "==" => value = left == right, 151 | "!=" => value = left != right, 152 | _ => (), 153 | } 154 | Ok(Value::from(value)) 155 | }), 156 | ); 157 | } 158 | 159 | for (op, precedence) in vec![("|", 70), ("^", 80), ("&", 90), ("<<", 100), (">>", 100)] { 160 | self.register( 161 | op, 162 | precedence, 163 | CALC, 164 | LEFT, 165 | Arc::new(move |left, right| { 166 | let (mut a, b) = (left.integer()?, right.integer()?); 167 | match op { 168 | "|" => a |= b, 169 | "^" => a ^= b, 170 | "&" => a &= b, 171 | "<<" => a <<= b, 172 | ">>" => a >>= b, 173 | _ => (), 174 | } 175 | Ok(Value::from(a)) 176 | }), 177 | ); 178 | } 179 | 180 | for (op, precedence) in vec![("+", 110), ("-", 110), ("*", 120), ("/", 120), ("%", 120)] { 181 | self.register( 182 | op, 183 | precedence, 184 | CALC, 185 | LEFT, 186 | Arc::new(move |left, right| { 187 | let (mut a, b) = (left.decimal()?, right.decimal()?); 188 | match op { 189 | "+" => a += b, 190 | "-" => a -= b, 191 | "*" => a *= b, 192 | "/" => a /= b, 193 | "%" => a %= b, 194 | _ => (), 195 | } 196 | Ok(Value::from(a)) 197 | }), 198 | ); 199 | } 200 | 201 | self.register( 202 | "beginWith", 203 | 200, 204 | CALC, 205 | LEFT, 206 | Arc::new(|left, right| { 207 | let (a, b) = (left.string()?, right.string()?); 208 | Ok(Value::from(a.starts_with(&b))) 209 | }), 210 | ); 211 | 212 | self.register( 213 | "endWith", 214 | 200, 215 | CALC, 216 | LEFT, 217 | Arc::new(|left, right| { 218 | let (a, b) = (left.string()?, right.string()?); 219 | Ok(Value::from(a.ends_with(&b))) 220 | }), 221 | ); 222 | 223 | self.register( 224 | "in", 225 | 200, 226 | InfixOpType::CALC, 227 | InfixOpAssociativity::LEFT, 228 | Arc::new(|left, right| { 229 | let list = right.list()?; 230 | for item in list { 231 | if item == left { 232 | return Ok(true.into()); 233 | } 234 | } 235 | Ok(false.into()) 236 | }), 237 | ); 238 | } 239 | 240 | pub fn register( 241 | &mut self, 242 | op: &str, 243 | precidence: i32, 244 | op_type: InfixOpType, 245 | op_associativity: InfixOpAssociativity, 246 | f: Arc, 247 | ) { 248 | self.store.lock().unwrap().insert( 249 | op.to_string(), 250 | InfixOpConfig(precidence, op_type, op_associativity, f), 251 | ); 252 | } 253 | 254 | pub fn get_handler(&self, op: &str) -> Result> { 255 | Ok(self.get(op)?.3) 256 | } 257 | 258 | pub fn get_precidence(&self, op: &str) -> (i32, i32) { 259 | let ans = self.get(op); 260 | if ans.is_err() { 261 | return (-1, -1); 262 | } 263 | let config = ans.unwrap(); 264 | let l_bp = config.0; 265 | let mut r_bp = 0; 266 | if config.2 == InfixOpAssociativity::LEFT { 267 | r_bp = l_bp + 1; 268 | } else if config.2 == InfixOpAssociativity::RIGHT { 269 | r_bp = l_bp - 1; 270 | } 271 | (l_bp, r_bp) 272 | } 273 | 274 | pub fn get_op_type(&self, op: &str) -> Result { 275 | Ok(self.get(op)?.1) 276 | } 277 | 278 | pub fn get(&self, op: &str) -> Result { 279 | let binding = self.store.lock().unwrap(); 280 | let ans = binding.get(op); 281 | if ans.is_none() { 282 | return Err(Error::InfixOpNotRegistered(op.to_string())); 283 | } 284 | Ok(ans.unwrap().clone()) 285 | } 286 | 287 | pub fn operators(&self) -> Vec<(String, i32)> { 288 | let mut ans = vec![]; 289 | let binding = self.store.lock().unwrap(); 290 | for (op, InfixOpConfig(precedence, _, _, _)) in binding.iter() { 291 | ans.push((op.clone(), precedence.clone())); 292 | } 293 | ans.sort_by(|a, b| a.1.cmp(&b.1)); 294 | ans 295 | } 296 | 297 | pub fn exist(&self, op: &str) -> bool { 298 | let binding = self.store.lock().unwrap(); 299 | binding.get(op).is_some() 300 | } 301 | } 302 | 303 | impl PrefixOpManager { 304 | pub fn new() -> Self { 305 | static STORE: OnceCell>>> = OnceCell::new(); 306 | let store = STORE.get_or_init(|| Mutex::new(HashMap::new())); 307 | PrefixOpManager { store: store } 308 | } 309 | 310 | pub fn init(&mut self) { 311 | self.register( 312 | "-", 313 | Arc::new(|param| { 314 | let a = match param { 315 | Value::Number(a) => a, 316 | _ => return Err(Error::ShouldBeNumber()), 317 | }; 318 | Ok(Value::Number(-a)) 319 | }), 320 | ); 321 | 322 | self.register( 323 | "+", 324 | Arc::new(|param| { 325 | let a = match param { 326 | Value::Number(a) => a, 327 | _ => return Err(Error::ShouldBeNumber()), 328 | }; 329 | Ok(Value::Number(a)) 330 | }), 331 | ); 332 | 333 | self.register( 334 | "!", 335 | Arc::new(|param| { 336 | let a = match param { 337 | Value::Bool(value) => !value, 338 | _ => return Err(Error::ShouldBeBool()), 339 | }; 340 | Ok(Value::Bool(a)) 341 | }), 342 | ); 343 | 344 | self.register( 345 | "not", 346 | Arc::new(|param| { 347 | let a = match param { 348 | Value::Bool(value) => !value, 349 | _ => return Err(Error::ShouldBeBool()), 350 | }; 351 | Ok(Value::Bool(a)) 352 | }), 353 | ); 354 | 355 | self.register( 356 | "AND", 357 | Arc::new(|value| { 358 | let list = value.list()?; 359 | for value in list { 360 | if !value.bool()? { 361 | return Ok(false.into()); 362 | } 363 | } 364 | Ok(true.into()) 365 | }), 366 | ); 367 | 368 | self.register( 369 | "OR", 370 | Arc::new(|value| { 371 | let list = value.list()?; 372 | for value in list { 373 | if value.bool()? { 374 | return Ok(true.into()); 375 | } 376 | } 377 | Ok(false.into()) 378 | }), 379 | ); 380 | } 381 | 382 | pub fn register(&mut self, op: &str, f: Arc) { 383 | self.store.lock().unwrap().insert(op.to_string(), f); 384 | } 385 | 386 | pub fn get(&self, op: &str) -> Result> { 387 | let binding = self.store.lock().unwrap(); 388 | let ans = binding.get(op); 389 | if ans.is_none() { 390 | return Err(Error::PrefixOpNotRegistered(op.to_string())); 391 | } 392 | Ok(ans.unwrap().clone()) 393 | } 394 | 395 | pub fn exist(&self, op: &str) -> bool { 396 | let binding = self.store.lock().unwrap(); 397 | binding.get(op).is_some() 398 | } 399 | } 400 | 401 | impl PostfixOpManager { 402 | pub fn new() -> Self { 403 | static STORE: OnceCell>>> = OnceCell::new(); 404 | let store = STORE.get_or_init(|| Mutex::new(HashMap::new())); 405 | Self { store: store } 406 | } 407 | 408 | pub fn init(&mut self) { 409 | self.register( 410 | "++", 411 | Arc::new(|param| { 412 | let a = match param { 413 | Value::Number(a) => a + Decimal::from_i32(1).unwrap(), 414 | _ => return Err(Error::ShouldBeNumber()), 415 | }; 416 | Ok(Value::Number(a)) 417 | }), 418 | ); 419 | 420 | self.register( 421 | "--", 422 | Arc::new(|param| { 423 | let a = match param { 424 | Value::Number(a) => a - Decimal::from_i32(1).unwrap(), 425 | _ => return Err(Error::ShouldBeNumber()), 426 | }; 427 | Ok(Value::Number(a)) 428 | }), 429 | ); 430 | } 431 | 432 | pub fn register(&mut self, op: &str, f: Arc) { 433 | self.store.lock().unwrap().insert(op.to_string(), f); 434 | } 435 | 436 | pub fn get(&self, op: &str) -> Result> { 437 | let binding = self.store.lock().unwrap(); 438 | let ans = binding.get(op); 439 | if ans.is_none() { 440 | return Err(Error::PrefixOpNotRegistered(op.to_string())); 441 | } 442 | Ok(ans.unwrap().clone()) 443 | } 444 | 445 | pub fn exist(&self, op: &str) -> bool { 446 | let binding = self.store.lock().unwrap(); 447 | binding.get(op).is_some() 448 | } 449 | } 450 | 451 | #[cfg(test)] 452 | mod tetst { 453 | use crate::operator::InfixOpManager; 454 | #[test] 455 | fn test_operators() { 456 | let result = InfixOpManager::new().operators(); 457 | for (op, precedence) in result { 458 | println!("|{}| {}||", op, precedence) 459 | } 460 | } 461 | } 462 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::context::Context; 2 | use crate::define::*; 3 | use crate::descriptor::DescriptorManager; 4 | use crate::error::Error; 5 | use crate::function::InnerFunctionManager; 6 | use crate::operator::{InfixOpManager, InfixOpType, PostfixOpManager, PrefixOpManager}; 7 | use crate::token::{DelimTokenType, Token}; 8 | use crate::tokenizer::Tokenizer; 9 | use crate::value::Value; 10 | use rust_decimal::prelude::*; 11 | use std::fmt; 12 | 13 | #[derive(Clone, PartialEq, Eq, Debug)] 14 | pub enum Literal<'a> { 15 | Number(Decimal), 16 | Bool(bool), 17 | String(&'a str), 18 | } 19 | 20 | #[cfg(not(tarpaulin_include))] 21 | impl<'a> fmt::Display for Literal<'a> { 22 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 23 | use Literal::*; 24 | match self { 25 | Number(value) => write!(f, "Number: {}", value.clone()), 26 | Bool(value) => write!(f, "Bool: {}", value.clone()), 27 | String(value) => write!(f, "String: {}", *value), 28 | } 29 | } 30 | } 31 | 32 | #[derive(Clone, PartialEq, Eq, Debug)] 33 | pub enum ExprAST<'a> { 34 | Literal(Literal<'a>), 35 | Unary(&'a str, Box>), 36 | Binary(&'a str, Box>, Box>), 37 | Postfix(Box>, String), 38 | Ternary(Box>, Box>, Box>), 39 | Reference(&'a str), 40 | Function(&'a str, Vec>), 41 | List(Vec>), 42 | Map(Vec<(ExprAST<'a>, ExprAST<'a>)>), 43 | Stmt(Vec>), 44 | None, 45 | } 46 | 47 | #[cfg(not(tarpaulin_include))] 48 | impl<'a> fmt::Display for ExprAST<'a> { 49 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 50 | match self { 51 | Self::Literal(val) => write!(f, "Literal AST: {}", val.clone()), 52 | Self::Unary(op, rhs) => { 53 | write!(f, "Unary AST: Op: {}, Rhs: {}", op, rhs.clone()) 54 | } 55 | Self::Binary(op, lhs, rhs) => write!( 56 | f, 57 | "Binary AST: Op: {}, Lhs: {}, Rhs: {}", 58 | op, 59 | lhs.clone(), 60 | rhs.clone() 61 | ), 62 | Self::Postfix(lhs, op) => { 63 | write!(f, "Postfix AST: Lhs: {}, Op: {}", lhs.clone(), op.clone(),) 64 | } 65 | Self::Ternary(condition, lhs, rhs) => write!( 66 | f, 67 | "Ternary AST: Condition: {}, Lhs: {}, Rhs: {}", 68 | condition.clone(), 69 | lhs.clone(), 70 | rhs.clone() 71 | ), 72 | Self::Reference(name) => write!(f, "Reference AST: reference: {}", name), 73 | Self::Function(name, params) => { 74 | let mut s = "[".to_string(); 75 | for param in params.into_iter() { 76 | s.push_str(format!("{},", param.clone()).as_str()); 77 | } 78 | s.push(']'); 79 | write!(f, "Function AST: name: {}, params: {}", name, s) 80 | } 81 | Self::List(params) => { 82 | let mut s = "[".to_string(); 83 | for param in params.into_iter() { 84 | s.push_str(format!("{},", param.clone()).as_str()); 85 | } 86 | s.push(']'); 87 | write!(f, "List AST: params: {}", s) 88 | } 89 | Self::Map(m) => { 90 | let mut s = String::new(); 91 | for (k, v) in m { 92 | s.push_str(format!("({} {}), ", k.clone(), v.clone()).as_str()); 93 | } 94 | write!(f, "Map AST: {}", s) 95 | } 96 | Self::Stmt(exprs) => { 97 | let mut s = String::new(); 98 | for expr in exprs { 99 | s.push_str(format!("{};", expr.clone()).as_str()); 100 | } 101 | write!(f, "Chain AST: {}", s) 102 | } 103 | Self::None => write!(f, "None"), 104 | } 105 | } 106 | } 107 | 108 | impl<'a> ExprAST<'a> { 109 | pub fn exec(&self, ctx: &mut Context) -> Result { 110 | use ExprAST::*; 111 | match self { 112 | Literal(literal) => self.exec_literal(literal.clone()), 113 | Reference(name) => self.exec_reference(name, ctx), 114 | Function(name, exprs) => self.exec_function(name, exprs.clone(), ctx), 115 | Unary(op, rhs) => self.exec_unary(op, rhs, ctx), 116 | Binary(op, lhs, rhs) => self.exec_binary(op, lhs, rhs, ctx), 117 | Postfix(lhs, op) => self.exec_postfix(lhs, op.clone(), ctx), 118 | Ternary(condition, lhs, rhs) => self.exec_ternary(condition, lhs, rhs, ctx), 119 | List(params) => self.exec_list(params.clone(), ctx), 120 | Stmt(exprs) => self.exec_chain(exprs.clone(), ctx), 121 | Map(m) => self.exec_map(m.clone(), ctx), 122 | None => Ok(Value::None), 123 | } 124 | } 125 | 126 | fn exec_literal(&self, literal: Literal<'a>) -> Result { 127 | match literal { 128 | Literal::Bool(value) => Ok(Value::from(value)), 129 | Literal::Number(value) => Ok(Value::from(value)), 130 | Literal::String(value) => Ok(Value::from(value)), 131 | } 132 | } 133 | 134 | fn exec_reference(&self, name: &'a str, ctx: &Context) -> Result { 135 | ctx.value(name) 136 | } 137 | 138 | fn exec_function( 139 | &self, 140 | name: &'a str, 141 | exprs: Vec>, 142 | ctx: &mut Context, 143 | ) -> Result { 144 | let mut params: Vec = Vec::new(); 145 | for expr in exprs.into_iter() { 146 | params.push(expr.exec(ctx)?) 147 | } 148 | match ctx.get_func(name) { 149 | Some(func) => func(params), 150 | None => self.redirect_inner_function(name, params), 151 | } 152 | } 153 | 154 | fn redirect_inner_function(&self, name: &str, params: Vec) -> Result { 155 | InnerFunctionManager::new().get(name)?(params) 156 | } 157 | 158 | fn exec_unary(&self, op: &'a str, rhs: &ExprAST, ctx: &mut Context) -> Result { 159 | PrefixOpManager::new().get(&op)?(rhs.exec(ctx)?) 160 | } 161 | 162 | fn exec_binary( 163 | &self, 164 | op: &'a str, 165 | lhs: &ExprAST<'a>, 166 | rhs: &ExprAST<'a>, 167 | ctx: &mut Context, 168 | ) -> Result { 169 | match InfixOpManager::new().get_op_type(&op)? { 170 | InfixOpType::CALC => { 171 | InfixOpManager::new().get_handler(&op)?(lhs.exec(ctx)?, rhs.exec(ctx)?) 172 | } 173 | InfixOpType::SETTER => { 174 | let (a, b) = (lhs.exec(ctx)?, rhs.exec(ctx)?); 175 | ctx.set_variable( 176 | lhs.get_reference_name()?, 177 | InfixOpManager::new().get_handler(&op)?(a, b)?, 178 | ); 179 | Ok(Value::None) 180 | } 181 | } 182 | } 183 | 184 | fn exec_postfix(&self, lhs: &ExprAST, op: String, ctx: &mut Context) -> Result { 185 | PostfixOpManager::new().get(&op)?(lhs.exec(ctx)?) 186 | } 187 | 188 | fn exec_ternary( 189 | &self, 190 | condition: &ExprAST, 191 | lhs: &ExprAST, 192 | rhs: &ExprAST, 193 | ctx: &mut Context, 194 | ) -> Result { 195 | match condition.exec(ctx)? { 196 | Value::Bool(val) => { 197 | if val { 198 | return lhs.exec(ctx); 199 | } 200 | rhs.exec(ctx) 201 | } 202 | _ => Err(Error::ShouldBeBool()), 203 | } 204 | } 205 | 206 | fn exec_list(&self, params: Vec, ctx: &mut Context) -> Result { 207 | let mut ans = Vec::new(); 208 | for expr in params { 209 | ans.push(expr.exec(ctx)?); 210 | } 211 | Ok(Value::List(ans)) 212 | } 213 | 214 | fn exec_chain(&self, params: Vec, ctx: &mut Context) -> Result { 215 | let mut ans = Value::None; 216 | for expr in params { 217 | ans = expr.exec(ctx)?; 218 | } 219 | Ok(ans) 220 | } 221 | 222 | fn exec_map(&self, m: Vec<(ExprAST, ExprAST)>, ctx: &mut Context) -> Result { 223 | let mut ans = Vec::new(); 224 | for (k, v) in m { 225 | ans.push((k.exec(ctx)?, v.exec(ctx)?)); 226 | } 227 | Ok(Value::Map(ans)) 228 | } 229 | 230 | fn get_precidence(&self) -> (bool, (i32, i32)) { 231 | match self { 232 | ExprAST::Binary(op, _, _) => (true, InfixOpManager::new().get_precidence(op)), 233 | _ => (false, (-1, -1)), 234 | } 235 | } 236 | 237 | fn get_reference_name(&self) -> Result<&'a str> { 238 | match self { 239 | ExprAST::Reference(name) => Ok(name), 240 | _ => Err(Error::NotReferenceExpr), 241 | } 242 | } 243 | } 244 | 245 | impl<'a> ExprAST<'a> { 246 | pub fn expr(&self) -> String { 247 | match self { 248 | Self::Literal(val) => self.literal_expr(val.clone()), 249 | Self::Reference(name) => self.reference_expr(name), 250 | Self::Function(name, exprs) => self.function_expr(name, exprs.clone()), 251 | Self::Unary(op, rhs) => self.unary_expr(op, rhs), 252 | Self::Binary(op, lhs, rhs) => self.binary_expr(op, lhs, rhs), 253 | Self::Postfix(lhs, op) => self.postfix_expr(lhs, op), 254 | Self::Ternary(condition, lhs, rhs) => self.ternary_expr(condition, lhs, rhs), 255 | Self::List(params) => self.list_expr(params.clone()), 256 | Self::Map(m) => self.map_expr(m.clone()), 257 | Self::Stmt(exprs) => self.chain_expr(exprs.clone()), 258 | Self::None => "".to_string(), 259 | } 260 | } 261 | 262 | fn literal_expr(&self, val: Literal) -> String { 263 | use Literal::*; 264 | match val { 265 | Number(value) => value.to_string(), 266 | Bool(value) => { 267 | if value { 268 | "true".into() 269 | } else { 270 | "false".into() 271 | } 272 | } 273 | String(value) => "\"".to_string() + &value + "\"", 274 | } 275 | } 276 | 277 | fn reference_expr(&self, val: &'a str) -> String { 278 | val.to_string() 279 | } 280 | 281 | fn function_expr(&self, name: &'a str, exprs: Vec) -> String { 282 | let mut ans = name.to_string(); 283 | ans.push('('); 284 | for i in 0..exprs.len() { 285 | ans.push_str(&exprs[i].expr()); 286 | if i < exprs.len() - 1 { 287 | ans.push(','); 288 | } 289 | } 290 | ans.push(')'); 291 | ans 292 | } 293 | 294 | fn unary_expr(&self, op: &'a str, rhs: &ExprAST) -> String { 295 | op.to_string() + " " + &rhs.expr() 296 | } 297 | 298 | fn binary_expr(&self, op: &'a str, lhs: &ExprAST, rhs: &ExprAST) -> String { 299 | let left = { 300 | let (is, precidence) = lhs.get_precidence(); 301 | let mut tmp: String = lhs.expr(); 302 | if is && precidence < InfixOpManager::new().get_precidence(op) { 303 | tmp = "(".to_string() + &lhs.expr() + &")".to_string(); 304 | } 305 | tmp 306 | }; 307 | let right = { 308 | let (is, precidence) = rhs.get_precidence(); 309 | let mut tmp = rhs.expr(); 310 | if is && precidence < InfixOpManager::new().get_precidence(op) { 311 | tmp = "(".to_string() + &rhs.expr() + &")".to_string(); 312 | } 313 | tmp 314 | }; 315 | left + " " + op + " " + &right 316 | } 317 | 318 | fn postfix_expr(&self, lhs: &ExprAST, op: &str) -> String { 319 | lhs.expr() + " " + op 320 | } 321 | 322 | fn ternary_expr(&self, condition: &ExprAST, lhs: &ExprAST, rhs: &ExprAST) -> String { 323 | condition.expr() + " ? " + &lhs.expr() + " : " + &rhs.expr() 324 | } 325 | 326 | fn list_expr(&self, params: Vec) -> String { 327 | let mut s = String::from("["); 328 | for i in 0..params.len() { 329 | s.push_str(params[i].expr().as_str()); 330 | if i < params.len() - 1 { 331 | s.push_str(","); 332 | } 333 | } 334 | s.push_str("]"); 335 | s 336 | } 337 | 338 | fn map_expr(&self, m: Vec<(ExprAST, ExprAST)>) -> String { 339 | let mut s = String::from("{"); 340 | for i in 0..m.len() { 341 | let (key, value) = m[i].clone(); 342 | s.push_str(key.expr().as_str()); 343 | s.push_str(":"); 344 | s.push_str(value.expr().as_str()); 345 | if i < m.len() - 1 { 346 | s.push_str(","); 347 | } 348 | } 349 | s.push_str("}"); 350 | s 351 | } 352 | 353 | fn chain_expr(&self, exprs: Vec) -> String { 354 | let mut s = String::new(); 355 | for i in 0..exprs.len() { 356 | s.push_str(exprs[i].expr().as_str()); 357 | if i < exprs.len() - 1 { 358 | s.push_str(";"); 359 | } 360 | } 361 | s 362 | } 363 | } 364 | 365 | impl<'a> ExprAST<'a> { 366 | pub fn describe(&self) -> String { 367 | match self { 368 | Self::Literal(_) => self.expr(), 369 | Self::Unary(op, rhs) => DescriptorManager::new().get_unary_descriptor(op.to_string())( 370 | op.to_string(), 371 | rhs.describe(), 372 | ), 373 | Self::Binary(op, lhs, rhs) => DescriptorManager::new() 374 | .get_binary_descriptor(op.to_string())( 375 | op.to_string(), 376 | lhs.describe(), 377 | rhs.describe(), 378 | ), 379 | Self::Postfix(lhs, op) => DescriptorManager::new().get_postfix_descriptor(op.clone())( 380 | lhs.describe(), 381 | op.clone(), 382 | ), 383 | Self::List(values) => DescriptorManager::new().get_list_descriptor()( 384 | values.into_iter().map(|v| v.describe()).collect(), 385 | ), 386 | Self::Map(values) => DescriptorManager::new().get_map_descriptor()( 387 | values 388 | .into_iter() 389 | .map(|value| (value.0.describe(), value.1.describe())) 390 | .collect(), 391 | ), 392 | Self::Function(name, values) => DescriptorManager::new() 393 | .get_function_descriptor(name.to_string())( 394 | name.to_string(), 395 | values.into_iter().map(|v| v.describe()).collect(), 396 | ), 397 | Self::Reference(name) => DescriptorManager::new() 398 | .get_reference_descriptor(name.to_string())( 399 | name.to_string() 400 | ), 401 | Self::Stmt(values) => DescriptorManager::new().get_chain_descriptor()( 402 | values.into_iter().map(|v| v.describe()).collect(), 403 | ), 404 | Self::Ternary(condition, lhs, rhs) => { 405 | DescriptorManager::new().get_ternary_descriptor()( 406 | condition.describe(), 407 | lhs.describe(), 408 | rhs.describe(), 409 | ) 410 | } 411 | Self::None => "".to_string(), 412 | } 413 | } 414 | } 415 | 416 | pub struct Parser<'a> { 417 | tokenizer: Tokenizer<'a>, 418 | } 419 | 420 | impl<'a> Parser<'a> { 421 | fn cur_tok(&self) -> Token { 422 | self.tokenizer.cur_token.clone() 423 | } 424 | 425 | pub fn new(input: &'a str) -> Result { 426 | let mut tokenizer = Tokenizer::new(input); 427 | tokenizer.next()?; 428 | Ok(Self { 429 | tokenizer: tokenizer, 430 | }) 431 | } 432 | 433 | fn is_eof(&self) -> bool { 434 | self.cur_tok().is_eof() 435 | } 436 | 437 | pub fn next(&mut self) -> Result { 438 | self.tokenizer.next() 439 | } 440 | 441 | fn expect(&mut self, expected: &str) -> Result<()> { 442 | self.tokenizer.expect(expected) 443 | } 444 | 445 | fn parse_token(&mut self) -> Result> { 446 | let token = self.tokenizer.cur_token; 447 | match token { 448 | Token::Number(val, _) => { 449 | self.next()?; 450 | Ok(ExprAST::Literal(Literal::Number(val))) 451 | } 452 | Token::Bool(val, _) => { 453 | self.next()?; 454 | Ok(ExprAST::Literal(Literal::Bool(val))) 455 | } 456 | Token::String(val, _) => { 457 | self.next()?; 458 | Ok(ExprAST::Literal(Literal::String(val))) 459 | } 460 | Token::Reference(val, _) => { 461 | self.next()?; 462 | Ok(ExprAST::Reference(val)) 463 | } 464 | Token::Function(name, _) => self.parse_function(name), 465 | Token::Operator(op, _) => self.parse_unary(op), 466 | Token::Delim(ty, _) => self.parse_delim(ty), 467 | Token::EOF => Err(Error::UnexpectedEOF(0)), 468 | _ => Err(Error::UnexpectedToken()), 469 | } 470 | } 471 | 472 | pub fn parse_stmt(&mut self) -> Result> { 473 | let mut ans = Vec::new(); 474 | loop { 475 | if self.is_eof() { 476 | break; 477 | } 478 | ans.push(self.parse_expression()?); 479 | if self.cur_tok().is_semicolon() { 480 | self.next()?; 481 | } 482 | } 483 | if ans.len() == 1 { 484 | return Ok(ans[0].clone()); 485 | } 486 | Ok(ExprAST::Stmt(ans)) 487 | } 488 | 489 | pub fn parse_expression(&mut self) -> Result> { 490 | let lhs = self.parse_primary()?; 491 | self.parse_op(0, lhs) 492 | } 493 | 494 | fn parse_primary(&mut self) -> Result> { 495 | let lhs = self.parse_token()?; 496 | if self.tokenizer.cur_token.is_postfix_op_token() { 497 | let op = self.tokenizer.cur_token.string(); 498 | self.next()?; 499 | return Ok(ExprAST::Postfix(Box::new(lhs), op.to_string())); 500 | } 501 | Ok(lhs) 502 | } 503 | 504 | fn parse_op(&mut self, exec_prec: i32, mut lhs: ExprAST<'a>) -> Result> { 505 | let mut is_not = false; 506 | loop { 507 | if !self.tokenizer.cur_token.is_op_token() { 508 | return Ok(lhs); 509 | } 510 | if self.tokenizer.cur_token.is_not_token() { 511 | is_not = true; 512 | self.next()?; 513 | if !self.cur_tok().is_binop_token() { 514 | return Err(Error::ExpectBinOpToken); 515 | } 516 | continue; 517 | } 518 | if self.tokenizer.cur_token.is_question_mark() { 519 | self.next()?; 520 | let a = self.parse_expression()?; 521 | self.expect(":")?; 522 | let b = self.parse_expression()?; 523 | return Ok(ExprAST::Ternary(Box::new(lhs), Box::new(a), Box::new(b))); 524 | } 525 | let (l_bp, r_bp) = self.get_token_precidence(); 526 | if l_bp < exec_prec { 527 | return Ok(lhs); 528 | } 529 | let op: &str = match self.tokenizer.cur_token { 530 | Token::Operator(op, _) => op, 531 | _ => "", 532 | }; 533 | self.next()?; 534 | let mut rhs = self.parse_primary()?; 535 | 536 | let (cur_l_bp, _) = self.get_token_precidence(); 537 | if self.tokenizer.cur_token.is_binop_token() && r_bp < cur_l_bp { 538 | rhs = self.parse_op(r_bp, rhs)?; 539 | } 540 | lhs = ExprAST::Binary(op, Box::new(lhs), Box::new(rhs)); 541 | if is_not { 542 | lhs = ExprAST::Unary("not", Box::new(lhs)); 543 | is_not = false; 544 | } 545 | } 546 | } 547 | 548 | fn get_token_precidence(&self) -> (i32, i32) { 549 | match &self.cur_tok() { 550 | Token::Operator(op, _) => InfixOpManager::new().get_precidence(op), 551 | _ => (-1, -1), 552 | } 553 | } 554 | 555 | fn parse_delim(&mut self, ty: DelimTokenType) -> Result> { 556 | use DelimTokenType::*; 557 | match ty { 558 | OpenParen => self.parse_open_paren(), 559 | OpenBracket => self.parse_open_bracket(), 560 | OpenBrace => self.parse_open_brace(), 561 | _ => Err(Error::NoOpenDelim), 562 | } 563 | } 564 | 565 | fn parse_open_paren(&mut self) -> Result> { 566 | self.next()?; 567 | let expr = self.parse_expression()?; 568 | if !self.tokenizer.cur_token.is_close_paren() { 569 | return Err(Error::NoCloseDelim); 570 | } 571 | self.next()?; 572 | Ok(expr) 573 | } 574 | 575 | fn parse_open_bracket(&mut self) -> Result> { 576 | self.next()?; 577 | let mut exprs = Vec::new(); 578 | loop { 579 | if self.is_eof() || self.cur_tok().is_close_bracket() { 580 | break; 581 | } 582 | exprs.push(self.parse_expression()?); 583 | if !self.cur_tok().is_close_bracket() { 584 | self.expect(",")?; 585 | } 586 | } 587 | self.expect("]")?; 588 | Ok(ExprAST::List(exprs)) 589 | } 590 | 591 | fn parse_open_brace(&mut self) -> Result> { 592 | self.next()?; 593 | let mut m = Vec::new(); 594 | loop { 595 | if self.is_eof() || self.cur_tok().is_close_brace() { 596 | break; 597 | } 598 | let k = self.parse_expression()?; 599 | self.expect(":")?; 600 | let v = self.parse_expression()?; 601 | m.push((k, v)); 602 | if !self.cur_tok().is_close_brace() { 603 | self.expect(",")?; 604 | } 605 | } 606 | self.expect("}")?; 607 | Ok(ExprAST::Map(m)) 608 | } 609 | 610 | fn parse_unary(&mut self, op: &'a str) -> Result> { 611 | self.next()?; 612 | Ok(ExprAST::Unary(op, Box::new(self.parse_primary()?))) 613 | } 614 | 615 | fn parse_function(&mut self, name: &'a str) -> Result> { 616 | self.next()?; 617 | self.expect("(")?; 618 | let mut ans = Vec::new(); 619 | if self.cur_tok().is_close_paren() { 620 | self.next()?; 621 | return Ok(ExprAST::Function(name, ans)); 622 | } 623 | let has_right_paren; 624 | loop { 625 | ans.push(self.parse_expression()?); 626 | if self.cur_tok().is_close_paren() { 627 | has_right_paren = true; 628 | self.next()?; 629 | break; 630 | } 631 | self.expect(",")?; 632 | } 633 | if !has_right_paren { 634 | return Err(Error::NoCloseDelim); 635 | } 636 | Ok(ExprAST::Function(name, ans)) 637 | } 638 | } 639 | 640 | #[cfg(test)] 641 | mod tests { 642 | use crate::init::init; 643 | use crate::parser::{ExprAST, Literal, Parser}; 644 | use crate::value::Value; 645 | use rstest::rstest; 646 | use rust_decimal::prelude::*; 647 | 648 | #[rstest] 649 | #[case("5", ExprAST::Literal(Literal::Number(Decimal::from_str("5").unwrap_or_default())))] 650 | #[case("true", ExprAST::Literal(Literal::Bool(true)))] 651 | #[case("\n false", ExprAST::Literal(Literal::Bool(false)))] 652 | #[case("\n haha", ExprAST::Reference("haha"))] 653 | #[case("'haha '", ExprAST::Literal(Literal::String("haha ")))] 654 | #[case("!a", ExprAST::Unary("!", Box::new(ExprAST::Reference("a"))))] 655 | fn test_parse_expression_simple(#[case] input: &str, #[case] output: ExprAST) { 656 | init(); 657 | let parser = Parser::new(input); 658 | assert!(parser.is_ok()); 659 | let expr_ast = parser.unwrap().parse_expression(); 660 | assert!(expr_ast.is_ok()); 661 | assert_eq!(expr_ast.unwrap(), output); 662 | } 663 | 664 | #[rstest] 665 | #[case("2+3*5", ExprAST::Binary( 666 | "+", 667 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(2).unwrap_or_default()))), 668 | Box::new(ExprAST::Binary( 669 | "*", 670 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(3).unwrap_or_default()))), 671 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(5).unwrap_or_default()))), 672 | )) 673 | ))] 674 | #[case("(2+3)*5", ExprAST::Binary( 675 | "*", 676 | Box::new(ExprAST::Binary( 677 | "+", 678 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(2).unwrap_or_default()))), 679 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(3).unwrap_or_default()))), 680 | )), 681 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(5).unwrap_or_default()))), 682 | ))] 683 | #[case( 684 | "'hahhaff' beginWith 'hahha'", 685 | ExprAST::Binary( 686 | "beginWith", 687 | Box::new(ExprAST::Literal(Literal::String("hahhaff"))), 688 | Box::new(ExprAST::Literal(Literal::String("hahha"))), 689 | ) 690 | )] 691 | #[case("2=3=4", ExprAST::Binary( 692 | "=", 693 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(2).unwrap_or_default()))), 694 | Box::new( 695 | ExprAST::Binary( 696 | "=", 697 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(3).unwrap_or_default()))), 698 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(4).unwrap_or_default()))), 699 | ) 700 | ), 701 | ))] 702 | fn test_parse_expression_binary(#[case] input: &str, #[case] output: ExprAST) { 703 | init(); 704 | let parser = Parser::new(input); 705 | assert!(parser.is_ok()); 706 | let expr_ast = parser.unwrap().parse_expression(); 707 | assert!(expr_ast.is_ok()); 708 | assert_eq!(expr_ast.unwrap(), output); 709 | } 710 | 711 | #[rstest] 712 | #[case(" [] ", ExprAST::List(Vec::new()))] 713 | #[case("[1,!a,(2+3)*5,true, 'hahd', [1,!a,(2+3)*5,true, 'hahd']]", ExprAST::List( 714 | vec![ 715 | ExprAST::Literal(Literal::Number(Decimal::from_str("1").unwrap_or_default())), 716 | ExprAST::Unary( 717 | "!", Box::new(ExprAST::Reference("a")) 718 | ), 719 | ExprAST::Binary( 720 | "*", 721 | Box::new(ExprAST::Binary( 722 | "+", 723 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(2).unwrap_or_default()))), 724 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(3).unwrap_or_default()))), 725 | )), 726 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(5).unwrap_or_default()))), 727 | ), 728 | ExprAST::Literal(Literal::Bool(true)), 729 | ExprAST::Literal(Literal::String("hahd")), 730 | ExprAST::List( 731 | vec![ 732 | ExprAST::Literal(Literal::Number(Decimal::from_str("1").unwrap_or_default())), 733 | ExprAST::Unary( 734 | "!", Box::new(ExprAST::Reference("a")) 735 | ), 736 | ExprAST::Binary( 737 | "*", 738 | Box::new(ExprAST::Binary( 739 | "+", 740 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(2).unwrap_or_default()))), 741 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(3).unwrap_or_default()))), 742 | )), 743 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_i32(5).unwrap_or_default()))), 744 | ), 745 | ExprAST::Literal(Literal::Bool(true)), 746 | ExprAST::Literal(Literal::String("hahd")), 747 | ], 748 | ), 749 | ] 750 | ))] 751 | fn test_parse_expression_list(#[case] input: &str, #[case] output: ExprAST) { 752 | init(); 753 | let parser = Parser::new(input); 754 | assert!(parser.is_ok()); 755 | let expr_ast = parser.unwrap().parse_expression(); 756 | assert!(expr_ast.is_ok()); 757 | assert_eq!(expr_ast.unwrap(), output); 758 | } 759 | 760 | #[rstest] 761 | #[case(" true ? 234:'haha'", ExprAST::Ternary( 762 | Box::new(ExprAST::Literal(Literal::Bool(true))), 763 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_str("234").unwrap_or_default()))), 764 | Box::new(ExprAST::Literal(Literal::String("haha"))), 765 | ) 766 | )] 767 | fn test_parse_expression_ternary(#[case] input: &str, #[case] output: ExprAST) { 768 | init(); 769 | let parser = Parser::new(input); 770 | assert!(parser.is_ok()); 771 | let expr_ast = parser.unwrap().parse_expression(); 772 | assert!(expr_ast.is_ok()); 773 | assert_eq!(expr_ast.unwrap(), output); 774 | } 775 | 776 | #[rstest] 777 | #[case(" ")] 778 | #[case(" [ ")] 779 | #[case("[234,")] 780 | #[case(" { ")] 781 | #[case("{2:")] 782 | #[case("{2")] 783 | #[case("{2:}")] 784 | #[case(" (")] 785 | #[case("a(")] 786 | #[case("a(,)")] 787 | #[case("a(2,true,")] 788 | #[case("true ?")] 789 | #[case("true ? haha :")] 790 | #[case("2+ ")] 791 | fn test_parse_expression_error(#[case] input: &str) { 792 | init(); 793 | let parser = Parser::new(input); 794 | assert!(parser.is_ok()); 795 | let expr_ast = parser.unwrap().parse_expression(); 796 | assert!(expr_ast.is_err()); 797 | } 798 | 799 | #[rstest] 800 | #[case("+true")] 801 | #[case("- 'hha'")] 802 | #[case("! 'haha'")] 803 | #[case("fasle ++")] 804 | #[case("'haha' --")] 805 | fn test_execute_error(#[case] input: &str) { 806 | init(); 807 | let parser = Parser::new(input); 808 | assert!(parser.is_ok()); 809 | let expr_ast = parser.unwrap().parse_expression(); 810 | assert!(expr_ast.is_ok()); 811 | let mut ctx = create_context!( 812 | "d" => 3, 813 | "f" => Arc::new(|_| Ok(Value::from(3))) 814 | ); 815 | assert!(expr_ast.unwrap().exec(&mut ctx).is_err()) 816 | } 817 | 818 | #[rstest] 819 | #[case( 820 | " 821 | a=3; 822 | a+=4; 823 | b=a+5; 824 | [a,b] 825 | ", 826 | ExprAST::Stmt( 827 | vec![ 828 | ExprAST::Binary( 829 | "=", 830 | Box::new(ExprAST::Reference("a")), 831 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_str("3").unwrap_or_default()))) 832 | ), 833 | ExprAST::Binary( 834 | "+=", 835 | Box::new(ExprAST::Reference("a")), 836 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_str("4").unwrap_or_default()))) 837 | ), 838 | ExprAST::Binary( 839 | "=", 840 | Box::new(ExprAST::Reference("b")), 841 | Box::new(ExprAST::Binary( 842 | "+", 843 | Box::new(ExprAST::Reference("a")), 844 | Box::new(ExprAST::Literal(Literal::Number(Decimal::from_str("5").unwrap_or_default()))) 845 | )) 846 | ), 847 | ExprAST::List( 848 | vec![ 849 | ExprAST::Reference("a"), 850 | ExprAST::Reference("b") 851 | ] 852 | ), 853 | ] 854 | ), 855 | )] 856 | #[case("5", ExprAST::Literal(Literal::Number(Decimal::from_str("5").unwrap_or_default())))] 857 | #[case("true", ExprAST::Literal(Literal::Bool(true)))] 858 | #[case("\n false", ExprAST::Literal(Literal::Bool(false)))] 859 | #[case("\n haha", ExprAST::Reference("haha"))] 860 | #[case("'haha '", ExprAST::Literal(Literal::String("haha ")))] 861 | #[case("!a", ExprAST::Unary("!", Box::new(ExprAST::Reference("a"))))] 862 | #[case("2++", ExprAST::Postfix( 863 | Box::new(ExprAST::Literal(Literal::Number(2.into()))), 864 | "++".to_string(), 865 | ))] 866 | #[case("2--", ExprAST::Postfix( 867 | Box::new(ExprAST::Literal(Literal::Number(2.into()))), 868 | "--".to_string(), 869 | ))] 870 | #[case("2 not in [2]", ExprAST::Unary( 871 | "not", 872 | Box::new(ExprAST::Binary( 873 | "in", 874 | Box::new( 875 | ExprAST::Literal(Literal::Number(2.into())) 876 | ), 877 | Box::new( 878 | ExprAST::List( 879 | vec![ 880 | ExprAST::Literal(Literal::Number(2.into())) 881 | ] 882 | ) 883 | ) 884 | )) 885 | ))] 886 | fn test_parse_chain_expression(#[case] input: &str, #[case] output: ExprAST) { 887 | init(); 888 | let parser = Parser::new(input); 889 | assert!(parser.is_ok()); 890 | let expr_ast = parser.unwrap().parse_stmt(); 891 | assert!(expr_ast.is_ok()); 892 | assert_eq!(expr_ast.unwrap(), output); 893 | } 894 | 895 | #[rstest] 896 | #[case("")] 897 | #[case(" ")] 898 | fn test_parse_chain_expression_error(#[case] input: &str) { 899 | init(); 900 | let parser = Parser::new(input); 901 | assert!(parser.is_ok()); 902 | let expr_ast = parser.unwrap().parse_expression(); 903 | assert!(expr_ast.is_err()); 904 | } 905 | 906 | use crate::create_context; 907 | use crate::function::InnerFunctionManager; 908 | use std::sync::Arc; 909 | #[rstest] 910 | #[case("2", 2.into())] 911 | #[case("'haha'", "haha".into())] 912 | #[case("true", true.into())] 913 | #[case(" False", false.into())] 914 | #[case("!(2>3)", true.into())] 915 | #[case("2>3", false.into())] 916 | #[case(" 2<3 ", true.into())] 917 | #[case("2 >= 3", false.into())] 918 | #[case("2<=3", true.into())] 919 | #[case("2+3*5-2/2+6*(2+4 )-20", 32.into())] 920 | #[case("102%100",2.into())] 921 | #[case("2!=3", true.into())] 922 | #[case("2==3", false.into())] 923 | #[case("100>>3", (100>>3).into())] 924 | #[case("100<<3", (100<<3).into())] 925 | #[case("(2>3)&&true", false.into())] 926 | #[case("2>3||True", true.into())] 927 | #[case("d+=3;d", 6.into())] 928 | #[case("d-=2;d*5", 5.into())] 929 | #[case("d*=0.1;d+1.5", 1.8.into())] 930 | #[case("d/=2;d==1.5", true.into())] 931 | #[case("d%99;d", 3.into())] 932 | #[case("d<<=2;d", (3<<2).into())] 933 | #[case("d>>=2;d", (3>>2).into())] 934 | #[case("'hahhadf' beginWith \"hahha\"", true.into())] 935 | #[case("'hahhadf' endWith \"hahha\"", false.into())] 936 | #[case("true in [2, true, 'haha']", true.into())] 937 | #[case("-5*10", (-50).into())] 938 | #[case("AND[1>2,true]", false.into())] 939 | #[case("OR[1>2,true]", true.into())] 940 | #[case("[2>3,1+5]", Value::List( 941 | vec![false.into(),6.into()] 942 | ))] 943 | #[case("{'haha':2, 1+2:2>3}", Value::Map( 944 | vec![("haha".into(),2.into()),(3.into(),false.into())] 945 | ))] 946 | #[case("2<=3?'haha':false", "haha".into())] 947 | #[case("2>=3?'haha':false", false.into())] 948 | #[case("min(1,2,2+3*5,-10)", (-10).into())] 949 | #[case("max(1,2,2+3*5,-10)", 17.into())] 950 | #[case("mul(1,2,2+3*5,-10)", (-340).into())] 951 | #[case("sum(1,2,2+3*5,-10)", 10.into())] 952 | #[case("f(3)", 3.into())] 953 | #[case("d()", 4.into())] 954 | #[case("true in [2, true, 'haha']", true.into())] 955 | #[case("'hahf' in [2, true, 'haha']", false.into())] 956 | #[case("-5*10", (-50).into())] 957 | #[case("AND[1>2,true]", false.into())] 958 | #[case("AND[1<2, true]", true.into())] 959 | #[case("OR[1>2,true]", true.into())] 960 | #[case("OR[1>2, 2+2<2]", false.into())] 961 | #[case("[2>3,1+5]", Value::List( 962 | vec![false.into(),6.into()] 963 | ))] 964 | #[case("[2>3,1+5, true]", 965 | vec![false.into(),6.into(), true.into()].into() 966 | )] 967 | #[case("{'haha':2, 1+2:2>3}", Value::Map( 968 | vec![("haha".into(),2.into()),(3.into(),false.into())] 969 | ))] 970 | #[case("2<=3?'haha':false", "haha".into())] 971 | #[case("2>=3?'haha':false", false.into())] 972 | #[case("a=3;a%=2;a",(3%2).into())] 973 | #[case("a=3;a&=2;a",(3&2).into())] 974 | #[case("a=3;a^=2;a",(3^2).into())] 975 | #[case("a=3;a|=2;a",(3|2).into())] 976 | #[case("+5-2*4",(-3).into())] 977 | #[case("2-- +3", 4.into())] 978 | #[case("2++ *3", 9.into())] 979 | #[case("'a' not in ['a']", false.into())] 980 | #[case("2 not in ['a', false, true, 1+2]", true.into())] 981 | #[case("3 not in ['a', false, true, 1+2] || 3>=2", true.into())] 982 | fn test_exec(#[case] input: &str, #[case] output: Value) { 983 | init(); 984 | let mut ctx = create_context!( 985 | "d" => 3, 986 | "f" => Arc::new(|_| Ok(Value::from(3))) 987 | ); 988 | InnerFunctionManager::new().register("d", Arc::new(|_| Ok(4.into()))); 989 | let parser = Parser::new(input); 990 | assert!(parser.is_ok()); 991 | let expr_ast = parser.unwrap().parse_stmt(); 992 | assert!(expr_ast.is_ok()); 993 | let ast = expr_ast.unwrap(); 994 | let ans = ast.clone().exec(&mut ctx); 995 | assert!(ans.is_ok()); 996 | let res = ans.unwrap(); 997 | if res.clone() != output { 998 | print!("{}", ast.clone()); 999 | } 1000 | assert_eq!(res.clone(), output); 1001 | ast.clone().describe(); 1002 | } 1003 | 1004 | #[rstest] 1005 | #[case("5", "5")] 1006 | #[case(" true ", "true")] 1007 | #[case(" True ", "true")] 1008 | #[case(" false ", "false")] 1009 | #[case(" False ", "false")] 1010 | #[case("\n haha", "haha")] 1011 | #[case("\t 'haha '", "\"haha \"")] 1012 | #[case("!a", "! a")] 1013 | #[case("not a", "not a")] 1014 | #[case("2+3*5", "2 + 3 * 5")] 1015 | #[case("(2+3)*5", "(2 + 3) * 5")] 1016 | #[case("[]", "[]")] 1017 | #[case( 1018 | "[1,!a,(2+3)*5,true, 'hahd',[1,!a,(2+3)*5,true, 'hahd']]", 1019 | "[1,! a,(2 + 3) * 5,true,\"hahd\",[1,! a,(2 + 3) * 5,true,\"hahd\"]]" 1020 | )] 1021 | #[case(" a()", "a()")] 1022 | #[case( 1023 | "test(1,!a,(2+3)*5,true, 'hahd',[1,!a,(2+3)*5,true, 'hahd'])", 1024 | "test(1,! a,(2 + 3) * 5,true,\"hahd\",[1,! a,(2 + 3) * 5,true,\"hahd\"])" 1025 | )] 1026 | #[case("{}", "{}")] 1027 | #[case("{2+3:5,'haha':d}", "{2 + 3:5,\"haha\":d}")] 1028 | #[case("true?4: 2", "true ? 4 : 2")] 1029 | #[case("2+3 >5?4: 2", "2 + 3 > 5 ? 4 : 2")] 1030 | #[case("2++ + 3", "2 ++ + 3")] 1031 | #[case("a()++ * 2-7", "a() ++ * 2 - 7")] 1032 | #[case("2++ + 3", "2 ++ + 3")] 1033 | #[case("a()++ * 2-7", "a() ++ * 2 - 7")] 1034 | fn test_expression_expr(#[case] input: &str, #[case] output: &str) { 1035 | init(); 1036 | let parser = Parser::new(input); 1037 | assert!(parser.is_ok()); 1038 | let expr_ast = parser.unwrap().parse_expression(); 1039 | assert!(expr_ast.is_ok()); 1040 | assert_eq!(expr_ast.unwrap().expr(), output); 1041 | } 1042 | } 1043 | -------------------------------------------------------------------------------- /src/token.rs: -------------------------------------------------------------------------------- 1 | use crate::keyword; 2 | use core::clone::Clone; 3 | use rust_decimal::Decimal; 4 | use std::fmt; 5 | 6 | #[derive(Clone, PartialEq, Debug, Copy)] 7 | pub enum DelimTokenType { 8 | // "(" 9 | OpenParen, 10 | // ")" 11 | CloseParen, 12 | // "[" 13 | OpenBracket, 14 | // "]" 15 | CloseBracket, 16 | // "{" 17 | OpenBrace, 18 | // "}" 19 | CloseBrace, 20 | 21 | Unknown, 22 | } 23 | 24 | impl From for DelimTokenType { 25 | fn from(value: char) -> Self { 26 | use DelimTokenType::*; 27 | match value { 28 | '(' => OpenParen, 29 | ')' => CloseParen, 30 | '[' => OpenBracket, 31 | ']' => CloseBracket, 32 | '{' => OpenBrace, 33 | '}' => CloseBrace, 34 | _ => Unknown, 35 | } 36 | } 37 | } 38 | 39 | impl From<&str> for DelimTokenType { 40 | fn from(value: &str) -> Self { 41 | use DelimTokenType::*; 42 | match value { 43 | "(" => OpenParen, 44 | ")" => CloseParen, 45 | "[" => OpenBracket, 46 | "]" => CloseBracket, 47 | "{" => OpenBrace, 48 | "}" => CloseBrace, 49 | _ => Unknown, 50 | } 51 | } 52 | } 53 | 54 | impl DelimTokenType { 55 | pub fn string(&self) -> String { 56 | use DelimTokenType::*; 57 | match self { 58 | OpenParen => "(".to_string(), 59 | CloseParen => ")".to_string(), 60 | OpenBracket => "[".to_string(), 61 | CloseBracket => "]".to_string(), 62 | OpenBrace => "{".to_string(), 63 | CloseBrace => "}".to_string(), 64 | Unknown => "??".to_string(), 65 | } 66 | } 67 | } 68 | 69 | #[derive(Clone, PartialEq, Debug, Copy)] 70 | pub struct Span(pub usize, pub usize); 71 | 72 | #[derive(Clone, PartialEq, Debug, Copy)] 73 | pub enum Token<'input> { 74 | Operator(&'input str, Span), 75 | Delim(DelimTokenType, Span), 76 | Number(Decimal, Span), 77 | Comma(&'input str, Span), 78 | Bool(bool, Span), 79 | String(&'input str, Span), 80 | Reference(&'input str, Span), 81 | Function(&'input str, Span), 82 | Semicolon(&'input str, Span), 83 | EOF, 84 | } 85 | 86 | pub fn check_op(token: Token, expected: &str) -> bool { 87 | match token { 88 | Token::Delim(op, _) => { 89 | if op.string() == expected { 90 | return true; 91 | } 92 | } 93 | Token::Operator(op, _) => { 94 | if op == expected { 95 | return true; 96 | } 97 | } 98 | _ => return false, 99 | } 100 | return false; 101 | } 102 | 103 | impl<'input> Token<'input> { 104 | pub fn is_open_paren(self) -> bool { 105 | check_op(self, "(") 106 | } 107 | 108 | pub fn is_close_paren(self) -> bool { 109 | check_op(self, ")") 110 | } 111 | 112 | pub fn is_open_bracket(self) -> bool { 113 | check_op(self, "[") 114 | } 115 | 116 | pub fn is_close_bracket(self) -> bool { 117 | check_op(self, "]") 118 | } 119 | 120 | pub fn is_open_brace(self) -> bool { 121 | check_op(self, "{") 122 | } 123 | 124 | pub fn is_close_brace(self) -> bool { 125 | check_op(self, "}") 126 | } 127 | 128 | pub fn is_question_mark(self) -> bool { 129 | check_op(self, "?") 130 | } 131 | 132 | pub fn is_colon(self) -> bool { 133 | check_op(self, ":") 134 | } 135 | 136 | pub fn is_eof(self) -> bool { 137 | match self { 138 | Self::EOF => true, 139 | _ => false, 140 | } 141 | } 142 | 143 | pub fn is_op_token(&self) -> bool { 144 | match self { 145 | Self::Operator(_, _) => true, 146 | _ => false, 147 | } 148 | } 149 | 150 | pub fn is_postfix_op_token(&self) -> bool { 151 | match self { 152 | Self::Operator(op, _) => keyword::is_postfix_op(op), 153 | _ => false, 154 | } 155 | } 156 | 157 | pub fn is_binop_token(&self) -> bool { 158 | match self { 159 | Self::Operator(op, _) => keyword::is_infix_op(op), 160 | _ => false, 161 | } 162 | } 163 | 164 | pub fn is_not_token(&self) -> bool { 165 | match self { 166 | Self::Operator(op, _) => keyword::is_not(op), 167 | _ => false, 168 | } 169 | } 170 | 171 | pub fn is_semicolon(&self) -> bool { 172 | match self { 173 | Self::Semicolon(..) => true, 174 | _ => false, 175 | } 176 | } 177 | 178 | #[cfg(not(tarpaulin_include))] 179 | pub fn string(self) -> String { 180 | use Token::*; 181 | match self { 182 | Operator(op, _) => op.to_string(), 183 | Number(val, _) => val.to_string(), 184 | Comma(val, _) => val.to_string(), 185 | Bool(val, _) => val.to_string(), 186 | String(val, _) => val.to_string(), 187 | Reference(val, _) => val.to_string(), 188 | Function(val, _) => val.to_string(), 189 | Semicolon(val, _) => val.to_string(), 190 | Delim(ty, _) => ty.string(), 191 | EOF => "EOF".to_string(), 192 | } 193 | } 194 | } 195 | 196 | #[cfg(not(tarpaulin_include))] 197 | impl fmt::Display for Span { 198 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 199 | write!(f, "[{}..={})", self.0, self.1) 200 | } 201 | } 202 | 203 | #[cfg(not(tarpaulin_include))] 204 | impl<'input> fmt::Display for Token<'input> { 205 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 206 | use Token::*; 207 | match self { 208 | Bool(val, span) => write!(f, "Bool Token: {}, {}", val, span), 209 | Comma(val, span) => write!(f, "Comma Token: {}, {}", val, span), 210 | Number(val, span) => write!(f, "Number Token: {}, {}", val, span), 211 | Operator(val, span) => write!(f, "Operator Token: {}, {}", val, span), 212 | Reference(val, span) => write!(f, "Reference Token: {}, {}", val, span), 213 | Function(val, span) => write!(f, "Function Token: {}, {}", val, span), 214 | String(val, span) => write!(f, "String Token: {}, {}", val, span), 215 | Semicolon(val, span) => write!(f, "Semicolon Token: {}, {}", val, span), 216 | Delim(ty, span) => write!(f, "Delim Token: {}, {}", ty.string(), span), 217 | EOF => write!(f, "EOF"), 218 | } 219 | } 220 | } 221 | 222 | #[cfg(test)] 223 | mod tests { 224 | use super::{DelimTokenType, Span, Token}; 225 | use rstest::rstest; 226 | 227 | #[rstest] 228 | #[case("(", DelimTokenType::OpenParen)] 229 | #[case(")", DelimTokenType::CloseParen)] 230 | #[case("[", DelimTokenType::OpenBracket)] 231 | #[case("]", DelimTokenType::CloseBracket)] 232 | #[case("{", DelimTokenType::OpenBrace)] 233 | #[case("}", DelimTokenType::CloseBrace)] 234 | #[case("f", DelimTokenType::Unknown)] 235 | fn test_delim_token_type_from_str(#[case] input: &str, #[case] output: DelimTokenType) { 236 | assert_eq!(DelimTokenType::from(input), output) 237 | } 238 | 239 | #[rstest] 240 | #[case('(', DelimTokenType::OpenParen)] 241 | #[case(')', DelimTokenType::CloseParen)] 242 | #[case('[', DelimTokenType::OpenBracket)] 243 | #[case(']', DelimTokenType::CloseBracket)] 244 | #[case('{', DelimTokenType::OpenBrace)] 245 | #[case('}', DelimTokenType::CloseBrace)] 246 | #[case('b', DelimTokenType::Unknown)] 247 | fn test_delim_token_type_from_char(#[case] input: char, #[case] output: DelimTokenType) { 248 | assert_eq!(DelimTokenType::from(input), output) 249 | } 250 | 251 | #[rstest] 252 | #[case(Token::Delim(DelimTokenType::OpenParen, Span(0, 0)), true)] 253 | #[case(Token::Delim(DelimTokenType::CloseParen, Span(0, 0)), false)] 254 | #[case(Token::Bool(false, Span(0, 0)), false)] 255 | fn test_is_open_paren(#[case] input: Token, #[case] output: bool) { 256 | assert_eq!(input.is_open_paren(), output) 257 | } 258 | 259 | #[rstest] 260 | #[case(Token::Delim(DelimTokenType::OpenBrace, Span(0, 0)), true)] 261 | #[case(Token::Delim(DelimTokenType::CloseBrace, Span(0, 0)), false)] 262 | #[case(Token::Bool(false, Span(0, 0)), false)] 263 | fn test_is_open_brace(#[case] input: Token, #[case] output: bool) { 264 | assert_eq!(input.is_open_brace(), output) 265 | } 266 | 267 | #[rstest] 268 | #[case(Token::Delim(DelimTokenType::OpenBracket, Span(0, 0)), true)] 269 | #[case(Token::Delim(DelimTokenType::CloseBracket, Span(0, 0)), false)] 270 | #[case(Token::Bool(false, Span(0, 0)), false)] 271 | fn test_is_open_bracket(#[case] input: Token, #[case] output: bool) { 272 | assert_eq!(input.is_open_bracket(), output) 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/tokenizer.rs: -------------------------------------------------------------------------------- 1 | use crate::define::Result; 2 | use crate::error::Error; 3 | use crate::keyword; 4 | use crate::token::{Span, Token}; 5 | use rust_decimal::prelude::*; 6 | use std::str; 7 | 8 | #[derive(Clone)] 9 | pub struct Tokenizer<'a> { 10 | input: &'a str, 11 | chars: str::CharIndices<'a>, 12 | cur_char: char, 13 | pub cur_token: Token<'a>, 14 | pub prev_token: Token<'a>, 15 | } 16 | 17 | impl<'a> Tokenizer<'a> { 18 | pub fn new(input: &str) -> Tokenizer { 19 | Tokenizer { 20 | input: input, 21 | chars: input.char_indices(), 22 | cur_char: ' ', 23 | cur_token: Token::EOF, 24 | prev_token: Token::EOF, 25 | } 26 | } 27 | 28 | fn next_one(&mut self) -> Option<(usize, char)> { 29 | let (cur, cur_char) = self.chars.next()?; 30 | self.cur_char = cur_char; 31 | Some((cur, cur_char)) 32 | } 33 | 34 | fn peek_one(&mut self) -> Option<(usize, char)> { 35 | self.chars.clone().next() 36 | } 37 | 38 | pub fn next(&mut self) -> Result> { 39 | self.eat_whitespace(); 40 | self.prev_token = self.cur_token; 41 | self.cur_token = match self.next_one() { 42 | Some(( 43 | start, 44 | '+' | '-' | '*' | '/' | '^' | '%' | '&' | '!' | '=' | '?' | ':' | '>' | '<' | '|', 45 | )) => self.special_op_token(start), 46 | Some((start, '(' | ')' | '[' | ']' | '{' | '}')) => self.delim_token(start), 47 | Some((start, _ch @ '0'..='9')) => self.number_token(start), 48 | Some((start, '"' | '\'')) => self.string_token(start), 49 | Some((start, ';')) => self.semicolon_token(start), 50 | Some((start, ',')) => self.comma_token(start), 51 | None => Ok(Token::EOF), 52 | Some((start, ch)) => self.other_token(ch, start), 53 | }?; 54 | Ok(self.cur_token) 55 | } 56 | 57 | fn special_op_token(&mut self, start: usize) -> Result> { 58 | loop { 59 | match self.peek_one() { 60 | Some((_, _ch)) => { 61 | if keyword::is_op(&(self.input[start..self.current() + 1].to_string())) { 62 | self.next_one(); 63 | } else { 64 | break; 65 | } 66 | } 67 | None => break, 68 | } 69 | } 70 | Ok(Token::Operator( 71 | &self.input[start..self.current()], 72 | Span(start, self.current()), 73 | )) 74 | } 75 | 76 | fn other_token(&mut self, _: char, start: usize) -> Result> { 77 | if self.try_parse_op(start) { 78 | return self.operator_token(start); 79 | } 80 | let (atom, start) = self.parse_var(start); 81 | if atom == "True" || atom == "true" { 82 | return self.bool_token(start, true); 83 | } else if atom == "False" || atom == "false" { 84 | return self.bool_token(start, false); 85 | } 86 | return self.function_or_reference_token(atom, start); 87 | } 88 | 89 | fn try_parse_op(&self, start: usize) -> bool { 90 | let mut tmp = self.clone(); 91 | loop { 92 | match tmp.peek_one() { 93 | Some((_, ch)) => { 94 | if is_whitespace_char(ch) || is_delim_char(ch) { 95 | break; 96 | } 97 | tmp.next_one(); 98 | } 99 | None => break, 100 | } 101 | } 102 | keyword::is_op(&tmp.input[start..tmp.current()]) 103 | } 104 | 105 | fn operator_token(&mut self, start: usize) -> Result> { 106 | loop { 107 | match self.peek_one() { 108 | Some((_, ch)) => { 109 | if is_whitespace_char(ch) || is_delim_char(ch) { 110 | break; 111 | } 112 | self.next_one(); 113 | } 114 | None => break, 115 | } 116 | } 117 | return Ok(Token::Operator( 118 | self.input[start..self.current()].into(), 119 | Span(start, self.current()), 120 | )); 121 | } 122 | 123 | fn parse_var(&mut self, start: usize) -> (&'a str, usize) { 124 | loop { 125 | match self.peek_one() { 126 | Some((_, ch)) => { 127 | if is_param_char(ch) { 128 | self.next_one(); 129 | continue; 130 | } 131 | break; 132 | } 133 | None => break, 134 | } 135 | } 136 | (self.input[start..self.current()].into(), start) 137 | } 138 | 139 | pub fn peek(&self) -> Result { 140 | self.clone().next() 141 | } 142 | 143 | pub fn expect(&mut self, op: &str) -> Result<()> { 144 | let token = self.cur_token.clone(); 145 | self.next()?; 146 | match token { 147 | Token::Delim(bracket, _) => { 148 | if bracket.string() == op { 149 | return Ok(()); 150 | } 151 | } 152 | Token::Operator(operator, _) => { 153 | if operator == op { 154 | return Ok(()); 155 | } 156 | } 157 | Token::Comma(c, _) => { 158 | if c == op { 159 | return Ok(()); 160 | } 161 | } 162 | _ => { 163 | return Err(Error::ExpectedOpNotExist(op.to_string())); 164 | } 165 | } 166 | Ok(()) 167 | } 168 | 169 | fn delim_token(&mut self, start: usize) -> Result> { 170 | Ok(Token::Delim( 171 | self.input[start..start + 1].into(), 172 | Span(start, start + 1), 173 | )) 174 | } 175 | 176 | fn comma_token(&mut self, start: usize) -> Result> { 177 | Ok(Token::Comma( 178 | &self.input[start..start + 1], 179 | Span(start, start + 1), 180 | )) 181 | } 182 | 183 | fn semicolon_token(&mut self, start: usize) -> Result> { 184 | Ok(Token::Semicolon( 185 | &self.input[start..start + 1], 186 | Span(start, start + 1), 187 | )) 188 | } 189 | 190 | fn number_token(&mut self, start: usize) -> Result> { 191 | loop { 192 | match self.peek_one() { 193 | Some((_, ch)) => { 194 | if (ch == '+' || ch == '-') && (self.cur_char != 'e' && self.cur_char != 'E') { 195 | break; 196 | } 197 | if is_digit_char(ch) { 198 | self.next_one(); 199 | } else { 200 | break; 201 | } 202 | } 203 | None => break, 204 | } 205 | } 206 | match Decimal::from_str(&self.input[start..self.current()]) { 207 | Ok(val) => Ok(Token::Number(val, Span(start, self.current()))), 208 | Err(_) => Err(Error::InvalidNumber( 209 | self.input[start..self.current()].to_string(), 210 | )), 211 | } 212 | } 213 | 214 | fn function_or_reference_token(&self, atom: &'a str, start: usize) -> Result> { 215 | let peek = self.peek()?; 216 | if peek.is_open_paren() { 217 | return Ok(Token::Function(atom, Span(start, self.current()))); 218 | } 219 | Ok(Token::Reference(atom, Span(start, self.current()))) 220 | } 221 | 222 | fn string_token(&mut self, start: usize) -> Result> { 223 | let identifier = self.cur_char; 224 | let mut string_termmited = false; 225 | loop { 226 | match self.next_one() { 227 | Some((_, ch)) => { 228 | if ch == identifier { 229 | string_termmited = true; 230 | break; 231 | } 232 | } 233 | None => break, 234 | } 235 | } 236 | if !string_termmited { 237 | return Err(Error::UnterminatedString(self.current())); 238 | } 239 | Ok(Token::String( 240 | &self.input[start + 1..self.current() - 1], 241 | Span(start, self.current()), 242 | )) 243 | } 244 | 245 | fn bool_token(&mut self, start: usize, val: bool) -> Result> { 246 | Ok(Token::Bool(val, Span(start, self.current()))) 247 | } 248 | 249 | fn eat_whitespace(&mut self) -> Option<()> { 250 | loop { 251 | let (_, ch) = self.peek_one()?; 252 | if is_whitespace_char(ch) { 253 | self.next_one(); 254 | } else { 255 | break; 256 | } 257 | } 258 | Some(()) 259 | } 260 | 261 | fn current(&self) -> usize { 262 | self.chars 263 | .clone() 264 | .next() 265 | .map(|i| i.0) 266 | .unwrap_or_else(|| self.input.len()) 267 | } 268 | } 269 | 270 | fn is_digit_char(ch: char) -> bool { 271 | return '0' <= ch && ch <= '9' || ch == '.' || ch == '-' || ch == 'e' || ch == 'E' || ch == '+'; 272 | } 273 | 274 | fn is_whitespace_char(ch: char) -> bool { 275 | return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; 276 | } 277 | 278 | fn is_delim_char(ch: char) -> bool { 279 | return ch == '(' || ch == ')' || ch == '[' || ch == ']' || ch == '{' || ch == '}'; 280 | } 281 | 282 | fn is_param_char(ch: char) -> bool { 283 | return ('0' <= ch && ch <= '9') 284 | || ('a' <= ch && ch <= 'z') 285 | || ('A' <= ch && ch <= 'Z') 286 | || ch == '.' 287 | || ch == '_'; 288 | } 289 | 290 | #[cfg(test)] 291 | mod tests { 292 | use super::Tokenizer; 293 | use crate::init::init; 294 | use crate::token::DelimTokenType; 295 | use crate::token::Span; 296 | use crate::token::Token; 297 | use crate::token::Token::*; 298 | use rstest::rstest; 299 | use rust_decimal::prelude::*; 300 | 301 | #[rstest] 302 | #[case("true", true, 0, 4)] 303 | #[case(" True", true, 1, 5)] 304 | #[case(" \nfalse", false, 2, 7)] 305 | #[case(" \t False", false, 3, 8)] 306 | fn test_bool( 307 | #[case] input: &str, 308 | #[case] value: bool, 309 | #[case] start: usize, 310 | #[case] end: usize, 311 | ) { 312 | init(); 313 | let mut tokenizer = Tokenizer::new(input); 314 | let ans = tokenizer.next().unwrap(); 315 | assert_eq!(ans, Bool(value, Span(start, end))) 316 | } 317 | 318 | #[rstest] 319 | #[case(" 1234 ", "1234", 1, 5)] 320 | #[case(" 5.678 ", "5.678", 1, 6)] 321 | // #[case(" 10e-3 ", "10e-3", 1, 6)] 322 | // #[case(" 10e03 ", "10e03", 1, 6)] 323 | // #[case(" 2e+3 ", "2e+3", 1, 5)] 324 | fn test_number( 325 | #[case] input: &str, 326 | #[case] value: &str, 327 | #[case] start: usize, 328 | #[case] end: usize, 329 | ) { 330 | init(); 331 | let mut tokenizer = Tokenizer::new(input); 332 | let ans = tokenizer.next().unwrap(); 333 | assert_eq!( 334 | ans, 335 | Number( 336 | Decimal::from_str(value).unwrap_or_default(), 337 | Span(start, end) 338 | ) 339 | ) 340 | } 341 | 342 | #[rstest] 343 | #[case(" { ", DelimTokenType::OpenBrace, 1, 2)] 344 | #[case(" } ", DelimTokenType::CloseBrace, 1, 2)] 345 | #[case(" [", DelimTokenType::OpenBracket, 2, 3)] 346 | #[case("\n]", DelimTokenType::CloseBracket, 1, 2)] 347 | #[case(" ( ", DelimTokenType::OpenParen, 1, 2)] 348 | #[case(" \t)", DelimTokenType::CloseParen, 3, 4)] 349 | fn test_delim( 350 | #[case] input: &str, 351 | #[case] typ: DelimTokenType, 352 | #[case] start: usize, 353 | #[case] end: usize, 354 | ) { 355 | init(); 356 | let mut tokenizer = Tokenizer::new(input); 357 | let ans = tokenizer.next().unwrap(); 358 | assert_eq!(ans, Delim(typ, Span(start, end))) 359 | } 360 | 361 | #[rstest] 362 | #[case("", EOF)] 363 | #[case(" , ", Comma(",", Span(1, 2)))] 364 | #[case(" ; ", Semicolon(";", Span(1, 2)))] 365 | #[case(" +=", Operator("+=", Span(1, 3)))] 366 | #[case(" +=+", Operator("+=", Span(1, 3)))] 367 | #[case(" +=9", Operator("+=", Span(1, 3)))] 368 | #[case(" beginWith", Operator("beginWith", Span(1, 10)))] 369 | #[case(" endWith", Operator("endWith", Span(1, 8)))] 370 | fn test_other(#[case] input: &str, #[case] output: Token) { 371 | init(); 372 | let mut tokenizer = Tokenizer::new(input); 373 | let ans = tokenizer.next().unwrap(); 374 | assert_eq!(ans, output); 375 | } 376 | 377 | #[rstest] 378 | #[case(" 'dsfasdfdsa' ", "dsfasdfdsa", 1, 13)] 379 | #[case("\"dffd\"", "dffd", 0, 6)] 380 | fn test_string( 381 | #[case] input: &str, 382 | #[case] value: &str, 383 | #[case] start: usize, 384 | #[case] end: usize, 385 | ) { 386 | init(); 387 | let mut tokenizer = Tokenizer::new(input); 388 | let ans = tokenizer.next().unwrap(); 389 | assert_eq!(ans, String(value, Span(start, end))); 390 | } 391 | 392 | #[rstest] 393 | #[case(" d09f_5 ", Reference("d09f_5", Span(1, 7)))] 394 | #[case(" d09f_5() ", Function("d09f_5", Span(1, 7)))] 395 | #[case(" d09f_>", Reference("d09f_", Span(1, 6)))] 396 | fn test_reference_function(#[case] input: &str, #[case] output: Token) { 397 | init(); 398 | let mut tokenizer = Tokenizer::new(input); 399 | let ans = tokenizer.next().unwrap(); 400 | assert_eq!(ans, output); 401 | } 402 | 403 | #[rstest] 404 | #[case("\"jajd'")] 405 | #[case("0e.3")] 406 | fn test_err(#[case] input: &str) { 407 | init(); 408 | let mut tokenizer = Tokenizer::new(input); 409 | let ans = tokenizer.next(); 410 | assert!(ans.is_err()) 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | use crate::define::Result; 2 | use crate::error::Error; 3 | use rust_decimal::prelude::*; 4 | use std::fmt; 5 | 6 | #[derive(Clone, PartialEq, Debug)] 7 | pub enum Value { 8 | String(String), 9 | Number(Decimal), 10 | Bool(bool), 11 | List(Vec), 12 | Map(Vec<(Value, Value)>), 13 | None, 14 | } 15 | 16 | #[cfg(not(tarpaulin_include))] 17 | impl fmt::Display for Value { 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 | match self { 20 | Self::String(val) => write!(f, "value string: {}", val.clone()), 21 | Self::Number(val) => write!(f, "value number: {}", val.clone()), 22 | Self::Bool(val) => write!(f, "value bool: {}", val.clone()), 23 | Self::List(values) => { 24 | let mut s = String::from("["); 25 | for value in values { 26 | s.push_str(format!("{},", value.clone()).as_str()); 27 | } 28 | s.push_str("]"); 29 | write!(f, "value list: {}", s) 30 | } 31 | Self::Map(m) => { 32 | let mut s = String::from("{"); 33 | for (k, v) in m { 34 | s.push_str(format!("key: {},", k.clone()).as_str()); 35 | s.push_str(format!("value: {}; ", v.clone()).as_str()); 36 | } 37 | s.push_str("}"); 38 | write!(f, "value map: {}", s) 39 | } 40 | Self::None => write!(f, "None"), 41 | } 42 | } 43 | } 44 | 45 | impl From<&str> for Value { 46 | fn from(value: &str) -> Self { 47 | Value::String(value.to_string()) 48 | } 49 | } 50 | 51 | impl From for Value { 52 | fn from(value: String) -> Self { 53 | Value::String(value) 54 | } 55 | } 56 | 57 | impl From for Value { 58 | fn from(value: bool) -> Self { 59 | Value::Bool(value) 60 | } 61 | } 62 | 63 | impl From> for Value { 64 | fn from(value: Vec) -> Self { 65 | Value::List(value) 66 | } 67 | } 68 | 69 | impl From for Value { 70 | fn from(value: Decimal) -> Self { 71 | Value::Number(value) 72 | } 73 | } 74 | 75 | impl Value { 76 | pub fn decimal(self) -> Result { 77 | match self { 78 | Self::Number(val) => Ok(val), 79 | _ => Err(Error::ShouldBeNumber()), 80 | } 81 | } 82 | 83 | pub fn string(self) -> Result { 84 | match self { 85 | Self::String(val) => Ok(val), 86 | _ => Err(Error::ShouldBeString()), 87 | } 88 | } 89 | 90 | pub fn bool(self) -> Result { 91 | match self { 92 | Self::Bool(val) => Ok(val), 93 | _ => Err(Error::ShouldBeBool()), 94 | } 95 | } 96 | 97 | pub fn integer(self) -> Result { 98 | match self { 99 | Self::Number(val) => val 100 | .to_string() 101 | .parse() 102 | .map_or(Err(Error::InvalidInteger), |num| Ok(num)), 103 | _ => Err(Error::InvalidInteger), 104 | } 105 | } 106 | 107 | pub fn float(self) -> Result { 108 | match self { 109 | Self::Number(val) => val 110 | .to_string() 111 | .parse() 112 | .map_or(Err(Error::InvalidFloat), |num| Ok(num)), 113 | _ => Err(Error::InvalidFloat), 114 | } 115 | } 116 | 117 | pub fn list(self) -> Result> { 118 | match self { 119 | Self::List(list) => Ok(list), 120 | _ => Err(Error::ShouldBeList()), 121 | } 122 | } 123 | } 124 | 125 | macro_rules! impl_value_from_for_number { 126 | ($([$number_type:tt, $method_name: ident]),+) => { 127 | $( 128 | impl From<$number_type> for Value { 129 | fn from(value: $number_type) -> Self { 130 | Value::Number(Decimal::$method_name(value).unwrap_or_default()) 131 | } 132 | } 133 | )+ 134 | }; 135 | } 136 | 137 | impl_value_from_for_number!( 138 | [i128, from_i128], 139 | [i32, from_i32], 140 | [i64, from_i64], 141 | [i16, from_i16], 142 | [i8, from_i8], 143 | [u128, from_u128], 144 | [u64, from_u64], 145 | [u32, from_u32], 146 | [u16, from_u16], 147 | [u8, from_u8], 148 | [f64, from_f64], 149 | [f32, from_f32] 150 | ); 151 | --------------------------------------------------------------------------------