Struct rtlola_frontend::mir::RtLolaMir
source · pub struct RtLolaMir {
pub inputs: Vec<InputStream>,
pub outputs: Vec<OutputStream>,
pub time_driven: Vec<TimeDrivenStream>,
pub event_driven: Vec<EventDrivenStream>,
pub discrete_windows: Vec<DiscreteWindow>,
pub sliding_windows: Vec<SlidingWindow>,
pub triggers: Vec<Trigger>,
}Expand description
This struct constitutes the Mid-Level Intermediate Representation (MIR) of an RTLola specification.
The RtLolaMir is specifically designed to allow convenient navigation and access to data. Hence, it is perfect for working with the specification rather than work on it.
Most Notable Structs and Enums
- Stream is a trait offering several convenient access methods for everything constituting a stream.
- OutputStream 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 InputStream.
- StreamReference used for referencing streams within the Mir.
- Spawn and Close contain all information regarding the parametrization, spawning and closing behavior of streams.
- Eval contains the information regarding the evaluation condition and the expression of the stream. The Expression represents an computational evaluation. 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.
See Also
- rtlola_frontend for an overview regarding different representations.
- rtlola_frontend::parse to obtain an RtLolaMir for a specification in form of a string or path to a specification file.
- rtlola_hir::hir::RtLolaHir for a data structs designed for working _on_it.
- RtLolaAst, which is the most basic and down-to-syntax data structure available for RTLola.
Fields§
§inputs: Vec<InputStream>Contains all input streams.
outputs: Vec<OutputStream>Contains all output streams including all triggers. They only contain the information relevant for every single kind of output stream. Refer to RtLolaMir::time_driven, RtLolaMir::event_driven, and RtLolaMir::triggers for more information.
time_driven: Vec<TimeDrivenStream>References and pacing information of all time-driven streams.
event_driven: Vec<EventDrivenStream>References and pacing information of all event-driven streams.
discrete_windows: Vec<DiscreteWindow>A collection of all discrete windows.
sliding_windows: Vec<SlidingWindow>A collection of all sliding windows.
triggers: Vec<Trigger>References and message information of all triggers.
Implementations§
source§impl RtLolaMir
impl RtLolaMir
sourcepub fn from_hir(hir: RtLolaHir<CompleteMode>) -> RtLolaMir
pub fn from_hir(hir: RtLolaHir<CompleteMode>) -> RtLolaMir
Generates an Mir from a complete Hir.
source§impl RtLolaMir
impl RtLolaMir
sourcepub fn input_refs(&self) -> impl Iterator<Item = InputReference>
pub fn input_refs(&self) -> impl Iterator<Item = InputReference>
Returns a collection containing a reference to each input stream in the specification.
sourcepub fn output_refs(&self) -> impl Iterator<Item = OutputReference>
pub fn output_refs(&self) -> impl Iterator<Item = OutputReference>
Returns a collection containing a reference to each output stream in the specification.
sourcepub fn input_mut(&mut self, reference: StreamReference) -> &mut InputStream
pub fn input_mut(&mut self, reference: StreamReference) -> &mut InputStream
sourcepub fn input(&self, reference: StreamReference) -> &InputStream
pub fn input(&self, reference: StreamReference) -> &InputStream
sourcepub fn output_mut(&mut self, reference: StreamReference) -> &mut OutputStream
pub fn output_mut(&mut self, reference: StreamReference) -> &mut OutputStream
sourcepub fn output(&self, reference: StreamReference) -> &OutputStream
pub fn output(&self, reference: StreamReference) -> &OutputStream
Examples found in repository?
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
pub fn all_triggers(&self) -> Vec<&OutputStream> {
self.triggers.iter().map(|t| self.output(t.reference)).collect()
}
/// Provides a collection of all event-driven output streams.
pub fn all_event_driven(&self) -> Vec<&OutputStream> {
self.event_driven.iter().map(|t| self.output(t.reference)).collect()
}
/// Return true if the specification contains any time-driven features.
/// This includes time-driven streams and time-driven spawn conditions.
pub fn has_time_driven_features(&self) -> bool {
!self.time_driven.is_empty()
|| self
.outputs
.iter()
.any(|o| matches!(o.spawn.pacing, PacingType::Periodic(_)))
}
/// Provides a collection of all time-driven output streams.
pub fn all_time_driven(&self) -> Vec<&OutputStream> {
self.time_driven.iter().map(|t| self.output(t.reference)).collect()
}
/// Provides the activation contion of a event-driven stream and none if the stream is time-driven
pub fn get_ac(&self, sref: StreamReference) -> Option<&ActivationCondition> {
self.event_driven.iter().find(|e| e.reference == sref).map(|e| &e.ac)
}
/// Provides immutable access to a discrete window.
///
/// # Panic
/// Panics if `window` is a [WindowReference::Sliding].
pub fn discrete_window(&self, window: WindowReference) -> &DiscreteWindow {
match window {
WindowReference::Discrete(x) => &self.discrete_windows[x],
WindowReference::Sliding(_) => panic!("wrong type of window reference passed to getter"),
}
}
/// Provides immutable access to a sliding window.
///
/// # Panic
/// Panics if `window` is a [WindowReference::Discrete].
pub fn sliding_window(&self, window: WindowReference) -> &SlidingWindow {
match window {
WindowReference::Sliding(x) => &self.sliding_windows[x],
WindowReference::Discrete(_) => panic!("wrong type of window reference passed to getter"),
}
}
/// Provides immutable access to a window.
pub fn window(&self, window: WindowReference) -> &dyn Window {
match window {
WindowReference::Sliding(x) => &self.sliding_windows[x],
WindowReference::Discrete(x) => &self.discrete_windows[x],
}
}
/// Provides a representation for the evaluation layers of all event-driven output streams. Each element of the outer `Vec` represents a layer, each element of the inner `Vec` an output stream in the layer.
pub fn get_event_driven_layers(&self) -> Vec<Vec<Task>> {
let mut event_driven_spawns = self
.outputs
.iter()
.filter(|o| matches!(o.spawn.pacing, PacingType::Event(_)))
.peekable();
// Peekable is fine because the filter above does not have side effects
if self.event_driven.is_empty() && event_driven_spawns.peek().is_none() {
return vec![];
}
// Zip eval layer with stream reference.
let streams_with_layers = self
.event_driven
.iter()
.map(|s| s.reference)
.map(|r| (self.output(r).eval_layer().into(), Task::Evaluate(r.out_ix())));
let spawns_with_layers =
event_driven_spawns.map(|o| (o.spawn_layer().inner(), Task::Spawn(o.reference.out_ix())));
let tasks_with_layers: Vec<(usize, Task)> = streams_with_layers.chain(spawns_with_layers).collect();
// Streams are annotated with an evaluation layer. The layer is not minimal, so there might be
// layers without entries and more layers than streams.
// Minimization works as follows:
// a) Find the greatest layer
// b) For each potential layer...
// c) Find streams that would be in it.
// d) If there is none, skip this layer
// e) If there are some, add them as layer.
// a) Find the greatest layer. Maximum must exist because vec cannot be empty.
let max_layer = tasks_with_layers.iter().max_by_key(|(layer, _)| layer).unwrap().0;
let mut layers = Vec::new();
// b) For each potential layer
for i in 0..=max_layer {
// c) Find streams that would be in it.
let in_layer_i: Vec<Task> = tasks_with_layers
.iter()
.filter_map(|(l, r)| if *l == i { Some(*r) } else { None })
.collect();
if in_layer_i.is_empty() {
// d) If there is none, skip this layer
continue;
} else {
// e) If there are some, add them as layer.
layers.push(in_layer_i);
}
}
layers
}More examples
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
fn stream_infos(mir: &Mir, sref: StreamReference) -> NodeInformation {
let stream = mir.stream(sref);
let stream_name = stream.name();
let eval_layer: usize = stream.eval_layer().into();
let memory_bound = stream.values_to_memorize().unwrap();
let value_ty = stream.ty();
let value_str = value_ty.to_string();
match sref {
StreamReference::In(_) => {
NodeInformation::Input {
reference: sref,
stream_name,
memory_bound,
value_ty: value_str,
}
},
StreamReference::Out(_) => {
let output = mir.output(sref);
let pacing_str = mir.display(&output.eval.eval_pacing).to_string();
let expr_str = mir.display(&output.eval.expression).to_string();
NodeInformation::Output {
reference: sref,
stream_name,
eval_layer,
memory_bound,
pacing_ty: pacing_str,
value_ty: value_str,
expression: expr_str,
}
},
}
}70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
pub(crate) fn from(ir: &RtLolaMir) -> Result<Schedule, String> {
let stream_periods = ir
.time_driven
.iter()
.filter_map(|tds| ir.output(tds.reference).is_spawned().not().then(|| tds.period()));
let spawn_periods = ir.outputs.iter().filter_map(|o| {
if let PacingType::Periodic(freq) = &o.spawn.pacing {
Some(UOM_Time::new::<second>(freq.get::<uom::si::frequency::hertz>().inv()))
} else {
None
}
});
let close_periods = ir.outputs.iter().filter_map(|o| {
if let PacingType::Periodic(freq) = &o.close.pacing {
o.close
.has_self_reference
.not()
.then(|| UOM_Time::new::<second>(freq.get::<uom::si::frequency::hertz>().inv()))
} else {
None
}
});
let periods: Vec<UOM_Time> = stream_periods.chain(spawn_periods).chain(close_periods).collect();
if periods.is_empty() {
// Nothing to schedule here
return Ok(Schedule {
hyper_period: None,
deadlines: vec![],
});
}
let gcd = Self::find_extend_period(&periods);
let hyper_period = Self::find_hyper_period(&periods);
let extend_steps = Self::build_extend_steps(ir, gcd, hyper_period)?;
let extend_steps = Self::apply_periodicity(&extend_steps);
let mut deadlines = Self::condense_deadlines(gcd, extend_steps);
Self::sort_deadlines(ir, &mut deadlines);
let hyper_period = Duration::from_nanos(hyper_period.get::<nanosecond>().to_integer().to_u64().unwrap());
Ok(Schedule {
hyper_period: Some(hyper_period),
deadlines,
})
}
/// Determines the maximal amount of time the process can wait between successive checks for
/// due deadlines without missing one.
fn find_extend_period(rates: &[UOM_Time]) -> UOM_Time {
assert!(!rates.is_empty());
let rates: Vec<Rational> = rates.iter().map(|r| r.get::<nanosecond>()).collect();
let gcd = math::rational_gcd_all(&rates);
UOM_Time::new::<nanosecond>(gcd)
}
/// Determines the hyper period of the given `rates`.
fn find_hyper_period(rates: &[UOM_Time]) -> UOM_Time {
assert!(!rates.is_empty());
let rates: Vec<Rational> = rates.iter().map(|r| r.get::<nanosecond>()).collect();
let lcm = math::rational_lcm_all(&rates);
let lcm = math::rational_lcm(lcm, Rational::one()); // needs to be multiple of 1 ns
UOM_Time::new::<nanosecond>(lcm)
}
/// Takes a vec of gcd-sized intervals. In each interval, there are streams that need
/// to be scheduled periodically at this point in time.
/// Example:
/// Hyper-period: 2 seconds, gcd: 500ms, streams: (c @ .5Hz), (b @ 1Hz), (a @ 2Hz)
/// Input: `[[a] [b] [] [c]]`
/// Output: `[[a] [a,b] [a] [a,b,c]]`
fn apply_periodicity(steps: &[Vec<Task>]) -> Vec<Vec<Task>> {
// Whenever there are streams in a cell at index `i`,
// add them to every cell with index k*i within bounds, where k > 1.
// k = 0 would always schedule them initially, so this must be skipped.
// TODO: Skip last half of the array.
let mut res = vec![Vec::new(); steps.len()];
for (ix, streams) in steps.iter().enumerate() {
if !streams.is_empty() {
let mut k = 1;
while let Some(target) = res.get_mut(k * (ix + 1) - 1) {
target.extend(streams);
k += 1;
}
}
}
res
}
/// Build extend steps for each gcd-sized time interval up to the hyper period.
/// Example:
/// Hyper-period: 2 seconds, gcd: 500ms, streams: (c @ .5Hz), (b @ 1Hz), (a @ 2Hz)
/// Result: `[[a] [b] [] [c]]`
/// Meaning: `a` starts being scheduled after one gcd, `b` after two gcds, `c` after 4 gcds.
fn build_extend_steps(ir: &RtLolaMir, gcd: UOM_Time, hyper_period: UOM_Time) -> Result<Vec<Vec<Task>>, String> {
let num_steps = hyper_period.get::<second>() / gcd.get::<second>();
assert!(num_steps.is_integer());
let num_steps = num_steps.to_integer() as usize;
if num_steps >= 10_000_000 {
return Err("stream frequencies are too incompatible to generate schedule".to_string());
}
let mut extend_steps = vec![Vec::new(); num_steps];
for s in ir
.time_driven
.iter()
.filter(|tds| !ir.output(tds.reference).is_spawned())
{
let ix = s.period().get::<second>() / gcd.get::<second>();
// Period must be integer multiple of gcd by def of gcd
assert!(ix.is_integer());
let ix = ix.to_integer() as usize;
let ix = ix - 1;
extend_steps[ix].push(Task::Evaluate(s.reference.out_ix()));
}
let periodic_spawns = ir.outputs.iter().filter_map(|o| {
match &o.spawn.pacing {
PacingType::Periodic(freq) => {
Some((
o.reference.out_ix(),
UOM_Time::new::<second>(freq.get::<uom::si::frequency::hertz>().inv()),
))
},
_ => None,
}
});
for (out_ix, period) in periodic_spawns {
let ix = period.get::<second>() / gcd.get::<second>();
// Period must be integer multiple of gcd by def of gcd
assert!(ix.is_integer());
let ix = ix.to_integer() as usize;
let ix = ix - 1;
extend_steps[ix].push(Task::Spawn(out_ix));
}
let periodic_close = ir.outputs.iter().filter_map(|o| {
if let PacingType::Periodic(freq) = &o.close.pacing {
o.close.has_self_reference.not().then(|| {
(
o.reference.out_ix(),
UOM_Time::new::<second>(freq.get::<uom::si::frequency::hertz>().inv()),
)
})
} else {
None
}
});
for (out_ix, period) in periodic_close {
let ix = period.get::<second>() / gcd.get::<second>();
// Period must be integer multiple of gcd by def of gcd
assert!(ix.is_integer());
let ix = ix.to_integer() as usize;
let ix = ix - 1;
extend_steps[ix].push(Task::Close(out_ix));
}
Ok(extend_steps)
}239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
pub(crate) fn display_expression(mir: &Mir, expr: &Expression, current_level: u32) -> String {
match &expr.kind {
ExpressionKind::LoadConstant(c) => c.to_string(),
ExpressionKind::ArithLog(op, exprs) => {
let (op_level, associative) = precedence_level(op);
let display_exprs = exprs
.iter()
.map(|expr| display_expression(mir, expr, op_level))
.collect::<Vec<_>>();
let display = match display_exprs.len() {
1 => format!("{}{}", op, display_exprs[0]),
2 => format!("{} {} {}", display_exprs[0], op, display_exprs[1]),
_ => unreachable!(),
};
if (associative && current_level < op_level || !associative && current_level <= op_level)
&& current_level != 0
{
format!("({display})")
} else {
display
}
},
ExpressionKind::StreamAccess {
target,
parameters,
access_kind,
} => {
let stream_name = mir.stream(*target).name();
let target_name = if !parameters.is_empty() {
let parameter_list = parameters
.iter()
.map(|parameter| display_expression(mir, parameter, 0))
.collect::<Vec<_>>()
.join(", ");
format!("{stream_name}({parameter_list})")
} else {
stream_name.into()
};
match access_kind {
StreamAccessKind::Sync => target_name,
StreamAccessKind::DiscreteWindow(_) => todo!(),
StreamAccessKind::SlidingWindow(w) => {
let window = mir.sliding_window(*w);
let target_name = mir.stream(window.target).name();
let duration = window.duration.as_secs_f64().to_string();
let op = &window.op;
format!("{target_name}.aggregate(over: {duration}s, using: {op})")
},
StreamAccessKind::Hold => format!("{target_name}.hold()"),
StreamAccessKind::Offset(o) => format!("{target_name}.offset(by:-{o})"),
StreamAccessKind::Get => format!("{target_name}.get()"),
StreamAccessKind::Fresh => format!("{target_name}.fresh()"),
}
},
ExpressionKind::ParameterAccess(sref, parameter) => mir.output(*sref).params[*parameter].name.to_string(),
ExpressionKind::Ite {
condition,
consequence,
alternative,
} => {
let display_condition = display_expression(mir, condition, 0);
let display_consequence = display_expression(mir, consequence, 0);
let display_alternative = display_expression(mir, alternative, 0);
format!("if {display_condition} then {display_consequence} else {display_alternative}")
},
ExpressionKind::Tuple(exprs) => {
let display_exprs = exprs
.iter()
.map(|expr| display_expression(mir, expr, 0))
.collect::<Vec<_>>()
.join(", ");
format!("({display_exprs})")
},
ExpressionKind::TupleAccess(expr, i) => {
let display_expr = display_expression(mir, expr, 20);
format!("{display_expr}({i})")
},
ExpressionKind::Function(name, args) => {
let display_args = args
.iter()
.map(|arg| display_expression(mir, arg, 0))
.collect::<Vec<_>>()
.join(", ");
format!("{name}({display_args})")
},
ExpressionKind::Convert { expr: inner_expr } => {
let inner_display = display_expression(mir, inner_expr, 0);
format!("Cast<{},{}>({inner_display})", expr.ty, inner_expr.ty)
},
ExpressionKind::Default { expr, default } => {
let display_expr = display_expression(mir, expr, 0);
let display_default = display_expression(mir, default, 0);
format!("{display_expr}.defaults(to: {display_default})")
},
}
}
impl<'a> Display for RtLolaMirPrinter<'a, Expression> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "{}", display_expression(self.mir, self.inner, 0))
}
}
impl Display for InputStream {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let name = &self.name;
let ty = &self.ty;
write!(f, "input {name} : {ty}")
}
}
impl<'a> Display for RtLolaMirPrinter<'a, OutputStream> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let OutputStream {
name,
ty,
spawn,
eval,
close,
params,
..
} = self.inner;
let display_pacing = RtLolaMirPrinter::new(self.mir, &eval.eval_pacing).to_string();
let display_parameters = if !params.is_empty() {
let parameter_list = params
.iter()
.map(|parameter| format!("{} : {}", parameter.name, parameter.ty))
.join(", ");
format!("({parameter_list})")
} else {
"".into()
};
writeln!(f, "output {name}{display_parameters} : {ty}")?;
if let Some(spawn_expr) = &spawn.expression {
let display_spawn_expr = display_expression(self.mir, spawn_expr, 0);
write!(f, " spawn with {display_spawn_expr}")?;
if let Some(spawn_condition) = &spawn.condition {
let display_spawn_condition = display_expression(self.mir, spawn_condition, 0);
write!(f, " when {display_spawn_condition}")?;
}
writeln!(f)?;
}
write!(f, " eval @{display_pacing} ")?;
if let Some(eval_condition) = &eval.condition {
let display_eval_condition = display_expression(self.mir, eval_condition, 0);
write!(f, "when {display_eval_condition} ")?;
}
let display_eval_expr = display_expression(self.mir, &eval.expression, 0);
write!(f, "with {display_eval_expr}")?;
if let Some(close_condition) = &close.condition {
let display_close_condition = display_expression(self.mir, close_condition, 0);
write!(f, "\n close when {display_close_condition}")?;
}
Ok(())
}
}
impl<'a> Display for RtLolaMirPrinter<'a, Trigger> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let output = self.mir.output(self.inner.reference);
let output_name = output.name();
let message = &self.inner.message;
write!(f, "trigger {output_name} \"{message}\"")
}sourcepub fn stream(&self, reference: StreamReference) -> &dyn Stream
pub fn stream(&self, reference: StreamReference) -> &dyn Stream
Provides immutable access to a stream.
Examples found in repository?
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self.inner {
ActivationCondition::Conjunction(s) => {
let rs = s
.iter()
.map(|ac| RtLolaMirPrinter::new(self.mir, ac).to_string())
.join(&ArithLogOp::And.to_string());
write!(f, "{}", rs)
},
ActivationCondition::Disjunction(s) => {
let rs = s
.iter()
.map(|ac| RtLolaMirPrinter::new(self.mir, ac).to_string())
.join(&ArithLogOp::Or.to_string());
write!(f, "{}", rs)
},
ActivationCondition::Stream(s) => write!(f, "{}", self.mir.stream(*s).name()),
ActivationCondition::True => write!(f, "true"),
}
}
}
impl<'a> Display for RtLolaMirPrinter<'a, PacingType> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self.inner {
super::PacingType::Periodic(freq) => {
let s = freq
.into_format_args(uom::si::frequency::hertz, uom::fmt::DisplayStyle::Abbreviation)
.to_string();
write!(f, "{}Hz", &s[..s.len() - 3])
},
super::PacingType::Event(ac) => RtLolaMirPrinter::new(self.mir, ac).fmt(f),
super::PacingType::Constant => write!(f, "true"),
}
}
}
type Associativity = bool;
fn precedence_level(op: &ArithLogOp) -> (u32, Associativity) {
// https://en.cppreference.com/w/c/language/operator_precedence
let precedence = match op {
ArithLogOp::Not | ArithLogOp::BitNot | ArithLogOp::Neg => 2,
ArithLogOp::Mul | ArithLogOp::Rem | ArithLogOp::Pow | ArithLogOp::Div => 3,
ArithLogOp::Add | ArithLogOp::Sub => 4,
ArithLogOp::Shl | ArithLogOp::Shr => 5,
ArithLogOp::Lt | ArithLogOp::Le | ArithLogOp::Ge | ArithLogOp::Gt => 6,
ArithLogOp::Eq | ArithLogOp::Ne => 7,
ArithLogOp::BitAnd => 8,
ArithLogOp::BitXor => 9,
ArithLogOp::BitOr => 10,
ArithLogOp::And => 11,
ArithLogOp::Or => 12,
};
let associativity = !matches!(op, ArithLogOp::Div | ArithLogOp::Sub);
(precedence, associativity)
}
pub(crate) fn display_expression(mir: &Mir, expr: &Expression, current_level: u32) -> String {
match &expr.kind {
ExpressionKind::LoadConstant(c) => c.to_string(),
ExpressionKind::ArithLog(op, exprs) => {
let (op_level, associative) = precedence_level(op);
let display_exprs = exprs
.iter()
.map(|expr| display_expression(mir, expr, op_level))
.collect::<Vec<_>>();
let display = match display_exprs.len() {
1 => format!("{}{}", op, display_exprs[0]),
2 => format!("{} {} {}", display_exprs[0], op, display_exprs[1]),
_ => unreachable!(),
};
if (associative && current_level < op_level || !associative && current_level <= op_level)
&& current_level != 0
{
format!("({display})")
} else {
display
}
},
ExpressionKind::StreamAccess {
target,
parameters,
access_kind,
} => {
let stream_name = mir.stream(*target).name();
let target_name = if !parameters.is_empty() {
let parameter_list = parameters
.iter()
.map(|parameter| display_expression(mir, parameter, 0))
.collect::<Vec<_>>()
.join(", ");
format!("{stream_name}({parameter_list})")
} else {
stream_name.into()
};
match access_kind {
StreamAccessKind::Sync => target_name,
StreamAccessKind::DiscreteWindow(_) => todo!(),
StreamAccessKind::SlidingWindow(w) => {
let window = mir.sliding_window(*w);
let target_name = mir.stream(window.target).name();
let duration = window.duration.as_secs_f64().to_string();
let op = &window.op;
format!("{target_name}.aggregate(over: {duration}s, using: {op})")
},
StreamAccessKind::Hold => format!("{target_name}.hold()"),
StreamAccessKind::Offset(o) => format!("{target_name}.offset(by:-{o})"),
StreamAccessKind::Get => format!("{target_name}.get()"),
StreamAccessKind::Fresh => format!("{target_name}.fresh()"),
}
},
ExpressionKind::ParameterAccess(sref, parameter) => mir.output(*sref).params[*parameter].name.to_string(),
ExpressionKind::Ite {
condition,
consequence,
alternative,
} => {
let display_condition = display_expression(mir, condition, 0);
let display_consequence = display_expression(mir, consequence, 0);
let display_alternative = display_expression(mir, alternative, 0);
format!("if {display_condition} then {display_consequence} else {display_alternative}")
},
ExpressionKind::Tuple(exprs) => {
let display_exprs = exprs
.iter()
.map(|expr| display_expression(mir, expr, 0))
.collect::<Vec<_>>()
.join(", ");
format!("({display_exprs})")
},
ExpressionKind::TupleAccess(expr, i) => {
let display_expr = display_expression(mir, expr, 20);
format!("{display_expr}({i})")
},
ExpressionKind::Function(name, args) => {
let display_args = args
.iter()
.map(|arg| display_expression(mir, arg, 0))
.collect::<Vec<_>>()
.join(", ");
format!("{name}({display_args})")
},
ExpressionKind::Convert { expr: inner_expr } => {
let inner_display = display_expression(mir, inner_expr, 0);
format!("Cast<{},{}>({inner_display})", expr.ty, inner_expr.ty)
},
ExpressionKind::Default { expr, default } => {
let display_expr = display_expression(mir, expr, 0);
let display_default = display_expression(mir, default, 0);
format!("{display_expr}.defaults(to: {display_default})")
},
}
}More examples
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
fn stream_infos(mir: &Mir, sref: StreamReference) -> NodeInformation {
let stream = mir.stream(sref);
let stream_name = stream.name();
let eval_layer: usize = stream.eval_layer().into();
let memory_bound = stream.values_to_memorize().unwrap();
let value_ty = stream.ty();
let value_str = value_ty.to_string();
match sref {
StreamReference::In(_) => {
NodeInformation::Input {
reference: sref,
stream_name,
memory_bound,
value_ty: value_str,
}
},
StreamReference::Out(_) => {
let output = mir.output(sref);
let pacing_str = mir.display(&output.eval.eval_pacing).to_string();
let expr_str = mir.display(&output.eval.expression).to_string();
NodeInformation::Output {
reference: sref,
stream_name,
eval_layer,
memory_bound,
pacing_ty: pacing_str,
value_ty: value_str,
expression: expr_str,
}
},
}
}sourcepub fn all_streams(&self) -> impl Iterator<Item = StreamReference>
pub fn all_streams(&self) -> impl Iterator<Item = StreamReference>
Produces an iterator over all stream references.
Examples found in repository?
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
pub(super) fn new(mir: &'a Mir) -> Self {
let trigger: HashSet<_> = mir.triggers.iter().map(|t| t.reference).collect();
let stream_nodes = mir
.all_streams()
.filter_map(|sref| trigger.contains(&sref).not().then_some(Node::Stream(sref)));
let trigger_nodes = mir.triggers.iter().map(|t| Node::Trigger(t.trigger_reference));
let window_nodes = mir.sliding_windows.iter().map(|w| Node::Window(w.reference));
let nodes: Vec<_> = stream_nodes.chain(window_nodes).chain(trigger_nodes).collect();
let edges = edges(mir);
let infos = nodes.iter().map(|node| (*node, node_infos(mir, *node))).collect();
Self { nodes, edges, infos }
}sourcepub fn all_triggers(&self) -> Vec<&OutputStream>
pub fn all_triggers(&self) -> Vec<&OutputStream>
Provides a collection of all output streams representing a trigger.
sourcepub fn all_event_driven(&self) -> Vec<&OutputStream>
pub fn all_event_driven(&self) -> Vec<&OutputStream>
Provides a collection of all event-driven output streams.
sourcepub fn has_time_driven_features(&self) -> bool
pub fn has_time_driven_features(&self) -> bool
Return true if the specification contains any time-driven features. This includes time-driven streams and time-driven spawn conditions.
sourcepub fn all_time_driven(&self) -> Vec<&OutputStream>
pub fn all_time_driven(&self) -> Vec<&OutputStream>
Provides a collection of all time-driven output streams.
sourcepub fn get_ac(&self, sref: StreamReference) -> Option<&ActivationCondition>
pub fn get_ac(&self, sref: StreamReference) -> Option<&ActivationCondition>
Provides the activation contion of a event-driven stream and none if the stream is time-driven
sourcepub fn discrete_window(&self, window: WindowReference) -> &DiscreteWindow
pub fn discrete_window(&self, window: WindowReference) -> &DiscreteWindow
Provides immutable access to a discrete window.
Panic
Panics if window is a WindowReference::Sliding.
Examples found in repository?
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
fn window_infos(mir: &Mir, wref: WindowReference) -> NodeInformation {
let window = mir.window(wref);
let operation_str = window.op().to_string();
let duration_str = match wref {
WindowReference::Sliding(_) => {
let duration = mir.sliding_window(wref).duration;
format!("{}s", duration.as_secs_f64())
},
WindowReference::Discrete(_) => {
let duration = mir.discrete_window(wref).duration;
format!("{duration} values")
},
};
NodeInformation::Window {
reference: wref,
operation: operation_str,
duration: duration_str,
}
}sourcepub fn sliding_window(&self, window: WindowReference) -> &SlidingWindow
pub fn sliding_window(&self, window: WindowReference) -> &SlidingWindow
Provides immutable access to a sliding window.
Panic
Panics if window is a WindowReference::Discrete.
Examples found in repository?
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
fn window_infos(mir: &Mir, wref: WindowReference) -> NodeInformation {
let window = mir.window(wref);
let operation_str = window.op().to_string();
let duration_str = match wref {
WindowReference::Sliding(_) => {
let duration = mir.sliding_window(wref).duration;
format!("{}s", duration.as_secs_f64())
},
WindowReference::Discrete(_) => {
let duration = mir.discrete_window(wref).duration;
format!("{duration} values")
},
};
NodeInformation::Window {
reference: wref,
operation: operation_str,
duration: duration_str,
}
}More examples
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
pub(crate) fn display_expression(mir: &Mir, expr: &Expression, current_level: u32) -> String {
match &expr.kind {
ExpressionKind::LoadConstant(c) => c.to_string(),
ExpressionKind::ArithLog(op, exprs) => {
let (op_level, associative) = precedence_level(op);
let display_exprs = exprs
.iter()
.map(|expr| display_expression(mir, expr, op_level))
.collect::<Vec<_>>();
let display = match display_exprs.len() {
1 => format!("{}{}", op, display_exprs[0]),
2 => format!("{} {} {}", display_exprs[0], op, display_exprs[1]),
_ => unreachable!(),
};
if (associative && current_level < op_level || !associative && current_level <= op_level)
&& current_level != 0
{
format!("({display})")
} else {
display
}
},
ExpressionKind::StreamAccess {
target,
parameters,
access_kind,
} => {
let stream_name = mir.stream(*target).name();
let target_name = if !parameters.is_empty() {
let parameter_list = parameters
.iter()
.map(|parameter| display_expression(mir, parameter, 0))
.collect::<Vec<_>>()
.join(", ");
format!("{stream_name}({parameter_list})")
} else {
stream_name.into()
};
match access_kind {
StreamAccessKind::Sync => target_name,
StreamAccessKind::DiscreteWindow(_) => todo!(),
StreamAccessKind::SlidingWindow(w) => {
let window = mir.sliding_window(*w);
let target_name = mir.stream(window.target).name();
let duration = window.duration.as_secs_f64().to_string();
let op = &window.op;
format!("{target_name}.aggregate(over: {duration}s, using: {op})")
},
StreamAccessKind::Hold => format!("{target_name}.hold()"),
StreamAccessKind::Offset(o) => format!("{target_name}.offset(by:-{o})"),
StreamAccessKind::Get => format!("{target_name}.get()"),
StreamAccessKind::Fresh => format!("{target_name}.fresh()"),
}
},
ExpressionKind::ParameterAccess(sref, parameter) => mir.output(*sref).params[*parameter].name.to_string(),
ExpressionKind::Ite {
condition,
consequence,
alternative,
} => {
let display_condition = display_expression(mir, condition, 0);
let display_consequence = display_expression(mir, consequence, 0);
let display_alternative = display_expression(mir, alternative, 0);
format!("if {display_condition} then {display_consequence} else {display_alternative}")
},
ExpressionKind::Tuple(exprs) => {
let display_exprs = exprs
.iter()
.map(|expr| display_expression(mir, expr, 0))
.collect::<Vec<_>>()
.join(", ");
format!("({display_exprs})")
},
ExpressionKind::TupleAccess(expr, i) => {
let display_expr = display_expression(mir, expr, 20);
format!("{display_expr}({i})")
},
ExpressionKind::Function(name, args) => {
let display_args = args
.iter()
.map(|arg| display_expression(mir, arg, 0))
.collect::<Vec<_>>()
.join(", ");
format!("{name}({display_args})")
},
ExpressionKind::Convert { expr: inner_expr } => {
let inner_display = display_expression(mir, inner_expr, 0);
format!("Cast<{},{}>({inner_display})", expr.ty, inner_expr.ty)
},
ExpressionKind::Default { expr, default } => {
let display_expr = display_expression(mir, expr, 0);
let display_default = display_expression(mir, default, 0);
format!("{display_expr}.defaults(to: {display_default})")
},
}
}sourcepub fn window(&self, window: WindowReference) -> &dyn Window
pub fn window(&self, window: WindowReference) -> &dyn Window
Provides immutable access to a window.
Examples found in repository?
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
fn window_infos(mir: &Mir, wref: WindowReference) -> NodeInformation {
let window = mir.window(wref);
let operation_str = window.op().to_string();
let duration_str = match wref {
WindowReference::Sliding(_) => {
let duration = mir.sliding_window(wref).duration;
format!("{}s", duration.as_secs_f64())
},
WindowReference::Discrete(_) => {
let duration = mir.discrete_window(wref).duration;
format!("{duration} values")
},
};
NodeInformation::Window {
reference: wref,
operation: operation_str,
duration: duration_str,
}
}sourcepub fn get_event_driven_layers(&self) -> Vec<Vec<Task>>
pub fn get_event_driven_layers(&self) -> Vec<Vec<Task>>
Provides a representation for the evaluation layers of all event-driven output streams. Each element of the outer Vec represents a layer, each element of the inner Vec an output stream in the layer.
sourcepub fn compute_schedule(&self) -> Result<Schedule, String>
pub fn compute_schedule(&self) -> Result<Schedule, String>
Attempts to compute a schedule for all time-driven streams.
Fail
Fails if the resulting schedule would require at least 10^7 deadlines.
sourcepub fn display<'a, T>(&'a self, target: &'a T) -> RtLolaMirPrinter<'a, T>
pub fn display<'a, T>(&'a self, target: &'a T) -> RtLolaMirPrinter<'a, T>
Creates a new RtLolaMirPrinter for the Mir type T. It implements the Display Trait for type T.
Examples found in repository?
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
fn stream_infos(mir: &Mir, sref: StreamReference) -> NodeInformation {
let stream = mir.stream(sref);
let stream_name = stream.name();
let eval_layer: usize = stream.eval_layer().into();
let memory_bound = stream.values_to_memorize().unwrap();
let value_ty = stream.ty();
let value_str = value_ty.to_string();
match sref {
StreamReference::In(_) => {
NodeInformation::Input {
reference: sref,
stream_name,
memory_bound,
value_ty: value_str,
}
},
StreamReference::Out(_) => {
let output = mir.output(sref);
let pacing_str = mir.display(&output.eval.eval_pacing).to_string();
let expr_str = mir.display(&output.eval.expression).to_string();
NodeInformation::Output {
reference: sref,
stream_name,
eval_layer,
memory_bound,
pacing_ty: pacing_str,
value_ty: value_str,
expression: expr_str,
}
},
}
}sourcepub fn dependency_graph(&self) -> DependencyGraph<'_>
pub fn dependency_graph(&self) -> DependencyGraph<'_>
Represents the specification as a dependency graph