use crate::ast;
use crate::ivt::*;
use crate::parser::{parse_cddl, slice_parse_cddl};
use crate::util::ValidateError;
use std::convert::TryInto;
pub type FlattenResult<T> = std::result::Result<T, ValidateError>;
pub fn flatten_from_str(cddl_input: &str) -> FlattenResult<RulesByName> {
let cddl = parse_cddl(cddl_input).map_err(ValidateError::ParseError)?;
flatten(&cddl)
}
pub fn slice_flatten_from_str(cddl_input: &str) -> FlattenResult<RulesWithStrings> {
let cddl = slice_parse_cddl(cddl_input).map_err(ValidateError::ParseError)?;
slice_flatten(&cddl)
}
pub fn flatten(cddl: &ast::Cddl) -> FlattenResult<RulesByName> {
cddl.rules.iter().map(|rule| flatten_rule(rule)).collect()
}
pub fn slice_flatten(cddl: &ast::CddlSlice) -> FlattenResult<RulesWithStrings> {
cddl.rules
.iter()
.map(|(rule, s)| {
let (name, flat) = flatten_rule(rule)?;
Ok((name, (flat, s.clone())))
})
.collect()
}
fn flatten_rule(rule: &ast::Rule) -> FlattenResult<(String, RuleDef)> {
use ast::RuleVal;
let node = match &rule.val {
RuleVal::AssignType(t) => flatten_type(&t)?,
RuleVal::AssignGroup(g) => flatten_groupentry(&g)?,
};
let ruledef = RuleDef {
generic_parms: rule.generic_parms.clone(),
node,
};
Ok((rule.name.clone(), ruledef))
}
fn flatten_type(ty: &ast::Type) -> FlattenResult<Node> {
let options: FlattenResult<Vec<Node>> = ty.0.iter().map(|type1| flatten_type1(type1)).collect();
let options = options?;
match options.len() {
0 => Err(ValidateError::Unsupported(
"flatten type with 0 options".into(),
)),
1 => Ok(options.into_iter().next().unwrap()),
_ => Ok(Node::Choice(Choice { options })),
}
}
fn flatten_type1(ty1: &ast::Type1) -> FlattenResult<Node> {
use ast::Type1::*;
match ty1 {
Simple(ty2) => flatten_type2(ty2),
Range(r) => flatten_range(r),
Control(_) => Err(ValidateError::Unsupported("control operator".into())),
}
}
fn range_point(point: &ast::Type2) -> FlattenResult<Node> {
let node = match point {
ast::Type2::Value(v) => flatten_value(v),
ast::Type2::Typename(t) => flatten_name_generic(t),
_ => Err(ValidateError::Structural(
"bad type on range operator".into(),
)),
}?;
match node {
Node::Rule(_) | Node::Literal(_) => Ok(node),
_ => Err(ValidateError::Structural(
"bad type on range operator".into(),
)),
}
}
fn flatten_range(range: &ast::TypeRange) -> FlattenResult<Node> {
let start = range_point(&range.start)?;
let end = range_point(&range.end)?;
Ok(Node::Range(Range {
start: start.into(),
end: end.into(),
inclusive: range.inclusive,
}))
}
fn flatten_value(value: &ast::Value) -> FlattenResult<Node> {
use ast::Value;
match value {
Value::Text(s) => Ok(literal_text(s)),
Value::Uint(u) => {
let value = num_to_i128(*u)?;
Ok(literal_int(value))
}
Value::Nint(n) => {
let value = num_to_i128(*n)?;
Ok(literal_int(value))
}
Value::Float(f) => Ok(literal_float(*f)),
Value::Bytes(b) => Ok(literal_bytes(b.clone())),
}
}
fn flatten_type2(ty2: &ast::Type2) -> FlattenResult<Node> {
use ast::Type2;
match ty2 {
Type2::Value(v) => flatten_value(v),
Type2::Typename(s) => flatten_name_generic(s),
Type2::Parethesized(t) => flatten_type(t),
Type2::Map(g) => flatten_map(&g),
Type2::Array(g) => flatten_array(&g),
Type2::Unwrap(r) => Ok(Node::Unwrap(flatten_rule_generic(r)?)),
}
}
fn flatten_typename(name: &str) -> FlattenResult<Node> {
let unsupported = |s: &str| -> FlattenResult<Node> {
let msg = format!("prelude type '{}'", s);
Err(ValidateError::Unsupported(msg))
};
let result = match name {
"any" => Node::PreludeType(PreludeType::Any),
"nil" | "null" => Node::PreludeType(PreludeType::Nil),
"bool" => Node::PreludeType(PreludeType::Bool),
"false" => literal_bool(false),
"true" => literal_bool(true),
"int" => Node::PreludeType(PreludeType::Int),
"uint" => Node::PreludeType(PreludeType::Uint),
"nint" => Node::PreludeType(PreludeType::Nint),
"float" => Node::PreludeType(PreludeType::Float),
"tstr" | "text" => Node::PreludeType(PreludeType::Tstr),
"bstr" | "bytes" => Node::PreludeType(PreludeType::Bstr),
"float16" | "float32" | "float64" | "float16-32" | "float32-64" => {
Node::PreludeType(PreludeType::Float)
}
"tdate" | "uri" | "b64url" | "b64legacy" | "regexp" | "mime-message" => {
Node::PreludeType(PreludeType::Tstr)
}
"biguint" | "bignint" | "encoded-cbor" => Node::PreludeType(PreludeType::Bstr),
"eb64url" | "eb64legacy" | "eb16" | "cbor-any" => Node::PreludeType(PreludeType::Any),
"time" => return unsupported(name),
"bigint" | "integer" | "unsigned" | "number" => return unsupported(name),
"decfrac" | "bigfloat" | "undefined" => return unsupported(name),
_ => Node::Rule(Rule::new_name(name)),
};
Ok(result)
}
fn flatten_rule_generic(name_generic: &ast::NameGeneric) -> FlattenResult<Rule> {
let result = flatten_name_generic(name_generic);
match result {
Ok(Node::Rule(r)) => Ok(r),
_ => Err(ValidateError::GenericError),
}
}
fn flatten_name_generic(name_generic: &ast::NameGeneric) -> FlattenResult<Node> {
let mut node = flatten_typename(&name_generic.name)?;
match node {
Node::Rule(ref mut r) => {
for arg in &name_generic.generic_args {
let arg_node = flatten_type1(&arg)?;
r.generic_args.push(arg_node);
}
}
_ => {
if !name_generic.generic_args.is_empty() {
return Err(ValidateError::GenericError);
}
}
}
Ok(node)
}
fn flatten_map(group: &ast::Group) -> FlattenResult<Node> {
let kvs = flatten_group(group)?;
Ok(Node::Map(Map { members: kvs }))
}
fn flatten_array(group: &ast::Group) -> FlattenResult<Node> {
let kvs = flatten_group(group)?;
Ok(Node::Array(Array { members: kvs }))
}
fn flatten_group(group: &ast::Group) -> FlattenResult<Vec<Node>> {
let group_choices = &group.0;
if group_choices.len() == 1 {
let groupchoice = &group_choices[0];
flatten_groupchoice(groupchoice)
} else {
let options: FlattenResult<Vec<Node>> = group_choices
.iter()
.map(|gc| {
let inner_members = flatten_groupchoice(gc)?;
Ok(Node::Group(Group {
members: inner_members,
}))
})
.collect();
let options = options?;
Ok(vec![Node::Choice(Choice { options })])
}
}
fn flatten_groupchoice(groupchoice: &ast::GrpChoice) -> FlattenResult<Vec<Node>> {
let group_entries = &groupchoice.0;
let kvs: FlattenResult<Vec<Node>> = group_entries
.iter()
.map(|group_entry| flatten_groupentry(group_entry))
.collect();
kvs
}
fn flatten_groupentry(group_entry: &ast::GrpEnt) -> FlattenResult<Node> {
let node = flatten_groupentry_val(&group_entry.val)?;
Ok(occur_wrap(&group_entry.occur, node))
}
fn flatten_groupentry_val(gev: &ast::GrpEntVal) -> FlattenResult<Node> {
use ast::GrpEntVal;
match gev {
GrpEntVal::Member(m) => flatten_member(m),
GrpEntVal::Groupname(s) => flatten_typename(s),
GrpEntVal::Parenthesized(g) => {
let nodes = flatten_group(g)?;
Ok(Node::Group(Group { members: nodes }))
}
}
}
fn occur_wrap(occur: &Option<ast::Occur>, node: Node) -> Node {
match &occur {
Some(o) => Node::Occur(Occur::new(o.clone(), node)),
None => node,
}
}
fn flatten_member(member: &ast::Member) -> FlattenResult<Node> {
match &member.key {
Some(key) => {
let cut = key.cut;
let key = flatten_memberkey(&key)?;
let value = flatten_type(&member.value)?;
Ok(Node::KeyValue(KeyValue::new(key, value, cut)))
}
None => flatten_type(&member.value),
}
}
fn num_to_i128<T>(n: T) -> FlattenResult<i128>
where
T: TryInto<i128> + std::fmt::Display + Copy,
{
n.try_into().map_err(|_| {
let msg = format!("integer conversion failed: {}", n);
ValidateError::Structural(msg)
})
}
fn flatten_memberkey(memberkey: &ast::MemberKey) -> FlattenResult<Node> {
use ast::MemberKeyVal;
match &memberkey.val {
MemberKeyVal::Bareword(s) => {
Ok(literal_text(s.clone()))
}
MemberKeyVal::Type1(t1) => flatten_type1(&t1),
MemberKeyVal::Value(v) => flatten_value(&v),
}
}
#[cfg(test)]
#[macro_use]
mod test_utils {
use super::*;
use std::collections::BTreeMap;
impl From<&str> for Rule {
fn from(s: &str) -> Self {
Rule {
name: s.to_string(),
generic_args: vec![],
}
}
}
impl From<&str> for Node {
fn from(s: &str) -> Self {
Node::Rule(Rule::from(s))
}
}
impl From<Node> for RuleDef {
fn from(n: Node) -> Self {
RuleDef {
generic_parms: Vec::default(),
node: n,
}
}
}
pub fn make_rules(mut list: Vec<(&str, Node)>) -> RulesByName {
list.drain(..)
.map(|(s, n)| (s.to_string(), RuleDef::from(n)))
.collect()
}
pub fn make_rule(name: &str, node: Node) -> RulesByName {
let mut result = BTreeMap::new();
result.insert(name.to_string(), RuleDef::from(node));
result
}
pub fn make_generic_rule(name: &str, generic_parms: &[&str], node: Node) -> RulesByName {
let mut result = BTreeMap::new();
let generic_parms: Vec<String> = generic_parms.iter().map(|s| String::from(*s)).collect();
result.insert(
name.to_string(),
RuleDef {
generic_parms,
node,
},
);
result
}
pub trait CreateLiteral {
fn literal(self) -> Node;
}
impl CreateLiteral for &str {
fn literal(self) -> Node {
Node::Literal(Literal::Text(self.to_string()))
}
}
impl CreateLiteral for u64 {
fn literal(self) -> Node {
Node::Literal(Literal::Int(self.into()))
}
}
pub fn tstr() -> Node {
Node::PreludeType(PreludeType::Tstr)
}
pub fn make_map() -> Map {
Map {
members: Vec::new(),
}
}
pub fn make_array() -> Array {
Array {
members: Vec::new(),
}
}
pub trait Append {
fn append<T: Into<Node>>(self, t: T) -> Self;
}
impl Append for Map {
fn append<T: Into<Node>>(mut self, t: T) -> Self {
let node: Node = t.into();
self.members.push(node);
self
}
}
impl Append for Array {
fn append<T: Into<Node>>(mut self, t: T) -> Self {
let node: Node = t.into();
self.members.push(node);
self
}
}
impl From<Map> for Node {
fn from(m: Map) -> Self {
Node::Map(m)
}
}
impl From<Array> for Node {
fn from(a: Array) -> Self {
Node::Array(a)
}
}
impl From<KeyValue> for Node {
fn from(kv: KeyValue) -> Self {
Node::KeyValue(kv)
}
}
impl From<Rule> for Node {
fn from(r: Rule) -> Self {
Node::Rule(r)
}
}
#[derive(Copy, Clone)]
pub enum KvCut {
Cut,
NoCut,
}
pub use KvCut::*;
impl From<KvCut> for bool {
fn from(c: KvCut) -> bool {
match c {
Cut => true,
NoCut => false,
}
}
}
pub fn kv(k: Node, v: Node, cut: KvCut) -> KeyValue {
KeyValue {
key: Box::new(k),
value: Box::new(v),
cut: cut.into(),
}
}
}
#[cfg(test)]
mod tests {
use super::test_utils::*;
use super::*;
#[test]
fn test_flatten_literal_int() {
let cddl_input = r#"thing = 1"#;
let result = flatten_from_str(cddl_input).unwrap();
let expected = make_rule("thing", 1.literal());
assert_eq!(result, expected);
}
#[test]
fn test_flatten_literal_tstr() {
let cddl_input = r#"thing = "abc""#;
let result = flatten_from_str(cddl_input).unwrap();
let expected = make_rule("thing", "abc".literal());
assert_eq!(result, expected);
}
#[test]
fn test_flatten_prelude_reference() {
let cddl_input = r#"thing = int"#;
let result = flatten_from_str(cddl_input).unwrap();
let expected = make_rule("thing", Node::PreludeType(PreludeType::Int));
assert_eq!(result, expected);
}
#[test]
fn test_flatten_type_reference() {
let cddl_input = r#"thing = foo"#;
let result = flatten_from_str(cddl_input).unwrap();
assert_eq!(result, make_rule("thing", Node::Rule(Rule::from("foo"))));
}
#[test]
fn test_flatten_map() {
let cddl_input = r#"thing = { foo: tstr }"#;
let result = flatten_from_str(cddl_input).unwrap();
let expected = make_rule(
"thing",
make_map().append(kv("foo".literal(), tstr(), Cut)).into(),
);
assert_eq!(result, expected);
let cddl_input = r#"thing = { tstr => tstr }"#;
let result = flatten_from_str(cddl_input).unwrap();
let expected = make_rule("thing", make_map().append(kv(tstr(), tstr(), NoCut)).into());
assert_eq!(result, expected);
let cddl_input = r#"foo = "bar" thing = { foo => tstr }"#;
let result = flatten_from_str(cddl_input).unwrap();
let expected = make_rules(vec![
("foo", "bar".literal()),
(
"thing",
make_map().append(kv("foo".into(), tstr(), NoCut)).into(),
),
]);
assert_eq!(result, expected);
}
#[test]
fn test_flatten_generic() {
let cddl_input = "message<t, v> = [t, v]";
let result = flatten_from_str(cddl_input).unwrap();
let expected = make_generic_rule(
"message",
&["t", "v"],
make_array()
.append(Rule::from("t"))
.append(Rule::from("v"))
.into(),
);
assert_eq!(result, expected);
}
}