Skip to main content

minicas_core/rules/
pred_spec.rs

1use crate::ast::{Const, Node};
2use crate::pred::{Predicate, PredicateOp};
3use serde::Deserialize;
4use std::convert::TryInto;
5
6#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
7#[serde(deny_unknown_fields)]
8pub struct EqualPair {
9    l: Vec<usize>,
10    #[serde(alias = "and")]
11    r: Vec<usize>,
12}
13
14/// Describes how a predicate is specified in a rule file.
15#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
16#[serde(deny_unknown_fields)]
17pub struct PredSpec {
18    pub op: Option<String>,
19    pub not_op: Option<String>,
20
21    #[serde(alias = "const")]
22    pub const_val: Option<String>,
23
24    #[serde(alias = "equivalent")]
25    pub equal: Option<Vec<EqualPair>>,
26
27    #[serde(alias = "operand")]
28    pub lhs: Option<Box<PredSpec>>,
29    pub rhs: Option<Box<PredSpec>>,
30
31    #[serde(alias = "num_arms")]
32    pub arity: Option<usize>,
33
34    pub children: Option<Vec<PredSpec>>,
35}
36
37impl TryInto<Predicate> for PredSpec {
38    type Error = String;
39
40    fn try_into(self) -> Result<Predicate, Self::Error> {
41        let mut children = match (self.lhs, self.rhs) {
42            (Some(lhs), None) => vec![Some((*lhs).try_into().map_err(|e| format!("lhs: {}", e))?)],
43            (Some(lhs), Some(rhs)) => vec![
44                Some((*lhs).try_into().map_err(|e| format!("lhs: {}", e))?),
45                Some((*rhs).try_into().map_err(|e| format!("rhs: {}", e))?),
46            ],
47            (None, Some(rhs)) => vec![
48                None,
49                Some((*rhs).try_into().map_err(|e| format!("rhs: {}", e))?),
50            ],
51            (None, None) => vec![],
52        };
53        if children.len() > 0 && self.children.is_some() {
54            return Err("both lhs/rhs/operand and children[] specified".into());
55        }
56        if let Some(c) = self.children {
57            children = c
58                .into_iter()
59                .enumerate()
60                .map(|(i, p)| match p.try_into() {
61                    Ok(p) => Ok(Some(p)),
62                    Err(e) => Err(format!("children.{}: {}", i, e)),
63                })
64                .collect::<Result<Vec<Option<Predicate>>, Self::Error>>()?;
65        }
66
67        let equivalent = self
68            .equal
69            .map(|v| v.into_iter().map(|p| (p.l.into(), p.r.into())).collect())
70            .unwrap_or(vec![]);
71
72        Ok(Predicate {
73            op: match self.op {
74                Some(op) => Some(
75                    op.as_str()
76                        .try_into()
77                        .map_err(|_| format!("unknown op: {}", op))?,
78                ),
79                None => {
80                    if self.const_val.is_some() {
81                        Some(PredicateOp::Const)
82                    } else {
83                        None
84                    }
85                }
86            },
87            not_op: match self.not_op {
88                Some(not_op) => Some(
89                    not_op
90                        .as_str()
91                        .try_into()
92                        .map_err(|_| format!("unknown op: {}", not_op))?,
93                ),
94                None => None,
95            },
96            const_value: match self.const_val {
97                Some(s) => match Node::try_from(s.as_str())?.as_const() {
98                    Some(Const(tv)) => Some(tv.clone()),
99                    None => {
100                        return Err(format!("const value {} is not a constant expression", s));
101                    }
102                },
103                None => None,
104            },
105            arity: self.arity,
106            equivalent,
107            children,
108            ..Predicate::default()
109        })
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    use crate::{
117        ast::{BinaryOp, UnaryOp},
118        TyValue,
119    };
120    use toml::de;
121
122    #[test]
123    fn op() {
124        assert_eq!(
125            de::from_str::<PredSpec>(r#"op = '/'"#).unwrap().try_into(),
126            Ok(Predicate::op(PredicateOp::Binary(BinaryOp::Div)))
127        );
128    }
129    #[test]
130    fn not_op() {
131        assert_eq!(
132            de::from_str::<PredSpec>(r#"not_op = '*'"#)
133                .unwrap()
134                .try_into(),
135            Ok(Predicate {
136                not_op: Some(PredicateOp::Binary(BinaryOp::Mul)),
137                ..Predicate::default()
138            })
139        );
140    }
141
142    #[test]
143    fn const_val() {
144        assert_eq!(
145            de::from_str::<PredSpec>(r#"const = '2'"#)
146                .unwrap()
147                .try_into(),
148            Ok(Predicate {
149                const_value: Some(TyValue::from(2)),
150                ..Predicate::op(PredicateOp::Const)
151            })
152        );
153        assert_eq!(
154            de::from_str::<PredSpec>(r#"const = 'true'"#)
155                .unwrap()
156                .try_into(),
157            Ok(Predicate {
158                const_value: Some(TyValue::from(true)),
159                ..Predicate::op(PredicateOp::Const)
160            })
161        );
162    }
163
164    #[test]
165    fn nested() {
166        assert_eq!(
167            de::from_str::<PredSpec>(r#"lhs = {const = '2'}"#)
168                .unwrap()
169                .try_into(),
170            Ok(Predicate {
171                children: vec![Some(Predicate {
172                    const_value: Some(TyValue::from(2)),
173                    ..Predicate::op(PredicateOp::Const)
174                })],
175                ..Predicate::default()
176            })
177        );
178        assert_eq!(
179            de::from_str::<PredSpec>(r#"operand = {op = 'neg'}"#)
180                .unwrap()
181                .try_into(),
182            Ok(Predicate {
183                children: vec![Some(Predicate::op(PredicateOp::Unary(UnaryOp::Negate)))],
184                ..Predicate::default()
185            })
186        );
187        assert_eq!(
188            de::from_str::<PredSpec>(r#"rhs = {op = 'neg'}"#)
189                .unwrap()
190                .try_into(),
191            Ok(Predicate {
192                children: vec![
193                    None,
194                    Some(Predicate::op(PredicateOp::Unary(UnaryOp::Negate)))
195                ],
196                ..Predicate::default()
197            })
198        );
199        assert_eq!(
200            de::from_str::<PredSpec>(
201                r#"
202            	rhs = {op = 'neg'}
203            	lhs = {op = 'abs'}"#
204            )
205            .unwrap()
206            .try_into(),
207            Ok(Predicate {
208                children: vec![
209                    Some(Predicate::op(PredicateOp::Unary(UnaryOp::Abs))),
210                    Some(Predicate::op(PredicateOp::Unary(UnaryOp::Negate)))
211                ],
212                ..Predicate::default()
213            })
214        );
215    }
216}