diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 24985f5fedd..0f38bb8b77e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,7 +7,35 @@ "type": "process", "label": "Cargo Run", "command": "cargo", - "args": ["run"], + "args": ["run", "./tests/js/test.js"], + "problemMatcher": ["$rustc"], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "clear": true + } + }, + { + "type": "process", + "label": "Get Tokens", + "command": "cargo", + "args": ["run", "--", "-t=Debug", "./tests/js/test.js"], + "problemMatcher": ["$rustc"], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "clear": true + } + }, + { + "type": "process", + "label": "Get AST", + "command": "cargo", + "args": ["run", "--", "-a=Debug", "./tests/js/test.js"], "problemMatcher": ["$rustc"], "group": { "kind": "build", diff --git a/Cargo.lock b/Cargo.lock index cf9ff81a82c..97df8d2eee3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,9 +16,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811" +checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" dependencies = [ "memchr", ] @@ -32,12 +32,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "anyhow" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" - [[package]] name = "atty" version = "0.2.14" @@ -49,12 +43,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "autocfg" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" - [[package]] name = "autocfg" version = "1.0.0" @@ -77,9 +65,9 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502ae1441a0a5adb8fbd38a5955a6416b9493e92b465de5e4a9bde6a539c2c48" +checksum = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41" dependencies = [ "lazy_static", "memchr", @@ -89,24 +77,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.1.2" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb8038c1ddc0a5f73787b130f4cc75151e96ed33e417fde765eb5a81e3532f4" +checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" [[package]] name = "byteorder" -version = "1.3.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" - -[[package]] -name = "c2-chacha" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -dependencies = [ - "ppv-lite86", -] +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "cast" @@ -175,24 +154,26 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" +checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" dependencies = [ "crossbeam-epoch", "crossbeam-utils", + "maybe-uninit", ] [[package]] name = "crossbeam-epoch" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg 0.1.7", + "autocfg", "cfg-if", "crossbeam-utils", "lazy_static", + "maybe-uninit", "memoffset", "scopeguard", ] @@ -209,11 +190,11 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 0.1.7", + "autocfg", "cfg-if", "lazy_static", ] @@ -233,9 +214,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.6" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" dependencies = [ "memchr", ] @@ -285,9 +266,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" +checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" dependencies = [ "libc", ] @@ -309,9 +290,9 @@ checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "js-sys" -version = "0.3.35" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7889c7c36282151f6bf465be4700359318aef36baa951462382eae49e9577cf9" +checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" dependencies = [ "wasm-bindgen", ] @@ -324,9 +305,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" [[package]] name = "log" @@ -338,31 +319,24 @@ dependencies = [ ] [[package]] -name = "memchr" -version = "2.3.0" +name = "maybe-uninit" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" -dependencies = [ - "libc", -] +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] -name = "memoffset" -version = "0.5.3" +name = "memchr" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" -dependencies = [ - "rustc_version", -] +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] -name = "nom" -version = "4.2.3" +name = "memoffset" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" dependencies = [ - "memchr", - "version_check", + "autocfg", ] [[package]] @@ -371,7 +345,7 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ - "autocfg 1.0.0", + "autocfg", ] [[package]] @@ -410,35 +384,35 @@ checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" [[package]] name = "proc-macro-error" -version = "0.4.8" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875077759af22fa20b610ad4471d8155b321c89c3f2785526c9839b099be4e0a" +checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" dependencies = [ "proc-macro-error-attr", "proc-macro2", - "quote 1.0.2", - "rustversion", - "syn 1.0.14", + "quote 1.0.3", + "syn 1.0.17", + "version_check", ] [[package]] name = "proc-macro-error-attr" -version = "0.4.8" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5717d9fa2664351a01ed73ba5ef6df09c01a521cb42cb65a061432a826f3c7a" +checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" dependencies = [ "proc-macro2", - "quote 1.0.2", - "rustversion", - "syn 1.0.14", + "quote 1.0.3", + "syn 1.0.17", "syn-mid", + "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" dependencies = [ "unicode-xid 0.2.0", ] @@ -451,9 +425,9 @@ checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" dependencies = [ "proc-macro2", ] @@ -473,11 +447,11 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "c2-chacha", + "ppv-lite86", "rand_core", ] @@ -525,9 +499,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.3.4" +version = "1.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" +checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" dependencies = [ "aho-corasick", "memchr", @@ -537,18 +511,18 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" dependencies = [ "byteorder", ] [[package]] name = "regex-syntax" -version = "0.6.14" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" +checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" [[package]] name = "rustc_version" @@ -559,22 +533,11 @@ dependencies = [ "semver", ] -[[package]] -name = "rustversion" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" -dependencies = [ - "proc-macro2", - "quote 1.0.2", - "syn 1.0.14", -] - [[package]] name = "ryu" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" [[package]] name = "same-file" @@ -587,9 +550,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" @@ -608,41 +571,35 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.104" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +checksum = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.104" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +checksum = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" dependencies = [ "proc-macro2", - "quote 1.0.2", - "syn 1.0.14", + "quote 1.0.3", + "syn 1.0.17", ] [[package]] name = "serde_json" -version = "1.0.46" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b01d7f0288608a01dca632cf1df859df6fd6ffa885300fc275ce2ba6221953" +checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" dependencies = [ "itoa", "ryu", "serde", ] -[[package]] -name = "sourcefile" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" - [[package]] name = "strsim" version = "0.8.0" @@ -651,9 +608,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.9" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98" +checksum = "c8faa2719539bbe9d77869bfb15d4ee769f99525e707931452c97b693b3f159d" dependencies = [ "clap", "lazy_static", @@ -662,15 +619,15 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.2" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "095064aa1f5b94d14e635d0a5684cf140c43ae40a0fd990708d38f5d669e5f64" +checksum = "3f88b8e18c69496aad6f9ddf4630dd7d585bcaf765786cb415b9aec2fe5a0430" dependencies = [ "heck", "proc-macro-error", "proc-macro2", - "quote 1.0.2", - "syn 1.0.14", + "quote 1.0.3", + "syn 1.0.17", ] [[package]] @@ -686,12 +643,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" dependencies = [ "proc-macro2", - "quote 1.0.2", + "quote 1.0.3", "unicode-xid 0.2.0", ] @@ -702,8 +659,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" dependencies = [ "proc-macro2", - "quote 1.0.2", - "syn 1.0.14", + "quote 1.0.3", + "syn 1.0.17", ] [[package]] @@ -785,9 +742,9 @@ checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" [[package]] name = "version_check" -version = "0.1.5" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" [[package]] name = "walkdir" @@ -808,9 +765,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" -version = "0.2.58" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c" +checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -818,84 +775,56 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.58" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45" +checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" dependencies = [ "bumpalo", "lazy_static", "log", "proc-macro2", - "quote 1.0.2", - "syn 1.0.14", + "quote 1.0.3", + "syn 1.0.17", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.58" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "574094772ce6921576fb6f2e3f7497b8a76273b6db092be18fc48a082de09dc3" +checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" dependencies = [ - "quote 1.0.2", + "quote 1.0.3", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.58" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85031354f25eaebe78bb7db1c3d86140312a911a106b2e29f9cc440ce3e7668" +checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" dependencies = [ "proc-macro2", - "quote 1.0.2", - "syn 1.0.14", + "quote 1.0.3", + "syn 1.0.17", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.58" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e7e61fc929f4c0dddb748b102ebf9f632e2b8d739f2016542b4de2965a9601" - -[[package]] -name = "wasm-bindgen-webidl" -version = "0.2.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef012a0d93fc0432df126a8eaf547b2dce25a8ce9212e1d3cbeef5c11157975d" -dependencies = [ - "anyhow", - "heck", - "log", - "proc-macro2", - "quote 1.0.2", - "syn 1.0.14", - "wasm-bindgen-backend", - "weedle", -] +checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" [[package]] name = "web-sys" -version = "0.3.35" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf97caf6aa8c2b1dac90faf0db529d9d63c93846cca4911856f78a83cebf53b" +checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb" dependencies = [ - "anyhow", "js-sys", - "sourcefile", "wasm-bindgen", - "wasm-bindgen-webidl", -] - -[[package]] -name = "weedle" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" -dependencies = [ - "nom", ] [[package]] diff --git a/Dockerfile b/Dockerfile index fd11a6e1ad4..08e4e46ca34 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,7 @@ FROM rust:latest WORKDIR /usr/src/myapp -COPY . . - -# LLDB Server -EXPOSE 9228 - -RUN apt-get -y update && \ - apt-get -y upgrade && \ - apt-get install -y sudo software-properties-common libpython2.7 - -# codelldb depends on libpython2.7 -# https://stackoverflow.com/questions/20842732/libpython2-7-so-1-0-cannot-open-shared-object-file-no-such-file-or-directory - -# https://askubuntu.com/questions/787383/how-to-install-llvm-3-9 -# http://apt.llvm.org/ -RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - -RUN apt-add-repository "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-6.0 main" -RUN apt-get -y update -RUN sudo apt-get install -y lldb +RUN apt-get -y update && apt-get -y upgrade CMD ["/bin/bash"] \ No newline at end of file diff --git a/boa/Cargo.toml b/boa/Cargo.toml index c30d4caa45a..2d4c7150a38 100644 --- a/boa/Cargo.toml +++ b/boa/Cargo.toml @@ -17,13 +17,13 @@ default = ["wasm-bindgen"] [dependencies] gc = "0.3.3" gc_derive = "0.3.2" -serde_json = "1.0.46" +serde_json = "1.0.48" rand = "0.7.3" -regex = "1.3.4" +regex = "1.3.6" # Optional Dependencies -wasm-bindgen = { version = "0.2.58", optional = true } -serde = { version = "1.0", features = ["derive"], optional = true } +wasm-bindgen = { version = "0.2.59", optional = true } +serde = { version = "1.0.105", features = ["derive"], optional = true } [dev-dependencies] criterion = "0.3.1" diff --git a/boa/benches/parser.rs b/boa/benches/parser.rs index f201c712580..5c697a174ec 100644 --- a/boa/benches/parser.rs +++ b/boa/benches/parser.rs @@ -19,7 +19,7 @@ fn expression_parser(c: &mut Criterion) { "Expression (Parser)", move |b, tok| { b.iter(|| { - Parser::new(black_box(tok.to_vec())).parse_all().unwrap(); + Parser::new(&black_box(tok.to_vec())).parse_all().unwrap(); }) }, vec![tokens], diff --git a/boa/benches/string.rs b/boa/benches/string.rs index 166f279c26a..2a7b275a299 100644 --- a/boa/benches/string.rs +++ b/boa/benches/string.rs @@ -29,7 +29,7 @@ fn hello_world_parser(c: &mut Criterion) { "Hello World (Parser)", move |b, tok| { b.iter(|| { - Parser::new(black_box(tok.to_vec())).parse_all().unwrap(); + Parser::new(&black_box(tok.to_vec())).parse_all().unwrap(); }) }, vec![tokens], diff --git a/boa/src/builtins/array/tests.rs b/boa/src/builtins/array/tests.rs index 97ea12e627d..71b509f0b78 100644 --- a/boa/src/builtins/array/tests.rs +++ b/boa/src/builtins/array/tests.rs @@ -11,7 +11,7 @@ fn is_array() { var new_arr = new Array(); var many = ["a", "b", "c"]; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!(forward(&mut engine, "Array.isArray(empty)"), "true"); assert_eq!(forward(&mut engine, "Array.isArray(new_arr)"), "true"); assert_eq!(forward(&mut engine, "Array.isArray(many)"), "true"); @@ -51,7 +51,7 @@ fn concat() { // var empty = new Array(); // var one = new Array(1); // "#; - // forward(&mut engine, init); + // eprintln!("{}", forward(&mut engine, init)); // // Empty ++ Empty // let ee = forward(&mut engine, "empty.concat(empty)"); // assert_eq!(ee, String::from("[]")); @@ -75,7 +75,7 @@ fn join() { var one = ["a"]; var many = ["a", "b", "c"]; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); // Empty let empty = forward(&mut engine, "empty.join('.')"); assert_eq!(empty, String::from("")); @@ -96,7 +96,7 @@ fn to_string() { var one = ["a"]; var many = ["a", "b", "c"]; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); // Empty let empty = forward(&mut engine, "empty.toString()"); assert_eq!(empty, String::from("")); @@ -136,7 +136,7 @@ fn every() { return elem < 3; } "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let result = forward(&mut engine, "array.every(callback);"); assert_eq!(result, "true"); @@ -163,7 +163,7 @@ fn find() { } var many = ["a", "b", "c"]; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let found = forward(&mut engine, "many.find(comp)"); assert_eq!(found, String::from("a")); } @@ -201,7 +201,7 @@ fn push() { let init = r#" var arr = [1, 2]; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!(forward(&mut engine, "arr.push()"), "2"); assert_eq!(forward(&mut engine, "arr.push(3, 4)"), "4"); @@ -218,7 +218,7 @@ fn pop() { var one = [1]; var many = [1, 2, 3, 4]; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!( forward(&mut engine, "empty.pop()"), @@ -240,7 +240,7 @@ fn shift() { var one = [1]; var many = [1, 2, 3, 4]; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!( forward(&mut engine, "empty.shift()"), @@ -260,7 +260,7 @@ fn unshift() { let init = r#" var arr = [3, 4]; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!(forward(&mut engine, "arr.unshift()"), "2"); assert_eq!(forward(&mut engine, "arr.unshift(1, 2)"), "4"); @@ -276,7 +276,7 @@ fn reverse() { var arr = [1, 2]; var reversed = arr.reverse(); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!(forward(&mut engine, "reversed[0]"), "2"); assert_eq!(forward(&mut engine, "reversed[1]"), "1"); assert_eq!(forward(&mut engine, "arr[0]"), "2"); @@ -293,7 +293,7 @@ fn index_of() { var many = ["a", "b", "c"]; var duplicates = ["a", "b", "c", "a", "b"]; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); // Empty let empty = forward(&mut engine, "empty.indexOf('a')"); @@ -357,7 +357,7 @@ fn last_index_of() { var many = ["a", "b", "c"]; var duplicates = ["a", "b", "c", "a", "b"]; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); // Empty let empty = forward(&mut engine, "empty.lastIndexOf('a')"); @@ -509,7 +509,7 @@ fn fill() { } #[test] -fn inclues_value() { +fn includes_value() { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" @@ -519,7 +519,7 @@ fn inclues_value() { var duplicates = ["a", "b", "c", "a", "b"]; var undefined = [undefined]; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); // Empty let empty = forward(&mut engine, "empty.includes('a')"); @@ -619,7 +619,7 @@ fn slice() { var many2 = ["a", "b", "c", "d"].slice(2, 3); var many3 = ["a", "b", "c", "d"].slice(7); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!(forward(&mut engine, "empty.length"), "0"); assert_eq!(forward(&mut engine, "one[0]"), "a"); @@ -648,7 +648,7 @@ fn for_each() { } a.forEach(callingCallback); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!(forward(&mut engine, "sum"), "14"); assert_eq!(forward(&mut engine, "indexSum"), "6"); @@ -666,7 +666,7 @@ fn for_each_push_value() { } a.forEach(callingCallback); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); // [ 1, 2, 3, 4, 2, 4, 6, 8 ] assert_eq!(forward(&mut engine, "a.length"), "8"); diff --git a/boa/src/builtins/boolean/mod.rs b/boa/src/builtins/boolean/mod.rs index adcf5463111..604125a81c7 100644 --- a/boa/src/builtins/boolean/mod.rs +++ b/boa/src/builtins/boolean/mod.rs @@ -16,13 +16,10 @@ pub fn construct_boolean(this: &Value, args: &[Value], _: &mut Interpreter) -> R this.set_kind(ObjectKind::Boolean); // Get the argument, if any - match args.get(0) { - Some(ref value) => { - this.set_internal_slot("BooleanData", to_boolean(value)); - } - None => { - this.set_internal_slot("BooleanData", to_boolean(&to_value(false))); - } + if let Some(ref value) = args.get(0) { + this.set_internal_slot("BooleanData", to_boolean(value)); + } else { + this.set_internal_slot("BooleanData", to_boolean(&to_value(false))); } // no need to return `this` as its passed by reference @@ -69,7 +66,7 @@ pub fn create_constructor(global: &Value) -> Value { } // === Utility Functions === -/// [toBoolean](https://tc39.github.io/ecma262/#sec-toboolean) +/// [toBoolean](https://tc39.es/ecma262/#sec-toboolean) /// Creates a new boolean value from the input pub fn to_boolean(value: &Value) -> Value { match *value.deref().borrow() { diff --git a/boa/src/builtins/boolean/tests.rs b/boa/src/builtins/boolean/tests.rs index bf5cd1a7b04..bf78bb86089 100644 --- a/boa/src/builtins/boolean/tests.rs +++ b/boa/src/builtins/boolean/tests.rs @@ -20,7 +20,7 @@ fn construct_and_call() { var one = new Boolean(1); var zero = Boolean(0); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let one = forward_val(&mut engine, "one").unwrap(); let zero = forward_val(&mut engine, "zero").unwrap(); @@ -39,7 +39,7 @@ fn constructor_gives_true_instance() { var trueBool = new Boolean(trueVal); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let true_val = forward_val(&mut engine, "trueVal").expect("value expected"); let true_num = forward_val(&mut engine, "trueNum").expect("value expected"); let true_string = forward_val(&mut engine, "trueString").expect("value expected"); @@ -67,7 +67,7 @@ fn instances_have_correct_proto_set() { var boolProto = Boolean.prototype; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let bool_instance = forward_val(&mut engine, "boolInstance").expect("value expected"); let bool_prototype = forward_val(&mut engine, "boolProto").expect("value expected"); diff --git a/boa/src/builtins/console.rs b/boa/src/builtins/console.rs index 4bca7f75453..623d709ba93 100644 --- a/boa/src/builtins/console.rs +++ b/boa/src/builtins/console.rs @@ -1,11 +1,14 @@ -use crate::builtins::function::NativeFunctionData; -use crate::builtins::value::{ - from_value, log_string_from, to_value, ResultValue, Value, ValueData, +#![allow(clippy::print_stdout)] + +use crate::{ + builtins::{ + function::NativeFunctionData, + value::{from_value, log_string_from, to_value, ResultValue, Value, ValueData}, + }, + exec::Interpreter, }; -use crate::exec::Interpreter; use gc::Gc; -use std::iter::FromIterator; -use std::ops::Deref; +use std::{iter::FromIterator, ops::Deref}; /// Print a javascript value to the standard output stream /// diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 798451d8d3b..a2b067611bb 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -8,18 +8,19 @@ use crate::{ value::{to_value, ResultValue, Value, ValueData}, }, exec::Interpreter, - syntax::ast::expr::Expr, + syntax::ast::node::{FormalParameter, Node}, }; use gc::{custom_trace, Gc}; use gc_derive::{Finalize, Trace}; use std::fmt::{self, Debug}; +use std::ops::Deref; /// fn(this, arguments, ctx) pub type NativeFunctionData = fn(&Value, &[Value], &mut Interpreter) -> ResultValue; /// A Javascript function /// A member of the Object type that may be invoked as a subroutine -/// +/// /// In our implementation, Function is extending Object by holding an object field which some extra data /// A Javascript function @@ -37,26 +38,36 @@ pub struct RegularFunction { /// The fields associated with the function pub object: Object, /// This function's expression - pub expr: Expr, + pub node: Node, /// The argument declarations of the function - pub args: Vec, + pub args: Vec, } impl RegularFunction { /// Make a new regular function #[allow(clippy::cast_possible_wrap)] - pub fn new(expr: Expr, args: Vec) -> Self { + pub fn new(node: Node, f_args: Vec) -> Self { + let mut args = vec![]; + for i in f_args { + let node = if let Some(init) = &i.init { + init.deref().clone() + } else { + Node::Local(i.name.clone()) + }; + args.push(node); + } + let mut object = Object::default(); object.properties.insert( "arguments".to_string(), Property::default().value(Gc::new(ValueData::Integer(args.len() as i32))), ); - Self { object, expr, args } + Self { object, node, args } } } -#[derive(Finalize, Clone)] /// Represents a native javascript function in memory +#[derive(Finalize, Clone)] pub struct NativeFunction { /// The fields associated with the function pub object: Object, diff --git a/boa/src/builtins/function/tests.rs b/boa/src/builtins/function/tests.rs index 83c9f38a6ad..89801682d4c 100644 --- a/boa/src/builtins/function/tests.rs +++ b/boa/src/builtins/function/tests.rs @@ -14,7 +14,7 @@ fn check_arguments_object() { var val = jason(100, 6); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let expected_return_val: f64 = 100.0; let return_val = forward_val(&mut engine, "val").expect("value expected"); assert_eq!(return_val.is_double(), true); diff --git a/boa/src/builtins/json.rs b/boa/src/builtins/json.rs index 8be05f14501..0a7e9865a29 100644 --- a/boa/src/builtins/json.rs +++ b/boa/src/builtins/json.rs @@ -1,13 +1,13 @@ use crate::builtins::function::NativeFunctionData; use crate::builtins::object::{Object, ObjectKind, PROTOTYPE}; /// The JSON Object -/// +/// use crate::builtins::value::{to_value, ResultValue, Value, ValueData}; use crate::exec::Interpreter; use serde_json::{self, to_string_pretty, Value as JSONValue}; /// Parse a JSON string into a Javascript object -/// +/// pub fn parse(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { match serde_json::from_str::( &args diff --git a/boa/src/builtins/math/tests.rs b/boa/src/builtins/math/tests.rs index 8049e359913..405cc82f593 100644 --- a/boa/src/builtins/math/tests.rs +++ b/boa/src/builtins/math/tests.rs @@ -10,7 +10,7 @@ fn abs() { var b = Math.abs(1.23456 - 7.89012); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -30,7 +30,7 @@ fn acos() { var d = Math.acos(2); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward(&mut engine, "b"); @@ -53,7 +53,7 @@ fn acosh() { var c = Math.acosh(0.5); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward(&mut engine, "b"); @@ -73,7 +73,7 @@ fn asin() { var b = Math.asin(5 / 3); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward(&mut engine, "b"); @@ -91,7 +91,7 @@ fn asinh() { var b = Math.asinh(0); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -110,7 +110,7 @@ fn atan() { var c = Math.atan(-0); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -130,7 +130,7 @@ fn atan2() { var b = Math.atan2(15, 90); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -149,7 +149,7 @@ fn cbrt() { var c = Math.cbrt(1); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -170,7 +170,7 @@ fn ceil() { var c = Math.ceil(-7.004); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -190,7 +190,7 @@ fn cos() { var b = Math.cos(1); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -209,7 +209,7 @@ fn cosh() { var c = Math.cosh(-1); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -230,7 +230,7 @@ fn exp() { var c = Math.exp(2); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -251,7 +251,7 @@ fn floor() { var c = Math.floor(3.01); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -272,7 +272,7 @@ fn log() { var c = Math.log(-1); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -293,7 +293,7 @@ fn log10() { var c = Math.log10(-2); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -314,7 +314,7 @@ fn log2() { var c = Math.log2(-2); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -335,7 +335,7 @@ fn max() { var c = Math.max(-10, 20); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -356,7 +356,7 @@ fn min() { var c = Math.min(-10, 20); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -378,7 +378,7 @@ fn pow() { var d = Math.pow(7, -2); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -400,7 +400,7 @@ fn round() { var b = Math.round(-20.3); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -419,7 +419,7 @@ fn sign() { var c = Math.sign(0); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -439,7 +439,7 @@ fn sin() { var b = Math.sin(1); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -457,7 +457,7 @@ fn sinh() { var b = Math.sinh(1); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -476,7 +476,7 @@ fn sqrt() { var c = Math.sqrt(9); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -497,7 +497,7 @@ fn sqrt() { // var a = Math.tan(1.1); // "#; -// forward(&mut engine, init); +// eprintln!("{}", forward(&mut engine, init)); // let a = forward_val(&mut engine, "a").unwrap(); @@ -513,7 +513,7 @@ fn tanh() { var b = Math.tanh(0); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); @@ -531,7 +531,7 @@ fn trunc() { var b = Math.trunc(0.123); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index 220bbb42dae..74bc723a5a1 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -108,7 +108,6 @@ pub fn to_locale_string(this: &Value, _args: &[Value], _ctx: &mut Interpreter) - /// /// https://tc39.es/ecma262/#sec-number.prototype.toprecision pub fn to_precision(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - println!("Number::to_precision()"); let this_num = to_number(this); let _num_str_len = format!("{}", this_num.to_num()).len(); let _precision = match args.get(0) { @@ -119,7 +118,7 @@ pub fn to_precision(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> Res None => 0, }; // TODO: Implement toPrecision - unimplemented!(); + unimplemented!("TODO: Implement toPrecision"); } /// Number().toString() diff --git a/boa/src/builtins/number/tests.rs b/boa/src/builtins/number/tests.rs index dd8b0802ae8..a63b736c29a 100644 --- a/boa/src/builtins/number/tests.rs +++ b/boa/src/builtins/number/tests.rs @@ -24,7 +24,7 @@ fn call_number() { var from_exp = Number("2.34e+2"); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let default_zero = forward_val(&mut engine, "default_zero").unwrap(); let int_one = forward_val(&mut engine, "int_one").unwrap(); let float_two = forward_val(&mut engine, "float_two").unwrap(); @@ -57,7 +57,7 @@ fn to_exponential() { var noop_exp = Number("1.23e+2").toExponential(); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let default_exp = forward(&mut engine, "default_exp"); let int_exp = forward(&mut engine, "int_exp"); let float_exp = forward(&mut engine, "float_exp"); @@ -85,7 +85,7 @@ fn to_fixed() { var nan_fixed = Number("I am not a number").toFixed(); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let default_fixed = forward(&mut engine, "default_fixed"); let pos_fixed = forward(&mut engine, "pos_fixed"); let neg_fixed = forward(&mut engine, "neg_fixed"); @@ -113,7 +113,7 @@ fn to_locale_string() { // TODO: We don't actually do any locale checking here // To honor the spec we should print numbers according to user locale. - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let default_locale = forward(&mut engine, "default_locale"); let small_locale = forward(&mut engine, "small_locale"); let big_locale = forward(&mut engine, "big_locale"); @@ -139,7 +139,7 @@ fn to_precision() { var neg_precision = Number(-123456789).toPrecision(4); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let default_precision = forward(&mut engine, "default_precision"); let low_precision = forward(&mut engine, "low_precision"); let more_precision = forward(&mut engine, "more_precision"); @@ -170,7 +170,7 @@ fn to_string() { var neg_string = Number(-1.2).toString(); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let default_string = forward(&mut engine, "default_string"); let int_string = forward(&mut engine, "int_string"); let float_string = forward(&mut engine, "float_string"); @@ -198,7 +198,7 @@ fn value_of() { var neg_val = Number("-1.2e+4").valueOf() "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let default_val = forward_val(&mut engine, "default_val").unwrap(); let int_val = forward_val(&mut engine, "int_val").unwrap(); let float_val = forward_val(&mut engine, "float_val").unwrap(); diff --git a/boa/src/builtins/property.rs b/boa/src/builtins/property.rs index 1ea3fae662e..3b4f3d4fe51 100644 --- a/boa/src/builtins/property.rs +++ b/boa/src/builtins/property.rs @@ -2,8 +2,8 @@ use crate::builtins::value::{from_value, to_value, FromValue, ToValue, Value, Va use gc_derive::{Finalize, Trace}; /// A Javascript Property AKA The Property Descriptor -/// [[SPEC] - The Property Descriptor Specification Type](https://tc39.github.io/ecma262/#sec-property-descriptor-specification-type) -/// [[SPEC] - Default Attribute Values](https://tc39.github.io/ecma262/#table-4) +/// [[SPEC] - The Property Descriptor Specification Type](https://tc39.es/ecma262/#sec-property-descriptor-specification-type) +/// [[SPEC] - Default Attribute Values](https://tc39.es/ecma262/#table-4) /// /// Any field in a JavaScript Property may be present or absent. #[derive(Trace, Finalize, Clone, Debug)] diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 889f237be5a..c822acdbc39 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -192,19 +192,16 @@ pub fn test(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let mut last_index = from_value::(this.get_field_slice("lastIndex")).map_err(to_value)?; let result = this.with_internal_state_ref(|regex: &RegExp| { - let result = match regex.matcher.find_at(arg_str.as_str(), last_index) { - Some(m) => { - if regex.use_last_index { - last_index = m.end(); - } - true + let result = if let Some(m) = regex.matcher.find_at(arg_str.as_str(), last_index) { + if regex.use_last_index { + last_index = m.end(); } - None => { - if regex.use_last_index { - last_index = 0; - } - false + true + } else { + if regex.use_last_index { + last_index = 0; } + false }; Ok(Gc::new(ValueData::Boolean(result))) }); @@ -219,37 +216,34 @@ pub fn exec(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { from_value::(this.get_field_slice("lastIndex")).map_err(to_value)?; let result = this.with_internal_state_ref(|regex: &RegExp| { let mut locations = regex.matcher.capture_locations(); - let result = - match regex + let result = if let Some(m) = + regex .matcher .captures_read_at(&mut locations, arg_str.as_str(), last_index) - { - Some(m) => { - if regex.use_last_index { - last_index = m.end(); - } - let mut result = Vec::with_capacity(locations.len()); - for i in 0..locations.len() { - if let Some((start, end)) = locations.get(i) { - result.push(to_value( - arg_str.get(start..end).expect("Could not get slice"), - )); - } else { - result.push(Gc::new(ValueData::Undefined)); - } - } - let result = to_value(result); - result.set_prop_slice("index", Property::default().value(to_value(m.start()))); - result.set_prop_slice("input", Property::default().value(to_value(arg_str))); - result - } - None => { - if regex.use_last_index { - last_index = 0; - } - Gc::new(ValueData::Null) + { + if regex.use_last_index { + last_index = m.end(); + } + let mut result = Vec::with_capacity(locations.len()); + for i in 0..locations.len() { + if let Some((start, end)) = locations.get(i) { + result.push(to_value( + arg_str.get(start..end).expect("Could not get slice"), + )); + } else { + result.push(Gc::new(ValueData::Undefined)); } - }; + } + let result = to_value(result); + result.set_prop_slice("index", Property::default().value(to_value(m.start()))); + result.set_prop_slice("input", Property::default().value(to_value(arg_str))); + result + } else { + if regex.use_last_index { + last_index = 0; + } + Gc::new(ValueData::Null) + }; Ok(result) }); this.set_field_slice("lastIndex", to_value(last_index)); diff --git a/boa/src/builtins/regexp/tests.rs b/boa/src/builtins/regexp/tests.rs index 62f00964ad3..49ac140a73b 100644 --- a/boa/src/builtins/regexp/tests.rs +++ b/boa/src/builtins/regexp/tests.rs @@ -13,7 +13,7 @@ fn test_constructors() { var ctor_literal = new RegExp(/[0-9]+(\.[0-9]+)?/); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!(forward(&mut engine, "constructed.test('1.0')"), "true"); assert_eq!(forward(&mut engine, "literal.test('1.0')"), "true"); assert_eq!(forward(&mut engine, "ctor_literal.test('1.0')"), "true"); @@ -36,7 +36,7 @@ fn check_regexp_constructor_is_function() { // var re_sm = /test/sm; // "#; // -// forward(&mut engine, init); +// eprintln!("{}", forward(&mut engine, init)); // assert_eq!(forward(&mut engine, "re_gi.global"), "true"); // assert_eq!(forward(&mut engine, "re_gi.ignoreCase"), "true"); // assert_eq!(forward(&mut engine, "re_gi.multiline"), "false"); @@ -62,7 +62,7 @@ fn test_last_index() { var regex = /[0-9]+(\.[0-9]+)?/g; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!(forward(&mut engine, "regex.lastIndex"), "0"); assert_eq!(forward(&mut engine, "regex.test('1.0foo')"), "true"); assert_eq!(forward(&mut engine, "regex.lastIndex"), "3"); @@ -79,7 +79,7 @@ fn test_exec() { var result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog'); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!(forward(&mut engine, "result[0]"), "Quick Brown Fox Jumps"); assert_eq!(forward(&mut engine, "result[1]"), "Brown"); assert_eq!(forward(&mut engine, "result[2]"), "Jumps"); diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index c3ffc898137..61be0e13dd1 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -65,7 +65,7 @@ pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue /// Returns a single element String containing the code unit at index pos within the String value /// resulting from converting this object to a String. If there is no element at that index, the /// result is the empty String. The result is a String value, not a String object. -/// +/// pub fn char_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value @@ -99,7 +99,7 @@ pub fn char_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal /// Returns a Number (a nonnegative integer less than 216) that is the numeric value of the code /// unit at index pos within the String resulting from converting this object to a String. If there /// is no element at that index, the result is NaN. -/// +/// pub fn char_code_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value @@ -130,7 +130,7 @@ pub fn char_code_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Resu /// Returns a String that is the result of concatenating this String and all strings provided as /// arguments -/// +/// pub fn concat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value @@ -146,7 +146,7 @@ pub fn concat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValu /// Returns a String that is the result of repeating this String the number of times given by the /// first argument -/// +/// pub fn repeat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value @@ -163,7 +163,7 @@ pub fn repeat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValu /// Returns a String which contains the slice of the JS String from character at "start" index up /// to but not including character at "end" index -/// +/// pub fn slice(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value @@ -214,7 +214,7 @@ pub fn slice(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue /// Returns a Boolean indicating whether the sequence of code units of the /// "search string" is the same as the corresponding code units of this string /// starting at index "position" -/// +/// pub fn starts_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value @@ -253,7 +253,7 @@ pub fn starts_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Resul /// Returns a Boolean indicating whether the sequence of code units of the /// "search string" is the same as the corresponding code units of this string /// starting at position "end position" - length -/// +/// pub fn ends_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value @@ -295,7 +295,7 @@ pub fn ends_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultV /// the result of converting this object to a String, at one or more indices /// that are greater than or equal to position. If position is undefined, 0 is /// assumed, so as to search all of the String. -/// +/// pub fn includes(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value @@ -442,7 +442,7 @@ pub fn replace(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal /// position, then the smallest such index is returned; otherwise, -1 is /// returned. If position is undefined, 0 is assumed, so as to search all of the /// String. -/// +/// pub fn index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value @@ -488,7 +488,7 @@ pub fn index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa /// position, then the greatest such index is returned; otherwise, -1 is /// returned. If position is undefined, the length of the String value is /// assumed, so as to search all of the String. -/// +/// pub fn last_index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value diff --git a/boa/src/builtins/string/tests.rs b/boa/src/builtins/string/tests.rs index b64aa7a7e6d..08807ad62d5 100644 --- a/boa/src/builtins/string/tests.rs +++ b/boa/src/builtins/string/tests.rs @@ -21,7 +21,7 @@ fn check_string_constructor_is_function() { // const c = new String(' \b '); // cosnt d = new String('中文长度') // "#; -// forward(&mut engine, init); +// eprintln!("{}", forward(&mut engine, init)); // let a = forward(&mut engine, "a.length"); // assert_eq!(a, String::from("1")); // let b = forward(&mut engine, "b.length"); @@ -44,7 +44,7 @@ fn concat() { var world = new String('world! '); var nice = new String('Have a nice day.'); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); // Todo: fix this let _a = forward(&mut engine, "hello.concat(world, nice)"); @@ -63,7 +63,7 @@ fn construct_and_call() { var hello = new String('Hello'); var world = String('world'); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let hello = forward_val(&mut engine, "hello").unwrap(); let world = forward_val(&mut engine, "world").unwrap(); @@ -80,7 +80,7 @@ fn repeat() { var en = new String('english'); var zh = new String('中文'); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let empty = String::from(""); assert_eq!(forward(&mut engine, "empty.repeat(0)"), empty); @@ -108,7 +108,7 @@ fn replace() { a = a.replace("a", "2"); a "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let empty = String::from("2bc"); assert_eq!(forward(&mut engine, "a"), empty); @@ -131,7 +131,7 @@ fn replace_with_function() { a = a.replace(/c(o)(o)(l)/, replacer); a; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!( forward(&mut engine, "a"), String::from("ecmascript is awesome!") @@ -155,7 +155,7 @@ fn starts_with() { var enLiteral = 'english'; var zhLiteral = '中文'; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let pass = String::from("true"); assert_eq!(forward(&mut engine, "empty.startsWith('')"), pass); assert_eq!(forward(&mut engine, "en.startsWith('e')"), pass); @@ -179,7 +179,7 @@ fn ends_with() { var enLiteral = 'english'; var zhLiteral = '中文'; "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let pass = String::from("true"); assert_eq!(forward(&mut engine, "empty.endsWith('')"), pass); assert_eq!(forward(&mut engine, "en.endsWith('h')"), pass); @@ -250,7 +250,7 @@ fn match_all() { var str = 'table football, foosball'; var matches = str.matchAll(regexp); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!( forward(&mut engine, "matches[0][0]"), String::from("football") @@ -275,7 +275,7 @@ fn test_match() { var result4 = str.match(RegExp("B", 'g')); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); assert_eq!(forward(&mut engine, "result1[0]"), "Quick Brown Fox Jumps"); assert_eq!(forward(&mut engine, "result1[1]"), "Brown"); assert_eq!(forward(&mut engine, "result1[2]"), "Jumps"); diff --git a/boa/src/builtins/symbol/tests.rs b/boa/src/builtins/symbol/tests.rs index 8854a0fd540..4ef9e0b7072 100644 --- a/boa/src/builtins/symbol/tests.rs +++ b/boa/src/builtins/symbol/tests.rs @@ -17,7 +17,7 @@ fn call_symbol_and_check_return_type() { let init = r#" var sym = Symbol(); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let sym = forward_val(&mut engine, "sym").unwrap(); assert_eq!(sym.is_symbol(), true); } @@ -29,7 +29,7 @@ fn print_symbol_expect_description() { let init = r#" var sym = Symbol("Hello"); "#; - forward(&mut engine, init); + eprintln!("{}", forward(&mut engine, init)); let sym = forward_val(&mut engine, "sym.toString()").unwrap(); assert_eq!(sym.to_string(), "Symbol(Hello)"); } diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index f9a9050098e..03d676c6d9e 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -21,8 +21,8 @@ use std::{ str::FromStr, }; -#[must_use] /// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`) +#[must_use] pub type ResultValue = Result; /// A Garbage-collected Javascript value as represented in the interpreter pub type Value = Gc; @@ -57,17 +57,14 @@ pub enum ValueData { impl ValueData { /// Returns a new empty object pub fn new_obj(global: Option<&Value>) -> Value { - match global { - Some(glob) => { - let obj_proto = glob.get_field_slice("Object").get_field_slice(PROTOTYPE); + if let Some(glob) = global { + let obj_proto = glob.get_field_slice("Object").get_field_slice(PROTOTYPE); - let obj = Object::create(obj_proto); - Gc::new(ValueData::Object(GcCell::new(obj))) - } - None => { - let obj = Object::default(); - Gc::new(ValueData::Object(GcCell::new(obj))) - } + let obj = Object::create(obj_proto); + Gc::new(ValueData::Object(GcCell::new(obj))) + } else { + let obj = Object::default(); + Gc::new(ValueData::Object(GcCell::new(obj))) } } @@ -170,7 +167,7 @@ impl ValueData { } /// Returns true if the value is true - /// [toBoolean](https://tc39.github.io/ecma262/#sec-toboolean) + /// [toBoolean](https://tc39.es/ecma262/#sec-toboolean) pub fn is_true(&self) -> bool { match *self { ValueData::Object(_) => true, @@ -341,15 +338,14 @@ impl ValueData { }; // If the getter is populated, use that. If not use [[Value]] instead - match prop_getter { - Some(val) => val, - None => { - let val = prop - .value - .as_ref() - .expect("Could not get property as reference"); - val.clone() - } + if let Some(val) = prop_getter { + val + } else { + let val = prop + .value + .as_ref() + .expect("Could not get property as reference"); + val.clone() } } None => Gc::new(ValueData::Undefined), @@ -789,39 +785,37 @@ fn display_obj(v: &ValueData, print_internals: bool) -> String { indent: usize, print_internals: bool, ) -> String { - match *data { - ValueData::Object(ref v) => { - // The in-memory address of the current object - let addr = address_of(v.borrow().deref()); - - // We need not continue if this object has already been - // printed up the current chain - if encounters.contains(&addr) { - return String::from("[Cycle]"); - } + if let ValueData::Object(ref v) = *data { + // The in-memory address of the current object + let addr = address_of(v.borrow().deref()); + + // We need not continue if this object has already been + // printed up the current chain + if encounters.contains(&addr) { + return String::from("[Cycle]"); + } - // Mark the current object as encountered - encounters.insert(addr); + // Mark the current object as encountered + encounters.insert(addr); - let result = if print_internals { - print_obj_value!(all of v, display_obj_internal, indent, encounters).join(",\n") - } else { - print_obj_value!(props of v, display_obj_internal, indent, encounters, print_internals) + let result = if print_internals { + print_obj_value!(all of v, display_obj_internal, indent, encounters).join(",\n") + } else { + print_obj_value!(props of v, display_obj_internal, indent, encounters, print_internals) .join(",\n") - }; + }; - // If the current object is referenced in a different branch, - // it will not cause an infinte printing loop, so it is safe to be printed again - encounters.remove(&addr); + // If the current object is referenced in a different branch, + // it will not cause an infinte printing loop, so it is safe to be printed again + encounters.remove(&addr); - let closing_indent = String::from_utf8(vec![b' '; indent.wrapping_sub(4)]) - .expect("Could not create the closing brace's indentation string"); - - format!("{{\n{}\n{}}}", result, closing_indent) - } + let closing_indent = String::from_utf8(vec![b' '; indent.wrapping_sub(4)]) + .expect("Could not create the closing brace's indentation string"); + format!("{{\n{}\n{}}}", result, closing_indent) + } else { // Every other type of data is printed as is - _ => format!("{}", data), + format!("{}", data) } } @@ -829,7 +823,7 @@ fn display_obj(v: &ValueData, print_internals: bool) -> String { } impl Display for ValueData { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ValueData::Null => write!(f, "null"), ValueData::Undefined => write!(f, "undefined"), @@ -855,15 +849,14 @@ impl Display for ValueData { ValueData::Function(ref v) => match *v.borrow() { Function::NativeFunc(_) => write!(f, "function() {{ [native code] }}"), Function::RegularFunc(ref rf) => { - write!(f, "function(")?; - let last_index = rf.args.len() - 1; + write!(f, "function{}(", if rf.args.is_empty() { "" } else { " " })?; for (index, arg) in rf.args.iter().enumerate() { write!(f, "{}", arg)?; - if index != last_index { + if index + 1 != rf.args.len() { write!(f, ", ")?; } } - write!(f, "){}", rf.expr) + write!(f, ") {}", rf.node) } }, } diff --git a/boa/src/environment/declarative_environment_record.rs b/boa/src/environment/declarative_environment_record.rs index 27b409a7be5..553f720d92f 100644 --- a/boa/src/environment/declarative_environment_record.rs +++ b/boa/src/environment/declarative_environment_record.rs @@ -3,7 +3,7 @@ //! Each declarative Environment Record is associated with an ECMAScript program scope containing variable, //! `constant`, `let`, `class`, `module`, `import`, and/or function declarations. //! A declarative Environment Record binds the set of identifiers defined by the declarations contained within its scope. -//! More info: [ECMA-262 sec-declarative-environment-records](https://tc39.github.io/ecma262/#sec-declarative-environment-records) +//! More info: [ECMA-262 sec-declarative-environment-records](https://tc39.es/ecma262/#sec-declarative-environment-records) use crate::{ builtins::value::{Value, ValueData}, @@ -80,12 +80,11 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord { fn initialize_binding(&mut self, name: &str, value: Value) { if let Some(ref mut record) = self.env_rec.get_mut(name) { - match record.value { - Some(_) => { - // TODO: change this when error handling comes into play - panic!("Identifier {} has already been defined", name); - } - None => record.value = Some(value), + if record.value.is_none() { + record.value = Some(value); + } else { + // TODO: change this when error handling comes into play + panic!("Identifier {} has already been defined", name); } } } @@ -121,16 +120,15 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord { } fn get_binding_value(&self, name: &str, _strict: bool) -> Value { - match self.env_rec.get(name) { - Some(binding) => binding + if let Some(binding) = self.env_rec.get(name) { + binding .value .as_ref() .expect("Could not get record as reference") - .clone(), - None => { - // TODO: change this when error handling comes into play - panic!("ReferenceError: Cannot get binding value for {}", name); - } + .clone() + } else { + // TODO: change this when error handling comes into play + panic!("ReferenceError: Cannot get binding value for {}", name); } } diff --git a/boa/src/environment/environment_record_trait.rs b/boa/src/environment/environment_record_trait.rs index d527005affe..f2166289e1b 100644 --- a/boa/src/environment/environment_record_trait.rs +++ b/boa/src/environment/environment_record_trait.rs @@ -1,7 +1,7 @@ //! # Environment Records //! -//! -//! +//! +//! //! //! Some environments are stored as `JSObjects`. This is for GC, i.e we want to keep an environment if a variable is closed-over (a closure is returned). //! All of the logic to handle scope/environment records are stored in here. @@ -15,7 +15,7 @@ use crate::{ use gc::{Finalize, Trace}; use std::fmt::Debug; -/// +/// /// /// In the ECMAScript specification Environment Records are hierachical and have a base class with abstract methods. /// In this implementation we have a trait which represents the behaviour of all `EnvironmentRecord` types. diff --git a/boa/src/environment/function_environment_record.rs b/boa/src/environment/function_environment_record.rs index 569b1cf539a..e18abcc327f 100644 --- a/boa/src/environment/function_environment_record.rs +++ b/boa/src/environment/function_environment_record.rs @@ -6,7 +6,7 @@ //! If a function is not an `ArrowFunction` function and references super, //! its function Environment Record also contains the state that is used to perform super method invocations //! from within the function. -//! More info: +//! More info: use crate::{ builtins::value::{Value, ValueData}, @@ -32,7 +32,7 @@ pub enum BindingStatus { Uninitialized, } -/// +/// #[derive(Debug, Trace, Finalize, Clone)] pub struct FunctionEnvironmentRecord { pub env_rec: HashMap, @@ -179,16 +179,15 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord { } fn get_binding_value(&self, name: &str, _strict: bool) -> Value { - match self.env_rec.get(name) { - Some(binding) => binding + if let Some(binding) = self.env_rec.get(name) { + binding .value .as_ref() .expect("Could not get record as reference") - .clone(), - None => { - // TODO: change this when error handling comes into play - panic!("ReferenceError: Cannot get binding value for {}", name); - } + .clone() + } else { + // TODO: change this when error handling comes into play + panic!("ReferenceError: Cannot get binding value for {}", name); } } diff --git a/boa/src/environment/global_environment_record.rs b/boa/src/environment/global_environment_record.rs index fc72655b4f2..372fef25698 100644 --- a/boa/src/environment/global_environment_record.rs +++ b/boa/src/environment/global_environment_record.rs @@ -5,7 +5,7 @@ //! A global Environment Record provides the bindings for built-in globals (clause 18), //! properties of the global object, and for all top-level declarations (13.2.8, 13.2.10) //! that occur within a Script. -//! More info: +//! More info: use crate::{ builtins::value::{Value, ValueData}, diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index ae834db715b..77e1840fca7 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -1,6 +1,6 @@ //! # Lexical Environment //! -//! +//! //! //! The following operations are used to operate upon lexical environments //! This is the entrypoint to lexical environments. @@ -61,7 +61,7 @@ impl EnvironmentError { } impl fmt::Display for EnvironmentError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.details) } } @@ -237,7 +237,7 @@ pub fn new_function_environment( this_binding_status: BindingStatus::Uninitialized, // hardcoding to unitialized for now until short functions are properly supported home_object: Gc::new(ValueData::Undefined), new_target, - outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.github.io/ecma262/#sec-ecmascript-function-objects + outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.es/ecma262/#sec-ecmascript-function-objects this_value: Gc::new(ValueData::Undefined), // TODO: this_value should start as an Option as its not always there to begin with }))) } diff --git a/boa/src/environment/object_environment_record.rs b/boa/src/environment/object_environment_record.rs index 03448c19b7a..a17049f0191 100644 --- a/boa/src/environment/object_environment_record.rs +++ b/boa/src/environment/object_environment_record.rs @@ -4,7 +4,7 @@ //! An object Environment Record binds the set of string identifier names that directly //! correspond to the property names of its binding object. //! Property keys that are not strings in the form of an `IdentifierName` are not included in the set of bound identifiers. -//! More info: [Object Records](https://tc39.github.io/ecma262/#sec-object-environment-records) +//! More info: [Object Records](https://tc39.es/ecma262/#sec-object-environment-records) use crate::{ builtins::{ diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 231e9281322..45aa641fa1c 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -17,7 +17,7 @@ use crate::{ realm::Realm, syntax::ast::{ constant::Const, - expr::{Expr, ExprDef}, + node::{MethodDefinitionKind, Node, PropertyDefinition}, op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp}, }, }; @@ -32,7 +32,7 @@ pub trait Executor { /// Make a new execution engine fn new(realm: Realm) -> Self; /// Run an expression - fn run(&mut self, expr: &Expr) -> ResultValue; + fn run(&mut self, expr: &Node) -> ResultValue; } /// A Javascript intepreter @@ -48,7 +48,7 @@ fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value { AssignOp::Add => v_a + v_b, AssignOp::Sub => v_a - v_b, AssignOp::Mul => v_a * v_b, - AssignOp::Pow => v_a.as_num_to_power(v_b), + AssignOp::Exp => v_a.as_num_to_power(v_b), AssignOp::Div => v_a / v_b, AssignOp::Mod => v_a % v_b, AssignOp::And => v_a & v_b, @@ -68,18 +68,18 @@ impl Executor for Interpreter { } #[allow(clippy::match_same_arms)] - fn run(&mut self, expr: &Expr) -> ResultValue { - match expr.def { - ExprDef::Const(Const::Null) => Ok(to_value(None::<()>)), - ExprDef::Const(Const::Undefined) => Ok(Gc::new(ValueData::Undefined)), - ExprDef::Const(Const::Num(num)) => Ok(to_value(num)), - ExprDef::Const(Const::Int(num)) => Ok(to_value(num)), + fn run(&mut self, node: &Node) -> ResultValue { + match *node { + Node::Const(Const::Null) => Ok(to_value(None::<()>)), + Node::Const(Const::Undefined) => Ok(Gc::new(ValueData::Undefined)), + Node::Const(Const::Num(num)) => Ok(to_value(num)), + Node::Const(Const::Int(num)) => Ok(to_value(num)), // we can't move String from Const into value, because const is a garbage collected value // Which means Drop() get's called on Const, but str will be gone at that point. // Do Const values need to be garbage collected? We no longer need them once we've generated Values - ExprDef::Const(Const::String(ref str)) => Ok(to_value(str.to_owned())), - ExprDef::Const(Const::Bool(val)) => Ok(to_value(val)), - ExprDef::Block(ref es) => { + Node::Const(Const::String(ref str)) => Ok(to_value(str.to_owned())), + Node::Const(Const::Bool(val)) => Ok(to_value(val)), + Node::Block(ref es) => { { let env = &mut self.realm.environment; env.push(new_declarative_environment(Some( @@ -105,31 +105,31 @@ impl Executor for Interpreter { Ok(obj) } - ExprDef::Local(ref name) => { + Node::Local(ref name) => { let val = self.realm.environment.get_binding_value(name); Ok(val) } - ExprDef::GetConstField(ref obj, ref field) => { + Node::GetConstField(ref obj, ref field) => { let val_obj = self.run(obj)?; Ok(val_obj.borrow().get_field_slice(field)) } - ExprDef::GetField(ref obj, ref field) => { + Node::GetField(ref obj, ref field) => { let val_obj = self.run(obj)?; let val_field = self.run(field)?; Ok(val_obj .borrow() .get_field_slice(&val_field.borrow().to_string())) } - ExprDef::Call(ref callee, ref args) => { - let (this, func) = match callee.def { - ExprDef::GetConstField(ref obj, ref field) => { + Node::Call(ref callee, ref args) => { + let (this, func) = match callee.deref() { + Node::GetConstField(ref obj, ref field) => { let mut obj = self.run(obj)?; if obj.get_type() != "object" || obj.get_type() != "symbol" { obj = self.to_object(&obj).expect("failed to convert to object"); } (obj.clone(), obj.borrow().get_field_slice(field)) } - ExprDef::GetField(ref obj, ref field) => { + Node::GetField(ref obj, ref field) => { let obj = self.run(obj)?; let field = self.run(field)?; ( @@ -141,7 +141,7 @@ impl Executor for Interpreter { }; let mut v_args = Vec::with_capacity(args.len()); for arg in args.iter() { - if let ExprDef::UnaryOp(UnaryOp::Spread, ref x) = arg.def { + if let Node::Spread(ref x) = arg.deref() { let val = self.run(x)?; let mut vals = self.extract_array_properties(&val).unwrap(); v_args.append(&mut vals); @@ -158,31 +158,31 @@ impl Executor for Interpreter { fnct_result } - ExprDef::WhileLoop(ref cond, ref expr) => { + Node::WhileLoop(ref cond, ref expr) => { let mut result = Gc::new(ValueData::Undefined); while self.run(cond)?.borrow().is_true() { result = self.run(expr)?; } Ok(result) } - ExprDef::If(ref cond, ref expr, None) => Ok(if self.run(cond)?.borrow().is_true() { + Node::If(ref cond, ref expr, None) => Ok(if self.run(cond)?.borrow().is_true() { self.run(expr)? } else { Gc::new(ValueData::Undefined) }), - ExprDef::If(ref cond, ref expr, Some(ref else_e)) => { + Node::If(ref cond, ref expr, Some(ref else_e)) => { Ok(if self.run(cond)?.borrow().is_true() { self.run(expr)? } else { self.run(else_e)? }) } - ExprDef::Switch(ref val_e, ref vals, ref default) => { + Node::Switch(ref val_e, ref vals, ref default) => { let val = self.run(val_e)?; let mut result = Gc::new(ValueData::Null); let mut matched = false; for tup in vals.iter() { - let tup: &(Expr, Vec) = tup; + let tup: &(Node, Vec) = tup; let cond = &tup.0; let block = &tup.1; if val == self.run(cond)? { @@ -205,23 +205,39 @@ impl Executor for Interpreter { } Ok(result) } - ExprDef::ObjectDecl(ref map) => { + Node::Object(ref properties) => { let global_val = &self .realm .environment .get_global_object() .expect("Could not get the global object"); let obj = ValueData::new_obj(Some(global_val)); - for (key, val) in map.iter() { - obj.borrow().set_field_slice(&key.clone(), self.run(val)?); + + // TODO: Implement the rest of the property types. + for property in properties { + match property { + PropertyDefinition::Property(key, value) => { + obj.borrow().set_field_slice(&key.clone(), self.run(value)?); + } + PropertyDefinition::MethodDefinition(kind, name, func) => { + if let MethodDefinitionKind::Ordinary = kind { + obj.borrow().set_field_slice(&name.clone(), self.run(func)?); + } else { + // TODO: Implement other types of MethodDefinitionKinds. + unimplemented!("other types of property method definitions."); + } + } + i => unimplemented!("{:?} type of property", i), + } } + Ok(obj) } - ExprDef::ArrayDecl(ref arr) => { + Node::ArrayDecl(ref arr) => { let array = array::new_array(self)?; let mut elements: Vec = vec![]; for elem in arr.iter() { - if let ExprDef::UnaryOp(UnaryOp::Spread, ref x) = elem.def { + if let Node::Spread(ref x) = elem.deref() { let val = self.run(x)?; let mut vals = self.extract_array_properties(&val).unwrap(); elements.append(&mut vals); @@ -232,9 +248,9 @@ impl Executor for Interpreter { array::add_to_array_object(&array, &elements)?; Ok(array) } - ExprDef::FunctionDecl(ref name, ref args, ref expr) => { + Node::FunctionDecl(ref name, ref args, ref expr) => { let function = - Function::RegularFunc(RegularFunction::new(*expr.clone(), args.clone())); + Function::RegularFunc(RegularFunction::new(*expr.clone(), args.to_vec())); let val = Gc::new(ValueData::Function(Box::new(GcCell::new(function)))); if name.is_some() { self.realm.environment.create_mutable_binding( @@ -249,14 +265,14 @@ impl Executor for Interpreter { } Ok(val) } - ExprDef::ArrowFunctionDecl(ref args, ref expr) => { + Node::ArrowFunctionDecl(ref args, ref expr) => { let function = - Function::RegularFunc(RegularFunction::new(*expr.clone(), args.clone())); + Function::RegularFunc(RegularFunction::new(*expr.clone(), args.to_vec())); Ok(Gc::new(ValueData::Function(Box::new(GcCell::new( function, ))))) } - ExprDef::BinOp(BinOp::Num(ref op), ref a, ref b) => { + Node::BinOp(BinOp::Num(ref op), ref a, ref b) => { let v_r_a = self.run(a)?; let v_r_b = self.run(b)?; let v_a = (*v_r_a).clone(); @@ -265,12 +281,12 @@ impl Executor for Interpreter { NumOp::Add => v_a + v_b, NumOp::Sub => v_a - v_b, NumOp::Mul => v_a * v_b, - NumOp::Pow => v_a.as_num_to_power(v_b), + NumOp::Exp => v_a.as_num_to_power(v_b), NumOp::Div => v_a / v_b, NumOp::Mod => v_a % v_b, })) } - ExprDef::UnaryOp(ref op, ref a) => { + Node::UnaryOp(ref op, ref a) => { let v_r_a = self.run(a)?; let v_a = (*v_r_a).clone(); Ok(match *op { @@ -286,11 +302,10 @@ impl Executor for Interpreter { !(num_v_a as i32) }) } - UnaryOp::Spread => Gc::new(v_a), // for now we can do nothing but return the value as-is _ => unreachable!(), }) } - ExprDef::BinOp(BinOp::Bit(ref op), ref a, ref b) => { + Node::BinOp(BinOp::Bit(ref op), ref a, ref b) => { let v_r_a = self.run(a)?; let v_r_b = self.run(b)?; let v_a = (*v_r_a).clone(); @@ -301,9 +316,11 @@ impl Executor for Interpreter { BitOp::Xor => v_a ^ v_b, BitOp::Shl => v_a << v_b, BitOp::Shr => v_a >> v_b, + // TODO Fix + BitOp::UShr => v_a >> v_b, })) } - ExprDef::BinOp(BinOp::Comp(ref op), ref a, ref b) => { + Node::BinOp(BinOp::Comp(ref op), ref a, ref b) => { let v_r_a = self.run(a)?; let v_r_b = self.run(b)?; let v_a = v_r_a.borrow(); @@ -323,7 +340,7 @@ impl Executor for Interpreter { CompOp::LessThanOrEqual => v_a.to_num() <= v_b.to_num(), })) } - ExprDef::BinOp(BinOp::Log(ref op), ref a, ref b) => { + Node::BinOp(BinOp::Log(ref op), ref a, ref b) => { // turn a `Value` into a `bool` let to_bool = |val| from_value::(val).expect("Could not convert JS value to bool"); @@ -332,8 +349,8 @@ impl Executor for Interpreter { LogOp::Or => to_value(to_bool(self.run(a)?) || to_bool(self.run(b)?)), }) } - ExprDef::BinOp(BinOp::Assign(ref op), ref a, ref b) => match a.def { - ExprDef::Local(ref name) => { + Node::BinOp(BinOp::Assign(ref op), ref a, ref b) => match a.deref() { + Node::Local(ref name) => { let v_a = (*self.realm.environment.get_binding_value(&name)).clone(); let v_b = (*self.run(b)?).clone(); let value = exec_assign_op(op, v_a, v_b); @@ -342,7 +359,7 @@ impl Executor for Interpreter { .set_mutable_binding(&name, value.clone(), true); Ok(value) } - ExprDef::GetConstField(ref obj, ref field) => { + Node::GetConstField(ref obj, ref field) => { let v_r_a = self.run(obj)?; let v_a = (*v_r_a.borrow().get_field_slice(field)).clone(); let v_b = (*self.run(b)?).clone(); @@ -354,7 +371,12 @@ impl Executor for Interpreter { } _ => Ok(Gc::new(ValueData::Undefined)), }, - ExprDef::Construct(ref callee, ref args) => { + Node::New(ref call) => { + let (callee, args) = match call.as_ref() { + Node::Call(callee, args) => (callee, args), + _ => unreachable!("Node::New(ref call): 'call' must only be Node::Call type."), + }; + let func_object = self.run(callee)?; let mut v_args = Vec::with_capacity(args.len()); for arg in args.iter() { @@ -390,8 +412,8 @@ impl Executor for Interpreter { for i in 0..data.args.len() { let arg_expr = data.args.get(i).expect("Could not get data argument"); - let name = match arg_expr.def { - ExprDef::Local(ref n) => Some(n), + let name = match arg_expr.deref() { + Node::Local(ref n) => Some(n), _ => None, } .expect("Could not get argument"); @@ -403,7 +425,7 @@ impl Executor for Interpreter { ); env.initialize_binding(name, expr.to_owned()); } - let result = self.run(&data.expr); + let result = self.run(&data.node); self.realm.environment.pop(); result } @@ -411,7 +433,7 @@ impl Executor for Interpreter { _ => Ok(Gc::new(ValueData::Undefined)), } } - ExprDef::Return(ref ret) => { + Node::Return(ref ret) => { let result = match *ret { Some(ref v) => self.run(v), None => Ok(Gc::new(ValueData::Undefined)), @@ -420,11 +442,11 @@ impl Executor for Interpreter { self.is_return = true; result } - ExprDef::Throw(ref ex) => Err(self.run(ex)?), - ExprDef::Assign(ref ref_e, ref val_e) => { + Node::Throw(ref ex) => Err(self.run(ex)?), + Node::Assign(ref ref_e, ref val_e) => { let val = self.run(val_e)?; - match ref_e.def { - ExprDef::Local(ref name) => { + match ref_e.deref() { + Node::Local(ref name) => { if self.realm.environment.has_binding(name) { // Binding already exists self.realm @@ -439,13 +461,13 @@ impl Executor for Interpreter { self.realm.environment.initialize_binding(name, val.clone()); } } - ExprDef::GetConstField(ref obj, ref field) => { + Node::GetConstField(ref obj, ref field) => { let val_obj = self.run(obj)?; val_obj .borrow() .set_field_slice(&field.clone(), val.clone()); } - ExprDef::GetField(ref obj, ref field) => { + Node::GetField(ref obj, ref field) => { let val_obj = self.run(obj)?; let val_field = self.run(field)?; val_obj.borrow().set_field(val_field, val.clone()); @@ -454,7 +476,7 @@ impl Executor for Interpreter { } Ok(val) } - ExprDef::VarDecl(ref vars) => { + Node::VarDecl(ref vars) => { for var in vars.iter() { let (name, value) = var.clone(); let val = match value { @@ -470,7 +492,7 @@ impl Executor for Interpreter { } Ok(Gc::new(ValueData::Undefined)) } - ExprDef::LetDecl(ref vars) => { + Node::LetDecl(ref vars) => { for var in vars.iter() { let (name, value) = var.clone(); let val = match value { @@ -486,7 +508,7 @@ impl Executor for Interpreter { } Ok(Gc::new(ValueData::Undefined)) } - ExprDef::ConstDecl(ref vars) => { + Node::ConstDecl(ref vars) => { for (name, value) in vars.iter() { self.realm.environment.create_immutable_binding( name.clone(), @@ -498,7 +520,7 @@ impl Executor for Interpreter { } Ok(Gc::new(ValueData::Undefined)) } - ExprDef::TypeOf(ref val_e) => { + Node::TypeOf(ref val_e) => { let val = self.run(val_e)?; Ok(to_value(match *val { ValueData::Undefined => "undefined", @@ -510,6 +532,37 @@ impl Executor for Interpreter { ValueData::Function(_) => "function", })) } + Node::StatementList(ref list) => { + { + let env = &mut self.realm.environment; + env.push(new_declarative_environment(Some( + env.get_current_environment_ref().clone(), + ))); + } + + let mut obj = to_value(None::<()>); + for (i, item) in list.iter().enumerate() { + let val = self.run(item)?; + // early return + if self.is_return { + obj = val; + break; + } + if i + 1 == list.len() { + obj = val; + } + } + + // pop the block env + let _ = self.realm.environment.pop(); + + Ok(obj) + } + Node::Spread(ref node) => { + // TODO: for now we can do nothing but return the value as-is + Ok(Gc::new((*self.run(node)?).clone())) + } + ref i => unimplemented!("{}", i), } } } @@ -549,8 +602,8 @@ impl Interpreter { )); for i in 0..data.args.len() { let arg_expr = data.args.get(i).expect("Could not get data argument"); - match arg_expr.def { - ExprDef::Local(ref name) => { + match arg_expr.deref() { + Node::Local(ref name) => { let expr: &Value = arguments_list.get(i).expect("Could not get argument"); self.realm.environment.create_mutable_binding( @@ -562,8 +615,8 @@ impl Interpreter { .environment .initialize_binding(name, expr.clone()); } - ExprDef::UnaryOp(UnaryOp::Spread, ref expr) => { - if let ExprDef::Local(ref name) = expr.def { + Node::Spread(ref expr) => { + if let Node::Local(ref name) = expr.deref() { let array = array::new_array(self)?; array::add_to_array_object(&array, &arguments_list[i..])?; @@ -592,7 +645,7 @@ impl Interpreter { .environment .initialize_binding("arguments", arguments_obj); - let result = self.run(&data.expr); + let result = self.run(&data.node); self.realm.environment.pop(); result } @@ -764,7 +817,7 @@ impl Interpreter { } } - /// `extract_array_properties` converts an array object into a rust vector of Values. + /// `extract_array_properties` converts an array object into a rust vector of Values. /// This is useful for the spread operator, for any other object an `Err` is returned fn extract_array_properties(&mut self, value: &Value) -> Result>, ()> { if let ValueData::Object(ref x) = *value.deref().borrow() { diff --git a/boa/src/lib.rs b/boa/src/lib.rs index b5fae18ff61..aa31a174e33 100644 --- a/boa/src/lib.rs +++ b/boa/src/lib.rs @@ -1,6 +1,30 @@ -#![deny(unused_qualifications, clippy::correctness, clippy::style)] -#![warn(clippy::perf)] -#![allow(clippy::cognitive_complexity)] +#![deny( + unused_qualifications, + clippy::all, + unused_qualifications, + unused_import_braces, + unused_lifetimes, + unreachable_pub, + trivial_numeric_casts, + rustdoc, + missing_debug_implementations, + missing_copy_implementations, + deprecated_in_future, + meta_variable_misuse, + non_ascii_idents, + rust_2018_compatibility, + rust_2018_idioms, + future_incompatible, + nonstandard_style +)] +#![warn(clippy::perf, clippy::single_match_else, clippy::dbg_macro)] +#![allow( + clippy::missing_inline_in_public_items, + clippy::cognitive_complexity, + clippy::must_use_candidate, + clippy::missing_errors_doc, + clippy::as_conversions +)] pub mod builtins; pub mod environment; @@ -16,17 +40,18 @@ use crate::{ builtins::value::ResultValue, exec::{Executor, Interpreter}, realm::Realm, - syntax::{ast::expr::Expr, lexer::Lexer, parser::Parser}, + syntax::{ast::node::Node, lexer::Lexer, parser::Parser}, }; #[cfg(feature = "serde-ast")] pub use serde_json; -fn parser_expr(src: &str) -> Result { +fn parser_expr(src: &str) -> Result { let mut lexer = Lexer::new(src); lexer.lex().map_err(|e| format!("SyntaxError: {}", e))?; let tokens = lexer.tokens; - Parser::new(tokens) + // dbg!(&tokens); + Parser::new(&tokens) .parse_all() .map_err(|e| format!("ParsingError: {}", e)) } diff --git a/boa/src/syntax/ast/constant.rs b/boa/src/syntax/ast/constant.rs index 9ec126f9ced..6d7fbc160fb 100644 --- a/boa/src/syntax/ast/constant.rs +++ b/boa/src/syntax/ast/constant.rs @@ -3,10 +3,9 @@ use std::fmt::{Display, Formatter, Result}; #[cfg(feature = "serde-ast")] use serde::{Deserialize, Serialize}; - +/// A Javascript Constant. #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] -/// A Javascript Constant pub enum Const { /// A UTF-8 string, such as `"Hello, world"` String(String), @@ -23,7 +22,7 @@ pub enum Const { } impl Display for Const { - fn fmt(&self, f: &mut Formatter) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { match *self { Const::String(ref st) => write!(f, "\"{}\"", st), Const::Num(num) => write!(f, "{}", num), diff --git a/boa/src/syntax/ast/expr.rs b/boa/src/syntax/ast/expr.rs deleted file mode 100644 index 6198a46d56e..00000000000 --- a/boa/src/syntax/ast/expr.rs +++ /dev/null @@ -1,246 +0,0 @@ -use crate::syntax::ast::{ - constant::Const, - op::{BinOp, Operator, UnaryOp}, -}; -use gc_derive::{Finalize, Trace}; -use std::{ - collections::btree_map::BTreeMap, - fmt::{Display, Formatter, Result}, -}; - -#[cfg(feature = "serde-ast")] -use serde::{Deserialize, Serialize}; - -#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] -#[derive(Clone, Trace, Finalize, Debug, PartialEq)] -pub struct Expr { - /// The expression definition - pub def: ExprDef, -} - -impl Expr { - /// Create a new expression with a starting and ending position - pub fn new(def: ExprDef) -> Self { - Self { def } - } -} - -impl Display for Expr { - fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{}", self.def) - } -} - -#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, Trace, Finalize, PartialEq)] -/// A Javascript Expression -pub enum ExprDef { - /// Run a operation between 2 expressions - BinOp(BinOp, Box, Box), - /// Run an operation on a value - UnaryOp(UnaryOp, Box), - /// Make a constant value - Const(Const), - /// Const declaration - ConstDecl(Vec<(String, Expr)>), - /// Construct an object from the function and arg{ - Construct(Box, Vec), - /// Run several expressions from top-to-bottom - Block(Vec), - /// Load a reference to a value, or a function argument - Local(String), - /// Gets the constant field of a value - GetConstField(Box, String), - /// Gets the field of a value - GetField(Box, Box), - /// Call a function with some values - Call(Box, Vec), - /// Repeatedly run an expression while the conditional expression resolves to true - WhileLoop(Box, Box), - /// Check if a conditional expression is true and run an expression if it is and another expression if it isn't - If(Box, Box, Option>), - /// Run blocks whose cases match the expression - Switch(Box, Vec<(Expr, Vec)>, Option>), - /// Create an object out of the binary tree given - ObjectDecl(Box>), - /// Create an array with items inside - ArrayDecl(Vec), - /// Create a function with the given name, arguments, and expression - FunctionDecl(Option, Vec, Box), - /// Create an arrow function with the given arguments and expression - ArrowFunctionDecl(Vec, Box), - /// Return the expression from a function - Return(Option>), - /// Throw a value - Throw(Box), - /// Assign an expression to a value - Assign(Box, Box), - /// { - /// A variable declaratio - /// } - VarDecl(Vec<(String, Option)>), - /// Let declaraton - LetDecl(Vec<(String, Option)>), - /// Return a string representing the type of the given expression - TypeOf(Box), -} - -impl Operator for ExprDef { - fn get_assoc(&self) -> bool { - match *self { - ExprDef::Construct(_, _) - | ExprDef::UnaryOp(_, _) - | ExprDef::TypeOf(_) - | ExprDef::If(_, _, _) - | ExprDef::Assign(_, _) => false, - _ => true, - } - } - fn get_precedence(&self) -> u64 { - match self { - ExprDef::GetField(_, _) | ExprDef::GetConstField(_, _) => 1, - ExprDef::Call(_, _) | ExprDef::Construct(_, _) => 2, - ExprDef::UnaryOp(UnaryOp::IncrementPost, _) - | ExprDef::UnaryOp(UnaryOp::IncrementPre, _) - | ExprDef::UnaryOp(UnaryOp::DecrementPost, _) - | ExprDef::UnaryOp(UnaryOp::DecrementPre, _) => 3, - ExprDef::UnaryOp(UnaryOp::Not, _) - | ExprDef::UnaryOp(UnaryOp::Tilde, _) - | ExprDef::UnaryOp(UnaryOp::Minus, _) - | ExprDef::TypeOf(_) => 4, - ExprDef::BinOp(op, _, _) => op.get_precedence(), - ExprDef::If(_, _, _) => 15, - // 16 should be yield - ExprDef::Assign(_, _) => 17, - _ => 19, - } - } -} - -impl Display for ExprDef { - fn fmt(&self, f: &mut Formatter) -> Result { - match *self { - ExprDef::Const(ref c) => write!(f, "{}", c), - ExprDef::Block(ref block) => { - write!(f, "{{")?; - for expr in block.iter() { - write!(f, "{};", expr)?; - } - write!(f, "}}") - } - ExprDef::Local(ref s) => write!(f, "{}", s), - ExprDef::GetConstField(ref ex, ref field) => write!(f, "{}.{}", ex, field), - ExprDef::GetField(ref ex, ref field) => write!(f, "{}[{}]", ex, field), - ExprDef::Call(ref ex, ref args) => { - write!(f, "{}(", ex)?; - let arg_strs: Vec = args.iter().map(ToString::to_string).collect(); - write!(f, "{})", arg_strs.join(",")) - } - ExprDef::Construct(ref func, ref args) => { - f.write_fmt(format_args!("new {}", func))?; - f.write_str("(")?; - let mut first = true; - for e in args.iter() { - if !first { - f.write_str(", ")?; - } - first = false; - Display::fmt(e, f)?; - } - f.write_str(")") - } - ExprDef::WhileLoop(ref cond, ref expr) => write!(f, "while({}) {}", cond, expr), - ExprDef::If(ref cond, ref expr, None) => write!(f, "if({}) {}", cond, expr), - ExprDef::If(ref cond, ref expr, Some(ref else_e)) => { - write!(f, "if({}) {} else {}", cond, expr, else_e) - } - ExprDef::Switch(ref val, ref vals, None) => { - f.write_fmt(format_args!("switch({})", val))?; - f.write_str(" {")?; - for e in vals.iter() { - f.write_fmt(format_args!("case {}: \n", e.0))?; - join_expr(f, &e.1)?; - } - f.write_str("}") - } - ExprDef::Switch(ref val, ref vals, Some(ref def)) => { - f.write_fmt(format_args!("switch({})", val))?; - f.write_str(" {")?; - for e in vals.iter() { - f.write_fmt(format_args!("case {}: \n", e.0))?; - join_expr(f, &e.1)?; - } - f.write_str("default: \n")?; - Display::fmt(def, f)?; - f.write_str("}") - } - ExprDef::ObjectDecl(ref map) => { - f.write_str("{")?; - for (key, value) in map.iter() { - f.write_fmt(format_args!("{}: {},", key, value))?; - } - f.write_str("}") - } - ExprDef::ArrayDecl(ref arr) => { - f.write_str("[")?; - join_expr(f, arr)?; - f.write_str("]") - } - ExprDef::FunctionDecl(ref name, ref args, ref expr) => { - write!(f, "function ")?; - if let Some(func_name) = name { - f.write_fmt(format_args!("{}", func_name))?; - } - write!(f, "{{")?; - join_expr(f, args)?; - write!(f, "}} {}", expr) - } - ExprDef::ArrowFunctionDecl(ref args, ref expr) => { - write!(f, "(")?; - join_expr(f, args)?; - write!(f, ") => {}", expr) - } - ExprDef::BinOp(ref op, ref a, ref b) => write!(f, "{} {} {}", a, op, b), - ExprDef::UnaryOp(ref op, ref a) => write!(f, "{}{}", op, a), - ExprDef::Return(Some(ref ex)) => write!(f, "return {}", ex), - ExprDef::Return(None) => write!(f, "return"), - ExprDef::Throw(ref ex) => write!(f, "throw {}", ex), - ExprDef::Assign(ref ref_e, ref val) => write!(f, "{} = {}", ref_e, val), - ExprDef::VarDecl(ref vars) | ExprDef::LetDecl(ref vars) => { - if let ExprDef::VarDecl(_) = *self { - f.write_str("var ")?; - } else { - f.write_str("let ")?; - } - for (key, val) in vars.iter() { - match val { - Some(x) => f.write_fmt(format_args!("{} = {}", key, x))?, - None => f.write_fmt(format_args!("{}", key))?, - } - } - Ok(()) - } - ExprDef::ConstDecl(ref vars) => { - f.write_str("const ")?; - for (key, val) in vars.iter() { - f.write_fmt(format_args!("{} = {}", key, val))? - } - Ok(()) - } - ExprDef::TypeOf(ref e) => write!(f, "typeof {}", e), - } - } -} - -/// `join_expr` - Utility to join multiple Expressions into a single string -fn join_expr(f: &mut Formatter, expr: &[Expr]) -> Result { - let mut first = true; - for e in expr.iter() { - if !first { - f.write_str(", ")?; - } - first = false; - Display::fmt(e, f)?; - } - Ok(()) -} diff --git a/boa/src/syntax/ast/keyword.rs b/boa/src/syntax/ast/keyword.rs index 05dc45fd581..6a02a548ea6 100644 --- a/boa/src/syntax/ast/keyword.rs +++ b/boa/src/syntax/ast/keyword.rs @@ -7,10 +7,11 @@ use std::{ #[cfg(feature = "serde-ast")] use serde::{Deserialize, Serialize}; -#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] -#[derive(Clone, Copy, PartialEq, Debug)] /// A Javascript Keyword +/// /// As specificed by +#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] +#[derive(Clone, Copy, PartialEq, Debug)] pub enum Keyword { /// The `await` keyword Await, @@ -89,7 +90,7 @@ pub enum Keyword { #[derive(Debug, Clone, Copy)] pub struct KeywordError; impl Display for KeywordError { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { write!(f, "invalid token") } } @@ -150,7 +151,7 @@ impl FromStr for Keyword { } } impl Display for Keyword { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { write!( f, "{}", diff --git a/boa/src/syntax/ast/mod.rs b/boa/src/syntax/ast/mod.rs index 03cbbd536cf..12374f427dc 100644 --- a/boa/src/syntax/ast/mod.rs +++ b/boa/src/syntax/ast/mod.rs @@ -1,6 +1,6 @@ pub mod constant; -pub mod expr; pub mod keyword; +pub mod node; pub mod op; pub mod pos; pub mod punc; diff --git a/boa/src/syntax/ast/node.rs b/boa/src/syntax/ast/node.rs new file mode 100644 index 00000000000..fe50ba10bb1 --- /dev/null +++ b/boa/src/syntax/ast/node.rs @@ -0,0 +1,372 @@ +use crate::syntax::ast::{ + constant::Const, + op::{BinOp, Operator, UnaryOp}, +}; +use gc_derive::{Finalize, Trace}; +use std::fmt; + +#[cfg(feature = "serde-ast")] +use serde::{Deserialize, Serialize}; + +/// A Javascript AST Node. +#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, Trace, Finalize, PartialEq)] +pub enum Node { + /// Create an array with items inside. + ArrayDecl(Vec), + /// Create an arrow function with the given arguments and internal AST node. + ArrowFunctionDecl(Vec, Box), + /// Assign an AST node result to an AST node. + Assign(Box, Box), + /// Run an operation between 2 AST nodes. + BinOp(BinOp, Box, Box), + /// Run several AST nodes from top-to-bottom. + Block(Vec), + /// Break statement with an optional label. + Break(Option), + /// Call a function with some values. + Call(Box, Vec), + /// Conditional Operator (`{condition} ? {if true} : {if false}`). + ConditionalOp(Box, Box, Box), + /// Make a constant value. + Const(Const), + /// Const declaration. + ConstDecl(Vec<(String, Node)>), + /// Continue with an optional label. + Continue(Option), + /// Create a function with the given name, arguments, and internal AST node. + FunctionDecl(Option, Vec, Box), + /// Gets the constant field of a value. + GetConstField(Box, String), + /// Gets the [field] of a value. + GetField(Box, Box), + /// [init], [cond], [step], body + ForLoop( + Option>, + Option>, + Option>, + Box, + ), + /// Check if a conditional expression is true and run an expression if it is and another expression if it isn't + If(Box, Box, Option>), + /// Let declaraton + LetDecl(Vec<(String, Option)>), + /// Load a reference to a value, or a function argument + Local(String), + /// New + New(Box), + /// Object Declaration + Object(Vec), + /// Return the expression from a function + Return(Option>), + /// Run blocks whose cases match the expression + Switch(Box, Vec<(Node, Vec)>, Option>), + /// `...a` - spread an iterable value + Spread(Box), + // Similar to Block but without the braces + StatementList(Vec), + /// Throw a value + Throw(Box), + /// Return a string representing the type of the given expression + TypeOf(Box), + /// Try / Catch + Try( + Box, + Option>, + Option>, + Option>, + ), + /// The JavaScript `this` keyword refers to the object it belongs to. + /// + /// A property of an execution context (global, function or eval) that, + /// in non–strict mode, is always a reference to an object and in strict + /// mode can be any value. + /// + /// For more information, please check: + This, + /// Run an operation on a value + UnaryOp(UnaryOp, Box), + /// A variable declaration + VarDecl(Vec<(String, Option)>), + /// Repeatedly run an expression while the conditional expression resolves to true + WhileLoop(Box, Box), +} + +impl Operator for Node { + fn get_assoc(&self) -> bool { + match *self { + Node::UnaryOp(_, _) | Node::TypeOf(_) | Node::If(_, _, _) | Node::Assign(_, _) => false, + _ => true, + } + } + fn get_precedence(&self) -> u64 { + match self { + Node::GetField(_, _) | Node::GetConstField(_, _) => 1, + Node::Call(_, _) => 2, + Node::UnaryOp(UnaryOp::IncrementPost, _) + | Node::UnaryOp(UnaryOp::IncrementPre, _) + | Node::UnaryOp(UnaryOp::DecrementPost, _) + | Node::UnaryOp(UnaryOp::DecrementPre, _) => 3, + Node::UnaryOp(UnaryOp::Not, _) + | Node::UnaryOp(UnaryOp::Tilde, _) + | Node::UnaryOp(UnaryOp::Minus, _) + | Node::TypeOf(_) => 4, + Node::BinOp(op, _, _) => op.get_precedence(), + Node::If(_, _, _) => 15, + // 16 should be yield + Node::Assign(_, _) => 17, + _ => 19, + } + } +} + +impl fmt::Display for Node { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.display(f, 0) + } +} + +impl Node { + /// Implements the display formatting with indentation. + fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result { + let indent = " ".repeat(indentation); + match *self { + Self::Block(_) => {} + _ => write!(f, "{}", indent)?, + } + + match *self { + Self::Const(ref c) => write!(f, "{}", c), + Self::ConditionalOp(_, _, _) => write!(f, "Conditional op"), // TODO + Self::ForLoop(_, _, _, _) => write!(f, "for loop"), // TODO + Self::This => write!(f, "this"), // TODO + Self::Try(_, _, _, _) => write!(f, "try/catch/finally"), // TODO + Self::Break(_) => write!(f, "break"), // TODO: add potential value + Self::Continue(_) => write!(f, "continue"), // TODO: add potential value + Self::Spread(ref node) => write!(f, "...{}", node), + Self::Block(ref block) => { + writeln!(f, "{{")?; + for node in block.iter() { + node.display(f, indentation + 1)?; + + match node { + Self::Block(_) + | Self::If(_, _, _) + | Self::Switch(_, _, _) + | Self::FunctionDecl(_, _, _) + | Self::WhileLoop(_, _) + | Self::StatementList(_) => {} + _ => write!(f, ";")?, + } + writeln!(f)?; + } + write!(f, "{}}}", indent) + } + Node::StatementList(ref list) => { + for node in list.iter() { + node.display(f, indentation + 1)?; + + match node { + Self::Block(_) + | Self::If(_, _, _) + | Self::Switch(_, _, _) + | Self::FunctionDecl(_, _, _) + | Self::WhileLoop(_, _) + | Self::StatementList(_) => {} + _ => write!(f, ";")?, + } + writeln!(f)?; + } + Ok(()) + } + Self::Local(ref s) => write!(f, "{}", s), + Self::GetConstField(ref ex, ref field) => write!(f, "{}.{}", ex, field), + Self::GetField(ref ex, ref field) => write!(f, "{}[{}]", ex, field), + Self::Call(ref ex, ref args) => { + write!(f, "{}(", ex)?; + let arg_strs: Vec = args.iter().map(ToString::to_string).collect(); + write!(f, "{})", arg_strs.join(", ")) + } + Self::New(ref call) => { + let (func, args) = match call.as_ref() { + Node::Call(func, args) => (func, args), + _ => unreachable!("Node::New(ref call): 'call' must only be Node::Call type."), + }; + + write!(f, "new {}", func)?; + f.write_str("(")?; + let mut first = true; + for e in args.iter() { + if !first { + f.write_str(", ")?; + } + first = false; + write!(f, "{}", e)?; + } + f.write_str(")") + } + Self::WhileLoop(ref cond, ref node) => { + write!(f, "while ({}) ", cond)?; + node.display(f, indentation) + } + Self::If(ref cond, ref node, None) => { + write!(f, "if ({}) ", cond)?; + node.display(f, indentation) + } + Self::If(ref cond, ref node, Some(ref else_e)) => { + write!(f, "if ({}) ", cond)?; + node.display(f, indentation)?; + f.write_str(" else ")?; + else_e.display(f, indentation) + } + Self::Switch(ref val, ref vals, None) => { + writeln!(f, "switch ({}) {{", val)?; + for e in vals.iter() { + writeln!(f, "{}case {}:", indent, e.0)?; + join_nodes(f, &e.1)?; + } + writeln!(f, "{}}}", indent) + } + Self::Switch(ref val, ref vals, Some(ref def)) => { + writeln!(f, "switch ({}) {{", val)?; + for e in vals.iter() { + writeln!(f, "{}case {}:", indent, e.0)?; + join_nodes(f, &e.1)?; + } + writeln!(f, "{}default:", indent)?; + def.display(f, indentation + 1)?; + write!(f, "{}}}", indent) + } + Self::Object(ref properties) => { + f.write_str("{\n")?; + for property in properties { + match property { + PropertyDefinition::IdentifierReference(key) => { + write!(f, "{} {},", indent, key)?; + } + PropertyDefinition::Property(key, value) => { + write!(f, "{} {}: {},", indent, key, value)?; + } + PropertyDefinition::SpreadObject(key) => { + write!(f, "{} ...{},", indent, key)?; + } + PropertyDefinition::MethodDefinition(_kind, _key, _node) => { + // TODO: Implement display for PropertyDefinition::MethodDefinition. + unimplemented!("Display for PropertyDefinition::MethodDefinition"); + } + } + } + f.write_str("}") + } + Self::ArrayDecl(ref arr) => { + f.write_str("[")?; + join_nodes(f, arr)?; + f.write_str("]") + } + Self::FunctionDecl(ref name, ref _args, ref node) => { + write!(f, "function ")?; + if let Some(func_name) = name { + write!(f, "{}", func_name)?; + } + write!(f, "{{")?; + //join_nodes(f, args)?; TODO: port + f.write_str("} ")?; + node.display(f, indentation + 1) + } + Self::ArrowFunctionDecl(ref _args, ref node) => { + write!(f, "(")?; + //join_nodes(f, args)?; TODO: port + f.write_str(") => ")?; + node.display(f, indentation) + } + Self::BinOp(ref op, ref a, ref b) => write!(f, "{} {} {}", a, op, b), + Self::UnaryOp(ref op, ref a) => write!(f, "{}{}", op, a), + Self::Return(Some(ref ex)) => write!(f, "return {}", ex), + Self::Return(None) => write!(f, "return"), + Self::Throw(ref ex) => write!(f, "throw {}", ex), + Self::Assign(ref ref_e, ref val) => write!(f, "{} = {}", ref_e, val), + Self::VarDecl(ref vars) | Self::LetDecl(ref vars) => { + if let Self::VarDecl(_) = *self { + f.write_str("var ")?; + } else { + f.write_str("let ")?; + } + for (key, val) in vars.iter() { + match val { + Some(x) => write!(f, "{} = {}", key, x)?, + None => write!(f, "{}", key)?, + } + } + Ok(()) + } + Self::ConstDecl(ref vars) => { + f.write_str("const ")?; + for (key, val) in vars.iter() { + write!(f, "{} = {}", key, val)? + } + Ok(()) + } + Self::TypeOf(ref e) => write!(f, "typeof {}", e), + } + } +} + +/// Utility to join multiple Nodes into a single string. +fn join_nodes(f: &mut fmt::Formatter<'_>, nodes: &[Node]) -> fmt::Result { + let mut first = true; + for e in nodes { + if !first { + f.write_str(", ")?; + } + first = false; + write!(f, "{}", e)?; + } + Ok(()) +} + +/// "Formal parameter" is a fancy way of saying "function parameter". +/// +/// In the declaration of a function, the parameters must be identifiers, +/// not any value like numbers, strings, or objects. +///```javascript +///function foo(formalParametar1, formalParametar2) { +///} +///``` +/// For more information, please check +#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, PartialEq, Trace, Finalize)] +pub struct FormalParameter { + pub name: String, + pub init: Option>, + pub is_rest_param: bool, +} + +pub type FormalParameters = Vec; + +impl FormalParameter { + pub fn new(name: String, init: Option>, is_rest_param: bool) -> FormalParameter { + FormalParameter { + name, + init, + is_rest_param, + } + } +} + +// TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition +#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, PartialEq, Trace, Finalize)] +pub enum PropertyDefinition { + IdentifierReference(String), + Property(String, Node), + MethodDefinition(MethodDefinitionKind, String, Node), + SpreadObject(Node), +} + +#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, PartialEq, Trace, Finalize)] +pub enum MethodDefinitionKind { + Get, + Set, + Ordinary, +} diff --git a/boa/src/syntax/ast/op.rs b/boa/src/syntax/ast/op.rs index 59e3d7ac9a8..4c088b77607 100644 --- a/boa/src/syntax/ast/op.rs +++ b/boa/src/syntax/ast/op.rs @@ -16,9 +16,9 @@ pub trait Operator { } } +/// A numeric operation between 2 values #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] -/// A numeric operation between 2 values pub enum NumOp { /// `a + b` - Addition Add, @@ -29,13 +29,13 @@ pub enum NumOp { /// `a * b` - Multiplication Mul, /// `a ** b` - Exponentiation - Pow, + Exp, /// `a % b` - Modulus Mod, } impl Display for NumOp { - fn fmt(&self, f: &mut Formatter) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!( f, "{}", @@ -44,16 +44,18 @@ impl Display for NumOp { NumOp::Sub => "-", NumOp::Div => "/", NumOp::Mul => "*", - NumOp::Pow => "**", + NumOp::Exp => "**", NumOp::Mod => "%", } ) } } +/// A unary operation on a single value +/// +/// For more information, please check: #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] -/// A unary operation on a single value pub enum UnaryOp { /// `a++` - increment the value IncrementPost, @@ -71,12 +73,38 @@ pub enum UnaryOp { Not, /// `~a` - bitwise-not of the value Tilde, - /// `...a` - spread an iterable value - Spread, + /// `typeof` - Get the type of object + TypeOf, + /// The JavaScript `delete` operator removes a property from an object. + /// + /// Unlike what common belief suggests, the delete operator has nothing to do with + /// directly freeing memory. Memory management is done indirectly via breaking references. + /// If no more references to the same property are held, it is eventually released automatically. + /// + /// The `delete` operator returns `true` for all cases except when the property is an + /// [own](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) + /// [non-configurable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_delete) + /// property, in which case, `false` is returned in non-strict mode. + /// + /// For more information, please check: + Delete, + + /// The `void` operator evaluates the given `expression` and then returns `undefined`. + /// + /// This operator allows evaluating expressions that produce a value into places where an + /// expression that evaluates to `undefined` is desired. + /// The `void` operator is often used merely to obtain the `undefined` primitive value, usually using `void(0)` + /// (which is equivalent to `void 0`). In these cases, the global variable undefined can be used. + /// + /// When using an [immediately-invoked function expression](https://developer.mozilla.org/en-US/docs/Glossary/IIFE), + /// `void` can be used to force the function keyword to be treated as an expression instead of a declaration. + /// + /// For more information, please check: + Void, } impl Display for UnaryOp { - fn fmt(&self, f: &mut Formatter) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!( f, "{}", @@ -87,15 +115,17 @@ impl Display for UnaryOp { UnaryOp::Minus => "-", UnaryOp::Not => "!", UnaryOp::Tilde => "~", - UnaryOp::Spread => "...", + UnaryOp::Delete => "delete", + UnaryOp::TypeOf => "typeof", + UnaryOp::Void => "void", } ) } } +/// A bitwise operation between 2 values #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] -/// A bitwise operation between 2 values pub enum BitOp { /// `a & b` - Bitwise and And, @@ -107,10 +137,12 @@ pub enum BitOp { Shl, /// `a >> b` - Bit-shift rightrights Shr, + /// `a >>> b` - Zero-fill right shift + UShr, } impl Display for BitOp { - fn fmt(&self, f: &mut Formatter) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!( f, "{}", @@ -120,14 +152,15 @@ impl Display for BitOp { BitOp::Xor => "^", BitOp::Shl => "<<", BitOp::Shr => ">>", + BitOp::UShr => ">>>", } ) } } +/// A comparitive operation between 2 values #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] -/// A comparitive operation between 2 values pub enum CompOp { /// `a == b` - Equality Equal, @@ -148,7 +181,7 @@ pub enum CompOp { } impl Display for CompOp { - fn fmt(&self, f: &mut Formatter) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!( f, "{}", @@ -166,9 +199,9 @@ impl Display for CompOp { } } +/// A logical operation between 2 boolean values #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] -/// A logical operation between 2 boolean values pub enum LogOp { /// `a && b` - Logical and And, @@ -177,7 +210,7 @@ pub enum LogOp { } impl Display for LogOp { - fn fmt(&self, f: &mut Formatter) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!( f, "{}", @@ -189,9 +222,9 @@ impl Display for LogOp { } } +/// A binary operation between 2 values #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] -/// A binary operation between 2 values pub enum BinOp { /// Numeric operation Num(NumOp), @@ -211,10 +244,10 @@ impl Operator for BinOp { } fn get_precedence(&self) -> u64 { match *self { - BinOp::Num(NumOp::Pow) => 4, + BinOp::Num(NumOp::Exp) => 4, BinOp::Num(NumOp::Mul) | BinOp::Num(NumOp::Div) | BinOp::Num(NumOp::Mod) => 5, BinOp::Num(NumOp::Add) | BinOp::Num(NumOp::Sub) => 6, - BinOp::Bit(BitOp::Shl) | BinOp::Bit(BitOp::Shr) => 7, + BinOp::Bit(BitOp::Shl) | BinOp::Bit(BitOp::Shr) | BinOp::Bit(BitOp::UShr) => 7, BinOp::Comp(CompOp::LessThan) | BinOp::Comp(CompOp::LessThanOrEqual) | BinOp::Comp(CompOp::GreaterThan) @@ -234,7 +267,7 @@ impl Operator for BinOp { } impl Display for BinOp { - fn fmt(&self, f: &mut Formatter) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!( f, "{}", @@ -249,9 +282,11 @@ impl Display for BinOp { } } +/// A binary operation between 2 values +/// +/// #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] -/// A binary operation between 2 values pub enum AssignOp { /// `a += b` - Add assign Add, @@ -260,7 +295,7 @@ pub enum AssignOp { /// `a *= b` - Mul assign Mul, /// `a **= b` - Exponent assign - Pow, + Exp, /// `a /= b` - Div assign Div, /// `a %= b` - Modulus assign @@ -278,7 +313,7 @@ pub enum AssignOp { } impl Display for AssignOp { - fn fmt(&self, f: &mut Formatter) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!( f, "{}", @@ -286,7 +321,7 @@ impl Display for AssignOp { AssignOp::Add => "+=", AssignOp::Sub => "-=", AssignOp::Mul => "*=", - AssignOp::Pow => "**=", + AssignOp::Exp => "**=", AssignOp::Div => "/=", AssignOp::Mod => "%=", AssignOp::And => "&=", diff --git a/boa/src/syntax/ast/pos.rs b/boa/src/syntax/ast/pos.rs index 9c99a1d9982..154d035ab6b 100644 --- a/boa/src/syntax/ast/pos.rs +++ b/boa/src/syntax/ast/pos.rs @@ -1,13 +1,14 @@ #[cfg(feature = "serde-ast")] use serde::{Deserialize, Serialize}; -#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] -#[derive(Clone, Copy, PartialEq, Debug)] -/// A position in the Javascript source code +/// A position in the Javascript source code. +/// /// Stores both the column number and the line number /// /// ## Similar Implementations /// [V8: Location](https://cs.chromium.org/chromium/src/v8/src/parsing/scanner.h?type=cs&q=isValid+Location&g=0&l=216) +#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] +#[derive(Clone, Copy, PartialEq, Debug)] pub struct Position { // Column number pub column_number: u64, @@ -16,9 +17,9 @@ pub struct Position { } impl Position { - /// Create a new position, positions are usually created by Tokens.. + /// Creates a new `Position`. /// - /// See [Token](struct.token/Token.html) for example usage + /// Positions are usually created by a [`Token`](struct.token/Token.html). /// /// # Arguments /// diff --git a/boa/src/syntax/ast/punc.rs b/boa/src/syntax/ast/punc.rs index a6df171dc21..e840c7366a9 100644 --- a/boa/src/syntax/ast/punc.rs +++ b/boa/src/syntax/ast/punc.rs @@ -1,11 +1,12 @@ +use crate::syntax::ast::op::{BinOp, BitOp, CompOp, LogOp, NumOp}; use std::fmt::{Display, Error, Formatter}; #[cfg(feature = "serde-ast")] use serde::{Deserialize, Serialize}; +/// Punctuation #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[derive(PartialEq, Clone, Copy, Debug)] -/// Punctuation pub enum Punctuator { /// `+` Add, @@ -92,7 +93,7 @@ pub enum Punctuator { /// `|` Or, /// `**` - Pow, + Exp, /// `?` Question, /// `>>` @@ -112,8 +113,41 @@ pub enum Punctuator { /// `^` Xor, } + +impl Punctuator { + /// Attempts to convert a punctuator (`+`, `=`...) to a Binary Operator + /// + /// If there is no match, `None` will be returned. + pub fn as_binop(self) -> Option { + match self { + Punctuator::Add => Some(BinOp::Num(NumOp::Add)), + Punctuator::Sub => Some(BinOp::Num(NumOp::Sub)), + Punctuator::Mul => Some(BinOp::Num(NumOp::Mul)), + Punctuator::Div => Some(BinOp::Num(NumOp::Div)), + Punctuator::Mod => Some(BinOp::Num(NumOp::Mod)), + Punctuator::And => Some(BinOp::Bit(BitOp::And)), + Punctuator::Or => Some(BinOp::Bit(BitOp::Or)), + Punctuator::Xor => Some(BinOp::Bit(BitOp::Xor)), + Punctuator::BoolAnd => Some(BinOp::Log(LogOp::And)), + Punctuator::BoolOr => Some(BinOp::Log(LogOp::Or)), + Punctuator::Eq => Some(BinOp::Comp(CompOp::Equal)), + Punctuator::NotEq => Some(BinOp::Comp(CompOp::NotEqual)), + Punctuator::StrictEq => Some(BinOp::Comp(CompOp::StrictEqual)), + Punctuator::StrictNotEq => Some(BinOp::Comp(CompOp::StrictNotEqual)), + Punctuator::LessThan => Some(BinOp::Comp(CompOp::LessThan)), + Punctuator::GreaterThan => Some(BinOp::Comp(CompOp::GreaterThan)), + Punctuator::GreaterThanOrEq => Some(BinOp::Comp(CompOp::GreaterThanOrEqual)), + Punctuator::LessThanOrEq => Some(BinOp::Comp(CompOp::LessThanOrEqual)), + Punctuator::LeftSh => Some(BinOp::Bit(BitOp::Shl)), + Punctuator::RightSh => Some(BinOp::Bit(BitOp::Shr)), + Punctuator::URightSh => Some(BinOp::Bit(BitOp::UShr)), + _ => None, + } + } +} + impl Display for Punctuator { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { write!( f, "{}", @@ -160,7 +194,7 @@ impl Display for Punctuator { Punctuator::OpenBracket => "[", Punctuator::OpenParen => "(", Punctuator::Or => "|", - Punctuator::Pow => "**", + Punctuator::Exp => "**", Punctuator::Question => "?", Punctuator::RightSh => ">>", Punctuator::Semicolon => ";", diff --git a/boa/src/syntax/ast/token.rs b/boa/src/syntax/ast/token.rs index 844b488c515..f85870f68df 100644 --- a/boa/src/syntax/ast/token.rs +++ b/boa/src/syntax/ast/token.rs @@ -4,36 +4,36 @@ use std::fmt::{Debug, Display, Formatter, Result}; #[cfg(feature = "serde-ast")] use serde::{Deserialize, Serialize}; -/// Represents a token +/// Represents a token. #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq)] pub struct Token { /// The token Data - pub data: TokenData, + pub kind: TokenKind, /// Token position from original source code pub pos: Position, } impl Token { /// Create a new detailed token from the token data, line number and column number - pub fn new(data: TokenData, line_number: u64, column_number: u64) -> Self { + pub fn new(kind: TokenKind, line_number: u64, column_number: u64) -> Self { Self { - data, + kind, pos: Position::new(line_number, column_number), } } } impl Display for Token { - fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{}", self.data) + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.kind) } } pub struct VecToken(Vec); impl Debug for VecToken { - fn fmt(&self, f: &mut Formatter) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { let mut buffer = String::new(); for token in &self.0 { buffer.push_str(&token.to_string()); @@ -41,10 +41,11 @@ impl Debug for VecToken { write!(f, "{}", buffer) } } + +/// Represents the type of Token and the data it has inside. #[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] #[derive(Clone, PartialEq, Debug)] -/// Represents the type of Token -pub enum TokenData { +pub enum TokenKind { /// A boolean literal, which is either `true` or `false` BooleanLiteral(bool), /// The end of the file @@ -63,25 +64,25 @@ pub enum TokenData { StringLiteral(String), /// A regular expression, consisting of body and flags RegularExpressionLiteral(String, String), - /// A comment - Comment(String), + /// Indicates the end of a line \n + LineTerminator, } -impl Display for TokenData { - fn fmt(&self, f: &mut Formatter) -> Result { +impl Display for TokenKind { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { match *self { - TokenData::BooleanLiteral(ref val) => write!(f, "{}", val), - TokenData::EOF => write!(f, "end of file"), - TokenData::Identifier(ref ident) => write!(f, "{}", ident), - TokenData::Keyword(ref word) => write!(f, "{}", word), - TokenData::NullLiteral => write!(f, "null"), - TokenData::NumericLiteral(ref num) => write!(f, "{}", num), - TokenData::Punctuator(ref punc) => write!(f, "{}", punc), - TokenData::StringLiteral(ref lit) => write!(f, "{}", lit), - TokenData::RegularExpressionLiteral(ref body, ref flags) => { + TokenKind::BooleanLiteral(ref val) => write!(f, "{}", val), + TokenKind::EOF => write!(f, "end of file"), + TokenKind::Identifier(ref ident) => write!(f, "{}", ident), + TokenKind::Keyword(ref word) => write!(f, "{}", word), + TokenKind::NullLiteral => write!(f, "null"), + TokenKind::NumericLiteral(ref num) => write!(f, "{}", num), + TokenKind::Punctuator(ref punc) => write!(f, "{}", punc), + TokenKind::StringLiteral(ref lit) => write!(f, "{}", lit), + TokenKind::RegularExpressionLiteral(ref body, ref flags) => { write!(f, "/{}/{}", body, flags) } - TokenData::Comment(ref comm) => write!(f, "/*{}*/", comm), + TokenKind::LineTerminator => write!(f, "line terminator"), } } } diff --git a/boa/src/syntax/lexer/mod.rs b/boa/src/syntax/lexer/mod.rs index f74a0866f70..faeb02930aa 100644 --- a/boa/src/syntax/lexer/mod.rs +++ b/boa/src/syntax/lexer/mod.rs @@ -8,7 +8,7 @@ mod tests; use crate::syntax::ast::{ punc::Punctuator, - token::{Token, TokenData}, + token::{Token, TokenKind}, }; use std::{ char::{decode_utf16, from_u32}, @@ -84,7 +84,7 @@ impl LexerError { } impl fmt::Display for LexerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.details) } } @@ -136,14 +136,14 @@ impl<'a> Lexer<'a> { } } /// Push tokens onto the token queue - fn push_token(&mut self, tk: TokenData) { + fn push_token(&mut self, tk: TokenKind) { self.tokens .push(Token::new(tk, self.line_number, self.column_number)) } /// Push a punctuation token fn push_punc(&mut self, punc: Punctuator) { - self.push_token(TokenData::Punctuator(punc)); + self.push_token(TokenKind::Punctuator(punc)); } /// next fetches the next token and return it, or a LexerError if there are no more. @@ -154,23 +154,6 @@ impl<'a> Lexer<'a> { } } - /// read_line attempts to read until the end of the line and returns the String object or a LexerError - fn read_line(&mut self) -> Result { - let mut buf = String::new(); - while self.preview_next().is_some() { - let ch = self.next(); - match ch { - _ if ch.is_ascii_control() => { - break; - } - _ => { - buf.push(ch); - } - } - } - Ok(buf) - } - /// Preview the next character but don't actually increment fn preview_next(&mut self) -> Option { self.buffer.peek().copied() @@ -368,7 +351,7 @@ impl<'a> Lexer<'a> { } } let str_length = buf.len() as u64; - self.push_token(TokenData::StringLiteral(buf)); + self.push_token(TokenKind::StringLiteral(buf)); // Why +1? Quotation marks are not included, // So technically it would be +2, (for both " ") but we want to be 1 less // to compensate for the incrementing at the top @@ -379,7 +362,7 @@ impl<'a> Lexer<'a> { let num = match self.preview_next() { None => { - self.push_token(TokenData::NumericLiteral(0 as f64)); + self.push_token(TokenKind::NumericLiteral(0_f64)); return Ok(()); } Some('x') | Some('X') => { @@ -423,7 +406,7 @@ impl<'a> Lexer<'a> { } }; - self.push_token(TokenData::NumericLiteral(num)); + self.push_token(TokenKind::NumericLiteral(num)); //11.8.3 if let Err(e) = self.check_after_numeric_literal() { @@ -481,7 +464,7 @@ impl<'a> Lexer<'a> { } } // TODO make this a bit more safe -------------------------------VVVV - self.push_token(TokenData::NumericLiteral( + self.push_token(TokenKind::NumericLiteral( f64::from_str(&buf).map_err(|_| LexerError::new("Could not convert value to f64"))?, )) } @@ -497,14 +480,14 @@ impl<'a> Lexer<'a> { // Match won't compare &String to &str so i need to convert it :( let buf_compare: &str = &buf; self.push_token(match buf_compare { - "true" => TokenData::BooleanLiteral(true), - "false" => TokenData::BooleanLiteral(false), - "null" => TokenData::NullLiteral, + "true" => TokenKind::BooleanLiteral(true), + "false" => TokenKind::BooleanLiteral(false), + "null" => TokenKind::NullLiteral, slice => { if let Ok(keyword) = FromStr::from_str(slice) { - TokenData::Keyword(keyword) + TokenKind::Keyword(keyword) } else { - TokenData::Identifier(buf.clone()) + TokenKind::Identifier(buf.clone()) } } }); @@ -540,14 +523,16 @@ impl<'a> Lexer<'a> { match ch { // line comment '/' => { - let comment = "/".to_owned() + &self.read_line()?; - self.push_token(TokenData::Comment(comment)); + while self.preview_next().is_some() { + if self.next() == '\n' { + break; + } + } self.line_number += 1; self.column_number = 0; } // block comment '*' => { - let mut buf = "/".to_owned(); let mut lines = 0; loop { if self.preview_next().is_none() { @@ -555,9 +540,7 @@ impl<'a> Lexer<'a> { } match self.next() { '*' => { - buf.push('*'); if self.next_is('/') { - buf.push('/'); break; } } @@ -565,11 +548,9 @@ impl<'a> Lexer<'a> { if next_ch == '\n' { lines += 1; } - buf.push(next_ch) }, } } - self.push_token(TokenData::Comment(buf)); self.line_number += lines; self.column_number = 0; } @@ -609,7 +590,7 @@ impl<'a> Lexer<'a> { if regex { // body was parsed, now look for flags let flags = self.take_char_while(char::is_alphabetic)?; - self.push_token(TokenData::RegularExpressionLiteral( + self.push_token(TokenKind::RegularExpressionLiteral( body, flags, )); } else { @@ -617,11 +598,11 @@ impl<'a> Lexer<'a> { // parse either div or assigndiv self.buffer = original_buffer; if self.next_is('=') { - self.push_token(TokenData::Punctuator( + self.push_token(TokenKind::Punctuator( Punctuator::AssignDiv, )); } else { - self.push_token(TokenData::Punctuator(Punctuator::Div)); + self.push_token(TokenKind::Punctuator(Punctuator::Div)); } } } @@ -631,7 +612,7 @@ impl<'a> Lexer<'a> { } } '*' => op!(self, Punctuator::AssignMul, Punctuator::Mul, { - '*' => vop!(self, Punctuator::AssignPow, Punctuator::Pow) + '*' => vop!(self, Punctuator::AssignPow, Punctuator::Exp) }), '+' => op!(self, Punctuator::AssignAdd, Punctuator::Add, { '+' => Punctuator::Inc @@ -673,6 +654,7 @@ impl<'a> Lexer<'a> { ), '~' => self.push_punc(Punctuator::Neg), '\n' | '\u{2028}' | '\u{2029}' => { + self.push_token(TokenKind::LineTerminator); self.line_number += 1; self.column_number = 0; } diff --git a/boa/src/syntax/lexer/tests.rs b/boa/src/syntax/lexer/tests.rs index e41bff985c8..e21e443ae46 100644 --- a/boa/src/syntax/lexer/tests.rs +++ b/boa/src/syntax/lexer/tests.rs @@ -6,12 +6,12 @@ use crate::syntax::ast::keyword::Keyword; #[test] fn check_single_line_comment() { - let s1 = "var \n//=\nx"; + let s1 = "var \n//This is a comment\ntrue"; let mut lexer = Lexer::new(s1); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[0].data, TokenData::Keyword(Keyword::Var)); - assert_eq!(lexer.tokens[1].data, TokenData::Comment("//=".to_owned())); - assert_eq!(lexer.tokens[2].data, TokenData::Identifier("x".to_string())); + assert_eq!(lexer.tokens[0].kind, TokenKind::Keyword(Keyword::Var)); + assert_eq!(lexer.tokens[1].kind, TokenKind::LineTerminator); + assert_eq!(lexer.tokens[2].kind, TokenKind::BooleanLiteral(true)); } #[test] @@ -19,12 +19,8 @@ fn check_multi_line_comment() { let s = "var /* await \n break \n*/ x"; let mut lexer = Lexer::new(s); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[0].data, TokenData::Keyword(Keyword::Var)); - assert_eq!( - lexer.tokens[1].data, - TokenData::Comment("/* await \n break \n*/".to_owned()) - ); - assert_eq!(lexer.tokens[2].data, TokenData::Identifier("x".to_string())); + assert_eq!(lexer.tokens[0].kind, TokenKind::Keyword(Keyword::Var)); + assert_eq!(lexer.tokens[1].kind, TokenKind::Identifier("x".to_string())); } #[test] @@ -33,13 +29,13 @@ fn check_string() { let mut lexer = Lexer::new(s); lexer.lex().expect("failed to lex"); assert_eq!( - lexer.tokens[0].data, - TokenData::StringLiteral("aaa".to_string()) + lexer.tokens[0].kind, + TokenKind::StringLiteral("aaa".to_string()) ); assert_eq!( - lexer.tokens[1].data, - TokenData::StringLiteral("bbb".to_string()) + lexer.tokens[1].kind, + TokenKind::StringLiteral("bbb".to_string()) ); } @@ -47,196 +43,196 @@ fn check_string() { fn check_punctuators() { // https://tc39.es/ecma262/#sec-punctuators let s = "{ ( ) [ ] . ... ; , < > <= >= == != === !== \ - + - * % -- << >> >>> & | ^ ! ~ && || ? : \ - = += -= *= &= **= ++ ** <<= >>= >>>= &= |= ^= =>"; + + - * % -- << >> >>> & | ^ ! ~ && || ? : \ + = += -= *= &= **= ++ ** <<= >>= >>>= &= |= ^= =>"; let mut lexer = Lexer::new(s); lexer.lex().expect("failed to lex"); assert_eq!( - lexer.tokens[0].data, - TokenData::Punctuator(Punctuator::OpenBlock) + lexer.tokens[0].kind, + TokenKind::Punctuator(Punctuator::OpenBlock) ); assert_eq!( - lexer.tokens[1].data, - TokenData::Punctuator(Punctuator::OpenParen) + lexer.tokens[1].kind, + TokenKind::Punctuator(Punctuator::OpenParen) ); assert_eq!( - lexer.tokens[2].data, - TokenData::Punctuator(Punctuator::CloseParen) + lexer.tokens[2].kind, + TokenKind::Punctuator(Punctuator::CloseParen) ); assert_eq!( - lexer.tokens[3].data, - TokenData::Punctuator(Punctuator::OpenBracket) + lexer.tokens[3].kind, + TokenKind::Punctuator(Punctuator::OpenBracket) ); assert_eq!( - lexer.tokens[4].data, - TokenData::Punctuator(Punctuator::CloseBracket) + lexer.tokens[4].kind, + TokenKind::Punctuator(Punctuator::CloseBracket) ); - assert_eq!(lexer.tokens[5].data, TokenData::Punctuator(Punctuator::Dot)); + assert_eq!(lexer.tokens[5].kind, TokenKind::Punctuator(Punctuator::Dot)); assert_eq!( - lexer.tokens[6].data, - TokenData::Punctuator(Punctuator::Spread) + lexer.tokens[6].kind, + TokenKind::Punctuator(Punctuator::Spread) ); assert_eq!( - lexer.tokens[7].data, - TokenData::Punctuator(Punctuator::Semicolon) + lexer.tokens[7].kind, + TokenKind::Punctuator(Punctuator::Semicolon) ); assert_eq!( - lexer.tokens[8].data, - TokenData::Punctuator(Punctuator::Comma) + lexer.tokens[8].kind, + TokenKind::Punctuator(Punctuator::Comma) ); assert_eq!( - lexer.tokens[9].data, - TokenData::Punctuator(Punctuator::LessThan) + lexer.tokens[9].kind, + TokenKind::Punctuator(Punctuator::LessThan) ); assert_eq!( - lexer.tokens[10].data, - TokenData::Punctuator(Punctuator::GreaterThan) + lexer.tokens[10].kind, + TokenKind::Punctuator(Punctuator::GreaterThan) ); assert_eq!( - lexer.tokens[11].data, - TokenData::Punctuator(Punctuator::LessThanOrEq) + lexer.tokens[11].kind, + TokenKind::Punctuator(Punctuator::LessThanOrEq) ); assert_eq!( - lexer.tokens[12].data, - TokenData::Punctuator(Punctuator::GreaterThanOrEq) + lexer.tokens[12].kind, + TokenKind::Punctuator(Punctuator::GreaterThanOrEq) ); - assert_eq!(lexer.tokens[13].data, TokenData::Punctuator(Punctuator::Eq)); + assert_eq!(lexer.tokens[13].kind, TokenKind::Punctuator(Punctuator::Eq)); assert_eq!( - lexer.tokens[14].data, - TokenData::Punctuator(Punctuator::NotEq) + lexer.tokens[14].kind, + TokenKind::Punctuator(Punctuator::NotEq) ); assert_eq!( - lexer.tokens[15].data, - TokenData::Punctuator(Punctuator::StrictEq) + lexer.tokens[15].kind, + TokenKind::Punctuator(Punctuator::StrictEq) ); assert_eq!( - lexer.tokens[16].data, - TokenData::Punctuator(Punctuator::StrictNotEq) + lexer.tokens[16].kind, + TokenKind::Punctuator(Punctuator::StrictNotEq) ); assert_eq!( - lexer.tokens[17].data, - TokenData::Punctuator(Punctuator::Add) + lexer.tokens[17].kind, + TokenKind::Punctuator(Punctuator::Add) ); assert_eq!( - lexer.tokens[18].data, - TokenData::Punctuator(Punctuator::Sub) + lexer.tokens[18].kind, + TokenKind::Punctuator(Punctuator::Sub) ); assert_eq!( - lexer.tokens[19].data, - TokenData::Punctuator(Punctuator::Mul) + lexer.tokens[19].kind, + TokenKind::Punctuator(Punctuator::Mul) ); assert_eq!( - lexer.tokens[20].data, - TokenData::Punctuator(Punctuator::Mod) + lexer.tokens[20].kind, + TokenKind::Punctuator(Punctuator::Mod) ); assert_eq!( - lexer.tokens[21].data, - TokenData::Punctuator(Punctuator::Dec) + lexer.tokens[21].kind, + TokenKind::Punctuator(Punctuator::Dec) ); assert_eq!( - lexer.tokens[22].data, - TokenData::Punctuator(Punctuator::LeftSh) + lexer.tokens[22].kind, + TokenKind::Punctuator(Punctuator::LeftSh) ); assert_eq!( - lexer.tokens[23].data, - TokenData::Punctuator(Punctuator::RightSh) + lexer.tokens[23].kind, + TokenKind::Punctuator(Punctuator::RightSh) ); assert_eq!( - lexer.tokens[24].data, - TokenData::Punctuator(Punctuator::URightSh) + lexer.tokens[24].kind, + TokenKind::Punctuator(Punctuator::URightSh) ); assert_eq!( - lexer.tokens[25].data, - TokenData::Punctuator(Punctuator::And) + lexer.tokens[25].kind, + TokenKind::Punctuator(Punctuator::And) ); - assert_eq!(lexer.tokens[26].data, TokenData::Punctuator(Punctuator::Or)); + assert_eq!(lexer.tokens[26].kind, TokenKind::Punctuator(Punctuator::Or)); assert_eq!( - lexer.tokens[27].data, - TokenData::Punctuator(Punctuator::Xor) + lexer.tokens[27].kind, + TokenKind::Punctuator(Punctuator::Xor) ); assert_eq!( - lexer.tokens[28].data, - TokenData::Punctuator(Punctuator::Not) + lexer.tokens[28].kind, + TokenKind::Punctuator(Punctuator::Not) ); assert_eq!( - lexer.tokens[29].data, - TokenData::Punctuator(Punctuator::Neg) + lexer.tokens[29].kind, + TokenKind::Punctuator(Punctuator::Neg) ); assert_eq!( - lexer.tokens[30].data, - TokenData::Punctuator(Punctuator::BoolAnd) + lexer.tokens[30].kind, + TokenKind::Punctuator(Punctuator::BoolAnd) ); assert_eq!( - lexer.tokens[31].data, - TokenData::Punctuator(Punctuator::BoolOr) + lexer.tokens[31].kind, + TokenKind::Punctuator(Punctuator::BoolOr) ); assert_eq!( - lexer.tokens[32].data, - TokenData::Punctuator(Punctuator::Question) + lexer.tokens[32].kind, + TokenKind::Punctuator(Punctuator::Question) ); assert_eq!( - lexer.tokens[33].data, - TokenData::Punctuator(Punctuator::Colon) + lexer.tokens[33].kind, + TokenKind::Punctuator(Punctuator::Colon) ); assert_eq!( - lexer.tokens[34].data, - TokenData::Punctuator(Punctuator::Assign) + lexer.tokens[34].kind, + TokenKind::Punctuator(Punctuator::Assign) ); assert_eq!( - lexer.tokens[35].data, - TokenData::Punctuator(Punctuator::AssignAdd) + lexer.tokens[35].kind, + TokenKind::Punctuator(Punctuator::AssignAdd) ); assert_eq!( - lexer.tokens[36].data, - TokenData::Punctuator(Punctuator::AssignSub) + lexer.tokens[36].kind, + TokenKind::Punctuator(Punctuator::AssignSub) ); assert_eq!( - lexer.tokens[37].data, - TokenData::Punctuator(Punctuator::AssignMul) + lexer.tokens[37].kind, + TokenKind::Punctuator(Punctuator::AssignMul) ); assert_eq!( - lexer.tokens[38].data, - TokenData::Punctuator(Punctuator::AssignAnd) + lexer.tokens[38].kind, + TokenKind::Punctuator(Punctuator::AssignAnd) ); assert_eq!( - lexer.tokens[39].data, - TokenData::Punctuator(Punctuator::AssignPow) + lexer.tokens[39].kind, + TokenKind::Punctuator(Punctuator::AssignPow) ); assert_eq!( - lexer.tokens[40].data, - TokenData::Punctuator(Punctuator::Inc) + lexer.tokens[40].kind, + TokenKind::Punctuator(Punctuator::Inc) ); assert_eq!( - lexer.tokens[41].data, - TokenData::Punctuator(Punctuator::Pow) + lexer.tokens[41].kind, + TokenKind::Punctuator(Punctuator::Exp) ); assert_eq!( - lexer.tokens[42].data, - TokenData::Punctuator(Punctuator::AssignLeftSh) + lexer.tokens[42].kind, + TokenKind::Punctuator(Punctuator::AssignLeftSh) ); assert_eq!( - lexer.tokens[43].data, - TokenData::Punctuator(Punctuator::AssignRightSh) + lexer.tokens[43].kind, + TokenKind::Punctuator(Punctuator::AssignRightSh) ); assert_eq!( - lexer.tokens[44].data, - TokenData::Punctuator(Punctuator::AssignURightSh) + lexer.tokens[44].kind, + TokenKind::Punctuator(Punctuator::AssignURightSh) ); assert_eq!( - lexer.tokens[45].data, - TokenData::Punctuator(Punctuator::AssignAnd) + lexer.tokens[45].kind, + TokenKind::Punctuator(Punctuator::AssignAnd) ); assert_eq!( - lexer.tokens[46].data, - TokenData::Punctuator(Punctuator::AssignOr) + lexer.tokens[46].kind, + TokenKind::Punctuator(Punctuator::AssignOr) ); assert_eq!( - lexer.tokens[47].data, - TokenData::Punctuator(Punctuator::AssignXor) + lexer.tokens[47].kind, + TokenKind::Punctuator(Punctuator::AssignXor) ); assert_eq!( - lexer.tokens[48].data, - TokenData::Punctuator(Punctuator::Arrow) + lexer.tokens[48].kind, + TokenKind::Punctuator(Punctuator::Arrow) ); } @@ -244,48 +240,48 @@ fn check_punctuators() { fn check_keywords() { // https://tc39.es/ecma262/#sec-keywords let s = "await break case catch class const continue debugger default delete \ - do else export extends finally for function if import in instanceof \ - new return super switch this throw try typeof var void while with yield"; + do else export extends finally for function if import in instanceof \ + new return super switch this throw try typeof var void while with yield"; let mut lexer = Lexer::new(s); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[0].data, TokenData::Keyword(Keyword::Await)); - assert_eq!(lexer.tokens[1].data, TokenData::Keyword(Keyword::Break)); - assert_eq!(lexer.tokens[2].data, TokenData::Keyword(Keyword::Case)); - assert_eq!(lexer.tokens[3].data, TokenData::Keyword(Keyword::Catch)); - assert_eq!(lexer.tokens[4].data, TokenData::Keyword(Keyword::Class)); - assert_eq!(lexer.tokens[5].data, TokenData::Keyword(Keyword::Const)); - assert_eq!(lexer.tokens[6].data, TokenData::Keyword(Keyword::Continue)); - assert_eq!(lexer.tokens[7].data, TokenData::Keyword(Keyword::Debugger)); - assert_eq!(lexer.tokens[8].data, TokenData::Keyword(Keyword::Default)); - assert_eq!(lexer.tokens[9].data, TokenData::Keyword(Keyword::Delete)); - assert_eq!(lexer.tokens[10].data, TokenData::Keyword(Keyword::Do)); - assert_eq!(lexer.tokens[11].data, TokenData::Keyword(Keyword::Else)); - assert_eq!(lexer.tokens[12].data, TokenData::Keyword(Keyword::Export)); - assert_eq!(lexer.tokens[13].data, TokenData::Keyword(Keyword::Extends)); - assert_eq!(lexer.tokens[14].data, TokenData::Keyword(Keyword::Finally)); - assert_eq!(lexer.tokens[15].data, TokenData::Keyword(Keyword::For)); - assert_eq!(lexer.tokens[16].data, TokenData::Keyword(Keyword::Function)); - assert_eq!(lexer.tokens[17].data, TokenData::Keyword(Keyword::If)); - assert_eq!(lexer.tokens[18].data, TokenData::Keyword(Keyword::Import)); - assert_eq!(lexer.tokens[19].data, TokenData::Keyword(Keyword::In)); - assert_eq!( - lexer.tokens[20].data, - TokenData::Keyword(Keyword::InstanceOf) - ); - assert_eq!(lexer.tokens[21].data, TokenData::Keyword(Keyword::New)); - assert_eq!(lexer.tokens[22].data, TokenData::Keyword(Keyword::Return)); - assert_eq!(lexer.tokens[23].data, TokenData::Keyword(Keyword::Super)); - assert_eq!(lexer.tokens[24].data, TokenData::Keyword(Keyword::Switch)); - assert_eq!(lexer.tokens[25].data, TokenData::Keyword(Keyword::This)); - assert_eq!(lexer.tokens[26].data, TokenData::Keyword(Keyword::Throw)); - assert_eq!(lexer.tokens[27].data, TokenData::Keyword(Keyword::Try)); - assert_eq!(lexer.tokens[28].data, TokenData::Keyword(Keyword::TypeOf)); - assert_eq!(lexer.tokens[29].data, TokenData::Keyword(Keyword::Var)); - assert_eq!(lexer.tokens[30].data, TokenData::Keyword(Keyword::Void)); - assert_eq!(lexer.tokens[31].data, TokenData::Keyword(Keyword::While)); - assert_eq!(lexer.tokens[32].data, TokenData::Keyword(Keyword::With)); - assert_eq!(lexer.tokens[33].data, TokenData::Keyword(Keyword::Yield)); + assert_eq!(lexer.tokens[0].kind, TokenKind::Keyword(Keyword::Await)); + assert_eq!(lexer.tokens[1].kind, TokenKind::Keyword(Keyword::Break)); + assert_eq!(lexer.tokens[2].kind, TokenKind::Keyword(Keyword::Case)); + assert_eq!(lexer.tokens[3].kind, TokenKind::Keyword(Keyword::Catch)); + assert_eq!(lexer.tokens[4].kind, TokenKind::Keyword(Keyword::Class)); + assert_eq!(lexer.tokens[5].kind, TokenKind::Keyword(Keyword::Const)); + assert_eq!(lexer.tokens[6].kind, TokenKind::Keyword(Keyword::Continue)); + assert_eq!(lexer.tokens[7].kind, TokenKind::Keyword(Keyword::Debugger)); + assert_eq!(lexer.tokens[8].kind, TokenKind::Keyword(Keyword::Default)); + assert_eq!(lexer.tokens[9].kind, TokenKind::Keyword(Keyword::Delete)); + assert_eq!(lexer.tokens[10].kind, TokenKind::Keyword(Keyword::Do)); + assert_eq!(lexer.tokens[11].kind, TokenKind::Keyword(Keyword::Else)); + assert_eq!(lexer.tokens[12].kind, TokenKind::Keyword(Keyword::Export)); + assert_eq!(lexer.tokens[13].kind, TokenKind::Keyword(Keyword::Extends)); + assert_eq!(lexer.tokens[14].kind, TokenKind::Keyword(Keyword::Finally)); + assert_eq!(lexer.tokens[15].kind, TokenKind::Keyword(Keyword::For)); + assert_eq!(lexer.tokens[16].kind, TokenKind::Keyword(Keyword::Function)); + assert_eq!(lexer.tokens[17].kind, TokenKind::Keyword(Keyword::If)); + assert_eq!(lexer.tokens[18].kind, TokenKind::Keyword(Keyword::Import)); + assert_eq!(lexer.tokens[19].kind, TokenKind::Keyword(Keyword::In)); + assert_eq!( + lexer.tokens[20].kind, + TokenKind::Keyword(Keyword::InstanceOf) + ); + assert_eq!(lexer.tokens[21].kind, TokenKind::Keyword(Keyword::New)); + assert_eq!(lexer.tokens[22].kind, TokenKind::Keyword(Keyword::Return)); + assert_eq!(lexer.tokens[23].kind, TokenKind::Keyword(Keyword::Super)); + assert_eq!(lexer.tokens[24].kind, TokenKind::Keyword(Keyword::Switch)); + assert_eq!(lexer.tokens[25].kind, TokenKind::Keyword(Keyword::This)); + assert_eq!(lexer.tokens[26].kind, TokenKind::Keyword(Keyword::Throw)); + assert_eq!(lexer.tokens[27].kind, TokenKind::Keyword(Keyword::Try)); + assert_eq!(lexer.tokens[28].kind, TokenKind::Keyword(Keyword::TypeOf)); + assert_eq!(lexer.tokens[29].kind, TokenKind::Keyword(Keyword::Var)); + assert_eq!(lexer.tokens[30].kind, TokenKind::Keyword(Keyword::Void)); + assert_eq!(lexer.tokens[31].kind, TokenKind::Keyword(Keyword::While)); + assert_eq!(lexer.tokens[32].kind, TokenKind::Keyword(Keyword::With)); + assert_eq!(lexer.tokens[33].kind, TokenKind::Keyword(Keyword::Yield)); } #[test] @@ -293,15 +289,15 @@ fn check_variable_definition_tokens() { let s = "let a = 'hello';"; let mut lexer = Lexer::new(s); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[0].data, TokenData::Keyword(Keyword::Let)); - assert_eq!(lexer.tokens[1].data, TokenData::Identifier("a".to_string())); + assert_eq!(lexer.tokens[0].kind, TokenKind::Keyword(Keyword::Let)); + assert_eq!(lexer.tokens[1].kind, TokenKind::Identifier("a".to_string())); assert_eq!( - lexer.tokens[2].data, - TokenData::Punctuator(Punctuator::Assign) + lexer.tokens[2].kind, + TokenKind::Punctuator(Punctuator::Assign) ); assert_eq!( - lexer.tokens[3].data, - TokenData::StringLiteral("hello".to_string()) + lexer.tokens[3].kind, + TokenKind::StringLiteral("hello".to_string()) ); } @@ -332,37 +328,26 @@ fn check_positions() { // Semi Colon token starts on column 27 assert_eq!(lexer.tokens[6].pos.column_number, 27); assert_eq!(lexer.tokens[6].pos.line_number, 1); - // Comment start on column 29 - // Semi Colon token starts on column 27 - assert_eq!(lexer.tokens[7].pos.column_number, 29); - assert_eq!(lexer.tokens[7].pos.line_number, 1); } #[test] fn check_line_numbers() { - let s = "// Copyright (C) 2017 Ecma International. All rights reserved.\n\ - // This code is governed by the BSD license found in the LICENSE file.\n\ - /*---\n\ - description: |\n \ - Collection of assertion functions used throughout test262\n\ - defines: [assert]\n\ - ---*/\n\n\n\ - function assert(mustBeTrue, message) {"; + let s = "x\ny\n"; let mut lexer = Lexer::new(s); lexer.lex().expect("failed to lex"); - // The first column is 1 (not zero indexed), first line is also 1 + assert_eq!(lexer.tokens[0].pos.column_number, 1); assert_eq!(lexer.tokens[0].pos.line_number, 1); - // Second comment starts on line 2 - assert_eq!(lexer.tokens[1].pos.column_number, 1); - assert_eq!(lexer.tokens[1].pos.line_number, 2); - // Multiline comment starts on line 3 + + assert_eq!(lexer.tokens[1].pos.column_number, 2); + assert_eq!(lexer.tokens[1].pos.line_number, 1); + assert_eq!(lexer.tokens[2].pos.column_number, 1); - assert_eq!(lexer.tokens[2].pos.line_number, 3); - // Function Token is on line 10 - assert_eq!(lexer.tokens[3].pos.column_number, 1); - assert_eq!(lexer.tokens[3].pos.line_number, 10); + assert_eq!(lexer.tokens[2].pos.line_number, 2); + + assert_eq!(lexer.tokens[3].pos.column_number, 2); + assert_eq!(lexer.tokens[3].pos.line_number, 2); } // Increment/Decrement @@ -372,12 +357,12 @@ fn check_decrement_advances_lexer_2_places() { let s = "let a = b--;"; let mut lexer = Lexer::new(s); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[4].data, TokenData::Punctuator(Punctuator::Dec)); + assert_eq!(lexer.tokens[4].kind, TokenKind::Punctuator(Punctuator::Dec)); // Decrementing means adding 2 characters '--', the lexer should consume it as a single token // and move the curser forward by 2, meaning the next token should be a semicolon assert_eq!( - lexer.tokens[5].data, - TokenData::Punctuator(Punctuator::Semicolon) + lexer.tokens[5].kind, + TokenKind::Punctuator(Punctuator::Semicolon) ); } @@ -388,24 +373,24 @@ fn numbers() { ); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[0].data, TokenData::NumericLiteral(1.0)); - assert_eq!(lexer.tokens[1].data, TokenData::NumericLiteral(2.0)); - assert_eq!(lexer.tokens[2].data, TokenData::NumericLiteral(52.0)); - assert_eq!(lexer.tokens[3].data, TokenData::NumericLiteral(46.0)); - assert_eq!(lexer.tokens[4].data, TokenData::NumericLiteral(7.89)); - assert_eq!(lexer.tokens[5].data, TokenData::NumericLiteral(42.0)); - assert_eq!(lexer.tokens[6].data, TokenData::NumericLiteral(5000.0)); - assert_eq!(lexer.tokens[7].data, TokenData::NumericLiteral(5000.0)); - assert_eq!(lexer.tokens[8].data, TokenData::NumericLiteral(0.005)); - assert_eq!(lexer.tokens[9].data, TokenData::NumericLiteral(2.0)); - assert_eq!(lexer.tokens[10].data, TokenData::NumericLiteral(83.0)); - assert_eq!(lexer.tokens[11].data, TokenData::NumericLiteral(999.0)); - assert_eq!(lexer.tokens[12].data, TokenData::NumericLiteral(10.0)); - assert_eq!(lexer.tokens[13].data, TokenData::NumericLiteral(0.1)); - assert_eq!(lexer.tokens[14].data, TokenData::NumericLiteral(10.0)); - assert_eq!(lexer.tokens[15].data, TokenData::NumericLiteral(10.0)); - assert_eq!(lexer.tokens[16].data, TokenData::NumericLiteral(0.0)); - assert_eq!(lexer.tokens[17].data, TokenData::NumericLiteral(0.12)); + assert_eq!(lexer.tokens[0].kind, TokenKind::NumericLiteral(1.0)); + assert_eq!(lexer.tokens[1].kind, TokenKind::NumericLiteral(2.0)); + assert_eq!(lexer.tokens[2].kind, TokenKind::NumericLiteral(52.0)); + assert_eq!(lexer.tokens[3].kind, TokenKind::NumericLiteral(46.0)); + assert_eq!(lexer.tokens[4].kind, TokenKind::NumericLiteral(7.89)); + assert_eq!(lexer.tokens[5].kind, TokenKind::NumericLiteral(42.0)); + assert_eq!(lexer.tokens[6].kind, TokenKind::NumericLiteral(5000.0)); + assert_eq!(lexer.tokens[7].kind, TokenKind::NumericLiteral(5000.0)); + assert_eq!(lexer.tokens[8].kind, TokenKind::NumericLiteral(0.005)); + assert_eq!(lexer.tokens[9].kind, TokenKind::NumericLiteral(2.0)); + assert_eq!(lexer.tokens[10].kind, TokenKind::NumericLiteral(83.0)); + assert_eq!(lexer.tokens[11].kind, TokenKind::NumericLiteral(999.0)); + assert_eq!(lexer.tokens[12].kind, TokenKind::NumericLiteral(10.0)); + assert_eq!(lexer.tokens[13].kind, TokenKind::NumericLiteral(0.1)); + assert_eq!(lexer.tokens[14].kind, TokenKind::NumericLiteral(10.0)); + assert_eq!(lexer.tokens[15].kind, TokenKind::NumericLiteral(10.0)); + assert_eq!(lexer.tokens[16].kind, TokenKind::NumericLiteral(0.0)); + assert_eq!(lexer.tokens[17].kind, TokenKind::NumericLiteral(0.12)); } #[test] @@ -418,8 +403,8 @@ fn test_single_number_without_semicolon() { fn test_number_followed_by_dot() { let mut lexer = Lexer::new("1.."); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[0].data, TokenData::NumericLiteral(1.0)); - assert_eq!(lexer.tokens[1].data, TokenData::Punctuator(Punctuator::Dot)); + assert_eq!(lexer.tokens[0].kind, TokenKind::NumericLiteral(1.0)); + assert_eq!(lexer.tokens[1].kind, TokenKind::Punctuator(Punctuator::Dot)); } #[test] @@ -427,8 +412,8 @@ fn test_regex_literal() { let mut lexer = Lexer::new("/(?:)/"); lexer.lex().expect("failed to lex"); assert_eq!( - lexer.tokens[0].data, - TokenData::RegularExpressionLiteral("(?:)".to_string(), "".to_string()) + lexer.tokens[0].kind, + TokenKind::RegularExpressionLiteral("(?:)".to_string(), "".to_string()) ); } @@ -437,8 +422,8 @@ fn test_regex_literal_flags() { let mut lexer = Lexer::new(r"/\/[^\/]*\/*/gmi"); lexer.lex().expect("failed to lex"); assert_eq!( - lexer.tokens[0].data, - TokenData::RegularExpressionLiteral("\\/[^\\/]*\\/*".to_string(), "gmi".to_string()) + lexer.tokens[0].kind, + TokenKind::RegularExpressionLiteral("\\/[^\\/]*\\/*".to_string(), "gmi".to_string()) ); } @@ -446,55 +431,55 @@ fn test_regex_literal_flags() { fn test_addition_no_spaces() { let mut lexer = Lexer::new("1+1"); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[0].data, TokenData::NumericLiteral(1.0)); - assert_eq!(lexer.tokens[1].data, TokenData::Punctuator(Punctuator::Add)); - assert_eq!(lexer.tokens[2].data, TokenData::NumericLiteral(1.0)); + assert_eq!(lexer.tokens[0].kind, TokenKind::NumericLiteral(1.0)); + assert_eq!(lexer.tokens[1].kind, TokenKind::Punctuator(Punctuator::Add)); + assert_eq!(lexer.tokens[2].kind, TokenKind::NumericLiteral(1.0)); } #[test] fn test_addition_no_spaces_left_side() { let mut lexer = Lexer::new("1+ 1"); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[0].data, TokenData::NumericLiteral(1.0)); - assert_eq!(lexer.tokens[1].data, TokenData::Punctuator(Punctuator::Add)); - assert_eq!(lexer.tokens[2].data, TokenData::NumericLiteral(1.0)); + assert_eq!(lexer.tokens[0].kind, TokenKind::NumericLiteral(1.0)); + assert_eq!(lexer.tokens[1].kind, TokenKind::Punctuator(Punctuator::Add)); + assert_eq!(lexer.tokens[2].kind, TokenKind::NumericLiteral(1.0)); } #[test] fn test_addition_no_spaces_right_side() { let mut lexer = Lexer::new("1 +1"); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[0].data, TokenData::NumericLiteral(1.0)); - assert_eq!(lexer.tokens[1].data, TokenData::Punctuator(Punctuator::Add)); - assert_eq!(lexer.tokens[2].data, TokenData::NumericLiteral(1.0)); + assert_eq!(lexer.tokens[0].kind, TokenKind::NumericLiteral(1.0)); + assert_eq!(lexer.tokens[1].kind, TokenKind::Punctuator(Punctuator::Add)); + assert_eq!(lexer.tokens[2].kind, TokenKind::NumericLiteral(1.0)); } #[test] fn test_addition_no_spaces_e_number_left_side() { let mut lexer = Lexer::new("1e2+ 1"); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[0].data, TokenData::NumericLiteral(100.0)); - assert_eq!(lexer.tokens[1].data, TokenData::Punctuator(Punctuator::Add)); - assert_eq!(lexer.tokens[2].data, TokenData::NumericLiteral(1.0)); + assert_eq!(lexer.tokens[0].kind, TokenKind::NumericLiteral(100.0)); + assert_eq!(lexer.tokens[1].kind, TokenKind::Punctuator(Punctuator::Add)); + assert_eq!(lexer.tokens[2].kind, TokenKind::NumericLiteral(1.0)); } #[test] fn test_addition_no_spaces_e_number_right_side() { let mut lexer = Lexer::new("1 +1e3"); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[0].data, TokenData::NumericLiteral(1.0)); - assert_eq!(lexer.tokens[1].data, TokenData::Punctuator(Punctuator::Add)); - assert_eq!(lexer.tokens[2].data, TokenData::NumericLiteral(1000.0)); + assert_eq!(lexer.tokens[0].kind, TokenKind::NumericLiteral(1.0)); + assert_eq!(lexer.tokens[1].kind, TokenKind::Punctuator(Punctuator::Add)); + assert_eq!(lexer.tokens[2].kind, TokenKind::NumericLiteral(1000.0)); } #[test] fn test_addition_no_spaces_e_number() { let mut lexer = Lexer::new("1e3+1e11"); lexer.lex().expect("failed to lex"); - assert_eq!(lexer.tokens[0].data, TokenData::NumericLiteral(1000.0)); - assert_eq!(lexer.tokens[1].data, TokenData::Punctuator(Punctuator::Add)); + assert_eq!(lexer.tokens[0].kind, TokenKind::NumericLiteral(1000.0)); + assert_eq!(lexer.tokens[1].kind, TokenKind::Punctuator(Punctuator::Add)); assert_eq!( - lexer.tokens[2].data, - TokenData::NumericLiteral(100_000_000_000.0) + lexer.tokens[2].kind, + TokenKind::NumericLiteral(100_000_000_000.0) ); } diff --git a/boa/src/syntax/parser/cursor.rs b/boa/src/syntax/parser/cursor.rs new file mode 100644 index 00000000000..4609c6aa999 --- /dev/null +++ b/boa/src/syntax/parser/cursor.rs @@ -0,0 +1,102 @@ +//! Cursor implementation for the parser. + +use crate::syntax::ast::token::Token; + +/// Token cursor. +/// +/// This internal structure gives basic testable operations to the parser. +#[derive(Debug, Clone, Default)] +pub(super) struct Cursor<'a> { + /// The tokens being input. + tokens: &'a [Token], + /// The current position within the tokens. + pos: usize, +} + +impl<'a> Cursor<'a> { + /// Creates a new cursor. + pub(super) fn new(tokens: &'a [Token]) -> Self { + Self { + tokens, + ..Self::default() + } + } + + /// Retrieves the current position of the cursor in the token stream. + pub(super) fn pos(&self) -> usize { + self.pos + } + + /// Moves the cursor to the given position. + /// This is intended to be used *always* with `Cursor::pos()`. + /// + pub(super) fn seek(&mut self, pos: usize) { + self.pos = pos + } + + /// Moves the cursor to the next token and returns the token. + pub(super) fn next(&mut self) -> Option<&'a Token> { + let token = self.tokens.get(self.pos); + + if self.pos != self.tokens.len() { + self.pos += 1; + } + + token + } + + /// Moves the cursor to the next token after skipping tokens based on the predicate. + pub(super) fn next_skip

(&mut self, mut skip: P) -> Option<&'a Token> + where + P: FnMut(&Token) -> bool, + { + while let Some(token) = self.tokens.get(self.pos) { + self.pos += 1; + + if !skip(token) { + return Some(token); + } + } + None + } + + /// Peeks the next token without moving the cursor. + pub(super) fn peek(&self, skip: usize) -> Option<&'a Token> { + self.tokens.get(self.pos + skip) + } + + /// Peeks the next token after skipping tokens based on the predicate. + pub(super) fn peek_skip

(&self, mut skip: P) -> Option<&'a Token> + where + P: FnMut(&Token) -> bool, + { + let mut current = self.pos; + while let Some(token) = self.tokens.get(current) { + if !skip(token) { + return Some(token); + } + current += 1; + } + + None + } + + /// Moves the cursor to the previous token and returns the token. + pub(super) fn back(&mut self) { + assert!( + self.pos > 0, + "cannot go back in a cursor that is at the beginning of the list of tokens" + ); + + self.pos -= 1; + } + + /// Peeks the previous token without moving the cursor. + pub(super) fn peek_prev(&self) -> Option<&'a Token> { + if self.pos == 0 { + None + } else { + self.tokens.get(self.pos - 1) + } + } +} diff --git a/boa/src/syntax/parser/mod.rs b/boa/src/syntax/parser/mod.rs index e28aa6cdf98..f06bcb41746 100644 --- a/boa/src/syntax/parser/mod.rs +++ b/boa/src/syntax/parser/mod.rs @@ -1,916 +1,1733 @@ +//! Boa parser implementation. + +mod cursor; #[cfg(test)] mod tests; use crate::syntax::ast::{ constant::Const, - expr::{Expr, ExprDef}, keyword::Keyword, - op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, Operator, UnaryOp}, + node::{FormalParameter, FormalParameters, MethodDefinitionKind, Node, PropertyDefinition}, + op::{AssignOp, BinOp, NumOp, UnaryOp}, + pos::Position, punc::Punctuator, - token::{Token, TokenData}, + token::{Token, TokenKind}, }; -use std::{collections::btree_map::BTreeMap, fmt}; +use cursor::Cursor; +use std::fmt; /// `ParseError` is an enum which represents errors encounted during parsing an expression #[derive(Debug, Clone)] pub enum ParseError { /// When it expected a certain kind of token, but got another as part of something - Expected(Vec, Token, &'static str), + Expected(Vec, Token, Option<&'static str>), /// When it expected a certain expression, but got another - ExpectedExpr(&'static str, Expr), + ExpectedExpr(&'static str, Node, Position), /// When it didn't expect this keyword - UnexpectedKeyword(Keyword), + UnexpectedKeyword(Keyword, Position), + /// When a token is unexpected + Unexpected(Token, Option<&'static str>), /// When there is an abrupt end to the parsing AbruptEnd, + /// Out of range error, attempting to set a position where there is no token + RangeError, + /// Catch all General Error + General(&'static str, Option), } impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ParseError::Expected(expected, actual, routine) => write!( f, - "Expected token '{}', got '{}' in routine '{}' at line {}, col {}", - expected - .first() - .map(|t| t.to_string()) - .unwrap_or_else(String::new), + "Expected {}, got '{}'{} at line {}, col {}", + if expected.len() == 1 { + format!( + "token '{}'", + expected.first().map(TokenKind::to_string).unwrap() + ) + } else { + format!( + "one of {}", + expected + .iter() + .enumerate() + .map(|(i, t)| { + format!( + "{}'{}'", + if i == 0 { + "" + } else if i == expected.len() - 1 { + " or " + } else { + ", " + }, + t + ) + }) + .collect::() + ) + }, actual, - routine, + if let Some(routine) = routine { + format!(" in {}", routine) + } else { + String::new() + }, actual.pos.line_number, actual.pos.column_number ), - ParseError::ExpectedExpr(expected, actual) => { - write!(f, "Expected expression '{}', got '{}'", expected, actual) - } - ParseError::UnexpectedKeyword(keyword) => write!(f, "Unexpected keyword: {}", keyword), + ParseError::ExpectedExpr(expected, actual, pos) => write!( + f, + "Expected expression '{}', got '{}' at line {}, col {}", + expected, actual, pos.line_number, pos.column_number + ), + ParseError::UnexpectedKeyword(keyword, pos) => write!( + f, + "Unexpected keyword: '{}' at line {}, col {}", + keyword, pos.line_number, pos.column_number + ), + ParseError::Unexpected(tok, msg) => write!( + f, + "Unexpected Token '{}'{} at line {}, col {}", + tok, + if let Some(m) = msg { + format!(", {}", m) + } else { + String::new() + }, + tok.pos.line_number, + tok.pos.column_number + ), ParseError::AbruptEnd => write!(f, "Abrupt End"), + ParseError::General(msg, pos) => write!( + f, + "{}{}", + msg, + if let Some(pos) = pos { + format!(" at line {}, col {}", pos.line_number, pos.column_number) + } else { + String::new() + } + ), + ParseError::RangeError => write!(f, "RangeError!"), } } } -pub type ParseResult = Result; +pub type ParseResult = Result; #[derive(Debug)] -pub struct Parser { - /// The tokens being input - tokens: Vec, - /// The current position within the tokens - pos: usize, +pub struct Parser<'a> { + /// Cursor in the parser, the internal structure used to read tokens. + cursor: Cursor<'a>, } -impl Parser { +macro_rules! expression { ( $name:ident, $lower:ident, [ $( $op:path ),* ] ) => { + fn $name (&mut self) -> ParseResult { + let mut lhs = self. $lower ()?; + while let Some(tok) = self.peek_skip_lineterminator() { + match tok.kind { + // Parse assign expression + TokenKind::Punctuator(ref op) if op == &Punctuator::Assign => { + let _ = self.next_skip_lineterminator().expect("token disappeared"); + lhs = Node::Assign( + Box::new(lhs), + Box::new(self. $lower ()?) + ) + } + TokenKind::Punctuator(ref op) if $( op == &$op )||* => { + let _ = self.next_skip_lineterminator().expect("token disappeared"); + lhs = Node::BinOp( + op.as_binop().unwrap(), + Box::new(lhs), + Box::new(self. $lower ()?) + ) + } + _ => break + } + } + Ok(lhs) + } +} } + +impl<'a> Parser<'a> { /// Create a new parser, using `tokens` as input - pub fn new(tokens: Vec) -> Self { - Self { tokens, pos: 0 } + pub fn new(tokens: &'a [Token]) -> Self { + Self { + cursor: Cursor::new(tokens), + } } /// Parse all expressions in the token array pub fn parse_all(&mut self) -> ParseResult { - let mut exprs = Vec::new(); - while self.pos < self.tokens.len() { - let result = self.parse()?; - exprs.push(result); + self.read_statement_list() + } + + /// consume the next token and increment position + fn get_next_token(&mut self) -> Result<&Token, ParseError> { + self.cursor.next().ok_or(ParseError::AbruptEnd) + } + + /// Peek the next token skipping line terminators. + pub fn peek_skip_lineterminator(&mut self) -> Option<&'a Token> { + self.cursor + .peek_skip(|tk| tk.kind == TokenKind::LineTerminator) + } + + /// Consume the next token skipping line terminators. + pub fn next_skip_lineterminator(&mut self) -> Option<&'a Token> { + self.cursor + .next_skip(|tk| tk.kind == TokenKind::LineTerminator) + } + + /// Advance the cursor to the next token and retrieve it, only if it's of `kind` type. + /// + /// When the next token is a `kind` token, get the token, otherwise return `None`. + fn next_if(&mut self, kind: TokenKind) -> Option<&'a Token> { + let next_token = self.cursor.peek(0)?; + + if next_token.kind == kind { + self.cursor.next() + } else { + None } + } + + /// Advance the cursor to the next token and retrieve it, only if it's of `kind` type. + /// + /// When the next token is a `kind` token, get the token, otherwise return `None`. This + /// function skips line terminators. + fn next_if_skip_lineterminator(&mut self, kind: TokenKind) -> Option<&'a Token> { + let next_token = self.peek_skip_lineterminator()?; - Ok(Expr::new(ExprDef::Block(exprs))) + if next_token.kind == kind { + self.next_skip_lineterminator() + } else { + None + } } - fn get_token(&self, pos: usize) -> Result { - if pos < self.tokens.len() { - Ok(self.tokens.get(pos).expect("failed getting token").clone()) + /// Returns an error if the next Punctuator is not `tk` + fn expect(&mut self, kind: TokenKind, routine: Option<&'static str>) -> Result<(), ParseError> { + let next_token = self.cursor.next().ok_or(ParseError::AbruptEnd)?; + if next_token.kind == kind { + Ok(()) } else { - Err(ParseError::AbruptEnd) + Err(ParseError::Expected( + vec![kind], + next_token.clone(), + routine, + )) } } - fn parse_function_parameters(&mut self) -> Result, ParseError> { - self.expect_punc(Punctuator::OpenParen, "function parameters ( expected")?; - let mut args = Vec::new(); - let mut tk = self.get_token(self.pos)?; - while tk.data != TokenData::Punctuator(Punctuator::CloseParen) { - match tk.data { - TokenData::Identifier(ref id) => args.push(Expr::new(ExprDef::Local(id.clone()))), - TokenData::Punctuator(Punctuator::Spread) => { - args.push(self.parse()?); - self.pos -= 1; // roll back so we're sitting on the closeParen ')' - } - _ => { - return Err(ParseError::Expected( - vec![TokenData::Identifier("identifier".to_string())], - tk, - "function arguments", - )) - } - } - self.pos += 1; - if self.get_token(self.pos)?.data == TokenData::Punctuator(Punctuator::Comma) { - self.pos += 1; - } - tk = self.get_token(self.pos)?; + /// Returns an error if the next symbol is not `tk` + fn expect_no_lineterminator( + &mut self, + kind: TokenKind, + routine: Option<&'static str>, + ) -> Result<(), ParseError> { + let next_token = self + .cursor + .next_skip(|tk| tk.kind == TokenKind::LineTerminator) + .ok_or(ParseError::AbruptEnd)?; + + if next_token.kind == kind { + Ok(()) + } else { + Err(ParseError::Expected( + vec![kind], + next_token.clone(), + routine, + )) } + } - self.expect_punc(Punctuator::CloseParen, "function parameters ) expected")?; - Ok(args) + /// Returns an error if the next symbol is not the punctuator `p` + /// Consumes the next symbol otherwise + fn expect_punc( + &mut self, + p: Punctuator, + routine: Option<&'static str>, + ) -> Result<(), ParseError> { + self.expect(TokenKind::Punctuator(p), routine) } - fn parse_struct(&mut self, keyword: Keyword) -> ParseResult { - match keyword { - Keyword::Throw => { - let thrown = self.parse()?; - Ok(Expr::new(ExprDef::Throw(Box::new(thrown)))) - } - // vars, lets and consts are similar in parsing structure, we can group them together - Keyword::Var | Keyword::Let => { - let mut vars = Vec::new(); - loop { - let name = match self.get_token(self.pos) { - Ok(Token { - data: TokenData::Identifier(ref name), - .. - }) => name.clone(), - Ok(tok) => { - return Err(ParseError::Expected( - vec![TokenData::Identifier("identifier".to_string())], - tok, - "var/let declaration", - )) - } - Err(ParseError::AbruptEnd) => break, - Err(e) => return Err(e), - }; - self.pos += 1; - match self.get_token(self.pos) { - Ok(Token { - data: TokenData::Punctuator(Punctuator::Assign), - .. - }) => { - self.pos += 1; - let val = self.parse()?; - vars.push((name, Some(val))); - match self.get_token(self.pos) { - Ok(Token { - data: TokenData::Punctuator(Punctuator::Comma), - .. - }) => self.pos += 1, - _ => break, - } - } - Ok(Token { - data: TokenData::Punctuator(Punctuator::Comma), - .. - }) => { - self.pos += 1; - vars.push((name, None)); - } - _ => { - vars.push((name, None)); - break; - } - } - } + /// Reads a list of statements as a `Node::StatementList`. + /// + /// It will end at the end of file. + fn read_statement_list(&mut self) -> ParseResult { + self.read_statements(false).map(Node::StatementList) + } + + /// Reads a code block as a `Node::Block`. + /// + /// Note: it will start after the `{` character and stop after reading `}`. + fn read_block_statement(&mut self) -> ParseResult { + self.read_statements(true).map(Node::Block) + } + + /// Read a list of statements and stop after `}` + /// + /// Note: it will start after the `{` character and stop after reading `}`. + fn read_block(&mut self) -> ParseResult { + self.read_statements(true).map(Node::StatementList) + } + + /// Reads a list of statements. + /// + /// If `break_when_closingbrase` is `true`, it will stop as soon as it finds a `}` character. + fn read_statements(&mut self, break_when_closingbrase: bool) -> Result, ParseError> { + let mut items = Vec::new(); - match keyword { - Keyword::Let => Ok(Expr::new(ExprDef::LetDecl(vars))), - _ => Ok(Expr::new(ExprDef::VarDecl(vars))), + loop { + if let Some(token) = + self.next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::CloseBlock)) + { + if break_when_closingbrase { + break; + } else { + return Err(ParseError::Unexpected(token.clone(), None)); } } - Keyword::Const => { - let mut vars = Vec::new(); - loop { - let name = match self.get_token(self.pos) { - Ok(Token { - data: TokenData::Identifier(ref name), - .. - }) => name.clone(), - Ok(tok) => { - return Err(ParseError::Expected( - vec![TokenData::Identifier("identifier".to_string())], - tok, - "const declaration", - )) - } - Err(ParseError::AbruptEnd) => break, - Err(e) => return Err(e), - }; - self.pos += 1; - match self.get_token(self.pos) { - Ok(Token { - data: TokenData::Punctuator(Punctuator::Assign), - .. - }) => { - self.pos += 1; - let val = self.parse()?; - vars.push((name, val)); - match self.get_token(self.pos) { - Ok(Token { - data: TokenData::Punctuator(Punctuator::Comma), - .. - }) => self.pos += 1, - _ => break, - } - } - Ok(tok) => { - return Err(ParseError::Expected( - vec![TokenData::Punctuator(Punctuator::Assign)], - tok, - "const declration", - )) - } - _ => break, - } + + if self.peek_skip_lineterminator().is_none() { + if break_when_closingbrase { + return Err(ParseError::Expected( + vec![TokenKind::Punctuator(Punctuator::CloseBlock)], + self.get_next_token()?.clone(), + None, + )); + } else { + break; } + }; + + let item = self.read_statement_list_item()?; + items.push(item); + + // move the cursor forward for any consecutive semicolon. + while self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::Semicolon)) + .is_some() + {} + } - Ok(Expr::new(ExprDef::ConstDecl(vars))) + Ok(items) + } + + /// Reads an individual statement list item. + /// + /// A statement list item can either be an statement or a declaration. + /// + /// More information: + /// - ECMAScript reference: . + /// - MDN information page about statements and declarations: + /// . + fn read_statement_list_item(&mut self) -> ParseResult { + if let Some(tok) = self.peek_skip_lineterminator() { + match tok.kind { + TokenKind::Keyword(Keyword::Function) + | TokenKind::Keyword(Keyword::Const) + | TokenKind::Keyword(Keyword::Let) => self.read_declaration(), + _ => self.read_statement(), } - Keyword::Return => match self.get_token(self.pos)?.data { - TokenData::Punctuator(Punctuator::Semicolon) - | TokenData::Punctuator(Punctuator::CloseBlock) => { - Ok(Expr::new(ExprDef::Return(None))) - } - _ => Ok(Expr::new(ExprDef::Return(Some(Box::new(self.parse()?))))), - }, - Keyword::New => { - let call = self.parse()?; - match call.def { - ExprDef::Call(ref func, ref args) => { - Ok(Expr::new(ExprDef::Construct(func.clone(), args.clone()))) - } - _ => Err(ParseError::ExpectedExpr("constructor", call)), - } + } else { + Err(ParseError::AbruptEnd) + } + } + + /// Parses a declaration. + /// + /// More information:: + fn read_declaration(&mut self) -> ParseResult { + if let Some(tok) = self.next_skip_lineterminator() { + match tok.kind { + TokenKind::Keyword(Keyword::Function) => self.read_function_declaration(), + TokenKind::Keyword(Keyword::Const) => self.read_binding_list(true), + TokenKind::Keyword(Keyword::Let) => self.read_binding_list(false), + _ => unreachable!(), } - Keyword::TypeOf => Ok(Expr::new(ExprDef::TypeOf(Box::new(self.parse()?)))), - Keyword::If => { - self.expect_punc(Punctuator::OpenParen, "if block")?; - let cond = self.parse()?; - self.expect_punc(Punctuator::CloseParen, "if block")?; - let expr = self.parse()?; - let next = self.get_token(self.pos); - Ok(Expr::new(ExprDef::If( - Box::new(cond), - Box::new(expr), - if next.is_ok() - && next.expect("Could not get next value").data - == TokenData::Keyword(Keyword::Else) - { - self.pos += 1; - Some(Box::new(self.parse()?)) + } else { + Err(ParseError::AbruptEnd) + } + } + + /// Reads a binding list. + /// + /// It will return an error if a `const` declaration is being parsed and there is no + /// initializer. + /// + /// More information: . + fn read_binding_list(&mut self, is_const: bool) -> ParseResult { + // Create vectors to store the variable declarations + // Const and Let signatures are slightly different, Const needs definitions, Lets don't + let mut let_decls = Vec::new(); + let mut const_decls = Vec::new(); + + loop { + let token = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; + let name = if let TokenKind::Identifier(ref name) = token.kind { + name.clone() + } else { + return Err(ParseError::Expected( + vec![TokenKind::Identifier("identifier".to_owned())], + token.clone(), + if is_const { + Some("const declaration") } else { - None - }, - ))) - } - Keyword::While => { - self.expect_punc(Punctuator::OpenParen, "while condition")?; - let cond = self.parse()?; - self.expect_punc(Punctuator::CloseParen, "while condition")?; - let expr = self.parse()?; - Ok(Expr::new(ExprDef::WhileLoop( - Box::new(cond), - Box::new(expr), - ))) - } - Keyword::Switch => { - self.expect_punc(Punctuator::OpenParen, "switch value")?; - let value = self.parse(); - self.expect_punc(Punctuator::CloseParen, "switch value")?; - self.expect_punc(Punctuator::OpenBlock, "switch block")?; - let mut cases = Vec::new(); - let mut default = None; - while self.pos.wrapping_add(1) < self.tokens.len() { - let tok = self.get_token(self.pos)?; - self.pos += 1; - match tok.data { - TokenData::Keyword(Keyword::Case) => { - let cond = self.parse(); - let mut block = Vec::new(); - self.expect_punc(Punctuator::Colon, "switch case")?; - loop { - match self.get_token(self.pos)?.data { - TokenData::Keyword(Keyword::Case) - | TokenData::Keyword(Keyword::Default) - | TokenData::Punctuator(Punctuator::CloseBlock) => break, - _ => block.push(self.parse()?), - } - } - cases.push((cond.expect("No condition supplied"), block)); - } - TokenData::Keyword(Keyword::Default) => { - let mut block = Vec::new(); - self.expect_punc(Punctuator::Colon, "default switch case")?; - loop { - match self.get_token(self.pos)?.data { - TokenData::Keyword(Keyword::Case) - | TokenData::Keyword(Keyword::Default) - | TokenData::Punctuator(Punctuator::CloseBlock) => break, - _ => block.push(self.parse()?), - } - } - default = Some(Expr::new(ExprDef::Block(block))); - } - TokenData::Punctuator(Punctuator::CloseBlock) => break, - _ => { - return Err(ParseError::Expected( - vec![ - TokenData::Keyword(Keyword::Case), - TokenData::Keyword(Keyword::Default), - TokenData::Punctuator(Punctuator::CloseBlock), - ], - tok, - "switch block", - )) - } - } - } - self.expect_punc(Punctuator::CloseBlock, "switch block")?; - Ok(Expr::new(ExprDef::Switch( - Box::new(value.expect("Could not get value")), - cases, - match default { - Some(v) => Some(Box::new(v)), - None => None, + Some("let declaration") }, - ))) - } - Keyword::Function => { - // function [identifier] () { etc } - let tk = self.get_token(self.pos)?; - let name = match tk.data { - TokenData::Identifier(ref name) => { - self.pos += 1; - Some(name.clone()) - } - TokenData::Punctuator(Punctuator::OpenParen) => None, - _ => { - return Err(ParseError::Expected( - vec![TokenData::Identifier("identifier".to_string())], - tk, - "function name", - )) - } + )); + }; + + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::Assign)) + .is_some() + { + let init = Some(self.read_initializer()?); + if is_const { + const_decls.push((name, init.unwrap())); + } else { + let_decls.push((name, init)); }; - // Now we have the function identifier we should have an open paren for arguments ( ) - let args = self.parse_function_parameters()?; - let block = self.parse()?; - Ok(Expr::new(ExprDef::FunctionDecl( - name, - args, - Box::new(block), - ))) + } else if is_const { + return Err(ParseError::Expected( + vec![TokenKind::Punctuator(Punctuator::Assign)], + self.next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)? + .clone(), + Some("const declaration"), + )); + } else { + let_decls.push((name, None)); + } + + if !self.lexical_declaration_continuation()? { + break; + } + } + + if is_const { + Ok(Node::ConstDecl(const_decls)) + } else { + Ok(Node::LetDecl(let_decls)) + } + } + + /// Parses a function declaration. + /// + /// More information: + /// - ECMAScript specification: . + /// - MDN documentation: + /// + fn read_function_declaration(&mut self) -> ParseResult { + let token = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; + let name = if let TokenKind::Identifier(name) = &token.kind { + name.clone() + } else { + return Err(ParseError::Expected( + vec![TokenKind::Identifier(String::from("function name"))], + token.clone(), + Some("function declaration"), + )); + }; + + self.expect( + TokenKind::Punctuator(Punctuator::OpenParen), + Some("function declaration"), + )?; + + let params = self.read_formal_parameters()?; + + self.expect( + TokenKind::Punctuator(Punctuator::OpenBlock), + Some("function declaration"), + )?; + + let body = self.read_block()?; + + Ok(Node::FunctionDecl(Some(name), params, Box::new(body))) + } + + /// + fn read_statement(&mut self) -> ParseResult { + let tok = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; + + let mut is_expression_statement = false; + let stmt = match tok.kind { + TokenKind::Keyword(Keyword::If) => self.read_if_statement(), + TokenKind::Keyword(Keyword::Var) => self.read_variable_statement(), + TokenKind::Keyword(Keyword::While) => self.read_while_statement(), + TokenKind::Keyword(Keyword::For) => self.read_for_statement(), + TokenKind::Keyword(Keyword::Return) => self.read_return_statement(), + TokenKind::Keyword(Keyword::Break) => self.read_break_statement(), + TokenKind::Keyword(Keyword::Continue) => self.read_continue_statement(), + TokenKind::Keyword(Keyword::Try) => self.read_try_statement(), + TokenKind::Keyword(Keyword::Throw) => self.read_throw_statement(), + TokenKind::Keyword(Keyword::Switch) => self.read_switch_statement(), + TokenKind::Punctuator(Punctuator::OpenBlock) => self.read_block_statement(), + // TODO: https://tc39.es/ecma262/#prod-LabelledStatement + // TokenKind::Punctuator(Punctuator::Semicolon) => { + // return Ok(Node::new(NodeBase::Nope, tok.pos)) + // } + _ => { + self.cursor.back(); + is_expression_statement = true; + self.read_expression_statement() + } + }; + + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::Semicolon)) + .is_none() + && is_expression_statement + { + if let Some(tok) = self.cursor.peek(0) { + if tok.kind != TokenKind::LineTerminator + && tok.kind != TokenKind::Punctuator(Punctuator::CloseBlock) + { + return Err(ParseError::Expected( + vec![ + TokenKind::Punctuator(Punctuator::Semicolon), + TokenKind::Punctuator(Punctuator::CloseBlock), + TokenKind::LineTerminator, + ], + tok.clone(), + None, + )); + } } - _ => Err(ParseError::UnexpectedKeyword(keyword)), } + + stmt + } + + /// + fn read_switch_statement(&mut self) -> ParseResult { + // TODO: Reimplement the switch statement in the new parser. + unimplemented!("Switch statement parsing is not implemented"); } - /// Parse a single expression - pub fn parse(&mut self) -> ParseResult { - if self.pos > self.tokens.len() { - return Err(ParseError::AbruptEnd); + /// + fn read_expression_statement(&mut self) -> ParseResult { + self.read_expression() + } + + /// + fn read_break_statement(&mut self) -> ParseResult { + let tok = self.get_next_token()?; + match &tok.kind { + TokenKind::LineTerminator + | TokenKind::Punctuator(Punctuator::Semicolon) + | TokenKind::Punctuator(Punctuator::CloseParen) => { + self.cursor.back(); + Ok(Node::Break(None)) + } + TokenKind::Identifier(name) => Ok(Node::Break(Some(name.clone()))), + _ => Err(ParseError::Expected( + vec![ + TokenKind::Punctuator(Punctuator::Semicolon), + TokenKind::Punctuator(Punctuator::CloseParen), + TokenKind::LineTerminator, + TokenKind::Identifier("identifier".to_owned()), + ], + tok.clone(), + Some("break statement"), + )), } - let token = self.get_token(self.pos)?; - self.pos += 1; - let expr: Expr = match token.data { - TokenData::Punctuator(Punctuator::Semicolon) | TokenData::Comment(_) - if self.pos < self.tokens.len() => - { - self.parse()? - } - TokenData::Punctuator(Punctuator::Semicolon) | TokenData::Comment(_) => { - Expr::new(ExprDef::Const(Const::Undefined)) - } - TokenData::NumericLiteral(num) => Expr::new(ExprDef::Const(Const::Num(num))), - TokenData::NullLiteral => Expr::new(ExprDef::Const(Const::Null)), - TokenData::StringLiteral(text) => Expr::new(ExprDef::Const(Const::String(text))), - TokenData::BooleanLiteral(val) => Expr::new(ExprDef::Const(Const::Bool(val))), - TokenData::Identifier(ref s) if s == "undefined" => { - Expr::new(ExprDef::Const(Const::Undefined)) - } - TokenData::Identifier(s) => Expr::new(ExprDef::Local(s)), - TokenData::Keyword(keyword) => self.parse_struct(keyword)?, - TokenData::RegularExpressionLiteral(body, flags) => Expr::new(ExprDef::Construct( - Box::new(Expr::new(ExprDef::Local("RegExp".to_string()))), + } + + /// + fn read_continue_statement(&mut self) -> ParseResult { + let tok = self.get_next_token()?; + match &tok.kind { + TokenKind::LineTerminator + | TokenKind::Punctuator(Punctuator::Semicolon) + | TokenKind::Punctuator(Punctuator::CloseBlock) => { + self.cursor.back(); + Ok(Node::Continue(None)) + } + TokenKind::Identifier(name) => Ok(Node::Continue(Some(name.clone()))), + _ => Err(ParseError::Expected( vec![ - Expr::new(ExprDef::Const(Const::String(body))), - Expr::new(ExprDef::Const(Const::String(flags))), + TokenKind::Punctuator(Punctuator::Semicolon), + TokenKind::LineTerminator, + TokenKind::Punctuator(Punctuator::CloseBlock), ], + tok.clone(), + Some("continue statement"), )), - TokenData::Punctuator(Punctuator::OpenParen) => { - match self.get_token(self.pos)?.data { - TokenData::Punctuator(Punctuator::CloseParen) - if self.get_token(self.pos.wrapping_add(1))?.data - == TokenData::Punctuator(Punctuator::Arrow) => - { - self.pos += 2; - let expr = self.parse()?; - Expr::new(ExprDef::ArrowFunctionDecl(Vec::new(), Box::new(expr))) - } - _ => { - let next = self.parse()?; - let next_tok = self.get_token(self.pos)?; - self.pos += 1; - match next_tok.data { - TokenData::Punctuator(Punctuator::CloseParen) => next, - TokenData::Punctuator(Punctuator::Comma) => { - // at this point it's probably gonna be an arrow function - // if first param captured all arguments, we should expect a close paren - if let ExprDef::UnaryOp(UnaryOp::Spread, _) = next.def { - return Err(ParseError::Expected( - vec![TokenData::Punctuator(Punctuator::CloseParen)], - next_tok, - "arrow function", - )); - } - - let mut args = vec![ - match next.def { - ExprDef::Local(ref name) => { - Expr::new(ExprDef::Local((*name).clone())) - } - _ => Expr::new(ExprDef::Local("".to_string())), - }, - match self.get_token(self.pos)?.data { - TokenData::Identifier(ref id) => { - Expr::new(ExprDef::Local(id.clone())) - } - _ => Expr::new(ExprDef::Local("".to_string())), - }, - ]; - let mut expect_ident = true; - loop { - self.pos += 1; - let curr_tk = self.get_token(self.pos)?; - match curr_tk.data { - TokenData::Identifier(ref id) if expect_ident => { - args.push(Expr::new(ExprDef::Local(id.clone()))); - expect_ident = false; - } - TokenData::Punctuator(Punctuator::Comma) => { - expect_ident = true; - } - TokenData::Punctuator(Punctuator::Spread) => { - let ident_token = self.get_token(self.pos + 1)?; - if let TokenData::Identifier(ref _id) = ident_token.data - { - args.push(self.parse()?); - self.pos -= 1; - expect_ident = false; - } else { - return Err(ParseError::Expected( - vec![TokenData::Identifier( - "identifier".to_string(), - )], - ident_token, - "arrow function", - )); - } - } - TokenData::Punctuator(Punctuator::CloseParen) => { - self.pos += 1; - break; - } - _ if expect_ident => { - return Err(ParseError::Expected( - vec![TokenData::Identifier( - "identifier".to_string(), - )], - curr_tk, - "arrow function", - )) - } - _ => { - return Err(ParseError::Expected( - vec![ - TokenData::Punctuator(Punctuator::Comma), - TokenData::Punctuator(Punctuator::CloseParen), - TokenData::Punctuator(Punctuator::Spread), - ], - curr_tk, - "arrow function", - )) - } - } - } - self.expect( - TokenData::Punctuator(Punctuator::Arrow), - "arrow function", - )?; - let expr = self.parse()?; - Expr::new(ExprDef::ArrowFunctionDecl(args, Box::new(expr))) - } - _ => { - return Err(ParseError::Expected( - vec![TokenData::Punctuator(Punctuator::CloseParen)], - next_tok, - "brackets", - )) - } - } - } - } - } - TokenData::Punctuator(Punctuator::OpenBracket) => { - let mut array: Vec = vec![]; - let mut saw_expr_last = false; - loop { - let token = self.get_token(self.pos)?; - match token.data { - TokenData::Punctuator(Punctuator::CloseBracket) => { - self.pos += 1; - break; - } - TokenData::Punctuator(Punctuator::Comma) => { - if !saw_expr_last { - // An elision indicates that a space is saved in the array - array.push(Expr::new(ExprDef::Const(Const::Undefined))) - } - saw_expr_last = false; - self.pos += 1; - } - _ if saw_expr_last => { - // Two expressions in a row is not allowed, they must be comma-separated - return Err(ParseError::Expected( - vec![ - TokenData::Punctuator(Punctuator::Comma), - TokenData::Punctuator(Punctuator::CloseBracket), - ], - token, - "array declaration", - )); - } - _ => { - let parsed = self.parse()?; - saw_expr_last = true; - array.push(parsed); - } - } + } + } + + /// + fn read_throw_statement(&mut self) -> ParseResult { + if let Some(tok) = self.cursor.peek(0) { + match tok.kind { + TokenKind::LineTerminator // no `LineTerminator` here + | TokenKind::Punctuator(Punctuator::Semicolon) + | TokenKind::Punctuator(Punctuator::CloseBlock) => { + return Err(ParseError::Unexpected(tok.clone(), Some("throw statement"))); } - Expr::new(ExprDef::ArrayDecl(array)) + _ => {} } - TokenData::Punctuator(Punctuator::OpenBlock) - if self.get_token(self.pos)?.data - == TokenData::Punctuator(Punctuator::CloseBlock) => - { - self.pos += 1; - Expr::new(ExprDef::ObjectDecl(Box::new(BTreeMap::new()))) + } + + let expr = self.read_expression()?; + if let Some(tok) = self.cursor.peek(0) { + if tok.kind == TokenKind::Punctuator(Punctuator::Semicolon) { + let _ = self.cursor.next(); } - TokenData::Punctuator(Punctuator::OpenBlock) - if self.get_token(self.pos.wrapping_add(1))?.data - == TokenData::Punctuator(Punctuator::Colon) => - { - let mut map = Box::new(BTreeMap::new()); - while self.get_token(self.pos.wrapping_sub(1))?.data - == TokenData::Punctuator(Punctuator::Comma) - || map.len() == 0 - { - let tk = self.get_token(self.pos)?; - let name = match tk.data { - TokenData::Identifier(ref id) => id.clone(), - TokenData::StringLiteral(ref str) => str.clone(), - TokenData::Punctuator(Punctuator::CloseBlock) => { - self.pos += 1; - break; - } - _ => { - return Err(ParseError::Expected( - vec![ - TokenData::Identifier("identifier".to_string()), - TokenData::StringLiteral("string".to_string()), - ], - tk, - "object declaration", - )) - } - }; - self.pos += 1; - let value = match self.get_token(self.pos)?.data { - TokenData::Punctuator(Punctuator::Colon) => { - self.pos += 1; - self.parse()? - } - TokenData::Punctuator(Punctuator::OpenParen) => { - let args = self.parse_function_parameters()?; - self.pos += 1; // { - let expr = self.parse()?; - self.pos += 1; - Expr::new(ExprDef::FunctionDecl(None, args, Box::new(expr))) - } - _ => { - return Err(ParseError::Expected( - vec![ - TokenData::Punctuator(Punctuator::Colon), - TokenData::Punctuator(Punctuator::OpenParen), - ], - tk, - "object declaration", - )) - } - }; - map.insert(name, value); - self.pos += 1; + } + + Ok(Node::Throw(Box::new(expr))) + } + + /// + fn read_return_statement(&mut self) -> ParseResult { + if let Some(tok) = self.cursor.peek(0) { + match tok.kind { + TokenKind::LineTerminator | TokenKind::Punctuator(Punctuator::Semicolon) => { + let _ = self.cursor.next(); + return Ok(Node::Return(None)); } - Expr::new(ExprDef::ObjectDecl(map)) - } - TokenData::Punctuator(Punctuator::OpenBlock) => { - let mut exprs = Vec::new(); - loop { - if self.get_token(self.pos)?.data - == TokenData::Punctuator(Punctuator::CloseBlock) - { - break; - } else { - exprs.push(self.parse()?); - } + TokenKind::Punctuator(Punctuator::CloseBlock) => { + return Ok(Node::Return(None)); } - self.pos += 1; - Expr::new(ExprDef::Block(exprs)) - } - // Empty Block - TokenData::Punctuator(Punctuator::CloseBlock) - if self.get_token(self.pos.wrapping_sub(2))?.data - == TokenData::Punctuator(Punctuator::OpenBlock) => - { - Expr::new(ExprDef::Block(vec![])) + _ => {} } - TokenData::Punctuator(Punctuator::Sub) => { - Expr::new(ExprDef::UnaryOp(UnaryOp::Minus, Box::new(self.parse()?))) - } - TokenData::Punctuator(Punctuator::Add) => { - Expr::new(ExprDef::UnaryOp(UnaryOp::Plus, Box::new(self.parse()?))) + } + + let expr = self.read_expression()?; + if let Some(tok) = self.cursor.peek(0) { + if tok.kind == TokenKind::Punctuator(Punctuator::CloseBlock) { + let _ = self.cursor.next(); } - TokenData::Punctuator(Punctuator::Not) => { - Expr::new(ExprDef::UnaryOp(UnaryOp::Not, Box::new(self.parse()?))) + } + + Ok(Node::Return(Some(Box::new(expr)))) + } + + /// + fn read_if_statement(&mut self) -> ParseResult { + self.expect_punc(Punctuator::OpenParen, Some("if statement"))?; + + let cond = self.read_expression()?; + + self.expect_punc(Punctuator::CloseParen, Some("if statement"))?; + + let then_stm = self.read_statement()?; + + if let Some(else_tok) = self.cursor.next() { + if else_tok.kind == TokenKind::Keyword(Keyword::Else) { + let else_stm = self.read_statement()?; + return Ok(Node::If( + Box::new(cond), + Box::new(then_stm), + Some(Box::new(else_stm)), + )); + } else { + self.cursor.back(); } - TokenData::Punctuator(Punctuator::Neg) => { - Expr::new(ExprDef::UnaryOp(UnaryOp::Tilde, Box::new(self.parse()?))) + } + + Ok(Node::If(Box::new(cond), Box::new(then_stm), None)) + } + + /// + fn read_while_statement(&mut self) -> ParseResult { + self.expect_punc(Punctuator::OpenParen, Some("while statement"))?; + + let cond = self.read_expression()?; + + self.expect_punc(Punctuator::CloseParen, Some("while statement"))?; + + let body = self.read_statement()?; + + Ok(Node::WhileLoop(Box::new(cond), Box::new(body))) + } + + /// + fn read_try_statement(&mut self) -> ParseResult { + // TRY + self.expect_punc(Punctuator::OpenBlock, Some("try statement"))?; + let try_clause = self.read_block_statement()?; + + let next_token = self + .peek_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; + + if next_token.kind != TokenKind::Keyword(Keyword::Catch) + && next_token.kind != TokenKind::Keyword(Keyword::Finally) + { + return Err(ParseError::Expected( + vec![ + TokenKind::Keyword(Keyword::Catch), + TokenKind::Keyword(Keyword::Finally), + ], + next_token.clone(), + Some("try statement"), + )); + } + + // CATCH + let (catch, param) = if next_token.kind == TokenKind::Keyword(Keyword::Catch) { + let _ = self.next_skip_lineterminator(); // Advance the cursor + + // Catch binding + self.expect_punc(Punctuator::OpenParen, Some("catch in try statement"))?; + // TODO: should accept BindingPattern + let tok = self.get_next_token()?; + let catch_param = if let TokenKind::Identifier(s) = &tok.kind { + Node::Local(s.clone()) + } else { + return Err(ParseError::Expected( + vec![TokenKind::Identifier("identifier".to_owned())], + tok.clone(), + Some("catch in try statement"), + )); + }; + self.expect_punc(Punctuator::CloseParen, Some("catch in try statement"))?; + + // Catch block + self.expect_punc(Punctuator::OpenBlock, Some("catch in try statement"))?; + ( + Some(Box::new(self.read_block()?)), + Some(Box::new(catch_param)), + ) + } else { + (None, None) + }; + + // FINALLY + let finally_block = if self + .next_if_skip_lineterminator(TokenKind::Keyword(Keyword::Finally)) + .is_some() + { + self.expect_punc(Punctuator::OpenBlock, Some("finally in try statement"))?; + Some(Box::new(self.read_block_statement()?)) + } else { + None + }; + + Ok(Node::Try(Box::new(try_clause), catch, param, finally_block)) + } + + /// + fn read_for_statement(&mut self) -> ParseResult { + self.expect_punc(Punctuator::OpenParen, Some("for statement"))?; + + let init = match self.cursor.peek(0).ok_or(ParseError::AbruptEnd)?.kind { + TokenKind::Keyword(Keyword::Var) => { + Some(Box::new(self.read_variable_declaration_list()?)) } - TokenData::Punctuator(Punctuator::Inc) => Expr::new(ExprDef::UnaryOp( - UnaryOp::IncrementPre, - Box::new(self.parse()?), - )), - TokenData::Punctuator(Punctuator::Dec) => Expr::new(ExprDef::UnaryOp( - UnaryOp::DecrementPre, - Box::new(self.parse()?), - )), - TokenData::Punctuator(Punctuator::Spread) => { - Expr::new(ExprDef::UnaryOp(UnaryOp::Spread, Box::new(self.parse()?))) + TokenKind::Keyword(Keyword::Let) | TokenKind::Keyword(Keyword::Const) => { + Some(Box::new(self.read_declaration()?)) } - _ => return Err(ParseError::Expected(Vec::new(), token.clone(), "script")), + TokenKind::Punctuator(Punctuator::Semicolon) => None, + _ => Some(Box::new(self.read_expression()?)), }; - if self.pos >= self.tokens.len() { - Ok(expr) + + self.expect_punc(Punctuator::Semicolon, Some("for statement"))?; + + let cond = if self + .next_if(TokenKind::Punctuator(Punctuator::Semicolon)) + .is_some() + { + Some(Box::new(Node::Const(Const::Bool(true)))) } else { - self.parse_next(expr) - } + let step = Some(Box::new(self.read_expression()?)); + self.expect_punc(Punctuator::Semicolon, Some("for statement"))?; + step + }; + + let step = if self + .next_if(TokenKind::Punctuator(Punctuator::CloseParen)) + .is_some() + { + None + } else { + let step = self.read_expression()?; + self.expect( + TokenKind::Punctuator(Punctuator::CloseParen), + Some("for statement"), + )?; + Some(Box::new(step)) + }; + + let body = Box::new(self.read_statement()?); + + let for_node = Node::ForLoop(init, cond, step, body); + + Ok(Node::Block(vec![for_node])) + } + /// + fn read_variable_statement(&mut self) -> ParseResult { + self.read_variable_declaration_list() } - fn parse_next(&mut self, expr: Expr) -> ParseResult { - let next = self.get_token(self.pos)?; - let mut carry_on = true; - let mut result = expr.clone(); - match next.data { - TokenData::Punctuator(Punctuator::Dot) => { - self.pos += 1; - let tk = self.get_token(self.pos)?; - match tk.data { - TokenData::Identifier(ref s) => { - result = Expr::new(ExprDef::GetConstField(Box::new(expr), s.to_string())) - } - _ => { - return Err(ParseError::Expected( - vec![TokenData::Identifier("identifier".to_string())], - tk, - "field access", - )) - } + /// + fn read_variable_declaration_list(&mut self) -> ParseResult { + let mut list = Vec::new(); + + loop { + list.push(self.read_variable_declaration()?); + if !self.lexical_declaration_continuation()? { + break; + } + } + + Ok(Node::VarDecl(list)) + } + + /// Checks if the lexical declaration continues with more bindings. + /// + /// If it does, it will advance the internal cursor to the next identifier token. + /// A Lexical Declaration continues its binding list if we find a `,` character. A New line + /// indicates the same as a `;`. + /// + /// More information: . + fn lexical_declaration_continuation(&mut self) -> Result { + if let Some(tok) = self.cursor.peek(0) { + match tok.kind { + TokenKind::LineTerminator => { + let _ = self.cursor.next().ok_or(ParseError::AbruptEnd)?; + Ok(false) } - self.pos += 1; - } - TokenData::Punctuator(Punctuator::OpenParen) => { - let mut args = Vec::new(); - let mut expect_comma_or_end = self.get_token(self.pos.wrapping_add(1))?.data - == TokenData::Punctuator(Punctuator::CloseParen); - loop { - self.pos += 1; - let token = self.get_token(self.pos)?; - if token.data == TokenData::Punctuator(Punctuator::CloseParen) - && expect_comma_or_end - { - self.pos += 1; - break; - } else if token.data == TokenData::Punctuator(Punctuator::Comma) - && expect_comma_or_end - { - expect_comma_or_end = false; - } else if expect_comma_or_end { - return Err(ParseError::Expected( - vec![ - TokenData::Punctuator(Punctuator::Comma), - TokenData::Punctuator(Punctuator::CloseParen), - ], - token, - "function call arguments", - )); - } else { - let parsed = self.parse()?; - self.pos -= 1; - args.push(parsed); - expect_comma_or_end = true; - } + TokenKind::Punctuator(Punctuator::Semicolon) => Ok(false), + TokenKind::Punctuator(Punctuator::Comma) => { + let _ = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; + Ok(true) } - result = Expr::new(ExprDef::Call(Box::new(expr), args)); - } - TokenData::Punctuator(Punctuator::Question) => { - self.pos += 1; - let if_e = self.parse()?; - self.expect(TokenData::Punctuator(Punctuator::Colon), "if expression")?; - let else_e = self.parse()?; - result = Expr::new(ExprDef::If( - Box::new(expr), - Box::new(if_e), - Some(Box::new(else_e)), - )); + _ => Err(ParseError::Expected( + vec![ + TokenKind::Punctuator(Punctuator::Semicolon), + TokenKind::LineTerminator, + ], + self.cursor.next().ok_or(ParseError::AbruptEnd)?.clone(), + Some("lexical declaration"), + )), } - TokenData::Punctuator(Punctuator::OpenBracket) => { - self.pos += 1; - let index = self.parse()?; - self.expect( - TokenData::Punctuator(Punctuator::CloseBracket), - "array index", - )?; - result = Expr::new(ExprDef::GetField(Box::new(expr), Box::new(index))); - } - TokenData::Punctuator(Punctuator::Semicolon) | TokenData::Comment(_) => { - self.pos += 1; - } - TokenData::Punctuator(Punctuator::Assign) => { - self.pos += 1; - let next = self.parse()?; - result = Expr::new(ExprDef::Assign(Box::new(expr), Box::new(next))); - } - TokenData::Punctuator(Punctuator::AssignAdd) => { - result = self.binop(BinOp::Assign(AssignOp::Add), expr)? - } - TokenData::Punctuator(Punctuator::AssignSub) => { - result = self.binop(BinOp::Assign(AssignOp::Sub), expr)? - } - TokenData::Punctuator(Punctuator::AssignMul) => { - result = self.binop(BinOp::Assign(AssignOp::Mul), expr)? - } - TokenData::Punctuator(Punctuator::AssignPow) => { - result = self.binop(BinOp::Assign(AssignOp::Pow), expr)? - } - TokenData::Punctuator(Punctuator::AssignDiv) => { - result = self.binop(BinOp::Assign(AssignOp::Div), expr)? - } - TokenData::Punctuator(Punctuator::AssignAnd) => { - result = self.binop(BinOp::Assign(AssignOp::And), expr)? - } - TokenData::Punctuator(Punctuator::AssignOr) => { - result = self.binop(BinOp::Assign(AssignOp::Or), expr)? - } - TokenData::Punctuator(Punctuator::AssignXor) => { - result = self.binop(BinOp::Assign(AssignOp::Xor), expr)? - } - TokenData::Punctuator(Punctuator::AssignRightSh) => { - result = self.binop(BinOp::Assign(AssignOp::Shr), expr)? - } - TokenData::Punctuator(Punctuator::AssignLeftSh) => { - result = self.binop(BinOp::Assign(AssignOp::Shl), expr)? - } - TokenData::Punctuator(Punctuator::AssignMod) => { - result = self.binop(BinOp::Assign(AssignOp::Mod), expr)? - } - TokenData::Punctuator(Punctuator::Arrow) => { - self.pos += 1; - let mut args = Vec::with_capacity(1); - match result.def { - ExprDef::Local(ref name) => { - args.push(Expr::new(ExprDef::Local((*name).clone()))) + } else { + Ok(false) + } + } + + /// + fn read_variable_declaration(&mut self) -> Result<(String, Option), ParseError> { + let tok = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; + let name = if let TokenKind::Identifier(name) = &tok.kind { + name.clone() + } else { + return Err(ParseError::Expected( + vec![TokenKind::Identifier("identifier".to_string())], + tok.clone(), + Some("variable declaration"), + )); + }; + + if self + .next_if(TokenKind::Punctuator(Punctuator::Assign)) + .is_some() + { + Ok((name, Some(self.read_initializer()?))) + } else { + Ok((name, None)) + } + } + + /// Reads an initializer of variables. + /// + /// Note: it will expect the `=` token to have been already read. + /// + /// More information: + fn read_initializer(&mut self) -> ParseResult { + self.read_assignment_expression() + } + + // https://tc39.es/ecma262/#prod-Expression + expression!( + read_expression, + read_assignment_expression, + [Punctuator::Comma] + ); + + /// + fn read_assignment_expression(&mut self) -> ParseResult { + // Arrow function + let next_token = self.cursor.peek(0).ok_or(ParseError::AbruptEnd)?; + match next_token.kind { + // a=>{} + TokenKind::Identifier(_) => { + if let Some(tok) = self.cursor.peek(1) { + if tok.kind == TokenKind::Punctuator(Punctuator::Arrow) { + return self.read_arrow_function(); } - ExprDef::UnaryOp(UnaryOp::Spread, _) => args.push(result), - _ => return Err(ParseError::ExpectedExpr("identifier", result)), } - let next = self.parse()?; - result = Expr::new(ExprDef::ArrowFunctionDecl(args, Box::new(next))); - } - TokenData::Punctuator(Punctuator::Add) => { - result = self.binop(BinOp::Num(NumOp::Add), expr)? - } - TokenData::Punctuator(Punctuator::Sub) => { - result = self.binop(BinOp::Num(NumOp::Sub), expr)? } - TokenData::Punctuator(Punctuator::Mul) => { - result = self.binop(BinOp::Num(NumOp::Mul), expr)? + // (a,b)=>{} + TokenKind::Punctuator(Punctuator::OpenParen) => { + // TODO: breakpoints in the cursor. + let save_pos = self.cursor.pos(); + let f = self.read_arrow_function(); // We try to read an arrow function. + if f.is_err() { + self.cursor.seek(save_pos); + } else { + return f; + } } - TokenData::Punctuator(Punctuator::Pow) => { - result = self.binop(BinOp::Num(NumOp::Pow), expr)? + _ => {} + } + + let mut lhs = self.read_conditional_expression()?; + // let mut lhs = self.read_block()?; + + if let Ok(tok) = self.get_next_token() { + match tok.kind { + TokenKind::Punctuator(Punctuator::Assign) => { + lhs = Node::Assign(Box::new(lhs), Box::new(self.read_assignment_expression()?)) + } + TokenKind::Punctuator(Punctuator::AssignAdd) => { + let expr = self.read_assignment_expression()?; + lhs = Node::BinOp(BinOp::Assign(AssignOp::Add), Box::new(lhs), Box::new(expr)); + } + TokenKind::Punctuator(Punctuator::AssignSub) => { + let expr = self.read_assignment_expression()?; + lhs = Node::BinOp(BinOp::Assign(AssignOp::Sub), Box::new(lhs), Box::new(expr)); + } + TokenKind::Punctuator(Punctuator::AssignMul) => { + let expr = self.read_assignment_expression()?; + lhs = Node::BinOp(BinOp::Assign(AssignOp::Mul), Box::new(lhs), Box::new(expr)); + } + TokenKind::Punctuator(Punctuator::AssignDiv) => { + let expr = self.read_assignment_expression()?; + lhs = Node::BinOp(BinOp::Assign(AssignOp::Div), Box::new(lhs), Box::new(expr)); + } + TokenKind::Punctuator(Punctuator::AssignAnd) => { + let expr = self.read_assignment_expression()?; + lhs = Node::BinOp(BinOp::Assign(AssignOp::And), Box::new(lhs), Box::new(expr)); + } + TokenKind::Punctuator(Punctuator::AssignOr) => { + let expr = self.read_assignment_expression()?; + lhs = Node::BinOp(BinOp::Assign(AssignOp::Or), Box::new(lhs), Box::new(expr)); + } + TokenKind::Punctuator(Punctuator::AssignXor) => { + let expr = self.read_assignment_expression()?; + lhs = Node::BinOp(BinOp::Assign(AssignOp::Xor), Box::new(lhs), Box::new(expr)); + } + TokenKind::Punctuator(Punctuator::AssignRightSh) => { + let expr = self.read_assignment_expression()?; + lhs = Node::BinOp(BinOp::Assign(AssignOp::Shr), Box::new(lhs), Box::new(expr)); + } + TokenKind::Punctuator(Punctuator::AssignLeftSh) => { + let expr = self.read_assignment_expression()?; + lhs = Node::BinOp(BinOp::Assign(AssignOp::Shl), Box::new(lhs), Box::new(expr)); + } + TokenKind::Punctuator(Punctuator::AssignMod) => { + let expr = self.read_assignment_expression()?; + lhs = Node::BinOp(BinOp::Assign(AssignOp::Mod), Box::new(lhs), Box::new(expr)); + } + TokenKind::Punctuator(Punctuator::AssignPow) => { + let expr = self.read_assignment_expression()?; + lhs = Node::BinOp(BinOp::Assign(AssignOp::Exp), Box::new(lhs), Box::new(expr)); + } + _ => { + self.cursor.back(); + } } - TokenData::Punctuator(Punctuator::Div) => { - result = self.binop(BinOp::Num(NumOp::Div), expr)? + } + + Ok(lhs) + } + + /// + fn read_conditional_expression(&mut self) -> ParseResult { + let lhs = self.read_logical_or_expression()?; + + if let Ok(tok) = self.get_next_token() { + if tok.kind == TokenKind::Punctuator(Punctuator::Question) { + let then_ = self.read_assignment_expression()?; + self.expect_punc(Punctuator::Colon, Some("conditional expression"))?; + let else_ = self.read_assignment_expression()?; + return Ok(Node::ConditionalOp( + Box::new(lhs), + Box::new(then_), + Box::new(else_), + )); + } else { + self.cursor.back(); } - TokenData::Punctuator(Punctuator::Mod) => { - result = self.binop(BinOp::Num(NumOp::Mod), expr)? + } + + Ok(lhs) + } + + /// + fn read_arrow_function(&mut self) -> ParseResult { + let next_token = self.get_next_token()?; + let params = match &next_token.kind { + TokenKind::Punctuator(Punctuator::OpenParen) => self.read_formal_parameters()?, + TokenKind::Identifier(param_name) => vec![FormalParameter { + init: None, + name: param_name.clone(), + is_rest_param: false, + }], + _ => { + return Err(ParseError::Expected( + vec![ + TokenKind::Punctuator(Punctuator::OpenParen), + TokenKind::Identifier("identifier".to_owned()), + ], + next_token.clone(), + Some("arrow function"), + )) } - TokenData::Punctuator(Punctuator::BoolAnd) => { - result = self.binop(BinOp::Log(LogOp::And), expr)? + }; + + self.expect_punc(Punctuator::Arrow, Some("arrow function"))?; + + let body = if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::OpenBlock)) + .is_some() + { + self.read_block()? + } else { + Node::Return(Some(Box::new(self.read_assignment_expression()?))) + }; + + Ok(Node::ArrowFunctionDecl(params, Box::new(body))) + } + + /// Collect parameters from functions or arrow functions + fn read_formal_parameters(&mut self) -> Result { + let mut params = Vec::new(); + + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::CloseParen)) + .is_some() + { + return Ok(params); + } + + loop { + let mut rest_param = false; + + params.push( + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::Spread)) + .is_some() + { + rest_param = true; + self.read_function_rest_parameter()? + } else { + self.read_formal_parameter()? + }, + ); + + if self + .next_if(TokenKind::Punctuator(Punctuator::CloseParen)) + .is_some() + { + break; } - TokenData::Punctuator(Punctuator::BoolOr) => { - result = self.binop(BinOp::Log(LogOp::Or), expr)? + + if rest_param { + return Err(ParseError::Unexpected( + self.cursor + .peek_prev() + .expect("current token disappeared") + .clone(), + Some("rest parameter must be the last formal parameter"), + )); } - TokenData::Punctuator(Punctuator::And) => { - result = self.binop(BinOp::Bit(BitOp::And), expr)? + + self.expect_punc(Punctuator::Comma, Some("parameter list"))?; + } + + Ok(params) + } + + /// + fn read_function_rest_parameter(&mut self) -> Result { + let token = self.get_next_token()?; + Ok(FormalParameter::new( + if let TokenKind::Identifier(name) = &token.kind { + name.clone() + } else { + return Err(ParseError::Expected( + vec![TokenKind::Identifier("identifier".to_owned())], + token.clone(), + Some("rest parameter"), + )); + }, + None, + true, + )) + } + + /// + fn read_formal_parameter(&mut self) -> Result { + let token = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; + let name = if let TokenKind::Identifier(name) = &token.kind { + name + } else { + return Err(ParseError::Expected( + vec![TokenKind::Identifier("identifier".to_owned())], + token.clone(), + Some("formal parameter"), + )); + }; + + // TODO: Implement initializer. + Ok(FormalParameter::new(name.clone(), None, false)) + } + + // + expression!( + read_logical_or_expression, + read_logical_and_expression, + [Punctuator::BoolOr] + ); + + // + expression!( + read_logical_and_expression, + read_bitwise_or_expression, + [Punctuator::BoolAnd] + ); + + // https://tc39.es/ecma262/#prod-BitwiseORExpression + expression!( + read_bitwise_or_expression, + read_bitwise_xor_expression, + [Punctuator::Or] + ); + + // https://tc39.es/ecma262/#prod-BitwiseXORExpression + expression!( + read_bitwise_xor_expression, + read_bitwise_and_expression, + [Punctuator::Xor] + ); + + // + expression!( + read_bitwise_and_expression, + read_equality_expression, + [Punctuator::And] + ); + + // + expression!( + read_equality_expression, + read_relational_expression, + [ + Punctuator::Eq, + Punctuator::NotEq, + Punctuator::StrictEq, + Punctuator::StrictNotEq + ] + ); + + // + expression!( + read_relational_expression, + read_shift_expression, + [ + Punctuator::LessThan, + Punctuator::GreaterThan, + Punctuator::LessThanOrEq, + Punctuator::GreaterThanOrEq + ] + ); + + // + expression!( + read_shift_expression, + read_additive_expression, + [ + Punctuator::LeftSh, + Punctuator::RightSh, + Punctuator::URightSh + ] + ); + + // + expression!( + read_additive_expression, + read_multiplicate_expression, + [Punctuator::Add, Punctuator::Sub] + ); + + // + expression!( + read_multiplicate_expression, + read_exponentiation_expression, + [Punctuator::Mul, Punctuator::Div, Punctuator::Mod] + ); + + /// + fn read_exponentiation_expression(&mut self) -> ParseResult { + if self.is_unary_expression() { + return self.read_unary_expression(); + } + + let lhs = self.read_update_expression()?; + if let Ok(tok) = self.get_next_token() { + if let TokenKind::Punctuator(Punctuator::Exp) = tok.kind { + return Ok(Node::BinOp( + BinOp::Num(NumOp::Exp), + Box::new(lhs), + Box::new(self.read_exponentiation_expression()?), + )); + } else { + self.cursor.back(); } - TokenData::Punctuator(Punctuator::Or) => { - result = self.binop(BinOp::Bit(BitOp::Or), expr)? + } + Ok(lhs) + } + + // Checks by looking at the next token to see whether its a Unary operator or not. + fn is_unary_expression(&mut self) -> bool { + if let Some(tok) = self.cursor.peek(0) { + match tok.kind { + TokenKind::Keyword(Keyword::Delete) + | TokenKind::Keyword(Keyword::Void) + | TokenKind::Keyword(Keyword::TypeOf) + | TokenKind::Punctuator(Punctuator::Add) + | TokenKind::Punctuator(Punctuator::Sub) + | TokenKind::Punctuator(Punctuator::Not) + | TokenKind::Punctuator(Punctuator::Neg) => true, + _ => false, } - TokenData::Punctuator(Punctuator::Xor) => { - result = self.binop(BinOp::Bit(BitOp::Xor), expr)? + } else { + false + } + } + + /// + fn read_unary_expression(&mut self) -> ParseResult { + let tok = self.get_next_token()?; + match tok.kind { + TokenKind::Keyword(Keyword::Delete) => Ok(Node::UnaryOp( + UnaryOp::Delete, + Box::new(self.read_unary_expression()?), + )), + TokenKind::Keyword(Keyword::Void) => Ok(Node::UnaryOp( + UnaryOp::Void, + Box::new(self.read_unary_expression()?), + )), + TokenKind::Keyword(Keyword::TypeOf) => Ok(Node::UnaryOp( + UnaryOp::TypeOf, + Box::new(self.read_unary_expression()?), + )), + TokenKind::Punctuator(Punctuator::Add) => Ok(Node::UnaryOp( + UnaryOp::Plus, + Box::new(self.read_unary_expression()?), + )), + TokenKind::Punctuator(Punctuator::Sub) => Ok(Node::UnaryOp( + UnaryOp::Minus, + Box::new(self.read_unary_expression()?), + )), + TokenKind::Punctuator(Punctuator::Neg) => Ok(Node::UnaryOp( + UnaryOp::Tilde, + Box::new(self.read_unary_expression()?), + )), + TokenKind::Punctuator(Punctuator::Not) => Ok(Node::UnaryOp( + UnaryOp::Not, + Box::new(self.read_unary_expression()?), + )), + _ => { + self.cursor.back(); + self.read_update_expression() } - TokenData::Punctuator(Punctuator::LeftSh) => { - result = self.binop(BinOp::Bit(BitOp::Shl), expr)? + } + } + + /// + fn read_update_expression(&mut self) -> ParseResult { + let tok = self + .peek_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; + match tok.kind { + TokenKind::Punctuator(Punctuator::Inc) => { + self.next_skip_lineterminator().unwrap(); + return Ok(Node::UnaryOp( + UnaryOp::IncrementPre, + Box::new(self.read_left_hand_side_expression()?), + )); } - TokenData::Punctuator(Punctuator::RightSh) => { - result = self.binop(BinOp::Bit(BitOp::Shr), expr)? + TokenKind::Punctuator(Punctuator::Dec) => { + self.next_skip_lineterminator().unwrap(); + return Ok(Node::UnaryOp( + UnaryOp::DecrementPre, + Box::new(self.read_left_hand_side_expression()?), + )); } - TokenData::Punctuator(Punctuator::Eq) => { - result = self.binop(BinOp::Comp(CompOp::Equal), expr)? + _ => {} + } + + let lhs = self.read_left_hand_side_expression()?; + if let Some(tok) = self.cursor.peek(0) { + match tok.kind { + TokenKind::Punctuator(Punctuator::Inc) => { + self.get_next_token().unwrap(); + return Ok(Node::UnaryOp(UnaryOp::IncrementPost, Box::new(lhs))); + } + TokenKind::Punctuator(Punctuator::Dec) => { + self.get_next_token().unwrap(); + return Ok(Node::UnaryOp(UnaryOp::DecrementPost, Box::new(lhs))); + } + _ => {} } - TokenData::Punctuator(Punctuator::NotEq) => { - result = self.binop(BinOp::Comp(CompOp::NotEqual), expr)? + } + + Ok(lhs) + } + + /// + fn read_left_hand_side_expression(&mut self) -> ParseResult { + // TODO: Implement NewExpression: new MemberExpression + let lhs = self.read_member_expression()?; + match self.peek_skip_lineterminator() { + Some(ref tok) if tok.kind == TokenKind::Punctuator(Punctuator::OpenParen) => { + self.read_call_expression(lhs) + } + _ => self.read_new_expression(lhs), + } + } + + /// + fn read_new_expression(&mut self, first_member_expr: Node) -> ParseResult { + Ok(first_member_expr) + } + + /// + fn read_member_expression(&mut self) -> ParseResult { + let mut lhs = if self + .peek_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)? + .kind + == TokenKind::Keyword(Keyword::New) + { + let _ = self + .next_skip_lineterminator() + .expect("keyword disappeared"); + let lhs = self.read_member_expression()?; + self.expect_punc(Punctuator::OpenParen, Some("member expression"))?; + let args = self.read_arguments()?; + let call_node = Node::Call(Box::new(lhs), args); + + Node::New(Box::new(call_node)) + } else { + self.read_primary_expression()? + }; + while let Some(tok) = self.peek_skip_lineterminator() { + match &tok.kind { + TokenKind::Punctuator(Punctuator::Dot) => { + let _ = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; // We move the cursor forward. + match &self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)? + .kind + { + TokenKind::Identifier(name) => { + lhs = Node::GetConstField(Box::new(lhs), name.clone()) + } + TokenKind::Keyword(kw) => { + lhs = Node::GetConstField(Box::new(lhs), kw.to_string()) + } + _ => { + return Err(ParseError::Expected( + vec![TokenKind::Identifier("identifier".to_owned())], + tok.clone(), + Some("member expression"), + )); + } + } + } + TokenKind::Punctuator(Punctuator::OpenBracket) => { + let _ = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; // We move the cursor forward. + let idx = self.read_expression()?; + self.expect_punc(Punctuator::CloseBracket, Some("member expression"))?; + lhs = Node::GetField(Box::new(lhs), Box::new(idx)); + } + _ => break, } - TokenData::Punctuator(Punctuator::StrictEq) => { - result = self.binop(BinOp::Comp(CompOp::StrictEqual), expr)? + } + + Ok(lhs) + } + + /// + fn read_call_expression(&mut self, first_member_expr: Node) -> ParseResult { + let mut lhs = first_member_expr; + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::OpenParen)) + .is_some() + { + let args = self.read_arguments()?; + lhs = Node::Call(Box::new(lhs), args); + } else { + let next_token = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; + return Err(ParseError::Expected( + vec![TokenKind::Punctuator(Punctuator::OpenParen)], + next_token.clone(), + Some("call expression"), + )); + } + + while let Some(tok) = self.peek_skip_lineterminator() { + match tok.kind { + TokenKind::Punctuator(Punctuator::OpenParen) => { + let _ = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; // We move the cursor. + let args = self.read_arguments()?; + lhs = Node::Call(Box::new(lhs), args); + } + TokenKind::Punctuator(Punctuator::Dot) => { + let _ = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; // We move the cursor. + match &self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)? + .kind + { + TokenKind::Identifier(name) => { + lhs = Node::GetConstField(Box::new(lhs), name.clone()); + } + TokenKind::Keyword(kw) => { + lhs = Node::GetConstField(Box::new(lhs), kw.to_string()); + } + _ => { + return Err(ParseError::Expected( + vec![TokenKind::Identifier("identifier".to_owned())], + tok.clone(), + Some("call expression"), + )); + } + } + } + TokenKind::Punctuator(Punctuator::OpenBracket) => { + let _ = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; // We move the cursor. + let idx = self.read_expression()?; + self.expect_punc(Punctuator::CloseBracket, Some("call expression"))?; + lhs = Node::GetField(Box::new(lhs), Box::new(idx)); + } + _ => break, } - TokenData::Punctuator(Punctuator::StrictNotEq) => { - result = self.binop(BinOp::Comp(CompOp::StrictNotEqual), expr)? + } + + Ok(lhs) + } + + /// + fn read_arguments(&mut self) -> Result, ParseError> { + let mut args = Vec::new(); + loop { + let next_token = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; + match next_token.kind { + TokenKind::Punctuator(Punctuator::CloseParen) => break, + TokenKind::Punctuator(Punctuator::Comma) => { + if args.is_empty() { + return Err(ParseError::Unexpected(next_token.clone(), None)); + } + + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::CloseParen)) + .is_some() + { + break; + } + } + _ => { + if !args.is_empty() { + return Err(ParseError::Expected( + vec![ + TokenKind::Punctuator(Punctuator::Comma), + TokenKind::Punctuator(Punctuator::CloseParen), + ], + next_token.clone(), + Some("argument list"), + )); + } else { + self.cursor.back(); + } + } } - TokenData::Punctuator(Punctuator::LessThan) => { - result = self.binop(BinOp::Comp(CompOp::LessThan), expr)? + + if self + .next_if(TokenKind::Punctuator(Punctuator::Spread)) + .is_some() + { + args.push(Node::Spread(Box::new(self.read_assignment_expression()?))); + } else { + args.push(self.read_assignment_expression()?); } - TokenData::Punctuator(Punctuator::LessThanOrEq) => { - result = self.binop(BinOp::Comp(CompOp::LessThanOrEqual), expr)? + } + + Ok(args) + } + + /// + fn read_primary_expression(&mut self) -> ParseResult { + let tok = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; + + match &tok.kind { + TokenKind::Keyword(Keyword::This) => Ok(Node::This), + // TokenKind::Keyword(Keyword::Arguments) => Ok(Node::new(NodeBase::Arguments, tok.pos)), + TokenKind::Keyword(Keyword::Function) => self.read_function_expression(), + TokenKind::Punctuator(Punctuator::OpenParen) => { + let expr = self.read_expression(); + self.expect_punc(Punctuator::CloseParen, Some("primary expression"))?; + expr + } + TokenKind::Punctuator(Punctuator::OpenBracket) => self.read_array_literal(), + TokenKind::Punctuator(Punctuator::OpenBlock) => self.read_object_literal(), + TokenKind::BooleanLiteral(boolean) => Ok(Node::Const(Const::Bool(*boolean))), + // TODO: ADD TokenKind::UndefinedLiteral + TokenKind::Identifier(ref i) if i == "undefined" => Ok(Node::Const(Const::Undefined)), + TokenKind::NullLiteral => Ok(Node::Const(Const::Null)), + TokenKind::Identifier(ident) => Ok(Node::Local(ident.clone())), + TokenKind::StringLiteral(s) => Ok(Node::Const(Const::String(s.clone()))), + TokenKind::NumericLiteral(num) => Ok(Node::Const(Const::Num(*num))), + TokenKind::RegularExpressionLiteral(body, flags) => { + Ok(Node::New(Box::new(Node::Call( + Box::new(Node::Local("RegExp".to_string())), + vec![ + Node::Const(Const::String(body.clone())), + Node::Const(Const::String(flags.clone())), + ], + )))) + } + _ => Err(ParseError::Unexpected( + tok.clone(), + Some("primary expression"), + )), + } + } + + /// + fn read_function_expression(&mut self) -> ParseResult { + let name = if let TokenKind::Identifier(name) = + &self.cursor.peek(0).ok_or(ParseError::AbruptEnd)?.kind + { + Some(name.clone()) + } else { + None + }; + if name.is_some() { + // We move the cursor forward. + let _ = self.get_next_token()?; + } + + self.expect_punc(Punctuator::OpenParen, Some("function expression"))?; + + let params = self.read_formal_parameters()?; + + self.expect_punc(Punctuator::OpenBlock, Some("function expression"))?; + + let body = self.read_block()?; + + Ok(Node::FunctionDecl(name, params, Box::new(body))) + } + + /// + fn read_array_literal(&mut self) -> ParseResult { + let mut elements = Vec::new(); + + loop { + // TODO: Support all features. + while self + .next_if(TokenKind::Punctuator(Punctuator::Comma)) + .is_some() + { + elements.push(Node::Const(Const::Undefined)); } - TokenData::Punctuator(Punctuator::GreaterThan) => { - result = self.binop(BinOp::Comp(CompOp::GreaterThan), expr)? + + if self + .next_if(TokenKind::Punctuator(Punctuator::CloseBracket)) + .is_some() + { + break; } - TokenData::Punctuator(Punctuator::GreaterThanOrEq) => { - result = self.binop(BinOp::Comp(CompOp::GreaterThanOrEqual), expr)? + + let _ = self.cursor.peek(0).ok_or(ParseError::AbruptEnd)?; // Check that there are more tokens to read. + + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::Spread)) + .is_some() + { + let node = self.read_assignment_expression()?; + elements.push(Node::Spread(Box::new(node))); + } else { + elements.push(self.read_assignment_expression()?); } - TokenData::Punctuator(Punctuator::Inc) => { - result = Expr::new(ExprDef::UnaryOp( - UnaryOp::IncrementPost, - Box::new(self.parse()?), - )) + self.next_if(TokenKind::Punctuator(Punctuator::Comma)); + } + + Ok(Node::ArrayDecl(elements)) + } + + /// + fn read_object_literal(&mut self) -> ParseResult { + let mut elements = Vec::new(); + + loop { + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::CloseBlock)) + .is_some() + { + break; } - TokenData::Punctuator(Punctuator::Dec) => { - result = Expr::new(ExprDef::UnaryOp( - UnaryOp::DecrementPost, - Box::new(self.parse()?), - )) + + elements.push(self.read_property_definition()?); + + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::CloseBlock)) + .is_some() + { + break; } - _ => carry_on = false, - }; - if carry_on && self.pos < self.tokens.len() { - self.parse_next(result) - } else { - Ok(result) - } - } - - fn binop(&mut self, op: BinOp, orig: Expr) -> Result { - let (precedence, assoc) = op.get_precedence_and_assoc(); - self.pos += 1; - let next = self.parse()?; - Ok(match next.def { - ExprDef::BinOp(ref op2, ref a, ref b) => { - let other_precedence = op2.get_precedence(); - if precedence < other_precedence || (precedence == other_precedence && !assoc) { - Expr::new(ExprDef::BinOp( - op2.clone(), - b.clone(), - Box::new(Expr::new(ExprDef::BinOp(op, Box::new(orig), a.clone()))), - )) - } else { - Expr::new(ExprDef::BinOp(op, Box::new(orig), Box::new(next.clone()))) - } + + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::Comma)) + .is_none() + { + let next_token = self + .next_skip_lineterminator() + .ok_or(ParseError::AbruptEnd)?; + return Err(ParseError::Expected( + vec![ + TokenKind::Punctuator(Punctuator::Comma), + TokenKind::Punctuator(Punctuator::CloseBlock), + ], + next_token.clone(), + Some("object literal"), + )); } - _ => Expr::new(ExprDef::BinOp(op, Box::new(orig), Box::new(next))), - }) + } + + Ok(Node::Object(elements)) } - /// Returns an error if the next symbol is not `tk` - fn expect(&mut self, tk: TokenData, routine: &'static str) -> Result<(), ParseError> { - self.pos += 1; - let curr_tk = self.get_token(self.pos.wrapping_sub(1))?; - if curr_tk.data == tk { - Ok(()) - } else { - Err(ParseError::Expected(vec![tk], curr_tk, routine)) + /// + fn read_property_definition(&mut self) -> Result { + fn to_string(kind: &TokenKind) -> String { + match kind { + TokenKind::Identifier(name) => name.clone(), + TokenKind::NumericLiteral(n) => format!("{}", n), + TokenKind::StringLiteral(s) => s.clone(), + _ => unimplemented!("{:?}", kind), + } } - } - /// Returns an error if the next symbol is not the punctuator `p` - #[inline(always)] - fn expect_punc(&mut self, p: Punctuator, routine: &'static str) -> Result<(), ParseError> { - self.expect(TokenData::Punctuator(p), routine) + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::Spread)) + .is_some() + { + let node = self.read_assignment_expression()?; + return Ok(PropertyDefinition::SpreadObject(node)); + } + + let prop_name = self + .next_skip_lineterminator() + .map(|tok| to_string(&tok.kind)) + .ok_or(ParseError::AbruptEnd)?; + + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::Colon)) + .is_some() + { + let val = self.read_assignment_expression()?; + return Ok(PropertyDefinition::Property(prop_name, val)); + } + + // TODO: Split into separate function: read_property_method_definition + if self + .next_if_skip_lineterminator(TokenKind::Punctuator(Punctuator::OpenParen)) + .is_some() + { + let params = self.read_formal_parameters()?; + + self.expect( + TokenKind::Punctuator(Punctuator::OpenBlock), + Some("property method definition"), + )?; + + let body = self.read_block()?; + + return Ok(PropertyDefinition::MethodDefinition( + MethodDefinitionKind::Ordinary, + prop_name, + Node::FunctionDecl(None, params, Box::new(body)), + )); + } + + // TODO need to revisit this + // if let TokenKind::Identifier(name) = tok.kind { + // if name == "get" || name == "set" { + // let may_identifier = self.peek_skip_lineterminator(); + // if may_identifier.is_some() + // && matches!(may_identifier.unwrap().kind, TokenKind::Identifier(_)) + // { + // let f = self.read_function_expression()?; + // let func_name = if let NodeBase::FunctionExpr(ref name, _, _) = f.base { + // name.clone().unwrap() + // } else { + // panic!() + // }; + // return Ok(PropertyDefinition::MethodDefinition( + // if name == "get" { + // MethodDefinitionKind::Get + // } else { + // MethodDefinitionKind::Set + // }, + // func_name, + // f, + // )); + // } + // } + + // return Ok(PropertyDefinition::IdentifierReference(name)); + // } + + let pos = self + .cursor + .peek(0) + .map(|tok| tok.pos) + .ok_or(ParseError::AbruptEnd)?; + Err(ParseError::General( + "expected property definition", + Some(pos), + )) } } diff --git a/boa/src/syntax/parser/tests.rs b/boa/src/syntax/parser/tests.rs index 7669e96fb8b..ec25a0499b1 100644 --- a/boa/src/syntax/parser/tests.rs +++ b/boa/src/syntax/parser/tests.rs @@ -1,24 +1,24 @@ //! Tests for the parser. use super::*; -use crate::syntax::ast::{constant::Const, op::BinOp}; +use crate::syntax::ast::{constant::Const, op::BinOp, op::BitOp}; use crate::syntax::{ - ast::expr::{Expr, ExprDef}, + ast::node::{FormalParameter, Node}, lexer::Lexer, }; -fn create_bin_op(op: BinOp, exp1: Expr, exp2: Expr) -> Expr { - Expr::new(ExprDef::BinOp(op, Box::new(exp1), Box::new(exp2))) +fn create_bin_op(op: BinOp, exp1: Node, exp2: Node) -> Node { + Node::BinOp(op, Box::new(exp1), Box::new(exp2)) } #[allow(clippy::result_unwrap_used)] -fn check_parser(js: &str, expr: &[Expr]) { +fn check_parser(js: &str, expr: &[Node]) { let mut lexer = Lexer::new(js); lexer.lex().expect("failed to lex"); assert_eq!( - Parser::new(lexer.tokens).parse_all().unwrap(), - Expr::new(ExprDef::Block(expr.into())) + Parser::new(&lexer.tokens).parse_all().unwrap(), + Node::StatementList(expr.into()) ); } @@ -26,7 +26,7 @@ fn check_invalid(js: &str) { let mut lexer = Lexer::new(js); lexer.lex().expect("failed to lex"); - assert!(Parser::new(lexer.tokens).parse_all().is_err()); + assert!(Parser::new(&lexer.tokens).parse_all().is_err()); } #[test] @@ -34,70 +34,86 @@ fn check_string() { use crate::syntax::ast::constant::Const; // Check empty string - check_parser( - "\"\"", - &[Expr::new(ExprDef::Const(Const::String(String::new())))], - ); + check_parser("\"\"", &[Node::Const(Const::String(String::new()))]); // Check non-empty string check_parser( "\"hello\"", - &[Expr::new(ExprDef::Const(Const::String(String::from( - "hello", - ))))], + &[Node::Const(Const::String(String::from("hello")))], ); } + #[test] -fn check_object_short_function() { - // Testing short function syntax - let mut object_properties: BTreeMap = BTreeMap::new(); - object_properties.insert( - String::from("a"), - Expr::new(ExprDef::Const(Const::Bool(true))), - ); - object_properties.insert( - String::from("b"), - Expr::new(ExprDef::FunctionDecl( - None, - vec![], - Box::new(Expr::new(ExprDef::Block(vec![]))), - )), +fn check_object_literal() { + let object_properties = vec![ + PropertyDefinition::Property(String::from("a"), Node::Const(Const::Bool(true))), + PropertyDefinition::Property(String::from("b"), Node::Const(Const::Bool(false))), + ]; + + check_parser( + "const x = { + a: true, + b: false, + }; + ", + &[Node::ConstDecl(vec![( + String::from("x"), + Node::Object(object_properties), + )])], ); +} - check_parser( - "{ - a: true, - b() {} - }; - ", - &[Expr::new(ExprDef::ObjectDecl(Box::new(object_properties)))], +#[test] +fn check_object_short_function() { + // Testing short function syntax + let object_properties = vec![ + PropertyDefinition::Property(String::from("a"), Node::Const(Const::Bool(true))), + PropertyDefinition::MethodDefinition( + MethodDefinitionKind::Ordinary, + String::from("b"), + Node::FunctionDecl(None, Vec::new(), Box::new(Node::StatementList(Vec::new()))), + ), + ]; + + check_parser( + "const x = { + a: true, + b() {}, + }; + ", + &[Node::ConstDecl(vec![( + String::from("x"), + Node::Object(object_properties), + )])], ); } #[test] fn check_object_short_function_arguments() { // Testing short function syntax - let mut object_properties: BTreeMap = BTreeMap::new(); - object_properties.insert( - String::from("a"), - Expr::new(ExprDef::Const(Const::Bool(true))), - ); - object_properties.insert( - String::from("b"), - Expr::new(ExprDef::FunctionDecl( - None, - vec![Expr::new(ExprDef::Local(String::from("test")))], - Box::new(Expr::new(ExprDef::Block(vec![]))), - )), - ); + let object_properties = vec![ + PropertyDefinition::Property(String::from("a"), Node::Const(Const::Bool(true))), + PropertyDefinition::MethodDefinition( + MethodDefinitionKind::Ordinary, + String::from("b"), + Node::FunctionDecl( + None, + vec![FormalParameter::new(String::from("test"), None, false)], + Box::new(Node::StatementList(Vec::new())), + ), + ), + ]; check_parser( - "{ - a: true, - b(test) {} - }; - ", - &[Expr::new(ExprDef::ObjectDecl(Box::new(object_properties)))], + "const x = { + a: true, + b(test) {} + }; + ", + &[Node::ConstDecl(vec![( + String::from("x"), + Node::Object(object_properties), + )])], ); } #[test] @@ -105,77 +121,75 @@ fn check_array() { use crate::syntax::ast::constant::Const; // Check empty array - check_parser("[]", &[Expr::new(ExprDef::ArrayDecl(vec![]))]); + check_parser("[]", &[Node::ArrayDecl(vec![])]); // Check array with empty slot check_parser( "[,]", - &[Expr::new(ExprDef::ArrayDecl(vec![Expr::new( - ExprDef::Const(Const::Undefined), - )]))], + &[Node::ArrayDecl(vec![Node::Const(Const::Undefined)])], ); // Check numeric array check_parser( "[1, 2, 3]", - &[Expr::new(ExprDef::ArrayDecl(vec![ - Expr::new(ExprDef::Const(Const::Num(1.0))), - Expr::new(ExprDef::Const(Const::Num(2.0))), - Expr::new(ExprDef::Const(Const::Num(3.0))), - ]))], + &[Node::ArrayDecl(vec![ + Node::Const(Const::Num(1.0)), + Node::Const(Const::Num(2.0)), + Node::Const(Const::Num(3.0)), + ])], ); // Check numeric array with trailing comma check_parser( "[1, 2, 3,]", - &[Expr::new(ExprDef::ArrayDecl(vec![ - Expr::new(ExprDef::Const(Const::Num(1.0))), - Expr::new(ExprDef::Const(Const::Num(2.0))), - Expr::new(ExprDef::Const(Const::Num(3.0))), - ]))], + &[Node::ArrayDecl(vec![ + Node::Const(Const::Num(1.0)), + Node::Const(Const::Num(2.0)), + Node::Const(Const::Num(3.0)), + ])], ); // Check numeric array with an elision check_parser( "[1, 2, , 3]", - &[Expr::new(ExprDef::ArrayDecl(vec![ - Expr::new(ExprDef::Const(Const::Num(1.0))), - Expr::new(ExprDef::Const(Const::Num(2.0))), - Expr::new(ExprDef::Const(Const::Undefined)), - Expr::new(ExprDef::Const(Const::Num(3.0))), - ]))], + &[Node::ArrayDecl(vec![ + Node::Const(Const::Num(1.0)), + Node::Const(Const::Num(2.0)), + Node::Const(Const::Undefined), + Node::Const(Const::Num(3.0)), + ])], ); // Check numeric array with repeated elision check_parser( "[1, 2, ,, 3]", - &[Expr::new(ExprDef::ArrayDecl(vec![ - Expr::new(ExprDef::Const(Const::Num(1.0))), - Expr::new(ExprDef::Const(Const::Num(2.0))), - Expr::new(ExprDef::Const(Const::Undefined)), - Expr::new(ExprDef::Const(Const::Undefined)), - Expr::new(ExprDef::Const(Const::Num(3.0))), - ]))], + &[Node::ArrayDecl(vec![ + Node::Const(Const::Num(1.0)), + Node::Const(Const::Num(2.0)), + Node::Const(Const::Undefined), + Node::Const(Const::Undefined), + Node::Const(Const::Num(3.0)), + ])], ); // Check combined array check_parser( "[1, \"a\", 2]", - &[Expr::new(ExprDef::ArrayDecl(vec![ - Expr::new(ExprDef::Const(Const::Num(1.0))), - Expr::new(ExprDef::Const(Const::String(String::from("a")))), - Expr::new(ExprDef::Const(Const::Num(2.0))), - ]))], + &[Node::ArrayDecl(vec![ + Node::Const(Const::Num(1.0)), + Node::Const(Const::String(String::from("a"))), + Node::Const(Const::Num(2.0)), + ])], ); // Check combined array with empty string check_parser( "[1, \"\", 2]", - &[Expr::new(ExprDef::ArrayDecl(vec![ - Expr::new(ExprDef::Const(Const::Num(1.0))), - Expr::new(ExprDef::Const(Const::String(String::new()))), - Expr::new(ExprDef::Const(Const::Num(2.0))), - ]))], + &[Node::ArrayDecl(vec![ + Node::Const(Const::Num(1.0)), + Node::Const(Const::String(String::new())), + Node::Const(Const::Num(2.0)), + ])], ); } @@ -186,99 +200,81 @@ fn check_declarations() { // Check `var` declaration check_parser( "var a = 5;", - &[Expr::new(ExprDef::VarDecl(vec![( + &[Node::VarDecl(vec![( String::from("a"), - Some(Expr::new(ExprDef::Const(Const::Num(5.0)))), - )]))], + Some(Node::Const(Const::Num(5.0))), + )])], ); // Check `var` declaration with no spaces check_parser( "var a=5;", - &[Expr::new(ExprDef::VarDecl(vec![( + &[Node::VarDecl(vec![( String::from("a"), - Some(Expr::new(ExprDef::Const(Const::Num(5.0)))), - )]))], + Some(Node::Const(Const::Num(5.0))), + )])], ); // Check empty `var` declaration - check_parser( - "var a;", - &[Expr::new(ExprDef::VarDecl(vec![(String::from("a"), None)]))], - ); + check_parser("var a;", &[Node::VarDecl(vec![(String::from("a"), None)])]); // Check multiple `var` declaration check_parser( "var a = 5, b, c = 6;", - &[Expr::new(ExprDef::VarDecl(vec![ - ( - String::from("a"), - Some(Expr::new(ExprDef::Const(Const::Num(5.0)))), - ), + &[Node::VarDecl(vec![ + (String::from("a"), Some(Node::Const(Const::Num(5.0)))), (String::from("b"), None), - ( - String::from("c"), - Some(Expr::new(ExprDef::Const(Const::Num(6.0)))), - ), - ]))], + (String::from("c"), Some(Node::Const(Const::Num(6.0)))), + ])], ); // Check `let` declaration check_parser( "let a = 5;", - &[Expr::new(ExprDef::LetDecl(vec![( + &[Node::LetDecl(vec![( String::from("a"), - Some(Expr::new(ExprDef::Const(Const::Num(5.0)))), - )]))], + Some(Node::Const(Const::Num(5.0))), + )])], ); // Check `let` declaration with no spaces check_parser( "let a=5;", - &[Expr::new(ExprDef::LetDecl(vec![( + &[Node::LetDecl(vec![( String::from("a"), - Some(Expr::new(ExprDef::Const(Const::Num(5.0)))), - )]))], + Some(Node::Const(Const::Num(5.0))), + )])], ); // Check empty `let` declaration - check_parser( - "let a;", - &[Expr::new(ExprDef::LetDecl(vec![(String::from("a"), None)]))], - ); + check_parser("let a;", &[Node::LetDecl(vec![(String::from("a"), None)])]); // Check multiple `let` declaration check_parser( "let a = 5, b, c = 6;", - &[Expr::new(ExprDef::LetDecl(vec![ - ( - String::from("a"), - Some(Expr::new(ExprDef::Const(Const::Num(5.0)))), - ), + &[Node::LetDecl(vec![ + (String::from("a"), Some(Node::Const(Const::Num(5.0)))), (String::from("b"), None), - ( - String::from("c"), - Some(Expr::new(ExprDef::Const(Const::Num(6.0)))), - ), - ]))], + (String::from("c"), Some(Node::Const(Const::Num(6.0)))), + ])], ); // Check `const` declaration check_parser( "const a = 5;", - &[Expr::new(ExprDef::ConstDecl(vec![( + &[Node::ConstDecl(vec![( String::from("a"), - Expr::new(ExprDef::Const(Const::Num(5.0))), - )]))], + Node::Const(Const::Num(5.0)), + )])], ); // Check `const` declaration with no spaces check_parser( "const a=5;", - &[Expr::new(ExprDef::ConstDecl(vec![( + &[Node::ConstDecl(vec![( String::from("a"), - Expr::new(ExprDef::Const(Const::Num(5.0))), - )]))], + Node::Const(Const::Num(5.0)), + )])], ); // Check empty `const` declaration @@ -287,16 +283,10 @@ fn check_declarations() { // Check multiple `const` declaration check_parser( "const a = 5, c = 6;", - &[Expr::new(ExprDef::ConstDecl(vec![ - ( - String::from("a"), - Expr::new(ExprDef::Const(Const::Num(5.0))), - ), - ( - String::from("c"), - Expr::new(ExprDef::Const(Const::Num(6.0))), - ), - ]))], + &[Node::ConstDecl(vec![ + (String::from("a"), Node::Const(Const::Num(5.0))), + (String::from("c"), Node::Const(Const::Num(6.0))), + ])], ); } @@ -304,8 +294,8 @@ fn check_declarations() { fn check_operations() { use crate::syntax::ast::{constant::Const, op::BinOp}; - fn create_bin_op(op: BinOp, exp1: Expr, exp2: Expr) -> Expr { - Expr::new(ExprDef::BinOp(op, Box::new(exp1), Box::new(exp2))) + fn create_bin_op(op: BinOp, exp1: Node, exp2: Node) -> Node { + Node::BinOp(op, Box::new(exp1), Box::new(exp2)) } // Check numeric operations @@ -313,96 +303,96 @@ fn check_operations() { "a + b", &[create_bin_op( BinOp::Num(NumOp::Add), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a+1", &[create_bin_op( BinOp::Num(NumOp::Add), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Const(Const::Num(1.0))), + Node::Local(String::from("a")), + Node::Const(Const::Num(1.0)), )], ); check_parser( "a - b", &[create_bin_op( BinOp::Num(NumOp::Sub), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a-1", &[create_bin_op( BinOp::Num(NumOp::Sub), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Const(Const::Num(1.0))), + Node::Local(String::from("a")), + Node::Const(Const::Num(1.0)), )], ); check_parser( "a / b", &[create_bin_op( BinOp::Num(NumOp::Div), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a/2", &[create_bin_op( BinOp::Num(NumOp::Div), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Const(Const::Num(2.0))), + Node::Local(String::from("a")), + Node::Const(Const::Num(2.0)), )], ); check_parser( "a * b", &[create_bin_op( BinOp::Num(NumOp::Mul), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a*2", &[create_bin_op( BinOp::Num(NumOp::Mul), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Const(Const::Num(2.0))), + Node::Local(String::from("a")), + Node::Const(Const::Num(2.0)), )], ); check_parser( "a ** b", &[create_bin_op( - BinOp::Num(NumOp::Pow), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + BinOp::Num(NumOp::Exp), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a**2", &[create_bin_op( - BinOp::Num(NumOp::Pow), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Const(Const::Num(2.0))), + BinOp::Num(NumOp::Exp), + Node::Local(String::from("a")), + Node::Const(Const::Num(2.0)), )], ); check_parser( "a % b", &[create_bin_op( BinOp::Num(NumOp::Mod), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a%2", &[create_bin_op( BinOp::Num(NumOp::Mod), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Const(Const::Num(2.0))), + Node::Local(String::from("a")), + Node::Const(Const::Num(2.0)), )], ); @@ -411,21 +401,20 @@ fn check_operations() { "a + d*(b-3)+1", &[create_bin_op( BinOp::Num(NumOp::Add), - Expr::new(ExprDef::Local(String::from("a"))), create_bin_op( BinOp::Num(NumOp::Add), - // FIXME: shouldn't the last addition be on the right? - Expr::new(ExprDef::Const(Const::Num(1.0))), + Node::Local(String::from("a")), create_bin_op( BinOp::Num(NumOp::Mul), - Expr::new(ExprDef::Local(String::from("d"))), + Node::Local(String::from("d")), create_bin_op( BinOp::Num(NumOp::Sub), - Expr::new(ExprDef::Local(String::from("b"))), - Expr::new(ExprDef::Const(Const::Num(3.0))), + Node::Local(String::from("b")), + Node::Const(Const::Num(3.0)), ), ), ), + Node::Const(Const::Num(1.0)), )], ); @@ -434,16 +423,16 @@ fn check_operations() { "a & b", &[create_bin_op( BinOp::Bit(BitOp::And), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a&b", &[create_bin_op( BinOp::Bit(BitOp::And), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); @@ -451,16 +440,16 @@ fn check_operations() { "a | b", &[create_bin_op( BinOp::Bit(BitOp::Or), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a|b", &[create_bin_op( BinOp::Bit(BitOp::Or), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); @@ -468,16 +457,16 @@ fn check_operations() { "a ^ b", &[create_bin_op( BinOp::Bit(BitOp::Xor), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a^b", &[create_bin_op( BinOp::Bit(BitOp::Xor), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); @@ -485,16 +474,16 @@ fn check_operations() { "a << b", &[create_bin_op( BinOp::Bit(BitOp::Shl), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a<> b", &[create_bin_op( BinOp::Bit(BitOp::Shr), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a>>b", &[create_bin_op( BinOp::Bit(BitOp::Shr), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); @@ -520,99 +509,99 @@ fn check_operations() { "a += b", &[create_bin_op( BinOp::Assign(AssignOp::Add), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a -= b", &[create_bin_op( BinOp::Assign(AssignOp::Sub), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a *= b", &[create_bin_op( BinOp::Assign(AssignOp::Mul), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a **= b", &[create_bin_op( - BinOp::Assign(AssignOp::Pow), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + BinOp::Assign(AssignOp::Exp), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a /= b", &[create_bin_op( BinOp::Assign(AssignOp::Div), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a %= b", &[create_bin_op( BinOp::Assign(AssignOp::Mod), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a &= b", &[create_bin_op( BinOp::Assign(AssignOp::And), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a |= b", &[create_bin_op( BinOp::Assign(AssignOp::Or), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a ^= b", &[create_bin_op( BinOp::Assign(AssignOp::Xor), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a <<= b", &[create_bin_op( BinOp::Assign(AssignOp::Shl), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a >>= b", &[create_bin_op( BinOp::Assign(AssignOp::Shr), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + Node::Local(String::from("a")), + Node::Local(String::from("b")), )], ); check_parser( "a %= 10 / 2", &[create_bin_op( BinOp::Assign(AssignOp::Mod), - Expr::new(ExprDef::Local(String::from("a"))), + Node::Local(String::from("a")), create_bin_op( BinOp::Num(NumOp::Div), - Expr::new(ExprDef::Const(Const::Num(10.0))), - Expr::new(ExprDef::Const(Const::Num(2.0))), + Node::Const(Const::Num(10.0)), + Node::Const(Const::Num(2.0)), ), )], ); @@ -622,118 +611,101 @@ fn check_operations() { fn check_function_declarations() { check_parser( "function foo(a) { return a; }", - &[Expr::new(ExprDef::FunctionDecl( + &[Node::FunctionDecl( Some(String::from("foo")), - vec![Expr::new(ExprDef::Local(String::from("a")))], - Box::new(Expr::new(ExprDef::Block(vec![Expr::new(ExprDef::Return( - Some(Box::new(Expr::new(ExprDef::Local(String::from("a"))))), - ))]))), - ))], + vec![FormalParameter::new(String::from("a"), None, false)], + Box::new(Node::StatementList(vec![Node::Return(Some(Box::new( + Node::Local(String::from("a")), + )))])), + )], ); check_parser( "function foo(a) { return; }", - &[Expr::new(ExprDef::FunctionDecl( + &[Node::FunctionDecl( Some(String::from("foo")), - vec![Expr::new(ExprDef::Local(String::from("a")))], - Box::new(Expr::new(ExprDef::Block(vec![Expr::new(ExprDef::Return( - None, - ))]))), - ))], + vec![FormalParameter::new(String::from("a"), None, false)], + Box::new(Node::StatementList(vec![Node::Return(None)])), + )], ); check_parser( "function foo(a) { return }", - &[Expr::new(ExprDef::FunctionDecl( + &[Node::FunctionDecl( Some(String::from("foo")), - vec![Expr::new(ExprDef::Local(String::from("a")))], - Box::new(Expr::new(ExprDef::Block(vec![Expr::new(ExprDef::Return( - None, - ))]))), - ))], + vec![FormalParameter::new(String::from("a"), None, false)], + Box::new(Node::StatementList(vec![Node::Return(None)])), + )], ); check_parser( - "function (a, ...b) {}", - &[Expr::new(ExprDef::FunctionDecl( - None, + "function foo(a, ...b) {}", + &[Node::FunctionDecl( + Some(String::from("foo")), vec![ - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::UnaryOp( - UnaryOp::Spread, - Box::new(Expr::new(ExprDef::Local(String::from("b")))), - )), + FormalParameter::new(String::from("a"), None, false), + FormalParameter::new(String::from("b"), None, true), ], - Box::new(Expr::new(ExprDef::ObjectDecl(Box::new(BTreeMap::new())))), - ))], + Box::new(Node::StatementList(Vec::new())), + )], ); check_parser( "(...a) => {}", - &[Expr::new(ExprDef::ArrowFunctionDecl( - vec![Expr::new(ExprDef::UnaryOp( - UnaryOp::Spread, - Box::new(Expr::new(ExprDef::Local(String::from("a")))), - ))], - Box::new(Expr::new(ExprDef::ObjectDecl(Box::new(BTreeMap::new())))), - ))], + &[Node::ArrowFunctionDecl( + vec![FormalParameter::new(String::from("a"), None, true)], + Box::new(Node::StatementList(Vec::new())), + )], ); check_parser( "(a, b, ...c) => {}", - &[Expr::new(ExprDef::ArrowFunctionDecl( + &[Node::ArrowFunctionDecl( vec![ - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), - Expr::new(ExprDef::UnaryOp( - UnaryOp::Spread, - Box::new(Expr::new(ExprDef::Local(String::from("c")))), - )), + FormalParameter::new(String::from("a"), None, false), + FormalParameter::new(String::from("b"), None, false), + FormalParameter::new(String::from("c"), None, true), ], - Box::new(Expr::new(ExprDef::ObjectDecl(Box::new(BTreeMap::new())))), - ))], + Box::new(Node::StatementList(Vec::new())), + )], ); check_parser( "(a, b) => { return a + b; }", - &[Expr::new(ExprDef::ArrowFunctionDecl( + &[Node::ArrowFunctionDecl( vec![ - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + FormalParameter::new(String::from("a"), None, false), + FormalParameter::new(String::from("b"), None, false), ], - Box::new(Expr::new(ExprDef::Block(vec![Expr::new(ExprDef::Return( - Some(Box::new(create_bin_op( + Box::new(Node::StatementList(vec![Node::Return(Some(Box::new( + create_bin_op( BinOp::Num(NumOp::Add), - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), - ))), - ))]))), - ))], + Node::Local(String::from("a")), + Node::Local(String::from("b")), + ), + )))])), + )], ); check_parser( "(a, b) => { return; }", - &[Expr::new(ExprDef::ArrowFunctionDecl( + &[Node::ArrowFunctionDecl( vec![ - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + FormalParameter::new(String::from("a"), None, false), + FormalParameter::new(String::from("b"), None, false), ], - Box::new(Expr::new(ExprDef::Block(vec![Expr::new(ExprDef::Return( - None, - ))]))), - ))], + Box::new(Node::StatementList(vec![Node::Return(None)])), + )], ); check_parser( "(a, b) => { return }", - &[Expr::new(ExprDef::ArrowFunctionDecl( + &[Node::ArrowFunctionDecl( vec![ - Expr::new(ExprDef::Local(String::from("a"))), - Expr::new(ExprDef::Local(String::from("b"))), + FormalParameter::new(String::from("a"), None, false), + FormalParameter::new(String::from("b"), None, false), ], - Box::new(Expr::new(ExprDef::Block(vec![Expr::new(ExprDef::Return( - None, - ))]))), - ))], + Box::new(Node::StatementList(vec![Node::Return(None)])), + )], ); } diff --git a/boa/src/wasm.rs b/boa/src/wasm.rs index c062279cf4c..2bb8f7391e6 100644 --- a/boa/src/wasm.rs +++ b/boa/src/wasm.rs @@ -1,7 +1,7 @@ use crate::{ exec::{Executor, Interpreter}, realm::Realm, - syntax::{ast::expr::Expr, lexer::Lexer, parser::Parser}, + syntax::{ast::node::Node, lexer::Lexer, parser::Parser}, }; use wasm_bindgen::prelude::*; @@ -25,11 +25,11 @@ pub fn evaluate(src: &str) -> String { let tokens = lexer.tokens; // Setup executor - let expr: Expr; + let node: Node; - match Parser::new(tokens).parse_all() { + match Parser::new(&tokens).parse_all() { Ok(v) => { - expr = v; + node = v; } Err(_v) => { log("parsing fail"); @@ -39,7 +39,7 @@ pub fn evaluate(src: &str) -> String { // Create new Realm let realm = Realm::create(); let mut engine: Interpreter = Executor::new(realm); - let result = engine.run(&expr); + let result = engine.run(&node); match result { Ok(v) => v.to_string(), Err(v) => format!("{}: {}", "error", v.to_string()), diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index 07edb568656..abd015bcee6 100644 --- a/boa_cli/Cargo.toml +++ b/boa_cli/Cargo.toml @@ -12,4 +12,4 @@ edition = "2018" [dependencies] Boa = { path = "../boa", features = ["serde-ast"], default-features = false } -structopt = "0.3.9" +structopt = "0.3.12" diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs index 1be9d783974..38741d591d0 100644 --- a/boa_cli/src/main.rs +++ b/boa_cli/src/main.rs @@ -4,7 +4,7 @@ use boa::builtins::console::log; use boa::serde_json; -use boa::syntax::ast::{expr::Expr, token::Token}; +use boa::syntax::ast::{node::Node, token::Token}; use boa::{exec::Executor, forward_val, realm::Realm}; use std::io::{self, Write}; use std::{fs::read_to_string, path::PathBuf}; @@ -91,10 +91,10 @@ fn lex_source(src: &str) -> Result, String> { /// /// Returns a error of type String with a message, /// if the token stream has a parsing error. -fn parse_tokens(tokens: Vec) -> Result { +fn parse_tokens(tokens: Vec) -> Result { use boa::syntax::parser::Parser; - Parser::new(tokens) + Parser::new(&tokens) .parse_all() .map_err(|e| format!("ParsingError: {}", e)) }