teko/
interpret.rs

1//! Evaluation and library functions.
2//!
3//! ```
4//! extern crate teko;
5//! extern crate num_traits;
6//! use num_traits::cast::ToPrimitive;
7//! fn main() {
8//! 	let program = teko::parse::parse_string("(+ 1 2 4) (+ 1 2)").ok().unwrap();
9//! 	let env = teko::interpret::interpret(program);
10//! 	match env.result.1 {
11//! 		teko::data_structures::Coredata::Integer(ref value) => {
12//! 			assert_eq![value.to_i32().unwrap(), 3];
13//! 		}
14//! 		_ => {
15//! 			panic!["Expected Integer but got a different data type"];
16//! 		}
17//! 	}
18//! }
19//! ```
20use builtins::*;
21use data_structures::{Boolean, Commands, Env, Program, Sourcedata, Coredata, Macro, Function};
22use super::VEC_CAPACITY;
23use utilities::*;
24
25use num::BigInt;
26
27/// Evaluates a program with a given environment.
28///
29/// The `program` is considered completely evaluated when it is empty. The result of the program
30/// is stored in `env.result`. This function is mainly used to evaluate a program in some
31/// environment context.
32///
33/// ```
34/// extern crate teko;
35/// extern crate num_traits;
36/// use num_traits::cast::ToPrimitive;
37/// fn main() {
38/// 	let program = teko::parse::parse_string("(+ 1 2 4) (+ 1 2)").ok().unwrap();
39/// 	let env = teko::interpret::initialize_environment_with_standard_library();
40/// 	let env = teko::interpret::eval(program, env);
41/// 	match env.result.1 {
42/// 		teko::data_structures::Coredata::Integer(ref value) => {
43/// 			assert_eq![value.to_i32().unwrap(), 3];
44/// 		}
45/// 		_ => {
46/// 			panic!["Expected Integer but got a different data type"];
47/// 		}
48/// 	}
49/// }
50/// ```
51pub fn eval(mut program: Program, mut env: Env) -> Env {
52	macro_rules! ppush {
53		($source:expr, $data:expr,) => { ppush![$source, $data] };
54		($source:expr, $data:expr) => {
55			program.push(rc(Sourcedata($source.clone(), $data)))
56		};
57	}
58	while let Some(top) = program.pop() {
59		match *top {
60			// Source refers to the head of the pair from which the call originated
61			Sourcedata(ref source, Coredata::Internal(Commands::Call(ref statement))) => {
62				match **statement {
63					Sourcedata(_, Coredata::Function(Function::Builtin(ref transfer, ..))) => {
64						let error = transfer(&mut program, &mut env);
65						env.params.pop();
66						err(source, &error, &mut program, &mut env);
67					}
68					Sourcedata(_,
69					           Coredata::Function(Function::Library(ref parameters,
70					                                                ref transfer))) => {
71						if let Some(arguments) = env.params.pop() {
72							if arguments.len() != parameters.len() {
73								err(
74									source,
75									&Some(format![
76										"expected {} but got {} arguments",
77										parameters.len(),
78										arguments.len(),
79									]),
80									&mut program,
81									&mut env,
82								);
83							} else {
84								let cmd = Commands::Deparameterize(
85									optimize_tail_call(&mut program, &mut env, parameters),
86								);
87								ppush![source, Coredata::Internal(cmd)];
88								for (counter, parameter) in parameters.iter().enumerate() {
89									if env.store.contains_key(parameter) {
90										env.store.get_mut(parameter).unwrap().push(
91											arguments[counter]
92												.clone(),
93										);
94									} else {
95										env.store.insert(
96											parameter.clone(),
97											vec![arguments[counter].clone()],
98										);
99									}
100								}
101								program.extend(transfer.iter().cloned());
102							}
103						}
104					}
105					_ => {
106						err(
107							source,
108							&Some("element not callable".into()),
109							&mut program,
110							&mut env,
111						);
112					}
113				}
114			}
115			Sourcedata(_, Coredata::Internal(Commands::Deparameterize(ref arguments))) => {
116				pop_parameters(&mut program, &mut env, arguments);
117			}
118			Sourcedata(_, Coredata::Internal(Commands::Evaluate)) => {
119				program.push(env.result.clone());
120			}
121			Sourcedata(_, Coredata::Internal(Commands::If(ref first, ref second))) => {
122				if let Coredata::Boolean(Boolean::False) = env.result.1 {
123					program.push(second.clone());
124				} else {
125					program.push(first.clone());
126				}
127			}
128			Sourcedata(_, Coredata::Internal(Commands::Parameterize)) => {
129				let condition = if let Some(ref mut last) = env.params.last_mut() {
130					last.push(env.result.clone());
131					None
132				} else {
133					Some("parameter stack nonexistent".into())
134				};
135				err(&None, &condition, &mut program, &mut env);
136			}
137			// Source here is the HEAD of a pair, so (a b) has source of a, and (((a)) b) has source
138			// of ((a))
139			Sourcedata(ref source, Coredata::Internal(Commands::Prepare(ref arguments))) => {
140				match *env.result.clone() {
141					Sourcedata(_, Coredata::Function(..)) => {
142						env.params.push(vec![]);
143						ppush![
144							source,
145							Coredata::Internal(Commands::Call(env.result.clone())),
146						];
147						for argument in collect_pair_into_vec(arguments) {
148							ppush![None, Coredata::Internal(Commands::Parameterize)];
149							program.push(argument.clone());
150						}
151					}
152					Sourcedata(_, Coredata::Macro(Macro::Builtin(ref transfer, ..))) => {
153						env.result = arguments.clone();
154						let error = transfer(&mut program, &mut env);
155						err(source, &error, &mut program, &mut env);
156					}
157					Sourcedata(_, Coredata::Macro(Macro::Library(ref bound, ref code))) => {
158						ppush![None, Coredata::Internal(Commands::Evaluate)];
159						let command = optimize_tail_call(&mut program, &mut env, &[bound.clone()]);
160						if env.store.contains_key(bound) {
161							env.store.get_mut(bound).unwrap().push(arguments.clone());
162						} else {
163							env.store.insert(bound.clone(), vec![arguments.clone()]);
164						}
165						ppush![
166							source,
167							Coredata::Internal(Commands::Deparameterize(command)),
168						];
169						program.extend(code.iter().cloned());
170					}
171					_ => {
172						err(
173							source,
174							&Some("element not callable".into()),
175							&mut program,
176							&mut env,
177						);
178					}
179				}
180			}
181			Sourcedata(_, Coredata::Internal(Commands::Wind)) => {}
182			Sourcedata(_, Coredata::Pair(ref head, ref tail)) => {
183				ppush![head.0, Coredata::Internal(Commands::Prepare(tail.clone()))];
184				program.push(head.clone());
185			}
186			Sourcedata(ref source, Coredata::Symbol(ref string)) => {
187				if let Some(number) = BigInt::parse_bytes(string.as_bytes(), 10) {
188					env.result = rc(Sourcedata(source.clone(), Coredata::Integer(number)));
189				} else {
190					let error = if let Some(value) = env.store.get(string) {
191						if let Some(value) = value.last() {
192							env.result = value.clone();
193							None
194						} else {
195							Some(format!["variable not found: {}", string])
196						}
197					} else {
198						Some(format!["variable not found: {}", string])
199					};
200					err(source, &error, &mut program, &mut env);
201				}
202			}
203			_ => {
204				env.result = top.clone();
205			}
206		}
207	}
208	env
209}
210
211/// Initializes the environment with the standard library.
212///
213/// ```
214/// extern crate teko;
215/// let _: teko::data_structures::Env =
216/// 	teko::interpret::initialize_environment_with_standard_library();
217/// ```
218pub fn initialize_environment_with_standard_library() -> Env {
219	Env {
220		store: create_builtin_library_table(),
221		params: Vec::with_capacity(VEC_CAPACITY),
222		result: rc(Sourcedata(None, Coredata::Null)),
223	}
224}
225
226/// Sets up a standard environment and evaluate the program.
227///
228/// Used to evaluate a program with the standard library and all builtins.
229///
230/// ```
231/// extern crate teko;
232/// extern crate num_traits;
233/// use num_traits::cast::ToPrimitive;
234/// fn main() {
235/// 	let program = teko::parse::parse_string("(+ 1 2 4) (+ 1 2)").ok().unwrap();
236/// 	let env = teko::interpret::interpret(program);
237/// 	match env.result.1 {
238/// 		teko::data_structures::Coredata::Integer(ref value) => {
239/// 			assert_eq![value.to_i32().unwrap(), 3];
240/// 		}
241/// 		_ => {
242/// 			panic!["Expected Integer but got a different data type"];
243/// 		}
244/// 	}
245/// }
246/// ```
247pub fn interpret(program: Program) -> Env {
248	let env = initialize_environment_with_standard_library();
249	eval(program, env)
250}
251
252#[cfg(test)]
253mod tests {
254	use super::*;
255	use parse::parse_file;
256	#[test]
257	fn test_interpreter() {
258		let p = parse_file("examples/basic.tko").ok().unwrap();
259		interpret(p);
260	}
261}