// 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::{self as ast, Ranged},
lexer::{LexicalError, LexicalErrorType},
function::{ArgumentList, parse_args, parse_params, validate_arguments},
context::set_context,
string::parse_strings,
token::{self, StringKind},
text_size::TextSize, parser::optional_range
};
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::Mod = {
StartModule => ast::ModModule { body, type_ignores: vec![], range: optional_range(start, end) }.into(),
StartInteractive => ast::ModInteractive { body, range: optional_range(start, end) }.into(),
StartExpression ("\n")* => ast::ModExpression { body: Box::new(body), range: optional_range(start, end) }.into()
};
Program: ast::Suite = {
=> {
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" => {
statements.push(last);
statements
}
};
SmallStatement: ast::Stmt = {
ExpressionStatement,
PassStatement,
DelStatement,
FlowStatement,
ImportStatement,
GlobalStatement,
NonlocalStatement,
AssertStatement,
};
PassStatement: ast::Stmt = {
"pass" => {
ast::Stmt::Pass(ast::StmtPass { range: (location..end_location).into() })
},
};
DelStatement: ast::Stmt = {
"del" => {
ast::Stmt::Delete(
ast::StmtDelete { targets: targets.into_iter().map(|expr| set_context(expr, ast::ExprContext::Del)).collect(), range: (location..end_location).into() }
)
},
};
ExpressionStatement: ast::Stmt = {
=> {
// Just an expression, no assignment:
if suffix.is_empty() {
ast::Stmt::Expr(
ast::StmtExpr { value: Box::new(expression), range: (location..end_location).into() }
)
} else {
let mut targets = vec![set_context(expression, ast::ExprContext::Store)];
let mut values = suffix;
while values.len() > 1 {
targets.push(set_context(values.remove(0), ast::ExprContext::Store));
}
let value = Box::new(values.into_iter().next().unwrap());
ast::Stmt::Assign(
ast::StmtAssign { targets, value, type_comment: None, range: (location..end_location).into() }
)
}
},
=> {
ast::Stmt::AugAssign(
ast::StmtAugAssign {
target: Box::new(set_context(target, ast::ExprContext::Store)),
op,
value: Box::new(rhs),
range: (location..end_location).into()
},
)
},
> ":" > => {
let simple = target.is_name_expr();
ast::Stmt::AnnAssign(
ast::StmtAnnAssign {
target: Box::new(set_context(target, ast::ExprContext::Store)),
annotation: Box::new(annotation),
value: rhs.map(Box::new),
simple,
range: (location..end_location).into()
},
)
},
};
AssignSuffix: ast::Expr = {
"=" => e
};
TestListOrYieldExpr: ast::Expr = {
TestList,
YieldExpr
}
#[inline]
TestOrStarExprList: ast::Expr = {
// as far as I can tell, these were the same
TestList
};
TestOrStarExpr: ast::Expr = {
Test<"all">,
StarExpr,
};
NamedOrStarExpr: ast::Expr = {
NamedExpression,
StarExpr,
};
TestOrStarNamedExpr: ast::Expr = {
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::Stmt = {
"break" => {
ast::Stmt::Break(ast::StmtBreak { range: (location..end_location).into() })
},
"continue" => {
ast::Stmt::Continue(ast::StmtContinue { range: (location..end_location).into() })
},
"return" => {
ast::Stmt::Return(
ast::StmtReturn { value: value.map(Box::new), range: (location..end_location).into() }
)
},
=> {
ast::Stmt::Expr(
ast::StmtExpr { value: Box::new(expression), range: (location..end_location).into() }
)
},
RaiseStatement,
};
RaiseStatement: ast::Stmt = {
"raise" => {
ast::Stmt::Raise(
ast::StmtRaise { exc: None, cause: None, range: (location..end_location).into() }
)
},
"raise" > >)?> => {
ast::Stmt::Raise(
ast::StmtRaise { exc: Some(Box::new(t)), cause: c.map(|x| Box::new(x)), range: (location..end_location).into() }
)
},
};
ImportStatement: ast::Stmt = {
"import" >> => {
ast::Stmt::Import(
ast::StmtImport { names, range: (location..end_location).into() }
)
},
"from" "import" => {
let (level, module) = source;
ast::Stmt::ImportFrom(
ast::StmtImportFrom {
level,
module,
names,
range: (location..end_location).into()
},
)
},
};
ImportFromLocation: (Option, Option) = {
=> {
(Some(ast::Int::new(dots.iter().map(ast::Int::to_u32).sum())), Some(name))
},
=> {
(Some(ast::Int::new(dots.iter().map(ast::Int::to_u32).sum())), None)
},
};
ImportDots: ast::Int = {
"..." => ast::Int::new(3),
"." => ast::Int::new(1),
};
ImportAsNames: Vec = {
>> => i,
"(" >> ","? ")" => i,
"*" => {
// Star import all
vec![ast::Alias { name: ast::Identifier::new("*"), asname: None, range: (location..end_location).into() }]
},
};
#[inline]
ImportAsAlias: ast::Alias = {
)?> => ast::Alias { name, asname: a, range: (location..end_location).into() },
}
// A name like abc or abc.def.ghi
DottedName: ast::Identifier = {
=> ast::Identifier::new(n),
=> {
let mut r = n.to_string();
for x in n2 {
r.push('.');
r.push_str(x.1.as_str());
}
ast::Identifier::new(r)
},
};
GlobalStatement: ast::Stmt = {
"global" > => {
ast::Stmt::Global(
ast::StmtGlobal { names, range: (location..end_location).into() }
)
},
};
NonlocalStatement: ast::Stmt = {
"nonlocal" > => {
ast::Stmt::Nonlocal(
ast::StmtNonlocal { names, range: (location..end_location).into() }
)
},
};
AssertStatement: ast::Stmt = {
"assert" > >)?> => {
ast::Stmt::Assert(
ast::StmtAssert {
test: Box::new(test),
msg: msg.map(|e| Box::new(e)),
range: (location..end_location).into()
}
)
},
};
CompoundStatement: ast::Stmt = {
MatchStatement,
IfStatement,
WhileStatement,
ForStatement,
TryStatement,
WithStatement,
FuncDef,
ClassDef,
};
MatchStatement: ast::Stmt = {
"match" ":" "\n" Indent Dedent => {
let end_location = cases
.last()
.unwrap()
.body
.last()
.unwrap()
.end();
ast::Stmt::Match(
ast::StmtMatch {
subject: Box::new(subject),
cases,
range: (location..end_location).into()
}
)
},
"match" "," ":" "\n" Indent Dedent => {
let end_location = cases
.last()
.unwrap()
.body
.last()
.unwrap()
.end();
ast::Stmt::Match(
ast::StmtMatch {
subject: Box::new(subject),
cases,
range: (location..end_location).into()
}
)
},
"match" > ","? ":" "\n" Indent Dedent => {
let end_location = cases
.last()
.unwrap()
.body
.last()
.unwrap()
.end();
ast::Stmt::Match(
ast::StmtMatch {
subject: Box::new(ast::Expr::Tuple(
ast::ExprTuple {
elts: subjects,
ctx: ast::ExprContext::Load,
range: (location..end_location).into()
},
)),
cases,
range: (location..end_location).into()
}
)
}
}
MatchCase: ast::MatchCase = {
"case" ":" => {
ast::MatchCase {
pattern,
guard: guard.map(Box::new),
body,
range: optional_range(start, end)
}
},
}
Guard: ast::Expr = {
"if" => {
guard
}
}
Patterns: ast::Pattern = {
"," => ast::Pattern::MatchSequence(
ast::PatternMatchSequence {
patterns: vec![pattern],
range: (location..end_location).into()
},
),
> ","? => {
ast::Pattern::MatchSequence(
ast::PatternMatchSequence {
patterns,
range: (location..end_location).into()
},
)
},
=> pattern
}
Pattern: ast::Pattern = {
=> pattern,
=> pattern,
}
AsPattern: ast::Pattern = {
"as" =>? {
if name.as_str() == "_" {
Err(LexicalError {
error: LexicalErrorType::OtherError("cannot use '_' as a target".to_string()),
location,
})?
} else {
Ok(ast::Pattern::MatchAs(
ast::PatternMatchAs {
pattern: Some(Box::new(pattern)),
name: Some(name),
range: (location..end_location).into()
},
))
}
},
}
OrPattern: ast::Pattern = {
=> pattern,
> => {
ast::Pattern::MatchOr(
ast::PatternMatchOr { patterns, range: (location..end_location).into() }
)
}
}
ClosedPattern: ast::Pattern = {
=> node,
=> node,
=> node,
=> node,
=> node,
=> node,
=> node,
}
SequencePattern: ast::Pattern = {
// A single-item tuple is a special case: it's a group pattern, _not_ a sequence pattern.
"(" ")" => pattern,
"(" ")" => ast::PatternMatchSequence {
patterns: vec![],
range: (location..end_location).into()
}.into(),
"(" "," ")" => {
ast::PatternMatchSequence {
patterns: vec![pattern],
range: (location..end_location).into()
}.into()
},
"(" ",")+> ","? ")" => {
let mut patterns = patterns;
patterns.push(last);
ast::PatternMatchSequence {
patterns,
range: (location..end_location).into()
}.into()
},
"[" > "]" => ast::PatternMatchSequence {
patterns,
range: (location..end_location).into()
}.into(),
}
StarPattern: ast::Pattern = {
"*" => ast::PatternMatchStar {
name: if name.as_str() == "_" { None } else { Some(name) },
range: (location..end_location).into()
}.into(),
}
ConstantAtom: ast::Expr = {
=> ast::Expr::Constant(
ast::ExprConstant { value, kind: None, range: (location..end_location).into() }
),
}
ConstantExpr: ast::Expr = {
ConstantAtom,
"-" => ast::Expr::UnaryOp(
ast::ExprUnaryOp {
op: ast::Unaryop::USub,
operand: Box::new(operand),
range: (location..end_location).into()
}
),
}
AddOpExpr: ast::Expr = {
=> ast::Expr::BinOp(
ast::ExprBinOp {
left: Box::new(left),
op,
right: Box::new(right),
range: (location..end_location).into()
}
),
}
LiteralPattern: ast::Pattern = {
"None" => ast::PatternMatchSingleton {
value: ast::Constant::None,
range: (location..end_location).into()
}.into(),
"True" => ast::PatternMatchSingleton {
value: true.into(),
range: (location..end_location).into()
}.into(),
"False" => ast::PatternMatchSingleton {
value: false.into(),
range: (location..end_location).into()
}.into(),
=> ast::PatternMatchValue {
value: Box::new(value),
range: (location..end_location).into()
}.into(),
=> ast::PatternMatchValue {
value: Box::new(value),
range: (location..end_location).into()
}.into(),
=>? Ok(ast::PatternMatchValue {
value: Box::new(parse_strings(s)?),
range: (location..end_location).into()
}.into()),
}
CapturePattern: ast::Pattern = {
=> ast::PatternMatchAs {
pattern: None,
name: if name.as_str() == "_" { None } else { Some(name) },
range: (location..end_location).into()
}.into(),
}
MatchName: ast::Expr = {
=> ast::Expr::Name(
ast::ExprName { id: name, ctx: ast::ExprContext::Load, range: (location..end_location).into() },
),
}
MatchNameOrAttr: ast::Expr = {
"." => ast::Expr::Attribute(
ast::ExprAttribute {
value: Box::new(name),
attr,
ctx: ast::ExprContext::Load,
range: (location..end_location).into()
},
),
"." => ast::Expr::Attribute(
ast::ExprAttribute {
value: Box::new(e),
attr,
ctx: ast::ExprContext::Load,
range: (location..end_location).into()
},
)
}
ValuePattern: ast::Pattern = {
=> ast::PatternMatchValue {
value: Box::new(e),
range: (location..end_location).into()
}.into(),
}
MappingKey: ast::Expr = {
ConstantExpr,
AddOpExpr,
MatchNameOrAttr,
"None" => ast::Expr::Constant(
ast::ExprConstant {
value: ast::Constant::None,
kind: None,
range: (location..end_location).into()
},
),
"True" => ast::Expr::Constant(
ast::ExprConstant {
value: true.into(),
kind: None,
range: (location..end_location).into()
},
),
"False" => ast::Expr::Constant(
ast::ExprConstant {
value: false.into(),
kind: None,
range: (location..end_location).into()
},
),
=>? Ok(parse_strings(s)?),
}
MatchMappingEntry: (ast::Expr, ast::Pattern) = {
":" => (k, v),
};
MappingPattern: ast::Pattern = {
"{" "}" => {
return ast::PatternMatchMapping {
keys: vec![],
patterns: vec![],
rest: None,
range: (location..end_location).into()
}.into();
},
"{" > ","? "}" => {
let (keys, patterns) = e
.into_iter()
.unzip();
return ast::PatternMatchMapping {
keys,
patterns,
rest: None,
range: (location..end_location).into()
}.into();
},
"{" "**" ","? "}" => {
return ast::PatternMatchMapping {
keys: vec![],
patterns: vec![],
rest: Some(rest),
range: (location..end_location).into()
}.into();
},
"{" > "," "**" ","? "}" => {
let (keys, patterns) = e
.into_iter()
.unzip();
return ast::PatternMatchMapping {
keys,
patterns,
rest: Some(rest),
range: (location..end_location).into()
}.into();
},
}
MatchKeywordEntry: (ast::Identifier, ast::Pattern) = {
"=" => (k, v),
};
ClassPattern: ast::Pattern = {
"(" > "," > ","? ")" => {
let (kwd_attrs, kwd_patterns) = kwds
.into_iter()
.unzip();
ast::PatternMatchClass {
cls: Box::new(e),
patterns,
kwd_attrs,
kwd_patterns,
range: (location..end_location).into()
}.into()
},
"(" > ","? ")" => {
ast::PatternMatchClass {
cls: Box::new(e),
patterns,
kwd_attrs: vec![],
kwd_patterns: vec![],
range: (location..end_location).into()
}.into()
},
"(" > ","? ")" => {
let (kwd_attrs, kwd_patterns) = kwds
.into_iter()
.unzip();
ast::PatternMatchClass {
cls: Box::new(e),
patterns: vec![],
kwd_attrs,
kwd_patterns,
range: (location..end_location).into()
}.into()
},
"(" ")" => {
ast::PatternMatchClass {
cls: Box::new(e),
patterns: vec![],
kwd_attrs: vec![],
kwd_patterns: vec![],
range: (location..end_location).into()
}.into()
},
"(" > "," > ","? ")" => {
let (kwd_attrs, kwd_patterns) = kwds
.into_iter()
.unzip();
ast::PatternMatchClass {
cls: Box::new(e),
patterns,
kwd_attrs,
kwd_patterns,
range: (location..end_location).into()
}.into()
},
"(" > ","? ")" => {
ast::PatternMatchClass {
cls: Box::new(e),
patterns,
kwd_attrs: vec![],
kwd_patterns: vec![],
range: (location..end_location).into()
}.into()
},
"(" > ","? ")" => {
let (kwd_attrs, kwd_patterns) = kwds
.into_iter()
.unzip();
ast::PatternMatchClass {
cls: Box::new(e),
patterns: vec![],
kwd_attrs,
kwd_patterns,
range: (location..end_location).into()
}.into()
},
"(" ")" => {
ast::PatternMatchClass {
cls: Box::new(e),
patterns: vec![],
kwd_attrs: vec![],
kwd_patterns: vec![],
range: (location..end_location).into()
}.into()
},
}
IfStatement: ast::Stmt = {
"if" ":" => {
// Determine last else:
let mut last = s3.map(|s| s.2).unwrap_or_default();
let end_location = last
.last()
.or_else(|| s2.last().and_then(|last| last.4.last()))
.or_else(|| body.last())
.unwrap()
.end();
// handle elif:
for i in s2.into_iter().rev() {
let x = ast::Stmt::If(
ast::StmtIf { test: Box::new(i.2), body: i.4, orelse: last, range: (i.0..end_location).into() }
);
last = vec![x];
}
ast::Stmt::If(
ast::StmtIf { test: Box::new(test), body, orelse: last, range: (location..end_location).into() }
)
},
};
WhileStatement: ast::Stmt = {
"while" ":" => {
let orelse = s2.map(|s| s.2).unwrap_or_default();
let end_location = orelse
.last()
.or_else(|| body.last())
.unwrap()
.end();
ast::Stmt::While(
ast::StmtWhile {
test: Box::new(test),
body,
orelse,
range: (location..end_location).into()
},
)
},
};
ForStatement: ast::Stmt = {