esl_compiler/builder/
behavior.rs

1//! Behavior requirement builder module
2use crate::builder::design::{build_all_design_clauses, DesignClauses};
3use crate::builder::Stringency;
4use crate::context::Context;
5use crate::cursor::{Cursor, Error, Token};
6use crate::diagnostics::DiagnosticError;
7use crate::location::FileLocation;
8use crate::map::Map;
9use crate::parser::Rule;
10
11/// Behavior requirement or constraint. States how variable values should respond to sets of values
12/// for stimuli.
13#[derive(Clone, Debug, PartialEq)]
14pub struct Behavior {
15    /// Behavior's name or label.
16    pub name: Token,
17    /// Complete behavior's textual location.
18    pub loc: FileLocation,
19    /// All cases as mapping from their names to the case.
20    pub cases: Map<String, BehaviorCase>,
21    /// Whether this is a requirement or a constraint.
22    pub stringency: Stringency,
23}
24impl std::fmt::Display for Behavior {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        write!(f, "{}", self.name)
27    }
28}
29
30/// Behavior case that states how certain variables should respond to a set of values of stimuli.
31#[derive(Clone, Debug, PartialEq)]
32pub struct BehaviorCase {
33    /// Behavior case's name or label.
34    pub name: Token,
35    /// Case's textual location.
36    pub loc: FileLocation,
37    /// When clauses or fallback.
38    pub when: BehaviorWhen,
39    /// Then clauses.
40    pub then: BehaviorThen,
41}
42impl std::fmt::Display for BehaviorCase {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        write!(f, "{}", self.name)
45    }
46}
47
48/// When statement of a behavior case. Either the fallback case (at most one) or a set of
49/// clauses as a mapping.
50#[derive(Clone, Debug, PartialEq)]
51pub enum BehaviorWhen {
52    Fallback,
53    Clauses(DesignClauses),
54}
55
56/// Then statement of a behavior case. A mapping of names to design rules.
57pub type BehaviorThen = DesignClauses;
58
59/// Build a single behavior requirement or constraint.
60pub fn build_behavior_instantiation(
61    ctx: &mut Context,
62    behavior_requirement: Cursor,
63    stringency: &Stringency,
64) -> Result<Behavior, Error> {
65    let mut cursors = behavior_requirement.into_inner();
66    let name = cursors
67        .req_first(Rule::some_label)?
68        .into_inner()
69        .req_first(Rule::some_name)?
70        .into();
71    let mut behavior = Behavior {
72        name,
73        loc: cursors.as_location(),
74        stringency: stringency.clone(),
75        cases: Default::default(),
76    };
77
78    for behavior_case in cursors.get_all(Rule::behavior_case) {
79        let case = build_behavior_case(ctx, behavior_case)?;
80        if let Err(dupe) = behavior.cases.insert(case.to_string(), case) {
81            ctx.error(DiagnosticError::DuplicateDefinition, &dupe.loc);
82        }
83    }
84
85    Ok(behavior)
86}
87
88/// Build a single behavior case.
89pub fn build_behavior_case(
90    ctx: &mut Context,
91    behavior_case: Cursor,
92) -> Result<BehaviorCase, Error> {
93    let mut cursors = behavior_case.into_inner();
94    let name = cursors
95        .req_first(Rule::some_label)?
96        .into_inner()
97        .req_first(Rule::some_name)?
98        .into();
99
100    let when = cursors
101        .req_first(Rule::behavior_when)
102        .and_then(|behavior_when| build_behavior_when(ctx, behavior_when))?;
103
104    let then = cursors
105        .req_first(Rule::behavior_then)
106        .and_then(|behavior_then| build_behavior_then(ctx, behavior_then))?;
107
108    Ok(BehaviorCase {
109        name,
110        loc: cursors.as_location(),
111        when,
112        then,
113    })
114}
115
116/// Build a single behavior when statement.
117pub fn build_behavior_when(
118    ctx: &mut Context,
119    behavior_when: Cursor,
120) -> Result<BehaviorWhen, Error> {
121    let mut cursors = behavior_when.into_inner();
122    // We can safely check the first cursor, as it otherwise is the "when" (without "no other case applies").
123    let first = { cursors.req_next()? };
124    if first.as_rule() == Rule::kw_section_behavior_fallback {
125        Ok(BehaviorWhen::Fallback)
126    } else {
127        let clauses = build_all_design_clauses(ctx, cursors.get_all(Rule::design_clause))?;
128        Ok(BehaviorWhen::Clauses(clauses))
129    }
130}
131
132/// Build a single behavior then statement.
133pub fn build_behavior_then(
134    ctx: &mut Context,
135    behavior_then: Cursor,
136) -> Result<BehaviorThen, Error> {
137    build_all_design_clauses(ctx, behavior_then.into_inner().get_all(Rule::design_clause))
138}