Add interactive shell

This commit is contained in:
Windel Bouwman committed 2018-07-09 22:06:49 +02:00
1 parent 7d20869dc0
commit 2354102795
47 files changed
+87 -29

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
#[macro_use]
extern crate log;
mod parser;
pub mod parser;
mod python;
pub mod ast;
mod token;
+42 -4
View File
@@ -11,7 +11,11 @@ mod compile;
use clap::{Arg, App};
use std::path::Path;
use rustpython_parser::parse;
use rustpython_parser::parser::parse_source;
use rustpython_vm::evaluate;
use rustpython_vm::pyobject::PyObjectRef;
use std::io;
use std::io::prelude::*;
fn main() {
@@ -21,7 +25,7 @@ fn main() {
.author(crate_authors!())
.about("Rust implementation of the Python language")
.arg(Arg::with_name("script")
.required(true)
.required(false)
.index(1))
.arg(Arg::with_name("v")
.short("v")
@@ -29,10 +33,16 @@ fn main() {
.help("Give the verbosity"))
.get_matches();
// Figure out the filename:
let script_file = matches.value_of("script").unwrap_or("foo");
debug!("Running file {}", script_file);
// Figure out if a script was passed:
match matches.value_of("script") {
None => run_shell(),
Some(filename) => run_script(&filename.to_string()),
}
}
fn run_script(script_file: &String) {
debug!("Running file {}", script_file);
// Parse an ast from it:
let filepath = Path::new(script_file);
match parse(filepath) {
@@ -49,3 +59,31 @@ fn main() {
}
}
fn run_shell() {
println!("Welcome to the magnificent Rust Python interpreter");
// Read a single line:
loop {
let mut input = String::new();
print!(">>>");
io::stdout().flush().ok().expect("Could not flush stdout");
io::stdin().read_line(&mut input);
println!("You entered {:?}", input);
let result = eval(&input);
println!("{:?}", result);
}
}
fn eval(source: &String) -> Result<PyObjectRef, PyObjectRef> {
match parse_source(source) {
Ok(program) => {
debug!("Got ast: {:?}", program);
let bytecode = compile::compile(program);
debug!("Code object: {:?}", bytecode);
evaluate(bytecode)
},
Err(msg) => {
panic!("Parsing went horribly wrong: {}", msg);
},
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -3,6 +3,7 @@
# in the tests folder.
import sys
import os
import unittest
import glob
@@ -15,7 +16,7 @@ import compile_code
logger = logging.getLogger('tests')
ROOT_DIR = '..'
TEST_DIR = os.path.abspath(os.path.join(ROOT_DIR, 'tests'))
TEST_DIR = os.path.abspath(os.path.join(ROOT_DIR, 'tests', 'snippets'))
CPYTHON_RUNNER_DIR = os.path.abspath(os.path.join(ROOT_DIR, 'py_code_object'))
RUSTPYTHON_RUNNER_DIR = os.path.abspath(os.path.join(ROOT_DIR))
@@ -32,6 +33,8 @@ def perform_test(filename, method):
logger.info('Running %s via %s', filename, method)
if method == 'cpython':
run_via_cpython(filename)
elif method == 'cpython_bytecode':
run_via_cpython_bytecode(filename)
elif method == 'rustpython':
run_via_rustpython(filename)
else:
@@ -39,6 +42,11 @@ def perform_test(filename, method):
def run_via_cpython(filename):
""" Simply invoke python itself on the script """
subprocess.check_call([sys.executable, filename])
def run_via_cpython_bytecode(filename):
# Step1: Create bytecode file:
bytecode_filename = filename + '.bytecode'
with open(bytecode_filename, 'w') as f:
@@ -77,7 +85,7 @@ def create_test_function(cls, filename, method):
def populate(cls):
""" Decorator function which can populate a unittest.TestCase class """
for method in ['cpython', 'rustpython']:
for method in ['cpython', 'cpython_bytecode', 'rustpython']:
for filename in get_test_files():
create_test_function(cls, filename, method)
return cls
+1 -1
View File
@@ -5,7 +5,7 @@ extern crate env_logger;
//extern crate eval; use eval::eval::*;
// use py_code_object::{Function, NativeType, PyCodeObject};
mod pyobject;
pub mod pyobject;
pub mod bytecode;
mod builtins;
mod vm;
+3
View File
@@ -47,6 +47,9 @@ pub enum PyObject {
stop: Option<i32>,
step: Option<i32>,
},
NameError { // TODO: improve python object and type system
name: String,
},
Code {
code: bytecode::CodeObject,
},
+30 -21
View File
@@ -15,14 +15,14 @@ use super::pyobject::{PyObject, PyObjectRef};
// use objects::objects;
// Container of the virtual machine state:
pub fn evaluate(code: bytecode::CodeObject) {
pub fn evaluate(code: bytecode::CodeObject) -> Result<PyObjectRef, PyObjectRef> {
let mut vm = VirtualMachine::new();
// Register built in function:
// vm.scope.insert(String::from("print"), PyObject::RustFunction { function: builtins::print }.into_ref());
// { stack: Vec::new() };
vm.run(Rc::new(code));
vm.run(Rc::new(code))
}
// Objects are live when they are on stack, or referenced by a name (for now)
@@ -51,11 +51,6 @@ struct VirtualMachine {
frames: Vec<Frame>,
}
enum ExeResult {
Value { value: PyObjectRef },
Exception,
}
impl Frame {
pub fn new(code: Rc<bytecode::CodeObject>, callargs: HashMap<String, PyObjectRef>, globals: Option<HashMap<String, PyObjectRef>>) -> Frame {
//populate the globals and locals
@@ -122,22 +117,25 @@ impl VirtualMachine {
self.current_frame().pop_multiple(count)
}
fn run(&mut self, code: Rc<bytecode::CodeObject>) {
fn run(&mut self, code: Rc<bytecode::CodeObject>) -> Result<PyObjectRef, PyObjectRef> {
let frame = Frame::new(code, HashMap::new(), None);
self.run_frame(frame);
self.run_frame(frame)
}
// The Option<i32> is the return value of the frame, remove when we have implemented frame
// TODO: read the op codes directly from the internal code object
fn run_frame(&mut self, mut frame: Frame) -> PyObjectRef {
fn run_frame(&mut self, mut frame: Frame) -> Result<PyObjectRef, PyObjectRef> {
self.frames.push(frame);
let value = loop {
let result = self.execute_instruction();
match result {
None => {},
Some(ExeResult::Value { value }) => { break value; },
Some(ExeResult::Exception) => { panic!("TODO"); }
Some(Ok(value)) => { break Ok(value); },
Some(Err(value)) => {
// TODO: unwind stack on exception.
panic!("TODO {:?}", value);
}
}
/*if curr_frame.blocks.len() > 0 {
self.manage_block_stack(&why);
@@ -238,7 +236,7 @@ impl VirtualMachine {
}
// Execute a single instruction:
fn execute_instruction(&mut self) -> Option<ExeResult> {
fn execute_instruction(&mut self) -> Option<Result<PyObjectRef, PyObjectRef>> {
// let frame = self.frames.last_mut().unwrap();
let instruction = {
let frame = self.current_frame();
@@ -268,9 +266,14 @@ impl VirtualMachine {
},
&bytecode::Instruction::LoadName { ref name } => {
// Lookup name in scope and put it onto the stack!
let obj = self.current_frame().locals[name].clone();
self.push_value(obj);
None
if self.current_frame().locals.contains_key(name) {
let obj = self.current_frame().locals[name].clone();
self.push_value(obj);
None
} else {
let name_error = PyObject::NameError { name: name.to_string() };
Some(Err(name_error.into_ref()))
}
},
&bytecode::Instruction::StoreName { ref name } => {
// take top of stack and assign in scope:
@@ -342,7 +345,7 @@ impl VirtualMachine {
},
&bytecode::Instruction::ReturnValue => {
let value = self.pop_value();
Some(ExeResult::Value { value })
Some(Ok(value))
},
/*
&bytecode::Instruction::PushBlock { start, end } => {
@@ -401,9 +404,9 @@ impl VirtualMachine {
let mut args: Vec<PyObjectRef> = self.pop_multiple(count);
let func_ref = self.pop_value();
let f = func_ref.borrow();// = &*func_ref.borrow();
let result = match *f {
let func_result = match *f {
PyObject::RustFunction { ref function } => {
f.call(args)
Ok(f.call(args))
}
PyObject::Function { ref code } => {
let frame = Frame::new(Rc::new(code.clone()), HashMap::new(), None);
@@ -414,11 +417,17 @@ impl VirtualMachine {
panic!("Not impl");
}
};
self.push_value(result);
match func_result {
Ok(value) => { self.push_value(value); None },
Err(value) => {
// Ripple exception upwards:
Some(Err(value))
}
}
// call_stack.push();
// If a builtin function, then call directly, otherwise, execute it?
// execute(function.code);
None
},
/* TODO
&bytecode::Instruction::Jump { target } => {