// 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
use crate::ast;
use crate::fstring::parse_located_fstring;
use crate::function::{parse_args, parse_params};
use crate::error::LexicalError;
use crate::lexer;
use alloc::{boxed::Box, string::{String, ToString}, vec, vec::Vec};
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 ("\n")* => ast::Top::Expression(e),
};
Program: ast::Program = {
=> ast::Program {
statements: lines.into_iter().flatten().collect(),
},
};
// A file line either has a declaration, or an empty newline:
FileLine: ast::Suite = {
Statement,
"\n" => vec![],
};
Suite: ast::Suite = {
SimpleStatement,
"\n" Indent Dedent => s.into_iter().flatten().collect(),
};
Statement: ast::Suite = {
SimpleStatement,
=> vec![s],
};
SimpleStatement: ast::Suite = {
";"? "\n" => {
let mut statements = vec![s1];
statements.extend(s2.into_iter().map(|e| e.1));
statements
}
};
SmallStatement: ast::Statement = {
ExpressionStatement,
PassStatement,
DelStatement,
FlowStatement,
ImportStatement,
GlobalStatement,
NonlocalStatement,
AssertStatement,
};
PassStatement: ast::Statement = {
"pass" => {
ast::Statement {
location,
custom: (),
node: ast::StatementType::Pass,
}
},
};
DelStatement: ast::Statement = {
"del" => {
ast::Statement {
location,
custom: (),
node: ast::StatementType::Delete { targets },
}
},
};
ExpressionStatement: ast::Statement = {
=> {
// Just an expression, no assignment:
if suffix.is_empty() {
ast::Statement {
custom: (),
location,
node: ast::StatementType::Expression { expression }
}
} else {
let mut targets = vec![expression];
let mut values = suffix;
while values.len() > 1 {
targets.push(values.remove(0));
}
let value = values.into_iter().next().unwrap();
ast::Statement {
custom: (),
location,
node: ast::StatementType::Assign { targets, value },
}
}
},
=> {
ast::Statement {
custom: (),
location,
node: ast::StatementType::AugAssign {
target: Box::new(target),
op,
value: Box::new(rhs)
},
}
},
":" => {
ast::Statement {
custom: (),
location,
node: ast::StatementType::AnnAssign {
target: Box::new(target),
annotation: Box::new(annotation),
value: rhs
},
}
},
};
AssignSuffix: ast::Expression = {
"=" => e
};
TestListOrYieldExpr: ast::Expression = {
TestList,
YieldExpr
}
TestOrStarExprList: ast::Expression = {
> => {
if elements.len() == 1 && comma.is_none() {
elements.into_iter().next().unwrap()
} else {
ast::Expression {
location,
custom: (),
node: ast::ExpressionType::Tuple { elements }
}
}
}
};
TestOrStarNamedExprList: ast::Expression = {
> => {
if elements.len() == 1 && comma.is_none() {
elements.into_iter().next().unwrap()
} else {
ast::Expression {
location,
custom: (),
node: ast::ExpressionType::Tuple { elements }
}
}
}
};
TestOrStarExpr: ast::Expression = {
Test,
StarExpr,
};
TestOrStarNamedExpr: ast::Expression = {
NamedExpressionTest,
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::Statement = {
"break" => {
ast::Statement {
custom: (),
location,
node: ast::StatementType::Break,
}
},
"continue" => {
ast::Statement {
custom: (),
location,
node: ast::StatementType::Continue,
}
},
"return" => {
ast::Statement {
custom: (),
location,
node: ast::StatementType::Return { value },
}
},
=> {
ast::Statement {
custom: (),
location,
node: ast::StatementType::Expression { expression },
}
},
RaiseStatement,
};
RaiseStatement: ast::Statement = {
"raise" => {
ast::Statement {
custom: (),
location,
node: ast::StatementType::Raise { exception: None, cause: None },
}
},
"raise" => {
ast::Statement {
custom: (),
location,
node: ast::StatementType::Raise { exception: Some(t), cause: c.map(|x| x.1) },
}
},
};
ImportStatement: ast::Statement = {
"import" >> => {
ast::Statement {
custom: (),
location,
node: ast::StatementType::Import { names },
}
},
"from" "import" => {
let (level, module) = source;
ast::Statement {
custom: (),
location,
node: ast::StatementType::ImportFrom {
level,
module,
names
},
}
},
};
ImportFromLocation: (usize, Option) = {
=> {
(dots.iter().sum(), Some(name))
},
=> {
(dots.iter().sum(), None)
},
};
ImportDots: usize = {
"..." => 3,
"." => 1,
};
ImportAsNames: Vec = {
>> => i,
"(" >> ","? ")" => i,
"*" => {
// Star import all
vec![ast::ImportSymbol { symbol: "*".to_string(), alias: None }]
},
};
#[inline]
ImportAsAlias: ast::ImportSymbol = {
=> ast::ImportSymbol { symbol, alias: 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::Statement = {
"global" > => {
ast::Statement {
custom: (),
location,
node: ast::StatementType::Global { names }
}
},
};
NonlocalStatement: ast::Statement = {
"nonlocal" > => {
ast::Statement {
custom: (),
location,
node: ast::StatementType::Nonlocal { names }
}
},
};
AssertStatement: ast::Statement = {
"assert" => {
ast::Statement {
custom: (),
location,
node: ast::StatementType::Assert {
test, msg: msg.map(|e| e.1)
}
}
},
};
CompoundStatement: ast::Statement = {
IfStatement,
WhileStatement,
ForStatement,
TryStatement,
WithStatement,
FuncDef,
ClassDef,
};
IfStatement: ast::Statement = {
"if" ":" => {
// Determine last else:
let mut last = s3.map(|s| s.2);
// handle elif:
for i in s2.into_iter().rev() {
let x = ast::Statement {
custom: (),
location: i.0,
node: ast::StatementType::If { test: i.2, body: i.4, orelse: last },
};
last = Some(vec![x]);
}
ast::Statement {
custom: (),
location,
node: ast::StatementType::If { test, body, orelse: last }
}
},
};
WhileStatement: ast::Statement = {
"while" ":" => {
let orelse = s2.map(|s| s.2);
ast::Statement {
custom: (),
location,
node: ast::StatementType::While {
test,
body,
orelse
},
}
},
};
ForStatement: ast::Statement = {
"for" "in" ":" => {
let is_async = is_async.is_some();
let orelse = s2.map(|s| s.2);
ast::Statement {
custom: (),
location,
node: ast::StatementType::For {
is_async,
target: Box::new(target),
iter: Box::new(iter),
body,
orelse
},
}
},
};
TryStatement: ast::Statement = {
"try" ":" => {
let orelse = else_suite.map(|s| s.2);
let finalbody = finally.map(|s| s.2);
ast::Statement {
custom: (),
location,
node: ast::StatementType::Try {
body,
handlers,
orelse,
finalbody,
},
}
},
"try" ":" => {
let handlers = vec![];
let orelse = None;
let finalbody = Some(finally.2);
ast::Statement {
custom: (),
location,
node: ast::StatementType::Try {
body,
handlers,
orelse,
finalbody,
},
}
},
};
ExceptClause: ast::ExceptHandler = {
"except" ":" => {
ast::ExceptHandler {
location,
typ,
name: None,
body,
}
},
"except" ":" => {
ast::ExceptHandler {
location,
typ: Some(x.0),
name: Some(x.2),
body,
}
},
};
WithStatement: ast::Statement = {
"with" > ":" => {
let is_async = is_async.is_some();
ast::Statement {
custom: (),
location,
node: ast::StatementType::With { is_async, items, body },
}
},
};
WithItem: ast::WithItem = {
=> {
let optional_vars = n.map(|val| val.1);
ast::WithItem { context_expr, optional_vars }
},
};
FuncDef: ast::Statement = {
"def" " Test)?> ":" => {
let is_async = is_async.is_some();
ast::Statement {
custom: (),
location,
node: ast::StatementType::FunctionDef {
is_async,
name,
args: Box::new(args),
body,
decorator_list,
returns: r.map(|x| x.1),
}
}
},
};
Parameters: ast::Parameters = {
"(" )?> ")" => a.unwrap_or_default(),
};
// Note that this is a macro which is used once for function defs, and
// once for lambda defs.
ParameterList: ast::Parameters = {
> )?> ","? =>? {
let posonlyargs_count = param1.0.len();
let (names, defaults) = parse_params(param1)?;
// Now gather rest of parameters:
let (vararg, kwonlyargs, kw_defaults, kwarg) = args2.map_or((None, vec![], vec![], None), |x| x.1);
Ok(ast::Parameters {
posonlyargs_count,
args: names,
kwonlyargs,
vararg: vararg.into(),
kwarg: kwarg.into(),
defaults,
kw_defaults,
})
},
> )> ","? =>? {
let posonlyargs_count = param1.0.len();
let (names, defaults) = parse_params(param1)?;
// Now gather rest of parameters:
let vararg = None;
let kwonlyargs = vec![];
let kw_defaults = vec![];
let kwarg = Some(kw.1);
Ok(ast::Parameters {
posonlyargs_count,
args: names,
kwonlyargs,
vararg: vararg.into(),
kwarg: kwarg.into(),
defaults,
kw_defaults,
})
},
> ","? => {
let (vararg, kwonlyargs, kw_defaults, kwarg) = params;
ast::Parameters {
posonlyargs_count: 0,
args: vec![],
kwonlyargs,
vararg: vararg.into(),
kwarg: kwarg.into(),
defaults: vec![],
kw_defaults,
}
},
> ","? => {
ast::Parameters {
posonlyargs_count: 0,
args: vec![],
kwonlyargs: vec![],
vararg: ast::Varargs::None,
kwarg: Some(kw).into(),
defaults: vec![],
kw_defaults: vec![],
}
},
};
// Use inline here to make sure the "," is not creating an ambiguity.
#[inline]
ParameterDefs: (Vec<(ast::Parameter, Option)>, Vec<(ast::Parameter, Option)>) = {
>> => {
(vec![], args)
},
>> "," "/" )*> => {
(pos_args, args.into_iter().map(|e| e.1).collect())
},
};
ParameterDef: (ast::Parameter, Option) = {
=> (i, None),
"=" => (i, Some(e)),
};
UntypedParameter: ast::Parameter = {
=> ast::Parameter { location, arg, annotation: None },
};
TypedParameter: ast::Parameter = {
=> {
let annotation = a.map(|x| Box::new(x.1));
ast::Parameter { location, arg, annotation }
},
};
// Use inline here to make sure the "," is not creating an ambiguity.
// TODO: figure out another grammar that makes this inline no longer required.
#[inline]
ParameterListStarArgs: (Option