├── .gitignore ├── favicon.ico ├── wasm.bat ├── pkg ├── rust_ast_explorer_bg.wasm ├── package.json ├── rust_ast_explorer_bg.wasm.d.ts ├── README.md ├── rust_ast_explorer.d.ts ├── rust_ast_explorer.js └── rust_ast_explorer.mjs ├── Cargo.toml ├── README.md ├── src └── lib.rs ├── LICENSE └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | 4 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarlKCarlK/rust-ast-explorer/HEAD/favicon.ico -------------------------------------------------------------------------------- /wasm.bat: -------------------------------------------------------------------------------- 1 | wasm-pack build --target web 2 | copy .\pkg\rust_ast_explorer.js .\pkg\rust_ast_explorer.mjs -------------------------------------------------------------------------------- /pkg/rust_ast_explorer_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarlKCarlK/rust-ast-explorer/HEAD/pkg/rust_ast_explorer_bg.wasm -------------------------------------------------------------------------------- /pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rust-ast-explorer", 3 | "version": "0.1.1", 4 | "files": [ 5 | "rust_ast_explorer_bg.wasm", 6 | "rust_ast_explorer.js", 7 | "rust_ast_explorer.d.ts" 8 | ], 9 | "module": "rust_ast_explorer.js", 10 | "types": "rust_ast_explorer.d.ts", 11 | "sideEffects": false 12 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-ast-explorer" 3 | version = "0.1.1" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [profile.release] 10 | lto = true 11 | 12 | [dependencies] 13 | wasm-bindgen = "0.2.74" 14 | syn = { version = "1.0.60", features = ["extra-traits", "full"] } 15 | html-escape = "0.2.11" 16 | -------------------------------------------------------------------------------- /pkg/rust_ast_explorer_bg.wasm.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export const memory: WebAssembly.Memory; 4 | export function greet(a: number, b: number): void; 5 | export function search(a: number, b: number, c: number): void; 6 | export function __wbindgen_malloc(a: number): number; 7 | export function __wbindgen_realloc(a: number, b: number, c: number): number; 8 | export function __wbindgen_add_to_stack_pointer(a: number): number; 9 | export function __wbindgen_free(a: number, b: number): void; 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust AST Explorer 2 | 3 | Hosted Live: https://carlkcarlk.github.io/rust-ast-explorer/ 4 | 5 | Paste Rust code and see the AST (abstract syntax tree) it generates. 6 | 7 | ## Alternative: 8 | 9 | * https://astexplorer.net/ -- fancier, mouse over the language to select Rust 10 | 11 | ### Based on 12 | 13 | * The syn crate provides the Rust parser and the debug formatter. 14 | * https://docs.rs/syn/latest/syn/struct.File.html 15 | * WASM allows everything to run in the user's browser. 16 | * https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_wasm 17 | -------------------------------------------------------------------------------- /pkg/README.md: -------------------------------------------------------------------------------- 1 | # Rust AST Explorer 2 | 3 | Hosted Live: https://carlkcarlk.github.io/rust-ast-explorer/ 4 | 5 | Paste Rust code and see the AST (abstract syntax tree) it generates. 6 | 7 | ## Alternative: 8 | 9 | * https://astexplorer.net/ -- fancier, mouse over the language to select Rust 10 | 11 | ### Based on 12 | 13 | * The syn crate provides the Rust parser and the debug formatter. 14 | * https://docs.rs/syn/latest/syn/struct.File.html 15 | * WASM allows everything to run in the user's browser. 16 | * https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_wasm 17 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use html_escape::encode_text; 2 | // todo make dual license 3 | 4 | use wasm_bindgen::prelude::*; 5 | 6 | // See https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_wasm 7 | // See https://docs.rs/syn/latest/syn/struct.File.html 8 | 9 | #[wasm_bindgen] 10 | extern "C" { 11 | pub fn alert(s: &str); 12 | } 13 | 14 | #[wasm_bindgen] 15 | pub fn greet(name: &str) { 16 | alert(&format!("Hello, {}!", name)); 17 | } 18 | 19 | #[wasm_bindgen] 20 | pub fn search(code: &str) -> String { 21 | let ast_result = syn::parse_str::(code); 22 | match ast_result { 23 | Ok(ast) => { 24 | let debug_string = format!("{:#?}", ast); 25 | let html = encode_text(&debug_string).into_owned(); 26 | html 27 | } 28 | Err(e) => { 29 | format!("Error: {}", e) 30 | } 31 | } 32 | } 33 | 34 | #[test] 35 | fn test() { 36 | let code = "fn main() { println!(); }"; 37 | let result = search(code); 38 | println!("result='{}'", result); 39 | // assert_eq!(result, "code='fn main() { println!(); }', result=''"); 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Carl Kadie 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 | -------------------------------------------------------------------------------- /pkg/rust_ast_explorer.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * @param {string} name 5 | */ 6 | export function greet(name: string): void; 7 | /** 8 | * @param {string} code 9 | * @returns {string} 10 | */ 11 | export function search(code: string): string; 12 | 13 | export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; 14 | 15 | export interface InitOutput { 16 | readonly memory: WebAssembly.Memory; 17 | readonly greet: (a: number, b: number) => void; 18 | readonly search: (a: number, b: number, c: number) => void; 19 | readonly __wbindgen_malloc: (a: number) => number; 20 | readonly __wbindgen_realloc: (a: number, b: number, c: number) => number; 21 | readonly __wbindgen_add_to_stack_pointer: (a: number) => number; 22 | readonly __wbindgen_free: (a: number, b: number) => void; 23 | } 24 | 25 | export type SyncInitInput = BufferSource | WebAssembly.Module; 26 | /** 27 | * Instantiates the given `module`, which can either be bytes or 28 | * a precompiled `WebAssembly.Module`. 29 | * 30 | * @param {SyncInitInput} module 31 | * 32 | * @returns {InitOutput} 33 | */ 34 | export function initSync(module: SyncInitInput): InitOutput; 35 | 36 | /** 37 | * If `module_or_path` is {RequestInfo} or {URL}, makes a request and 38 | * for everything else, calls `WebAssembly.instantiate` directly. 39 | * 40 | * @param {InitInput | Promise} module_or_path 41 | * 42 | * @returns {Promise} 43 | */ 44 | export default function init (module_or_path?: InitInput | Promise): Promise; 45 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Rust AST Explorer 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 31 | 32 | 33 | 34 |
16 |

