tin/
lib.rs

1//! Tin is a simple embeddable programming language.
2//!
3//! The aim is to have a very light-weight footprint and a simple API.
4//!
5//! # Examples
6//!
7//! ```
8//! # extern crate failure;
9//! # extern crate tin;
10//! # fn main() -> Result<(), failure::Error> {
11//! let source = r#"
12//! pickFirst = |a: i32, b: i32| i32 {
13//!   capture = |x: i32| i32 { a + x };
14//!   capture(b)
15//! };
16//! main = || i32 { pickFirst(42i32, 62i32) };
17//! "#;
18//!
19//! let mut tin = tin::Tin::new();
20//! tin.load("main.tn", source)?;
21//!
22//! /*
23//! let mut module = tin.compile()?;
24//! let main = module.function::<tin::module::Function0<i32>>("main").unwrap();
25//!
26//! let result = main();
27//! assert_eq!(42, result);
28//! */
29//! # Ok(())
30//! # }
31//! ```
32#![deny(nonstandard_style, warnings, unused)]
33#![deny(
34    missing_docs,
35    missing_debug_implementations,
36    missing_copy_implementations,
37    trivial_casts,
38    trivial_numeric_casts,
39    unstable_features,
40    unused_import_braces,
41    unused_qualifications
42)]
43#![cfg_attr(feature = "cargo-clippy", deny(clippy::all, clippy::pedantic))]
44
45#[macro_use]
46extern crate enum_primitive_derive;
47#[macro_use]
48extern crate failure;
49#[macro_use]
50extern crate lalrpop_util;
51#[macro_use]
52extern crate lazy_static;
53#[macro_use]
54extern crate log;
55#[macro_use]
56extern crate specs_derive;
57#[macro_use]
58extern crate specs_visitor_derive;
59#[cfg(test)]
60#[macro_use]
61extern crate pretty_assertions;
62
63use std::fmt;
64
65mod ast;
66mod codegen;
67mod interpreter;
68mod ir;
69mod parser;
70mod value;
71
72#[cfg(test)]
73mod test_util;
74
75pub mod diagnostic;
76pub mod error;
77pub mod graph;
78pub mod module;
79
80pub use crate::error::Error;
81pub use crate::error::Result;
82
83/// An instance of the Tin runtime.
84pub struct Tin {
85    ir: ir::Ir,
86    codemap: codespan::CodeMap,
87    parser: <ast::Module<parser::Context> as parser::Parse>::Parser,
88}
89
90impl Tin {
91    /// Creates a new instance of the Tin runtime.
92    pub fn new() -> Tin {
93        use crate::parser::Parse;
94
95        let ir = ir::Ir::new();
96        let codemap = codespan::CodeMap::new();
97        let parser = ast::Module::new_parser();
98
99        Tin {
100            ir,
101            codemap,
102            parser,
103        }
104    }
105
106    /// Loads the specified source code as a module.
107    ///
108    /// # Errors
109    ///
110    /// This function will return an error if the source code contains a syntax error or if the
111    /// resulting logical structure has semantic errors or type errors.
112    ///
113    /// Calling this function several times will load source code into the same module scope, but
114    /// references in code from earlier calls will not be able to refer to definitions from code
115    /// from later calls.  Any references will eagerly be resolved and fail early.
116    ///
117    /// # Examples
118    ///
119    /// Loading a very basic module:
120    ///
121    /// ```
122    /// # extern crate failure;
123    /// # extern crate tin;
124    /// # fn main() -> Result<(), failure::Error> {
125    /// let mut tin = tin::Tin::new();
126    /// tin.load("main.tn", "main = || i32 { 42i32 };")?;
127    /// # Ok(())
128    /// # }
129    /// ```
130    ///
131    /// Unresolved references are not allowed:
132    ///
133    /// ```
134    /// # extern crate failure;
135    /// # extern crate tin;
136    /// # fn main() -> Result<(), failure::Error> {
137    /// let mut tin = tin::Tin::new();
138    /// let result = tin.load("main.tn", "main = || i32 { a };");
139    /// assert!(result.is_err());
140    /// # Ok(())
141    /// # }
142    /// ```
143    pub fn load<F>(&mut self, file_name: F, source: &str) -> Result<()>
144    where
145        F: Into<codespan::FileName>,
146    {
147        let span = self
148            .codemap
149            .add_filemap(file_name.into(), source.to_owned())
150            .span();
151        let module = parser::Parser::parse(&mut self.parser, span, source)?;
152        self.ir.load(&module)?;
153
154        Ok(())
155    }
156
157    /// Creates a graph representation of the current IR of this Tin instance.
158    ///
159    /// This can be used to for example visualize the code using GraphViz or other tools.
160    pub fn graph(&self) -> graph::Graph {
161        graph::Graph::new(&self.ir)
162    }
163
164    /// Compiles the code loaded so far into a stand-alone module.
165    ///
166    /// This module is detached from the runtime and can be used even after the runtime has been
167    /// dropped.  Once all required Tin code has been loaded, it is therefore recommended to drop
168    /// this instance and only keep the compiled module around.
169    ///
170    /// # Examples
171    ///
172    /// Compiling a very basic module:
173    ///
174    /// ```
175    /// # extern crate failure;
176    /// # extern crate tin;
177    /// # fn main() -> Result<(), failure::Error> {
178    /// let mut tin = tin::Tin::new();
179    /// tin.load("main.tn", "main = || i32 { 42i32 };")?;
180    ///
181    /// let mut module = tin.compile()?;
182    /// let main = module.function::<tin::module::Function0<i32>>("main").unwrap();
183    ///
184    /// let result = main.call()?;
185    /// assert_eq!(42, result);
186    /// # Ok(())
187    /// # }
188    /// ```
189    pub fn compile(&mut self) -> Result<module::Module> {
190        self.ir.check_types()?;
191        let module = codegen::Codegen::new(&self.ir, &self.codemap).compile();
192        Ok(module)
193    }
194
195    /// Returns a reference to the current code map, which contains location mapping for all source
196    /// code loaded so far.
197    pub fn codemap(&self) -> &codespan::CodeMap {
198        &self.codemap
199    }
200}
201
202impl Default for Tin {
203    fn default() -> Self {
204        Tin::new()
205    }
206}
207
208impl fmt::Debug for Tin {
209    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210        f.debug_struct("Tin").finish()
211    }
212}