use crate::CompilationError; use crate::context::Env; use crate::schemas::path; use crate::step::Node; use preserves::value::BinarySource; use preserves::value::BytesBinarySource; use preserves::value::IOValue; use preserves::value::NestedValue; use preserves::value::Reader; use std::iter::Iterator; #[derive(Debug)] enum Binop { Union, Intersection, } fn split_values_by_symbol<'a>(tokens: &'a [IOValue], separator: &str) -> Vec<&'a [IOValue]> { tokens .split(|t| matches!(t.value().as_symbol(), Some(s) if s == separator)) .collect() } fn split_binop(tokens: &[IOValue]) -> Result<(Vec<&[IOValue]>, Option), CompilationError> { let union_pieces = split_values_by_symbol(&tokens, "+"); let intersection_pieces = split_values_by_symbol(&tokens, "&"); match (union_pieces.len(), intersection_pieces.len()) { (1, 1) => Ok((union_pieces, None)), (_, 1) => Ok((union_pieces, Some(Binop::Union))), (1, _) => Ok((intersection_pieces, Some(Binop::Intersection))), _ => Err(CompilationError::MixedOperators), } } pub fn parse_selector(env: &Env, tokens: &[IOValue]) -> Result { let mut steps = Vec::new(); let mut tokens = tokens; while let Some((s, remaining)) = parse_step(env, tokens)? { steps.push(s); tokens = remaining; } Ok(path::Selector(steps)) } pub fn parse_predicate(env: &Env, tokens: &[IOValue]) -> Result { let (pieces, binop) = split_binop(tokens)?; match binop { None => parse_non_binop(env, &pieces[0]), Some(o) => { let preds = pieces.into_iter().map(|ts| parse_non_binop(env, &ts)).collect::>()?; Ok(match o { Binop::Union => path::Predicate::Or { preds }, Binop::Intersection => path::Predicate::And { preds }, }) } } } fn parse_non_binop(env: &Env, tokens: &[IOValue]) -> Result { if !tokens.is_empty() { let t = tokens[0].value(); if let Some("!") = t.as_symbol().map(|s| s.as_str()) { return Ok(path::Predicate::Not { pred: Box::new(parse_non_binop(env, &tokens[1..])?) }); } } Ok(path::Predicate::Selector(Box::new(parse_selector(env, tokens)?))) } fn parse_schema_definition_name(env: &Env, token: &IOValue) -> Result<(Vec, String), CompilationError> { let defpath = token.value().to_symbol().map_err(|_| CompilationError::InvalidStep)?; let mut module: Vec = defpath.split('.').map(|s| s.to_string()).collect(); let name = module.pop().expect("at least one element in the Schema name"); match env.lookup_definition(&module, &name) { Some(_) => Ok((module, name)), None => Err(CompilationError::UndefinedSchemaDefinitionName(format!("{:?}", token))), } } fn parse_step<'a>(env: &Env, tokens: &'a [IOValue]) -> Result, CompilationError> { if tokens.is_empty() { return Ok(None); } let remainder = &tokens[1..]; if tokens[0].value().is_sequence() { return Ok(Some((path::Step::Filter(Box::new(path::Filter::Test { pred: Box::new(parse_predicate(env, tokens[0].value().as_sequence().unwrap())?), })), remainder))); } match tokens[0].value().as_symbol() { None => return Err(CompilationError::InvalidStep), Some(t) => match t.as_str() { "/" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Values)), remainder))), "//" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Descendants)), remainder))), "." => { let (key, remainder) = pop_step_arg(remainder)?; Ok(Some((path::Step::Axis(Box::new(path::Axis::At { key })), remainder))) } ".^" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Label)), remainder))), ".keys" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Keys)), remainder))), ".length" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Length)), remainder))), ".annotations" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Annotations)), remainder))), ".embedded" => Ok(Some((path::Step::Axis(Box::new(path::Axis::Embedded)), remainder))), "%" => { let (defpath, remainder) = pop_step_arg(remainder)?; let (module, name) = parse_schema_definition_name(env, &defpath)?; Ok(Some((path::Step::Axis(Box::new(path::Axis::Parse { module, name })), remainder))) } "%-" => { let (defpath, remainder) = pop_step_arg(remainder)?; let (module, name) = parse_schema_definition_name(env, &defpath)?; Ok(Some((path::Step::Axis(Box::new(path::Axis::Unparse { module, name })), remainder))) } "*" => Ok(Some((path::Step::Filter(Box::new(path::Filter::Nop)), remainder))), "eq" | "=" => parse_comparison(remainder, path::Comparison::Eq), "ne" | "!=" => parse_comparison(remainder, path::Comparison::Ne), "lt" => parse_comparison(remainder, path::Comparison::Lt), "gt" => parse_comparison(remainder, path::Comparison::Gt), "le" => parse_comparison(remainder, path::Comparison::Le), "ge" => parse_comparison(remainder, path::Comparison::Ge), "re" | "=r" => { let (regex_val, remainder) = pop_step_arg(remainder)?; let regex = regex_val.value().to_string().map_err(|_| CompilationError::InvalidStep)?.clone(); let _ = regex::Regex::new(®ex)?; Ok(Some((path::Step::Filter(Box::new(path::Filter::Regex { regex })), remainder))) } "^" => { let (literal, remainder) = pop_step_arg(remainder)?; Ok(Some((path::Step::Filter(Box::new(path::Filter::Test { pred: Box::new(path::Predicate::Selector(Box::new(path::Selector(vec![ path::Step::Axis(Box::new(path::Axis::Label)), path::Step::Filter(Box::new(path::Filter::Compare { op: Box::new(path::Comparison::Eq), literal, })), ])))), })), remainder))) } "~real" => Ok(Some((path::Step::Filter(Box::new(path::Filter::Real)), remainder))), "~int" => Ok(Some((path::Step::Filter(Box::new(path::Filter::Int)), remainder))), "bool" => Ok(Some((path::Step::from(path::ValueKind::Boolean), remainder))), "float" => Ok(Some((path::Step::from(path::ValueKind::Float), remainder))), "double" => Ok(Some((path::Step::from(path::ValueKind::Double), remainder))), "int" => Ok(Some((path::Step::from(path::ValueKind::SignedInteger), remainder))), "string" => Ok(Some((path::Step::from(path::ValueKind::String), remainder))), "bytes" => Ok(Some((path::Step::from(path::ValueKind::ByteString), remainder))), "symbol" => Ok(Some((path::Step::from(path::ValueKind::Symbol), remainder))), "rec" => Ok(Some((path::Step::from(path::ValueKind::Record), remainder))), "seq" => Ok(Some((path::Step::from(path::ValueKind::Sequence), remainder))), "set" => Ok(Some((path::Step::from(path::ValueKind::Set), remainder))), "dict" => Ok(Some((path::Step::from(path::ValueKind::Dictionary), remainder))), "embedded" => Ok(Some((path::Step::from(path::ValueKind::Embedded), remainder))), _ => Err(CompilationError::InvalidStep), } } } impl From for path::Step { fn from(k: path::ValueKind) -> Self { path::Step::Filter(Box::new(path::Filter::Kind { kind: Box::new(k), })) } } fn pop_step_arg(tokens: &[IOValue]) -> Result<(IOValue, &[IOValue]), CompilationError> { if tokens.is_empty() { return Err(CompilationError::InvalidStep); } Ok((tokens[0].clone(), &tokens[1..])) } fn parse_comparison( tokens: &[IOValue], op: path::Comparison, ) -> Result, CompilationError> { let (literal, remainder) = pop_step_arg(tokens)?; Ok(Some((path::Step::Filter(Box::new(path::Filter::Compare { op: Box::new(op), literal, })), remainder))) } impl path::Selector { pub fn from_str(env: &Env, s: &str) -> Result { parse_selector(env, &(BytesBinarySource::new(s.as_bytes()) .text_iovalues() .configured(false) .collect::, _>>()?)) } } impl Node { pub fn from_str(env: &Env, s: &str) -> Result { let expr = path::Selector::from_str(env, s)?; expr.compile() } }