diff --git a/.gitignore b/.gitignore index bdf973f033..e965f02426 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ /target -wasm/target **/*.rs.bk **/*.bytecode __pycache__ diff --git a/.travis.yml b/.travis.yml index 2a28c7ef57..f9ec114857 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,3 @@ - language: rust rust: @@ -7,85 +6,87 @@ rust: - nightly script: - - cargo build --verbose --all - - cargo test --verbose --all + - cargo build --verbose --all + - cargo test --verbose --all env: - # This is used to only capture the regular nightly test in allow_failures - - REGULAR_TEST=true + # This is used to only capture the regular nightly test in allow_failures + - REGULAR_TEST=true cache: cargo matrix: - include: - # To test the snippets, we use Travis' Python environment (because - # installing rust ourselves is a lot easier than installing Python) - - language: python - python: 3.6 - cache: - pip: true - # Because we're using the Python Travis environment, we can't use - # the built-in cargo cacher - directories: - - /home/travis/.cargo - - target - env: - - TRAVIS_RUST_VERSION=stable - - REGULAR_TEST=false - script: tests/.travis-runner.sh - - language: python - python: 3.6 - cache: - pip: true - # Because we're using the Python Travis environment, we can't use - # the built-in cargo cacher - directories: - - /home/travis/.cargo - - target - env: - - TRAVIS_RUST_VERSION=beta - - REGULAR_TEST=false - script: tests/.travis-runner.sh - - name: rustfmt - language: rust - rust: stable - cache: cargo - before_script: - - rustup component add rustfmt-preview - script: - # Code references the generated python.rs, so put something in - # place to make `cargo fmt` happy. (We use `echo` rather than - # `touch` because rustfmt complains about the empty file touch - # creates.) - - echo > parser/src/python.rs - - cargo fmt --all -- --check - env: - - REGULAR_TEST=false - - name: publish documentation - language: rust - rust: stable - cache: cargo - script: - - cargo doc --no-deps --all - if: branch = release - env: - - REGULAR_TEST=false - - name: WASM online demo - language: rust - rust: nightly - cache: cargo - install: - - nvm install node - - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh # install wasm-pack - script: - - cd wasm - - bash release.sh - if: branch = release - env: - - REGULAR_TEST=false - allow_failures: - - rust: nightly - env: REGULAR_TEST=true + include: + # To test the snippets, we use Travis' Python environment (because + # installing rust ourselves is a lot easier than installing Python) + - language: python + python: 3.6 + cache: + pip: true + # Because we're using the Python Travis environment, we can't use + # the built-in cargo cacher + directories: + - /home/travis/.cargo + - target + env: + - TRAVIS_RUST_VERSION=stable + - REGULAR_TEST=false + script: tests/.travis-runner.sh + - language: python + python: 3.6 + cache: + pip: true + # Because we're using the Python Travis environment, we can't use + # the built-in cargo cacher + directories: + - /home/travis/.cargo + - target + env: + - TRAVIS_RUST_VERSION=beta + - REGULAR_TEST=false + script: tests/.travis-runner.sh + - name: rustfmt + language: rust + rust: stable + cache: cargo + before_script: + - rustup component add rustfmt-preview + script: + # Code references the generated python.rs, so put something in + # place to make `cargo fmt` happy. (We use `echo` rather than + # `touch` because rustfmt complains about the empty file touch + # creates.) + - echo > parser/src/python.rs + - cargo fmt --all -- --check + env: + - REGULAR_TEST=false + - name: publish documentation + language: rust + rust: stable + cache: cargo + script: + - cargo doc --no-deps --all + if: branch = release + env: + - REGULAR_TEST=false + - name: WASM online demo + language: rust + rust: nightly + cache: cargo + install: + - nvm install node + # install wasm-pack + - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + script: + - cd wasm/demo + - npm install + - npm run dist + if: branch = release + env: + - REGULAR_TEST=false + allow_failures: + - rust: nightly + env: REGULAR_TEST=true deploy: - provider: pages @@ -93,16 +94,18 @@ deploy: target-branch: master local-dir: target/doc skip-cleanup: true - github-token: $WEBSITE_GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable + # Set in the settings page of your repository, as a secure variable + github-token: $WEBSITE_GITHUB_TOKEN keep-history: true on: branch: release - provider: pages repo: RustPython/demo target-branch: master - local-dir: wasm/app/dist + local-dir: wasm/demo/dist skip-cleanup: true - github-token: $WEBSITE_GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable + # Set in the settings page of your repository, as a secure variable + github-token: $WEBSITE_GITHUB_TOKEN keep-history: true on: branch: release diff --git a/Cargo.lock b/Cargo.lock index 80f179fc31..ddc013a582 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,7 +136,7 @@ name = "caseless" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -230,10 +230,22 @@ dependencies = [ "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "env_logger" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "failure" version = "0.1.2" @@ -286,6 +298,14 @@ dependencies = [ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "heck" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "humantime" version = "1.1.1" @@ -307,6 +327,14 @@ name = "itoa" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "js-sys" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wasm-bindgen 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -426,6 +454,14 @@ name = "nodrop" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nom" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-bigint" version = "0.2.1" @@ -596,19 +632,19 @@ dependencies = [ "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -674,7 +710,7 @@ dependencies = [ "num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustpython_parser 0.0.1", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", @@ -682,6 +718,18 @@ dependencies = [ "statrs 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rustpython_wasm" +version = "0.1.0" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustpython_parser 0.0.1", + "rustpython_vm 0.1.0", + "wasm-bindgen 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustyline" version = "2.1.0" @@ -749,6 +797,11 @@ name = "siphasher" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "sourcefile" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "statrs" version = "0.10.0" @@ -808,6 +861,16 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syn" +version = "0.15.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "synstructure" version = "0.9.0" @@ -836,6 +899,14 @@ dependencies = [ "wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "termcolor" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "termion" version = "1.5.1" @@ -856,11 +927,10 @@ dependencies = [ [[package]] name = "thread_local" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -921,6 +991,89 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wasm-bindgen" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wasm-bindgen-macro 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro-support 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasm-bindgen-webidl" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "weedle 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "web-sys" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-webidl 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "weedle" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi" version = "0.2.8" @@ -945,6 +1098,14 @@ name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -958,6 +1119,15 @@ dependencies = [ "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wincolor" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" @@ -989,6 +1159,7 @@ dependencies = [ "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" "checksum ena 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe5a5078ac8c506d3e4430763b1ba9b609b1286913e7d08e581d1c2de9b7e5" "checksum env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0e6e40ebb0e66918a37b38c7acab4e10d299e0463fe2af5d29b9cc86710cfd2a" +"checksum env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afb070faf94c85d17d50ca44f6ad076bce18ae92f0037d350947240a36e9d42e" "checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9" "checksum failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "946d0e98a50d9831f5d589038d2ca7f8f455b1c21028c0db0e84116a12696426" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" @@ -996,9 +1167,11 @@ dependencies = [ "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" +"checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" "checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450" "checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606" +"checksum js-sys 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58cfec35fd4a94f3cf357d5cb7da71c71cd52720c2f2a7320090a8db5f06f655" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lalrpop 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba451f7bd819b7afc99d4cf4bdcd5a4861e64955ba9680ac70df3a50625ad6cf" "checksum lalrpop-snap 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "60013fd6be14317d43f47658b1440956a9ca48a9ed0257e0e0a59aac13e43a1f" @@ -1011,6 +1184,7 @@ dependencies = [ "checksum new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdc457076c78ab54d5e0d6fa7c47981757f1e34dc39ff92787f217dede586c4" "checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9c349f68f25f596b9f44cf0e7c69752a5c633b0550c3ff849518bfba0233774a" "checksum num-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "10b8423ea72ec64751198856a853e07b37087cfc9b53a87ecb19bff67b6d1320" "checksum num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "107b9be86cd2481930688277b675b0114578227f034674726605b8a482d8baf8" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" @@ -1033,7 +1207,7 @@ dependencies = [ "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "214a97e49be64fd2c86f568dd0cb2c757d2cc53de95b273b6ad0a1c908482f26" "checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" -"checksum regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75ecf88252dce580404a22444fc7d626c01815debba56a7f4f536772a5ff19d3" +"checksum regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3d8c9f33201f46669484bacc312b00e7541bed6aaf296dffe2bb4e0ac6b8ce2a" "checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" "checksum regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1ac0f60d675cc6cf13a20ec076568254472551051ad5dd050364d70671bf6b" @@ -1046,6 +1220,7 @@ dependencies = [ "checksum serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "44dd2cfde475037451fa99b7e5df77aa3cfd1536575fa8e7a538ab36dcde49ae" "checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" +"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" "checksum statrs 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "10102ac8d55e35db2b3fafc26f81ba8647da2e15879ab686a67e6d19af2685e8" "checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" "checksum string_cache_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "35293b05cf1494e8ddd042a7df6756bf18d07f42d234f32e71dce8a7aabb0191" @@ -1053,12 +1228,14 @@ dependencies = [ "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" +"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc" "checksum synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb9b7550d063ea184027c9b8c20ac167cd36d3e06b3a40bceb9d746dc1a7b7" "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" "checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83" +"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" -"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" @@ -1070,9 +1247,19 @@ dependencies = [ "checksum utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum wasm-bindgen 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "91f95b8f30407b9ca0c2de157281d3828bbed1fc1f55bea6eb54f40c52ec75ec" +"checksum wasm-bindgen-backend 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "ab7c242ebcb45bae45340986c48d1853eb2c1c52ff551f7724951b62a2c51429" +"checksum wasm-bindgen-macro 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "6e353f83716dec9a3597b5719ef88cb6c9e461ec16528f38aa023d3224b4e569" +"checksum wasm-bindgen-macro-support 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "3cc90b65fe69c3dd5a09684517dc79f42b847baa2d479c234d125e0a629d9b0a" +"checksum wasm-bindgen-shared 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "a71a37df4f5845025f96f279d20bbe5b19cbcb77f5410a3a90c6c544d889a162" +"checksum wasm-bindgen-webidl 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "b064b8b2336b5a6bf5f31bc95fc1310842395df29877d910cb6f8f791070f319" +"checksum web-sys 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d7c588c2e5a091bc4532c5a87032955f9133b644e868b54d08ead0185dcc5b9" +"checksum weedle 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26a4c67f132386d965390b8a734d5d10adbcd30eb5cc74bd9229af8b83f10044" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" +"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" diff --git a/Cargo.toml b/Cargo.toml index 2bd834312c..23b9e02a5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.1" authors = ["Windel Bouwman", "Shing Lyu "] [workspace] +members = [".", "vm", "wasm/lib", "parser"] [dependencies] log="0.4.1" @@ -12,3 +13,6 @@ clap = "2.31.2" rustpython_parser = {path = "parser"} rustpython_vm = {path = "vm"} rustyline = "2.1.0" + +[profile.release] +opt-level = "s" diff --git a/README.md b/README.md index 4776605dd2..92623e8dba 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # RustPython -A Python-3 (CPython >= 3.5.0) Interpreter written in Rust :snake: :scream: :metal:. + +A Python-3 (CPython >= 3.5.0) Interpreter written in Rust :snake: :scream: :metal:. [![Build Status](https://travis-ci.org/RustPython/RustPython.svg?branch=master)](https://travis-ci.org/RustPython/RustPython) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) @@ -24,7 +25,6 @@ Or use the interactive shell: >>>>> 2+2 4 - # Goals - Full Python-3 environment entirely in Rust (not CPython bindings) @@ -51,13 +51,13 @@ If you wish to update the online documentation. Push directly to the `release` b - `parser/src`: python lexing, parsing and ast - `vm/src`: python virtual machine - - `builtins.rs`: Builtin functions - - `compile.rs`: the python compiler from ast to bytecode - - `obj`: python builtin types + - `builtins.rs`: Builtin functions + - `compile.rs`: the python compiler from ast to bytecode + - `obj`: python builtin types - `src`: using the other subcrates to bring rustpython to life. - `docs`: documentation (work in progress) - `py_code_object`: CPython bytecode to rustpython bytecode convertor (work in progress) -- `wasm`: Binary crate and resources for WebAssembly build +- `wasm`: Binary crate and resources for WebAssembly build - `tests`: integration test snippets # Contributing @@ -68,7 +68,7 @@ Most tasks are listed in the [issue tracker](https://github.com/RustPython/RustP Check issues labeled with `good first issue` if you wish to start coding. Another approach is to checkout the sourcecode: builtin functions and object methods are often the simplest -and easiest way to contribute. +and easiest way to contribute. You can also simply run `cargo run tests/snippets/whats_left_to_implement.py` to assist in finding any @@ -135,44 +135,51 @@ cd emsdk-portable/ ## Build -Move into the `wasm` directory. This contains a custom library crate optimized for wasm build of RustPython. +Move into the `wasm` directory. This directory contains a library crate for interop +with python to rust to js and back in `wasm/lib`, the demo website found at +https://rustpython.github.io/demo in `wasm/demo`, and an example of how to use +the crate as a library in one's own JS app in `wasm/example`. -```bash +```sh cd wasm ``` -For testing on a development server, you can run the `build.sh` script. For release build which generates files for deploying to a HTTP server, run `release.sh`. +Go to the demo directory. This is the best way of seeing the changes made to either +the library or the JS demo, as the `rustpython_wasm` module is set to the global +JS variable `rp` on the website. -If you don't want to use the above scripts, you can do it manually as follows: - -Run the build. This can take several minutes depending on the machine. - -``` -wasm-pack build +```sh +cd demo ``` -Upon successful build, cd in the the `/pkg` directory and run: +Now, start the webpack development server. It'll compile the crate and then +the demo app. This will likely take a long time, both the wasm-pack portion and +the webpack portion (from after it says "Your crate has been correctly compiled"), +so be patient. -``` -npm link +```sh +npm run dev ``` -Now move back out into the `/app` directory. The files here have been adapted from [wasm-pack-template](https://github.com/rustwasm/wasm-pack-template). +You can now open the webpage on https://localhost:8080 and Python code in either +the text box or browser devtools with: -Finally, run: - -``` -npm install -npm link rustpython_wasm +```js +rp.pyEval( + ` +print(js_vars['a'] * 9) +`, + { + vars: { + a: 9 + } + } +); ``` -and you will be able to run the files with: - -``` -node_modules/.bin/webpack-dev-server -``` - -Now open the webpage on https://localhost:8080, you'll be able to run Python code in the text box. +Alternatively, you can run `npm run build` to build the app once, without watching +for changes, or `npm run dist` to build the app in release mode, both for the +crate and webpack. # Code style @@ -195,4 +202,3 @@ These are some useful links to related projects: - https://github.com/ProgVal/pythonvm-rust - https://github.com/shinglyu/RustPython - https://github.com/windelbouwman/rspython - diff --git a/wasm/.gitignore b/wasm/.gitignore index 882d44992f..3542b58965 100644 --- a/wasm/.gitignore +++ b/wasm/.gitignore @@ -1,2 +1,5 @@ bin/ pkg/ +dist/ +node_modules/ + diff --git a/wasm/app/.prettierrc b/wasm/.prettierrc similarity index 100% rename from wasm/app/.prettierrc rename to wasm/.prettierrc diff --git a/wasm/app/.gitignore b/wasm/app/.gitignore deleted file mode 100644 index 1eae0cf670..0000000000 --- a/wasm/app/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -dist/ -node_modules/ diff --git a/wasm/app/bootstrap.js b/wasm/app/bootstrap.js deleted file mode 100644 index 61136ee9b8..0000000000 --- a/wasm/app/bootstrap.js +++ /dev/null @@ -1,6 +0,0 @@ -// A dependency graph that contains any wasm must all be imported -// asynchronously. This `bootstrap.js` file does the single async import, so -// that no one else needs to worry about it again. -import('./index.js').catch(e => - console.error('Error importing `index.js`:', e) -); diff --git a/wasm/app/index.html b/wasm/app/index.html deleted file mode 100644 index f46ce1251a..0000000000 --- a/wasm/app/index.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - - RustPython Demo - - - -

RustPython Demo

-

- RustPython is a Python interpreter writter in Rust. This demo is - compiled from Rust to WebAssembly so it runs in the browser -

-

Please input your python code below and click Run:

-

- Alternatively, open up your browser's devtools and play with - rp.eval_py('print("a")') -

- - -
- -

Standard Output

- - -

Here's some info regarding the rp.eval_py() function

- - - - Fork me on GitHub - - diff --git a/wasm/app/index.js b/wasm/app/index.js deleted file mode 100644 index d21ac7a045..0000000000 --- a/wasm/app/index.js +++ /dev/null @@ -1,27 +0,0 @@ -import * as rp from 'rustpython_wasm'; - -// so people can play around with it -window.rp = rp; - -function runCodeFromTextarea(_) { - const consoleElement = document.getElementById('console'); - const errorElement = document.getElementById('error'); - - // Clean the console and errors - consoleElement.value = ''; - errorElement.textContent = ''; - - const code = document.getElementById('code').value; - try { - rp.run_from_textbox(code); - } catch (e) { - errorElement.textContent = e; - console.error(e); - } -} - -document - .getElementById('run-btn') - .addEventListener('click', runCodeFromTextarea); - -runCodeFromTextarea(); // Run once for demo diff --git a/wasm/app/package.json b/wasm/app/package.json deleted file mode 100644 index a1f9c41912..0000000000 --- a/wasm/app/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "app", - "version": "1.0.0", - "description": "", - "main": "index.js", - "dependencies": { - "hello-wasm-pack": "^0.1.0", - "webpack": "^4.16.3", - "webpack-cli": "^3.1.0", - "webpack-dev-server": "^3.1.5", - "copy-webpack-plugin": "^4.5.2" - }, - "devDependencies": {}, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/RustPython/RustPython.git" - }, - "author": "Ryan Liddle", - "license": "MIT", - "bugs": { - "url": "https://github.com/RustPython/RustPython/issues" - }, - "homepage": "https://github.com/RustPython/RustPython#readme" -} diff --git a/wasm/app/webpack.config.js b/wasm/app/webpack.config.js deleted file mode 100644 index 80ad8142d2..0000000000 --- a/wasm/app/webpack.config.js +++ /dev/null @@ -1,14 +0,0 @@ -const CopyWebpackPlugin = require("copy-webpack-plugin"); -const path = require('path'); - -module.exports = { - entry: "./bootstrap.js", - output: { - path: path.resolve(__dirname, "dist"), - filename: "bootstrap.js", - }, - mode: "development", - plugins: [ - new CopyWebpackPlugin(['index.html']) - ], -}; diff --git a/wasm/build.sh b/wasm/build.sh deleted file mode 100644 index bc8dba97f1..0000000000 --- a/wasm/build.sh +++ /dev/null @@ -1,7 +0,0 @@ -wasm-pack build --debug && \ -cd pkg && \ -npm link && \ -cd ../app && \ -npm install && \ -npm link rustpython_wasm && \ -node_modules/.bin/webpack-dev-server diff --git a/wasm/demo/package.json b/wasm/demo/package.json new file mode 100644 index 0000000000..97583dd6da --- /dev/null +++ b/wasm/demo/package.json @@ -0,0 +1,34 @@ +{ + "name": "app", + "version": "1.0.0", + "description": "Bindings to the RustPython library for WebAssembly", + "main": "index.js", + "dependencies": { + "codemirror": "^5.42.0" + }, + "devDependencies": { + "@wasm-tool/wasm-pack-plugin": "0.2.0", + "webpack": "^4.16.3", + "webpack-cli": "^3.1.0", + "webpack-dev-server": "^3.1.5", + "copy-webpack-plugin": "^4.5.2", + "mini-css-extract-plugin": "^0.5.0", + "html-webpack-plugin": "^3.2.0", + "css-loader": "^2.0.1" + }, + "scripts": { + "dev": "webpack-dev-server -d", + "build": "webpack", + "dist": "webpack --mode production" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/RustPython/RustPython.git" + }, + "author": "Ryan Liddle", + "license": "MIT", + "bugs": { + "url": "https://github.com/RustPython/RustPython/issues" + }, + "homepage": "https://github.com/RustPython/RustPython#readme" +} diff --git a/wasm/demo/src/index.html b/wasm/demo/src/index.html new file mode 100644 index 0000000000..0b64564632 --- /dev/null +++ b/wasm/demo/src/index.html @@ -0,0 +1,75 @@ + + + + + RustPython Demo + + +

