// 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; use crate::fstring::parse_located_fstring; use crate::function::{parse_args, parse_params}; use crate::error::LexicalError; use crate::lexer; use alloc::{boxed::Box, string::{String, ToString}, vec, vec::Vec}; 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 ("\n")* => ast::Top::Expression(e), }; Program: ast::Program = { => ast::Program { statements: 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::Statement = { ExpressionStatement, PassStatement, DelStatement, FlowStatement, ImportStatement, GlobalStatement, NonlocalStatement, AssertStatement, }; PassStatement: ast::Statement = { "pass" => { ast::Statement { location, custom: (), node: ast::StatementType::Pass, } }, }; DelStatement: ast::Statement = { "del" => { ast::Statement { location, custom: (), node: ast::StatementType::Delete { targets }, } }, }; ExpressionStatement: ast::Statement = { => { // Just an expression, no assignment: if suffix.is_empty() { ast::Statement { custom: (), location, node: ast::StatementType::Expression { expression } } } else { let mut targets = vec![expression]; let mut values = suffix; while values.len() > 1 { targets.push(values.remove(0)); } let value = values.into_iter().next().unwrap(); ast::Statement { custom: (), location, node: ast::StatementType::Assign { targets, value }, } } }, => { ast::Statement { custom: (), location, node: ast::StatementType::AugAssign { target: Box::new(target), op, value: Box::new(rhs) }, } }, ":" => { ast::Statement { custom: (), location, node: ast::StatementType::AnnAssign { target: Box::new(target), annotation: Box::new(annotation), value: rhs }, } }, }; AssignSuffix: ast::Expression = { "=" => e }; TestListOrYieldExpr: ast::Expression = { TestList, YieldExpr } TestOrStarExprList: ast::Expression = { > => { if elements.len() == 1 && comma.is_none() { elements.into_iter().next().unwrap() } else { ast::Expression { location, custom: (), node: ast::ExpressionType::Tuple { elements } } } } }; TestOrStarNamedExprList: ast::Expression = { > => { if elements.len() == 1 && comma.is_none() { elements.into_iter().next().unwrap() } else { ast::Expression { location, custom: (), node: ast::ExpressionType::Tuple { elements } } } } }; TestOrStarExpr: ast::Expression = { Test, StarExpr, }; TestOrStarNamedExpr: ast::Expression = { 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::Statement = { "break" => { ast::Statement { custom: (), location, node: ast::StatementType::Break, } }, "continue" => { ast::Statement { custom: (), location, node: ast::StatementType::Continue, } }, "return" => { ast::Statement { custom: (), location, node: ast::StatementType::Return { value }, } }, => { ast::Statement { custom: (), location, node: ast::StatementType::Expression { expression }, } }, RaiseStatement, }; RaiseStatement: ast::Statement = { "raise" => { ast::Statement { custom: (), location, node: ast::StatementType::Raise { exception: None, cause: None }, } }, "raise" => { ast::Statement { custom: (), location, node: ast::StatementType::Raise { exception: Some(t), cause: c.map(|x| x.1) }, } }, }; ImportStatement: ast::Statement = { "import" >> => { ast::Statement { custom: (), location, node: ast::StatementType::Import { names }, } }, "from" "import" => { let (level, module) = source; ast::Statement { custom: (), location, node: ast::StatementType::ImportFrom { level, module, names }, } }, }; ImportFromLocation: (usize, Option) = { => { (dots.iter().sum(), Some(name)) }, => { (dots.iter().sum(), None) }, }; ImportDots: usize = { "..." => 3, "." => 1, }; ImportAsNames: Vec = { >> => i, "(" >> ","? ")" => i, "*" => { // Star import all vec![ast::ImportSymbol { symbol: "*".to_string(), alias: None }] }, }; #[inline] ImportAsAlias: ast::ImportSymbol = { => ast::ImportSymbol { symbol, alias: 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::Statement = { "global" > => { ast::Statement { custom: (), location, node: ast::StatementType::Global { names } } }, }; NonlocalStatement: ast::Statement = { "nonlocal" > => { ast::Statement { custom: (), location, node: ast::StatementType::Nonlocal { names } } }, }; AssertStatement: ast::Statement = { "assert" => { ast::Statement { custom: (), location, node: ast::StatementType::Assert { test, msg: msg.map(|e| e.1) } } }, }; CompoundStatement: ast::Statement = { IfStatement, WhileStatement, ForStatement, TryStatement, WithStatement, FuncDef, ClassDef, }; IfStatement: ast::Statement = { "if" ":" => { // Determine last else: let mut last = s3.map(|s| s.2); // handle elif: for i in s2.into_iter().rev() { let x = ast::Statement { custom: (), location: i.0, node: ast::StatementType::If { test: i.2, body: i.4, orelse: last }, }; last = Some(vec![x]); } ast::Statement { custom: (), location, node: ast::StatementType::If { test, body, orelse: last } } }, }; WhileStatement: ast::Statement = { "while" ":" => { let orelse = s2.map(|s| s.2); ast::Statement { custom: (), location, node: ast::StatementType::While { test, body, orelse }, } }, }; ForStatement: ast::Statement = { "for" "in" ":" => { let is_async = is_async.is_some(); let orelse = s2.map(|s| s.2); ast::Statement { custom: (), location, node: ast::StatementType::For { is_async, target: Box::new(target), iter: Box::new(iter), body, orelse }, } }, }; TryStatement: ast::Statement = { "try" ":" => { let orelse = else_suite.map(|s| s.2); let finalbody = finally.map(|s| s.2); ast::Statement { custom: (), location, node: ast::StatementType::Try { body, handlers, orelse, finalbody, }, } }, "try" ":" => { let handlers = vec![]; let orelse = None; let finalbody = Some(finally.2); ast::Statement { custom: (), location, node: ast::StatementType::Try { body, handlers, orelse, finalbody, }, } }, }; ExceptClause: ast::ExceptHandler = { "except" ":" => { ast::ExceptHandler { location, typ, name: None, body, } }, "except" ":" => { ast::ExceptHandler { location, typ: Some(x.0), name: Some(x.2), body, } }, }; WithStatement: ast::Statement = { "with" > ":" => { let is_async = is_async.is_some(); ast::Statement { custom: (), location, node: ast::StatementType::With { is_async, items, body }, } }, }; WithItem: ast::WithItem = { => { let optional_vars = n.map(|val| val.1); ast::WithItem { context_expr, optional_vars } }, }; FuncDef: ast::Statement = { "def" " Test)?> ":" => { let is_async = is_async.is_some(); ast::Statement { custom: (), location, node: ast::StatementType::FunctionDef { is_async, name, args: Box::new(args), body, decorator_list, returns: r.map(|x| x.1), } } }, }; Parameters: ast::Parameters = { "(" )?> ")" => a.unwrap_or_default(), }; // Note that this is a macro which is used once for function defs, and // once for lambda defs. ParameterList: ast::Parameters = { > )?> ","? =>? { let posonlyargs_count = param1.0.len(); let (names, 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::Parameters { posonlyargs_count, args: names, kwonlyargs, vararg: vararg.into(), kwarg: kwarg.into(), defaults, kw_defaults, }) }, > )> ","? =>? { let posonlyargs_count = param1.0.len(); let (names, defaults) = parse_params(param1)?; // Now gather rest of parameters: let vararg = None; let kwonlyargs = vec![]; let kw_defaults = vec![]; let kwarg = Some(kw.1); Ok(ast::Parameters { posonlyargs_count, args: names, kwonlyargs, vararg: vararg.into(), kwarg: kwarg.into(), defaults, kw_defaults, }) }, > ","? => { let (vararg, kwonlyargs, kw_defaults, kwarg) = params; ast::Parameters { posonlyargs_count: 0, args: vec![], kwonlyargs, vararg: vararg.into(), kwarg: kwarg.into(), defaults: vec![], kw_defaults, } }, > ","? => { ast::Parameters { posonlyargs_count: 0, args: vec![], kwonlyargs: vec![], vararg: ast::Varargs::None, kwarg: Some(kw).into(), defaults: vec![], kw_defaults: vec![], } }, }; // Use inline here to make sure the "," is not creating an ambiguity. #[inline] ParameterDefs: (Vec<(ast::Parameter, Option)>, Vec<(ast::Parameter, Option)>) = { >> => { (vec![], args) }, >> "," "/" )*> => { (pos_args, args.into_iter().map(|e| e.1).collect()) }, }; ParameterDef: (ast::Parameter, Option) = { => (i, None), "=" => (i, Some(e)), }; UntypedParameter: ast::Parameter = { => ast::Parameter { location, arg, annotation: None }, }; TypedParameter: ast::Parameter = { => { let annotation = a.map(|x| Box::new(x.1)); ast::Parameter { location, arg, annotation } }, }; // 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![]; 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 = kwarg.map(|n| n.1); (Some(va), kwonlyargs, kw_defaults, kwarg) } }; KwargParameter: Option = { "**" => { kwarg } }; ClassDef: ast::Statement = { "class" ":" => { let (bases, keywords) = match a { Some((_, arg, _)) => (arg.args, arg.keywords), None => (vec![], vec![]), }; ast::Statement { custom: (), location, node: ast::StatementType::ClassDef { name, bases, keywords, body, decorator_list, }, } }, }; Path: ast::Expression = { => ast::Expression { location, custom: (), node: ast::ExpressionType::Identifier { name: n } }, "." => { ast::Expression { location, custom: (), node: ast::ExpressionType::Attribute { value: Box::new(p), name: n, } } }, }; // Decorators: Decorator: ast::Expression = { "@" "\n" => { p }, }; YieldExpr: ast::Expression = { "yield" => ast::Expression { location, custom: (), node: ast::ExpressionType::Yield { value: value.map(Box::new) } }, "yield" "from" => ast::Expression { location, custom: (), node: ast::ExpressionType::YieldFrom { value: Box::new(e) } }, }; Test: ast::Expression = { => { if let Some(c) = condition { ast::Expression { location: c.0, custom: (), node: ast::ExpressionType::IfExpression { test: Box::new(c.2), body: Box::new(expr), orelse: Box::new(c.4), } } } else { expr } }, LambdaDef, }; NamedExpressionTest: ast::Expression = { ?> ":" => ast::Expression { location, custom: (), node: ast::ExpressionType::Lambda { args: Box::new(p.unwrap_or_default()), body: Box::new(body) } } } OrTest: ast::Expression = { => { if e2.is_empty() { e1 } else { let mut values = vec![e1]; values.extend(e2.into_iter().map(|e| e.1)); ast::Expression { location, custom: (), node: ast::ExpressionType::BoolOp { op: ast::BooleanOperator::Or, values } } } }, }; AndTest: ast::Expression = { => { if e2.is_empty() { e1 } else { let mut values = vec![e1]; values.extend(e2.into_iter().map(|e| e.1)); ast::Expression { location, custom: (), node: ast::ExpressionType::BoolOp { op: ast::BooleanOperator::And, values } } } }, }; NotTest: ast::Expression = { "not" => ast::Expression { location, custom: (), node: ast::ExpressionType::Unop { a: Box::new(e), op: ast::UnaryOperator::Not } }, Comparison, }; Comparison: ast::Expression = { => { let mut vals = vec![e]; let mut ops = vec![]; for x in comparisons { ops.push(x.0); vals.push(x.1); } ast::Expression { location, custom: (), node: ast::ExpressionType::Compare { vals, ops } } }, Expression, }; 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 { location, custom: (), node: ast::ExpressionType::Binop { a: Box::new(e1), op: ast::Operator::BitOr, b: Box::new(e2) } }, XorExpression, }; XorExpression: ast::Expression = { "^" => ast::Expression { location, custom: (), node: ast::ExpressionType::Binop { a: Box::new(e1), op: ast::Operator::BitXor, b: Box::new(e2) } }, AndExpression, }; AndExpression: ast::Expression = { "&" => ast::Expression { location, custom: (), node: ast::ExpressionType::Binop { a: Box::new(e1), op: ast::Operator::BitAnd, b: Box::new(e2) } }, ShiftExpression, }; ShiftExpression: ast::Expression = { => ast::Expression { location, custom: (), node: ast::ExpressionType::Binop { a: Box::new(e1), op, b: Box::new(e2) } }, ArithmaticExpression, }; ShiftOp: ast::Operator = { "<<" => ast::Operator::LShift, ">>" => ast::Operator::RShift, }; ArithmaticExpression: ast::Expression = { => ast::Expression { location, custom: (), node: ast::ExpressionType::Binop { a: Box::new(a), op, b: Box::new(b) } }, Term, }; AddOp: ast::Operator = { "+" => ast::Operator::Add, "-" => ast::Operator::Sub, }; Term: ast::Expression = { => ast::Expression { location, custom: (), node: ast::ExpressionType::Binop { a: Box::new(a), 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 { location, custom: (), node: ast::ExpressionType::Unop { a: Box::new(e), op } }, Power, }; UnOp: ast::UnaryOperator = { "+" => ast::UnaryOperator::Pos, "-" => ast::UnaryOperator::Neg, "~" => ast::UnaryOperator::Inv, }; Power: ast::Expression = { => { match e2 { None => e, Some((location, _, b)) => ast::Expression { location, custom: (), node: ast::ExpressionType::Binop { a: Box::new(e), op: ast::Operator::Pow, b: Box::new(b) } }, } } }; AtomExpr: ast::Expression = { => { if is_await.is_some() { ast::Expression { location, custom: (), node: ast::ExpressionType::Await { value: Box::new(atom) } } } else { atom } } } AtomExpr2: ast::Expression = { Atom, "(" ")" => { ast::Expression { location, custom: (), node: ast::ExpressionType::Call { function: Box::new(f), args: a.args, keywords: a.keywords } } }, "[" "]" => ast::Expression { location, custom: (), node: ast::ExpressionType::Subscript { a: Box::new(e), b: Box::new(s) } }, "." => ast::Expression { location, custom: (), node: ast::ExpressionType::Attribute { value: Box::new(e), name } }, }; SubscriptList: ast::Expression = { ","? => { if s2.is_empty() { s1 } else { let mut dims = vec![s1]; for x in s2 { dims.push(x.1) } ast::Expression { location, custom: (), node: ast::ExpressionType::Tuple { elements: dims }, } } } }; Subscript: ast::Expression = { Test, ":" => { let s1 = e1.unwrap_or(ast::Expression { location, custom: (), node: ast::ExpressionType::None }); let s2 = e2.unwrap_or(ast::Expression { location, custom: (), node: ast::ExpressionType::None }); let s3 = e3.unwrap_or(ast::Expression { location, custom: (), node: ast::ExpressionType::None }); ast::Expression { location, custom: (), node: ast::ExpressionType::Slice { elements: vec![s1, s2, s3] } } } }; SliceOp: ast::Expression = { ":" => e.unwrap_or(ast::Expression {location, custom: (), node: ast::ExpressionType::None}) } Atom: ast::Expression = { => ast::Expression { location, custom: (), node: ast::ExpressionType::String { value } }, => ast::Expression { location, custom: (), node: ast::ExpressionType::Bytes { value } }, => ast::Expression { location, custom: (), node: ast::ExpressionType::Number { value } }, => ast::Expression { location, custom: (), node: ast::ExpressionType::Identifier { name } }, "[" "]" => { let elements = e.unwrap_or_default(); ast::Expression { location, custom: (), node: ast::ExpressionType::List { elements } } }, "[" "]" => { ast::Expression { location, custom: (), node: ast::ExpressionType::Comprehension { kind: Box::new(ast::ComprehensionKind::List { element }), generators, } } }, "(" ")" => { elements.unwrap_or(ast::Expression { location, custom: (), node: ast::ExpressionType::Tuple { elements: Vec::new() } }) }, "(" ")" => e, "(" ")" => { ast::Expression { location, custom: (), node: ast::ExpressionType::Comprehension { kind: Box::new(ast::ComprehensionKind::GeneratorExpression { element }), generators, } } }, "{" "}" => ast::Expression { location, custom: (), node: ast::ExpressionType::Dict { elements: e.unwrap_or_default() } }, "{" "}" => { ast::Expression { location, custom: (), node: ast::ExpressionType::Comprehension { kind: Box::new(ast::ComprehensionKind::Dict { key: e1.0, value: e1.1 }), generators, } } }, "{" "}" => ast::Expression { location, custom: (), node: ast::ExpressionType::Set { elements } }, "{" "}" => { ast::Expression { location, custom: (), node: ast::ExpressionType::Comprehension { kind: Box::new(ast::ComprehensionKind::Set { element }), generators, } } }, "True" => ast::Expression { location, custom: (), node: ast::ExpressionType::True }, "False" => ast::Expression { location, custom: (), node: ast::ExpressionType::False }, "None" => ast::Expression { location, custom: (), node: ast::ExpressionType::None }, "..." => ast::Expression { location, custom: (), node: ast::ExpressionType::Ellipsis }, }; ListLiteralValues: Vec = { > ","? => e, }; DictLiteralValues: Vec<(Option, ast::Expression)> = { > ","? => elements, }; DictEntry: (ast::Expression, ast::Expression) = { ":" => (e1, e2), }; DictElement: (Option, ast::Expression) = { => (Some(e.0), e.1), "**" => (None, e), }; SetLiteralValues: Vec = { > ","? => e1 }; ExpressionOrStarExpression = { Expression, StarExpr }; ExpressionList: ast::Expression = { > => { if elements.len() == 1 && trailing_comma.is_none() { elements.into_iter().next().unwrap() } else { ast::Expression { location, custom: (), node: ast::ExpressionType::Tuple { elements }, } } }, }; ExpressionList2: Vec = { > ","? => elements, }; // A test list is one of: // - a list of expressions // - a single expression // - a single expression followed by a trailing comma TestList: ast::Expression = { > => { if elements.len() == 1 && trailing_comma.is_none() { elements.into_iter().next().unwrap() } else { ast::Expression { location, custom: (), node: ast::ExpressionType::Tuple { elements }, } } } }; // Test StarExpr: ast::Expression = { "*" => ast::Expression { location, custom: (), node: ast::ExpressionType::Starred { value: Box::new(e) }, } }; // Comprehensions: CompFor: Vec = => c; SingleForComprehension: ast::Comprehension = { "for" "in" => { let is_async = is_async.is_some(); ast::Comprehension { location, target, iter, ifs, is_async } } }; ExpressionNoCond: ast::Expression = OrTest; ComprehensionIf: ast::Expression = "if" => c; ArgumentList: ast::ArgumentList = { > =>? { let arg_list = parse_args(e)?; Ok(arg_list) } }; FunctionArgument: (Option>, ast::Expression) = { => { let expr = match c { Some(c) => ast::Expression { location: e.location, custom: (), node: ast::ExpressionType::Comprehension { kind: Box::new(ast::ComprehensionKind::GeneratorExpression { element: e }), generators: c, } }, None => e, }; (None, expr) }, "=" => (Some(Some(i)), e), "*" => (None, ast::Expression { location, custom: (), node: ast::ExpressionType::Starred { value: Box::new(e) } }), "**" => (Some(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 } }; Number: ast::Number = { => { ast::Number::Integer { value } }, => { ast::Number::Float { value } }, => { ast::Number::Complex { real: s.0, imag: s.1 } }, }; StringGroup: ast::StringGroup = { =>? { let mut values = vec![]; for (value, is_fstring) in s { values.push(if is_fstring { parse_located_fstring(&value, loc)? } else { ast::StringGroup::Constant { value } }) } Ok(if values.len() > 1 { ast::StringGroup::Joined { values } } else { values.into_iter().next().unwrap() }) }, }; 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, 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::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: , is_fstring: }, bytes => lexer::Tok::Bytes { value: > }, name => lexer::Tok::Name { name: }, "\n" => lexer::Tok::Newline, ";" => lexer::Tok::Semi, } }