Skip to main content

edn/
parse.rs

1// Copyright 2016 Mozilla
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
4// this file except in compliance with the License. You may obtain a copy of the
5// License at http://www.apache.org/licenses/LICENSE-2.0
6// Unless required by applicable law or agreed to in writing, software distributed
7// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
8// CONDITIONS OF ANY KIND, either express or implied. See the License for the
9// specific language governing permissions and limitations under the License.
10
11use std::collections::{BTreeMap, BTreeSet, LinkedList};
12use std::iter::FromIterator;
13
14use chrono::{DateTime, Utc};
15use num::BigInt;
16use ordered_float::OrderedFloat;
17use uuid::Uuid;
18
19use crate::entities::*;
20use crate::query;
21use crate::query::FromValue;
22use crate::symbols::*;
23use crate::types::{Span, SpannedValue, ValueAndSpan};
24
25pub type ParseError = peg::error::ParseError<peg::str::LineCol>;
26
27// Goal: Be able to parse https://github.com/edn-format/edn
28// Also extensible to help parse http://docs.datomic.com/query.html
29
30// Debugging hint: test using `cargo test --features peg/trace -- --nocapture`
31// to trace where the parser is failing
32
33peg::parser! {
34    grammar inner() for str {
35
36        pub rule nil() -> SpannedValue = "nil" { SpannedValue::Nil }
37        pub rule nan() -> SpannedValue = "#f" whitespace()+ "NaN" { SpannedValue::Float(OrderedFloat(f64::NAN)) }
38
39        pub rule infinity() -> SpannedValue = "#f" whitespace()+ s:$(sign()) "Infinity"
40            { SpannedValue::Float(OrderedFloat(if s == "+" { f64::INFINITY } else { f64::NEG_INFINITY })) }
41
42        pub rule boolean() -> SpannedValue
43            = "true"  { SpannedValue::Boolean(true) }
44            / "false" { SpannedValue::Boolean(false) }
45
46        rule digit() = ['0'..='9']
47        rule alphanumeric() = ['0'..='9' | 'a'..='z' | 'A'..='Z']
48        rule octaldigit() = ['0'..='7']
49        rule validbase() = ['3'] ['0'..='6'] / ['1' | '2'] ['0'..='9'] / ['2'..='9']
50        rule hex() = ['0'..='9' | 'a'..='f' | 'A'..='F']
51        rule sign() = ['+' | '-']
52
53        pub rule raw_bigint() -> BigInt = b:$( sign()? digit()+ ) "N"
54            { b.parse::<BigInt>().unwrap() }
55        pub rule raw_octalinteger() -> i64 = "0" i:$( octaldigit()+ )
56            { i64::from_str_radix(i, 8).unwrap() }
57        pub rule raw_hexinteger() -> i64 = "0x" i:$( hex()+ )
58            { i64::from_str_radix(i, 16).unwrap() }
59        pub rule raw_basedinteger() -> i64 = b:$( validbase() ) "r" i:$( alphanumeric()+ )
60            { i64::from_str_radix(i, b.parse::<u32>().unwrap()).unwrap() }
61        pub rule raw_integer() -> i64 = i:$( sign()? digit()+ ) !("." / (['e' | 'E']))
62            { i.parse::<i64>().unwrap() }
63        pub rule raw_float() -> OrderedFloat<f64> = f:$(sign()? digit()+ ("." digit()+)? (['e' | 'E'] sign()? digit()+)?)
64            { OrderedFloat(f.parse::<f64>().unwrap()) }
65
66        pub rule bigint() -> SpannedValue = v:raw_bigint() { SpannedValue::BigInteger(v) }
67        pub rule octalinteger() -> SpannedValue = v:raw_octalinteger() { SpannedValue::Integer(v) }
68        pub rule hexinteger() -> SpannedValue = v:raw_hexinteger() { SpannedValue::Integer(v) }
69        pub rule basedinteger() -> SpannedValue = v:raw_basedinteger() { SpannedValue::Integer(v) }
70        pub rule integer() -> SpannedValue = v:raw_integer() { SpannedValue::Integer(v) }
71        pub rule float() -> SpannedValue = v:raw_float() { SpannedValue::Float(v) }
72
73        rule number() -> SpannedValue = ( bigint() / basedinteger() / hexinteger() / octalinteger() / integer() / float() )
74
75        // TODO: standalone characters: \<char>, \newline, \return, \space and \tab.
76
77        rule string_special_char() -> &'input str = "\\" s:$(['\\' | '"' | 'n' | 't' | 'r']) { s }
78        rule string_normal_chars() -> &'input str = $([^ '"' | '\\']+)
79
80        // This is what we need to do in order to unescape. We can't just match the entire string slice:
81        // we get a Vec<&str> from rust-peg, where some of the parts might be unescaped special characters,
82        // and we join it together to form an output string.
83        pub rule raw_text() -> String = "\"" t:((string_special_char() / string_normal_chars())*) "\""
84            { t.join("").to_string() }
85
86        pub rule text() -> SpannedValue
87            = v:raw_text() { SpannedValue::Text(v) }
88
89        // RFC 3339 timestamps. #inst "1985-04-12T23:20:50.52Z"
90        // We accept an arbitrary depth of decimals.
91        // Note that we discard the timezone information -- all times are translated to UTC.
92        rule inst_string() -> DateTime<Utc> =
93            "#inst" whitespace()+ "\"" d:$(
94                ['0'..='9']*<4> "-" ['0'..='2'] ['0'..='9'] "-" ['0'..='3'] ['0'..='9']
95                "T"
96                ['0'..='2'] ['0'..='9'] ":" ['0'..='5'] ['0'..='9'] ":" ['0'..='6'] ['0'..='9']
97                ("." ['0'..='9']+)?
98                ("Z" / (("+" / "-") ['0'..='2'] ['0'..='9'] ":" ['0'..='5'] ['0'..='9']))
99            )
100            "\"" {?
101                DateTime::parse_from_rfc3339(d)
102                    .map(|t| t.with_timezone(&Utc))
103                    .map_err(|_| "invalid datetime")
104            }
105
106        rule inst_micros() -> DateTime<Utc> =
107            "#instmicros" whitespace()+ d:$( digit()+ ) {
108                let micros = d.parse::<i64>().unwrap();
109                let seconds: i64 = micros / 1000000;
110                let nanos: u32 = ((micros % 1000000).unsigned_abs() as u32) * 1000;
111                DateTime::from_timestamp(seconds, nanos).unwrap()
112            }
113
114        rule inst_millis() -> DateTime<Utc> =
115            "#instmillis" whitespace()+ d:$( digit()+ ) {
116                let millis = d.parse::<i64>().unwrap();
117                let seconds: i64 = millis / 1000;
118                let nanos: u32 = ((millis % 1000).unsigned_abs() as u32) * 1000000;
119                DateTime::from_timestamp(seconds, nanos).unwrap()
120            }
121
122        rule inst() -> SpannedValue = t:(inst_millis() / inst_micros() / inst_string())
123            { SpannedValue::Instant(t) }
124
125        rule uuid_string() -> Uuid =
126            "\"" u:$( ['a'..='f' | '0'..='9']*<8> "-" ['a'..='f' | '0'..='9']*<4> "-" ['a'..='f' | '0'..='9']*<4> "-" ['a'..='f' | '0'..='9']*<4> "-" ['a'..='f' | '0'..='9']*<12> ) "\"" {
127                Uuid::parse_str(u).expect("this is a valid UUID string")
128            }
129
130        pub rule uuid() -> SpannedValue = "#uuid" whitespace()+ u:uuid_string()
131            { SpannedValue::Uuid(u) }
132
133        rule namespace_divider() = "."
134        rule namespace_separator() = "/"
135
136        // TODO: Be more picky here
137        // Keywords follow the rules of symbols, except they can (and must) begin with :
138        // e.g. :fred or :my/fred. See https://github.com/edn-format/edn#keywords
139        rule symbol_char_initial() = ['a'..='z' | 'A'..='Z' | '0'..='9' | '*' | '!' | '_' | '?' | '$' | '%' | '&' | '=' | '<' | '>']
140        rule symbol_char_subsequent() = ['+' | 'a'..='z' | 'A'..='Z' | '0'..='9' | '*' | '!' | '_' | '?' | '$' | '%' | '&' | '=' | '<' | '>' | '-']
141
142        rule symbol_namespace() = symbol_char_initial() symbol_char_subsequent()* (namespace_divider() symbol_char_subsequent()+)*
143        rule symbol_name() = ( symbol_char_initial()+ symbol_char_subsequent()* )
144        rule plain_symbol_name() = symbol_name() / "..." / "."
145
146        rule keyword_prefix() = ":"
147
148        pub rule symbol() -> SpannedValue =
149            ns:( sns:$(symbol_namespace()) namespace_separator() { sns })?
150            n:$(plain_symbol_name())
151            { SpannedValue::from_symbol(ns, n) }
152            / expected!("symbol")
153
154        pub rule keyword() -> SpannedValue =
155            keyword_prefix()
156            ns:( sns:$(symbol_namespace()) namespace_separator() { sns })?
157            n:$(symbol_name())
158            { SpannedValue::from_keyword(ns, n) }
159            / expected!("keyword")
160
161        pub rule list() -> SpannedValue = "(" __() v:(value())* __() ")"
162            { SpannedValue::List(LinkedList::from_iter(v)) }
163
164        pub rule vector() -> SpannedValue = "[" __() v:(value())* __() "]"
165            { SpannedValue::Vector(v) }
166
167        pub rule set() -> SpannedValue = "#{" __() v:(value())* __() "}"
168            { SpannedValue::Set(BTreeSet::from_iter(v)) }
169
170        rule pair() -> (ValueAndSpan, ValueAndSpan) =
171            k:(value()) v:(value()) {
172                (k, v)
173            }
174
175        pub rule map() -> SpannedValue = "{" __() v:(pair())* __() "}"
176            { SpannedValue::Map(BTreeMap::from_iter(v)) }
177
178        // It's important that float comes before integer or the parser assumes that
179        // floats are integers and fails to parse
180        pub rule value() -> ValueAndSpan =
181            __() start:position!() v:(nil() / nan() / infinity() / boolean() / number() / inst() / uuid() / text() / keyword() / symbol() / list() / vector() / map() / set()) end:position!() __() {
182                ValueAndSpan {
183                    inner: v,
184                    span: Span::new(start, end)
185                }
186            }
187            / expected!("value")
188
189        rule atom() -> ValueAndSpan
190            = v:value() {? if v.is_atom() { Ok(v) } else { Err("expected atom") } }
191
192        // Clojure (and thus EDN) regards commas as whitespace, and thus the two-element vectors [1 2] and
193        // [1,,,,2] are equivalent, as are the maps {:a 1, :b 2} and {:a 1 :b 2}.
194        rule whitespace() = quiet!{[' ' | '\r' | '\n' | '\t' | ',']}
195        rule comment() = quiet!{";" [^ '\r' | '\n']* ['\r' | '\n']?}
196
197        rule __() = (whitespace() / comment())*
198
199        // Transaction entity parser starts here.
200
201        pub rule op() -> OpType
202            = ":db/add"     { OpType::Add }
203            / ":db/retract" { OpType::Retract }
204
205        rule raw_keyword() -> Keyword =
206            keyword_prefix()
207            ns:( sns:$(symbol_namespace()) namespace_separator() { sns })?
208            n:$(symbol_name()) {
209                match ns {
210                    Some(ns) => Keyword::namespaced(ns, n),
211                    None => Keyword::plain(n),
212                }
213            }
214            / expected!("keyword")
215
216        rule raw_forward_keyword() -> Keyword
217            = v:raw_keyword() {? if v.is_forward() { Ok(v) } else { Err("expected :forward or :forward/keyword") } }
218
219        rule raw_backward_keyword() -> Keyword
220            = v:raw_keyword() {? if v.is_backward() { Ok(v) } else { Err("expected :_backword or :backward/_keyword") } }
221
222        rule raw_namespaced_keyword() -> Keyword
223            = keyword_prefix() ns:$(symbol_namespace()) namespace_separator() n:$(symbol_name()) { Keyword::namespaced(ns, n) }
224            / expected!("namespaced keyword")
225
226        rule raw_forward_namespaced_keyword() -> Keyword
227            = v:raw_namespaced_keyword() {? if v.is_forward() { Ok(v) } else { Err("expected namespaced :forward/keyword") } }
228
229        rule raw_backward_namespaced_keyword() -> Keyword
230            = v:raw_namespaced_keyword() {? if v.is_backward() { Ok(v) } else { Err("expected namespaced :backward/_keyword") } }
231
232        rule entid() -> EntidOrIdent
233            = v:( raw_basedinteger() / raw_hexinteger() / raw_octalinteger() / raw_integer() ) { EntidOrIdent::Entid(v) }
234            / v:raw_namespaced_keyword() { EntidOrIdent::Ident(v) }
235            / expected!("entid")
236
237        rule forward_entid() -> EntidOrIdent
238            = v:( raw_basedinteger() / raw_hexinteger() / raw_octalinteger() / raw_integer() ) { EntidOrIdent::Entid(v) }
239            / v:raw_forward_namespaced_keyword() { EntidOrIdent::Ident(v) }
240            / expected!("forward entid")
241
242        rule backward_entid() -> EntidOrIdent
243            = v:raw_backward_namespaced_keyword() { EntidOrIdent::Ident(v.to_reversed()) }
244            / expected!("backward entid")
245
246        rule lookup_ref() -> LookupRef<ValueAndSpan>
247            = "(" __() "lookup-ref" __() a:(entid()) __() v:(value()) __() ")" { LookupRef { a: AttributePlace::Entid(a), v } }
248            / expected!("lookup-ref")
249
250        rule tx_function() -> TxFunction
251            = "(" __() n:$(symbol_name()) __() ")" { TxFunction { op: PlainSymbol::plain(n) } }
252
253        rule entity_place() -> EntityPlace<ValueAndSpan>
254            = v:raw_text() { EntityPlace::TempId(TempId::External(v).into()) }
255            / v:entid() { EntityPlace::Entid(v) }
256            / v:lookup_ref() { EntityPlace::LookupRef(v) }
257            / v:tx_function() { EntityPlace::TxFunction(v) }
258
259        rule value_place_pair() -> (EntidOrIdent, ValuePlace<ValueAndSpan>)
260            = k:(entid()) __() v:(value_place()) { (k, v) }
261
262        rule map_notation() -> MapNotation<ValueAndSpan>
263            = "{" __() kvs:(value_place_pair()*) __() "}" { kvs.into_iter().collect() }
264
265        rule value_place() -> ValuePlace<ValueAndSpan>
266            = __() v:lookup_ref() __() { ValuePlace::LookupRef(v) }
267            / __() v:tx_function() __() { ValuePlace::TxFunction(v) }
268            / __() "[" __() vs:(value_place()*) __() "]" __() { ValuePlace::Vector(vs) }
269            / __() v:map_notation() __() { ValuePlace::MapNotation(v) }
270            / __() v:atom() __() { ValuePlace::Atom(v) }
271
272        pub rule entity() -> Entity<ValueAndSpan>
273            = __() "[" __() op:(op()) __() e:(entity_place()) __() a:(forward_entid())  __() v:(value_place()) __()  "]" __() { Entity::AddOrRetract { op, e, a: AttributePlace::Entid(a), v } }
274            / __() "[" __() op:(op()) __() e:(value_place())  __() a:(backward_entid()) __() v:(entity_place()) __() "]" __() { Entity::AddOrRetract { op, e: v, a: AttributePlace::Entid(a), v: e } }
275            / __() map:map_notation() __() { Entity::MapNotation(map) }
276            / expected!("entity")
277
278        pub rule entities() -> Vec<Entity<ValueAndSpan>>
279            = __() "[" __() es:(entity()*) __() "]" __() { es }
280
281        // Query parser starts here.
282        //
283        // We expect every rule except the `raw_*` rules to eat whitespace
284        // (with `__`) at its start and finish.  That means that every string
285        // pattern (say "[") should be bracketed on either side with either a
286        // whitespace-eating rule or an explicit whitespace eating `__`.
287
288        // "+", "-", and "/" are special-cased here rather than added to
289        // symbol_char_initial because they conflict with number parsing (sign
290        // prefix) and namespace separation respectively. Adding them globally
291        // would let symbol_name match "-3" as a symbol in contexts that bypass
292        // the value rule's number-before-symbol ordering.
293        rule query_function() -> query::QueryFunction
294            = __() n:$(symbol_name() / "/" / "+" / "-") __() {? query::QueryFunction::from_symbol(&PlainSymbol::plain(n)).ok_or("expected query function") }
295
296        rule sexpr() -> query::FnArg
297            = __() "(" func:query_function() args:fn_arg()* ")" __() { query::FnArg::SExpr(func.0, args) }
298
299        rule fn_arg() -> query::FnArg
300            = sexpr()
301            / v:value() {? query::FnArg::from_value(&v).ok_or("expected query function argument") }
302
303        rule find_elem() -> query::Element
304            = __() v:variable() __() { query::Element::Variable(v) }
305            / __() "(" __() "the" v:variable() ")" __() { query::Element::Corresponding(v) }
306            / __() "(" __() "pull" var:variable() "[" patterns:pull_attribute()+ "]" __() ")" __() { query::Element::Pull(query::Pull { var, patterns }) }
307            / __() "(" func:query_function() args:fn_arg()* ")" __() { query::Element::Aggregate(query::Aggregate { func, args }) }
308
309        rule find_spec() -> query::FindSpec
310            = f:find_elem() "." __() { query::FindSpec::FindScalar(f) }
311            / fs:find_elem()+ { query::FindSpec::FindRel(fs) }
312            / __() "[" f:find_elem() __() "..." __() "]" __() { query::FindSpec::FindColl(f) }
313            / __() "[" fs:find_elem()+ "]" __() { query::FindSpec::FindTuple(fs) }
314
315        rule pull_attribute() -> query::PullAttributeSpec
316            = __() "*" __() { query::PullAttributeSpec::Wildcard }
317            / __() k:raw_forward_namespaced_keyword() __() alias:(":as" __() alias:raw_forward_keyword() __() { alias })? {
318                let attribute = query::PullConcreteAttribute::Ident(::std::sync::Arc::new(k));
319                let alias = alias.map(::std::sync::Arc::new);
320                query::PullAttributeSpec::Attribute(
321                    query::NamedPullAttribute {
322                        attribute,
323                        alias,
324                    })
325            }
326
327        rule limit() -> query::Limit
328            = __() v:variable() __() { query::Limit::Variable(v) }
329            / __() n:(raw_octalinteger() / raw_hexinteger() / raw_basedinteger() / raw_integer()) __() {?
330                if n > 0 {
331                    Ok(query::Limit::Fixed(n as u64))
332                } else {
333                    Err("expected positive integer")
334                }
335            }
336
337        rule order() -> query::Order
338            = __() "[" v:variable() __() ":desc" __() "]" __() { query::Order(query::Direction::Descending, v) }
339            / __() "[" v:variable() __() ":asc" __() "]" __() { query::Order(query::Direction::Ascending, v) }
340            / __() "[" v:variable() __() "]" __() { query::Order(query::Direction::Ascending, v) }
341
342
343        rule pattern_value_place() -> query::PatternValuePlace
344            = v:value() {? query::PatternValuePlace::from_value(&v).ok_or("expected pattern_value_place") }
345
346        rule pattern_non_value_place() -> query::PatternNonValuePlace
347            = v:value() {? query::PatternNonValuePlace::from_value(&v).ok_or("expected pattern_non_value_place") }
348
349        rule pattern() -> query::WhereClause
350            = __() "["
351              src:src_var()?
352              e:pattern_non_value_place()
353              a:pattern_non_value_place()
354              v:pattern_value_place()?
355              tx:pattern_non_value_place()?
356              "]" __()
357            {?
358                let v = v.unwrap_or(query::PatternValuePlace::Placeholder);
359                let tx = tx.unwrap_or(query::PatternNonValuePlace::Placeholder);
360
361                // Pattern::new takes care of reversal of reversed
362                // attributes: [?x :foo/_bar ?y] turns into
363                // [?y :foo/bar ?x].
364                query::Pattern::new(src, e, a, v, tx)
365                    .map(query::WhereClause::Pattern)
366                    .ok_or("expected pattern")
367            }
368
369        // TODO: this shouldn't be checked at parse time.
370        rule rule_vars() -> BTreeSet<query::Variable>
371            = vs:variable()+ {?
372                let given = vs.len();
373                let set: BTreeSet<query::Variable> = vs.into_iter().collect();
374                if given != set.len() {
375                    Err("expected unique variables")
376                } else {
377                    Ok(set)
378                }
379            }
380
381        rule or_pattern_clause() -> query::OrWhereClause
382            = clause:where_clause() { query::OrWhereClause::Clause(clause) }
383
384        rule or_and_clause() -> query::OrWhereClause
385            = __() "(" __() "and" clauses:where_clause()+ ")" __() { query::OrWhereClause::And(clauses) }
386
387        rule or_where_clause() -> query::OrWhereClause
388            = or_pattern_clause()
389            / or_and_clause()
390
391        rule or_clause() -> query::WhereClause
392            = __() "(" __() "or" clauses:or_where_clause()+ ")" __() {
393                 query::WhereClause::OrJoin(query::OrJoin::new(query::UnifyVars::Implicit, clauses))
394            }
395
396        rule or_join_clause() -> query::WhereClause
397            = __() "(" __() "or-join" __() "[" vars:rule_vars() "]" clauses:or_where_clause()+ ")" __() {
398                 query::WhereClause::OrJoin(query::OrJoin::new(query::UnifyVars::Explicit(vars), clauses))
399            }
400
401        rule not_clause() -> query::WhereClause
402            = __() "(" __() "not" clauses:where_clause()+ ")" __() {
403                 query::WhereClause::NotJoin(query::NotJoin::new(query::UnifyVars::Implicit, clauses))
404            }
405
406        rule not_join_clause() -> query::WhereClause
407            = __() "(" __() "not-join" __() "[" vars:rule_vars() "]" clauses:where_clause()+ ")" __() {
408                 query::WhereClause::NotJoin(query::NotJoin::new(query::UnifyVars::Explicit(vars), clauses))
409            }
410
411        rule type_annotation() -> query::WhereClause
412            = __() "[" __() "(" __() "type" var:variable() __() ty:raw_keyword() __() ")" __() "]" __() {
413                query::WhereClause::TypeAnnotation(
414                    query::TypeAnnotation {
415                        value_type: ty,
416                        variable: var,
417                    })
418            }
419
420        rule pred() -> query::WhereClause
421            = __() "[" __() expr:sexpr() __() "]" __() {
422                query::WhereClause::Pred(
423                    query::Predicate {
424                        expr,
425                    })
426            }
427
428        pub rule where_fn() -> query::WhereClause
429            = __() "[" __() expr:sexpr() __() binding:binding() "]" __() {
430                query::WhereClause::WhereFn(
431                    query::WhereFn {
432                        expr,
433                        binding,
434                    })
435            }
436
437        rule where_clause() -> query::WhereClause
438            // Right now we only support patterns and predicates. See #239 for more.
439            = pattern()
440            / or_join_clause()
441            / or_clause()
442            / not_join_clause()
443            / not_clause()
444            / type_annotation()
445            / pred()
446            / where_fn()
447
448        rule query_part() -> query::QueryPart
449            = __() ":find" fs:find_spec() { query::QueryPart::FindSpec(fs) }
450            / __() ":in" bs:binding()* { query::QueryPart::InBindings(bs) }
451            / __() ":limit" l:limit() { query::QueryPart::Limit(l) }
452            / __() ":order" os:order()+ { query::QueryPart::Order(os) }
453            / __() ":where" ws:where_clause()+ { query::QueryPart::WhereClauses(ws) }
454            / __() ":with" with_vars:variable()+ { query::QueryPart::WithVars(with_vars) }
455
456        rule map_query_part() -> query::QueryPart
457            = __() ":find" __() "[" fs:find_spec() "]" __() { query::QueryPart::FindSpec(fs) }
458            / __() ":in" __() "[" __() bs:binding()* "]" __() { query::QueryPart::InBindings(bs) }
459            / __() ":where" __() "[" ws:where_clause()+ "]" __() { query::QueryPart::WhereClauses(ws) }
460            / __() ":order" __() "[" os:order()+ "]" __() { query::QueryPart::Order(os) }
461            / __() ":limit" l:limit() { query::QueryPart::Limit(l) }
462
463        pub rule parse_query() -> query::ParsedQuery
464            = __() "[" qps:query_part()+ "]" __() {? query::ParsedQuery::from_parts(qps) }
465            / __() "{" qps:map_query_part()+ "}" __() {? query::ParsedQuery::from_parts(qps) }
466
467        rule variable() -> query::Variable
468            = v:value() {? query::Variable::from_value(&v).ok_or("expected variable") }
469
470        rule src_var() -> query::SrcVar
471            = v:value() {? query::SrcVar::from_value(&v).ok_or("expected src_var") }
472
473        rule variable_or_placeholder() -> query::VariableOrPlaceholder
474            = v:variable() { query::VariableOrPlaceholder::Variable(v) }
475            / __() "_" __() { query::VariableOrPlaceholder::Placeholder }
476
477        rule binding() -> query::Binding
478            = __() "[" __() "[" vs:variable_or_placeholder()+ "]" __() "]" __() { query::Binding::BindRel(vs) }
479            / __() "[" v:variable() "..." __() "]" __() { query::Binding::BindColl(v) }
480            / __() "[" vs:variable_or_placeholder()+ "]" __() { query::Binding::BindTuple(vs) }
481            / v:variable() { query::Binding::BindScalar(v) }
482    }
483}
484
485pub use inner::*;