This commit is contained in:
MinGyo Jung
2019-08-21 15:01:24 +09:00
67 changed files with 1136 additions and 1719 deletions

View File

@@ -12,8 +12,9 @@ before_cache:
matrix:
fast_finish: true
include:
- name: Run Rust tests
- name: Run Rust tests(linux)
language: rust
os: linux
rust: stable
cache: cargo
script:
@@ -24,6 +25,19 @@ matrix:
# See: https://docs.travis-ci.com/user/caching/#caches-and-build-matrices
- JOBCACHE=1
- name: Run Rust tests(osx)
language: rust
os: osx
rust: stable
cache: cargo
script:
- cargo build --verbose --all
- cargo test --verbose --all
env:
# Prevention of cache corruption.
# See: https://docs.travis-ci.com/user/caching/#caches-and-build-matrices
- JOBCACHE=11
# To test the snippets, we use Travis' Python environment (because
# installing rust ourselves is a lot easier than installing Python)
- name: Python test snippets

12
Cargo.lock generated
View File

@@ -731,6 +731,16 @@ dependencies = [
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-iter"
version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-rational"
version = "0.2.1"
@@ -1168,6 +1178,7 @@ dependencies = [
"num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2102,6 +2113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718"
"checksum num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "107b9be86cd2481930688277b675b0114578227f034674726605b8a482d8baf8"
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
"checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e"
"checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10"
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273"

171
Lib/imghdr.py Normal file
View File

@@ -0,0 +1,171 @@
"""Recognize image file formats based on their first few bytes."""
#from os import PathLike
__all__ = ["what"]
# should replace using FileIO into file
from io import FileIO
#-------------------------#
# Recognize image headers #
#-------------------------#
def what(file, h=None):
f = None
try:
if h is None:
# if isinstance(file, (str, PathLike))
if isinstance(file, str): # FIXME(corona10): RustPython doesn't support PathLike yet.
f = FileIO(file, 'rb')
h = f.read(32)
else:
location = file.tell()
h = file.read(32)
file.seek(location)
for tf in tests:
res = tf(h, f)
if res:
return res
finally:
if f: f.close()
return None
#---------------------------------#
# Subroutines per image file type #
#---------------------------------#
tests = []
def test_jpeg(h, f):
"""JPEG data in JFIF or Exif format"""
if h[6:10] in (b'JFIF', b'Exif'):
return 'jpeg'
tests.append(test_jpeg)
def test_png(h, f):
if h.startswith(b'\211PNG\r\n\032\n'):
return 'png'
tests.append(test_png)
def test_gif(h, f):
"""GIF ('87 and '89 variants)"""
if h[:6] in (b'GIF87a', b'GIF89a'):
return 'gif'
tests.append(test_gif)
def test_tiff(h, f):
"""TIFF (can be in Motorola or Intel byte order)"""
if h[:2] in (b'MM', b'II'):
return 'tiff'
tests.append(test_tiff)
def test_rgb(h, f):
"""SGI image library"""
if h.startswith(b'\001\332'):
return 'rgb'
tests.append(test_rgb)
def test_pbm(h, f):
"""PBM (portable bitmap)"""
if len(h) >= 3 and \
h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r':
return 'pbm'
tests.append(test_pbm)
def test_pgm(h, f):
"""PGM (portable graymap)"""
if len(h) >= 3 and \
h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r':
return 'pgm'
tests.append(test_pgm)
def test_ppm(h, f):
"""PPM (portable pixmap)"""
if len(h) >= 3 and \
h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r':
return 'ppm'
tests.append(test_ppm)
def test_rast(h, f):
"""Sun raster file"""
if h.startswith(b'\x59\xA6\x6A\x95'):
return 'rast'
tests.append(test_rast)
def test_xbm(h, f):
"""X bitmap (X10 or X11)"""
if h.startswith(b'#define '):
return 'xbm'
tests.append(test_xbm)
def test_bmp(h, f):
if h.startswith(b'BM'):
return 'bmp'
tests.append(test_bmp)
def test_webp(h, f):
if h.startswith(b'RIFF') and h[8:12] == b'WEBP':
return 'webp'
tests.append(test_webp)
def test_exr(h, f):
if h.startswith(b'\x76\x2f\x31\x01'):
return 'exr'
tests.append(test_exr)
#--------------------#
# Small test program #
#--------------------#
def test():
import sys
recursive = 0
if sys.argv[1:] and sys.argv[1] == '-r':
del sys.argv[1:2]
recursive = 1
try:
if sys.argv[1:]:
testall(sys.argv[1:], recursive, 1)
else:
testall(['.'], recursive, 1)
except KeyboardInterrupt:
sys.stderr.write('\n[Interrupted]\n')
sys.exit(1)
def testall(list, recursive, toplevel):
import sys
import os
for filename in list:
if os.path.isdir(filename):
print(filename + '/:', end=' ')
if recursive or toplevel:
print('recursing down:')
import glob
names = glob.glob(os.path.join(filename, '*'))
testall(names, recursive, 0)
else:
print('*** directory (use -r) ***')
else:
print(filename + ':', end=' ')
sys.stdout.flush()
try:
print(what(filename))
except OSError:
print('*** not found ***')
if __name__ == '__main__':
test()

View File

@@ -1,4 +1,5 @@
import os
import time
import sys
@@ -19,8 +20,11 @@ benchmarks = [
['benchmarks/mandelbrot.py'],
]
@pytest.mark.parametrize('exe', pythons)
@pytest.mark.parametrize('args', benchmarks)
exe_ids = ['cpython', 'rustpython']
benchmark_ids = [benchmark[0].split('/')[-1] for benchmark in benchmarks]
@pytest.mark.parametrize('exe', pythons, ids=exe_ids)
@pytest.mark.parametrize('args', benchmarks, ids=benchmark_ids)
def test_bench(exe, args, benchmark):
def bench():
subprocess.run([exe] + args)

View File

@@ -204,8 +204,25 @@ impl SymbolTableAnalyzer {
// all is well
}
SymbolScope::Unknown => {
if symbol.is_assigned {
// Try hard to figure out what the scope of this symbol is.
if symbol.is_assigned || symbol.is_parameter {
symbol.scope = SymbolScope::Local;
} else {
// TODO: comment this out and make it work properly:
/*
let found_in_outer_scope = self
.tables
.iter()
.any(|t| t.symbols.contains_key(&symbol.name));
if found_in_outer_scope {
// Symbol is in some outer scope.
} else {
// Well, it must be a global then :)
// symbol.scope = SymbolScope::Global;
}
*/
}
}
}
@@ -713,7 +730,10 @@ impl SymbolTableBuilder {
});
}
}
SymbolUsage::Parameter | SymbolUsage::Assigned => {
SymbolUsage::Parameter => {
symbol.is_parameter = true;
}
SymbolUsage::Assigned => {
symbol.is_assigned = true;
}
SymbolUsage::Global => {

View File

@@ -24,7 +24,7 @@ pub enum Top {
#[derive(Debug, PartialEq)]
pub struct Program {
pub statements: Vec<Statement>,
pub statements: Suite,
}
#[derive(Debug, PartialEq)]
@@ -40,6 +40,7 @@ pub struct Located<T> {
}
pub type Statement = Located<StatementType>;
pub type Suite = Vec<Statement>;
/// Abstract syntax tree nodes for python statements.
#[derive(Debug, PartialEq)]
@@ -90,39 +91,39 @@ pub enum StatementType {
},
If {
test: Expression,
body: Vec<Statement>,
orelse: Option<Vec<Statement>>,
body: Suite,
orelse: Option<Suite>,
},
While {
test: Expression,
body: Vec<Statement>,
orelse: Option<Vec<Statement>>,
body: Suite,
orelse: Option<Suite>,
},
With {
is_async: bool,
items: Vec<WithItem>,
body: Vec<Statement>,
body: Suite,
},
For {
is_async: bool,
target: Box<Expression>,
iter: Box<Expression>,
body: Vec<Statement>,
orelse: Option<Vec<Statement>>,
body: Suite,
orelse: Option<Suite>,
},
Raise {
exception: Option<Expression>,
cause: Option<Expression>,
},
Try {
body: Vec<Statement>,
body: Suite,
handlers: Vec<ExceptHandler>,
orelse: Option<Vec<Statement>>,
finalbody: Option<Vec<Statement>>,
orelse: Option<Suite>,
finalbody: Option<Suite>,
},
ClassDef {
name: String,
body: Vec<Statement>,
body: Suite,
bases: Vec<Expression>,
keywords: Vec<Keyword>,
decorator_list: Vec<Expression>,
@@ -131,7 +132,7 @@ pub enum StatementType {
is_async: bool,
name: String,
args: Box<Parameters>,
body: Vec<Statement>,
body: Suite,
decorator_list: Vec<Expression>,
returns: Option<Expression>,
},
@@ -331,7 +332,7 @@ pub struct ExceptHandler {
pub location: Location,
pub typ: Option<Expression>,
pub name: Option<String>,
pub body: Vec<Statement>,
pub body: Suite,
}
#[derive(Debug, PartialEq)]

View File

@@ -31,22 +31,22 @@ Program: ast::Program = {
};
// A file line either has a declaration, or an empty newline:
FileLine: Vec<ast::Statement> = {
FileLine: ast::Suite = {
Statement,
"\n" => vec![],
};
Suite: Vec<ast::Statement> = {
Suite: ast::Suite = {
SimpleStatement,
"\n" indent <s:Statement+> dedent => s.into_iter().flatten().collect(),
};
Statement: Vec<ast::Statement> = {
Statement: ast::Suite = {
SimpleStatement,
<s:CompoundStatement> => vec![s],
};
SimpleStatement: Vec<ast::Statement> = {
SimpleStatement: ast::Suite = {
<s1:SmallStatement> <s2:(";" SmallStatement)*> ";"? "\n" => {
let mut statements = vec![s1];
statements.extend(s2.into_iter().map(|e| e.1));
@@ -365,7 +365,7 @@ ForStatement: ast::Statement = {
};
TryStatement: ast::Statement = {
<location:@L> "try" ":" <body:Suite> <handlers:ExceptClause*> <else_suite:("else" ":" Suite)?> <finally:("finally" ":" Suite)?> => {
<location:@L> "try" ":" <body:Suite> <handlers:ExceptClause+> <else_suite:("else" ":" Suite)?> <finally:("finally" ":" Suite)?> => {
let orelse = else_suite.map(|s| s.2);
let finalbody = finally.map(|s| s.2);
ast::Statement {
@@ -378,6 +378,20 @@ TryStatement: ast::Statement = {
},
}
},
<location:@L> "try" ":" <body:Suite> <finally:("finally" ":" Suite)> => {
let handlers = vec![];
let orelse = None;
let finalbody = Some(finally.2);
ast::Statement {
location,
node: ast::StatementType::Try {
body,
handlers,
orelse,
finalbody,
},
}
},
};
ExceptClause: ast::ExceptHandler = {

View File

@@ -1 +0,0 @@
target

View File

@@ -1,241 +0,0 @@
[[package]]
name = "aho-corasick"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cfg-if"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dtoa"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "env_logger"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itoa"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
version = "0.2.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "py_code_object"
version = "0.1.0"
dependencies = [
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"rustpython_vm 0.1.0",
"serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "regex"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustpython_vm"
version = "0.1.0"
dependencies = [
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_codegen"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_codegen_internals 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_codegen_internals"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde_codegen 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread-id"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "utf8-ranges"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18"
"checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d"
"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f"
"checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6fddaa003a65722a7fb9e26b0ce95921fe4ba590542ced664d8ce2fa26f9f3ac"
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
"checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
"checksum serde_codegen 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c5d8a33087d8984f9535daa62a6498a08f6476050b00ab9339dd847e4c25cc"
"checksum serde_codegen_internals 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "afad7924a009f859f380e4a2e3a509a845c2ac66435fcead74a4d983b21ae806"
"checksum serde_derive 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "ce44e5f4264b39e9d29c875357b7cc3ebdfb967bb9e22bfb5e44ffa400af5306"
"checksum serde_json 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "67f7d2e9edc3523a9c8ec8cd6ec481b3a27810aafee3e625d311febd3e656b4c"
"checksum syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)" = "58fd09df59565db3399efbba34ba8a2fec1307511ebd245d0061ff9d42691673"
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

View File

@@ -1,13 +0,0 @@
[package]
name = "py_code_object"
version = "0.1.0"
authors = ["Shing Lyu <shing.lyu@gmail.com>"]
edition = "2018"
[dependencies]
log = "0.3"
env_logger = "0.3"
serde = "0.8.22"
serde_derive = "0.8"
serde_json = "0.8"
rustpython_vm = {path = "../vm"}

View File

@@ -1,3 +0,0 @@
This crate contains the Rust representation of the CPython PyCodeObject.
It has to be compatible with CPython output, so when you edit this crate please run the test suite in `vm/`.

View File

@@ -1,219 +0,0 @@
[[package]]
name = "aho-corasick"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "aho-corasick"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cpython"
version = "0.2.0"
source = "git+https://github.com/dgrunwald/rust-cpython.git#50e96c9755630604eab09587c8976a8a9c096a7d"
dependencies = [
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"python3-sys 0.2.0 (git+https://github.com/dgrunwald/rust-cpython.git)",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "python27-sys"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "python3-sys"
version = "0.2.0"
source = "git+https://github.com/dgrunwald/rust-cpython.git#50e96c9755630604eab09587c8976a8a9c096a7d"
dependencies = [
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "python_compiler"
version = "0.1.0"
dependencies = [
"cpython 0.2.0 (git+https://github.com/dgrunwald/rust-cpython.git)",
"python27-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "regex-syntax"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread-id"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ucd-util"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unreachable"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "utf8-ranges"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "utf8-ranges"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
"checksum cpython 0.2.0 (git+https://github.com/dgrunwald/rust-cpython.git)" = "<none>"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
"checksum libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)" = "ac8ebf8343a981e2fa97042b14768f02ed3e1d602eac06cae6166df3c8ced206"
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28"
"checksum python27-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4daec30bfe241e55f2c905cee2badf176ae2be49d9bddf4267103fea780238c3"
"checksum python3-sys 0.2.0 (git+https://github.com/dgrunwald/rust-cpython.git)" = "<none>"
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384"
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

View File

@@ -1,11 +0,0 @@
[package]
name = "python_compiler"
version = "0.1.0"
authors = ["Shing Lyu <shing.lyu@gmail.com>"]
edition = "2018"
[dependencies]
cpython = { git = "https://github.com/dgrunwald/rust-cpython.git" }
[dependencies.python27-sys]
version = "*"

View File

@@ -1,7 +0,0 @@
extern crate python_compiler;
use python_compiler::python_compiler::compile;
fn main() {
println!("{:?}", compile());
}

View File

@@ -1,4 +0,0 @@
extern crate cpython;
extern crate python27_sys;
pub mod python_compiler;

View File

@@ -1,35 +0,0 @@
use cpython::Python;
use cpython::ObjectProtocol; //for call method
use cpython::PyObject;
use cpython::PyDict;
use python27_sys::PyCodeObject;
//pub fn compile() -> PyObject {
pub fn compile(){
let gil = Python::acquire_gil();
let py = gil.python();
let locals = PyDict::new(py);
// TODO: read the filename from commandline
//locals.set_item(py, "filename", "../tests/function.py").unwrap();
let load_file = "\
import os
print(os.getcwd())
filename = '../tests/function.py'
with open(filename, 'rU') as f:\
code = f.read()\
";
py.run(load_file, None, Some(&locals)).unwrap();
let code = py.eval("compile(code, \"foo\", \"exec\")", None, Some(&locals)).unwrap();
//println!("{:?}", code.getattr(py, "co_name").unwrap());
//println!("{:?}", code.getattr(py, "co_filename").unwrap());
//println!("{:?}", code.getattr(py, "co_code").unwrap());
//println!("{:?}", code.getattr(py, "co_freevars").unwrap());
//println!("{:?}", code.getattr(py, "co_cellvars").unwrap());
println!("{:?}", code.getattr(py, "co_consts").unwrap());
//let consts = code.getattr(py, "co_consts").unwrap();
//println!("{:?}", consts.get_item(py, 0).unwrap().getattr(py, "co_code"));
}

View File

@@ -1,237 +0,0 @@
// A function which takes CPython bytecode (from json) and transforms
// this into RustPython bytecode. This to decouple RustPython from CPython
// internal bytecode representations.
use rustpython_vm::bytecode;
use py_code_object::{PyCodeObject, NativeType};
pub fn convert(cpython_bytecode: PyCodeObject) -> bytecode::CodeObject {
let mut c = Converter::new();
c.convert(cpython_bytecode);
c.code2
}
// TODO: think of an appropriate name for this thing:
pub struct Converter {
code: Option<PyCodeObject>,
code2: bytecode::CodeObject,
}
impl Converter {
pub fn new() -> Converter {
Converter {
code: None,
code2: bytecode::CodeObject::new(),
}
}
pub fn convert(&mut self, code: PyCodeObject) {
// self.code = Some(code);
for op_code in &code.co_code {
self.dispatch(&code, op_code);
}
}
fn dispatch(&mut self, code: &PyCodeObject, op_code: &(usize, String, Option<usize>)) {
debug!("Converting op code: {:?}", op_code);
match (op_code.1.as_ref(), op_code.2) {
("LOAD_CONST", Some(consti)) => {
// println!("Loading const at index: {}", consti);
let cons = &code.co_consts[consti];
let value = match cons {
// NativeType::Boolean { value } => { bytecode::Constant::Boolean { true } },
NativeType::Int { 0: value } => { bytecode::Constant::Integer { value: *value } },
NativeType::Str { 0: ref value } => { bytecode::Constant::String { value: value.clone() } }
NativeType::NoneType => { bytecode::Constant::None }
_ => { panic!("Not impl {:?}", cons); }
};
self.emit(bytecode::Instruction::LoadConst { value: value });
},
// TODO: universal stack element type
("LOAD_CONST", None) => {
self.emit(bytecode::Instruction::LoadConst { value: bytecode::Constant::None });
},
("POP_TOP", None) => {
self.emit(bytecode::Instruction::Pop);
},
("STORE_NAME", Some(namei)) => {
// println!("Loading const at index: {}", consti);
let name = code.co_names[namei].clone();
self.emit(bytecode::Instruction::StoreName { name });
},
("LOAD_NAME", Some(namei)) => {
// println!("Loading const at index: {}", consti);
let name = code.co_names[namei].clone();
self.emit(bytecode::Instruction::LoadName { name });
},
("LOAD_GLOBAL", Some(namei)) => {
// We need to load the underlying value the name points to, but stuff like
// AssertionError is in the names right after compile, so we load the string
// instead for now
// let curr_frame = self.curr_frame();
// let name = &curr_frame.code.co_names[namei];
},
("BUILD_LIST", Some(count)) => {
self.emit(bytecode::Instruction::BuildList { size: count });
},
("BUILD_SLICE", Some(count)) => {
assert!(count == 2 || count == 3);
self.emit(bytecode::Instruction::BuildSlice { size: count });
},
("GET_ITER", None) => {
self.emit(bytecode::Instruction::GetIter);
},
("FOR_ITER", Some(delta)) => {
self.emit(bytecode::Instruction::ForIter);
},
("COMPARE_OP", Some(cmp_op_i)) => {
let op = match cmp_op_i {
0 => bytecode::ComparisonOperator::Less,
1 => bytecode::ComparisonOperator::LessOrEqual,
2 => bytecode::ComparisonOperator::Equal,
3 => bytecode::ComparisonOperator::NotEqual,
4 => bytecode::ComparisonOperator::Greater,
5 => bytecode::ComparisonOperator::GreaterOrEqual,
_ => { panic!("Not impl {:?}", cmp_op_i); }
};
self.emit(bytecode::Instruction::CompareOperation { op: op} );
},
("POP_JUMP_IF_TRUE", Some(ref target)) => {
self.emit(bytecode::Instruction::JumpIf { target: *target} );
}
/*
("POP_JUMP_IF_FALSE", Some(ref target)) => {
// Convert into two internal bytecodes:
self.emit(bytecode::Instruction::UnaryOperation { op: bytecode::UnaryOperation::Not } );
self.emit(bytecode::Instruction::JumpIf { target: target} );
}*/
/*
("JUMP_FORWARD", Some(ref delta)) => {
let curr_frame = self.curr_frame();
let last_offset = curr_frame.get_bytecode_offset().unwrap();
curr_frame.lasti = curr_frame.labels.get(&(last_offset + delta)).unwrap().clone();
None
},
("JUMP_ABSOLUTE", Some(ref target)) => {
let curr_frame = self.curr_frame();
curr_frame.lasti = curr_frame.labels.get(target).unwrap().clone();
None
},
("BREAK_LOOP", None) => {
// Do we still need to return the why if we use unwind from jsapy?
self.unwind("break".to_string());
None //?
},
*/
("RAISE_VARARGS", Some(argc)) => {
self.emit(bytecode::Instruction::Raise { argc: argc });
}
/*
("INPLACE_ADD", None) => {
self.emit(bytecode::Instruction::BinaryOperation { op: BinaryOperator::Add });
},
("STORE_SUBSCR", None) => {
let curr_frame = self.curr_frame();
let tos = curr_frame.stack.pop().unwrap();
let tos1 = curr_frame.stack.pop().unwrap();
let tos2 = curr_frame.stack.pop().unwrap();
match (tos1.deref(), tos.deref()) {
(&NativeType::List(ref refl), &NativeType::Int(index)) => {
refl.borrow_mut()[index as usize] = (*tos2).clone();
},
(&NativeType::Str(_), &NativeType::Int(_)) => {
// TODO: raise TypeError: 'str' object does not support item assignment
panic!("TypeError: 'str' object does not support item assignment")
},
_ => panic!("TypeError in STORE_SUBSCR")
}
curr_frame.stack.push(tos1);
},
*/
("BINARY_ADD", None) => {
self.emit(bytecode::Instruction::BinaryOperation { op: bytecode::BinaryOperator::Add });
},
("BINARY_POWER", None) => {
self.emit(bytecode::Instruction::BinaryOperation { op: bytecode::BinaryOperator::Power });
},
("BINARY_MULTIPLY", None) => {
self.emit(bytecode::Instruction::BinaryOperation { op: bytecode::BinaryOperator::Multiply });
},
("BINARY_TRUE_DIVIDE", None) => {
self.emit(bytecode::Instruction::BinaryOperation { op: bytecode::BinaryOperator::Divide });
},
("BINARY_MODULO", None) => {
self.emit(bytecode::Instruction::BinaryOperation { op: bytecode::BinaryOperator::Modulo });
},
("BINARY_SUBTRACT", None) => {
self.emit(bytecode::Instruction::BinaryOperation { op: bytecode::BinaryOperator::Subtract });
},
("BINARY_SUBSCR", None) => {
self.emit(bytecode::Instruction::BinaryOperation { op: bytecode::BinaryOperator::Subscript });
},
("ROT_TWO", None) => {
self.emit(bytecode::Instruction::Rotate { amount: 2 });
}
("UNARY_NEGATIVE", None) => {
self.emit(bytecode::Instruction::UnaryOperation { op: bytecode::UnaryOperator::Minus });
},
("UNARY_POSITIVE", None) => {
self.emit(bytecode::Instruction::UnaryOperation { op: bytecode::UnaryOperator::Plus });
},
/*
("PRINT_ITEM", None) => {
// TODO: Print without the (...)
println!("{:?}", curr_frame.stack.pop().unwrap());
},
("PRINT_NEWLINE", None) => {
print!("\n");
},*/
/*
("MAKE_FUNCTION", Some(argc)) => {
// https://docs.python.org/3.4/library/dis.html#opcode-MAKE_FUNCTION
self.emit(bytecode::Instruction::MakeFunction { });
},
*/
("CALL_FUNCTION", Some(argc)) => {
let kw_count = (argc >> 8) as u8;
let pos_count = (argc & 0xFF) as usize;
self.emit(bytecode::Instruction::CallFunction { count: pos_count });
},
("RETURN_VALUE", None) => {
self.emit(bytecode::Instruction::ReturnValue);
},
/*
("SETUP_LOOP", Some(delta)) => {
let curr_frame = self.curr_frame();
let curr_offset = curr_frame.get_bytecode_offset().unwrap();
curr_frame.blocks.push(Block {
block_type: "loop".to_string(),
handler: *curr_frame.labels.get(&(curr_offset + delta)).unwrap(),
});
},
*/
("POP_BLOCK", None) => {
self.emit(bytecode::Instruction::PopBlock);
}
("SetLineno", _) | ("LABEL", _)=> {
// Skip
},
(name, _) => {
panic!("Unrecongnizable op code: {}", name);
}
} // end match
} // end dispatch function
fn emit(&mut self, instruction: bytecode::Instruction) {
self.code2.instructions.push(instruction);
}
}

View File

@@ -1,59 +0,0 @@
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
use std::cell::RefCell;
use std::rc::Rc;
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub enum NativeType{
NoneType,
Boolean(bool),
Int(i32),
Float(f64),
Str(String),
Unicode(String),
#[serde(skip_serializing, skip_deserializing)]
List(RefCell<Vec<NativeType>>),
Tuple(Vec<NativeType>),
Iter(Vec<NativeType>), // TODO: use Iterator instead
Code(PyCodeObject),
Function(Function),
Slice(Option<i32>, Option<i32>, Option<i32>), // start, stop, step
#[serde(skip_serializing, skip_deserializing)]
NativeFunction(fn(Vec<Rc<NativeType>>) -> NativeType ),
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct PyCodeObject {
pub co_consts: Vec<NativeType>,
pub co_names: Vec<String>,
// TODO: use vector of bytecode objects instead of strings?
pub co_code: Vec<(usize, String, Option<usize>)>, //size, name, args
pub co_varnames: Vec<String>,
}
impl PyCodeObject {
pub fn new() -> PyCodeObject {
PyCodeObject {
co_consts: Vec::<NativeType>::new(),
co_names: Vec::<String>::new(),
co_code: Vec::<(usize, String, Option<usize>)>::new(), //size, name, args
co_varnames: Vec::<String>::new(),
}
}
}
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
pub struct Function {
pub code: PyCodeObject
}
impl Function {
pub fn new(code: PyCodeObject) -> Function {
Function {
code: code
}
}
}

View File

@@ -1,37 +0,0 @@
extern crate env_logger;
extern crate py_code_object;
extern crate rustpython_vm;
extern crate serde_json;
#[macro_use]
extern crate log;
mod convert;
use rustpython_vm::evaluate;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use py_code_object::PyCodeObject;
fn main() {
env_logger::init().unwrap();
// TODO: read this from args
let args: Vec<String> = env::args().collect();
let filename = &args[1];
let mut f = File::open(filename).unwrap();
// println!("Read file");
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
// println!("Read string");
// TODO: Extract this so we don't depend on json
let cpython_code: PyCodeObject = match serde_json::from_str(&s) {
Ok(c) => c,
Err(_) => panic!("Fail to parse the bytecode")
};
let code = convert::convert(cpython_code);
evaluate(code);
}

View File

@@ -1,653 +0,0 @@
// extern crate py_code_object;
use std::collections::HashMap;
use std::cell::RefCell;
use std::rc::Rc;
use std::ops::Deref;
const CMP_OP: &'static [&'static str] = &[">",
"<=",
"==",
"!=",
">",
">=",
"in",
"not in",
"is",
"is not",
"exception match",
"BAD"
];
impl Frame {
/// Get the current bytecode offset calculated from curr_frame.lasti
fn get_bytecode_offset(&self) -> Option<usize> {
// Linear search the labels HashMap, inefficient. Consider build a reverse HashMap
let mut last_offset = None;
for (offset, instr_idx) in self.labels.iter() {
if *instr_idx == self.lasti {
last_offset = Some(*offset)
}
}
last_offset
}
}
pub struct VirtualMachine{
frames: Vec<Frame>,
}
impl VirtualMachine {
fn unwind(&mut self, reason: String) {
let curr_frame = self.curr_frame();
let curr_block = curr_frame.blocks[curr_frame.blocks.len()-1].clone(); // use last?
curr_frame.why = reason; // Why do we need this?
debug!("block status: {:?}, {:?}", curr_block.block_type, curr_frame.why);
match (curr_block.block_type.as_ref(), curr_frame.why.as_ref()) {
("loop", "break") => {
curr_frame.lasti = curr_block.handler; //curr_frame.labels[curr_block.handler]; // Jump to the end
// Return the why as None
curr_frame.blocks.pop();
},
("loop", "none") => (), //skipped
_ => panic!("block stack operation not implemented")
}
}
// Can we get rid of the code parameter?
fn make_frame(&self, code: PyCodeObject, callargs: HashMap<String, Rc<NativeType>>, globals: Option<HashMap<String, Rc<NativeType>>>) -> Frame {
//populate the globals and locals
let mut labels = HashMap::new();
let mut curr_offset = 0;
for (idx, op) in code.co_code.iter().enumerate() {
labels.insert(curr_offset, idx);
curr_offset += op.0;
}
//TODO: This is wrong, check https://github.com/nedbat/byterun/blob/31e6c4a8212c35b5157919abff43a7daa0f377c6/byterun/pyvm2.py#L95
let globals = match globals {
Some(g) => g,
None => HashMap::new(),
};
let mut locals = globals;
locals.extend(callargs);
//TODO: move this into the __builtin__ module when we have a module type
locals.insert("print".to_string(), Rc::new(NativeType::NativeFunction(builtins::print)));
locals.insert("len".to_string(), Rc::new(NativeType::NativeFunction(builtins::len)));
Frame {
code: code,
stack: vec![],
blocks: vec![],
// save the callargs as locals
globals: locals.clone(),
locals: locals,
labels: labels,
lasti: 0,
return_value: NativeType::NoneType,
why: "none".to_string(),
}
}
// The Option<i32> is the return value of the frame, remove when we have implemented frame
// TODO: read the op codes directly from the internal code object
fn run_frame(&mut self, frame: Frame) -> NativeType {
self.frames.push(frame);
//let mut why = None;
// Change this to a loop for jump
loop {
//while curr_frame.lasti < curr_frame.code.co_code.len() {
let op_code = {
let curr_frame = self.curr_frame();
if curr_frame.code.co_code.len() == 0 { panic!("Trying to run an empty frame. Check if the bytecode is empty"); }
let op_code = curr_frame.code.co_code[curr_frame.lasti].clone();
curr_frame.lasti += 1;
op_code
};
let why = self.dispatch(op_code);
/*if curr_frame.blocks.len() > 0 {
self.manage_block_stack(&why);
}
*/
if let Some(_) = why {
break;
}
}
let return_value = {
//let curr_frame = self.frames.last_mut().unwrap();
self.curr_frame().return_value.clone()
};
self.pop_frame();
return_value
}
pub fn run_code(&mut self, code: PyCodeObject) {
let frame = self.make_frame(code, HashMap::new(), None);
self.run_frame(frame);
// check if there are any leftover frame, fail if any
}
fn dispatch(&mut self, op_code: (usize, String, Option<usize>)) -> Option<String> {
match (op_code.1.as_ref(), op_code.2) {
("LOAD_CONST", Some(consti)) => {
// println!("Loading const at index: {}", consti);
let curr_frame = self.curr_frame();
curr_frame.stack.push(Rc::new(curr_frame.code.co_consts[consti].clone()));
None
},
// TODO: universal stack element type
("LOAD_CONST", None) => {
// println!("Loading const at index: {}", consti);
self.curr_frame().stack.push(Rc::new(NativeType::NoneType));
None
},
("POP_TOP", None) => {
self.curr_frame().stack.pop();
None
},
("LOAD_FAST", Some(var_num)) => {
// println!("Loading const at index: {}", consti);
let curr_frame = self.curr_frame();
let ref name = curr_frame.code.co_varnames[var_num];
curr_frame.stack.push(curr_frame.locals.get::<str>(name).unwrap().clone());
None
},
("STORE_NAME", Some(namei)) => {
// println!("Loading const at index: {}", consti);
let curr_frame = self.curr_frame();
curr_frame.locals.insert(curr_frame.code.co_names[namei].clone(), curr_frame.stack.pop().unwrap().clone());
None
},
("LOAD_NAME", Some(namei)) => {
// println!("Loading const at index: {}", consti);
let curr_frame = self.curr_frame();
if let Some(code) = curr_frame.locals.get::<str>(&curr_frame.code.co_names[namei]) {
curr_frame.stack.push(code.clone());
}
else {
panic!("Can't find symbol {:?} in the current frame", &curr_frame.code.co_names[namei]);
}
None
},
("LOAD_GLOBAL", Some(namei)) => {
// We need to load the underlying value the name points to, but stuff like
// AssertionError is in the names right after compile, so we load the string
// instead for now
let curr_frame = self.curr_frame();
let name = &curr_frame.code.co_names[namei];
curr_frame.stack.push(curr_frame.globals.get::<str>(name).unwrap().clone());
None
},
("BUILD_LIST", Some(count)) => {
let curr_frame = self.curr_frame();
let mut vec = vec!();
for _ in 0..count {
vec.push((*curr_frame.stack.pop().unwrap()).clone());
}
vec.reverse();
curr_frame.stack.push(Rc::new(NativeType::List(RefCell::new(vec))));
None
},
("BUILD_SLICE", Some(count)) => {
let curr_frame = self.curr_frame();
assert!(count == 2 || count == 3);
let mut vec = vec!();
for _ in 0..count {
vec.push(curr_frame.stack.pop().unwrap());
}
vec.reverse();
let mut out:Vec<Option<i32>> = vec.into_iter().map(|x| match *x {
NativeType::Int(n) => Some(n),
NativeType::NoneType => None,
_ => panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x),
}).collect();
if out.len() == 2 {
out.push(None);
}
assert!(out.len() == 3);
// TODO: assert the stop start and step are NativeType::Int
// See https://users.rust-lang.org/t/how-do-you-assert-enums/1187/8
curr_frame.stack.push(Rc::new(NativeType::Slice(out[0], out[1], out[2])));
None
},
("GET_ITER", None) => {
let curr_frame = self.curr_frame();
let tos = curr_frame.stack.pop().unwrap();
let iter = match *tos {
//TODO: is this clone right?
// Return a Iterator instead vvv
NativeType::Tuple(ref vec) => NativeType::Iter(vec.clone()),
NativeType::List(ref vec) => NativeType::Iter(vec.borrow().clone()),
_ => panic!("TypeError: object is not iterable")
};
curr_frame.stack.push(Rc::new(iter));
None
},
("FOR_ITER", Some(delta)) => {
// This function should be rewrote to use Rust native iterator
let curr_frame = self.curr_frame();
let tos = curr_frame.stack.pop().unwrap();
let result = match *tos {
NativeType::Iter(ref v) => {
if v.len() > 0 {
Some(v.clone()) // Unnessary clone here
}
else {
None
}
}
_ => panic!("FOR_ITER: Not an iterator")
};
if let Some(vec) = result {
let (first, rest) = vec.split_first().unwrap();
// Unnessary clone here
curr_frame.stack.push(Rc::new(NativeType::Iter(rest.to_vec())));
curr_frame.stack.push(Rc::new(first.clone()));
}
else {
// Iterator was already poped in the first line of this function
let last_offset = curr_frame.get_bytecode_offset().unwrap();
curr_frame.lasti = curr_frame.labels.get(&(last_offset + delta)).unwrap().clone();
}
None
},
("COMPARE_OP", Some(cmp_op_i)) => {
let curr_frame = self.curr_frame();
let v1 = curr_frame.stack.pop().unwrap();
let v2 = curr_frame.stack.pop().unwrap();
match CMP_OP[cmp_op_i] {
// To avoid branch explotion, use an array of callables instead
"==" => {
match (v1.deref(), v2.deref()) {
(&NativeType::Int(ref v1i), &NativeType::Int(ref v2i)) => {
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2i == v1i)));
},
(&NativeType::Float(ref v1f), &NativeType::Float(ref v2f)) => {
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2f == v1f)));
},
(&NativeType::Str(ref v1s), &NativeType::Str(ref v2s)) => {
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2s == v1s)));
},
(&NativeType::Int(ref v1i), &NativeType::Float(ref v2f)) => {
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2f == &(*v1i as f64))));
},
(&NativeType::List(ref l1), &NativeType::List(ref l2)) => {
curr_frame.stack.push(Rc::new(NativeType::Boolean(l2 == l1)));
},
_ => panic!("TypeError in COMPARE_OP: can't compare {:?} with {:?}", v1, v2)
};
}
">" => {
match (v1.deref(), v2.deref()) {
(&NativeType::Int(ref v1i), &NativeType::Int(ref v2i)) => {
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2i < v1i)));
},
(&NativeType::Float(ref v1f), &NativeType::Float(ref v2f)) => {
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2f < v1f)));
},
_ => panic!("TypeError in COMPARE_OP")
};
}
_ => panic!("Unimplemented COMPARE_OP operator")
}
None
},
("POP_JUMP_IF_TRUE", Some(ref target)) => {
let curr_frame = self.curr_frame();
let v = curr_frame.stack.pop().unwrap();
if *v == NativeType::Boolean(true) {
curr_frame.lasti = curr_frame.labels.get(target).unwrap().clone();
}
None
}
("POP_JUMP_IF_FALSE", Some(ref target)) => {
let curr_frame = self.curr_frame();
let v = curr_frame.stack.pop().unwrap();
if *v == NativeType::Boolean(false) {
curr_frame.lasti = curr_frame.labels.get(target).unwrap().clone();
}
None
}
("JUMP_FORWARD", Some(ref delta)) => {
let curr_frame = self.curr_frame();
let last_offset = curr_frame.get_bytecode_offset().unwrap();
curr_frame.lasti = curr_frame.labels.get(&(last_offset + delta)).unwrap().clone();
None
},
("JUMP_ABSOLUTE", Some(ref target)) => {
let curr_frame = self.curr_frame();
curr_frame.lasti = curr_frame.labels.get(target).unwrap().clone();
None
},
("BREAK_LOOP", None) => {
// Do we still need to return the why if we use unwind from jsapy?
self.unwind("break".to_string());
None //?
},
("RAISE_VARARGS", Some(argc)) => {
let curr_frame = self.curr_frame();
// let (exception, params, traceback) = match argc {
let exception = match argc {
1 => curr_frame.stack.pop().unwrap(),
0 | 2 | 3 => panic!("Not implemented!"),
_ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3")
};
panic!("{:?}", exception);
}
("INPLACE_ADD", None) => {
let curr_frame = self.curr_frame();
let tos = curr_frame.stack.pop().unwrap();
let tos1 = curr_frame.stack.pop().unwrap();
match (tos.deref(), tos1.deref()) {
(&NativeType::Int(ref tosi), &NativeType::Int(ref tos1i)) => {
curr_frame.stack.push(Rc::new(NativeType::Int(tos1i + tosi)));
},
_ => panic!("TypeError in BINARY_ADD")
}
None
},
("STORE_SUBSCR", None) => {
let curr_frame = self.curr_frame();
let tos = curr_frame.stack.pop().unwrap();
let tos1 = curr_frame.stack.pop().unwrap();
let tos2 = curr_frame.stack.pop().unwrap();
match (tos1.deref(), tos.deref()) {
(&NativeType::List(ref refl), &NativeType::Int(index)) => {
refl.borrow_mut()[index as usize] = (*tos2).clone();
},
(&NativeType::Str(_), &NativeType::Int(_)) => {
// TODO: raise TypeError: 'str' object does not support item assignment
panic!("TypeError: 'str' object does not support item assignment")
},
_ => panic!("TypeError in STORE_SUBSCR")
}
curr_frame.stack.push(tos1);
None
},
("BINARY_ADD", None) => {
let curr_frame = self.curr_frame();
let v1 = curr_frame.stack.pop().unwrap();
let v2 = curr_frame.stack.pop().unwrap();
match (v1.deref(), v2.deref()) {
(&NativeType::Int(ref v1i), &NativeType::Int(ref v2i)) => {
curr_frame.stack.push(Rc::new(NativeType::Int(v2i + v1i)));
}
(&NativeType::Float(ref v1f), &NativeType::Int(ref v2i)) => {
curr_frame.stack.push(Rc::new(NativeType::Float(*v2i as f64 + v1f)));
}
(&NativeType::Int(ref v1i), &NativeType::Float(ref v2f)) => {
curr_frame.stack.push(Rc::new(NativeType::Float(v2f + *v1i as f64)));
}
(&NativeType::Float(ref v1f), &NativeType::Float(ref v2f)) => {
curr_frame.stack.push(Rc::new(NativeType::Float(v2f + v1f)));
}
(&NativeType::Str(ref str1), &NativeType::Str(ref str2)) => {
curr_frame.stack.push(Rc::new(NativeType::Str(format!("{}{}", str2, str1))));
}
(&NativeType::List(ref l1), &NativeType::List(ref l2)) => {
let mut new_l = l2.clone();
// TODO: remove unnessary copy
new_l.borrow_mut().append(&mut l1.borrow().clone());
curr_frame.stack.push(Rc::new(NativeType::List(new_l)));
}
_ => panic!("TypeError in BINARY_ADD")
}
None
},
("BINARY_POWER", None) => {
let curr_frame = self.curr_frame();
let v1 = curr_frame.stack.pop().unwrap();
let v2 = curr_frame.stack.pop().unwrap();
match (v1.deref(), v2.deref()) {
(&NativeType::Int(v1i), &NativeType::Int(v2i)) => {
curr_frame.stack.push(Rc::new(NativeType::Int(v2i.pow(v1i as u32))));
}
(&NativeType::Float(v1f), &NativeType::Int(v2i)) => {
curr_frame.stack.push(Rc::new(NativeType::Float((v2i as f64).powf(v1f))));
}
(&NativeType::Int(v1i), &NativeType::Float(v2f)) => {
curr_frame.stack.push(Rc::new(NativeType::Float(v2f.powi(v1i))));
}
(&NativeType::Float(v1f), &NativeType::Float(v2f)) => {
curr_frame.stack.push(Rc::new(NativeType::Float(v2f.powf(v1f))));
}
_ => panic!("TypeError in BINARY_POWER")
}
None
},
("BINARY_MULTIPLY", None) => {
let curr_frame = self.curr_frame();
let v1 = curr_frame.stack.pop().unwrap();
let v2 = curr_frame.stack.pop().unwrap();
match (v1.deref(), v2.deref()) {
(&NativeType::Int(v1i), &NativeType::Int(v2i)) => {
curr_frame.stack.push(Rc::new(NativeType::Int(v2i * v1i)));
},
/*
(NativeType::Float(v1f), NativeType::Int(v2i)) => {
curr_frame.stack.push(NativeType::Float((v2i as f64) * v1f));
},
(NativeType::Int(v1i), NativeType::Float(v2f)) => {
curr_frame.stack.push(NativeType::Float(v2f * (v1i as f64)));
},
(NativeType::Float(v1f), NativeType::Float(v2f)) => {
curr_frame.stack.push(NativeType::Float(v2f * v1f));
},
*/
//TODO: String multiply
_ => panic!("TypeError in BINARY_MULTIPLY")
}
None
},
("BINARY_TRUE_DIVIDE", None) => {
let curr_frame = self.curr_frame();
let v1 = curr_frame.stack.pop().unwrap();
let v2 = curr_frame.stack.pop().unwrap();
match (v1.deref(), v2.deref()) {
(&NativeType::Int(v1i), &NativeType::Int(v2i)) => {
curr_frame.stack.push(Rc::new(NativeType::Int(v2i / v1i)));
},
_ => panic!("TypeError in BINARY_DIVIDE")
}
None
},
("BINARY_MODULO", None) => {
let curr_frame = self.curr_frame();
let v1 = curr_frame.stack.pop().unwrap();
let v2 = curr_frame.stack.pop().unwrap();
match (v1.deref(), v2.deref()) {
(&NativeType::Int(v1i), &NativeType::Int(v2i)) => {
curr_frame.stack.push(Rc::new(NativeType::Int(v2i % v1i)));
},
_ => panic!("TypeError in BINARY_MODULO")
}
None
},
("BINARY_SUBTRACT", None) => {
let curr_frame = self.curr_frame();
let v1 = curr_frame.stack.pop().unwrap();
let v2 = curr_frame.stack.pop().unwrap();
match (v1.deref(), v2.deref()) {
(&NativeType::Int(v1i), &NativeType::Int(v2i)) => {
curr_frame.stack.push(Rc::new(NativeType::Int(v2i - v1i)));
},
_ => panic!("TypeError in BINARY_SUBSTRACT")
}
None
},
("BINARY_SUBSCR", None) => {
let curr_frame = self.curr_frame();
let tos = curr_frame.stack.pop().unwrap();
let tos1 = curr_frame.stack.pop().unwrap();
debug!("tos: {:?}, tos1: {:?}", tos, tos1);
match (tos1.deref(), tos.deref()) {
(&NativeType::List(ref l), &NativeType::Int(ref index)) => {
let pos_index = (index + l.borrow().len() as i32) % l.borrow().len() as i32;
curr_frame.stack.push(Rc::new(l.borrow()[pos_index as usize].clone()))
},
(&NativeType::List(ref l), &NativeType::Slice(ref opt_start, ref opt_stop, ref opt_step)) => {
let start = match opt_start {
&Some(start) => ((start + l.borrow().len() as i32) % l.borrow().len() as i32) as usize,
&None => 0,
};
let stop = match opt_stop {
&Some(stop) => ((stop + l.borrow().len() as i32) % l.borrow().len() as i32) as usize,
&None => l.borrow().len() as usize,
};
let step = match opt_step {
//Some(step) => step as usize,
&None => 1 as usize,
_ => unimplemented!(),
};
// TODO: we could potentially avoid this copy and use slice
curr_frame.stack.push(Rc::new(NativeType::List(RefCell::new(l.borrow()[start..stop].to_vec()))));
},
(&NativeType::Tuple(ref t), &NativeType::Int(ref index)) => curr_frame.stack.push(Rc::new(t[*index as usize].clone())),
(&NativeType::Str(ref s), &NativeType::Int(ref index)) => {
let idx = (index + s.len() as i32) % s.len() as i32;
curr_frame.stack.push(Rc::new(NativeType::Str(s.chars().nth(idx as usize).unwrap().to_string())));
},
(&NativeType::Str(ref s), &NativeType::Slice(ref opt_start, ref opt_stop, ref opt_step)) => {
let start = match opt_start {
&Some(start) if start > s.len() as i32 => s.len(),
&Some(start) if start <= s.len() as i32 => ((start + s.len() as i32) % s.len() as i32) as usize,
&Some(_) => panic!("Bad start index for string slicing"),
&Some(start) => ((start + s.len() as i32) % s.len() as i32) as usize,
&None => 0,
};
let stop = match opt_stop {
&Some(stop) if stop > s.len() as i32 => s.len(),
&Some(stop) if stop <= s.len() as i32 => ((stop + s.len() as i32) % s.len() as i32) as usize, // Do we need this modding?
&Some(_) => panic!("Bad stop index for string slicing"),
&None => s.len() as usize,
};
let step = match opt_step {
//Some(step) => step as usize,
&None => 1 as usize,
_ => unimplemented!(),
};
curr_frame.stack.push(Rc::new(NativeType::Str(s[start..stop].to_string())));
},
// TODO: implement other Slice possibilities
_ => panic!("TypeError: indexing type {:?} with index {:?} is not supported (yet?)", tos1, tos)
};
None
},
("ROT_TWO", None) => {
let curr_frame = self.curr_frame();
let tos = curr_frame.stack.pop().unwrap();
let tos1 = curr_frame.stack.pop().unwrap();
curr_frame.stack.push(tos);
curr_frame.stack.push(tos1);
None
}
("CALL_FUNCTION", Some(argc)) => {
let kw_count = (argc >> 8) as u8;
let pos_count = (argc & 0xFF) as u8;
// Pop the arguments based on argc
let mut kw_args = HashMap::new();
let mut pos_args = Vec::new();
{
let curr_frame = self.curr_frame();
for _ in 0..kw_count {
let native_val = curr_frame.stack.pop().unwrap();
let native_key = curr_frame.stack.pop().unwrap();
if let (ref val, &NativeType::Str(ref key)) = (native_val, native_key.deref()) {
kw_args.insert(key.clone(), val.clone());
}
else {
panic!("Incorrect type found while building keyword argument list")
}
}
for _ in 0..pos_count {
pos_args.push(curr_frame.stack.pop().unwrap());
}
}
let locals = {
// FIXME: no clone here
self.curr_frame().locals.clone()
};
let func = {
match self.curr_frame().stack.pop().unwrap().deref() {
&NativeType::Function(ref func) => {
// pop argc arguments
// argument: name, args, globals
// build the callargs hashmap
pos_args.reverse();
let mut callargs = HashMap::new();
for (name, val) in func.code.co_varnames.iter().zip(pos_args) {
callargs.insert(name.to_string(), val);
}
// merge callargs with kw_args
let return_value = {
let frame = self.make_frame(func.code.clone(), callargs, Some(locals));
self.run_frame(frame)
};
self.curr_frame().stack.push(Rc::new(return_value));
},
&NativeType::NativeFunction(func) => {
pos_args.reverse();
let return_value = func(pos_args);
self.curr_frame().stack.push(Rc::new(return_value));
},
_ => panic!("The item on the stack should be a code object")
}
};
None
},
("RETURN_VALUE", None) => {
// Hmmm... what is this used?
// I believe we need to push this to the next frame
self.curr_frame().return_value = (*self.curr_frame().stack.pop().unwrap()).clone();
Some("return".to_string())
},
("SETUP_LOOP", Some(delta)) => {
let curr_frame = self.curr_frame();
let curr_offset = curr_frame.get_bytecode_offset().unwrap();
curr_frame.blocks.push(Block {
block_type: "loop".to_string(),
handler: *curr_frame.labels.get(&(curr_offset + delta)).unwrap(),
});
None
},
("POP_BLOCK", None) => {
self.curr_frame().blocks.pop();
None
}
("SetLineno", _) | ("LABEL", _)=> {
// Skip
None
},
(name, _) => {
panic!("Unrecongnizable op code: {}", name);
}
} // end match
} // end dispatch function
}
#[test]
fn test_tuple_serialization(){
let tuple = NativeType::Tuple(vec![NativeType::Int(1),NativeType::Int(2)]);
println!("{}", serde_json::to_string(&tuple).unwrap());
}

View File

@@ -441,10 +441,10 @@ fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> Result<(), Com
Ok(value) => {
// Save non-None values as "_"
use rustpython_vm::pyobject::{IdProtocol, IntoPyObject};
use rustpython_vm::pyobject::IdProtocol;
if !value.is(&vm.get_none()) {
let key = objstr::PyString::from("_").into_pyobject(vm);
let key = "_";
scope.globals.set_item(key, value, vm).unwrap();
}
}

View File

@@ -155,3 +155,12 @@ assert T4.t1.__doc__ == "t1"
cm = classmethod(lambda cls: cls)
assert cm.__func__(int) is int
assert str(super(int, 5)) == "<super: <class 'int'>, <int object>>"
class T5(int):
pass
assert str(super(int, T5(5))) == "<super: <class 'int'>, <T5 object>>"
#assert str(super(type, None)) == "<super: <class 'type'>, NULL>"

View File

@@ -39,9 +39,17 @@ v = Value()
assert f'{v}' == 'foo'
assert f'{v!r}' == 'bar'
assert f'{v!s}' == 'baz'
assert f'{v!a}' == 'bar'
# advanced expressions:
assert f'{True or True}' == 'True'
assert f'{1 == 1}' == 'True'
assert f'{"0" if True else "1"}' == '0'
# Test ascii representation of unicodes:
v = "\u262e"
assert f'>{v}' == '>\u262e'
assert f'>{v!r}' == ">'\u262e'"
assert f'>{v!s}' == '>\u262e'
assert f'>{v!a}' == r">'\u262e'"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

