mirror of
https://github.com/ParkerTenBroeck/automata.git
synced 2026-06-06 21:24:06 -04:00
changed how machines are parsed/represented
This commit is contained in:
parent
132380e777
commit
34b20ec1fe
23 changed files with 1577 additions and 206 deletions
403
Cargo.lock
generated
403
Cargo.lock
generated
|
|
@ -2,9 +2,28 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "automata"
|
name = "automata"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_with",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "automata-web"
|
name = "automata-web"
|
||||||
|
|
@ -18,18 +37,46 @@ dependencies = [
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.19.1"
|
version = "3.19.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.52"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
|
||||||
|
dependencies = [
|
||||||
|
"find-msvc-tools",
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
||||||
|
dependencies = [
|
||||||
|
"iana-time-zone",
|
||||||
|
"num-traits",
|
||||||
|
"serde",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "console_error_panic_hook"
|
name = "console_error_panic_hook"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
|
@ -40,6 +87,152 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deranged"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
|
||||||
|
dependencies = [
|
||||||
|
"powerfmt",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dyn-clone"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find-msvc-tools"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown 0.12.3",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown 0.16.1",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
|
|
@ -56,18 +249,51 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.180"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.6"
|
version = "2.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-conv"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.3"
|
version = "1.21.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "powerfmt"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.103"
|
version = "1.0.103"
|
||||||
|
|
@ -86,12 +312,56 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ref-cast"
|
||||||
|
version = "1.0.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
|
||||||
|
dependencies = [
|
||||||
|
"ref-cast-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ref-cast-impl"
|
||||||
|
version = "1.0.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.22"
|
version = "1.0.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schemars"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f"
|
||||||
|
dependencies = [
|
||||||
|
"dyn-clone",
|
||||||
|
"ref-cast",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schemars"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2"
|
||||||
|
dependencies = [
|
||||||
|
"dyn-clone",
|
||||||
|
"ref-cast",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.228"
|
version = "1.0.228"
|
||||||
|
|
@ -135,6 +405,49 @@ dependencies = [
|
||||||
"zmij",
|
"zmij",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with"
|
||||||
|
version = "3.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"chrono",
|
||||||
|
"hex",
|
||||||
|
"indexmap 1.9.3",
|
||||||
|
"indexmap 2.13.0",
|
||||||
|
"schemars 0.9.0",
|
||||||
|
"schemars 1.2.0",
|
||||||
|
"serde_core",
|
||||||
|
"serde_json",
|
||||||
|
"serde_with_macros",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with_macros"
|
||||||
|
version = "3.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.111"
|
version = "2.0.111"
|
||||||
|
|
@ -146,6 +459,37 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
|
||||||
|
dependencies = [
|
||||||
|
"deranged",
|
||||||
|
"itoa",
|
||||||
|
"num-conv",
|
||||||
|
"powerfmt",
|
||||||
|
"serde",
|
||||||
|
"time-core",
|
||||||
|
"time-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-core"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-macros"
|
||||||
|
version = "0.2.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
|
||||||
|
dependencies = [
|
||||||
|
"num-conv",
|
||||||
|
"time-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.22"
|
version = "1.0.22"
|
||||||
|
|
@ -207,6 +551,65 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.62.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.59.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,13 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
serde = { version = "1.0", features = ["derive"], optional = true}
|
||||||
|
serde_with = { version = "3.0", features = ["default"], optional = true}
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
serde = ["dep:serde", "dep:serde_with"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["web"]
|
members = ["web"]
|
||||||
|
|
||||||
|
|
|
||||||
278
src/automatan/fa.rs
Normal file
278
src/automatan/fa.rs
Normal file
|
|
@ -0,0 +1,278 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::loader::{
|
||||||
|
Context, DELTA_LOWER, GAMMA_UPPER, SIGMA_UPPER, Spanned,
|
||||||
|
ast::{self, Symbol as Sym},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub struct TransitionFrom<'a> {
|
||||||
|
pub state: State<'a>,
|
||||||
|
pub letter: Option<Letter<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub struct TransitionTo<'a> {
|
||||||
|
pub state: State<'a>,
|
||||||
|
|
||||||
|
pub transition: Span,
|
||||||
|
pub function: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[allow(unused)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "serde", serde_with::serde_as)]
|
||||||
|
pub struct Fa<'a> {
|
||||||
|
pub initial_state: State<'a>,
|
||||||
|
pub states: HashMap<State<'a>, StateInfo>,
|
||||||
|
pub alphabet: HashMap<Letter<'a>, LetterInfo>,
|
||||||
|
pub final_states: HashMap<State<'a>, StateInfo>,
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
#[serde_as(as = "serde_with::Seq<(_, _)>")]
|
||||||
|
pub transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>>,
|
||||||
|
#[cfg(not(feature = "serde"))]
|
||||||
|
pub transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Fa<'a> {
|
||||||
|
pub fn parse(
|
||||||
|
items: impl Iterator<Item = Spanned<ast::TopLevel<'a>>>,
|
||||||
|
ctx: &mut Context<'a>,
|
||||||
|
options: Options,
|
||||||
|
) -> Option<Fa<'a>> {
|
||||||
|
|
||||||
|
let mut initial_state = None;
|
||||||
|
|
||||||
|
let mut states = HashMap::new();
|
||||||
|
let mut alphabet = HashMap::new();
|
||||||
|
let mut final_states = HashMap::new();
|
||||||
|
|
||||||
|
let mut transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>> =
|
||||||
|
HashMap::new();
|
||||||
|
|
||||||
|
for Spanned(element, span) in items {
|
||||||
|
use Spanned as S;
|
||||||
|
use ast::TopLevel as TL;
|
||||||
|
match element {
|
||||||
|
TL::Item(S("Q", _), list) => {
|
||||||
|
if !states.is_empty() {
|
||||||
|
ctx.emit_error("states already set", span);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if states
|
||||||
|
.insert(State(ident), StateInfo { definition: item.1 })
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
ctx.emit_error("state redefined", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.is_empty() {
|
||||||
|
ctx.emit_error("states cannot be empty", span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TL::Item(S("E" | SIGMA_UPPER | "sigma", _), list) => {
|
||||||
|
if !alphabet.is_empty() {
|
||||||
|
ctx.emit_error("alphabet already set", span);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ident.chars().count() != 1 {
|
||||||
|
ctx.emit_error("letter cannot be longer than one char", item.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if alphabet
|
||||||
|
.insert(Letter(ident), LetterInfo { definition: item.1 })
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
ctx.emit_error("letter redefined", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if list.is_empty() {
|
||||||
|
ctx.emit_error("alphabet cannot be empty", span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TL::Item(S("F", _), list) => {
|
||||||
|
if final_states.is_empty() {
|
||||||
|
ctx.emit_error("final states already set", span);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if states.contains_key(&State(ident)) {
|
||||||
|
if final_states
|
||||||
|
.insert(State(ident), StateInfo { definition: item.1 })
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
ctx.emit_error("final state redefined", item.1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.emit_error("final state not defined in set of states", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TL::Item(S("I" | "q0", _), S(src, src_d)) => match src {
|
||||||
|
ast::Item::Symbol(Sym::Ident(ident)) => {
|
||||||
|
if initial_state.is_some() {
|
||||||
|
ctx.emit_error("initial state already set", span);
|
||||||
|
}
|
||||||
|
if states.contains_key(&State(ident)) {
|
||||||
|
initial_state = Some(State(ident))
|
||||||
|
} else {
|
||||||
|
ctx.emit_error("initial state symbol not defined as a state", src_d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ctx.emit_error("expected ident", src_d),
|
||||||
|
},
|
||||||
|
TL::Item(S(name, dest_s), _) => {
|
||||||
|
ctx.emit_error(format!("unknown item {name:?}, expected 'Q' | 'E' | '{SIGMA_UPPER}' | 'sigma' | 'F' | 'T' | '{GAMMA_UPPER}' | 'gamma' | 'I' | 'q0' | 'S' | 'z0'"), dest_s);
|
||||||
|
}
|
||||||
|
|
||||||
|
TL::TransitionFunc(S((S("d" | DELTA_LOWER | "delta", _), tuple), _), list) => {
|
||||||
|
let list = list.set_weak();
|
||||||
|
let Some((state, letter)) = tuple.as_ref().expect_fa_transition_function(ctx)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !states.contains_key(&State(state.0)) {
|
||||||
|
ctx.emit_error("transition state not defined as state", state.1);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let letter: Option<Letter<'_>> = match letter.0 {
|
||||||
|
Sym::Epsilon => {
|
||||||
|
if !options.epsilon_moves {
|
||||||
|
ctx.emit_error("epsilon moves not permitted", letter.1);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Sym::Ident(val) => {
|
||||||
|
if !alphabet.contains_key(&Letter(val)) {
|
||||||
|
ctx.emit_error(
|
||||||
|
"transition letter not defined in alphabet",
|
||||||
|
letter.1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(Letter(val))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for item in list {
|
||||||
|
let Some(next_state) = item.expect_ident(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let next_state = Spanned(next_state, item.1);
|
||||||
|
|
||||||
|
if !states.contains_key(&State(next_state.0)) {
|
||||||
|
ctx.emit_error("transition state not defined as state", next_state.1);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let entry: &mut _ = transitions
|
||||||
|
.entry(TransitionFrom {
|
||||||
|
letter,
|
||||||
|
state: State(state.0),
|
||||||
|
})
|
||||||
|
.or_default();
|
||||||
|
if !entry.is_empty() && !options.non_deterministic {
|
||||||
|
ctx.emit_error("transition already defined for this starting point (non determinism not permitted)", item.1);
|
||||||
|
}
|
||||||
|
if !entry.insert(TransitionTo {
|
||||||
|
state: State(next_state.0),
|
||||||
|
|
||||||
|
function: tuple.1,
|
||||||
|
transition: item.1,
|
||||||
|
}) {
|
||||||
|
ctx.emit_warning("duplicate transition", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TL::TransitionFunc(S((S(name, _), _), dest_s), _) => {
|
||||||
|
ctx.emit_error(
|
||||||
|
format!(
|
||||||
|
"unknown function {name:?}, expected 'd' | 'delta' | '{DELTA_LOWER}'"
|
||||||
|
),
|
||||||
|
dest_s,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TL::ProductionRule(_, _) => {
|
||||||
|
ctx.emit_error("unexpected production rule", span);
|
||||||
|
}
|
||||||
|
TL::Table() => ctx.emit_error("unexpected table", span),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if alphabet.is_empty() {
|
||||||
|
ctx.emit_error_locless("alphabet never defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
if states.is_empty() {
|
||||||
|
ctx.emit_error_locless("states never defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
let initial_state = match initial_state {
|
||||||
|
Some(some) => some,
|
||||||
|
None => {
|
||||||
|
if states.contains_key(&State("q0")) {
|
||||||
|
ctx.emit_warning_locless("initial state not defined, defaulting to 'q0'");
|
||||||
|
} else {
|
||||||
|
ctx.emit_error_locless("initial state not defined");
|
||||||
|
}
|
||||||
|
State("q0")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if ctx.contains_errors() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Fa {
|
||||||
|
initial_state,
|
||||||
|
states,
|
||||||
|
alphabet,
|
||||||
|
final_states,
|
||||||
|
transitions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Spanned<&ast::Tuple<'a>> {
|
||||||
|
fn expect_fa_transition_function(
|
||||||
|
&self,
|
||||||
|
ctx: &mut Context<'a>,
|
||||||
|
) -> Option<(Spanned<&'a str>, Spanned<ast::Symbol<'a>>)> {
|
||||||
|
match &self.0.0[..] {
|
||||||
|
[
|
||||||
|
Spanned(ast::Item::Symbol(ast::Symbol::Ident(state)), state_span),
|
||||||
|
Spanned(ast::Item::Symbol(letter), letter_span),
|
||||||
|
] => {
|
||||||
|
return Some((Spanned(state, *state_span), Spanned(*letter, *letter_span)));
|
||||||
|
}
|
||||||
|
_ => ctx.emit_error("expected FA transition function (ident, ident|~)", self.1),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/automatan/mod.rs
Normal file
43
src/automatan/mod.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::loader::Span;
|
||||||
|
|
||||||
|
pub mod fa;
|
||||||
|
pub mod pda;
|
||||||
|
pub mod tm;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Options {
|
||||||
|
pub non_deterministic: bool,
|
||||||
|
pub epsilon_moves: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
|
||||||
|
pub struct State<'a>(pub &'a str);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
|
||||||
|
pub struct Symbol<'a>(pub &'a str);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
|
||||||
|
pub struct Letter<'a>(pub &'a str);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub struct StateInfo {
|
||||||
|
pub definition: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub struct SymbolInfo {
|
||||||
|
pub definition: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub struct LetterInfo {
|
||||||
|
pub definition: Span,
|
||||||
|
}
|
||||||
399
src/automatan/pda.rs
Normal file
399
src/automatan/pda.rs
Normal file
|
|
@ -0,0 +1,399 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::loader::{
|
||||||
|
Context, DELTA_LOWER, GAMMA_UPPER, SIGMA_UPPER, Spanned,
|
||||||
|
ast::{self, Symbol as Sym},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub struct TransitionFrom<'a> {
|
||||||
|
pub state: State<'a>,
|
||||||
|
pub letter: Option<Letter<'a>>,
|
||||||
|
pub symbol: Symbol<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub struct TransitionTo<'a> {
|
||||||
|
pub state: State<'a>,
|
||||||
|
pub stack: Vec<Symbol<'a>>,
|
||||||
|
|
||||||
|
pub transition: Span,
|
||||||
|
pub function: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[allow(unused)]
|
||||||
|
#[cfg_attr(feature = "serde", serde_with::serde_as)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub struct Pda<'a> {
|
||||||
|
pub initial_state: State<'a>,
|
||||||
|
pub initial_stack: Symbol<'a>,
|
||||||
|
pub states: HashMap<State<'a>, StateInfo>,
|
||||||
|
pub symbols: HashMap<Symbol<'a>, SymbolInfo>,
|
||||||
|
pub alphabet: HashMap<Letter<'a>, LetterInfo>,
|
||||||
|
|
||||||
|
pub final_states: Option<HashMap<State<'a>, StateInfo>>,
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
#[serde_as(as = "serde_with::Seq<(_, _)>")]
|
||||||
|
pub transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>>,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "serde"))]
|
||||||
|
pub transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Pda<'a> {
|
||||||
|
pub fn parse(
|
||||||
|
items: impl Iterator<Item = Spanned<ast::TopLevel<'a>>>,
|
||||||
|
ctx: &mut Context<'a>,
|
||||||
|
options: Options,
|
||||||
|
) -> Option<Pda<'a>> {
|
||||||
|
let mut initial_state = None;
|
||||||
|
let mut initial_stack = None;
|
||||||
|
|
||||||
|
let mut states = HashMap::new();
|
||||||
|
let mut symbols = HashMap::new();
|
||||||
|
let mut alphabet = HashMap::new();
|
||||||
|
let mut final_states = None;
|
||||||
|
|
||||||
|
let mut transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>> =
|
||||||
|
HashMap::new();
|
||||||
|
|
||||||
|
for Spanned(element, span) in items {
|
||||||
|
use Spanned as S;
|
||||||
|
use ast::TopLevel as TL;
|
||||||
|
match element {
|
||||||
|
TL::Item(S("Q", _), list) => {
|
||||||
|
if !states.is_empty() {
|
||||||
|
ctx.emit_error("states already set", span);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if states
|
||||||
|
.insert(State(ident), StateInfo { definition: item.1 })
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
ctx.emit_error("state redefined", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.is_empty() {
|
||||||
|
ctx.emit_error("states cannot be empty", span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TL::Item(S("E" | SIGMA_UPPER | "sigma", _), list) => {
|
||||||
|
if !alphabet.is_empty() {
|
||||||
|
ctx.emit_error("alphabet already set", span);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ident.chars().count() != 1 {
|
||||||
|
ctx.emit_error("letter cannot be longer than one char", item.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if alphabet
|
||||||
|
.insert(Letter(ident), LetterInfo { definition: item.1 })
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
ctx.emit_error("letter redefined", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if list.is_empty() {
|
||||||
|
ctx.emit_error("alphabet cannot be empty", span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TL::Item(S("F", _), list) => {
|
||||||
|
if final_states.is_some() {
|
||||||
|
ctx.emit_error("final states already set", span);
|
||||||
|
}
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
let Some(list) = list.expect_set(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if states.contains_key(&State(ident)) {
|
||||||
|
if map
|
||||||
|
.insert(State(ident), StateInfo { definition: item.1 })
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
ctx.emit_error("final state redefined", item.1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.emit_error("final state not defined in set of states", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final_states = Some(map);
|
||||||
|
}
|
||||||
|
TL::Item(S("T" | GAMMA_UPPER | "gamma", _), list) => {
|
||||||
|
if !symbols.is_empty() {
|
||||||
|
ctx.emit_error("stack symbols already set", span);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if symbols
|
||||||
|
.insert(Symbol(ident), SymbolInfo { definition: item.1 })
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
ctx.emit_error("stack symbol redefined", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.is_empty() {
|
||||||
|
ctx.emit_error("stack symbols cannot be empty", span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TL::Item(S("I" | "q0", _), S(src, src_d)) => match src {
|
||||||
|
ast::Item::Symbol(Sym::Ident(ident)) => {
|
||||||
|
if initial_state.is_some() {
|
||||||
|
ctx.emit_error("initial state already set", span);
|
||||||
|
}
|
||||||
|
if states.contains_key(&State(ident)) {
|
||||||
|
initial_state = Some(State(ident))
|
||||||
|
} else {
|
||||||
|
ctx.emit_error("initial state symbol not defined as a state", src_d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ctx.emit_error("expected ident", src_d),
|
||||||
|
},
|
||||||
|
TL::Item(S("S" | "z0", _), S(src, src_d)) => match src {
|
||||||
|
ast::Item::Symbol(Sym::Ident(ident)) => {
|
||||||
|
if initial_stack.is_some() {
|
||||||
|
ctx.emit_error("initial stack already set", span);
|
||||||
|
}
|
||||||
|
if symbols.contains_key(&Symbol(ident)) {
|
||||||
|
initial_stack = Some(Symbol(ident));
|
||||||
|
} else {
|
||||||
|
ctx.emit_error(
|
||||||
|
"initial stack symbol not defined as a stack symbol",
|
||||||
|
src_d,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ctx.emit_error("expected ident", src_d),
|
||||||
|
},
|
||||||
|
TL::Item(S(name, dest_s), _) => {
|
||||||
|
ctx.emit_error(format!("unknown item {name:?}, expected 'Q' | 'E' | '{SIGMA_UPPER}' | 'sigma' | 'F' | 'T' | '{GAMMA_UPPER}' | 'gamma' | 'I' | 'q0' | 'S' | 'z0'"), dest_s);
|
||||||
|
}
|
||||||
|
|
||||||
|
TL::TransitionFunc(S((S("d" | DELTA_LOWER | "delta", _), tuple), _), list) => {
|
||||||
|
let list = list.set_weak();
|
||||||
|
let Some((state, letter, stack_symbol)) =
|
||||||
|
tuple.as_ref().expect_pda_transition_function(ctx)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !states.contains_key(&State(state.0)) {
|
||||||
|
ctx.emit_error("transition state not defined as state", state.1);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !symbols.contains_key(&Symbol(stack_symbol.0)) {
|
||||||
|
ctx.emit_error(
|
||||||
|
"transition stack symbol not defined as stack symbol",
|
||||||
|
stack_symbol.1,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let letter: Option<Letter<'_>> = match letter.0 {
|
||||||
|
Sym::Epsilon => {
|
||||||
|
if !options.epsilon_moves {
|
||||||
|
ctx.emit_error("epsilon moves not permitted", letter.1);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Sym::Ident(val) => {
|
||||||
|
if !alphabet.contains_key(&Letter(val)) {
|
||||||
|
ctx.emit_error(
|
||||||
|
"transition letter not defined in alphabet",
|
||||||
|
letter.1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(Letter(val))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for item in list {
|
||||||
|
let Some((next_state, stack)) = item
|
||||||
|
.expect_tuple(ctx)
|
||||||
|
.and_then(|item| item.expect_pda_transition(ctx))
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if !states.contains_key(&State(next_state.0)) {
|
||||||
|
ctx.emit_error("transition state not defined as state", next_state.1);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let stack: Vec<_> = stack
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.filter_map(|symbol| {
|
||||||
|
if matches!(symbol.0, ast::Item::Symbol(Sym::Epsilon)) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let ident = symbol.expect_ident(ctx)?;
|
||||||
|
|
||||||
|
if !symbols.contains_key(&Symbol(ident)) {
|
||||||
|
ctx.emit_error("transition stack symbol not defined", symbol.1);
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some(Symbol(ident))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let entry: &mut _ = transitions
|
||||||
|
.entry(TransitionFrom {
|
||||||
|
letter,
|
||||||
|
state: State(state.0),
|
||||||
|
symbol: Symbol(stack_symbol.0),
|
||||||
|
})
|
||||||
|
.or_default();
|
||||||
|
if !entry.is_empty() && !options.non_deterministic {
|
||||||
|
ctx.emit_error("transition already defined for this starting point (non determinism not permitted)", item.1);
|
||||||
|
}
|
||||||
|
if !entry.insert(TransitionTo {
|
||||||
|
state: State(next_state.0),
|
||||||
|
stack,
|
||||||
|
|
||||||
|
function: tuple.1,
|
||||||
|
transition: item.1,
|
||||||
|
}) {
|
||||||
|
ctx.emit_warning("duplicate transition", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TL::TransitionFunc(S((S(name, _), _), dest_s), _) => {
|
||||||
|
ctx.emit_error(
|
||||||
|
format!(
|
||||||
|
"unknown function {name:?}, expected 'd' | 'delta' | '{DELTA_LOWER}'"
|
||||||
|
),
|
||||||
|
dest_s,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TL::ProductionRule(_, _) => {
|
||||||
|
ctx.emit_error("unexpected production rule", span);
|
||||||
|
}
|
||||||
|
TL::Table() => ctx.emit_error("unexpected table", span),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if symbols.is_empty() {
|
||||||
|
ctx.emit_error_locless("stack symbols never defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
if alphabet.is_empty() {
|
||||||
|
ctx.emit_error_locless("alphabet never defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
if states.is_empty() {
|
||||||
|
ctx.emit_error_locless("states never defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
let initial_stack = match initial_stack {
|
||||||
|
Some(some) => some,
|
||||||
|
None => {
|
||||||
|
if symbols.contains_key(&Symbol("z0")) {
|
||||||
|
ctx.emit_warning_locless(
|
||||||
|
"initial stack symbol not defined, defaulting to 'z0'",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ctx.emit_error_locless("initial stack symbol not defined");
|
||||||
|
}
|
||||||
|
Symbol("z0")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let initial_state = match initial_state {
|
||||||
|
Some(some) => some,
|
||||||
|
None => {
|
||||||
|
if states.contains_key(&State("q0")) {
|
||||||
|
ctx.emit_warning_locless("initial state not defined, defaulting to 'q0'");
|
||||||
|
} else {
|
||||||
|
ctx.emit_error_locless("initial state not defined");
|
||||||
|
}
|
||||||
|
State("q0")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if ctx.contains_errors() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Pda {
|
||||||
|
initial_state,
|
||||||
|
initial_stack,
|
||||||
|
states,
|
||||||
|
symbols,
|
||||||
|
alphabet,
|
||||||
|
final_states,
|
||||||
|
transitions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> Spanned<&'b ast::Tuple<'a>> {
|
||||||
|
fn expect_pda_transition_function(
|
||||||
|
&self,
|
||||||
|
ctx: &mut Context<'a>,
|
||||||
|
) -> Option<(Spanned<&'a str>, Spanned<ast::Symbol<'a>>, Spanned<&'a str>)> {
|
||||||
|
match &self.0.0[..] {
|
||||||
|
[
|
||||||
|
Spanned(ast::Item::Symbol(ast::Symbol::Ident(state)), state_span),
|
||||||
|
Spanned(ast::Item::Symbol(letter), letter_span),
|
||||||
|
Spanned(ast::Item::Symbol(ast::Symbol::Ident(symbol)), symbol_span),
|
||||||
|
] => {
|
||||||
|
return Some((
|
||||||
|
Spanned(state, *state_span),
|
||||||
|
Spanned(*letter, *letter_span),
|
||||||
|
Spanned(symbol, *symbol_span),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => ctx.emit_error(
|
||||||
|
"expected PDA transition function (ident, ident|~, ident)",
|
||||||
|
self.1,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn expect_pda_transition(
|
||||||
|
&self,
|
||||||
|
ctx: &mut Context<'a>,
|
||||||
|
) -> Option<(Spanned<&'a str>, &'b [Spanned<ast::Item<'a>>])> {
|
||||||
|
match &self.0.0[..] {
|
||||||
|
[
|
||||||
|
Spanned(ast::Item::Symbol(ast::Symbol::Ident(state)), state_span),
|
||||||
|
list,
|
||||||
|
] => {
|
||||||
|
return Some((Spanned(state, *state_span), list.list_weak()));
|
||||||
|
}
|
||||||
|
_ => ctx.emit_error("expected PDA transition (ident, item|[item])", self.1),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
346
src/automatan/tm.rs
Normal file
346
src/automatan/tm.rs
Normal file
|
|
@ -0,0 +1,346 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::loader::{
|
||||||
|
Context, DELTA_LOWER, GAMMA_UPPER, SIGMA_UPPER, Spanned,
|
||||||
|
ast::{self, Symbol as Sym},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub struct TransitionFrom<'a> {
|
||||||
|
pub state: State<'a>,
|
||||||
|
pub symbol: Symbol<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub enum Direction {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub struct TransitionTo<'a> {
|
||||||
|
pub state: State<'a>,
|
||||||
|
pub symbol: Symbol<'a>,
|
||||||
|
pub direction: Direction,
|
||||||
|
|
||||||
|
pub transition: Span,
|
||||||
|
pub function: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[allow(unused)]
|
||||||
|
#[cfg_attr(feature = "serde", serde_with::serde_as)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub struct Tm<'a> {
|
||||||
|
pub initial_state: State<'a>,
|
||||||
|
pub initial_tape: Symbol<'a>,
|
||||||
|
pub states: HashMap<State<'a>, StateInfo>,
|
||||||
|
pub symbols: HashMap<Symbol<'a>, SymbolInfo>,
|
||||||
|
|
||||||
|
pub final_states: HashMap<State<'a>, StateInfo>,
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
#[serde_as(as = "serde_with::Seq<(_, _)>")]
|
||||||
|
pub transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>>,
|
||||||
|
#[cfg(not(feature = "serde"))]
|
||||||
|
pub transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Tm<'a> {
|
||||||
|
pub fn parse(
|
||||||
|
items: impl Iterator<Item = Spanned<ast::TopLevel<'a>>>,
|
||||||
|
ctx: &mut Context<'a>,
|
||||||
|
options: Options,
|
||||||
|
) -> Option<Tm<'a>> {
|
||||||
|
let mut initial_state = None;
|
||||||
|
let mut initial_tape = None;
|
||||||
|
|
||||||
|
let mut states = HashMap::new();
|
||||||
|
let mut symbols = HashMap::new();
|
||||||
|
let mut final_states = HashMap::new();
|
||||||
|
|
||||||
|
let mut transitions: HashMap<TransitionFrom<'a>, HashSet<TransitionTo<'a>>> =
|
||||||
|
HashMap::new();
|
||||||
|
|
||||||
|
for Spanned(element, span) in items {
|
||||||
|
use Spanned as S;
|
||||||
|
use ast::TopLevel as TL;
|
||||||
|
match element {
|
||||||
|
TL::Item(S("Q", _), list) => {
|
||||||
|
if !states.is_empty() {
|
||||||
|
ctx.emit_error("states already set", span);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if states
|
||||||
|
.insert(State(ident), StateInfo { definition: item.1 })
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
ctx.emit_error("state redefined", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.is_empty() {
|
||||||
|
ctx.emit_error("states cannot be empty", span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TL::Item(S("F", _), list) => {
|
||||||
|
if !final_states.is_empty() {
|
||||||
|
ctx.emit_error("final states already set", span);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if states.contains_key(&State(ident)) {
|
||||||
|
if final_states
|
||||||
|
.insert(State(ident), StateInfo { definition: item.1 })
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
ctx.emit_error("final state redefined", item.1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.emit_error("final state not defined in set of states", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TL::Item(S("T" | GAMMA_UPPER | "gamma", _), list) => {
|
||||||
|
if !symbols.is_empty() {
|
||||||
|
ctx.emit_error("tape symbols already set", span);
|
||||||
|
}
|
||||||
|
let Some(list) = list.expect_set(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for item in list {
|
||||||
|
let Some(ident) = item.expect_ident(ctx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if symbols
|
||||||
|
.insert(Symbol(ident), SymbolInfo { definition: item.1 })
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
ctx.emit_error("tape symbol redefined", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.is_empty() {
|
||||||
|
ctx.emit_error("tape symbols cannot be empty", span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TL::Item(S("I" | "q0", _), S(src, src_d)) => match src {
|
||||||
|
ast::Item::Symbol(Sym::Ident(ident)) => {
|
||||||
|
if initial_state.is_some() {
|
||||||
|
ctx.emit_error("initial state already set", span);
|
||||||
|
}
|
||||||
|
if states.contains_key(&State(ident)) {
|
||||||
|
initial_state = Some(State(ident))
|
||||||
|
} else {
|
||||||
|
ctx.emit_error("initial state symbol not defined as a state", src_d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ctx.emit_error("expected ident", src_d),
|
||||||
|
},
|
||||||
|
TL::Item(S("S" | "z0", _), S(src, src_d)) => match src {
|
||||||
|
ast::Item::Symbol(Sym::Ident(ident)) => {
|
||||||
|
if initial_tape.is_some() {
|
||||||
|
ctx.emit_error("initial tape symbol already set", span);
|
||||||
|
}
|
||||||
|
if symbols.contains_key(&Symbol(ident)) {
|
||||||
|
initial_tape = Some(Symbol(ident));
|
||||||
|
} else {
|
||||||
|
ctx.emit_error(
|
||||||
|
"initial tape symbol not defined as a tape symbol",
|
||||||
|
src_d,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ctx.emit_error("expected ident", src_d),
|
||||||
|
},
|
||||||
|
TL::Item(S(name, dest_s), _) => {
|
||||||
|
ctx.emit_error(format!("unknown item {name:?}, expected 'Q' | 'E' | '{SIGMA_UPPER}' | 'sigma' | 'F' | 'T' | '{GAMMA_UPPER}' | 'gamma' | 'I' | 'q0' | 'S' | 'z0'"), dest_s);
|
||||||
|
}
|
||||||
|
|
||||||
|
TL::TransitionFunc(S((S("d" | DELTA_LOWER | "delta", _), tuple), _), list) => {
|
||||||
|
let list = list.set_weak();
|
||||||
|
let Some((from_state, from_tape)) =
|
||||||
|
tuple.as_ref().expect_tm_transition_function(ctx)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !states.contains_key(&State(from_state.0)) {
|
||||||
|
ctx.emit_error("transition state not defined as state", from_state.1);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !symbols.contains_key(&Symbol(from_tape.0)) {
|
||||||
|
ctx.emit_error(
|
||||||
|
"transition tape symbol not defined as tape symbol",
|
||||||
|
from_tape.1,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
for item in list {
|
||||||
|
let Some((to_state, to_tape, direction)) = item
|
||||||
|
.expect_tuple(ctx)
|
||||||
|
.and_then(|item| item.expect_tm_transition(ctx))
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if !states.contains_key(&State(to_state.0)) {
|
||||||
|
ctx.emit_error("transition state not defined as state", to_state.1);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let entry: &mut _ = transitions
|
||||||
|
.entry(TransitionFrom {
|
||||||
|
state: State(from_state.0),
|
||||||
|
symbol: Symbol(from_tape.0),
|
||||||
|
})
|
||||||
|
.or_default();
|
||||||
|
if !entry.is_empty() && !options.non_deterministic {
|
||||||
|
ctx.emit_error("transition already defined for this starting point (non determinism not permitted)", item.1);
|
||||||
|
}
|
||||||
|
if !entry.insert(TransitionTo {
|
||||||
|
state: State(to_state.0),
|
||||||
|
symbol: Symbol(to_tape.0),
|
||||||
|
direction: direction.0,
|
||||||
|
|
||||||
|
function: tuple.1,
|
||||||
|
transition: item.1,
|
||||||
|
}) {
|
||||||
|
ctx.emit_warning("duplicate transition", item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TL::TransitionFunc(S((S(name, _), _), dest_s), _) => {
|
||||||
|
ctx.emit_error(
|
||||||
|
format!(
|
||||||
|
"unknown function {name:?}, expected 'd' | 'delta' | '{DELTA_LOWER}'"
|
||||||
|
),
|
||||||
|
dest_s,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TL::ProductionRule(_, _) => {
|
||||||
|
ctx.emit_error("unexpected production rule", span);
|
||||||
|
}
|
||||||
|
TL::Table() => ctx.emit_error("unexpected table", span),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if symbols.is_empty() {
|
||||||
|
ctx.emit_error_locless("tape symbols never defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
if states.is_empty() {
|
||||||
|
ctx.emit_error_locless("states never defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
let initial_tape = match initial_tape {
|
||||||
|
Some(some) => some,
|
||||||
|
None => {
|
||||||
|
if symbols.contains_key(&Symbol("z0")) {
|
||||||
|
ctx.emit_warning_locless("initial tape symbol not defined, defaulting to 'z0'");
|
||||||
|
} else {
|
||||||
|
ctx.emit_error_locless("initial tape symbol not defined");
|
||||||
|
}
|
||||||
|
Symbol("z0")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let initial_state = match initial_state {
|
||||||
|
Some(some) => some,
|
||||||
|
None => {
|
||||||
|
if states.contains_key(&State("q0")) {
|
||||||
|
ctx.emit_warning_locless("initial state not defined, defaulting to 'q0'");
|
||||||
|
} else {
|
||||||
|
ctx.emit_error_locless("initial state not defined");
|
||||||
|
}
|
||||||
|
State("q0")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if ctx.contains_errors() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Tm {
|
||||||
|
initial_state,
|
||||||
|
initial_tape,
|
||||||
|
states,
|
||||||
|
symbols,
|
||||||
|
final_states,
|
||||||
|
transitions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Spanned<&ast::Tuple<'a>> {
|
||||||
|
fn expect_tm_transition_function(
|
||||||
|
&self,
|
||||||
|
ctx: &mut Context<'a>,
|
||||||
|
) -> Option<(Spanned<&'a str>, Spanned<&'a str>)> {
|
||||||
|
match &self.0.0[..] {
|
||||||
|
[
|
||||||
|
Spanned(ast::Item::Symbol(ast::Symbol::Ident(state)), state_span),
|
||||||
|
Spanned(ast::Item::Symbol(ast::Symbol::Ident(tape)), tape_span),
|
||||||
|
] => {
|
||||||
|
return Some((Spanned(state, *state_span), Spanned(*tape, *tape_span)));
|
||||||
|
}
|
||||||
|
_ => ctx.emit_error("expected TM transition function (ident, ident)", self.1),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_tm_transition(
|
||||||
|
&self,
|
||||||
|
ctx: &mut Context<'a>,
|
||||||
|
) -> Option<(Spanned<&'a str>, Spanned<&'a str>, Spanned<Direction>)> {
|
||||||
|
match &self.0.0[..] {
|
||||||
|
[
|
||||||
|
Spanned(ast::Item::Symbol(ast::Symbol::Ident(state)), state_span),
|
||||||
|
Spanned(ast::Item::Symbol(ast::Symbol::Ident(tape)), tape_span),
|
||||||
|
Spanned(ast::Item::Symbol(direction), direction_span),
|
||||||
|
] => {
|
||||||
|
let direction = match direction {
|
||||||
|
ast::Symbol::Ident("left" | "L" | "<") => Direction::Left,
|
||||||
|
ast::Symbol::Ident("right" | "R" | ">") => Direction::Right,
|
||||||
|
ast::Symbol::Epsilon | ast::Symbol::Ident("~") => Direction::None,
|
||||||
|
ast::Symbol::Ident(ident) => {
|
||||||
|
ctx.emit_error(
|
||||||
|
format!("invalid direction specified '{ident}'"),
|
||||||
|
*direction_span,
|
||||||
|
);
|
||||||
|
Direction::None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return Some((
|
||||||
|
Spanned(state, *state_span),
|
||||||
|
Spanned(*tape, *tape_span),
|
||||||
|
Spanned(direction, *direction_span),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => ctx.emit_error(
|
||||||
|
"expected TM transition function (ident, ident, ident)",
|
||||||
|
self.1,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
pub mod automata;
|
pub mod automatan;
|
||||||
pub mod loader;
|
pub mod loader;
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,10 @@ pub struct ProductionGroup<'a>(pub Vec<Spanned<Symbol<'a>>>);
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum TopLevel<'a> {
|
pub enum TopLevel<'a> {
|
||||||
Item(Spanned<&'a str>, Spanned<Item<'a>>),
|
Item(Spanned<&'a str>, Spanned<Item<'a>>),
|
||||||
TransitionFunc(Spanned<(Spanned<&'a str>, Spanned<Tuple<'a>>)>, Spanned<Item<'a>>),
|
TransitionFunc(
|
||||||
|
Spanned<(Spanned<&'a str>, Spanned<Tuple<'a>>)>,
|
||||||
|
Spanned<Item<'a>>,
|
||||||
|
),
|
||||||
ProductionRule(
|
ProductionRule(
|
||||||
Spanned<ProductionGroup<'a>>,
|
Spanned<ProductionGroup<'a>>,
|
||||||
Spanned<Vec<Spanned<ProductionGroup<'a>>>>,
|
Spanned<Vec<Spanned<ProductionGroup<'a>>>>,
|
||||||
|
|
@ -62,12 +65,19 @@ pub enum TopLevel<'a> {
|
||||||
use crate::loader::Context;
|
use crate::loader::Context;
|
||||||
|
|
||||||
impl<'a> Spanned<Item<'a>> {
|
impl<'a> Spanned<Item<'a>> {
|
||||||
|
pub fn expect_symbol(&self, ctx: &mut Context<'a>) -> Option<Symbol<'a>> {
|
||||||
|
match &self.0 {
|
||||||
|
Item::Symbol(sym) => return Some(*sym),
|
||||||
|
Item::Tuple(_) => ctx.emit_error("expected ident found tuple", self.1),
|
||||||
|
Item::List(_) => ctx.emit_error("expected ident found list", self.1),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expect_ident(&self, ctx: &mut Context<'a>) -> Option<&'a str> {
|
pub fn expect_ident(&self, ctx: &mut Context<'a>) -> Option<&'a str> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Item::Symbol(Symbol::Ident(ident)) => return Some(ident),
|
Item::Symbol(Symbol::Ident(ident)) => return Some(ident),
|
||||||
Item::Symbol(Symbol::Epsilon) => {
|
Item::Symbol(Symbol::Epsilon) => ctx.emit_error("expected ident found epsilon", self.1),
|
||||||
ctx.emit_error("expected ident found epsilon", self.1)
|
|
||||||
}
|
|
||||||
Item::Tuple(_) => ctx.emit_error("expected ident found tuple", self.1),
|
Item::Tuple(_) => ctx.emit_error("expected ident found tuple", self.1),
|
||||||
Item::List(_) => ctx.emit_error("expected ident found list", self.1),
|
Item::List(_) => ctx.emit_error("expected ident found list", self.1),
|
||||||
}
|
}
|
||||||
|
|
@ -111,71 +121,10 @@ impl<'a> Spanned<Item<'a>> {
|
||||||
pub fn expect_tuple(&self, ctx: &mut Context<'a>) -> Option<Spanned<&Tuple<'a>>> {
|
pub fn expect_tuple(&self, ctx: &mut Context<'a>) -> Option<Spanned<&Tuple<'a>>> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Item::Symbol(Symbol::Ident(_)) => ctx.emit_error("expected tuple found ident", self.1),
|
Item::Symbol(Symbol::Ident(_)) => ctx.emit_error("expected tuple found ident", self.1),
|
||||||
Item::Symbol(Symbol::Epsilon) => {
|
Item::Symbol(Symbol::Epsilon) => ctx.emit_error("expected tuple found epsilon", self.1),
|
||||||
ctx.emit_error("expected tuple found epsilon", self.1)
|
|
||||||
}
|
|
||||||
Item::Tuple(tuple) => return Some(Spanned(tuple, self.1)),
|
Item::Tuple(tuple) => return Some(Spanned(tuple, self.1)),
|
||||||
Item::List(_) => ctx.emit_error("expected tuple found list", self.1),
|
Item::List(_) => ctx.emit_error("expected tuple found list", self.1),
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Spanned<&'b Tuple<'a>> {
|
|
||||||
pub fn expect_dfa_transition(&self, _: &mut Context<'a>) -> ! {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
pub fn expect_nfa_transition(&self, _: &mut Context<'a>) -> ! {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expect_dpda_transition(&self, _: &mut Context<'a>) -> ! {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expect_npda_transition_function(
|
|
||||||
&self,
|
|
||||||
ctx: &mut Context<'a>,
|
|
||||||
) -> Option<(Spanned<&'a str>, Spanned<Symbol<'a>>, Spanned<&'a str>)> {
|
|
||||||
match &self.0.0[..] {
|
|
||||||
[
|
|
||||||
Spanned(Item::Symbol(Symbol::Ident(state)), state_span),
|
|
||||||
Spanned(Item::Symbol(letter), letter_span),
|
|
||||||
Spanned(Item::Symbol(Symbol::Ident(symbol)), symbol_span),
|
|
||||||
] => {
|
|
||||||
return Some((
|
|
||||||
Spanned(state, *state_span),
|
|
||||||
Spanned(*letter, *letter_span),
|
|
||||||
Spanned(symbol, *symbol_span),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
_ => ctx.emit_error(
|
|
||||||
"expected NPDA transition function (ident, ident|~, ident)",
|
|
||||||
self.1,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
pub fn expect_npda_transition(
|
|
||||||
&self,
|
|
||||||
ctx: &mut Context<'a>,
|
|
||||||
) -> Option<(Spanned<&'a str>, &'b [Spanned<Item<'a>>])> {
|
|
||||||
match &self.0.0[..] {
|
|
||||||
[
|
|
||||||
Spanned(Item::Symbol(Symbol::Ident(state)), state_span),
|
|
||||||
list,
|
|
||||||
] => {
|
|
||||||
return Some((Spanned(state, *state_span), list.list_weak()));
|
|
||||||
}
|
|
||||||
_ => ctx.emit_error("expected NPDA transition (ident, item|[item])", self.1),
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expect_tm_transition(&self, _: &Context<'a>) -> ! {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
pub fn expect_ntm_transition(&self, _: &Context<'a>) -> ! {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,7 @@ pub enum Error {
|
||||||
|
|
||||||
impl<'a> Lexer<'a> {
|
impl<'a> Lexer<'a> {
|
||||||
pub fn new(input: &'a str) -> Self {
|
pub fn new(input: &'a str) -> Self {
|
||||||
Self {
|
Self { input, position: 0 }
|
||||||
input,
|
|
||||||
position: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume(&mut self) -> Option<char> {
|
fn consume(&mut self) -> Option<char> {
|
||||||
|
|
@ -92,7 +89,6 @@ impl<'a> Lexer<'a> {
|
||||||
self.position -= previous.len_utf8();
|
self.position -= previous.len_utf8();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin_ident(c: char) -> bool {
|
fn begin_ident(c: char) -> bool {
|
||||||
|
|
@ -110,12 +106,12 @@ impl<'a> std::iter::Iterator for Lexer<'a> {
|
||||||
while let Some(c) = self.peek()
|
while let Some(c) = self.peek()
|
||||||
&& c.is_whitespace()
|
&& c.is_whitespace()
|
||||||
{
|
{
|
||||||
if c == '\n'{
|
if c == '\n' {
|
||||||
let start = self.position;
|
let start = self.position;
|
||||||
self.consume();
|
self.consume();
|
||||||
let res = Some(Spanned(Ok(Token::LineEnd), Span(start, self.position)));
|
let res = Some(Spanned(Ok(Token::LineEnd), Span(start, self.position)));
|
||||||
return res;
|
return res;
|
||||||
}else{
|
} else {
|
||||||
self.consume();
|
self.consume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -151,7 +147,7 @@ impl<'a> std::iter::Iterator for Lexer<'a> {
|
||||||
|
|
||||||
'/' => match self.consume() {
|
'/' => match self.consume() {
|
||||||
Some('/') => loop {
|
Some('/') => loop {
|
||||||
match self.consume(){
|
match self.consume() {
|
||||||
Some('\n') => {
|
Some('\n') => {
|
||||||
self.backtrack();
|
self.backtrack();
|
||||||
break Ok(Token::Comment(&self.input[start + 2..=self.position]));
|
break Ok(Token::Comment(&self.input[start + 2..=self.position]));
|
||||||
|
|
@ -166,9 +162,7 @@ impl<'a> std::iter::Iterator for Lexer<'a> {
|
||||||
match self.consume() {
|
match self.consume() {
|
||||||
Some('*') if self.peek() == Some('/') => {
|
Some('*') if self.peek() == Some('/') => {
|
||||||
self.consume();
|
self.consume();
|
||||||
break Ok(Token::Comment(
|
break Ok(Token::Comment(&self.input[start + 2..self.position - 2]));
|
||||||
&self.input[start + 2..self.position - 2],
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
Some(_) => {}
|
Some(_) => {}
|
||||||
None => break Err(Error::UnclosedMultiLine),
|
None => break Err(Error::UnclosedMultiLine),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{fmt::Display};
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::loader::Span;
|
use crate::loader::Span;
|
||||||
|
|
||||||
|
|
@ -64,11 +64,11 @@ impl Logs {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn displayable_with<'a>(&'a self, src: &'a str) -> impl Iterator<Item = LogEntryDisplay<'a>> {
|
pub fn displayable_with<'a>(
|
||||||
self.logs.iter().map(|entry| LogEntryDisplay {
|
&'a self,
|
||||||
src,
|
src: &'a str,
|
||||||
entry,
|
) -> impl Iterator<Item = LogEntryDisplay<'a>> {
|
||||||
})
|
self.logs.iter().map(|entry| LogEntryDisplay { src, entry })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entries(&self) -> &[LogEntry] {
|
pub fn entries(&self) -> &[LogEntry] {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{automata::npda, loader::ast::TopLevel};
|
use crate::{automatan::*, loader::ast::TopLevel};
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
|
|
@ -13,7 +13,11 @@ pub const GAMMA_UPPER: &str = "Γ";
|
||||||
pub const GAMMA_LOWER: &str = "γ";
|
pub const GAMMA_LOWER: &str = "γ";
|
||||||
|
|
||||||
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
|
||||||
pub struct Span(pub usize, pub usize);
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
pub struct Span(
|
||||||
|
#[cfg_attr(feature = "serde", serde(rename = "start"))] pub usize,
|
||||||
|
#[cfg_attr(feature = "serde", serde(rename = "end"))] pub usize,
|
||||||
|
);
|
||||||
impl Span {
|
impl Span {
|
||||||
pub fn join(&self, end: Span) -> Span {
|
pub fn join(&self, end: Span) -> Span {
|
||||||
Span(self.0, end.1)
|
Span(self.0, end.1)
|
||||||
|
|
@ -32,26 +36,28 @@ impl<T> Spanned<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Context<'a> {
|
||||||
pub struct Context<'a>{
|
|
||||||
logs: log::Logs,
|
logs: log::Logs,
|
||||||
src: &'a str
|
src: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Context<'a>{
|
impl<'a> Context<'a> {
|
||||||
pub fn new(src: &'a str) -> Self{
|
pub fn new(src: &'a str) -> Self {
|
||||||
Self { logs: log::Logs::new(), src }
|
Self {
|
||||||
|
logs: log::Logs::new(),
|
||||||
|
src,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn src(&self) -> &'a str{
|
pub fn src(&self) -> &'a str {
|
||||||
self.src
|
self.src
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn logs_display(&self) -> impl Iterator<Item = log::LogEntryDisplay<'_>>{
|
pub fn logs_display(&self) -> impl Iterator<Item = log::LogEntryDisplay<'_>> {
|
||||||
self.logs.displayable_with(self.src)
|
self.logs.displayable_with(self.src)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eof(&self) -> Span{
|
pub fn eof(&self) -> Span {
|
||||||
Span(self.src.len(), self.src.len())
|
Span(self.src.len(), self.src.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,37 +89,40 @@ impl<'a> Context<'a>{
|
||||||
self.logs.contains_errors()
|
self.logs.contains_errors()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_logs(self) -> log::Logs{
|
pub fn into_logs(self) -> log::Logs {
|
||||||
self.logs
|
self.logs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum Machine<'a> {
|
||||||
pub enum Machine{
|
Fa(fa::Fa<'a>),
|
||||||
Npda(npda::Npda)
|
Pda(pda::Pda<'a>),
|
||||||
|
Tm(tm::Tm<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_universal(ctx: &mut Context<'_>) -> Option<Machine> {
|
pub fn parse_universal<'a>(ctx: &mut Context<'a>) -> Option<Machine<'a>> {
|
||||||
let mut items = parser::Parser::new(ctx).collect::<Vec<_>>().into_iter();
|
let mut items = parser::Parser::new(ctx).collect::<Vec<_>>().into_iter();
|
||||||
if ctx.logs.contains_errors(){
|
if ctx.logs.contains_errors() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
use Spanned as S;
|
use Spanned as S;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Type{
|
enum Type {
|
||||||
Dfa,
|
Dfa,
|
||||||
Nfa,
|
Nfa,
|
||||||
Dpda,
|
Dpda,
|
||||||
Npda,
|
Npda,
|
||||||
Tm,
|
Tm,
|
||||||
Ntm
|
Ntm,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_type<'a>(item: Option<S<TopLevel<'a>>>, ctx: &mut Context<'a>) -> Option<Type>{
|
fn parse_type<'a>(item: Option<S<TopLevel<'a>>>, ctx: &mut Context<'a>) -> Option<Type> {
|
||||||
let (str, span) = match item{
|
let (str, span) = match item {
|
||||||
Some(S(TopLevel::Item(S("type", _), item@S(_,span)), _)) => (item.expect_ident(ctx)?, span),
|
Some(S(TopLevel::Item(S("type", _), item @ S(_, span)), _)) => {
|
||||||
|
(item.expect_ident(ctx)?, span)
|
||||||
|
}
|
||||||
Some(S(_, span)) => {
|
Some(S(_, span)) => {
|
||||||
ctx.emit_error("expected type=<type> as first item", span);
|
ctx.emit_error("expected type=<type> as first item", span);
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -124,25 +133,39 @@ pub fn parse_universal(ctx: &mut Context<'_>) -> Option<Machine> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(match str{
|
Some(match str {
|
||||||
"dfa"|"DFA" => Type::Dfa,
|
"dfa" | "DFA" => Type::Dfa,
|
||||||
"nfa"|"NFA" => Type::Nfa,
|
"nfa" | "NFA" => Type::Nfa,
|
||||||
"dpda"|"DPDA" => Type::Dpda,
|
"dpda" | "DPDA" => Type::Dpda,
|
||||||
"npdaA"|"NPDA" => Type::Npda,
|
"npdaA" | "NPDA" => Type::Npda,
|
||||||
"tm"|"TM" => Type::Tm,
|
"tm" | "TM" => Type::Tm,
|
||||||
"ntm"|"NTM" => Type::Ntm,
|
"ntm" | "NTM" => Type::Ntm,
|
||||||
_ => {
|
_ => {
|
||||||
ctx.emit_error("unknown type, expected 'DFA' | 'NFA' | 'DPDA' | 'NPDA' | 'TM' | 'NTM'", span);
|
ctx.emit_error(
|
||||||
|
"unknown type, expected 'DFA' | 'NFA' | 'DPDA' | 'NPDA' | 'TM' | 'NTM'",
|
||||||
|
span,
|
||||||
|
);
|
||||||
return None;
|
return None;
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(match parse_type(items.next(), ctx)?{
|
const D: Options = Options {
|
||||||
Type::Npda => Machine::Npda(npda::Npda::load_from_ast(items, ctx)?),
|
non_deterministic: false,
|
||||||
ty => {
|
epsilon_moves: false,
|
||||||
ctx.emit_error_locless(format!("currently unsupported type {ty:?}"));
|
};
|
||||||
return None;
|
|
||||||
}
|
const N: Options = Options {
|
||||||
|
non_deterministic: true,
|
||||||
|
epsilon_moves: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(match parse_type(items.next(), ctx)? {
|
||||||
|
Type::Dfa => Machine::Fa(fa::Fa::parse(items, ctx, D)?),
|
||||||
|
Type::Nfa => Machine::Fa(fa::Fa::parse(items, ctx, N)?),
|
||||||
|
Type::Dpda => Machine::Pda(pda::Pda::parse(items, ctx, D)?),
|
||||||
|
Type::Npda => Machine::Pda(pda::Pda::parse(items, ctx, N)?),
|
||||||
|
Type::Tm => Machine::Tm(tm::Tm::parse(items, ctx, D)?),
|
||||||
|
Type::Ntm => Machine::Tm(tm::Tm::parse(items, ctx, N)?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
35
src/main.rs
35
src/main.rs
|
|
@ -1,35 +0,0 @@
|
||||||
use automata::{automata::npda, loader::Context};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = include_str!("../example.npda");
|
|
||||||
let mut ctx = Context::new(input);
|
|
||||||
|
|
||||||
let machine = automata::loader::parse_universal(&mut ctx);
|
|
||||||
for log in ctx.logs_display(){
|
|
||||||
println!("{log}")
|
|
||||||
}
|
|
||||||
|
|
||||||
let machine = match machine{
|
|
||||||
Some(automata::loader::Machine::Npda(npda)) => {
|
|
||||||
npda
|
|
||||||
},
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
let input = "aababaaba";
|
|
||||||
println!("running on: '{input}'");
|
|
||||||
let mut simulator = npda::Simulator::begin(input, machine);
|
|
||||||
loop {
|
|
||||||
match simulator.step() {
|
|
||||||
npda::SimulatorResult::Pending => {}
|
|
||||||
npda::SimulatorResult::Reject => {
|
|
||||||
println!("REJECTED");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
npda::SimulatorResult::Accept(npda) => {
|
|
||||||
println!("ACCEPT: {npda:?}");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"]
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
automata = {path=".."}
|
automata = {path="..", features = ["serde"]}
|
||||||
console_error_panic_hook = "0.1.7"
|
console_error_panic_hook = "0.1.7"
|
||||||
wasm-bindgen = "*"
|
wasm-bindgen = "*"
|
||||||
web-sys = { version = "0.3.83", features = ["Window", "Document", "HtmlElement", "Text"] }
|
web-sys = { version = "0.3.83", features = ["Window", "Document", "HtmlElement", "Text"] }
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,11 @@ function buildAnalysis(text: string, doc: Text) {
|
||||||
const { log, log_formatted, graph } = compile(text);
|
const { log, log_formatted, graph } = compile(text);
|
||||||
|
|
||||||
if (graph){
|
if (graph){
|
||||||
|
try{
|
||||||
setAutomaton(JSON.parse(graph))
|
setAutomaton(JSON.parse(graph))
|
||||||
|
}catch(e){
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build ONE Decoration set: syntax + diagnostics
|
// Build ONE Decoration set: syntax + diagnostics
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,7 @@ export function clearAutomaton() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setAutomaton(auto: GraphDef) {
|
export function setAutomaton(auto: GraphDef) {
|
||||||
|
console.log(auto);
|
||||||
automaton = auto;
|
automaton = auto;
|
||||||
automaton.final_states = new Set(automaton.final_states)
|
automaton.final_states = new Set(automaton.final_states)
|
||||||
automaton.states = new Set(automaton.states)
|
automaton.states = new Set(automaton.states)
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,6 @@ pub struct CompileLog {
|
||||||
pub end: Option<usize>,
|
pub end: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
pub struct Graph<'a> {
|
pub struct Graph<'a> {
|
||||||
initial: &'a str,
|
initial: &'a str,
|
||||||
|
|
@ -171,50 +170,11 @@ pub fn compile(input: &str) -> CompileResult {
|
||||||
let result = automata::loader::parse_universal(&mut ctx);
|
let result = automata::loader::parse_universal(&mut ctx);
|
||||||
|
|
||||||
let graph = if let Some(result) = result {
|
let graph = if let Some(result) = result {
|
||||||
match result {
|
Some(match result {
|
||||||
loader::Machine::Npda(npda) => {
|
loader::Machine::Fa(fa) => serde_json::to_string(&fa).unwrap(),
|
||||||
let mut transitions = HashMap::new();
|
loader::Machine::Pda(pda) => serde_json::to_string(&pda).unwrap(),
|
||||||
for ((from, symbol), to_transitions) in npda.transitions().entries(){
|
loader::Machine::Tm(tm) => serde_json::to_string(&tm).unwrap(),
|
||||||
let from = npda.get_state_name(from).unwrap_or("<INVALID>");
|
|
||||||
let symbol = npda.get_symbol_name(symbol).unwrap_or("<INVALID>");
|
|
||||||
for (char, to) in to_transitions.entries(){
|
|
||||||
for to in to{
|
|
||||||
let to_state = npda.get_state_name(to.state()).unwrap_or("<INVALID>");
|
|
||||||
let string: &mut String = transitions.entry(format!("{from}#{to_state}")).or_default();
|
|
||||||
if !string.is_empty(){
|
|
||||||
string.push('\n');
|
|
||||||
}
|
|
||||||
let char = char.unwrap_or('ε');
|
|
||||||
let stack = to.stack().iter().map(|s|npda.get_symbol_name(*s).unwrap_or("<INVALID>")).fold(String::new(), |mut s, b|{
|
|
||||||
if !s.is_empty(){
|
|
||||||
s.push_str(", ");
|
|
||||||
}
|
|
||||||
s.push_str(b);
|
|
||||||
s
|
|
||||||
});
|
|
||||||
write!(string, "{char}, {symbol} -> [{stack}]").unwrap();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let graph = Graph {
|
|
||||||
states: npda.states().map(|(_, n)| n).collect(),
|
|
||||||
initial: npda
|
|
||||||
.get_state_name(npda.initial_state())
|
|
||||||
.unwrap_or("<INVALID>"),
|
|
||||||
final_states: npda
|
|
||||||
.final_states()
|
|
||||||
.map(|i| {
|
|
||||||
i.map(|s| npda.get_state_name(s).unwrap_or("<INVALID>"))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
})
|
})
|
||||||
.unwrap_or_default(),
|
|
||||||
transitions
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(serde_json::to_string(&graph).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue