Skip to content

Commit 5ffb20b

Browse files
committed
Add builtin oct function. Add weakref.ref class.
1 parent 8d59874 commit 5ffb20b

File tree

9 files changed

+137
-12
lines changed

9 files changed

+137
-12
lines changed

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ Or use the interactive shell:
2626
- Full Python-3 environment entirely in Rust (not CPython bindings)
2727
- A clean implementation without compatibility hacks
2828

29+
# Documentation
30+
31+
Currently the project is in an early phase, and so is the documentation.
32+
33+
You can generate documentation by running:
34+
35+
```shell
36+
$ cargo doc
37+
```
38+
39+
Documentation HTML files can then be found in the `target/doc` directory.
40+
2941
# Code organization
3042

3143
- `parser`: python lexing, parsing and ast
@@ -64,6 +76,24 @@ There also are some unittests, you can run those will cargo:
6476
$ cargo test --all
6577
```
6678

79+
# Using another standard library
80+
81+
As of now the standard library is under construction.
82+
83+
You can play around
84+
with other standard libraries for python. For example,
85+
the [ouroboros library](https://github.com/pybee/ouroboros).
86+
87+
To do this, follow this method:
88+
89+
```shell
90+
$ cd ~/GIT
91+
$ git clone [email protected]:pybee/ouroboros.git
92+
$ export PYTHONPATH=~/GIT/ouroboros/ouroboros
93+
$ cd RustPython
94+
$ cargo run -- -c 'import statistics'
95+
```
96+
6797
# Compiling to WebAssembly
6898

6999
## Setup

parser/src/parser.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use super::python;
1212
use super::token;
1313

1414
pub fn read_file(filename: &Path) -> Result<String, String> {
15+
info!("Loading file {:?}", filename);
1516
match File::open(&filename) {
1617
Ok(mut file) => {
1718
let mut s = String::new();

tests/snippets/weakrefs.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from _weakref import ref
2+
3+
4+
class X:
5+
pass
6+
7+
8+
a = X()
9+
b = ref(a)
10+
11+
assert b() is a
12+

vm/src/builtins.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,19 @@ fn builtin_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
399399
}
400400
}
401401

402-
// builtin_oct
402+
fn builtin_oct(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
403+
arg_check!(vm, args, required = [(number, Some(vm.ctx.int_type()))]);
404+
405+
let n = objint::get_value(number);
406+
let s = if n.is_negative() {
407+
format!("-0o{:o}", n.abs())
408+
} else {
409+
format!("0o{:o}", n)
410+
};
411+
412+
Ok(vm.new_str(s))
413+
}
414+
403415
// builtin_open
404416

405417
fn builtin_ord(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -576,6 +588,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
576588
dict.insert(String::from("max"), ctx.new_rustfunc(builtin_max));
577589
dict.insert(String::from("min"), ctx.new_rustfunc(builtin_min));
578590
dict.insert(String::from("object"), ctx.object());
591+
dict.insert(String::from("oct"), ctx.new_rustfunc(builtin_oct));
579592
dict.insert(String::from("ord"), ctx.new_rustfunc(builtin_ord));
580593
dict.insert(String::from("next"), ctx.new_rustfunc(builtin_next));
581594
dict.insert(String::from("pow"), ctx.new_rustfunc(builtin_pow));

vm/src/bytecode.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
1-
/*
2-
* Implement python as a virtual machine with bytecodes.
3-
*/
4-
5-
/*
6-
let load_const_string = 0x16;
7-
let call_function = 0x64;
8-
*/
1+
//! Implement python as a virtual machine with bytecodes. This module
2+
//! implements bytecode structure.
93
104
/*
115
* Primitive instruction type, which can be encoded and decoded.
@@ -17,6 +11,8 @@ use rustpython_parser::ast;
1711
use std::collections::HashMap;
1812
use std::fmt;
1913

14+
/// Primary container of a single code object. Each python function has
15+
/// a codeobject. Also a module has a codeobject.
2016
#[derive(Clone, PartialEq)]
2117
pub struct CodeObject {
2218
pub instructions: Vec<Instruction>,
@@ -63,6 +59,7 @@ bitflags! {
6359

6460
pub type Label = usize;
6561

62+
/// A Single bytecode instruction.
6663
#[derive(Debug, Clone, PartialEq)]
6764
pub enum Instruction {
6865
Import {

vm/src/pyobject.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use num_traits::{One, Zero};
2424
use std::cell::RefCell;
2525
use std::collections::HashMap;
2626
use std::fmt;
27-
use std::rc::Rc;
27+
use std::rc::{Rc, Weak};
2828

2929
/* Python objects and references.
3030
@@ -53,6 +53,9 @@ pub type PyRef<T> = Rc<RefCell<T>>;
5353
/// to the python object by 1.
5454
pub type PyObjectRef = PyRef<PyObject>;
5555

56+
/// Same as PyObjectRef, except for being a weak reference.
57+
pub type PyObjectWeakRef = Weak<RefCell<PyObject>>;
58+
5659
/// Use this type for function which return a python object or and exception.
5760
/// Both the python object and the python exception are `PyObjectRef` types
5861
/// since exceptions are also python objects.
@@ -599,7 +602,7 @@ impl DictProtocol for PyObjectRef {
599602
PyObjectKind::Scope { ref mut scope } => {
600603
scope.locals.set_item(k, v);
601604
}
602-
_ => panic!("TODO"),
605+
ref k => panic!("TODO {:?}", k),
603606
};
604607
}
605608
}
@@ -722,6 +725,9 @@ pub enum PyObjectKind {
722725
dict: PyObjectRef,
723726
mro: Vec<PyObjectRef>,
724727
},
728+
WeakRef {
729+
referent: PyObjectWeakRef,
730+
},
725731
Instance {
726732
dict: PyObjectRef,
727733
},
@@ -741,6 +747,7 @@ impl fmt::Debug for PyObjectKind {
741747
&PyObjectKind::Sequence { elements: _ } => write!(f, "list or tuple"),
742748
&PyObjectKind::Dict { elements: _ } => write!(f, "dict"),
743749
&PyObjectKind::Set { elements: _ } => write!(f, "set"),
750+
&PyObjectKind::WeakRef { .. } => write!(f, "weakref"),
744751
&PyObjectKind::Iterator {
745752
position: _,
746753
iterated_obj: _,
@@ -813,6 +820,7 @@ impl PyObject {
813820
.collect::<Vec<_>>()
814821
.join(", ")
815822
),
823+
PyObjectKind::WeakRef { .. } => String::from("weakref"),
816824
PyObjectKind::None => String::from("None"),
817825
PyObjectKind::Class {
818826
ref name,

vm/src/stdlib/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod re;
88
mod time_module;
99
mod tokenize;
1010
mod types;
11+
mod weakref;
1112
use std::collections::HashMap;
1213

1314
use super::pyobject::{PyContext, PyObjectRef};
@@ -29,5 +30,6 @@ pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
2930
tokenize::mk_module as StdlibInitFunc,
3031
);
3132
modules.insert("types".to_string(), types::mk_module as StdlibInitFunc);
33+
modules.insert("_weakref".to_string(), weakref::mk_module as StdlibInitFunc);
3234
modules
3335
}

vm/src/stdlib/weakref.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//! Implementation in line with the python `weakref` module.
2+
//!
3+
//! See also:
4+
//! - [python weakref module](https://docs.python.org/3/library/weakref.html)
5+
//! - [rust weak struct](https://doc.rust-lang.org/std/rc/struct.Weak.html)
6+
//!
7+
8+
use super::super::obj::objtype;
9+
use super::super::pyobject::{
10+
AttributeProtocol, DictProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef,
11+
PyObjectWeakRef, PyResult, TypeProtocol,
12+
};
13+
use super::super::VirtualMachine;
14+
use std::rc::Rc;
15+
16+
pub fn mk_module(ctx: &PyContext) -> PyObjectRef {
17+
let py_mod = ctx.new_module(&"_weakref".to_string(), ctx.new_scope(None));
18+
19+
let py_ref_class = ctx.new_class("ref", ctx.object());
20+
py_ref_class.set_attr("__new__", ctx.new_rustfunc(ref_new));
21+
py_ref_class.set_attr("__call__", ctx.new_rustfunc(ref_call));
22+
py_mod.set_item("ref", py_ref_class);
23+
py_mod
24+
}
25+
26+
fn ref_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
27+
// TODO: check first argument for subclass of `ref`.
28+
arg_check!(vm, args, required = [(cls, None), (referent, None)]);
29+
let referent = Rc::downgrade(referent);
30+
Ok(PyObject::new(
31+
PyObjectKind::WeakRef { referent: referent },
32+
cls.clone(),
33+
))
34+
}
35+
36+
/// Dereference the weakref, and check if we still refer something.
37+
fn ref_call(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
38+
// TODO: check first argument for subclass of `ref`.
39+
arg_check!(vm, args, required = [(cls, None)]);
40+
let referent = get_value(cls);
41+
let py_obj = if let Some(obj) = referent.upgrade() {
42+
obj
43+
} else {
44+
vm.get_none()
45+
};
46+
Ok(py_obj)
47+
}
48+
49+
fn get_value(obj: &PyObjectRef) -> PyObjectWeakRef {
50+
if let PyObjectKind::WeakRef { referent } = &obj.borrow().kind {
51+
referent.clone()
52+
} else {
53+
panic!("Inner error getting weak ref {:?}", obj);
54+
}
55+
}

vm/src/vm.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ use super::sysmodule;
2727

2828
// Objects are live when they are on stack, or referenced by a name (for now)
2929

30+
/// Top level container of a python virtual machine. In theory you could
31+
/// create more instances of this struct and have them operate fully isolated.
3032
pub struct VirtualMachine {
3133
builtins: PyObjectRef,
3234
pub sys_module: PyObjectRef,
@@ -45,6 +47,7 @@ impl VirtualMachine {
4547
self.ctx.new_str(s)
4648
}
4749

50+
/// Create a new python bool object.
4851
pub fn new_bool(&self, b: bool) -> PyObjectRef {
4952
self.ctx.new_bool(b)
5053
}
@@ -73,6 +76,8 @@ impl VirtualMachine {
7376
self.new_exception(type_error, msg)
7477
}
7578

79+
/// Create a new python ValueError object. Useful for raising errors from
80+
/// python functions implemented in rust.
7681
pub fn new_value_error(&mut self, msg: String) -> PyObjectRef {
7782
let value_error = self.ctx.exceptions.value_error.clone();
7883
self.new_exception(value_error, msg)
@@ -225,7 +230,9 @@ impl VirtualMachine {
225230
} => self.invoke(function.clone(), args.insert(object.clone())),
226231
PyObjectKind::Instance { .. } => self.call_method_pyargs(&func_ref, "__call__", args),
227232
ref kind => {
228-
unimplemented!("invoke unimplemented for: {:?}", kind);
233+
// TODO: is it safe to just invoke __call__ otherwise?
234+
trace!("invoke __call__ for: {:?}", kind);
235+
self.call_method_pyargs(&func_ref, "__call__", args)
229236
}
230237
}
231238
}

0 commit comments

Comments
 (0)