forked from Orchid/orchid
simplified rule interface
This commit is contained in:
11
src/rule/bad_state_error.rs
Normal file
11
src/rule/bad_state_error.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
use std::{fmt, error::Error};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct BadState(Vec<String>);
|
||||||
|
|
||||||
|
impl fmt::Display for BadState {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "The following key(s) weren't produced by the matching pattern: {:?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Error for BadState {}
|
||||||
14
src/rule/executor.rs
Normal file
14
src/rule/executor.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
use crate::expression::Expr;
|
||||||
|
|
||||||
|
use super::{Rule, BadState};
|
||||||
|
|
||||||
|
pub fn execute<Src, Tgt>(src: &Src, tgt: &Tgt, mut input: Vec<Expr>)
|
||||||
|
-> Result<(Vec<Expr>, bool), BadState> where Src: Rule, Tgt: Rule {
|
||||||
|
let (range, state) = match src.scan_slice(&input) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => return Ok((input, false))
|
||||||
|
};
|
||||||
|
let output = tgt.write(&state)?;
|
||||||
|
input.splice(range, output);
|
||||||
|
Ok((input, true))
|
||||||
|
}
|
||||||
@@ -1 +1,6 @@
|
|||||||
mod rule;
|
mod rule;
|
||||||
|
mod executor;
|
||||||
|
mod bad_state_error;
|
||||||
|
|
||||||
|
pub use rule::Rule;
|
||||||
|
pub use bad_state_error::BadState;
|
||||||
3
src/rule/name.rs
Normal file
3
src/rule/name.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
struct Name {
|
||||||
|
qualified: Vec<String>
|
||||||
|
}
|
||||||
@@ -1,38 +1,50 @@
|
|||||||
use std::cmp::{min, max};
|
use std::{cmp::{min, max}, error::Error, fmt, ops::Range};
|
||||||
|
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
use crate::expression::Expr;
|
use crate::expression::Expr;
|
||||||
|
|
||||||
|
use super::BadState;
|
||||||
|
|
||||||
|
type State = HashMap<String, Expr>;
|
||||||
|
|
||||||
pub trait Rule {
|
pub trait Rule {
|
||||||
type OutIter: Iterator<Item = Option<Expr>>;
|
type Out: Iterator<Item = Expr>;
|
||||||
/// The minimum and maximum set of symbols this rule may match.
|
/// The minimum and maximum set of symbols this rule may match.
|
||||||
fn len(&self) -> (Option<usize>, Option<usize>);
|
fn len(&self) -> (Option<usize>, Option<usize>);
|
||||||
/// The exact tokens the pattern consumes (None if varies)
|
/// Check if the slice matches, and extract data
|
||||||
fn consumes(&self) -> Option<HashSet<Vec<String>>>;
|
fn read(&self, input: &[Expr]) -> Option<State>;
|
||||||
/// The exact tokens the pattern produces (None if varies)
|
/// Construct item from state
|
||||||
fn produces(&self) -> Option<HashSet<Vec<String>>>;
|
fn write(&self, state: &State) -> Result<Self::Out, BadState>;
|
||||||
/// Check if the slice matches, and produce the necessary transformations
|
/// Placeholders present in this pattern (all consumed must also be provided)
|
||||||
fn produce(&self, base: &[Expr]) -> Option<Self::OutIter>;
|
fn placeholders(&'_ self) -> &'_ [&'_ str];
|
||||||
/// Try all subsections of Vec of appropriate size, longest first, front-to-back
|
/// Try all subsections of Vec of appropriate size, longest first, front-to-back
|
||||||
/// Match the first, execute the substitution, return the vector and whether any
|
/// Match the first, return the position and produced state
|
||||||
/// substitutions happened
|
fn scan_slice(&self, input: &[Expr]) -> Option<(Range<usize>, State)> {
|
||||||
fn apply(&self, mut base: Vec<Expr>) -> (Vec<Expr>, bool) {
|
|
||||||
let len_range = self.len();
|
let len_range = self.len();
|
||||||
let lo = max(len_range.0.unwrap_or(1), 1);
|
let lo = max(len_range.0.unwrap_or(1), 1);
|
||||||
let hi = min(len_range.1.unwrap_or(base.len()), base.len());
|
let hi = min(len_range.1.unwrap_or(input.len()), input.len());
|
||||||
for width in (lo..hi).rev() {
|
for width in (lo..hi).rev() {
|
||||||
let starts = (0..base.len() - width).into_iter();
|
let starts = (0..input.len() - width).into_iter();
|
||||||
let first_match = starts.filter_map(|start| {
|
let first_match = starts.filter_map(|start| {
|
||||||
self.produce(&base[start..start+width])
|
let res = self.read(&input[start..start+width])?;
|
||||||
.map(|res| (start, res))
|
Some((start..start+width, res))
|
||||||
}).next();
|
}).next();
|
||||||
if let Some((start, substitution)) = first_match {
|
if first_match.is_some() {
|
||||||
let diff = substitution.enumerate().filter_map(|(i, opt)| opt.map(|val| (i, val)));
|
return first_match;
|
||||||
for (idx, item) in diff { base[start + idx] = item }
|
|
||||||
return (base, true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(base, false)
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn verify<Src, Tgt>(src: &Src, tgt: &Tgt) -> Option<Vec<String>> where Src: Rule, Tgt: Rule {
|
||||||
|
let mut amiss: Vec<String> = Vec::new();
|
||||||
|
for ent in tgt.placeholders() {
|
||||||
|
if src.placeholders().iter().find(|x| x == &ent).is_none() {
|
||||||
|
amiss.push(ent.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if amiss.len() > 0 { Some(amiss) }
|
||||||
|
else { None }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user