1use crate::builder::auxiliary::{build_kw_aux_verb, AuxiliaryVerb};
4use crate::builder::subject::Subject;
5use crate::builder::value::{build_some_value, Value};
6use crate::builder::variable::build_some_variable;
7use crate::builder::Stringency;
8use crate::context::Context;
9use crate::cursor::{Cursor, Error, ExhaustedSnafu, Token, UnexpectedRuleSnafu};
10use crate::diagnostics::DiagnosticError;
11use crate::location::FileLocation;
12use crate::map::Map;
13use crate::parser::Rule;
14use crate::reference::{Reference, VariableTarget};
15
16#[derive(Clone, Debug, PartialEq)]
18pub struct Design {
19 pub name: Token,
21 pub loc: FileLocation,
23 pub rules: DesignRules,
25 pub subclauses: DesignClauses,
27 pub stringency: Stringency,
29}
30impl std::fmt::Display for Design {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 write!(f, "{}", self.name)
33 }
34}
35
36pub type DesignRules = Vec<DesignRule>;
38
39pub type DesignClauses = Map<String, DesignClause>;
41
42#[derive(Clone, Debug, PartialEq)]
44pub struct DesignClause {
45 pub name: Token,
47 pub loc: FileLocation,
49 pub rules: DesignRules,
51}
52impl std::fmt::Display for DesignClause {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 write!(f, "{}", self.name)
55 }
56}
57
58#[derive(Clone, Debug, PartialEq)]
60pub enum DesignRule {
61 Comparison(Comparison),
63 Objective(Objective),
65}
66
67#[derive(Clone, Debug, PartialEq)]
69pub struct Comparison {
70 pub loc: FileLocation,
72 pub subject: Reference<Subject, VariableTarget>,
74 pub auxiliary: AuxiliaryVerb,
76 pub operator: ComparisonOperator,
78 pub reference: ComparisonReference,
80}
81
82#[derive(Clone, Debug, PartialEq)]
84pub enum ComparisonOperator {
85 Approximately,
87 EqualTo,
89 NotEqualTo,
91 AtLeast,
93 GreaterThan,
95 AtMost,
97 SmallerThan,
99}
100
101#[derive(Clone, Debug, PartialEq)]
103pub enum ComparisonReference {
104 Value(Value),
106 Variable(Reference<Subject, VariableTarget>),
108}
109
110#[derive(Clone, Debug, PartialEq)]
112pub struct Objective {
113 pub loc: FileLocation,
115 pub subject: Reference<Subject, VariableTarget>,
117 pub auxiliary: AuxiliaryVerb,
119 pub operator: ObjectiveOperator,
121}
122
123#[derive(Clone, Debug, PartialEq)]
125pub enum ObjectiveOperator {
126 Minimized,
128 Maximized,
130}
131
132pub fn build_design_instantiation(
134 ctx: &mut Context,
135 design_instantiation: Cursor,
136 stringency: Stringency,
137) -> Result<Design, Error> {
138 let mut cs = design_instantiation.into_inner();
139 let loc = cs.as_location();
140
141 let clause = build_design_clause(ctx, cs.req_first(Rule::design_clause)?)?;
142 let subclauses = match cs.find_first(Rule::with_subclauses) {
143 Some(with_subclauses) => build_all_design_clauses(
144 ctx,
145 with_subclauses.into_inner().get_all(Rule::design_clause),
146 )?,
147 None => Default::default(),
148 };
149
150 Ok(Design {
151 name: clause.name,
152 loc,
153 rules: clause.rules,
154 subclauses,
155 stringency,
156 })
157}
158
159pub fn build_design_clause(
161 ctx: &mut Context,
162 design_clause: Cursor,
163) -> Result<DesignClause, Error> {
164 let loc = design_clause.as_location();
165 let mut cs = design_clause.into_inner();
166 let name = cs
167 .req_first(Rule::some_label)?
168 .into_inner()
169 .req_first(Rule::some_name)?
170 .into();
171 let rules = cs
172 .get_all(Rule::design_rule)
173 .map(|design_rule| build_design_rule(ctx, design_rule))
174 .collect::<Result<DesignRules, Error>>()?;
175 Ok(DesignClause { name, loc, rules })
176}
177
178pub fn build_with_subclauses(
180 ctx: &mut Context,
181 with_subclauses: Cursor,
182) -> Result<DesignClauses, Error> {
183 build_all_design_clauses(
184 ctx,
185 with_subclauses.into_inner().get_all(Rule::design_clause),
186 )
187}
188
189pub fn build_all_design_clauses<'a, T: Iterator<Item = Cursor<'a>>>(
191 ctx: &mut Context,
192 design_clauses: T,
193) -> Result<DesignClauses, Error> {
194 let mut clauses: DesignClauses = Default::default();
195 for c in design_clauses {
196 let clause = build_design_clause(ctx, c)?;
197 if let Err(dupe) = clauses.insert(clause.to_string(), clause) {
198 ctx.error(DiagnosticError::DuplicateDefinition, &dupe.loc);
199 }
200 }
201 Ok(clauses)
202}
203
204pub fn build_design_rule(ctx: &mut Context, design_rule: Cursor) -> Result<DesignRule, Error> {
206 let mut cs = design_rule.into_inner();
207 let subject = cs
208 .req_first(Rule::some_variable)
209 .and_then(|some_variable| build_some_variable(ctx, some_variable))?;
210 let auxiliary = cs
211 .req_first(Rule::design_rule_aux_verb)
212 .and_then(|design_rule_aux_verb| build_design_rule_aux_verb(ctx, design_rule_aux_verb))?;
213
214 let next = cs.req_next()?;
215 let loc = next.as_location();
216 let design_rule: DesignRule = match next.as_rule() {
217 Rule::design_rule_objective => {
218 let operator = build_design_rule_objective(ctx, next)?;
219 DesignRule::Objective(Objective {
220 loc,
221 subject,
222 auxiliary,
223 operator,
224 })
225 }
226 Rule::design_rule_comparison => {
227 let mut cs = next.into_inner();
228 let operator =
229 cs.req_first(Rule::design_rule_operator)
230 .and_then(|design_rule_operator| {
231 build_design_rule_operator(ctx, design_rule_operator)
232 })?;
233 let reference = cs.req_next().and_then(|p| match p.as_rule() {
234 Rule::some_value => build_some_value(ctx, p).map(ComparisonReference::Value),
235 Rule::some_variable => {
236 build_some_variable(ctx, p).map(ComparisonReference::Variable)
237 }
238 _ => UnexpectedRuleSnafu::from(p).fail()?,
239 })?;
240
241 DesignRule::Comparison(Comparison {
242 loc,
243 subject,
244 auxiliary,
245 operator,
246 reference,
247 })
248 }
249 _ => UnexpectedRuleSnafu::from(next).fail()?,
250 };
251 Ok(design_rule)
252}
253
254pub fn build_design_rule_aux_verb(
256 ctx: &mut Context,
257 design_rule_aux_verb: Cursor,
258) -> Result<AuxiliaryVerb, Error> {
259 let first = design_rule_aux_verb.into_inner().req_next()?;
260 match first.as_rule() {
261 Rule::kw_aux_is => Ok(AuxiliaryVerb::Is),
262 Rule::kw_aux_verb => Ok(build_kw_aux_verb(ctx, first)?),
263 _ => UnexpectedRuleSnafu::from(first).fail()?,
264 }
265}
266
267pub fn build_design_rule_objective(
269 ctx: &mut Context,
270 design_rule_objective: Cursor,
271) -> Result<ObjectiveOperator, Error> {
272 let c = design_rule_objective.into_inner().req_next()?;
273 match c.as_rule() {
274 Rule::kw_comparison_minimized => Ok(ObjectiveOperator::Minimized),
275 Rule::kw_comparison_maximized => Ok(ObjectiveOperator::Maximized),
276 _ => UnexpectedRuleSnafu::from(c).fail()?,
277 }
278}
279
280pub fn build_design_rule_operator(
282 ctx: &mut Context,
283 design_rule_operator: Cursor,
284) -> Result<ComparisonOperator, Error> {
285 let loc = design_rule_operator.as_location();
286 for c in design_rule_operator.into_inner() {
287 match c.as_rule() {
288 Rule::kw_comparison_at => continue,
289 Rule::kw_comparison_to => continue,
290 Rule::kw_comparison_approximately => return Ok(ComparisonOperator::Approximately),
291 Rule::kw_comparison_greater => return Ok(ComparisonOperator::GreaterThan),
292 Rule::kw_comparison_smaller => return Ok(ComparisonOperator::SmallerThan),
293 Rule::kw_comparison_not => return Ok(ComparisonOperator::NotEqualTo),
294 Rule::kw_comparison_equal => return Ok(ComparisonOperator::EqualTo),
295 Rule::kw_comparison_least => return Ok(ComparisonOperator::AtLeast),
296 Rule::kw_comparison_most => return Ok(ComparisonOperator::AtMost),
297 _ => UnexpectedRuleSnafu::from(c).fail()?,
298 };
299 }
300 ExhaustedSnafu { loc }.fail()
301}