]> git.lizzy.rs Git - rust.git/blob - src/librusti/rusti.rs
Change 'print(fmt!(...))' to printf!/printfln! in src/lib*
[rust.git] / src / librusti / rusti.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /*!
12  * rusti - A REPL using the JIT backend
13  *
14  * Rusti works by serializing state between lines of input. This means that each
15  * line can be run in a separate task, and the only limiting factor is that all
16  * local bound variables are encodable.
17  *
18  * This is accomplished by feeding in generated input to rustc for execution in
19  * the JIT compiler. Currently input actually gets fed in three times to get
20  * information about the program.
21  *
22  * - Pass #1
23  *   In this pass, the input is simply thrown at the parser and the input comes
24  *   back. This validates the structure of the program, and at this stage the
25  *   global items (fns, structs, impls, traits, etc.) are filtered from the
26  *   input into the "global namespace". These declarations shadow all previous
27  *   declarations of an item by the same name.
28  *
29  * - Pass #2
30  *   After items have been stripped, the remaining input is passed to rustc
31  *   along with all local variables declared (initialized to nothing). This pass
32  *   runs up to typechecking. From this, we can learn about the types of each
33  *   bound variable, what variables are bound, and also ensure that all the
34  *   types are encodable (the input can actually be run).
35  *
36  * - Pass #3
37  *   Finally, a program is generated to deserialize the local variable state,
38  *   run the code input, and then reserialize all bindings back into a local
39  *   hash map. This code is then run in the JIT engine provided by the rust
40  *   compiler.
41  *
42  * - Pass #4
43  *   Once this code runs, the input has fully been run and the hash map of local
44  *   variables from TLS is read back into the local store of variables. This is
45  *   then used later to pass back along to the parent rusti task and then begin
46  *   waiting for input again.
47  *
48  * - Pass #5
49  *   When running rusti code, it's important to consume ownership of the LLVM
50  *   jit contextual information to prevent code from being deallocated too soon
51  *   (before drop glue runs, see #7732). For this reason, the jit context is
52  *   consumed and also passed along to the parent task. The parent task then
53  *   keeps around all contexts while rusti is running. This must be done because
54  *   tasks could in theory be spawned off and running in the background (still
55  *   using the code).
56  *
57  * Encoding/decoding is done with EBML, and there is simply a map of ~str ->
58  * ~[u8] maintaining the values of each local binding (by name).
59  */
60
61 #[link(name = "rusti",
62        vers = "0.8-pre",
63        uuid = "7fb5bf52-7d45-4fee-8325-5ad3311149fc",
64        url = "https://github.com/mozilla/rust/tree/master/src/rusti")];
65
66 #[license = "MIT/ASL2"];
67 #[crate_type = "lib"];
68
69 extern mod extra;
70 extern mod rustc;
71 extern mod syntax;
72
73 use std::{libc, io, os, task};
74 use std::cell::Cell;
75 use extra::rl;
76
77 use rustc::driver::{driver, session};
78 use rustc::back::link::jit;
79 use syntax::{ast, diagnostic};
80 use syntax::ast_util::*;
81 use syntax::parse::token;
82 use syntax::print::pprust;
83
84 use program::Program;
85 use utils::*;
86
87 mod program;
88 pub mod utils;
89
90 /**
91  * A structure shared across REPL instances for storing history
92  * such as statements and view items. I wish the AST was sendable.
93  */
94 pub struct Repl {
95     prompt: ~str,
96     binary: ~str,
97     running: bool,
98     lib_search_paths: ~[~str],
99     engines: ~[~jit::Engine],
100
101     program: ~Program,
102 }
103
104 // Action to do after reading a :command
105 enum CmdAction {
106     action_none,
107     action_run_line(~str),
108 }
109
110 /// Run an input string in a Repl, returning the new Repl.
111 fn run(mut program: ~Program, binary: ~str, lib_search_paths: ~[~str],
112        input: ~str) -> (~Program, Option<~jit::Engine>)
113 {
114     // Build some necessary rustc boilerplate for compiling things
115     let binary = binary.to_managed();
116     let options = @session::options {
117         crate_type: session::unknown_crate,
118         binary: binary,
119         addl_lib_search_paths: @mut lib_search_paths.map(|p| Path(*p)),
120         jit: true,
121         .. (*session::basic_options()).clone()
122     };
123     // Because we assume that everything is encodable (and assert so), add some
124     // extra helpful information if the error crops up. Otherwise people are
125     // bound to be very confused when they find out code is running that they
126     // never typed in...
127     let sess = driver::build_session(options, |cm, msg, lvl| {
128         diagnostic::emit(cm, msg, lvl);
129         if msg.contains("failed to find an implementation of trait") &&
130            msg.contains("extra::serialize::Encodable") {
131             diagnostic::emit(cm,
132                              "Currrently rusti serializes bound locals between \
133                               different lines of input. This means that all \
134                               values of local variables need to be encodable, \
135                               and this type isn't encodable",
136                              diagnostic::note);
137         }
138     });
139     let intr = token::get_ident_interner();
140
141     //
142     // Stage 1: parse the input and filter it into the program (as necessary)
143     //
144     debug!("parsing: %s", input);
145     let crate = parse_input(sess, binary, input);
146     let mut to_run = ~[];       // statements to run (emitted back into code)
147     let new_locals = @mut ~[];  // new locals being defined
148     let mut result = None;      // resultant expression (to print via pp)
149     do find_main(crate, sess) |blk| {
150         // Fish out all the view items, be sure to record 'extern mod' items
151         // differently beause they must appear before all 'use' statements
152         for blk.view_items.iter().advance |vi| {
153             let s = do with_pp(intr) |pp, _| {
154                 pprust::print_view_item(pp, vi);
155             };
156             match vi.node {
157                 ast::view_item_extern_mod(*) => {
158                     program.record_extern(s);
159                 }
160                 ast::view_item_use(*) => { program.record_view_item(s); }
161             }
162         }
163
164         // Iterate through all of the block's statements, inserting them into
165         // the correct portions of the program
166         for blk.stmts.iter().advance |stmt| {
167             let s = do with_pp(intr) |pp, _| { pprust::print_stmt(pp, *stmt); };
168             match stmt.node {
169                 ast::stmt_decl(d, _) => {
170                     match d.node {
171                         ast::decl_item(it) => {
172                             let name = sess.str_of(it.ident);
173                             match it.node {
174                                 // Structs are treated specially because to make
175                                 // them at all usable they need to be decorated
176                                 // with #[deriving(Encoable, Decodable)]
177                                 ast::item_struct(*) => {
178                                     program.record_struct(name, s);
179                                 }
180                                 // Item declarations are hoisted out of main()
181                                 _ => { program.record_item(name, s); }
182                             }
183                         }
184
185                         // Local declarations must be specially dealt with,
186                         // record all local declarations for use later on
187                         ast::decl_local(l) => {
188                             let mutbl = l.is_mutbl;
189                             do each_binding(l) |path, _| {
190                                 let s = do with_pp(intr) |pp, _| {
191                                     pprust::print_path(pp, path, false);
192                                 };
193                                 new_locals.push((s, mutbl));
194                             }
195                             to_run.push(s);
196                         }
197                     }
198                 }
199
200                 // run statements with expressions (they have effects)
201                 ast::stmt_mac(*) | ast::stmt_semi(*) | ast::stmt_expr(*) => {
202                     to_run.push(s);
203                 }
204             }
205         }
206         result = do blk.expr.map_consume |e| {
207             do with_pp(intr) |pp, _| { pprust::print_expr(pp, e); }
208         };
209     }
210     // return fast for empty inputs
211     if to_run.len() == 0 && result.is_none() {
212         return (program, None);
213     }
214
215     //
216     // Stage 2: run everything up to typeck to learn the types of the new
217     //          variables introduced into the program
218     //
219     info!("Learning about the new types in the program");
220     program.set_cache(); // before register_new_vars (which changes them)
221     let input = to_run.connect("\n");
222     let test = program.test_code(input, &result, *new_locals);
223     debug!("testing with ^^^^^^ %?", (||{ println(test) })());
224     let dinput = driver::str_input(test.to_managed());
225     let cfg = driver::build_configuration(sess, binary, &dinput);
226     let outputs = driver::build_output_filenames(&dinput, &None, &None, [], sess);
227     let (crate, tcx) = driver::compile_upto(sess, cfg.clone(), &dinput,
228                                             driver::cu_typeck, Some(outputs));
229     // Once we're typechecked, record the types of all local variables defined
230     // in this input
231     do find_main(crate.expect("crate after cu_typeck"), sess) |blk| {
232         program.register_new_vars(blk, tcx.expect("tcx after cu_typeck"));
233     }
234
235     //
236     // Stage 3: Actually run the code in the JIT
237     //
238     info!("actually running code");
239     let code = program.code(input, &result);
240     debug!("actually running ^^^^^^ %?", (||{ println(code) })());
241     let input = driver::str_input(code.to_managed());
242     let cfg = driver::build_configuration(sess, binary, &input);
243     let outputs = driver::build_output_filenames(&input, &None, &None, [], sess);
244     let sess = driver::build_session(options, diagnostic::emit);
245     driver::compile_upto(sess, cfg, &input, driver::cu_everything,
246                          Some(outputs));
247
248     //
249     // Stage 4: Inform the program that computation is done so it can update all
250     //          local variable bindings.
251     //
252     info!("cleaning up after code");
253     program.consume_cache();
254
255     //
256     // Stage 5: Extract the LLVM execution engine to take ownership of the
257     //          generated JIT code. This means that rusti can spawn parallel
258     //          tasks and we won't deallocate the code emitted until rusti
259     //          itself is destroyed.
260     //
261     return (program, jit::consume_engine());
262
263     fn parse_input(sess: session::Session, binary: @str,
264                    input: &str) -> @ast::Crate {
265         let code = fmt!("fn main() {\n %s \n}", input);
266         let input = driver::str_input(code.to_managed());
267         let cfg = driver::build_configuration(sess, binary, &input);
268         let outputs = driver::build_output_filenames(&input, &None, &None, [], sess);
269         let (crate, _) = driver::compile_upto(sess, cfg, &input,
270                                               driver::cu_parse, Some(outputs));
271         crate.expect("parsing should return a crate")
272     }
273
274     fn find_main(crate: @ast::Crate, sess: session::Session,
275                  f: &fn(&ast::Block)) {
276         for crate.module.items.iter().advance |item| {
277             match item.node {
278                 ast::item_fn(_, _, _, _, ref blk) => {
279                     if item.ident == sess.ident_of("main") {
280                         return f(blk);
281                     }
282                 }
283                 _ => {}
284             }
285         }
286         fail!("main function was expected somewhere...");
287     }
288 }
289
290 // Compiles a crate given by the filename as a library if the compiled
291 // version doesn't exist or is older than the source file. Binary is
292 // the name of the compiling executable. Returns Some(true) if it
293 // successfully compiled, Some(false) if the crate wasn't compiled
294 // because it already exists and is newer than the source file, or
295 // None if there were compile errors.
296 fn compile_crate(src_filename: ~str, binary: ~str) -> Option<bool> {
297     match do task::try {
298         let src_path = Path(src_filename);
299         let binary = binary.to_managed();
300         let options = @session::options {
301             binary: binary,
302             addl_lib_search_paths: @mut ~[os::getcwd()],
303             .. (*session::basic_options()).clone()
304         };
305         let input = driver::file_input(src_path.clone());
306         let sess = driver::build_session(options, diagnostic::emit);
307         *sess.building_library = true;
308         let cfg = driver::build_configuration(sess, binary, &input);
309         let outputs = driver::build_output_filenames(
310             &input, &None, &None, [], sess);
311         // If the library already exists and is newer than the source
312         // file, skip compilation and return None.
313         let mut should_compile = true;
314         let dir = os::list_dir_path(&Path(outputs.out_filename.dirname()));
315         let maybe_lib_path = do dir.iter().find_ |file| {
316             // The actual file's name has a hash value and version
317             // number in it which is unknown at this time, so looking
318             // for a file that matches out_filename won't work,
319             // instead we guess which file is the library by matching
320             // the prefix and suffix of out_filename to files in the
321             // directory.
322             let file_str = file.filename().get();
323             file_str.starts_with(outputs.out_filename.filestem().get())
324                 && file_str.ends_with(outputs.out_filename.filetype().get())
325         };
326         match maybe_lib_path {
327             Some(lib_path) => {
328                 let (src_mtime, _) = src_path.get_mtime().get();
329                 let (lib_mtime, _) = lib_path.get_mtime().get();
330                 if lib_mtime >= src_mtime {
331                     should_compile = false;
332                 }
333             },
334             None => { },
335         }
336         if (should_compile) {
337             printfln!("compiling %s...", src_filename);
338             driver::compile_upto(sess, cfg, &input, driver::cu_everything,
339                                  Some(outputs));
340             true
341         } else { false }
342     } {
343         Ok(true) => Some(true),
344         Ok(false) => Some(false),
345         Err(_) => None,
346     }
347 }
348
349 /// Tries to get a line from rl after outputting a prompt. Returns
350 /// None if no input was read (e.g. EOF was reached).
351 fn get_line(use_rl: bool, prompt: &str) -> Option<~str> {
352     if use_rl {
353         let result = unsafe { rl::read(prompt) };
354
355         match result {
356             None => None,
357             Some(line) => {
358                 unsafe { rl::add_history(line) };
359                 Some(line)
360             }
361         }
362     } else {
363         if io::stdin().eof() {
364             None
365         } else {
366             Some(io::stdin().read_line())
367         }
368     }
369 }
370
371 /// Run a command, e.g. :clear, :exit, etc.
372 fn run_cmd(repl: &mut Repl, _in: @io::Reader, _out: @io::Writer,
373            cmd: ~str, args: ~[~str], use_rl: bool) -> CmdAction {
374     let mut action = action_none;
375     match cmd {
376         ~"exit" => repl.running = false,
377         ~"clear" => {
378             repl.program.clear();
379
380             // XXX: Win32 version of linenoise can't do this
381             //rl::clear();
382         }
383         ~"help" => {
384             println(
385                 ":{\\n ..lines.. \\n:}\\n - execute multiline command\n\
386                  :load <crate> ... - loads given crates as dynamic libraries\n\
387                  :clear - clear the bindings\n\
388                  :exit - exit from the repl\n\
389                  :help - show this message");
390         }
391         ~"load" => {
392             let mut loaded_crates: ~[~str] = ~[];
393             for args.iter().advance |arg| {
394                 let (crate, filename) =
395                     if arg.ends_with(".rs") || arg.ends_with(".rc") {
396                     (arg.slice_to(arg.len() - 3).to_owned(), (*arg).clone())
397                 } else {
398                     ((*arg).clone(), *arg + ".rs")
399                 };
400                 match compile_crate(filename, repl.binary.clone()) {
401                     Some(_) => loaded_crates.push(crate),
402                     None => { }
403                 }
404             }
405             for loaded_crates.iter().advance |crate| {
406                 let crate_path = Path(*crate);
407                 let crate_dir = crate_path.dirname();
408                 repl.program.record_extern(fmt!("extern mod %s;", *crate));
409                 if !repl.lib_search_paths.iter().any(|x| x == &crate_dir) {
410                     repl.lib_search_paths.push(crate_dir);
411                 }
412             }
413             if loaded_crates.is_empty() {
414                 println("no crates loaded");
415             } else {
416                 printfln!("crates loaded: %s", loaded_crates.connect(", "));
417             }
418         }
419         ~"{" => {
420             let mut multiline_cmd = ~"";
421             let mut end_multiline = false;
422             while (!end_multiline) {
423                 match get_line(use_rl, "rusti| ") {
424                     None => fail!("unterminated multiline command :{ .. :}"),
425                     Some(line) => {
426                         if line.trim() == ":}" {
427                             end_multiline = true;
428                         } else {
429                             multiline_cmd.push_str(line);
430                             multiline_cmd.push_char('\n');
431                         }
432                     }
433                 }
434             }
435             action = action_run_line(multiline_cmd);
436         }
437         _ => println(~"unknown cmd: " + cmd)
438     }
439     return action;
440 }
441
442 /// Executes a line of input, which may either be rust code or a
443 /// :command. Returns a new Repl if it has changed.
444 pub fn run_line(repl: &mut Repl, in: @io::Reader, out: @io::Writer, line: ~str,
445                 use_rl: bool) -> bool
446 {
447     if line.starts_with(":") {
448         // drop the : and the \n (one byte each)
449         let full = line.slice(1, line.len());
450         let split: ~[~str] = full.word_iter().transform(|s| s.to_owned()).collect();
451         let len = split.len();
452
453         if len > 0 {
454             let cmd = split[0].clone();
455
456             if !cmd.is_empty() {
457                 let args = if len > 1 {
458                     split.slice(1, len).to_owned()
459                 } else { ~[] };
460
461                 match run_cmd(repl, in, out, cmd, args, use_rl) {
462                     action_none => { }
463                     action_run_line(multiline_cmd) => {
464                         if !multiline_cmd.is_empty() {
465                             return run_line(repl, in, out, multiline_cmd, use_rl);
466                         }
467                     }
468                 }
469                 return true;
470             }
471         }
472     }
473
474     let line = Cell::new(line);
475     let program = Cell::new(repl.program.clone());
476     let lib_search_paths = Cell::new(repl.lib_search_paths.clone());
477     let binary = Cell::new(repl.binary.clone());
478     let result = do task::try {
479         run(program.take(), binary.take(), lib_search_paths.take(), line.take())
480     };
481
482     match result {
483         Ok((program, engine)) => {
484             repl.program = program;
485             match engine {
486                 Some(e) => { repl.engines.push(e); }
487                 None => {}
488             }
489             return true;
490         }
491         Err(*) => { return false; }
492     }
493 }
494
495 pub fn main() {
496     let args = os::args();
497     let in = io::stdin();
498     let out = io::stdout();
499     let mut repl = Repl {
500         prompt: ~"rusti> ",
501         binary: args[0].clone(),
502         running: true,
503         lib_search_paths: ~[],
504         engines: ~[],
505
506         program: ~Program::new(),
507     };
508
509     let istty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;
510
511     // only print this stuff if the user is actually typing into rusti
512     if istty {
513         println("WARNING: The Rust REPL is experimental and may be");
514         println("unstable. If you encounter problems, please use the");
515         println("compiler instead. Type :help for help.");
516
517         unsafe {
518             do rl::complete |line, suggest| {
519                 if line.starts_with(":") {
520                     suggest(~":clear");
521                     suggest(~":exit");
522                     suggest(~":help");
523                     suggest(~":load");
524                 }
525             }
526         }
527     }
528
529     while repl.running {
530         match get_line(istty, repl.prompt) {
531             None => break,
532             Some(line) => {
533                 if line.is_empty() {
534                     if istty {
535                         println("()");
536                     }
537                     loop;
538                 }
539                 run_line(&mut repl, in, out, line, istty);
540             }
541         }
542     }
543 }
544
545 #[cfg(test)]
546 mod tests {
547     use std::io;
548     use program::Program;
549     use super::*;
550
551     fn repl() -> Repl {
552         Repl {
553             prompt: ~"rusti> ",
554             binary: ~"rusti",
555             running: true,
556             lib_search_paths: ~[],
557             engines: ~[],
558             program: ~Program::new(),
559         }
560     }
561
562     // FIXME: #7220 rusti on 32bit mac doesn't work.
563     // FIXME: #7641 rusti on 32bit linux cross compile doesn't work
564     // FIXME: #7115 re-enable once LLVM has been upgraded
565     #[cfg(thiswillneverbeacfgflag)]
566     fn run_program(prog: &str) {
567         let mut r = repl();
568         for prog.split_iter('\n').advance |cmd| {
569             assert!(run_line(&mut r, io::stdin(), io::stdout(),
570                              cmd.to_owned(), false),
571                     "the command '%s' failed", cmd);
572         }
573     }
574     fn run_program(_: &str) {}
575
576     #[test]
577     fn super_basic() {
578         run_program("");
579     }
580
581     #[test]
582     fn regression_5937() {
583         run_program("use std::hashmap;");
584     }
585
586     #[test]
587     fn regression_5784() {
588         run_program("let a = 3;");
589     }
590
591     #[test] #[ignore]
592     fn new_tasks() {
593         // XXX: can't spawn new tasks because the JIT code is cleaned up
594         //      after the main function is done.
595         run_program("
596             spawn( || println(\"Please don't segfault\") );
597             do spawn { println(\"Please?\"); }
598         ");
599     }
600
601     #[test]
602     fn inferred_integers_usable() {
603         run_program("let a = 2;\n()\n");
604         run_program("
605             let a = 3;
606             let b = 4u;
607             assert!((a as uint) + b == 7)
608         ");
609     }
610
611     #[test]
612     fn local_variables_allow_shadowing() {
613         run_program("
614             let a = 3;
615             let a = 5;
616             assert!(a == 5)
617         ");
618     }
619
620     #[test]
621     fn string_usable() {
622         run_program("
623             let a = ~\"\";
624             let b = \"\";
625             let c = @\"\";
626             let d = a + b + c;
627             assert!(d.len() == 0);
628         ");
629     }
630
631     #[test]
632     fn vectors_usable() {
633         run_program("
634             let a = ~[1, 2, 3];
635             let b = &[1, 2, 3];
636             let c = @[1, 2, 3];
637             let d = a + b + c;
638             assert!(d.len() == 9);
639             let e: &[int] = [];
640         ");
641     }
642
643     #[test]
644     fn structs_usable() {
645         run_program("
646             struct A{ a: int }
647             let b = A{ a: 3 };
648             assert!(b.a == 3)
649         ");
650     }
651
652     #[test]
653     fn mutable_variables_work() {
654         run_program("
655             let mut a = 3;
656             a = 5;
657             let mut b = std::hashmap::HashSet::new::<int>();
658             b.insert(a);
659             assert!(b.contains(&5))
660             assert!(b.len() == 1)
661         ");
662     }
663
664     #[test]
665     fn functions_saved() {
666         run_program("
667             fn fib(x: int) -> int { if x < 2 {x} else { fib(x - 1) + fib(x - 2) } }
668             let a = fib(3);
669             let a = a + fib(4);
670             assert!(a == 5)
671         ");
672     }
673
674     #[test]
675     fn modules_saved() {
676         run_program("
677             mod b { pub fn foo() -> uint { 3 } }
678             assert!(b::foo() == 3)
679         ");
680     }
681
682     #[test]
683     fn multiple_functions() {
684         run_program("
685             fn f() {}
686             fn f() {}
687             f()
688         ");
689     }
690
691     #[test]
692     fn multiple_items_same_name() {
693         run_program("
694             fn f() {}
695             mod f {}
696             struct f;
697             enum f {}
698             fn f() {}
699             f()
700         ");
701     }
702
703     #[test]
704     fn simultaneous_definition_and_expression() {
705         run_program("
706             let a = 3; a as u8
707         ");
708     }
709
710     #[test]
711     fn exit_quits() {
712         let mut r = repl();
713         assert!(r.running);
714         let result = run_line(&mut r, io::stdin(), io::stdout(),
715                               ~":exit", false);
716         assert!(result);
717         assert!(!r.running);
718     }
719 }