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;