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 {
|
if let Some(ns) = namespace {
|
||||||
Self::namespaced(ns, name)
|
Self::namespaced(ns, name)
|
||||||
} else {
|
} else {
|
||||||
|
@ -115,9 +115,9 @@ impl NamespaceableName {
|
||||||
let name = self.name();
|
let name = self.name();
|
||||||
|
|
||||||
if name.starts_with('_') {
|
if name.starts_with('_') {
|
||||||
Self::dwim(self.namespace(), &name[1..])
|
Self::new(self.namespace(), &name[1..])
|
||||||
} else {
|
} 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);
|
def_as_ref!(as_namespaced_symbol, $t::NamespacedSymbol, symbols::NamespacedSymbol);
|
||||||
|
|
||||||
pub fn as_keyword(&self) -> Option<&symbols::Keyword> {
|
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 {
|
match self {
|
||||||
&$t::Keyword(ref k) if !k.is_namespaced() => Some(k),
|
&$t::Keyword(ref k) if !k.is_namespaced() => Some(k),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -376,6 +383,13 @@ macro_rules! def_common_value_methods {
|
||||||
def_into!(into_namespaced_symbol, $t::NamespacedSymbol, symbols::NamespacedSymbol,);
|
def_into!(into_namespaced_symbol, $t::NamespacedSymbol, symbols::NamespacedSymbol,);
|
||||||
|
|
||||||
pub fn into_keyword(self) -> Option<symbols::Keyword> {
|
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 {
|
match self {
|
||||||
$t::Keyword(k) => {
|
$t::Keyword(k) => {
|
||||||
if !k.is_namespaced() {
|
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::Set(BTreeSet::new()).cmp(&Value::Set(BTreeSet::new())), Ordering::Equal);
|
||||||
assert_eq!(Value::Map(BTreeMap::new()).cmp(&Value::Map(BTreeMap::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_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_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_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_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_vector, i == 10, vec![Value::Integer(1)]);
|
||||||
def_test_as_type!(value, as_list, i == 11, LinkedList::from_iter(vec![]));
|
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_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_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_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_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_vector, i == 10, vec![Value::Integer(1)]);
|
||||||
def_test_into_type!(value, into_list, i == 11, LinkedList::from_iter(vec![]));
|
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")
|
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>> {
|
pub fn any_keyword_<'a>(input: Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>> {
|
||||||
satisfy_map(|v: &'a edn::ValueAndSpan|
|
satisfy_map(|v: &'a edn::ValueAndSpan| v.inner.as_keyword())
|
||||||
v.inner.as_namespaced_keyword()
|
|
||||||
.and_then(|k| if k.is_namespaced() { Some(k) } else { None })
|
|
||||||
)
|
|
||||||
.parse_lazy(input)
|
.parse_lazy(input)
|
||||||
.into()
|
.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>>>> {
|
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")
|
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>> {
|
pub fn forward_any_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 }))
|
satisfy_map(|v: &'a edn::ValueAndSpan| v.inner.as_keyword().and_then(|k| if k.is_forward() { Some(k) } else { None }))
|
||||||
.parse_lazy(input)
|
.parse_lazy(input)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn forward_keyword<'a>() -> Expected<FnParser<Stream<'a>, fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>>> {
|
pub fn forward_any_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")
|
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>> {
|
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_backward() && k.is_namespaced() { Some(k) } else { None }))
|
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)
|
.parse_lazy(input)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn backward_keyword<'a>() -> Expected<FnParser<Stream<'a>, fn(Stream<'a>) -> ParseResult<&'a edn::Keyword, Stream<'a>>>> {
|
pub fn forward_namespaced_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")
|
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.
|
/// 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() {
|
match input.uncons() {
|
||||||
Ok(value) => {
|
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 &keyword.name() == $keyword {
|
||||||
if $tmp.is_some() {
|
if $tmp.is_some() {
|
||||||
// Repeated match -- bail out! Providing good error
|
// 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::{
|
use self::mentat_parser_utils::value_and_span::{
|
||||||
Item,
|
Item,
|
||||||
OfExactlyParsing,
|
OfExactlyParsing,
|
||||||
forward_keyword,
|
forward_any_keyword,
|
||||||
|
forward_namespaced_keyword,
|
||||||
keyword_map,
|
keyword_map,
|
||||||
list,
|
list,
|
||||||
map,
|
map,
|
||||||
|
@ -306,12 +307,15 @@ def_parser!(Query, aggregate, Aggregate, {
|
||||||
});
|
});
|
||||||
|
|
||||||
def_parser!(Query, pull_concrete_attribute_ident, PullConcreteAttribute, {
|
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, {
|
def_parser!(Query, pull_concrete_attribute, PullAttributeSpec, {
|
||||||
(Query::pull_concrete_attribute_ident(),
|
(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)|
|
.map(|(attribute, alias)|
|
||||||
PullAttributeSpec::Attribute(
|
PullAttributeSpec::Attribute(
|
||||||
NamedPullAttribute {
|
NamedPullAttribute {
|
||||||
|
@ -1216,6 +1220,7 @@ mod test {
|
||||||
let foo_bar = ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "bar"));
|
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_baz = ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "baz"));
|
||||||
let foo_horse = ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "horse"));
|
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,
|
assert_edn_parses_to!(Query::pull_concrete_attribute,
|
||||||
":foo/bar",
|
":foo/bar",
|
||||||
PullAttributeSpec::Attribute(
|
PullAttributeSpec::Attribute(
|
||||||
|
@ -1238,6 +1243,20 @@ mod test {
|
||||||
PullConcreteAttribute::Ident(foo_baz.clone()).into()),
|
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,
|
assert_parse_failure_contains!(Find::elem,
|
||||||
"(pull ?x [* :foo/bar])",
|
"(pull ?x [* :foo/bar])",
|
||||||
r#"errors: [Unexpected(Borrowed("wildcard with specified attributes"))]"#);
|
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.
|
// Execute a scalar query where the body is constant.
|
||||||
// TODO: we shouldn't require `:where`; that makes this non-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]) .
|
:neighborhood/name]) .
|
||||||
:in ?hood
|
:in ?hood
|
||||||
:where [?hood :neighborhood/district _]]"#;
|
:where [?hood :neighborhood/district _]]"#;
|
||||||
|
@ -173,7 +173,7 @@ fn test_simple_pull() {
|
||||||
|
|
||||||
let expected: StructuredMap = vec![
|
let expected: StructuredMap = vec![
|
||||||
(kw!(:neighborhood/name), TypedValue::from("Beacon Hill")),
|
(kw!(:neighborhood/name), TypedValue::from("Beacon Hill")),
|
||||||
(kw!(:neighborhood/id), TypedValue::Ref(beacon)),
|
(kw!(:neighborhood), TypedValue::Ref(beacon)),
|
||||||
].into();
|
].into();
|
||||||
assert_eq!(result, expected.into());
|
assert_eq!(result, expected.into());
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue