Skip to content

Commit fc8aba1

Browse files
Merge pull request RustPython#1215 from RustPython/syntax-fixes
Add variable annotation syntax.
2 parents 543b0ef + 2fdfcb9 commit fc8aba1

File tree

11 files changed

+263
-94
lines changed

11 files changed

+263
-94
lines changed

compiler/src/compile.rs

Lines changed: 79 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -368,33 +368,7 @@ impl<O: OutputStream> Compiler<O> {
368368
}
369369
self.set_label(end_label);
370370
}
371-
While { test, body, orelse } => {
372-
let start_label = self.new_label();
373-
let else_label = self.new_label();
374-
let end_label = self.new_label();
375-
self.emit(Instruction::SetupLoop {
376-
start: start_label,
377-
end: end_label,
378-
});
379-
380-
self.set_label(start_label);
381-
382-
self.compile_test(test, None, Some(else_label), EvalContext::Statement)?;
383-
384-
let was_in_loop = self.in_loop;
385-
self.in_loop = true;
386-
self.compile_statements(body)?;
387-
self.in_loop = was_in_loop;
388-
self.emit(Instruction::Jump {
389-
target: start_label,
390-
});
391-
self.set_label(else_label);
392-
self.emit(Instruction::PopBlock);
393-
if let Some(orelse) = orelse {
394-
self.compile_statements(orelse)?;
395-
}
396-
self.set_label(end_label);
397-
}
371+
While { test, body, orelse } => self.compile_while(test, body, orelse)?,
398372
With {
399373
is_async,
400374
items,
@@ -563,6 +537,11 @@ impl<O: OutputStream> Compiler<O> {
563537
self.compile_op(op, true);
564538
self.compile_store(target)?;
565539
}
540+
AnnAssign {
541+
target,
542+
annotation,
543+
value,
544+
} => self.compile_annotated_assign(target, annotation, value)?,
566545
Delete { targets } => {
567546
for target in targets {
568547
self.compile_delete(target)?;
@@ -1011,6 +990,40 @@ impl<O: OutputStream> Compiler<O> {
1011990
}
1012991
}
1013992

993+
fn compile_while(
994+
&mut self,
995+
test: &ast::Expression,
996+
body: &[ast::Statement],
997+
orelse: &Option<Vec<ast::Statement>>,
998+
) -> Result<(), CompileError> {
999+
let start_label = self.new_label();
1000+
let else_label = self.new_label();
1001+
let end_label = self.new_label();
1002+
self.emit(Instruction::SetupLoop {
1003+
start: start_label,
1004+
end: end_label,
1005+
});
1006+
1007+
self.set_label(start_label);
1008+
1009+
self.compile_test(test, None, Some(else_label), EvalContext::Statement)?;
1010+
1011+
let was_in_loop = self.in_loop;
1012+
self.in_loop = true;
1013+
self.compile_statements(body)?;
1014+
self.in_loop = was_in_loop;
1015+
self.emit(Instruction::Jump {
1016+
target: start_label,
1017+
});
1018+
self.set_label(else_label);
1019+
self.emit(Instruction::PopBlock);
1020+
if let Some(orelse) = orelse {
1021+
self.compile_statements(orelse)?;
1022+
}
1023+
self.set_label(end_label);
1024+
Ok(())
1025+
}
1026+
10141027
fn compile_for(
10151028
&mut self,
10161029
target: &ast::Expression,
@@ -1129,6 +1142,39 @@ impl<O: OutputStream> Compiler<O> {
11291142
Ok(())
11301143
}
11311144

1145+
fn compile_annotated_assign(
1146+
&mut self,
1147+
target: &ast::Expression,
1148+
annotation: &ast::Expression,
1149+
value: &Option<ast::Expression>,
1150+
) -> Result<(), CompileError> {
1151+
if let Some(value) = value {
1152+
self.compile_expression(value)?;
1153+
self.compile_store(target)?;
1154+
}
1155+
1156+
// Compile annotation:
1157+
self.compile_expression(annotation)?;
1158+
1159+
if let ast::ExpressionType::Identifier { name } = &target.node {
1160+
// Store as dict entry in __annotations__ dict:
1161+
self.emit(Instruction::LoadName {
1162+
name: String::from("__annotations__"),
1163+
scope: bytecode::NameScope::Local,
1164+
});
1165+
self.emit(Instruction::LoadConst {
1166+
value: bytecode::Constant::String {
1167+
value: name.to_string(),
1168+
},
1169+
});
1170+
self.emit(Instruction::StoreSubscript);
1171+
} else {
1172+
// Drop annotation if not assigned to simple identifier.
1173+
self.emit(Instruction::Pop);
1174+
}
1175+
Ok(())
1176+
}
1177+
11321178
fn compile_store(&mut self, target: &ast::Expression) -> Result<(), CompileError> {
11331179
match &target.node {
11341180
ast::ExpressionType::Identifier { name } => {
@@ -1272,6 +1318,8 @@ impl<O: OutputStream> Compiler<O> {
12721318

12731319
fn compile_expression(&mut self, expression: &ast::Expression) -> Result<(), CompileError> {
12741320
trace!("Compiling {:?}", expression);
1321+
self.set_source_location(&expression.location);
1322+
12751323
use ast::ExpressionType::*;
12761324
match &expression.node {
12771325
Call {
@@ -1664,6 +1712,10 @@ impl<O: OutputStream> Compiler<O> {
16641712

16651713
let mut loop_labels = vec![];
16661714
for generator in generators {
1715+
if generator.is_async {
1716+
unimplemented!("async for comprehensions");
1717+
}
1718+
16671719
if loop_labels.is_empty() {
16681720
// Load iterator onto stack (passed as first argument):
16691721
self.emit(Instruction::LoadName {

compiler/src/symboltable.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,17 @@ impl SymbolTableBuilder {
335335
self.scan_expression(target)?;
336336
self.scan_expression(value)?;
337337
}
338+
AnnAssign {
339+
target,
340+
annotation,
341+
value,
342+
} => {
343+
self.scan_expression(target)?;
344+
self.scan_expression(annotation)?;
345+
if let Some(value) = value {
346+
self.scan_expression(value)?;
347+
}
348+
}
338349
With { items, body, .. } => {
339350
for item in items {
340351
self.scan_expression(&item.context_expr)?;

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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ pub enum StatementType {
7474
op: Operator,
7575
value: Box<Expression>,
7676
},
77+
AnnAssign {
78+
target: Box<Expression>,
79+
annotation: Box<Expression>,
80+
value: Option<Expression>,
81+
},
7782
Expression {
7883
expression: Expression,
7984
},
@@ -313,6 +318,7 @@ pub struct Comprehension {
313318
pub target: Expression,
314319
pub iter: Expression,
315320
pub ifs: Vec<Expression>,
321+
pub is_async: bool,
316322
}
317323

318324
#[derive(Debug, PartialEq)]

parser/src/lexer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ where
396396

397397
let value = f64::from_str(&value_text).unwrap();
398398
// Parse trailing 'j':
399-
if self.chr0 == Some('j') {
399+
if self.chr0 == Some('j') || self.chr0 == Some('J') {
400400
self.next_char();
401401
let end_pos = self.get_pos();
402402
Ok((
@@ -413,7 +413,7 @@ where
413413
}
414414
} else {
415415
// Parse trailing 'j':
416-
if self.chr0 == Some('j') {
416+
if self.chr0 == Some('j') || self.chr0 == Some('J') {
417417
self.next_char();
418418
let end_pos = self.get_pos();
419419
let imag = f64::from_str(&value_text).unwrap();

parser/src/parser.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ mod tests {
376376
assert_eq!(
377377
parse_ast,
378378
ast::Expression {
379-
location: ast::Location::new(1, 2),
379+
location: ast::Location::new(1, 1),
380380
node: ast::ExpressionType::Comprehension {
381381
kind: Box::new(ast::ComprehensionKind::List {
382382
element: mk_ident("x", 1, 2),
@@ -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
}
@@ -399,7 +400,7 @@ mod tests {
399400
assert_eq!(
400401
parse_ast,
401402
ast::Expression {
402-
location: ast::Location::new(1, 2),
403+
location: ast::Location::new(1, 1),
403404
node: ast::ExpressionType::Comprehension {
404405
kind: Box::new(ast::ComprehensionKind::List {
405406
element: mk_ident("x", 1, 2)
@@ -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
}

0 commit comments

Comments
 (0)