├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Gemfile ├── Gemfile.lock ├── README.md ├── _config.yaml ├── examples ├── arith.pl ├── axioms.pl ├── basics.pl ├── bitvec.pl ├── cat1.pl ├── category │ ├── braided.pl │ ├── braided_test.pl │ ├── cartesian.pl │ ├── category.pl │ ├── monoidal.pl │ ├── monoidal_test.pl │ └── symmetric.pl ├── datalog.pl ├── epeg.pl ├── eq_solve.pl ├── formula.pl ├── geom.pl ├── id_unique.pl ├── lists.pl ├── logic.pl ├── mem.pl ├── pb2.pl ├── pb3.pl ├── pb_compose.pl ├── peano.pl ├── qft.pl ├── rel.pl ├── ski.pl ├── stream_fusion.pl ├── talk.pl ├── types.pl └── whitespace.pl ├── index.md ├── pkg ├── egglog.js └── egglog_bg.wasm ├── src ├── gensym.rs ├── lib.rs ├── logic.rs ├── main.rs ├── parser.rs └── types.rs └── tests ├── cat.pl ├── clause.pl ├── cram ├── arith.t ├── axioms.t ├── basics.t ├── cat1.t ├── datalog.t ├── id_unique.t ├── run_test.sh ├── ski.t └── whitespace.t ├── eqclause.pl ├── eqclause2.pl ├── example.pl ├── example.rs └── monic2.dl /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /_site 3 | /.sass-cache 4 | /tests/cram/*.orig 5 | /tests/cram/*.t.err 6 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "arrayvec" 7 | version = "0.5.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 10 | 11 | [[package]] 12 | name = "atty" 13 | version = "0.2.14" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 16 | dependencies = [ 17 | "hermit-abi", 18 | "libc", 19 | "winapi", 20 | ] 21 | 22 | [[package]] 23 | name = "autocfg" 24 | version = "1.0.1" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 27 | 28 | [[package]] 29 | name = "bitflags" 30 | version = "1.2.1" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 33 | 34 | [[package]] 35 | name = "bitvec" 36 | version = "0.19.5" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" 39 | dependencies = [ 40 | "funty", 41 | "radium", 42 | "tap", 43 | "wyz", 44 | ] 45 | 46 | [[package]] 47 | name = "bumpalo" 48 | version = "3.7.0" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" 51 | 52 | [[package]] 53 | name = "byteorder" 54 | version = "1.4.3" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 57 | 58 | [[package]] 59 | name = "cfg-if" 60 | version = "1.0.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 63 | 64 | [[package]] 65 | name = "clap" 66 | version = "3.0.0-beta.4" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "fcd70aa5597dbc42f7217a543f9ef2768b2ef823ba29036072d30e1d88e98406" 69 | dependencies = [ 70 | "atty", 71 | "bitflags", 72 | "clap_derive", 73 | "indexmap", 74 | "lazy_static", 75 | "os_str_bytes", 76 | "strsim", 77 | "termcolor", 78 | "textwrap", 79 | "vec_map", 80 | ] 81 | 82 | [[package]] 83 | name = "clap_derive" 84 | version = "3.0.0-beta.4" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "0b5bb0d655624a0b8770d1c178fb8ffcb1f91cc722cb08f451e3dc72465421ac" 87 | dependencies = [ 88 | "heck", 89 | "proc-macro-error", 90 | "proc-macro2", 91 | "quote", 92 | "syn", 93 | ] 94 | 95 | [[package]] 96 | name = "egg" 97 | version = "0.6.1-dev" 98 | source = "git+https://github.com/philzook58/egg?branch=proof#bf1443591cc9171cbb7b37a037b98b9b1df2f789" 99 | dependencies = [ 100 | "fxhash", 101 | "hashbrown", 102 | "indexmap", 103 | "instant", 104 | "log", 105 | "once_cell", 106 | "smallvec", 107 | "symbolic_expressions", 108 | "thiserror", 109 | ] 110 | 111 | [[package]] 112 | name = "egglog" 113 | version = "0.1.0" 114 | dependencies = [ 115 | "clap", 116 | "egg", 117 | "nom", 118 | "parking_lot", 119 | "wasm-bindgen", 120 | ] 121 | 122 | [[package]] 123 | name = "funty" 124 | version = "1.1.0" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" 127 | 128 | [[package]] 129 | name = "fxhash" 130 | version = "0.2.1" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 133 | dependencies = [ 134 | "byteorder", 135 | ] 136 | 137 | [[package]] 138 | name = "hashbrown" 139 | version = "0.11.2" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 142 | 143 | [[package]] 144 | name = "heck" 145 | version = "0.3.3" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 148 | dependencies = [ 149 | "unicode-segmentation", 150 | ] 151 | 152 | [[package]] 153 | name = "hermit-abi" 154 | version = "0.1.19" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 157 | dependencies = [ 158 | "libc", 159 | ] 160 | 161 | [[package]] 162 | name = "indexmap" 163 | version = "1.7.0" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" 166 | dependencies = [ 167 | "autocfg", 168 | "hashbrown", 169 | ] 170 | 171 | [[package]] 172 | name = "instant" 173 | version = "0.1.10" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" 176 | dependencies = [ 177 | "cfg-if", 178 | "js-sys", 179 | "wasm-bindgen", 180 | "web-sys", 181 | ] 182 | 183 | [[package]] 184 | name = "js-sys" 185 | version = "0.3.54" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "1866b355d9c878e5e607473cbe3f63282c0b7aad2db1dbebf55076c686918254" 188 | dependencies = [ 189 | "wasm-bindgen", 190 | ] 191 | 192 | [[package]] 193 | name = "lazy_static" 194 | version = "1.4.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 197 | 198 | [[package]] 199 | name = "lexical-core" 200 | version = "0.7.6" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" 203 | dependencies = [ 204 | "arrayvec", 205 | "bitflags", 206 | "cfg-if", 207 | "ryu", 208 | "static_assertions", 209 | ] 210 | 211 | [[package]] 212 | name = "libc" 213 | version = "0.2.101" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" 216 | 217 | [[package]] 218 | name = "lock_api" 219 | version = "0.4.5" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" 222 | dependencies = [ 223 | "scopeguard", 224 | ] 225 | 226 | [[package]] 227 | name = "log" 228 | version = "0.4.14" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 231 | dependencies = [ 232 | "cfg-if", 233 | ] 234 | 235 | [[package]] 236 | name = "memchr" 237 | version = "2.3.4" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 240 | 241 | [[package]] 242 | name = "nom" 243 | version = "6.2.1" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6" 246 | dependencies = [ 247 | "bitvec", 248 | "funty", 249 | "lexical-core", 250 | "memchr", 251 | "version_check", 252 | ] 253 | 254 | [[package]] 255 | name = "once_cell" 256 | version = "1.8.0" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" 259 | 260 | [[package]] 261 | name = "os_str_bytes" 262 | version = "3.1.0" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "6acbef58a60fe69ab50510a55bc8cdd4d6cf2283d27ad338f54cb52747a9cf2d" 265 | 266 | [[package]] 267 | name = "parking_lot" 268 | version = "0.11.2" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 271 | dependencies = [ 272 | "instant", 273 | "lock_api", 274 | "parking_lot_core", 275 | ] 276 | 277 | [[package]] 278 | name = "parking_lot_core" 279 | version = "0.8.5" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" 282 | dependencies = [ 283 | "cfg-if", 284 | "instant", 285 | "libc", 286 | "redox_syscall", 287 | "smallvec", 288 | "winapi", 289 | ] 290 | 291 | [[package]] 292 | name = "proc-macro-error" 293 | version = "1.0.4" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 296 | dependencies = [ 297 | "proc-macro-error-attr", 298 | "proc-macro2", 299 | "quote", 300 | "syn", 301 | "version_check", 302 | ] 303 | 304 | [[package]] 305 | name = "proc-macro-error-attr" 306 | version = "1.0.4" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 309 | dependencies = [ 310 | "proc-macro2", 311 | "quote", 312 | "version_check", 313 | ] 314 | 315 | [[package]] 316 | name = "proc-macro2" 317 | version = "1.0.29" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" 320 | dependencies = [ 321 | "unicode-xid", 322 | ] 323 | 324 | [[package]] 325 | name = "quote" 326 | version = "1.0.9" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 329 | dependencies = [ 330 | "proc-macro2", 331 | ] 332 | 333 | [[package]] 334 | name = "radium" 335 | version = "0.5.3" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" 338 | 339 | [[package]] 340 | name = "redox_syscall" 341 | version = "0.2.10" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 344 | dependencies = [ 345 | "bitflags", 346 | ] 347 | 348 | [[package]] 349 | name = "ryu" 350 | version = "1.0.5" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 353 | 354 | [[package]] 355 | name = "scopeguard" 356 | version = "1.1.0" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 359 | 360 | [[package]] 361 | name = "smallvec" 362 | version = "1.6.1" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 365 | 366 | [[package]] 367 | name = "static_assertions" 368 | version = "1.1.0" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 371 | 372 | [[package]] 373 | name = "strsim" 374 | version = "0.10.0" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 377 | 378 | [[package]] 379 | name = "symbolic_expressions" 380 | version = "5.0.3" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "7c68d531d83ec6c531150584c42a4290911964d5f0d79132b193b67252a23b71" 383 | 384 | [[package]] 385 | name = "syn" 386 | version = "1.0.76" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" 389 | dependencies = [ 390 | "proc-macro2", 391 | "quote", 392 | "unicode-xid", 393 | ] 394 | 395 | [[package]] 396 | name = "tap" 397 | version = "1.0.1" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 400 | 401 | [[package]] 402 | name = "termcolor" 403 | version = "1.1.2" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 406 | dependencies = [ 407 | "winapi-util", 408 | ] 409 | 410 | [[package]] 411 | name = "textwrap" 412 | version = "0.14.2" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" 415 | dependencies = [ 416 | "unicode-width", 417 | ] 418 | 419 | [[package]] 420 | name = "thiserror" 421 | version = "1.0.29" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" 424 | dependencies = [ 425 | "thiserror-impl", 426 | ] 427 | 428 | [[package]] 429 | name = "thiserror-impl" 430 | version = "1.0.29" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" 433 | dependencies = [ 434 | "proc-macro2", 435 | "quote", 436 | "syn", 437 | ] 438 | 439 | [[package]] 440 | name = "unicode-segmentation" 441 | version = "1.8.0" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" 444 | 445 | [[package]] 446 | name = "unicode-width" 447 | version = "0.1.8" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 450 | 451 | [[package]] 452 | name = "unicode-xid" 453 | version = "0.2.2" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 456 | 457 | [[package]] 458 | name = "vec_map" 459 | version = "0.8.2" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 462 | 463 | [[package]] 464 | name = "version_check" 465 | version = "0.9.3" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 468 | 469 | [[package]] 470 | name = "wasm-bindgen" 471 | version = "0.2.77" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "5e68338db6becec24d3c7977b5bf8a48be992c934b5d07177e3931f5dc9b076c" 474 | dependencies = [ 475 | "cfg-if", 476 | "wasm-bindgen-macro", 477 | ] 478 | 479 | [[package]] 480 | name = "wasm-bindgen-backend" 481 | version = "0.2.77" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "f34c405b4f0658583dba0c1c7c9b694f3cac32655db463b56c254a1c75269523" 484 | dependencies = [ 485 | "bumpalo", 486 | "lazy_static", 487 | "log", 488 | "proc-macro2", 489 | "quote", 490 | "syn", 491 | "wasm-bindgen-shared", 492 | ] 493 | 494 | [[package]] 495 | name = "wasm-bindgen-macro" 496 | version = "0.2.77" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "b9d5a6580be83b19dc570a8f9c324251687ab2184e57086f71625feb57ec77c8" 499 | dependencies = [ 500 | "quote", 501 | "wasm-bindgen-macro-support", 502 | ] 503 | 504 | [[package]] 505 | name = "wasm-bindgen-macro-support" 506 | version = "0.2.77" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "e3775a030dc6f5a0afd8a84981a21cc92a781eb429acef9ecce476d0c9113e92" 509 | dependencies = [ 510 | "proc-macro2", 511 | "quote", 512 | "syn", 513 | "wasm-bindgen-backend", 514 | "wasm-bindgen-shared", 515 | ] 516 | 517 | [[package]] 518 | name = "wasm-bindgen-shared" 519 | version = "0.2.77" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "c279e376c7a8e8752a8f1eaa35b7b0bee6bb9fb0cdacfa97cc3f1f289c87e2b4" 522 | 523 | [[package]] 524 | name = "web-sys" 525 | version = "0.3.54" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "0a84d70d1ec7d2da2d26a5bd78f4bca1b8c3254805363ce743b7a05bc30d195a" 528 | dependencies = [ 529 | "js-sys", 530 | "wasm-bindgen", 531 | ] 532 | 533 | [[package]] 534 | name = "winapi" 535 | version = "0.3.9" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 538 | dependencies = [ 539 | "winapi-i686-pc-windows-gnu", 540 | "winapi-x86_64-pc-windows-gnu", 541 | ] 542 | 543 | [[package]] 544 | name = "winapi-i686-pc-windows-gnu" 545 | version = "0.4.0" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 548 | 549 | [[package]] 550 | name = "winapi-util" 551 | version = "0.1.5" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 554 | dependencies = [ 555 | "winapi", 556 | ] 557 | 558 | [[package]] 559 | name = "winapi-x86_64-pc-windows-gnu" 560 | version = "0.4.0" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 563 | 564 | [[package]] 565 | name = "wyz" 566 | version = "0.2.0" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" 569 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "egglog" 3 | version = "0.1.0" 4 | authors = ["Philip Zucker "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [lib] 10 | name = "egglog" 11 | path = "src/lib.rs" 12 | crate-type = ["cdylib", "rlib"] 13 | 14 | [[bin]] 15 | name = "egglog" 16 | path = "src/main.rs" 17 | 18 | 19 | [dependencies] 20 | nom = "6" 21 | egg = { git = "https://github.com/philzook58/egg", branch="proof" } #{path = "../egg/"} 22 | wasm-bindgen = "0.2.74" 23 | parking_lot = { version = "0.11.1", features = ["wasm-bindgen"]} 24 | clap = "3.0.0-beta.2" 25 | # rustyline = "9.0.0" -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } 6 | 7 | # gem "rails" 8 | 9 | gem 'github-pages' 10 | gem 'jekyll-theme-tactile' 11 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (6.0.4) 5 | concurrent-ruby (~> 1.0, >= 1.0.2) 6 | i18n (>= 0.7, < 2) 7 | minitest (~> 5.1) 8 | tzinfo (~> 1.1) 9 | zeitwerk (~> 2.2, >= 2.2.2) 10 | addressable (2.8.0) 11 | public_suffix (>= 2.0.2, < 5.0) 12 | coffee-script (2.4.1) 13 | coffee-script-source 14 | execjs 15 | coffee-script-source (1.11.1) 16 | colorator (1.1.0) 17 | commonmarker (0.17.13) 18 | ruby-enum (~> 0.5) 19 | concurrent-ruby (1.1.9) 20 | dnsruby (1.61.7) 21 | simpleidn (~> 0.1) 22 | em-websocket (0.5.2) 23 | eventmachine (>= 0.12.9) 24 | http_parser.rb (~> 0.6.0) 25 | ethon (0.14.0) 26 | ffi (>= 1.15.0) 27 | eventmachine (1.2.7) 28 | execjs (2.8.1) 29 | faraday (1.5.1) 30 | faraday-em_http (~> 1.0) 31 | faraday-em_synchrony (~> 1.0) 32 | faraday-excon (~> 1.1) 33 | faraday-httpclient (~> 1.0.1) 34 | faraday-net_http (~> 1.0) 35 | faraday-net_http_persistent (~> 1.1) 36 | faraday-patron (~> 1.0) 37 | multipart-post (>= 1.2, < 3) 38 | ruby2_keywords (>= 0.0.4) 39 | faraday-em_http (1.0.0) 40 | faraday-em_synchrony (1.0.0) 41 | faraday-excon (1.1.0) 42 | faraday-httpclient (1.0.1) 43 | faraday-net_http (1.0.1) 44 | faraday-net_http_persistent (1.2.0) 45 | faraday-patron (1.0.0) 46 | ffi (1.15.3) 47 | forwardable-extended (2.6.0) 48 | gemoji (3.0.1) 49 | github-pages (216) 50 | github-pages-health-check (= 1.17.2) 51 | jekyll (= 3.9.0) 52 | jekyll-avatar (= 0.7.0) 53 | jekyll-coffeescript (= 1.1.1) 54 | jekyll-commonmark-ghpages (= 0.1.6) 55 | jekyll-default-layout (= 0.1.4) 56 | jekyll-feed (= 0.15.1) 57 | jekyll-gist (= 1.5.0) 58 | jekyll-github-metadata (= 2.13.0) 59 | jekyll-mentions (= 1.6.0) 60 | jekyll-optional-front-matter (= 0.3.2) 61 | jekyll-paginate (= 1.1.0) 62 | jekyll-readme-index (= 0.3.0) 63 | jekyll-redirect-from (= 0.16.0) 64 | jekyll-relative-links (= 0.6.1) 65 | jekyll-remote-theme (= 0.4.3) 66 | jekyll-sass-converter (= 1.5.2) 67 | jekyll-seo-tag (= 2.7.1) 68 | jekyll-sitemap (= 1.4.0) 69 | jekyll-titles-from-headings (= 0.5.3) 70 | jemoji (= 0.12.0) 71 | kramdown (= 2.3.1) 72 | kramdown-parser-gfm (= 1.1.0) 73 | liquid (= 4.0.3) 74 | mercenary (~> 0.3) 75 | minima (= 2.5.1) 76 | nokogiri (>= 1.10.4, < 2.0) 77 | rouge (= 3.26.0) 78 | terminal-table (~> 1.4) 79 | github-pages-health-check (1.17.2) 80 | addressable (~> 2.3) 81 | dnsruby (~> 1.60) 82 | octokit (~> 4.0) 83 | public_suffix (>= 2.0.2, < 5.0) 84 | typhoeus (~> 1.3) 85 | html-pipeline (2.14.0) 86 | activesupport (>= 2) 87 | nokogiri (>= 1.4) 88 | http_parser.rb (0.6.0) 89 | i18n (0.9.5) 90 | concurrent-ruby (~> 1.0) 91 | jekyll (3.9.0) 92 | addressable (~> 2.4) 93 | colorator (~> 1.0) 94 | em-websocket (~> 0.5) 95 | i18n (~> 0.7) 96 | jekyll-sass-converter (~> 1.0) 97 | jekyll-watch (~> 2.0) 98 | kramdown (>= 1.17, < 3) 99 | liquid (~> 4.0) 100 | mercenary (~> 0.3.3) 101 | pathutil (~> 0.9) 102 | rouge (>= 1.7, < 4) 103 | safe_yaml (~> 1.0) 104 | jekyll-avatar (0.7.0) 105 | jekyll (>= 3.0, < 5.0) 106 | jekyll-coffeescript (1.1.1) 107 | coffee-script (~> 2.2) 108 | coffee-script-source (~> 1.11.1) 109 | jekyll-commonmark (1.3.1) 110 | commonmarker (~> 0.14) 111 | jekyll (>= 3.7, < 5.0) 112 | jekyll-commonmark-ghpages (0.1.6) 113 | commonmarker (~> 0.17.6) 114 | jekyll-commonmark (~> 1.2) 115 | rouge (>= 2.0, < 4.0) 116 | jekyll-default-layout (0.1.4) 117 | jekyll (~> 3.0) 118 | jekyll-feed (0.15.1) 119 | jekyll (>= 3.7, < 5.0) 120 | jekyll-gist (1.5.0) 121 | octokit (~> 4.2) 122 | jekyll-github-metadata (2.13.0) 123 | jekyll (>= 3.4, < 5.0) 124 | octokit (~> 4.0, != 4.4.0) 125 | jekyll-mentions (1.6.0) 126 | html-pipeline (~> 2.3) 127 | jekyll (>= 3.7, < 5.0) 128 | jekyll-optional-front-matter (0.3.2) 129 | jekyll (>= 3.0, < 5.0) 130 | jekyll-paginate (1.1.0) 131 | jekyll-readme-index (0.3.0) 132 | jekyll (>= 3.0, < 5.0) 133 | jekyll-redirect-from (0.16.0) 134 | jekyll (>= 3.3, < 5.0) 135 | jekyll-relative-links (0.6.1) 136 | jekyll (>= 3.3, < 5.0) 137 | jekyll-remote-theme (0.4.3) 138 | addressable (~> 2.0) 139 | jekyll (>= 3.5, < 5.0) 140 | jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) 141 | rubyzip (>= 1.3.0, < 3.0) 142 | jekyll-sass-converter (1.5.2) 143 | sass (~> 3.4) 144 | jekyll-seo-tag (2.7.1) 145 | jekyll (>= 3.8, < 5.0) 146 | jekyll-sitemap (1.4.0) 147 | jekyll (>= 3.7, < 5.0) 148 | jekyll-theme-tactile (0.1.1) 149 | jekyll (~> 3.5) 150 | jekyll-seo-tag (~> 2.0) 151 | jekyll-titles-from-headings (0.5.3) 152 | jekyll (>= 3.3, < 5.0) 153 | jekyll-watch (2.2.1) 154 | listen (~> 3.0) 155 | jemoji (0.12.0) 156 | gemoji (~> 3.0) 157 | html-pipeline (~> 2.2) 158 | jekyll (>= 3.0, < 5.0) 159 | kramdown (2.3.1) 160 | rexml 161 | kramdown-parser-gfm (1.1.0) 162 | kramdown (~> 2.0) 163 | liquid (4.0.3) 164 | listen (3.6.0) 165 | rb-fsevent (~> 0.10, >= 0.10.3) 166 | rb-inotify (~> 0.9, >= 0.9.10) 167 | mercenary (0.3.6) 168 | minima (2.5.1) 169 | jekyll (>= 3.5, < 5.0) 170 | jekyll-feed (~> 0.9) 171 | jekyll-seo-tag (~> 2.1) 172 | minitest (5.14.4) 173 | multipart-post (2.1.1) 174 | nokogiri (1.11.7-x86_64-linux) 175 | racc (~> 1.4) 176 | octokit (4.21.0) 177 | faraday (>= 0.9) 178 | sawyer (~> 0.8.0, >= 0.5.3) 179 | pathutil (0.16.2) 180 | forwardable-extended (~> 2.6) 181 | public_suffix (4.0.6) 182 | racc (1.5.2) 183 | rb-fsevent (0.11.0) 184 | rb-inotify (0.10.1) 185 | ffi (~> 1.0) 186 | rexml (3.2.5) 187 | rouge (3.26.0) 188 | ruby-enum (0.9.0) 189 | i18n 190 | ruby2_keywords (0.0.5) 191 | rubyzip (2.3.2) 192 | safe_yaml (1.0.5) 193 | sass (3.7.4) 194 | sass-listen (~> 4.0.0) 195 | sass-listen (4.0.0) 196 | rb-fsevent (~> 0.9, >= 0.9.4) 197 | rb-inotify (~> 0.9, >= 0.9.7) 198 | sawyer (0.8.2) 199 | addressable (>= 2.3.5) 200 | faraday (> 0.8, < 2.0) 201 | simpleidn (0.2.1) 202 | unf (~> 0.1.4) 203 | terminal-table (1.8.0) 204 | unicode-display_width (~> 1.1, >= 1.1.1) 205 | thread_safe (0.3.6) 206 | typhoeus (1.4.0) 207 | ethon (>= 0.9.0) 208 | tzinfo (1.2.9) 209 | thread_safe (~> 0.1) 210 | unf (0.1.4) 211 | unf_ext 212 | unf_ext (0.0.7.7) 213 | unicode-display_width (1.7.0) 214 | zeitwerk (2.4.2) 215 | 216 | PLATFORMS 217 | x86_64-linux 218 | 219 | DEPENDENCIES 220 | github-pages 221 | jekyll-theme-tactile 222 | 223 | BUNDLED WITH 224 | 2.2.24 225 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Defunct repo: Please see https://github.com/egraphs-good/egglog 2 | 3 | 4 | # egglog0 5 | 6 | Using the [egg](https://egraphs-good.github.io/) library with a file format and semantics similar to datalog. 7 | 8 | Short talk proposal paper for EGRAPHS 2022 9 | 10 | Explanatory blog posts: 11 | - 12 | - 13 | ## Try It Online!!! 14 | 15 | 16 | 17 | ## Building 18 | 19 | To run on a file locally: 20 | `cargo run --release tests/examples.pl` 21 | 22 | To build the wasm library: 23 | `wasm-pack build --target web` 24 | 25 | Note: I started modifying egg a bit. I exposed the Subst datatypes field. 26 | 27 | ## Ideas 28 | 29 | 30 | - [x] MultiApplier could be useful / efficient 31 | - [ ] Using Conditional Equals could be useful if all variables known 32 | - [ ] But really getting patterns to compile with substituion pieces considered known subsumes this optimization I think in modern egg with yihong's optimization. 33 | - [ ] Sanity checks that needed variables exist would be good. when it does crash it names rules, so that's something. 34 | - [ ] May want to run Runner multiple times since it may not get restarted. Currently I have that vec![0] hack 35 | - [ ] _ for dummy variables 36 | - [ ] The ability to check to see if something is in the egraph. 37 | - [ ] graphviz dumping the egraph. graphviz wasm? 38 | - [ ] harrop formula 39 | - [ ] merge_subst that doesn't copy? 40 | - [ ] Give rules names. Keep a hash table of them? 41 | - [x] Queries with variables 42 | - [x] Queries should be conjunctions 43 | - [X] a REPL would be sweet. especially if we have higher order rules, we could watch the database, add queries 44 | - [ ] termination based on the query condition 45 | - [ ] side effectful searchers and appliers (printing mostly), functions. 46 | - [ ] Astsize with weighting? Does that get me anywhere? 47 | - [ ] infix operators 48 | - [ ] better printers 49 | - [ ] rewrite/proof files that allow intermediate queriess. set of support? 50 | - [x] cli 51 | - [ ] smtlib subset (forall (a b ) (= (f a) (g c)) ) ! :pattern) or horn cluase style. 52 | - [ ] vaguely ML/coq style synax 53 | - [ ] tptp syntax? 54 | - [ ] push pop directives instead of clear. 55 | - [ ] only allow stuff that compresses the egraph? Appliers that do not add terms to the egraph or only add a couple? Or keeps counts. 56 | - [ ] directives to changes egraph params. or flags? 57 | - [ ] Macros/simplification stage? 58 | - [ ] typed symbollang - would this even be an optimization? 59 | - [ ] defunctionalization of lambdas. lambda-egglog 60 | - [ ] backchain until stumped? depth limitted backchain? 61 | - [ ] hashlog - experiment with same thing but on hashcons instead of egraph. Easier to understand semi naive? 62 | - [ ] epeg extraction 63 | - [ ] faster multipattern via compilation 64 | - [ ] integerate analysis? 65 | - [ ] gensym 66 | - [ ] serialize the egraph 67 | - [ ] negation checks. nonlogical 68 | - [ ] cli options to the runner 69 | 70 | ### Tests 71 | 72 | `cram tests/cram/*.t -i` 73 | -------------------------------------------------------------------------------- /_config.yaml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-tactile -------------------------------------------------------------------------------- /examples/arith.pl: -------------------------------------------------------------------------------- 1 | /* Doesn't work. Need to implement infix operators 2 | A + B <- B + A. 3 | A + (B + C) <-> (A + B) + C. 4 | 5 | A * B <- B * A. 6 | A * (B * C) <-> (A * B) * C. 7 | */ 8 | plus(X,Y) <- plus(Y,X). 9 | plus(plus(X,Y),Z) <-> plus(X,plus(Y,Z)). 10 | mul(X,Y) <- mul(Y,X). 11 | mul(mul(X,Y),Z) <-> mul(X, mul(Y,Z)). 12 | /* distributive */ 13 | mul(X,plus(Y,Z)) <-> plus(mul(X,Y),mul(X,Z)). 14 | X <- mul(one, X). 15 | X <- plus(zero, X). 16 | 17 | plus(one,X) <- succ(X). 18 | two = succ(one). 19 | three = succ(two). 20 | four = succ(three). 21 | five = succ(four). 22 | six = succ(five). 23 | 24 | plus(mul(x, three), mul(four,x)). 25 | plus(mul(x, three), mul(two, plus(one, mul(four,x)))). 26 | mul(two,two). 27 | ?- mul(two,two) = plus(two,two). 28 | ?- mul(two,two) = plus(one, X). 29 | ?- mul(two,two) = Z. 30 | ?- plus(mul(x, three), mul(two, plus(one, mul(four,x)))) = Z. 31 | -------------------------------------------------------------------------------- /examples/axioms.pl: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* Base equality facts */ 4 | Axiom ax1 : y = x. 5 | Axiom ax2 : z = y. 6 | /* Base egraph insertions. No implied equalities. For "seeding" the egraph. */ 7 | Axiom ax3 : f x. 8 | Axiom ax4 : bar boo. 9 | Axiom ax7 : plus p r. 10 | Axiom ax9 : forall x e, f (f (f x)) = e => e = x. 11 | Axiom ax10 : forall x e, f (f x) = e => e = x. 12 | /* general looking prolog-like rules */ 13 | Axiom fizbo : forall x, f x = q => bar x. 14 | Axiom fozbo : forall x z, fizzy floozy /\ buppo z => biz z = baz (biz boz). 15 | 16 | |- f x = x. 17 | |- x = x. 18 | |- y = x. 19 | |- plus p r = plus r p. 20 | |- junk boo = otherjunk baz. 21 | |- exists z, f z = x. 22 | ?- f(x) = x. 23 | ?- x = x. 24 | ?- y = x. 25 | ?- plus(p,r) = plus(r,p). 26 | ?- junk(boo) = otherjunk(baz). -------------------------------------------------------------------------------- /examples/basics.pl: -------------------------------------------------------------------------------- 1 | /* Base equality facts */ 2 | y = x. 3 | z = y. 4 | /* Base egraph insertions. No implied equalities. For "seeding" the egraph. */ 5 | f(x). 6 | bar(boo). 7 | plus(p,r). 8 | 9 | /* general looking prolog-like rules */ 10 | bar(X) :- f(X) = q. 11 | biz(Z) = baz(biz(boz)) :- fizzy(floozy), buppo(Z). 12 | 13 | /* rewrite rules. Variables denoted by capitalization */ 14 | plus(X,Y) <- plus(Y,X). 15 | /* In principle syntactic shorthand for plus(X,Y) = C :- plus(Y,X) = C. */ 16 | X <- f(f(f(X))). 17 | X <- f(f(X)). 18 | 19 | /* bidirectional rewrite. A useful syntactic shorthand for two rewrite rules. */ 20 | plus(X,plus(Y,Z)) <-> plus(plus(X,Y),Z). 21 | 22 | /* Guarded rewrite. */ 23 | fiz(baz) <- bar(boo), x = z. 24 | 25 | 26 | /* Queries 27 | Note that this does NOT insert into the egraph. Should I change that? Or give a new notation for "insert all subterms and then query"? 28 | */ 29 | ?- f(x) = x. 30 | ?- x = x. 31 | ?- y = x. 32 | ?- plus(p,r) = plus(r,p). 33 | ?- junk(boo) = otherjunk(baz). 34 | 35 | /* Query with variables. */ 36 | f(f(f(f(x)))). 37 | ?- f(f(f(f(x)))) = X. 38 | 39 | /* 40 | TODO: Directives. 41 | :- node_limit(1000). 42 | :- include("mylibrary.pl") 43 | */ -------------------------------------------------------------------------------- /examples/bitvec.pl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | add(len(A), len(B)) <- len(concat(A,B)). 5 | 6 | /* if you try to extract more than is there, blow up world. Or maybe assume wraparound? */ 7 | Size <- len(extract(Start, Size, A)). 8 | 9 | A <- extract(zero, len(A), concat(A,B)). 10 | B <- extract(len(A), len(B), concat(A,B)). 11 | 12 | 13 | /* 14 | bitvector arith 15 | */ 16 | B <- bvadd(bvzero(N),B). 17 | 18 | 19 | 20 | 21 | /* 22 | axioms of arithemtic 23 | */ -------------------------------------------------------------------------------- /examples/cat1.pl: -------------------------------------------------------------------------------- 1 | /* Axioms of category */ 2 | 3 | /* Typing rules */ 4 | /* identity exists */ 5 | type(id(A)) = hom(A,A) :- ob = type(A). 6 | /* ob = type(A) is probabnly slightly more efficient to search for than type(A) = ob */ 7 | 8 | /* Composition exists if types work out */ 9 | type(comp(F,G)) = hom(A,C) :- hom(A,B) = type(F), hom(B,C) = type(G). 10 | 11 | 12 | /* Now this is possible 13 | F = comp(id(A), F) :- hom(A,B) = type(F). 14 | F = comp(F, id(B)) :- hom(A,B) = type(F). 15 | */ 16 | /* 17 | More efficient form 18 | */ 19 | F <- comp(id(A), F). 20 | F <- comp(F, id(A)). 21 | 22 | 23 | /* associativity of composition */ 24 | comp(comp(F,G),H) <-> comp(F, comp(G,H)). 25 | 26 | 27 | /* specify types */ 28 | type(a) = ob. 29 | type(b) = ob. 30 | type(c) = ob. 31 | type(d) = ob. 32 | 33 | type(f) = hom(a,b). 34 | type(g) = hom(b,d). 35 | type(h) = hom(a,c). 36 | type(k) = hom(c,d). 37 | 38 | /* assume g is monic */ 39 | F = H :- comp(F,g) = comp(H,g), hom(A,b) = type(F), hom(A,b) = type(H). 40 | 41 | /* square is pullback */ 42 | comp(f,g) = comp(h,k). /* square commutes */ 43 | 44 | /* universal triangle 1 */ 45 | comp(univ(F,H,E),h) = H 46 | :- comp(F,g) = comp(H,k), hom(E,b) = type(F), hom(E,c) = type(H). 47 | 48 | /* universal triangle 2 */ 49 | comp(univ(F,H,E),f) = F 50 | :- comp(F,g) = comp(H,k), hom(E,b) = type(F), hom(E,c) = type(H). 51 | 52 | 53 | /* uniqueness given square and triangles */ 54 | U = univ(F,H,E) :- 55 | comp(F,g) = comp(H,k), comp(U,h) = H, comp(U,f) = F, hom(E,b) = type(F), hom(E,c) = type(H). 56 | 57 | /* Theorem: 58 | h is monic. => forall P Q, comp(P,h) = comp(Q,h), dom(P) = dom(Q) => P = Q 59 | 60 | We can take p and q to left of seqeunt, or intro them. 61 | They are arbitrary symbols. We introduce the domain as z. 62 | */ 63 | type(z) = ob. 64 | type(p) = hom(z,a). 65 | type(q) = hom(z,a). 66 | comp(p,h) = comp(q,h). 67 | 68 | ?- p = q. 69 | ?- f = g. 70 | ?- p = f. 71 | ?- k = h. 72 | ?- k = g. 73 | ?- type(comp(p,h)) = T. 74 | ?- type(id(a)) = hom(a,a). 75 | ?- comp(comp(id(a), h), k) = T. 76 | -------------------------------------------------------------------------------- /examples/category/braided.pl: -------------------------------------------------------------------------------- 1 | :- include(examples/category/monoidal.pl). 2 | /* right over left = rol */ 3 | /* left over right = lor */ 4 | /* 5 | type(rol(A,B)) = hom(otimes(A,B), otimes(B,A)) :- ob = type(A), ob = type(B). 6 | type(lor(A,B)) = hom(otimes(A,B), otimes(B,A)) :- ob = type(A), ob = type(B). 7 | */ 8 | id(otimes(A,B)) <-> comp(rol(A,B), lor(B,A)). 9 | id(otimes(A,B)) <-> comp(lor(A,B), rol(B,A)). 10 | 11 | id(A) <-> rol(A, munit). 12 | id(A) <-> rol(munit,A). 13 | 14 | id(A) <-> lor(A, munit). 15 | id(A) <-> lor(munit,A). 16 | 17 | 18 | /* naturality */ 19 | /* These need guarding 20 | Consider allowing bidirectional guards. 21 | Hmm. These are discovery not ConditionalEqual. 22 | Is this the moment I need to consider making multipattern matching work better? 23 | */ 24 | comp(otimes(F,G), rol(B,D)) = X :- comp(rol(C,A), otimes(G,F)) = X, hom(A,B) = type(F), hom(C,D) = type(G). 25 | comp(rol(C,A), otimes(G,F)) = X :- comp(otimes(F,G), rol(B,D)) = X, hom(A,B) = type(F), hom(C,D) = type(G). 26 | 27 | 28 | rol(A, otimes(B,C)) <-> comp( otimes(rol(A,B), id(C)), otimes(id(B), rol(A,C))). 29 | rol(otimes(A,B), C) <-> comp( otimes(id(A), rol(B,C)), otimes(rol(A,C), id(B))). 30 | /* and likewise for lor 31 | Given they are inverses do the other equations follow? 32 | */ 33 | 34 | comp(otimes(F,G), lor(B,D)) = X :- comp(lor(C,A), otimes(G,F)) = X, hom(A,B) = type(F), hom(C,D) = type(G). 35 | comp(lor(C,A), otimes(G,F)) = X :- comp(otimes(F,G), lor(B,D)) = X, hom(A,B) = type(F), hom(C,D) = type(G). 36 | 37 | 38 | lor(A, otimes(B,C)) <-> comp( otimes(lor(A,B), id(C)), otimes(id(B), lor(A,C))). 39 | lor(otimes(A,B), C) <-> comp( otimes(id(A), lor(B,C)), otimes(lor(A,C), id(B))). -------------------------------------------------------------------------------- /examples/category/braided_test.pl: -------------------------------------------------------------------------------- 1 | 2 | :- include(examples/category/braided.pl). 3 | 4 | type(a) = ob. 5 | type(b) = ob. 6 | type(c) = ob. 7 | type(d) = ob. 8 | 9 | 10 | rol(a, otimes(b,c,d)). 11 | 12 | comp( otimes(rol(a,b), id(c), id(d) ), 13 | otimes(id(b), rol(a,c), id(d) ), 14 | otimes(id(b), id(c), rol(a,d)) 15 | ). 16 | 17 | ?- rol(a, otimes(otimes(b,c),d)) = 18 | comp( otimes(rol(a,b), id(c), id(d) ), 19 | otimes(id(b), rol(a,c), id(d) ), 20 | otimes(id(b), id(c), rol(a,d)) 21 | ). 22 | 23 | /* id(otimes(a,b,c)). */ 24 | comp( otimes(lor(a,b) , id(c)), 25 | otimes(id(b), lor(a,c) ), 26 | rol( otimes(b,c), a )). 27 | 28 | ?- id(otimes(a,b,c)) = 29 | comp( otimes(lor(a,b) , id(c)), 30 | otimes(id(b), lor(a,c) ), 31 | rol( otimes(b,c), a)). 32 | 33 | ?- X = 34 | comp( otimes(lor(a,b) , id(c)), 35 | otimes(id(b), lor(a,c) ), 36 | rol( otimes(b,c), a)). 37 | 38 | /* 39 | maybe not. 40 | type(cup(A)) = hom(munit, otimes(A,A)) :- ob = type(A). 41 | type(cap(A)) = hom(otimes(A,A), munit) :- ob = type(A). 42 | comp( otimes(id(A), cup(A)) , otimes(cap(A), id(A)) ) <-> id(A). 43 | comp( otimes(cup(A), id(A)) , otimes(id(A), cap(A)) ) <-> id(A). 44 | */ -------------------------------------------------------------------------------- /examples/category/cartesian.pl: -------------------------------------------------------------------------------- 1 | :- include(examples/category/symmetric.pl). 2 | 3 | comp(dup(A), otimes(del(A), id(A)) <-> id(A). 4 | comp(dup(A), otimes(id(A), del(A)) <-> id(A). 5 | comp(dup(A), swap(A,A)) <-> dup(A). 6 | comp(otimes(dup(A),dup(B)), otimes(id(A), swap(A,B), id(B))) <-> dup(otimes(A,B)). 7 | comp(dup(A), otimes(id(A),dup(A)) <-> comp(dup(A), otimes(dup(A),id(A))). 8 | del(otimes(A,B)) <-> otimes(del(A), del(B)). 9 | dup(munit) = id(munit). 10 | del(munit) = id(munit). 11 | 12 | /* definition of proj and fan */ 13 | fan(F,G) <-> comp(dup(A), otimes(F,G)), type(F) = hom(A,B), type(G) = hom(A,C). 14 | 15 | proj1(A,B) <-> otimes(id(A),del(B)). 16 | proj2(A,B) <-> otimes(del(A),id(B)). 17 | 18 | del(A) = X :- comp(F, del(B)) = X, type(F) = hom(A,B). 19 | 20 | /* or write in cod dom form */ 21 | comp(F,dup(A)) = X :- comp(dup(B), otimes(F,F)) = X, type(F) = hom(A,B). 22 | comp(dup(B), otimes(F,F)) = X :- comp(F,dup(A)) = X, type(F) = hom(A,B). 23 | 24 | /* this fle is incomplete */ 25 | 26 | /* 27 | 28 | vec![rw!( "swap(munit(), munit()) => id(munit() oo munit())" ; "(swap munit munit)" => "(id (oo munit munit))" )], 29 | rw!( "dup(a) . ((del)(a) om id(a)) == id(a)" ; "(. (dup ?a) (om (del ?a) (id ?a)))" <=> "(id ?a)" ), 30 | rw!( "dup(a) . (id(a) om (del)(a)) == id(a)" ; "(. (dup ?a) (om (id ?a) (del ?a)))" <=> "(id ?a)" ), 31 | rw!( "dup(a) . swap(a, a) == dup(a)" ; "(. (dup ?a) (swap ?a ?a))" <=> "(dup ?a)" ), 32 | rw!( "(dup(a) om dup(b)) . ((id(a) om swap(a, b)) om id(b)) == dup(a oo b)" ; "(. (om (dup ?a) (dup ?b)) (om (om (id ?a) (swap ?a ?b)) (id ?b)))" <=> "(dup (oo ?a ?b))" ), 33 | rw!( "dup(a) . (dup(a) om id(a)) == dup(a) . (id(a) om dup(a))" ; "(. (dup ?a) (om (dup ?a) (id ?a)))" <=> "(. (dup ?a) (om (id ?a) (dup ?a)))" ), 34 | rw!( "(del)(a oo b) == (del)(a) om (del)(b)" ; "(del (oo ?a ?b))" <=> "(om (del ?a) (del ?b))" ), 35 | rw!( "dup(munit()) == id(munit())" ; "(dup munit)" <=> "(id munit)" ), 36 | rw!( "(del)(munit()) == id(munit())" ; "(del munit)" <=> "(id munit)" ), 37 | vec![rw!( "pair(f, k) => dup(dom(type(f))) . (f om k)" ; "(pair ?f ?k)" => "(. (dup (dom (type ?f))) (om ?f ?k))" )], 38 | rw!( "proj1(a, b) == id(a) om (del)(b)" ; "(proj1 ?a ?b)" <=> "(om (id ?a) (del ?b))" ), 39 | rw!( "proj2(a, b) == (del)(a) om id(b)" ; "(proj2 ?a ?b)" <=> "(om (del ?a) (id ?b))" ), 40 | vec![rw!( "f . (del)(b) => (del)(dom(type(f)))" ; "(. ?f (del ?b))" => "(del (dom (type ?f)))" )], 41 | vec![rw!( "f . dup(b) => dup(dom(type(f))) . (f om f)" ; "(. ?f (dup ?b))" => "(. (dup (dom (type ?f))) (om ?f ?f))" )], 42 | vec![rw!( "dup(a) . (f om f) => f . dup(cod(type(f)))" ; "(. (dup ?a) (om ?f ?f))" => "(. ?f (dup (cod (type ?f))))" )], 43 | 44 | */ -------------------------------------------------------------------------------- /examples/category/category.pl: -------------------------------------------------------------------------------- 1 | /* Identities exist */ 2 | type(id(A)) = hom(A,A) :- ob = type(A). 3 | /* Composition exists if types work out */ 4 | type(comp(F,G)) = hom(A,C) :- hom(A,B) = type(F), hom(B,C) = type(G). 5 | /* Identity Absorption */ 6 | F <- comp(id(A), F). 7 | F <- comp(F, id(A)). 8 | /* associativity of composition */ 9 | comp(comp(F,G),H) <-> comp(F, comp(G,H)). 10 | 11 | /* convenience. These would be better as macros? */ 12 | comp(F,comp(G,H)) <- comp(F,G,H). 13 | comp(F,comp(G,comp(H,K))) <- comp(F,G,H,K). -------------------------------------------------------------------------------- /examples/category/monoidal.pl: -------------------------------------------------------------------------------- 1 | :- include(examples/category/category.pl). 2 | /* We should make this better */ 3 | 4 | /* Monoidal. This just sends it off into the stratosphere. 5 | type(otimes(A,B)) = ob :- ob = type(A), ob = type(B). 6 | type(otimes(F,G)) = hom(otimes(A,B),otimes(C,D)) :- hom(A,C) = type(F), hom(B,D) = type(G). 7 | */ 8 | 9 | /* The non-generative form */ 10 | hom(otimes(A,B), otimes(C,D)) = T :- type(otimes(F,G)) = T, hom(A,C) = type(F), hom(B,D) = type(G). 11 | 12 | /* Covers the object case too */ 13 | otimes(otimes(F,G),H) <-> otimes(F, otimes(G,H)). 14 | 15 | type(munit) = ob. 16 | A <- otimes(munit, A). 17 | A <- otimes(A,munit). 18 | F <- otimes(id(munit), F). 19 | F <- otimes(F, id(munit)). 20 | 21 | /* Is this one necessary? */ 22 | id(otimes(A,B)) <-> otimes(id(A), id(B)). 23 | 24 | comp(otimes(F,G), otimes(P,Q)) <- otimes(comp(F,P), comp(G,Q)). 25 | /* And the other way */ 26 | 27 | /* Convenience. Macros? */ 28 | otimes(A,otimes(B,C)) <- otimes(A,B,C). 29 | otimes(A,otimes(B,otimes(C,D))) <- otimes(A,B,C,D). -------------------------------------------------------------------------------- /examples/category/monoidal_test.pl: -------------------------------------------------------------------------------- 1 | :- include(examples/category/monoidal.pl). 2 | 3 | type(a) = ob. 4 | type(b) = ob. 5 | type(c) = ob. 6 | /* simplify to id */ 7 | ?- comp(id(a),id(a)) = F. 8 | comp(id(a),otimes(id(a), id(munit))). 9 | ?- comp(id(a),otimes(id(a), id(munit))) = F. 10 | 11 | type(f) = hom(a,b). 12 | otimes(f,f,f). 13 | ?- otimes(f,f,f) = otimes(otimes(f,f),f). 14 | otimes(f,f,id(munit),f). 15 | 16 | ?- otimes(f,f,id(munit),f) = otimes(otimes(f,f),f). 17 | 18 | -------------------------------------------------------------------------------- /examples/category/symmetric.pl: -------------------------------------------------------------------------------- 1 | :- include(examples/category/monoidal.pl). 2 | 3 | id(A) <- swap(A, munit). 4 | id(A) <- swap(munit,A). 5 | id(otimes(A,B)) <- comp(swap(A,B), swap(B,A)). -------------------------------------------------------------------------------- /examples/datalog.pl: -------------------------------------------------------------------------------- 1 | /* Straight from https://en.wikipedia.org/wiki/Datalog */ 2 | parent(xerces, brooke). 3 | parent(brooke, damocles). 4 | ancestor(X, Y) :- parent(X, Y). 5 | ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y). 6 | ?- ancestor(xerces, X). 7 | 8 | 9 | /* https://www.stephendiehl.com/posts/exotic04.html */ 10 | /* All men are mortal / valar morghulis */ 11 | mortal(X) :- human(X). 12 | 13 | /* Socrates is a man. */ 14 | human(socrates). 15 | 16 | /* Is Socrates mortal? */ 17 | ?- mortal(socrates). 18 | 19 | /* https://souffle-lang.github.io/simple */ 20 | 21 | path(X, Y) :- edge(X, Y). 22 | path(X, Y) :- path(X, Z), edge(Z, Y). 23 | edge(a,b). 24 | edge(b,c). 25 | 26 | ?- path(X,Y). -------------------------------------------------------------------------------- /examples/epeg.pl: -------------------------------------------------------------------------------- 1 | /* https://arxiv.org/pdf/1012.1802.pdf 2 | appendix axiosms. 3 | 4 | 5 | theta cells are kind of like unfold/cons 6 | nats = cons(0, nats1 + 1) 7 | where everything has been stream lifted. so +1 is really map (+1) 8 | */ 9 | A = T :- theta(L, A, T) = T. 10 | apply(F, ite(C,A,B)) <-> ite(C,apply(F,A),apply(F,B)). 11 | /* I call phi noes ite */ 12 | A <- ite(C,A,A). 13 | ite(C,A,E) <- ite(C,ite(C,A,B),E). 14 | /* These are not axioms in the index? */ 15 | A <- ite(true,A,B). 16 | B <- ite(false, A, B). 17 | 18 | 19 | apply(F, ite(C,A,B)) <-> ite(C,apply(F,A),apply(F,B)). 20 | 21 | phi() -------------------------------------------------------------------------------- /examples/eq_solve.pl: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* These are not rewrite rules. They truly need the prolog like capabilities. 4 | Egglog enahnced with floats might be cool. Getting into metatheory territory then. 5 | */ 6 | A = div(C,B) :- mul(A,B) = C. /* guard on nonzero? x - x is zero. */ 7 | mul(A,B) = C :- A = div(C,B). 8 | sub(C,B) = A :- plus(A,B) = C. 9 | plus(A,B) = C :- sub(C,B) = A. 10 | 11 | 12 | 13 | /* Properties of div and sub */ 14 | mul(X, div(N,D)) <-> div(mul(X,N),D). 15 | 16 | 17 | /* 18 | Didn't I have some story about grobner solving? 19 | Or really like some heurstic waay to reduce to -1 20 | */ 21 | /* The egg math examples */ 22 | 23 | /* 24 | summing geometric series 25 | 1 + x + ... = 26 | S - xS = 1 27 | 28 | S(x,zero) = 1 29 | S(x,n) = pow(x,n) + S(x, n - 1). 30 | 31 | 32 | */ -------------------------------------------------------------------------------- /examples/formula.pl: -------------------------------------------------------------------------------- 1 | 2 | 3 | Axiom myaxion : forall x, f x => g x. 4 | 5 | Axiom a_is_f : f a. 6 | 7 | |- g a. -------------------------------------------------------------------------------- /examples/geom.pl: -------------------------------------------------------------------------------- 1 | /* https://en.wikipedia.org/wiki/List_of_first-order_theories */ 2 | 3 | /* exists!(C, on(A,C), on(B,C)):- distinct(A,B) */ 4 | on(A, line(A,B)), on(B, line(A,B)) :- point(A), distinct(A,B), point(B). 5 | F = line(A,B) :- point(A), point(B), distinct(A,B), on(A,L), on(B,L). /* unique */ 6 | 7 | /* veblen axiom */ 8 | 9 | /* 3 distinct points */ 10 | distinct(p1(L), p2(L)), 11 | distinct(p2(L), p3(L)), 12 | distinct(p1(L), p3(L)), 13 | on(p1(L), L), 14 | on(p2(L), L), 15 | on(p3(L), L), :- 16 | line(L). 17 | 18 | distinct(B,A) :- distinct(A,B). 19 | /* equal? use <- ? */ 20 | 21 | /* better not have a contradiction */ 22 | contradiction :- distinct(A,A). 23 | 24 | point(a). 25 | line(l). 26 | 27 | 28 | /* hmm I could have just run cpp rather than done this stuff myself. 29 | Of course, that'd make web compiling not works so good */ 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/id_unique.pl: -------------------------------------------------------------------------------- 1 | type(id(A)) = hom(A,A) :- ob = type(A). 2 | /* ob = type(A) is probabnly slightly more efficient to search for than type(A) = ob */ 3 | F <- comp(id(A), F). 4 | F <- comp(F, id(A)). 5 | 6 | comp(F,id(B)) = F :- type(F) = hom(A,B). 7 | comp(id(A),F) = F :- type(F) = hom(A,B). 8 | /* associativity of composition */ 9 | comp(comp(F,G),H) <-> comp(F, comp(G,H)). 10 | 11 | /* Composition exists if types work out */ 12 | type(comp(F,G)) = hom(A,C) :- hom(A,B) = type(F), hom(B,C) = type(G). 13 | 14 | /* A supposed second identity for object a */ 15 | 16 | type(a) = ob. 17 | type(id2(a)) = hom(a,a). 18 | 19 | F <- comp(F,id2(a)). 20 | F <- comp(id2(a),F). 21 | comp(F,id2(a)) = F :- type(F) = hom(A,a). 22 | comp(id2(a),F) = F :- type(F) = hom(a,B). 23 | 24 | 25 | ?- id2(a) = id(a). 26 | 27 | /* sanity check. f should not be the identity a priori */ 28 | type(f) = hom(a,a). 29 | ?- f = id2(a). 30 | -------------------------------------------------------------------------------- /examples/lists.pl: -------------------------------------------------------------------------------- 1 | list(nil). 2 | list(cons(X,Y)) :- list(Y), cons(X,Y). 3 | 4 | A <- car(cons(A,L)). 5 | L <- cdr(cons(A,L)). 6 | 7 | memb(A,cons(A,L)) :- cons(A,L). 8 | 9 | /* 10 | How should this work? Aliasing issues. We may discover K1 is K later. 11 | lookup() <- lookup( K1, cons(kv(K,V),L) ), K1 != K . 12 | V <- lookup( K, cons(kv(K,V),L) ). 13 | */ 14 | 15 | append(nil,Y) <-> Y. 16 | append(Y,nil) <-> Y. 17 | append(cons(X,Y),Z) <-> cons(X, append(Y,Z)). 18 | 19 | 20 | 21 | rev(nil) = nil. 22 | X <- rev(rev(X)). 23 | rev(append(X,Y)) <-> append(rev(Y),rev(X)). 24 | 25 | 26 | cons(x,cons(y,cons(z,nil))) = l. 27 | ?- append(X,Y) = l. 28 | 29 | nil <- map(F,nil). 30 | cons(apply(F,X), map(F,L)) <- map(F,cons(X,L)). 31 | 32 | apply(F,apply(G,X)) <-> apply(comp(F,G), X). 33 | map(F,map(G,L)) <-> map(comp(F,G),L). 34 | 35 | nil <- filter(F,nil). 36 | cons(X,filter(F,L)) <- filter( F, cons(X,L) ), apply(F,X) = true. 37 | filter(F,L) <- filter( F, cons(X,L) ), apply(F,X) = false. 38 | 39 | /* defunctionalization */ 40 | X <- apply(id, X). 41 | /* What is id? a partial function over the union of all types? A relation? */ 42 | 43 | filter(id, cons(true,cons(false,nil))). 44 | ?- filter(id, cons(true,cons(false,nil))) = Res. 45 | -------------------------------------------------------------------------------- /examples/logic.pl: -------------------------------------------------------------------------------- 1 | 2 | /* https://www.cs.cornell.edu/gries/TechReports/94-1455.pdf 3 | http://www.mathmeth.com/read.shtml 4 | */ 5 | /* reflection 6 | The first reflection achieves the substitution property of the equality predicate 7 | The second is just very useful, but also makes the ergaph explode. 8 | predicate form - in egraph means true. Doesn't make much sense though? 9 | A = B :- eq(A,B). 10 | eq(A,B):- A = B. 11 | */ 12 | A = B :- eq(A,B) = true. 13 | eq(A,B) = true :- A = B. 14 | /* 15 | This is the rule that's more dangerous. 16 | eq(A,B) = true :- A = B. 17 | */ 18 | /* derived? Not in case eq(A,B) = false. */ 19 | eq(eq(A,B),C) <-> eq(A,eq(B,C)). 20 | eq(A,B) <- eq(B,A). 21 | not(eq(A,B)) <-> eq(not(A),B). 22 | /* eq(A,A) = true :- A. already implied by reflection */ 23 | eq(false, not(true)). 24 | eq(false, true) = false. 25 | eq(true, false) = false. 26 | 27 | false = not(true). 28 | eq(not(eq(p,q)), not(p)). 29 | ?- eq(not(eq(p,q)), not(p)) = q. 30 | /* ?- A. */ 31 | 32 | /* ?- eq(eq(A,B),C) = eq(A,eq(B,C)). 33 | ?- eq(A,B) = eq(B,A). 34 | ?- eq(A,A) = A. 35 | ?- eq(false, not(true)). 36 | ?- eq(false,true) = false. 37 | ?- false = true. 38 | ?- eq(p,q). */ 39 | 40 | r = true. 41 | or(p,q). 42 | ?- or(r,q) = true. /* How does it know this. Color me actually kind of impressed. */ 43 | ?- false = true. /* sanity check */ 44 | 45 | true <- or(P, not(P)). 46 | or(A,or(B,C)) <-> or(or(A,B),C). 47 | or(A,B) <- or(B,A). 48 | A <- or(A,A). 49 | /* Hmmm. Does this type check to the left. You can only `or` booleans */ 50 | or(A, eq(B,C)) <-> eq( or(A,B), or(A,C) ). 51 | /* what. This did not crash */ 52 | or(P, not(P)) = true. 53 | 54 | /* redundant */ 55 | or(false,false). 56 | ?- or(false,false) = false. 57 | ?- or(true,false) = true. 58 | ?- or(true,true) = true. 59 | ?- or(false,true) = true. 60 | 61 | eq(or(P,Q),Q) <-> eq(and(P,Q),P). 62 | 63 | ?- and(true,false) = false. 64 | ?- and(true,true) = true. 65 | ?- and(false,false) = false. 66 | 67 | and(q,p). 68 | 69 | ?- and(p,q) = and(q,p). 70 | /* 71 | and(A,B) = true :- true = A, true = B. 72 | */ 73 | 74 | imp(P,Q) <-> eq(or(P,Q), Q). 75 | 76 | imp(p,p). 77 | ?- imp(p,p) = true. 78 | and(p,or(p,q)). 79 | ?- and(p,or(p,q)) = p. 80 | imp(p, imp(q, p)). 81 | ?- imp(p, imp(q, p)) = true. 82 | 83 | imp(imp(s, imp(p,q)),imp(imp(s,p),imp(s,q))). 84 | ?- imp(imp(s, imp(p,q)),imp(imp(s,p),imp(s,q))) = true. 85 | 86 | ?- or(eq(p,true),eq(p,false)) = true. 87 | or(eq(p,red),eq(p,blue)) = true. 88 | ?- eq(p,or(red,blue)). /* Why not? */ 89 | 90 | /* 91 | dijstra's brakcet. 92 | bracket(eq(a,b)) = true. 93 | bracket(). 94 | */ -------------------------------------------------------------------------------- /examples/mem.pl: -------------------------------------------------------------------------------- 1 | 2 | /* inequality predicate neq */ 3 | /* symmettric */ 4 | neq(Y,X) :- neq(X,Y). 5 | 6 | /* blow up the world if something equal is said to be not equal. */ 7 | false :- neq(X,X). 8 | 9 | /* injective functions */ 10 | neq(neg(X), neg(Y)) :- neq(X,Y). 11 | neq(add(Z,X), add(Z,Y)) :- neq(X,Y), add(Z,X). 12 | neq(mul(Z,X), mul(Z,Y)) :- neq(X,Y), mul(Z,X), mul(Z,Y), notzero(Z). 13 | 14 | 15 | /* 16 | Smtlib theory of arrays 17 | https://smtlib.cs.uiowa.edu/theories-ArraysEx.shtml 18 | http://smtlib.cs.uiowa.edu/version1/theories/Arrays.smt 19 | */ 20 | 21 | 22 | /* select grabs stored value */ 23 | V <- select(A ,store(A, V, Mem)). 24 | /* select ignores diffierent addresses */ 25 | select(A1,Mem) = E :- select(A1,store(A2,V,Mem)) = E, neq(A1,A2). 26 | /* non aliasing writes commute */ 27 | store(A2,V2, store(A1,V1,Mem)) = E :- store(A1,V1, store(A2,V2,Mem)) = E, neq(A1,A2). 28 | /* Aliasing Writes destroy old value. */ 29 | store(A, V1, Mem) <- store(A, V1, store(A,V2,Mem)). 30 | 31 | 32 | zero <- select(A,emp). 33 | 34 | 35 | /* 36 | Extensionality axioms 37 | (forall i, select(i, Mem) = select(i, Mem2)) -> Mem = Mem2. 38 | 39 | Drinker's address. 40 | 41 | neq(Mem1,Mem2) -> neq(select(diff(Mem1, Mem2), Mem1), select(diff(Mem1,Mem2), Mem2) 42 | 43 | 44 | 45 | */ 46 | 47 | 48 | /* Simple properties of arithmetic */ 49 | add(X,Y) <- add(Y,X). 50 | add(add(X,Y),Z) <-> add(X,add(Y,Z)). 51 | mul(X,Y) <- mul(Y,X). 52 | mul(mul(X,Y),Z) <-> mul(X, mul(Y,Z)). 53 | /* distributive */ 54 | mul(X,add(Y,Z)) <-> add(mul(X,Y),mul(X,Z)). 55 | X <- mul(one, X). 56 | X <- add(zero, X). 57 | 58 | /* start with registers being different */ 59 | neq(r1,r2). 60 | neq(r2,r3). 61 | neq(r1,r3). 62 | 63 | /* initialize egraph with appropiate term */ 64 | select(r1, store(r1, a, store(r0, b, mem0))). 65 | ?- select(r1, store(r1, a, store(r0, b, mem0))) = E. 66 | 67 | select(r3, store(r1, a, store(r1, b, mem0))). 68 | ?- select(r3, store(r1, a, store(r1, b, mem0))) = E. 69 | 70 | 71 | select(add(r1,r2), store(add(zero,add(r2,r1)), a, store(r0, b, mem0))). 72 | ?- select(add(r1,r2), store(add(zero,add(r2,r1)), a, store(r0, b, mem0))) = E. 73 | 74 | select(add(r0,r3), store(add(r1,r0), a, store(add(r0,r1), b, mem0))) . 75 | ?- select(add(r0,r3), store(add(r1,r0), a, store(add(r0,r1), b, mem0))) = E. 76 | 77 | /* 78 | 79 | bitvectors 80 | https://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml 81 | 82 | extract and concat will require integer reasoning. Or tagging of all bitvectors by size? 83 | Or len(A) = N? 84 | Or do bitvectors as lists? 85 | 86 | A = E :- extract(0, N,concat(A,B)) = E, len(A) = N. 87 | :- extract(0,N,A), len(A) = N. 88 | 89 | A <- extract( concat( A,B) ) 90 | zero <- extract(nothing, A) 91 | A <- concat(zero,A). 92 | B <- concat(B,zero). 93 | 94 | zero is zro length bitvector 95 | 96 | 97 | */ 98 | 99 | /* ?- neq(R1, R2). */ 100 | -------------------------------------------------------------------------------- /examples/pb2.pl: -------------------------------------------------------------------------------- 1 | type(id(A)) = hom(A,A) :- ob = type(A). 2 | /* ob = type(A) is probabnly slightly more efficient to search for than type(A) = ob */ 3 | F <- comp(id(A), F). 4 | F <- comp(F, id(A)). 5 | 6 | comp(F,id(B)) = F :- type(F) = hom(A,B). 7 | comp(id(A),F) = F :- type(F) = hom(A,B). 8 | /* associativity of composition */ 9 | comp(comp(F,G),H) <-> comp(F, comp(G,H)). 10 | 11 | /* Composition exists if types work out */ 12 | type(comp(F,G)) = hom(A,C) :- hom(A,B) = type(F), hom(B,C) = type(G). 13 | 14 | /* all pullbacks exist */ 15 | type(pb1(F,G)) = hom( pbo(F,G), A ), 16 | type(pb2(F,G)) = hom( pbo(F,G), B ), 17 | type(pbo(F,G)) = ob, 18 | comp(pb1(F,G),F) = comp(pb2(F,G),G) 19 | :- hom(A,C) = type(F), hom(B,C) = type(G). 20 | 21 | 22 | 23 | /* triangles */ 24 | comp(univ(F,G,H,K),pb1(F,G)) = H, 25 | comp(univ(F,G,H,K),pb2(F,G)) = K, 26 | type(univ(F,G,H,K)) = hom(Z,pbo(F,G)) 27 | :- comp(H,F) = comp(K,G), type(F) = hom(A,B), type(H) = hom(Z,A). 28 | 29 | /* unique */ 30 | univ(F,G,H,K) = U :- comp(H,F) = comp(K,G), H = comp(U,pb1(F,G)), K = comp(U,pb2(F,G)). 31 | /* 32 | 33 | a <-H- 34 | F | | K 35 | v v 36 | c <-G -b J d 37 | */ 38 | /* pb1(id(A),id(A)) */ 39 | type(a) = ob. 40 | /* type(b) = ob. 41 | type(c) = ob. */ 42 | /* type(d) = ob. */ 43 | 44 | /* type(f) = hom(a,c). 45 | type(g) = hom(b,c). */ 46 | /* type(j) = hom(d,b). */ 47 | ?- comp(id(a),id(a)) = id(a). 48 | ?- f = id(a). 49 | ?- comp(pb1(f,g),f) = comp(pb2(f,g),g). 50 | ?- pb1(id(a), id(a)) = id(a). /* Hmm. actually this is not a theorem */ 51 | 52 | 53 | /* Why? A useful canoncial choice to prevent explosion. But does*/ 54 | pbo(id(a),id(a)) = a. 55 | pb1(id(a),id(a)) = id(a). 56 | ?- pb2(id(a),id(a)) = id(a). 57 | ?- pbo(id(a),id(a)) = a. -------------------------------------------------------------------------------- /examples/pb3.pl: -------------------------------------------------------------------------------- 1 | type(id(A)) = hom(A,A) :- ob = type(A). 2 | /* ob = type(A) is probabnly slightly more efficient to search for than type(A) = ob */ 3 | F <- comp(id(A), F). 4 | F <- comp(F, id(A)). 5 | 6 | comp(F,id(B)) = F :- type(F) = hom(A,B). 7 | comp(id(A),F) = F :- type(F) = hom(A,B). 8 | /* associativity of composition */ 9 | comp(comp(F,G),H) <-> comp(F, comp(G,H)). 10 | comp(F,comp(G,H)) <- comp(F,G,H). 11 | /* Composition exists if types work out */ 12 | type(comp(F,G)) = hom(A,C) :- hom(A,B) = type(F), hom(B,C) = type(G). 13 | 14 | /* pullback is square */ 15 | comp(H,F) = comp(K,G) :- pullback(F,G,H,K). 16 | 17 | 18 | /* universal morphism exists. triangles 19 | is univ a function of H1 K1? 20 | */ 21 | comp(univ(F,G,H,K),H1) = H, 22 | comp(univ(F,G,H,K),K1) = K, 23 | type(univ(F,G,H,K)) = hom(Z,E) 24 | :- pullback(F,G,H1,K1), comp(H,F) = comp(K,G), type(F) = hom(A,B), type(H) = hom(Z,A), type(H1) = hom(E,A). 25 | 26 | /* unique */ 27 | univ(F,G,H,K) = U :- pullback(F,G,H1,K1), comp(H,F) = comp(K,G), H = comp(U,H1), K = comp(U,K1). 28 | 29 | G = K :- monic(F), comp(G,F) = comp(K,F). 30 | /* 31 | 32 | a <-H- d p a1 33 | F | | K Q 34 | v v 35 | c <-G -b J e 36 | */ 37 | 38 | /* ideally users don't have to fill out this table */ 39 | type(a) = ob. 40 | type(b) = ob. 41 | type(c) = ob. 42 | type(d) = ob. 43 | type(e) = ob. 44 | type(a1) = ob. 45 | 46 | 47 | type(f) = hom(a,c). 48 | type(g) = hom(b,c). 49 | type(h) = hom(d,a). 50 | type(k) = hom(d,b). 51 | /* 52 | type(j) = hom(e,b). 53 | type(p) = hom(a1,d). 54 | type(q) = hom(a1,e). 55 | */ 56 | monic(f). 57 | 58 | 59 | pullback(f,g,h,k). 60 | 61 | /* 62 | Well defined-ness of comp. Convenient for lowering the type annotation requirements. An actual typechker would be desirable 63 | in my opinion since types are easy. 64 | 65 | type(dom(f)) = ob, 66 | type(cod(f)) = ob, 67 | type(f) = hom(dom(f),cod(f)), 68 | cod(F) = dom(G), 69 | type(dom(G)) = ob, 70 | type(cod(G)) = ob, 71 | type(G) = hom(dom(G),cod(G)), 72 | :- comp(F,G). 73 | 74 | monic(F) :- forall(g,h, comp(g,F) = comp(h,F) => g = h ). 75 | ?- monic(h). 76 | */ 77 | /* pullback(k,j,p,q). 78 | 79 | 80 | comp(p,h,f). 81 | comp(q,j,g). 82 | ?- comp(p,h,f) = comp(q,j,g). 83 | */ 84 | 85 | 86 | type(z) = ob. 87 | type(p) = hom(z,d). 88 | type(q) = hom(z,d). 89 | comp(p,k) = comp(q,k). 90 | 91 | ?- p = q. 92 | ?- f = g. 93 | ?- p = f. 94 | ?- k = h. 95 | ?- k = g. 96 | ?- type(comp(p,h)) = T. 97 | ?- type(id(a)) = hom(a,a). 98 | ?- comp(comp(id(a), h), k) = T. 99 | -------------------------------------------------------------------------------- /examples/pb_compose.pl: -------------------------------------------------------------------------------- 1 | /* https://proofwiki.org/wiki/Pullback_Lemma 2 | https://ncatlab.org/nlab/show/pasting+law+for+pullbacks 3 | 4 | */ 5 | 6 | /* Standard categorical definitions */ 7 | type(id(A)) = hom(A,A) :- ob = type(A). 8 | 9 | F <- comp(id(A), F). 10 | F <- comp(F, id(A)). 11 | 12 | comp(F,id(B)) = F :- type(F) = hom(A,B). 13 | comp(id(A),F) = F :- type(F) = hom(A,B). 14 | /* associativity of composition */ 15 | comp(comp(F,G),H) <-> comp(F, comp(G,H)). 16 | comp(F,comp(G,H)) <-> comp(F,G,H). 17 | /* Composition exists if types work out */ 18 | type(comp(F,G)) = hom(A,C) :- hom(A,B) = type(F), hom(B,C) = type(G). 19 | 20 | /* Pullback definitions */ 21 | /* pullback is square */ 22 | comp(H,F) = comp(K,G) :- pullback(F,G,H,K). 23 | 24 | /* universal morphism exists. Triangles commute 25 | TODO: is univ a function of H1 K1? 26 | */ 27 | comp(univ(F,G,H,K),H1) = H, 28 | comp(univ(F,G,H,K),K1) = K, 29 | type(univ(F,G,H,K)) = hom(Z,E) 30 | :- pullback(F,G,H1,K1), comp(H,F) = comp(K,G), type(F) = hom(A,B), type(H) = hom(Z,A), type(H1) = hom(E,A). 31 | 32 | /* uniqueness of universal morphism */ 33 | univ(F,G,H,K) = U :- pullback(F,G,H1,K1), comp(H,F) = comp(K,G), H = comp(U,H1), K = comp(U,K1). 34 | 35 | 36 | /* 37 | 38 | a <-H- d p a1 39 | F | | K Q 40 | v v 41 | c <-G -b J e 42 | */ 43 | 44 | /* 45 | Ideally users don't have to fill out this table. 46 | It is obnoxious, obvious, and error prone. 47 | */ 48 | type(a) = ob. 49 | type(b) = ob. 50 | type(c) = ob. 51 | type(d) = ob. 52 | type(e) = ob. 53 | type(a1) = ob. 54 | 55 | 56 | type(f) = hom(a,c). 57 | type(g) = hom(b,c). 58 | type(h) = hom(d,a). 59 | type(k) = hom(d,b). 60 | type(j) = hom(e,b). 61 | type(p) = hom(a1,d). 62 | type(q) = hom(a1,e). 63 | 64 | 65 | pullback(f,g,h,k). 66 | pullback(k,j,p,q). 67 | 68 | 69 | /* 70 | Is big square a pullback? 71 | In some world it would be nice to reuse the above definition. I don't know how to do this 72 | 73 | 1. Does the square commute? 74 | */ 75 | ?- comp(p,h,f) = comp(q,j,g). 76 | 77 | 78 | /* 79 | 2: Given another square, is there a morphism that makes the triangles commute 80 | */ 81 | 82 | /* 83 | -- r -- z 84 | 85 | a <-H- d p a1 86 | F | | K Q w | 87 | v v 88 | c <-G -b J e 89 | */ 90 | type(z) = ob. 91 | type(r) = hom(z,a). 92 | type(w) = hom(z,e). 93 | comp(r,f) = comp(w,j,g). /* is square */ 94 | /* exists a morphism for which triangles commute */ 95 | ?- comp(U,p,h) = r. 96 | ?- comp(U,q) = w. 97 | 98 | /* 3: and it is unique? 99 | 100 | Some questions here about how to phrase this. 101 | 102 | Is this right? or am I positing that the require morphism already exists with this? 103 | I think the uniqueness of the eclass actually might do it. 104 | That's interesting. 105 | ALso there might be a 106 | (build morphism, write it down if you find it, now instanatiate it explcitly in this query) semanatics 107 | ?- u2 = univ(k,j, univ(f,g,r,comp(w,j)), w). 108 | Eh that doesn't really matter does it? 109 | Well it matters that it succeeded before I inserted this stuff maybe. 110 | 111 | -? (comp p h f) = (comp q j g) 112 | []; 113 | -? (comp ?U p h) = r 114 | [?U = (univ k j (univ f g r (comp w j)) w)]; 115 | -? (comp ?U q) = w 116 | [?U = (univ k j (univ f g r (comp w j)) w)]; 117 | 118 | 119 | 120 | 121 | type(u2) = hom(z,a1). 122 | comp(u2,comp(p,h)) = r. 123 | comp(u2,q) = w. 124 | ?- u2 = U. 125 | 126 | 127 | 128 | */ 129 | 130 | 131 | -------------------------------------------------------------------------------- /examples/peano.pl: -------------------------------------------------------------------------------- 1 | 2 | nat(z). 3 | nat(s(X)) :- nat(X). 4 | nat(plus(X,Y)) :- nat(X), nat(Y). 5 | 6 | X <-> plus(z,X). /* Hmm. Is this even right? I need X to be a nat */ 7 | plus(s(X),Y) <-> s(plus(X,Y)). 8 | 9 | /* Can I prove these? */ 10 | /* 11 | plus(X,Y) <- plus(Y,X). 12 | plus(plus(X,Y),Z) <-> plus(X,plus(Y,Z)). 13 | plus(X,plus(Y,Z)) <- plus(X,Y,Z). 14 | */ 15 | 16 | /* These solve probably because we enumerate the integers */ 17 | ?- plus(s(z),s(s(z))) = Z. 18 | ?- plus(Y, s(z)) = s(s(s(s(s(z))))). 19 | nat(c). 20 | ?- plus(c,z) = plus(z,c). /* This will never prove. Needs reasoning by cases. */ 21 | 22 | 23 | /* 24 | binary : 25 | i(n) = 2*n+1 26 | o(n) = 2*n 27 | 28 | 2*n + 2 * m = 2*(m+n) 29 | 2*n+1 = i(s(u())) 30 | 31 | 32 | */ -------------------------------------------------------------------------------- /examples/qft.pl: -------------------------------------------------------------------------------- 1 | 2 | /* mul(adag(i), adag(j)) <-> mul(adag(i), adag(j)) 3 | How to deal with delta_ij 4 | */ 5 | 6 | plus(X,Y) <- plus(Y,X). 7 | plus(plus(X,Y),Z) <-> plus(X,plus(Y,Z)). 8 | /* mul(X,Y) <- mul(Y,X). */ 9 | mul(mul(X,Y),Z) <-> mul(X, mul(Y,Z)). 10 | 11 | mul(a , ket0 ) = zero. 12 | O <- dag(dag(O)). 13 | dag(ket0) = bra0. 14 | dag(mul(A,B)) <-> mul(dag(B), dag(A)). 15 | 16 | 17 | mul(dag(a),a) <-> plus(one, mul(a,dag(a)) ). 18 | 19 | /* special multiplication for constants? 20 | This is sort of an analysis built intrinsically into the egraph mechanism. 21 | 22 | const(zero). 23 | const(one). 24 | const( succ(X) ) :- const(X). 25 | 26 | */ 27 | 28 | one = mul(bra0, ket0). 29 | 30 | -------------------------------------------------------------------------------- /examples/rel.pl: -------------------------------------------------------------------------------- 1 | 2 | /* Does it make sense to suppress the types here. Do I have a model? Partial functions? Relations? */ 3 | R <-> comp(id, R). 4 | R <-> comp(R,id). 5 | 6 | G <- comp(snd,fan(F,G)). 7 | F <- comp(fst,fan(F,G)). 8 | /* bifunctor laws? Are these necessary? */ 9 | comp(fan(F,G),H) <-> fan(comp(F,H),comp(G,H)). 10 | 11 | comp(F,comp(G,H)) <-> comp(comp(F,G),H). 12 | comp(F,G,H) <-> comp(F,comp(G,H)). 13 | 14 | dup = fan(id,id). 15 | swap = fan(snd,fst). 16 | par(F,G) <-> fan(comp(F,fst),comp(G,snd)). 17 | 18 | par(f,par(g,h)). 19 | par(par(f,g),h). 20 | comp(snd,par(f,par(g,h))) . 21 | comp(par(h,h),fan(f,g)). 22 | fan(comp(h,f),comp(h,g)). 23 | comp(par(f,g),par(h,k)). 24 | par(comp(f,h), comp(g,k)). 25 | 26 | ?- comp(par(h,h),fan(f,g)) = fan(comp(h,f),comp(h,g)). 27 | ?- comp(par(f,g),par(h,k)) = par(comp(f,h), comp(g,k)). 28 | ?- comp(snd,par(f,par(g,h))) = A. 29 | ?- f = g. /* sanity check */ 30 | 31 | 32 | /* comp(split(F,G), left) */ 33 | 34 | /* curry uncurry apply */ 35 | -------------------------------------------------------------------------------- /examples/ski.pl: -------------------------------------------------------------------------------- 1 | /* 2 | Defunctionalized SKI Combinators 3 | https://en.wikipedia.org/wiki/SKI_combinator_calculus 4 | */ 5 | 6 | apply(i,X) <-> i(X). 7 | 8 | apply(k,X) <-> k(X). 9 | apply(k(X),Y) <-> k(X,Y). 10 | 11 | apply(s,X) <-> s(X). 12 | apply(s(X),Y) <-> s(X,Y). 13 | apply(s(X,Y),Z) <-> s(X,Y,Z). 14 | 15 | X <- i(X). 16 | Y <- k(X,Y). 17 | apply(apply(X,Z),apply(Y,Z)) <- s(X,Y,Z). 18 | 19 | k(i(k),i(i)). 20 | ?- k(i(k),i(i)) = A. 21 | 22 | s(k,k,s). 23 | ?- s(k,k,s) = A. -------------------------------------------------------------------------------- /examples/stream_fusion.pl: -------------------------------------------------------------------------------- 1 | comp(map(F),map(G)) <-> map(comp(F,G)). 2 | comp(filter(F), filter(G)) <-> filter(and(F,G)). 3 | comp(rev,rev) <-> id. 4 | comp(F,comp(G,H)) <-> comp(comp(F,G),H). 5 | F <- comp(id,F). 6 | F <- comp(F,id). 7 | 8 | /* point free style or no? Is bare id ok? */ 9 | 10 | -------------------------------------------------------------------------------- /examples/talk.pl: -------------------------------------------------------------------------------- 1 | /* 2 | ************************************************************************** 3 | ************************************************************************** 4 | _ _ ______ 5 | | | (_) | ____| 6 | | | ___ __ _ __ _ _ _ __ __ _ __ _ _ __ | |__ __ _ __ _ 7 | | | / _ \ / _` |/ _` | | '_ \ / _` | / _` | '_ \ | __| / _` |/ _` | 8 | | |___| (_) | (_| | (_| | | | | | (_| | | (_| | | | | | |___| (_| | (_| | 9 | |______\___/ \__, |\__, |_|_| |_|\__, | \__,_|_| |_| |______\__, |\__, | 10 | __/ | __/ | __/ | __/ | __/ | 11 | |___/ |___/ |___/ |___/ |___/ 12 | ************************************************************************** 13 | ************************************************************************** 14 | Datalog on E-Graphs 15 | 16 | > Philip Zucker 17 | > pzucker@draper.com 18 | > Draper Laboratory 19 | 20 | 21 | 22 | ------- 23 | Datalog 24 | ------- 25 | 26 | > Bottom up relative of prolog 27 | > Efficiently executable, beautiful semantics 28 | > Databases, Relations, and Logic 29 | > Query RHS, insert LHS 30 | */ 31 | 32 | /* Facts */ 33 | edge(a,b). 34 | edge(b,c). 35 | edge(c,d). 36 | 37 | /* Rules */ 38 | path(X,Y) :- edge(X,Y). 39 | path(X,Z) :- edge(X,Y), path(Y,Z). 40 | 41 | /* Query */ 42 | ?- path(X,Y). 43 | 44 | /* 45 | ------- 46 | Egglog0 47 | ------- 48 | > Inspired by Relational E-Matching 49 | > E-Graphs are a Database 50 | > This database holds terms and equality relation 51 | > Supports ordinary datalog with terms 52 | > Rules: query RHS (e-matching multipattern), instantiate and insert LHS 53 | > Special equality `_=_` is E-graph equality / union find 54 | > Queries: e-match and extract all results. 55 | */ 56 | 57 | X = E :- add(X,zero) = E. 58 | add(Y,X) = E :- add(X,Y) = E. 59 | add(Y,X) <- add(X,Y). /* syntax sugar */ 60 | 61 | add(zero,a). 62 | ?- add(zero,a) = Z. 63 | 64 | /* 65 | ------------- 66 | Multipatterns 67 | ------------- 68 | > Guards check, Multipatterns bind. 69 | > Threads the e-matching compiler environment binding between patterns 70 | > Upstreamed to egg https://github.com/egraphs-good/egg/pull/168 71 | 72 | */ 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | /* 86 | *********************************************** 87 | *********************************************** 88 | ______ _ 89 | | ____| | | 90 | | |__ __ ____ _ _ __ ___ _ __ | | ___ ___ 91 | | __| \ \/ / _` | '_ ` _ \| '_ \| |/ _ \/ __| 92 | | |____ > < (_| | | | | | | |_) | | __/\__ \ 93 | |______/_/\_\__,_|_| |_| |_| .__/|_|\___||___/ 94 | | | 95 | |_| 96 | *********************************************** 97 | *********************************************** 98 | */ 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | /* 110 | ----------- 111 | Injectivity 112 | ----------- 113 | 114 | > ∀ a b, f(a) = f(b) -> a = b 115 | > ex: Constructors, addition 116 | > Unification 117 | 118 | */ 119 | X = Y, Xs = Ys :- cons(X,Xs) = cons(Y,Ys). 120 | X = Y :- add(X,Z) = add(Y,Z). 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | /* 130 | --------------------- 131 | Memory Simplification 132 | --------------------- 133 | 134 | > Alias Analysis + Simplification 135 | > SMTlib theory of arrays 136 | > Many SMT theories are expressible as Horn Clauses (side conditions) 137 | */ 138 | /*select grabs stored value*/ 139 | V <- select(A, store(A, V, Mem)). 140 | /*select ignores different addresses*/ 141 | select(A1, Mem) = E :- select(A1, store(A2, V, Mem)) = E, neq(A1, A2). 142 | /*non-aliasing writes commute*/ 143 | store(A2, V2, store(A1, V1, Mem)) = E :- store(A1, V1, store(A2, V2, Mem)) = E, neq(A1,A2). 144 | /*Aliasing Writes destroy old value.*/ 145 | store(A, V1, Mem) <- store(A, V1, store(A,V2,Mem)). 146 | 147 | neq(r0,r1). 148 | select(r1, store(r0, v0, store(r1, v1, mem))). 149 | ?- select(r1, store(r0, v0, store(r1, v1, mem))) = T. 150 | 151 | /* 152 | ---------------- 153 | Equation Solving 154 | ---------------- 155 | > Do the same thing to both sides. 156 | > Variable Isolation 157 | > Extract terms without unwanted variables 158 | */ 159 | 160 | add(Z,neg(X)) = Y :- add(X,Y) = Z. 161 | X <- neg(neg(X)). 162 | zero <- add(X,neg(X)). 163 | neg(add(X,Y)) <-> add(neg(X),neg(Y)). 164 | 165 | /* Hack extraction by giving unwanted variable a big name */ 166 | add(a,add(c,my(big(expr)))) = c. 167 | ?- my(big(expr)) = T. 168 | 169 | 170 | 171 | 172 | 173 | /* 174 | ---------- 175 | Reflection 176 | ---------- 177 | > Hypothetical reasoning 178 | > Boolean algebraic reasoning 179 | */ 180 | A = B :- true = eq(A,B). 181 | true = eq(A,B) :- A = B. 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | /* 194 | ------------------------- 195 | Uniqueness Quantification 196 | ------------------------- 197 | > Common in universal constructions in category theory 198 | > Skolemize existentials 199 | `∀ x, P(x) -> ∃ y, Q(x,y)` becomes 200 | `∀ x, P(x) -> Q(x,f(x))` 201 | > Uniqueness Property `∀ a b, P(a) /\ P(b) -> a = b` 202 | is directly expressible in Egglog0. 203 | > See "Pullback of Monic is Monic" and 204 | "Composition of Pullbacks" examples for more detail 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | *************************************************** 218 | *************************************************** 219 | ____ _ _ ___ 220 | / __ \ | | (_) |__ \ 221 | | | | |_ _ ___ ___| |_ _ ___ _ __ ___ ) | 222 | | | | | | | |/ _ \/ __| __| |/ _ \| '_ \/ __|/ / 223 | | |__| | |_| | __/\__ \ |_| | (_) | | | \__ \_| 224 | \___\_\\__,_|\___||___/\__|_|\___/|_| |_|___(_) 225 | *************************************************** 226 | *************************************************** 227 | > Thanks to Yihong Zhang, Yisu Remy Wang, Max Willsey, 228 | Zachary Tatlock, Alessandro Cheli, Cody Roux, 229 | James Fairbanks, and Evan Patterson for their 230 | helpful discussions. 231 | 232 | ------------ 233 | Related Work 234 | ------------ 235 | > Relational E-Matching https://arxiv.org/abs/2108.02290 236 | > Egg-lite 237 | > Souffle Egg https://www.hytradboi.com/2022/writing-part-of-a-compiler-in-datalog 238 | > SMT Multipatterns 239 | */ -------------------------------------------------------------------------------- /examples/types.pl: -------------------------------------------------------------------------------- 1 | /* Typing derivations? */ -------------------------------------------------------------------------------- /examples/whitespace.pl: -------------------------------------------------------------------------------- 1 | /* klkd */ 2 | 3 | 4 | f ( sjk , jkd , sjkd ) . 5 | 6 | 7 | 8 | sjkl( sl , skld ) <-> jklsd ( ds , d , s ,s ) . 9 | 10 | sjkl( sl , skld ) <- jklsd ( ds , d , s ,s ) . 11 | /* :- include("./examples/basics.pl"). */ 12 | 13 | 14 | f(x). 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Egglog0" 3 | --- 4 | 5 | A prototype implementation. You may be looking for 6 | 7 | ## Try It Out! 8 | 9 | 26 | 27 | 60 | 61 | 63 | 64 | 76 | 77 |
78 | 79 | 80 | # What is this? 81 | 82 | A prolog like syntax for interfacing with the egg egraph library. 83 | 84 | 85 | 86 | Github repo: 87 | Read more here: 88 | - [Talk abstract](https://github.com/philzook58/egglog0-talk/blob/main/out.pdf) 89 | - - Early version of egglog, motivations. 90 | - - A simple category theory theorem about pullbacks. 91 | - - Arithmetic, SKI combinator, datalog, lists examples 92 | -------------------------------------------------------------------------------- /pkg/egglog.js: -------------------------------------------------------------------------------- 1 | 2 | let wasm; 3 | 4 | const heap = new Array(32).fill(undefined); 5 | 6 | heap.push(undefined, null, true, false); 7 | 8 | function getObject(idx) { return heap[idx]; } 9 | 10 | let heap_next = heap.length; 11 | 12 | function dropObject(idx) { 13 | if (idx < 36) return; 14 | heap[idx] = heap_next; 15 | heap_next = idx; 16 | } 17 | 18 | function takeObject(idx) { 19 | const ret = getObject(idx); 20 | dropObject(idx); 21 | return ret; 22 | } 23 | 24 | let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); 25 | 26 | cachedTextDecoder.decode(); 27 | 28 | let cachegetUint8Memory0 = null; 29 | function getUint8Memory0() { 30 | if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) { 31 | cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); 32 | } 33 | return cachegetUint8Memory0; 34 | } 35 | 36 | function getStringFromWasm0(ptr, len) { 37 | return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); 38 | } 39 | 40 | function addHeapObject(obj) { 41 | if (heap_next === heap.length) heap.push(heap.length + 1); 42 | const idx = heap_next; 43 | heap_next = heap[idx]; 44 | 45 | heap[idx] = obj; 46 | return idx; 47 | } 48 | 49 | function debugString(val) { 50 | // primitive types 51 | const type = typeof val; 52 | if (type == 'number' || type == 'boolean' || val == null) { 53 | return `${val}`; 54 | } 55 | if (type == 'string') { 56 | return `"${val}"`; 57 | } 58 | if (type == 'symbol') { 59 | const description = val.description; 60 | if (description == null) { 61 | return 'Symbol'; 62 | } else { 63 | return `Symbol(${description})`; 64 | } 65 | } 66 | if (type == 'function') { 67 | const name = val.name; 68 | if (typeof name == 'string' && name.length > 0) { 69 | return `Function(${name})`; 70 | } else { 71 | return 'Function'; 72 | } 73 | } 74 | // objects 75 | if (Array.isArray(val)) { 76 | const length = val.length; 77 | let debug = '['; 78 | if (length > 0) { 79 | debug += debugString(val[0]); 80 | } 81 | for(let i = 1; i < length; i++) { 82 | debug += ', ' + debugString(val[i]); 83 | } 84 | debug += ']'; 85 | return debug; 86 | } 87 | // Test for built-in 88 | const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); 89 | let className; 90 | if (builtInMatches.length > 1) { 91 | className = builtInMatches[1]; 92 | } else { 93 | // Failed to match the standard '[object ClassName]' 94 | return toString.call(val); 95 | } 96 | if (className == 'Object') { 97 | // we're a user defined class or Object 98 | // JSON.stringify avoids problems with cycles, and is generally much 99 | // easier than looping through ownProperties of `val`. 100 | try { 101 | return 'Object(' + JSON.stringify(val) + ')'; 102 | } catch (_) { 103 | return 'Object'; 104 | } 105 | } 106 | // errors 107 | if (val instanceof Error) { 108 | return `${val.name}: ${val.message}\n${val.stack}`; 109 | } 110 | // TODO we could test for more things here, like `Set`s and `Map`s. 111 | return className; 112 | } 113 | 114 | let WASM_VECTOR_LEN = 0; 115 | 116 | let cachedTextEncoder = new TextEncoder('utf-8'); 117 | 118 | const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' 119 | ? function (arg, view) { 120 | return cachedTextEncoder.encodeInto(arg, view); 121 | } 122 | : function (arg, view) { 123 | const buf = cachedTextEncoder.encode(arg); 124 | view.set(buf); 125 | return { 126 | read: arg.length, 127 | written: buf.length 128 | }; 129 | }); 130 | 131 | function passStringToWasm0(arg, malloc, realloc) { 132 | 133 | if (realloc === undefined) { 134 | const buf = cachedTextEncoder.encode(arg); 135 | const ptr = malloc(buf.length); 136 | getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); 137 | WASM_VECTOR_LEN = buf.length; 138 | return ptr; 139 | } 140 | 141 | let len = arg.length; 142 | let ptr = malloc(len); 143 | 144 | const mem = getUint8Memory0(); 145 | 146 | let offset = 0; 147 | 148 | for (; offset < len; offset++) { 149 | const code = arg.charCodeAt(offset); 150 | if (code > 0x7F) break; 151 | mem[ptr + offset] = code; 152 | } 153 | 154 | if (offset !== len) { 155 | if (offset !== 0) { 156 | arg = arg.slice(offset); 157 | } 158 | ptr = realloc(ptr, len, len = offset + arg.length * 3); 159 | const view = getUint8Memory0().subarray(ptr + offset, ptr + len); 160 | const ret = encodeString(arg, view); 161 | 162 | offset += ret.written; 163 | } 164 | 165 | WASM_VECTOR_LEN = offset; 166 | return ptr; 167 | } 168 | 169 | let cachegetInt32Memory0 = null; 170 | function getInt32Memory0() { 171 | if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) { 172 | cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); 173 | } 174 | return cachegetInt32Memory0; 175 | } 176 | /** 177 | * @param {string} s 178 | * @returns {string} 179 | */ 180 | export function run_wasm_simple(s) { 181 | try { 182 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 183 | var ptr0 = passStringToWasm0(s, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 184 | var len0 = WASM_VECTOR_LEN; 185 | wasm.run_wasm_simple(retptr, ptr0, len0); 186 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 187 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 188 | return getStringFromWasm0(r0, r1); 189 | } finally { 190 | wasm.__wbindgen_add_to_stack_pointer(16); 191 | wasm.__wbindgen_free(r0, r1); 192 | } 193 | } 194 | 195 | /** 196 | * @param {string} s 197 | * @param {boolean} proof 198 | * @param {boolean} graph 199 | * @returns {string} 200 | */ 201 | export function run_wasm(s, proof, graph) { 202 | try { 203 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 204 | var ptr0 = passStringToWasm0(s, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 205 | var len0 = WASM_VECTOR_LEN; 206 | wasm.run_wasm(retptr, ptr0, len0, proof, graph); 207 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 208 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 209 | return getStringFromWasm0(r0, r1); 210 | } finally { 211 | wasm.__wbindgen_add_to_stack_pointer(16); 212 | wasm.__wbindgen_free(r0, r1); 213 | } 214 | } 215 | 216 | function handleError(f, args) { 217 | try { 218 | return f.apply(this, args); 219 | } catch (e) { 220 | wasm.__wbindgen_exn_store(addHeapObject(e)); 221 | } 222 | } 223 | 224 | async function load(module, imports) { 225 | if (typeof Response === 'function' && module instanceof Response) { 226 | if (typeof WebAssembly.instantiateStreaming === 'function') { 227 | try { 228 | return await WebAssembly.instantiateStreaming(module, imports); 229 | 230 | } catch (e) { 231 | if (module.headers.get('Content-Type') != 'application/wasm') { 232 | console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); 233 | 234 | } else { 235 | throw e; 236 | } 237 | } 238 | } 239 | 240 | const bytes = await module.arrayBuffer(); 241 | return await WebAssembly.instantiate(bytes, imports); 242 | 243 | } else { 244 | const instance = await WebAssembly.instantiate(module, imports); 245 | 246 | if (instance instanceof WebAssembly.Instance) { 247 | return { instance, module }; 248 | 249 | } else { 250 | return instance; 251 | } 252 | } 253 | } 254 | 255 | async function init(input) { 256 | if (typeof input === 'undefined') { 257 | input = new URL('egglog_bg.wasm', import.meta.url); 258 | } 259 | const imports = {}; 260 | imports.wbg = {}; 261 | imports.wbg.__wbindgen_object_drop_ref = function(arg0) { 262 | takeObject(arg0); 263 | }; 264 | imports.wbg.__wbindgen_string_new = function(arg0, arg1) { 265 | var ret = getStringFromWasm0(arg0, arg1); 266 | return addHeapObject(ret); 267 | }; 268 | imports.wbg.__wbg_now_e3cde1a07a4d3e37 = function(arg0) { 269 | var ret = getObject(arg0).now(); 270 | return ret; 271 | }; 272 | imports.wbg.__wbg_newnoargs_ac91a24e57fcaec8 = function(arg0, arg1) { 273 | var ret = new Function(getStringFromWasm0(arg0, arg1)); 274 | return addHeapObject(ret); 275 | }; 276 | imports.wbg.__wbg_get_ed86ad8212b73674 = function() { return handleError(function (arg0, arg1) { 277 | var ret = Reflect.get(getObject(arg0), getObject(arg1)); 278 | return addHeapObject(ret); 279 | }, arguments) }; 280 | imports.wbg.__wbg_call_9e1eb05d905a21d9 = function() { return handleError(function (arg0, arg1) { 281 | var ret = getObject(arg0).call(getObject(arg1)); 282 | return addHeapObject(ret); 283 | }, arguments) }; 284 | imports.wbg.__wbindgen_object_clone_ref = function(arg0) { 285 | var ret = getObject(arg0); 286 | return addHeapObject(ret); 287 | }; 288 | imports.wbg.__wbg_self_bce917bbd61b0be0 = function() { return handleError(function () { 289 | var ret = self.self; 290 | return addHeapObject(ret); 291 | }, arguments) }; 292 | imports.wbg.__wbg_window_08048ce184ae3496 = function() { return handleError(function () { 293 | var ret = window.window; 294 | return addHeapObject(ret); 295 | }, arguments) }; 296 | imports.wbg.__wbg_globalThis_d6f1ff349571af81 = function() { return handleError(function () { 297 | var ret = globalThis.globalThis; 298 | return addHeapObject(ret); 299 | }, arguments) }; 300 | imports.wbg.__wbg_global_63b22b64d239db75 = function() { return handleError(function () { 301 | var ret = global.global; 302 | return addHeapObject(ret); 303 | }, arguments) }; 304 | imports.wbg.__wbindgen_is_undefined = function(arg0) { 305 | var ret = getObject(arg0) === undefined; 306 | return ret; 307 | }; 308 | imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { 309 | var ret = debugString(getObject(arg1)); 310 | var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 311 | var len0 = WASM_VECTOR_LEN; 312 | getInt32Memory0()[arg0 / 4 + 1] = len0; 313 | getInt32Memory0()[arg0 / 4 + 0] = ptr0; 314 | }; 315 | imports.wbg.__wbindgen_throw = function(arg0, arg1) { 316 | throw new Error(getStringFromWasm0(arg0, arg1)); 317 | }; 318 | 319 | if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { 320 | input = fetch(input); 321 | } 322 | 323 | 324 | 325 | const { instance, module } = await load(await input, imports); 326 | 327 | wasm = instance.exports; 328 | init.__wbindgen_wasm_module = module; 329 | 330 | return wasm; 331 | } 332 | 333 | export default init; 334 | 335 | -------------------------------------------------------------------------------- /pkg/egglog_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/egglog0/75b1ee67f05a3ea56775bda3e577d0cdc6230dc7/pkg/egglog_bg.wasm -------------------------------------------------------------------------------- /src/gensym.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicUsize, Ordering}; 2 | 3 | static GENSYM_COUNTER: AtomicUsize = AtomicUsize::new(0); 4 | 5 | pub fn fresh() -> usize { 6 | GENSYM_COUNTER.fetch_add(1, Ordering::SeqCst) 7 | } 8 | 9 | pub fn gensym(prefix: &str) -> String { 10 | format!("#{}#{}", prefix, fresh()) 11 | } 12 | 13 | #[cfg(test)] 14 | mod tests { 15 | use super::*; 16 | #[test] 17 | fn it_works2() { 18 | assert_eq!(fresh(), 0); 19 | assert_eq!(fresh(), 1); 20 | assert_eq!(gensym("fred"), "#fred#2"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use egg::*; 2 | use std::fmt::Write; 3 | use std::sync::Arc; 4 | mod gensym; 5 | mod logic; 6 | mod types; 7 | pub use types::*; 8 | use Entry::*; 9 | use EqWrap::*; 10 | use Term::*; 11 | //use types::Directive::*; 12 | mod parser; 13 | pub use parser::*; 14 | 15 | fn merge_subst2(s1: &Subst, s2: &Subst) -> Option { 16 | let mut s1 = s1.clone(); 17 | for (v, i) in s2.vec.iter() { 18 | match s1.insert(*v, *i) { 19 | // Oh actually we should check 20 | Some(i1) => { 21 | if *i != i1 { 22 | return None; 23 | } 24 | } 25 | None => (), 26 | } 27 | } 28 | return Some(s1); 29 | } 30 | 31 | fn merge_substs(substs1: &Vec, substs2: &Vec) -> Vec { 32 | // s1s.iter() 33 | // .flat_map(|s1| s2s.iter().filter_map(move |s2| merge_subst2(s1, s2))) 34 | // .collect() 35 | let mut substs = vec![]; // this is merge substs above. 36 | for subst1 in substs1 { 37 | for subst2 in substs2 { 38 | if let Some(subst) = merge_subst2(subst1, subst2) { 39 | substs.push(subst); 40 | } 41 | } 42 | } 43 | substs 44 | } 45 | 46 | #[derive(Debug, PartialEq, Clone)] 47 | struct MultiPattern { 48 | patterns: Vec, 49 | } 50 | use std::fmt; 51 | 52 | impl fmt::Display for MultiPattern 53 | where 54 | S: fmt::Display, 55 | { 56 | fn fmt(&self, buf: &mut fmt::Formatter) -> fmt::Result { 57 | let mut iter = self.patterns.iter(); 58 | if let Some(item) = iter.next() { 59 | write!(buf, "{}", item)?; 60 | for item in iter { 61 | write!(buf, ", {}", item)?; 62 | } 63 | } 64 | Ok(()) 65 | } 66 | } 67 | 68 | impl> Searcher for EqWrap> { 69 | fn search_eclass(&self, egraph: &EGraph, eclass: Id) -> Option> { 70 | match self { 71 | Bare(p) => p.search_eclass(egraph, eclass), 72 | /* match p.search_eclass(egraph, eclass) { 73 | None => None, 74 | Some(matches) => { 75 | Some(SearchMatches { 76 | ast : None, 77 | ..matches 78 | }) 79 | } 80 | } */ 81 | Eq(p1, p2) => { 82 | let matches = p1.search_eclass(egraph, eclass)?; 83 | let matches2 = p2.search_eclass(egraph, eclass)?; 84 | let substs = merge_substs(&matches.substs, &matches2.substs); 85 | if substs.len() == 0 { 86 | None 87 | } else { 88 | Some(SearchMatches { 89 | eclass, 90 | substs, 91 | ast: None, 92 | }) 93 | } 94 | } 95 | } 96 | } 97 | fn vars(&self) -> Vec { 98 | match self { 99 | Bare(p) => p.vars(), 100 | Eq(l, r) => { 101 | let mut vars = l.vars(); 102 | vars.extend(r.vars()); 103 | vars 104 | } 105 | } 106 | } 107 | } 108 | 109 | impl, P: Searcher> Searcher for MultiPattern

{ 110 | fn search_eclass(&self, egraph: &EGraph, eclass: Id) -> Option> { 111 | let mut iter = self.patterns.iter(); 112 | let firstpat = iter.next()?; 113 | let searchmatches = firstpat.search_eclass(egraph, eclass)?; 114 | let mut matches = searchmatches.substs; 115 | for pat in iter { 116 | let mut temp_matches = vec![]; 117 | for pmatch in pat.search(egraph) { 118 | temp_matches.append(&mut merge_substs(&matches, &pmatch.substs)); 119 | } 120 | matches = temp_matches; 121 | } 122 | Some(SearchMatches { 123 | eclass, 124 | substs: matches, 125 | ast: None, 126 | }) 127 | } 128 | fn vars(&self) -> Vec { 129 | let mut pats: Vec<_> = self 130 | .patterns 131 | .iter() 132 | .flat_map(|p| >::vars(p)) 133 | .collect(); 134 | pats.sort(); 135 | pats.dedup(); 136 | pats 137 | } 138 | } 139 | 140 | impl Applier for MultiPattern 141 | where 142 | L: Language, 143 | N: Analysis, 144 | A: Applier, 145 | { 146 | fn apply_one( 147 | &self, 148 | egraph: &mut EGraph, 149 | eclass: Id, 150 | subst: &Subst, 151 | searcher_ast: Option<&PatternAst>, 152 | rule_name: Arc, 153 | ) -> Vec { 154 | let mut added = vec![]; // added are union updates, of which there are none 155 | for applier in &self.patterns { 156 | added.extend(applier.apply_one(egraph, eclass, subst, searcher_ast, rule_name.clone())); 157 | } 158 | added 159 | } 160 | 161 | fn apply_matches( 162 | &self, 163 | egraph: &mut EGraph, 164 | matches: &[SearchMatches], 165 | rule_name: Arc, 166 | ) -> Vec { 167 | let mut added = vec![]; 168 | for applier in &self.patterns { 169 | added.extend(applier.apply_matches(egraph, matches, rule_name.clone())); 170 | } 171 | added 172 | } 173 | 174 | fn vars(&self) -> Vec { 175 | let mut vars = vec![]; 176 | for applier in &self.patterns { 177 | vars.extend(applier.vars()); 178 | } 179 | // Is this necessary? How is var even used? 180 | vars.sort(); 181 | vars.dedup(); 182 | vars 183 | } 184 | } 185 | 186 | impl Applier for EqWrap> 187 | where 188 | L: Language, 189 | N: Analysis, 190 | { 191 | fn apply_one( 192 | &self, 193 | _egraph: &mut EGraph, 194 | _eclass: Id, 195 | _subst: &Subst, 196 | searcher_ast: Option<&PatternAst>, 197 | rule_name: Arc, 198 | ) -> Vec { 199 | // self.0.apply_one(egraph, eclass, subst) 200 | panic!("EqApply.apply_one was called"); 201 | } 202 | 203 | // Could copy using apply_pat for better efficiency 204 | fn apply_matches( 205 | &self, 206 | egraph: &mut EGraph, 207 | matches: &[SearchMatches], 208 | rule_name: Arc, 209 | ) -> Vec { 210 | match self { 211 | Bare(a) => 212 | //a.apply_matches(egraph, matches, rule_name) 213 | { 214 | // Ignoreapply semantics 215 | let mut added = vec![]; 216 | for mat in matches { 217 | for subst in &mat.substs { 218 | let ast = a.ast.as_ref(); 219 | let mut id_buf = vec![0.into(); ast.len()]; 220 | let id = apply_pat(&mut id_buf, ast, egraph, subst); 221 | added.push(id) 222 | // root is just ignored? 223 | } 224 | } 225 | // TODO: REALLY THINK ABOUT THIS!!!! 226 | added 227 | } 228 | 229 | Eq(l, r) => { 230 | let mut added = vec![]; // added are union updates, of which there are none 231 | for mat in matches { 232 | for subst in &mat.substs { 233 | // This should be ok because we know they are patterns. Not very safe. 234 | //let id1 = l.apply_one(egraph, 0.into(), subst, None, rule_name)[0]; 235 | //let id2 = r.apply_one(egraph, 0.into(), subst, None, rule_name)[0]; 236 | let (to, did_something) = 237 | egraph.union_instantiations(&l.ast, &r.ast, subst, rule_name.clone()); 238 | if did_something { 239 | added.push(to) 240 | } 241 | } 242 | } 243 | added 244 | } 245 | } 246 | } 247 | 248 | fn vars(&self) -> Vec { 249 | match self { 250 | Bare(a) => a.vars(), 251 | Eq(l, r) => { 252 | let mut vars = l.vars(); 253 | vars.extend(r.vars()); 254 | vars 255 | } 256 | } 257 | } 258 | } 259 | 260 | /* 261 | struct EqApply { 262 | l: Pattern, 263 | r: Pattern, 264 | } 265 | // Hmm. Should I dfefine an applier for EqWrap instead? 266 | impl Applier for EqApply 267 | where 268 | L: Language, 269 | N: Analysis, 270 | { 271 | fn apply_one(&self, _egraph: &mut EGraph, _eclass: Id, _subst: &Subst, searcher_ast: Option<&PatternAst>, 272 | rule_name: Arc) -> Vec { 273 | // self.0.apply_one(egraph, eclass, subst) 274 | panic!("EqApply.apply_one was called"); 275 | } 276 | 277 | // Could copy using apply_pat for better efficiency 278 | fn apply_matches(&self, egraph: &mut EGraph, matches: &[SearchMatches], rule_name: Arc) -> Vec { 279 | let mut added = vec![]; // added are union updates, of which there are none 280 | for mat in matches { 281 | for subst in &mat.substs { 282 | // This should be ok because we know they are patterns. Not very safe. 283 | let id1 = self.l.apply_one(egraph, 0.into(), subst, None, rule_name)[0]; 284 | let id2 = self.r.apply_one(egraph, 0.into(), subst, None, rule_name)[0]; 285 | let (to, did_something) = egraph.union(id1, id2); 286 | if did_something { 287 | added.push(to) 288 | } 289 | } 290 | } 291 | added 292 | } 293 | 294 | fn vars(&self) -> Vec { 295 | let mut vars = self.l.vars(); 296 | vars.extend(self.r.vars()); 297 | vars 298 | } 299 | } 300 | */ 301 | 302 | // Could probably generalize from pattern. 303 | struct IgnoreApply(Pattern); 304 | 305 | impl Applier for IgnoreApply 306 | where 307 | L: Language, 308 | N: Analysis, 309 | { 310 | fn apply_one( 311 | &self, 312 | egraph: &mut EGraph, 313 | eclass: Id, 314 | subst: &Subst, 315 | searcher_ast: Option<&PatternAst>, 316 | rule_name: Arc, 317 | ) -> Vec { 318 | self.0 319 | .apply_one(egraph, eclass, subst, searcher_ast, rule_name) 320 | } 321 | 322 | // TODO: Could copy using apply_pat from Pattern impl for better efficiency. Need to make it public? 323 | fn apply_matches( 324 | &self, 325 | egraph: &mut EGraph, 326 | matches: &[SearchMatches], 327 | rule_name: Arc, 328 | ) -> Vec { 329 | // let mut added = vec![]; // added are union updates, of which there are none 330 | for mat in matches { 331 | for subst in &mat.substs { 332 | self.apply_one(egraph, 0.into(), subst, None, rule_name.clone()); 333 | // root is just ignored? 334 | } 335 | } 336 | // TODO: REALLY THINK ABOUT THIS!!!! 337 | vec![0.into()] // so a clause may not make more stuff happen. Early saturation. 338 | } 339 | 340 | fn vars(&self) -> Vec { 341 | self.0.vars() 342 | } 343 | } 344 | 345 | type SymExpr = RecExpr; 346 | type SymEGraph = EGraph; 347 | 348 | fn simplify(egraph: &SymEGraph, eid: Id) -> SymExpr { 349 | let extractor = Extractor::new(egraph, AstSize); 350 | let (_best_cost, best) = extractor.find_best(eid); 351 | best 352 | } 353 | 354 | fn print_subst( 355 | buf: &mut T, 356 | egraph: &EGraph, 357 | subst: &Subst, 358 | ) -> Result<(), std::fmt::Error> { 359 | write!(buf, "["); 360 | let mut iter = subst.vec.iter(); 361 | if let Some((k, eid)) = iter.next() { 362 | let best = simplify(egraph, *eid); 363 | write!(buf, "{} = {}", k, best)?; 364 | for (k, eid) in iter { 365 | let best = simplify(egraph, *eid); 366 | write!(buf, ", {} = {}", k, best)?; 367 | } 368 | } 369 | writeln!(buf, "];") 370 | } 371 | 372 | type SymMultiPattern = MultiPattern>>; 373 | 374 | // Current directory and already included set? 375 | #[derive(Debug)] 376 | pub struct Env { 377 | runner: Runner, 378 | rules: Vec>, 379 | queries: Vec>>>, 380 | } 381 | 382 | impl Default for Env { 383 | fn default() -> Self { 384 | Env { 385 | runner: Runner::default(), 386 | queries: vec![], 387 | rules: vec![], 388 | } 389 | } 390 | } 391 | 392 | use std::fs; 393 | /* // For use in the include directive 394 | fn load_file(env: &mut Env, filename: &str) -> Result<(), String> { 395 | match fs::read_to_string(filename) { 396 | Err(e) => Err(format!("Error: file {} not found", filename)), 397 | Ok(contents) => match parse_file(contents) { 398 | Err(e) => Err(format!( 399 | "Error : file {} failed to parse with error : {}", 400 | filename, e 401 | )), 402 | Ok(entries) => { 403 | for entry in entries { 404 | process_entry(env, entry); 405 | } 406 | Ok(()) 407 | } 408 | }, 409 | } 410 | } 411 | */ 412 | 413 | use std::collections::HashSet; 414 | #[derive(Debug, PartialEq, Clone)] 415 | struct Env2 { 416 | freshvars: HashSet, // forall x adds into this set 417 | metavars: HashSet, // exists x add into this set. 418 | // entries : Vec, 419 | } 420 | 421 | impl Env2 { 422 | fn new() -> Self { 423 | Env2 { 424 | freshvars: HashSet::default(), 425 | metavars: HashSet::default(), 426 | } 427 | } 428 | } 429 | 430 | fn interp_term(env: &Env2, t: &Term) -> Term { 431 | match t { 432 | Var(x) => panic!("Impossible"), // should parse formula at groundterms. 433 | Apply(f, args) => { 434 | if args.len() == 0 && env.freshvars.contains(f) { 435 | Var(f.clone()) 436 | } else { 437 | Apply( 438 | f.to_string(), 439 | args.iter().map(|f2| interp_term(env, f2)).collect(), 440 | ) 441 | } 442 | } 443 | } 444 | } 445 | 446 | fn interp_eqwrap(env: &Env2, t: &EqWrap) -> EqWrap { 447 | match t { 448 | Eq(a, b) => Eq(interp_term(env, a), interp_term(env, b)), 449 | Bare(a) => Bare(interp_term(env, a)), 450 | } 451 | } 452 | 453 | fn interp_term_goal(env: &Env2, t: &Term) -> Term { 454 | match t { 455 | Var(x) => panic!("Impossible"), 456 | Apply(f, args) => { 457 | if args.len() == 0 && env.metavars.contains(f) { 458 | Var(f.clone()) 459 | } else { 460 | Apply( 461 | f.to_string(), 462 | args.iter().map(|f2| interp_term_goal(env, f2)).collect(), 463 | ) 464 | } 465 | } 466 | } 467 | } 468 | 469 | fn interp_eqwrap_goal(env: &Env2, t: &EqWrap) -> EqWrap { 470 | match t { 471 | Eq(a, b) => Eq(interp_term_goal(env, a), interp_term_goal(env, b)), 472 | Bare(a) => Bare(interp_term_goal(env, a)), 473 | } 474 | } 475 | use Formula::*; 476 | // We shouldn't be using mutable envs. What am I thinking? 477 | 478 | /* 479 | More imperative style to a program? 480 | enum SearchProgram { 481 | Run, 482 | Clear, 483 | } 484 | */ 485 | 486 | // Module? 487 | #[derive(Debug, Clone)] 488 | pub struct Program { 489 | // eqfacts and facts, or just duplicate for base facts? 490 | facts: Vec<(RecExpr, RecExpr)>, 491 | rules: Vec>, 492 | queries: Vec>>>, 493 | } 494 | 495 | impl Default for Program { 496 | fn default() -> Self { 497 | Program { 498 | facts: vec![], 499 | queries: vec![], 500 | rules: vec![], 501 | } 502 | } 503 | } 504 | 505 | pub fn process_entry_prog(prog: &mut Program, entry: Entry) { 506 | match entry { 507 | Directive(types::Directive::Include(filename)) => (), // load_file(state, &filename).unwrap(), 508 | Fact(Eq(a, b)) => { 509 | let a = recexpr_of_groundterm(&a); 510 | let b = recexpr_of_groundterm(&b); 511 | prog.facts.push((a, b)) 512 | } 513 | Fact(Bare(a)) => { 514 | let a = recexpr_of_groundterm(&a); 515 | prog.facts.push((a.clone(), a)) 516 | } 517 | Clause(head, body) => { 518 | let body = body 519 | .iter() 520 | .map(|eqt| match eqt { 521 | Bare(p) => Bare(pattern_of_term(p)), 522 | Eq(a, b) => Eq(pattern_of_term(a), pattern_of_term(b)), 523 | }) 524 | .collect(); 525 | let searcher = MultiPattern { patterns: body }; 526 | let head = head 527 | .iter() 528 | .map(|eqt| match eqt { 529 | Bare(p) => Bare(pattern_of_term(p)), 530 | Eq(a, b) => Eq(pattern_of_term(a), pattern_of_term(b)), 531 | }) 532 | .collect(); 533 | let applier = MultiPattern { patterns: head }; 534 | prog.rules.push( 535 | egg::Rewrite::new(format!("{}:-{}.", applier, searcher), searcher, applier) 536 | .unwrap(), 537 | ); 538 | } 539 | BiRewrite(a, b) => { 540 | let a = pattern_of_term(&a); 541 | let b = pattern_of_term(&b); 542 | prog.rules 543 | .push(egg::Rewrite::new(format!("{} -> {}", a, b), a.clone(), b.clone()).unwrap()); 544 | prog.rules 545 | .push(egg::Rewrite::new(format!("{} -> {}", b, a), b, a).unwrap()); 546 | } 547 | Rewrite(a, b, body) => { 548 | let a = pattern_of_term(&a); 549 | let b = pattern_of_term(&b); 550 | // consider shortcircuiting case where body = [] 551 | let conditions: Vec<_> = body 552 | .iter() 553 | .map(|e| { 554 | let (l, r) = match e { 555 | Eq(a, b) => (pattern_of_term(&a), pattern_of_term(&b)), 556 | Bare(a) => (pattern_of_term(&a), pattern_of_term(&a)), 557 | }; 558 | ConditionEqual::new(l, r) 559 | }) 560 | .collect(); 561 | let condition = move |egraph: &mut EGraph<_, ()>, eclass: Id, subst: &Subst| { 562 | conditions.iter().all(|c| c.check(egraph, eclass, subst)) 563 | }; 564 | let applier = ConditionalApplier { 565 | condition, 566 | applier: a.clone(), 567 | }; 568 | if body.len() == 0 { 569 | prog.rules 570 | .push(egg::Rewrite::new(format!("{} -> {}", b, a), b, applier).unwrap()); 571 | } else { 572 | prog.rules.push( 573 | egg::Rewrite::new(format!("{} -{:?}> {}", b, body, a), b, applier).unwrap(), 574 | ); 575 | } 576 | } 577 | Query(qs) => { 578 | let qs = qs.iter().map(pattern_of_eqterm).collect(); 579 | prog.queries.push(MultiPattern { patterns: qs }); 580 | } 581 | Axiom(_name, f) => interp_formula(prog, &mut Env2::new(), f), // I should use the name 582 | Goal(f) => interp_goal(prog, &mut Env2::new(), f), 583 | } 584 | } 585 | 586 | // run_program with default Runner 587 | fn run_program2(prog: &Program) -> Vec> { 588 | let runner = Runner::default() 589 | .with_iter_limit(30) 590 | .with_node_limit(10_000) 591 | .with_time_limit(Duration::from_secs(5)); 592 | let (_runner, res) = run_program(prog, runner); 593 | res 594 | } 595 | 596 | fn run_program( 597 | prog: &Program, 598 | mut runner: Runner, 599 | ) -> (Runner, Vec>) { 600 | let egraph = &mut runner.egraph; 601 | for (a, b) in &prog.facts { 602 | //let a_id = egraph.add_expr(&a); 603 | //let b_id = egraph.add_expr(&b); 604 | let a = a.to_string().parse().unwrap(); 605 | let b = b.to_string().parse().unwrap(); 606 | egraph.union_instantiations(&a, &b, &Subst::with_capacity(0), Arc::from("Base Fact")); 607 | } 608 | let runner = runner.run(&prog.rules); 609 | let res = prog 610 | .queries 611 | .iter() 612 | .map(|q| { 613 | let matches = q.search(&runner.egraph); 614 | matches.into_iter().flat_map(|mat| mat.substs).collect() 615 | }) 616 | .collect(); 617 | (runner, res) 618 | } 619 | 620 | use std::collections::HashMap; 621 | fn freshen_formula(vs: Vec, f: &Formula) -> Formula { 622 | let mut freshmap = HashMap::new(); 623 | for v in &vs { 624 | freshmap.insert(v.clone(), gensym::gensym(&v)); 625 | } 626 | fn freshen_term(freshmap: &HashMap, vs: &Vec, t: &Term) -> Term { 627 | match t { 628 | Var(x) => Var(x.clone()), 629 | Apply(f, args) => { 630 | if args.len() == 0 { 631 | if vs.contains(f) { 632 | Apply(freshmap.get(f).unwrap().clone(), vec![]) 633 | } else { 634 | Apply(f.clone(), vec![]) 635 | } 636 | } else { 637 | Apply( 638 | f.clone(), 639 | args.iter() 640 | .map(|arg| freshen_term(freshmap, vs, arg)) 641 | .collect(), 642 | ) 643 | } 644 | } 645 | } 646 | } 647 | fn worker(fm: &HashMap, vs: &Vec, f: &Formula) -> Formula { 648 | if vs.len() == 0 { 649 | f.clone() 650 | } else { 651 | match f { 652 | Impl(hyp, conc) => Impl( 653 | Box::new(worker(fm, vs, &*hyp)), 654 | Box::new(worker(fm, vs, &*conc)), 655 | ), 656 | Conj(fs) => Conj(fs.iter().map(|f| worker(fm, vs, f)).collect()), 657 | Disj(fs) => Disj(fs.iter().map(|f| worker(fm, vs, f)).collect()), 658 | ForAll(vs2, f) => { 659 | let mut vs = vs.clone(); 660 | vs.retain(|v| !vs2.contains(v)); 661 | ForAll(vs2.clone(), Box::new(worker(fm, &vs, &*f))) 662 | } 663 | Exists(vs2, f) => { 664 | let mut vs = vs.clone(); 665 | vs.retain(|v| !vs2.contains(v)); 666 | Exists(vs2.clone(), Box::new(worker(fm, &vs, &*f))) 667 | } 668 | Atom(t) => Atom(match t { 669 | Eq(a, b) => Eq(freshen_term(fm, vs, a), freshen_term(fm, vs, b)), 670 | Bare(a) => Bare(freshen_term(fm, vs, a)), 671 | }), 672 | } 673 | } 674 | } 675 | worker(&freshmap, &vs, f) 676 | } 677 | 678 | fn interp_goal(prog: &mut Program, env: &Env2, formula: Formula) { 679 | match formula { 680 | Conj(fs) => { 681 | let ps = fs 682 | .iter() 683 | .map(|g| match g { 684 | // TODO: How to not insist on Atom here? 685 | // recurse on fresh progs, accumulate all queries into single query. 686 | // (sum of products) 687 | Atom(g) => pattern_of_eqterm(&interp_eqwrap_goal(env, g)), 688 | _ => panic!("unexpected form in goal"), 689 | }) 690 | .collect(); 691 | prog.queries.push(MultiPattern { patterns: ps }) 692 | } 693 | Atom(f) => { 694 | let g = MultiPattern { 695 | patterns: vec![pattern_of_eqterm(&interp_eqwrap_goal(env, &f))], 696 | }; 697 | prog.queries.push(g) 698 | } 699 | Exists(vs, f) => { 700 | let mut env = env.clone(); 701 | // freshen? 702 | env.metavars.extend(vs); // patvars? 703 | interp_goal(prog, &env, *f) 704 | } 705 | //Impl(hyp, conc) => { 706 | //Is this right? Aren't I injecting hypotheses into the program for other queries then? 707 | // These hypotheses need to be retracted. 708 | // interp_formula(prog, env, *hyp); 709 | // interp_goal(prog, env, *conc); 710 | //} 711 | ForAll(vs, f) => { 712 | let f = freshen_formula(vs, &*f); 713 | // freshvars mapping rather than doing eager freshen? 714 | interp_goal(prog, env, f); 715 | } 716 | _ => panic!("no other goal"), 717 | } 718 | } 719 | 720 | // I should make env immutable this is not right as is. 721 | // Or what is even the point of being this fancy 722 | // Could I implement higher order rules by extending symbollang? 723 | /* 724 | P => (Q => R) becomes 725 | P => x 726 | x /\ Q => R 727 | P => (Q => R) should really be P /\ Q => R ? 728 | 729 | (P => Q) => R 730 | 731 | 732 | */ 733 | struct Sequent { 734 | hyps: Vec, 735 | conc: Formula, // sig : Vec 736 | } 737 | /* 738 | The only obligations that can be discharged via egglog are those of the form 739 | ---------------------------------------- (egglog) 740 | atom, atom, hyp => conc, hyp => conc |- conj(q1,q2,q3), conj() 741 | which is indeed the form of a "Program". 742 | 743 | All other transformations should be recorded as an internal proof tree. 744 | 745 | enum Proof { 746 | LAnd(Int, Box, Box), 747 | 748 | 749 | } 750 | */ 751 | /* 752 | fn proof(s : Sequent) -> Vec, Proof { 753 | match s.conc { 754 | Conj(fs) => fs.map(|conc| proof(Sequent{ s.hyps.clone(), conc})), 755 | Disj(_) => fs.map(|conc| ), 756 | ForAll(vs,f) => proof(hyps, freshen_formula(vs, f)) 757 | Impl(hyp,conc) => proof(hyps.push(hyp), conc ) 758 | Atom() => run_query( hyps, q ) 759 | } 760 | } 761 | 762 | 763 | 764 | fn run_query(hyps, q) { 765 | prog = Program::default() 766 | while let Some(hyp) = hyps.pop() { 767 | 768 | } 769 | for hyp in hyps { 770 | match hyp { 771 | Atom(f) => prog.facts.push(f) 772 | Conj(fs) => hyps.extend(f) // Left And 773 | ForAll(vs, f) => patvarize(vs, f) //| is_prim_rewrite(f) => 774 | //| otherwise => 775 | Impl(a,b) | is_prim_pat(a) && is_prim_applier(b) => ReWrite 776 | Impl(a,b) => proof( other_hyps, a ) and proof(hyps + b, q) 777 | 778 | 779 | } 780 | } 781 | } 782 | */ 783 | 784 | fn interp_formula(prog: &mut Program, env: &Env2, formula: Formula) { 785 | match formula { 786 | Atom(a) => { // I can support forall x, f x = g x as a bidirectional rule 787 | match interp_eqwrap(env, &a) { 788 | Eq(a, b) => { 789 | let a = recexpr_of_groundterm(&is_ground(&a).unwrap()); 790 | let b = recexpr_of_groundterm(&is_ground(&b).unwrap()); 791 | prog.facts.push((a, b)) 792 | } 793 | Bare(a) => { 794 | let a = recexpr_of_groundterm(&is_ground(&a).unwrap()); 795 | prog.facts.push((a.clone(), a)) 796 | } 797 | }; 798 | } 799 | Conj(fs) => { 800 | for f in fs { 801 | interp_formula(prog, env, f); 802 | } 803 | } 804 | Impl(hyp, conc) => { 805 | /* 806 | prog = Program::defualt(); 807 | let prog = interp_goal( prog, env, &hyp ); 808 | assert prog.clauses == 0 809 | assert prog.facts == 0 810 | assert prog.queries.len() == 1 811 | Rewrite(prog.queries, prog. 812 | */ 813 | let hyps = match *hyp { 814 | Atom(hyp) => vec![pattern_of_eqterm(&interp_eqwrap(env, &hyp))], 815 | Conj(hyps) => hyps 816 | .iter() 817 | .map(|hyp| match hyp { 818 | Atom(hyp) => pattern_of_eqterm(&interp_eqwrap(env, hyp)), 819 | _ => panic!("invalid hyp in conj"), 820 | }) 821 | .collect(), 822 | _ => panic!("Invalid hyp {:?}", *hyp), 823 | }; 824 | // I should be not duplicating code here. 825 | // call interp_formula here. assert no queries only facts? 826 | let concs = match *conc { 827 | Atom(conc) => vec![pattern_of_eqterm(&interp_eqwrap(env, &conc))], 828 | Conj(concs) => concs 829 | .iter() 830 | .map(|conc| match conc { 831 | Atom(conc) => pattern_of_eqterm(&interp_eqwrap(env, conc)), 832 | _ => panic!("invalid conc in conj"), 833 | }) 834 | .collect(), 835 | _ => panic!("Invalid conc {:?}", *conc), 836 | }; 837 | let searcher = MultiPattern { patterns: hyps }; 838 | let applier = MultiPattern { patterns: concs }; 839 | prog.rules 840 | .push(egg::Rewrite::new("", searcher, applier).unwrap()) 841 | } 842 | // Exists in conclusion. Skolemized on freshvars? 843 | // We can't allow unguarded exists though. uh. Yes we can. 844 | //Exists(vs, f) => { 845 | // freshvars is a bad name 846 | // let freshvars = vs.map(|v| Apply(gensym(v), env.freshvars ); 847 | 848 | //} 849 | // Nested Programs? swaping facts and queries in some sense? 850 | ForAll(vs, f) => { 851 | let mut env = env.clone(); 852 | env.freshvars.extend(vs); // patvars? 853 | interp_formula(prog, &env, *f) 854 | } 855 | _ => panic!("unexpected formula {:?} in interp_formula", formula), 856 | } 857 | } 858 | /* 859 | 860 | // allowing P /\ A => B in head of rules would be useful for exists_unique. 861 | 862 | 863 | // This vs implementing searcher for a formula itself. 864 | fn interp_searcher(formula : Formula) -> impl Searcher { 865 | match formula { 866 | Conj(xs) => , 867 | Atom(Eq(a,b)) =>, 868 | Atom(Bare(a)) =>, 869 | _ => panic 870 | } 871 | 872 | } 873 | 874 | // Alt pattern implements "Or" search. 875 | // It should run each of it's searchers and collate the results. 876 | // Unlike MultiPattern it make no sense as an Applier, since we don't know which case to use. 877 | struct AltPattern

{ 878 | pats : Vec

879 | } 880 | 881 | // Only succeeds if P fails. 882 | struct NegPattern

{ 883 | pat : P 884 | } 885 | 886 | */ 887 | 888 | fn apply_subst( 889 | pat: &PatternAst, 890 | subst: &Subst, 891 | egraph: &EGraph, 892 | ) -> RecExpr { 893 | //let mut r = RecExpr::default(); 894 | //for (i, pat_node) in pat.iter().enumerate() { 895 | fn worker( 896 | i: Id, 897 | pat: &PatternAst, 898 | subst: &Subst, 899 | egraph: &EGraph, 900 | ) -> String { 901 | match &pat[i] { 902 | ENodeOrVar::Var(w) => { 903 | //let expr = simplify(egraph,subst[*w] ); 904 | // r.add(egraph.extract(subst[*w])) 905 | simplify(egraph, *subst.get(*w).unwrap()).to_string() 906 | } 907 | ENodeOrVar::ENode(e) => { 908 | let args: String = e 909 | .children 910 | .iter() 911 | .map(|i| worker(*i, pat, subst, egraph)) 912 | .collect(); 913 | format!("({} {})", e.op.to_string(), args) 914 | //let n = e.clone().map_children(|child| ids[usize::from(child)]); 915 | //egraph.add(n) 916 | } 917 | } 918 | } 919 | worker((pat.as_ref().len() - 1).into(), pat, subst, egraph) 920 | .parse() 921 | .unwrap() 922 | } 923 | 924 | use core::time::Duration; 925 | // Refactor this to return not string. 926 | fn run_file(file: Vec, opts: &Opts) -> String { 927 | //let mut env = Env::default(); 928 | let mut prog = Program::default(); 929 | 930 | for entry in file { 931 | //process_entry(&mut env, entry) 932 | process_entry_prog(&mut prog, entry) 933 | } 934 | let runner = Runner::default() 935 | .with_iter_limit(30) 936 | .with_node_limit(10_000) 937 | .with_time_limit(Duration::from_secs(5)) 938 | .with_explanations_enabled(); 939 | let (mut runner, query_results) = run_program(&prog, runner); 940 | // Two useful things to turn on. Command line arguments? 941 | //runner.print_report(); 942 | // runner.egraph.dot().to_png("target/foo.png").unwrap(); 943 | let mut buf = String::new(); 944 | for (q, res) in prog.queries.iter().zip(query_results) { 945 | writeln!(buf, "-? {}", q); 946 | //let matches = q.search(&runner.egraph); 947 | if res.len() == 0 { 948 | writeln!(buf, "unknown."); 949 | } else { 950 | for subst in res { 951 | print_subst(&mut buf, &runner.egraph, &subst); 952 | if opts.proof { 953 | for ab in &q.patterns { 954 | if let Eq(a, b) = ab { 955 | /* 956 | let ast = a.ast.as_ref(); 957 | let mut id_buf = vec![0.into(); ast.len()]; 958 | let start = egg::pattern::apply_pat(&mut id_buf, ast, &mut runner.egraph, &subst); 959 | let start = simplify(&runner.egraph, start); 960 | 961 | let ast = b.ast.as_ref(); 962 | let mut id_buf = vec![0.into(); ast.len()]; 963 | let end = egg::pattern::apply_pat(&mut id_buf, ast, &mut runner.egraph, &subst); 964 | let end = simplify(&runner.egraph, end); 965 | */ 966 | let start = apply_subst(&a.ast, &subst, &runner.egraph); 967 | let end = apply_subst(&b.ast, &subst, &runner.egraph); 968 | writeln!( 969 | buf, 970 | "Proof {} = {}: {}", 971 | a, 972 | b, 973 | runner.explain_equivalence(&start, &end).get_flat_string() 974 | ); 975 | } 976 | } 977 | } 978 | } 979 | } 980 | } 981 | buf 982 | } 983 | 984 | use clap::{AppSettings, Clap}; 985 | 986 | /// A Prolog-like theorem prover based on Egg 987 | #[derive(Clap)] 988 | #[clap(version = "0.01", author = "Philip Zucker ")] 989 | #[clap(setting = AppSettings::ColoredHelp)] 990 | pub struct Opts { 991 | /// Path of Egglog file to run 992 | pub filename: Option, 993 | /// Turn off verbosity TODO 994 | #[clap(short, long)] 995 | pub verbose: bool, // quiet? 996 | /// Enable Proof Generation (Experimental) 997 | #[clap(short, long)] 998 | pub proof: bool, // quiet? 999 | /// Output graphical representation TODO 1000 | #[clap(short, long)] 1001 | pub graph: Option, 1002 | } 1003 | 1004 | impl Default for Opts { 1005 | fn default() -> Self { 1006 | Opts { 1007 | filename: None, 1008 | verbose: false, 1009 | proof: false, 1010 | graph: None, 1011 | } 1012 | } 1013 | } 1014 | 1015 | pub fn run(s: String, opts: &Opts) -> Result { 1016 | let f = parse_file(s)?; 1017 | Ok(run_file(f, opts)) 1018 | } 1019 | 1020 | use wasm_bindgen::prelude::*; 1021 | #[wasm_bindgen] 1022 | pub fn run_wasm_simple(s: String) -> String { 1023 | let opts = Opts::default(); 1024 | match run(s, &opts) { 1025 | Ok(e) => e, 1026 | Err(e) => e, 1027 | } 1028 | } 1029 | 1030 | #[wasm_bindgen] 1031 | pub fn run_wasm(s: String, proof: bool, graph: bool) -> String { 1032 | let opts = Opts::default(); 1033 | let opts = Opts { 1034 | proof, 1035 | //graph : if graph {Some "graphout.viz"} else None, 1036 | ..opts 1037 | }; 1038 | match run(s, &opts) { 1039 | Ok(e) => e, 1040 | Err(e) => e, 1041 | } 1042 | } 1043 | -------------------------------------------------------------------------------- /src/logic.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | // G and D formula? My intend is for this to be query formula for the moment. 3 | 4 | #[derive(Debug, PartialEq, Clone)] 5 | pub enum Mode { 6 | Axiom(Formula), 7 | Goal(Formula), 8 | } 9 | 10 | /* 11 | There are multiple approaches. 12 | Try to trasnform the formula into something better. 13 | Skolemize, 14 | Also backwards chaining queries 15 | 16 | Compiles formula into prolog based synatx/ Clauses and Queries. 17 | Compile time gensyms. 18 | Introduce the necessary 19 | Could make Mode just another entry in Entry. 20 | It would be useful to get to use all kinds of rules. 21 | I'd need to make thevoriginal parser whitespace capable. 22 | 23 | 24 | fn form_to_rewrite(f : &Formula) -> { 25 | 26 | } 27 | */ 28 | 29 | enum GoalFormula { 30 | ForAll(String, Box), // Introduce fresh variable. 31 | Exists(String, Box), // Pattern variable. does not need skolemization. pattern variable can only be instanniteated with already instroduced variables 32 | // ExistsUnique ? == exists b, (P(b) /\ forall c, P(c) => c = b), and b can't contain c. oof. That's a toughy. Doable as an analysis. 33 | Conj(Vec), // we can normalize to disj of conj form? Also we could just build out the matching language. 34 | Disj(Vec), 35 | Implies(ProgramFormula, Box), 36 | Atom(EqWrap), 37 | } 38 | 39 | enum PatternFormula { 40 | Conj(Vec), 41 | Disj(Vec), 42 | Exists(String, Box), 43 | Atom(EqWrap), 44 | } 45 | 46 | // Perhaps if I'm willing to accept higher order appliers that insert into the rule set 47 | // I can combine some of these. 48 | enum ApplierFormula { 49 | Conj(Vec), 50 | Exists(String, Box), // gensym semantics. in context of all foralls? 51 | Atom(EqWrap), 52 | } 53 | 54 | type AtomicFormula = EqWrap; 55 | 56 | // is programformula the same as applierformula and goalformula the same as patternformual 57 | 58 | enum ProgramFormula { 59 | /* Implies(PatternFormula, ProgramFormula ), 60 | //Forall(String, Box ), // No not fine. foo(X,Y,Z) needs to be disallowed. 61 | Apply(ApplierFormula) 62 | Conj(Box, Box), 63 | // Exists // gensym semantics? exists a b c, foo(a,b,c). seems fine. 64 | // ForallBounded? it's like forall x : [0..10], or something. we need to know where to pull from 65 | ForAllScoped( Vec, PatternFormula, ApplierFormula) // Requires a pattern formula that contains all the variables. 66 | */} 67 | /* 68 | ConjPattern = MultiPattern<> 69 | DisjPattern = OrPattern<> 70 | ematch { 71 | matches = vec![] 72 | for pat in patterns { 73 | matches.extend(pat.match) 74 | } 75 | } 76 | vars = intersction of vars of patterns rather than union 77 | 78 | 79 | datalog over a hash cons would be a weaker egglog. Interesting to think about. 80 | 81 | 82 | compiled form 83 | 84 | 85 | 86 | Impl(ReWrite) 87 | 88 | */ 89 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use egglog::*; 2 | use std::fs; 3 | /* 4 | mod lib; 5 | use lib::*; 6 | mod parser; 7 | 8 | use parser::*; 9 | */ 10 | 11 | // use lib; 12 | //use parser::*; 13 | 14 | /* 15 | use rustyline::error::ReadlineError; 16 | use rustyline::Editor; 17 | 18 | fn repl() { 19 | let mut rl = Editor::<()>::new(); 20 | let mut prog = Program::default(); 21 | println!( 22 | "\ 23 | Egglog - Philip Zucker 2021\n\ 24 | Type \":- help.\" for more information.\n\ 25 | " 26 | ); 27 | loop { 28 | let readline = rl.readline(">> "); 29 | match readline { 30 | Ok(line) => { 31 | rl.add_history_entry(line.as_str()); 32 | match parse_file(line) { 33 | Ok(entries) => { 34 | for entry in entries { 35 | process_entry_prog(&mut prog, entry); 36 | } 37 | } 38 | Err(e) => { 39 | println!("Error: Could not parse. {}", e); 40 | } 41 | } 42 | // println!("Line: {}", line); 43 | } 44 | Err(ReadlineError::Interrupted) => { 45 | println!("CTRL-C"); 46 | break; 47 | } 48 | Err(ReadlineError::Eof) => { 49 | println!("CTRL-D"); 50 | break; 51 | } 52 | Err(err) => { 53 | println!("Error: {:?}", err); 54 | break; 55 | } 56 | } 57 | } 58 | } 59 | */ 60 | fn repl() { 61 | println!("Sorry. REPL currently disabled because wasm was being weird. Please select a file to run as an argument.") 62 | } 63 | 64 | use clap::Clap; 65 | fn main() { 66 | // TODO: better command line grabber 67 | // Interactve mode? 68 | let opts: Opts = Opts::parse(); 69 | let filename = &opts.filename; // td::env::args().nth(1).expect("no file path given"); 70 | match filename { 71 | Some(filename) => { 72 | let contents = 73 | fs::read_to_string(filename).expect("Something went wrong reading the file"); 74 | match run(contents, &opts) { 75 | Ok(res) => println!("Results : \n{}", res), 76 | Err(err) => println!("Error : \n {}", err), 77 | } 78 | } 79 | None => repl(), 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use nom::{ 3 | branch::alt, 4 | bytes::complete::{tag, take_until}, 5 | character::complete::{alphanumeric0, alphanumeric1, char, multispace0, multispace1, satisfy}, 6 | combinator::{map, opt, value}, 7 | error::ParseError, 8 | multi::{many0, separated_list0, separated_list1}, 9 | sequence::{delimited, pair, preceded, terminated, tuple}, 10 | IResult, 11 | }; 12 | 13 | fn clause(input: &str) -> IResult<&str, Entry> { 14 | let (input, head) = separated_list1(ws(char(',')), eqterm)(input)?; 15 | let (input, body) = preceded(ws(tag(":-")), separated_list1(ws(char(',')), eqterm))(input)?; 16 | Ok((input, Clause(head, body))) 17 | } 18 | 19 | fn rewrite(input: &str) -> IResult<&str, Entry> { 20 | let (input, a) = terminated(term, ws(tag("<-")))(input)?; 21 | let (input, b) = term(input)?; 22 | let (input, body) = opt(preceded( 23 | ws(tag(",")), 24 | separated_list1(ws(char(',')), eqterm), 25 | ))(input)?; 26 | let body = match body { 27 | None => vec![], 28 | Some(v) => v, 29 | }; 30 | Ok((input, Rewrite(a, b, body))) 31 | } 32 | 33 | fn birewrite(input: &str) -> IResult<&str, Entry> { 34 | let (input, a) = terminated(term, ws(tag("<->")))(input)?; 35 | let (input, b) = term(input)?; 36 | Ok((input, BiRewrite(a, b))) 37 | } 38 | 39 | fn query(input: &str) -> IResult<&str, Entry> { 40 | map( 41 | preceded(ws(tag("?-")), separated_list1(ws(char(',')), eqterm)), 42 | |eqterms| Query(eqterms), 43 | )(input) 44 | } 45 | /* 46 | fn forall(input: &str) -> IResult<&str, Formula> { 47 | let (input, v) = preceded(tag("forall("), alphanumeric0)(input)?; 48 | let (input, f) = terminated(formula, tag(")"))(input)?; 49 | Ok((input, Formula::ForAll(vec![v.to_string()], Box::new(f)))) 50 | } 51 | 52 | fn conj(input: &str) -> IResult<&str, Formula> { 53 | map(separated_list1(alt((tag(","), tag("&"))), formula), |fs| { 54 | Formula::Conj(fs) 55 | })(input) 56 | } 57 | 58 | fn atom(input: &str) -> IResult<&str, Formula> { 59 | map(eqterm, |eqt| Formula::Atom(eqt))(input) 60 | } 61 | 62 | fn formula(input: &str) -> IResult<&str, Formula> { 63 | alt(( 64 | delimited(tag("("), formula, tag(")")), 65 | alt((forall, conj, atom)), 66 | ))(input) 67 | } 68 | */ 69 | fn include(input: &str) -> IResult<&str, Directive> { 70 | map( 71 | delimited(ws(tag("include(")), take_until(")"), ws(tag(")"))), 72 | |filename: &str| Directive::Include(filename.to_string()), 73 | )(input) 74 | } 75 | 76 | fn directive(input: &str) -> IResult<&str, Entry> { 77 | map(preceded(ws(tag(":-")), include), |d| Directive(d))(input) 78 | } 79 | 80 | fn fact(input: &str) -> IResult<&str, Entry> { 81 | map(eqgroundterm, |a| Fact(a))(input) 82 | } 83 | 84 | fn entry(input: &str) -> IResult<&str, Entry> { 85 | // I should factor this more. 86 | ws(terminated( 87 | alt(( 88 | query, directive, axiom, goal, birewrite, rewrite, clause, fact, 89 | )), 90 | char('.'), 91 | ))(input) 92 | } 93 | 94 | pub fn pinline_comment<'a>(i: &'a str) -> IResult<&'a str, ()> { 95 | value( 96 | (), // Output is thrown away. 97 | tuple((tag("/*"), take_until("*/"), tag("*/"))), 98 | )(i) 99 | } 100 | 101 | fn file(input: &str) -> IResult<&str, Vec> { 102 | let (input, _) = many0(ws(pinline_comment))(input)?; 103 | many0(terminated(entry, many0(ws(pinline_comment))))(input) 104 | //many0(alt((entry, map(pinline_comment, )))(input) 105 | } 106 | 107 | pub fn parse_file(input: String) -> Result, String> { 108 | // input.retain(|c| !c.is_whitespace()); 109 | match file(&input) { 110 | Ok((rem, f)) => { 111 | if rem.is_empty() { 112 | Ok(f) 113 | } else { 114 | Err(format!("Remainder: {}", rem)) 115 | } 116 | } 117 | Err(e) => Err(e.to_string()), 118 | } 119 | } 120 | fn upper(input: &str) -> IResult<&str, char> { 121 | satisfy(|c| c.is_ascii_uppercase())(input) 122 | } 123 | 124 | fn var(input: &str) -> IResult<&str, Term> { 125 | let (input, (c, s)) = pair(upper, alphanumeric0)(input)?; 126 | Ok((input, Var(format!("{}{}", c, s)))) // seems awkward. whatever 127 | } 128 | // TODO: infix operators. 129 | fn groundterm(input: &str) -> IResult<&str, GroundTerm> { 130 | let (input, head) = alphanumeric1(input)?; 131 | let (input, body) = opt(delimited( 132 | ws(char('(')), 133 | separated_list0(ws(char(',')), groundterm), // TODO: whitespace 134 | ws(char(')')), 135 | ))(input)?; 136 | let body = body.unwrap_or(vec![]); 137 | Ok(( 138 | input, 139 | GroundTerm { 140 | head: head.to_string(), 141 | args: body, 142 | }, 143 | )) 144 | } 145 | 146 | fn eqgroundterm(input: &str) -> IResult<&str, EqWrap> { 147 | map( 148 | pair(groundterm, opt(preceded(ws(char('=')), groundterm))), 149 | |(t, ot)| match ot { 150 | Some(t2) => Eq(t, t2), 151 | None => Bare(t), 152 | }, 153 | )(input) 154 | } 155 | 156 | fn apply(input: &str) -> IResult<&str, Term> { 157 | let (input, head) = alphanumeric1(input)?; 158 | let (input, body) = opt(delimited( 159 | ws(char('(')), 160 | separated_list0(ws(char(',')), term), // TODO: whitespace 161 | ws(char(')')), 162 | ))(input)?; 163 | let body = body.unwrap_or(vec![]); 164 | Ok((input, Apply(head.to_string(), body))) 165 | } 166 | // Behaves incorrectly on capital named terms. Whatever. Don't do that. 167 | fn term(input: &str) -> IResult<&str, Term> { 168 | ws(alt((var, apply)))(input) 169 | } 170 | 171 | fn eqterm(input: &str) -> IResult<&str, EqWrap> { 172 | map( 173 | pair(term, opt(preceded(ws(char('=')), term))), 174 | |(t, ot)| match ot { 175 | Some(t2) => Eq(t, t2), 176 | None => Bare(t), 177 | }, 178 | )(input) 179 | } 180 | 181 | /// A combinator that takes a parser `inner` and produces a parser that also consumes both leading and 182 | /// trailing whitespace, returning the output of `inner`. 183 | fn ws<'a, F: 'a, O, E: ParseError<&'a str>>( 184 | inner: F, 185 | ) -> impl FnMut(&'a str) -> IResult<&'a str, O, E> 186 | where 187 | F: FnMut(&'a str) -> IResult<&'a str, O, E>, 188 | { 189 | delimited(multispace0, inner, multispace0) 190 | } 191 | 192 | /* 193 | I could redesign it so that variables must be bound. 194 | 195 | */ 196 | 197 | //existsunique 198 | 199 | // Hmmm. Should the parens go... somewhere deeper? 200 | // What about f (f x). 201 | fn primform(input: &str) -> IResult<&str, Formula> { 202 | alt((delimited(tag("("), form, tag(")")), atom2))(input) 203 | } 204 | 205 | fn conjform(input: &str) -> IResult<&str, Formula> { 206 | map(separated_list1(ws(tag("/\\")), primform), |mut fs| { 207 | if fs.len() == 1 { 208 | fs.remove(0) 209 | } else { 210 | Formula::Conj(fs) 211 | } 212 | })(input) 213 | } 214 | fn disjform(input: &str) -> IResult<&str, Formula> { 215 | map(separated_list1(ws(tag("\\/")), conjform), |mut fs| { 216 | if fs.len() == 1 { 217 | fs.remove(0) 218 | } else { 219 | Formula::Disj(fs) 220 | } 221 | })(input) 222 | } 223 | 224 | fn implform(input: &str) -> IResult<&str, Formula> { 225 | map(separated_list1(ws(tag("=>")), disjform), |fs| { 226 | let mut iter = fs.into_iter().rev(); 227 | let mut f = iter.next().unwrap(); // since seperatedlist1 we can just unwrap 228 | for f1 in iter { 229 | f = Formula::Impl(Box::new(f1), Box::new(f)); 230 | } 231 | f 232 | })(input) 233 | } 234 | 235 | fn quantifier(input: &str) -> IResult<&str, Formula> { 236 | let (input, q) = alt(( 237 | value( 238 | Formula::ForAll as fn(Vec, Box) -> Formula, 239 | tag("forall"), 240 | ), 241 | // fn(_,_) -> _ also works 242 | //more cryptic or not? Function pointer casting https://stackoverflow.com/questions/27895946/expected-fn-item-found-a-different-fn-item-when-working-with-function-pointer 243 | value( 244 | Formula::Exists as fn(Vec, Box) -> Formula, 245 | tag("exists"), 246 | ), 247 | ))(input)?; 248 | let (input, args) = 249 | terminated(ws(separated_list1(multispace1, alphanumeric1)), tag(","))(input)?; 250 | let (input, f) = form(input)?; 251 | Ok(( 252 | input, 253 | q(args.iter().map(|s| s.to_string()).collect(), Box::new(f)), 254 | )) 255 | } 256 | 257 | fn form(input: &str) -> IResult<&str, Formula> { 258 | ws(alt((quantifier, implform)))(input) 259 | } 260 | 261 | /* 262 | 263 | forall x, => yada. 264 | forall x, => yada. 265 | 266 | -------------------- `--` starts a comment line? 267 | |- something. thi is query syntax 268 | |- something. 269 | 270 | */ 271 | 272 | fn primterm(input: &str) -> IResult<&str, Term> { 273 | alt(( 274 | delimited(tag("("), term2, tag(")")), 275 | map(alphanumeric1, |s: &str| Apply(s.to_string(), vec![])), 276 | ))(input) 277 | } 278 | fn apply2(input: &str) -> IResult<&str, Term> { 279 | let (input, head) = terminated(alphanumeric1, multispace0)(input)?; 280 | let (input, body) = separated_list0(multispace1, primterm)(input)?; 281 | Ok((input, Apply(head.to_string(), body))) 282 | } 283 | // SHould just switch to groundterm 284 | fn term2(input: &str) -> IResult<&str, Term> { 285 | ws(alt((delimited(tag("("), term2, tag(")")), apply2)))(input) 286 | } 287 | 288 | fn eqterm2(input: &str) -> IResult<&str, EqWrap> { 289 | map( 290 | ws(pair(term2, opt(preceded(ws(char('=')), term2)))), 291 | |(t, ot)| match ot { 292 | Some(t2) => Eq(t, t2), 293 | None => Bare(t), 294 | }, 295 | )(input) 296 | } 297 | 298 | fn atom2(input: &str) -> IResult<&str, Formula> { 299 | map(eqterm2, |eqt| Formula::Atom(eqt))(input) 300 | } 301 | 302 | fn axiom(input: &str) -> IResult<&str, Entry> { 303 | let (input, name) = delimited(ws(tag("Axiom")), alphanumeric1, ws(tag(":")))(input)?; 304 | let (input, f) = ws(form)(input)?; 305 | Ok((input, Axiom(name.to_string(), f))) 306 | } 307 | 308 | fn goal(input: &str) -> IResult<&str, Entry> { 309 | map(preceded(ws(tag("|-")), form), Goal)(input) 310 | } 311 | /* 312 | fn entry2(input: &str) -> IResult<&str, Mode> { 313 | // I should factor this more. 314 | terminated(alt((goal, map(form, Mode::Axiom))), char('.'))(input) 315 | } 316 | 317 | fn helper(input: &str) -> IResult<&str, Vec> { 318 | let (input, _) = many0(ws(pinline_comment))(&input)?; 319 | ws(many0(terminated(entry2, many0(ws(pinline_comment)))))(input) 320 | } 321 | 322 | pub fn parse_file2(mut input: String) -> Result, String> { 323 | match helper(&input) { 324 | Ok((rem, f)) => { 325 | if rem.is_empty() { 326 | Ok(f) 327 | } else { 328 | Err(format!("Remainder: {}", rem)) 329 | } 330 | } 331 | Err(e) => Err(e.to_string()), 332 | } 333 | } 334 | */ 335 | //https://coq.inria.fr/refman/language/coq-library.html 336 | // -> <-> \/ /\ ~ = 337 | //alt((eqterm, conj, disj, implies, forall, exists )) 338 | 339 | #[cfg(test)] 340 | mod tests { 341 | use super::*; 342 | use Formula::*; 343 | #[test] 344 | fn parser2_test() { 345 | let f = "f".to_string(); 346 | let x = Apply("x".to_string(), vec![]); 347 | let fx = Atom(Bare(Apply(f.clone(), vec![x.clone()]))); 348 | 349 | assert_eq!(term2("(f x)").unwrap().1, Apply(f.clone(), vec![x.clone()])); 350 | 351 | assert_eq!( 352 | term2(" f x ").unwrap().1, 353 | Apply(f.clone(), vec![x.clone()]) 354 | ); 355 | assert_eq!( 356 | eqterm2(" f x ").unwrap().1, 357 | Bare(Apply(f.clone(), vec![x.clone()])) 358 | ); 359 | assert_eq!(atom2(" f x ").unwrap().1, fx); 360 | assert!(quantifier(" f x ").is_err()); 361 | //assert!(conj2(" f x ").is_err()); 362 | 363 | assert_eq!(form(" f x ").unwrap().1, fx); 364 | assert_eq!(form(" f x ").unwrap().1, fx); 365 | assert_eq!( 366 | form(" f x /\\ f x").unwrap().1, 367 | Conj(vec![fx.clone(), fx.clone()]) 368 | ); 369 | assert_eq!( 370 | form("f x /\\ f x\\/f x").unwrap().1, 371 | Disj(vec![Conj(vec![fx.clone(), fx.clone()]), fx.clone()]) 372 | ); 373 | 374 | assert_eq!( 375 | form("forall x, f x").unwrap().1, 376 | ForAll(vec!["x".to_string()], Box::new(fx.clone())) 377 | ); 378 | assert_eq!( 379 | form("forall x, f x => f x").unwrap().1, 380 | ForAll( 381 | vec!["x".to_string()], 382 | Box::new(Impl(Box::new(fx.clone()), Box::new(fx.clone()))) 383 | ) 384 | ); 385 | 386 | assert_eq!( 387 | form("forall x y z, f x").unwrap().1, 388 | ForAll( 389 | vec!["x".to_string(), "y".to_string(), "z".to_string()], 390 | Box::new(fx.clone()) 391 | ) 392 | ); 393 | 394 | //assert_eq!(formula2("(f x)").unwrap().1, Atom( Bare(Apply(f, vec![x]) ) )); 395 | } 396 | #[test] 397 | fn it_works() { 398 | let f = "f".to_string(); 399 | let x = Apply("x".to_string(), vec![]); 400 | assert_eq!(term("f()").unwrap().1, Apply("f".into(), vec![])); 401 | assert_eq!( 402 | entry("f().").unwrap().1, 403 | Fact(Bare(GroundTerm { 404 | head: f, 405 | args: vec![] 406 | })) 407 | ); 408 | assert_eq!(entry("x<->x.").unwrap().1, BiRewrite(x.clone(), x)); 409 | /* (clause("f().")); 410 | dbg!(clause("f():-f().")); 411 | dbg!(clause("f():-f(),greg().")); 412 | dbg!(clause("f():-f(),,greg().")); 413 | dbg!(clause("f(x).")); 414 | dbg!(clause("Xy.")); 415 | dbg!(eqterm("f=g")); */ 416 | } 417 | #[test] 418 | fn includetest() { 419 | let f = "foo.pl".to_string(); 420 | assert_eq!( 421 | entry(":-include(foo.pl).").unwrap().1, 422 | Directive(Directive::Include(f.clone())) 423 | ); 424 | } 425 | } 426 | 427 | /* 428 | TODO: make these tests 429 | println!("Hello, world!"); 430 | dbg!(Var("fred".to_string())); 431 | dbg!(term("f()")); 432 | dbg!(clause("f().")); 433 | dbg!(clause("f():-f().")); 434 | dbg!(clause("f():-f(),greg().")); 435 | dbg!(clause("f():-f(),,greg().")); 436 | dbg!(clause("f(x).")); 437 | dbg!(clause("Xy.")); 438 | dbg!(eqterm("f=g")); 439 | dbg!(term("f(x,X,g(y))").map(|(_, t) | pattern_of_term(&t).to_string())); 440 | dbg!(term("f(r(HENRY,phanto),X,g(y))").map(|(_, t) | pattern_of_term(&t).to_string())); 441 | //dbg!(term("f(e(HENRY,phanto),X,g(y))")); 442 | */ 443 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | use egg::*; 2 | use std::fmt; 3 | 4 | #[derive(Debug, PartialEq, Clone)] 5 | pub enum Term { 6 | Var(String), 7 | Apply(String, Vec), 8 | } 9 | use Term::*; 10 | 11 | impl fmt::Display for Term { 12 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 13 | match self { 14 | Var(v) => write!(f, "?{}", v), 15 | 16 | Apply(g, args) => { 17 | write!(f, "{}(", g)?; 18 | for a in args { 19 | write!(f, "{},", a)?; 20 | } 21 | write!(f, ")") 22 | } 23 | } 24 | } 25 | } 26 | 27 | // There is an argument to be made that I should directly be using RecExpr for groundterm and Pattern for Term 28 | #[derive(Debug, PartialEq)] 29 | pub struct GroundTerm { 30 | pub head: String, 31 | pub args: Vec, 32 | } 33 | 34 | impl fmt::Display for GroundTerm { 35 | fn fmt(&self, buf: &mut fmt::Formatter) -> fmt::Result { 36 | if self.args.len() == 0 { 37 | write!(buf, "{}", self.head) 38 | } else { 39 | write!(buf, "{}(", self.head)?; 40 | let mut iter = self.args.iter(); 41 | if let Some(item) = iter.next() { 42 | write!(buf, "{}", item)?; 43 | for item in iter { 44 | write!(buf, ", {}", item)?; 45 | } 46 | } 47 | write!(buf, ")") 48 | } 49 | } 50 | } 51 | 52 | // toplevel of term is eq only 53 | #[derive(Debug, PartialEq, Clone)] 54 | pub enum EqWrap { 55 | Eq(T, T), 56 | Bare(T), 57 | } 58 | // We could also consider MultiEq { terms : Vec } for more than one A= B = C = D 59 | 60 | impl EqWrap { 61 | pub fn map U>(self, f: F) -> EqWrap { 62 | match self { 63 | EqWrap::Bare(x) => EqWrap::Bare(f(x)), 64 | EqWrap::Eq(a, b) => EqWrap::Eq(f(a), f(b)), 65 | } 66 | } 67 | } 68 | 69 | impl fmt::Display for EqWrap 70 | where 71 | T: fmt::Display, 72 | { 73 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 74 | match self { 75 | EqWrap::Eq(a, b) => write!(f, "{} = {}", a, b), 76 | EqWrap::Bare(v) => write!(f, "{}", v), 77 | } 78 | } 79 | } 80 | 81 | #[derive(Debug, PartialEq)] 82 | pub enum Directive { 83 | Include(String), 84 | } 85 | 86 | #[derive(Debug, PartialEq, Clone)] 87 | pub enum Formula { 88 | Impl(Box, Box), 89 | Conj(Vec), 90 | Disj(Vec), 91 | ForAll(Vec, Box), 92 | Exists(Vec, Box), 93 | Atom(EqWrap), 94 | } 95 | 96 | #[derive(Debug, PartialEq)] 97 | pub enum Entry { 98 | Clause(Vec>, Vec>), 99 | Fact(EqWrap), 100 | Rewrite(Term, Term, Vec>), 101 | BiRewrite(Term, Term), 102 | Directive(Directive), 103 | Query(Vec>), // Should I only allow GroundTerm queries? 104 | Axiom(String, Formula), 105 | Goal(Formula), 106 | } 107 | 108 | /* enum Directive { 109 | NodeLimit, 110 | ClassLimit 111 | TimeLimit, 112 | Include, 113 | Clear/Reset 114 | } 115 | 116 | */ 117 | pub fn is_ground(t: &Term) -> Option { 118 | match t { 119 | Var(_) => None, 120 | Apply(f, args) => { 121 | let oargs: Option> = args.iter().map(is_ground).collect(); 122 | oargs.map(|args| GroundTerm { 123 | head: f.to_string(), 124 | args, 125 | }) 126 | } 127 | } 128 | } 129 | 130 | pub fn eid_of_groundterm(egraph: &mut EGraph, t: &GroundTerm) -> Id { 131 | let args = t 132 | .args 133 | .iter() 134 | .map(|a| eid_of_groundterm(egraph, a)) 135 | .collect(); 136 | egraph.add(SymbolLang::new(t.head.clone(), args)) 137 | } 138 | 139 | fn recexpr_of_groundterm_aux(expr: &mut RecExpr, t: &GroundTerm) -> Id { 140 | let expr_args = t 141 | .args 142 | .iter() 143 | .map(|a| recexpr_of_groundterm_aux(expr, &a)) 144 | .collect(); 145 | expr.add(SymbolLang::new(t.head.clone(), expr_args)) 146 | } 147 | 148 | pub fn recexpr_of_groundterm(t: &GroundTerm) -> RecExpr { 149 | let mut expr = RecExpr::default(); 150 | recexpr_of_groundterm_aux(&mut expr, t); 151 | expr 152 | } 153 | 154 | pub fn pattern_of_eqterm(t: &EqWrap) -> EqWrap> { 155 | match t { 156 | EqWrap::Bare(x) => EqWrap::Bare(pattern_of_term(x)), 157 | EqWrap::Eq(x, y) => EqWrap::Eq(pattern_of_term(x), pattern_of_term(y)), 158 | } 159 | } 160 | /* 161 | fn pattern_of_term(t : &Term) -> Pattern { 162 | let mut ast = RecExpr::default(); 163 | fn worker(t : &Term){ 164 | match t { 165 | Var(x) => ast.add(ENodeOrVar::Var(Var(Symbol::from("x")))) 166 | Apply(f,args) => 167 | let args = args.iter().map(worker).collect(); 168 | ast.add(ENodeOrVar::ENode( SymbolLang::new(f.clone(),args))) 169 | } 170 | } 171 | worker(t); 172 | let program = egg::machine::Program::compile_from_pat(&ast); 173 | Pattern { ast, program } 174 | } 175 | */ 176 | pub fn sexp_of_term(t: &Term) -> String { 177 | match t { 178 | Var(x) => format!(" ?{} ", x), 179 | Apply(f, args) => { 180 | let args: String = args.iter().map(sexp_of_term).collect(); 181 | format!("({}{})", f, args) 182 | } 183 | } 184 | } 185 | 186 | // This sort of stuff is what From traits are for right? 187 | pub fn pattern_of_term(t: &Term) -> Pattern { 188 | sexp_of_term(t).parse().unwrap() 189 | } 190 | 191 | /* 192 | 193 | */ 194 | /* 195 | // Private. options ; 196 | // 1 trasmute the memory. Yikes. 197 | // 2 Rebuild the machine infrastructure in a file here. Compile a single machine that produces a single subst. 198 | // 3 Fork egg 199 | fn merge_subst( s1 : &Subst, s2 : &Subst ) -> Option{ 200 | let s1 = s1.clone(); 201 | for (v,i) in s2.vec { 202 | if let Some(id) = s1.insert(v,i){ 203 | return None; 204 | } 205 | } 206 | return Some(s1); 207 | } 208 | */ 209 | -------------------------------------------------------------------------------- /tests/cat.pl: -------------------------------------------------------------------------------- 1 | 2 | /* A <- dom(hom(A,B)). 3 | B <- cod(hom(A,B)). 4 | hom(A,A) <- type(id(A)). 5 | hom(dom(type(F)), cod(type(G))) <- type(comp(F,G)). 6 | */ 7 | 8 | type(id(A)) = hom(A,A) :- ob = type(A). 9 | /* Composition exists if types work out */ 10 | type(comp(F,G)) = hom(A,C) :- hom(A,B) = type(F), hom(B,C) = type(G). 11 | F <- comp(id(A), F). 12 | F <- comp(F, id(A)). 13 | /* associativity of composition */ 14 | comp(comp(F,G),H) <-> comp(F, comp(G,H)). 15 | 16 | /* Monoidal */ 17 | type(otimes(A,B)) = ob :- ob = type(A), ob = type(B). 18 | type(otimes(F,G)) = hom(otimes(A,B),otimes(C,D)) :- hom(A,C) = type(F), hom(B,D) = type(G). 19 | 20 | /* The non-generative form */ 21 | hom(otimes(A,B),otimes(C,D)) = T :- type(otimes(F,G)) = T, hom(A,C) = type(F), hom(B,D) = type(G). 22 | 23 | 24 | /* Covers the object case too */ 25 | otimes(otimes(F,G),H) <-> otimes(F, otimes(G,H)). 26 | 27 | type(munit) = ob. 28 | A <- otimes(munit, A). 29 | A <- otimes(A,munit). 30 | F <- otimes(id(munit), F). 31 | F <- otimes(F, id(munit)). 32 | 33 | /* Is this one necessary? */ 34 | id(otimes(A,B)) <-> otimes(id(A), id(B)). 35 | 36 | comp(otimes(F,G), otimes(P,Q)). 37 | 38 | 39 | /* Now this is possible 40 | F = comp(id(A), F) :- hom(A,B) = type(F). 41 | F = comp(F, id(B)) :- hom(A,B) = type(F). 42 | */ 43 | /* 44 | More efficient form 45 | */ 46 | 47 | type(a) = ob. 48 | /* Uhhh. Hmm. */ 49 | type(id(A)) = hom(A,A) :- type(A) = ob. 50 | 51 | type(id(a)). 52 | type(comp(id(a),id(a))). 53 | type(f) = hom(a,b). 54 | type(comp(id(a), f)). 55 | /* 56 | Should we be using the tuff in the query injected into the database? 57 | 58 | */ 59 | ?- type(id(a)), type(comp(id(a),f)). 60 | /* 61 | let mut rules : Vec> = vec![ 62 | vec![rw!( "dom(hom(a, b)) => a" ; "(dom (hom ?a ?b))" => "?a" )], 63 | vec![rw!( "cod(hom(a, b)) => b" ; "(cod (hom ?a ?b))" => "?b" )], 64 | vec![rw!( "type(id(a)) => hom(a, a)" ; "(type (id ?a))" => "(hom ?a ?a)" )], 65 | vec![rw!( "type(f . g) => hom(dom(type(f)), cod(type(g)))" ; "(type (. ?f ?g))" => "(hom (dom (type ?f)) (cod (type ?g)))" )], 66 | vec![rw!( "type(f om g) => hom(dom(type(f)) oo dom(type(g)), cod(type(f)) oo cod(type(g)))" ; "(type (om ?f ?g))" => "(hom (oo (dom (type ?f)) (dom (type ?g))) (oo (cod (type ?f)) (cod (type ?g))))" )], 67 | vec![rw!( "type(a oo b) => :ob" ; "(type (oo ?a ?b))" => "ob" )], 68 | vec![rw!( "type(munit()) => :ob" ; "(type munit)" => "ob" )], 69 | vec![rw!( "type(swap(a, b)) => hom(a oo b, b oo a)" ; "(type (swap ?a ?b))" => "(hom (oo ?a ?b) (oo ?b ?a))" )], 70 | vec![rw!( "type((del)(a)) => hom(a, munit())" ; "(type (del ?a))" => "(hom ?a munit)" )], 71 | vec![rw!( "type(dup(a)) => hom(a, a oo a)" ; "(type (dup ?a))" => "(hom ?a (oo ?a ?a))" )], 72 | vec![rw!( "type(pair(f, g)) => hom(dom(type(f)), cod(type(f)) oo cod(type(g)))" ; "(type (pair ?f ?g))" => "(hom (dom (type ?f)) (oo (cod (type ?f)) (cod (type ?g))))" )], 73 | vec![rw!( "type(proj1(a, b)) => hom(a oo b, a)" ; "(type (proj1 ?a ?b))" => "(hom (oo ?a ?b) ?a)" )], 74 | vec![rw!( "type(proj2(a, b)) => hom(a oo b, b)" ; "(type (proj2 ?a ?b))" => "(hom (oo ?a ?b) ?b)" )], 75 | vec![rw!( "f . id(b) => f" ; "(. ?f (id ?b))" => "?f" )], 76 | vec![rw!( "id(a) . f => f" ; "(. (id ?a) ?f)" => "?f" )], 77 | vec![rw!( "a oo munit() => a" ; "(oo ?a munit)" => "?a" )], 78 | vec![rw!( "munit() oo a => a" ; "(oo munit ?a)" => "?a" )], 79 | rw!( "f . (g . h) == (f . g) . h" ; "(. ?f (. ?g ?h))" <=> "(. (. ?f ?g) ?h)" ), 80 | vec![rw!( "id(munit()) om f => f" ; "(om (id munit) ?f)" => "?f" )], 81 | vec![rw!( "f om id(munit()) => f" ; "(om ?f (id munit))" => "?f" )], 82 | rw!( "a oo (b oo c) == (a oo b) oo c" ; "(oo ?a (oo ?b ?c))" <=> "(oo (oo ?a ?b) ?c)" ), 83 | rw!( "f om (h om j) == (f om h) om j" ; "(om ?f (om ?h ?j))" <=> "(om (om ?f ?h) ?j)" ), 84 | rw!( "id(a oo b) == id(a) om id(b)" ; "(id (oo ?a ?b))" <=> "(om (id ?a) (id ?b))" ), 85 | vec![rw!( "(f . g) om (p . q) => (f om p) . (g om q)" ; "(om (. ?f ?g) (. ?p ?q))" => "(. (om ?f ?p) (om ?g ?q))" )], 86 | rw!( "swap(a, b) . swap(b, a) == id(a oo b)" ; "(. (swap ?a ?b) (swap ?b ?a))" <=> "(id (oo ?a ?b))" ), 87 | rw!( "(swap(a, b) om id(c)) . (id(b) om swap(a, c)) == swap(a, b oo c)" ; "(. (om (swap ?a ?b) (id ?c)) (om (id ?b) (swap ?a ?c)))" <=> "(swap ?a (oo ?b ?c))" ), 88 | rw!( "(id(a) om swap(b, c)) . (swap(a, c) om id(b)) == swap(a oo b, c)" ; "(. (om (id ?a) (swap ?b ?c)) (om (swap ?a ?c) (id ?b)))" <=> "(swap (oo ?a ?b) ?c)" ), 89 | rw!( "swap(a, munit()) == id(a)" ; "(swap ?a munit)" <=> "(id ?a)" ), 90 | rw!( "swap(munit(), a) == id(a)" ; "(swap munit ?a)" <=> "(id ?a)" ), 91 | 92 | vec![rw!( "swap(munit(), munit()) => id(munit() oo munit())" ; "(swap munit munit)" => "(id (oo munit munit))" )], 93 | rw!( "dup(a) . ((del)(a) om id(a)) == id(a)" ; "(. (dup ?a) (om (del ?a) (id ?a)))" <=> "(id ?a)" ), 94 | rw!( "dup(a) . (id(a) om (del)(a)) == id(a)" ; "(. (dup ?a) (om (id ?a) (del ?a)))" <=> "(id ?a)" ), 95 | rw!( "dup(a) . swap(a, a) == dup(a)" ; "(. (dup ?a) (swap ?a ?a))" <=> "(dup ?a)" ), 96 | rw!( "(dup(a) om dup(b)) . ((id(a) om swap(a, b)) om id(b)) == dup(a oo b)" ; "(. (om (dup ?a) (dup ?b)) (om (om (id ?a) (swap ?a ?b)) (id ?b)))" <=> "(dup (oo ?a ?b))" ), 97 | rw!( "dup(a) . (dup(a) om id(a)) == dup(a) . (id(a) om dup(a))" ; "(. (dup ?a) (om (dup ?a) (id ?a)))" <=> "(. (dup ?a) (om (id ?a) (dup ?a)))" ), 98 | rw!( "(del)(a oo b) == (del)(a) om (del)(b)" ; "(del (oo ?a ?b))" <=> "(om (del ?a) (del ?b))" ), 99 | rw!( "dup(munit()) == id(munit())" ; "(dup munit)" <=> "(id munit)" ), 100 | rw!( "(del)(munit()) == id(munit())" ; "(del munit)" <=> "(id munit)" ), 101 | vec![rw!( "pair(f, k) => dup(dom(type(f))) . (f om k)" ; "(pair ?f ?k)" => "(. (dup (dom (type ?f))) (om ?f ?k))" )], 102 | rw!( "proj1(a, b) == id(a) om (del)(b)" ; "(proj1 ?a ?b)" <=> "(om (id ?a) (del ?b))" ), 103 | rw!( "proj2(a, b) == (del)(a) om id(b)" ; "(proj2 ?a ?b)" <=> "(om (del ?a) (id ?b))" ), 104 | vec![rw!( "f . (del)(b) => (del)(dom(type(f)))" ; "(. ?f (del ?b))" => "(del (dom (type ?f)))" )], 105 | vec![rw!( "f . dup(b) => dup(dom(type(f))) . (f om f)" ; "(. ?f (dup ?b))" => "(. (dup (dom (type ?f))) (om ?f ?f))" )], 106 | vec![rw!( "dup(a) . (f om f) => f . dup(cod(type(f)))" ; "(. (dup ?a) (om ?f ?f))" => "(. ?f (dup (cod (type ?f))))" )], 107 | 108 | ].concat(); 109 | */ -------------------------------------------------------------------------------- /tests/clause.pl: -------------------------------------------------------------------------------- 1 | a(x). 2 | q(x). 3 | b(X) :- a(X), q(X). 4 | c(X) <- b(X). 5 | ?- c(x) = b(x), a(x) = b(x). 6 | -------------------------------------------------------------------------------- /tests/cram/arith.t: -------------------------------------------------------------------------------- 1 | $ $TESTDIR/run_test.sh arith.pl 2 | Results : 3 | -? (mul two two) = (plus two two) 4 | []; 5 | -? (mul two two) = (plus one ?X) 6 | [?X = three]; 7 | -? (mul two two) = ?Z 8 | [?Z = four]; 9 | -? (plus (mul x three) (mul two (plus one (mul four x)))) = ?Z 10 | [?Z = (plus two (mul x (plus five six)))]; 11 | 12 | -------------------------------------------------------------------------------- /tests/cram/axioms.t: -------------------------------------------------------------------------------- 1 | $ $TESTDIR/run_test.sh axioms.pl 2 | Results : 3 | -? (f x) = x 4 | unknown. 5 | -? x = x 6 | []; 7 | -? y = x 8 | []; 9 | -? (plus p r) = (plus r p) 10 | unknown. 11 | -? (junk boo) = (otherjunk baz) 12 | unknown. 13 | -? (f ?z) = x 14 | unknown. 15 | -? (f x) = x 16 | unknown. 17 | -? x = x 18 | []; 19 | -? y = x 20 | []; 21 | -? (plus p r) = (plus r p) 22 | unknown. 23 | -? (junk boo) = (otherjunk baz) 24 | unknown. 25 | 26 | -------------------------------------------------------------------------------- /tests/cram/basics.t: -------------------------------------------------------------------------------- 1 | $ $TESTDIR/run_test.sh basics.pl 2 | Results : 3 | -? (f x) = x 4 | []; 5 | -? x = x 6 | []; 7 | -? y = x 8 | []; 9 | -? (plus p r) = (plus r p) 10 | []; 11 | -? (junk boo) = (otherjunk baz) 12 | unknown. 13 | -? (f (f (f (f x)))) = ?X 14 | [?X = y]; 15 | 16 | -------------------------------------------------------------------------------- /tests/cram/cat1.t: -------------------------------------------------------------------------------- 1 | $ $TESTDIR/run_test.sh cat1.pl 2 | Results : 3 | -? p = q 4 | []; 5 | -? f = g 6 | unknown. 7 | -? p = f 8 | unknown. 9 | -? k = h 10 | unknown. 11 | -? k = g 12 | unknown. 13 | -? (type (comp p h)) = ?T 14 | [?T = (hom z c)]; 15 | -? (type (id a)) = (hom a a) 16 | []; 17 | -? (comp (comp (id a) h) k) = ?T 18 | [?T = (comp f g)]; 19 | 20 | -------------------------------------------------------------------------------- /tests/cram/datalog.t: -------------------------------------------------------------------------------- 1 | $ $TESTDIR/run_test.sh datalog.pl 2 | Results : 3 | -? (ancestor xerces ?X) 4 | [?X = damocles]; 5 | [?X = brooke]; 6 | -? (mortal socrates) 7 | []; 8 | -? (path ?X ?Y) 9 | [?X = a, ?Y = b]; 10 | [?X = a, ?Y = c]; 11 | [?X = b, ?Y = c]; 12 | 13 | -------------------------------------------------------------------------------- /tests/cram/id_unique.t: -------------------------------------------------------------------------------- 1 | $ $TESTDIR/run_test.sh id_unique.pl 2 | Results : 3 | -? (id2 a) = (id a) 4 | []; 5 | -? f = (id2 a) 6 | unknown. 7 | 8 | -------------------------------------------------------------------------------- /tests/cram/run_test.sh: -------------------------------------------------------------------------------- 1 | cargo run --manifest-path "$TESTDIR/../../Cargo.toml" --quiet "$TESTDIR/../../examples/$1" 2>/dev/null -------------------------------------------------------------------------------- /tests/cram/ski.t: -------------------------------------------------------------------------------- 1 | $ $TESTDIR/run_test.sh ski.pl 2 | Results : 3 | -? (k (i k) (i i)) = ?A 4 | [?A = i]; 5 | -? (s k k s) = ?A 6 | [?A = (k s)]; 7 | 8 | -------------------------------------------------------------------------------- /tests/cram/whitespace.t: -------------------------------------------------------------------------------- 1 | $ $TESTDIR/run_test.sh whitespace.pl 2 | Results : 3 | 4 | -------------------------------------------------------------------------------- /tests/eqclause.pl: -------------------------------------------------------------------------------- 1 | a(x). 2 | b(x). 3 | c(X) = d(Y) :- a(X), b(Y). 4 | ?- c(x) = d(x), a(x) = c(x), b(x) = d(x). -------------------------------------------------------------------------------- /tests/eqclause2.pl: -------------------------------------------------------------------------------- 1 | 2 | a = b. 3 | c = d :- a = b. 4 | ?- c = d, a = c. -------------------------------------------------------------------------------- /tests/example.pl: -------------------------------------------------------------------------------- 1 | 2 | f(x) = x. 3 | /* 4 | g(X)=f(x):-z. 5 | f(X) = g(Q) :- Q = X, f(x). 6 | */ 7 | y = x. 8 | plus(X,Y) <- plus(Y,X). 9 | plus(b,q). 10 | ?- f(x) = x, x = x, y = x, plus(b,q) = plus(q,b), f(f(x)). -------------------------------------------------------------------------------- /tests/example.rs: -------------------------------------------------------------------------------- 1 | //use lib::*; 2 | use egglog::run; 3 | use std::fs; 4 | 5 | #[test] 6 | fn it_adds_two() { 7 | let filename = "tests/example.pl"; 8 | let contents = fs::read_to_string(filename).expect("Something went wrong reading the file"); 9 | dbg!(run(contents)); 10 | } 11 | -------------------------------------------------------------------------------- /tests/monic2.dl: -------------------------------------------------------------------------------- 1 | /* Axioms of category */ 2 | 3 | /* identities exists */ 4 | id(A) :- type(A) = ob 5 | 6 | /* Alternate axioms */ 7 | type(id(A)) = hom(A,A) :- type(A) = ob. 8 | 9 | /* Alternate: Explicitly spelled out */ 10 | type(id(a)) = hom(a,a). 11 | type(id(b)) = hom(b,b). 12 | 13 | /* type(a) = ob vs ob(a) style. 14 | type(F) = hom(A,B) vs hom(A,B,F) vs cod(F) = a, dom(F) = b 15 | */ 16 | 17 | /* composition exists */ 18 | comp(F,G) :- type(F) = hom(A,B), type(G) = hom(B,C). 19 | type(comp(F,G)) = hom(A,C) :- type(F) = hom(A,B), type(G) = hom(B,C). 20 | 21 | 22 | /* identity axioms */ 23 | F <- comp(id(A), F). 24 | F <- comp(F, id(A)). 25 | comp(id(A), F) = F :- type(F) = hom(A,B). 26 | 27 | /* Associativty */ 28 | comp(comp(F,G),H) <-> comp(F, comp(G,H)). 29 | 30 | 31 | /* And so on */ 32 | 33 | /* specify types */ 34 | type(a) = ob. 35 | type(b) = ob. 36 | type(c) = ob. 37 | type(d) = ob. 38 | 39 | type(f) = hom(a,b). 40 | type(g) = hom(b,d). 41 | type(h) = hom(a,c). 42 | type(k) = hom(c,d). 43 | 44 | /* assume g is monic */ 45 | F = H :- comp(F,g) = comp(H,g), dom(F) = dom(H). 46 | 47 | /* square is pullback */ 48 | comp(f,g) = comp(h,k). /* square commutes */ 49 | 50 | /* univ exist for other squares. Skolemized exists */ 51 | comp(univ(F,H,E),h) = H, comp(univ(F,H,E),f) = F 52 | :- comp(F,g) = comp(H,k), dom(F) = dom(H) = E. 53 | /* unique */ 54 | U = univ(F,H,E) :- comp(F,g) = comp(H,k), dom(F) = dom(H) = E, comp(U,h) = H, comp(U,f) = F. 55 | 56 | /* Theorem: 57 | h is monic. => forall P Q, comp(P,h) = comp(Q,h), dom(P) = dom(Q) => P = Q 58 | 59 | We can take p and q to left of seqeunt, or intro them. 60 | They are arbitrary 61 | */ 62 | type(z) = ob 63 | type(p) = hom(z,a). 64 | type(q) = hom(z,a). 65 | comp(p,h) = comp(q,h). 66 | 67 | ?- p = q. 68 | 69 | /* so in bottom up evaluation, how does this work. 70 | comp(p,f) exists 71 | comp(q,f) exists 72 | 73 | p . f . g = p . h . k is a sqaure 74 | q . f . g = q . h . k is a square 75 | p . f . g = q . h . k is a sqaure 76 | q . f . g = p . h . k is a square 77 | 78 | 79 | we instatiate the pullback on each 80 | univ(p.f , p.h , z) 81 | univ(q.f , q.h , z) 82 | 83 | 84 | 85 | */ --------------------------------------------------------------------------------