diff --git a/Cargo.lock b/Cargo.lock
index 5326152b43..b4cbaea08f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -23,6 +23,60 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
+[[package]]
+name = "aead"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "aes"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561"
+dependencies = [
+ "aes-soft",
+ "aesni",
+ "cipher",
+]
+
+[[package]]
+name = "aes-gcm"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
+[[package]]
+name = "aes-soft"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
+dependencies = [
+ "cipher",
+ "opaque-debug",
+]
+
+[[package]]
+name = "aesni"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
+dependencies = [
+ "cipher",
+ "opaque-debug",
+]
+
[[package]]
name = "ahash"
version = "0.8.8"
@@ -423,12 +477,6 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
-[[package]]
-name = "base64ct"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
-
[[package]]
name = "bit-set"
version = "0.8.0"
@@ -542,16 +590,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
-version = "0.4.34"
+version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
+checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
- "windows-targets 0.52.0",
+ "windows-link",
]
[[package]]
@@ -560,6 +608,15 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901"
+[[package]]
+name = "cipher"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
+dependencies = [
+ "generic-array",
+]
+
[[package]]
name = "clap"
version = "4.5.0"
@@ -610,7 +667,6 @@ dependencies = [
"lazy_static",
"mutants",
"rand 0.8.5",
- "rand_chacha 0.3.1",
"regex",
"rstest",
"rstest_reuse",
@@ -620,30 +676,7 @@ dependencies = [
"serde_json",
"serde_stacker",
"slog",
- "stacks-common 0.0.1",
-]
-
-[[package]]
-name = "clarity"
-version = "0.0.1"
-source = "git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2#8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2"
-dependencies = [
- "hashbrown 0.15.2",
- "integer-sqrt",
- "lazy_static",
- "rand 0.8.5",
- "rand_chacha 0.3.1",
- "regex",
- "rstest",
- "rstest_reuse",
- "rusqlite",
- "serde",
- "serde_derive",
- "serde_json",
- "serde_stacker",
- "slog",
- "stacks-common 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "time 0.2.27",
+ "stacks-common",
]
[[package]]
@@ -685,18 +718,29 @@ dependencies = [
"tempfile",
]
-[[package]]
-name = "const-oid"
-version = "0.9.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
-
[[package]]
name = "const_fn"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e"
+[[package]]
+name = "cookie"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951"
+dependencies = [
+ "aes-gcm",
+ "base64 0.13.1",
+ "hkdf",
+ "hmac 0.10.1",
+ "percent-encoding",
+ "rand 0.8.5",
+ "sha2 0.9.9",
+ "time 0.2.27",
+ "version_check",
+]
+
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -722,6 +766,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "cpuid-bool"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba"
+
[[package]]
name = "crc32fast"
version = "1.4.0"
@@ -763,6 +813,25 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "crypto-mac"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
+[[package]]
+name = "ctr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f"
+dependencies = [
+ "cipher",
+]
+
[[package]]
name = "curve25519-dalek"
version = "4.1.3"
@@ -777,7 +846,6 @@ dependencies = [
"rustc_version 0.4.0",
"serde",
"subtle",
- "zeroize",
]
[[package]]
@@ -797,16 +865,6 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
-[[package]]
-name = "der"
-version = "0.7.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
-dependencies = [
- "const-oid",
- "zeroize",
-]
-
[[package]]
name = "deranged"
version = "0.4.0"
@@ -880,8 +938,6 @@ version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
dependencies = [
- "pkcs8",
- "serde",
"signature",
]
@@ -893,11 +949,8 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
dependencies = [
"curve25519-dalek",
"ed25519",
- "rand_core 0.6.4",
- "serde",
"sha2 0.10.8",
"subtle",
- "zeroize",
]
[[package]]
@@ -1206,8 +1259,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if 1.0.0",
+ "js-sys",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "ghash"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375"
+dependencies = [
+ "opaque-debug",
+ "polyval",
]
[[package]]
@@ -1341,13 +1406,33 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd"
+[[package]]
+name = "hkdf"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f"
+dependencies = [
+ "digest 0.9.0",
+ "hmac 0.10.1",
+]
+
[[package]]
name = "hmac"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840"
dependencies = [
- "crypto-mac",
+ "crypto-mac 0.8.0",
+ "digest 0.9.0",
+]
+
+[[package]]
+name = "hmac"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
+dependencies = [
+ "crypto-mac 0.10.0",
"digest 0.9.0",
]
@@ -1359,7 +1444,7 @@ checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1"
dependencies = [
"digest 0.9.0",
"generic-array",
- "hmac",
+ "hmac 0.8.1",
]
[[package]]
@@ -1426,7 +1511,9 @@ checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad"
dependencies = [
"anyhow",
"async-channel 1.9.0",
+ "async-std",
"base64 0.13.1",
+ "cookie",
"futures-lite 1.13.0",
"infer",
"pin-project-lite",
@@ -1754,11 +1841,11 @@ dependencies = [
name = "libsigner"
version = "0.0.1"
dependencies = [
- "clarity 0.0.1",
+ "clarity",
"hashbrown 0.15.2",
"lazy_static",
"libc",
- "libstackerdb 0.0.1",
+ "libstackerdb",
"mutants",
"rand 0.8.5",
"rand_core 0.6.4",
@@ -1766,28 +1853,8 @@ dependencies = [
"serde_json",
"sha2 0.10.8",
"slog",
- "stacks-common 0.0.1",
- "stackslib 0.0.1",
- "thiserror",
- "tiny_http",
-]
-
-[[package]]
-name = "libsigner"
-version = "0.0.1"
-source = "git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2#8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2"
-dependencies = [
- "clarity 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "hashbrown 0.15.2",
- "lazy_static",
- "libc",
- "libstackerdb 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "serde",
- "serde_json",
- "sha2 0.10.8",
- "slog",
- "stacks-common 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "stackslib 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
+ "stacks-common",
+ "stackslib",
"thiserror",
"tiny_http",
]
@@ -1807,23 +1874,11 @@ dependencies = [
name = "libstackerdb"
version = "0.0.1"
dependencies = [
- "clarity 0.0.1",
- "secp256k1",
- "serde",
- "sha2 0.10.8",
- "stacks-common 0.0.1",
-]
-
-[[package]]
-name = "libstackerdb"
-version = "0.0.1"
-source = "git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2#8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2"
-dependencies = [
- "clarity 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
+ "clarity",
"secp256k1",
"serde",
"sha2 0.10.8",
- "stacks-common 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
+ "stacks-common",
]
[[package]]
@@ -2196,16 +2251,6 @@ dependencies = [
"futures-io",
]
-[[package]]
-name = "pkcs8"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
-dependencies = [
- "der",
- "spki",
-]
-
[[package]]
name = "pkg-config"
version = "0.3.30"
@@ -2242,6 +2287,17 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "polyval"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd"
+dependencies = [
+ "cpuid-bool",
+ "opaque-debug",
+ "universal-hash",
+]
+
[[package]]
name = "powerfmt"
version = "0.2.0"
@@ -2252,20 +2308,10 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
name = "pox-locking"
version = "2.4.0"
dependencies = [
- "clarity 0.0.1",
+ "clarity",
"mutants",
"slog",
- "stacks-common 0.0.1",
-]
-
-[[package]]
-name = "pox-locking"
-version = "2.4.0"
-source = "git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2#8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2"
-dependencies = [
- "clarity 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "slog",
- "stacks-common 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
+ "stacks-common",
]
[[package]]
@@ -3045,9 +3091,6 @@ name = "signature"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
-dependencies = [
- "rand_core 0.6.4",
-]
[[package]]
name = "similar"
@@ -3139,16 +3182,6 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
-[[package]]
-name = "spki"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
-dependencies = [
- "base64ct",
- "der",
-]
-
[[package]]
name = "stacker"
version = "0.1.15"
@@ -3169,13 +3202,13 @@ dependencies = [
"chrono",
"curve25519-dalek",
"ed25519-dalek",
+ "getrandom 0.2.12",
"hashbrown 0.15.2",
"lazy_static",
"libsecp256k1",
"nix",
"proptest",
"rand 0.8.5",
- "rand_core 0.6.4",
"ripemd",
"rusqlite",
"secp256k1",
@@ -3191,34 +3224,6 @@ dependencies = [
"winapi 0.3.9",
]
-[[package]]
-name = "stacks-common"
-version = "0.0.1"
-source = "git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2#8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2"
-dependencies = [
- "chrono",
- "curve25519-dalek",
- "ed25519-dalek",
- "hashbrown 0.15.2",
- "lazy_static",
- "libsecp256k1",
- "nix",
- "rand 0.8.5",
- "ripemd",
- "rusqlite",
- "secp256k1",
- "serde",
- "serde_derive",
- "serde_json",
- "sha2 0.10.8",
- "sha3",
- "slog",
- "slog-term",
- "time 0.2.27",
- "toml",
- "winapi 0.3.9",
-]
-
[[package]]
name = "stacks-node"
version = "0.1.0"
@@ -3228,13 +3233,12 @@ dependencies = [
"backtrace",
"base64 0.12.3",
"chrono",
- "clarity 0.0.1",
+ "clarity",
"hashbrown 0.15.2",
"http-types",
"lazy_static",
"libc",
- "libsigner 0.0.1",
- "libsigner 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
+ "libsigner",
"madhouse",
"mockito",
"mutants",
@@ -3250,12 +3254,9 @@ dependencies = [
"serde_json",
"serial_test",
"slog",
- "stacks-common 0.0.1",
- "stacks-common 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "stacks-signer 0.0.1",
- "stacks-signer 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "stackslib 0.0.1",
- "stackslib 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
+ "stacks-common",
+ "stacks-signer",
+ "stackslib",
"stdext",
"stx-genesis",
"tempfile",
@@ -3275,11 +3276,11 @@ version = "0.0.1"
dependencies = [
"backoff",
"clap",
- "clarity 0.0.1",
+ "clarity",
"hashbrown 0.15.2",
"lazy_static",
- "libsigner 0.0.1",
- "libstackerdb 0.0.1",
+ "libsigner",
+ "libstackerdb",
"num-traits",
"prometheus",
"rand 0.8.5",
@@ -3292,8 +3293,8 @@ dependencies = [
"slog",
"slog-json",
"slog-term",
- "stacks-common 0.0.1",
- "stackslib 0.0.1",
+ "stacks-common",
+ "stackslib",
"stdext",
"thiserror",
"tiny_http",
@@ -3303,52 +3304,22 @@ dependencies = [
"url",
]
-[[package]]
-name = "stacks-signer"
-version = "0.0.1"
-source = "git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2#8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2"
-dependencies = [
- "backoff",
- "clap",
- "clarity 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "hashbrown 0.15.2",
- "lazy_static",
- "libsigner 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "libstackerdb 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "rand 0.8.5",
- "rand_core 0.6.4",
- "reqwest",
- "rusqlite",
- "secp256k1",
- "serde",
- "serde_json",
- "slog",
- "slog-term",
- "stacks-common 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "stackslib 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "thiserror",
- "toml",
- "tracing",
- "tracing-subscriber",
- "url",
-]
-
[[package]]
name = "stackslib"
version = "0.0.1"
dependencies = [
"assert-json-diff 1.1.0",
"chrono",
- "clarity 0.0.1",
+ "clarity",
"ed25519-dalek",
"hashbrown 0.15.2",
"lazy_static",
- "libstackerdb 0.0.1",
+ "libstackerdb",
"mio 0.6.23",
"mutants",
"nix",
"percent-encoding",
- "pox-locking 2.4.0",
+ "pox-locking",
"prometheus",
"rand 0.8.5",
"rand_chacha 0.3.1",
@@ -3366,7 +3337,7 @@ dependencies = [
"sha2 0.10.8",
"siphasher",
"slog",
- "stacks-common 0.0.1",
+ "stacks-common",
"stdext",
"stx-genesis",
"tempfile",
@@ -3377,42 +3348,6 @@ dependencies = [
"winapi 0.3.9",
]
-[[package]]
-name = "stackslib"
-version = "0.0.1"
-source = "git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2#8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2"
-dependencies = [
- "chrono",
- "clarity 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "ed25519-dalek",
- "hashbrown 0.15.2",
- "lazy_static",
- "libstackerdb 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "mio 0.6.23",
- "nix",
- "percent-encoding",
- "pox-locking 2.4.0 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "rand 0.8.5",
- "rand_chacha 0.3.1",
- "rand_core 0.6.4",
- "regex",
- "ripemd",
- "rusqlite",
- "secp256k1",
- "serde",
- "serde_derive",
- "serde_json",
- "sha2 0.10.8",
- "siphasher",
- "slog",
- "stacks-common 0.0.1 (git+https://github.com/stacks-network/stacks-core.git?rev=8a79aaa7df0f13dfc5ab0d0d0bcb8201c90bcba2)",
- "tikv-jemallocator",
- "time 0.2.27",
- "toml",
- "url",
- "winapi 0.3.9",
-]
-
[[package]]
name = "standback"
version = "0.2.17"
@@ -3932,6 +3867,16 @@ dependencies = [
"tinyvec",
]
+[[package]]
+name = "universal-hash"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -4178,6 +4123,12 @@ dependencies = [
"windows-targets 0.52.0",
]
+[[package]]
+name = "windows-link"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
+
[[package]]
name = "windows-sys"
version = "0.48.0"
@@ -4349,9 +4300,3 @@ dependencies = [
"quote",
"syn 2.0.58",
]
-
-[[package]]
-name = "zeroize"
-version = "1.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
diff --git a/Cargo.toml b/Cargo.toml
index d604038f94..838e9d5000 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,24 +14,29 @@ members = [
# Dependencies we want to keep the same between workspace members
[workspace.dependencies]
-ed25519-dalek = { version = "2.1.1", features = ["serde", "rand_core"] }
+ed25519-dalek = { version = "2.1.1", default-features = false }
hashbrown = { version = "0.15.2", features = ["serde"] }
+lazy_static = "1.4.0"
rand_core = "0.6.4"
rand = "0.8"
rand_chacha = "0.3.1"
-tikv-jemallocator = "0.5.4"
+serde = "1"
+serde_derive = "1"
+serde_json = { version = "1.0", features = ["arbitrary_precision", "unbounded_depth"] }
+slog = { version = "2.5.2", features = [ "max_level_trace" ] }
rusqlite = { version = "0.31.0", features = ["blob", "serde_json", "i128_blob", "bundled", "trace"] }
+tikv-jemallocator = "0.5.4"
thiserror = "1.0.65"
toml = "0.5.6"
# Use a bit more than default optimization for
-# dev builds to speed up test execution
+# dev builds to speed up test execution
[profile.dev]
opt-level = 1
# Use release-level optimization for dependencies
# This slows down "first" builds on development environments,
-# but won't impact subsequent builds.
+# but won't impact subsequent builds.
[profile.dev.package."*"]
opt-level = 3
diff --git a/clarity/Cargo.toml b/clarity/Cargo.toml
index 37b3f0ed6f..8b714fd86b 100644
--- a/clarity/Cargo.toml
+++ b/clarity/Cargo.toml
@@ -18,38 +18,46 @@ name = "clarity"
path = "./src/libclarity.rs"
[dependencies]
-rand = { workspace = true }
-rand_chacha = { workspace = true }
-serde = "1"
-serde_derive = "1"
-serde_stacker = "0.1"
-regex = "1"
-lazy_static = "1.4.0"
-integer-sqrt = "0.1.3"
-slog = { version = "2.5.2", features = [ "max_level_trace" ] }
-stacks_common = { package = "stacks-common", path = "../stacks-common", default-features = false }
-rstest = "0.17.0"
-rstest_reuse = "0.5.0"
hashbrown = { workspace = true }
-rusqlite = { workspace = true, optional = true }
+lazy_static = { workspace = true }
+regex = { version = "1", default-features = false }
+serde = { workspace = true }
+serde_derive = { workspace = true }
+serde_json = { workspace = true }
+slog = { workspace = true }
+stacks_common = { package = "stacks-common", path = "../stacks-common", default-features = false }
-[dependencies.serde_json]
-version = "1.0"
-features = ["arbitrary_precision", "unbounded_depth"]
+# Optional dependencies
+rand = { workspace = true, optional = true }
+serde_stacker = { version = "0.1", default-features = false, optional = true }
+integer-sqrt = { version = "0.1.3", default-features = false, optional = true }
+rusqlite = { workspace = true, optional = true }
+rstest = { version = "0.17.0", default-features = false, optional = true }
+rstest_reuse = { version = "0.5.0", default-features = false, optional = true }
[dev-dependencies]
assert-json-diff = "1.0.0"
mutants = "0.0.3"
-# a nightly rustc regression (35dbef235 2021-03-02) prevents criterion from compiling
-# but it isn't necessary for tests: only benchmarks. therefore, commenting out for now.
-# criterion = "0.3"
[features]
-default = ["rusqlite"]
-developer-mode = ["stacks_common/developer-mode"]
+# The default feature set provides the full Clarity virtual machine with its SQLite-based
+# database backend.
+# To use `clarity` as a lightweight serialization/deserialization library,
+# depend on it with `default-features = false`.
+default = ["vm", "rusqlite"]
+# Enables the complete Clarity Virtual Machine. This includes the parser, analyzer,
+# cost-checking system, and execution engine. It transitively enables all necessary
+# dependencies for running smart contracts. This feature is required for any on-chain
+# contract execution or local contract testing.
+vm = ["dep:rand", "dep:serde_stacker", "dep:integer-sqrt"]
+developer-mode = ["vm", "stacks_common/developer-mode"]
+devtools = ["vm"]
+testing = ["vm", "dep:rstest", "dep:rstest_reuse", "rusqlite"]
+rusqlite = ["vm", "stacks_common/rusqlite", "dep:rusqlite"]
slog_json = ["stacks_common/slog_json"]
-rusqlite = ["stacks_common/rusqlite", "dep:rusqlite"]
-testing = []
-devtools = []
-rollback_value_check = []
-disable-costs = []
+rollback_value_check = ["vm"]
+disable-costs = ["vm"]
+
+# Wasm-specific features for easier configuration
+wasm-web = ["stacks_common/wasm-web"]
+wasm-deterministic = ["stacks_common/wasm-deterministic"]
diff --git a/clarity/src/libclarity.rs b/clarity/src/libclarity.rs
index 7ce2a4f903..4b3bbf269c 100644
--- a/clarity/src/libclarity.rs
+++ b/clarity/src/libclarity.rs
@@ -20,10 +20,6 @@
#![allow(non_upper_case_globals)]
#![cfg_attr(test, allow(unused_variables, unused_assignments))]
-#[allow(unused_imports)]
-#[macro_use(o, slog_log, slog_trace, slog_debug, slog_info, slog_warn, slog_error)]
-extern crate slog;
-
#[macro_use]
extern crate serde_derive;
@@ -49,6 +45,7 @@ pub use stacks_common::{
/// The Clarity virtual machine
pub mod vm;
+#[cfg(feature = "vm")]
pub mod boot_util {
use stacks_common::types::chainstate::StacksAddress;
diff --git a/clarity/src/vm/analysis/arithmetic_checker/tests.rs b/clarity/src/vm/analysis/arithmetic_checker/tests.rs
index 0e7d520cb3..6453402f9b 100644
--- a/clarity/src/vm/analysis/arithmetic_checker/tests.rs
+++ b/clarity/src/vm/analysis/arithmetic_checker/tests.rs
@@ -215,13 +215,13 @@ fn test_functions_clarity1() {
Err(FunctionNotPermitted(NativeFunctions::SetVar))),
("(define-private (foo (a principal)) (ft-get-balance tokaroos a))",
Err(FunctionNotPermitted(NativeFunctions::GetTokenBalance))),
- ("(define-private (foo (a principal))
+ ("(define-private (foo (a principal))
(ft-transfer? stackaroo u50 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF))",
Err(FunctionNotPermitted(NativeFunctions::TransferToken))),
- ("(define-private (foo (a principal))
+ ("(define-private (foo (a principal))
(ft-mint? stackaroo u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR))",
Err(FunctionNotPermitted(NativeFunctions::MintToken))),
- ("(define-private (foo (a principal))
+ ("(define-private (foo (a principal))
(nft-mint? stackaroo \"Roo\" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR))",
Err(FunctionNotPermitted(NativeFunctions::MintAsset))),
("(nft-transfer? stackaroo \"Roo\" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)",
@@ -293,7 +293,7 @@ fn test_functions_clarity1() {
Ok(())),
("(buff-to-uint-be 0x0001)",
Ok(())),
- ("(is-standard 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6)",
+ ("(is-standard 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6)",
Ok(())),
("(principal-destruct? 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6)",
Ok(())),
@@ -358,7 +358,7 @@ fn test_functions_clarity2() {
Err(FunctionNotPermitted(NativeFunctions::IsStandard))),
("(principal-destruct? 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6)",
Err(FunctionNotPermitted(NativeFunctions::PrincipalDestruct))),
- ("(principal-construct? 0x22 0xfa6bf38ed557fe417333710d6033e9419391a320)",
+ ("(principal-construct? 0x22 0xfa6bf38ed557fe417333710d6033e9419391a320)",
Err(FunctionNotPermitted(NativeFunctions::PrincipalConstruct))),
("(string-to-int? \"-1\")",
Err(FunctionNotPermitted(NativeFunctions::StringToInt))),
diff --git a/clarity/src/vm/analysis/engine.rs b/clarity/src/vm/analysis/engine.rs
new file mode 100644
index 0000000000..4355c8ac9e
--- /dev/null
+++ b/clarity/src/vm/analysis/engine.rs
@@ -0,0 +1,172 @@
+// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
+// Copyright (C) 2020 Stacks Open Internet Foundation
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+use stacks_common::types::StacksEpochId;
+
+pub use crate::vm::analysis::analysis_db::AnalysisDatabase;
+use crate::vm::analysis::arithmetic_checker::ArithmeticOnlyChecker;
+use crate::vm::analysis::contract_interface_builder::build_contract_interface;
+pub use crate::vm::analysis::errors::{CheckError, CheckErrors, CheckResult};
+use crate::vm::analysis::read_only_checker::ReadOnlyChecker;
+use crate::vm::analysis::trait_checker::TraitChecker;
+use crate::vm::analysis::type_checker::v2_05::TypeChecker as TypeChecker2_05;
+use crate::vm::analysis::type_checker::v2_1::TypeChecker as TypeChecker2_1;
+pub use crate::vm::analysis::types::{AnalysisPass, ContractAnalysis};
+#[cfg(feature = "rusqlite")]
+use crate::vm::ast::{build_ast_with_rules, ASTRules};
+use crate::vm::costs::LimitedCostTracker;
+#[cfg(feature = "rusqlite")]
+use crate::vm::database::MemoryBackingStore;
+use crate::vm::database::STORE_CONTRACT_SRC_INTERFACE;
+use crate::vm::representations::SymbolicExpression;
+use crate::vm::types::QualifiedContractIdentifier;
+#[cfg(feature = "rusqlite")]
+use crate::vm::types::TypeSignature;
+use crate::vm::ClarityVersion;
+
+/// Used by CLI tools like the docs generator. Not used in production
+#[cfg(feature = "rusqlite")]
+pub fn mem_type_check(
+ snippet: &str,
+ version: ClarityVersion,
+ epoch: StacksEpochId,
+) -> CheckResult<(Option, ContractAnalysis)> {
+ let contract_identifier: QualifiedContractIdentifier = QualifiedContractIdentifier::transient();
+ let contract: Vec = build_ast_with_rules(
+ &contract_identifier,
+ snippet,
+ &mut (),
+ version,
+ epoch,
+ ASTRules::PrecheckSize,
+ )
+ .map_err(|_| CheckErrors::Expects("Failed to build AST".into()))?
+ .expressions;
+
+ let mut marf: MemoryBackingStore = MemoryBackingStore::new();
+ let mut analysis_db = marf.as_analysis_db();
+ let cost_tracker = LimitedCostTracker::new_free();
+ match run_analysis(
+ &QualifiedContractIdentifier::transient(),
+ &contract,
+ &mut analysis_db,
+ false,
+ cost_tracker,
+ epoch,
+ version,
+ true,
+ ) {
+ Ok(x) => {
+ // return the first type result of the type checker
+ let first_type = x
+ .type_map
+ .as_ref()
+ .ok_or_else(|| CheckErrors::Expects("Should be non-empty".into()))?
+ .get_type_expected(
+ x.expressions
+ .last()
+ .ok_or_else(|| CheckErrors::Expects("Should be non-empty".into()))?,
+ )
+ .cloned();
+ Ok((first_type, x))
+ }
+ Err((e, _)) => Err(e),
+ }
+}
+
+// Legacy function
+// The analysis is not just checking type.
+#[cfg(test)]
+pub fn type_check(
+ contract_identifier: &QualifiedContractIdentifier,
+ expressions: &mut [SymbolicExpression],
+ analysis_db: &mut AnalysisDatabase,
+ insert_contract: bool,
+ epoch: &StacksEpochId,
+ version: &ClarityVersion,
+) -> CheckResult {
+ run_analysis(
+ contract_identifier,
+ expressions,
+ analysis_db,
+ insert_contract,
+ // for the type check tests, the cost tracker's epoch doesn't
+ // matter: the costs in those tests are all free anyways.
+ LimitedCostTracker::new_free(),
+ *epoch,
+ *version,
+ true,
+ )
+ .map_err(|(e, _cost_tracker)| e)
+}
+
+#[allow(clippy::too_many_arguments)]
+pub fn run_analysis(
+ contract_identifier: &QualifiedContractIdentifier,
+ expressions: &[SymbolicExpression],
+ analysis_db: &mut AnalysisDatabase,
+ save_contract: bool,
+ cost_tracker: LimitedCostTracker,
+ epoch: StacksEpochId,
+ version: ClarityVersion,
+ build_type_map: bool,
+) -> Result {
+ let mut contract_analysis = ContractAnalysis::new(
+ contract_identifier.clone(),
+ expressions.to_vec(),
+ cost_tracker,
+ epoch,
+ version,
+ );
+ let result = analysis_db.execute(|db| {
+ ReadOnlyChecker::run_pass(&epoch, &mut contract_analysis, db)?;
+ match epoch {
+ StacksEpochId::Epoch20 | StacksEpochId::Epoch2_05 => {
+ TypeChecker2_05::run_pass(&epoch, &mut contract_analysis, db, build_type_map)
+ }
+ StacksEpochId::Epoch21
+ | StacksEpochId::Epoch22
+ | StacksEpochId::Epoch23
+ | StacksEpochId::Epoch24
+ | StacksEpochId::Epoch25
+ | StacksEpochId::Epoch30
+ | StacksEpochId::Epoch31 => {
+ TypeChecker2_1::run_pass(&epoch, &mut contract_analysis, db, build_type_map)
+ }
+ StacksEpochId::Epoch10 => {
+ return Err(CheckErrors::Expects(
+ "Epoch 1.0 is not a valid epoch for analysis".into(),
+ )
+ .into())
+ }
+ }?;
+ TraitChecker::run_pass(&epoch, &mut contract_analysis, db)?;
+ ArithmeticOnlyChecker::check_contract_cost_eligible(&mut contract_analysis);
+
+ if STORE_CONTRACT_SRC_INTERFACE {
+ let interface = build_contract_interface(&contract_analysis)?;
+ contract_analysis.contract_interface = Some(interface);
+ }
+ if save_contract {
+ db.insert_contract(contract_identifier, &contract_analysis)?;
+ }
+ Ok(())
+ });
+ match result {
+ Ok(_) => Ok(contract_analysis),
+ Err(e) => Err((e, contract_analysis.take_contract_cost_tracker())),
+ }
+}
diff --git a/clarity/src/vm/analysis/mod.rs b/clarity/src/vm/analysis/mod.rs
index 19183f5f67..450f927e3c 100644
--- a/clarity/src/vm/analysis/mod.rs
+++ b/clarity/src/vm/analysis/mod.rs
@@ -14,171 +14,27 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+pub mod errors;
+
+#[cfg(feature = "vm")]
pub mod analysis_db;
+#[cfg(feature = "vm")]
pub mod arithmetic_checker;
+#[cfg(feature = "vm")]
pub mod contract_interface_builder;
-pub mod errors;
+#[cfg(feature = "vm")]
pub mod read_only_checker;
+#[cfg(feature = "vm")]
pub mod trait_checker;
+#[cfg(feature = "vm")]
pub mod type_checker;
+#[cfg(feature = "vm")]
pub mod types;
-use stacks_common::types::StacksEpochId;
-
-pub use self::analysis_db::AnalysisDatabase;
-use self::arithmetic_checker::ArithmeticOnlyChecker;
-use self::contract_interface_builder::build_contract_interface;
-pub use self::errors::{CheckError, CheckErrors, CheckResult};
-use self::read_only_checker::ReadOnlyChecker;
-use self::trait_checker::TraitChecker;
-use self::type_checker::v2_05::TypeChecker as TypeChecker2_05;
-use self::type_checker::v2_1::TypeChecker as TypeChecker2_1;
-pub use self::types::{AnalysisPass, ContractAnalysis};
-#[cfg(feature = "rusqlite")]
-use crate::vm::ast::{build_ast_with_rules, ASTRules};
-use crate::vm::costs::LimitedCostTracker;
-#[cfg(feature = "rusqlite")]
-use crate::vm::database::MemoryBackingStore;
-use crate::vm::database::STORE_CONTRACT_SRC_INTERFACE;
-use crate::vm::representations::SymbolicExpression;
-use crate::vm::types::QualifiedContractIdentifier;
-#[cfg(feature = "rusqlite")]
-use crate::vm::types::TypeSignature;
-use crate::vm::ClarityVersion;
-
-/// Used by CLI tools like the docs generator. Not used in production
-#[cfg(feature = "rusqlite")]
-pub fn mem_type_check(
- snippet: &str,
- version: ClarityVersion,
- epoch: StacksEpochId,
-) -> CheckResult<(Option, ContractAnalysis)> {
- let contract_identifier = QualifiedContractIdentifier::transient();
- let contract = build_ast_with_rules(
- &contract_identifier,
- snippet,
- &mut (),
- version,
- epoch,
- ASTRules::PrecheckSize,
- )
- .map_err(|_| CheckErrors::Expects("Failed to build AST".into()))?
- .expressions;
-
- let mut marf = MemoryBackingStore::new();
- let mut analysis_db = marf.as_analysis_db();
- let cost_tracker = LimitedCostTracker::new_free();
- match run_analysis(
- &QualifiedContractIdentifier::transient(),
- &contract,
- &mut analysis_db,
- false,
- cost_tracker,
- epoch,
- version,
- true,
- ) {
- Ok(x) => {
- // return the first type result of the type checker
- let first_type = x
- .type_map
- .as_ref()
- .ok_or_else(|| CheckErrors::Expects("Should be non-empty".into()))?
- .get_type_expected(
- x.expressions
- .last()
- .ok_or_else(|| CheckErrors::Expects("Should be non-empty".into()))?,
- )
- .cloned();
- Ok((first_type, x))
- }
- Err((e, _)) => Err(e),
- }
-}
-
-// Legacy function
-// The analysis is not just checking type.
-#[cfg(test)]
-pub fn type_check(
- contract_identifier: &QualifiedContractIdentifier,
- expressions: &mut [SymbolicExpression],
- analysis_db: &mut AnalysisDatabase,
- insert_contract: bool,
- epoch: &StacksEpochId,
- version: &ClarityVersion,
-) -> CheckResult {
- run_analysis(
- contract_identifier,
- expressions,
- analysis_db,
- insert_contract,
- // for the type check tests, the cost tracker's epoch doesn't
- // matter: the costs in those tests are all free anyways.
- LimitedCostTracker::new_free(),
- *epoch,
- *version,
- true,
- )
- .map_err(|(e, _cost_tracker)| e)
-}
-
-#[allow(clippy::too_many_arguments)]
-pub fn run_analysis(
- contract_identifier: &QualifiedContractIdentifier,
- expressions: &[SymbolicExpression],
- analysis_db: &mut AnalysisDatabase,
- save_contract: bool,
- cost_tracker: LimitedCostTracker,
- epoch: StacksEpochId,
- version: ClarityVersion,
- build_type_map: bool,
-) -> Result {
- let mut contract_analysis = ContractAnalysis::new(
- contract_identifier.clone(),
- expressions.to_vec(),
- cost_tracker,
- epoch,
- version,
- );
- let result = analysis_db.execute(|db| {
- ReadOnlyChecker::run_pass(&epoch, &mut contract_analysis, db)?;
- match epoch {
- StacksEpochId::Epoch20 | StacksEpochId::Epoch2_05 => {
- TypeChecker2_05::run_pass(&epoch, &mut contract_analysis, db, build_type_map)
- }
- StacksEpochId::Epoch21
- | StacksEpochId::Epoch22
- | StacksEpochId::Epoch23
- | StacksEpochId::Epoch24
- | StacksEpochId::Epoch25
- | StacksEpochId::Epoch30
- | StacksEpochId::Epoch31 => {
- TypeChecker2_1::run_pass(&epoch, &mut contract_analysis, db, build_type_map)
- }
- StacksEpochId::Epoch10 => {
- return Err(CheckErrors::Expects(
- "Epoch 1.0 is not a valid epoch for analysis".into(),
- )
- .into())
- }
- }?;
- TraitChecker::run_pass(&epoch, &mut contract_analysis, db)?;
- ArithmeticOnlyChecker::check_contract_cost_eligible(&mut contract_analysis);
-
- if STORE_CONTRACT_SRC_INTERFACE {
- let interface = build_contract_interface(&contract_analysis)?;
- contract_analysis.contract_interface = Some(interface);
- }
- if save_contract {
- db.insert_contract(contract_identifier, &contract_analysis)?;
- }
- Ok(())
- });
- match result {
- Ok(_) => Ok(contract_analysis),
- Err(e) => Err((e, contract_analysis.take_contract_cost_tracker())),
- }
-}
+#[cfg(feature = "vm")]
+pub mod engine;
+#[cfg(feature = "vm")]
+pub use engine::*;
#[cfg(test)]
mod tests;
diff --git a/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts.rs b/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts.rs
index 838be9e6bb..6e6f57df8f 100644
--- a/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts.rs
+++ b/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts.rs
@@ -111,8 +111,8 @@ const SIMPLE_TOKENS: &str = "(define-map tokens { account: principal } { balance
const SIMPLE_NAMES: &str = "(define-constant burn-address 'SP000000000000000000002Q6VF78)
(define-private (price-function (name uint))
(if (< name u100000) u1000 u100))
-
- (define-map name-map
+
+ (define-map name-map
{ name: uint } { owner: principal })
(define-map preorder-map
{ name-hash: (buff 20) }
@@ -121,7 +121,7 @@ const SIMPLE_NAMES: &str = "(define-constant burn-address 'SP0000000000000000000
(define-private (check-balance)
(contract-call? .tokens my-get-token-balance tx-sender))
- (define-public (preorder
+ (define-public (preorder
(name-hash (buff 20))
(name-price uint))
(let ((xfer-result (contract-call? .tokens token-transfer
@@ -145,13 +145,13 @@ const SIMPLE_NAMES: &str = "(define-constant burn-address 'SP0000000000000000000
;; preorder entry must exist!
(unwrap! (map-get? preorder-map
(tuple (name-hash (hash160 (xor name salt))))) (err 2)))
- (name-entry
+ (name-entry
(map-get? name-map (tuple (name name)))))
(if (and
;; name shouldn't *already* exist
(is-none name-entry)
;; preorder must have paid enough
- (<= (price-function name)
+ (<= (price-function name)
(get paid preorder-entry))
;; preorder must have been the current principal
(is-eq tx-sender
@@ -280,7 +280,7 @@ fn test_names_tokens_contracts_interface() {
{ "name": "tn1", "type": "bool" },
{ "name": "tn2", "type": "int128" },
{ "name": "tn3", "type": { "buffer": { "length": 1 } }}
- ] } }
+ ] } }
},
{ "name": "f11",
"access": "private",
@@ -413,7 +413,7 @@ fn test_names_tokens_contracts_interface() {
"name": "n2",
"type": "bool"
}
- ]
+ ]
}
}]
}
@@ -1478,10 +1478,10 @@ fn test_trait_to_subtrait_and_back() {
))
(define-private (foo-0 (impl-contract ))
(foo-1 impl-contract))
-
+
(define-private (foo-1 (impl-contract ))
(foo-2 impl-contract))
-
+
(define-private (foo-2 (impl-contract ))
true)";
diff --git a/clarity/src/vm/ast/ast_builder.rs b/clarity/src/vm/ast/ast_builder.rs
new file mode 100644
index 0000000000..dc56482382
--- /dev/null
+++ b/clarity/src/vm/ast/ast_builder.rs
@@ -0,0 +1,616 @@
+// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
+// Copyright (C) 2020 Stacks Open Internet Foundation
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+use stacks_common::types::StacksEpochId;
+
+use crate::vm::ast::definition_sorter::DefinitionSorter;
+use crate::vm::ast::errors::ParseResult;
+use crate::vm::ast::expression_identifier::ExpressionIdentifier;
+use crate::vm::ast::parser::v1::{
+ parse as parse_v1, parse_no_stack_limit as parse_v1_no_stack_limit,
+};
+use crate::vm::ast::parser::v2::parse as parse_v2;
+use crate::vm::ast::stack_depth_checker::{StackDepthChecker, VaryStackDepthChecker};
+use crate::vm::ast::sugar_expander::SugarExpander;
+use crate::vm::ast::traits_resolver::TraitsResolver;
+use crate::vm::ast::types::BuildASTPass;
+pub use crate::vm::ast::types::ContractAST;
+use crate::vm::costs::cost_functions::ClarityCostFunction;
+use crate::vm::costs::{runtime_cost, CostTracker};
+use crate::vm::diagnostic::{Diagnostic, Level};
+use crate::vm::representations::PreSymbolicExpression;
+use crate::vm::types::QualifiedContractIdentifier;
+use crate::vm::ClarityVersion;
+
+/// Legacy function
+#[cfg(any(test, feature = "testing"))]
+pub fn parse(
+ contract_identifier: &QualifiedContractIdentifier,
+ source_code: &str,
+ version: ClarityVersion,
+ epoch: StacksEpochId,
+) -> Result, crate::vm::errors::Error> {
+ let ast = build_ast(contract_identifier, source_code, &mut (), version, epoch)?;
+ Ok(ast.expressions)
+}
+
+// AST parser rulesets to apply.
+define_u8_enum!(ASTRules {
+ Typical = 0,
+ PrecheckSize = 1
+});
+
+/// Parse a program based on which epoch is active
+fn parse_in_epoch(
+ source_code: &str,
+ epoch_id: StacksEpochId,
+ ast_rules: ASTRules,
+) -> ParseResult> {
+ if epoch_id >= StacksEpochId::Epoch21 {
+ parse_v2(source_code)
+ } else if ast_rules == ASTRules::Typical {
+ parse_v1_no_stack_limit(source_code)
+ } else {
+ parse_v1(source_code)
+ }
+}
+
+/// This is the part of the AST parser that runs without respect to cost analysis, specifically
+/// pertaining to verifying that the AST is reasonably-sized.
+/// Used mainly to filter transactions that might be too costly, as an optimization heuristic.
+pub fn ast_check_size(
+ contract_identifier: &QualifiedContractIdentifier,
+ source_code: &str,
+ clarity_version: ClarityVersion,
+ epoch_id: StacksEpochId,
+) -> ParseResult {
+ let pre_expressions = parse_in_epoch(source_code, epoch_id, ASTRules::PrecheckSize)?;
+ let mut contract_ast = ContractAST::new(contract_identifier.clone(), pre_expressions);
+ StackDepthChecker::run_pass(&mut contract_ast, clarity_version)?;
+ VaryStackDepthChecker::run_pass(&mut contract_ast, clarity_version)?;
+ Ok(contract_ast)
+}
+
+/// Build an AST according to a ruleset
+pub fn build_ast_with_rules(
+ contract_identifier: &QualifiedContractIdentifier,
+ source_code: &str,
+ cost_track: &mut T,
+ clarity_version: ClarityVersion,
+ epoch: StacksEpochId,
+ ruleset: ASTRules,
+) -> ParseResult {
+ match ruleset {
+ // After epoch 2.1, prechecking the size is required
+ ASTRules::Typical if epoch < StacksEpochId::Epoch21 => build_ast_typical(
+ contract_identifier,
+ source_code,
+ cost_track,
+ clarity_version,
+ epoch,
+ ),
+ _ => build_ast_precheck_size(
+ contract_identifier,
+ source_code,
+ cost_track,
+ clarity_version,
+ epoch,
+ ),
+ }
+}
+
+/// Build an AST with the typical rules
+fn build_ast_typical(
+ contract_identifier: &QualifiedContractIdentifier,
+ source_code: &str,
+ cost_track: &mut T,
+ clarity_version: ClarityVersion,
+ epoch: StacksEpochId,
+) -> ParseResult {
+ let (contract, _, _) = inner_build_ast(
+ contract_identifier,
+ source_code,
+ cost_track,
+ clarity_version,
+ epoch,
+ ASTRules::Typical,
+ true,
+ )?;
+ Ok(contract)
+}
+
+/// Used by developer tools only. Continues on through errors by inserting
+/// placeholders into the AST. Collects as many diagnostics as possible.
+/// Always returns a ContractAST, a vector of diagnostics, and a boolean
+/// that indicates if the build was successful.
+#[allow(clippy::unwrap_used)]
+pub fn build_ast_with_diagnostics(
+ contract_identifier: &QualifiedContractIdentifier,
+ source_code: &str,
+ cost_track: &mut T,
+ clarity_version: ClarityVersion,
+ epoch: StacksEpochId,
+) -> (ContractAST, Vec, bool) {
+ inner_build_ast(
+ contract_identifier,
+ source_code,
+ cost_track,
+ clarity_version,
+ epoch,
+ ASTRules::PrecheckSize,
+ false,
+ )
+ .unwrap()
+}
+
+fn inner_build_ast(
+ contract_identifier: &QualifiedContractIdentifier,
+ source_code: &str,
+ cost_track: &mut T,
+ clarity_version: ClarityVersion,
+ epoch: StacksEpochId,
+ ast_rules: ASTRules,
+ error_early: bool,
+) -> ParseResult<(ContractAST, Vec, bool)> {
+ let cost_err = match runtime_cost(
+ ClarityCostFunction::AstParse,
+ cost_track,
+ source_code.len() as u64,
+ ) {
+ Err(e) if error_early => return Err(e.into()),
+ Err(e) => Some(e),
+ _ => None,
+ };
+
+ let (pre_expressions, mut diagnostics, mut success) = if epoch >= StacksEpochId::Epoch21 {
+ if error_early {
+ let exprs = crate::vm::ast::parser::v2::parse(source_code)?;
+ (exprs, Vec::new(), true)
+ } else {
+ crate::vm::ast::parser::v2::parse_collect_diagnostics(source_code)
+ }
+ } else {
+ let parse_result = match ast_rules {
+ ASTRules::Typical => parse_v1_no_stack_limit(source_code),
+ ASTRules::PrecheckSize => parse_v1(source_code),
+ };
+ match parse_result {
+ Ok(pre_expressions) => (pre_expressions, vec![], true),
+ Err(error) if error_early => return Err(error),
+ Err(error) => (vec![], vec![error.diagnostic], false),
+ }
+ };
+
+ if let Some(e) = cost_err {
+ diagnostics.insert(
+ 0,
+ Diagnostic {
+ level: Level::Error,
+ message: format!("runtime_cost error: {:?}", e),
+ spans: vec![],
+ suggestion: None,
+ },
+ );
+ }
+
+ let mut contract_ast = ContractAST::new(contract_identifier.clone(), pre_expressions);
+ match StackDepthChecker::run_pass(&mut contract_ast, clarity_version) {
+ Err(e) if error_early => return Err(e),
+ Err(e) => {
+ diagnostics.push(e.diagnostic);
+ success = false;
+ }
+ _ => (),
+ }
+
+ if ast_rules != ASTRules::Typical {
+ // run extra stack-depth pass for tuples
+ match VaryStackDepthChecker::run_pass(&mut contract_ast, clarity_version) {
+ Err(e) if error_early => return Err(e),
+ Err(e) => {
+ diagnostics.push(e.diagnostic);
+ success = false;
+ }
+ _ => (),
+ }
+ }
+
+ match ExpressionIdentifier::run_pre_expression_pass(&mut contract_ast, clarity_version) {
+ Err(e) if error_early => return Err(e),
+ Err(e) => {
+ diagnostics.push(e.diagnostic);
+ success = false;
+ }
+ _ => (),
+ }
+ match DefinitionSorter::run_pass(&mut contract_ast, cost_track, clarity_version) {
+ Err(e) if error_early => return Err(e),
+ Err(e) => {
+ diagnostics.push(e.diagnostic);
+ success = false;
+ }
+ _ => (),
+ }
+ match TraitsResolver::run_pass(&mut contract_ast, clarity_version) {
+ Err(e) if error_early => return Err(e),
+ Err(e) => {
+ diagnostics.push(e.diagnostic);
+ success = false;
+ }
+ _ => (),
+ }
+ match SugarExpander::run_pass(&mut contract_ast, clarity_version) {
+ Err(e) if error_early => return Err(e),
+ Err(e) => {
+ diagnostics.push(e.diagnostic);
+ success = false;
+ }
+ _ => (),
+ }
+ match ExpressionIdentifier::run_expression_pass(&mut contract_ast, clarity_version) {
+ Err(e) if error_early => return Err(e),
+ Err(e) => {
+ diagnostics.push(e.diagnostic);
+ success = false;
+ }
+ _ => (),
+ }
+ Ok((contract_ast, diagnostics, success))
+}
+
+/// Built an AST, but pre-check the size of the AST before doing more work
+fn build_ast_precheck_size(
+ contract_identifier: &QualifiedContractIdentifier,
+ source_code: &str,
+ cost_track: &mut T,
+ clarity_version: ClarityVersion,
+ epoch: StacksEpochId,
+) -> ParseResult {
+ let (contract, _, _) = inner_build_ast(
+ contract_identifier,
+ source_code,
+ cost_track,
+ clarity_version,
+ epoch,
+ ASTRules::PrecheckSize,
+ true,
+ )?;
+ Ok(contract)
+}
+
+/// Test compatibility
+#[cfg(any(test, feature = "testing"))]
+pub fn build_ast(
+ contract_identifier: &QualifiedContractIdentifier,
+ source_code: &str,
+ cost_track: &mut T,
+ clarity_version: ClarityVersion,
+ epoch_id: StacksEpochId,
+) -> ParseResult {
+ build_ast_typical(
+ contract_identifier,
+ source_code,
+ cost_track,
+ clarity_version,
+ epoch_id,
+ )
+}
+
+#[cfg(test)]
+mod test {
+ use hashbrown::HashMap;
+ use stacks_common::types::StacksEpochId;
+
+ use crate::vm::ast::errors::ParseErrors;
+ use crate::vm::ast::stack_depth_checker::AST_CALL_STACK_DEPTH_BUFFER;
+ use crate::vm::ast::{build_ast, build_ast_with_rules, ASTRules};
+ use crate::vm::costs::{LimitedCostTracker, *};
+ use crate::vm::representations::depth_traverse;
+ use crate::vm::types::QualifiedContractIdentifier;
+ use crate::vm::{ClarityCostFunction, ClarityName, ClarityVersion, MAX_CALL_STACK_DEPTH};
+
+ #[derive(PartialEq, Debug)]
+ struct UnitTestTracker {
+ invoked_functions: Vec<(ClarityCostFunction, Vec)>,
+ invocation_count: u64,
+ cost_addition_count: u64,
+ }
+ impl UnitTestTracker {
+ pub fn new() -> Self {
+ UnitTestTracker {
+ invoked_functions: vec![],
+ invocation_count: 0,
+ cost_addition_count: 0,
+ }
+ }
+ }
+ impl CostTracker for UnitTestTracker {
+ fn compute_cost(
+ &mut self,
+ cost_f: ClarityCostFunction,
+ input: &[u64],
+ ) -> std::result::Result {
+ self.invoked_functions.push((cost_f, input.to_vec()));
+ self.invocation_count += 1;
+ Ok(ExecutionCost::ZERO)
+ }
+ fn add_cost(&mut self, _cost: ExecutionCost) -> std::result::Result<(), CostErrors> {
+ self.cost_addition_count += 1;
+ Ok(())
+ }
+ fn add_memory(&mut self, _memory: u64) -> std::result::Result<(), CostErrors> {
+ Ok(())
+ }
+ fn drop_memory(&mut self, _memory: u64) -> std::result::Result<(), CostErrors> {
+ Ok(())
+ }
+ fn reset_memory(&mut self) {}
+ fn short_circuit_contract_call(
+ &mut self,
+ _contract: &QualifiedContractIdentifier,
+ _function: &ClarityName,
+ _input: &[u64],
+ ) -> Result {
+ Ok(false)
+ }
+ }
+
+ #[test]
+ fn test_cost_tracking_deep_contracts_2_05() {
+ let clarity_version = ClarityVersion::Clarity1;
+ let stack_limit =
+ (AST_CALL_STACK_DEPTH_BUFFER + (MAX_CALL_STACK_DEPTH as u64) + 1) as usize;
+ let exceeds_stack_depth_tuple = format!(
+ "{}u1 {}",
+ "{ a : ".repeat(stack_limit + 1),
+ "} ".repeat(stack_limit + 1)
+ );
+
+ // for deep lists, a test like this works:
+ // it can assert a limit, that you can also verify
+ // by disabling `VaryStackDepthChecker` and arbitrarily bumping up the parser lexer limits
+ // and see that it produces the same result
+ let exceeds_stack_depth_list = format!(
+ "{}u1 {}",
+ "(list ".repeat(stack_limit + 1),
+ ")".repeat(stack_limit + 1)
+ );
+
+ // with old rules, this is just ExpressionStackDepthTooDeep
+ let mut cost_track = UnitTestTracker::new();
+ let err = build_ast_with_rules(
+ &QualifiedContractIdentifier::transient(),
+ &exceeds_stack_depth_list,
+ &mut cost_track,
+ clarity_version,
+ StacksEpochId::Epoch2_05,
+ ASTRules::Typical,
+ )
+ .expect_err("Contract should error in parsing");
+
+ let expected_err = ParseErrors::ExpressionStackDepthTooDeep;
+ let expected_list_cost_state = UnitTestTracker {
+ invoked_functions: vec![(ClarityCostFunction::AstParse, vec![500])],
+ invocation_count: 1,
+ cost_addition_count: 1,
+ };
+
+ assert_eq!(&expected_err, &err.err);
+ assert_eq!(expected_list_cost_state, cost_track);
+
+ // with new rules, this is now VaryExpressionStackDepthTooDeep
+ let mut cost_track = UnitTestTracker::new();
+ let err = build_ast_with_rules(
+ &QualifiedContractIdentifier::transient(),
+ &exceeds_stack_depth_list,
+ &mut cost_track,
+ clarity_version,
+ StacksEpochId::Epoch2_05,
+ ASTRules::PrecheckSize,
+ )
+ .expect_err("Contract should error in parsing");
+
+ let expected_err = ParseErrors::VaryExpressionStackDepthTooDeep;
+ let expected_list_cost_state = UnitTestTracker {
+ invoked_functions: vec![(ClarityCostFunction::AstParse, vec![500])],
+ invocation_count: 1,
+ cost_addition_count: 1,
+ };
+
+ assert_eq!(&expected_err, &err.err);
+ assert_eq!(expected_list_cost_state, cost_track);
+
+ // you cannot do the same for tuples!
+ // in ASTRules::Typical, this passes
+ let mut cost_track = UnitTestTracker::new();
+ let _ = build_ast_with_rules(
+ &QualifiedContractIdentifier::transient(),
+ &exceeds_stack_depth_tuple,
+ &mut cost_track,
+ clarity_version,
+ StacksEpochId::Epoch2_05,
+ ASTRules::Typical,
+ )
+ .expect("Contract should parse with ASTRules::Typical");
+
+ // this actually won't even error without
+ // the VaryStackDepthChecker changes.
+ let mut cost_track = UnitTestTracker::new();
+ let err = build_ast_with_rules(
+ &QualifiedContractIdentifier::transient(),
+ &exceeds_stack_depth_tuple,
+ &mut cost_track,
+ clarity_version,
+ StacksEpochId::Epoch2_05,
+ ASTRules::PrecheckSize,
+ )
+ .expect_err("Contract should error in parsing with ASTRules::PrecheckSize");
+
+ let expected_err = ParseErrors::VaryExpressionStackDepthTooDeep;
+ let expected_list_cost_state = UnitTestTracker {
+ invoked_functions: vec![(ClarityCostFunction::AstParse, vec![571])],
+ invocation_count: 1,
+ cost_addition_count: 1,
+ };
+
+ assert_eq!(&expected_err, &err.err);
+ assert_eq!(expected_list_cost_state, cost_track);
+ }
+
+ #[test]
+ fn test_cost_tracking_deep_contracts_2_1() {
+ for clarity_version in &[ClarityVersion::Clarity1, ClarityVersion::Clarity2] {
+ let stack_limit =
+ (AST_CALL_STACK_DEPTH_BUFFER + (MAX_CALL_STACK_DEPTH as u64) + 1) as usize;
+ let exceeds_stack_depth_tuple = format!(
+ "{}u1 {}",
+ "{ a : ".repeat(stack_limit + 1),
+ "} ".repeat(stack_limit + 1)
+ );
+
+ // for deep lists, a test like this works:
+ // it can assert a limit, that you can also verify
+ // by disabling `VaryStackDepthChecker` and arbitrarily bumping up the parser lexer limits
+ // and see that it produces the same result
+ let exceeds_stack_depth_list = format!(
+ "{}u1 {}",
+ "(list ".repeat(stack_limit + 1),
+ ")".repeat(stack_limit + 1)
+ );
+
+ // with old rules, this is just ExpressionStackDepthTooDeep
+ let mut cost_track = UnitTestTracker::new();
+ let err = build_ast_with_rules(
+ &QualifiedContractIdentifier::transient(),
+ &exceeds_stack_depth_list,
+ &mut cost_track,
+ *clarity_version,
+ StacksEpochId::Epoch21,
+ ASTRules::Typical,
+ )
+ .expect_err("Contract should error in parsing");
+
+ let expected_err = ParseErrors::ExpressionStackDepthTooDeep;
+ let expected_list_cost_state = UnitTestTracker {
+ invoked_functions: vec![(ClarityCostFunction::AstParse, vec![500])],
+ invocation_count: 1,
+ cost_addition_count: 1,
+ };
+
+ assert_eq!(&expected_err, &err.err);
+ assert_eq!(expected_list_cost_state, cost_track);
+
+ // in 2.1, this is still ExpressionStackDepthTooDeep
+ let mut cost_track = UnitTestTracker::new();
+ let err = build_ast_with_rules(
+ &QualifiedContractIdentifier::transient(),
+ &exceeds_stack_depth_list,
+ &mut cost_track,
+ *clarity_version,
+ StacksEpochId::Epoch21,
+ ASTRules::PrecheckSize,
+ )
+ .expect_err("Contract should error in parsing");
+
+ let expected_err = ParseErrors::ExpressionStackDepthTooDeep;
+ let expected_list_cost_state = UnitTestTracker {
+ invoked_functions: vec![(ClarityCostFunction::AstParse, vec![500])],
+ invocation_count: 1,
+ cost_addition_count: 1,
+ };
+
+ assert_eq!(&expected_err, &err.err);
+ assert_eq!(expected_list_cost_state, cost_track);
+
+ // in 2.1, ASTRules::Typical is ignored -- this still fails to parse
+ let mut cost_track = UnitTestTracker::new();
+ let _ = build_ast_with_rules(
+ &QualifiedContractIdentifier::transient(),
+ &exceeds_stack_depth_tuple,
+ &mut cost_track,
+ *clarity_version,
+ StacksEpochId::Epoch21,
+ ASTRules::Typical,
+ )
+ .expect_err("Contract should error in parsing");
+
+ let expected_err = ParseErrors::ExpressionStackDepthTooDeep;
+ let expected_list_cost_state = UnitTestTracker {
+ invoked_functions: vec![(ClarityCostFunction::AstParse, vec![571])],
+ invocation_count: 1,
+ cost_addition_count: 1,
+ };
+
+ assert_eq!(&expected_err, &err.err);
+ assert_eq!(expected_list_cost_state, cost_track);
+
+ // in 2.1, ASTRules::PrecheckSize is still ignored -- this still fails to parse
+ let mut cost_track = UnitTestTracker::new();
+ let err = build_ast_with_rules(
+ &QualifiedContractIdentifier::transient(),
+ &exceeds_stack_depth_tuple,
+ &mut cost_track,
+ *clarity_version,
+ StacksEpochId::Epoch21,
+ ASTRules::PrecheckSize,
+ )
+ .expect_err("Contract should error in parsing");
+
+ let expected_err = ParseErrors::ExpressionStackDepthTooDeep;
+ let expected_list_cost_state = UnitTestTracker {
+ invoked_functions: vec![(ClarityCostFunction::AstParse, vec![571])],
+ invocation_count: 1,
+ cost_addition_count: 1,
+ };
+
+ assert_eq!(&expected_err, &err.err);
+ assert_eq!(expected_list_cost_state, cost_track);
+ }
+ }
+
+ #[test]
+ fn test_expression_identification_tuples() {
+ for version in &[ClarityVersion::Clarity1, ClarityVersion::Clarity2] {
+ for epoch in &[StacksEpochId::Epoch2_05, StacksEpochId::Epoch21] {
+ let progn = "{ a: (+ 1 2 3),
+ b: 1,
+ c: 3 }";
+
+ let mut cost_track = LimitedCostTracker::new_free();
+ let ast = build_ast(
+ &QualifiedContractIdentifier::transient(),
+ progn,
+ &mut cost_track,
+ *version,
+ *epoch,
+ )
+ .unwrap()
+ .expressions;
+
+ let mut visited = HashMap::new();
+
+ for expr in ast.iter() {
+ depth_traverse::<_, _, ()>(expr, |x| {
+ assert!(!visited.contains_key(&x.id));
+ visited.insert(x.id, true);
+ Ok(())
+ })
+ .unwrap();
+ }
+ }
+ }
+ }
+}
diff --git a/clarity/src/vm/ast/mod.rs b/clarity/src/vm/ast/mod.rs
index 263fc86526..c02c709bd0 100644
--- a/clarity/src/vm/ast/mod.rs
+++ b/clarity/src/vm/ast/mod.rs
@@ -14,610 +14,22 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+#[cfg(feature = "vm")]
pub mod definition_sorter;
+pub mod errors;
+#[cfg(feature = "vm")]
pub mod expression_identifier;
pub mod parser;
-pub mod traits_resolver;
-
-pub mod errors;
+#[cfg(feature = "vm")]
pub mod stack_depth_checker;
+#[cfg(feature = "vm")]
pub mod sugar_expander;
+#[cfg(feature = "vm")]
+pub mod traits_resolver;
+#[cfg(feature = "vm")]
pub mod types;
-use stacks_common::types::StacksEpochId;
-
-use self::definition_sorter::DefinitionSorter;
-use self::errors::ParseResult;
-use self::expression_identifier::ExpressionIdentifier;
-use self::parser::v1::{parse as parse_v1, parse_no_stack_limit as parse_v1_no_stack_limit};
-use self::parser::v2::parse as parse_v2;
-use self::stack_depth_checker::{StackDepthChecker, VaryStackDepthChecker};
-use self::sugar_expander::SugarExpander;
-use self::traits_resolver::TraitsResolver;
-use self::types::BuildASTPass;
-pub use self::types::ContractAST;
-use crate::vm::costs::cost_functions::ClarityCostFunction;
-use crate::vm::costs::{runtime_cost, CostTracker};
-use crate::vm::diagnostic::{Diagnostic, Level};
-use crate::vm::representations::PreSymbolicExpression;
-use crate::vm::types::QualifiedContractIdentifier;
-use crate::vm::ClarityVersion;
-
-/// Legacy function
-#[cfg(any(test, feature = "testing"))]
-pub fn parse(
- contract_identifier: &QualifiedContractIdentifier,
- source_code: &str,
- version: ClarityVersion,
- epoch: StacksEpochId,
-) -> Result, crate::vm::errors::Error> {
- let ast = build_ast(contract_identifier, source_code, &mut (), version, epoch)?;
- Ok(ast.expressions)
-}
-
-// AST parser rulesets to apply.
-define_u8_enum!(ASTRules {
- Typical = 0,
- PrecheckSize = 1
-});
-
-/// Parse a program based on which epoch is active
-fn parse_in_epoch(
- source_code: &str,
- epoch_id: StacksEpochId,
- ast_rules: ASTRules,
-) -> ParseResult> {
- if epoch_id >= StacksEpochId::Epoch21 {
- parse_v2(source_code)
- } else if ast_rules == ASTRules::Typical {
- parse_v1_no_stack_limit(source_code)
- } else {
- parse_v1(source_code)
- }
-}
-
-/// This is the part of the AST parser that runs without respect to cost analysis, specifically
-/// pertaining to verifying that the AST is reasonably-sized.
-/// Used mainly to filter transactions that might be too costly, as an optimization heuristic.
-pub fn ast_check_size(
- contract_identifier: &QualifiedContractIdentifier,
- source_code: &str,
- clarity_version: ClarityVersion,
- epoch_id: StacksEpochId,
-) -> ParseResult {
- let pre_expressions = parse_in_epoch(source_code, epoch_id, ASTRules::PrecheckSize)?;
- let mut contract_ast = ContractAST::new(contract_identifier.clone(), pre_expressions);
- StackDepthChecker::run_pass(&mut contract_ast, clarity_version)?;
- VaryStackDepthChecker::run_pass(&mut contract_ast, clarity_version)?;
- Ok(contract_ast)
-}
-
-/// Build an AST according to a ruleset
-pub fn build_ast_with_rules(
- contract_identifier: &QualifiedContractIdentifier,
- source_code: &str,
- cost_track: &mut T,
- clarity_version: ClarityVersion,
- epoch: StacksEpochId,
- ruleset: ASTRules,
-) -> ParseResult {
- match ruleset {
- // After epoch 2.1, prechecking the size is required
- ASTRules::Typical if epoch < StacksEpochId::Epoch21 => build_ast_typical(
- contract_identifier,
- source_code,
- cost_track,
- clarity_version,
- epoch,
- ),
- _ => build_ast_precheck_size(
- contract_identifier,
- source_code,
- cost_track,
- clarity_version,
- epoch,
- ),
- }
-}
-
-/// Build an AST with the typical rules
-fn build_ast_typical(
- contract_identifier: &QualifiedContractIdentifier,
- source_code: &str,
- cost_track: &mut T,
- clarity_version: ClarityVersion,
- epoch: StacksEpochId,
-) -> ParseResult {
- let (contract, _, _) = inner_build_ast(
- contract_identifier,
- source_code,
- cost_track,
- clarity_version,
- epoch,
- ASTRules::Typical,
- true,
- )?;
- Ok(contract)
-}
-
-/// Used by developer tools only. Continues on through errors by inserting
-/// placeholders into the AST. Collects as many diagnostics as possible.
-/// Always returns a ContractAST, a vector of diagnostics, and a boolean
-/// that indicates if the build was successful.
-#[allow(clippy::unwrap_used)]
-pub fn build_ast_with_diagnostics(
- contract_identifier: &QualifiedContractIdentifier,
- source_code: &str,
- cost_track: &mut T,
- clarity_version: ClarityVersion,
- epoch: StacksEpochId,
-) -> (ContractAST, Vec, bool) {
- inner_build_ast(
- contract_identifier,
- source_code,
- cost_track,
- clarity_version,
- epoch,
- ASTRules::PrecheckSize,
- false,
- )
- .unwrap()
-}
-
-fn inner_build_ast(
- contract_identifier: &QualifiedContractIdentifier,
- source_code: &str,
- cost_track: &mut T,
- clarity_version: ClarityVersion,
- epoch: StacksEpochId,
- ast_rules: ASTRules,
- error_early: bool,
-) -> ParseResult<(ContractAST, Vec, bool)> {
- let cost_err = match runtime_cost(
- ClarityCostFunction::AstParse,
- cost_track,
- source_code.len() as u64,
- ) {
- Err(e) if error_early => return Err(e.into()),
- Err(e) => Some(e),
- _ => None,
- };
-
- let (pre_expressions, mut diagnostics, mut success) = if epoch >= StacksEpochId::Epoch21 {
- if error_early {
- let exprs = parser::v2::parse(source_code)?;
- (exprs, Vec::new(), true)
- } else {
- parser::v2::parse_collect_diagnostics(source_code)
- }
- } else {
- let parse_result = match ast_rules {
- ASTRules::Typical => parse_v1_no_stack_limit(source_code),
- ASTRules::PrecheckSize => parse_v1(source_code),
- };
- match parse_result {
- Ok(pre_expressions) => (pre_expressions, vec![], true),
- Err(error) if error_early => return Err(error),
- Err(error) => (vec![], vec![error.diagnostic], false),
- }
- };
-
- if let Some(e) = cost_err {
- diagnostics.insert(
- 0,
- Diagnostic {
- level: Level::Error,
- message: format!("runtime_cost error: {:?}", e),
- spans: vec![],
- suggestion: None,
- },
- );
- }
-
- let mut contract_ast = ContractAST::new(contract_identifier.clone(), pre_expressions);
- match StackDepthChecker::run_pass(&mut contract_ast, clarity_version) {
- Err(e) if error_early => return Err(e),
- Err(e) => {
- diagnostics.push(e.diagnostic);
- success = false;
- }
- _ => (),
- }
-
- if ast_rules != ASTRules::Typical {
- // run extra stack-depth pass for tuples
- match VaryStackDepthChecker::run_pass(&mut contract_ast, clarity_version) {
- Err(e) if error_early => return Err(e),
- Err(e) => {
- diagnostics.push(e.diagnostic);
- success = false;
- }
- _ => (),
- }
- }
-
- match ExpressionIdentifier::run_pre_expression_pass(&mut contract_ast, clarity_version) {
- Err(e) if error_early => return Err(e),
- Err(e) => {
- diagnostics.push(e.diagnostic);
- success = false;
- }
- _ => (),
- }
- match DefinitionSorter::run_pass(&mut contract_ast, cost_track, clarity_version) {
- Err(e) if error_early => return Err(e),
- Err(e) => {
- diagnostics.push(e.diagnostic);
- success = false;
- }
- _ => (),
- }
- match TraitsResolver::run_pass(&mut contract_ast, clarity_version) {
- Err(e) if error_early => return Err(e),
- Err(e) => {
- diagnostics.push(e.diagnostic);
- success = false;
- }
- _ => (),
- }
- match SugarExpander::run_pass(&mut contract_ast, clarity_version) {
- Err(e) if error_early => return Err(e),
- Err(e) => {
- diagnostics.push(e.diagnostic);
- success = false;
- }
- _ => (),
- }
- match ExpressionIdentifier::run_expression_pass(&mut contract_ast, clarity_version) {
- Err(e) if error_early => return Err(e),
- Err(e) => {
- diagnostics.push(e.diagnostic);
- success = false;
- }
- _ => (),
- }
- Ok((contract_ast, diagnostics, success))
-}
-
-/// Built an AST, but pre-check the size of the AST before doing more work
-fn build_ast_precheck_size(
- contract_identifier: &QualifiedContractIdentifier,
- source_code: &str,
- cost_track: &mut T,
- clarity_version: ClarityVersion,
- epoch: StacksEpochId,
-) -> ParseResult {
- let (contract, _, _) = inner_build_ast(
- contract_identifier,
- source_code,
- cost_track,
- clarity_version,
- epoch,
- ASTRules::PrecheckSize,
- true,
- )?;
- Ok(contract)
-}
-
-/// Test compatibility
-#[cfg(any(test, feature = "testing"))]
-pub fn build_ast(
- contract_identifier: &QualifiedContractIdentifier,
- source_code: &str,
- cost_track: &mut T,
- clarity_version: ClarityVersion,
- epoch_id: StacksEpochId,
-) -> ParseResult {
- build_ast_typical(
- contract_identifier,
- source_code,
- cost_track,
- clarity_version,
- epoch_id,
- )
-}
-
-#[cfg(test)]
-mod test {
- use hashbrown::HashMap;
- use stacks_common::types::StacksEpochId;
-
- use crate::vm::ast::errors::ParseErrors;
- use crate::vm::ast::stack_depth_checker::AST_CALL_STACK_DEPTH_BUFFER;
- use crate::vm::ast::{build_ast, build_ast_with_rules, ASTRules};
- use crate::vm::costs::{LimitedCostTracker, *};
- use crate::vm::representations::depth_traverse;
- use crate::vm::types::QualifiedContractIdentifier;
- use crate::vm::{ClarityCostFunction, ClarityName, ClarityVersion, MAX_CALL_STACK_DEPTH};
-
- #[derive(PartialEq, Debug)]
- struct UnitTestTracker {
- invoked_functions: Vec<(ClarityCostFunction, Vec)>,
- invocation_count: u64,
- cost_addition_count: u64,
- }
- impl UnitTestTracker {
- pub fn new() -> Self {
- UnitTestTracker {
- invoked_functions: vec![],
- invocation_count: 0,
- cost_addition_count: 0,
- }
- }
- }
- impl CostTracker for UnitTestTracker {
- fn compute_cost(
- &mut self,
- cost_f: ClarityCostFunction,
- input: &[u64],
- ) -> std::result::Result {
- self.invoked_functions.push((cost_f, input.to_vec()));
- self.invocation_count += 1;
- Ok(ExecutionCost::ZERO)
- }
- fn add_cost(&mut self, _cost: ExecutionCost) -> std::result::Result<(), CostErrors> {
- self.cost_addition_count += 1;
- Ok(())
- }
- fn add_memory(&mut self, _memory: u64) -> std::result::Result<(), CostErrors> {
- Ok(())
- }
- fn drop_memory(&mut self, _memory: u64) -> std::result::Result<(), CostErrors> {
- Ok(())
- }
- fn reset_memory(&mut self) {}
- fn short_circuit_contract_call(
- &mut self,
- _contract: &QualifiedContractIdentifier,
- _function: &ClarityName,
- _input: &[u64],
- ) -> Result {
- Ok(false)
- }
- }
-
- #[test]
- fn test_cost_tracking_deep_contracts_2_05() {
- let clarity_version = ClarityVersion::Clarity1;
- let stack_limit =
- (AST_CALL_STACK_DEPTH_BUFFER + (MAX_CALL_STACK_DEPTH as u64) + 1) as usize;
- let exceeds_stack_depth_tuple = format!(
- "{}u1 {}",
- "{ a : ".repeat(stack_limit + 1),
- "} ".repeat(stack_limit + 1)
- );
-
- // for deep lists, a test like this works:
- // it can assert a limit, that you can also verify
- // by disabling `VaryStackDepthChecker` and arbitrarily bumping up the parser lexer limits
- // and see that it produces the same result
- let exceeds_stack_depth_list = format!(
- "{}u1 {}",
- "(list ".repeat(stack_limit + 1),
- ")".repeat(stack_limit + 1)
- );
-
- // with old rules, this is just ExpressionStackDepthTooDeep
- let mut cost_track = UnitTestTracker::new();
- let err = build_ast_with_rules(
- &QualifiedContractIdentifier::transient(),
- &exceeds_stack_depth_list,
- &mut cost_track,
- clarity_version,
- StacksEpochId::Epoch2_05,
- ASTRules::Typical,
- )
- .expect_err("Contract should error in parsing");
-
- let expected_err = ParseErrors::ExpressionStackDepthTooDeep;
- let expected_list_cost_state = UnitTestTracker {
- invoked_functions: vec![(ClarityCostFunction::AstParse, vec![500])],
- invocation_count: 1,
- cost_addition_count: 1,
- };
-
- assert_eq!(&expected_err, &err.err);
- assert_eq!(expected_list_cost_state, cost_track);
-
- // with new rules, this is now VaryExpressionStackDepthTooDeep
- let mut cost_track = UnitTestTracker::new();
- let err = build_ast_with_rules(
- &QualifiedContractIdentifier::transient(),
- &exceeds_stack_depth_list,
- &mut cost_track,
- clarity_version,
- StacksEpochId::Epoch2_05,
- ASTRules::PrecheckSize,
- )
- .expect_err("Contract should error in parsing");
-
- let expected_err = ParseErrors::VaryExpressionStackDepthTooDeep;
- let expected_list_cost_state = UnitTestTracker {
- invoked_functions: vec![(ClarityCostFunction::AstParse, vec![500])],
- invocation_count: 1,
- cost_addition_count: 1,
- };
-
- assert_eq!(&expected_err, &err.err);
- assert_eq!(expected_list_cost_state, cost_track);
-
- // you cannot do the same for tuples!
- // in ASTRules::Typical, this passes
- let mut cost_track = UnitTestTracker::new();
- let _ = build_ast_with_rules(
- &QualifiedContractIdentifier::transient(),
- &exceeds_stack_depth_tuple,
- &mut cost_track,
- clarity_version,
- StacksEpochId::Epoch2_05,
- ASTRules::Typical,
- )
- .expect("Contract should parse with ASTRules::Typical");
-
- // this actually won't even error without
- // the VaryStackDepthChecker changes.
- let mut cost_track = UnitTestTracker::new();
- let err = build_ast_with_rules(
- &QualifiedContractIdentifier::transient(),
- &exceeds_stack_depth_tuple,
- &mut cost_track,
- clarity_version,
- StacksEpochId::Epoch2_05,
- ASTRules::PrecheckSize,
- )
- .expect_err("Contract should error in parsing with ASTRules::PrecheckSize");
-
- let expected_err = ParseErrors::VaryExpressionStackDepthTooDeep;
- let expected_list_cost_state = UnitTestTracker {
- invoked_functions: vec![(ClarityCostFunction::AstParse, vec![571])],
- invocation_count: 1,
- cost_addition_count: 1,
- };
-
- assert_eq!(&expected_err, &err.err);
- assert_eq!(expected_list_cost_state, cost_track);
- }
-
- #[test]
- fn test_cost_tracking_deep_contracts_2_1() {
- for clarity_version in &[ClarityVersion::Clarity1, ClarityVersion::Clarity2] {
- let stack_limit =
- (AST_CALL_STACK_DEPTH_BUFFER + (MAX_CALL_STACK_DEPTH as u64) + 1) as usize;
- let exceeds_stack_depth_tuple = format!(
- "{}u1 {}",
- "{ a : ".repeat(stack_limit + 1),
- "} ".repeat(stack_limit + 1)
- );
-
- // for deep lists, a test like this works:
- // it can assert a limit, that you can also verify
- // by disabling `VaryStackDepthChecker` and arbitrarily bumping up the parser lexer limits
- // and see that it produces the same result
- let exceeds_stack_depth_list = format!(
- "{}u1 {}",
- "(list ".repeat(stack_limit + 1),
- ")".repeat(stack_limit + 1)
- );
-
- // with old rules, this is just ExpressionStackDepthTooDeep
- let mut cost_track = UnitTestTracker::new();
- let err = build_ast_with_rules(
- &QualifiedContractIdentifier::transient(),
- &exceeds_stack_depth_list,
- &mut cost_track,
- *clarity_version,
- StacksEpochId::Epoch21,
- ASTRules::Typical,
- )
- .expect_err("Contract should error in parsing");
-
- let expected_err = ParseErrors::ExpressionStackDepthTooDeep;
- let expected_list_cost_state = UnitTestTracker {
- invoked_functions: vec![(ClarityCostFunction::AstParse, vec![500])],
- invocation_count: 1,
- cost_addition_count: 1,
- };
-
- assert_eq!(&expected_err, &err.err);
- assert_eq!(expected_list_cost_state, cost_track);
-
- // in 2.1, this is still ExpressionStackDepthTooDeep
- let mut cost_track = UnitTestTracker::new();
- let err = build_ast_with_rules(
- &QualifiedContractIdentifier::transient(),
- &exceeds_stack_depth_list,
- &mut cost_track,
- *clarity_version,
- StacksEpochId::Epoch21,
- ASTRules::PrecheckSize,
- )
- .expect_err("Contract should error in parsing");
-
- let expected_err = ParseErrors::ExpressionStackDepthTooDeep;
- let expected_list_cost_state = UnitTestTracker {
- invoked_functions: vec![(ClarityCostFunction::AstParse, vec![500])],
- invocation_count: 1,
- cost_addition_count: 1,
- };
-
- assert_eq!(&expected_err, &err.err);
- assert_eq!(expected_list_cost_state, cost_track);
-
- // in 2.1, ASTRules::Typical is ignored -- this still fails to parse
- let mut cost_track = UnitTestTracker::new();
- let _ = build_ast_with_rules(
- &QualifiedContractIdentifier::transient(),
- &exceeds_stack_depth_tuple,
- &mut cost_track,
- *clarity_version,
- StacksEpochId::Epoch21,
- ASTRules::Typical,
- )
- .expect_err("Contract should error in parsing");
-
- let expected_err = ParseErrors::ExpressionStackDepthTooDeep;
- let expected_list_cost_state = UnitTestTracker {
- invoked_functions: vec![(ClarityCostFunction::AstParse, vec![571])],
- invocation_count: 1,
- cost_addition_count: 1,
- };
-
- assert_eq!(&expected_err, &err.err);
- assert_eq!(expected_list_cost_state, cost_track);
-
- // in 2.1, ASTRules::PrecheckSize is still ignored -- this still fails to parse
- let mut cost_track = UnitTestTracker::new();
- let err = build_ast_with_rules(
- &QualifiedContractIdentifier::transient(),
- &exceeds_stack_depth_tuple,
- &mut cost_track,
- *clarity_version,
- StacksEpochId::Epoch21,
- ASTRules::PrecheckSize,
- )
- .expect_err("Contract should error in parsing");
-
- let expected_err = ParseErrors::ExpressionStackDepthTooDeep;
- let expected_list_cost_state = UnitTestTracker {
- invoked_functions: vec![(ClarityCostFunction::AstParse, vec![571])],
- invocation_count: 1,
- cost_addition_count: 1,
- };
-
- assert_eq!(&expected_err, &err.err);
- assert_eq!(expected_list_cost_state, cost_track);
- }
- }
-
- #[test]
- fn test_expression_identification_tuples() {
- for version in &[ClarityVersion::Clarity1, ClarityVersion::Clarity2] {
- for epoch in &[StacksEpochId::Epoch2_05, StacksEpochId::Epoch21] {
- let progn = "{ a: (+ 1 2 3),
- b: 1,
- c: 3 }";
-
- let mut cost_track = LimitedCostTracker::new_free();
- let ast = build_ast(
- &QualifiedContractIdentifier::transient(),
- progn,
- &mut cost_track,
- *version,
- *epoch,
- )
- .unwrap()
- .expressions;
-
- let mut visited = HashMap::new();
- for expr in ast.iter() {
- depth_traverse::<_, _, ()>(expr, |x| {
- assert!(!visited.contains_key(&x.id));
- visited.insert(x.id, true);
- Ok(())
- })
- .unwrap();
- }
- }
- }
- }
-}
+#[cfg(feature = "vm")]
+pub mod ast_builder;
+#[cfg(feature = "vm")]
+pub use ast_builder::*;
diff --git a/clarity/src/vm/ast/parser/mod.rs b/clarity/src/vm/ast/parser/mod.rs
index ced9f3aafc..0e54c950cc 100644
--- a/clarity/src/vm/ast/parser/mod.rs
+++ b/clarity/src/vm/ast/parser/mod.rs
@@ -14,5 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+#[cfg(feature = "vm")]
pub mod v1;
pub mod v2;
diff --git a/clarity/src/vm/ast/parser/v2/lexer/mod.rs b/clarity/src/vm/ast/parser/v2/lexer/mod.rs
index bbd6136916..6775bee523 100644
--- a/clarity/src/vm/ast/parser/v2/lexer/mod.rs
+++ b/clarity/src/vm/ast/parser/v2/lexer/mod.rs
@@ -1425,7 +1425,7 @@ mod tests {
+-*/ < <= >
>=.: ;; comment
"hello" u"world" 0x0123456789abcdeffedcba9876543210
-
+
foo-bar_
"#,
diff --git a/clarity/src/vm/ast/parser/v2/mod.rs b/clarity/src/vm/ast/parser/v2/mod.rs
index dd5a900364..046a151535 100644
--- a/clarity/src/vm/ast/parser/v2/mod.rs
+++ b/clarity/src/vm/ast/parser/v2/mod.rs
@@ -1,3613 +1,6 @@
pub mod lexer;
-use stacks_common::util::hash::hex_bytes;
-
-use self::lexer::token::{PlacedToken, Token};
-use self::lexer::Lexer;
-use crate::vm::ast::errors::{ParseError, ParseErrors, ParseResult, PlacedError};
-use crate::vm::ast::stack_depth_checker::AST_CALL_STACK_DEPTH_BUFFER;
-use crate::vm::diagnostic::{DiagnosableError, Diagnostic, Level};
-use crate::vm::representations::{ClarityName, ContractName, PreSymbolicExpression, Span};
-use crate::vm::types::{
- CharType, PrincipalData, QualifiedContractIdentifier, SequenceData, TraitIdentifier, UTF8Data,
- Value,
-};
-use crate::vm::MAX_CALL_STACK_DEPTH;
-
-pub struct Parser<'a> {
- lexer: Lexer<'a>,
- tokens: Vec,
- next_token: usize,
- diagnostics: Vec,
- success: bool,
- // `fail_fast` mode indicates that the parser should not report warnings
- // and should exit on the first error. This is useful for parsing in the
- // context of a stacks-node, while normal mode is useful for developers.
- fail_fast: bool,
- nesting_depth: u64,
-}
-
-pub const MAX_STRING_LEN: usize = 128;
-pub const MAX_CONTRACT_NAME_LEN: usize = 40;
-pub const MAX_NESTING_DEPTH: u64 = AST_CALL_STACK_DEPTH_BUFFER + (MAX_CALL_STACK_DEPTH as u64) + 1;
-
-enum OpenTupleStatus {
- /// The next thing to parse is a key
- ParseKey,
- /// The next thing to parse is a value
- ParseValue,
-}
-
-enum SetupTupleResult {
- OpenTuple(OpenTuple),
- Closed(PreSymbolicExpression),
-}
-
-struct OpenTuple {
- nodes: Vec,
- span: Span,
- /// Is the next node is expected to be a key or value? All of the preparatory work is done _before_ the parse loop tries to digest the next
- /// node (i.e., whitespace ingestion and checking for commas)
- expects: OpenTupleStatus,
- /// This is the last peeked token before trying to parse a key or value node, used for
- /// diagnostic reporting
- diagnostic_token: PlacedToken,
-}
-
-enum ParserStackElement {
- OpenList {
- nodes: Vec,
- span: Span,
- whitespace: bool,
- },
- OpenTuple(OpenTuple),
-}
-
-impl<'a> Parser<'a> {
- pub fn new(input: &'a str, fail_fast: bool) -> Result {
- let lexer = match Lexer::new(input, fail_fast) {
- Ok(lexer) => lexer,
- Err(e) => return Err(ParseErrors::Lexer(e)),
- };
- let mut p = Self {
- lexer,
- tokens: vec![],
- next_token: 0,
- diagnostics: vec![],
- success: true,
- fail_fast,
- nesting_depth: 0,
- };
-
- loop {
- let token = match p.lexer.read_token() {
- Ok(token) => token,
- Err(e) => {
- assert!(
- fail_fast,
- "Parser::read_token should not return an error when not in fail_fast mode"
- );
- p.success = false;
- return Err(ParseErrors::Lexer(e));
- }
- };
- if token.token == Token::Eof {
- p.tokens.push(token);
- break;
- }
- p.tokens.push(token);
- }
- p.diagnostics = p
- .lexer
- .diagnostics
- .iter()
- .map(|lex_error| PlacedError {
- e: ParseErrors::Lexer(lex_error.e.clone()),
- span: lex_error.span.clone(),
- })
- .collect();
- p.success = p.lexer.success;
- Ok(p)
- }
-
- fn add_diagnostic(&mut self, e: ParseErrors, span: Span) -> ParseResult<()> {
- if self.fail_fast {
- return Err(ParseError::new(e));
- } else {
- if e.level() == Level::Error {
- self.success = false;
- }
- self.diagnostics.push(PlacedError { e, span });
- }
- Ok(())
- }
-
- fn next_token(&mut self) -> Option {
- if self.next_token >= self.tokens.len() {
- return None;
- }
- let token = self.tokens[self.next_token].clone();
- self.next_token += 1;
- Some(token)
- }
-
- fn peek_next_token(&mut self) -> PlacedToken {
- if self.next_token >= self.tokens.len() {
- PlacedToken {
- span: Span {
- start_line: 1,
- start_column: 1,
- end_line: 1,
- end_column: 1,
- },
- token: Token::Eof,
- }
- } else {
- self.tokens[self.next_token].clone()
- }
- }
-
- /// Get a reference to the last processed token. If there is no last token,
- /// raises an UnexpectedParserFailure.
- fn peek_last_token(&self) -> ParseResult<&PlacedToken> {
- if self.next_token == 0 {
- return Err(ParseError::new(ParseErrors::UnexpectedParserFailure));
- }
- self.tokens
- .get(self.next_token - 1)
- .ok_or_else(|| ParseError::new(ParseErrors::UnexpectedParserFailure))
- }
-
- fn skip_to_end(&mut self) {
- self.next_token = self.tokens.len();
- }
-
- fn ignore_whitespace(&mut self) -> bool {
- let mut found = false;
- loop {
- if self.next_token >= self.tokens.len() {
- return found;
- }
- let token = &self.tokens[self.next_token];
- match &token.token {
- Token::Whitespace => {
- self.next_token += 1;
- found = true;
- }
- _ => return found,
- }
- }
- }
-
- fn ignore_whitespace_and_comments(&mut self) -> Vec {
- let mut comments = Vec::new();
- loop {
- if self.next_token >= self.tokens.len() {
- return comments;
- }
- let token = &self.tokens[self.next_token];
- match &token.token {
- Token::Whitespace => {
- self.next_token += 1;
- }
- Token::Comment(comment) => {
- let mut comment = PreSymbolicExpression::comment(comment.to_string());
- comment.copy_span(&token.span);
- comments.push(comment);
- self.next_token += 1;
- }
- _ => return comments,
- }
- }
- }
-
- // TODO: add tests from mutation testing results #4829
- #[cfg_attr(test, mutants::skip)]
- /// Process a new child node for an AST expression that is open and waiting for children nodes. For example,
- /// a list or tuple expression that is waiting for child expressions.
- ///
- /// Returns Some(node) if the open node is finished and should be popped from the stack.
- /// Returns None if the open node is not finished and should remain on the parser stack.
- fn handle_open_node(
- &mut self,
- open_node: &mut ParserStackElement,
- node_opt: Option,
- ) -> ParseResult