// 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 #![allow(unknown_lints,clippy)] use super::ast; use super::lexer; use std::iter::FromIterator; 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 => ast::Top::Expression(e), }; Program: ast::Program = { => ast::Program { statements: Vec::from_iter(lines.into_iter().filter_map(|e| e)) }, }; // A file line either has a declaration, or an empty newline: FileLine: Option = { => Some(s), "\n" => None, }; Suite: Vec = { => vec![s], "\n" indent dedent => s, }; Statement: ast::LocatedStatement = { SimpleStatement, CompoundStatement, }; SimpleStatement: ast::LocatedStatement = { "\n" => s, ";" => s, }; SmallStatement: ast::LocatedStatement = { ExpressionStatement, PassStatement, DelStatement, FlowStatement, ImportStatement, GlobalStatement, NonlocalStatement, AssertStatement, }; PassStatement: ast::LocatedStatement = { "pass" => { ast::LocatedStatement { location: loc, node: ast::Statement::Pass, } }, }; DelStatement: ast::LocatedStatement = { "del" => { ast::LocatedStatement { location: loc, node: ast::Statement::Delete { targets: e }, } }, }; ExpressionStatement: ast::LocatedStatement = { => { // Just an expression, no assignment: if suffix.is_empty() { ast::LocatedStatement { location: loc.clone(), node: ast::Statement::Expression { expression: expr } } } else { let mut targets = vec![expr]; let mut values = suffix; while values.len() > 1 { targets.push(values.remove(0)); } let value = values.into_iter().next().unwrap(); ast::LocatedStatement { location: loc.clone(), node: ast::Statement::Assign { targets, value }, } } }, => { // TODO: this works in most cases: let rhs = e2.into_iter().next().unwrap(); ast::LocatedStatement { location: loc, node: ast::Statement::AugAssign { target: expr, op: op, value: rhs }, } }, }; AssignSuffix: ast::Expression = { "=" => { if e.len() > 1 { ast::Expression::Tuple { elements: e } } else { e.into_iter().next().unwrap() } }, "=" => e, }; TestOrStarExprList: ast::Expression = { => { let mut res = vec![e]; res.extend(e2.into_iter().map(|x| x.1)); // First build tuple from first item: let expr = if (res.len() > 1) || comma.is_some() { ast::Expression::Tuple { elements: res } } else { res.into_iter().next().unwrap() }; expr } }; TestOrStarExpr: ast::Expression = { Test, 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::LocatedStatement = { "break" => { ast::LocatedStatement { location: loc, node: ast::Statement::Break, } }, "continue" => { ast::LocatedStatement { location: loc, node: ast::Statement::Continue, } }, "return" => { ast::LocatedStatement { location: loc, node: ast::Statement::Return { value: t }, } }, => { ast::LocatedStatement { location: loc, node: ast::Statement::Expression { expression: y }, } }, RaiseStatement, }; RaiseStatement: ast::LocatedStatement = { "raise" => { ast::LocatedStatement { location: loc, node: ast::Statement::Raise { exception: None, cause: None }, } }, "raise" => { ast::LocatedStatement { location: loc, node: ast::Statement::Raise { exception: Some(t), cause: c.map(|x| x.1) }, } }, }; ImportStatement: ast::LocatedStatement = { "import" >>> => { ast::LocatedStatement { location: loc, node: ast::Statement::Import { import_parts: i .iter() .map(|(n, a)| ast::SingleImport { module: n.to_string(), symbol: None, alias: a.clone() }) .collect() }, } }, "from" "import" => { ast::LocatedStatement { location: loc, node: ast::Statement::Import { import_parts: i .iter() .map(|(i, a)| ast::SingleImport { module: n.to_string(), symbol: Some(i.to_string()), alias: a.clone() }) .collect() }, } }, }; ImportFromLocation: String = { => { let mut r = "".to_string(); for _dot in dots { r.push_str("."); } r.push_str(&name); r }, => { let mut r = "".to_string(); for _dot in dots { r.push_str("."); } r }, }; ImportAsNames: Vec<(String, Option)> = { >> => i, "(" >> ")" => i, "*" => { // Star import all vec![("*".to_string(), None)] }, }; #[inline] ImportPart: (String, Option) = { => (i, 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::LocatedStatement = { "global" > => { ast::LocatedStatement { location: loc, node: ast::Statement::Global { names } } }, }; NonlocalStatement: ast::LocatedStatement = { "nonlocal" > => { ast::LocatedStatement { location: loc, node: ast::Statement::Nonlocal { names } } }, }; AssertStatement: ast::LocatedStatement = { "assert" => { ast::LocatedStatement { location: loc, node: ast::Statement::Assert { test: t, msg: match m { Some(e) => Some(e.1), None => None, } } } }, }; CompoundStatement: ast::LocatedStatement = { IfStatement, WhileStatement, ForStatement, TryStatement, WithStatement, FuncDef, ClassDef, }; IfStatement: ast::LocatedStatement = { "if" ":" => { // Determine last else: let mut last = match s3 { Some(s) => Some(s.2), None => None, }; // handle elif: for i in s2.into_iter().rev() { let x = ast::LocatedStatement { location: i.0, node: ast::Statement::If { test: i.2, body: i.4, orelse: last }, }; last = Some(vec![x]); } ast::LocatedStatement { location: loc, node: ast::Statement::If { test: t, body: s1, orelse: last } } }, }; WhileStatement: ast::LocatedStatement = { "while" ":" => { let or_else = match s2 { Some(s) => Some(s.2), None => None, }; ast::LocatedStatement { location: loc, node: ast::Statement::While { test: e, body: s, orelse: or_else }, } }, }; ForStatement: ast::LocatedStatement = { "for" "in" ":" => { let or_else = match s2 { Some(s) => Some(s.2), None => None, }; ast::LocatedStatement { location: loc, node: ast::Statement::For { target: e, iter: t, body: s, orelse: or_else }, } }, }; TryStatement: ast::LocatedStatement = { "try" ":" => { let or_else = match else_suite { Some(s) => Some(s.2), None => None, }; let finalbody = match finally { Some(s) => Some(s.2), None => None, }; ast::LocatedStatement { location: loc, node: ast::Statement::Try { body: body, handlers: handlers, orelse: or_else, finalbody: finalbody, }, } }, }; ExceptClause: ast::ExceptHandler = { "except" ":" => { ast::ExceptHandler { typ: typ, name: None, body: body, } }, "except" ":" => { ast::ExceptHandler { typ: Some(x.0), name: Some(x.2), body: body, } }, }; WithStatement: ast::LocatedStatement = { "with" > ":" => { ast::LocatedStatement { location: loc, node: ast::Statement::With { items: items, body: s }, } }, }; WithItem: ast::WithItem = { => { let optional_vars = match n { Some(val) => Some(val.1), None => None, }; ast::WithItem { context_expr: t, optional_vars } }, }; FuncDef: ast::LocatedStatement = { "def" ":" => { ast::LocatedStatement { location: loc, node: ast::Statement::FunctionDef { name: i, args: a, body: s, decorator_list: d, } } }, }; Parameters: ast::Parameters = { "(" ")" => { match a { Some(a) => a, None => Default::default(), } }, }; // parameters are (String, None), kwargs are (String, Some(Test)) where Test is // the default TypedArgsList: ast::Parameters = { => { let (names, default_elements) = param1; // Now gather rest of parameters: let (vararg, kwonlyargs, kw_defaults, kwarg) = match args2 { Some((_, x)) => x, None => (None, vec![], vec![], None), }; ast::Parameters { args: names, kwonlyargs: kwonlyargs, vararg: vararg, kwarg: kwarg, defaults: default_elements, kw_defaults: kw_defaults, } }, => { let (names, default_elements) = param1; // Now gather rest of parameters: let vararg = None; let kwonlyargs = vec![]; let kw_defaults = vec![]; let kwarg = Some(kw.1); ast::Parameters { args: names, kwonlyargs: kwonlyargs, vararg: vararg, kwarg: kwarg, defaults: default_elements, kw_defaults: kw_defaults, } }, => { let (vararg, kwonlyargs, kw_defaults, kwarg) = params; ast::Parameters { args: vec![], kwonlyargs: kwonlyargs, vararg: vararg, kwarg: kwarg, defaults: vec![], kw_defaults: kw_defaults, } }, => { ast::Parameters { args: vec![], kwonlyargs: vec![], vararg: None, kwarg: Some(kw), defaults: vec![], kw_defaults: vec![], } }, }; // Use inline here to make sure the "," is not creating an ambiguity. #[inline] TypedParameters: (Vec, Vec) = { => { // Combine first parameters: let mut args = vec![param1]; args.extend(param2.into_iter().map(|x| x.1)); let mut names = vec![]; let mut default_elements = vec![]; for (name, default) in args.into_iter() { names.push(name.clone()); if let Some(default) = default { default_elements.push(default); } else { if default_elements.len() > 0 { // Once we have started with defaults, all remaining arguments must // have defaults panic!( "non-default argument follows default argument: {}", name ); } } } (names, default_elements) } }; TypedParameterDef: (String, Option) = { => (i, None), "=" => (i, Some(e)), }; // TODO: add type annotations here: TypedParameter: String = { Identifier, }; ParameterListStarArgs: (Option>, Vec, Vec>, Option>) = { "*" => { // Extract keyword arguments: let mut kwonlyargs = vec![]; let mut kw_defaults = vec![]; for (name, value) in kw.into_iter().map(|x| x.1) { kwonlyargs.push(name); kw_defaults.push(value); } let kwarg = match kwarg { Some((_, name)) => Some(name), None => None, }; (Some(va), kwonlyargs, kw_defaults, kwarg) } }; KwargParameter: Option = { "**" => { kwarg } }; ClassDef: ast::LocatedStatement = { "class" ":" => { let (bases, keywords) = match a { Some((_, args, _)) => args, None => (vec![], vec![]), }; ast::LocatedStatement { location: loc, node: ast::Statement::ClassDef { name: n, bases: bases, keywords: keywords, body: s, decorator_list: d, }, } }, }; // Decorators: Decorator: ast::Expression = { "@" "\n" => { let name = ast::Expression::Identifier { name: n }; match a { Some((_, args, _)) => ast::Expression::Call { function: Box::new(name), args: args.0, keywords: args.1, }, None => name, } }, }; YieldExpr: ast::Expression = { "yield" => { ast::Expression::Yield { value: ex.map(|expr| Box::new( if expr.len() > 1 { ast::Expression::Tuple { elements: expr } } else { expr.into_iter().next().unwrap() }) ) } }, "yield" "from" => { ast::Expression::YieldFrom { value: Box::new(e), } }, }; Test: ast::Expression = { => { match c { Some(c) => { ast::Expression::IfExpression { test: Box::new(c.1), body: Box::new(e), orelse: Box::new(c.3), } }, None => e, } }, => e, }; LambdaDef: ast::Expression = { "lambda" ":" => ast::Expression::Lambda { args: p.unwrap_or(Default::default()), body:Box::new(b) } } OrTest: ast::Expression = { => e, "or" => ast::Expression::BoolOp { a: Box::new(e1), op: ast::BooleanOperator::Or, b: Box::new(e2) }, }; AndTest: ast::Expression = { => e, "and" => ast::Expression::BoolOp { a: Box::new(e1), op: ast::BooleanOperator::And, b: Box::new(e2) }, }; NotTest: ast::Expression = { "not" => ast::Expression::Unop { a: Box::new(e), op: ast::UnaryOperator::Not }, => e, }; Comparison: ast::Expression = { => ast::Expression::Compare { a: Box::new(e1), op: op, b: Box::new(e2) }, => e, }; CompOp: ast::Comparison = { "==" => ast::Comparison::Equal, "!=" => ast::Comparison::NotEqual, "<" => ast::Comparison::Less, "<=" => ast::Comparison::LessOrEqual, ">" => ast::Comparison::Greater, ">=" => ast::Comparison::GreaterOrEqual, "in" => ast::Comparison::In, "not" "in" => ast::Comparison::NotIn, "is" => ast::Comparison::Is, "is" "not" => ast::Comparison::IsNot, }; Expression: ast::Expression = { "|" => ast::Expression::Binop { a: Box::new(e1), op: ast::Operator::BitOr, b: Box::new(e2) }, => e, }; XorExpression: ast::Expression = { "^" => ast::Expression::Binop { a: Box::new(e1), op: ast::Operator::BitXor, b: Box::new(e2) }, AndExpression, }; AndExpression: ast::Expression = { "&" => ast::Expression::Binop { a: Box::new(e1), op: ast::Operator::BitAnd, b: Box::new(e2) }, ShiftExpression, }; ShiftExpression: ast::Expression = { => ast::Expression::Binop { a: Box::new(e1), op: op, b: Box::new(e2) }, ArithmaticExpression, }; ShiftOp: ast::Operator = { "<<" => ast::Operator::LShift, ">>" => ast::Operator::RShift, }; ArithmaticExpression: ast::Expression = { => ast::Expression::Binop { a: Box::new(a), op: op, b: Box::new(b) }, Term, }; AddOp: ast::Operator = { "+" => ast::Operator::Add, "-" => ast::Operator::Sub, }; Term: ast::Expression = { => ast::Expression::Binop { a: Box::new(a), op: op, b: Box::new(b) }, Factor, }; MulOp: ast::Operator = { "*" => ast::Operator::Mult, "/" => ast::Operator::Div, "//" => ast::Operator::FloorDiv, "%" => ast::Operator::Mod, "@" => ast::Operator::MatMult, }; Factor: ast::Expression = { "+" => ast::Expression::Unop { a: Box::new(e), op: ast::UnaryOperator::Pos }, "-" => ast::Expression::Unop { a: Box::new(e), op: ast::UnaryOperator::Neg }, "~" => ast::Expression::Unop { a: Box::new(e), op: ast::UnaryOperator::Inv }, => e, }; Power: ast::Expression = { => { match e2 { None => e, Some(x) => ast::Expression::Binop { a: Box::new(e), op: ast::Operator::Pow, b: Box::new(x.1) }, } } }; AtomExpr: ast::Expression = { => e, "(" ")" => ast::Expression::Call { function: Box::new(f), args: a.0, keywords: a.1 }, "[" "]" => ast::Expression::Subscript { a: Box::new(e), b: Box::new(s) }, "." => ast::Expression::Attribute { value: Box::new(e), name: n }, }; Subscript: ast::Expression = { => e, ":" => { let s1 = e1.unwrap_or(ast::Expression::None); let s2 = e2.unwrap_or(ast::Expression::None); let s3 = e3.unwrap_or(ast::Expression::None); ast::Expression::Slice { elements: vec![s1, s2, s3] } } }; SliceOp: ast::Expression = { ":" => e.unwrap_or(ast::Expression::None) } Atom: ast::Expression = { StringConstant, => ast::Expression::Number { value: n }, => ast::Expression::Identifier { name: i }, "[" "]" => { let elements = e.unwrap_or(Vec::new()); ast::Expression::List { elements } }, "[" "]" => { // List comprehension: e }, "(" ")" => { match e { None => ast::Expression::Tuple { elements: Vec::new() }, Some(elements) => { if elements.len() == 1 && trailing_comma.is_none() { // This is "(e)", which is equivalent to "e" elements.into_iter().next().unwrap() } else { ast::Expression::Tuple { elements } } } } }, "(" ")" => { ast::Expression::Comprehension { kind: Box::new(ast::ComprehensionKind::GeneratorExpression { element: e }), generators: c, } }, "{" "}" => ast::Expression::Dict { elements: e.unwrap_or(Vec::new()) }, "{" "}" => e, "{" "}" => ast::Expression::Set { elements: e }, "{" "}" => e, "True" => ast::Expression::True, "False" => ast::Expression::False, "None" => ast::Expression::None, }; TestListComp: Vec = { > <_trailing_comma:","?> => { e }, }; TestListComp2: ast::Expression = { => { ast::Expression::Comprehension { kind: Box::new(ast::ComprehensionKind::List { element: e }), generators: c, } }, }; TestDict: Vec<(ast::Expression, ast::Expression)> = { > <_trailing_comma:","?> => { e1 } }; TestDictComp: ast::Expression = { => { ast::Expression::Comprehension { kind: Box::new(ast::ComprehensionKind::Dict { key: e1.0, value: e1.1 }), generators: c, } } }; DictEntry: (ast::Expression, ast::Expression) = { ":" => (e1, e2), }; TestSet: Vec = { > ","? => { e1 } }; TestSetComp: ast::Expression = { => { ast::Expression::Comprehension { kind: Box::new(ast::ComprehensionKind::Set { element: e1 }), generators: c, } } }; ExpressionList: ast::Expression = { => { if e.len() == 1 { e.into_iter().next().unwrap() } else { ast::Expression::Tuple { elements: e } } }, }; ExpressionList2: Vec = { ","? => { let mut l = vec![e1]; l.extend(e2.into_iter().map(|x| x.1)); l }, }; #[inline] TestList: Vec = { => { let mut l = vec![e1]; l.extend(e2.into_iter().map(|x| x.1)); l } }; // Test StarExpr: ast::Expression = { "*" => ast::Expression::Starred { value: Box::new(e) }, }; // Comprehensions: CompFor: Vec = { => c, }; SingleForComprehension: ast::Comprehension = { "for" "in" => { ast::Comprehension { target: e, iter: i, ifs: c2, } } }; ExpressionNoCond: ast::Expression = { OrTest, }; ComprehensionIf: ast::Expression = { "if" => c, }; ArgumentList: (Vec, Vec) = { > => { let mut args = vec![]; let mut keywords = vec![]; for (name, value) in e { match name { Some(n) => { keywords.push(ast::Keyword { name: n, value: value }); }, None => { if keywords.len() > 0 { panic!("positional argument follows keyword argument"); }; args.push(value); }, } } (args, keywords) } }; FunctionArgument: (Option>, ast::Expression) = { => { let expr = match c { Some(c) => ast::Expression::Comprehension { kind: Box::new(ast::ComprehensionKind::GeneratorExpression { element: e }), generators: c, }, None => e, }; (None, expr) }, "=" => (Some(Some(i.clone())), e), "*" => (None, ast::Expression::Starred { value: Box::new(e) }), "**" => (Some(None), e), }; 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 } }; Number: ast::Number = { => { ast::Number::Integer { value: s } }, => { ast::Number::Float { value: s } }, => { ast::Number::Complex { real: s.0, imag: s.1 } }, }; StringConstant: ast::Expression = { => { let glued = s.join(""); ast::Expression::String { value: glued } }, => { let glued = s.into_iter().flatten().collect::>(); ast::Expression::Bytes { value: glued } }, }; Identifier: String = => s; // Hook external lexer: extern { type Location = lexer::Location; type Error = lexer::LexicalError; enum lexer::Tok { indent => lexer::Tok::Indent, dedent => lexer::Tok::Dedent, StartProgram => lexer::Tok::StartProgram, StartStatement => lexer::Tok::StartStatement, StartExpression => lexer::Tok::StartExpression, "+" => lexer::Tok::Plus, "-" => lexer::Tok::Minus, "~" => lexer::Tok::Tilde, ":" => lexer::Tok::Colon, "." => lexer::Tok::Dot, "," => 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::EqEqual, "!=" => lexer::Tok::NotEqual, "<" => lexer::Tok::Less, "<=" => lexer::Tok::LessEqual, ">" => lexer::Tok::Greater, ">=" => lexer::Tok::GreaterEqual, "and" => lexer::Tok::And, "as" => lexer::Tok::As, "assert" => lexer::Tok::Assert, "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: }, bytes => lexer::Tok::Bytes { value: > }, name => lexer::Tok::Name { name: }, "\n" => lexer::Tok::Newline, ";" => lexer::Tok::Semi, } }