RustPython Demo

+

+ RustPython is a Python interpreter writter in Rust. This demo is + compiled from Rust to WebAssembly so it runs in the browser.
+ Please input your python code below and click Run, or you + can open up your browser's devtools and play with + rp.pyEval('print("a")') +

+ + +
+

Standard Output

+ + +

Here's some info regarding the rp.pyEval() function

+ + + + Fork me on GitHub + + + diff --git a/wasm/demo/src/index.js b/wasm/demo/src/index.js new file mode 100644 index 0000000000..bf5257df2a --- /dev/null +++ b/wasm/demo/src/index.js @@ -0,0 +1,10 @@ +import './style.css'; +import 'codemirror/lib/codemirror.css'; + +// A dependency graph that contains any wasm must all be imported +// asynchronously. This `index.js` file does the single async import, so +// that no one else needs to worry about it again. +import('./main.js').catch(e => { + console.error('Error importing `main.js`:', e); + document.getElementById('error').textContent = e; +}); diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js new file mode 100644 index 0000000000..d8934b5c10 --- /dev/null +++ b/wasm/demo/src/main.js @@ -0,0 +1,49 @@ +import * as rp from '../../lib/pkg'; +import CodeMirror from 'codemirror'; +import 'codemirror/mode/python/python'; +import 'codemirror/addon/comment/comment'; + +// so people can play around with it +window.rp = rp; + +const editor = CodeMirror.fromTextArea(document.getElementById('code'), { + extraKeys: { + 'Ctrl-Enter': runCodeFromTextarea, + 'Cmd-Enter': runCodeFromTextarea, + 'Shift-Tab': 'indentLess', + 'Ctrl-/': 'toggleComment', + 'Cmd-/': 'toggleComment' + }, + lineNumbers: true, + mode: 'text/x-python', + indentUnit: 4, + autofocus: true +}); + +function runCodeFromTextarea() { + const consoleElement = document.getElementById('console'); + const errorElement = document.getElementById('error'); + + // Clean the console and errors + consoleElement.value = ''; + errorElement.textContent = ''; + + const code = editor.getValue(); + try { + const result = rp.pyEval(code, { + stdout: '#console' + }); + if (result !== null) { + consoleElement.value += `\n${result}\n`; + } + } catch (e) { + errorElement.textContent = e; + console.error(e); + } +} + +document + .getElementById('run-btn') + .addEventListener('click', runCodeFromTextarea); + +runCodeFromTextarea(); // Run once for demo diff --git a/wasm/demo/src/style.css b/wasm/demo/src/style.css new file mode 100644 index 0000000000..9fadc7c2db --- /dev/null +++ b/wasm/demo/src/style.css @@ -0,0 +1,27 @@ +textarea { + font-family: monospace; + resize: none; +} + +#code, +#console { + height: 30vh; + width: calc(100% - 3px); +} + +.CodeMirror { + border: 1px solid #ddd; + height: 30vh !important; +} + +#run-btn { + width: 6em; + height: 2em; + font-size: 24px; +} + +#error { + color: tomato; + margin-top: 10px; + font-family: monospace; +} diff --git a/wasm/demo/webpack.config.js b/wasm/demo/webpack.config.js new file mode 100644 index 0000000000..6d114d4363 --- /dev/null +++ b/wasm/demo/webpack.config.js @@ -0,0 +1,30 @@ +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin'); +const path = require('path'); + +module.exports = { + entry: './src/index.js', + output: { + path: path.join(__dirname, 'dist'), + filename: 'index.js' + }, + mode: 'development', + module: { + rules: [ + { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] } + ] + }, + plugins: [ + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'src/index.html' + }), + new MiniCssExtractPlugin({ + filename: 'styles.css' + }), + new WasmPackPlugin({ + crateDirectory: path.join(__dirname, '../lib') + }) + ] +}; diff --git a/wasm/example/index.html b/wasm/example/index.html new file mode 100644 index 0000000000..86a99c42f1 --- /dev/null +++ b/wasm/example/index.html @@ -0,0 +1,12 @@ + + + + + + + RustPython Basic Example + + + + + diff --git a/wasm/example/package.json b/wasm/example/package.json new file mode 100644 index 0000000000..cee4b7702b --- /dev/null +++ b/wasm/example/package.json @@ -0,0 +1,18 @@ +{ + "name": "rustpython-wasm-example", + "version": "1.0.0", + "//1": "`dependencies.rustpython_wasm` would be the version of the npm", + "//2": "library in a real app", + "dependencies": { + "rustpython_wasm": "file:../lib/pkg/" + }, + "devDependencies": { + "raw-loader": "1.0.0", + "webpack": "4.28.2", + "webpack-cli": "^3.1.2" + }, + "scripts": { + "build": "webpack" + }, + "license": "MIT" +} diff --git a/wasm/example/src/index.js b/wasm/example/src/index.js new file mode 100644 index 0000000000..6480aaf3e7 --- /dev/null +++ b/wasm/example/src/index.js @@ -0,0 +1,6 @@ +// A dependency graph that contains any wasm must all be imported +// asynchronously. This `index.js` file does the single async import, so +// that no one else needs to worry about it again. +import('./main.js').catch(e => { + console.error('Error importing `main.js`:', e); +}); diff --git a/wasm/example/src/main.js b/wasm/example/src/main.js new file mode 100644 index 0000000000..fcd32165e8 --- /dev/null +++ b/wasm/example/src/main.js @@ -0,0 +1,11 @@ +import * as py from 'rustpython_wasm'; +import pyCode from 'raw-loader!./main.py'; + +fetch('https://github-trending-api.now.sh/repositories') + .then(r => r.json()) + .then(repos => { + const result = py.pyEval(pyCode, { + vars: { repos } + }); + alert(result); + }); diff --git a/wasm/example/src/main.py b/wasm/example/src/main.py new file mode 100644 index 0000000000..e2d590a3f3 --- /dev/null +++ b/wasm/example/src/main.py @@ -0,0 +1,8 @@ +repos = js_vars['repos'] + +star_sum = 0 + +for repo in repos: + star_sum += repo['stars'] + +return 'Average github trending star count: ' + str(star_sum / len(repos)) diff --git a/wasm/example/webpack.config.js b/wasm/example/webpack.config.js new file mode 100644 index 0000000000..9a6f41e9e3 --- /dev/null +++ b/wasm/example/webpack.config.js @@ -0,0 +1,12 @@ +const path = require('path'); + +module.exports = { + entry: './src/index.js', + mode: 'development', + output: { + path: path.join(__dirname, 'dist'), + filename: 'app.js', + publicPath: '/dist/' + }, + devtool: 'cheap-module-eval-source-map' +}; diff --git a/wasm/Cargo.lock b/wasm/lib/Cargo.lock similarity index 100% rename from wasm/Cargo.lock rename to wasm/lib/Cargo.lock diff --git a/wasm/Cargo.toml b/wasm/lib/Cargo.toml similarity index 55% rename from wasm/Cargo.toml rename to wasm/lib/Cargo.toml index 4be409dfc2..6c78e06405 100644 --- a/wasm/Cargo.toml +++ b/wasm/lib/Cargo.toml @@ -2,16 +2,16 @@ name = "rustpython_wasm" version = "0.1.0" authors = ["Ryan Liddle "] +license = "MIT" +description = "A Python-3 (CPython >= 3.5.0) Interpreter written in Rust, compiled to WASM" +repository = "https://github.com/RustPython/RustPython/tree/master/wasm/lib" [lib] crate-type = ["cdylib", "rlib"] -[workspace] -members = [] - [dependencies] -rustpython_parser = {path = "../parser"} -rustpython_vm = {path = "../vm"} +rustpython_parser = {path = "../../parser"} +rustpython_vm = {path = "../../vm"} cfg-if = "0.1.2" wasm-bindgen = "0.2" js-sys = "0.3" @@ -19,7 +19,3 @@ js-sys = "0.3" [dependencies.web-sys] version = "0.3" features = [ "console", "Document", "Element", "HtmlTextAreaElement", "Window" ] - - -[profile.release] -opt-level = "s" diff --git a/wasm/lib/README.md b/wasm/lib/README.md new file mode 100644 index 0000000000..b278e8cb94 --- /dev/null +++ b/wasm/lib/README.md @@ -0,0 +1,37 @@ +# RustPython + +A Python-3 (CPython >= 3.5.0) Interpreter written in Rust. + +[![Build Status](https://travis-ci.org/RustPython/RustPython.svg?branch=master)](https://travis-ci.org/RustPython/RustPython) +[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) +[![Contributors](https://img.shields.io/github/contributors/RustPython/RustPython.svg)](https://github.com/RustPython/RustPython/graphs/contributors) +[![Gitter](https://badges.gitter.im/RustPython/Lobby.svg)](https://gitter.im/rustpython/Lobby) + +## Usage + +### Check out our [online demo](https://rustpython.github.io/demo/) running on WebAssembly. + +## Goals + +- Full Python-3 environment entirely in Rust (not CPython bindings) +- A clean implementation without compatibility hacks + +## Quick Documentation + +```js +pyEval(code, options?); +``` + +`code`: `string`: The Python code to run + +`options`: + +- `vars?`: `{ [key: string]: any }`: Variables passed to the VM that can be + accessed in Python with the variable `js_vars`. Functions do work, and + recieve the Python kwargs as the `this` argument. +- `stdout?`: `(out: string) => void`: A function to replace the native print + function, by default `console.log`. + +## License + +This project is licensed under the MIT license. diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs new file mode 100644 index 0000000000..4bc0891b2f --- /dev/null +++ b/wasm/lib/src/lib.rs @@ -0,0 +1,236 @@ +mod wasm_builtins; + +extern crate js_sys; +extern crate rustpython_vm; +extern crate wasm_bindgen; +extern crate web_sys; + +use js_sys::{Array, Object, Reflect, TypeError}; +use rustpython_vm::compile; +use rustpython_vm::pyobject::{self, PyFuncArgs, PyObjectRef, PyResult}; +use rustpython_vm::VirtualMachine; +use wasm_bindgen::{prelude::*, JsCast}; + +// Hack to comment out wasm-bindgen's typescript definitons +#[wasm_bindgen(typescript_custom_section)] +const TS_CMT_START: &'static str = "/*"; + +fn py_str_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> String { + vm.to_pystr(&py_err) + .unwrap_or_else(|_| "Error, and error getting error message".into()) +} + +fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { + let dumps = rustpython_vm::import::import( + vm, + std::path::PathBuf::default(), + "json", + &Some("dumps".into()), + ) + .expect("Couldn't get json.dumps function"); + match vm.invoke(dumps, pyobject::PyFuncArgs::new(vec![py_obj], vec![])) { + Ok(value) => { + let json = vm.to_pystr(&value).unwrap(); + js_sys::JSON::parse(&json).unwrap_or(JsValue::UNDEFINED) + } + Err(_) => JsValue::UNDEFINED, + } +} + +fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { + if js_val.is_object() { + if Array::is_array(&js_val) { + let js_arr: Array = js_val.into(); + let elems = js_arr + .values() + .into_iter() + .map(|val| js_to_py(vm, val.expect("Iteration over array failed"))) + .collect(); + vm.ctx.new_list(elems) + } else { + let dict = vm.new_dict(); + for pair in Object::entries(&Object::from(js_val)).values() { + let pair = pair.expect("Iteration over object failed"); + let key = Reflect::get(&pair, &"0".into()).unwrap(); + let val = Reflect::get(&pair, &"1".into()).unwrap(); + let py_val = js_to_py(vm, val); + vm.ctx + .set_item(&dict, &String::from(js_sys::JsString::from(key)), py_val); + } + dict + } + } else if js_val.is_function() { + let func = js_sys::Function::from(js_val); + vm.ctx.new_rustfunc( + move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { + let func = func.clone(); + let this = Object::new(); + for (k, v) in args.kwargs { + Reflect::set(&this, &k.into(), &py_to_js(vm, v)) + .expect("Couldn't set this property"); + } + let js_args = Array::new(); + for v in args.args { + js_args.push(&py_to_js(vm, v)); + } + func.apply(&this, &js_args) + .map(|val| js_to_py(vm, val)) + .map_err(|err| js_to_py(vm, err)) + }, + ) + } else if let Some(err) = js_val.dyn_ref::() { + let exc_type = match String::from(err.name()).as_str() { + "TypeError" => &vm.ctx.exceptions.type_error, + "ReferenceError" => &vm.ctx.exceptions.name_error, + "SyntaxError" => &vm.ctx.exceptions.syntax_error, + _ => &vm.ctx.exceptions.exception_type, + } + .clone(); + vm.new_exception(exc_type, err.message().into()) + } else if js_val.is_undefined() { + // Because `JSON.stringify(undefined)` returns undefined + vm.get_none() + } else { + let loads = rustpython_vm::import::import( + vm, + std::path::PathBuf::default(), + "json", + &Some("loads".into()), + ) + .expect("Couldn't get json.loads function"); + + let json = match js_sys::JSON::stringify(&js_val) { + Ok(json) => String::from(json), + Err(_) => return vm.get_none(), + }; + let py_json = vm.new_str(json); + + vm.invoke(loads, pyobject::PyFuncArgs::new(vec![py_json], vec![])) + // can safely unwrap because we know it's valid JSON + .unwrap() + } +} + +fn base_scope(vm: &mut VirtualMachine) -> PyObjectRef { + let builtins = vm.get_builtin_scope(); + vm.context().new_scope(Some(builtins)) +} + +fn eval(vm: &mut VirtualMachine, source: &str, vars: PyObjectRef) -> PyResult { + // HACK: if the code doesn't end with newline it crashes. + let mut source = source.to_string(); + if !source.ends_with('\n') { + source.push('\n'); + } + + let code_obj = compile::compile(vm, &source, compile::Mode::Exec, None)?; + + vm.run_code_obj(code_obj, vars) +} + +#[wasm_bindgen(js_name = pyEval)] +/// Evaluate Python code +/// +/// ```js +/// pyEval(code, options?); +/// ``` +/// +/// `code`: `string`: The Python code to run +/// +/// `options`: +/// +/// - `vars?`: `{ [key: string]: any }`: Variables passed to the VM that can be +/// accessed in Python with the variable `js_vars`. Functions do work, and +/// recieve the Python kwargs as the `this` argument. +/// - `stdout?`: `(out: string) => void`: A function to replace the native print +/// function, by default `console.log`. +pub fn eval_py(source: &str, options: Option) -> Result { + let options = options.unwrap_or_else(|| Object::new()); + let js_vars = { + let prop = Reflect::get(&options, &"vars".into())?; + if prop.is_undefined() { + None + } else if prop.is_object() { + Some(Object::from(prop)) + } else { + return Err(TypeError::new("vars must be an object").into()); + } + }; + let stdout = { + let prop = Reflect::get(&options, &"stdout".into())?; + if prop.is_undefined() { + None + } else { + Some(prop) + } + }; + let mut vm = VirtualMachine::new(); + + let print_fn: Box PyResult> = match stdout { + Some(val) => { + if let Some(selector) = val.as_string() { + Box::new( + move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { + wasm_builtins::builtin_print_html(vm, args, &selector) + }, + ) + } else if val.is_function() { + let func = js_sys::Function::from(val); + Box::new( + move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { + func.call1( + &JsValue::UNDEFINED, + &wasm_builtins::format_print_args(vm, args)?.into(), + ) + .map_err(|err| js_to_py(vm, err))?; + Ok(vm.get_none()) + }, + ) + } else { + return Err(TypeError::new("stdout must be a function or a css selector").into()); + } + } + None => Box::new(wasm_builtins::builtin_print_console), + }; + + vm.ctx.set_attr( + &vm.builtins, + "print", + vm.ctx.new_rustfunc_from_box(print_fn), + ); + + let mut vars = base_scope(&mut vm); + + let injections = vm.new_dict(); + + if let Some(js_vars) = js_vars.clone() { + for pair in Object::entries(&js_vars).values() { + let pair = pair?; + let key = Reflect::get(&pair, &"0".into()).unwrap(); + let val = Reflect::get(&pair, &"1".into()).unwrap(); + let py_val = js_to_py(&mut vm, val); + vm.ctx.set_item( + &injections, + &String::from(js_sys::JsString::from(key)), + py_val, + ); + } + } + + vm.ctx.set_item(&mut vars, "js_vars", injections); + + eval(&mut vm, source, vars) + .map(|value| py_to_js(&mut vm, value)) + .map_err(|err| py_str_err(&mut vm, &err).into()) +} + +#[wasm_bindgen(typescript_custom_section)] +const TYPESCRIPT_DEFS: &'static str = r#" +*/ +export interface PyEvalOptions { + stdout: (out: string) => void; + vars: { [key: string]: any }; +} + +export function pyEval(code: string, options?: PyEvalOptions): any; +"#; diff --git a/wasm/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs similarity index 52% rename from wasm/src/wasm_builtins.rs rename to wasm/lib/src/wasm_builtins.rs index 99e59f6bee..ca3ab013aa 100644 --- a/wasm/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -8,47 +8,53 @@ extern crate js_sys; extern crate wasm_bindgen; extern crate web_sys; +use crate::js_to_py; use js_sys::Array; -use rustpython_vm::obj::objstr; -use rustpython_vm::pyobject::{PyFuncArgs, PyResult}; +use rustpython_vm::pyobject::{PyFuncArgs, PyObjectRef, PyResult}; use rustpython_vm::VirtualMachine; -use wasm_bindgen::JsCast; +use wasm_bindgen::{JsCast, JsValue}; use web_sys::{console, window, HtmlTextAreaElement}; // The HTML id of the textarea element that act as our STDOUT -const CONSOLE_ELEMENT_ID: &str = "console"; -pub fn print_to_html(text: &str) { +pub fn print_to_html(text: &str, selector: &str) -> Result<(), JsValue> { let document = window().unwrap().document().unwrap(); let element = document - .get_element_by_id(CONSOLE_ELEMENT_ID) - .expect("Can't find the console textarea"); - let textarea = element.dyn_ref::().unwrap(); + .query_selector(selector)? + .ok_or_else(|| js_sys::TypeError::new("Couldn't get element"))?; + let textarea = element + .dyn_ref::() + .ok_or_else(|| js_sys::TypeError::new("Element must be a textarea"))?; let value = textarea.value(); textarea.set_value(&format!("{}{}", value, text)); + Ok(()) } -pub fn builtin_print_html(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { + let mut output = String::new(); let mut first = true; for a in args.args { if first { first = false; } else { - print_to_html(&" ") + output.push_str(" "); } - let v = vm.to_str(&a)?; - let s = objstr::get_value(&v); - print_to_html(&format!("{}\n", s)) + output.push_str(&vm.to_pystr(&a)?); + output.push('\n'); } + Ok(output) +} + +pub fn builtin_print_html(vm: &mut VirtualMachine, args: PyFuncArgs, selector: &str) -> PyResult { + let output = format_print_args(vm, args)?; + print_to_html(&output, selector).map_err(|err| js_to_py(vm, err))?; Ok(vm.get_none()) } pub fn builtin_print_console(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let arr = Array::new(); - for a in args.args { - let v = vm.to_str(&a)?; - let s = objstr::get_value(&v); - arr.push(&s.into()); + for arg in args.args { + arr.push(&vm.to_pystr(&arg)?.into()); } console::log(&arr); Ok(vm.get_none()) diff --git a/wasm/lib/target b/wasm/lib/target new file mode 120000 index 0000000000..6bcd2fc5d2 --- /dev/null +++ b/wasm/lib/target @@ -0,0 +1 @@ +../../target \ No newline at end of file diff --git a/wasm/release.sh b/wasm/release.sh deleted file mode 100644 index ea5b388c6f..0000000000 --- a/wasm/release.sh +++ /dev/null @@ -1,8 +0,0 @@ -wasm-pack build && \ -cd pkg && \ -npm link && \ -cd ../app && \ -npm install && \ -npm link rustpython_wasm && \ -node_modules/.bin/webpack --mode production && \ -echo "Output saved to app/dist" diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs deleted file mode 100644 index 9ac5677d79..0000000000 --- a/wasm/src/lib.rs +++ /dev/null @@ -1,143 +0,0 @@ -mod wasm_builtins; - -extern crate js_sys; -extern crate rustpython_vm; -extern crate wasm_bindgen; -extern crate web_sys; - -use rustpython_vm::compile; -use rustpython_vm::pyobject::{self, PyObjectRef, PyResult}; -use rustpython_vm::VirtualMachine; -use wasm_bindgen::prelude::*; -use web_sys::console; - -fn py_str_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> String { - vm.to_pystr(&py_err) - .unwrap_or_else(|_| "Error, and error getting error message".into()) -} - -fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { - let dumps = rustpython_vm::import::import( - vm, - std::path::PathBuf::default(), - "json", - &Some("dumps".into()), - ) - .expect("Couldn't get json.dumps function"); - match vm.invoke(dumps, pyobject::PyFuncArgs::new(vec![py_obj], vec![])) { - Ok(value) => { - let json = vm.to_pystr(&value).unwrap(); - js_sys::JSON::parse(&json).unwrap_or(JsValue::UNDEFINED) - } - Err(_) => JsValue::UNDEFINED, - } -} - -fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { - let json = match js_sys::JSON::stringify(&js_val) { - Ok(json) => String::from(json), - Err(_) => return vm.get_none(), - }; - - let loads = rustpython_vm::import::import( - vm, - std::path::PathBuf::default(), - "json", - &Some("loads".into()), - ) - .expect("Couldn't get json.loads function"); - - let py_json = vm.new_str(json); - - vm.invoke(loads, pyobject::PyFuncArgs::new(vec![py_json], vec![])) - // can safely unwrap because we know it's valid JSON - .unwrap() -} - -fn eval(vm: &mut VirtualMachine, source: &str, setup_scope: F) -> PyResult -where - F: Fn(&mut VirtualMachine, &PyObjectRef), -{ - // HACK: if the code doesn't end with newline it crashes. - let mut source = source.to_string(); - if !source.ends_with('\n') { - source.push('\n'); - } - - let code_obj = compile::compile(vm, &source, compile::Mode::Exec, None)?; - - let builtins = vm.get_builtin_scope(); - let mut vars = vm.context().new_scope(Some(builtins)); - - setup_scope(vm, &mut vars); - - vm.run_code_obj(code_obj, vars) -} - -#[wasm_bindgen] -pub fn eval_py(source: &str, js_injections: Option) -> Result { - if let Some(js_injections) = js_injections.clone() { - if !js_injections.is_object() { - return Err(js_sys::TypeError::new("The second argument must be an object").into()); - } - } - - let mut vm = VirtualMachine::new(); - - vm.ctx.set_attr( - &vm.builtins, - "print", - vm.context() - .new_rustfunc(wasm_builtins::builtin_print_console), - ); - - let res = eval(&mut vm, source, |vm, vars| { - let injections = if let Some(js_injections) = js_injections.clone() { - js_to_py(vm, js_injections.into()) - } else { - vm.new_dict() - }; - - vm.ctx.set_item(vars, "js_vars", injections); - }); - - res.map(|value| py_to_js(&mut vm, value)) - .map_err(|err| py_str_err(&mut vm, &err).into()) -} - -#[wasm_bindgen] -pub fn run_from_textbox(source: &str) -> Result { - //add hash in here - console::log_1(&"Running RustPython".into()); - console::log_1(&"Running code:".into()); - console::log_1(&source.to_string().into()); - - let mut vm = VirtualMachine::new(); - - // We are monkey-patching the builtin print to use console.log - // TODO: monkey-patch sys.stdout instead, after print actually uses sys.stdout - vm.ctx.set_attr( - &vm.builtins, - "print", - vm.context().new_rustfunc(wasm_builtins::builtin_print_html), - ); - - match eval(&mut vm, source, |_, _| {}) { - Ok(value) => { - console::log_1(&"Execution successful".into()); - match value.borrow().kind { - pyobject::PyObjectKind::None => {} - _ => { - if let Ok(text) = vm.to_pystr(&value) { - wasm_builtins::print_to_html(&text); - } - } - } - Ok(JsValue::UNDEFINED) - } - Err(err) => { - console::log_1(&"Execution failed".into()); - Err(py_str_err(&mut vm, &err).into()) - } - } -}