Allow pull aliases to be non-namespaced.
This commit is contained in:
parent
60cb5d2432
commit
bf8f8c9954
6 changed files with 98 additions and 24 deletions
|
@ -89,7 +89,7 @@ impl NamespaceableName {
|
|||
}
|
||||
}
|
||||
|
||||
fn dwim<N, T>(namespace: Option<N>, name: T) -> Self where N: AsRef<str>, T: AsRef<str> {
|
||||
fn new<N, T>(namespace: Option<N>, name: T) -> Self where N: AsRef<str>, T: AsRef<str> {
|
||||
if let Some(ns) = namespace {
|
||||
Self::namespaced(ns, name)
|
||||
} else {
|
||||
|
@ -115,9 +115,9 @@ impl NamespaceableName {
|
|||
let name = self.name();
|
||||
|
||||
if name.starts_with('_') {
|
||||
Self::dwim(self.namespace(), &name[1..])
|
||||
Self::new(self.namespace(), &name[1..])
|
||||
} else {
|
||||
Self::dwim(self.namespace(), &format!("_{}", name))
|
||||
Self::new(self.namespace(), &format!("_{}", name))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -346,6 +346,13 @@ macro_rules! def_common_value_methods {
|
|||
def_as_ref!(as_namespaced_symbol, $t::NamespacedSymbol, symbols::NamespacedSymbol);
|
||||
|
||||
pub fn as_keyword(&self) -> Option<&symbols::Keyword> {
|
||||
match self {
|
||||
&$t::Keyword(ref k) => Some(k),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_plain_keyword(&self) -> Option<&symbols::Keyword> {
|
||||
match self {
|
||||
&$t::Keyword(ref k) if !k.is_namespaced() => Some(k),
|
||||
_ => None,
|
||||
|
@ -376,6 +383,13 @@ macro_rules! def_common_value_methods {
|
|||
def_into!(into_namespaced_symbol, $t::NamespacedSymbol, symbols::NamespacedSymbol,);
|
||||
|
||||
pub fn into_keyword(self) -> Option<symbols::Keyword> {
|
||||
match self {
|
||||
$t::Keyword(k) => Some(k),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_plain_keyword(self) -> Option<symbols::Keyword> {
|
||||
match self {
|
||||
$t::Keyword(k) => {
|
||||
if !k.is_namespaced() {
|
||||
|
@ -726,4 +740,28 @@ mod test {
|
|||
assert_eq!(Value::Set(BTreeSet::new()).cmp(&Value::Set(BTreeSet::new())), Ordering::Equal);
|
||||
assert_eq!(Value::Map(BTreeMap::new()).cmp(&Value::Map(BTreeMap::new())), Ordering::Equal);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keyword_as() {
|
||||
let namespaced = symbols::Keyword::namespaced("foo", "bar");
|
||||
let plain = symbols::Keyword::plain("bar");
|
||||
let n_v = Value::Keyword(namespaced);
|
||||
let p_v = Value::Keyword(plain);
|
||||
|
||||
assert!(n_v.as_keyword().is_some());
|
||||
assert!(n_v.as_plain_keyword().is_none());
|
||||
assert!(n_v.as_namespaced_keyword().is_some());
|
||||
|
||||
assert!(p_v.as_keyword().is_some());
|
||||
assert!(p_v.as_plain_keyword().is_some());
|
||||
assert!(p_v.as_namespaced_keyword().is_none());
|
||||
|
||||
assert!(n_v.clone().into_keyword().is_some());
|
||||
assert!(n_v.clone().into_plain_keyword().is_none());
|
||||
assert!(n_v.clone().into_namespaced_keyword().is_some());
|
||||
|
||||
assert!(p_v.clone().into_keyword().is_some());
|
||||
assert!(p_v.clone().into_plain_keyword().is_some());
|
||||
assert!(p_v.clone().into_namespaced_keyword().is_none());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1476,7 +1476,7 @@ fn test_is_and_as_type_helper_functions() {
|
|||
def_test_as_type!(value, as_text, i == 5, "hello world".to_string());
|
||||
def_test_as_type!(value, as_symbol, i == 6, symbols::PlainSymbol::plain("$symbol"));
|
||||
def_test_as_type!(value, as_namespaced_symbol, i == 7, symbols::NamespacedSymbol::namespaced("$ns", "$symbol"));
|
||||
def_test_as_type!(value, as_keyword, i == 8, symbols::Keyword::plain("hello"));
|
||||
def_test_as_type!(value, as_plain_keyword, i == 8, symbols::Keyword::plain("hello"));
|
||||
def_test_as_type!(value, as_namespaced_keyword, i == 9, symbols::Keyword::namespaced("hello", "world"));
|
||||
def_test_as_type!(value, as_vector, i == 10, vec![Value::Integer(1)]);
|
||||
def_test_as_type!(value, as_list, i == 11, LinkedList::from_iter(vec![]));
|
||||
|
@ -1494,7 +1494,7 @@ fn test_is_and_as_type_helper_functions() {
|
|||
def_test_into_type!(value, into_text, i == 5, "hello world".to_string());
|
||||
def_test_into_type!(value, into_symbol, i == 6, symbols::PlainSymbol::plain("$symbol"));
|
||||
def_test_into_type!(value, into_namespaced_symbol, i == 7, symbols::NamespacedSymbol::namespaced("$ns", "$symbol"));
|
||||
def_test_into_type!(value, into_keyword, i == 8, symbols::Keyword::plain("hello"));
|
||||
def_test_into_type!(value, into_plain_keyword, i == 8, symbols::Keyword::plain("hello"));
|
||||
def_test_into_type!(value, into_namespaced_keyword, i == 9, symbols::Keyword::namespaced("hello", "world"));
|
||||
def_test_into_type!(value, into_vector, i == 10, vec![Value::Integer(1)]);
|
||||
def_test_into_type!(value, into_list, i == 11, LinkedList::from_iter(vec![]));
|
||||
|
|
|
@ -442,37 +442,54 @@ pub fn integer<'a>() -> Expected<FnParser<Stream<'a>, fn(Stream<'a>) -> ParseRes
|
|||
parser(integer_ as fn(Stream<'a>) -> ParseResult<i64, Stream<'a>>).expected("integer")
|
||||
}
|
||||
|
||||
pub fn namespaced_keyword_<'a>(input: Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>> {
|
||||
satisfy_map(|v: &'a edn::ValueAndSpan|
|
||||
v.inner.as_namespaced_keyword()
|
||||
.and_then(|k| if k.is_namespaced() { Some(k) } else { None })
|
||||
)
|
||||
pub fn any_keyword_<'a>(input: Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>> {
|
||||
satisfy_map(|v: &'a edn::ValueAndSpan| v.inner.as_keyword())
|
||||
.parse_lazy(input)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn namespaced_keyword_<'a>(input: Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>> {
|
||||
satisfy_map(|v: &'a edn::ValueAndSpan| v.inner.as_namespaced_keyword())
|
||||
.parse_lazy(input)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn any_keyword<'a>() -> Expected<FnParser<Stream<'a>, fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>>> {
|
||||
parser(any_keyword_ as fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>).expected("any_keyword")
|
||||
}
|
||||
|
||||
pub fn namespaced_keyword<'a>() -> Expected<FnParser<Stream<'a>, fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>>> {
|
||||
parser(namespaced_keyword_ as fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>).expected("namespaced_keyword")
|
||||
}
|
||||
|
||||
pub fn forward_keyword_<'a>(input: Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>> {
|
||||
satisfy_map(|v: &'a edn::ValueAndSpan| v.inner.as_namespaced_keyword().and_then(|k| if k.is_forward() && k.is_namespaced() { Some(k) } else { None }))
|
||||
pub fn forward_any_keyword_<'a>(input: Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>> {
|
||||
satisfy_map(|v: &'a edn::ValueAndSpan| v.inner.as_keyword().and_then(|k| if k.is_forward() { Some(k) } else { None }))
|
||||
.parse_lazy(input)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn forward_keyword<'a>() -> Expected<FnParser<Stream<'a>, fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>>> {
|
||||
parser(forward_keyword_ as fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>).expected("forward_keyword")
|
||||
pub fn forward_any_keyword<'a>() -> Expected<FnParser<Stream<'a>, fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>>> {
|
||||
parser(forward_any_keyword_ as fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>).expected("forward_any_keyword")
|
||||
}
|
||||
|
||||
pub fn backward_keyword_<'a>(input: Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>> {
|
||||
satisfy_map(|v: &'a edn::ValueAndSpan| v.inner.as_namespaced_keyword().and_then(|k| if k.is_backward() && k.is_namespaced() { Some(k) } else { None }))
|
||||
pub fn forward_namespaced_keyword_<'a>(input: Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>> {
|
||||
satisfy_map(|v: &'a edn::ValueAndSpan| v.inner.as_namespaced_keyword().and_then(|k| if k.is_forward() { Some(k) } else { None }))
|
||||
.parse_lazy(input)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn backward_keyword<'a>() -> Expected<FnParser<Stream<'a>, fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>>> {
|
||||
parser(backward_keyword_ as fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>).expected("backward_keyword")
|
||||
pub fn forward_namespaced_keyword<'a>() -> Expected<FnParser<Stream<'a>, fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>>> {
|
||||
parser(forward_namespaced_keyword_ as fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>).expected("forward_namespaced_keyword")
|
||||
}
|
||||
|
||||
pub fn backward_namespaced_keyword_<'a>(input: Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>> {
|
||||
satisfy_map(|v: &'a edn::ValueAndSpan| v.inner.as_namespaced_keyword().and_then(|k| if k.is_backward() { Some(k) } else { None }))
|
||||
.parse_lazy(input)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn backward_namespaced_keyword<'a>() -> Expected<FnParser<Stream<'a>, fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>>> {
|
||||
parser(backward_namespaced_keyword_ as fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>).expected("backward_namespaced_keyword")
|
||||
}
|
||||
|
||||
/// Generate a `satisfy` expression that matches a `PlainSymbol` value with the given name.
|
||||
|
@ -570,7 +587,7 @@ macro_rules! keyword_map_parser {
|
|||
match input.uncons() {
|
||||
Ok(value) => {
|
||||
$(
|
||||
if let Some(ref keyword) = value.inner.as_keyword() {
|
||||
if let Some(ref keyword) = value.inner.as_plain_keyword() {
|
||||
if &keyword.name() == $keyword {
|
||||
if $tmp.is_some() {
|
||||
// Repeated match -- bail out! Providing good error
|
||||
|
|
|
@ -46,7 +46,8 @@ use self::mentat_parser_utils::value_and_span::Stream as ValueStream;
|
|||
use self::mentat_parser_utils::value_and_span::{
|
||||
Item,
|
||||
OfExactlyParsing,
|
||||
forward_keyword,
|
||||
forward_any_keyword,
|
||||
forward_namespaced_keyword,
|
||||
keyword_map,
|
||||
list,
|
||||
map,
|
||||
|
@ -306,12 +307,15 @@ def_parser!(Query, aggregate, Aggregate, {
|
|||
});
|
||||
|
||||
def_parser!(Query, pull_concrete_attribute_ident, PullConcreteAttribute, {
|
||||
forward_keyword().map(|k| PullConcreteAttribute::Ident(::std::rc::Rc::new(k.clone())))
|
||||
forward_namespaced_keyword()
|
||||
.map(|k| PullConcreteAttribute::Ident(::std::rc::Rc::new(k.clone())))
|
||||
});
|
||||
|
||||
def_parser!(Query, pull_concrete_attribute, PullAttributeSpec, {
|
||||
(Query::pull_concrete_attribute_ident(),
|
||||
optional(try(Query::alias_as().with(forward_keyword().map(|alias| ::std::rc::Rc::new(alias.clone()))))))
|
||||
optional(try(Query::alias_as()
|
||||
.with(forward_any_keyword()
|
||||
.map(|alias| ::std::rc::Rc::new(alias.clone()))))))
|
||||
.map(|(attribute, alias)|
|
||||
PullAttributeSpec::Attribute(
|
||||
NamedPullAttribute {
|
||||
|
@ -1216,6 +1220,7 @@ mod test {
|
|||
let foo_bar = ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "bar"));
|
||||
let foo_baz = ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "baz"));
|
||||
let foo_horse = ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "horse"));
|
||||
let horse = ::std::rc::Rc::new(edn::Keyword::plain("horse"));
|
||||
assert_edn_parses_to!(Query::pull_concrete_attribute,
|
||||
":foo/bar",
|
||||
PullAttributeSpec::Attribute(
|
||||
|
@ -1238,6 +1243,20 @@ mod test {
|
|||
PullConcreteAttribute::Ident(foo_baz.clone()).into()),
|
||||
],
|
||||
}));
|
||||
assert_edn_parses_to!(Find::elem,
|
||||
"(pull ?v [:foo/bar :as :horse, :foo/baz])",
|
||||
Element::Pull(Pull {
|
||||
var: Variable::from_valid_name("?v"),
|
||||
patterns: vec![
|
||||
PullAttributeSpec::Attribute(
|
||||
NamedPullAttribute {
|
||||
attribute: PullConcreteAttribute::Ident(foo_bar.clone()),
|
||||
alias: Some(horse),
|
||||
}),
|
||||
PullAttributeSpec::Attribute(
|
||||
PullConcreteAttribute::Ident(foo_baz.clone()).into()),
|
||||
],
|
||||
}));
|
||||
assert_parse_failure_contains!(Find::elem,
|
||||
"(pull ?x [* :foo/bar])",
|
||||
r#"errors: [Unexpected(Borrowed("wildcard with specified attributes"))]"#);
|
||||
|
|
|
@ -162,7 +162,7 @@ fn test_simple_pull() {
|
|||
|
||||
// Execute a scalar query where the body is constant.
|
||||
// TODO: we shouldn't require `:where`; that makes this non-constant!
|
||||
let query = r#"[:find (pull ?hood [:db/id :as :neighborhood/id
|
||||
let query = r#"[:find (pull ?hood [:db/id :as :neighborhood
|
||||
:neighborhood/name]) .
|
||||
:in ?hood
|
||||
:where [?hood :neighborhood/district _]]"#;
|
||||
|
@ -173,7 +173,7 @@ fn test_simple_pull() {
|
|||
|
||||
let expected: StructuredMap = vec![
|
||||
(kw!(:neighborhood/name), TypedValue::from("Beacon Hill")),
|
||||
(kw!(:neighborhood/id), TypedValue::Ref(beacon)),
|
||||
(kw!(:neighborhood), TypedValue::Ref(beacon)),
|
||||
].into();
|
||||
assert_eq!(result, expected.into());
|
||||
|
||||
|
|
Loading…
Reference in a new issue