Skip to content

Commit 5e27cc4

Browse files
committed
Initial struct module
1 parent cef6707 commit 5e27cc4

8 files changed

Lines changed: 157 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/snippets/basic_types.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828

2929
a = bytes([1, 2, 3])
3030
print(a)
31+
b = bytes([1, 2, 3])
32+
assert a == b
33+
3134
try:
3235
bytes([object()])
3336
except TypeError:

tests/snippets/test_struct.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
import struct
3+
4+
data = struct.pack('IH', 14, 12)
5+
assert data == bytes([14,0,0,0,12,0])
6+

vm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ rustpython_parser = {path = "../parser"}
1010
serde = "1.0.66"
1111
serde_derive = "1.0.66"
1212
serde_json = "1.0.26"
13+
byteorder = "1.2.6"

vm/src/obj/objbytes.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use super::objtype;
1010
// Fill bytes class methods:
1111
pub fn init(context: &PyContext) {
1212
let ref bytes_type = context.bytes_type;
13+
bytes_type.set_attr("__eq__", context.new_rustfunc(bytes_eq));
1314
bytes_type.set_attr("__init__", context.new_rustfunc(bytes_init));
1415
bytes_type.set_attr("__repr__", context.new_rustfunc(bytes_repr));
1516
}
@@ -38,6 +39,21 @@ fn bytes_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
3839
Ok(vm.get_none())
3940
}
4041

42+
fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
43+
arg_check!(
44+
vm,
45+
args,
46+
required = [(a, Some(vm.ctx.bytes_type())), (b, None)]
47+
);
48+
49+
let result = if objtype::isinstance(b, vm.ctx.bytes_type()) {
50+
get_value(a) == get_value(b)
51+
} else {
52+
false
53+
};
54+
Ok(vm.ctx.new_bool(result))
55+
}
56+
4157
pub fn get_value(obj: &PyObjectRef) -> Vec<u8> {
4258
if let PyObjectKind::Bytes { value } = &obj.borrow().kind {
4359
value.clone()

vm/src/pyobject.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ impl PyContext {
246246
PyObject::new(PyObjectKind::String { value: s }, self.str_type())
247247
}
248248

249+
pub fn new_bytes(&self, data: Vec<u8>) -> PyObjectRef {
250+
PyObject::new(PyObjectKind::Bytes { value: data }, self.bytes_type())
251+
}
252+
249253
pub fn new_bool(&self, b: bool) -> PyObjectRef {
250254
if b {
251255
self.true_value.clone()

vm/src/stdlib/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod io;
33
mod json;
44
mod keyword;
55
mod math;
6+
mod pystruct;
67
use std::collections::HashMap;
78

89
use super::pyobject::{PyContext, PyObjectRef};
@@ -16,5 +17,6 @@ pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
1617
modules.insert("ast".to_string(), ast::mk_module as StdlibInitFunc);
1718
modules.insert("keyword".to_string(), keyword::mk_module as StdlibInitFunc);
1819
modules.insert("math".to_string(), math::mk_module as StdlibInitFunc);
20+
modules.insert("struct".to_string(), pystruct::mk_module as StdlibInitFunc);
1921
modules
2022
}

vm/src/stdlib/pystruct.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Python struct module.
3+
*
4+
* renamed to pystruct since struct is a rust keyword.
5+
*
6+
* Use this rust module to do byte packing:
7+
* https://docs.rs/byteorder/1.2.6/byteorder/
8+
*/
9+
10+
extern crate byteorder;
11+
use self::byteorder::{LittleEndian, WriteBytesExt};
12+
13+
use super::super::obj::{objint, objstr, objtype};
14+
use super::super::pyobject::{DictProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult};
15+
use super::super::VirtualMachine;
16+
17+
#[derive(Debug)]
18+
struct FormatCode {
19+
code: char,
20+
size: i32,
21+
repeat: i32,
22+
}
23+
24+
// fn bp_float(v: PyObjectRef) {}
25+
26+
// fn bp_uint() {}
27+
28+
fn parse_format_string(fmt: String) -> Vec<FormatCode> {
29+
// First determine "<", ">","!" or "="
30+
// TODO
31+
32+
// Now, analyze struct string furter:
33+
let mut codes = vec![];
34+
for c in fmt.chars() {
35+
match c {
36+
'I' => codes.push(FormatCode {
37+
code: c,
38+
size: 1,
39+
repeat: 1,
40+
}),
41+
'H' => codes.push(FormatCode {
42+
code: c,
43+
size: 1,
44+
repeat: 1,
45+
}),
46+
_ => {}
47+
}
48+
}
49+
codes
50+
}
51+
52+
fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
53+
if args.args.len() < 1 {
54+
Err(vm.new_type_error(format!(
55+
"Expected at least 1 argument (got: {})",
56+
args.args.len()
57+
)))
58+
} else {
59+
let fmt_arg = args.args[0].clone();
60+
if objtype::isinstance(&fmt_arg, vm.ctx.str_type()) {
61+
let fmt_str = objstr::get_value(&fmt_arg);
62+
63+
let codes = parse_format_string(fmt_str);
64+
65+
if codes.len() + 1 == args.args.len() {
66+
// Create data vector:
67+
let mut data = Vec::<u8>::new();
68+
// Loop over all opcodes:
69+
for (code, arg) in codes.iter().zip(args.args.iter().skip(1)) {
70+
debug!("code: {:?}", code);
71+
match code.code {
72+
'I' => {
73+
if objtype::isinstance(&arg, vm.ctx.int_type()) {
74+
let v = objint::get_value(arg) as u32;
75+
match data.write_u32::<LittleEndian>(v) {
76+
Ok(_v) => {}
77+
Err(err) => panic!("Error: {:?}", err),
78+
}
79+
} else {
80+
return Err(vm.new_type_error(format!("Expected int")));
81+
}
82+
}
83+
'H' => {
84+
if objtype::isinstance(&arg, vm.ctx.int_type()) {
85+
let v = objint::get_value(arg) as u16;
86+
match data.write_u16::<LittleEndian>(v) {
87+
Ok(_v) => {}
88+
Err(err) => panic!("Error: {:?}", err),
89+
}
90+
} else {
91+
return Err(vm.new_type_error(format!("Expected int")));
92+
}
93+
}
94+
_ => {
95+
panic!("Unsupported format code");
96+
}
97+
}
98+
}
99+
100+
Ok(vm.ctx.new_bytes(data))
101+
} else {
102+
Err(vm.new_type_error(format!(
103+
"Expected {} arguments (got: {})",
104+
codes.len() + 1,
105+
args.args.len()
106+
)))
107+
}
108+
} else {
109+
Err(vm.new_type_error(format!("First argument must be of str type")))
110+
}
111+
}
112+
}
113+
114+
pub fn mk_module(ctx: &PyContext) -> PyObjectRef {
115+
let py_mod = ctx.new_module(&"struct".to_string(), ctx.new_scope(None));
116+
py_mod.set_item("pack", ctx.new_rustfunc(struct_pack));
117+
py_mod
118+
}

0 commit comments

Comments
 (0)