View File

@@ -0,0 +1,3 @@
P4
16 16
ûñ¿úßÕ­±[ñ¥a_ÁX°°ðððð?ÿÿ

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1020 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

View File

@@ -0,0 +1,6 @@
#define python_width 16
#define python_height 16
static char python_bits[] = {
0xDF, 0xFE, 0x8F, 0xFD, 0x5F, 0xFB, 0xAB, 0xFE, 0xB5, 0x8D, 0xDA, 0x8F,
0xA5, 0x86, 0xFA, 0x83, 0x1A, 0x80, 0x0D, 0x80, 0x0D, 0x80, 0x0F, 0xE0,
0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xFC, 0xFF, 0xFF, };

View File

@@ -1,5 +1,8 @@
from testutils import assert_raises
assert -3 // 2 == -2
assert -3 % 2 == 1
a = 4
#print(a ** 3)

View File

@@ -83,6 +83,13 @@ assert str(math.frexp(0.0)) == str((+0.0, 0))
assert str(math.frexp(-0.0)) == str((-0.0, 0))
assert math.frexp(1) == (0.5, 1)
assert math.frexp(1.5) == (0.75, 1)
assert_raises(TypeError, lambda: math.frexp(None))
assert str(math.ldexp(+0.0, 0)) == str(0.0)
assert str(math.ldexp(-0.0, 0)) == str(-0.0)
assert math.ldexp(0.5, 1) == 1
assert math.ldexp(0.75, 1) == 1.5
assert_raises(TypeError, lambda: math.ldexp(None, None))
assert math.frexp(float('inf')) == (float('inf'), 0)
assert str(math.frexp(float('nan'))) == str((float('nan'), 0))
@@ -97,3 +104,17 @@ assert math.gcd(1, -1) == 1
assert math.gcd(-1, -1) == 1
assert math.gcd(125, -255) == 5
assert_raises(TypeError, lambda: math.gcd(1.1, 2))
assert math.factorial(0) == 1
assert math.factorial(1) == 1
assert math.factorial(2) == 2
assert math.factorial(3) == 6
assert math.factorial(10) == 3628800
assert math.factorial(20) == 2432902008176640000
assert_raises(ValueError, lambda: math.factorial(-1))
assert math.modf(1.25) == (0.25, 1.0)
assert math.modf(-1.25) == (-0.25, -1.0)
assert math.modf(2.56) == (0.56, 2.0)
assert math.modf(-2.56) == (-0.56, -2.0)
assert math.modf(1) == (0.0, 1.0)

View File

@@ -30,3 +30,9 @@ fi.read()
fi.close()
with assertRaises(ValueError):
fi.read()
with FileIO('README.md') as fio:
nres = fio.read(1)
assert len(nres) == 1
nres = fio.read(2)
assert len(nres) == 2

View File

@@ -183,3 +183,21 @@ assert_matches_seq(it, [1, 2])
it = i([1, 2, 3], None, None, 3)
assert_matches_seq(it, [1])
# itertools.filterfalse
it = itertools.filterfalse(lambda x: x%2, range(10))
assert 0 == next(it)
assert 2 == next(it)
assert 4 == next(it)
assert 6 == next(it)
assert 8 == next(it)
with assertRaises(StopIteration):
next(it)
l = [0, 1, None, False, True, [], {}]
it = itertools.filterfalse(None, l)
assert 0 == next(it)
assert None == next(it)
assert False == next(it)
assert [] == next(it)
assert {} == next(it)

View File

@@ -232,6 +232,11 @@ with TestWithTempDir() as tmpdir:
os.stat(fname, follow_symlinks=False).st_ino == os.stat(symlink_file, follow_symlinks=False).st_ino
os.stat(fname, follow_symlinks=False).st_mode == os.stat(symlink_file, follow_symlinks=False).st_mode
# os.chmod
if os.name != "nt":
os.chmod(fname, 0o666)
assert oct(os.stat(fname).st_mode) == '0o100666'
# os.path
assert os.path.exists(fname) == True
assert os.path.exists("NO_SUCH_FILE") == False

View File

@@ -0,0 +1,27 @@
# unittest for modified imghdr.py
# Should be replace it into https://github.com/python/cpython/blob/master/Lib/test/test_imghdr.py
import os
import imghdr
TEST_FILES = (
#('python.png', 'png'),
('python.gif', 'gif'),
('python.bmp', 'bmp'),
('python.ppm', 'ppm'),
('python.pgm', 'pgm'),
('python.pbm', 'pbm'),
('python.jpg', 'jpeg'),
('python.ras', 'rast'),
#('python.sgi', 'rgb'),
('python.tiff', 'tiff'),
('python.xbm', 'xbm'),
('python.webp', 'webp'),
('python.exr', 'exr'),
)
resource_dir = os.path.join(os.path.dirname(__file__), 'imghdrdata')
for fname, expected in TEST_FILES:
res = imghdr.what(os.path.join(resource_dir, fname))
assert res == expected

View File

@@ -7,6 +7,7 @@ x = time.gmtime(1000)
assert x.tm_year == 1970
assert x.tm_min == 16
assert x.tm_sec == 40
assert x.tm_isdst == 0
s = time.strftime('%Y-%m-%d-%H-%M-%S', x)
# print(s)

View File

@@ -238,3 +238,21 @@ try:
raise NameError from ex
except NameError as ex2:
pass
# the else clause requires at least one except clause:
with assertRaises(SyntaxError):
exec("""
try:
pass
else:
pass
""")
# Try requires at least except or finally (or both)
with assertRaises(SyntaxError):
exec("""
try:
pass
""")

View File

@@ -29,6 +29,7 @@ num-bigint = { version = "0.2", features = ["serde"] }
num-traits = "=0.2.6"
num-integer = "=0.1.39"
num-rational = "0.2.1"
num-iter = "0.1"
rand = "0.5"
log = "0.3"
rustpython-derive = {path = "../derive", version = "0.1.0"}

View File

@@ -60,8 +60,15 @@ fn builtin_any(iterable: PyIterable<bool>, vm: &VirtualMachine) -> PyResult<bool
fn builtin_ascii(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<String> {
let repr = vm.to_repr(&obj)?;
let ascii = to_ascii(&repr.value);
Ok(ascii)
}
/// Convert a string to ascii compatible, escaping unicodes into escape
/// sequences.
pub fn to_ascii(value: &str) -> String {
let mut ascii = String::new();
for c in repr.value.chars() {
for c in value.chars() {
if c.is_ascii() {
ascii.push(c)
} else {
@@ -74,7 +81,7 @@ fn builtin_ascii(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<String> {
ascii.push_str(&hex)
}
}
Ok(ascii)
ascii
}
fn builtin_bin(x: PyIntRef, _vm: &VirtualMachine) -> String {

View File

@@ -1,7 +1,7 @@
use crate::obj::objbool;
use crate::obj::objstr::PyString;
use crate::pyhash;
use crate::pyobject::{IdProtocol, PyObjectRef, PyResult};
use crate::pyobject::{IdProtocol, IntoPyObject, PyObjectRef, PyResult};
use crate::vm::VirtualMachine;
use num_bigint::ToBigInt;
/// Ordered dictionary implementation.
@@ -53,12 +53,12 @@ impl<T: Clone> Dict<T> {
&mut self,
hash_index: HashIndex,
hash_value: HashValue,
key: &PyObjectRef,
key: PyObjectRef,
value: T,
) {
let entry = DictEntry {
hash: hash_value,
key: key.clone(),
key,
value,
};
let entry_index = self.entries.len();
@@ -73,7 +73,12 @@ impl<T: Clone> Dict<T> {
}
/// Store a key
pub fn insert(&mut self, vm: &VirtualMachine, key: &PyObjectRef, value: T) -> PyResult<()> {
pub fn insert<K: DictKey + IntoPyObject + Copy>(
&mut self,
vm: &VirtualMachine,
key: K,
value: T,
) -> PyResult<()> {
match self.lookup(vm, key)? {
LookupResult::Existing(index) => {
// Update existing key
@@ -89,13 +94,13 @@ impl<T: Clone> Dict<T> {
hash_value,
} => {
// New key:
self.unchecked_push(hash_index, hash_value, key, value);
self.unchecked_push(hash_index, hash_value, key.into_pyobject(vm)?, value);
Ok(())
}
}
}
pub fn contains<K: DictKey>(&self, vm: &VirtualMachine, key: &K) -> PyResult<bool> {
pub fn contains<K: DictKey + Copy>(&self, vm: &VirtualMachine, key: K) -> PyResult<bool> {
if let LookupResult::Existing(_) = self.lookup(vm, key)? {
Ok(true)
} else {
@@ -113,7 +118,7 @@ impl<T: Clone> Dict<T> {
/// Retrieve a key
#[cfg_attr(feature = "flame-it", flame("Dict"))]
pub fn get<K: DictKey>(&self, vm: &VirtualMachine, key: &K) -> PyResult<Option<T>> {
pub fn get<K: DictKey + Copy>(&self, vm: &VirtualMachine, key: K) -> PyResult<Option<T>> {
if let LookupResult::Existing(index) = self.lookup(vm, key)? {
Ok(Some(self.unchecked_get(index)))
} else {
@@ -156,7 +161,7 @@ impl<T: Clone> Dict<T> {
LookupResult::NewIndex {
hash_value,
hash_index,
} => self.unchecked_push(hash_index, hash_value, &key, value),
} => self.unchecked_push(hash_index, hash_value, key.clone(), value),
};
Ok(())
}
@@ -201,7 +206,7 @@ impl<T: Clone> Dict<T> {
/// Lookup the index for the given key.
#[cfg_attr(feature = "flame-it", flame("Dict"))]
fn lookup<K: DictKey>(&self, vm: &VirtualMachine, key: &K) -> PyResult<LookupResult> {
fn lookup<K: DictKey + Copy>(&self, vm: &VirtualMachine, key: K) -> PyResult<LookupResult> {
let hash_value = key.do_hash(vm)?;
let perturb = hash_value;
let mut hash_index: HashIndex = hash_value;
@@ -244,7 +249,7 @@ impl<T: Clone> Dict<T> {
}
/// Retrieve and delete a key
pub fn pop<K: DictKey>(&mut self, vm: &VirtualMachine, key: &K) -> PyResult<Option<T>> {
pub fn pop<K: DictKey + Copy>(&mut self, vm: &VirtualMachine, key: K) -> PyResult<Option<T>> {
if let LookupResult::Existing(index) = self.lookup(vm, key)? {
let value = self.unchecked_get(index);
self.unchecked_delete(index);
@@ -280,26 +285,26 @@ enum LookupResult {
/// - PyObjectRef -> arbitrary python type used as key
/// - str -> string reference used as key, this is often used internally
pub trait DictKey {
fn do_hash(&self, vm: &VirtualMachine) -> PyResult<HashValue>;
fn do_is(&self, other: &PyObjectRef) -> bool;
fn do_eq(&self, vm: &VirtualMachine, other_key: &PyObjectRef) -> PyResult<bool>;
fn do_hash(self, vm: &VirtualMachine) -> PyResult<HashValue>;
fn do_is(self, other: &PyObjectRef) -> bool;
fn do_eq(self, vm: &VirtualMachine, other_key: &PyObjectRef) -> PyResult<bool>;
}
/// Implement trait for PyObjectRef such that we can use python objects
/// to index dictionaries.
impl DictKey for PyObjectRef {
fn do_hash(&self, vm: &VirtualMachine) -> PyResult<HashValue> {
impl DictKey for &PyObjectRef {
fn do_hash(self, vm: &VirtualMachine) -> PyResult<HashValue> {
let raw_hash = vm._hash(self)?;
let mut hasher = DefaultHasher::new();
raw_hash.hash(&mut hasher);
Ok(hasher.finish() as HashValue)
}
fn do_is(&self, other: &PyObjectRef) -> bool {
fn do_is(self, other: &PyObjectRef) -> bool {
self.is(other)
}
fn do_eq(&self, vm: &VirtualMachine, other_key: &PyObjectRef) -> PyResult<bool> {
fn do_eq(self, vm: &VirtualMachine, other_key: &PyObjectRef) -> PyResult<bool> {
let result = vm._eq(self.clone(), other_key.clone())?;
objbool::boolval(vm, result)
}
@@ -307,8 +312,35 @@ impl DictKey for PyObjectRef {
/// Implement trait for the str type, so that we can use strings
/// to index dictionaries.
impl DictKey for String {
fn do_hash(&self, _vm: &VirtualMachine) -> PyResult<HashValue> {
impl DictKey for &str {
fn do_hash(self, _vm: &VirtualMachine) -> PyResult<HashValue> {
// follow a similar route as the hashing of PyStringRef
let raw_hash = pyhash::hash_value(&self.to_string()).to_bigint().unwrap();
let raw_hash = pyhash::hash_bigint(&raw_hash);
let mut hasher = DefaultHasher::new();
raw_hash.hash(&mut hasher);
Ok(hasher.finish() as HashValue)
}
fn do_is(self, _other: &PyObjectRef) -> bool {
// No matter who the other pyobject is, we are never the same thing, since
// we are a str, not a pyobject.
false
}
fn do_eq(self, vm: &VirtualMachine, other_key: &PyObjectRef) -> PyResult<bool> {
if let Some(py_str_value) = other_key.payload::<PyString>() {
Ok(py_str_value.value == self)
} else {
// Fall back to PyString implementation.
let s = vm.new_str(self.to_string());
s.do_eq(vm, other_key)
}
}
}
impl DictKey for &String {
fn do_hash(self, _vm: &VirtualMachine) -> PyResult<HashValue> {
// follow a similar route as the hashing of PyStringRef
let raw_hash = pyhash::hash_value(self).to_bigint().unwrap();
let raw_hash = pyhash::hash_bigint(&raw_hash);
@@ -317,13 +349,13 @@ impl DictKey for String {
Ok(hasher.finish() as HashValue)
}
fn do_is(&self, _other: &PyObjectRef) -> bool {
fn do_is(self, _other: &PyObjectRef) -> bool {
// No matter who the other pyobject is, we are never the same thing, since
// we are a str, not a pyobject.
false
}
fn do_eq(&self, vm: &VirtualMachine, other_key: &PyObjectRef) -> PyResult<bool> {
fn do_eq(self, vm: &VirtualMachine, other_key: &PyObjectRef) -> PyResult<bool> {
if let Some(py_str_value) = other_key.payload::<PyString>() {
Ok(&py_str_value.value == self)
} else {
@@ -364,9 +396,9 @@ mod tests {
assert_eq!(2, dict.len());
assert_eq!(true, dict.contains(&vm, &key1).unwrap());
assert_eq!(true, dict.contains(&vm, &"x".to_string()).unwrap());
assert_eq!(true, dict.contains(&vm, "x").unwrap());
let val = dict.get(&vm, &"x".to_string()).unwrap().unwrap();
let val = dict.get(&vm, "x").unwrap().unwrap();
vm._eq(val, value2)
.expect("retrieved value must be equal to inserted value.");
}
@@ -389,8 +421,8 @@ mod tests {
fn check_hash_equivalence(text: &str) {
let vm: VirtualMachine = Default::default();
let value1 = text.to_string();
let value2 = vm.new_str(value1.clone());
let value1 = text;
let value2 = vm.new_str(value1.to_string());
let hash1 = value1.do_hash(&vm).expect("Hash should not fail.");
let hash2 = value2.do_hash(&vm).expect("Hash should not fail.");

View File

@@ -17,17 +17,14 @@ pub fn eval(vm: &VirtualMachine, source: &str, scope: Scope, source_path: &str)
mod tests {
use super::eval;
use super::VirtualMachine;
use crate::pyobject::IdProtocol;
#[test]
fn test_print_42() {
let source = String::from("print('Hello world')");
let vm: VirtualMachine = Default::default();
let vm = VirtualMachine::default();
let vars = vm.new_scope_with_builtins();
let _result = eval(&vm, &source, vars, "<unittest>");
// TODO: check result?
//assert_eq!(
// parse_ast,
// );
let result = eval(&vm, &source, vars, "<unittest>").expect("this should pass");
assert!(result.is(&vm.ctx.none()));
}
}

View File

@@ -285,12 +285,12 @@ impl Frame {
let dict: PyDictRef =
obj.downcast().expect("Need a dictionary to build a map.");
for (key, value) in dict {
map_obj.set_item(key, value, vm).unwrap();
map_obj.set_item(&key, value, vm).unwrap();
}
}
} else {
for (key, value) in self.pop_multiple(2 * size).into_iter().tuples() {
map_obj.set_item(key, value, vm).unwrap();
map_obj.set_item(&key, value, vm).unwrap();
}
}
@@ -666,7 +666,7 @@ impl Frame {
let value = match conversion {
Some(Str) => vm.to_str(&self.pop_value())?.into_object(),
Some(Repr) => vm.to_repr(&self.pop_value())?.into_object(),
Some(Ascii) => self.pop_value(), // TODO
Some(Ascii) => vm.to_ascii(&self.pop_value())?,
None => self.pop_value(),
};
@@ -946,14 +946,14 @@ impl Frame {
let idx = self.pop_value();
let obj = self.pop_value();
let value = self.pop_value();
obj.set_item(idx, value, vm)?;
obj.set_item(&idx, value, vm)?;
Ok(None)
}
fn execute_delete_subscript(&self, vm: &VirtualMachine) -> FrameResult {
let idx = self.pop_value();
let obj = self.pop_value();
obj.del_item(idx, vm)?;
obj.del_item(&idx, vm)?;
Ok(None)
}
@@ -1062,7 +1062,7 @@ impl Frame {
bytecode::BinaryOperator::Divide => vm._truediv(a_ref, b_ref),
bytecode::BinaryOperator::FloorDivide => vm._floordiv(a_ref, b_ref),
// TODO: Subscript should probably have its own op
bytecode::BinaryOperator::Subscript => a_ref.get_item(b_ref, vm),
bytecode::BinaryOperator::Subscript => a_ref.get_item(&b_ref, vm),
bytecode::BinaryOperator::Modulo => vm._mod(a_ref, b_ref),
bytecode::BinaryOperator::Lshift => vm._lshift(a_ref, b_ref),
bytecode::BinaryOperator::Rshift => vm._rshift(a_ref, b_ref),

View File

@@ -12,7 +12,7 @@ use super::objbool;
use super::objiter;
use super::objstr;
use super::objtype;
use crate::dictdatatype;
use crate::dictdatatype::{self, DictKey};
use crate::obj::objtype::PyClassRef;
use crate::pyobject::PyClassImpl;
@@ -200,19 +200,45 @@ impl PyDictRef {
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
self.entries.borrow_mut().insert(vm, &key, value)
self.inner_setitem_fast(&key, value, vm)
}
/// Set item variant which can be called with multiple
/// key types, such as str to name a notable one.
fn inner_setitem_fast<K: DictKey + IntoPyObject + Copy>(
&self,
key: K,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
self.entries.borrow_mut().insert(vm, key, value)
}
#[cfg_attr(feature = "flame-it", flame("PyDictRef"))]
fn inner_getitem(self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if let Some(value) = self.entries.borrow().get(vm, &key)? {
return Ok(value);
if let Some(value) = self.inner_getitem_option(&key, vm)? {
Ok(value)
} else {
Err(vm.new_key_error(key.clone()))
}
}
/// Return an optional inner item, or an error (can be key error as well)
fn inner_getitem_option<K: DictKey + IntoPyObject + Copy>(
&self,
key: K,
vm: &VirtualMachine,
) -> PyResult<Option<PyObjectRef>> {
if let Some(value) = self.entries.borrow().get(vm, key)? {
return Ok(Some(value));
}
if let Some(method_or_err) = vm.get_method(self.clone().into_object(), "__missing__") {
let method = method_or_err?;
return vm.invoke(&method, vec![key]);
Ok(Some(vm.invoke(&method, vec![key.into_pyobject(vm)?])?))
} else {
Ok(None)
}
Err(vm.new_key_error(key.clone()))
}
fn get(
@@ -318,18 +344,44 @@ impl PyDictRef {
self.entries.borrow().size()
}
pub fn get_item_option<T: IntoPyObject>(
/// This function can be used to get an item without raising the
/// KeyError, so we can simply check upon the result being Some
/// python value, or None.
/// Note that we can pass any type which implements the DictKey
/// trait. Notable examples are String and PyObjectRef.
pub fn get_item_option<T: IntoPyObject + DictKey + Copy>(
&self,
key: T,
vm: &VirtualMachine,
) -> PyResult<Option<PyObjectRef>> {
match self.get_item(key, vm) {
Ok(value) => Ok(Some(value)),
Err(exc) => {
if objtype::isinstance(&exc, &vm.ctx.exceptions.key_error) {
Ok(None)
} else {
Err(exc)
// Test if this object is a true dict, or mabye a subclass?
// If it is a dict, we can directly invoke inner_get_item_option,
// and prevent the creation of the KeyError exception.
// Also note, that we prevent the creation of a full PyString object
// if we lookup local names (which happens all of the time).
if self.typ().is(&vm.ctx.dict_type()) {
// We can take the short path here!
match self.inner_getitem_option(key, vm) {
Err(exc) => {
if objtype::isinstance(&exc, &vm.ctx.exceptions.key_error) {
Ok(None)
} else {
Err(exc)
}
}
Ok(x) => Ok(x),
}
} else {
// Fall back to full get_item with KeyError checking
match self.get_item(key, vm) {
Ok(value) => Ok(Some(value)),
Err(exc) => {
if objtype::isinstance(&exc, &vm.ctx.exceptions.key_error) {
Ok(None)
} else {
Err(exc)
}
}
}
}
@@ -337,20 +389,26 @@ impl PyDictRef {
}
impl ItemProtocol for PyDictRef {
fn get_item<T: IntoPyObject>(&self, key: T, vm: &VirtualMachine) -> PyResult {
fn get_item<T: IntoPyObject + DictKey + Copy>(&self, key: T, vm: &VirtualMachine) -> PyResult {
self.as_object().get_item(key, vm)
}
fn set_item<T: IntoPyObject>(
fn set_item<T: IntoPyObject + DictKey + Copy>(
&self,
key: T,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult {
self.as_object().set_item(key, value, vm)
if self.typ().is(&vm.ctx.dict_type()) {
self.inner_setitem_fast(key, value, vm)
.map(|_| vm.ctx.none())
} else {
// Fall back to slow path if we are in a dict subclass:
self.as_object().set_item(key, value, vm)
}
}
fn del_item<T: IntoPyObject>(&self, key: T, vm: &VirtualMachine) -> PyResult {
fn del_item<T: IntoPyObject + DictKey + Copy>(&self, key: T, vm: &VirtualMachine) -> PyResult {
self.as_object().del_item(key, vm)
}
}

View File

@@ -143,7 +143,7 @@ fn inner_mod(int1: &PyInt, int2: &PyInt, vm: &VirtualMachine) -> PyResult {
if int2.value.is_zero() {
Err(vm.new_zero_division_error("integer modulo by zero".to_string()))
} else {
Ok(vm.ctx.new_int(&int1.value % &int2.value))
Ok(vm.ctx.new_int(int1.value.mod_floor(&int2.value)))
}
}
@@ -299,7 +299,7 @@ impl PyInt {
#[pymethod(name = "__floordiv__")]
fn floordiv(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.int_type()) {
let other = other.payload::<PyInt>().unwrap();
let other = get_py_int(&other);
inner_floordiv(self, &other, &vm)
} else {
Ok(vm.ctx.not_implemented())
@@ -309,7 +309,7 @@ impl PyInt {
#[pymethod(name = "__rfloordiv__")]
fn rfloordiv(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.int_type()) {
let other = other.payload::<PyInt>().unwrap();
let other = get_py_int(&other);
inner_floordiv(&other, self, &vm)
} else {
Ok(vm.ctx.not_implemented())
@@ -322,7 +322,7 @@ impl PyInt {
return Ok(vm.ctx.not_implemented());
}
let other = other.payload::<PyInt>().unwrap();
let other = get_py_int(&other);
inner_lshift(self, other, vm)
}
@@ -332,7 +332,7 @@ impl PyInt {
return Ok(vm.ctx.not_implemented());
}
let other = other.payload::<PyInt>().unwrap();
let other = get_py_int(&other);
inner_lshift(other, self, vm)
}
@@ -342,7 +342,7 @@ impl PyInt {
return Ok(vm.ctx.not_implemented());
}
let other = other.payload::<PyInt>().unwrap();
let other = get_py_int(&other);
inner_rshift(self, other, vm)
}
@@ -352,7 +352,7 @@ impl PyInt {
return Ok(vm.ctx.not_implemented());
}
let other = other.payload::<PyInt>().unwrap();
let other = get_py_int(&other);
inner_rshift(other, self, vm)
}
@@ -402,7 +402,7 @@ impl PyInt {
#[pymethod(name = "__pow__")]
fn pow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.int_type()) {
let other = other.payload::<PyInt>().unwrap();
let other = get_py_int(&other);
inner_pow(self, &other, vm)
} else {
Ok(vm.ctx.not_implemented())
@@ -412,7 +412,7 @@ impl PyInt {
#[pymethod(name = "__rpow__")]
fn rpow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.int_type()) {
let other = other.payload::<PyInt>().unwrap();
let other = get_py_int(&other);
inner_pow(&other, self, vm)
} else {
Ok(vm.ctx.not_implemented())
@@ -422,7 +422,7 @@ impl PyInt {
#[pymethod(name = "__mod__")]
fn mod_(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.int_type()) {
let other = other.payload::<PyInt>().unwrap();
let other = get_py_int(&other);
inner_mod(self, &other, vm)
} else {
Ok(vm.ctx.not_implemented())
@@ -432,7 +432,7 @@ impl PyInt {
#[pymethod(name = "__rmod__")]
fn rmod(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.int_type()) {
let other = other.payload::<PyInt>().unwrap();
let other = get_py_int(&other);
inner_mod(&other, self, vm)
} else {
Ok(vm.ctx.not_implemented())
@@ -442,7 +442,7 @@ impl PyInt {
#[pymethod(name = "__divmod__")]
fn divmod(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.int_type()) {
let other = other.payload::<PyInt>().unwrap();
let other = get_py_int(&other);
inner_divmod(self, &other, vm)
} else {
Ok(vm.ctx.not_implemented())
@@ -452,7 +452,7 @@ impl PyInt {
#[pymethod(name = "__rdivmod__")]
fn rdivmod(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.int_type()) {
let other = other.payload::<PyInt>().unwrap();
let other = get_py_int(&other);
inner_divmod(&other, self, vm)
} else {
Ok(vm.ctx.not_implemented())
@@ -768,11 +768,11 @@ pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, mut base: u32) -> PyResult
// Retrieve inner int value:
pub fn get_value(obj: &PyObjectRef) -> &BigInt {
&obj.payload::<PyInt>().unwrap().value
&get_py_int(obj).value
}
pub fn get_float_value(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<f64> {
obj.payload::<PyInt>().unwrap().float(vm)
get_py_int(obj).float(vm)
}
#[inline]
@@ -821,6 +821,10 @@ fn get_shift_amount(amount: &PyInt, vm: &VirtualMachine) -> PyResult<usize> {
}
}
fn get_py_int(obj: &PyObjectRef) -> &PyInt {
&obj.payload::<PyInt>().unwrap()
}
pub fn init(context: &PyContext) {
PyInt::extend_class(context, &context.types.int_type);
extend_class!(context, &context.types.int_type, {

View File

@@ -78,7 +78,7 @@ fn object_setattr(
}
if let Some(ref dict) = obj.clone().dict {
dict.set_item(attr_name, value, vm)?;
dict.set_item(&attr_name.value, value, vm)?;
Ok(())
} else {
Err(vm.new_attribute_error(format!(
@@ -99,7 +99,7 @@ fn object_delattr(obj: PyObjectRef, attr_name: PyStringRef, vm: &VirtualMachine)
}
if let Some(ref dict) = obj.dict {
dict.del_item(attr_name, vm)?;
dict.del_item(&attr_name.value, vm)?;
Ok(())
} else {
Err(vm.new_attribute_error(format!(

View File

@@ -17,11 +17,14 @@ use super::objslice::{PySlice, PySliceRef};
use super::objtuple::PyTuple;
pub trait PySliceableSequence {
fn do_slice(&self, range: Range<usize>) -> Self;
fn do_slice_reverse(&self, range: Range<usize>) -> Self;
fn do_stepped_slice(&self, range: Range<usize>, step: usize) -> Self;
fn do_stepped_slice_reverse(&self, range: Range<usize>, step: usize) -> Self;
fn empty() -> Self;
type Sliced;
fn do_slice(&self, range: Range<usize>) -> Self::Sliced;
fn do_slice_reverse(&self, range: Range<usize>) -> Self::Sliced;
fn do_stepped_slice(&self, range: Range<usize>, step: usize) -> Self::Sliced;
fn do_stepped_slice_reverse(&self, range: Range<usize>, step: usize) -> Self::Sliced;
fn empty() -> Self::Sliced;
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
fn get_pos(&self, p: i32) -> Option<usize> {
@@ -63,7 +66,11 @@ pub trait PySliceableSequence {
start..stop
}
fn get_slice_items(&self, vm: &VirtualMachine, slice: &PyObjectRef) -> Result<Self, PyObjectRef>
fn get_slice_items(
&self,
vm: &VirtualMachine,
slice: &PyObjectRef,
) -> Result<Self::Sliced, PyObjectRef>
where
Self: Sized,
{
@@ -121,25 +128,27 @@ pub trait PySliceableSequence {
}
impl<T: Clone> PySliceableSequence for Vec<T> {
fn do_slice(&self, range: Range<usize>) -> Self {
type Sliced = Vec<T>;
fn do_slice(&self, range: Range<usize>) -> Self::Sliced {
self[range].to_vec()
}
fn do_slice_reverse(&self, range: Range<usize>) -> Self {
fn do_slice_reverse(&self, range: Range<usize>) -> Self::Sliced {
let mut slice = self[range].to_vec();
slice.reverse();
slice
}
fn do_stepped_slice(&self, range: Range<usize>, step: usize) -> Self {
fn do_stepped_slice(&self, range: Range<usize>, step: usize) -> Self::Sliced {
self[range].iter().step_by(step).cloned().collect()
}
fn do_stepped_slice_reverse(&self, range: Range<usize>, step: usize) -> Self {
fn do_stepped_slice_reverse(&self, range: Range<usize>, step: usize) -> Self::Sliced {
self[range].iter().rev().step_by(step).cloned().collect()
}
fn empty() -> Self {
fn empty() -> Self::Sliced {
Vec::new()
}
@@ -235,7 +244,7 @@ pub fn get_item(
}
} else {
Err(vm.new_type_error(format!(
"TypeError: indexing type {:?} with index {:?} is not supported (yet?)",
"indexing type {:?} with index {:?} is not supported (yet?)",
sequence, subscript
)))
}

View File

@@ -996,7 +996,7 @@ impl PyString {
let mut translated = String::new();
for c in self.value.chars() {
match table.get_item(c as u32, vm) {
match table.get_item(&(c as u32).into_pyobject(vm)?, vm) {
Ok(value) => {
if let Some(text) = value.payload::<PyString>() {
translated.push_str(&text.value);
@@ -1036,11 +1036,11 @@ impl PyString {
Ok(from_str) => {
if to_str.len(vm) == from_str.len(vm) {
for (c1, c2) in from_str.value.chars().zip(to_str.value.chars()) {
new_dict.set_item(c1 as u32, vm.new_int(c2 as u32), vm)?;
new_dict.set_item(&vm.new_int(c1 as u32), vm.new_int(c2 as u32), vm)?;
}
if let OptionalArg::Present(none_str) = none_str {
for c in none_str.value.chars() {
new_dict.set_item(c as u32, vm.get_none(), vm)?;
new_dict.set_item(&vm.new_int(c as u32), vm.get_none(), vm)?;
}
}
new_dict.into_pyobject(vm)
@@ -1061,11 +1061,15 @@ impl PyString {
Ok(dict) => {
for (key, val) in dict {
if let Some(num) = key.payload::<PyInt>() {
new_dict.set_item(num.as_bigint().to_i32(), val, vm)?;
new_dict.set_item(
&num.as_bigint().to_i32().into_pyobject(vm)?,
val,
vm,
)?;
} else if let Some(string) = key.payload::<PyString>() {
if string.len(vm) == 1 {
let num_value = string.value.chars().next().unwrap() as u32;
new_dict.set_item(num_value, val, vm)?;
new_dict.set_item(&num_value.into_pyobject(vm)?, val, vm)?;
} else {
return Err(vm.new_value_error(
"string keys in translate table must be of length 1".to_owned(),
@@ -1459,13 +1463,15 @@ fn perform_format(
}
impl PySliceableSequence for String {
fn do_slice(&self, range: Range<usize>) -> Self {
type Sliced = String;
fn do_slice(&self, range: Range<usize>) -> Self::Sliced {
to_graphemes(self)
.get(range)
.map_or(String::default(), |c| c.join(""))
}
fn do_slice_reverse(&self, range: Range<usize>) -> Self {
fn do_slice_reverse(&self, range: Range<usize>) -> Self::Sliced {
to_graphemes(self)
.get_mut(range)
.map_or(String::default(), |slice| {
@@ -1474,7 +1480,7 @@ impl PySliceableSequence for String {
})
}
fn do_stepped_slice(&self, range: Range<usize>, step: usize) -> Self {
fn do_stepped_slice(&self, range: Range<usize>, step: usize) -> Self::Sliced {
if let Some(s) = to_graphemes(self).get(range) {
return s
.iter()
@@ -1486,7 +1492,7 @@ impl PySliceableSequence for String {
String::default()
}
fn do_stepped_slice_reverse(&self, range: Range<usize>, step: usize) -> Self {
fn do_stepped_slice_reverse(&self, range: Range<usize>, step: usize) -> Self::Sliced {
if let Some(s) = to_graphemes(self).get(range) {
return s
.iter()
@@ -1499,7 +1505,7 @@ impl PySliceableSequence for String {
String::default()
}
fn empty() -> Self {
fn empty() -> Self::Sliced {
String::default()
}

View File

@@ -24,6 +24,7 @@ pub type PySuperRef = PyRef<PySuper>;
pub struct PySuper {
obj: PyObjectRef,
typ: PyObjectRef,
obj_type: PyObjectRef,
}
impl PyValue for PySuper {
@@ -53,9 +54,31 @@ pub fn init(context: &PyContext) {
"__new__" => context.new_rustfunc(super_new),
"__getattribute__" => context.new_rustfunc(super_getattribute),
"__doc__" => context.new_str(super_doc.to_string()),
"__str__" => context.new_rustfunc(super_str),
"__repr__" => context.new_rustfunc(super_repr),
});
}
fn super_str(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
vm.call_method(&zelf, "__repr__", vec![])
}
fn super_repr(zelf: PyObjectRef, _vm: &VirtualMachine) -> String {
let super_obj = zelf.downcast::<PySuper>().unwrap();
let class_type_str = if let Ok(type_class) = super_obj.typ.clone().downcast::<PyClass>() {
type_class.name.clone()
} else {
"NONE".to_string()
};
match super_obj.obj_type.clone().downcast::<PyClass>() {
Ok(obj_class_typ) => format!(
"<super: <class '{}'>, <{} object>>",
class_type_str, obj_class_typ.name
),
_ => format!("<super: <class '{}'> NULL>", class_type_str),
}
}
fn super_getattribute(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
@@ -141,7 +164,7 @@ fn super_new(
};
// Check obj type:
if !objtype::isinstance(&py_obj, &py_type) {
let obj_type = if !objtype::isinstance(&py_obj, &py_type) {
let is_subclass = if let Ok(py_obj) = PyClassRef::try_from_object(vm, py_obj.clone()) {
objtype::issubclass(&py_obj, &py_type)
} else {
@@ -152,11 +175,15 @@ fn super_new(
"super(type, obj): obj must be an instance or subtype of type".to_string(),
));
}
}
PyClassRef::try_from_object(vm, py_obj.clone())?
} else {
py_obj.class()
};
PySuper {
obj: py_obj,
typ: py_type.into_object(),
obj_type: obj_type.into_object(),
}
.into_ref_with_type(vm, cls)
}

View File

@@ -211,7 +211,7 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> {
// Although JSON keys must be strings, implementation accepts any keys
// and can be reused by other deserializers without such limit
while let Some((key_obj, value)) = access.next_entry_seed(self.clone(), self.clone())? {
dict.set_item(key_obj, value, self.vm).unwrap();
dict.set_item(&key_obj, value, self.vm).unwrap();
}
Ok(dict.into_object())
}

View File

@@ -11,6 +11,7 @@ use num_complex::Complex64;
use num_traits::{One, Zero};
use crate::bytecode;
use crate::dictdatatype::DictKey;
use crate::exceptions;
use crate::function::{IntoPyNativeFunc, PyFuncArgs};
use crate::obj::objbuiltinfunc::PyBuiltinFunction;
@@ -764,15 +765,17 @@ impl<T> TypeProtocol for PyRef<T> {
}
}
/// The python item protocol. Mostly applies to dictionaries.
/// Allows getting, setting and deletion of keys-value pairs.
pub trait ItemProtocol {
fn get_item<T: IntoPyObject>(&self, key: T, vm: &VirtualMachine) -> PyResult;
fn set_item<T: IntoPyObject>(
fn get_item<T: IntoPyObject + DictKey + Copy>(&self, key: T, vm: &VirtualMachine) -> PyResult;
fn set_item<T: IntoPyObject + DictKey + Copy>(
&self,
key: T,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult;
fn del_item<T: IntoPyObject>(&self, key: T, vm: &VirtualMachine) -> PyResult;
fn del_item<T: IntoPyObject + DictKey + Copy>(&self, key: T, vm: &VirtualMachine) -> PyResult;
}
impl ItemProtocol for PyObjectRef {
@@ -965,6 +968,12 @@ impl IntoPyObject for PyObjectRef {
}
}
impl IntoPyObject for &PyObjectRef {
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
Ok(self.clone())
}
}
impl<T> IntoPyObject for PyResult<T>
where
T: IntoPyObject,

View File

@@ -138,11 +138,8 @@ impl NameProtocol for Scope {
}
}
if let Some(value) = self.globals.get_item_option(name, vm).unwrap() {
return Some(value);
}
vm.get_attribute(vm.builtins.clone(), name).ok()
// Fall back to loading a global after all scopes have been searched!
self.load_global(vm, name)
}
#[cfg_attr(feature = "flame-it", flame("Scope"))]
@@ -174,7 +171,11 @@ impl NameProtocol for Scope {
#[cfg_attr(feature = "flame-it", flame("Scope"))]
fn load_global(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
self.globals.get_item_option(name, vm).unwrap()
if let Some(value) = self.globals.get_item_option(name, vm).unwrap() {
Some(value)
} else {
vm.get_attribute(vm.builtins.clone(), name).ok()
}
}
fn store_global(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef) {

View File

@@ -252,7 +252,7 @@ fn optional_statements_to_ast(
let statements = if let Some(statements) = statements {
statements_to_ast(vm, statements)?.into_object()
} else {
vm.ctx.none()
vm.ctx.new_list(vec![])
};
Ok(statements)
}
@@ -283,6 +283,17 @@ fn make_string_list(vm: &VirtualMachine, names: &[String]) -> PyObjectRef {
)
}
fn optional_expressions_to_ast(
vm: &VirtualMachine,
expressions: &[Option<ast::Expression>],
) -> PyResult<PyListRef> {
let py_expression_nodes: PyResult<_> = expressions
.iter()
.map(|expression| Ok(optional_expression_to_ast(vm, expression)?))
.collect();
Ok(vm.ctx.new_list(py_expression_nodes?).downcast().unwrap())
}
fn optional_expression_to_ast(vm: &VirtualMachine, value: &Option<ast::Expression>) -> PyResult {
let value = if let Some(value) = value {
expression_to_ast(vm, value)?.into_object()
@@ -526,8 +537,23 @@ fn operator_string(op: &ast::Operator) -> String {
}
fn parameters_to_ast(vm: &VirtualMachine, args: &ast::Parameters) -> PyResult<AstNodeRef> {
let args = map_ast(parameter_to_ast, vm, &args.args)?;
Ok(node!(vm, arguments, { args => args }))
Ok(node!(vm, arguments, {
args => map_ast(parameter_to_ast, vm, &args.args)?,
vararg => vararg_to_ast(vm, &args.vararg)?,
kwonlyargs => map_ast(parameter_to_ast, vm, &args.kwonlyargs)?,
kw_defaults => optional_expressions_to_ast(vm, &args.kw_defaults)?,
kwarg => vararg_to_ast(vm, &args.kwarg)?,
defaults => expressions_to_ast(vm, &args.defaults)?
}))
}
fn vararg_to_ast(vm: &VirtualMachine, vararg: &ast::Varargs) -> PyResult {
let py_node = match vararg {
ast::Varargs::None => vm.get_none(),
ast::Varargs::Unnamed => vm.get_none(),
ast::Varargs::Named(parameter) => parameter_to_ast(vm, parameter)?.into_object(),
};
Ok(py_node)
}
fn parameter_to_ast(vm: &VirtualMachine, parameter: &ast::Parameter) -> PyResult<AstNodeRef> {
@@ -537,10 +563,15 @@ fn parameter_to_ast(vm: &VirtualMachine, parameter: &ast::Parameter) -> PyResult
vm.ctx.none()
};
Ok(node!(vm, arg, {
let py_node = node!(vm, arg, {
arg => vm.ctx.new_str(parameter.arg.to_string()),
annotation => py_annotation
}))
});
let lineno = vm.ctx.new_int(parameter.location.row());
vm.set_attr(py_node.as_object(), "lineno", lineno)?;
Ok(py_node)
}
fn optional_string_to_py_obj(vm: &VirtualMachine, name: &Option<String>) -> PyObjectRef {

View File

@@ -9,7 +9,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
for (name, code) in ERROR_CODES {
let name = vm.new_str((*name).to_owned());
let code = vm.ctx.new_int(*code);
errorcode.set_item(code.clone(), name.clone(), vm).unwrap();
errorcode.set_item(&code, name.clone(), vm).unwrap();
vm.set_attr(&module, name, code).unwrap();
}
module

View File

@@ -346,18 +346,36 @@ fn file_io_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
}
fn file_io_read(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(file_io, None)]);
arg_check!(
vm,
args,
required = [(file_io, None)],
optional = [(read_byte, Some(vm.ctx.int_type()))]
);
let file_no = vm.get_attribute(file_io.clone(), "fileno")?;
let raw_fd = objint::get_value(&file_no).to_i64().unwrap();
let mut handle = os::rust_file(raw_fd);
let mut bytes = vec![];
match handle.read_to_end(&mut bytes) {
Ok(_) => {}
Err(_) => return Err(vm.new_value_error("Error reading from Buffer".to_string())),
}
let bytes = match read_byte {
None => {
let mut bytes = vec![];
handle
.read_to_end(&mut bytes)
.map_err(|_| vm.new_value_error("Error reading from Buffer".to_string()))?;
bytes
}
Some(read_byte) => {
let mut bytes = vec![0; objint::get_value(&read_byte).to_usize().unwrap()];
handle
.read_exact(&mut bytes)
.map_err(|_| vm.new_value_error("Error reading from Buffer".to_string()))?;
let updated = os::raw_file_number(handle);
vm.set_attr(file_io, "fileno", vm.ctx.new_int(updated))?;
bytes
}
};
Ok(vm.ctx.new_bytes(bytes))
}

View File

@@ -418,6 +418,64 @@ impl PyItertoolsIslice {
}
}
#[pyclass]
#[derive(Debug)]
struct PyItertoolsFilterFalse {
predicate: PyObjectRef,
iterable: PyObjectRef,
}
impl PyValue for PyItertoolsFilterFalse {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.class("itertools", "filterfalse")
}
}
#[pyimpl]
impl PyItertoolsFilterFalse {
#[pymethod(name = "__new__")]
#[allow(clippy::new_ret_no_self)]
fn new(
_cls: PyClassRef,
predicate: PyObjectRef,
iterable: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult {
let iter = get_iter(vm, &iterable)?;
Ok(PyItertoolsFilterFalse {
predicate,
iterable: iter,
}
.into_ref(vm)
.into_object())
}
#[pymethod(name = "__next__")]
fn next(&self, vm: &VirtualMachine) -> PyResult {
let predicate = &self.predicate;
let iterable = &self.iterable;
loop {
let obj = call_next(vm, iterable)?;
let pred_value = if predicate.is(&vm.get_none()) {
obj.clone()
} else {
vm.invoke(predicate, vec![obj.clone()])?
};
if !objbool::boolval(vm, pred_value)? {
return Ok(obj);
}
}
}
#[pymethod(name = "__iter__")]
fn iter(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyRef<Self> {
zelf
}
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
@@ -436,6 +494,9 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let islice = PyItertoolsIslice::make_class(ctx);
let filterfalse = ctx.new_class("filterfalse", ctx.object());
PyItertoolsFilterFalse::extend_class(ctx, &filterfalse);
py_module!(vm, "itertools", {
"chain" => chain,
"count" => count,
@@ -443,5 +504,6 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
"starmap" => starmap,
"takewhile" => takewhile,
"islice" => islice,
"filterfalse" => filterfalse,
})
}

View File

@@ -6,9 +6,14 @@
use statrs::function::erf::{erf, erfc};
use statrs::function::gamma::{gamma, ln_gamma};
use num_bigint::BigInt;
use num_traits::cast::ToPrimitive;
use num_traits::{One, Zero};
use crate::function::PyFuncArgs;
use crate::obj::objfloat::PyFloatRef;
use crate::obj::objint::PyIntRef;
use crate::obj::{objfloat, objtype};
use crate::obj::{objfloat, objint, objtype};
use crate::pyobject::{PyObjectRef, PyResult, TypeProtocol};
use crate::vm::VirtualMachine;
@@ -226,11 +231,41 @@ fn math_frexp(value: PyObjectRef, vm: &VirtualMachine) -> PyResult {
)
}
fn math_ldexp(value: PyFloatRef, i: PyIntRef, vm: &VirtualMachine) -> PyResult {
Ok(vm
.ctx
.new_float(value.to_f64() * (2_f64).powf(i.as_bigint().to_f64().unwrap())))
}
fn math_gcd(a: PyIntRef, b: PyIntRef, vm: &VirtualMachine) -> PyResult {
use num_integer::Integer;
Ok(vm.new_int(a.as_bigint().gcd(b.as_bigint())))
}
fn math_factorial(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(value, None)]);
let value = objint::get_value(value);
if *value < BigInt::zero() {
return Err(vm.new_value_error("factorial() not defined for negative values".to_string()));
} else if *value <= BigInt::one() {
return Ok(vm.ctx.new_int(BigInt::from(1u64)));
}
let ret: BigInt = num_iter::range_inclusive(BigInt::from(1u64), value.clone()).product();
Ok(vm.ctx.new_int(ret))
}
fn math_modf(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(value, None)]);
let x = objfloat::make_float(vm, value)?;
let fract = x.fract();
let int = x.trunc();
Ok(vm
.ctx
.new_tuple(vec![vm.ctx.new_float(fract), vm.ctx.new_float(int)]))
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
@@ -279,6 +314,8 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
"lgamma" => ctx.new_rustfunc(math_lgamma),
"frexp" => ctx.new_rustfunc(math_frexp),
"ldexp" => ctx.new_rustfunc(math_ldexp),
"modf" => ctx.new_rustfunc(math_modf),
// Rounding functions:
"trunc" => ctx.new_rustfunc(math_trunc),
@@ -288,6 +325,9 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
// Gcd function
"gcd" => ctx.new_rustfunc(math_gcd),
// Factorial function
"factorial" => ctx.new_rustfunc(math_factorial),
// Constants:
"pi" => ctx.new_float(std::f64::consts::PI), // 3.14159...
"e" => ctx.new_float(std::f64::consts::E), // 2.71..

View File

@@ -1,3 +1,4 @@
use num_cpus;
use std::cell::RefCell;
use std::ffi::CStr;
use std::fs::File;
@@ -6,8 +7,6 @@ use std::io::{self, Error, ErrorKind, Read, Write};
use std::time::{Duration, SystemTime};
use std::{env, fs};
use num_cpus;
#[cfg(unix)]
use nix::errno::Errno;
#[cfg(all(unix, not(target_os = "redox")))]
@@ -221,6 +220,121 @@ fn convert_nix_errno(vm: &VirtualMachine, errno: Errno) -> PyClassRef {
}
}
// Flags for os_access
bitflags! {
pub struct AccessFlags: u8{
const F_OK = 0;
const R_OK = 4;
const W_OK = 2;
const X_OK = 1;
}
}
#[cfg(unix)]
struct Permissions {
is_readable: bool,
is_writable: bool,
is_executable: bool,
}
#[cfg(unix)]
fn get_permissions(mode: u32) -> Permissions {
Permissions {
is_readable: mode & 4 != 0,
is_writable: mode & 2 != 0,
is_executable: mode & 1 != 0,
}
}
#[cfg(unix)]
fn get_right_permission(
mode: u32,
file_owner: Uid,
file_group: Gid,
) -> Result<Permissions, nix::Error> {
let owner_mode = (mode & 0o700) >> 6;
let owner_permissions = get_permissions(owner_mode);
let group_mode = (mode & 0o070) >> 3;
let group_permissions = get_permissions(group_mode);
let others_mode = mode & 0o007;
let others_permissions = get_permissions(others_mode);
let user_id = nix::unistd::getuid();
let groups_ids = getgroups()?;
if file_owner == user_id {
Ok(owner_permissions)
} else if groups_ids.contains(&file_group) {
Ok(group_permissions)
} else {
Ok(others_permissions)
}
}
#[cfg(target_os = "macos")]
fn getgroups() -> nix::Result<Vec<Gid>> {
use libc::{c_int, gid_t};
use std::ptr;
let ret = unsafe { libc::getgroups(0, ptr::null_mut()) };
let mut groups = Vec::<Gid>::with_capacity(Errno::result(ret)? as usize);
loop {
let ret = unsafe {
libc::getgroups(
groups.capacity() as c_int,
groups.as_mut_ptr() as *mut gid_t,
)
};
return Errno::result(ret).map(|s| {
unsafe { groups.set_len(s as usize) };
groups
});
}
}
#[cfg(target_os = "linux")]
fn getgroups() -> nix::Result<Vec<Gid>> {
nix::unistd::getgroups()
}
#[cfg(unix)]
fn os_access(path: PyStringRef, mode: u8, vm: &VirtualMachine) -> PyResult<bool> {
use std::os::unix::fs::MetadataExt;
let path = path.as_str();
let flags = AccessFlags::from_bits(mode).ok_or_else(|| {
vm.new_value_error(
"One of the flags is wrong, there are only 4 possibilities F_OK, R_OK, W_OK and X_OK"
.to_string(),
)
})?;
let metadata = fs::metadata(path);
// if it's only checking for F_OK
if flags == AccessFlags::F_OK {
return Ok(metadata.is_ok());
}
let metadata = metadata.map_err(|err| convert_io_error(vm, err))?;
let user_id = metadata.uid();
let group_id = metadata.gid();
let mode = metadata.mode();
let perm = get_right_permission(mode, Uid::from_raw(user_id), Gid::from_raw(group_id))
.map_err(|err| convert_nix_error(vm, err))?;
let r_ok = !flags.contains(AccessFlags::R_OK) || perm.is_readable;
let w_ok = !flags.contains(AccessFlags::W_OK) || perm.is_writable;
let x_ok = !flags.contains(AccessFlags::X_OK) || perm.is_executable;
Ok(r_ok && w_ok && x_ok)
}
fn os_error(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
@@ -748,6 +862,28 @@ fn os_chdir(path: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
env::set_current_dir(&path.value).map_err(|err| convert_io_error(vm, err))
}
#[cfg(unix)]
fn os_chmod(
path: PyStringRef,
dir_fd: DirFd,
mode: u32,
follow_symlinks: FollowSymlinks,
vm: &VirtualMachine,
) -> PyResult<()> {
use std::os::unix::fs::PermissionsExt;
let path = make_path(vm, path, &dir_fd);
let metadata = if follow_symlinks.follow_symlinks {
fs::metadata(&path.value)
} else {
fs::symlink_metadata(&path.value)
};
let meta = metadata.map_err(|err| convert_io_error(vm, err))?;
let mut permissions = meta.permissions();
permissions.set_mode(mode);
fs::set_permissions(&path.value, permissions).map_err(|err| convert_io_error(vm, err))?;
Ok(())
}
fn os_fspath(path: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::issubclass(&path.class(), &vm.ctx.str_type())
|| objtype::issubclass(&path.class(), &vm.ctx.bytes_type())
@@ -960,12 +1096,11 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
}
}
}
let support_funcs = vec![
let mut support_funcs = vec![
SupportFunc::new(vm, "open", os_open, None, Some(false), None),
// access Some Some None
SupportFunc::new(vm, "chdir", os_chdir, Some(false), None, None),
// chflags Some, None Some
// chmod Some Some Some
// chown Some Some Some
// chroot Some None None
SupportFunc::new(vm, "listdir", os_listdir, Some(false), None, None),
@@ -985,6 +1120,15 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
SupportFunc::new(vm, "unlink", os_remove, Some(false), Some(false), None),
// utime Some Some Some
];
#[cfg(unix)]
support_funcs.extend(vec![SupportFunc::new(
vm,
"chmod",
os_chmod,
Some(false),
Some(false),
Some(false),
)]);
let supports_fd = PySet::default().into_ref(vm);
let supports_dir_fd = PySet::default().into_ref(vm);
let supports_follow_symlinks = PySet::default().into_ref(vm);
@@ -1014,10 +1158,10 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
"O_APPEND" => ctx.new_int(FileCreationFlags::O_APPEND.bits()),
"O_EXCL" => ctx.new_int(FileCreationFlags::O_EXCL.bits()),
"O_CREAT" => ctx.new_int(FileCreationFlags::O_CREAT.bits()),
"F_OK" => ctx.new_int(0),
"R_OK" => ctx.new_int(4),
"W_OK" => ctx.new_int(2),
"X_OK" => ctx.new_int(1),
"F_OK" => ctx.new_int(AccessFlags::F_OK.bits()),
"R_OK" => ctx.new_int(AccessFlags::R_OK.bits()),
"W_OK" => ctx.new_int(AccessFlags::W_OK.bits()),
"X_OK" => ctx.new_int(AccessFlags::X_OK.bits()),
"getpid" => ctx.new_rustfunc(os_getpid),
"cpu_count" => ctx.new_rustfunc(os_cpu_count)
});
@@ -1068,6 +1212,8 @@ fn extend_module_platform_specific(vm: &VirtualMachine, module: PyObjectRef) ->
"setgid" => ctx.new_rustfunc(os_setgid),
"setpgid" => ctx.new_rustfunc(os_setpgid),
"setuid" => ctx.new_rustfunc(os_setuid),
"access" => ctx.new_rustfunc(os_access),
"chmod" => ctx.new_rustfunc(os_chmod)
});
#[cfg(not(target_os = "redox"))]

View File

@@ -2,16 +2,19 @@
/// See also:
/// https://docs.python.org/3/library/time.html
use std::fmt;
use std::ops::Range;
use std::thread;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use crate::function::{OptionalArg, PyFuncArgs};
use crate::obj::objint::PyIntRef;
use crate::obj::objint::PyInt;
use crate::obj::objsequence::get_sequence_index;
use crate::obj::objsequence::PySliceableSequence;
use crate::obj::objslice::PySlice;
use crate::obj::objstr::PyStringRef;
use crate::obj::objtype::PyClassRef;
use crate::obj::{objfloat, objint, objtype};
use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue};
use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol};
use crate::vm::VirtualMachine;
use num_traits::cast::ToPrimitive;
@@ -85,13 +88,15 @@ fn time_gmtime(secs: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult<
OptionalArg::Present(secs) => pyobj_to_naive_date_time(&secs, vm)?.unwrap_or(default),
OptionalArg::Missing => default,
};
let value = PyStructTime::new(instant);
let value = PyStructTime::new(instant, 0);
Ok(value)
}
fn time_localtime(secs: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult<PyStructTime> {
let instant = optional_or_localtime(secs, vm)?;
let value = PyStructTime::new(instant);
// TODO: isdst flag must be valid value here
// https://docs.python.org/3/library/time.html#time.localtime
let value = PyStructTime::new(instant, -1);
Ok(value)
}
@@ -157,13 +162,14 @@ fn time_strptime(
};
let instant = NaiveDateTime::parse_from_str(&string.value, &format)
.map_err(|e| vm.new_value_error(format!("Parse error: {:?}", e)))?;
let struct_time = PyStructTime::new(instant);
let struct_time = PyStructTime::new(instant, -1);
Ok(struct_time)
}
#[pyclass(name = "struct_time")]
struct PyStructTime {
tm: NaiveDateTime,
isdst: i32,
}
type PyStructTimeRef = PyRef<PyStructTime>;
@@ -182,20 +188,19 @@ impl PyValue for PyStructTime {
#[pyimpl]
impl PyStructTime {
fn new(tm: NaiveDateTime) -> Self {
PyStructTime { tm }
fn new(tm: NaiveDateTime, isdst: i32) -> Self {
PyStructTime { tm, isdst }
}
#[pymethod(name = "__repr__")]
fn repr(&self, _vm: &VirtualMachine) -> String {
// TODO: extract year day and isdst somehow..
format!(
"time.struct_time(tm_year={}, tm_mon={}, tm_mday={}, tm_hour={}, tm_min={}, tm_sec={}, tm_wday={}, tm_yday={})",
self.tm.date().year(), self.tm.date().month(), self.tm.date().day(),
self.tm.time().hour(), self.tm.time().minute(), self.tm.time().second(),
self.tm.date().weekday().num_days_from_monday(),
self.tm.date().ordinal()
)
"time.struct_time(tm_year={}, tm_mon={}, tm_mday={}, tm_hour={}, tm_min={}, tm_sec={}, tm_wday={}, tm_yday={}, tm_isdst={})",
self._year(), self._mon(), self._mday(),
self._hour(), self._min(), self._sec(),
self._wday(), self._yday(), self._isdst(),
)
}
fn get_date_time(&self) -> NaiveDateTime {
@@ -204,65 +209,182 @@ impl PyStructTime {
#[pymethod(name = "__len__")]
fn len(&self, _vm: &VirtualMachine) -> usize {
8
9
}
#[pymethod(name = "__getitem__")]
fn getitem(&self, needle: PyIntRef, vm: &VirtualMachine) -> PyResult {
let index = get_sequence_index(vm, &needle, 8)?;
match index {
0 => Ok(vm.ctx.new_int(self.tm.date().year())),
1 => Ok(vm.ctx.new_int(self.tm.date().month())),
2 => Ok(vm.ctx.new_int(self.tm.date().day())),
3 => Ok(vm.ctx.new_int(self.tm.time().hour())),
4 => Ok(vm.ctx.new_int(self.tm.time().minute())),
5 => Ok(vm.ctx.new_int(self.tm.time().second())),
6 => Ok(vm
.ctx
.new_int(self.tm.date().weekday().num_days_from_monday())),
7 => Ok(vm.ctx.new_int(self.tm.date().ordinal())),
_ => unreachable!(),
fn getitem(&self, subscript: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if subscript.payload::<PyInt>().is_some() {
let needle = subscript.downcast().unwrap();
let index = get_sequence_index(vm, &needle, 9)?;
let tm_fn = TM_FUNCTIONS[index];
Ok(vm.new_int(tm_fn(self)))
} else if subscript.payload::<PySlice>().is_some() {
let values = self.get_slice_items(vm, &subscript)?;
let objs = values.iter().map(|v| vm.new_int(*v));
Ok(vm.ctx.new_tuple(objs.collect()))
} else {
Err(vm.new_type_error(format!(
"TypeError: tuple indices must be integers or slices, {}",
subscript.class().name
)))
}
}
#[inline]
fn _year(&self) -> i32 {
self.tm.date().year()
}
#[inline]
fn _mon(&self) -> i32 {
self.tm.date().month() as i32
}
#[inline]
fn _mday(&self) -> i32 {
self.tm.date().day() as i32
}
#[inline]
fn _hour(&self) -> i32 {
self.tm.time().hour() as i32
}
#[inline]
fn _min(&self) -> i32 {
self.tm.time().minute() as i32
}
#[inline]
fn _sec(&self) -> i32 {
self.tm.time().second() as i32
}
#[inline]
fn _wday(&self) -> i32 {
self.tm.date().weekday().num_days_from_monday() as i32
}
#[inline]
fn _yday(&self) -> i32 {
self.tm.date().ordinal() as i32
}
#[inline]
fn _isdst(&self) -> i32 {
self.isdst
}
#[pyproperty(name = "tm_year")]
fn tm_year(&self, _vm: &VirtualMachine) -> i32 {
self.tm.date().year()
self._year()
}
#[pyproperty(name = "tm_mon")]
fn tm_mon(&self, _vm: &VirtualMachine) -> u32 {
self.tm.date().month()
self._mon() as u32
}
#[pyproperty(name = "tm_mday")]
fn tm_mday(&self, _vm: &VirtualMachine) -> u32 {
self.tm.date().day()
self._mday() as u32
}
#[pyproperty(name = "tm_hour")]
fn tm_hour(&self, _vm: &VirtualMachine) -> u32 {
self.tm.time().hour()
self._hour() as u32
}
#[pyproperty(name = "tm_min")]
fn tm_min(&self, _vm: &VirtualMachine) -> u32 {
self.tm.time().minute()
self._min() as u32
}
#[pyproperty(name = "tm_sec")]
fn tm_sec(&self, _vm: &VirtualMachine) -> u32 {
self.tm.time().second()
self._sec() as u32
}
#[pyproperty(name = "tm_wday")]
fn tm_wday(&self, _vm: &VirtualMachine) -> u32 {
self.tm.date().weekday().num_days_from_monday()
self._wday() as u32
}
#[pyproperty(name = "tm_yday")]
fn tm_yday(&self, _vm: &VirtualMachine) -> u32 {
self.tm.date().ordinal()
self._yday() as u32
}
#[pyproperty(name = "tm_isdst")]
fn tm_isdst(&self, _vm: &VirtualMachine) -> i32 {
self._isdst()
}
}
type TmFunction = fn(&PyStructTime) -> i32;
const TM_FUNCTIONS: [TmFunction; 9] = [
PyStructTime::_year,
PyStructTime::_mon,
PyStructTime::_mday,
PyStructTime::_hour,
PyStructTime::_min,
PyStructTime::_sec,
PyStructTime::_wday,
PyStructTime::_yday,
PyStructTime::_isdst,
];
impl PySliceableSequence for PyStructTime {
type Sliced = Vec<i32>;
fn do_slice(&self, range: Range<usize>) -> Self::Sliced {
if let Some(fs) = TM_FUNCTIONS.get(range) {
fs.iter().map(|f| f(self)).collect()
} else {
vec![]
}
}
fn do_slice_reverse(&self, range: Range<usize>) -> Self::Sliced {
if let Some(fs) = TM_FUNCTIONS.get(range) {
fs.iter().rev().map(|f| f(self)).collect()
} else {
vec![]
}
}
fn do_stepped_slice(&self, range: Range<usize>, step: usize) -> Self::Sliced {
if let Some(fs) = TM_FUNCTIONS.get(range) {
fs.iter().cloned().step_by(step).map(|f| f(self)).collect()
} else {
vec![]
}
}
fn do_stepped_slice_reverse(&self, range: Range<usize>, step: usize) -> Self::Sliced {
if let Some(fs) = TM_FUNCTIONS.get(range) {
fs.iter()
.rev()
.cloned()
.step_by(step)
.map(|f| f(self))
.collect()
} else {
vec![]
}
}
fn empty() -> Self::Sliced {
panic!("struct_time is not empty");
}
fn len(&self) -> usize {
TM_FUNCTIONS.len()
}
fn is_empty(&self) -> bool {
false
}
}

View File

@@ -99,7 +99,7 @@ fn zlib_compress(
.unwrap_or(libz::Z_DEFAULT_COMPRESSION);
let compression = match level {
valid_level @ libz::Z_NO_COMPRESSION...libz::Z_BEST_COMPRESSION => {
valid_level @ libz::Z_NO_COMPRESSION..=libz::Z_BEST_COMPRESSION => {
Compression::new(valid_level as u32)
}
libz::Z_DEFAULT_COMPRESSION => Compression::default(),

View File

@@ -11,7 +11,7 @@ use std::fmt;
use std::rc::Rc;
use std::sync::{Mutex, MutexGuard};
use crate::builtins;
use crate::builtins::{self, to_ascii};
use crate::bytecode;
use crate::frame::{ExecutionResult, Frame, FrameRef};
use crate::frozen;
@@ -445,6 +445,13 @@ impl VirtualMachine {
TryFromObject::try_from_object(self, repr)
}
pub fn to_ascii(&self, obj: &PyObjectRef) -> PyResult {
let repr = self.call_method(obj, "__repr__", vec![])?;
let repr: PyStringRef = TryFromObject::try_from_object(self, repr)?;
let ascii = to_ascii(&repr.value);
Ok(self.new_str(ascii))
}
pub fn import(&self, module: &str, from_list: &PyObjectRef, level: usize) -> PyResult {
// if the import inputs seem weird, e.g a package import or something, rather than just
// a straight `import ident`
@@ -452,14 +459,13 @@ impl VirtualMachine {
|| level != 0
|| objbool::boolval(self, from_list.clone()).unwrap_or(true);
let module = self.new_str(module.to_owned());
let cached_module = if weird {
None
} else {
let sys_modules = self.get_attribute(self.sys_module.clone(), "modules")?;
sys_modules.get_item(module.clone(), self).ok()
sys_modules.get_item(module, self).ok()
};
match cached_module {
Some(module) => Ok(module),
None => {
@@ -478,7 +484,7 @@ impl VirtualMachine {
self.invoke(
&import_func,
vec![
module,
self.new_str(module.to_owned()),
globals,
locals,
from_list.clone(),
@@ -936,7 +942,7 @@ impl VirtualMachine {
}
let attr = if let Some(ref dict) = obj.dict {
dict.get_item_option(name_str.clone(), self)?
dict.get_item_option(&name_str.value, self)?
} else {
None
};