use crate::{
either::Either, eof::EndOfFile, literal::Literal, parser::Parser, repeat::Repeat,
result::Error, sequence::Sequence, until::Utf8Until, utf8class::Utf8Class,
utf8util::read_utf8_char,
};
const OPEN_PAREN: Literal = Literal::from_str_const("(");
const CLOSE_PAREN: Literal = Literal::from_str_const(")");
const PIPE: Literal = Literal::from_str_const("|");
const ASTERISK: Literal = Literal::from_str_const("*");
const PLUS: Literal = Literal::from_str_const("+");
const QUESTION: Literal = Literal::from_str_const("?");
const LESS_THAN: Literal = Literal::from_str_const("<");
const SLASH: Literal = Literal::from_str_const("/");
const OPEN_BRACKET: Literal = Literal::from_str_const("[");
const CARET: Literal = Literal::from_str_const("^");
const QUOTE: Literal = Literal::from_str_const("\"");
const AT_SYMBOL: Literal = Literal::from_str_const("@");
const EQUALS: Literal = Literal::from_str_const("=");
const START_KEYWORD: Literal = Literal::from_str_const("start");
const DIGITS_KEYWORD: Literal = Literal::from_str_const("digits");
const ALPHA_KEYWORD: Literal = Literal::from_str_const("alpha");
const ALPHANUMERIC_KEYWORD: Literal = Literal::from_str_const("alphanumeric");
const WHITESPACE_KEYWORD: Literal = Literal::from_str_const("whitespace");
const HEXDIGITS_KEYWORD: Literal = Literal::from_str_const("hexdigits");
const UDIGITS_KEYWORD: Literal = Literal::from_str_const("udigits");
const UALPHA_KEYWORD: Literal = Literal::from_str_const("ualpha");
const UALPHANUMERIC_KEYWORD: Literal = Literal::from_str_const("ualphanumeric");
const UWHITESPACE_KEYWORD: Literal = Literal::from_str_const("uwhitespace");
const EOF_KEYWORD: Literal = Literal::from_str_const("eof");
#[derive(Debug, Clone, Default)]
struct GrammarContext {
rules: std::collections::HashMap<String, GrammarNode>,
start_rule: Option<String>,
}
#[derive(Clone)]
pub struct GrammarParser;
impl Default for GrammarParser {
fn default() -> Self {
Self::new()
}
}
impl GrammarParser {
pub fn new() -> Self {
Self
}
}
impl Parser<()> for GrammarParser {
type Output = Grammar;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
_context: &mut (),
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
let mut grammar_context = GrammarContext::default();
let mut last_node = None;
let ws = crate::utf8class::Utf8Class::whitespace();
loop {
let _ = ws.parse(source, cache, &mut grammar_context);
match self.read(source, cache, &mut grammar_context) {
Ok(node) => {
last_node = Some(node);
}
Err(Error::NoMatch) => {
break;
}
Err(other_error) => {
return Err(other_error);
}
}
}
if let Some(start) = grammar_context.start_rule.as_deref() {
if let Some(rule) = grammar_context.rules.get(start).cloned() {
Ok(Grammar::new(rule, grammar_context))
} else {
Err(Error::NoMatch)
}
} else if let Some(last_node) = last_node {
Ok(Grammar::new(last_node, grammar_context))
} else {
Err(Error::NoMatch)
}
}
}
impl Parser<GrammarContext> for GrammarParser {
type Output = GrammarNode;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
if let Ok(start_rule) = StartDirective.parse(source, cache, context) {
context.start_rule = Some(start_rule);
return Ok(GrammarNode::Empty);
}
if let Ok((name, node)) = RuleDefinition.parse(source, cache, context) {
context.rules.insert(name, node);
return Ok(GrammarNode::Empty);
}
if let Ok(literal) = Terminal.parse(source, cache, context) {
return Ok(GrammarNode::Terminal(literal));
}
if let Ok(digits) = Digits.parse(source, cache, context) {
return Ok(GrammarNode::Digits(digits));
}
if let Ok(alphanumeric) = Alphanumeric.parse(source, cache, context) {
return Ok(GrammarNode::Alphanumeric(alphanumeric));
}
if let Ok(alpha) = Alpha.parse(source, cache, context) {
return Ok(GrammarNode::Alpha(alpha));
}
if let Ok(whitespace) = Whitespace.parse(source, cache, context) {
return Ok(GrammarNode::Whitespace(whitespace));
}
if let Ok(udigits) = UDigits.parse(source, cache, context) {
return Ok(GrammarNode::UDigits(udigits));
}
if let Ok(ualphanumeric) = UAlphanumeric.parse(source, cache, context) {
return Ok(GrammarNode::UAlphanumeric(ualphanumeric));
}
if let Ok(ualpha) = UAlpha.parse(source, cache, context) {
return Ok(GrammarNode::UAlpha(ualpha));
}
if let Ok(uwhitespace) = UWhitespace.parse(source, cache, context) {
return Ok(GrammarNode::UWhitespace(uwhitespace));
}
if let Ok(hexdigits) = HexDigits.parse(source, cache, context) {
return Ok(GrammarNode::HexDigits(hexdigits));
}
if let Ok(eof) = EndOfFileParser.parse(source, cache, context) {
return Ok(GrammarNode::EndOfFile(eof));
}
if let Ok(inclass) = InClass.parse(source, cache, context) {
return Ok(GrammarNode::InClass(inclass));
}
if let Ok(notinclass) = NotInClass.parse(source, cache, context) {
return Ok(GrammarNode::NotInClass(notinclass));
}
if let Ok(expr) = Alternatives.parse(source, cache, context) {
return Ok(expr);
}
if let Ok(expr) = ZeroOrMoreSeparated.parse(source, cache, context) {
return Ok(expr);
}
if let Ok(expr) = OneOrMoreSeparated.parse(source, cache, context) {
return Ok(expr);
}
if let Ok(expr) = ZeroOrMore.parse(source, cache, context) {
return Ok(expr);
}
if let Ok(expr) = OneOrMore.parse(source, cache, context) {
return Ok(expr);
}
if let Ok(expr) = ZeroOrOne.parse(source, cache, context) {
return Ok(expr);
}
if let Ok(expr) = ReadUntilAndParse.parse(source, cache, context) {
return Ok(expr);
}
if let Ok(expr) = ReadUntil.parse(source, cache, context) {
return Ok(expr);
}
if let Ok(expr) = RuleReferenceParser.parse(source, cache, context) {
return Ok(expr);
}
if let Ok(expr) = Sequential.parse(source, cache, context) {
return Ok(expr);
}
Err(Error::NoMatch)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GrammarResult {
Literal(Vec<u8>),
Unicode(String),
Bytes(Vec<u8>),
Sequence(Vec<GrammarResult>),
Alternative(Box<GrammarResult>),
Repetition(Vec<GrammarResult>),
Optional(Option<Box<GrammarResult>>),
Empty,
}
impl GrammarResult {
pub fn to_bytes(&self) -> Vec<u8> {
match self {
GrammarResult::Literal(bytes) => bytes.clone(),
GrammarResult::Unicode(string) => string.as_bytes().to_vec(),
GrammarResult::Bytes(bytes) => bytes.clone(),
GrammarResult::Sequence(results) => {
let mut combined = Vec::new();
for result in results {
combined.extend(result.to_bytes());
}
combined
}
GrammarResult::Alternative(result) => result.to_bytes(),
GrammarResult::Repetition(results) => {
let mut combined = Vec::new();
for result in results {
combined.extend(result.to_bytes());
}
combined
}
GrammarResult::Optional(Some(result)) => result.to_bytes(),
GrammarResult::Optional(None) | GrammarResult::Empty => Vec::new(),
}
}
pub fn is_empty(&self) -> bool {
matches!(self, GrammarResult::Empty | GrammarResult::Optional(None))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum GrammarNode {
RuleReference(String),
Empty,
Terminal(Literal),
Digits(Utf8Class),
Alpha(Utf8Class),
Alphanumeric(Utf8Class),
Whitespace(Utf8Class),
UDigits(Utf8Class),
UAlpha(Utf8Class),
UAlphanumeric(Utf8Class),
UWhitespace(Utf8Class),
HexDigits(Utf8Class),
EndOfFile(EndOfFile),
InClass(Utf8Class),
NotInClass(Utf8Class),
Sequential(Sequence<Box<GrammarNode>, Box<GrammarNode>>),
SequentialEnd(Sequence<Box<GrammarNode>, ()>),
Alternatives(Either<Box<GrammarNode>, Box<GrammarNode>>),
ZeroOrMore(Repeat<Box<GrammarNode>, ()>),
OneOrMore(Repeat<Box<GrammarNode>, ()>),
ZeroOrOne(Repeat<Box<GrammarNode>>),
ZeroOrMoreSeparated(Repeat<Box<GrammarNode>, Box<GrammarNode>>),
OneOrMoreSeparated(Repeat<Box<GrammarNode>, Box<GrammarNode>>),
ReadUntil(Utf8Until<Box<GrammarNode>>),
ReadUntilParse(Utf8Until<Box<GrammarNode>>, Box<GrammarNode>),
}
#[derive(Debug, Clone)]
pub struct Grammar {
node: GrammarNode,
context: GrammarContext,
}
impl Grammar {
fn new(node: GrammarNode, context: GrammarContext) -> Self {
Self { node, context }
}
pub fn rule_exists(&self, name: &str) -> bool {
self.context.rules.contains_key(name)
}
pub fn start_rule(&self) -> Option<&str> {
self.context.start_rule.as_deref()
}
}
impl Parser<()> for Grammar {
type Output = GrammarResult;
fn id(&self) -> u64 {
use std::any::TypeId;
use std::hash::{DefaultHasher, Hash, Hasher};
let mut hasher = DefaultHasher::new();
TypeId::of::<Self>().hash(&mut hasher);
self.node.id().hash(&mut hasher);
self.context.rules.len().hash(&mut hasher);
if let Some(ref start_rule) = self.context.start_rule {
start_rule.hash(&mut hasher);
}
hasher.finish()
}
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
_context: &mut (),
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
let mut grammar_context = self.context.clone();
self.node.read(source, cache, &mut grammar_context)
}
}
impl Parser<GrammarContext> for Grammar {
type Output = GrammarResult;
fn id(&self) -> u64 {
use std::any::TypeId;
use std::hash::{DefaultHasher, Hash, Hasher};
let mut hasher = DefaultHasher::new();
TypeId::of::<Self>().hash(&mut hasher);
self.node.id().hash(&mut hasher);
self.context.rules.len().hash(&mut hasher);
if let Some(ref start_rule) = self.context.start_rule {
start_rule.hash(&mut hasher);
}
hasher.finish()
}
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
self.node.read(source, cache, context)
}
}
impl Parser<GrammarContext> for GrammarNode {
type Output = GrammarResult;
fn id(&self) -> u64 {
use std::any::TypeId;
use std::hash::{DefaultHasher, Hash, Hasher};
use std::mem;
let mut hasher = DefaultHasher::new();
TypeId::of::<Self>().hash(&mut hasher);
mem::discriminant(self).hash(&mut hasher);
match self {
GrammarNode::RuleReference(name) => {
name.hash(&mut hasher);
}
GrammarNode::Empty => {
}
GrammarNode::Terminal(literal) => {
<Literal as Parser<GrammarContext>>::id(literal).hash(&mut hasher);
}
GrammarNode::Digits(utf8_class) => {
<Utf8Class as Parser<GrammarContext>>::id(utf8_class).hash(&mut hasher);
}
GrammarNode::Alpha(utf8_class) => {
<Utf8Class as Parser<GrammarContext>>::id(utf8_class).hash(&mut hasher);
}
GrammarNode::Alphanumeric(utf8_class) => {
<Utf8Class as Parser<GrammarContext>>::id(utf8_class).hash(&mut hasher);
}
GrammarNode::Whitespace(utf8_class) => {
<Utf8Class as Parser<GrammarContext>>::id(utf8_class).hash(&mut hasher);
}
GrammarNode::UDigits(utf8_class) => {
<Utf8Class as Parser<GrammarContext>>::id(utf8_class).hash(&mut hasher);
}
GrammarNode::UAlpha(utf8_class) => {
<Utf8Class as Parser<GrammarContext>>::id(utf8_class).hash(&mut hasher);
}
GrammarNode::UAlphanumeric(utf8_class) => {
<Utf8Class<fn(char) -> bool> as Parser<GrammarContext>>::id(utf8_class)
.hash(&mut hasher);
}
GrammarNode::UWhitespace(utf8_class) => {
<Utf8Class<fn(char) -> bool> as Parser<GrammarContext>>::id(utf8_class)
.hash(&mut hasher);
}
GrammarNode::HexDigits(utf8_class) => {
<Utf8Class as Parser<GrammarContext>>::id(utf8_class).hash(&mut hasher);
}
GrammarNode::EndOfFile(eof) => {
<EndOfFile as Parser<GrammarContext>>::id(eof).hash(&mut hasher);
}
GrammarNode::InClass(utf8_class) => {
<Utf8Class as Parser<GrammarContext>>::id(utf8_class).hash(&mut hasher);
}
GrammarNode::NotInClass(utf8_class) => {
<Utf8Class as Parser<GrammarContext>>::id(utf8_class).hash(&mut hasher);
}
GrammarNode::Sequential(sequence) => {
<Sequence<Box<GrammarNode>, Box<GrammarNode>> as Parser<GrammarContext>>::id(
sequence,
)
.hash(&mut hasher);
}
GrammarNode::SequentialEnd(sequence) => {
<Sequence<Box<GrammarNode>, ()> as Parser<GrammarContext>>::id(sequence)
.hash(&mut hasher);
}
GrammarNode::Alternatives(either) => {
<Either<Box<GrammarNode>, Box<GrammarNode>> as Parser<GrammarContext>>::id(either)
.hash(&mut hasher);
}
GrammarNode::ZeroOrMore(repeat) => {
<Repeat<Box<GrammarNode>, ()> as Parser<GrammarContext>>::id(repeat)
.hash(&mut hasher);
}
GrammarNode::OneOrMore(repeat) => {
<Repeat<Box<GrammarNode>, ()> as Parser<GrammarContext>>::id(repeat)
.hash(&mut hasher);
}
GrammarNode::ZeroOrOne(repeat) => {
<Repeat<Box<GrammarNode>> as Parser<GrammarContext>>::id(repeat).hash(&mut hasher);
}
GrammarNode::ZeroOrMoreSeparated(repeat) => {
<Repeat<Box<GrammarNode>, Box<GrammarNode>> as Parser<GrammarContext>>::id(repeat)
.hash(&mut hasher);
}
GrammarNode::OneOrMoreSeparated(repeat) => {
<Repeat<Box<GrammarNode>, Box<GrammarNode>> as Parser<GrammarContext>>::id(repeat)
.hash(&mut hasher);
}
GrammarNode::ReadUntil(until) => {
<Utf8Until<Box<GrammarNode>> as Parser<GrammarContext>>::id(until)
.hash(&mut hasher);
}
GrammarNode::ReadUntilParse(until, content_parser) => {
<Utf8Until<Box<GrammarNode>> as Parser<GrammarContext>>::id(until)
.hash(&mut hasher);
<Box<GrammarNode> as Parser<GrammarContext>>::id(content_parser).hash(&mut hasher);
}
}
hasher.finish()
}
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
match self {
GrammarNode::RuleReference(name) => {
if let Some(rule_node) = context.rules.get(name).cloned() {
rule_node.parse(source, cache, context)
} else {
Err(Error::NoMatch)
}
}
GrammarNode::Empty => {
Ok(GrammarResult::Empty)
}
GrammarNode::Terminal(literal) => {
let result = literal.parse(source, cache, context)?;
Ok(GrammarResult::Literal(result.into()))
}
GrammarNode::Digits(utf8_class) => {
let result = utf8_class.parse(source, cache, context)?;
Ok(GrammarResult::Unicode(result))
}
GrammarNode::Alpha(utf8_class) => {
let result = utf8_class.parse(source, cache, context)?;
Ok(GrammarResult::Unicode(result))
}
GrammarNode::Alphanumeric(utf8_class) => {
let result = utf8_class.parse(source, cache, context)?;
Ok(GrammarResult::Unicode(result))
}
GrammarNode::Whitespace(utf8_class) => {
let result = utf8_class.parse(source, cache, context)?;
Ok(GrammarResult::Unicode(result))
}
GrammarNode::UDigits(utf8_class) => {
let result = utf8_class.parse(source, cache, context)?;
Ok(GrammarResult::Unicode(result))
}
GrammarNode::UAlpha(utf8_class) => {
let result = utf8_class.parse(source, cache, context)?;
Ok(GrammarResult::Unicode(result))
}
GrammarNode::UAlphanumeric(utf8_class) => {
let result = utf8_class.parse(source, cache, context)?;
Ok(GrammarResult::Unicode(result))
}
GrammarNode::UWhitespace(utf8_class) => {
let result = utf8_class.parse(source, cache, context)?;
Ok(GrammarResult::Unicode(result))
}
GrammarNode::HexDigits(utf8_class) => {
let result = utf8_class.parse(source, cache, context)?;
Ok(GrammarResult::Unicode(result))
}
GrammarNode::EndOfFile(eof) => {
eof.parse(source, cache, context)?;
Ok(GrammarResult::Empty)
}
GrammarNode::InClass(utf8_class) => {
let result = utf8_class.parse(source, cache, context)?;
Ok(GrammarResult::Unicode(result))
}
GrammarNode::NotInClass(utf8_class) => {
let result = utf8_class.parse(source, cache, context)?;
Ok(GrammarResult::Unicode(result))
}
GrammarNode::Sequential(sequence) => {
let result = sequence.parse(source, cache, context)?;
Ok(GrammarResult::Sequence(vec![result.0, result.1]))
}
GrammarNode::SequentialEnd(sequence) => {
let result = sequence.parse(source, cache, context)?;
Ok(result.0)
}
GrammarNode::Alternatives(either) => {
let result = either.parse(source, cache, context)?;
match result {
(Some(left), None) => Ok(GrammarResult::Alternative(Box::new(left))),
(None, Some(right)) => Ok(GrammarResult::Alternative(Box::new(right))),
_ => unreachable!("Either should return exactly one result"),
}
}
GrammarNode::ZeroOrMore(repeat) => {
let results = repeat.parse(source, cache, context)?;
Ok(GrammarResult::Repetition(results))
}
GrammarNode::OneOrMore(repeat) => {
let results = repeat.parse(source, cache, context)?;
Ok(GrammarResult::Repetition(results))
}
GrammarNode::ZeroOrOne(option) => {
let result = option.parse(source, cache, context)?;
if result.is_empty() {
Ok(GrammarResult::Optional(None))
} else {
Ok(GrammarResult::Optional(
result.into_iter().next().map(Box::new),
))
}
}
GrammarNode::ZeroOrMoreSeparated(repeat) => {
let results = repeat.parse(source, cache, context)?;
Ok(GrammarResult::Repetition(results))
}
GrammarNode::OneOrMoreSeparated(repeat) => {
let results = repeat.parse(source, cache, context)?;
Ok(GrammarResult::Repetition(results))
}
GrammarNode::ReadUntil(until) => {
let result = until.parse(source, cache, context)?;
Ok(GrammarResult::Unicode(result))
}
GrammarNode::ReadUntilParse(until, content_parser) => {
let captured = until.parse(source, cache, context)?;
let mut captured_input = std::io::Cursor::new(captured.as_bytes());
let mut captured_source = crate::parser::Source::new(&mut captured_input);
let content_result = content_parser.parse(&mut captured_source, cache, context)?;
Ok(content_result)
}
}
}
}
struct Terminal;
impl Parser<GrammarContext> for Terminal {
type Output = Literal;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
QUOTE.parse(source, cache, context)?;
let mut bytes = Vec::new();
let mut escaped = false;
let mut reading = true;
while reading {
let byte = source.peek1()?;
if escaped {
bytes.push(byte);
escaped = false;
source.advance(1);
} else if byte == b'\\' {
escaped = true;
source.advance(1);
} else if byte == b'"' {
reading = false;
} else {
bytes.push(byte);
source.advance(1);
}
}
QUOTE.parse(source, cache, context)?;
Ok(Literal::from_bytes(&bytes))
}
}
struct Digits;
impl Parser<GrammarContext> for Digits {
type Output = Utf8Class;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
DIGITS_KEYWORD.parse(source, cache, context)?;
Ok(Utf8Class::digits())
}
}
struct Alpha;
impl Parser<GrammarContext> for Alpha {
type Output = Utf8Class;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
ALPHA_KEYWORD.parse(source, cache, context)?;
Ok(Utf8Class::alpha())
}
}
struct Alphanumeric;
impl Parser<GrammarContext> for Alphanumeric {
type Output = Utf8Class;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
ALPHANUMERIC_KEYWORD.parse(source, cache, context)?;
Ok(Utf8Class::alphanumeric())
}
}
struct Whitespace;
impl Parser<GrammarContext> for Whitespace {
type Output = Utf8Class;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
WHITESPACE_KEYWORD.parse(source, cache, context)?;
Ok(Utf8Class::whitespace())
}
}
struct HexDigits;
impl Parser<GrammarContext> for HexDigits {
type Output = Utf8Class;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
HEXDIGITS_KEYWORD.parse(source, cache, context)?;
Ok(Utf8Class::hex_digits())
}
}
struct UDigits;
impl Parser<GrammarContext> for UDigits {
type Output = Utf8Class<fn(char) -> bool>;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
UDIGITS_KEYWORD.parse(source, cache, context)?;
Ok(Utf8Class::unicode_digits())
}
}
struct UAlpha;
impl Parser<GrammarContext> for UAlpha {
type Output = Utf8Class<fn(char) -> bool>;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
UALPHA_KEYWORD.parse(source, cache, context)?;
Ok(Utf8Class::unicode_alpha())
}
}
struct UAlphanumeric;
impl Parser<GrammarContext> for UAlphanumeric {
type Output = Utf8Class<fn(char) -> bool>;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
UALPHANUMERIC_KEYWORD.parse(source, cache, context)?;
Ok(Utf8Class::from_predicate_min(|c| c.is_alphanumeric(), 1))
}
}
struct UWhitespace;
impl Parser<GrammarContext> for UWhitespace {
type Output = Utf8Class<fn(char) -> bool>;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
UWHITESPACE_KEYWORD.parse(source, cache, context)?;
Ok(Utf8Class::unicode_whitespace())
}
}
struct EndOfFileParser;
impl Parser<GrammarContext> for EndOfFileParser {
type Output = EndOfFile;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
EOF_KEYWORD.parse(source, cache, context)?;
Ok(EndOfFile::new())
}
}
struct Identifier;
impl Parser<GrammarContext> for Identifier {
type Output = String;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
let first_char = Utf8Class::alpha().parse(source, cache, context)?;
let rest_chars = Utf8Class::from_predicate_min(|c| c.is_alphanumeric() || c == '_', 0)
.parse(source, cache, context)
.unwrap_or_default();
Ok(format!("{first_char}{rest_chars}"))
}
}
struct InClass;
impl Parser<GrammarContext> for InClass {
type Output = Utf8Class;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
OPEN_BRACKET.parse(source, cache, context)?;
if source.peek1()? == b'^' {
return Err(Error::NoMatch);
}
let mut chars = String::new();
let mut escaped = false;
loop {
let ch = read_utf8_char(source)?;
if escaped {
chars.push(ch);
escaped = false;
} else if ch == '\\' {
escaped = true;
} else if ch == ']' {
break;
} else {
chars.push(ch);
}
}
Ok(Utf8Class::with_min(&chars, 1))
}
}
struct NotInClass;
impl Parser<GrammarContext> for NotInClass {
type Output = Utf8Class;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
OPEN_BRACKET.parse(source, cache, context)?;
CARET.parse(source, cache, context)?;
let mut chars = String::new();
let mut escaped = false;
loop {
let ch = read_utf8_char(source)?;
if escaped {
chars.push(ch);
escaped = false;
} else if ch == '\\' {
escaped = true;
} else if ch == ']' {
break;
} else {
chars.push(ch);
}
}
Ok(Utf8Class::not_in_with_min(&chars, 1))
}
}
struct Sequential;
impl Parser<GrammarContext> for Sequential {
type Output = GrammarNode;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
OPEN_PAREN.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let mut expressions: Vec<GrammarNode> = Vec::new();
while source.peek1().unwrap_or(0) != b')' {
let _ = Utf8Class::whitespace().parse(source, cache, context);
if source.peek1().unwrap_or(0) == b')' {
break;
}
match <GrammarParser as Parser<GrammarContext>>::read(
&GrammarParser,
source,
cache,
context,
) {
Ok(expr) => {
expressions.push(expr);
}
Err(_) => {
return Err(Error::NoMatch);
}
}
let _ = Utf8Class::whitespace().parse(source, cache, context);
}
CLOSE_PAREN.parse(source, cache, context)?;
if expressions.is_empty() {
return Err(Error::NoMatch);
}
if expressions.len() == 1 {
return Ok(expressions.into_iter().next().unwrap());
}
let mut expressions = expressions;
expressions.reverse();
let last_expr = expressions.remove(0); let mut result = GrammarNode::SequentialEnd(Sequence::new(Box::new(last_expr), ()));
for expr in expressions {
let sequence = Sequence::new(Box::new(expr), Box::new(result));
result = GrammarNode::Sequential(sequence);
}
Ok(result)
}
}
struct Alternatives;
impl Parser<GrammarContext> for Alternatives {
type Output = GrammarNode;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
OPEN_PAREN.parse(source, cache, context)?;
PIPE.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let mut alternatives: Vec<GrammarNode> = Vec::new();
while source.peek1().unwrap_or(0) != b')' {
let _ = Utf8Class::whitespace().parse(source, cache, context);
if source.peek1().unwrap_or(0) == b')' {
break;
}
match <GrammarParser as Parser<GrammarContext>>::read(
&GrammarParser,
source,
cache,
context,
) {
Ok(expr) => {
alternatives.push(expr);
}
Err(_) => {
return Err(Error::NoMatch);
}
}
let _ = Utf8Class::whitespace().parse(source, cache, context);
}
CLOSE_PAREN.parse(source, cache, context)?;
if alternatives.is_empty() {
return Err(Error::NoMatch);
}
if alternatives.len() == 1 {
return Ok(alternatives.into_iter().next().unwrap());
}
let mut alternatives = alternatives;
alternatives.reverse();
let mut result = alternatives.remove(0);
for expr in alternatives {
let either = Either::new(Box::new(expr), Box::new(result));
result = GrammarNode::Alternatives(either);
}
Ok(result)
}
}
struct ZeroOrMore;
impl Parser<GrammarContext> for ZeroOrMore {
type Output = GrammarNode;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
OPEN_PAREN.parse(source, cache, context)?;
ASTERISK.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let inner_expr = GrammarParser.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
CLOSE_PAREN.parse(source, cache, context)?;
let repeat = Repeat::new(Box::new(inner_expr));
Ok(GrammarNode::ZeroOrMore(repeat))
}
}
struct OneOrMore;
impl Parser<GrammarContext> for OneOrMore {
type Output = GrammarNode;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
OPEN_PAREN.parse(source, cache, context)?;
PLUS.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let inner_expr = GrammarParser.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
CLOSE_PAREN.parse(source, cache, context)?;
let repeat = Repeat::with_min(Box::new(inner_expr), 1);
Ok(GrammarNode::OneOrMore(repeat))
}
}
struct ZeroOrMoreSeparated;
impl Parser<GrammarContext> for ZeroOrMoreSeparated {
type Output = GrammarNode;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
OPEN_PAREN.parse(source, cache, context)?;
ASTERISK.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let inner_expr = GrammarParser.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
SLASH.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let separator_expr = GrammarParser.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
CLOSE_PAREN.parse(source, cache, context)?;
let repeat = Repeat::with_joint(Box::new(inner_expr), Box::new(separator_expr));
Ok(GrammarNode::ZeroOrMoreSeparated(repeat))
}
}
struct OneOrMoreSeparated;
impl Parser<GrammarContext> for OneOrMoreSeparated {
type Output = GrammarNode;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
OPEN_PAREN.parse(source, cache, context)?;
PLUS.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let inner_expr = GrammarParser.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
SLASH.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let separator_expr = GrammarParser.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
CLOSE_PAREN.parse(source, cache, context)?;
let repeat = Repeat::with_joint_min(Box::new(inner_expr), Box::new(separator_expr), 1);
Ok(GrammarNode::OneOrMoreSeparated(repeat))
}
}
struct ZeroOrOne;
impl Parser<GrammarContext> for ZeroOrOne {
type Output = GrammarNode;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
OPEN_PAREN.parse(source, cache, context)?;
QUESTION.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let inner_expr = GrammarParser.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
CLOSE_PAREN.parse(source, cache, context)?;
let repeat = Repeat::with_max(Box::new(inner_expr), 1);
Ok(GrammarNode::ZeroOrOne(repeat))
}
}
struct ReadUntil;
impl Parser<GrammarContext> for ReadUntil {
type Output = GrammarNode;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
OPEN_PAREN.parse(source, cache, context)?;
LESS_THAN.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let end_expr = GrammarParser.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
CLOSE_PAREN.parse(source, cache, context)?;
let utf8_until = Utf8Until::new(Box::new(end_expr));
Ok(GrammarNode::ReadUntil(utf8_until))
}
}
struct ReadUntilAndParse;
impl Parser<GrammarContext> for ReadUntilAndParse {
type Output = GrammarNode;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
OPEN_PAREN.parse(source, cache, context)?;
LESS_THAN.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let end_expr = GrammarParser.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let content_parser = GrammarParser.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
CLOSE_PAREN.parse(source, cache, context)?;
let utf8_until = Utf8Until::new(Box::new(end_expr));
Ok(GrammarNode::ReadUntilParse(
utf8_until,
Box::new(content_parser),
))
}
}
struct StartDirective;
impl Parser<GrammarContext> for StartDirective {
type Output = String;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
AT_SYMBOL.parse(source, cache, context)?;
START_KEYWORD.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let name = Identifier.parse(source, cache, context)?;
Ok(name)
}
}
struct RuleDefinition;
impl Parser<GrammarContext> for RuleDefinition {
type Output = (String, GrammarNode);
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
let name = Identifier.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
EQUALS.parse(source, cache, context)?;
let _ = Utf8Class::whitespace().parse(source, cache, context);
let expression = <GrammarParser as Parser<GrammarContext>>::read(
&GrammarParser,
source,
cache,
context,
)?;
Ok((name, expression))
}
}
struct RuleReferenceParser;
impl Parser<GrammarContext> for RuleReferenceParser {
type Output = GrammarNode;
fn read<S>(
&self,
source: &mut crate::parser::Source<S>,
cache: &mut impl crate::cache::ParsingCache,
context: &mut GrammarContext,
) -> crate::result::ParseResult<Self::Output>
where
S: crate::parser::Parsable,
{
let name = Identifier.parse(source, cache, context)?;
if matches!(
name.as_str(),
"digits"
| "alpha"
| "alphanumeric"
| "whitespace"
| "hexdigits"
| "udigits"
| "ualpha"
| "ualphanumeric"
| "uwhitespace"
| "eof"
) {
return Err(Error::NoMatch);
}
Ok(GrammarNode::RuleReference(name))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::{parse, parse_with_context};
use std::io::Cursor;
#[test]
fn test_inclass_basic() {
let parser = InClass;
let mut input = Cursor::new(b"[abc]");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
let _utf8_class = result;
}
#[test]
fn test_inclass_with_utf8() {
let parser = InClass;
let mut input = Cursor::new("[αβγ世界]".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
let _utf8_class = result;
}
#[test]
fn test_inclass_with_escapes() {
let parser = InClass;
let mut input = Cursor::new(b"[\\[\\]]");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
let _utf8_class = result;
}
#[test]
fn test_inclass_empty() {
let parser = InClass;
let mut input = Cursor::new(b"[]");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
let _utf8_class = result;
}
#[test]
fn test_inclass_missing_closing_bracket() {
let parser = InClass;
let mut input = Cursor::new(b"[abc");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_err());
}
#[test]
fn test_inclass_functional() {
let parser = InClass;
let mut input = Cursor::new(b"[abc]");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let char_class = parse_with_context(parser, &mut source, &mut context).unwrap();
let mut test_input1 = Cursor::new(b"a");
let mut test_source1 = crate::parser::Source::new(&mut test_input1);
let result1 = parse(char_class.clone(), &mut test_source1);
assert!(result1.is_ok()); if let Ok(matched) = result1 {
assert_eq!(matched, "a"); }
let mut test_input2 = Cursor::new(b"d");
let mut test_source2 = crate::parser::Source::new(&mut test_input2);
let result2 = parse(char_class, &mut test_source2);
assert!(result2.is_err()); }
#[test]
fn test_not_inclass_basic() {
let parser = NotInClass;
let mut input = Cursor::new(b"[^abc]");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
let _utf8_class = result;
}
#[test]
fn test_not_inclass_with_utf8() {
let parser = NotInClass;
let mut input = Cursor::new("[^αβγ世界]".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
let _utf8_class = result;
}
#[test]
fn test_not_inclass_with_escapes() {
let parser = NotInClass;
let mut input = Cursor::new(b"[^\\[\\]]");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
let _utf8_class = result;
}
#[test]
fn test_not_inclass_empty() {
let parser = NotInClass;
let mut input = Cursor::new(b"[^]");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
let _utf8_class = result;
}
#[test]
fn test_not_inclass_missing_closing_bracket() {
let parser = NotInClass;
let mut input = Cursor::new(b"[^abc");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_err());
}
#[test]
fn test_not_inclass_missing_caret() {
let parser = NotInClass;
let mut input = Cursor::new(b"[abc]");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_err());
}
#[test]
fn test_not_inclass_functional() {
let parser = NotInClass;
let mut input = Cursor::new(b"[^abc]");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let char_class = parse_with_context(parser, &mut source, &mut context).unwrap();
let mut test_input1 = Cursor::new(b"d");
let mut test_source1 = crate::parser::Source::new(&mut test_input1);
let result1 = parse(char_class.clone(), &mut test_source1);
assert!(result1.is_ok()); if let Ok(matched) = result1 {
assert_eq!(matched, "d"); }
let mut test_input2 = Cursor::new(b"a");
let mut test_source2 = crate::parser::Source::new(&mut test_input2);
let result2 = parse(char_class, &mut test_source2);
assert!(result2.is_err()); }
#[test]
fn test_grammar_expression_terminal() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"\"hello\"");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
match parse_with_context(parser, &mut source, &mut context) {
Ok(GrammarNode::Terminal(literal)) => {
assert_eq!(literal, b"hello".as_slice().into());
}
_ => panic!("Expected Terminal expression"),
}
}
#[test]
fn test_grammar_expression_digits() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"digits");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
match parse_with_context(parser, &mut source, &mut context) {
Ok(GrammarNode::Digits(_)) => {
}
_ => panic!("Expected Digits expression"),
}
}
#[test]
fn test_grammar_expression_alpha() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"alpha");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
match parse_with_context(parser, &mut source, &mut context) {
Ok(GrammarNode::Alpha(_)) => {
}
_ => panic!("Expected Alpha expression"),
}
}
#[test]
fn test_grammar_expression_inclass() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"[abc]");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
match parse_with_context(parser, &mut source, &mut context) {
Ok(GrammarNode::InClass(_)) => {
}
_ => panic!("Expected InClass expression"),
}
}
#[test]
fn test_grammar_expression_not_inclass() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"[^abc]");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
match parse_with_context(parser, &mut source, &mut context) {
Ok(GrammarNode::NotInClass(_)) => {
}
_ => panic!("Expected NotInClass expression"),
}
}
#[test]
fn test_grammar_expression_no_match() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"@#$%");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_err());
}
#[test]
fn test_grammar_expression_parenthesized_sequence() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"(\"hello\" \"world\")");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok());
}
#[test]
fn test_grammar_expression_alternatives() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"(| \"hello\" \"world\")");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok());
}
#[test]
fn test_alternatives_parser_direct() {
let mut input = Cursor::new(b"(| \"hello\" \"world\")");
let mut source = crate::parser::Source::new(&mut input);
let result = parse_with_context(Alternatives, &mut source, &mut GrammarContext::default());
println!("Direct Alternatives parser result: {result:?}");
assert!(result.is_ok());
}
#[test]
fn test_grammar_expression_repetition() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"(* \"hello\")");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok());
}
#[test]
fn test_grammar_expression_one_or_more() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"(+ \"hello\")");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok());
}
#[test]
fn test_grammar_expression_zero_or_one() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"(? \"hello\")");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok());
}
#[test]
fn test_grammar_expression_nested_sequences() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"(\"hello\" digits \"world\")");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok());
}
#[test]
fn test_grammar_expression_sequence_structure() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"(\"hello\" \"world\" \"test\")");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok());
if let Ok(GrammarNode::Sequential(_sequence)) = result {
} else {
panic!("Expected Sequential expression");
}
}
#[test]
fn test_grammar_expression_alternatives_structure() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"(| \"hello\" \"world\" \"test\")");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok());
if let Ok(GrammarNode::Alternatives(_either)) = result {
} else {
panic!("Expected Alternatives expression");
}
}
#[test]
fn test_grammar_expression_repetition_structure() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"(* \"hello\")");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok());
if let Ok(GrammarNode::ZeroOrMore(_repeat)) = result {
} else {
panic!("Expected ZeroOrMore expression");
}
}
#[test]
fn test_sequence_parsing_basic() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"("hello" "world")"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(result.is_ok(), "Sequence parsing should succeed");
if let Ok(expr) = result {
match expr.node {
GrammarNode::Sequential(_) => {
}
_ => panic!("Expected Sequential expression, got {expr:?}"),
}
}
}
#[test]
fn test_alternatives_parsing_basic() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(| "hello" "world")"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(result.is_ok(), "Alternatives parsing should succeed");
if let Ok(expr) = result {
match expr.node {
GrammarNode::Alternatives(_) => {
}
_ => panic!("Expected Alternatives expression, got {expr:?}"),
}
}
}
#[test]
#[allow(clippy::type_complexity)]
fn test_all_character_class_keywords() {
let test_cases: Vec<(&'static str, Box<dyn Fn(&GrammarNode) -> bool>)> = vec![
(
"digits",
Box::new(|expr| matches!(expr, GrammarNode::Digits(_))),
),
(
"alpha",
Box::new(|expr| matches!(expr, GrammarNode::Alpha(_))),
),
(
"alphanumeric",
Box::new(|expr| matches!(expr, GrammarNode::Alphanumeric(_))),
),
(
"whitespace",
Box::new(|expr| matches!(expr, GrammarNode::Whitespace(_))),
),
(
"hexdigits",
Box::new(|expr| matches!(expr, GrammarNode::HexDigits(_))),
),
(
"udigits",
Box::new(|expr| matches!(expr, GrammarNode::UDigits(_))),
),
(
"ualpha",
Box::new(|expr| matches!(expr, GrammarNode::UAlpha(_))),
),
(
"ualphanumeric",
Box::new(|expr| matches!(expr, GrammarNode::UAlphanumeric(_))),
),
(
"uwhitespace",
Box::new(|expr| matches!(expr, GrammarNode::UWhitespace(_))),
),
];
for (keyword, matcher) in test_cases {
let parser = GrammarParser::new();
let mut input = Cursor::new(keyword.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok(), "Failed to parse keyword: {keyword}");
if let Ok(expr) = result {
assert!(
matcher(&expr),
"Wrong expression type for keyword {keyword}: {expr:?}",
);
}
}
}
#[test]
fn test_terminal_with_escape_sequences() {
let test_cases = vec![
(r#""hello""#, "hello"),
(r#""hello world""#, "hello world"),
(r#""line\nbreak""#, "linenbreak"), (r#""quote\"inside""#, r#"quote"inside"#), (r#""backslash\\here""#, r#"backslash\here"#), (r#""tab\there""#, "tabthere"), ];
for (input, expected) in test_cases {
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok(), "Failed to parse terminal: {input}");
if let Ok(GrammarNode::Terminal(literal)) = result {
assert_eq!(
literal,
expected.as_bytes().into(),
"Wrong literal value for: {input}",
);
} else {
panic!("Expected Terminal expression for: {input}");
}
}
}
#[test]
fn test_character_class_parsing() {
let test_cases = vec![
("[abc]", true), ("[^abc]", true), ("[a-z]", true), ("[αβγ]", true), ("[]", true), ("[^]", true), ];
for (input, should_succeed) in test_cases {
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
if should_succeed {
assert!(result.is_ok(), "Failed to parse character class: {input}");
match result {
Ok(GrammarNode::InClass(_)) | Ok(GrammarNode::NotInClass(_)) => {
}
_ => panic!("Expected character class expression for: {input}"),
}
} else {
assert!(result.is_err(), "Should have failed to parse: {input}");
}
}
}
#[test]
#[allow(clippy::type_complexity)]
fn test_repetition_operators() {
let test_cases: Vec<(&'static str, Box<dyn Fn(&GrammarNode) -> bool>)> = vec![
(
"(* \"hello\")",
Box::new(|expr| matches!(expr, GrammarNode::ZeroOrMore(_))),
),
(
"(+ \"hello\")",
Box::new(|expr| matches!(expr, GrammarNode::OneOrMore(_))),
),
(
"(? \"hello\")",
Box::new(|expr| matches!(expr, GrammarNode::ZeroOrOne(_))),
),
];
for (input, matcher) in test_cases {
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok(), "Failed to parse repetition: {input}");
if let Ok(expr) = result {
assert!(
matcher(&expr),
"Wrong expression type for repetition: {input}",
);
}
}
}
#[test]
#[allow(clippy::type_complexity)]
fn test_separated_repetitions() {
let test_cases: Vec<(&'static str, Box<dyn Fn(&GrammarNode) -> bool>)> = vec![
(
"(*\"item\" / \",\")",
Box::new(|expr| matches!(expr, GrammarNode::ZeroOrMoreSeparated(_))),
),
(
"(+\"item\" / \",\")",
Box::new(|expr| matches!(expr, GrammarNode::OneOrMoreSeparated(_))),
),
];
for (input, matcher) in test_cases {
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(
result.is_ok(),
"Failed to parse separated repetition: {input}",
);
if let Ok(expr) = result {
assert!(
matcher(&expr),
"Wrong expression type for separated repetition: {input}",
);
}
}
}
#[test]
fn test_deeply_nested_sequences() {
let input = "(\"a\" \"b\" \"c\" \"d\" \"e\" \"f\")";
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok(), "Failed to parse deeply nested sequence");
if let Ok(GrammarNode::Sequential(_)) = result {
} else {
panic!("Expected Sequential expression for deeply nested sequence");
}
}
#[test]
fn test_deeply_nested_alternatives() {
let input = "(| \"a\" \"b\" \"c\" \"d\" \"e\" \"f\")";
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok(), "Failed to parse deeply nested alternatives");
if let Ok(GrammarNode::Alternatives(_)) = result {
} else {
panic!("Expected Alternatives expression for deeply nested alternatives");
}
}
#[test]
fn test_nested_mixed_expressions() {
let input = "(\"start\" (| \"option1\" \"option2\") \"end\")";
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok(), "Failed to parse nested mixed expressions");
if let Ok(GrammarNode::Sequential(_)) = result {
} else {
panic!("Expected Sequential expression for nested mixed expressions");
}
}
#[test]
fn test_malformed_parentheses() {
let error_cases = vec!["(\"unclosed", "(\"missing_close\""];
for input in error_cases {
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(matches!(result, Err(crate::result::Error::NoMatch)));
}
let success_cases = vec!["\"unopened\")", "\"missing_open\")"];
for input in success_cases {
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok());
}
let nested_case = "((\"double\"))";
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(nested_case.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok());
}
#[test]
fn test_malformed_character_classes() {
let error_cases = vec!["[unclosed", "[^unclosed"];
for input in error_cases {
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(
result.is_err(),
"Should have failed for malformed character class: {input}",
);
}
}
#[test]
fn test_malformed_terminals() {
let error_cases = vec!["\"unclosed", "\"unterminated\\"];
for input in error_cases {
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(
dbg!(result).is_err(),
"Should have failed for malformed terminal: {input}",
);
}
}
#[test]
fn test_empty_input() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b"");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_err(), "Should fail on empty input");
}
#[test]
fn test_whitespace_only() {
let parser = GrammarParser::new();
let mut input = Cursor::new(b" \t\n ");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_err(), "Should fail on whitespace-only input");
}
#[test]
fn test_very_long_terminal() {
let long_content = "a".repeat(1000);
let input = format!("\"{long_content}\"");
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok(), "Should handle very long terminals");
if let Ok(GrammarNode::Terminal(literal)) = result {
assert_eq!(literal, long_content.as_bytes().into());
} else {
panic!("Expected Terminal expression for very long terminal");
}
}
#[test]
fn test_very_long_character_class() {
let long_chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".repeat(10);
let input = format!("[{long_chars}]");
let parser = GrammarParser::new();
let mut input_cursor = Cursor::new(input.as_bytes());
let mut source = crate::parser::Source::new(&mut input_cursor);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok(), "Should handle very long character classes");
if let Ok(GrammarNode::InClass(_)) = result {
} else {
panic!("Expected InClass expression for very long character class");
}
}
#[test]
fn test_expression_parser_terminal() {
let expr = GrammarNode::Terminal(Literal::from_str("hello"));
let mut input = Cursor::new(b"hello");
let mut source = crate::parser::Source::new(&mut input);
let result = parse_with_context(expr, &mut source, &mut GrammarContext::default());
assert!(result.is_ok(), "Expression parser should work for Terminal");
if let Ok(GrammarResult::Literal(bytes)) = result {
assert_eq!(bytes, b"hello".to_vec());
} else {
panic!("Expected Literal result");
}
}
#[test]
fn test_expression_parser_digits() {
let expr = GrammarNode::Digits(Utf8Class::digits());
let mut input = Cursor::new(b"12345");
let mut source = crate::parser::Source::new(&mut input);
let result = parse_with_context(expr, &mut source, &mut GrammarContext::default());
assert!(result.is_ok(), "Expression parser should work for Digits");
if let Ok(GrammarResult::Unicode(chars)) = result {
assert_eq!(chars, "12345");
} else {
panic!("Expected Bytes result");
}
}
#[test]
fn test_expression_parser_sequence() {
let hello_expr = GrammarNode::Terminal(Literal::from_str("hello"));
let world_expr = GrammarNode::Terminal(Literal::from_str("world"));
let sequence = Sequence::new(Box::new(hello_expr), Box::new(world_expr));
let expr = GrammarNode::Sequential(sequence);
let mut input = Cursor::new(b"helloworld");
let mut source = crate::parser::Source::new(&mut input);
let result = parse_with_context(expr, &mut source, &mut GrammarContext::default());
assert!(
result.is_ok(),
"Expression parser should work for Sequential"
);
if let Ok(GrammarResult::Sequence(results)) = result {
assert_eq!(results.len(), 2);
let combined_bytes = GrammarResult::Sequence(results).to_bytes();
assert_eq!(combined_bytes, b"helloworld".to_vec());
} else {
panic!("Expected Sequence result");
}
}
#[test]
fn test_expression_parser_alternatives() {
let hello_expr = GrammarNode::Terminal(Literal::from_str("hello"));
let world_expr = GrammarNode::Terminal(Literal::from_str("world"));
let either = Either::new(Box::new(hello_expr), Box::new(world_expr));
let expr = GrammarNode::Alternatives(either);
let mut input1 = Cursor::new(b"hello");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 =
parse_with_context(expr.clone(), &mut source1, &mut GrammarContext::default());
assert!(
result1.is_ok(),
"Expression parser should work for first alternative"
);
if let Ok(GrammarResult::Alternative(boxed_result)) = result1 {
assert_eq!(boxed_result.to_bytes(), b"hello".to_vec());
} else {
panic!("Expected Alternative result");
}
let mut input2 = Cursor::new(b"world");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse_with_context(expr, &mut source2, &mut GrammarContext::default());
assert!(
result2.is_ok(),
"Expression parser should work for second alternative"
);
if let Ok(GrammarResult::Alternative(boxed_result)) = result2 {
assert_eq!(boxed_result.to_bytes(), b"world".to_vec());
} else {
panic!("Expected Alternative result");
}
}
#[test]
fn test_expression_parser_zero_or_more() {
let a_expr = GrammarNode::Terminal(Literal::from_str("a"));
let repeat = Repeat::new(Box::new(a_expr));
let expr = GrammarNode::ZeroOrMore(repeat);
let mut input = Cursor::new(b"aaab");
let mut source = crate::parser::Source::new(&mut input);
let result = parse_with_context(expr, &mut source, &mut GrammarContext::default());
assert!(
result.is_ok(),
"Expression parser should work for ZeroOrMore"
);
if let Ok(GrammarResult::Repetition(results)) = result {
let combined_bytes = GrammarResult::Repetition(results).to_bytes();
assert_eq!(combined_bytes, b"aaa".to_vec());
} else {
panic!("Expected Repetition result");
}
}
#[test]
fn test_expression_parser_zero_or_one() {
let maybe_expr = GrammarNode::Terminal(Literal::from_str("maybe"));
let option = Repeat::with_max(Box::new(maybe_expr), 1);
let expr = GrammarNode::ZeroOrOne(option);
let mut input = Cursor::new(b"maybe");
let mut source = crate::parser::Source::new(&mut input);
let result = parse_with_context(expr, &mut source, &mut GrammarContext::default());
assert!(
result.is_ok(),
"Expression parser should work for ZeroOrOne"
);
if let Ok(GrammarResult::Optional(Some(boxed_result))) = result {
assert_eq!(boxed_result.to_bytes(), b"maybe".to_vec());
} else {
panic!("Expected Optional(Some) result");
}
}
#[test]
fn test_expression_parser_sequential_end() {
let hello_expr = GrammarNode::Terminal(Literal::from_str("hello"));
let sequence = Sequence::new(Box::new(hello_expr), ());
let expr = GrammarNode::SequentialEnd(sequence);
let mut input = Cursor::new(b"hello");
let mut source = crate::parser::Source::new(&mut input);
let result = parse_with_context(expr, &mut source, &mut GrammarContext::default());
assert!(
result.is_ok(),
"Expression parser should work for SequentialEnd"
);
if let Ok(GrammarResult::Literal(bytes)) = result {
assert_eq!(bytes, b"hello".to_vec());
} else {
panic!("Expected Literal result from SequentialEnd");
}
}
#[test]
fn test_expression_parser_complex_nested() {
let start_expr = GrammarNode::Terminal(Literal::from_str("start"));
let digits_expr = GrammarNode::Digits(Utf8Class::digits());
let inner_seq = Sequence::new(Box::new(digits_expr), ());
let inner_expr = GrammarNode::SequentialEnd(inner_seq);
let outer_seq = Sequence::new(Box::new(start_expr), Box::new(inner_expr));
let expr = GrammarNode::Sequential(outer_seq);
let mut input = Cursor::new(b"start123");
let mut source = crate::parser::Source::new(&mut input);
let result = parse_with_context(expr, &mut source, &mut GrammarContext::default());
assert!(
result.is_ok(),
"Expression parser should work for complex nested structures"
);
if let Ok(GrammarResult::Sequence(results)) = result {
let combined_bytes = GrammarResult::Sequence(results).to_bytes();
assert_eq!(combined_bytes, b"start123".to_vec());
} else {
panic!("Expected Sequence result from complex nested structure");
}
}
#[test]
fn test_expression_result_to_bytes() {
let literal_result = GrammarResult::Literal(b"hello".to_vec());
assert_eq!(literal_result.to_bytes(), b"hello".to_vec());
let unicode_result = GrammarResult::Unicode("world".to_string());
assert_eq!(unicode_result.to_bytes(), b"world".to_vec());
let bytes_result = GrammarResult::Bytes(b"test".to_vec());
assert_eq!(bytes_result.to_bytes(), b"test".to_vec());
let seq_result = GrammarResult::Sequence(vec![
GrammarResult::Literal(b"hello".to_vec()),
GrammarResult::Literal(b"world".to_vec()),
]);
assert_eq!(seq_result.to_bytes(), b"helloworld".to_vec());
let alt_result =
GrammarResult::Alternative(Box::new(GrammarResult::Literal(b"choice".to_vec())));
assert_eq!(alt_result.to_bytes(), b"choice".to_vec());
let rep_result = GrammarResult::Repetition(vec![
GrammarResult::Literal(b"a".to_vec()),
GrammarResult::Literal(b"b".to_vec()),
GrammarResult::Literal(b"c".to_vec()),
]);
assert_eq!(rep_result.to_bytes(), b"abc".to_vec());
let opt_some_result =
GrammarResult::Optional(Some(Box::new(GrammarResult::Literal(b"maybe".to_vec()))));
assert_eq!(opt_some_result.to_bytes(), b"maybe".to_vec());
let opt_none_result = GrammarResult::Optional(None);
assert_eq!(opt_none_result.to_bytes(), Vec::<u8>::new());
let empty_result = GrammarResult::Empty;
assert_eq!(empty_result.to_bytes(), Vec::<u8>::new());
}
#[test]
fn test_expression_result_is_empty() {
assert!(!GrammarResult::Literal(b"hello".to_vec()).is_empty());
assert!(!GrammarResult::Unicode("world".to_string()).is_empty());
assert!(!GrammarResult::Bytes(b"test".to_vec()).is_empty());
assert!(!GrammarResult::Sequence(vec![]).is_empty()); assert!(
!GrammarResult::Alternative(Box::new(GrammarResult::Literal(b"a".to_vec()))).is_empty()
);
assert!(!GrammarResult::Repetition(vec![]).is_empty()); assert!(
!GrammarResult::Optional(Some(Box::new(GrammarResult::Literal(b"a".to_vec()))))
.is_empty()
);
assert!(GrammarResult::Optional(None).is_empty());
assert!(GrammarResult::Empty.is_empty());
}
#[test]
fn test_id_implementation_grammar_expression() {
let grammar1 = GrammarParser::new();
let grammar2 = GrammarParser::new();
assert_eq!(
<GrammarParser as crate::parser::Parser<()>>::id(&grammar1),
<GrammarParser as crate::parser::Parser<()>>::id(&grammar2),
"GrammarParser::new() instances should have same ID since they have no parameters"
);
}
#[test]
fn test_id_implementation_different_expressions() {
let expr1 = GrammarNode::Terminal(Literal::from_str("hello"));
let expr2 = GrammarNode::Terminal(Literal::from_str("world"));
let expr3 = GrammarNode::Digits(Utf8Class::digits());
assert_ne!(
<GrammarNode as crate::parser::Parser<GrammarContext>>::id(&expr1),
<GrammarNode as crate::parser::Parser<GrammarContext>>::id(&expr2),
"Different Expression instances should have different IDs to avoid cache collisions"
);
assert_ne!(
<GrammarNode as crate::parser::Parser<GrammarContext>>::id(&expr1),
<GrammarNode as crate::parser::Parser<GrammarContext>>::id(&expr3),
"Different Expression variants should have different IDs to avoid cache collisions"
);
assert_ne!(
<GrammarNode as crate::parser::Parser<GrammarContext>>::id(&expr2),
<GrammarNode as crate::parser::Parser<GrammarContext>>::id(&expr3),
"Different Expression variants should have different IDs to avoid cache collisions"
);
}
#[test]
fn test_id_implementation_same_expressions() {
let expr1 = GrammarNode::Terminal(Literal::from_str("hello"));
let expr2 = GrammarNode::Terminal(Literal::from_str("hello"));
assert_eq!(
<GrammarNode as crate::parser::Parser<GrammarContext>>::id(&expr1),
<GrammarNode as crate::parser::Parser<GrammarContext>>::id(&expr2),
"Identical Expression instances should have the same ID for cache efficiency"
);
}
#[test]
fn test_id_implementation_expression_cache_correctness() {
use std::io::Cursor;
let expr1 = GrammarNode::Terminal(Literal::from_str("hello"));
let expr2 = GrammarNode::Terminal(Literal::from_str("world"));
let mut input1 = Cursor::new(b"hello");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse_with_context(expr1, &mut source1, &mut GrammarContext::default());
assert!(result1.is_ok(), "First parse should succeed");
let mut input2 = Cursor::new(b"world");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse_with_context(expr2, &mut source2, &mut GrammarContext::default());
assert!(result2.is_ok(), "Second parse should succeed");
if let (Ok(GrammarResult::Literal(bytes1)), Ok(GrammarResult::Literal(bytes2))) =
(result1, result2)
{
assert_ne!(
bytes1, bytes2,
"Results should be different - no cache collision should occur"
);
assert_eq!(bytes1, b"hello".to_vec());
assert_eq!(bytes2, b"world".to_vec());
} else {
panic!("Expected literal results");
}
}
#[test]
fn test_id_implementation_box_expression() {
let boxed_expr1 = Box::new(GrammarNode::Terminal(Literal::from_str("test1")));
let boxed_expr2 = Box::new(GrammarNode::Terminal(Literal::from_str("test2")));
let id1 = <Box<GrammarNode> as crate::parser::Parser<GrammarContext>>::id(&boxed_expr1);
let id2 = <Box<GrammarNode> as crate::parser::Parser<GrammarContext>>::id(&boxed_expr2);
assert_ne!(
id1, id2,
"Different Box<Grammar> instances should have different IDs to avoid cache collisions"
);
}
#[test]
fn test_id_implementation_same_box_expression() {
let boxed_expr1 = Box::new(GrammarNode::Terminal(Literal::from_str("test")));
let boxed_expr2 = Box::new(GrammarNode::Terminal(Literal::from_str("test")));
assert_eq!(
<Box<GrammarNode> as crate::parser::Parser<GrammarContext>>::id(&boxed_expr1),
<Box<GrammarNode> as crate::parser::Parser<GrammarContext>>::id(&boxed_expr2),
"Identical Box<GrammarNode> instances should have the same ID for cache efficiency"
);
}
#[test]
fn test_id_implementation_different_keyword_parsers() {
let digits_parser = Digits;
let alpha_parser = Alpha;
let whitespace_parser = Whitespace;
assert_ne!(
<Digits as crate::parser::Parser<GrammarContext>>::id(&digits_parser),
<Alpha as crate::parser::Parser<GrammarContext>>::id(&alpha_parser),
"Different parser types should have different IDs"
);
assert_ne!(
<Alpha as crate::parser::Parser<GrammarContext>>::id(&alpha_parser),
<Whitespace as crate::parser::Parser<GrammarContext>>::id(&whitespace_parser),
"Different parser types should have different IDs"
);
assert_ne!(
<Digits as crate::parser::Parser<GrammarContext>>::id(&digits_parser),
<Whitespace as crate::parser::Parser<GrammarContext>>::id(&whitespace_parser),
"Different parser types should have different IDs"
);
let digits_parser2 = Digits;
assert_eq!(
<Digits as crate::parser::Parser<GrammarContext>>::id(&digits_parser),
<Digits as crate::parser::Parser<GrammarContext>>::id(&digits_parser2),
"Same parser type instances should have same ID"
);
}
#[test]
fn test_read_until_parse_functionality() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< "end" "captured")"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(result.is_ok(), "ReadUntilParse parsing should succeed");
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntilParse(_, _) => {
}
_ => panic!("Expected ReadUntilParse expression, got {expr:?}"),
}
}
}
#[test]
fn test_read_until_basic_parsing() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< "end")"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(result.is_ok(), "ReadUntil parsing should succeed");
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntil(_) => {
}
_ => panic!("Expected ReadUntil expression, got {expr:?}"),
}
}
}
#[test]
fn test_read_until_complex_end_condition() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< (| "end" "stop"))"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(
result.is_ok(),
"ReadUntil with complex end condition should succeed"
);
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntil(_) => {
}
_ => panic!("Expected ReadUntil expression, got {expr:?}"),
}
}
}
#[test]
fn test_read_until_parse_complex_parser() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< "end" (| "hello" "world"))"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(
result.is_ok(),
"ReadUntilParse with complex parser should succeed"
);
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntilParse(_, _) => {
}
_ => panic!("Expected ReadUntilParse expression, got {expr:?}"),
}
}
}
#[test]
fn test_read_until_with_whitespace() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< "end" )"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(result.is_ok(), "ReadUntil with whitespace should succeed");
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntil(_) => {
}
_ => panic!("Expected ReadUntil expression, got {expr:?}"),
}
}
}
#[test]
fn test_read_until_parse_with_whitespace() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< "end" "content" )"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(
result.is_ok(),
"ReadUntilParse with whitespace should succeed"
);
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntilParse(_, _) => {
}
_ => panic!("Expected ReadUntilParse expression, got {expr:?}"),
}
}
}
#[test]
fn test_read_until_nested() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(* (< "end"))"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(result.is_ok(), "Nested ReadUntil should succeed");
if let Ok(expr) = result {
match expr.node {
GrammarNode::ZeroOrMore(_) => {
}
_ => panic!("Expected ZeroOrMore expression containing ReadUntil, got {expr:?}",),
}
}
}
#[test]
fn test_read_until_parse_nested() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(+ (< "end" "content"))"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(result.is_ok(), "Nested ReadUntilParse should succeed");
if let Ok(expr) = result {
match expr.node {
GrammarNode::OneOrMore(_) => {
}
_ => {
panic!("Expected OneOrMore expression containing ReadUntilParse, got {expr:?}",)
}
}
}
}
#[test]
fn test_read_until_in_sequence() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"("start" (< "end") "finish")"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(result.is_ok(), "ReadUntil in sequence should succeed");
if let Ok(expr) = result {
match expr.node {
GrammarNode::Sequential(_) => {
}
_ => panic!("Expected Sequential expression containing ReadUntil, got {expr:?}",),
}
}
}
#[test]
fn test_read_until_parse_in_alternatives() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(| (< "end" "content") "fallback")"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(
result.is_ok(),
"ReadUntilParse in alternatives should succeed"
);
if let Ok(expr) = result {
match expr.node {
GrammarNode::Alternatives(_) => {
}
_ => panic!(
"Expected Alternatives expression containing ReadUntilParse, got {expr:?}",
),
}
}
}
#[test]
fn test_read_until_with_keyword_end() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< digits)"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(result.is_ok(), "ReadUntil with keyword end should succeed");
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntil(_) => {
}
_ => panic!("Expected ReadUntil expression, got {expr:?}"),
}
}
}
#[test]
fn test_read_until_parse_with_keyword_parser() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< "end" alpha)"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(
result.is_ok(),
"ReadUntilParse with keyword parser should succeed"
);
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntilParse(_, _) => {
}
_ => panic!("Expected ReadUntilParse expression, got {expr:?}"),
}
}
}
#[test]
fn test_read_until_with_character_class() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< [abc])"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(
result.is_ok(),
"ReadUntil with character class should succeed"
);
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntil(_) => {
}
_ => panic!("Expected ReadUntil expression, got {expr:?}"),
}
}
}
#[test]
fn test_read_until_parse_with_character_class_parser() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< "end" [0-9])"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(
result.is_ok(),
"ReadUntilParse with character class parser should succeed"
);
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntilParse(_, _) => {
}
_ => panic!("Expected ReadUntilParse expression, got {expr:?}"),
}
}
}
#[test]
fn test_read_until_malformed_missing_closing_paren() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< "end""#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(result.is_err(), "Malformed ReadUntil should fail");
}
#[test]
fn test_read_until_parse_malformed_missing_content_parser() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< "end")"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(
result.is_ok(),
"ReadUntil (not ReadUntilParse) should succeed"
);
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntil(_) => {
}
_ => panic!("Expected ReadUntil expression, got {expr:?}"),
}
}
}
#[test]
fn test_read_until_parse_malformed_missing_closing_paren() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< "end" "content""#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(result.is_err(), "Malformed ReadUntilParse should fail");
}
#[test]
fn test_read_until_deeply_nested_expressions() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< ("prefix" (| "end1" "end2")))"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(
result.is_ok(),
"ReadUntil with deeply nested end should succeed"
);
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntil(_) => {
}
_ => panic!("Expected ReadUntil expression, got {expr:?}"),
}
}
}
#[test]
fn test_read_until_parse_deeply_nested_content_parser() {
let grammar = GrammarParser::new();
let mut input = Cursor::new(r#"(< "end" (* (| "hello" digits)))"#.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar, &mut source);
assert!(
result.is_ok(),
"ReadUntilParse with deeply nested content parser should succeed"
);
if let Ok(expr) = result {
match expr.node {
GrammarNode::ReadUntilParse(_, _) => {
}
_ => panic!("Expected ReadUntilParse expression, got {expr:?}"),
}
}
}
#[test]
fn test_start_directive_basic() {
let parser = StartDirective;
let mut input = Cursor::new(b"@start expr");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
assert_eq!(result, "expr".to_string());
}
#[test]
fn test_start_directive_with_whitespace() {
let parser = StartDirective;
let mut input = Cursor::new(b"@start main");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
assert_eq!(result, "main".to_string());
}
#[test]
fn test_start_directive_invalid() {
let parser = StartDirective;
let mut input = Cursor::new(b"start expr");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_err());
}
#[test]
fn test_rule_definition_basic() {
let parser = RuleDefinition;
let mut input = Cursor::new(b"expr = digits");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
assert_eq!(result.0, "expr".to_string());
match result.1 {
GrammarNode::Digits(_) => {}
_ => panic!("Expected Digits node, got {:?}", result.1),
}
}
#[test]
fn test_rule_definition_with_whitespace() {
let parser = RuleDefinition;
let mut input = Cursor::new(b"term = alpha");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
assert_eq!(result.0, "term".to_string());
match result.1 {
GrammarNode::Alpha(_) => {}
_ => panic!("Expected Alpha node, got {:?}", result.1),
}
}
#[test]
fn test_rule_reference_parser_with_known_rule() {
let parser = RuleReferenceParser;
let mut input = Cursor::new(b"expr");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
context.rules.insert(
"expr".to_string(),
GrammarNode::Digits(crate::utf8class::Utf8Class::digits()),
);
let result = parse_with_context(parser, &mut source, &mut context).unwrap();
match result {
GrammarNode::RuleReference(name) => assert_eq!(name, "expr".to_string()),
_ => panic!("Expected RuleReference, got {result:?}"),
}
}
#[test]
fn test_rule_reference_parser_with_unknown_rule() {
let parser = RuleReferenceParser;
let mut input = Cursor::new(b"unknown");
let mut source = crate::parser::Source::new(&mut input);
let mut context = GrammarContext::default();
let result = parse_with_context(parser, &mut source, &mut context);
assert!(result.is_ok());
if let GrammarNode::RuleReference(name) = result.unwrap() {
assert_eq!(name, "unknown");
} else {
panic!("Expected RuleReference");
}
}
#[test]
fn test_grammar_parser_basic_expressions_still_work() {
let grammar_parser = GrammarParser::new();
let mut input = Cursor::new(b"digits");
let mut source = crate::parser::Source::new(&mut input);
let grammar = parse_with_context(
grammar_parser.clone(),
&mut source,
&mut GrammarContext::default(),
)
.expect("Should parse 'digits' keyword");
let mut test_input = Cursor::new(b"123");
let mut test_source = crate::parser::Source::new(&mut test_input);
let result = parse_with_context(grammar, &mut test_source, &mut GrammarContext::default())
.expect("Should parse '123' with digits grammar");
match result {
GrammarResult::Unicode(s) => assert_eq!(s, "123"),
_ => panic!("Expected Unicode result for digits, got {result:?}"),
}
}
#[test]
fn test_grammar_parser_terminal_strings() {
let grammar_parser = GrammarParser::new();
let mut input = Cursor::new(b"\"hello\"");
let mut source = crate::parser::Source::new(&mut input);
let grammar =
parse_with_context(grammar_parser, &mut source, &mut GrammarContext::default())
.expect("Should parse terminal string");
let mut test_input = Cursor::new(b"hello");
let mut test_source = crate::parser::Source::new(&mut test_input);
let result = parse_with_context(grammar, &mut test_source, &mut GrammarContext::default())
.expect("Should parse 'hello' with terminal grammar");
match result {
GrammarResult::Literal(bytes) => assert_eq!(bytes, b"hello"),
_ => panic!("Expected Literal result for terminal, got {result:?}"),
}
}
#[test]
fn test_rule_reference_succeeds_without_context() {
let grammar_parser = GrammarParser::new();
let mut input = Cursor::new(b"unknownrule");
let mut source = crate::parser::Source::new(&mut input);
let result =
parse_with_context(grammar_parser, &mut source, &mut GrammarContext::default());
assert!(
result.is_ok(),
"Parsing unknown identifier should succeed as a rule reference"
);
}
#[test]
fn test_start_directive_parsed_fail_if_no_such_rule() {
let grammar_parser = GrammarParser::new();
let mut input = Cursor::new(b"@start expr\nother = alpha");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(grammar_parser, &mut source);
result.expect_err(
"GrammarParser should fail if @start directive refers to an nonexistent rule",
);
}
#[test]
fn test_rule_reference_with_context() {
let grammar_parser = GrammarParser::new();
let mut input1 = Cursor::new(b"number = digits");
let mut source1 = crate::parser::Source::new(&mut input1);
let _grammar1 = parse_with_context(
grammar_parser.clone(),
&mut source1,
&mut GrammarContext::default(),
)
.expect("Should parse rule definition");
let mut input2 = Cursor::new(b"number");
let mut source2 = crate::parser::Source::new(&mut input2);
let result =
parse_with_context(grammar_parser, &mut source2, &mut GrammarContext::default());
assert!(
result.is_ok(),
"Rule reference parsing should succeed, execution will fail if rule doesn't exist"
);
}
}