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
//! A parser for RTLola specifications
//!
//! This crate offers functionality to transform a textual representation of an RTLola specification into an abstract syntax tree. The Ast is not the most convenient data structure for
//! modifying or analyzing a specification; there are other options available, outlined below.
//!
//! # 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: [RtLolaAst], and [parse()].
//! * [RtLolaHir](https://docs.rs/rtlola_hir/struct.RtLolaHir.html): 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](https://docs.rs/rtlola_hir).
//! * [RtLolaMir](https://docs.rs/rtlola_frontend/struct.RtLolaMir.html): 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: [rtlola_hir](https://docs.rs/rtlola_hir).
//! As a rule of thumb, if you want to analyze and/or enrich a specification, use the [RtLolaHir](https://docs.rs/rtlola_hir/struct.RtLolaHir.html). If you only need a convenient representation of the specification for some devious
//! activity such as compiling it into something else, the [RtLolaMir](https://docs.rs/rtlola_frontend/struct.RtLolaMir.html) is the way to go.
//!
//! # Modules
//! * [ast] Contains anything related to the [RtLolaAst].
#![forbid(unused_must_use)] // disallow discarding errors
#![warn(
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
//! This module provides the functionality needed to parse an RTLola specification into a [RtLolaAst].
mod parse; // Shall not be exposed; use parse function instead.
use std::fs::File;
use std::io::{self, Read};
use std::path::PathBuf;
// Public exports
pub mod ast;
pub use ast::RtLolaAst;
use rtlola_reporting::Handler;
#[derive(Debug, Clone)]
/// The configuration of the parser.
pub struct ParserConfig {
/// The path to the specification file that should be parsed
path: Option<PathBuf>,
/// The specification given as a string
spec: String,
}
impl ParserConfig {
/// Reads the specification from the given path and creates a new parser configuration for it.
pub fn from_path(path_to_spec: PathBuf) -> io::Result<Self> {
let mut file = File::open(&path_to_spec)?;
let mut spec = String::new();
file.read_to_string(&mut spec)?;
drop(file);
Ok(ParserConfig {
path: Some(path_to_spec),
spec,
})
}
/// Creates a new parser configuration for the given specification.
pub fn for_string(spec: String) -> Self {
ParserConfig { path: None, spec }
}
/// Invokes the parser on the specification given in the configuration.
pub fn parse(self) -> Result<RtLolaAst, String> {
parse(self)
}
/// Returns the path of the specification.
pub fn path(&self) -> &Option<PathBuf> {
&self.path
}
/// Returns the specification of the configuration.
pub fn spec(&self) -> &str {
&self.spec
}
}
/// Invokes the parser with the given configuration.
pub fn parse(cfg: ParserConfig) -> Result<RtLolaAst, String> {
let handler = if let Some(path) = &cfg.path {
rtlola_reporting::Handler::new(path.clone(), cfg.spec.clone())
} else {
rtlola_reporting::Handler::without_file(cfg.spec.clone())
};
let spec = match crate::parse::RtLolaParser::parse(&handler, cfg) {
Ok(spec) => spec,
Err(e) => {
return Err(format!("error: invalid syntax:\n{}", e));
},
};
Ok(spec)
}
/// Invokes the parser with the given configuration and uses the provided [Handler] for error reporting.
pub fn parse_with_handler(cfg: ParserConfig, handler: &Handler) -> Result<RtLolaAst, String> {
let spec = match crate::parse::RtLolaParser::parse(&handler, cfg) {
Ok(spec) => spec,
Err(e) => {
return Err(format!("error: invalid syntax:\n{}", e));
},
};
Ok(spec)
}