├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── ROADMAP.md ├── ffi ├── .gitignore ├── README.md ├── benches │ └── colors.js ├── lib │ └── index.js ├── native │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ └── lib.rs ├── package-lock.json ├── package.json └── test │ ├── 00.js │ └── 01-ratel.js ├── playground ├── README.md ├── package-lock.json ├── package.json └── src │ ├── client.css │ ├── client.js │ ├── favicon.ico │ └── index.html ├── ratel-codegen ├── Cargo.toml ├── benches │ └── colors.rs └── src │ ├── expression.rs │ ├── function.rs │ ├── lib.rs │ └── statement.rs ├── ratel-transformer ├── Cargo.toml ├── benches │ └── colors.rs ├── src │ ├── es2015 │ │ ├── arrow.rs │ │ └── mod.rs │ ├── es2016 │ │ └── mod.rs │ ├── lib.rs │ ├── scope.rs │ └── statement.rs └── transformer_old.rs ├── ratel-visitor ├── Cargo.toml ├── benches │ └── colors.rs └── src │ ├── expression.rs │ ├── function.rs │ ├── lib.rs │ └── statement.rs ├── ratel-wasm ├── .gitignore ├── Cargo.toml ├── package-lock.json ├── src │ └── lib.rs └── target ├── ratel ├── Cargo.toml ├── README.md ├── benches │ └── colors.rs └── src │ ├── ast │ ├── expression.rs │ ├── function.rs │ ├── literal.rs │ ├── mod.rs │ ├── node.rs │ ├── operator.rs │ ├── statement.rs │ ├── types.rs │ └── variable.rs │ ├── astgen │ ├── expression.rs │ ├── function.rs │ ├── macros.rs │ ├── mod.rs │ ├── statement.rs │ └── value.rs │ ├── error.rs │ ├── lexer │ ├── labels.rs │ ├── mod.rs │ ├── token.rs │ └── util.rs │ ├── lib.rs │ ├── module.rs │ └── parser │ ├── error.rs │ ├── expression.rs │ ├── function.rs │ ├── macros.rs │ ├── mod.rs │ ├── nested.rs │ └── statement.rs └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /Cargo.lock 3 | */target/ 4 | */Cargo.lock 5 | rls/ 6 | .vscode 7 | 8 | wasm-pack.log* 9 | bin/ 10 | pkg/ 11 | 12 | node_modules 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | .cache 17 | dist 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | cache: 4 | cargo: true 5 | 6 | rust: 7 | - stable 8 | - beta 9 | - nightly 10 | 11 | env: 12 | global: 13 | - CLIPPY_TOOLCHAIN=nightly 14 | 15 | matrix: 16 | allow_failures: 17 | - rust: nightly 18 | 19 | os: 20 | - linux 21 | 22 | before_install: 23 | - source $HOME/.nvm/nvm.sh 24 | - nvm install --lts 25 | - nvm use --lts 26 | 27 | before_script: 28 | - if [ $TRAVIS_RUST_VERSION = $CLIPPY_TOOLCHAIN ]; then rustup component add clippy-preview --toolchain=$CLIPPY_TOOLCHAIN; fi 29 | - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f 30 | 31 | script: ./test.sh 32 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "ratel", 4 | "ratel-codegen", 5 | "ratel-visitor", 6 | "ratel-transformer", 7 | "ratel-wasm", 8 | ] 9 | exclude = [ "ffi" ] 10 | 11 | [profile] 12 | release = { lto = true } 13 | bench = { lto = true } 14 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Maciej Hirsz 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Maciej Hirsz 2 | 3 | The MIT License (MIT) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Ratel](http://maciej.codes/things/ratel-400.png) 2 | 3 | # ratel-core 4 | 5 | [![Travis CI](https://travis-ci.org/ratel-rust/ratel-core.svg)](https://travis-ci.org/ratel-rust/ratel-core) 6 | [![Crates.io](https://img.shields.io/crates/v/ratel.svg)](https://crates.io/crates/ratel) 7 | [![Discord](https://img.shields.io/discord/530727712969588746.svg?logo=discord)](https://discord.gg/5YmRBvu) 8 | [![Gitter](https://img.shields.io/gitter/room/ratel-rust/Lobby.svg?logo=gitter)](https://gitter.im/ratel-rust/Lobby) 9 | 10 | **Ratel** is a high performance JavaScript to JavaScript compiler with a Rust core. Its goal is to take newest versions of JavaScript as input, and produce output that's compatible with older versions of the language. 11 | 12 | [**Online REPL with Wasm**](http://maciej.codes/ratel-wasm/), courtesy of [cmtt](https://github.com/cmtt). 13 | 14 | This repo is split in two separate folders: 15 | 16 | - `core` contains the main Rust codebase that does all the heavy lifting. 17 | - `ffi` contains the Node.js wrapper around the Rust core with [Neon](https://neon-bindings.com/) bindings. 18 | 19 | For common usage checkout the [ratel-cli](https://github.com/ratel-rust/ratel-cli) repo. 20 | 21 | ## Performance 22 | 23 | While still incomplete, the Parser part of **Ratel** can run circles around even the fastest parsers built in JavaScript, here it is compared to [Esprima](http://esprima.org/) using the ratel FFI. 24 | 25 | ![ratel chart](https://user-images.githubusercontent.com/787228/46786973-beee0c80-cd36-11e8-989a-62b92d624d38.png) 26 | 27 | The benchmarks visualized above can be executed in the `ffi` folder using the `npm run benches` command. 28 | 29 | ## Contributors 30 | 31 | This project is created and maintained by [Maciej Hirsz](https://github.com/maciejhirsz) with the help of awesome [contributors](https://github.com/ratel-rust/ratel-core/graphs/contributors). Extended thanks to: 32 | 33 | - [cmtt](https://github.com/cmtt) for work on the Node.js FFI and testing. 34 | - [Jan Schulte](https://github.com/schultyy) for the initial version of transformer and codegen. 35 | 36 | ## Logo 37 | 38 | The smirky **Ratel** by the courtesy of [A. L. Palmer](https://www.behance.net/alpalmer60b4). 39 | 40 | ## License 41 | 42 | This code is distributed under the terms of both the MIT license 43 | and the Apache License (Version 2.0), choose whatever works for you. 44 | 45 | See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. 46 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | - ~~remove rust-cli, benchmarks~~ 2 | - use Node buffers instead of strings 3 | - pass transpilation options from cli to core 4 | - additional tests for core library 5 | - Test and list which ES2015 features are not yet covered 6 | - [ ] Scientific notation for numbers 7 | - [ ] Function default parameters 8 | - [ ] Regular expressions 9 | - [ ] Generator functions 10 | - [ ] one-line for loops 11 | - [ ] Template strings 12 | - [ ] Destructing 13 | - ... 14 | - implement tests in Rust 15 | - implement tests in JS 16 | - webpack loader in separate repository 17 | - transform ratel AST to JavaScript AST 18 | -------------------------------------------------------------------------------- /ffi/.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | Cargo.lock 3 | native/target 4 | native/index.node 5 | native/artifacts.json 6 | **/*~ 7 | **/node_modules 8 | **/.DS_Store 9 | -------------------------------------------------------------------------------- /ffi/README.md: -------------------------------------------------------------------------------- 1 | # Node.js ffi for the `ratel` crate 2 | 3 | ## Test: 4 | 5 | ``` 6 | npm test 7 | ``` 8 | 9 | ## Benches: 10 | 11 | ``` 12 | npm run benches 13 | ``` 14 | -------------------------------------------------------------------------------- /ffi/benches/colors.js: -------------------------------------------------------------------------------- 1 | const SOURCE = ` 2 | 'use strict'; 3 | 4 | /** 5 | * Extract red color out of a color integer: 6 | * 7 | * 0x00DEAD -> 0x00 8 | * 9 | * @param {Number} color 10 | * @return {Number} 11 | */ 12 | function red( color ) 13 | { 14 | let foo = 3.14; 15 | return color >> 16; 16 | } 17 | 18 | /** 19 | * Extract green out of a color integer: 20 | * 21 | * 0x00DEAD -> 0xDE 22 | * 23 | * @param {Number} color 24 | * @return {Number} 25 | */ 26 | function green( color ) 27 | { 28 | return ( color >> 8 ) & 0xFF; 29 | } 30 | 31 | /** 32 | * Extract blue color out of a color integer: 33 | * 34 | * 0x00DEAD -> 0xAD 35 | * 36 | * @param {Number} color 37 | * @return {Number} 38 | */ 39 | function blue( color ) 40 | { 41 | return color & 0xFF; 42 | } 43 | 44 | /** 45 | * Converts an integer containing a color such as 0x00DEAD to a hex 46 | * string, such as '#00DEAD'; 47 | * 48 | * @param {Number} int 49 | * @return {String} 50 | */ 51 | function intToHex( int ) 52 | { 53 | const mask = '#000000'; 54 | 55 | const hex = int.toString( 16 ); 56 | 57 | return mask.substring( 0, 7 - hex.length ) + hex; 58 | } 59 | 60 | /** 61 | * Converts a hex string containing a color such as '#00DEAD' to 62 | * an integer, such as 0x00DEAD; 63 | * 64 | * @param {Number} num 65 | * @return {String} 66 | */ 67 | function hexToInt( hex ) 68 | { 69 | return parseInt( hex.substring( 1 ), 16 ); 70 | } 71 | 72 | module.exports = { 73 | red, 74 | green, 75 | blue, 76 | intToHex, 77 | hexToInt, 78 | }; 79 | `; 80 | 81 | const PARSERS = [ 82 | ['ratel', '../', (module) => module.ast(SOURCE, true)], 83 | ['acorn', 'acorn', (module) => module.parse(SOURCE)], 84 | ['babel', 'babylon', (module) => module.parse(SOURCE, {})], 85 | ['esformatter-parser', 'esformatter-parser', (module) => module.parse(SOURCE)], 86 | ['espree', 'espree', (module) => module.parse(SOURCE, { ecmaVersion: 2015 })], 87 | ['esprima', 'esprima', (module) => module.parseScript(SOURCE)], 88 | ['flow', 'flow-parser', (module) => module.parse(SOURCE, {})], 89 | ['recast', 'recast', (module) => module.parse(SOURCE, {})], 90 | ['typescript', 'typescript', (module) => { 91 | module.createSourceFile(__filename, SOURCE, module.ScriptTarget.Latest); 92 | }], 93 | ['@typescript-eslint/parser', '@typescript-eslint/parser', (module) => module.parse(SOURCE)] 94 | ]; 95 | 96 | suite('sourcecode', () => { 97 | PARSERS.map((args) => { 98 | const [name, requirePath, fn] = args; 99 | try { 100 | const func = fn.bind(null, require(requirePath)); 101 | bench(name, func); 102 | } catch (e) { 103 | console.log(`Cannot load ${requirePath}: ${e.message}`); 104 | } 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /ffi/lib/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../native'); 2 | -------------------------------------------------------------------------------- /ffi/native/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ffi" 3 | version = "0.0.2" 4 | authors = ["Matthias Thoemmes "] 5 | license = "MIT" 6 | build = "build.rs" 7 | exclude = ["artifacts.json", "index.node"] 8 | 9 | [lib] 10 | name = "ffi" 11 | crate-type = ["dylib"] 12 | path = "src/lib.rs" 13 | 14 | [build-dependencies] 15 | neon-build = "0.3.3" 16 | 17 | [dependencies] 18 | neon = { version = "0.3.3" } 19 | ratel = { path = "../../ratel", version = "0.8.0" } 20 | ratel-codegen = { path = "../../ratel-codegen", version = "0.8.0" } 21 | serde = "1.0" 22 | serde_json = { version = "1.0", features = ["preserve_order"] } 23 | serde_derive = "1.0" 24 | 25 | [profile.release] 26 | lto = true 27 | 28 | [profile.bench] 29 | lto = true 30 | -------------------------------------------------------------------------------- /ffi/native/build.rs: -------------------------------------------------------------------------------- 1 | extern crate neon_build; 2 | 3 | fn main() { 4 | neon_build::setup(); // must be called in build.rs 5 | } 6 | -------------------------------------------------------------------------------- /ffi/native/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate neon; 2 | extern crate ratel; 3 | extern crate ratel_codegen; 4 | extern crate serde; 5 | extern crate serde_json; 6 | 7 | use neon::prelude::*; 8 | 9 | use ratel::Module; 10 | use ratel::error::{Error, ParseError}; 11 | 12 | #[inline] 13 | fn format_errors(errors: Vec, source: Handle) -> Vec { 14 | errors 15 | .into_iter() 16 | .map(|err| { 17 | match err { 18 | Error { start, end, .. } => { 19 | ParseError::UnexpectedToken { start, end, source: source.value() } 20 | } 21 | } 22 | }) 23 | .map(|err| format!("{}", err)) 24 | .collect() 25 | } 26 | 27 | #[inline] 28 | fn generate_ast(module: &Module, minify: bool) -> Result { 29 | if minify { 30 | serde_json::to_string(&module) 31 | } else { 32 | serde_json::to_string_pretty(&module) 33 | } 34 | } 35 | 36 | fn ast(mut cx: FunctionContext) -> JsResult { 37 | if cx.len() == 0 { 38 | return cx.throw_type_error("First argument must be a string") 39 | } 40 | 41 | let source = cx.argument::(0)?; 42 | let minify = cx.argument::(1)?; 43 | 44 | let module = match ratel::parse(&source.value()) { 45 | Err(errors) => { 46 | let str = format_errors(errors, source).join("\n"); 47 | return cx.throw_type_error(&str) 48 | }, 49 | Ok(module) => module, 50 | }; 51 | 52 | let result = generate_ast(&module, minify.value()).unwrap(); 53 | 54 | Ok(cx.string(&result)) 55 | } 56 | 57 | fn transform(mut cx: FunctionContext) -> JsResult { 58 | if cx.len() == 0 { 59 | return cx.throw_type_error("First argument must be a string") 60 | } 61 | 62 | let source = cx.argument::(0)?; 63 | let minify = cx.argument::(1)?; 64 | 65 | let module = match ratel::parse(&source.value()) { 66 | Err(errors) => { 67 | let str = format_errors(errors, source).join("\n"); 68 | return cx.throw_type_error(&str) 69 | }, 70 | Ok(module) => module, 71 | }; 72 | // transformer::transform(&mut module, transformer::Settings::target_es5()); 73 | let out = ratel_codegen::codegen(&module, minify.value()); 74 | 75 | Ok(cx.string(&out)) 76 | } 77 | 78 | fn parse(mut cx: FunctionContext) -> JsResult { 79 | if cx.len() == 0 { 80 | return cx.throw_type_error("First argument must be a string") 81 | } 82 | 83 | let source = cx.argument::(0)?; 84 | 85 | let module = match ratel::parse(&source.value()) { 86 | Err(errors) => { 87 | let str = format_errors(errors, source).join("\n"); 88 | return cx.throw_type_error(&str) 89 | }, 90 | Ok(module) => module, 91 | }; 92 | 93 | let out = format!("{:?}", module.body()); 94 | 95 | Ok(cx.string(&out)) 96 | } 97 | 98 | register_module!(mut cx, { 99 | cx.export_function("transform", transform)?; 100 | cx.export_function("parse", parse)?; 101 | cx.export_function("ast", ast)?; 102 | Ok(()) 103 | }); 104 | -------------------------------------------------------------------------------- /ffi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ratel-core-ffi", 3 | "version": "0.0.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "author": "Matthias Thoemmes ", 7 | "license": "MIT", 8 | "dependencies": { 9 | "neon": "^2.0.0", 10 | "neon-cli": "^0.3.3" 11 | }, 12 | "devDependencies": { 13 | "@typescript-eslint/parser": "^2.6.1", 14 | "acorn": "^7.1.0", 15 | "babylon": "^6.18.0", 16 | "bench-runner": "^1.3.4", 17 | "esformatter-parser": "^1.0.0", 18 | "eslint": "^6.6.0", 19 | "espree": "^6.1.2", 20 | "esprima": "^4.0.1", 21 | "flow-parser": "^0.111.3", 22 | "mocha": "^6.2.2", 23 | "recast": "^0.18.5", 24 | "typescript": "^3.7.2" 25 | }, 26 | "scripts": { 27 | "install": "neon build --release", 28 | "test": "mocha", 29 | "benches": "bench-runner" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ffi/test/00.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const assert = require('assert'); 3 | const basePath = path.join.bind(path, __dirname, '..'); 4 | 5 | global.RUST_BACKTRACE = 'RUST_BACKTRACE' in process.env; 6 | 7 | global.assert = assert; 8 | global.basePath = basePath; 9 | global.path = path; 10 | global.requireRelativePath = (...path) => require(global.basePath(...path)); 11 | 12 | global.Ratel = global.requireRelativePath('.'); 13 | -------------------------------------------------------------------------------- /ffi/test/01-ratel.js: -------------------------------------------------------------------------------- 1 | const acorn = require('acorn'); 2 | 3 | describe('Ratel ffi', () => { 4 | it('is an object, has transform and parse methods', () => { 5 | assert.equal(typeof Ratel, 'object'); 6 | assert.equal(typeof Ratel.transform, 'function'); 7 | assert.equal(typeof Ratel.parse, 'function'); 8 | }); 9 | 10 | describe('transform', () => { 11 | it('throws an error without a string as first argument', () => { 12 | assert.throws(() => { 13 | Ratel.transform(); 14 | }); 15 | }); 16 | 17 | it('throws an error without a boolean as second argument', () => { 18 | assert.throws(() => { 19 | Ratel.transform(''); 20 | }); 21 | }); 22 | 23 | it('transforms', () => { 24 | // const result = Ratel.transform('2**2', true); 25 | const result = Ratel.transform('Math.pow(2, 2)', true); 26 | assert.equal(typeof result, 'string'); 27 | assert.equal(result, 'Math.pow(2,2);'); 28 | }); 29 | }); 30 | 31 | describe('parse', () => { 32 | it('throws an error without a string as first argument', () => { 33 | assert.throws(() => { 34 | Ratel.parse(); 35 | }); 36 | }); 37 | 38 | it('throws syntax errors', () => { 39 | assert.throws(() => { 40 | Ratel.parse('function function () {}'); 41 | }, /Unexpected token/); 42 | }); 43 | 44 | it('parses', () => { 45 | const result = Ratel.parse('2'); 46 | assert.equal(typeof result, 'string'); 47 | const expected = `[Loc { start: 0, end: 1, item: Expression(Loc { start: 0, end: 1, item: Literal(Number("2")) }) }]`; 48 | assert.equal(result, expected); 49 | }); 50 | }); 51 | 52 | describe('ast', () => { 53 | it('returns an AST', () => { 54 | const result = Ratel.ast('this;', false); 55 | const json = JSON.parse(result); 56 | assert.deepEqual(json, { 57 | "type": "Program", 58 | "body": [ 59 | { 60 | "type": "ExpressionStatement", 61 | "expression": { 62 | "type": "ThisExpression", 63 | "start": 0, 64 | "end": 4, 65 | }, 66 | "start": 0, 67 | "end": 4 68 | } 69 | ], 70 | "start": 0, 71 | "end": 4 72 | }); 73 | }); 74 | 75 | it('generates AST comparable with acorn', () => { 76 | const source = `const foo = 2; let bar=4; foo**bar === 16`; 77 | const ast = Ratel.ast(source, true); 78 | const tree = JSON.parse(ast); 79 | const acornAST = acorn.parse(source); 80 | delete acornAST['sourceType']; 81 | assert.deepEqual(tree, acornAST); 82 | }); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /playground/README.md: -------------------------------------------------------------------------------- 1 | # Ratel playground 2 | 3 | This is the source for the Ratel playground. It currently supports trying out parsing and transpilation inside the browser, using WebAssembly. 4 | 5 | ## Setup 6 | 7 | You should have Node.js, the Rust toolchain and [wasm-pack](https://rustwasm.github.io/wasm-pack/) installed. 8 | 9 | ``` 10 | npm install 11 | ``` 12 | 13 | ## Developing 14 | 15 | To start a development server which automatically reloads: 16 | 17 | ``` 18 | npm run start 19 | ``` 20 | 21 | To build a static version in `dist/`: 22 | 23 | ``` 24 | npm run build 25 | ``` 26 | -------------------------------------------------------------------------------- /playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ratel-playground", 3 | "version": "1.0.0", 4 | "description": "WASM playground to try Ratel", 5 | "scripts": { 6 | "start": "parcel src/index.html", 7 | "build": "parcel build src/index.html" 8 | }, 9 | "license": "Apache-2.0 OR MIT", 10 | "devDependencies": { 11 | "parcel-bundler": "^1.11.0", 12 | "parcel-plugin-wasm.rs": "^1.2.13" 13 | }, 14 | "dependencies": { 15 | "clipboard": "^2.0.4", 16 | "material-design-lite": "^1.3.0", 17 | "material-icons": "^0.2.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /playground/src/client.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Material Icons'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: url(../node_modules/material-icons/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */ 6 | src: local('Material Icons'), 7 | local('MaterialIcons-Regular'), 8 | url(../node_modules/material-icons/iconfont/MaterialIcons-Regular.woff2) format('woff2'), 9 | url(../node_modules/material-icons/iconfont/MaterialIcons-Regular.woff) format('woff'), 10 | url(../node_modules/material-icons/iconfont/MaterialIcons-Regular.ttf) format('truetype'); 11 | } 12 | 13 | .material-icons { 14 | font-family: 'Material Icons'; 15 | font-weight: normal; 16 | font-style: normal; 17 | font-size: 24px; /* Preferred icon size */ 18 | display: inline-block; 19 | line-height: 1; 20 | text-transform: none; 21 | letter-spacing: normal; 22 | word-wrap: normal; 23 | white-space: nowrap; 24 | direction: ltr; 25 | 26 | /* Support for all WebKit browsers. */ 27 | -webkit-font-smoothing: antialiased; 28 | /* Support for Safari and Chrome. */ 29 | text-rendering: optimizeLegibility; 30 | 31 | /* Support for Firefox. */ 32 | -moz-osx-font-smoothing: grayscale; 33 | 34 | /* Support for IE. */ 35 | font-feature-settings: 'liga'; 36 | } 37 | 38 | body { 39 | margin: 0; 40 | overflow: hidden; 41 | } 42 | 43 | .mdl-grid { 44 | height: 100%; 45 | } 46 | 47 | .mdl-card { 48 | width: 100%; 49 | } 50 | 51 | .mdl-card__supporting-text { 52 | display: flex; 53 | flex-grow: 1; 54 | padding: 0; 55 | width: auto; 56 | } 57 | 58 | .mdl-card__actions { 59 | padding: 1em; 60 | } 61 | 62 | .mdl-textfield { 63 | display: flex; 64 | flex-grow: 1; 65 | width: 100%; 66 | padding: 0; 67 | } 68 | 69 | .mdl-textfield__label { 70 | left: 0; 71 | top: 0; 72 | } 73 | 74 | .ratel__source { 75 | display: flex; 76 | } 77 | 78 | .ratel__output { 79 | display: flex; 80 | background-color: #eee; 81 | color: #222; 82 | } 83 | 84 | .ratel__output .mdl-card { 85 | background: none; 86 | } 87 | 88 | .ratel__fab { 89 | position: fixed; 90 | right: 1.5em; 91 | bottom: 1.5em; 92 | z-index: 20; 93 | } 94 | 95 | #source_input { 96 | display: flex; 97 | flex-grow: 1; 98 | overflow: auto; 99 | font-family: monospace; 100 | font-size: 12px; 101 | line-height: 1em; 102 | white-space: pre; 103 | width: 100%; 104 | padding: 1em; 105 | } 106 | 107 | #ast_output { 108 | display: flex; 109 | flex-grow: 1; 110 | overflow: auto; 111 | font-family: monospace; 112 | font-size: 12px; 113 | line-height: 1em; 114 | white-space: pre; 115 | width: 100%; 116 | padding: 1em; 117 | } 118 | 119 | #run_output { 120 | display: none; 121 | } 122 | 123 | #copy_output { 124 | display: none; 125 | } 126 | 127 | .ratel__source__minify { 128 | float: right; 129 | } 130 | -------------------------------------------------------------------------------- /playground/src/client.js: -------------------------------------------------------------------------------- 1 | import Clipboard from 'clipboard' 2 | import { generateAST, generateASTEstree, transform } from '../../ratel-wasm/Cargo.toml' 3 | 4 | var DISPLAY_TIMEOUT = 150; 5 | 6 | var mode = 0; 7 | var valueTimer = 0; 8 | var output = null; 9 | var minify = false; 10 | var clipboard = null; 11 | 12 | function bindListener (id, fn) { 13 | var el = document.getElementById(id); 14 | if (!el) { 15 | throw new Error('Element "' + id + '" not found'); 16 | } 17 | el.onclick = bindUpdate(fn); 18 | } 19 | 20 | function bindUpdate (fn) { 21 | return function (e) { 22 | fn(e); 23 | 24 | copy_output.style.display = mode > 0 ? 'block' : 'none'; 25 | run_output.style.display = mode === 0 ? 'block' : 'none'; 26 | 27 | var minifyElement = checkbox_0.parentElement.MaterialCheckbox; 28 | 29 | if (minify) { 30 | minifyElement.check(); 31 | } else { 32 | minifyElement.uncheck(); 33 | } 34 | 35 | for (var i = 0; i < 3; ++i) { 36 | var radioElement = document.getElementById('output_' + i); 37 | var mdlNode = radioElement.parentElement.MaterialRadio; 38 | if (mode === i) { 39 | mdlNode.check(); 40 | } else { 41 | mdlNode.uncheck(); 42 | } 43 | } 44 | 45 | onInput(source_input.value, true); 46 | }; 47 | } 48 | 49 | function onInput (value, immediate) { 50 | clearTimeout(valueTimer); 51 | valueTimer = setTimeout(function () { 52 | if (mode === 0) { 53 | output = transform(value, minify); 54 | } else if (mode === 1) { 55 | output = generateAST(value, minify); 56 | } else if (mode === 2) { 57 | output = generateASTEstree(value, minify); 58 | } 59 | ast_output.textContent = output; 60 | }, immediate ? 0 : DISPLAY_TIMEOUT); 61 | } 62 | 63 | function bindEvents () { 64 | source_input.oninput = function (e) { 65 | try { 66 | onInput(e.target.value); 67 | } catch (e) { 68 | ast_output.textContent = e.message || e; 69 | } 70 | }; 71 | 72 | run_output.onclick = function (e) { 73 | try { 74 | var out = eval(output); 75 | console.log(out); 76 | } catch (e) { 77 | console.error(e.stack); 78 | } 79 | }; 80 | 81 | bindListener('checkbox_0', function (e) { 82 | minify = e.target.checked ? 1 : 0; 83 | }); 84 | 85 | bindListener('output_0', function (e) { 86 | mode = 0; 87 | }); 88 | 89 | bindListener('output_1', function (e) { 90 | mode = 1; 91 | }); 92 | 93 | bindListener('output_2', function (e) { 94 | mode = 2; 95 | }); 96 | 97 | if (clipboard) { 98 | clipboard.on('success', function (e) { 99 | e.clearSelection(); 100 | }); 101 | 102 | clipboard.on('error', function (e) { 103 | throw e; 104 | }); 105 | } 106 | } 107 | 108 | bindUpdate(function (a) { 109 | if (Clipboard.isSupported()) { 110 | clipboard = new Clipboard('.btn'); 111 | copy_output.disabled = false; 112 | } 113 | 114 | bindEvents(); 115 | })(); -------------------------------------------------------------------------------- /playground/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratel-rust/ratel-core/e55a1310ba69a3f5ce2a9a6eef643feced02ac08/playground/src/favicon.ico -------------------------------------------------------------------------------- /playground/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ratel-wasm 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 | 38 | 39 |
40 |
41 |
42 | 43 | 47 | 48 |
49 | 53 | 57 | 61 |
62 |
63 |
64 |
65 |
66 |
67 | 70 | 73 |
74 |
75 |
76 |
77 | 78 |
79 |
80 |
81 |
82 |
83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /ratel-codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ratel-codegen" 3 | version = "0.8.0" 4 | authors = ["Maciej Hirsz "] 5 | license = "MIT/Apache-2.0" 6 | description = "JavaScript transpiler in Rust" 7 | repository = "https://github.com/ratel-rust/ratel-core" 8 | documentation = "https://github.com/ratel-rust/ratel-core" 9 | 10 | [dependencies] 11 | ratel = { path = "../ratel", version = "0.8.0" } 12 | 13 | [dev-dependencies] 14 | pretty_assertions = "0.4" 15 | -------------------------------------------------------------------------------- /ratel-codegen/benches/colors.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | extern crate ratel; 5 | extern crate ratel_codegen; 6 | 7 | use test::Bencher; 8 | 9 | static SOURCE: &'static str = r#" 10 | 11 | 'use strict'; 12 | 13 | /** 14 | * Extract red color out of a color integer: 15 | * 16 | * 0x00DEAD -> 0x00 17 | * 18 | * @param {Number} color 19 | * @return {Number} 20 | */ 21 | function red( color ) 22 | { 23 | let foo = 3.14; 24 | return color >> 16; 25 | } 26 | 27 | /** 28 | * Extract green out of a color integer: 29 | * 30 | * 0x00DEAD -> 0xDE 31 | * 32 | * @param {Number} color 33 | * @return {Number} 34 | */ 35 | function green( color ) 36 | { 37 | return ( color >> 8 ) & 0xFF; 38 | } 39 | 40 | 41 | /** 42 | * Extract blue color out of a color integer: 43 | * 44 | * 0x00DEAD -> 0xAD 45 | * 46 | * @param {Number} color 47 | * @return {Number} 48 | */ 49 | function blue( color ) 50 | { 51 | return color & 0xFF; 52 | } 53 | 54 | 55 | /** 56 | * Converts an integer containing a color such as 0x00DEAD to a hex 57 | * string, such as '#00DEAD'; 58 | * 59 | * @param {Number} int 60 | * @return {String} 61 | */ 62 | function intToHex( int ) 63 | { 64 | const mask = '#000000'; 65 | 66 | const hex = int.toString( 16 ); 67 | 68 | return mask.substring( 0, 7 - hex.length ) + hex; 69 | } 70 | 71 | 72 | /** 73 | * Converts a hex string containing a color such as '#00DEAD' to 74 | * an integer, such as 0x00DEAD; 75 | * 76 | * @param {Number} num 77 | * @return {String} 78 | */ 79 | function hexToInt( hex ) 80 | { 81 | return parseInt( hex.substring( 1 ), 16 ); 82 | } 83 | 84 | module.exports = { 85 | red, 86 | green, 87 | blue, 88 | intToHex, 89 | hexToInt, 90 | }; 91 | 92 | "#; 93 | 94 | #[bench] 95 | fn codegen_from_ast(b: &mut Bencher) { 96 | let module = ratel::parse(SOURCE).expect("Must parse"); 97 | let output = ratel_codegen::codegen(&module, true); 98 | 99 | b.bytes = output.len() as u64; 100 | 101 | b.iter(|| { 102 | ratel_codegen::codegen(&module, true) 103 | }); 104 | } 105 | -------------------------------------------------------------------------------- /ratel-codegen/src/function.rs: -------------------------------------------------------------------------------- 1 | use ratel::ast::{Function, Class, ClassMember, Name, EmptyName, MandatoryName, OptionalName, MethodKind}; 2 | 3 | use {ToCode, Generator}; 4 | 5 | 6 | impl ToCode for EmptyName { 7 | #[inline] 8 | fn to_code(&self, _: &mut G) {} 9 | } 10 | 11 | impl<'ast, G: Generator> ToCode for MandatoryName<'ast> { 12 | #[inline] 13 | fn to_code(&self, gen: &mut G) { 14 | gen.write_byte(b' '); 15 | gen.write(&self.0); 16 | } 17 | } 18 | 19 | impl<'ast, G: Generator> ToCode for OptionalName<'ast> { 20 | #[inline] 21 | fn to_code(&self, gen: &mut G) { 22 | match self.0 { 23 | Some(ref name) => { 24 | gen.write_byte(b' '); 25 | gen.write(name); 26 | }, 27 | None => gen.write_pretty(b' '), 28 | } 29 | } 30 | } 31 | 32 | pub trait ClassFunctionDeclaration { 33 | #[inline] 34 | fn write_class(gen: &mut G) { 35 | gen.write_bytes(b"class"); 36 | } 37 | 38 | #[inline] 39 | fn write_function(gen: &mut G) { 40 | gen.write_bytes(b"function"); 41 | } 42 | } 43 | 44 | impl ClassFunctionDeclaration for EmptyName { 45 | #[inline] 46 | fn write_class(_: &mut G) {} 47 | 48 | #[inline] 49 | fn write_function(_: &mut G) {} 50 | } 51 | 52 | impl<'ast, G: Generator> ClassFunctionDeclaration for OptionalName<'ast> {} 53 | impl<'ast, G: Generator> ClassFunctionDeclaration for MandatoryName<'ast> {} 54 | 55 | impl<'ast, G, N> ToCode for Function<'ast, N> where 56 | G: Generator, 57 | N: Name<'ast> + ToCode + ClassFunctionDeclaration, 58 | { 59 | #[inline] 60 | fn to_code(&self, gen: &mut G) { 61 | N::write_function(gen); 62 | gen.write(&self.name); 63 | gen.write_byte(b'('); 64 | gen.write_list(&self.params); 65 | gen.write_byte(b')'); 66 | gen.write_pretty(b' '); 67 | gen.write(&self.body); 68 | } 69 | } 70 | 71 | impl<'ast, G: Generator> ToCode for ClassMember<'ast> { 72 | #[inline] 73 | fn to_code(&self, gen: &mut G) { 74 | use ratel::ast::ClassMember::*; 75 | 76 | match *self { 77 | Error => panic!("Module contains errors"), 78 | Method { 79 | is_static, 80 | kind, 81 | ref key, 82 | ref value, 83 | } => { 84 | if is_static { 85 | gen.write_bytes(b"static "); 86 | } 87 | match kind { 88 | MethodKind::Get => gen.write_bytes(b"get "), 89 | MethodKind::Set => gen.write_bytes(b"set "), 90 | _ => {}, 91 | } 92 | gen.write(key); 93 | gen.write(value); 94 | }, 95 | Literal { 96 | is_static, 97 | ref key, 98 | ref value, 99 | } => { 100 | if is_static { 101 | gen.write_bytes(b"static "); 102 | } 103 | gen.write(key); 104 | gen.write_pretty(b' '); 105 | gen.write_byte(b'='); 106 | gen.write_pretty(b' '); 107 | gen.write(value); 108 | gen.write_byte(b';'); 109 | } 110 | } 111 | } 112 | } 113 | 114 | impl<'ast, G, N> ToCode for Class<'ast, N> where 115 | G: Generator, 116 | N: Name<'ast> + ToCode + ClassFunctionDeclaration, 117 | { 118 | #[inline] 119 | fn to_code(&self, gen: &mut G) { 120 | N::write_class(gen); 121 | gen.write(&self.name); 122 | if let Some(ref super_class) = self.extends { 123 | gen.write_bytes(b" extends "); 124 | gen.write(super_class); 125 | } 126 | gen.write_pretty(b' '); 127 | gen.write(&self.body); 128 | } 129 | } 130 | 131 | #[cfg(test)] 132 | mod test { 133 | use assert_min; 134 | 135 | #[test] 136 | fn function() { 137 | assert_min("function foo() {}", "function foo(){}"); 138 | assert_min("function foo(a) {}", "function foo(a){}"); 139 | assert_min("function foo(a, b, c) {}", "function foo(a,b,c){}"); 140 | assert_min("function foo(bar) { return 10; }", "function foo(bar){return 10;}"); 141 | } 142 | 143 | #[test] 144 | fn rest_and_spread() { 145 | assert_min("function foo(...things) { bar(...things); }", "function foo(...things){bar(...things);}"); 146 | } 147 | 148 | #[test] 149 | fn class() { 150 | assert_min("class Foo {}", "class Foo{}"); 151 | assert_min("class Foo extends Bar {}", "class Foo extends Bar{}"); 152 | assert_min("class Foo { constructor(a, b) { debug; } }", "class Foo{constructor(a,b){debug;}}"); 153 | assert_min("class Foo { static constructor(a, b) { debug; } }", "class Foo{static constructor(a,b){debug;}}"); 154 | assert_min("class Foo { method(a, b) { debug; } }", "class Foo{method(a,b){debug;}}"); 155 | assert_min("class Foo { static method(a, b) { debug; } }", "class Foo{static method(a,b){debug;}}"); 156 | assert_min("class Foo { a = 10; b = 20; }", "class Foo{a=10;b=20;}"); 157 | assert_min("class Foo { static a = 10; b = 20; }", "class Foo{static a=10;b=20;}"); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /ratel-codegen/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | #[macro_use] 3 | extern crate pretty_assertions; 4 | extern crate ratel; 5 | 6 | use ratel::ast::{Node, ExpressionNode, Loc, Block, Pattern}; 7 | use ratel::Module; 8 | 9 | mod expression; 10 | mod statement; 11 | mod function; 12 | 13 | pub trait Generator: Sized { 14 | type Output; 15 | 16 | fn consume(self) -> Self::Output; 17 | fn write_byte(&mut self, u8); 18 | fn write_bytes(&mut self, &[u8]); 19 | fn write_pretty(&mut self, u8); 20 | 21 | #[inline] 22 | fn write(&mut self, item: &T) where 23 | T: ToCode, 24 | { 25 | item.to_code(self); 26 | } 27 | 28 | /// Helper that adds parenthesis if required by the binding power of inner expression 29 | #[inline] 30 | fn write_expression<'ast>(&mut self, item: &ExpressionNode<'ast>, bp: u8) { 31 | if item.binding_power() < bp { 32 | self.write_byte(b'('); 33 | item.to_code(self); 34 | self.write_byte(b')'); 35 | } else { 36 | item.to_code(self); 37 | } 38 | } 39 | 40 | #[inline] 41 | fn write_list<'a, T, I>(&mut self, items: I) where 42 | T: ToCode + 'a, 43 | I: IntoIterator>, 44 | { 45 | let mut items = items.into_iter(); 46 | 47 | if let Some(item) = items.next() { 48 | self.write(item); 49 | } 50 | 51 | for item in items { 52 | self.write_byte(b','); 53 | self.write_pretty(b' '); 54 | self.write(item); 55 | } 56 | } 57 | 58 | #[inline] 59 | fn write_block<'a, T, I>(&mut self, items: I) where 60 | T: ToCode + 'a, 61 | I: IntoIterator>, 62 | { 63 | let mut items = items.into_iter(); 64 | 65 | match items.next() { 66 | Some(item) => { 67 | self.indent(); 68 | self.new_line(); 69 | self.write(item); 70 | }, 71 | None => return, 72 | } 73 | 74 | for item in items { 75 | self.new_line(); 76 | self.write(item); 77 | } 78 | self.dedent(); 79 | self.new_line(); 80 | } 81 | 82 | #[inline] 83 | fn new_line(&mut self) {} 84 | 85 | #[inline] 86 | fn indent(&mut self) {} 87 | 88 | #[inline] 89 | fn dedent(&mut self) {} 90 | } 91 | 92 | pub struct MinifyingGenerator { 93 | code: Vec 94 | } 95 | 96 | 97 | impl Default for MinifyingGenerator { 98 | fn default() -> Self { 99 | MinifyingGenerator { 100 | code: Vec::with_capacity(128) 101 | } 102 | } 103 | } 104 | 105 | impl Generator for MinifyingGenerator { 106 | type Output = String; 107 | 108 | fn consume(self) -> String { 109 | unsafe { String::from_utf8_unchecked(self.code) } 110 | } 111 | 112 | #[inline] 113 | fn write_byte(&mut self, ch: u8) { 114 | self.code.push(ch); 115 | } 116 | 117 | #[inline] 118 | fn write_pretty(&mut self, _: u8) {} 119 | 120 | #[inline] 121 | fn write_bytes(&mut self, slice: &[u8]) { 122 | extend_from_slice(&mut self.code, slice); 123 | } 124 | } 125 | 126 | pub struct PrettyGenerator { 127 | code: Vec, 128 | dent: usize, 129 | } 130 | 131 | impl Default for PrettyGenerator { 132 | fn default() -> Self { 133 | PrettyGenerator { 134 | code: Vec::with_capacity(128), 135 | dent: 0, 136 | } 137 | } 138 | } 139 | 140 | impl Generator for PrettyGenerator { 141 | type Output = String; 142 | 143 | fn consume(self) -> String { 144 | unsafe { String::from_utf8_unchecked(self.code) } 145 | } 146 | 147 | #[inline] 148 | fn write_byte(&mut self, ch: u8) { 149 | self.code.push(ch); 150 | } 151 | 152 | #[inline] 153 | fn write_pretty(&mut self, ch: u8) { 154 | self.code.push(ch); 155 | } 156 | 157 | #[inline] 158 | fn write_bytes(&mut self, slice: &[u8]) { 159 | extend_from_slice(&mut self.code, slice); 160 | } 161 | 162 | #[inline] 163 | fn new_line(&mut self) { 164 | self.write_byte(b'\n'); 165 | for _ in 0..self.dent { 166 | self.write_bytes(b" "); 167 | } 168 | } 169 | 170 | #[inline] 171 | fn indent(&mut self) { 172 | self.dent += 1; 173 | } 174 | 175 | #[inline] 176 | fn dedent(&mut self) { 177 | self.dent -= 1; 178 | } 179 | } 180 | 181 | pub fn codegen(module: &Module, minify: bool) -> String { 182 | if minify { 183 | let mut gen = MinifyingGenerator::default(); 184 | 185 | for statement in module.body() { 186 | gen.write(statement); 187 | } 188 | 189 | gen.consume() 190 | } else { 191 | let mut gen = PrettyGenerator::default(); 192 | let mut body = module.body().iter(); 193 | 194 | gen.write(&body.next().cloned()); 195 | 196 | for statement in body { 197 | gen.new_line(); 198 | gen.write(statement); 199 | } 200 | 201 | gen.consume() 202 | } 203 | } 204 | 205 | /// The `ToCode` trait provides an interface to pieces of grammar, that allows 206 | /// to efficiently write characters and string slices to the code `Generator`. 207 | pub trait ToCode { 208 | fn to_code(&self, gen: &mut G); 209 | } 210 | 211 | impl<'ast, G, T> ToCode for Node<'ast, T> where 212 | G: Generator, 213 | T: 'ast + ToCode, 214 | { 215 | #[inline] 216 | fn to_code(&self, gen: &mut G) { 217 | (**self).to_code(gen) 218 | } 219 | } 220 | 221 | impl ToCode for Loc where 222 | G: Generator, 223 | T: ToCode, 224 | { 225 | #[inline] 226 | fn to_code(&self, gen: &mut G) { 227 | self.item.to_code(gen) 228 | } 229 | } 230 | 231 | impl<'a, G, T> ToCode for &'a Loc where 232 | G: Generator, 233 | T: ToCode, 234 | { 235 | #[inline] 236 | fn to_code(&self, gen: &mut G) { 237 | self.item.to_code(gen) 238 | } 239 | } 240 | 241 | impl<'a, G> ToCode for &'a str where 242 | G: Generator, 243 | { 244 | #[inline] 245 | fn to_code(&self, gen: &mut G) { 246 | gen.write_bytes(self.as_bytes()) 247 | } 248 | } 249 | 250 | impl ToCode for u64 { 251 | #[inline] 252 | fn to_code(&self, gen: &mut G) { 253 | gen.write_bytes(format!("{}", self).as_str().as_bytes()) 254 | } 255 | } 256 | 257 | impl ToCode for Option where 258 | G: Generator, 259 | T: ToCode 260 | { 261 | #[inline] 262 | fn to_code(&self, gen: &mut G) { 263 | if let Some(ref val) = *self { 264 | gen.write(val); 265 | } 266 | } 267 | } 268 | 269 | impl<'ast, G, T> ToCode for Block<'ast, T> where 270 | G: Generator, 271 | T: ToCode 272 | { 273 | #[inline] 274 | fn to_code(&self, gen: &mut G) { 275 | gen.write_byte(b'{'); 276 | gen.write_block(&self.body); 277 | gen.write_byte(b'}'); 278 | } 279 | } 280 | 281 | impl<'ast, G: Generator> ToCode for Pattern<'ast> { 282 | #[inline] 283 | fn to_code(&self, gen: &mut G) { 284 | use ratel::ast::Pattern::*; 285 | 286 | match *self { 287 | Void => {}, 288 | Identifier(ref ident) => gen.write(ident), 289 | ObjectPattern { 290 | ref properties, 291 | } => { 292 | gen.write_byte(b'{'); 293 | gen.write_list(properties); 294 | gen.write_byte(b'}'); 295 | }, 296 | ArrayPattern { 297 | ref elements, 298 | } => { 299 | gen.write_byte(b'['); 300 | gen.write_list(elements); 301 | gen.write_byte(b']'); 302 | }, 303 | RestElement { 304 | ref argument, 305 | } => { 306 | gen.write_bytes(b"..."); 307 | gen.write(argument); 308 | }, 309 | AssignmentPattern { 310 | ref left, 311 | ref right, 312 | } => { 313 | gen.write(left); 314 | gen.write_pretty(b' '); 315 | gen.write_byte(b'='); 316 | gen.write_pretty(b' '); 317 | gen.write(right); 318 | } 319 | } 320 | } 321 | } 322 | 323 | // From: https://github.com/dtolnay/fastwrite/blob/master/src/lib.rs#L68 324 | // 325 | // LLVM is not able to lower `Vec::extend_from_slice` into a memcpy, so this 326 | // helps eke out that last bit of performance. 327 | #[inline] 328 | fn extend_from_slice(dst: &mut Vec, src: &[u8]) { 329 | let dst_len = dst.len(); 330 | let src_len = src.len(); 331 | 332 | dst.reserve(src_len); 333 | 334 | unsafe { 335 | // We would have failed if `reserve` overflowed 336 | dst.set_len(dst_len + src_len); 337 | 338 | ::std::ptr::copy_nonoverlapping( 339 | src.as_ptr(), 340 | dst.as_mut_ptr().add(dst_len), 341 | src_len); 342 | } 343 | } 344 | 345 | #[cfg(test)] 346 | fn assert_min(source: &str, expected: &str) { 347 | use ratel::parse; 348 | 349 | let module = parse(source).unwrap(); 350 | 351 | assert_eq!(codegen(&module, true).as_str(), expected); 352 | } 353 | 354 | #[cfg(test)] 355 | fn assert_pretty(source: &str, expected: &str) { 356 | use ratel::parse; 357 | 358 | let module = parse(source).unwrap(); 359 | 360 | assert_eq!(codegen(&module, false).as_str(), expected); 361 | } 362 | -------------------------------------------------------------------------------- /ratel-codegen/src/statement.rs: -------------------------------------------------------------------------------- 1 | use ratel::ast::{Statement, Declarator, DeclarationKind}; 2 | use ratel::ast::statement::*; 3 | 4 | use {ToCode, Generator}; 5 | use ratel::ast::Node; 6 | 7 | 8 | impl<'ast, G: Generator> ToCode for Statement<'ast> { 9 | #[inline] 10 | fn to_code(&self, gen: &mut G) { 11 | use ratel::ast::Statement::*; 12 | 13 | match *self { 14 | Empty => {}, 15 | Debugger => { 16 | gen.write_bytes(b"debugger"); 17 | }, 18 | Expression(ref expression) => { 19 | if expression.is_allowed_as_bare_statement() { 20 | gen.write(expression); 21 | } else { 22 | gen.write_byte(b'('); 23 | gen.write(expression); 24 | gen.write_byte(b')'); 25 | } 26 | gen.write_byte(b';'); 27 | }, 28 | Declaration(ref declaration) => { 29 | gen.write(declaration); 30 | gen.write_byte(b';'); 31 | }, 32 | Return(ref return_statement) => gen.write(return_statement), 33 | Break(ref break_statement) => gen.write(break_statement), 34 | Throw(ref throw) => gen.write(throw), 35 | If(ref if_statement) => gen.write(if_statement), 36 | While(ref while_statement) => gen.write(while_statement), 37 | Do(ref do_statement) => gen.write(do_statement), 38 | For(ref for_statement) => gen.write(for_statement), 39 | ForIn(ref for_in) => gen.write(for_in), 40 | ForOf(ref for_of) => gen.write(for_of), 41 | Try(ref try) => gen.write(try), 42 | Labeled(ref labeled) => gen.write(labeled), 43 | Block(ref block) => gen.write(block), 44 | Function(ref function) => gen.write(function), 45 | Class(ref class) => gen.write(class), 46 | Continue(ref cont) => gen.write(cont), 47 | Switch(ref switch) => gen.write(switch), 48 | Import(ref import) => gen.write(import) 49 | } 50 | } 51 | } 52 | 53 | impl ToCode for DeclarationKind { 54 | #[inline] 55 | fn to_code(&self, gen: &mut G) { 56 | use ratel::ast::DeclarationKind::*; 57 | 58 | match *self { 59 | Var => gen.write_bytes(b"var "), 60 | Let => gen.write_bytes(b"let "), 61 | Const => gen.write_bytes(b"const "), 62 | } 63 | } 64 | } 65 | 66 | impl<'ast, G: Generator> ToCode for Declarator<'ast> { 67 | #[inline] 68 | fn to_code(&self, gen: &mut G) { 69 | gen.write(&self.id); 70 | 71 | if let Some(ref init) = self.init { 72 | gen.write_pretty(b' '); 73 | gen.write_byte(b'='); 74 | gen.write_pretty(b' '); 75 | gen.write(init); 76 | } 77 | } 78 | } 79 | 80 | impl<'ast, G: Generator> ToCode for DeclarationStatement<'ast> { 81 | #[inline] 82 | fn to_code(&self, gen: &mut G) { 83 | gen.write(&self.kind); 84 | gen.write_list(&self.declarators); 85 | } 86 | } 87 | 88 | impl<'ast, G: Generator> ToCode for ReturnStatement<'ast> { 89 | #[inline] 90 | fn to_code(&self, gen: &mut G) { 91 | match self.value { 92 | Some(ref value) => { 93 | gen.write_bytes(b"return "); 94 | gen.write(value); 95 | gen.write_byte(b';'); 96 | }, 97 | None => gen.write_bytes(b"return;") 98 | } 99 | } 100 | } 101 | 102 | impl<'ast, G: Generator> ToCode for BreakStatement<'ast> { 103 | #[inline] 104 | fn to_code(&self, gen: &mut G) { 105 | match self.label { 106 | Some(ref label) => { 107 | gen.write_bytes(b"break "); 108 | gen.write(label); 109 | gen.write_byte(b';'); 110 | }, 111 | None => gen.write_bytes(b"break;") 112 | } 113 | } 114 | } 115 | 116 | impl<'ast, G: Generator> ToCode for ThrowStatement<'ast> { 117 | #[inline] 118 | fn to_code(&self, gen: &mut G) { 119 | gen.write_bytes(b"throw "); 120 | gen.write(&self.value); 121 | gen.write_byte(b';'); 122 | } 123 | } 124 | 125 | impl<'ast, G: Generator> ToCode for IfStatement<'ast> { 126 | #[inline] 127 | fn to_code(&self, gen: &mut G) { 128 | gen.write_bytes(b"if"); 129 | gen.write_pretty(b' '); 130 | gen.write_byte(b'('); 131 | gen.write(&self.test); 132 | gen.write_byte(b')'); 133 | gen.write_pretty(b' '); 134 | gen.write(&self.consequent); 135 | 136 | if let Some(ref alternate) = self.alternate { 137 | if self.consequent.is_block() { 138 | gen.write_pretty(b' '); 139 | } else { 140 | gen.write_byte(b' '); 141 | } 142 | gen.write_bytes(b"else"); 143 | if alternate.is_block() { 144 | gen.write_pretty(b' '); 145 | } else { 146 | gen.write_byte(b' '); 147 | } 148 | gen.write(alternate); 149 | } 150 | } 151 | } 152 | 153 | impl<'ast, G: Generator> ToCode for WhileStatement<'ast> { 154 | #[inline] 155 | fn to_code(&self, gen: &mut G) { 156 | gen.write_bytes(b"while"); 157 | gen.write_pretty(b' '); 158 | gen.write_byte(b'('); 159 | gen.write(&self.test); 160 | gen.write_byte(b')'); 161 | gen.write_pretty(b' '); 162 | gen.write(&self.body); 163 | } 164 | } 165 | 166 | impl<'ast, G: Generator> ToCode for DoStatement<'ast> { 167 | #[inline] 168 | fn to_code(&self, gen: &mut G) { 169 | gen.write_bytes(b"do"); 170 | if self.body.is_block() { 171 | gen.write_pretty(b' '); 172 | } else { 173 | gen.write_byte(b' '); 174 | } 175 | gen.write(&self.body); 176 | gen.write_bytes(b"while"); 177 | gen.write_pretty(b' '); 178 | gen.write_byte(b'('); 179 | gen.write(&self.test); 180 | gen.write_byte(b')'); 181 | } 182 | } 183 | 184 | impl<'ast, G: Generator> ToCode for ForInit<'ast> { 185 | #[inline] 186 | fn to_code(&self, gen: &mut G) { 187 | match *self { 188 | ForInit::Declaration(ref declaration) => gen.write(declaration), 189 | ForInit::Expression(ref expression) => gen.write(expression), 190 | } 191 | } 192 | } 193 | 194 | impl<'ast, G: Generator> ToCode for ForStatement<'ast> { 195 | #[inline] 196 | fn to_code(&self, gen: &mut G) { 197 | gen.write_bytes(b"for"); 198 | gen.write_pretty(b' '); 199 | gen.write_byte(b'('); 200 | gen.write(&self.init); 201 | gen.write_byte(b';'); 202 | gen.write_pretty(b' '); 203 | gen.write(&self.test); 204 | gen.write_byte(b';'); 205 | gen.write_pretty(b' '); 206 | gen.write(&self.update); 207 | gen.write_byte(b')'); 208 | gen.write_pretty(b' '); 209 | gen.write(&self.body); 210 | } 211 | } 212 | 213 | impl<'ast, G: Generator> ToCode for ForInStatement<'ast> { 214 | #[inline] 215 | fn to_code(&self, gen: &mut G) { 216 | gen.write_bytes(b"for"); 217 | gen.write_pretty(b' '); 218 | gen.write_byte(b'('); 219 | gen.write(&self.left); 220 | gen.write_bytes(b" in "); 221 | gen.write(&self.right); 222 | gen.write_byte(b')'); 223 | gen.write_pretty(b' '); 224 | gen.write(&self.body); 225 | } 226 | } 227 | 228 | impl<'ast, G: Generator> ToCode for ForOfStatement<'ast> { 229 | #[inline] 230 | fn to_code(&self, gen: &mut G) { 231 | gen.write_bytes(b"for"); 232 | gen.write_pretty(b' '); 233 | gen.write_byte(b'('); 234 | gen.write(&self.left); 235 | gen.write_bytes(b" of "); 236 | gen.write(&self.right); 237 | gen.write_byte(b')'); 238 | gen.write_pretty(b' '); 239 | gen.write(&self.body); 240 | } 241 | } 242 | 243 | impl<'ast, G: Generator> ToCode for TryStatement<'ast> { 244 | #[inline] 245 | fn to_code(&self, gen: &mut G) { 246 | gen.write_bytes(b"try"); 247 | gen.write_pretty(b' '); 248 | gen.write(&self.block); 249 | if let Some(ref handler) = self.handler { 250 | gen.write_pretty(b' '); 251 | gen.write_bytes(b"catch"); 252 | gen.write_pretty(b' '); 253 | gen.write_byte(b'('); 254 | gen.write(&handler.param); 255 | gen.write_byte(b')'); 256 | gen.write_pretty(b' '); 257 | gen.write(&handler.body); 258 | } 259 | if let Some(ref finalizer) = self.finalizer { 260 | gen.write_pretty(b' '); 261 | gen.write(finalizer); 262 | } 263 | } 264 | } 265 | 266 | impl<'ast, G: Generator> ToCode for LabeledStatement<'ast> { 267 | #[inline] 268 | fn to_code(&self, gen: &mut G) { 269 | gen.write(&self.label); 270 | gen.write_byte(b':'); 271 | gen.write_pretty(b' '); 272 | gen.write(&self.body); 273 | } 274 | } 275 | 276 | impl<'ast, G: Generator> ToCode for ContinueStatement<'ast> { 277 | #[inline] 278 | fn to_code(&self, gen: &mut G) { 279 | match self.label { 280 | Some(ref label) => { 281 | gen.write_bytes(b"continue "); 282 | gen.write(label); 283 | gen.write_byte(b';'); 284 | }, 285 | None => gen.write_bytes(b"continue;") 286 | } 287 | } 288 | } 289 | 290 | impl<'ast, G: Generator> ToCode for SwitchStatement<'ast> { 291 | #[inline] 292 | fn to_code(&self, gen: &mut G) { 293 | gen.write_bytes(b"switch"); 294 | gen.write_pretty(b' '); 295 | gen.write_byte(b'('); 296 | gen.write(&self.discriminant); 297 | gen.write_byte(b')'); 298 | gen.write(&self.cases); 299 | } 300 | } 301 | 302 | impl<'ast, G: Generator> ToCode for SwitchCase<'ast> { 303 | #[inline] 304 | fn to_code(&self, gen: &mut G) { 305 | match self.test { 306 | Some(ref test) => { 307 | gen.write_bytes(b"case "); 308 | gen.write(test); 309 | gen.write_byte(b':'); 310 | } 311 | None => gen.write_bytes(b"default:") 312 | } 313 | gen.write_block(&self.consequent); 314 | } 315 | } 316 | 317 | impl<'ast, G: Generator> ToCode for ImportDeclaration<'ast> { 318 | #[inline] 319 | fn to_code(&self, gen: &mut G) { 320 | gen.write_bytes(b"import "); 321 | if self.specifiers.is_empty() { 322 | gen.write_byte(b'\''); 323 | gen.write(&self.source); 324 | gen.write_byte(b'\''); 325 | return 326 | } 327 | 328 | fn is_import_specifier<'ast>(spec: &Node<'ast, ForImportSpecifier<'ast>>) -> bool { 329 | match spec.item { 330 | ForImportSpecifier::ImportDefaultSpecifier(_) | 331 | ForImportSpecifier::ImportNamespaceSpecifier(_) => false, 332 | ForImportSpecifier::ImportSpecifier(_) => true 333 | } 334 | }; 335 | 336 | let default_ns_specs = 337 | self.specifiers.iter().filter(|spec| !is_import_specifier(spec)).collect::>(); 338 | gen.write_list(default_ns_specs.clone()); 339 | 340 | let selective_specs = 341 | self.specifiers.iter().filter(|spec| is_import_specifier(spec)).collect::>(); 342 | if !selective_specs.is_empty() { 343 | if !default_ns_specs.is_empty() { 344 | gen.write_bytes(b","); 345 | } 346 | gen.write_byte(b'{'); 347 | gen.write_list(selective_specs); 348 | gen.write_byte(b'}'); 349 | } 350 | 351 | gen.write_bytes(b" from '"); 352 | gen.write_bytes(self.source.as_bytes()); 353 | gen.write_byte(b'\''); 354 | } 355 | } 356 | impl<'ast, G: Generator> ToCode for ForImportSpecifier<'ast> { 357 | #[inline] 358 | fn to_code(&self, gen: &mut G) { 359 | match *self { 360 | ForImportSpecifier::ImportDefaultSpecifier(import) => { 361 | gen.write(&import.local); 362 | }, 363 | ForImportSpecifier::ImportNamespaceSpecifier(import) => { 364 | gen.write_bytes(b"* as "); 365 | gen.write(&import.local); 366 | }, 367 | ForImportSpecifier::ImportSpecifier(import) => { 368 | if import.imported.item == import.local.item { 369 | gen.write(&import.imported); 370 | } else { 371 | gen.write(&import.imported); 372 | gen.write_bytes(b" as "); 373 | gen.write(&import.local); 374 | } 375 | }, 376 | } 377 | } 378 | } 379 | 380 | #[cfg(test)] 381 | mod test { 382 | use assert_min; 383 | 384 | #[test] 385 | fn debugger_statement() { 386 | assert_min("debugger", "debugger"); 387 | assert_min("debugger;", "debugger"); 388 | } 389 | 390 | #[test] 391 | fn block_statement() { 392 | assert_min("{}", "{}"); 393 | assert_min("{foo;}", "{foo;}"); 394 | } 395 | 396 | #[test] 397 | fn labeled_statement() { 398 | assert_min("foo: {}", "foo:{}"); 399 | assert_min("foo: bar;", "foo:bar;"); 400 | } 401 | 402 | #[test] 403 | fn function_statement() { 404 | assert_min("function foo() {}", "function foo(){}"); 405 | } 406 | 407 | #[test] 408 | fn declaration_statement() { 409 | assert_min("var foo;", "var foo;"); 410 | assert_min("let foo;", "let foo;"); 411 | assert_min("const foo;", "const foo;"); 412 | assert_min("var foo = 10;", "var foo=10;"); 413 | assert_min("let foo = 10;", "let foo=10;"); 414 | assert_min("const foo = 10;", "const foo=10;"); 415 | assert_min("var foo, bar;", "var foo,bar;"); 416 | assert_min("let foo, bar;", "let foo,bar;"); 417 | assert_min("const foo, bar;", "const foo,bar;"); 418 | assert_min("var foo = 10, bar = 20;", "var foo=10,bar=20;"); 419 | assert_min("let foo = 10, bar = 20;", "let foo=10,bar=20;"); 420 | assert_min("const foo = 10, bar = 20;", "const foo=10,bar=20;"); 421 | assert_min("const a = {...foo};", "const a={...foo};"); 422 | } 423 | 424 | #[test] 425 | fn if_statement() { 426 | assert_min("if (true) foo;", "if(true)foo;"); 427 | assert_min("if (true) { foo; }", "if(true){foo;}"); 428 | assert_min("if (true) foo; else bar;", "if(true)foo; else bar;"); 429 | assert_min("if (true) { foo; } else { bar; }", "if(true){foo;}else{bar;}"); 430 | assert_min("if (true) foo; else { bar; }", "if(true)foo; else{bar;}"); 431 | assert_min("if (true) { foo; } else bar;", "if(true){foo;}else bar;"); 432 | } 433 | 434 | #[test] 435 | fn while_statement() { 436 | assert_min("while (true) foo;", "while(true)foo;"); 437 | assert_min("while (true) { foo; }", "while(true){foo;}"); 438 | } 439 | 440 | #[test] 441 | fn do_statement() { 442 | assert_min("do { foo; } while (true)", "do{foo;}while(true)"); 443 | assert_min("do foo; while (true)", "do foo;while(true)"); 444 | } 445 | 446 | #[test] 447 | fn for_statement() { 448 | assert_min("for (var i = 0; i < 10; i++) {}", "for(var i=0;i<10;i++){}"); 449 | assert_min("for (i = 0; i < 10; i++) {}", "for(i=0;i<10;i++){}"); 450 | assert_min("for (;;) {}", "for(;;){}"); 451 | assert_min("for (foo in bar){}", "for(foo in bar){}"); 452 | assert_min("for (let foo in bar){}", "for(let foo in bar){}"); 453 | assert_min("for (foo of bar){}", "for(foo of bar){}"); 454 | assert_min("for (let foo of bar){}", "for(let foo of bar){}"); 455 | } 456 | 457 | #[test] 458 | fn import_statement() { 459 | assert_min("import 'fuga'", "import 'fuga'"); 460 | assert_min("import foo from 'fuga'", "import foo from 'fuga'"); 461 | assert_min("import foo,{hoge as HOGE} from 'fuga'", "import foo,{hoge as HOGE} from 'fuga'"); 462 | assert_min("import {hoge as HOGE,fuga} from 'fuga'", "import {hoge as HOGE,fuga} from 'fuga'"); 463 | } 464 | } 465 | -------------------------------------------------------------------------------- /ratel-transformer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ratel-transformer" 3 | version = "0.8.0" 4 | authors = ["Maciej Hirsz "] 5 | license = "MIT/Apache-2.0" 6 | description = "JavaScript transpiler in Rust" 7 | repository = "https://github.com/ratel-rust/ratel-core" 8 | documentation = "https://github.com/ratel-rust/ratel-core" 9 | 10 | [dependencies] 11 | ratel = { path = "../ratel", version = "0.8.0" } 12 | ratel-visitor = { path = "../ratel-visitor", version = "0.8.0" } 13 | toolshed = { version = "0.4", features = ["impl_serialize"] } 14 | 15 | [dev-dependencies] 16 | pretty_assertions = "0.4" 17 | -------------------------------------------------------------------------------- /ratel-transformer/benches/colors.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | extern crate ratel; 5 | extern crate ratel_transformer; 6 | 7 | use test::{Bencher, black_box}; 8 | 9 | static SOURCE: &'static str = r#" 10 | 11 | 'use strict'; 12 | 13 | /** 14 | * Extract red color out of a color integer: 15 | * 16 | * 0x00DEAD -> 0x00 17 | * 18 | * @param {Number} color 19 | * @return {Number} 20 | */ 21 | function red( color ) 22 | { 23 | let foo = 3.14; 24 | return color >> 16; 25 | } 26 | 27 | /** 28 | * Extract green out of a color integer: 29 | * 30 | * 0x00DEAD -> 0xDE 31 | * 32 | * @param {Number} color 33 | * @return {Number} 34 | */ 35 | function green( color ) 36 | { 37 | return ( color >> 8 ) & 0xFF; 38 | } 39 | 40 | 41 | /** 42 | * Extract blue color out of a color integer: 43 | * 44 | * 0x00DEAD -> 0xAD 45 | * 46 | * @param {Number} color 47 | * @return {Number} 48 | */ 49 | function blue( color ) 50 | { 51 | return color & 0xFF; 52 | } 53 | 54 | 55 | /** 56 | * Converts an integer containing a color such as 0x00DEAD to a hex 57 | * string, such as '#00DEAD'; 58 | * 59 | * @param {Number} int 60 | * @return {String} 61 | */ 62 | function intToHex( int ) 63 | { 64 | const mask = '#000000'; 65 | 66 | const hex = int.toString( 16 ); 67 | 68 | return mask.substring( 0, 7 - hex.length ) + hex; 69 | } 70 | 71 | 72 | /** 73 | * Converts a hex string containing a color such as '#00DEAD' to 74 | * an integer, such as 0x00DEAD; 75 | * 76 | * @param {Number} num 77 | * @return {String} 78 | */ 79 | function hexToInt( hex ) 80 | { 81 | return parseInt( hex.substring( 1 ), 16 ); 82 | } 83 | 84 | module.exports = { 85 | red, 86 | green, 87 | blue, 88 | intToHex, 89 | hexToInt, 90 | }; 91 | 92 | "#; 93 | 94 | #[bench] 95 | fn scope_analysis(b: &mut Bencher) { 96 | use ratel_transformer::scope::analyze; 97 | 98 | let module = ratel::parse(SOURCE).expect("Must parse"); 99 | let arena = module.arena(); 100 | let offset = unsafe { arena.offset() }; 101 | 102 | b.iter(|| { 103 | unsafe { arena.reset_to(offset) }; 104 | 105 | black_box(analyze(&module)); 106 | }); 107 | } 108 | -------------------------------------------------------------------------------- /ratel-transformer/src/es2015/arrow.rs: -------------------------------------------------------------------------------- 1 | use ratel::ast::{NodeList, ExpressionNode, Function, Name, OptionalName, Block}; 2 | use ratel::ast::expression::{ArrowExpression, ArrowBody}; 3 | use ratel::ast::statement::ReturnStatement; 4 | use ratel_visitor::Visitor; 5 | 6 | use TransformerCtxt; 7 | 8 | pub struct TransformArrow<'ast> { 9 | ctx: TransformerCtxt<'ast> 10 | } 11 | 12 | impl<'ast> TransformArrow<'ast> { 13 | pub fn new(ctx: TransformerCtxt<'ast>) -> TransformArrow<'ast> { 14 | TransformArrow { 15 | ctx 16 | } 17 | } 18 | } 19 | 20 | impl<'ast> Visitor<'ast> for TransformArrow<'ast> { 21 | fn on_arrow_expression(&mut self, node: &ArrowExpression<'ast>, ptr: &'ast ExpressionNode<'ast>) { 22 | let body = match node.body { 23 | ArrowBody::Block(block) => block, 24 | ArrowBody::Expression(expr) => { 25 | let ret = self.ctx.alloc_as_loc(&expr, ReturnStatement { 26 | value: Some(expr) 27 | }); 28 | 29 | self.ctx.alloc_as_loc(&ret, Block { 30 | body: NodeList::from(self.ctx.arena, ret) 31 | }) 32 | } 33 | }; 34 | 35 | self.ctx.swap(*ptr, Function { 36 | name: OptionalName::empty(), 37 | generator: false, 38 | params: node.params, 39 | body, 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ratel-transformer/src/es2015/mod.rs: -------------------------------------------------------------------------------- 1 | mod arrow; 2 | 3 | use self::arrow::TransformArrow; 4 | 5 | pub type PresetES2015<'ast> = TransformArrow<'ast>; 6 | -------------------------------------------------------------------------------- /ratel-transformer/src/es2016/mod.rs: -------------------------------------------------------------------------------- 1 | use ratel::ast::{Node, Loc, Expression, ExpressionNode, OperatorKind}; 2 | use ratel::ast::expression::{BinaryExpression, MemberExpression, CallExpression}; 3 | use ratel_visitor::Visitor; 4 | 5 | use TransformerCtxt; 6 | 7 | pub struct PresetES2016<'ast> { 8 | ctx: TransformerCtxt<'ast> 9 | } 10 | 11 | static MATH: &Loc = &Loc { 12 | start: 0, 13 | end: 0, 14 | item: Expression::Identifier("Math") 15 | }; 16 | static POW: &Loc<&str> = &Loc { 17 | start: 0, 18 | end: 0, 19 | item: "pow" 20 | }; 21 | 22 | impl<'ast> Visitor<'ast> for PresetES2016<'ast> { 23 | fn on_binary_expression(&mut self, node: &BinaryExpression<'ast>, ptr: &ExpressionNode<'ast>) { 24 | match node.operator { 25 | OperatorKind::Exponent => { 26 | let callee = self.ctx.alloc(MemberExpression { 27 | object: Node::new(MATH), 28 | property: Node::new(POW), 29 | }); 30 | let arguments = self.ctx.list([node.left, node.right]); 31 | 32 | self.ctx.swap(ptr, CallExpression { 33 | callee, 34 | arguments 35 | }); 36 | }, 37 | 38 | OperatorKind::ExponentAssign => { 39 | let callee = self.ctx.alloc(MemberExpression { 40 | object: Node::new(MATH), 41 | property: Node::new(POW), 42 | }); 43 | let arguments = self.ctx.list([node.left, node.right]); 44 | let right = self.ctx.alloc(CallExpression { 45 | callee, 46 | arguments 47 | }); 48 | 49 | self.ctx.swap(ptr, BinaryExpression { 50 | operator: OperatorKind::Assign, 51 | left: node.left, 52 | right, 53 | }); 54 | }, 55 | 56 | _ => {} 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ratel-transformer/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | #[macro_use] 3 | extern crate pretty_assertions; 4 | extern crate ratel; 5 | extern crate ratel_visitor; 6 | extern crate toolshed; 7 | 8 | use toolshed::Arena; 9 | use toolshed::list::ListBuilder; 10 | use ratel::ast::{Loc, Node, NodeList}; 11 | use ratel::Module; 12 | use ratel_visitor::{ScopeKind, Visitable}; 13 | 14 | pub mod es2015; 15 | // pub mod es2016; 16 | 17 | pub mod scope; 18 | 19 | use self::scope::Scope; 20 | 21 | #[derive(Copy, Clone)] 22 | pub struct TransformerCtxt<'ast> { 23 | pub arena: &'ast Arena, 24 | pub scope: &'ast Scope<'ast>, 25 | } 26 | 27 | impl<'ast> TransformerCtxt<'ast> { 28 | #[inline] 29 | pub fn alloc(&self, item: I) -> Node<'ast, T> where 30 | T: Copy, 31 | I: Into, 32 | { 33 | Node::new(self.arena.alloc(Loc::new(0, 0, item.into()))) 34 | } 35 | 36 | #[inline] 37 | pub fn alloc_as_loc(&self, loc: &Node<'ast, L>, item: I) -> Node<'ast, T> where 38 | T: Copy + 'ast, 39 | I: Into, 40 | { 41 | Node::new(self.arena.alloc(Loc::new(loc.start, loc.end, item.into()))) 42 | } 43 | 44 | #[inline] 45 | pub fn list(&mut self, source: I) -> NodeList<'ast, T> where 46 | T: 'ast + Copy, 47 | I: AsRef<[Node<'ast, T>]> 48 | { 49 | let mut iter = source.as_ref().iter(); 50 | 51 | let builder = match iter.next() { 52 | Some(item) => ListBuilder::new(self.arena, *item), 53 | None => return NodeList::empty(), 54 | }; 55 | 56 | for item in iter { 57 | builder.push(self.arena, *item); 58 | } 59 | 60 | builder.as_list() 61 | } 62 | 63 | #[inline] 64 | pub fn swap(&self, ptr: Node<'ast, T>, item: I) where 65 | T: Copy + 'ast, 66 | I: Into, 67 | { 68 | let new = self.arena.alloc(Loc { 69 | start: ptr.start, 70 | end: ptr.end, 71 | item: item.into() 72 | }); 73 | 74 | ptr.set(new); 75 | } 76 | } 77 | 78 | pub fn transform<'ast>(module: &'ast mut Module<'ast>) { 79 | let arena = module.arena(); 80 | 81 | let ctx = TransformerCtxt { 82 | arena, 83 | scope: arena.alloc(Scope::new(ScopeKind::Function, None)), 84 | }; 85 | 86 | module.visit_with(&mut es2015::PresetES2015::new(ctx)); 87 | } 88 | -------------------------------------------------------------------------------- /ratel-transformer/src/scope.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Debug}; 2 | 3 | use ratel::Module; 4 | use ratel::ast::{Identifier, ExpressionNode}; 5 | use ratel_visitor::{Visitable, ScopeKind, Visitor}; 6 | use toolshed::{Arena, CopyCell}; 7 | use toolshed::list::GrowableList; 8 | use toolshed::map::BloomMap; 9 | 10 | /// Traverse the AST and produce a tree of `Scope`s. 11 | #[inline] 12 | pub fn analyze<'ast>(module: &'ast Module<'ast>) -> &'ast Scope<'ast> { 13 | let mut visitor = ScopeAnalyzer::new(module.arena()); 14 | 15 | module.visit_with(&mut visitor); 16 | 17 | visitor.current.get() 18 | } 19 | 20 | pub type ReferenceData = (); 21 | 22 | #[derive(Clone, Copy)] 23 | pub struct Scope<'ast> { 24 | /// Kind of the scope. 25 | pub kind: ScopeKind, 26 | 27 | /// Whether or not the `super` keyword was used 28 | pub used_super: CopyCell, 29 | 30 | /// Whether or not the `this` keyword was used 31 | pub used_this: CopyCell, 32 | 33 | /// All references used in this scope 34 | pub used_refs: BloomMap<'ast, &'ast str, ReferenceData>, 35 | 36 | /// All references declared in this scope 37 | pub declared_refs: BloomMap<'ast, &'ast str, ReferenceData>, 38 | 39 | /// Parent scope of this scope 40 | pub parent: Option<&'ast Scope<'ast>>, 41 | 42 | /// All children of the this scope 43 | pub children: GrowableList<'ast, &'ast Scope<'ast>>, 44 | } 45 | 46 | impl<'ast> Scope<'ast> { 47 | #[inline] 48 | pub fn new(kind: ScopeKind, parent: Option<&'ast Scope<'ast>>) -> Self { 49 | Scope { 50 | kind, 51 | used_super: CopyCell::new(false), 52 | used_this: CopyCell::new(false), 53 | used_refs: BloomMap::new(), 54 | declared_refs: BloomMap::new(), 55 | parent, 56 | children: GrowableList::new(), 57 | } 58 | } 59 | 60 | #[inline] 61 | pub fn as_usize(&'ast self) -> usize { 62 | self as *const Scope as usize 63 | } 64 | 65 | #[inline] 66 | pub unsafe fn from_usize(ptr: usize) -> &'ast Self { 67 | &*(ptr as *const Scope) 68 | } 69 | } 70 | 71 | /// Need to manually implement Debug to avoid circular reference on `parent` 72 | impl<'ast> Debug for Scope<'ast> { 73 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 74 | fmt.debug_struct("Scope") 75 | .field("kind", &self.kind) 76 | .field("used_super", &self.used_super) 77 | .field("used_this", &self.used_this) 78 | .field("used_refs", &self.used_refs) 79 | .field("declared_refs", &self.declared_refs) 80 | .field("children", &self.children) 81 | .finish() 82 | } 83 | } 84 | 85 | /// Enough to check if the pointers are the same 86 | impl<'ast> PartialEq for &'ast Scope<'ast> { 87 | #[inline] 88 | fn eq(&self, other: &&'ast Scope<'ast>) -> bool { 89 | self.as_usize() == other.as_usize() 90 | } 91 | } 92 | 93 | struct ScopeAnalyzer<'ast> { 94 | arena: &'ast Arena, 95 | pub current: CopyCell<&'ast Scope<'ast>>, 96 | } 97 | 98 | impl<'ast> ScopeAnalyzer<'ast> { 99 | #[inline] 100 | fn new(arena: &'ast Arena) -> Self { 101 | let current = CopyCell::new( 102 | arena.alloc(Scope::new(ScopeKind::Function, None)) 103 | ); 104 | 105 | ScopeAnalyzer { 106 | arena, 107 | current, 108 | } 109 | } 110 | } 111 | 112 | impl<'ast> Visitor<'ast> for ScopeAnalyzer<'ast> { 113 | #[inline] 114 | fn on_enter_scope(&mut self, kind: ScopeKind) { 115 | self.current.set( 116 | self.arena.alloc(Scope::new(kind, Some(self.current.get()))) 117 | ); 118 | } 119 | 120 | #[inline] 121 | fn on_leave_scope(&mut self) { 122 | let popped = self.current.get(); 123 | 124 | self.current.set(popped.parent.unwrap()); 125 | self.current.get().children.push(self.arena, popped); 126 | } 127 | 128 | #[inline] 129 | fn on_reference_use(&mut self, ident: &Identifier<'ast>) { 130 | self.current.get().used_refs.insert(self.arena, *ident, ()); 131 | } 132 | 133 | #[inline] 134 | fn on_reference_declaration(&mut self, ident: &Identifier<'ast>) { 135 | self.current.get().declared_refs.insert(self.arena, *ident, ()); 136 | } 137 | 138 | #[inline] 139 | fn on_this_expression(&mut self, _: &ExpressionNode<'ast>) { 140 | self.current.get().used_this.set(true); 141 | } 142 | } 143 | 144 | #[cfg(test)] 145 | mod test { 146 | use super::*; 147 | use ratel::parse; 148 | 149 | #[test] 150 | fn scope_analysis() { 151 | let module = parse("function test_function(bar) { doge; { moon; } return 10; }").unwrap(); 152 | let root = analyze(&module); 153 | 154 | assert_eq!(root.parent, None); 155 | assert_eq!(root.kind, ScopeKind::Function); 156 | assert_eq!(root.used_refs.is_empty(), true); 157 | assert_eq!(root.declared_refs.contains_key("test_function"), true); 158 | 159 | let test_function = *root.children.as_list().only_element().unwrap(); 160 | 161 | assert_eq!(test_function.parent, Some(root)); 162 | assert_eq!(test_function.kind, ScopeKind::Function); 163 | assert_eq!(test_function.used_refs.contains_key("doge"), true); 164 | assert_eq!(test_function.declared_refs.contains_key("bar"), true); 165 | 166 | let moon = *test_function.children.as_list().only_element().unwrap(); 167 | 168 | assert_eq!(moon.parent, Some(test_function)); 169 | assert_eq!(moon.kind, ScopeKind::Block); 170 | assert_eq!(moon.used_refs.contains_key("moon"), true); 171 | assert_eq!(moon.declared_refs.is_empty(), true); 172 | assert_eq!(moon.children.as_list().is_empty(), true); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /ratel-transformer/src/statement.rs: -------------------------------------------------------------------------------- 1 | use transformer::{Transformer, Transformable}; 2 | use ast::{Statement, StatementNode, StatementList}; 3 | 4 | impl<'ast> Transformable<'ast> for StatementList<'ast> { 5 | #[inline] 6 | fn transform(&self, t: &Transformer) { 7 | for statement in self.ptr_iter() { 8 | statement.transform(t); 9 | } 10 | } 11 | } 12 | 13 | impl<'ast> Transformable<'ast> for StatementNode<'ast> { 14 | fn transform(&self, t: &Transformer) { 15 | use self::Statement::*; 16 | 17 | match self.item { 18 | Error => panic!("Module contains errors"), 19 | Empty => {}, 20 | Expression { 21 | ref expression 22 | } => { 23 | unimplemented!(); 24 | }, 25 | Declaration { 26 | ref kind, 27 | ref declarators, 28 | } => { 29 | unimplemented!(); 30 | }, 31 | Return { 32 | ref value, 33 | } => { 34 | unimplemented!(); 35 | }, 36 | Break { 37 | ref label, 38 | } => { 39 | unimplemented!(); 40 | }, 41 | Throw { 42 | ref value, 43 | } => { 44 | unimplemented!(); 45 | }, 46 | If { 47 | ref test, 48 | ref consequent, 49 | ref alternate, 50 | } => { 51 | unimplemented!(); 52 | }, 53 | While { 54 | ref test, 55 | ref body, 56 | } => { 57 | unimplemented!(); 58 | }, 59 | Do { 60 | ref test, 61 | ref body, 62 | } => { 63 | unimplemented!(); 64 | }, 65 | For { 66 | ref init, 67 | ref test, 68 | ref update, 69 | ref body, 70 | } => { 71 | unimplemented!(); 72 | }, 73 | ForIn { 74 | ref left, 75 | ref right, 76 | ref body, 77 | } => { 78 | unimplemented!(); 79 | }, 80 | ForOf { 81 | ref left, 82 | ref right, 83 | ref body, 84 | } => { 85 | unimplemented!(); 86 | }, 87 | Try { 88 | ref body, 89 | ref error, 90 | ref handler, 91 | } => { 92 | unimplemented!(); 93 | }, 94 | Labeled { 95 | ref label, 96 | ref body, 97 | } => { 98 | unimplemented!(); 99 | }, 100 | Block { 101 | ref body, 102 | } => { 103 | body.transform(t); 104 | }, 105 | Function { 106 | ref function, 107 | } => { 108 | unimplemented!(); 109 | }, 110 | Class { 111 | ref class, 112 | } => { 113 | unimplemented!(); 114 | }, 115 | Continue { 116 | ref label, 117 | } => { 118 | unimplemented!(); 119 | }, 120 | Switch { 121 | ref discriminant, 122 | ref cases 123 | } => { 124 | unimplemented!(); 125 | }, 126 | SwitchCase { 127 | ref test, 128 | ref consequent 129 | } => { 130 | unimplemented!(); 131 | }, 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /ratel-visitor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ratel-visitor" 3 | version = "0.8.0" 4 | authors = ["Maciej Hirsz "] 5 | license = "MIT/Apache-2.0" 6 | description = "JavaScript transpiler in Rust" 7 | repository = "https://github.com/ratel-rust/ratel-core" 8 | documentation = "https://github.com/ratel-rust/ratel-core" 9 | 10 | [dependencies] 11 | ratel = { path = "../ratel", version = "0.8.0" } 12 | 13 | [dev-dependencies] 14 | pretty_assertions = "0.4" 15 | -------------------------------------------------------------------------------- /ratel-visitor/benches/colors.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | extern crate ratel; 5 | extern crate ratel_visitor; 6 | 7 | use ratel_visitor::{Visitable, Visitor}; 8 | use test::{Bencher, black_box}; 9 | 10 | static SOURCE: &'static str = r#" 11 | 12 | 'use strict'; 13 | 14 | /** 15 | * Extract red color out of a color integer: 16 | * 17 | * 0x00DEAD -> 0x00 18 | * 19 | * @param {Number} color 20 | * @return {Number} 21 | */ 22 | function red( color ) 23 | { 24 | let foo = 3.14; 25 | return color >> 16; 26 | } 27 | 28 | /** 29 | * Extract green out of a color integer: 30 | * 31 | * 0x00DEAD -> 0xDE 32 | * 33 | * @param {Number} color 34 | * @return {Number} 35 | */ 36 | function green( color ) 37 | { 38 | return ( color >> 8 ) & 0xFF; 39 | } 40 | 41 | 42 | /** 43 | * Extract blue color out of a color integer: 44 | * 45 | * 0x00DEAD -> 0xAD 46 | * 47 | * @param {Number} color 48 | * @return {Number} 49 | */ 50 | function blue( color ) 51 | { 52 | return color & 0xFF; 53 | } 54 | 55 | 56 | /** 57 | * Converts an integer containing a color such as 0x00DEAD to a hex 58 | * string, such as '#00DEAD'; 59 | * 60 | * @param {Number} int 61 | * @return {String} 62 | */ 63 | function intToHex( int ) 64 | { 65 | const mask = '#000000'; 66 | 67 | const hex = int.toString( 16 ); 68 | 69 | return mask.substring( 0, 7 - hex.length ) + hex; 70 | } 71 | 72 | 73 | /** 74 | * Converts a hex string containing a color such as '#00DEAD' to 75 | * an integer, such as 0x00DEAD; 76 | * 77 | * @param {Number} num 78 | * @return {String} 79 | */ 80 | function hexToInt( hex ) 81 | { 82 | return parseInt( hex.substring( 1 ), 16 ); 83 | } 84 | 85 | module.exports = { 86 | red, 87 | green, 88 | blue, 89 | intToHex, 90 | hexToInt, 91 | }; 92 | 93 | "#; 94 | 95 | struct DummyStaticVisitor; 96 | 97 | impl<'ast> Visitor<'ast> for DummyStaticVisitor {} 98 | 99 | // looks like clippy mistakenly reports an issue here 100 | // even though there's an error if you change anything 101 | // TODO: resolve upstream 102 | #[cfg_attr(feature = "cargo-clippy", allow(unit_arg))] 103 | 104 | #[bench] 105 | fn empty_traverse(b: &mut Bencher) { 106 | let module = ratel::parse(SOURCE).expect("Must parse"); 107 | let arena = module.arena(); 108 | let offset = unsafe { arena.offset() }; 109 | 110 | b.iter(|| { 111 | unsafe { arena.reset_to(offset) }; 112 | 113 | black_box(module.visit_with(&mut DummyStaticVisitor)); 114 | }); 115 | } 116 | -------------------------------------------------------------------------------- /ratel-visitor/src/expression.rs: -------------------------------------------------------------------------------- 1 | use ratel::ast::{Identifier, Expression, ExpressionNode, StatementNode, Literal}; 2 | use ratel::ast::expression::*; 3 | 4 | use {Visitor, Visitable, ParentNode}; 5 | 6 | 7 | impl<'ast> Visitable<'ast> for ExpressionNode<'ast> { 8 | type Parent = StatementNode<'ast>; 9 | 10 | #[inline] 11 | fn visit_with(&'ast self, visitor: &mut V) 12 | where 13 | V: Visitor<'ast>, 14 | { 15 | use self::Expression::*; 16 | 17 | match self.item { 18 | Void => { 19 | // Void doesn't have children, we return early to avoid calling pop_parent 20 | return; 21 | }, 22 | This(_) => { 23 | visitor.on_this_expression(&self); 24 | return; 25 | }, 26 | Identifier(ref ident) => { 27 | visitor.on_identifier_expression(ident, self); 28 | visitor.push_parent(ParentNode::from(self)); 29 | ident.visit_with(visitor); 30 | }, 31 | Literal(ref literal) => { 32 | visitor.on_literal_expression(literal, self); 33 | return; 34 | }, 35 | Sequence(ref sequence) => { 36 | visitor.on_sequence_expression(sequence, self); 37 | visitor.push_parent(ParentNode::from(self)); 38 | sequence.visit_with(visitor); 39 | }, 40 | Array(ref array) => { 41 | visitor.on_array_expression(array, self); 42 | visitor.push_parent(ParentNode::from(self)); 43 | array.visit_with(visitor); 44 | }, 45 | Member(ref member) => { 46 | visitor.on_member_expression(member, self); 47 | visitor.push_parent(ParentNode::from(self)); 48 | member.visit_with(visitor); 49 | }, 50 | ComputedMember(ref computed) => { 51 | visitor.on_computed_member_expression(computed, self); 52 | visitor.push_parent(ParentNode::from(self)); 53 | computed.visit_with(visitor); 54 | }, 55 | MetaProperty(ref property) => { 56 | visitor.on_meta_property(property, self); 57 | visitor.push_parent(ParentNode::from(self)); 58 | property.visit_with(visitor); 59 | }, 60 | Call(ref call) => { 61 | visitor.on_call_expression(call, self); 62 | visitor.push_parent(ParentNode::from(self)); 63 | call.visit_with(visitor); 64 | }, 65 | Binary(ref binary) => { 66 | visitor.on_binary_expression(binary, self); 67 | visitor.push_parent(ParentNode::from(self)); 68 | binary.visit_with(visitor); 69 | }, 70 | Prefix(ref prefix) => { 71 | visitor.on_prefix_expression(prefix, self); 72 | visitor.push_parent(ParentNode::from(self)); 73 | prefix.visit_with(visitor); 74 | }, 75 | Postfix(ref postfix) => { 76 | visitor.on_postfix_expression(postfix, self); 77 | visitor.push_parent(ParentNode::from(self)); 78 | postfix.visit_with(visitor); 79 | }, 80 | Conditional(ref conditional) => { 81 | visitor.on_conditional_expression(conditional, self); 82 | visitor.push_parent(ParentNode::from(self)); 83 | conditional.visit_with(visitor); 84 | }, 85 | Template(ref template) => { 86 | visitor.on_template_literal(template, self); 87 | visitor.push_parent(ParentNode::from(self)); 88 | template.visit_with(visitor); 89 | }, 90 | TaggedTemplate(ref tagged) => { 91 | visitor.on_tagged_template_expression(tagged, self); 92 | visitor.push_parent(ParentNode::from(self)); 93 | tagged.visit_with(visitor); 94 | }, 95 | Spread(ref spread) => { 96 | visitor.on_spread_expression(spread, self); 97 | visitor.push_parent(ParentNode::from(self)); 98 | spread.visit_with(visitor); 99 | }, 100 | Arrow(ref arrow) => { 101 | visitor.on_arrow_expression(arrow, self); 102 | visitor.push_parent(ParentNode::from(self)); 103 | arrow.visit_with(visitor); 104 | }, 105 | Object(ref object) => { 106 | visitor.on_object_expression(object, self); 107 | visitor.push_parent(ParentNode::from(self)); 108 | object.visit_with(visitor); 109 | }, 110 | Function(ref function) => { 111 | visitor.on_function_expression(function, self); 112 | visitor.push_parent(ParentNode::from(self)); 113 | function.visit_with(visitor); 114 | }, 115 | Class(ref class) => { 116 | visitor.on_class_expression(class, self); 117 | visitor.push_parent(ParentNode::from(self)); 118 | class.visit_with(visitor); 119 | } 120 | } 121 | visitor.pop_parent(); 122 | } 123 | } 124 | 125 | impl<'ast> Visitable<'ast> for ThisExpression { 126 | type Parent = ExpressionNode<'ast>; 127 | 128 | #[inline] 129 | fn visit_with>(&self, _: &mut V) {} 130 | } 131 | 132 | impl<'ast> Visitable<'ast> for Identifier<'ast> { 133 | type Parent = ExpressionNode<'ast>; 134 | 135 | #[inline] 136 | fn visit_with>(&self, visitor: &mut V) { 137 | visitor.on_reference_use(self); 138 | } 139 | } 140 | 141 | impl<'ast> Visitable<'ast> for Literal<'ast> { 142 | type Parent = ExpressionNode<'ast>; 143 | 144 | #[inline] 145 | fn visit_with>(&self, _: &mut V) {} 146 | } 147 | 148 | impl<'ast> Visitable<'ast> for SequenceExpression<'ast> { 149 | type Parent = ExpressionNode<'ast>; 150 | 151 | #[inline] 152 | fn visit_with(&'ast self, visitor: &mut V) 153 | where 154 | V: Visitor<'ast>, 155 | { 156 | self.body.visit_with(visitor); 157 | } 158 | } 159 | 160 | impl<'ast> Visitable<'ast> for ArrayExpression<'ast> { 161 | type Parent = ExpressionNode<'ast>; 162 | 163 | #[inline] 164 | fn visit_with(&'ast self, visitor: &mut V) 165 | where 166 | V: Visitor<'ast>, 167 | { 168 | self.body.visit_with(visitor); 169 | } 170 | } 171 | 172 | impl<'ast> Visitable<'ast> for MemberExpression<'ast> { 173 | type Parent = ExpressionNode<'ast>; 174 | 175 | #[inline] 176 | fn visit_with(&'ast self, visitor: &mut V) 177 | where 178 | V: Visitor<'ast>, 179 | { 180 | self.object.visit_with(visitor); 181 | } 182 | } 183 | 184 | impl<'ast> Visitable<'ast> for ComputedMemberExpression<'ast> { 185 | type Parent = ExpressionNode<'ast>; 186 | 187 | #[inline] 188 | fn visit_with(&'ast self, visitor: &mut V) 189 | where 190 | V: Visitor<'ast>, 191 | { 192 | self.object.visit_with(visitor); 193 | self.property.visit_with(visitor); 194 | } 195 | } 196 | 197 | impl<'ast> Visitable<'ast> for MetaPropertyExpression<'ast> { 198 | type Parent = ExpressionNode<'ast>; 199 | 200 | #[inline] 201 | fn visit_with(&'ast self, visitor: &mut V) 202 | where 203 | V: Visitor<'ast>, 204 | { 205 | self.meta.visit_with(visitor); 206 | self.property.visit_with(visitor); 207 | } 208 | } 209 | 210 | impl<'ast> Visitable<'ast> for CallExpression<'ast> { 211 | type Parent = ExpressionNode<'ast>; 212 | 213 | #[inline] 214 | fn visit_with(&'ast self, visitor: &mut V) 215 | where 216 | V: Visitor<'ast>, 217 | { 218 | self.callee.visit_with(visitor); 219 | self.arguments.visit_with(visitor); 220 | } 221 | } 222 | 223 | impl<'ast> Visitable<'ast> for BinaryExpression<'ast> { 224 | type Parent = ExpressionNode<'ast>; 225 | 226 | #[inline] 227 | fn visit_with(&'ast self, visitor: &mut V) 228 | where 229 | V: Visitor<'ast>, 230 | { 231 | self.left.visit_with(visitor); 232 | self.right.visit_with(visitor); 233 | } 234 | } 235 | 236 | impl<'ast> Visitable<'ast> for PrefixExpression<'ast> { 237 | type Parent = ExpressionNode<'ast>; 238 | 239 | #[inline] 240 | fn visit_with(&'ast self, visitor: &mut V) 241 | where 242 | V: Visitor<'ast>, 243 | { 244 | self.operand.visit_with(visitor); 245 | } 246 | } 247 | 248 | impl<'ast> Visitable<'ast> for PostfixExpression<'ast> { 249 | type Parent = ExpressionNode<'ast>; 250 | 251 | #[inline] 252 | fn visit_with(&'ast self, visitor: &mut V) 253 | where 254 | V: Visitor<'ast>, 255 | { 256 | self.operand.visit_with(visitor); 257 | } 258 | } 259 | 260 | impl<'ast> Visitable<'ast> for ConditionalExpression<'ast> { 261 | type Parent = ExpressionNode<'ast>; 262 | 263 | #[inline] 264 | fn visit_with(&'ast self, visitor: &mut V) 265 | where 266 | V: Visitor<'ast>, 267 | { 268 | self.test.visit_with(visitor); 269 | self.consequent.visit_with(visitor); 270 | self.alternate.visit_with(visitor); 271 | } 272 | } 273 | 274 | impl<'ast> Visitable<'ast> for TemplateLiteral<'ast> { 275 | type Parent = ExpressionNode<'ast>; 276 | 277 | #[inline] 278 | fn visit_with(&'ast self, visitor: &mut V) 279 | where 280 | V: Visitor<'ast>, 281 | { 282 | self.expressions.visit_with(visitor); 283 | } 284 | } 285 | 286 | impl<'ast> Visitable<'ast> for TaggedTemplateExpression<'ast> { 287 | type Parent = ExpressionNode<'ast>; 288 | 289 | #[inline] 290 | fn visit_with(&'ast self, visitor: &mut V) 291 | where 292 | V: Visitor<'ast>, 293 | { 294 | self.tag.visit_with(visitor); 295 | self.quasi.visit_with(visitor); 296 | } 297 | } 298 | 299 | impl<'ast> Visitable<'ast> for SpreadExpression<'ast> { 300 | type Parent = ExpressionNode<'ast>; 301 | 302 | #[inline] 303 | fn visit_with(&'ast self, visitor: &mut V) 304 | where 305 | V: Visitor<'ast>, 306 | { 307 | self.argument.visit_with(visitor); 308 | } 309 | } 310 | 311 | impl<'ast> Visitable<'ast> for ArrowBody<'ast> { 312 | type Parent = ExpressionNode<'ast>; 313 | 314 | #[inline] 315 | fn visit_with(&'ast self, visitor: &mut V) 316 | where 317 | V: Visitor<'ast>, 318 | { 319 | match *self { 320 | ArrowBody::Expression(ref expression) => expression.visit_with(visitor), 321 | ArrowBody::Block(ref block) => block.body.visit_with(visitor), 322 | } 323 | } 324 | } 325 | 326 | impl<'ast> Visitable<'ast> for ArrowExpression<'ast> { 327 | type Parent = ExpressionNode<'ast>; 328 | 329 | #[inline] 330 | fn visit_with(&'ast self, visitor: &mut V) 331 | where 332 | V: Visitor<'ast>, 333 | { 334 | self.params.visit_with(visitor); 335 | self.body.visit_with(visitor); 336 | } 337 | } 338 | 339 | impl<'ast> Visitable<'ast> for ObjectExpression<'ast> { 340 | type Parent = ExpressionNode<'ast>; 341 | 342 | #[inline] 343 | fn visit_with(&'ast self, visitor: &mut V) 344 | where 345 | V: Visitor<'ast>, 346 | { 347 | self.body.visit_with(visitor); 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /ratel-visitor/src/function.rs: -------------------------------------------------------------------------------- 1 | use ratel::ast::{Function, Class, ClassMember, Name, EmptyName, OptionalName, MandatoryName}; 2 | use ratel::ast::{Node, ExpressionNode, StatementNode}; 3 | 4 | use {Visitable, Visitor, ScopeKind, NoParent}; 5 | 6 | 7 | impl<'ast> Visitable<'ast> for EmptyName { 8 | type Parent = NoParent; 9 | 10 | #[inline] 11 | fn visit_with(&'ast self, _: &mut V) 12 | where 13 | V: Visitor<'ast>, 14 | {} 15 | } 16 | 17 | impl<'ast> Visitable<'ast> for OptionalName<'ast> { 18 | type Parent = ExpressionNode<'ast>; 19 | 20 | #[inline] 21 | fn visit_with(&'ast self, _: &mut V) 22 | where 23 | V: Visitor<'ast>, 24 | {} 25 | } 26 | 27 | impl<'ast> Visitable<'ast> for MandatoryName<'ast> { 28 | type Parent = StatementNode<'ast>; 29 | 30 | #[inline] 31 | fn visit_with(&'ast self, visitor: &mut V) 32 | where 33 | V: Visitor<'ast>, 34 | { 35 | visitor.on_reference_declaration(&(self.0).item); 36 | } 37 | } 38 | 39 | impl<'ast, N> Visitable<'ast> for Function<'ast, N> 40 | where 41 | N: Visitable<'ast> + Name<'ast>, 42 | { 43 | type Parent = N::Parent; 44 | 45 | #[inline] 46 | fn visit_with(&'ast self, visitor: &mut V) 47 | where 48 | V: Visitor<'ast>, 49 | { 50 | self.name.visit_with(visitor); 51 | 52 | // Call visit on the StatementList instead of BlockNode since we 53 | // need to make sure that function parameters end up inside the block 54 | visitor.on_enter_scope(ScopeKind::Function); 55 | self.params.visit_with(visitor); 56 | self.body.body.visit_with(visitor); 57 | visitor.on_leave_scope(); 58 | } 59 | } 60 | 61 | impl<'ast> Visitable<'ast> for ClassMember<'ast> { 62 | type Parent = Node<'ast, Self>; 63 | 64 | #[inline] 65 | fn visit_with(&'ast self, visitor: &mut V) 66 | where 67 | V: Visitor<'ast>, 68 | { 69 | use self::ClassMember::*; 70 | 71 | match *self { 72 | Error => panic!("Invalid AST"), 73 | Method { 74 | ref key, 75 | ref value, 76 | .. 77 | } => { 78 | key.visit_with(visitor); 79 | value.visit_with(visitor); 80 | }, 81 | Literal { 82 | ref key, 83 | ref value, 84 | .. 85 | } => { 86 | key.visit_with(visitor); 87 | value.visit_with(visitor); 88 | }, 89 | } 90 | } 91 | } 92 | 93 | impl<'ast, N> Visitable<'ast> for Class<'ast, N> 94 | where 95 | N: Visitable<'ast> + Name<'ast>, 96 | { 97 | type Parent = N::Parent; 98 | 99 | #[inline] 100 | fn visit_with(&'ast self, visitor: &mut V) 101 | where 102 | V: Visitor<'ast>, 103 | { 104 | self.name.visit_with(visitor); 105 | self.extends.visit_with(visitor); 106 | self.body.body.visit_with(visitor); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /ratel-visitor/src/statement.rs: -------------------------------------------------------------------------------- 1 | use ratel::ast::{Node, Statement, StatementNode}; 2 | use ratel::ast::statement::*; 3 | 4 | use {Visitor, Visitable, ParentNode, ScopeKind, NoParent}; 5 | 6 | 7 | impl<'ast> Visitable<'ast> for StatementNode<'ast> { 8 | type Parent = NoParent; 9 | 10 | #[inline] 11 | fn visit_with(&'ast self, visitor: &mut V) 12 | where 13 | V: Visitor<'ast>, 14 | { 15 | use self::Statement::*; 16 | 17 | match self.item { 18 | Empty => { 19 | // EmptyStatement doesn't have children, we return early to avoid calling pop_parent 20 | return; 21 | }, 22 | Debugger => { 23 | return; 24 | }, 25 | Expression(ref expression) => { 26 | visitor.on_expression_statement(expression, self); 27 | visitor.push_parent(ParentNode::from(self)); 28 | expression.visit_with(visitor); 29 | }, 30 | Declaration(ref declaration) => { 31 | visitor.on_declaration_statement(declaration, self); 32 | visitor.push_parent(ParentNode::from(self)); 33 | declaration.visit_with(visitor); 34 | }, 35 | Return(ref return_statement) => { 36 | visitor.on_return_statement(return_statement, self); 37 | visitor.push_parent(ParentNode::from(self)); 38 | return_statement.visit_with(visitor); 39 | }, 40 | Break(ref break_statement) => { 41 | visitor.on_break_statement(break_statement, self); 42 | visitor.push_parent(ParentNode::from(self)); 43 | break_statement.visit_with(visitor); 44 | }, 45 | Continue(ref continue_statement) => { 46 | visitor.on_continue_statement(continue_statement, self); 47 | visitor.push_parent(ParentNode::from(self)); 48 | continue_statement.visit_with(visitor); 49 | }, 50 | Throw(ref throw) => { 51 | visitor.on_throw_statement(throw, self); 52 | visitor.push_parent(ParentNode::from(self)); 53 | throw.visit_with(visitor); 54 | }, 55 | If(ref if_statement) => { 56 | visitor.on_if_statement(if_statement, self); 57 | visitor.push_parent(ParentNode::from(self)); 58 | if_statement.visit_with(visitor); 59 | }, 60 | While(ref while_statement) => { 61 | visitor.on_while_statement(while_statement, self); 62 | visitor.push_parent(ParentNode::from(self)); 63 | while_statement.visit_with(visitor); 64 | }, 65 | Do(ref do_statement) => { 66 | visitor.on_do_statement(do_statement, self); 67 | visitor.push_parent(ParentNode::from(self)); 68 | do_statement.visit_with(visitor); 69 | }, 70 | For(ref for_statement) => { 71 | visitor.on_for_statement(for_statement, self); 72 | visitor.push_parent(ParentNode::from(self)); 73 | for_statement.visit_with(visitor); 74 | }, 75 | ForIn(ref for_in) => { 76 | visitor.on_for_in_statement(for_in, self); 77 | visitor.push_parent(ParentNode::from(self)); 78 | for_in.visit_with(visitor); 79 | }, 80 | ForOf(ref for_of) => { 81 | visitor.on_for_of_statement(for_of, self); 82 | visitor.push_parent(ParentNode::from(self)); 83 | for_of.visit_with(visitor); 84 | }, 85 | Try(ref try) => { 86 | visitor.on_try_statement(try, self); 87 | visitor.push_parent(ParentNode::from(self)); 88 | try.visit_with(visitor); 89 | }, 90 | Labeled(ref labeled) => { 91 | visitor.on_labeled_statement(labeled, self); 92 | visitor.push_parent(ParentNode::from(self)); 93 | labeled.visit_with(visitor); 94 | }, 95 | Block(ref block) => { 96 | visitor.on_block_statement(block, self); 97 | visitor.push_parent(ParentNode::from(self)); 98 | block.visit_with(visitor); 99 | }, 100 | Switch(ref switch) => { 101 | visitor.on_switch_statement(switch, self); 102 | visitor.push_parent(ParentNode::from(self)); 103 | switch.visit_with(visitor); 104 | }, 105 | Function(ref function) => { 106 | visitor.on_function_statement(function, self); 107 | visitor.push_parent(ParentNode::from(self)); 108 | function.visit_with(visitor); 109 | }, 110 | Class(ref class) => { 111 | visitor.on_class_statement(class, self); 112 | visitor.push_parent(ParentNode::from(self)); 113 | class.visit_with(visitor); 114 | }, 115 | Import(ref import) => { 116 | visitor.on_import_declaration_statement(import, self); 117 | import.visit_with(visitor); 118 | } 119 | } 120 | visitor.pop_parent(); 121 | } 122 | } 123 | 124 | impl<'ast> Visitable<'ast> for BlockStatement<'ast> { 125 | type Parent = StatementNode<'ast>; 126 | 127 | #[inline] 128 | fn visit_with(&'ast self, visitor: &mut V) 129 | where 130 | V: Visitor<'ast>, 131 | { 132 | visitor.on_enter_scope(ScopeKind::Block); 133 | self.body.visit_with(visitor); 134 | visitor.on_leave_scope(); 135 | } 136 | } 137 | 138 | impl<'ast> Visitable<'ast> for Node<'ast, BlockStatement<'ast>> { 139 | type Parent = StatementNode<'ast>; 140 | 141 | #[inline] 142 | fn visit_with(&'ast self, visitor: &mut V) 143 | where 144 | V: Visitor<'ast>, 145 | { 146 | self.item.visit_with(visitor); 147 | } 148 | } 149 | 150 | impl<'ast> Visitable<'ast> for Declarator<'ast> { 151 | type Parent = Node<'ast, Self>; 152 | 153 | #[inline] 154 | fn visit_with(&'ast self, visitor: &mut V) 155 | where 156 | V: Visitor<'ast>, 157 | { 158 | self.id.visit_with(visitor); 159 | self.init.visit_with(visitor); 160 | } 161 | } 162 | 163 | impl<'ast> Visitable<'ast> for DeclarationStatement<'ast> { 164 | type Parent = StatementNode<'ast>; 165 | 166 | #[inline] 167 | fn visit_with(&'ast self, visitor: &mut V) 168 | where 169 | V: Visitor<'ast>, 170 | { 171 | self.declarators.visit_with(visitor); 172 | } 173 | } 174 | 175 | impl<'ast> Visitable<'ast> for ReturnStatement<'ast> { 176 | type Parent = StatementNode<'ast>; 177 | 178 | #[inline] 179 | fn visit_with(&'ast self, visitor: &mut V) 180 | where 181 | V: Visitor<'ast>, 182 | { 183 | self.value.visit_with(visitor); 184 | } 185 | } 186 | 187 | impl<'ast> Visitable<'ast> for BreakStatement<'ast> { 188 | type Parent = StatementNode<'ast>; 189 | 190 | #[inline] 191 | fn visit_with>(&self, _visitor: &mut V) { 192 | // FIXME: 193 | // INTENTIONALLY KEPT EMPTY FOR NOW! 194 | // The identifier here is a label reference, _not_ a variable! 195 | } 196 | } 197 | 198 | impl<'ast> Visitable<'ast> for ContinueStatement<'ast> { 199 | type Parent = StatementNode<'ast>; 200 | 201 | #[inline] 202 | fn visit_with>(&self, _visitor: &mut V) { 203 | // FIXME: 204 | // INTENTIONALLY KEPT EMPTY FOR NOW! 205 | // The identifier here is a label reference, _not_ a variable! 206 | } 207 | } 208 | 209 | impl<'ast> Visitable<'ast> for ThrowStatement<'ast> { 210 | type Parent = StatementNode<'ast>; 211 | 212 | #[inline] 213 | fn visit_with(&'ast self, visitor: &mut V) 214 | where 215 | V: Visitor<'ast>, 216 | { 217 | self.value.visit_with(visitor); 218 | } 219 | } 220 | 221 | impl<'ast> Visitable<'ast> for IfStatement<'ast> { 222 | type Parent = StatementNode<'ast>; 223 | 224 | #[inline] 225 | fn visit_with(&'ast self, visitor: &mut V) 226 | where 227 | V: Visitor<'ast>, 228 | { 229 | self.test.visit_with(visitor); 230 | self.consequent.visit_with(visitor); 231 | self.alternate.visit_with(visitor); 232 | } 233 | } 234 | 235 | impl<'ast> Visitable<'ast> for WhileStatement<'ast> { 236 | type Parent = StatementNode<'ast>; 237 | 238 | #[inline] 239 | fn visit_with(&'ast self, visitor: &mut V) 240 | where 241 | V: Visitor<'ast>, 242 | { 243 | self.test.visit_with(visitor); 244 | self.body.visit_with(visitor); 245 | } 246 | } 247 | 248 | impl<'ast> Visitable<'ast> for DoStatement<'ast> { 249 | type Parent = StatementNode<'ast>; 250 | 251 | #[inline] 252 | fn visit_with(&'ast self, visitor: &mut V) 253 | where 254 | V: Visitor<'ast>, 255 | { 256 | self.body.visit_with(visitor); 257 | self.test.visit_with(visitor); 258 | } 259 | } 260 | 261 | impl<'ast> Visitable<'ast> for ForInit<'ast> { 262 | type Parent = Node<'ast, Self>; 263 | 264 | #[inline] 265 | fn visit_with(&'ast self, visitor: &mut V) 266 | where 267 | V: Visitor<'ast>, 268 | { 269 | match *self { 270 | ForInit::Declaration(ref declaration) => declaration.visit_with(visitor), 271 | ForInit::Expression(ref expression) => expression.visit_with(visitor), 272 | } 273 | } 274 | } 275 | 276 | impl<'ast> Visitable<'ast> for ForStatement<'ast> { 277 | type Parent = StatementNode<'ast>; 278 | 279 | #[inline] 280 | fn visit_with(&'ast self, visitor: &mut V) 281 | where 282 | V: Visitor<'ast>, 283 | { 284 | self.init.visit_with(visitor); 285 | self.test.visit_with(visitor); 286 | self.update.visit_with(visitor); 287 | self.body.visit_with(visitor); 288 | } 289 | } 290 | 291 | impl<'ast> Visitable<'ast> for ForInStatement<'ast> { 292 | type Parent = StatementNode<'ast>; 293 | 294 | #[inline] 295 | fn visit_with(&'ast self, visitor: &mut V) 296 | where 297 | V: Visitor<'ast>, 298 | { 299 | self.left.visit_with(visitor); 300 | self.right.visit_with(visitor); 301 | self.body.visit_with(visitor); 302 | } 303 | } 304 | 305 | impl<'ast> Visitable<'ast> for ForOfStatement<'ast> { 306 | type Parent = StatementNode<'ast>; 307 | 308 | #[inline] 309 | fn visit_with(&'ast self, visitor: &mut V) 310 | where 311 | V: Visitor<'ast>, 312 | { 313 | self.left.visit_with(visitor); 314 | self.right.visit_with(visitor); 315 | self.body.visit_with(visitor); 316 | } 317 | } 318 | 319 | impl<'ast> Visitable<'ast> for CatchClause<'ast> { 320 | type Parent = Node<'ast, Self>; 321 | 322 | #[inline] 323 | fn visit_with(&'ast self, visitor: &mut V) 324 | where 325 | V: Visitor<'ast>, 326 | { 327 | self.param.visit_with(visitor); 328 | self.body.visit_with(visitor); 329 | } 330 | } 331 | 332 | impl<'ast> Visitable<'ast> for TryStatement<'ast> { 333 | type Parent = StatementNode<'ast>; 334 | 335 | #[inline] 336 | fn visit_with(&'ast self, visitor: &mut V) 337 | where 338 | V: Visitor<'ast>, 339 | { 340 | self.block.visit_with(visitor); 341 | self.handler.visit_with(visitor); 342 | self.finalizer.visit_with(visitor); 343 | } 344 | } 345 | 346 | impl<'ast> Visitable<'ast> for LabeledStatement<'ast> { 347 | type Parent = StatementNode<'ast>; 348 | 349 | #[inline] 350 | fn visit_with(&'ast self, visitor: &mut V) 351 | where 352 | V: Visitor<'ast>, 353 | { 354 | // FIXME: newtype for label 355 | self.body.visit_with(visitor); 356 | } 357 | } 358 | 359 | impl<'ast> Visitable<'ast> for SwitchCase<'ast> { 360 | type Parent = Node<'ast, Self>; 361 | 362 | #[inline] 363 | fn visit_with(&'ast self, visitor: &mut V) 364 | where 365 | V: Visitor<'ast>, 366 | { 367 | self.test.visit_with(visitor); 368 | self.consequent.visit_with(visitor); 369 | } 370 | } 371 | 372 | impl<'ast> Visitable<'ast> for SwitchStatement<'ast> { 373 | type Parent = StatementNode<'ast>; 374 | 375 | #[inline] 376 | fn visit_with(&'ast self, visitor: &mut V) 377 | where 378 | V: Visitor<'ast>, 379 | { 380 | self.discriminant.visit_with(visitor); 381 | visitor.on_enter_scope(ScopeKind::Block); 382 | self.cases.body.visit_with(visitor); 383 | visitor.on_leave_scope(); 384 | } 385 | } 386 | 387 | impl<'ast> Visitable<'ast> for ImportDeclaration<'ast> { 388 | type Parent = StatementNode<'ast>; 389 | 390 | #[inline] 391 | fn visit_with(&'ast self, visitor: &mut V) 392 | where 393 | V: Visitor<'ast>, 394 | { 395 | self.source.visit_with(visitor); 396 | self.specifiers.visit_with(visitor); 397 | } 398 | } 399 | 400 | impl<'ast> Visitable<'ast> for ForImportSpecifier<'ast> { 401 | type Parent = Node<'ast, ForImportSpecifier<'ast>>; 402 | 403 | #[inline] 404 | fn visit_with(&'ast self, visitor: &mut V) 405 | where 406 | V: Visitor<'ast>, 407 | { 408 | match *self { 409 | ForImportSpecifier::ImportSpecifier(ref spec) => spec.visit_with(visitor), 410 | ForImportSpecifier::ImportDefaultSpecifier(ref spec) => spec.visit_with(visitor), 411 | ForImportSpecifier::ImportNamespaceSpecifier(ref spec) => spec.visit_with(visitor), 412 | } 413 | } 414 | } 415 | 416 | impl<'ast> Visitable<'ast> for ImportSpecifier<'ast> { 417 | type Parent = ForImportSpecifier<'ast>; 418 | 419 | #[inline] 420 | fn visit_with(&'ast self, visitor: &mut V) 421 | where 422 | V: Visitor<'ast>, 423 | { 424 | self.local.visit_with(visitor); 425 | self.imported.visit_with(visitor); 426 | } 427 | } 428 | 429 | impl<'ast> Visitable<'ast> for ImportDefaultSpecifier<'ast> { 430 | type Parent = ForImportSpecifier<'ast>; 431 | 432 | #[inline] 433 | fn visit_with(&'ast self, visitor: &mut V) 434 | where 435 | V: Visitor<'ast>, 436 | { 437 | self.local.visit_with(visitor); 438 | } 439 | } 440 | 441 | impl<'ast> Visitable<'ast> for ImportNamespaceSpecifier<'ast> { 442 | type Parent = ForImportSpecifier<'ast>; 443 | 444 | #[inline] 445 | fn visit_with(&'ast self, visitor: &mut V) 446 | where 447 | V: Visitor<'ast>, 448 | { 449 | self.local.visit_with(visitor); 450 | } 451 | } 452 | -------------------------------------------------------------------------------- /ratel-wasm/.gitignore: -------------------------------------------------------------------------------- 1 | ratel_wasm.wasm 2 | -------------------------------------------------------------------------------- /ratel-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ratel-wasm" 3 | version = "0.0.1" 4 | authors = ["Matthias Thoemmes "] 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | serde_json = "1.0" 11 | ratel = { path = "../ratel", version = "0.8.0" } 12 | ratel-codegen = { path = "../ratel-codegen", version = "0.8.0" } 13 | wasm-bindgen = "0.2.36" 14 | -------------------------------------------------------------------------------- /ratel-wasm/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1 3 | } 4 | -------------------------------------------------------------------------------- /ratel-wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate ratel; 2 | extern crate ratel_codegen; 3 | extern crate serde_json; 4 | extern crate wasm_bindgen; 5 | 6 | use wasm_bindgen::prelude::*; 7 | 8 | use ratel::error::{Error, ParseError}; 9 | 10 | fn format_errors(errors: Vec, source: &str) -> String { 11 | let error = errors 12 | .into_iter() 13 | .map(|err| { 14 | match err { 15 | Error { start, end, .. } => { 16 | ParseError::UnexpectedToken { start, end, source: source.to_string() } 17 | } 18 | } 19 | }) 20 | .map(|err| format!("{}", err)) 21 | .collect::>() 22 | .join("\n"); 23 | 24 | format!("Error: {}", error) 25 | } 26 | 27 | #[wasm_bindgen] 28 | pub fn transform(data: &str, minify: bool) -> String { 29 | match ratel::parse(&data) { 30 | Ok(module) => { 31 | ratel_codegen::codegen(&module, minify) 32 | }, 33 | Err(errors) => format_errors(errors, data) 34 | } 35 | } 36 | 37 | #[wasm_bindgen(js_name = generateAST)] 38 | pub fn generate_ast(data: &str, minify: bool) -> String { 39 | match ratel::parse(&data) { 40 | Ok(module) => { 41 | if minify { 42 | format!("{:?}", module.body()) 43 | } else { 44 | format!("{:#?}", module.body()) 45 | } 46 | }, 47 | Err(errors) => format_errors(errors, data) 48 | } 49 | } 50 | 51 | #[wasm_bindgen(js_name = generateASTEstree)] 52 | pub fn generate_ast_estree(data: &str, minify: bool) -> String { 53 | match ratel::parse(&data) { 54 | Ok(module) => { 55 | if minify { 56 | serde_json::to_string(&module).unwrap() 57 | } else { 58 | serde_json::to_string_pretty(&module).unwrap() 59 | } 60 | }, 61 | Err(errors) => format_errors(errors, data) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ratel-wasm/target: -------------------------------------------------------------------------------- 1 | ../target -------------------------------------------------------------------------------- /ratel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ratel" 3 | version = "0.8.0" 4 | authors = ["Maciej Hirsz "] 5 | license = "MIT/Apache-2.0" 6 | description = "JavaScript transpiler in Rust" 7 | repository = "https://github.com/ratel-rust/ratel-core" 8 | documentation = "https://github.com/ratel-rust/ratel-core" 9 | 10 | [dependencies] 11 | serde = "1.0" 12 | serde_derive = "1.0" 13 | toolshed = { version = "0.4", features = ["impl_serialize"] } 14 | 15 | [dev-dependencies] 16 | pretty_assertions = "0.4" 17 | serde_json = { version = "1.0", features = ["preserve_order"] } 18 | -------------------------------------------------------------------------------- /ratel/README.md: -------------------------------------------------------------------------------- 1 | # the `ratel` crate 2 | 3 | ## Test: 4 | 5 | ``` 6 | cargo test 7 | ``` 8 | 9 | ## Benchmark: 10 | 11 | ``` 12 | cargo bench 13 | ``` 14 | 15 | Benchmarks require a nightly version of Rust to work. 16 | -------------------------------------------------------------------------------- /ratel/benches/colors.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | extern crate ratel; 5 | extern crate toolshed; 6 | extern crate serde_json; 7 | 8 | use test::Bencher; 9 | 10 | static SOURCE: &'static str = r#" 11 | 12 | 'use strict'; 13 | 14 | /** 15 | * Extract red color out of a color integer: 16 | * 17 | * 0x00DEAD -> 0x00 18 | * 19 | * @param {Number} color 20 | * @return {Number} 21 | */ 22 | function red( color ) 23 | { 24 | let foo = 3.14; 25 | return color >> 16; 26 | } 27 | 28 | /** 29 | * Extract green out of a color integer: 30 | * 31 | * 0x00DEAD -> 0xDE 32 | * 33 | * @param {Number} color 34 | * @return {Number} 35 | */ 36 | function green( color ) 37 | { 38 | return ( color >> 8 ) & 0xFF; 39 | } 40 | 41 | 42 | /** 43 | * Extract blue color out of a color integer: 44 | * 45 | * 0x00DEAD -> 0xAD 46 | * 47 | * @param {Number} color 48 | * @return {Number} 49 | */ 50 | function blue( color ) 51 | { 52 | return color & 0xFF; 53 | } 54 | 55 | 56 | /** 57 | * Converts an integer containing a color such as 0x00DEAD to a hex 58 | * string, such as '#00DEAD'; 59 | * 60 | * @param {Number} int 61 | * @return {String} 62 | */ 63 | function intToHex( int ) 64 | { 65 | const mask = '#000000'; 66 | 67 | const hex = int.toString( 16 ); 68 | 69 | return mask.substring( 0, 7 - hex.length ) + hex; 70 | } 71 | 72 | 73 | /** 74 | * Converts a hex string containing a color such as '#00DEAD' to 75 | * an integer, such as 0x00DEAD; 76 | * 77 | * @param {Number} num 78 | * @return {String} 79 | */ 80 | function hexToInt( hex ) 81 | { 82 | return parseInt( hex.substring( 1 ), 16 ); 83 | } 84 | 85 | module.exports = { 86 | red, 87 | green, 88 | blue, 89 | intToHex, 90 | hexToInt, 91 | }; 92 | 93 | "#; 94 | 95 | #[bench] 96 | fn parse_to_ast(b: &mut Bencher) { 97 | b.bytes = SOURCE.len() as u64; 98 | 99 | b.iter(|| { 100 | let _module = ratel::parse(SOURCE).expect("Must parse"); 101 | }); 102 | } 103 | 104 | 105 | #[bench] 106 | fn tokenize(b: &mut Bencher) { 107 | let arena = toolshed::Arena::new(); 108 | let ptr = arena.alloc_str_with_nul(SOURCE); 109 | b.bytes = SOURCE.len() as u64; 110 | 111 | b.iter(|| { 112 | let mut lexer = unsafe { ratel::lexer::Lexer::from_ptr(ptr) }; 113 | 114 | while lexer.token != ratel::lexer::Token::EndOfProgram { 115 | lexer.consume() 116 | } 117 | }); 118 | } 119 | 120 | #[bench] 121 | fn serialize_to_json(b: &mut Bencher) { 122 | let module = ratel::parse(SOURCE).expect("Must parse"); 123 | let output = serde_json::to_string(&module).unwrap(); 124 | 125 | b.bytes = output.len() as u64; 126 | 127 | b.iter(|| { 128 | serde_json::to_string(&module).unwrap() 129 | }) 130 | } 131 | -------------------------------------------------------------------------------- /ratel/src/ast/expression.rs: -------------------------------------------------------------------------------- 1 | use ast::{Node, NodeList, Literal, OperatorKind, Function, Class, EmptyName, OptionalName}; 2 | use ast::{Identifier, IdentifierNode, BlockNode, ExpressionNode, Statement, ExpressionList, Pattern}; 3 | 4 | #[derive(Debug, PartialEq, Clone, Copy)] 5 | pub enum PropertyKey<'ast> { 6 | Computed(ExpressionNode<'ast>), 7 | Literal(&'ast str), 8 | Binary(&'ast str), 9 | } 10 | 11 | #[derive(Debug, PartialEq, Clone, Copy)] 12 | pub enum Property<'ast> { 13 | Shorthand(&'ast str), 14 | Literal { 15 | key: Node<'ast, PropertyKey<'ast>>, 16 | value: ExpressionNode<'ast>, 17 | }, 18 | Method { 19 | key: Node<'ast, PropertyKey<'ast>>, 20 | value: Node<'ast, Function<'ast, EmptyName>>, 21 | }, 22 | Spread { 23 | argument: ExpressionNode<'ast>, 24 | } 25 | } 26 | 27 | /// While not technically necessary, having a type 28 | /// helps with implementing the visitor pattern on AST. 29 | #[derive(Debug, PartialEq, Clone, Copy)] 30 | pub struct ThisExpression; 31 | 32 | #[derive(Debug, PartialEq, Clone, Copy)] 33 | pub struct SequenceExpression<'ast> { 34 | pub body: ExpressionList<'ast> 35 | } 36 | 37 | #[derive(Debug, PartialEq, Clone, Copy)] 38 | pub struct ArrayExpression<'ast> { 39 | pub body: ExpressionList<'ast> 40 | } 41 | 42 | #[derive(Debug, PartialEq, Clone, Copy)] 43 | pub struct MemberExpression<'ast> { 44 | pub object: ExpressionNode<'ast>, 45 | pub property: IdentifierNode<'ast>, 46 | } 47 | 48 | #[derive(Debug, PartialEq, Clone, Copy)] 49 | pub struct MetaPropertyExpression<'ast> { 50 | pub meta: IdentifierNode<'ast>, 51 | pub property: IdentifierNode<'ast>, 52 | } 53 | 54 | #[derive(Debug, PartialEq, Clone, Copy)] 55 | pub struct ComputedMemberExpression<'ast> { 56 | pub object: ExpressionNode<'ast>, 57 | pub property: ExpressionNode<'ast>, 58 | } 59 | 60 | #[derive(Debug, PartialEq, Clone, Copy)] 61 | pub struct CallExpression<'ast> { 62 | pub callee: ExpressionNode<'ast>, 63 | pub arguments: ExpressionList<'ast>, 64 | } 65 | 66 | #[derive(Debug, PartialEq, Clone, Copy)] 67 | pub struct BinaryExpression<'ast> { 68 | pub operator: OperatorKind, 69 | pub left: ExpressionNode<'ast>, 70 | pub right: ExpressionNode<'ast>, 71 | } 72 | 73 | #[derive(Debug, PartialEq, Clone, Copy)] 74 | pub struct PrefixExpression<'ast> { 75 | pub operator: OperatorKind, 76 | pub operand: ExpressionNode<'ast>, 77 | } 78 | 79 | #[derive(Debug, PartialEq, Clone, Copy)] 80 | pub struct PostfixExpression<'ast> { 81 | pub operator: OperatorKind, 82 | pub operand: ExpressionNode<'ast>, 83 | } 84 | 85 | #[derive(Debug, PartialEq, Clone, Copy)] 86 | pub struct ConditionalExpression<'ast> { 87 | pub test: ExpressionNode<'ast>, 88 | pub consequent: ExpressionNode<'ast>, 89 | pub alternate: ExpressionNode<'ast>, 90 | } 91 | 92 | #[derive(Debug, PartialEq, Clone, Copy)] 93 | pub struct TemplateLiteral<'ast> { 94 | pub expressions: ExpressionList<'ast>, 95 | pub quasis: NodeList<'ast, &'ast str>, 96 | } 97 | 98 | #[derive(Debug, PartialEq, Clone, Copy)] 99 | pub struct TaggedTemplateExpression<'ast> { 100 | pub tag: ExpressionNode<'ast>, 101 | pub quasi: Node<'ast, TemplateLiteral<'ast>> 102 | } 103 | 104 | #[derive(Debug, PartialEq, Clone, Copy)] 105 | pub struct SpreadExpression<'ast> { 106 | pub argument: ExpressionNode<'ast> 107 | } 108 | 109 | #[derive(Debug, PartialEq, Clone, Copy)] 110 | pub enum ArrowBody<'ast> { 111 | Expression(ExpressionNode<'ast>), 112 | Block(BlockNode<'ast, Statement<'ast>>) 113 | } 114 | 115 | #[derive(Debug, PartialEq, Clone, Copy)] 116 | pub struct ArrowExpression<'ast> { 117 | pub params: NodeList<'ast, Pattern<'ast>>, 118 | pub body: ArrowBody<'ast>, 119 | } 120 | 121 | #[derive(Debug, PartialEq, Clone, Copy)] 122 | pub struct ObjectExpression<'ast> { 123 | pub body: NodeList<'ast, Property<'ast>>, 124 | } 125 | 126 | pub type FunctionExpression<'ast> = Function<'ast, OptionalName<'ast>>; 127 | pub type ClassExpression<'ast> = Class<'ast, OptionalName<'ast>>; 128 | 129 | #[derive(Debug, PartialEq, Clone, Copy)] 130 | pub enum Expression<'ast> { 131 | Void, 132 | This(ThisExpression), 133 | Identifier(Identifier<'ast>), 134 | Literal(Literal<'ast>), 135 | Sequence(SequenceExpression<'ast>), 136 | Array(ArrayExpression<'ast>), 137 | Member(MemberExpression<'ast>), 138 | ComputedMember(ComputedMemberExpression<'ast>), 139 | MetaProperty(MetaPropertyExpression<'ast>), 140 | Call(CallExpression<'ast>), 141 | Binary(BinaryExpression<'ast>), 142 | Prefix(PrefixExpression<'ast>), 143 | Postfix(PostfixExpression<'ast>), 144 | Conditional(ConditionalExpression<'ast>), 145 | Template(TemplateLiteral<'ast>), 146 | TaggedTemplate(TaggedTemplateExpression<'ast>), 147 | Spread(SpreadExpression<'ast>), 148 | Arrow(ArrowExpression<'ast>), 149 | Object(ObjectExpression<'ast>), 150 | Function(FunctionExpression<'ast>), 151 | Class(ClassExpression<'ast>), 152 | } 153 | 154 | macro_rules! impl_from { 155 | ($( $type:ty => $variant:ident ),*) => ($( 156 | impl<'ast> From<$type> for Expression<'ast> { 157 | #[inline] 158 | fn from(val: $type) -> Expression<'ast> { 159 | Expression::$variant(val) 160 | } 161 | } 162 | )*) 163 | } 164 | 165 | impl_from! { 166 | ThisExpression => This, 167 | Identifier<'ast> => Identifier, 168 | Literal<'ast> => Literal, 169 | SequenceExpression<'ast> => Sequence, 170 | ArrayExpression<'ast> => Array, 171 | MemberExpression<'ast> => Member, 172 | ComputedMemberExpression<'ast> => ComputedMember, 173 | MetaPropertyExpression<'ast> => MetaProperty, 174 | CallExpression<'ast> => Call, 175 | BinaryExpression<'ast> => Binary, 176 | PrefixExpression<'ast> => Prefix, 177 | PostfixExpression<'ast> => Postfix, 178 | ConditionalExpression<'ast> => Conditional, 179 | TemplateLiteral<'ast> => Template, 180 | TaggedTemplateExpression<'ast> => TaggedTemplate, 181 | SpreadExpression<'ast> => Spread, 182 | ArrowExpression<'ast> => Arrow, 183 | ObjectExpression<'ast> => Object, 184 | FunctionExpression<'ast> => Function, 185 | ClassExpression<'ast> => Class 186 | } 187 | 188 | impl<'ast> Expression<'ast> { 189 | #[inline] 190 | pub fn binding_power(&self) -> u8 { 191 | use self::Expression::*; 192 | 193 | match *self { 194 | Member(_) | MetaProperty(_) | Arrow(_) => 18, 195 | 196 | Call(_) => 17, 197 | 198 | Prefix(_) => 15, 199 | 200 | Binary(BinaryExpression { ref operator, .. }) | 201 | Postfix(PostfixExpression { ref operator, .. }) => operator.binding_power(), 202 | 203 | Conditional(_) => 4, 204 | 205 | Sequence(_) => 0, 206 | 207 | _ => 100, 208 | } 209 | } 210 | 211 | #[inline] 212 | pub fn is_allowed_as_bare_statement(&self) -> bool { 213 | use self::Expression::*; 214 | 215 | match *self { 216 | Object(_) | 217 | Function(_) | 218 | Class(_) => false, 219 | _ => true, 220 | } 221 | } 222 | 223 | #[inline] 224 | pub fn is_lvalue(&self) -> bool { 225 | use self::Expression::*; 226 | 227 | match *self { 228 | Identifier(_) | 229 | Member(_) | 230 | ComputedMember(_) | 231 | Object(_) | 232 | Array(_) | 233 | Spread(_) => true, 234 | _ => false, 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /ratel/src/ast/function.rs: -------------------------------------------------------------------------------- 1 | use ast::{Node, Loc, IdentifierNode, ExpressionNode}; 2 | use ast::{BlockNode, Statement, PatternList, PropertyKey}; 3 | 4 | pub trait Name<'ast>: Copy { 5 | fn empty() -> Self; 6 | } 7 | 8 | #[derive(Debug, PartialEq, Clone, Copy)] 9 | pub struct EmptyName; 10 | 11 | #[derive(Debug, PartialEq, Clone, Copy)] 12 | pub struct MandatoryName<'ast>(pub IdentifierNode<'ast>); 13 | 14 | #[derive(Debug, PartialEq, Clone, Copy)] 15 | pub struct OptionalName<'ast>(pub Option>); 16 | 17 | pub type Method<'ast> = Function<'ast, EmptyName>; 18 | 19 | impl<'ast> Name<'ast> for EmptyName { 20 | fn empty() -> Self { 21 | EmptyName 22 | } 23 | } 24 | 25 | impl<'ast> Name<'ast> for MandatoryName<'ast> { 26 | fn empty() -> Self { 27 | MandatoryName(Node::new(&Loc { 28 | start: 0, 29 | end: 0, 30 | item: "" 31 | })) 32 | } 33 | } 34 | 35 | impl<'ast> Name<'ast> for OptionalName<'ast> { 36 | fn empty() -> Self { 37 | OptionalName(None) 38 | } 39 | } 40 | 41 | #[cfg(test)] 42 | impl<'ast> From> for MandatoryName<'ast> { 43 | #[inline] 44 | fn from(name: IdentifierNode<'ast>) -> Self { 45 | MandatoryName(name) 46 | } 47 | } 48 | 49 | #[cfg(test)] 50 | impl<'ast> From> for OptionalName<'ast> { 51 | #[inline] 52 | fn from(name: IdentifierNode<'ast>) -> Self { 53 | OptionalName(Some(name)) 54 | } 55 | } 56 | 57 | #[cfg(test)] 58 | impl<'ast> From>> for OptionalName<'ast> { 59 | #[inline] 60 | fn from(name: Option>) -> Self { 61 | OptionalName(name) 62 | } 63 | } 64 | 65 | #[derive(Debug, PartialEq, Clone, Copy)] 66 | pub struct Function<'ast, N: Name<'ast>> { 67 | pub name: N, 68 | pub generator: bool, 69 | pub params: PatternList<'ast>, 70 | pub body: BlockNode<'ast, Statement<'ast>>, 71 | } 72 | 73 | #[derive(Debug, PartialEq, Clone, Copy)] 74 | pub enum MethodKind { 75 | Constructor, 76 | Method, 77 | Get, 78 | Set, 79 | } 80 | 81 | #[derive(Debug, PartialEq, Clone, Copy)] 82 | pub enum ClassMember<'ast> { 83 | Error, 84 | Method { 85 | is_static: bool, 86 | key: Node<'ast, PropertyKey<'ast>>, 87 | kind: MethodKind, 88 | value: Node<'ast, Function<'ast, EmptyName>>, 89 | }, 90 | Literal { 91 | is_static: bool, 92 | key: Node<'ast, PropertyKey<'ast>>, 93 | value: ExpressionNode<'ast>, 94 | } 95 | } 96 | 97 | #[derive(Debug, PartialEq, Clone, Copy)] 98 | pub struct Class<'ast, N: Name<'ast>> { 99 | pub name: N, 100 | pub extends: Option>, 101 | pub body: BlockNode<'ast, ClassMember<'ast>>, 102 | } 103 | -------------------------------------------------------------------------------- /ratel/src/ast/literal.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq, Clone, Copy)] 2 | pub enum Literal<'ast> { 3 | Undefined, 4 | Null, 5 | True, 6 | False, 7 | Number(&'ast str), 8 | Binary(&'ast str), 9 | String(&'ast str), 10 | RegEx(&'ast str), 11 | } 12 | -------------------------------------------------------------------------------- /ratel/src/ast/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod variable; 3 | mod operator; 4 | // mod types; 5 | mod function; 6 | mod literal; 7 | pub mod node; 8 | pub mod expression; 9 | pub mod statement; 10 | 11 | use toolshed::list::List; 12 | use std::ops::Deref; 13 | 14 | pub use ast::variable::*; 15 | pub use ast::operator::*; 16 | pub use ast::node::Node; 17 | // pub use ast::types::{Type, Primitive}; 18 | pub use ast::expression::{Expression, Property, PropertyKey}; 19 | pub use ast::statement::{Statement, Declarator, BlockStatement}; 20 | pub use ast::function::{Function, Class, ClassMember, Method, MethodKind}; 21 | pub use ast::function::{Name, EmptyName, OptionalName, MandatoryName}; 22 | pub use ast::literal::Literal; 23 | 24 | 25 | #[derive(Debug, PartialEq, Clone, Copy)] 26 | pub struct Block<'ast, T: 'ast> { 27 | pub body: NodeList<'ast, T> 28 | } 29 | 30 | #[derive(Debug, PartialEq, Clone, Copy)] 31 | pub enum Pattern<'ast> { 32 | /// Only used inside ArrayPattern 33 | Void, 34 | Identifier(Identifier<'ast>), 35 | ObjectPattern { 36 | properties: NodeList<'ast, Property<'ast>> 37 | }, 38 | ArrayPattern { 39 | elements: NodeList<'ast, Pattern<'ast>> 40 | }, 41 | RestElement { 42 | argument: IdentifierNode<'ast> 43 | }, 44 | AssignmentPattern { 45 | left: Node<'ast, Pattern<'ast>>, 46 | right: ExpressionNode<'ast>, 47 | } 48 | } 49 | 50 | // Handful of useful aliases 51 | pub type Identifier<'ast> = &'ast str; 52 | pub type NodeList<'ast, T> = List<'ast, Node<'ast, T>>; 53 | pub type BlockNode<'ast, T> = Node<'ast, Block<'ast, T>>; 54 | pub type PatternList<'ast> = NodeList<'ast, Pattern<'ast>>; 55 | pub type PropertyNode<'ast> = Node<'ast, Property<'ast>>; 56 | pub type ExpressionNode<'ast> = Node<'ast, Expression<'ast>>; 57 | pub type ExpressionList<'ast> = NodeList<'ast, Expression<'ast>>; 58 | pub type StatementNode<'ast> = Node<'ast, Statement<'ast>>; 59 | pub type StatementList<'ast> = NodeList<'ast, Statement<'ast>>; 60 | pub type IdentifierNode<'ast> = Node<'ast, &'ast str>; 61 | pub type IdentifierList<'ast> = NodeList<'ast, &'ast str>; 62 | // pub type TypeNode<'ast> = NodeList<'ast, Type<'ast>>; 63 | // pub type TypeList<'ast> = NodeList<'ast, Type<'ast>>; 64 | 65 | #[derive(Debug, Clone)] 66 | pub struct Loc { 67 | pub start: u32, 68 | pub end: u32, 69 | pub item: T, 70 | } 71 | 72 | impl Copy for Loc {} 73 | 74 | impl Deref for Loc { 75 | type Target = T; 76 | 77 | #[inline] 78 | fn deref(&self) -> &T { 79 | &self.item 80 | } 81 | } 82 | 83 | #[derive(Debug, Clone)] 84 | pub struct Program<'ast> { 85 | pub source: &'ast str, 86 | pub body: NodeList<'ast, Statement<'ast>>, 87 | } 88 | 89 | impl Loc { 90 | #[inline] 91 | pub fn new(start: u32, end: u32, item: T) -> Self { 92 | Loc { 93 | start, 94 | end, 95 | item, 96 | } 97 | } 98 | } 99 | 100 | impl PartialEq for Loc { 101 | #[inline] 102 | fn eq(&self, other: &Self) -> bool { 103 | self.item.eq(&other.item) 104 | } 105 | } 106 | 107 | impl<'ast> Program<'ast> { 108 | #[inline] 109 | pub fn statements(&'ast self) -> &'ast NodeList<'ast, Statement<'ast>> { 110 | &self.body 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /ratel/src/ast/node.rs: -------------------------------------------------------------------------------- 1 | use toolshed::CopyCell; 2 | use std::ops::Deref; 3 | use std::fmt::{self, Debug}; 4 | use ast::Loc; 5 | 6 | /// `Node` is a specialized `Cell` that holds a reference to T instead of T. 7 | /// `Node` has defined lifetime and implements `Defer` for convenience. 8 | #[derive(Clone, Copy)] 9 | pub struct Node<'ast, T: 'ast> { 10 | inner: CopyCell<&'ast Loc> 11 | } 12 | 13 | impl<'ast, T: 'ast> Node<'ast, T> { 14 | #[inline] 15 | pub fn new(ptr: &'ast Loc) -> Self { 16 | Node { 17 | inner: CopyCell::new(ptr) 18 | } 19 | } 20 | 21 | #[inline] 22 | pub fn set(&self, ptr: &'ast Loc) { 23 | self.inner.set(ptr) 24 | } 25 | 26 | #[inline] 27 | pub fn get_mut(&mut self) -> &mut &'ast Loc { 28 | self.inner.get_mut() 29 | } 30 | } 31 | 32 | impl<'ast, T: 'ast> Deref for Node<'ast, T> { 33 | type Target = Loc; 34 | 35 | #[inline] 36 | fn deref(&self) -> &Self::Target { 37 | self.inner.get() 38 | } 39 | } 40 | 41 | impl<'ast, T: 'ast + PartialEq> PartialEq for Node<'ast, T> { 42 | #[inline] 43 | fn eq(&self, other: &Self) -> bool { 44 | self.deref().eq(other.deref()) 45 | } 46 | } 47 | 48 | impl<'ast, T: 'ast + Debug> Debug for Node<'ast, T> { 49 | #[inline] 50 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 51 | Debug::fmt(self.deref(), f) 52 | } 53 | } 54 | 55 | #[cfg(test)] 56 | mod test { 57 | use super::*; 58 | 59 | #[test] 60 | fn ptr() { 61 | let one = Loc::new(0, 0, "one"); 62 | let two = Loc::new(0, 0, "two"); 63 | 64 | let one_ptr = Node::new(&one); 65 | let two_ptr = one_ptr; 66 | 67 | assert_eq!(*one_ptr, Loc::new(0, 0, "one")); 68 | assert_eq!(*two_ptr, Loc::new(0, 0, "one")); 69 | 70 | two_ptr.set(&two); 71 | 72 | assert_eq!(*one_ptr, Loc::new(0, 0, "one")); 73 | assert_eq!(*two_ptr, Loc::new(0, 0, "two")); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ratel/src/ast/operator.rs: -------------------------------------------------------------------------------- 1 | use lexer::Token; 2 | use lexer::Token::*; 3 | 4 | #[derive(Debug, PartialEq, Clone, Copy)] 5 | pub enum OperatorKind { 6 | FatArrow, // … => … 7 | New, // new … 8 | Increment, // ++ … | … ++ 9 | Decrement, // -- … | … -- 10 | LogicalNot, // ! … 11 | BitwiseNot, // ~ … 12 | Typeof, // typeof … 13 | Void, // void … 14 | Delete, // delete … 15 | Multiplication, // … * … 16 | Division, // … / … 17 | Remainder, // … % … 18 | Exponent, // … ** … 19 | Addition, // … + … | + … 20 | Subtraction, // … - … | - … 21 | BitShiftLeft, // … << … 22 | BitShiftRight, // … >> … 23 | UBitShiftRight, // … >>> … 24 | Lesser, // … < … 25 | LesserEquals, // … <= … 26 | Greater, // … > … 27 | GreaterEquals, // … >= … 28 | Instanceof, // … instanceof … 29 | In, // … in … 30 | StrictEquality, // … === … 31 | StrictInequality, // … !== … 32 | Equality, // … == … 33 | Inequality, // … != … 34 | BitwiseAnd, // … & … 35 | BitwiseXor, // … ^ … 36 | BitwiseOr, // … | … 37 | LogicalAnd, // … && … 38 | LogicalOr, // … || … 39 | Conditional, // … ? … : … 40 | Assign, // … = … 41 | AddAssign, // … += … 42 | SubtractAssign, // … -= … 43 | ExponentAssign, // … **= … 44 | MultiplyAssign, // … *= … 45 | DivideAssign, // … /= … 46 | RemainderAssign, // … %= … 47 | BSLAssign, // … <<= … 48 | BSRAssign, // … >>= … 49 | UBSRAssign, // … >>>= … 50 | BitAndAssign, // … &= … 51 | BitXorAssign, // … ^= … 52 | BitOrAssign, // … |= … 53 | Spread, // ... … 54 | } 55 | 56 | 57 | #[derive(Debug, PartialEq, Clone)] 58 | pub enum OperatorCategory { 59 | Minus, 60 | Plus, 61 | Safe, 62 | Word, 63 | } 64 | 65 | use self::OperatorKind::*; 66 | 67 | impl OperatorKind { 68 | #[inline] 69 | pub fn from_token(token: Token) -> Option { 70 | match token { 71 | OperatorFatArrow => Some(FatArrow), 72 | OperatorNew => Some(New), 73 | OperatorIncrement => Some(Increment), 74 | OperatorDecrement => Some(Decrement), 75 | OperatorLogicalNot => Some(LogicalNot), 76 | OperatorBitwiseNot => Some(BitwiseNot), 77 | OperatorTypeof => Some(Typeof), 78 | OperatorVoid => Some(Void), 79 | OperatorDelete => Some(Delete), 80 | OperatorMultiplication => Some(Multiplication), 81 | OperatorDivision => Some(Division), 82 | OperatorRemainder => Some(Remainder), 83 | OperatorExponent => Some(Exponent), 84 | OperatorAddition => Some(Addition), 85 | OperatorSubtraction => Some(Subtraction), 86 | OperatorBitShiftLeft => Some(BitShiftLeft), 87 | OperatorBitShiftRight => Some(BitShiftRight), 88 | OperatorUBitShiftRight => Some(UBitShiftRight), 89 | OperatorLesser => Some(Lesser), 90 | OperatorLesserEquals => Some(LesserEquals), 91 | OperatorGreater => Some(Greater), 92 | OperatorGreaterEquals => Some(GreaterEquals), 93 | OperatorInstanceof => Some(Instanceof), 94 | OperatorIn => Some(In), 95 | OperatorStrictEquality => Some(StrictEquality), 96 | OperatorStrictInequality => Some(StrictInequality), 97 | OperatorEquality => Some(Equality), 98 | OperatorInequality => Some(Inequality), 99 | OperatorBitwiseAnd => Some(BitwiseAnd), 100 | OperatorBitwiseXor => Some(BitwiseXor), 101 | OperatorBitwiseOr => Some(BitwiseOr), 102 | OperatorLogicalAnd => Some(LogicalAnd), 103 | OperatorLogicalOr => Some(LogicalOr), 104 | OperatorConditional => Some(Conditional), 105 | OperatorAssign => Some(Assign), 106 | OperatorAddAssign => Some(AddAssign), 107 | OperatorSubtractAssign => Some(SubtractAssign), 108 | OperatorExponentAssign => Some(ExponentAssign), 109 | OperatorMultiplyAssign => Some(MultiplyAssign), 110 | OperatorDivideAssign => Some(DivideAssign), 111 | OperatorRemainderAssign => Some(RemainderAssign), 112 | OperatorBSLAssign => Some(BSLAssign), 113 | OperatorBSRAssign => Some(BSRAssign), 114 | OperatorUBSRAssign => Some(UBSRAssign), 115 | OperatorBitAndAssign => Some(BitAndAssign), 116 | OperatorBitXorAssign => Some(BitXorAssign), 117 | OperatorBitOrAssign => Some(BitOrAssign), 118 | OperatorSpread => Some(Spread), 119 | _ => None 120 | } 121 | } 122 | 123 | #[inline] 124 | pub fn as_str(&self) -> &'static str { 125 | match self { 126 | FatArrow => "=>", 127 | New => "new", 128 | Increment => "++", 129 | Decrement => "--", 130 | LogicalNot => "!", 131 | BitwiseNot => "~", 132 | Typeof => "typeof", 133 | Void => "void", 134 | Delete => "delete", 135 | Multiplication => "*", 136 | Division => "/", 137 | Remainder => "%", 138 | Exponent => "**", 139 | Addition => "+", 140 | Subtraction => "-", 141 | BitShiftLeft => "<<", 142 | BitShiftRight => ">>", 143 | UBitShiftRight => ">>>", 144 | Lesser => "<", 145 | LesserEquals => "<=", 146 | Greater => ">", 147 | GreaterEquals => ">=", 148 | Instanceof => "instanceof", 149 | In => "in", 150 | StrictEquality => "===", 151 | StrictInequality => "!==", 152 | Equality => "==", 153 | Inequality => "!=", 154 | BitwiseAnd => "&", 155 | BitwiseXor => "^", 156 | BitwiseOr => "|", 157 | LogicalAnd => "&&", 158 | LogicalOr => "||", 159 | Conditional => "?", 160 | Assign => "=", 161 | AddAssign => "+=", 162 | SubtractAssign => "-=", 163 | ExponentAssign => "**=", 164 | MultiplyAssign => "*=", 165 | DivideAssign => "/=", 166 | RemainderAssign => "%=", 167 | BSLAssign => "<<=", 168 | BSRAssign => ">>=", 169 | UBSRAssign => ">>>=", 170 | BitAndAssign => "&=", 171 | BitXorAssign => "^=", 172 | BitOrAssign => "|=", 173 | Spread => "...", 174 | } 175 | } 176 | 177 | /// According to the Operator Precedence Table 178 | /// Note: Unary operators default to 15! 179 | #[inline] 180 | pub fn binding_power(&self) -> u8 { 181 | match self { 182 | FatArrow => 18, 183 | 184 | New => 17, 185 | 186 | Increment | 187 | Decrement => 16, 188 | 189 | LogicalNot | 190 | BitwiseNot | 191 | Typeof | 192 | Void | 193 | Delete => 15, 194 | 195 | Multiplication | 196 | Division | 197 | Remainder | 198 | Exponent => 14, 199 | 200 | Addition | 201 | Subtraction => 13, 202 | 203 | BitShiftLeft | 204 | BitShiftRight | 205 | UBitShiftRight => 12, 206 | 207 | Lesser | 208 | LesserEquals | 209 | Greater | 210 | GreaterEquals | 211 | Instanceof | 212 | In => 11, 213 | 214 | StrictEquality | 215 | StrictInequality | 216 | Equality | 217 | Inequality => 10, 218 | 219 | BitwiseAnd => 9, 220 | BitwiseXor => 8, 221 | BitwiseOr => 7, 222 | LogicalAnd => 6, 223 | LogicalOr => 5, 224 | Conditional => 4, 225 | 226 | Assign | 227 | AddAssign | 228 | SubtractAssign | 229 | ExponentAssign | 230 | MultiplyAssign | 231 | DivideAssign | 232 | RemainderAssign | 233 | BSLAssign | 234 | BSRAssign | 235 | UBSRAssign | 236 | BitAndAssign | 237 | BitXorAssign | 238 | BitOrAssign => 3, 239 | 240 | Spread => 1, 241 | } 242 | } 243 | 244 | #[inline] 245 | pub fn prefix(&self) -> bool { 246 | match self { 247 | LogicalNot | 248 | BitwiseNot | 249 | Typeof | 250 | Void | 251 | Delete | 252 | New | 253 | Spread | 254 | Increment | 255 | Decrement | 256 | Addition | 257 | Subtraction => true, 258 | 259 | _ => false 260 | } 261 | } 262 | 263 | #[inline] 264 | pub fn infix(&self) -> bool { 265 | match self { 266 | FatArrow | 267 | Multiplication | 268 | Division | 269 | Remainder | 270 | Exponent | 271 | StrictEquality | 272 | StrictInequality | 273 | Equality | 274 | Inequality | 275 | Lesser | 276 | LesserEquals | 277 | Greater | 278 | GreaterEquals | 279 | Instanceof | 280 | In | 281 | BitShiftLeft | 282 | BitShiftRight | 283 | UBitShiftRight | 284 | BitwiseAnd | 285 | BitwiseXor | 286 | BitwiseOr | 287 | LogicalAnd | 288 | LogicalOr | 289 | Conditional | 290 | Addition | 291 | Subtraction | 292 | Assign | 293 | AddAssign | 294 | SubtractAssign | 295 | ExponentAssign | 296 | MultiplyAssign | 297 | DivideAssign | 298 | RemainderAssign | 299 | BSLAssign | 300 | BSRAssign | 301 | UBSRAssign | 302 | BitAndAssign | 303 | BitXorAssign | 304 | BitOrAssign => true, 305 | 306 | _ => false 307 | } 308 | } 309 | 310 | #[inline] 311 | pub fn assignment(&self) -> bool { 312 | match self { 313 | Assign | 314 | AddAssign | 315 | SubtractAssign | 316 | ExponentAssign | 317 | MultiplyAssign | 318 | DivideAssign | 319 | RemainderAssign | 320 | BSLAssign | 321 | BSRAssign | 322 | UBSRAssign | 323 | BitAndAssign | 324 | BitXorAssign | 325 | BitOrAssign => true, 326 | 327 | _ => false 328 | } 329 | } 330 | 331 | #[inline] 332 | pub fn category(&self) -> OperatorCategory { 333 | match self { 334 | In | 335 | New | 336 | Typeof | 337 | Void | 338 | Delete | 339 | Instanceof => OperatorCategory::Word, 340 | Addition | 341 | Increment => OperatorCategory::Plus, 342 | Subtraction | 343 | Decrement => OperatorCategory::Minus, 344 | 345 | _ => OperatorCategory::Safe, 346 | } 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /ratel/src/ast/statement.rs: -------------------------------------------------------------------------------- 1 | use ast::{Node, NodeList, DeclarationKind, Function, Class, MandatoryName, IdentifierNode}; 2 | use ast::{ExpressionNode, StatementNode, StatementList, Block, BlockNode, Pattern}; 3 | 4 | #[derive(Debug, PartialEq, Clone, Copy)] 5 | pub struct Declarator<'ast> { 6 | pub id: Node<'ast, Pattern<'ast>>, 7 | pub init: Option>, 8 | } 9 | 10 | #[derive(Debug, PartialEq, Clone, Copy)] 11 | pub struct DeclarationStatement<'ast> { 12 | pub kind: DeclarationKind, 13 | pub declarators: NodeList<'ast, Declarator<'ast>>, 14 | } 15 | 16 | #[derive(Debug, PartialEq, Clone, Copy)] 17 | pub struct ReturnStatement<'ast> { 18 | pub value: Option>, 19 | } 20 | 21 | #[derive(Debug, PartialEq, Clone, Copy)] 22 | pub struct BreakStatement<'ast> { 23 | // TODO: This should be a `LabelNode`, with `Label` being a newtype for &str. 24 | pub label: Option>, 25 | } 26 | 27 | #[derive(Debug, PartialEq, Clone, Copy)] 28 | pub struct ContinueStatement<'ast> { 29 | // TODO: This should be a `LabelNode`, with `Label` being a newtype for &str. 30 | pub label: Option> 31 | } 32 | 33 | #[derive(Debug, PartialEq, Clone, Copy)] 34 | pub struct ThrowStatement<'ast> { 35 | pub value: ExpressionNode<'ast> 36 | } 37 | 38 | #[derive(Debug, PartialEq, Clone, Copy)] 39 | pub struct IfStatement<'ast> { 40 | pub test: ExpressionNode<'ast>, 41 | pub consequent: StatementNode<'ast>, 42 | pub alternate: Option>, 43 | } 44 | 45 | #[derive(Debug, PartialEq, Clone, Copy)] 46 | pub struct WhileStatement<'ast> { 47 | pub test: ExpressionNode<'ast>, 48 | pub body: StatementNode<'ast>, 49 | } 50 | 51 | #[derive(Debug, PartialEq, Clone, Copy)] 52 | pub struct DoStatement<'ast> { 53 | pub body: StatementNode<'ast>, 54 | pub test: ExpressionNode<'ast>, 55 | } 56 | 57 | #[derive(Debug, PartialEq, Clone, Copy)] 58 | pub enum ForInit<'ast> { 59 | Declaration(DeclarationStatement<'ast>), 60 | Expression(ExpressionNode<'ast>) 61 | } 62 | 63 | #[derive(Debug, PartialEq, Clone, Copy)] 64 | pub struct ForStatement<'ast> { 65 | pub init: Option>>, 66 | pub test: Option>, 67 | pub update: Option>, 68 | pub body: StatementNode<'ast> 69 | } 70 | 71 | #[derive(Debug, PartialEq, Clone, Copy)] 72 | pub struct ForInStatement<'ast> { 73 | pub left: Node<'ast, ForInit<'ast>>, 74 | pub right: ExpressionNode<'ast>, 75 | pub body: StatementNode<'ast> 76 | } 77 | 78 | #[derive(Debug, PartialEq, Clone, Copy)] 79 | pub struct ForOfStatement<'ast> { 80 | pub left: Node<'ast, ForInit<'ast>>, 81 | pub right: ExpressionNode<'ast>, 82 | pub body: StatementNode<'ast> 83 | } 84 | 85 | #[derive(Debug, PartialEq, Clone, Copy)] 86 | pub struct CatchClause<'ast> { 87 | pub param: Node<'ast, Pattern<'ast>>, 88 | pub body: BlockNode<'ast, Statement<'ast>>, 89 | } 90 | 91 | #[derive(Debug, PartialEq, Clone, Copy)] 92 | pub struct TryStatement<'ast> { 93 | pub block: BlockNode<'ast, Statement<'ast>>, 94 | pub handler: Option>>, 95 | pub finalizer: Option>>, 96 | } 97 | 98 | #[derive(Debug, PartialEq, Clone, Copy)] 99 | pub struct LabeledStatement<'ast> { 100 | pub label: &'ast str, 101 | pub body: StatementNode<'ast>, 102 | } 103 | 104 | #[derive(Debug, PartialEq, Clone, Copy)] 105 | pub struct SwitchStatement<'ast> { 106 | pub discriminant: ExpressionNode<'ast>, 107 | pub cases: BlockNode<'ast, SwitchCase<'ast>>, 108 | } 109 | 110 | #[derive(Debug, PartialEq, Clone, Copy)] 111 | pub struct SwitchCase<'ast> { 112 | pub test: Option>, 113 | pub consequent: StatementList<'ast>, 114 | } 115 | 116 | #[derive(Debug, PartialEq, Clone, Copy)] 117 | pub struct ImportDeclaration<'ast> { 118 | pub specifiers: NodeList<'ast, ForImportSpecifier<'ast>>, 119 | pub source: &'ast str, 120 | } 121 | 122 | #[derive(Debug, PartialEq, Clone, Copy)] 123 | pub struct ImportSpecifier<'ast> { 124 | pub imported: IdentifierNode<'ast>, 125 | pub local: IdentifierNode<'ast>, 126 | } 127 | 128 | #[derive(Debug, PartialEq, Clone, Copy)] 129 | pub struct ImportDefaultSpecifier<'ast> { 130 | pub local: IdentifierNode<'ast>, 131 | } 132 | 133 | #[derive(Debug, PartialEq, Clone, Copy)] 134 | pub struct ImportNamespaceSpecifier<'ast> { 135 | pub local: IdentifierNode<'ast>, 136 | } 137 | 138 | #[derive(Debug, PartialEq, Clone, Copy)] 139 | pub enum ForImportSpecifier<'ast> { 140 | ImportSpecifier(ImportSpecifier<'ast>), 141 | ImportDefaultSpecifier(ImportDefaultSpecifier<'ast>), 142 | ImportNamespaceSpecifier(ImportNamespaceSpecifier<'ast>), 143 | } 144 | 145 | 146 | 147 | pub type BlockStatement<'ast> = Block<'ast, Statement<'ast>>; 148 | pub type FunctionStatement<'ast> = Function<'ast, MandatoryName<'ast>>; 149 | pub type ClassStatement<'ast> = Class<'ast, MandatoryName<'ast>>; 150 | 151 | #[derive(Debug, PartialEq, Clone, Copy)] 152 | pub enum Statement<'ast> { 153 | Empty, 154 | Debugger, 155 | Expression(ExpressionNode<'ast>), 156 | Declaration(DeclarationStatement<'ast>), 157 | Return(ReturnStatement<'ast>), 158 | Break(BreakStatement<'ast>), 159 | Continue(ContinueStatement<'ast>), 160 | Throw(ThrowStatement<'ast>), 161 | If(IfStatement<'ast>), 162 | While(WhileStatement<'ast>), 163 | Do(DoStatement<'ast>), 164 | For(ForStatement<'ast>), 165 | ForIn(ForInStatement<'ast>), 166 | ForOf(ForOfStatement<'ast>), 167 | Try(TryStatement<'ast>), 168 | Block(BlockStatement<'ast>), 169 | Labeled(LabeledStatement<'ast>), 170 | Function(FunctionStatement<'ast>), 171 | Class(ClassStatement<'ast>), 172 | Switch(SwitchStatement<'ast>), 173 | Import(ImportDeclaration<'ast>) 174 | } 175 | 176 | macro_rules! impl_from { 177 | ($( $type:ident => $variant:ident ),*) => ($( 178 | impl<'ast> From<$type<'ast>> for Statement<'ast> { 179 | #[inline] 180 | fn from(val: $type<'ast>) -> Self { 181 | Statement::$variant(val) 182 | } 183 | } 184 | )*) 185 | } 186 | 187 | impl_from! { 188 | ExpressionNode => Expression, 189 | DeclarationStatement => Declaration, 190 | ReturnStatement => Return, 191 | BreakStatement => Break, 192 | ThrowStatement => Throw, 193 | IfStatement => If, 194 | WhileStatement => While, 195 | DoStatement => Do, 196 | ForStatement => For, 197 | ForInStatement => ForIn, 198 | ForOfStatement => ForOf, 199 | TryStatement => Try, 200 | BlockStatement => Block, 201 | LabeledStatement => Labeled, 202 | ContinueStatement => Continue, 203 | FunctionStatement => Function, 204 | ClassStatement => Class, 205 | SwitchStatement => Switch, 206 | ImportDeclaration => Import 207 | } 208 | 209 | impl<'ast> From> for ForInit<'ast> { 210 | #[inline] 211 | fn from(val: DeclarationStatement<'ast>) -> Self { 212 | ForInit::Declaration(val) 213 | } 214 | } 215 | 216 | impl<'ast> From> for ForInit<'ast> { 217 | #[inline] 218 | fn from(val: ExpressionNode<'ast>) -> Self { 219 | ForInit::Expression(val) 220 | } 221 | } 222 | 223 | impl<'ast> From> for ForImportSpecifier<'ast> { 224 | #[inline] 225 | fn from(val: ImportSpecifier<'ast>) -> Self { 226 | ForImportSpecifier::ImportSpecifier(val) 227 | } 228 | } 229 | impl<'ast> From> for ForImportSpecifier<'ast> { 230 | #[inline] 231 | fn from(val: ImportDefaultSpecifier<'ast>) -> Self { 232 | ForImportSpecifier::ImportDefaultSpecifier(val) 233 | } 234 | } 235 | impl<'ast> From> for ForImportSpecifier<'ast> { 236 | #[inline] 237 | fn from(val: ImportNamespaceSpecifier<'ast>) -> Self { 238 | ForImportSpecifier::ImportNamespaceSpecifier(val) 239 | } 240 | } 241 | 242 | impl<'ast> Statement<'ast> { 243 | #[inline] 244 | pub fn is_block(&self) -> bool { 245 | match *self { 246 | Statement::Block(_) => true, 247 | _ => false, 248 | } 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /ratel/src/ast/types.rs: -------------------------------------------------------------------------------- 1 | use ast::{Loc, TypeNode, TypeList}; 2 | 3 | static TYPE_ANY: &Loc> = &Loc { 4 | start: 0, 5 | end: 0, 6 | item: Type::Any 7 | }; 8 | 9 | #[derive(Debug, PartialEq, Clone, Copy)] 10 | pub enum Primitive { 11 | Number, 12 | String, 13 | Boolean, 14 | Null, 15 | Undefined, 16 | } 17 | 18 | #[derive(Debug, PartialEq, Clone, Copy)] 19 | pub enum Type<'ast> { 20 | Any, 21 | Primitive(Primitive), 22 | Identifier(&'ast str), 23 | Union { 24 | variants: TypeList<'ast>, 25 | }, 26 | Generic { 27 | ident: &'ast str, 28 | subtypes: TypeList<'ast>, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ratel/src/ast/variable.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq, Clone, Copy)] 2 | pub enum DeclarationKind { 3 | Var, 4 | Let, 5 | Const, 6 | } 7 | -------------------------------------------------------------------------------- /ratel/src/astgen/function.rs: -------------------------------------------------------------------------------- 1 | use serde::ser::{Serialize, Serializer, SerializeStruct}; 2 | use astgen::SerializeInLoc; 3 | use ast::{Function, Class, Name, MandatoryName, OptionalName, EmptyName, ClassMember, Block}; 4 | use ast::MethodKind; 5 | 6 | impl<'ast> Serialize for MethodKind { 7 | fn serialize(&self, serializer: S) -> Result 8 | where 9 | S: Serializer, 10 | { 11 | use self::MethodKind::*; 12 | match *self { 13 | Constructor => serializer.serialize_str("constructor"), 14 | Method => serializer.serialize_str("method"), 15 | Get => serializer.serialize_str("get"), 16 | Set => serializer.serialize_str("set"), 17 | } 18 | } 19 | } 20 | 21 | impl<'ast> SerializeInLoc for ClassMember<'ast> { 22 | fn serialize(&self, serializer: S) -> Result 23 | where 24 | S: Serializer, 25 | { 26 | use self::ClassMember::*; 27 | 28 | match *self { 29 | Error { .. } => panic!("Module contains errors"), 30 | Method { is_static, key, kind, value } => { 31 | self.in_loc(serializer, "MethodDefinition", 5, |state| { 32 | state.serialize_field("kind", &kind)?; 33 | state.serialize_field("static", &is_static)?; 34 | state.serialize_field("computed", &false)?; 35 | state.serialize_field("key", &*key)?; 36 | state.serialize_field("value", &value) 37 | }) 38 | }, 39 | Literal { .. } => { 40 | unimplemented!() 41 | } 42 | } 43 | } 44 | } 45 | 46 | // TODO: DRY with BlockStatement 47 | impl<'ast> SerializeInLoc for Block<'ast, ClassMember<'ast>> { 48 | fn serialize(&self, serializer: S) -> Result 49 | where 50 | S: Serializer, 51 | { 52 | self.in_loc(serializer, "ClassBody", 1, |state| { 53 | state.serialize_field("body", &self.body) 54 | }) 55 | } 56 | } 57 | 58 | pub trait NameType<'ast>: Name<'ast> { 59 | const IN_CLASS: &'static str = "ClassExpression"; 60 | const IN_FUNCTION: &'static str = "FunctionExpression"; 61 | } 62 | 63 | impl<'ast> NameType<'ast> for EmptyName {} 64 | 65 | impl<'ast> Serialize for EmptyName { 66 | fn serialize(&self, serializer: S) -> Result 67 | where 68 | S: Serializer, 69 | { 70 | serializer.serialize_none() 71 | } 72 | } 73 | 74 | impl<'ast> NameType<'ast> for OptionalName<'ast> {} 75 | 76 | impl<'ast> Serialize for OptionalName<'ast> { 77 | fn serialize(&self, serializer: S) -> Result 78 | where 79 | S: Serializer, 80 | { 81 | (self.0).serialize(serializer) 82 | } 83 | } 84 | 85 | impl<'ast> NameType<'ast> for MandatoryName<'ast> { 86 | const IN_CLASS: &'static str = "ClassDeclaration"; 87 | const IN_FUNCTION: &'static str = "FunctionDeclaration"; 88 | } 89 | 90 | impl<'ast> Serialize for MandatoryName<'ast> { 91 | fn serialize(&self, serializer: S) -> Result 92 | where 93 | S: Serializer, 94 | { 95 | (self.0).serialize(serializer) 96 | } 97 | } 98 | 99 | impl<'ast, N> SerializeInLoc for Class<'ast, N> 100 | where 101 | N: Serialize + NameType<'ast>, 102 | { 103 | fn serialize(&self, serializer: S) -> Result 104 | where 105 | S: Serializer, 106 | { 107 | self.in_loc(serializer, N::IN_CLASS, 3, |state| { 108 | state.serialize_field("id", &self.name)?; 109 | state.serialize_field("superClass", &self.extends)?; 110 | state.serialize_field("body", &self.body) 111 | }) 112 | } 113 | } 114 | 115 | impl<'ast, N> SerializeInLoc for Function<'ast, N> 116 | where 117 | N: Serialize + NameType<'ast>, 118 | { 119 | fn serialize(&self, serializer: S) -> Result 120 | where 121 | S: Serializer, 122 | { 123 | self.in_loc(serializer, N::IN_FUNCTION, 3, |state| { 124 | state.serialize_field("generator", &self.generator)?; 125 | state.serialize_field("id", &self.name)?; 126 | state.serialize_field("params", &self.params)?; 127 | state.serialize_field("body", &self.body) 128 | }) 129 | } 130 | } -------------------------------------------------------------------------------- /ratel/src/astgen/macros.rs: -------------------------------------------------------------------------------- 1 | /// Parses the given input string into an AST and compares it 2 | /// with the given JSON input. 3 | 4 | #[cfg(test)] 5 | #[macro_export] 6 | macro_rules! expect_parse { 7 | ($expr:expr, $expected:tt) => {{ 8 | use $crate::parser::parse; 9 | use $crate::serde_json::to_value; 10 | 11 | let module = parse($expr).unwrap(); 12 | let result = to_value(&module).unwrap(); 13 | let expected = json!($expected); 14 | assert_eq!(result, expected); 15 | }}; 16 | } 17 | -------------------------------------------------------------------------------- /ratel/src/astgen/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod macros; 3 | mod statement; 4 | mod expression; 5 | mod function; 6 | mod value; 7 | 8 | use serde::ser::{Serialize, Serializer, SerializeStruct}; 9 | use ast::{Loc, Node}; 10 | use module::Module; 11 | 12 | pub trait SerializeInLoc { 13 | #[inline] 14 | fn in_loc(&self, serializer: S, name: &'static str, length: usize, build: F) -> Result 15 | where 16 | S: Serializer, 17 | F: FnOnce(&mut S::SerializeStruct) -> Result<(), S::Error> 18 | { 19 | let mut state = serializer.serialize_struct(name, length + 3)?; 20 | state.serialize_field("type", name)?; 21 | build(&mut state).map(move |_| state) 22 | } 23 | 24 | fn serialize(&self, serializer: S) -> Result 25 | where S: Serializer; 26 | } 27 | 28 | impl<'ast, T: SerializeInLoc> Serialize for Loc { 29 | fn serialize(&self, serializer: S) -> Result 30 | where 31 | S: Serializer 32 | { 33 | let mut state = self.item.serialize(serializer)?; 34 | state.serialize_field("start", &self.start)?; 35 | state.serialize_field("end", &self.end)?; 36 | state.end() 37 | } 38 | } 39 | 40 | impl<'ast, T: SerializeInLoc> Serialize for Node<'ast, T> { 41 | fn serialize(&self, serializer: S) -> Result 42 | where 43 | S: Serializer 44 | { 45 | Loc::::serialize(&*self, serializer) 46 | } 47 | } 48 | 49 | impl<'ast> Serialize for Module<'ast> { 50 | fn serialize(&self, serializer: S) -> Result 51 | where 52 | S: Serializer 53 | { 54 | let body = self.body(); 55 | 56 | let mut start = 0; 57 | let mut end = 0; 58 | let mut iter = body.iter(); 59 | 60 | if let Some(node) = iter.next() { 61 | start = node.start; 62 | end = node.end; 63 | } 64 | 65 | if let Some(node) = iter.last() { 66 | end = node.end; 67 | } 68 | 69 | let name = "Program"; 70 | let mut state = serializer.serialize_struct(name, 4)?; 71 | state.serialize_field("type", &name)?; 72 | state.serialize_field("body", &body)?; 73 | state.serialize_field("start", &start)?; 74 | state.serialize_field("end", &end)?; 75 | state.end() 76 | } 77 | } 78 | 79 | #[cfg(test)] 80 | mod test { 81 | #[test] 82 | fn test_generate_ast_empty() { 83 | expect_parse!("", { 84 | "type": "Program", 85 | "body": [], 86 | "start": 0, 87 | "end": 0, 88 | }); 89 | } 90 | #[test] 91 | fn test_generate_ast_expression() { 92 | expect_parse!("this;", { 93 | "type": "Program", 94 | "body": [ 95 | { 96 | "type": "ExpressionStatement", 97 | "expression": { 98 | "type": "ThisExpression", 99 | "start": 0, 100 | "end": 4, 101 | }, 102 | "start": 0, 103 | "end": 4, 104 | } 105 | ], 106 | "start": 0, 107 | "end": 4, 108 | }); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /ratel/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Debug, Display}; 2 | use lexer::Token; 3 | 4 | /// Error type used by the tokenizer and the parser internally. 5 | #[derive(PartialEq, Clone)] 6 | pub struct Error { 7 | pub token: Token, 8 | pub raw: Box, 9 | pub start: usize, 10 | pub end: usize, 11 | } 12 | 13 | impl Debug for Error { 14 | #[inline] 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | write!(f, "Unexpected {:?}({}) at {}:{}", &self.token, &*self.raw, self.start, self.end) 17 | } 18 | } 19 | 20 | /// Error type returned by `parser::parse`. This error will include 21 | /// owned `String` of the source code where the error occurred, so 22 | /// that a meaningful error can be printed out. 23 | pub enum ParseError { 24 | UnexpectedEndOfProgram, 25 | UnexpectedToken { 26 | source: String, 27 | start: usize, 28 | end: usize, 29 | }, 30 | } 31 | 32 | impl Debug for ParseError { 33 | #[inline] 34 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 | fmt::Display::fmt(self, f) 36 | } 37 | } 38 | 39 | impl Display for ParseError { 40 | #[inline] 41 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 42 | match *self { 43 | ParseError::UnexpectedEndOfProgram => { 44 | write!(f, "Unexpected end of program")? 45 | }, 46 | 47 | ParseError::UnexpectedToken { 48 | ref source, 49 | start, 50 | end 51 | } => { 52 | let (lineno, line) = source[..start] 53 | .lines() 54 | .enumerate() 55 | .last() 56 | .unwrap_or((0, "")); 57 | 58 | let colno = line.chars().count(); 59 | let token_len = source[start..end].chars().count(); 60 | 61 | writeln!(f, "Unexpected token at {}:{}\n", lineno + 1, colno + 1)?; 62 | 63 | let iter = source 64 | .lines() 65 | .enumerate() 66 | .skip_while(|&(index, _)| index < lineno.saturating_sub(2)) 67 | .take_while(|&(index, _)| index < lineno + 3); 68 | 69 | let width = log10(lineno + 3); 70 | 71 | for (index, line) in iter { 72 | if index == lineno { 73 | writeln!(f, "> {0:1$} | {2}", index+1, width, line)?; 74 | 75 | for _ in 0..width { 76 | write!(f, " ")?; 77 | } 78 | 79 | write!(f, " | ")?; 80 | 81 | for _ in 0..colno { 82 | write!(f, " ")?; 83 | } 84 | 85 | for _ in 0..token_len { 86 | write!(f, "^")?; 87 | } 88 | 89 | writeln!(f)?; 90 | } else { 91 | writeln!(f, "{0:1$} | {2}", index+1, width+2, line)?; 92 | } 93 | } 94 | 95 | }, 96 | } 97 | 98 | Ok(()) 99 | } 100 | } 101 | 102 | fn log10(mut num: usize) -> usize { 103 | let mut log = 0; 104 | 105 | while num > 0 { 106 | log += 1; 107 | num /= 10; 108 | } 109 | 110 | log 111 | } 112 | 113 | pub type Result = ::std::result::Result; 114 | 115 | pub type ParseResult = ::std::result::Result; 116 | 117 | #[cfg(test)] 118 | mod test { 119 | use super::*; 120 | 121 | #[test] 122 | fn test_format_unexpected_token_error () { 123 | let err = ParseError::UnexpectedToken { 124 | source: "foo".to_string(), 125 | start: 0, 126 | end: 1 127 | }; 128 | 129 | let expected = "Unexpected token at 1:1\n\n> 1 | foo\n | ^\n"; 130 | 131 | assert_eq!(format!("{}", err), expected); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /ratel/src/lexer/labels.rs: -------------------------------------------------------------------------------- 1 | use lexer::{util, ByteHandler}; 2 | use lexer::token::Token::*; 3 | 4 | macro_rules! match_label { 5 | ($lex:ident [$( $byte:expr )* => $token:expr]) => { 6 | if $( 7 | $lex.next_byte() == $byte && 8 | )* {$lex.bump(); !util::legal_in_label($lex.read_byte())} { 9 | return $lex.token = $token; 10 | } 11 | }; 12 | 13 | ($lex:ident { [=> $token:expr] $( $match:tt $cont:tt )+ }) => { 14 | match $lex.next_byte() { 15 | $( 16 | $match => match_label!($lex $cont), 17 | )* 18 | ch if !util::legal_in_label(ch) => return $lex.token = $token, 19 | _ => {} 20 | } 21 | }; 22 | 23 | ($lex:ident { $match:tt $cont:tt }) => { 24 | if $lex.next_byte() == $match { 25 | match_label!($lex $cont) 26 | } 27 | }; 28 | 29 | ($lex:ident { $( $match:tt $cont:tt )+ }) => { 30 | match $lex.next_byte() { 31 | $( 32 | $match => match_label!($lex $cont), 33 | )* 34 | _ => {} 35 | } 36 | } 37 | } 38 | 39 | // Non-keyword Identifier: starting with a letter, _ or $ 40 | pub const IDT: ByteHandler = Some(|lex| { 41 | lex.bump(); 42 | lex.read_label(); 43 | lex.token = Identifier; 44 | }); 45 | 46 | // Identifier or keyword starting with a letter `b` 47 | pub const L_B: ByteHandler = Some(|lex| { 48 | match_label!(lex [b'r' b'e' b'a' b'k' => Break]); 49 | 50 | lex.read_label(); 51 | lex.token = Identifier; 52 | }); 53 | 54 | // Identifier or keyword starting with a letter `c` 55 | pub const L_C: ByteHandler = Some(|lex| { 56 | match_label!(lex { 57 | b'o'{ 58 | b'n'{ 59 | b's'[b't' => DeclarationConst] 60 | b't'[b'i' b'n' b'u' b'e' => Continue] 61 | } 62 | } 63 | b'a'{ 64 | b's'[b'e' => Case] 65 | b't'[b'c' b'h' => Catch] 66 | } 67 | b'l'[b'a' b's' b's' => Class] 68 | }); 69 | 70 | lex.read_label(); 71 | lex.token = Identifier; 72 | }); 73 | 74 | // Identifier or keyword starting with a letter `d` 75 | pub const L_D: ByteHandler = Some(|lex| { 76 | match_label!(lex { 77 | b'o'[ => Do] 78 | b'e'{ 79 | b'l'[b'e' b't' b'e' => OperatorDelete] 80 | b'f'[b'a' b'u' b'l' b't' => Default] 81 | b'b'[b'u' b'g' b'g' b'e' b'r' => Debugger] 82 | } 83 | }); 84 | 85 | lex.read_label(); 86 | lex.token = Identifier; 87 | }); 88 | 89 | // Identifier or keyword starting with a letter `e` 90 | pub const L_E: ByteHandler = Some(|lex| { 91 | match_label!(lex { 92 | b'l'[b's' b'e' => Else] 93 | b'x'{ 94 | b'p'[b'o' b'r' b't' => Export] 95 | b't'[b'e' b'n' b'd' b's' => Extends] 96 | } 97 | b'n'[b'u' b'm' => ReservedEnum] 98 | }); 99 | 100 | lex.read_label(); 101 | lex.token = Identifier; 102 | }); 103 | 104 | // Identifier or keyword starting with a letter `f` 105 | pub const L_F: ByteHandler = Some(|lex| { 106 | match_label!(lex { 107 | b'i'[b'n' b'a' b'l' b'l' b'y' => Finally] 108 | b'o'[b'r' => For] 109 | b'u'[b'n' b'c' b't' b'i' b'o' b'n' => Function] 110 | b'a'[b'l' b's' b'e' => LiteralFalse] 111 | }); 112 | 113 | lex.read_label(); 114 | lex.token = Identifier; 115 | }); 116 | 117 | // Identifier or keyword starting with a letter `i` 118 | pub const L_I: ByteHandler = Some(|lex| { 119 | match_label!(lex { 120 | b'n'{ 121 | [ => OperatorIn] 122 | b's'[b't' b'a' b'n' b'c' b'e' b'o' b'f' => OperatorInstanceof] 123 | b't'[b'e' b'r' b'f' b'a' b'c' b'e' => ReservedInterface] 124 | } 125 | b'f'[ => If] 126 | b'm'{ 127 | b'p'{ 128 | b'o'[b'r' b't' => Import] 129 | b'l'[b'e' b'm' b'e' b'n' b't' b's' => ReservedImplements] 130 | } 131 | } 132 | }); 133 | 134 | lex.read_label(); 135 | lex.token = Identifier; 136 | }); 137 | 138 | // Identifier or keyword starting with a letter `l` 139 | pub const L_L: ByteHandler = Some(|lex| { 140 | match_label!(lex [b'e' b't' => DeclarationLet]); 141 | 142 | lex.read_label(); 143 | lex.token = Identifier; 144 | }); 145 | 146 | // Identifier or keyword starting with a letter `n` 147 | pub const L_N: ByteHandler = Some(|lex| { 148 | match_label!(lex { 149 | b'e'[b'w' => OperatorNew] 150 | b'u'[b'l' b'l' => LiteralNull] 151 | }); 152 | 153 | lex.read_label(); 154 | lex.token = Identifier; 155 | }); 156 | 157 | // Identifier or keyword starting with a letter `p` 158 | pub const L_P: ByteHandler = Some(|lex| { 159 | match_label!(lex { 160 | b'a'[b'c' b'k' b'a' b'g' b'e' => ReservedPackage] 161 | b'u'[b'b' b'l' b'i' b'c' => ReservedPublic] 162 | b'r'{ 163 | b'o'[b't' b'e' b'c' b't' b'e' b'd' => ReservedProtected] 164 | b'i'[b'v' b'a' b't' b'e' => ReservedPrivate] 165 | } 166 | }); 167 | 168 | lex.read_label(); 169 | lex.token = Identifier; 170 | }); 171 | 172 | // Identifier or keyword starting with a letter `r` 173 | pub const L_R: ByteHandler = Some(|lex| { 174 | match_label!(lex [b'e' b't' b'u' b'r' b'n' => Return]); 175 | 176 | lex.read_label(); 177 | lex.token = Identifier; 178 | }); 179 | 180 | // Identifier or keyword starting with a letter `s` 181 | pub const L_S: ByteHandler = Some(|lex| { 182 | match_label!(lex { 183 | b'u'[b'p' b'e' b'r' => Super] 184 | b'w'[b'i' b't' b'c' b'h' => Switch] 185 | b't'[b'a' b't' b'i' b'c' => Static] 186 | }); 187 | 188 | lex.read_label(); 189 | lex.token = Identifier; 190 | }); 191 | 192 | // Identifier or keyword starting with a letter `t` 193 | pub const L_T: ByteHandler = Some(|lex| { 194 | match_label!(lex { 195 | b'y'[b'p' b'e' b'o' b'f' => OperatorTypeof] 196 | b'h'{ 197 | b'i'[b's' => This] 198 | b'r'[b'o' b'w' => Throw] 199 | } 200 | b'r'{ 201 | b'y'[ => Try] 202 | b'u'[b'e' => LiteralTrue] 203 | } 204 | }); 205 | 206 | lex.read_label(); 207 | lex.token = Identifier; 208 | }); 209 | 210 | // Identifier or keyword starting with a letter `u` 211 | pub const L_U: ByteHandler = Some(|lex| { 212 | match_label!(lex [b'n' b'd' b'e' b'f' b'i' b'n' b'e' b'd' => LiteralUndefined]); 213 | 214 | lex.read_label(); 215 | lex.token = Identifier; 216 | }); 217 | 218 | // Identifier or keyword starting with a letter `v` 219 | pub const L_V: ByteHandler = Some(|lex| { 220 | match_label!(lex { 221 | b'a'[b'r' => DeclarationVar] 222 | b'o'[b'i' b'd' => OperatorVoid] 223 | }); 224 | 225 | lex.read_label(); 226 | lex.token = Identifier; 227 | }); 228 | 229 | // Identifier or keyword starting with a letter `w` 230 | pub const L_W: ByteHandler = Some(|lex| { 231 | match_label!(lex { 232 | b'h'[b'i' b'l' b'e' => While] 233 | b'i'[b't' b'h' => With] 234 | }); 235 | 236 | lex.read_label(); 237 | lex.token = Identifier; 238 | }); 239 | 240 | // Identifier or keyword starting with a letter `y` 241 | pub const L_Y: ByteHandler = Some(|lex| { 242 | match_label!(lex [b'i' b'e' b'l' b'd' => Yield]); 243 | 244 | lex.read_label(); 245 | lex.token = Identifier; 246 | }); 247 | -------------------------------------------------------------------------------- /ratel/src/lexer/token.rs: -------------------------------------------------------------------------------- 1 | // Lookup table layout: 2 | // ==================== 3 | // 4 | // EOF ; : , ( ) [ ] { } => NEW 5 | // ++ -- ! ~ TYPOF VOID DELET * / % ** + 6 | // - << >> >>> < <= > >= INSOF IN === !== 7 | // == != & ^ | && || ? = += -= **= 8 | // *= /= %= <<= >>= >>>= &= ^= |= ... VAR LET 9 | // CONST BREAK DO CASE ELSE CATCH EXPRT CLASS EXTND RET WHILE FINLY 10 | // SUPER WITH CONT FOR SWTCH YIELD DBGGR FUNCT THIS DEFLT IF THROW 11 | // IMPRT TRY STATI TRUE FALSE NULL UNDEF STR NUM BIN REGEX ENUM 12 | // IMPL PCKG PROT IFACE PRIV PUBLI IDENT ACCSS TPL_O TPL_C ERR_T ERR_E 13 | 14 | #[derive(Debug, PartialEq, Clone, Copy)] 15 | pub enum Token { 16 | EndOfProgram, 17 | Semicolon, 18 | Colon, 19 | Comma, 20 | ParenOpen, 21 | ParenClose, 22 | BracketOpen, 23 | BracketClose, 24 | BraceOpen, 25 | BraceClose, 26 | OperatorFatArrow, // … => … 27 | OperatorNew, // new … 28 | OperatorIncrement, // ++ … | … ++ 29 | OperatorDecrement, // -- … | … -- 30 | OperatorLogicalNot, // ! … 31 | OperatorBitwiseNot, // ~ … 32 | OperatorTypeof, // typeof … 33 | OperatorVoid, // void … 34 | OperatorDelete, // delete … 35 | OperatorMultiplication, // … * … 36 | OperatorDivision, // … / … 37 | OperatorRemainder, // … % … 38 | OperatorExponent, // … ** … 39 | OperatorAddition, // … + … | + … 40 | OperatorSubtraction, // … - … | - … 41 | OperatorBitShiftLeft, // … << … 42 | OperatorBitShiftRight, // … >> … 43 | OperatorUBitShiftRight, // … >>> … 44 | OperatorLesser, // … < … 45 | OperatorLesserEquals, // … <= … 46 | OperatorGreater, // … > … 47 | OperatorGreaterEquals, // … >= … 48 | OperatorInstanceof, // … instanceof … 49 | OperatorIn, // … in … 50 | OperatorStrictEquality, // … === … 51 | OperatorStrictInequality, // … !== … 52 | OperatorEquality, // … == … 53 | OperatorInequality, // … != … 54 | OperatorBitwiseAnd, // … & … 55 | OperatorBitwiseXor, // … ^ … 56 | OperatorBitwiseOr, // … | … 57 | OperatorLogicalAnd, // … && … 58 | OperatorLogicalOr, // … || … 59 | OperatorConditional, // … ? … : … 60 | OperatorAssign, // … = … 61 | OperatorAddAssign, // … += … 62 | OperatorSubtractAssign, // … -= … 63 | OperatorExponentAssign, // … **= … 64 | OperatorMultiplyAssign, // … *= … 65 | OperatorDivideAssign, // … /= … 66 | OperatorRemainderAssign, // … %= … 67 | OperatorBSLAssign, // … <<= … 68 | OperatorBSRAssign, // … >>= … 69 | OperatorUBSRAssign, // … >>>= … 70 | OperatorBitAndAssign, // … &= … 71 | OperatorBitXorAssign, // … ^= … 72 | OperatorBitOrAssign, // … |= … 73 | OperatorSpread, // ... … 74 | DeclarationVar, 75 | DeclarationLet, 76 | DeclarationConst, 77 | Break, 78 | Do, 79 | Case, 80 | Else, 81 | Catch, 82 | Export, 83 | Class, 84 | Extends, 85 | Return, 86 | While, 87 | Finally, 88 | Super, 89 | With, 90 | Continue, 91 | For, 92 | Switch, 93 | Yield, 94 | Debugger, 95 | Function, 96 | This, 97 | Default, 98 | If, 99 | Throw, 100 | Import, 101 | Try, 102 | Static, 103 | LiteralTrue, 104 | LiteralFalse, 105 | LiteralNull, 106 | LiteralUndefined, 107 | LiteralString, 108 | LiteralNumber, 109 | LiteralBinary, 110 | LiteralRegEx, 111 | ReservedEnum, 112 | ReservedImplements, 113 | ReservedPackage, 114 | ReservedProtected, 115 | ReservedInterface, 116 | ReservedPrivate, 117 | ReservedPublic, 118 | Identifier, 119 | Accessor, 120 | TemplateOpen, 121 | TemplateClosed, 122 | UnexpectedToken, 123 | UnexpectedEndOfProgram, 124 | } 125 | 126 | impl Token { 127 | #[inline] 128 | pub fn is_word(&self) -> bool { 129 | use self::Token::*; 130 | 131 | match self { 132 | Identifier | 133 | Break | 134 | Do | 135 | Case | 136 | Else | 137 | Catch | 138 | Export | 139 | Class | 140 | Extends | 141 | Return | 142 | While | 143 | Finally | 144 | Super | 145 | With | 146 | Continue | 147 | For | 148 | Switch | 149 | Yield | 150 | Debugger | 151 | Function | 152 | This | 153 | Default | 154 | If | 155 | Throw | 156 | Import | 157 | Try | 158 | Static | 159 | OperatorNew | 160 | OperatorTypeof | 161 | OperatorVoid | 162 | OperatorDelete | 163 | OperatorInstanceof | 164 | LiteralTrue | 165 | LiteralFalse | 166 | LiteralNull | 167 | LiteralUndefined => true, 168 | 169 | _ => false, 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /ratel/src/lexer/util.rs: -------------------------------------------------------------------------------- 1 | #[inline] 2 | pub fn legal_in_label(byte: u8) -> bool { 3 | // Look up table that marks which ASCII characters are allowed in identifiers 4 | const NU: bool = true; // digit 5 | const AL: bool = true; // alphabet 6 | const DO: bool = true; // dollar sign $ 7 | const US: bool = true; // underscore 8 | const UN: bool = true; // unicode 9 | const BS: bool = true; // backslash 10 | const __: bool = false; 11 | 12 | static TABLE: [bool; 256] = [ 13 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 14 | __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0 15 | __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 1 16 | __, __, __, __, DO, __, __, __, __, __, __, __, __, __, __, __, // 2 17 | NU, NU, NU, NU, NU, NU, NU, NU, NU, NU, __, __, __, __, __, __, // 3 18 | __, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, // 4 19 | AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, __, BS, __, __, US, // 5 20 | __, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, // 6 21 | AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, __, __, __, __, __, // 7 22 | UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, // 8 23 | UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, // 9 24 | UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, // A 25 | UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, // B 26 | UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, // C 27 | UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, // D 28 | UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, // E 29 | UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, // F 30 | ]; 31 | 32 | unsafe { *(&TABLE as *const bool).offset(byte as isize) } 33 | } 34 | -------------------------------------------------------------------------------- /ratel/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit="128"] 2 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::trivially_copy_pass_by_ref))] 3 | 4 | extern crate serde; 5 | extern crate toolshed; 6 | 7 | #[macro_use] 8 | extern crate serde_derive; 9 | 10 | #[cfg(test)] 11 | #[macro_use] 12 | extern crate serde_json; 13 | 14 | #[cfg(test)] 15 | #[macro_use] 16 | extern crate pretty_assertions; 17 | 18 | pub mod ast; 19 | pub mod error; 20 | pub mod lexer; 21 | 22 | mod module; 23 | mod parser; 24 | mod astgen; 25 | 26 | pub use parser::parse; 27 | pub use module::Module; 28 | -------------------------------------------------------------------------------- /ratel/src/module.rs: -------------------------------------------------------------------------------- 1 | use toolshed::list::UnsafeList; 2 | use toolshed::Arena; 3 | use ast::StatementList; 4 | 5 | use std::fmt; 6 | use std::marker::PhantomData; 7 | 8 | 9 | /// A JavaScript module parsed to an AST. 10 | pub struct Module<'ast> { 11 | body: UnsafeList, 12 | arena: Arena, 13 | _phantom: PhantomData<&'ast StatementList<'ast>> 14 | } 15 | 16 | impl<'ast> Module<'ast> { 17 | #[inline] 18 | pub(crate) fn new(body: UnsafeList, arena: Arena) -> Self { 19 | Module { 20 | body, 21 | arena, 22 | _phantom: PhantomData, 23 | } 24 | } 25 | 26 | /// Get the body of the module as a list of statements. 27 | #[inline] 28 | pub fn body(&self) -> StatementList<'ast> { 29 | unsafe { self.body.into_list() } 30 | } 31 | 32 | /// Get a reference to the `Arena` on which the AST is allocated. 33 | #[inline] 34 | pub fn arena(&'ast self) -> &'ast Arena { 35 | &self.arena 36 | } 37 | } 38 | 39 | impl<'ast> fmt::Debug for Module<'ast> { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | write!(f, "Module:").unwrap(); 42 | for elem in self.body().iter() { 43 | write!(f, "\n\t{:?}", elem).unwrap() 44 | } 45 | Ok(()) 46 | } 47 | } -------------------------------------------------------------------------------- /ratel/src/parser/error.rs: -------------------------------------------------------------------------------- 1 | use error::Error; 2 | 3 | use ast::{Node, Loc, NodeList, Pattern}; 4 | use ast::{Name, ClassMember, Property, PropertyKey, MandatoryName, Block}; 5 | use parser::Parser; 6 | 7 | pub trait Handle<'ast> { 8 | fn handle_error(parser: &mut Parser<'ast>, err: Error) -> Self; 9 | } 10 | 11 | pub trait ToError { 12 | fn to_error() -> Self; 13 | } 14 | 15 | impl<'ast, I> ToError for Block<'ast, I> { 16 | fn to_error() -> Self { 17 | Block { body: NodeList::empty() } 18 | } 19 | } 20 | 21 | impl<'ast> ToError for MandatoryName<'ast> { 22 | fn to_error() -> Self { 23 | MandatoryName::empty() 24 | } 25 | } 26 | 27 | impl<'ast> ToError for Node<'ast, Property<'ast>> { 28 | fn to_error() -> Self { 29 | Node::new(&Loc { 30 | start: 0, 31 | end: 0, 32 | item: Property::Shorthand("") 33 | }) 34 | } 35 | } 36 | 37 | impl<'ast> ToError for Node<'ast, ClassMember<'ast>> { 38 | fn to_error() -> Self { 39 | Node::new(&Loc { 40 | start: 0, 41 | end: 0, 42 | item: ClassMember::Error, 43 | }) 44 | } 45 | } 46 | 47 | impl<'ast, T: 'ast + ToError> ToError for Loc { 48 | fn to_error() -> Self { 49 | Loc { 50 | start: 0, 51 | end: 0, 52 | item: T::to_error() 53 | } 54 | } 55 | } 56 | 57 | impl<'ast, T: 'ast + Copy> ToError for NodeList<'ast, T> { 58 | fn to_error() -> Self { 59 | NodeList::empty() 60 | } 61 | } 62 | 63 | impl<'ast> ToError for Node<'ast, &'ast str> { 64 | fn to_error() -> Self { 65 | Node::new(&Loc { 66 | start: 0, 67 | end: 0, 68 | item: "" 69 | }) 70 | } 71 | } 72 | 73 | 74 | impl<'ast> ToError for Pattern<'ast> { 75 | #[inline] 76 | fn to_error() -> Self { 77 | Pattern::Void 78 | } 79 | } 80 | 81 | impl<'ast> ToError for Node<'ast, Pattern<'ast>> { 82 | #[inline] 83 | fn to_error() -> Self { 84 | Node::new(&Loc { 85 | start: 0, 86 | end: 0, 87 | item: Pattern::Void 88 | }) 89 | } 90 | } 91 | 92 | impl<'ast> ToError for Node<'ast, PropertyKey<'ast>> { 93 | #[inline] 94 | fn to_error() -> Self { 95 | Node::new(&Loc { 96 | start: 0, 97 | end: 0, 98 | item: PropertyKey::Literal("") 99 | }) 100 | } 101 | } 102 | 103 | impl ToError for () { 104 | #[inline] 105 | fn to_error() {} 106 | } 107 | -------------------------------------------------------------------------------- /ratel/src/parser/macros.rs: -------------------------------------------------------------------------------- 1 | /// If the next token matches `$p`, consume that token and execute `$eval`. 2 | #[macro_export] 3 | macro_rules! allow { 4 | ($parser:ident, $p:pat => $eval:expr) => { 5 | match $parser.lexer.token { 6 | $p => { 7 | $parser.lexer.consume(); 8 | $eval; 9 | }, 10 | _ => {} 11 | } 12 | } 13 | } 14 | 15 | /// Return an error if the next token doesn't match $p. 16 | #[macro_export] 17 | macro_rules! expect { 18 | ($parser:ident, $p:pat) => { 19 | match $parser.lexer.token { 20 | $p => $parser.lexer.consume(), 21 | _ => $parser.error() 22 | } 23 | } 24 | } 25 | 26 | #[macro_export] 27 | macro_rules! parameter_key { 28 | ($parser:ident) => { 29 | match $parser.lexer.token { 30 | ParenClose => { 31 | $parser.lexer.consume(); 32 | break; 33 | }, 34 | Identifier => { 35 | let ident = $parser.lexer.token_as_str(); 36 | $parser.lexer.consume(); 37 | ParameterKey::Identifier(ident) 38 | }, 39 | _ => return $parser.error() 40 | } 41 | } 42 | } 43 | 44 | #[cfg(test)] 45 | #[macro_export] 46 | macro_rules! assert_expr { 47 | ($src:expr, $expr:expr) => ({ 48 | let module = parse($src).unwrap(); 49 | let mut body = module.body().iter(); 50 | 51 | match body.next().map(|s| s.item).unwrap() { 52 | Statement::Expression(ref expression) => assert_eq!(expression.item, Expression::from($expr)), 53 | _ => panic!("Statement isn't an expression!") 54 | } 55 | 56 | assert_eq!(body.next(), None); 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /ratel/src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod macros; 3 | mod error; 4 | mod expression; 5 | mod statement; 6 | mod function; 7 | mod nested; 8 | 9 | use toolshed::list::ListBuilder; 10 | use toolshed::Arena; 11 | use error::Error; 12 | use module::Module; 13 | 14 | use self::error::ToError; 15 | use self::nested::*; 16 | 17 | use ast::{Loc, Node, Statement, NodeList, Block, BlockNode}; 18 | use ast::{Expression, ExpressionNode, ExpressionList, IdentifierNode}; 19 | use ast::{OperatorKind, Pattern}; 20 | use ast::expression::BinaryExpression; 21 | use lexer::{Lexer, Asi}; 22 | use lexer::Token::*; 23 | 24 | pub trait Parse<'ast> { 25 | type Output; 26 | 27 | fn parse(&mut Parser<'ast>) -> Self::Output; 28 | } 29 | 30 | pub struct Parser<'ast> { 31 | arena: &'ast Arena, 32 | 33 | /// Lexer will produce tokens from the source 34 | lexer: Lexer<'ast>, 35 | 36 | /// Errors occurred during parsing 37 | errors: Vec, 38 | 39 | /// AST under construction 40 | body: NodeList<'ast, Statement<'ast>>, 41 | } 42 | 43 | impl<'ast> Parser<'ast> { 44 | pub fn new(source: &str, arena: &'ast Arena) -> Self { 45 | Parser { 46 | arena, 47 | lexer: Lexer::new(arena, source), 48 | errors: Vec::new(), 49 | body: NodeList::empty(), 50 | } 51 | } 52 | 53 | fn error(&mut self) -> T { 54 | let err = self.lexer.invalid_token(); 55 | 56 | self.errors.push(err); 57 | 58 | T::to_error() 59 | } 60 | 61 | #[inline] 62 | fn asi(&mut self) -> Asi { 63 | self.lexer.asi() 64 | } 65 | 66 | #[inline] 67 | fn loc(&self) -> (u32, u32) { 68 | self.lexer.loc() 69 | } 70 | 71 | #[inline] 72 | fn in_loc(&self, item: T) -> Loc { 73 | let (start, end) = self.loc(); 74 | 75 | Loc::new(start, end, item) 76 | } 77 | 78 | #[inline] 79 | fn alloc(&mut self, val: Loc) -> Node<'ast, T> where 80 | T: Copy, 81 | { 82 | Node::new(self.arena.alloc(val)) 83 | } 84 | 85 | #[inline] 86 | fn alloc_in_loc(&mut self, item: I) -> Node<'ast, T> where 87 | T: Copy, 88 | I: Into, 89 | { 90 | let node = self.in_loc(item.into()); 91 | self.alloc(node) 92 | } 93 | 94 | #[inline] 95 | fn alloc_at_loc(&mut self, start: u32, end: u32, item: I) -> Node<'ast, T> where 96 | T: Copy, 97 | I: Into, 98 | { 99 | self.alloc(Loc::new(start, end, item.into())) 100 | } 101 | 102 | #[inline] 103 | fn parse(&mut self) { 104 | if self.lexer.token == EndOfProgram { 105 | return; 106 | } 107 | 108 | let statement = self.statement(); 109 | let builder = ListBuilder::new(self.arena, statement); 110 | 111 | while self.lexer.token != EndOfProgram { 112 | builder.push(self.arena, self.statement()); 113 | } 114 | 115 | self.body = builder.as_list() 116 | } 117 | 118 | #[inline] 119 | fn block(&mut self) -> BlockNode<'ast, I> where 120 | I: Parse<'ast, Output = Node<'ast, I>> + Copy 121 | { 122 | let start = self.lexer.start(); 123 | 124 | match self.lexer.token { 125 | BraceOpen => self.lexer.consume(), 126 | _ => self.error::<()>(), 127 | } 128 | 129 | let block = self.raw_block(); 130 | let end = self.lexer.end_then_consume(); 131 | 132 | self.alloc_at_loc(start, end, block) 133 | } 134 | 135 | /// Same as above, but assumes that the opening brace has already been checked 136 | #[inline] 137 | fn unchecked_block(&mut self) -> BlockNode<'ast, I> where 138 | I: Parse<'ast, Output = Node<'ast, I>> + Copy 139 | { 140 | let start = self.lexer.start_then_consume(); 141 | let block = self.raw_block(); 142 | let end = self.lexer.end_then_consume(); 143 | 144 | self.alloc_at_loc(start, end, block) 145 | } 146 | 147 | #[inline] 148 | fn raw_block(&mut self) -> Block<'ast, I> where 149 | I: Parse<'ast, Output = Node<'ast, I>> + Copy 150 | { 151 | if self.lexer.token == BraceClose { 152 | return Block { body: NodeList::empty() }; 153 | } 154 | 155 | let statement = I::parse(self); 156 | let builder = ListBuilder::new(self.arena, statement); 157 | 158 | while self.lexer.token != BraceClose && self.lexer.token != EndOfProgram { 159 | builder.push(self.arena, I::parse(self)); 160 | } 161 | 162 | Block { body: builder.as_list() } 163 | } 164 | 165 | #[inline] 166 | fn identifier(&mut self) -> IdentifierNode<'ast> { 167 | match self.lexer.token { 168 | Identifier => { 169 | let ident = self.lexer.token_as_str(); 170 | let ident = self.alloc_in_loc(ident); 171 | self.lexer.consume(); 172 | ident 173 | }, 174 | _ => self.error() 175 | } 176 | } 177 | 178 | #[inline] 179 | fn pattern_from_expression(&mut self, expression: ExpressionNode<'ast>) -> Node<'ast, Pattern<'ast>> { 180 | let pattern = match expression.item { 181 | Expression::Binary(BinaryExpression { 182 | operator: OperatorKind::Assign, 183 | left, 184 | right, 185 | }) => { 186 | Pattern::AssignmentPattern { 187 | left: self.pattern_from_expression(left), 188 | right 189 | } 190 | }, 191 | Expression::Identifier(ident) => { 192 | Pattern::Identifier(ident) 193 | }, 194 | _ => self.error() 195 | }; 196 | 197 | self.alloc_at_loc(expression.start, expression.end, pattern) 198 | } 199 | 200 | #[inline] 201 | fn params_from_expressions(&mut self, expressions: ExpressionList<'ast>) -> NodeList<'ast, Pattern<'ast>> { 202 | let mut expressions = expressions.iter(); 203 | 204 | let builder = match expressions.next() { 205 | Some(&expression) => { 206 | let param = self.pattern_from_expression(expression); 207 | 208 | ListBuilder::new(self.arena, param) 209 | }, 210 | None => return NodeList::empty() 211 | }; 212 | 213 | for &expression in expressions { 214 | builder.push(self.arena, self.pattern_from_expression(expression)); 215 | } 216 | 217 | builder.as_list() 218 | } 219 | } 220 | 221 | /// Parse the JavaScript source `&str` and produce an Abstract Syntax Tree `Module`. 222 | pub fn parse<'src, 'ast>(source: &'src str) -> Result, Vec> { 223 | let arena = Arena::new(); 224 | 225 | let (body, errors) = { 226 | let mut parser = Parser::new(source, &arena); 227 | 228 | parser.parse(); 229 | 230 | (parser.body.into_unsafe(), parser.errors) 231 | }; 232 | 233 | match errors.len() { 234 | 0 => Ok(Module::new(body, arena)), 235 | _ => Err(errors) 236 | } 237 | } 238 | 239 | #[cfg(test)] 240 | mod mock { 241 | use super::*; 242 | use ast::{Literal, ExpressionNode, Block, BlockNode, Name}; 243 | 244 | pub struct Mock { 245 | arena: Arena 246 | } 247 | 248 | impl Mock { 249 | pub fn new() -> Self { 250 | Mock { 251 | arena: Arena::new() 252 | } 253 | } 254 | 255 | pub fn ptr<'a, T, I>(&'a self, val: I) -> Node<'a, T> where 256 | T: 'a + Copy, 257 | I: Into, 258 | { 259 | Node::new(self.arena.alloc(Loc::new(0, 0, val.into()))) 260 | } 261 | 262 | pub fn name<'a, N>(&'a self, val: &'a str) -> N where 263 | N: Name<'a> + From>, 264 | { 265 | N::from(Node::new(self.arena.alloc(Loc::new(0, 0, val)))) 266 | } 267 | 268 | pub fn number<'a>(&'a self, number: &'static str) -> ExpressionNode<'a> { 269 | self.ptr(Literal::Number(number)) 270 | } 271 | 272 | pub fn block(&self, list: L) -> BlockNode where 273 | I: Copy, 274 | T: Into + Copy, 275 | L: AsRef<[T]> 276 | { 277 | self.ptr(Block { body: self.list(list) }) 278 | } 279 | 280 | pub fn empty_block(&self) -> BlockNode { 281 | self.ptr(Block { body: NodeList::empty() }) 282 | } 283 | 284 | pub fn list<'a, T, I, L>(&'a self, list: L) -> NodeList<'a, T> where 285 | T: 'a + Copy, 286 | L: AsRef<[I]>, 287 | I: Into + Copy, 288 | { 289 | NodeList::from_iter(&self.arena, list.as_ref().iter().cloned().map(|i| { 290 | Node::new(self.arena.alloc(Loc::new(0, 0, i.into()))) 291 | })) 292 | } 293 | } 294 | } 295 | 296 | #[cfg(test)] 297 | mod test { 298 | use super::*; 299 | use parser::mock::Mock; 300 | 301 | #[test] 302 | fn empty_parse() { 303 | assert_eq!(parse("").unwrap().body(), NodeList::empty()); 304 | } 305 | 306 | #[test] 307 | fn empty_statements() { 308 | let mock = Mock::new(); 309 | 310 | let expected = mock.list([ 311 | Statement::Empty, 312 | Statement::Empty, 313 | Statement::Empty 314 | ]); 315 | 316 | assert_eq!(parse(";;;").unwrap().body(), expected); 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | echo "" 4 | echo "" 5 | echo "CHECKING: clippy" 6 | echo "#######################" 7 | echo "" 8 | 9 | if [ $TRAVIS_RUST_VERSION = $CLIPPY_TOOLCHAIN ]; then 10 | cargo +$CLIPPY_TOOLCHAIN clippy 11 | fi 12 | 13 | echo "" 14 | echo "" 15 | echo "TESTING: ratel + crates" 16 | echo "#######################" 17 | echo "" 18 | 19 | cargo test 20 | 21 | echo "" 22 | echo "" 23 | echo "TESTING: ffi" 24 | echo "############" 25 | echo "" 26 | 27 | cd ffi 28 | npm i 29 | npm test 30 | cd .. 31 | --------------------------------------------------------------------------------