pub mod ast;
mod error;
mod serializer;
pub mod standard_graph;
mod value;
mod parser;
#[cfg(feature = "wasm")]
mod wasm;
#[cfg(feature = "python")]
mod python;
pub use ast::{AstPattern, AstSubject};
pub use error::{Location, SerializeError};
pub use parser::ParseError;
pub use serializer::{to_gram, to_gram_pattern, to_gram_with_header};
pub use value::Value;
pub use pattern_core::{Pattern, PropertyRecord as Record, Subject};
pub fn parse_gram(input: &str) -> Result<Vec<Pattern<Subject>>, ParseError> {
if input.trim().is_empty() {
return Ok(vec![]);
}
match parser::gram_patterns(input) {
Ok((remaining, patterns)) => {
if !remaining.trim().is_empty() {
let offset = input.len() - remaining.len();
let location = parser::Location::from_offset(input, offset);
return Err(ParseError::UnexpectedInput {
location,
snippet: remaining.chars().take(20).collect(),
});
}
Ok(patterns)
}
Err(e) => Err(parser::ParseError::from_nom_error(input, e)),
}
}
pub fn parse_gram_with_header(
input: &str,
) -> Result<(Option<Record>, Vec<Pattern<Subject>>), ParseError> {
let mut patterns = parse_gram(input)?;
if patterns.is_empty() {
return Ok((None, vec![]));
}
let first = &patterns[0];
if first.value.identity.0.is_empty()
&& first.value.labels.is_empty()
&& first.elements.is_empty()
&& !first.value.properties.is_empty()
{
let header_record = patterns.remove(0).value.properties;
Ok((Some(header_record), patterns))
} else {
Ok((None, patterns))
}
}
pub fn parse_to_ast(input: &str) -> Result<AstPattern, ParseError> {
let patterns = parse_gram(input)?;
if patterns.is_empty() {
return Ok(AstPattern::empty());
}
let document_pattern = wrap_as_document(patterns);
Ok(AstPattern::from_pattern(&document_pattern))
}
fn wrap_as_document(mut patterns: Vec<Pattern<Subject>>) -> Pattern<Subject> {
if patterns.len() == 1 {
let first = &patterns[0];
if !first.value.identity.0.is_empty()
|| !first.value.labels.is_empty()
|| !first.elements.is_empty()
|| !first.value.properties.is_empty()
{
return patterns.remove(0);
}
}
let mut properties = Record::new();
if !patterns.is_empty() {
let first = &patterns[0];
if first.value.identity.0.is_empty()
&& first.value.labels.is_empty()
&& first.elements.is_empty()
&& !first.value.properties.is_empty()
{
properties = patterns.remove(0).value.properties;
}
}
let subject = Subject {
identity: pattern_core::Symbol(String::new()),
labels: std::collections::HashSet::new(),
properties,
};
Pattern::pattern(subject, patterns)
}
pub fn validate_gram(input: &str) -> Result<(), ParseError> {
parse_gram(input).map(|_| ())
}
pub fn parse_single_pattern(input: &str) -> Result<Pattern<Subject>, ParseError> {
let patterns = parse_gram(input)?;
match patterns.len() {
0 => Err(ParseError::UnexpectedInput {
location: parser::Location::start(),
snippet: "Input contains no patterns".to_string(),
}),
1 => Ok(patterns.into_iter().next().unwrap()),
n => Err(ParseError::UnexpectedInput {
location: parser::Location::start(),
snippet: format!("Input contains {} patterns, expected exactly 1", n),
}),
}
}
pub use parse_gram as parse_gram_notation;
pub use standard_graph::FromGram;