Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add no_std support #294

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
replace std dependencies with core and alloc equivalent
stevefan1999-personal authored Apr 16, 2022
commit f8774cd282f19c451fb5b7e433cb39fe825783b5
63 changes: 63 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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.0" }
peg-runtime = { path = "./peg-runtime", version = "= 0.8.0" }

@@ -26,6 +27,5 @@ path = "tests/trybuild.rs"
harness = false

[features]
default = ["std"]
std = []
trace = ["peg-macros/trace"]
no_std = ["peg-macros/no_std", "peg-runtime/no_std"]
2 changes: 2 additions & 0 deletions benches/expr.rs
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@ extern crate peg;

extern crate test;

#[cfg(feature = "no_std")] #[macro_use] extern crate alloc;

use test::Bencher;

peg::parser!(grammar parser() for str {
2 changes: 2 additions & 0 deletions benches/json.rs
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@ extern crate peg;

extern crate test;

#[cfg(feature = "no_std")] #[macro_use] extern crate alloc;

use test::Bencher;

peg::parser!(grammar parser() for str {
3 changes: 3 additions & 0 deletions peg-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -11,9 +11,12 @@ edition = "2018"
quote = "1.0"
proc-macro2 = "1.0.24"
peg-runtime = { version = "= 0.8.0", path = "../peg-runtime" }
cfg-if = "1.0.0"

[features]
trace = []
no_std = []
hashbrown = []

[lib]
proc-macro = true
4 changes: 4 additions & 0 deletions peg-macros/bin.rs
Original file line number Diff line number Diff line change
@@ -15,6 +15,10 @@ use std::process;
// requires `::peg` paths.
extern crate peg_runtime as peg;

#[cfg(feature = "no_std")]
#[macro_use]
extern crate alloc;

mod analysis;
mod ast;
mod grammar;
2 changes: 2 additions & 0 deletions peg-macros/lib.rs
Original file line number Diff line number Diff line change
@@ -14,6 +14,8 @@ mod grammar;
mod tokens;
mod translate;

#[cfg(feature = "no_std")] extern crate alloc;

/// The main macro for creating a PEG parser.
///
/// For the grammar syntax, see the `peg` crate documentation.
77 changes: 66 additions & 11 deletions peg-macros/translate.rs
Original file line number Diff line number Diff line change
@@ -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);)
}
@@ -153,25 +155,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<usize, ::peg::RuleResult<#ret_ty>> },
);

let span = {
cfg_if! {
if #[cfg(feature = "no_std")] {
cfg_if! {
if #[cfg(feature = "hashbrown")] {
quote_spanned! { span => #name: ::hashbrown::HashMap<usize, ::peg::RuleResult<#ret_ty>> }
} else {
quote_spanned! { span => #name: ::alloc::collections::btree_map::BTreeMap<usize, ::peg::RuleResult<#ret_ty>> }
}
}
} else {
quote_spanned! { span => #name: ::std::collections::HashMap<usize, ::peg::RuleResult<#ret_ty>> }
}
}
};

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),*
}
};

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()),*
cfg_if! {
if #[cfg(feature = "no_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: ::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()),*
}
}
}
}
}
@@ -331,7 +386,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<PositionRepr<#(#grammar_lifetime_params),*>>> {
#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<PositionRepr<#(#grammar_lifetime_params),*>>> {
#![allow(non_snake_case, unused)]

let mut __err_state = ::peg::error::ErrorState::new(::peg::Parse::start(__input));
5 changes: 4 additions & 1 deletion peg-runtime/Cargo.toml
Original file line number Diff line number Diff line change
@@ -8,4 +8,7 @@ description = "Runtime support for rust-peg grammars. To use rust-peg, see the `
edition = "2018"

[lib]
path = "lib.rs"
path = "lib.rs"

[features]
no_std = []
14 changes: 9 additions & 5 deletions peg-runtime/error.rs
Original file line number Diff line number Diff line change
@@ -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(feature = "no_std")] use alloc::{collections::btree_set::BTreeSet, vec::Vec};
#[cfg(not(feature = "no_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<L> {
}

impl<L: Display> Display for ParseError<L> {
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<L: Display> Display for ParseError<L> {
}
}

// Unfortuantely, std::error::Error never made it into core::error
#[cfg(not(feature = "no_std"))]
impl<L: Display + Debug> ::std::error::Error for ParseError<L> {
fn description(&self) -> &str {
"parse error"
@@ -79,7 +83,7 @@ impl ErrorState {
suppress_fail: 0,
reparsing_on_error: false,
expected: ExpectedSet {
expected: HashSet::new(),
expected: BTreeSet::new(),
},
}
}
8 changes: 6 additions & 2 deletions peg-runtime/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use std::fmt::Display;
#![cfg_attr(feature = "no_std", no_std)]

#[cfg(feature = "no_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<T> {
Matched(usize, T),
4 changes: 2 additions & 2 deletions peg-runtime/str.rs
Original file line number Diff line number Diff line change
@@ -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)]
@@ -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)
}
}