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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! Tin is a simple embeddable programming language.
//!
//! The aim is to have a very light-weight footprint and a simple API.
//!
//! # Examples
//!
//! ```
//! # extern crate failure;
//! # extern crate tin_lang;
//! # fn main() -> Result<(), failure::Error> {
//! let source = r#"
//! Int = 0u32;
//! pickFirst = |a: Int, b: Int| Int {
//!   capture = |x: Int| Int { a };
//!   capture(b)
//! };
//! main = || Int { pickFirst(42u32, 62u32) };
//! "#;
//!
//! let mut tin = tin_lang::Tin::new();
//! tin.load(source)?;
//!
//! /*
//! let mut module = tin.compile()?;
//! let main = module.function::<tin_lang::module::Function0<u32>>("main").unwrap();
//!
//! let result = main();
//! assert_eq!(42, result);
//! */
//! # Ok(())
//! # }
//! ```
#![deny(nonstandard_style, warnings, unused)]
#![deny(
    missing_docs,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unstable_features,
    unused_import_braces,
    unused_qualifications
)]
#![cfg_attr(feature = "cargo-clippy", deny(clippy::all, clippy::pedantic))]

extern crate cranelift;
extern crate cranelift_module;
extern crate cranelift_simplejit;
extern crate dot;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate lalrpop_util;
#[macro_use]
extern crate log;
extern crate specs;
#[macro_use]
extern crate specs_derive;
extern crate specs_visitor;
#[macro_use]
extern crate specs_visitor_derive;
#[cfg(test)]
extern crate env_logger;

use std::fmt;

mod ast;
mod codegen;
mod ir;
mod parser;
#[cfg(test)]
mod test_util;

pub mod error;
pub mod graph;
pub mod module;

pub use error::Error;
pub use error::Result;

/// An instance of the Tin runtime.
pub struct Tin {
    ir: ir::Ir,
    parser: <ast::Module<parser::Context> as parser::Parse>::Parser,
}

impl Tin {
    /// Creates a new instance of the Tin runtime.
    pub fn new() -> Tin {
        use parser::Parse;

        let ir = ir::Ir::new();
        let parser = ast::Module::new_parser();

        Tin { ir, parser }
    }

    /// Loads the specified source code as a module.
    ///
    /// # Errors
    ///
    /// This function will return an error if the source code contains a syntax error or if the
    /// resulting logical structure has semantic errors or type errors.
    ///
    /// Calling this function several times will load source code into the same module scope, but
    /// references in code from earlier calls will not be able to refer to definitions from code
    /// from later calls.  Any references will eagerly be resolved and fail early.
    ///
    /// # Examples
    ///
    /// Loading a very basic module:
    ///
    /// ```
    /// # extern crate failure;
    /// # extern crate tin_lang;
    /// # fn main() -> Result<(), failure::Error> {
    /// let mut tin = tin_lang::Tin::new();
    /// tin.load("U32 = 0u32; main = || U32 { 42u32 };")?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// Unresolved references are not allowed:
    ///
    /// ```
    /// # extern crate failure;
    /// # extern crate tin_lang;
    /// # fn main() -> Result<(), failure::Error> {
    /// let mut tin = tin_lang::Tin::new();
    /// let result = tin.load("U32 = 0u32; main = || U32 { a };");
    /// assert!(result.is_err());
    /// # Ok(())
    /// # }
    /// ```
    pub fn load(&mut self, source: &str) -> Result<()> {
        let module = parser::Parser::parse(&mut self.parser, source)?;
        self.ir.load(&module)?;

        Ok(())
    }

    /// Creates a graph representation of the current IR of this Tin instance.
    ///
    /// This can be used to for example visualize the code using GraphViz or other tools.
    pub fn graph(&self) -> graph::Graph {
        graph::Graph::new(&self.ir)
    }

    /// Compiles the code loaded so far into a stand-alone module.
    ///
    /// This module is detached from the runtime and can be used even after the runtime has been
    /// dropped.  Once all required Tin code has been loaded, it is therefore recommended to drop
    /// this instance and only keep the compiled module around.
    ///
    /// # Examples
    ///
    /// Compiling a very basic module:
    ///
    /// ```
    /// # extern crate failure;
    /// # extern crate tin_lang;
    /// # fn main() -> Result<(), failure::Error> {
    /// let mut tin = tin_lang::Tin::new();
    /// tin.load("U32 = 0u32; main = || U32 { 42u32 };")?;
    ///
    /// let mut module = tin.compile()?;
    /// let main = module.function::<tin_lang::module::Function0<u32>>("main").unwrap();
    ///
    /// let result = main();
    /// assert_eq!(42, result);
    /// # Ok(())
    /// # }
    /// ```
    pub fn compile(&mut self) -> Result<module::Module> {
        self.ir.check_types();
        let module = codegen::Codegen::new(&self.ir).compile();
        Ok(module)
    }
}

impl Default for Tin {
    fn default() -> Self {
        Tin::new()
    }
}

impl fmt::Debug for Tin {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Tin").finish()
    }
}