├── examples ├── parol-app │ ├── .rustfmt.toml │ ├── Cargo.toml │ ├── json.par │ ├── app.rs │ ├── tests │ │ └── codegen.rs │ ├── grammar.rs │ ├── parser.rs │ └── grammar_trait.rs ├── lalrpop-app │ ├── build.rs │ ├── json_val.rs │ ├── Cargo.toml │ ├── app.rs │ ├── json.lalrpop │ └── foo.rs ├── null-app │ ├── Cargo.toml │ └── app.rs ├── peg-app │ ├── Cargo.toml │ ├── app.rs │ └── parser.rs ├── yap-app │ ├── Cargo.toml │ ├── app.rs │ └── parser.rs ├── logos-app │ ├── Cargo.toml │ ├── app.rs │ └── parser.rs ├── winnow-app │ ├── Cargo.toml │ ├── json.rs │ ├── app.rs │ └── parser.rs ├── chumsky-app │ ├── Cargo.toml │ ├── app.rs │ └── parser.rs ├── combine-app │ ├── Cargo.toml │ ├── app.rs │ └── parser.rs ├── nom-app │ ├── Cargo.toml │ ├── app.rs │ └── parser.rs ├── serde_json-app │ ├── Cargo.toml │ └── app.rs ├── pest-app │ ├── Cargo.toml │ ├── json.pest │ ├── app.rs │ └── parser.rs └── grmtools-app │ ├── json.l │ ├── json_val.rs │ ├── Cargo.toml │ ├── app.rs │ ├── build.rs │ └── json.y ├── .gitignore ├── third_party └── nativejson-benchmark │ └── LINK ├── Cargo.toml ├── .github ├── renovate.json5 ├── settings.yml └── workflows │ ├── audit.yml │ └── ci.yml ├── LICENSE ├── format.py ├── README.md ├── bench.py ├── deny.toml └── runs ├── 2022-07-14-Doomslug.json ├── 2023-03-23-seon.json └── 2024-01-26-seon.json /examples/parol-app/.rustfmt.toml: -------------------------------------------------------------------------------- 1 | style_edition = "2024" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | **/*.rs.bk 3 | /.idea 4 | /*.iml 5 | examples/lalrpop-app/json.rs 6 | -------------------------------------------------------------------------------- /third_party/nativejson-benchmark/LINK: -------------------------------------------------------------------------------- 1 | https://github.com/miloyip/nativejson-benchmark.git 2 | -------------------------------------------------------------------------------- /examples/lalrpop-app/build.rs: -------------------------------------------------------------------------------- 1 | extern crate lalrpop; 2 | 3 | fn main() { 4 | lalrpop::process_root().unwrap() 5 | } 6 | -------------------------------------------------------------------------------- /examples/null-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "null-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "null-app" 7 | path = "app.rs" 8 | 9 | [lints] 10 | workspace = true 11 | -------------------------------------------------------------------------------- /examples/peg-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "peg-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "peg-app" 7 | path = "app.rs" 8 | 9 | [dependencies] 10 | peg = "0.8.5" 11 | 12 | [lints] 13 | workspace = true 14 | -------------------------------------------------------------------------------- /examples/yap-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yap-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "yap-app" 7 | path = "app.rs" 8 | 9 | [dependencies] 10 | yap = "0.12" 11 | 12 | [lints] 13 | workspace = true 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "examples/*", 5 | ] 6 | 7 | [workspace.package] 8 | edition = "2021" 9 | 10 | [workspace.lints.rust] 11 | dead_code = "allow" # relying on `black_box` / debug repr 12 | -------------------------------------------------------------------------------- /examples/logos-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "logos-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "logos-app" 7 | path = "app.rs" 8 | 9 | [dependencies] 10 | logos = "0.15.1" 11 | 12 | [lints] 13 | workspace = true 14 | -------------------------------------------------------------------------------- /examples/winnow-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winnow-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "winnow-app" 7 | path = "app.rs" 8 | 9 | [dependencies] 10 | winnow = "0.7.13" 11 | 12 | [lints] 13 | workspace = true 14 | -------------------------------------------------------------------------------- /examples/chumsky-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chumsky-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "chumsky-app" 7 | path = "app.rs" 8 | 9 | [dependencies] 10 | chumsky = "0.11.1" 11 | 12 | [lints] 13 | workspace = true 14 | -------------------------------------------------------------------------------- /examples/combine-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "combine-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "combine-app" 7 | path = "app.rs" 8 | 9 | [dependencies] 10 | combine = "3.8.1" 11 | 12 | [lints] 13 | workspace = true 14 | -------------------------------------------------------------------------------- /examples/nom-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nom-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "nom-app" 7 | path = "app.rs" 8 | 9 | [dependencies] 10 | nom = "8.0.0" 11 | nom-language = "0.1.0" 12 | 13 | [lints] 14 | workspace = true 15 | -------------------------------------------------------------------------------- /examples/serde_json-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serde_json-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "serde_json-app" 7 | path = "app.rs" 8 | 9 | [dependencies] 10 | serde_json = "1.0.145" 11 | 12 | [lints] 13 | workspace = true 14 | -------------------------------------------------------------------------------- /examples/pest-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pest-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "pest-app" 7 | path = "app.rs" 8 | 9 | [dependencies] 10 | pest = "2.8.2" 11 | pest_derive = "2.8.2" 12 | 13 | [lints] 14 | workspace = true 15 | -------------------------------------------------------------------------------- /examples/grmtools-app/json.l: -------------------------------------------------------------------------------- 1 | %% 2 | "[^"]*" "STRING" 3 | -?(0|([1-9][0-9]*))(\.[0-9]*)?([eE][-+]?[0-9]+)? "FLOAT" 4 | \[ "[" 5 | \] "]" 6 | \{ "{" 7 | \} "}" 8 | : ":" 9 | , "," 10 | false "FALSE" 11 | null "NULL" 12 | true "TRUE" 13 | [\n\r\t ]+ ; 14 | . "UNMATCHED" 15 | -------------------------------------------------------------------------------- /examples/lalrpop-app/json_val.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | #[derive(Debug, PartialEq, Clone)] 4 | pub enum Value { 5 | Null, 6 | Boolean(bool), 7 | Str(String), 8 | Num(f64), 9 | Array(Vec), 10 | Object(HashMap), 11 | } 12 | -------------------------------------------------------------------------------- /examples/grmtools-app/json_val.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | #[derive(Debug, PartialEq, Clone)] 4 | pub enum Value { 5 | Null, 6 | Boolean(bool), 7 | Str(String), 8 | Num(f64), 9 | Array(Vec), 10 | Object(HashMap), 11 | } 12 | -------------------------------------------------------------------------------- /examples/winnow-app/json.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | #[derive(Debug, PartialEq, Clone)] 4 | pub enum JsonValue { 5 | Null, 6 | Boolean(bool), 7 | Str(String), 8 | Num(f64), 9 | Array(Vec), 10 | Object(HashMap), 11 | } 12 | -------------------------------------------------------------------------------- /examples/parol-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parol-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "parol-app" 7 | path = "app.rs" 8 | 9 | [dependencies] 10 | parol_runtime = "4.0.1" 11 | scnr2 = "0.3.3" 12 | 13 | [dev-dependencies] 14 | parol = "4.1.1" 15 | snapbox = "0.6.21" 16 | 17 | [lints] 18 | workspace = true 19 | -------------------------------------------------------------------------------- /examples/grmtools-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "grmtools-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "grmtools-app" 7 | path = "app.rs" 8 | 9 | [build-dependencies] 10 | cfgrammar = "0.14" 11 | lrlex = "0.14" 12 | lrpar = "0.14" 13 | 14 | [dependencies] 15 | cfgrammar = "0.14" 16 | lrlex = "0.14" 17 | lrpar = "0.14" 18 | 19 | [lints] 20 | workspace = true 21 | -------------------------------------------------------------------------------- /examples/lalrpop-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lalrpop-app" 3 | edition.workspace = true 4 | 5 | [[bin]] 6 | name = "lalrpop-app" 7 | path = "app.rs" 8 | 9 | [build-dependencies] 10 | lalrpop = { version = "0.22", features = ["lexer", "unicode"] } 11 | 12 | [dependencies] 13 | lalrpop-util = { version = "0.22", features = ["lexer", "unicode"] } 14 | 15 | [lints] 16 | workspace = true 17 | -------------------------------------------------------------------------------- /examples/null-app/app.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs}; 2 | 3 | fn main() -> Result<(), Box> { 4 | let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument")) 5 | .expect("Failed to read file"); 6 | 7 | #[cfg(debug_assertions)] 8 | { 9 | println!("{:#?}", src); 10 | } 11 | #[cfg(not(debug_assertions))] 12 | { 13 | std::hint::black_box(src); 14 | } 15 | 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /examples/yap-app/app.rs: -------------------------------------------------------------------------------- 1 | mod parser; 2 | 3 | use std::{env, fs}; 4 | 5 | fn main() { 6 | let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument")) 7 | .expect("Failed to read file"); 8 | 9 | match parser::parse(&src) { 10 | Ok(json) => { 11 | #[cfg(debug_assertions)] 12 | { 13 | println!("{:#?}", json); 14 | } 15 | #[cfg(not(debug_assertions))] 16 | { 17 | std::hint::black_box(json); 18 | } 19 | } 20 | Err(err) => { 21 | eprintln!("{:?}", err); 22 | std::process::exit(1); 23 | } 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /examples/peg-app/app.rs: -------------------------------------------------------------------------------- 1 | mod parser; 2 | 3 | use std::{env, fs}; 4 | 5 | fn main() { 6 | let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument")) 7 | .expect("Failed to read file"); 8 | 9 | match parser::parser::json(&src) { 10 | Ok(json) => { 11 | #[cfg(debug_assertions)] 12 | { 13 | println!("{:#?}", json); 14 | } 15 | #[cfg(not(debug_assertions))] 16 | { 17 | std::hint::black_box(json); 18 | } 19 | } 20 | Err(err) => { 21 | eprintln!("{}", err); 22 | std::process::exit(1); 23 | } 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /examples/parol-app/json.par: -------------------------------------------------------------------------------- 1 | 2 | %start Json 3 | %title "Json grammar" 4 | %comment "Derived from http://Json.org for parol by Joerg Singer." 5 | 6 | %% 7 | 8 | Json: Value 9 | ; 10 | 11 | Object 12 | : '{'^ Pair { ','^ Pair } '}'^ 13 | | '{'^ '}'^ 14 | ; 15 | 16 | Pair: String ':'^ Value 17 | ; 18 | 19 | Array 20 | : '['^ Value { ','^ Value } ']'^ 21 | | '['^ ']'^ 22 | ; 23 | 24 | Value 25 | : String 26 | | Number 27 | | Object 28 | | Array 29 | | 'true'^ 30 | | 'false'^ 31 | | 'null'^ 32 | ; 33 | 34 | String 35 | : /"(\\.|[^"])*"/ 36 | ; 37 | 38 | Number 39 | : /-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][-+]?(0|[1-9][0-9]*)?)?/ 40 | ; 41 | -------------------------------------------------------------------------------- /examples/serde_json-app/app.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs}; 2 | 3 | fn main() -> Result<(), Box> { 4 | let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument")) 5 | .expect("Failed to read file"); 6 | 7 | match serde_json::from_str::(&src) { 8 | Ok(json) => { 9 | #[cfg(debug_assertions)] 10 | { 11 | println!("{:#?}", json); 12 | } 13 | #[cfg(not(debug_assertions))] 14 | { 15 | std::hint::black_box(json); 16 | } 17 | } 18 | Err(e) => eprintln!("{e}"), 19 | } 20 | 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /examples/chumsky-app/app.rs: -------------------------------------------------------------------------------- 1 | //! This is a parser for JSON. 2 | //! Run it with the following command: 3 | //! cargo run --example json -- examples/sample.json 4 | 5 | mod parser; 6 | 7 | use std::{env, fs}; 8 | 9 | use chumsky::Parser; 10 | 11 | fn main() { 12 | let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument")) 13 | .expect("Failed to read file"); 14 | 15 | let (json, errs) = parser::parser().parse(&src).into_output_errors(); 16 | #[cfg(debug_assertions)] 17 | { 18 | println!("{:#?}", json); 19 | } 20 | #[cfg(not(debug_assertions))] 21 | { 22 | std::hint::black_box(json); 23 | } 24 | for err in errs { 25 | eprintln!("{err}"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/logos-app/app.rs: -------------------------------------------------------------------------------- 1 | mod parser; 2 | 3 | use std::{env, fs}; 4 | 5 | use logos::Logos as _; 6 | 7 | fn main() { 8 | let filename = env::args().nth(1).expect("Expected file argument"); 9 | let src = fs::read_to_string(&filename).expect("Failed to read file"); 10 | 11 | let mut lexer = parser::Token::lexer(src.as_str()); 12 | match parser::parse_value(&mut lexer) { 13 | Ok(json) => { 14 | #[cfg(debug_assertions)] 15 | { 16 | println!("{:#?}", json); 17 | } 18 | #[cfg(not(debug_assertions))] 19 | { 20 | std::hint::black_box(json); 21 | } 22 | } 23 | Err((msg, span)) => { 24 | eprintln!("{filename}:{span:?}: {msg}"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/lalrpop-app/app.rs: -------------------------------------------------------------------------------- 1 | extern crate lalrpop_util; 2 | 3 | use std::env; 4 | use std::fs; 5 | 6 | #[rustfmt::skip] 7 | #[allow(clippy::all)] 8 | mod json; 9 | mod json_val; 10 | 11 | fn main() { 12 | let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument")) 13 | .expect("Failed to read file"); 14 | 15 | match json::ValueParser::new().parse(&src) { 16 | Ok(json) => { 17 | #[cfg(debug_assertions)] 18 | { 19 | println!("{:#?}", json); 20 | } 21 | #[cfg(not(debug_assertions))] 22 | { 23 | std::hint::black_box(json); 24 | } 25 | } 26 | Err(err) => { 27 | eprintln!("{}", err); 28 | std::process::exit(1); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/combine-app/app.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate combine; 3 | 4 | mod parser; 5 | 6 | use std::{env, fs}; 7 | 8 | use combine::Parser; 9 | 10 | fn main() { 11 | let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument")) 12 | .expect("Failed to read file"); 13 | 14 | let mut parser = parser::json_value(); 15 | match parser.easy_parse(src.as_bytes()) { 16 | Ok(json) => { 17 | #[cfg(debug_assertions)] 18 | { 19 | println!("{:#?}", json); 20 | } 21 | #[cfg(not(debug_assertions))] 22 | { 23 | std::hint::black_box(json); 24 | } 25 | } 26 | Err(err) => { 27 | eprintln!("{:#?}", err); 28 | std::process::exit(1); 29 | } 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /examples/parol-app/app.rs: -------------------------------------------------------------------------------- 1 | mod grammar; 2 | mod grammar_trait; 3 | mod parser; 4 | 5 | use std::{env, fs}; 6 | 7 | use parol_runtime::Report; 8 | 9 | struct JSONErrorReporter; 10 | impl Report for JSONErrorReporter {} 11 | 12 | fn main() { 13 | let path = env::args().nth(1).expect("Expected file argument"); 14 | let src = fs::read_to_string(&path).expect("Failed to read file"); 15 | 16 | let mut json_grammar = grammar::Grammar::new(); 17 | match parser::parse(&src, &path, &mut json_grammar) { 18 | Ok(_) => { 19 | #[cfg(debug_assertions)] 20 | { 21 | println!("{}", json_grammar); 22 | } 23 | } 24 | Err(err) => { 25 | let _ = JSONErrorReporter::report_error(&err, &path); 26 | std::process::exit(1); 27 | } 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /examples/grmtools-app/app.rs: -------------------------------------------------------------------------------- 1 | use lrlex::lrlex_mod; 2 | use lrpar::lrpar_mod; 3 | use std::{env, fs}; 4 | 5 | lrlex_mod!("json.l"); 6 | lrpar_mod!("json.y"); 7 | 8 | mod json_val; 9 | 10 | fn main() { 11 | let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument")) 12 | .expect("Failed to read file"); 13 | 14 | let lexerdef = json_l::lexerdef(); 15 | let lexer = lexerdef.lexer(&src); 16 | let (res, errs) = json_y::parse(&lexer); 17 | for e in errs { 18 | println!("{}", e.pp(&lexer, &json_y::token_epp)); 19 | } 20 | match res { 21 | Some(r) => { 22 | #[cfg(debug_assertions)] 23 | println!("{r:#?}"); 24 | #[cfg(not(debug_assertions))] 25 | let _ = std::hint::black_box(r); 26 | } 27 | None => panic!(), 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | schedule: [ 3 | 'before 5am on the first day of the month', 4 | ], 5 | semanticCommits: 'enabled', 6 | configMigration: true, 7 | dependencyDashboard: true, 8 | packageRules: [ 9 | // Goals: 10 | // - Rollup safe upgrades to reduce CI runner load 11 | // - Have lockfile and manifest in-sync 12 | { 13 | matchManagers: [ 14 | 'cargo', 15 | ], 16 | matchCurrentVersion: '>=0.1.0', 17 | matchUpdateTypes: [ 18 | 'patch', 19 | ], 20 | automerge: true, 21 | groupName: 'compatible', 22 | }, 23 | { 24 | matchManagers: [ 25 | 'cargo', 26 | ], 27 | matchCurrentVersion: '>=1.0.0', 28 | matchUpdateTypes: [ 29 | 'minor', 30 | ], 31 | automerge: true, 32 | groupName: 'compatible', 33 | }, 34 | ], 35 | } 36 | -------------------------------------------------------------------------------- /examples/winnow-app/app.rs: -------------------------------------------------------------------------------- 1 | mod json; 2 | mod parser; 3 | 4 | use std::{env, fs}; 5 | 6 | use winnow::error::ContextError; 7 | use winnow::prelude::*; 8 | 9 | fn main() { 10 | let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument")) 11 | .expect("Failed to read file"); 12 | 13 | match parser::json:: 14 | .parse(src.as_str()) 15 | .map_err(|e| e.to_string()) 16 | { 17 | Ok(json) => { 18 | #[cfg(debug_assertions)] 19 | { 20 | println!("{:#?}", json); 21 | } 22 | #[cfg(not(debug_assertions))] 23 | { 24 | std::hint::black_box(json); 25 | } 26 | } 27 | Err(err) => { 28 | eprintln!("{}", err); 29 | std::process::exit(1); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/grmtools-app/build.rs: -------------------------------------------------------------------------------- 1 | use cfgrammar::yacc::YaccKind; 2 | use lrlex::CTLexerBuilder; 3 | use std::{env, path::PathBuf}; 4 | 5 | fn main() { 6 | CTLexerBuilder::new() 7 | .lrpar_config(|ctp| { 8 | ctp.yacckind(YaccKind::Grmtools) 9 | .grammar_path("json.y") 10 | .output_path( 11 | [env::var("OUT_DIR").unwrap().as_str(), "json.y.rs"] 12 | .iter() 13 | .collect::(), 14 | ) 15 | .mod_name("json_y") 16 | }) 17 | .lexer_path("json.l") 18 | .output_path( 19 | [env::var("OUT_DIR").unwrap().as_str(), "json.l.rs"] 20 | .iter() 21 | .collect::(), 22 | ) 23 | .mod_name("json_l") 24 | .build() 25 | .unwrap(); 26 | } 27 | -------------------------------------------------------------------------------- /examples/pest-app/json.pest: -------------------------------------------------------------------------------- 1 | json = _{ SOI ~ (object | array) ~ EOI } 2 | 3 | value = _{ object | array | string | number | boolean | null } 4 | 5 | object = { 6 | "{" ~ "}" | 7 | "{" ~ pair ~ ("," ~ pair)* ~ "}" 8 | } 9 | pair = { string ~ ":" ~ value } 10 | 11 | array = { 12 | "[" ~ "]" | 13 | "[" ~ value ~ ("," ~ value)* ~ "]" 14 | } 15 | 16 | string = ${ "\"" ~ inner ~ "\"" } 17 | inner = @{ char* } 18 | char = { 19 | !("\"" | "\\") ~ ANY 20 | | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") 21 | | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) 22 | } 23 | 24 | number = @{ 25 | "-"? 26 | ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) 27 | ~ ("." ~ ASCII_DIGIT*)? 28 | ~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)? 29 | } 30 | 31 | boolean = { "true" | "false" } 32 | 33 | null = { "null" } 34 | 35 | WHITESPACE = _{ " " | "\t" | "\r" | "\n" } 36 | -------------------------------------------------------------------------------- /examples/nom-app/app.rs: -------------------------------------------------------------------------------- 1 | mod parser; 2 | 3 | use std::{env, fs}; 4 | 5 | use nom::Err; 6 | use nom_language::error::convert_error; 7 | use nom_language::error::VerboseError; 8 | 9 | fn main() { 10 | let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument")) 11 | .expect("Failed to read file"); 12 | 13 | match parser::root::>(src.as_str()) { 14 | Ok(json) => { 15 | #[cfg(debug_assertions)] 16 | { 17 | println!("{:#?}", json); 18 | } 19 | #[cfg(not(debug_assertions))] 20 | { 21 | std::hint::black_box(json); 22 | } 23 | } 24 | Err(Err::Error(err)) | Err(Err::Failure(err)) => { 25 | let err = convert_error(src.as_str(), err); 26 | eprintln!("{}", err); 27 | std::process::exit(1); 28 | } 29 | Err(err) => { 30 | eprintln!("{}", err); 31 | std::process::exit(1); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/lalrpop-app/json.lalrpop: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::str::FromStr; 3 | use crate::json_val::Value; 4 | 5 | grammar; 6 | 7 | // https://datatracker.ietf.org/doc/html/rfc7159 8 | 9 | pub Value: Value = { 10 | Object => Value::Object(<>), 11 | Array => Value::Array(<>), 12 | Number => Value::Num(<>), 13 | String => Value::Str(<>), 14 | "false" => Value::Boolean(false), 15 | "null" => Value::Null, 16 | "true" => Value::Boolean(true), 17 | }; 18 | 19 | Object: HashMap = { 20 | "{" > "}" => HashMap::from_iter(<>) 21 | }; 22 | 23 | Member: (String, Value) = { 24 | ":" => (s,v), 25 | }; 26 | 27 | Array: Vec = { 28 | "[" > "]", 29 | }; 30 | 31 | Number: f64 = { 32 | r"-?(0|([1-9][0-9]*))(\.[0-9]*)?([eE][-+]?[0-9]+)?" => f64::from_str(<>).unwrap() 33 | }; 34 | 35 | String: String = { 36 | r#""[^"]*""# => <>.into(), 37 | }; 38 | 39 | Comma: Vec = { 40 | ",")*> => { 41 | v.into_iter().chain(e).collect() 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Evgeniy Reizner 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /examples/pest-app/app.rs: -------------------------------------------------------------------------------- 1 | // pest. The Elegant Parser 2 | // Copyright (c) 2018 Dragoș Tiselice 3 | // 4 | // Licensed under the Apache License, Version 2.0 5 | // or the MIT 6 | // license , at your 7 | // option. All files in the project carrying such notice may not be copied, 8 | // modified, or distributed except according to those terms. 9 | 10 | mod parser; 11 | 12 | use std::{env, fs}; 13 | 14 | fn main() { 15 | let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument")) 16 | .expect("Failed to read file"); 17 | 18 | match parser::parse_json_file(&src) { 19 | Ok(json) => { 20 | #[cfg(debug_assertions)] 21 | { 22 | println!("{:#?}", json); 23 | } 24 | #[cfg(not(debug_assertions))] 25 | { 26 | std::hint::black_box(json); 27 | } 28 | } 29 | Err(err) => { 30 | eprintln!("{}", err); 31 | std::process::exit(1); 32 | } 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /examples/parol-app/tests/codegen.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn codegen() { 3 | use snapbox::Data; 4 | use snapbox::assert_data_eq; 5 | 6 | let tmp_dir = env!("CARGO_TARGET_TMPDIR"); 7 | let mut output_dir = std::path::PathBuf::from(tmp_dir); 8 | output_dir.push("parol"); 9 | std::fs::create_dir_all(&output_dir).unwrap(); 10 | 11 | let expected_root = std::path::Path::new("."); 12 | 13 | let mut builder = parol::build::Builder::with_explicit_output_dir(&output_dir); 14 | builder.grammar_file("json.par"); 15 | builder.parser_output_file("parser.rs"); 16 | builder.actions_output_file("grammar_trait.rs"); 17 | builder.trim_parse_tree(); 18 | builder.minimize_boxed_types(); 19 | builder.generate_parser().unwrap(); 20 | 21 | for entry in std::fs::read_dir(&output_dir).unwrap() { 22 | let entry = entry.unwrap(); 23 | let actual_path = entry.path(); 24 | let actual_name = entry.file_name(); 25 | let actual = std::fs::read_to_string(&actual_path).unwrap(); 26 | let expected_path = expected_root.join(actual_name); 27 | assert_data_eq!(actual, Data::read_from(&expected_path, None).raw()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/lalrpop-app/foo.rs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env cargo 2 | 3 | #![cargo(r#" 4 | [dependencies] 5 | foo = "1.2.3" 6 | "#)] 7 | 8 | 9 | 10 | 11 | #!/usr/bin/env cargo 12 | 13 | /*!```cargo 14 | [dependencies] 15 | foo = "1.2.3" 16 | */ 17 | 18 | 19 | 20 | #!/usr/bin/env cargo 21 | 22 | //! ```cargo 23 | //! [dependencies] 24 | //! foo = "1.2.3" 25 | //! ``` 26 | 27 | 28 | 29 | #!/usr/bin/env cargo 30 | --- 31 | [dependencies] 32 | foo = "1.2.3" 33 | --- 34 | 35 | 36 | 37 | #!/usr/bin/env cargo 38 | #[cargo(version = "1.2.3")] 39 | extern crate foo; 40 | 41 | 42 | 43 | 44 | 45 | 46 | - Module level: nice to have at top 47 | - Attribute has a lot of concepts if we have to do string 48 | - 49 | 50 | 51 | In educational material, a comment for dependencies likely is already being used, so this is no different 52 | 53 | custom class to hide the code block 54 | "hidden" attribute in infostring 55 | 56 | attributes are a "here be dragons" 57 | 58 | 59 | "I don't event teach attributes until the second course" 60 | - Nathan 61 | 62 | 63 | Disable `mod foo;`? 64 | 65 | 66 | doc-comment 67 | - easy to understand when seen 68 | - but concern from Nathan on teaching people to write it from scratch 69 | -------------------------------------------------------------------------------- /.github/settings.yml: -------------------------------------------------------------------------------- 1 | # These settings are synced to GitHub by https://probot.github.io/apps/settings/ 2 | 3 | repository: 4 | description: Comparing parser APIs 5 | topics: rust parser 6 | has_issues: true 7 | has_projects: false 8 | has_wiki: false 9 | has_downloads: true 10 | default_branch: main 11 | 12 | allow_squash_merge: true 13 | allow_merge_commit: true 14 | allow_rebase_merge: true 15 | 16 | # Manual: allow_auto_merge: true, see https://github.com/probot/settings/issues/402 17 | delete_branch_on_merge: true 18 | 19 | labels: 20 | # Type 21 | - name: bug 22 | color: '#b60205' 23 | description: Not as expected 24 | - name: enhancement 25 | color: '#1d76db' 26 | description: Improve the expected 27 | # Flavor 28 | - name: question 29 | color: "#cc317c" 30 | description: Uncertainty is involved 31 | - name: breaking-change 32 | color: "#e99695" 33 | - name: good first issue 34 | color: '#c2e0c6' 35 | description: Help wanted! 36 | 37 | branches: 38 | - name: main 39 | protection: 40 | required_pull_request_reviews: null 41 | required_conversation_resolution: true 42 | required_status_checks: 43 | # Required. Require branches to be up to date before merging. 44 | strict: false 45 | contexts: ["CI"] 46 | enforce_admins: false 47 | restrictions: null 48 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request: 8 | paths: 9 | - '**/Cargo.toml' 10 | - '**/Cargo.lock' 11 | push: 12 | branches: 13 | - main 14 | 15 | env: 16 | RUST_BACKTRACE: 1 17 | CARGO_TERM_COLOR: always 18 | CLICOLOR: 1 19 | 20 | concurrency: 21 | group: "${{ github.workflow }}-${{ github.ref }}" 22 | cancel-in-progress: true 23 | 24 | jobs: 25 | security_audit: 26 | permissions: 27 | issues: write # to create issues (actions-rs/audit-check) 28 | checks: write # to create check (actions-rs/audit-check) 29 | runs-on: ubuntu-latest 30 | # Prevent sudden announcement of a new advisory from failing ci: 31 | continue-on-error: true 32 | steps: 33 | - name: Checkout repository 34 | uses: actions/checkout@v6 35 | - uses: actions-rs/audit-check@v1 36 | with: 37 | token: ${{ secrets.GITHUB_TOKEN }} 38 | 39 | cargo_deny: 40 | permissions: 41 | issues: write # to create issues (actions-rs/audit-check) 42 | checks: write # to create check (actions-rs/audit-check) 43 | runs-on: ubuntu-latest 44 | strategy: 45 | matrix: 46 | checks: 47 | - bans licenses sources 48 | steps: 49 | - uses: actions/checkout@v6 50 | - uses: EmbarkStudios/cargo-deny-action@v2 51 | with: 52 | command: check ${{ matrix.checks }} 53 | rust-version: stable 54 | -------------------------------------------------------------------------------- /examples/grmtools-app/json.y: -------------------------------------------------------------------------------- 1 | %start Object 2 | %expect-unused Unmatched "UNMATCHED" 3 | 4 | %% 5 | 6 | Object -> Result>: 7 | "{" ObjectMembersOpt "}" { Ok(Value::Object(HashMap::from_iter($2?))) } 8 | ; 9 | 10 | ObjectMembersOpt -> Result, Box>: 11 | ObjectMembers { $1 } 12 | | { Ok(Vec::new()) } 13 | ; 14 | 15 | ObjectMembers -> Result, Box>: 16 | ObjectMembers "," ObjectMember { flatten($1, $3) } 17 | | ObjectMember { Ok(vec![$1?]) } 18 | ; 19 | 20 | ObjectMember -> Result<(String, Value), Box>: 21 | "STRING" ":" Member { 22 | let s = $lexer.span_str($1.unwrap().span()); 23 | Ok((s[1..s.len() - 1].to_owned(), $3?)) 24 | } 25 | ; 26 | 27 | Member -> Result>: 28 | "[" ArrayMembersOpt "]" { Ok(Value::Array($2?)) } 29 | | "FALSE" { Ok(Value::Boolean(false)) } 30 | | "FLOAT" { Ok(Value::Num($lexer.span_str($1?.span()).parse::().unwrap())) } 31 | | "NULL" { Ok(Value::Null) } 32 | | Object { $1 } 33 | | "STRING" { 34 | let s = $lexer.span_str($1.unwrap().span()); 35 | Ok(Value::Str(s[1..s.len() - 1].to_owned())) 36 | } 37 | | "TRUE" { Ok(Value::Boolean(true)) } 38 | ; 39 | 40 | ArrayMembersOpt -> Result, Box>: 41 | ArrayMembers { $1 } 42 | | { Ok(Vec::new()) } 43 | ; 44 | 45 | ArrayMembers -> Result, Box>: 46 | ArrayMembers "," Member { flatten($1, $3) } 47 | | Member { Ok(vec![$1?])} 48 | ; 49 | 50 | Unmatched -> (): 51 | "UNMATCHED" { } 52 | ; 53 | 54 | %% 55 | 56 | use crate::json_val::Value; 57 | use std::{collections::HashMap, error::Error}; 58 | 59 | fn flatten(lhs: Result, Box>, rhs: Result>) 60 | -> Result, Box> 61 | { 62 | let mut lhs = lhs?; 63 | let rhs = rhs?; 64 | lhs.push(rhs); 65 | Ok(lhs) 66 | } 67 | -------------------------------------------------------------------------------- /format.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pathlib 4 | import json 5 | import argparse 6 | 7 | 8 | def main(): 9 | repo_root = pathlib.Path(__name__).parent 10 | runs_root = repo_root / "runs" 11 | default_run_path = sorted(runs_root.glob("*.json"))[-1] 12 | 13 | parser = argparse.ArgumentParser() 14 | parser.add_argument("--run", metavar="PATH", type=pathlib.Path, default=default_run_path, help="Default: %(default)s") 15 | args = parser.parse_args() 16 | 17 | data = json.loads(args.run.read_text()) 18 | cases = sorted(data["libs"].values(), key=lambda c: (c["crate"] if c["crate"] else "", c["name"])) 19 | 20 | print("Name | Overhead (release) | Build (debug) | Parse (release) | Downloads | Version") 21 | print("-----|--------------------|---------------|-----------------|-----------|--------") 22 | for case in cases: 23 | if case["name"] != "null": 24 | count_link = "![Download count](https://img.shields.io/crates/dr/{})".format(case["crate"]) 25 | else: 26 | count_link = "-" 27 | row = [ 28 | case["name"], 29 | fmt_size(case, cases[0]), 30 | fmt_time(case, "build"), 31 | fmt_time(case, "run"), 32 | count_link, 33 | case["version"] if case["version"] else "-", 34 | ] 35 | print(" | ".join(row)) 36 | print() 37 | print(f"*System: {data['os']} {data['os_ver']} ({data['arch']}), {data.get('rustc', '')} w/ `-j {data['cpus']}`*") 38 | 39 | 40 | def fmt_time(case, bench): 41 | bench = case[bench] 42 | if bench is None: 43 | return "N/A" 44 | 45 | value = bench["results"][0]["median"] 46 | if value < 1: 47 | value *= 1000 48 | return "{:.0f}ms".format(value) 49 | else: 50 | return "{:.0f}s".format(value) 51 | 52 | 53 | def fmt_size(case, null_case): 54 | delta = (case["size"] - null_case["size"]) / 1024 55 | return "{:,.0f} KiB".format(delta) 56 | 57 | 58 | if __name__ == "__main__": 59 | main() 60 | -------------------------------------------------------------------------------- /examples/pest-app/parser.rs: -------------------------------------------------------------------------------- 1 | // pest. The Elegant Parser 2 | // Copyright (c) 2018 Dragoș Tiselice 3 | // 4 | // Licensed under the Apache License, Version 2.0 5 | // or the MIT 6 | // license , at your 7 | // option. All files in the project carrying such notice may not be copied, 8 | // modified, or distributed except according to those terms. 9 | 10 | use std::collections::HashMap; 11 | 12 | use pest::error::Error; 13 | use pest::Parser; 14 | use pest_derive::Parser; 15 | 16 | #[derive(Parser)] 17 | #[grammar = "json.pest"] 18 | struct JSONParser; 19 | 20 | #[derive(Debug, PartialEq)] 21 | pub enum Json<'i> { 22 | Null, 23 | Bool(bool), 24 | Number(f64), 25 | String(&'i str), 26 | Array(Vec>), 27 | Object(HashMap<&'i str, Json<'i>>), 28 | } 29 | 30 | pub fn parse_json_file(input: &str) -> Result, Error> { 31 | use pest::iterators::Pair; 32 | 33 | let json = JSONParser::parse(Rule::json, input)?.next().unwrap(); 34 | 35 | fn parse_value(pair: Pair) -> Json { 36 | match pair.as_rule() { 37 | Rule::object => Json::Object( 38 | pair.into_inner() 39 | .map(|pair| { 40 | let mut inner_rules = pair.into_inner(); 41 | let name = inner_rules 42 | .next() 43 | .unwrap() 44 | .into_inner() 45 | .next() 46 | .unwrap() 47 | .as_str(); 48 | let value = parse_value(inner_rules.next().unwrap()); 49 | (name, value) 50 | }) 51 | .collect(), 52 | ), 53 | Rule::array => Json::Array(pair.into_inner().map(parse_value).collect()), 54 | Rule::string => Json::String(pair.into_inner().next().unwrap().as_str()), 55 | Rule::number => Json::Number(pair.as_str().parse().unwrap()), 56 | Rule::boolean => Json::Bool(pair.as_str().parse().unwrap()), 57 | Rule::null => Json::Null, 58 | Rule::json 59 | | Rule::EOI 60 | | Rule::pair 61 | | Rule::value 62 | | Rule::inner 63 | | Rule::char 64 | | Rule::WHITESPACE => unreachable!(), 65 | } 66 | } 67 | 68 | Ok(parse_value(json)) 69 | } 70 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request: 8 | push: 9 | branches: 10 | - main 11 | 12 | env: 13 | RUST_BACKTRACE: 1 14 | CARGO_TERM_COLOR: always 15 | CLICOLOR: 1 16 | 17 | concurrency: 18 | group: "${{ github.workflow }}-${{ github.ref }}" 19 | cancel-in-progress: true 20 | 21 | jobs: 22 | smoke: 23 | name: Quick Check 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout repository 27 | uses: actions/checkout@v6 28 | - name: Install Rust 29 | uses: dtolnay/rust-toolchain@stable 30 | with: 31 | toolchain: stable 32 | - uses: Swatinem/rust-cache@v2 33 | - name: Default features 34 | run: cargo check --workspace --all-targets 35 | test: 36 | name: Test 37 | runs-on: ubuntu-latest 38 | steps: 39 | - name: Checkout repository 40 | uses: actions/checkout@v6 41 | - name: Install Rust 42 | uses: dtolnay/rust-toolchain@stable 43 | with: 44 | toolchain: stable 45 | - uses: Swatinem/rust-cache@v2 46 | - name: Default features 47 | run: cargo test --workspace 48 | rustfmt: 49 | name: rustfmt 50 | runs-on: ubuntu-latest 51 | steps: 52 | - name: Checkout repository 53 | uses: actions/checkout@v6 54 | - name: Install Rust 55 | uses: dtolnay/rust-toolchain@stable 56 | with: 57 | toolchain: stable 58 | components: rustfmt 59 | - uses: Swatinem/rust-cache@v2 60 | - name: Check formatting 61 | run: cargo fmt --all -- --check 62 | clippy: 63 | name: clippy 64 | runs-on: ubuntu-latest 65 | permissions: 66 | security-events: write # to upload sarif results 67 | steps: 68 | - name: Checkout repository 69 | uses: actions/checkout@v6 70 | - name: Install Rust 71 | uses: dtolnay/rust-toolchain@stable 72 | with: 73 | toolchain: stable 74 | components: clippy 75 | - uses: Swatinem/rust-cache@v2 76 | - name: Install SARIF tools 77 | run: cargo install clippy-sarif --locked 78 | - name: Install SARIF tools 79 | run: cargo install sarif-fmt --locked 80 | - name: Check 81 | run: > 82 | cargo clippy --workspace --all-features --all-targets --message-format=json 83 | | clippy-sarif 84 | | tee clippy-results.sarif 85 | | sarif-fmt 86 | continue-on-error: true 87 | - name: Upload 88 | uses: github/codeql-action/upload-sarif@v3 89 | with: 90 | sarif_file: clippy-results.sarif 91 | wait-for-processing: true 92 | - name: Report status 93 | run: cargo clippy --workspace --all-features --all-targets -- -D warnings --allow deprecated 94 | -------------------------------------------------------------------------------- /examples/peg-app/parser.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Cow, collections::HashMap, str::FromStr}; 2 | 3 | #[derive(Debug, PartialEq, Clone)] 4 | pub enum JsonValue { 5 | Null, 6 | Boolean(bool), 7 | Str(String), 8 | Num(f64), 9 | Array(Vec), 10 | Object(HashMap), 11 | } 12 | 13 | peg::parser!(pub grammar parser() for str { 14 | 15 | pub rule json() -> JsonValue 16 | = _ value:value() _ { value } 17 | 18 | rule _() = [' ' | '\t' | '\r' | '\n']* 19 | rule value_separator() = _ "," _ 20 | 21 | rule value() -> JsonValue 22 | = boolean() / null() / object() / array() / number() / string() 23 | 24 | rule null() -> JsonValue 25 | = "null" { JsonValue::Null } 26 | 27 | rule boolean() -> JsonValue 28 | = "true" { JsonValue::Boolean(true) } 29 | / "false" { JsonValue::Boolean(false) } 30 | 31 | rule object() -> JsonValue 32 | = "{" _ elements:(member() ** value_separator()) _ "}" { 33 | JsonValue::Object(elements.into_iter().collect()) 34 | } 35 | 36 | rule member() -> (String, JsonValue) 37 | = key:raw_string() _ ":" _ value:value() { (key, value) } 38 | 39 | rule array() -> JsonValue 40 | = "[" _ elements:(value() ** value_separator()) _ "]" { 41 | JsonValue::Array(elements) 42 | } 43 | 44 | rule string() -> JsonValue 45 | = value:raw_string() { JsonValue::Str(value) } 46 | 47 | rule raw_string() -> String 48 | = "\"" slices:string_slice()* "\"" { slices.concat() } 49 | 50 | /// A substring of same-kind (escaped or unescaped) characters 51 | rule string_slice() -> Cow<'input, str> 52 | = value:string_characters() { Cow::Borrowed(value) } 53 | / value:string_escapes() { Cow::Owned(value.into_iter().collect()) } 54 | 55 | /// A substring of unescaped characters 56 | rule string_characters() -> &'input str 57 | = $([^ '\"' | '\\']+) 58 | 59 | /// A substring of escaped characters 60 | rule string_escapes() -> Vec 61 | = ("\\" value:string_escape_char() { value })+ 62 | 63 | /// Handles a single escape 64 | rule string_escape_char() -> char 65 | = "\"" { '"' } 66 | / "\\" { '\\' } 67 | / "/" { '/' } 68 | / "b" { '\x08' } 69 | / "f" { '\x0C' } 70 | / "n" { '\n' } 71 | / "r" { '\r' } 72 | / "t" { '\t' } 73 | / "u" digits:$(hex_digit()*<4>) { ? 74 | let value = u16::from_str_radix(digits, 16).unwrap(); 75 | char::from_u32(value.into()).ok_or("invalid unicode escape") 76 | } 77 | 78 | rule hex_digit() 79 | = ['0'..='9' | 'a'..='f' | 'A'..='F'] 80 | 81 | rule number() -> JsonValue 82 | = "-"? value:$(int() frac()? exp()?) { ? 83 | Ok(JsonValue::Num(f64::from_str(value).map_err(|_| "invalid number")?)) 84 | } 85 | 86 | rule int() 87 | = ['0'] / ['1'..='9']['0'..='9']* 88 | 89 | rule exp() 90 | = ("e" / "E") ("-" / "+")? ['0'..='9']*<1,> 91 | 92 | rule frac() 93 | = "." ['0'..='9']*<1,> 94 | }); 95 | -------------------------------------------------------------------------------- /examples/chumsky-app/parser.rs: -------------------------------------------------------------------------------- 1 | //! This is a parser for JSON. 2 | //! Run it with the following command: 3 | //! cargo run --example json -- examples/sample.json 4 | 5 | use chumsky::prelude::*; 6 | use std::collections::HashMap; 7 | 8 | #[derive(Clone, Debug)] 9 | pub enum Json { 10 | Invalid, 11 | Null, 12 | Bool(bool), 13 | Str(String), 14 | Num(f64), 15 | Array(Vec), 16 | Object(HashMap), 17 | } 18 | 19 | pub fn parser<'a>() -> impl Parser<'a, &'a str, Json> { 20 | recursive(|value| { 21 | let digits = text::digits(10).to_slice(); 22 | 23 | let frac = just('.').then(digits); 24 | 25 | let exp = just('e') 26 | .or(just('E')) 27 | .then(one_of("+-").or_not()) 28 | .then(digits); 29 | 30 | let number = just('-') 31 | .or_not() 32 | .then(text::int(10)) 33 | .then(frac.or_not()) 34 | .then(exp.or_not()) 35 | .to_slice() 36 | .map(|s: &str| s.parse().unwrap()); 37 | 38 | let escape = just('\\') 39 | .then(choice(( 40 | just('\\'), 41 | just('/'), 42 | just('"'), 43 | just('b').to('\x08'), 44 | just('f').to('\x0C'), 45 | just('n').to('\n'), 46 | just('r').to('\r'), 47 | just('t').to('\t'), 48 | just('u').ignore_then(text::digits(16).exactly(4).to_slice().validate( 49 | |digits, _e, emitter| { 50 | char::from_u32(u32::from_str_radix(digits, 16).unwrap()).unwrap_or_else( 51 | || { 52 | emitter.emit(Default::default()); 53 | '\u{FFFD}' // unicode replacement character 54 | }, 55 | ) 56 | }, 57 | )), 58 | ))) 59 | .ignored(); 60 | 61 | let string = none_of("\\\"") 62 | .ignored() 63 | .or(escape) 64 | .repeated() 65 | .to_slice() 66 | .map(ToString::to_string) 67 | .delimited_by(just('"'), just('"')); 68 | 69 | let array = value 70 | .clone() 71 | .separated_by(just(',').padded()) 72 | .allow_trailing() 73 | .collect() 74 | .padded() 75 | .delimited_by(just('['), just(']')); 76 | 77 | let member = string.clone().then_ignore(just(':').padded()).then(value); 78 | let object = member 79 | .clone() 80 | .separated_by(just(',').padded()) 81 | .collect() 82 | .padded() 83 | .delimited_by(just('{'), just('}')); 84 | 85 | choice(( 86 | just("null").to(Json::Null), 87 | just("true").to(Json::Bool(true)), 88 | just("false").to(Json::Bool(false)), 89 | number.map(Json::Num), 90 | string.map(Json::Str), 91 | array.map(Json::Array), 92 | object.map(Json::Object), 93 | )) 94 | .padded() 95 | }) 96 | } 97 | -------------------------------------------------------------------------------- /examples/parol-app/grammar.rs: -------------------------------------------------------------------------------- 1 | use crate::grammar_trait::*; 2 | use parol_runtime::Result; 3 | use std::fmt::{Debug, Display, Error, Formatter}; 4 | 5 | impl Display for Json<'_> { 6 | fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), Error> { 7 | write!(f, "{}", self.value) 8 | } 9 | } 10 | 11 | impl Display for Value<'_> { 12 | fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), Error> { 13 | match self { 14 | Value::String(v) => write!(f, "{}", v.string.string.text()), 15 | Value::Number(v) => write!(f, "{}", v.number.number.text()), 16 | Value::Object(v) => write!(f, "{{{}}}", v.object.object_suffix), 17 | Value::Array(v) => write!(f, "[{}]", v.array.array_suffix), 18 | Value::True(_) => write!(f, "true"), 19 | Value::False(_) => write!(f, "false"), 20 | Value::Null(_) => write!(f, "null"), 21 | } 22 | } 23 | } 24 | 25 | impl Display for ObjectSuffix<'_> { 26 | fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), Error> { 27 | match self { 28 | ObjectSuffix::PairObjectListRBrace(o) => write!( 29 | f, 30 | "{}{}", 31 | o.pair, 32 | o.object_list 33 | .iter() 34 | .map(|e| format!("{}", e)) 35 | .collect::>() 36 | .join("") 37 | ), 38 | ObjectSuffix::RBrace(_) => Ok(()), 39 | } 40 | } 41 | } 42 | 43 | impl Display for ObjectList<'_> { 44 | fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), Error> { 45 | write!(f, ", {}", self.pair) 46 | } 47 | } 48 | 49 | impl Display for ArraySuffix<'_> { 50 | fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), Error> { 51 | match self { 52 | ArraySuffix::ValueArrayListRBracket(a) => write!( 53 | f, 54 | "{}{}", 55 | a.value, 56 | a.array_list 57 | .iter() 58 | .map(|e| format!("{}", e)) 59 | .collect::>() 60 | .join("") 61 | ), 62 | ArraySuffix::RBracket(_) => Ok(()), 63 | } 64 | } 65 | } 66 | 67 | impl Display for ArrayList<'_> { 68 | fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), Error> { 69 | write!(f, ", {}", self.value) 70 | } 71 | } 72 | 73 | impl Display for Pair<'_> { 74 | fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), Error> { 75 | write!(f, "{}: {}", self.string.string.text(), self.value) 76 | } 77 | } 78 | 79 | /// 80 | /// Data structure used to build up a json structure during parsing 81 | /// 82 | #[derive(Debug, Default)] 83 | pub struct Grammar<'t> { 84 | pub json: Option>, 85 | } 86 | 87 | impl Display for Grammar<'_> { 88 | fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), Error> { 89 | match &self.json { 90 | Some(json) => write!(f, "{}", json), 91 | None => write!(f, "No parse result"), 92 | } 93 | } 94 | } 95 | 96 | impl Grammar<'_> { 97 | pub fn new() -> Self { 98 | Grammar::default() 99 | } 100 | } 101 | 102 | impl<'t> GrammarTrait<'t> for Grammar<'t> { 103 | fn json(&mut self, arg: &Json<'t>) -> Result<()> { 104 | self.json = Some(arg.clone()); 105 | Ok(()) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /examples/nom-app/parser.rs: -------------------------------------------------------------------------------- 1 | use nom::{ 2 | branch::alt, 3 | bytes::complete::{escaped, tag, take_while}, 4 | character::complete::{alphanumeric1 as alphanumeric, char, one_of}, 5 | combinator::{cut, map, opt, value}, 6 | error::{context, ContextError, ParseError}, 7 | multi::separated_list0, 8 | number::complete::double, 9 | sequence::{delimited, preceded, separated_pair, terminated}, 10 | IResult, Parser, 11 | }; 12 | use std::collections::HashMap; 13 | use std::str; 14 | 15 | #[derive(Debug, PartialEq)] 16 | pub enum JsonValue { 17 | Null, 18 | Str(String), 19 | Boolean(bool), 20 | Num(f64), 21 | Array(Vec), 22 | Object(HashMap), 23 | } 24 | 25 | fn sp<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> { 26 | let chars = " \t\r\n"; 27 | 28 | take_while(move |c| chars.contains(c))(i) 29 | } 30 | 31 | fn parse_str<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> { 32 | escaped(alphanumeric, '\\', one_of("\"n\\"))(i) 33 | } 34 | 35 | fn boolean<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, bool, E> { 36 | let parse_true = value(true, tag("true")); 37 | 38 | let parse_false = value(false, tag("false")); 39 | 40 | alt((parse_true, parse_false)).parse(input) 41 | } 42 | 43 | fn null<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, (), E> { 44 | value((), tag("null")).parse(input) 45 | } 46 | 47 | fn string<'a, E: ParseError<&'a str> + ContextError<&'a str>>( 48 | i: &'a str, 49 | ) -> IResult<&'a str, &'a str, E> { 50 | context( 51 | "string", 52 | preceded(char('\"'), cut(terminated(parse_str, char('\"')))), 53 | ) 54 | .parse(i) 55 | } 56 | 57 | fn array<'a, E: ParseError<&'a str> + ContextError<&'a str>>( 58 | i: &'a str, 59 | ) -> IResult<&'a str, Vec, E> { 60 | context( 61 | "array", 62 | preceded( 63 | char('['), 64 | cut(terminated( 65 | separated_list0(preceded(sp, char(',')), json_value), 66 | preceded(sp, char(']')), 67 | )), 68 | ), 69 | ) 70 | .parse(i) 71 | } 72 | 73 | fn key_value<'a, E: ParseError<&'a str> + ContextError<&'a str>>( 74 | i: &'a str, 75 | ) -> IResult<&'a str, (&'a str, JsonValue), E> { 76 | separated_pair( 77 | preceded(sp, string), 78 | cut(preceded(sp, char(':'))), 79 | json_value, 80 | ) 81 | .parse(i) 82 | } 83 | 84 | fn hash<'a, E: ParseError<&'a str> + ContextError<&'a str>>( 85 | i: &'a str, 86 | ) -> IResult<&'a str, HashMap, E> { 87 | context( 88 | "map", 89 | preceded( 90 | char('{'), 91 | cut(terminated( 92 | map( 93 | separated_list0(preceded(sp, char(',')), key_value), 94 | |tuple_vec| { 95 | tuple_vec 96 | .into_iter() 97 | .map(|(k, v)| (String::from(k), v)) 98 | .collect() 99 | }, 100 | ), 101 | preceded(sp, char('}')), 102 | )), 103 | ), 104 | ) 105 | .parse(i) 106 | } 107 | 108 | fn json_value<'a, E: ParseError<&'a str> + ContextError<&'a str>>( 109 | i: &'a str, 110 | ) -> IResult<&'a str, JsonValue, E> { 111 | preceded( 112 | sp, 113 | alt(( 114 | map(hash, JsonValue::Object), 115 | map(array, JsonValue::Array), 116 | map(string, |s| JsonValue::Str(String::from(s))), 117 | map(double, JsonValue::Num), 118 | map(boolean, JsonValue::Boolean), 119 | map(null, |_| JsonValue::Null), 120 | )), 121 | ) 122 | .parse(i) 123 | } 124 | 125 | pub fn root<'a, E: ParseError<&'a str> + ContextError<&'a str>>( 126 | i: &'a str, 127 | ) -> IResult<&'a str, JsonValue, E> { 128 | delimited( 129 | sp, 130 | alt(( 131 | map(hash, JsonValue::Object), 132 | map(array, JsonValue::Array), 133 | map(null, |_| JsonValue::Null), 134 | )), 135 | opt(sp), 136 | ) 137 | .parse(i) 138 | } 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Parsing Benchmarks 2 | 3 | This repo tries to assess Rust parsing performance. 4 | 5 | | crate | parser type | action code | integration | input type | precedence | parameterized rules | streaming input | 6 | |------------|---------------|-------------|--------------------|-------------------------|------------------------|---------------------|-----------------| 7 | | [chumsky] | combinators | in source | library | `&str`, `&[u8]`, custom | [pratt][chumsky-pratt] | Yes | Yes | 8 | | [combine] | combinators | in source | library | `&str` | ? | ? | ? | 9 | | [grmtools] | CFG | in grammar | library | ? | ? | ? | ? | 10 | | [lalrpop] | LR(1) | in grammar | build script | `&str` | none | Yes | No | 11 | | [logos] | lexer | in source | proc macro | `&str`, `&[u8]` | ? | ? | ? | 12 | | [nom] | combinators | in source | library | `&str`, `&[u8]`, custom | [pratt][nom-pratt] | Yes | Yes | 13 | | [parol] | LL(k)/LALR(1) | in source | build script | `&str` | climbing | No | No | 14 | | [peg] | PEG | in grammar | proc macro (block) | `&str`, `&[T]`, custom | climbing | Yes | No | 15 | | [pest] | PEG | external | proc macro (file) | `&str` | climbing | No | No | 16 | | [winnow] | combinators | in source | library | `&str`, `&[T]`, custom | none | Yes | Yes | 17 | | [yap] | combinators | in source | library | `&str`, `&[T]`, custom | none | Yes | ? | 18 | 19 | Formerly, we compared: 20 | - [pom]: lack of notoriety 21 | - [lelwel]: example is too different than others 22 | 23 | # Results 24 | 25 | Name | Overhead (release) | Build (debug) | Parse (release) | Downloads | Version 26 | -----|--------------------|---------------|-----------------|-----------|-------- 27 | null | 0 KiB | 249ms | 3ms | - | - 28 | grmtools | 2,610 KiB | 11s | 164ms | ![Download count](https://img.shields.io/crates/dr/cfgrammar) | v0.13.10 29 | chumsky | 150 KiB | 4s | 32ms | ![Download count](https://img.shields.io/crates/dr/chumsky) | v0.10.1 30 | combine | 181 KiB | 4s | 53ms | ![Download count](https://img.shields.io/crates/dr/combine) | v3.8.1 31 | lalrpop | 1,523 KiB | 11s | 37ms | ![Download count](https://img.shields.io/crates/dr/lalrpop) | v0.22.2 32 | logos | 90 KiB | 4s | 21ms | ![Download count](https://img.shields.io/crates/dr/logos) | v0.15.0 33 | nom | 98 KiB | 3s | 65ms | ![Download count](https://img.shields.io/crates/dr/nom) | v8.0.0 34 | parol | 492 KiB | 9s | 174ms | ![Download count](https://img.shields.io/crates/dr/parol) | v4.1.0 35 | peg | 80 KiB | 2s | 23ms | ![Download count](https://img.shields.io/crates/dr/peg) | v0.8.5 36 | pest | 130 KiB | 5s | 61ms | ![Download count](https://img.shields.io/crates/dr/pest) | v2.8.1 37 | serde_json | 59 KiB | 3s | 14ms | ![Download count](https://img.shields.io/crates/dr/serde_json) | v1.0.142 38 | winnow | 75 KiB | 2s | 28ms | ![Download count](https://img.shields.io/crates/dr/winnow) | v0.7.12 39 | yap | 61 KiB | 527ms | 33ms | ![Download count](https://img.shields.io/crates/dr/yap) | v0.12.0 40 | 41 | *System: Linux 6.8.0-62-generic (x86_64), rustc 1.89.0 (29483883e 2025-08-04) w/ `-j 8`* 42 | 43 | Note: 44 | - For more "Parse (release)" comparisons, see [parser_benchmarks](https://github.com/rust-bakery/parser_benchmarks) 45 | - Parsers have not been validated and might have differing levels of quality ([#5](https://github.com/epage/parse-benchmarks-rs/issues/5)) 46 | 47 | # Running the Benchmarks 48 | 49 | ```bash 50 | $ ./bench.py 51 | $ ./format.py 52 | ``` 53 | 54 | [chumsky]: https://github.com/zesterer/chumsky 55 | [chumsky-pratt]: https://docs.rs/chumsky/latest/chumsky/pratt/index.html 56 | [combine]: https://github.com/Marwes/combine 57 | [lalrpop]: https://github.com/lalrpop/lalrpop 58 | [lelwel]: https://github.com/0x2a-42/lelwel 59 | [logos]: https://github.com/maciejhirsz/logos 60 | [nom]: https://github.com/geal/nom 61 | [nom-pratt]: https://docs.rs/nom-language/latest/nom_language/precedence/fn.precedence.html 62 | [parol]: https://github.com/jsinger67/parol 63 | [peg]: https://github.com/kevinmehall/rust-peg 64 | [pest]: https://github.com/pest-parser/pest 65 | [pom]: https://github.com/j-f-liu/pom 66 | [winnow]: https://github.com/winnow-rs/winnow 67 | [yap]: https://github.com/jsdw/yap 68 | [yap]: https://github.com/jsdw/yap 69 | [grmtools]: https://crates.io/crates/cfgrammar 70 | -------------------------------------------------------------------------------- /examples/combine-app/parser.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use combine::error::ParseError; 4 | use combine::{Parser, RangeStream, StreamOnce}; 5 | 6 | use combine::parser::byte::{byte, spaces}; 7 | use combine::parser::choice::{choice, optional}; 8 | use combine::parser::combinator::no_partial; 9 | use combine::parser::item::{one_of, satisfy_map}; 10 | use combine::parser::range; 11 | use combine::parser::repeat::{escaped, sep_by}; 12 | use combine::parser::sequence::between; 13 | 14 | #[derive(PartialEq, Debug)] 15 | pub enum Value { 16 | Number(f64), 17 | String(String), 18 | Bool(bool), 19 | Null, 20 | Object(HashMap), 21 | Array(Vec), 22 | } 23 | 24 | #[inline(always)] 25 | pub fn json_value<'a, I>() -> impl Parser + 'a 26 | where 27 | I: RangeStream + 'a, 28 | I::Error: ParseError, 29 | { 30 | spaces().with(json_value_()) 31 | } 32 | 33 | // We need to use `parser!` to break the recursive use of `value` to prevent the returned parser 34 | // from containing itself 35 | parser! { 36 | #[inline(always)] 37 | fn json_value_['a, I]()(I) -> Value 38 | where [ I: RangeStream + 'a ] 39 | { 40 | choice(( 41 | json_string().map(Value::String), 42 | object().map(Value::Object), 43 | array().map(Value::Array), 44 | number().map(Value::Number), 45 | lex(range::range(&b"false"[..]).map(|_| Value::Bool(false))), 46 | lex(range::range(&b"true"[..]).map(|_| Value::Bool(true))), 47 | lex(range::range(&b"null"[..]).map(|_| Value::Null)), 48 | )) 49 | } 50 | } 51 | 52 | fn object<'a, I>() -> impl Parser> + 'a 53 | where 54 | I: RangeStream + 'a, 55 | I::Error: ParseError, 56 | { 57 | let field = (json_string(), lex(byte(b':')), json_value_()).map(|t| (t.0, t.2)); 58 | let fields = sep_by(field, lex(byte(b','))); 59 | between(lex(byte(b'{')), lex(byte(b'}')), fields).expected("object") 60 | } 61 | 62 | fn array<'a, I>() -> impl Parser> + 'a 63 | where 64 | I: RangeStream + 'a, 65 | I::Error: ParseError, 66 | { 67 | between( 68 | lex(byte(b'[')), 69 | lex(byte(b']')), 70 | sep_by(json_value_(), lex(byte(b','))), 71 | ) 72 | .expected("array") 73 | } 74 | 75 | fn json_string<'a, I>() -> impl Parser + 'a 76 | where 77 | I: RangeStream + 'a, 78 | I::Error: ParseError, 79 | { 80 | let back_slash_byte = satisfy_map(|c| { 81 | Some(match c { 82 | b'"' => b'"', 83 | b'\\' => b'\\', 84 | b'/' => b'/', 85 | b'b' => '\u{0008}' as u8, 86 | b'f' => '\u{000c}' as u8, 87 | b'n' => b'\n', 88 | b'r' => b'\r', 89 | b't' => b'\t', 90 | _ => return None, 91 | }) 92 | }); 93 | let inner = range::recognize(escaped( 94 | range::take_while1(|b| b != b'\\' && b != b'"'), 95 | b'\\', 96 | back_slash_byte, 97 | )) 98 | .map(|s| std::str::from_utf8(s).unwrap().to_owned()); 99 | between(byte(b'"'), lex(byte(b'"')), inner).expected("string") 100 | } 101 | 102 | fn number<'a, I>() -> impl Parser + 'a 103 | where 104 | I: RangeStream + 'a, 105 | I::Error: ParseError, 106 | { 107 | no_partial( 108 | lex(range::recognize(no_partial(( 109 | optional(one_of("+-".bytes())), 110 | byte(b'0').or((digits(), optional((byte(b'.'), digits()))).map(|_| b'0')), 111 | optional(( 112 | (one_of("eE".bytes()), optional(one_of("+-".bytes()))), 113 | digits(), 114 | )), 115 | )))) 116 | .map(|s: &'a [u8]| std::str::from_utf8(s).unwrap().parse().unwrap()) 117 | .expected("number"), 118 | ) 119 | } 120 | 121 | fn digits<'a, I>() -> impl Parser + 'a 122 | where 123 | I: RangeStream + 'a, 124 | I::Error: ParseError, 125 | { 126 | range::take_while1(|b| b >= b'0' && b <= b'9') 127 | } 128 | 129 | fn lex<'a, P>(p: P) -> impl Parser 130 | where 131 | P: Parser, 132 | P::Input: RangeStream, 133 | ::Error: ParseError< 134 | ::Item, 135 | ::Range, 136 | ::Position, 137 | >, 138 | { 139 | no_partial(p.skip(range::take_while(|b| { 140 | b == b' ' || b == b'\t' || b == b'\r' || b == b'\n' 141 | }))) 142 | } 143 | -------------------------------------------------------------------------------- /bench.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import copy 4 | import datetime 5 | import json 6 | import multiprocessing 7 | import pathlib 8 | import platform 9 | import subprocess 10 | import sys 11 | import tempfile 12 | 13 | 14 | def main(): 15 | repo_root = pathlib.Path(__name__).parent 16 | 17 | timestamp = datetime.datetime.now().strftime("%Y-%m-%d") 18 | hostname = platform.node() 19 | uname = platform.uname() 20 | cpus = multiprocessing.cpu_count() 21 | rustc = subprocess.run(["rustc", "--version"], check=True, capture_output=True, encoding="utf-8").stdout.strip() 22 | 23 | extension = ".exe" if sys.platform in ("win32", "cygwin") else "" 24 | 25 | runs_root = repo_root / "runs" 26 | runs_root.mkdir(parents=True, exist_ok=True) 27 | raw_run_path = runs_root / "{}-{}.json".format(timestamp, hostname) 28 | if raw_run_path.exists(): 29 | old_raw_run = json.loads(raw_run_path.read_text()) 30 | else: 31 | old_raw_run = {} 32 | 33 | raw_run = { 34 | "timestamp": timestamp, 35 | "hostname": hostname, 36 | "os": uname.system, 37 | "os_ver": uname.release, 38 | "arch": uname.machine, 39 | "cpus": cpus, 40 | "rustc": rustc, 41 | "libs": {}, 42 | } 43 | 44 | with tempfile.TemporaryDirectory() as tmpdir: 45 | for example_path in sorted((repo_root / "examples").glob("*-app")): 46 | manifest_path = example_path / "Cargo.toml" 47 | name = example_path.name.rsplit("-", 1)[0] 48 | metadata = harvest_metadata(manifest_path, name) 49 | 50 | build_report_path = pathlib.Path(tmpdir) / f"{example_path.name}-build.json" 51 | if True: 52 | hyperfine_cmd = [ 53 | "hyperfine", 54 | "--warmup=1", 55 | "--min-runs=5", 56 | f"--export-json={build_report_path}", 57 | "--prepare=cargo clean", 58 | # Doing debug builds because that is more likely the 59 | # time directly impacting people 60 | f"cargo build -j {cpus} --package {example_path.name}" 61 | ] 62 | if False: 63 | hyperfine_cmd.append("--show-output") 64 | subprocess.run( 65 | hyperfine_cmd, 66 | cwd=repo_root, 67 | check=True, 68 | ) 69 | build_report = json.loads(build_report_path.read_text()) 70 | else: 71 | build_report = old_raw_run.get("libs", {}).get(str(manifest_path), {}).get("build", None) 72 | 73 | if True: 74 | # Doing release builds because that is where size probably matters most 75 | subprocess.run(["cargo", "build", "--release", "--package", example_path.name], cwd=repo_root, check=True) 76 | app_path = repo_root / f"target/release/{example_path.name}{extension}" 77 | file_size = app_path.stat().st_size 78 | else: 79 | app_path = None 80 | file_size = old_raw_run.get("libs", {}).get(str(manifest_path), {}).get("size", None) 81 | 82 | run_report_path = pathlib.Path(tmpdir) / f"{example_path.name}-run.json" 83 | if True and app_path is not None: 84 | json_path = pathlib.Path(__file__).parent / "third_party/nativejson-benchmark/data/canada.json" 85 | assert json_path.exists() 86 | hyperfine_cmd = [ 87 | "hyperfine", 88 | "--warmup=1", 89 | "--min-runs=5", 90 | f"--export-json={run_report_path}", 91 | f"{app_path} {json_path}" 92 | ] 93 | if False: 94 | hyperfine_cmd.append("--show-output") 95 | subprocess.run( 96 | hyperfine_cmd, 97 | cwd=repo_root, 98 | check=True, 99 | ) 100 | run_report = json.loads(run_report_path.read_text()) 101 | else: 102 | run_report = old_raw_run.get("libs", {}).get(str(manifest_path), {}).get("run", None) 103 | 104 | raw_run["libs"][str(manifest_path)] = { 105 | "name": example_path.name.rsplit("-", 1)[0], 106 | "manifest_path": str(manifest_path), 107 | "crate": metadata["name"], 108 | "version": metadata["version"], 109 | "build": build_report, 110 | "run": run_report, 111 | "size": file_size, 112 | } 113 | 114 | raw_run_path.write_text(json.dumps(raw_run, indent=2)) 115 | print(raw_run_path) 116 | 117 | 118 | def harvest_metadata(manifest_path, name): 119 | p = subprocess.run(["cargo", "tree"], check=True, cwd=manifest_path.parent, capture_output=True, encoding="utf-8") 120 | lines = p.stdout.strip().splitlines() 121 | app_line = lines.pop(0) 122 | if lines: 123 | self_line = lines[0] 124 | first_name, _ = _extract_line(self_line) 125 | unique = dict( 126 | _extract_line(line) 127 | for line in lines 128 | if "(*)" not in line and "[build-dependencies]" not in line and "[dev-dependencies" not in line 129 | ) 130 | version = unique.get(name) 131 | if version is None: 132 | name = first_name 133 | version = unique.get(first_name) 134 | else: 135 | name = None 136 | version = None 137 | 138 | return { 139 | "name": name, 140 | "version": version, 141 | } 142 | 143 | 144 | def _extract_line(line): 145 | if line.endswith(" (proc-macro)"): 146 | line = line[0:-len(" (proc-macro)")] 147 | _, name, version = line.rsplit(" ", 2) 148 | return name, version 149 | 150 | 151 | 152 | if __name__ == "__main__": 153 | main() 154 | -------------------------------------------------------------------------------- /examples/logos-app/parser.rs: -------------------------------------------------------------------------------- 1 | //! JSON parser written in Rust, using Logos. 2 | //! 3 | //! If the file is a valid JSON value, it will be printed 4 | //! to the terminal using the debug format. 5 | //! 6 | //! Otherwise, an error will be printed with its location. 7 | //! 8 | //! Usage: 9 | //! cargo run --example json 10 | //! 11 | //! Example: 12 | //! cargo run --example json examples/example.json 13 | 14 | /* ANCHOR: all */ 15 | use logos::{Lexer, Logos, Span}; 16 | 17 | use std::collections::HashMap; 18 | 19 | type Error = (String, Span); 20 | 21 | type Result = std::result::Result; 22 | 23 | /* ANCHOR: tokens */ 24 | /// All meaningful JSON tokens. 25 | /// 26 | /// > NOTE: regexes for [`Token::Number`] and [`Token::String`] may not 27 | /// > catch all possible values, especially for strings. If you find 28 | /// > errors, please report them so that we can improve the regex. 29 | #[derive(Debug, Logos)] 30 | #[logos(skip r"[ \t\r\n\f]+")] 31 | pub enum Token { 32 | #[token("false", |_| false)] 33 | #[token("true", |_| true)] 34 | Bool(bool), 35 | 36 | #[token("{")] 37 | BraceOpen, 38 | 39 | #[token("}")] 40 | BraceClose, 41 | 42 | #[token("[")] 43 | BracketOpen, 44 | 45 | #[token("]")] 46 | BracketClose, 47 | 48 | #[token(":")] 49 | Colon, 50 | 51 | #[token(",")] 52 | Comma, 53 | 54 | #[token("null")] 55 | Null, 56 | 57 | #[regex(r"-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?", |lex| lex.slice().parse::().unwrap())] 58 | Number(f64), 59 | 60 | #[regex(r#""([^"\\]|\\["\\bnfrt]|u[a-fA-F0-9]{4})*""#, |lex| lex.slice().to_owned())] 61 | String(String), 62 | } 63 | /* ANCHOR_END: tokens */ 64 | 65 | /* ANCHOR: values */ 66 | /// Represent any valid JSON value. 67 | #[derive(Debug)] 68 | pub enum Value { 69 | /// null. 70 | Null, 71 | /// true or false. 72 | Bool(bool), 73 | /// Any floating point number. 74 | Number(f64), 75 | /// Any quoted string. 76 | String(String), 77 | /// An array of values 78 | Array(Vec), 79 | /// An dictionary mapping keys and values. 80 | Object(HashMap), 81 | } 82 | /* ANCHOR_END: values */ 83 | 84 | /* ANCHOR: value */ 85 | /// Parse a token stream into a JSON value. 86 | pub fn parse_value(lexer: &mut Lexer<'_, Token>) -> Result { 87 | if let Some(token) = lexer.next() { 88 | match token { 89 | Ok(Token::Bool(b)) => Ok(Value::Bool(b)), 90 | Ok(Token::BraceOpen) => parse_object(lexer), 91 | Ok(Token::BracketOpen) => parse_array(lexer), 92 | Ok(Token::Null) => Ok(Value::Null), 93 | Ok(Token::Number(n)) => Ok(Value::Number(n)), 94 | Ok(Token::String(s)) => Ok(Value::String(s)), 95 | _ => Err(( 96 | "unexpected token here (context: value)".to_owned(), 97 | lexer.span(), 98 | )), 99 | } 100 | } else { 101 | Err(("empty values are not allowed".to_owned(), lexer.span())) 102 | } 103 | } 104 | /* ANCHOR_END: value */ 105 | 106 | /* ANCHOR: array */ 107 | /// Parse a token stream into an array and return when 108 | /// a valid terminator is found. 109 | /// 110 | /// > NOTE: we assume '[' was consumed. 111 | fn parse_array(lexer: &mut Lexer<'_, Token>) -> Result { 112 | let mut array = Vec::new(); 113 | let span = lexer.span(); 114 | let mut awaits_comma = false; 115 | let mut awaits_value = false; 116 | 117 | while let Some(token) = lexer.next() { 118 | match token { 119 | Ok(Token::Bool(b)) if !awaits_comma => { 120 | array.push(Value::Bool(b)); 121 | awaits_value = false; 122 | } 123 | Ok(Token::BraceOpen) if !awaits_comma => { 124 | let object = parse_object(lexer)?; 125 | array.push(object); 126 | awaits_value = false; 127 | } 128 | Ok(Token::BracketOpen) if !awaits_comma => { 129 | let sub_array = parse_array(lexer)?; 130 | array.push(sub_array); 131 | awaits_value = false; 132 | } 133 | Ok(Token::BracketClose) if !awaits_value => return Ok(Value::Array(array)), 134 | Ok(Token::Comma) if awaits_comma => awaits_value = true, 135 | Ok(Token::Null) if !awaits_comma => { 136 | array.push(Value::Null); 137 | awaits_value = false 138 | } 139 | Ok(Token::Number(n)) if !awaits_comma => { 140 | array.push(Value::Number(n)); 141 | awaits_value = false; 142 | } 143 | Ok(Token::String(s)) if !awaits_comma => { 144 | array.push(Value::String(s)); 145 | awaits_value = false; 146 | } 147 | _ => { 148 | return Err(( 149 | "unexpected token here (context: array)".to_owned(), 150 | lexer.span(), 151 | )) 152 | } 153 | } 154 | awaits_comma = !awaits_value; 155 | } 156 | Err(("unmatched opening bracket defined here".to_owned(), span)) 157 | } 158 | /* ANCHOR_END: array */ 159 | 160 | /* ANCHOR: object */ 161 | /// Parse a token stream into an object and return when 162 | /// a valid terminator is found. 163 | /// 164 | /// > NOTE: we assume '{' was consumed. 165 | fn parse_object(lexer: &mut Lexer<'_, Token>) -> Result { 166 | let mut map = HashMap::new(); 167 | let span = lexer.span(); 168 | let mut awaits_comma = false; 169 | let mut awaits_key = false; 170 | 171 | while let Some(token) = lexer.next() { 172 | match token { 173 | Ok(Token::BraceClose) if !awaits_key => return Ok(Value::Object(map)), 174 | Ok(Token::Comma) if awaits_comma => awaits_key = true, 175 | Ok(Token::String(key)) if !awaits_comma => { 176 | match lexer.next() { 177 | Some(Ok(Token::Colon)) => (), 178 | _ => { 179 | return Err(( 180 | "unexpected token here, expecting ':'".to_owned(), 181 | lexer.span(), 182 | )) 183 | } 184 | } 185 | let value = parse_value(lexer)?; 186 | map.insert(key, value); 187 | awaits_key = false; 188 | } 189 | _ => { 190 | return Err(( 191 | "unexpected token here (context: object)".to_owned(), 192 | lexer.span(), 193 | )) 194 | } 195 | } 196 | awaits_comma = !awaits_key; 197 | } 198 | Err(("unmatched opening brace defined here".to_owned(), span)) 199 | } 200 | /* ANCHOR_END: object */ 201 | -------------------------------------------------------------------------------- /examples/parol-app/parser.rs: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------- 2 | // This file was generated by parol. 3 | // It is not intended for manual editing and changes will be 4 | // lost after next build. 5 | // --------------------------------------------------------- 6 | 7 | use parol_runtime::{ 8 | ParolError, ParseTree, TokenStream, 9 | parser::{ 10 | LLKParser, LookaheadDFA, ParseType, Production, Trans, parse_tree_type::TreeConstruct, 11 | }, 12 | }; 13 | use scnr2::scanner; 14 | use std::path::Path; 15 | 16 | use crate::grammar::Grammar; 17 | use crate::grammar_trait::GrammarAuto; 18 | 19 | pub const TERMINAL_NAMES: &[&str; 17] = &[ 20 | /* 0 */ "EndOfInput", 21 | /* 1 */ "Newline", 22 | /* 2 */ "Whitespace", 23 | /* 3 */ "LineComment", 24 | /* 4 */ "BlockComment", 25 | /* 5 */ "LBrace", 26 | /* 6 */ "RBrace", 27 | /* 7 */ "Comma", 28 | /* 8 */ "Colon", 29 | /* 9 */ "LBracket", 30 | /* 10 */ "RBracket", 31 | /* 11 */ "True", 32 | /* 12 */ "False", 33 | /* 13 */ "Null", 34 | /* 14 */ "String", 35 | /* 15 */ "Number", 36 | /* 16 */ "Error", 37 | ]; 38 | 39 | scanner! { 40 | GrammarScanner { 41 | mode INITIAL { 42 | token r"\r\n|\r|\n" => 1; // "Newline" 43 | token r"[\s--\r\n]+" => 2; // "Whitespace" 44 | token r"\{" => 5; // "LBrace" 45 | token r"\}" => 6; // "RBrace" 46 | token r"," => 7; // "Comma" 47 | token r":" => 8; // "Colon" 48 | token r"\[" => 9; // "LBracket" 49 | token r"\]" => 10; // "RBracket" 50 | token r"true" => 11; // "True" 51 | token r"false" => 12; // "False" 52 | token r"null" => 13; // "Null" 53 | token r#""(\\.|[^"])*""# => 14; // "String" 54 | token r"-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][-+]?(0|[1-9][0-9]*)?)?" => 15; // "Number" 55 | token r"." => 16; // "Error" 56 | } 57 | } 58 | } 59 | 60 | const MAX_K: usize = 1; 61 | 62 | pub const NON_TERMINALS: &[&str; 11] = &[ 63 | /* 0 */ "Array", 64 | /* 1 */ "ArrayList", 65 | /* 2 */ "ArraySuffix", 66 | /* 3 */ "Json", 67 | /* 4 */ "Number", 68 | /* 5 */ "Object", 69 | /* 6 */ "ObjectList", 70 | /* 7 */ "ObjectSuffix", 71 | /* 8 */ "Pair", 72 | /* 9 */ "String", 73 | /* 10 */ "Value", 74 | ]; 75 | 76 | pub const LOOKAHEAD_AUTOMATA: &[LookaheadDFA; 11] = &[ 77 | /* 0 - "Array" */ 78 | LookaheadDFA { 79 | prod0: 7, 80 | transitions: &[], 81 | k: 0, 82 | }, 83 | /* 1 - "ArrayList" */ 84 | LookaheadDFA { 85 | prod0: -1, 86 | transitions: &[Trans(0, 7, 1, 10), Trans(0, 10, 2, 11)], 87 | k: 1, 88 | }, 89 | /* 2 - "ArraySuffix" */ 90 | LookaheadDFA { 91 | prod0: -1, 92 | transitions: &[ 93 | Trans(0, 5, 1, 8), 94 | Trans(0, 9, 1, 8), 95 | Trans(0, 10, 2, 9), 96 | Trans(0, 11, 1, 8), 97 | Trans(0, 12, 1, 8), 98 | Trans(0, 13, 1, 8), 99 | Trans(0, 14, 1, 8), 100 | Trans(0, 15, 1, 8), 101 | ], 102 | k: 1, 103 | }, 104 | /* 3 - "Json" */ 105 | LookaheadDFA { 106 | prod0: 0, 107 | transitions: &[], 108 | k: 0, 109 | }, 110 | /* 4 - "Number" */ 111 | LookaheadDFA { 112 | prod0: 20, 113 | transitions: &[], 114 | k: 0, 115 | }, 116 | /* 5 - "Object" */ 117 | LookaheadDFA { 118 | prod0: 1, 119 | transitions: &[], 120 | k: 0, 121 | }, 122 | /* 6 - "ObjectList" */ 123 | LookaheadDFA { 124 | prod0: -1, 125 | transitions: &[Trans(0, 6, 2, 5), Trans(0, 7, 1, 4)], 126 | k: 1, 127 | }, 128 | /* 7 - "ObjectSuffix" */ 129 | LookaheadDFA { 130 | prod0: -1, 131 | transitions: &[Trans(0, 6, 2, 3), Trans(0, 14, 1, 2)], 132 | k: 1, 133 | }, 134 | /* 8 - "Pair" */ 135 | LookaheadDFA { 136 | prod0: 6, 137 | transitions: &[], 138 | k: 0, 139 | }, 140 | /* 9 - "String" */ 141 | LookaheadDFA { 142 | prod0: 19, 143 | transitions: &[], 144 | k: 0, 145 | }, 146 | /* 10 - "Value" */ 147 | LookaheadDFA { 148 | prod0: -1, 149 | transitions: &[ 150 | Trans(0, 5, 3, 14), 151 | Trans(0, 9, 4, 15), 152 | Trans(0, 11, 5, 16), 153 | Trans(0, 12, 6, 17), 154 | Trans(0, 13, 7, 18), 155 | Trans(0, 14, 1, 12), 156 | Trans(0, 15, 2, 13), 157 | ], 158 | k: 1, 159 | }, 160 | ]; 161 | 162 | pub const PRODUCTIONS: &[Production; 21] = &[ 163 | // 0 - Json: Value; 164 | Production { 165 | lhs: 3, 166 | production: &[ParseType::N(10)], 167 | }, 168 | // 1 - Object: '{'^ /* Clipped */ ObjectSuffix; 169 | Production { 170 | lhs: 5, 171 | production: &[ParseType::N(7), ParseType::T(5)], 172 | }, 173 | // 2 - ObjectSuffix: Pair ObjectList /* Vec */ '}'^ /* Clipped */; 174 | Production { 175 | lhs: 7, 176 | production: &[ParseType::T(6), ParseType::N(6), ParseType::N(8)], 177 | }, 178 | // 3 - ObjectSuffix: '}'^ /* Clipped */; 179 | Production { 180 | lhs: 7, 181 | production: &[ParseType::T(6)], 182 | }, 183 | // 4 - ObjectList: ','^ /* Clipped */ Pair ObjectList; 184 | Production { 185 | lhs: 6, 186 | production: &[ParseType::N(6), ParseType::N(8), ParseType::T(7)], 187 | }, 188 | // 5 - ObjectList: ; 189 | Production { 190 | lhs: 6, 191 | production: &[], 192 | }, 193 | // 6 - Pair: String ':'^ /* Clipped */ Value; 194 | Production { 195 | lhs: 8, 196 | production: &[ParseType::N(10), ParseType::T(8), ParseType::N(9)], 197 | }, 198 | // 7 - Array: '['^ /* Clipped */ ArraySuffix; 199 | Production { 200 | lhs: 0, 201 | production: &[ParseType::N(2), ParseType::T(9)], 202 | }, 203 | // 8 - ArraySuffix: Value ArrayList /* Vec */ ']'^ /* Clipped */; 204 | Production { 205 | lhs: 2, 206 | production: &[ParseType::T(10), ParseType::N(1), ParseType::N(10)], 207 | }, 208 | // 9 - ArraySuffix: ']'^ /* Clipped */; 209 | Production { 210 | lhs: 2, 211 | production: &[ParseType::T(10)], 212 | }, 213 | // 10 - ArrayList: ','^ /* Clipped */ Value ArrayList; 214 | Production { 215 | lhs: 1, 216 | production: &[ParseType::N(1), ParseType::N(10), ParseType::T(7)], 217 | }, 218 | // 11 - ArrayList: ; 219 | Production { 220 | lhs: 1, 221 | production: &[], 222 | }, 223 | // 12 - Value: String; 224 | Production { 225 | lhs: 10, 226 | production: &[ParseType::N(9)], 227 | }, 228 | // 13 - Value: Number; 229 | Production { 230 | lhs: 10, 231 | production: &[ParseType::N(4)], 232 | }, 233 | // 14 - Value: Object; 234 | Production { 235 | lhs: 10, 236 | production: &[ParseType::N(5)], 237 | }, 238 | // 15 - Value: Array; 239 | Production { 240 | lhs: 10, 241 | production: &[ParseType::N(0)], 242 | }, 243 | // 16 - Value: 'true'^ /* Clipped */; 244 | Production { 245 | lhs: 10, 246 | production: &[ParseType::T(11)], 247 | }, 248 | // 17 - Value: 'false'^ /* Clipped */; 249 | Production { 250 | lhs: 10, 251 | production: &[ParseType::T(12)], 252 | }, 253 | // 18 - Value: 'null'^ /* Clipped */; 254 | Production { 255 | lhs: 10, 256 | production: &[ParseType::T(13)], 257 | }, 258 | // 19 - String: /"(\\.|[^"])*"/; 259 | Production { 260 | lhs: 9, 261 | production: &[ParseType::T(14)], 262 | }, 263 | // 20 - Number: /-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][-+]?(0|[1-9][0-9]*)?)?/; 264 | Production { 265 | lhs: 4, 266 | production: &[ParseType::T(15)], 267 | }, 268 | ]; 269 | 270 | pub fn parse<'t, T>( 271 | input: &'t str, 272 | file_name: T, 273 | user_actions: &mut Grammar<'t>, 274 | ) -> Result 275 | where 276 | T: AsRef, 277 | { 278 | use parol_runtime::{ 279 | parser::{parse_tree_type::SynTree, parser_types::SynTreeFlavor}, 280 | syntree::Builder, 281 | }; 282 | let mut builder = Builder::::new_with(); 283 | parse_into(input, &mut builder, file_name, user_actions)?; 284 | Ok(builder.build()?) 285 | } 286 | #[allow(dead_code)] 287 | pub fn parse_into<'t, T: TreeConstruct<'t>>( 288 | input: &'t str, 289 | tree_builder: &mut T, 290 | file_name: impl AsRef, 291 | user_actions: &mut Grammar<'t>, 292 | ) -> Result<(), ParolError> 293 | where 294 | ParolError: From, 295 | { 296 | use grammar_scanner::GrammarScanner; 297 | let mut llk_parser = LLKParser::new( 298 | 3, 299 | LOOKAHEAD_AUTOMATA, 300 | PRODUCTIONS, 301 | TERMINAL_NAMES, 302 | NON_TERMINALS, 303 | ); 304 | llk_parser.trim_parse_tree(); 305 | let scanner = GrammarScanner::new(); 306 | // Initialize wrapper 307 | let mut user_actions = GrammarAuto::new(user_actions); 308 | llk_parser.parse_into( 309 | tree_builder, 310 | TokenStream::new( 311 | input, 312 | file_name, 313 | scanner.scanner_impl.clone(), 314 | &GrammarScanner::match_function, 315 | MAX_K, 316 | ) 317 | .unwrap(), 318 | &mut user_actions, 319 | ) 320 | } 321 | -------------------------------------------------------------------------------- /examples/yap-app/parser.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use yap::{IntoTokens, TokenLocation, Tokens}; 3 | 4 | /// Parse JSON from a string. Just a very thin wrapper around `value()`. 5 | pub fn parse(s: &str) -> Result { 6 | value(&mut s.into_tokens()) 7 | } 8 | 9 | /// This is what we'll parse our JSON into. 10 | #[derive(Clone, PartialEq, Debug)] 11 | pub enum Value { 12 | Null, 13 | Number(f64), 14 | String(String), 15 | Bool(bool), 16 | Array(Vec), 17 | Object(HashMap), 18 | } 19 | 20 | /// Some errors that can be emitted if things go wrong. 21 | /// In this example, each error has a start and end location 22 | /// denoting where the issue is in the string. 23 | #[derive(PartialEq, Debug)] 24 | pub struct Error { 25 | // Start and end location of the error 26 | location: (usize, usize), 27 | // What was the nature of the error? 28 | kind: ErrorKind, 29 | } 30 | 31 | #[derive(PartialEq, Debug)] 32 | enum ErrorKind { 33 | // No ']' seen while parsing array. 34 | ArrayNotClosed, 35 | // No '}' seen while parsing object. 36 | ObjectNotClosed, 37 | // Object field isn't a valid string. 38 | InvalidObjectField, 39 | // No ':' seen between object field and valud. 40 | MissingObjectFieldSeparator, 41 | // String escape char (ie char after \) isn't valid. 42 | InvalidEscapeChar(char), 43 | // the file ended while we were still parsing. 44 | UnexpectedEof, 45 | // We didn't successfully parse any valid JSON at all. 46 | InvalidJson, 47 | } 48 | 49 | impl ErrorKind { 50 | fn at(self, start: T, end: T) -> Error { 51 | Error { 52 | location: (start.offset(), end.offset()), 53 | kind: self, 54 | } 55 | } 56 | } 57 | 58 | /// This is the `yap` entry point, and is responsible for parsing JSON values. 59 | /// 60 | /// Try parsing each of the different types of value we know about, 61 | /// and return the first error that we encounter, or a valid `Value`. 62 | fn value(toks: &mut impl Tokens) -> Result { 63 | // Return the first thing we parse successfully from our token stream, 64 | // mapping values into their `Value` container. 65 | let value = yap::one_of!(ts from toks; 66 | array(ts).map(|res| res.map(Value::Array)), 67 | string(ts).map(|res| res.map(Value::String)), 68 | object(ts).map(|res| res.map(Value::Object)), 69 | number(ts).map(|v| Ok(Value::Number(v))), 70 | bool(ts).map(|v| Ok(Value::Bool(v))), 71 | null(ts).then_some(Ok(Value::Null)) 72 | ); 73 | 74 | // No value? This means that the input doesn't begin with any valid JSON 75 | // character. 76 | match value { 77 | Some(r) => r, 78 | None => Err(ErrorKind::InvalidJson.at(toks.location(), toks.location())), 79 | } 80 | } 81 | 82 | /// Arrays start and end with [ and ], and contain JSON values, which we can 83 | /// use our top level value parser to handle. 84 | /// 85 | /// - `Some(Ok(values))` means we successfully parsed 0 or more array values. 86 | /// - `Some(Err(e))` means that we hit an error parsing the array. 87 | /// - `None` means that this wasn't an array and so nothing was parsed. 88 | fn array(toks: &mut impl Tokens) -> Option, Error>> { 89 | // Note the location of the start of the array. 90 | let start = toks.location(); 91 | 92 | // Try to consume a '['. If we can't, we consume nothing and bail. 93 | if !toks.token('[') { 94 | return None; 95 | } 96 | skip_whitespace(&mut *toks); 97 | 98 | // Use our `value()` parser to parse each array value, separated by ','. 99 | let values: Vec = toks 100 | .sep_by(|t| value(t).ok(), |t| field_separator(t)) 101 | .collect(); 102 | 103 | skip_whitespace(&mut *toks); 104 | if !toks.token(']') { 105 | // Record the start and end location of the array in our error. 106 | return Some(Err(ErrorKind::ArrayNotClosed.at(start, toks.location()))); 107 | } 108 | 109 | Some(Ok(values)) 110 | } 111 | 112 | /// Objects begin with {, and then have 0 or more "field":value pairs (for which we just 113 | /// lean on our string and value parsers to handle), and then should close with a }. 114 | /// 115 | /// - `Some(Ok(values))` means we successfully parsed 0 or more object values. 116 | /// - `Some(Err(e))` means that we hit an error parsing the object. 117 | /// - `None` means that this wasn't an object and so nothing was parsed. 118 | fn object(toks: &mut impl Tokens) -> Option, Error>> { 119 | // Note the location of the start of the object. 120 | let start = toks.location(); 121 | 122 | // Try to consume a '{'. If we can't, we consume nothing and bail. 123 | if !toks.token('{') { 124 | return None; 125 | } 126 | skip_whitespace(&mut *toks); 127 | 128 | // Expect object fields like `name: value` to be separated like arrays are. 129 | let values: Result, Error> = toks 130 | .sep_by(|t| object_field(t), |t| field_separator(t)) 131 | .collect(); 132 | 133 | // If we hit any errors above, return it. 134 | let Ok(values) = values else { 135 | return Some(values); 136 | }; 137 | 138 | skip_whitespace(&mut *toks); 139 | if !toks.token('}') { 140 | // Record the start and end location of the object in our error. 141 | return Some(Err(ErrorKind::ObjectNotClosed.at(start, toks.location()))); 142 | } 143 | 144 | Some(Ok(values)) 145 | } 146 | 147 | /// Each object contains zero or more fields, which each have names and values. 148 | /// 149 | /// - `Some(Ok((key, val)))` means we parsed a keyval field pair. 150 | /// - `Some(Err(e))` means we hit some unrecoverable error. 151 | /// - `None` means we parsed nothing and hit the end of the object. 152 | fn object_field(toks: &mut impl Tokens) -> Option> { 153 | if toks.peek() == Some('}') { 154 | return None; 155 | } 156 | let start = toks.location(); 157 | 158 | // Any valid string is also a valid field name. If we don't find a 159 | // string here, or it fails to parse, we kick up a fuss. 160 | let name = match string(&mut *toks) { 161 | None => return Some(Err(ErrorKind::InvalidObjectField.at(start.clone(), start))), 162 | Some(Err(err)) => return Some(Err(err)), 163 | Some(Ok(s)) => s, 164 | }; 165 | 166 | skip_whitespace(&mut *toks); 167 | if !toks.token(':') { 168 | let loc = toks.location(); 169 | return Some(Err( 170 | ErrorKind::MissingObjectFieldSeparator.at(loc.clone(), loc) 171 | )); 172 | } 173 | skip_whitespace(&mut *toks); 174 | 175 | // And after the name comes some arbitrary value: 176 | let val = match value(&mut *toks) { 177 | Ok(val) => val, 178 | Err(e) => return Some(Err(e)), 179 | }; 180 | 181 | Some(Ok((name, val))) 182 | } 183 | 184 | /// Some fairly naive parsing of strings which just manually iterates over tokens 185 | /// to handle basic escapes and pushes them to a string. 186 | /// 187 | /// - `None` if nothing consumed and not a string 188 | /// - `Some(Ok(s))` if we parsed a string successfully 189 | /// - `Some(Err(e))` if something went wrong parsing a string. 190 | fn string(toks: &mut impl Tokens) -> Option> { 191 | // Try to consume a '"'. If we can't, we consume nothing and bail. 192 | if !toks.token('"') { 193 | return None; 194 | } 195 | 196 | // manually iterate over chars and handle them as needed, 197 | // adding them to our string. 198 | let mut s = String::new(); 199 | while let Some(char) = toks.next() { 200 | match char { 201 | // Handle escape chars (naively; ignore \uXXX for instance): 202 | '\\' => { 203 | let Some(escape_char) = toks.next() else { 204 | let loc = toks.location(); 205 | return Some(Err(ErrorKind::UnexpectedEof.at(loc.clone(), loc))); 206 | }; 207 | let substitute_char = match escape_char { 208 | 'n' => '\n', 209 | 't' => '\t', 210 | 'r' => '\r', 211 | '"' => '"', 212 | '\\' => '\\', 213 | // If we don't recognise the escape char, return an error: 214 | c => { 215 | let loc = toks.location(); 216 | return Some(Err(ErrorKind::InvalidEscapeChar(c).at(loc.clone(), loc))); 217 | } 218 | }; 219 | s.push(substitute_char) 220 | } 221 | // String closed; return it! 222 | '"' => return Some(Ok(s)), 223 | // Some standard char; add it to our string. 224 | c => s.push(c), 225 | } 226 | } 227 | 228 | // The string should have been closed above; if we get this far, it hasn't 229 | // been, so return an error. 230 | let loc = toks.location(); 231 | Some(Err(ErrorKind::UnexpectedEof.at(loc.clone(), loc))) 232 | } 233 | 234 | /// true or false; None if neither! 235 | fn bool(toks: &mut impl Tokens) -> Option { 236 | yap::one_of!(toks; 237 | toks.tokens("true".chars()).then_some(true), 238 | toks.tokens("false".chars()).then_some(false) 239 | ) 240 | } 241 | 242 | // Is null seen? None if not. 243 | fn null(toks: &mut impl Tokens) -> bool { 244 | toks.tokens("null".chars()) 245 | } 246 | 247 | /// Use the [`yap::chars::parse_f64`] helper function to parse 248 | /// anything that rust considers a valid float (which is a little more 249 | /// permissive than the JSON standard, actually). 250 | fn number(toks: &mut impl Tokens) -> Option { 251 | yap::chars::parse_f64::(toks) 252 | } 253 | 254 | fn skip_whitespace(toks: &mut impl Tokens) { 255 | toks.skip_while(|c| c.is_ascii_whitespace()); 256 | } 257 | 258 | fn field_separator(toks: &mut impl Tokens) -> bool { 259 | toks.surrounded_by(|t| t.token(','), |t| skip_whitespace(t)) 260 | } 261 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | # Note that all fields that take a lint level have these possible values: 2 | # * deny - An error will be produced and the check will fail 3 | # * warn - A warning will be produced, but the check will not fail 4 | # * allow - No warning or error will be produced, though in some cases a note 5 | # will be 6 | 7 | # Root options 8 | 9 | # The graph table configures how the dependency graph is constructed and thus 10 | # which crates the checks are performed against 11 | [graph] 12 | # If 1 or more target triples (and optionally, target_features) are specified, 13 | # only the specified targets will be checked when running `cargo deny check`. 14 | # This means, if a particular package is only ever used as a target specific 15 | # dependency, such as, for example, the `nix` crate only being used via the 16 | # `target_family = "unix"` configuration, that only having windows targets in 17 | # this list would mean the nix crate, as well as any of its exclusive 18 | # dependencies not shared by any other crates, would be ignored, as the target 19 | # list here is effectively saying which targets you are building for. 20 | targets = [ 21 | # The triple can be any string, but only the target triples built in to 22 | # rustc (as of 1.40) can be checked against actual config expressions 23 | #"x86_64-unknown-linux-musl", 24 | # You can also specify which target_features you promise are enabled for a 25 | # particular target. target_features are currently not validated against 26 | # the actual valid features supported by the target architecture. 27 | #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, 28 | ] 29 | # When creating the dependency graph used as the source of truth when checks are 30 | # executed, this field can be used to prune crates from the graph, removing them 31 | # from the view of cargo-deny. This is an extremely heavy hammer, as if a crate 32 | # is pruned from the graph, all of its dependencies will also be pruned unless 33 | # they are connected to another crate in the graph that hasn't been pruned, 34 | # so it should be used with care. The identifiers are [Package ID Specifications] 35 | # (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html) 36 | #exclude = [] 37 | # If true, metadata will be collected with `--all-features`. Note that this can't 38 | # be toggled off if true, if you want to conditionally enable `--all-features` it 39 | # is recommended to pass `--all-features` on the cmd line instead 40 | all-features = false 41 | # If true, metadata will be collected with `--no-default-features`. The same 42 | # caveat with `all-features` applies 43 | no-default-features = false 44 | # If set, these feature will be enabled when collecting metadata. If `--features` 45 | # is specified on the cmd line they will take precedence over this option. 46 | #features = [] 47 | 48 | # The output table provides options for how/if diagnostics are outputted 49 | [output] 50 | # When outputting inclusion graphs in diagnostics that include features, this 51 | # option can be used to specify the depth at which feature edges will be added. 52 | # This option is included since the graphs can be quite large and the addition 53 | # of features from the crate(s) to all of the graph roots can be far too verbose. 54 | # This option can be overridden via `--feature-depth` on the cmd line 55 | feature-depth = 1 56 | 57 | # This section is considered when running `cargo deny check advisories` 58 | # More documentation for the advisories section can be found here: 59 | # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html 60 | [advisories] 61 | # The path where the advisory databases are cloned/fetched into 62 | #db-path = "$CARGO_HOME/advisory-dbs" 63 | # The url(s) of the advisory databases to use 64 | #db-urls = ["https://github.com/rustsec/advisory-db"] 65 | # A list of advisory IDs to ignore. Note that ignored advisories will still 66 | # output a note when they are encountered. 67 | ignore = [ 68 | #"RUSTSEC-0000-0000", 69 | #{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" }, 70 | #"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish 71 | #{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" }, 72 | ] 73 | # If this is true, then cargo deny will use the git executable to fetch advisory database. 74 | # If this is false, then it uses a built-in git library. 75 | # Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support. 76 | # See Git Authentication for more information about setting up git authentication. 77 | #git-fetch-with-cli = true 78 | 79 | # This section is considered when running `cargo deny check licenses` 80 | # More documentation for the licenses section can be found here: 81 | # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html 82 | [licenses] 83 | # List of explicitly allowed licenses 84 | # See https://spdx.org/licenses/ for list of possible licenses 85 | # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. 86 | allow = [ 87 | "MIT", 88 | "MIT-0", 89 | "Apache-2.0", 90 | "BSD-2-Clause", 91 | "BSD-3-Clause", 92 | "MPL-2.0", 93 | "Unicode-DFS-2016", 94 | "CC0-1.0", 95 | "ISC", 96 | "OpenSSL", 97 | ] 98 | # The confidence threshold for detecting a license from license text. 99 | # The higher the value, the more closely the license text must be to the 100 | # canonical license text of a valid SPDX license file. 101 | # [possible values: any between 0.0 and 1.0]. 102 | confidence-threshold = 0.8 103 | # Allow 1 or more licenses on a per-crate basis, so that particular licenses 104 | # aren't accepted for every possible crate as with the normal allow list 105 | exceptions = [ 106 | # Each entry is the crate and version constraint, and its specific allow 107 | # list 108 | #{ allow = ["Zlib"], crate = "adler32" }, 109 | ] 110 | 111 | # Some crates don't have (easily) machine readable licensing information, 112 | # adding a clarification entry for it allows you to manually specify the 113 | # licensing information 114 | [[licenses.clarify]] 115 | # The package spec the clarification applies to 116 | crate = "ring" 117 | # The SPDX expression for the license requirements of the crate 118 | expression = "MIT AND ISC AND OpenSSL" 119 | # One or more files in the crate's source used as the "source of truth" for 120 | # the license expression. If the contents match, the clarification will be used 121 | # when running the license check, otherwise the clarification will be ignored 122 | # and the crate will be checked normally, which may produce warnings or errors 123 | # depending on the rest of your configuration 124 | license-files = [ 125 | # Each entry is a crate relative path, and the (opaque) hash of its contents 126 | { path = "LICENSE", hash = 0xbd0eed23 } 127 | ] 128 | 129 | [licenses.private] 130 | # If true, ignores workspace crates that aren't published, or are only 131 | # published to private registries. 132 | # To see how to mark a crate as unpublished (to the official registry), 133 | # visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field. 134 | ignore = true 135 | # One or more private registries that you might publish crates to, if a crate 136 | # is only published to private registries, and ignore is true, the crate will 137 | # not have its license(s) checked 138 | registries = [ 139 | #"https://sekretz.com/registry 140 | ] 141 | 142 | # This section is considered when running `cargo deny check bans`. 143 | # More documentation about the 'bans' section can be found here: 144 | # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html 145 | [bans] 146 | # Lint level for when multiple versions of the same crate are detected 147 | multiple-versions = "warn" 148 | # Lint level for when a crate version requirement is `*` 149 | wildcards = "allow" 150 | # The graph highlighting used when creating dotgraphs for crates 151 | # with multiple versions 152 | # * lowest-version - The path to the lowest versioned duplicate is highlighted 153 | # * simplest-path - The path to the version with the fewest edges is highlighted 154 | # * all - Both lowest-version and simplest-path are used 155 | highlight = "all" 156 | # The default lint level for `default` features for crates that are members of 157 | # the workspace that is being checked. This can be overridden by allowing/denying 158 | # `default` on a crate-by-crate basis if desired. 159 | workspace-default-features = "allow" 160 | # The default lint level for `default` features for external crates that are not 161 | # members of the workspace. This can be overridden by allowing/denying `default` 162 | # on a crate-by-crate basis if desired. 163 | external-default-features = "allow" 164 | # List of crates that are allowed. Use with care! 165 | allow = [ 166 | #"ansi_term@0.11.0", 167 | #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is allowed" }, 168 | ] 169 | # List of crates to deny 170 | deny = [ 171 | #"ansi_term@0.11.0", 172 | #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is banned" }, 173 | # Wrapper crates can optionally be specified to allow the crate when it 174 | # is a direct dependency of the otherwise banned crate 175 | #{ crate = "ansi_term@0.11.0", wrappers = ["this-crate-directly-depends-on-ansi_term"] }, 176 | ] 177 | 178 | # List of features to allow/deny 179 | # Each entry the name of a crate and a version range. If version is 180 | # not specified, all versions will be matched. 181 | #[[bans.features]] 182 | #crate = "reqwest" 183 | # Features to not allow 184 | #deny = ["json"] 185 | # Features to allow 186 | #allow = [ 187 | # "rustls", 188 | # "__rustls", 189 | # "__tls", 190 | # "hyper-rustls", 191 | # "rustls", 192 | # "rustls-pemfile", 193 | # "rustls-tls-webpki-roots", 194 | # "tokio-rustls", 195 | # "webpki-roots", 196 | #] 197 | # If true, the allowed features must exactly match the enabled feature set. If 198 | # this is set there is no point setting `deny` 199 | #exact = true 200 | 201 | # Certain crates/versions that will be skipped when doing duplicate detection. 202 | skip = [ 203 | #"ansi_term@0.11.0", 204 | #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason why it can't be updated/removed" }, 205 | ] 206 | # Similarly to `skip` allows you to skip certain crates during duplicate 207 | # detection. Unlike skip, it also includes the entire tree of transitive 208 | # dependencies starting at the specified crate, up to a certain depth, which is 209 | # by default infinite. 210 | skip-tree = [ 211 | #"ansi_term@0.11.0", # will be skipped along with _all_ of its direct and transitive dependencies 212 | #{ crate = "ansi_term@0.11.0", depth = 20 }, 213 | ] 214 | 215 | # This section is considered when running `cargo deny check sources`. 216 | # More documentation about the 'sources' section can be found here: 217 | # https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html 218 | [sources] 219 | # Lint level for what to happen when a crate from a crate registry that is not 220 | # in the allow list is encountered 221 | unknown-registry = "deny" 222 | # Lint level for what to happen when a crate from a git repository that is not 223 | # in the allow list is encountered 224 | unknown-git = "deny" 225 | # List of URLs for allowed crate registries. Defaults to the crates.io index 226 | # if not specified. If it is specified but empty, no registries are allowed. 227 | allow-registry = ["https://github.com/rust-lang/crates.io-index"] 228 | # List of URLs for allowed Git repositories 229 | allow-git = [] 230 | 231 | [sources.allow-org] 232 | # 1 or more github.com organizations to allow git sources for 233 | github = [] 234 | # 1 or more gitlab.com organizations to allow git sources for 235 | gitlab = [] 236 | # 1 or more bitbucket.org organizations to allow git sources for 237 | bitbucket = [] 238 | -------------------------------------------------------------------------------- /examples/winnow-app/parser.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::str; 3 | 4 | use winnow::prelude::*; 5 | use winnow::Result; 6 | use winnow::{ 7 | ascii::float, 8 | combinator::empty, 9 | combinator::fail, 10 | combinator::peek, 11 | combinator::{alt, dispatch}, 12 | combinator::{delimited, preceded, separated_pair, terminated}, 13 | combinator::{repeat, separated}, 14 | error::{AddContext, ParserError, StrContext}, 15 | token::{any, none_of, take, take_while}, 16 | }; 17 | 18 | use crate::json::JsonValue; 19 | 20 | pub type Stream<'i> = &'i str; 21 | 22 | /// The root element of a JSON parser is any value 23 | /// 24 | /// A parser has the following signature: 25 | /// `&mut Stream -> Result` 26 | /// 27 | /// most of the times you can ignore the error type and use the default (but this 28 | /// examples shows custom error types later on!) 29 | /// 30 | /// Here we use `&str` as input type, but parsers can be generic over 31 | /// the input type, work directly with `&[u8]`, or any other type that 32 | /// implements the required traits. 33 | pub fn json<'i, E: ParserError> + AddContext, StrContext>>( 34 | input: &mut Stream<'i>, 35 | ) -> Result { 36 | delimited(ws, json_value, ws).parse_next(input) 37 | } 38 | 39 | /// `alt` is a combinator that tries multiple parsers one by one, until 40 | /// one of them succeeds 41 | fn json_value<'i, E: ParserError> + AddContext, StrContext>>( 42 | input: &mut Stream<'i>, 43 | ) -> Result { 44 | // `dispatch` gives you `match`-like behavior compared to `alt` successively trying different 45 | // implementations. 46 | dispatch!(peek(any); 47 | 'n' => null.value(JsonValue::Null), 48 | 't' => true_.map(JsonValue::Boolean), 49 | 'f' => false_.map(JsonValue::Boolean), 50 | '"' => string.map(JsonValue::Str), 51 | '+' => float.map(JsonValue::Num), 52 | '-' => float.map(JsonValue::Num), 53 | '0'..='9' => float.map(JsonValue::Num), 54 | '[' => array.map(JsonValue::Array), 55 | '{' => object.map(JsonValue::Object), 56 | _ => fail, 57 | ) 58 | .parse_next(input) 59 | } 60 | 61 | /// `literal(string)` generates a parser that takes the argument string. 62 | /// 63 | /// This also shows returning a sub-slice of the original input 64 | fn null<'i, E: ParserError>>(input: &mut Stream<'i>) -> Result<&'i str, E> { 65 | // This is a parser that returns `"null"` if it sees the string "null", and 66 | // an error otherwise 67 | "null".parse_next(input) 68 | } 69 | 70 | /// We can combine `tag` with other functions, like `value` which returns a given constant value on 71 | /// success. 72 | fn true_<'i, E: ParserError>>(input: &mut Stream<'i>) -> Result { 73 | // This is a parser that returns `true` if it sees the string "true", and 74 | // an error otherwise 75 | "true".value(true).parse_next(input) 76 | } 77 | 78 | /// We can combine `tag` with other functions, like `value` which returns a given constant value on 79 | /// success. 80 | fn false_<'i, E: ParserError>>(input: &mut Stream<'i>) -> Result { 81 | // This is a parser that returns `false` if it sees the string "false", and 82 | // an error otherwise 83 | "false".value(false).parse_next(input) 84 | } 85 | 86 | /// This parser gathers all `char`s up into a `String`with a parse to take the double quote 87 | /// character, before the string (using `preceded`) and after the string (using `terminated`). 88 | fn string<'i, E: ParserError> + AddContext, StrContext>>( 89 | input: &mut Stream<'i>, 90 | ) -> Result { 91 | preceded( 92 | '\"', 93 | terminated( 94 | repeat(0.., character).fold(String::new, |mut string, c| { 95 | string.push(c); 96 | string 97 | }), 98 | '\"', 99 | ), 100 | ) 101 | // `context` lets you add a static string to errors to provide more information in the 102 | // error chain (to indicate which parser had an error) 103 | .context(StrContext::Expected("string".into())) 104 | .parse_next(input) 105 | } 106 | 107 | /// You can mix the above declarative parsing with an imperative style to handle more unique cases, 108 | /// like escaping 109 | fn character<'i, E: ParserError>>(input: &mut Stream<'i>) -> Result { 110 | let c = none_of('\"').parse_next(input)?; 111 | if c == '\\' { 112 | dispatch!(any; 113 | '"' => empty.value('"'), 114 | '\\' => empty.value('\\'), 115 | '/' => empty.value('/'), 116 | 'b' => empty.value('\x08'), 117 | 'f' => empty.value('\x0C'), 118 | 'n' => empty.value('\n'), 119 | 'r' => empty.value('\r'), 120 | 't' => empty.value('\t'), 121 | 'u' => unicode_escape, 122 | _ => fail, 123 | ) 124 | .parse_next(input) 125 | } else { 126 | Ok(c) 127 | } 128 | } 129 | 130 | fn unicode_escape<'i, E: ParserError>>(input: &mut Stream<'i>) -> Result { 131 | alt(( 132 | // Not a surrogate 133 | u16_hex 134 | .verify(|cp| !(0xD800..0xE000).contains(cp)) 135 | .map(|cp| cp as u32), 136 | // See https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details 137 | separated_pair(u16_hex, "\\u", u16_hex) 138 | .verify(|(high, low)| (0xD800..0xDC00).contains(high) && (0xDC00..0xE000).contains(low)) 139 | .map(|(high, low)| { 140 | let high_ten = (high as u32) - 0xD800; 141 | let low_ten = (low as u32) - 0xDC00; 142 | (high_ten << 10) + low_ten + 0x10000 143 | }), 144 | )) 145 | .verify_map( 146 | // Could be probably replaced with .unwrap() or _unchecked due to the verify checks 147 | std::char::from_u32, 148 | ) 149 | .parse_next(input) 150 | } 151 | 152 | fn u16_hex<'i, E: ParserError>>(input: &mut Stream<'i>) -> Result { 153 | take(4usize) 154 | .verify_map(|s| u16::from_str_radix(s, 16).ok()) 155 | .parse_next(input) 156 | } 157 | 158 | /// Some combinators, like `separated` or `repeat`, will call a parser repeatedly, 159 | /// accumulating results in a `Vec`, until it encounters an error. 160 | /// If you want more control on the parser application, check out the `iterator` 161 | /// combinator (cf `examples/iterator.rs`) 162 | fn array<'i, E: ParserError> + AddContext, StrContext>>( 163 | input: &mut Stream<'i>, 164 | ) -> Result, E> { 165 | preceded( 166 | ('[', ws), 167 | terminated(separated(0.., json_value, (ws, ',', ws)), (ws, ']')), 168 | ) 169 | .context(StrContext::Expected("array".into())) 170 | .parse_next(input) 171 | } 172 | 173 | fn object<'i, E: ParserError> + AddContext, StrContext>>( 174 | input: &mut Stream<'i>, 175 | ) -> Result, E> { 176 | preceded( 177 | ('{', ws), 178 | terminated(separated(0.., key_value, (ws, ',', ws)), (ws, '}')), 179 | ) 180 | .context(StrContext::Expected("object".into())) 181 | .parse_next(input) 182 | } 183 | 184 | fn key_value<'i, E: ParserError> + AddContext, StrContext>>( 185 | input: &mut Stream<'i>, 186 | ) -> Result<(String, JsonValue), E> { 187 | separated_pair(string, (ws, ':', ws), json_value).parse_next(input) 188 | } 189 | 190 | /// Parser combinators are constructed from the bottom up: 191 | /// first we write parsers for the smallest elements (here a space character), 192 | /// then we'll combine them in larger parsers 193 | fn ws<'i, E: ParserError>>(input: &mut Stream<'i>) -> Result<&'i str, E> { 194 | // Combinators like `take_while` return a function. That function is the 195 | // parser,to which we can pass the input 196 | take_while(0.., WS).parse_next(input) 197 | } 198 | 199 | const WS: &[char] = &[' ', '\t', '\r', '\n']; 200 | 201 | #[cfg(test)] 202 | mod test { 203 | #[allow(clippy::useless_attribute)] 204 | #[allow(unused_imports)] // its dead for benches 205 | use super::*; 206 | 207 | #[allow(clippy::useless_attribute)] 208 | #[allow(dead_code)] // its dead for benches 209 | type Error = winnow::error::ContextError; 210 | 211 | #[test] 212 | fn json_string() { 213 | assert_eq!(string::.parse_peek("\"\""), Ok(("", "".to_owned()))); 214 | assert_eq!( 215 | string::.parse_peek("\"abc\""), 216 | Ok(("", "abc".to_owned())) 217 | ); 218 | assert_eq!( 219 | string:: 220 | .parse_peek("\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""), 221 | Ok(("", "abc\"\\/\x08\x0C\n\r\t\x01——def".to_owned())), 222 | ); 223 | assert_eq!( 224 | string::.parse_peek("\"\\uD83D\\uDE10\""), 225 | Ok(("", "😐".to_owned())) 226 | ); 227 | 228 | assert!(string::.parse_peek("\"").is_err()); 229 | assert!(string::.parse_peek("\"abc").is_err()); 230 | assert!(string::.parse_peek("\"\\\"").is_err()); 231 | assert!(string::.parse_peek("\"\\u123\"").is_err()); 232 | assert!(string::.parse_peek("\"\\uD800\"").is_err()); 233 | assert!(string::.parse_peek("\"\\uD800\\uD800\"").is_err()); 234 | assert!(string::.parse_peek("\"\\uDC00\"").is_err()); 235 | } 236 | 237 | #[test] 238 | fn json_object() { 239 | use JsonValue::{Num, Object, Str}; 240 | 241 | let input = r#"{"a":42,"b":"x"}"#; 242 | 243 | let expected = Object( 244 | vec![ 245 | ("a".to_owned(), Num(42.0)), 246 | ("b".to_owned(), Str("x".to_owned())), 247 | ] 248 | .into_iter() 249 | .collect(), 250 | ); 251 | 252 | assert_eq!(json::.parse_peek(input), Ok(("", expected))); 253 | } 254 | 255 | #[test] 256 | fn json_array() { 257 | use JsonValue::{Array, Num, Str}; 258 | 259 | let input = r#"[42,"x"]"#; 260 | 261 | let expected = Array(vec![Num(42.0), Str("x".to_owned())]); 262 | 263 | assert_eq!(json::.parse_peek(input), Ok(("", expected))); 264 | } 265 | 266 | #[test] 267 | fn json_whitespace() { 268 | use JsonValue::{Array, Boolean, Null, Num, Object, Str}; 269 | 270 | let input = r#" 271 | { 272 | "null" : null, 273 | "true" :true , 274 | "false": false , 275 | "number" : 123e4 , 276 | "string" : " abc 123 " , 277 | "array" : [ false , 1 , "two" ] , 278 | "object" : { "a" : 1.0 , "b" : "c" } , 279 | "empty_array" : [ ] , 280 | "empty_object" : { } 281 | } 282 | "#; 283 | 284 | assert_eq!( 285 | json::.parse_peek(input), 286 | Ok(( 287 | "", 288 | Object( 289 | vec![ 290 | ("null".to_owned(), Null), 291 | ("true".to_owned(), Boolean(true)), 292 | ("false".to_owned(), Boolean(false)), 293 | ("number".to_owned(), Num(123e4)), 294 | ("string".to_owned(), Str(" abc 123 ".to_owned())), 295 | ( 296 | "array".to_owned(), 297 | Array(vec![Boolean(false), Num(1.0), Str("two".to_owned())]) 298 | ), 299 | ( 300 | "object".to_owned(), 301 | Object( 302 | vec![ 303 | ("a".to_owned(), Num(1.0)), 304 | ("b".to_owned(), Str("c".to_owned())), 305 | ] 306 | .into_iter() 307 | .collect() 308 | ) 309 | ), 310 | ("empty_array".to_owned(), Array(vec![]),), 311 | ("empty_object".to_owned(), Object(HashMap::new()),), 312 | ] 313 | .into_iter() 314 | .collect() 315 | ) 316 | )) 317 | ); 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /runs/2022-07-14-Doomslug.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2022-07-14", 3 | "hostname": "Doomslug", 4 | "os": "Linux", 5 | "os_ver": "5.10.16.3-microsoft-standard-WSL2", 6 | "arch": "x86_64", 7 | "cpus": 20, 8 | "libs": { 9 | "examples/chumsky-app/Cargo.toml": { 10 | "name": "chumsky", 11 | "manifest_path": "examples/chumsky-app/Cargo.toml", 12 | "crate": "ariadne", 13 | "version": "v0.1.5", 14 | "build": { 15 | "results": [ 16 | { 17 | "command": "cargo build -j 20 --package chumsky-app", 18 | "mean": 4.9691443115, 19 | "stddev": 0.04871086987009003, 20 | "median": 4.9503248623, 21 | "user": 7.549100640000001, 22 | "system": 0.8903444, 23 | "min": 4.9250849213, 24 | "max": 5.0498349583, 25 | "times": [ 26 | 4.9503248623, 27 | 4.9440213933, 28 | 4.9250849213, 29 | 4.9764554223, 30 | 5.0498349583 31 | ], 32 | "exit_codes": [ 33 | 0, 34 | 0, 35 | 0, 36 | 0, 37 | 0 38 | ] 39 | } 40 | ] 41 | }, 42 | "run": { 43 | "results": [ 44 | { 45 | "command": "target/release/chumsky-app third_party/nativejson-benchmark/data/canada.json", 46 | "mean": 0.748252019, 47 | "stddev": 0.029624534001256076, 48 | "median": 0.7395108818, 49 | "user": 0.72012992, 50 | "system": 0.028041, 51 | "min": 0.7185949488, 52 | "max": 0.7929958988, 53 | "times": [ 54 | 0.7287642478, 55 | 0.7613941178, 56 | 0.7185949488, 57 | 0.7929958988, 58 | 0.7395108818 59 | ], 60 | "exit_codes": [ 61 | 0, 62 | 0, 63 | 0, 64 | 0, 65 | 0 66 | ] 67 | } 68 | ] 69 | }, 70 | "size": 4760184 71 | }, 72 | "examples/nom-app/Cargo.toml": { 73 | "name": "nom", 74 | "manifest_path": "examples/nom-app/Cargo.toml", 75 | "crate": "nom", 76 | "version": "v7.1.1", 77 | "build": { 78 | "results": [ 79 | { 80 | "command": "cargo build -j 20 --package nom-app", 81 | "mean": 1.6659424982600002, 82 | "stddev": 0.017710873388571382, 83 | "median": 1.6627810414600002, 84 | "user": 2.0015135, 85 | "system": 0.23396246, 86 | "min": 1.64640270246, 87 | "max": 1.69090554646, 88 | "times": [ 89 | 1.69090554646, 90 | 1.65392146546, 91 | 1.64640270246, 92 | 1.6627810414600002, 93 | 1.6757017354600001 94 | ], 95 | "exit_codes": [ 96 | 0, 97 | 0, 98 | 0, 99 | 0, 100 | 0 101 | ] 102 | } 103 | ] 104 | }, 105 | "run": { 106 | "results": [ 107 | { 108 | "command": "target/release/nom-app third_party/nativejson-benchmark/data/canada.json", 109 | "mean": 0.5015574125000001, 110 | "stddev": 0.0079614971225169, 111 | "median": 0.5036880207000001, 112 | "user": 0.47354248, 113 | "system": 0.0279898, 114 | "min": 0.48852677270000006, 115 | "max": 0.5097072677000001, 116 | "times": [ 117 | 0.5036880207000001, 118 | 0.48852677270000006, 119 | 0.5008108497000001, 120 | 0.5097072677000001, 121 | 0.5050541517000001 122 | ], 123 | "exit_codes": [ 124 | 0, 125 | 0, 126 | 0, 127 | 0, 128 | 0 129 | ] 130 | } 131 | ] 132 | }, 133 | "size": 4016288 134 | }, 135 | "examples/null-app/Cargo.toml": { 136 | "name": "null", 137 | "manifest_path": "examples/null-app/Cargo.toml", 138 | "crate": null, 139 | "version": null, 140 | "build": { 141 | "results": [ 142 | { 143 | "command": "cargo build -j 20 --package null-app", 144 | "mean": 0.27607208866, 145 | "stddev": 0.004039183350445615, 146 | "median": 0.27573472666000004, 147 | "user": 0.2277622, 148 | "system": 0.060631439999999995, 149 | "min": 0.26911436266000005, 150 | "max": 0.28102570166, 151 | "times": [ 152 | 0.27489802466, 153 | 0.27864734266, 154 | 0.27561319166000003, 155 | 0.27542462966000003, 156 | 0.27585626166000005, 157 | 0.28102570166, 158 | 0.26911436266000005, 159 | 0.28100385266000005, 160 | 0.27030508966000005, 161 | 0.27883242966000005 162 | ], 163 | "exit_codes": [ 164 | 0, 165 | 0, 166 | 0, 167 | 0, 168 | 0, 169 | 0, 170 | 0, 171 | 0, 172 | 0, 173 | 0 174 | ] 175 | } 176 | ] 177 | }, 178 | "run": { 179 | "results": [ 180 | { 181 | "command": "target/release/null-app third_party/nativejson-benchmark/data/canada.json", 182 | "mean": 0.027776051847692326, 183 | "stddev": 0.0011826406998936877, 184 | "median": 0.02788691854, 185 | "user": 0.027039766153846175, 186 | "system": 0.0007154230769230769, 187 | "min": 0.02388870654, 188 | "max": 0.03026570554, 189 | "times": [ 190 | 0.02841181454, 191 | 0.02789629154, 192 | 0.02747055354, 193 | 0.02514877654, 194 | 0.029796357540000002, 195 | 0.02821384054, 196 | 0.028003253540000002, 197 | 0.02799294254, 198 | 0.02787754554, 199 | 0.02388870654, 200 | 0.02832426754, 201 | 0.029289442540000002, 202 | 0.02823268654, 203 | 0.02910474054, 204 | 0.02927472054, 205 | 0.02700120654, 206 | 0.02674622154, 207 | 0.02874952154, 208 | 0.02705319254, 209 | 0.02635258854, 210 | 0.02679290854, 211 | 0.02800715354, 212 | 0.02934314054, 213 | 0.02898549254, 214 | 0.02978037154, 215 | 0.02715302354, 216 | 0.02865313754, 217 | 0.02789904454, 218 | 0.02708786154, 219 | 0.02748260754, 220 | 0.02845507354, 221 | 0.02790722754, 222 | 0.02905179654, 223 | 0.02587478554, 224 | 0.02939456854, 225 | 0.02760405354, 226 | 0.02806291654, 227 | 0.027213182540000002, 228 | 0.02890886154, 229 | 0.02719116254, 230 | 0.02886045454, 231 | 0.02794811454, 232 | 0.02596825654, 233 | 0.029518000540000002, 234 | 0.02786312954, 235 | 0.027105900540000002, 236 | 0.02723215654, 237 | 0.02864957154, 238 | 0.02749238754, 239 | 0.02717519054, 240 | 0.02956117654, 241 | 0.028249944540000002, 242 | 0.02636851454, 243 | 0.02651949254, 244 | 0.02803657354, 245 | 0.02830593154, 246 | 0.02666412654, 247 | 0.02718427554, 248 | 0.02814344554, 249 | 0.02771619854, 250 | 0.02720653954, 251 | 0.02914724854, 252 | 0.026408775540000002, 253 | 0.02690138354, 254 | 0.02641394954, 255 | 0.02705789754, 256 | 0.02583649754, 257 | 0.02675576454, 258 | 0.02600341554, 259 | 0.02688415954, 260 | 0.027844478540000002, 261 | 0.02661956154, 262 | 0.02761406954, 263 | 0.03026570554, 264 | 0.02535244054, 265 | 0.02845661354, 266 | 0.02587350354, 267 | 0.02871764654, 268 | 0.02888691854, 269 | 0.02834775154, 270 | 0.02730215554, 271 | 0.028458102540000002, 272 | 0.028408363540000002, 273 | 0.02998238954, 274 | 0.025639448540000002, 275 | 0.02857515754, 276 | 0.02814815254, 277 | 0.02849718154, 278 | 0.02942788054, 279 | 0.02808353254, 280 | 0.027281476540000002, 281 | 0.02984013654, 282 | 0.028121774540000002, 283 | 0.02943537454, 284 | 0.02724169954, 285 | 0.02913040654, 286 | 0.02750339054, 287 | 0.02847725654, 288 | 0.02731427154, 289 | 0.02651872454, 290 | 0.02630995454, 291 | 0.02787043254, 292 | 0.02681027554, 293 | 0.02749765054 294 | ], 295 | "exit_codes": [ 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 0, 301 | 0, 302 | 0, 303 | 0, 304 | 0, 305 | 0, 306 | 0, 307 | 0, 308 | 0, 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 0, 320 | 0, 321 | 0, 322 | 0, 323 | 0, 324 | 0, 325 | 0, 326 | 0, 327 | 0, 328 | 0, 329 | 0, 330 | 0, 331 | 0, 332 | 0, 333 | 0, 334 | 0, 335 | 0, 336 | 0, 337 | 0, 338 | 0, 339 | 0, 340 | 0, 341 | 0, 342 | 0, 343 | 0, 344 | 0, 345 | 0, 346 | 0, 347 | 0, 348 | 0, 349 | 0, 350 | 0, 351 | 0, 352 | 0, 353 | 0, 354 | 0, 355 | 0, 356 | 0, 357 | 0, 358 | 0, 359 | 0, 360 | 0, 361 | 0, 362 | 0, 363 | 0, 364 | 0, 365 | 0, 366 | 0, 367 | 0, 368 | 0, 369 | 0, 370 | 0, 371 | 0, 372 | 0, 373 | 0, 374 | 0, 375 | 0, 376 | 0, 377 | 0, 378 | 0, 379 | 0, 380 | 0, 381 | 0, 382 | 0, 383 | 0, 384 | 0, 385 | 0, 386 | 0, 387 | 0, 388 | 0, 389 | 0, 390 | 0, 391 | 0, 392 | 0, 393 | 0, 394 | 0, 395 | 0, 396 | 0, 397 | 0, 398 | 0, 399 | 0 400 | ] 401 | } 402 | ] 403 | }, 404 | "size": 3852288 405 | }, 406 | "examples/pom-app/Cargo.toml": { 407 | "name": "pom", 408 | "manifest_path": "examples/pom-app/Cargo.toml", 409 | "crate": "pom", 410 | "version": "v3.2.0", 411 | "build": { 412 | "results": [ 413 | { 414 | "command": "cargo build -j 20 --package pom-app", 415 | "mean": 0.6009288554600001, 416 | "stddev": 0.011954527435616678, 417 | "median": 0.60320529166, 418 | "user": 0.73326332, 419 | "system": 0.13338152, 420 | "min": 0.5880766846600001, 421 | "max": 0.6140005016600001, 422 | "times": [ 423 | 0.6103415686600001, 424 | 0.5880766846600001, 425 | 0.6140005016600001, 426 | 0.5890202306600001, 427 | 0.60320529166 428 | ], 429 | "exit_codes": [ 430 | 0, 431 | 0, 432 | 0, 433 | 0, 434 | 0 435 | ] 436 | } 437 | ] 438 | }, 439 | "run": { 440 | "results": [ 441 | { 442 | "command": "target/release/pom-app third_party/nativejson-benchmark/data/canada.json", 443 | "mean": 0.8476651460200001, 444 | "stddev": 0.028963132367556748, 445 | "median": 0.8479757276200001, 446 | "user": 0.81363374, 447 | "system": 0.0339904, 448 | "min": 0.8168055026200001, 449 | "max": 0.88193351562, 450 | "times": [ 451 | 0.8168055026200001, 452 | 0.82108909762, 453 | 0.8705218866200001, 454 | 0.8479757276200001, 455 | 0.88193351562 456 | ], 457 | "exit_codes": [ 458 | 0, 459 | 0, 460 | 0, 461 | 0, 462 | 0 463 | ] 464 | } 465 | ] 466 | }, 467 | "size": 4074520 468 | } 469 | } 470 | } -------------------------------------------------------------------------------- /runs/2023-03-23-seon.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2023-03-23", 3 | "hostname": "seon", 4 | "os": "Linux", 5 | "os_ver": "5.4.0-104-generic", 6 | "arch": "x86_64", 7 | "cpus": 8, 8 | "libs": { 9 | "examples/chumsky-app/Cargo.toml": { 10 | "name": "chumsky", 11 | "manifest_path": "examples/chumsky-app/Cargo.toml", 12 | "crate": "ariadne", 13 | "version": "v0.2.0", 14 | "build": { 15 | "results": [ 16 | { 17 | "command": "cargo build -j 8 --package chumsky-app", 18 | "mean": 9.637761924720001, 19 | "stddev": 0.13528939025155165, 20 | "median": 9.67642876212, 21 | "user": 19.03410874, 22 | "system": 1.82739232, 23 | "min": 9.46620146112, 24 | "max": 9.81693457912, 25 | "times": [ 26 | 9.68211702212, 27 | 9.54712779912, 28 | 9.67642876212, 29 | 9.46620146112, 30 | 9.81693457912 31 | ], 32 | "exit_codes": [ 33 | 0, 34 | 0, 35 | 0, 36 | 0, 37 | 0 38 | ] 39 | } 40 | ] 41 | }, 42 | "run": { 43 | "results": [ 44 | { 45 | "command": "target/release/chumsky-app third_party/nativejson-benchmark/data/canada.json", 46 | "mean": 1.83224174468, 47 | "stddev": 0.09950179020276849, 48 | "median": 1.82475635188, 49 | "user": 1.7625470399999998, 50 | "system": 0.06824966, 51 | "min": 1.69851301488, 52 | "max": 1.96292433488, 53 | "times": [ 54 | 1.78991746588, 55 | 1.82475635188, 56 | 1.96292433488, 57 | 1.88509755588, 58 | 1.69851301488 59 | ], 60 | "exit_codes": [ 61 | 0, 62 | 0, 63 | 0, 64 | 0, 65 | 0 66 | ] 67 | } 68 | ] 69 | }, 70 | "size": 5102064 71 | }, 72 | "examples/combine-app/Cargo.toml": { 73 | "name": "combine", 74 | "manifest_path": "examples/combine-app/Cargo.toml", 75 | "crate": "combine", 76 | "version": "v3.8.1", 77 | "build": { 78 | "results": [ 79 | { 80 | "command": "cargo build -j 8 --package combine-app", 81 | "mean": 5.97566948762, 82 | "stddev": 0.06752937905320192, 83 | "median": 5.94078607482, 84 | "user": 9.31636328, 85 | "system": 0.7573197, 86 | "min": 5.91252057282, 87 | "max": 6.04974148182, 88 | "times": [ 89 | 5.94078607482, 90 | 6.04974148182, 91 | 5.91252057282, 92 | 6.04790959582, 93 | 5.92738971282 94 | ], 95 | "exit_codes": [ 96 | 0, 97 | 0, 98 | 0, 99 | 0, 100 | 0 101 | ] 102 | } 103 | ] 104 | }, 105 | "run": { 106 | "results": [ 107 | { 108 | "command": "target/release/combine-app third_party/nativejson-benchmark/data/canada.json", 109 | "mean": 1.1345009392999998, 110 | "stddev": 0.017452506361378857, 111 | "median": 1.1320996025, 112 | "user": 1.0945619199999999, 113 | "system": 0.03992922, 114 | "min": 1.1109305455, 115 | "max": 1.1552027275, 116 | "times": [ 117 | 1.1320996025, 118 | 1.1552027275, 119 | 1.1474939954999999, 120 | 1.1109305455, 121 | 1.1267778255 122 | ], 123 | "exit_codes": [ 124 | 0, 125 | 0, 126 | 0, 127 | 0, 128 | 0 129 | ] 130 | } 131 | ] 132 | }, 133 | "size": 4528352 134 | }, 135 | "examples/nom-app/Cargo.toml": { 136 | "name": "nom", 137 | "manifest_path": "examples/nom-app/Cargo.toml", 138 | "crate": "nom", 139 | "version": "v7.1.3", 140 | "build": { 141 | "results": [ 142 | { 143 | "command": "cargo build -j 8 --package nom-app", 144 | "mean": 3.1123793668, 145 | "stddev": 0.09617417132840148, 146 | "median": 3.0547013880000002, 147 | "user": 4.4482596, 148 | "system": 0.4919105199999999, 149 | "min": 3.0348798820000003, 150 | "max": 3.2597106630000003, 151 | "times": [ 152 | 3.1605454080000004, 153 | 3.0520594930000002, 154 | 3.0547013880000002, 155 | 3.2597106630000003, 156 | 3.0348798820000003 157 | ], 158 | "exit_codes": [ 159 | 0, 160 | 0, 161 | 0, 162 | 0, 163 | 0 164 | ] 165 | } 166 | ] 167 | }, 168 | "run": { 169 | "results": [ 170 | { 171 | "command": "target/release/nom-app third_party/nativejson-benchmark/data/canada.json", 172 | "mean": 1.214101866, 173 | "stddev": 0.08270558269627223, 174 | "median": 1.223305128, 175 | "user": 1.15940982, 176 | "system": 0.05433481999999999, 177 | "min": 1.1066168060000001, 178 | "max": 1.311484689, 179 | "times": [ 180 | 1.2704051680000001, 181 | 1.223305128, 182 | 1.311484689, 183 | 1.158697539, 184 | 1.1066168060000001 185 | ], 186 | "exit_codes": [ 187 | 0, 188 | 0, 189 | 0, 190 | 0, 191 | 0 192 | ] 193 | } 194 | ] 195 | }, 196 | "size": 4457424 197 | }, 198 | "examples/null-app/Cargo.toml": { 199 | "name": "null", 200 | "manifest_path": "examples/null-app/Cargo.toml", 201 | "crate": null, 202 | "version": null, 203 | "build": { 204 | "results": [ 205 | { 206 | "command": "cargo build -j 8 --package null-app", 207 | "mean": 0.4315150056066667, 208 | "stddev": 0.013792526964690608, 209 | "median": 0.42920756194000004, 210 | "user": 0.4146054133333333, 211 | "system": 0.10358707333333332, 212 | "min": 0.41799861844, 213 | "max": 0.45777986744000004, 214 | "times": [ 215 | 0.41799861844, 216 | 0.42935041144, 217 | 0.42327291044000004, 218 | 0.42906471244000005, 219 | 0.45777986744000004, 220 | 0.43162351344000005 221 | ], 222 | "exit_codes": [ 223 | 0, 224 | 0, 225 | 0, 226 | 0, 227 | 0, 228 | 0 229 | ] 230 | } 231 | ] 232 | }, 233 | "run": { 234 | "results": [ 235 | { 236 | "command": "target/release/null-app third_party/nativejson-benchmark/data/canada.json", 237 | "mean": 0.03323835017311476, 238 | "stddev": 0.00289456311360135, 239 | "median": 0.03226416596, 240 | "user": 0.03165806327868853, 241 | "system": 0.00163377868852459, 242 | "min": 0.03095531496, 243 | "max": 0.048197164960000004, 244 | "times": [ 245 | 0.048197164960000004, 246 | 0.03237450596, 247 | 0.03197675696, 248 | 0.03221827496, 249 | 0.03142384396, 250 | 0.03271261996, 251 | 0.03174088196, 252 | 0.03215190596, 253 | 0.03229654396, 254 | 0.03265914496, 255 | 0.03228895396, 256 | 0.03162796396, 257 | 0.031249497959999997, 258 | 0.03209146596, 259 | 0.03177323596, 260 | 0.03162043796, 261 | 0.03315453896, 262 | 0.03259714796, 263 | 0.031737410960000004, 264 | 0.03966217296, 265 | 0.031903106960000004, 266 | 0.032326264960000003, 267 | 0.03186630296, 268 | 0.03197093796, 269 | 0.03289602096, 270 | 0.03200369096, 271 | 0.03226416596, 272 | 0.03198097396, 273 | 0.03184733396, 274 | 0.03222825896, 275 | 0.03225188596, 276 | 0.03169216796, 277 | 0.03223295296, 278 | 0.03209543596, 279 | 0.03269883696, 280 | 0.031862806960000004, 281 | 0.03148831096, 282 | 0.03212150696, 283 | 0.03309940296, 284 | 0.03241580496, 285 | 0.03095531496, 286 | 0.031010279959999998, 287 | 0.03115824196, 288 | 0.03507136896, 289 | 0.03119287296, 290 | 0.03616684296, 291 | 0.03175102296, 292 | 0.03718761396, 293 | 0.03335365596, 294 | 0.034428078960000004, 295 | 0.03370856396, 296 | 0.03268973096, 297 | 0.03375651196, 298 | 0.03296328296, 299 | 0.03596672296, 300 | 0.03329943196, 301 | 0.04374269996, 302 | 0.03534762396, 303 | 0.03485388896, 304 | 0.03419635996, 305 | 0.03593860896 306 | ], 307 | "exit_codes": [ 308 | 0, 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 0, 320 | 0, 321 | 0, 322 | 0, 323 | 0, 324 | 0, 325 | 0, 326 | 0, 327 | 0, 328 | 0, 329 | 0, 330 | 0, 331 | 0, 332 | 0, 333 | 0, 334 | 0, 335 | 0, 336 | 0, 337 | 0, 338 | 0, 339 | 0, 340 | 0, 341 | 0, 342 | 0, 343 | 0, 344 | 0, 345 | 0, 346 | 0, 347 | 0, 348 | 0, 349 | 0, 350 | 0, 351 | 0, 352 | 0, 353 | 0, 354 | 0, 355 | 0, 356 | 0, 357 | 0, 358 | 0, 359 | 0, 360 | 0, 361 | 0, 362 | 0, 363 | 0, 364 | 0, 365 | 0, 366 | 0, 367 | 0, 368 | 0 369 | ] 370 | } 371 | ] 372 | }, 373 | "size": 4295600 374 | }, 375 | "examples/peg-app/Cargo.toml": { 376 | "name": "peg", 377 | "manifest_path": "examples/peg-app/Cargo.toml", 378 | "crate": "peg", 379 | "version": "v0.8.1", 380 | "build": { 381 | "results": [ 382 | { 383 | "command": "cargo build -j 8 --package peg-app", 384 | "mean": 3.17895432094, 385 | "stddev": 0.12563620550396398, 386 | "median": 3.13382325394, 387 | "user": 6.27652494, 388 | "system": 0.7520773, 389 | "min": 3.05711800994, 390 | "max": 3.33086559994, 391 | "times": [ 392 | 3.05711800994, 393 | 3.0790892959400002, 394 | 3.13382325394, 395 | 3.33086559994, 396 | 3.2938754449400003 397 | ], 398 | "exit_codes": [ 399 | 0, 400 | 0, 401 | 0, 402 | 0, 403 | 0 404 | ] 405 | } 406 | ] 407 | }, 408 | "run": { 409 | "results": [ 410 | { 411 | "command": "target/release/peg-app third_party/nativejson-benchmark/data/canada.json", 412 | "mean": 0.027039583685309728, 413 | "stddev": 0.005064854138583792, 414 | "median": 0.025171983880000002, 415 | "user": 0.02541443946902655, 416 | "system": 0.0016297929203539824, 417 | "min": 0.02180774988, 418 | "max": 0.04609679788, 419 | "times": [ 420 | 0.02584863988, 421 | 0.02572559788, 422 | 0.02454185588, 423 | 0.027004253880000002, 424 | 0.02609183488, 425 | 0.02614623788, 426 | 0.02400686388, 427 | 0.02445608588, 428 | 0.02391130488, 429 | 0.02590732788, 430 | 0.025536701880000002, 431 | 0.02433871488, 432 | 0.03593447788, 433 | 0.02631337288, 434 | 0.02319991788, 435 | 0.02396346988, 436 | 0.03341651388, 437 | 0.02590098888, 438 | 0.027508651880000002, 439 | 0.023464343880000002, 440 | 0.03209035288, 441 | 0.029054635880000002, 442 | 0.02328416488, 443 | 0.04190473488, 444 | 0.02325922088, 445 | 0.02323640188, 446 | 0.03174664388, 447 | 0.02536505488, 448 | 0.02554195588, 449 | 0.02406933888, 450 | 0.03196533788, 451 | 0.035434132880000004, 452 | 0.03208344788, 453 | 0.02817761188, 454 | 0.03533774888, 455 | 0.02339624988, 456 | 0.02527344188, 457 | 0.02337668288, 458 | 0.02329374588, 459 | 0.02336158088, 460 | 0.02365827288, 461 | 0.02261797188, 462 | 0.02250410088, 463 | 0.02277093788, 464 | 0.02254110288, 465 | 0.02248211088, 466 | 0.022458284880000002, 467 | 0.02293418388, 468 | 0.02341464388, 469 | 0.02262558688, 470 | 0.03391254588, 471 | 0.02180774988, 472 | 0.02902282788, 473 | 0.02190300288, 474 | 0.04178573688, 475 | 0.02233236988, 476 | 0.02938632788, 477 | 0.02319529788, 478 | 0.022518734880000002, 479 | 0.038432620880000004, 480 | 0.02248409988, 481 | 0.02318445288, 482 | 0.02288580188, 483 | 0.022678043880000002, 484 | 0.02332119988, 485 | 0.02485273088, 486 | 0.02340264588, 487 | 0.02444097188, 488 | 0.02540076088, 489 | 0.02570787688, 490 | 0.02810902288, 491 | 0.023231705880000002, 492 | 0.02307082688, 493 | 0.025171983880000002, 494 | 0.02972645088, 495 | 0.03203647388, 496 | 0.027888516880000002, 497 | 0.04609679788, 498 | 0.02739648588, 499 | 0.02328668088, 500 | 0.02320929788, 501 | 0.023666384880000002, 502 | 0.02999754788, 503 | 0.023119347880000002, 504 | 0.02504687088, 505 | 0.040141452880000004, 506 | 0.02485422088, 507 | 0.025511271880000002, 508 | 0.03483541988, 509 | 0.02704429388, 510 | 0.02581929088, 511 | 0.02484083188, 512 | 0.02598170888, 513 | 0.02507303788, 514 | 0.02470609688, 515 | 0.030529214880000002, 516 | 0.026962859880000002, 517 | 0.02414013788, 518 | 0.02324179788, 519 | 0.02407976288, 520 | 0.02319079488, 521 | 0.034240341880000004, 522 | 0.03201080188, 523 | 0.03526492288, 524 | 0.03056096388, 525 | 0.031544743880000004, 526 | 0.03613170288, 527 | 0.03090282188, 528 | 0.02369576188, 529 | 0.03522426688, 530 | 0.02261503888, 531 | 0.029978895880000002, 532 | 0.03616346588 533 | ], 534 | "exit_codes": [ 535 | 0, 536 | 0, 537 | 0, 538 | 0, 539 | 0, 540 | 0, 541 | 0, 542 | 0, 543 | 0, 544 | 0, 545 | 0, 546 | 0, 547 | 0, 548 | 0, 549 | 0, 550 | 0, 551 | 0, 552 | 0, 553 | 0, 554 | 0, 555 | 0, 556 | 0, 557 | 0, 558 | 0, 559 | 0, 560 | 0, 561 | 0, 562 | 0, 563 | 0, 564 | 0, 565 | 0, 566 | 0, 567 | 0, 568 | 0, 569 | 0, 570 | 0, 571 | 0, 572 | 0, 573 | 0, 574 | 0, 575 | 0, 576 | 0, 577 | 0, 578 | 0, 579 | 0, 580 | 0, 581 | 0, 582 | 0, 583 | 0, 584 | 0, 585 | 0, 586 | 0, 587 | 0, 588 | 0, 589 | 0, 590 | 0, 591 | 0, 592 | 0, 593 | 0, 594 | 0, 595 | 0, 596 | 0, 597 | 0, 598 | 0, 599 | 0, 600 | 0, 601 | 0, 602 | 0, 603 | 0, 604 | 0, 605 | 0, 606 | 0, 607 | 0, 608 | 0, 609 | 0, 610 | 0, 611 | 0, 612 | 0, 613 | 0, 614 | 0, 615 | 0, 616 | 0, 617 | 0, 618 | 0, 619 | 0, 620 | 0, 621 | 0, 622 | 0, 623 | 0, 624 | 0, 625 | 0, 626 | 0, 627 | 0, 628 | 0, 629 | 0, 630 | 0, 631 | 0, 632 | 0, 633 | 0, 634 | 0, 635 | 0, 636 | 0, 637 | 0, 638 | 0, 639 | 0, 640 | 0, 641 | 0, 642 | 0, 643 | 0, 644 | 0, 645 | 0, 646 | 0, 647 | 0 648 | ] 649 | } 650 | ] 651 | }, 652 | "size": 4329776 653 | }, 654 | "examples/pest-app/Cargo.toml": { 655 | "name": "pest", 656 | "manifest_path": "examples/pest-app/Cargo.toml", 657 | "crate": "pest", 658 | "version": "v2.5.6", 659 | "build": { 660 | "results": [ 661 | { 662 | "command": "cargo build -j 8 --package pest-app", 663 | "mean": 6.889660276220001, 664 | "stddev": 0.24463930269502393, 665 | "median": 6.94322656622, 666 | "user": 13.549066499999999, 667 | "system": 1.2859718199999999, 668 | "min": 6.58077237122, 669 | "max": 7.195389754220001, 670 | "times": [ 671 | 6.712187703220001, 672 | 6.58077237122, 673 | 6.94322656622, 674 | 7.01672498622, 675 | 7.195389754220001 676 | ], 677 | "exit_codes": [ 678 | 0, 679 | 0, 680 | 0, 681 | 0, 682 | 0 683 | ] 684 | } 685 | ] 686 | }, 687 | "run": { 688 | "results": [ 689 | { 690 | "command": "target/release/pest-app third_party/nativejson-benchmark/data/canada.json", 691 | "mean": 1.13168286254, 692 | "stddev": 0.039462177838940835, 693 | "median": 1.1333543727400002, 694 | "user": 1.05622184, 695 | "system": 0.07522968, 696 | "min": 1.08005490174, 697 | "max": 1.1776397577400002, 698 | "times": [ 699 | 1.1605227147400001, 700 | 1.1333543727400002, 701 | 1.10684256574, 702 | 1.08005490174, 703 | 1.1776397577400002 704 | ], 705 | "exit_codes": [ 706 | 0, 707 | 0, 708 | 0, 709 | 0, 710 | 0 711 | ] 712 | } 713 | ] 714 | }, 715 | "size": 4446688 716 | }, 717 | "examples/pom-app/Cargo.toml": { 718 | "name": "pom", 719 | "manifest_path": "examples/pom-app/Cargo.toml", 720 | "crate": "pom", 721 | "version": "v3.2.0", 722 | "build": { 723 | "results": [ 724 | { 725 | "command": "cargo build -j 8 --package pom-app", 726 | "mean": 1.17102681318, 727 | "stddev": 0.012937062919207227, 728 | "median": 1.17500261518, 729 | "user": 1.86460048, 730 | "system": 0.27456344, 731 | "min": 1.1568075521799999, 732 | "max": 1.18540887618, 733 | "times": [ 734 | 1.15809316018, 735 | 1.17982186218, 736 | 1.18540887618, 737 | 1.17500261518, 738 | 1.1568075521799999 739 | ], 740 | "exit_codes": [ 741 | 0, 742 | 0, 743 | 0, 744 | 0, 745 | 0 746 | ] 747 | } 748 | ] 749 | }, 750 | "run": { 751 | "results": [ 752 | { 753 | "command": "target/release/pom-app third_party/nativejson-benchmark/data/canada.json", 754 | "mean": 2.14320024194, 755 | "stddev": 0.04284806353505112, 756 | "median": 2.13728204654, 757 | "user": 2.0889882199999996, 758 | "system": 0.05367553999999999, 759 | "min": 2.1085660345400004, 760 | "max": 2.21504690154, 761 | "times": [ 762 | 2.11242590154, 763 | 2.14268032554, 764 | 2.21504690154, 765 | 2.1085660345400004, 766 | 2.13728204654 767 | ], 768 | "exit_codes": [ 769 | 0, 770 | 0, 771 | 0, 772 | 0, 773 | 0 774 | ] 775 | } 776 | ] 777 | }, 778 | "size": 4516072 779 | }, 780 | "examples/winnow-app/Cargo.toml": { 781 | "name": "winnow", 782 | "manifest_path": "examples/winnow-app/Cargo.toml", 783 | "crate": "winnow", 784 | "version": "v0.4.0", 785 | "build": { 786 | "results": [ 787 | { 788 | "command": "cargo build -j 8 --package winnow-app", 789 | "mean": 2.2363113001, 790 | "stddev": 0.030415015085390396, 791 | "median": 2.2475373565, 792 | "user": 2.7787813800000003, 793 | "system": 0.29403672, 794 | "min": 2.1867445485, 795 | "max": 2.2639143625, 796 | "times": [ 797 | 2.2294770855, 798 | 2.1867445485, 799 | 2.2538831475, 800 | 2.2639143625, 801 | 2.2475373565 802 | ], 803 | "exit_codes": [ 804 | 0, 805 | 0, 806 | 0, 807 | 0, 808 | 0 809 | ] 810 | } 811 | ] 812 | }, 813 | "run": { 814 | "results": [ 815 | { 816 | "command": "target/release/winnow-app third_party/nativejson-benchmark/data/canada.json", 817 | "mean": 0.9888506441, 818 | "stddev": 0.04442709119907814, 819 | "median": 0.9837690201, 820 | "user": 0.93516116, 821 | "system": 0.05295166, 822 | "min": 0.9418596051000001, 823 | "max": 1.0618351921, 824 | "times": [ 825 | 0.9707891981000001, 826 | 0.9860002051000001, 827 | 0.9837690201, 828 | 1.0618351921, 829 | 0.9418596051000001 830 | ], 831 | "exit_codes": [ 832 | 0, 833 | 0, 834 | 0, 835 | 0, 836 | 0 837 | ] 838 | } 839 | ] 840 | }, 841 | "size": 4437744 842 | }, 843 | "examples/yap-app/Cargo.toml": { 844 | "name": "yap", 845 | "manifest_path": "examples/yap-app/Cargo.toml", 846 | "crate": "yap", 847 | "version": "v0.10.0", 848 | "build": { 849 | "results": [ 850 | { 851 | "command": "cargo build -j 8 --package yap-app", 852 | "mean": 0.8371240685, 853 | "stddev": 0.02464052521204184, 854 | "median": 0.8492245831, 855 | "user": 1.1050418999999998, 856 | "system": 0.18283754000000002, 857 | "min": 0.7982224431, 858 | "max": 0.8593426981000001, 859 | "times": [ 860 | 0.8279110501, 861 | 0.8492245831, 862 | 0.8593426981000001, 863 | 0.8509195681, 864 | 0.7982224431 865 | ], 866 | "exit_codes": [ 867 | 0, 868 | 0, 869 | 0, 870 | 0, 871 | 0 872 | ] 873 | } 874 | ] 875 | }, 876 | "run": { 877 | "results": [ 878 | { 879 | "command": "target/release/yap-app third_party/nativejson-benchmark/data/canada.json", 880 | "mean": 0.9717407231399999, 881 | "stddev": 0.018986260793857825, 882 | "median": 0.97739544194, 883 | "user": 0.91613052, 884 | "system": 0.055273779999999995, 885 | "min": 0.9391895449400001, 886 | "max": 0.98861600094, 887 | "times": [ 888 | 0.9795235249400001, 889 | 0.97739544194, 890 | 0.98861600094, 891 | 0.97397910294, 892 | 0.9391895449400001 893 | ], 894 | "exit_codes": [ 895 | 0, 896 | 0, 897 | 0, 898 | 0, 899 | 0 900 | ] 901 | } 902 | ] 903 | }, 904 | "size": 4386400 905 | } 906 | } 907 | } -------------------------------------------------------------------------------- /examples/parol-app/grammar_trait.rs: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------- 2 | // This file was generated by parol. 3 | // It is not intended for manual editing and changes will be 4 | // lost after next build. 5 | // --------------------------------------------------------- 6 | 7 | // Disable clippy warnings that can result in the way how parol generates code. 8 | #![allow(clippy::enum_variant_names)] 9 | #![allow(clippy::large_enum_variant)] 10 | #![allow(clippy::upper_case_acronyms)] 11 | 12 | use parol_runtime::log::trace; 13 | #[allow(unused_imports)] 14 | use parol_runtime::parol_macros::{pop_and_reverse_item, pop_item}; 15 | use parol_runtime::parser::{ParseTreeType, UserActionsTrait}; 16 | use parol_runtime::{ParserError, Result, Token}; 17 | 18 | /// Semantic actions trait generated for the user grammar 19 | /// All functions have default implementations. 20 | pub trait GrammarTrait<'t> { 21 | /// Semantic action for non-terminal 'Json' 22 | fn json(&mut self, _arg: &Json<'t>) -> Result<()> { 23 | Ok(()) 24 | } 25 | 26 | /// Semantic action for non-terminal 'Object' 27 | fn object(&mut self, _arg: &Object<'t>) -> Result<()> { 28 | Ok(()) 29 | } 30 | 31 | /// Semantic action for non-terminal 'Pair' 32 | fn pair(&mut self, _arg: &Pair<'t>) -> Result<()> { 33 | Ok(()) 34 | } 35 | 36 | /// Semantic action for non-terminal 'Array' 37 | fn array(&mut self, _arg: &Array<'t>) -> Result<()> { 38 | Ok(()) 39 | } 40 | 41 | /// Semantic action for non-terminal 'Value' 42 | fn value(&mut self, _arg: &Value<'t>) -> Result<()> { 43 | Ok(()) 44 | } 45 | 46 | /// Semantic action for non-terminal 'String' 47 | fn string(&mut self, _arg: &String<'t>) -> Result<()> { 48 | Ok(()) 49 | } 50 | 51 | /// Semantic action for non-terminal 'Number' 52 | fn number(&mut self, _arg: &Number<'t>) -> Result<()> { 53 | Ok(()) 54 | } 55 | 56 | /// This method provides skipped language comments. 57 | /// If you need comments please provide your own implementation of this method. 58 | fn on_comment(&mut self, _token: Token<'t>) {} 59 | } 60 | 61 | // ------------------------------------------------------------------------------------------------- 62 | // 63 | // Output Types of productions deduced from the structure of the transformed grammar 64 | // 65 | 66 | /// 67 | /// Type derived for production 2 68 | /// 69 | /// `ObjectSuffix: Pair ObjectList /* Vec */ '}'^ /* Clipped */;` 70 | /// 71 | #[allow(dead_code)] 72 | #[derive(Debug, Clone)] 73 | pub struct ObjectSuffixPairObjectListRBrace<'t> { 74 | pub pair: Box>, 75 | pub object_list: Vec>, 76 | } 77 | 78 | /// 79 | /// Type derived for production 3 80 | /// 81 | /// `ObjectSuffix: '}'^ /* Clipped */;` 82 | /// 83 | #[allow(dead_code)] 84 | #[derive(Debug, Clone)] 85 | pub struct ObjectSuffixRBrace {} 86 | 87 | /// 88 | /// Type derived for production 8 89 | /// 90 | /// `ArraySuffix: Value ArrayList /* Vec */ ']'^ /* Clipped */;` 91 | /// 92 | #[allow(dead_code)] 93 | #[derive(Debug, Clone)] 94 | pub struct ArraySuffixValueArrayListRBracket<'t> { 95 | pub value: Box>, 96 | pub array_list: Vec>, 97 | } 98 | 99 | /// 100 | /// Type derived for production 9 101 | /// 102 | /// `ArraySuffix: ']'^ /* Clipped */;` 103 | /// 104 | #[allow(dead_code)] 105 | #[derive(Debug, Clone)] 106 | pub struct ArraySuffixRBracket {} 107 | 108 | /// 109 | /// Type derived for production 12 110 | /// 111 | /// `Value: String;` 112 | /// 113 | #[allow(dead_code)] 114 | #[derive(Debug, Clone)] 115 | pub struct ValueString<'t> { 116 | pub string: String<'t>, 117 | } 118 | 119 | /// 120 | /// Type derived for production 13 121 | /// 122 | /// `Value: Number;` 123 | /// 124 | #[allow(dead_code)] 125 | #[derive(Debug, Clone)] 126 | pub struct ValueNumber<'t> { 127 | pub number: Number<'t>, 128 | } 129 | 130 | /// 131 | /// Type derived for production 14 132 | /// 133 | /// `Value: Object;` 134 | /// 135 | #[allow(dead_code)] 136 | #[derive(Debug, Clone)] 137 | pub struct ValueObject<'t> { 138 | pub object: Object<'t>, 139 | } 140 | 141 | /// 142 | /// Type derived for production 15 143 | /// 144 | /// `Value: Array;` 145 | /// 146 | #[allow(dead_code)] 147 | #[derive(Debug, Clone)] 148 | pub struct ValueArray<'t> { 149 | pub array: Array<'t>, 150 | } 151 | 152 | /// 153 | /// Type derived for production 16 154 | /// 155 | /// `Value: 'true'^ /* Clipped */;` 156 | /// 157 | #[allow(dead_code)] 158 | #[derive(Debug, Clone)] 159 | pub struct ValueTrue {} 160 | 161 | /// 162 | /// Type derived for production 17 163 | /// 164 | /// `Value: 'false'^ /* Clipped */;` 165 | /// 166 | #[allow(dead_code)] 167 | #[derive(Debug, Clone)] 168 | pub struct ValueFalse {} 169 | 170 | /// 171 | /// Type derived for production 18 172 | /// 173 | /// `Value: 'null'^ /* Clipped */;` 174 | /// 175 | #[allow(dead_code)] 176 | #[derive(Debug, Clone)] 177 | pub struct ValueNull {} 178 | 179 | // ------------------------------------------------------------------------------------------------- 180 | // 181 | // Types of non-terminals deduced from the structure of the transformed grammar 182 | // 183 | 184 | /// 185 | /// Type derived for non-terminal Array 186 | /// 187 | #[allow(dead_code)] 188 | #[derive(Debug, Clone)] 189 | pub struct Array<'t> { 190 | pub array_suffix: ArraySuffix<'t>, 191 | } 192 | 193 | /// 194 | /// Type derived for non-terminal ArrayList 195 | /// 196 | #[allow(dead_code)] 197 | #[derive(Debug, Clone)] 198 | pub struct ArrayList<'t> { 199 | pub value: Value<'t>, 200 | } 201 | 202 | /// 203 | /// Type derived for non-terminal ArraySuffix 204 | /// 205 | #[allow(dead_code)] 206 | #[derive(Debug, Clone)] 207 | pub enum ArraySuffix<'t> { 208 | ValueArrayListRBracket(ArraySuffixValueArrayListRBracket<'t>), 209 | RBracket(ArraySuffixRBracket), 210 | } 211 | 212 | /// 213 | /// Type derived for non-terminal Json 214 | /// 215 | #[allow(dead_code)] 216 | #[derive(Debug, Clone)] 217 | pub struct Json<'t> { 218 | pub value: Value<'t>, 219 | } 220 | 221 | /// 222 | /// Type derived for non-terminal Number 223 | /// 224 | #[allow(dead_code)] 225 | #[derive(Debug, Clone)] 226 | pub struct Number<'t> { 227 | pub number: Token<'t>, /* -?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][-+]?(0|[1-9][0-9]*)?)? */ 228 | } 229 | 230 | /// 231 | /// Type derived for non-terminal Object 232 | /// 233 | #[allow(dead_code)] 234 | #[derive(Debug, Clone)] 235 | pub struct Object<'t> { 236 | pub object_suffix: ObjectSuffix<'t>, 237 | } 238 | 239 | /// 240 | /// Type derived for non-terminal ObjectList 241 | /// 242 | #[allow(dead_code)] 243 | #[derive(Debug, Clone)] 244 | pub struct ObjectList<'t> { 245 | pub pair: Pair<'t>, 246 | } 247 | 248 | /// 249 | /// Type derived for non-terminal ObjectSuffix 250 | /// 251 | #[allow(dead_code)] 252 | #[derive(Debug, Clone)] 253 | pub enum ObjectSuffix<'t> { 254 | PairObjectListRBrace(ObjectSuffixPairObjectListRBrace<'t>), 255 | RBrace(ObjectSuffixRBrace), 256 | } 257 | 258 | /// 259 | /// Type derived for non-terminal Pair 260 | /// 261 | #[allow(dead_code)] 262 | #[derive(Debug, Clone)] 263 | pub struct Pair<'t> { 264 | pub string: String<'t>, 265 | pub value: Value<'t>, 266 | } 267 | 268 | /// 269 | /// Type derived for non-terminal String 270 | /// 271 | #[allow(dead_code)] 272 | #[derive(Debug, Clone)] 273 | pub struct String<'t> { 274 | pub string: Token<'t>, /* "(\\.|[^"])*" */ 275 | } 276 | 277 | /// 278 | /// Type derived for non-terminal Value 279 | /// 280 | #[allow(dead_code)] 281 | #[derive(Debug, Clone)] 282 | pub enum Value<'t> { 283 | String(ValueString<'t>), 284 | Number(ValueNumber<'t>), 285 | Object(ValueObject<'t>), 286 | Array(ValueArray<'t>), 287 | True(ValueTrue), 288 | False(ValueFalse), 289 | Null(ValueNull), 290 | } 291 | 292 | // ------------------------------------------------------------------------------------------------- 293 | 294 | /// 295 | /// Deduced ASTType of expanded grammar 296 | /// 297 | #[allow(dead_code)] 298 | #[derive(Debug, Clone)] 299 | pub enum ASTType<'t> { 300 | Array(Array<'t>), 301 | ArrayList(Vec>), 302 | ArraySuffix(ArraySuffix<'t>), 303 | Json(Json<'t>), 304 | Number(Number<'t>), 305 | Object(Object<'t>), 306 | ObjectList(Vec>), 307 | ObjectSuffix(ObjectSuffix<'t>), 308 | Pair(Pair<'t>), 309 | String(String<'t>), 310 | Value(Value<'t>), 311 | } 312 | 313 | // ------------------------------------------------------------------------------------------------- 314 | 315 | /// Auto-implemented adapter grammar 316 | /// 317 | /// The lifetime parameter `'t` refers to the lifetime of the scanned text. 318 | /// The lifetime parameter `'u` refers to the lifetime of user grammar object. 319 | /// 320 | #[allow(dead_code)] 321 | pub struct GrammarAuto<'t, 'u> 322 | where 323 | 't: 'u, 324 | { 325 | // Mutable reference of the actual user grammar to be able to call the semantic actions on it 326 | user_grammar: &'u mut dyn GrammarTrait<'t>, 327 | // Stack to construct the AST on it 328 | item_stack: Vec>, 329 | } 330 | 331 | /// 332 | /// The `GrammarAuto` impl is automatically generated for the 333 | /// given grammar. 334 | /// 335 | impl<'t, 'u> GrammarAuto<'t, 'u> { 336 | pub fn new(user_grammar: &'u mut dyn GrammarTrait<'t>) -> Self { 337 | Self { 338 | user_grammar, 339 | item_stack: Vec::new(), 340 | } 341 | } 342 | 343 | #[allow(dead_code)] 344 | fn push(&mut self, item: ASTType<'t>, context: &str) { 345 | trace!("push {context}: {item:?}"); 346 | self.item_stack.push(item) 347 | } 348 | 349 | #[allow(dead_code)] 350 | fn pop(&mut self, context: &str) -> Option> { 351 | let item = self.item_stack.pop(); 352 | if let Some(ref item) = item { 353 | trace!("pop {context}: {item:?}"); 354 | } 355 | item 356 | } 357 | 358 | #[allow(dead_code)] 359 | // Use this function for debugging purposes: 360 | // trace!("{}", self.trace_item_stack(context)); 361 | fn trace_item_stack(&self, context: &str) -> std::string::String { 362 | format!( 363 | "Item stack at {}:\n{}", 364 | context, 365 | self.item_stack 366 | .iter() 367 | .rev() 368 | .map(|s| format!(" {s:?}")) 369 | .collect::>() 370 | .join("\n") 371 | ) 372 | } 373 | 374 | /// Semantic action for production 0: 375 | /// 376 | /// `Json: Value;` 377 | /// 378 | #[parol_runtime::function_name::named] 379 | fn json(&mut self, _value: &ParseTreeType<'t>) -> Result<()> { 380 | let context = function_name!(); 381 | trace!("{}", self.trace_item_stack(context)); 382 | let value = pop_item!(self, value, Value, context); 383 | let json_built = Json { value }; 384 | // Calling user action here 385 | self.user_grammar.json(&json_built)?; 386 | self.push(ASTType::Json(json_built), context); 387 | Ok(()) 388 | } 389 | 390 | /// Semantic action for production 1: 391 | /// 392 | /// `Object: '{'^ /* Clipped */ ObjectSuffix;` 393 | /// 394 | #[parol_runtime::function_name::named] 395 | fn object( 396 | &mut self, 397 | _l_brace: &ParseTreeType<'t>, 398 | _object_suffix: &ParseTreeType<'t>, 399 | ) -> Result<()> { 400 | let context = function_name!(); 401 | trace!("{}", self.trace_item_stack(context)); 402 | let object_suffix = pop_item!(self, object_suffix, ObjectSuffix, context); 403 | let object_built = Object { object_suffix }; 404 | // Calling user action here 405 | self.user_grammar.object(&object_built)?; 406 | self.push(ASTType::Object(object_built), context); 407 | Ok(()) 408 | } 409 | 410 | /// Semantic action for production 2: 411 | /// 412 | /// `ObjectSuffix: Pair ObjectList /* Vec */ '}'^ /* Clipped */;` 413 | /// 414 | #[parol_runtime::function_name::named] 415 | fn object_suffix_0( 416 | &mut self, 417 | _pair: &ParseTreeType<'t>, 418 | _object_list: &ParseTreeType<'t>, 419 | _r_brace: &ParseTreeType<'t>, 420 | ) -> Result<()> { 421 | let context = function_name!(); 422 | trace!("{}", self.trace_item_stack(context)); 423 | let object_list = pop_and_reverse_item!(self, object_list, ObjectList, context); 424 | let pair = pop_item!(self, pair, Pair, context); 425 | let object_suffix_0_built = ObjectSuffixPairObjectListRBrace { 426 | pair: Box::new(pair), 427 | object_list, 428 | }; 429 | let object_suffix_0_built = ObjectSuffix::PairObjectListRBrace(object_suffix_0_built); 430 | self.push(ASTType::ObjectSuffix(object_suffix_0_built), context); 431 | Ok(()) 432 | } 433 | 434 | /// Semantic action for production 3: 435 | /// 436 | /// `ObjectSuffix: '}'^ /* Clipped */;` 437 | /// 438 | #[parol_runtime::function_name::named] 439 | fn object_suffix_1(&mut self, _r_brace: &ParseTreeType<'t>) -> Result<()> { 440 | let context = function_name!(); 441 | trace!("{}", self.trace_item_stack(context)); 442 | let object_suffix_1_built = ObjectSuffixRBrace {}; 443 | let object_suffix_1_built = ObjectSuffix::RBrace(object_suffix_1_built); 444 | self.push(ASTType::ObjectSuffix(object_suffix_1_built), context); 445 | Ok(()) 446 | } 447 | 448 | /// Semantic action for production 4: 449 | /// 450 | /// `ObjectList /* Vec::Push */: ','^ /* Clipped */ Pair ObjectList;` 451 | /// 452 | #[parol_runtime::function_name::named] 453 | fn object_list_0( 454 | &mut self, 455 | _comma: &ParseTreeType<'t>, 456 | _pair: &ParseTreeType<'t>, 457 | _object_list: &ParseTreeType<'t>, 458 | ) -> Result<()> { 459 | let context = function_name!(); 460 | trace!("{}", self.trace_item_stack(context)); 461 | let mut object_list = pop_item!(self, object_list, ObjectList, context); 462 | let pair = pop_item!(self, pair, Pair, context); 463 | let object_list_0_built = ObjectList { pair }; 464 | // Add an element to the vector 465 | object_list.push(object_list_0_built); 466 | self.push(ASTType::ObjectList(object_list), context); 467 | Ok(()) 468 | } 469 | 470 | /// Semantic action for production 5: 471 | /// 472 | /// `ObjectList /* Vec::New */: ;` 473 | /// 474 | #[parol_runtime::function_name::named] 475 | fn object_list_1(&mut self) -> Result<()> { 476 | let context = function_name!(); 477 | trace!("{}", self.trace_item_stack(context)); 478 | let object_list_1_built = Vec::new(); 479 | self.push(ASTType::ObjectList(object_list_1_built), context); 480 | Ok(()) 481 | } 482 | 483 | /// Semantic action for production 6: 484 | /// 485 | /// `Pair: String ':'^ /* Clipped */ Value;` 486 | /// 487 | #[parol_runtime::function_name::named] 488 | fn pair( 489 | &mut self, 490 | _string: &ParseTreeType<'t>, 491 | _colon: &ParseTreeType<'t>, 492 | _value: &ParseTreeType<'t>, 493 | ) -> Result<()> { 494 | let context = function_name!(); 495 | trace!("{}", self.trace_item_stack(context)); 496 | let value = pop_item!(self, value, Value, context); 497 | let string = pop_item!(self, string, String, context); 498 | let pair_built = Pair { string, value }; 499 | // Calling user action here 500 | self.user_grammar.pair(&pair_built)?; 501 | self.push(ASTType::Pair(pair_built), context); 502 | Ok(()) 503 | } 504 | 505 | /// Semantic action for production 7: 506 | /// 507 | /// `Array: '['^ /* Clipped */ ArraySuffix;` 508 | /// 509 | #[parol_runtime::function_name::named] 510 | fn array( 511 | &mut self, 512 | _l_bracket: &ParseTreeType<'t>, 513 | _array_suffix: &ParseTreeType<'t>, 514 | ) -> Result<()> { 515 | let context = function_name!(); 516 | trace!("{}", self.trace_item_stack(context)); 517 | let array_suffix = pop_item!(self, array_suffix, ArraySuffix, context); 518 | let array_built = Array { array_suffix }; 519 | // Calling user action here 520 | self.user_grammar.array(&array_built)?; 521 | self.push(ASTType::Array(array_built), context); 522 | Ok(()) 523 | } 524 | 525 | /// Semantic action for production 8: 526 | /// 527 | /// `ArraySuffix: Value ArrayList /* Vec */ ']'^ /* Clipped */;` 528 | /// 529 | #[parol_runtime::function_name::named] 530 | fn array_suffix_0( 531 | &mut self, 532 | _value: &ParseTreeType<'t>, 533 | _array_list: &ParseTreeType<'t>, 534 | _r_bracket: &ParseTreeType<'t>, 535 | ) -> Result<()> { 536 | let context = function_name!(); 537 | trace!("{}", self.trace_item_stack(context)); 538 | let array_list = pop_and_reverse_item!(self, array_list, ArrayList, context); 539 | let value = pop_item!(self, value, Value, context); 540 | let array_suffix_0_built = ArraySuffixValueArrayListRBracket { 541 | value: Box::new(value), 542 | array_list, 543 | }; 544 | let array_suffix_0_built = ArraySuffix::ValueArrayListRBracket(array_suffix_0_built); 545 | self.push(ASTType::ArraySuffix(array_suffix_0_built), context); 546 | Ok(()) 547 | } 548 | 549 | /// Semantic action for production 9: 550 | /// 551 | /// `ArraySuffix: ']'^ /* Clipped */;` 552 | /// 553 | #[parol_runtime::function_name::named] 554 | fn array_suffix_1(&mut self, _r_bracket: &ParseTreeType<'t>) -> Result<()> { 555 | let context = function_name!(); 556 | trace!("{}", self.trace_item_stack(context)); 557 | let array_suffix_1_built = ArraySuffixRBracket {}; 558 | let array_suffix_1_built = ArraySuffix::RBracket(array_suffix_1_built); 559 | self.push(ASTType::ArraySuffix(array_suffix_1_built), context); 560 | Ok(()) 561 | } 562 | 563 | /// Semantic action for production 10: 564 | /// 565 | /// `ArrayList /* Vec::Push */: ','^ /* Clipped */ Value ArrayList;` 566 | /// 567 | #[parol_runtime::function_name::named] 568 | fn array_list_0( 569 | &mut self, 570 | _comma: &ParseTreeType<'t>, 571 | _value: &ParseTreeType<'t>, 572 | _array_list: &ParseTreeType<'t>, 573 | ) -> Result<()> { 574 | let context = function_name!(); 575 | trace!("{}", self.trace_item_stack(context)); 576 | let mut array_list = pop_item!(self, array_list, ArrayList, context); 577 | let value = pop_item!(self, value, Value, context); 578 | let array_list_0_built = ArrayList { value }; 579 | // Add an element to the vector 580 | array_list.push(array_list_0_built); 581 | self.push(ASTType::ArrayList(array_list), context); 582 | Ok(()) 583 | } 584 | 585 | /// Semantic action for production 11: 586 | /// 587 | /// `ArrayList /* Vec::New */: ;` 588 | /// 589 | #[parol_runtime::function_name::named] 590 | fn array_list_1(&mut self) -> Result<()> { 591 | let context = function_name!(); 592 | trace!("{}", self.trace_item_stack(context)); 593 | let array_list_1_built = Vec::new(); 594 | self.push(ASTType::ArrayList(array_list_1_built), context); 595 | Ok(()) 596 | } 597 | 598 | /// Semantic action for production 12: 599 | /// 600 | /// `Value: String;` 601 | /// 602 | #[parol_runtime::function_name::named] 603 | fn value_0(&mut self, _string: &ParseTreeType<'t>) -> Result<()> { 604 | let context = function_name!(); 605 | trace!("{}", self.trace_item_stack(context)); 606 | let string = pop_item!(self, string, String, context); 607 | let value_0_built = ValueString { string }; 608 | let value_0_built = Value::String(value_0_built); 609 | // Calling user action here 610 | self.user_grammar.value(&value_0_built)?; 611 | self.push(ASTType::Value(value_0_built), context); 612 | Ok(()) 613 | } 614 | 615 | /// Semantic action for production 13: 616 | /// 617 | /// `Value: Number;` 618 | /// 619 | #[parol_runtime::function_name::named] 620 | fn value_1(&mut self, _number: &ParseTreeType<'t>) -> Result<()> { 621 | let context = function_name!(); 622 | trace!("{}", self.trace_item_stack(context)); 623 | let number = pop_item!(self, number, Number, context); 624 | let value_1_built = ValueNumber { number }; 625 | let value_1_built = Value::Number(value_1_built); 626 | // Calling user action here 627 | self.user_grammar.value(&value_1_built)?; 628 | self.push(ASTType::Value(value_1_built), context); 629 | Ok(()) 630 | } 631 | 632 | /// Semantic action for production 14: 633 | /// 634 | /// `Value: Object;` 635 | /// 636 | #[parol_runtime::function_name::named] 637 | fn value_2(&mut self, _object: &ParseTreeType<'t>) -> Result<()> { 638 | let context = function_name!(); 639 | trace!("{}", self.trace_item_stack(context)); 640 | let object = pop_item!(self, object, Object, context); 641 | let value_2_built = ValueObject { object }; 642 | let value_2_built = Value::Object(value_2_built); 643 | // Calling user action here 644 | self.user_grammar.value(&value_2_built)?; 645 | self.push(ASTType::Value(value_2_built), context); 646 | Ok(()) 647 | } 648 | 649 | /// Semantic action for production 15: 650 | /// 651 | /// `Value: Array;` 652 | /// 653 | #[parol_runtime::function_name::named] 654 | fn value_3(&mut self, _array: &ParseTreeType<'t>) -> Result<()> { 655 | let context = function_name!(); 656 | trace!("{}", self.trace_item_stack(context)); 657 | let array = pop_item!(self, array, Array, context); 658 | let value_3_built = ValueArray { array }; 659 | let value_3_built = Value::Array(value_3_built); 660 | // Calling user action here 661 | self.user_grammar.value(&value_3_built)?; 662 | self.push(ASTType::Value(value_3_built), context); 663 | Ok(()) 664 | } 665 | 666 | /// Semantic action for production 16: 667 | /// 668 | /// `Value: 'true'^ /* Clipped */;` 669 | /// 670 | #[parol_runtime::function_name::named] 671 | fn value_4(&mut self, _true: &ParseTreeType<'t>) -> Result<()> { 672 | let context = function_name!(); 673 | trace!("{}", self.trace_item_stack(context)); 674 | let value_4_built = ValueTrue {}; 675 | let value_4_built = Value::True(value_4_built); 676 | // Calling user action here 677 | self.user_grammar.value(&value_4_built)?; 678 | self.push(ASTType::Value(value_4_built), context); 679 | Ok(()) 680 | } 681 | 682 | /// Semantic action for production 17: 683 | /// 684 | /// `Value: 'false'^ /* Clipped */;` 685 | /// 686 | #[parol_runtime::function_name::named] 687 | fn value_5(&mut self, _false: &ParseTreeType<'t>) -> Result<()> { 688 | let context = function_name!(); 689 | trace!("{}", self.trace_item_stack(context)); 690 | let value_5_built = ValueFalse {}; 691 | let value_5_built = Value::False(value_5_built); 692 | // Calling user action here 693 | self.user_grammar.value(&value_5_built)?; 694 | self.push(ASTType::Value(value_5_built), context); 695 | Ok(()) 696 | } 697 | 698 | /// Semantic action for production 18: 699 | /// 700 | /// `Value: 'null'^ /* Clipped */;` 701 | /// 702 | #[parol_runtime::function_name::named] 703 | fn value_6(&mut self, _null: &ParseTreeType<'t>) -> Result<()> { 704 | let context = function_name!(); 705 | trace!("{}", self.trace_item_stack(context)); 706 | let value_6_built = ValueNull {}; 707 | let value_6_built = Value::Null(value_6_built); 708 | // Calling user action here 709 | self.user_grammar.value(&value_6_built)?; 710 | self.push(ASTType::Value(value_6_built), context); 711 | Ok(()) 712 | } 713 | 714 | /// Semantic action for production 19: 715 | /// 716 | /// `String: /"(\\.|[^"])*"/;` 717 | /// 718 | #[parol_runtime::function_name::named] 719 | fn string(&mut self, string: &ParseTreeType<'t>) -> Result<()> { 720 | let context = function_name!(); 721 | trace!("{}", self.trace_item_stack(context)); 722 | let string = string.token()?.clone(); 723 | let string_built = String { string }; 724 | // Calling user action here 725 | self.user_grammar.string(&string_built)?; 726 | self.push(ASTType::String(string_built), context); 727 | Ok(()) 728 | } 729 | 730 | /// Semantic action for production 20: 731 | /// 732 | /// `Number: /-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][-+]?(0|[1-9][0-9]*)?)?/;` 733 | /// 734 | #[parol_runtime::function_name::named] 735 | fn number(&mut self, number: &ParseTreeType<'t>) -> Result<()> { 736 | let context = function_name!(); 737 | trace!("{}", self.trace_item_stack(context)); 738 | let number = number.token()?.clone(); 739 | let number_built = Number { number }; 740 | // Calling user action here 741 | self.user_grammar.number(&number_built)?; 742 | self.push(ASTType::Number(number_built), context); 743 | Ok(()) 744 | } 745 | } 746 | 747 | impl<'t> UserActionsTrait<'t> for GrammarAuto<'t, '_> { 748 | /// 749 | /// This function is implemented automatically for the user's item Grammar. 750 | /// 751 | fn call_semantic_action_for_production_number( 752 | &mut self, 753 | prod_num: usize, 754 | children: &[ParseTreeType<'t>], 755 | ) -> Result<()> { 756 | match prod_num { 757 | 0 => self.json(&children[0]), 758 | 1 => self.object(&children[0], &children[1]), 759 | 2 => self.object_suffix_0(&children[0], &children[1], &children[2]), 760 | 3 => self.object_suffix_1(&children[0]), 761 | 4 => self.object_list_0(&children[0], &children[1], &children[2]), 762 | 5 => self.object_list_1(), 763 | 6 => self.pair(&children[0], &children[1], &children[2]), 764 | 7 => self.array(&children[0], &children[1]), 765 | 8 => self.array_suffix_0(&children[0], &children[1], &children[2]), 766 | 9 => self.array_suffix_1(&children[0]), 767 | 10 => self.array_list_0(&children[0], &children[1], &children[2]), 768 | 11 => self.array_list_1(), 769 | 12 => self.value_0(&children[0]), 770 | 13 => self.value_1(&children[0]), 771 | 14 => self.value_2(&children[0]), 772 | 15 => self.value_3(&children[0]), 773 | 16 => self.value_4(&children[0]), 774 | 17 => self.value_5(&children[0]), 775 | 18 => self.value_6(&children[0]), 776 | 19 => self.string(&children[0]), 777 | 20 => self.number(&children[0]), 778 | _ => Err(ParserError::InternalError(format!( 779 | "Unhandled production number: {prod_num}" 780 | )) 781 | .into()), 782 | } 783 | } 784 | 785 | fn on_comment(&mut self, token: Token<'t>) { 786 | self.user_grammar.on_comment(token) 787 | } 788 | } 789 | -------------------------------------------------------------------------------- /runs/2024-01-26-seon.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2024-01-26", 3 | "hostname": "seon", 4 | "os": "Linux", 5 | "os_ver": "5.4.0-124-generic", 6 | "arch": "x86_64", 7 | "cpus": 8, 8 | "libs": { 9 | "examples/chumsky-app/Cargo.toml": { 10 | "name": "chumsky", 11 | "manifest_path": "examples/chumsky-app/Cargo.toml", 12 | "crate": "ariadne", 13 | "version": "v0.3.0", 14 | "build": { 15 | "results": [ 16 | { 17 | "command": "cargo build -j 8 --package chumsky-app", 18 | "mean": 8.0917698457, 19 | "stddev": 0.161142368614438, 20 | "median": 8.0014606049, 21 | "user": 15.48826342, 22 | "system": 1.3211080999999998, 23 | "min": 7.9715503399, 24 | "max": 8.3389262729, 25 | "times": [ 26 | 8.0014606049, 27 | 7.9715503399, 28 | 8.1722028289, 29 | 7.9747091819, 30 | 8.3389262729 31 | ], 32 | "exit_codes": [ 33 | 0, 34 | 0, 35 | 0, 36 | 0, 37 | 0 38 | ] 39 | } 40 | ] 41 | }, 42 | "run": { 43 | "results": [ 44 | { 45 | "command": "target/release/chumsky-app third_party/nativejson-benchmark/data/canada.json", 46 | "mean": 1.4681769382399998, 47 | "stddev": 0.028436699728311633, 48 | "median": 1.46336358144, 49 | "user": 1.38987674, 50 | "system": 0.07831674, 51 | "min": 1.4467672464399999, 52 | "max": 1.51646577844, 53 | "times": [ 54 | 1.51646577844, 55 | 1.44771593044, 56 | 1.46336358144, 57 | 1.4467672464399999, 58 | 1.4665721544399999 59 | ], 60 | "exit_codes": [ 61 | 0, 62 | 0, 63 | 0, 64 | 0, 65 | 0 66 | ] 67 | } 68 | ] 69 | }, 70 | "size": 5129056 71 | }, 72 | "examples/combine-app/Cargo.toml": { 73 | "name": "combine", 74 | "manifest_path": "examples/combine-app/Cargo.toml", 75 | "crate": "combine", 76 | "version": "v3.8.1", 77 | "build": { 78 | "results": [ 79 | { 80 | "command": "cargo build -j 8 --package combine-app", 81 | "mean": 4.97195296846, 82 | "stddev": 0.060781214544008234, 83 | "median": 4.95233970966, 84 | "user": 7.78478584, 85 | "system": 0.5256114999999999, 86 | "min": 4.914723454660001, 87 | "max": 5.062198420660001, 88 | "times": [ 89 | 4.914723454660001, 90 | 5.00322181066, 91 | 5.062198420660001, 92 | 4.92728144666, 93 | 4.95233970966 94 | ], 95 | "exit_codes": [ 96 | 0, 97 | 0, 98 | 0, 99 | 0, 100 | 0 101 | ] 102 | } 103 | ] 104 | }, 105 | "run": { 106 | "results": [ 107 | { 108 | "command": "target/release/combine-app third_party/nativejson-benchmark/data/canada.json", 109 | "mean": 1.0557123035, 110 | "stddev": 0.014987480000614816, 111 | "median": 1.0491726905, 112 | "user": 0.99332482, 113 | "system": 0.0624135, 114 | "min": 1.0462031785, 115 | "max": 1.0822037925, 116 | "times": [ 117 | 1.0822037925, 118 | 1.0526103285000001, 119 | 1.0483715275, 120 | 1.0462031785, 121 | 1.0491726905 122 | ], 123 | "exit_codes": [ 124 | 0, 125 | 0, 126 | 0, 127 | 0, 128 | 0 129 | ] 130 | } 131 | ] 132 | }, 133 | "size": 4647048 134 | }, 135 | "examples/lalrpop-app/Cargo.toml": { 136 | "name": "lalrpop", 137 | "manifest_path": "examples/lalrpop-app/Cargo.toml", 138 | "crate": "lalrpop-util", 139 | "version": "v0.20.0", 140 | "build": { 141 | "results": [ 142 | { 143 | "command": "cargo build -j 8 --package lalrpop-app", 144 | "mean": 13.31118201486, 145 | "stddev": 0.05033157873771928, 146 | "median": 13.311053921460001, 147 | "user": 44.49745338, 148 | "system": 3.1118082199999995, 149 | "min": 13.240229170460001, 150 | "max": 13.38173185646, 151 | "times": [ 152 | 13.38173185646, 153 | 13.311053921460001, 154 | 13.303658867460001, 155 | 13.31923625846, 156 | 13.240229170460001 157 | ], 158 | "exit_codes": [ 159 | 0, 160 | 0, 161 | 0, 162 | 0, 163 | 0 164 | ] 165 | } 166 | ] 167 | }, 168 | "run": { 169 | "results": [ 170 | { 171 | "command": "target/release/lalrpop-app third_party/nativejson-benchmark/data/canada.json", 172 | "mean": 2.27980252654, 173 | "stddev": 0.05964592952841896, 174 | "median": 2.25084223714, 175 | "user": 2.2293604399999998, 176 | "system": 0.05037688, 177 | "min": 2.22284341714, 178 | "max": 2.34585263614, 179 | "times": [ 180 | 2.34259309714, 181 | 2.23688124514, 182 | 2.22284341714, 183 | 2.25084223714, 184 | 2.34585263614 185 | ], 186 | "exit_codes": [ 187 | 0, 188 | 0, 189 | 0, 190 | 0, 191 | 0 192 | ] 193 | } 194 | ] 195 | }, 196 | "size": 6114672 197 | }, 198 | "examples/logos-app/Cargo.toml": { 199 | "name": "logos", 200 | "manifest_path": "examples/logos-app/Cargo.toml", 201 | "crate": "ariadne", 202 | "version": "v0.4.0", 203 | "build": { 204 | "results": [ 205 | { 206 | "command": "cargo build -j 8 --package logos-app", 207 | "mean": 5.83359145312, 208 | "stddev": 0.07461425097232303, 209 | "median": 5.829562618120001, 210 | "user": 15.199506659999997, 211 | "system": 1.1083230800000001, 212 | "min": 5.756479183120001, 213 | "max": 5.92568291512, 214 | "times": [ 215 | 5.8904292731200005, 216 | 5.756479183120001, 217 | 5.92568291512, 218 | 5.829562618120001, 219 | 5.765803276120001 220 | ], 221 | "exit_codes": [ 222 | 0, 223 | 0, 224 | 0, 225 | 0, 226 | 0 227 | ] 228 | } 229 | ] 230 | }, 231 | "run": { 232 | "results": [ 233 | { 234 | "command": "target/release/logos-app third_party/nativejson-benchmark/data/canada.json", 235 | "mean": 0.97651713868, 236 | "stddev": 0.021281893775360845, 237 | "median": 0.9662504204800001, 238 | "user": 0.9130945999999998, 239 | "system": 0.0633426, 240 | "min": 0.9600463804800001, 241 | "max": 1.01264851748, 242 | "times": [ 243 | 0.9783541524800001, 244 | 1.01264851748, 245 | 0.9600463804800001, 246 | 0.9662504204800001, 247 | 0.9652862224800001 248 | ], 249 | "exit_codes": [ 250 | 0, 251 | 0, 252 | 0, 253 | 0, 254 | 0 255 | ] 256 | } 257 | ] 258 | }, 259 | "size": 4632672 260 | }, 261 | "examples/nom-app/Cargo.toml": { 262 | "name": "nom", 263 | "manifest_path": "examples/nom-app/Cargo.toml", 264 | "crate": "nom", 265 | "version": "v7.1.3", 266 | "build": { 267 | "results": [ 268 | { 269 | "command": "cargo build -j 8 --package nom-app", 270 | "mean": 2.47064659616, 271 | "stddev": 0.037597476217242395, 272 | "median": 2.47123265696, 273 | "user": 3.67014484, 274 | "system": 0.30593738, 275 | "min": 2.41137982996, 276 | "max": 2.51378535396, 277 | "times": [ 278 | 2.51378535396, 279 | 2.47123265696, 280 | 2.46948282496, 281 | 2.48735231496, 282 | 2.41137982996 283 | ], 284 | "exit_codes": [ 285 | 0, 286 | 0, 287 | 0, 288 | 0, 289 | 0 290 | ] 291 | } 292 | ] 293 | }, 294 | "run": { 295 | "results": [ 296 | { 297 | "command": "target/release/nom-app third_party/nativejson-benchmark/data/canada.json", 298 | "mean": 1.19559846862, 299 | "stddev": 0.024339887352568586, 300 | "median": 1.19951169002, 301 | "user": 1.14909764, 302 | "system": 0.04653348, 303 | "min": 1.1673271550200002, 304 | "max": 1.22330552302, 305 | "times": [ 306 | 1.1673271550200002, 307 | 1.19951169002, 308 | 1.17415214702, 309 | 1.22330552302, 310 | 1.21369582802 311 | ], 312 | "exit_codes": [ 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0 318 | ] 319 | } 320 | ] 321 | }, 322 | "size": 4560352 323 | }, 324 | "examples/null-app/Cargo.toml": { 325 | "name": "null", 326 | "manifest_path": "examples/null-app/Cargo.toml", 327 | "crate": null, 328 | "version": null, 329 | "build": { 330 | "results": [ 331 | { 332 | "command": "cargo build -j 8 --package null-app", 333 | "mean": 0.2611224241066667, 334 | "stddev": 0.011819698869930298, 335 | "median": 0.25833680544000004, 336 | "user": 0.2541420288888889, 337 | "system": 0.07544287777777776, 338 | "min": 0.24871747544000003, 339 | "max": 0.27996334044000004, 340 | "times": [ 341 | 0.24871747544000003, 342 | 0.25270991644, 343 | 0.27996334044000004, 344 | 0.24910241944000003, 345 | 0.27448322644, 346 | 0.27100574944000005, 347 | 0.26455099344000005, 348 | 0.25123189044000005, 349 | 0.25833680544000004 350 | ], 351 | "exit_codes": [ 352 | 0, 353 | 0, 354 | 0, 355 | 0, 356 | 0, 357 | 0, 358 | 0, 359 | 0, 360 | 0 361 | ] 362 | } 363 | ] 364 | }, 365 | "run": { 366 | "results": [ 367 | { 368 | "command": "target/release/null-app third_party/nativejson-benchmark/data/canada.json", 369 | "mean": 0.038977259808780515, 370 | "stddev": 0.006717429582954064, 371 | "median": 0.03489542226, 372 | "user": 0.03712742682926828, 373 | "system": 0.0018842097560975604, 374 | "min": 0.03341794076000001, 375 | "max": 0.05560157076, 376 | "times": [ 377 | 0.035826378760000006, 378 | 0.05560157076, 379 | 0.03616036076, 380 | 0.04773737076000001, 381 | 0.038835417760000004, 382 | 0.03600729376, 383 | 0.05172487776, 384 | 0.034225026760000006, 385 | 0.05061064776000001, 386 | 0.034730673760000004, 387 | 0.034691927760000005, 388 | 0.03502133476, 389 | 0.034847631760000004, 390 | 0.03473279976, 391 | 0.034385805760000006, 392 | 0.03497574576, 393 | 0.034797205760000005, 394 | 0.03551628376, 395 | 0.034825219760000005, 396 | 0.03452382276, 397 | 0.033831571760000005, 398 | 0.033755613760000004, 399 | 0.033956297760000004, 400 | 0.034999853760000005, 401 | 0.03469653176, 402 | 0.03489073276000001, 403 | 0.049768083760000005, 404 | 0.034571409760000005, 405 | 0.046400067760000004, 406 | 0.049737696760000005, 407 | 0.03470454676, 408 | 0.034247480760000004, 409 | 0.033786946760000006, 410 | 0.03513919276000001, 411 | 0.034742563760000005, 412 | 0.047823420760000006, 413 | 0.04883541676000001, 414 | 0.03499846176, 415 | 0.03483247976000001, 416 | 0.035337152760000004, 417 | 0.034768532760000005, 418 | 0.049947146760000005, 419 | 0.03480329076000001, 420 | 0.034737977760000004, 421 | 0.03459438176000001, 422 | 0.050047877760000005, 423 | 0.034706747760000003, 424 | 0.04784470276, 425 | 0.05012568776, 426 | 0.034720349760000005, 427 | 0.043068206760000005, 428 | 0.03561493276, 429 | 0.03441532676, 430 | 0.03478148876000001, 431 | 0.044711702760000006, 432 | 0.053325382760000005, 433 | 0.03354975576000001, 434 | 0.049129464760000005, 435 | 0.03341794076000001, 436 | 0.04832503776000001, 437 | 0.03403596276, 438 | 0.042815728760000005, 439 | 0.03444225976, 440 | 0.034663201760000005, 441 | 0.034454872760000005, 442 | 0.03482681976, 443 | 0.034685705760000005, 444 | 0.03450482776, 445 | 0.03521240576, 446 | 0.035989425760000006, 447 | 0.050869251760000005, 448 | 0.034900111760000005, 449 | 0.04571973176000001, 450 | 0.051892838760000004, 451 | 0.03479602876000001, 452 | 0.035018773760000003, 453 | 0.03468038776000001, 454 | 0.04992950476000001, 455 | 0.03453379476000001, 456 | 0.050262549760000004, 457 | 0.03559438176, 458 | 0.03483788476000001 459 | ], 460 | "exit_codes": [ 461 | 0, 462 | 0, 463 | 0, 464 | 0, 465 | 0, 466 | 0, 467 | 0, 468 | 0, 469 | 0, 470 | 0, 471 | 0, 472 | 0, 473 | 0, 474 | 0, 475 | 0, 476 | 0, 477 | 0, 478 | 0, 479 | 0, 480 | 0, 481 | 0, 482 | 0, 483 | 0, 484 | 0, 485 | 0, 486 | 0, 487 | 0, 488 | 0, 489 | 0, 490 | 0, 491 | 0, 492 | 0, 493 | 0, 494 | 0, 495 | 0, 496 | 0, 497 | 0, 498 | 0, 499 | 0, 500 | 0, 501 | 0, 502 | 0, 503 | 0, 504 | 0, 505 | 0, 506 | 0, 507 | 0, 508 | 0, 509 | 0, 510 | 0, 511 | 0, 512 | 0, 513 | 0, 514 | 0, 515 | 0, 516 | 0, 517 | 0, 518 | 0, 519 | 0, 520 | 0, 521 | 0, 522 | 0, 523 | 0, 524 | 0, 525 | 0, 526 | 0, 527 | 0, 528 | 0, 529 | 0, 530 | 0, 531 | 0, 532 | 0, 533 | 0, 534 | 0, 535 | 0, 536 | 0, 537 | 0, 538 | 0, 539 | 0, 540 | 0, 541 | 0, 542 | 0 543 | ] 544 | } 545 | ] 546 | }, 547 | "size": 4424936 548 | }, 549 | "examples/peg-app/Cargo.toml": { 550 | "name": "peg", 551 | "manifest_path": "examples/peg-app/Cargo.toml", 552 | "crate": "peg", 553 | "version": "v0.8.2", 554 | "build": { 555 | "results": [ 556 | { 557 | "command": "cargo build -j 8 --package peg-app", 558 | "mean": 2.20428010214, 559 | "stddev": 0.07261868057956396, 560 | "median": 2.23710811614, 561 | "user": 3.7408026799999994, 562 | "system": 0.3869578000000001, 563 | "min": 2.0823354631399997, 564 | "max": 2.26118203514, 565 | "times": [ 566 | 2.24674076314, 567 | 2.23710811614, 568 | 2.26118203514, 569 | 2.1940341331399997, 570 | 2.0823354631399997 571 | ], 572 | "exit_codes": [ 573 | 0, 574 | 0, 575 | 0, 576 | 0, 577 | 0 578 | ] 579 | } 580 | ] 581 | }, 582 | "run": { 583 | "results": [ 584 | { 585 | "command": "target/release/peg-app third_party/nativejson-benchmark/data/canada.json", 586 | "mean": 0.023805514839024385, 587 | "stddev": 0.0032404517268087934, 588 | "median": 0.022965131399999998, 589 | "user": 0.022191465853658544, 590 | "system": 0.0016335234146341463, 591 | "min": 0.022022988900000002, 592 | "max": 0.04082475790000001, 593 | "times": [ 594 | 0.035880585900000005, 595 | 0.036790916900000005, 596 | 0.0222030599, 597 | 0.0224898869, 598 | 0.0223073539, 599 | 0.0222938869, 600 | 0.0228557529, 601 | 0.0223159149, 602 | 0.0234795619, 603 | 0.0223978889, 604 | 0.0226554219, 605 | 0.0229483169, 606 | 0.0228198429, 607 | 0.0229969259, 608 | 0.0232906789, 609 | 0.0234504529, 610 | 0.0229172759, 611 | 0.0229074319, 612 | 0.0233235249, 613 | 0.0232968869, 614 | 0.0228949049, 615 | 0.023032844900000002, 616 | 0.0242954979, 617 | 0.0221789069, 618 | 0.0225364619, 619 | 0.022022988900000002, 620 | 0.0221024599, 621 | 0.0227331969, 622 | 0.022144152900000002, 623 | 0.0226529899, 624 | 0.0228437569, 625 | 0.0229852979, 626 | 0.0229650599, 627 | 0.0233529359, 628 | 0.0245754249, 629 | 0.0229126619, 630 | 0.0230719039, 631 | 0.0229182769, 632 | 0.0228880619, 633 | 0.0232856649, 634 | 0.0238205169, 635 | 0.0225722299, 636 | 0.0228570099, 637 | 0.0221732289, 638 | 0.0221473029, 639 | 0.0230783439, 640 | 0.0244355849, 641 | 0.023100182900000002, 642 | 0.0236752569, 643 | 0.0228220179, 644 | 0.0240508379, 645 | 0.0241302029, 646 | 0.0229652029, 647 | 0.0232008259, 648 | 0.0242467859, 649 | 0.0236452029, 650 | 0.0252975629, 651 | 0.0238771089, 652 | 0.0242445909, 653 | 0.0233053519, 654 | 0.0369045959, 655 | 0.0229572359, 656 | 0.0228365789, 657 | 0.0237953099, 658 | 0.0233969659, 659 | 0.023146685900000002, 660 | 0.0229046469, 661 | 0.024193901900000002, 662 | 0.022852067900000002, 663 | 0.0230347009, 664 | 0.0227785579, 665 | 0.0229408199, 666 | 0.0235190599, 667 | 0.0228758819, 668 | 0.04082475790000001, 669 | 0.0222300289, 670 | 0.0232149689, 671 | 0.0221829609, 672 | 0.0225577659, 673 | 0.0226863459, 674 | 0.0237631539, 675 | 0.0247948589 676 | ], 677 | "exit_codes": [ 678 | 0, 679 | 0, 680 | 0, 681 | 0, 682 | 0, 683 | 0, 684 | 0, 685 | 0, 686 | 0, 687 | 0, 688 | 0, 689 | 0, 690 | 0, 691 | 0, 692 | 0, 693 | 0, 694 | 0, 695 | 0, 696 | 0, 697 | 0, 698 | 0, 699 | 0, 700 | 0, 701 | 0, 702 | 0, 703 | 0, 704 | 0, 705 | 0, 706 | 0, 707 | 0, 708 | 0, 709 | 0, 710 | 0, 711 | 0, 712 | 0, 713 | 0, 714 | 0, 715 | 0, 716 | 0, 717 | 0, 718 | 0, 719 | 0, 720 | 0, 721 | 0, 722 | 0, 723 | 0, 724 | 0, 725 | 0, 726 | 0, 727 | 0, 728 | 0, 729 | 0, 730 | 0, 731 | 0, 732 | 0, 733 | 0, 734 | 0, 735 | 0, 736 | 0, 737 | 0, 738 | 0, 739 | 0, 740 | 0, 741 | 0, 742 | 0, 743 | 0, 744 | 0, 745 | 0, 746 | 0, 747 | 0, 748 | 0, 749 | 0, 750 | 0, 751 | 0, 752 | 0, 753 | 0, 754 | 0, 755 | 0, 756 | 0, 757 | 0, 758 | 0, 759 | 0 760 | ] 761 | } 762 | ] 763 | }, 764 | "size": 4446408 765 | }, 766 | "examples/pest-app/Cargo.toml": { 767 | "name": "pest", 768 | "manifest_path": "examples/pest-app/Cargo.toml", 769 | "crate": "pest", 770 | "version": "v2.7.6", 771 | "build": { 772 | "results": [ 773 | { 774 | "command": "cargo build -j 8 --package pest-app", 775 | "mean": 4.682842930620001, 776 | "stddev": 0.040534289366170685, 777 | "median": 4.6867370824200005, 778 | "user": 9.3244561, 779 | "system": 0.7890200199999999, 780 | "min": 4.63975085742, 781 | "max": 4.73568181342, 782 | "times": [ 783 | 4.6867370824200005, 784 | 4.7061475044200005, 785 | 4.63975085742, 786 | 4.6458973954200005, 787 | 4.73568181342 788 | ], 789 | "exit_codes": [ 790 | 0, 791 | 0, 792 | 0, 793 | 0, 794 | 0 795 | ] 796 | } 797 | ] 798 | }, 799 | "run": { 800 | "results": [ 801 | { 802 | "command": "target/release/pest-app third_party/nativejson-benchmark/data/canada.json", 803 | "mean": 0.9623206057400001, 804 | "stddev": 0.008615101552821159, 805 | "median": 0.9572640281400001, 806 | "user": 0.9157410799999999, 807 | "system": 0.04649436, 808 | "min": 0.95604973814, 809 | "max": 0.9755957701400001, 810 | "times": [ 811 | 0.9561573171400001, 812 | 0.9755957701400001, 813 | 0.9572640281400001, 814 | 0.95604973814, 815 | 0.9665361751400001 816 | ], 817 | "exit_codes": [ 818 | 0, 819 | 0, 820 | 0, 821 | 0, 822 | 0 823 | ] 824 | } 825 | ] 826 | }, 827 | "size": 4564888 828 | }, 829 | "examples/pom-app/Cargo.toml": { 830 | "name": "pom", 831 | "manifest_path": "examples/pom-app/Cargo.toml", 832 | "crate": "pom", 833 | "version": "v3.3.0", 834 | "build": { 835 | "results": [ 836 | { 837 | "command": "cargo build -j 8 --package pom-app", 838 | "mean": 2.13315805482, 839 | "stddev": 0.07275142902431052, 840 | "median": 2.1331498960200004, 841 | "user": 4.3714480799999995, 842 | "system": 0.36866293999999994, 843 | "min": 2.0649364460200004, 844 | "max": 2.24656099802, 845 | "times": [ 846 | 2.0649364460200004, 847 | 2.0742647300200003, 848 | 2.24656099802, 849 | 2.14687820402, 850 | 2.1331498960200004 851 | ], 852 | "exit_codes": [ 853 | 0, 854 | 0, 855 | 0, 856 | 0, 857 | 0 858 | ] 859 | } 860 | ] 861 | }, 862 | "run": { 863 | "results": [ 864 | { 865 | "command": "target/release/pom-app third_party/nativejson-benchmark/data/canada.json", 866 | "mean": 1.94798154848, 867 | "stddev": 0.042810400667262924, 868 | "median": 1.94342181768, 869 | "user": 1.8898234199999997, 870 | "system": 0.05673404, 871 | "min": 1.89504471668, 872 | "max": 2.0147967176800003, 873 | "times": [ 874 | 1.94358422368, 875 | 1.89504471668, 876 | 2.0147967176800003, 877 | 1.94306026668, 878 | 1.94342181768 879 | ], 880 | "exit_codes": [ 881 | 0, 882 | 0, 883 | 0, 884 | 0, 885 | 0 886 | ] 887 | } 888 | ] 889 | }, 890 | "size": 4605632 891 | }, 892 | "examples/winnow-app/Cargo.toml": { 893 | "name": "winnow", 894 | "manifest_path": "examples/winnow-app/Cargo.toml", 895 | "crate": "winnow", 896 | "version": "v0.5.35", 897 | "build": { 898 | "results": [ 899 | { 900 | "command": "cargo build -j 8 --package winnow-app", 901 | "mean": 2.0628146903799998, 902 | "stddev": 0.02502016552304547, 903 | "median": 2.0551109629799997, 904 | "user": 2.6884846, 905 | "system": 0.22573096, 906 | "min": 2.03798567598, 907 | "max": 2.10382390798, 908 | "times": [ 909 | 2.06601036498, 910 | 2.0551109629799997, 911 | 2.10382390798, 912 | 2.05114253998, 913 | 2.03798567598 914 | ], 915 | "exit_codes": [ 916 | 0, 917 | 0, 918 | 0, 919 | 0, 920 | 0 921 | ] 922 | } 923 | ] 924 | }, 925 | "run": { 926 | "results": [ 927 | { 928 | "command": "target/release/winnow-app third_party/nativejson-benchmark/data/canada.json", 929 | "mean": 1.02188729576, 930 | "stddev": 0.026512541262441718, 931 | "median": 1.01856192676, 932 | "user": 0.9742689800000001, 933 | "system": 0.04710104, 934 | "min": 0.98260179676, 935 | "max": 1.05441999276, 936 | "times": [ 937 | 1.01856192676, 938 | 0.98260179676, 939 | 1.01832144876, 940 | 1.05441999276, 941 | 1.03553131376 942 | ], 943 | "exit_codes": [ 944 | 0, 945 | 0, 946 | 0, 947 | 0, 948 | 0 949 | ] 950 | } 951 | ] 952 | }, 953 | "size": 4533432 954 | }, 955 | "examples/yap-app/Cargo.toml": { 956 | "name": "yap", 957 | "manifest_path": "examples/yap-app/Cargo.toml", 958 | "crate": "yap", 959 | "version": "v0.12.0", 960 | "build": { 961 | "results": [ 962 | { 963 | "command": "cargo build -j 8 --package yap-app", 964 | "mean": 0.6185163573400001, 965 | "stddev": 0.021319896865512188, 966 | "median": 0.61437242434, 967 | "user": 0.8576166799999999, 968 | "system": 0.13662898, 969 | "min": 0.60019130134, 970 | "max": 0.65505628434, 971 | "times": [ 972 | 0.60754323434, 973 | 0.61437242434, 974 | 0.61541854234, 975 | 0.65505628434, 976 | 0.60019130134 977 | ], 978 | "exit_codes": [ 979 | 0, 980 | 0, 981 | 0, 982 | 0, 983 | 0 984 | ] 985 | } 986 | ] 987 | }, 988 | "run": { 989 | "results": [ 990 | { 991 | "command": "target/release/yap-app third_party/nativejson-benchmark/data/canada.json", 992 | "mean": 0.9362099234800001, 993 | "stddev": 0.010534909972039236, 994 | "median": 0.9379767564800001, 995 | "user": 0.89194648, 996 | "system": 0.04397736, 997 | "min": 0.9195227854800001, 998 | "max": 0.9487983354800001, 999 | "times": [ 1000 | 0.9487983354800001, 1001 | 0.9366116514800001, 1002 | 0.9379767564800001, 1003 | 0.9195227854800001, 1004 | 0.9381400884800001 1005 | ], 1006 | "exit_codes": [ 1007 | 0, 1008 | 0, 1009 | 0, 1010 | 0, 1011 | 0 1012 | ] 1013 | } 1014 | ] 1015 | }, 1016 | "size": 4522024 1017 | } 1018 | } 1019 | } --------------------------------------------------------------------------------