1use std::cell::RefCell;
17use std::collections::{BTreeSet, HashMap};
18use std::error::Error;
19use std::path::PathBuf;
20use std::process;
21use std::rc::Rc;
22
23use atty;
24use atty::Stream;
25use rustyline;
26use rustyline::error::ReadlineError;
27use simple_error;
28
29use crate::ast::*;
30use crate::build::opcode::pointer::OpPointer;
31use crate::build::opcode::translate;
32use crate::build::opcode::translate::OpsMap;
33use crate::build::opcode::Environment;
34use crate::build::opcode::VM;
35use crate::iter::OffsetStrIter;
36use crate::parse::parse;
37
38pub mod format;
39pub mod ir;
40pub mod opcode;
41pub mod scope;
42
43pub mod stdlib;
44
45pub use self::ir::Val;
46
47type BuildResult = Result<(), Box<dyn Error>>;
49
50pub struct AssertCollector {
52 pub counter: i32,
53 pub success: bool,
54 pub summary: String,
55 pub failures: String,
56}
57
58impl AssertCollector {
59 pub fn new() -> Self {
60 Self {
61 counter: 0,
62 success: true,
63 summary: String::new(),
64 failures: String::new(),
65 }
66 }
67
68 fn record_assert_result(&mut self, msg: &str, is_success: bool) {
69 if !is_success {
70 let msg = format!("{} - NOT OK: {}\n", self.counter, msg);
71 self.summary.push_str(&msg);
72 self.failures.push_str(&msg);
73 self.success = false;
74 } else {
75 let msg = format!("{} - OK: {}\n", self.counter, msg);
76 self.summary.push_str(&msg);
77 }
78 self.counter += 1;
79 }
80}
81
82pub struct FileBuilder<'a, Stdout, Stderr>
85where
86 Stdout: std::io::Write + Clone,
87 Stderr: std::io::Write + Clone,
88{
89 pub environment: &'a RefCell<Environment<Stdout, Stderr>>,
90 working_dir: PathBuf,
91 strict: bool,
92 std: Rc<HashMap<String, &'static str>>,
93 import_path: &'a Vec<PathBuf>,
94 pub last: Option<Rc<Val>>,
95 pub out: Option<Rc<Val>>,
96 validate_mode: bool,
97}
98
99impl<'a, Stdout, Stderr> FileBuilder<'a, Stdout, Stderr>
100where
101 Stdout: std::io::Write + Clone,
102 Stderr: std::io::Write + Clone,
103{
104 pub fn new<P: Into<PathBuf>>(
106 working_dir: P,
107 import_paths: &'a Vec<PathBuf>,
108 environment: &'a RefCell<Environment<Stdout, Stderr>>,
109 ) -> Self {
110 FileBuilder {
111 environment: environment,
112 strict: false,
113 working_dir: working_dir.into(),
115 std: Rc::new(stdlib::get_libs()),
116 import_path: import_paths,
117 out: None,
118 last: None,
119 validate_mode: false,
120 }
121 }
122
123 pub fn clone_builder(&self) -> Self {
124 FileBuilder {
125 environment: self.environment,
126 strict: self.strict,
127 working_dir: self.working_dir.clone(),
128 std: self.std.clone(),
129 import_path: self.import_path,
130 out: None,
131 last: None,
132 validate_mode: self.validate_mode,
133 }
134 }
135
136 pub fn set_strict(&mut self, strict: bool) {
137 self.strict = strict;
138 }
139
140 pub fn build<P: Into<PathBuf>>(&mut self, file: P) -> BuildResult {
142 let file = file.into();
143 self.working_dir = file.parent().unwrap().to_path_buf();
144 let ptr = self.environment.borrow_mut().get_ops_for_path(&file)?;
145 let eval_result = self.eval_ops(ptr, Some(file.clone()));
146 match eval_result {
147 Ok(_) => {
148 self.last = self.out.clone();
149 Ok(())
150 }
151 Err(e) => {
152 let err = simple_error::SimpleError::new(&format!(
153 "Error building file: {}\n{}",
154 file.to_string_lossy(),
155 e.as_ref()
156 ));
157 Err(Box::new(err))
158 }
159 }
160 }
161
162 pub fn enable_validate_mode(&mut self) {
167 self.validate_mode = true;
168 }
169
170 fn link_ops(&self, ops: &OpPointer) -> BuildResult {
171 let mut links = Vec::new();
172 for (link, pos) in &ops.pos_map.links {
173 links.push((link.clone(), pos.clone()));
174 }
175 let mut found = BTreeSet::new();
176 loop {
177 let (link, path_pos) = match links.pop() {
178 Some(t) => t,
179 None => break,
180 };
181 if found.contains(&link) {
182 continue;
183 }
184 let ops = match self
185 .environment
186 .borrow_mut()
187 .get_ops_for_path(link.as_ref())
188 {
189 Ok(ops) => ops,
190 Err(e) => return Err(Box::new(e.with_pos(path_pos))),
191 };
192 found.insert(link);
193 for (link, pos) in &ops.pos_map.links {
194 links.push((link.clone(), pos.clone()));
195 }
196 }
197 Ok(())
198 }
199
200 fn eval_ops(&mut self, ops: OpPointer, path: Option<PathBuf>) -> BuildResult {
201 self.link_ops(&ops)?;
202 let mut vm = VM::with_pointer(self.strict, ops, &self.working_dir);
203 if path.is_some() {
204 vm.set_path(path.unwrap());
205 }
206 if self.validate_mode {
207 vm.enable_validate_mode();
208 }
209 vm.run(self.environment)?;
210 self.out = Some(Rc::new(vm.symbols_to_tuple(false).into()));
211 Ok(())
212 }
213
214 pub fn eval_stmts(&mut self, ast: Vec<Statement>, path: Option<PathBuf>) -> BuildResult {
216 let ops = translate::AST::translate(ast, &self.working_dir);
218 self.eval_ops(OpPointer::new(Rc::new(ops)), path)
219 }
220
221 pub fn repl(&mut self, mut editor: rustyline::Editor<()>, config_home: PathBuf) -> BuildResult {
222 let mut lines = crate::io::StatementAccumulator::new();
224 if atty::is(Stream::Stdin) {
225 println!("Welcome to the UCG repl. Ctrl-D to exit, Ctrl-C to abort expression.");
226 println!("Type '#help' for help.");
227 println!("");
228 }
229 let mut vm = VM::new(self.strict, Rc::new(OpsMap::new()), &self.working_dir);
231 loop {
232 let line = match editor.readline(&format!("{}> ", lines.next_line())) {
234 Ok(l) => l,
235 Err(e) => {
236 if let ReadlineError::Eof = e {
237 eprintln!("Recieved EOF Exiting...");
238 process::exit(0);
239 }
240 if let ReadlineError::Interrupted = e {
241 eprintln!("Interrupted!");
243 lines.reset();
244 continue;
245 }
246 eprintln!("Error: {}", e);
247 process::exit(1);
248 }
249 };
250 let trimmed = line.trim();
252 if trimmed.starts_with("#") {
253 if trimmed.starts_with("#help") {
255 println!(include_str!("../help/repl.txt"));
256 } else if trimmed.starts_with("#del") {
257 let args: Vec<&str> = trimmed.split(" ").skip(1).collect();
259 if args.len() != 1 {
260 eprintln!("The '#del' command expects a single argument specifying \nthe binding to delete.");
262 } else {
263 let key = args[0].to_string();
264 if let None = vm.remove_symbol(&key) {
265 eprintln!("No such binding {}", key);
266 }
267 }
268 } else if trimmed.starts_with("#exit") {
269 process::exit(0);
270 } else {
271 eprintln!("Invalid repl command...");
272 eprintln!("");
273 println!(include_str!("../help/repl.txt"));
274 }
275 continue;
276 }
277 lines.push(line);
278 loop {
280 if let Some(stmt) = lines.get_statement() {
282 let stmts = parse(OffsetStrIter::new(&stmt), None)?;
285 let ops = translate::AST::translate(stmts, &self.working_dir);
286 vm = vm.to_new_pointer(OpPointer::new(Rc::new(ops)));
287 match vm.run(self.environment) {
288 Err(e) => eprintln!("{}", e),
290 Ok(_) => {
291 match vm.last {
292 Some((ref val, _)) => {
293 println!("{}", val);
294 vm.last = None;
295 }
296 None => {}
297 }
298 editor.history_mut().add(stmt);
299 editor.save_history(&config_home)?;
300 }
301 }
302 break;
304 }
305 lines.push(editor.readline(&format!("{}> ", lines.next_line()))?);
307 }
308 }
309 }
310
311 pub fn eval_input(
312 &mut self,
313 input: OffsetStrIter,
314 path: Option<PathBuf>,
315 ) -> Result<Rc<Val>, Box<dyn Error>> {
316 match parse(input.clone(), None) {
317 Ok(stmts) => {
318 self.eval_stmts(stmts, path)?;
319 if let Some(v) = self.out.clone() {
320 return Ok(v);
321 }
322 unreachable!();
323 }
324 Err(err) => Err(Box::new(err)),
325 }
326 }
327
328 pub fn eval_string(&mut self, input: &str) -> Result<Rc<Val>, Box<dyn Error>> {
330 self.eval_input(OffsetStrIter::new(input), None)
331 }
332
333 pub fn eval_expr(&mut self, expr: Expression) -> Result<Rc<Val>, Box<dyn Error>> {
334 let ops_map =
335 translate::AST::translate(vec![Statement::Expression(expr)], &self.working_dir);
336 self.eval_ops(
337 OpPointer::new(Rc::new(ops_map)),
338 Some(self.working_dir.clone()),
339 )?;
340 if let Some(val) = &self.last {
341 return Ok(val.clone());
342 }
343 unreachable!();
344 }
345
346 pub fn get_out_by_name(&self, name: &str) -> Option<Rc<Val>> {
347 if let Some(val) = self.out.clone() {
348 if let &Val::Tuple(ref flds) = val.as_ref() {
349 for (k, v) in flds.iter() {
350 if k.as_ref() == name {
351 return Some(v.clone());
352 }
353 }
354 }
355 }
356 return None;
357 }
358
359 pub fn assert_results(&self) -> bool {
360 self.environment.borrow().assert_results.success
361 }
362
363 pub fn assert_summary(&self) -> String {
364 self.environment.borrow().assert_results.summary.clone()
365 }
366}
367
368#[cfg(test)]
369mod compile_test;
370
371#[cfg(test)]
372mod test;