1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 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
//! The front-end of the RTLola Monitoring Framework
//!
//! This crate offers functionality to transform a textual representation of an RTLola specification into one of three tree-shaped representation and perform a variety of checks.
//!
//! # Specification Representations
//! * [RtLolaAst]: The Ast represents the abstract syntax of the specification.  It is obtained by first parsing the specification into a homogenous tree
//!  and then remove concrete syntax fragments irrelevant for the logics of the specification.  Apart from that, the Ast does not provide much functionality.
//!  The only checks performed when creating the Ast concern the correct syntax.  See also: [rtlola_parser], [RtLolaAst], and [parse_to_ast].
//! * [RtLolaHir]: The Hir represents a high-level intermediate representation optimized for analyzability.  It contains more convenient methods than the Ast, enables different
//!  analysis steps and provides their reports.  The Hir traverses several modes representing the level to which it was analyzed and refined.
//!  Its base mode is `RtLolaHir<BaseMode>` and its fully analyzed version is `RtLolaHir<CompleteMode>`.  See also: [rtlola_hir], [rtlola_hir::RtLolaHir], [parse_to_base_hir], and [parse_to_base_hir].
//! * [RtLolaMir]: The Mir represents a mid-level intermediate representation optimized for external use such as interpretation and compilation.  It contains several interconnections
//!  enabling easy accesses and additional annotation such as memory bounds for each stream. See also: [RtLolaMir], [parse].
//! As a rule of thumb, if you want to analyze and/or enrich a specification, use the [RtLolaHir].  If you only need a convenient representation of the specification for some devious
//! activity such as compiling it into something else, the [RtLolaMir] is the way to go.
//!
//! # Modules
//! * [mir] Contains anything related to the [RtLolaMir].

#![forbid(unused_must_use)]
#![warn(
    missing_docs,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces,
    unused_qualifications
)]

mod lowering;
pub mod mir;

use mir::Mir;
use rtlola_hir::hir::TransformationErr;
use rtlola_hir::{BaseMode, CompleteMode, HirErr};
use rtlola_parser::RtLolaAst;

#[cfg(test)]
mod tests;

pub(crate) use rtlola_hir::hir::RtLolaHir;
pub use rtlola_parser::ParserConfig;
use rtlola_reporting::Handler;

pub use crate::mir::RtLolaMir;

/// Attempts to parse a textual specification into an [RtLolaMir].
///
/// The specification is wrapped into a [ParserConfig] and can either be a string or a path to a specification file.
///
/// # Fail
/// Fails if either the parsing was unsuccessful due to parsing errors such as incorrect syntax (cf. [FrontEndErr::Parser]) or an analysis failed
/// due to a semantic error such as inconsistent types or unknown identifiers (cf. [FrontEndErr::Analysis] and [HirErr]).
pub fn parse(config: ParserConfig) -> Result<RtLolaMir, FrontEndErr> {
    let hir = parse_to_final_hir(config)?;
    Ok(Mir::from_hir(hir))
}

/// Attempts to parse a textual specification into a fully analyzed [RtLolaHir<CompleteMode>].
///
/// The specification is wrapped into a [ParserConfig] and can either be a string or a path to a specification file.
///
/// # Fail
/// Fails if either the parsing was unsuccessful due to parsing errors such as incorrect syntax (cf. [FrontEndErr::Parser]) or an analysis failed
/// due to a semantic error such as inconsistent types or unknown identifiers (cf. [FrontEndErr::Analysis] and [HirErr]).
pub fn parse_to_final_hir(cfg: ParserConfig) -> Result<RtLolaHir<CompleteMode>, FrontEndErr> {
    let handler = create_handler(&cfg);
    let spec = rtlola_parser::parse_with_handler(cfg, &handler)?;

    Ok(rtlola_hir::fully_analyzed(spec, &handler)?)
}

/// Attempts to parse a textual specification into an [RtLolaHir<BaseMode>].
///
/// The specification is wrapped into a [ParserConfig] and can either be a string or a path to a specification file.
///
/// # Fail
/// Fails if either the parsing was unsuccessful due to parsing errors such as incorrect syntax (cf. [FrontEndErr::Parser]) or the initial analysis failed
/// due occurrences of unknown identifiers (cf. [FrontEndErr::Analysis] and [HirErr::Ast], specifically [rtlola_hir::hir::TransformationErr]).
pub fn parse_to_base_hir(cfg: ParserConfig) -> Result<RtLolaHir<BaseMode>, FrontEndErr> {
    let handler = create_handler(&cfg);
    let spec = rtlola_parser::parse_with_handler(cfg, &handler)?;

    Ok(rtlola_hir::from_ast(spec, &handler)?)
}

/// Attempts to parse a textual specification into an [RtLolaAst].
///
/// The specification is wrapped into a [ParserConfig] and can either be a string or a path to a specification file.
///
/// # Fail
/// Fails if the parsing was unsuccessful due to parsing errors such as incorrect syntax (cf. [FrontEndErr::Parser]).
pub fn parse_to_ast(cfg: ParserConfig) -> Result<RtLolaAst, FrontEndErr> {
    let handler = create_handler(&cfg);
    Ok(rtlola_parser::parse_with_handler(cfg, &handler)?)
}

fn create_handler(cfg: &ParserConfig) -> Handler {
    if let Some(path) = &cfg.path() {
        rtlola_reporting::Handler::new(path.clone(), String::from(cfg.spec()))
    } else {
        rtlola_reporting::Handler::without_file(String::from(cfg.spec()))
    }
}

/// A wrapper for the different kinds of errors that can occur in the front-end.
#[derive(Debug, Clone)]
pub enum FrontEndErr {
    /// Indicates a parsing error and provides a more detailed error description.  The error originates in [rtlola_parser].
    Parser(String),
    /// Indicates an analysis error and provides a more detailed error description (cf. [HirErr]). The error originates in [rtlola_hir].
    Analysis(HirErr),
}

impl From<String> for FrontEndErr {
    fn from(s: String) -> FrontEndErr {
        Self::Parser(s)
    }
}

impl From<HirErr> for FrontEndErr {
    fn from(e: HirErr) -> FrontEndErr {
        Self::Analysis(e)
    }
}

impl From<TransformationErr> for FrontEndErr {
    fn from(e: TransformationErr) -> FrontEndErr {
        Self::Analysis(HirErr::from(e))
    }
}

//TODO: Do we want something like this?
impl std::fmt::Display for FrontEndErr {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            Self::Parser(e) => write!(f, "Could not parse specification: {}", e),
            Self::Analysis(e) => write!(f, "Specification is not valid: {:?}", e),
        }
    }
}