diff --git a/Cargo.lock b/Cargo.lock index 259d1df..66236fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,49 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "hashbrown" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" +dependencies = [ + "ahash", +] + [[package]] name = "itoa" version = "0.4.6" @@ -20,10 +57,23 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "libc" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + [[package]] name = "peg" version = "0.8.1" dependencies = [ + "hashbrown", "peg-macros", "peg-runtime", "trybuild", @@ -33,6 +83,7 @@ dependencies = [ name = "peg-macros" version = "0.8.1" dependencies = [ + "cfg-if", "peg-runtime", "proc-macro2", "quote", @@ -146,6 +197,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index acdf042..ead9377 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ readme = "README.md" edition = "2018" [dependencies] +hashbrown = { version = "0.12.0", optional = true } peg-macros = { path = "./peg-macros", version = "= 0.8.1" } peg-runtime = { path = "./peg-runtime", version = "= 0.8.1" } @@ -26,4 +27,6 @@ path = "tests/trybuild.rs" harness = false [features] +default = ["std"] trace = ["peg-macros/trace"] +std = ["peg-macros/std", "peg-runtime/std"] \ No newline at end of file diff --git a/benches/expr.rs b/benches/expr.rs index 84b3ea5..ec9e76c 100644 --- a/benches/expr.rs +++ b/benches/expr.rs @@ -3,6 +3,8 @@ extern crate peg; extern crate test; +#[cfg(not(feature = "std"))] #[macro_use] extern crate alloc; + use test::Bencher; peg::parser!(grammar parser() for str { diff --git a/benches/json.rs b/benches/json.rs index 398d85d..b2d786d 100644 --- a/benches/json.rs +++ b/benches/json.rs @@ -3,6 +3,8 @@ extern crate peg; extern crate test; +#[cfg(not(feature = "std"))] #[macro_use] extern crate alloc; + use test::Bencher; peg::parser!(grammar parser() for str { diff --git a/peg-macros/Cargo.toml b/peg-macros/Cargo.toml index c4a9e9d..4cca56d 100644 --- a/peg-macros/Cargo.toml +++ b/peg-macros/Cargo.toml @@ -11,9 +11,12 @@ edition = "2018" quote = "1.0" proc-macro2 = "1.0.24" peg-runtime = { version = "= 0.8.1", path = "../peg-runtime" } +cfg-if = "1.0.0" [features] +std = ["peg-runtime/std"] trace = [] +hashbrown = [] [lib] proc-macro = true diff --git a/peg-macros/bin.rs b/peg-macros/bin.rs index a66f4c5..5f3773b 100644 --- a/peg-macros/bin.rs +++ b/peg-macros/bin.rs @@ -15,6 +15,10 @@ use std::process; // requires `::peg` paths. extern crate peg_runtime as peg; +#[cfg(not(feature = "std"))] +#[macro_use] +extern crate alloc; + mod analysis; mod ast; mod grammar; diff --git a/peg-macros/grammar.rs b/peg-macros/grammar.rs index a9a484b..3014270 100644 --- a/peg-macros/grammar.rs +++ b/peg-macros/grammar.rs @@ -6,13 +6,13 @@ pub mod peg { type PositionRepr = ::PositionRepr; #[allow(unused_parens)] struct ParseState<'input> { - _phantom: ::std::marker::PhantomData<(&'input ())>, + _phantom: ::core::marker::PhantomData<(&'input ())>, primary_cache: ::std::collections::HashMap>, } impl<'input> ParseState<'input> { fn new() -> ParseState<'input> { ParseState { - _phantom: ::std::marker::PhantomData, + _phantom: ::core::marker::PhantomData, primary_cache: ::std::collections::HashMap::new(), } } @@ -23,7 +23,7 @@ pub mod peg { use proc_macro2::{Delimiter, Group, Ident, Literal, Span, TokenStream}; pub fn peg_grammar<'input>( __input: &'input Input, - ) -> ::std::result::Result> { + ) -> ::core::result::Result> { #![allow(non_snake_case, unused)] let mut __err_state = ::peg::error::ErrorState::new(::peg::Parse::start(__input)); let mut __state = ParseState::new(); diff --git a/peg-macros/lib.rs b/peg-macros/lib.rs index 6393788..995b414 100644 --- a/peg-macros/lib.rs +++ b/peg-macros/lib.rs @@ -14,6 +14,8 @@ mod grammar; mod tokens; mod translate; +#[cfg(not(feature = "std"))] extern crate alloc; + /// The main macro for creating a PEG parser. /// /// For the grammar syntax, see the `peg` crate documentation. diff --git a/peg-macros/translate.rs b/peg-macros/translate.rs index 3354d0e..09803cb 100644 --- a/peg-macros/translate.rs +++ b/peg-macros/translate.rs @@ -7,6 +7,8 @@ pub use self::Expr::*; use crate::analysis; use crate::ast::*; +use cfg_if::cfg_if; + pub fn report_error(span: Span, msg: String) -> TokenStream { quote_spanned!(span=>compile_error!(#msg);) } @@ -161,25 +163,78 @@ fn make_parse_state(grammar: &Grammar) -> TokenStream { if rule.cache.is_some() && rule.params.is_empty() && rule.ty_params.is_none() { let name = format_ident!("{}_cache", rule.name); let ret_ty = rule.ret_type.clone().unwrap_or_else(|| quote!(())); - cache_fields_def.push( - quote_spanned! { span => #name: ::std::collections::HashMap> }, - ); + + let span = { + cfg_if! { + if #[cfg(not(feature = "std"))] { + cfg_if! { + if #[cfg(feature = "hashbrown")] { + quote_spanned! { span => #name: ::hashbrown::HashMap> } + } else { + quote_spanned! { span => #name: ::alloc::collections::btree_map::BTreeMap> } + } + } + } else { + quote_spanned! { span => #name: ::std::collections::HashMap> } + } + } + }; + + cache_fields_def.push(span); cache_fields.push(name); } } - quote_spanned! { span => + let parse_state_struct = quote_spanned! { span => #[allow(unused_parens)] struct ParseState<'input #(, #grammar_lifetime_params)*> { - _phantom: ::std::marker::PhantomData<(&'input () #(, &#grammar_lifetime_params ())*)>, + _phantom: ::core::marker::PhantomData<(&'input () #(, &#grammar_lifetime_params ())*)>, #(#cache_fields_def),* } + }; + + cfg_if! { + if #[cfg(not(feature = "std"))] { + cfg_if! { + if #[cfg(feature = "hashbrown")] { + quote_spanned! { span => + #parse_state_struct - impl<'input #(, #grammar_lifetime_params)*> ParseState<'input #(, #grammar_lifetime_params)*> { - fn new() -> ParseState<'input #(, #grammar_lifetime_params)*> { - ParseState { - _phantom: ::std::marker::PhantomData, - #(#cache_fields: ::std::collections::HashMap::new()),* + impl<'input #(, #grammar_lifetime_params)*> ParseState<'input #(, #grammar_lifetime_params)*> { + fn new() -> ParseState<'input #(, #grammar_lifetime_params)*> { + ParseState { + _phantom: ::core::marker::PhantomData, + #(#cache_fields: ::hashbrown::HashMap::new()),* + } + } + } + } + } else { + quote_spanned! { span => + #parse_state_struct + + impl<'input #(, #grammar_lifetime_params)*> ParseState<'input #(, #grammar_lifetime_params)*> { + fn new() -> ParseState<'input #(, #grammar_lifetime_params)*> { + ParseState { + _phantom: ::core::marker::PhantomData, + #(#cache_fields: ::alloc::collections::btree_map::BTreeMap::new()),* + } + } + } + } + } + } + } else { + quote_spanned! { span => + #parse_state_struct + + impl<'input #(, #grammar_lifetime_params)*> ParseState<'input #(, #grammar_lifetime_params)*> { + fn new() -> ParseState<'input #(, #grammar_lifetime_params)*> { + ParseState { + _phantom: ::core::marker::PhantomData, + #(#cache_fields: ::std::collections::HashMap::new()),* + } + } } } } @@ -369,7 +424,7 @@ fn compile_rule_export(context: &Context, rule: &Rule) -> TokenStream { quote_spanned! { span => #doc - #visibility fn #name<'input #(, #grammar_lifetime_params)* #(, #ty_params)*>(__input: #input_ty #extra_args_def #(, #rule_params)*) -> ::std::result::Result<#ret_ty, ::peg::error::ParseError>> { + #visibility fn #name<'input #(, #grammar_lifetime_params)* #(, #ty_params)*>(__input: #input_ty #extra_args_def #(, #rule_params)*) -> ::core::result::Result<#ret_ty, ::peg::error::ParseError>> { #![allow(non_snake_case, unused)] let mut __err_state = ::peg::error::ErrorState::new(::peg::Parse::start(__input)); diff --git a/peg-runtime/Cargo.toml b/peg-runtime/Cargo.toml index ed2d549..1b1e048 100644 --- a/peg-runtime/Cargo.toml +++ b/peg-runtime/Cargo.toml @@ -8,4 +8,7 @@ description = "Runtime support for rust-peg grammars. To use rust-peg, see the ` edition = "2018" [lib] -path = "lib.rs" \ No newline at end of file +path = "lib.rs" + +[features] +std = [] \ No newline at end of file diff --git a/peg-runtime/error.rs b/peg-runtime/error.rs index cc017d8..06b9a5d 100644 --- a/peg-runtime/error.rs +++ b/peg-runtime/error.rs @@ -1,13 +1,15 @@ //! Parse error reporting use crate::{Parse, RuleResult}; -use std::collections::HashSet; -use std::fmt::{self, Debug, Display}; +use core::fmt::{self, Debug, Display}; + +#[cfg(not(feature = "std"))] use alloc::{collections::btree_set::BTreeSet, vec::Vec}; +#[cfg(feature = "std")] use std::collections::BTreeSet; /// A set of literals or names that failed to match #[derive(PartialEq, Eq, Debug, Clone)] pub struct ExpectedSet { - expected: HashSet<&'static str>, + expected: BTreeSet<&'static str>, } impl ExpectedSet { @@ -49,7 +51,7 @@ pub struct ParseError { } impl Display for ParseError { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> { + fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::result::Result<(), ::core::fmt::Error> { write!( fmt, "error at {}: expected {}", @@ -58,6 +60,8 @@ impl Display for ParseError { } } +// Unfortuantely, std::error::Error never made it into core::error +#[cfg(feature = "std")] impl ::std::error::Error for ParseError { fn description(&self) -> &str { "parse error" @@ -88,7 +92,7 @@ impl ErrorState { suppress_fail: 0, reparsing_on_error: false, expected: ExpectedSet { - expected: HashSet::new(), + expected: BTreeSet::new(), }, } } diff --git a/peg-runtime/lib.rs b/peg-runtime/lib.rs index b351f24..566a3a1 100644 --- a/peg-runtime/lib.rs +++ b/peg-runtime/lib.rs @@ -1,4 +1,8 @@ -use std::fmt::Display; +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] extern crate alloc; + +use core::fmt::Display; pub mod error; mod slice; @@ -7,7 +11,7 @@ pub mod str; /// The result type used internally in the parser. /// /// You'll only need this if implementing the `Parse*` traits for a custom input -/// type. The public API of a parser adapts errors to `std::result::Result`. +/// type. The public API of a parser adapts errors to `core::result::Result`. #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] pub enum RuleResult { /// Success, with final location diff --git a/peg-runtime/str.rs b/peg-runtime/str.rs index 7b07097..347e90f 100644 --- a/peg-runtime/str.rs +++ b/peg-runtime/str.rs @@ -1,7 +1,7 @@ //! Utilities for `str` input use super::{Parse, ParseElem, ParseLiteral, ParseSlice, RuleResult}; -use std::fmt::Display; +use core::fmt::Display; /// Line and column within a string #[derive(PartialEq, Eq, Debug, Clone, Copy)] @@ -17,7 +17,7 @@ pub struct LineCol { } impl Display for LineCol { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> { + fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::result::Result<(), ::core::fmt::Error> { write!(fmt, "{}:{}", self.line, self.column) } } diff --git a/src/lib.rs b/src/lib.rs index a1a9792..c9d9739 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -334,6 +334,8 @@ //! ... //! ``` +#![cfg_attr(not(feature = "std"), no_std)] + extern crate peg_macros; extern crate peg_runtime as runtime; diff --git a/tests/run-pass/arithmetic_with_left_recursion.rs b/tests/run-pass/arithmetic_with_left_recursion.rs index f22c1f8..e09af8d 100644 --- a/tests/run-pass/arithmetic_with_left_recursion.rs +++ b/tests/run-pass/arithmetic_with_left_recursion.rs @@ -2,6 +2,8 @@ extern crate peg; use arithmetic::sum; +#[cfg(not(feature = "std"))] #[macro_use] extern crate alloc; + peg::parser!( grammar arithmetic() for str { #[cache_left_rec] pub rule sum() -> i64 diff --git a/tests/run-pass/grammar_with_args_and_cache.rs b/tests/run-pass/grammar_with_args_and_cache.rs index a2fdf4e..9e65922 100644 --- a/tests/run-pass/grammar_with_args_and_cache.rs +++ b/tests/run-pass/grammar_with_args_and_cache.rs @@ -1,5 +1,7 @@ extern crate peg; +#[cfg(not(feature = "std"))] #[macro_use] extern crate alloc; + peg::parser! { grammar lol(config: bool) for str { #[cache_left_rec] diff --git a/tests/run-pass/lifetimes.rs b/tests/run-pass/lifetimes.rs index 3be1521..4e22f62 100644 --- a/tests/run-pass/lifetimes.rs +++ b/tests/run-pass/lifetimes.rs @@ -1,3 +1,5 @@ +#[cfg(not(feature = "std"))] #[macro_use] extern crate alloc; + #[derive(Copy, Clone)] pub struct Token<'text>(&'text str); diff --git a/tests/run-pass/memoization.rs b/tests/run-pass/memoization.rs index 44b0eaf..517b9f1 100644 --- a/tests/run-pass/memoization.rs +++ b/tests/run-pass/memoization.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] #[macro_use] extern crate alloc; extern crate peg; peg::parser!{ grammar memo() for str { diff --git a/tests/run-pass/test-hygiene.rs b/tests/run-pass/test-hygiene.rs index d22d166..9bf7496 100644 --- a/tests/run-pass/test-hygiene.rs +++ b/tests/run-pass/test-hygiene.rs @@ -1,3 +1,5 @@ +#[cfg(not(feature = "std"))] #[macro_use] extern crate alloc; + use ::peg as realpeg; struct Result; struct ParseResult;