// 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 ruff_text_size::{Ranged, TextLen, TextRange, TextSize}; use ruff_python_ast::{self as ast, Int, IpyEscapeKind}; use crate::{ FStringErrorType, Mode, lexer::{LexicalError, LexicalErrorType}, function::{ArgumentList, parse_arguments, validate_pos_params, validate_arguments}, context::set_context, string::{StringType, concatenated_strings, parse_fstring_literal_element, parse_string_literal}, token::{self, StringKind}, invalid, }; use lalrpop_util::ParseError; grammar(source_code: &str, mode: Mode); // 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(crate) Top: ast::Mod = { StartModule => ast::ModModule { body, range: (start..end).into() }.into(), StartExpression ("\n")* => ast::ModExpression { body: Box::new(body.into()), range: (start..end).into() }.into() }; Program: ast::Suite = { => vec![], // Compound statements => { statements.push(next); statements }, // Small statements ";")*> ";"? "\n" => { statements.extend(small); statements.push(last); statements }, // Empty lines "\n" => s, }; Suite: ast::Suite = { ";")*> ";"? "\n" => { statements.push(last); statements }, "\n" Indent Dedent => s, }; // One or more statements Statements: Vec = { // First simple statement ";")*> ";"? "\n" => { head.push(last); head }, // The first compound statement => vec![s], // Any subsequent compound statements => { statements.push(next); statements }, // Any subsequent small statements ";")*> ";"? "\n" => { statements.extend(small); statements.push(last); statements }, }; SmallStatement: ast::Stmt = { ExpressionStatement, PassStatement, DelStatement, FlowStatement, ImportStatement, GlobalStatement, NonlocalStatement, AssertStatement, TypeAliasStatement, IpyEscapeCommandStatement, IpyHelpEndEscapeCommandStatement, }; PassStatement: ast::Stmt = { "pass" => { ast::Stmt::Pass(ast::StmtPass { range: (location..end_location).into() }) }, }; DelStatement: ast::Stmt = { "del" => { ast::Stmt::Delete( ast::StmtDelete { targets: targets.into_iter().map(|expr| set_context(expr.into(), ast::ExprContext::Del)).collect(), range: (location..end_location).into() } ) }, }; ExpressionStatement: ast::Stmt = { =>? { // Just an expression, no assignment: if suffix.is_empty() { Ok(ast::Stmt::Expr( ast::StmtExpr { value: Box::new(expression.into()), range: (location..end_location).into() } )) } else { let mut targets = vec![set_context(expression.into(), ast::ExprContext::Store)]; let mut values = suffix; let value = Box::new(values.pop().unwrap().into()); for target in values { targets.push(set_context(target.into(), ast::ExprContext::Store)); } invalid::assignment_targets(&targets)?; Ok(ast::Stmt::Assign( ast::StmtAssign { targets, value, range: (location..end_location).into() } )) } }, =>? { invalid::assignment_target(&target.expr)?; Ok(ast::Stmt::AugAssign( ast::StmtAugAssign { target: Box::new(set_context(target.into(), ast::ExprContext::Store)), op, value: Box::new(rhs.into()), range: (location..end_location).into() }, )) }, > ":" > =>? { let simple = target.expr.is_name_expr(); invalid::assignment_target(&target.expr)?; Ok(ast::Stmt::AnnAssign( ast::StmtAnnAssign { target: Box::new(set_context(target.into(), ast::ExprContext::Store)), annotation: Box::new(annotation.into()), value: rhs.map(ast::Expr::from).map(Box::new), simple, range: (location..end_location).into() }, )) }, }; AssignSuffix: crate::parser::ParenthesizedExpr = { "=" => e, "=" => e }; TestListOrYieldExpr: crate::parser::ParenthesizedExpr = { TestList, YieldExpr } #[inline] TestOrStarExprList: crate::parser::ParenthesizedExpr = { // as far as I can tell, these were the same TestList }; TestOrStarExpr: crate::parser::ParenthesizedExpr = { Test<"all">, StarExpr, }; NamedOrStarExpr: crate::parser::ParenthesizedExpr = { NamedExpression, StarExpr, }; TestOrStarNamedExpr: crate::parser::ParenthesizedExpr = { NamedExpressionTest, StarExpr, }; AugAssign: ast::Operator = { "+=" => ast::Operator::Add, "-=" => ast::Operator::Sub, "*=" => ast::Operator::Mult, "@=" => ast::Operator::MatMult, "/=" => ast::Operator::Div, "%=" => ast::Operator::Mod, "&=" => ast::Operator::BitAnd, "|=" => ast::Operator::BitOr, "^=" => ast::Operator::BitXor, "<<=" => ast::Operator::LShift, ">>=" => ast::Operator::RShift, "**=" => ast::Operator::Pow, "//=" => ast::Operator::FloorDiv, }; FlowStatement: ast::Stmt = { "break" => { ast::Stmt::Break(ast::StmtBreak { range: (location..end_location).into() }) }, "continue" => { ast::Stmt::Continue(ast::StmtContinue { range: (location..end_location).into() }) }, "return" => { ast::Stmt::Return( ast::StmtReturn { value: value.map(ast::Expr::from).map(Box::new), range: (location..end_location).into() } ) }, => { ast::Stmt::Expr( ast::StmtExpr { value: Box::new(expression.into()), range: (location..end_location).into() } ) }, RaiseStatement, }; RaiseStatement: ast::Stmt = { "raise" => { ast::Stmt::Raise( ast::StmtRaise { exc: None, cause: None, range: (location..end_location).into() } ) }, "raise" > >)?> => { ast::Stmt::Raise( ast::StmtRaise { exc: Some(Box::new(exc.into())), cause: cause.map(ast::Expr::from).map(Box::new), range: (location..end_location).into() } ) }, }; ImportStatement: ast::Stmt = { "import" >> => { ast::Stmt::Import( ast::StmtImport { names, range: (location..end_location).into() } ) }, "from" "import" => { let (level, module) = source; ast::Stmt::ImportFrom( ast::StmtImportFrom { level, module, names, range: (location..end_location).into() }, ) }, }; ImportFromLocation: (Option, Option) = { => { (Some(dots.iter().sum()), Some(name)) }, => { (Some(dots.iter().sum()), None) }, }; ImportDots: u32 = { "..." => 3, "." => 1, }; ImportAsNames: Vec = { >> => i, "(" >> ","? ")" => i, "*" => { // Star import all vec![ast::Alias { name: ast::Identifier::new("*", (location..end_location).into()), asname: None, range: (location..end_location).into() }] }, }; #[inline] ImportAsAlias: ast::Alias = { )?> => ast::Alias { name, asname: a, range: (location..end_location).into() }, } // A name like abc or abc.def.ghi DottedName: ast::Identifier = { => ast::Identifier::new(n, (location..end_location).into()), => { let mut r = String::from(n); for x in n2 { r.push('.'); r.push_str(x.1.as_str()); } ast::Identifier::new(r, (location..end_location).into()) }, }; GlobalStatement: ast::Stmt = { "global" > => { ast::Stmt::Global( ast::StmtGlobal { names, range: (location..end_location).into() } ) }, }; NonlocalStatement: ast::Stmt = { "nonlocal" > => { ast::Stmt::Nonlocal( ast::StmtNonlocal { names, range: (location..end_location).into() } ) }, }; AssertStatement: ast::Stmt = { "assert" > >)?> => { ast::Stmt::Assert( ast::StmtAssert { test: Box::new(test.into()), msg: msg.map(ast::Expr::from).map(Box::new), range: (location..end_location).into() } ) }, }; IpyEscapeCommandStatement: ast::Stmt = { =>? { if mode == Mode::Ipython { Ok(ast::Stmt::IpyEscapeCommand( ast::StmtIpyEscapeCommand { kind: c.0, value: c.1, range: (location..end_location).into() } )) } else { Err(LexicalError::new( LexicalErrorType::OtherError("IPython escape commands are only allowed in `Mode::Ipython`".to_string().into_boxed_str()), location, ))? } } } IpyEscapeCommandExpr: crate::parser::ParenthesizedExpr = { =>? { if mode == Mode::Ipython { // This should never occur as the lexer won't allow it. if !matches!(c.0, IpyEscapeKind::Magic | IpyEscapeKind::Shell) { return Err(LexicalError::new( LexicalErrorType::OtherError("IPython escape command expr is only allowed for % and !".to_string().into_boxed_str()), location, ))?; } Ok(ast::ExprIpyEscapeCommand { kind: c.0, value: c.1, range: (location..end_location).into() }.into()) } else { Err(LexicalError::new( LexicalErrorType::OtherError("IPython escape commands are only allowed in `Mode::Ipython`".to_string().into_boxed_str()), location, ))? } } } IpyHelpEndEscapeCommandStatement: ast::Stmt = { // We are permissive than the original implementation because we would allow whitespace // between the expression and the suffix while the IPython implementation doesn't allow it. // For example, `foo ?` would be valid in our case but invalid from IPython. > =>? { fn unparse_expr(expr: &ast::Expr, buffer: &mut String) -> Result<(), LexicalError> { match expr { ast::Expr::Name(ast::ExprName { id, .. }) => { buffer.push_str(id.as_str()); }, ast::Expr::Subscript(ast::ExprSubscript { value, slice, range, .. }) => { let ast::Expr::NumberLiteral(ast::ExprNumberLiteral { value: ast::Number::Int(integer), .. }) = slice.as_ref() else { return Err(LexicalError::new( LexicalErrorType::OtherError("only integer literals are allowed in Subscript expressions in help end escape command".to_string().into_boxed_str()), range.start(), )); }; unparse_expr(value, buffer)?; buffer.push('['); buffer.push_str(&format!("{}", integer)); buffer.push(']'); }, ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) => { unparse_expr(value, buffer)?; buffer.push('.'); buffer.push_str(attr.as_str()); }, _ => { return Err(LexicalError::new( LexicalErrorType::OtherError("only Name, Subscript and Attribute expressions are allowed in help end escape command".to_string().into_boxed_str()), expr.start(), )); } } Ok(()) } if mode != Mode::Ipython { return Err(ParseError::User { error: LexicalError::new( LexicalErrorType::OtherError("IPython escape commands are only allowed in `Mode::Ipython`".to_string().into_boxed_str()), location, ), }); } let kind = match suffix.len() { 1 => IpyEscapeKind::Help, 2 => IpyEscapeKind::Help2, _ => { return Err(ParseError::User { error: LexicalError::new( LexicalErrorType::OtherError("maximum of 2 `?` tokens are allowed in help end escape command".to_string().into_boxed_str()), location, ), }); } }; let mut value = String::new(); unparse_expr(&e.into(), &mut value)?; Ok(ast::Stmt::IpyEscapeCommand( ast::StmtIpyEscapeCommand { kind, value: value.into_boxed_str(), range: (location..end_location).into() } )) } } CompoundStatement: ast::Stmt = { MatchStatement, IfStatement, WhileStatement, ForStatement, TryStatement, WithStatement, FuncDef, ClassDef, }; MatchStatement: ast::Stmt = { "match" ":" "\n" Indent Dedent => { let end_location = cases .last() .unwrap() .body .last() .unwrap() .end(); ast::Stmt::Match( ast::StmtMatch { subject: Box::new(subject.into()), cases, range: (location..end_location).into() } ) }, "match" "," ":" "\n" Indent Dedent => { let end_location = cases .last() .unwrap() .body .last() .unwrap() .end(); ast::Stmt::Match( ast::StmtMatch { subject: Box::new(ast::Expr::Tuple( ast::ExprTuple { elts: vec![subject.into()], ctx: ast::ExprContext::Load, range: (tuple_location..tuple_end_location).into() }, )), cases, range: (location..end_location).into() } ) }, "match" > ","? ":" "\n" Indent Dedent => { let end_location = cases .last() .unwrap() .body .last() .unwrap() .end(); let elts = elts.into_iter().map(ast::Expr::from).collect(); ast::Stmt::Match( ast::StmtMatch { subject: Box::new(ast::Expr::Tuple( ast::ExprTuple { elts, ctx: ast::ExprContext::Load, range: (tuple_location..tuple_end_location).into() }, )), cases, range: (location..end_location).into() } ) } } MatchCase: ast::MatchCase = { "case" ":" => { // SAFETY: `body` is never empty because it is non-optional and `Suite` matches one or more statements. let end = body.last().unwrap().end(); ast::MatchCase { pattern, guard: guard.map(Box::new), body, range: (start..end).into() } }, } Guard: ast::Expr = { "if" => { guard.into() } } Patterns: ast::Pattern = { "," => ast::Pattern::MatchSequence( ast::PatternMatchSequence { patterns: vec![pattern], range: (location..end_location).into() }, ), > ","? => { ast::Pattern::MatchSequence( ast::PatternMatchSequence { patterns, range: (location..end_location).into() }, ) }, => pattern } Pattern: ast::Pattern = { => pattern, => pattern, } AsPattern: ast::Pattern = { "as" =>? { if name.as_str() == "_" { Err(LexicalError::new( LexicalErrorType::OtherError("cannot use '_' as a target".to_string().into_boxed_str()), location, ))? } else { Ok(ast::Pattern::MatchAs( ast::PatternMatchAs { pattern: Some(Box::new(pattern)), name: Some(name), range: (location..end_location).into() }, )) } }, } OrPattern: ast::Pattern = { => pattern, > => { ast::Pattern::MatchOr( ast::PatternMatchOr { patterns, range: (location..end_location).into() } ) } } ClosedPattern: ast::Pattern = { => node, => node, => node, => node, => node, => node, => node, } SequencePattern: ast::Pattern = { // A single-item tuple is a special case: it's a group pattern, _not_ a sequence pattern. "(" ")" => pattern, "(" ")" => ast::PatternMatchSequence { patterns: vec![], range: (location..end_location).into() }.into(), "(" "," ")" => { ast::PatternMatchSequence { patterns: vec![pattern], range: (location..end_location).into() }.into() }, "(" ",")+> ","? ")" => { let mut patterns = patterns; patterns.push(last); ast::PatternMatchSequence { patterns, range: (location..end_location).into() }.into() }, "[" > "]" => ast::PatternMatchSequence { patterns, range: (location..end_location).into() }.into(), } StarPattern: ast::Pattern = { "*" => ast::PatternMatchStar { name: if name.as_str() == "_" { None } else { Some(name) }, range: (location..end_location).into() }.into(), } NumberAtom: crate::parser::ParenthesizedExpr = { => ast::Expr::NumberLiteral( ast::ExprNumberLiteral { value, range: (location..end_location).into() } ).into(), } NumberExpr: crate::parser::ParenthesizedExpr = { NumberAtom, "-" => ast::Expr::UnaryOp( ast::ExprUnaryOp { op: ast::UnaryOp::USub, operand: Box::new(operand.into()), range: (location..end_location).into() } ).into(), } AddOpExpr: crate::parser::ParenthesizedExpr = { => ast::ExprBinOp { left: Box::new(left.into()), op, right: Box::new(right.into()), range: (location..end_location).into() }.into(), } LiteralPattern: ast::Pattern = { "None" => ast::PatternMatchSingleton { value: ast::Singleton::None, range: (location..end_location).into() }.into(), "True" => ast::PatternMatchSingleton { value: true.into(), range: (location..end_location).into() }.into(), "False" => ast::PatternMatchSingleton { value: false.into(), range: (location..end_location).into() }.into(), => ast::PatternMatchValue { value: Box::new(value.into()), range: (location..end_location).into() }.into(), => ast::PatternMatchValue { value: Box::new(value.into()), range: (location..end_location).into() }.into(), => ast::PatternMatchValue { value: Box::new(string.into()), range: (location..end_location).into() }.into(), > =>? Ok(ast::PatternMatchValue { value: Box::new(concatenated_strings(strings, (location..end_location).into())?), range: (location..end_location).into() }.into()), } CapturePattern: ast::Pattern = { => ast::PatternMatchAs { pattern: None, name: if name.as_str() == "_" { None } else { Some(name) }, range: (location..end_location).into() }.into(), } MatchName: ast::Expr = { => ast::Expr::Name( ast::ExprName { id: id.into(), ctx: ast::ExprContext::Load, range: (location..end_location).into() }, ), } MatchNameOrAttr: ast::Expr = { "." => ast::ExprAttribute { value: Box::new(name), attr, ctx: ast::ExprContext::Load, range: (location..end_location).into() }.into(), "." => ast::ExprAttribute { value: Box::new(e), attr, ctx: ast::ExprContext::Load, range: (location..end_location).into() }.into(), } ValuePattern: ast::Pattern = { => ast::PatternMatchValue { value: Box::new(e), range: (location..end_location).into() }.into(), } MappingKey: ast::Expr = { MatchNameOrAttr, String, => e.into(), => e.into(), "None" => ast::ExprNoneLiteral { range: (location..end_location).into() }.into(), "True" => ast::ExprBooleanLiteral { value: true, range: (location..end_location).into() }.into(), "False" => ast::ExprBooleanLiteral { value: false, range: (location..end_location).into() }.into(), } MatchMappingEntry: (ast::Expr, ast::Pattern) = { ":" => (k, v), }; MappingPattern: ast::Pattern = { "{" "}" => { ast::PatternMatchMapping { keys: vec![], patterns: vec![], rest: None, range: (location..end_location).into() }.into() }, "{" > ","? "}" => { let (keys, patterns) = e .into_iter() .unzip(); ast::PatternMatchMapping { keys, patterns, rest: None, range: (location..end_location).into() }.into() }, "{" "**" ","? "}" => { ast::PatternMatchMapping { keys: vec![], patterns: vec![], rest: Some(rest), range: (location..end_location).into() }.into() }, "{" > "," "**" ","? "}" => { let (keys, patterns) = e .into_iter() .unzip(); ast::PatternMatchMapping { keys, patterns, rest: Some(rest), range: (location..end_location).into() }.into() }, } MatchKeywordEntry: ast::PatternKeyword = { "=" => ast::PatternKeyword { attr, pattern, range: (location..end_location).into() }, }; ClassPattern: ast::Pattern = { => { ast::PatternMatchClass { cls: Box::new(cls), arguments, range: (location..end_location).into() }.into() }, => { ast::PatternMatchClass { cls: Box::new(cls), arguments, range: (location..end_location).into() }.into() }, } PatternArguments: ast::PatternArguments = { "(" > "," > ","? ")" => { ast::PatternArguments { patterns, keywords, range: (location..end_location).into() } }, "(" > ","? ")" => { ast::PatternArguments { patterns, keywords: vec![], range: (location..end_location).into() } }, "(" > ","? ")" => { ast::PatternArguments { patterns: vec![], keywords, range: (location..end_location).into() } }, "(" ")" => { ast::PatternArguments { patterns: vec![], keywords: vec![], range: (location..end_location).into() } }, }; IfStatement: ast::Stmt = { "if" ":" "elif" ":" )*> "else" ":" )?> => { let elif_else_clauses: Vec<_> = s2.into_iter().map(|(start, test, body)| ast::ElifElseClause { range: (start..body.last().unwrap().end()).into(), test: Some(test.into()), body, }).chain(s3.into_iter().map(|(start, body)| ast::ElifElseClause { range: (start..body.last().unwrap().end()).into(), test: None, body, })).collect(); let end_location = elif_else_clauses .last() .map_or_else(|| body.last().unwrap().end(), Ranged::end); ast::Stmt::If( ast::StmtIf { test: Box::new(test.into()), body, elif_else_clauses, range: (location..end_location).into() } ) }, }; WhileStatement: ast::Stmt = { "while" ":" )?> => { let orelse = s2.unwrap_or_default(); let end_location = orelse .last() .or_else(|| body.last()) .unwrap() .end(); ast::Stmt::While( ast::StmtWhile { test: Box::new(test.into()), body, orelse, range: (location..end_location).into() }, ) }, }; ForStatement: ast::Stmt = { "for" "in" ":" )?> => { let orelse = orelse.unwrap_or_default(); let end_location = orelse .last() .or_else(|| body.last()) .unwrap() .end(); let target = Box::new(set_context(target.into(), ast::ExprContext::Store)); let iter = Box::new(iter.into()); ast::Stmt::For(ast::StmtFor { target, iter, body, orelse, is_async: is_async.is_some(), range: (location..end_location).into() }) }, }; TryStatement: ast::Stmt = { "try" ":" )?> )?> => { let orelse = orelse.unwrap_or_default(); let finalbody = finalbody.unwrap_or_default(); let end_location = finalbody .last() .map(Ranged::end) .or_else(|| orelse.last().map(Ranged::end)) .or_else(|| handlers.last().map(Ranged::end)) .unwrap(); ast::Stmt::Try( ast::StmtTry { body, handlers, orelse, finalbody, is_star: false, range: (location..end_location).into() }, ) }, "try" ":" )?> )?> => { let orelse = orelse.unwrap_or_default(); let finalbody = finalbody.unwrap_or_default(); let end_location = finalbody .last() .or_else(|| orelse.last()) .map(Ranged::end) .or_else(|| handlers.last().map(Ranged::end)) .unwrap(); ast::Stmt::Try( ast::StmtTry { body, handlers, orelse, finalbody, is_star: true, range: (location..end_location).into() }, ) }, "try" ":" )> => { let handlers = vec![]; let orelse = vec![]; let end_location = finalbody.last().unwrap().end(); ast::Stmt::Try( ast::StmtTry { body, handlers, orelse, finalbody, is_star: false, range: (location..end_location).into() }, ) }, }; ExceptStarClause: ast::ExceptHandler = { "except" "*" > ":" => { let end_location = body.last().unwrap().end(); ast::ExceptHandler::ExceptHandler( ast::ExceptHandlerExceptHandler { type_: Some(Box::new(typ.into())), name: None, body, range: (location..end_location).into() }, ) }, "except" "*" > "as" )> ":" => { let end_location = body.last().unwrap().end(); ast::ExceptHandler::ExceptHandler( ast::ExceptHandlerExceptHandler { type_: Some(Box::new(x.0.into())), name: Some(x.1), body, range: (location..end_location).into() }, ) }, }; ExceptClause: ast::ExceptHandler = { "except" ?> ":" => { let end_location = body.last().unwrap().end(); ast::ExceptHandler::ExceptHandler( ast::ExceptHandlerExceptHandler { type_: typ.map(ast::Expr::from).map(Box::new), name: None, body, range: (location..end_location).into() }, ) }, "except" > "as" )> ":" => { let end_location = body.last().unwrap().end(); ast::ExceptHandler::ExceptHandler( ast::ExceptHandlerExceptHandler { type_: Some(Box::new(x.0.into())), name: Some(x.1), body, range: (location..end_location).into() }, ) }, }; WithStatement: ast::Stmt = { "with" ":" => { let end_location = body.last().unwrap().end(); ast::StmtWith { items, body, is_async: is_async.is_some(), range: (location..end_location).into() }.into() }, }; WithItems: Vec = { "(" ","? ")", "(" ",")?> >)*> ","? ")" => { left.into_iter().flatten().chain([mid]).chain(right).collect() }, > => { // Special-case: if the `WithItem` is a parenthesized named expression, then the item // should _exclude_ the outer parentheses in its range. For example: // ```python // with (a := 0): pass // ``` // In this case, the `(` and `)` are part of the `with` statement. // The same applies to `yield` and `yield from`. let item = if item.optional_vars.is_none() && matches!(item.context_expr, ast::Expr::NamedExpr(_) | ast::Expr::Yield(_) | ast::Expr::YieldFrom(_)) { ast::WithItem { range: item.range().add_start(TextSize::new(1)).sub_end(TextSize::new(1)), context_expr: item.context_expr, optional_vars: item.optional_vars, } } else { item }; vec![item] }, > >)+> => { [item].into_iter().chain(items).collect() } }; #[inline] WithItemsNoAs: Vec = { >> => { all.into_iter().map(|context_expr| ast::WithItem { range: context_expr.range(), context_expr: context_expr.into(), optional_vars: None, }).collect() }, } WithItem: ast::WithItem = { > => { ast::WithItem { range: context_expr.range(), context_expr: context_expr.into(), optional_vars: None, } }, , }; WithItemAs: ast::WithItem = { > "as" > => { let optional_vars = Some(Box::new(set_context(optional_vars.into(), ast::ExprContext::Store))); ast::WithItem { context_expr: context_expr.into(), optional_vars, range: (location..end_location).into(), } }, } FuncDef: ast::Stmt = { "def" " >)?> ":" => { let parameters = Box::new(parameters); let returns = returns.map(ast::Expr::from).map(Box::new); let end_location = body.last().unwrap().end(); ast::StmtFunctionDef { name, parameters, body, decorator_list, returns, type_params, is_async: is_async.is_some(), range: (location..end_location).into(), }.into() }, }; TypeAliasName: ast::Expr = { => ast::ExprName { id: name.into(), ctx: ast::ExprContext::Store, range: (location..end_location).into(), }.into(), } TypeAliasStatement: ast::Stmt = { "type" "=" > => { ast::Stmt::TypeAlias( ast::StmtTypeAlias { name: Box::new(name), value: Box::new(value.into()), type_params, range: (location..end_location).into() }, ) }, }; Parameters: ast::Parameters = { "(" )?> ")" =>? { a.as_ref().map(validate_arguments).transpose()?; let range = (location..end_location).into(); let args = a .map_or_else(|| ast::Parameters::empty(range), |mut arguments| { arguments.range = range; arguments }); Ok(args) } }; // Note that this is a macro which is used once for function defs, and // once for lambda defs. ParameterList: ast::Parameters = { > >)?> ","? =>? { validate_pos_params(¶m1)?; let (posonlyargs, args) = param1; // Now gather rest of parameters: let (vararg, kwonlyargs, kwarg) = args2.unwrap_or((None, vec![], None)); Ok(ast::Parameters { posonlyargs, args, kwonlyargs, vararg, kwarg, range: (location..end_location).into() }) }, > >)> ","? =>? { validate_pos_params(¶m1)?; let (posonlyargs, args) = param1; // Now gather rest of parameters: let vararg = None; let kwonlyargs = vec![]; let kwarg = kw; Ok(ast::Parameters { posonlyargs, args, kwonlyargs, vararg, kwarg, range: (location..end_location).into() }) }, > ","? => { let (vararg, kwonlyargs, kwarg) = params; ast::Parameters { posonlyargs: vec![], args: vec![], kwonlyargs, vararg, kwarg, range: (location..end_location).into() } }, > ","? => { ast::Parameters { posonlyargs: vec![], args: vec![], kwonlyargs: vec![], vararg: None, kwarg, range: (location..end_location).into() } }, }; // Use inline here to make sure the "," is not creating an ambiguity. #[inline] ParameterDefs: (Vec, Vec) = { >> => { (vec![], args) }, >> "," "/" >)*> => { (posonlyargs, args) }, }; ParameterDef: ast::ParameterWithDefault = { => i, "=" > => { i.default = Some(Box::new(default.into())); i.range = (i.range.start()..end_location).into(); i }, }; UntypedParameter: ast::ParameterWithDefault = { => { let parameter = ast::Parameter { name, annotation: None, range: (location..end_location).into() }; ast::ParameterWithDefault { parameter, default: None, range: (location..end_location).into() } }, }; StarUntypedParameter: ast::Parameter = { => ast::Parameter { name:arg, annotation: None, range: (location..end_location).into() }, }; TypedParameter: ast::ParameterWithDefault = { >)?> => { let annotation = annotation.map(ast::Expr::from).map(Box::new); let parameter = ast::Parameter { name, annotation, range: (location..end_location).into() }; ast::ParameterWithDefault { parameter, default: None, range: (location..end_location).into() } }, }; StarTypedParameter: ast::Parameter = { )?> => { let annotation = annotation.map(ast::Expr::from).map(Box::new); ast::Parameter { name, annotation, range: (location..end_location).into() } }, }; DoubleStarTypedParameter: ast::Parameter = { >)?> => { let annotation = annotation.map(ast::Expr::from).map(Box::new); ast::Parameter { name, annotation, range: (location..end_location).into() } }, }; // 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, Option>) = { "*" >)*> >)?> =>? { if va.is_none() && kwonlyargs.is_empty() && kwarg.is_none() { return Err(LexicalError::new( LexicalErrorType::OtherError("named arguments must follow bare *".to_string().into_boxed_str()), location, ))?; } let kwarg = kwarg.flatten(); let va = va.map(Box::new); Ok((va, kwonlyargs, kwarg)) } }; KwargParameter: Option> = { "**" => { kwarg.map(Box::new) } }; ClassDef: ast::Stmt = { "class" ":" => { let end_location = body.last().unwrap().end(); ast::Stmt::ClassDef( ast::StmtClassDef { name, arguments: arguments.map(Box::new), body, decorator_list, type_params: type_params.map(Box::new), range: (location..end_location).into() }, ) }, }; TypeParams: ast::TypeParams = { "[" > ","? "]" => { ast::TypeParams { type_params: vars, range: (location..end_location).into() } } }; TypeParam: ast::TypeParam = { >)?> => { ast::TypeParam::TypeVar( ast::TypeParamTypeVar { name, bound: bound.map(ast::Expr::from).map(Box::new), range: (location..end_location).into() } ) }, "*" => { ast::TypeParam::TypeVarTuple( ast::TypeParamTypeVarTuple { name, range: (location..end_location).into() } ) }, "**" => { ast::TypeParam::ParamSpec( ast::TypeParamParamSpec { name, range: (location..end_location).into() } ) } }; // Decorators: Decorator: ast::Decorator = { "@" "\n" => { ast::Decorator { range: (location..end_location).into(), expression: expression.into() } }, }; YieldExpr: crate::parser::ParenthesizedExpr = { "yield" => ast::ExprYield { value: value.map(ast::Expr::from).map(Box::new), range: (location..end_location).into(), }.into(), "yield" "from" > => ast::ExprYieldFrom { value: Box::new(value.into()), range: (location..end_location).into(), }.into(), }; Test: crate::parser::ParenthesizedExpr = { > "if" > "else" > => ast::ExprIfExp { test: Box::new(test.into()), body: Box::new(body.into()), orelse: Box::new(orelse.into()), range: (location..end_location).into() }.into(), OrTest, LambdaDef, }; NamedExpressionTest: crate::parser::ParenthesizedExpr = { NamedExpression, Test<"all">, } NamedExpressionName: crate::parser::ParenthesizedExpr = { => ast::ExprName { id: id.into(), ctx: ast::ExprContext::Store, range: (location..end_location).into(), }.into(), } NamedExpression: crate::parser::ParenthesizedExpr = { ":=" > => { ast::ExprNamedExpr { target: Box::new(target.into()), value: Box::new(value.into()), range: (location..end_location).into(), }.into() }, }; LambdaDef: crate::parser::ParenthesizedExpr = { "lambda" ?> ":" > =>? { if fstring_middle.is_some() { return Err(LexicalError::new( LexicalErrorType::FStringError(FStringErrorType::LambdaWithoutParentheses), location, ))?; } parameters.as_ref().map(validate_arguments).transpose()?; Ok(ast::ExprLambda { parameters: parameters.map(Box::new), body: Box::new(body.into()), range: (location..end_location).into() }.into()) } } OrTest: crate::parser::ParenthesizedExpr = { > "or")+> > => { let values = values.into_iter().chain(std::iter::once(last)).map(ast::Expr::from).collect(); ast::ExprBoolOp { op: ast::BoolOp::Or, values, range: (location..end_location).into() }.into() }, AndTest, }; AndTest: crate::parser::ParenthesizedExpr = { > "and")+> > => { let values = values.into_iter().chain(std::iter::once(last)).map(ast::Expr::from).collect(); ast::ExprBoolOp { op: ast::BoolOp::And, values, range: (location..end_location).into() }.into() }, NotTest, }; NotTest: crate::parser::ParenthesizedExpr = { "not" > => ast::ExprUnaryOp { operand: Box::new(operand.into()), op: ast::UnaryOp::Not, range: (location..end_location).into(), }.into(), Comparison, }; Comparison: crate::parser::ParenthesizedExpr = { > )+> => { let mut ops = Vec::with_capacity(comparisons.len()); let mut comparators = Vec::with_capacity(comparisons.len()); for (op, comparator) in comparisons { ops.push(op); comparators.push(comparator.into()); } ast::ExprCompare { left: Box::new(left.into()), ops: ops.into_boxed_slice(), comparators: comparators.into_boxed_slice(), range: (location..end_location).into(), }.into() }, 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: crate::parser::ParenthesizedExpr = { > "|" > => ast::ExprBinOp { left: Box::new(left.into()), op: ast::Operator::BitOr, right: Box::new(right.into()), range: (location..end_location).into() }.into(), XorExpression, }; XorExpression: crate::parser::ParenthesizedExpr = { > "^" > => ast::ExprBinOp { left: Box::new(left.into()), op: ast::Operator::BitXor, right: Box::new(right.into()), range: (location..end_location).into() }.into(), AndExpression, }; AndExpression: crate::parser::ParenthesizedExpr = { > "&" > => ast::ExprBinOp { left: Box::new(left.into()), op: ast::Operator::BitAnd, right: Box::new(right.into()), range: (location..end_location).into() }.into(), ShiftExpression, }; ShiftExpression: crate::parser::ParenthesizedExpr = { > > => ast::ExprBinOp { left: Box::new(left.into()), op, right: Box::new(right.into()), range: (location..end_location).into() }.into(), ArithmeticExpression, }; ShiftOp: ast::Operator = { "<<" => ast::Operator::LShift, ">>" => ast::Operator::RShift, }; ArithmeticExpression: crate::parser::ParenthesizedExpr = { > > => ast::ExprBinOp { left: Box::new(left.into()), op, right: Box::new(right.into()), range: (location..end_location).into(), }.into(), Term, }; AddOp: ast::Operator = { "+" => ast::Operator::Add, "-" => ast::Operator::Sub, }; Term: crate::parser::ParenthesizedExpr = { > > => ast::ExprBinOp { left: Box::new(left.into()), op, right: Box::new(right.into()), range: (location..end_location).into(), }.into(), Factor, }; MulOp: ast::Operator = { "*" => ast::Operator::Mult, "/" => ast::Operator::Div, "//" => ast::Operator::FloorDiv, "%" => ast::Operator::Mod, "@" => ast::Operator::MatMult, }; Factor: crate::parser::ParenthesizedExpr = { > => ast::ExprUnaryOp { operand: Box::new(operand.into()), op, range: (location..end_location).into(), }.into(), Power, }; UnaryOp: ast::UnaryOp = { "+" => ast::UnaryOp::UAdd, "-" => ast::UnaryOp::USub, "~" => ast::UnaryOp::Invert, }; Power: crate::parser::ParenthesizedExpr = { > "**" > => ast::ExprBinOp { left: Box::new(left.into()), op: ast::Operator::Pow, right: Box::new(right.into()), range: (location..end_location).into(), }.into(), AtomExpr, }; AtomExpr: crate::parser::ParenthesizedExpr = { "await" > => { ast::ExprAwait { value: Box::new(value.into()), range: (location..end_location).into() }.into() }, AtomExpr2, } AtomExpr2: crate::parser::ParenthesizedExpr = { Atom, > => ast::ExprCall { func: Box::new(func.into()), arguments, range: (location..end_location).into(), }.into(), > "[" "]" => ast::ExprSubscript { value: Box::new(value.into()), slice: Box::new(slice.into()), ctx: ast::ExprContext::Load, range: (location..end_location).into(), }.into(), > "." => ast::ExprAttribute { value: Box::new(value.into()), attr, ctx: ast::ExprContext::Load, range: (location..end_location).into(), }.into(), }; SubscriptList: crate::parser::ParenthesizedExpr = { Subscript, "," => { ast::ExprTuple { elts: vec![s1.into()], ctx: ast::ExprContext::Load, range: (location..end_location).into(), }.into() }, > ","? => { let elts = elts.into_iter().map(ast::Expr::from).collect(); ast::ExprTuple { elts, ctx: ast::ExprContext::Load, range: (location..end_location).into(), }.into() } }; Subscript: crate::parser::ParenthesizedExpr = { TestOrStarNamedExpr, ?> ":" ?> => { let lower = lower.map(ast::Expr::from).map(Box::new); let upper = upper.map(ast::Expr::from).map(Box::new); let step = step.flatten().map(ast::Expr::from).map(Box::new); ast::Expr::Slice( ast::ExprSlice { lower, upper, step, range: (location..end_location).into() } ).into() } }; SliceOp: Option = { ":" ?> => e, } String: ast::Expr = { => string.into(), > =>? { Ok(concatenated_strings(strings, (location..end_location).into())?) } }; StringLiteralOrFString: StringType = { StringLiteral, FStringExpr, }; StringLiteral: StringType = { =>? { let (source, kind, triple_quoted) = string; Ok(parse_string_literal(source, kind, triple_quoted, (location..end_location).into())?) } }; FStringExpr: StringType = { FStringStart FStringEnd => { StringType::FString(ast::FString { elements, range: (location..end_location).into() }) } }; FStringMiddlePattern: ast::FStringElement = { FStringReplacementField, =>? { let (source, is_raw, _) = fstring_middle; Ok(parse_fstring_literal_element(source, is_raw, (location..end_location).into())?) } }; FStringReplacementField: ast::FStringElement = { "{" "}" =>? { if value.expr.is_lambda_expr() && !value.is_parenthesized() { return Err(LexicalError::new( LexicalErrorType::FStringError(FStringErrorType::LambdaWithoutParentheses), value.start(), ))?; } let debug_text = debug.map(|_| { let start_offset = location + "{".text_len(); let end_offset = if let Some((conversion_start, _)) = conversion { conversion_start } else { format_spec.as_ref().map_or_else( || end_location - "}".text_len(), |spec| spec.start() - ":".text_len(), ) }; ast::DebugText { leading: source_code[TextRange::new(start_offset, value.expr.start())].to_string(), trailing: source_code[TextRange::new(value.expr.end(), end_offset)].to_string(), } }); Ok( ast::FStringElement::Expression(ast::FStringExpressionElement { expression: Box::new(value.into()), debug_text, conversion: conversion.map_or(ast::ConversionFlag::None, |(_, conversion_flag)| { conversion_flag }), format_spec: format_spec.map(Box::new), range: (location..end_location).into(), }) ) } }; FStringFormatSpecSuffix: ast::FStringFormatSpec = { ":" => format_spec }; FStringFormatSpec: ast::FStringFormatSpec = { => ast::FStringFormatSpec { elements, range: (location..end_location).into(), }, }; FStringConversion: (TextSize, ast::ConversionFlag) = { "!" =>? { let conversion = match s.as_ref() { "s" => ast::ConversionFlag::Str, "r" => ast::ConversionFlag::Repr, "a" => ast::ConversionFlag::Ascii, _ => Err(LexicalError::new( LexicalErrorType::FStringError(FStringErrorType::InvalidConversionFlag), name_location, ))? }; Ok((location, conversion)) } }; Atom: crate::parser::ParenthesizedExpr = { => expr.into(), => ast::ExprNumberLiteral { value, range: (location..end_location).into(), }.into(), => ast::ExprName { id: id.into(), ctx: ast::ExprContext::Load, range: (location..end_location).into(), }.into(), "[" "]" => { let elts = elts.into_iter().flatten().map(ast::Expr::from).collect(); ast::ExprList { elts, ctx: ast::ExprContext::Load, range: (location..end_location).into() }.into() }, "[" "]" => { ast::ExprListComp { elt: Box::new(elt.into()), generators, range: (location..end_location).into() }.into() }, "(" >> ")" if Goal != "no-withitems" => { if elts.len() == 1 && trailing_comma.is_none() { crate::parser::ParenthesizedExpr { expr: elts.into_iter().next().unwrap().into(), range: (location..end_location).into(), } } else { let elts = elts.into_iter().map(ast::Expr::from).collect(); ast::ExprTuple { elts, ctx: ast::ExprContext::Load, range: (location..end_location).into() }.into() } }, "(" >> ",")?> )*> ")" =>? { if left.is_none() && right.is_empty() && trailing_comma.is_none() { if mid.expr.is_starred_expr() { return Err(LexicalError::new( LexicalErrorType::OtherError("cannot use starred expression here".to_string().into_boxed_str()), mid.start(), ))?; } Ok(crate::parser::ParenthesizedExpr { expr: mid.into(), range: (location..end_location).into(), }) } else { let elts = left.into_iter().flatten().chain([mid]).chain(right).map(ast::Expr::from).collect(); Ok(ast::ExprTuple { elts, ctx: ast::ExprContext::Load, range: (location..end_location).into() }.into()) } }, "(" ")" => ast::ExprTuple { elts: Vec::new(), ctx: ast::ExprContext::Load, range: (location..end_location).into(), }.into(), "(" ")" => crate::parser::ParenthesizedExpr { expr: e.into(), range: (location..end_location).into(), }, "(" ")" => ast::ExprGeneratorExp { elt: Box::new(elt.into()), generators, range: (location..end_location).into(), }.into(), "(" "**" > ")" =>? { Err(LexicalError::new( LexicalErrorType::OtherError("cannot use double starred expression here".to_string().into_boxed_str()), location, ).into()) }, "{" "}" => { let (keys, values) = e .unwrap_or_default() .into_iter() .map(|(k, v)| (k.map(|x| ast::Expr::from(*x)), ast::Expr::from(v))) .unzip(); ast::ExprDict { keys, values, range: (location..end_location).into() }.into() }, "{" "}" => { ast::ExprDictComp { key: Box::new(e1.0.into()), value: Box::new(e1.1.into()), generators, range: (location..end_location).into() }.into() }, "{" "}" => { let elts = elts.into_iter().map(ast::Expr::from).collect(); ast::ExprSet { elts, range: (location..end_location).into(), }.into() }, "{" "}" => ast::ExprSetComp { elt: Box::new(elt.into()), generators, range: (location..end_location).into(), }.into(), "True" => ast::ExprBooleanLiteral { value: true, range: (location..end_location).into() }.into(), "False" => ast::ExprBooleanLiteral { value: false, range: (location..end_location).into() }.into(), "None" => ast::ExprNoneLiteral { range: (location..end_location).into() }.into(), "..." => ast::ExprEllipsisLiteral { range: (location..end_location).into() }.into(), }; ListLiteralValues: Vec = { > ","? => e, }; DictLiteralValues: Vec<(Option>, crate::parser::ParenthesizedExpr)> = { > ","? => elements, }; DictEntry: (crate::parser::ParenthesizedExpr, crate::parser::ParenthesizedExpr) = { > ":" > => (e1, e2), }; DictElement: (Option>, crate::parser::ParenthesizedExpr) = { => (Some(Box::new(e.0)), e.1), "**" > => (None, e), }; SetLiteralValues: Vec = { > ","? => e1 }; ExpressionOrStarExpression: crate::parser::ParenthesizedExpr = { Expression<"all">, StarExpr }; ExpressionList: crate::parser::ParenthesizedExpr = { 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: crate::parser::ParenthesizedExpr = { GenericList }; GenericList: crate::parser::ParenthesizedExpr = { > => { if elts.len() == 1 && trailing_comma.is_none() { crate::parser::ParenthesizedExpr { expr: elts.into_iter().next().unwrap().into(), range: (location..end_location).into(), } } else { let elts = elts.into_iter().map(ast::Expr::from).collect(); ast::ExprTuple { elts, ctx: ast::ExprContext::Load, range: (location..end_location).into() }.into() } } } // Test StarExpr: crate::parser::ParenthesizedExpr = { "*" > => ast::ExprStarred { value: Box::new(value.into()), ctx: ast::ExprContext::Load, range: (location..end_location).into(), }.into(), }; // Comprehensions: CompFor: Vec = => c; SingleForComprehension: ast::Comprehension = { "for" "in" > => { let is_async = is_async.is_some(); let ifs = ifs.into_iter().map(ast::Expr::from).collect(); ast::Comprehension { target: set_context(target.into(), ast::ExprContext::Store), iter: iter.into(), ifs, is_async, range: (location..end_location).into() } } }; ExpressionNoCond: crate::parser::ParenthesizedExpr = OrTest<"all">; ComprehensionIf: crate::parser::ParenthesizedExpr = "if" => c; Arguments: ast::Arguments = { "(" > ")" =>? { let ArgumentList { args, keywords } = parse_arguments(e)?; Ok(ast::Arguments { args: args.into_boxed_slice(), keywords: keywords.into_boxed_slice(), range: (location..end_location).into() }) } }; FunctionArgument: (Option<(TextSize, TextSize, Option)>, ast::Expr) = { => { let expr = match generators { Some(generators) => ast::Expr::GeneratorExp( ast::ExprGeneratorExp { elt: Box::new(elt.into()), generators, range: (location..end_location).into() } ), None => elt.into(), }; (None, expr) }, "=" > => (Some((location, end_location, Some(i))), e.into()), "*" > => { let expr = ast::Expr::Starred(ast::ExprStarred { value: Box::new(value.into()), ctx: ast::ExprContext::Load, range: (location..end_location).into(), }); (None, expr) }, "**" > => (Some((location, end_location, None)), e.into()), }; /// Comma separated sequence that allows an optional trailing comma. #[inline] Comma: Vec = { ",")*> => { if let Some(element) = last { v.push(element); } v } }; /// One ore more items that are separated by a comma. OneOrMore: Vec = { => vec![e], > "," => { v.push(e); v } }; /// Two or more items that are separated by `Sep` TwoOrMoreSep: Vec = { Sep => vec![e1, e2], > Sep => { v.push(e); v } }; /// Two or more items that are contiguous. TwoOrMore: Vec = { => vec![e1, e2], > => { v.push(e); v } }; Number: ast::Number = { => ast::Number::Int(value), => ast::Number::Float(value), => ast::Number::Complex { real: s.0, imag: s.1 }, }; Identifier: ast::Identifier = { => ast::Identifier::new(s, (location..end_location).into()) }; // Hook external lexer: extern { type Location = TextSize; type Error = LexicalError; enum token::Tok { Indent => token::Tok::Indent, Dedent => token::Tok::Dedent, StartModule => token::Tok::StartModule, StartExpression => token::Tok::StartExpression, FStringStart => token::Tok::FStringStart, FStringEnd => token::Tok::FStringEnd, "!" => token::Tok::Exclamation, "?" => token::Tok::Question, "+" => 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, "type" => token::Tok::Type, "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: }, fstring_middle => token::Tok::FStringMiddle { value: >, is_raw: , triple_quoted: }, name => token::Tok::Name { name: > }, ipy_escape_command => token::Tok::IpyEscapeCommand { kind: , value: > }, "\n" => token::Tok::Newline, ";" => token::Tok::Semi, // "#" => token::Tok::Comment(_), } }