mod line;
mod location;
mod rule_line;
#[cfg(test)]
mod tests;
use crate::error::{ParseError, ParseErrorKind, ParseLimitError};
use crate::program::{ParseLimits, RuleSet, SourceByteCount};
use crate::source::{ProgramSource, SourceLineNumber};
use line::RawSourceLine;
use location::source_line_number;
pub(crate) fn parse_rules_impl(
source: ProgramSource<'_>,
limits: ParseLimits,
) -> Result<RuleSet, ParseError> {
ensure_source_within_limit(source, limits)?;
let mut rule_set = RuleSet::new();
for (zero_based_line, raw_line) in source.as_bytes().split(|&byte| byte == b'\n').enumerate() {
let line_number = source_line_number(zero_based_line)?;
let compact_code = RawSourceLine::new(line_number, raw_line, limits.code_line_byte_limit())
.into_code_line()?
.into_compact_line()?;
let Some(non_empty_code) = compact_code.into_non_empty() else {
continue;
};
let parsed_rule = non_empty_code
.into_rule_syntax()?
.parse(limits.payload_byte_limit())?;
rule_set.push_parsed_rule(parsed_rule, limits.rule_limit())?;
}
Ok(rule_set)
}
fn ensure_source_within_limit(
source: ProgramSource<'_>,
limits: ParseLimits,
) -> Result<(), ParseError> {
let attempted_len = SourceByteCount::new(source.as_bytes().len());
let limit = limits.source_byte_limit();
if attempted_len.get() <= limit.get() {
return Ok(());
}
Err(ParseError::at_line(
SourceLineNumber::ONE,
ParseErrorKind::Limit(ParseLimitError::source(limit, attempted_len)),
))
}