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}