Skip to content

Commit 2fdfcb9

Browse files
committed
Add parser support for async for loop. Add yield expression inside parentheses.
1 parent c5e7fba commit 2fdfcb9

File tree

7 files changed

+97
-11
lines changed

7 files changed

+97
-11
lines changed

compiler/src/compile.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,10 @@ impl Compiler {
17021702

17031703
let mut loop_labels = vec![];
17041704
for generator in generators {
1705+
if generator.is_async {
1706+
unimplemented!("async for comprehensions");
1707+
}
1708+
17051709
if loop_labels.is_empty() {
17061710
// Load iterator onto stack (passed as first argument):
17071711
self.emit(Instruction::LoadName {

examples/parse_folder.rs

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ extern crate log;
1414
use clap::{App, Arg};
1515

1616
use rustpython_parser::{ast, parser};
17-
use std::path::Path;
17+
use std::path::{Path, PathBuf};
18+
use std::time::Instant;
1819

1920
fn main() {
2021
env_logger::init();
@@ -32,14 +33,21 @@ fn main() {
3233
let folder = Path::new(matches.value_of("folder").unwrap());
3334
if folder.exists() && folder.is_dir() {
3435
println!("Parsing folder of python code: {:?}", folder);
35-
let res = parse_folder(&folder).unwrap();
36-
println!("Processed {:?} files", res.len());
36+
let t1 = Instant::now();
37+
let parsed_files = parse_folder(&folder).unwrap();
38+
let t2 = Instant::now();
39+
let results = ScanResult {
40+
t1,
41+
t2,
42+
parsed_files,
43+
};
44+
statistics(results);
3745
} else {
3846
println!("{:?} is not a folder.", folder);
3947
}
4048
}
4149

42-
fn parse_folder(path: &Path) -> std::io::Result<Vec<ast::Program>> {
50+
fn parse_folder(path: &Path) -> std::io::Result<Vec<ParsedFile>> {
4351
let mut res = vec![];
4452
info!("Parsing folder of python code: {:?}", path);
4553
for entry in path.read_dir()? {
@@ -49,22 +57,66 @@ fn parse_folder(path: &Path) -> std::io::Result<Vec<ast::Program>> {
4957

5058
let path = entry.path();
5159
if metadata.is_dir() {
52-
let x = parse_folder(&path)?;
53-
res.extend(x);
60+
res.extend(parse_folder(&path)?);
5461
}
5562

5663
if metadata.is_file() && path.extension().and_then(|s| s.to_str()) == Some("py") {
57-
match parse_python_file(&path) {
58-
Ok(x) => res.push(x),
64+
let result = parse_python_file(&path);
65+
match &result {
66+
Ok(_) => {}
5967
Err(y) => error!("Erreur in file {:?} {:?}", path, y),
6068
}
69+
res.push(ParsedFile {
70+
filename: Box::new(path),
71+
result,
72+
});
6173
}
6274
}
6375
Ok(res)
6476
}
6577

66-
fn parse_python_file(filename: &Path) -> Result<ast::Program, String> {
78+
fn parse_python_file(filename: &Path) -> ParseResult {
6779
info!("Parsing file {:?}", filename);
6880
let source = std::fs::read_to_string(filename).map_err(|e| e.to_string())?;
6981
parser::parse_program(&source).map_err(|e| e.to_string())
7082
}
83+
84+
fn statistics(results: ScanResult) {
85+
// println!("Processed {:?} files", res.len());
86+
println!("Scanned a total of {} files", results.parsed_files.len());
87+
let total = results.parsed_files.len();
88+
let failed = results
89+
.parsed_files
90+
.iter()
91+
.filter(|p| p.result.is_err())
92+
.count();
93+
let passed = results
94+
.parsed_files
95+
.iter()
96+
.filter(|p| p.result.is_ok())
97+
.count();
98+
println!("Passed: {} Failed: {} Total: {}", passed, failed, total);
99+
println!(
100+
"That is {} % success rate.",
101+
(passed as f64 * 100.0) / total as f64
102+
);
103+
let duration = results.t2 - results.t1;
104+
println!("Total time spend: {:?}", duration);
105+
println!(
106+
"File processing rate: {} files/second",
107+
(total * 1_000_000) as f64 / duration.as_micros() as f64
108+
);
109+
}
110+
111+
struct ScanResult {
112+
t1: Instant,
113+
t2: Instant,
114+
parsed_files: Vec<ParsedFile>,
115+
}
116+
117+
struct ParsedFile {
118+
filename: Box<PathBuf>,
119+
result: ParseResult,
120+
}
121+
122+
type ParseResult = Result<ast::Program, String>;

parser/src/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ pub struct Comprehension {
318318
pub target: Expression,
319319
pub iter: Expression,
320320
pub ifs: Vec<Expression>,
321+
pub is_async: bool,
321322
}
322323

323324
#[derive(Debug, PartialEq)]

parser/src/parser.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ mod tests {
386386
target: mk_ident("y", 1, 8),
387387
iter: mk_ident("z", 1, 13),
388388
ifs: vec![],
389+
is_async: false,
389390
}],
390391
}
391392
}
@@ -415,6 +416,7 @@ mod tests {
415416
},
416417
iter: mk_ident("z", 1, 17),
417418
ifs: vec![],
419+
is_async: false,
418420
},
419421
ast::Comprehension {
420422
location: ast::Location::new(1, 19),
@@ -436,6 +438,7 @@ mod tests {
436438
},
437439
},
438440
],
441+
is_async: false,
439442
}
440443
],
441444
}

parser/src/python.lalrpop

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,7 @@ Atom: ast::Expression = {
908908
node: ast::ExpressionType::Tuple { elements: Vec::new() }
909909
})
910910
},
911+
"(" <e:YieldExpr> ")" => e,
911912
<location:@L> "(" <element:Test> <generators:CompFor> ")" => {
912913
ast::Expression {
913914
location,
@@ -1021,8 +1022,9 @@ StarExpr: ast::Expression = {
10211022
CompFor: Vec<ast::Comprehension> = <c:SingleForComprehension+> => c;
10221023

10231024
SingleForComprehension: ast::Comprehension = {
1024-
<location:@L> "for" <target:ExpressionList> "in" <iter:OrTest> <ifs:ComprehensionIf*> => {
1025-
ast::Comprehension { location, target, iter, ifs }
1025+
<location:@L> <is_async:"async"?> "for" <target:ExpressionList> "in" <iter:OrTest> <ifs:ComprehensionIf*> => {
1026+
let is_async = is_async.is_some();
1027+
ast::Comprehension { location, target, iter, ifs, is_async }
10261028
}
10271029
};
10281030

tests/snippets/generators.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,26 @@ def catch_exception():
6464

6565
with assertRaises(KeyError):
6666
assert g.throw(KeyError, KeyError(), None) == 2
67+
68+
69+
r = []
70+
def p(a, b, c):
71+
# print(a, b, c)
72+
r.append(a)
73+
r.append(b)
74+
r.append(c)
75+
76+
77+
def g5():
78+
p('a', (yield 2), (yield 5))
79+
yield 99
80+
81+
g = g5()
82+
g.send(None)
83+
g.send(66)
84+
# g.send(88)
85+
l = list(g)
86+
# print(r)
87+
# print(l)
88+
assert l == [99]
89+
assert r == ['a', 66, None]

vm/src/stdlib/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,7 @@ fn comprehension_to_ast(
579579
target => expression_to_ast(vm, &comprehension.target)?,
580580
iter => expression_to_ast(vm, &comprehension.iter)?,
581581
ifs => expressions_to_ast(vm, &comprehension.ifs)?,
582+
is_async => vm.new_bool(comprehension.is_async),
582583
}))
583584
}
584585

0 commit comments

Comments
 (0)