Hide low-level parser code in backend module

This commit is contained in:
2025-06-07 12:09:14 -07:00
parent 75315499bc
commit ea0c5ff7ad
2 changed files with 113 additions and 92 deletions
+1
View File
@@ -379,6 +379,7 @@ pub fn grammar(ts: TokenStream) -> TokenStream {
let generated = r.definition.generate_code(); let generated = r.definition.generate_code();
quote! { quote! {
fn #fn_name(p: &mut crate::peg::ParseState<Tag>) -> bool { fn #fn_name(p: &mut crate::peg::ParseState<Tag>) -> bool {
use crate::peg::backend::LowLevel;
#generated #generated
} }
} }
+85 -65
View File
@@ -1,7 +1,4 @@
use std::{ use std::{num::NonZero, ops::Range};
num::NonZero,
ops::{Range, RangeInclusive},
};
pub struct ParseState<T: Clone> { pub struct ParseState<T: Clone> {
pub input: String, pub input: String,
@@ -9,11 +6,6 @@ pub struct ParseState<T: Clone> {
captures: Vec<RawCapture<T>>, captures: Vec<RawCapture<T>>,
} }
pub struct Savepoint {
offset: usize,
captures_len: usize,
}
pub struct Captures<'a, T: Clone> { pub struct Captures<'a, T: Clone> {
input: &'a str, input: &'a str,
raw: &'a [RawCapture<T>], raw: &'a [RawCapture<T>],
@@ -41,57 +33,40 @@ impl<T: Clone> ParseState<T> {
} }
} }
pub fn save(&self) -> Savepoint { pub fn captures<'a>(&'a self) -> Captures<'a, T> {
Savepoint { Captures {
offset: self.offset, input: &self.input,
captures_len: self.captures.len(), raw: &self.captures,
index: 0,
}
} }
} }
pub fn restore(&mut self, save: Savepoint) { pub mod backend {
self.offset = save.offset; use std::{num::NonZero, ops::RangeInclusive};
self.captures.truncate(save.captures_len);
use super::{ParseState, RawCapture};
pub struct Savepoint {
offset: usize,
captures_len: usize,
} }
pub fn literal(&mut self, s: &str) -> bool { pub trait LowLevel<T: Clone> {
if self.input[self.offset..].starts_with(s) { fn any(&mut self) -> bool;
self.offset += s.len(); fn begin_capture(&mut self, tag: T) -> Savepoint;
true fn commit_capture(&mut self, start: Savepoint);
} else { fn eoi(&self) -> bool;
false fn literal(&mut self, s: &str) -> bool;
} fn literal_i(&mut self, s: &str) -> bool;
fn range(&mut self, r: RangeInclusive<char>) -> bool;
fn range_i(&mut self, r: RangeInclusive<char>) -> bool;
fn restore(&mut self, save: Savepoint);
fn save(&self) -> Savepoint;
} }
pub fn literal_i(&mut self, s: &str) -> bool { impl<T: Clone> LowLevel<T> for ParseState<T> {
if self.input[self.offset..(self.offset + s.len())].eq_ignore_ascii_case(s) { fn any(&mut self) -> bool {
self.offset += s.len();
true
} else {
false
}
}
pub fn range(&mut self, r: RangeInclusive<char>) -> 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<char>) -> 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() { if let Some(c) = self.input[self.offset..].chars().next() {
self.offset += c.len_utf8(); self.offset += c.len_utf8();
true true
@@ -100,11 +75,7 @@ impl<T: Clone> ParseState<T> {
} }
} }
pub fn eoi(&self) -> bool { fn begin_capture(&mut self, tag: T) -> Savepoint {
self.offset >= self.input.len()
}
pub fn begin_capture(&mut self, tag: T) -> Savepoint {
let result = self.save(); let result = self.save();
self.captures.push(RawCapture { self.captures.push(RawCapture {
kind: tag, kind: tag,
@@ -114,7 +85,7 @@ impl<T: Clone> ParseState<T> {
result result
} }
pub fn commit_capture(&mut self, start: Savepoint) { fn commit_capture(&mut self, start: Savepoint) {
let index = start.captures_len; let index = start.captures_len;
assert_eq!(self.captures[index].subtree_len, None); assert_eq!(self.captures[index].subtree_len, None);
@@ -123,11 +94,59 @@ impl<T: Clone> ParseState<T> {
self.captures[index].subtree_len = NonZero::new(subtree_len); self.captures[index].subtree_len = NonZero::new(subtree_len);
} }
pub fn captures<'a>(&'a self) -> Captures<'a, T> { fn eoi(&self) -> bool {
Captures { self.offset >= self.input.len()
input: &self.input, }
raw: &self.captures,
index: 0, 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<char>) -> 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<char>) -> 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(),
}
} }
} }
} }
@@ -174,7 +193,7 @@ mod tests {
use insta::assert_debug_snapshot; use insta::assert_debug_snapshot;
use peg_macro::grammar; use peg_macro::grammar;
use super::*; use super::ParseState;
grammar! { grammar! {
fake_csv = line "\n" line EOI; fake_csv = line "\n" line EOI;
@@ -208,6 +227,7 @@ mod tests {
} }
fn parse<C: Clone, T: Fn(&mut ParseState<C>) -> bool>(rule: T, s: &str) -> bool { fn parse<C: Clone, T: Fn(&mut ParseState<C>) -> bool>(rule: T, s: &str) -> bool {
use super::backend::LowLevel;
println!("About to parse: {}", s); println!("About to parse: {}", s);
let mut p = ParseState::new(s); let mut p = ParseState::new(s);
rule(&mut p) && p.eoi() rule(&mut p) && p.eoi()