From 1e3b45ee0dc68bcce8712624beab4fc1da787cd7 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 29 Oct 2018 21:55:58 +0100 Subject: [PATCH] Add complex basic type --- Cargo.lock | 16 ++++++++ parser/src/ast.rs | 1 + parser/src/token.rs | 1 + tests/snippets/basic_types.py | 4 ++ vm/Cargo.toml | 1 + vm/src/builtins.rs | 2 +- vm/src/bytecode.rs | 5 ++- vm/src/compile.rs | 10 +++-- vm/src/frame.rs | 1 + vm/src/lib.rs | 3 ++ vm/src/obj/mod.rs | 1 + vm/src/obj/objcomplex.rs | 71 +++++++++++++++++++++++++++++++++++ vm/src/obj/objfloat.rs | 12 +++++- vm/src/obj/objlist.rs | 8 +++- vm/src/pyobject.rs | 19 ++++++++++ vm/src/stdlib/ast.rs | 4 ++ 16 files changed, 149 insertions(+), 10 deletions(-) create mode 100644 vm/src/obj/objcomplex.rs diff --git a/Cargo.lock b/Cargo.lock index 135b849ff..da6b7b79d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -409,6 +409,19 @@ name = "nodrop" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "num-complex" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ordermap" version = "0.3.5" @@ -595,6 +608,7 @@ dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustpython_parser 0.0.1", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", @@ -916,6 +930,8 @@ dependencies = [ "checksum new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdc457076c78ab54d5e0d6fa7c47981757f1e34dc39ff92787f217dede586c4" "checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "107b9be86cd2481930688277b675b0114578227f034674726605b8a482d8baf8" +"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" "checksum phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "05a079dd052e7b674d21cb31cbb6c05efd56a2cd2827db7692e2f1a507ebd998" diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 32e37a940..fdd3711fc 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -300,4 +300,5 @@ pub enum Comparison { pub enum Number { Integer { value: i32 }, Float { value: f64 }, + Complex { real: f64, imag: f64 }, } diff --git a/parser/src/token.rs b/parser/src/token.rs index b31f4cde5..e3b63e06b 100644 --- a/parser/src/token.rs +++ b/parser/src/token.rs @@ -3,6 +3,7 @@ pub enum Tok { Name { name: String }, Number { value: String }, + Complex { real: f64, imag: f64 }, String { value: String }, Bytes { value: Vec }, Newline, diff --git a/tests/snippets/basic_types.py b/tests/snippets/basic_types.py index 6d279f725..49e4099f3 100644 --- a/tests/snippets/basic_types.py +++ b/tests/snippets/basic_types.py @@ -37,3 +37,7 @@ except TypeError: pass assert int() == 0 + +a = complex(2, 4) +assert type(a) is complex +assert type(a + a) is complex diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 5897d829e..b6c603856 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Shing Lyu "] [dependencies] bitflags = "1.0.4" +num-complex = "0.2" log = "0.3" rustpython_parser = {path = "../parser"} serde = "1.0.66" diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 112582675..8ea6586cb 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -126,7 +126,6 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { compile::compile(vm, &source, mode, None) } -// builtin_complex // builtin_delattr fn builtin_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -543,6 +542,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { dict.insert(String::from("bytes"), ctx.bytes_type()); dict.insert(String::from("chr"), ctx.new_rustfunc(builtin_chr)); dict.insert(String::from("compile"), ctx.new_rustfunc(builtin_compile)); + dict.insert(String::from("complex"), ctx.complex_type()); dict.insert(String::from("dict"), ctx.dict_type()); dict.insert(String::from("divmod"), ctx.new_rustfunc(builtin_divmod)); dict.insert(String::from("dir"), ctx.new_rustfunc(builtin_dir)); diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 0b101b2a0..ced8e7a2a 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -10,9 +10,9 @@ let call_function = 0x64; /* * Primitive instruction type, which can be encoded and decoded. */ -extern crate rustpython_parser; -use self::rustpython_parser::ast; +use num_complex::Complex64; +use rustpython_parser::ast; use std::collections::HashMap; use std::fmt; @@ -197,6 +197,7 @@ pub enum CallType { pub enum Constant { Integer { value: i32 }, // TODO: replace by arbitrary big int math. Float { value: f64 }, + Complex { value: Complex64 }, Boolean { value: bool }, String { value: String }, Bytes { value: Vec }, diff --git a/vm/src/compile.rs b/vm/src/compile.rs index bd47474c5..afe0758ea 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -6,12 +6,11 @@ * https://github.com/micropython/micropython/blob/master/py/compile.c */ -extern crate rustpython_parser; - -use self::rustpython_parser::{ast, parser}; use super::bytecode::{self, CallType, CodeObject, Instruction}; use super::pyobject::{PyObject, PyObjectKind, PyResult}; use super::vm::VirtualMachine; +use num_complex::Complex64; +use rustpython_parser::{ast, parser}; struct Compiler { code_object_stack: Vec, @@ -846,6 +845,9 @@ impl Compiler { let const_value = match value { ast::Number::Integer { value } => bytecode::Constant::Integer { value: *value }, ast::Number::Float { value } => bytecode::Constant::Float { value: *value }, + ast::Number::Complex { real, imag } => bytecode::Constant::Complex { + value: Complex64::new(*real, *imag), + }, }; self.emit(Instruction::LoadConst { value: const_value }); } @@ -1301,8 +1303,8 @@ mod tests { use super::bytecode::CodeObject; use super::bytecode::Constant::*; use super::bytecode::Instruction::*; - use super::rustpython_parser::parser; use super::Compiler; + use rustpython_parser::parser; fn compile_exec(source: &str) -> CodeObject { let mut compiler = Compiler::new(); compiler.push_new_code_object(Option::None, "".to_string()); diff --git a/vm/src/frame.rs b/vm/src/frame.rs index afe38dd27..661820a95 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -1074,6 +1074,7 @@ impl Frame { match *value { bytecode::Constant::Integer { ref value } => vm.ctx.new_int(*value), bytecode::Constant::Float { ref value } => vm.ctx.new_float(*value), + bytecode::Constant::Complex { ref value } => vm.ctx.new_complex(*value), bytecode::Constant::String { ref value } => vm.new_str(value.clone()), bytecode::Constant::Bytes { ref value } => vm.ctx.new_bytes(value.clone()), bytecode::Constant::Boolean { ref value } => vm.new_bool(value.clone()), diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 9c154abdf..d05e43cd4 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -3,9 +3,12 @@ extern crate bitflags; #[macro_use] extern crate log; // extern crate env_logger; +extern crate num_complex; extern crate serde; extern crate serde_json; +extern crate rustpython_parser; + //extern crate eval; use eval::eval::*; // use py_code_object::{Function, NativeType, PyCodeObject}; diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 67fdccf09..643504e90 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -1,5 +1,6 @@ pub mod objbool; pub mod objbytes; +pub mod objcomplex; pub mod objdict; pub mod objfloat; pub mod objfunction; diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs new file mode 100644 index 000000000..464d60ede --- /dev/null +++ b/vm/src/obj/objcomplex.rs @@ -0,0 +1,71 @@ +use super::super::pyobject::{ + AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, + TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objfloat; +use super::objtype; +use num_complex::Complex64; + +pub fn init(context: &PyContext) { + let ref complex_type = context.complex_type; + complex_type.set_attr("__add__", context.new_rustfunc(complex_add)); + complex_type.set_attr("__new__", context.new_rustfunc(complex_new)); + complex_type.set_attr("__repr__", context.new_rustfunc(complex_repr)); +} + +pub fn get_value(obj: &PyObjectRef) -> Complex64 { + if let PyObjectKind::Complex { value } = &obj.borrow().kind { + *value + } else { + panic!("Inner error getting complex"); + } +} + +fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(cls, None)], + optional = [(real, None), (imag, None)] + ); + + if !objtype::issubclass(cls, &vm.ctx.complex_type()) { + return Err(vm.new_type_error(format!("{:?} is not a subtype of complex", cls))); + } + + let real = match real { + None => 0.0, + Some(value) => objfloat::make_float(vm, value)?, + }; + + let imag = match imag { + None => 0.0, + Some(value) => objfloat::make_float(vm, value)?, + }; + + let value = Complex64::new(real, imag); + + Ok(PyObject::new(PyObjectKind::Complex { value }, cls.clone())) +} + +fn complex_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.complex_type())), (i2, None)] + ); + + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.complex_type()) { + Ok(vm.ctx.new_complex(v1 + get_value(i2))) + } else { + Err(vm.new_type_error(format!("Cannot add {:?} and {:?}", i, i2))) + } +} + +fn complex_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(obj, Some(vm.ctx.complex_type()))]); + let v = get_value(obj); + Ok(vm.new_str(v.to_string())) +} diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 9ca0258c9..116c65020 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -fn float_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { +fn float_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(float, Some(vm.ctx.float_type()))]); let v = get_value(float); Ok(vm.new_str(v.to_string())) @@ -38,6 +38,16 @@ pub fn get_value(obj: &PyObjectRef) -> f64 { } } +pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> Result { + if objtype::isinstance(obj, &vm.ctx.float_type()) { + Ok(get_value(obj)) + } else if objtype::isinstance(obj, &vm.ctx.int_type()) { + Ok(objint::get_value(obj) as f64) + } else { + Err(vm.new_type_error(format!("Cannot cast {:?} to float", obj))) + } +} + fn set_value(obj: &PyObjectRef, value: f64) { obj.borrow_mut().kind = PyObjectKind::Float { value }; } diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 6f50d7fcd..ca0b6a372 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -1,5 +1,6 @@ use super::super::pyobject::{ - AttributeProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol, + AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, + TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; @@ -56,7 +57,10 @@ fn list_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } }; - Ok(vm.ctx.new_list(elements)) + Ok(PyObject::new( + PyObjectKind::List { elements: elements }, + cls.clone(), + )) } fn list_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 1ea875435..98331656b 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -3,6 +3,7 @@ use super::exceptions; use super::frame::Frame; use super::obj::objbool; use super::obj::objbytes; +use super::obj::objcomplex; use super::obj::objdict; use super::obj::objfloat; use super::obj::objfunction; @@ -16,6 +17,7 @@ use super::obj::objstr; use super::obj::objtuple; use super::obj::objtype; use super::vm::VirtualMachine; +use num_complex::Complex64; use std::cell::RefCell; use std::collections::HashMap; use std::fmt; @@ -57,6 +59,7 @@ pub struct PyContext { pub dict_type: PyObjectRef, pub int_type: PyObjectRef, pub float_type: PyObjectRef, + pub complex_type: PyObjectRef, pub bytes_type: PyObjectRef, pub bool_type: PyObjectRef, pub true_value: PyObjectRef, @@ -130,6 +133,7 @@ impl PyContext { let set_type = create_type("set", &type_type, &object_type, &dict_type); let int_type = create_type("int", &type_type, &object_type, &dict_type); let float_type = create_type("float", &type_type, &object_type, &dict_type); + let complex_type = create_type("complex", &type_type, &object_type, &dict_type); let bytes_type = create_type("bytes", &type_type, &object_type, &dict_type); let tuple_type = create_type("tuple", &type_type, &object_type, &dict_type); let iter_type = create_type("iter", &type_type, &object_type, &dict_type); @@ -146,6 +150,7 @@ impl PyContext { let context = PyContext { int_type: int_type, float_type: float_type, + complex_type: complex_type, bytes_type: bytes_type, list_type: list_type, set_type: set_type, @@ -176,6 +181,7 @@ impl PyContext { objgenerator::init(&context); objint::init(&context); objfloat::init(&context); + objcomplex::init(&context); objbytes::init(&context); objstr::init(&context); objtuple::init(&context); @@ -193,6 +199,10 @@ impl PyContext { self.float_type.clone() } + pub fn complex_type(&self) -> PyObjectRef { + self.complex_type.clone() + } + pub fn bytes_type(&self) -> PyObjectRef { self.bytes_type.clone() } @@ -262,6 +272,10 @@ impl PyContext { PyObject::new(PyObjectKind::Float { value: i }, self.float_type()) } + pub fn new_complex(&self, i: Complex64) -> PyObjectRef { + PyObject::new(PyObjectKind::Complex { value: i }, self.complex_type()) + } + pub fn new_str(&self, s: String) -> PyObjectRef { PyObject::new(PyObjectKind::String { value: s }, self.str_type()) } @@ -624,6 +638,9 @@ pub enum PyObjectKind { Float { value: f64, }, + Complex { + value: Complex64, + }, Bytes { value: Vec, }, @@ -690,6 +707,7 @@ impl fmt::Debug for PyObjectKind { &PyObjectKind::String { ref value } => write!(f, "str \"{}\"", value), &PyObjectKind::Integer { ref value } => write!(f, "int {}", value), &PyObjectKind::Float { ref value } => write!(f, "float {}", value), + &PyObjectKind::Complex { ref value } => write!(f, "complex {}", value), &PyObjectKind::Bytes { ref value } => write!(f, "bytes {:?}", value), &PyObjectKind::List { elements: _ } => write!(f, "list"), &PyObjectKind::Tuple { elements: _ } => write!(f, "tuple"), @@ -740,6 +758,7 @@ impl PyObject { PyObjectKind::String { ref value } => value.clone(), PyObjectKind::Integer { ref value } => format!("{:?}", value), PyObjectKind::Float { ref value } => format!("{:?}", value), + PyObjectKind::Complex { ref value } => format!("{:?}", value), PyObjectKind::Bytes { ref value } => format!("b'{:?}'", value), PyObjectKind::List { ref elements } => format!( "[{}]", diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index cabaa4e9a..2232e4670 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -13,6 +13,7 @@ use super::super::pyobject::{ AttributeProtocol, DictProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, }; use super::super::VirtualMachine; +use num_complex::Complex64; use std::ops::Deref; /* @@ -391,6 +392,9 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR let py_n = match value { ast::Number::Integer { value } => ctx.new_int(*value), ast::Number::Float { value } => ctx.new_float(*value), + ast::Number::Complex { real, imag } => { + ctx.new_complex(Complex64::new(*real, *imag)) + } }; node.set_attr("n", py_n);