mod compile;
mod executor;
mod forking;
mod joins;
mod keyword_lexing;
mod lexing;
mod load;
mod operations;
mod ordering;
mod parting;
mod precedence;
mod program;
mod reducing;
mod repetition;
mod results;
mod roots;
mod rules;
mod separators;
mod text;
mod token_conflicts;
mod token_ranks;
mod tree;
mod walk;
use std::cell::{Cell, RefCell};
use std::collections::HashSet;
use std::fmt;
use std::rc::Rc;
pub use load::GrammarRuntimeError;
pub use operations::OperationValue;
pub use tree::{Ambiguity, LeafText, SyntaxAttributes, SyntaxTree};
use super::Grammar;
use super::decorators::{decorate_grammar, decorate_syntax_tree};
use crate::decorators::{DecoratorError, DecoratorLevel, DecoratorSet};
use executor::Executor;
use operations::Abort;
use program::{Compiled, SettlingStep};
use results::{Expectations, KeywordLexing, Outcome, Shared, TokenOrder};
const DEFAULT_MAX_DEPTH: usize = 1000;
const DEFAULT_MEMO_LIMIT: usize = 1_000_000;
const DEFAULT_MEMORY_LIMIT: usize = 2_000_000;
const PARSE_STACK: usize = 256 * 1024 * 1024;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct FeatureParseOptions {
pub start_rule: Option<String>,
pub max_depth: Option<usize>,
pub step_limit: Option<usize>,
pub memo_limit: Option<usize>,
pub memory_limit: Option<usize>,
pub reject_ambiguity: Option<bool>,
pub accept_recovery: Option<bool>,
pub error_recovery: Option<bool>,
pub max_repairs: Option<usize>,
pub decorators: DecoratorSet,
}
impl FeatureParseOptions {
fn over(&self, base: &Self) -> Self {
Self {
start_rule: self.start_rule.clone().or_else(|| base.start_rule.clone()),
max_depth: self.max_depth.or(base.max_depth),
step_limit: self.step_limit.or(base.step_limit),
memo_limit: self.memo_limit.or(base.memo_limit),
memory_limit: self.memory_limit.or(base.memory_limit),
reject_ambiguity: self.reject_ambiguity.or(base.reject_ambiguity),
accept_recovery: self.accept_recovery.or(base.accept_recovery),
error_recovery: self.error_recovery.or(base.error_recovery),
max_repairs: self.max_repairs.or(base.max_repairs),
decorators: if self.decorators.is_empty() {
base.decorators.clone()
} else {
self.decorators.clone()
},
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ParseRejection {
pub reason: &'static str,
pub offset: Option<usize>,
pub line: Option<usize>,
pub column: Option<usize>,
pub expected: Option<Vec<String>>,
pub limit: Option<usize>,
}
impl ParseRejection {
const fn limited(reason: &'static str, limit: usize) -> Self {
Self {
reason,
offset: None,
line: None,
column: None,
expected: None,
limit: Some(limit),
}
}
fn positioned(reason: &'static str, bytes: &[u8], offset: usize) -> Self {
let (line, column) = text::line_and_column(bytes, offset);
Self {
reason,
offset: Some(offset),
line: Some(line),
column: Some(column),
expected: None,
limit: None,
}
}
}
impl fmt::Display for ParseRejection {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let place = match (self.line, self.column) {
(Some(line), Some(column)) => format!(" at line {line} column {column}"),
_ => String::new(),
};
let limit = self.limit.unwrap_or_default();
match self.reason {
"syntax" => {
let expected = self.expected.as_deref().unwrap_or_default();
let expected = if expected.is_empty() {
"nothing more".to_owned()
} else {
expected.join(", ")
};
write!(formatter, "syntax error{place}: expected {expected}")
}
"nestingDepth" => write!(formatter, "the input nests deeper than {limit} rules"),
"stepLimit" => write!(formatter, "the parse needed more than {limit} steps"),
"memoryBudget" => write!(formatter, "the parse needed more than {limit} memo cells"),
"recovered" => write!(formatter, "the input needed error recovery{place}"),
"ambiguity" => write!(formatter, "the input is ambiguous{place}"),
reason => write!(formatter, "the input is rejected ({reason})"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ParseOutcome {
pub ok: bool,
pub tree: Option<SyntaxTree>,
pub ambiguities: Vec<Ambiguity>,
pub rejection: Option<ParseRejection>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GrammarParseError {
pub rejection: ParseRejection,
}
impl fmt::Display for GrammarParseError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.rejection.fmt(formatter)
}
}
impl std::error::Error for GrammarParseError {}
pub type GrammarResolver<'a> = &'a dyn Fn(&str) -> Option<Grammar>;
#[derive(Debug)]
pub struct FeatureGrammarParser {
compiled: Compiled,
options: FeatureParseOptions,
}
pub fn compile_feature_grammar(
grammar: &Grammar,
resolve_grammar: Option<GrammarResolver<'_>>,
options: FeatureParseOptions,
) -> Result<FeatureGrammarParser, GrammarRuntimeError> {
let decorated = decorate_grammar(grammar, &options.decorators, DecoratorLevel::GrammarRule)
.map_err(decorator_error)?;
Ok(FeatureGrammarParser {
compiled: load::load(&decorated, resolve_grammar)?,
options,
})
}
impl FeatureGrammarParser {
pub fn parse_tree(
&self,
source: &[u8],
options: &FeatureParseOptions,
) -> Result<ParseOutcome, GrammarRuntimeError> {
let options = options.over(&self.options);
let program = &self.compiled.programs[self.compiled.main];
let start_name = options
.start_rule
.clone()
.or_else(|| program.start.clone())
.unwrap_or_default();
let Some(&start_rule) = program.rule_index.get(&start_name) else {
return Err(GrammarRuntimeError {
reason: "reference",
message: format!("undefined start rule {start_name}"),
});
};
let compiled = &self.compiled;
let mut outcome = std::thread::scope(|scope| {
std::thread::Builder::new()
.stack_size(PARSE_STACK)
.spawn_scoped(scope, || {
parse_program(compiled, start_rule, source, &options)
})
.map_or_else(
|_| parse_program(compiled, start_rule, source, &options),
|handle| {
handle
.join()
.unwrap_or_else(|panic| std::panic::resume_unwind(panic))
},
)
});
if let Some(tree) = &outcome.tree {
outcome.tree = Some(
decorate_syntax_tree(tree, &options.decorators, source).map_err(decorator_error)?,
);
}
Ok(outcome)
}
pub fn parse(
&self,
source: &[u8],
options: &FeatureParseOptions,
) -> Result<SyntaxTree, GrammarParseError> {
let outcome = self
.parse_tree(source, options)
.map_err(|_| GrammarParseError {
rejection: ParseRejection::positioned("syntax", source, 0),
})?;
match (outcome.rejection, outcome.tree) {
(None, Some(tree)) => Ok(tree),
(rejection, _) => Err(GrammarParseError {
rejection: rejection
.unwrap_or_else(|| ParseRejection::positioned("syntax", source, 0)),
}),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ParseWithGrammarError {
Runtime(GrammarRuntimeError),
Parse(GrammarParseError),
}
impl fmt::Display for ParseWithGrammarError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Runtime(error) => error.fmt(formatter),
Self::Parse(error) => error.fmt(formatter),
}
}
}
impl std::error::Error for ParseWithGrammarError {}
pub fn parse_with_grammar(
grammar: &Grammar,
source: &[u8],
options: &FeatureParseOptions,
) -> Result<SyntaxTree, ParseWithGrammarError> {
compile_feature_grammar(grammar, None, options.clone())
.map_err(ParseWithGrammarError::Runtime)?
.parse(source, options)
.map_err(ParseWithGrammarError::Parse)
}
fn decorator_error(error: DecoratorError) -> GrammarRuntimeError {
GrammarRuntimeError {
reason: "decorator",
message: error.message,
}
}
const DEFAULT_MAX_REPAIRS: usize = 32;
fn repair_parse(
failed: Outcome,
max_repairs: usize,
attempt: impl Fn(Option<&HashSet<usize>>) -> Result<Outcome, Abort>,
) -> Result<Outcome, Abort> {
let mut points = HashSet::new();
let mut outcome = failed;
while points.len() < max_repairs {
let Outcome::Failed {
farthest,
element_farthest,
..
} = &outcome
else {
break;
};
if !points.insert(element_farthest.unwrap_or(*farthest)) {
break;
}
outcome = match attempt(Some(&points)) {
Err(Abort::StepLimit) => break,
attempted => attempted?,
};
if matches!(outcome, Outcome::Parsed(_)) {
return Ok(outcome);
}
}
Ok(match outcome {
Outcome::Failed {
partial: Some(partial),
..
} => Outcome::Parsed(partial),
outcome => outcome,
})
}
fn parse_program(
compiled: &Compiled,
start_rule: usize,
bytes: &[u8],
options: &FeatureParseOptions,
) -> ParseOutcome {
let max_depth = options.max_depth.unwrap_or(DEFAULT_MAX_DEPTH);
let limit = options.step_limit.unwrap_or_else(|| {
(100_000 + 1000 * bytes.len())
* if options.error_recovery == Some(true) {
2
} else {
1
}
});
let refused = |rejection, tree| ParseOutcome {
ok: false,
tree,
ambiguities: Vec::new(),
rejection: Some(rejection),
};
let tokens = compiled.programs[compiled.main]
.token_ranks
.as_ref()
.filter(|_| {
compiled.programs[compiled.main]
.settling
.has(SettlingStep::Tokens)
})
.map(|ranks| TokenOrder {
ranks,
bytes,
orders: &compiled.programs[compiled.main].precedence_orders,
grammar: &compiled.programs[compiled.main].grammar,
trivia: &compiled.programs[compiled.main].trivia,
});
let keywords = tokens.map(|_| RefCell::new(KeywordLexing::default()));
let expectations = RefCell::new(Expectations::default());
let memory_limit = options.memory_limit.unwrap_or(DEFAULT_MEMORY_LIMIT);
let cells = Rc::new(Cell::new(0));
let attempt = |points: Option<&HashSet<usize>>| loop {
let mut record = expectations.take();
record.restart();
let shared = Shared {
steps: Cell::new(0),
limit,
frames: Cell::new(0),
memo_limit: options.memo_limit.unwrap_or(DEFAULT_MEMO_LIMIT),
cells: Rc::clone(&cells),
memory_limit,
expectations: RefCell::new(record),
};
let outcome = {
let mut executor = Executor::new(
compiled,
compiled.main,
bytes,
0,
bytes.len(),
&shared,
max_depth,
);
executor.repair_points = points.cloned();
executor.keywords = keywords.as_ref();
executor.run(start_rule)
};
let record = shared.expectations.into_inner();
let stale = record.stale;
expectations.replace(record);
if outcome.is_err() || !stale {
break outcome;
}
};
let recovery = options.error_recovery == Some(true);
let outcome = loop {
let outcome =
attempt(recovery.then(HashSet::new).as_ref()).and_then(|outcome| match outcome {
Outcome::Failed { .. } if recovery => repair_parse(
outcome,
options.max_repairs.unwrap_or(DEFAULT_MAX_REPAIRS),
attempt,
),
outcome => Ok(outcome),
});
if let (Ok(Outcome::Parsed(root)), Some(keywords), Some(tokens)) =
(&outcome, &keywords, tokens)
&& keywords.borrow_mut().conflicts(
root,
tokens,
&compiled.programs[compiled.main].rules,
)
{
continue;
}
break outcome;
};
let root = match outcome {
Err(Abort::StepLimit) => {
return refused(ParseRejection::limited("stepLimit", limit), None);
}
Err(Abort::MemoryBudget) => {
return refused(ParseRejection::limited("memoryBudget", memory_limit), None);
}
Err(Abort::NestingTooDeep) => {
let tree = tree::error_tree(bytes, 0, bytes.len(), Some("nestingDepth"));
return refused(
ParseRejection::limited("nestingDepth", max_depth),
Some(tree),
);
}
Ok(Outcome::Failed {
farthest, expected, ..
}) => {
let mut rejection = ParseRejection::positioned("syntax", bytes, farthest);
rejection.expected = Some(expected);
return refused(rejection, None);
}
Ok(Outcome::Parsed(root)) => root,
};
let tree = tree::public_tree(&root, bytes);
let mut ambiguities = Vec::new();
tree::collect_ambiguities(&root, compiled, compiled.main, &mut ambiguities);
let recovered = tree
.first_recovery()
.filter(|_| options.accept_recovery != Some(true))
.map(|recovery| ParseRejection::positioned("recovered", bytes, recovery.start()));
let rejection = recovered.or_else(|| {
ambiguities
.first()
.filter(|_| options.reject_ambiguity == Some(true))
.map(|first| ParseRejection::positioned("ambiguity", bytes, first.start))
});
ParseOutcome {
ok: rejection.is_none(),
tree: Some(tree),
ambiguities,
rejection,
}
}