// See also: file:///usr/share/doc/python/html/reference/grammar.html?highlight=grammar
// See also: https://github.com/antlr/grammars-v4/blob/master/python3/Python3.g4
// See also: file:///usr/share/doc/python/html/reference/compound_stmts.html#function-definitions
// See also: https://greentreesnakes.readthedocs.io/en/latest/nodes.html#keyword
#![allow(unknown_lints,clippy)]
use super::ast;
use super::lexer;
use std::iter::FromIterator;
use num_bigint::BigInt;
grammar;
// This is a hack to reduce the amount of lalrpop tables generated:
// For each public entry point, a full parse table is generated.
// By having only a single pub function, we reduce this to one.
pub Top: ast::Top = {
StartProgram => ast::Top::Program(p),
StartStatement => ast::Top::Statement(s),
StartExpression => ast::Top::Expression(e),
};
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,
};
Statement: ast::LocatedStatement = {
SimpleStatement,
CompoundStatement,
};
SimpleStatement: ast::LocatedStatement = {
"\n" => s,
";" => s,
};
SmallStatement: ast::LocatedStatement = {
ExpressionStatement,
PassStatement,
DelStatement,
FlowStatement,
ImportStatement,
GlobalStatement,
NonlocalStatement,
AssertStatement,
};
PassStatement: ast::LocatedStatement = {
"pass" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Pass,
}
},
};
DelStatement: ast::LocatedStatement = {
"del" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Delete { targets: e },
}
},
};
ExpressionStatement: ast::LocatedStatement = {
=> {
// Just an expression, no assignment:
if suffix.is_empty() {
ast::LocatedStatement {
location: loc.clone(),
node: ast::Statement::Expression { expression: expr }
}
} else {
let mut targets = vec![expr];
let mut values = suffix;
while values.len() > 1 {
targets.push(values.remove(0));
}
let value = values.into_iter().next().unwrap();
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: expr, op: op, value: rhs },
}
},
};
AssignSuffix: ast::Expression = {
"=" => {
if e.len() > 1 {
ast::Expression::Tuple {
elements: e
}
} else {
e.into_iter().next().unwrap()
}
},
"=" => e,
};
TestOrStarExprList: ast::Expression = {
=> {
let mut res = vec![e];
res.extend(e2.into_iter().map(|x| x.1));
// First build tuple from first item:
let expr = if (res.len() > 1) || comma.is_some() {
ast::Expression::Tuple { elements: res }
} else {
res.into_iter().next().unwrap()
};
expr
}
};
TestOrStarExpr: ast::Expression = {
Test,
StarExpr,
};
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 },
}
},
=> {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Expression { expression: y },
}
},
RaiseStatement,
};
RaiseStatement: ast::LocatedStatement = {
"raise" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Raise { exception: None, cause: None },
}
},
"raise" => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Raise { exception: Some(t), cause: c.map(|x| x.1) },
}
},
};
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
},
};
GlobalStatement: ast::LocatedStatement = {
"global" > => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Global { names }
}
},
};
NonlocalStatement: ast::LocatedStatement = {
"nonlocal" > => {
ast::LocatedStatement {
location: loc,
node: ast::Statement::Nonlocal { names }
}
},
};
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" > ":" => {
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: ast::Parameters = {
"(" ")" => {
match a {
Some(a) => a,
None => Default::default(),
}
},
};
// parameters are (String, None), kwargs are (String, Some(Test)) where Test is
// the default
TypedArgsList: ast::Parameters = {
=> {
let (names, default_elements) = param1;
// Now gather rest of parameters:
let (vararg, kwonlyargs, kw_defaults, kwarg) = match args2 {
Some((_, x)) => x,
None => (None, vec![], vec![], None),
};
ast::Parameters {
args: names,
kwonlyargs: kwonlyargs,
vararg: vararg,
kwarg: kwarg,
defaults: default_elements,
kw_defaults: kw_defaults,
}
},
=> {
let (names, default_elements) = param1;
// Now gather rest of parameters:
let vararg = None;
let kwonlyargs = vec![];
let kw_defaults = vec![];
let kwarg = Some(kw.1);
ast::Parameters {
args: names,
kwonlyargs: kwonlyargs,
vararg: vararg,
kwarg: kwarg,
defaults: default_elements,
kw_defaults: kw_defaults,
}
},
=> {
let (vararg, kwonlyargs, kw_defaults, kwarg) = params;
ast::Parameters {
args: vec![],
kwonlyargs: kwonlyargs,
vararg: vararg,
kwarg: kwarg,
defaults: vec![],
kw_defaults: kw_defaults,
}
},
=> {
ast::Parameters {
args: vec![],
kwonlyargs: vec![],
vararg: None,
kwarg: Some(kw),
defaults: vec![],
kw_defaults: vec![],
}
},
};
// Use inline here to make sure the "," is not creating an ambiguity.
#[inline]
TypedParameters: (Vec, Vec) = {
=> {
// Combine first parameters:
let mut args = vec![param1];
args.extend(param2.into_iter().map(|x| x.1));
let mut names = vec![];
let mut default_elements = vec![];
for (name, default) in args.into_iter() {
names.push(name.clone());
if let Some(default) = default {
default_elements.push(default);
} else {
if default_elements.len() > 0 {
// Once we have started with defaults, all remaining arguments must
// have defaults
panic!(
"non-default argument follows default argument: {}",
name
);
}
}
}
(names, default_elements)
}
};
TypedParameterDef: (String, Option) = {
=> (i, None),
"=" => (i, Some(e)),
};
// TODO: add type annotations here:
TypedParameter: String = {
Identifier,
};
ParameterListStarArgs: (Option