├── .config
└── nextest.toml
├── .github
├── CODEOWNERS
└── workflows
│ ├── build.yml
│ └── deploy.yml
├── .gitignore
├── CHANGELOG.md
├── CITATION.bib
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── Makefile
├── README.md
├── benches
└── example_benchmarks.rs
├── build.rs
├── clippy.toml
├── release-instructions.md
├── rust-toolchain.toml
├── scripts
└── minimize.rkt
├── src
├── actions.rs
├── ast
│ ├── check_shadowing.rs
│ ├── desugar.rs
│ ├── expr.rs
│ ├── mod.rs
│ ├── parse.rs
│ └── remove_globals.rs
├── cli.rs
├── constraint.rs
├── core.rs
├── extract.rs
├── function
│ ├── binary_search.rs
│ ├── index.rs
│ ├── mod.rs
│ └── table.rs
├── gj.rs
├── lib.rs
├── main.rs
├── serialize.rs
├── sort
│ ├── bigint.rs
│ ├── bigrat.rs
│ ├── bool.rs
│ ├── f64.rs
│ ├── fn.rs
│ ├── i64.rs
│ ├── macros.rs
│ ├── map.rs
│ ├── mod.rs
│ ├── multiset.rs
│ ├── set.rs
│ ├── string.rs
│ ├── unit.rs
│ └── vec.rs
├── termdag.rs
├── typechecking.rs
├── unionfind.rs
├── util.rs
└── value.rs
├── tests
├── antiunify.egg
├── array.egg
├── bdd.egg
├── before-proofs.egg
├── bignum.egg
├── birewrite.egg
├── bitwise.egg
├── bool.egg
├── calc.egg
├── combinators.egg
├── combined-nested.egg
├── container-rebuild.egg
├── cyk.egg
├── cykjson.egg
├── cykjson_End.csv
├── cykjson_Prod.csv
├── cykjson_medium_token.csv
├── cykjson_small_token.csv
├── datatypes.egg
├── delete.egg
├── eggcc-extraction.egg
├── eqsat-basic-multiset.egg
├── eqsat-basic.egg
├── eqsolve.egg
├── f64.egg
├── fail-typecheck
│ ├── arity-mismatch.egg
│ ├── constructor_non_sort.egg
│ ├── looking_up_nonconstructor_in_action_case_let.egg
│ ├── looking_up_nonconstructor_in_action_case_set.egg
│ ├── looking_up_nonconstructor_in_action_case_union.egg
│ ├── looking_up_nonconstructor_in_birewrite.egg
│ ├── looking_up_nonconstructor_in_rewrite.egg
│ ├── repro-containers-disallowed.egg
│ ├── repro-duplicated-var.egg
│ ├── semi_naive_set_function.egg
│ ├── set-a-primitive.egg
│ ├── unbound.egg
│ ├── union_non_sort.egg
│ ├── unstable-fn-wrong-args-type.egg
│ ├── unstable-fn-wrong-args.egg
│ ├── unstable-fn-wrong-return-type.egg
│ └── unstable-fn-wrong-return.egg
├── fail_wrong_assertion.egg
├── fibonacci-demand.egg
├── fibonacci.egg
├── files.rs
├── fusion.egg
├── herbie-tutorial.egg
├── herbie.egg
├── i64.egg
├── include.egg
├── integer_math.egg
├── integration_test.rs
├── intersection.egg
├── interval.egg
├── knapsack.egg
├── lambda.egg
├── levenshtein-distance.egg
├── list.egg
├── looking_up_global.egg
├── looking_up_nonconstructor_in_rewrite_good.egg
├── map.egg
├── math-microbenchmark.egg
├── math.egg
├── matrix.egg
├── merge-during-rebuild.egg
├── merge-saturates.egg
├── merge_read.egg
├── multiset.egg
├── name-resolution.egg
├── no-messages
│ ├── README.md
│ ├── extract-vec-bench.egg
│ ├── python_array_optimize.egg
│ └── stresstest_large_expr.egg
├── path-union.egg
├── path.egg
├── pathproof.egg
├── points-to.egg
├── primitives.egg
├── prims.egg
├── push-pop.egg
├── rat-pow-eval.egg
├── repro-define.egg
├── repro-desugar-143.egg
├── repro-empty-query.egg
├── repro-equal-constant.egg
├── repro-equal-constant2.egg
├── repro-noteqbug.egg
├── repro-primitive-query.egg
├── repro-querybug.egg
├── repro-querybug2.egg
├── repro-querybug3.egg
├── repro-querybug4.egg
├── repro-should-saturate.egg
├── repro-silly-panic.egg
├── repro-typechecking-schedule.egg
├── repro-unsound-htutorial.egg
├── repro-unsound.egg
├── repro-vec-unequal.egg
├── resolution.egg
├── rw-analysis.egg
├── schedule-demo.egg
├── set.egg
├── set_sort_function.egg
├── stratified.egg
├── string.egg
├── string_quotes.csv
├── string_quotes.egg
├── subsume-relation.egg
├── subsume.egg
├── terms.rs
├── test-combined-steps.egg
├── test-combined.egg
├── towers-of-hanoi.egg
├── tricky-type-checking.egg
├── type-constraints-tests.egg
├── typecheck.egg
├── typeinfer.egg
├── unification-points-to.egg
├── unify.egg
├── unstable-fn.egg
├── until.egg
└── vec.egg
└── web-demo
├── Cargo.toml
├── examples.py
├── src
└── lib.rs
└── static
├── base64.mjs
├── index.html
├── lzma-url.mjs
└── worker.js
/.config/nextest.toml:
--------------------------------------------------------------------------------
1 | [profile.default]
2 | slow-timeout.period = "5s"
3 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @egraphs-good/egglog-reviewers
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches:
6 | - "main"
7 | pull_request:
8 | workflow_dispatch:
9 | jobs:
10 | test:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - run: echo "CARGO_INCREMENTAL=0" >> "$GITHUB_ENV"
14 | - uses: actions/checkout@v3
15 | - uses: taiki-e/install-action@v2
16 | with:
17 | tool: nextest
18 | - uses: Swatinem/rust-cache@v2
19 | - run: make test
20 | nits:
21 | runs-on: ubuntu-latest
22 | steps:
23 | - run: echo "CARGO_INCREMENTAL=0" >> "$GITHUB_ENV"
24 | - uses: actions/checkout@v3
25 | - uses: Swatinem/rust-cache@v2
26 | - run: make nits
27 | benchmark:
28 | runs-on: ubuntu-latest
29 | steps:
30 | - run: echo "CARGO_INCREMENTAL=0" >> "$GITHUB_ENV"
31 | - uses: actions/checkout@v4
32 | - uses: taiki-e/install-action@v2
33 | with:
34 | tool: cargo-codspeed
35 | - uses: Swatinem/rust-cache@v2
36 | - run: cargo codspeed build
37 | - uses: CodSpeedHQ/action@v3
38 | with:
39 | run: cargo codspeed run
40 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Web Demo and Docs
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Disable Incremental Build
10 | run: echo "CARGO_INCREMENTAL=0" >> "$GITHUB_ENV"
11 |
12 | - uses: actions/checkout@v3
13 |
14 | - name: Install wasm-pack
15 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
16 |
17 | - name: Cache
18 | uses: Swatinem/rust-cache@v2
19 |
20 | - name: Build
21 | run: make web
22 |
23 | # Upload the built website as an artifact, so that runs which are not deployed
24 | # (i.e. other branches and PRs) to Github Pages can be be downloaded
25 | # (https://docs.github.com/en/actions/managing-workflow-runs/downloading-workflow-artifacts)
26 | # and viewed locally.
27 | #
28 | # When Github adds support for PR Github Pages previews
29 | # (https://github.com/orgs/community/discussions/7730)
30 | # this can be removed.
31 | - name: Upload web artifact
32 | uses: actions/upload-artifact@v4
33 | with:
34 | name: www
35 | path: target/www
36 |
37 | - name: Deploy
38 | uses: peaceiris/actions-gh-pages@v3
39 | # only actually deploy if pushed to main branch
40 | if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
41 | with:
42 | github_token: ${{ secrets.GITHUB_TOKEN }}
43 | publish_dir: target/www
44 | force_orphan: true
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | notes.md
3 | .vscode
4 | node_modules
5 | dist/
6 | pkg/
7 |
8 | # perf stuff
9 | flamegraph.svg
10 | perf.data*
11 | profile.json
12 |
13 | _scratch.egg
14 | /*.egg
15 | *.log
16 | *.dot
17 | *.svg
18 | *.DS_Store
19 |
20 | # racket
21 | scripts/compiled
22 | tests/*.json
23 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changes
2 |
3 | ## [Unreleased] - ReleaseDate
4 |
5 | ## [0.4.0] - 2024-1-TODO
6 |
7 | Semantic change (BREAKING)
8 |
9 | - Split `function` into `constructor` and `functions` with merge functions. (#461)
10 | - Remove `:default` keyword. (#461)
11 | - Disallow lookup functions in the right hand side. (#461)
12 | - Remove `:on_merge`, `:cost`, and `:unextractable` from functions, require `:no-merge` (#485)
13 |
14 | Language features
15 |
16 | - Add multi-sets (#446, #454, #471)
17 | - Recursive datatypes with `datatype*` (#432)
18 | - Add `BigInt` and `BigRat` and move `Rational` to `egglog-experimental` (#457, #475, #499)
19 |
20 | Command-line interface and web demo
21 |
22 | - Display build info when in binary mode (#427)
23 | - Expose egglog CLI (#507, #510)
24 | - Add a new interactive visualizer (#426)
25 | - Disable build script for library builds (#467)
26 |
27 | Rust interface improvements
28 |
29 | - Make the type constraint system user-extensible (#509)
30 | - New extensible parser (#435, #450, #484, #489, #497, #498, #506)
31 | - Remove `Value::tag` when in release mode (#448)
32 |
33 | Extraction
34 |
35 | - Remove unused 'serde-1' attribute (#465)
36 | - Extract egraph-serialize features (#466)
37 | - Expose extraction module publicly (#503)
38 | - Use `set-of` instead of `set-insert` for extraction result of sets. (#514)
39 |
40 | Bug fixes
41 |
42 | - Fix the behavior of i64 primitives on overflow (#502)
43 | - Fix memory blowup issue in `TermDag::to_string`
44 | - Fix the issue that rule names are ignored (#500)
45 |
46 | Cleanups and improvements
47 |
48 | - Allow disabling messages for performance (#492)
49 | - Determinize egglog (#438, #439)
50 | - Refactor sort extraction API (#495)
51 | - Add automated benchmarking to continuous integration (#443)
52 | - Improvements to performance of testing (#458)
53 | - Other small cleanups and improvements (#428, #429, #433, #434, #436, #437, #440, #442, #444, #445, #449, #453, #456, #469, #474, #477, #490, #491, #494, #501, #504, #508, #511)
54 |
55 | ## [0.3.0] - 2024-9-12
56 |
57 | Cleanups
58 |
59 | - Remove `declare` and `calc` keywords (#418, #419)
60 | - Fix determinism bug from new combined ruleset code (#406)
61 | - Fix performance bug in typechecking containers (#395)
62 | - Minor improvements to the web demo (#413, #414, #415)
63 | - Add power operators to i64 and f64 (#412)
64 |
65 | Error reporting
66 |
67 | - Report the source locations for errors (#389, #398, #405)
68 |
69 | Serialization
70 |
71 | - Include subsumption information in serialization (#424)
72 | - Move splitting primitive nodes into the serialize library (#407)
73 | - Support omitted nodes (#394)
74 | - Support Class ID <-> Value conversion (#396)
75 |
76 | REPL
77 |
78 | - Evaluate multiple lines at once (#402)
79 | - Show build information in the REPL (#427)
80 |
81 | Higher-order functions (UNSTABLE)
82 |
83 | - Infer types of function values based on names (#400)
84 |
85 | Import relation from files
86 |
87 | - Accept f64 function arguments #384
88 |
89 | ## [0.2.0] - 2024-05-17
90 |
91 | Usability
92 |
93 | - Improve statistics for runs (#284)
94 | - Improve user-defined primitive support (#280, #288)
95 | - Improve serialization (#293)
96 | - Add more container primitives (#306)
97 |
98 | Web demo
99 |
100 | - Add slidemode in the web demo (#302)
101 | - Fix box shadowing problem (#372)
102 |
103 | Refactor
104 |
105 | - Big refactoring to the intermediate representation (#320)
106 | - Make global variables a syntactic sugar (#338)
107 | - Drop experimental implementation for proofs and terms (#320, #342)
108 |
109 | New features
110 |
111 | - Support Subsumptions (#301)
112 | - Add basic support for first-class, higher-order functions (UNSTABLE) (#348)
113 | - Support combined rulesets (UNSTABLE) (#362)
114 |
115 | Others
116 |
117 | - Numerous bug fixes
118 |
119 | ## [0.1.0] - 2023-10-24
120 | This is egglog's first release! Egglog is ready for use, but is still fairly experimental. Expect some significant changes in the future.
121 |
122 | - Egglog is better than [egg](https://github.com/egraphs-good/egg) in many ways, including performance and new features.
123 | - Egglog now includes cargo documentation for the language interface.
124 |
125 | As of yet, the rust interface is not documented or well supported. We reccomend using the language interface. Egglog also lacks proofs, a feature that egg has.
126 |
127 |
128 | [Unreleased]: https://github.com/egraphs-good/egglog/compare/v0.2.0...HEAD
129 | [0.1.0]: https://github.com/egraphs-good/egglog/tree/v0.1.0
130 | [0.2.0]: https://github.com/egraphs-good/egglog/tree/v0.2.0
131 | [0.3.0]: https://github.com/egraphs-good/egglog/tree/v0.3.0
132 | [0.4.0]: https://github.com/egraphs-good/egglog/tree/v0.4.0
133 |
134 |
135 | See release-instructions.md for more information on how to do a release.
136 |
--------------------------------------------------------------------------------
/CITATION.bib:
--------------------------------------------------------------------------------
1 | @article{egglog,
2 | author = {
3 | Zhang, Yihong and
4 | Wang, Yisu Remy and
5 | Flatt, Oliver and
6 | Cao, David and
7 | Zucker, Philip and
8 | Rosenthal, Eli and
9 | Tatlock, Zachary and
10 | Willsey, Max
11 | },
12 | title = {Better Together: Unifying Datalog and Equality Saturation},
13 | year = {2023},
14 | issue_date = {June 2023},
15 | publisher = {Association for Computing Machinery},
16 | address = {New York, NY, USA},
17 | volume = {7},
18 | number = {PLDI},
19 | url = {https://doi.org/10.1145/3591239},
20 | doi = {10.1145/3591239},
21 | abstract = {
22 | We present egglog, a fixpoint reasoning system that unifies Datalog and equality saturation (EqSat).
23 | Like Datalog, egglog supports efficient incremental execution, cooperating analyses, and lattice-based reasoning.
24 | Like EqSat, egglog supports term rewriting, efficient congruence closure, and extraction of optimized terms.
25 | We identify two recent applications -- a unification-based pointer analysis in Datalog and an EqSat-based floating-point term rewriter --
26 | that have been hampered by features missing from Datalog but found in EqSat or vice-versa.
27 | We evaluate our system by reimplementing those projects in egglog.
28 | The resulting systems in egglog are faster, simpler, and fix bugs found in the original systems.
29 | },
30 | journal = {Proc. ACM Program. Lang.},
31 | month = {jun},
32 | articleno = {125},
33 | numpages = {25},
34 | keywords = {Equality saturation, Program optimization, Datalog, Rewrite systems}
35 | }
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | edition = "2021"
3 | name = "egglog"
4 | version = "0.4.0"
5 | description = "egglog is a language that combines the benefits of equality saturation and datalog. It can be used for analysis, optimization, and synthesis of programs. It is the successor to the popular rust library egg."
6 | repository = "https://github.com/egraphs-good/egglog"
7 | keywords = ["e-graphs", "egglog", "datalog", "compiler", "equality"]
8 | license = "MIT"
9 | readme = "README.md"
10 |
11 | [[bench]]
12 | name = "example_benchmarks"
13 | harness = false
14 |
15 | [workspace]
16 | members = [".", "web-demo"]
17 |
18 | [[test]]
19 | name = "files"
20 | harness = false
21 | required-features = ["bin"]
22 |
23 | [[bin]]
24 | name = "egglog"
25 | path = "src/main.rs"
26 | required-features = ["bin"]
27 |
28 | [features]
29 | default = ["bin"]
30 |
31 | bin = [
32 | "serde",
33 | "graphviz",
34 | "dep:clap",
35 | "dep:env_logger",
36 | "dep:chrono",
37 | ]
38 | serde = ["egraph-serialize/serde"]
39 | graphviz = ["egraph-serialize/graphviz"]
40 | wasm-bindgen = ["instant/wasm-bindgen", "dep:getrandom"]
41 | nondeterministic = []
42 |
43 | [dependencies]
44 | clap = { version = "4", features = ["derive"], optional = true }
45 | egraph-serialize = { version = "0.2.0", default-features = false }
46 | env_logger = { version = "0.10", optional = true }
47 | hashbrown = { version = "0.15" }
48 | im-rc = "15.1.0"
49 | im = "15.1.0"
50 | indexmap = "2.0"
51 | instant = "0.1"
52 | lazy_static = "1.4"
53 | log = "0.4"
54 | num = "0.4.3"
55 | ordered-float = { version = "3.7" }
56 | rustc-hash = "1.1"
57 | smallvec = "1.11"
58 | symbol_table = { version = "0.4.0", features = ["global"] }
59 | thiserror = "1"
60 |
61 | # Need to add "js" feature for "graphviz-rust" to work in wasm
62 | getrandom = { version = "0.2.10", optional = true, features = ["js"] }
63 |
64 | [build-dependencies]
65 | chrono = { version = "0.4", default-features = false, features = ["now"], optional = true }
66 |
67 | [dev-dependencies]
68 | codspeed-criterion-compat = "2.7.2"
69 | glob = "0.3.1"
70 | libtest-mimic = "0.6.1"
71 |
72 | [profile.release]
73 | incremental = true
74 |
75 | # https://github.com/mstange/samply/?tab=readme-ov-file#turn-on-debug-info-for-full-stacks
76 | [profile.profiling]
77 | inherits = "release"
78 | debug = true
79 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Max Willsey
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all web test nits docs serve graphs rm-graphs
2 |
3 | RUST_SRC=$(shell find . -type f -wholename '*/src/*.rs' -or -name 'Cargo.toml')
4 | TESTS=$(shell find tests/ -type f -name '*.egg' -not -name '*repro-*')
5 |
6 | WWW=${PWD}/target/www/
7 |
8 | WEB_SRC=$(wildcard web-demo/static/*)
9 |
10 | WASM=web_demo.js web_demo_bg.wasm
11 | DIST_WASM=$(addprefix ${WWW}, ${WASM})
12 |
13 | all: test nits web docs
14 |
15 | test:
16 | cargo nextest run --release
17 | # nextest doesn't run doctests, so do it here
18 | cargo test --doc --release
19 |
20 | nits:
21 | @rustup component add clippy
22 | cargo clippy --tests -- -D warnings
23 | @rustup component add rustfmt
24 | cargo fmt --check
25 |
26 | docs:
27 | mkdir -p ${WWW}
28 | cargo doc --no-deps --all-features
29 | touch target/doc/.nojekyll # prevent github from trying to run jekyll
30 | cp -r target/doc ${WWW}/docs
31 |
32 | web: docs ${DIST_WASM} ${WEB_SRC} ${WWW}/examples.json
33 | mkdir -p ${WWW}
34 | cp ${WEB_SRC} ${WWW}
35 | find target -name .gitignore -delete # ignored files are wonky to deploy
36 |
37 | serve:
38 | cargo watch --shell "make web && python3 -m http.server 8080 -d ${WWW}"
39 |
40 | ${WWW}/examples.json: web-demo/examples.py ${TESTS}
41 | $^ > $@
42 |
43 | ${DIST_WASM}: ${RUST_SRC}
44 | wasm-pack build web-demo --target no-modules --no-typescript --out-dir ${WWW}
45 | rm -f ${WWW}/{.gitignore,package.json}
46 |
47 | graphs: $(patsubst %.egg,%.svg,$(filter-out $(wildcard tests/repro-*.egg),$(wildcard tests/*.egg)))
48 |
49 | json: $(patsubst %.egg,%.json,$(filter-out $(wildcard tests/repro-*.egg),$(wildcard tests/*.egg)))
50 |
51 | %.svg: %.egg
52 | cargo run --release -- --to-dot --to-svg $^
53 |
54 | %.json: %.egg
55 | cargo run --release -- --to-json $^
56 |
57 | rm-graphs:
58 | rm -f tests/*.dot tests/*.svg
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # egglog
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | This is the repo for the `egglog` tool accompanying the paper
11 | "Better Together: Unifying Datalog and Equality Saturation"
12 | ([ACM DL](https://dl.acm.org/doi/10.1145/3591239), [arXiv](https://arxiv.org/abs/2304.04332)).
13 |
14 | If you use this work, please use [this citation](./CITATION.bib).
15 |
16 | See also the Python binding, which provides a bit more documentation:
17 | https://egglog-python.readthedocs.io/
18 |
19 | ## Chat
20 |
21 | There is a Zulip chat about egglog here:
22 | https://egraphs.zulipchat.com/#narrow/stream/375765-egglog
23 |
24 | ## Prerequisites & compilation
25 |
26 | ```
27 | apt-get install make cargo
28 | cargo install cargo-nextest
29 | make all
30 | ```
31 |
32 |
33 | ## Usage
34 |
35 | ```
36 | cargo run [-f fact-path] [-naive] [--to-json] [--to-dot] [--to-svg]
37 | ```
38 |
39 | or just
40 |
41 | ```
42 | cargo run
43 | ```
44 |
45 | for the REPL.
46 |
47 | * The `--to-dot` command will save a graphviz dot file at the end of the program, replacing the `.egg` extension with `.dot`.
48 | * The `--to-svg`, which requires [Graphviz to be installed](https://graphviz.org/download/), will save a graphviz svg file at the end of the program, replacing the `.egg` extension with `.svg`.
49 |
50 |
51 | ## Community extensions
52 |
53 | * [@hatoo](https://github.com/hatoo) maintains an [egglog-language extension](https://marketplace.visualstudio.com/items?itemName=hatookov.egglog-language) in VS Code (just search for "egglog" in VS Code).
54 | * [@segeljakt](https://github.com/segeljakt) maintains a [Neovim plugin](https://github.com/segeljakt/tree-sitter-egg) for egglog using tree-sitter.
55 |
56 | ## Development
57 |
58 | To run the tests use `make test`.
59 |
60 | ## Benchmarks
61 |
62 | We run all of our "examples" [as benchmarks in codspeed](https://codspeed.io/egraphs-good/egglog). These are in CI
63 | for every commit in main and for all PRs. It will run the examples with extra instrumentation added so that it can
64 | capture a single trace of the CPU interactions ([src](https://docs.codspeed.io/features/understanding-the-metrics/)):
65 |
66 | > CodSpeed instruments your benchmarks to measure the performance of your code. A benchmark will be run only once and the CPU behavior will be simulated. This ensures that the measurement is as accurate as possible, taking into account not only the instructions executed but also the cache and memory access patterns. The simulation gives us an equivalent of the CPU cycles that includes cache and memory access.
67 |
68 | Since many of the shorter running benchmarks have unstable timings due to non deterministic performance ([like in the memory allocator](https://github.com/oxc-project/backlog/issues/89)),
69 | we ["ignore"](https://docs.codspeed.io/features/ignoring-benchmarks/) them in codspeed. That way, we still
70 | capture their performance, but their timings don't show up in our reports by default.
71 |
72 | We use 50ms as our cutoff currently, any benchmarks shorter than that are ignored. This number was selected to try to ignore
73 | any benchmarks with have changes > 1% when they haven't been modified. Note that all the ignoring is done manually,
74 | so if you add another example that's short, an admin on the codspeed project will need to manually ignore it.
75 |
76 | ## Profiling
77 |
78 | One way to profile egglog is to use [samply](https://github.com/mstange/samply/). Here's how you can use it:
79 |
80 | ```bash
81 | # install samply
82 | cargo install --locked samply
83 | # build a profile build which includes debug symbols
84 | cargo build --profile profiling
85 | # run the egglog file and profile
86 | samply record ./target/profiling/egglog tests/extract-vec-bench.egg
87 | # [optional] run the egglog file without logging or printing messages, which can help reduce the stdout
88 | # when you are profiling extracting a large expression
89 | env RUST_LOG=error samply record ./target/profiling/egglog --dont-print-messages tests/extract-vec-bench.egg
90 | ```
91 |
92 | # Documentation
93 |
94 | To view documentation, run `cargo doc --open`.
95 |
96 |
--------------------------------------------------------------------------------
/benches/example_benchmarks.rs:
--------------------------------------------------------------------------------
1 | use codspeed_criterion_compat::{criterion_group, criterion_main, Criterion};
2 | use egglog::EGraph;
3 |
4 | fn run_example(filename: &str, program: &str, no_messages: bool) {
5 | let mut egraph = EGraph::default();
6 | if no_messages {
7 | egraph.disable_messages();
8 | }
9 | egraph
10 | .parse_and_run_program(Some(filename.to_owned()), program)
11 | .unwrap();
12 | // test performance of serialization as well
13 | let _ = egraph.serialize(egglog::SerializeConfig::default());
14 | }
15 |
16 | pub fn criterion_benchmark(c: &mut Criterion) {
17 | for entry in glob::glob("tests/**/*.egg").unwrap() {
18 | let path = entry.unwrap().clone();
19 | let path_string = path.to_string_lossy().to_string();
20 | if path_string.contains("fail-typecheck") {
21 | continue;
22 | }
23 | let name = path.file_stem().unwrap().to_string_lossy().to_string();
24 | let filename = path.to_string_lossy().to_string();
25 | let program = std::fs::read_to_string(&filename).unwrap();
26 | let no_messages = path_string.contains("no-messages");
27 | c.bench_function(&name, |b| {
28 | b.iter(|| run_example(&filename, &program, no_messages))
29 | });
30 | }
31 | }
32 |
33 | criterion_group!(benches, criterion_benchmark);
34 | criterion_main!(benches);
35 |
--------------------------------------------------------------------------------
/build.rs:
--------------------------------------------------------------------------------
1 | #[cfg(feature = "bin")]
2 | #[allow(clippy::disallowed_macros)] // for println!
3 | fn main() {
4 | use std::{env, process::Command};
5 |
6 | let git_hash = Command::new("git")
7 | .args(["rev-parse", "--short", "HEAD"])
8 | .output()
9 | .map(|output| {
10 | String::from_utf8(output.stdout)
11 | .map(|s| "_".to_owned() + &s)
12 | .unwrap_or_default()
13 | })
14 | .unwrap_or_default();
15 | let build_date = chrono::Utc::now().format("%Y-%m-%d");
16 | let version = env::var("CARGO_PKG_VERSION").unwrap();
17 | let full_version = format!("{}_{}{}", version, build_date, git_hash);
18 | println!("cargo:rustc-env=FULL_VERSION={}", full_version);
19 | }
20 |
21 | #[cfg(not(feature = "bin"))]
22 | fn main() {}
23 |
--------------------------------------------------------------------------------
/clippy.toml:
--------------------------------------------------------------------------------
1 | disallowed-types = [
2 | # prefer hashbrown
3 | "std::collections::HashMap",
4 | "std::collections::HashSet",
5 | # prefer instant crate, it works on wasm
6 | "std::time::Instant",
7 | ]
8 |
9 | disallowed-macros = [
10 | # only allowed in main.rs
11 | "std::print",
12 | "std::println",
13 | # use log crate instead
14 | "std::eprint",
15 | "std::eprintln",
16 | ]
17 |
18 | type-complexity-threshold = 350
19 |
--------------------------------------------------------------------------------
/release-instructions.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | How to do a release:
4 | 1. Update `CHANGELOG.md` with a new entry and new link at the bottom.
5 | 2. Find and replace in the codebase to update the version number. Make sure to get `Cargo.toml` and places in the changelog. Be careful not the screw up old links though!
6 | 4. Commit.
7 | 5. Tag the commit with the version number.
8 | 6. Make a PR and make sure the tag gets added.
9 | 7. Merge the PR
10 | 8. `cargo publish --dry-run`
11 | 1. Sometimes this can result in an error- you may need to run `cargo update` to update `cargo.lock`
12 | 9. `cargo publish`
13 |
--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | channel = "1.79.0"
3 |
--------------------------------------------------------------------------------
/scripts/minimize.rkt:
--------------------------------------------------------------------------------
1 | #lang racket
2 |
3 | (require racket/runtime-path)
4 |
5 | (define (read-lines port)
6 | (define line (read port))
7 | (if (eof-object? line)
8 | '()
9 | (cons line (read-lines port))))
10 |
11 | ;; don't remove any check statements
12 | (define (remove-at n lst)
13 | (define-values (head tail) (split-at lst n))
14 | (define line (car tail))
15 | (if (and (list? line)
16 | (or (equal? (first line) 'check)
17 | (equal? (first line) 'keep)))
18 | lst
19 | (append head (cdr tail))))
20 |
21 | (define-runtime-path egglog-binary
22 | "../target/release/egglog")
23 |
24 | ;; timeout in seconds
25 | (define TIMEOUT 5)
26 | (define ITERATIONS 1)
27 | (define RANDOM-SAMPLE-FACTOR 1)
28 | (define MUST-NOT-STRINGS `())
29 | (define TARGET-STRINGS `("src/lib.rs:250"))
30 |
31 | (define (desugar line)
32 | (match line
33 | [`(keep ,body)
34 | body]
35 | [else line]))
36 |
37 | (define (desired-error? program)
38 | (displayln (format "Trying program of size ~a" (length program)))
39 | (flush-output)
40 | (define-values (egglog-process egglog-output egglog-in err)
41 | (subprocess (current-output-port) #f #f egglog-binary))
42 |
43 | (for ([line program])
44 | (writeln (desugar line) egglog-in))
45 | (close-output-port egglog-in)
46 |
47 | (when (not (sync/timeout TIMEOUT egglog-process))
48 | (displayln "Timed out"))
49 | (subprocess-kill egglog-process #t)
50 | (displayln "checking output")
51 | (flush-output)
52 | (define err-str (read-string 10000 err))
53 | (close-input-port err)
54 | (define still-unsound (and (string? err-str)
55 | (for/and ([must-not-string MUST-NOT-STRINGS])
56 | (not (string-contains? err-str must-not-string)))
57 | (for/or ([TARGET-STRING TARGET-STRINGS])
58 | (string-contains? err-str TARGET-STRING))))
59 | (println err-str)
60 | (if still-unsound
61 | (displayln "Reduced program")
62 | (displayln "Did not reduce"))
63 | still-unsound)
64 |
65 | (define (min-program program index)
66 | (fprintf (current-output-port) "Trying to remove index ~a out of ~a\n" index (length program))
67 | (flush-output)
68 |
69 | (cond
70 | [(>= index (length program)) program]
71 | [else
72 | (define removed (remove-at index program))
73 | (cond
74 | [(equal? (length removed) (length program))
75 | (min-program removed (+ index 1))]
76 | [(desired-error? removed)
77 | (min-program removed index)]
78 | [else (min-program program (+ index 1))])]))
79 |
80 | (define (remove-random-lines program n)
81 | (cond
82 | [(<= n 0) program]
83 | [else
84 | (define index (random (length program)))
85 | (define new-program (remove-at index program))
86 | (remove-random-lines new-program (- n 1))]))
87 |
88 | (define (min-program-random program iters)
89 | (cond
90 | [(= iters 0) program]
91 | [else
92 | (define index (random (length program)))
93 | (define new-program (remove-at index program))
94 | (if (desired-error? new-program)
95 | (min-program-random new-program (- iters 1))
96 | (min-program-random program (- iters 1)))]))
97 |
98 | (define (min-program-greedy program num)
99 | (cond
100 | [(< num 1)
101 | program]
102 | [else
103 | (define prog (remove-random-lines program num))
104 | (if (desired-error? prog)
105 | (min-program-greedy prog num)
106 | (min-program-greedy program (* num 2/3)))]))
107 |
108 | (define (random-and-sequential program)
109 | (define binary (min-program-greedy program (/ (length program) 2)))
110 | (define random-prog (min-program-random binary (* (length binary) RANDOM-SAMPLE-FACTOR)))
111 | (min-program random-prog 0))
112 |
113 | (define (min-iterations program)
114 | (define programs (for/list ([i (in-range ITERATIONS)])
115 | (random-and-sequential program)))
116 | (first (sort programs (lambda (a b) (< (length a) (length b))))))
117 |
118 |
119 |
120 | (define (minimize port-in port-out)
121 | #;((define-values (process out in err) (subprocess #f #f #f "cargo"))
122 | (define err-str (read-string 800 err))
123 | (when (not (string=? err-str ""))
124 | (error err-str))
125 | (close-input-port out)
126 | (close-output-port in)
127 | (close-input-port err)
128 | (subprocess-wait process))
129 |
130 | (define egglog (read-lines port-in))
131 | (pretty-print egglog)
132 |
133 | (when (not (desired-error? egglog))
134 | (error "Original program did not have error"))
135 |
136 | (define minimized (min-iterations egglog))
137 | (for ([line minimized])
138 | (writeln (desugar line) port-out)))
139 |
140 |
141 | (module+ main
142 | (command-line
143 | #:args (file-in file-out)
144 | (minimize (open-input-file file-in) (open-output-file file-out #:exists 'replace))))
145 |
--------------------------------------------------------------------------------
/src/ast/check_shadowing.rs:
--------------------------------------------------------------------------------
1 | use crate::*;
2 |
3 | #[derive(Clone, Debug, Default)]
4 | pub(crate) struct Names(HashMap);
5 |
6 | impl Names {
7 | fn check(&mut self, name: Symbol, new: Span) -> Result<(), Error> {
8 | if let Some(old) = self.0.get(&name) {
9 | Err(Error::Shadowing(name, old.clone(), new))
10 | } else {
11 | self.0.insert(name, new);
12 | Ok(())
13 | }
14 | }
15 |
16 | /// WARNING: this function does not handle `push` and `pop`.
17 | /// Because `Names` is contained on the `EGraph`, this will
18 | /// work correctly when executed from `process_command`, but
19 | /// a unit test that called this function multiple times without
20 | /// changing the `EGraph` will be wrong.
21 | pub(crate) fn check_shadowing(&mut self, command: &ResolvedNCommand) -> Result<(), Error> {
22 | match command {
23 | ResolvedNCommand::Sort(span, name, _args) => self.check(*name, span.clone()),
24 | ResolvedNCommand::Function(decl) => self.check(decl.name, decl.span.clone()),
25 | ResolvedNCommand::AddRuleset(span, name) => self.check(*name, span.clone()),
26 | ResolvedNCommand::UnstableCombinedRuleset(span, name, _args) => {
27 | self.check(*name, span.clone())
28 | }
29 | ResolvedNCommand::NormRule { rule, .. } => {
30 | let mut inner = self.clone();
31 | inner.check_shadowing_query(&rule.body)?;
32 | for action in rule.head.iter() {
33 | inner.check_shadowing_action(action)?;
34 | }
35 | Ok(())
36 | }
37 | ResolvedNCommand::CoreAction(action) => self.check_shadowing_action(action),
38 | ResolvedNCommand::Check(_span, query) => {
39 | let mut inner = self.clone();
40 | inner.check_shadowing_query(query)
41 | }
42 | ResolvedNCommand::Fail(_span, command) => {
43 | let mut inner = self.clone();
44 | inner.check_shadowing(command)
45 | }
46 | ResolvedNCommand::SetOption { .. } => Ok(()),
47 | ResolvedNCommand::RunSchedule(..) => Ok(()),
48 | ResolvedNCommand::PrintOverallStatistics => Ok(()),
49 | ResolvedNCommand::PrintTable(..) => Ok(()),
50 | ResolvedNCommand::PrintSize(..) => Ok(()),
51 | ResolvedNCommand::Input { .. } => Ok(()),
52 | ResolvedNCommand::Output { .. } => Ok(()),
53 | ResolvedNCommand::Push(..) => Ok(()),
54 | ResolvedNCommand::Pop(..) => Ok(()),
55 | }
56 | }
57 |
58 | fn check_shadowing_query(&mut self, query: &[ResolvedFact]) -> Result<(), Error> {
59 | // we want to allow names in queries to shadow each other, so we first collect
60 | // all of the variable names, and then we check each of those names once
61 | fn get_expr_names(expr: &ResolvedExpr, inner: &mut Names) {
62 | match expr {
63 | ResolvedExpr::Lit(..) => {}
64 | ResolvedExpr::Var(span, name) => {
65 | if !inner.0.contains_key(&name.name) {
66 | inner.0.insert(name.name, span.clone());
67 | }
68 | }
69 | ResolvedExpr::Call(_span, _func, args) => {
70 | args.iter().for_each(|e| get_expr_names(e, inner))
71 | }
72 | };
73 | }
74 |
75 | let mut inner = Names::default();
76 |
77 | for fact in query {
78 | match fact {
79 | ResolvedFact::Eq(_span, e1, e2) => {
80 | get_expr_names(e1, &mut inner);
81 | get_expr_names(e2, &mut inner);
82 | }
83 | ResolvedFact::Fact(e) => get_expr_names(e, &mut inner),
84 | }
85 | }
86 |
87 | for (name, span) in inner.0 {
88 | self.check(name, span.clone())?;
89 | }
90 |
91 | Ok(())
92 | }
93 |
94 | fn check_shadowing_action(&mut self, action: &ResolvedAction) -> Result<(), Error> {
95 | if let ResolvedAction::Let(span, name, _args) = action {
96 | self.check(name.name, span.clone())
97 | } else {
98 | Ok(())
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/function/binary_search.rs:
--------------------------------------------------------------------------------
1 | use std::cmp::Ordering;
2 |
3 | use super::table::Table;
4 |
5 | /// Binary search a [`Table`] for the smallest index with a timestamp greater
6 | /// than or equal to `target`.
7 | pub(crate) fn binary_search_table_by_key(data: &Table, target: u32) -> Option {
8 | if data.is_empty() {
9 | return None;
10 | }
11 | if data.max_ts() < target {
12 | return None;
13 | }
14 | if data.min_ts().unwrap() > target {
15 | return Some(0);
16 | }
17 | // adapted from std::slice::binary_search_by
18 | let mut size = data.num_offsets();
19 | let mut left = 0;
20 | let mut right = size;
21 | while left < right {
22 | let mut mid = left + size / 2;
23 | let cmp = data.get_timestamp(mid).unwrap().cmp(&target);
24 |
25 | // The std implementation claims that if/else generates better code than match.
26 | if cmp == Ordering::Less {
27 | left = mid + 1;
28 | } else if cmp == Ordering::Greater {
29 | right = mid;
30 | } else {
31 | // We need to march back to the start of the matching elements. We
32 | // could have jumped into the middle of a run.
33 | //
34 | // TODO: this makes the algorithm O(n); we can use a variant of
35 | // gallop to get it back to log(n) if needed. See
36 | // https://github.com/frankmcsherry/blog/blob/master/posts/2018-05-19.md
37 | while mid > 0 {
38 | let next_mid = mid - 1;
39 | if data.get_timestamp(next_mid).unwrap() != target {
40 | break;
41 | }
42 | mid = next_mid;
43 | }
44 | return Some(mid);
45 | }
46 | size = right - left;
47 | }
48 | Some(left)
49 | }
50 |
51 | #[cfg(test)]
52 | mod tests {
53 | use super::*;
54 | use crate::Value;
55 |
56 | fn make_value(bits: u32) -> Value {
57 | Value {
58 | #[cfg(debug_assertions)]
59 | tag: "testing".into(),
60 | bits: bits as u64,
61 | }
62 | }
63 |
64 | fn insert_to_map(table: &mut Table, i: u32, ts: u32) {
65 | let v = make_value(i);
66 | table.insert(&[v], v, ts);
67 | }
68 |
69 | #[test]
70 | fn binary_search() {
71 | let mut map = Table::default();
72 | assert_eq!(binary_search_table_by_key(&map, 0), None);
73 | insert_to_map(&mut map, 1, 1);
74 | assert_eq!(binary_search_table_by_key(&map, 0), Some(0));
75 | map.clear();
76 | for i in 0..128 {
77 | // have a run of 4 24s and then skip to 26
78 | let v = if i == 50 || i == 51 { 24 } else { i / 2 };
79 | insert_to_map(&mut map, i, v);
80 | }
81 |
82 | assert_eq!(binary_search_table_by_key(&map, 3), Some(6));
83 | assert_eq!(binary_search_table_by_key(&map, 0), Some(0));
84 | assert_eq!(binary_search_table_by_key(&map, 63), Some(126));
85 | assert_eq!(binary_search_table_by_key(&map, 200), None);
86 | assert_eq!(binary_search_table_by_key(&map, 24), Some(48));
87 | assert_eq!(binary_search_table_by_key(&map, 25), Some(52));
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/function/index.rs:
--------------------------------------------------------------------------------
1 | //! Column-level indexes on values from a common sort.
2 | use smallvec::SmallVec;
3 |
4 | use crate::{unionfind::UnionFind, util::HashMap, Symbol, Value};
5 |
6 | pub(crate) type Offset = u32;
7 |
8 | #[derive(Clone, Debug)]
9 | pub(crate) struct ColumnIndex {
10 | sort: Symbol,
11 | ids: HashMap>,
12 | }
13 |
14 | impl ColumnIndex {
15 | pub(crate) fn new(sort: Symbol) -> ColumnIndex {
16 | ColumnIndex {
17 | sort,
18 | ids: Default::default(),
19 | }
20 | }
21 |
22 | pub(crate) fn sort(&self) -> Symbol {
23 | self.sort
24 | }
25 |
26 | pub(crate) fn add(&mut self, v: Value, i: usize) {
27 | #[cfg(debug_assertions)]
28 | assert_eq!(v.tag, self.sort);
29 |
30 | self.ids.entry(v.bits).or_default().push(i as Offset);
31 | }
32 |
33 | pub(crate) fn clear(&mut self) {
34 | self.ids.clear()
35 | }
36 |
37 | pub(crate) fn len(&self) -> usize {
38 | self.ids.len()
39 | }
40 |
41 | pub(crate) fn get(&self, v: &Value) -> Option<&[Offset]> {
42 | self.get_indexes_for_bits(v.bits)
43 | }
44 |
45 | fn get_indexes_for_bits(&self, bits: u64) -> Option<&[Offset]> {
46 | self.ids.get(&bits).map(|x| x.as_slice())
47 | }
48 |
49 | pub(crate) fn iter(&self) -> impl Iterator- + '_ {
50 | self.ids.iter().map(|(bits, v)| {
51 | (
52 | Value {
53 | #[cfg(debug_assertions)]
54 | tag: self.sort,
55 | bits: *bits,
56 | },
57 | v.as_slice(),
58 | )
59 | })
60 | }
61 |
62 | pub(crate) fn to_canonicalize<'a>(
63 | &'a self,
64 | uf: &'a UnionFind,
65 | ) -> impl Iterator
- + 'a {
66 | uf.dirty_ids(self.sort).flat_map(|x| {
67 | self.get_indexes_for_bits(x)
68 | .unwrap_or(&[])
69 | .iter()
70 | .copied()
71 | .map(|x| x as usize)
72 | })
73 | }
74 | }
75 | #[derive(Clone, Debug)]
76 | pub(crate) struct CompositeColumnIndex(SmallVec<[ColumnIndex; 2]>);
77 |
78 | impl CompositeColumnIndex {
79 | pub(crate) fn new() -> CompositeColumnIndex {
80 | CompositeColumnIndex(SmallVec::new())
81 | }
82 |
83 | pub(crate) fn add(&mut self, s: Symbol, v: Value, i: usize) {
84 | if let Some(index) = self.0.iter().position(|index| index.sort() == s) {
85 | (self.0)[index].add(v, i);
86 | } else {
87 | let mut index = ColumnIndex::new(s);
88 | index.add(v, i);
89 | self.0.push(index);
90 | }
91 | }
92 |
93 | pub(crate) fn clear(&mut self) {
94 | for index in self.0.iter_mut() {
95 | index.clear();
96 | }
97 | }
98 |
99 | pub(crate) fn iter(&self) -> impl Iterator
- {
100 | self.0.iter()
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | egglog::cli(egglog::EGraph::default())
3 | }
4 |
--------------------------------------------------------------------------------
/src/sort/bigint.rs:
--------------------------------------------------------------------------------
1 | use num::BigInt;
2 | use std::ops::{Shl, Shr};
3 | use std::sync::Mutex;
4 |
5 | type Z = BigInt;
6 | use crate::{ast::Literal, util::IndexSet};
7 |
8 | use super::*;
9 |
10 | lazy_static! {
11 | static ref BIG_INT_SORT_NAME: Symbol = "BigInt".into();
12 | static ref INTS: Mutex> = Default::default();
13 | }
14 |
15 | #[derive(Debug)]
16 | pub struct BigIntSort;
17 |
18 | impl Sort for BigIntSort {
19 | fn name(&self) -> Symbol {
20 | *BIG_INT_SORT_NAME
21 | }
22 |
23 | fn as_arc_any(self: Arc) -> Arc {
24 | self
25 | }
26 |
27 | #[rustfmt::skip]
28 | fn register_primitives(self: Arc, eg: &mut TypeInfo) {
29 | type Opt = Option;
30 |
31 | add_primitives!(eg, "bigint" = |a: i64| -> Z { a.into() });
32 |
33 | add_primitives!(eg, "+" = |a: Z, b: Z| -> Z { a + b });
34 | add_primitives!(eg, "-" = |a: Z, b: Z| -> Z { a - b });
35 | add_primitives!(eg, "*" = |a: Z, b: Z| -> Z { a * b });
36 | add_primitives!(eg, "/" = |a: Z, b: Z| -> Opt { (b != BigInt::ZERO).then(|| a / b) });
37 | add_primitives!(eg, "%" = |a: Z, b: Z| -> Opt { (b != BigInt::ZERO).then(|| a % b) });
38 |
39 | add_primitives!(eg, "&" = |a: Z, b: Z| -> Z { a & b });
40 | add_primitives!(eg, "|" = |a: Z, b: Z| -> Z { a | b });
41 | add_primitives!(eg, "^" = |a: Z, b: Z| -> Z { a ^ b });
42 | add_primitives!(eg, "<<" = |a: Z, b: i64| -> Z { a.shl(b) });
43 | add_primitives!(eg, ">>" = |a: Z, b: i64| -> Z { a.shr(b) });
44 | add_primitives!(eg, "not-Z" = |a: Z| -> Z { !a });
45 |
46 | add_primitives!(eg, "bits" = |a: Z| -> Z { a.bits().into() });
47 |
48 | add_primitives!(eg, "<" = |a: Z, b: Z| -> Opt { (a < b).then_some(()) });
49 | add_primitives!(eg, ">" = |a: Z, b: Z| -> Opt { (a > b).then_some(()) });
50 | add_primitives!(eg, "<=" = |a: Z, b: Z| -> Opt { (a <= b).then_some(()) });
51 | add_primitives!(eg, ">=" = |a: Z, b: Z| -> Opt { (a >= b).then_some(()) });
52 |
53 | add_primitives!(eg, "bool-=" = |a: Z, b: Z| -> bool { a == b });
54 | add_primitives!(eg, "bool-<" = |a: Z, b: Z| -> bool { a < b });
55 | add_primitives!(eg, "bool->" = |a: Z, b: Z| -> bool { a > b });
56 | add_primitives!(eg, "bool-<=" = |a: Z, b: Z| -> bool { a <= b });
57 | add_primitives!(eg, "bool->=" = |a: Z, b: Z| -> bool { a >= b });
58 |
59 | add_primitives!(eg, "min" = |a: Z, b: Z| -> Z { a.min(b) });
60 | add_primitives!(eg, "max" = |a: Z, b: Z| -> Z { a.max(b) });
61 |
62 | add_primitives!(eg, "to-string" = |a: Z| -> Symbol { a.to_string().into() });
63 | add_primitives!(eg, "from-string" = |a: Symbol| -> Opt { a.as_str().parse::().ok() });
64 | }
65 |
66 | fn extract_term(
67 | &self,
68 | _egraph: &EGraph,
69 | value: Value,
70 | _extractor: &Extractor,
71 | termdag: &mut TermDag,
72 | ) -> Option<(Cost, Term)> {
73 | #[cfg(debug_assertions)]
74 | debug_assert_eq!(value.tag, self.name());
75 |
76 | let bigint = Z::load(self, &value);
77 |
78 | let as_string = termdag.lit(Literal::String(bigint.to_string().into()));
79 | Some((1, termdag.app("from-string".into(), vec![as_string])))
80 | }
81 | }
82 |
83 | impl FromSort for Z {
84 | type Sort = BigIntSort;
85 | fn load(_sort: &Self::Sort, value: &Value) -> Self {
86 | let i = value.bits as usize;
87 | INTS.lock().unwrap().get_index(i).unwrap().clone()
88 | }
89 | }
90 |
91 | impl IntoSort for Z {
92 | type Sort = BigIntSort;
93 | fn store(self, _sort: &Self::Sort) -> Option {
94 | let (i, _) = INTS.lock().unwrap().insert_full(self);
95 | Some(Value {
96 | #[cfg(debug_assertions)]
97 | tag: BigIntSort.name(),
98 | bits: i as u64,
99 | })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/sort/bool.rs:
--------------------------------------------------------------------------------
1 | use crate::ast::Literal;
2 |
3 | use super::*;
4 |
5 | #[derive(Debug)]
6 | pub struct BoolSort;
7 |
8 | lazy_static! {
9 | static ref BOOL_SORT_NAME: Symbol = "bool".into();
10 | }
11 |
12 | impl Sort for BoolSort {
13 | fn name(&self) -> Symbol {
14 | *BOOL_SORT_NAME
15 | }
16 |
17 | fn as_arc_any(self: Arc) -> Arc {
18 | self
19 | }
20 |
21 | #[rustfmt::skip]
22 | fn register_primitives(self: Arc, eg: &mut TypeInfo) {
23 | add_primitives!(eg, "not" = |a: bool| -> bool { !a });
24 | add_primitives!(eg, "and" = |a: bool, b: bool| -> bool { a && b });
25 | add_primitives!(eg, "or" = |a: bool, b: bool| -> bool { a || b });
26 | add_primitives!(eg, "xor" = |a: bool, b: bool| -> bool { a ^ b });
27 | add_primitives!(eg, "=>" = |a: bool, b: bool| -> bool { !a || b });
28 | }
29 |
30 | fn extract_term(
31 | &self,
32 | _egraph: &EGraph,
33 | value: Value,
34 | _extractor: &Extractor,
35 | termdag: &mut TermDag,
36 | ) -> Option<(Cost, Term)> {
37 | #[cfg(debug_assertions)]
38 | debug_assert_eq!(value.tag, self.name());
39 |
40 | Some((1, termdag.lit(Literal::Bool(value.bits > 0))))
41 | }
42 | }
43 |
44 | impl IntoSort for bool {
45 | type Sort = BoolSort;
46 | fn store(self, _sort: &Self::Sort) -> Option {
47 | Some(Value {
48 | #[cfg(debug_assertions)]
49 | tag: BoolSort.name(),
50 | bits: self as u64,
51 | })
52 | }
53 | }
54 |
55 | impl FromSort for bool {
56 | type Sort = BoolSort;
57 | fn load(_sort: &Self::Sort, value: &Value) -> Self {
58 | value.bits != 0
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/sort/f64.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 | use crate::ast::Literal;
3 | use ordered_float::OrderedFloat;
4 |
5 | /// 64-bit floating point numbers supporting these primitives:
6 | /// - Arithmetic: `+`, `-`, `*`, `/`, `%`, `^`, `neg`, `abs`
7 | /// - Comparisons: `<`, `>`, `<=`, `>=`
8 | /// - Other: `min`, `max`, `to-i64`, `to-string`
9 | #[derive(Debug)]
10 | pub struct F64Sort;
11 |
12 | lazy_static! {
13 | static ref F64_SORT_NAME: Symbol = "f64".into();
14 | }
15 |
16 | impl Sort for F64Sort {
17 | fn name(&self) -> Symbol {
18 | *F64_SORT_NAME
19 | }
20 |
21 | fn as_arc_any(self: Arc) -> Arc {
22 | self
23 | }
24 |
25 | #[rustfmt::skip]
26 | // We need the closure for division and mod operations, as they can panic.
27 | // cf https://github.com/rust-lang/rust-clippy/issues/9422
28 | #[allow(clippy::unnecessary_lazy_evaluations)]
29 | fn register_primitives(self: Arc, eg: &mut TypeInfo) {
30 | type Opt = Option;
31 |
32 | add_primitives!(eg, "+" = |a: f64, b: f64| -> f64 { a + b });
33 | add_primitives!(eg, "-" = |a: f64, b: f64| -> f64 { a - b });
34 | add_primitives!(eg, "*" = |a: f64, b: f64| -> f64 { a * b });
35 | add_primitives!(eg, "/" = |a: f64, b: f64| -> Opt { (b != 0.0).then(|| a / b) });
36 | add_primitives!(eg, "%" = |a: f64, b: f64| -> Opt { (b != 0.0).then(|| a % b) });
37 | add_primitives!(eg, "^" = |a: f64, b: f64| -> f64 { a.powf(b) });
38 | add_primitives!(eg, "neg" = |a: f64| -> f64 { -a });
39 |
40 | add_primitives!(eg, "<" = |a: f64, b: f64| -> Opt { (a < b).then(|| ()) });
41 | add_primitives!(eg, ">" = |a: f64, b: f64| -> Opt { (a > b).then(|| ()) });
42 | add_primitives!(eg, "<=" = |a: f64, b: f64| -> Opt { (a <= b).then(|| ()) });
43 | add_primitives!(eg, ">=" = |a: f64, b: f64| -> Opt { (a >= b).then(|| ()) });
44 |
45 | add_primitives!(eg, "min" = |a: f64, b: f64| -> f64 { a.min(b) });
46 | add_primitives!(eg, "max" = |a: f64, b: f64| -> f64 { a.max(b) });
47 | add_primitives!(eg, "abs" = |a: f64| -> f64 { a.abs() });
48 |
49 | // `to-f64` should be in `i64.rs`, but `F64Sort` wouldn't exist yet
50 | add_primitives!(eg, "to-f64" = |a: i64| -> f64 { a as f64 });
51 | add_primitives!(eg, "to-i64" = |a: f64| -> i64 { a as i64 });
52 | // Use debug instead of to_string so that decimal place is always printed
53 | add_primitives!(eg, "to-string" = |a: f64| -> Symbol { format!("{:?}", a).into() });
54 | }
55 |
56 | fn extract_term(
57 | &self,
58 | _egraph: &EGraph,
59 | value: Value,
60 | _extractor: &Extractor,
61 | termdag: &mut TermDag,
62 | ) -> Option<(Cost, Term)> {
63 | #[cfg(debug_assertions)]
64 | debug_assert_eq!(value.tag, self.name());
65 |
66 | Some((
67 | 1,
68 | termdag.lit(Literal::Float(OrderedFloat(f64::from_bits(value.bits)))),
69 | ))
70 | }
71 | }
72 |
73 | impl IntoSort for f64 {
74 | type Sort = F64Sort;
75 | fn store(self, _sort: &Self::Sort) -> Option {
76 | Some(Value {
77 | #[cfg(debug_assertions)]
78 | tag: F64Sort.name(),
79 | bits: self.to_bits(),
80 | })
81 | }
82 | }
83 |
84 | impl FromSort for f64 {
85 | type Sort = F64Sort;
86 | fn load(_sort: &Self::Sort, value: &Value) -> Self {
87 | f64::from_bits(value.bits)
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/sort/i64.rs:
--------------------------------------------------------------------------------
1 | use crate::{ast::Literal, constraint::AllEqualTypeConstraint};
2 |
3 | use super::*;
4 |
5 | /// Signed 64-bit integers supporting these primitives:
6 | /// - Arithmetic: `+`, `-`, `*`, `/`, `%`
7 | /// - Bitwise: `&`, `|`, `^`, `<<`, `>>`, `not-i64`
8 | /// - Fallible comparisons: `<`, `>`, `<=`, `>=`
9 | /// - Boolean comparisons: `bool-=`, `bool-<`, `bool->`, `bool-<=`, `bool->=`
10 | /// - Other: `min`, `max`, `to-f64`, `to-string`, `log2`
11 | ///
12 | /// Note: fallible comparisons are used at the top-level of a query.
13 | /// For example, this rule will only match if `a` is less than `b`.
14 | /// ```text
15 | /// (rule (... (< a b)) (...))
16 | /// ```
17 | /// On the other hand, boolean comparisons will always match, and so
18 | /// make sense to use inside expressions.
19 | #[derive(Debug)]
20 | pub struct I64Sort;
21 |
22 | lazy_static! {
23 | static ref I64_SORT_NAME: Symbol = "i64".into();
24 | }
25 |
26 | impl Sort for I64Sort {
27 | fn name(&self) -> Symbol {
28 | *I64_SORT_NAME
29 | }
30 |
31 | fn as_arc_any(self: Arc) -> Arc {
32 | self
33 | }
34 |
35 | #[rustfmt::skip]
36 | fn register_primitives(self: Arc, typeinfo: &mut TypeInfo) {
37 | typeinfo.add_primitive(TermOrderingMin {
38 | });
39 | typeinfo.add_primitive(TermOrderingMax {
40 | });
41 |
42 | type Opt = Option;
43 |
44 | add_primitives!(typeinfo, "+" = |a: i64, b: i64| -> Opt { a.checked_add(b) });
45 | add_primitives!(typeinfo, "-" = |a: i64, b: i64| -> Opt { a.checked_sub(b) });
46 | add_primitives!(typeinfo, "*" = |a: i64, b: i64| -> Opt { a.checked_mul(b) });
47 | add_primitives!(typeinfo, "/" = |a: i64, b: i64| -> Opt { a.checked_div(b) });
48 | add_primitives!(typeinfo, "%" = |a: i64, b: i64| -> Opt { a.checked_rem(b) });
49 |
50 | add_primitives!(typeinfo, "&" = |a: i64, b: i64| -> i64 { a & b });
51 | add_primitives!(typeinfo, "|" = |a: i64, b: i64| -> i64 { a | b });
52 | add_primitives!(typeinfo, "^" = |a: i64, b: i64| -> i64 { a ^ b });
53 | add_primitives!(typeinfo, "<<" = |a: i64, b: i64| -> Opt { b.try_into().ok().and_then(|b| a.checked_shl(b)) });
54 | add_primitives!(typeinfo, ">>" = |a: i64, b: i64| -> Opt { b.try_into().ok().and_then(|b| a.checked_shr(b)) });
55 | add_primitives!(typeinfo, "not-i64" = |a: i64| -> i64 { !a });
56 |
57 | add_primitives!(typeinfo, "log2" = |a: i64| -> i64 { (a as i64).ilog2() as i64 });
58 |
59 | add_primitives!(typeinfo, "<" = |a: i64, b: i64| -> Opt { (a < b).then_some(()) });
60 | add_primitives!(typeinfo, ">" = |a: i64, b: i64| -> Opt { (a > b).then_some(()) });
61 | add_primitives!(typeinfo, "<=" = |a: i64, b: i64| -> Opt { (a <= b).then_some(()) });
62 | add_primitives!(typeinfo, ">=" = |a: i64, b: i64| -> Opt { (a >= b).then_some(()) });
63 |
64 | add_primitives!(typeinfo, "bool-=" = |a: i64, b: i64| -> bool { a == b });
65 | add_primitives!(typeinfo, "bool-<" = |a: i64, b: i64| -> bool { a < b });
66 | add_primitives!(typeinfo, "bool->" = |a: i64, b: i64| -> bool { a > b });
67 | add_primitives!(typeinfo, "bool-<=" = |a: i64, b: i64| -> bool { a <= b });
68 | add_primitives!(typeinfo, "bool->=" = |a: i64, b: i64| -> bool { a >= b });
69 |
70 | add_primitives!(typeinfo, "min" = |a: i64, b: i64| -> i64 { a.min(b) });
71 | add_primitives!(typeinfo, "max" = |a: i64, b: i64| -> i64 { a.max(b) });
72 |
73 | add_primitives!(typeinfo, "to-string" = |a: i64| -> Symbol { a.to_string().into() });
74 |
75 | // Must be in the i64 sort register function because the string sort is registered before the i64 sort.
76 | typeinfo.add_primitive(CountMatches {
77 | name: "count-matches".into(),
78 | string: typeinfo.get_sort_nofail(),
79 | int: self.clone(),
80 | });
81 |
82 | }
83 |
84 | fn extract_term(
85 | &self,
86 | _egraph: &EGraph,
87 | value: Value,
88 | _extractor: &Extractor,
89 | termdag: &mut TermDag,
90 | ) -> Option<(Cost, Term)> {
91 | Some((1, termdag.lit(Literal::Int(value.bits as _))))
92 | }
93 | }
94 |
95 | impl IntoSort for i64 {
96 | type Sort = I64Sort;
97 | fn store(self, _sort: &Self::Sort) -> Option {
98 | Some(Value {
99 | #[cfg(debug_assertions)]
100 | tag: I64Sort.name(),
101 | bits: self as u64,
102 | })
103 | }
104 | }
105 |
106 | impl FromSort for i64 {
107 | type Sort = I64Sort;
108 | fn load(_sort: &Self::Sort, value: &Value) -> Self {
109 | value.bits as Self
110 | }
111 | }
112 |
113 | struct CountMatches {
114 | name: Symbol,
115 | string: Arc,
116 | int: Arc,
117 | }
118 |
119 | impl PrimitiveLike for CountMatches {
120 | fn name(&self) -> Symbol {
121 | self.name
122 | }
123 |
124 | fn get_type_constraints(&self, span: &Span) -> Box {
125 | AllEqualTypeConstraint::new(self.name(), span.clone())
126 | .with_all_arguments_sort(self.string.clone())
127 | .with_exact_length(3)
128 | .with_output_sort(self.int.clone())
129 | .into_box()
130 | }
131 |
132 | fn apply(
133 | &self,
134 | values: &[Value],
135 | _sorts: (&[ArcSort], &ArcSort),
136 | _egraph: Option<&mut EGraph>,
137 | ) -> Option {
138 | let string1 = Symbol::load(&self.string, &values[0]).to_string();
139 | let string2 = Symbol::load(&self.string, &values[1]).to_string();
140 | Some(Value::from(string1.matches(&string2).count() as i64))
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/sort/macros.rs:
--------------------------------------------------------------------------------
1 | #[macro_export]
2 | macro_rules! add_primitives {
3 | ($type_info:expr,
4 | $name:literal = |$($param:ident : $param_t:ty),*| -> $ret:ty { $body:expr }
5 | ) => {{
6 | let type_info: &mut _ = $type_info;
7 | #[allow(unused_imports, non_snake_case)]
8 | {
9 | use $crate::{*, ast::*, sort::*, constraint::*};
10 | use ::std::sync::Arc;
11 |
12 | struct MyPrim {$(
13 | $param: Arc<<$param_t as FromSort>::Sort>,
14 | )*
15 | __out: Arc<<$ret as IntoSort>::Sort>,
16 | }
17 |
18 | impl PrimitiveLike for MyPrim {
19 | fn name(&self) -> Symbol {
20 | $name.into()
21 | }
22 |
23 | fn get_type_constraints(
24 | &self,
25 | span: &Span
26 | ) -> Box {
27 | let sorts = vec![$(self.$param.clone() as ArcSort,)* self.__out.clone() as ArcSort];
28 | SimpleTypeConstraint::new(self.name(), sorts, span.clone()).into_box()
29 | }
30 |
31 | fn apply(
32 | &self,
33 | values: &[Value],
34 | _sorts: (&[ArcSort], &ArcSort),
35 | _egraph: Option<&mut EGraph>,
36 | ) -> Option {
37 | if let [$($param),*] = values {
38 | $(let $param: $param_t = <$param_t as FromSort>::load(&self.$param, $param);)*
39 | // print!("{}( ", $name);
40 | // $( print!("{}={:?}, ", stringify!($param), $param); )*
41 | let result: $ret = $body;
42 | // println!(") = {result:?}");
43 | result.store(&self.__out)
44 | } else {
45 | panic!("wrong number of arguments")
46 | }
47 | }
48 | }
49 | type_info.add_primitive( Primitive::from(MyPrim {
50 | $( $param: type_info.get_sort_nofail::<<$param_t as IntoSort>::Sort>(), )*
51 | __out: type_info.get_sort_nofail::<<$ret as IntoSort>::Sort>(),
52 | }))
53 | }
54 | }};
55 | }
56 |
--------------------------------------------------------------------------------
/src/sort/mod.rs:
--------------------------------------------------------------------------------
1 | #[macro_use]
2 | mod macros;
3 | use lazy_static::lazy_static;
4 | use std::fmt::Debug;
5 | use std::{any::Any, sync::Arc};
6 |
7 | mod bigint;
8 | pub use bigint::*;
9 | mod bigrat;
10 | pub use bigrat::*;
11 | mod bool;
12 | pub use self::bool::*;
13 | mod string;
14 | pub use string::*;
15 | mod unit;
16 | pub use unit::*;
17 | mod i64;
18 | pub use self::i64::*;
19 | mod f64;
20 | pub use self::f64::*;
21 | mod map;
22 | pub use map::*;
23 | mod set;
24 | pub use set::*;
25 | mod vec;
26 | pub use vec::*;
27 | mod r#fn;
28 | pub use r#fn::*;
29 | mod multiset;
30 | pub use multiset::*;
31 |
32 | use crate::constraint::AllEqualTypeConstraint;
33 | use crate::extract::{Cost, Extractor};
34 | use crate::*;
35 |
36 | pub trait Sort: Any + Send + Sync + Debug {
37 | fn name(&self) -> Symbol;
38 |
39 | fn as_arc_any(self: Arc) -> Arc;
40 |
41 | fn is_eq_sort(&self) -> bool {
42 | false
43 | }
44 |
45 | // return true if it is a container sort.
46 | fn is_container_sort(&self) -> bool {
47 | false
48 | }
49 |
50 | // return true if it is a container sort that contains ids.
51 | // only eq_sort and eq_container_sort need to be canonicalized.
52 | fn is_eq_container_sort(&self) -> bool {
53 | false
54 | }
55 |
56 | // Only eq_container_sort need to implement this method,
57 | // which returns a list of ids to be tracked.
58 | fn foreach_tracked_values<'a>(
59 | &'a self,
60 | value: &'a Value,
61 | mut f: Box,
62 | ) {
63 | for (sort, value) in self.inner_values(value) {
64 | if sort.is_eq_sort() {
65 | f(sort, value)
66 | }
67 | }
68 | }
69 |
70 | // Sort-wise canonicalization. Return true if value is modified.
71 | // Only EqSort or containers of EqSort should override.
72 | fn canonicalize(&self, value: &mut Value, unionfind: &UnionFind) -> bool {
73 | #[cfg(debug_assertions)]
74 | debug_assert_eq!(self.name(), value.tag);
75 |
76 | #[cfg(not(debug_assertions))]
77 | let _ = value;
78 | let _ = unionfind;
79 | false
80 | }
81 |
82 | /// Return the serialized name of the sort
83 | ///
84 | /// Only used for container sorts, which cannot be serialized with make_expr so need an explicit name
85 | fn serialized_name(&self, _value: &Value) -> Symbol {
86 | self.name()
87 | }
88 |
89 | /// Return the inner values and sorts.
90 | /// Only eq_container_sort need to implement this method,
91 | fn inner_values(&self, value: &Value) -> Vec<(ArcSort, Value)> {
92 | let _ = value;
93 | vec![]
94 | }
95 |
96 | fn register_primitives(self: Arc, info: &mut TypeInfo) {
97 | let _ = info;
98 | }
99 |
100 | /// Extracting a term (with smallest cost) out of a primitive value
101 | fn extract_term(
102 | &self,
103 | egraph: &EGraph,
104 | value: Value,
105 | _extractor: &Extractor,
106 | _termdag: &mut TermDag,
107 | ) -> Option<(Cost, Term)>;
108 | }
109 |
110 | // Note: this trait is currently intended to be implemented on the
111 | // same struct as `Sort`. If in the future we have dynamic presorts
112 | // (for example, we want to add partial application) we should revisit
113 | // this and make the methods take a `self` parameter.
114 | pub trait Presort {
115 | fn presort_name() -> Symbol;
116 | fn reserved_primitives() -> Vec;
117 | fn make_sort(
118 | typeinfo: &mut TypeInfo,
119 | name: Symbol,
120 | args: &[Expr],
121 | ) -> Result;
122 | }
123 |
124 | #[derive(Debug)]
125 | pub struct EqSort {
126 | pub name: Symbol,
127 | }
128 |
129 | impl Sort for EqSort {
130 | fn name(&self) -> Symbol {
131 | self.name
132 | }
133 |
134 | fn as_arc_any(self: Arc) -> Arc {
135 | self
136 | }
137 |
138 | fn is_eq_sort(&self) -> bool {
139 | true
140 | }
141 |
142 | fn canonicalize(&self, value: &mut Value, unionfind: &UnionFind) -> bool {
143 | #[cfg(debug_assertions)]
144 | debug_assert_eq!(self.name(), value.tag);
145 |
146 | let bits = unionfind.find(value.bits);
147 | if bits != value.bits {
148 | value.bits = bits;
149 | true
150 | } else {
151 | false
152 | }
153 | }
154 |
155 | fn extract_term(
156 | &self,
157 | _egraph: &EGraph,
158 | _value: Value,
159 | _extractor: &Extractor,
160 | _termdag: &mut TermDag,
161 | ) -> Option<(Cost, Term)> {
162 | unimplemented!("No extract_term for EqSort {}", self.name)
163 | }
164 | }
165 |
166 | pub trait FromSort: Sized {
167 | type Sort: Sort;
168 | fn load(sort: &Self::Sort, value: &Value) -> Self;
169 | }
170 |
171 | pub trait IntoSort: Sized {
172 | type Sort: Sort;
173 | fn store(self, sort: &Self::Sort) -> Option;
174 | }
175 |
176 | impl IntoSort for Option {
177 | type Sort = T::Sort;
178 |
179 | fn store(self, sort: &Self::Sort) -> Option {
180 | self?.store(sort)
181 | }
182 | }
183 |
184 | pub type PreSort =
185 | fn(typeinfo: &mut TypeInfo, name: Symbol, params: &[Expr]) -> Result;
186 |
187 | pub(crate) struct ValueEq;
188 |
189 | impl PrimitiveLike for ValueEq {
190 | fn name(&self) -> Symbol {
191 | "value-eq".into()
192 | }
193 |
194 | fn get_type_constraints(&self, span: &Span) -> Box {
195 | AllEqualTypeConstraint::new(self.name(), span.clone())
196 | .with_exact_length(3)
197 | .with_output_sort(Arc::new(UnitSort))
198 | .into_box()
199 | }
200 |
201 | fn apply(
202 | &self,
203 | values: &[Value],
204 | _sorts: (&[ArcSort], &ArcSort),
205 | _egraph: Option<&mut EGraph>,
206 | ) -> Option {
207 | assert_eq!(values.len(), 2);
208 | if values[0] == values[1] {
209 | Some(Value::unit())
210 | } else {
211 | None
212 | }
213 | }
214 | }
215 |
216 | pub fn literal_sort(lit: &Literal) -> ArcSort {
217 | match lit {
218 | Literal::Int(_) => Arc::new(I64Sort) as ArcSort,
219 | Literal::Float(_) => Arc::new(F64Sort) as ArcSort,
220 | Literal::String(_) => Arc::new(StringSort) as ArcSort,
221 | Literal::Bool(_) => Arc::new(BoolSort) as ArcSort,
222 | Literal::Unit => Arc::new(UnitSort) as ArcSort,
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/src/sort/string.rs:
--------------------------------------------------------------------------------
1 | use std::num::NonZeroU32;
2 |
3 | use crate::{ast::Literal, constraint::AllEqualTypeConstraint};
4 |
5 | use super::*;
6 |
7 | #[derive(Debug)]
8 | pub struct StringSort;
9 |
10 | lazy_static! {
11 | static ref STRING_SORT_NAME: Symbol = "String".into();
12 | }
13 |
14 | impl Sort for StringSort {
15 | fn name(&self) -> Symbol {
16 | *STRING_SORT_NAME
17 | }
18 |
19 | fn as_arc_any(self: Arc) -> Arc {
20 | self
21 | }
22 |
23 | fn extract_term(
24 | &self,
25 | _egraph: &EGraph,
26 | value: Value,
27 | _extractor: &Extractor,
28 | termdag: &mut TermDag,
29 | ) -> Option<(Cost, Term)> {
30 | #[cfg(debug_assertions)]
31 | debug_assert_eq!(value.tag, self.name());
32 |
33 | let sym = Symbol::from(NonZeroU32::new(value.bits as _).unwrap());
34 | Some((1, termdag.lit(Literal::String(sym))))
35 | }
36 |
37 | fn register_primitives(self: Arc, typeinfo: &mut TypeInfo) {
38 | typeinfo.add_primitive(Add {
39 | name: "+".into(),
40 | string: self.clone(),
41 | });
42 | typeinfo.add_primitive(Replace {
43 | name: "replace".into(),
44 | string: self,
45 | });
46 | }
47 | }
48 |
49 | // TODO could use a local symbol table
50 |
51 | impl IntoSort for Symbol {
52 | type Sort = StringSort;
53 | fn store(self, _sort: &Self::Sort) -> Option {
54 | Some(Value {
55 | #[cfg(debug_assertions)]
56 | tag: StringSort.name(),
57 | bits: NonZeroU32::from(self).get() as _,
58 | })
59 | }
60 | }
61 |
62 | impl FromSort for Symbol {
63 | type Sort = StringSort;
64 | fn load(_sort: &Self::Sort, value: &Value) -> Self {
65 | NonZeroU32::new(value.bits as u32).unwrap().into()
66 | }
67 | }
68 |
69 | struct Add {
70 | name: Symbol,
71 | string: Arc,
72 | }
73 |
74 | impl PrimitiveLike for Add {
75 | fn name(&self) -> Symbol {
76 | self.name
77 | }
78 |
79 | fn get_type_constraints(&self, span: &Span) -> Box {
80 | AllEqualTypeConstraint::new(self.name(), span.clone())
81 | .with_all_arguments_sort(self.string.clone())
82 | .into_box()
83 | }
84 |
85 | fn apply(
86 | &self,
87 | values: &[Value],
88 | _sorts: (&[ArcSort], &ArcSort),
89 | _egraph: Option<&mut EGraph>,
90 | ) -> Option {
91 | let mut res_string: String = "".to_owned();
92 | for value in values {
93 | let sym = Symbol::load(&self.string, value);
94 | res_string.push_str(sym.as_str());
95 | }
96 | let res_symbol: Symbol = res_string.into();
97 | Some(Value::from(res_symbol))
98 | }
99 | }
100 |
101 | struct Replace {
102 | name: Symbol,
103 | string: Arc,
104 | }
105 |
106 | impl PrimitiveLike for Replace {
107 | fn name(&self) -> Symbol {
108 | self.name
109 | }
110 |
111 | fn get_type_constraints(&self, span: &Span) -> Box {
112 | AllEqualTypeConstraint::new(self.name(), span.clone())
113 | .with_all_arguments_sort(self.string.clone())
114 | .with_exact_length(4)
115 | .into_box()
116 | }
117 |
118 | fn apply(
119 | &self,
120 | values: &[Value],
121 | _sorts: (&[ArcSort], &ArcSort),
122 | _egraph: Option<&mut EGraph>,
123 | ) -> Option {
124 | let string1 = Symbol::load(&self.string, &values[0]).to_string();
125 | let string2 = Symbol::load(&self.string, &values[1]).to_string();
126 | let string3 = Symbol::load(&self.string, &values[2]).to_string();
127 | let res: Symbol = string1.replace(&string2, &string3).into();
128 | Some(Value::from(res))
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/sort/unit.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 | use crate::{ast::Literal, constraint::AllEqualTypeConstraint, ArcSort, PrimitiveLike};
3 |
4 | #[derive(Debug)]
5 | pub struct UnitSort;
6 |
7 | lazy_static! {
8 | static ref UNIT_SORT_NAME: Symbol = "Unit".into();
9 | }
10 |
11 | impl Sort for UnitSort {
12 | fn name(&self) -> Symbol {
13 | *UNIT_SORT_NAME
14 | }
15 |
16 | fn as_arc_any(self: Arc) -> Arc {
17 | self
18 | }
19 |
20 | fn register_primitives(self: Arc, type_info: &mut TypeInfo) {
21 | type_info.add_primitive(NotEqualPrimitive { unit: self })
22 | }
23 |
24 | fn extract_term(
25 | &self,
26 | _egraph: &EGraph,
27 | _value: Value,
28 | _extractor: &Extractor,
29 | termdag: &mut TermDag,
30 | ) -> Option<(Cost, Term)> {
31 | Some((1, termdag.lit(Literal::Unit)))
32 | }
33 | }
34 |
35 | impl IntoSort for () {
36 | type Sort = UnitSort;
37 |
38 | fn store(self, _sort: &Self::Sort) -> Option {
39 | Some(Value::unit())
40 | }
41 | }
42 |
43 | pub struct NotEqualPrimitive {
44 | unit: ArcSort,
45 | }
46 |
47 | impl PrimitiveLike for NotEqualPrimitive {
48 | fn name(&self) -> Symbol {
49 | "!=".into()
50 | }
51 |
52 | fn get_type_constraints(&self, span: &Span) -> Box {
53 | AllEqualTypeConstraint::new(self.name(), span.clone())
54 | .with_exact_length(3)
55 | .with_output_sort(self.unit.clone())
56 | .into_box()
57 | }
58 |
59 | fn apply(
60 | &self,
61 | values: &[Value],
62 | _sorts: (&[ArcSort], &ArcSort),
63 | _egraph: Option<&mut EGraph>,
64 | ) -> Option {
65 | (values[0] != values[1]).then(Value::unit)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/unionfind.rs:
--------------------------------------------------------------------------------
1 | //! Baseline union-find implementation without sizes or ranks, using path
2 | //! halving for compression.
3 | //!
4 | //! This implementation uses interior mutability for `find`.
5 | use crate::util::HashMap;
6 | use crate::{Symbol, Value};
7 |
8 | use std::cell::Cell;
9 | use std::fmt::Debug;
10 | use std::mem;
11 |
12 | pub type Id = u64;
13 |
14 | #[derive(Debug, Clone, Default)]
15 | pub struct UnionFind {
16 | parents: Vec
>,
17 | n_unions: usize,
18 | recent_ids: HashMap>,
19 | staged_ids: HashMap>,
20 | }
21 |
22 | impl UnionFind {
23 | /// The number of unions that have been performed over the lifetime of this
24 | /// data-structure.
25 | pub(crate) fn n_unions(&self) -> usize {
26 | self.n_unions
27 | }
28 |
29 | /// Create a fresh [`Id`].
30 | pub(crate) fn make_set(&mut self) -> Id {
31 | let res = self.parents.len() as u64;
32 | self.parents.push(Cell::new(res));
33 | res
34 | }
35 |
36 | /// The number of ids that recently stopped being canonical.
37 | pub(crate) fn new_ids(&self, sort_filter: impl Fn(Symbol) -> bool) -> usize {
38 | self.recent_ids
39 | .iter()
40 | .filter_map(|(sort, ids)| {
41 | if sort_filter(*sort) {
42 | Some(ids.len())
43 | } else {
44 | None
45 | }
46 | })
47 | .sum()
48 | }
49 |
50 | /// Clear any ids currently marked as dirty and then move any ids marked
51 | /// non-canonical since the last call to this method (or the
52 | /// data-structure's creation) into the dirty set.
53 | pub(crate) fn clear_recent_ids(&mut self) {
54 | mem::swap(&mut self.recent_ids, &mut self.staged_ids);
55 | self.staged_ids.values_mut().for_each(Vec::clear);
56 | }
57 |
58 | /// Iterate over the ids of the given sort marked as "dirty", i.e. any
59 | /// [`Id`]s that ceased to be canonical between the last call to
60 | /// [`clear_recent_ids`] and the call prior to that.
61 | ///
62 | /// [`clear_recent_ids`]: UnionFind::clear_recent_ids
63 | pub(crate) fn dirty_ids(&self, sort: Symbol) -> impl Iterator- + '_ {
64 | let ids = self
65 | .recent_ids
66 | .get(&sort)
67 | .map(|ids| ids.as_slice())
68 | .unwrap_or(&[]);
69 | ids.iter().copied()
70 | }
71 |
72 | /// Look up the canonical representative for the given [`Id`].
73 | pub fn find(&self, id: Id) -> Id {
74 | let mut cur = self.parent(id);
75 | loop {
76 | let next = self.parent(cur.get());
77 | if cur.get() == next.get() {
78 | return cur.get();
79 | }
80 | // Path halving
81 | let grand = self.parent(next.get());
82 | cur.set(grand.get());
83 | cur = grand;
84 | }
85 | }
86 |
87 | /// Merge the equivalence classes associated with the two values.
88 | ///
89 | /// This method assumes that the given values belong to the same, "eq-able",
90 | /// sort. Its behavior is unspecified on other values.
91 | pub(crate) fn union_values(&mut self, val1: Value, val2: Value, sort: Symbol) -> Value {
92 | #[cfg(debug_assertions)]
93 | debug_assert_eq!(val1.tag, val2.tag);
94 |
95 | Value {
96 | #[cfg(debug_assertions)]
97 | tag: val1.tag,
98 | bits: self.union(val1.bits, val2.bits, sort),
99 | }
100 | }
101 |
102 | /// Like [`union_values`], but operating on raw [`Id`]s.
103 | ///
104 | /// [`union_values`]: UnionFind::union_values
105 | pub(crate) fn union(&mut self, id1: Id, id2: Id, sort: Symbol) -> Id {
106 | let (res, reparented) = self.do_union(id1, id2);
107 | if let Some(id) = reparented {
108 | self.staged_ids.entry(sort).or_default().push(id)
109 | }
110 | res
111 | }
112 |
113 | fn do_union(&mut self, id1: Id, id2: Id) -> (Id, Option) {
114 | let id1 = self.find(id1);
115 | let id2 = self.find(id2);
116 | if id1 != id2 {
117 | self.parent(id2).set(id1);
118 | self.n_unions += 1;
119 | (id1, Some(id2))
120 | } else {
121 | (id1, None)
122 | }
123 | }
124 |
125 | fn parent(&self, id: Id) -> &Cell {
126 | &self.parents[id as usize]
127 | }
128 | }
129 |
130 | #[cfg(test)]
131 | mod tests {
132 | use super::*;
133 |
134 | fn ids(us: impl IntoIterator
- ) -> Vec
> {
135 | us.into_iter().map(Cell::new).collect()
136 | }
137 |
138 | #[test]
139 | fn union_find() {
140 | let n = 10;
141 |
142 | let mut uf = UnionFind::default();
143 | for _ in 0..n {
144 | uf.make_set();
145 | }
146 |
147 | // test the initial condition of everyone in their own set
148 | assert_eq!(uf.parents, ids(0..n));
149 |
150 | // build up one set
151 | uf.union(0, 1, "T".into());
152 | uf.union(0, 2, "T".into());
153 | uf.union(0, 3, "T".into());
154 |
155 | // build up another set
156 | uf.union(6, 7, "T".into());
157 | uf.union(6, 8, "T".into());
158 | uf.union(6, 9, "T".into());
159 |
160 | // this should compress all paths
161 | for i in 0..n {
162 | uf.find(i);
163 | }
164 |
165 | // indexes: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
166 | let expected = vec![0, 0, 0, 0, 4, 5, 6, 6, 6, 6];
167 | assert_eq!(uf.parents, ids(expected));
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/util.rs:
--------------------------------------------------------------------------------
1 | #![allow(unused)]
2 |
3 | use std::fmt::Display;
4 |
5 | use crate::core::SpecializedPrimitive;
6 | #[allow(unused_imports)]
7 | use crate::*;
8 |
9 | pub(crate) type BuildHasher = std::hash::BuildHasherDefault;
10 |
11 | /// Use an index map by default everywhere.
12 | /// We could fix the seed, but symbol generation is not determinisic so
13 | /// this doesn't fix the problem.
14 | #[cfg(not(feature = "nondeterministic"))]
15 | pub(crate) type HashMap = indexmap::IndexMap;
16 | #[cfg(feature = "nondeterministic")]
17 | pub(crate) type HashMap = hashbrown::HashMap;
18 |
19 | #[cfg(not(feature = "nondeterministic"))]
20 | pub(crate) type HashSet = indexmap::IndexSet;
21 | #[cfg(feature = "nondeterministic")]
22 | pub(crate) type HashSet = hashbrown::HashSet;
23 |
24 | #[cfg(feature = "nondeterministic")]
25 | pub(crate) type HEntry<'a, A, B, D> = hashbrown::hash_map::Entry<'a, A, B, D>;
26 | #[cfg(not(feature = "nondeterministic"))]
27 | pub(crate) type HEntry<'a, A, B> = Entry<'a, A, B>;
28 |
29 | pub type IndexMap = indexmap::IndexMap;
30 | pub type IndexSet = indexmap::IndexSet;
31 |
32 | pub(crate) fn concat_vecs(to: &mut Vec, mut from: Vec) {
33 | if to.len() < from.len() {
34 | std::mem::swap(to, &mut from)
35 | }
36 | to.extend(from);
37 | }
38 |
39 | pub(crate) struct ListDisplay<'a, TS>(pub TS, pub &'a str);
40 |
41 | impl<'a, TS> Display for ListDisplay<'a, TS>
42 | where
43 | TS: Clone + IntoIterator,
44 | TS::Item: Display,
45 | {
46 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 | let mut did_something = false;
48 | for item in self.0.clone().into_iter() {
49 | if did_something {
50 | f.write_str(self.1)?;
51 | }
52 | Display::fmt(&item, f)?;
53 | did_something = true;
54 | }
55 | Ok(())
56 | }
57 | }
58 |
59 | pub(crate) struct ListDebug<'a, TS>(pub TS, pub &'a str);
60 |
61 | impl<'a, TS> Debug for ListDebug<'a, TS>
62 | where
63 | TS: Clone + IntoIterator,
64 | TS::Item: Debug,
65 | {
66 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 | let mut did_something = false;
68 | for item in self.0.clone().into_iter() {
69 | if did_something {
70 | f.write_str(self.1)?;
71 | }
72 | Debug::fmt(&item, f)?;
73 | did_something = true;
74 | }
75 | Ok(())
76 | }
77 | }
78 |
79 | /// Generates fresh symbols for internal use during typechecking and flattening.
80 | /// These are guaranteed not to collide with the
81 | /// user's symbols because they use $.
82 | #[derive(Debug, Clone, PartialEq, Eq)]
83 | pub struct SymbolGen {
84 | gen: usize,
85 | reserved_string: String,
86 | }
87 |
88 | impl SymbolGen {
89 | pub fn new(reserved_string: String) -> Self {
90 | Self {
91 | gen: 0,
92 | reserved_string,
93 | }
94 | }
95 |
96 | pub fn has_been_used(&self) -> bool {
97 | self.gen > 0
98 | }
99 | }
100 |
101 | /// This trait lets us statically dispatch between `fresh` methods for generic structs.
102 | pub trait FreshGen {
103 | fn fresh(&mut self, name_hint: &Head) -> Leaf;
104 | }
105 |
106 | impl FreshGen for SymbolGen {
107 | fn fresh(&mut self, name_hint: &Symbol) -> Symbol {
108 | let s = format!("{}{}{}", self.reserved_string, name_hint, self.gen);
109 | self.gen += 1;
110 | Symbol::from(s)
111 | }
112 | }
113 |
114 | impl FreshGen for SymbolGen {
115 | fn fresh(&mut self, name_hint: &ResolvedCall) -> ResolvedVar {
116 | let s = format!("{}{}{}", self.reserved_string, name_hint, self.gen);
117 | self.gen += 1;
118 | let sort = match name_hint {
119 | ResolvedCall::Func(f) => f.output.clone(),
120 | ResolvedCall::Primitive(SpecializedPrimitive { output, .. }) => output.clone(),
121 | };
122 | ResolvedVar {
123 | name: s.into(),
124 | sort,
125 | // fresh variables are never global references, since globals
126 | // are desugared away by `remove_globals`
127 | is_global_ref: false,
128 | }
129 | }
130 | }
131 |
132 | // This is a convenient for `for<'a> impl Into for &'a T`
133 | pub(crate) trait SymbolLike {
134 | fn to_symbol(&self) -> Symbol;
135 | }
136 |
137 | impl SymbolLike for Symbol {
138 | fn to_symbol(&self) -> Symbol {
139 | *self
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/value.rs:
--------------------------------------------------------------------------------
1 | use ordered_float::OrderedFloat;
2 | use std::num::NonZeroU32;
3 |
4 | use lazy_static::lazy_static;
5 |
6 | use crate::ast::Symbol;
7 |
8 | #[cfg(debug_assertions)]
9 | use crate::{BoolSort, F64Sort, I64Sort, Sort, StringSort};
10 |
11 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
12 | // FIXME this shouldn't be pub
13 | pub struct Value {
14 | // since egglog is type-safe, we don't need to store the tag
15 | // however, it is useful in debugging, so we keep it in debug builds
16 | #[cfg(debug_assertions)]
17 | pub tag: Symbol,
18 | pub bits: u64,
19 | }
20 |
21 | lazy_static! {
22 | static ref BOGUS: Symbol = "__bogus__".into();
23 | static ref UNIT: Symbol = "Unit".into();
24 | }
25 |
26 | impl Value {
27 | pub fn unit() -> Self {
28 | Value {
29 | #[cfg(debug_assertions)]
30 | tag: *UNIT,
31 | bits: 0,
32 | }
33 | }
34 |
35 | pub fn fake() -> Self {
36 | Value {
37 | #[cfg(debug_assertions)]
38 | tag: *BOGUS,
39 | bits: 1234567890,
40 | }
41 | }
42 | }
43 |
44 | impl From for Value {
45 | fn from(i: i64) -> Self {
46 | Self {
47 | #[cfg(debug_assertions)]
48 | tag: I64Sort.name(),
49 | bits: i as u64,
50 | }
51 | }
52 | }
53 |
54 | impl From> for Value {
55 | fn from(f: OrderedFloat) -> Self {
56 | Self {
57 | #[cfg(debug_assertions)]
58 | tag: F64Sort.name(),
59 | bits: f.into_inner().to_bits(),
60 | }
61 | }
62 | }
63 |
64 | impl From for Value {
65 | fn from(s: Symbol) -> Self {
66 | Self {
67 | #[cfg(debug_assertions)]
68 | tag: StringSort.name(),
69 | bits: NonZeroU32::from(s).get().into(),
70 | }
71 | }
72 | }
73 |
74 | impl From for Value {
75 | fn from(b: bool) -> Self {
76 | Self {
77 | #[cfg(debug_assertions)]
78 | tag: BoolSort.name(),
79 | bits: b as u64,
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/tests/antiunify.egg:
--------------------------------------------------------------------------------
1 | (datatype Expr
2 | (Num i64)
3 | (Var String)
4 | (Add Expr Expr))
5 |
6 | (rewrite (Add x y) (Add y x))
7 | (rewrite (Add (Num x) (Num y)) (Num (+ x y)))
8 |
9 | ;; antiunificaiton returns an expression that could unify with either
10 | ;; of the input expressions
11 | ;; (AU x y) can be considered a placeholder variable
12 | (constructor AU (Expr Expr) Expr)
13 |
14 | (rewrite (AU x x) x)
15 | (rewrite
16 | (AU (Add a b) (Add c d))
17 | (Add (AU a c) (AU b d)))
18 |
19 | (let e1 (Add (Var "x") (Add (Num 1) (Num 2))))
20 | (let e2 (Add (Num 3) (Var "y")))
21 |
22 | (let au12 (AU e1 e2))
23 |
24 | (run 4)
25 | (check (= au12 (Add (Num 3) (AU (Var "x") (Var "y")))))
26 | (query-extract au12)
27 |
--------------------------------------------------------------------------------
/tests/array.egg:
--------------------------------------------------------------------------------
1 | ; Smtlib theory of arrays
2 | ; https://smtlib.cs.uiowa.edu/theories-ArraysEx.shtml
3 | ; http://smtlib.cs.uiowa.edu/version1/theories/Arrays.smt
4 |
5 | (datatype Math
6 | (Num i64)
7 | (Var String)
8 | )
9 |
10 |
11 | (datatype Array
12 | (Const i64)
13 | (AVar String)
14 | )
15 |
16 | (constructor add (Math Math) Math)
17 | (constructor select (Array Math) Math)
18 | (constructor store (Array Math Math) Array)
19 |
20 | (relation neq (Math Math))
21 |
22 | (rule ((neq x y))
23 | ((neq y x)))
24 |
25 | (rule ((neq x x))
26 | ((panic "query (neq x x) found something equal to itself")))
27 |
28 |
29 | ; injectivity rules take not equal to not equal.
30 | (rule ((neq x y) (= (add x z) e))
31 | ((neq (add x z) (add y z))))
32 | (rule ((= (add x (Num i)) e) (!= i 0))
33 | ((neq e x)))
34 |
35 |
36 | (rule ((= (Num a) n1) (= (Num b) n2) (!= a b))
37 | ((neq n1 n2)))
38 |
39 | ; select gets from store
40 | (rewrite (select (store mem i e) i) e)
41 | ; select passes through wrong index
42 | (rule ((= (select (store mem i1 e) i2) e1) (neq i1 i2))
43 | ((union (select mem i2) e1)))
44 | ; aliasing writes destroy old value
45 | (rewrite (store (store mem i e1) i e2) (store mem i e2))
46 | ; non-aliasing writes commutes
47 | (rule ((= (store (store mem i2 e2) i1 e1) mem1) (neq i1 i2))
48 | ((union (store (store mem i1 e1) i2 e2) mem1)))
49 |
50 | ; typical math rules
51 | (rewrite (add x y) (add y x))
52 | (rewrite (add (add x y) z) (add x (add y z)))
53 | (rewrite (add (Num x) (Num y)) (Num (+ x y)))
54 | (rewrite (add x (Num 0)) x)
55 |
56 | (push)
57 | (let r1 (Var "r1"))
58 | (let r2 (Var "r2"))
59 | (let r3 (Var "r3"))
60 | (let mem1 (AVar "mem1"))
61 |
62 | (neq r1 r2)
63 | (neq r2 r3)
64 | (neq r1 r3)
65 | (let test1 (select (store mem1 r1 (Num 42)) r1))
66 | (let test2 (select (store mem1 r1 (Num 42)) (add r1 (Num 17))))
67 | (let test3 (select (store (store mem1 (add r1 r2) (Num 1)) (add r2 r1) (Num 2)) (add r1 r3)))
68 | (let test4 (add (Num 1) (add (add (Num 1) (add (Num 1) r1)) (Num -3))))
69 |
70 | (run 5)
71 | (check (= test1 (Num 42)))
72 | (check (neq r1 r2))
73 | (check (neq r1 (add r1 (Num 17))))
74 | (check (= test2 (select mem1 (add r1 (Num 17)))))
75 | (check (= test3 (select mem1 (add r1 r3))))
76 | (check (= test4 r1))
77 | (pop)
78 |
--------------------------------------------------------------------------------
/tests/bdd.egg:
--------------------------------------------------------------------------------
1 | ; Binary Decision Diagrams are if-then-else trees/ compressed tries that hash cons their leaves
2 | ; This is easily expressible in the facilities provided. Everything in egglog is automatcally shared
3 | ; and Compression is easily expressible as a rule.
4 |
5 | ; They are a notion of first class set useful for certain classes of uniformly describable sets.
6 | ; https://en.wikipedia.org/wiki/Binary_decision_diagram
7 | ; https://www.lri.fr/~filliatr/ftp/publis/hash-consing2.pdf Type-Safe Modular Hash-Consing - Section 3.3
8 |
9 | (datatype BDD
10 | (ITE i64 BDD BDD) ; variables labelled by number
11 | )
12 | (constructor TrueConst () BDD)
13 | (let True (TrueConst))
14 | (constructor FalseConst () BDD)
15 | (let False (FalseConst))
16 |
17 | ; compress unneeded nodes
18 | (rewrite (ITE n a a) a)
19 |
20 | (constructor bddand (BDD BDD) BDD)
21 | (rewrite (bddand x y) (bddand y x))
22 | (rewrite (bddand False n) False)
23 | (rewrite (bddand True x) x)
24 |
25 | ; We use an order where low variables are higher in tree
26 | ; Could go the other way.
27 | (rewrite (bddand (ITE n a1 a2) (ITE m b1 b2))
28 | (ITE n (bddand a1 (ITE m b1 b2)) (bddand a2 (ITE m b1 b2)))
29 | :when ((< n m))
30 | )
31 | (rewrite (bddand (ITE n a1 a2) (ITE n b1 b2))
32 | (ITE n (bddand a1 b1) (bddand a2 b2))
33 | )
34 |
35 | (constructor bddor (BDD BDD) BDD)
36 | (rewrite (bddor x y) (bddor y x))
37 | (rewrite (bddor True n) True)
38 | (rewrite (bddor False x) x)
39 | (rewrite (bddor (ITE n a1 a2) (ITE m b1 b2))
40 | (ITE n (bddor a1 (ITE m b1 b2)) (bddor a2 (ITE m b1 b2)))
41 | :when ((< n m))
42 | )
43 | (rewrite (bddor (ITE n a1 a2) (ITE n b1 b2))
44 | (ITE n (bddor a1 b1) (bddor a2 b2))
45 | )
46 |
47 | (constructor bddnot (BDD) BDD)
48 | (rewrite (bddnot True) False)
49 | (rewrite (bddnot False) True)
50 | (rewrite (bddnot (ITE n a1 a2)) (ITE n (bddnot a1) (bddnot a2)))
51 |
52 |
53 | (constructor bddxor (BDD BDD) BDD)
54 | (rewrite (bddxor x y) (bddxor y x))
55 | (rewrite (bddxor True n) (bddnot n))
56 | (rewrite (bddxor False x) x)
57 |
58 | (rewrite (bddxor (ITE n a1 a2) (ITE m b1 b2))
59 | (ITE n (bddxor a1 (ITE m b1 b2)) (bddxor a2 (ITE m b1 b2)))
60 | :when ((< n m))
61 | )
62 | (rewrite (bddxor (ITE n a1 a2) (ITE n b1 b2))
63 | (ITE n (bddxor a1 b1) (bddxor a2 b2))
64 | )
65 |
66 | (push)
67 | ;;; Tests
68 |
69 | (let v0 (ITE 0 True False))
70 | (let v1 (ITE 1 True False))
71 | (let v2 (ITE 2 True False))
72 |
73 | (let t0 (bddnot (bddnot v0)))
74 | (let t1 (bddor v0 (bddnot v0)))
75 | (let t2 (bddand v0 (bddnot v0)))
76 | (let t3 (bddand v0 v0))
77 | (let t4 (bddor v0 v0))
78 | (let t5 (bddxor (bddnot v0) v0))
79 | (let t6 (bddand (bddor v1 v2) v2))
80 |
81 | (let t7a (bddxor (bddnot v0) v1))
82 | (let t7b (bddxor v0 (bddnot v1)))
83 | (let t7c (bddnot (bddxor v0 v1)))
84 |
85 | (let t8 (bddand v1 v2))
86 |
87 | (let t9 (bddand (bddnot v1) (bddand (bddnot v0) (bddxor v0 v1))))
88 | (let t10 (bddor (bddnot v1) (bddor (bddnot v0) (bddxor v0 (bddnot v1)))))
89 |
90 | (run 30)
91 |
92 | (check (= t0 v0)) ; bddnot cancels
93 | (check (= t1 True))
94 | (check (= t2 False))
95 | (check (= t3 v0))
96 | (check (= t4 v0))
97 | (check (= t5 True))
98 | (check (= t6 v2))
99 |
100 | (check (= t7a t7b))
101 | (check (= t7a t7c))
102 |
103 | (check (= t8 (ITE 1 (ITE 2 True False) False)))
104 |
105 | (check (= t9 False))
106 | (check (= t10 True))
107 | (pop)
108 |
--------------------------------------------------------------------------------
/tests/before-proofs.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Add Math Math)
3 | (Sub Math Math)
4 | (Const i64)
5 | (Var String))
6 |
7 | (rewrite (Add a b) (Add (Add a b) (Const 0)))
8 |
9 | (rewrite (Add a b) (Add b a))
10 |
11 |
12 | (rewrite (Add a (Add b c))
13 | (Add (Add a b) c))
14 |
15 | (let two 2)
16 | (let start1 (Add (Var "x") (Const two)))
17 | ;; add original proofs
18 |
19 | (run 3)
20 |
21 |
22 | (check (!= (Var "x") (Const two)))
23 | (check (= (Add (Var "x") (Const two))
24 | (Add (Const two) (Var "x"))))
25 |
26 | (let zero (Const 0))
27 | (let addx2 (Add (Var "x") (Const two)))
28 | (let addx20 (Add addx2 zero))
29 | (let addzerofront (Add (Add zero (Var "x")) (Const two)))
30 |
31 | (check (= addx2
32 | addx20))
33 |
--------------------------------------------------------------------------------
/tests/bignum.egg:
--------------------------------------------------------------------------------
1 |
2 | (let x (bigint -1234))
3 | (let y (from-string "2"))
4 | (let z (bigrat x y))
5 | (check (= (to-string (numer z)) "-617"))
6 |
7 | (function bignums (BigInt BigInt) BigRat :no-merge)
8 | (set (bignums x y) z)
9 | (check
10 | (= (bignums a b) c)
11 | (= (numer c) (>> a 1))
12 | (= (denom c) (>> b 1))
13 | )
14 |
--------------------------------------------------------------------------------
/tests/birewrite.egg:
--------------------------------------------------------------------------------
1 | (datatype Math (Add Math Math) (Lit i64))
2 |
3 | (birewrite (Add (Add x y) z) (Add x (Add y z)))
4 |
5 | (let a (Lit 1))
6 | (let b (Lit 2))
7 | (let c (Lit 3))
8 |
9 | (let d (Lit 4))
10 | (let e (Lit 5))
11 | (let f (Lit 6))
12 |
13 | (let ex1 (Add (Add a b) c))
14 | (let ex2 (Add d (Add e f)))
15 |
16 | (run 10)
17 | (check (= ex1 (Add a (Add b c))))
18 | (check (= ex2 (Add (Add d e) f)))
19 |
--------------------------------------------------------------------------------
/tests/bitwise.egg:
--------------------------------------------------------------------------------
1 | (check (= 0 (& 10 0)))
2 | (check (= 8 (& 8 10)))
3 | (check (= 10 (| 8 10)))
4 | (check (= 2 (^ 8 10)))
5 | (check (= 8 (<< 1 3)))
6 | (check (= 1 (>> 8 3)))
7 | (check (= 2 (% 8 3)))
8 | (check (= 2 (/ 8 3)))
9 | (check (= -1 (not-i64 0)))
10 |
11 | ; bitsets
12 | ;(function bs-union (i64 i64) i64)
13 | ;(rewrite (bs-union a b) (| a b))
14 |
15 | ;(function bs-inter (i64 i64) i64)
16 | ;(rewrite (bs-inter a b) (& a b))
17 |
18 | ;(function bs-comp (i64) i64)
19 | ;(rewrite (bs-comp a) (bvnot a))
20 |
21 | ; singleton set
22 | ;(function bs-sing (i64) i64)
23 | ;(rewrite (bs-sing a) (1 << a))
24 |
25 | ;(function bs-insert (i64 i64) i64)
26 | ;(rewrite (bs-insert s x) (| s (1 << a))
27 |
28 | ;(function bs-diff (i64 i64) i64)
29 | ;(rewrite (bs-diff a b) (^ a (bs-inter a b))
30 |
31 | ;(let bs-empty 0)
32 |
33 | ;(let bs-subset (i64 i64) bool)
34 | ;(rewrite (bs-subset x y) (is-zero (bs-diff x y)))
35 |
36 | ;(let bs-is-elem (i64 i64) bool)
37 | ;(rewrite (bs-is-elem s x) (not (is-zero (bs-inter s (sing x)))))
38 |
--------------------------------------------------------------------------------
/tests/bool.egg:
--------------------------------------------------------------------------------
1 |
2 | (check (= (and true true) true))
3 | (check (= (and true false) false))
4 | (check (= (or true false) true))
5 | (check (!= (or true false) false))
6 |
7 | (check (= (bool-= 1 1) true))
8 | (check (= (bool-= -5 -5) true))
9 | (check (= (bool-= 1 3) false))
10 | (check (= (bool-= 3 1) false))
11 |
12 | (check (= (bool-< 1 2) true))
13 | (check (= (bool-< 2 1) false))
14 | (check (= (bool-< 1 1) false))
15 |
16 | (check (= (bool-<= 1 2) true))
17 | (check (= (bool-<= 2 1) false))
18 | (check (= (bool-<= 1 1) true))
19 |
20 | (check (= (bool-> 1 2) false))
21 | (check (= (bool-> 2 1) true))
22 | (check (= (bool-> 1 1) false))
23 |
24 | (check (= (bool->= 1 2) false))
25 | (check (= (bool->= 2 1) true))
26 | (check (= (bool->= 1 1) true))
27 |
28 | ; Test bool's tag
29 | (relation R (i64))
30 | (function F (i64) bool :no-merge)
31 |
32 | (rule
33 | ((R i))
34 | ((set (F i) true))
35 | )
36 |
37 | (R 0)
38 |
39 | (run 3)
40 |
--------------------------------------------------------------------------------
/tests/calc.egg:
--------------------------------------------------------------------------------
1 | (datatype G)
2 | (constructor IConst () G)
3 | (let I (IConst))
4 | (constructor AConst () G)
5 | (let A (AConst))
6 | (constructor BConst () G)
7 | (let B (BConst))
8 | (constructor g* (G G) G)
9 | (constructor inv (G) G)
10 | (birewrite (g* (g* a b) c) (g* a (g* b c))) ; assoc
11 | (rewrite (g* I a) a) ; idl
12 | (rewrite (g* a I) a) ; idr
13 | (rewrite (g* (inv a) a) I) ; invl
14 | (rewrite (g* a (inv a)) I) ; invr
15 |
16 | ; A is cyclic of period 4
17 | (rewrite (g* A (g* A (g* A A))) I)
18 |
19 | (let A2 (g* A A))
20 | (let A4 (g* A2 A2))
21 | (let A8 (g* A4 A4))
22 |
23 |
24 | (push)
25 | (g* A4 A4)
26 |
27 | (run 10000 :until (= (g* A4 A4) (g* (g* A2 A2) (g* A2 A2))))
28 |
29 | (check (= (g* A4 A4) (g* (g* A2 A2) (g* A2 A2))))
30 | (pop)
31 |
32 | (push)
33 | (g* (g* A2 A2) (g* A2 A2))
34 |
35 | (run 10000 :until (= (g* (g* A2 A2) (g* A2 A2))
36 | (g* A2 (g* A2 (g* A2 A2)))))
37 | (check (= (g* (g* A2 A2) (g* A2 A2))
38 | (g* A2 (g* A2 (g* A2 A2)))))
39 | (pop)
40 |
41 |
42 | (constructor aConst () G)
43 | (constructor bConst () G)
44 | (let a (aConst))
45 | (let b (bConst))
46 | (push)
47 |
48 | (g* (g* b (g* (inv a) a)) (inv b))
49 |
50 | (run 100000 :until (= (g* (g* b (g* (inv a) a)) (inv b)) (g* b (inv b))))
51 |
52 | (check (= (g* (g* b (g* (inv a) a)) (inv b)) (g* b (inv b))))
53 |
54 | (pop)
55 |
56 | (push)
57 | (g* b (inv b))
58 | (run 100000 :until (= (g* b (inv b)) I))
59 | (check (= (g* b (inv b)) I))
60 |
61 | (pop)
62 |
--------------------------------------------------------------------------------
/tests/combinators.egg:
--------------------------------------------------------------------------------
1 | ; Substitution in lambda-calculus via S/K/I combinators. Extremely slow, as
2 | ; abstraction elimination does not pay attention to whether variables are free
3 | ; in an expression before introducing 'S'.
4 | ;
5 | ; Provides an example of how to implement substitution by embedding in a
6 | ; 'richer' data-type and then mapping back to syntax.
7 |
8 | (datatype Expr
9 | (Var String :cost 100)
10 | (Abs String Expr)
11 | (If Expr Expr Expr)
12 | (N i64)
13 | (Add Expr Expr)
14 | (App Expr Expr))
15 | (constructor TConst () Expr)
16 | (let T (TConst))
17 | (constructor FConst () Expr)
18 | (let F (FConst))
19 |
20 |
21 | ; (\x. (if x then 0 else 1) + 2) false
22 | (let test
23 | (App
24 | (Abs "x" (Add (If (Var "x") (N 0) (N 1)) (N 2))) F))
25 |
26 | (datatype CExpr
27 | (CVar String :cost 10000) ; (variables that haven't been eliminated yet)
28 | (CAbs String CExpr :cost 10000) ; (abstractions that haven't been eliminated yet)
29 | (CN i64)
30 | (CApp CExpr CExpr))
31 | (constructor CTConst () CExpr)
32 | (let CT (CTConst))
33 | (constructor CFConst () CExpr)
34 | (let CF (CFConst))
35 | (constructor CIfConst () CExpr)
36 | (let CIf (CIfConst))
37 | (constructor CAddConst () CExpr)
38 | (let CAdd (CAddConst))
39 | (constructor SConst () CExpr)
40 | (let S (SConst))
41 | (constructor KConst () CExpr)
42 | (let K (KConst))
43 | (constructor IConst () CExpr)
44 | (let I (IConst))
45 |
46 | ;;;; Conversion functions
47 | (constructor Comb (Expr) CExpr :cost 1000000)
48 | (constructor Uncomb (CExpr) Expr)
49 | (rewrite (Comb (Uncomb cx)) cx)
50 | (rewrite (Uncomb (Comb x)) x)
51 |
52 | ; Mechanical mappings back and forth.
53 | ; Note: we avoid resugaring S/K/I
54 | (rule ((= x (N n))) ((union (Comb x) (CN n))))
55 | (rule ((= cx (CN n))) ((union (Uncomb cx) (N n))))
56 | (rule ((= x T)) ((union (Comb x) CT)))
57 | (rule ((= cx CT)) ((union (Uncomb cx) T)))
58 | (rule ((= x F)) ((union (Comb x) CF)))
59 | (rule ((= cx CF)) ((union (Uncomb cx) F)))
60 |
61 | (rule ((= x (If c t f)))
62 | ((union (Comb x) (CApp (CApp (CApp CIf (Comb c)) (Comb t)) (Comb f)))))
63 | (rule ((= cx (CApp (CApp (CApp CIf cc) ct) cf)))
64 | ((union (Uncomb cx) (If (Uncomb cc) (Uncomb ct) (Uncomb cf)))))
65 |
66 | (rule ((= x (Add l r)))
67 | ((union (Comb x) (CApp (CApp CAdd (Comb l)) (Comb r)))))
68 | (rule ((= cx (CApp (CApp CAdd cl) cr)))
69 | ((union (Uncomb cx) (Add (Uncomb cl) (Uncomb cr)))))
70 | (rule ((= x (App f a))) ((union (Comb x) (CApp (Comb f) (Comb a)))))
71 |
72 | (rule ((= x (Var v))) ((union (Comb x) (CVar v))))
73 | (rule ((= x (Abs v body))) ((union (Comb x) (CAbs v (Comb body)))))
74 |
75 | ;;;; Abstraction Elimination
76 | (rewrite (CAbs v (CVar v)) I)
77 | ; Hacks, could be replaced by !free computation.
78 | (rewrite (CAbs v1 (CVar v2)) (CApp K (CVar v2))
79 | :when ((!= v1 v2)))
80 | (rewrite (CAbs v (CN n)) (CApp K (CN n)))
81 | (rewrite (CAbs v CT) (CApp K CT))
82 | (rewrite (CAbs v CF) (CApp K CF))
83 | (rewrite (CAbs v CIf) (CApp K CIf))
84 | (rewrite (CAbs v CAdd) (CApp K CAdd))
85 | (rewrite (CAbs v (CApp x y)) (CApp (CApp S (CAbs v x)) (CAbs v y)))
86 | ; May be needed for multiple nested variables
87 | (rewrite (CAbs v (CApp K (CVar v))) K)
88 |
89 | ;;;; Primitive Evaluation rules (letd on "surface syntax")
90 | (rewrite (If T t f) t)
91 | (rewrite (If F t f) f)
92 | (rewrite (Add (N n) (N m)) (N (+ n m)))
93 |
94 | ;;;; Substitution Rules (letd on the combinator representation)
95 | (rewrite (CApp I cx) cx)
96 | (rewrite (CApp (CApp K cx) cy) cx)
97 | ; Without demand, this can cause an explosion in DB size.
98 | (rewrite (CApp (CApp (CApp S cx) cy) cz) (CApp (CApp cx cz) (CApp cy cz)))
99 |
100 | (run 11)
101 | (query-extract (Comb test))
102 | (check (= test (N 3)))
--------------------------------------------------------------------------------
/tests/combined-nested.egg:
--------------------------------------------------------------------------------
1 | (relation number (i64))
2 |
3 |
4 | (ruleset myrules1)
5 | (rule ()
6 | ((number 1))
7 | :ruleset myrules1)
8 | (ruleset myrules2)
9 | (rule ()
10 | ((number 2))
11 | :ruleset myrules2)
12 |
13 | (unstable-combined-ruleset rules1and2
14 | myrules1 myrules2)
15 |
16 | ;; allowed to add to myrules2 and the change is reflected
17 | (rule ()
18 | ((number 3))
19 | :ruleset myrules2)
20 |
21 | ;; not allowed to add to combined ruleset
22 | (fail
23 | (rule ()
24 | ((number 4))
25 | :ruleset myrules1and2))
26 |
27 |
28 | (fail
29 | (rule ()
30 | ((number 4))
31 | :ruleset unboundruleset))
32 |
33 | (ruleset myrules5)
34 | (rule ()
35 | ((number 5))
36 | :ruleset myrules5)
37 |
38 | (unstable-combined-ruleset rules1and2and5
39 | rules1and2 myrules5)
40 |
41 | (run-schedule
42 | rules1and2and5)
43 |
44 | (check (number 1))
45 | (check (number 2))
46 | (check (number 3))
47 | (check (number 5))
48 | (fail (check (number 4)))
49 |
--------------------------------------------------------------------------------
/tests/container-rebuild.egg:
--------------------------------------------------------------------------------
1 | (push)
2 | (datatype Math
3 | (Num i64))
4 |
5 | (sort MathVec (Vec Math))
6 |
7 | (let v1 (vec-of (Num 1) (Num 2)))
8 | (let v2 (vec-of (Num 2) (Num 2)))
9 |
10 | (union (Num 1) (Num 2))
11 |
12 | (check (= v1 v2))
13 |
14 | (constructor MyVec (MathVec) Math)
15 |
16 | (MyVec v1)
17 |
18 | (check (MyVec v2))
19 |
20 | (check (= (MyVec v1) (MyVec v2)))
21 |
22 | (let v3 (vec-of (Num 4) (Num 5)))
23 |
24 | (union (Num 4) (Num 6))
25 | (union (Num 5) (Num 7))
26 |
27 | ;; We don't have any (MyVec v3) yet
28 | (fail (check (= (MyVec v3) (MyVec (vec-of (Num 6) (Num 7))))))
29 |
30 | (MyVec v3)
31 | (check (= (MyVec v3) (MyVec (vec-of (Num 6) (Num 7)))))
32 |
33 | (pop)
34 |
35 | (push)
36 |
37 | (datatype Math
38 | (Num i64))
39 |
40 | (sort MathVec (Vec Math))
41 |
42 |
43 | (let v1 (vec-of (Num 1) (Num 2)))
44 | (let v2 (vec-of (Num 2) (Num 2)))
45 |
46 | (union (Num 1) (Num 2))
47 |
48 | (constructor MyVec (MathVec) Math)
49 |
50 | ;; make a reference to v1
51 | (MyVec v1)
52 |
53 | (extract (MyVec v1))
54 |
55 | ;; rebuilding creates (MyVec v2)
56 | (check (= (MyVec v1) (MyVec v2)))
57 | (pop)
58 |
59 | (push)
60 | (datatype Math
61 | (Add i64 i64)
62 | (Expensive :cost 100))
63 |
64 | (sort MathVec (Vec Math))
65 |
66 | (let myvec (vec-of (Expensive)))
67 | (let cheapvec (vec-of (Add 1 2)))
68 |
69 | (constructor VecContainer (MathVec) Math)
70 |
71 | (let myvecontainer (VecContainer cheapvec))
72 |
73 |
74 | (union myvecontainer (Expensive))
75 |
76 | ;; (vec-push (vec-empty) (VecContainer (vec-push (vec-empty) (Add 1 2))))
77 | ;; should have cost 4
78 | (extract myvec 0)
79 |
80 | (pop)
--------------------------------------------------------------------------------
/tests/cyk.egg:
--------------------------------------------------------------------------------
1 | (datatype term (Term String))
2 | (datatype nonterm (NonTerm String))
3 | (datatype tree (NT String tree tree)
4 | (T String String))
5 |
6 | (function getString (i64) String :no-merge)
7 |
8 | (relation Prod (nonterm nonterm nonterm))
9 | (relation End (nonterm String))
10 |
11 |
12 |
13 | (relation P (i64 i64 nonterm))
14 | (constructor B (i64 i64 nonterm) tree :cost 1000)
15 |
16 | (rule ((End (NonTerm a) s)
17 | (= s (getString pos)))
18 | ((P 1 pos (NonTerm a))
19 | (union (B 1 pos (NonTerm a)) (T a s))))
20 |
21 | (rule ((Prod (NonTerm a) (NonTerm b) (NonTerm c)) ;; a -> bc
22 | (P p1 s (NonTerm b))
23 | (P p2 (+ s p1) (NonTerm c)))
24 | ((P (+ p1 p2) s (NonTerm a))))
25 |
26 |
27 | (rule ((Prod (NonTerm a) (NonTerm b) (NonTerm c))
28 | (= f1 (B p1 s (NonTerm b)))
29 | (= f2 (B p2 (+ s p1) (NonTerm c))))
30 | ((union (B (+ p1 p2) s (NonTerm a))
31 | (NT a f1 f2))))
32 |
33 | (push)
34 |
35 |
36 | (set (getString 1) "she")
37 | (set (getString 2) "eats")
38 | (set (getString 3) "a")
39 | (set (getString 4) "fish")
40 | (set (getString 5) "with")
41 | (set (getString 6) "a")
42 | (set (getString 7) "fork")
43 |
44 |
45 | (Prod (NonTerm "S") (NonTerm "NP") (NonTerm "VP"))
46 | (Prod (NonTerm "VP") (NonTerm "VP") (NonTerm "PP"))
47 | (Prod (NonTerm "VP") (NonTerm "V") (NonTerm "NP"))
48 | (End (NonTerm "VP") "eats")
49 | (Prod (NonTerm "PP") (NonTerm "P") (NonTerm "NP"))
50 | (Prod (NonTerm "NP") (NonTerm "DET") (NonTerm "N"))
51 | (End (NonTerm "NP") "she")
52 | (End (NonTerm "V") "eats")
53 | (End (NonTerm "P") "with")
54 | (End (NonTerm "N") "fish")
55 | (End (NonTerm "N") "fork")
56 | (End (NonTerm "DET") "a")
57 |
58 |
59 | (run 100)
60 |
61 | (let test1 (B 7 1 (NonTerm "S")))
62 |
63 | (check (P 7 1 (NonTerm "S")))
64 | (fail (check (P 7 1 (NonTerm "VP"))))
65 | (fail (check (P 7 1 (NonTerm ""))))
66 |
67 | (query-extract test1)
68 |
69 | (pop)
70 |
71 | (push)
72 |
73 | (Prod (NonTerm "S") (NonTerm "A") (NonTerm "B"))
74 | (Prod (NonTerm "S") (NonTerm "B") (NonTerm "C"))
75 | (Prod (NonTerm "A") (NonTerm "B") (NonTerm "A"))
76 | (End (NonTerm "A") "a")
77 | (Prod (NonTerm "B") (NonTerm "C") (NonTerm "C"))
78 | (End (NonTerm "B") "b")
79 | (Prod (NonTerm "C") (NonTerm "A") (NonTerm "B"))
80 | (End (NonTerm "C") "a")
81 |
82 | (push)
83 |
84 | (set (getString 1) "a")
85 | (set (getString 2) "b")
86 | (set (getString 3) "a")
87 | (set (getString 4) "a")
88 | (set (getString 5) "b")
89 |
90 | (run 100)
91 | (check (P 5 1 (NonTerm "S")))
92 | (fail (check (P 5 1 (NonTerm "B"))))
93 | (let test2 (B 5 1 (NonTerm "S")))
94 | (query-extract :variants 10 test2)
95 |
96 | (pop)
97 |
98 | (push)
99 |
100 | (set (getString 1) "a")
101 | (set (getString 2) "a")
102 | (set (getString 3) "a")
103 | (set (getString 4) "a")
104 | (set (getString 5) "a")
105 |
106 | (run 100)
107 | (check (P 5 1 (NonTerm "S")))
108 | (check (P 5 1 (NonTerm "A")))
109 | (fail (check (P 5 1 (NonTerm "B"))))
110 | (fail (check (P 5 1 (NonTerm ""))))
111 | (fail (check (P 5 1 (NonTerm "unrelated"))))
112 | (let test3 (B 5 1 (NonTerm "S")))
113 | (query-extract :variants 10 test3)
114 |
115 | (pop)
--------------------------------------------------------------------------------
/tests/cykjson.egg:
--------------------------------------------------------------------------------
1 | (datatype tree (NT String tree tree)
2 | (T String String))
3 |
4 | (function getString (i64) String :no-merge)
5 |
6 | (relation Prod (String String String))
7 | (relation End (String String))
8 |
9 |
10 | (relation P (i64 i64 String))
11 | (function B (i64 i64 String) tree :no-merge)
12 |
13 | (rule ((End a s)
14 | (= s (getString pos)))
15 | ((P 1 pos a)
16 | (set (B 1 pos a) (T a s))))
17 |
18 | (rule ((Prod a b c) ;; a -> bc
19 | (P p1 s b)
20 | (P p2 (+ s p1) c))
21 | ((P (+ p1 p2) s a)))
22 |
23 |
24 | (rule ((Prod a b c)
25 | (= f1 (B p1 s b))
26 | (= f2 (B p2 (+ s p1) c)))
27 | ((set (B (+ p1 p2) s a)
28 | (NT a f1 f2))))
29 |
30 |
31 | (input Prod "./tests/cykjson_Prod.csv")
32 | (input End "./tests/cykjson_End.csv")
33 |
34 | ; small size 801
35 | (input getString "./tests/cykjson_small_token.csv")
36 |
37 | ; medium size 7821 but runs for 2 min.
38 | ;(input getString "./tests/cykjson_medium_token.csv")
39 |
40 | (run 10000)
41 |
42 | (let test1 (B 801 1 "VAL"))
43 |
44 | (check (P 801 1 "VAL"))
--------------------------------------------------------------------------------
/tests/cykjson_End.csv:
--------------------------------------------------------------------------------
1 | VAL str
2 | VAL num
3 | VAL true
4 | VAL false
5 | VAL null
6 | ELM str
7 | ELM num
8 | ELM true
9 | ELM false
10 | ELM null
11 | H0 {
12 | H1 }
13 | H3 [
14 | H4 ]
15 | H9 ,
16 | H10 str
17 | H11 :
--------------------------------------------------------------------------------
/tests/cykjson_Prod.csv:
--------------------------------------------------------------------------------
1 | VAL H0 H1
2 | VAL H2 H1
3 | VAL H3 H4
4 | VAL H5 H4
5 | MEM H6 MEM
6 | MEM H7 VAL
7 | PR H7 VAL
8 | ELM H8 ELM
9 | ELM H0 H1
10 | ELM H2 H1
11 | ELM H3 H4
12 | ELM H5 H4
13 | H2 H0 MEM
14 | H5 H3 ELM
15 | H6 PR H9
16 | H7 H10 H11
17 | H8 VAL H9
--------------------------------------------------------------------------------
/tests/datatypes.egg:
--------------------------------------------------------------------------------
1 | (datatype*
2 | (Math
3 | (Add Math Math)
4 | (Sum MathVec)
5 | (B Bool))
6 | (sort MathVec (Vec Math))
7 | (Bool
8 | (True)
9 | (False)))
10 |
11 | (let expr (Add (Sum (vec-of (B (True)) (B (False)))) (B (True))))
12 |
--------------------------------------------------------------------------------
/tests/delete.egg:
--------------------------------------------------------------------------------
1 | (function foo (i64) i64 :no-merge)
2 | (set (foo 1) 7)
3 | (check (= (foo 1) 7))
4 | (delete (foo 1))
5 | (rule ((= x (foo 1))) ((panic "foo 1 was there!")))
6 | (run 1)
--------------------------------------------------------------------------------
/tests/eqsat-basic-multiset.egg:
--------------------------------------------------------------------------------
1 | ;; Example showing how to use multisets to hold associative & commutative operations
2 |
3 | (datatype*
4 | (Math
5 | (Num i64)
6 | (Var String)
7 | (Add Math Math)
8 | (Mul Math Math)
9 | (Product MathMultiSet)
10 | (Sum MathMultiSet))
11 | (sort MathToMath (UnstableFn (Math) Math))
12 | (sort MathMultiSet (MultiSet Math)))
13 |
14 | ;; expr1 = 2 * (x + 3)
15 | (let expr1 (Mul (Num 2) (Add (Var "x") (Num 3))))
16 | ;; expr2 = 6 + 2 * x
17 | (let expr2 (Add (Num 6) (Mul (Num 2) (Var "x"))))
18 |
19 | (rewrite (Add a b) (Sum (multiset-of a b)))
20 | (rewrite (Mul a b) (Product (multiset-of a b)))
21 |
22 | ;; 0 or 1 elements sums/products also can be extracted back to numbers
23 | (rule
24 | (
25 | (= sum (Sum sum-inner))
26 | (= 0 (multiset-length sum-inner))
27 | )
28 | ((union sum (Num 0)))
29 | )
30 | (rule
31 | (
32 | (= sum (Sum sum-inner))
33 | (= 1 (multiset-length sum-inner))
34 | )
35 | ((union sum (multiset-pick sum-inner)))
36 | )
37 |
38 | (rule
39 | (
40 | (= product (Product product-inner))
41 | (= 0 (multiset-length product-inner))
42 | )
43 | ((union product (Num 1)))
44 | )
45 | (rule
46 | (
47 | (= product (Product product-inner))
48 | (= 1 (multiset-length product-inner))
49 | )
50 | ((union product (multiset-pick product-inner)))
51 | )
52 |
53 | ; (rewrite (Mul a (Add b c))
54 | ; (Add (Mul a b) (Mul a c)))
55 |
56 | ; -> we would like to write it like this, but cannot (yet) bc we can't match on the inner structure of the multisets
57 | ; and we don't support anonymous functions
58 |
59 | ; (rewrite (Product (multiset-insert a (Sum bc)))
60 | ; (Sum (multiset-map (lambda (x) (Product (multiset-insert a x))) bc)))
61 |
62 |
63 | ;; so instead we can define a function and partially apply it to get the same function as the lambda
64 | (constructor tmp-fn (MathMultiSet Math) Math)
65 | (rewrite (tmp-fn xs x) (Product (multiset-insert xs x)))
66 |
67 | (rule
68 | (
69 | ;; and we can do a cross product search of all possible pairs of products/sums to find one we want
70 | (= sum (Sum bc))
71 | (= product (Product product-inner))
72 | (multiset-contains product-inner sum)
73 | (> (multiset-length product-inner) 1)
74 | (= a (multiset-remove product-inner sum))
75 | )
76 | (
77 | (union product (Sum
78 | (unstable-multiset-map
79 | (unstable-fn "tmp-fn" a)
80 | bc)
81 | ))
82 | )
83 | )
84 |
85 | ; (rewrite (Add (Num a) (Num b))
86 | ; (Num (+ a b)))
87 |
88 | (rule
89 | (
90 | (= sum (Sum sum-inner))
91 | (= num-a (Num a))
92 | (multiset-contains sum-inner num-a)
93 | (= without-a (multiset-remove sum-inner num-a))
94 | (= num-b (Num b))
95 | (multiset-contains without-a num-b)
96 | )
97 | (
98 | (union sum
99 | (Sum (multiset-insert (multiset-remove without-a num-b) (Num (+ a b))))
100 | )
101 | )
102 | )
103 |
104 | ; (rewrite (Mul (Num a) (Num b))
105 | ; (Num (* a b)))
106 |
107 | (rule
108 | (
109 | (= product (Product product-inner))
110 | (= num-a (Num a))
111 | (multiset-contains product-inner num-a)
112 | (= without-a (multiset-remove product-inner num-a))
113 | (= num-b (Num b))
114 | (multiset-contains without-a num-b)
115 | )
116 | (
117 | (union product
118 | (Product (multiset-insert (multiset-remove without-a num-b) (Num (* a b))))
119 | )
120 | )
121 | )
122 |
123 | (run 100)
124 | (check (= expr1 expr2))
125 |
--------------------------------------------------------------------------------
/tests/eqsat-basic.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Num i64)
3 | (Var String)
4 | (Add Math Math)
5 | (Mul Math Math))
6 |
7 | ;; expr1 = 2 * (x + 3)
8 | (let expr1 (Mul (Num 2) (Add (Var "x") (Num 3))))
9 | ;; expr2 = 6 + 2 * x
10 | (let expr2 (Add (Num 6) (Mul (Num 2) (Var "x"))))
11 |
12 |
13 | ;; (rule ((= __root (Add a b)))
14 | ;; ((union __root (Add b a)))
15 | (rewrite (Add a b)
16 | (Add b a))
17 | (rewrite (Mul a (Add b c))
18 | (Add (Mul a b) (Mul a c)))
19 | (rewrite (Add (Num a) (Num b))
20 | (Num (+ a b)))
21 | (rewrite (Mul (Num a) (Num b))
22 | (Num (* a b)))
23 |
24 | (run 10)
25 | (check (= expr1 expr2))
26 |
--------------------------------------------------------------------------------
/tests/eqsolve.egg:
--------------------------------------------------------------------------------
1 | (datatype Expr
2 | (Add Expr Expr)
3 | (Neg Expr)
4 | (Num i64)
5 | (Mul Expr Expr)
6 | (Var String)
7 | )
8 |
9 | (rewrite (Add x y) (Add y x))
10 | (rewrite (Add (Add x y) z) (Add x (Add y z)))
11 | (rewrite (Add (Num x) (Num y)) (Num (+ x y)))
12 | (rule ((= (Add x y) z))
13 | ((union (Add z (Neg y)) x)))
14 | (rewrite (Neg (Neg x)) x)
15 | (rewrite (Neg (Num n)) (Num (- 0 n)))
16 |
17 | (rule ((= x (Var v))) ((union (Mul (Num 1) x) x)))
18 | (rule ((= x (Add x1 x2))) ((union (Mul (Num 1) x) x)))
19 | (rewrite (Add (Mul y x) (Mul z x)) (Mul (Add y z) x))
20 | (rewrite (Mul x y) (Mul y x))
21 | (rule ((= (Mul (Num x) y) (Num z))
22 | (= (% z x) 0))
23 | ((union y (Num (/ z x)))))
24 |
25 | ; system 1: x + 2 = 7
26 | (union (Add (Var "x") (Num 2)) (Num 7))
27 | ; system 2: z + y = 6, 2z = y
28 | (union (Add (Var "z") (Var "y")) (Num 6))
29 | (union (Add (Var "z") (Var "z")) (Var "y"))
30 |
31 | (run 5)
32 | (query-extract (Var "x"))
33 | (query-extract (Var "y"))
34 | (query-extract (Var "z"))
35 | (check (= (Var "z") (Add (Num 6) (Neg (Var "y")))))
36 | (check (= (Var "y") (Add (Add (Num 6) (Neg (Var "y"))) (Add (Num 6) (Neg (Var "y"))))))
37 | (check (= (Var "y") (Add (Add (Num 12) (Neg (Var "y"))) (Neg (Var "y")))))
38 | (check (= (Add (Var "y") (Var "y"))
39 | (Add (Num 12) (Neg (Var "y")))))
40 | (check (= (Add (Add (Var "y") (Var "y")) (Var "y"))
41 | (Num 12)))
42 | (check (= (Add (Mul (Num 2) (Var "y")) (Var "y"))
43 | (Num 12)))
44 | (check (= (Mul (Num 3) (Var "y"))
45 | (Num 12)))
46 |
--------------------------------------------------------------------------------
/tests/f64.egg:
--------------------------------------------------------------------------------
1 | (check (= (neg 1.5) -1.5))
2 | (check (= (+ 1.5 9.2) 10.7))
3 | (check (= (/ 12.5 2.0) 6.25))
4 | (check (< 1.5 9.2))
5 | (check (>= 9.2 1.5))
6 | (check (= (^ 9.0 2.5) 243.0))
7 | (fail (check (= (^ 4.0 2.5) 31.99)))
8 | (fail (check (< 9.2 1.5)))
9 | (fail (check (= (+ 1.5 9.2) 10.6)))
10 | (check (= (to-f64 1) 1.0))
11 | (check (= (to-i64 1.0) 1))
12 | (check (= (to-string 1.2) "1.2"))
13 | (check (= (to-string 1.0) "1.0"))
14 |
--------------------------------------------------------------------------------
/tests/fail-typecheck/arity-mismatch.egg:
--------------------------------------------------------------------------------
1 | (+ 1 2 3)
2 |
--------------------------------------------------------------------------------
/tests/fail-typecheck/constructor_non_sort.egg:
--------------------------------------------------------------------------------
1 | (constructor f () i64)
--------------------------------------------------------------------------------
/tests/fail-typecheck/looking_up_nonconstructor_in_action_case_let.egg:
--------------------------------------------------------------------------------
1 | (function f () i64)
2 | (function g () i64 :merge (min old new))
3 | (datatype E)
4 | (function h () E :merge old)
5 | (rule (
6 | ) (
7 | (let x (f))
8 | ))
9 | (rule (
10 | ) (
11 | (let x (g))
12 | ))
13 | (rule (
14 | ) (
15 | (let x (h))
16 | ))
17 | (run 1)
--------------------------------------------------------------------------------
/tests/fail-typecheck/looking_up_nonconstructor_in_action_case_set.egg:
--------------------------------------------------------------------------------
1 | (datatype E)
2 | (function f () E :merge old)
3 | (rule (
4 | ) (
5 | (set (f) (f))
6 | ))
7 |
8 | (run 1)
--------------------------------------------------------------------------------
/tests/fail-typecheck/looking_up_nonconstructor_in_action_case_union.egg:
--------------------------------------------------------------------------------
1 | (function f (i64) i64)
2 | (datatype E
3 | (Sum i64 i64))
4 | (rule (
5 | (= 1 (f 2))
6 | ) (
7 | (union
8 | (Sum 3 4)
9 | (Sum 5 (+ 6 (f 7)))
10 | )
11 | ))
12 | (run 1)
--------------------------------------------------------------------------------
/tests/fail-typecheck/looking_up_nonconstructor_in_birewrite.egg:
--------------------------------------------------------------------------------
1 | (function f (i64) i64)
2 | (datatype E
3 | (Sum i64 i64))
4 | (birewrite
5 | (Sum 5 (+ 6 (f 7)))
6 | (Sum 3 4)
7 | )
8 | (run 1)
--------------------------------------------------------------------------------
/tests/fail-typecheck/looking_up_nonconstructor_in_rewrite.egg:
--------------------------------------------------------------------------------
1 | (function f (i64) i64)
2 | (datatype E
3 | (Sum i64 i64))
4 | (rewrite
5 | (Sum 3 4)
6 | (Sum 5 (+ 6 (f 7)))
7 | )
8 | (run 1)
--------------------------------------------------------------------------------
/tests/fail-typecheck/repro-containers-disallowed.egg:
--------------------------------------------------------------------------------
1 | (sort IVec (Vec i64))
2 |
3 | ; Test vec-of
4 | (fail (check (= (vec-of 1 2) (vec-push (vec-push (vec-empty) 1) 2))))
5 |
--------------------------------------------------------------------------------
/tests/fail-typecheck/repro-duplicated-var.egg:
--------------------------------------------------------------------------------
1 | (function f (i64) i64)
2 | ;; the let's y should fail checking
3 | (rule ((= x 1) (= y x) (= f1 (f 1))) ((let y f1) (set (f 0) 0)))
--------------------------------------------------------------------------------
/tests/fail-typecheck/semi_naive_set_function.egg:
--------------------------------------------------------------------------------
1 | ;; From issue#93. The change happened in right-hand-side of a rule may also impact output in semi-naive cases
2 | (push)
3 | (function f (i64) i64 :merge (max old new))
4 |
5 | (set (f 0) 0)
6 | (set (f 3) 0)
7 |
8 | (rule ((= f0 (f 0))) ((set (f 1) f0)))
9 | (rule ((= f1 (f 1))) ((set (f 2) f1)))
10 |
11 | ;; update f3 some iters later to make sure f(0) is inactive
12 | (rule ((= f2 (f 2))) ((set (f 3) 3)))
13 |
14 | (push)
15 |
16 | ;; This rule should fire and set f(0) to be 3, but because f0 is inactive,
17 | ;; it does not fire (despite that f3 is active now)
18 | (rule ((= f0 (f 0))) ((set (f 0) (f 3))))
19 |
20 | (run 100)
21 | (print-function f 100) ;; f0 is expected to have value 3, but has 0 in reality.
22 |
23 | (check (= (f 0) 3))
24 | (check (= (f 1) 3))
25 | (check (= (f 2) 3))
26 | (check (= (f 3) 3))
27 |
28 | (pop)
29 | (push)
30 |
31 | ;; variants of the last rule.
32 | (rule ((= f0 (f 0)) (= x 3) (= y x)) ((set (f 0) (f y))))
33 |
34 | (run 100)
35 | (check (= (f 0) 3))
36 | (check (= (f 1) 3))
37 | (check (= (f 2) 3))
38 | (check (= (f 3) 3))
39 |
40 | (pop)
41 | (push)
42 |
43 | ;; adding let binding
44 | (rule ((= f0 (f 0))) ((let x 3) (let y x) (set (f 0) (f y))))
45 |
46 | (run 100)
47 | (check (= (f 0) 3))
48 | (check (= (f 1) 3))
49 | (check (= (f 2) 3))
50 | (check (= (f 3) 3))
51 |
52 | (pop)
53 | (push)
54 |
55 | (function g (i64) i64 :merge (max old new))
56 | (set (g 0) 3)
57 |
58 | ;; bind to another function
59 | (rule ((= f0 (f 0))) ((let x (g 0)) (let y x) (set (f 0) (f y))))
60 |
61 | (run 100)
62 | (check (= (f 0) 3))
63 | (check (= (f 1) 3))
64 | (check (= (f 2) 3))
65 | (check (= (f 3) 3))
66 |
67 | (pop)
68 | (pop)
69 |
70 | ;; more complicated case, when the evaluation never finish
71 | ;; the semi_naive and naive behavior diverage a bit
72 | (function f (i64) i64 :merge (max old new))
73 |
74 | (set (f 0) 0)
75 | (set (f 3) 0)
76 |
77 | (rule ((= f0 (f 0))) ((set (f 1) (+ 1 f0))))
78 | (rule ((= f1 (f 1))) ((set (f 2) (+ 1 f1))))
79 |
80 | (push)
81 |
82 | (rule ((= f2 (f 2))) ((set (f 3) 1)))
83 | (rule ((= f0 (f 0))) ((set (f 0) (f (f 3)))))
84 |
85 |
86 | (run 100)
87 | (print-function f 100)
88 | (check (!= 0 (f 0)))
89 | (check (!= 0 (f 1)))
90 | (check (!= 0 (f 2)))
91 |
92 | (pop)
93 |
94 |
95 | ;; id function that will set all int values, but need strong induction.
96 | (function g (i64) i64 :merge (max old new))
97 | (set (g 0) 0)
98 | (set (g 1) 1)
99 | (rule ((= x (g x)) (= y (g (- x 1)))) ((set (g (+ x 1)) (+ y 2))))
100 |
101 | (run 100)
102 | (print-function g 100)
103 |
104 | (check (= 20 (g 20)))
--------------------------------------------------------------------------------
/tests/fail-typecheck/set-a-primitive.egg:
--------------------------------------------------------------------------------
1 | (set (+ 1 2) 3)
2 |
--------------------------------------------------------------------------------
/tests/fail-typecheck/unbound.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Add Math Math)
3 | (Sub Math Math)
4 | )
5 |
6 | (rule ((= e (Add x y))) ((Add x i)))
7 |
--------------------------------------------------------------------------------
/tests/fail-typecheck/union_non_sort.egg:
--------------------------------------------------------------------------------
1 | (function f () i64 :no-merge)
2 | (set (f) 1)
3 | (function g () i64 :no-merge)
4 | (set (g) 2)
5 | (union (f) (g))
--------------------------------------------------------------------------------
/tests/fail-typecheck/unstable-fn-wrong-args-type.egg:
--------------------------------------------------------------------------------
1 | ;; test that you can't resolve a function with the wrong type of args
2 |
3 | (datatype Math
4 | (Zero)
5 | (Inc Math))
6 |
7 | (sort Fn (UnstableFn (i64) Math))
8 | (unstable-fn "Inc")
9 |
--------------------------------------------------------------------------------
/tests/fail-typecheck/unstable-fn-wrong-args.egg:
--------------------------------------------------------------------------------
1 | ;; test that applying a function with the wrong number of args will violate the type checker
2 |
3 |
4 | (datatype Math
5 | (Inc Math))
6 |
7 | (sort Fn (UnstableFn (Math) Math))
8 | (unstable-app (unstable-fn "Inc") 10)
9 |
--------------------------------------------------------------------------------
/tests/fail-typecheck/unstable-fn-wrong-return-type.egg:
--------------------------------------------------------------------------------
1 | ;; test that you can't resolve a function with the wrong return type
2 |
3 | (datatype Math
4 | (Zero)
5 | (Inc Math))
6 |
7 | (sort Fn (UnstableFn (Math) i64))
8 | (unstable-fn "Inc")
9 |
--------------------------------------------------------------------------------
/tests/fail-typecheck/unstable-fn-wrong-return.egg:
--------------------------------------------------------------------------------
1 | ;; test that the value of a applied function is well typed
2 |
3 |
4 | (datatype Math
5 | (Zero)
6 | (Inc Math))
7 |
8 | (sort Fn (UnstableFn (Math) Math))
9 | (let x (unstable-app (unstable-fn "Inc") (Zero)))
10 |
11 | (+ x 10)
12 |
--------------------------------------------------------------------------------
/tests/fail_wrong_assertion.egg:
--------------------------------------------------------------------------------
1 | ;; This test ensure check test fails for wrong assertion
2 | (function f (i64) i64 :merge (min old new))
3 |
4 | (set (f 1) 4)
5 | (set (f 1) 5)
6 |
7 | (check (= (f 1) 4))
8 | (fail (check (= (f 1) 2)))
9 |
10 | (delete (f 1))
11 | (fail (check (= (f 1) 4)))
12 |
13 | (function g (i64 i64) i64 :merge (min old new))
14 |
15 | (set (g 1 2) 3)
16 | (set (g 2 3) 3)
17 |
18 | (check (= (g 1 2) (g 2 3)))
19 | (fail (check (!= (g 1 2) (g 2 3))))
20 | (fail (check (= (g 0 2) (g 2 3))))
21 | (check (= x (g 1 2)))
22 | (fail (check (= x (g 1 3))))
23 | (check (= x (g 1 2)) (= y (g 2 3)) (= x y))
24 | (fail (check (= x (g 0 0)) (= y (g 1 1)) (= x y)))
--------------------------------------------------------------------------------
/tests/fibonacci-demand.egg:
--------------------------------------------------------------------------------
1 | (datatype Expr
2 | (Num i64 :cost 1)
3 | (Add Expr Expr :cost 5))
4 |
5 | (constructor Fib (i64) Expr :cost 10)
6 |
7 | (rewrite (Add (Num a) (Num b)) (Num (+ a b)))
8 | (rewrite (Fib x) (Add (Fib (- x 1)) (Fib (- x 2)))
9 | :when ((> x 1)))
10 | (rewrite (Fib x) (Num x)
11 | :when ((<= x 1)))
12 |
13 | (let f7 (Fib 7))
14 | (run 1000)
15 | (print-function Fib 10)
16 | (extract f7)
17 | (check (= f7 (Num 13)))
18 |
19 |
--------------------------------------------------------------------------------
/tests/fibonacci.egg:
--------------------------------------------------------------------------------
1 | (function fib (i64) i64 :no-merge)
2 | (set (fib 0) 0)
3 | (set (fib 1) 1)
4 |
5 | (rule ((= f0 (fib x))
6 | (= f1 (fib (+ x 1))))
7 | ((set (fib (+ x 2)) (+ f0 f1))))
8 |
9 | (run 7)
10 |
11 | (check (= (fib 7) 13))
--------------------------------------------------------------------------------
/tests/files.rs:
--------------------------------------------------------------------------------
1 | use std::path::PathBuf;
2 |
3 | use egglog::*;
4 | use libtest_mimic::Trial;
5 |
6 | #[derive(Clone)]
7 | struct Run {
8 | path: PathBuf,
9 | resugar: bool,
10 | }
11 |
12 | impl Run {
13 | fn run(&self) {
14 | let _ = env_logger::builder().is_test(true).try_init();
15 | let program = std::fs::read_to_string(&self.path)
16 | .unwrap_or_else(|err| panic!("Couldn't read {:?}: {:?}", self.path, err));
17 |
18 | if !self.resugar {
19 | self.test_program(
20 | self.path.to_str().map(String::from),
21 | &program,
22 | "Top level error",
23 | );
24 | } else {
25 | let mut egraph = EGraph::default();
26 | egraph.run_mode = RunMode::ShowDesugaredEgglog;
27 | let desugared_str = egraph
28 | .parse_and_run_program(self.path.to_str().map(String::from), &program)
29 | .unwrap()
30 | .join("\n");
31 |
32 | self.test_program(
33 | None,
34 | &desugared_str,
35 | "ERROR after parse, to_string, and parse again.",
36 | );
37 | }
38 | }
39 |
40 | fn test_program(&self, filename: Option, program: &str, message: &str) {
41 | let mut egraph = EGraph::default();
42 | match egraph.parse_and_run_program(filename, program) {
43 | Ok(msgs) => {
44 | if self.should_fail() {
45 | panic!(
46 | "Program should have failed! Instead, logged:\n {}",
47 | msgs.join("\n")
48 | );
49 | } else {
50 | for msg in msgs {
51 | log::info!(" {}", msg);
52 | }
53 | // Test graphviz dot generation
54 | let mut serialized = egraph.serialize(SerializeConfig {
55 | max_functions: Some(40),
56 | max_calls_per_function: Some(40),
57 | ..Default::default()
58 | });
59 | serialized.to_dot();
60 | // Also try splitting and inlining
61 | serialized.split_classes(|id, _| egraph.from_node_id(id).is_primitive());
62 | serialized.inline_leaves();
63 | serialized.to_dot();
64 | }
65 | }
66 | Err(err) => {
67 | if !self.should_fail() {
68 | panic!("{}: {err}", message)
69 | }
70 | }
71 | };
72 | }
73 |
74 | fn into_trial(self) -> Trial {
75 | let name = self.name().to_string();
76 | Trial::test(name, move || {
77 | self.run();
78 | Ok(())
79 | })
80 | }
81 |
82 | fn name(&self) -> impl std::fmt::Display + '_ {
83 | struct Wrapper<'a>(&'a Run);
84 | impl std::fmt::Display for Wrapper<'_> {
85 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 | let stem = self.0.path.file_stem().unwrap();
87 | let stem_str = stem.to_string_lossy().replace(['.', '-', ' '], "_");
88 | write!(f, "{stem_str}")?;
89 | if self.0.resugar {
90 | write!(f, "_resugar")?;
91 | }
92 | Ok(())
93 | }
94 | }
95 | Wrapper(self)
96 | }
97 |
98 | fn should_fail(&self) -> bool {
99 | self.path.to_string_lossy().contains("fail-typecheck")
100 | }
101 | }
102 |
103 | fn generate_tests(glob: &str) -> Vec {
104 | let mut trials = vec![];
105 | let mut push_trial = |run: Run| trials.push(run.into_trial());
106 |
107 | for entry in glob::glob(glob).unwrap() {
108 | let run = Run {
109 | path: entry.unwrap().clone(),
110 | resugar: false,
111 | };
112 | let should_fail = run.should_fail();
113 |
114 | push_trial(run.clone());
115 | if !should_fail {
116 | push_trial(Run {
117 | resugar: true,
118 | ..run.clone()
119 | });
120 | }
121 | }
122 |
123 | trials
124 | }
125 |
126 | fn main() {
127 | let args = libtest_mimic::Arguments::from_args();
128 | let tests = generate_tests("tests/**/*.egg");
129 | libtest_mimic::run(&args, tests).exit();
130 | }
131 |
--------------------------------------------------------------------------------
/tests/fusion.egg:
--------------------------------------------------------------------------------
1 | (datatype Var)
2 | (datatype Term
3 | (App Term Term)
4 | (Lam Var Term)
5 | (TVar Var)
6 | (Let Var Term Term)
7 | (Add Term Term)
8 | (Num i64)
9 | (CaseSplit Term Term Term)
10 | (Cons Term Term))
11 | (constructor NilConst () Term)
12 | (let Nil (NilConst))
13 |
14 | (constructor V (String) Var)
15 | (constructor From (Term) Var)
16 |
17 | ;; ==== FV ====
18 | (sort StringSet (Set Var))
19 | (function freer (Term) StringSet :merge (set-intersect old new))
20 | (rule ((= e (App e1 e2))
21 | (= (freer e1) fv1)
22 | (= (freer e2) fv2))
23 | ((set (freer e) (set-union fv1 fv2))))
24 | (rule ((= e (Lam var body))
25 | (= (freer body) fv))
26 | ((set (freer e) (set-remove fv var))))
27 | (rule ((= e (TVar v)))
28 | ((set (freer e) (set-insert (set-empty) v))))
29 | (rule ((= e (Let var e1 e2))
30 | (= (freer e1) fv1)
31 | (= (freer e2) fv2))
32 | ((set (freer e) (set-union fv1 (set-remove fv2 var)))))
33 | (rule ((= e (Add e1 e2))
34 | (= (freer e1) fv1)
35 | (= (freer e2) fv2))
36 | ((set (freer e) (set-union fv1 fv2))))
37 | (rule ((= e (Num v)))
38 | ((set (freer e) (set-empty))))
39 | (rule ((= e (CaseSplit e1 e2 e3))
40 | (= (freer e1) fv1)
41 | (= (freer e2) fv2)
42 | (= (freer e3) fv3))
43 | ((set (freer e) (set-union (set-union fv1 fv2) fv3))))
44 | (rule ((= e (Cons e1 e2))
45 | (= (freer e1) fv1)
46 | (= (freer e2) fv2))
47 | ((set (freer e) (set-union fv1 fv2))))
48 | (rule ((= e Nil))
49 | ((set (freer e) (set-empty))))
50 |
51 | ;; ==== eval ====
52 | ; beta
53 | (rewrite (App (Lam v body) e) (Let v e body))
54 | ; case-split-nil
55 | (rewrite (CaseSplit Nil e1 e2) e1)
56 | ; case-split-cons
57 | (rewrite (CaseSplit (Cons x xs) e1 e2) (App (App e2 x) xs))
58 |
59 | ; let-num
60 | (rewrite (Let v e (Num n)) (Num n))
61 | ; let-nil
62 | (rewrite (Let v e Nil) Nil)
63 | ; let-var-same
64 | (rewrite (Let v1 e (TVar v1)) e)
65 | ; let-var-diff
66 | (rewrite (Let v1 e (TVar v2)) (TVar v2) :when ((!= v1 v2)))
67 |
68 | ; let-lam-close
69 | (rewrite (Let v1 e expr) expr :when ((set-not-contains (freer expr) v1)))
70 | ; let-app
71 | (rewrite (Let v e expr) (App (Let v e a) (Let v e b)) :when ((= expr (App a b)) (set-contains (freer expr) v)))
72 | ; let-add
73 | (rewrite (Let v e expr) (Add (Let v e a) (Let v e b)) :when ((= expr (Add a b)) (set-contains (freer expr) v)))
74 | ; let-cons
75 | (rewrite (Let v e expr) (Cons (Let v e x) (Let v e xs)) :when ((= expr (Cons x xs)) (set-contains (freer expr) v)))
76 | ; let-case-split
77 | (rewrite (Let v e expr)
78 | (CaseSplit (Let v e e1) (Let v e e2) (Let v e e3))
79 | :when ((= expr (CaseSplit e1 e2 e3))
80 | (set-contains (freer expr) v)))
81 | ; let-lam-same
82 | (rewrite (Let v1 e (Lam v1 body)) (Lam v1 body))
83 | ; let-lam-diff
84 | (rewrite (Let v1 e (Lam v2 body)) (Lam v2 (Let v1 e body))
85 | :when ((set-contains (freer body) v1)
86 | (!= v1 v2)
87 | (= fvs (freer e))
88 | (set-not-contains fvs v2)))
89 | (rule ((= expr (Let v1 e (Lam v2 body)))
90 | (set-contains (freer body) v1)
91 | (!= v1 v2)
92 | (= fvs (freer e))
93 | (set-contains fvs v2))
94 | ((union expr (Lam (From expr) (Let v1 e (Let v2 (TVar (From expr)) body))))))
95 |
96 | (constructor pushdown (Term Term) Term :cost 10000)
97 | (rewrite (App f (App (Lam x e) e2))
98 | (App (Lam x (pushdown f e)) e2))
99 |
100 | (rewrite (pushdown f (CaseSplit e e1 (Lam x (Lam xs e2))))
101 | (CaseSplit e (App f e1) (Lam x (Lam xs (App f e2)))))
102 |
103 | (relation is-tail (Term))
104 | (rule ((= demand (pushdown f e)) (= e (App e1 e2))) ((is-tail e)))
105 | (rule ((= demand (pushdown f e)) (= e (Lam x e))) ((is-tail e)))
106 | (rule ((= demand (pushdown f e)) (= e (TVar x))) ((is-tail e)))
107 | (rule ((= demand (pushdown f e)) (= e (Cons e1 e2))) ((is-tail e)))
108 | (rule ((= demand (pushdown f e)) (= e Nil)) ((is-tail e)))
109 | (rule ((= demand (pushdown f e)) (= e (Add e1 e2))) ((is-tail e)))
110 | (rule ((= demand (pushdown f e)) (= e (Num n1))) ((is-tail e)))
111 | (rewrite (pushdown f e) (App f e) :when ((is-tail e)))
112 |
113 | ;; ==== definition ====
114 |
115 | (constructor sum () Term :cost 1000)
116 | (constructor mapf () Term :cost 1000)
117 | (constructor sum-o-mapf () Term)
118 | (rewrite (App (sum) (App (mapf) x)) (App (sum-o-mapf) x))
119 | (union (sum) (Lam (V "xs")
120 | (CaseSplit (TVar (V "xs"))
121 | (Num 0)
122 | (Lam (V "x") (Lam (V "xs'")
123 | (Add (TVar (V "x")) (App (sum) (TVar (V "xs'")))))))))
124 |
125 | (union (mapf) (Lam (V "xs")
126 | (CaseSplit (TVar (V "xs"))
127 | Nil
128 | (Lam (V "x") (Lam (V "xs'")
129 | (Cons (Add (TVar (V "x")) (Num 1))
130 | (App (mapf) (TVar (V "xs'")))))))))
131 |
132 | (set (freer (sum)) (set-empty))
133 | (set (freer (mapf)) (set-empty))
134 |
135 | (let expr (App (sum) (App (mapf) (TVar (V "expr")))))
136 |
137 | (run 100)
138 |
139 | (query-extract (freer expr))
140 |
141 |
142 | (let my-output
143 | (CaseSplit (TVar (V "expr")) (Num 0)
144 | (Lam (V "x") (Lam (V "xs'")
145 | (Add (Add (TVar (V "x")) (Num 1))
146 | (App (sum-o-mapf) (TVar (V "xs'"))))))))
147 |
148 | (check (= (App (sum-o-mapf) (TVar (V "expr")))
149 | (CaseSplit (TVar (V "expr")) (Num 0)
150 | (Lam (V "x") (Lam (V "xs'")
151 | (Add (Add (TVar (V "x")) (Num 1))
152 | (App (sum-o-mapf) (TVar (V "xs'")))))))))
153 |
--------------------------------------------------------------------------------
/tests/herbie-tutorial.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Num BigRat)
3 | (Var String)
4 | (Add Math Math)
5 | (Div Math Math)
6 | (Mul Math Math))
7 |
8 | (let zero (Num (bigrat (bigint 0) (bigint 1))))
9 | (let one (Num (bigrat (bigint 1) (bigint 1))))
10 | (let two (Num (bigrat (bigint 2) (bigint 1))))
11 |
12 | (rewrite (Add a b) (Add b a))
13 | (rewrite (Add a zero) a)
14 | (rewrite (Add (Num r1) (Num r2))
15 | (Num (+ r1 r2)))
16 |
17 | (let one-two (Add one two))
18 |
19 | (push)
20 | (run 1)
21 | ;; yay, constant folding works
22 | (check (= one-two (Num (bigrat (bigint 3) (bigint 1)))))
23 | ;; also, commutativity works
24 | (check (= (Add two one) one-two))
25 | (pop)
26 |
27 | (push)
28 | ;; rule is like rewrite, but more general
29 | ;; the following rule doesn't union (Num r) with the result:
30 | (rule ((Num r))
31 | ((union one (Div (Num r) (Num r)))))
32 | ;; uh oh, division by zero!
33 | (run 1)
34 |
35 | (pop)
36 |
37 | ;; we need to detect when things are non-zero
38 | (function lower-bound (Math) BigRat :merge (max old new))
39 | (function upper-bound (Math) BigRat :merge (min old new))
40 |
41 | (rule ((Num r))
42 | ((set (lower-bound (Num r)) r)
43 | (set (upper-bound (Num r)) r)))
44 | (rule ((= e (Add a b)) (= x (lower-bound a)) (= y (lower-bound b)))
45 | ((set (lower-bound e) (+ x y))))
46 | (rule ((= e (Add a b)) (= x (upper-bound a)) (= y (upper-bound b)))
47 | ((set (upper-bound e) (+ x y))))
48 | (rule ((= e (Mul a b))
49 | (= lba (lower-bound a))
50 | (= lbb (lower-bound b))
51 | (= uba (upper-bound a))
52 | (= ubb (upper-bound b))
53 | )
54 | ((set (lower-bound e)
55 | (min (* lba lbb)
56 | (min (* lba ubb)
57 | (min (* uba lbb)
58 | (* uba ubb)))))
59 | (set (upper-bound e)
60 | (min (* lba lbb)
61 | (min (* lba ubb)
62 | (min (* uba lbb)
63 | (* uba ubb)))))))
64 |
65 | (rule ((= e (Add a b))
66 | (> (lower-bound e) (bigrat (bigint 0) (bigint 1))))
67 | ((union one (Div (Add a b) (Add a b)))))
68 |
69 | (let x (Var "x"))
70 | (let x1 (Add x one))
71 |
72 | (push)
73 | (set (lower-bound x) (bigrat (bigint 0) (bigint 1)))
74 | (set (upper-bound x) (bigrat (bigint 1) (bigint 1)))
75 |
76 | (run 3)
77 |
78 | (query-extract (lower-bound x1))
79 | (query-extract (upper-bound x1))
80 | (check (= one (Div x1 x1)))
81 |
82 | (pop)
83 |
84 |
85 | ;; Set the variable x to a particular input value 200/201
86 | (set (lower-bound x) (bigrat (bigint 200) (bigint 201)))
87 | (set (upper-bound x) (bigrat (bigint 200) (bigint 201)))
88 |
89 | (run 3)
90 |
91 | (query-extract (lower-bound x1))
92 | (query-extract (upper-bound x1))
93 |
94 | (function true-value (Math) f64 :no-merge)
95 |
96 | (rule ((= (to-f64 (lower-bound e))
97 | (to-f64 (upper-bound e)))
98 | (= lbe (lower-bound e))
99 | )
100 | ((set (true-value e)
101 | (to-f64 lbe))))
102 |
103 | (run 1)
104 | (query-extract (true-value x1))
105 |
106 | (function best-error (Math) f64 :merge new)
107 |
108 | (rule ((Num n))
109 | ((set (best-error (Num n)) (to-f64 n))))
110 | (rule ((Add a b)) ((set (best-error (Add a b)) (to-f64 (bigrat (bigint 10000) (bigint 1))))))
111 |
112 | ;; finally, the mega rule for finding more accurate programs
113 | (rule ((= expr (Add a b))
114 | (= (best-error a) va)
115 | (= (best-error b) vb)
116 | (= true-v (true-value (Add a b)))
117 | (= computed (+ va vb))
118 | (< (abs (- computed true-v))
119 | (best-error (Add a b))))
120 | ((set (best-error (Add a b)) computed)))
121 |
122 |
123 |
124 | (push)
125 |
126 | (let target
127 | (Add
128 | (Add (Num (bigrat (bigint 1) (bigint 100))) (Num (bigrat (bigint 1) (bigint 100))))
129 | (Num (bigrat (bigint -2) (bigint 100)))))
130 |
131 | (run 1)
132 |
133 | ;; set a default
134 | (set (best-error target) (to-f64 (bigrat (bigint 10000) (bigint 1))))
135 | ;; error is bad, constant folding hasn't fired enough
136 | (query-extract (best-error target))
137 |
138 | (run 1)
139 |
140 | ;; error is good, constant folding has fired enough
141 | (query-extract (best-error target))
142 |
143 |
144 | (pop)
--------------------------------------------------------------------------------
/tests/i64.egg:
--------------------------------------------------------------------------------
1 | (check (= (to-string 20) "20"))
2 |
--------------------------------------------------------------------------------
/tests/include.egg:
--------------------------------------------------------------------------------
1 | (include "tests/path.egg")
2 | (check (path 1 3))
3 |
--------------------------------------------------------------------------------
/tests/integer_math.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Diff Math Math)
3 | (Integral Math Math)
4 |
5 | (Add Math Math)
6 | (Sub Math Math)
7 | (Mul Math Math)
8 | (Div Math Math)
9 | (Pow Math Math)
10 | (RShift Math Math)
11 | (LShift Math Math)
12 | (Mod Math Math)
13 | (Not Math)
14 |
15 | (Const i64)
16 | (Var String))
17 |
18 | (relation MathU (Math))
19 | (rule ((= e (Diff x y))) ((MathU e)))
20 | (rule ((= e (Integral x y))) ((MathU e)))
21 | (rule ((= e (Add x y))) ((MathU e)))
22 | (rule ((= e (Sub x y))) ((MathU e)))
23 | (rule ((= e (Mul x y))) ((MathU e)))
24 | (rule ((= e (Div x y))) ((MathU e)))
25 | (rule ((= e (Pow x y))) ((MathU e)))
26 | (rule ((= e (Const x))) ((MathU e)))
27 | (rule ((= e (Var x))) ((MathU e)))
28 | (rule ((= e (RShift x y))) ((MathU e)))
29 | (rule ((= e (LShift x y))) ((MathU e)))
30 | (rule ((= e (Mod x y))) ((MathU e)))
31 | (rule ((= e (Not x))) ((MathU e)))
32 |
33 | (relation evals-to (Math i64))
34 | (rule ((evals-to x vx)) ((union x (Const vx))))
35 | (rule ((= e (Const c))) ((evals-to e c)))
36 |
37 | (relation is-not-zero (Math))
38 | (rule ((MathU a) (!= a (Const 0))) ((is-not-zero a)))
39 |
40 | ;; Evaluation
41 | (rewrite (Add (Const a) (Const b))
42 | (Const (+ a b)))
43 | (rewrite (Sub (Const a) (Const b))
44 | (Const (- a b)))
45 | (rewrite (Mul (Const a) (Const b)) (Const (* a b)))
46 | (rewrite (Div (Const a) (Const b)) (Const (/ a b)) :when ((!= 0 b)))
47 | (rewrite (RShift (Const a) (Const b)) (Const (>> a b)))
48 | (rewrite (LShift (Const a) (Const b)) (Const (<< a b)))
49 | (rewrite (Not (Const a)) (Const (not-i64 a)))
50 |
51 | ;; Properties
52 | (rewrite (Add a b) (Add b a))
53 | (rewrite (Mul a b) (Mul b a))
54 | (rewrite (Add a (Add b c)) (Add (Add a b) c))
55 | (rewrite (Mul a (Mul b c)) (Mul (Mul a b) c))
56 |
57 | (rewrite (Sub a b) (Add a (Mul (Const -1) b)))
58 |
59 | (rewrite (Add a (Const 0)) a)
60 | (rewrite (Mul a (Const 0)) (Const 0))
61 | (rewrite (Mul a (Const 1)) a)
62 |
63 | (rule ((MathU a) (!= a (Const 0))) ((union a (Add a (Const 0)))))
64 | (rule ((MathU a) (!= a (Const 1))) ((union a (Mul a (Const 1)))))
65 |
66 | (rewrite (Sub a a) (Const 0))
67 | (rewrite (Div a a) (Const 1) :when ((is-not-zero a)))
68 |
69 | (rewrite (Mul a (Add b c)) (Add (Mul a b) (Mul a c)))
70 | (rewrite (Add (Mul a b) (Mul a c)) (Mul a (Add b c)))
71 |
72 | ; This rule doesn't work when pow is negative - consider 2^-1 * 2^1, which is 0, but 2^0 = 1
73 | (rewrite (Mul (Pow a b) (Pow a c)) (Pow a (Add b c)) :when ((is-not-zero b) (is-not-zero c)))
74 |
75 | (rewrite (Pow x (Const 0)) (Const 1) :when ((is-not-zero x)))
76 | (rewrite (Pow x (Const 1 )) x)
77 | (rewrite (Pow x (Const 2)) (Mul x x))
78 |
79 | (rewrite (Pow x (Const -1)) (Div (Const 1) x) :when ((is-not-zero x)))
80 |
81 | (rewrite (Mul x (Pow (Const 2) y)) (LShift x y))
82 | (rewrite (Div x (Pow (Const 2) y)) (RShift x y))
83 |
84 | (rewrite (Not (Not x)) x)
85 |
86 |
87 | ;; Tests
88 | (let start-expr (Div (
89 | Mul (Var "a") (Pow (Const 2) (Const 3))
90 | ) (
91 | Add (Var "c") (
92 | Sub (Mul (Var "b") (Const 2)) (Mul (Var "b") (Const 2))
93 | )
94 | )))
95 |
96 | (let equiv-expr (Div (
97 | LShift (Var "a") (Const 3)
98 | ) (
99 | Mul (Var "c") (Not (Not (Const 1)))
100 | )
101 | ))
102 |
103 | (run 4)
104 |
105 | (check (= start-expr equiv-expr))
106 |
107 |
--------------------------------------------------------------------------------
/tests/intersection.egg:
--------------------------------------------------------------------------------
1 | ;; computes "e-graph intersection"
2 |
3 | (datatype Expr
4 | (Var String)
5 | (f Expr))
6 |
7 | (constructor intersect (Expr Expr) Expr)
8 |
9 | (rule (
10 | (= x3 (intersect x1 x2))
11 | (= f1 (f x1))
12 | (= f2 (f x2))
13 | )(
14 | (union (intersect f1 f2) (f x3))
15 | ))
16 |
17 | (let a1 (Var "a1")) (let a2 (Var "a2")) (let a3 (Var "a3"))
18 | (let b1 (Var "b1")) (let b2 (Var "b2")) (let b3 (Var "b3"))
19 |
20 | ;; e-graph 1: f(a) = f(b), f(f(a))
21 | (let t1 (f (f a1)))
22 | (let fb1 (f b1))
23 | (union (f a1) fb1)
24 |
25 | ;; e-graph 2: f(f(a)) = f(f(b))
26 | (let t2 (f (f a2)))
27 | (let t2p (f (f b2)))
28 | (union t2 t2p)
29 |
30 | (union (intersect a1 a2) a3)
31 | (union (intersect b1 b2) b3)
32 |
33 | (run 100)
34 |
35 | (let t3 (f (f a3)))
36 | (query-extract :variants 5 t3)
37 |
38 | ;; f(f(a)) = f(f(b)) is preserved
39 | (check (= (f (f a3)) (f (f b3))))
40 | ;; but not f(a) = f(b), it was only in e-graph 1
41 | (check (!= (f a3) (f b3)))
--------------------------------------------------------------------------------
/tests/interval.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Num i64)
3 | (Var String)
4 | (Mul Math Math))
5 |
6 | (function hi (Math) i64 :merge (min old new))
7 | (function lo (Math) i64 :merge (max old new))
8 |
9 | (rule ((= mul (Mul a b))
10 | (= loa (lo a))
11 | (= lob (lo b))
12 | (= hia (hi a))
13 | (= hib (hi b))
14 | )
15 | ((set (lo mul)
16 | (min (min (* loa lob) (* loa hib))
17 | (min (* hia lob) (* hia hib))))))
18 |
19 | (let x (Var "x"))
20 | (let e (Mul x x))
21 |
22 | (set (lo x) -10)
23 | (set (hi x) 10)
24 |
25 | (run 1)
26 |
27 | (check (= (lo e) -100))
28 |
29 | (rule ((= mul (Mul a a))
30 | (= loa (lo a))
31 | )
32 | ((set (lo mul) (* loa loa))))
33 |
34 | (run 1)
35 | (check (= (lo e) 100))
36 |
37 | ;; testing extraction of rationals
38 | (query-extract (lo e))
39 |
--------------------------------------------------------------------------------
/tests/knapsack.egg:
--------------------------------------------------------------------------------
1 | (datatype expr
2 | (Num i64)
3 | (Add expr expr)
4 | (Max expr expr))
5 | (rewrite (Add (Num a) (Num b)) (Num (+ a b)))
6 | (rewrite (Max (Num a) (Num b)) (Num (max a b)))
7 |
8 | ; List of (weight, value) pairs
9 | (datatype objects
10 | (Cons i64 i64 objects))
11 | (constructor NilConst () objects)
12 | (let Nil (NilConst))
13 |
14 | ; Given a capacity and a list of objects, finds the maximum value of a
15 | ; collection of objects whose total weight does not exceed the capacity.
16 | (constructor Knap (i64 objects) expr)
17 |
18 | (rule ((= f (Knap capacity (Cons weight val rest))) (<= weight capacity))
19 | ((union (Knap capacity (Cons weight val rest))
20 | (Max
21 | (Add (Num val) (Knap (- capacity weight) rest))
22 | (Knap capacity rest)))))
23 |
24 | (rule ((= f (Knap capacity (Cons weight val rest))) (> weight capacity))
25 | ((union (Knap capacity (Cons weight val rest))
26 | (Knap capacity rest))))
27 |
28 | (rule ((= f (Knap capacity Nil)))
29 | ((union (Knap capacity Nil) (Num 0))))
30 |
31 | (let test1 (Knap 13 (Cons 5 5 (Cons 3 3 (Cons 12 12 (Cons 5 5 Nil))))))
32 |
33 | (let test2 (Knap 5 (Cons 6 6 Nil)))
34 |
35 | (let test3 (Knap 5 (Cons 1 1 (Cons 1 1 (Cons 1 1 Nil)))))
36 |
37 | (let test4 (Knap 15 (Cons 12 40 (Cons 2 20 (Cons 1 20 (Cons 1 10 (Cons 4 100 Nil)))))))
38 |
39 | ; turn a (Num n) into n
40 | (function Unwrap (expr) i64 :no-merge)
41 | (rule ((= x (Num n))) ((set (Unwrap (Num n)) n)))
42 |
43 | (run 100)
44 |
45 | (check (= test1 (Num 13)))
46 |
47 |
--------------------------------------------------------------------------------
/tests/levenshtein-distance.egg:
--------------------------------------------------------------------------------
1 | ; Datatypes
2 |
3 | (datatype expr
4 | (Num i64)
5 | (Add expr expr)
6 | (Min expr expr expr))
7 | (rewrite (Add (Num a) (Num b)) (Num (+ a b)))
8 | (rewrite (Min (Num a) (Num b) (Num c)) (Num (min (min a b) c)))
9 |
10 | ; `String` supports limited operations, let's just use it as a char type
11 | (datatype str
12 | (Cons String str))
13 | (constructor EmptyConst () str)
14 | (let Empty (EmptyConst))
15 |
16 | ; Length function
17 |
18 | (constructor Length (str) expr)
19 |
20 | (rule ((= f (Length Empty)))
21 | ((union (Length Empty) (Num 0))))
22 |
23 | (rule ((= f (Length (Cons c cs))))
24 | ((union (Length (Cons c cs)) (Add (Num 1) (Length cs)))))
25 |
26 | ; EditDist function
27 |
28 | (constructor EditDist (str str) expr)
29 |
30 | (rule ((= f (EditDist Empty s)))
31 | ((union (EditDist Empty s) (Length s))))
32 |
33 | (rule ((= f (EditDist s Empty)))
34 | ((union (EditDist s Empty) (Length s))))
35 |
36 | (rule ((= f (EditDist (Cons head rest1) (Cons head rest2))))
37 | ((union (EditDist (Cons head rest1) (Cons head rest2))
38 | (EditDist rest1 rest2))))
39 |
40 | (rule ((= f (EditDist (Cons head1 rest1) (Cons head2 rest2))) (!= head1 head2))
41 | ((union (EditDist (Cons head1 rest1) (Cons head2 rest2))
42 | (Add (Num 1)
43 | (Min (EditDist rest1 rest2)
44 | (EditDist (Cons head1 rest1) rest2)
45 | (EditDist rest1 (Cons head2 rest2)))))))
46 |
47 | ; Unwrap function - turn a (Num n) into n
48 |
49 | (function Unwrap (expr) i64 :no-merge)
50 | (rule ((= x (Num n))) ((set (Unwrap (Num n)) n)))
51 |
52 | ; Tests
53 | (let HorseStr (Cons "h" (Cons "o" (Cons "r" (Cons "s" (Cons "e" Empty))))))
54 | (let RosStr (Cons "r" (Cons "o" (Cons "s" Empty))))
55 | (let IntentionStr (Cons "i" (Cons "n" (Cons "t" (Cons "e" (Cons "n" (Cons "t" (Cons "i" (Cons "o" (Cons "n" Empty))))))))))
56 | (let ExecutionStr (Cons "e" (Cons "x" (Cons "e" (Cons "c" (Cons "u" (Cons "t" (Cons "i" (Cons "o" (Cons "n" Empty))))))))))
57 |
58 | (let Test1 (EditDist HorseStr RosStr))
59 | (let Test2 (EditDist IntentionStr ExecutionStr))
60 | (let Test3 (EditDist HorseStr Empty))
61 |
62 | (run 100)
63 |
64 | (extract (Unwrap Test1))
65 | (check (= Test1 (Num 3)))
66 |
67 | (extract (Unwrap Test2))
68 | (check (= Test2 (Num 5)))
69 |
70 | (extract (Unwrap Test3))
71 | (check (= Test3 (Num 5)))
--------------------------------------------------------------------------------
/tests/list.egg:
--------------------------------------------------------------------------------
1 | (datatype List
2 | (Nil)
3 | (Cons i64 List))
4 |
5 | (ruleset list-ruleset)
6 |
7 | (function list-length (List) i64 :no-merge)
8 | (relation list-length-demand (List))
9 | (rule
10 | ((list-length-demand (Nil)))
11 | ((set (list-length (Nil)) 0))
12 | :ruleset list-ruleset)
13 | (rule
14 | ((list-length-demand (Cons head tail)))
15 | ((list-length-demand tail))
16 | :ruleset list-ruleset)
17 | (rule
18 | ( (list-length-demand (Cons head tail))
19 | (= (list-length tail) tail-length))
20 | ((set (list-length (Cons head tail)) (+ tail-length 1)))
21 | :ruleset list-ruleset)
22 |
23 | (function list-get (List i64) i64 :no-merge)
24 | (relation list-get-demand (List i64))
25 | (rule
26 | ( (list-get-demand list 0)
27 | (= list (Cons head tail)))
28 | ((set (list-get list 0) head))
29 | :ruleset list-ruleset)
30 | (rule
31 | ( (list-get-demand list n) (> n 0)
32 | (= list (Cons head tail)))
33 | ((list-get-demand tail (- n 1)))
34 | :ruleset list-ruleset)
35 | (rule
36 | ( (list-get-demand list n)
37 | (= list (Cons head tail))
38 | (= item (list-get tail (- n 1))))
39 | ((set (list-get list n) item))
40 | :ruleset list-ruleset)
41 |
42 | (constructor list-append (List List) List)
43 | (rewrite (list-append (Nil) list) list :ruleset list-ruleset)
44 | (rewrite (list-append (Cons head tail) list) (Cons head (list-append tail list)) :ruleset list-ruleset)
45 |
46 | ; list-contains Nil _ => false
47 | ; list-contains (Cons item tail) item => true
48 | ; list-contains (Cons head tail) item => assert(head != item); (list-contains tail item)
49 | ; list-contains needs inequality
50 |
51 | (constructor list-set (List i64 i64) List)
52 | (rewrite (list-set (Cons head tail) 0 item) (Cons item tail) :ruleset list-ruleset)
53 | (rewrite (list-set (Cons head tail) i item) (Cons head (list-set tail (- i 1) item)) :when ((> i 0)) :ruleset list-ruleset)
54 |
55 | ; Tests
56 | (let a (Cons 1 (Cons 2 (Nil))))
57 | (let b (Cons 3 (Nil)))
58 | (let c (Cons 1 (Cons 2 (Cons 3 (Nil)))))
59 | (let d (Cons 1 (Cons 4 (Nil))))
60 | (let e (list-append a b))
61 | (let f (list-set a 1 4))
62 |
63 | (list-length-demand c)
64 | (list-get-demand b 0)
65 | (list-get-demand a 1)
66 |
67 | (run-schedule (saturate (run list-ruleset)))
68 |
69 | (check (= e c))
70 | (check (= (list-length c) 3))
71 | (check (= (list-get b 0) 3))
72 | (check (= (list-get a 1) 2))
73 | (check (= f d))
74 |
--------------------------------------------------------------------------------
/tests/looking_up_global.egg:
--------------------------------------------------------------------------------
1 | (function f () i64 :no-merge)
2 | (set (f) 0)
3 | (let x (f))
4 |
5 | (function g () i64 :no-merge)
6 | (fail (let y (g)))
--------------------------------------------------------------------------------
/tests/looking_up_nonconstructor_in_rewrite_good.egg:
--------------------------------------------------------------------------------
1 | (function f (i64) i64 :no-merge)
2 | (datatype E
3 | (Sum i64 i64))
4 | (rewrite
5 | (Sum 5 (+ 6 (f 7)))
6 | (Sum 3 4)
7 | )
8 | (run 1)
--------------------------------------------------------------------------------
/tests/map.egg:
--------------------------------------------------------------------------------
1 | (sort MyMap (Map i64 String))
2 |
3 | (let my_map1 (map-insert (map-empty) 1 "one"))
4 | (let my_map2 (map-insert my_map1 2 "two"))
5 |
6 | (check (= "one" (map-get my_map1 1)))
7 | (query-extract my_map2)
--------------------------------------------------------------------------------
/tests/math-microbenchmark.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Diff Math Math)
3 | (Integral Math Math)
4 |
5 | (Add Math Math)
6 | (Sub Math Math)
7 | (Mul Math Math)
8 | (Div Math Math)
9 | (Pow Math Math)
10 | (Ln Math)
11 | (Sqrt Math)
12 |
13 | (Sin Math)
14 | (Cos Math)
15 |
16 | (Const i64)
17 | (Var String))
18 |
19 | (rewrite (Add a b) (Add b a))
20 | (rewrite (Mul a b) (Mul b a))
21 | (rewrite (Add a (Add b c)) (Add (Add a b) c))
22 | (rewrite (Mul a (Mul b c)) (Mul (Mul a b) c))
23 |
24 | (rewrite (Sub a b) (Add a (Mul (Const -1) b)))
25 | ;; (rewrite (Div a b) (Mul a (Pow b (Const -1))) :when ((is-not-zero b)))
26 |
27 | (rewrite (Add a (Const 0)) a)
28 | (rewrite (Mul a (Const 0)) (Const 0))
29 | (rewrite (Mul a (Const 1)) a)
30 |
31 | (rewrite (Sub a a) (Const 0))
32 |
33 | (rewrite (Mul a (Add b c)) (Add (Mul a b) (Mul a c)))
34 | (rewrite (Add (Mul a b) (Mul a c)) (Mul a (Add b c)))
35 |
36 | (rewrite (Mul (Pow a b) (Pow a c)) (Pow a (Add b c)))
37 | (rewrite (Pow x (Const 1)) x)
38 | (rewrite (Pow x (Const 2)) (Mul x x))
39 |
40 | (rewrite (Diff x (Add a b)) (Add (Diff x a) (Diff x b)))
41 | (rewrite (Diff x (Mul a b)) (Add (Mul a (Diff x b)) (Mul b (Diff x a))))
42 |
43 | (rewrite (Diff x (Sin x)) (Cos x))
44 | (rewrite (Diff x (Cos x)) (Mul (Const -1) (Sin x)))
45 |
46 | (rewrite (Integral (Const 1) x) x)
47 | (rewrite (Integral (Cos x) x) (Sin x))
48 | (rewrite (Integral (Sin x) x) (Mul (Const -1) (Cos x)))
49 | (rewrite (Integral (Add f g) x) (Add (Integral f x) (Integral g x)))
50 | (rewrite (Integral (Sub f g) x) (Sub (Integral f x) (Integral g x)))
51 | (rewrite (Integral (Mul a b) x)
52 | (Sub (Mul a (Integral b x))
53 | (Integral (Mul (Diff x a) (Integral b x)) x)))
54 | (Integral (Ln (Var "x")) (Var "x"))
55 | (Integral (Add (Var "x") (Cos (Var "x"))) (Var "x"))
56 | (Integral (Mul (Cos (Var "x")) (Var "x")) (Var "x"))
57 | (Diff (Var "x") (Add (Const 1) (Mul (Const 2) (Var "x"))))
58 | (Diff (Var "x") (Sub (Pow (Var "x") (Const 3)) (Mul (Const 7) (Pow (Var "x") (Const 2)))))
59 | (Add (Mul (Var "y") (Add (Var "x") (Var "y"))) (Sub (Add (Var "x") (Const 2)) (Add (Var "x") (Var "x"))))
60 | (Div (Const 1)
61 | (Sub (Div (Add (Const 1)
62 | (Sqrt (Var "five")))
63 | (Const 2))
64 | (Div (Sub (Const 1)
65 | (Sqrt (Var "five")))
66 | (Const 2))))
67 | (run 11)
68 | (print-size Add)
69 | (print-size Mul)
70 |
71 | (print-size)
72 |
73 | (print-stats)
--------------------------------------------------------------------------------
/tests/math.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Diff Math Math)
3 | (Integral Math Math)
4 |
5 | (Add Math Math)
6 | (Sub Math Math)
7 | (Mul Math Math)
8 | (Div Math Math)
9 | (Pow Math Math)
10 | (Ln Math)
11 | (Sqrt Math)
12 |
13 | (Sin Math)
14 | (Cos Math)
15 |
16 | (Const i64)
17 | (Var String))
18 |
19 | (relation MathU (Math))
20 | (rule ((= e (Diff x y))) ((MathU e)))
21 | (rule ((= e (Integral x y))) ((MathU e)))
22 | (rule ((= e (Add x y))) ((MathU e)))
23 | (rule ((= e (Sub x y))) ((MathU e)))
24 | (rule ((= e (Mul x y))) ((MathU e)))
25 | (rule ((= e (Div x y))) ((MathU e)))
26 | (rule ((= e (Pow x y))) ((MathU e)))
27 | (rule ((= e (Ln x))) ((MathU e)))
28 | (rule ((= e (Sqrt x))) ((MathU e)))
29 | (rule ((= e (Sin x))) ((MathU e)))
30 | (rule ((= e (Cos x))) ((MathU e)))
31 | (rule ((= e (Const x))) ((MathU e)))
32 | (rule ((= e (Var x))) ((MathU e)))
33 |
34 | (relation evals-to (Math i64))
35 |
36 | (rule ((= e (Const c))) ((evals-to e c)))
37 | (rule ((= e (Add a b)) (evals-to a va) (evals-to b vb))
38 | ((evals-to e (+ va vb))))
39 | (rule ((= e (Sub a b)) (evals-to a va) (evals-to b vb))
40 | ((evals-to e (- va vb))))
41 | (rule ((= e (Mul a b)) (evals-to a va) (evals-to b vb))
42 | ((evals-to e (* va vb))))
43 | (rule ((= e (Div a b)) (evals-to a va) (evals-to b vb) (!= vb 0))
44 | ((evals-to e (/ va vb))))
45 | (rule ((evals-to x vx)) ((union x (Const vx))))
46 |
47 | (relation is-const (Math))
48 | (rule ((evals-to a va)) ((is-const a)))
49 |
50 | (relation is-sym (Math))
51 | (rule ((= e (Var s))) ((is-sym e)))
52 |
53 | (relation is-not-zero (Math))
54 | (rule ((evals-to x vx)
55 | (!= vx 0))
56 | ((is-not-zero x)))
57 |
58 | (relation is-const-or-distinct-var-demand (Math Math))
59 | (relation is-const-or-distinct-var (Math Math))
60 | (rule ((is-const-or-distinct-var-demand v w)
61 | (is-const v))
62 | ((is-const-or-distinct-var v w)))
63 | (rule ((is-const-or-distinct-var-demand v w)
64 | (= v (Var vv))
65 | (= w (Var vw))
66 | (!= vv vw))
67 | ((is-const-or-distinct-var v w)))
68 |
69 | (rewrite (Add a b) (Add b a))
70 | (rewrite (Mul a b) (Mul b a))
71 | (rewrite (Add a (Add b c)) (Add (Add a b) c))
72 | (rewrite (Mul a (Mul b c)) (Mul (Mul a b) c))
73 |
74 | (rewrite (Sub a b) (Add a (Mul (Const -1) b)))
75 | (rewrite (Div a b) (Mul a (Pow b (Const -1))) :when ((is-not-zero b)))
76 |
77 | (rewrite (Add a (Const 0)) a)
78 | (rewrite (Mul a (Const 0)) (Const 0))
79 | (rewrite (Mul a (Const 1)) a)
80 |
81 | ;; NOTE: these two rules are different from math.rs, as math.rs does pruning
82 | (rule ((MathU a) (!= a (Const 0))) ((union a (Add a (Const 0)))))
83 | (rule ((MathU a) (!= a (Const 1))) ((union a (Mul a (Const 1)))))
84 |
85 | (rewrite (Sub a a) (Const 0))
86 | (rewrite (Div a a) (Const 1) :when ((is-not-zero a)))
87 |
88 | (rewrite (Mul a (Add b c)) (Add (Mul a b) (Mul a c)))
89 | (rewrite (Add (Mul a b) (Mul a c)) (Mul a (Add b c)))
90 |
91 | (rewrite (Mul (Pow a b) (Pow a c)) (Pow a (Add b c)))
92 | (rewrite (Pow x (Const 0)) (Const 1) :when ((is-not-zero x)))
93 | (rewrite (Pow x (Const 1)) x)
94 | (rewrite (Pow x (Const 2)) (Mul x x))
95 | (rewrite (Pow x (Const -1)) (Div (Const 1) x) :when ((is-not-zero x)))
96 | (rewrite (Mul x (Div (Const 1) x)) (Const 1) :when ((is-not-zero x)))
97 |
98 | (rewrite (Diff x x) (Const 1) :when ((is-sym x)))
99 | (rule ((= e (Diff x c))
100 | (is-sym x))
101 | ((is-const-or-distinct-var-demand c x)))
102 | (rewrite (Diff x c) (Const 0) :when ((is-sym x) (is-const-or-distinct-var c x)))
103 |
104 | (rewrite (Diff x (Add a b)) (Add (Diff x a) (Diff x b)))
105 | (rewrite (Diff x (Mul a b)) (Add (Mul a (Diff x b)) (Mul b (Diff x a))))
106 |
107 | (rewrite (Diff x (Sin x)) (Cos x))
108 | (rewrite (Diff x (Cos x)) (Mul (Const -1) (Sin x)))
109 |
110 | (rewrite (Diff x (Ln x)) (Div (Const 1) x) :when ((is-not-zero x)))
111 |
112 | (rewrite (Diff x (Pow f g))
113 | (Mul (Pow f g)
114 | (Add (Mul (Diff x f) (Div g f))
115 | (Mul (Diff x g) (Ln f))))
116 | :when ((is-not-zero f)
117 | (is-not-zero g)))
118 |
119 | (rewrite (Integral (Const 1) x) x)
120 | (rewrite (Integral (Pow x c) x)
121 | (Div (Pow x (Add c (Const 1))) (Add c (Const 1)))
122 | :when ((is-const c)))
123 | (rewrite (Integral (Cos x) x) (Sin x))
124 | (rewrite (Integral (Sin x) x) (Mul (Const -1) (Cos x)))
125 | (rewrite (Integral (Add f g) x) (Add (Integral f x) (Integral g x)))
126 | (rewrite (Integral (Sub f g) x) (Sub (Integral f x) (Integral g x)))
127 | (rewrite (Integral (Mul a b) x)
128 | (Sub (Mul a (Integral b x))
129 | (Integral (Mul (Diff x a) (Integral b x)) x)))
130 |
131 |
132 | (let start-expr2 (Add (Const 1)
133 | (Sub (Var "a")
134 | (Mul (Sub (Const 2)
135 | (Const 1))
136 | (Var "a")))))
137 |
138 | (run 6)
139 |
140 | (let end-expr2 (Const 1))
141 |
142 | (check (= start-expr2 end-expr2))
143 |
144 | (query-extract start-expr2)
--------------------------------------------------------------------------------
/tests/matrix.egg:
--------------------------------------------------------------------------------
1 |
2 | (datatype Dim (Times Dim Dim) (NamedDim String) (Lit i64))
3 |
4 | (rewrite (Times a (Times b c)) (Times (Times a b) c))
5 | (rewrite (Times (Times a b) c) (Times a (Times b c)) )
6 | (rewrite (Times (Lit i) (Lit j)) (Lit (* i j)))
7 | (rewrite (Times a b) (Times b a))
8 |
9 | (datatype MExpr
10 | (MMul MExpr MExpr)
11 | (Kron MExpr MExpr)
12 | (NamedMat String)
13 | (Id Dim)
14 | ; DSum
15 | ; HStack
16 | ; VStack
17 | ; Transpose
18 | ; Inverse
19 | ; Zero Math Math
20 | ; ScalarMul
21 | )
22 |
23 | ; alternative encoding (type A) = (Matrix n m) may be more useful for "large story example"
24 | (constructor nrows (MExpr) Dim)
25 | (constructor ncols (MExpr) Dim)
26 |
27 | (rewrite (nrows (Kron A B)) (Times (nrows A) (nrows B)))
28 | (rewrite (ncols (Kron A B)) (Times (ncols A) (ncols B)))
29 |
30 | (rewrite (nrows (MMul A B)) (nrows A))
31 | (rewrite (ncols (MMul A B)) (ncols B))
32 |
33 | (rewrite (nrows (Id n)) n)
34 | (rewrite (ncols (Id n)) n)
35 |
36 | (rewrite (MMul (Id n) A) A)
37 | (rewrite (MMul A (Id n)) A)
38 |
39 | (rewrite (MMul A (MMul B C)) (MMul (MMul A B) C))
40 | (rewrite (MMul (MMul A B) C) (MMul A (MMul B C)))
41 |
42 | (rewrite (Kron A (Kron B C)) (Kron (Kron A B) C))
43 | (rewrite (Kron (Kron A B) C) (Kron A (Kron B C)))
44 |
45 | (rewrite (Kron (MMul A C) (MMul B D)) (MMul (Kron A B) (Kron C D)))
46 |
47 |
48 | (rewrite (MMul (Kron A B) (Kron C D))
49 | (Kron (MMul A C) (MMul B D))
50 | :when
51 | ((= (ncols A) (nrows C))
52 | (= (ncols B) (nrows D)))
53 | )
54 |
55 | ; demand
56 | (rule ((= e (MMul A B)))
57 | ((ncols A)
58 | (nrows A)
59 | (ncols B)
60 | (nrows B))
61 | )
62 |
63 | (rule ((= e (Kron A B)))
64 | ((ncols A)
65 | (nrows A)
66 | (ncols B)
67 | (nrows B))
68 | )
69 |
70 |
71 | (let n (NamedDim "n"))
72 | (let m (NamedDim "m"))
73 | (let p (NamedDim "p"))
74 |
75 | (let A (NamedMat "A"))
76 | (let B (NamedMat "B"))
77 | (let C (NamedMat "C"))
78 |
79 | (union (nrows A) n)
80 | (union (ncols A) n)
81 | (union (nrows B) m)
82 | (union (ncols B) m)
83 | (union (nrows C) p)
84 | (union (ncols C) p)
85 | (let ex1 (MMul (Kron (Id n) B) (Kron A (Id m))))
86 | (let rows1 (nrows ex1))
87 | (let cols1 (ncols ex1))
88 |
89 | (run 20)
90 |
91 | (check (= (nrows B) m))
92 | (check (= (nrows (Kron (Id n) B)) (Times n m)))
93 | (let simple_ex1 (Kron A B))
94 | (check (= ex1 simple_ex1))
95 |
96 | (let ex2 (MMul (Kron (Id p) C) (Kron A (Id m))))
97 | (run 10)
98 | (fail (check (= ex2 (Kron A C))))
99 |
--------------------------------------------------------------------------------
/tests/merge-during-rebuild.egg:
--------------------------------------------------------------------------------
1 | ; This file tests that non-union merges can be triggered during rebuilds as well
2 | ; as "inline" during a set action. See issue #42
3 |
4 | (datatype N (Node i64))
5 | (function distance (N N) i64 :merge (min old new))
6 |
7 | (let a (Node 0))
8 | (let b (Node 1))
9 | (let x (Node 2))
10 | (let y (Node 3))
11 | (set (distance x y) 1)
12 | (set (distance a b) 2)
13 |
14 | (union a x)
15 | (union b y)
16 |
17 | (run 1)
18 | (check (= (distance x y) 1)) ; fails, the distance has gone up!
19 |
--------------------------------------------------------------------------------
/tests/merge-saturates.egg:
--------------------------------------------------------------------------------
1 | (function foo () i64 :merge (min old new))
2 |
3 | (set (foo) 0)
4 |
5 | ; This should break at iteration 0 because the merge doesn't cause any updates
6 | (rule ((= f (foo))) ((set (foo) 1)))
7 | (run 100)
8 |
9 |
10 | ; This should run for about 50 iterations, because even though the merge doesn't
11 | ; change the value of baz, it has a side effect of expanding the domain of bar.
12 |
13 | ;(function baz (i64) i64 :default 0)
14 |
15 | ;(function bar () i64 :merge (min (baz new) 0))
16 |
17 | ;(set (bar) 1)
18 | ;(set (bar) 2)
19 |
20 | ;(rule ((= f (baz x)) (< x 50))
21 | ; ((set (bar) (+ x 1))))
22 |
23 | ;(run 100)
24 | ;(check (= 0 (baz 50)))
25 |
26 | ; The exploit above is no longer valid due to the removal of default
27 | ; however, can still do with lookups in merge and constructors/relations
28 |
29 | (relation baz (i64))
30 |
31 | (function const (Unit) i64 :no-merge)
32 |
33 | (set (const ()) 0)
34 |
35 | (function bar () i64 :merge (const (baz new)))
36 |
37 | (set (bar) 0)
38 | (set (bar) 1)
39 | (set (bar) 2)
40 |
41 | (rule (
42 | (baz x)
43 | (< x 50)
44 | ) (
45 | (set (bar) (+ x 1))
46 | ))
47 |
48 | (run 100)
49 | (check (baz 50))
--------------------------------------------------------------------------------
/tests/merge_read.egg:
--------------------------------------------------------------------------------
1 | (function foo () i64 :no-merge)
2 |
3 | (function bar () i64 :merge (foo))
4 |
5 | (set (bar) 0)
6 |
7 | (fail (set (bar) 1))
--------------------------------------------------------------------------------
/tests/multiset.egg:
--------------------------------------------------------------------------------
1 | (datatype Math (Num i64))
2 | (sort MathToMath (UnstableFn (Math) Math))
3 | (sort Maths (MultiSet Math))
4 |
5 | (let xs (multiset-of (Num 1) (Num 2) (Num 3)))
6 |
7 | ;; verify equal to other ordering
8 | (check (=
9 | (multiset-of (Num 3) (Num 2) (Num 1))
10 | xs
11 | ))
12 |
13 | ;; verify not equal to different counts
14 | (check (!=
15 | (multiset-of (Num 3) (Num 2) (Num 1) (Num 1))
16 | xs
17 | ))
18 |
19 | ;; Unclear why check won't work if this is defined inline
20 | (let inserted (multiset-insert xs (Num 4)))
21 | ;; insert
22 | (check (=
23 | (multiset-of (Num 1) (Num 2) (Num 3) (Num 4))
24 | inserted
25 | ))
26 |
27 |
28 | ;; contains and not contains
29 | (check (multiset-contains xs (Num 1)))
30 | (check (multiset-not-contains xs (Num 4)))
31 |
32 | ;; remove last
33 | (check (=
34 | (multiset-of (Num 1) (Num 3))
35 | (multiset-remove xs (Num 2))
36 | ))
37 | ;; remove one of
38 | (check (= (multiset-of (Num 1)) (multiset-remove (multiset-of (Num 1) (Num 1)) (Num 1))))
39 |
40 |
41 | ;; length
42 | (check (= 3 (multiset-length xs)))
43 | ;; length repeated
44 | (check (= 3 (multiset-length (multiset-of (Num 1) (Num 1) (Num 1)))))
45 |
46 | ;; pick
47 | (check (= (Num 1) (multiset-pick (multiset-of (Num 1)))))
48 |
49 | ;; map
50 | (constructor square (Math) Math)
51 | (rewrite (square (Num x)) (Num (* x x)))
52 |
53 | (let squared-xs (unstable-multiset-map (unstable-fn "square") xs))
54 | (run 1)
55 | (check (=
56 | (multiset-of (Num 1) (Num 4) (Num 9))
57 | squared-xs
58 | ))
59 |
60 | ;; sum
61 | (check (=
62 | (multiset-sum (multiset-of (Num 1) (Num 2) (Num 3)) (multiset-of (Num 1) (Num 2) (Num 4)))
63 | (multiset-of (Num 1) (Num 4) (Num 2) (Num 3) (Num 2) (Num 1))
64 | ))
65 |
66 | ;; verify that sum computes length
67 | (check (=
68 | (multiset-length (multiset-sum (multiset-of (Num 1) (Num 2) (Num 3)) (multiset-of (Num 1) (Num 2) (Num 4))))
69 | 6
70 | ))
71 |
--------------------------------------------------------------------------------
/tests/name-resolution.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Add Math Math)
3 | (Num i64))
4 |
5 | (let zero (Num 0))
6 |
7 |
8 | ;; zero here refers to the function/constant zero, not a free variable
9 | (rewrite (Add zero x) x)
10 |
11 | (let a (Add (Num 0) (Num 3)))
12 | (let b (Add (Num 7) (Num 9)))
13 | (let c (Num 16))
14 | (union b c)
15 |
16 | ;; crash if we merge two numbers
17 | (rule (
18 | (= (Num x) (Num y))
19 | (!= x y)
20 | )(
21 | (panic "ahhh")
22 | ))
23 |
24 |
25 | (run 10)
--------------------------------------------------------------------------------
/tests/no-messages/README.md:
--------------------------------------------------------------------------------
1 | These tests are run without saving or printing messages, to more accurately profile the performance when its embedded
2 | in a larger application and we use other ways to get the output, like the extraction report.
3 |
--------------------------------------------------------------------------------
/tests/path-union.egg:
--------------------------------------------------------------------------------
1 | (datatype Node
2 | (mk i64))
3 |
4 | (relation edge (Node Node))
5 | (relation path (Node Node))
6 |
7 | (rule ((edge x y))
8 | ((path x y)))
9 |
10 | (rule ((path x y) (edge y z))
11 | ((path x z)))
12 |
13 | (edge (mk 1) (mk 2))
14 | (edge (mk 2) (mk 3))
15 | (edge (mk 5) (mk 6))
16 |
17 | (union (mk 3) (mk 5))
18 |
19 | (run 10)
20 | (check (edge (mk 3) (mk 6)))
21 | (check (path (mk 1) (mk 6)))
--------------------------------------------------------------------------------
/tests/path.egg:
--------------------------------------------------------------------------------
1 | (relation path (i64 i64))
2 | (relation edge (i64 i64))
3 |
4 | (rule ((edge x y))
5 | ((path x y)))
6 |
7 | (rule ((path x y) (edge y z))
8 | ((path x z)))
9 |
10 | (edge 1 2)
11 | (edge 2 3)
12 | (edge 3 4)
13 | (check (edge 1 2))
14 | (fail (check (path 1 2)))
15 | (run 3)
16 |
17 | (print-function path 100)
18 | (check (path 1 4))
19 | (fail (check (path 4 1)))
20 |
--------------------------------------------------------------------------------
/tests/pathproof.egg:
--------------------------------------------------------------------------------
1 | ; proofs of connectivity are paths
2 | (datatype Proof
3 | (Trans i64 Proof)
4 | (Edge i64 i64))
5 |
6 | ; We enhance the path relation to carry a proof field
7 | (relation path (i64 i64 Proof))
8 | (relation edge (i64 i64))
9 |
10 | (edge 2 1)
11 | (edge 3 2)
12 | (edge 1 3)
13 |
14 | (rule ((edge x y))
15 | ((path x y (Edge x y))))
16 | (rule ((edge x y) (path y z p))
17 | ((path x z (Trans x p))))
18 |
19 | ; We consider equal all paths tha connect same points.
20 | ; Smallest Extraction will extract shortest path.
21 | (rule ((path x y p1) (path x y p2))
22 | ((union p1 p2)))
23 |
24 | (run 3)
25 | (check (path 3 1 (Trans 3 (Edge 2 1))))
26 | ; Would prefer being able to check
27 | ;(check (path 1 2 _))
28 | ; or extract
29 | ;(query-extract (path 1 4 ?p))
30 | (print-function path 100)
--------------------------------------------------------------------------------
/tests/points-to.egg:
--------------------------------------------------------------------------------
1 | ; Identifiers represented as strings, keep some newtypes around to aid clarity
2 | (datatype ClassT (Class String))
3 | (datatype FieldT (Field String))
4 |
5 | (datatype Stmt
6 | (New String ClassT)
7 | ; Assign dst src
8 | (Assign String String)
9 | ; Store dst field src
10 | (Store String FieldT String)
11 | ; Load dst src field
12 | (Load String String FieldT))
13 |
14 | (relation VarPointsTo (String ClassT))
15 | (relation HeapPointsTo (ClassT FieldT ClassT))
16 |
17 | ; New variables point to classes they're initialized as
18 | (rule ((= x (New a b))) ((VarPointsTo a b)))
19 |
20 | ; If I assign v1 <- v2 and v2 points to a class c2, then v1 points to class c2
21 | ; as well
22 | (rule ((= x (Assign v1 v2)) (VarPointsTo v2 c2))
23 | ((VarPointsTo v1 c2)))
24 |
25 | ; If c1.f points to c2, and v2 points to class c1, then assigning v1 <- v2.f
26 | ; means v1 points to c2
27 | (rule ((= x (Load v1 v2 f))
28 | (VarPointsTo v2 c1)
29 | (HeapPointsTo c1 f c2))
30 | ((VarPointsTo v1 c2)))
31 |
32 | ; If v1 points to class c1, and v2 to c2, and if v1.f <- v2, then c1.f points to
33 | ; c2
34 | (rule ((= x (Store v1 f v2))
35 | (VarPointsTo v1 c1)
36 | (VarPointsTo v2 c2))
37 | ((HeapPointsTo c1 f c2)))
38 |
39 | ; Example in "From Datalog to Flix"
40 | ; l1: ClassA o1 = new ClassA();
41 | ; l2: ClassB o2 = new ClassB();
42 | ; l3: ClassB o3 = o2;
43 | ; l4: o2.f = o1;
44 | ; l5: Object r = o3.f;
45 |
46 | (let A (Class "A"))
47 | (let B (Class "B"))
48 | (let f (Field "f"))
49 |
50 | (let l1 (New "o1" A))
51 | (let l2 (New "o2" B))
52 | (let l3 (Assign "o3" "o2"))
53 | (let l4 (Store "o2" f "o1"))
54 | (let l5 (Load "r" "o3" f))
55 |
56 | (run 3)
57 |
58 | (check (VarPointsTo "o1" A))
59 | (check (VarPointsTo "o2" B))
60 |
61 | (check (VarPointsTo "o3" B))
62 | (check (HeapPointsTo B f A))
63 | (check (VarPointsTo "r" A))
--------------------------------------------------------------------------------
/tests/primitives.egg:
--------------------------------------------------------------------------------
1 | (check (= (+ 2 2) 4))
2 | (check (= (- 2 1) 1))
3 | (check (= (- 1 2) -1))
4 | (check (< 1 2))
5 | (check (> 1 -2))
--------------------------------------------------------------------------------
/tests/prims.egg:
--------------------------------------------------------------------------------
1 | ; A nasty, imperative implementation of Prim's algorithm... in egglog!
2 | ; https://en.wikipedia.org/wiki/Prim%27s_algorithm
3 |
4 | ; Weighted edge (vertex 1 * vertex 2 * weight)
5 | (datatype edge (Edge i64 i64 i64))
6 | (relation edge-exists (edge))
7 |
8 | (relation mytrue ())
9 | (mytrue)
10 | (let infinity 99999999) ; close enough
11 |
12 | ; ==== PROBLEM INSTANCES ====
13 |
14 | ; Graph 1
15 | ; (1)--2--(2)
16 | ; \ |
17 | ; 1 2
18 | ; \ |
19 | ; (3)--3--(4)
20 | (ruleset graph1)
21 | (rule ((mytrue))
22 | ((edge-exists (Edge 1 2 2))
23 | (edge-exists (Edge 1 4 1))
24 | (edge-exists (Edge 2 4 2))
25 | (edge-exists (Edge 3 4 3)))
26 | :ruleset graph1)
27 |
28 | ; Graph 2
29 | ; (1)-2-(2) (3)
30 | ; |\ /| / |
31 | ; | 3 5 | 4 |
32 | ; 5 X 2 / 5
33 | ; | / \ |/ |
34 | ; (4)-4-(5)-7-(6)
35 | (ruleset graph2)
36 | (rule ((mytrue))
37 | ((edge-exists (Edge 1 2 1))
38 | (edge-exists (Edge 1 4 5))
39 | (edge-exists (Edge 1 5 3))
40 | (edge-exists (Edge 2 4 5))
41 | (edge-exists (Edge 2 5 2))
42 | (edge-exists (Edge 3 5 4))
43 | (edge-exists (Edge 3 6 5))
44 | (edge-exists (Edge 4 5 4))
45 | (edge-exists (Edge 5 6 7)))
46 | :ruleset graph2)
47 |
48 | ; ==== "INIT" RULESET ====
49 |
50 | (ruleset init)
51 |
52 | ; Graph is undirected
53 | (rule ((= e (Edge x y weight)))
54 | ((union e (Edge y x weight)))
55 | :ruleset init)
56 |
57 | ; Whether a vertex is included *so far* (this changes). Returns 0 or 1.
58 | (function vertex-included (i64) i64 :merge (max old new))
59 |
60 | ; All vertices default to being not included (note vertex-included's :merge)
61 | (rule ((edge-exists (Edge x y weight)))
62 | ((set (vertex-included x) 0))
63 | :ruleset init)
64 |
65 | ; Keep track of the current iteration
66 | (function current-iteration () i64 :merge (max old new))
67 |
68 | ; Map iteration to best edge found so far
69 | (function iteration-to-best-edge (i64) edge :merge new)
70 | (function iteration-to-best-edge-weight (i64) i64 :merge new)
71 |
72 | (rule ((mytrue))
73 | ((set (vertex-included 1) 1) ; Initially just include vertex 1
74 | (set (current-iteration) 0)
75 | (set (iteration-to-best-edge-weight 0) infinity))
76 | :ruleset init)
77 |
78 | ; === "CHOOSE BEST EDGE" RULESET ===
79 |
80 | (relation edge-in-mst (edge)) ; whether an edge is in our solution
81 |
82 | (ruleset choose-best-edge)
83 | (rule ((= i (current-iteration))
84 | (edge-exists (Edge x y weight))
85 | (= 1 (vertex-included x))
86 | (= 0 (vertex-included y))
87 | (< weight (iteration-to-best-edge-weight i)))
88 | ((set (iteration-to-best-edge-weight i) weight)
89 | (set (iteration-to-best-edge i) (Edge x y weight)))
90 | :ruleset choose-best-edge)
91 |
92 | ; === "FINISH ITERATION" RULESET ===
93 |
94 | (ruleset finish-iteration)
95 | (rule ((= i (current-iteration))
96 | (= (Edge x y weight) (iteration-to-best-edge i)))
97 | ((edge-in-mst (Edge x y weight)) ; incorporate chosen best edge
98 | (set (vertex-included x) 1) ; mark its vertices as included
99 | (set (vertex-included y) 1)
100 | (set (current-iteration) (+ i 1)) ; advance iteration
101 | (set (iteration-to-best-edge-weight (+ i 1)) infinity))
102 | :ruleset finish-iteration)
103 |
104 | ; === RUN VIA SCHEDULE ===
105 |
106 | (run-schedule
107 | (saturate init graph1) ; change to graph2 to see other example
108 | (saturate (saturate choose-best-edge) finish-iteration)
109 | )
110 |
111 | ; === PRINT RESULTS ===
112 |
113 | ; (print-function edge-in-mst) ; this is not very helpful
114 |
115 | ; Just copy canonical edges to solution
116 | (relation solution (i64 i64 i64))
117 |
118 | (ruleset finalize)
119 | (rule ((edge-in-mst (Edge x y weight)) (< x y))
120 | ((solution x y weight))
121 | :ruleset finalize)
122 | (run-schedule (saturate finalize))
123 |
124 | (print-function solution 100) ; this is better
125 |
--------------------------------------------------------------------------------
/tests/push-pop.egg:
--------------------------------------------------------------------------------
1 | (function foo () i64 :merge (max old new))
2 |
3 | (set (foo) 1)
4 | (check (= (foo) 1))
5 |
6 | (push)
7 | (set (foo) 2)
8 | (check (= (foo) 2))
9 | (pop)
10 |
11 | (check (= (foo) 1))
--------------------------------------------------------------------------------
/tests/rat-pow-eval.egg:
--------------------------------------------------------------------------------
1 | (let zero (bigrat (bigint 0) (bigint 1)))
2 | (let zero-alt (bigrat (bigint 0) (bigint -1)))
3 | (check (= zero zero-alt))
4 | (check (= zero (* (bigrat (bigint -1) (bigint 1)) zero-alt)))
5 |
6 | (let one (bigrat (bigint 1) (bigint 1)))
7 | (let two (bigrat (bigint 2) (bigint 1)))
8 |
9 | (let four (bigrat (bigint 4) (bigint 1)))
10 | (let fourth (bigrat (bigint 1) (bigint 4)))
11 | (check (!= four fourth))
12 |
13 | (let neg-one (bigrat (bigint -1) (bigint 1)))
14 | (let neg-one-alt (bigrat (bigint 1) (bigint -1)))
15 | (check (= neg-one neg-one-alt))
16 |
17 | (let neg-two (* neg-one two))
18 |
19 |
20 | ; 1 = 0^0 (zero-to-zero edge case)
21 | (check (= one (pow zero zero)))
22 | (check (= one (pow zero zero-alt)))
23 | ; 0 = 0^2 (a positive power)
24 | (check (= zero (pow zero two)))
25 | ; 1/0 error condition
26 | (fail (pow zero neg-one))
27 | (fail (pow zero neg-two))
28 |
29 | ; 4 = 2^2
30 | (check (= four (pow two two)))
31 | ; 1/4 == 4^-1
32 | (check (= fourth (pow four neg-one)))
33 | ; 1/4 = 2^-2
34 | (check (= fourth (pow two neg-two)))
35 | ; 1 = 1^-2
36 | (check (= one (pow one neg-two)))
37 | ; 1 = 2^0
38 | (check (= one (pow two zero)))
39 | ; 1 = (-2)^0
40 | (check (= one (pow neg-two zero)))
41 |
42 | ; rational powers are forbidden!
43 | (fail (pow zero fourth))
44 | (fail (pow two fourth))
45 |
46 |
47 | ; big numbers
48 | (let sixty-four (bigrat (bigint 64) (bigint 1)))
49 | (let sixty-three (- sixty-four one))
50 | (let max-i64 (- (pow two sixty-three) one ))
51 | (let max-u64 (pow two sixty-four))
52 |
53 | ; max power is max-i64
54 | (check (= one (pow one max-i64)))
55 | ; adding one more to the power should fail
56 | (fail (check (= one (pow one (+ one max-i64) ))))
57 | (fail (pow two max-u64))
58 |
--------------------------------------------------------------------------------
/tests/repro-define.egg:
--------------------------------------------------------------------------------
1 | (datatype Nat
2 | (S Nat))
3 | (constructor ZeroConst () Nat)
4 | (let Zero (ZeroConst))
5 |
6 | (let two (S (S Zero)))
7 |
8 | (union two (S (S (S Zero))))
9 | (check (= two (S (S (S Zero)))))
10 |
--------------------------------------------------------------------------------
/tests/repro-desugar-143.egg:
--------------------------------------------------------------------------------
1 | ;; To test on issue #143
2 | (rule ((= x 1) (= y x)) ())
3 | (rule ((= x 1) (= y x) (= z y)) ())
4 |
5 | (function f (i64) i64 :no-merge)
6 |
7 | (set (f 0) 0)
8 |
9 | ;; a funky id rule
10 | (rule ((f x) (= x y) (= z y) (= a (f z))) ((set (f (+ z 1)) (+ a 1))))
11 |
12 | (run 20)
13 |
14 | (print-function f 100)
15 | (check (= (f 10) 10))
16 |
17 | (datatype Value (Num i64))
18 | (constructor fib (Value) Value)
19 |
20 | ;; a funky fibonacci that test on more complex case and user defined datatype
21 | (rule ((= (Num a) (fib (Num x)))
22 | (= (Num b) (fib (Num y)))
23 | (= x1 x)
24 | (= y1 y)
25 | (= a1 a)
26 | (= b1 b)
27 | (= x1 x2)
28 | (= y1 y2)
29 | (= a1 a2)
30 | (= b1 b2)
31 | (= 1 (- x2 y2)))
32 | ((let n (+ x 1)) (let sum (+ a1 b2)) (union (fib (Num n)) (Num sum))))
33 |
34 | (union (fib (Num 1)) (Num 1))
35 | (union (fib (Num 2)) (Num 1))
36 |
37 | (run 20)
38 |
39 | (print-function fib 100)
40 | (check (= (fib (Num 10)) (Num 55)))
41 |
--------------------------------------------------------------------------------
/tests/repro-empty-query.egg:
--------------------------------------------------------------------------------
1 | (function foo () i64 :merge (min old new))
2 |
3 | (rule () ((set (foo) 4)))
4 |
5 | (set (foo) 10)
6 |
7 | (run 3)
8 |
9 | (check (= (foo) 4))
--------------------------------------------------------------------------------
/tests/repro-equal-constant.egg:
--------------------------------------------------------------------------------
1 | (function foo () i64 :merge (min old new))
2 |
3 | (rule ((= (foo) 5)) ((set (foo) 4)))
4 |
5 | (set (foo) 10)
6 |
7 | (run 3)
8 |
9 | (check (!= (foo) 4))
--------------------------------------------------------------------------------
/tests/repro-equal-constant2.egg:
--------------------------------------------------------------------------------
1 | (function foo () i64 :merge (min old new))
2 |
3 | (rule ((= (foo) 10)) ((set (foo) 4)))
4 |
5 | (set (foo) 10)
6 |
7 | (run 3)
8 |
9 | (check (= (foo) 4))
--------------------------------------------------------------------------------
/tests/repro-noteqbug.egg:
--------------------------------------------------------------------------------
1 | (datatype r (R i64))
2 | (union (R 1) (R 2))
3 |
4 | (check (= (R 1) (R 2)))
5 | (fail (check (!= (R 1) (R 2))))
6 |
7 | (run 0)
8 |
9 |
10 | (check (= (R 1) (R 2)))
11 | (fail (check (!= (R 1) (R 2))))
12 |
--------------------------------------------------------------------------------
/tests/repro-primitive-query.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Num i64))
3 |
4 | (Num 1)
5 | (Num 2)
6 |
7 | (rule ((Num ?a)
8 | (Num ?b)
9 | (= (+ ?a ?b) 5))
10 | ((panic "should not have matched")))
11 |
12 | (run 100)
--------------------------------------------------------------------------------
/tests/repro-querybug.egg:
--------------------------------------------------------------------------------
1 | (datatype list
2 | (Cons i64 list))
3 | (constructor EmptyConst () list)
4 | (let Empty (EmptyConst))
5 |
6 | (relation eq (list list))
7 |
8 | (eq Empty Empty)
9 |
10 | ; Oddly, this version works:
11 | ; (rule ((= x (Cons x1 rest1)) (= y (Cons x2 rest2)) (= 0 (- x1 x2)) (eq rest1 rest2))
12 | (rule ((= x (Cons x1 rest1)) (= y (Cons x2 rest2)) (= x1 x2) (eq rest1 rest2))
13 | ((eq (Cons x1 rest1) (Cons x2 rest2))))
14 |
15 | (let mylist (Cons 1 Empty))
16 |
17 | (run 100)
18 |
--------------------------------------------------------------------------------
/tests/repro-querybug2.egg:
--------------------------------------------------------------------------------
1 | (datatype Nat
2 | (Num i64)
3 | (OtherNum i64))
4 |
5 |
6 |
7 | (rule ((= y 2))
8 | ((union (OtherNum y) (Num y))))
9 |
10 | (Num 2)
11 |
12 |
13 | (run 100)
14 |
15 | (check (= (OtherNum 2) (Num 2)))
16 |
--------------------------------------------------------------------------------
/tests/repro-querybug3.egg:
--------------------------------------------------------------------------------
1 | (datatype VarT)
2 | (datatype Term)
3 | (constructor App (Term Term) Term)
4 | (constructor Lam (VarT Term) Term)
5 | (constructor Var (VarT) Term)
6 | (constructor Let (VarT Term Term) Term)
7 | (constructor Add (Term Term) Term)
8 | (constructor Num (i64) Term)
9 | (constructor CaseSplit (Term Term Term) Term)
10 | (constructor Cons (Term Term) Term)
11 | (constructor Nil () Term)
12 | (constructor V (String) VarT)
13 | (sort StringSet (Set VarT))
14 | (function freer (Term) StringSet :merge (set-intersect old new))
15 |
16 | ;;(rule ((= e (App e1 e2))
17 | ;; (= (freer e1) fv1)
18 | ;; (= (freer e2) fv2))
19 | ;; ((set (freer e) (set-union fv1 fv2))))
20 |
21 | (rule ((= e (App e1 e2))
22 | (= fvar1 (freer e1))
23 | (= fvar1 fv1)
24 | (= fvar2 (freer e2))
25 | (= fvar2 fv2))
26 | ((set (freer e) (set-union fv1 fv2))))
27 | (rule ((= e (Var v))) ((set (freer e) (set-insert (set-empty) v))))
28 | (constructor sum () Term :cost 1000)
29 | (union (sum) (Lam (V "xs") (CaseSplit (Var (V "xs")) (Num 0) (Lam (V "x") (Lam (V "xs'") (Add (Var (V "x")) (App (sum) (Var (V "xs'")))))))))
30 | (set (freer (sum)) (set-empty))
31 | (run 100)
32 |
--------------------------------------------------------------------------------
/tests/repro-querybug4.egg:
--------------------------------------------------------------------------------
1 | (sort Nat)
2 | (constructor Num (i64) Nat)
3 | (constructor OtherNum (i64) Nat)
4 | (rule ((= fvar5__ 2) (= fvar6__ fvar5__) (= y fvar5__))
5 | ((union (OtherNum fvar5__) (Num fvar5__))))
6 |
7 | (Num 2)
8 | (run 100)
9 | (check (= (OtherNum 2) (Num 2)))
--------------------------------------------------------------------------------
/tests/repro-should-saturate.egg:
--------------------------------------------------------------------------------
1 |
2 | (function MyMap () i64 :merge (min old new))
3 |
4 | (set (MyMap) 1)
5 |
6 | (rule ((MyMap))
7 | ((set (MyMap) 1)
8 | (set (MyMap) 2)))
9 |
10 | (run-schedule (saturate (run)))
11 |
--------------------------------------------------------------------------------
/tests/repro-silly-panic.egg:
--------------------------------------------------------------------------------
1 | (datatype KAT
2 | (par KAT KAT)
3 | )
4 | (constructor AConst () KAT)
5 | (let A (AConst))
6 |
7 | (rewrite (par p p) p)
8 | (rule ((= r (par q r)) (= q (par q r))) ((union r q)))
9 |
10 | ; tests
11 | (let q (par A A))
12 | (run 10)
--------------------------------------------------------------------------------
/tests/repro-typechecking-schedule.egg:
--------------------------------------------------------------------------------
1 | (rule () ())
2 |
3 | ;; This should type check
4 | (run-schedule
5 | (seq (run :until (= a 1))
6 | (run :until (= a "s"))))
7 |
--------------------------------------------------------------------------------
/tests/repro-unsound-htutorial.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Num i64)
3 | (Var String)
4 | (Add Math Math)
5 | (Div Math Math)
6 | (Mul Math Math))
7 |
8 | (let z (Var "z"))
9 |
10 | (Add (Var "x") (Var "y"))
11 |
12 | (rewrite (Add a z) a)
13 |
14 | (run 2)
15 |
16 | (fail (check (= (Var "x") (Add (Var "x") (Var "y")))))
--------------------------------------------------------------------------------
/tests/repro-unsound.egg:
--------------------------------------------------------------------------------
1 |
2 | (datatype HerbieType (Type String))
3 | (datatype Math (Num HerbieType i64) (Var HerbieType String) (Fma HerbieType Math Math Math) (If HerbieType Math Math Math) (Less HerbieType Math Math) (LessEq HerbieType Math Math) (Greater HerbieType Math Math) (GreaterEq HerbieType Math Math) (Eq HerbieType Math Math) (NotEq HerbieType Math Math) (Add HerbieType Math Math) (Sub HerbieType Math Math) (Mul HerbieType Math Math) (Div HerbieType Math Math) (Pow HerbieType Math Math) (Atan2 HerbieType Math Math) (Hypot HerbieType Math Math) (And HerbieType Math Math) (Or HerbieType Math Math) (Not HerbieType Math) (Neg HerbieType Math) (Sqrt HerbieType Math) (Cbrt HerbieType Math) (Fabs HerbieType Math) (Ceil HerbieType Math) (Floor HerbieType Math) (Round HerbieType Math) (Log HerbieType Math) (Exp HerbieType Math) (Sin HerbieType Math) (Cos HerbieType Math) (Tan HerbieType Math) (Atan HerbieType Math) (Asin HerbieType Math) (Acos HerbieType Math) (Expm1 HerbieType Math) (Log1p HerbieType Math) (Sinh HerbieType Math) (Cosh HerbieType Math) (Tanh HerbieType Math) (PI HerbieType) (E HerbieType) (INFINITY HerbieType) (TRUE HerbieType) (FALSE HerbieType))
4 | (let r-zero 0)
5 | (let r-one 1)
6 | (let r-two 2)
7 | (let r-three 3)
8 | (let r-four 4)
9 | (let r-neg-one -1)
10 | (relation universe (Math HerbieType))
11 | (rule ((= t (Expm1 ty a))) ((universe t ty)))
12 | (rewrite (Mul ty a b) (Mul ty b a))
13 |
14 | (rewrite (Sub ty x x) (Num ty r-zero))
15 |
16 | (rewrite (Mul ty x (Num ty r-one)) x)
17 |
18 | (rewrite (Div ty x (Num ty r-one)) x)
19 |
20 | (rewrite (Neg ty x) (Sub ty (Num ty r-zero) x))
21 |
22 | (rewrite (Neg ty x) (Mul ty (Num ty r-neg-one) x))
23 |
24 | (rule ((universe t ty)) ((union t (Mul ty (Num ty r-one) t))))
25 |
26 | (rewrite (Div ty (Sub ty a b) c)
27 | (Sub ty (Div ty a c) (Div ty b c)))
28 |
29 |
30 | (rewrite (Div ty (Mul ty a b) (Mul ty c d)) ;; not defined if c or d is zero
31 | (Mul ty (Div ty a c) (Div ty b d)))
32 |
33 |
34 | ;; errors if a or b errors
35 | (rewrite (Add ty a b)
36 | (If ty
37 | (NotEq ty (Sub ty a b) (Num ty r-zero)) ;; errors if a or b errors
38 | (Div ty
39 | (Sub ty (Mul ty a a) (Mul ty b b))
40 | (Sub ty a b))
41 | (Add ty a b)))
42 |
43 |
44 | (rewrite (Sub ty (Div ty a b) (Div ty c d)) ;; errors when b = 0 or d = 0
45 | (Div ty (Sub ty (Mul ty a d) (Mul ty b c))
46 | (Mul ty b d))) ;; errors when b = 0 or d = 0
47 |
48 |
49 | (rewrite (Sub ty (Mul ty x y) z)
50 | (Fma ty x y (Neg ty z)))
51 |
52 |
53 | (rewrite (Expm1 ty x)
54 | (Sub ty (Exp ty x) (Num ty r-one)))
55 |
56 |
57 |
58 |
59 | (let eggvar1 (Div (Type "binary64") (Expm1 (Type "binary64") (Add (Type "binary64") (Var (Type "binary64") "h0") (Var (Type "binary64") "h0"))) (Expm1 (Type "binary64") (Var (Type "binary64") "h0"))))
60 |
61 | (run 10)
62 |
63 | (check (= (Num ty n) (Num ty m)) (!= n m))
64 |
--------------------------------------------------------------------------------
/tests/repro-vec-unequal.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Num i64))
3 |
4 | (sort MathVec (Vec Math))
5 |
6 | (let v1 (vec-of (Num 1) (Num 2)))
7 | (let v2 (vec-of (Num 2) (Num 2)))
8 |
9 | (fail (check (= v1 v2)))
10 |
11 |
12 | (sort IVec (Vec i64))
13 |
14 | (let v3 (vec-of 1 2))
15 | (let v4 (vec-of 2 2))
16 |
17 | (fail (check (= v3 v4)))
--------------------------------------------------------------------------------
/tests/resolution.egg:
--------------------------------------------------------------------------------
1 | ; Resolution theorem proving
2 | ;
3 | ; Traditional resolution theorem provers maintain a clause database
4 | ; of formulas in Conjunction Normal Form (CNF a big And of Ors).
5 | ; Each clause is a set of positive and negative literals
6 | ; The prover saturates this set by taking two clauses
7 | ; {a}\/c1 {not a}\/c2 and creating a new clause c1 \/ c2.
8 | ; Clauses also are pruned by simplications, unit propagation,
9 | ; and subsumption.
10 | ; These systems use sophisticated term indexing to find matching clauses
11 |
12 | ; A natural question is whether egglog's saturation and term indexing gives
13 | ; a leg up towards building one of these systems. A programmable one even,
14 | ; with built in support for equality reasoning
15 |
16 | ; Resolution is provided by a join
17 | ; unit propagation is an equation solving process and egraph substitution
18 | ; Clause Simplification is provided by rewrite rules
19 |
20 | ; This encoding seems about right but is unsatisfying
21 | ; Using AC to encode the set nature of clauses is inefficient
22 |
23 | ; An important aspect of these provers that seems challenging to encode shallowly
24 | ; is that the match also occurs modulo _unification_.
25 | ; The unification variables of each clause are not globally scoped, really
26 | ; they are scoped outside the body of each clase in an implicit \forall
27 | ; This encoding as it stands really only supports ground atoms modulo equality
28 |
29 | (datatype Bool)
30 | (constructor TrueConst () Bool)
31 | (let True (TrueConst))
32 | (constructor FalseConst () Bool)
33 | (let False (FalseConst))
34 | (constructor myor (Bool Bool) Bool)
35 | (constructor negate (Bool) Bool)
36 |
37 | ; clauses are assumed in the normal form (or a (or b (or c False)))
38 |
39 | (union (negate False) True)
40 | (union (negate True) False)
41 |
42 | ; "Solving" negation equations
43 | (rule ((= (negate p) True)) ((union p False)))
44 | (rule ((= (negate p) False)) ((union p True)))
45 |
46 | ; canonicalize associtivity. "append" for clauses
47 | ; terminate with false
48 | (rewrite (myor (myor a b) c) (myor a (myor b c)))
49 | ; commutativity
50 | (rewrite (myor a (myor b c)) (myor b (myor a c)))
51 |
52 | ;absorption
53 | (rewrite (myor a (myor a b)) (myor a b))
54 | (rewrite (myor a (myor (negate a) b)) True)
55 |
56 | ; simplification
57 | (rewrite (myor False a) a)
58 | (rewrite (myor a False) a)
59 | (rewrite (myor True a) True)
60 | (rewrite (myor a True) True)
61 |
62 | ; unit propagation
63 | ; This is kind of interesting actually.
64 | ; Looks a bit like equation solving
65 |
66 | ; The following is not valid egglog but could be?
67 | ;(rewrite p True
68 | ; :when ((= True (or p False))))
69 |
70 | (rule ((= True (myor p False))) ((union p True)))
71 |
72 | ; resolution
73 | ; This counts on commutativity to bubble everything possible up to the front of the clause.
74 | (rule ((= True (myor a as)) (= True (myor (negate a) bs)))
75 | ((union (myor as bs) True)))
76 |
77 | ; example predicate
78 | (constructor p (i64) Bool)
79 | (let p0 (p 0))
80 | (let p1 (p 1))
81 | (let p2 (p 2))
82 | ;(union (or p0 (or p1 (or p2 False))) True)
83 | ;(union (or (negate p0) (or p1 (or (negate p2) False))) True)
84 | (union (myor p1 (myor (negate p2) False)) True)
85 | (union (myor p2 (myor (negate p0) False)) True)
86 | (union (myor p0 (myor (negate p1) False)) True)
87 | (union p1 False)
88 | (union (myor (negate p0) (myor p1 (myor p2 False))) True)
89 | (run 10)
90 |
91 |
92 | (check (!= True False))
93 | (check (= p0 False))
94 | (check (= p2 False))
95 |
96 | ; we could turn the original axioms into _patterns_ in all possible directions.
97 | ; Which is kind of compelling
98 | ; (rule ((or (pat x))) )
99 | ; or let a unification expansion happen and use thos
100 |
101 |
102 |
--------------------------------------------------------------------------------
/tests/schedule-demo.egg:
--------------------------------------------------------------------------------
1 | ; Step with alternating feet, left before right
2 | (relation left (i64))
3 | (relation right (i64))
4 |
5 | (left 0)
6 | (right 0)
7 |
8 | (ruleset step-left)
9 | (rule ((left x) (right x))
10 | ((left (+ x 1)))
11 | :ruleset step-left)
12 |
13 | (ruleset step-right)
14 | (rule ((left x) (right y) (= x (+ y 1)))
15 | ((right x))
16 | :ruleset step-right)
17 |
18 | (run-schedule
19 | (repeat 10
20 | (saturate step-right)
21 | (saturate step-left)))
22 |
23 | ; We took 10 steps with the left, but the right couldn't go the first round,
24 | ; so we took only 9 steps with the right.
25 | (check (left 10))
26 | (check (right 9))
27 | (fail (check (left 11)))
28 | (fail (check (right 10)))
29 |
--------------------------------------------------------------------------------
/tests/set.egg:
--------------------------------------------------------------------------------
1 | (sort ISetBase (Set i64))
2 |
3 | ; Test set-of
4 | (check (= (set-of 1 2) (set-insert (set-insert (set-empty) 1) 2)))
5 | (check (= (set-of 1 2) (set-insert (set-insert (set-empty) 2) 1)))
6 |
7 | ; Test set-union
8 | (check (= (set-union (set-of 1 2) (set-of 3 4)) (set-of 1 2 3 4)))
9 |
10 | ; Test set-length
11 | (check (= 0 (set-length (set-empty))))
12 | (check (= 1 (set-length (set-of 1 1 1))))
13 | (check (= 2 (set-length (set-of 1 -1 1 1))))
14 |
15 | ; Test set-get
16 | (check (= 1 (set-get (set-of 1 -1 2 4 1) 0)))
17 | (check (= 2 (set-get (set-of 1 -1 2 4 1) 1)))
18 | (check (= 4 (set-get (set-of 1 -1 2 4 1) 2)))
19 | (check (= -1 (set-get (set-of 1 -1 2 4 1) 3)))
20 |
21 | ; Test set-remove
22 | (check (= (set-remove (set-of 1 2 3) 3) (set-of 1 2)))
23 |
24 | ; Reify set
25 | (sort ISet)
26 | (constructor IS (ISetBase) ISet)
27 |
28 | (function ISet-get (ISet i64) i64 :no-merge)
29 | (rule ((IS x) (> (set-length x) 0))
30 | ((set (ISet-get (IS x) 0) (set-get x 0))))
31 | (rule ((ISet-get (IS x) j)
32 | (= i (+ j 1)) (< i (set-length x)))
33 | ((set (ISet-get (IS x) i) (set-get x i))))
34 |
35 | (let myset (IS (set-of 2 4 1 4 -1)))
36 | (run 100)
37 | (check (= 1 (ISet-get myset 0)))
38 | (check (= 2 (ISet-get myset 1)))
39 | (check (= 4 (ISet-get myset 2)))
40 | (check (= -1 (ISet-get myset 3)))
41 |
--------------------------------------------------------------------------------
/tests/set_sort_function.egg:
--------------------------------------------------------------------------------
1 | (sort Foo)
2 | (function bar () Foo :no-merge)
3 | (constructor baz () Foo)
4 | (constructor qux () Foo)
5 | (constructor quux () Foo)
6 |
7 | (set (bar) (baz))
8 | (union (baz) (qux))
9 | (set (bar) (qux))
10 | (fail (set (bar) (quux)))
--------------------------------------------------------------------------------
/tests/stratified.egg:
--------------------------------------------------------------------------------
1 | (relation path (i64 i64))
2 | (relation edge (i64 i64))
3 |
4 | (rule ((edge x y))
5 | ((path x y)))
6 |
7 | (edge 1 2)
8 | (edge 2 3)
9 | (edge 3 4)
10 | (check (edge 1 2))
11 | (run 3)
12 | (check (path 1 2))
13 |
14 | (ruleset path-rules)
15 |
16 | (rule ((path x y) (edge y z))
17 | ((path x z))
18 | :ruleset path-rules)
19 |
20 | (edge 3 8)
21 | (run path-rules 1)
22 | (check (path 1 3))
23 |
24 |
25 |
26 | ; Should fail
27 | ; (check (path 1 4))
28 | ; (check (path 3 8))
29 |
--------------------------------------------------------------------------------
/tests/string.egg:
--------------------------------------------------------------------------------
1 | ; Tests for the string sort
2 |
3 | ; Concatenation
4 | (check (= (+ "a" "bc" "de") "abcde"))
5 | ; Counting the number of substring occurances
6 | (check (= (count-matches "ab ab" "ab") 2))
7 | ; replacing a substring
8 | (check (= (replace "ab ab" "ab" "cd") "cd cd"))
9 |
--------------------------------------------------------------------------------
/tests/string_quotes.csv:
--------------------------------------------------------------------------------
1 | abc
2 |
--------------------------------------------------------------------------------
/tests/string_quotes.egg:
--------------------------------------------------------------------------------
1 | (function f () String :no-merge)
2 | (input f "tests/string_quotes.csv")
3 | (check (= (f) "abc"))
4 |
--------------------------------------------------------------------------------
/tests/subsume-relation.egg:
--------------------------------------------------------------------------------
1 | (datatype V (A) (B) (C))
2 | (relation R (V V))
3 |
4 | (A) (B) (C)
5 | (subsume (R (A) (B)))
6 | (R (A) (C))
7 | (union (B) (C))
8 |
9 | (check (= (R (A) (B)) ()))
10 |
--------------------------------------------------------------------------------
/tests/subsume.egg:
--------------------------------------------------------------------------------
1 | ;; Let's pretend that we are optimizing mathematical expressions, but for some reason on our compiler
2 | ;; multiplying by three is very expensive. So we want to rewrite those forms to three additions instead, and always
3 | ;; extract that form.
4 |
5 | (datatype Math
6 | (Num i64)
7 | (Var String)
8 | (Add Math Math)
9 | (Mul Math Math))
10 |
11 |
12 | (rewrite (Mul (Num 3) x) (Add x (Add x x)) :subsume)
13 |
14 | (let x (Mul (Num 2) (Mul (Num 3) (Var "x"))))
15 |
16 | (run 10)
17 |
18 | ; When X is extracted, we get the optimized form, where the * 3 is expanded out
19 | (check (= x (Mul (Num 2) (Add (Var "x") (Add (Var "x") (Var "x"))))))
20 | (extract x)
21 | ; Will be (Mul (Num 2) (Add (Var "x") (Add (Var "x") (Var "x"))))
22 |
23 | ; Even though it can't be extracted, we can still check that x equal 2 * (3 * x)
24 | (check (= x (Mul (Num 2) (Mul (Num 3) (Var "x")))))
25 |
26 | ; Also if we make multiplication commutative and run that run, we won't get that result either
27 | ; since the original expr has been subsumed when it was replaced with the addition
28 | (rewrite (Mul x y) (Mul y x))
29 | (run 10)
30 | (extract x)
31 |
--------------------------------------------------------------------------------
/tests/terms.rs:
--------------------------------------------------------------------------------
1 | use egglog::*;
2 |
3 | // This file tests the public API to terms.
4 |
5 | #[test]
6 | fn test_termdag_public() {
7 | let mut td = TermDag::default();
8 | let x = td.var("x".into());
9 | let seven = td.lit(7.into());
10 | let f = td.app("f".into(), vec![x, seven]);
11 | assert_eq!(td.to_string(&f), "(f x 7)");
12 | }
13 |
14 | #[test]
15 | #[should_panic]
16 | fn test_termdag_malicious_client() {
17 | // here is an example of how TermIds can be misused by passing
18 | // them into the wrong DAG.
19 |
20 | let mut td = TermDag::default();
21 | let x = td.var("x".into());
22 | // at this point, td = [0 |-> x]
23 | // snapshot the current td
24 | let td2 = td.clone();
25 | let y = td.var("y".into());
26 | // now td = [0 |-> x, 1 |-> y]
27 | let f = td.app("f".into(), vec![x.clone(), y.clone()]);
28 | // f is Term::App("f", [0, 1])
29 | assert_eq!(td.to_string(&f), "(f x y)");
30 | // recall that td2 = [0 |-> x]
31 | // notice that f refers to index 1, so this crashes:
32 | td2.to_string(&f);
33 | }
34 |
--------------------------------------------------------------------------------
/tests/test-combined-steps.egg:
--------------------------------------------------------------------------------
1 | ; Step with alternating feet, left before right
2 | (relation left (i64))
3 | (relation right (i64))
4 | (relation middle (i64))
5 |
6 | (left 0)
7 | (right 0)
8 |
9 | (ruleset step-left)
10 | (rule ((left x) (right x))
11 | ((left (+ x 1)))
12 | :ruleset step-left)
13 |
14 | (ruleset step-right)
15 | (rule ((left x) (right y) (= x (+ y 1)))
16 | ((right x))
17 | :ruleset step-right)
18 |
19 | (ruleset step-middle)
20 | (rule ((left x))
21 | ((middle x))
22 | :ruleset step-middle)
23 |
24 | (unstable-combined-ruleset
25 | my-combination
26 | step-left step-right
27 | step-middle)
28 |
29 | (run-schedule (repeat 1 my-combination))
30 |
31 | (check (left 1))
32 | (check (right 0))
33 | ;; middle didn't observe anything except original step
34 | (check (middle 0))
35 | (fail (check (left 2)))
36 | (fail (check (right 1)))
37 | (fail (check (middle 1)))
38 | (fail (check (middle 2)))
39 |
40 |
41 | (run-schedule
42 | (repeat 9
43 | (saturate step-right)
44 | my-combination
45 | (saturate step-right)))
46 |
47 | (check (left 10))
48 | (check (right 10))
49 | ;; middle didn't get a chance to observe (left 10)
50 | (check (middle 9))
51 | (fail (check (middle 10)))
52 | (fail (check (left 11)))
53 | (fail (check (right 11)))
54 |
--------------------------------------------------------------------------------
/tests/test-combined.egg:
--------------------------------------------------------------------------------
1 | (relation edge (i64 i64))
2 | (relation path (i64 i64))
3 |
4 |
5 | (ruleset myrules1)
6 | (rule ((edge x y))
7 | ((path x y))
8 | :ruleset myrules1)
9 | (ruleset myrules2)
10 | (rule ((path x y) (edge y z))
11 | ((path x z))
12 | :ruleset myrules2)
13 |
14 | (unstable-combined-ruleset myrules-combined
15 | myrules1 myrules2)
16 |
17 |
18 | (edge 0 1)
19 | (edge 1 2)
20 | (edge 2 3)
21 | (edge 2 4)
22 |
23 | (run-schedule
24 | (repeat 3 myrules-combined))
25 |
26 |
27 | (check (path 0 1))
28 | (check (path 0 2))
29 | (check (path 0 3))
30 | (check (path 0 4))
31 | (check (path 1 2))
32 | (check (path 1 3))
33 | (check (path 1 4))
34 |
--------------------------------------------------------------------------------
/tests/towers-of-hanoi.egg:
--------------------------------------------------------------------------------
1 | (datatype Stack
2 | (Empty)
3 | (Cons i64 Stack))
4 |
5 | (function Config (Stack Stack Stack) i64 :merge (min old new))
6 |
7 | ;; move from first stack
8 | (rule ((= len (Config (Cons x a) b c)))
9 | ((set (Config a (Cons x b) c) (+ len 1))
10 | (set (Config a b (Cons x c)) (+ len 1))))
11 |
12 | ;; move from second stack
13 | (rule ((= len (Config a (Cons x b) c)))
14 | ((set (Config (Cons x a) b c) (+ len 1))
15 | (set (Config a b (Cons x c)) (+ len 1))))
16 |
17 | ;; move from third stack
18 | (rule ((= len (Config a b (Cons x c))))
19 | ((set (Config (Cons x a) b c) (+ len 1))
20 | (set (Config a (Cons x b) c) (+ len 1))))
21 |
22 | (let e (Empty))
23 |
24 |
25 | ;; initial state [123 _ _] with path "length" 0
26 | (set (Config (Cons 1 (Cons 2 (Cons 3 e))) e e) 0)
27 |
28 | ;; find all reachable states
29 | (run 1000000)
30 |
31 | ;; print first 10 tuples
32 | (print-function Config 10)
33 | (print-size Config)
34 |
35 | ;; how to long to move to state [_ _ 123]
36 | (query-extract (Config e e (Cons 1 (Cons 2 (Cons 3 e)))))
37 |
38 | ;; actually do the assertion
39 | (check (= 5 (Config e e (Cons 1 (Cons 2 (Cons 3 e))))))
--------------------------------------------------------------------------------
/tests/tricky-type-checking.egg:
--------------------------------------------------------------------------------
1 | ;;;;;;;;;;;;;;;;;;
2 | ;; From repro-constraineq
3 |
4 | ;; repro-constraineq
5 | (push)
6 | (rule ((= x 1) (= y x) (= z y)) ())
7 | (run 1)
8 | (pop)
9 |
10 | ;; repro-constraineq2
11 | (push)
12 | (rule ((= x 1) (= y x)) ())
13 | (run 1)
14 | (pop)
15 |
16 | ;; repro-constraineq3
17 | (push)
18 | (relation f (i64))
19 |
20 | (rule ((= x 1)
21 | (= x 2))
22 | ((f x)))
23 |
24 | (run 1)
25 | (print-function f 10)
26 | (pop)
27 |
28 | ;;;;;;;;;;;;;;;;;;
29 | ;; Atoms need to be order-insensitive
30 |
31 | ;; Issue #196
32 | (push)
33 | (relation R (i64))
34 |
35 | (rule
36 | ((= x y)
37 | (= y 1))
38 | ((R x)))
39 | (run 1)
40 | (check (R 1))
41 | (pop)
42 |
43 | (push)
44 | (relation R (i64))
45 |
46 | (rule
47 | ((= x (+ y 1))
48 | (= y 1))
49 | ((R x)))
50 | (run 1)
51 | (check (R 2))
52 | (pop)
53 |
54 | ;; Issue #80
55 | (push)
56 | (datatype TYPE)
57 | (datatype TERM)
58 | (constructor type (TERM) TYPE)
59 | (constructor Ob () TYPE)
60 | (constructor Hom (TERM TERM) TYPE)
61 |
62 | (constructor id (TERM) TERM)
63 | (rule ((type (id A)))
64 | ((type A)))
65 | (rewrite (type (id A))
66 | (Hom A A)
67 | :when ((= (type A) (Ob))))
68 |
69 | (constructor compose (TERM TERM) TERM)
70 | (rule ((type (compose f g)))
71 | ((type f)
72 | (type g)))
73 | (rewrite (type (compose f g))
74 | (Hom A C)
75 | :when ((= (type f) (Hom A B))
76 | (= (type g) (Hom B C))))
77 |
78 | (birewrite (compose (compose f g) h)
79 | (compose f (compose g h))
80 | :when ((= (type A) (Ob))
81 | (= (type B) (Ob))
82 | (= (type C) (Ob))
83 | (= (type D) (Ob))
84 | (= (type f) (Hom A B))
85 | (= (type g) (Hom B C))
86 | (= (type h) (Hom C D))))
87 | (birewrite (compose f (id B)) f
88 | :when ((= (type A) (Ob))
89 | (= (type B) (Ob))
90 | (= (type f) (Hom A B))))
91 | (birewrite (compose (id A) f) f
92 | :when ((= (type A) (Ob))
93 | (= (type B) (Ob))
94 | (= (type f) (Hom A B))))
95 |
96 | (constructor AConst () TERM)
97 | (let A (AConst))
98 | (constructor BConst () TERM)
99 | (let B (BConst))
100 | (constructor fConst () TERM)
101 | (let f (fConst))
102 | (constructor gConst () TERM)
103 | (let g (gConst))
104 | (let fog (compose g f))
105 | (union (type f) (Hom A B))
106 | (union (type g) (Hom B A))
107 | (union (type A) (Ob))
108 | (union (type B) (Ob))
109 | (type fog)
110 | (run 10)
111 | (print-function type 10)
112 | (check (= (type f)
113 | (type (compose (id A)
114 | (compose f (id B))))))
115 | (check (= (type fog)
116 | (Hom B B)))
117 | (pop)
118 |
119 |
120 | ;;;;;;;;;;;;;;;;;;
121 | ;; Finding the right type in case of container types and primitives
122 |
123 | ;; Issue #113
124 |
125 | (push)
126 | (sort MyMap (Map i64 String))
127 | (sort MyMap1 (Map i64 i64))
128 |
129 | (let my_map1 (map-insert (map-empty) 1 "one"))
130 | (pop)
131 |
132 | (push)
133 | (sort MyMap1 (Map i64 i64))
134 | (sort MyMap (Map i64 String))
135 |
136 | (let my_map1 (map-insert (map-empty) 1 "one"))
137 | (pop)
138 |
139 |
--------------------------------------------------------------------------------
/tests/type-constraints-tests.egg:
--------------------------------------------------------------------------------
1 | (datatype Operand)
2 | (sort VecOperandBase (Vec Operand))
3 | (datatype VecOperand (VO VecOperandBase))
4 | (sort VecVecOperandBase (Vec VecOperand))
5 |
6 | (rule
7 | ((= v1 (vec-of))
8 | (= v2 (VO v1))
9 | (= v3 (vec-of v2)))
10 | ())
11 |
--------------------------------------------------------------------------------
/tests/typecheck.egg:
--------------------------------------------------------------------------------
1 | ; type checking for simply typed lambda calculus
2 |
3 | (datatype Type
4 | (TArr Type Type) ; t1 -> t2
5 | )
6 | (constructor TUnitConst () Type)
7 | (let TUnit (TUnitConst))
8 |
9 | (datatype Expr
10 | (Lam String Type Expr) ; lam x : t . e
11 | (App Expr Expr)
12 | (Var String)
13 | )
14 | (constructor MyUnitConst () Expr)
15 | (let MyUnit (MyUnitConst))
16 |
17 | (datatype Ctx
18 | (Cons String Type Ctx)
19 | )
20 | (constructor NilConst () Ctx)
21 | (let Nil (NilConst))
22 |
23 | ; ctx |- expr : type
24 | (constructor typeof (Ctx Expr) Type)
25 |
26 | ; ctx |- () : unit
27 | (rewrite (typeof ctx MyUnit) TUnit)
28 |
29 | ; ctx; x: t |- x : t
30 | (rewrite (typeof (Cons x t ctx) (Var x)) t)
31 |
32 | ; ctx |- f :- t1 -> t2
33 | ; ctx |- e : t1
34 | ; -----------------
35 | ; ctx |- f e : t2
36 |
37 | (rule (
38 | (= (typeof ctx (App f e)) t2)
39 | )(
40 | (typeof ctx f)
41 | (typeof ctx e)
42 | ))
43 |
44 | (rule (
45 | (= (typeof ctx (App f e)) t1)
46 | (= (typeof ctx f) (TArr (typeof ctx e) t2))
47 | )(
48 | (union t1 t2)
49 | ))
50 |
51 | ; ctx |- x : t
52 | ; ------------------ y != x
53 | ; ctx; y: t |- x : t
54 |
55 | (rewrite (typeof (Cons y ty ctx) (Var x))
56 | (typeof ctx (Var x))
57 | :when ((!= x y)))
58 |
59 | ; ctx; x: t1 |- e : t2
60 | ; ------------------------------
61 | ; ctx |- lam x: t1. e : t1 -> t2
62 |
63 | ; rhs of rewrite creates demand
64 | (rewrite (typeof ctx (Lam x t1 e))
65 | (TArr t1 (typeof (Cons x t1 ctx) e)))
66 |
67 | ; TEST
68 | ; ----
69 |
70 | ; lam x : unit, f : unit -> unit . f x
71 | (let e
72 | (Lam "x" TUnit
73 | (Lam "f" (TArr TUnit TUnit)
74 | (App (Var "f") (Var "x")))))
75 |
76 | ; lam x : unit . x
77 | (let id (Lam "x" TUnit (Var "x")))
78 | (let t-id (typeof Nil id))
79 |
80 | ; (e () id) = ()
81 | (let app-unit-id (App (App e MyUnit) id))
82 | (let t-app (typeof Nil app-unit-id))
83 |
84 | (let free (Lam "x" TUnit (Var "y")))
85 | (let t-free-ill (typeof Nil free))
86 | (let t-free-1 (typeof (Cons "y" TUnit Nil) free))
87 | (let t-free-2 (typeof (Cons "y" (TArr (TArr TUnit TUnit) TUnit) Nil) free))
88 |
89 | (run 15)
90 |
91 | (query-extract t-id)
92 | (check (= t-id (TArr TUnit TUnit)))
93 |
94 | (query-extract t-app)
95 | (check (= t-app TUnit))
96 |
97 | (query-extract t-free-1)
98 | (check (= t-free-1 (TArr TUnit TUnit)))
99 | (query-extract t-free-2)
100 | (check (= t-free-2 (TArr TUnit (TArr (TArr TUnit TUnit) TUnit))))
101 | ; this will err
102 | ; (query-extract t-free-ill)
103 |
--------------------------------------------------------------------------------
/tests/unify.egg:
--------------------------------------------------------------------------------
1 | (datatype Expr
2 | (Mul Expr Expr)
3 | (Var String)
4 | (Lit i64)
5 | )
6 |
7 | ; Assume injectivity of Mul for unification
8 | (rule ((= (Mul a b) (Mul c d)))
9 | ((union a c)
10 | (union b d)))
11 |
12 | ;; (relation False (i64))
13 | ; If any Literal make equal to something it can't be, false is derived
14 | ;(rule ((= (Lit i) (Lit j)) (!= i j))
15 | ; ((False 0)))
16 | (rule ((= (Lit i) (Mul a b)))
17 | ((panic "Literal cannot be equal to a product")))
18 |
19 | (union (Mul (Var "a") (Var "a"))
20 | (Mul (Lit 1) (Lit 2)))
21 |
22 |
23 | (run 3)
24 | (check (= (Var "a") (Lit 1)))
25 | (check (= (Lit 2) (Lit 1)))
26 | ; (check (False 0)) ;; this should fail because we don't want prove false
--------------------------------------------------------------------------------
/tests/unstable-fn.egg:
--------------------------------------------------------------------------------
1 | (datatype Math
2 | (Num i64)
3 | (Var String)
4 | (Add Math Math)
5 | (Mul Math Math))
6 |
7 | (rewrite (Mul (Num x) (Num y)) (Num (* x y)))
8 |
9 | (datatype MathList
10 | (Nil)
11 | (Cons Math MathList))
12 |
13 | (sort MathFn (UnstableFn (Math) Math))
14 |
15 |
16 | (constructor square (Math) Math)
17 | (rewrite (square x) (Mul x x))
18 |
19 | (let square-fn (unstable-fn "square" ))
20 |
21 | ;; test that we can call a function
22 | (let squared-3 (unstable-app square-fn (Num 3)))
23 | (check (= squared-3 (square (Num 3))))
24 |
25 | ;; test that we can apply a function to a list
26 |
27 | (constructor list-map-math (MathList MathFn) MathList)
28 | (rewrite (list-map-math (Nil) fn) (Nil))
29 | (rewrite (list-map-math (Cons x xs) fn) (Cons (unstable-app fn x) (list-map-math xs fn)))
30 |
31 | (let x (Cons (Num 1) (Cons (Num 2) (Cons (Num 3) (Nil)))))
32 | (let squared-x (list-map-math x square-fn))
33 | (run-schedule (saturate (run)))
34 | (check (= squared-x (Cons (Num 1) (Cons (Num 4) (Cons (Num 9) (Nil))))))
35 |
36 | ;; Test that we can partially apply a function in a rewrite rule
37 |
38 | (constructor list-multiply-by (MathList Math) MathList)
39 | (rewrite (list-multiply-by l i) (list-map-math l (unstable-fn "Mul" i)))
40 |
41 | (let doubled-x (list-multiply-by x (Num 2)))
42 | (run-schedule (saturate (run)))
43 | (check (= doubled-x (Cons (Num 2) (Cons (Num 4) (Cons (Num 6) (Nil))))))
44 |
45 | ;; Test we can define a higher order compose function
46 |
47 | (constructor composed-math (MathFn MathFn Math) Math)
48 | (rewrite (composed-math f g v) (unstable-app f (unstable-app g v)))
49 |
50 | (let square-of-double (unstable-fn "composed-math" square-fn (unstable-fn "Mul" (Num 2))))
51 |
52 | (let squared-doubled-x (list-map-math x square-of-double))
53 | (run-schedule (saturate (run)))
54 | (check (= squared-doubled-x (Cons (Num 4) (Cons (Num 16) (Cons (Num 36) (Nil))))))
55 |
56 |
57 | ;; See that it supports primitive values as well
58 | (sort i64Fun (UnstableFn (i64) i64))
59 |
60 | (constructor composed-i64-math (MathFn i64Fun i64) Math)
61 | (rewrite (composed-i64-math f g v) (unstable-app f (Num (unstable-app g v))))
62 |
63 | (let res (composed-i64-math square-fn (unstable-fn "*" 2) 4))
64 | (run-schedule (saturate (run)))
65 | (check (= res (Num 64)))
66 |
67 | ;; Verify that function parsing works with a function with no args
68 | (sort TestNullaryFunction (UnstableFn () Math))
69 | ;; Verify that we know the type of a function based on the string name
70 | (extract (unstable-fn "square"))
71 |
--------------------------------------------------------------------------------
/tests/until.egg:
--------------------------------------------------------------------------------
1 | ; A simple group
2 | (datatype G)
3 | (constructor IConst () G)
4 | (let I (IConst))
5 | (constructor AConst () G)
6 | (let A (AConst))
7 | (constructor BConst () G)
8 | (let B (BConst))
9 |
10 | (constructor g* (G G) G)
11 | (constructor inv (G) G)
12 | (birewrite (g* (g* a b) c) (g* a (g* b c))) ; assoc
13 | (rewrite (g* I a) a) ; idl
14 | (rewrite (g* a I) a) ; idr
15 |
16 | ; A is cyclic of period 4
17 | (rewrite (g* A (g* A (g* A A))) I)
18 |
19 | (let A2 (g* A A))
20 | (let A4 (g* A2 A2))
21 | (let A8 (g* A4 A4))
22 |
23 | ; non terminating rule
24 | (relation allgs (G))
25 | (rule ((allgs x)) ((allgs (g* B x))))
26 | (allgs A)
27 |
28 | ; if you remove :until, this will take a very long time
29 | (run 10000 :until (= A8 I))
30 | (check (= A8 I))
31 | (check (!= B A))
32 | (check (!= I A))
33 | ; If you need multiple stop conditions, consider using a (relation relation stop (unit))
34 | ; With rules filling it in with different stop conditions of interest.
35 |
--------------------------------------------------------------------------------
/tests/vec.egg:
--------------------------------------------------------------------------------
1 | (sort IVec (Vec i64))
2 |
3 | ; Test vec-of
4 | (check (= (vec-of 1 2) (vec-push (vec-push (vec-empty) 1) 2)))
5 |
6 | ; Test vec-append
7 | (check (= (vec-append (vec-of 1 2) (vec-of 3 4)) (vec-of 1 2 3 4)))
8 |
9 | ; Test vec-pop
10 | (check (= (vec-pop (vec-of 1 2 3)) (vec-of 1 2)))
11 |
12 | ; Test vec-not-contains
13 | (check (vec-not-contains (vec-of 1 2 3) 4))
14 |
15 | ; Test vec-contains
16 | (check (vec-contains (vec-of 1 2 3) 2))
17 |
18 | ; Test length
19 | (check (= (vec-length (vec-of 1 2 3)) 3))
20 |
21 | ; Test vec-get
22 | (check (= (vec-get (vec-of 1 2 3) 1) 2))
23 |
24 | ; Test vec-set
25 | (check (= (vec-set (vec-of 1 2 3) 1 4) (vec-of 1 4 3)))
26 |
--------------------------------------------------------------------------------
/web-demo/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | edition = "2021"
3 | name = "web-demo"
4 | version = "0.4.0"
5 |
6 | [lib]
7 | crate-type = ["cdylib"]
8 |
9 | [dependencies.egglog]
10 | default-features = false
11 | features = ["serde", "graphviz", "wasm-bindgen"]
12 | path = ".."
13 |
14 | [dependencies]
15 | wee_alloc = "0.4.5"
16 |
17 | log = "0.4.19"
18 | wasm-logger = "0.2"
19 | serde_json = "1.0"
20 | console_error_panic_hook = "0.1.7"
21 | js-sys = "0.3"
22 | wasm-bindgen = "0.2"
23 | web-sys = { version = "0.3.64", features = [
24 | # "Blob",
25 | # "BlobPropertyBag",
26 | # "console",
27 | "MessageEvent", # "Url",
28 | # "Window",
29 | # "Location",
30 | # "Document",
31 | # "HtmlElement",
32 | # "Node",
33 | # "Text",
34 | "Worker",
35 | "DedicatedWorkerGlobalScope",
36 | ] }
37 |
--------------------------------------------------------------------------------
/web-demo/examples.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import json
4 | import sys
5 | from pathlib import Path
6 |
7 | if len(sys.argv) <= 1:
8 | print("ERROR: give some files as input")
9 | sys.exit(1)
10 |
11 | files = sorted(sys.argv[1:])
12 |
13 | result = {}
14 | for filename in files:
15 | with open(filename) as f:
16 | name = Path(filename).stem
17 | result[name] = f.read()
18 |
19 | json.dump(result, sys.stdout, indent=2)
--------------------------------------------------------------------------------
/web-demo/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![allow(clippy::unused_unit)] // weird clippy bug with wasm-bindgen
2 | use egglog::SerializeConfig;
3 | use log::{Level, Log, Metadata, Record};
4 | use wasm_bindgen::prelude::*;
5 | use web_sys::console;
6 |
7 | #[global_allocator]
8 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
9 |
10 | #[wasm_bindgen(getter_with_clone)]
11 | pub struct Result {
12 | pub text: String,
13 | pub dot: String,
14 | pub json: String,
15 | }
16 |
17 | #[wasm_bindgen]
18 | pub fn run_program(input: &str) -> Result {
19 | let mut egraph = egglog::EGraph::default();
20 | match egraph.parse_and_run_program(Some("web-demo.egg".into()), input) {
21 | Ok(outputs) => {
22 | let serialized = egraph.serialize(SerializeConfig {
23 | max_functions: Some(40),
24 | max_calls_per_function: Some(40),
25 | ..Default::default()
26 | });
27 | let json = serde_json::to_string(&serialized).unwrap();
28 | Result {
29 | text: outputs.join("\n"),
30 | dot: serialized.to_dot(),
31 | json,
32 | }
33 | }
34 | Err(e) => Result {
35 | text: e.to_string(),
36 | dot: "".to_string(),
37 | json: "{}".to_string(),
38 | },
39 | }
40 | }
41 |
42 | #[wasm_bindgen(start)]
43 | pub fn start() {
44 | init();
45 | console_error_panic_hook::set_once();
46 | }
47 |
48 | /// The log styles
49 | struct Style {
50 | lvl_trace: String,
51 | lvl_debug: String,
52 | lvl_info: String,
53 | lvl_warn: String,
54 | lvl_error: String,
55 | }
56 |
57 | impl Style {
58 | fn new() -> Self {
59 | let base = String::from("color: white; padding: 0 3px; background:");
60 | Style {
61 | lvl_trace: format!("{} gray;", base),
62 | lvl_debug: format!("{} blue;", base),
63 | lvl_info: format!("{} green;", base),
64 | lvl_warn: format!("{} orange;", base),
65 | lvl_error: format!("{} darkred;", base),
66 | }
67 | }
68 |
69 | fn get_lvl_style(&self, lvl: Level) -> &str {
70 | match lvl {
71 | Level::Trace => &self.lvl_trace,
72 | Level::Debug => &self.lvl_debug,
73 | Level::Info => &self.lvl_info,
74 | Level::Warn => &self.lvl_warn,
75 | Level::Error => &self.lvl_error,
76 | }
77 | }
78 | }
79 |
80 | // This is inspired by wasm_logger
81 | struct WebDemoLogger {
82 | style: Style,
83 | }
84 |
85 | impl Log for WebDemoLogger {
86 | fn enabled(&self, _metadata: &Metadata<'_>) -> bool {
87 | true
88 | }
89 |
90 | fn log(&self, record: &Record<'_>) {
91 | if self.enabled(record.metadata()) {
92 | let style = &self.style;
93 | let s = format!(
94 | "{}\n{}\n",
95 | style.get_lvl_style(record.level()),
96 | record.level(),
97 | record.args(),
98 | );
99 | log(record.level().as_str(), &s);
100 | }
101 | }
102 |
103 | fn flush(&self) {}
104 | }
105 |
106 | #[wasm_bindgen]
107 | extern "C" {
108 | #[wasm_bindgen]
109 | fn log(level: &str, s: &str);
110 | }
111 |
112 | pub fn init() {
113 | let max_level = Level::Debug;
114 | let wl = WebDemoLogger {
115 | style: Style::new(),
116 | };
117 |
118 | match log::set_boxed_logger(Box::new(wl)) {
119 | Ok(_) => log::set_max_level(max_level.to_level_filter()),
120 | Err(e) => console::error_1(&JsValue::from(e.to_string())),
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/web-demo/static/base64.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Convert between Uint8Array and Base64 strings
3 | * Allows for any encoded JS string to be converted (as opposed to atob()/btoa() which only supports latin1)
4 | *
5 | * Original implementation by madmurphy on MDN
6 | * @see https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#Solution_1_–_JavaScript%27s_UTF-16_%3E_base64
7 | */
8 |
9 | function b64ToUint6(nChr) {
10 | return nChr > 64 && nChr < 91
11 | ? nChr - 65
12 | : nChr > 96 && nChr < 123
13 | ? nChr - 71
14 | : nChr > 47 && nChr < 58
15 | ? nChr + 4
16 | : nChr === 43
17 | ? 62
18 | : nChr === 47
19 | ? 63
20 | : 0
21 | }
22 |
23 | export function decodeToArray(base64string, blockSize) {
24 | var sB64Enc = base64string.replace(/[^A-Za-z0-9\+\/]/g, ''),
25 | nInLen = sB64Enc.length,
26 | nOutLen = blockSize
27 | ? Math.ceil(((nInLen * 3 + 1) >>> 2) / blockSize) * blockSize
28 | : (nInLen * 3 + 1) >>> 2,
29 | aBytes = new Uint8Array(nOutLen)
30 |
31 | for (
32 | var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0;
33 | nInIdx < nInLen;
34 | nInIdx++
35 | ) {
36 | nMod4 = nInIdx & 3
37 | nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << (18 - 6 * nMod4)
38 | if (nMod4 === 3 || nInLen - nInIdx === 1) {
39 | for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
40 | aBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255
41 | }
42 | nUint24 = 0
43 | }
44 | }
45 |
46 | return aBytes
47 | }
48 |
49 | function uint6ToB64(nUint6) {
50 | return nUint6 < 26
51 | ? nUint6 + 65
52 | : nUint6 < 52
53 | ? nUint6 + 71
54 | : nUint6 < 62
55 | ? nUint6 - 4
56 | : nUint6 === 62
57 | ? 43
58 | : nUint6 === 63
59 | ? 47
60 | : 65
61 | }
62 |
63 | export function encodeFromArray(bytes) {
64 | var eqLen = (3 - (bytes.length % 3)) % 3,
65 | sB64Enc = ''
66 |
67 | for (
68 | var nMod3, nLen = bytes.length, nUint24 = 0, nIdx = 0;
69 | nIdx < nLen;
70 | nIdx++
71 | ) {
72 | nMod3 = nIdx % 3
73 | /* Uncomment the following line in order to split the output in lines 76-character long: */
74 | /*
75 | if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += "\r\n"; }
76 | */
77 | nUint24 |= bytes[nIdx] << ((16 >>> nMod3) & 24)
78 | if (nMod3 === 2 || bytes.length - nIdx === 1) {
79 | sB64Enc += String.fromCharCode(
80 | uint6ToB64((nUint24 >>> 18) & 63),
81 | uint6ToB64((nUint24 >>> 12) & 63),
82 | uint6ToB64((nUint24 >>> 6) & 63),
83 | uint6ToB64(nUint24 & 63)
84 | )
85 | nUint24 = 0
86 | }
87 | }
88 |
89 | return eqLen === 0
90 | ? sB64Enc
91 | : sB64Enc.substring(0, sB64Enc.length - eqLen) + (eqLen === 1 ? '=' : '==')
92 | }
93 |
94 | /**
95 | * URL-safe variants of Base64 conversion functions (aka base64url)
96 | * @see https://tools.ietf.org/html/rfc4648#section-5
97 | */
98 |
99 | export function encodeFromArrayUrlSafe(bytes) {
100 | return encodeURIComponent(
101 | encodeFromArray(bytes)
102 | .replace(/\+/g, '-')
103 | .replace(/\//g, '_')
104 | )
105 | }
106 |
107 | export function decodeToArrayUrlSafe(base64string) {
108 | return decodeToArray(
109 | decodeURIComponent(base64string)
110 | .replace(/-/g, '+')
111 | .replace(/_/g, '/')
112 | )
113 | }
114 |
--------------------------------------------------------------------------------
/web-demo/static/worker.js:
--------------------------------------------------------------------------------
1 | importScripts("web_demo.js")
2 | console.log("I'm in the worker")
3 |
4 | let { run_program } = wasm_bindgen;
5 | async function work() {
6 | await wasm_bindgen("web_demo_bg.wasm");
7 |
8 | // Set callback to handle messages passed to the worker.
9 | self.onmessage = async event => {
10 | try {
11 | logbuffer = [];
12 | let result = run_program(event.data);
13 | console.log("Got result from worker", result);
14 | // Can't send the result directly, since it contains a reference to the
15 | // wasm memory. Instead, we send the dot and text separately.
16 | self.postMessage({ dot: result.dot, text: result.text, log: logbuffer, json: result.json });
17 | } catch (error) {
18 | console.log(error);
19 | self.postMessage({ dot: "", text: "Something panicked! Check the console logs...", log: logbuffer, json: "{}" });
20 | }
21 | };
22 | }
23 |
24 | logbuffer = [];
25 | function log(level, str) {
26 | logbuffer.push([level, str]);
27 | }
28 |
29 | work()
30 |
--------------------------------------------------------------------------------
| |