auto merge of #7070 : alexcrichton/rust/rusti++, r=graydon
More descriptive comments can be found throughout the code, but the bulk of this is that it addresses a vast number of issues with the old rusti model. Closes #6772, #5675, #5469, and #6617
This commit is contained in:
commit
4ec08d8e12
|
@ -0,0 +1,434 @@
|
||||||
|
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use std::cast;
|
||||||
|
use std::hashmap::HashMap;
|
||||||
|
use std::local_data;
|
||||||
|
use std::sys;
|
||||||
|
|
||||||
|
use syntax::ast;
|
||||||
|
use syntax::parse::token;
|
||||||
|
use syntax::print::pprust;
|
||||||
|
use rustc::middle::ty;
|
||||||
|
use rustc::util::ppaux;
|
||||||
|
|
||||||
|
use utils::*;
|
||||||
|
|
||||||
|
/// This structure keeps track of the state of the world for the code being
|
||||||
|
/// executed in rusti.
|
||||||
|
struct Program {
|
||||||
|
/// All known local variables
|
||||||
|
local_vars: HashMap<~str, LocalVariable>,
|
||||||
|
/// New variables which will be present (learned from typechecking)
|
||||||
|
newvars: HashMap<~str, LocalVariable>,
|
||||||
|
/// All known view items (use statements), distinct because these must
|
||||||
|
/// follow extern mods
|
||||||
|
view_items: ~str,
|
||||||
|
/// All known 'extern mod' statements (must always come first)
|
||||||
|
externs: ~str,
|
||||||
|
/// All known structs defined. These need to have
|
||||||
|
/// #[deriving(Encodable,Decodable)] to be at all useful in rusti
|
||||||
|
structs: HashMap<~str, ~str>,
|
||||||
|
/// All other items, can all be intermingled. Duplicate definitions of the
|
||||||
|
/// same name have the previous one overwritten.
|
||||||
|
items: HashMap<~str, ~str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a local variable that the program is currently using.
|
||||||
|
struct LocalVariable {
|
||||||
|
/// Should this variable be locally declared as mutable?
|
||||||
|
mutable: bool,
|
||||||
|
/// This is the type of the serialized data below
|
||||||
|
ty: ~str,
|
||||||
|
/// This is the serialized version of the variable
|
||||||
|
data: ~[u8],
|
||||||
|
/// When taking borrowed pointers or slices, care must be taken to ensure
|
||||||
|
/// that the deserialization produces what we'd expect. If some magic is in
|
||||||
|
/// order, the first element of this pair is the actual type of the local
|
||||||
|
/// variable (which can be different from the deserialized type), and the
|
||||||
|
/// second element are the '&'s which need to be prepended.
|
||||||
|
alterations: Option<(~str, ~str)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocalCache = @mut HashMap<~str, @~[u8]>;
|
||||||
|
fn tls_key(_k: @LocalCache) {}
|
||||||
|
|
||||||
|
impl Program {
|
||||||
|
pub fn new() -> Program {
|
||||||
|
Program {
|
||||||
|
local_vars: HashMap::new(),
|
||||||
|
newvars: HashMap::new(),
|
||||||
|
view_items: ~"",
|
||||||
|
externs: ~"",
|
||||||
|
structs: HashMap::new(),
|
||||||
|
items: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears all local bindings about variables, items, externs, etc.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
*self = Program::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a block of code to be fed to rustc. This code is not meant to
|
||||||
|
/// run, but rather it is meant to learn about the input given. This will
|
||||||
|
/// assert that the types of all bound local variables are encodable,
|
||||||
|
/// along with checking syntax and other rust-related things. The reason
|
||||||
|
/// that we only check for encodability is that some super-common types
|
||||||
|
/// (like &'static str) are not decodable, but are encodable. By doing some
|
||||||
|
/// mild approximation when decoding, we can emulate at least &str and &[T].
|
||||||
|
///
|
||||||
|
/// Once this code has been fed to rustc, it is intended that the code()
|
||||||
|
/// function is used to actually generate code to fully compile and run.
|
||||||
|
pub fn test_code(&self, user_input: &str, to_print: &Option<~str>,
|
||||||
|
new_locals: &[(~str, bool)]) -> ~str {
|
||||||
|
let mut code = self.program_header();
|
||||||
|
code.push_str("
|
||||||
|
fn assert_encodable<T: Encodable<::extra::ebml::writer::Encoder>>(t: &T) {}
|
||||||
|
");
|
||||||
|
|
||||||
|
code.push_str("fn main() {\n");
|
||||||
|
// It's easy to initialize things if we don't run things...
|
||||||
|
for self.local_vars.each |name, var| {
|
||||||
|
let mt = var.mt();
|
||||||
|
code.push_str(fmt!("let%s %s: %s = fail!();\n", mt, *name, var.ty));
|
||||||
|
var.alter(*name, &mut code);
|
||||||
|
}
|
||||||
|
code.push_str("{\n");
|
||||||
|
code.push_str(user_input);
|
||||||
|
code.push_char('\n');
|
||||||
|
match *to_print {
|
||||||
|
Some(ref s) => {
|
||||||
|
code.push_str(*s);
|
||||||
|
code.push_char('\n');
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
for new_locals.each |p| {
|
||||||
|
code.push_str(fmt!("assert_encodable(&%s);\n", *p.first_ref()));
|
||||||
|
}
|
||||||
|
code.push_str("};}");
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a program to be fed into rustc. This program is structured to
|
||||||
|
/// deserialize all bindings into local variables, run the code input, and
|
||||||
|
/// then reserialize all the variables back out.
|
||||||
|
///
|
||||||
|
/// This program (unlike test_code) is meant to run to actually execute the
|
||||||
|
/// user's input
|
||||||
|
pub fn code(&mut self, user_input: &str, to_print: &Option<~str>) -> ~str {
|
||||||
|
let mut code = self.program_header();
|
||||||
|
code.push_str("
|
||||||
|
fn main() {
|
||||||
|
");
|
||||||
|
|
||||||
|
let key: sys::Closure = unsafe {
|
||||||
|
let tls_key: &'static fn(@LocalCache) = tls_key;
|
||||||
|
cast::transmute(tls_key)
|
||||||
|
};
|
||||||
|
// First, get a handle to the tls map which stores all the local
|
||||||
|
// variables. This works by totally legitimately using the 'code'
|
||||||
|
// pointer of the 'tls_key' function as a uint, and then casting it back
|
||||||
|
// up to a function
|
||||||
|
code.push_str(fmt!("
|
||||||
|
let __tls_map: @mut ::std::hashmap::HashMap<~str, @~[u8]> = unsafe {
|
||||||
|
let key = ::std::sys::Closure{ code: %? as *(),
|
||||||
|
env: ::std::ptr::null() };
|
||||||
|
let key = ::std::cast::transmute(key);
|
||||||
|
*::std::local_data::local_data_get(key).unwrap()
|
||||||
|
};\n", key.code as uint));
|
||||||
|
|
||||||
|
// Using this __tls_map handle, deserialize each variable binding that
|
||||||
|
// we know about
|
||||||
|
for self.local_vars.each |name, var| {
|
||||||
|
let mt = var.mt();
|
||||||
|
code.push_str(fmt!("let%s %s: %s = {
|
||||||
|
let data = __tls_map.get_copy(&~\"%s\");
|
||||||
|
let doc = ::extra::ebml::reader::Doc(data);
|
||||||
|
let mut decoder = ::extra::ebml::reader::Decoder(doc);
|
||||||
|
::extra::serialize::Decodable::decode(&mut decoder)
|
||||||
|
};\n", mt, *name, var.ty, *name));
|
||||||
|
var.alter(*name, &mut code);
|
||||||
|
}
|
||||||
|
|
||||||
|
// After all that, actually run the user's code.
|
||||||
|
code.push_str(user_input);
|
||||||
|
code.push_char('\n');
|
||||||
|
|
||||||
|
match *to_print {
|
||||||
|
Some(ref s) => { code.push_str(fmt!("pp({\n%s\n});", *s)); }
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
do self.newvars.consume |name, var| {
|
||||||
|
self.local_vars.insert(name, var);
|
||||||
|
}
|
||||||
|
|
||||||
|
// After the input code is run, we can re-serialize everything back out
|
||||||
|
// into tls map (to be read later on by this task)
|
||||||
|
for self.local_vars.each |name, var| {
|
||||||
|
code.push_str(fmt!("{
|
||||||
|
let local: %s = %s;
|
||||||
|
let bytes = do ::std::io::with_bytes_writer |io| {
|
||||||
|
let mut enc = ::extra::ebml::writer::Encoder(io);
|
||||||
|
local.encode(&mut enc);
|
||||||
|
};
|
||||||
|
__tls_map.insert(~\"%s\", @bytes);
|
||||||
|
}\n", var.real_ty(), *name, *name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close things up, and we're done.
|
||||||
|
code.push_str("}");
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the header of the programs which are generated to send to rustc
|
||||||
|
fn program_header(&self) -> ~str {
|
||||||
|
// up front, disable lots of annoying lints, then include all global
|
||||||
|
// state such as items, view items, and extern mods.
|
||||||
|
let mut code = fmt!("
|
||||||
|
#[allow(ctypes)];
|
||||||
|
#[allow(heap_memory)];
|
||||||
|
#[allow(implicit_copies)];
|
||||||
|
#[allow(managed_heap_memory)];
|
||||||
|
#[allow(non_camel_case_types)];
|
||||||
|
#[allow(owned_heap_memory)];
|
||||||
|
#[allow(path_statement)];
|
||||||
|
#[allow(unrecognized_lint)];
|
||||||
|
#[allow(unused_imports)];
|
||||||
|
#[allow(while_true)];
|
||||||
|
#[allow(unused_variable)];
|
||||||
|
#[allow(dead_assignment)];
|
||||||
|
#[allow(unused_unsafe)];
|
||||||
|
#[allow(unused_mut)];
|
||||||
|
#[allow(unreachable_code)];
|
||||||
|
|
||||||
|
extern mod extra;
|
||||||
|
%s // extern mods
|
||||||
|
|
||||||
|
use extra::serialize::*;
|
||||||
|
%s // view items
|
||||||
|
", self.externs, self.view_items);
|
||||||
|
for self.structs.each_value |s| {
|
||||||
|
// The structs aren't really useful unless they're encodable
|
||||||
|
code.push_str("#[deriving(Encodable, Decodable)]");
|
||||||
|
code.push_str(*s);
|
||||||
|
code.push_str("\n");
|
||||||
|
}
|
||||||
|
for self.items.each_value |s| {
|
||||||
|
code.push_str(*s);
|
||||||
|
code.push_str("\n");
|
||||||
|
}
|
||||||
|
code.push_str("fn pp<T>(t: T) { println(fmt!(\"%?\", t)); }\n");
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes the task-local cache of all local variables known to the
|
||||||
|
/// program. This will be used to read local variables out of once the
|
||||||
|
/// program starts
|
||||||
|
pub fn set_cache(&self) {
|
||||||
|
let map = @mut HashMap::new();
|
||||||
|
for self.local_vars.each |name, value| {
|
||||||
|
map.insert(copy *name, @copy value.data);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
local_data::local_data_set(tls_key, @map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Once the program has finished running, this function will consume the
|
||||||
|
/// task-local cache of local variables. After the program finishes running,
|
||||||
|
/// it updates this cache with the new values of each local variable.
|
||||||
|
pub fn consume_cache(&mut self) {
|
||||||
|
let map = unsafe {
|
||||||
|
local_data::local_data_pop(tls_key).expect("tls is empty")
|
||||||
|
};
|
||||||
|
do map.consume |name, value| {
|
||||||
|
match self.local_vars.find_mut(&name) {
|
||||||
|
Some(v) => { v.data = copy *value; }
|
||||||
|
None => { fail!("unknown variable %s", name) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple functions to record various global things (as strings)
|
||||||
|
|
||||||
|
pub fn record_view_item(&mut self, vi: &str) {
|
||||||
|
self.view_items.push_str(vi);
|
||||||
|
self.view_items.push_char('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn record_struct(&mut self, name: &str, s: ~str) {
|
||||||
|
let name = name.to_owned();
|
||||||
|
self.items.remove(&name);
|
||||||
|
self.structs.insert(name, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn record_item(&mut self, name: &str, it: ~str) {
|
||||||
|
let name = name.to_owned();
|
||||||
|
self.structs.remove(&name);
|
||||||
|
self.items.insert(name, it);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn record_extern(&mut self, name: &str) {
|
||||||
|
self.externs.push_str(name);
|
||||||
|
self.externs.push_char('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This monster function is responsible for reading the main function
|
||||||
|
/// generated by test_code() to determine the type of each local binding
|
||||||
|
/// created by the user's input.
|
||||||
|
///
|
||||||
|
/// Once the types are known, they are inserted into the local_vars map in
|
||||||
|
/// this Program (to be deserialized later on
|
||||||
|
pub fn register_new_vars(&mut self, blk: &ast::blk, tcx: ty::ctxt) {
|
||||||
|
debug!("looking for new variables");
|
||||||
|
let newvars = @mut HashMap::new();
|
||||||
|
do each_user_local(blk) |local| {
|
||||||
|
let mutable = local.node.is_mutbl;
|
||||||
|
do each_binding(local) |path, id| {
|
||||||
|
let name = do with_pp(token::get_ident_interner()) |pp, _| {
|
||||||
|
pprust::print_path(pp, path, false);
|
||||||
|
};
|
||||||
|
let mut t = ty::node_id_to_type(tcx, id);
|
||||||
|
let mut tystr = ~"";
|
||||||
|
let mut lvar = LocalVariable {
|
||||||
|
ty: ~"",
|
||||||
|
data: ~[],
|
||||||
|
mutable: mutable,
|
||||||
|
alterations: None,
|
||||||
|
};
|
||||||
|
// This loop is responsible for figuring out what "alterations"
|
||||||
|
// are necessary for this local variable.
|
||||||
|
loop {
|
||||||
|
match ty::get(t).sty {
|
||||||
|
// &T encoded will decode to T, so we need to be sure to
|
||||||
|
// re-take a loan after decoding
|
||||||
|
ty::ty_rptr(_, mt) => {
|
||||||
|
if mt.mutbl == ast::m_mutbl {
|
||||||
|
tystr.push_str("&mut ");
|
||||||
|
} else {
|
||||||
|
tystr.push_str("&");
|
||||||
|
}
|
||||||
|
t = mt.ty;
|
||||||
|
}
|
||||||
|
// Literals like [1, 2, 3] and (~[0]).slice() will both
|
||||||
|
// be serialized to ~[T], whereas it's requested to be a
|
||||||
|
// &[T] instead.
|
||||||
|
ty::ty_evec(mt, ty::vstore_slice(*)) |
|
||||||
|
ty::ty_evec(mt, ty::vstore_fixed(*)) => {
|
||||||
|
let vty = ppaux::ty_to_str(tcx, mt.ty);
|
||||||
|
let derefs = copy tystr;
|
||||||
|
lvar.ty = tystr + "~[" + vty + "]";
|
||||||
|
lvar.alterations = Some((tystr + "&[" + vty + "]",
|
||||||
|
derefs));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Similar to vectors, &str serializes to ~str, so a
|
||||||
|
// borrow must be taken
|
||||||
|
ty::ty_estr(ty::vstore_slice(*)) => {
|
||||||
|
let derefs = copy tystr;
|
||||||
|
lvar.ty = tystr + "~str";
|
||||||
|
lvar.alterations = Some((tystr + "&str", derefs));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Don't generate extra stuff if there's no borrowing
|
||||||
|
// going on here
|
||||||
|
_ if "" == tystr => {
|
||||||
|
lvar.ty = ppaux::ty_to_str(tcx, t);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If we're just borrowing (no vectors or strings), then
|
||||||
|
// we just need to record how many borrows there were.
|
||||||
|
_ => {
|
||||||
|
let derefs = copy tystr;
|
||||||
|
let tmptystr = ppaux::ty_to_str(tcx, t);
|
||||||
|
lvar.alterations = Some((tystr + tmptystr, derefs));
|
||||||
|
lvar.ty = tmptystr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newvars.insert(name, lvar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// I'm not an @ pointer, so this has to be done outside.
|
||||||
|
do newvars.consume |k, v| {
|
||||||
|
self.newvars.insert(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper functions to perform ast iteration
|
||||||
|
fn each_user_local(blk: &ast::blk, f: &fn(@ast::local)) {
|
||||||
|
do find_user_block(blk) |blk| {
|
||||||
|
for blk.node.stmts.each |stmt| {
|
||||||
|
match stmt.node {
|
||||||
|
ast::stmt_decl(d, _) => {
|
||||||
|
match d.node {
|
||||||
|
ast::decl_local(l) => { f(l); }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_user_block(blk: &ast::blk, f: &fn(&ast::blk)) {
|
||||||
|
for blk.node.stmts.each |stmt| {
|
||||||
|
match stmt.node {
|
||||||
|
ast::stmt_semi(e, _) => {
|
||||||
|
match e.node {
|
||||||
|
ast::expr_block(ref blk) => { return f(blk); }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail!("couldn't find user block");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalVariable {
|
||||||
|
/// Performs alterations to the code provided, given the name of this
|
||||||
|
/// variable.
|
||||||
|
fn alter(&self, name: &str, code: &mut ~str) {
|
||||||
|
match self.alterations {
|
||||||
|
Some((ref real_ty, ref prefix)) => {
|
||||||
|
code.push_str(fmt!("let%s %s: %s = %s%s;\n",
|
||||||
|
self.mt(), name,
|
||||||
|
*real_ty, *prefix, name));
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn real_ty<'a>(&'a self) -> &'a str {
|
||||||
|
match self.alterations {
|
||||||
|
Some((ref real_ty, _)) => {
|
||||||
|
let ret: &'a str = *real_ty;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let ret: &'a str = self.ty;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mt(&self) -> &'static str {
|
||||||
|
if self.mutable {" mut"} else {""}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,40 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
// rusti - REPL using the JIT backend
|
/*!
|
||||||
|
* rusti - A REPL using the JIT backend
|
||||||
|
*
|
||||||
|
* Rusti works by serializing state between lines of input. This means that each
|
||||||
|
* line can be run in a separate task, and the only limiting factor is that all
|
||||||
|
* local bound variables are encodable.
|
||||||
|
*
|
||||||
|
* This is accomplished by feeding in generated input to rustc for execution in
|
||||||
|
* the JIT compiler. Currently input actually gets fed in three times to get
|
||||||
|
* information about the program.
|
||||||
|
*
|
||||||
|
* - Pass #1
|
||||||
|
* In this pass, the input is simply thrown at the parser and the input comes
|
||||||
|
* back. This validates the structure of the program, and at this stage the
|
||||||
|
* global items (fns, structs, impls, traits, etc.) are filtered from the
|
||||||
|
* input into the "global namespace". These declarations shadow all previous
|
||||||
|
* declarations of an item by the same name.
|
||||||
|
*
|
||||||
|
* - Pass #2
|
||||||
|
* After items have been stripped, the remaining input is passed to rustc
|
||||||
|
* along with all local variables declared (initialized to nothing). This pass
|
||||||
|
* runs up to typechecking. From this, we can learn about the types of each
|
||||||
|
* bound variable, what variables are bound, and also ensure that all the
|
||||||
|
* types are encodable (the input can actually be run).
|
||||||
|
*
|
||||||
|
* - Pass #3
|
||||||
|
* Finally, a program is generated to deserialize the local variable state,
|
||||||
|
* run the code input, and then reserialize all bindings back into a local
|
||||||
|
* hash map. Once this code runs, the input has fully been run and the REPL
|
||||||
|
* waits for new input.
|
||||||
|
*
|
||||||
|
* Encoding/decoding is done with EBML, and there is simply a map of ~str ->
|
||||||
|
* ~[u8] maintaining the values of each local binding (by name).
|
||||||
|
*/
|
||||||
|
|
||||||
#[link(name = "rusti",
|
#[link(name = "rusti",
|
||||||
vers = "0.7-pre",
|
vers = "0.7-pre",
|
||||||
|
@ -18,24 +51,25 @@
|
||||||
#[license = "MIT/ASL2"];
|
#[license = "MIT/ASL2"];
|
||||||
#[crate_type = "lib"];
|
#[crate_type = "lib"];
|
||||||
|
|
||||||
#[no_std];
|
extern mod extra;
|
||||||
|
|
||||||
extern mod core(name = "std");
|
|
||||||
extern mod std(name = "extra");
|
|
||||||
|
|
||||||
extern mod rustc;
|
extern mod rustc;
|
||||||
extern mod syntax;
|
extern mod syntax;
|
||||||
|
|
||||||
use core::prelude::*;
|
use std::{libc, io, os, task, vec};
|
||||||
use core::*;
|
use std::cell::Cell;
|
||||||
|
use extra::rl;
|
||||||
|
|
||||||
use core::cell::Cell;
|
|
||||||
use rustc::driver::{driver, session};
|
use rustc::driver::{driver, session};
|
||||||
use syntax::{ast, diagnostic};
|
use syntax::{ast, diagnostic};
|
||||||
use syntax::ast_util::*;
|
use syntax::ast_util::*;
|
||||||
use syntax::parse::token;
|
use syntax::parse::token;
|
||||||
use syntax::print::{pp, pprust};
|
use syntax::print::pprust;
|
||||||
use std::rl;
|
|
||||||
|
use program::Program;
|
||||||
|
use utils::*;
|
||||||
|
|
||||||
|
mod program;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A structure shared across REPL instances for storing history
|
* A structure shared across REPL instances for storing history
|
||||||
|
@ -45,9 +79,9 @@ pub struct Repl {
|
||||||
prompt: ~str,
|
prompt: ~str,
|
||||||
binary: ~str,
|
binary: ~str,
|
||||||
running: bool,
|
running: bool,
|
||||||
view_items: ~str,
|
|
||||||
lib_search_paths: ~[~str],
|
lib_search_paths: ~[~str],
|
||||||
stmts: ~str
|
|
||||||
|
program: Program,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action to do after reading a :command
|
// Action to do after reading a :command
|
||||||
|
@ -56,67 +90,9 @@ enum CmdAction {
|
||||||
action_run_line(~str),
|
action_run_line(~str),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A utility function that hands off a pretty printer to a callback.
|
|
||||||
fn with_pp(intr: @token::ident_interner,
|
|
||||||
cb: &fn(@pprust::ps, @io::Writer)) -> ~str {
|
|
||||||
do io::with_str_writer |writer| {
|
|
||||||
let pp = pprust::rust_printer(writer, intr);
|
|
||||||
|
|
||||||
cb(pp, writer);
|
|
||||||
pp::eof(pp.s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The AST (or the rest of rustc) are not sendable yet,
|
|
||||||
* so recorded things are printed to strings. A terrible hack that
|
|
||||||
* needs changes to rustc in order to be outed. This is unfortunately
|
|
||||||
* going to cause the REPL to regress in parser performance,
|
|
||||||
* because it has to parse the statements and view_items on each
|
|
||||||
* input.
|
|
||||||
*/
|
|
||||||
fn record(mut repl: Repl, blk: &ast::blk, intr: @token::ident_interner) -> Repl {
|
|
||||||
if blk.node.view_items.len() > 0 {
|
|
||||||
let new_view_items = do with_pp(intr) |pp, writer| {
|
|
||||||
for blk.node.view_items.each |view_item| {
|
|
||||||
pprust::print_view_item(pp, *view_item);
|
|
||||||
writer.write_line("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("new view items %s", new_view_items);
|
|
||||||
|
|
||||||
repl.view_items = repl.view_items + "\n" + new_view_items
|
|
||||||
}
|
|
||||||
if blk.node.stmts.len() > 0 {
|
|
||||||
let new_stmts = do with_pp(intr) |pp, writer| {
|
|
||||||
for blk.node.stmts.each |stmt| {
|
|
||||||
match stmt.node {
|
|
||||||
ast::stmt_decl(*) | ast::stmt_mac(*) => {
|
|
||||||
pprust::print_stmt(pp, *stmt);
|
|
||||||
writer.write_line("");
|
|
||||||
}
|
|
||||||
ast::stmt_expr(expr, _) | ast::stmt_semi(expr, _) => {
|
|
||||||
match expr.node {
|
|
||||||
ast::expr_assign(*) |
|
|
||||||
ast::expr_assign_op(*) |
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("new stmts %s", new_stmts);
|
|
||||||
|
|
||||||
repl.stmts = repl.stmts + "\n" + new_stmts
|
|
||||||
}
|
|
||||||
|
|
||||||
return repl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run an input string in a Repl, returning the new Repl.
|
/// Run an input string in a Repl, returning the new Repl.
|
||||||
fn run(repl: Repl, input: ~str) -> Repl {
|
fn run(mut repl: Repl, input: ~str) -> Repl {
|
||||||
|
// Build some necessary rustc boilerplate for compiling things
|
||||||
let binary = repl.binary.to_managed();
|
let binary = repl.binary.to_managed();
|
||||||
let options = @session::options {
|
let options = @session::options {
|
||||||
crate_type: session::unknown_crate,
|
crate_type: session::unknown_crate,
|
||||||
|
@ -125,56 +101,165 @@ fn run(repl: Repl, input: ~str) -> Repl {
|
||||||
jit: true,
|
jit: true,
|
||||||
.. copy *session::basic_options()
|
.. copy *session::basic_options()
|
||||||
};
|
};
|
||||||
|
// Because we assume that everything is encodable (and assert so), add some
|
||||||
|
// extra helpful information if the error crops up. Otherwise people are
|
||||||
|
// bound to be very confused when they find out code is running that they
|
||||||
|
// never typed in...
|
||||||
|
let sess = driver::build_session(options, |cm, msg, lvl| {
|
||||||
|
diagnostic::emit(cm, msg, lvl);
|
||||||
|
if msg.contains("failed to find an implementation of trait") &&
|
||||||
|
msg.contains("extra::serialize::Encodable") {
|
||||||
|
diagnostic::emit(cm,
|
||||||
|
"Currrently rusti serializes bound locals between \
|
||||||
|
different lines of input. This means that all \
|
||||||
|
values of local variables need to be encodable, \
|
||||||
|
and this type isn't encodable",
|
||||||
|
diagnostic::note);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let intr = token::get_ident_interner();
|
||||||
|
|
||||||
debug!("building driver input");
|
//
|
||||||
let head = include_str!("wrapper.rs").to_owned();
|
// Stage 1: parse the input and filter it into the program (as necessary)
|
||||||
let foot = fmt!("fn main() {\n%s\n%s\n\nprint({\n%s\n})\n}",
|
//
|
||||||
repl.view_items, repl.stmts, input);
|
debug!("parsing: %s", input);
|
||||||
let wrapped = driver::str_input((head + foot).to_managed());
|
let crate = parse_input(sess, binary, input);
|
||||||
|
let mut to_run = ~[]; // statements to run (emitted back into code)
|
||||||
|
let new_locals = @mut ~[]; // new locals being defined
|
||||||
|
let mut result = None; // resultant expression (to print via pp)
|
||||||
|
do find_main(crate, sess) |blk| {
|
||||||
|
// Fish out all the view items, be sure to record 'extern mod' items
|
||||||
|
// differently beause they must appear before all 'use' statements
|
||||||
|
for blk.node.view_items.each |vi| {
|
||||||
|
let s = do with_pp(intr) |pp, _| {
|
||||||
|
pprust::print_view_item(pp, *vi);
|
||||||
|
};
|
||||||
|
match vi.node {
|
||||||
|
ast::view_item_extern_mod(*) => {
|
||||||
|
repl.program.record_extern(s);
|
||||||
|
}
|
||||||
|
ast::view_item_use(*) => { repl.program.record_view_item(s); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debug!("inputting %s", head + foot);
|
// Iterate through all of the block's statements, inserting them into
|
||||||
|
// the correct portions of the program
|
||||||
|
for blk.node.stmts.each |stmt| {
|
||||||
|
let s = do with_pp(intr) |pp, _| { pprust::print_stmt(pp, *stmt); };
|
||||||
|
match stmt.node {
|
||||||
|
ast::stmt_decl(d, _) => {
|
||||||
|
match d.node {
|
||||||
|
ast::decl_item(it) => {
|
||||||
|
let name = sess.str_of(it.ident);
|
||||||
|
match it.node {
|
||||||
|
// Structs are treated specially because to make
|
||||||
|
// them at all usable they need to be decorated
|
||||||
|
// with #[deriving(Encoable, Decodable)]
|
||||||
|
ast::item_struct(*) => {
|
||||||
|
repl.program.record_struct(name, s);
|
||||||
|
}
|
||||||
|
// Item declarations are hoisted out of main()
|
||||||
|
_ => { repl.program.record_item(name, s); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debug!("building a driver session");
|
// Local declarations must be specially dealt with,
|
||||||
let sess = driver::build_session(options, diagnostic::emit);
|
// record all local declarations for use later on
|
||||||
|
ast::decl_local(l) => {
|
||||||
|
let mutbl = l.node.is_mutbl;
|
||||||
|
do each_binding(l) |path, _| {
|
||||||
|
let s = do with_pp(intr) |pp, _| {
|
||||||
|
pprust::print_path(pp, path, false);
|
||||||
|
};
|
||||||
|
new_locals.push((s, mutbl));
|
||||||
|
}
|
||||||
|
to_run.push(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debug!("building driver configuration");
|
// run statements with expressions (they have effects)
|
||||||
let cfg = driver::build_configuration(sess,
|
ast::stmt_mac(*) | ast::stmt_semi(*) | ast::stmt_expr(*) => {
|
||||||
binary,
|
to_run.push(s);
|
||||||
&wrapped);
|
|
||||||
|
|
||||||
let outputs = driver::build_output_filenames(&wrapped, &None, &None, [], sess);
|
|
||||||
debug!("calling compile_upto");
|
|
||||||
|
|
||||||
let crate = driver::parse_input(sess, copy cfg, &wrapped);
|
|
||||||
driver::compile_rest(sess, cfg, driver::compile_upto { from: driver::cu_parse,
|
|
||||||
to: driver::cu_everything },
|
|
||||||
Some(outputs), Some(crate));
|
|
||||||
|
|
||||||
let mut opt = None;
|
|
||||||
|
|
||||||
for crate.node.module.items.each |item| {
|
|
||||||
match item.node {
|
|
||||||
ast::item_fn(_, _, _, _, ref blk) => {
|
|
||||||
if item.ident == sess.ident_of("main") {
|
|
||||||
opt = blk.node.expr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
result = do blk.node.expr.map_consume |e| {
|
||||||
|
do with_pp(intr) |pp, _| { pprust::print_expr(pp, e); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// return fast for empty inputs
|
||||||
|
if to_run.len() == 0 && result.is_none() {
|
||||||
|
return repl;
|
||||||
}
|
}
|
||||||
|
|
||||||
let e = opt.unwrap();
|
//
|
||||||
let blk = match e.node {
|
// Stage 2: run everything up to typeck to learn the types of the new
|
||||||
ast::expr_call(_, ref exprs, _) => {
|
// variables introduced into the program
|
||||||
match exprs[0].node {
|
//
|
||||||
ast::expr_block(ref blk) => blk,
|
info!("Learning about the new types in the program");
|
||||||
_ => fail!()
|
repl.program.set_cache(); // before register_new_vars (which changes them)
|
||||||
|
let input = to_run.connect("\n");
|
||||||
|
let test = repl.program.test_code(input, &result, *new_locals);
|
||||||
|
debug!("testing with ^^^^^^ %?", (||{ println(test) })());
|
||||||
|
let dinput = driver::str_input(test.to_managed());
|
||||||
|
let cfg = driver::build_configuration(sess, binary, &dinput);
|
||||||
|
let outputs = driver::build_output_filenames(&dinput, &None, &None, [], sess);
|
||||||
|
let (crate, tcx) = driver::compile_upto(sess, copy cfg, &dinput,
|
||||||
|
driver::cu_typeck, Some(outputs));
|
||||||
|
// Once we're typechecked, record the types of all local variables defined
|
||||||
|
// in this input
|
||||||
|
do find_main(crate.expect("crate after cu_typeck"), sess) |blk| {
|
||||||
|
repl.program.register_new_vars(blk, tcx.expect("tcx after cu_typeck"));
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Stage 3: Actually run the code in the JIT
|
||||||
|
//
|
||||||
|
info!("actually running code");
|
||||||
|
let code = repl.program.code(input, &result);
|
||||||
|
debug!("actually running ^^^^^^ %?", (||{ println(code) })());
|
||||||
|
let input = driver::str_input(code.to_managed());
|
||||||
|
let cfg = driver::build_configuration(sess, binary, &input);
|
||||||
|
let outputs = driver::build_output_filenames(&input, &None, &None, [], sess);
|
||||||
|
let sess = driver::build_session(options, diagnostic::emit);
|
||||||
|
driver::compile_upto(sess, cfg, &input, driver::cu_everything,
|
||||||
|
Some(outputs));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Stage 4: Inform the program that computation is done so it can update all
|
||||||
|
// local variable bindings.
|
||||||
|
//
|
||||||
|
info!("cleaning up after code");
|
||||||
|
repl.program.consume_cache();
|
||||||
|
|
||||||
|
return repl;
|
||||||
|
|
||||||
|
fn parse_input(sess: session::Session, binary: @str,
|
||||||
|
input: &str) -> @ast::crate {
|
||||||
|
let code = fmt!("fn main() {\n %s \n}", input);
|
||||||
|
let input = driver::str_input(code.to_managed());
|
||||||
|
let cfg = driver::build_configuration(sess, binary, &input);
|
||||||
|
let outputs = driver::build_output_filenames(&input, &None, &None, [], sess);
|
||||||
|
let (crate, _) = driver::compile_upto(sess, cfg, &input,
|
||||||
|
driver::cu_parse, Some(outputs));
|
||||||
|
crate.expect("parsing should return a crate")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_main(crate: @ast::crate, sess: session::Session,
|
||||||
|
f: &fn(&ast::blk)) {
|
||||||
|
for crate.node.module.items.each |item| {
|
||||||
|
match item.node {
|
||||||
|
ast::item_fn(_, _, _, _, ref blk) => {
|
||||||
|
if item.ident == sess.ident_of("main") {
|
||||||
|
return f(blk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => fail!()
|
fail!("main function was expected somewhere...");
|
||||||
};
|
}
|
||||||
debug!("recording input into repl history");
|
|
||||||
record(repl, blk, token::get_ident_interner())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compiles a crate given by the filename as a library if the compiled
|
// Compiles a crate given by the filename as a library if the compiled
|
||||||
|
@ -265,8 +350,7 @@ fn run_cmd(repl: &mut Repl, _in: @io::Reader, _out: @io::Writer,
|
||||||
match cmd {
|
match cmd {
|
||||||
~"exit" => repl.running = false,
|
~"exit" => repl.running = false,
|
||||||
~"clear" => {
|
~"clear" => {
|
||||||
repl.view_items = ~"";
|
repl.program.clear();
|
||||||
repl.stmts = ~"";
|
|
||||||
|
|
||||||
// XXX: Win32 version of linenoise can't do this
|
// XXX: Win32 version of linenoise can't do this
|
||||||
//rl::clear();
|
//rl::clear();
|
||||||
|
@ -296,12 +380,9 @@ fn run_cmd(repl: &mut Repl, _in: @io::Reader, _out: @io::Writer,
|
||||||
for loaded_crates.each |crate| {
|
for loaded_crates.each |crate| {
|
||||||
let crate_path = Path(*crate);
|
let crate_path = Path(*crate);
|
||||||
let crate_dir = crate_path.dirname();
|
let crate_dir = crate_path.dirname();
|
||||||
let crate_name = crate_path.filename().get();
|
repl.program.record_extern(fmt!("extern mod %s;", *crate));
|
||||||
if !repl.view_items.contains(*crate) {
|
if !repl.lib_search_paths.contains(&crate_dir) {
|
||||||
repl.view_items += fmt!("extern mod %s;\n", crate_name);
|
repl.lib_search_paths.push(crate_dir);
|
||||||
if !repl.lib_search_paths.contains(&crate_dir) {
|
|
||||||
repl.lib_search_paths.push(crate_dir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if loaded_crates.is_empty() {
|
if loaded_crates.is_empty() {
|
||||||
|
@ -340,7 +421,7 @@ pub fn run_line(repl: &mut Repl, in: @io::Reader, out: @io::Writer, line: ~str,
|
||||||
-> Option<Repl> {
|
-> Option<Repl> {
|
||||||
if line.starts_with(":") {
|
if line.starts_with(":") {
|
||||||
// FIXME #5898: conflicts with Cell.take(), so can't be at the top level
|
// FIXME #5898: conflicts with Cell.take(), so can't be at the top level
|
||||||
use core::iterator::IteratorUtil;
|
use std::iterator::IteratorUtil;
|
||||||
|
|
||||||
// drop the : and the \n (one byte each)
|
// drop the : and the \n (one byte each)
|
||||||
let full = line.slice(1, line.len() - 1);
|
let full = line.slice(1, line.len() - 1);
|
||||||
|
@ -388,9 +469,9 @@ pub fn main() {
|
||||||
prompt: ~"rusti> ",
|
prompt: ~"rusti> ",
|
||||||
binary: copy args[0],
|
binary: copy args[0],
|
||||||
running: true,
|
running: true,
|
||||||
view_items: ~"",
|
|
||||||
lib_search_paths: ~[],
|
lib_search_paths: ~[],
|
||||||
stmts: ~""
|
|
||||||
|
program: Program::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let istty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;
|
let istty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;
|
||||||
|
@ -434,23 +515,24 @@ pub fn main() {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::io;
|
||||||
|
use std::iterator::IteratorUtil;
|
||||||
|
use program::Program;
|
||||||
use super::*;
|
use super::*;
|
||||||
use core::io;
|
|
||||||
|
|
||||||
fn repl() -> Repl {
|
fn repl() -> Repl {
|
||||||
Repl {
|
Repl {
|
||||||
prompt: ~"rusti> ",
|
prompt: ~"rusti> ",
|
||||||
binary: ~"rusti",
|
binary: ~"rusti",
|
||||||
running: true,
|
running: true,
|
||||||
view_items: ~"",
|
|
||||||
lib_search_paths: ~[],
|
lib_search_paths: ~[],
|
||||||
stmts: ~""
|
program: Program::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_cmds(cmds: &[&str]) {
|
fn run_program(prog: &str) {
|
||||||
let mut r = repl();
|
let mut r = repl();
|
||||||
for cmds.each |&cmd| {
|
for prog.split_iter('\n').advance |cmd| {
|
||||||
let result = run_line(&mut r, io::stdin(), io::stdout(),
|
let result = run_line(&mut r, io::stdin(), io::stdout(),
|
||||||
cmd.to_owned(), false);
|
cmd.to_owned(), false);
|
||||||
r = result.expect(fmt!("the command '%s' failed", cmd));
|
r = result.expect(fmt!("the command '%s' failed", cmd));
|
||||||
|
@ -469,18 +551,102 @@ mod tests {
|
||||||
// To get some interesting output, run with RUST_LOG=rusti::tests
|
// To get some interesting output, run with RUST_LOG=rusti::tests
|
||||||
|
|
||||||
debug!("hopefully this runs");
|
debug!("hopefully this runs");
|
||||||
run_cmds([""]);
|
run_program("");
|
||||||
|
|
||||||
debug!("regression test for #5937");
|
debug!("regression test for #5937");
|
||||||
run_cmds(["use std;", ""]);
|
run_program("use std::hashmap;");
|
||||||
|
|
||||||
debug!("regression test for #5784");
|
debug!("regression test for #5784");
|
||||||
run_cmds(["let a = 1;"]);
|
run_program("let a = 3;");
|
||||||
|
|
||||||
// XXX: can't spawn new tasks because the JIT code is cleaned up
|
// XXX: can't spawn new tasks because the JIT code is cleaned up
|
||||||
// after the main function is done.
|
// after the main function is done.
|
||||||
// debug!("regression test for #5803");
|
// debug!("regression test for #5803");
|
||||||
// run_cmds(["spawn( || println(\"Please don't segfault\") );",
|
// run_program("
|
||||||
// "do spawn { println(\"Please?\"); }"]);
|
// spawn( || println(\"Please don't segfault\") );
|
||||||
|
// do spawn { println(\"Please?\"); }
|
||||||
|
// ");
|
||||||
|
|
||||||
|
debug!("inferred integers are usable");
|
||||||
|
run_program("let a = 2;\n()\n");
|
||||||
|
run_program("
|
||||||
|
let a = 3;
|
||||||
|
let b = 4u;
|
||||||
|
assert!((a as uint) + b == 7)
|
||||||
|
");
|
||||||
|
|
||||||
|
debug!("local variables can be shadowed");
|
||||||
|
run_program("
|
||||||
|
let a = 3;
|
||||||
|
let a = 5;
|
||||||
|
assert!(a == 5)
|
||||||
|
");
|
||||||
|
|
||||||
|
debug!("strings are usable");
|
||||||
|
run_program("
|
||||||
|
let a = ~\"\";
|
||||||
|
let b = \"\";
|
||||||
|
let c = @\"\";
|
||||||
|
let d = a + b + c;
|
||||||
|
assert!(d.len() == 0);
|
||||||
|
");
|
||||||
|
|
||||||
|
debug!("vectors are usable");
|
||||||
|
run_program("
|
||||||
|
let a = ~[1, 2, 3];
|
||||||
|
let b = &[1, 2, 3];
|
||||||
|
let c = @[1, 2, 3];
|
||||||
|
let d = a + b + c;
|
||||||
|
assert!(d.len() == 9);
|
||||||
|
let e: &[int] = [];
|
||||||
|
");
|
||||||
|
|
||||||
|
debug!("structs are usable");
|
||||||
|
run_program("
|
||||||
|
struct A{ a: int }
|
||||||
|
let b = A{ a: 3 };
|
||||||
|
assert!(b.a == 3)
|
||||||
|
");
|
||||||
|
|
||||||
|
debug!("mutable variables");
|
||||||
|
run_program("
|
||||||
|
let mut a = 3;
|
||||||
|
a = 5;
|
||||||
|
let mut b = std::hashmap::HashSet::new::<int>();
|
||||||
|
b.insert(a);
|
||||||
|
assert!(b.contains(&5))
|
||||||
|
assert!(b.len() == 1)
|
||||||
|
");
|
||||||
|
|
||||||
|
debug!("functions are cached");
|
||||||
|
run_program("
|
||||||
|
fn fib(x: int) -> int { if x < 2 {x} else { fib(x - 1) + fib(x - 2) } }
|
||||||
|
let a = fib(3);
|
||||||
|
let a = a + fib(4);
|
||||||
|
assert!(a == 5)
|
||||||
|
");
|
||||||
|
|
||||||
|
debug!("modules are cached");
|
||||||
|
run_program("
|
||||||
|
mod b { pub fn foo() -> uint { 3 } }
|
||||||
|
assert!(b::foo() == 3)
|
||||||
|
");
|
||||||
|
|
||||||
|
debug!("multiple function definitions are allowed");
|
||||||
|
run_program("
|
||||||
|
fn f() {}
|
||||||
|
fn f() {}
|
||||||
|
f()
|
||||||
|
");
|
||||||
|
|
||||||
|
debug!("multiple item definitions are allowed");
|
||||||
|
run_program("
|
||||||
|
fn f() {}
|
||||||
|
mod f {}
|
||||||
|
struct f;
|
||||||
|
enum f {}
|
||||||
|
fn f() {}
|
||||||
|
f()
|
||||||
|
");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
use syntax::ast;
|
||||||
|
use syntax::print::pp;
|
||||||
|
use syntax::print::pprust;
|
||||||
|
use syntax::parse::token;
|
||||||
|
|
||||||
|
pub fn each_binding(l: @ast::local, f: @fn(@ast::Path, ast::node_id)) {
|
||||||
|
use syntax::visit;
|
||||||
|
|
||||||
|
let vt = visit::mk_simple_visitor(
|
||||||
|
@visit::SimpleVisitor {
|
||||||
|
visit_pat: |pat| {
|
||||||
|
match pat.node {
|
||||||
|
ast::pat_ident(_, path, _) => {
|
||||||
|
f(path, pat.id);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.. *visit::default_simple_visitor()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
(vt.visit_pat)(l.node.pat, ((), vt));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A utility function that hands off a pretty printer to a callback.
|
||||||
|
pub fn with_pp(intr: @token::ident_interner,
|
||||||
|
cb: &fn(@pprust::ps, @io::Writer)) -> ~str {
|
||||||
|
do io::with_str_writer |writer| {
|
||||||
|
let pp = pprust::rust_printer(writer, intr);
|
||||||
|
|
||||||
|
cb(pp, writer);
|
||||||
|
pp::eof(pp.s);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
#[allow(ctypes)];
|
|
||||||
#[allow(heap_memory)];
|
|
||||||
#[allow(implicit_copies)];
|
|
||||||
#[allow(managed_heap_memory)];
|
|
||||||
#[allow(non_camel_case_types)];
|
|
||||||
#[allow(owned_heap_memory)];
|
|
||||||
#[allow(path_statement)];
|
|
||||||
#[allow(unrecognized_lint)];
|
|
||||||
#[allow(unused_imports)];
|
|
||||||
#[allow(while_true)];
|
|
||||||
#[allow(unused_variable)];
|
|
||||||
#[allow(dead_assignment)];
|
|
||||||
#[allow(unused_unsafe)];
|
|
||||||
#[allow(unused_mut)];
|
|
||||||
|
|
||||||
extern mod std;
|
|
||||||
|
|
||||||
fn print<T>(result: T) {
|
|
||||||
println(fmt!("%?", result));
|
|
||||||
}
|
|
Loading…
Reference in New Issue