use std::*;
use super::ast;
use std::rc::Rc;
use annis::operator::EdgeAnnoSearchSpec;
use annis::db::exec::nodesearch::NodeSearchSpec;
use annis::db::aql::operators::{
OverlapSpec,
IdenticalCoverageSpec,
PrecedenceSpec,
DominanceSpec,
PointingSpec,
PartOfSubCorpusSpec,
InclusionSpec,
IdenticalNodeSpec,
};
grammar;
match {
"tok" => TOK,
"node" => NODE,
"_=_" => IDENT_COV,
"_ident_" => IDENT_NODE,
"_o_" => OVERLAP,
"_i_" => INCLUSION,
} else {
r"[a-zA-Z_%][a-zA-Z0-9_\-%]*" => ID,
r##"#[0-9]+"## => NODE_REF,
r##"#[a-zA-Z][a-zA-Z0-9]*"## => VARIABLE_NODE_REF,
r##"[a-zA-Z][a-zA-Z0-9]*#"## => VARIABLE_DEF,
r"[0-9]+" => DIGITS,
_
}
pub Disjunction : ast::Disjunction = {
<head:Conjunction> <tail:("|" Conjunction)*> => {
let mut result = ast::Disjunction::new();
for t in tail.into_iter() {
result.push_front(t.1);
}
result.push_front(head);
return result;
},
}
Conjunction : ast::Conjunction = {
<head:Factor> <tail:("&" Factor)*> => {
let mut r = ast::Conjunction::new();
for t in tail.into_iter() {
for f in t.1.into_iter() {
r.push_front(f);
}
}
for f in head.into_iter() {
r.push_front(f);
}
return r;
},
}
Factor : Vec<ast::Factor> = {
Literal => {
let mut result = Vec::new();
for l in <>.into_iter() {
result.push(ast::Factor::Literal(l));
}
result
},
"(" <d:Disjunction> ")" => vec![ast::Factor::Disjunction(d)],
}
Literal : Vec<ast::Literal> = {
// any node annotation search
<start: @L> <var:(VARIABLE_DEF)?> <spec:NodeSearch> <end: @R> => {
let pos = Some(ast::Pos{start, end});
vec![ast::Literal::NodeSearch{pos, spec, variable: var.and_then(|s| Some(s[0..s.len()-1].to_string()))}]
},
// binary operator
<start: @L> <lhs:Operand> <op:BinaryOpSpec> <rhs:Operand> <tail:(BinaryOpSpec Operand)*> <end: @R> => {
let mut result : Vec<ast::Literal> = Vec::new();
// TODO: can we get the position for each individual binary operator?
let pos = ast::Pos {
start, end
};
result.push(ast::Literal::BinaryOp{lhs: lhs.clone(), op, rhs: rhs.clone(), pos: Some(pos.clone())});
let mut last_operand = rhs.clone();
for t in tail.into_iter() {
result.push(ast::Literal::BinaryOp{
lhs: last_operand.clone(),
op: t.0,
rhs: t.1.clone(),
pos: Some(pos.clone())
});
last_operand = t.1;
}
return result;
},
// legacy meta-data query `meta::doc="..."`
<start: @L> "meta::" <name:QName> "=" <text:TextSearch> <end: @R> => {
let pos = ast::Pos {start, end};
let spec = match text.1 {
ast::StringMatchType::Exact => {
NodeSearchSpec::ExactValue {
ns: name.0,
name: name.1,
val: Some(text.0),
is_meta: true,
}
},
ast::StringMatchType::Regex => {
NodeSearchSpec::RegexValue {
ns: name.0,
name: name.1,
val: text.0,
is_meta: true,
}
},
};
vec![ast::Literal::LegacyMetaSearch{spec, pos}]
},
}
Operand : ast::Operand = {
NodeRef => ast::Operand::NodeRef(<>),
<start:@L> <var:(VARIABLE_DEF)?> <spec:NodeSearch> <end:@R> => {
let pos = ast::Pos {start, end};
let spec = Rc::from(spec);
let variable = var.and_then(|s| Some(s[0..s.len()-1].to_string()));
ast::Operand::Literal{spec, pos, variable, }
},
}
/// General search for annotation nodes
NodeSearch : NodeSearchSpec = {
// searching for nodes with `node`
NODE => NodeSearchSpec::AnyNode,
// searching for tokens with `tok`
TOK => NodeSearchSpec::AnyToken,
// searching for a token value, e.g. tok="abc" or "abc"
<tok_def:(TOK "=")?> <val:TextSearch>=> {
// TODO: negation
let spec = match val.1 {
ast::StringMatchType::Exact => {
NodeSearchSpec::ExactTokenValue {
val: val.0,
leafs_only: tok_def.is_some(),
}
},
ast::StringMatchType::Regex => {
NodeSearchSpec::RegexTokenValue {
val: val.0,
leafs_only: tok_def.is_some(),
}
},
};
spec
},
// named annotation search with value, e.g. pos="NN"
<name:QName> "=" <text:TextSearch> => {
// TODO: negation
let spec = match text.1 {
ast::StringMatchType::Exact => {
NodeSearchSpec::ExactValue {
ns: name.0,
name: name.1,
val: Some(text.0),
is_meta: false,
}
},
ast::StringMatchType::Regex => {
NodeSearchSpec::RegexValue {
ns: name.0,
name: name.1,
val: text.0,
is_meta: false,
}
},
};
spec
},
// search for annotation name without value, e.g. pos
<name:QName> => {
let spec = NodeSearchSpec::ExactValue {
ns: name.0,
name: name.1,
val: None,
is_meta: false,
};
spec
},
}
/// Node references like `#1` (using the index of the node) or `#abc`
/// (using explicit name for variable)
NodeRef : ast::NodeRef = {
<v:NODE_REF> => ast::NodeRef::ID(v[1..].parse::<usize>().unwrap()),
<v:VARIABLE_NODE_REF> => ast::NodeRef::Name(v[1..].to_string()),
}
/// Binary operators that take a LHS and RHS as argument, e.g. `#1 ->dep #2`
BinaryOpSpec : ast::BinaryOpSpec = {
// Dominance (direct edge annotation)
<type_def:r">([a-zA-Z_%][a-zA-Z0-9_\-%]*)?"> <anno:EdgeAnno> => {
let name = type_def[">".len()..].to_string();
ast::BinaryOpSpec::Dominance(DominanceSpec {
name,
min_dist: 1,
max_dist: 1,
edge_anno: Some(anno),
})
},
// Dominance (without edge annotation)
<type_def:r">([a-zA-Z_%][a-zA-Z0-9_\-%]*)?"> <range:(RangeSpec)?> => {
let name = type_def[">".len()..].to_string();
if let Some(range) = range {
ast::BinaryOpSpec::Dominance(DominanceSpec {
name,
min_dist: range.min_dist,
max_dist: range.max_dist,
edge_anno: None,
})
} else {
ast::BinaryOpSpec::Dominance(DominanceSpec {
name,
min_dist: 1,
max_dist: 1,
edge_anno: None,
})
}
},
// Pointing (direct with edge annotation)
<type_def:r"->[a-zA-Z_%][a-zA-Z0-9_\-%]*"> <anno:EdgeAnno> => {
let name = type_def["->".len()..].to_string();
ast::BinaryOpSpec::Pointing(PointingSpec {
name,
min_dist: 1,
max_dist: 1,
edge_anno: Some(anno),
})
},
// Pointing (without edge annotation)
<type_def:r"->[a-zA-Z_%][a-zA-Z0-9_\-%]*"> <range:(RangeSpec)?> => {
let name = type_def["->".len()..].to_string();
if let Some(range) = range {
ast::BinaryOpSpec::Pointing(PointingSpec {
name,
min_dist: range.min_dist,
max_dist: range.max_dist,
edge_anno: None,
})
} else {
ast::BinaryOpSpec::Pointing(PointingSpec {
name,
min_dist: 1,
max_dist: 1,
edge_anno: None,
})
}
},
//Precedence
<prec_def:r"\.([a-zA-Z_%][a-zA-Z0-9_\-%]*)?"> <range:(RangeSpec)?> => {
let seg_name = prec_def[".".len()..].to_string();
let segmentation = if seg_name.is_empty() {
None
} else {
Some(seg_name)
};
if let Some(range) = range {
ast::BinaryOpSpec::Precedence(PrecedenceSpec {
segmentation,
min_dist: range.min_dist,
max_dist: range.max_dist,
})
} else {
ast::BinaryOpSpec::Precedence(PrecedenceSpec {
segmentation,
min_dist: 1,
max_dist: 1,
})
}
},
// Part of subcorpus
"@" <range:(RangeSpec)?> => {
if let Some(range) = range {
ast::BinaryOpSpec::PartOfSubCorpus(PartOfSubCorpusSpec {
min_dist: range.min_dist,
max_dist: range.max_dist,
})
} else {
ast::BinaryOpSpec::PartOfSubCorpus(PartOfSubCorpusSpec {
min_dist: 1,
max_dist: 1,
})
}
},
// Overlap
OVERLAP => ast::BinaryOpSpec::Overlap(OverlapSpec {}),
// Identical coverage
IDENT_COV => ast::BinaryOpSpec::IdenticalCoverage(IdenticalCoverageSpec {}),
// Inclusion
INCLUSION => ast::BinaryOpSpec::Inclusion(InclusionSpec {}),
// Identical node
IDENT_NODE => ast::BinaryOpSpec::IdenticalNode(IdenticalNodeSpec {}),
// TODO: add more operators
}
TextSearch: ast::TextSearch = {
<v:r#""[^"]*""#> => ast::TextSearch(String::from(&v[1..v.len()-1]), ast::StringMatchType::Exact),
// see https://stackoverflow.com/questions/37032620/regex-for-matching-a-string-literal-in-java
// for a example how to match escaped quotation characters
<v:r#"/[^/\\]*(\\.[^/\\]*)*/"#> => ast::TextSearch(String::from(&v[1..v.len()-1]), ast::StringMatchType::Regex),
};
EdgeAnno: EdgeAnnoSearchSpec = {
"[" <name:QName> "=" <val:TextSearch> "]" => {
// TODO: negation
// TODO: multiple edge annotations
let spec = match val.1 {
ast::StringMatchType::Exact => {
EdgeAnnoSearchSpec::ExactValue {
ns: name.0,
name: name.1,
val: Some(val.0),
}
},
ast::StringMatchType::Regex => {
EdgeAnnoSearchSpec::RegexValue {
ns: name.0,
name: name.1,
val: val.0,
}
},
};
spec
},
}
RangeSpec: ast::RangeSpec = {
(",")? <min:DIGITS> "," <max:DIGITS> => ast::RangeSpec {
min_dist: min.parse().unwrap_or_default(),
max_dist: max.parse().unwrap_or_default(),
},
(",")? <exact:DIGITS> => ast::RangeSpec {
min_dist: exact.parse().unwrap_or_default(),
max_dist: exact.parse().unwrap_or_default(),
},
"*" => ast::RangeSpec {
min_dist: 1,
max_dist: usize::max_value(),
}
}
QName: ast::QName = {
<ns:ID> ":" <name:ID> => ast::QName(Some(String::from(ns)), String::from(name)),
<name:ID> => ast::QName(None, String::from(name)),
};