From f436ab7f731212439eee23e29ff9421343085665 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 5 Dec 2023 17:41:15 +0000 Subject: [PATCH] Added some examples --- examples/to_object.rs | 145 ++++++++++++++++++++++++++++++++ examples/to_object_unsafe.rs | 157 +++++++++++++++++++++++++++++++++++ lib.rs | 9 +- 3 files changed, 308 insertions(+), 3 deletions(-) create mode 100644 examples/to_object.rs create mode 100644 examples/to_object_unsafe.rs diff --git a/examples/to_object.rs b/examples/to_object.rs new file mode 100644 index 0000000..d99df2c --- /dev/null +++ b/examples/to_object.rs @@ -0,0 +1,145 @@ +use std::collections::HashMap; + +use simple_json_parser::{parse, JSONKey, RootJSONValue}; + +fn main() { + let content = r#"{ + "name": "ezno", + "version": "0.0.14", + "description": "A JavaScript compiler and TypeScript checker written in Rust with a focus on static analysis and runtime performance", + "license": "MIT", + "repository": "https://github.com/kaleidawave/ezno", + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "type": "module", + "exports": { + ".": { + "import": "./dist/index.mjs" + }, + "./initialised": { + "import": "./dist/initialised.mjs" + } + }, + "scripts": { + "clean": "rmdir dist && rmdir build", + "build": "cargo build --lib --target wasm32-unknown-unknown && npm run bind && npm run build-js", + "build-release": "cargo build --lib --release --target wasm32-unknown-unknown && npm run bind-release && npm run build-js", + "bind": "wasm-bindgen --out-dir build --target web ../../target/wasm32-unknown-unknown/debug/ezno_lib.wasm", + "bind-release": "wasm-bindgen --out-dir build --target web ../../target/wasm32-unknown-unknown/release/ezno_lib.wasm", + "build-js": "unbuild && cp ./build/ezno_lib_bg.wasm dist/shared && cp src/cli_node.cjs dist/cli.cjs", + "test": "npm run build && npm run run-tests", + "run-tests": "node test.mjs && deno run -A test.mjs" + }, + "keywords": [ + "typescript", + "checker", + "type-checker", + "compiler" + ], + "files": [ + "dist" + ], + "bin": { + "ezno": "./dist/cli.mjs" + }, + "author": { + "name": "Ben", + "email": "kaleidawave@gmail.com", + "url": "https://kaleidawave.github.io/" + }, + "funding": { + "type": "individual", + /* + multiline comment + */ + "url": "https://github.com/sponsors/kaleidawave" + }, + "build": { + "failOnWarn": false, + "entries": [ + { + "builder": "rollup", + "input": "./src/index" + }, + { + "builder": "rollup", + "input": "./src/initialised" + }, + { + "builder": "rollup", + "input": "./src/cli" + } + ], + // some comment + "rollup": { + "commonjs": true, + "esbuild": { + "target": "esnext" + } + } + }, + "devDependencies": { + "unbuild": "^1.1.2" + } + }"#; + + pub type Object = HashMap; + + #[derive(Debug)] + pub enum Value { + Object(Object), + String(String), + Number(String), + Boolean(bool), + Null, + } + + impl Value { + pub fn new_empty_object() -> Self { + Self::Object(HashMap::new()) + } + + pub fn set<'a>(&'a mut self, keys: &'a [JSONKey<'a>], value: RootJSONValue<'a>) { + if let Value::Object(ref mut obj) = self { + if let [last] = keys { + let name = match last { + JSONKey::Slice(s) => s.to_string(), + JSONKey::Index(i) => i.to_string(), + }; + let value = match value { + RootJSONValue::String(s) => Value::String(s.to_string()), + RootJSONValue::Number(n) => Value::Number(n.to_string()), + RootJSONValue::True => Value::Boolean(true), + RootJSONValue::False => Value::Boolean(false), + RootJSONValue::Null => Value::Null, + }; + let existing = obj.insert(name, value); + debug_assert!(existing.is_none()); + } else if let [first, others @ ..] = keys { + let name = match first { + JSONKey::Slice(s) => s.to_string(), + JSONKey::Index(i) => i.to_string(), + }; + obj.entry(name) + .or_insert_with(Value::new_empty_object) + .set(others, value); + } else { + unreachable!("empty keys") + } + } else { + unreachable!() + } + } + } + + let mut root = Value::new_empty_object(); + + let result = parse(content, |keys, value| root.set(keys, value)); + + match result { + Ok(()) => { + eprintln!("Object:\n{:#?}", root); + } + Err((idx, err)) => eprintln!("{err:?} @ {idx}"), + } +} diff --git a/examples/to_object_unsafe.rs b/examples/to_object_unsafe.rs new file mode 100644 index 0000000..931c9db --- /dev/null +++ b/examples/to_object_unsafe.rs @@ -0,0 +1,157 @@ +//! Uses iterations rather than recursion to build a object map. Unfortunately at the cost of using `unsafe` + +use std::collections::HashMap; + +use simple_json_parser::{parse, JSONKey, RootJSONValue}; + +fn main() { + let content = r#"{ + "name": "ezno", + "version": "0.0.14", + "description": "A JavaScript compiler and TypeScript checker written in Rust with a focus on static analysis and runtime performance", + "license": "MIT", + "repository": "https://github.com/kaleidawave/ezno", + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "type": "module", + "exports": { + ".": { + "import": "./dist/index.mjs" + }, + "./initialised": { + "import": "./dist/initialised.mjs" + } + }, + "scripts": { + "clean": "rmdir dist && rmdir build", + "build": "cargo build --lib --target wasm32-unknown-unknown && npm run bind && npm run build-js", + "build-release": "cargo build --lib --release --target wasm32-unknown-unknown && npm run bind-release && npm run build-js", + "bind": "wasm-bindgen --out-dir build --target web ../../target/wasm32-unknown-unknown/debug/ezno_lib.wasm", + "bind-release": "wasm-bindgen --out-dir build --target web ../../target/wasm32-unknown-unknown/release/ezno_lib.wasm", + "build-js": "unbuild && cp ./build/ezno_lib_bg.wasm dist/shared && cp src/cli_node.cjs dist/cli.cjs", + "test": "npm run build && npm run run-tests", + "run-tests": "node test.mjs && deno run -A test.mjs" + }, + "keywords": [ + "typescript", + "checker", + "type-checker", + "compiler" + ], + "files": [ + "dist" + ], + "bin": { + "ezno": "./dist/cli.mjs" + }, + "author": { + "name": "Ben", + "email": "kaleidawave@gmail.com", + "url": "https://kaleidawave.github.io/" + }, + "funding": { + "type": "individual", + /* + multiline comment + */ + "url": "https://github.com/sponsors/kaleidawave" + }, + "build": { + "failOnWarn": false, + "entries": [ + { + "builder": "rollup", + "input": "./src/index" + }, + { + "builder": "rollup", + "input": "./src/initialised" + }, + { + "builder": "rollup", + "input": "./src/cli" + } + ], + // some comment + "rollup": { + "commonjs": true, + "esbuild": { + "target": "esnext" + } + } + }, + "devDependencies": { + "unbuild": "^1.1.2" + } + }"#; + + pub type Object = HashMap; + + #[derive(Debug)] + pub enum Value { + Object(Object), + String(String), + Number(String), + Boolean(bool), + Null, + } + + impl Value { + pub fn new_empty_object() -> Self { + Self::Object(HashMap::new()) + } + } + + let mut root = Object::new(); + + let result = parse(content, |keys, value| { + let [path @ .., end] = keys else { + unreachable!("empty key change") + }; + let pointer = &mut root; + + let mut to_add_to: *mut Object = pointer; + + for key in path { + let name = match key { + JSONKey::Slice(s) => s.to_string(), + JSONKey::Index(i) => i.to_string(), + }; + if let Some(Value::Object(ref mut obj)) = + unsafe { (to_add_to.as_mut().unwrap()).get_mut(&name) } + { + to_add_to = obj; + } else { + let value = unsafe { + (to_add_to.as_mut().unwrap()) + .entry(name) + .or_insert_with(Value::new_empty_object) + }; + if let Value::Object(ref mut obj) = value { + to_add_to = obj; + } + } + } + let name = match end { + JSONKey::Slice(s) => s.to_string(), + JSONKey::Index(i) => i.to_string(), + }; + let value = match value { + RootJSONValue::String(s) => Value::String(s.to_string()), + RootJSONValue::Number(n) => Value::Number(n.to_string()), + RootJSONValue::True => Value::Boolean(true), + RootJSONValue::False => Value::Boolean(false), + RootJSONValue::Null => Value::Null, + }; + unsafe { + (to_add_to.as_mut().unwrap()).insert(name, value); + } + }); + + match result { + Ok(()) => { + eprintln!("Object:\n{:#?}", root); + } + Err((idx, err)) => eprintln!("{err:?} @ {idx}"), + } +} diff --git a/lib.rs b/lib.rs index 60e6ac8..1aaacd8 100644 --- a/lib.rs +++ b/lib.rs @@ -24,7 +24,7 @@ pub enum JSONParseError { pub fn parse<'a>( on: &'a str, - cb: impl for<'b> Fn(&'b [JSONKey<'a>], RootJSONValue<'a>), + mut cb: impl for<'b> FnMut(&'b [JSONKey<'a>], RootJSONValue<'a>), ) -> Result<(), (usize, JSONParseError)> { let chars = on.char_indices(); @@ -39,6 +39,7 @@ pub fn parse<'a>( start: usize, multiline: bool, last_was_asterisk: bool, + hash: bool, }, ExpectingValue, None, @@ -119,6 +120,7 @@ pub fn parse<'a>( State::Comment { ref mut last_was_asterisk, ref mut multiline, + hash, start, } => { if char == '\n' && !*multiline { @@ -127,7 +129,7 @@ pub fn parse<'a>( } else { state = State::InObject } - } else if char == '*' && start + 1 == idx { + } else if char == '*' && start + 1 == idx && !hash { *multiline = true } else if *multiline { if *last_was_asterisk && char == '/' { @@ -152,10 +154,11 @@ pub fn parse<'a>( start: idx + '"'.len_utf8(), escaped: false, }, - '/' => State::Comment { + c @ ('/' | '#') => State::Comment { last_was_asterisk: false, start: idx, multiline: false, + hash: c == '#', }, '0'..='9' | '-' => State::NumberValue { start: idx }, 't' | 'f' | 'n' => State::TrueFalseNull { start: idx },