///|
/// Statement parsing helpers for moonpython.
///
///|
fn expr_has_async_comprehension(expr : Expr) -> Bool {
match expr {
Expr::WithSpan(span=_, expr=inner) => expr_has_async_comprehension(inner)
Expr::ListComp(elt~, clauses~, filters~)
| Expr::SetComp(elt~, clauses~, filters~) => {
for clause in clauses {
if clause.is_async {
return true
}
if expr_has_async_comprehension(clause.iter) {
return true
}
}
for filter in filters {
if expr_has_async_comprehension(filter) {
return true
}
}
expr_has_async_comprehension(elt)
}
Expr::GenExp(elt~, clauses~, filters~) => {
// Async generator expressions are allowed outside async functions; do not
// treat `async for` clauses here as an error trigger.
for clause in clauses {
if expr_has_async_comprehension(clause.iter) {
return true
}
}
for filter in filters {
if expr_has_async_comprehension(filter) {
return true
}
}
expr_has_async_comprehension(elt)
}
Expr::DictComp(key~, value~, clauses~, filters~) => {
if expr_has_async_comprehension(key) ||
expr_has_async_comprehension(value) {
return true
}
for clause in clauses {
if clause.is_async {
return true
}
if expr_has_async_comprehension(clause.iter) {
return true
}
}
for filter in filters {
if expr_has_async_comprehension(filter) {
return true
}
}
false
}
Expr::List(items) | Expr::Tuple(items) | Expr::Set(items) => {
for item in items {
if expr_has_async_comprehension(item) {
return true
}
}
false
}
Expr::Dict(items) => {
for pair in items {
if expr_has_async_comprehension(pair.0) ||
expr_has_async_comprehension(pair.1) {
return true
}
}
false
}
Expr::Lambda(params~, defaults~, body~) => {
let _ = params
for d in defaults {
if expr_has_async_comprehension(d) {
return true
}
}
expr_has_async_comprehension(body)
}
Expr::IfExpr(condition~, then_expr~, else_expr~) =>
expr_has_async_comprehension(condition) ||
expr_has_async_comprehension(then_expr) ||
expr_has_async_comprehension(else_expr)
Expr::NamedExpr(name~, value~) => {
let _ = name
expr_has_async_comprehension(value)
}
Expr::Yield(expr_opt) =>
match expr_opt {
Some(v) => expr_has_async_comprehension(v)
None => false
}
Expr::YieldFrom(value) | Expr::Await(value) =>
expr_has_async_comprehension(value)
Expr::Slice(start~, end~, step~) => {
match start {
Some(v) => if expr_has_async_comprehension(v) { return true }
None => ()
}
match end {
Some(v) => if expr_has_async_comprehension(v) { return true }
None => ()
}
match step {
Some(v) => if expr_has_async_comprehension(v) { return true }
None => ()
}
false
}
Expr::Attribute(value~, attr~) => {
let _ = attr
expr_has_async_comprehension(value)
}
Expr::Subscript(value~, index~) =>
expr_has_async_comprehension(value) || expr_has_async_comprehension(index)
Expr::Call(callee~, args~) => {
if expr_has_async_comprehension(callee) {
return true
}
for arg in args {
if expr_has_async_comprehension(arg) {
return true
}
}
false
}
Expr::Keyword(name~, value~) => {
let _ = name
expr_has_async_comprehension(value)
}
Expr::Starred(value~) | Expr::DoubleStarred(value~) =>
expr_has_async_comprehension(value)
Expr::Unary(op~, expr~) => {
let _ = op
expr_has_async_comprehension(expr)
}
Expr::Binary(op~, left~, right~) => {
let _ = op
expr_has_async_comprehension(left) || expr_has_async_comprehension(right)
}
Expr::BoolOp(op~, values~) => {
let _ = op
for v in values {
if expr_has_async_comprehension(v) {
return true
}
}
false
}
Expr::Compare(left~, ops~, comparators~) => {
let _ = ops
if expr_has_async_comprehension(left) {
return true
}
for v in comparators {
if expr_has_async_comprehension(v) {
return true
}
}
false
}
_ => false
}
}
///|
fn expr_has_yield_or_await(expr : Expr) -> Bool {
match expr {
Expr::WithSpan(span=_, expr=inner) => expr_has_yield_or_await(inner)
Expr::Yield(_) | Expr::YieldFrom(_) | Expr::Await(_) => true
Expr::ListComp(elt~, clauses~, filters~)
| Expr::SetComp(elt~, clauses~, filters~)
| Expr::GenExp(elt~, clauses~, filters~) => {
if expr_has_yield_or_await(elt) {
return true
}
for clause in clauses {
if expr_has_yield_or_await(clause.iter) {
return true
}
}
for filter in filters {
if expr_has_yield_or_await(filter) {
return true
}
}
false
}
Expr::DictComp(key~, value~, clauses~, filters~) => {
if expr_has_yield_or_await(key) || expr_has_yield_or_await(value) {
return true
}
for clause in clauses {
if expr_has_yield_or_await(clause.iter) {
return true
}
}
for filter in filters {
if expr_has_yield_or_await(filter) {
return true
}
}
false
}
Expr::List(items) | Expr::Tuple(items) | Expr::Set(items) => {
for item in items {
if expr_has_yield_or_await(item) {
return true
}
}
false
}
Expr::Dict(items) => {
for pair in items {
if expr_has_yield_or_await(pair.0) || expr_has_yield_or_await(pair.1) {
return true
}
}
false
}
Expr::Lambda(params~, defaults~, body~) => {
let _ = params
for d in defaults {
if expr_has_yield_or_await(d) {
return true
}
}
expr_has_yield_or_await(body)
}
Expr::IfExpr(condition~, then_expr~, else_expr~) =>
expr_has_yield_or_await(condition) ||
expr_has_yield_or_await(then_expr) ||
expr_has_yield_or_await(else_expr)
Expr::NamedExpr(name~, value~) => {
let _ = name
expr_has_yield_or_await(value)
}
Expr::Slice(start~, end~, step~) => {
match start {
Some(v) => if expr_has_yield_or_await(v) { return true }
None => ()
}
match end {
Some(v) => if expr_has_yield_or_await(v) { return true }
None => ()
}
match step {
Some(v) => if expr_has_yield_or_await(v) { return true }
None => ()
}
false
}
Expr::Attribute(value~, attr~) => {
let _ = attr
expr_has_yield_or_await(value)
}
Expr::Subscript(value~, index~) =>
expr_has_yield_or_await(value) || expr_has_yield_or_await(index)
Expr::Call(callee~, args~) => {
if expr_has_yield_or_await(callee) {
return true
}
for arg in args {
if expr_has_yield_or_await(arg) {
return true
}
}
false
}
Expr::Keyword(name~, value~) => {
let _ = name
expr_has_yield_or_await(value)
}
Expr::Starred(value~) | Expr::DoubleStarred(value~) =>
expr_has_yield_or_await(value)
Expr::Unary(op~, expr~) => {
let _ = op
expr_has_yield_or_await(expr)
}
Expr::Binary(op~, left~, right~) => {
let _ = op
expr_has_yield_or_await(left) || expr_has_yield_or_await(right)
}
Expr::BoolOp(op~, values~) => {
let _ = op
for v in values {
if expr_has_yield_or_await(v) {
return true
}
}
false
}
Expr::Compare(left~, ops~, comparators~) => {
let _ = ops
if expr_has_yield_or_await(left) {
return true
}
for cmp in comparators {
if expr_has_yield_or_await(cmp) {
return true
}
}
false
}
Expr::Literal(_) | Expr::Name(_) | Expr::FString(_) => false
}
}
///|
fn expr_has_illegal_await(expr : Expr, in_genexp : Bool) -> Bool {
match expr {
Expr::WithSpan(span=_, expr=inner) =>
expr_has_illegal_await(inner, in_genexp)
Expr::Await(_) => !in_genexp
Expr::GenExp(elt~, clauses~, filters~) => {
// `await` is permitted inside generator expressions (it makes the genexp
// an async generator expression).
if expr_has_illegal_await(elt, true) {
return true
}
for clause in clauses {
if expr_has_illegal_await(clause.iter, true) {
return true
}
}
for filter in filters {
if expr_has_illegal_await(filter, true) {
return true
}
}
false
}
Expr::ListComp(elt~, clauses~, filters~)
| Expr::SetComp(elt~, clauses~, filters~) => {
if expr_has_illegal_await(elt, in_genexp) {
return true
}
for clause in clauses {
if expr_has_illegal_await(clause.iter, in_genexp) {
return true
}
}
for filter in filters {
if expr_has_illegal_await(filter, in_genexp) {
return true
}
}
false
}
Expr::DictComp(key~, value~, clauses~, filters~) => {
if expr_has_illegal_await(key, in_genexp) ||
expr_has_illegal_await(value, in_genexp) {
return true
}
for clause in clauses {
if expr_has_illegal_await(clause.iter, in_genexp) {
return true
}
}
for filter in filters {
if expr_has_illegal_await(filter, in_genexp) {
return true
}
}
false
}
Expr::List(items) | Expr::Tuple(items) | Expr::Set(items) => {
for item in items {
if expr_has_illegal_await(item, in_genexp) {
return true
}
}
false
}
Expr::Dict(items) => {
for pair in items {
if expr_has_illegal_await(pair.0, in_genexp) ||
expr_has_illegal_await(pair.1, in_genexp) {
return true
}
}
false
}
Expr::Lambda(params~, defaults~, body~) => {
let _ = params
for d in defaults {
if expr_has_illegal_await(d, in_genexp) {
return true
}
}
expr_has_illegal_await(body, in_genexp)
}
Expr::IfExpr(condition~, then_expr~, else_expr~) =>
expr_has_illegal_await(condition, in_genexp) ||
expr_has_illegal_await(then_expr, in_genexp) ||
expr_has_illegal_await(else_expr, in_genexp)
Expr::NamedExpr(name~, value~) => {
let _ = name
expr_has_illegal_await(value, in_genexp)
}
Expr::Yield(expr_opt) =>
match expr_opt {
Some(v) => expr_has_illegal_await(v, in_genexp)
None => false
}
Expr::YieldFrom(value) => expr_has_illegal_await(value, in_genexp)
Expr::Slice(start~, end~, step~) => {
match start {
Some(v) => if expr_has_illegal_await(v, in_genexp) { return true }
None => ()
}
match end {
Some(v) => if expr_has_illegal_await(v, in_genexp) { return true }
None => ()
}
match step {
Some(v) => if expr_has_illegal_await(v, in_genexp) { return true }
None => ()
}
false
}
Expr::Attribute(value~, attr~) => {
let _ = attr
expr_has_illegal_await(value, in_genexp)
}
Expr::Subscript(value~, index~) =>
expr_has_illegal_await(value, in_genexp) ||
expr_has_illegal_await(index, in_genexp)
Expr::Call(callee~, args~) => {
if expr_has_illegal_await(callee, in_genexp) {
return true
}
for arg in args {
if expr_has_illegal_await(arg, in_genexp) {
return true
}
}
false
}
Expr::Keyword(name~, value~) => {
let _ = name
expr_has_illegal_await(value, in_genexp)
}
Expr::Starred(value~) | Expr::DoubleStarred(value~) =>
expr_has_illegal_await(value, in_genexp)
Expr::Unary(op~, expr~) => {
let _ = op
expr_has_illegal_await(expr, in_genexp)
}
Expr::Binary(op~, left~, right~) => {
let _ = op
expr_has_illegal_await(left, in_genexp) ||
expr_has_illegal_await(right, in_genexp)
}
Expr::BoolOp(op~, values~) => {
let _ = op
for v in values {
if expr_has_illegal_await(v, in_genexp) {
return true
}
}
false
}
Expr::Compare(left~, ops~, comparators~) => {
let _ = ops
if expr_has_illegal_await(left, in_genexp) {
return true
}
for v in comparators {
if expr_has_illegal_await(v, in_genexp) {
return true
}
}
false
}
_ => false
}
}
///|
fn expr_first_illegal_await_line(
expr : Expr,
in_genexp : Bool,
fallback_line : Int32,
) -> Int32? {
match expr {
Expr::WithSpan(span~, expr=inner) =>
expr_first_illegal_await_line(inner, in_genexp, span.line)
Expr::Await(_) => if in_genexp { None } else { Some(fallback_line) }
Expr::GenExp(elt~, clauses~, filters~) => {
match expr_first_illegal_await_line(elt, true, fallback_line) {
Some(line) => return Some(line)
None => ()
}
for clause in clauses {
match expr_first_illegal_await_line(clause.iter, true, fallback_line) {
Some(line) => return Some(line)
None => ()
}
}
for filter in filters {
match expr_first_illegal_await_line(filter, true, fallback_line) {
Some(line) => return Some(line)
None => ()
}
}
None
}
Expr::ListComp(elt~, clauses~, filters~)
| Expr::SetComp(elt~, clauses~, filters~) => {
match expr_first_illegal_await_line(elt, in_genexp, fallback_line) {
Some(_) => return Some(fallback_line)
None => ()
}
for clause in clauses {
match
expr_first_illegal_await_line(clause.iter, in_genexp, fallback_line) {
Some(_) => return Some(fallback_line)
None => ()
}
}
for filter in filters {
match expr_first_illegal_await_line(filter, in_genexp, fallback_line) {
Some(_) => return Some(fallback_line)
None => ()
}
}
None
}
Expr::DictComp(key~, value~, clauses~, filters~) => {
match expr_first_illegal_await_line(key, in_genexp, fallback_line) {
Some(_) => return Some(fallback_line)
None => ()
}
match expr_first_illegal_await_line(value, in_genexp, fallback_line) {
Some(_) => return Some(fallback_line)
None => ()
}
for clause in clauses {
match
expr_first_illegal_await_line(clause.iter, in_genexp, fallback_line) {
Some(_) => return Some(fallback_line)
None => ()
}
}
for filter in filters {
match expr_first_illegal_await_line(filter, in_genexp, fallback_line) {
Some(_) => return Some(fallback_line)
None => ()
}
}
None
}
Expr::List(items) | Expr::Tuple(items) | Expr::Set(items) => {
for item in items {
match expr_first_illegal_await_line(item, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
}
None
}
Expr::Dict(items) => {
for pair in items {
match expr_first_illegal_await_line(pair.0, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
match expr_first_illegal_await_line(pair.1, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
}
None
}
Expr::Lambda(params~, defaults~, body~) => {
let _ = params
for default_value in defaults {
match
expr_first_illegal_await_line(default_value, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
}
expr_first_illegal_await_line(body, in_genexp, fallback_line)
}
Expr::IfExpr(condition~, then_expr~, else_expr~) => {
match expr_first_illegal_await_line(condition, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
match expr_first_illegal_await_line(then_expr, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
expr_first_illegal_await_line(else_expr, in_genexp, fallback_line)
}
Expr::NamedExpr(name~, value~) => {
let _ = name
expr_first_illegal_await_line(value, in_genexp, fallback_line)
}
Expr::Yield(expr_opt) =>
match expr_opt {
Some(value) =>
expr_first_illegal_await_line(value, in_genexp, fallback_line)
None => None
}
Expr::YieldFrom(value) =>
expr_first_illegal_await_line(value, in_genexp, fallback_line)
Expr::Slice(start~, end~, step~) => {
match start {
Some(value) =>
match expr_first_illegal_await_line(value, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
None => ()
}
match end {
Some(value) =>
match expr_first_illegal_await_line(value, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
None => ()
}
match step {
Some(value) =>
expr_first_illegal_await_line(value, in_genexp, fallback_line)
None => None
}
}
Expr::Attribute(value~, attr~) => {
let _ = attr
expr_first_illegal_await_line(value, in_genexp, fallback_line)
}
Expr::Subscript(value~, index~) => {
match expr_first_illegal_await_line(value, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
expr_first_illegal_await_line(index, in_genexp, fallback_line)
}
Expr::Call(callee~, args~) => {
match expr_first_illegal_await_line(callee, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
for arg in args {
match expr_first_illegal_await_line(arg, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
}
None
}
Expr::Keyword(name~, value~) => {
let _ = name
expr_first_illegal_await_line(value, in_genexp, fallback_line)
}
Expr::Starred(value~) | Expr::DoubleStarred(value~) =>
expr_first_illegal_await_line(value, in_genexp, fallback_line)
Expr::Unary(op~, expr~) => {
let _ = op
expr_first_illegal_await_line(expr, in_genexp, fallback_line)
}
Expr::Binary(op~, left~, right~) => {
let _ = op
match expr_first_illegal_await_line(left, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
expr_first_illegal_await_line(right, in_genexp, fallback_line)
}
Expr::BoolOp(op~, values~) => {
let _ = op
for value in values {
match expr_first_illegal_await_line(value, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
}
None
}
Expr::Compare(left~, ops~, comparators~) => {
let _ = ops
match expr_first_illegal_await_line(left, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
for value in comparators {
match expr_first_illegal_await_line(value, in_genexp, fallback_line) {
Some(line) => return Some(line)
None => ()
}
}
None
}
_ => None
}
}
///|
fn target_first_illegal_await_line(
target : Target,
fallback_line : Int32,
) -> Int32? {
match target {
Target::Tuple(items) | Target::List(items) => {
for item in items {
match target_first_illegal_await_line(item, fallback_line) {
Some(line) => return Some(line)
None => ()
}
}
None
}
Target::Starred(inner) =>
target_first_illegal_await_line(inner, fallback_line)
Target::Attribute(value~, attr~) => {
let _ = attr
expr_first_illegal_await_line(value, false, fallback_line)
}
Target::Subscript(value~, index~) => {
match expr_first_illegal_await_line(value, false, fallback_line) {
Some(line) => return Some(line)
None => ()
}
expr_first_illegal_await_line(index, false, fallback_line)
}
_ => None
}
}
///|
fn stmt_first_illegal_await_line(stmt : Stmt, fallback_line : Int32) -> Int32? {
match stmt {
Stmt::WithSpan(span~, stmt=inner) =>
stmt_first_illegal_await_line(inner, span.line)
Stmt::ExprStmt(expr) =>
expr_first_illegal_await_line(expr, false, fallback_line)
Stmt::Assign(target~, value~) => {
match target_first_illegal_await_line(target, fallback_line) {
Some(line) => return Some(line)
None => ()
}
expr_first_illegal_await_line(value, false, fallback_line)
}
Stmt::AnnAssign(target~, annotation~, value~) => {
match target_first_illegal_await_line(target, fallback_line) {
Some(line) => return Some(line)
None => ()
}
match expr_first_illegal_await_line(annotation, false, fallback_line) {
Some(line) => return Some(line)
None => ()
}
match value {
Some(v) => expr_first_illegal_await_line(v, false, fallback_line)
None => None
}
}
Stmt::AugAssign(target~, op~, value~) => {
let _ = op
match target_first_illegal_await_line(target, fallback_line) {
Some(line) => return Some(line)
None => ()
}
expr_first_illegal_await_line(value, false, fallback_line)
}
Stmt::Return(expr_opt) | Stmt::Yield(expr_opt) =>
match expr_opt {
Some(v) => expr_first_illegal_await_line(v, false, fallback_line)
None => None
}
Stmt::YieldFrom(expr) =>
expr_first_illegal_await_line(expr, false, fallback_line)
Stmt::Assert(condition~, message~) => {
match expr_first_illegal_await_line(condition, false, fallback_line) {
Some(line) => return Some(line)
None => ()
}
match message {
Some(v) => expr_first_illegal_await_line(v, false, fallback_line)
None => None
}
}
Stmt::Raise(exc~, cause~) => {
match exc {
Some(v) =>
match expr_first_illegal_await_line(v, false, fallback_line) {
Some(line) => return Some(line)
None => ()
}
None => ()
}
match cause {
Some(v) => expr_first_illegal_await_line(v, false, fallback_line)
None => None
}
}
Stmt::Del(targets) => {
for target in targets {
match target_first_illegal_await_line(target, fallback_line) {
Some(line) => return Some(line)
None => ()
}
}
None
}
Stmt::With(context~, target~, body~)
| Stmt::AsyncWith(context~, target~, body~) => {
let _ = target
match expr_first_illegal_await_line(context, false, fallback_line) {
Some(line) => return Some(line)
None => ()
}
stmts_first_illegal_await_line(body)
}
Stmt::If(condition~, body~, else_body~)
| Stmt::While(condition~, body~, else_body~) => {
match expr_first_illegal_await_line(condition, false, fallback_line) {
Some(line) => return Some(line)
None => ()
}
match stmts_first_illegal_await_line(body) {
Some(line) => return Some(line)
None => ()
}
stmts_first_illegal_await_line(else_body)
}
Stmt::For(target~, iter~, body~, else_body~)
| Stmt::AsyncFor(target~, iter~, body~, else_body~) => {
match target_first_illegal_await_line(target, fallback_line) {
Some(line) => return Some(line)
None => ()
}
match expr_first_illegal_await_line(iter, false, fallback_line) {
Some(line) => return Some(line)
None => ()
}
match stmts_first_illegal_await_line(body) {
Some(line) => return Some(line)
None => ()
}
stmts_first_illegal_await_line(else_body)
}
Stmt::Try(body~, handlers~, else_body~, finally_body~) => {
match stmts_first_illegal_await_line(body) {
Some(line) => return Some(line)
None => ()
}
for handler in handlers {
match handler.exc {
Some(v) =>
match expr_first_illegal_await_line(v, false, fallback_line) {
Some(line) => return Some(line)
None => ()
}
None => ()
}
match stmts_first_illegal_await_line(handler.body) {
Some(line) => return Some(line)
None => ()
}
}
match stmts_first_illegal_await_line(else_body) {
Some(line) => return Some(line)
None => ()
}
stmts_first_illegal_await_line(finally_body)
}
Stmt::Match(subject~, cases~) => {
match expr_first_illegal_await_line(subject, false, fallback_line) {
Some(line) => return Some(line)
None => ()
}
for case in cases {
match case.1 {
Some(v) =>
match expr_first_illegal_await_line(v, false, fallback_line) {
Some(line) => return Some(line)
None => ()
}
None => ()
}
match stmts_first_illegal_await_line(case.2) {
Some(line) => return Some(line)
None => ()
}
}
None
}
_ => None
}
}
///|
fn stmts_first_illegal_await_line(stmts : Array[Stmt]) -> Int32? {
for stmt in stmts {
match stmt_first_illegal_await_line(stmt, 1) {
Some(line) => return Some(line)
None => ()
}
}
None
}
///|
fn stmt_first_except_star_control_flow_line(
stmt : Stmt,
fallback_line : Int32,
) -> Int32? {
match stmt {
Stmt::WithSpan(span~, stmt=inner) =>
stmt_first_except_star_control_flow_line(inner, span.line)
Stmt::Break | Stmt::Continue | Stmt::Return(_) => Some(fallback_line)
Stmt::If(condition=_, body~, else_body~)
| Stmt::While(condition=_, body~, else_body~) => {
match stmts_first_except_star_control_flow_line(body) {
Some(line) => return Some(line)
None => ()
}
stmts_first_except_star_control_flow_line(else_body)
}
Stmt::For(target=_, iter=_, body~, else_body~)
| Stmt::AsyncFor(target=_, iter=_, body~, else_body~) => {
match stmts_first_except_star_control_flow_line(body) {
Some(line) => return Some(line)
None => ()
}
stmts_first_except_star_control_flow_line(else_body)
}
Stmt::With(context=_, target=_, body~)
| Stmt::AsyncWith(context=_, target=_, body~) =>
stmts_first_except_star_control_flow_line(body)
Stmt::Try(body~, handlers~, else_body~, finally_body~) => {
match stmts_first_except_star_control_flow_line(body) {
Some(line) => return Some(line)
None => ()
}
for handler in handlers {
match stmts_first_except_star_control_flow_line(handler.body) {
Some(line) => return Some(line)
None => ()
}
}
match stmts_first_except_star_control_flow_line(else_body) {
Some(line) => return Some(line)
None => ()
}
stmts_first_except_star_control_flow_line(finally_body)
}
Stmt::Match(subject=_, cases~) => {
for case in cases {
match stmts_first_except_star_control_flow_line(case.2) {
Some(line) => return Some(line)
None => ()
}
}
None
}
_ => None
}
}
///|
fn stmts_first_except_star_control_flow_line(stmts : Array[Stmt]) -> Int32? {
for stmt in stmts {
match stmt_first_except_star_control_flow_line(stmt, 1) {
Some(line) => return Some(line)
None => ()
}
}
None
}
///|
fn stmt_first_invalid_control_flow_line(
stmt : Stmt,
fallback_line : Int32,
in_function : Bool,
loop_depth : Int32,
) -> Int32? {
match stmt {
Stmt::WithSpan(span~, stmt=inner) =>
stmt_first_invalid_control_flow_line(
inner,
span.line,
in_function,
loop_depth,
)
Stmt::Return(_) | Stmt::Yield(_) | Stmt::YieldFrom(_) =>
if in_function {
None
} else {
Some(fallback_line)
}
Stmt::Break | Stmt::Continue =>
if loop_depth > 0 {
None
} else {
Some(fallback_line)
}
Stmt::If(condition=_, body~, else_body~) => {
match
stmts_first_invalid_control_flow_line(body, in_function, loop_depth) {
Some(line) => return Some(line)
None => ()
}
stmts_first_invalid_control_flow_line(else_body, in_function, loop_depth)
}
Stmt::While(condition=_, body~, else_body~) => {
match
stmts_first_invalid_control_flow_line(body, in_function, loop_depth + 1) {
Some(line) => return Some(line)
None => ()
}
stmts_first_invalid_control_flow_line(else_body, in_function, loop_depth)
}
Stmt::For(target=_, iter=_, body~, else_body~)
| Stmt::AsyncFor(target=_, iter=_, body~, else_body~) => {
match
stmts_first_invalid_control_flow_line(body, in_function, loop_depth + 1) {
Some(line) => return Some(line)
None => ()
}
stmts_first_invalid_control_flow_line(else_body, in_function, loop_depth)
}
Stmt::With(context=_, target=_, body~)
| Stmt::AsyncWith(context=_, target=_, body~) =>
stmts_first_invalid_control_flow_line(body, in_function, loop_depth)
Stmt::Try(body~, handlers~, else_body~, finally_body~) => {
match
stmts_first_invalid_control_flow_line(body, in_function, loop_depth) {
Some(line) => return Some(line)
None => ()
}
for handler in handlers {
match
stmts_first_invalid_control_flow_line(
handler.body,
in_function,
loop_depth,
) {
Some(line) => return Some(line)
None => ()
}
}
match
stmts_first_invalid_control_flow_line(
else_body, in_function, loop_depth,
) {
Some(line) => return Some(line)
None => ()
}
stmts_first_invalid_control_flow_line(
finally_body, in_function, loop_depth,
)
}
Stmt::Match(subject=_, cases~) => {
for case in cases {
match
stmts_first_invalid_control_flow_line(case.2, in_function, loop_depth) {
Some(line) => return Some(line)
None => ()
}
}
None
}
Stmt::Function(body~, ..) =>
stmts_first_invalid_control_flow_line(body, true, 0)
Stmt::Class(body~, ..) =>
stmts_first_invalid_control_flow_line(body, false, 0)
_ => None
}
}
///|
fn stmts_first_invalid_control_flow_line(
stmts : Array[Stmt],
in_function : Bool,
loop_depth : Int32,
) -> Int32? {
for stmt in stmts {
match
stmt_first_invalid_control_flow_line(stmt, 1, in_function, loop_depth) {
Some(line) => return Some(line)
None => ()
}
}
None
}
///|
fn expr_contains_name_for_class_comp(expr : Expr, wanted : String) -> Bool {
match expr {
Expr::WithSpan(span=_, expr=inner) =>
expr_contains_name_for_class_comp(inner, wanted)
Expr::Name(name) => name == wanted
Expr::Tuple(items) | Expr::List(items) | Expr::Set(items) => {
for item in items {
if expr_contains_name_for_class_comp(item, wanted) {
return true
}
}
false
}
Expr::Dict(items) => {
for item in items {
if expr_contains_name_for_class_comp(item.0, wanted) ||
expr_contains_name_for_class_comp(item.1, wanted) {
return true
}
}
false
}
Expr::IfExpr(condition~, then_expr~, else_expr~) =>
expr_contains_name_for_class_comp(condition, wanted) ||
expr_contains_name_for_class_comp(then_expr, wanted) ||
expr_contains_name_for_class_comp(else_expr, wanted)
Expr::NamedExpr(name~, value~) =>
name == wanted || expr_contains_name_for_class_comp(value, wanted)
Expr::Yield(expr_opt) =>
match expr_opt {
Some(value) => expr_contains_name_for_class_comp(value, wanted)
None => false
}
Expr::YieldFrom(value) | Expr::Await(value) =>
expr_contains_name_for_class_comp(value, wanted)
Expr::Slice(start~, end~, step~) => {
match start {
Some(value) =>
if expr_contains_name_for_class_comp(value, wanted) {
return true
}
None => ()
}
match end {
Some(value) =>
if expr_contains_name_for_class_comp(value, wanted) {
return true
}
None => ()
}
match step {
Some(value) => expr_contains_name_for_class_comp(value, wanted)
None => false
}
}
Expr::Attribute(value~, attr=_) =>
expr_contains_name_for_class_comp(value, wanted)
Expr::Subscript(value~, index~) =>
expr_contains_name_for_class_comp(value, wanted) ||
expr_contains_name_for_class_comp(index, wanted)
Expr::Call(callee~, args~) => {
if expr_contains_name_for_class_comp(callee, wanted) {
return true
}
for arg in args {
if expr_contains_name_for_class_comp(arg, wanted) {
return true
}
}
false
}
Expr::Keyword(name=_, value~) =>
expr_contains_name_for_class_comp(value, wanted)
Expr::Starred(value~) | Expr::DoubleStarred(value~) =>
expr_contains_name_for_class_comp(value, wanted)
Expr::Unary(op=_, expr~) => expr_contains_name_for_class_comp(expr, wanted)
Expr::Binary(op=_, left~, right~) =>
expr_contains_name_for_class_comp(left, wanted) ||
expr_contains_name_for_class_comp(right, wanted)
Expr::BoolOp(op=_, values~) => {
for value in values {
if expr_contains_name_for_class_comp(value, wanted) {
return true
}
}
false
}
Expr::Compare(left~, ops=_, comparators~) => {
if expr_contains_name_for_class_comp(left, wanted) {
return true
}
for cmp in comparators {
if expr_contains_name_for_class_comp(cmp, wanted) {
return true
}
}
false
}
Expr::Lambda(params=_, defaults~, body~) => {
for default_value in defaults {
if expr_contains_name_for_class_comp(default_value, wanted) {
return true
}
}
expr_contains_name_for_class_comp(body, wanted)
}
_ => false
}
}
///|
fn expr_has_class_body_comprehension_name(expr : Expr, wanted : String) -> Bool {
match expr {
Expr::WithSpan(span=_, expr=inner) =>
expr_has_class_body_comprehension_name(inner, wanted)
Expr::ListComp(elt~, clauses~, filters~)
| Expr::GenExp(elt~, clauses~, filters~)
| Expr::SetComp(elt~, clauses~, filters~) => {
if expr_contains_name_for_class_comp(elt, wanted) {
return true
}
for clause in clauses {
if expr_contains_name_for_class_comp(clause.iter, wanted) {
return true
}
}
for filter in filters {
if expr_contains_name_for_class_comp(filter, wanted) {
return true
}
}
false
}
Expr::DictComp(key~, value~, clauses~, filters~) => {
if expr_contains_name_for_class_comp(key, wanted) ||
expr_contains_name_for_class_comp(value, wanted) {
return true
}
for clause in clauses {
if expr_contains_name_for_class_comp(clause.iter, wanted) {
return true
}
}
for filter in filters {
if expr_contains_name_for_class_comp(filter, wanted) {
return true
}
}
false
}
Expr::Tuple(items) | Expr::List(items) | Expr::Set(items) => {
for item in items {
if expr_has_class_body_comprehension_name(item, wanted) {
return true
}
}
false
}
Expr::Dict(items) => {
for item in items {
if expr_has_class_body_comprehension_name(item.0, wanted) ||
expr_has_class_body_comprehension_name(item.1, wanted) {
return true
}
}
false
}
Expr::IfExpr(condition~, then_expr~, else_expr~) =>
expr_has_class_body_comprehension_name(condition, wanted) ||
expr_has_class_body_comprehension_name(then_expr, wanted) ||
expr_has_class_body_comprehension_name(else_expr, wanted)
Expr::NamedExpr(name=_, value~) =>
expr_has_class_body_comprehension_name(value, wanted)
Expr::Yield(expr_opt) =>
match expr_opt {
Some(value) => expr_has_class_body_comprehension_name(value, wanted)
None => false
}
Expr::YieldFrom(value) | Expr::Await(value) =>
expr_has_class_body_comprehension_name(value, wanted)
Expr::Slice(start~, end~, step~) => {
match start {
Some(value) =>
if expr_has_class_body_comprehension_name(value, wanted) {
return true
}
None => ()
}
match end {
Some(value) =>
if expr_has_class_body_comprehension_name(value, wanted) {
return true
}
None => ()
}
match step {
Some(value) => expr_has_class_body_comprehension_name(value, wanted)
None => false
}
}
Expr::Attribute(value~, attr=_) =>
expr_has_class_body_comprehension_name(value, wanted)
Expr::Subscript(value~, index~) =>
expr_has_class_body_comprehension_name(value, wanted) ||
expr_has_class_body_comprehension_name(index, wanted)
Expr::Call(callee~, args~) => {
if expr_has_class_body_comprehension_name(callee, wanted) {
return true
}
for arg in args {
if expr_has_class_body_comprehension_name(arg, wanted) {
return true
}
}
false
}
Expr::Keyword(name=_, value~) =>
expr_has_class_body_comprehension_name(value, wanted)
Expr::Starred(value~) | Expr::DoubleStarred(value~) =>
expr_has_class_body_comprehension_name(value, wanted)
Expr::Unary(op=_, expr~) =>
expr_has_class_body_comprehension_name(expr, wanted)
Expr::Binary(op=_, left~, right~) =>
expr_has_class_body_comprehension_name(left, wanted) ||
expr_has_class_body_comprehension_name(right, wanted)
Expr::BoolOp(op=_, values~) => {
for value in values {
if expr_has_class_body_comprehension_name(value, wanted) {
return true
}
}
false
}
Expr::Compare(left~, ops=_, comparators~) => {
if expr_has_class_body_comprehension_name(left, wanted) {
return true
}
for cmp in comparators {
if expr_has_class_body_comprehension_name(cmp, wanted) {
return true
}
}
false
}
Expr::Lambda(params=_, defaults~, body=_) => {
for default_value in defaults {
if expr_has_class_body_comprehension_name(default_value, wanted) {
return true
}
}
false
}
_ => false
}
}
///|
fn stmt_has_class_body_comprehension_name(stmt : Stmt, wanted : String) -> Bool {
match stmt {
Stmt::WithSpan(span=_, stmt=inner) =>
stmt_has_class_body_comprehension_name(inner, wanted)
Stmt::ExprStmt(expr) => expr_has_class_body_comprehension_name(expr, wanted)
Stmt::Assign(target=_, value~) =>
expr_has_class_body_comprehension_name(value, wanted)
Stmt::AnnAssign(target=_, annotation~, value~) => {
if expr_has_class_body_comprehension_name(annotation, wanted) {
return true
}
match value {
Some(expr) => expr_has_class_body_comprehension_name(expr, wanted)
None => false
}
}
Stmt::AugAssign(target=_, op=_, value~) =>
expr_has_class_body_comprehension_name(value, wanted)
Stmt::If(condition~, body~, else_body~)
| Stmt::While(condition~, body~, else_body~) => {
if expr_has_class_body_comprehension_name(condition, wanted) {
return true
}
stmts_have_class_body_comprehension_name(body, wanted) ||
stmts_have_class_body_comprehension_name(else_body, wanted)
}
Stmt::For(target=_, iter~, body~, else_body~)
| Stmt::AsyncFor(target=_, iter~, body~, else_body~) =>
expr_has_class_body_comprehension_name(iter, wanted) ||
stmts_have_class_body_comprehension_name(body, wanted) ||
stmts_have_class_body_comprehension_name(else_body, wanted)
Stmt::With(context~, target=_, body~)
| Stmt::AsyncWith(context~, target=_, body~) =>
expr_has_class_body_comprehension_name(context, wanted) ||
stmts_have_class_body_comprehension_name(body, wanted)
Stmt::Try(body~, handlers~, else_body~, finally_body~) => {
if stmts_have_class_body_comprehension_name(body, wanted) {
return true
}
for handler in handlers {
match handler.exc {
Some(expr) =>
if expr_has_class_body_comprehension_name(expr, wanted) {
return true
}
None => ()
}
if stmts_have_class_body_comprehension_name(handler.body, wanted) {
return true
}
}
stmts_have_class_body_comprehension_name(else_body, wanted) ||
stmts_have_class_body_comprehension_name(finally_body, wanted)
}
Stmt::Match(subject~, cases~) => {
if expr_has_class_body_comprehension_name(subject, wanted) {
return true
}
for case in cases {
match case.1 {
Some(expr) =>
if expr_has_class_body_comprehension_name(expr, wanted) {
return true
}
None => ()
}
if stmts_have_class_body_comprehension_name(case.2, wanted) {
return true
}
}
false
}
Stmt::Function(..) | Stmt::Class(..) => false
_ => false
}
}
///|
fn stmts_have_class_body_comprehension_name(
stmts : Array[Stmt],
wanted : String,
) -> Bool {
for stmt in stmts {
if stmt_has_class_body_comprehension_name(stmt, wanted) {
return true
}
}
false
}
///|
fn stmt_first_param_scope_decl_line(
stmt : Stmt,
fallback_line : Int32,
params : Array[String],
) -> Int32? {
match stmt {
Stmt::WithSpan(span~, stmt=inner) =>
stmt_first_param_scope_decl_line(inner, span.line, params)
Stmt::Nonlocal(names) | Stmt::Global(names) => {
for name in names {
for param in params {
match normalize_param_name_for_compare(param) {
Some(param_name) =>
if param_name == name {
return Some(fallback_line)
}
None => ()
}
}
}
None
}
Stmt::If(condition=_, body~, else_body~)
| Stmt::While(condition=_, body~, else_body~) => {
match stmts_first_param_scope_decl_line(body, params) {
Some(line) => return Some(line)
None => ()
}
stmts_first_param_scope_decl_line(else_body, params)
}
Stmt::For(target=_, iter=_, body~, else_body~)
| Stmt::AsyncFor(target=_, iter=_, body~, else_body~) => {
match stmts_first_param_scope_decl_line(body, params) {
Some(line) => return Some(line)
None => ()
}
stmts_first_param_scope_decl_line(else_body, params)
}
Stmt::With(context=_, target=_, body~)
| Stmt::AsyncWith(context=_, target=_, body~) =>
stmts_first_param_scope_decl_line(body, params)
Stmt::Try(body~, handlers~, else_body~, finally_body~) => {
match stmts_first_param_scope_decl_line(body, params) {
Some(line) => return Some(line)
None => ()
}
for handler in handlers {
match stmts_first_param_scope_decl_line(handler.body, params) {
Some(line) => return Some(line)
None => ()
}
}
match stmts_first_param_scope_decl_line(else_body, params) {
Some(line) => return Some(line)
None => ()
}
stmts_first_param_scope_decl_line(finally_body, params)
}
Stmt::Match(subject=_, cases~) => {
for case_item in cases {
match stmts_first_param_scope_decl_line(case_item.2, params) {
Some(line) => return Some(line)
None => ()
}
}
None
}
Stmt::Function(..) | Stmt::Class(..) => None
_ => None
}
}
///|
fn stmts_first_param_scope_decl_line(
stmts : Array[Stmt],
params : Array[String],
) -> Int32? {
for stmt in stmts {
match stmt_first_param_scope_decl_line(stmt, 1, params) {
Some(line) => return Some(line)
None => ()
}
}
None
}
///|
fn stmt_first_function_import_star_line(
stmt : Stmt,
fallback_line : Int32,
) -> Int32? {
match stmt {
Stmt::WithSpan(span~, stmt=inner) =>
stmt_first_function_import_star_line(inner, span.line)
Stmt::FromImport(module_name=_, names~) => {
for item in names {
if item.0 == "*" {
return Some(fallback_line)
}
}
None
}
Stmt::If(condition=_, body~, else_body~)
| Stmt::While(condition=_, body~, else_body~) => {
match stmts_first_function_import_star_line(body) {
Some(line) => return Some(line)
None => ()
}
stmts_first_function_import_star_line(else_body)
}
Stmt::For(target=_, iter=_, body~, else_body~)
| Stmt::AsyncFor(target=_, iter=_, body~, else_body~) => {
match stmts_first_function_import_star_line(body) {
Some(line) => return Some(line)
None => ()
}
stmts_first_function_import_star_line(else_body)
}
Stmt::With(context=_, target=_, body~)
| Stmt::AsyncWith(context=_, target=_, body~) =>
stmts_first_function_import_star_line(body)
Stmt::Try(body~, handlers~, else_body~, finally_body~) => {
match stmts_first_function_import_star_line(body) {
Some(line) => return Some(line)
None => ()
}
for handler in handlers {
match stmts_first_function_import_star_line(handler.body) {
Some(line) => return Some(line)
None => ()
}
}
match stmts_first_function_import_star_line(else_body) {
Some(line) => return Some(line)
None => ()
}
stmts_first_function_import_star_line(finally_body)
}
Stmt::Match(subject=_, cases~) => {
for case_item in cases {
match stmts_first_function_import_star_line(case_item.2) {
Some(line) => return Some(line)
None => ()
}
}
None
}
Stmt::Function(..) | Stmt::Class(..) => None
_ => None
}
}
///|
fn stmts_first_function_import_star_line(stmts : Array[Stmt]) -> Int32? {
for stmt in stmts {
match stmt_first_function_import_star_line(stmt, 1) {
Some(line) => return Some(line)
None => ()
}
}
None
}
///|
fn target_has_async_comprehension(target : Target) -> Bool {
match target {
Target::Tuple(items) | Target::List(items) => {
for item in items {
if target_has_async_comprehension(item) {
return true
}
}
false
}
Target::Starred(inner) => target_has_async_comprehension(inner)
Target::Attribute(value~, attr~) => {
let _ = attr
expr_has_async_comprehension(value)
}
Target::Subscript(value~, index~) =>
expr_has_async_comprehension(value) || expr_has_async_comprehension(index)
_ => false
}
}
///|
fn stmt_has_async_comprehension(stmt : Stmt) -> Bool {
match stmt {
Stmt::WithSpan(span=_, stmt=inner) => stmt_has_async_comprehension(inner)
Stmt::ExprStmt(expr) => expr_has_async_comprehension(expr)
Stmt::Assign(target~, value~) =>
target_has_async_comprehension(target) ||
expr_has_async_comprehension(value)
Stmt::AugAssign(target~, op~, value~) => {
let _ = op
target_has_async_comprehension(target) ||
expr_has_async_comprehension(value)
}
Stmt::Return(expr_opt) =>
match expr_opt {
Some(v) => expr_has_async_comprehension(v)
None => false
}
Stmt::Yield(expr_opt) =>
match expr_opt {
Some(v) => expr_has_async_comprehension(v)
None => false
}
Stmt::YieldFrom(expr) => expr_has_async_comprehension(expr)
Stmt::Assert(condition~, message~) =>
if expr_has_async_comprehension(condition) {
true
} else {
match message {
Some(v) => expr_has_async_comprehension(v)
None => false
}
}
Stmt::Raise(exc~, cause~) => {
let exc_async = match exc {
Some(v) => expr_has_async_comprehension(v)
None => false
}
if exc_async {
true
} else {
match cause {
Some(v) => expr_has_async_comprehension(v)
None => false
}
}
}
Stmt::Del(targets) => {
for t in targets {
if target_has_async_comprehension(t) {
return true
}
}
false
}
Stmt::With(context~, target~, body~)
| Stmt::AsyncWith(context~, target~, body~) => {
let _ = target
if expr_has_async_comprehension(context) {
return true
}
for s in body {
if stmt_has_async_comprehension(s) {
return true
}
}
false
}
Stmt::If(condition~, body~, else_body~) => {
if expr_has_async_comprehension(condition) {
return true
}
for s in body {
if stmt_has_async_comprehension(s) {
return true
}
}
for s in else_body {
if stmt_has_async_comprehension(s) {
return true
}
}
false
}
Stmt::While(condition~, body~, else_body~) => {
if expr_has_async_comprehension(condition) {
return true
}
for s in body {
if stmt_has_async_comprehension(s) {
return true
}
}
for s in else_body {
if stmt_has_async_comprehension(s) {
return true
}
}
false
}
Stmt::For(target~, iter~, body~, else_body~)
| Stmt::AsyncFor(target~, iter~, body~, else_body~) => {
if target_has_async_comprehension(target) ||
expr_has_async_comprehension(iter) {
return true
}
for s in body {
if stmt_has_async_comprehension(s) {
return true
}
}
for s in else_body {
if stmt_has_async_comprehension(s) {
return true
}
}
false
}
Stmt::Try(body~, handlers~, else_body~, finally_body~) => {
for s in body {
if stmt_has_async_comprehension(s) {
return true
}
}
for handler in handlers {
match handler.exc {
Some(v) => if expr_has_async_comprehension(v) { return true }
None => ()
}
for s in handler.body {
if stmt_has_async_comprehension(s) {
return true
}
}
}
for s in else_body {
if stmt_has_async_comprehension(s) {
return true
}
}
for s in finally_body {
if stmt_has_async_comprehension(s) {
return true
}
}
false
}
Stmt::Match(subject~, cases~) => {
if expr_has_async_comprehension(subject) {
return true
}
for case in cases {
match case.1 {
Some(v) => if expr_has_async_comprehension(v) { return true }
None => ()
}
for s in case.2 {
if stmt_has_async_comprehension(s) {
return true
}
}
}
false
}
_ => false
}
}
///|
fn stmts_have_async_comprehension(stmts : Array[Stmt]) -> Bool {
for stmt in stmts {
if stmt_has_async_comprehension(stmt) {
return true
}
}
false
}
///|
fn parse_pattern(
text : String,
line_index : Int32,
base_offset : Int32,
) -> Result[Pattern, ParseError] {
let trimmed = text.trim(chars=" \t\n\r").to_owned()
if trimmed.length() == 0 {
return Err(
make_parse_error(ParseErrorKind::Syntax, "invalid syntax".to_string(), {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
}),
)
}
if trimmed == "{}" {
return Ok(Pattern::TypeCapture(type_name="dict", name="_"))
}
if trimmed == "_" {
return Ok(Pattern::Wildcard)
}
if trimmed == "True" {
return Ok(Pattern::Literal(Literal::Bool(true)))
}
if trimmed == "False" {
return Ok(Pattern::Literal(Literal::Bool(false)))
}
if trimmed == "None" {
return Ok(Pattern::Literal(Literal::None))
}
let chars = trimmed.to_array()
let mut all_digits = true
for c in chars {
if !is_digit(c) {
all_digits = false
break
}
}
if all_digits {
return Ok(
Pattern::Literal(
Literal::Int(@bigint.BigInt::from_int64(parse_int64_digits(trimmed))),
),
)
}
fn is_valid_ident(text : String) -> Bool {
if text.length() == 0 {
return false
}
let chars = text.to_array()
for i = 0; i < chars.length(); i = i + 1 {
let c = chars[i]
if !(is_alnum(c) || c == '_') || (i == 0 && is_digit(c)) {
return false
}
}
true
}
fn is_valid_qualified_ident(text : String) -> Bool {
if text.length() == 0 {
return false
}
let mut start = 0
let chars = text.to_array()
for i = 0; i <= chars.length(); i = i + 1 {
let end_reached = i == chars.length()
if end_reached || chars[i] == '.' {
let part = substring(text, start, i).to_string()
if !is_valid_ident(part) {
return false
}
start = i + 1
}
}
true
}
// Minimal support for class patterns like `str(name)` used by the stdlib.
// This is not a full PEP 634 implementation.
match find_top_level_paren_span(trimmed) {
Some(span) => {
let open_pos = span.0
let close_pos = span.1
if open_pos > 0 && close_pos == trimmed.length() - 1 {
let type_name = substring(trimmed, 0, open_pos)
.trim(chars=" \t\n\r")
.to_owned()
let inner = substring(trimmed, open_pos + 1, close_pos)
.trim(chars=" \t\n\r")
.to_owned()
if is_valid_qualified_ident(type_name) {
// `TypeName()` is treated like `TypeName(_)` (match by type only).
if inner.length() == 0 {
return Ok(Pattern::TypeCapture(type_name~, name="_"))
}
if is_valid_ident(inner) {
return Ok(Pattern::TypeCapture(type_name~, name=inner))
}
}
}
}
None => ()
}
// Tuple patterns require a top-level comma. Parentheses alone are grouping.
if chars.length() >= 2 && chars[0] == '(' && chars[chars.length() - 1] == ')' {
match find_top_level_paren_span(trimmed) {
Some(span) =>
if span.0 == 0 && span.1 == trimmed.length() - 1 {
let inner = substring(trimmed, 1, trimmed.length() - 1)
.trim(chars=" \t\n\r")
.to_owned()
if inner.length() == 0 {
return Ok(Pattern::Tuple([]))
}
let inner_parts = split_top_level_commas(inner)
if inner_parts.length() == 1 {
return parse_pattern(inner, line_index, base_offset)
}
let patterns : Array[Pattern] = []
for raw_part in inner_parts {
let part = raw_part.trim(chars=" \t\n\r").to_owned()
if part.length() == 0 {
continue
}
let pat = match parse_pattern(part, line_index, base_offset) {
Ok(value) => value
Err(err) => return Err(err)
}
patterns.push(pat)
}
return Ok(Pattern::Tuple(patterns))
}
None => ()
}
}
let raw_parts = split_top_level_commas(trimmed)
if raw_parts.length() > 1 {
let patterns : Array[Pattern] = []
for raw_part in raw_parts {
let part = raw_part.trim(chars=" \t\n\r").to_owned()
if part.length() == 0 {
continue
}
let pat = match parse_pattern(part, line_index, base_offset) {
Ok(value) => value
Err(err) => return Err(err)
}
patterns.push(pat)
}
return Ok(Pattern::Tuple(patterns))
}
let mut valid_ident = true
for i = 0; i < chars.length(); i = i + 1 {
let c = chars[i]
if !(is_alnum(c) || c == '_') || (i == 0 && is_digit(c)) {
valid_ident = false
break
}
}
if valid_ident {
return Ok(Pattern::Name(trimmed))
}
// Unknown syntax must never silently become a catch-all pattern.
match tokenize_expr(trimmed, line_index + 1, base_offset, 1) {
Ok(tokens) =>
match parse_tokens_expr(tokens) {
Ok(expr) =>
match pattern_literal(expr) {
Some(value) => return Ok(Pattern::Literal(value))
None => ()
}
Err(_) => ()
}
Err(_) => ()
}
Err(pattern_syntax_error(line_index, base_offset))
}
///|
fn target_from_expr(expr : Expr, token : Token) -> Result[Target, ParseError] {
match expr {
Expr::WithSpan(span=_, expr=inner) => target_from_expr(inner, token)
Expr::Name(name) => Ok(Target::Name(name))
Expr::Attribute(value~, attr~) => Ok(Target::Attribute(value~, attr~))
Expr::Subscript(value~, index~) => Ok(Target::Subscript(value~, index~))
Expr::Starred(value~) => {
let inner = match target_from_expr(value, token) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Target::Starred(inner))
}
Expr::Tuple(items) => {
let targets : Array[Target] = []
let mut star_count = 0
for item in items {
let target = match target_from_expr(item, token) {
Ok(value) => value
Err(err) => return Err(err)
}
match target {
Target::Starred(_) => {
star_count += 1
if star_count > 1 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
}
_ => ()
}
targets.push(target)
}
Ok(Target::Tuple(targets))
}
Expr::List(items) => {
let targets : Array[Target] = []
let mut star_count = 0
for item in items {
let target = match target_from_expr(item, token) {
Ok(value) => value
Err(err) => return Err(err)
}
match target {
Target::Starred(_) => {
star_count += 1
if star_count > 1 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
}
_ => ()
}
targets.push(target)
}
Ok(Target::List(targets))
}
_ =>
Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
}
///|
fn target_allows_annotation(target : Target) -> Bool {
match target {
Target::Name(_) | Target::Attribute(_) | Target::Subscript(_) => true
_ => false
}
}
///|
/// Parse assignment targets from a token slice. This is more permissive than
/// expression parsing: it supports `a, *rest = ...` where `*rest` is only valid
/// in assignment target context.
fn target_from_assignment_tokens(
tokens : Array[Token],
token : Token,
) -> Result[Target, ParseError] {
if tokens.length() == 0 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
// `tokens` are expected to include an Eof at the end (see assignment parsing below).
let body_end = if tokens.length() > 0 { tokens.length() - 1 } else { 0 }
let body = slice_tokens(tokens, 0, body_end)
if body.length() > 0 && body[0].kind == LBrace {
for token in body {
if token.kind == For || token.kind == Async {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
}
}
let mut has_top_comma = false
let mut depth = 0
for t in body {
let kind = t.kind
if kind == LParen || kind == LBracket || kind == LBrace {
depth += 1
} else if kind == RParen || kind == RBracket || kind == RBrace {
if depth > 0 {
depth -= 1
}
} else if kind == Comma && depth == 0 {
has_top_comma = true
break
}
}
if !has_top_comma {
let expr = match parse_tokens_expr(tokens) {
Ok(v) => v
Err(err) => return Err(err)
}
return target_from_expr(expr, token)
}
let parts : Array[Array[Token]] = []
let mut start = 0
depth = 0
for i = 0; i < body.length(); i = i + 1 {
let kind = body[i].kind
if kind == LParen || kind == LBracket || kind == LBrace {
depth += 1
} else if kind == RParen || kind == RBracket || kind == RBrace {
if depth > 0 {
depth -= 1
}
} else if kind == Comma && depth == 0 {
parts.push(slice_tokens(body, start, i))
start = i + 1
}
}
parts.push(slice_tokens(body, start, body.length()))
let targets : Array[Target] = []
let mut star_count = 0
for part in parts {
if part.length() == 0 {
continue
}
let is_star = part[0].kind == Star
let inner_part = if is_star {
slice_tokens(part, 1, part.length())
} else {
part
}
if is_star {
star_count += 1
if star_count > 1 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
}
if inner_part.length() == 0 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
inner_part.push({
kind: Eof,
start: token.start,
end: token.start,
line: token.line,
column: token.column,
})
let expr = match parse_tokens_expr(inner_part) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut target0 = match target_from_expr(expr, token) {
Ok(v) => v
Err(err) => return Err(err)
}
if is_star {
target0 = Target::Starred(target0)
}
targets.push(target0)
}
Ok(Target::Tuple(targets))
}
///|
fn finalize_simple_statement(
stmts : Array[Stmt],
allow_async_comprehensions : Bool,
span : Span,
base_offset : Int32,
) -> Result[Array[Stmt], ParseError] {
if !allow_async_comprehensions && stmts_have_async_comprehension(stmts) {
return Err(
make_parse_error(ParseErrorKind::Syntax, "invalid syntax".to_string(), {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: span.line,
column: 1,
}),
)
}
let out : Array[Stmt] = []
for stmt in stmts {
out.push(Stmt::WithSpan(span~, stmt~))
}
Ok(out)
}
///|
fn span_for_line(
offsets : Array[Int32],
line_index : Int32,
indent : Int32,
) -> Span {
let start = offsets[line_index] + indent
// Best-effort: span covers the whole physical line (excluding the trailing '\n').
// This is used for tracebacks/caret diagnostics (PEP 657 tests in Lib/test).
let end = if line_index + 1 < offsets.length() {
offsets[line_index + 1] - 1
} else {
start + 1
}
Span::{ start, end, line: line_index + 1, column: indent + 1, }
}
///|
fn wrap_stmt_for_line(
offsets : Array[Int32],
line_index : Int32,
indent : Int32,
stmt : Stmt,
) -> Stmt {
Stmt::WithSpan(span=span_for_line(offsets, line_index, indent), stmt~)
}
///|
fn parse_simple_statement(
line_text : String,
line_index : Int32,
line_start_offset : Int32,
base_offset : Int32,
allow_async_comprehensions : Bool,
) -> Result[Array[Stmt], ParseError] {
// `line_text` may be a sliced inline statement (e.g. after `:` in a one-liner
// suite), so its end offset is relative to `base_offset`, not `line_start_offset`.
// `line_text` can include physical newlines due to bracket continuation.
// Keep statement spans on the first physical line so traceback caret ranges
// don't become "very long single-line" offsets.
let mut line_len = line_text.length()
match line_text.find("\n") {
Some(idx) => line_len = idx
None => ()
}
if line_len <= 0 {
line_len = 1
}
let line_end_offset = base_offset + line_len
let span = Span::{
start: base_offset,
end: line_end_offset,
line: line_index + 1,
column: base_offset - line_start_offset + 1,
}
let stmts : Array[Stmt] = []
let mut trimmed = line_text.trim(chars=" \t").to_owned()
if trimmed.length() == 0 {
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
trimmed = strip_inline_comment(trimmed).trim(chars=" \t").to_owned()
if trimmed.length() == 0 {
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
match find_top_level_char(trimmed, '=') {
Some(eq_pos) => {
let chars = trimmed.to_array()
let prev = if eq_pos > 0 { chars[eq_pos - 1] } else { '\u{0}' }
let next = if eq_pos + 1 < chars.length() {
chars[eq_pos + 1]
} else {
'\u{0}'
}
let plain_assign = prev != '=' &&
prev != '<' &&
prev != '>' &&
prev != '!' &&
prev != ':' &&
next != '='
if plain_assign && trimmed.has_prefix("{") {
let left_text = substring(trimmed, 0, eq_pos).to_string()
if left_text.contains(" for ") || left_text.contains("\nfor ") {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
{
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
},
),
)
}
}
}
None => ()
}
if trimmed == "..." {
stmts.push(Stmt::Pass)
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if trimmed == "break" {
stmts.push(Stmt::Break)
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if trimmed == "continue" {
stmts.push(Stmt::Continue)
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if trimmed == "yield" {
stmts.push(Stmt::Yield(None))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if trimmed.has_prefix("yield from") {
let rest = substring(trimmed, 10, trimmed.length())
.trim(chars=" \t")
.to_owned()
if rest.length() == 0 {
let token = {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
let tokens = match
tokenize_expr(
rest,
line_index + 1,
base_offset + 10,
base_offset + 10 - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let expr = match parse_tokens_expr(tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
stmts.push(Stmt::YieldFrom(expr))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if trimmed.has_prefix("yield ") || trimmed.has_prefix("yield\t") {
let rest = substring(trimmed, 5, trimmed.length())
.trim(chars=" \t")
.to_owned()
if rest.length() == 0 {
stmts.push(Stmt::Yield(None))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
let tokens = match
tokenize_expr(
rest,
line_index + 1,
base_offset + 5,
base_offset + 5 - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let expr = match parse_tokens_expr(tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
stmts.push(Stmt::Yield(Some(expr)))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if trimmed.has_prefix("{") && trimmed.contains(" for ") {
let token = {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
}
if trimmed.contains("+=") {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"'dict comprehension' is an illegal expression for augmented assignment",
token,
),
)
}
if !trimmed.contains(":") && has_bare_assign(trimmed) {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"cannot assign to dict comprehension here. Maybe you meant '==' instead of '='?",
token,
),
)
}
}
if trimmed.has_prefix("__builtins__['") && trimmed.contains("]=") {
let end_key = find_char(trimmed, ']')
if end_key is Some(end_key) {
let key = substring(trimmed, 14, end_key).trim(chars="'\"").to_owned()
let value_text = substring(trimmed, end_key + 2, trimmed.length())
.trim(chars=" \t")
.to_owned()
let tokens = match
tokenize_expr(
value_text,
line_index + 1,
base_offset + end_key + 2,
base_offset + end_key + 2 - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let expr = match parse_tokens_expr(tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
stmts.push(Stmt::Assign(target=Target::Name(key), value=expr))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
}
if trimmed.has_prefix("__debug__") && trimmed.contains("=") {
let eq_pos = find_char(trimmed, '=')
let pos = eq_pos.unwrap_or(0)
let token = {
kind: Eof,
start: base_offset + pos,
end: base_offset + pos + 1,
line: line_index + 1,
column: pos + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
if trimmed == "pass" {
stmts.push(Stmt::Pass)
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if trimmed.has_prefix("global ") {
let names : Array[String] = []
let rest = substring(trimmed, 7, trimmed.length())
for part in rest.split(",") {
let name = part.trim(chars=" \t").to_owned()
if name.length() > 0 {
names.push(name)
}
}
stmts.push(Stmt::Global(names))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if trimmed.has_prefix("nonlocal ") {
let names : Array[String] = []
let rest = substring(trimmed, 9, trimmed.length())
for part in rest.split(",") {
let name = part.trim(chars=" \t").to_owned()
if name.length() > 0 {
names.push(name)
}
}
stmts.push(Stmt::Nonlocal(names))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if trimmed.has_prefix("import ") {
let module_text = substring(trimmed, 7, trimmed.length())
.trim(chars=" \t")
.to_owned()
for item in module_text.split(",") {
let part = item.trim(chars=" \t").to_owned()
if part.length() == 0 {
continue
}
let mut module_name : String = ""
let mut alias_opt : String? = None
if part.contains(" as ") {
let pieces : Array[String] = []
for p in part.split(" as ") {
if p.length() > 0 {
pieces.push(p.trim(chars=" \t").to_owned())
}
}
if pieces.length() == 2 {
module_name = pieces[0]
alias_opt = Some(pieces[1])
} else {
module_name = part
}
} else {
module_name = part
}
if module_name == "." {
module_name = ""
}
stmts.push(Stmt::Import(module_name~, alias_name=alias_opt))
}
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if trimmed.has_prefix("from ") {
let tail_check = trimmed.trim(chars=" \t").to_owned()
if tail_check.has_suffix(",") && !tail_check.contains("(") {
let comma_pos = find_last_char(tail_check, ',')
match comma_pos {
Some(pos) => {
let token = {
kind: Eof,
start: base_offset + pos,
end: base_offset + pos + 1,
line: line_index + 1,
column: pos + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"trailing comma not allowed without surrounding parentheses".to_string(),
token,
),
)
}
None => ()
}
}
let parts : Array[String] = []
for part in trimmed.split(" ") {
if part.length() > 0 {
parts.push(part.to_owned())
}
}
let token = {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
}
if parts.length() < 2 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
let mut module_name = parts[1]
if module_name == "." {
module_name = ""
}
if module_name == ".__future__" {
if parts.length() >= 4 {
let feature = parts[3]
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"future feature " + feature + " is not defined",
token,
),
)
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
let import_pos = match find_top_level_import(trimmed) {
Some(pos) => pos
None =>
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
if import_pos + 6 > trimmed.length() {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
let mut imported = substring(trimmed, import_pos + 6, trimmed.length())
.trim(chars=" \t\n\r")
.to_owned()
if imported.has_prefix("(") && imported.has_suffix(")") {
imported = substring(imported, 1, imported.length() - 1)
.trim(chars=" \t\n\r")
.to_owned()
}
let names : Array[(String, String?)] = []
if imported == "*" {
names.push(("*", (None : String?)))
} else {
for item in imported.split(",") {
let part = item.trim(chars=" \t\n\r").to_owned()
if part.length() == 0 {
continue
}
if part.contains(" as ") {
let pieces : Array[String] = []
for p in part.split(" as ") {
if p.length() > 0 {
pieces.push(p.trim(chars=" \t\n\r").to_owned())
}
}
if pieces.length() != 2 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
names.push((pieces[0], Some(pieces[1])))
} else {
names.push((part, (None : String?)))
}
}
}
stmts.push(Stmt::FromImport(module_name~, names~))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if trimmed.has_prefix("type ") {
let rest = substring(trimmed, 5, trimmed.length())
.trim(chars=" \t")
.to_owned()
if !rest.has_prefix("=") {
if rest.length() == 0 {
let token = {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: base_offset - line_start_offset + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
let chars = rest.to_array()
let mut quote : Char? = None
let mut escaped = false
let mut depth = 0
let mut eq_pos : Int32? = None
for i = 0; i < chars.length(); i = i + 1 {
let c = chars[i]
match quote {
Some(q) =>
if escaped {
escaped = false
} else if c == '\\' {
escaped = true
} else if c == q {
quote = None
}
None =>
if c == '\'' || c == '"' {
quote = Some(c)
escaped = false
} else if c == '(' || c == '[' || c == '{' {
depth += 1
} else if c == ')' || c == ']' || c == '}' {
if depth > 0 {
depth -= 1
}
} else if c == '=' && depth == 0 {
eq_pos = Some(i)
break
}
}
}
if eq_pos is None {
let token = {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: base_offset - line_start_offset + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
let eq_pos = eq_pos.unwrap()
let name_raw = substring(rest, 0, eq_pos)
let value_raw = substring(rest, eq_pos + 1, rest.length())
let mut name_leading = 0
let name_raw_chars = name_raw.to_array()
while name_leading < name_raw_chars.length() &&
name_raw_chars[name_leading] == ' ' {
name_leading += 1
}
let name_text = name_raw.trim(chars=" \t").to_owned()
let mut value_leading = 0
let value_raw_chars = value_raw.to_array()
while value_leading < value_raw_chars.length() &&
value_raw_chars[value_leading] == ' ' {
value_leading += 1
}
let value_text = value_raw.trim(chars=" \t").to_owned()
if name_text.length() == 0 || value_text.length() == 0 {
let token = {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: base_offset - line_start_offset + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
let name_token = {
kind: Eof,
start: base_offset + 5 + name_leading,
end: base_offset + 5 + name_leading + 1,
line: line_index + 1,
column: base_offset + 5 + name_leading - line_start_offset + 1,
}
let (name, type_params) = match
parse_type_param_header(name_text, name_token) {
Ok(value) => value
Err(err) => return Err(err)
}
let name_chars = name.to_array()
for i = 0; i < name_chars.length(); i = i + 1 {
let c = name_chars[i]
if !(is_alnum(c) || c == '_') || (i == 0 && is_digit(c)) {
let token = {
kind: Eof,
start: base_offset + 5 + name_leading + i,
end: base_offset + 5 + name_leading + i + 1,
line: line_index + 1,
column: base_offset + 5 + name_leading + i - line_start_offset + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
}
let value_offset = base_offset + 5 + eq_pos + 1 + value_leading
let tokens = match
tokenize_expr(
value_text,
line_index + 1,
value_offset,
value_offset - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let value_expr = match parse_tokens_expr(tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
stmts.push(Stmt::TypeAlias(name~, type_params~, value=value_expr))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
}
if trimmed.has_prefix("assert ") || trimmed.has_prefix("assert(") {
let raw_rest = substring(trimmed, 6, trimmed.length()).to_string()
let rest_leading = count_indent(raw_rest)
let rest = raw_rest.trim(chars=" \t").to_owned()
let rest_base = base_offset + 6 + rest_leading
let rest_column = rest_base - line_start_offset + 1
if rest.has_prefix("(") {
let condition_tokens = match
tokenize_expr(rest, line_index + 1, rest_base, rest_column) {
Ok(value) => value
Err(err) => return Err(err)
}
let condition = match parse_tokens_expr(condition_tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
let message : Expr? = None
stmts.push(Stmt::Assert(condition~, message~))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
let mut comma_index : Int32? = None
let chars = rest.to_array()
let mut depth = 0
let mut quote : Char? = None
let mut escape = false
for i = 0; i < chars.length(); i = i + 1 {
let c = chars[i]
match quote {
Some(q) =>
if escape {
escape = false
} else if c == '\\' {
escape = true
} else if c == q {
quote = None
}
None =>
if c == '\'' || c == '"' {
quote = Some(c)
escape = false
} else if c == '(' || c == '[' || c == '{' {
depth += 1
} else if c == ')' || c == ']' || c == '}' {
if depth > 0 {
depth -= 1
}
} else if c == ',' && depth == 0 {
comma_index = Some(i)
break
}
}
}
let condition_text = match comma_index {
Some(idx) => substring(rest, 0, idx).trim(chars=" \t").to_owned()
None => rest.trim(chars=" \t").to_owned()
}
let message_text = match comma_index {
Some(idx) =>
substring(rest, idx + 1, rest.length()).trim(chars=" \t").to_owned()
None => ""
}
let condition_leading = count_indent(
match comma_index {
Some(idx) => substring(rest, 0, idx).to_string()
None => rest
},
)
let condition_base = rest_base + condition_leading
let condition_column = condition_base - line_start_offset + 1
let condition_tokens = match
tokenize_expr(
condition_text,
line_index + 1,
condition_base,
condition_column,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let condition = match parse_tokens_expr(condition_tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
if message_text.length() == 0 {
let message : Expr? = None
stmts.push(Stmt::Assert(condition~, message~))
} else {
let message_leading = count_indent(
match comma_index {
Some(idx) => substring(rest, idx + 1, rest.length()).to_string()
None => rest
},
)
let message_base = match comma_index {
Some(idx) => rest_base + idx + 1 + message_leading
None => rest_base + message_leading
}
let message_column = message_base - line_start_offset + 1
let message_tokens = match
tokenize_expr(
message_text,
line_index + 1,
message_base,
message_column,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let message_expr = match parse_tokens_expr(message_tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
stmts.push(Stmt::Assert(condition~, message=Some(message_expr)))
}
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if has_keyword_prefix(trimmed, "raise") {
let rest = substring(trimmed, 5, trimmed.length())
.trim(chars=" \t")
.to_owned()
if rest.length() == 0 {
stmts.push(Stmt::Raise(exc=None, cause=None))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
let mut exc_text = rest
let mut cause_text = ""
let from_idx = find_top_level_from(rest)
if from_idx is Some(idx) {
if idx == 0 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
Token::{
kind: TokenKind::Eof,
start: 0,
end: 0,
line: line_index + 1,
column: base_offset + 1,
},
),
)
}
exc_text = substring(rest, 0, idx).trim(chars=" \t").to_owned()
cause_text = substring(rest, idx + 4, rest.length())
.trim(chars=" \t")
.to_owned()
if exc_text.length() == 0 || cause_text.length() == 0 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
Token::{
kind: TokenKind::Eof,
start: 0,
end: 0,
line: line_index + 1,
column: base_offset + 1,
},
),
)
}
}
let exc_tokens = match
tokenize_expr(
exc_text,
line_index + 1,
base_offset + 5,
base_offset + 5 - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let exc_expr = match parse_tokens_expr(exc_tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
let cause_expr = if cause_text.length() > 0 {
let cause_tokens = match
tokenize_expr(
cause_text,
line_index + 1,
base_offset + 5,
base_offset + 5 - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let expr = match parse_tokens_expr(cause_tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
Some(expr)
} else {
None
}
stmts.push(Stmt::Raise(exc=Some(exc_expr), cause=cause_expr))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if trimmed.has_prefix("del ") || trimmed.has_prefix("del(") {
let prefix_len = if trimmed.has_prefix("del(") { 3 } else { 4 }
let rest = substring(trimmed, prefix_len, trimmed.length())
let targets : Array[Target] = []
for part in split_top_level_commas(rest) {
let text = part.trim(chars=" \t").to_owned()
if text.length() == 0 {
continue
}
let tokens = match
tokenize_expr(
text,
line_index + 1,
base_offset + prefix_len,
base_offset + prefix_len - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let expr = match parse_tokens_expr(tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
let target = match target_from_expr(expr, tokens[0]) {
Ok(value) => value
Err(_) =>
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
{
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
},
),
)
}
targets.push(target)
}
stmts.push(Stmt::Del(targets))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if has_keyword_prefix(trimmed, "async") {
// Treat async constructs as no-ops so spec-generated blocks can parse.
let rest = substring(trimmed, 5, trimmed.length())
.trim(chars=" \t")
.to_owned()
if has_keyword_prefix(rest, "def") ||
has_keyword_prefix(rest, "for") ||
has_keyword_prefix(rest, "with") {
stmts.push(Stmt::Pass)
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
return Err(
make_parse_error(ParseErrorKind::Syntax, "invalid syntax".to_string(), {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
}),
)
}
if has_keyword_prefix(trimmed, "type") &&
(trimmed.has_prefix("type ") || trimmed.has_prefix("type\t")) &&
trimmed.contains("=") {
// PEP 695 `type` statements are currently parsed as no-ops.
stmts.push(Stmt::Pass)
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
if has_keyword_prefix(trimmed, "return") {
let raw_expr_text = substring(trimmed, 6, trimmed.length()).to_string()
let expr_text = raw_expr_text.trim(chars=" \t").to_owned()
if expr_text.length() == 0 {
stmts.push(Stmt::Return(None))
} else {
let tokens = match
tokenize_expr(
raw_expr_text,
line_index + 1,
base_offset + 6,
base_offset + 6 - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let expr = match parse_tokens_expr(tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
stmts.push(Stmt::Return(Some(expr)))
}
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
// PEP 526 annotated assignment.
if trimmed.contains(":") {
let colon_pos = find_top_level_char(trimmed, ':')
let eq_pos = find_top_level_char(trimmed, '=')
match (colon_pos, eq_pos) {
(Some(colon_pos), None) => {
let left_text = substring(trimmed, 0, colon_pos)
.trim(chars=" \t")
.to_owned()
// Avoid swallowing other colon-terminated statements like `match x:`
// and `if x:`: only treat it as an annotation when the left side parses
// as an assignment target.
let left_target = match
tokenize_expr(
left_text,
line_index + 1,
base_offset,
base_offset - line_start_offset + 1,
) {
Ok(tokens) =>
match parse_tokens_expr(tokens) {
Ok(expr) =>
match
target_from_expr(expr, {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
}) {
Ok(target) => Some(target)
Err(_) => None
}
Err(_) => None
}
Err(_) => None
}
match left_target {
Some(target) => {
match target {
Target::Name("__debug__")
| Target::Attribute(attr="__debug__", ..) =>
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"cannot assign to __debug__".to_string(),
{
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
},
),
)
_ => ()
}
if !target_allows_annotation(target) {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
{
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
},
),
)
}
let annotation_text = substring(
trimmed,
colon_pos + 1,
trimmed.length(),
)
.trim(chars=" \t")
.to_owned()
if annotation_text.length() == 0 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
{
kind: Eof,
start: base_offset + colon_pos,
end: base_offset + colon_pos + 1,
line: line_index + 1,
column: colon_pos + 1,
},
),
)
}
let annotation_expr = match
tokenize_expr(
annotation_text,
line_index + 1,
base_offset + colon_pos + 1,
base_offset + colon_pos + 1 - line_start_offset + 1,
) {
Ok(tokens) =>
match parse_tokens_expr(tokens) {
Ok(value) => value
Err(_) =>
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
{
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
},
),
)
}
Err(_) =>
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
{
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
},
),
)
}
stmts.push(
Stmt::AnnAssign(target~, annotation=annotation_expr, value=None),
)
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
None => ()
}
}
(Some(colon_pos), Some(eq_pos)) =>
if colon_pos < eq_pos {
let left_text = substring(trimmed, 0, colon_pos)
.trim(chars=" \t")
.to_owned()
let left_expr = match
tokenize_expr(
left_text,
line_index + 1,
base_offset,
base_offset - line_start_offset + 1,
) {
Ok(tokens) =>
match parse_tokens_expr(tokens) {
Ok(expr) => Some(expr)
Err(_) => None
}
Err(_) => None
}
match left_expr {
Some(left_expr) => {
let target_opt = match
target_from_expr(left_expr, {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
}) {
Ok(value) => Some(value)
// Not an assignment target; let the general statement parser handle it.
Err(_) => None
}
match target_opt {
Some(target) => {
match target {
Target::Name("__debug__")
| Target::Attribute(attr="__debug__", ..) =>
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"cannot assign to __debug__".to_string(),
{
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
},
),
)
_ => ()
}
if !target_allows_annotation(target) {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
{
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
},
),
)
}
let annotation_text = substring(
trimmed,
colon_pos + 1,
eq_pos,
)
.trim(chars=" \t")
.to_owned()
if annotation_text.length() == 0 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
{
kind: Eof,
start: base_offset + colon_pos,
end: base_offset + colon_pos + 1,
line: line_index + 1,
column: colon_pos + 1,
},
),
)
}
let ann_tokens = match
tokenize_expr(
annotation_text,
line_index + 1,
base_offset + colon_pos + 1,
base_offset + colon_pos + 1 - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let ann_expr = match parse_tokens_expr(ann_tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
let mut value_text = substring(
trimmed,
eq_pos + 1,
trimmed.length(),
)
.trim(chars=" \t")
.to_owned()
let comma_pos = find_char(value_text, ',')
if value_text.contains("*") && comma_pos is Some(comma_pos) {
value_text = substring(value_text, 0, comma_pos)
.trim(chars=" \t")
.to_owned()
}
let tokens = match
tokenize_expr(
value_text,
line_index + 1,
base_offset + eq_pos + 1,
base_offset + eq_pos + 1 - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let expr = match parse_tokens_expr(tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
stmts.push(
Stmt::AnnAssign(
target~,
annotation=ann_expr,
value=Some(expr),
),
)
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
None => ()
}
}
None => ()
}
}
_ => ()
}
}
if has_keyword_prefix(trimmed, "lambda") {
let tokens = match
tokenize_expr(
trimmed,
line_index + 1,
base_offset,
base_offset - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let expr = match parse_tokens_expr(tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
stmts.push(Stmt::ExprStmt(expr))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
match find_top_level_augassign(trimmed) {
Some((pos, op_text)) => {
let left_text = substring(trimmed, 0, pos).trim(chars=" \t").to_owned()
let right_text = substring(
trimmed,
pos + op_text.length(),
trimmed.length(),
)
.trim(chars=" \t")
.to_owned()
if left_text.length() == 0 || right_text.length() == 0 {
let token = {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
let left_tokens = match
tokenize_expr(
left_text,
line_index + 1,
base_offset,
base_offset - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let left_expr = match parse_tokens_expr(left_tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
let target = match
target_from_expr(left_expr, {
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
}) {
Ok(value) => value
Err(err) => return Err(err)
}
match target {
Target::Name(_) | Target::Attribute(_) | Target::Subscript(_) => ()
_ =>
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
{
kind: Eof,
start: base_offset,
end: base_offset + 1,
line: line_index + 1,
column: 1,
},
),
)
}
let right_tokens = match
tokenize_expr(
right_text,
line_index + 1,
base_offset + pos + op_text.length(),
base_offset + pos + op_text.length() - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let value_expr = match parse_tokens_expr(right_tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
let op = match op_text {
"+=" => BinaryOp::Add
"-=" => BinaryOp::Sub
"*=" => BinaryOp::Mul
"@=" => BinaryOp::MatMul
"/=" => BinaryOp::Div
"//=" => BinaryOp::FloorDiv
"%=" => BinaryOp::Mod
"**=" => BinaryOp::Pow
"<<=" => BinaryOp::ShiftLeft
">>=" => BinaryOp::ShiftRight
"&=" => BinaryOp::BitAnd
"^=" => BinaryOp::BitXor
"|=" => BinaryOp::BitOr
_ => BinaryOp::Add
}
stmts.push(Stmt::AugAssign(target~, op~, value=value_expr))
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}
None => ()
}
let tokens = match
tokenize_expr(
trimmed,
line_index + 1,
base_offset,
base_offset - line_start_offset + 1,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let assign_positions : Array[Int32] = []
let mut depth = 0
let mut lambda_depth = 0
for idx = 0; idx < tokens.length(); idx = idx + 1 {
let kind = tokens[idx].kind
if kind == LParen || kind == LBracket || kind == LBrace {
depth += 1
} else if kind == RParen || kind == RBracket || kind == RBrace {
if depth > 0 {
depth -= 1
}
} else if kind == Lambda && depth == 0 {
// `=` inside lambda parameter defaults must not be treated as assignment.
lambda_depth += 1
} else if kind == Colon && depth == 0 && lambda_depth > 0 {
// End of lambda header; the lambda body is a normal expression again.
lambda_depth -= 1
} else if kind == Assign && depth == 0 && lambda_depth == 0 {
assign_positions.push(idx)
}
}
if assign_positions.length() > 0 {
let last_idx = assign_positions[assign_positions.length() - 1]
let value_tokens = slice_tokens(tokens, last_idx + 1, tokens.length())
let value_expr = match parse_tokens_expr(value_tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
let temp_name : String? = if assign_positions.length() > 1 {
let next_id = assign_temp_id_ref.val
assign_temp_id_ref.val = next_id + 1
Some("$__mpython_assign_tmp__" + next_id.to_string())
} else {
None
}
match temp_name {
Some(name) =>
stmts.push(Stmt::Assign(target=Target::Name(name), value=value_expr))
None => ()
}
let assigned_expr = match temp_name {
Some(name) => Expr::Name(name)
None => value_expr
}
for seg = 0; seg < assign_positions.length(); seg = seg + 1 {
let start = if seg == 0 { 0 } else { assign_positions[seg - 1] + 1 }
let end = assign_positions[seg]
let target_tokens = slice_tokens(tokens, start, end)
if target_tokens.length() == 0 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
tokens[end],
),
)
}
target_tokens.push({
kind: Eof,
start: tokens[end].start,
end: tokens[end].start,
line: tokens[end].line,
column: tokens[end].column,
})
let token = if end > 0 { tokens[end - 1] } else { tokens[end] }
let target = match target_from_assignment_tokens(target_tokens, token) {
Ok(value) => value
Err(err) => return Err(err)
}
stmts.push(Stmt::Assign(target~, value=assigned_expr))
}
match temp_name {
Some(name) => stmts.push(Stmt::Del([Target::Name(name)]))
None => ()
}
} else {
let expr = match parse_tokens_expr(tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
stmts.push(Stmt::ExprStmt(expr))
}
return finalize_simple_statement(
stmts, allow_async_comprehensions, span, base_offset,
)
}