Skip to content

Commit 95b095c

Browse files
committedFeb 1, 2021
update deps and initial abnf support
1 parent c50aa68 commit 95b095c

File tree

4 files changed

+109
-2
lines changed

4 files changed

+109
-2
lines changed
 

‎Cargo.toml

+5-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ chrono = { version = "0.4", optional = true }
2525
clap = { version = "2.33", optional = true }
2626
codespan-reporting = "0.11"
2727
hexf-parse = "0.1"
28-
itertools = "0.9"
28+
itertools = "0.10"
2929
lexical-core = "0.7"
3030
regex = { version = "1.4", default-features = false, features = ["std"] }
3131
regex-syntax = { version = "0.6", optional = true }
@@ -34,13 +34,16 @@ serde_cbor = { version = "0.11.1", optional = true, default-features = false, fe
3434
serde_json = { version = "1.0", optional = true, default-features = false }
3535
uriparse = { version = "0.6", optional = true }
3636
base64-url = { version = "1.4", optional = true }
37+
abnf_to_pest = "0.5"
38+
pest_meta = "2.1"
39+
pest_vm = "2.1"
3740

3841
[dev-dependencies]
3942
indoc = "1.0"
4043
pretty_assertions = { git = "https://github.com/colin-kiegel/rust-pretty-assertions" }
4144

4245
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
43-
crossterm = { version = "0.18", optional = true }
46+
crossterm = { version = "0.19", optional = true }
4447

4548
[target.'cfg(target_arch = "wasm32")'.dependencies]
4649
console_error_panic_hook = "0.1"

‎src/token.rs

+14
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ pub enum Token<'a> {
123123
/// Proposed control extension for numeric addition. See
124124
/// https://tools.ietf.org/html/draft-ietf-cbor-cddl-control-01#section-2.2.
125125
PLUS,
126+
/// .abnf control operator
127+
/// Proposed control extension for embedded ABNF as UTF-8. See
128+
/// https://tools.ietf.org/html/draft-ietf-cbor-cddl-control-01#section-3
129+
ABNF,
130+
/// .abnfb control operator
131+
/// Proposed control extension for embedded ABNF as a sequence of bytes. See
132+
/// https://tools.ietf.org/html/draft-ietf-cbor-cddl-control-01#section-3
133+
ABNFB,
126134

127135
/// group to choice enumeration '&'
128136
GTOCHOICE,
@@ -488,6 +496,8 @@ impl<'a> fmt::Display for Token<'a> {
488496
Token::WITHIN => write!(f, ".within"),
489497
Token::CAT => write!(f, ".cat"),
490498
Token::PLUS => write!(f, ".plus"),
499+
Token::ABNF => write!(f, ".abnf"),
500+
Token::ABNFB => write!(f, ".abnfb"),
491501
Token::AND => write!(f, ".and"),
492502
Token::LT => write!(f, ".lt"),
493503
Token::LE => write!(f, ".le"),
@@ -568,6 +578,8 @@ pub fn lookup_control_from_str<'a>(ident: &str) -> Option<Token<'a>> {
568578
".pcre" => Some(Token::PCRE),
569579
".cat" => Some(Token::CAT),
570580
".plus" => Some(Token::PLUS),
581+
".abnf" => Some(Token::ABNF),
582+
".abnfb" => Some(Token::ABNFB),
571583
_ => None,
572584
}
573585
}
@@ -605,6 +617,8 @@ pub fn control_str_from_token(t: &Token) -> Option<&'static str> {
605617
Token::PCRE => Some(".pcre"),
606618
Token::CAT => Some(".cat"),
607619
Token::PLUS => Some(".plus"),
620+
Token::ABNF => Some(".abnf"),
621+
Token::ABNFB => Some(".abnfb"),
608622
_ => None,
609623
}
610624
}

‎src/validator/control.rs

