2017-01-06 15:15:57 +00:00
|
|
|
/* vim: set filetype=rust.rustpeg */
|
|
|
|
|
|
|
|
// Copyright 2016 Mozilla
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
|
|
// this file except in compliance with the License. You may obtain a copy of the
|
|
|
|
// License at http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
// Unless required by applicable law or agreed to in writing, software distributed
|
|
|
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
|
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
|
|
// specific language governing permissions and limitations under the License.
|
|
|
|
|
|
|
|
use std::collections::{BTreeSet, BTreeMap, LinkedList};
|
|
|
|
use std::iter::FromIterator;
|
2017-02-03 18:14:23 +00:00
|
|
|
use std::f64::{NAN, INFINITY, NEG_INFINITY};
|
2017-01-11 21:51:34 +00:00
|
|
|
|
2017-04-29 03:11:55 +00:00
|
|
|
use chrono::{
|
|
|
|
DateTime,
|
|
|
|
TimeZone,
|
2017-11-21 16:24:08 +00:00
|
|
|
Utc
|
2017-04-29 03:11:55 +00:00
|
|
|
};
|
2017-01-06 15:15:57 +00:00
|
|
|
use num::BigInt;
|
|
|
|
use ordered_float::OrderedFloat;
|
2017-04-29 03:11:55 +00:00
|
|
|
use uuid::Uuid;
|
2017-02-14 15:43:32 +00:00
|
|
|
|
|
|
|
use types::{SpannedValue, Span, ValueAndSpan};
|
2017-01-06 15:15:57 +00:00
|
|
|
|
|
|
|
// Goal: Be able to parse https://github.com/edn-format/edn
|
|
|
|
// Also extensible to help parse http://docs.datomic.com/query.html
|
|
|
|
|
|
|
|
// Debugging hint: test using `cargo test --features peg/trace -- --nocapture`
|
|
|
|
// to trace where the parser is failing
|
|
|
|
|
|
|
|
// TODO: Support tagged elements
|
|
|
|
// TODO: Support discard
|
|
|
|
|
2017-02-14 15:43:32 +00:00
|
|
|
pub nil -> ValueAndSpan =
|
|
|
|
start:#position "nil" end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Nil,
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-06 15:15:57 +00:00
|
|
|
|
2017-02-14 15:43:32 +00:00
|
|
|
pub nan -> ValueAndSpan =
|
|
|
|
start:#position "#f" whitespace+ "NaN" end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Float(OrderedFloat(NAN)),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-03 18:14:23 +00:00
|
|
|
|
2017-02-14 15:43:32 +00:00
|
|
|
pub infinity -> ValueAndSpan =
|
|
|
|
start:#position "#f" whitespace+ s:$(sign) "Infinity" end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Float(OrderedFloat(if s == "+" { INFINITY } else { NEG_INFINITY })),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
2017-02-03 18:14:23 +00:00
|
|
|
}
|
|
|
|
|
2017-02-14 15:43:32 +00:00
|
|
|
pub boolean -> ValueAndSpan =
|
|
|
|
start:#position "true" end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Boolean(true),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
|
|
|
} /
|
|
|
|
start:#position "false" end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Boolean(false),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-06 15:15:57 +00:00
|
|
|
|
|
|
|
digit = [0-9]
|
2017-02-11 00:03:35 +00:00
|
|
|
alphanumeric = [0-9a-zA-Z]
|
|
|
|
octaldigit = [0-7]
|
|
|
|
validbase = [3][0-6] / [12][0-9] / [2-9]
|
|
|
|
hex = [0-9a-fA-F]
|
2017-01-06 15:15:57 +00:00
|
|
|
sign = "-" / "+"
|
|
|
|
|
2017-02-14 15:43:32 +00:00
|
|
|
pub bigint -> ValueAndSpan =
|
|
|
|
start:#position b:$( sign? digit+ ) "N" end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::BigInteger(b.parse::<BigInt>().unwrap()),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
2017-02-02 10:36:53 +00:00
|
|
|
}
|
2017-01-06 15:15:57 +00:00
|
|
|
|
2017-02-14 15:43:32 +00:00
|
|
|
pub octalinteger -> ValueAndSpan =
|
|
|
|
start:#position "0" i:$( octaldigit+ ) end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Integer(i64::from_str_radix(i, 8).unwrap()),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
2017-02-11 00:03:35 +00:00
|
|
|
}
|
|
|
|
|
2017-02-14 15:43:32 +00:00
|
|
|
pub hexinteger -> ValueAndSpan =
|
|
|
|
start:#position "0x" i:$( hex+ ) end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Integer(i64::from_str_radix(i, 16).unwrap()),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
2017-02-11 00:03:35 +00:00
|
|
|
}
|
|
|
|
|
2017-02-14 15:43:32 +00:00
|
|
|
pub basedinteger -> ValueAndSpan =
|
2017-02-11 00:03:35 +00:00
|
|
|
// Only allow values 2-36
|
2017-02-14 15:43:32 +00:00
|
|
|
start:#position b:$( validbase ) "r" i:$( alphanumeric+ ) end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Integer(i64::from_str_radix(i, b.parse::<u32>().unwrap()).unwrap()),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
2017-02-11 00:03:35 +00:00
|
|
|
}
|
|
|
|
|
2017-02-14 15:43:32 +00:00
|
|
|
pub integer -> ValueAndSpan =
|
|
|
|
start:#position i:$( sign? digit+ ) end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Integer(i.parse::<i64>().unwrap()),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
2017-02-02 10:36:53 +00:00
|
|
|
}
|
2017-01-06 15:15:57 +00:00
|
|
|
|
|
|
|
frac = sign? digit+ "." digit+
|
|
|
|
exp = sign? digit+ ("e" / "E") sign? digit+
|
|
|
|
frac_exp = sign? digit+ "." digit+ ("e" / "E") sign? digit+
|
|
|
|
|
|
|
|
// The order here is important - frac_exp must come before (exp / frac) or the
|
|
|
|
// parser assumes exp or frac when the float is really a frac_exp and fails
|
2017-02-14 15:43:32 +00:00
|
|
|
pub float -> ValueAndSpan =
|
|
|
|
start:#position f:$( frac_exp / exp / frac ) end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Float(OrderedFloat(f.parse::<f64>().unwrap())),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
2017-02-02 10:36:53 +00:00
|
|
|
}
|
2017-01-06 15:15:57 +00:00
|
|
|
|
|
|
|
// TODO: \newline, \return, \space and \tab
|
|
|
|
special_char = quote / tab
|
|
|
|
quote = "\\\""
|
|
|
|
tab = "\\tab"
|
|
|
|
char = [^"] / special_char
|
|
|
|
|
2017-02-14 15:43:32 +00:00
|
|
|
pub text -> ValueAndSpan =
|
|
|
|
start:#position "\"" t:$( char* ) "\"" end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Text(t.to_string()),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
2017-02-02 10:36:53 +00:00
|
|
|
}
|
2017-01-06 15:15:57 +00:00
|
|
|
|
2017-04-29 03:11:55 +00:00
|
|
|
|
|
|
|
// RFC 3339 timestamps. #inst "1985-04-12T23:20:50.52Z"
|
|
|
|
// We accept an arbitrary depth of decimals.
|
|
|
|
// Note that we discard the timezone information -- all times are translated to UTC.
|
2017-11-21 16:24:08 +00:00
|
|
|
pub inst_string -> DateTime<Utc> =
|
2017-04-29 03:11:55 +00:00
|
|
|
"#inst" whitespace+ "\"" d:$( [0-9]*<4> "-" [0-2][0-9] "-" [0-3][0-9]
|
|
|
|
"T"
|
|
|
|
[0-2][0-9] ":" [0-5][0-9] ":" [0-6][0-9]
|
|
|
|
("." [0-9]+)?
|
|
|
|
"Z" / (("+" / "-") [0-2][0-9] ":" [0-5][0-9])
|
|
|
|
)
|
|
|
|
"\"" {?
|
|
|
|
DateTime::parse_from_rfc3339(d)
|
2017-11-21 16:24:08 +00:00
|
|
|
.map(|t| t.with_timezone(&Utc))
|
2017-04-29 03:11:55 +00:00
|
|
|
.map_err(|_| "invalid datetime") // Oh, rustpeg.
|
|
|
|
}
|
|
|
|
|
2017-11-21 16:24:08 +00:00
|
|
|
pub inst_micros -> DateTime<Utc> =
|
2017-04-29 03:11:55 +00:00
|
|
|
"#instmicros" whitespace+ d:$( digit+ ) {
|
|
|
|
let micros = d.parse::<i64>().unwrap();
|
|
|
|
let seconds: i64 = micros / 1000000;
|
|
|
|
let nanos: u32 = ((micros % 1000000).abs() as u32) * 1000;
|
2017-11-21 16:24:08 +00:00
|
|
|
Utc.timestamp(seconds, nanos)
|
2017-04-29 03:11:55 +00:00
|
|
|
}
|
|
|
|
|
2017-11-21 16:24:08 +00:00
|
|
|
pub inst_millis -> DateTime<Utc> =
|
2017-04-29 03:11:55 +00:00
|
|
|
"#instmillis" whitespace+ d:$( digit+ ) {
|
|
|
|
let millis = d.parse::<i64>().unwrap();
|
|
|
|
let seconds: i64 = millis / 1000;
|
|
|
|
let nanos: u32 = ((millis % 1000).abs() as u32) * 1000000;
|
2017-11-21 16:24:08 +00:00
|
|
|
Utc.timestamp(seconds, nanos)
|
2017-04-29 03:11:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub inst -> ValueAndSpan =
|
|
|
|
start:#position t:(inst_millis / inst_micros / inst_string) end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Instant(t),
|
|
|
|
span: Span::new(start, end)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub uuid_string -> Uuid =
|
|
|
|
"\"" u:$( [a-f0-9]*<8> "-" [a-f0-9]*<4> "-" [a-f0-9]*<4> "-" [a-f0-9]*<4> "-" [a-f0-9]*<12> ) "\"" {
|
|
|
|
Uuid::parse_str(u).expect("this is a valid UUID string")
|
|
|
|
}
|
|
|
|
|
|
|
|
pub uuid -> ValueAndSpan =
|
|
|
|
start:#position "#uuid" whitespace+ u:(uuid_string) end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Uuid(u),
|
|
|
|
span: Span::new(start, end)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-11 21:51:34 +00:00
|
|
|
namespace_divider = "."
|
|
|
|
namespace_separator = "/"
|
|
|
|
|
2017-01-06 15:15:57 +00:00
|
|
|
// TODO: Be more picky here
|
2017-02-02 10:52:34 +00:00
|
|
|
// Keywords follow the rules of symbols, except they can (and must) begin with :
|
|
|
|
// e.g. :fred or :my/fred. See https://github.com/edn-format/edn#keywords
|
2017-01-11 21:51:34 +00:00
|
|
|
symbol_char_initial = [a-z] / [A-Z] / [0-9] / [*!_?$%&=<>]
|
|
|
|
symbol_char_subsequent = [a-z] / [A-Z] / [0-9] / [-*!_?$%&=<>]
|
2017-01-06 15:15:57 +00:00
|
|
|
|
2017-02-02 10:52:34 +00:00
|
|
|
symbol_namespace = symbol_char_initial symbol_char_subsequent* (namespace_divider symbol_char_subsequent+)*
|
2017-03-06 22:55:14 +00:00
|
|
|
symbol_name = ( symbol_char_initial+ symbol_char_subsequent* )
|
|
|
|
plain_symbol_name = symbol_name / "..." / "."
|
2017-01-11 21:51:34 +00:00
|
|
|
|
|
|
|
keyword_prefix = ":"
|
2017-01-06 15:15:57 +00:00
|
|
|
|
2017-02-14 15:43:32 +00:00
|
|
|
pub symbol -> ValueAndSpan =
|
|
|
|
start:#position
|
|
|
|
ns:( sns:$(symbol_namespace) namespace_separator { sns })?
|
2017-03-06 22:55:14 +00:00
|
|
|
n:$(plain_symbol_name)
|
2017-02-14 15:43:32 +00:00
|
|
|
end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::from_symbol(ns, n),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub keyword -> ValueAndSpan =
|
|
|
|
start:#position
|
|
|
|
keyword_prefix
|
|
|
|
ns:( sns:$(symbol_namespace) namespace_separator { sns })?
|
|
|
|
n:$(symbol_name)
|
|
|
|
end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::from_keyword(ns, n),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub list -> ValueAndSpan =
|
|
|
|
start:#position "(" __ v:(value)* __ ")" end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::List(LinkedList::from_iter(v)),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub vector -> ValueAndSpan =
|
|
|
|
start:#position "[" __ v:(value)* __ "]" end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Vector(v),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub set -> ValueAndSpan =
|
|
|
|
start:#position "#{" __ v:(value)* __ "}" end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Set(BTreeSet::from_iter(v)),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pair -> (ValueAndSpan, ValueAndSpan) =
|
2017-02-02 10:36:53 +00:00
|
|
|
k:(value) v:(value) {
|
|
|
|
(k, v)
|
|
|
|
}
|
2017-01-06 15:15:57 +00:00
|
|
|
|
2017-02-14 15:43:32 +00:00
|
|
|
pub map -> ValueAndSpan =
|
|
|
|
start:#position "{" __ v:(pair)* __ "}" end:#position {
|
|
|
|
ValueAndSpan {
|
|
|
|
inner: SpannedValue::Map(BTreeMap::from_iter(v)),
|
2017-04-06 17:06:28 +00:00
|
|
|
span: Span::new(start, end)
|
2017-02-14 15:43:32 +00:00
|
|
|
}
|
2017-02-02 10:36:53 +00:00
|
|
|
}
|
2017-01-06 15:15:57 +00:00
|
|
|
|
|
|
|
// It's important that float comes before integer or the parser assumes that
|
|
|
|
// floats are integers and fails to parse
|
2017-02-14 15:43:32 +00:00
|
|
|
pub value -> ValueAndSpan =
|
2017-04-29 03:11:55 +00:00
|
|
|
__ v:(nil / nan / infinity / boolean / float / octalinteger / hexinteger / basedinteger / inst / uuid / bigint / integer / text / keyword / symbol / list / vector / map / set) __ {
|
2017-02-02 10:36:53 +00:00
|
|
|
v
|
|
|
|
}
|
2017-01-17 19:26:45 +00:00
|
|
|
|
|
|
|
// Clojure (and thus EDN) regards commas as whitespace, and thus the two-element vectors [1 2] and
|
|
|
|
// [1,,,,2] are equivalent, as are the maps {:a 1, :b 2} and {:a 1 :b 2}.
|
|
|
|
whitespace = (" " / "\r" / "\n" / "\t" / ",")
|
2017-01-17 19:25:31 +00:00
|
|
|
comment = ";" [^\r\n]* ("\r" / "\n")?
|
2017-01-06 15:15:57 +00:00
|
|
|
|
2017-01-17 19:25:31 +00:00
|
|
|
__ = (whitespace / comment)*
|