├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── analyze-mml.sh ├── download-mml.sh ├── mml.patch ├── rustfmt.toml └── src ├── accom.rs ├── analyze.rs ├── ast.rs ├── bignum.rs ├── cache.rs ├── checker.rs ├── equate.rs ├── equate └── polynomial.rs ├── error.rs ├── export.rs ├── format.rs ├── global.rs ├── main.rs ├── parser ├── article.rs ├── miz.rs ├── mod.rs └── msm.rs ├── reader.rs ├── types.rs ├── unify.rs ├── util.rs └── write.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /miz 3 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/target/debug/miz-rs", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": false, 17 | "MIMode": "gdb", 18 | "setupCommands": [ 19 | { 20 | "description": "Enable pretty-printing for gdb", 21 | "text": "-enable-pretty-printing", 22 | "ignoreFailures": true 23 | }, 24 | { 25 | "description": "Set Disassembly Flavor to Intel", 26 | "text": "-gdb-set disassembly-flavor intel", 27 | "ignoreFailures": true 28 | } 29 | ] 30 | }, 31 | { 32 | "type": "lldb", 33 | "request": "launch", 34 | "name": "Debug executable 'miz-rs'", 35 | "cargo": { 36 | "args": [ 37 | "build", 38 | "--bin=miz-rs", 39 | "--package=miz-rs" 40 | ], 41 | "filter": { 42 | "name": "miz-rs", 43 | "kind": "bin" 44 | } 45 | }, 46 | "args": [], 47 | "cwd": "${workspaceFolder}", 48 | }, 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /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 = "addr2line" 7 | version = "0.22.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "anstream" 22 | version = "0.6.14" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" 25 | dependencies = [ 26 | "anstyle", 27 | "anstyle-parse", 28 | "anstyle-query", 29 | "anstyle-wincon", 30 | "colorchoice", 31 | "is_terminal_polyfill", 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle" 37 | version = "1.0.7" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" 40 | 41 | [[package]] 42 | name = "anstyle-parse" 43 | version = "0.2.4" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" 46 | dependencies = [ 47 | "utf8parse", 48 | ] 49 | 50 | [[package]] 51 | name = "anstyle-query" 52 | version = "1.1.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" 55 | dependencies = [ 56 | "windows-sys", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-wincon" 61 | version = "3.0.3" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" 64 | dependencies = [ 65 | "anstyle", 66 | "windows-sys", 67 | ] 68 | 69 | [[package]] 70 | name = "arrayvec" 71 | version = "0.5.2" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 74 | 75 | [[package]] 76 | name = "autocfg" 77 | version = "1.3.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 80 | 81 | [[package]] 82 | name = "backtrace" 83 | version = "0.3.73" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" 86 | dependencies = [ 87 | "addr2line", 88 | "cc", 89 | "cfg-if", 90 | "libc", 91 | "miniz_oxide", 92 | "object", 93 | "rustc-demangle", 94 | ] 95 | 96 | [[package]] 97 | name = "bitflags" 98 | version = "2.6.0" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 101 | 102 | [[package]] 103 | name = "bitmaps" 104 | version = "2.1.0" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" 107 | dependencies = [ 108 | "typenum", 109 | ] 110 | 111 | [[package]] 112 | name = "bytecount" 113 | version = "0.6.8" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" 116 | 117 | [[package]] 118 | name = "cc" 119 | version = "1.0.104" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" 122 | 123 | [[package]] 124 | name = "cfg-if" 125 | version = "1.0.0" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 128 | 129 | [[package]] 130 | name = "cfg_aliases" 131 | version = "0.1.1" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 134 | 135 | [[package]] 136 | name = "clap" 137 | version = "4.5.8" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" 140 | dependencies = [ 141 | "clap_builder", 142 | "clap_derive", 143 | ] 144 | 145 | [[package]] 146 | name = "clap_builder" 147 | version = "4.5.8" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" 150 | dependencies = [ 151 | "anstream", 152 | "anstyle", 153 | "clap_lex", 154 | "strsim", 155 | ] 156 | 157 | [[package]] 158 | name = "clap_derive" 159 | version = "4.5.8" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" 162 | dependencies = [ 163 | "heck", 164 | "proc-macro2", 165 | "quote", 166 | "syn", 167 | ] 168 | 169 | [[package]] 170 | name = "clap_lex" 171 | version = "0.7.1" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" 174 | 175 | [[package]] 176 | name = "colorchoice" 177 | version = "1.0.1" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" 180 | 181 | [[package]] 182 | name = "console" 183 | version = "0.15.8" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" 186 | dependencies = [ 187 | "encode_unicode", 188 | "lazy_static", 189 | "libc", 190 | "unicode-width", 191 | "windows-sys", 192 | ] 193 | 194 | [[package]] 195 | name = "ctrlc" 196 | version = "3.4.4" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" 199 | dependencies = [ 200 | "nix", 201 | "windows-sys", 202 | ] 203 | 204 | [[package]] 205 | name = "either" 206 | version = "1.13.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 209 | 210 | [[package]] 211 | name = "encode_unicode" 212 | version = "0.3.6" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 215 | 216 | [[package]] 217 | name = "endian-type" 218 | version = "0.1.2" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" 221 | 222 | [[package]] 223 | name = "enum-map" 224 | version = "2.7.3" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" 227 | dependencies = [ 228 | "enum-map-derive", 229 | ] 230 | 231 | [[package]] 232 | name = "enum-map-derive" 233 | version = "0.17.0" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" 236 | dependencies = [ 237 | "proc-macro2", 238 | "quote", 239 | "syn", 240 | ] 241 | 242 | [[package]] 243 | name = "gimli" 244 | version = "0.29.0" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" 247 | 248 | [[package]] 249 | name = "heck" 250 | version = "0.5.0" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 253 | 254 | [[package]] 255 | name = "hermit-abi" 256 | version = "0.3.9" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 259 | 260 | [[package]] 261 | name = "im" 262 | version = "15.1.0" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" 265 | dependencies = [ 266 | "bitmaps", 267 | "rand_core", 268 | "rand_xoshiro", 269 | "sized-chunks", 270 | "typenum", 271 | "version_check", 272 | ] 273 | 274 | [[package]] 275 | name = "indicatif" 276 | version = "0.17.8" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" 279 | dependencies = [ 280 | "console", 281 | "instant", 282 | "number_prefix", 283 | "portable-atomic", 284 | "unicode-width", 285 | ] 286 | 287 | [[package]] 288 | name = "instant" 289 | version = "0.1.13" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" 292 | dependencies = [ 293 | "cfg-if", 294 | ] 295 | 296 | [[package]] 297 | name = "is_terminal_polyfill" 298 | version = "1.70.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" 301 | 302 | [[package]] 303 | name = "itertools" 304 | version = "0.10.5" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 307 | dependencies = [ 308 | "either", 309 | ] 310 | 311 | [[package]] 312 | name = "itoa" 313 | version = "1.0.11" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 316 | 317 | [[package]] 318 | name = "lazy_static" 319 | version = "1.5.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 322 | 323 | [[package]] 324 | name = "libc" 325 | version = "0.2.155" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" 328 | 329 | [[package]] 330 | name = "log" 331 | version = "0.4.22" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 334 | 335 | [[package]] 336 | name = "memchr" 337 | version = "2.7.4" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 340 | 341 | [[package]] 342 | name = "miniz_oxide" 343 | version = "0.7.4" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" 346 | dependencies = [ 347 | "adler", 348 | ] 349 | 350 | [[package]] 351 | name = "mizar-rs" 352 | version = "0.1.0" 353 | dependencies = [ 354 | "backtrace", 355 | "bytecount", 356 | "clap", 357 | "ctrlc", 358 | "enum-map", 359 | "im", 360 | "indicatif", 361 | "itertools", 362 | "memchr", 363 | "num-bigint", 364 | "num-traits", 365 | "num_cpus", 366 | "once_cell", 367 | "paste", 368 | "pretty", 369 | "quick-xml", 370 | "radix_trie", 371 | "serde", 372 | "serde_derive", 373 | "serde_json", 374 | "stacker", 375 | ] 376 | 377 | [[package]] 378 | name = "nibble_vec" 379 | version = "0.1.0" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" 382 | dependencies = [ 383 | "smallvec", 384 | ] 385 | 386 | [[package]] 387 | name = "nix" 388 | version = "0.28.0" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" 391 | dependencies = [ 392 | "bitflags", 393 | "cfg-if", 394 | "cfg_aliases", 395 | "libc", 396 | ] 397 | 398 | [[package]] 399 | name = "num-bigint" 400 | version = "0.4.6" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 403 | dependencies = [ 404 | "num-integer", 405 | "num-traits", 406 | ] 407 | 408 | [[package]] 409 | name = "num-integer" 410 | version = "0.1.46" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 413 | dependencies = [ 414 | "num-traits", 415 | ] 416 | 417 | [[package]] 418 | name = "num-traits" 419 | version = "0.2.19" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 422 | dependencies = [ 423 | "autocfg", 424 | ] 425 | 426 | [[package]] 427 | name = "num_cpus" 428 | version = "1.16.0" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 431 | dependencies = [ 432 | "hermit-abi", 433 | "libc", 434 | ] 435 | 436 | [[package]] 437 | name = "number_prefix" 438 | version = "0.4.0" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 441 | 442 | [[package]] 443 | name = "object" 444 | version = "0.36.1" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" 447 | dependencies = [ 448 | "memchr", 449 | ] 450 | 451 | [[package]] 452 | name = "once_cell" 453 | version = "1.19.0" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 456 | 457 | [[package]] 458 | name = "paste" 459 | version = "1.0.15" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 462 | 463 | [[package]] 464 | name = "portable-atomic" 465 | version = "1.6.0" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" 468 | 469 | [[package]] 470 | name = "pretty" 471 | version = "0.11.3" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "83f3aa1e3ca87d3b124db7461265ac176b40c277f37e503eaa29c9c75c037846" 474 | dependencies = [ 475 | "arrayvec", 476 | "log", 477 | "typed-arena", 478 | "unicode-segmentation", 479 | ] 480 | 481 | [[package]] 482 | name = "proc-macro2" 483 | version = "1.0.86" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 486 | dependencies = [ 487 | "unicode-ident", 488 | ] 489 | 490 | [[package]] 491 | name = "psm" 492 | version = "0.1.21" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" 495 | dependencies = [ 496 | "cc", 497 | ] 498 | 499 | [[package]] 500 | name = "quick-xml" 501 | version = "0.28.2" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" 504 | dependencies = [ 505 | "memchr", 506 | ] 507 | 508 | [[package]] 509 | name = "quote" 510 | version = "1.0.36" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 513 | dependencies = [ 514 | "proc-macro2", 515 | ] 516 | 517 | [[package]] 518 | name = "radix_trie" 519 | version = "0.2.1" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" 522 | dependencies = [ 523 | "endian-type", 524 | "nibble_vec", 525 | ] 526 | 527 | [[package]] 528 | name = "rand_core" 529 | version = "0.6.4" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 532 | 533 | [[package]] 534 | name = "rand_xoshiro" 535 | version = "0.6.0" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" 538 | dependencies = [ 539 | "rand_core", 540 | ] 541 | 542 | [[package]] 543 | name = "rustc-demangle" 544 | version = "0.1.24" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 547 | 548 | [[package]] 549 | name = "ryu" 550 | version = "1.0.18" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 553 | 554 | [[package]] 555 | name = "serde" 556 | version = "1.0.204" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" 559 | dependencies = [ 560 | "serde_derive", 561 | ] 562 | 563 | [[package]] 564 | name = "serde_derive" 565 | version = "1.0.204" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" 568 | dependencies = [ 569 | "proc-macro2", 570 | "quote", 571 | "syn", 572 | ] 573 | 574 | [[package]] 575 | name = "serde_json" 576 | version = "1.0.120" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" 579 | dependencies = [ 580 | "itoa", 581 | "ryu", 582 | "serde", 583 | ] 584 | 585 | [[package]] 586 | name = "sized-chunks" 587 | version = "0.6.5" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" 590 | dependencies = [ 591 | "bitmaps", 592 | "typenum", 593 | ] 594 | 595 | [[package]] 596 | name = "smallvec" 597 | version = "1.13.2" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 600 | 601 | [[package]] 602 | name = "stacker" 603 | version = "0.1.15" 604 | source = "registry+https://github.com/rust-lang/crates.io-index" 605 | checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" 606 | dependencies = [ 607 | "cc", 608 | "cfg-if", 609 | "libc", 610 | "psm", 611 | "winapi", 612 | ] 613 | 614 | [[package]] 615 | name = "strsim" 616 | version = "0.11.1" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 619 | 620 | [[package]] 621 | name = "syn" 622 | version = "2.0.68" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" 625 | dependencies = [ 626 | "proc-macro2", 627 | "quote", 628 | "unicode-ident", 629 | ] 630 | 631 | [[package]] 632 | name = "typed-arena" 633 | version = "2.0.2" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" 636 | 637 | [[package]] 638 | name = "typenum" 639 | version = "1.17.0" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 642 | 643 | [[package]] 644 | name = "unicode-ident" 645 | version = "1.0.12" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 648 | 649 | [[package]] 650 | name = "unicode-segmentation" 651 | version = "1.11.0" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" 654 | 655 | [[package]] 656 | name = "unicode-width" 657 | version = "0.1.13" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" 660 | 661 | [[package]] 662 | name = "utf8parse" 663 | version = "0.2.2" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 666 | 667 | [[package]] 668 | name = "version_check" 669 | version = "0.9.4" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 672 | 673 | [[package]] 674 | name = "winapi" 675 | version = "0.3.9" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 678 | dependencies = [ 679 | "winapi-i686-pc-windows-gnu", 680 | "winapi-x86_64-pc-windows-gnu", 681 | ] 682 | 683 | [[package]] 684 | name = "winapi-i686-pc-windows-gnu" 685 | version = "0.4.0" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 688 | 689 | [[package]] 690 | name = "winapi-x86_64-pc-windows-gnu" 691 | version = "0.4.0" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 694 | 695 | [[package]] 696 | name = "windows-sys" 697 | version = "0.52.0" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 700 | dependencies = [ 701 | "windows-targets", 702 | ] 703 | 704 | [[package]] 705 | name = "windows-targets" 706 | version = "0.52.6" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 709 | dependencies = [ 710 | "windows_aarch64_gnullvm", 711 | "windows_aarch64_msvc", 712 | "windows_i686_gnu", 713 | "windows_i686_gnullvm", 714 | "windows_i686_msvc", 715 | "windows_x86_64_gnu", 716 | "windows_x86_64_gnullvm", 717 | "windows_x86_64_msvc", 718 | ] 719 | 720 | [[package]] 721 | name = "windows_aarch64_gnullvm" 722 | version = "0.52.6" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 725 | 726 | [[package]] 727 | name = "windows_aarch64_msvc" 728 | version = "0.52.6" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 731 | 732 | [[package]] 733 | name = "windows_i686_gnu" 734 | version = "0.52.6" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 737 | 738 | [[package]] 739 | name = "windows_i686_gnullvm" 740 | version = "0.52.6" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 743 | 744 | [[package]] 745 | name = "windows_i686_msvc" 746 | version = "0.52.6" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 749 | 750 | [[package]] 751 | name = "windows_x86_64_gnu" 752 | version = "0.52.6" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 755 | 756 | [[package]] 757 | name = "windows_x86_64_gnullvm" 758 | version = "0.52.6" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 761 | 762 | [[package]] 763 | name = "windows_x86_64_msvc" 764 | version = "0.52.6" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 767 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mizar-rs" 3 | version = "0.1.0" 4 | authors = ["Mario Carneiro "] 5 | edition = "2021" 6 | description = "Mizar verifier toolchain" 7 | repository = "https://github.com/digama0/mizar-rs" 8 | readme = "README.md" 9 | license = "GPL-3.0-or-later" 10 | keywords = ["theorem", "proving", "proof", "verifier"] 11 | categories = ["command-line-utilities", "development-tools", "mathematics"] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [profile.release] 16 | debug = true 17 | 18 | [dependencies] 19 | quick-xml = "0.28.1" 20 | once_cell = "1.12" 21 | enum-map = "2.4" 22 | backtrace = "0.3" 23 | itertools = "0.10" 24 | paste = "1.0" 25 | ctrlc = "3.2" 26 | pretty = "0.11" 27 | num-bigint = "0.4" 28 | num-traits = "0.2" 29 | stacker = "0.1" 30 | memchr = "2.5" 31 | radix_trie = "0.2" 32 | im = "15.1" 33 | bytecount = "0.6.3" 34 | indicatif = "0.17.3" 35 | clap = { version = "4.1.10", features = ["derive"] } 36 | num_cpus = "1.15" 37 | serde = { version = "1.0", features = ["rc"] } 38 | serde_derive = "1.0" 39 | serde_json = "1.0" 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mizar proof checker 2 | 3 | This is a (still experimental) proof checker for the [Mizar language](http://mizar.org). 4 | To compile the project, get Rust 1.67 or later (https://rustup.rs) and then build using 5 | `cargo build --release`. 6 | 7 | The program works on a patched version of the Mizar Mathematical Library. 8 | To get it, run `./download-mml.sh`, which will download the MML into the `miz/` 9 | directory and apply the patch. 10 | ```sh 11 | ./download-mml.sh 12 | ``` 13 | Alternatively you can symlink `miz/` to your local mizar installation. 14 | 15 | The first thing you will want to do is compile the `prel/` files which represent the 16 | the theory exports for each article. This breaks the dependencies between files and 17 | allows everything to be processed in parallel. 18 | ```shell 19 | $ time ./analyze-mml.sh 20 | Executed in 21.52 secs fish external 21 | usr time 192.45 secs 454.00 micros 192.45 secs 22 | sys time 4.22 secs 122.00 micros 4.22 secs 23 | ``` 24 | 25 | You can then test the checker on the MML. You should get a result like this: 26 | ```shell 27 | $ time cargo run --release 28 | 10: subset in 0.006s 29 | 6: xfamily in 0.007s 30 | 2: boole in 0.008s 31 | 0: tarski in 0.010s 32 | ... 33 | 1465: realalg3 in 35.913s 34 | 1461: field_14 in 65.490s 35 | 1436: polynom9 in 160.927s 36 | 1428: hilb10_7 in 190.613s 37 | 1459: number11 in 124.127s 38 | success: 1362727 39 | ________________________________________________________ 40 | Executed in 16.62 mins fish external 41 | usr time 167.58 mins 742.00 micros 167.58 mins 42 | sys time 0.22 mins 0.00 micros 0.22 mins 43 | ``` 44 | 45 | Here is a performance comparison of running the original Mizar checker vs the new `mizar-rs` checker on the entire MML, on 12 cores: 46 | 47 | | | `mizar-rs` | `verifier` | speedup | 48 | |-------------------------------|------------|------------|---------| 49 | | real time, analyzer | 1.61 min | 13.39 min | 8.33x | 50 | | CPU time, analyzer | 15.34 min | 129.90 min | 8.47x | 51 | | real time, checker | 14.73 min | 68.43 min | 4.65x | 52 | | CPU time, checker | 142.75 min | 698.58 min | 4.89x | 53 | | real time, analyzer + checker | 16.17 min | 71.46 min | 4.42x | 54 | | CPU time, analyzer + checker | 165.00 min | 748.40 min | 4.54x | 55 | | real time, full | 83.59 min | 16.62 min | 5.03x | 56 | | CPU time, full | 167.58 min | 798.38 min | 4.76x | 57 | 58 | Note that, compared to `verifier`, `mizar-rs` benefits more from running both parts together rather than separately (the `analyzer + checker` row is less than `analyzer` plus `checker`), because `mizar-rs` does not do two passes. 59 | See [#2](https://github.com/digama0/mizar-rs/issues/2#issuecomment-1467281905) for more detailed plots and per-file measurements. 60 | 61 | Here some additional mizar-rs modes, together with the time taken to check the MML on 12 threads: 62 | 63 | | | command | real time | CPU time | 64 | |----------------------------|------------|-----------|------------| 65 | | accom + export | `-ex` | 34.09 sec | 5.40 min | 66 | | export | `-PMex` | 19.85 sec | 3.19 min | 67 | | accom + analyzer | `-a` | 1.35 min | 13.80 min | 68 | | analyzer | `-PMa` | 1.60 min | 15.34 min | 69 | | checker | `-PMc` | 14.73 min | 142.75 min | 70 | | accom + analyzer + checker | (default) | 16.62 min | 167.58 min | 71 | | analyzer + checker | `-PM` | 16.17 min | 165.00 min | 72 | 73 | * The "export" mode also includes running the analyzer in "quick" mode, which does just enough analysis to construct theorem statements. This constructs the `prel/` data for dependent articles, and represents the not-trivially-parallelizable part of MML processing when generating data files from scratch. 74 | 75 | * The "accom" mode (which can be mixed with any of the others) uses the `prel/` files directly to import dependencies, rather than using the files prepared by Mizar's `accom` tool. As the numbers show, the cost of this extra processing is sometimes negative, because we are reading fewer files total and doing less XML parsing. 76 | 77 | ## Usage and configuration 78 | 79 | The `mizar-rs --help` output is replicated here for convenience: 80 | ``` 81 | Mizar verifier toolchain. Common usage cases: 82 | 83 | * mizar-rs -dex --overwrite-prel 84 | Read the MML .miz files and generate the prel/ folder 85 | * mizar-rs 86 | Parse and compile the whole MML from scratch 87 | * mizar-rs nat_4 --one-file 88 | Parse and compile only article nat_4 89 | * mizar-rs nat_4 14 --unify-insts 90 | Give debugging info regarding the item at line 14 of article nat_4 91 | 92 | Usage: mizar-rs [OPTIONS] [FILE] [FIRST_VERBOSE_LINE] 93 | 94 | Arguments: 95 | [FILE] 96 | The name of the first file to process, or the index of the file in `mml.lar` 97 | 98 | [FIRST_VERBOSE_LINE] 99 | The line on which to turn on verbose mode 100 | 101 | Options: 102 | -h, --help 103 | Print help (see a summary with '-h') 104 | 105 | -V, --version 106 | Print version 107 | 108 | Pass selection options: 109 | -c, --checker 110 | Enables (only) the checker, checking 'by' proofs straight from .xml 111 | 112 | -C, --no-checker 113 | Disables the checker, checking the proof skeleton but not individual by steps 114 | 115 | -a, --analyzer 116 | Enables (only) the analyzer, checking the proof skeleton but not individual by steps 117 | 118 | -A, --no-analyzer 119 | Disables the analyzer 120 | 121 | -e, --export 122 | Enables (only) the exporter, doing the minimal amount of work to produce theorem statements 123 | 124 | -E, --no-export 125 | Disables the exporter 126 | 127 | -v, --verify-export 128 | Check that the exported statements exactly match the `miz/mizshare/prel/` directory 129 | 130 | -x, --xml-export 131 | Produce exported statements to the `miz/prel/` directory (requires `-e`) 132 | 133 | -M, --no-accom 134 | Disables the accomodator. (requires `-P`) 135 | 136 | -P, --no-parser 137 | Disables the parser, reading .wsx files instead of .miz 138 | 139 | -N, --no-nameck 140 | Disables name resolution, reading .msx instead of .wsx (requires `-P`) 141 | 142 | -d, --dep-order 143 | Strictly follow dependency order, instead of using `prel/` 144 | 145 | -j, --parallelism 146 | The number of threads to use (currently only file level parallelism is supported) 147 | 148 | [default: 8] 149 | 150 | --orig-mizar 151 | Use `mizar-rs` as a frontend for the original mizar `verifier` 152 | 153 | --one-item[=] 154 | Exit after processing the first verbose item 155 | 156 | [default: true] 157 | 158 | --one-file[=] 159 | Exit after processing the first selected file 160 | 161 | [default: false] 162 | 163 | --skip-to-verbose[=] 164 | Disable the checker while not in verbose mode 165 | 166 | [default: false] 167 | 168 | Other options: 169 | --panic-on-fail[=] 170 | Panic on the first error 171 | 172 | [default: false] 173 | 174 | --overwrite-prel[=] 175 | Write exported statements to `miz/mizshare/prel/` instead of `miz/prel/`, overwriting the originals 176 | 177 | [default: false] 178 | 179 | --no-cache 180 | Always read cross-article theorems from `prel/` instead of from memory 181 | 182 | --no-progress 183 | Don't show the fancy progress bar 184 | 185 | Debugging tools: 186 | --top-item-header[=] 187 | Print a header at every top level item 188 | 189 | [default: false] 190 | 191 | --always-verbose-item[=] 192 | Print the full AST for each item, even when not in verbose mode 193 | 194 | [default: false] 195 | 196 | --item-header[=] 197 | Print a header at each item 198 | 199 | [default: false] 200 | 201 | --checker-inputs[=] 202 | Print the checker input facts in verbose mode 203 | 204 | [default: false] 205 | 206 | --checker-header[=] 207 | Print the checker header in verbose mode 208 | 209 | [default: false] 210 | 211 | --checker-conjuncts[=] 212 | Print the processed checker conjuncts in verbose mode 213 | 214 | [default: false] 215 | 216 | --checker-result[=] 217 | Print the checker result in verbose mode 218 | 219 | [default: false] 220 | 221 | --unify-header[=] 222 | Print the input to the unifier module in verbose mode 223 | 224 | [default: false] 225 | 226 | --unify-insts[=] 227 | Print the instantiation produced by the unifier in verbose mode 228 | 229 | [default: false] 230 | 231 | --dump [...] 232 | Dump the contents of various system components, or `--dump` without arguments to print everything 233 | 234 | [possible values: config, constructors, requirements, notations, clusters, definitions, libraries, formatter] 235 | 236 | Bugs and unsound flags: 237 | --legacy-flex-handling[=] 238 | This is an UNSOUND FLAG that enables checking of `P[a] & ... & P[b]` equality by checking only the endpoints `P[a]` and `P[b]`. This is needed to check some MML proofs 239 | 240 | [default: true] 241 | 242 | --attr-sort-bug[=] 243 | This is buggy behavior, but not unsound. It is required to interpret some MML files 244 | 245 | [default: true] 246 | ``` 247 | 248 | ## Formatter configuration 249 | 250 | There is some additional configuration at the top of [format.rs](src/format.rs) which 251 | controls how expressions are printed. 252 | 253 | ```rust 254 | enable_formatter: true, 255 | ``` 256 | This one sounds odd but it is possible to disable the formatter in the sense that it will 257 | not attempt to load symbols and constructor names. So instead of printing 258 | `c2 " is Element of bool [:c1, c0:]` it will show `F11(c2) is M2 of F18(F19(c1, c0))`. 259 | 260 | ```rust 261 | show_infer: false, 262 | show_only_infer: false, 263 | ``` 264 | These control whether to show "infer constants" as `e`, `?3=e` or `?3`, assuming 265 | constant `?3` is defined to be `e`. 266 | 267 | ```rust 268 | show_priv: false, 269 | ``` 270 | This shows private functors/predicates as `$F2(x + 1, y):=(x + 1 - y)` or `$F2(x + 1, y)`. 271 | 272 | ```rust 273 | show_marks: false, 274 | ``` 275 | This shows EqMark nodes in an expression in the style `3'(x + y)`. 276 | 277 | ```rust 278 | show_invisible: false, 279 | ``` 280 | This controls whether to show all arguments to a functor or just the ones marked "visible". 281 | For example `incl A` (where `A is Subset of Y`) has one visible argument but 282 | `Y` is an invisible argument; when this option is enabled it will be shown as `incl(Y, A)` 283 | instead. (Because the formats which specify how many arguments of a function go left and 284 | how many go to the right, and these have to match the number of visible arguments, enabling 285 | this option causes such functions to be displayed in prefix style.) 286 | 287 | ```rust 288 | show_orig: false, 289 | ``` 290 | This is a slightly more readable version of `ENABLE_FORMATTER = false`. It shows the numbers 291 | for each constructor in brackets after it. So the example from before would be shown as 292 | `c2 "[11] is Element[2] of bool[18] [:[19] c1, c0:]`. Besides being useful in cross-referencing 293 | with Mizar numbers, it also makes it clear when overloading or redefinitions are involved, 294 | since these can appear the same but have different constructor numbers. 295 | 296 | ```rust 297 | upper_clusters: false, 298 | both_clusters: false, 299 | ``` 300 | This shows the inferred cluster attributes after each type. 301 | Upper clusters are marked with a `+`. Example: 302 | * Default (lower clusters only): 303 | ``` 304 | Function-like quasi_total Element of bool [:{}, b1:] 305 | ``` 306 | * Upper clusters only: 307 | ``` 308 | +Relation-like +non-empty +empty-yielding +{}-defined +b1-valued +Function-like 309 | +one-to-one +constant +functional +empty +trivial 310 | +non proper +total +quasi_total Element of bool [:{}, b1:] 311 | ``` 312 | * Both clusters: 313 | ``` 314 | Function-like quasi_total +Relation-like +non-empty +empty-yielding 315 | +{}-defined +b1-valued +Function-like +one-to-one +constant +functional +empty +trivial 316 | +non proper +total +quasi_total Element of bool [:{}, b1:] 317 | ``` 318 | 319 | ```rust 320 | negation_sugar: true, 321 | ``` 322 | This controls whether to heuristically use `→`,`∨`,`∧` and `∀`/`∃` to minimize the number 323 | of explicit `¬` symbols, or whether to use negations precisely as they are represented 324 | internally and use only `∧` and `∀`. 325 | 326 | * With negation sugar: 327 | ``` 328 | ∃ b0: Relation-like Function-like set st 329 | ((proj1 b0) = S0()) ∧ 330 | (∀ b1: object holds 331 | (b1 in S0()) → ((SP0[b1] → ((b0 . b1) = S2(b1))) ∧ (SP0[b1] ∨ ((b0 . b1) = S3(b1))))) 332 | ``` 333 | * No negation sugar: 334 | ``` 335 | ¬(∀ b0: Relation-like Function-like set holds 336 | ¬(((proj1 b0) = S0()) ∧ 337 | (∀ b1: object holds 338 | ¬((b1 in S0()) ∧ 339 | ¬(¬(SP0[b1] ∧ ¬((b0 . b1) = S2(b1))) ∧ ¬(¬SP0[b1] ∧ ¬((b0 . b1) = S3(b1)))))))) 340 | ``` 341 | 342 | *Note: Currently we are making no attempt to make these expressions re-parsable by Mizar. 343 | They are intended purely for presentation purposes.* 344 | 345 | ## Future directions 346 | 347 | Right now this project contains the checker and analyzer parts of the Mizar system, but 348 | with a bit more work the parser too can be incorporated and then this will be a fully 349 | fledged potential `mizf` replacement. Besides this, a major component that has not yet 350 | been started is proof export; this will require a bit of design work but should help 351 | to address the soundness issues in the current architecture. 352 | 353 | ## License 354 | 355 | This project is based on the [Mizar system](https://github.com/MizarProject/system), 356 | and is distributed under the terms of the GPLv3 license. See [LICENSE](LICENSE) for more information. 357 | 358 | ## Contributing 359 | 360 | Contributions are most welcome. Please post issues for any bugs you find or submit PRs for improvements. 361 | I (@digama0) hope that this tool and its open development process can improve the state of Mizar tooling 362 | for everyone. 363 | -------------------------------------------------------------------------------- /analyze-mml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # disable the multi progress bar because it goes too fast and looks noisy 3 | cargo run --release -- -dex --no-multi-progress 4 | echo 'copying prel/ files to mizshare/prel/' 5 | cp -r miz/prel/* miz/mizshare/prel/ 6 | rm -rf miz/prel 7 | -------------------------------------------------------------------------------- /download-mml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mkdir -p miz/zip 3 | cd miz/zip 4 | wget https://mizar.uwb.edu.pl/~softadm/pub/system/i386-win32/mizar-8.1.14_5.78.1462-i386-win32.exe -O mizar.exe 5 | unzip mizar.exe 6 | mkdir -p ../mizshare/mml ../mizshare/prel 7 | cd ../mizshare/mml 8 | unzip ../../zip/mmlfull.zip 9 | cd ../prel 10 | unzip ../../zip/prel.zip 11 | cd .. 12 | unzip ../zip/mizdb1.zip 13 | rm -rf zip 14 | patch -p0 < ../../mml.patch 15 | -------------------------------------------------------------------------------- /mml.patch: -------------------------------------------------------------------------------- 1 | --- mml/alggeo_1.miz 2 | +++ mml/alggeo_1.miz 3 | @@ -39 +39 @@ 4 | - requirements NUMERALS, SUBSET, ARITHM, REAL; 5 | + requirements NUMERALS, SUBSET, ARITHM, REAL, BOOLE; 6 | --- mml/bagord_2.miz 7 | +++ mml/bagord_2.miz 8 | @@ -554 +554,2 @@ 9 | - for x,y being Element of N holds x <= y iff R[x,y]; 10 | + for x,y being Element of N holds x <= y iff R[x,y] 11 | +proof 12 | --- mml/binari_2.miz 13 | +++ mml/binari_2.miz 14 | @@ -28 +28 @@ 15 | - requirements REAL, NUMERALS, SUBSET, ARITHM; 16 | + requirements BOOLE, REAL, NUMERALS, SUBSET, ARITHM; 17 | --- mml/binop_1.miz 18 | +++ mml/binop_1.miz 19 | @@ -745 +745,2 @@ 20 | - ex z being set st z in Z() & P[x,y,z]; 21 | + ex z being set st z in Z() & P[x,y,z] 22 | +proof 23 | --- mml/card_4.miz 24 | +++ mml/card_4.miz 25 | @@ -453 +453,2 @@ 26 | - { f(n) : P[n] } is countable; 27 | + { f(n) : P[n] } is countable 28 | +proof 29 | --- mml/circtrm1.miz 30 | +++ mml/circtrm1.miz 31 | @@ -553 +553,2 @@ 32 | -ex b being Element of B().i st P[i,a,b]; 33 | +ex b being Element of B().i st P[i,a,b] 34 | +proof 35 | --- mml/complex1.miz 36 | +++ mml/complex1.miz 37 | @@ -25 +25 @@ 38 | - requirements REAL, NUMERALS, SUBSET, ARITHM; 39 | + requirements BOOLE, REAL, NUMERALS, SUBSET, ARITHM; 40 | --- mml/complex2.miz 41 | +++ mml/complex2.miz 42 | @@ -27 +27 @@ 43 | - requirements SUBSET, REAL, NUMERALS, ARITHM; 44 | + requirements BOOLE, SUBSET, REAL, NUMERALS, ARITHM; 45 | --- mml/complsp1.miz 46 | +++ mml/complsp1.miz 47 | @@ -26 +26 @@ 48 | - requirements NUMERALS, SUBSET, ARITHM; 49 | + requirements BOOLE, NUMERALS, SUBSET, ARITHM; 50 | --- mml/comptrig.miz 51 | +++ mml/comptrig.miz 52 | @@ -30 +30 @@ 53 | - requirements NUMERALS, SUBSET, REAL, ARITHM; 54 | + requirements BOOLE, NUMERALS, SUBSET, REAL, ARITHM; 55 | --- mml/decomp_1.miz 56 | +++ mml/decomp_1.miz 57 | @@ -22 +22 @@ 58 | - requirements SUBSET; 59 | + requirements BOOLE, SUBSET; 60 | --- mml/diophan2.miz 61 | +++ mml/diophan2.miz 62 | @@ -31 +31 @@ 63 | - requirements REAL, NUMERALS, SUBSET, ARITHM; 64 | + requirements BOOLE, REAL, NUMERALS, SUBSET, ARITHM; 65 | --- mml/euclid_8.miz 66 | +++ mml/euclid_8.miz 67 | @@ -31 +31 @@ 68 | - requirements SUBSET, NUMERALS, ARITHM; 69 | + requirements BOOLE, SUBSET, NUMERALS, ARITHM; 70 | --- mml/fdiff_10.miz 71 | +++ mml/fdiff_10.miz 72 | @@ -28 +28 @@ 73 | - requirements SUBSET, NUMERALS, ARITHM; 74 | + requirements BOOLE, SUBSET, NUMERALS, ARITHM; 75 | --- mml/fdiff_11.miz 76 | +++ mml/fdiff_11.miz 77 | @@ -29 +29 @@ 78 | - requirements SUBSET, NUMERALS, ARITHM, REAL; 79 | + requirements BOOLE, SUBSET, NUMERALS, ARITHM, REAL; 80 | --- mml/fdiff_4.miz 81 | +++ mml/fdiff_4.miz 82 | @@ -29 +29 @@ 83 | - requirements SUBSET, REAL, NUMERALS, ARITHM; 84 | + requirements BOOLE, SUBSET, REAL, NUMERALS, ARITHM; 85 | --- mml/fdiff_6.miz 86 | +++ mml/fdiff_6.miz 87 | @@ -30 +30 @@ 88 | - requirements SUBSET, REAL, NUMERALS, ARITHM; 89 | + requirements BOOLE, SUBSET, REAL, NUMERALS, ARITHM; 90 | --- mml/fdiff_7.miz 91 | +++ mml/fdiff_7.miz 92 | @@ -30 +30 @@ 93 | - requirements SUBSET, NUMERALS, ARITHM; 94 | + requirements BOOLE, SUBSET, NUMERALS, ARITHM; 95 | --- mml/fdiff_8.miz 96 | +++ mml/fdiff_8.miz 97 | @@ -30 +30 @@ 98 | - requirements SUBSET, REAL, NUMERALS, ARITHM; 99 | + requirements BOOLE, SUBSET, REAL, NUMERALS, ARITHM; 100 | --- mml/fdiff_9.miz 101 | +++ mml/fdiff_9.miz 102 | @@ -30 +30 @@ 103 | - requirements SUBSET, REAL, NUMERALS, ARITHM; 104 | + requirements BOOLE, SUBSET, REAL, NUMERALS, ARITHM; 105 | --- mml/field_12.miz 106 | +++ mml/field_12.miz 107 | @@ -810 +810,2 @@ 108 | -A1:for n being Nat for x being Field ex y being Field st P[n,x,y]; 109 | +A1:for n being Nat for x being Field ex y being Field st P[n,x,y] 110 | +proof 111 | --- mml/finseq_4.miz 112 | +++ mml/finseq_4.miz 113 | @@ -2417 +2417,2 @@ 114 | -A1: for n st n in Seg N() ex d being Element of D() st P[n,d]; 115 | +A1: for n st n in Seg N() ex d being Element of D() st P[n,d] 116 | +proof 117 | --- mml/funct_2.miz 118 | +++ mml/funct_2.miz 119 | @@ -2709 +2709,2 @@ 120 | -A1: for x being set st x in X() holds F(x) in Y(); 121 | +A1: for x being set st x in X() holds F(x) in Y() 122 | +proof 123 | --- mml/funct_3.miz 124 | +++ mml/funct_3.miz 125 | @@ -119 +119,2 @@ 126 | - ex z being object st P[x,y,z]; 127 | + ex z being object st P[x,y,z] 128 | +proof 129 | @@ -166 +167,2 @@ 130 | - for x,y being object st x in A() & y in B() holds f.(x,y) = F(x,y); 131 | + for x,y being object st x in A() & y in B() holds f.(x,y) = F(x,y) 132 | +proof 133 | --- mml/funct_5.miz 134 | +++ mml/funct_5.miz 135 | @@ -41 +41,2 @@ 136 | - ex f st dom f = FS() & for g st g in FS () holds f.g = f(g); 137 | + ex f st dom f = FS() & for g st g in FS () holds f.g = f(g) 138 | +proof 139 | --- mml/funct_7.miz 140 | +++ mml/funct_7.miz 141 | @@ -837 +837,2 @@ 142 | -A2: for d1,d2 being Element of D() st G(d1) = G(d2) holds d1 = d2; 143 | +A2: for d1,d2 being Element of D() st G(d1) = G(d2) holds d1 = d2 144 | +proof 145 | --- mml/funct_9.miz 146 | +++ mml/funct_9.miz 147 | @@ -27 +27 @@ 148 | - requirements NUMERALS, SUBSET, ARITHM; 149 | + requirements BOOLE, NUMERALS, SUBSET, ARITHM; 150 | --- mml/fuzzy_5.miz 151 | +++ mml/fuzzy_5.miz 152 | @@ -32 +32 @@ 153 | - requirements NUMERALS, REAL, SUBSET, ARITHM; 154 | + requirements BOOLE, NUMERALS, REAL, SUBSET, ARITHM; 155 | --- mml/fuzzy_7.miz 156 | +++ mml/fuzzy_7.miz 157 | @@ -32 +32 @@ 158 | - requirements NUMERALS, REAL, SUBSET, ARITHM; 159 | + requirements BOOLE, NUMERALS, REAL, SUBSET, ARITHM; 160 | --- mml/gobrd12.miz 161 | +++ mml/gobrd12.miz 162 | @@ -31 +31 @@ 163 | - requirements NUMERALS, REAL, SUBSET, ARITHM; 164 | + requirements BOOLE, NUMERALS, REAL, SUBSET, ARITHM; 165 | --- mml/gobrd13.miz 166 | +++ mml/gobrd13.miz 167 | @@ -29 +29 @@ 168 | - requirements NUMERALS, REAL, SUBSET, ARITHM; 169 | + requirements BOOLE, NUMERALS, REAL, SUBSET, ARITHM; 170 | --- mml/group_1a.miz 171 | +++ mml/group_1a.miz 172 | @@ -65 +65,2 @@ 173 | - ex z being Element of Z() st P[x,y,z]; 174 | + ex z being Element of Z() st P[x,y,z] 175 | +proof 176 | --- mml/gtarski3.miz 177 | +++ mml/gtarski3.miz 178 | @@ -1964,2 +1964,2 @@ 179 | - A,B for Subset of S, 180 | - a,b,c,p,q,r,s for POINT of S; 181 | + A,B for Subset of S, 182 | + a,b,c,p,q,r,s,x for POINT of S; 183 | --- mml/gtarski4.miz 184 | +++ mml/gtarski4.miz 185 | @@ -817,5 +817,5 @@ 186 | -reserve S for non empty 187 | - satisfying_Lower_Dimension_Axiom 188 | - satisfying_Tarski-model 189 | - TarskiGeometryStruct, 190 | - a,b,c,p,q,x,y,z,t for POINT of S; 191 | +reserve S for non empty 192 | + satisfying_Lower_Dimension_Axiom 193 | + satisfying_Tarski-model 194 | + TarskiGeometryStruct, 195 | + a,b,c,c9,p,q,x,y,z,t for POINT of S; 196 | --- mml/hilb10_3.miz 197 | +++ mml/hilb10_3.miz 198 | @@ -709 +709,2 @@ 199 | - for i1,i2,i3,i4,i5 holds {p: P[p.i1,p.i2,F(p.i3,p.i4,p.i5),p.i3,p.i4,p.i5]} 200 | + for n,i1,i2,i3,i4,i5 holds 201 | + {p: P[p.i1,p.i2,F(p.i3,p.i4,p.i5),p.i3,p.i4,p.i5]} 202 | @@ -784 +785 @@ 203 | - for i1,i2,i3,i4,i5 holds {p: P[p.i1,p.i2,F(p.i3,p.i4,p.i5)]} 204 | + for n,i1,i2,i3,i4,i5 holds {p: P[p.i1,p.i2,F(p.i3,p.i4,p.i5)]} 205 | --- mml/hilb10_4.miz 206 | +++ mml/hilb10_4.miz 207 | @@ -35 +35 @@ 208 | - requirements NUMERALS, SUBSET, ARITHM, REAL; 209 | + requirements BOOLE, NUMERALS, SUBSET, ARITHM, REAL; 210 | --- mml/hilb10_5.miz 211 | +++ mml/hilb10_5.miz 212 | @@ -44 +44 @@ 213 | - requirements NUMERALS, SUBSET, ARITHM, REAL; 214 | + requirements BOOLE, NUMERALS, SUBSET, ARITHM, REAL; 215 | --- mml/hilb10_8.miz 216 | +++ mml/hilb10_8.miz 217 | @@ -31 +31 @@ 218 | - requirements NUMERALS, SUBSET, ARITHM, REAL; 219 | + requirements BOOLE, NUMERALS, SUBSET, ARITHM, REAL; 220 | --- mml/integr12.miz 221 | +++ mml/integr12.miz 222 | @@ -35 +35 @@ 223 | - requirements NUMERALS, SUBSET, ARITHM, REAL; 224 | + requirements BOOLE, NUMERALS, SUBSET, ARITHM, REAL; 225 | --- mml/integr13.miz 226 | +++ mml/integr13.miz 227 | @@ -30 +30 @@ 228 | - requirements NUMERALS, SUBSET, ARITHM; 229 | + requirements BOOLE, NUMERALS, SUBSET, ARITHM; 230 | --- mml/integr14.miz 231 | +++ mml/integr14.miz 232 | @@ -30 +30 @@ 233 | - requirements NUMERALS, SUBSET, ARITHM; 234 | + requirements BOOLE, NUMERALS, SUBSET, ARITHM; 235 | --- mml/integr20.miz 236 | +++ mml/integr20.miz 237 | @@ -1818 +1818,2 @@ 238 | - holds s.(2*n) = F(n) & s.(2*n+1) = G(n); 239 | + holds s.(2*n) = F(n) & s.(2*n+1) = G(n) 240 | +proof 241 | --- mml/jct_misc.miz 242 | +++ mml/jct_misc.miz 243 | @@ -51 +51,2 @@ 244 | - the set of all F(a) where a is Element of A() is non empty; 245 | + the set of all F(a) where a is Element of A() is non empty 246 | +proof 247 | --- mml/jordan1d.miz 248 | +++ mml/jordan1d.miz 249 | @@ -31 +31 @@ 250 | - requirements NUMERALS, SUBSET, REAL, ARITHM; 251 | + requirements BOOLE, NUMERALS, SUBSET, REAL, ARITHM; 252 | --- mml/lagra4sq.miz 253 | +++ mml/lagra4sq.miz 254 | @@ -31 +31 @@ 255 | - requirements REAL, NUMERALS, SUBSET, ARITHM; 256 | + requirements BOOLE, REAL, NUMERALS, SUBSET, ARITHM; 257 | --- mml/l_hospit.miz 258 | +++ mml/l_hospit.miz 259 | @@ -30 +30 @@ 260 | - requirements SUBSET, ARITHM, NUMERALS; 261 | + requirements BOOLE, SUBSET, ARITHM, NUMERALS; 262 | --- mml/limfunc4.miz 263 | +++ mml/limfunc4.miz 264 | @@ -27 +27 @@ 265 | - requirements SUBSET, ARITHM, NUMERALS; 266 | + requirements BOOLE, SUBSET, ARITHM, NUMERALS; 267 | --- mml/lmod_7.miz 268 | +++ mml/lmod_7.miz 269 | @@ -45 +45,2 @@ 270 | - (for x being object holds x in X2 iff P[x]) holds X1 = X2; 271 | + (for x being object holds x in X2 iff P[x]) holds X1 = X2 272 | +proof 273 | --- mml/lopban_4.miz 274 | +++ mml/lopban_4.miz 275 | @@ -644 +644,2 @@ 276 | - holds (n <= k implies seq.n=F(k,n)) & (n > k implies seq.n=0.RNS()); 277 | + holds (n <= k implies seq.n=F(k,n)) & (n > k implies seq.n=0.RNS()) 278 | +proof 279 | --- mml/ltlaxio2.miz 280 | +++ mml/ltlaxio2.miz 281 | @@ -34 +34 @@ 282 | - requirements NUMERALS, SUBSET, ARITHM, REAL; 283 | + requirements BOOLE, NUMERALS, SUBSET, ARITHM, REAL; 284 | --- mml/matrixc1.miz 285 | +++ mml/matrixc1.miz 286 | @@ -32 +32 @@ 287 | - requirements NUMERALS, SUBSET, REAL, ARITHM; 288 | + requirements BOOLE, NUMERALS, SUBSET, REAL, ARITHM; 289 | --- mml/mcart_1.miz 290 | +++ mml/mcart_1.miz 291 | @@ -1404 +1404,2 @@ 292 | -A1: x in A() implies ex y,z st y in B() & z in C() & P[x,y,z]; 293 | +A1: x in A() implies ex y,z st y in B() & z in C() & P[x,y,z] 294 | +proof 295 | --- mml/menelaus.miz 296 | +++ mml/menelaus.miz 297 | @@ -30 +30 @@ 298 | - requirements SUBSET, NUMERALS, ARITHM; 299 | + requirements BOOLE, SUBSET, NUMERALS, ARITHM; 300 | --- mml/modelc_1.miz 301 | +++ mml/modelc_1.miz 302 | @@ -2382 +2382,2 @@ 303 | - st for f being object st f in M() holds o.(f) = F(f); 304 | + st for f being object st f in M() holds o.(f) = F(f) 305 | +proof 306 | @@ -2394 +2395,2 @@ 307 | - & (for f being object st f in M() holds o2.f = F(f)) holds o1=o2; 308 | + & (for f being object st f in M() holds o2.f = F(f)) holds o1=o2 309 | +proof 310 | @@ -2415 +2417,2 @@ 311 | - (s,f(),g()) =TRUE iff (Fid(h,S())).s=TRUE; 312 | + (s,f(),g()) =TRUE iff (Fid(h,S())).s=TRUE 313 | +proof 314 | --- mml/msafree1.miz 315 | +++ mml/msafree1.miz 316 | @@ -681 +681,2 @@ 317 | -holds IT2().s.y = x iff P[s,x,y]; 318 | +holds IT2().s.y = x iff P[s,x,y] 319 | +proof 320 | --- mml/nat_1.miz 321 | +++ mml/nat_1.miz 322 | @@ -1020 +1020,2 @@ 323 | - for n being Nat holds f.(n+1) = G(n,f.n); 324 | + for n being Nat holds f.(n+1) = G(n,f.n) 325 | +proof 326 | @@ -1628 +1629,2 @@ 327 | - ex z being Element of Z() st P[x,y,z]; 328 | + ex z being Element of Z() st P[x,y,z] 329 | +proof 330 | --- mml/ordinal1.miz 331 | +++ mml/ordinal1.miz 332 | @@ -1223 +1223,2 @@ 333 | -A2: for A st A in dom L() holds L().A = F(A); 334 | +A2: for A st A in dom L() holds L().A = F(A) 335 | +proof 336 | --- mml/ordinal2.miz 337 | +++ mml/ordinal2.miz 338 | @@ -603 +603,2 @@ 339 | - is limit_ordinal holds L.A = D(A,L|A); 340 | + is limit_ordinal holds L.A = D(A,L|A) 341 | +proof 342 | --- mml/partfun1.miz 343 | +++ mml/partfun1.miz 344 | @@ -111 +111,2 @@ 345 | - (not C[x] implies f.x = G(x)); 346 | + (not C[x] implies f.x = G(x)) 347 | +proof 348 | @@ -1547 +1548,2 @@ 349 | - (C[x] implies f.x = F(x)) & (not C[x] implies f.x = G(x)); 350 | + (C[x] implies f.x = F(x)) & (not C[x] implies f.x = G(x)) 351 | +proof 352 | @@ -1696 +1698,2 @@ 353 | - (not C[x] implies f.x = G(x)); 354 | + (not C[x] implies f.x = G(x)) 355 | +proof 356 | --- mml/pboole.miz 357 | +++ mml/pboole.miz 358 | @@ -161 +161,2 @@ 359 | - for e being object holds e in X.i iff e in A().i & P[i,e]; 360 | + for e being object holds e in X.i iff e in A().i & P[i,e] 361 | +proof 362 | @@ -1951 +1952,2 @@ 363 | - for i being object st i in I() holds f.i = F(i); 364 | + for i being object st i in I() holds f.i = F(i) 365 | +proof 366 | @@ -2297 +2299,2 @@ 367 | -A1: for i being Element of I() ex j being object st P[i,j]; 368 | +A1: for i being Element of I() ex j being object st P[i,j] 369 | +proof 370 | @@ -2462 +2465,2 @@ 371 | - for i being set st i in I() holds f.i = F(i); 372 | + for i being set st i in I() holds f.i = F(i) 373 | +proof 374 | --- mml/pcomps_1.miz 375 | +++ mml/pcomps_1.miz 376 | @@ -523 +523,2 @@ 377 | -A1: for Z being Subset of T() st Z in X() holds F(Z) in Y(); 378 | +A1: for Z being Subset of T() st Z in X() holds F(Z) in Y() 379 | +proof 380 | --- mml/pcomps_2.miz 381 | +++ mml/pcomps_2.miz 382 | @@ -560 +560,2 @@ 383 | - PM() : P[V]}; 384 | + PM() : P[V]} 385 | +proof 386 | --- mml/pdiff_3.miz 387 | +++ mml/pdiff_3.miz 388 | @@ -29 +29 @@ 389 | - requirements SUBSET, REAL, NUMERALS, ARITHM; 390 | + requirements BOOLE, SUBSET, REAL, NUMERALS, ARITHM; 391 | --- mml/pdiff_5.miz 392 | +++ mml/pdiff_5.miz 393 | @@ -29 +29 @@ 394 | - requirements SUBSET, REAL, NUMERALS, ARITHM; 395 | + requirements BOOLE, SUBSET, REAL, NUMERALS, ARITHM; 396 | --- mml/pre_circ.miz 397 | +++ mml/pre_circ.miz 398 | @@ -85 +85,2 @@ 399 | - implies f.i = F(i)) & (not P[i] implies f.i = G(i)); 400 | + implies f.i = F(i)) & (not P[i] implies f.i = G(i)) 401 | +proof 402 | --- mml/prgcor_1.miz 403 | +++ mml/prgcor_1.miz 404 | @@ -25 +25 @@ 405 | - requirements REAL, NUMERALS, SUBSET, ARITHM; 406 | + requirements BOOLE, REAL, NUMERALS, SUBSET, ARITHM; 407 | --- mml/ramsey_1.miz 408 | +++ mml/ramsey_1.miz 409 | @@ -1399 +1399 @@ 410 | - BinInd2 { P[Nat,Nat] } : P[m,n] 411 | + BinInd2 { P[Nat,Nat] } : for n,m holds P[m,n] 412 | --- mml/realset1.miz 413 | +++ mml/realset1.miz 414 | @@ -23 +23 @@ 415 | - requirements SUBSET; 416 | + requirements BOOLE, SUBSET; 417 | --- mml/recdef_1.miz 418 | +++ mml/recdef_1.miz 419 | @@ -71 +71,2 @@ 420 | -A1: for n being Nat for x being set ex y being set st P[n,x,y ]; 421 | +A1: for n being Nat for x being set ex y being set st P[n,x,y] 422 | +proof 423 | --- mml/relset_1.miz 424 | +++ mml/relset_1.miz 425 | @@ -891 +891,2 @@ 426 | - st for x,y being set holds [x,y] in R iff x in A() & y in B() & P[x,y]; 427 | + st for x,y being set holds [x,y] in R iff x in A() & y in B() & P[x,y] 428 | +proof 429 | --- mml/rewrite1.miz 430 | +++ mml/rewrite1.miz 431 | @@ -194 +194,2 @@ 432 | -A3: len p() > 0 & len q() > 0 & p().len p() = q().1; 433 | +A3: len p() > 0 & len q() > 0 & p().len p() = q().1 434 | +proof 435 | --- mml/roughs_3.miz 436 | +++ mml/roughs_3.miz 437 | @@ -100 +100,2 @@ 438 | - P[B1 \/ B2]; 439 | + P[B1 \/ B2] 440 | + proof 441 | --- mml/scheme1.miz 442 | +++ mml/scheme1.miz 443 | @@ -360 +360,2 @@ 444 | - ex s being Real_Sequence st for n holds s.(2*n) = F(n) & s.(2*n+1) = G(n); 445 | + ex s being Real_Sequence st for n holds s.(2*n) = F(n) & s.(2*n+1) = G(n) 446 | +proof 447 | --- mml/scmfsa8b.miz 448 | +++ mml/scmfsa8b.miz 449 | @@ -36 +36 @@ 450 | - requirements NUMERALS, REAL, SUBSET, ARITHM; 451 | + requirements BOOLE, NUMERALS, REAL, SUBSET, ARITHM; 452 | --- mml/scmfsa8c.miz 453 | +++ mml/scmfsa8c.miz 454 | @@ -37 +37 @@ 455 | - requirements REAL, NUMERALS, SUBSET, ARITHM; 456 | + requirements BOOLE, REAL, NUMERALS, SUBSET, ARITHM; 457 | --- mml/scmfsa_9.miz 458 | +++ mml/scmfsa_9.miz 459 | @@ -33 +33 @@ 460 | - requirements REAL, NUMERALS, SUBSET, ARITHM; 461 | + requirements BOOLE, REAL, NUMERALS, SUBSET, ARITHM; 462 | --- mml/scmfsa_x.miz 463 | +++ mml/scmfsa_x.miz 464 | @@ -35 +35 @@ 465 | - requirements NUMERALS, SUBSET, ARITHM, REAL; 466 | + requirements BOOLE, NUMERALS, SUBSET, ARITHM, REAL; 467 | --- mml/scmpds_6.miz 468 | +++ mml/scmpds_6.miz 469 | @@ -32 +32 @@ 470 | - requirements NUMERALS, REAL, SUBSET, ARITHM; 471 | + requirements BOOLE, NUMERALS, REAL, SUBSET, ARITHM; 472 | --- mml/scmpds_7.miz 473 | +++ mml/scmpds_7.miz 474 | @@ -33 +33 @@ 475 | - requirements REAL, NUMERALS, SUBSET, ARITHM; 476 | + requirements BOOLE, REAL, NUMERALS, SUBSET, ARITHM; 477 | --- mml/seqfunc.miz 478 | +++ mml/seqfunc.miz 479 | @@ -28 +28 @@ 480 | - requirements NUMERALS, SUBSET, ARITHM; 481 | + requirements BOOLE, NUMERALS, SUBSET, ARITHM; 482 | @@ -117 +117,2 @@ 483 | - being Functional_Sequence of D1(), D2() st for n being Nat holds G.n = F(n); 484 | + being Functional_Sequence of D1(), D2() st for n being Nat holds G.n = F(n) 485 | +proof 486 | --- mml/sfmastr3.miz 487 | +++ mml/sfmastr3.miz 488 | @@ -39 +39 @@ 489 | - requirements REAL, NUMERALS, SUBSET, ARITHM; 490 | + requirements BOOLE, REAL, NUMERALS, SUBSET, ARITHM; 491 | --- mml/sin_cos6.miz 492 | +++ mml/sin_cos6.miz 493 | @@ -27 +27 @@ 494 | - requirements SUBSET, NUMERALS, ARITHM, REAL; 495 | + requirements BOOLE, SUBSET, NUMERALS, ARITHM, REAL; 496 | --- mml/sin_cos9.miz 497 | +++ mml/sin_cos9.miz 498 | @@ -31 +31 @@ 499 | - requirements SUBSET, NUMERALS, ARITHM, REAL; 500 | + requirements BOOLE, SUBSET, NUMERALS, ARITHM, REAL; 501 | --- mml/sppol_1.miz 502 | +++ mml/sppol_1.miz 503 | @@ -71 +71,2 @@ 504 | - {F(f(),i): i in dom f() & P[i]} is finite; 505 | + {F(f(),i): i in dom f() & P[i]} is finite 506 | +proof 507 | --- mml/subset_1.miz 508 | +++ mml/subset_1.miz 509 | @@ -824 +824,2 @@ 510 | - in X iff x in A() & P[x]; 511 | + in X iff x in A() & P[x] 512 | +proof 513 | @@ -886 +887,2 @@ 514 | - x being Element of A() holds x in B iff P[x]; 515 | + x being Element of A() holds x in B iff P[x] 516 | +proof 517 | --- mml/topalg_7.miz 518 | +++ mml/topalg_7.miz 519 | @@ -112 +112,2 @@ 520 | - for s being Point of S(), t being Point of T() holds f.(s,t) = F(s,t); 521 | + for s being Point of S(), t being Point of T() holds f.(s,t) = F(s,t) 522 | +proof 523 | @@ -130 +131,2 @@ 524 | - holds f = g; 525 | + holds f = g 526 | +proof 527 | --- mml/tops_4.miz 528 | +++ mml/tops_4.miz 529 | @@ -28 +28 @@ 530 | - requirements SUBSET, REAL; 531 | + requirements BOOLE, SUBSET, REAL; 532 | --- mml/wellset1.miz 533 | +++ mml/wellset1.miz 534 | @@ -94 +94,2 @@ 535 | - ex B st for R being Relation holds R in B iff R in A() & P[R]; 536 | + ex B st for R being Relation holds R in B iff R in A() & P[R] 537 | +proof 538 | --- mml/xxreal_2.miz 539 | +++ mml/xxreal_2.miz 540 | @@ -40 +40,2 @@ 541 | - of INT: m()<=i & i<=n() & P[i]} is finite; 542 | + of INT: m()<=i & i<=n() & P[i]} is finite 543 | +proof 544 | --- mml/yellow18.miz 545 | +++ mml/yellow18.miz 546 | @@ -3945 +3945,2 @@ 547 | -ex y being object st y in B().i & P[i,x,y]; 548 | +ex y being object st y in B().i & P[i,x,y] 549 | +proof 550 | --- mml/zf_refle.miz 551 | +++ mml/zf_refle.miz 552 | @@ -174 +174,2 @@ 553 | -A1: for a being Ordinal of W() ex b being Ordinal of W() st P[a,b]; 554 | +A1: for a being Ordinal of W() ex b being Ordinal of W() st P[a,b] 555 | +proof 556 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | brace_style = "SameLineWhere" 2 | fn_args_layout = "Compressed" 3 | imports_granularity = "Module" 4 | group_imports = "One" 5 | fn_single_line = true 6 | match_arm_blocks = false 7 | trailing_semicolon = false 8 | use_field_init_shorthand = true 9 | where_single_line = true 10 | use_small_heuristics = "Max" 11 | tab_spaces = 2 12 | -------------------------------------------------------------------------------- /src/accom.rs: -------------------------------------------------------------------------------- 1 | use crate::error::report_accom_warning; 2 | use crate::parser::{catch_missing, ParseError, PathResult}; 3 | use crate::reader::DefiniensId; 4 | use crate::types::*; 5 | use crate::{mk_id, CmpStyle, MizPath, VisitMut}; 6 | use std::collections::HashMap; 7 | use std::io; 8 | 9 | mk_id! { 10 | VocId(u32), 11 | SigId(u32), 12 | } 13 | 14 | #[derive(Debug, Default)] 15 | struct VocBuilder { 16 | pub voc: IdxVec, 17 | base: SymbolsBase, 18 | } 19 | 20 | impl VocBuilder { 21 | fn push(&mut self, art: Article, val: &SymbolsBase) -> VocId { 22 | let i = self.voc.push((art, self.base)); 23 | self.base += val; 24 | i 25 | } 26 | 27 | fn get_or_push(&mut self, art: Article, val: &SymbolsBase) -> VocId { 28 | let res = self.voc.enum_iter().find(|p| p.1 .0 == art); 29 | if let Some((i, (_, val2))) = res { 30 | assert_eq!(*self.hi(i) - val2, *val); 31 | i 32 | } else { 33 | self.push(art, val) 34 | } 35 | } 36 | 37 | fn hi(&self, id: VocId) -> &SymbolsBase { 38 | self.voc.get(VocId(id.0 + 1)).map_or(&self.base, |(_, base)| base) 39 | } 40 | 41 | fn truncate(&mut self, len: usize) { 42 | if let Some(voc) = self.voc.0.get_mut(len) { 43 | self.base = voc.1; 44 | self.voc.0.truncate(len) 45 | } 46 | } 47 | } 48 | 49 | #[derive(Debug, Default)] 50 | pub struct SigBuilder { 51 | pub sig: IdxVec, 52 | pub base: ConstructorsBase, 53 | } 54 | 55 | impl SigBuilder { 56 | fn push(&mut self, constrs: Option<&mut Constructors>, art: Article) -> PathResult { 57 | let mut dco = Default::default(); 58 | MizPath { art }.read_dco(false, &mut dco, constrs.is_some())?; 59 | Ok(self.push_from(constrs, art, &mut dco)) 60 | } 61 | 62 | fn push_from( 63 | &mut self, constrs: Option<&mut Constructors>, art: Article, dco: &mut DepConstructors, 64 | ) -> SigId { 65 | if let Some(constrs) = constrs { 66 | let mut rename = RenameConstr::default(); 67 | for &art2 in &dco.sig { 68 | let i = self.get(art2); 69 | rename.push(self, i, true); 70 | } 71 | let i = self.sig.push((art, self.base)); 72 | self.base += dco.counts; 73 | rename.push(self, i, true); 74 | dco.constrs.visit(&mut rename); 75 | constrs.append(&mut dco.constrs); 76 | i 77 | } else { 78 | let i = self.sig.push((art, self.base)); 79 | self.base += dco.counts; 80 | i 81 | } 82 | } 83 | 84 | fn get_or_push(&mut self, constrs: Option<&mut Constructors>, art: Article) -> PathResult { 85 | if let Some(p) = self.sig.enum_iter().find(|p| p.1 .0 == art) { 86 | return Ok(p.0) 87 | } 88 | self.push(constrs, art) 89 | } 90 | 91 | #[allow(clippy::panic)] 92 | fn get(&self, art: Article) -> SigId { 93 | match self.sig.enum_iter().find(|p| p.1 .0 == art) { 94 | Some((id, _)) => id, 95 | None => panic!("{art} declared out of order"), 96 | } 97 | } 98 | 99 | pub fn hi(&self, id: SigId) -> &ConstructorsBase { 100 | self.sig.get(SigId(id.0 + 1)).map_or(&self.base, |(_, base)| base) 101 | } 102 | 103 | fn rename<'a>( 104 | &mut self, sig: &[Article], ctx: Option<&'a Constructors>, 105 | ) -> PathResult> { 106 | let mut rename = RenameConstr { ctx, ..Default::default() }; 107 | let limit = self.sig.len(); 108 | for &art in sig { 109 | let id = self.get_or_push(None, art)?; 110 | rename.push(self, id, id.into_usize() < limit) 111 | } 112 | Ok(rename) 113 | } 114 | 115 | fn truncate(&mut self, len: usize) { 116 | if let Some(sig) = self.sig.0.get_mut(len) { 117 | self.base = sig.1; 118 | self.sig.0.truncate(len) 119 | } 120 | } 121 | } 122 | 123 | #[derive(Debug, Default)] 124 | pub struct Accomodator { 125 | article: Article, 126 | pub dirs: Directives, 127 | pub sig: SigBuilder, 128 | pub articles_vec: IdxVec>, 129 | pub articles: HashMap, 130 | dict: VocBuilder, 131 | pub has_errors: bool, 132 | } 133 | 134 | #[derive(Debug, Default)] 135 | struct RenameSymbol { 136 | trans: Vec<(SymbolsBase, SymbolsBase, bool)>, 137 | base: SymbolsBase, 138 | failed: bool, 139 | } 140 | impl RenameSymbol { 141 | fn push(&mut self, val: &SymbolsBase, tgt: &SymbolsBase, allow: bool) { 142 | self.trans.push((self.base, *tgt, allow)); 143 | self.base += val; 144 | } 145 | fn apply(&mut self, k: SymbolKindClass, n: &mut u32) { 146 | #[allow(clippy::indexing_slicing)] 147 | if let Some(i) = self.trans.partition_point(|(base, _, _)| base.0[k] <= *n).checked_sub(1) { 148 | let (from, to, allow) = &self.trans[i]; 149 | *n = *n - from.0[k] + to.0[k]; 150 | self.failed |= !*allow; 151 | } 152 | } 153 | fn ok(&mut self) -> bool { !std::mem::take(&mut self.failed) } 154 | } 155 | 156 | #[derive(Debug, Default)] 157 | struct RenameConstr<'a> { 158 | trans: Vec<(ConstructorsBase, ConstructorsBase, bool)>, 159 | base: ConstructorsBase, 160 | failed: bool, 161 | ctx: Option<&'a Constructors>, 162 | } 163 | impl RenameConstr<'_> { 164 | #[allow(clippy::indexing_slicing)] 165 | fn push(&mut self, sig: &SigBuilder, i: SigId, allow: bool) { 166 | let lo = self.base; 167 | self.base += *sig.hi(i) - sig.sig[i].1; 168 | self.trans.push((lo, sig.sig[i].1, allow)) 169 | } 170 | 171 | fn apply(&mut self, n: &mut u32, key: impl Fn(&ConstructorsBase) -> u32) { 172 | assert!(*n < key(&self.base)); 173 | if let Some(i) = self.trans.partition_point(|p| key(&p.0) <= *n).checked_sub(1) { 174 | #[allow(clippy::indexing_slicing)] 175 | let (from, to, allow) = &self.trans[i]; 176 | *n = *n - key(from) + key(to); 177 | self.failed |= !*allow; 178 | } 179 | } 180 | 181 | fn ok(&mut self) -> bool { !std::mem::take(&mut self.failed) } 182 | } 183 | 184 | impl VisitMut for RenameConstr<'_> { 185 | const MODIFY_IDS: bool = true; 186 | fn abort(&self) -> bool { self.failed } 187 | fn visit_mode_id(&mut self, n: &mut ModeId) { self.apply(&mut n.0, |b| b.mode) } 188 | fn visit_struct_id(&mut self, n: &mut StructId) { self.apply(&mut n.0, |b| b.struct_mode) } 189 | fn visit_attr_id(&mut self, n: &mut AttrId) { self.apply(&mut n.0, |b| b.attribute) } 190 | fn visit_pred_id(&mut self, n: &mut PredId) { self.apply(&mut n.0, |b| b.predicate) } 191 | fn visit_func_id(&mut self, n: &mut FuncId) { self.apply(&mut n.0, |b| b.functor) } 192 | fn visit_sel_id(&mut self, n: &mut SelId) { self.apply(&mut n.0, |b| b.selector) } 193 | fn visit_aggr_id(&mut self, n: &mut AggrId) { self.apply(&mut n.0, |b| b.aggregate) } 194 | fn visit_attrs(&mut self, attrs: &mut Attrs) { 195 | if let Attrs::Consistent(attrs) = attrs { 196 | for attr in &mut *attrs { 197 | self.visit_attr_id(&mut attr.nr); 198 | self.visit_terms(&mut attr.args); 199 | if self.failed { 200 | return 201 | } 202 | } 203 | attrs.sort_by(|a, b| a.cmp(self.ctx, None, b, CmpStyle::Attr)); 204 | } 205 | } 206 | } 207 | 208 | macro_rules! try_p { 209 | ($self:expr, $e:expr) => { 210 | Accomodator::report_parse_err(&mut $self.has_errors, $e) 211 | }; 212 | ($self:expr, $pos:expr => $msg:expr, $e:expr) => { 213 | Accomodator::report_missing_warning(&mut $self.has_errors, $self.article, $pos, $e, { 214 | #[allow(unused)] 215 | use DirectiveKind::*; 216 | $msg 217 | }) 218 | }; 219 | } 220 | 221 | impl Accomodator { 222 | fn report_parse_err(has_errors: &mut bool, val: PathResult) -> Option { 223 | match val { 224 | Ok(t) => Some(t), 225 | Err((path, e)) => { 226 | e.report(&path); 227 | *has_errors = true; 228 | None 229 | } 230 | } 231 | } 232 | fn report_missing_warning( 233 | has_errors: &mut bool, art: Article, pos: Position, val: PathResult, kind: DirectiveKind, 234 | ) -> Option { 235 | match val { 236 | Ok(t) => Some(t), 237 | Err((path, ParseError::MissingFile)) => { 238 | report_accom_warning(kind, path, art, pos); 239 | None 240 | } 241 | Err((path, e)) => { 242 | e.report(&path); 243 | *has_errors = true; 244 | None 245 | } 246 | } 247 | } 248 | 249 | pub fn build_vocabularies(&self, vocs: &mut Vocabularies) { 250 | for (i, &(art, ref lo)) in self.dict.voc.enum_iter() { 251 | vocs.0.push((art, *self.dict.hi(i) - lo)) 252 | } 253 | } 254 | 255 | /// ProcessVocabularies 256 | pub fn accom_symbols<'a>( 257 | &mut self, mml_vct: &'a [u8], syms: &mut Symbols, priority: &mut Vec<(PriorityKind, u32)>, 258 | mut infinitives: Option<&mut Vec<(PredSymId, &'a str)>>, 259 | ) { 260 | #[allow(clippy::indexing_slicing)] 261 | for &(_, art) in &self.dirs.0[DirectiveKind::Vocabularies] { 262 | let mut voc = Default::default(); 263 | match art.read_vct(mml_vct, &mut voc) { 264 | Ok(true) => {} 265 | Ok(false) => { 266 | // TODO: private vocabularies 267 | println!( 268 | "error: {}: vocabulary for {art} not found (TODO: private vocabularies)", 269 | crate::mml_vct_path() 270 | ); 271 | self.has_errors = true; 272 | continue 273 | } 274 | Err(e) => { 275 | e.report(crate::mml_vct_path().as_ref()); 276 | self.has_errors = true; 277 | continue 278 | } 279 | } 280 | let hidden = self.dict.voc.is_empty(); 281 | self.dict.voc.push((art, self.dict.base)); 282 | if hidden { 283 | // The setup here is a bit weird. The HIDDEN article has some tokens defined 284 | // here by hard-coding, and other tokens defined in mml.vct. The net result is 285 | // that even though mml.vct says "G0 K0 L0 M1 O0 R2 U0 V1" it is really 286 | // treated as "G0 K3 L3 M2 O0 R3 U0 V1" when appearing in .dno files. 287 | for &(c, token) in SymbolData::BUILTIN_SYMBOLS { 288 | let n = self.dict.base.0[c]; 289 | self.dict.base.0[c] += 1; 290 | syms.push(((c, n).into(), token.to_owned())); 291 | } 292 | } 293 | for SymbolData { kind, token } in voc.symbols { 294 | let c = kind.class(); 295 | let n = self.dict.base.0[c]; 296 | self.dict.base.0[c] += 1; 297 | match kind { 298 | SymbolDataKind::LeftBrk => 299 | priority.push((PriorityKind::LeftBrk(LeftBrkSymId(n)), DEFAULT_PRIO)), 300 | SymbolDataKind::RightBrk => 301 | priority.push((PriorityKind::RightBrk(RightBrkSymId(n)), DEFAULT_PRIO)), 302 | SymbolDataKind::Func { prio } => 303 | priority.push((PriorityKind::Functor(FuncSymId(n)), prio)), 304 | SymbolDataKind::Pred { infinitive: Some(inf) } => 305 | if let Some(infs) = &mut infinitives { 306 | infs.push((PredSymId(n), inf)) 307 | }, 308 | _ => {} 309 | } 310 | syms.push(((c, n).into(), token.to_owned())) 311 | } 312 | } 313 | } 314 | 315 | #[allow(clippy::indexing_slicing)] 316 | pub fn accom_articles(&mut self) { 317 | let mut id = ArticleId(1); // 0 is reserved for SELF 318 | self.articles_vec.push(None); 319 | for &(_, art) in &self.dirs.0[DirectiveKind::Theorems] { 320 | self.articles_vec.push(Some(art)); 321 | assert!(self.articles.insert(art, id.fresh()).is_none()) 322 | } 323 | for &(_, art) in &self.dirs.0[DirectiveKind::Schemes] { 324 | self.articles.entry(art).or_insert_with(|| id.fresh()); 325 | } 326 | } 327 | 328 | /// ProcessConstructors 329 | #[allow(clippy::indexing_slicing)] 330 | pub fn accom_constructors(&mut self, constrs: &mut Constructors) -> io::Result<()> { 331 | for &(pos, art) in &self.dirs.0[DirectiveKind::Constructors] { 332 | let mut dco = Default::default(); 333 | let result = MizPath { art }.read_dco(false, &mut dco, true); 334 | if try_p!(self, pos => Constructors, result).is_none() { 335 | continue 336 | } 337 | for &art2 in &dco.sig { 338 | try_p!(self, self.sig.get_or_push(Some(constrs), art2)); 339 | } 340 | if !self.sig.sig.0.iter().any(|p| p.0 == art) { 341 | self.sig.push_from(Some(constrs), art, &mut dco); 342 | } 343 | } 344 | Ok(()) 345 | } 346 | 347 | /// ProcessRequirements 348 | #[allow(clippy::indexing_slicing)] 349 | pub fn accom_requirements( 350 | &mut self, ctx: &Constructors, idx: &mut RequirementIndexes, 351 | ) -> io::Result<()> { 352 | for &(pos, art) in &self.dirs.0[DirectiveKind::Requirements] { 353 | let mut dre = Default::default(); 354 | if try_p!(self, pos => Requirements, MizPath { art }.read_dre(&mut dre)).is_none() { 355 | continue 356 | } 357 | let len = self.sig.sig.len(); 358 | let Some(mut rename) = try_p!(self, self.sig.rename(&dre.sig, Some(ctx))) else { continue }; 359 | for DepRequirement { req, mut kind } in dre.reqs { 360 | kind.visit(&mut rename); 361 | assert!(rename.ok() && kind.lt(&ctx.len()), "inaccessible requirement"); 362 | idx.set(req, kind); 363 | } 364 | self.sig.truncate(len); 365 | } 366 | Ok(()) 367 | } 368 | 369 | /// ProcessClusters 370 | #[allow(clippy::indexing_slicing)] 371 | pub fn accom_clusters(&mut self, ctx: &Constructors, clusters: &mut Clusters) -> io::Result<()> { 372 | for &(_, art) in &self.dirs.0[DirectiveKind::Registrations] { 373 | let mut dcl = Default::default(); 374 | let result = MizPath { art }.read_dcl(false, &mut dcl); 375 | let Some(_) = try_p!(self, catch_missing(result)) else { continue }; 376 | let len = self.sig.sig.len(); 377 | let Some(mut rename) = try_p!(self, self.sig.rename(&dcl.sig, Some(ctx))) else { continue }; 378 | for mut cl in dcl.cl.registered { 379 | cl.visit(&mut rename); 380 | if rename.ok() { 381 | clusters.registered.push(cl); 382 | } 383 | } 384 | for mut cl in dcl.cl.functor { 385 | cl.visit(&mut rename); 386 | if rename.ok() { 387 | clusters.functor.0.push(cl); 388 | } 389 | } 390 | for mut cl in dcl.cl.conditional { 391 | cl.visit(&mut rename); 392 | if rename.ok() { 393 | clusters.conditional.push(ctx, cl); 394 | } 395 | } 396 | self.sig.truncate(len); 397 | } 398 | Ok(()) 399 | } 400 | 401 | /// ProcessNotation 402 | #[allow(clippy::indexing_slicing)] 403 | pub fn accom_notations( 404 | &mut self, fmt_map: &mut HashMap, mut fmts: Option<&mut Formats>, 405 | pats: &mut Vec, 406 | ) -> io::Result<()> { 407 | if let Some(fmts) = &mut fmts { 408 | assert_eq!(fmts.formats.push(Format::Attr(FormatAttr::STRICT)), FormatId::STRICT); 409 | fmt_map.insert(Format::Attr(FormatAttr::STRICT), FormatId::STRICT); 410 | } 411 | for &(pos, art) in &self.dirs.0[DirectiveKind::Notations] { 412 | let dict_len = self.dict.voc.len(); 413 | let mut dno = Default::default(); 414 | let result = MizPath { art }.read_dno(false, &mut dno); 415 | let Some(_) = try_p!(self, pos => Notations, result) else { continue }; 416 | let mut s_rename = RenameSymbol::default(); 417 | for &(art, ref val) in &dno.vocs.0 { 418 | let i = self.dict.get_or_push(art, val); 419 | s_rename.push(val, &self.dict.voc[i].1, i.0 < dict_len as u32); 420 | } 421 | let sig_len = self.sig.sig.len(); 422 | let Some(mut rename) = try_p!(self, self.sig.rename(&dno.sig, None)) else { continue }; 423 | for Pattern { article, abs_nr, mut kind, mut fmt, mut primary, visible, pos } in dno.pats { 424 | fmt.visit_mut(|k, c| s_rename.apply(k, c)); 425 | if s_rename.ok() { 426 | if let Some(fmt) = match &mut fmts { 427 | Some(fmts) => Some(*fmt_map.entry(fmt).or_insert_with(|| fmts.formats.push(fmt))), 428 | None => fmt_map.get(&fmt).copied(), 429 | } { 430 | kind.visit(&mut rename); 431 | primary.visit(&mut rename); 432 | if rename.ok() { 433 | pats.push(Pattern { article, abs_nr, kind, fmt, primary, visible, pos }) 434 | } 435 | } 436 | } 437 | } 438 | self.dict.truncate(dict_len); 439 | self.sig.truncate(sig_len); 440 | } 441 | pats.sort_by_key(|pat| pat.kind.class() as u8); 442 | Ok(()) 443 | } 444 | 445 | /// ProcessIdentify 446 | #[allow(clippy::indexing_slicing)] 447 | pub fn accom_identify_regs( 448 | &mut self, ctx: &Constructors, ids: &mut Vec, 449 | ) -> io::Result<()> { 450 | for &(_, art) in &self.dirs.0[DirectiveKind::Registrations] { 451 | let (mut sig, mut did) = Default::default(); 452 | let result = MizPath { art }.read_did(false, &mut sig, &mut did); 453 | let Some(_) = try_p!(self, catch_missing(result)) else { continue }; 454 | let len = self.sig.sig.len(); 455 | let Some(mut rename) = try_p!(self, self.sig.rename(&sig, Some(ctx))) else { continue }; 456 | for mut id in did { 457 | id.visit(&mut rename); 458 | if rename.ok() { 459 | ids.push(id); 460 | } 461 | } 462 | self.sig.truncate(len); 463 | } 464 | Ok(()) 465 | } 466 | 467 | /// ProcessReductions 468 | #[allow(clippy::indexing_slicing)] 469 | pub fn accom_reduction_regs( 470 | &mut self, ctx: &Constructors, reds: &mut Vec, 471 | ) -> io::Result<()> { 472 | for &(_, art) in &self.dirs.0[DirectiveKind::Registrations] { 473 | let (mut sig, mut drd) = Default::default(); 474 | let result = MizPath { art }.read_drd(false, &mut sig, &mut drd); 475 | let Some(_) = try_p!(self, catch_missing(result)) else { continue }; 476 | let len = self.sig.sig.len(); 477 | let Some(mut rename) = try_p!(self, self.sig.rename(&sig, Some(ctx))) else { continue }; 478 | for mut red in drd { 479 | red.visit(&mut rename); 480 | if rename.ok() { 481 | reds.push(red); 482 | } 483 | } 484 | self.sig.truncate(len); 485 | } 486 | Ok(()) 487 | } 488 | 489 | /// ProcessProperties 490 | #[allow(clippy::indexing_slicing)] 491 | pub fn accom_properties( 492 | &mut self, ctx: &Constructors, props: &mut Vec, 493 | ) -> io::Result<()> { 494 | for &(_, art) in &self.dirs.0[DirectiveKind::Registrations] { 495 | let (mut sig, mut dpr) = Default::default(); 496 | let result = MizPath { art }.read_dpr(false, &mut sig, &mut dpr); 497 | let Some(_) = try_p!(self, catch_missing(result)) else { continue }; 498 | let len = self.sig.sig.len(); 499 | let Some(mut rename) = try_p!(self, self.sig.rename(&sig, Some(ctx))) else { continue }; 500 | for mut prop in dpr { 501 | prop.visit(&mut rename); 502 | if rename.ok() { 503 | props.push(prop); 504 | } 505 | } 506 | self.sig.truncate(len); 507 | } 508 | Ok(()) 509 | } 510 | 511 | /// ProcessDefinitions 512 | #[allow(clippy::indexing_slicing)] 513 | pub fn accom_definitions( 514 | &mut self, ctx: &Constructors, kind: DirectiveKind, defs: &mut Vec, 515 | ) -> io::Result<()> { 516 | for &(pos, art) in &self.dirs.0[kind] { 517 | let (mut sig, mut def) = Default::default(); 518 | let result = MizPath { art }.read_def(false, &mut sig, &mut def); 519 | let Some(_) = try_p!(self, pos => kind, result) else { continue }; 520 | let len = self.sig.sig.len(); 521 | let Some(mut rename) = try_p!(self, self.sig.rename(&sig, Some(ctx))) else { continue }; 522 | for mut def in def { 523 | def.visit(&mut rename); 524 | if rename.ok() { 525 | defs.push(def); 526 | } 527 | } 528 | self.sig.truncate(len); 529 | } 530 | Ok(()) 531 | } 532 | 533 | /// ProcessTheorems 534 | #[allow(clippy::indexing_slicing)] 535 | pub fn accom_theorems( 536 | &mut self, write_eth: bool, ctx: &Constructors, def_map: &mut HashMap, 537 | libs: &mut Libraries, 538 | ) -> io::Result<()> { 539 | let mut w = write_eth.then(|| MizPath { art: self.article }.write_eth()); 540 | let mut defthms = DefiniensId::default(); 541 | for &(pos, art) in &self.dirs.0[DirectiveKind::Theorems] { 542 | let mut thms = Default::default(); 543 | let result = MizPath { art }.read_the(false, &mut thms); 544 | let Some(_) = try_p!(self, pos => Theorems, result) else { continue }; 545 | let len = self.sig.sig.len(); 546 | let Some(mut rename) = try_p!(self, self.sig.rename(&thms.sig, Some(ctx))) else { continue }; 547 | let (mut thm_nr, mut def_nr) = <(ThmId, DefId)>::default(); 548 | let lib_nr = self.articles[&art]; 549 | for mut thm in thms.thm { 550 | match thm.kind { 551 | TheoremKind::Def(_) | TheoremKind::CanceledDef => { 552 | let def_nr = def_nr.fresh(); 553 | thm.stmt.visit(&mut rename); 554 | if rename.ok() { 555 | if let Some(w) = &mut w { 556 | w.write(lib_nr, def_nr.0, art, &thm) 557 | } 558 | libs.def.insert((lib_nr, def_nr), thm.stmt); 559 | if let TheoremKind::Def(_) = thm.kind { 560 | def_map.insert((lib_nr, def_nr), defthms.fresh()); 561 | } 562 | } 563 | } 564 | TheoremKind::Thm | TheoremKind::CanceledThm => { 565 | let thm_nr = thm_nr.fresh(); 566 | thm.stmt.visit(&mut rename); 567 | if rename.ok() { 568 | if let Some(w) = &mut w { 569 | w.write(lib_nr, thm_nr.0, art, &thm) 570 | } 571 | libs.thm.insert((lib_nr, thm_nr), thm.stmt); 572 | } 573 | } 574 | } 575 | } 576 | self.sig.truncate(len); 577 | } 578 | if let Some(w) = w { 579 | w.finish() 580 | } 581 | Ok(()) 582 | } 583 | 584 | /// ProcessSchemes 585 | #[allow(clippy::indexing_slicing)] 586 | pub fn accom_schemes( 587 | &mut self, write_esh: bool, ctx: &Constructors, libs: &mut Libraries, 588 | ) -> io::Result<()> { 589 | let mut w = write_esh.then(|| MizPath { art: self.article }.write_esh()); 590 | for &(pos, art) in &self.dirs.0[DirectiveKind::Schemes] { 591 | let mut schs = Default::default(); 592 | let result = MizPath { art }.read_sch(false, &mut schs); 593 | let Some(_) = try_p!(self, pos => Schemes, result) else { continue }; 594 | let len = self.sig.sig.len(); 595 | let Some(mut rename) = try_p!(self, self.sig.rename(&schs.sig, Some(ctx))) else { continue }; 596 | let mut sch_nr = SchId::default(); 597 | let lib_nr = self.articles[&art]; 598 | for sch in schs.sch { 599 | let sch_nr = sch_nr.fresh(); 600 | if let Some(mut sch) = sch { 601 | sch.visit(&mut rename); 602 | if rename.ok() { 603 | if let Some(w) = &mut w { 604 | w.write(lib_nr, sch_nr.0, art, &sch) 605 | } 606 | libs.sch.insert((lib_nr, sch_nr), sch); 607 | } 608 | } 609 | } 610 | self.sig.truncate(len); 611 | } 612 | if let Some(w) = w { 613 | w.finish() 614 | } 615 | Ok(()) 616 | } 617 | } 618 | -------------------------------------------------------------------------------- /src/ast.rs: -------------------------------------------------------------------------------- 1 | use crate::mk_id; 2 | use crate::types::*; 3 | use serde_derive::Serialize; 4 | use std::rc::Rc; 5 | use std::str::FromStr; 6 | 7 | #[derive(Clone, Debug, Serialize)] 8 | pub struct Variable { 9 | pub pos: Position, 10 | /// 'varnr' attribute, MSLocusObj.nVarNr, MSVariableObj.nVarNr 11 | #[serde(skip_serializing_if = "Option::is_none")] 12 | pub var: Option, 13 | pub spelling: Rc, 14 | } 15 | 16 | impl Variable { 17 | #[track_caller] 18 | pub fn var(&self) -> ConstId { self.var.expect("variable is not resolved") } 19 | 20 | pub fn to_term(&self) -> Term { 21 | Term::Var { 22 | pos: self.pos, 23 | kind: self.var.map(VarKind::Const), 24 | spelling: (*self.spelling).into(), 25 | } 26 | } 27 | } 28 | 29 | #[derive(Copy, Clone, Debug, Serialize)] 30 | pub enum VarKind { 31 | Bound(BoundId), 32 | Const(ConstId), 33 | Reserved(ReservedId), 34 | } 35 | 36 | #[derive(Copy, Clone, Debug, Serialize)] 37 | pub enum PrivFuncKind { 38 | PrivFunc(PrivFuncId), 39 | SchFunc(SchFuncId), 40 | } 41 | 42 | #[derive(Debug, Serialize)] 43 | pub enum Term { 44 | Placeholder { 45 | pos: Position, 46 | nr: LocusId, 47 | }, 48 | Numeral { 49 | pos: Position, 50 | value: u32, 51 | }, 52 | Var { 53 | pos: Position, 54 | #[serde(skip_serializing_if = "Option::is_none")] 55 | kind: Option, 56 | spelling: String, 57 | }, 58 | PrivFunc { 59 | pos: Position, 60 | #[serde(skip_serializing_if = "Option::is_none")] 61 | kind: Option, 62 | spelling: Rc, 63 | #[serde(skip_serializing_if = "Vec::is_empty")] 64 | args: Vec, 65 | }, 66 | Infix { 67 | pos: Position, 68 | sym: (FuncSymId, String), 69 | left: u8, 70 | #[serde(skip_serializing_if = "Vec::is_empty")] 71 | args: Vec, 72 | }, 73 | Bracket { 74 | pos: Position, 75 | lsym: (LeftBrkSymId, String), 76 | rsym: (RightBrkSymId, String), 77 | #[serde(skip_serializing_if = "Vec::is_empty")] 78 | args: Vec, 79 | }, 80 | Aggregate { 81 | pos: Position, 82 | sym: (StructSymId, String), 83 | #[serde(skip_serializing_if = "Vec::is_empty")] 84 | args: Vec, 85 | }, 86 | SubAggr { 87 | pos: Position, 88 | sym: (StructSymId, String), 89 | arg: Box, 90 | }, 91 | Selector { 92 | pos: Position, 93 | sym: (SelSymId, String), 94 | arg: Box, 95 | }, 96 | InternalSelector { 97 | pos: Position, 98 | sym: (SelSymId, String), 99 | #[serde(skip_serializing_if = "Option::is_none")] 100 | id: Option, 101 | }, 102 | Qua { 103 | pos: Position, 104 | term: Box, 105 | ty: Box, 106 | }, 107 | The { 108 | pos: Position, 109 | ty: Box, 110 | }, 111 | Fraenkel { 112 | pos: Position, 113 | vars: Vec, 114 | scope: Box, 115 | #[serde(skip_serializing_if = "Option::is_none")] 116 | compr: Option>, 117 | #[serde(skip)] 118 | nameck: Option>, 119 | }, 120 | It { 121 | pos: Position, 122 | }, 123 | } 124 | impl Term { 125 | pub fn pos(&self) -> Position { 126 | match self { 127 | Term::Placeholder { pos, .. } 128 | | Term::Numeral { pos, .. } 129 | | Term::Var { pos, .. } 130 | | Term::PrivFunc { pos, .. } 131 | | Term::Infix { pos, .. } 132 | | Term::Bracket { pos, .. } 133 | | Term::Aggregate { pos, .. } 134 | | Term::SubAggr { pos, .. } 135 | | Term::Selector { pos, .. } 136 | | Term::InternalSelector { pos, .. } 137 | | Term::Qua { pos, .. } 138 | | Term::The { pos, .. } 139 | | Term::Fraenkel { pos, .. } 140 | | Term::It { pos } => *pos, 141 | } 142 | } 143 | } 144 | 145 | mk_id! { 146 | ReservedId(u32), 147 | ResGroupId(u32), 148 | } 149 | 150 | #[derive(Debug, Serialize)] 151 | pub enum Type { 152 | Mode { 153 | pos: Position, 154 | sym: (ModeSymId, String), 155 | #[serde(skip_serializing_if = "Vec::is_empty")] 156 | args: Vec, 157 | }, 158 | Struct { 159 | pos: Position, 160 | sym: (StructSymId, String), 161 | #[serde(skip_serializing_if = "Vec::is_empty")] 162 | args: Vec, 163 | }, 164 | Cluster { 165 | pos: Position, 166 | #[serde(skip_serializing_if = "Vec::is_empty")] 167 | attrs: Vec, 168 | ty: Box, 169 | }, 170 | Reservation { 171 | pos: Position, 172 | group: ResGroupId, 173 | #[serde(skip_serializing_if = "Vec::is_empty")] 174 | subst: Vec, 175 | }, 176 | } 177 | impl Type { 178 | pub fn pos(&self) -> Position { 179 | match self { 180 | Type::Mode { pos, .. } 181 | | Type::Struct { pos, .. } 182 | | Type::Cluster { pos, .. } 183 | | Type::Reservation { pos, .. } => *pos, 184 | } 185 | } 186 | } 187 | 188 | #[derive(Copy, Clone, Debug, Serialize)] 189 | pub enum PrivPredKind { 190 | PrivPred(PrivPredId), 191 | SchPred(SchPredId), 192 | } 193 | 194 | #[derive(Copy, Clone, Debug, Serialize)] 195 | pub enum FormulaBinop { 196 | And, 197 | Or, 198 | Imp, 199 | Iff, 200 | FlexAnd, 201 | FlexOr, 202 | } 203 | 204 | #[derive(Copy, Clone, Debug, Serialize)] 205 | pub enum FormulaBinder { 206 | ForAll, 207 | Exists, 208 | } 209 | 210 | #[derive(Debug, Serialize)] 211 | pub struct Pred { 212 | pub pos: Position, 213 | pub positive: bool, 214 | pub sym: (PredSymId, String), 215 | pub left: u8, 216 | #[serde(skip_serializing_if = "Vec::is_empty")] 217 | pub args: Vec, 218 | } 219 | 220 | #[derive(Debug, Serialize)] 221 | pub struct PredRhs { 222 | pub pos: Position, 223 | pub positive: bool, 224 | pub sym: (PredSymId, String), 225 | #[serde(skip_serializing_if = "Vec::is_empty")] 226 | pub right: Vec, 227 | } 228 | 229 | #[derive(Debug, Serialize)] 230 | pub enum Formula { 231 | Not { 232 | pos: Position, 233 | f: Box, 234 | }, 235 | Binop { 236 | kind: FormulaBinop, 237 | pos: Position, 238 | f1: Box, 239 | f2: Box, 240 | }, 241 | Pred(Box), 242 | ChainPred { 243 | pos: Position, 244 | first: Box, 245 | rest: Vec, 246 | }, 247 | PrivPred { 248 | pos: Position, 249 | #[serde(skip_serializing_if = "Option::is_none")] 250 | kind: Option, 251 | spelling: Rc, 252 | #[serde(skip_serializing_if = "Vec::is_empty")] 253 | args: Vec, 254 | }, 255 | Attr { 256 | pos: Position, 257 | positive: bool, 258 | term: Box, 259 | #[serde(skip_serializing_if = "Vec::is_empty")] 260 | attrs: Vec, 261 | }, 262 | Is { 263 | pos: Position, 264 | positive: bool, 265 | term: Box, 266 | ty: Box, 267 | }, 268 | Binder { 269 | kind: FormulaBinder, 270 | pos: Position, 271 | vars: Vec, 272 | #[serde(skip_serializing_if = "Option::is_none")] 273 | st: Option>, 274 | scope: Box, 275 | }, 276 | False { 277 | pos: Position, 278 | }, 279 | Thesis { 280 | pos: Position, 281 | }, 282 | } 283 | impl Formula { 284 | pub fn pos(&self) -> Position { 285 | match self { 286 | Formula::Pred(p) => p.pos, 287 | Formula::Not { pos, .. } 288 | | Formula::Binop { pos, .. } 289 | | Formula::False { pos, .. } 290 | | Formula::ChainPred { pos, .. } 291 | | Formula::PrivPred { pos, .. } 292 | | Formula::Attr { pos, .. } 293 | | Formula::Is { pos, .. } 294 | | Formula::Binder { pos, .. } 295 | | Formula::Thesis { pos } => *pos, 296 | } 297 | } 298 | } 299 | 300 | #[derive(Debug, Serialize)] 301 | pub struct Proposition { 302 | #[serde(skip_serializing_if = "Option::is_none")] 303 | pub label: Option>, 304 | pub f: Formula, 305 | } 306 | 307 | #[derive(Debug, Serialize)] 308 | pub enum SchRef { 309 | #[serde(untagged)] 310 | Resolved(ArticleId, SchId), 311 | #[serde(untagged)] 312 | UnresolvedPriv(String), 313 | } 314 | 315 | #[derive(Debug, Serialize)] 316 | pub enum InferenceKind { 317 | By { 318 | #[serde(skip_serializing_if = "Option::is_none")] 319 | link: Option, 320 | }, 321 | From { 322 | sch: SchRef, 323 | }, 324 | } 325 | 326 | #[derive(Debug, Clone, Serialize)] 327 | pub enum RefFragment { 328 | Thm { pos: Position, id: ThmId }, 329 | Def { pos: Position, id: DefId }, 330 | } 331 | #[derive(Debug, Clone, Serialize)] 332 | pub enum ReferenceKind { 333 | Priv(#[serde(skip_serializing_if = "Option::is_none")] Option), 334 | Global(ArticleId, Vec), 335 | #[serde(untagged)] 336 | UnresolvedPriv(String), 337 | } 338 | 339 | #[derive(Debug, Clone, Serialize)] 340 | pub struct Reference { 341 | pub pos: Position, 342 | pub kind: ReferenceKind, 343 | } 344 | 345 | #[derive(Debug, Serialize)] 346 | pub enum Justification { 347 | Inference { 348 | pos: Position, 349 | kind: InferenceKind, 350 | #[serde(skip_serializing_if = "Vec::is_empty")] 351 | refs: Vec, 352 | }, 353 | Block { 354 | pos: (Position, Position), 355 | #[serde(skip_serializing_if = "Vec::is_empty")] 356 | items: Vec, 357 | }, 358 | } 359 | 360 | #[derive(Debug, Serialize)] 361 | pub enum SchemeBinderGroup { 362 | Pred { 363 | pos: Position, 364 | vars: Vec, 365 | #[serde(skip_serializing_if = "Vec::is_empty")] 366 | tys: Vec, 367 | }, 368 | Func { 369 | pos: Position, 370 | vars: Vec, 371 | #[serde(skip_serializing_if = "Vec::is_empty")] 372 | tys: Vec, 373 | ret: Type, 374 | }, 375 | } 376 | 377 | #[derive(Debug, Serialize)] 378 | pub struct BinderGroup { 379 | pub vars: Vec, 380 | // The vars list must be length 1 when this is None 381 | #[serde(skip_serializing_if = "Option::is_none")] 382 | pub ty: Option>, 383 | } 384 | 385 | #[derive(Debug, Serialize)] 386 | pub enum ReconsiderVar { 387 | /// Only occurs in wsm 388 | Var(Variable), 389 | Equality { 390 | var: Variable, 391 | tm: Term, 392 | }, 393 | } 394 | 395 | #[derive(Debug, Serialize)] 396 | pub struct Item { 397 | pub pos: Position, 398 | pub kind: ItemKind, 399 | } 400 | 401 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] 402 | pub enum CaseKind { 403 | Case, 404 | Suppose, 405 | } 406 | impl CaseKind { 407 | pub fn name(self) -> &'static str { 408 | match self { 409 | CaseKind::Case => "Case", 410 | CaseKind::Suppose => "Suppose", 411 | } 412 | } 413 | pub fn block_name(self) -> &'static str { 414 | match self { 415 | CaseKind::Case => "CaseBlock", 416 | CaseKind::Suppose => "SupposeBlock", 417 | } 418 | } 419 | } 420 | 421 | #[derive(Debug, Serialize)] 422 | pub struct Field { 423 | pub pos: Position, 424 | pub sym: (SelSymId, Rc), 425 | } 426 | 427 | #[derive(Debug, Serialize)] 428 | pub struct FieldGroup { 429 | pub pos: Position, 430 | pub vars: Vec, 431 | pub ty: Type, 432 | } 433 | 434 | #[derive(Debug, Serialize)] 435 | pub struct PatternStruct { 436 | pub sym: (StructSymId, String), 437 | #[serde(skip_serializing_if = "Vec::is_empty")] 438 | pub args: Vec, 439 | } 440 | 441 | impl PatternStruct { 442 | pub fn to_mode_format(&self) -> FormatStruct { 443 | FormatStruct { sym: self.sym.0, args: self.args.len() as u8 } 444 | } 445 | pub fn to_aggr_format(&self, n: usize) -> FormatAggr { 446 | FormatAggr { sym: self.sym.0, args: n as u8 } 447 | } 448 | pub fn to_subaggr_format(&self) -> StructSymId { self.sym.0 } 449 | } 450 | 451 | #[derive(Debug, Serialize)] 452 | pub enum PatternFunc { 453 | Func { 454 | pos: Position, 455 | sym: (FuncSymId, String), 456 | left: u8, 457 | #[serde(skip_serializing_if = "Vec::is_empty")] 458 | args: Vec, 459 | }, 460 | Bracket { 461 | pos: Position, 462 | lsym: (LeftBrkSymId, String), 463 | rsym: (RightBrkSymId, String), 464 | #[serde(skip_serializing_if = "Vec::is_empty")] 465 | args: Vec, 466 | }, 467 | } 468 | 469 | impl PatternFunc { 470 | pub fn pos(&self) -> Position { 471 | let (PatternFunc::Func { pos, .. } | PatternFunc::Bracket { pos, .. }) = *self; 472 | pos 473 | } 474 | pub fn args(&self) -> &[Variable] { 475 | let (PatternFunc::Func { args, .. } | PatternFunc::Bracket { args, .. }) = self; 476 | args 477 | } 478 | pub fn args_mut(&mut self) -> &mut [Variable] { 479 | let (PatternFunc::Func { args, .. } | PatternFunc::Bracket { args, .. }) = self; 480 | args 481 | } 482 | pub fn to_format(&self) -> FormatFunc { 483 | match *self { 484 | PatternFunc::Func { ref sym, left, ref args, .. } => 485 | FormatFunc::Func { sym: sym.0, left, right: args.len() as u8 - left }, 486 | PatternFunc::Bracket { ref lsym, ref rsym, ref args, .. } => 487 | FormatFunc::Bracket { lsym: lsym.0, rsym: rsym.0, args: args.len() as u8 }, 488 | } 489 | } 490 | } 491 | 492 | #[derive(Debug, Serialize)] 493 | pub struct PatternPred { 494 | pub pos: Position, 495 | pub sym: (PredSymId, String), 496 | pub left: u8, 497 | #[serde(skip_serializing_if = "Vec::is_empty")] 498 | pub args: Vec, 499 | } 500 | impl PatternPred { 501 | pub fn to_format(&self) -> FormatPred { 502 | FormatPred { sym: self.sym.0, left: self.left, right: self.args.len() as u8 - self.left } 503 | } 504 | } 505 | 506 | #[derive(Debug, Serialize)] 507 | pub struct PatternMode { 508 | pub pos: Position, 509 | pub sym: (ModeSymId, String), 510 | #[serde(skip_serializing_if = "Vec::is_empty")] 511 | pub args: Vec, 512 | } 513 | impl PatternMode { 514 | pub fn to_format(&self) -> FormatMode { 515 | FormatMode { sym: self.sym.0, args: self.args.len() as u8 } 516 | } 517 | } 518 | 519 | #[derive(Debug, Serialize)] 520 | pub struct PatternAttr { 521 | pub pos: Position, 522 | pub sym: (AttrSymId, String), 523 | #[serde(skip_serializing_if = "Vec::is_empty")] 524 | pub args: Vec, 525 | } 526 | impl PatternAttr { 527 | pub fn to_format(&self) -> FormatAttr { 528 | FormatAttr { sym: self.sym.0, args: self.args.len() as u8 } 529 | } 530 | } 531 | 532 | #[derive(Debug, Serialize)] 533 | pub enum Pattern { 534 | Pred(Box), 535 | Func(Box), 536 | Mode(Box), 537 | Attr(Box), 538 | } 539 | 540 | impl Pattern { 541 | pub fn pos(&self) -> Position { 542 | match self { 543 | Pattern::Pred(p) => p.pos, 544 | Pattern::Func(p) => p.pos(), 545 | Pattern::Mode(p) => p.pos, 546 | Pattern::Attr(p) => p.pos, 547 | } 548 | } 549 | } 550 | 551 | #[derive(Debug, Serialize)] 552 | pub struct DefCase { 553 | pub case: Box, 554 | pub guard: Box, 555 | } 556 | 557 | #[derive(Debug, Serialize)] 558 | pub struct DefBody { 559 | /// nPartialDefinientia 560 | #[serde(skip_serializing_if = "Vec::is_empty")] 561 | pub cases: Vec>, 562 | #[serde(skip_serializing_if = "Option::is_none")] 563 | pub otherwise: Option>, 564 | } 565 | 566 | #[derive(Debug, Serialize)] 567 | pub enum DefValue { 568 | Term(DefBody), 569 | Formula(DefBody), 570 | } 571 | 572 | #[derive(Debug, Serialize)] 573 | pub struct Definiens { 574 | pub pos: Position, 575 | #[serde(skip_serializing_if = "Option::is_none")] 576 | pub label: Option>, 577 | pub kind: DefValue, 578 | } 579 | 580 | #[derive(Debug, Serialize)] 581 | pub enum DefModeKind { 582 | Expandable { 583 | expansion: Box, 584 | }, 585 | Standard { 586 | #[serde(skip_serializing_if = "Option::is_none")] 587 | spec: Option>, 588 | #[serde(skip_serializing_if = "Option::is_none")] 589 | def: Option>, 590 | }, 591 | } 592 | 593 | #[derive(Debug, Serialize)] 594 | pub enum PatternRedef { 595 | Pred { new: Box, orig: Box, pos: bool }, 596 | Func { new: Box, orig: Box }, 597 | Mode { new: Box, orig: Box }, 598 | Attr { new: Box, orig: Box, pos: bool }, 599 | } 600 | 601 | #[derive(Debug, Serialize)] 602 | pub enum Attr { 603 | Attr { 604 | pos: Position, 605 | sym: (AttrSymId, String), 606 | #[serde(skip_serializing_if = "Vec::is_empty")] 607 | args: Vec, 608 | }, 609 | Non { 610 | pos: Position, 611 | attr: Box, 612 | }, 613 | } 614 | 615 | impl Attr { 616 | pub fn pos(&self) -> Position { 617 | match *self { 618 | Attr::Attr { pos, .. } | Attr::Non { pos, .. } => pos, 619 | } 620 | } 621 | } 622 | 623 | #[derive(Debug, Serialize)] 624 | pub enum ClusterDeclKind { 625 | Exist { 626 | concl: Vec, 627 | ty: Box, 628 | }, 629 | Func { 630 | term: Box, 631 | concl: Vec, 632 | #[serde(skip_serializing_if = "Option::is_none")] 633 | ty: Option>, 634 | }, 635 | Cond { 636 | #[serde(skip_serializing_if = "Vec::is_empty")] 637 | antecedent: Vec, 638 | concl: Vec, 639 | ty: Box, 640 | }, 641 | } 642 | 643 | #[derive(Debug, Serialize)] 644 | pub struct Label { 645 | pub pos: Position, 646 | pub id: (Option, Rc), 647 | } 648 | 649 | #[derive(Debug, Serialize)] 650 | pub enum Assumption { 651 | Single { pos: Position, prop: Box }, 652 | Collective { pos: Position, conds: Vec }, 653 | } 654 | impl Assumption { 655 | pub fn conds(&mut self) -> &mut [Proposition] { 656 | match self { 657 | Assumption::Single { prop, .. } => std::slice::from_mut(prop), 658 | Assumption::Collective { conds, .. } => conds, 659 | } 660 | } 661 | } 662 | 663 | #[derive(Debug, Serialize)] 664 | pub struct SetDecl { 665 | pub var: Box, 666 | pub value: Box, 667 | } 668 | 669 | #[derive(Debug, Serialize)] 670 | pub struct TakeDecl { 671 | #[serde(skip_serializing_if = "Option::is_none")] 672 | pub var: Option>, 673 | pub term: Box, 674 | } 675 | 676 | #[derive(Debug, Serialize)] 677 | pub struct IterStep { 678 | pub pos: Position, 679 | pub rhs: Term, 680 | pub just: Justification, 681 | } 682 | 683 | #[derive(Debug, Serialize)] 684 | pub enum Statement { 685 | Proposition { 686 | prop: Box, 687 | just: Box, 688 | }, 689 | IterEquality { 690 | /// lhs = first rhs 691 | prop: Box, 692 | just: Box, 693 | /// subsequent rhs's 694 | steps: Vec, 695 | }, 696 | Now { 697 | #[serde(skip_serializing_if = "Option::is_none")] 698 | label: Option>, 699 | #[serde(skip_serializing_if = "Vec::is_empty")] 700 | items: Vec, 701 | end: Position, 702 | }, 703 | } 704 | 705 | #[derive(Debug, Serialize)] 706 | pub enum DefinitionKind { 707 | Func { 708 | pat: Box, 709 | #[serde(skip_serializing_if = "Option::is_none")] 710 | spec: Option>, 711 | #[serde(skip_serializing_if = "Option::is_none")] 712 | def: Option>, 713 | }, 714 | Pred { 715 | pat: Box, 716 | #[serde(skip_serializing_if = "Option::is_none")] 717 | def: Option>, 718 | }, 719 | Mode { 720 | pat: Box, 721 | kind: DefModeKind, 722 | }, 723 | Attr { 724 | pat: Box, 725 | #[serde(skip_serializing_if = "Option::is_none")] 726 | def: Option>, 727 | }, 728 | } 729 | 730 | impl DefinitionKind { 731 | pub fn pos(&self) -> Position { 732 | match self { 733 | DefinitionKind::Func { pat, .. } => match **pat { 734 | PatternFunc::Func { pos, .. } | PatternFunc::Bracket { pos, .. } => pos, 735 | }, 736 | DefinitionKind::Pred { pat, .. } => pat.pos, 737 | DefinitionKind::Mode { pat, .. } => pat.pos, 738 | DefinitionKind::Attr { pat, .. } => pat.pos, 739 | } 740 | } 741 | } 742 | 743 | #[derive(Debug, Serialize)] 744 | pub struct CorrCond { 745 | pub pos: Position, 746 | pub kind: CorrCondKind, 747 | pub just: Justification, 748 | } 749 | 750 | #[derive(Debug, Serialize)] 751 | pub struct Correctness { 752 | pub pos: Position, 753 | pub just: Justification, 754 | } 755 | 756 | #[derive(Debug, Serialize)] 757 | pub struct Property { 758 | pub pos: Position, 759 | pub kind: PropertyKind, 760 | pub just: Box, 761 | } 762 | 763 | #[derive(Debug, Serialize)] 764 | pub struct SchemeHead { 765 | #[serde(skip_serializing_if = "Option::is_none")] 766 | pub sym: Option>, 767 | #[serde(skip_serializing_if = "Option::is_none")] 768 | pub nr: Option, 769 | pub groups: Vec, 770 | pub concl: Formula, 771 | pub prems: Vec, 772 | } 773 | 774 | #[derive(Debug, Serialize)] 775 | pub struct SchemeBlock { 776 | #[serde(flatten)] 777 | pub head: SchemeHead, 778 | #[serde(skip_serializing_if = "Vec::is_empty")] 779 | pub items: Vec, 780 | pub end: Position, 781 | } 782 | 783 | #[derive(Debug, Serialize)] 784 | pub struct Reservation { 785 | pub vars: Vec, 786 | #[serde(skip_serializing_if = "Option::is_none")] 787 | pub tys: Option>, 788 | #[serde(skip_serializing_if = "Option::is_none")] 789 | pub fvars: Option>, 790 | pub ty: Box, 791 | } 792 | 793 | #[derive(Debug, Serialize)] 794 | pub struct Definition { 795 | pub kind: DefinitionKind, 796 | #[serde(flatten)] 797 | pub body: DefinitionBody, 798 | } 799 | #[derive(Debug, Serialize)] 800 | pub struct DefinitionBody { 801 | #[serde(skip_serializing_if = "std::ops::Not::not")] 802 | pub redef: bool, 803 | #[serde(skip_serializing_if = "Vec::is_empty")] 804 | pub conds: Vec, 805 | #[serde(skip_serializing_if = "Option::is_none")] 806 | pub corr: Option, 807 | #[serde(skip_serializing_if = "Vec::is_empty")] 808 | pub props: Vec, 809 | } 810 | 811 | #[derive(Debug, Serialize)] 812 | pub struct DefStruct { 813 | #[serde(skip_serializing_if = "Vec::is_empty")] 814 | pub parents: Vec, 815 | #[serde(skip_serializing_if = "Vec::is_empty")] 816 | pub fields: Vec, 817 | pub pat: PatternStruct, 818 | } 819 | 820 | #[derive(Debug, Serialize)] 821 | pub struct Cluster { 822 | pub kind: ClusterDeclKind, 823 | #[serde(skip_serializing_if = "Vec::is_empty")] 824 | pub conds: Vec, 825 | #[serde(skip_serializing_if = "Option::is_none")] 826 | pub corr: Option, 827 | } 828 | 829 | #[derive(Debug, Serialize)] 830 | pub struct IdentifyFunc { 831 | pub lhs: Box, 832 | pub rhs: Box, 833 | #[serde(skip_serializing_if = "Vec::is_empty")] 834 | pub eqs: Vec<(Variable, Variable)>, 835 | #[serde(skip_serializing_if = "Vec::is_empty")] 836 | pub conds: Vec, 837 | #[serde(skip_serializing_if = "Option::is_none")] 838 | pub corr: Option, 839 | } 840 | 841 | #[derive(Debug, Serialize)] 842 | pub struct Reduction { 843 | pub from: Box, 844 | pub to: Box, 845 | #[serde(skip_serializing_if = "Vec::is_empty")] 846 | pub conds: Vec, 847 | #[serde(skip_serializing_if = "Option::is_none")] 848 | pub corr: Option, 849 | } 850 | 851 | #[derive(Debug, Serialize)] 852 | pub struct CaseBlock { 853 | pub hyp: Box, 854 | #[serde(skip_serializing_if = "Vec::is_empty")] 855 | pub items: Vec, 856 | pub end: Position, 857 | } 858 | 859 | #[derive(Debug, Serialize)] 860 | pub enum Pragma { 861 | /// $CD, $CT, $CS 862 | Canceled(CancelKind, u32), 863 | /// $N 864 | ThmDesc(String), 865 | /// $INSERT 866 | Insert(String), 867 | /// $V-, $V+ 868 | SetVerify(bool), 869 | Other(String), 870 | } 871 | 872 | impl FromStr for Pragma { 873 | type Err = std::convert::Infallible; 874 | fn from_str(spelling: &str) -> Result { 875 | let parse_num = |s: &str| { 876 | if s.is_empty() { 877 | 1 878 | } else { 879 | s.trim().parse::().unwrap() 880 | } 881 | }; 882 | Ok(if let Some(s) = spelling.strip_prefix("$CD") { 883 | Pragma::Canceled(CancelKind::Def, parse_num(s)) 884 | } else if let Some(s) = spelling.strip_prefix("$CT") { 885 | Pragma::Canceled(CancelKind::Thm, parse_num(s)) 886 | } else if let Some(s) = spelling.strip_prefix("$CS") { 887 | Pragma::Canceled(CancelKind::Sch, parse_num(s)) 888 | } else if let Some(s) = spelling.strip_prefix("$N") { 889 | Pragma::ThmDesc(s.trim_start().to_owned()) 890 | } else if let Some(s) = spelling.strip_prefix("$INSERT") { 891 | Pragma::Insert(s.trim_start().to_owned()) 892 | } else { 893 | match spelling { 894 | "$V-" => Pragma::SetVerify(false), 895 | "$V+" => Pragma::SetVerify(true), 896 | _ => Pragma::Other(spelling.to_owned()), 897 | } 898 | }) 899 | } 900 | } 901 | 902 | #[derive(Debug, Serialize)] 903 | pub enum ItemKind { 904 | Section, 905 | Block { 906 | kind: BlockKind, 907 | #[serde(skip_serializing_if = "Vec::is_empty")] 908 | items: Vec, 909 | end: Position, 910 | }, 911 | SchemeBlock(Box), 912 | Theorem { 913 | prop: Box, 914 | just: Box, 915 | }, 916 | Reservation(Vec), 917 | /// itConclusion 918 | Thus(Statement), 919 | /// itChoice 920 | Consider { 921 | vars: Vec, 922 | conds: Vec, 923 | just: Box, 924 | }, 925 | Reconsider { 926 | vars: Vec, 927 | ty: Box, 928 | just: Box, 929 | }, 930 | /// itPrivFuncDefinition 931 | DefFunc { 932 | var: Box, 933 | #[serde(skip_serializing_if = "Vec::is_empty")] 934 | tys: Vec, 935 | value: Box, 936 | }, 937 | /// itPrivPredDefinition 938 | DefPred { 939 | var: Box, 940 | #[serde(skip_serializing_if = "Vec::is_empty")] 941 | tys: Vec, 942 | value: Box, 943 | }, 944 | /// itConstantDefinition 945 | Set(Vec), 946 | /// itGeneralization, itLociDeclaration 947 | Let { 948 | vars: Vec, 949 | #[serde(skip_serializing_if = "Vec::is_empty")] 950 | conds: Vec, 951 | }, 952 | /// itExistentialAssumption 953 | Given { 954 | vars: Vec, 955 | conds: Vec, 956 | }, 957 | /// itExemplification 958 | Take(Vec), 959 | PerCases { 960 | just: Box, 961 | kind: CaseKind, 962 | #[serde(skip_serializing_if = "Vec::is_empty")] 963 | blocks: Vec, 964 | }, 965 | Assume(Assumption), 966 | Unfold(Vec), 967 | Definition(Box), 968 | DefStruct(Box), 969 | PatternRedef(PatternRedef), 970 | Cluster(Box), 971 | IdentifyFunc(Box), 972 | Reduction(Box), 973 | SethoodRegistration { 974 | ty: Box, 975 | just: Box, 976 | }, 977 | Pragma(Pragma), 978 | /// parser internal use only 979 | SchemeHead(Box), 980 | /// parser internal use only 981 | CaseHead(CaseKind, Assumption), 982 | /// parser internal use only 983 | PerCasesHead(Box), 984 | #[serde(untagged)] 985 | Statement(Statement), 986 | } 987 | 988 | impl Default for ItemKind { 989 | fn default() -> Self { Self::Section } 990 | } 991 | -------------------------------------------------------------------------------- /src/bignum.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::BigInt; 2 | use num_traits::sign::Signed; 3 | use std::borrow::Cow; 4 | use std::cmp::Ordering; 5 | use std::fmt::{Debug, Display}; 6 | 7 | #[derive(PartialEq, Eq, Clone, Debug)] 8 | enum Integer { 9 | Small(i32), 10 | Large(Box), 11 | } 12 | impl From for Integer { 13 | fn from(v: i32) -> Self { Self::Small(v) } 14 | } 15 | impl From for Integer { 16 | fn from(v: BigInt) -> Self { Self::large(v) } 17 | } 18 | 19 | impl Display for Integer { 20 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 21 | match self { 22 | Integer::Small(n) => Display::fmt(n, f), 23 | Integer::Large(n) => Display::fmt(n, f), 24 | } 25 | } 26 | } 27 | 28 | impl Integer { 29 | const ZERO: Self = Self::Small(0); 30 | const ONE: Self = Self::Small(1); 31 | 32 | fn large(i: BigInt) -> Self { 33 | match i.try_into() { 34 | Ok(n) => Self::Small(n), 35 | Err(n) => Self::Large(Box::new(n.into_original())), 36 | } 37 | } 38 | 39 | fn as_large(&self) -> Cow<'_, BigInt> { 40 | match self { 41 | &Integer::Small(n) => Cow::Owned(n.into()), 42 | Integer::Large(n) => Cow::Borrowed(n), 43 | } 44 | } 45 | 46 | fn gcd(mut self, mut rhs: Self) -> Self { 47 | if self >= rhs { 48 | std::mem::swap(&mut self, &mut rhs) 49 | } 50 | loop { 51 | if self == Self::ZERO { 52 | return rhs 53 | } 54 | (self, rhs) = (rhs % self.clone(), self) 55 | } 56 | } 57 | 58 | fn abs(self) -> Self { 59 | if let Integer::Small(a) = self { 60 | if let Some(out) = a.checked_abs() { 61 | return out.into() 62 | } 63 | } 64 | self.as_large().abs().into() 65 | } 66 | } 67 | 68 | impl std::ops::Add for Integer { 69 | type Output = Self; 70 | fn add(self, rhs: Self) -> Self { 71 | if let (&Integer::Small(a), &Integer::Small(b)) = (&self, &rhs) { 72 | if let Some(out) = a.checked_add(b) { 73 | return out.into() 74 | } 75 | } 76 | self.as_large().into_owned().add(&*rhs.as_large()).into() 77 | } 78 | } 79 | 80 | impl std::ops::Neg for Integer { 81 | type Output = Self; 82 | fn neg(self) -> Self { 83 | if let Integer::Small(a) = self { 84 | if let Some(out) = a.checked_neg() { 85 | return out.into() 86 | } 87 | } 88 | self.as_large().into_owned().neg().into() 89 | } 90 | } 91 | 92 | impl std::ops::Sub for Integer { 93 | type Output = Self; 94 | fn sub(self, rhs: Self) -> Self { self + (-rhs) } 95 | } 96 | 97 | impl std::ops::Mul for Integer { 98 | type Output = Self; 99 | fn mul(self, rhs: Self) -> Self { 100 | if let (&Integer::Small(a), &Integer::Small(b)) = (&self, &rhs) { 101 | if let Some(out) = a.checked_mul(b) { 102 | return out.into() 103 | } 104 | } 105 | self.as_large().into_owned().mul(&*rhs.as_large()).into() 106 | } 107 | } 108 | 109 | // panics on division by zero 110 | impl std::ops::Div for Integer { 111 | type Output = Self; 112 | fn div(self, rhs: Self) -> Self::Output { 113 | if let (&Integer::Small(a), &Integer::Small(b)) = (&self, &rhs) { 114 | if let Some(out) = a.checked_div(b) { 115 | return out.into() 116 | } 117 | } 118 | self.as_large().into_owned().div(&*rhs.as_large()).into() 119 | } 120 | } 121 | 122 | // panics on division by zero 123 | impl std::ops::Rem for Integer { 124 | type Output = Self; 125 | fn rem(self, rhs: Self) -> Self { 126 | if let (&Integer::Small(a), &Integer::Small(b)) = (&self, &rhs) { 127 | if let Some(out) = a.checked_rem(b) { 128 | return out.into() 129 | } 130 | } 131 | self.as_large().into_owned().rem(&*rhs.as_large()).into() 132 | } 133 | } 134 | 135 | impl Ord for Integer { 136 | fn cmp(&self, other: &Self) -> Ordering { 137 | if let (Integer::Small(a), Integer::Small(b)) = (self, other) { 138 | a.cmp(b) 139 | } else { 140 | self.as_large().cmp(&other.as_large()) 141 | } 142 | } 143 | } 144 | 145 | impl PartialOrd for Integer { 146 | fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } 147 | } 148 | 149 | #[derive(PartialEq, Eq, Clone)] 150 | pub struct Rational { 151 | num: Integer, 152 | den: Integer, // Invariant: positive 153 | } 154 | 155 | impl Default for Rational { 156 | fn default() -> Self { Self::ZERO } 157 | } 158 | 159 | impl Display for Rational { 160 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 161 | if self.den == Integer::ONE { 162 | write!(f, "{}", self.num) 163 | } else { 164 | write!(f, "{}/{}", self.num, self.den) 165 | } 166 | } 167 | } 168 | 169 | impl Rational { 170 | pub const fn int(num: i32) -> Self { Self { num: Integer::Small(num), den: Integer::ONE } } 171 | pub const ZERO: Self = Self::int(0); 172 | pub const ONE: Self = Self::int(1); 173 | pub const NEG_ONE: Self = Self::int(-1); 174 | 175 | fn lex(&self, other: &Self) -> Ordering { 176 | self.num.cmp(&other.num).then_with(|| self.den.cmp(&other.den)) 177 | } 178 | } 179 | 180 | impl std::ops::Add for Rational { 181 | type Output = Self; 182 | fn add(self, rhs: Self) -> Self::Output { 183 | let g = self.den.clone().gcd(rhs.den.clone()); 184 | if g == Integer::ONE { 185 | Self { num: self.num * rhs.den.clone() + rhs.num * self.den.clone(), den: self.den * rhs.den } 186 | } else { 187 | let den = (self.den.clone() / g.clone()) * rhs.den.clone(); 188 | let num = self.num * (rhs.den / g.clone()) + rhs.num * (self.den / g.clone()); 189 | let g1 = (num.clone().abs() % g.clone()).gcd(g); 190 | if g1 == Integer::ONE { 191 | Self { num, den } 192 | } else { 193 | Self { num: num / g1.clone(), den: den / g1 } 194 | } 195 | } 196 | } 197 | } 198 | 199 | impl std::ops::Neg for Rational { 200 | type Output = Self; 201 | fn neg(self) -> Self::Output { Self { num: -self.num, den: self.den } } 202 | } 203 | 204 | impl std::ops::Sub for Rational { 205 | type Output = Self; 206 | fn sub(self, rhs: Self) -> Self::Output { self + (-rhs) } 207 | } 208 | 209 | impl std::ops::Mul for Rational { 210 | type Output = Self; 211 | fn mul(self, rhs: Self) -> Self::Output { 212 | let g1 = self.num.clone().abs().gcd(rhs.den.clone()); 213 | let g2 = rhs.num.clone().abs().gcd(self.den.clone()); 214 | Self { 215 | num: (self.num / g1.clone()) * (rhs.num / g2.clone()), 216 | den: (self.den / g2) * (rhs.den / g1), 217 | } 218 | } 219 | } 220 | 221 | impl Rational { 222 | pub fn inv(self) -> Self { 223 | match self.num.cmp(&Integer::ZERO) { 224 | Ordering::Less => Self { num: -self.den, den: -self.num }, 225 | Ordering::Equal => panic!("division by zero"), 226 | Ordering::Greater => Self { num: self.den, den: self.num }, 227 | } 228 | } 229 | } 230 | 231 | impl std::ops::Div for Rational { 232 | type Output = Self; 233 | #[allow(clippy::suspicious_arithmetic_impl)] 234 | fn div(self, rhs: Self) -> Self::Output { self * rhs.inv() } 235 | } 236 | 237 | impl Ord for Rational { 238 | fn cmp(&self, other: &Self) -> Ordering { 239 | #[allow(clippy::comparison_chain)] 240 | if self < other { 241 | Ordering::Less 242 | } else if other < self { 243 | Ordering::Greater 244 | } else { 245 | Ordering::Equal 246 | } 247 | } 248 | } 249 | impl PartialOrd for Rational { 250 | fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } 251 | fn lt(&self, other: &Self) -> bool { 252 | if self.num < Integer::ZERO && other.num >= Integer::ZERO { 253 | true 254 | } else if self.num == Integer::ZERO { 255 | Integer::ZERO < other.num 256 | } else if Integer::ZERO < self.num && other.num <= Integer::ZERO { 257 | false 258 | } else { 259 | self.num.clone() * other.den.clone() < other.num.clone() * self.den.clone() 260 | } 261 | } 262 | fn le(&self, other: &Self) -> bool { !other.lt(self) } 263 | fn gt(&self, other: &Self) -> bool { other < self } 264 | fn ge(&self, other: &Self) -> bool { !self.lt(other) } 265 | } 266 | 267 | #[derive(PartialEq, Eq, Clone, Default)] 268 | pub struct Complex { 269 | pub re: Rational, 270 | pub im: Rational, 271 | } 272 | impl Ord for Complex { 273 | fn cmp(&self, other: &Self) -> Ordering { 274 | self.re.lex(&other.re).then_with(|| self.im.lex(&other.im)) 275 | } 276 | } 277 | impl PartialOrd for Complex { 278 | fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } 279 | } 280 | 281 | impl Display for Complex { 282 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 283 | if self.im == Rational::ZERO { 284 | self.re.fmt(f) 285 | } else if self.re == Rational::ZERO { 286 | write!(f, "{}i", self.im) 287 | } else { 288 | write!(f, "{}+{}i", self.re, self.im) 289 | } 290 | } 291 | } 292 | 293 | impl Debug for Complex { 294 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{self}") } 295 | } 296 | 297 | impl Complex { 298 | pub const fn real(re: Rational) -> Self { Self { re, im: Rational::ZERO } } 299 | // TODO: this should be infallible 300 | pub fn int(num: u64) -> Self { Self::real(Rational::int(num.try_into().unwrap())) } 301 | pub const ZERO: Self = Self::real(Rational::ZERO); 302 | pub const ONE: Self = Self::real(Rational::ONE); 303 | pub const NEG_ONE: Self = Self::real(Rational::NEG_ONE); 304 | pub const I: Self = Self { re: Rational::ZERO, im: Rational::ONE }; 305 | 306 | pub fn pow(mut self, n: u64) -> Self { 307 | match n { 308 | 0 => Complex::ONE, 309 | 1 => self, 310 | _ => { 311 | let a = self.clone(); 312 | for i in (0..n.ilog2()).rev() { 313 | self *= self.clone(); 314 | if (n >> i) & 1 != 0 { 315 | self *= a.clone(); 316 | } 317 | } 318 | self 319 | } 320 | } 321 | } 322 | } 323 | impl From for Complex { 324 | fn from(value: Rational) -> Self { Self::real(value) } 325 | } 326 | impl From for Complex { 327 | fn from(value: u32) -> Self { Self::int(value.into()) } 328 | } 329 | 330 | impl std::ops::Add for Complex { 331 | type Output = Self; 332 | fn add(self, rhs: Self) -> Self::Output { Self { re: self.re + rhs.re, im: self.im + rhs.im } } 333 | } 334 | impl std::ops::AddAssign for Complex { 335 | fn add_assign(&mut self, rhs: Self) { *self = std::mem::take(self) + rhs } 336 | } 337 | 338 | impl std::ops::Neg for Complex { 339 | type Output = Self; 340 | fn neg(self) -> Self::Output { Self { re: -self.re, im: -self.im } } 341 | } 342 | 343 | impl std::ops::Sub for Complex { 344 | type Output = Self; 345 | fn sub(self, rhs: Self) -> Self::Output { Self { re: self.re - rhs.re, im: self.im - rhs.im } } 346 | } 347 | 348 | impl std::ops::Mul for Complex { 349 | type Output = Self; 350 | fn mul(self, rhs: Self) -> Self::Output { 351 | Self { 352 | re: self.re.clone() * rhs.re.clone() - self.im.clone() * rhs.im.clone(), 353 | im: self.re * rhs.im + rhs.re * self.im, 354 | } 355 | } 356 | } 357 | impl std::ops::MulAssign for Complex { 358 | fn mul_assign(&mut self, rhs: Self) { *self = std::mem::take(self) * rhs } 359 | } 360 | 361 | impl Complex { 362 | pub fn inv(self) -> Self { 363 | let d = (self.re.clone() * self.re.clone() + self.im.clone() * self.im.clone()).inv(); 364 | Self { re: self.re * d.clone(), im: -self.im * d } 365 | } 366 | } 367 | 368 | impl std::ops::Div for Complex { 369 | type Output = Self; 370 | #[allow(clippy::suspicious_arithmetic_impl)] 371 | fn div(self, rhs: Self) -> Self::Output { self * rhs.inv() } 372 | } 373 | -------------------------------------------------------------------------------- /src/cache.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::{MaybeMut, ParseError, PathResult}; 2 | use crate::types::*; 3 | use crate::MizPath; 4 | use once_cell::sync::OnceCell; 5 | use std::collections::HashMap; 6 | 7 | struct CacheMap { 8 | articles: HashMap, 9 | reqs: HashMap>, 10 | } 11 | 12 | static CACHE: OnceCell = OnceCell::new(); 13 | 14 | #[derive(Default)] 15 | pub struct Cache { 16 | pub wait: bool, 17 | pub dfr: OnceCell<(Vocabularies, Vec)>, 18 | pub dno: OnceCell, 19 | pub dco: OnceCell, 20 | pub dcl: OnceCell, 21 | pub def: OnceCell<(Vec
, Vec)>, 22 | pub dpr: OnceCell<(Vec
, Vec)>, 23 | pub did: OnceCell<(Vec
, Vec)>, 24 | pub drd: OnceCell<(Vec
, Vec)>, 25 | pub the: OnceCell, 26 | pub sch: OnceCell, 27 | } 28 | 29 | #[allow(clippy::unwrap_used)] 30 | pub fn init_cache<'a>(articles: impl Iterator) { 31 | let articles = ["hidden", "tarski_0", "tarski_a"] 32 | .into_iter() 33 | .map(|x| (x, false)) 34 | .chain(articles) 35 | .map(|(s, wait)| { 36 | (Article::from_lower(s.as_bytes()).unwrap(), Cache { wait, ..Cache::default() }) 37 | }) 38 | .collect(); 39 | let reqs = ["arithm", "boole", "hidden", "numerals", "real", "subset"] 40 | .into_iter() 41 | .map(|s| (Article::from_lower(s.as_bytes()).unwrap(), Default::default())) 42 | .collect(); 43 | CACHE.set(CacheMap { articles, reqs }).ok().unwrap() 44 | } 45 | 46 | impl MizPath { 47 | pub fn with_cache(&self, get: impl FnOnce(&Cache) -> &OnceCell, value: T) { 48 | if let Some(c) = CACHE.get().and_then(|map| map.articles.get(&self.art)) { 49 | get(c).get_or_init(|| value); 50 | } 51 | } 52 | 53 | fn get_cache( 54 | &self, no: bool, args: &mut A, get: impl FnOnce(&Cache) -> &OnceCell, 55 | read: impl FnOnce(&mut A, bool) -> Result, take: impl FnOnce(&mut A, R) -> T, 56 | copy: impl FnOnce(&mut A, &T) -> Result, 57 | ) -> Result { 58 | if !no { 59 | if let Some(c) = CACHE.get().and_then(|map| map.articles.get(&self.art)) { 60 | let t = if c.wait { 61 | get(c).wait() 62 | } else { 63 | get(c).get_or_try_init(|| read(args, true).map(|r| take(args, r)))? 64 | }; 65 | return copy(args, t) 66 | } 67 | } 68 | read(args, false) 69 | } 70 | 71 | fn get_cache_basic( 72 | &self, no: bool, args: &mut A, get: impl FnOnce(&Cache) -> &OnceCell, 73 | read: impl FnOnce(&mut A) -> Result, result: impl FnOnce(&A) -> Result, 74 | ) -> Result { 75 | self.get_cache( 76 | no, 77 | args, 78 | get, 79 | |a, _| read(a), 80 | |a, _| std::mem::take(a), 81 | |a, t| { 82 | a.clone_from(t); 83 | result(t) 84 | }, 85 | ) 86 | } 87 | 88 | pub fn read_dfr( 89 | &self, new_prel: bool, vocs: &mut Vocabularies, formats: &mut IdxVec, 90 | ) -> PathResult<()> { 91 | self.get_cache( 92 | new_prel, 93 | &mut (vocs, formats), 94 | |c| &c.dfr, 95 | |(vocs, formats), _| self.read_dfr_uncached(new_prel, vocs, formats), 96 | |(vocs, formats), _| (std::mem::take(vocs), std::mem::take(&mut formats.0)), 97 | |(vocs, formats), (vocs2, formats2)| { 98 | vocs.clone_from(vocs2); 99 | formats.0.clone_from(formats2); 100 | if formats2.is_empty() { 101 | Err((self.to_path(false, new_prel, "dfr"), ParseError::MissingFile)) 102 | } else { 103 | Ok(()) 104 | } 105 | }, 106 | ) 107 | } 108 | 109 | pub fn read_dno(&self, new_prel: bool, dno: &mut DepNotation) -> PathResult<()> { 110 | self.get_cache_basic( 111 | new_prel, 112 | dno, 113 | |c| &c.dno, 114 | |dno| self.read_dno_uncached(new_prel, dno), 115 | |dno| { 116 | if dno.pats.is_empty() { 117 | Err((self.to_path(false, new_prel, "dno"), ParseError::MissingFile)) 118 | } else { 119 | Ok(()) 120 | } 121 | }, 122 | ) 123 | } 124 | 125 | pub fn read_dco( 126 | &self, new_prel: bool, dco: &mut DepConstructors, read_constrs: bool, 127 | ) -> PathResult<()> { 128 | self.get_cache( 129 | new_prel, 130 | dco, 131 | |c| &c.dco, 132 | |dco, cache| self.read_dco_uncached(new_prel, dco, read_constrs || cache), 133 | |dco, _| std::mem::take(dco), 134 | |dco, dco2| { 135 | dco.sig.clone_from(&dco2.sig); 136 | dco.counts = dco2.counts; 137 | if read_constrs { 138 | dco.constrs.clone_from(&dco2.constrs); 139 | } 140 | if dco2.constrs.as_ref().is_empty() { 141 | Err((self.to_path(false, new_prel, "dco"), ParseError::MissingFile)) 142 | } else { 143 | Ok(()) 144 | } 145 | }, 146 | ) 147 | } 148 | 149 | pub fn read_dre(&self, dre: &mut DepRequirements) -> PathResult<()> { 150 | if let Some(c) = CACHE.get().and_then(|map| map.reqs.get(&self.art)) { 151 | dre.clone_from(c.get_or_try_init(|| -> PathResult { 152 | let mut dre = Default::default(); 153 | self.read_dre_uncached(&mut dre)?; 154 | Ok(dre) 155 | })?); 156 | Ok(()) 157 | } else { 158 | self.read_dre_uncached(dre) 159 | } 160 | } 161 | 162 | pub fn read_dcl(&self, new_prel: bool, dcl: &mut DepClusters) -> PathResult<()> { 163 | self.get_cache_basic( 164 | new_prel, 165 | dcl, 166 | |c| &c.dcl, 167 | |dcl| self.read_dcl_uncached(new_prel, dcl), 168 | |dcl| { 169 | if dcl.cl.as_ref().is_empty() { 170 | Err((self.to_path(false, new_prel, "dcl"), ParseError::MissingFile)) 171 | } else { 172 | Ok(()) 173 | } 174 | }, 175 | ) 176 | } 177 | 178 | pub fn read_def( 179 | &self, new_prel: bool, sig: &mut Vec
, defs: &mut Vec, 180 | ) -> PathResult<()> { 181 | self.get_cache( 182 | new_prel, 183 | &mut (sig, defs), 184 | |c| &c.def, 185 | |(sig, defs), _| self.read_definitions(MaybeMut::None, new_prel, "def", Some(sig), defs), 186 | |(sig, defs), _| (std::mem::take(sig), std::mem::take(defs)), 187 | |(sig, defs), (sig2, defs2)| { 188 | sig.clone_from(sig2); 189 | defs.clone_from(defs2); 190 | if defs2.is_empty() { 191 | Err((self.to_path(false, new_prel, "def"), ParseError::MissingFile)) 192 | } else { 193 | Ok(()) 194 | } 195 | }, 196 | ) 197 | } 198 | 199 | pub fn read_dpr( 200 | &self, new_prel: bool, sig: &mut Vec
, dpr: &mut Vec, 201 | ) -> PathResult<()> { 202 | self.get_cache( 203 | new_prel, 204 | &mut (sig, dpr), 205 | |c| &c.dpr, 206 | |(sig, dpr), _| self.read_properties(MaybeMut::None, new_prel, "dpr", Some(sig), dpr), 207 | |(sig, dpr), _| (std::mem::take(sig), std::mem::take(dpr)), 208 | |(sig, dpr), (sig2, dpr2)| { 209 | sig.clone_from(sig2); 210 | dpr.clone_from(dpr2); 211 | if dpr2.is_empty() { 212 | Err((self.to_path(false, new_prel, "dpr"), ParseError::MissingFile)) 213 | } else { 214 | Ok(()) 215 | } 216 | }, 217 | ) 218 | } 219 | 220 | pub fn read_did( 221 | &self, new_prel: bool, sig: &mut Vec
, did: &mut Vec, 222 | ) -> PathResult<()> { 223 | self.get_cache( 224 | new_prel, 225 | &mut (sig, did), 226 | |c| &c.did, 227 | |(sig, did), _| self.read_identify_regs(MaybeMut::None, new_prel, "did", Some(sig), did), 228 | |(sig, did), _| (std::mem::take(sig), std::mem::take(did)), 229 | |(sig, did), (sig2, did2)| { 230 | sig.clone_from(sig2); 231 | did.clone_from(did2); 232 | if did2.is_empty() { 233 | Err((self.to_path(false, new_prel, "did"), ParseError::MissingFile)) 234 | } else { 235 | Ok(()) 236 | } 237 | }, 238 | ) 239 | } 240 | 241 | pub fn read_drd( 242 | &self, new_prel: bool, sig: &mut Vec
, drd: &mut Vec, 243 | ) -> PathResult<()> { 244 | self.get_cache( 245 | new_prel, 246 | &mut (sig, drd), 247 | |c| &c.drd, 248 | |(sig, drd), _| self.read_reduction_regs(MaybeMut::None, new_prel, "drd", Some(sig), drd), 249 | |(sig, drd), _| (std::mem::take(sig), std::mem::take(drd)), 250 | |(sig, drd), (sig2, drd2)| { 251 | sig.clone_from(sig2); 252 | drd.clone_from(drd2); 253 | if drd2.is_empty() { 254 | Err((self.to_path(false, new_prel, "drd"), ParseError::MissingFile)) 255 | } else { 256 | Ok(()) 257 | } 258 | }, 259 | ) 260 | } 261 | 262 | pub fn read_the(&self, new_prel: bool, the: &mut DepTheorems) -> PathResult<()> { 263 | self.get_cache_basic( 264 | new_prel, 265 | the, 266 | |c| &c.the, 267 | |the| self.read_the_uncached(new_prel, the), 268 | |the| { 269 | if the.thm.is_empty() { 270 | Err((self.to_path(false, new_prel, "the"), ParseError::MissingFile)) 271 | } else { 272 | Ok(()) 273 | } 274 | }, 275 | ) 276 | } 277 | 278 | pub fn read_sch(&self, new_prel: bool, sch: &mut DepSchemes) -> PathResult<()> { 279 | self.get_cache_basic( 280 | new_prel, 281 | sch, 282 | |c| &c.sch, 283 | |sch| self.read_sch_uncached(new_prel, sch), 284 | |sch| { 285 | if sch.sch.is_empty() { 286 | Err((self.to_path(false, new_prel, "sch"), ParseError::MissingFile)) 287 | } else { 288 | Ok(()) 289 | } 290 | }, 291 | ) 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /src/equate/polynomial.rs: -------------------------------------------------------------------------------- 1 | use crate::bignum::{Complex, Rational}; 2 | use crate::checker::{OrUnsat, Unsat}; 3 | use crate::mk_id; 4 | use crate::types::*; 5 | use itertools::{EitherOrBoth, Itertools}; 6 | use std::cmp::Ordering; 7 | use std::collections::{BTreeMap, BTreeSet}; 8 | 9 | #[derive(Clone)] 10 | pub struct Monomial { 11 | /// invariant: not zero inside polynomial 12 | coeff: Complex, 13 | /// invariant: map does not contain zero powers 14 | powers: BTreeMap, 15 | } 16 | 17 | impl std::fmt::Debug for Monomial { 18 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 19 | let mut strs = vec![]; 20 | if self.coeff == Complex::NEG_ONE { 21 | write!(f, "-")? 22 | } else if self.coeff != Complex::ONE { 23 | if self.coeff.im != Rational::ZERO { 24 | strs.push(format!("({})", self.coeff)) 25 | } else { 26 | strs.push(format!("{}", self.coeff)) 27 | } 28 | } 29 | for (&et, &k) in &self.powers { 30 | if k == 1 { 31 | strs.push(format!("x{et:?}")) 32 | } else { 33 | strs.push(format!("x{et:?}^{k}")) 34 | } 35 | } 36 | if strs.is_empty() { 37 | write!(f, "1") 38 | } else { 39 | write!(f, "{}", strs.iter().format("*")) 40 | } 41 | } 42 | } 43 | 44 | // This ignores the coefficients 45 | impl Ord for Monomial { 46 | fn cmp(&self, other: &Self) -> Ordering { 47 | self.degree().cmp(&other.degree()).then_with(|| { 48 | self.powers.len().cmp(&other.powers.len()).then_with(|| self.powers.cmp(&other.powers)) 49 | }) 50 | } 51 | } 52 | impl PartialOrd for Monomial { 53 | fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } 54 | } 55 | impl PartialEq for Monomial { 56 | fn eq(&self, other: &Self) -> bool { self.cmp(other) == Ordering::Equal } 57 | } 58 | impl Eq for Monomial {} 59 | 60 | impl Monomial { 61 | pub const fn cnst(coeff: Complex) -> Self { Self { coeff, powers: BTreeMap::new() } } 62 | pub fn coeff_atom(coeff: Complex, var: I) -> Self { 63 | Self { coeff, powers: std::iter::once((var, 1)).collect() } 64 | } 65 | pub fn atom(var: I) -> Self { Self::coeff_atom(Complex::ONE, var) } 66 | pub fn degree(&self) -> u32 { self.powers.iter().map(|p| p.1).sum() } 67 | 68 | fn mul(&self, other: &Self) -> Self { 69 | let coeff = self.coeff.clone() * other.coeff.clone(); 70 | let mut powers = BTreeMap::new(); 71 | for item in self.powers.iter().merge_join_by(&other.powers, |a, b| a.0.cmp(b.0)) { 72 | match item { 73 | EitherOrBoth::Left((&et, &n)) | EitherOrBoth::Right((&et, &n)) => powers.insert(et, n), 74 | EitherOrBoth::Both((&et, &n1), (_, &n2)) => powers.insert(et, n1.checked_add(n2).unwrap()), 75 | }; 76 | } 77 | Monomial { coeff, powers } 78 | } 79 | 80 | fn lex_powers(a: &BTreeMap, b: &BTreeMap) -> Ordering { 81 | a.iter().map(|p| (p.0, !*p.1)).cmp(b.iter().map(|p| (p.0, !*p.1))) 82 | } 83 | 84 | fn lex(&self, other: &Self) -> Ordering { Self::lex_powers(&self.powers, &other.powers) } 85 | 86 | pub fn contains(&self, v: I) -> bool { self.powers.contains_key(&v) } 87 | 88 | pub fn is_var(&self) -> Option { 89 | if self.powers.len() != 1 || self.coeff != Complex::ONE { 90 | return None 91 | } 92 | match self.powers.first_key_value() { 93 | Some((&et, 1)) => Some(et), 94 | _ => None, 95 | } 96 | } 97 | 98 | pub fn is_const(&self) -> Option { 99 | if self.powers.is_empty() { 100 | Some(self.coeff.clone()) 101 | } else { 102 | None 103 | } 104 | } 105 | 106 | pub fn pow(&mut self, n: u32) { 107 | match n { 108 | 0 => *self = Monomial::cnst(Complex::ONE), 109 | 1 => {} 110 | _ => { 111 | self.coeff = std::mem::take(&mut self.coeff).pow(n.into()); 112 | for i in self.powers.values_mut() { 113 | *i = i.checked_mul(n).unwrap(); 114 | } 115 | } 116 | } 117 | } 118 | } 119 | 120 | #[derive(Clone)] 121 | pub struct Polynomial( 122 | /// sorted by Monomial::lex 123 | Vec>, 124 | ); 125 | 126 | impl std::fmt::Debug for Polynomial { 127 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 128 | if self.0.is_empty() { 129 | write!(f, "poly 0") 130 | } else { 131 | write!(f, "poly {:?}", self.0.iter().format(" + ")) 132 | } 133 | } 134 | } 135 | 136 | impl Ord for Polynomial { 137 | fn cmp(&self, other: &Self) -> Ordering { self.fcmp(other, |a, b| a.cmp(b)) } 138 | } 139 | impl PartialOrd for Polynomial { 140 | fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } 141 | } 142 | impl PartialEq for Polynomial { 143 | fn eq(&self, other: &Self) -> bool { self.cmp(other) == Ordering::Equal } 144 | } 145 | impl Eq for Polynomial {} 146 | 147 | impl std::ops::Add for Polynomial { 148 | type Output = Self; 149 | fn add(self, other: Self) -> Self { 150 | let mut out = Polynomial::ZERO; 151 | for item in self.0.into_iter().merge_join_by(other.0, Monomial::lex) { 152 | match item { 153 | EitherOrBoth::Left(mon) | EitherOrBoth::Right(mon) => out.0.push(mon), 154 | EitherOrBoth::Both(mut m1, m2) => { 155 | m1.coeff += m2.coeff; 156 | if m1.coeff != Complex::ZERO { 157 | out.0.push(m1) 158 | } 159 | } 160 | } 161 | } 162 | out 163 | } 164 | } 165 | 166 | impl std::ops::Sub for Polynomial { 167 | type Output = Self; 168 | fn sub(self, other: Self) -> Self { self + other * &Complex::NEG_ONE } 169 | } 170 | 171 | impl std::ops::Mul<&Complex> for Polynomial { 172 | type Output = Self; 173 | fn mul(mut self, other: &Complex) -> Self { 174 | if *other == Complex::ZERO { 175 | return Polynomial::ZERO 176 | } 177 | if *other == Complex::ONE { 178 | return self 179 | } 180 | for mon in &mut self.0 { 181 | mon.coeff = std::mem::take(&mut mon.coeff) * other.clone() 182 | } 183 | self 184 | } 185 | } 186 | 187 | impl std::ops::Mul for &Polynomial { 188 | type Output = Polynomial; 189 | fn mul(self, other: Self) -> Polynomial { 190 | if self.is_zero() || other.is_zero() { 191 | return Polynomial::ZERO 192 | } 193 | let mut out = Polynomial::ZERO; 194 | for i in &self.0 { 195 | for j in &other.0 { 196 | let mon = i.mul(j); 197 | out.0.push(mon) 198 | } 199 | } 200 | out.0.sort_unstable_by(Monomial::lex); 201 | out.dedup(); 202 | out 203 | } 204 | } 205 | 206 | impl std::ops::Mul<&Monomial> for Polynomial { 207 | type Output = Self; 208 | fn mul(mut self, other: &Monomial) -> Self { 209 | if other.coeff == Complex::ZERO { 210 | return Polynomial::ZERO 211 | } 212 | if other.is_const() == Some(Complex::ONE) { 213 | return self 214 | } 215 | for mon in &mut self.0 { 216 | mon.coeff = std::mem::take(&mut mon.coeff) * other.coeff.clone(); 217 | for (&et, &i) in &other.powers { 218 | let v = mon.powers.entry(et).or_default(); 219 | *v = v.checked_add(i).unwrap(); 220 | } 221 | } 222 | self.0.sort_unstable_by(Monomial::lex); 223 | self 224 | } 225 | } 226 | 227 | impl Polynomial { 228 | pub const ZERO: Self = Self(Vec::new()); 229 | 230 | pub fn single(mon: Monomial) -> Self { 231 | if mon.coeff == Complex::ZERO { 232 | Self(Vec::new()) 233 | } else { 234 | Self(vec![mon]) 235 | } 236 | } 237 | } 238 | 239 | impl Polynomial { 240 | fn fcmp(&self, other: &Self, f: impl Fn(&Monomial, &Monomial) -> Ordering) -> Ordering { 241 | self.0.len().cmp(&other.0.len()).then_with(|| { 242 | for (a, b) in self.0.iter().zip(&other.0) { 243 | match f(a, b).then_with(|| a.coeff.cmp(&b.coeff)) { 244 | Ordering::Equal => {} 245 | non_eq => return non_eq, 246 | } 247 | } 248 | Ordering::Equal 249 | }) 250 | } 251 | 252 | pub fn is_zero(&self) -> bool { self.0.is_empty() } 253 | 254 | fn dedup(&mut self) { 255 | let mut it = std::mem::take(&mut self.0).into_iter(); 256 | if let Some(mut mon) = it.next() { 257 | for m2 in it { 258 | if mon == m2 { 259 | mon.coeff += m2.coeff; 260 | } else { 261 | if mon.coeff != Complex::ZERO { 262 | self.0.push(mon) 263 | } 264 | mon = m2; 265 | } 266 | } 267 | if mon.coeff != Complex::ZERO { 268 | self.0.push(mon) 269 | } 270 | } 271 | } 272 | 273 | pub fn is_var(&self) -> Option { 274 | match *self.0 { 275 | [ref mon] => mon.is_var(), 276 | _ => None, 277 | } 278 | } 279 | 280 | /// Returns `Some(v)` if the polynomial is a power of `v` 281 | pub fn is_var_power(&self) -> Option { 282 | match &*self.0 { 283 | [mon] if mon.powers.len() == 1 => Some(*mon.powers.first_key_value()?.0), 284 | _ => None, 285 | } 286 | } 287 | 288 | pub fn is_const(&self) -> Option { 289 | match *self.0 { 290 | [] => Some(Complex::ZERO), 291 | [ref mon] => mon.is_const(), 292 | _ => None, 293 | } 294 | } 295 | 296 | pub fn contains(&self, v: I) -> bool { self.0.iter().any(|mon| mon.contains(v)) } 297 | 298 | pub fn pow(&self, n: u32) -> Self { 299 | match n { 300 | 0 => Polynomial::single(Monomial::cnst(Complex::ONE)), 301 | 1 => self.clone(), 302 | _ => match *self.0 { 303 | [] => Polynomial::ZERO, 304 | [ref mon] => { 305 | let mut mon = mon.clone(); 306 | mon.pow(n); 307 | Polynomial(vec![mon]) 308 | } 309 | _ => { 310 | let mut out = self.clone(); 311 | for i in (0..n.ilog2()).rev() { 312 | out = &out * &out; 313 | if (n >> i) & 1 != 0 { 314 | out = &out * self; 315 | } 316 | } 317 | out 318 | } 319 | }, 320 | } 321 | } 322 | 323 | /// computes self = self[v |-> p] 324 | pub fn subst(&mut self, v: I, p: &Self) { 325 | for mut mon in std::mem::take(&mut self.0) { 326 | if let Some(n) = mon.powers.remove(&v) { 327 | if !p.is_zero() { 328 | self.0.append(&mut (p.pow(n) * &mon).0); 329 | } 330 | } else { 331 | self.0.push(mon) 332 | } 333 | } 334 | self.0.sort_by(Monomial::lex); 335 | self.dedup() 336 | } 337 | } 338 | 339 | #[derive(Clone, Default)] 340 | pub struct LinVar { 341 | coeff: Complex, 342 | var: I, 343 | } 344 | 345 | // This ignores the coefficients 346 | impl Ord for LinVar { 347 | fn cmp(&self, other: &Self) -> Ordering { self.var.cmp(&other.var) } 348 | } 349 | impl PartialOrd for LinVar { 350 | fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } 351 | } 352 | impl PartialEq for LinVar { 353 | fn eq(&self, other: &Self) -> bool { self.cmp(other) == Ordering::Equal } 354 | } 355 | impl Eq for LinVar {} 356 | 357 | impl std::fmt::Debug for LinVar { 358 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 359 | if self.coeff == Complex::NEG_ONE { 360 | write!(f, "-")? 361 | } else if self.coeff != Complex::ONE { 362 | if self.coeff.im != Rational::ZERO { 363 | write!(f, "({})*", self.coeff)? 364 | } else { 365 | write!(f, "{}*", self.coeff)? 366 | } 367 | } 368 | write!(f, "m{:?}", self.var) 369 | } 370 | } 371 | 372 | #[derive(Clone)] 373 | pub struct LinPoly { 374 | cnst: Complex, 375 | /// sorted 376 | terms: Vec>, 377 | } 378 | impl Default for LinPoly { 379 | fn default() -> Self { Self::ZERO } 380 | } 381 | 382 | impl std::fmt::Debug for LinPoly { 383 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 384 | write!(f, "lin ")?; 385 | for v in &self.terms { 386 | write!(f, "{v:?} + ")? 387 | } 388 | write!(f, "{:?}", self.cnst) 389 | } 390 | } 391 | 392 | impl Ord for LinPoly { 393 | fn cmp(&self, other: &Self) -> Ordering { 394 | for (a, b) in self.terms.iter().zip(&other.terms) { 395 | match a.var.cmp(&b.var).then_with(|| a.coeff.cmp(&b.coeff)) { 396 | Ordering::Equal => {} 397 | non_eq => return non_eq, 398 | } 399 | } 400 | self.terms.len().cmp(&other.terms.len()).then_with(|| self.cnst.cmp(&other.cnst)) 401 | } 402 | } 403 | impl PartialOrd for LinPoly { 404 | fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } 405 | } 406 | impl PartialEq for LinPoly { 407 | fn eq(&self, other: &Self) -> bool { self.cmp(other) == Ordering::Equal } 408 | } 409 | impl Eq for LinPoly {} 410 | 411 | impl std::ops::MulAssign for LinPoly { 412 | fn mul_assign(&mut self, rhs: Complex) { 413 | if rhs != Complex::ONE { 414 | for v in &mut self.terms { 415 | v.coeff *= rhs.clone(); 416 | } 417 | self.cnst *= rhs 418 | } 419 | } 420 | } 421 | 422 | impl std::ops::Mul for LinPoly { 423 | type Output = Self; 424 | fn mul(mut self, rhs: Complex) -> Self { 425 | self *= rhs; 426 | self 427 | } 428 | } 429 | 430 | impl std::ops::Add for LinPoly { 431 | type Output = Self; 432 | fn add(self, other: Self) -> Self { 433 | let mut out = LinPoly::cnst(self.cnst + other.cnst); 434 | for item in self.terms.into_iter().merge_join_by(other.terms, Ord::cmp) { 435 | match item { 436 | EitherOrBoth::Left(mon) | EitherOrBoth::Right(mon) => out.terms.push(mon), 437 | EitherOrBoth::Both(mut m1, m2) => { 438 | m1.coeff += m2.coeff; 439 | if m1.coeff != Complex::ZERO { 440 | out.terms.push(m1) 441 | } 442 | } 443 | } 444 | } 445 | out 446 | } 447 | } 448 | 449 | impl std::ops::Sub for LinPoly { 450 | type Output = Self; 451 | fn sub(self, other: Self) -> Self { self + other * Complex::NEG_ONE } 452 | } 453 | impl std::ops::SubAssign for LinPoly { 454 | fn sub_assign(&mut self, rhs: Self) { *self = std::mem::take(self) - rhs } 455 | } 456 | 457 | impl LinPoly { 458 | const fn cnst(cnst: Complex) -> Self { Self { cnst, terms: vec![] } } 459 | const ZERO: Self = Self::cnst(Complex::ZERO); 460 | } 461 | impl LinPoly { 462 | fn dedup(&mut self) { 463 | let mut it = std::mem::take(&mut self.terms).into_iter(); 464 | if let Some(mut mon) = it.next() { 465 | for m2 in it { 466 | if mon == m2 { 467 | mon.coeff += m2.coeff; 468 | } else { 469 | if mon.coeff != Complex::ZERO { 470 | self.terms.push(mon) 471 | } 472 | mon = m2; 473 | } 474 | } 475 | if mon.coeff != Complex::ZERO { 476 | self.terms.push(mon) 477 | } 478 | } 479 | } 480 | } 481 | 482 | mk_id! { MonomialId(u32), } 483 | 484 | pub(super) fn gaussian_elimination( 485 | vars: &mut SortedIdxVec>, polys: BTreeSet>, 486 | ) -> OrUnsat>> { 487 | if polys.is_empty() { 488 | return Ok(vec![]) 489 | } 490 | let mut eqs = BTreeSet::new(); 491 | for p in polys { 492 | let mut eq = LinPoly::ZERO; 493 | for mon in p.0 { 494 | if mon.powers.is_empty() { 495 | eq.cnst += mon.coeff 496 | } else { 497 | let var = vars 498 | .find_index(|m| Monomial::lex_powers(m, &mon.powers)) 499 | .unwrap_or_else(|idx| vars.insert_at(idx, mon.powers.clone())); 500 | eq.terms.push(LinVar { coeff: mon.coeff, var }); 501 | } 502 | } 503 | eq.terms.sort_unstable(); 504 | eq.dedup(); 505 | if let [v, ..] = &*eq.terms { 506 | eq *= v.coeff.clone().inv(); 507 | eqs.insert(eq); 508 | } else if eq.cnst != Complex::ZERO { 509 | return Err(Unsat) 510 | } 511 | } 512 | 513 | // GaussElimination 514 | if eqs.len() <= 1 { 515 | return Ok(eqs.into_iter().collect()) 516 | } 517 | let mut eqs2 = vec![]; 518 | while let Some(eq1) = eqs.pop_first() { 519 | let v1 = eq1.terms[0].var; 520 | while matches!(eqs.first(), Some(eq2) if eq2.terms[0].var == v1) { 521 | let mut eq = eq1.clone() - eqs.pop_first().unwrap(); 522 | if let [v, ..] = &*eq.terms { 523 | eq *= v.coeff.clone().inv(); 524 | eqs.insert(eq); 525 | } else if eq.cnst != Complex::ZERO { 526 | return Err(Unsat) 527 | } 528 | } 529 | eqs2.push(eq1); 530 | } 531 | for i in 1..eqs2.len() { 532 | let [lo @ .., eq1] = &mut eqs2[..=i] else { unreachable!() }; 533 | let v1 = eq1.terms[0].var; 534 | for eq2 in lo { 535 | if let Some(lv) = eq2.terms.iter().find(|lv| v1 <= lv.var) { 536 | if v1 == lv.var { 537 | let eq1 = eq1.clone() * lv.coeff.clone(); 538 | *eq2 = std::mem::take(eq2) - eq1; 539 | } 540 | } 541 | } 542 | } 543 | let mut unsat = false; 544 | eqs2.retain_mut(|eq| { 545 | !eq.terms.is_empty() || { 546 | unsat |= eq.cnst != Complex::ZERO; 547 | false 548 | } 549 | }); 550 | if unsat { 551 | return Err(Unsat) 552 | } 553 | Ok(eqs2) 554 | } 555 | 556 | impl Polynomial { 557 | pub(super) fn reduce( 558 | self, vars: &SortedIdxVec>, eqs: &[LinPoly], 559 | ) -> bool { 560 | let mut eq = LinPoly::ZERO; 561 | for mon in self.0 { 562 | if mon.powers.is_empty() { 563 | eq.cnst += mon.coeff 564 | } else if let Ok(var) = vars.find_index(|m| Monomial::lex_powers(m, &mon.powers)) { 565 | eq.terms.push(LinVar { coeff: mon.coeff, var }) 566 | } else { 567 | return false 568 | } 569 | } 570 | eq.terms.sort_unstable(); 571 | eq.dedup(); 572 | 573 | // LinearEquationReduce 574 | let mut it = eqs.iter(); 575 | 'next: loop { 576 | let [v1, ..] = &*eq.terms else { return eq.cnst == Complex::ZERO }; 577 | for eq2 in it.by_ref() { 578 | if v1.var == eq2.terms[0].var { 579 | eq -= eq2.clone() * v1.coeff.clone(); 580 | continue 'next 581 | } 582 | } 583 | return false 584 | } 585 | } 586 | } 587 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::{try_to_line_col, ParseError}; 2 | use crate::types::{Article, DirectiveKind, Formula, Position}; 3 | use crate::{Global, LocalContext, MizPath}; 4 | use std::path::{Path, PathBuf}; 5 | 6 | #[derive(PartialEq, Eq)] 7 | enum Severity { 8 | Error, 9 | #[allow(unused)] 10 | Warning, 11 | } 12 | 13 | impl ParseError { 14 | pub fn report(self, path: &Path) { 15 | if let Some(pos) = self.pos() { 16 | if let Ok((line, col)) = try_to_line_col(path, pos) { 17 | eprintln!("{}:{line}:{col}: error: {self}", path.to_string_lossy()) 18 | } else { 19 | eprintln!("{}: index {pos}: error: {self}", path.to_string_lossy()) 20 | } 21 | } else { 22 | eprintln!("{}: error: {self}", path.to_string_lossy()) 23 | } 24 | } 25 | } 26 | 27 | pub fn report_accom_warning(kind: DirectiveKind, path: PathBuf, art: Article, pos: Position) { 28 | eprintln!( 29 | "{file}:{pos:?}: warning: {kind} for {art} not found or empty (looked in {path})", 30 | file = MizPath { art }.to_path(true, false, "miz").to_string_lossy(), 31 | kind = kind.name(), 32 | path = path.to_string_lossy() 33 | ); 34 | } 35 | 36 | #[derive(Debug)] 37 | pub enum MizError { 38 | UnexpectedPragma(String), 39 | IterEqualityNotAnEquality(Box), 40 | } 41 | 42 | impl MizError { 43 | pub fn report(self, art: Article, pos: Position, _g: &Global, lc: &LocalContext) -> bool { 44 | let severity = Severity::Error; 45 | let msg = match &self { 46 | MizError::UnexpectedPragma(pragma) => format!("unknown pragma '{pragma}'"), 47 | MizError::IterEqualityNotAnEquality(f) => format!("not an equality: {}", lc.pp(f)), 48 | }; 49 | let file = MizPath { art }.to_path(true, false, "miz"); 50 | let sev = match severity { 51 | Severity::Error => "error", 52 | Severity::Warning => "warning", 53 | }; 54 | eprintln!("{}:{pos:?}: {sev}: {msg}", file.to_string_lossy()); 55 | severity == Severity::Error 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/export.rs: -------------------------------------------------------------------------------- 1 | use crate::accom::SigBuilder; 2 | use crate::analyze::Analyzer; 3 | use crate::parser::{catch_missing, MaybeMut, PathResult}; 4 | use crate::reader::DefiniensId; 5 | use crate::types::*; 6 | use crate::{Assignment, LocalContext, OnVarMut, VisitMut}; 7 | use enum_map::EnumMap; 8 | use itertools::Itertools; 9 | use std::fmt::Debug; 10 | 11 | const DOUBLE_CHECK: bool = false; 12 | 13 | #[derive(Default)] 14 | pub struct Exporter { 15 | pub constrs_base: ConstructorsBase, 16 | pub clusters_base: ClustersBase, 17 | pub definitions_base: DefiniensId, 18 | pub identify_base: u32, 19 | pub reductions_base: u32, 20 | pub properties_base: u32, 21 | pub theorems: Vec, 22 | pub schemes: Vec>, 23 | } 24 | 25 | #[allow(clippy::panic)] 26 | fn assert_eq_iter, U: Debug>( 27 | header: &str, mut it1: impl Iterator + Clone, mut it2: impl Iterator + Clone, 28 | ) { 29 | if !it1.clone().eq(it2.clone()) { 30 | eprintln!("failure in {header}:"); 31 | for i in 0.. { 32 | match (it1.next(), it2.next()) { 33 | (None, None) => break, 34 | (Some(x1), Some(x2)) if x1 == x2 => eprintln!("{i}: both: {x1:?}"), 35 | (a, b) => eprintln!("{i}: mismatch:\n{a:?}\n{b:?}\n"), 36 | } 37 | } 38 | panic!("failure in {header}"); 39 | } 40 | } 41 | 42 | struct ExportPrep<'a> { 43 | ctx: Option<&'a Constructors>, 44 | lc: &'a LocalContext, 45 | ic: &'a IdxVec, 46 | depth: u32, 47 | } 48 | impl VisitMut for ExportPrep<'_> { 49 | fn push_bound(&mut self, _: IdentId, _: &mut Type) { self.depth += 1 } 50 | fn pop_bound(&mut self, n: u32) { self.depth -= n } 51 | fn visit_term(&mut self, tm: &mut Term) { 52 | if let Term::Infer(nr) = *tm { 53 | *tm = self.ic[nr].def.visit_cloned(&mut OnVarMut(|v| *v += self.depth)); 54 | } 55 | self.super_visit_term(tm); 56 | } 57 | fn visit_attrs(&mut self, attrs: &mut Attrs) { 58 | attrs.reinsert_all(self.ctx, self.lc, true, |attr| self.visit_terms(&mut attr.args)) 59 | } 60 | fn visit_attr_pair(&mut self, attrs: &mut (Attrs, Attrs)) { 61 | self.visit_attrs(&mut attrs.0); 62 | attrs.1.clone_from(&attrs.0); 63 | } 64 | } 65 | 66 | impl<'a> ExportPrep<'a> { 67 | fn with_ctx(&mut self, ctx: Option<&Constructors>, f: impl FnOnce(&mut ExportPrep<'_>)) { 68 | f(&mut ExportPrep { ctx, ..*self }); 69 | } 70 | } 71 | 72 | fn mark_formats( 73 | vocs: &Vocabularies, marked_vocs: &mut Vocabularies, fmts: &mut [T], 74 | get: impl Fn(&mut T) -> &mut Format, 75 | ) { 76 | let mut trans = EnumMap::<_, Vec<_>>::default(); 77 | let (mut hi, mut new) = <(SymbolsBase, SymbolsBase)>::default(); 78 | for (art, counts) in &vocs.0 { 79 | let lo = hi; 80 | hi += counts; 81 | let used = fmts.iter_mut().any(|fmt| { 82 | let mut used = false; 83 | get(fmt).visit(|k, sym| used |= (lo.0[k]..hi.0[k]).contains(&sym)); 84 | used 85 | }); 86 | if used { 87 | marked_vocs.0.push((*art, *counts)); 88 | for (kind, &count) in &counts.0 { 89 | trans[kind].extend((0..count).map(|i| Some(i + new.0[kind]))) 90 | } 91 | new += counts; 92 | } else { 93 | for (kind, &count) in &counts.0 { 94 | trans[kind].extend((0..count).map(|_| None)) 95 | } 96 | } 97 | } 98 | #[allow(clippy::unwrap_used, clippy::indexing_slicing)] 99 | for fmt in fmts { 100 | get(fmt).visit_mut(|k, sym| *sym = trans[k][*sym as usize].unwrap()); 101 | } 102 | } 103 | 104 | #[derive(Debug)] 105 | struct MarkConstr<'a> { 106 | /// Article, plus the constructor counts *excluding* this article. 107 | /// The current article may be in the list, in which case it is at the end. 108 | accum: &'a [(Article, ConstructorsBase)], 109 | /// The total constructor counts 110 | base: &'a ConstructorsBase, 111 | /// This is a parallel array, where `used[i]` corresponds to `accum[i]`, 112 | /// and `used.last()` is true if constructors from the current article are used. 113 | /// (Because this always contains an entry for the current article it may either 114 | /// be the same length or one longer than accum.) 115 | used: Vec, 116 | } 117 | 118 | impl<'a> MarkConstr<'a> { 119 | fn new(sig: &'a SigBuilder, n: usize) -> Self { 120 | Self { accum: &sig.sig.0, base: &sig.base, used: vec![false; n + 1] } 121 | } 122 | 123 | fn mark(&mut self, n: u32, key: impl Fn(&ConstructorsBase) -> u32) { 124 | if n < key(self.base) { 125 | self.used[self.accum[1..].partition_point(|(_, base)| key(base) <= n)] = true 126 | } 127 | } 128 | 129 | fn closure(&mut self, constrs: &mut Constructors) { 130 | let mut base = *self.base; 131 | // We skip the first two because the first is HIDDEN (which is always used) 132 | // and the second step can't have any effect 133 | for (i, (_, lo)) in self.accum.iter().enumerate().skip(2).rev() { 134 | if self.used[i] { 135 | constrs.visit_range(self, lo..&base) 136 | } 137 | base = *lo 138 | } 139 | self.used[0] = true 140 | } 141 | 142 | fn apply(&self) -> ApplyMarkConstr { 143 | let (mut offset, mut base) = Default::default(); 144 | let mut apply = ApplyMarkConstr::default(); 145 | let mut prev = true; 146 | for (accum, &mark) in self.accum.iter().map(|p| &p.1).chain([self.base]).zip(&self.used).skip(1) 147 | { 148 | if prev != mark { 149 | if mark { 150 | offset += *accum - base; 151 | apply.0.push((base, offset)); 152 | } else { 153 | base = *accum; 154 | } 155 | prev = mark 156 | } 157 | } 158 | apply 159 | } 160 | 161 | fn apply_with(&self, f: impl FnOnce(&mut ApplyMarkConstr)) { 162 | let mut apply = self.apply(); 163 | if !apply.0.is_empty() { 164 | f(&mut apply) 165 | } 166 | } 167 | 168 | fn filtered(&self, ts: &[T]) -> Vec { 169 | ts.iter().zip(&self.used).filter(|p| *p.1).map(|p| *p.0).collect_vec() 170 | } 171 | } 172 | 173 | impl VisitMut for MarkConstr<'_> { 174 | fn visit_mode_id(&mut self, n: &mut ModeId) { self.mark(n.0, |b| b.mode) } 175 | fn visit_struct_id(&mut self, n: &mut StructId) { self.mark(n.0, |b| b.struct_mode) } 176 | fn visit_attr_id(&mut self, n: &mut AttrId) { self.mark(n.0, |b| b.attribute) } 177 | fn visit_pred_id(&mut self, n: &mut PredId) { self.mark(n.0, |b| b.predicate) } 178 | fn visit_func_id(&mut self, n: &mut FuncId) { self.mark(n.0, |b| b.functor) } 179 | fn visit_sel_id(&mut self, n: &mut SelId) { self.mark(n.0, |b| b.selector) } 180 | fn visit_aggr_id(&mut self, n: &mut AggrId) { self.mark(n.0, |b| b.aggregate) } 181 | } 182 | 183 | #[derive(Default, Debug)] 184 | struct ApplyMarkConstr(Vec<(ConstructorsBase, ConstructorsBase)>); 185 | impl ApplyMarkConstr { 186 | fn apply(&mut self, n: &mut u32, key: impl Fn(&ConstructorsBase) -> u32) { 187 | if let Some(i) = self.0.partition_point(|(base, _)| key(base) <= *n).checked_sub(1) { 188 | *n -= key(&self.0[i].1) 189 | } 190 | } 191 | } 192 | 193 | impl VisitMut for ApplyMarkConstr { 194 | const MODIFY_IDS: bool = true; 195 | fn visit_mode_id(&mut self, n: &mut ModeId) { self.apply(&mut n.0, |b| b.mode) } 196 | fn visit_struct_id(&mut self, n: &mut StructId) { self.apply(&mut n.0, |b| b.struct_mode) } 197 | fn visit_attr_id(&mut self, n: &mut AttrId) { self.apply(&mut n.0, |b| b.attribute) } 198 | fn visit_pred_id(&mut self, n: &mut PredId) { self.apply(&mut n.0, |b| b.predicate) } 199 | fn visit_func_id(&mut self, n: &mut FuncId) { self.apply(&mut n.0, |b| b.functor) } 200 | fn visit_sel_id(&mut self, n: &mut SelId) { self.apply(&mut n.0, |b| b.selector) } 201 | fn visit_aggr_id(&mut self, n: &mut AggrId) { self.apply(&mut n.0, |b| b.aggregate) } 202 | } 203 | 204 | impl AccumConstructors { 205 | fn mark Visitable> + Visitable>( 206 | &mut self, t: &mut T, n: usize, arts: &[Article], 207 | ) -> Vec
{ 208 | let mut marks = MarkConstr::new(&self.sig, n); 209 | t.visit(&mut marks); 210 | marks.closure(&mut self.constrs); 211 | marks.apply_with(|v| t.visit(v)); 212 | marks.filtered(arts) 213 | } 214 | } 215 | 216 | macro_rules! try_p { 217 | ($self:expr, $e:expr) => { 218 | ok_parse_err(&mut $self.r.has_errors, $e) 219 | }; 220 | } 221 | macro_rules! assert_eq_nonempty { 222 | ($self:expr, $ne:expr, $e:expr) => { 223 | if let Some(p) = try_p!($self, catch_missing($e)) { 224 | assert_eq!($ne, p.is_some()); 225 | } 226 | }; 227 | } 228 | fn ok_parse_err(has_errors: &mut bool, val: PathResult) -> Option { 229 | match val { 230 | Ok(t) => Some(t), 231 | Err((path, e)) => { 232 | e.report(&path); 233 | *has_errors = true; 234 | None 235 | } 236 | } 237 | } 238 | 239 | impl Analyzer<'_> { 240 | pub fn export(&mut self) { 241 | // This file deals with expressions after renumbering, so the formatter is liable to panic 242 | self.r.lc.formatter.cfg.enable_formatter = false; 243 | let ep = &mut ExportPrep { 244 | ctx: Some(&self.r.g.constrs), 245 | lc: &self.r.lc, 246 | ic: &self.r.lc.infer_const.borrow().vec, 247 | depth: 0, 248 | }; 249 | let new_prel = !self.g.cfg.overwrite_prel; 250 | 251 | // loading .sgl 252 | let mut arts2 = vec![]; 253 | if let Some(accom) = &self.accom { 254 | arts2.extend(accom.sig.sig.0.iter().map(|p| p.0)); 255 | } else { 256 | try_p!(self, self.path.read_sgl(&mut arts2)); 257 | } 258 | let arts1 = if self.g.constrs.since(&self.export.constrs_base).is_empty() { 259 | &*arts2 260 | } else { 261 | let n = arts2.len(); 262 | arts2.push(self.article); 263 | &arts2[..n] 264 | }; 265 | 266 | // loading .vcl, .aco 267 | let (mut vocs1, mut aco) = <(Vocabularies, AccumConstructors)>::default(); 268 | if let Some(accom) = &mut self.r.accom { 269 | accom.build_vocabularies(&mut vocs1); 270 | aco.sig = std::mem::take(&mut accom.sig); 271 | aco.constrs.extend(&self.g.constrs.upto(&aco.sig.base)); 272 | aco.constrs.visit(ep); 273 | } else { 274 | self.path.read_vcl(&mut vocs1).unwrap(); 275 | self.path.read_aco(&mut aco).unwrap(); 276 | } 277 | assert_eq!(self.export.constrs_base, aco.constrs.len()); 278 | assert_eq!(arts1.len(), aco.sig.sig.len()); 279 | 280 | // validating .dfr 281 | { 282 | let mut dfr1 = self.lc.formatter.formats.0[self.formats_base..].to_owned(); 283 | let nonempty = !dfr1.is_empty(); 284 | let (mut marked_vocs, mut vocs2, mut dfr2) = Default::default(); 285 | if self.g.cfg.verify_export { 286 | assert_eq_nonempty!(self, nonempty, self.path.read_dfr(false, &mut vocs2, &mut dfr2)); 287 | } 288 | if nonempty { 289 | mark_formats(&vocs1, &mut marked_vocs, &mut dfr1, |x| x); 290 | if self.g.cfg.verify_export { 291 | assert_eq!(marked_vocs, vocs2); 292 | assert_eq!(dfr1, dfr2.0); 293 | } 294 | if self.g.cfg.xml_export { 295 | self.path.write_dfr(new_prel, &marked_vocs, &dfr1); 296 | if DOUBLE_CHECK { 297 | let (mut vocs3, mut dfr3) = Default::default(); 298 | self.path.read_dfr(new_prel, &mut vocs3, &mut dfr3).unwrap(); 299 | assert_eq!(marked_vocs, vocs3); 300 | assert_eq!(dfr1, dfr3.0); 301 | } 302 | } 303 | } 304 | if self.g.cfg.cache_prel { 305 | self.path.with_cache(|c| &c.dfr, (marked_vocs, dfr1)); 306 | } 307 | } 308 | 309 | // validating .dco (also push current article to aco) 310 | { 311 | let since1 = self.r.g.constrs.since(&self.export.constrs_base); 312 | let nonempty = !since1.is_empty(); 313 | let (mut dco1, mut dco2) = <(DepConstructors, _)>::default(); 314 | if self.g.cfg.verify_export { 315 | assert_eq_nonempty!(self, nonempty, self.path.read_dco(false, &mut dco2, true)); 316 | } 317 | if nonempty { 318 | dco1.constrs = since1.to_owned(); 319 | dco1.counts = dco1.constrs.len(); 320 | dco1.constrs.visit(ep); 321 | assert_eq!(aco.sig.base, aco.constrs.len()); 322 | aco.constrs.append(&mut dco1.constrs.clone()); 323 | let mut marks = MarkConstr::new(&aco.sig, arts1.len()); 324 | *marks.used.last_mut().unwrap() = true; 325 | dco1.constrs.visit(&mut marks); 326 | marks.closure(&mut aco.constrs); 327 | marks.apply_with(|v| dco1.constrs.visit(v)); 328 | dco1.sig = marks.filtered(arts1); 329 | if self.g.cfg.verify_export { 330 | assert_eq!(dco1.sig, dco2.sig); 331 | assert_eq!(dco1.counts, dco2.counts); 332 | macro_rules! process { ($($field:ident),*) => {$( 333 | assert_eq_iter(concat!("constrs.", stringify!($field)), 334 | dco1.constrs.$field.0.iter(), dco2.constrs.$field.0.iter()); 335 | )*}} 336 | process!(mode, struct_mode, attribute, predicate, functor, selector, aggregate); 337 | } 338 | if self.g.cfg.xml_export { 339 | self.path.write_dco(new_prel, &aco.sig.base, &dco1); 340 | if DOUBLE_CHECK { 341 | let mut dco3 = Default::default(); 342 | self.path.read_dco(new_prel, &mut dco3, true).unwrap(); 343 | assert_eq!(dco1, dco3); 344 | } 345 | } 346 | aco.sig.sig.push((self.article, aco.sig.base)); 347 | aco.sig.base = self.g.constrs.len(); 348 | } 349 | if self.g.cfg.cache_prel { 350 | self.path.with_cache(|c| &c.dco, dco1); 351 | } 352 | } 353 | 354 | // validating .dno 355 | { 356 | let (mut dno1, mut dno2) = <(DepNotation, _)>::default(); 357 | dno1.pats = (self.notations.iter()) 358 | .flat_map(|(i, nota)| ¬a[self.notations_base[i] as usize..]) 359 | .map(|pat| { 360 | let Pattern { article, abs_nr, kind, fmt, primary, visible, pos } = pat.visit_cloned(ep); 361 | let fmt = self.lc.formatter.formats[fmt]; 362 | Pattern { article, abs_nr, kind, fmt, primary, visible, pos } 363 | }) 364 | .collect(); 365 | let nonempty = !dno1.pats.is_empty(); 366 | if self.g.cfg.verify_export { 367 | assert_eq_nonempty!(self, nonempty, self.path.read_dno(false, &mut dno2)); 368 | } 369 | if nonempty { 370 | mark_formats(&vocs1, &mut dno1.vocs, &mut dno1.pats, |p| &mut p.fmt); 371 | let mut marks = MarkConstr::new(&aco.sig, arts1.len()); 372 | dno1.pats.iter_mut().for_each(|p| p.visit(&mut marks)); 373 | marks.closure(&mut aco.constrs); 374 | marks.apply_with(|v| dno1.pats.iter_mut().for_each(|p| p.visit(v))); 375 | dno1.sig = marks.filtered(&arts2); 376 | if self.g.cfg.verify_export { 377 | assert_eq!(dno1.sig, dno2.sig); 378 | assert_eq!(dno1.vocs, dno2.vocs); 379 | assert_eq_iter("notations", dno1.pats.iter(), dno2.pats.iter()); 380 | } 381 | if self.g.cfg.xml_export { 382 | self.path.write_dno(new_prel, &dno1); 383 | if DOUBLE_CHECK { 384 | let mut dno3 = Default::default(); 385 | self.path.read_dno(new_prel, &mut dno3).unwrap(); 386 | assert_eq!(dno1, dno3); 387 | } 388 | } 389 | } 390 | if self.g.cfg.cache_prel { 391 | self.path.with_cache(|c| &c.dno, dno1); 392 | } 393 | } 394 | 395 | // validating .dcl 396 | { 397 | let (mut dcl1, mut dcl2) = <(DepClusters, _)>::default(); 398 | let since1 = self.r.g.clusters.since(&self.export.clusters_base); 399 | let nonempty = !since1.is_empty(); 400 | if self.g.cfg.verify_export { 401 | assert_eq_nonempty!(self, nonempty, self.path.read_dcl(false, &mut dcl2)); 402 | } 403 | if nonempty { 404 | dcl1.cl = since1.to_owned(); 405 | dcl1.cl.visit(ep); 406 | dcl1.sig = aco.mark(&mut dcl1.cl, arts1.len(), &arts2); 407 | if self.g.cfg.verify_export { 408 | ep.with_ctx(None, |ep| dcl2.cl.visit(ep)); 409 | assert_eq!(dcl1.sig, dcl2.sig); 410 | macro_rules! process { ($($field:ident),*) => {$({ 411 | assert_eq_iter(concat!("clusters.", stringify!($field)), 412 | dcl1.cl.$field.iter(), dcl2.cl.$field.iter()); 413 | })*}} 414 | process!(registered, functor, conditional); 415 | } 416 | if self.g.cfg.xml_export { 417 | self.path.write_dcl(new_prel, &dcl1); 418 | if DOUBLE_CHECK { 419 | let mut dcl3 = Default::default(); 420 | self.path.read_dcl(new_prel, &mut dcl3).unwrap(); 421 | assert_eq!(dcl1, dcl3); 422 | } 423 | } 424 | } 425 | if self.g.cfg.cache_prel { 426 | self.path.with_cache(|c| &c.dcl, dcl1); 427 | } 428 | } 429 | 430 | // validating .def 431 | { 432 | let mut def1 = self.definitions.0[self.export.definitions_base.into_usize()..].to_owned(); 433 | let nonempty = !def1.is_empty(); 434 | let (mut sig1, mut sig, mut def2) = Default::default(); 435 | if self.g.cfg.verify_export { 436 | assert_eq_nonempty!(self, nonempty, self.path.read_def(false, &mut sig, &mut def2)); 437 | } 438 | if nonempty { 439 | def1.visit(ep); 440 | sig1 = aco.mark(&mut def1, arts1.len(), &arts2); 441 | if self.g.cfg.verify_export { 442 | ep.with_ctx(None, |ep| def2.visit(ep)); 443 | assert_eq!(sig1, sig); 444 | assert_eq_iter("definitions", def1.iter(), def2.iter()); 445 | } 446 | if self.g.cfg.xml_export { 447 | self.path.write_def(new_prel, &sig1, &def1); 448 | if DOUBLE_CHECK { 449 | let (mut sig3, mut def3) = Default::default(); 450 | (self.path) 451 | .read_definitions(MaybeMut::None, new_prel, "def", Some(&mut sig3), &mut def3) 452 | .unwrap(); 453 | ep.with_ctx(None, |ep| def3.visit(ep)); 454 | assert_eq!(sig1, sig3); 455 | assert_eq!(def1, def3); 456 | } 457 | } 458 | } 459 | if self.g.cfg.cache_prel { 460 | self.path.with_cache(|c| &c.def, (sig1, def1)); 461 | } 462 | } 463 | 464 | // validating .did 465 | { 466 | let mut did1 = self.identify[self.export.identify_base as usize..].to_owned(); 467 | let nonempty = !did1.is_empty(); 468 | let (mut sig1, mut sig, mut did2) = Default::default(); 469 | if self.g.cfg.verify_export { 470 | assert_eq_nonempty!(self, nonempty, self.path.read_did(false, &mut sig, &mut did2)); 471 | } 472 | if nonempty { 473 | did1.visit(ep); 474 | sig1 = aco.mark(&mut did1, arts1.len(), &arts2); 475 | if self.g.cfg.verify_export { 476 | ep.with_ctx(None, |ep| did2.visit(ep)); 477 | assert_eq!(sig1, sig); 478 | assert_eq_iter("identities", did1.iter(), did2.iter()); 479 | } 480 | if self.g.cfg.xml_export { 481 | self.path.write_did(new_prel, &sig1, &did1); 482 | if DOUBLE_CHECK { 483 | let (mut sig3, mut did3) = Default::default(); 484 | (self.path) 485 | .read_identify_regs(MaybeMut::None, new_prel, "did", Some(&mut sig3), &mut did3) 486 | .unwrap(); 487 | ep.with_ctx(None, |ep| did3.visit(ep)); 488 | assert_eq!(sig1, sig3); 489 | assert_eq!(did1, did3); 490 | } 491 | } 492 | } 493 | if self.g.cfg.cache_prel { 494 | self.path.with_cache(|c| &c.did, (sig1, did1)); 495 | } 496 | } 497 | 498 | // validating .drd 499 | { 500 | let mut drd1 = self.reductions[self.export.reductions_base as usize..].to_owned(); 501 | let nonempty = !drd1.is_empty(); 502 | let (mut sig1, mut sig, mut drd2) = Default::default(); 503 | if self.g.cfg.verify_export { 504 | assert_eq_nonempty!(self, nonempty, self.path.read_drd(false, &mut sig, &mut drd2)); 505 | } 506 | if nonempty { 507 | drd1.visit(ep); 508 | sig1 = aco.mark(&mut drd1, arts1.len(), &arts2); 509 | if self.g.cfg.verify_export { 510 | ep.with_ctx(None, |ep| drd2.visit(ep)); 511 | assert_eq!(sig1, sig); 512 | assert_eq_iter("reductions", drd1.iter(), drd2.iter()); 513 | } 514 | if self.g.cfg.xml_export { 515 | self.path.write_drd(new_prel, &sig1, &drd1); 516 | if DOUBLE_CHECK { 517 | let (mut sig3, mut drd3) = Default::default(); 518 | (self.path) 519 | .read_reduction_regs(MaybeMut::None, new_prel, "drd", Some(&mut sig3), &mut drd3) 520 | .unwrap(); 521 | ep.with_ctx(None, |ep| drd3.visit(ep)); 522 | assert_eq!(sig1, sig3); 523 | assert_eq!(drd1, drd3); 524 | } 525 | } 526 | } 527 | if self.g.cfg.cache_prel { 528 | self.path.with_cache(|c| &c.drd, (sig1, drd1)); 529 | } 530 | } 531 | 532 | // validating .dpr 533 | { 534 | let mut dpr1 = self.properties[self.export.properties_base as usize..].to_owned(); 535 | let nonempty = !dpr1.is_empty(); 536 | let (mut sig1, mut sig, mut dpr2) = Default::default(); 537 | if self.g.cfg.verify_export { 538 | assert_eq_nonempty!(self, nonempty, self.path.read_dpr(false, &mut sig, &mut dpr2)); 539 | dpr2.visit(ep); 540 | } 541 | if nonempty { 542 | dpr1.visit(ep); 543 | sig1 = aco.mark(&mut dpr1, arts1.len(), &arts2); 544 | if self.g.cfg.verify_export { 545 | ep.with_ctx(None, |ep| dpr2.visit(ep)); 546 | assert_eq!(sig1, sig); 547 | assert_eq_iter("properties", dpr1.iter(), dpr2.iter()); 548 | } 549 | if self.g.cfg.xml_export { 550 | self.path.write_dpr(new_prel, &sig1, &dpr1); 551 | if DOUBLE_CHECK { 552 | let (mut sig3, mut dpr3) = Default::default(); 553 | (self.path) 554 | .read_properties(MaybeMut::None, new_prel, "dpr", Some(&mut sig3), &mut dpr3) 555 | .unwrap(); 556 | ep.with_ctx(None, |ep| dpr3.visit(ep)); 557 | assert_eq!(sig1, sig3); 558 | assert_eq!(dpr1, dpr3); 559 | } 560 | } 561 | } 562 | if self.g.cfg.cache_prel { 563 | self.path.with_cache(|c| &c.dpr, (sig1, dpr1)); 564 | } 565 | } 566 | 567 | // validating .the 568 | { 569 | let (mut thms1, mut thms2) = <(DepTheorems, _)>::default(); 570 | std::mem::swap(&mut thms1.thm, &mut self.export.theorems); 571 | let nonempty = !thms1.thm.is_empty(); 572 | if self.g.cfg.verify_export { 573 | assert_eq_nonempty!(self, nonempty, self.path.read_the(false, &mut thms2)); 574 | } 575 | if nonempty { 576 | thms1.thm.visit(ep); 577 | thms1.sig = aco.mark(&mut thms1.thm, arts1.len(), &arts2); 578 | if self.g.cfg.verify_export { 579 | ep.with_ctx(None, |ep| thms2.thm.visit(ep)); 580 | assert_eq!(thms1.sig, thms2.sig); 581 | assert_eq_iter("theorems", thms1.thm.iter(), thms2.thm.iter()); 582 | } 583 | if self.g.cfg.xml_export { 584 | self.path.write_the(new_prel, &thms1); 585 | if DOUBLE_CHECK { 586 | let mut thms3 = Default::default(); 587 | self.path.read_the(new_prel, &mut thms3).unwrap(); 588 | ep.with_ctx(None, |ep| thms3.thm.visit(ep)); 589 | assert_eq!(thms1, thms3); 590 | } 591 | } 592 | } 593 | if self.g.cfg.cache_prel { 594 | self.path.with_cache(|c| &c.the, thms1); 595 | } 596 | } 597 | 598 | // validating .sch 599 | { 600 | let (mut schs1, mut schs2) = <(DepSchemes, _)>::default(); 601 | schs1.sch = (self.export.schemes.iter()) 602 | .map(|i| i.map(|i| self.libs.sch[&(ArticleId::SELF, i)].visit_cloned(ep))) 603 | .collect(); 604 | let nonempty = !schs1.sch.is_empty(); 605 | if self.g.cfg.verify_export { 606 | assert_eq_nonempty!(self, nonempty, self.path.read_sch(false, &mut schs2)); 607 | } 608 | if nonempty { 609 | schs1.sig = aco.mark(&mut schs1.sch, arts1.len(), &arts2); 610 | if self.g.cfg.verify_export { 611 | ep.with_ctx(None, |ep| schs2.sch.visit(ep)); 612 | assert_eq!(schs1.sig, schs2.sig); 613 | assert_eq_iter("schemes", schs1.sch.iter(), schs2.sch.iter()); 614 | } 615 | if self.g.cfg.xml_export { 616 | self.path.write_sch(new_prel, &schs1); 617 | if DOUBLE_CHECK { 618 | let mut schs3 = Default::default(); 619 | self.path.read_sch(new_prel, &mut schs3).unwrap(); 620 | ep.with_ctx(None, |ep| schs3.sch.visit(ep)); 621 | assert_eq!(schs1, schs3); 622 | } 623 | } 624 | } 625 | if self.g.cfg.cache_prel { 626 | self.path.with_cache(|c| &c.sch, schs1); 627 | } 628 | } 629 | } 630 | } 631 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // #![warn(clippy::unwrap_used, clippy::expect_used, clippy::panic)] 2 | use crate::format::FormatterConfig; 3 | use crate::types::*; 4 | use clap::{ArgAction, CommandFactory, Parser, ValueEnum}; 5 | use enum_map::EnumMap; 6 | use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle}; 7 | use itertools::Itertools; 8 | use once_cell::sync::OnceCell; 9 | use std::cell::Cell; 10 | use std::cmp::Ordering; 11 | use std::collections::{BTreeMap, HashMap}; 12 | use std::fmt::Display; 13 | use std::io::{self, Write}; 14 | use std::sync::atomic::AtomicBool; 15 | use std::sync::Mutex; 16 | 17 | mod accom; 18 | mod analyze; 19 | mod ast; 20 | mod bignum; 21 | mod cache; 22 | mod checker; 23 | mod equate; 24 | mod error; 25 | mod export; 26 | mod format; 27 | mod global; 28 | mod parser; 29 | mod reader; 30 | mod types; 31 | mod unify; 32 | mod util; 33 | mod write; 34 | 35 | pub use global::*; 36 | 37 | thread_local! { 38 | static JOB_ID: Cell> = const { Cell::new(None) }; 39 | } 40 | 41 | #[allow(clippy::unwrap_used)] 42 | pub fn stat(s: &'static str, fail: bool) { 43 | *STATS.lock().unwrap().get_or_insert_with(HashMap::new).entry(s).or_default() += 1; 44 | if fail { 45 | if let Some((failures, _)) = JOBS.get() { 46 | JOB_ID.with(|job_id| { 47 | if let Some(job_id) = job_id.get() { 48 | failures[job_id].store(true, std::sync::atomic::Ordering::SeqCst); 49 | } 50 | }) 51 | } 52 | } 53 | } 54 | 55 | #[macro_export] 56 | macro_rules! vprintln { 57 | ($($args:tt)*) => { 58 | if $crate::verbose() { 59 | eprintln!($($args)*) 60 | } 61 | }; 62 | } 63 | 64 | #[allow(unused)] 65 | #[macro_export] 66 | macro_rules! vdbg { 67 | ($($args:tt)*) => { 68 | if $crate::verbose() { 69 | dbg!($($args)*) 70 | } else { 71 | ($($args)*) 72 | } 73 | }; 74 | } 75 | 76 | static VERBOSE: AtomicBool = AtomicBool::new(false); 77 | pub fn verbose() -> bool { DEBUG && VERBOSE.load(std::sync::atomic::Ordering::SeqCst) } 78 | pub fn set_verbose(b: bool) { VERBOSE.store(b, std::sync::atomic::Ordering::SeqCst) } 79 | 80 | static STATS: Mutex>> = Mutex::new(None); 81 | 82 | static JOBS: OnceCell<(Vec, Vec)> = OnceCell::new(); 83 | 84 | fn print_stats_and_exit(has_errors: bool) { 85 | #[allow(clippy::unwrap_used)] 86 | let mut g = STATS.lock().unwrap(); 87 | let mut vec: Vec<_> = g.get_or_insert_with(HashMap::new).iter().collect(); 88 | vec.sort(); 89 | for (s, i) in vec { 90 | println!("{s}: {i}"); 91 | } 92 | if let Some((failures, jobs)) = JOBS.get() { 93 | let mut first = true; 94 | for (fail, job) in failures.iter().zip(jobs) { 95 | if fail.load(std::sync::atomic::Ordering::SeqCst) { 96 | if std::mem::take(&mut first) { 97 | println!("failed articles:") 98 | } 99 | println!(" {job}") 100 | } 101 | } 102 | } 103 | std::process::exit(has_errors as i32) 104 | } 105 | 106 | #[derive(Clone)] 107 | struct Progress { 108 | multi: MultiProgress, 109 | main: Option, 110 | style: ProgressStyle, 111 | } 112 | 113 | impl Progress { 114 | #[allow(clippy::unwrap_used)] 115 | fn new(num_jobs: usize, known_len: bool) -> Option { 116 | let multi = MultiProgress::with_draw_target(ProgressDrawTarget::stdout_with_hz(5)); 117 | if multi.is_hidden() { 118 | return None 119 | } 120 | multi.set_alignment(indicatif::MultiProgressAlignment::Bottom); 121 | Some(Progress { 122 | main: if num_jobs > 1 { Some(multi.add(ProgressBar::new(num_jobs as u64))) } else { None }, 123 | style: if known_len { 124 | ProgressStyle::with_template("{msg:14} [{pos:>5}] {wide_bar} {elapsed_precise}").unwrap() 125 | } else { 126 | ProgressStyle::with_template("{msg:14} [{pos:>5}] {spinner} {elapsed_precise}").unwrap() 127 | }, 128 | multi, 129 | }) 130 | } 131 | } 132 | 133 | const fn bool_to_str(b: bool) -> &'static str { 134 | if b { 135 | "true" 136 | } else { 137 | "false" 138 | } 139 | } 140 | 141 | /// Mizar verifier toolchain. Common usage cases: 142 | /// 143 | /// * mizar-rs -dex --overwrite-prel 144 | /// Read the MML .miz files and generate the prel/ folder 145 | /// * mizar-rs 146 | /// Parse and compile the whole MML from scratch 147 | /// * mizar-rs nat_4 --one-file 148 | /// Parse and compile only article nat_4 149 | /// * mizar-rs nat_4 14 --unify-insts 150 | /// Give debugging info regarding the item at line 14 of article nat_4 151 | #[derive(Debug, clap::Parser)] 152 | #[command(author, version, about, long_about, verbatim_doc_comment)] 153 | struct Cli { 154 | /// The name of the first file to process, or the index of the file in `mml.lar` 155 | file: Option, 156 | /// The line on which to turn on verbose mode 157 | first_verbose_line: Option, 158 | 159 | #[command(flatten, next_help_heading = "Pass selection options")] 160 | passes: CliPasses, 161 | 162 | /// Strictly follow dependency order, instead of using `prel/` 163 | #[arg(short = 'd', long)] 164 | dep_order: bool, 165 | 166 | /// The number of threads to use (currently only file level parallelism is supported) 167 | #[arg(short = 'j', long, default_value_t = if DEBUG { 1 } else { num_cpus::get() })] 168 | parallelism: usize, 169 | 170 | /// Use `mizar-rs` as a frontend for the original mizar `verifier` 171 | #[arg(long)] 172 | orig_mizar: bool, 173 | 174 | /// Exit after processing the first verbose item 175 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = "true", 176 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 177 | one_item: bool, 178 | 179 | /// Exit after processing the first selected file 180 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(DEBUG), 181 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 182 | one_file: bool, 183 | 184 | /// Index of the last file to process, if specified 185 | #[arg(long)] 186 | last_file: Option, 187 | 188 | /// Disable the checker while not in verbose mode 189 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(DEBUG), 190 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 191 | skip_to_verbose: bool, 192 | 193 | #[command(flatten, next_help_heading = "Other options")] 194 | other: CliOther, 195 | 196 | #[command(flatten, next_help_heading = "Debugging tools")] 197 | debug: CliDebug, 198 | 199 | /// Dump the contents of various system components, 200 | /// or `--dump` without arguments to print everything 201 | #[arg(long, value_delimiter = ',', num_args = 0..)] 202 | dump: Option>, 203 | 204 | #[command(flatten, next_help_heading = "Bugs and unsound flags")] 205 | unsound: CliUnsound, 206 | } 207 | 208 | #[derive(Debug, clap::Args)] 209 | struct CliPasses { 210 | /// Enables (only) the checker, checking 'by' proofs straight from .xml 211 | #[arg(short = 'c', long)] 212 | checker: bool, 213 | /// Disables the checker, checking the proof skeleton but not individual by steps 214 | #[arg(short = 'C', long)] 215 | no_checker: bool, 216 | /// Enables (only) the analyzer, checking the proof skeleton but not individual by steps 217 | #[arg(short = 'a', long)] 218 | analyzer: bool, 219 | /// Disables the analyzer 220 | #[arg(short = 'A', long)] 221 | no_analyzer: bool, 222 | /// Enables (only) the exporter, doing the minimal amount of work to produce theorem statements 223 | #[arg(short = 'e', long)] 224 | export: bool, 225 | /// Disables the exporter 226 | #[arg(short = 'E', long)] 227 | no_export: bool, 228 | /// Check that the exported statements exactly match the `miz/mizshare/prel/` directory 229 | #[arg(short = 'v', long)] 230 | verify_export: bool, 231 | /// Produce exported statements to the `miz/prel/` directory (requires `-e`) 232 | #[arg(short = 'x', long)] 233 | xml_export: bool, 234 | /// Produce XML files for internal data structures, in Mizar-compatible format 235 | #[arg(short = 'X', long)] 236 | xml_internals: bool, 237 | /// Test output XML files for well-formedness 238 | #[arg(long)] 239 | xml_internals_self_test: bool, 240 | /// Produce a JSON dump of the parser result 241 | #[arg(long)] 242 | json_parse: bool, 243 | /// Disables the accomodator. (requires `-P`) 244 | #[arg(short = 'M', long)] 245 | no_accom: bool, 246 | /// Disables the parser, reading .wsx files instead of .miz 247 | #[arg(short = 'P', long)] 248 | no_parser: bool, 249 | /// Disables name resolution, reading .msx instead of .wsx (requires `-P`) 250 | #[arg(short = 'N', long)] 251 | no_nameck: bool, 252 | } 253 | 254 | #[derive(Debug, clap::Args)] 255 | struct CliOther { 256 | /// Panic on the first error 257 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(DEBUG), 258 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 259 | panic_on_fail: bool, 260 | 261 | /// Write exported statements to `miz/mizshare/prel/` instead of `miz/prel/`, 262 | /// overwriting the originals 263 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(false), 264 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 265 | overwrite_prel: bool, 266 | /// Always read cross-article theorems from `prel/` instead of from memory 267 | #[arg(long)] 268 | no_cache: bool, 269 | /// Only show the main progress bar 270 | #[arg(long)] 271 | no_multi_progress: bool, 272 | /// Don't show the fancy progress bar 273 | #[arg(long)] 274 | no_progress: bool, 275 | } 276 | 277 | #[derive(Debug, clap::Args)] 278 | struct CliDebug { 279 | /// Print a header at every top level item 280 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(false), 281 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 282 | top_item_header: bool, 283 | /// Print the full AST for each item, even when not in verbose mode 284 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(false), 285 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 286 | always_verbose_item: bool, 287 | /// Print a header at each item 288 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(DEBUG), 289 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 290 | item_header: bool, 291 | /// Print the checker input facts in verbose mode 292 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(DEBUG), 293 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 294 | checker_inputs: bool, 295 | /// Print the checker header in verbose mode 296 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(DEBUG), 297 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 298 | checker_header: bool, 299 | /// Print the processed checker conjuncts in verbose mode 300 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(DEBUG), 301 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 302 | checker_conjuncts: bool, 303 | /// Print the checker result in verbose mode 304 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(DEBUG), 305 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 306 | checker_result: bool, 307 | /// Print the input to the unifier module in verbose mode 308 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(DEBUG), 309 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 310 | unify_header: bool, 311 | /// Print the instantiation produced by the unifier in verbose mode 312 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(DEBUG), 313 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 314 | unify_insts: bool, 315 | } 316 | 317 | #[derive(Debug, clap::Args)] 318 | struct CliUnsound { 319 | /// This is an UNSOUND FLAG that enables checking of `P[a] & ... & P[b]` 320 | /// equality by checking only the endpoints `P[a]` and `P[b]`. 321 | /// This is needed to check some MML proofs 322 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(true), 323 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 324 | legacy_flex_handling: bool, 325 | 326 | /// This is buggy behavior, but not unsound. It is required to interpret some MML files. 327 | #[arg(long, num_args = 0..=1, action = ArgAction::Set, default_value = bool_to_str(true), 328 | require_equals = true, default_missing_value = "true", hide_possible_values = true)] 329 | attr_sort_bug: bool, 330 | } 331 | 332 | // impl Cli { 333 | // fn die(message: impl Display) -> ! { 334 | // Cli::command().error(clap::error::ErrorKind::ArgumentConflict, message).exit() 335 | // } 336 | // } 337 | 338 | macro_rules! mk_dump { 339 | (struct $dump:ident { 340 | $($id:ident,)* 341 | }) => { 342 | #[derive(Clone, Debug, Default)] 343 | pub struct $dump { 344 | $(pub $id: bool,)* 345 | } 346 | #[derive(Clone, Copy, Debug, ValueEnum)] 347 | #[allow(non_camel_case_types)] 348 | enum DumpKind { $($id,)* } 349 | impl From<&Option>> for $dump { 350 | fn from(it: &Option>) -> $dump { 351 | match it { 352 | None => Dump { $($id: false),* }, 353 | Some(it) if it.is_empty() => Dump { $($id: true),* }, 354 | Some(it) => { 355 | let mut out = $dump::default(); 356 | for &k in it { 357 | match k { $(DumpKind::$id => out.$id = true,)* } 358 | } 359 | out 360 | } 361 | } 362 | } 363 | } 364 | } 365 | } 366 | mk_dump! { 367 | struct Dump { 368 | config, 369 | constructors, 370 | requirements, 371 | notations, 372 | clusters, 373 | definitions, 374 | libraries, 375 | formatter, 376 | } 377 | } 378 | 379 | #[derive(Clone, Debug)] 380 | pub struct Config { 381 | pub top_item_header: bool, 382 | pub always_verbose_item: bool, 383 | pub item_header: bool, 384 | pub checker_inputs: bool, 385 | pub checker_header: bool, 386 | pub checker_conjuncts: bool, 387 | pub checker_result: bool, 388 | pub unify_header: bool, 389 | pub unify_insts: bool, 390 | 391 | pub dump: Dump, 392 | 393 | pub accom_enabled: bool, 394 | pub parser_enabled: bool, 395 | pub nameck_enabled: bool, 396 | pub analyzer_enabled: bool, 397 | pub analyzer_full: bool, 398 | pub checker_enabled: bool, 399 | pub exporter_enabled: bool, 400 | pub verify_export: bool, 401 | pub xml_export: bool, 402 | pub xml_internals: bool, 403 | pub xml_internals_self_test: bool, 404 | pub json_parse: bool, 405 | pub overwrite_prel: bool, 406 | pub cache_prel: bool, 407 | 408 | // Unsound flags // 409 | /// This flag enables checking of `P[a] & ... & P[b]` equality by checking 410 | /// only the endpoints `P[a]` and `P[b]`. This is unsound, but needed to 411 | /// check some proofs 412 | pub legacy_flex_handling: bool, 413 | 414 | /// Cluster lists in `Attrs` are supposed to be sorted, but Mizar fails 415 | /// to re-sort after some operations that can change relative sort order, 416 | /// notably instantiation. Unfortunately this is user-visible because of 417 | /// implicit argument inference in ambiguous cases; afinsq_2 needs a bunch 418 | /// of `qua`s and I think there are some cases which are just impossible 419 | /// to specify this way. (This is not unsound.) 420 | pub attr_sort_bug: bool, 421 | 422 | pub panic_on_fail: bool, 423 | pub first_verbose_line: Option, 424 | pub one_item: bool, 425 | pub skip_to_verbose: bool, 426 | } 427 | 428 | const DEBUG: bool = cfg!(debug_assertions); 429 | const GC_THRESHOLD: usize = 5000; 430 | const READ_MAX_LINE_COUNT: bool = true; 431 | 432 | impl FormatterConfig { 433 | const DEFAULT: Self = Self { 434 | enable_formatter: true, 435 | show_infer: false, 436 | show_only_infer: false, 437 | show_priv: false, 438 | show_marks: true, 439 | show_invisible: false, 440 | show_orig: true, 441 | show_var_names: true, 442 | upper_clusters: false, 443 | both_clusters: false, 444 | negation_sugar: true, 445 | }; 446 | } 447 | 448 | fn conflict(msg: impl Display) -> ! { 449 | Cli::command().error(clap::error::ErrorKind::ArgumentConflict, msg).exit() 450 | } 451 | 452 | fn mizfiles() -> &'static str { 453 | static MIZFILES: OnceCell> = OnceCell::new(); 454 | MIZFILES.get_or_init(|| std::env::var("MIZFILES").ok()).as_deref().unwrap_or("miz/mizshare") 455 | } 456 | 457 | fn mizbin() -> &'static str { 458 | static MIZBIN: OnceCell> = OnceCell::new(); 459 | MIZBIN.get_or_init(|| std::env::var("MIZBIN").ok()).as_deref().unwrap_or("miz/mizbin") 460 | } 461 | 462 | fn mml_lar_path() -> String { format!("{}/mml.lar", mizfiles()) } 463 | fn mml_vct_path() -> String { format!("{}/mml.vct", mizfiles()) } 464 | 465 | fn main() { 466 | let cli = Cli::parse(); 467 | let enable = cli.passes.analyzer || cli.passes.checker || cli.passes.export; 468 | let disable = cli.passes.no_analyzer || cli.passes.no_checker || cli.passes.no_export; 469 | if enable && disable { 470 | conflict("can't use positive and negative pass selectors together") 471 | } 472 | let mut cfg = Config { 473 | accom_enabled: !cli.passes.no_accom, 474 | parser_enabled: !cli.passes.no_parser, 475 | nameck_enabled: !cli.passes.no_nameck, 476 | analyzer_enabled: if enable { cli.passes.analyzer } else { !cli.passes.no_analyzer }, 477 | analyzer_full: Default::default(), 478 | checker_enabled: if enable { cli.passes.checker } else { !cli.passes.no_checker }, 479 | exporter_enabled: if enable { cli.passes.export } else { !cli.passes.no_export }, 480 | verify_export: cli.passes.verify_export, 481 | xml_export: cli.passes.xml_export, 482 | xml_internals: cli.passes.xml_internals, 483 | xml_internals_self_test: cli.passes.xml_internals_self_test, 484 | json_parse: cli.passes.json_parse, 485 | overwrite_prel: cli.other.overwrite_prel, 486 | cache_prel: Default::default(), 487 | 488 | top_item_header: cli.debug.top_item_header, 489 | always_verbose_item: cli.debug.always_verbose_item, 490 | item_header: cli.debug.item_header, 491 | checker_inputs: cli.debug.checker_inputs, 492 | checker_header: cli.debug.checker_header, 493 | checker_conjuncts: cli.debug.checker_conjuncts, 494 | checker_result: cli.debug.checker_result, 495 | unify_header: cli.debug.unify_header, 496 | unify_insts: cli.debug.unify_insts, 497 | 498 | dump: (&cli.dump).into(), 499 | 500 | legacy_flex_handling: cli.unsound.legacy_flex_handling, 501 | attr_sort_bug: cli.unsound.attr_sort_bug, 502 | 503 | panic_on_fail: cli.other.panic_on_fail, 504 | first_verbose_line: cli.first_verbose_line, // None, 505 | one_item: cli.one_item, 506 | skip_to_verbose: cli.skip_to_verbose, 507 | }; 508 | 509 | const FIRST_FILE: usize = 0; 510 | const LAST_FILE: Option = None; //Some(11); 511 | 512 | // set_verbose(true); 513 | // let path = MizPath(Article::from_bytes(b"TEST"), "../test/text/test".into()); 514 | // path.with_reader(&cfg, |v| v.run_checker(&path)); 515 | // print_stats_and_exit(cfg.parallelism); 516 | cfg.analyzer_full = cfg.analyzer_enabled; 517 | cfg.accom_enabled |= cfg.parser_enabled; // parser needs accom 518 | cfg.nameck_enabled |= cfg.parser_enabled; // parser needs nameck 519 | cfg.analyzer_full |= cfg.checker_enabled; // checker needs analyzer_full (if analyzer is used) 520 | cfg.cache_prel = !cli.one_file && !cli.other.no_cache; 521 | cfg.exporter_enabled &= cfg.xml_export || cfg.verify_export || cfg.cache_prel; 522 | cfg.analyzer_enabled |= cfg.exporter_enabled; // exporter needs (quick) analyzer 523 | if cfg.cache_prel && cli.dep_order && cfg.verify_export { 524 | conflict("VERIFY_EXPORT and DEP_ORDER + CACHE are incompatible") 525 | } 526 | let one_file = cli.one_file; 527 | 528 | let file = std::fs::read_to_string(mml_lar_path()).unwrap_or_else(|e| { 529 | println!("IO error reading {}: {e}", mml_lar_path()); 530 | std::process::abort() 531 | }); 532 | let mml_vct = &if cfg.accom_enabled { 533 | let mml_vct_path = mml_vct_path(); 534 | std::fs::read(&mml_vct_path).unwrap_or_else(|e| { 535 | println!("IO error reading {mml_vct_path}: {e}"); 536 | std::process::abort() 537 | }) 538 | } else { 539 | vec![] 540 | }; 541 | let mut jobs = file.lines().enumerate().collect_vec(); 542 | let first_file = match cli.file { 543 | None => FIRST_FILE, 544 | Some(n) => n.parse().unwrap_or_else(|_| { 545 | let n = n.to_lowercase(); 546 | jobs.iter().position(|s| s.1 == n).unwrap_or_else(|| { 547 | Cli::command().error(clap::error::ErrorKind::InvalidValue, "article not found").exit() 548 | }) 549 | }), 550 | }; 551 | if cfg.dump.config { 552 | println!("config: {cfg:#?}"); 553 | match jobs.get(first_file) { 554 | Some(&(_, s)) => println!("first_file: {first_file} = {s}"), 555 | None => println!("first_file: {first_file}"), 556 | } 557 | println!("one_file: {one_file}"); 558 | } 559 | if cfg.cache_prel { 560 | cache::init_cache(jobs.iter().map(|&(i, x)| (x, cli.dep_order && i >= first_file))) 561 | } 562 | if let Some(n) = cli.last_file.or(LAST_FILE) { 563 | jobs.truncate(n + 1) 564 | } 565 | drop(jobs.drain(..first_file)); 566 | if one_file { 567 | jobs.truncate(1) 568 | } 569 | if jobs.is_empty() { 570 | println!("nothing to do"); 571 | std::process::exit(0) 572 | } 573 | let parallelism = cli.parallelism.min(jobs.len()); 574 | let progress = &if !cli.other.no_progress { 575 | Progress::new(jobs.len(), cfg.parser_enabled && READ_MAX_LINE_COUNT) 576 | } else { 577 | None 578 | }; 579 | JOBS.get_or_init(|| { 580 | let failures = std::iter::repeat_with(<_>::default).take(jobs.len()).collect(); 581 | (failures, jobs.iter().map(|(i, s)| format!("{i}: {s}")).collect()) 582 | }); 583 | let _ = ctrlc::set_handler(|| print_stats_and_exit(true)); 584 | 585 | let jobs = &Mutex::new(jobs.into_iter().enumerate()); 586 | let running = &*std::iter::repeat_with(|| { 587 | (progress.as_ref().filter(|_| !cli.other.no_multi_progress)) 588 | .map(|p| p.multi.insert(0, ProgressBar::hidden()).with_style(p.style.clone())) 589 | }) 590 | .take(parallelism) 591 | .collect_vec(); 592 | if let Some(p) = progress { 593 | if let Some(m) = &p.main { 594 | m.tick() 595 | } 596 | p.multi.set_move_cursor(true); 597 | } 598 | let cfg = &cfg; 599 | let mut has_errors = AtomicBool::new(false); 600 | std::thread::scope(|s| { 601 | let has_errors = &has_errors; 602 | for thread in running { 603 | s.spawn(move || { 604 | while let Some((job, (i, s))) = { 605 | #[allow(clippy::unwrap_used)] 606 | let mut lock = jobs.lock().unwrap(); 607 | lock.next() 608 | } { 609 | JOB_ID.set(Some(job)); 610 | let path = match MizPath::new(s) { 611 | Ok(t) => t, 612 | Err(e) => { 613 | println!("error: {}:{}: {e}", mml_lar_path(), i + 1); 614 | has_errors.store(true, std::sync::atomic::Ordering::Relaxed); 615 | continue 616 | } 617 | }; 618 | if let Some(thread) = &thread { 619 | thread.set_message(format!("{i:4}: {s}")); 620 | thread.set_length(1); 621 | thread.set_position(0); 622 | thread.reset_elapsed(); 623 | } 624 | let start = std::time::Instant::now(); 625 | let result = std::panic::catch_unwind(|| -> io::Result { 626 | if cli.orig_mizar { 627 | let mizbin = mizbin(); 628 | if cfg.accom_enabled { 629 | let mut cmd = std::process::Command::new(format!("{mizbin}/accom")); 630 | cmd.arg("-lqs"); 631 | let output = cmd.arg(format!("{}.miz", path.mml().display())).output()?; 632 | if !output.status.success() { 633 | eprintln!("\nfile {} failed. Output:", path.art); 634 | std::io::stderr().write_all(&output.stderr)?; 635 | std::io::stdout().write_all(&output.stdout)?; 636 | std::io::stdout().flush()?; 637 | stat("fail", true); 638 | if cfg.panic_on_fail { 639 | std::process::abort() 640 | } 641 | } 642 | } 643 | if cfg.analyzer_full || cfg.checker_enabled { 644 | let mut cmd = std::process::Command::new(format!("{mizbin}/verifier")); 645 | let cmd = match (cfg.analyzer_full, cfg.checker_enabled) { 646 | (true, false) => cmd.arg("-a"), 647 | (false, true) => cmd.arg("-c"), 648 | (true, true) => &mut cmd, 649 | (false, false) => unreachable!(), 650 | }; 651 | let output = cmd.arg(format!("{}.miz", path.mml().display())).output()?; 652 | if !output.status.success() { 653 | eprintln!("\nfile {} failed. Output:", path.art); 654 | std::io::stderr().write_all(&output.stderr)?; 655 | std::io::stdout().write_all(&output.stdout)?; 656 | std::io::stdout().flush()?; 657 | stat("fail", true); 658 | if cfg.panic_on_fail { 659 | std::process::abort() 660 | } 661 | } 662 | // println!("{}", String::from_utf8(output.stdout)?); 663 | } 664 | Ok(false) 665 | } else if cfg.parser_enabled || cfg.analyzer_enabled { 666 | path.with_reader(cfg, thread.as_ref(), mml_vct, &mut |v, p| v.run_analyzer(&path, p)) 667 | } else if cfg.checker_enabled { 668 | path.with_reader(cfg, thread.as_ref(), mml_vct, &mut |v, _| v.run_checker(&path)) 669 | } else { 670 | Ok(false) 671 | } 672 | }); 673 | match result { 674 | Ok(Ok(err)) => 675 | if err { 676 | has_errors.store(true, std::sync::atomic::Ordering::Relaxed) 677 | }, 678 | Ok(Err(err)) => { 679 | println!("error: {i}: {s} IO error: {err}"); 680 | stat("panic", true); 681 | if cfg.panic_on_fail { 682 | std::process::abort() 683 | } 684 | } 685 | Err(_payload) => { 686 | println!("error: {i}: {s} panicked"); 687 | stat("panic", true); 688 | if cfg.panic_on_fail { 689 | std::process::abort() 690 | } 691 | } 692 | } 693 | if let Some(p) = progress.as_ref().filter(|p| !p.multi.is_hidden()) { 694 | let msg = format!("{i:4}: {s:8} in {:.3}s", start.elapsed().as_secs_f32()); 695 | let _ = p.multi.println(msg); 696 | } else { 697 | println!("{i:4}: {s:8} in {:.3}s", start.elapsed().as_secs_f32()) 698 | } 699 | if let Some(thread) = &thread { 700 | if let Some(len) = thread.length() { 701 | thread.set_position(len); 702 | } 703 | } 704 | if let Some(main) = progress.as_ref().and_then(|p| p.main.as_ref()) { 705 | main.inc(1) 706 | } 707 | if one_file || LAST_FILE == Some(i) { 708 | break 709 | } 710 | } 711 | if let Some(p) = progress { 712 | p.multi.set_move_cursor(false); 713 | } 714 | if let Some(thread) = thread { 715 | thread.finish_and_clear(); 716 | } 717 | }); 718 | } 719 | }); 720 | if let Some(p) = progress { 721 | drop(p.multi.clear()); 722 | } 723 | // std::thread::sleep(std::time::Duration::from_secs(60 * 60)); 724 | print_stats_and_exit(*has_errors.get_mut()); 725 | } 726 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | pub trait RetainMutFrom { 2 | /// Retains only the elements specified by the predicate, passing a mutable reference to it. 3 | /// The first `start` elements are skipped (`f` is not called) and always retained. 4 | /// 5 | /// In other words, remove all elements `e` such that `f(&mut e)` returns `false`. 6 | /// This method operates in place, visiting each element above `start` exactly once in the 7 | /// original order, and preserves the order of the retained elements. 8 | fn retain_mut_from(&mut self, start: usize, f: F) 9 | where F: FnMut(&mut T) -> bool; 10 | } 11 | 12 | impl RetainMutFrom for Vec { 13 | fn retain_mut_from(&mut self, start: usize, mut f: F) 14 | where F: FnMut(&mut T) -> bool { 15 | let original_len = self.len(); 16 | // Avoid double drop if the drop guard is not executed, 17 | // since we may make some holes during the process. 18 | unsafe { self.set_len(start) }; 19 | 20 | // Vec: [Kept, Kept, Hole, Hole, Hole, Hole, Unchecked, Unchecked] 21 | // |<- processed len ->| ^- next to check 22 | // |<- deleted cnt ->| 23 | // |<- original_len ->| 24 | // Kept: Elements which predicate returns true on. 25 | // Hole: Moved or dropped element slot. 26 | // Unchecked: Unchecked valid elements. 27 | // 28 | // This drop guard will be invoked when predicate or `drop` of element panicked. 29 | // It shifts unchecked elements to cover holes and `set_len` to the correct length. 30 | // In cases when predicate and `drop` never panick, it will be optimized out. 31 | struct BackshiftOnDrop<'a, T> { 32 | v: &'a mut Vec, 33 | processed_len: usize, 34 | deleted_cnt: usize, 35 | original_len: usize, 36 | } 37 | 38 | impl Drop for BackshiftOnDrop<'_, T> { 39 | fn drop(&mut self) { 40 | if self.deleted_cnt > 0 { 41 | // SAFETY: Trailing unchecked items must be valid since we never touch them. 42 | unsafe { 43 | std::ptr::copy( 44 | self.v.as_ptr().add(self.processed_len), 45 | self.v.as_mut_ptr().add(self.processed_len - self.deleted_cnt), 46 | self.original_len - self.processed_len, 47 | ); 48 | } 49 | } 50 | // SAFETY: After filling holes, all items are in contiguous memory. 51 | unsafe { 52 | self.v.set_len(self.original_len - self.deleted_cnt); 53 | } 54 | } 55 | } 56 | 57 | let mut g = BackshiftOnDrop { v: self, processed_len: start, deleted_cnt: 0, original_len }; 58 | 59 | fn process_loop( 60 | original_len: usize, f: &mut F, g: &mut BackshiftOnDrop<'_, T>, 61 | ) where F: FnMut(&mut T) -> bool { 62 | while g.processed_len != original_len { 63 | // SAFETY: Unchecked element must be valid. 64 | let cur = unsafe { &mut *g.v.as_mut_ptr().add(g.processed_len) }; 65 | if !f(cur) { 66 | // Advance early to avoid double drop if `drop_in_place` panicked. 67 | g.processed_len += 1; 68 | g.deleted_cnt += 1; 69 | // SAFETY: We never touch this element again after dropped. 70 | unsafe { std::ptr::drop_in_place(cur) }; 71 | // We already advanced the counter. 72 | if DELETED { 73 | continue 74 | } else { 75 | break 76 | } 77 | } 78 | if DELETED { 79 | // SAFETY: `deleted_cnt` > 0, so the hole slot must not overlap with current element. 80 | // We use copy for move, and never touch this element again. 81 | unsafe { 82 | let hole_slot = g.v.as_mut_ptr().add(g.processed_len - g.deleted_cnt); 83 | std::ptr::copy_nonoverlapping(cur, hole_slot, 1); 84 | } 85 | } 86 | g.processed_len += 1; 87 | } 88 | } 89 | 90 | // Stage 1: Nothing was deleted. 91 | process_loop::(original_len, &mut f, &mut g); 92 | 93 | // Stage 2: Some elements were deleted. 94 | process_loop::(original_len, &mut f, &mut g); 95 | 96 | // All item are processed. This can be optimized to `set_len` by LLVM. 97 | drop(g); 98 | } 99 | } 100 | --------------------------------------------------------------------------------