Skip to content

Commit

Permalink
feat: CDDL module structure (#55)
Browse files Browse the repository at this point in the history
* feat: module extension

* refactor: compact return type

* chore: fmtfix

* fix: lint

* fix: function docs comment

* feat: add placeholders

* refactor: compact error mapping

* fix: remove cddl test error type

* fix: postlude test

* fix: move postlude content inside test

* docs: CDDLErrorType

* docs: fix typo

* fix: test print

* feat: placeholder files

* refactor: remove direct type

* chore: fix linting

* fix: return unit instead

* fix: input as &mut String

* chore: fmtfix
  • Loading branch information
apskhem authored Jan 10, 2024
1 parent 33a01a3 commit 99608dc
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// working in progress
1 change: 1 addition & 0 deletions hermes/crates/cbork/cddl-parser/src/grammar/rfc_9615.pest
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// working in progress
130 changes: 105 additions & 25 deletions hermes/crates/cbork/cddl-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,86 @@
use std::fmt::Debug;

pub use pest::Parser;
use pest_derive::Parser;

extern crate derive_more;
use derive_more::{Display, From};
use pest::{error::Error, iterators::Pairs};

// Parser with DEBUG rules. These rules are only used in tests.
#[derive(Parser)]
#[grammar = "grammar/cddl.pest"]
#[grammar = "grammar/cddl_test.pest"] // Ideally this would only be used in tests.
pub struct CDDLParser;
pub mod rfc_8610 {
pub use pest::Parser;

/// Represents an error that may occur during CDDL parsing.
#[derive(Display, Debug, From)]
pub struct CDDLError(pest::error::Error<Rule>);
#[derive(pest_derive::Parser)]
#[grammar = "grammar/rfc_8610.pest"]
pub struct RFC8610Parser;
}

pub mod rfc_9615 {
pub use pest::Parser;

#[derive(pest_derive::Parser)]
#[grammar = "grammar/rfc_8610.pest"]
#[grammar = "grammar/rfc_9615.pest"]
pub struct RFC8610Parser;
}

pub mod cddl {
pub use pest::Parser;

#[derive(pest_derive::Parser)]
#[grammar = "grammar/rfc_8610.pest"]
#[grammar = "grammar/rfc_9615.pest"]
#[grammar = "grammar/cddl_modules.pest"]
pub struct RFC8610Parser;
}

pub mod cddl_test {
pub use pest::Parser;

// Parser with DEBUG rules. These rules are only used in tests.
#[derive(pest_derive::Parser)]
#[grammar = "grammar/rfc_8610.pest"]
#[grammar = "grammar/rfc_9615.pest"]
#[grammar = "grammar/cddl_modules.pest"]
#[grammar = "grammar/cddl_test.pest"] // Ideally this would only be used in tests.
pub struct CDDLTestParser;
}

/// Represents different parser extensions for handling CDDL specifications.
pub enum Extension {
/// RFC8610 ONLY limited parser.
RFC8610Parser,
/// RFC8610 and RFC9615 limited parser.
RFC9615Parser,
/// RFC8610, RFC9615, and CDDL modules.
CDDLParser,
}

// CDDL Standard Postlude - read from an external file
pub const POSTLUDE: &str = include_str!("grammar/postlude.cddl");

// TODO: this is temporary. need to add more pragmatic nodes
#[derive(Debug)]
pub enum AST<'a> {
RFC8610(Pairs<'a, rfc_8610::Rule>),
RFC9615(Pairs<'a, rfc_9615::Rule>),
CDDL(Pairs<'a, cddl::Rule>),
}

/// Represents different types of errors related to different types of extension.
#[derive(Display, Debug)]
pub enum CDDLErrorType {
/// An error related to RFC 8610 extension.
RFC8610(Error<rfc_8610::Rule>),
/// An error related to RFC 9615 extension.
RFC9615(Error<rfc_9615::Rule>),
/// An error related to CDDL modules extension.
CDDL(Error<cddl::Rule>),
}

/// Represents an error that may occur during CDDL parsing.
#[derive(Display, Debug, From)]
pub struct CDDLError(CDDLErrorType);

/// Parses and checks semantically a CDDL input string.
///
/// # Arguments
Expand All @@ -45,35 +107,53 @@ pub const POSTLUDE: &str = include_str!("grammar/postlude.cddl");
/// # Examples
///
/// ```rs
/// use cddl_parser::parse_cddl;
/// use cddl_parser::{parse_cddl, Extension};
/// use std:fs;
///
/// let input = fs::read_to_string("path/to/your/file.cddl").unwrap();
/// let result = parse_cddl(&input);
/// let mut input = fs::read_to_string("path/to/your/file.cddl").unwrap();
/// let result = parse_cddl(&mut input, &Extension::CDDLParser);
/// assert!(result.is_ok());
/// ```
pub fn parse_cddl(input: &str) -> Result<(), Box<CDDLError>> {
let result = CDDLParser::parse(Rule::cddl, input);

match result {
Ok(c) => println!("{c:?}"),
Err(e) => {
println!("{e:?}");
println!("{e}");
return Err(Box::new(CDDLError::from(e)));
pub fn parse_cddl<'a>(
input: &'a mut String, extension: &Extension,
) -> Result<Box<AST<'a>>, Box<CDDLError>> {
input.push_str("\n\n");
input.push_str(POSTLUDE);

let result = match extension {
Extension::RFC8610Parser => {
rfc_8610::RFC8610Parser::parse(rfc_8610::Rule::cddl, input)
.map(AST::RFC8610)
.map_err(CDDLErrorType::RFC8610)
},
}
Extension::RFC9615Parser => {
rfc_9615::RFC8610Parser::parse(rfc_9615::Rule::cddl, input)
.map(AST::RFC9615)
.map_err(CDDLErrorType::RFC9615)
},
Extension::CDDLParser => {
cddl::RFC8610Parser::parse(cddl::Rule::cddl, input)
.map(AST::CDDL)
.map_err(CDDLErrorType::CDDL)
},
};

result.map(Box::new).map_err(|e| {
println!("{e:?}");
println!("{e}");

Ok(())
Box::new(CDDLError::from(e))
})
}

#[cfg(test)]
mod tests {
use crate::{parse_cddl, POSTLUDE};
use crate::*;

#[test]
fn it_works() {
let result = parse_cddl(POSTLUDE);
let mut input = String::new();
let result = parse_cddl(&mut input, &Extension::CDDLParser);

match result {
Ok(c) => println!("{c:?}"),
Expand Down
17 changes: 10 additions & 7 deletions hermes/crates/cbork/cddl-parser/tests/byte_sequences.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// cspell: words hexpair rstuvw abcdefghijklmnopqrstuvwyz rstuvw

use cddl_parser::{self, CDDLParser, Parser, Rule};
use cddl_parser::{
self,
cddl_test::{CDDLTestParser, Parser, Rule},
};

#[test]
/// Test if the `HEX_PAIR` rule passes properly.
Expand All @@ -10,12 +13,12 @@ fn check_hexpair() {
let not_hex_pairs = vec!["0", " 0", "0 ", "az", "0p"];

for hp in hex_pairs {
let parse = CDDLParser::parse(Rule::HEX_PAIR, hp);
let parse = CDDLTestParser::parse(Rule::HEX_PAIR, hp);
assert!(parse.is_ok());
}

for hp in not_hex_pairs {
let parse = CDDLParser::parse(Rule::HEX_PAIR, hp);
let parse = CDDLTestParser::parse(Rule::HEX_PAIR, hp);
assert!(parse.is_err());
}
}
Expand All @@ -37,12 +40,12 @@ fn check_url_base64() {
];

for test in tests {
let parse = CDDLParser::parse(Rule::URL_BASE64_TEST, test);
let parse = CDDLTestParser::parse(Rule::URL_BASE64_TEST, test);
assert!(parse.is_ok());
}

for test in fails {
let parse = CDDLParser::parse(Rule::URL_BASE64_TEST, test);
let parse = CDDLTestParser::parse(Rule::URL_BASE64_TEST, test);
assert!(parse.is_err());
}
}
Expand Down Expand Up @@ -77,12 +80,12 @@ fn check_bytes() {
];

for test in test {
let parse = CDDLParser::parse(Rule::bytes_TEST, test);
let parse = CDDLTestParser::parse(Rule::bytes_TEST, test);
assert!(parse.is_ok());
}

for test in fail {
let parse = CDDLParser::parse(Rule::bytes_TEST, test);
let parse = CDDLTestParser::parse(Rule::bytes_TEST, test);
assert!(parse.is_err());
}
}
49 changes: 26 additions & 23 deletions hermes/crates/cbork/cddl-parser/tests/character_sets.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// cspell: words PCHAR pchar BCHAR bchar SESC sesc SCHAR schar fffd fffe

use cddl_parser::{self, CDDLParser, Parser, Rule};
use cddl_parser::{
self,
cddl_test::{CDDLTestParser, Parser, Rule},
};

#[test]
/// Test if the `WHITESPACE` rule passes properly.
Expand All @@ -10,11 +13,11 @@ fn check_whitespace() {
let not_whitespace = "not";

for ws in whitespace {
let parse = CDDLParser::parse(Rule::WHITESPACE, ws);
let parse = CDDLTestParser::parse(Rule::WHITESPACE, ws);
assert!(parse.is_ok());
}

let parse = CDDLParser::parse(Rule::WHITESPACE, not_whitespace);
let parse = CDDLTestParser::parse(Rule::WHITESPACE, not_whitespace);
assert!(parse.is_err());
}

Expand All @@ -23,15 +26,15 @@ fn check_whitespace() {
fn check_pchar() {
for x in ('\u{0}'..='\u{ff}').map(char::from) {
let test = format!("{x}");
let parse = CDDLParser::parse(Rule::PCHAR, &test);
let parse = CDDLTestParser::parse(Rule::PCHAR, &test);
if x < ' ' || x == '\u{7f}' {
assert!(parse.is_err());
} else {
assert!(parse.is_ok());
}
}

let parse = CDDLParser::parse(Rule::ASCII_VISIBLE, "\r");
let parse = CDDLTestParser::parse(Rule::ASCII_VISIBLE, "\r");
assert!(parse.is_err());
}

Expand All @@ -40,15 +43,15 @@ fn check_pchar() {
fn check_bchar() {
for x in ('\u{0}'..='\u{ff}').map(char::from) {
let test = format!("{x}");
let parse = CDDLParser::parse(Rule::BCHAR, &test);
let parse = CDDLTestParser::parse(Rule::BCHAR, &test);
if x != '\n' && x != '\r' && x < ' ' || x == '\u{27}' || x == '\u{5c}' || x == '\u{7f}' {
assert!(parse.is_err());
} else {
assert!(parse.is_ok());
}
}

let parse = CDDLParser::parse(Rule::ASCII_VISIBLE, "\r");
let parse = CDDLTestParser::parse(Rule::ASCII_VISIBLE, "\r");
assert!(parse.is_err());
}

Expand All @@ -57,15 +60,15 @@ fn check_bchar() {
fn check_sesc() {
for x in (' '..='\u{ff}').map(char::from) {
let test = format!("\\{x}");
let parse = CDDLParser::parse(Rule::SESC, &test);
let parse = CDDLTestParser::parse(Rule::SESC, &test);
if x == '\u{7f}' {
assert!(parse.is_err());
} else {
assert!(parse.is_ok());
}
}

let parse = CDDLParser::parse(Rule::ASCII_VISIBLE, "\r");
let parse = CDDLTestParser::parse(Rule::ASCII_VISIBLE, "\r");
assert!(parse.is_err());
}

Expand All @@ -74,14 +77,14 @@ fn check_sesc() {
fn check_ascii_visible() {
for x in (b' '..=b'~').map(char::from) {
let test = x.to_string();
let parse = CDDLParser::parse(Rule::ASCII_VISIBLE, &test);
let parse = CDDLTestParser::parse(Rule::ASCII_VISIBLE, &test);
assert!(parse.is_ok());
}

let parse = CDDLParser::parse(Rule::ASCII_VISIBLE, "\r");
let parse = CDDLTestParser::parse(Rule::ASCII_VISIBLE, "\r");
assert!(parse.is_err());

let parse = CDDLParser::parse(Rule::ASCII_VISIBLE, "\u{80}");
let parse = CDDLTestParser::parse(Rule::ASCII_VISIBLE, "\u{80}");
assert!(parse.is_err());
}

Expand All @@ -91,18 +94,18 @@ fn check_schar_ascii_visible() {
let invalids = "\"\\";
for x in (b' '..=b'~').map(char::from) {
let test = x.to_string();
let parse = CDDLParser::parse(Rule::SCHAR_ASCII_VISIBLE, &test);
let parse = CDDLTestParser::parse(Rule::SCHAR_ASCII_VISIBLE, &test);
if invalids.contains(x) {
assert!(parse.is_err());
} else {
assert!(parse.is_ok());
}
}

let parse = CDDLParser::parse(Rule::SCHAR_ASCII_VISIBLE, "\r");
let parse = CDDLTestParser::parse(Rule::SCHAR_ASCII_VISIBLE, "\r");
assert!(parse.is_err());

let parse = CDDLParser::parse(Rule::SCHAR_ASCII_VISIBLE, "\u{80}");
let parse = CDDLTestParser::parse(Rule::SCHAR_ASCII_VISIBLE, "\u{80}");
assert!(parse.is_err());
}

Expand All @@ -112,36 +115,36 @@ fn check_bchar_ascii_visible() {
let invalids = "'\\";
for x in (b' '..=b'~').map(char::from) {
let test = x.to_string();
let parse = CDDLParser::parse(Rule::BCHAR_ASCII_VISIBLE, &test);
let parse = CDDLTestParser::parse(Rule::BCHAR_ASCII_VISIBLE, &test);
if invalids.contains(x) {
assert!(parse.is_err());
} else {
assert!(parse.is_ok());
}
}

let parse = CDDLParser::parse(Rule::BCHAR_ASCII_VISIBLE, "\r");
let parse = CDDLTestParser::parse(Rule::BCHAR_ASCII_VISIBLE, "\r");
assert!(parse.is_err());

let parse = CDDLParser::parse(Rule::BCHAR_ASCII_VISIBLE, "\u{80}");
let parse = CDDLTestParser::parse(Rule::BCHAR_ASCII_VISIBLE, "\u{80}");
assert!(parse.is_err());
}

#[test]
/// Test if the `UNICODE_CHAR` rule passes properly.
fn check_unicode() {
let parse = CDDLParser::parse(Rule::UNICODE_CHAR, "\r");
let parse = CDDLTestParser::parse(Rule::UNICODE_CHAR, "\r");
assert!(parse.is_err());

let parse = CDDLParser::parse(Rule::UNICODE_CHAR, "\u{80}");
let parse = CDDLTestParser::parse(Rule::UNICODE_CHAR, "\u{80}");
assert!(parse.is_ok());

let parse = CDDLParser::parse(Rule::UNICODE_CHAR, "\u{10fffd}");
let parse = CDDLTestParser::parse(Rule::UNICODE_CHAR, "\u{10fffd}");
assert!(parse.is_ok());

let parse = CDDLParser::parse(Rule::UNICODE_CHAR, "\u{7ffff}");
let parse = CDDLTestParser::parse(Rule::UNICODE_CHAR, "\u{7ffff}");
assert!(parse.is_ok());

let parse = CDDLParser::parse(Rule::UNICODE_CHAR, "\u{10fffe}");
let parse = CDDLTestParser::parse(Rule::UNICODE_CHAR, "\u{10fffe}");
assert!(parse.is_err());
}
Loading

0 comments on commit 99608dc

Please sign in to comment.