{"id":15654076,"url":"https://github.com/danielgtaylor/mexpr","last_synced_at":"2026-04-23T02:00:51.491Z","repository":{"id":136300963,"uuid":"421896558","full_name":"danielgtaylor/mexpr","owner":"danielgtaylor","description":"Micro expression parser library for Go","archived":false,"fork":false,"pushed_at":"2026-04-22T06:30:42.000Z","size":170,"stargazers_count":34,"open_issues_count":1,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-04-22T07:36:56.043Z","etag":null,"topics":["ast","expression-evaluator","expression-language","go","golang-package","hacktoberfest"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielgtaylor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-10-27T16:38:17.000Z","updated_at":"2026-04-22T05:47:52.000Z","dependencies_parsed_at":null,"dependency_job_id":"d5350999-b453-4469-adca-67d989143ff4","html_url":"https://github.com/danielgtaylor/mexpr","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/danielgtaylor/mexpr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielgtaylor%2Fmexpr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielgtaylor%2Fmexpr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielgtaylor%2Fmexpr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielgtaylor%2Fmexpr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielgtaylor","download_url":"https://codeload.github.com/danielgtaylor/mexpr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielgtaylor%2Fmexpr/sbom","scorecard":{"id":319777,"data":{"date":"2025-08-11","repo":{"name":"github.com/danielgtaylor/mexpr","commit":"cc7bbaa8c7fb4df8daeb27e20339a062fc342e47"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.1,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:8: update your workflow using https://app.stepsecurity.io/secureworkflow/danielgtaylor/mexpr/ci.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/danielgtaylor/mexpr/ci.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yaml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/danielgtaylor/mexpr/ci.yaml/main?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/22 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":10,"reason":"project is fuzzed","details":["Info: GoBuiltInFuzzer integration found: interpreter_test.go:235"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 16 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T01:05:59.124Z","repository_id":136300963,"created_at":"2025-08-18T01:05:59.125Z","updated_at":"2025-08-18T01:05:59.125Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32162611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-22T17:06:48.269Z","status":"online","status_checked_at":"2026-04-23T02:00:06.710Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ast","expression-evaluator","expression-language","go","golang-package","hacktoberfest"],"created_at":"2024-10-03T12:49:24.799Z","updated_at":"2026-04-23T02:00:51.475Z","avatar_url":"https://github.com/danielgtaylor.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MicroExpr\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/danielgtaylor/mexpr.svg)](https://pkg.go.dev/github.com/danielgtaylor/mexpr) [![Go Report Card](https://goreportcard.com/badge/github.com/danielgtaylor/mexpr)](https://goreportcard.com/report/github.com/danielgtaylor/mexpr) ![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/danielgtaylor/mexpr)\n\nA small \u0026 fast dependency-free library for parsing micro expressions.\n\nThis library was originally built for use in templating languages (e.g. for-loop variable selection, if-statement evaluation) so is minimal in what it supports by design. If you need a more full-featured expression parser, check out [antonmedv/expr](https://github.com/antonmedv/expr) instead.\n\nFeatures:\n\n- Fast, low-allocation parser and runtime\n  - Many simple expressions are zero-allocation\n- Type checking during parsing\n- Typed scalar functions and lazy `func()` values\n- Simple\n  - Easy to learn\n  - Easy to read\n  - No hiding complex branching logic in expressions\n- Intuitive, e.g. `\"id\" + 1` =\u003e `\"id1\"`\n- Useful error messages, example:\n  ```\n  missing right operand\n    not (1- \u003c= 5)\n    ......^\n  ```\n- Fuzz tested to prevent crashes\n\n## Usage\n\nTry it out on the [Go Playground](https://play.golang.org/p/Z0UcEBgfxu_r)! You can find many [example expressions in the tests](https://github.com/danielgtaylor/mexpr/blob/main/interpreter_test.go#L18).\n\n```go\nimport \"github.com/danielgtaylor/mexpr\"\n\n// Convenience for lexing/parsing/running in one step:\nresult, err := mexpr.Eval(\"a \u003e b\", map[string]any{\n\t\"a\": 2,\n\t\"b\": 1,\n})\n\n// Manual method with type checking and fast AST re-use. Error handling is\n// omitted for brevity.\nl := mexpr.NewLexer(\"a \u003e b\")\np := mexpr.NewParser(l)\nast, err := p.Parse()\ntypeExamples := map[string]any{\n\t\"a\": 2,\n\t\"b\": 1,\n}\nerr = mexpr.TypeCheck(ast, typeExamples)\ninterpreter := mexpr.NewInterpreter(ast)\nresult1, err := interpreter.Run(map[string]any{\n\t\"a\": 1,\n\t\"b\": 2,\n})\nresult2, err := interpreter.Run(map[string]any{\n\t\"a\": 150,\n\t\"b\": 30,\n})\n```\n\nPretty errors use the passed-in input along with the error's offset to display an arrow of where within the expression the error occurs.\n\nOffsets and lengths are rune-based, so caret placement stays correct for Unicode input.\n\n```go\ninputStr := \"2 * foo\"\n_, err := mexpr.Eval(inputStr, nil)\nif err != nil {\n\tfmt.Println(err.Pretty(inputStr))\n}\n```\n\n### Options\n\nWhen running the interpreter a set of options can be passed in to change behavior. Available options:\n\n| Option            | Default | Description                                                                                        |\n| ----------------- | ------- | -------------------------------------------------------------------------------------------------- |\n| `StrictMode`      | `false` | Be more strict, for example return an error when an identifier is not found rather than `nil`      |\n| `UnquotedStrings` | `false` | Enable the use of unquoted strings, i.e. return a string instead of `nil` for undefined parameters |\n\n```go\n// Using the top-level eval\nresult, err := mexpr.Eval(expression, inputObj, mexpr.StrictMode)\n\n// Using an interpreter instance\ninterpreter := mexpr.NewInterpreter(ast, mexpr.StrictMode)\nresult, err = interpreter.Run(inputObj)\n```\n\n## Syntax\n\n### Literals\n\n- **strings** double quoted e.g. `\"hello\"`\n- **numbers** e.g. `123`, `2.5`, `1_000_000`\n\nInternally all numbers are treated as `float64`, which means fewer conversions/casts when taking arbitrary JSON/YAML inputs.\n\n### Accessing properties\n\n- Use `.` between property names\n- Use `[` and `]` for indexes, which can be negative\n\n```py\nfoo.bar[0].value\n```\n\n### Arithmetic operators\n\n- `+` (addition)\n- `-` (subtration)\n- `*` (multiplication)\n- `/` (division)\n- `%` (modulus)\n- `^` (power)\n\n```py\n(1 + 2) * 3^2\n```\n\nMath operations between constants are precomputed when possible, so it is efficient to write meaningful operations like `size \u003c= 4 * 1024 * 1024`. The interpreter will see this as `size \u003c= 4194304`.\n\n### Comparison operators\n\n- `==` (equal)\n- `!=` (not equal)\n- `\u003c` (less than)\n- `\u003e` (greater than)\n- `\u003c=` (less than or equal to)\n- `\u003e=` (greater than or equal to)\n\n```py\n100 \u003e= 42\n```\n\n### Logical operators\n\n- `not` (negation)\n- `and`\n- `or`\n\nBoth `and` and `or` are short-circuited.\n\n```py\n1 \u003c 2 and 3 \u003c 4\n```\n\nNon-boolean values are converted to booleans. The following result in `true`:\n\n- numbers greater than zero\n- non-empty string\n- array with at least one item\n- map with at least one key/value pair\n\n### Functions\n\n- `identifier(...)`\n\nFunctions can be called by providing them in the variables map.\n\n```go\nresult, err := mexpr.Eval(\"myFunc(a, b)\", map[string]interface{}{\n\t\"myFunc\": func(a, b int) int { return a + b },\n\t\"a\": 1,\n\t\"b\": 2,\n})\n```\n\nCurrent limitations:\n\n- only regular, non-variadic functions are supported\n- parameter and return types must be `bool`, integer, float, or `string`\n- functions must have exactly one return value\n- zero-argument scalar functions can also be used as lazy values, e.g. `id + 1`\n\nNumeric arguments are coerced to the target Go type using standard Go conversions. For example, calling a function that takes an `int` with `1.9` will pass `1` to the function. If you need fractional behavior, make the function accept a float type.\n\n### String operators\n\n- Indexing, e.g. `foo[0]`\n- Slicing, e.g. `foo[1:2]` or `foo[2:]`\n- `.length` pseudo-property, e.g. `foo.length`\n- `.lower` pseudo-property for lowercase, e.g. `foo.lower`\n- `.upper` pseudo-property for uppercase, e.g. `foo.upper`\n- `+` (concatenation)\n- `in` e.g. `\"f\" in \"foo\"`\n- `contains` e.g. `\"foo\" contains \"f\"`\n- `startsWith` e.g. `\"foo\" startsWith \"f\"`\n- `endsWith` e.g. `\"foo\" endsWith \"o\"`\n\nIndexes are zero-based. Slice indexes are optional and are _inclusive_. `foo[1:2]` returns `el` if the `foo` is `hello`. Indexes can be negative, e.g. `foo[-1]` selects the last item in the array.\n\nAny value concatenated with a string will result in a string. For example `\"id\" + 1` will result in `\"id1\"`.\n\nString length, indexing, and slicing are Unicode-aware and operate on runes rather than raw bytes.\n\n#### Date Comparisons\n\nString dates \u0026 times can be compared if they follow RFC 3339 / ISO 8601 with or without timezones.\n\n- `before`, e.g. `start before \"2020-01-01\"`\n- `after`, e.g. `created after \"2020-01-01T12:00:00Z\"`\n\n### Array/slice operators\n\n- Indexing, e.g. `foo[1]`\n- Slicing, e.g. `foo[1:2]` or `foo[2:]`\n- `.length` pseudo-property, e.g. `foo.length`\n- `+` (concatenation)\n- `in` (has item), e.g. `1 in foo`\n- `contains` e.g. `foo contains 1`\n\nCommon Go slice inputs like `[]any`, `[]int`, `[]float64`, and `[]string` are supported directly without normalizing them into `[]any` first. Other slice and array types fall back to reflection.\n\nIndexes are zero-based. Slice indexes are optional and are _inclusive_. `foo[1:2]` returns `[2, 3]` if the `foo` is `[1, 2, 3, 4]`. Indexes can be negative, e.g. `foo[-1]` selects the last item in the array.\n\n#### Array/slice filtering\n\nA `where` clause can be used to filter the items in an array. The left side of the clause is the array to be filtered, while the right side is an expression to run on each item of the array. If the right side expression evaluates to true then the item is added to the result slice. For example:\n\n```\n// Get a list of items where the item.id is bigger than 3\nitems where id \u003e 3\n\n// More complex example\nitems where (id \u003e 3 and labels contains \"best\")\n```\n\nThis also makes it possible to implement one/any/all/none logic:\n\n```\n// One\n(items where id \u003e 3).length == 1\n\n// Any\nitems where id \u003e 3\n(items where id \u003e 3).length \u003e 0\n\n// All\n(items where id \u003e 3).length == items.length\n\n// None\nnot (items where id \u003e 3)\n(items where id \u003e 3).length == 0\n```\n\n### Map operators\n\n- Accessing values, e.g. `foo.bar.baz`\n- `in` (has key), e.g. `\"key\" in foo`\n- `contains` e.g. `foo contains \"key\"`\n\n### Conversions\n\nAny value concatenated with a string will result in a string. For example `\"id\" + 1` will result in `\"id1\"`.\n\nThe value of a variable can be mapped to a function. This allows the implementor to use functions to retrieve actual values of variables rather than pre-computing values:\n\n```go\nresult, _ := mexpr.Eval(`id + 1`, map[string]interface{}{\n    \"id\": func() int { return 123 },\n})\n// result is 124\n```\n\nIn combination with `and`/`or` short-circuiting, this allows lazy evaluation.\n\n#### Map wildcard filtering\n\nA `where` clause can be used as a wildcard key to filter values for all keys in a map. The left side of the clause is the map to be filtered, while the right side is an expression to run on each value of the map. If the right side expression evaluates to true then the value is added to the result slice. For example, given:\n\n```json\n{\n  \"operations\": {\n    \"id1\": { \"method\": \"GET\", \"path\": \"/op1\" },\n    \"id2\": { \"method\": \"PUT\", \"path\": \"/op2\" },\n    \"id3\": { \"method\": \"DELETE\", \"path\": \"/op3\" }\n  }\n}\n```\n\nYou can run:\n\n```\n// Get all operations where the HTTP method is GET\noperations where method == \"GET\"\n```\n\nAnd the result would be a slice of matched values:\n\n```json\n[{ \"method\": \"GET\", \"path\": \"/op1\" }]\n```\n\n## Performance\n\nPerformance compares favorably to [antonmedv/expr](https://github.com/antonmedv/expr) for both `Eval(...)` and cached program performance, which is expected given the more limited feature set. The `slow` benchmarks include lexing/parsing/interpreting while the `cached` ones are just the interpreting step. The `complex` example expression used is non-trivial: `foo.bar / (1 * 1024 * 1024) \u003e= 1.0 and \"v\" in baz and baz.length \u003e 3 and arr[2:].length == 1`.\n\n```\ngoos: darwin\ngoarch: amd64\npkg: github.com/danielgtaylor/mexpr\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\nBenchmark/mexpr-field-slow-12           3673572       286.5 ns/op    144 B/op      6 allocs/op\nBenchmark/_expr-field-slow-12            956689      1276 ns/op     1096 B/op     23 allocs/op\n\nBenchmark/mexpr-comparison-slow-12      1000000      1020 ns/op      656 B/op     16 allocs/op\nBenchmark/_expr-comparison-slow-12       383491      3069 ns/op     2224 B/op     38 allocs/op\n\nBenchmark/mexpr-logical-slow-12         1000000      1063 ns/op      464 B/op     17 allocs/op\nBenchmark/_expr-logical-slow-12          292824      4148 ns/op     2336 B/op     38 allocs/op\n\nBenchmark/mexpr-math-slow-12            1000000      1035 ns/op      656 B/op     16 allocs/op\nBenchmark/_expr-math-slow-12             399708      3004 ns/op     2184 B/op     38 allocs/op\n\nBenchmark/mexpr-string-slow-12          1822945       655.6 ns/op    258 B/op     10 allocs/op\nBenchmark/_expr-string-slow-12           428604      2508 ns/op     1640 B/op     35 allocs/op\n\nBenchmark/mexpr-index-slow-12           2015856       592.0 ns/op    280 B/op     10 allocs/op\nBenchmark/_expr-index-slow-12            517360      2301 ns/op     1872 B/op     30 allocs/op\n\nBenchmark/mexpr-complex-slow-12          244039      5078 ns/op     2232 B/op     64 allocs/op\nBenchmark/_expr-complex-slow-12           69387     16825 ns/op    14378 B/op    107 allocs/op\n\nBenchmark/mexpr-field-cached-12       100000000        11.37 ns/op     0 B/op      0 allocs/op\nBenchmark/_expr-field-cached-12         7761153       146.5 ns/op     48 B/op      2 allocs/op\n\nBenchmark/mexpr-comparison-cached-12   38098502        30.93 ns/op     0 B/op      0 allocs/op\nBenchmark/_expr-comparison-cached-12    4563463       251.0 ns/op     64 B/op      3 allocs/op\n\nBenchmark/mexpr-logical-cached-12      37563720        31.35 ns/op     0 B/op      0 allocs/op\nBenchmark/_expr-logical-cached-12      11000991       105.9 ns/op     32 B/op      1 allocs/op\n\nBenchmark/mexpr-math-cached-12         24463279        47.41 ns/op     8 B/op      1 allocs/op\nBenchmark/_expr-math-cached-12          4531693       268.0 ns/op     72 B/op      4 allocs/op\n\nBenchmark/mexpr-string-cached-12       43399368        26.83 ns/op     0 B/op      0 allocs/op\nBenchmark/_expr-string-cached-12        7302940       162.0 ns/op     48 B/op      2 allocs/op\n\nBenchmark/mexpr-index-cached-12        45289230        25.67 ns/op     0 B/op      0 allocs/op\nBenchmark/_expr-index-cached-12         6057562       180.0 ns/op     48 B/op      2 allocs/op\n\nBenchmark/mexpr-complex-cached-12       4271955       278.7 ns/op     40 B/op      3 allocs/op\nBenchmark/_expr-complex-cached-12       1456266       818.7 ns/op    208 B/op      9 allocs/op\n\n```\n\nOn average mexpr is around 3-10x faster for both full parsing and cached performance.\n\n## References\n\nThese were a big help in understanding how Pratt parsers work:\n\n- https://dev.to/jrop/pratt-parsing\n- https://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/\n- https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html\n- https://www.oilshell.org/blog/2017/03/31.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielgtaylor%2Fmexpr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielgtaylor%2Fmexpr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielgtaylor%2Fmexpr/lists"}