// See also: file:///usr/share/doc/python/html/reference/grammar.html?highlight=grammar
#![allow(unknown_lints,clippy)]
use super::ast;
use super::lexer;
use std::iter::FromIterator;
use std::str::FromStr;
grammar;
pub Program: ast::Program = {
=> ast::Program { statements: Vec::from_iter(lines.into_iter().filter_map(|e| e)) },
};
// A file line either has a declaration, or an empty newline:
FileLine: Option = {
=> Some(s),
"\n" => None,
};
Suite: Vec = {
=> vec![s],
"\n" indent dedent => s,
};
pub Statement: ast::LocatedStatement = {
SimpleStatement,
CompoundStatement,
};
SimpleStatement: ast::LocatedStatement = {
"\n" => s,
";" => s,
};
SmallStatement: ast::LocatedStatement = {
// => ast::Statement::Expression { expression: e },
ExpressionStatement,
"pass" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Pass,
}
},
FlowStatement,
ImportStatement,
AssertStatement,
};
ExpressionStatement: ast::LocatedStatement = {
=> {
// Just an expression, no assignment:
if suffix.is_empty() {
if expr.len() > 1 {
ast::LocatedStatement {
location: loc.clone(),
node: ast::Statement::Expression { expression: ast::Expression::Tuple { elements: expr } }
}
} else {
ast::LocatedStatement {
location: loc.clone(),
node: ast::Statement::Expression { expression: expr[0].clone() },
}
}
} else {
let mut targets = vec![if expr.len() > 1 {
ast::Expression::Tuple { elements: expr }
} else {
expr[0].clone()
}];
let mut values : Vec = suffix.into_iter().map(|test_list| if test_list.len() > 1 { ast::Expression::Tuple { elements: test_list }} else { test_list[0].clone() }).collect();
while values.len() > 1 {
targets.push(values.remove(0));
}
let value = values[0].clone();
ast::LocatedStatement {
location: loc.clone(),
node: ast::Statement::Assign { targets, value },
}
}
},
=> {
// TODO: this works in most cases:
let rhs = e2.into_iter().next().unwrap();
ast::LocatedStatement {
location: loc,
node: ast::Statement::AugAssign { target: e1, op: op, value: rhs },
}
},
};
AssignSuffix: Vec = {
"=" => e,
};
AugAssign: ast::Operator = {
"+=" => ast::Operator::Add,
"-=" => ast::Operator::Sub,
"*=" => ast::Operator::Mult,
"@=" => ast::Operator::MatMult,
"/=" => ast::Operator::Div,
"%=" => ast::Operator::Mod,
"&=" => ast::Operator::BitAnd,
"|=" => ast::Operator::BitOr,
"^=" => ast::Operator::BitXor,
"<<=" => ast::Operator::LShift,
">>=" => ast::Operator::RShift,
"**=" => ast::Operator::Pow,
"//=" => ast::Operator::FloorDiv,
};
FlowStatement: ast::LocatedStatement = {
"break" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Break,
}
},
"continue" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Continue,
}
},
"return" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Return { value: t },
}
},
"raise" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Raise { expression: t },
}
},
=> {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Expression { expression: y },
}
},
};
ImportStatement: ast::LocatedStatement = {
"import" >>> => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Import {
import_parts: i
.iter()
.map(|(n, a)|
ast::SingleImport {
module: n.to_string(),
symbol: None,
alias: a.clone()
})
.collect()
},
}
},
"from" "import" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Import {
import_parts: i
.iter()
.map(|(i, a)|
ast::SingleImport {
module: n.to_string(),
symbol: Some(i.to_string()),
alias: a.clone()
})
.collect()
},
}
},
};
ImportFromLocation: String = {
=> {
let mut r = "".to_string();
for _dot in dots {
r.push_str(".");
}
r.push_str(&name);
r
},
=> {
let mut r = "".to_string();
for _dot in dots {
r.push_str(".");
}
r
},
};
ImportAsNames: Vec<(String, Option)> = {
>> => i,
"(" >> ")" => i,
"*" => {
// Star import all
vec![("*".to_string(), None)]
},
};
#[inline]
ImportPart: (String, Option) = {
=> (i, a.map(|a| a.1)),
};
// A name like abc or abc.def.ghi
DottedName: String = {
=> n,
=> {
let mut r = n.to_string();
for x in n2 {
r.push_str(".");
r.push_str(&x.1);
}
r
},
};
AssertStatement: ast::LocatedStatement = {
"assert" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Assert {
test: t,
msg: match m {
Some(e) => Some(e.1),
None => None,
}
}
}
},
};
CompoundStatement: ast::LocatedStatement = {
IfStatement,
WhileStatement,
ForStatement,
TryStatement,
WithStatement,
FuncDef,
ClassDef,
};
IfStatement: ast::LocatedStatement = {
"if" ":" => {
// Determine last else:
let mut last = match s3 {
Some(s) => Some(s.2),
None => None,
};
// handle elif:
for i in s2.into_iter().rev() {
let x = ast::LocatedStatement {
location: i.0,
node: ast::Statement::If { test: i.2, body: i.4, orelse: last },
};
last = Some(vec![x]);
}
ast::LocatedStatement {
location: loc,
node: ast::Statement::If { test: t, body: s1, orelse: last }
}
},
};
WhileStatement: ast::LocatedStatement = {
"while" ":" => {
let or_else = match s2 {
Some(s) => Some(s.2),
None => None,
};
ast::LocatedStatement {
location: loc,
node: ast::Statement::While { test: e, body: s, orelse: or_else },
}
},
};
ForStatement: ast::LocatedStatement = {
"for" "in" ":" => {
let or_else = match s2 {
Some(s) => Some(s.2),
None => None,
};
ast::LocatedStatement {
location: loc,
node: ast::Statement::For { target: e, iter: t, body: s, orelse: or_else },
}
},
};
TryStatement: ast::LocatedStatement = {
"try" ":" => {
let or_else = match else_suite {
Some(s) => Some(s.2),
None => None,
};
let finalbody = match finally {
Some(s) => Some(s.2),
None => None,
};
ast::LocatedStatement {
location: loc,
node: ast::Statement::Try {
body: body,
handlers: handlers,
orelse: or_else,
finalbody: finalbody,
},
}
},
};
ExceptClause: ast::ExceptHandler = {
"except" ":" => {
ast::ExceptHandler {
typ: typ,
name: None,
body: body,
}
},
"except" ":" => {
ast::ExceptHandler {
typ: Some(x.0),
name: Some(x.2),
body: body,
}
},
};
WithStatement: ast::LocatedStatement = {
"with" ":" => {
let mut items = vec![i1];
for item in i2 {
items.push(item.1);
}
ast::LocatedStatement {
location: loc,
node: ast::Statement::With { items: items, body: s },
}
},
};
WithItem: ast::WithItem = {
=> {
let optional_vars = match n {
Some(val) => Some(val.1),
None => None,
};
ast::WithItem { context_expr: t, optional_vars }
},
};
FuncDef: ast::LocatedStatement = {
"def" ":" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::FunctionDef {
name: i,
args: a,
body: s,
decorator_list: d,
}
}
},
};
Parameters: Vec<(String, Option)> = {
"(" ")" => a,
};
// parameters are (String, None), kwargs are (String, Some(Test)) where Test is
// the default
TypedArgsList: Vec<(String, Option)> = {
> => a
};
Parameter: (String, Option) = {
=> (i.clone(), None),
"=" => (i.clone(), Some(e.clone())),
};
ClassDef: ast::LocatedStatement = {
"class" ":" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::ClassDef {
name: n,
args: a.unwrap_or(vec![]),
body: s,
decorator_list: d,
},
}
},
};
// Decorators:
Decorator: ast::Expression = {
"@" "\n" => {
let name = ast::Expression::Identifier { name: n };
match a {
Some((_, args, _)) => ast::Expression::Call {
function: Box::new(name),
args: args,
},
None => name,
}
},
};
YieldExpr: ast::Expression = {
"yield" => {
ast::Expression::Yield {
expression: None,
}
}
};
Test: ast::Expression = {
=> e,
=> e,
};
LambdaDef: ast::Expression = {
"lambda" ":" =>
ast::Expression::Lambda {
args:p,
body:Box::new(b)
}
}
OrTest: ast::Expression = {
=> e,
"or" => ast::Expression::BoolOp { a: Box::new(e1), op: ast::BooleanOperator::Or, b: Box::new(e2) },
};
AndTest: ast::Expression = {
=> e,
"and" => ast::Expression::BoolOp { a: Box::new(e1), op: ast::BooleanOperator::And, b: Box::new(e2) },
};
NotTest: ast::Expression = {
"not" => ast::Expression::Unop { a: Box::new(e), op: ast::UnaryOperator::Not },
=> e,
};
Comparison: ast::Expression = {
=> ast::Expression::Compare { a: Box::new(e1), op: op, b: Box::new(e2) },
=> e,
};
CompOp: ast::Comparison = {
"==" => ast::Comparison::Equal,
"!=" => ast::Comparison::NotEqual,
"<" => ast::Comparison::Less,
"<=" => ast::Comparison::LessOrEqual,
">" => ast::Comparison::Greater,
">=" => ast::Comparison::GreaterOrEqual,
"in" => ast::Comparison::In,
"not" "in" => ast::Comparison::NotIn,
"is" => ast::Comparison::Is,
"is" "not" => ast::Comparison::IsNot,
};
pub Expression: ast::Expression = {
"|" => ast::Expression::Binop { a: Box::new(e1), op: ast::Operator::BitOr, b: Box::new(e2) },
=> e,
};
XorExpression: ast::Expression = {
"^" => ast::Expression::Binop { a: Box::new(e1), op: ast::Operator::BitXor, b: Box::new(e2) },
=> e,
};
AndExpression: ast::Expression = {
"&" => ast::Expression::Binop { a: Box::new(e1), op: ast::Operator::BitAnd, b: Box::new(e2) },
=> e,
};
ArithmaticExpression: ast::Expression = {
=> ast::Expression::Binop { a: Box::new(a), op: op, b: Box::new(b) },
Term,
};
AddOp: ast::Operator = {
"+" => ast::Operator::Add,
"-" => ast::Operator::Sub,
};
Term: ast::Expression = {
=> ast::Expression::Binop { a: Box::new(a), op: op, b: Box::new(b) },
Factor,
};
MulOp: ast::Operator = {
"*" => ast::Operator::Mult,
"/" => ast::Operator::Div,
"//" => ast::Operator::FloorDiv,
"%" => ast::Operator::Mod,
"@" => ast::Operator::MatMult,
};
Factor: ast::Expression = {
"+" => e,
"-" => ast::Expression::Unop { a: Box::new(e), op: ast::UnaryOperator::Neg },
=> e,
};
Power: ast::Expression = {
=> {
match e2 {
None => e,
Some(x) => ast::Expression::Binop { a: Box::new(e), op: ast::Operator::Pow, b: Box::new(x.1) },
}
}
};
AtomExpr: ast::Expression = {
=> e,
"(" ")" => ast::Expression::Call { function: Box::new(f), args: a },
"[" "]" => ast::Expression::Subscript { a: Box::new(e), b: Box::new(s) },
"." => ast::Expression::Attribute { value: Box::new(e), name: n },
};
Subscript: ast::Expression = {
=> e,
":" => {
let s1 = e1.unwrap_or(ast::Expression::None);
let s2 = e2.unwrap_or(ast::Expression::None);
let s3 = e3.unwrap_or(ast::Expression::None);
ast::Expression::Slice { elements: vec![s1, s2, s3] }
}
};
SliceOp: ast::Expression = {
":" => e.unwrap_or(ast::Expression::None)
}
Atom: ast::Expression = {
=> ast::Expression::String { value: s },
=> ast::Expression::Number { value: n },
=> ast::Expression::Identifier { name: i },
"[" <_trailing_comma:","?> "]" => {
match e {
None => ast::Expression::List { elements: Vec::new() },
Some(elements) => ast::Expression::List { elements },
}
},
"(" ")" => {
match e {
None => ast::Expression::Tuple { elements: Vec::new() },
Some(elements) => {
if elements.len() == 1 && trailing_comma.is_none() {
// This is "(e)", which is equivalent to "e"
elements.into_iter().next().unwrap()
} else {
ast::Expression::Tuple { elements }
}
}
}
},
"{" "}" => ast::Expression::Dict { elements: e.unwrap_or(Vec::new()) },
"True" => ast::Expression::True,
"False" => ast::Expression::False,
"None" => ast::Expression::None,
};
TestDict: Vec<(ast::Expression, ast::Expression)> = {
<_trailing_comma:","?> => {
let mut d = vec![e1];
d.extend(e2.into_iter().map(|x| x.1));
d
}
};
DictEntry: (ast::Expression, ast::Expression) = {
":" => (e1, e2),
};
ExpressionList: Vec = {
> => e,
};
#[inline]
TestList: Vec = {
=> {
let mut l = vec![e1];
l.extend(e2.into_iter().map(|x| x.1));
l
}
};
FunctionArguments: Vec<(Option, ast::Expression)> = {
> => e,
};
FunctionArgument: (Option, ast::Expression) = {
=> (None, e.clone()),
"=" => (Some(i.clone()), e.clone()),
};
Comma: Vec = {
",")*> => {
let mut items = items;
items.extend(last);
items
}
};
Number: ast::Number = {
=> {
if s.contains(".") {
ast::Number::Float { value: f64::from_str(&s).unwrap() }
} else {
ast::Number::Integer { value: i32::from_str(&s).unwrap() }
}
}
};
String: String = {
=> {
s.join("")
},
};
Identifier: String = => s;
// Hook external lexer:
extern {
type Location = lexer::Location;
type Error = lexer::LexicalError;
enum lexer::Tok {
indent => lexer::Tok::Indent,
dedent => lexer::Tok::Dedent,
"+" => lexer::Tok::Plus,
"-" => lexer::Tok::Minus,
":" => lexer::Tok::Colon,
"." => lexer::Tok::Dot,
"," => lexer::Tok::Comma,
"*" => lexer::Tok::Star,
"**" => lexer::Tok::DoubleStar,
"&" => lexer::Tok::Amper,
"@" => lexer::Tok::At,
"%" => lexer::Tok::Percent,
"//" => lexer::Tok::DoubleSlash,
"^" => lexer::Tok::CircumFlex,
"|" => lexer::Tok::Vbar,
"/" => lexer::Tok::Slash,
"(" => lexer::Tok::Lpar,
")" => lexer::Tok::Rpar,
"[" => lexer::Tok::Lsqb,
"]" => lexer::Tok::Rsqb,
"{" => lexer::Tok::Lbrace,
"}" => lexer::Tok::Rbrace,
"=" => lexer::Tok::Equal,
"+=" => lexer::Tok::PlusEqual,
"-=" => lexer::Tok::MinusEqual,
"*=" => lexer::Tok::StarEqual,
"@=" => lexer::Tok::AtEqual,
"/=" => lexer::Tok::SlashEqual,
"%=" => lexer::Tok::PercentEqual,
"&=" => lexer::Tok::AmperEqual,
"|=" => lexer::Tok::VbarEqual,
"^=" => lexer::Tok::CircumflexEqual,
"<<=" => lexer::Tok::LeftShiftEqual,
">>=" => lexer::Tok::RightShiftEqual,
"**=" => lexer::Tok::DoubleStarEqual,
"//=" => lexer::Tok::DoubleSlashEqual,
"==" => lexer::Tok::EqEqual,
"!=" => lexer::Tok::NotEqual,
"<" => lexer::Tok::Less,
"<=" => lexer::Tok::LessEqual,
">" => lexer::Tok::Greater,
">=" => lexer::Tok::GreaterEqual,
"and" => lexer::Tok::And,
"as" => lexer::Tok::As,
"assert" => lexer::Tok::Assert,
"break" => lexer::Tok::Break,
"class" => lexer::Tok::Class,
"continue" => lexer::Tok::Break,
"def" => lexer::Tok::Def,
"elif" => lexer::Tok::Elif,
"else" => lexer::Tok::Else,
"except" => lexer::Tok::Except,
"finally" => lexer::Tok::Finally,
"for" => lexer::Tok::For,
"if" => lexer::Tok::If,
"in" => lexer::Tok::In,
"is" => lexer::Tok::Is,
"import" => lexer::Tok::Import,
"from" => lexer::Tok::From,
"lambda" => lexer::Tok::Lambda,
"not" => lexer::Tok::Not,
"or" => lexer::Tok::Or,
"pass" => lexer::Tok::Pass,
"raise" => lexer::Tok::Raise,
"return" => lexer::Tok::Return,
"try" => lexer::Tok::Try,
"while" => lexer::Tok::While,
"with" => lexer::Tok::With,
"yield" => lexer::Tok::Yield,
"True" => lexer::Tok::True,
"False" => lexer::Tok::False,
"None" => lexer::Tok::None,
number => lexer::Tok::Number { value: },
string => lexer::Tok::String { value: },
name => lexer::Tok::Name { name: },
"\n" => lexer::Tok::Newline,
";" => lexer::Tok::Semi,
}
}