Skip to content

Commit c4bdb63

Browse files
committed
2 parents 7728ee1 + de6fabf commit c4bdb63

6 files changed

Lines changed: 115 additions & 22 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
__pycache__
55
**/*.pytest_cache
66
.*sw*
7+
.repl_history.txt

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ env_logger="0.5.10"
1111
clap = "2.31.2"
1212
rustpython_parser = {path = "parser"}
1313
rustpython_vm = {path = "vm"}
14+
rustyline = "2.1.0"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Or use the interactive shell:
1717

1818
$ cargo run
1919
Welcome to rustpython
20-
>>>>> 2+2
20+
>>> 2+2
2121
4
2222

2323
<!-- Or use pip to install extra modules:

src/main.rs

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ extern crate env_logger;
66
extern crate log;
77
extern crate rustpython_parser;
88
extern crate rustpython_vm;
9+
extern crate rustyline;
910

1011
use clap::{App, Arg};
1112
use rustpython_parser::parser;
@@ -14,8 +15,8 @@ use rustpython_vm::print_exception;
1415
use rustpython_vm::pyobject::{PyObjectRef, PyResult};
1516
use rustpython_vm::VirtualMachine;
1617
use rustpython_vm::{compile, import};
17-
use std::io;
18-
use std::io::prelude::*;
18+
use rustyline::error::ReadlineError;
19+
use rustyline::Editor;
1920
use std::path::Path;
2021

2122
fn main() {
@@ -146,23 +147,30 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult {
146147

147148
// Read a single line:
148149
let mut input = String::new();
150+
let mut rl = Editor::<()>::new();
151+
152+
// TODO: Store the history in a proper XDG directory
153+
let repl_history_path = ".repl_history.txt";
154+
if rl.load_history(repl_history_path).is_err() {
155+
println!("No previous history.");
156+
}
157+
149158
loop {
150159
// TODO: modules dont support getattr / setattr yet
151160
//let prompt = match vm.get_attribute(vm.sys_module.clone(), "ps1") {
152161
// Ok(value) => objstr::get_value(&value),
153162
// Err(_) => ">>>>> ".to_string(),
154163
//};
155-
print!(">>>>> ");
156164

157-
io::stdout().flush().expect("Could not flush stdout");
158-
match io::stdin().read_line(&mut input) {
159-
Ok(0) => {
160-
break;
161-
}
162-
Ok(_) => {
165+
match rl.readline(">>> ") {
166+
Ok(line) => {
167+
input.push_str(&line);
168+
input.push_str("\n");
169+
163170
debug!("You entered {:?}", input);
164171
if shell_exec(vm, &input, vars.clone()) {
165172
// Line was complete.
173+
rl.add_history_entry(input.as_ref());
166174
input = String::new();
167175
} else {
168176
loop {
@@ -171,16 +179,11 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult {
171179
// Ok(value) => objstr::get_value(&value),
172180
// Err(_) => "..... ".to_string(),
173181
//};
174-
print!("..... ");
175-
io::stdout().flush().expect("Could not flush stdout");
176-
let mut line = String::new();
177-
match io::stdin().read_line(&mut line) {
178-
Ok(_) => {
179-
line = line
180-
.trim_right_matches(|c| c == '\r' || c == '\n')
181-
.to_string();
182+
match rl.readline("... ") {
183+
Ok(line) => {
182184
if line.len() == 0 {
183185
if shell_exec(vm, &input, vars.clone()) {
186+
rl.add_history_entry(input.as_ref());
184187
input = String::new();
185188
break;
186189
}
@@ -194,9 +197,21 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult {
194197
}
195198
}
196199
}
197-
Err(msg) => panic!("Error: {:?}", msg),
200+
Err(ReadlineError::Interrupted) => {
201+
// TODO: Raise a real KeyboardInterrupt exception
202+
println!("^C");
203+
break;
204+
}
205+
Err(ReadlineError::Eof) => {
206+
break;
207+
}
208+
Err(err) => {
209+
println!("Error: {:?}", err);
210+
break;
211+
}
198212
};
199213
}
214+
rl.save_history(repl_history_path).unwrap();
200215

201216
Ok(vm.get_none())
202217
}

tests/snippets/builtin_ord.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
assert ord("a") == 97
2+
assert ord("é") == 233
3+
assert ord("🤡") == 129313
4+
try:
5+
ord()
6+
except TypeError:
7+
pass
8+
else:
9+
assert False, "TypeError not raised when ord() is called with no argument"
10+
11+
try:
12+
ord("")
13+
except TypeError:
14+
pass
15+
else:
16+
assert False, "TypeError not raised when ord() is called with an empty string"
17+
18+
try:
19+
ord("ab")
20+
except TypeError:
21+
pass
22+
else:
23+
assert False, "TypeError not raised when ord() is called with more than one character"

vm/src/builtins.rs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,36 @@ fn builtin_map(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
293293
Ok(vm.ctx.new_list(elements))
294294
}
295295

296-
// builtin_max
296+
fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
297+
arg_check!(vm, args, required = [(x, None), (y, None)]);
298+
299+
let order = vm.call_method(x, "__gt__", vec![y.clone()])?;
300+
301+
if objbool::get_value(&order) {
302+
Ok(x.clone())
303+
} else {
304+
Ok(y.clone())
305+
}
306+
}
307+
297308
// builtin_memoryview
298-
// builtin_min
309+
310+
fn builtin_min(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
311+
arg_check!(
312+
vm,
313+
args,
314+
required = [(x, Some(vm.ctx.int_type())), (y, Some(vm.ctx.int_type()))]
315+
);
316+
317+
use std::cmp::Ordering;
318+
319+
let order = x.cmp(y);
320+
321+
match order {
322+
Ordering::Less | Ordering::Equal => Ok(x.clone()),
323+
_ => Ok(y.clone()),
324+
}
325+
}
299326

300327
fn builtin_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
301328
arg_check!(
@@ -323,7 +350,27 @@ fn builtin_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
323350
// builtin_object
324351
// builtin_oct
325352
// builtin_open
326-
// builtin_ord
353+
354+
fn builtin_ord(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
355+
arg_check!(vm, args, required = [(string, Some(vm.ctx.str_type()))]);
356+
let string = objstr::get_value(string);
357+
let string_len = string.chars().count();
358+
if string_len > 1 {
359+
return Err(vm.new_type_error(
360+
format!(
361+
"ord() expected a character, but string of length {} found",
362+
string_len
363+
)
364+
.to_string(),
365+
));
366+
}
367+
match string.chars().next() {
368+
Some(character) => Ok(vm.context().new_int(character as i32)),
369+
None => Err(vm.new_type_error(
370+
"ord() could not guess the integer representing this character".to_string(),
371+
)),
372+
}
373+
}
327374

328375
fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
329376
arg_check!(
@@ -454,6 +501,12 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
454501
dict.insert(String::from("list"), ctx.list_type());
455502
dict.insert(String::from("locals"), ctx.new_rustfunc(builtin_locals));
456503
dict.insert(String::from("map"), ctx.new_rustfunc(builtin_map));
504+
505+
dict.insert(String::from("max"), ctx.new_rustfunc(builtin_max));
506+
dict.insert(String::from("min"), ctx.new_rustfunc(builtin_min));
507+
508+
dict.insert(String::from("ord"), ctx.new_rustfunc(builtin_ord));
509+
457510
dict.insert(String::from("next"), ctx.new_rustfunc(builtin_next));
458511
dict.insert(String::from("pow"), ctx.new_rustfunc(builtin_pow));
459512
dict.insert(String::from("print"), ctx.new_rustfunc(builtin_print));

0 commit comments

Comments
 (0)