Skip to main content

tx3_lang/
facade.rs

1use std::collections::{BTreeMap, HashMap};
2
3use tx3_tir::reduce::{Apply, ArgValue};
4
5use crate::{analyzing, ast, lowering, parsing};
6
7#[derive(Debug, thiserror::Error, miette::Diagnostic)]
8pub enum Error {
9    #[error("I/O error: {0}")]
10    Io(#[from] std::io::Error),
11
12    #[error("Missing main code")]
13    MissingMain,
14
15    #[error("Parsing error: {0}")]
16    #[diagnostic(transparent)]
17    Parsing(#[from] parsing::Error),
18
19    #[error("Analyzing error")]
20    Analyzing(#[from] analyzing::AnalyzeReport),
21
22    #[error("Apply error: {0}")]
23    Apply(#[from] tx3_tir::reduce::Error),
24}
25
26pub type Code = String;
27
28pub struct Workspace {
29    main: Option<Code>,
30    ast: Option<ast::Program>,
31    analisis: Option<analyzing::AnalyzeReport>,
32    tir: HashMap<String, tx3_tir::model::v1beta0::Tx>,
33}
34
35impl Workspace {
36    pub fn from_file(main: impl AsRef<std::path::Path>) -> Result<Self, Error> {
37        let main = std::fs::read_to_string(main.as_ref())?;
38
39        Ok(Self {
40            main: Some(main),
41            ast: None,
42            analisis: None,
43            tir: HashMap::new(),
44        })
45    }
46
47    pub fn from_string(main: Code) -> Self {
48        Self {
49            main: Some(main),
50            ast: None,
51            analisis: None,
52            tir: HashMap::new(),
53        }
54    }
55
56    fn ensure_main(&self) -> Result<&Code, Error> {
57        if self.main.is_none() {
58            return Err(Error::MissingMain);
59        }
60
61        Ok(self.main.as_ref().unwrap())
62    }
63
64    pub fn parse(&mut self) -> Result<(), Error> {
65        let main = self.ensure_main()?;
66        let ast = parsing::parse_string(main)?;
67        self.ast = Some(ast);
68        Ok(())
69    }
70
71    fn ensure_ast(&mut self) -> Result<(), Error> {
72        if self.ast.is_none() {
73            self.parse()?;
74        }
75
76        Ok(())
77    }
78
79    pub fn ast(&self) -> Option<&ast::Program> {
80        self.ast.as_ref()
81    }
82
83    pub fn analyze(&mut self) -> Result<(), Error> {
84        self.ensure_ast()?;
85
86        let ast = self.ast.as_mut().unwrap();
87
88        self.analisis = Some(analyzing::analyze(ast));
89
90        Ok(())
91    }
92
93    pub fn ensure_analisis(&mut self) -> Result<(), Error> {
94        if self.analisis.is_none() {
95            self.analyze()?;
96        }
97
98        Ok(())
99    }
100
101    pub fn analisis(&self) -> Option<&analyzing::AnalyzeReport> {
102        self.analisis.as_ref()
103    }
104
105    pub fn lower(&mut self) -> Result<(), Error> {
106        self.ensure_analisis()?;
107
108        let analisis = self.analisis().unwrap();
109
110        if !analisis.errors.is_empty() {
111            return Err(Error::from(analisis.clone()));
112        }
113
114        let ast = self.ast.as_ref().unwrap();
115
116        for tx in ast.txs.iter() {
117            let tir = lowering::lower(ast, &tx.name.value).unwrap();
118            self.tir.insert(tx.name.value.clone(), tir);
119        }
120
121        Ok(())
122    }
123
124    pub fn ensure_tir(&mut self) -> Result<(), Error> {
125        if self.tir.is_empty() {
126            self.lower()?;
127        }
128
129        Ok(())
130    }
131
132    pub fn tir(&self, name: &str) -> Option<&tx3_tir::model::v1beta0::Tx> {
133        self.tir.get(name)
134    }
135
136    pub fn apply_args(&mut self, args: &BTreeMap<String, ArgValue>) -> Result<(), Error> {
137        self.ensure_tir()?;
138
139        let values = self.tir.drain();
140        let mut new_tir = HashMap::new();
141
142        for (key, tir) in values {
143            let tir = tir.apply_args(args)?;
144            new_tir.insert(key, tir);
145        }
146
147        self.tir = new_tir;
148
149        Ok(())
150    }
151}
152
153#[cfg(test)]
154pub mod tests {
155    use super::*;
156
157    #[test]
158    fn smoke_test_happy_path() {
159        let manifest_dir = env!("CARGO_MANIFEST_DIR");
160
161        let mut workspace =
162            Workspace::from_file(&format!("{manifest_dir}/../..//examples/transfer.tx3")).unwrap();
163
164        workspace.parse().unwrap();
165        workspace.analyze().unwrap();
166        workspace.lower().unwrap();
167    }
168}