diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..cc360fb --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,50 @@ +name: CI + +on: + pull_request: + branches: + - "*" + +permissions: + deployments: read + contents: read + pull-requests: write + +jobs: + machete: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: bnjbvr/cargo-machete@main + + run-brainfuck-vm-unit-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2024-01-04 + - uses: Swatinem/rust-cache@v2 + - run: cargo +nightly-2024-01-04 test --lib + + run-brainfuck-vm-integration-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2024-01-04 + - uses: Swatinem/rust-cache@v2 + - run: cargo +nightly-2024-01-04 test --test '*' + + all-tests: + runs-on: ubuntu-latest + needs: + - machete + - run-brainfuck-vm-unit-tests + - run-brainfuck-vm-integration-tests + + steps: + - uses: re-actors/alls-green@v1.2.2 + with: + jobs: ${{ toJSON(needs) }} diff --git a/.github/workflows/trunk-check.yaml b/.github/workflows/trunk-check.yaml new file mode 100644 index 0000000..6377762 --- /dev/null +++ b/.github/workflows/trunk-check.yaml @@ -0,0 +1,22 @@ +name: Trunk +on: [pull_request] +concurrency: + group: ${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: read-all + +jobs: + trunk_check: + name: Trunk Code Quality Runner + runs-on: ubuntu-latest + permissions: + checks: write # For trunk to post annotations + contents: read # For repo checkout + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Trunk Code Quality + uses: trunk-io/trunk-action@v1 diff --git a/.trunk/.gitignore b/.trunk/.gitignore new file mode 100644 index 0000000..15966d0 --- /dev/null +++ b/.trunk/.gitignore @@ -0,0 +1,9 @@ +*out +*logs +*actions +*notifications +*tools +plugins +user_trunk.yaml +user.yaml +tmp diff --git a/.trunk/configs/.rustfmt.toml b/.trunk/configs/.rustfmt.toml new file mode 100644 index 0000000..994840e --- /dev/null +++ b/.trunk/configs/.rustfmt.toml @@ -0,0 +1,4 @@ +# See: https://rust-lang.github.io/rustfmt +normalize_comments = true +use_field_init_shorthand = true +edition = "2021" diff --git a/.trunk/configs/.shellcheckrc b/.trunk/configs/.shellcheckrc new file mode 100644 index 0000000..8c7b1ad --- /dev/null +++ b/.trunk/configs/.shellcheckrc @@ -0,0 +1,7 @@ +enable=all +source-path=SCRIPTDIR +disable=SC2154 + +# If you're having issues with shellcheck following source, disable the errors via: +# disable=SC1090 +# disable=SC1091 diff --git a/.trunk/configs/.yamllint.yaml b/.trunk/configs/.yamllint.yaml new file mode 100644 index 0000000..184e251 --- /dev/null +++ b/.trunk/configs/.yamllint.yaml @@ -0,0 +1,7 @@ +rules: + quoted-strings: + required: only-when-needed + extra-allowed: ["{|}"] + key-duplicates: {} + octal-values: + forbid-implicit-octal: true diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml new file mode 100644 index 0000000..3a5fb09 --- /dev/null +++ b/.trunk/trunk.yaml @@ -0,0 +1,48 @@ +# This file controls the behavior of Trunk: https://docs.trunk.io/cli +# To learn more about the format of this file, see https://docs.trunk.io/reference/trunk-yaml +version: 0.1 +cli: + version: 1.22.8 +plugins: + sources: + - id: trunk + ref: v1.6.4 + uri: https://github.com/trunk-io/plugins +actions: + disabled: + - trunk-announce + - trunk-check-pre-push + enabled: + - trunk-fmt-pre-commit + - trunk-upgrade-available +runtimes: + enabled: + - go@1.21.0 + - node@18.12.1 + - python@3.10.8 +downloads: + - name: rust + downloads: + - os: linux + url: https://static.rust-lang.org/dist/2024-01-04/rust-nightly-x86_64-unknown-linux-gnu.tar.gz + strip_components: 2 +lint: + definitions: + - name: clippy + commands: + - name: lint + run: cargo clippy --message-format json --locked --all-targets --all-features -- -D warnings + enabled: + - actionlint@1.7.4 + - checkov@3.2.278 + - clippy@1.65.0 + - git-diff-check + - markdownlint@0.42.0 + - osv-scanner@1.9.1 + - prettier@3.3.3 + - rustfmt@1.65.0 + - shellcheck@0.10.0 + - shfmt@3.6.0 + - taplo@0.9.3 + - trufflehog@3.83.2 + - yamllint@1.35.1 diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6d9cf7f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1170 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bigdecimal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "blake3" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brainfuck_prover" +version = "0.1.0" + +[[package]] +name = "brainfuck_vm" +version = "0.1.0" +dependencies = [ + "clap", + "num-traits", + "stwo-prover", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "educe" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bd92664bf78c4d3dba9b7cdafce6fa15b13ed3ed16175218196942e99168a8" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "starknet-crypto" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e2c30c01e8eb0fc913c4ee3cf676389fffc1d1182bfe5bb9670e4e72e968064" +dependencies = [ + "crypto-bigint", + "hex", + "hmac", + "num-bigint", + "num-integer", + "num-traits", + "rfc6979", + "sha2", + "starknet-crypto-codegen", + "starknet-curve", + "starknet-ff", + "zeroize", +] + +[[package]] +name = "starknet-crypto-codegen" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc159a1934c7be9761c237333a57febe060ace2bc9e3b337a59a37af206d19f" +dependencies = [ + "starknet-curve", + "starknet-ff", + "syn 2.0.85", +] + +[[package]] +name = "starknet-curve" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1c383518bb312751e4be80f53e8644034aa99a0afb29d7ac41b89a997db875b" +dependencies = [ + "starknet-ff", +] + +[[package]] +name = "starknet-ff" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abf1b44ec5b18d87c1ae5f54590ca9d0699ef4dd5b2ffa66fc97f24613ec585" +dependencies = [ + "ark-ff", + "bigdecimal", + "crypto-bigint", + "getrandom", + "hex", + "serde", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "stwo-prover" +version = "0.1.1" +source = "git+https://github.com/starkware-libs/stwo?rev=f6214d1#f6214d1c5a3f0a0857179027faaf559fdb17e2e6" +dependencies = [ + "blake2", + "blake3", + "bytemuck", + "cfg-if", + "downcast-rs", + "educe", + "hex", + "itertools 0.12.1", + "num-traits", + "rand", + "serde", + "starknet-crypto", + "starknet-ff", + "thiserror", + "tracing", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.85", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..87d4b7c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[workspace] +members = ["crates/brainfuck_vm", "crates/brainfuck_prover"] +resolver = "2" + +[workspace.package] +version = "0.1.0" +edition = "2021" +authors = ["Simon Malatrait "] +description = "Brainfuck ZK-VM with STWO" + +[workspace.dependencies] +clap = { version = "4.3.10", features = ["derive"] } +stwo-prover = { git = "https://github.com/starkware-libs/stwo", rev = "f6214d1" } +tracing = "0.1" +tracing-subscriber = "0.3" diff --git a/brainfuck_programs/a-bc.bf b/brainfuck_programs/a-bc.bf new file mode 100644 index 0000000..06aca27 --- /dev/null +++ b/brainfuck_programs/a-bc.bf @@ -0,0 +1 @@ +++>,<[>+.<-] diff --git a/brainfuck_programs/collatz.bf b/brainfuck_programs/collatz.bf new file mode 100644 index 0000000..4206ec7 --- /dev/null +++ b/brainfuck_programs/collatz.bf @@ -0,0 +1,11 @@ +>, +[ + ----------[ + >>>[>>>>]+[[-]+<[->>>>++>>>>+[>>>>]++[->+<<<<<]]<<<] + ++++++[>------<-]>--[>>[->>>>]+>+[<<<<]>-],< + ]> +]>>>++>+>>[ + <<[>>>>[-]+++++++++<[>-<-]+++++++++>[-[<->-]+[<<<<]]<[>+<-]>] + >[>[>>>>]+[[-]<[+[->>>>]>+<]>[<+>[<<<<]]+<<<<]>>>[->>>>]+>+[<<<<]] + >[[>+>>[<<<<+>>>>-]>]<<<<[-]>[-<<<<]]>>>>>>> +]>>+[[-]++++++>>>>]<<<<[[<++++++++>-]<.[-]<[-]<[-]<] diff --git a/brainfuck_programs/hello1.bf b/brainfuck_programs/hello1.bf new file mode 100644 index 0000000..265e751 --- /dev/null +++ b/brainfuck_programs/hello1.bf @@ -0,0 +1 @@ +++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. diff --git a/brainfuck_programs/hello2.bf b/brainfuck_programs/hello2.bf new file mode 100644 index 0000000..8fa0f72 --- /dev/null +++ b/brainfuck_programs/hello2.bf @@ -0,0 +1 @@ +++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++. diff --git a/brainfuck_programs/hello3.bf b/brainfuck_programs/hello3.bf new file mode 100644 index 0000000..281a0a5 --- /dev/null +++ b/brainfuck_programs/hello3.bf @@ -0,0 +1,2 @@ ++++++++++++[>++++++>+++++++++>++++++++>++++>+++>+<<<<<<-]>+++ ++++.>++.+++++++..+++.>>.>-.<<-.<.+++.------.--------.>>>+.>-. diff --git a/brainfuck_programs/hello4.bf b/brainfuck_programs/hello4.bf new file mode 100644 index 0000000..d71248a --- /dev/null +++ b/brainfuck_programs/hello4.bf @@ -0,0 +1,2 @@ +>++++++++[-<+++++++++>]<.>>+>-[+]++>++>+++[>[->+++<<+++>]<<]>-----.>-> ++++..+++.>-.<<+[>[+>+]>>]<--------------.>>.+++.------.--------.>+.>+. diff --git a/brainfuck_programs/loop.bf b/brainfuck_programs/loop.bf new file mode 100644 index 0000000..53cefec --- /dev/null +++ b/brainfuck_programs/loop.bf @@ -0,0 +1 @@ +[->+<] diff --git a/brainfuck_programs/sierpinski.bf b/brainfuck_programs/sierpinski.bf new file mode 100644 index 0000000..4c66685 --- /dev/null +++ b/brainfuck_programs/sierpinski.bf @@ -0,0 +1,5 @@ +++++++++[>+>++++<<-]>++>>+<[-[>>+<<-]+>>]>+[ + -<<<[ + ->[+[-]+>++>>>-<<]<[<]>>++++++[<<+++++>>-]+<<++.[-]<< + ]>.>+[>>]>+ +] diff --git a/crates/brainfuck_prover/Cargo.toml b/crates/brainfuck_prover/Cargo.toml new file mode 100644 index 0000000..1d813a5 --- /dev/null +++ b/crates/brainfuck_prover/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "brainfuck_prover" +version.workspace = true +edition.workspace = true +authors.workspace = true +description.workspace = true + +[dependencies] diff --git a/crates/brainfuck_prover/src/main.rs b/crates/brainfuck_prover/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/crates/brainfuck_prover/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/crates/brainfuck_vm/Cargo.toml b/crates/brainfuck_vm/Cargo.toml new file mode 100644 index 0000000..0d90c2f --- /dev/null +++ b/crates/brainfuck_vm/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "brainfuck_vm" +version.workspace = true +edition.workspace = true +authors.workspace = true +description.workspace = true + +[dependencies] +clap = { workspace = true, features = ["derive"] } +num-traits = "0.2.19" +stwo-prover.workspace = true +tracing.workspace = true +tracing-subscriber = { workspace = true, features = ["env-filter"] } diff --git a/crates/brainfuck_vm/src/bin/brainfuck_vm.rs b/crates/brainfuck_vm/src/bin/brainfuck_vm.rs new file mode 100644 index 0000000..3aa489d --- /dev/null +++ b/crates/brainfuck_vm/src/bin/brainfuck_vm.rs @@ -0,0 +1,58 @@ +// Adapted from rkdud007 brainfuck-zkvm https://github.com/rkdud007/brainfuck-zkvm/blob/main/src/main.rs + +use clap::{Parser, ValueHint}; +use std::{ + fs, + io::{stdin, stdout}, + path::PathBuf, +}; +use tracing_subscriber::{fmt, EnvFilter}; + +use brainfuck_vm::{compiler::Compiler, machine::Machine}; + +#[derive(Parser)] +#[clap(author, version, about, long_about = None)] +struct Args { + #[clap(value_parser, value_hint=ValueHint::FilePath)] + filename: PathBuf, + #[clap(long)] + trace: bool, + #[clap(long)] + pad_trace: bool, + #[clap(long)] + ram_size: Option, +} + +fn main() { + let args = Args::parse(); + + // Constructs a subscriber whose severity level is filtered by `RUST_LOG` + fmt().with_env_filter(EnvFilter::from_default_env()).init(); + + let code = fs::read_to_string(&args.filename) + .expect("Failed to read file") + .replace(' ', ""); + let mut bf_compiler = Compiler::new(code); + let ins = bf_compiler.compile(); + tracing::info!( + "Assembled instructions: {:?}", + ins.iter().map(|x| x.0).collect::>() + ); + tracing::info!("Program execution"); + let stdin = stdin(); + let stdout = stdout(); + let mut bf_vm = match args.ram_size { + Some(size) => Machine::new_with_config(ins, stdin, stdout, size), + None => Machine::new(ins, stdin, stdout), + }; + tracing::info!("Provide inputs separated by linefeeds: "); + bf_vm.execute().unwrap(); + if args.trace { + if args.pad_trace { + bf_vm.pad_trace(); + }; + let trace = bf_vm.get_trace(); + tracing::info!("Execution trace: {:#?}", trace); + } + // Ok(()) +} diff --git a/crates/brainfuck_vm/src/compiler.rs b/crates/brainfuck_vm/src/compiler.rs new file mode 100644 index 0000000..35eb8d2 --- /dev/null +++ b/crates/brainfuck_vm/src/compiler.rs @@ -0,0 +1,69 @@ +// Adapted from rkdud007 brainfuck-zkvm https://github.com/rkdud007/brainfuck-zkvm/blob/main/src/compiler.rs + +use stwo_prover::core::fields::m31::BaseField; + +pub struct Compiler { + code: Vec, + instructions: Vec, +} + +impl Compiler { + pub fn new(code: String) -> Self { + let trimmed_code = code.chars().filter(|c| !c.is_whitespace()).collect(); + Self { + code: trimmed_code, + instructions: vec![], + } + } + + pub fn compile(&mut self) -> Vec { + let mut loop_stack = vec![]; + for symbol in &self.code { + self.instructions.push(BaseField::from(*symbol as u32)); + + match *symbol { + '[' => { + self.instructions.push(BaseField::from(0)); + loop_stack.push(self.instructions.len() - 1); + } + ']' => { + let start_pos = loop_stack.pop().unwrap(); + let loop_end_pos = self.instructions.len() + 1; + self.instructions[start_pos] = BaseField::from((loop_end_pos - 1) as u32); + self.instructions + .push(BaseField::from((start_pos + 1) as u32)); + } + _ => (), + } + } + + self.instructions.clone() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let code = "++>,<[>+.<-]".to_string(); + let compiler = Compiler::new(code); + let expected_trimmed_code = + vec!['+', '+', '>', ',', '<', '[', '>', '+', '.', '<', '-', ']']; + assert_eq!(expected_trimmed_code, compiler.code,); + } + + #[test] + fn test_compile() { + let code = "++>,<[>+.<-]".to_string(); + let mut compiler = Compiler::new(code); + let instructions = compiler.compile(); + let expected_ins: Vec = + vec![43, 43, 62, 44, 60, 91, 13, 62, 43, 46, 60, 45, 93, 7] + .into_iter() + .map(|x| BaseField::from(x as u32)) + .collect(); + assert_eq!(instructions, expected_ins); + } +} diff --git a/crates/brainfuck_vm/src/instruction.rs b/crates/brainfuck_vm/src/instruction.rs new file mode 100644 index 0000000..8e6e3b7 --- /dev/null +++ b/crates/brainfuck_vm/src/instruction.rs @@ -0,0 +1,168 @@ +// Taken from rkdud007 brainfuck-zkvm https://github.com/rkdud007/brainfuck-zkvm/blob/main/src/instruction.rs + +use std::{fmt::Display, str::FromStr}; + +#[derive(Debug, Clone)] +pub struct Instruction { + pub ins_type: InstructionType, + pub argument: u8, +} + +#[derive(PartialEq, Debug, Clone)] +pub enum InstructionType { + // '>': Increment the data pointer (to point to the next cell to the right). + Right, + // '<': Decrement the data pointer (to point to the next cell to the left). + Left, + // '+': Increment (increase by one) the byte at the data pointer. + Plus, + // '-': Decrement (decrease by one) the byte at the data pointer. + Minus, + // '.': Output the byte at the data pointer. + PutChar, + // ',': Accept one byte of input, storing its value in the byte at the data pointer. + ReadChar, + // '[': If the byte at the data pointer is zero, then instead of moving the instruction pointer forward to the next command, jump it forward to the command after the matching ']' command. + JumpIfZero, + // ']': If the byte at the data pointer is nonzero, then instead of moving the instruction pointer forward to the next command, jump it back to the command after the matching '[' command. + JumpIfNotZero, +} + +impl FromStr for InstructionType { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + ">" => Ok(InstructionType::Right), + "<" => Ok(InstructionType::Left), + "+" => Ok(InstructionType::Plus), + "-" => Ok(InstructionType::Minus), + "." => Ok(InstructionType::PutChar), + "," => Ok(InstructionType::ReadChar), + "[" => Ok(InstructionType::JumpIfZero), + "]" => Ok(InstructionType::JumpIfNotZero), + _ => Err(()), + } + } +} + +impl Display for InstructionType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let symbol = match self { + InstructionType::Right => ">", + InstructionType::Left => "<", + InstructionType::Plus => "+", + InstructionType::Minus => "-", + InstructionType::PutChar => ".", + InstructionType::ReadChar => ",", + InstructionType::JumpIfZero => "[", + InstructionType::JumpIfNotZero => "]", + }; + write!(f, "{}", symbol) + } +} + +impl InstructionType { + pub fn from_u8(ins: u8) -> Self { + Self::from_str(&(ins as char).to_string()).expect("Invalid instruction") + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Test FromStr implementation + #[test] + fn test_instruction_type_from_str() { + // Test valid instruction mappings + assert_eq!( + InstructionType::from_str(">").unwrap(), + InstructionType::Right + ); + assert_eq!( + InstructionType::from_str("<").unwrap(), + InstructionType::Left + ); + assert_eq!( + InstructionType::from_str("+").unwrap(), + InstructionType::Plus + ); + assert_eq!( + InstructionType::from_str("-").unwrap(), + InstructionType::Minus + ); + assert_eq!( + InstructionType::from_str(".").unwrap(), + InstructionType::PutChar + ); + assert_eq!( + InstructionType::from_str(",").unwrap(), + InstructionType::ReadChar + ); + assert_eq!( + InstructionType::from_str("[").unwrap(), + InstructionType::JumpIfZero + ); + assert_eq!( + InstructionType::from_str("]").unwrap(), + InstructionType::JumpIfNotZero + ); + } + + // Test invalid input for FromStr + #[test] + fn test_instruction_type_from_str_invalid() { + assert!(InstructionType::from_str("x").is_err()); + assert!(InstructionType::from_str("").is_err()); + assert!(InstructionType::from_str("++").is_err()); + } + + // Test Display implementation + #[test] + fn test_instruction_type_display() { + assert_eq!(format!("{}", InstructionType::Right), ">"); + assert_eq!(format!("{}", InstructionType::Left), "<"); + assert_eq!(format!("{}", InstructionType::Plus), "+"); + assert_eq!(format!("{}", InstructionType::Minus), "-"); + assert_eq!(format!("{}", InstructionType::PutChar), "."); + assert_eq!(format!("{}", InstructionType::ReadChar), ","); + assert_eq!(format!("{}", InstructionType::JumpIfZero), "["); + assert_eq!(format!("{}", InstructionType::JumpIfNotZero), "]"); + } + + // Test from_u8 implementation + #[test] + fn test_instruction_type_from_u8() { + assert_eq!(InstructionType::from_u8(b'>'), InstructionType::Right); + assert_eq!(InstructionType::from_u8(b'<'), InstructionType::Left); + assert_eq!(InstructionType::from_u8(b'+'), InstructionType::Plus); + assert_eq!(InstructionType::from_u8(b'-'), InstructionType::Minus); + assert_eq!(InstructionType::from_u8(b'.'), InstructionType::PutChar); + assert_eq!(InstructionType::from_u8(b','), InstructionType::ReadChar); + assert_eq!(InstructionType::from_u8(b'['), InstructionType::JumpIfZero); + assert_eq!( + InstructionType::from_u8(b']'), + InstructionType::JumpIfNotZero + ); + } + + // Test from_u8 with invalid input (should panic) + #[test] + #[should_panic(expected = "Invalid instruction")] + fn test_instruction_type_from_u8_invalid() { + InstructionType::from_u8(b'x'); + } + + // Test Instruction struct creation + #[test] + fn test_instruction_creation() { + let instruction = Instruction { + ins_type: InstructionType::Right, + argument: 42, + }; + + assert_eq!(instruction.ins_type, InstructionType::Right); + assert_eq!(instruction.argument, 42); + } +} diff --git a/crates/brainfuck_vm/src/lib.rs b/crates/brainfuck_vm/src/lib.rs new file mode 100644 index 0000000..461a445 --- /dev/null +++ b/crates/brainfuck_vm/src/lib.rs @@ -0,0 +1,5 @@ +pub mod compiler; +pub mod instruction; +pub mod machine; +pub mod registers; +pub mod test_helper; diff --git a/crates/brainfuck_vm/src/machine.rs b/crates/brainfuck_vm/src/machine.rs new file mode 100644 index 0000000..0e78f35 --- /dev/null +++ b/crates/brainfuck_vm/src/machine.rs @@ -0,0 +1,424 @@ +// Adapted from rkdud007 brainfuck-zkvm https://github.com/rkdud007/brainfuck-zkvm/blob/main/src/machine.rs + +use std::{ + error::Error, + io::{Read, Write}, +}; + +use num_traits::identities::{One, Zero}; + +use stwo_prover::core::fields::{m31::BaseField, FieldExpOps}; + +use crate::{instruction::InstructionType, registers::Registers}; + +pub struct ProgramMemory { + code: Vec, +} + +pub struct MutableState { + ram: Vec, + registers: Registers, +} + +pub struct IO { + input: Box, + output: Box, +} + +pub struct Machine { + program: ProgramMemory, + state: MutableState, + io: IO, + trace: Vec, +} + +impl Machine { + pub const DEFAULT_RAM_SIZE: usize = 30000; + + pub fn new_with_config( + code: Vec, + input: R, + output: W, + ram_size: usize, + ) -> Machine + where + R: Read + 'static, + W: Write + 'static, + { + Machine { + program: ProgramMemory { code }, + state: MutableState { + ram: vec![BaseField::zero(); ram_size], + registers: Registers::new(), + }, + io: IO { + input: Box::new(input), + output: Box::new(output), + }, + trace: vec![], + } + } + + pub fn new(code: Vec, input: R, output: W) -> Machine + where + R: Read + 'static, + W: Write + 'static, + { + Self::new_with_config(code, input, output, Self::DEFAULT_RAM_SIZE) + } + + pub fn execute(&mut self) -> Result<(), Box> { + while self.state.registers.ip < BaseField::from(self.program.code.len()) { + self.state.registers.ci = self.program.code[self.state.registers.ip.0 as usize]; + self.state.registers.ni = + if self.state.registers.ip == BaseField::from(self.program.code.len() - 1) { + BaseField::zero() + } else { + self.program.code[(self.state.registers.ip + BaseField::one()).0 as usize] + }; + self.write_trace(); + let ins_type = InstructionType::from_u8(self.state.registers.ci.0 as u8); + self.execute_instruction(ins_type)?; + self.next_clock_cycle(); + } + + // Last clock cycle + self.state.registers.ci = BaseField::zero(); + self.state.registers.ni = BaseField::zero(); + self.write_trace(); + Ok(()) + } + + fn read_char(&mut self) -> Result<(), std::io::Error> { + let mut buf = [0; 1]; + self.io.input.read_exact(&mut buf)?; + let input_char = buf[0] as usize; + self.state.ram[self.state.registers.mp.0 as usize] = BaseField::from(input_char as u32); + Ok(()) + } + + fn write_char(&mut self) -> Result<(), std::io::Error> { + let char_to_write = self.state.ram[self.state.registers.mp.0 as usize].0 as u8; + self.io.output.write_all(&[char_to_write])?; + Ok(()) + } + + fn execute_instruction(&mut self, ins: InstructionType) -> Result<(), Box> { + match ins { + InstructionType::Right => { + self.state.registers.mp += BaseField::one(); + } + InstructionType::Left => { + self.state.registers.mp -= BaseField::one(); + } + InstructionType::Plus => { + let mp = self.state.registers.mp; + self.state.ram[mp.0 as usize] += BaseField::one(); + } + InstructionType::Minus => { + let mp = self.state.registers.mp; + self.state.ram[mp.0 as usize] -= BaseField::one(); + } + InstructionType::ReadChar => { + self.read_char()?; + } + InstructionType::PutChar => { + self.write_char()?; + } + InstructionType::JumpIfZero => { + let mp = self.state.registers.mp; + let ip = self.state.registers.ip; + let argument = self.program.code[(ip + BaseField::one()).0 as usize]; + self.state.registers.ni = argument; + if self.state.ram[mp.0 as usize] == BaseField::zero() { + self.state.registers.ip = argument; + return Ok(()); + } + self.state.registers.ip += BaseField::one(); + } + InstructionType::JumpIfNotZero => { + let mp = self.state.registers.mp; + let ip = self.state.registers.ip; + let argument = self.program.code[(ip + BaseField::one()).0 as usize]; + if self.state.ram[mp.0 as usize] != BaseField::zero() { + self.state.registers.ip = argument - BaseField::one(); + return Ok(()); + } + self.state.registers.ip += BaseField::one(); + } + } + self.state.registers.mv = self.state.ram[self.state.registers.mp.0 as usize]; + self.state.registers.mvi = if self.state.registers.mv == BaseField::zero() { + BaseField::zero() + } else { + self.state.registers.mv.inverse() + }; + + Ok(()) + } + + fn next_clock_cycle(&mut self) { + self.state.registers.clk += BaseField::one(); + self.state.registers.ip += BaseField::one(); + } + + fn write_trace(&mut self) { + self.trace.push(self.state.registers.clone()); + } + + pub fn get_trace(&self) -> Vec { + self.trace.clone() + } + + pub fn pad_trace(&mut self) { + let last_register = &self.state.registers; + let trace_len = self.trace.len() as u32; + let padding_offset = trace_len.next_power_of_two() + 1 - trace_len; + for i in 1..padding_offset { + let dummy = Registers { + clk: BaseField::from(last_register.clk.0 + i), + ..last_register.clone() + }; + self.trace.push(dummy); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_helper::*; + use stwo_prover::core::fields::m31::P; + + #[test] + fn test_default_machine_initialization() { + let code = vec![BaseField::from(43)]; + let (machine, _) = create_test_machine(code.clone(), &[]); + + assert_eq!(machine.program.code, code); + assert_eq!(machine.state.ram.len(), Machine::DEFAULT_RAM_SIZE); + assert!(machine.state.ram.iter().all(|&x| x == BaseField::zero())); + } + + #[test] + fn test_custom_ram_machine_initialization() { + let code = vec![BaseField::from(43)]; + let input: &[u8] = &[]; + let output = TestWriter::new(); + let ram_size = 55000; + let machine = Machine::new_with_config(code.clone(), input, output, ram_size); + + assert_eq!(machine.program.code, code); + assert_eq!(machine.state.ram.len(), ram_size); + assert!(machine.state.ram.iter().all(|&x| x == BaseField::zero())); + } + + #[test] + fn test_right_instruction() -> Result<(), Box> { + // '>>' + let code = vec![BaseField::from(62), BaseField::from(62)]; + let (mut machine, _) = create_test_machine(code, &[]); + machine.execute()?; + + assert_eq!(machine.state.registers.mp, BaseField::from(2)); + Ok(()) + } + + #[test] + fn test_left_instruction() -> Result<(), Box> { + // '>><' + let code = vec![ + BaseField::from(62), + BaseField::from(62), + BaseField::from(60), + ]; + let (mut machine, _) = create_test_machine(code, &[]); + machine.execute()?; + + assert_eq!(machine.state.registers.mp, BaseField::from(1)); + Ok(()) + } + + #[test] + fn test_plus_instruction() -> Result<(), Box> { + // '+' + let code = vec![BaseField::from(43)]; + let (mut machine, _) = create_test_machine(code, &[]); + machine.execute()?; + + assert_eq!(machine.state.ram[0], BaseField::from(1)); + assert_eq!(machine.state.registers.mv, BaseField::from(1)); + Ok(()) + } + #[test] + + fn test_minus_instruction() -> Result<(), Box> { + // '--' + let code = vec![BaseField::from(45), BaseField::from(45)]; + let (mut machine, _) = create_test_machine(code, &[]); + machine.execute()?; + + assert_eq!(machine.state.ram[0], BaseField::from(P - 2)); + assert_eq!(machine.state.registers.mv, BaseField::from(P - 2)); + Ok(()) + } + + #[test] + fn test_read_write_char() -> Result<(), Box> { + // ',.' + let code = vec![BaseField::from(44), BaseField::from(46)]; + let input = b"a"; + let (mut machine, output) = create_test_machine(code, input); + + machine.execute()?; + + let output_data = output.get_output(); + assert_eq!(output_data, input); + Ok(()) + } + + #[test] + fn test_skip_loop() -> Result<(), Box> { + // Skip the loop + // '[-]+' + let code = vec![ + BaseField::from(91), + BaseField::from(4), // Jump to index 5 if zero + BaseField::from(45), // This should be skipped + BaseField::from(93), + BaseField::from(2), + BaseField::from(43), + ]; + let (mut machine, _) = create_test_machine(code, &[]); + machine.execute()?; + + assert_eq!(machine.state.ram[0], BaseField::one()); + assert_eq!(machine.state.registers.mv, BaseField::one()); + Ok(()) + } + + #[test] + fn test_enter_loop() -> Result<(), Box> { + // Enter the loop + // '+[+>]' + let code = vec![ + BaseField::from(43), + BaseField::from(91), + BaseField::from(6), + BaseField::from(43), + BaseField::from(62), + BaseField::from(93), + BaseField::from(3), + ]; + let (mut machine, _) = create_test_machine(code, &[]); + machine.execute()?; + + assert_eq!(machine.state.ram[0], BaseField::from(2)); + assert_eq!(machine.state.registers.mp, BaseField::one()); + assert_eq!(machine.state.registers.mv, BaseField::zero()); + Ok(()) + } + + #[test] + fn test_get_trace() -> Result<(), Box> { + // '++' + let code = vec![BaseField::from(43), BaseField::from(43)]; + let (mut machine, _) = create_test_machine(code, &[]); + machine.execute()?; + + // Initial state + executed instructions + let trace = machine.get_trace(); + let initial_state = Registers { + clk: BaseField::zero(), + ip: BaseField::zero(), + ci: BaseField::from(43), + ni: BaseField::from(43), + mp: BaseField::zero(), + mv: BaseField::zero(), + mvi: BaseField::zero(), + }; + let intermediate_state = Registers { + clk: BaseField::one(), + ip: BaseField::one(), + ci: BaseField::from(43), + ni: BaseField::zero(), + mp: BaseField::zero(), + mv: BaseField::one(), + mvi: BaseField::one(), + }; + let final_state = Registers { + clk: BaseField::from(2), + ip: BaseField::from(2), + ci: BaseField::from(0), + ni: BaseField::from(0), + mp: BaseField::zero(), + mv: BaseField::from(2), + mvi: BaseField::from(2).inverse(), + }; + + assert_eq!(trace.len(), 3); + assert_eq!(trace[0], initial_state); + assert_eq!(trace[1], intermediate_state); + assert_eq!(trace[2], final_state); + + Ok(()) + } + + #[test] + fn test_pad_trace() -> Result<(), Box> { + // '++' + let code = vec![BaseField::from(43), BaseField::from(43)]; + let (mut machine, _) = create_test_machine(code, &[]); + machine.execute()?; + + // Initial state + executed instructions + let trace = machine.get_trace(); + let initial_state = Registers { + clk: BaseField::zero(), + ip: BaseField::zero(), + ci: BaseField::from(43), + ni: BaseField::from(43), + mp: BaseField::zero(), + mv: BaseField::zero(), + mvi: BaseField::zero(), + }; + let intermediate_state = Registers { + clk: BaseField::one(), + ip: BaseField::one(), + ci: BaseField::from(43), + ni: BaseField::zero(), + mp: BaseField::zero(), + mv: BaseField::one(), + mvi: BaseField::one(), + }; + let final_state = Registers { + clk: BaseField::from(2), + ip: BaseField::from(2), + ci: BaseField::from(0), + ni: BaseField::from(0), + mp: BaseField::zero(), + mv: BaseField::from(2), + mvi: BaseField::from(2).inverse(), + }; + + assert_eq!(trace.len(), 3); + assert_eq!(trace[0], initial_state); + assert_eq!(trace[1], intermediate_state); + assert_eq!(trace[2], final_state); + + machine.pad_trace(); + let trace = machine.get_trace(); + let dummy = Registers { + clk: final_state.clk + BaseField::one(), + ..final_state + }; + + assert_eq!(trace.len(), 4); + assert_eq!(trace[0], initial_state); + assert_eq!(trace[1], intermediate_state); + assert_eq!(trace[2], final_state); + assert_eq!(trace[3], dummy); + + Ok(()) + } +} diff --git a/crates/brainfuck_vm/src/registers.rs b/crates/brainfuck_vm/src/registers.rs new file mode 100644 index 0000000..2d3adff --- /dev/null +++ b/crates/brainfuck_vm/src/registers.rs @@ -0,0 +1,137 @@ +// Adapted from rkdud007 brainfuck-zkvm https://github.com/rkdud007/brainfuck-zkvm/blob/main/src/registers.rs + +use num_traits::identities::Zero; +use stwo_prover::core::fields::m31::BaseField; + +#[derive(PartialEq, Clone)] +pub struct Registers { + /// Clock Cycle Counter + pub clk: BaseField, + /// Instruction Pointer (i.e. PC) + pub ip: BaseField, + /// Current Instruction + pub ci: BaseField, + /// Next Instruction + pub ni: BaseField, + /// Memory Pointer (i.e. AP) + pub mp: BaseField, + /// Memory Value (i.e. [AP]) + pub mv: BaseField, + /// Memory Value Inverse + pub mvi: BaseField, +} + +impl Default for Registers { + fn default() -> Self { + Self::new() + } +} + +impl Registers { + pub fn new() -> Registers { + Registers { + clk: BaseField::zero(), + ip: BaseField::zero(), + ci: BaseField::zero(), + ni: BaseField::zero(), + mp: BaseField::zero(), + mv: BaseField::zero(), + mvi: BaseField::zero(), + } + } +} + +impl std::fmt::Display for Registers { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "clk:{}, ip:{}, ci:{}, ni:{}, mp:{}, mv:{}, mvi:{}", + self.clk, self.ip, self.ci, self.ni, self.mp, self.mv, self.mvi + ) + } +} + +impl std::fmt::Debug for Registers { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "clk:{}, ip:{}, ci:{}, ni:{}, mp:{}, mv:{}, mvi:{}", + self.clk, self.ip, self.ci, self.ni, self.mp, self.mv, self.mvi + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use num_traits::Zero; + + // Test default and new() implementation + #[test] + fn test_registers_default_and_new() { + // Create registers using default() method + let default_registers = Registers::default(); + + // Create registers using new() method + let new_registers = Registers::new(); + + // Check that all fields are zero + assert!( + default_registers.clk.is_zero(), + "Clock cycle should be zero" + ); + assert!( + default_registers.ip.is_zero(), + "Instruction pointer should be zero" + ); + assert!( + default_registers.ci.is_zero(), + "Current instruction should be zero" + ); + assert!( + default_registers.ni.is_zero(), + "Next instruction should be zero" + ); + assert!( + default_registers.mp.is_zero(), + "Memory pointer should be zero" + ); + assert!( + default_registers.mv.is_zero(), + "Memory value should be zero" + ); + assert!( + default_registers.mvi.is_zero(), + "Memory value inverse should be zero" + ); + + // Ensure default() and new() produce identical results + assert_eq!(default_registers.clk, new_registers.clk); + assert_eq!(default_registers.ip, new_registers.ip); + assert_eq!(default_registers.ci, new_registers.ci); + assert_eq!(default_registers.ni, new_registers.ni); + assert_eq!(default_registers.mp, new_registers.mp); + assert_eq!(default_registers.mv, new_registers.mv); + assert_eq!(default_registers.mvi, new_registers.mvi); + } + + // Test Display implementation + #[test] + fn test_registers_display() { + let registers = Registers { + clk: BaseField::from(1), + ip: BaseField::from(2), + ci: BaseField::from(3), + ni: BaseField::from(4), + mp: BaseField::from(5), + mv: BaseField::from(6), + mvi: BaseField::from(7), + }; + + let display_string = format!("{}", registers); + assert_eq!( + display_string, "clk:1, ip:2, ci:3, ni:4, mp:5, mv:6, mvi:7", + "Display format should match expected output" + ); + } +} diff --git a/crates/brainfuck_vm/src/test_helper.rs b/crates/brainfuck_vm/src/test_helper.rs new file mode 100644 index 0000000..ccdf2a5 --- /dev/null +++ b/crates/brainfuck_vm/src/test_helper.rs @@ -0,0 +1,55 @@ +use crate::machine::Machine; +use std::cell::RefCell; +use std::io::{Cursor, Write}; +use std::rc::Rc; +use stwo_prover::core::fields::m31::BaseField; + +pub struct TestWriter { + buffer: Rc>>, +} + +impl TestWriter { + pub fn new() -> Self { + TestWriter { + buffer: Rc::new(RefCell::new(Vec::new())), + } + } + + pub fn get_output(&self) -> Vec { + self.buffer.borrow().clone() + } +} + +impl Default for TestWriter { + fn default() -> Self { + Self::new() + } +} + +impl Write for TestWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.buffer.borrow_mut().extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +impl Clone for TestWriter { + fn clone(&self) -> Self { + TestWriter { + buffer: Rc::clone(&self.buffer), // This creates a new reference to the same buffer + } + } +} + +// Helper function to create a test machine +pub fn create_test_machine(code: Vec, input: &[u8]) -> (Machine, TestWriter) { + let input = Cursor::new(input.to_vec()); + let output = TestWriter::new(); + let test_output = output.clone(); + let machine = Machine::new(code, input, output); + (machine, test_output) +} diff --git a/crates/brainfuck_vm/tests/integration.rs b/crates/brainfuck_vm/tests/integration.rs new file mode 100644 index 0000000..a8415b5 --- /dev/null +++ b/crates/brainfuck_vm/tests/integration.rs @@ -0,0 +1,79 @@ +use brainfuck_vm::{compiler::Compiler, test_helper::*}; +use std::fs; +use stwo_prover::core::fields::m31::BaseField; + +fn compile_from_path(path: &str) -> Vec { + let code = fs::read_to_string(path) + .unwrap_or_else(|_| panic!("Failed to read file")) + .replace(' ', ""); + let mut bf_compiler = Compiler::new(code); + bf_compiler.compile() +} + +#[test] +fn test_a_bc() { + let path = "../../brainfuck_programs/a-bc.bf"; + + let code = compile_from_path(path); + let input = b"a"; + let (mut machine, output) = create_test_machine(code, input); + machine.execute().unwrap(); + let expected_output = b"bc".to_vec(); + assert_eq!(output.get_output(), expected_output); +} + +#[test] +fn test_collatz() { + let path = "../../brainfuck_programs/collatz.bf"; + + let code = compile_from_path(path); + let input = &[0x37, 10]; // 7 EOF + let (mut machine, output) = create_test_machine(code, input); + machine.execute().unwrap(); + let expected_output = [0x31, 0x36, 10].to_vec(); // 16 EOF + assert_eq!(output.get_output(), expected_output); +} + +#[test] +fn test_hello_world_1() { + let path = "../../brainfuck_programs/hello1.bf"; + + let code = compile_from_path(path); + let (mut machine, output) = create_test_machine(code, &[]); + machine.execute().unwrap(); + let expected_output = b"Hello World!\n".to_vec(); + assert_eq!(output.get_output(), expected_output); +} + +#[test] +fn test_hello_world_2() { + let path = "../../brainfuck_programs/hello2.bf"; + + let code = compile_from_path(path); + let (mut machine, output) = create_test_machine(code, &[]); + machine.execute().unwrap(); + let expected_output = b"Hello World!\n".to_vec(); + assert_eq!(output.get_output(), expected_output); +} + +#[test] +fn test_hello_world_3() { + let path = "../../brainfuck_programs/hello3.bf"; + + let code = compile_from_path(path); + let (mut machine, output) = create_test_machine(code, &[]); + machine.execute().unwrap(); + let expected_output = b"Hello, World!\n".to_vec(); + assert_eq!(output.get_output(), expected_output); +} + +#[test] +fn test_hello_world_4() { + let path = "../../brainfuck_programs/hello4.bf"; + + let code = compile_from_path(path); + let (mut machine, output) = create_test_machine(code, &[]); + machine.execute().unwrap(); + let expected_output = b"Hello World!\n".to_vec(); + assert_eq!(output.get_output(), expected_output); +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..26c4283 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2024-01-04" +profile = "default"