Rust AST Explorer

17 |

Paste Rust code

18 | 20 | 21 |

22 |
...
26 |

27 | https://github.com/CarlKCarlK/rust-ast-explorer

28 |

Alternative: https://astexplorer.net/ 29 | -- fancier, mouse over the language to select Rust

30 |
35 | 36 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /pkg/rust_ast_explorer.js: -------------------------------------------------------------------------------- 1 | 2 | let wasm; 3 | 4 | const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); 5 | 6 | cachedTextDecoder.decode(); 7 | 8 | let cachedUint8Memory0 = new Uint8Array(); 9 | 10 | function getUint8Memory0() { 11 | if (cachedUint8Memory0.byteLength === 0) { 12 | cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); 13 | } 14 | return cachedUint8Memory0; 15 | } 16 | 17 | function getStringFromWasm0(ptr, len) { 18 | return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); 19 | } 20 | 21 | let WASM_VECTOR_LEN = 0; 22 | 23 | const cachedTextEncoder = new TextEncoder('utf-8'); 24 | 25 | const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' 26 | ? function (arg, view) { 27 | return cachedTextEncoder.encodeInto(arg, view); 28 | } 29 | : function (arg, view) { 30 | const buf = cachedTextEncoder.encode(arg); 31 | view.set(buf); 32 | return { 33 | read: arg.length, 34 | written: buf.length 35 | }; 36 | }); 37 | 38 | function passStringToWasm0(arg, malloc, realloc) { 39 | 40 | if (realloc === undefined) { 41 | const buf = cachedTextEncoder.encode(arg); 42 | const ptr = malloc(buf.length); 43 | getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); 44 | WASM_VECTOR_LEN = buf.length; 45 | return ptr; 46 | } 47 | 48 | let len = arg.length; 49 | let ptr = malloc(len); 50 | 51 | const mem = getUint8Memory0(); 52 | 53 | let offset = 0; 54 | 55 | for (; offset < len; offset++) { 56 | const code = arg.charCodeAt(offset); 57 | if (code > 0x7F) break; 58 | mem[ptr + offset] = code; 59 | } 60 | 61 | if (offset !== len) { 62 | if (offset !== 0) { 63 | arg = arg.slice(offset); 64 | } 65 | ptr = realloc(ptr, len, len = offset + arg.length * 3); 66 | const view = getUint8Memory0().subarray(ptr + offset, ptr + len); 67 | const ret = encodeString(arg, view); 68 | 69 | offset += ret.written; 70 | } 71 | 72 | WASM_VECTOR_LEN = offset; 73 | return ptr; 74 | } 75 | /** 76 | * @param {string} name 77 | */ 78 | export function greet(name) { 79 | const ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 80 | const len0 = WASM_VECTOR_LEN; 81 | wasm.greet(ptr0, len0); 82 | } 83 | 84 | let cachedInt32Memory0 = new Int32Array(); 85 | 86 | function getInt32Memory0() { 87 | if (cachedInt32Memory0.byteLength === 0) { 88 | cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); 89 | } 90 | return cachedInt32Memory0; 91 | } 92 | /** 93 | * @param {string} code 94 | * @returns {string} 95 | */ 96 | export function search(code) { 97 | try { 98 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 99 | const ptr0 = passStringToWasm0(code, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 100 | const len0 = WASM_VECTOR_LEN; 101 | wasm.search(retptr, ptr0, len0); 102 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 103 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 104 | return getStringFromWasm0(r0, r1); 105 | } finally { 106 | wasm.__wbindgen_add_to_stack_pointer(16); 107 | wasm.__wbindgen_free(r0, r1); 108 | } 109 | } 110 | 111 | async function load(module, imports) { 112 | if (typeof Response === 'function' && module instanceof Response) { 113 | if (typeof WebAssembly.instantiateStreaming === 'function') { 114 | try { 115 | return await WebAssembly.instantiateStreaming(module, imports); 116 | 117 | } catch (e) { 118 | if (module.headers.get('Content-Type') != 'application/wasm') { 119 | console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); 120 | 121 | } else { 122 | throw e; 123 | } 124 | } 125 | } 126 | 127 | const bytes = await module.arrayBuffer(); 128 | return await WebAssembly.instantiate(bytes, imports); 129 | 130 | } else { 131 | const instance = await WebAssembly.instantiate(module, imports); 132 | 133 | if (instance instanceof WebAssembly.Instance) { 134 | return { instance, module }; 135 | 136 | } else { 137 | return instance; 138 | } 139 | } 140 | } 141 | 142 | function getImports() { 143 | const imports = {}; 144 | imports.wbg = {}; 145 | imports.wbg.__wbg_alert_c5e057933ef1c53c = function(arg0, arg1) { 146 | alert(getStringFromWasm0(arg0, arg1)); 147 | }; 148 | 149 | return imports; 150 | } 151 | 152 | function initMemory(imports, maybe_memory) { 153 | 154 | } 155 | 156 | function finalizeInit(instance, module) { 157 | wasm = instance.exports; 158 | init.__wbindgen_wasm_module = module; 159 | cachedInt32Memory0 = new Int32Array(); 160 | cachedUint8Memory0 = new Uint8Array(); 161 | 162 | 163 | return wasm; 164 | } 165 | 166 | function initSync(module) { 167 | const imports = getImports(); 168 | 169 | initMemory(imports); 170 | 171 | if (!(module instanceof WebAssembly.Module)) { 172 | module = new WebAssembly.Module(module); 173 | } 174 | 175 | const instance = new WebAssembly.Instance(module, imports); 176 | 177 | return finalizeInit(instance, module); 178 | } 179 | 180 | async function init(input) { 181 | if (typeof input === 'undefined') { 182 | input = new URL('rust_ast_explorer_bg.wasm', import.meta.url); 183 | } 184 | const imports = getImports(); 185 | 186 | if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { 187 | input = fetch(input); 188 | } 189 | 190 | initMemory(imports); 191 | 192 | const { instance, module } = await load(await input, imports); 193 | 194 | return finalizeInit(instance, module); 195 | } 196 | 197 | export { initSync } 198 | export default init; 199 | -------------------------------------------------------------------------------- /pkg/rust_ast_explorer.mjs: -------------------------------------------------------------------------------- 1 | 2 | let wasm; 3 | 4 | const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); 5 | 6 | cachedTextDecoder.decode(); 7 | 8 | let cachedUint8Memory0 = new Uint8Array(); 9 | 10 | function getUint8Memory0() { 11 | if (cachedUint8Memory0.byteLength === 0) { 12 | cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); 13 | } 14 | return cachedUint8Memory0; 15 | } 16 | 17 | function getStringFromWasm0(ptr, len) { 18 | return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); 19 | } 20 | 21 | let WASM_VECTOR_LEN = 0; 22 | 23 | const cachedTextEncoder = new TextEncoder('utf-8'); 24 | 25 | const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' 26 | ? function (arg, view) { 27 | return cachedTextEncoder.encodeInto(arg, view); 28 | } 29 | : function (arg, view) { 30 | const buf = cachedTextEncoder.encode(arg); 31 | view.set(buf); 32 | return { 33 | read: arg.length, 34 | written: buf.length 35 | }; 36 | }); 37 | 38 | function passStringToWasm0(arg, malloc, realloc) { 39 | 40 | if (realloc === undefined) { 41 | const buf = cachedTextEncoder.encode(arg); 42 | const ptr = malloc(buf.length); 43 | getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); 44 | WASM_VECTOR_LEN = buf.length; 45 | return ptr; 46 | } 47 | 48 | let len = arg.length; 49 | let ptr = malloc(len); 50 | 51 | const mem = getUint8Memory0(); 52 | 53 | let offset = 0; 54 | 55 | for (; offset < len; offset++) { 56 | const code = arg.charCodeAt(offset); 57 | if (code > 0x7F) break; 58 | mem[ptr + offset] = code; 59 | } 60 | 61 | if (offset !== len) { 62 | if (offset !== 0) { 63 | arg = arg.slice(offset); 64 | } 65 | ptr = realloc(ptr, len, len = offset + arg.length * 3); 66 | const view = getUint8Memory0().subarray(ptr + offset, ptr + len); 67 | const ret = encodeString(arg, view); 68 | 69 | offset += ret.written; 70 | } 71 | 72 | WASM_VECTOR_LEN = offset; 73 | return ptr; 74 | } 75 | /** 76 | * @param {string} name 77 | */ 78 | export function greet(name) { 79 | const ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 80 | const len0 = WASM_VECTOR_LEN; 81 | wasm.greet(ptr0, len0); 82 | } 83 | 84 | let cachedInt32Memory0 = new Int32Array(); 85 | 86 | function getInt32Memory0() { 87 | if (cachedInt32Memory0.byteLength === 0) { 88 | cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); 89 | } 90 | return cachedInt32Memory0; 91 | } 92 | /** 93 | * @param {string} code 94 | * @returns {string} 95 | */ 96 | export function search(code) { 97 | try { 98 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 99 | const ptr0 = passStringToWasm0(code, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 100 | const len0 = WASM_VECTOR_LEN; 101 | wasm.search(retptr, ptr0, len0); 102 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 103 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 104 | return getStringFromWasm0(r0, r1); 105 | } finally { 106 | wasm.__wbindgen_add_to_stack_pointer(16); 107 | wasm.__wbindgen_free(r0, r1); 108 | } 109 | } 110 | 111 | async function load(module, imports) { 112 | if (typeof Response === 'function' && module instanceof Response) { 113 | if (typeof WebAssembly.instantiateStreaming === 'function') { 114 | try { 115 | return await WebAssembly.instantiateStreaming(module, imports); 116 | 117 | } catch (e) { 118 | if (module.headers.get('Content-Type') != 'application/wasm') { 119 | console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); 120 | 121 | } else { 122 | throw e; 123 | } 124 | } 125 | } 126 | 127 | const bytes = await module.arrayBuffer(); 128 | return await WebAssembly.instantiate(bytes, imports); 129 | 130 | } else { 131 | const instance = await WebAssembly.instantiate(module, imports); 132 | 133 | if (instance instanceof WebAssembly.Instance) { 134 | return { instance, module }; 135 | 136 | } else { 137 | return instance; 138 | } 139 | } 140 | } 141 | 142 | function getImports() { 143 | const imports = {}; 144 | imports.wbg = {}; 145 | imports.wbg.__wbg_alert_c5e057933ef1c53c = function(arg0, arg1) { 146 | alert(getStringFromWasm0(arg0, arg1)); 147 | }; 148 | 149 | return imports; 150 | } 151 | 152 | function initMemory(imports, maybe_memory) { 153 | 154 | } 155 | 156 | function finalizeInit(instance, module) { 157 | wasm = instance.exports; 158 | init.__wbindgen_wasm_module = module; 159 | cachedInt32Memory0 = new Int32Array(); 160 | cachedUint8Memory0 = new Uint8Array(); 161 | 162 | 163 | return wasm; 164 | } 165 | 166 | function initSync(module) { 167 | const imports = getImports(); 168 | 169 | initMemory(imports); 170 | 171 | if (!(module instanceof WebAssembly.Module)) { 172 | module = new WebAssembly.Module(module); 173 | } 174 | 175 | const instance = new WebAssembly.Instance(module, imports); 176 | 177 | return finalizeInit(instance, module); 178 | } 179 | 180 | async function init(input) { 181 | if (typeof input === 'undefined') { 182 | input = new URL('rust_ast_explorer_bg.wasm', import.meta.url); 183 | } 184 | const imports = getImports(); 185 | 186 | if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { 187 | input = fetch(input); 188 | } 189 | 190 | initMemory(imports); 191 | 192 | const { instance, module } = await load(await input, imports); 193 | 194 | return finalizeInit(instance, module); 195 | } 196 | 197 | export { initSync } 198 | export default init; 199 | --------------------------------------------------------------------------------