// 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, error::{LexicalError, LexicalErrorType}, function::{ArgumentList, parse_args, parse_params}, lexer, context::set_context, string::parse_strings, token::StringKind, with::{ExprOrWithitems, TupleOrWithitems}, }; 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 { location, end_location: Some(end_location), custom: (), node: ast::StmtKind::Pass, } }, }; DelStatement: ast::Stmt = { "del" => { ast::Stmt { location, end_location: Some(end_location), custom: (), node: 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 { custom: (), location, end_location: Some(end_location), node: 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 { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Assign { targets, value, type_comment: None }, } } }, => { ast::Stmt { custom: (), location, end_location: Some(end_location), node: 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 { custom: (), location, end_location: Some(end_location), node: 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, StarExpr, }; TestOrStarNamedExpr: ast::Expr = { NamedExpressionTest, StarExpr, }; TestOrStarNamedExprOrWithitem: (ast::Expr, Option>) = { => (e, None), => (e, None), "as" => (e, Some(Box::new(v))), } 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 { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Break, } }, "continue" => { ast::Stmt { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Continue, } }, "return" => { ast::Stmt { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Return { value: value.map(Box::new) }, } }, => { ast::Stmt { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Expr { value: Box::new(expression) }, } }, RaiseStatement, }; RaiseStatement: ast::Stmt = { "raise" => { ast::Stmt { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Raise { exc: None, cause: None }, } }, "raise" => { ast::Stmt { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Raise { exc: Some(Box::new(t)), cause: c.map(|x| Box::new(x.1)) }, } }, }; ImportStatement: ast::Stmt = { "import" >> => { ast::Stmt { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Import { names }, } }, "from" "import" => { let (level, module) = source; ast::Stmt { custom: (), location, end_location: Some(end_location), node: 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 { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Global { names } } }, }; NonlocalStatement: ast::Stmt = { "nonlocal" > => { ast::Stmt { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Nonlocal { names } } }, }; AssertStatement: ast::Stmt = { "assert" => { ast::Stmt { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Assert { test: Box::new(test), msg: msg.map(|e| Box::new(e.1)) } } }, }; CompoundStatement: ast::Stmt = { IfStatement, WhileStatement, ForStatement, TryStatement, WithStatement, FuncDef, ClassDef, }; IfStatement: ast::Stmt = { "if" ":" => { // Determine last else: let mut last = s3.map(|s| s.2).unwrap_or_default(); // handle elif: for i in s2.into_iter().rev() { let x = ast::Stmt { custom: (), location: i.0, end_location: Some(i.5), node: ast::StmtKind::If { test: Box::new(i.2), body: i.4, orelse: last }, }; last = vec![x]; } ast::Stmt { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::If { test: Box::new(test), body, orelse: last } } }, }; WhileStatement: ast::Stmt = { "while" ":" => { let orelse = s2.map(|s| s.2).unwrap_or_default(); ast::Stmt { custom: (), location, end_location: Some(end_location), node: 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 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(); ast::Stmt { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Try { body, handlers, orelse, finalbody, }, } }, "try" ":" => { let handlers = vec![]; let orelse = vec![]; let finalbody = finally.2; ast::Stmt { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::Try { body, handlers, orelse, finalbody, }, } }, }; ExceptClause: ast::Excepthandler = { "except" ":" => { ast::Excepthandler::new( location, end_location, ast::ExcepthandlerKind::ExceptHandler { type_: typ.map(Box::new), name: None, body, }, ) }, "except" ":" => { 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 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) }, }; // These are only used for their types as macro parameters ExprGoal: ast::Expr = {}; ExprOrWithitemsGoal: ExprOrWithitems = {}; WithItems: Vec = { > =>? items.try_into(), > "as" =>? { let optional_vars = Some(Box::new(set_context(vars, ast::ExprContext::Store))); let context_expr = Box::new(first.try_into()?); Ok(vec![ast::Withitem { context_expr, optional_vars }]) }, > "," > =>? { let optional_vars = n.map(|val| Box::new(set_context(val.1, ast::ExprContext::Store))); let context_expr = Box::new(first.try_into()?); items.insert(0, ast::Withitem { context_expr, optional_vars }); Ok(items) } }; WithItem: ast::Withitem = { => { let optional_vars = n.map(|val| Box::new(set_context(val.1, ast::ExprContext::Store))); let context_expr = Box::new(context_expr); ast::Withitem { context_expr, optional_vars } }, }; FuncDef: ast::Stmt = { "def" " Test)?> ":" => { let args = Box::new(args); let returns = r.map(|x| Box::new(x.1)); 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 = { "(" )?> ")" => { a.unwrap_or_else(|| ast::Arguments { posonlyargs: vec![], args: vec![], vararg: None, kwonlyargs: vec![], kw_defaults: vec![], kwarg: None, defaults: vec![] }) } }; // 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 }) }, }; // 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()); let kwarg = kwarg.map(|n| n.1).flatten(); let va = va.map(Box::new); (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![]), }; ast::Stmt { custom: (), location, end_location: Some(end_location), node: ast::StmtKind::ClassDef { name, bases, keywords, body, decorator_list, }, } }, }; // Decorators: Decorator: ast::Expr = { "@" "\n" => { p }, }; YieldExpr: ast::Expr = { "yield" => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Yield { value: value.map(Box::new) } }, "yield" "from" => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::YieldFrom { value: Box::new(e) } }, }; Test = TestAs; TestAs: Goal = { "if" "else" => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::IfExp { test: Box::new(test), body: Box::new(body), orelse: Box::new(orelse), } }.into(), OrTestAs, => e.into(), }; NamedExpressionTest: ast::Expr = { ?> ":" => { let p = p.unwrap_or_else(|| { ast::Arguments { posonlyargs: vec![], args: vec![], vararg: None, kwonlyargs: vec![], kw_defaults: vec![], kwarg: None, defaults: vec![] } }); ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Lambda { args: Box::new(p), body: Box::new(body) } } } } OrTest = OrTestAs; OrTestAs: Goal = { => { let mut values = vec![e1]; values.extend(e2.into_iter().map(|e| e.1)); ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::BoolOp { op: ast::Boolop::Or, values } }.into() }, AndTestAs, }; AndTest = AndTestAs; AndTestAs: Goal = { => { let mut values = vec![e1]; values.extend(e2.into_iter().map(|e| e.1)); ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::BoolOp { op: ast::Boolop::And, values } }.into() }, NotTestAs, }; NotTest = NotTestAs; NotTestAs: Goal = { "not" => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::UnaryOp { operand: Box::new(e), op: ast::Unaryop::Not } }.into(), ComparisonAs, }; Comparison = ComparisonAs; ComparisonAs: Goal = { => { let (ops, comparators) = comparisons.into_iter().unzip(); ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Compare { left: Box::new(left), ops, comparators } }.into() }, ExpressionAs, }; 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 = ExpressionAs; ExpressionAs: Goal = { "|" => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::BinOp { left: Box::new(e1), op: ast::Operator::BitOr, right: Box::new(e2) } }.into(), XorExpressionAs, }; XorExpression = XorExpressionAs; XorExpressionAs: Goal = { "^" => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::BinOp { left: Box::new(e1), op: ast::Operator::BitXor, right: Box::new(e2) } }.into(), AndExpressionAs, }; AndExpression = AndExpressionAs; AndExpressionAs: Goal = { "&" => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::BinOp { left: Box::new(e1), op: ast::Operator::BitAnd, right: Box::new(e2) } }.into(), ShiftExpressionAs, }; ShiftExpression = ShiftExpressionAs; ShiftExpressionAs: Goal = { => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::BinOp { left: Box::new(e1), op, right: Box::new(e2) } }.into(), ArithmeticExpressionAs, }; ShiftOp: ast::Operator = { "<<" => ast::Operator::LShift, ">>" => ast::Operator::RShift, }; ArithmeticExpression = ArithmeticExpressionAs; ArithmeticExpressionAs: Goal = { => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::BinOp { left: Box::new(a), op, right: Box::new(b) } }.into(), TermAs, }; AddOp: ast::Operator = { "+" => ast::Operator::Add, "-" => ast::Operator::Sub, }; Term = TermAs; TermAs: Goal = { => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::BinOp { left: Box::new(a), op, right: Box::new(b) } }.into(), FactorAs, }; MulOp: ast::Operator = { "*" => ast::Operator::Mult, "/" => ast::Operator::Div, "//" => ast::Operator::FloorDiv, "%" => ast::Operator::Mod, "@" => ast::Operator::MatMult, }; Factor = FactorAs; FactorAs: Goal = { => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::UnaryOp { operand: Box::new(e), op } }.into(), PowerAs, }; UnaryOp: ast::Unaryop = { "+" => ast::Unaryop::UAdd, "-" => ast::Unaryop::USub, "~" => ast::Unaryop::Invert, }; Power = PowerAs; PowerAs: Goal = { "**" => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::BinOp { left: Box::new(e), op: ast::Operator::Pow, right: Box::new(b) } }.into(), AtomExprAs, }; AtomExpr = AtomExprAs; AtomExprAs: Goal = { "await" => { ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Await { value: Box::new(atom) } }.into() }, AtomExpr2As, } AtomExpr2 = AtomExpr2As; AtomExpr2As: Goal = { AtomAs, "(" ")" => { ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Call { func: Box::new(f), args: a.args, keywords: a.keywords } }.into() }, "[" "]" => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Subscript { value: Box::new(e), slice: Box::new(s), ctx: ast::ExprContext::Load } }.into(), "." => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Attribute { value: Box::new(e), attr, ctx: ast::ExprContext::Load } }.into(), }; 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 { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Tuple { elts: dims, ctx: ast::ExprContext::Load }, } } } }; Subscript: ast::Expr = { Test, ":" => { let lower = e1.map(Box::new); let upper = e2.map(Box::new); let step = e3.flatten().map(Box::new); ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Slice { lower, upper, step } } } }; SliceOp: Option = { ":" => e, } Atom = AtomAs; AtomAs: Goal = { =>? Ok(parse_strings(s)?.into()), => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Constant { value, kind: None } }.into(), => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Name { id: name, ctx: ast::ExprContext::Load } }.into(), "[" "]" => { let elts = e.unwrap_or_default(); ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::List { elts, ctx: ast::ExprContext::Load } }.into() }, "[" "]" => { ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::ListComp { elt: Box::new(elt), generators } }.into() }, "(" > ")" =>? { if items.len() == 1 && items[0].1.is_none() && trailing_comma.is_none() { match items[0].0.node { ast::ExprKind::Starred { .. } => { Err(LexicalError{ error: LexicalErrorType::OtherError("cannot use starred expression here".to_string()), location: items[0].0.location, }.into()) } _ => { Ok(items.into_iter().next().unwrap().0.into()) } } } else { TupleOrWithitems { location, end_location, items }.try_into() } }, "(" ")" => ast::Expr::new( location, end_location, ast::ExprKind::Tuple { elts: Vec::new(), ctx: ast::ExprContext::Load } ).into(), "(" ")" => e.into(), "(" ")" => { ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::GeneratorExp { elt: Box::new(elt), generators } }.into() }, "(" "**" ")" =>? { Err(LexicalError{ error : LexicalErrorType::OtherError("cannot use double starred expression here".to_string()), location: location, }.into()) }, "{" "}" => { let pairs = e.unwrap_or_default(); let (keys, values) = match pairs.iter().position(|(k,_)| k.is_none()) { Some(unpack_idx) => { let mut pairs = pairs; let (keys, mut values): (_, Vec<_>) = pairs.drain(..unpack_idx).map(|(k, v)| (*k.unwrap(), v)).unzip(); fn build_map(items: &mut Vec<(ast::Expr, ast::Expr)>) -> ast::Expr { let location = items[0].0.location; let end_location = items[0].0.end_location; let (keys, values) = items.drain(..).unzip(); ast::Expr { location, end_location, custom: (), node: ast::ExprKind::Dict { keys, values } } } let mut items = Vec::new(); for (key, value) in pairs.into_iter() { if let Some(key) = key { items.push((*key, value)); continue; } if !items.is_empty() { values.push(build_map(&mut items)); } values.push(value); } if !items.is_empty() { values.push(build_map(&mut items)); } (keys, values) }, None => pairs.into_iter().map(|(k, v)| (*k.unwrap(), v)).unzip() }; ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Dict { keys, values } }.into() }, "{" "}" => { ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::DictComp { key: Box::new(e1.0), value: Box::new(e1.1), generators, } }.into() }, "{" "}" => ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Set { elts } }.into(), "{" "}" => { ast::Expr { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::SetComp { elt: Box::new(elt), generators } }.into() }, "True" => ast::Expr::new(location, end_location, ast::ExprKind::Constant { value: true.into(), kind: None }).into(), "False" => ast::Expr::new(location, end_location, ast::ExprKind::Constant { value: false.into(), kind: None }).into(), "None" => ast::Expr::new(location, end_location, ast::ExprKind::Constant { value: ast::Constant::None, kind: None }).into(), "..." => ast::Expr::new(location, end_location, ast::ExprKind::Constant { value: ast::Constant::Ellipsis, kind: None }).into(), }; 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, 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 { location, end_location: Some(end_location), custom: (), node: ast::ExprKind::Tuple { elts, ctx: ast::ExprContext::Load } } } } } // Test StarExpr: ast::Expr = { "*" => ast::Expr { location, end_location: Some(end_location), custom: (), node: 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: Box::new(set_context(target, ast::ExprContext::Store)), iter: Box::new(iter), ifs, is_async: if is_async { 1 } else { 0 }, } } }; ExpressionNoCond: ast::Expr = OrTest; 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 { location: e.location, end_location: e.end_location, custom: (), node: 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::Bytes(b.into_iter().flatten().collect()), => ast::Constant::Int(value), => ast::Constant::Float(value), => ast::Constant::Complex { real: s.0, imag: s.1 }, }; Bytes: Vec = { => { s.into_iter().flatten().collect::>() }, }; Identifier: String = => s; // Hook external lexer: extern { type Location = ast::Location; type Error = LexicalError; enum lexer::Tok { Indent => lexer::Tok::Indent, Dedent => lexer::Tok::Dedent, StartModule => lexer::Tok::StartModule, StartInteractive => lexer::Tok::StartInteractive, StartExpression => lexer::Tok::StartExpression, "+" => lexer::Tok::Plus, "-" => lexer::Tok::Minus, "~" => lexer::Tok::Tilde, ":" => lexer::Tok::Colon, "." => lexer::Tok::Dot, "..." => lexer::Tok::Ellipsis, "," => lexer::Tok::Comma, "*" => lexer::Tok::Star, "**" => lexer::Tok::DoubleStar, "&" => lexer::Tok::Amper, "@" => lexer::Tok::At, "%" => lexer::Tok::Percent, "//" => lexer::Tok::DoubleSlash, "^" => lexer::Tok::CircumFlex, "|" => lexer::Tok::Vbar, "<<" => lexer::Tok::LeftShift, ">>" => lexer::Tok::RightShift, "/" => lexer::Tok::Slash, "(" => lexer::Tok::Lpar, ")" => lexer::Tok::Rpar, "[" => lexer::Tok::Lsqb, "]" => lexer::Tok::Rsqb, "{" => lexer::Tok::Lbrace, "}" => lexer::Tok::Rbrace, "=" => lexer::Tok::Equal, "+=" => lexer::Tok::PlusEqual, "-=" => lexer::Tok::MinusEqual, "*=" => lexer::Tok::StarEqual, "@=" => lexer::Tok::AtEqual, "/=" => lexer::Tok::SlashEqual, "%=" => lexer::Tok::PercentEqual, "&=" => lexer::Tok::AmperEqual, "|=" => lexer::Tok::VbarEqual, "^=" => lexer::Tok::CircumflexEqual, "<<=" => lexer::Tok::LeftShiftEqual, ">>=" => lexer::Tok::RightShiftEqual, "**=" => lexer::Tok::DoubleStarEqual, "//=" => lexer::Tok::DoubleSlashEqual, ":=" => lexer::Tok::ColonEqual, "==" => lexer::Tok::EqEqual, "!=" => lexer::Tok::NotEqual, "<" => lexer::Tok::Less, "<=" => lexer::Tok::LessEqual, ">" => lexer::Tok::Greater, ">=" => lexer::Tok::GreaterEqual, "->" => lexer::Tok::Rarrow, "and" => lexer::Tok::And, "as" => lexer::Tok::As, "assert" => lexer::Tok::Assert, "async" => lexer::Tok::Async, "await" => lexer::Tok::Await, "break" => lexer::Tok::Break, "class" => lexer::Tok::Class, "continue" => lexer::Tok::Continue, "def" => lexer::Tok::Def, "del" => lexer::Tok::Del, "elif" => lexer::Tok::Elif, "else" => lexer::Tok::Else, "except" => lexer::Tok::Except, "finally" => lexer::Tok::Finally, "for" => lexer::Tok::For, "from" => lexer::Tok::From, "global" => lexer::Tok::Global, "if" => lexer::Tok::If, "in" => lexer::Tok::In, "is" => lexer::Tok::Is, "import" => lexer::Tok::Import, "from" => lexer::Tok::From, "lambda" => lexer::Tok::Lambda, "nonlocal" => lexer::Tok::Nonlocal, "not" => lexer::Tok::Not, "or" => lexer::Tok::Or, "pass" => lexer::Tok::Pass, "raise" => lexer::Tok::Raise, "return" => lexer::Tok::Return, "try" => lexer::Tok::Try, "while" => lexer::Tok::While, "with" => lexer::Tok::With, "yield" => lexer::Tok::Yield, "True" => lexer::Tok::True, "False" => lexer::Tok::False, "None" => lexer::Tok::None, int => lexer::Tok::Int { value: }, float => lexer::Tok::Float { value: }, complex => lexer::Tok::Complex { real: , imag: }, string => lexer::Tok::String { value: , kind: }, bytes => lexer::Tok::Bytes { value: > }, name => lexer::Tok::Name { name: }, "\n" => lexer::Tok::Newline, ";" => lexer::Tok::Semi, "#" => lexer::Tok::Comment, } }