Skip to content

Commit 2220ed7

Browse files
committed
Abstract over the readline implementation
1 parent 04a5aec commit 2220ed7

File tree

3 files changed

+266
-229
lines changed

3 files changed

+266
-229
lines changed

src/main.rs

Lines changed: 5 additions & 228 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,23 @@ extern crate env_logger;
55
extern crate log;
66

77
use clap::{App, AppSettings, Arg, ArgMatches};
8-
use rustpython_compiler::{compile, error::CompileError, error::CompileErrorType};
9-
use rustpython_parser::error::ParseErrorType;
8+
use rustpython_compiler::compile;
109
use rustpython_vm::{
1110
import, match_class,
1211
obj::{objint::PyInt, objtuple::PyTuple, objtype},
1312
print_exception,
14-
pyobject::{ItemProtocol, PyObjectRef, PyResult},
13+
pyobject::{ItemProtocol, PyResult},
1514
scope::Scope,
1615
util, PySettings, VirtualMachine,
1716
};
18-
use std::convert::TryInto;
1917

18+
use std::convert::TryInto;
2019
use std::env;
2120
use std::path::PathBuf;
2221
use std::process;
2322
use std::str::FromStr;
2423

25-
mod shell_helper;
24+
mod shell;
2625

2726
fn main() {
2827
#[cfg(feature = "flame-it")]
@@ -367,7 +366,7 @@ fn run_rustpython(vm: &VirtualMachine, matches: &ArgMatches) -> PyResult<()> {
367366
} else if let Some(filename) = matches.value_of("script") {
368367
run_script(&vm, scope, filename)?
369368
} else {
370-
run_shell(&vm, scope)?;
369+
shell::run_shell(&vm, scope)?;
371370
}
372371

373372
Ok(())
@@ -459,225 +458,3 @@ fn test_run_script() {
459458
let r = run_script(&vm, vm.new_scope_with_builtins(), "tests/snippets/dir_main");
460459
assert!(r.is_ok());
461460
}
462-
463-
enum ShellExecResult {
464-
Ok,
465-
PyErr(PyObjectRef),
466-
Continue,
467-
}
468-
469-
fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> ShellExecResult {
470-
match vm.compile(source, compile::Mode::Single, "<stdin>".to_string()) {
471-
Ok(code) => {
472-
match vm.run_code_obj(code, scope.clone()) {
473-
Ok(value) => {
474-
// Save non-None values as "_"
475-
if !vm.is_none(&value) {
476-
let key = "_";
477-
scope.globals.set_item(key, value, vm).unwrap();
478-
}
479-
ShellExecResult::Ok
480-
}
481-
Err(err) => ShellExecResult::PyErr(err),
482-
}
483-
}
484-
Err(CompileError {
485-
error: CompileErrorType::Parse(ParseErrorType::EOF),
486-
..
487-
}) => ShellExecResult::Continue,
488-
Err(err) => ShellExecResult::PyErr(vm.new_syntax_error(&err)),
489-
}
490-
}
491-
492-
#[cfg(not(target_os = "wasi"))]
493-
fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
494-
use rustyline::{error::ReadlineError, CompletionType, Config, Editor};
495-
496-
println!(
497-
"Welcome to the magnificent Rust Python {} interpreter \u{1f631} \u{1f596}",
498-
crate_version!()
499-
);
500-
501-
// Read a single line:
502-
let mut repl = Editor::with_config(
503-
Config::builder()
504-
.completion_type(CompletionType::List)
505-
.build(),
506-
);
507-
repl.set_helper(Some(shell_helper::ShellHelper::new(vm, scope.clone())));
508-
let mut full_input = String::new();
509-
510-
// Retrieve a `history_path_str` dependent on the OS
511-
let repl_history_path = match dirs::config_dir() {
512-
Some(mut path) => {
513-
path.push("rustpython");
514-
path.push("repl_history.txt");
515-
path
516-
}
517-
None => ".repl_history.txt".into(),
518-
};
519-
520-
if !repl_history_path.exists() {
521-
if let Some(parent) = repl_history_path.parent() {
522-
std::fs::create_dir_all(parent).unwrap();
523-
}
524-
}
525-
526-
if repl.load_history(&repl_history_path).is_err() {
527-
println!("No previous history.");
528-
}
529-
530-
let mut continuing = false;
531-
532-
loop {
533-
let prompt_name = if continuing { "ps2" } else { "ps1" };
534-
let prompt = vm
535-
.get_attribute(vm.sys_module.clone(), prompt_name)
536-
.and_then(|prompt| vm.to_str(&prompt));
537-
let prompt = match prompt {
538-
Ok(ref s) => s.as_str(),
539-
Err(_) => "",
540-
};
541-
let result = match repl.readline(prompt) {
542-
Ok(line) => {
543-
debug!("You entered {:?}", line);
544-
545-
repl.add_history_entry(line.trim_end());
546-
547-
let stop_continuing = line.is_empty();
548-
549-
if full_input.is_empty() {
550-
full_input = line;
551-
} else {
552-
full_input.push_str(&line);
553-
}
554-
full_input.push_str("\n");
555-
556-
if continuing {
557-
if stop_continuing {
558-
continuing = false;
559-
} else {
560-
continue;
561-
}
562-
}
563-
564-
match shell_exec(vm, &full_input, scope.clone()) {
565-
ShellExecResult::Ok => {
566-
full_input.clear();
567-
Ok(())
568-
}
569-
ShellExecResult::Continue => {
570-
continuing = true;
571-
Ok(())
572-
}
573-
ShellExecResult::PyErr(err) => {
574-
full_input.clear();
575-
Err(err)
576-
}
577-
}
578-
}
579-
Err(ReadlineError::Interrupted) => {
580-
continuing = false;
581-
full_input.clear();
582-
let keyboard_interrupt = vm
583-
.new_empty_exception(vm.ctx.exceptions.keyboard_interrupt.clone())
584-
.unwrap();
585-
Err(keyboard_interrupt)
586-
}
587-
Err(ReadlineError::Eof) => {
588-
break;
589-
}
590-
Err(err) => {
591-
eprintln!("Readline error: {:?}", err);
592-
break;
593-
}
594-
};
595-
596-
if let Err(exc) = result {
597-
if objtype::isinstance(&exc, &vm.ctx.exceptions.system_exit) {
598-
repl.save_history(&repl_history_path).unwrap();
599-
return Err(exc);
600-
}
601-
print_exception(vm, &exc);
602-
}
603-
}
604-
repl.save_history(&repl_history_path).unwrap();
605-
606-
Ok(())
607-
}
608-
609-
#[cfg(target_os = "wasi")]
610-
fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
611-
use std::io::prelude::*;
612-
use std::io::{self, BufRead};
613-
614-
println!(
615-
"Welcome to the magnificent Rust Python {} interpreter \u{1f631} \u{1f596}",
616-
crate_version!()
617-
);
618-
619-
// Read a single line:
620-
let mut input = String::new();
621-
let mut continuing = false;
622-
623-
loop {
624-
let prompt_name = if continuing { "ps2" } else { "ps1" };
625-
let prompt = vm
626-
.get_attribute(vm.sys_module.clone(), prompt_name)
627-
.and_then(|prompt| vm.to_str(&prompt));
628-
let prompt = match prompt {
629-
Ok(ref s) => s.as_str(),
630-
Err(_) => "",
631-
};
632-
print!("{}", prompt);
633-
io::stdout().flush().ok().expect("Could not flush stdout");
634-
635-
let stdin = io::stdin();
636-
637-
let result = match stdin.lock().lines().next().unwrap() {
638-
Ok(line) => {
639-
debug!("You entered {:?}", line);
640-
let stop_continuing = line.is_empty();
641-
642-
if input.is_empty() {
643-
input = line;
644-
} else {
645-
input.push_str(&line);
646-
}
647-
input.push_str("\n");
648-
649-
if continuing {
650-
if stop_continuing {
651-
continuing = false;
652-
} else {
653-
continue;
654-
}
655-
}
656-
657-
match shell_exec(vm, &input, scope.clone()) {
658-
ShellExecResult::Ok => {
659-
input.clear();
660-
Ok(())
661-
}
662-
ShellExecResult::Continue => {
663-
continuing = true;
664-
Ok(())
665-
}
666-
ShellExecResult::PyErr(err) => {
667-
input.clear();
668-
Err(err)
669-
}
670-
}
671-
}
672-
Err(err) => {
673-
eprintln!("Readline error: {:?}", err);
674-
break;
675-
}
676-
};
677-
678-
if let Err(exc) = result {
679-
print_exception(vm, &exc);
680-
}
681-
}
682-
Ok(())
683-
}

0 commit comments

Comments
 (0)