minicas_core/rules/
pred_spec.rs1use 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#[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}