rtlola_hir/
hir.rs

1//! This module covers the High-Level Intermediate Representation (HIR) of an RTLola specification.
2//!
3//! The [RtLolaHir] is specifically designed to allow for convenient manipulation and analysis.  Hence, it is perfect for working *on* the specification rather than work *with* it.  
4//! # Most Notable Structs and Enums
5//! * [RtLolaMir](https://docs.rs/rtlola_frontend/struct.RtLolaMir.html) is the root data structure representing the specification.
6//! * [Output] represents a single output stream.  The data structure is enriched with information regarding streams accessing it or accessed by it and much more.  For input streams confer [Input].
7//! * [StreamReference] used for referencing streams within the Mir.
8//! * [Expression] represents an expression.  It contains its [ExpressionKind] and its type.  The latter contains all information specific to a certain kind of expression such as sub-expressions of operators.
9//!
10//! # See Also
11//! * [rtlola_frontend](https://docs.rs/rtlola_frontend) for an overview regarding different representations.
12//! * [from_ast](crate::from_ast) / [fully_analyzed](crate::fully_analyzed) to obtain an [RtLolaHir] for a specification in form of an Ast.
13//! * [RtLolaHir] for a data structs designed for working _on_it.
14//! * [RtLolaAst](rtlola_parser::RtLolaAst), which is the most basic and down-to-syntax data structure available for RTLola.
15
16mod expression;
17mod feature_selector;
18mod print;
19pub mod selector;
20
21use std::collections::HashMap;
22use std::time::Duration;
23
24pub use feature_selector::{Feature, FeatureSelector};
25use rtlola_reporting::Span;
26use serde::{Deserialize, Serialize};
27use uom::si::rational64::Frequency as UOM_Frequency;
28
29pub use crate::hir::expression::*;
30pub use crate::modes::ast_conversion::TransformationErr;
31pub use crate::modes::dependencies::{DependencyErr, DependencyGraph, EdgeWeight, Origin};
32pub use crate::modes::memory_bounds::MemorizationBound;
33pub use crate::modes::ordering::{Layer, StreamLayers};
34use crate::modes::HirMode;
35pub use crate::modes::{
36    BaseMode, CompleteMode, DepAnaMode, DepAnaTrait, HirStage, MemBoundMode, MemBoundTrait, OrderedMode, OrderedTrait,
37    TypedMode, TypedTrait,
38};
39use crate::stdlib::FuncDecl;
40pub use crate::type_check::{
41    ActivationCondition, ConcretePacingType, ConcreteStreamPacing, ConcreteValueType, StreamType,
42};
43
44/// This struct constitutes the Mid-Level Intermediate Representation (MIR) of an RTLola specification.
45///
46/// The [RtLolaHir] is specifically designed to allow for convenient manipulation and analysis.  Hence, it is perfect for working *on* the specification rather than work *with* it.  
47///
48/// # Most Notable Structs and Enums
49/// * [RtLolaMir](https://docs.rs/rtlola_frontend/struct.RtLolaMir.html) is the root data structure representing the specification.
50/// * [Output] represents a single output stream.  The data structure is enriched with information regarding streams accessing it or accessed by it and much more.  For input streams confer [Input].
51/// * [StreamReference] used for referencing streams within the Mir.
52/// * [Expression] represents an expression.  It contains its [ExpressionKind] and its type.  The latter contains all information specific to a certain kind of expression such as sub-expressions of operators.
53///
54/// # Type-State
55/// The Hir follows a type-state pattern.  To this end, it has a type parameter, its HirMode.  The Hir starts in the [BaseMode] and progresses through different stages until reaching [CompleteMode].  
56/// Each stage constitutes another level of refinement and adds functionality.  The functionality can be accesses by importing the respective trait and requiring the mode of the Hir to implement the trait.
57/// The following traits exist.
58/// * [DepAnaTrait] provides access to a dependency graph (see [petgraph](petgraph::stable_graph::StableGraph)) and functions to access immediate neighbors of streams. Obtained via [determine_evaluation_order](RtLolaHir::<TypeMode>::determine_evaluation_order).
59/// * [TypedTrait] provides type information. Obtained via [check_types](crate::hir::RtLolaHir::<DepAnaMode>::check_types).
60/// * [OrderedTrait] provides information regarding the evaluation order of streams. Obtained via [determine_evaluation_order](crate::hir::RtLolaHir::<TypedMode>::determine_evaluation_order).
61/// * [MemBoundTrait] provides information on how many values of a stream have to be kept in memory at the same time. Obtained via [determine_memory_bounds](crate::hir::RtLolaHir::<OrderedMode>::determine_memory_bounds).
62///
63/// Progression through different stages is managed by the [HirStage] trait, in particular [HirStage::progress].
64///
65/// # See Also
66/// * [rtlola_frontend](https://docs.rs/rtlola_frontend) for an overview regarding different representations.
67/// * [from_ast](crate::from_ast) / [fully_analyzed](crate::fully_analyzed) to obtain an [RtLolaHir] for a specification in form of an Ast.
68/// * [RtLolaHir] for a data structs designed for working _on_it.
69/// * [RtLolaAst](rtlola_parser::RtLolaAst), which is the most basic and down-to-syntax data structure available for RTLola.
70#[derive(Debug, Clone)]
71pub struct RtLolaHir<M: HirMode> {
72    /// Collection of input streams
73    pub(crate) inputs: Vec<Input>,
74    /// Collection of output streams
75    pub(crate) outputs: Vec<Output>,
76    /// Next free input reference used to create new input streams
77    pub(crate) next_input_ref: usize,
78    /// Next free output reference used to create new output streams
79    pub(crate) next_output_ref: usize,
80    /// Maps expression ids to their expressions.
81    pub(crate) expr_maps: ExpressionMaps,
82    /// The current mode
83    pub(crate) mode: M,
84}
85
86pub(crate) type Hir<M> = RtLolaHir<M>;
87
88impl<M: HirMode> Hir<M> {
89    /// Provides access to an iterator over all input streams.
90    pub fn inputs(&self) -> impl Iterator<Item = &Input> {
91        self.inputs.iter()
92    }
93
94    /// Provides access to an iterator over all output streams.
95    pub fn outputs(&self) -> impl Iterator<Item = &Output> {
96        self.outputs.iter()
97    }
98
99    /// Provides access to an iterator over all triggers.
100    pub fn triggers(&self) -> impl Iterator<Item = &Output> {
101        self.outputs()
102            .filter(|output| matches!(output.kind, OutputKind::Trigger(_)))
103    }
104
105    /// Yields the number of input streams present in the Hir. Not necessarily equal to the number of input streams in the specification.
106    pub fn num_inputs(&self) -> usize {
107        self.inputs.len()
108    }
109
110    /// Yields the number of output streams present in the Hir.  Not necessarily equal to the number of output streams in the specification.
111    pub fn num_outputs(&self) -> usize {
112        self.outputs.len()
113    }
114
115    /// Yields the number of triggers present in the Hir.  Not necessarily equal to the number of triggers in the specification.
116    pub fn num_triggers(&self) -> usize {
117        self.triggers().count()
118    }
119
120    /// Provides access to an iterator over all streams, i.e., inputs, outputs, and triggers.
121    pub fn all_streams(&'_ self) -> impl Iterator<Item = SRef> + '_ {
122        self.inputs
123            .iter()
124            .map(|i| i.sr)
125            .chain(self.outputs.iter().map(|o| o.sr))
126    }
127
128    /// Retrieves an input stream based on its name.  Fails if no such input stream exists.
129    pub fn get_input_with_name(&self, name: &str) -> Option<&Input> {
130        self.inputs.iter().find(|&i| i.name == name)
131    }
132
133    /// Retrieves an output stream based on its name.  Fails if no such output stream exists.
134    pub fn get_output_with_name(&self, name: &str) -> Option<&Output> {
135        self.outputs.iter().find(|&o| o.name() == name)
136    }
137
138    /// Retrieves an output stream based on a stream reference.  Fails if no such stream exists or `sref` is a [StreamReference::In].
139    pub fn output(&self, sref: SRef) -> Option<&Output> {
140        self.outputs().find(|o| o.sr == sref)
141    }
142
143    /// Retrieves an input stream based on a stream reference.  Fails if no such stream exists or `sref` is a [StreamReference::Out].
144    pub fn input(&self, sref: SRef) -> Option<&Input> {
145        self.inputs().find(|i| i.sr == sref)
146    }
147
148    /// Provides access to a collection of references for all windows occurring in the Hir.
149    pub fn window_refs(&self) -> Vec<WRef> {
150        self.expr_maps
151            .sliding_windows
152            .keys()
153            .chain(self.expr_maps.discrete_windows.keys())
154            .cloned()
155            .collect()
156    }
157
158    /// Provides access to a collection of references for all sliding windows occurring in the Hir.
159    pub fn sliding_windows(&self) -> Vec<&Window<SlidingAggr>> {
160        self.expr_maps.sliding_windows.values().clone().collect()
161    }
162
163    /// Provides access to a collection of references for all discrete windows occurring in the Hir.
164    pub fn discrete_windows(&self) -> Vec<&Window<DiscreteAggr>> {
165        self.expr_maps.discrete_windows.values().clone().collect()
166    }
167
168    /// Provides access to a collection of references for all discrete windows occurring in the Hir.
169    pub fn instance_aggregations(&self) -> Vec<&InstanceAggregation> {
170        self.expr_maps.instance_aggregations.values().clone().collect()
171    }
172
173    /// Retrieves an expression for a given expression id.
174    ///
175    /// # Panic
176    /// Panics if the expression does not exist.
177    pub fn expression(&self, id: ExprId) -> &Expression {
178        &self.expr_maps.exprid_to_expr[&id]
179    }
180
181    /// Retrieves a function declaration for a given function name.
182    ///
183    /// # Panic
184    /// Panics if the declaration does not exist.
185    pub fn func_declaration(&self, func_name: &str) -> &FuncDecl {
186        &self.expr_maps.func_table[func_name]
187    }
188
189    /// Retrieves a single sliding window for a given reference.  
190    ///
191    /// # Panic
192    /// Panics if no such window exists.
193    pub fn single_sliding(&self, window: WRef) -> Window<SlidingAggr> {
194        *self
195            .sliding_windows()
196            .into_iter()
197            .find(|w| w.reference == window)
198            .unwrap()
199    }
200
201    /// Retrieves a single discrete window for a given reference.  
202    ///
203    /// # Panic
204    /// Panics if no such window exists.
205    pub fn single_discrete(&self, window: WRef) -> Window<DiscreteAggr> {
206        *self
207            .discrete_windows()
208            .into_iter()
209            .find(|w| w.reference == window)
210            .unwrap()
211    }
212
213    /// Retrieves a single instance aggregation for a given reference.
214    ///
215    /// # Panic
216    /// Panics if no such aggregation exists.
217    pub fn single_instance_aggregation(&self, window: WRef) -> InstanceAggregation {
218        *self
219            .instance_aggregations()
220            .into_iter()
221            .find(|w| w.reference == window)
222            .unwrap()
223    }
224
225    /// Retrieves the spawn definition of a particular output stream or trigger or `None` for input references.
226    pub fn spawn(&self, sr: SRef) -> Option<SpawnDef> {
227        match sr {
228            SRef::In(_) => None,
229            SRef::Out(_) => {
230                let output = self.outputs.iter().find(|o| o.sr == sr);
231                output.and_then(|o| o.spawn()).map(|st| {
232                    SpawnDef::new(
233                        st.expression.map(|e| self.expression(e)),
234                        st.condition.map(|e| self.expression(e)),
235                        &st.pacing,
236                        st.span,
237                    )
238                })
239            },
240        }
241    }
242
243    /// Retrieves the spawn condition of a particular output stream or `None` for input and trigger references.
244    /// If all parts of the [SpawnDef] are needed, see [RtLolaHir::spawn]
245    pub fn spawn_cond(&self, sr: SRef) -> Option<&Expression> {
246        match sr {
247            SRef::In(_) => None,
248            SRef::Out(_) => {
249                self.outputs
250                    .iter()
251                    .find(|o| o.sr == sr)
252                    .and_then(|o| o.spawn_cond())
253                    .map(|eid| self.expression(eid))
254            },
255        }
256    }
257
258    /// Retrieves the spawn expresion of a particular output stream or `None` for input and trigger references.
259    /// If all parts of the [SpawnDef] are needed, see [RtLolaHir::spawn]
260    pub fn spawn_expr(&self, sr: SRef) -> Option<&Expression> {
261        match sr {
262            SRef::In(_) => None,
263            SRef::Out(_) => {
264                self.outputs
265                    .iter()
266                    .find(|o| o.sr == sr)
267                    .and_then(|o| o.spawn_expr())
268                    .map(|eid| self.expression(eid))
269            },
270        }
271    }
272
273    /// Retrieves the spawn pacing of a particular output stream or `None` for input and trigger references.
274    /// If all parts of the [SpawnDef] are needed, see [RtLolaHir::spawn]
275    pub fn spawn_pacing(&self, sr: SRef) -> Option<&AnnotatedPacingType> {
276        match sr {
277            SRef::In(_) => None,
278            SRef::Out(_) => self.outputs.iter().find(|o| o.sr == sr).and_then(|o| o.spawn_pacing()),
279        }
280    }
281
282    /// Same behavior as [spawn].
283    /// # Panic
284    /// Panics if the stream does not exist or is an input/trigger.
285    #[cfg(test)]
286    pub(crate) fn spawn_unchecked(&self, sr: SRef) -> SpawnDef {
287        self.spawn(sr).expect("Invalid for input and triggers references")
288    }
289
290    /// Retrieves the eval definitions of a particular output stream or trigger or `None` for input references.
291    pub fn eval(&self, sr: SRef) -> Option<Vec<EvalDef>> {
292        match sr {
293            SRef::In(_) => None,
294            SRef::Out(_) => {
295                let output = self.outputs.iter().find(|o| o.sr == sr);
296                output.map(|o| {
297                    o.eval()
298                        .iter()
299                        .map(|eval| {
300                            EvalDef::new(
301                                eval.condition.map(|id| self.expression(id)),
302                                self.expression(eval.expr),
303                                &eval.annotated_pacing_type,
304                                eval.span,
305                            )
306                        })
307                        .collect()
308                })
309            },
310        }
311    }
312
313    /// Retrieves all eval conditions of the clauses of a particular output stream or `None` for input and trigger references.
314    /// For each eval clause of the stream, the element in the Vec is `None` if no condition is
315    /// or the coresponding condition otherwise.
316    /// If all parts of the [EvalDef] are needed, see [RtLolaHir::eval]
317    pub fn eval_cond(&self, sr: SRef) -> Option<Vec<Option<&Expression>>> {
318        match sr {
319            SRef::In(_) => None,
320            SRef::Out(o) => {
321                if o < self.outputs.len() {
322                    self.outputs.iter().find(|o| o.sr == sr).map(|output| {
323                        output
324                            .eval
325                            .iter()
326                            .map(|e| e.condition.map(|eid| self.expression(eid)))
327                            .collect()
328                    })
329                } else {
330                    Some(vec![None])
331                }
332            },
333        }
334    }
335
336    /// Retrieves the eval expressions of all eval clauses of a particular output stream or trigger and `None` for input references.
337    /// If all parts of the [EvalDef] are needed, see [RtLolaHir::eval]
338    pub fn eval_expr(&self, sr: SRef) -> Option<Vec<&Expression>> {
339        match sr {
340            SRef::In(_) => None,
341            SRef::Out(_) => {
342                self.outputs
343                    .iter()
344                    .find(|o| o.sr == sr)
345                    .map(|output| output.eval.iter().map(|eval| self.expression(eval.expr)).collect())
346            },
347        }
348    }
349
350    /// Retrieves the annotated eval pacing of each eval clause of a particular output stream or trigger `None` for input references.
351    /// If all parts of the [EvalDef] are needed, see [RtLolaHir::eval]
352    pub fn eval_pacing(&self, sr: SRef) -> Option<Vec<&AnnotatedPacingType>> {
353        match sr {
354            SRef::In(_) => None,
355            SRef::Out(_) => {
356                let output = self.outputs.iter().find(|o| o.sr == sr)?;
357                Some(output.eval.iter().map(|eval| &eval.annotated_pacing_type).collect())
358            },
359        }
360    }
361
362    /// Same behavior as [`eval`](fn@Hir).
363    /// # Panic
364    /// Panics if the stream does not exist or is an input.
365    pub(crate) fn eval_unchecked(&self, sr: StreamReference) -> Vec<EvalDef> {
366        self.eval(sr).expect("Invalid for input references")
367    }
368
369    /// Retrieves the expressions representing the close definition of a particular output stream or `None` for input and trigger references.
370    pub fn close(&self, sr: SRef) -> Option<CloseDef> {
371        match sr {
372            SRef::In(_) => None,
373            SRef::Out(_) => {
374                let ct = self.outputs.iter().find(|o| o.sr == sr).and_then(|o| o.close());
375                ct.map(|ct| CloseDef::new(Some(self.expression(ct.condition)), &ct.pacing, ct.span))
376            },
377        }
378    }
379
380    /// Retrieves the expression representing the close condition of a particular output stream or `None` for input and trigger references.
381    /// If all parts of the [CloseDef] are needed, see [RtLolaHir::close]
382    pub fn close_cond(&self, sr: SRef) -> Option<&Expression> {
383        match sr {
384            SRef::In(_) => None,
385            SRef::Out(_) => {
386                self.outputs
387                    .iter()
388                    .find(|o| o.sr == sr)
389                    .and_then(|o| o.close_cond())
390                    .map(|eid| self.expression(eid))
391            },
392        }
393    }
394
395    /// Retrieves the close pacing of a particular output stream or `None` for input and trigger references.
396    /// If all parts of the [CloseDef] are needed, see [RtLolaHir::close]
397    pub fn close_pacing(&self, sr: SRef) -> Option<&AnnotatedPacingType> {
398        match sr {
399            SRef::In(_) => None,
400            SRef::Out(_) => self.outputs.iter().find(|o| o.sr == sr).and_then(|o| o.close_pacing()),
401        }
402    }
403
404    /// Same behavior as [`close`](fn@Hir).
405    /// # Panic
406    /// Panics if the stream does not exist or is an input/trigger.
407    #[cfg(test)]
408    pub(crate) fn close_unchecked(&self, sr: StreamReference) -> CloseDef {
409        self.close(sr).expect("Invalid for input and triggers references")
410    }
411
412    /// Generates a map from a [StreamReference] to the name of the corresponding stream.
413    pub fn names(&self) -> HashMap<SRef, String> {
414        self.inputs()
415            .map(|i| (i.sr, i.name.clone()))
416            .chain(self.outputs().map(|o| (o.sr, o.name())))
417            .collect()
418    }
419}
420
421/// A collection of maps for expression-related lookups, i.e., expressions, functions, and windows.
422#[derive(Clone, Debug)]
423pub(crate) struct ExpressionMaps {
424    exprid_to_expr: HashMap<ExprId, Expression>,
425    sliding_windows: HashMap<WRef, Window<SlidingAggr>>,
426    discrete_windows: HashMap<WRef, Window<DiscreteAggr>>,
427    instance_aggregations: HashMap<WRef, InstanceAggregation>,
428    func_table: HashMap<String, FuncDecl>,
429}
430
431impl ExpressionMaps {
432    /// Creates a new expression map.
433    pub(crate) fn new(
434        exprid_to_expr: HashMap<ExprId, Expression>,
435        sliding_windows: HashMap<WRef, Window<SlidingAggr>>,
436        discrete_windows: HashMap<WRef, Window<DiscreteAggr>>,
437        instance_aggregations: HashMap<WRef, InstanceAggregation>,
438        func_table: HashMap<String, FuncDecl>,
439    ) -> Self {
440        Self {
441            exprid_to_expr,
442            sliding_windows,
443            discrete_windows,
444            instance_aggregations,
445            func_table,
446        }
447    }
448}
449
450/// Represents the name of a function including its arguments.
451#[derive(Debug, Clone)]
452pub enum FunctionName {
453    /// the function has a fixed number of (possibly named) arguments
454    FixedParameters {
455        /// The name of the function
456        name: String,
457        /// For each argument its name (or None if it does not have a name)
458        arg_names: Vec<Option<String>>,
459    },
460    /// The function has an arbitrary amount of (unnamed) arguments
461    ArbitraryParameters {
462        /// The name of the function
463        name: String,
464    },
465}
466
467impl FunctionName {
468    /// Creates a new FunctionName with a predefined number of arguments.
469    pub(crate) fn new(name: String, arg_names: &[Option<String>]) -> Self {
470        Self::FixedParameters {
471            name,
472            arg_names: Vec::from(arg_names),
473        }
474    }
475
476    pub(crate) fn new_repeating(name: String) -> Self {
477        Self::ArbitraryParameters { name }
478    }
479
480    pub(crate) fn name(&self) -> &str {
481        match self {
482            FunctionName::FixedParameters { name, .. } => name,
483            FunctionName::ArbitraryParameters { name } => name,
484        }
485    }
486}
487
488impl PartialEq for FunctionName {
489    fn eq(&self, other: &Self) -> bool {
490        match (self, other) {
491            (Self::ArbitraryParameters { name }, other) | (other, Self::ArbitraryParameters { name }) => {
492                name == other.name()
493            },
494            (
495                Self::FixedParameters {
496                    name: s_name,
497                    arg_names: s_arg_names,
498                },
499                Self::FixedParameters {
500                    name: o_name,
501                    arg_names: o_arg_names,
502                },
503            ) => s_name == o_name && s_arg_names == o_arg_names,
504        }
505    }
506}
507
508impl std::hash::Hash for FunctionName {
509    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
510        match self {
511            FunctionName::FixedParameters { name, arg_names: _ } => name,
512            FunctionName::ArbitraryParameters { name } => name,
513        }
514        .hash(state)
515    }
516}
517
518impl Eq for FunctionName {}
519
520/// Represents an input stream in an RTLola specification.
521#[derive(Debug, Clone)]
522pub struct Input {
523    /// The name of the stream.
524    pub name: String,
525    /// The reference pointing to this stream.
526    pub(crate) sr: SRef,
527    /// The user annotated Type
528    pub(crate) annotated_type: AnnotatedType,
529    /// The code span the input represents
530    pub(crate) span: Span,
531}
532
533impl Input {
534    /// Yields the reference referring to this input stream.
535    pub fn sr(&self) -> StreamReference {
536        self.sr
537    }
538
539    /// Yields the span referring to a part of the specification from which this stream originated.
540    pub fn span(&self) -> Span {
541        self.span
542    }
543}
544
545/// Whether the given output stream is a regular named output or represents a trigger
546#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
547pub enum OutputKind {
548    /// The output is a regular named output stream
549    NamedOutput(String),
550    /// The output represents a trigger
551    Trigger(usize),
552}
553
554/// Represents an output stream in an RTLola specification.
555#[derive(Debug, Clone)]
556pub struct Output {
557    /// The kind of the stream.
558    pub kind: OutputKind,
559    /// The user annotated Type
560    pub(crate) annotated_type: Option<AnnotatedType>,
561    /// The parameters of a parameterized output stream; The vector is empty in non-parametrized streams
562    pub(crate) params: Vec<Parameter>,
563    /// The optional information on the spawning behavior of the stream
564    pub(crate) spawn: Option<Spawn>,
565    /// The information regarding evaluation expression and condition of the stream
566    pub(crate) eval: Vec<Eval>,
567    /// The optional closing condition
568    pub(crate) close: Option<Close>,
569    /// The reference pointing to this stream.
570    pub(crate) sr: SRef,
571    /// The code span the output represents
572    pub(crate) span: Span,
573}
574
575impl Output {
576    /// Returns the name of this stream.
577    pub fn name(&self) -> String {
578        match &self.kind {
579            OutputKind::NamedOutput(s) => s.clone(),
580            OutputKind::Trigger(idx) => format!("trigger_{idx}"),
581        }
582    }
583
584    /// Returns an iterator over the parameters of this stream.
585    pub fn params(&self) -> impl Iterator<Item = &Parameter> {
586        self.params.iter()
587    }
588
589    /// Yields the reference referring to this input stream.
590    pub fn sr(&self) -> StreamReference {
591        self.sr
592    }
593
594    /// Returns the [Spawn] template of the stream
595    pub(crate) fn spawn(&self) -> Option<&Spawn> {
596        self.spawn.as_ref()
597    }
598
599    /// Returns the expression id for the spawn condition of this stream
600    /// If all parts of [Spawn] are required, see [spawn](fn@Hir)
601    pub(crate) fn spawn_cond(&self) -> Option<ExprId> {
602        self.spawn.as_ref().and_then(|st| st.condition)
603    }
604
605    /// Returns the expression id for the spawn expression of this stream
606    /// If all parts of [Spawn] are required, see [spawn](fn@Hir)
607    pub(crate) fn spawn_expr(&self) -> Option<ExprId> {
608        self.spawn.as_ref().and_then(|st| st.expression)
609    }
610
611    /// Returns the pacing for the spawn condition of this stream
612    /// If all parts of [Spawn] are required, see [spawn](fn@Hir)
613    #[allow(dead_code)]
614    pub(crate) fn spawn_pacing(&self) -> Option<&AnnotatedPacingType> {
615        self.spawn.as_ref().map(|st| &st.pacing)
616    }
617
618    /// Returns the [Close] template of the stream
619    pub(crate) fn close(&self) -> Option<&Close> {
620        self.close.as_ref()
621    }
622
623    /// Returns the expression id for the close condition of this stream
624    /// If all parts of [Close] are required, see [close](fn@Hir)
625    pub(crate) fn close_cond(&self) -> Option<ExprId> {
626        self.close.as_ref().map(|ct| ct.condition)
627    }
628
629    /// Returns the pacing for the close condition of this stream
630    /// If all parts of [Close] are required, see [close](fn@Hir))
631    #[allow(dead_code)]
632    pub(crate) fn close_pacing(&self) -> Option<&AnnotatedPacingType> {
633        self.close.as_ref().map(|ct| &ct.pacing)
634    }
635
636    /// Returns the [Eval] template of the stream
637    pub(crate) fn eval(&self) -> &[Eval] {
638        &self.eval
639    }
640
641    /// Yields the span referring to a part of the specification from which this stream originated.
642    pub fn span(&self) -> Span {
643        self.span
644    }
645}
646
647/// Represents a single parameter of a parametrized output stream.
648#[derive(Debug, PartialEq, Clone, Eq)]
649pub struct Parameter {
650    /// The name of this parameter
651    pub name: String,
652    /// The annotated type of this parameter
653    pub(crate) annotated_type: Option<AnnotatedType>,
654    /// The index of this parameter
655    pub(crate) idx: usize,
656    /// The code span of the parameter
657    pub(crate) span: Span,
658}
659
660impl Parameter {
661    /// Yields the index of this parameter.  If the index is 3, then the parameter is the fourth parameter of the respective stream.
662    pub fn index(&self) -> usize {
663        self.idx
664    }
665
666    /// Yields the span referring to a part of the specification where this parameter occurs.
667    pub fn span(&self) -> Span {
668        self.span
669    }
670}
671
672#[derive(Debug, Clone, Copy, PartialEq, Eq)]
673/// Frequency of an annotated pacing information for stream
674pub struct AnnotatedFrequency {
675    /// A span to the part of the specification containing the frequency
676    pub span: Span,
677    /// The actual frequency
678    pub value: UOM_Frequency,
679}
680
681/// Pacing information for stream; contains either a frequency or a condition on input streams.
682#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
683pub enum AnnotatedPacingType {
684    /// The global evaluation frequency
685    GlobalFrequency(AnnotatedFrequency),
686    /// The local evaluation frequency
687    LocalFrequency(AnnotatedFrequency),
688    /// The expression which constitutes the condition under which the stream should be evaluated.
689    Event(ExprId),
690    /// The stream is not annotated with a pacing
691    #[default]
692    NotAnnotated,
693}
694
695impl AnnotatedPacingType {
696    /// Returns the span of the annotated type.
697    pub fn span<M: HirMode>(&self, hir: &Hir<M>) -> Span {
698        match self {
699            AnnotatedPacingType::GlobalFrequency(freq) | AnnotatedPacingType::LocalFrequency(freq) => freq.span,
700            AnnotatedPacingType::Event(id) => hir.expression(*id).span,
701            AnnotatedPacingType::NotAnnotated => Span::Unknown,
702        }
703    }
704}
705
706/// Information regarding the spawning behavior of a stream
707#[derive(Debug, Clone, Default)]
708pub(crate) struct Spawn {
709    /// The expression defining the parameter instances. If the stream has more than one parameter, the expression needs to return a tuple, with one element for each parameter
710    pub(crate) expression: Option<ExprId>,
711    /// The activation condition describing when a new instance is created.
712    pub(crate) pacing: AnnotatedPacingType,
713    /// An additional condition for the creation of an instance, i.e., an instance is only created if the condition is true.
714    pub(crate) condition: Option<ExprId>,
715    /// The range in the specification corresponding to the spawn clause.
716    pub(crate) span: Span,
717}
718
719impl Spawn {
720    /// Returns a reference to the `Expression` representing the spawn expression if it exists
721    pub(crate) fn spawn_expr<'a, M: HirMode>(&self, hir: &'a RtLolaHir<M>) -> Option<&'a Expression> {
722        self.expression.map(|eid| hir.expression(eid))
723    }
724
725    /// Returns a vector of `Expression` references representing the expressions with which the parameters of the stream are initialized
726    pub(crate) fn spawn_args<'a, M: HirMode>(&self, hir: &'a RtLolaHir<M>) -> Vec<&'a Expression> {
727        self.spawn_expr(hir)
728            .map(|se| {
729                match &se.kind {
730                    ExpressionKind::Tuple(spawns) => spawns.iter().collect(),
731                    _ => vec![se],
732                }
733            })
734            .unwrap_or_default()
735    }
736
737    /// Returns a reference to the `Expression` representing the spawn condition if it exists
738    pub(crate) fn spawn_cond<'a, M: HirMode>(&self, hir: &'a RtLolaHir<M>) -> Option<&'a Expression> {
739        self.condition.map(|eid| hir.expression(eid))
740    }
741}
742
743/// Information regarding the evaluation condition and evaluation behavior of a stream
744#[derive(Debug, Clone)]
745pub(crate) struct Eval {
746    /// The activation condition, which defines when a new value of a stream is computed.
747    pub(crate) annotated_pacing_type: AnnotatedPacingType,
748    /// The expression defining when an instance is evaluated
749    pub(crate) condition: Option<ExprId>,
750    /// The stream expression of a output stream, e.g., a + b.offset(by: -1).defaults(to: 0)
751    pub(crate) expr: ExprId,
752    /// The range in the specification corresponding to the eval clause.
753    pub(crate) span: Span,
754}
755
756/// Information regarding the closing behavior of a stream
757#[derive(Debug, Clone)]
758pub(crate) struct Close {
759    /// The expression defining if an instance is closed
760    pub(crate) condition: ExprId,
761    /// The activation condition describing when an instance is closed
762    pub(crate) pacing: AnnotatedPacingType,
763    /// The range in the specification corresponding to the close clause.
764    pub(crate) span: Span,
765}
766
767/// The Hir Spawn definition is composed of two optional expressions and the annotated pacing.
768/// The first one refers to the spawn expression while the second one represents the spawn condition.
769#[derive(Debug, Clone, Copy)]
770pub struct SpawnDef<'a> {
771    /// The expression of the stream is spawned with, setting the parameters, e.g. spawn with (3,x)
772    pub expression: Option<&'a Expression>,
773    /// The conditional expression of the spawn, e.g. when x > 5
774    pub condition: Option<&'a Expression>,
775    /// The pacing type  of the spawn, e.g. @1Hz or @input_i
776    pub annotated_pacing: &'a AnnotatedPacingType,
777    /// The range in the specification corresponding to the spawn clause.
778    pub span: Span,
779}
780
781impl<'a> SpawnDef<'a> {
782    /// Constructs a new [SpawnDef]
783    pub fn new(
784        expression: Option<&'a Expression>,
785        condition: Option<&'a Expression>,
786        annotated_pacing: &'a AnnotatedPacingType,
787        span: Span,
788    ) -> Self {
789        Self {
790            expression,
791            condition,
792            annotated_pacing,
793            span,
794        }
795    }
796}
797
798/// The Hir Eval definition is composed of three expressions and the annotated pacing.
799/// The first one refers to the evaluation condition, while the second one represents the evaluation expression, defining the value of the stream.
800#[derive(Debug, Clone, Copy)]
801pub struct EvalDef<'a> {
802    /// The evaluation condition has to evaluated to true in order for the stream expression to be evaluated.
803    pub condition: Option<&'a Expression>,
804    /// The stream expression defines the computed value of the stream.
805    pub expression: &'a Expression,
806    /// The annotated pacing of the stream evaluation, describing when the condition and expression should be evaluated in a temporal manner.
807    pub annotated_pacing: &'a AnnotatedPacingType,
808    /// The range in the specification corresponding to the eval clause.
809    pub span: Span,
810}
811
812impl<'a> EvalDef<'a> {
813    /// Constructs a new [EvalDef]
814    pub fn new(
815        condition: Option<&'a Expression>,
816        expr: &'a Expression,
817        annotated_pacing: &'a AnnotatedPacingType,
818        span: Span,
819    ) -> Self {
820        Self {
821            condition,
822            expression: expr,
823            annotated_pacing,
824            span,
825        }
826    }
827}
828
829/// The Hir Close definition is composed of the Close condition expression and the annotated pacing.
830#[derive(Debug, Clone, Copy)]
831pub struct CloseDef<'a> {
832    /// The close condition, defining when a stream instance is closed and no longer evaluated.
833    pub condition: Option<&'a Expression>,
834    /// The annotated pacing, indicating when the condition should be evaluated.
835    pub annotated_pacing: &'a AnnotatedPacingType,
836    /// The range in the specification corresponding to the close clause.
837    pub span: Span,
838}
839
840impl<'a> CloseDef<'a> {
841    /// Constructs a new [CloseDef]
842    pub fn new(condition: Option<&'a Expression>, annotated_pacing: &'a AnnotatedPacingType, span: Span) -> Self {
843        Self {
844            condition,
845            annotated_pacing,
846            span,
847        }
848    }
849}
850
851/// Represents the annotated given type for constants, input streams, etc.
852/// It is converted from the AST type and an input for the type checker.
853/// After typechecking HirType is used to represent all type information.
854#[derive(Debug, PartialEq, Eq, Clone, Hash)]
855pub(crate) enum AnnotatedType {
856    Int(u32),
857    Float(u32),
858    UInt(u32),
859    Bool,
860    String,
861    Bytes,
862    Option(Box<AnnotatedType>),
863    Tuple(Vec<AnnotatedType>),
864    Numeric,
865    Signed,
866    Sequence,
867    Param(usize, String),
868    Any,
869}
870
871impl AnnotatedType {
872    /// Yields a collection of primitive types and their names.
873    pub(crate) fn primitive_types() -> Vec<(&'static str, &'static AnnotatedType)> {
874        let mut types = vec![];
875        types.extend_from_slice(&crate::stdlib::PRIMITIVE_TYPES);
876        types.extend_from_slice(&crate::stdlib::PRIMITIVE_TYPES_ALIASES);
877
878        types
879    }
880}
881
882/// Allows for referencing a window instance.
883#[derive(Hash, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
884pub enum WindowReference {
885    /// Refers to a sliding window
886    Sliding(usize),
887    /// Refers to a discrete window
888    Discrete(usize),
889    /// Refers to a instance aggregation
890    Instance(usize),
891}
892
893pub(crate) type WRef = WindowReference;
894
895impl WindowReference {
896    /// Provides access to the index inside the reference.
897    pub fn idx(self) -> usize {
898        match self {
899            WindowReference::Sliding(u) => u,
900            WindowReference::Discrete(u) => u,
901            WindowReference::Instance(u) => u,
902        }
903    }
904}
905
906/// Allows for referencing an input stream within the specification.
907pub type InputReference = usize;
908/// Allows for referencing an output stream within the specification.
909pub type OutputReference = usize;
910
911/// Allows for referencing a stream within the specification.
912#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
913pub enum StreamReference {
914    /// References an input stream.
915    In(InputReference),
916    /// References an output stream.
917    Out(OutputReference),
918}
919
920pub(crate) type SRef = StreamReference;
921
922impl StreamReference {
923    /// Returns the index inside the reference if it is an output reference.  Panics otherwise.
924    pub fn out_ix(&self) -> usize {
925        match self {
926            StreamReference::In(_) => unreachable!(),
927            StreamReference::Out(ix) => *ix,
928        }
929    }
930
931    /// Returns the index inside the reference if it is an input reference.  Panics otherwise.
932    pub fn in_ix(&self) -> usize {
933        match self {
934            StreamReference::Out(_) => unreachable!(),
935            StreamReference::In(ix) => *ix,
936        }
937    }
938
939    /// Returns the index inside the reference disregarding whether it is an input or output reference.
940    pub fn ix_unchecked(&self) -> usize {
941        match self {
942            StreamReference::In(ix) | StreamReference::Out(ix) => *ix,
943        }
944    }
945
946    /// True if the reference is an instance of [StreamReference::In], false otherwise.
947    pub fn is_input(&self) -> bool {
948        match self {
949            StreamReference::Out(_) => false,
950            StreamReference::In(_) => true,
951        }
952    }
953
954    /// True if the reference is an instance of [StreamReference::Out], false otherwise.
955    pub fn is_output(&self) -> bool {
956        match self {
957            StreamReference::Out(_) => true,
958            StreamReference::In(_) => false,
959        }
960    }
961}
962
963impl PartialOrd for StreamReference {
964    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
965        use std::cmp::Ordering;
966        match (self, other) {
967            (StreamReference::In(i), StreamReference::In(i2)) => Some(i.cmp(i2)),
968            (StreamReference::Out(o), StreamReference::Out(o2)) => Some(o.cmp(o2)),
969            (StreamReference::In(_), StreamReference::Out(_)) => Some(Ordering::Less),
970            (StreamReference::Out(_), StreamReference::In(_)) => Some(Ordering::Greater),
971        }
972    }
973}
974
975impl Ord for StreamReference {
976    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
977        use std::cmp::Ordering;
978        match (self, other) {
979            (StreamReference::In(i), StreamReference::In(i2)) => i.cmp(i2),
980            (StreamReference::Out(o), StreamReference::Out(o2)) => o.cmp(o2),
981            (StreamReference::In(_), StreamReference::Out(_)) => Ordering::Less,
982            (StreamReference::Out(_), StreamReference::In(_)) => Ordering::Greater,
983        }
984    }
985}
986
987/// Offset used in the lookup expression
988#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
989pub enum Offset {
990    /// A strictly positive discrete offset, e.g., `4`, or `42`
991    FutureDiscrete(u32),
992    /// A non-negative discrete offset, e.g., `0`, `-4`, or `-42`
993    PastDiscrete(u32),
994    /// A positive real-time offset, e.g., `-3ms`, `-4min`, `-2.3h`
995    FutureRealTime(Duration),
996    /// A non-negative real-time offset, e.g., `0`, `4min`, `2.3h`
997    PastRealTime(Duration),
998}
999
1000impl Offset {
1001    /// Returns `true`, iff the Offset is negative
1002    pub(crate) fn has_negative_offset(&self) -> bool {
1003        match self {
1004            Offset::FutureDiscrete(_) | Offset::FutureRealTime(_) => false,
1005            Offset::PastDiscrete(o) => *o != 0,
1006            Offset::PastRealTime(o) => o.as_nanos() != 0,
1007        }
1008    }
1009
1010    pub(crate) fn as_memory_bound(&self, dynamic: bool) -> MemorizationBound {
1011        match self {
1012            Offset::PastDiscrete(o) => MemorizationBound::Bounded(*o) + MemorizationBound::default_value(dynamic),
1013            Offset::FutureDiscrete(_) => unimplemented!(),
1014            Offset::FutureRealTime(_) => unimplemented!(),
1015            Offset::PastRealTime(_) => unimplemented!(),
1016        }
1017    }
1018}
1019
1020impl PartialOrd for Offset {
1021    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1022        use std::cmp::Ordering;
1023
1024        use Offset::*;
1025        match (self, other) {
1026            (PastDiscrete(_), FutureDiscrete(_))
1027            | (PastRealTime(_), FutureRealTime(_))
1028            | (PastDiscrete(_), FutureRealTime(_))
1029            | (PastRealTime(_), FutureDiscrete(_)) => Some(Ordering::Less),
1030
1031            (FutureDiscrete(_), PastDiscrete(_))
1032            | (FutureDiscrete(_), PastRealTime(_))
1033            | (FutureRealTime(_), PastDiscrete(_))
1034            | (FutureRealTime(_), PastRealTime(_)) => Some(Ordering::Greater),
1035
1036            (FutureDiscrete(a), FutureDiscrete(b)) => Some(a.cmp(b)),
1037            (PastDiscrete(a), PastDiscrete(b)) => Some(b.cmp(a)),
1038
1039            (_, _) => unimplemented!(),
1040        }
1041    }
1042}
1043
1044impl Ord for Offset {
1045    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1046        self.partial_cmp(other).unwrap()
1047    }
1048}