// 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" ":" => { let orelse = s2.map(|s| s.2).unwrap_or_default(); let end_location = orelse .last() .or_else(|| body.last()) .unwrap() .end(); let target = Box::new(set_context(target, ast::ExprContext::Store)); let iter = Box::new(iter); let type_comment = None; let node = if is_async.is_some() { ast::StmtKind::AsyncFor { target, iter, body, orelse, type_comment } } else { ast::StmtKind::For { target, iter, body, orelse, type_comment } }; ast::Stmt::new(location, end_location, node) }, }; TryStatement: ast::Stmt = { "try" ":" => { let orelse = else_suite.map(|s| s.2).unwrap_or_default(); let finalbody = finally.map(|s| s.2).unwrap_or_default(); let end_location = finalbody .last() .map(|last| last.end()) .or_else(|| orelse.last().map(|last| last.end())) .or_else(|| handlers.last().map(|last| last.end())) .unwrap(); ast::Stmt::new( location, end_location, ast::StmtKind::Try { body, handlers, orelse, finalbody, }, ) }, "try" ":" => { let orelse = else_suite.map(|s| s.2).unwrap_or_default(); let finalbody = finally.map(|s| s.2).unwrap_or_default(); let end_location = finalbody .last() .or_else(|| orelse.last()) .map(|last| last.end()) .or_else(|| handlers.last().map(|last| last.end())) .unwrap(); ast::Stmt::new( location, end_location, ast::StmtKind::TryStar { body, handlers, orelse, finalbody, }, ) }, "try" ":" => { let handlers = vec![]; let orelse = vec![]; let finalbody = finally.2; let end_location = finalbody.last().unwrap().end(); ast::Stmt::new( location, end_location, ast::StmtKind::Try { body, handlers, orelse, finalbody, }, ) }, }; ExceptStarClause: ast::Excepthandler = { "except" "*" > ":" => { let end_location = body.last().unwrap().end(); ast::Excepthandler::new( location, end_location, ast::ExcepthandlerKind::ExceptHandler { type_: Some(Box::new(typ)), name: None, body, }, ) }, "except" "*" "as" Identifier)> ":" => { let end_location = body.last().unwrap().end(); ast::Excepthandler::new( location, end_location, ast::ExcepthandlerKind::ExceptHandler { type_: Some(Box::new(x.0)), name: Some(x.2), body, }, ) }, }; ExceptClause: ast::Excepthandler = { "except" ?> ":" => { let end_location = body.last().unwrap().end(); ast::Excepthandler::new( location, end_location, ast::ExcepthandlerKind::ExceptHandler { type_: typ.map(Box::new), name: None, body, }, ) }, "except" "as" Identifier)> ":" => { let end_location = body.last().unwrap().end(); ast::Excepthandler::new( location, end_location, ast::ExcepthandlerKind::ExceptHandler { type_: Some(Box::new(x.0)), name: Some(x.2), body, }, ) }, }; WithStatement: ast::Stmt = { "with" ":" => { let end_location = body.last().unwrap().end(); let type_comment = None; let node = if is_async.is_some() { ast::StmtKind::AsyncWith { items, body, type_comment } } else { ast::StmtKind::With { items, body, type_comment } }; ast::Stmt::new(location, end_location, node) }, }; WithItems: Vec = { "(" ","? ")", "(" ",")?> > >)*> ","? ")" => { left.into_iter().flatten().chain([mid]).chain(right).collect() }, > => vec![<>], > >)+> => { [item].into_iter().chain(items).collect() } }; #[inline] WithItemsNoAs: Vec = { >> => { <>.into_iter().map(|context_expr| ast::Withitem { context_expr, optional_vars: None }).collect() }, } WithItem: ast::Withitem = { > if Goal != "as" => ast::Withitem { context_expr: <>, optional_vars: None }, > "as" > => { let optional_vars = Some(Box::new(set_context(vars, ast::ExprContext::Store))); ast::Withitem { context_expr, optional_vars } }, }; FuncDef: ast::Stmt = { "def" " Test<"all">)?> ":" => { let args = Box::new(args); let returns = r.map(|x| Box::new(x.1)); let end_location = body.last().unwrap().end(); let type_comment = None; let node = if is_async.is_some() { ast::StmtKind::AsyncFunctionDef { name, args, body, decorator_list, returns, type_comment } } else { ast::StmtKind::FunctionDef { name, args, body, decorator_list, returns, type_comment } }; ast::Stmt::new(location, end_location, node) }, }; Parameters: ast::Arguments = { "(" )?> ")" =>? { let args = validate_arguments( a.unwrap_or_else(|| ast::Arguments { posonlyargs: vec![], args: vec![], vararg: None, kwonlyargs: vec![], kw_defaults: vec![], kwarg: None, defaults: vec![] }) )?; Ok(args) } }; // Note that this is a macro which is used once for function defs, and // once for lambda defs. ParameterList: ast::Arguments = { > )?> ","? =>? { let (posonlyargs, args, 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::Arguments { posonlyargs, args, kwonlyargs, vararg, kwarg, defaults, kw_defaults, }) }, > )> ","? =>? { let (posonlyargs, args, defaults) = parse_params(param1)?; // Now gather rest of parameters: let vararg = None; let kwonlyargs = vec![]; let kw_defaults = vec![]; let kwarg = kw.1; Ok(ast::Arguments { posonlyargs, args, kwonlyargs, vararg, kwarg, defaults, kw_defaults, }) }, > ","? => { let (vararg, kwonlyargs, kw_defaults, kwarg) = params; ast::Arguments { posonlyargs: vec![], args: vec![], kwonlyargs, vararg, kwarg, defaults: vec![], kw_defaults, } }, > ","? => { ast::Arguments { posonlyargs: vec![], args: vec![], kwonlyargs: vec![], vararg: None, kwarg, defaults: vec![], kw_defaults: vec![], } }, }; // Use inline here to make sure the "," is not creating an ambiguity. #[inline] ParameterDefs: (Vec<(ast::Arg, Option)>, Vec<(ast::Arg, Option)>) = { >> => { (vec![], args) }, >> "," "/" )*> => { (pos_args, args.into_iter().map(|e| e.1).collect()) }, }; ParameterDef: (ast::Arg, Option) = { => (i, None), "=" > => (i, Some(e)), }; UntypedParameter: ast::Arg = { => ast::Arg::new( location, end_location, ast::ArgData { arg, annotation: None, type_comment: None }, ), }; TypedParameter: ast::Arg = { )?> => { let annotation = a.map(|x| Box::new(x.1)); ast::Arg::new(location, end_location, ast::ArgData { arg, annotation, type_comment: None }) }, }; StarTypedParameter: ast::Arg = { => { let annotation = a.map(|x| Box::new(x.1)); ast::Arg::new(location, end_location, ast::ArgData { arg, annotation, type_comment: None }) }, }; // 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>, Vec, Vec, Option>) = { "*" )*> )?> =>? { // Extract keyword arguments: let mut kwonlyargs = Vec::new(); let mut kw_defaults = Vec::new(); let mut kwargs = Vec::new(); for (name, value) in kw.into_iter().map(|x| x.1) { if let Some(value) = value { kwonlyargs.push(name); kw_defaults.push(value); } else { kwargs.push(name); } } kwargs.extend(kwonlyargs.into_iter()); if va.is_none() && kwargs.is_empty() && kwarg.is_none() { Err(LexicalError { error: LexicalErrorType::OtherError("named arguments must follow bare *".to_string()), location, })? } let kwarg = kwarg.map(|n| n.1).flatten(); let va = va.map(Box::new); Ok((va, kwargs, kw_defaults, kwarg)) } }; KwargParameter: Option> = { "**" => { kwarg.map(Box::new) } }; ClassDef: ast::Stmt = { "class" ":" => { let (bases, keywords) = match a { Some((_, arg, _)) => (arg.args, arg.keywords), None => (vec![], vec![]), }; let end_location = body.last().unwrap().end(); ast::Stmt::new( location, end_location, ast::StmtKind::ClassDef { name, bases, keywords, body, decorator_list, }, ) }, }; // Decorators: Decorator: ast::Expr = { "@" "\n" => { p }, }; YieldExpr: ast::Expr = { "yield" => ast::Expr::new( location, end_location, ast::ExprKind::Yield { value: value.map(Box::new) } ), "yield" "from" > => ast::Expr::new( location, end_location, ast::ExprKind::YieldFrom { value: Box::new(e) } ), }; Test: ast::Expr = { > "if" > "else" > => ast::Expr::new( location, end_location, ast::ExprKind::IfExp { test: Box::new(test), body: Box::new(body), orelse: Box::new(orelse), } ), OrTest, LambdaDef, }; NamedExpressionTest: ast::Expr = { NamedExpression, Test<"all">, } NamedExpression: ast::Expr = { ":=" > => { ast::Expr::new( location, value.end(), ast::ExprKind::NamedExpr { target: Box::new(ast::Expr::new( location, end_location, ast::ExprKind::Name { id, ctx: ast::ExprContext::Store }, )), value: Box::new(value), } ) }, }; LambdaDef: ast::Expr = { "lambda" ?> ":" > =>? { let p = validate_arguments( p.unwrap_or_else(|| { ast::Arguments { posonlyargs: vec![], args: vec![], vararg: None, kwonlyargs: vec![], kw_defaults: vec![], kwarg: None, defaults: vec![] } } ))?; Ok(ast::Expr::new( location, end_location, ast::ExprKind::Lambda { args: Box::new(p), body: Box::new(body) } )) } } OrTest: ast::Expr = { > )+> => { let mut values = vec![e1]; values.extend(e2.into_iter().map(|e| e.1)); ast::Expr::new( location, end_location, ast::ExprKind::BoolOp { op: ast::Boolop::Or, values } ) }, AndTest, }; AndTest: ast::Expr = { > )+> => { let mut values = vec![e1]; values.extend(e2.into_iter().map(|e| e.1)); ast::Expr::new( location, end_location, ast::ExprKind::BoolOp { op: ast::Boolop::And, values } ) }, NotTest, }; NotTest: ast::Expr = { "not" > => ast::Expr::new( location, end_location, ast::ExprKind::UnaryOp { operand: Box::new(e), op: ast::Unaryop::Not } ), Comparison, }; Comparison: ast::Expr = { > )+> => { let (ops, comparators) = comparisons.into_iter().unzip(); ast::Expr::new( location, end_location, ast::ExprKind::Compare { left: Box::new(left), ops, comparators } ) }, Expression, }; CompOp: ast::Cmpop = { "==" => ast::Cmpop::Eq, "!=" => ast::Cmpop::NotEq, "<" => ast::Cmpop::Lt, "<=" => ast::Cmpop::LtE, ">" => ast::Cmpop::Gt, ">=" => ast::Cmpop::GtE, "in" => ast::Cmpop::In, "not" "in" => ast::Cmpop::NotIn, "is" => ast::Cmpop::Is, "is" "not" => ast::Cmpop::IsNot, }; Expression: ast::Expr = { > "|" > => ast::Expr::new( location, end_location, ast::ExprKind::BinOp { left: Box::new(e1), op: ast::Operator::BitOr, right: Box::new(e2) } ), XorExpression, }; XorExpression: ast::Expr = { > "^" > => ast::Expr::new( location, end_location, ast::ExprKind::BinOp { left: Box::new(e1), op: ast::Operator::BitXor, right: Box::new(e2) } ), AndExpression, }; AndExpression: ast::Expr = { > "&" > => ast::Expr::new( location, end_location, ast::ExprKind::BinOp { left: Box::new(e1), op: ast::Operator::BitAnd, right: Box::new(e2) } ), ShiftExpression, }; ShiftExpression: ast::Expr = { > > => ast::Expr::new( location, end_location, ast::ExprKind::BinOp { left: Box::new(e1), op, right: Box::new(e2) } ), ArithmeticExpression, }; ShiftOp: ast::Operator = { "<<" => ast::Operator::LShift, ">>" => ast::Operator::RShift, }; ArithmeticExpression: ast::Expr = { > > => ast::Expr::new( location, end_location, ast::ExprKind::BinOp { left: Box::new(a), op, right: Box::new(b) } ), Term, }; AddOp: ast::Operator = { "+" => ast::Operator::Add, "-" => ast::Operator::Sub, }; Term: ast::Expr = { > > => ast::Expr::new( location, end_location, ast::ExprKind::BinOp { left: Box::new(a), op, right: Box::new(b) } ), Factor, }; MulOp: ast::Operator = { "*" => ast::Operator::Mult, "/" => ast::Operator::Div, "//" => ast::Operator::FloorDiv, "%" => ast::Operator::Mod, "@" => ast::Operator::MatMult, }; Factor: ast::Expr = { > => ast::Expr::new( location, end_location, ast::ExprKind::UnaryOp { operand: Box::new(e), op } ), Power, }; UnaryOp: ast::Unaryop = { "+" => ast::Unaryop::UAdd, "-" => ast::Unaryop::USub, "~" => ast::Unaryop::Invert, }; Power: ast::Expr = { > "**" > => ast::Expr::new( location, end_location, ast::ExprKind::BinOp { left: Box::new(e), op: ast::Operator::Pow, right: Box::new(b) } ), AtomExpr, }; AtomExpr: ast::Expr = { "await" > => { ast::Expr::new( location, end_location, ast::ExprKind::Await { value: Box::new(atom) } ) }, AtomExpr2, } AtomExpr2: ast::Expr = { Atom, > "(" ")" => { ast::Expr::new( location, end_location, ast::ExprKind::Call { func: Box::new(f), args: a.args, keywords: a.keywords } ) }, > "[" "]" => ast::Expr::new( location, end_location, ast::ExprKind::Subscript { value: Box::new(e), slice: Box::new(s), ctx: ast::ExprContext::Load } ), > "." => ast::Expr::new( location, end_location, ast::ExprKind::Attribute { value: Box::new(e), attr, ctx: ast::ExprContext::Load } ), }; SubscriptList: ast::Expr = { => { if s2.is_empty() && trailing_comma.is_none() { s1 } else { let mut dims = vec![s1]; for x in s2 { dims.push(x.1) } ast::Expr::new( location, end_location, ast::ExprKind::Tuple { elts: dims, ctx: ast::ExprContext::Load }, ) } } }; Subscript: ast::Expr = { TestOrStarNamedExpr, ?> ":" ?> => { let lower = e1.map(Box::new); let upper = e2.map(Box::new); let step = e3.flatten().map(Box::new); ast::Expr::new( location, end_location, ast::ExprKind::Slice { lower, upper, step } ) } }; SliceOp: Option = { ":" ?> => e, } Atom: ast::Expr = { =>? Ok(parse_strings(s)?), => ast::Expr::new( location, end_location, ast::ExprKind::Constant { value, kind: None } ), => ast::Expr::new( location, end_location, ast::ExprKind::Name { id: name, ctx: ast::ExprContext::Load } ), "[" "]" => { let elts = e.unwrap_or_default(); ast::Expr::new( location, end_location, ast::ExprKind::List { elts, ctx: ast::ExprContext::Load } ) }, "[" "]" => { ast::Expr::new( location, end_location, ast::ExprKind::ListComp { elt: Box::new(elt), generators } ) }, "(" >> ")" if Goal != "no-withitems" => { if elts.len() == 1 && trailing_comma.is_none() { elts.into_iter().next().unwrap() } else { ast::Expr::new( location, end_location, ast::ExprKind::Tuple { elts, ctx: ast::ExprContext::Load }, ) } }, "(" >> ",")?> )*> ")" =>? { if left.is_none() && right.is_empty() && trailing_comma.is_none() { if matches!(mid.node, ast::ExprKind::Starred { .. }) { Err(LexicalError{ error: LexicalErrorType::OtherError("cannot use starred expression here".to_string()), location: mid.start(), })? } Ok(mid) } else { let elts = left.into_iter().flatten().chain([mid]).chain(right).collect(); Ok(ast::Expr::new( location, end_location, ast::ExprKind::Tuple { elts, ctx: ast::ExprContext::Load }, )) } }, "(" ")" => ast::Expr::new( location, end_location, ast::ExprKind::Tuple { elts: Vec::new(), ctx: ast::ExprContext::Load } ), "(" ")" => e, "(" ")" => { ast::Expr::new( location, end_location, ast::ExprKind::GeneratorExp { elt: Box::new(elt), generators } ) }, "(" "**" > ")" =>? { Err(LexicalError{ error : LexicalErrorType::OtherError("cannot use double starred expression here".to_string()), location, }.into()) }, "{" "}" => { let (keys, values) = e .unwrap_or_default() .into_iter() .map(|(k, v)| (k.map(|x| *x), v)) .unzip(); ast::Expr::new( location, end_location, ast::ExprKind::Dict { keys, values } ) }, "{" "}" => { ast::Expr::new( location, end_location, ast::ExprKind::DictComp { key: Box::new(e1.0), value: Box::new(e1.1), generators, } ) }, "{" "}" => ast::Expr::new( location, end_location, ast::ExprKind::Set { elts } ), "{" "}" => { ast::Expr::new( location, end_location, ast::ExprKind::SetComp { elt: Box::new(elt), generators } ) }, "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 }), "None" => ast::Expr::new(location, end_location, ast::ExprKind::Constant { value: ast::Constant::None, kind: None }), "..." => ast::Expr::new(location, end_location, ast::ExprKind::Constant { value: ast::Constant::Ellipsis, kind: None }), }; ListLiteralValues: Vec = { > ","? => e, }; DictLiteralValues: Vec<(Option>, ast::Expr)> = { > ","? => elements, }; DictEntry: (ast::Expr, ast::Expr) = { > ":" > => (e1, e2), }; DictElement: (Option>, ast::Expr) = { => (Some(Box::new(e.0)), e.1), "**" > => (None, e), }; SetLiteralValues: Vec = { > ","? => e1 }; ExpressionOrStarExpression = { Expression<"all">, StarExpr }; ExpressionList: ast::Expr = { GenericList }; ExpressionList2: Vec = { > ","? => elements, }; // A test list is one of: // - a list of expressions // - a single expression // - a single expression followed by a trailing comma #[inline] TestList: ast::Expr = { GenericList }; GenericList: ast::Expr = { > => { if elts.len() == 1 && trailing_comma.is_none() { elts.into_iter().next().unwrap() } else { ast::Expr::new( location, end_location, ast::ExprKind::Tuple { elts, ctx: ast::ExprContext::Load } ) } } } // Test StarExpr: ast::Expr = { "*" > => ast::Expr::new( location, end_location, ast::ExprKind::Starred { value: Box::new(e), ctx: ast::ExprContext::Load }, ) }; // Comprehensions: CompFor: Vec = => c; SingleForComprehension: ast::Comprehension = { "for" "in" > => { let is_async = is_async.is_some(); ast::Comprehension { target: set_context(target, ast::ExprContext::Store), iter, ifs, is_async: if is_async { 1 } else { 0 }, } } }; ExpressionNoCond: ast::Expr = OrTest<"all">; ComprehensionIf: ast::Expr = "if" => c; ArgumentList: ArgumentList = { > =>? { let arg_list = parse_args(e)?; Ok(arg_list) } }; FunctionArgument: (Option<(ast::Location, ast::Location, Option)>, ast::Expr) = { => { let expr = match c { Some(c) => ast::Expr::new( location, end_location, ast::ExprKind::GeneratorExp { elt: Box::new(e), generators: c, } ), None => e, }; (None, expr) }, "=" > => (Some((location, end_location, Some(i))), e), "*" > => { let expr = ast::Expr::new( location, end_location, ast::ExprKind::Starred { value: Box::new(e), ctx: ast::ExprContext::Load }, ); (None, expr) }, "**" > => (Some((location, end_location, None)), e), }; #[inline] Comma: Vec = { ",")*> => { let mut items = items; items.extend(last); items } }; #[inline] OneOrMore: Vec = { => { let mut items = vec![i1]; items.extend(i2.into_iter().map(|e| e.1)); items } }; Constant: ast::Constant = { => ast::Constant::Int(value), => ast::Constant::Float(value), => ast::Constant::Complex { real: s.0, imag: s.1 }, }; Identifier: String = => s; // Hook external lexer: extern { type Location = ast::Location; type Error = LexicalError; enum token::Tok { Indent => token::Tok::Indent, Dedent => token::Tok::Dedent, StartModule => token::Tok::StartModule, StartInteractive => token::Tok::StartInteractive, StartExpression => token::Tok::StartExpression, "+" => token::Tok::Plus, "-" => token::Tok::Minus, "~" => token::Tok::Tilde, ":" => token::Tok::Colon, "." => token::Tok::Dot, "..." => token::Tok::Ellipsis, "," => token::Tok::Comma, "*" => token::Tok::Star, "**" => token::Tok::DoubleStar, "&" => token::Tok::Amper, "@" => token::Tok::At, "%" => token::Tok::Percent, "//" => token::Tok::DoubleSlash, "^" => token::Tok::CircumFlex, "|" => token::Tok::Vbar, "<<" => token::Tok::LeftShift, ">>" => token::Tok::RightShift, "/" => token::Tok::Slash, "(" => token::Tok::Lpar, ")" => token::Tok::Rpar, "[" => token::Tok::Lsqb, "]" => token::Tok::Rsqb, "{" => token::Tok::Lbrace, "}" => token::Tok::Rbrace, "=" => token::Tok::Equal, "+=" => token::Tok::PlusEqual, "-=" => token::Tok::MinusEqual, "*=" => token::Tok::StarEqual, "@=" => token::Tok::AtEqual, "/=" => token::Tok::SlashEqual, "%=" => token::Tok::PercentEqual, "&=" => token::Tok::AmperEqual, "|=" => token::Tok::VbarEqual, "^=" => token::Tok::CircumflexEqual, "<<=" => token::Tok::LeftShiftEqual, ">>=" => token::Tok::RightShiftEqual, "**=" => token::Tok::DoubleStarEqual, "//=" => token::Tok::DoubleSlashEqual, ":=" => token::Tok::ColonEqual, "==" => token::Tok::EqEqual, "!=" => token::Tok::NotEqual, "<" => token::Tok::Less, "<=" => token::Tok::LessEqual, ">" => token::Tok::Greater, ">=" => token::Tok::GreaterEqual, "->" => token::Tok::Rarrow, "and" => token::Tok::And, "as" => token::Tok::As, "assert" => token::Tok::Assert, "async" => token::Tok::Async, "await" => token::Tok::Await, "break" => token::Tok::Break, "class" => token::Tok::Class, "continue" => token::Tok::Continue, "def" => token::Tok::Def, "del" => token::Tok::Del, "elif" => token::Tok::Elif, "else" => token::Tok::Else, "except" => token::Tok::Except, "finally" => token::Tok::Finally, "for" => token::Tok::For, "from" => token::Tok::From, "global" => token::Tok::Global, "if" => token::Tok::If, "import" => token::Tok::Import, "in" => token::Tok::In, "is" => token::Tok::Is, "lambda" => token::Tok::Lambda, "nonlocal" => token::Tok::Nonlocal, "not" => token::Tok::Not, "or" => token::Tok::Or, "pass" => token::Tok::Pass, "raise" => token::Tok::Raise, "return" => token::Tok::Return, "try" => token::Tok::Try, "while" => token::Tok::While, "match" => token::Tok::Match, "case" => token::Tok::Case, "with" => token::Tok::With, "yield" => token::Tok::Yield, "True" => token::Tok::True, "False" => token::Tok::False, "None" => token::Tok::None, int => token::Tok::Int { value: }, float => token::Tok::Float { value: }, complex => token::Tok::Complex { real: , imag: }, string => token::Tok::String { value: , kind: , triple_quoted: }, name => token::Tok::Name { name: }, "\n" => token::Tok::Newline, ";" => token::Tok::Semi, "#" => token::Tok::Comment(_), } }