// 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,
lexer::{LexicalError, LexicalErrorType},
function::{ArgumentList, parse_args, parse_params, validate_arguments},
context::set_context,
string::parse_strings,
token::{self, StringKind},
};
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::Mod::Module { body, type_ignores: vec![] },
StartInteractive => ast::Mod::Interactive { body },
StartExpression ("\n")* => ast::Mod::Expression { body: Box::new(body) },
};
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" => {
let mut statements = vec![s1];
statements.extend(s2.into_iter().map(|e| e.1));
statements
}
};
SmallStatement: ast::Stmt = {
ExpressionStatement,
PassStatement,
DelStatement,
FlowStatement,
ImportStatement,
GlobalStatement,
NonlocalStatement,
AssertStatement,
};
PassStatement: ast::Stmt = {
"pass" => {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Pass,
)
},
};
DelStatement: ast::Stmt = {
"del" => {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Delete { targets: targets.into_iter().map(|expr| set_context(expr, ast::ExprContext::Del)).collect() },
)
},
};
ExpressionStatement: ast::Stmt = {
=> {
// Just an expression, no assignment:
if suffix.is_empty() {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Expr { value: Box::new(expression) }
)
} 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::new(
location,
end_location,
ast::StmtKind::Assign { targets, value, type_comment: None },
)
}
},
=> {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::AugAssign {
target: Box::new(set_context(target, ast::ExprContext::Store)),
op,
value: Box::new(rhs)
},
)
},
> ":" > => {
let simple = matches!(target.node, ast::ExprKind::Name { .. });
ast::Stmt::new(
location,
end_location,
ast::StmtKind::AnnAssign {
target: Box::new(set_context(target, ast::ExprContext::Store)),
annotation: Box::new(annotation),
value: rhs.map(Box::new),
simple: if simple { 1 } else { 0 },
},
)
},
};
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::new(
location,
end_location,
ast::StmtKind::Break,
)
},
"continue" => {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Continue,
)
},
"return" => {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Return { value: value.map(Box::new) },
)
},
=> {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Expr { value: Box::new(expression) },
)
},
RaiseStatement,
};
RaiseStatement: ast::Stmt = {
"raise" => {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Raise { exc: None, cause: None },
)
},
"raise" > )?> => {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Raise { exc: Some(Box::new(t)), cause: c.map(|x| Box::new(x.1)) },
)
},
};
ImportStatement: ast::Stmt = {
"import" >> => {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Import { names },
)
},
"from" "import" => {
let (level, module) = source;
ast::Stmt::new(
location,
end_location,
ast::StmtKind::ImportFrom {
level,
module,
names
},
)
},
};
ImportFromLocation: (Option, Option) = {
=> {
(Some(dots.iter().sum()), Some(name))
},
=> {
(Some(dots.iter().sum()), None)
},
};
ImportDots: usize = {
"..." => 3,
"." => 1,
};
ImportAsNames: Vec = {
>> => i,
"(" >> ","? ")" => i,
"*" => {
// Star import all
vec![ast::Alias::new(location, end_location, ast::AliasData { name: "*".to_string(), asname: None })]
},
};
#[inline]
ImportAsAlias: ast::Alias = {
=> ast::Alias::new(location, end_location, ast::AliasData { name, asname: 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::Stmt = {
"global" > => {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Global { names }
)
},
};
NonlocalStatement: ast::Stmt = {
"nonlocal" > => {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Nonlocal { names }
)
},
};
AssertStatement: ast::Stmt = {
"assert" > )?> => {
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Assert {
test: Box::new(test),
msg: msg.map(|e| Box::new(e.1))
}
)
},
};
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::new(
location,
end_location,
ast::StmtKind::Match {
subject: Box::new(subject),
cases
}
)
},
"match" "," ":" "\n" Indent Dedent => {
let end_location = cases
.last()
.unwrap()
.body
.last()
.unwrap()
.end();
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Match {
subject: Box::new(subject),
cases
}
)
},
"match" "," > ","? ":" "\n" Indent Dedent => {
let end_location = cases
.last()
.unwrap()
.body
.last()
.unwrap()
.end();
let mut subjects = subjects;
subjects.insert(0, subject);
ast::Stmt::new(
location,
end_location,
ast::StmtKind::Match {
subject: Box::new(ast::Expr::new(
location,
end_location,
ast::ExprKind::Tuple {
elts: subjects,
ctx: ast::ExprContext::Load,
},
)),
cases
}
)
}
}
MatchCase: ast::MatchCase = {
"case" ":" => {
ast::MatchCase {
pattern,
guard: guard.map(Box::new),
body
}
},
}
Guard: ast::Expr = {
"if" => {
guard
}
}
Patterns: ast::Pattern = {
"," => ast::Pattern::new(
location,
end_location,
ast::PatternKind::MatchSequence {
patterns: vec![pattern]
},
),
"," > ","? => {
let mut patterns = patterns;
patterns.insert(0, pattern);
ast::Pattern::new(
location,
end_location,
ast::PatternKind::MatchSequence {
patterns
},
)
},
=> pattern
}
Pattern: ast::Pattern = {
=> pattern,
=> pattern,
}
AsPattern: ast::Pattern = {
"as" =>? {
if name == "_" {
Err(LexicalError {
error: LexicalErrorType::OtherError("cannot use '_' as a target".to_string()),
location,
})?
} else {
Ok(ast::Pattern::new(
location,
end_location,
ast::PatternKind::MatchAs {
pattern: Some(Box::new(pattern)),
name: Some(name),
},
))
}
},
}
OrPattern: ast::Pattern = {
=> pattern,
)+> => {
let mut patterns = patterns;
patterns.insert(0, pattern);
ast::Pattern::new(
location,
end_location,
ast::PatternKind::MatchOr { patterns }
)
}
}
ClosedPattern: ast::Pattern = {
=> ast::Pattern::new(
location,
end_location,
node,
),
=> ast::Pattern::new(
location,
end_location,
node,
),
=> ast::Pattern::new(
location,
end_location,
node,
),
=> ast::Pattern::new(
location,
end_location,
node,
),
=> ast::Pattern::new(
location,
end_location,
node,
),
=> ast::Pattern::new(
location,
end_location,
node,
),
=> ast::Pattern::new(
location,
end_location,
node,
),
}
SequencePattern: ast::PatternKind = {
// A single-item tuple is a special case: it's a group pattern, _not_ a sequence pattern.
"(" ")" => pattern.node,
"(" ")" => ast::PatternKind::MatchSequence {
patterns: vec![],
},
"(" "," > ")" => {
let mut patterns = patterns;
patterns.insert(0, pattern);
ast::PatternKind::MatchSequence {
patterns
}
},
"[" > "]" => ast::PatternKind::MatchSequence {
patterns
},
}
StarPattern: ast::PatternKind = {
"*" => ast::PatternKind::MatchStar {
name: if name == "_" { None } else { Some(name) }
},
}
ConstantAtom: ast::Expr = {
=> ast::Expr::new(
location,
end_location,
ast::ExprKind::Constant { value, kind: None }
),
}
ConstantExpr: ast::Expr = {
ConstantAtom,
"-" => ast::Expr::new(
location,
end_location,
ast::ExprKind::UnaryOp {
op: ast::Unaryop::USub,
operand: Box::new(operand)
}
),
}
AddOpExpr: ast::Expr = {
=> ast::Expr::new(
location,
end_location,
ast::ExprKind::BinOp {
left: Box::new(left),
op,
right: Box::new(right),
}
),
}
LiteralPattern: ast::PatternKind = {
"None" => ast::PatternKind::MatchSingleton {
value: ast::Constant::None
},
"True" => ast::PatternKind::MatchSingleton {
value: true.into()
},
"False" => ast::PatternKind::MatchSingleton {
value: false.into()
},
=> ast::PatternKind::MatchValue {
value: Box::new(value)
},
=> ast::PatternKind::MatchValue {
value: Box::new(value)
},
=>? Ok(ast::PatternKind::MatchValue {
value: Box::new(parse_strings(s)?)
}),
}
CapturePattern: ast::PatternKind = {
=> ast::PatternKind::MatchAs {
pattern: None,
name: if name == "_" { None } else { Some(name) }
},
}
MatchName: ast::Expr = {
=> ast::Expr::new(
location,
end_location,
ast::ExprKind::Name { id: name, ctx: ast::ExprContext::Load },
),
}
MatchNameOrAttr: ast::Expr = {
"." => ast::Expr::new(
location,
end_location,
ast::ExprKind::Attribute {
value: Box::new(name),
attr,
ctx: ast::ExprContext::Load,
},
),
"." => ast::Expr::new(
location,
end_location,
ast::ExprKind::Attribute {
value: Box::new(e),
attr,
ctx: ast::ExprContext::Load,
}
)
}
ValuePattern: ast::PatternKind = {
=> ast::PatternKind::MatchValue {
value: Box::new(e)
},
}
MappingKey: ast::Expr = {
ConstantExpr,
AddOpExpr,
MatchNameOrAttr,
"None" => ast::Expr::new(
location,
end_location,
ast::ExprKind::Constant {
value: ast::Constant::None,
kind: None,
},
),
"True" => ast::Expr::new(
location,
end_location,
ast::ExprKind::Constant {
value: true.into(),
kind: None,
},
),
"False" => ast::Expr::new(
location,
end_location,
ast::ExprKind::Constant {
value: false.into(),
kind: None,
},
),
=>? Ok(parse_strings(s)?),
}
MatchMappingEntry: (ast::Expr, ast::Pattern) = {
":" => (k, v),
};
MappingPattern: ast::PatternKind = {
"{" "}" => {
return ast::PatternKind::MatchMapping {
keys: vec![],
patterns: vec![],
rest: None,
};
},
"{" > ","? "}" => {
let (keys, patterns) = e
.into_iter()
.unzip();
return ast::PatternKind::MatchMapping {
keys,
patterns,
rest: None,
};
},
"{" "**" ","? "}" => {
return ast::PatternKind::MatchMapping {
keys: vec![],
patterns: vec![],
rest: Some(rest),
};
},
"{" > "," "**" ","? "}" => {
let (keys, patterns) = e
.into_iter()
.unzip();
return ast::PatternKind::MatchMapping {
keys,
patterns,
rest: Some(rest),
};
},
}
MatchKeywordEntry: (String, ast::Pattern) = {
"=" => (k, v),
};
ClassPattern: ast::PatternKind = {
"(" > "," > ","? ")" => {
let (kwd_attrs, kwd_patterns) = kwds
.into_iter()
.unzip();
ast::PatternKind::MatchClass {
cls: Box::new(e),
patterns,
kwd_attrs,
kwd_patterns,
}
},
"(" > ","? ")" => {
ast::PatternKind::MatchClass {
cls: Box::new(e),
patterns,
kwd_attrs: vec![],
kwd_patterns: vec![],
}
},
"(" > ","? ")" => {
let (kwd_attrs, kwd_patterns) = kwds
.into_iter()
.unzip();
ast::PatternKind::MatchClass {
cls: Box::new(e),
patterns: vec![],
kwd_attrs,
kwd_patterns,
}
},
"(" ")" => {
ast::PatternKind::MatchClass {
cls: Box::new(e),
patterns: vec![],
kwd_attrs: vec![],
kwd_patterns: vec![],
}
},
"(" > "," > ","? ")" => {
let (kwd_attrs, kwd_patterns) = kwds
.into_iter()
.unzip();
ast::PatternKind::MatchClass {
cls: Box::new(e),
patterns,
kwd_attrs,
kwd_patterns,
}
},
"(" > ","? ")" => {
ast::PatternKind::MatchClass {
cls: Box::new(e),
patterns,
kwd_attrs: vec![],
kwd_patterns: vec![],
}
},
"(" > ","? ")" => {
let (kwd_attrs, kwd_patterns) = kwds
.into_iter()
.unzip();
ast::PatternKind::MatchClass {
cls: Box::new(e),
patterns: vec![],
kwd_attrs,
kwd_patterns,
}
},
"(" ")" => {
ast::PatternKind::MatchClass {
cls: Box::new(e),
patterns: vec![],
kwd_attrs: vec![],
kwd_patterns: vec![],
}
},
}
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::new(
i.0,
end_location,
ast::StmtKind::If { test: Box::new(i.2), body: i.4, orelse: last },
);
last = vec![x];
}
ast::Stmt::new(
location,
end_location,
ast::StmtKind::If { test: Box::new(test), body, orelse: last }
)
},
};
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::new(
location,
end_location,
ast::StmtKind::While {
test: Box::new(test),
body,
orelse
},
)
},
};
ForStatement: ast::Stmt = {
"for" "in" ":"