diff --git a/Cargo.lock b/Cargo.lock index 21fc673..f1434dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -22,9 +22,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "arrayvec" @@ -34,11 +34,11 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "assert_cmd" -version = "2.0.6" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba45b8163c49ab5f972e59a8a5a03b6d2972619d486e19ec9fe744f7c2753d3c" +checksum = "9834fcc22e0874394a010230586367d4a3e9f11b560f469262678547e1d2575e" dependencies = [ - "bstr 1.0.1", + "bstr", "doc-comment", "predicates", "predicates-core", @@ -48,9 +48,9 @@ dependencies = [ [[package]] name = "assert_fs" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429b32ede0cb31afd9f6cb1e8f06f1e32a4c75ed9290f9f4d3cda0c5981e061" +checksum = "d94b2a3f3786ff2996a98afbd6b4e5b7e890d685ccf67577f508ee2342c71cc9" dependencies = [ "doc-comment", "globwalk", @@ -66,7 +66,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -97,18 +97,9 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.17" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "memchr", -] - -[[package]] -name = "bstr" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca0852af221f458706eb0725c03e4ed6c46af9ac98e6a689d5e634215d594dd" +checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" dependencies = [ "memchr", "once_cell", @@ -118,9 +109,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "bytecount" @@ -136,9 +127,9 @@ checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" [[package]] name = "cc" -version = "1.0.74" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -148,29 +139,29 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", "num-integer", "num-traits", - "time 0.1.44", + "time 0.1.45", "wasm-bindgen", "winapi", ] [[package]] name = "clap" -version = "4.0.18" +version = "4.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335867764ed2de42325fafe6d18b8af74ba97ee0c590fa016f157535b42ab04b" +checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" dependencies = [ - "atty", "bitflags", "clap_derive", "clap_lex", + "is-terminal", "once_cell", "strsim", "termcolor", @@ -178,9 +169,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.18" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a1b0f6422af32d5da0c58e2703320f379216ee70198241c84173a8c5ac28f3" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" dependencies = [ "heck", "proc-macro-error", @@ -191,9 +182,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" dependencies = [ "os_str_bytes", ] @@ -248,9 +239,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.11" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" dependencies = [ "autocfg", "cfg-if", @@ -261,18 +252,18 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", ] [[package]] name = "cxx" -version = "1.0.80" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" +checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" dependencies = [ "cc", "cxxbridge-flags", @@ -282,9 +273,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.80" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" +checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" dependencies = [ "cc", "codespan-reporting", @@ -297,15 +288,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.80" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" +checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" [[package]] name = "cxxbridge-macro" -version = "1.0.80" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" +checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" dependencies = [ "proc-macro2", "quote", @@ -347,9 +338,30 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] [[package]] name = "fastrand" @@ -409,12 +421,12 @@ dependencies = [ [[package]] name = "globset" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", - "bstr 0.2.17", + "bstr", "fnv", "log", "regex", @@ -439,9 +451,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -452,6 +464,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "iana-time-zone" version = "0.1.53" @@ -478,11 +499,10 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" dependencies = [ - "crossbeam-utils", "globset", "lazy_static", "log", @@ -496,9 +516,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -513,6 +533,28 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys 0.42.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -524,15 +566,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -558,19 +600,25 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.137" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "link-cplusplus" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" dependencies = [ "cc", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "log" version = "0.4.17" @@ -588,16 +636,16 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.6.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg", ] [[package]] name = "morty" -version = "0.8.0" +version = "0.9.0" dependencies = [ "anyhow", "assert_cmd", @@ -778,11 +826,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -797,15 +845,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "os_str_bytes" -version = "6.3.1" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "petgraph" @@ -819,9 +867,9 @@ dependencies = [ [[package]] name = "predicates" -version = "2.1.2" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab68289ded120dcbf9d571afcf70163233229052aec9b08ab09532f698d0e1e6" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ "difflib", "float-cmp", @@ -833,15 +881,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e7125585d872860e9955ca571650b27a4979c5823084168c5ed5bbfb016b56" +checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" [[package]] name = "predicates-tree" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad3f7fa8d61e139cbc7c3edfebf3b6678883a53f5ffac65d1259329a93ee43a5" +checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" dependencies = [ "predicates-core", "termtree", @@ -873,9 +921,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] @@ -894,9 +942,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -909,21 +957,19 @@ checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" [[package]] name = "rayon" -version = "1.5.3" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -953,9 +999,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -970,9 +1016,9 @@ checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "remove_dir_all" @@ -983,17 +1029,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustix" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.42.0", +] + [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "same-file" @@ -1012,24 +1072,24 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" [[package]] name = "serde" -version = "1.0.147" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -1038,9 +1098,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ "itoa", "ryu", @@ -1056,8 +1116,8 @@ dependencies = [ "atty", "colored", "log", - "time 0.3.16", - "windows-sys", + "time 0.3.17", + "windows-sys 0.42.0", ] [[package]] @@ -1080,9 +1140,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "sv-parser" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172a5b3cb5516198bb3511c0f5b25c7f9911cd46189f4d07c8245d0488ad7c93" +checksum = "ed6567e4ff8c8f26fbd561e8797f2bf658462b68346d481d9a53f9390873688d" dependencies = [ "nom 6.1.2", "nom-greedyerror", @@ -1094,18 +1154,18 @@ dependencies = [ [[package]] name = "sv-parser-error" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31d940ac5717eab14042763f6c67ef2c9e0bcf381b726694eb92c32b96c21b9f" +checksum = "d8ab9840f6af470b46a27ec709c5bdba054d0fe57d408ba550d99b6de027b143" dependencies = [ "thiserror", ] [[package]] name = "sv-parser-macros" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed5b1dbf2209da2f4aa7f623ad0e9a941844ec586b2c2ca9747a9a4de815065" +checksum = "60599265d86a4647a32e97f5c9ae758c1ac82402a6e0d123347384997148ba6a" dependencies = [ "quote", "syn", @@ -1113,9 +1173,9 @@ dependencies = [ [[package]] name = "sv-parser-parser" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44acd0cd81361b2be53349e5e612b08e58f8e4175d1a3484b05828da53135adf" +checksum = "4628479b05874500c1227228f481e04e9aaf858bc43eecd87fbbf4b6999b7269" dependencies = [ "nom 6.1.2", "nom-greedyerror", @@ -1130,9 +1190,9 @@ dependencies = [ [[package]] name = "sv-parser-pp" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7d2da3c2ace6950bc7d9d88f9bd5ddc37b85af9bd28f75eca511264c687953" +checksum = "db489604d13d9f630173a477357a0010d310597df4728a35bc54409af72ebb3c" dependencies = [ "nom 6.1.2", "nom-greedyerror", @@ -1143,9 +1203,9 @@ dependencies = [ [[package]] name = "sv-parser-syntaxtree" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57964e3fb7332344b6d9e38919f4a417f9dc4ac44dcac15d1b6c3cd194b4bb61" +checksum = "a49debd66a99e1783badfa9780bb96b3a96b92d94ebd58ca4b82f3a32ca498a3" dependencies = [ "regex", "sv-parser-macros", @@ -1154,9 +1214,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.103" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -1196,9 +1256,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -1211,18 +1271,18 @@ checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -1240,9 +1300,9 @@ dependencies = [ [[package]] name = "time" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", @@ -1251,9 +1311,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fab5c8b9980850e06d92ddbe3ab839c062c801f3927c0fb8abd6fc8e918fbca" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ "itoa", "libc", @@ -1271,9 +1331,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bb801831d812c562ae7d2bfb531f26e66e4e1f6b17307ba4149c5064710e5b" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" dependencies = [ "time-core", ] @@ -1289,9 +1349,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-width" @@ -1339,9 +1399,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1349,9 +1409,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -1364,9 +1424,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1374,9 +1434,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -1387,9 +1447,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "winapi" @@ -1437,47 +1497,71 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "wyz" diff --git a/Cargo.toml b/Cargo.toml index 4ca951b..2d3530e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "morty" -version = "0.8.0" +version = "0.9.0" description = "A SystemVerilog source file pickler." authors = ["Florian Zaruba ", "Fabian Schuiki ", "Michael Rogenmoser "] edition = "2018" diff --git a/src/lib.rs b/src/lib.rs index 5cd7a6b..3a491bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,18 +8,21 @@ #[macro_use] extern crate log; +// use crate::printer::printer::*; +// use crate::printer::printer::Printer; use anyhow::{anyhow, Context as _, Error, Result}; -use chrono::Local; -use petgraph::algo::dijkstra; -use petgraph::graph::{Graph, NodeIndex}; -use rayon::prelude::*; +// use chrono::Local; +// use petgraph::algo::dijkstra; +// use petgraph::graph::{Graph, NodeIndex}; +// use rayon::prelude::*; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use std::collections::HashSet; -use std::convert::TryFrom; +// use std::collections::HashSet; +// use std::convert::TryFrom; use std::ffi::OsStr; -use std::fs::File; -use std::io::{BufWriter, Write}; +use std::fmt; +// use std::fs::File; +// use std::io::{BufWriter, Write}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use sv_parser::Error as SvParserError; @@ -28,755 +31,759 @@ use sv_parser::{ }; pub mod doc; -mod printer; - -pub fn do_pickle<'a>( - prefix: Option<&'a String>, - suffix: Option<&'a String>, - exclude_rename: HashSet<&'a String>, - exclude: HashSet<&'a String>, - library_bundle: LibraryBundle, - mut syntax_trees: Vec, - mut out: Box, - top_module: Option<&'a String>, - keep_defines: bool, - propagate_defines: bool, - remove_timeunits: bool, -) -> Result> { - let mut pickle = Pickle::new( - // Collect renaming options. - prefix, - suffix, - exclude_rename, - exclude, - library_bundle, - ); - - // Gather information for pickling. - for pf in &syntax_trees { - // println!("{}", pf.ast); - for node in &pf.ast { - trace!("{:#?}", node); - match node { - // Module declarations. - RefNode::ModuleDeclarationAnsi(x) => { - // unwrap_node! gets the nearest ModuleIdentifier from x - let id = unwrap_node!(x, ModuleIdentifier).unwrap(); - pickle.register_declaration(&pf.ast, id, pf.path.clone()); - } - RefNode::ModuleDeclarationNonansi(x) => { - let id = unwrap_node!(x, ModuleIdentifier).unwrap(); - pickle.register_declaration(&pf.ast, id, pf.path.clone()); - } - // Interface Declaration. - RefNode::InterfaceDeclaration(x) => { - let id = unwrap_node!(x, InterfaceIdentifier).unwrap(); - pickle.register_declaration(&pf.ast, id, pf.path.clone()); - } - // Package declarations. - RefNode::PackageDeclaration(x) => { - let id = unwrap_node!(x, PackageIdentifier).unwrap(); - pickle.register_declaration(&pf.ast, id, pf.path.clone()); - } - _ => (), - } - } - } - - let mut library_files: Vec = vec![]; - for pf in &syntax_trees { - // global package import - let global_packages = &pf - .ast - .into_iter() - .filter_map(|node| { - if let RefNode::DescriptionPackageItem(x) = node { - if let Some(package_import) = unwrap_node!(x, PackageImportDeclaration) { - let (name, _loc) = get_identifier( - &pf.ast, - unwrap_node!(package_import, SimpleIdentifier).unwrap(), - ); - eprintln!( - "Global package import in {}:\n\t{}", - &pf.path, - &pf.source[Locate::try_from(x).unwrap().offset - ..(Locate::try_from(x).unwrap().offset - + Locate::try_from(x).unwrap().len)] - ); - Some(name) - } else { - None - } - } else { - None - } - }) - .collect::>(); - - for node in &pf.ast { - match node { - // Module declarations. - RefNode::ModuleDeclarationAnsi(x) => { - // unwrap_node! gets the nearest ModuleIdentifier from x - let parent_id = unwrap_node!(x, ModuleIdentifier).unwrap(); - let (parent_name, _) = get_identifier(&pf.ast, parent_id); - - let my_ref_node: RefNode = x.into(); - pickle.find_and_register_instantiations( - &pf.ast, - my_ref_node, - &parent_name, - &mut library_files, - ); - for package in global_packages { - pickle.add_dependency_relation(package, &parent_name); - } - } - RefNode::ModuleDeclarationNonansi(x) => { - let parent_id = unwrap_node!(x, ModuleIdentifier).unwrap(); - let (parent_name, _) = get_identifier(&pf.ast, parent_id); - - let my_ref_node: RefNode = x.into(); - pickle.find_and_register_instantiations( - &pf.ast, - my_ref_node, - &parent_name, - &mut library_files, - ); - for package in global_packages { - pickle.add_dependency_relation(package, &parent_name); - } - } - // Interface Declaration. - RefNode::InterfaceDeclaration(x) => { - let parent_id = unwrap_node!(x, InterfaceIdentifier).unwrap(); - let (parent_name, _) = get_identifier(&pf.ast, parent_id); - - let my_ref_node: RefNode = x.into(); - pickle.find_and_register_instantiations( - &pf.ast, - my_ref_node, - &parent_name, - &mut library_files, - ); - for package in global_packages { - pickle.add_dependency_relation(package, &parent_name); - } - } - // Package declarations. - RefNode::PackageDeclaration(x) => { - let parent_id = unwrap_node!(x, PackageIdentifier).unwrap(); - let (parent_name, _) = get_identifier(&pf.ast, parent_id); - - let my_ref_node: RefNode = x.into(); - pickle.find_and_register_instantiations( - &pf.ast, - my_ref_node, - &parent_name, - &mut library_files, - ); - for package in global_packages { - pickle.add_dependency_relation(package, &parent_name); - } - } - _ => (), - } - } - } - - syntax_trees.extend(library_files); - write!( - out, - "// Compiled by morty-{} / {}\n\n", - env!("CARGO_PKG_VERSION"), - Local::now() - ) - .unwrap(); - - if let Some(top) = top_module { - if propagate_defines { - warn!( - "Pickle might be non-functional as some files can be excluded due to use of --top={}.\ - \n\tThis might lead to required components being excluded. Use at your own risk!!!", - top - ); - } - pickle.prune_graph(top)?; - } - - let needed_files = pickle - .module_file_map - .clone() - .into_values() - .collect::>(); - - // Emit the pickled source files. - for pf in &syntax_trees { - if top_module.is_some() && !needed_files.contains(&pf.path) { - continue; - } - // For each file, start with a clean replacement table. - pickle.replace_table.clear(); - // Iterate again and check for usage - for node in &pf.ast { - match node { - // Instantiations, end-labels. - RefNode::ModuleIdentifier(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - pickle.register_usage(&pf.ast, id); - } - // Interface identifier. - RefNode::InterfaceIdentifier(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - pickle.register_usage(&pf.ast, id); - } - // Package Qualifier (i.e., explicit package constants). - RefNode::ClassScope(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - pickle.register_usage(&pf.ast, id); - } - // Package Import. - RefNode::PackageIdentifier(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - pickle.register_usage(&pf.ast, id); - } - // Check whether we want to exclude the given module from the file sources. - RefNode::ModuleDeclarationAnsi(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - pickle.register_exclude(&pf.ast, id, Locate::try_from(x).unwrap()) - } - RefNode::ModuleDeclarationNonansi(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - pickle.register_exclude(&pf.ast, id, Locate::try_from(x).unwrap()) - } - RefNode::InterfaceDeclaration(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - pickle.register_exclude(&pf.ast, id, Locate::try_from(x).unwrap()) - } - RefNode::PackageDeclaration(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - pickle.register_exclude(&pf.ast, id, Locate::try_from(x).unwrap()) - } - RefNode::TimeunitsDeclaration(x) => { - let loc = Locate::try_from(x).unwrap(); - if remove_timeunits { - pickle - .replace_table - .push((loc.offset, loc.len, "".to_string())); - } - } - _ => (), - } - } - - // Find macros to be removed - let mut new_replace_table = Vec::new(); - - if !keep_defines { - for node in &pf.ast { - if let RefNode::TextMacroDefinition(x) = node { - let loc = Locate::try_from(x).unwrap(); - new_replace_table.push((loc.offset, loc.len, "".to_string())); - } - } - } - - new_replace_table.append(&mut pickle.replace_table); - - // sort replace table - new_replace_table.sort_by(|a, b| a.0.cmp(&b.0)); - - // Error on overlapping -> correct overlapping! - for i in 0..new_replace_table.len() - 1 { - if new_replace_table[i].0 + new_replace_table[i].1 > new_replace_table[i + 1].0 { - eprintln!( - "Offset error, please contact Michael\n{:?}", - new_replace_table[i] - ); - } - } - - // Replace according to `replace_table`. - // Apply the replacements. - debug!("Replace Table: {:?}", new_replace_table); - let mut pos = 0; - for (offset, len, repl) in new_replace_table.iter() { - // Because we are partially stripping modules it can be the case that we don't need to apply some of the upcoming replacements. - if pos > *offset { - continue; - } - trace!("Replacing: {},{}, {}", offset, len, repl); - write!(out, "{}", &pf.source[pos..*offset]).unwrap(); - write!(out, "{}", repl).unwrap(); - pos = offset + len; - } - write!(out, "{}", &pf.source[pos..]).unwrap(); - // Make sure that each file ends with a newline. - if !pf.source.ends_with('\n') { - writeln!(out).unwrap(); - } - } - - Ok(pickle) -} - -pub fn build_syntax_tree( - file_list: &Vec, - strip_comments: bool, - ignore_unparseable: bool, - propagate_defines: bool, - force_sequential: bool, -) -> Result> { - // Parse the input files. - let mut syntax_trees = vec![]; - - let mut internal_defines: Defines = HashMap::new(); - - for bundle in file_list { - let bundle_include_dirs: Vec<_> = bundle.include_dirs.iter().map(Path::new).collect(); - - if propagate_defines { - internal_defines.extend(defines_to_sv_parser(&bundle.defines)); - } else { - internal_defines = defines_to_sv_parser(&bundle.defines); - } - - // For each file in the file bundle preprocess and parse it. - // Use a neat trick of `collect` here, which allows you to collect a - // `Result` iterator into a `Result>`, i.e. bubbling up the - // error. - let v = if force_sequential | propagate_defines { - let tmp = bundle.files.iter().map(|filename| -> Result<_> { - let pf = parse_file( - filename, - &bundle_include_dirs, - &internal_defines, - strip_comments, - )?; - if propagate_defines { - internal_defines.extend(pf.defines.clone()); - } - Ok(pf) - }); - if ignore_unparseable { - tmp.filter_map(|r| r.map_err(|e| warn!("Continuing with {:?}", e)).ok()) - .collect() - } else { - tmp.collect::>>()? - } - } else { - let tmp = bundle.files.par_iter().map(|filename| -> Result<_> { - parse_file( - filename, - &bundle_include_dirs, - &internal_defines, - strip_comments, - ) - }); - if ignore_unparseable { - tmp.filter_map(|r| r.map_err(|e| warn!("Continuing with {:?}", e)).ok()) - .collect() - } else { - tmp.collect::>>()? - } - }; - syntax_trees.extend(v); - } - - Ok(syntax_trees) -} - -pub fn just_preprocess(syntax_trees: Vec, mut out: Box) -> Result<()> { - write!( - out, - "// Compiled by morty-{} / {}\n\n", - env!("CARGO_PKG_VERSION"), - Local::now() - ) - .unwrap(); - for pf in syntax_trees { - eprintln!("{}:", pf.path); - writeln!(out, "{:}", pf.source).unwrap(); - } - Ok(()) -} - -pub fn build_doc(syntax_trees: Vec, dir: &str) -> Result<()> { - let doc = doc::Doc::new(&syntax_trees); - let mut html = doc::Renderer::new(Path::new(dir)); - html.render(&doc)?; - Ok(()) -} - -pub fn write_manifest( - manifest_file: &str, - pickle: Pickle, - file_list: Vec, - include_dirs: Vec, - defines: HashMap>, - top_module: Option<&String>, -) -> Result<()> { - let mut undef_modules = Vec::new(); - - // find undefined modules - for name in &pickle.inst_table { - if !pickle.rename_table.contains_key(name) { - undef_modules.push(name.to_string()); - } - } - - let mut top_modules = Vec::new(); - - // find top modules - match top_module { - Some(x) => { - top_modules.push(pickle.rename_table[x].to_string()); - } - None => { - for new_name in pickle.rename_table.values() { - if !pickle.inst_table.contains(new_name) { - top_modules.push(new_name.to_string()); - } - } - } - } - - let mut base_files = Vec::new(); - let mut bundles = Vec::new(); - let pickled_files = pickle - .module_file_map - .clone() - .into_values() - .collect::>(); - for mut bundle in file_list { - if bundle.include_dirs == include_dirs && bundle.defines == defines { - base_files.extend(bundle.files.clone()); - if top_module.is_some() { - base_files.retain(|v| pickled_files.clone().contains(v)); - } - } else { - if top_module.is_some() { - bundle.files.retain(|v| pickled_files.contains(v)); - } - if !bundle.files.is_empty() { - bundles.push(bundle); - } - } - } - base_files.extend(pickle.used_libs); - if !base_files.is_empty() { - bundles.push(FileBundle { - include_dirs, - export_incdirs: HashMap::new(), - defines, - files: base_files, - }); - } - - let json = serde_json::to_string_pretty(&Manifest { - sources: bundles, - tops: top_modules, - undefined: undef_modules, - }) - .unwrap(); - - let path = Path::new(manifest_file); - let mut out = Box::new(BufWriter::new(File::create(path).unwrap())) as Box; - writeln!(out, "{}", json).unwrap(); - - Ok(()) -} - -/// Write module graph to file -pub fn write_dot_graph(pickle: &Pickle, graph_file: &str) -> Result<()> { - let path = Path::new(graph_file); - let mut out = Box::new(BufWriter::new(File::create(path).unwrap())) as Box; - writeln!( - out, - "{:?}", - petgraph::dot::Dot::with_config( - &pickle.module_graph, - &[petgraph::dot::Config::EdgeNoLabel] - ) - ) - .unwrap(); - Ok(()) -} - -/// Struct containing information about -/// what should be pickled and how. -#[derive(Debug)] -pub struct Pickle<'a> { - /// Optional name prefix. - pub prefix: Option<&'a String>, - /// Optional name suffix. - pub suffix: Option<&'a String>, - /// Declarations which are excluded from re-naming. - pub exclude_rename: HashSet<&'a String>, - /// Declarations which are excluded from the pickled sources. - pub exclude: HashSet<&'a String>, - /// Table containing thing that should be re-named. - pub rename_table: HashMap, - /// Locations of text which should be replaced. - pub replace_table: Vec<(usize, usize, String)>, - /// A set of instantiated modules. - pub inst_table: HashSet, - /// Information for library files - pub libs: LibraryBundle, - /// List of library files used during parsing. - pub used_libs: Vec, - /// Module hierarchy graph - pub module_graph: Graph, - /// Map for module names to graph nodes - pub module_graph_nodes: HashMap, - /// Map module name to declaration file - pub module_file_map: HashMap, -} - -impl<'a> Pickle<'a> { - pub fn new( - prefix: Option<&'a String>, - suffix: Option<&'a String>, - exclude_rename: HashSet<&'a String>, - exclude: HashSet<&'a String>, - libs: LibraryBundle, - ) -> Self { - Self { - prefix, - suffix, - exclude_rename, - exclude, - // Create a rename table. - rename_table: HashMap::new(), - replace_table: vec![], - inst_table: HashSet::new(), - libs, - used_libs: vec![], - // Create graph. - module_graph: Graph::new(), - module_graph_nodes: HashMap::new(), - module_file_map: HashMap::new(), - } - } - - /// Register a declaration such as a package or module. - pub fn register_declaration(&mut self, syntax_tree: &SyntaxTree, id: RefNode, file: String) { - let (module_name, loc) = get_identifier(syntax_tree, id); - info!("module_name: {:?}", module_name); - self.module_graph_nodes.insert( - module_name.clone(), - self.module_graph.add_node(module_name.clone()), - ); - self.module_file_map.insert(module_name.clone(), file); - if self.exclude_rename.contains(&module_name) || self.exclude.contains(&module_name) { - return; - } - let mut new_name = module_name.clone(); - if let Some(prefix) = self.prefix { - new_name = format!("{}{}", prefix, new_name); - } - if let Some(suffix) = self.suffix { - new_name = format!("{}{}", new_name, suffix); - } - debug!("Declaration `{}`: {:?}", module_name, loc); - self.rename_table.insert(module_name, new_name); - } - - pub fn register_instantiation(&mut self, syntax_tree: &SyntaxTree, id: RefNode) { - let (inst_name, _) = get_identifier(syntax_tree, id.clone()); - self.inst_table.insert(inst_name.clone()); - - if let Some((parent_name, _)) = get_calling_module(syntax_tree, id) { - self.add_dependency_relation(&inst_name, &parent_name); - } - } - - pub fn register_instantiation_with_parent( - &mut self, - syntax_tree: &SyntaxTree, - id: RefNode, - parent_name: &str, - ) { - let (inst_name, _) = get_identifier(syntax_tree, id.clone()); - self.inst_table.insert(inst_name.clone()); - - self.add_dependency_relation(&inst_name, parent_name); - } - - pub fn add_dependency_relation(&mut self, inst_name: &str, parent_name: &str) { - if !self.module_graph_nodes.contains_key(inst_name) { - self.module_graph_nodes.insert( - inst_name.to_string(), - self.module_graph.add_node(inst_name.to_string()), - ); - } - self.module_graph.update_edge( - self.module_graph_nodes[parent_name], - self.module_graph_nodes[inst_name], - (), - ); - } - - pub fn find_and_register_instantiations( - &mut self, - syntax_tree: &SyntaxTree, - id: RefNode, - parent_name: &str, - library_files: &mut Vec, - ) { - for node in id { - match node { - RefNode::ModuleInstantiation(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - self.register_instantiation_with_parent(syntax_tree, id.clone(), parent_name); - - let (inst_name, _) = get_identifier(syntax_tree, id.clone()); - if !self.rename_table.contains_key(&inst_name) { - info!("Could not find {}, checking libraries...", &inst_name); - self.load_library_module(&inst_name, library_files); - } - } - RefNode::PackageImportItem(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - self.register_instantiation_with_parent(syntax_tree, id.clone(), parent_name); - - let (inst_name, _) = get_identifier(syntax_tree, id); - if !self.rename_table.contains_key(&inst_name) { - info!("Could not find {}, checking libraries...", &inst_name); - self.load_library_module(&inst_name, library_files); - } - } - RefNode::PackageScope(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - self.register_instantiation_with_parent(syntax_tree, id.clone(), parent_name); - - let (inst_name, _) = get_identifier(syntax_tree, id); - if !self.rename_table.contains_key(&inst_name) { - info!("Could not find {}, checking libraries...", &inst_name); - self.load_library_module(&inst_name, library_files); - } - } - RefNode::InterfacePortHeader(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - self.register_instantiation_with_parent(syntax_tree, id.clone(), parent_name); - - let (inst_name, _) = get_identifier(syntax_tree, id); - if !self.rename_table.contains_key(&inst_name) { - info!("Could not find {}, checking libraries...", &inst_name); - self.load_library_module(&inst_name, library_files); - } - } - RefNode::ClassScope(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - self.register_instantiation_with_parent(syntax_tree, id.clone(), parent_name); - - let (inst_name, _) = get_identifier(syntax_tree, id); - if !self.rename_table.contains_key(&inst_name) { - info!("Could not find {}, checking libraries...", &inst_name); - self.load_library_module(&inst_name, library_files); - } - } - _ => (), - } - } - } - - /// Register a usage of the identifier. - pub fn register_usage(&mut self, syntax_tree: &SyntaxTree, id: RefNode) { - let (inst_name, loc) = get_identifier(syntax_tree, id); - let new_name = match self.rename_table.get(&inst_name) { - Some(x) => x, - None => return, - }; - debug!("Usage `{}`: {:?}", inst_name, loc); - self.replace_table - .push((loc.offset, loc.len, new_name.clone())); - } - - // Check whether a given declaration should be striped from the sources. - pub fn register_exclude(&mut self, syntax_tree: &SyntaxTree, id: RefNode, locate: Locate) { - let (inst_name, loc) = get_identifier(syntax_tree, id); - if self.exclude.contains(&inst_name) { - debug!("Exclude `{}`: {:?}", inst_name, loc); - self.replace_table - .push((locate.offset, locate.len, "".to_string())); - } - } - - // Load the module with name 'module_name' and append the resulting ParsedFile to 'files'. - // This function may recursively load other modules if the library uses another library module. - // If no module is found in the library bundle, this function does nothing. - pub fn load_library_module(&mut self, module_name: &str, files: &mut Vec) { - let rm = self.libs.load_module(module_name, &mut self.used_libs); - match rm { - Ok(pf) => { - // register all declarations from this library file. - for node in &pf.ast { - match node { - RefNode::ModuleDeclarationAnsi(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - self.register_declaration(&pf.ast, id, pf.path.clone()); - } - RefNode::ModuleDeclarationNonansi(x) => { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - self.register_declaration(&pf.ast, id, pf.path.clone()); - } - _ => (), - } - } - // look for all module instantiations - for node in &pf.ast { - if let RefNode::ModuleInstantiation(x) = node { - let id = unwrap_node!(x, SimpleIdentifier).unwrap(); - self.register_instantiation(&pf.ast, id.clone()); - - // if this module is undefined, recursively attempt to load a library - // module for it. - let (inst_name, _) = get_identifier(&pf.ast, id); - info!( - "Instantiation `{}` in library module `{}`", - &inst_name, &module_name - ); - if !self.rename_table.contains_key(&inst_name) { - info!("load library module {}", &inst_name); - self.load_library_module(&inst_name, files); - } - } - } - // add the parsed file to the vector. - files.push(pf); - } - Err(e) => info!("error loading library: {}", e), - } - } - - pub fn prune_graph(&mut self, top_module: &str) -> Result<()> { - if !self.module_graph_nodes.contains_key(top_module) { - return Err(anyhow!("Module {} not found!", top_module)); - } - let test_weights = dijkstra( - &self.module_graph, - self.module_graph_nodes[top_module], - None, - |_| 1, - ); - - self.module_graph - .retain_nodes(|_, n| test_weights.contains_key(&n)); - self.module_graph_nodes - .retain(|_, v| test_weights.contains_key(v)); - - let test_keys = self.module_graph_nodes.clone(); - self.module_file_map - .retain(|k, _| test_keys.contains_key(k)); - - self.inst_table.retain(|k| test_keys.contains_key(k)); - - self.rename_table.retain(|k, _| test_keys.contains_key(k)); - - Ok(()) - } -} +pub mod pickle; +pub mod printer; + +// pub fn do_pickle<'a>( +// prefix: Option<&'a String>, +// suffix: Option<&'a String>, +// exclude_rename: HashSet<&'a String>, +// exclude: HashSet<&'a String>, +// library_bundle: LibraryBundle, +// mut syntax_trees: Vec, +// mut out: Box, +// top_module: Option<&'a String>, +// keep_defines: bool, +// propagate_defines: bool, +// remove_timeunits: bool, +// ) -> Result> { +// let mut pickle = Pickle::new( +// // Collect renaming options. +// prefix, +// suffix, +// exclude_rename, +// exclude, +// library_bundle, +// ); + +// // Gather information for pickling. +// for pf in &syntax_trees { +// // println!("{}", pf.ast); +// for node in &pf.ast { +// trace!("{:#?}", node); +// match node { +// // Module declarations. +// RefNode::ModuleDeclarationAnsi(x) => { +// // unwrap_node! gets the nearest ModuleIdentifier from x +// let id = unwrap_node!(x, ModuleIdentifier).unwrap(); +// pickle.register_declaration(&pf.ast, id, pf.path.clone()); +// } +// RefNode::ModuleDeclarationNonansi(x) => { +// let id = unwrap_node!(x, ModuleIdentifier).unwrap(); +// pickle.register_declaration(&pf.ast, id, pf.path.clone()); +// } +// // Interface Declaration. +// RefNode::InterfaceDeclaration(x) => { +// let id = unwrap_node!(x, InterfaceIdentifier).unwrap(); +// pickle.register_declaration(&pf.ast, id, pf.path.clone()); +// } +// // Package declarations. +// RefNode::PackageDeclaration(x) => { +// let id = unwrap_node!(x, PackageIdentifier).unwrap(); +// pickle.register_declaration(&pf.ast, id, pf.path.clone()); +// } +// _ => (), +// } +// } +// } + +// let mut library_files: Vec = vec![]; +// for pf in &syntax_trees { +// // global package import +// let global_packages = &pf +// .ast +// .into_iter() +// .filter_map(|node| { +// if let RefNode::DescriptionPackageItem(x) = node { +// if let Some(package_import) = unwrap_node!(x, PackageImportDeclaration) { +// let (name, _loc) = get_identifier( +// &pf.ast, +// unwrap_node!(package_import, SimpleIdentifier).unwrap(), +// ); +// eprintln!( +// "Global package import in {}:\n\t{}", +// &pf.path, +// &pf.source[Locate::try_from(x).unwrap().offset +// ..(Locate::try_from(x).unwrap().offset +// + Locate::try_from(x).unwrap().len)] +// ); +// Some(name) +// } else { +// None +// } +// } else { +// None +// } +// }) +// .collect::>(); + +// for node in &pf.ast { +// match node { +// // Module declarations. +// RefNode::ModuleDeclarationAnsi(x) => { +// // unwrap_node! gets the nearest ModuleIdentifier from x +// let parent_id = unwrap_node!(x, ModuleIdentifier).unwrap(); +// let (parent_name, _) = get_identifier(&pf.ast, parent_id); + +// let my_ref_node: RefNode = x.into(); +// pickle.find_and_register_instantiations( +// &pf.ast, +// my_ref_node, +// &parent_name, +// &mut library_files, +// ); +// for package in global_packages { +// pickle.add_dependency_relation(package, &parent_name); +// } +// } +// RefNode::ModuleDeclarationNonansi(x) => { +// let parent_id = unwrap_node!(x, ModuleIdentifier).unwrap(); +// let (parent_name, _) = get_identifier(&pf.ast, parent_id); + +// let my_ref_node: RefNode = x.into(); +// pickle.find_and_register_instantiations( +// &pf.ast, +// my_ref_node, +// &parent_name, +// &mut library_files, +// ); +// for package in global_packages { +// pickle.add_dependency_relation(package, &parent_name); +// } +// } +// // Interface Declaration. +// RefNode::InterfaceDeclaration(x) => { +// let parent_id = unwrap_node!(x, InterfaceIdentifier).unwrap(); +// let (parent_name, _) = get_identifier(&pf.ast, parent_id); + +// let my_ref_node: RefNode = x.into(); +// pickle.find_and_register_instantiations( +// &pf.ast, +// my_ref_node, +// &parent_name, +// &mut library_files, +// ); +// for package in global_packages { +// pickle.add_dependency_relation(package, &parent_name); +// } +// } +// // Package declarations. +// RefNode::PackageDeclaration(x) => { +// let parent_id = unwrap_node!(x, PackageIdentifier).unwrap(); +// let (parent_name, _) = get_identifier(&pf.ast, parent_id); + +// let my_ref_node: RefNode = x.into(); +// pickle.find_and_register_instantiations( +// &pf.ast, +// my_ref_node, +// &parent_name, +// &mut library_files, +// ); +// for package in global_packages { +// pickle.add_dependency_relation(package, &parent_name); +// } +// } +// _ => (), +// } +// } +// } + +// syntax_trees.extend(library_files); +// write!( +// out, +// "// Compiled by morty-{} / {}\n\n", +// env!("CARGO_PKG_VERSION"), +// Local::now() +// ) +// .unwrap(); + +// if let Some(top) = top_module { +// if propagate_defines { +// warn!( +// "Pickle might be non-functional as some files can be excluded due to use of --top={}.\ +// \n\tThis might lead to required components being excluded. Use at your own risk!!!", +// top +// ); +// } +// pickle.prune_graph(top)?; +// } + +// let needed_files = pickle +// .module_file_map +// .clone() +// .into_values() +// .collect::>(); + +// // Emit the pickled source files. +// for pf in &syntax_trees { +// if top_module.is_some() && !needed_files.contains(&pf.path) { +// continue; +// } +// // For each file, start with a clean replacement table. +// pickle.replace_table.clear(); +// // Iterate again and check for usage +// for node in &pf.ast { +// match node { +// // Instantiations, end-labels. +// RefNode::ModuleIdentifier(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// pickle.register_usage(&pf.ast, id); +// } +// // Interface identifier. +// RefNode::InterfaceIdentifier(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// pickle.register_usage(&pf.ast, id); +// } +// // Package Qualifier (i.e., explicit package constants). +// RefNode::ClassScope(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// pickle.register_usage(&pf.ast, id); +// } +// // Package Import. +// RefNode::PackageIdentifier(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// pickle.register_usage(&pf.ast, id); +// } +// // Check whether we want to exclude the given module from the file sources. +// RefNode::ModuleDeclarationAnsi(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// pickle.register_exclude(&pf.ast, id, Locate::try_from(x).unwrap()) +// } +// RefNode::ModuleDeclarationNonansi(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// pickle.register_exclude(&pf.ast, id, Locate::try_from(x).unwrap()) +// } +// RefNode::InterfaceDeclaration(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// pickle.register_exclude(&pf.ast, id, Locate::try_from(x).unwrap()) +// } +// RefNode::PackageDeclaration(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// pickle.register_exclude(&pf.ast, id, Locate::try_from(x).unwrap()) +// } +// RefNode::TimeunitsDeclaration(x) => { +// let loc = Locate::try_from(x).unwrap(); +// if remove_timeunits { +// pickle +// .replace_table +// .push((loc.offset, loc.len, "".to_string())); +// } +// } +// _ => (), +// } +// } + +// // Find macros to be removed +// let mut new_replace_table = Vec::new(); + +// if !keep_defines { +// for node in &pf.ast { +// match node { +// RefNode::TextMacroDefinition(x) => { +// let loc = Locate::try_from(x).unwrap(); +// new_replace_table.push((loc.offset, loc.len, "".to_string())); +// } +// _ => (), +// } +// } +// } + +// new_replace_table.append(&mut pickle.replace_table); + +// // sort replace table +// new_replace_table.sort_by(|a, b| a.0.cmp(&b.0)); + +// // Error on overlapping -> correct overlapping! +// for i in 0..new_replace_table.len() - 1 { +// if new_replace_table[i].0 + new_replace_table[i].1 > new_replace_table[i + 1].0 { +// eprintln!( +// "Offset error, please contact Michael\n{:?}", +// new_replace_table[i] +// ); +// } +// } + +// // Replace according to `replace_table`. +// // Apply the replacements. +// debug!("Replace Table: {:?}", new_replace_table); +// let mut pos = 0; +// for (offset, len, repl) in new_replace_table.iter() { +// // Because we are partially stripping modules it can be the case that we don't need to apply some of the upcoming replacements. +// if pos > *offset { +// continue; +// } +// trace!("Replacing: {},{}, {}", offset, len, repl); +// write!(out, "{}", &pf.source[pos..*offset]).unwrap(); +// write!(out, "{}", repl).unwrap(); +// pos = offset + len; +// } +// write!(out, "{}", &pf.source[pos..]).unwrap(); +// // Make sure that each file ends with a newline. +// if !pf.source.ends_with('\n') { +// writeln!(out).unwrap(); +// } +// } + +// Ok(pickle) +// } + +// pub fn build_syntax_tree( +// file_list: &Vec, +// strip_comments: bool, +// ignore_unparseable: bool, +// propagate_defines: bool, +// force_sequential: bool, +// ) -> Result> { +// // Parse the input files. +// let mut syntax_trees = vec![]; + +// let mut internal_defines: Defines = HashMap::new(); + +// for bundle in file_list { +// let bundle_include_dirs: Vec<_> = bundle.include_dirs.iter().map(Path::new).collect(); + +// if propagate_defines { +// internal_defines.extend(defines_to_sv_parser(&bundle.defines)); +// } else { +// internal_defines = defines_to_sv_parser(&bundle.defines); +// } + +// // For each file in the file bundle preprocess and parse it. +// // Use a neat trick of `collect` here, which allows you to collect a +// // `Result` iterator into a `Result>`, i.e. bubbling up the +// // error. +// let v = if force_sequential | propagate_defines { +// let tmp = bundle.files.iter().map(|filename| -> Result<_> { +// let pf = parse_file( +// filename, +// &bundle_include_dirs, +// &internal_defines, +// strip_comments, +// )?; +// if propagate_defines { +// internal_defines.extend(pf.defines.clone()); +// } +// Ok(pf) +// }); +// if ignore_unparseable { +// tmp.filter_map(|r| r.map_err(|e| warn!("Continuing with {:?}", e)).ok()) +// .collect() +// } else { +// tmp.collect::>>()? +// } +// } else { +// let tmp = bundle.files.par_iter().map(|filename| -> Result<_> { +// parse_file( +// filename, +// &bundle_include_dirs, +// &internal_defines, +// strip_comments, +// ) +// }); +// if ignore_unparseable { +// tmp.filter_map(|r| r.map_err(|e| warn!("Continuing with {:?}", e)).ok()) +// .collect() +// } else { +// tmp.collect::>>()? +// } +// }; +// syntax_trees.extend(v); +// } + +// Ok(syntax_trees) +// } + +// pub fn just_preprocess(syntax_trees: Vec, mut out: Box) -> Result<()> { +// write!( +// out, +// "// Compiled by morty-{} / {}\n\n", +// env!("CARGO_PKG_VERSION"), +// Local::now() +// ) +// .unwrap(); +// for pf in syntax_trees { +// eprintln!("{}:", pf.path); +// writeln!(out, "{:}", pf.source).unwrap(); +// } +// Ok(()) +// } + +// pub fn build_doc(syntax_trees: Vec, dir: &str) -> Result<()> { +// let doc = doc::Doc::new(&syntax_trees); +// let mut html = doc::Renderer::new(Path::new(dir)); +// html.render(&doc)?; +// Ok(()) +// } + +// pub fn write_manifest( +// manifest_file: &str, +// pickle: Pickle, +// file_list: Vec, +// include_dirs: Vec, +// defines: HashMap>, +// top_module: Option<&String>, +// ) -> Result<()> { +// let mut undef_modules = Vec::new(); + +// // find undefined modules +// for name in &pickle.inst_table { +// if !pickle.rename_table.contains_key(name) { +// undef_modules.push(name.to_string()); +// } +// } + +// let mut top_modules = Vec::new(); + +// // find top modules +// match top_module { +// Some(x) => { +// top_modules.push(pickle.rename_table[x].to_string()); +// } +// None => { +// for new_name in pickle.rename_table.values() { +// if !pickle.inst_table.contains(new_name) { +// top_modules.push(new_name.to_string()); +// } +// } +// } +// } + +// let mut base_files = Vec::new(); +// let mut bundles = Vec::new(); +// let pickled_files = pickle +// .module_file_map +// .clone() +// .into_values() +// .collect::>(); +// for mut bundle in file_list { +// if bundle.include_dirs == include_dirs && bundle.defines == defines { +// base_files.extend(bundle.files.clone()); +// if top_module.is_some() { +// base_files.retain(|v| pickled_files.clone().contains(v)); +// } +// } else { +// if top_module.is_some() { +// bundle.files.retain(|v| pickled_files.contains(v)); +// } +// if !bundle.files.is_empty() { +// bundles.push(bundle); +// } +// } +// } +// base_files.extend(pickle.used_libs); +// if !base_files.is_empty() { +// bundles.push(FileBundle { +// include_dirs, +// export_incdirs: HashMap::new(), +// defines, +// files: base_files, +// }); +// } + +// let json = serde_json::to_string_pretty(&Manifest { +// sources: bundles, +// tops: top_modules, +// undefined: undef_modules, +// }) +// .unwrap(); + +// let path = Path::new(manifest_file); +// let mut out = Box::new(BufWriter::new(File::create(&path).unwrap())) as Box; +// writeln!(out, "{}", json).unwrap(); + +// Ok(()) +// } + +// /// Write module graph to file +// pub fn write_dot_graph(pickle: &Pickle, graph_file: &str) -> Result<()> { +// let path = Path::new(graph_file); +// let mut out = Box::new(BufWriter::new(File::create(&path).unwrap())) as Box; +// writeln!( +// out, +// "{:?}", +// petgraph::dot::Dot::with_config( +// &pickle.module_graph, +// &[petgraph::dot::Config::EdgeNoLabel] +// ) +// ) +// .unwrap(); +// Ok(()) +// } + +// /// Struct containing information about +// /// what should be pickled and how. +// #[derive(Debug)] +// pub struct Pickle<'a> { +// /// Optional name prefix. +// pub prefix: Option<&'a String>, +// /// Optional name suffix. +// pub suffix: Option<&'a String>, +// /// Declarations which are excluded from re-naming. +// pub exclude_rename: HashSet<&'a String>, +// /// Declarations which are excluded from the pickled sources. +// pub exclude: HashSet<&'a String>, +// /// Table containing thing that should be re-named. +// pub rename_table: HashMap, +// /// Locations of text which should be replaced. +// pub replace_table: Vec<(usize, usize, String)>, +// /// A set of instantiated modules. +// pub inst_table: HashSet, +// /// Information for library files +// pub libs: LibraryBundle, +// /// List of library files used during parsing. +// pub used_libs: Vec, +// /// Module hierarchy graph +// pub module_graph: Graph, +// /// Map for module names to graph nodes +// pub module_graph_nodes: HashMap, +// /// Map module name to declaration file +// pub module_file_map: HashMap, +// } + +// impl<'a> Pickle<'a> { +// pub fn new( +// prefix: Option<&'a String>, +// suffix: Option<&'a String>, +// exclude_rename: HashSet<&'a String>, +// exclude: HashSet<&'a String>, +// libs: LibraryBundle, +// ) -> Self { +// Self { +// prefix, +// suffix, +// exclude_rename, +// exclude, +// // Create a rename table. +// rename_table: HashMap::new(), +// replace_table: vec![], +// inst_table: HashSet::new(), +// libs, +// used_libs: vec![], +// // Create graph. +// module_graph: Graph::new(), +// module_graph_nodes: HashMap::new(), +// module_file_map: HashMap::new(), +// } +// } + +// /// Register a declaration such as a package or module. +// pub fn register_declaration(&mut self, syntax_tree: &SyntaxTree, id: RefNode, file: String) { +// let (module_name, loc) = get_identifier(syntax_tree, id); +// info!("module_name: {:?}", module_name); +// self.module_graph_nodes.insert( +// module_name.clone(), +// self.module_graph.add_node(module_name.clone()), +// ); +// self.module_file_map.insert(module_name.clone(), file); +// if self.exclude_rename.contains(&module_name) || self.exclude.contains(&module_name) { +// return; +// } +// let mut new_name = module_name.clone(); +// if let Some(prefix) = self.prefix { +// new_name = format!("{}{}", prefix, new_name); +// } +// if let Some(suffix) = self.suffix { +// new_name = format!("{}{}", new_name, suffix); +// } +// debug!("Declaration `{}`: {:?}", module_name, loc); +// self.rename_table.insert(module_name, new_name); +// } + +// pub fn register_instantiation(&mut self, syntax_tree: &SyntaxTree, id: RefNode) { +// let (inst_name, _) = get_identifier(syntax_tree, id.clone()); +// self.inst_table.insert(inst_name.clone()); + +// if let Some((parent_name, _)) = get_calling_module(syntax_tree, id) { +// self.add_dependency_relation(&inst_name, &parent_name); +// } +// } + +// pub fn register_instantiation_with_parent( +// &mut self, +// syntax_tree: &SyntaxTree, +// id: RefNode, +// parent_name: &str, +// ) { +// let (inst_name, _) = get_identifier(syntax_tree, id.clone()); +// self.inst_table.insert(inst_name.clone()); + +// self.add_dependency_relation(&inst_name, parent_name); +// } + +// pub fn add_dependency_relation(&mut self, inst_name: &str, parent_name: &str) { +// if !self.module_graph_nodes.contains_key(inst_name) { +// self.module_graph_nodes.insert( +// inst_name.to_string(), +// self.module_graph.add_node(inst_name.to_string()), +// ); +// } +// self.module_graph.update_edge( +// self.module_graph_nodes[parent_name], +// self.module_graph_nodes[inst_name], +// (), +// ); +// } + +// pub fn find_and_register_instantiations( +// &mut self, +// syntax_tree: &SyntaxTree, +// id: RefNode, +// parent_name: &str, +// library_files: &mut Vec, +// ) { +// for node in id { +// match node { +// RefNode::ModuleInstantiation(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// self.register_instantiation_with_parent(syntax_tree, id.clone(), parent_name); + +// let (inst_name, _) = get_identifier(syntax_tree, id.clone()); +// if !self.rename_table.contains_key(&inst_name) { +// info!("Could not find {}, checking libraries...", &inst_name); +// self.load_library_module(&inst_name, library_files); +// } +// } +// RefNode::PackageImportItem(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// self.register_instantiation_with_parent(syntax_tree, id.clone(), parent_name); + +// let (inst_name, _) = get_identifier(syntax_tree, id); +// if !self.rename_table.contains_key(&inst_name) { +// info!("Could not find {}, checking libraries...", &inst_name); +// self.load_library_module(&inst_name, library_files); +// } +// } +// RefNode::PackageScope(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// self.register_instantiation_with_parent(syntax_tree, id.clone(), parent_name); + +// let (inst_name, _) = get_identifier(syntax_tree, id); +// if !self.rename_table.contains_key(&inst_name) { +// info!("Could not find {}, checking libraries...", &inst_name); +// self.load_library_module(&inst_name, library_files); +// } +// } +// RefNode::InterfacePortHeader(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// self.register_instantiation_with_parent(syntax_tree, id.clone(), parent_name); + +// let (inst_name, _) = get_identifier(syntax_tree, id); +// if !self.rename_table.contains_key(&inst_name) { +// info!("Could not find {}, checking libraries...", &inst_name); +// self.load_library_module(&inst_name, library_files); +// } +// } +// RefNode::ClassScope(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// self.register_instantiation_with_parent(syntax_tree, id.clone(), parent_name); + +// let (inst_name, _) = get_identifier(syntax_tree, id); +// if !self.rename_table.contains_key(&inst_name) { +// info!("Could not find {}, checking libraries...", &inst_name); +// self.load_library_module(&inst_name, library_files); +// } +// } +// _ => (), +// } +// } +// } + +// /// Register a usage of the identifier. +// pub fn register_usage(&mut self, syntax_tree: &SyntaxTree, id: RefNode) { +// let (inst_name, loc) = get_identifier(syntax_tree, id); +// let new_name = match self.rename_table.get(&inst_name) { +// Some(x) => x, +// None => return, +// }; +// debug!("Usage `{}`: {:?}", inst_name, loc); +// self.replace_table +// .push((loc.offset, loc.len, new_name.clone())); +// } + +// // Check whether a given declaration should be striped from the sources. +// pub fn register_exclude(&mut self, syntax_tree: &SyntaxTree, id: RefNode, locate: Locate) { +// let (inst_name, loc) = get_identifier(syntax_tree, id); +// if self.exclude.contains(&inst_name) { +// debug!("Exclude `{}`: {:?}", inst_name, loc); +// self.replace_table +// .push((locate.offset, locate.len, "".to_string())); +// } +// } + +// // Load the module with name 'module_name' and append the resulting ParsedFile to 'files'. +// // This function may recursively load other modules if the library uses another library module. +// // If no module is found in the library bundle, this function does nothing. +// pub fn load_library_module(&mut self, module_name: &str, files: &mut Vec) { +// let rm = self.libs.load_module(module_name, &mut self.used_libs); +// match rm { +// Ok(pf) => { +// // register all declarations from this library file. +// for node in &pf.ast { +// match node { +// RefNode::ModuleDeclarationAnsi(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// self.register_declaration(&pf.ast, id, pf.path.clone()); +// } +// RefNode::ModuleDeclarationNonansi(x) => { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// self.register_declaration(&pf.ast, id, pf.path.clone()); +// } +// _ => (), +// } +// } +// // look for all module instantiations +// for node in &pf.ast { +// if let RefNode::ModuleInstantiation(x) = node { +// let id = unwrap_node!(x, SimpleIdentifier).unwrap(); +// self.register_instantiation(&pf.ast, id.clone()); + +// // if this module is undefined, recursively attempt to load a library +// // module for it. +// let (inst_name, _) = get_identifier(&pf.ast, id); +// info!( +// "Instantiation `{}` in library module `{}`", +// &inst_name, &module_name +// ); +// if !self.rename_table.contains_key(&inst_name) { +// info!("load library module {}", &inst_name); +// self.load_library_module(&inst_name, files); +// } +// } +// } +// // add the parsed file to the vector. +// files.push(pf); +// } +// Err(e) => info!("error loading library: {}", e), +// } +// } + +// pub fn prune_graph(&mut self, top_module: &str) -> Result<()> { +// if !self.module_graph_nodes.contains_key(top_module) { +// return Err(anyhow!("Module {} not found!", top_module)); +// } +// let test_weights = dijkstra( +// &self.module_graph, +// self.module_graph_nodes[top_module], +// None, +// |_| 1, +// ); + +// self.module_graph +// .retain_nodes(|_, n| test_weights.contains_key(&n)); +// self.module_graph_nodes +// .retain(|_, v| test_weights.contains_key(v)); + +// let test_keys = self.module_graph_nodes.clone(); +// self.module_file_map +// .retain(|k, _| test_keys.contains_key(k)); + +// self.inst_table.retain(|k| test_keys.contains_key(k)); + +// self.rename_table.retain(|k, _| test_keys.contains_key(k)); + +// Ok(()) +// } +// } // Returns true if this file has a library extension (.v or .sv). pub fn has_libext(p: &Path) -> bool { @@ -829,7 +836,7 @@ pub fn parse_file( let buffer = pp.0.text().to_string(); let syntax_tree = parse_sv_pp(pp.0, pp.1, false).or_else(|err| -> Result<_> { - let printer = Arc::new(Mutex::new(printer::Printer::new())); + let printer = Arc::new(Mutex::new(printer::printer::Printer::new())); let printer = &mut *printer.lock().unwrap(); print_parse_error(printer, &err, false)?; Err(Error::new(err)) @@ -1002,6 +1009,7 @@ impl LibraryBundle { } /// A parsed input file. +// #[derive(Clone)] pub struct ParsedFile { /// The path to the file. pub path: String, @@ -1013,9 +1021,15 @@ pub struct ParsedFile { pub defines: Defines, } +impl fmt::Debug for ParsedFile { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "File: {:?}\n{}", self.path, self.ast) + } +} + #[cfg_attr(tarpaulin, skip)] pub fn print_parse_error( - printer: &mut printer::Printer, + printer: &mut printer::printer::Printer, error: &SvParserError, single: bool, ) -> Result<()> { diff --git a/src/main.rs b/src/main.rs index 42a077c..1f43378 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ #[macro_use] extern crate log; +use crate::pickle::Pickle; use anyhow::Result; use clap::{Arg, ArgAction, Command}; use log::LevelFilter; @@ -20,6 +21,8 @@ use std::process; use morty::*; +mod pickle; + fn main() -> Result<()> { let matches = Command::new(env!("CARGO_PKG_NAME")) .version(clap::crate_version!()) @@ -160,7 +163,7 @@ fn main() -> Result<()> { Arg::new("top_module") .long("top") .value_name("TOP_MODULE") - .help("Top module, strips all unneeded files. May be incompatible with `--propagate_defines`.") + .help("Top module, strips all unneeded files.") .num_args(1), ) .arg( @@ -187,22 +190,21 @@ fn main() -> Result<()> { .arg( Arg::new("propagate_defines") .long("propagate_defines") - .help("Propagate defines from first files to the following files. Enables sequential.") + .help("Propagate defines from first files to the following files.") .num_args(0) .action(ArgAction::SetTrue), ) .arg( - Arg::new("sequential") - .short('q') - .long("sequential") - .help("Enforce sequential processing of files. Slows down performance, but can avoid STACK_OVERFLOW.") + Arg::new("keep_timeunits") + .long("keep_timeunits") + .help("Keeps timeunits declared throughout the design, may result in bad pickles.") .num_args(0) .action(ArgAction::SetTrue), ) .arg( - Arg::new("keep_timeunits") - .long("keep_timeunits") - .help("Keeps timeunits declared throughout the design, may result in bad pickles.") + Arg::new("infer_dot_star") + .long("infer_dot_star") + .help("Infer signals from module declaration and replace .* expressions") .num_args(0) .action(ArgAction::SetTrue), ) @@ -388,14 +390,16 @@ fn main() -> Result<()> { let strip_comments = matches.get_flag("strip_comments"); - let syntax_trees = build_syntax_tree( + let mut pickle = Pickle::new(); + pickle.add_files( &file_list, strip_comments, matches.get_flag("ignore_unparseable"), matches.get_flag("propagate_defines"), - matches.get_flag("sequential"), )?; + pickle.add_libs(library_bundle)?; + let out = match matches.get_one::("output") { Some(file) => { info!("Setting output to `{}`", file); @@ -410,46 +414,68 @@ fn main() -> Result<()> { // Just preprocess. if matches.get_flag("preproc") { - return just_preprocess(syntax_trees, out); + return pickle.just_preprocess(out); } - info!("Finished reading {} source files.", syntax_trees.len()); + info!("Finished reading {} source files.", pickle.all_files.len()); // Emit documentation if requested. if let Some(dir) = matches.get_one::("docdir") { info!("Generating documentation in `{}`", dir); - return build_doc(syntax_trees, dir); + return pickle.build_doc(dir); + } + + pickle.build_graph()?; + + if let Some(top) = matches.get_one::("top_module") { + pickle.prune_graph(top)?; } - let pickle = do_pickle( + if !matches.get_flag("keep_defines") { + pickle.remove_macros()?; + } + + if !matches.get_flag("keep_timeunits") { + pickle.remove_timeunits()?; + } + + if matches.get_flag("infer_dot_star") { + pickle.infer_dot_star()?; + } + + pickle.rename( matches.get_one::("prefix"), matches.get_one::("suffix"), exclude_rename, - exclude, - library_bundle, - syntax_trees, - out, - matches.get_one::("top_module"), - matches.get_flag("keep_defines"), - matches.get_flag("propagate_defines"), - !matches.get_flag("keep_timeunits"), )?; + // TODO: add transforms + // - replace interfaces + // - replace impossible parameters + // - replace types (and uniquify/elaborate) + + if matches.get_one::("top_module").is_some() { + pickle.get_pickle(out, exclude)?; + } else { + pickle.get_classic_pickle(out, exclude)?; + } + if let Some(graph_file) = matches.get_one::("graph_file") { - write_dot_graph(&pickle, graph_file)?; + let graph_path = Path::new(graph_file); + let graph_out = + Box::new(BufWriter::new(File::create(graph_path).unwrap())) as Box; + + pickle.get_dot(graph_out)?; } // if the user requested a manifest we need to compute the information and output it in json // form if let Some(manifest_file) = matches.get_one::("manifest") { - write_manifest( - manifest_file, - pickle, - file_list, - stdin_incdirs, - stdin_defines, - matches.get_one::("top_module"), - )?; + let manifest_path = Path::new(manifest_file); + let manifest_out = + Box::new(BufWriter::new(File::create(manifest_path).unwrap())) as Box; + + pickle.get_manifest(manifest_out, file_list, stdin_incdirs, stdin_defines)?; } Ok(()) diff --git a/src/pickle.rs b/src/pickle.rs new file mode 100644 index 0000000..31af8bf --- /dev/null +++ b/src/pickle.rs @@ -0,0 +1,1008 @@ +// Copyright 2022 Michael Rogenmoser + +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + defines_to_sv_parser, doc, get_identifier, print_parse_error, printer, FileBundle, + LibraryBundle, Manifest, ParsedFile, +}; +use anyhow::{anyhow, Context, Error, Result}; +use chrono::Local; +use petgraph::algo::dijkstra; +use petgraph::graph::{Graph, NodeIndex}; +use petgraph::{Incoming, Outgoing}; +use std::collections::{HashMap, HashSet}; +use std::convert::TryFrom; +use std::io::Write; +use std::path::Path; +use std::sync::{Arc, Mutex}; +use sv_parser::{parse_sv_pp, preprocess, unwrap_locate, unwrap_node, Defines, Locate, RefNode}; + +/// Struct used for transformations +#[derive(Debug)] +pub struct Pickle { + /// All files + pub all_files: Vec, + /// libraries + pub libs: Option, + /// Module hierarchy graph + pub module_graph: Graph, + /// Map for module names to graph nodes + pub module_graph_nodes: HashMap, + /// Map of all declarations + pub declarations: HashMap, + /// Map of all usages + pub usages: HashMap>, + /// list of replacements + pub replace_table: Vec<(/* file id */ usize, Locate, String)>, +} + +impl Default for Pickle { + fn default() -> Self { + Self::new() + } +} + +impl Pickle { + pub fn new() -> Self { + Self { + all_files: Vec::new(), + libs: None, + module_graph: Graph::new(), + module_graph_nodes: HashMap::new(), + declarations: HashMap::new(), + usages: HashMap::new(), + replace_table: Vec::new(), + } + } + + /// Parse a single file and add it to all_files + pub fn parse_file( + &mut self, + filename: &str, + bundle_include_dirs: &[&Path], + bundle_defines: &Defines, + strip_comments: bool, + ) -> Result { + info!("Adding {:?}", filename); + + // Preprocess the verilog files. + let pp = preprocess( + filename, + bundle_defines, + bundle_include_dirs, + strip_comments, + false, + ) + .with_context(|| format!("Failed to preprocess `{}`", filename))?; + + let buffer = pp.0.text().to_string(); + let syntax_tree = parse_sv_pp(pp.0, pp.1, false).or_else(|err| -> Result<_> { + let printer = Arc::new(Mutex::new(printer::printer::Printer::new())); + let printer = &mut *printer.lock().unwrap(); + print_parse_error(printer, &err, false)?; + Err(Error::new(err)) + })?; + + self.all_files.push(ParsedFile { + path: String::from(filename), + source: buffer, + ast: syntax_tree.0, + defines: syntax_tree.1.clone(), + }); + + Ok(syntax_tree.1) + } + + /// Parse and add multiple files + pub fn add_files( + &mut self, + file_list: &Vec, + strip_comments: bool, + ignore_unparseable: bool, + propagate_defines: bool, + ) -> Result<()> { + let mut internal_defines: Defines = HashMap::new(); + + for bundle in file_list { + let bundle_include_dirs: Vec<_> = bundle.include_dirs.iter().map(Path::new).collect(); + + if propagate_defines { + internal_defines.extend(defines_to_sv_parser(&bundle.defines)); + } else { + internal_defines = defines_to_sv_parser(&bundle.defines); + } + + let v = bundle.files.iter().map(|filename| -> Result<_> { + let pf_defines = self.parse_file( + filename, + &bundle_include_dirs, + &internal_defines, + strip_comments, + )?; + if propagate_defines { + internal_defines.extend(pf_defines); + } + Ok(()) + }); + + if ignore_unparseable { + v.filter_map(|r| r.map_err(|e| warn!("Continuing with {:?}", e)).ok()) + .for_each(drop); + } else { + v.collect::>>()?; + }; + } + + Ok(()) + } + + /// Add Library Bundle + pub fn add_libs(&mut self, libs: LibraryBundle) -> Result<()> { + self.libs = Some(libs); + + Ok(()) + } + + /// Helper function to register all declarations + fn register_declarations(&mut self) -> Result<()> { + for i in 0..self.all_files.len() { + let pf = &self.all_files[i]; + for node in &pf.ast { + match node { + // Module declarations. + RefNode::ModuleDeclaration(x) => { + // unwrap_node! gets the nearest ModuleIdentifier from x + let id = unwrap_node!(x, ModuleIdentifier).unwrap(); + let (module_name, _loc) = get_identifier(&pf.ast, id); + info!("module_name: {:?}", module_name); + + if self.declarations.contains_key(&module_name) { + return Err(anyhow!("Module {} declared mutliple times!", module_name)); + } + self.module_graph_nodes.insert( + module_name.clone(), + self.module_graph.add_node(module_name.clone()), + ); + self.declarations.insert( + module_name.clone(), + (SVConstructType::Module, i, Locate::try_from(x).unwrap()), + ); + self.usages.insert(module_name, Vec::new()); + } + // Interface Declaration. + RefNode::InterfaceDeclaration(x) => { + let id = unwrap_node!(x, InterfaceIdentifier).unwrap(); + let (module_name, _loc) = get_identifier(&pf.ast, id); + info!("module_name: {:?}", module_name); + + if self.declarations.contains_key(&module_name) { + return Err(anyhow!( + "Interface {} declared mutliple times!", + module_name + )); + } + self.module_graph_nodes.insert( + module_name.clone(), + self.module_graph.add_node(module_name.clone()), + ); + self.declarations.insert( + module_name.clone(), + (SVConstructType::Interface, i, Locate::try_from(x).unwrap()), + ); + self.usages.insert(module_name, Vec::new()); + } + // Package declarations. + RefNode::PackageDeclaration(x) => { + let id = unwrap_node!(x, PackageIdentifier).unwrap(); + let (module_name, _loc) = get_identifier(&pf.ast, id); + info!("module_name: {:?}", module_name); + + if self.declarations.contains_key(&module_name) { + return Err(anyhow!( + "Package {} declared mutliple times!", + module_name + )); + } + self.module_graph_nodes.insert( + module_name.clone(), + self.module_graph.add_node(module_name.clone()), + ); + self.declarations.insert( + module_name.clone(), + (SVConstructType::Package, i, Locate::try_from(x).unwrap()), + ); + self.usages.insert(module_name, Vec::new()); + } + _ => (), + } + } + } + Ok(()) + } + + /// Load a module from the library + fn _load_library_module(&self, module_name: &str) -> Result<()> { + let mut used_libs = vec![]; + if let Some(libs) = &self.libs { + let rm = libs.load_module(module_name, &mut used_libs); + match rm { + Ok(_pf) => { + unimplemented!(); + // TODO: register all declarations and instantiations + // TODO: add file to all_files + // TODO: check how used_libs is supposed to be used + } + Err(e) => info!("error loading library: {}", e), + } + } + + Ok(()) + } + + /// Helper function to find and register instantiations (when finding usages) + fn find_and_register_instantiations( + &self, + file_id: usize, + parent_node: RefNode, + ) -> Vec<(String, SVConstructType, Locate)> { + let mut mapping = Vec::new(); + + for node in parent_node { + match node { + RefNode::ModuleInstantiation(x) => { + let id = unwrap_node!(x, SimpleIdentifier).unwrap(); + let (inst_name, _) = get_identifier(&self.all_files[file_id].ast, id.clone()); + mapping.push(( + inst_name.clone(), + SVConstructType::Module, + Locate::try_from(x).unwrap(), + )); + } + RefNode::PackageImportItem(x) => { + let id = unwrap_node!(x, SimpleIdentifier).unwrap(); + let (inst_name, _) = get_identifier(&self.all_files[file_id].ast, id.clone()); + mapping.push(( + inst_name, + SVConstructType::Package, + Locate::try_from(x).unwrap(), + )); + } + RefNode::PackageScope(x) => { + let id = unwrap_node!(x, SimpleIdentifier).unwrap(); + let (inst_name, _) = get_identifier(&self.all_files[file_id].ast, id.clone()); + mapping.push(( + inst_name, + SVConstructType::Package, + Locate::try_from(x).unwrap(), + )); + } + RefNode::InterfacePortHeader(x) => { + let id = unwrap_node!(x, SimpleIdentifier).unwrap(); + let (inst_name, _) = get_identifier(&self.all_files[file_id].ast, id.clone()); + mapping.push(( + inst_name, + SVConstructType::Interface, + Locate::try_from(x).unwrap(), + )); + } + RefNode::InterfaceInstantiation(x) => { + let id = unwrap_node!(x, SimpleIdentifier).unwrap(); + let (inst_name, _) = get_identifier(&self.all_files[file_id].ast, id.clone()); + mapping.push(( + inst_name, + SVConstructType::Interface, + Locate::try_from(x).unwrap(), + )); + } + RefNode::ClassScope(x) => { + let id = unwrap_node!(x, SimpleIdentifier).unwrap(); + let (inst_name, _) = get_identifier(&self.all_files[file_id].ast, id.clone()); + mapping.push(( + inst_name, + SVConstructType::Package, + Locate::try_from(x).unwrap(), + )); + } + _ => {} + } + } + mapping + } + + /// Helper function to register all module usages in the source files + fn register_usages(&mut self) -> Result<()> { + for i in 0..self.all_files.len() { + let pf = &self.all_files[i]; + + // global package import + let global_packages = &pf + .ast + .into_iter() + .filter_map(|node| { + if let RefNode::DescriptionPackageItem(x) = node { + if let Some(package_import) = unwrap_node!(x, PackageImportDeclaration) { + let (name, _loc) = get_identifier( + &pf.ast, + unwrap_node!(package_import, SimpleIdentifier).unwrap(), + ); + warn!( + "Global package import in {}:\n\t{}", + &pf.path, + &pf.source[Locate::try_from(x).unwrap().offset + ..(Locate::try_from(x).unwrap().offset + + Locate::try_from(x).unwrap().len)] + ); + Some((name, Locate::try_from(x).unwrap())) + } else { + None + } + } else { + None + } + }) + .collect::>(); + // register usages of global packages + for package in global_packages { + if !self.module_graph_nodes.contains_key(&package.0) { + self.module_graph_nodes.insert( + package.0.clone(), + self.module_graph.add_node(package.0.clone()), + ); + self.usages.insert(package.0.clone(), Vec::new()); + } + if let Some(decl) = self.declarations.get(&package.0) { + if decl.0 != SVConstructType::Package { + warn!( + "Possibly type mismatch for {}: declaring {:?}, instantiating {:?}", + &package.0, + decl.0, + SVConstructType::Package + ); + } + } + self.usages + .get_mut(&package.0) + .unwrap() + .push((i, package.1)); + } + + for node in &pf.ast { + match node { + // Module declarations. + RefNode::ModuleDeclaration(x) => { + // unwrap_node! gets the nearest ModuleIdentifier from x + let parent_id = unwrap_node!(x, ModuleIdentifier).unwrap(); + let (parent_name, _) = get_identifier(&pf.ast, parent_id).clone(); + let instantiations = self.find_and_register_instantiations(i, x.into()); + + for inst in instantiations { + // Add nodes in case they are not present + if !self.module_graph_nodes.contains_key(&inst.0) { + self.module_graph_nodes.insert( + inst.0.clone(), + self.module_graph.add_node(inst.0.clone()), + ); + self.usages.insert(inst.0.clone(), Vec::new()); + } + if let Some(decl) = self.declarations.get(&inst.0) { + if decl.0 != inst.1 { + warn!("Possibly type mismatch for {}: declaring {:?}, instantiating {:?}", &inst.0, decl.0, inst.1); + } + } + self.usages.get_mut(&inst.0).unwrap().push((i, inst.2)); + + self.module_graph.update_edge( + self.module_graph_nodes[&parent_name], + self.module_graph_nodes[&inst.0], + (), + ); + } + for package in global_packages { + self.module_graph.update_edge( + self.module_graph_nodes[&parent_name], + self.module_graph_nodes[&package.0], + (), + ); + } + } + // Interface Declaration. + RefNode::InterfaceDeclaration(x) => { + // unwrap_node! gets the nearest ModuleIdentifier from x + let parent_id = unwrap_node!(x, InterfaceIdentifier).unwrap(); + let (parent_name, _) = get_identifier(&pf.ast, parent_id); + let instantiations = self.find_and_register_instantiations(i, x.into()); + + for inst in instantiations { + // Add nodes in case they are not present + if !self.module_graph_nodes.contains_key(&inst.0) { + self.module_graph_nodes.insert( + inst.0.clone(), + self.module_graph.add_node(inst.0.clone()), + ); + self.usages.insert(inst.0.clone(), Vec::new()); + } + if let Some(decl) = self.declarations.get(&inst.0) { + if decl.0 != inst.1 { + warn!("Possibly type mismatch for {}: declaring {:?}, instantiating {:?}", &inst.0, decl.0, inst.1); + } + } + self.usages.get_mut(&inst.0).unwrap().push((i, inst.2)); + + self.module_graph.update_edge( + self.module_graph_nodes[&parent_name], + self.module_graph_nodes[&inst.0], + (), + ); + } + for package in global_packages { + self.module_graph.update_edge( + self.module_graph_nodes[&parent_name], + self.module_graph_nodes[&package.0], + (), + ); + } + } + // Package declarations. + RefNode::PackageDeclaration(x) => { + // unwrap_node! gets the nearest ModuleIdentifier from x + let parent_id = unwrap_node!(x, PackageIdentifier).unwrap(); + let (parent_name, _) = get_identifier(&pf.ast, parent_id); + let instantiations = self.find_and_register_instantiations(i, x.into()); + + for inst in instantiations { + // Add nodes in case they are not present + if !self.module_graph_nodes.contains_key(&inst.0) { + self.module_graph_nodes.insert( + inst.0.clone(), + self.module_graph.add_node(inst.0.clone()), + ); + self.usages.insert(inst.0.clone(), Vec::new()); + } + if let Some(decl) = self.declarations.get(&inst.0) { + if decl.0 != inst.1 { + warn!("Possibly type mismatch for {}: declaring {:?}, instantiating {:?}", &inst.0, decl.0, inst.1); + } + } + self.usages.get_mut(&inst.0).unwrap().push((i, inst.2)); + + self.module_graph.update_edge( + self.module_graph_nodes[&parent_name], + self.module_graph_nodes[&inst.0], + (), + ); + } + for package in global_packages { + self.module_graph.update_edge( + self.module_graph_nodes[&parent_name], + self.module_graph_nodes[&package.0], + (), + ); + } + } + _ => {} + } + } + } + Ok(()) + } + + /// Build Graph + pub fn build_graph(&mut self) -> Result<()> { + // Register declarations + self.register_declarations()?; + + // Register usages + self.register_usages()?; + + // TODO: find additional declarations for undeclared but used modules in library + + Ok(()) + } + + /// Prune Graph + pub fn prune_graph(&mut self, top_module: &str) -> Result<()> { + if !self.module_graph_nodes.contains_key(top_module) { + return Err(anyhow!("Module {} not found!", top_module)); + } + let test_weights = dijkstra( + &self.module_graph, + self.module_graph_nodes[top_module], + None, + |_| 1, + ); + + self.module_graph + .retain_nodes(|_, n| test_weights.contains_key(&n)); + self.refactor_graph_nodes(None, None)?; + + Ok(()) + } + + /// Helper function to fix NodeIndex references + fn refactor_graph_nodes( + &mut self, + graph: Option<&Graph>, + nodes: Option<&mut HashMap>, + ) -> Result<()> { + let int_graph = match graph { + Some(x) => x, + None => &self.module_graph, + }; + + let int_nodes = match nodes { + Some(x) => x, + None => &mut self.module_graph_nodes, + }; + + let mut new_nodes = HashMap::::new(); + + for node in int_graph.node_indices() { + new_nodes.insert(int_graph[node].clone(), node); + } + + *int_nodes = new_nodes; + + Ok(()) + } + + /// Add replacements to remove macro definitions + pub fn remove_macros(&mut self) -> Result<()> { + for i in 0..self.all_files.len() { + let pf = &self.all_files[i]; + for node in &pf.ast { + if let RefNode::TextMacroDefinition(x) = node { + let loc = Locate::try_from(x).unwrap(); + self.replace_table.push((i, loc, "".to_string())); + } + } + } + + Ok(()) + } + + /// Add replacements to remove timeunit and timeprecision + pub fn remove_timeunits(&mut self) -> Result<()> { + for i in 0..self.all_files.len() { + let pf = &self.all_files[i]; + for node in &pf.ast { + if let RefNode::TimeunitsDeclaration(x) = node { + let loc = Locate::try_from(x).unwrap(); + self.replace_table.push((i, loc, "".to_string())); + } + } + } + + Ok(()) + } + + /// Add replacements to remove timeunit and timeprecision + pub fn infer_dot_star(&mut self) -> Result<()> { + for i in 0..self.all_files.len() { + let pf = &self.all_files[i]; + + for node in &pf.ast { + if let RefNode::ModuleInstantiation(x) = node { + let asterisk = unwrap_node!(x, NamedPortConnectionAsterisk); + if asterisk.is_some() { + let id = unwrap_node!(x, ModuleIdentifier).unwrap(); + let (inst_name, _) = get_identifier(&pf.ast, id).clone(); + let list_of_ports_node = + unwrap_node!(x, ListOfPortConnectionsNamed).unwrap(); + let mut already_added_ports = Vec::new(); + for port in list_of_ports_node { + if let RefNode::PortIdentifier(p) = port { + // we need this check to be sure that it is a port and not an + // expression + let port_node = unwrap_node!(p, SimpleIdentifier).unwrap(); + let (port_name, _) = get_identifier(&pf.ast, port_node.clone()); + already_added_ports.push(port_name.clone()); + } + } + let module_declaration = self.get_node_from_locate( + self.declarations[&inst_name].1, + self.declarations[&inst_name].2, + )?; + let mut all_port_of_module = Vec::new(); + for n in module_declaration { + match n { + RefNode::PortDeclaration(p) => { + let id = unwrap_node!(p, PortIdentifier).unwrap(); + let (port_name, _) = get_identifier( + &self.all_files[self.declarations[&inst_name].1].ast, + id, + ); + println!("{}", port_name); + all_port_of_module.push(port_name); + } + RefNode::AnsiPortDeclaration(p) => { + let id = unwrap_node!(p, PortIdentifier).unwrap(); + let (port_name, _) = get_identifier( + &self.all_files[self.declarations[&inst_name].1].ast, + id, + ); + all_port_of_module.push(port_name); + } + _ => {} + } + } + let mut ports_string = String::new(); + for port in all_port_of_module { + if !already_added_ports.contains(&port) { + ports_string.push_str(&format!(".{}({}), ", port, port)); + } + } + ports_string = ports_string.trim_end_matches(", ").to_string(); + let loc = unwrap_locate!(asterisk.unwrap()).unwrap(); + self.replace_table.push((i, *loc, ports_string)); + } + } + } + } + + Ok(()) + } + + fn get_node_from_locate(&self, file_id: usize, location: Locate) -> Result { + let node = self.all_files[file_id].ast.into_iter().find(|x| + match x { + RefNode::ModuleDeclaration(y) => Locate::try_from(*y).unwrap(), + RefNode::InterfaceDeclaration(y) => Locate::try_from(*y).unwrap(), + RefNode::PackageDeclaration(y) => Locate::try_from(*y).unwrap(), + RefNode::DescriptionPackageItem(y) => Locate::try_from(*y).unwrap(), + RefNode::TextMacroDefinition(y) => Locate::try_from(*y).unwrap(), + RefNode::ModuleInstantiation(y) => Locate::try_from(*y).unwrap(), + RefNode::PackageImportItem(y) => Locate::try_from(*y).unwrap(), + RefNode::PackageScope(y) => Locate::try_from(*y).unwrap(), + RefNode::InterfacePortHeader(y) => Locate::try_from(*y).unwrap(), + RefNode::InterfaceInstantiation(y) => Locate::try_from(*y).unwrap(), + RefNode::ClassScope(y) => Locate::try_from(*y).unwrap(), + _ => Locate { offset: 0, line: 0, len: 0 } + } == location + ); + + match node { + Some(x) => Ok(x), + None => Err(anyhow!( + "Internal error matching the specified location: {:?}", + location + )), + } + } + + /// Add replacements to rename modules with prefix and suffix + pub fn rename( + &mut self, + prefix: Option<&String>, + suffix: Option<&String>, + exclude_rename: HashSet<&String>, + ) -> Result<()> { + info!("Prefixing: {:?}, Suffixing: {:?}", prefix, suffix); + for (name, module) in &self.declarations { + if exclude_rename.contains(name) { + continue; + } + let mut new_string = name.to_string(); + if let Some(ref pre) = prefix { + new_string = format!("{}{}", pre, new_string); + } + if let Some(ref suf) = suffix { + new_string = format!("{}{}", new_string, suf); + } + + let found_node = self.get_node_from_locate(module.1, module.2)?; + let node_named = unwrap_node!( + unwrap_node!( + found_node, + ModuleIdentifier, + PackageIdentifier, + InterfaceIdentifier + ) + .unwrap(), + SimpleIdentifier, + EscapedIdentifier + ) + .unwrap(); + let found_locate = match node_named { + RefNode::SimpleIdentifier(x) => x.nodes.0, + RefNode::EscapedIdentifier(x) => x.nodes.0, + _ => unimplemented!(), + }; + + self.replace_table + .push((module.1, found_locate, new_string.to_string())); + + for (use_file_id, use_locate) in &self.usages[name] { + let use_node = self.get_node_from_locate(*use_file_id, *use_locate)?; + let use_identifier = unwrap_node!( + unwrap_node!( + use_node, + ModuleIdentifier, + InterfaceIdentifier, + ClassScope, + PackageIdentifier + ) + .unwrap(), + SimpleIdentifier, + EscapedIdentifier + ) + .unwrap(); + let use_final_locate = match use_identifier { + RefNode::SimpleIdentifier(x) => x.nodes.0, + RefNode::EscapedIdentifier(x) => x.nodes.0, + _ => unimplemented!(), + }; + self.replace_table + .push((*use_file_id, use_final_locate, new_string.to_string())) + } + } + + Ok(()) + } + + /// Helper function to get string of Locate in a file with needed string replacements + fn get_replaced_string(&self, file_id: usize, location: Locate) -> Result { + let int_offset = location.offset; + let int_len = location.len; + + let mut replacements = self + .replace_table + .iter() + .filter(|x| x.0 == file_id) + .map(|x| (x.1.offset, x.1.len, x.2.clone())) + .filter(|x| x.0 > int_offset) + .map(|x| (x.0 - int_offset, x.1, x.2)) + .filter(|x| x.0 < int_len) + .collect::>(); + + replacements.sort_by(|a, b| a.0.cmp(&b.0)); + + // Error on overlapping -> TODO: figure out how to handle overlapped replacements + for i in 1..replacements.len() { + if replacements[i - 1].0 + replacements[i - 1].1 > replacements[i].0 { + eprintln!( + "Replacement offset error, the selected replacements may not be supported yet\n{:?}", + replacements[i-1] + ); + unimplemented!(); + } + } + + let needed_string = self.all_files[file_id] + .ast + .get_str(&location) + .unwrap() + .to_string(); + + let mut out_string = "".to_owned(); + let mut pos = 0; + + for (offset, len, repl) in replacements.iter() { + info!( + "Replacing {:?} with {:?}", + &needed_string[*offset..*offset + *len], + &repl + ); + out_string.push_str(&needed_string[pos..*offset]); + out_string.push_str(repl); + pos = offset + len; + } + out_string.push_str(&needed_string[pos..]); + if !needed_string.ends_with('\n') { + out_string.push('\n'); + } + + Ok(out_string) + } + + /// Export AST to a pickled file + pub fn get_pickle(&mut self, mut out: Box, exclude: HashSet<&String>) -> Result<()> { + write!( + out, + "// Compiled by morty-{} / {}\n\n", + env!("CARGO_PKG_VERSION"), + Local::now() + ) + .unwrap(); + + let mut internal_graph = self.module_graph.clone(); + let mut internal_nodes = self.module_graph_nodes.clone(); + + let mut keeper_nodes = Vec::new(); + let mut keeper_names = Vec::new(); + for (name, node) in &internal_nodes { + if self.declarations.contains_key(name) { + keeper_nodes.push(*node); + keeper_names.push(name); + } + } + + internal_graph.retain_nodes(|_, n| keeper_nodes.contains(&n)); + self.refactor_graph_nodes(Some(&internal_graph), Some(&mut internal_nodes))?; + + let mut limit = internal_nodes.len(); + while !internal_nodes.is_empty() { + if limit == 0 { + // Can be caused by cyclical module instantiations + return Err(anyhow!( + "Unable to print individual modules, loop not terminating." + )); + } + limit -= 1; + for (name, node) in &internal_nodes { + if internal_graph.neighbors_directed(*node, Outgoing).count() == 0 { + // Remove node from graph + if internal_graph.remove_node(*node).is_none() { + return Err(anyhow!("Unable to remove node {} from graph", name)); + } + + if self.declarations.contains_key(name) && !exclude.contains(name) { + writeln!( + out, + "{:}", + self.get_replaced_string( + self.declarations[name].1, + self.declarations[name].2 + )? + )?; + } + break; + } + } + self.refactor_graph_nodes(Some(&internal_graph), Some(&mut internal_nodes))?; + } + + Ok(()) + } + + /// Export AST to a pickled file + pub fn get_classic_pickle( + &mut self, + mut out: Box, + _exclude: HashSet<&String>, + ) -> Result<()> { + write!( + out, + "// Compiled by morty-{} / {}\n\n", + env!("CARGO_PKG_VERSION"), + Local::now() + ) + .unwrap(); + + for i in 0..self.all_files.len() { + // I don't think exclude is handled properly in the classic version... + + for node in &self.all_files[i].ast { + if let RefNode::SourceText(x) = node { + let source_locate = Locate::try_from(x).unwrap(); + writeln!(out, "{:}", self.get_replaced_string(i, source_locate)?)?; + } + } + } + + Ok(()) + } + + /// get .dot file for the module graph + pub fn get_dot(&self, mut out: Box) -> Result<()> { + writeln!( + out, + "{:?}", + petgraph::dot::Dot::with_config( + &self.module_graph, + &[petgraph::dot::Config::EdgeNoLabel] + ) + )?; + + Ok(()) + } + + /// get manifest file + pub fn get_manifest( + &self, + mut out: Box, + file_list: Vec, + include_dirs: Vec, + defines: HashMap>, + ) -> Result<()> { + let mut undef_modules = Vec::new(); + // find undefined modules + for module in self.module_graph_nodes.keys() { + if !self.declarations.contains_key(module) { + undef_modules.push(module.to_string()); + } + } + + let mut top_modules = Vec::new(); + // find top modules + for (name, node) in &self.module_graph_nodes { + if self + .module_graph + .neighbors_directed(*node, Incoming) + .count() + == 0 + { + top_modules.push(name.to_string()); + } + } + + let mut base_files = Vec::new(); + let mut bundles = Vec::::new(); + let mut needed_files = HashSet::new(); + for module in self.declarations.values() { + needed_files.insert(self.all_files[module.1].path.clone()); + } + for mut bundle in file_list { + if bundle.include_dirs == include_dirs && bundle.defines == defines { + base_files.extend(bundle.files.clone()); + // May need to disable the following for backwards compatibility + base_files.retain(|v| needed_files.clone().contains(v)); + } else { + // May need to disable the following for backwards compatibility + bundle.files.retain(|v| needed_files.clone().contains(v)); + if !bundle.files.is_empty() { + bundles.push(bundle); + } + } + } + // TODO: add libs + // base_files.extend() + + if !base_files.is_empty() { + bundles.push(FileBundle { + include_dirs, + export_incdirs: HashMap::new(), + defines, + files: base_files, + }); + } + + let json = serde_json::to_string_pretty(&Manifest { + sources: bundles, + tops: top_modules, + undefined: undef_modules, + }) + .unwrap(); + + writeln!(out, "{}", json)?; + + Ok(()) + } + + /// Function to only preprocess files + pub fn just_preprocess(&self, mut out: Box) -> Result<()> { + write!( + out, + "// Compiled by morty-{} / {}\n\n", + env!("CARGO_PKG_VERSION"), + Local::now() + ) + .unwrap(); + for pf in &self.all_files { + eprintln!("{}:", pf.path); + + for node in &pf.ast { + if let RefNode::SourceText(x) = node { + writeln!(out, "{:}", pf.ast.get_str(x).unwrap())?; + } + } + } + Ok(()) + } + + /// Function to build documentation + pub fn build_doc(&self, dir: &str) -> Result<()> { + let doc = doc::Doc::new(&self.all_files); + let mut html = doc::Renderer::new(Path::new(dir)); + html.render(&doc)?; + Ok(()) + } +} + +#[derive(Debug, PartialEq)] +pub enum SVConstructType { + Module, + Interface, + Package, + _Class, +} diff --git a/src/printer/mod.rs b/src/printer/mod.rs new file mode 100644 index 0000000..3f31bdf --- /dev/null +++ b/src/printer/mod.rs @@ -0,0 +1 @@ +pub mod printer; diff --git a/src/printer.rs b/src/printer/printer.rs similarity index 99% rename from src/printer.rs rename to src/printer/printer.rs index 7d3349d..eb9d7fb 100644 --- a/src/printer.rs +++ b/src/printer/printer.rs @@ -179,6 +179,7 @@ impl Printer { }); } + #[allow(clippy::too_many_arguments)] #[cfg_attr(tarpaulin, skip)] fn print_pretty( &mut self, diff --git a/test/infer_dot_star/expected/expected.sv b/test/infer_dot_star/expected/expected.sv new file mode 100644 index 0000000..f7bea29 --- /dev/null +++ b/test/infer_dot_star/expected/expected.sv @@ -0,0 +1,40 @@ +module top #( + parameter int unsigned TestPara = 4, + parameter int unsigned TestParaOne = 4, + parameter int unsigned TestParaTwo = 4, + parameter int unsigned TestParaThree = 4 +) ( + input logic clk_i, + input logic rst_ni, + + input logic signal_one_i, + output logic signal_two_o, + input logic unused_signal +); + + (* dont_touch *) + submodule #( + .SubPara(TestPara) + ) i_submodule_test ( + .rst_ni(rst_ni), + .clk_i(clk_i), .signal_one_i(signal_one_i), .signal_two_o(signal_two_o) + ); + +endmodule + + +module submodule #( + parameter int unsigned SubPara = 4 +) ( + input logic clk_i, + input logic rst_ni, + + input logic signal_one_i, + output logic signal_two_o +); + + always_comb begin : dummy_comb + signal_two_o = ~signal_one_i; + end + +endmodule \ No newline at end of file diff --git a/test/infer_dot_star/submodule.sv b/test/infer_dot_star/submodule.sv new file mode 100644 index 0000000..9d1de1c --- /dev/null +++ b/test/infer_dot_star/submodule.sv @@ -0,0 +1,15 @@ +module submodule #( + parameter int unsigned SubPara = 4 +) ( + input logic clk_i, + input logic rst_ni, + + input logic signal_one_i, + output logic signal_two_o +); + + always_comb begin : dummy_comb + signal_two_o = ~signal_one_i; + end + +endmodule diff --git a/test/infer_dot_star/top.sv b/test/infer_dot_star/top.sv new file mode 100644 index 0000000..19fc27d --- /dev/null +++ b/test/infer_dot_star/top.sv @@ -0,0 +1,25 @@ + +module top #( + parameter int unsigned TestPara = 4, + parameter int unsigned TestParaOne = 4, + parameter int unsigned TestParaTwo = 4, + parameter int unsigned TestParaThree = 4 +) ( + input logic clk_i, + input logic rst_ni, + + input logic signal_one_i, + output logic signal_two_o, + input logic unused_signal +); + + (* dont_touch *) + submodule #( + .SubPara(TestPara) + ) i_submodule_test ( + .rst_ni(rst_ni), + .* + ); + +endmodule + diff --git a/tests/cli_tests.rs b/tests/cli_tests.rs index e36a1d8..7fea6e3 100644 --- a/tests/cli_tests.rs +++ b/tests/cli_tests.rs @@ -5,6 +5,7 @@ use anyhow::Result; use assert_cmd::prelude::*; // Add methods on commands use predicates::prelude::*; // Used for writing assertions +use std::fs; use std::path::Path; use std::process::Command; // Run programs // use std::fs::File; @@ -17,9 +18,9 @@ mod tests { fn simple_help_print_check() -> Result<()> { let mut cmd = Command::cargo_bin("morty")?; cmd.arg("-h"); - cmd.assert() - .success() - .stdout(predicate::str::contains("Print version information\n")); + cmd.assert().success().stdout(predicate::str::contains( + "A SystemVerilog source file pickler.\n", + )); Ok(()) } @@ -85,4 +86,23 @@ mod tests { Ok(()) } + + #[test] + fn test_infer_dot_star() -> Result<(), Box> { + let expected_output = fs::read_to_string("test/infer_dot_star/expected/expected.sv")?; + let mut cmd = Command::cargo_bin("morty")?; + cmd.arg("--infer_dot_star") + .arg(Path::new("test/infer_dot_star/top.sv").as_os_str()) + .arg(Path::new("test/infer_dot_star/submodule.sv").as_os_str()); + let binding = cmd.assert().success(); + // we have to do it this complex such that windows tests are passing + // windows has a different output format and injects \r into the output + let output = &binding.get_output().stdout; + let output_str = String::from_utf8(output.clone()).unwrap(); + let expected_output_stripped = expected_output.replace(&['\r'][..], ""); + let output_str_stripped = output_str.replace(&['\r'][..], ""); + let compare_fn = predicate::str::contains(expected_output_stripped); + assert_eq!(compare_fn.eval(&output_str_stripped), true); + Ok(()) + } }