+67
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use pest_meta::parser;
2+
13
use crate::{
24
ast::{Identifier, Operator, RangeCtlOp, Rule, Type2, CDDL},
35
token::ByteValue,
@@ -561,3 +563,68 @@ pub fn plus_operation<'a>(
561563

562564
Ok(values)
563565
}
566+
567+
pub fn validate_abnf(abnf: &str, target: &str) -> Result<(), String> {
568+
if let Some(idx) = abnf.find('\n') {
569+
let (rule, abnf) = abnf.split_at(idx);
570+
571+
let rules = abnf_to_pest::parse_abnf(abnf).map_err(|e| e.to_string())?;
572+
let mut w = Vec::new();
573+
abnf_to_pest::render_rules_to_pest(rules)
574+
.render(0, &mut w)
575+
.unwrap();
576+
let pest = String::from_utf8(w).unwrap();
577+
578+
let pairs = pest_meta::parser::parse(pest_meta::parser::Rule::grammar_rules, &pest)
579+
.map_err(|e| e.to_string())?;
580+
581+
let ast = pest_meta::parser::consume_rules(pairs).unwrap();
582+
583+
let vm = pest_vm::Vm::new(pest_meta::optimizer::optimize(ast.clone()));
584+
585+
let rule = rule.replace("-", "_");
586+
let _ = vm.parse(&rule, target).map_err(|e| e.to_string())?;
587+
}
588+
589+
Ok(())
590+
}
591+
592+
#[cfg(test)]
593+
mod tests {
594+
use super::*;
595+
use indoc::indoc;
596+
597+
#[test]
598+
fn test_abnf() -> std::result::Result<(), Box<dyn std::error::Error>> {
599+
let abnf_str = indoc!(
600+
r#"
601+
date-fullyear
602+
date-fullyear = 4DIGIT
603+
date-month = 2DIGIT ; 01-12
604+
date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on
605+
; month/year
606+
time-hour = 2DIGIT ; 00-23
607+
time-minute = 2DIGIT ; 00-59
608+
time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap sec
609+
; rules
610+
time-secfrac = "." 1*DIGIT
611+
time-numoffset = ("+" / "-") time-hour ":" time-minute
612+
time-offset = "Z" / time-numoffset
613+
614+
partial-time = time-hour ":" time-minute ":" time-second
615+
[time-secfrac]
616+
full-date = date-fullyear "-" date-month "-" date-mday
617+
full-time = partial-time time-offset
618+
619+
date-time = full-date "T" full-time
620+
621+
DIGIT = %x30-39 ; 0-9
622+
; abbreviated here
623+
"#
624+
);
625+
626+
validate_abnf(abnf_str, "2009")?;
627+
628+
Ok(())
629+
}
630+
}

‎src/validator/json.rs

+23
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{
66
visitor::{self, *},
77
};
88
use chrono::{TimeZone, Utc};
9+
use control::validate_abnf;
910
use serde_json::Value;
1011
use std::{borrow::Cow, collections::HashMap, convert::TryFrom, fmt};
1112

@@ -1175,6 +1176,25 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> {
11751176

11761177
self.ctrl = None;
11771178
}
1179+
t @ Some(Token::ABNF) => {
1180+
self.ctrl = t;
1181+
1182+
match target {
1183+
Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1184+
match self.json {
1185+
Value::String(_) | Value::Array(_) => self.visit_type2(controller)?,
1186+
_ => self.add_error(format!(
1187+
".abnf control can only be matched against a JSON string, got {}",
1188+
self.json,
1189+
)),
1190+
}
1191+
}
1192+
_ => self.add_error(format!(
1193+
".abnf can only be matched against string data type, got {}",
1194+
target,
1195+
)),
1196+
}
1197+
}
11781198
_ => {
11791199
self.add_error(format!("unsupported control operator {}", ctrl));
11801200
}
@@ -2016,6 +2036,9 @@ impl<'a> Visitor<'a, Error> for JSONValidator<'a> {
20162036
Some(format!("expected \"{}\" to match regex \"{}\"", s, t))
20172037
}
20182038
}
2039+
Some(Token::ABNF) => validate_abnf(t, s)
2040+
.err()
2041+
.map(|e| format!("\"{}\" is not valid against abnf: {}", s, e)),
20192042
_ => {
20202043
if s == t {
20212044
None

0 commit comments

Comments
 (0)