From ea0c5ff7adc69b622c9539e7ef30d000dec5e145 Mon Sep 17 00:00:00 2001 From: Chris Mounce Date: Sat, 7 Jun 2025 12:09:14 -0700 Subject: [PATCH] Hide low-level parser code in backend module --- peg_macro/src/lib.rs | 1 + src/peg/peg.rs | 204 ++++++++++++++++++++++++------------------- 2 files changed, 113 insertions(+), 92 deletions(-) diff --git a/peg_macro/src/lib.rs b/peg_macro/src/lib.rs index ebed033..9b381b2 100644 --- a/peg_macro/src/lib.rs +++ b/peg_macro/src/lib.rs @@ -379,6 +379,7 @@ pub fn grammar(ts: TokenStream) -> TokenStream { let generated = r.definition.generate_code(); quote! { fn #fn_name(p: &mut crate::peg::ParseState) -> bool { + use crate::peg::backend::LowLevel; #generated } } diff --git a/src/peg/peg.rs b/src/peg/peg.rs index 28cf425..f17217b 100644 --- a/src/peg/peg.rs +++ b/src/peg/peg.rs @@ -1,7 +1,4 @@ -use std::{ - num::NonZero, - ops::{Range, RangeInclusive}, -}; +use std::{num::NonZero, ops::Range}; pub struct ParseState { pub input: String, @@ -9,11 +6,6 @@ pub struct ParseState { captures: Vec>, } -pub struct Savepoint { - offset: usize, - captures_len: usize, -} - pub struct Captures<'a, T: Clone> { input: &'a str, raw: &'a [RawCapture], @@ -41,88 +33,6 @@ impl ParseState { } } - pub fn save(&self) -> Savepoint { - Savepoint { - offset: self.offset, - captures_len: self.captures.len(), - } - } - - pub fn restore(&mut self, save: Savepoint) { - self.offset = save.offset; - self.captures.truncate(save.captures_len); - } - - pub fn literal(&mut self, s: &str) -> bool { - if self.input[self.offset..].starts_with(s) { - self.offset += s.len(); - true - } else { - false - } - } - - pub fn literal_i(&mut self, s: &str) -> bool { - if self.input[self.offset..(self.offset + s.len())].eq_ignore_ascii_case(s) { - self.offset += s.len(); - true - } else { - false - } - } - - pub fn range(&mut self, r: RangeInclusive) -> bool { - if let Some(next) = self.input[self.offset..].chars().next() { - if r.contains(&next) { - self.offset += next.len_utf8(); - return true; - } - } - false - } - - pub fn range_i(&mut self, r: RangeInclusive) -> bool { - if let Some(next) = self.input[self.offset..].chars().next() { - if r.contains(&next.to_ascii_lowercase()) || r.contains(&next.to_ascii_uppercase()) { - self.offset += next.len_utf8(); - return true; - } - } - false - } - - pub fn any(&mut self) -> bool { - if let Some(c) = self.input[self.offset..].chars().next() { - self.offset += c.len_utf8(); - true - } else { - false - } - } - - pub fn eoi(&self) -> bool { - self.offset >= self.input.len() - } - - pub fn begin_capture(&mut self, tag: T) -> Savepoint { - let result = self.save(); - self.captures.push(RawCapture { - kind: tag, - span: 0..0, - subtree_len: None, - }); - result - } - - pub fn commit_capture(&mut self, start: Savepoint) { - let index = start.captures_len; - assert_eq!(self.captures[index].subtree_len, None); - - let subtree_len = self.captures.len() - index; - self.captures[index].span = start.offset..self.offset; - self.captures[index].subtree_len = NonZero::new(subtree_len); - } - pub fn captures<'a>(&'a self) -> Captures<'a, T> { Captures { input: &self.input, @@ -132,6 +42,115 @@ impl ParseState { } } +pub mod backend { + use std::{num::NonZero, ops::RangeInclusive}; + + use super::{ParseState, RawCapture}; + + pub struct Savepoint { + offset: usize, + captures_len: usize, + } + + pub trait LowLevel { + fn any(&mut self) -> bool; + fn begin_capture(&mut self, tag: T) -> Savepoint; + fn commit_capture(&mut self, start: Savepoint); + fn eoi(&self) -> bool; + fn literal(&mut self, s: &str) -> bool; + fn literal_i(&mut self, s: &str) -> bool; + fn range(&mut self, r: RangeInclusive) -> bool; + fn range_i(&mut self, r: RangeInclusive) -> bool; + fn restore(&mut self, save: Savepoint); + fn save(&self) -> Savepoint; + } + + impl LowLevel for ParseState { + fn any(&mut self) -> bool { + if let Some(c) = self.input[self.offset..].chars().next() { + self.offset += c.len_utf8(); + true + } else { + false + } + } + + fn begin_capture(&mut self, tag: T) -> Savepoint { + let result = self.save(); + self.captures.push(RawCapture { + kind: tag, + span: 0..0, + subtree_len: None, + }); + result + } + + fn commit_capture(&mut self, start: Savepoint) { + let index = start.captures_len; + assert_eq!(self.captures[index].subtree_len, None); + + let subtree_len = self.captures.len() - index; + self.captures[index].span = start.offset..self.offset; + self.captures[index].subtree_len = NonZero::new(subtree_len); + } + + fn eoi(&self) -> bool { + self.offset >= self.input.len() + } + + fn literal(&mut self, s: &str) -> bool { + if self.input[self.offset..].starts_with(s) { + self.offset += s.len(); + true + } else { + false + } + } + + fn literal_i(&mut self, s: &str) -> bool { + if self.input[self.offset..(self.offset + s.len())].eq_ignore_ascii_case(s) { + self.offset += s.len(); + true + } else { + false + } + } + + fn range(&mut self, r: RangeInclusive) -> bool { + if let Some(next) = self.input[self.offset..].chars().next() { + if r.contains(&next) { + self.offset += next.len_utf8(); + return true; + } + } + false + } + + fn range_i(&mut self, r: RangeInclusive) -> bool { + if let Some(next) = self.input[self.offset..].chars().next() { + if r.contains(&next.to_ascii_lowercase()) || r.contains(&next.to_ascii_uppercase()) + { + self.offset += next.len_utf8(); + return true; + } + } + false + } + + fn restore(&mut self, save: Savepoint) { + self.offset = save.offset; + self.captures.truncate(save.captures_len); + } + + fn save(&self) -> Savepoint { + Savepoint { + offset: self.offset, + captures_len: self.captures.len(), + } + } + } +} + impl<'a, T: Clone> Iterator for Captures<'a, T> { type Item = Capture<'a, T>; @@ -174,7 +193,7 @@ mod tests { use insta::assert_debug_snapshot; use peg_macro::grammar; - use super::*; + use super::ParseState; grammar! { fake_csv = line "\n" line EOI; @@ -208,6 +227,7 @@ mod tests { } fn parse) -> bool>(rule: T, s: &str) -> bool { + use super::backend::LowLevel; println!("About to parse: {}", s); let mut p = ParseState::new(s); rule(&mut p) && p.eoi()