///|
/// Statement execution.

///|
priv enum BlockFlow {
  Normal(Value)
  Return(Value)
  Break
  Continue
}

///|
let active_exception_stack : Ref[Array[RuntimeError]] = { val: [], }

///|

///|
fn trim_runtime_error_for_active_handler(err : RuntimeError) -> RuntimeError {
  // Our RuntimeError captures a snapshot of the full active traceback stack at
  // raise-time. When an exception is caught, CPython's traceback stops at the
  // handler frame (it does not include frames above the handler).
  //
  // Trim away frames that are above the currently active handler frame.
  let active_len = active_frame_stack.val.length()
  if active_len <= 1 {
    return err
  }
  let drop = active_len - 1
  if err.traceback.length() <= drop {
    return err
  }
  let trimmed_tb : Array[TracebackFrame] = []
  for i = drop; i < err.traceback.length(); i = i + 1 {
    trimmed_tb.push(err.traceback[i])
  }
  let trimmed_envs : Array[TracebackEnv] = []
  if err.traceback_envs.length() == err.traceback.length() {
    for i = drop; i < err.traceback_envs.length(); i = i + 1 {
      trimmed_envs.push(err.traceback_envs[i])
    }
  }
  RuntimeError::{
    kind: err.kind,
    message: err.message,
    span: err.span,
    traceback: trimmed_tb,
    traceback_envs: trimmed_envs,
    exc_type: err.exc_type,
    exc_args: err.exc_args,
    exc_value: err.exc_value,
    exc_cause: err.exc_cause,
    exc_context: err.exc_context,
    exc_suppress_context: err.exc_suppress_context,
  }
}

///|
fn active_exception() -> RuntimeError? {
  if active_exception_stack.val.length() == 0 {
    None
  } else {
    Some(active_exception_stack.val[active_exception_stack.val.length() - 1])
  }
}

///|
fn is_base_exception_class(klass : ClassValue) -> Result[Bool, RuntimeError] {
  let mro = match class_mro(klass) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  for cls in mro {
    if cls.name == "BaseException" {
      return Ok(true)
    }
  }
  Ok(false)
}

///|
fn runtime_error_type_matches_handler_name(
  err_type : String,
  handler_name : String,
) -> Bool {
  if err_type == handler_name {
    return true
  }
  match handler_name {
    "BaseException" => true
    "Exception" =>
      err_type != "BaseException" &&
      err_type != "BaseExceptionGroup" &&
      err_type != "SystemExit" &&
      err_type != "KeyboardInterrupt" &&
      err_type != "GeneratorExit"
    "ArithmeticError" =>
      err_type == "ZeroDivisionError" ||
      err_type == "OverflowError" ||
      err_type == "FloatingPointError"
    "LookupError" => err_type == "IndexError" || err_type == "KeyError"
    "NameError" => err_type == "UnboundLocalError"
    "ValueError" =>
      err_type == "UnicodeError" ||
      err_type == "UnicodeDecodeError" ||
      err_type == "UnicodeEncodeError" ||
      err_type == "UnicodeTranslateError"
    "OSError" =>
      err_type == "BlockingIOError" ||
      err_type == "ChildProcessError" ||
      err_type == "ConnectionError" ||
      err_type == "BrokenPipeError" ||
      err_type == "ConnectionAbortedError" ||
      err_type == "ConnectionRefusedError" ||
      err_type == "ConnectionResetError" ||
      err_type == "InterruptedError" ||
      err_type == "ProcessLookupError" ||
      err_type == "TimeoutError" ||
      err_type == "FileNotFoundError" ||
      err_type == "FileExistsError" ||
      err_type == "PermissionError" ||
      err_type == "IsADirectoryError" ||
      err_type == "NotADirectoryError"
    "ImportError" => err_type == "ModuleNotFoundError"
    "RuntimeError" => err_type == "RecursionError"
    "SyntaxError" => err_type == "IndentationError" || err_type == "TabError"
    _ => false
  }
}

///|
fn ensure_exception_instance_from_value(
  value : Value,
  globals : Array[(String, Value)],
  builtins : Array[(String, Value)],
  io : MockIO,
) -> Result[Value, RuntimeError] {
  match value {
    Value::Instance(inst) => {
      let ok = match is_base_exception_class(inst.class) {
        Ok(v) => v
        Err(err) => return Err(err)
      }
      if ok {
        Ok(Value::Instance(inst))
      } else {
        Err(
          make_runtime_error(
            RuntimeErrorKind::Type,
            "exceptions must derive from BaseException".to_string(),
          ),
        )
      }
    }
    Value::Class(klass) => {
      let ok = match is_base_exception_class(klass) {
        Ok(v) => v
        Err(err) => return Err(err)
      }
      if !ok {
        return Err(
          make_runtime_error(
            RuntimeErrorKind::Type,
            "exceptions must derive from BaseException".to_string(),
          ),
        )
      }
      let inst_value = match
        call_callable_with_env(
          Value::Class(klass),
          [],
          [],
          globals,
          builtins,
          io,
        ) {
        Ok(value) => value
        Err(err) => return Err(err)
      }
      match inst_value {
        Value::Instance(_) => Ok(inst_value)
        _ =>
          Err(
            make_runtime_error(
              RuntimeErrorKind::Type,
              "exceptions must derive from BaseException".to_string(),
            ),
          )
      }
    }
    _ =>
      Err(
        make_runtime_error(
          RuntimeErrorKind::Type,
          "exceptions must derive from BaseException".to_string(),
        ),
      )
  }
}

///|
fn exception_matches_handler(
  err : RuntimeError,
  handler_class : ClassValue,
  builtins : Array[(String, Value)],
) -> Result[Bool, RuntimeError] {
  if handler_class.name == "BaseException" {
    return Ok(true)
  }
  match err.exc_value {
    Some(Value::Instance(_)) | Some(Value::Class(_)) =>
      return exception_value_matches_handler(
        err.exc_value.unwrap(),
        handler_class,
        builtins,
      )
    _ => ()
  }
  match get_named_value(builtins, err.exc_type) {
    Some(Value::Class(err_class)) => {
      if err_class.name == handler_class.name {
        return Ok(true)
      }
      let mro = match class_mro(err_class) {
        Ok(value) => value
        Err(e) => return Err(e)
      }
      for cls in mro {
        if cls.name == handler_class.name {
          return Ok(true)
        }
      }
      Ok(false)
    }
    _ =>
      Ok(
        runtime_error_type_matches_handler_name(
          err.exc_type,
          handler_class.name,
        ),
      )
  }
}

///|
fn exception_value_matches_handler(
  exc_value : Value,
  handler_class : ClassValue,
  builtins : Array[(String, Value)],
) -> Result[Bool, RuntimeError] {
  let _ = builtins
  if handler_class.name == "BaseException" {
    return Ok(true)
  }
  let exc_class = match exc_value {
    Value::Instance(inst) => Some(inst.class)
    Value::Class(klass) => Some(klass)
    _ => None
  }
  match exc_class {
    Some(err_class) => {
      if err_class.name == handler_class.name {
        return Ok(true)
      }
      let mro = match class_mro(err_class) {
        Ok(value) => value
        Err(e) => return Err(e)
      }
      for cls in mro {
        if cls.name == handler_class.name {
          return Ok(true)
        }
      }
      Ok(false)
    }
    None => Ok(false)
  }
}

///|
fn handler_classes_from_value(
  value : Value,
  builtins : Array[(String, Value)],
) -> Result[Array[ClassValue], RuntimeError] {
  let classes : Array[ClassValue] = []
  let _ = builtins
  fn ensure_handler_class(klass : ClassValue) -> Result[Unit, RuntimeError] {
    let ok = match is_base_exception_class(klass) {
      Ok(v) => v
      Err(err) => return Err(err)
    }
    if ok {
      Ok(())
    } else {
      Err(
        make_runtime_error(
          RuntimeErrorKind::Type,
          "catching classes that do not inherit from BaseException is not allowed".to_string(),
        ),
      )
    }
  }

  match value {
    Value::Class(klass) => {
      let _ = match ensure_handler_class(klass) {
        Ok(v) => v
        Err(err) => return Err(err)
      }
      classes.push(klass)
    }
    Value::Tuple(items) =>
      for item in items {
        match item {
          Value::Class(klass) => {
            let _ = match ensure_handler_class(klass) {
              Ok(v) => v
              Err(err) => return Err(err)
            }
            classes.push(klass)
          }
          _ =>
            return Err(
              make_runtime_error(
                RuntimeErrorKind::Type,
                "catching classes that do not inherit from BaseException is not allowed".to_string(),
              ),
            )
        }
      }
    _ =>
      return Err(
        make_runtime_error(
          RuntimeErrorKind::Type,
          "catching classes that do not inherit from BaseException is not allowed".to_string(),
        ),
      )
  }
  Ok(classes)
}

///|
fn exception_group_message_value(inst : InstanceValue) -> Value {
  match get_named_value(inst.dict, "message") {
    Some(v) => v
    None => Value::Str("")
  }
}

///|
fn exception_group_exceptions(
  inst : InstanceValue,
) -> Result[Array[Value], RuntimeError] {
  match get_named_value(inst.dict, "exceptions") {
    Some(Value::List(values)) => Ok(values)
    Some(Value::Tuple(values)) => Ok(values)
    _ =>
      Err(
        make_runtime_error(
          RuntimeErrorKind::Type,
          "ExceptionGroup exceptions must be a list".to_string(),
        ),
      )
  }
}

///|
fn exception_group_make(
  klass : ClassValue,
  message : Value,
  exceptions : Array[Value],
  globals : Array[(String, Value)],
  builtins : Array[(String, Value)],
  io : MockIO,
) -> Result[Value, RuntimeError] {
  call_callable_with_env(
    Value::Class(klass),
    [message, Value::List(exceptions)],
    [],
    globals,
    builtins,
    io,
  )
}

///|
fn exception_group_split_by_classes(
  group_value : Value,
  handler_classes : Array[ClassValue],
  globals : Array[(String, Value)],
  builtins : Array[(String, Value)],
  io : MockIO,
) -> Result[(Value?, Value?), RuntimeError] {
  fn ensure_group_instance(
    value : Value,
  ) -> Result[InstanceValue, RuntimeError] {
    match value {
      Value::Instance(inst) => {
        if inst.class.name != "ExceptionGroup" &&
          inst.class.name != "BaseExceptionGroup" {
          return Err(
            make_runtime_error(
              RuntimeErrorKind::Type,
              "except* requires an ExceptionGroup".to_string(),
            ),
          )
        }
        Ok(inst)
      }
      _ =>
        Err(
          make_runtime_error(
            RuntimeErrorKind::Type,
            "except* requires an ExceptionGroup".to_string(),
          ),
        )
    }
  }

  fn copy_group_metadata(src : InstanceValue, dst : InstanceValue) -> Unit {
    for
      key in [
        "__traceback__", "__cause__", "__context__", "__suppress_context__", "__notes__",
      ] {
      match get_named_value(src.dict, key) {
        Some(v) => set_named_value(dst.dict, key, v)
        None => ()
      }
    }
  }

  if handler_classes.length() == 0 {
    return Ok((None, Some(group_value)))
  }
  let inst = match ensure_group_instance(group_value) {
    Ok(v) => v
    Err(err) => return Err(err)
  }
  let message = exception_group_message_value(inst)
  let exceptions = match exception_group_exceptions(inst) {
    Ok(v) => v
    Err(err) => return Err(err)
  }
  let matched : Array[Value] = []
  let remaining : Array[Value] = []
  for exc in exceptions {
    // Preserve nested group structure by splitting recursively.
    match exc {
      Value::Instance(nested_inst) =>
        if nested_inst.class.name == "ExceptionGroup" ||
          nested_inst.class.name == "BaseExceptionGroup" {
          let (m_opt, r_opt) = match
            exception_group_split_by_classes(
              Value::Instance(nested_inst),
              handler_classes,
              globals,
              builtins,
              io,
            ) {
            Ok(v) => v
            Err(err) => return Err(err)
          }
          match m_opt {
            Some(v) => matched.push(v)
            None => ()
          }
          match r_opt {
            Some(v) => remaining.push(v)
            None => ()
          }
          continue
        }
      _ => ()
    }
    let mut is_match = false
    for klass in handler_classes {
      match exception_value_matches_handler(exc, klass, builtins) {
        Ok(true) => {
          is_match = true
          break
        }
        Ok(false) => ()
        Err(err) => return Err(err)
      }
    }
    if is_match {
      matched.push(exc)
    } else {
      remaining.push(exc)
    }
  }
  if matched.length() == 0 {
    return Ok((None, Some(group_value)))
  }
  let matched_group_value = match
    exception_group_make(inst.class, message, matched, globals, builtins, io) {
    Ok(v) => v
    Err(err) => return Err(err)
  }
  match matched_group_value {
    Value::Instance(dst) => copy_group_metadata(inst, dst)
    _ => ()
  }
  let remaining_group_value : Value? = if remaining.length() > 0 {
    let rem_value = match
      exception_group_make(
        inst.class,
        message,
        remaining,
        globals,
        builtins,
        io,
      ) {
      Ok(v) => v
      Err(err) => return Err(err)
    }
    match rem_value {
      Value::Instance(dst) => copy_group_metadata(inst, dst)
      _ => ()
    }
    Some(rem_value)
  } else {
    None
  }
  Ok((Some(matched_group_value), remaining_group_value))
}

///|
fn exception_group_from_runtime_error(
  err : RuntimeError,
  globals : Array[(String, Value)],
  builtins : Array[(String, Value)],
  io : MockIO,
) -> Result[Value, RuntimeError] {
  let err_value = exception_value_from_runtime_error(err, globals, builtins, io)
  if err.exc_type == "ExceptionGroup" || err.exc_type == "BaseExceptionGroup" {
    match err_value {
      Value::Instance(inst) => return Ok(Value::Instance(inst))
      _ => ()
    }
  }
  let exc_value = match
    ensure_exception_instance_from_value(err_value, globals, builtins, io) {
    Ok(v) => v
    Err(err) => return Err(err)
  }
  match get_named_value(builtins, "ExceptionGroup") {
    Some(Value::Class(klass)) =>
      exception_group_make(
        klass,
        Value::Str(""),
        [exc_value],
        globals,
        builtins,
        io,
      )
    _ =>
      Err(
        make_runtime_error(
          RuntimeErrorKind::Runtime,
          "RuntimeError: ExceptionGroup is not available".to_string(),
        ),
      )
  }
}

///|
fn runtime_error_from_exception_instance(inst : InstanceValue) -> RuntimeError {
  fn kind_from_exception_name(name : String) -> RuntimeErrorKind {
    match name {
      "ZeroDivisionError" => RuntimeErrorKind::ZeroDivision
      "NameError" | "UnboundLocalError" => RuntimeErrorKind::Name
      "TypeError" => RuntimeErrorKind::Type
      "AttributeError" => RuntimeErrorKind::Attribute
      "IndexError" => RuntimeErrorKind::Index
      "KeyError" => RuntimeErrorKind::Key
      "NotImplementedError" => RuntimeErrorKind::NotImplemented
      _ => RuntimeErrorKind::Runtime
    }
  }

  fn traceback_from_exception_instance(
    inst : InstanceValue,
  ) -> Array[TracebackFrame]? {
    let mut tb_value = match get_named_value(inst.dict, "__traceback__") {
      Some(Value::None) => return None
      Some(v) => v
      None => return None
    }
    let frames : Array[TracebackFrame] = []
    // Avoid infinite loops if user code mutates `__traceback__` into a cycle.
    let mut steps = 0
    while steps < 2048 {
      steps = steps + 1
      match tb_value {
        Value::None => break
        Value::Instance(tb_inst) => {
          let (name, filename) = match
            get_named_value(tb_inst.dict, "tb_frame") {
            Some(Value::Instance(frame_inst)) => {
              let name = match get_named_value(frame_inst.dict, "f_name") {
                Some(Value::Str(v)) => v
                _ => "".to_string()
              }
              let filename = match
                get_named_value(frame_inst.dict, "f_filename") {
                Some(Value::Str(v)) => v
                _ => "".to_string()
              }
              (name, filename)
            }
            _ => ("".to_string(), "".to_string())
          }
          let line = match get_named_value(tb_inst.dict, "tb_lineno") {
            Some(Value::Int(v)) =>
              match bigint_to_int_checked(v) {
                Ok(n) => n
                Err(_) => 1
              }
            _ => 1
          }
          let column = match get_named_value(tb_inst.dict, "tb_colno") {
            Some(Value::Int(v)) =>
              match bigint_to_int_checked(v) {
                Ok(n) => n
                Err(_) => 1
              }
            _ => 1
          }
          frames.push(TracebackFrame::{
            name,
            filename,
            line,
            column,
            end_column: 0,
          })
          tb_value = match get_named_value(tb_inst.dict, "tb_next") {
            Some(v) => v
            None => Value::None
          }
        }
        _ => return None
      }
    }
    if frames.length() == 0 {
      None
    } else {
      Some(frames)
    }
  }

  let mut message = "".to_string()
  match get_named_value(inst.dict, "message") {
    Some(Value::Str(text)) => message = text
    Some(value) => message = value_to_string(value)
    None => ()
  }
  let kind = kind_from_exception_name(inst.class.name)
  // `RuntimeErrorKind` controls how we format errors. For "structured" kinds
  // (TypeError/AttributeError/...), keep `message` as the payload only; for
  // generic runtime errors, keep the fully-qualified "ExcType: msg" form.
  let printed = match kind {
    RuntimeErrorKind::Runtime =>
      if message.length() > 0 {
        inst.class.name + ": " + message
      } else {
        inst.class.name
      }
    _ => message
  }
  let args : Array[String] = if message.length() > 0 { [message] } else { [] }
  let exc_value : Value? = Some(Value::Instance(inst))
  let exc_cause = match get_named_value(inst.dict, "__cause__") {
    Some(Value::None) => (None : Value?)
    Some(value) => Some(value)
    None => (None : Value?)
  }
  let exc_context = match get_named_value(inst.dict, "__context__") {
    Some(Value::None) => (None : Value?)
    Some(value) => Some(value)
    None => (None : Value?)
  }
  let exc_suppress_context = match
    get_named_value(inst.dict, "__suppress_context__") {
    Some(Value::Bool(v)) => v
    _ => false
  }
  let traceback = match traceback_from_exception_instance(inst) {
    Some(frames) => frames
    None => snapshot_traceback()
  }
  let traceback_envs = match traceback_from_exception_instance(inst) {
    Some(_) => []
    None => snapshot_frame_env_stack()
  }
  RuntimeError::{
    kind,
    message: printed,
    span: current_span(),
    traceback,
    traceback_envs,
    exc_type: inst.class.name,
    exc_args: args,
    exc_value,
    exc_cause,
    exc_context,
    exc_suppress_context,
  }
}

///|
fn make_frame_value(frame : TracebackFrame, builtins_dict : Value) -> Value {
  let globals_pairs : Array[(Value, Value)] = []
  if frame.filename.contains("/Lib/unittest/") ||
    frame.filename.contains("\\Lib\\unittest\\") {
    globals_pairs.push((Value::Str("__unittest"), Value::Bool(true)))
  }
  let frame_class_dict : Array[(String, Value)] = [
    (
      "clear",
      Value::Function(FunctionValue::{
        name: "frame.clear",
        params: ["self"],
        defaults: [],
        body: [],
        is_generator: false,
        is_async: false,
        closure: [],
      }),
    ),
  ]
  // We don't currently model real frame locals; keep a small placeholder dict so
  // `traceback.clear_frames()` can exercise `frame.clear()` semantics.
  let locals_pairs : Array[(Value, Value)] = [
    (Value::Str("__mpython_locals__"), Value::None),
  ]
  let code_value = Value::Instance(InstanceValue::{
    class: ClassValue::{ name: "code", bases: [], dict: [], },
    dict: [
      ("co_filename", Value::Str(frame.filename)),
      ("co_name", Value::Str(frame.name)),
      ("co_firstlineno", Value::Int(@bigint.BigInt::from_int(frame.line))),
      (
        "__mpython_co_positions",
        Value::List([
          Value::Tuple([Value::None, Value::None, Value::None, Value::None]),
        ]),
      ),
    ],
  })
  Value::Instance(InstanceValue::{
    class: ClassValue::{ name: "frame", bases: [], dict: frame_class_dict, },
    dict: [
      ("f_name", Value::Str(frame.name)),
      ("f_filename", Value::Str(frame.filename)),
      ("f_lineno", Value::Int(@bigint.BigInt::from_int(frame.line))),
      ("f_colno", Value::Int(@bigint.BigInt::from_int(frame.column))),
      ("f_code", code_value),
      ("f_back", Value::None),
      // Used by parts of the CPython stdlib (e.g. unittest) when cleaning tracebacks.
      ("f_globals", Value::Dict(globals_pairs)),
      ("f_builtins", builtins_dict),
      ("f_locals", Value::Dict(locals_pairs)),
    ],
  })
}

///|
fn filter_frame_builtins(
  builtins : Array[(String, Value)],
) -> Array[(String, Value)] {
  // The interpreter's builtin dispatch table includes many internal/dotted
  // helper names (e.g. "str.join"). For suggestion formatting (traceback.py),
  // expose a CPython-like subset to keep candidate sets small.
  let out : Array[(String, Value)] = []
  for pair in builtins {
    let name = pair.0
    if name.contains(".") || name.has_prefix("__mpython") {
      continue
    }
    out.push(pair)
  }
  out
}

///|
fn frame_builtins_dict_from_builtins(
  builtins : Array[(String, Value)],
) -> Value {
  let pairs : Array[(Value, Value)] = []
  for pair in filter_frame_builtins(builtins) {
    pairs.push((Value::Str(pair.0), pair.1))
  }
  Value::Dict(pairs)
}

///|
fn make_traceback_value(
  frames : Array[TracebackFrame],
  builtins_dict : Value,
  span : Span?,
) -> Value {
  if frames.length() == 0 {
    // Some stdlib helpers expect `tb_frame.f_code` etc to exist even when the
    // interpreter doesn't have a real stack. Provide a minimal placeholder.
    let frame_value = make_frame_value(
      TracebackFrame::{
        name: "",
        filename: "",
        line: 1,
        column: 1,
        end_column: 0,
      },
      builtins_dict,
    )
    return Value::Instance(InstanceValue::{
      class: ClassValue::{ name: "traceback", bases: [], dict: [], },
      dict: [
        ("tb_frame", frame_value),
        ("tb_lineno", Value::Int(1N)),
        ("tb_colno", Value::Int(1N)),
        ("tb_next", Value::None),
        ("tb_lasti", Value::Int(@bigint.BigInt::from_int(-1))),
      ],
    })
  }
  let mut next_value : Value = Value::None
  let mut i = frames.length()
  while i > 0 {
    i = i - 1
    let frame = frames[i]
    let frame_value = make_frame_value(frame, builtins_dict)
    let is_deepest = i == frames.length() - 1
    let mut tb_lasti = -1
    let mut col0_opt : Int32? = None
    let mut end0_opt : Int32? = None
    if frame.end_column > 0 {
      let mut col0 = frame.column - 1
      if col0 < 0 {
        col0 = 0
      }
      let mut end0 = frame.end_column - 1
      if end0 <= col0 {
        end0 = col0 + 1
      }
      tb_lasti = 0
      col0_opt = Some(col0)
      end0_opt = Some(end0)
    } else if is_deepest {
      // Fallback for runtimes that only provide the deepest span.
      match span {
        Some(s) => {
          let mut col0 = s.column - 1
          if col0 < 0 {
            col0 = 0
          }
          let mut width = s.end - s.start
          if width <= 0 {
            width = 1
          }
          tb_lasti = 0
          col0_opt = Some(col0)
          end0_opt = Some(col0 + width)
        }
        None => ()
      }
    }
    match (col0_opt, end0_opt) {
      (Some(col0), Some(end0)) =>
        match frame_value {
          Value::Instance(inst) =>
            match get_named_value(inst.dict, "f_code") {
              Some(Value::Instance(code_inst)) =>
                set_named_value(
                  code_inst.dict,
                  "__mpython_co_positions",
                  Value::List([
                    Value::Tuple([
                      Value::None,
                      Value::None,
                      Value::Int(@bigint.BigInt::from_int(col0)),
                      Value::Int(@bigint.BigInt::from_int(end0)),
                    ]),
                  ]),
                )
              _ => ()
            }
          _ => ()
        }
      _ => ()
    }
    let dict : Array[(String, Value)] = []
    dict.push(("tb_frame", frame_value))
    dict.push(("tb_lineno", Value::Int(@bigint.BigInt::from_int(frame.line))))
    dict.push(("tb_colno", Value::Int(@bigint.BigInt::from_int(frame.column))))
    dict.push(("tb_lasti", Value::Int(@bigint.BigInt::from_int(tb_lasti))))
    dict.push(("tb_next", next_value))
    next_value = Value::Instance(InstanceValue::{
      class: ClassValue::{ name: "traceback", bases: [], dict: [], },
      dict,
    })
  }
  next_value
}

///|
fn make_traceback_value_with_env(
  frames : Array[TracebackFrame],
  envs : Array[TracebackEnv],
  builtins_dict : Value,
  span : Span?,
) -> Value {
  if frames.length() == 0 {
    return make_traceback_value(frames, builtins_dict, span)
  }
  if envs.length() != frames.length() {
    return make_traceback_value(frames, builtins_dict, span)
  }
  let mut next_value : Value = Value::None
  let mut i = frames.length()
  while i > 0 {
    i = i - 1
    let frame = frames[i]
    let env = envs[i]
    let frame_value = make_frame_value(frame, builtins_dict)
    let is_deepest = i == frames.length() - 1
    let mut tb_lasti = -1
    let mut col0_opt : Int32? = None
    let mut end0_opt : Int32? = None
    if frame.end_column > 0 {
      let mut col0 = frame.column - 1
      if col0 < 0 {
        col0 = 0
      }
      let mut end0 = frame.end_column - 1
      if end0 <= col0 {
        end0 = col0 + 1
      }
      tb_lasti = 0
      col0_opt = Some(col0)
      end0_opt = Some(end0)
    } else if is_deepest {
      match span {
        Some(s) => {
          let mut col0 = s.column - 1
          if col0 < 0 {
            col0 = 0
          }
          let mut width = s.end - s.start
          if width <= 0 {
            width = 1
          }
          tb_lasti = 0
          col0_opt = Some(col0)
          end0_opt = Some(col0 + width)
        }
        None => ()
      }
    }
    match (col0_opt, end0_opt) {
      (Some(col0), Some(end0)) =>
        match frame_value {
          Value::Instance(inst) =>
            match get_named_value(inst.dict, "f_code") {
              Some(Value::Instance(code_inst)) =>
                set_named_value(
                  code_inst.dict,
                  "__mpython_co_positions",
                  Value::List([
                    Value::Tuple([
                      Value::None,
                      Value::None,
                      Value::Int(@bigint.BigInt::from_int(col0)),
                      Value::Int(@bigint.BigInt::from_int(end0)),
                    ]),
                  ]),
                )
              _ => ()
            }
          _ => ()
        }
      _ => ()
    }
    match frame_value {
      Value::Instance(inst) => {
        let locals_pairs : Array[(Value, Value)] = []
        for pair in env.locals {
          if pair.0.has_prefix("$__") {
            continue
          }
          let value_opt = if is_cell_value(pair.1) {
            cell_get_value(pair.1)
          } else {
            Some(pair.1)
          }
          match value_opt {
            Some(v) => locals_pairs.push((Value::Str(pair.0), v))
            None => ()
          }
        }
        set_named_value(inst.dict, "f_locals", Value::Dict(locals_pairs))
        let globals_pairs : Array[(Value, Value)] = []
        for pair in env.globals {
          if pair.0.has_prefix("$__") {
            continue
          }
          globals_pairs.push((Value::Str(pair.0), pair.1))
        }
        // Preserve any pre-populated globals markers (e.g. __unittest).
        match get_named_value(inst.dict, "f_globals") {
          Some(Value::Dict(existing)) =>
            for gp in globals_pairs {
              existing.push(gp)
            }
          _ =>
            set_named_value(inst.dict, "f_globals", Value::Dict(globals_pairs))
        }
        let tb_value = Value::Instance(InstanceValue::{
          class: ClassValue::{ name: "traceback", bases: [], dict: [], },
          dict: [
            ("tb_frame", Value::Instance(inst)),
            ("tb_lineno", Value::Int(@bigint.BigInt::from_int(frame.line))),
            ("tb_colno", Value::Int(@bigint.BigInt::from_int(frame.column))),
            ("tb_next", next_value),
            ("tb_lasti", Value::Int(@bigint.BigInt::from_int(tb_lasti))),
          ],
        })
        next_value = tb_value
      }
      _ => {
        // Fallback: keep the original behavior.
        let tb_value = Value::Instance(InstanceValue::{
          class: ClassValue::{ name: "traceback", bases: [], dict: [], },
          dict: [
            ("tb_frame", frame_value),
            ("tb_lineno", Value::Int(@bigint.BigInt::from_int(frame.line))),
            ("tb_colno", Value::Int(@bigint.BigInt::from_int(frame.column))),
            ("tb_next", next_value),
            ("tb_lasti", Value::Int(@bigint.BigInt::from_int(tb_lasti))),
          ],
        })
        next_value = tb_value
      }
    }
  }
  next_value
}

///|
fn exception_value_from_runtime_error(
  err : RuntimeError,
  globals : Array[(String, Value)],
  builtins : Array[(String, Value)],
  io : MockIO,
) -> Value {
  let frame_builtins_dict = frame_builtins_dict_from_builtins(builtins)
  fn maybe_populate_os_error(
    dict : Array[(String, Value)],
    err : RuntimeError,
  ) -> Unit {
    if get_named_value(dict, "errno") is Some(_) {
      return
    }
    let errno = match err.exc_type {
      "FileNotFoundError" => 2
      "FileExistsError" => 17
      "NotADirectoryError" => 20
      "IsADirectoryError" => 21
      "PermissionError" => 13
      "OSError" => 1
      _ => -1
    }
    if errno < 0 {
      return
    }
    let strerror = if err.exc_args.length() > 0 {
      err.exc_args[0]
    } else {
      err.message
    }
    set_named_value(dict, "errno", Value::Int(@bigint.BigInt::from_int(errno)))
    set_named_value(dict, "strerror", Value::Str(strerror))
    if get_named_value(dict, "filename") is None {
      set_named_value(dict, "filename", Value::None)
    }
    if get_named_value(dict, "filename2") is None {
      set_named_value(dict, "filename2", Value::None)
    }
  }

  fn maybe_populate_syntax_error(
    dict : Array[(String, Value)],
    err : RuntimeError,
    globals : Array[(String, Value)],
  ) -> Unit {
    if err.exc_type != "SyntaxError" &&
      err.exc_type != "IndentationError" &&
      err.exc_type != "TabError" {
      return
    }
    // CPython's traceback helpers expect these fields on SyntaxError instances.
    let filename = match get_named_value(globals, "__file__") {
      Some(Value::Str(name)) => name
      _ => "".to_string()
    }
    match get_named_value(dict, "filename") {
      None | Some(Value::None) =>
        set_named_value(dict, "filename", Value::Str(filename))
      _ => ()
    }
    match get_named_value(dict, "msg") {
      None | Some(Value::None) =>
        set_named_value(dict, "msg", Value::Str(err.message))
      _ => ()
    }
    // `traceback` and `unittest` expect this to exist even when we don't have
    // access to the full source line.
    if get_named_value(dict, "text") is None {
      set_named_value(dict, "text", Value::None)
    }
    match err.span {
      Some(span) => {
        let has_lineno = match get_named_value(dict, "lineno") {
          None | Some(Value::None) => false
          Some(_) => true
        }
        let has_offset = match get_named_value(dict, "offset") {
          None | Some(Value::None) => false
          Some(_) => true
        }
        match get_named_value(dict, "lineno") {
          None | Some(Value::None) =>
            set_named_value(
              dict,
              "lineno",
              Value::Int(@bigint.BigInt::from_int(span.line)),
            )
          _ => ()
        }
        match get_named_value(dict, "offset") {
          None | Some(Value::None) =>
            set_named_value(
              dict,
              "offset",
              Value::Int(@bigint.BigInt::from_int(span.column)),
            )
          _ => ()
        }
        match get_named_value(dict, "end_lineno") {
          None | Some(Value::None) =>
            if !has_lineno {
              set_named_value(
                dict,
                "end_lineno",
                Value::Int(@bigint.BigInt::from_int(span.line)),
              )
            }
          _ => ()
        }
        match get_named_value(dict, "end_offset") {
          None | Some(Value::None) =>
            if !has_offset {
              set_named_value(
                dict,
                "end_offset",
                Value::Int(@bigint.BigInt::from_int(span.column)),
              )
            }
          _ => ()
        }
      }
      None => ()
    }
  }

  match err.exc_value {
    Some(Value::Instance(inst)) => {
      // Keep the existing exception object, but sync core metadata so
      // context managers (assertRaises, etc.) see a proper exc_type/value/tb.
      let tb_value = make_traceback_value_with_env(
        err.traceback,
        err.traceback_envs,
        frame_builtins_dict,
        err.span,
      )
      set_named_value(inst.dict, "__traceback__", tb_value)
      set_named_value(
        inst.dict,
        "__cause__",
        match err.exc_cause {
          Some(v) => v
          None => Value::None
        },
      )
      set_named_value(
        inst.dict,
        "__context__",
        match err.exc_context {
          Some(v) => v
          None => Value::None
        },
      )
      set_named_value(
        inst.dict,
        "__suppress_context__",
        Value::Bool(err.exc_suppress_context),
      )
      maybe_populate_syntax_error(inst.dict, err, globals)
      maybe_populate_os_error(inst.dict, err)
      return Value::Instance(inst)
    }
    _ => ()
  }
  let positional : Array[Value] = []
  if err.exc_type == "StopIteration" {
    match err.exc_value {
      Some(value) => positional.push(value)
      None => ()
    }
  } else {
    for arg in err.exc_args {
      positional.push(Value::Str(arg))
    }
  }
  fn clone_values(values : Array[Value]) -> Array[Value] {
    let out : Array[Value] = []
    for v in values {
      out.push(v)
    }
    out
  }

  fn attach_exc_metadata(
    inst : InstanceValue,
    positional : Array[Value],
    err : RuntimeError,
    globals : Array[(String, Value)],
  ) -> Unit {
    fn parse_single_quoted_name(text : String) -> String? {
      let chars = text.to_array()
      let mut start = -1
      for i = 0; i < chars.length(); i = i + 1 {
        if chars[i] == '\'' {
          start = i + 1
          break
        }
      }
      if start < 0 || start >= chars.length() {
        return None
      }
      let mut end = -1
      for j = start; j < chars.length(); j = j + 1 {
        if chars[j] == '\'' {
          end = j
          break
        }
      }
      if end < 0 || end < start {
        return None
      }
      let buf = StringBuilder()
      for k = start; k < end; k = k + 1 {
        buf.write_char(chars[k])
      }
      Some(buf.to_string())
    }

    set_named_value(inst.dict, "args", Value::Tuple(positional))
    set_named_value(
      inst.dict,
      "__traceback__",
      make_traceback_value_with_env(
        err.traceback,
        err.traceback_envs,
        frame_builtins_dict,
        err.span,
      ),
    )
    set_named_value(
      inst.dict,
      "__cause__",
      match err.exc_cause {
        Some(v) => v
        None => Value::None
      },
    )
    set_named_value(
      inst.dict,
      "__context__",
      match err.exc_context {
        Some(v) => v
        None => Value::None
      },
    )
    set_named_value(
      inst.dict,
      "__suppress_context__",
      Value::Bool(err.exc_suppress_context),
    )
    // CPython exposes `NameError.name` (and UnboundLocalError.name) to power
    // suggestion formatting in traceback.py.
    if err.exc_type == "NameError" {
      match get_named_value(inst.dict, "name") {
        Some(Value::None) | None =>
          match get_named_value(inst.dict, "args") {
            Some(Value::Tuple(args)) if args.length() > 0 =>
              match args[0] {
                Value::Str(msg) =>
                  match parse_single_quoted_name(msg) {
                    Some(name) =>
                      set_named_value(inst.dict, "name", Value::Str(name))
                    None => ()
                  }
                _ => ()
              }
            _ => ()
          }
        Some(_) => ()
      }
    }
    // For "from X import Y" errors, CPython populates ImportError.name and
    // ImportError.name_from for suggestion formatting in traceback.py.
    if err.exc_type == "ImportError" {
      let has_name_from = match get_named_value(inst.dict, "name_from") {
        Some(Value::None) | None => false
        Some(_) => true
      }
      if !has_name_from {
        match get_named_value(inst.dict, "args") {
          Some(Value::Tuple(args)) if args.length() > 0 =>
            match args[0] {
              Value::Str(msg) =>
                // Best-effort parse "cannot import name '' from ''".
                match parse_single_quoted_name(msg) {
                  Some(name_from) => {
                    let chars = msg.to_array()
                    // Find the next quoted segment after the first closing quote.
                    let mut start2 = -1
                    let mut end1 = -1
                    let mut saw_first = false
                    for i = 0; i < chars.length(); i = i + 1 {
                      if chars[i] == '\'' {
                        if !saw_first {
                          // first quote
                          saw_first = true
                        } else if end1 < 0 {
                          // end of first segment
                          end1 = i
                          break
                        }
                      }
                    }
                    if end1 >= 0 {
                      for j = end1 + 1; j < chars.length(); j = j + 1 {
                        if chars[j] == '\'' {
                          start2 = j + 1
                          break
                        }
                      }
                    }
                    if start2 >= 0 && start2 < chars.length() {
                      let mut end2 = -1
                      for k = start2; k < chars.length(); k = k + 1 {
                        if chars[k] == '\'' {
                          end2 = k
                          break
                        }
                      }
                      if end2 > start2 {
                        let buf = StringBuilder()
                        for k = start2; k < end2; k = k + 1 {
                          buf.write_char(chars[k])
                        }
                        let mod_name = buf.to_string()
                        set_named_value(
                          inst.dict,
                          "name_from",
                          Value::Str(name_from),
                        )
                        set_named_value(inst.dict, "name", Value::Str(mod_name))
                      }
                    }
                  }
                  None => ()
                }
              _ => ()
            }
          _ => ()
        }
      }
    }
    maybe_populate_syntax_error(inst.dict, err, globals)
    maybe_populate_os_error(inst.dict, err)
  }

  fn fallback_instance_for_class(
    klass : ClassValue,
    positional : Array[Value],
    err : RuntimeError,
    globals : Array[(String, Value)],
  ) -> Value {
    let dict : Array[(String, Value)] = []
    let inst = InstanceValue::{ class: klass, dict, }
    attach_exc_metadata(inst, positional, err, globals)
    Value::Instance(inst)
  }

  match get_named_value(builtins, err.exc_type) {
    Some(Value::Class(klass)) =>
      match
        call_callable_with_env(
          Value::Class(klass),
          clone_values(positional),
          [],
          globals,
          builtins,
          io,
        ) {
        Ok(Value::Instance(inst)) => {
          attach_exc_metadata(inst, clone_values(positional), err, globals)
          Value::Instance(inst)
        }
        _ =>
          // If constructing with args fails (e.g. ExceptionGroup), still return
          // a real exception instance so higher-level helpers can set attributes
          // like `__traceback__` without crashing.
          match
            call_callable_with_env(
              Value::Class(klass),
              [],
              [],
              globals,
              builtins,
              io,
            ) {
            Ok(Value::Instance(inst)) => {
              attach_exc_metadata(inst, clone_values(positional), err, globals)
              Value::Instance(inst)
            }
            _ =>
              fallback_instance_for_class(
                klass,
                clone_values(positional),
                err,
                globals,
              )
          }
      }
    _ => {
      // Unknown exception type: fall back to a generic Exception instance.
      let klass = builtin_class_from_name("Exception", builtins)
      fallback_instance_for_class(klass, clone_values(positional), err, globals)
    }
  }
}

///|
fn eval_block_flow(
  body : Array[Stmt],
  locals : Array[(String, Value)],
  globals : Array[(String, Value)],
  builtins : Array[(String, Value)],
  io : MockIO,
  config : Config,
  loop_depth : Int32,
) -> Result[BlockFlow, RuntimeError] {
  let mut last = Value::None
  for stmt in body {
    match stmt {
      Stmt::WithSpan(span~, stmt~) => {
        push_active_span(span)
        let result = eval_block_flow(
          [stmt],
          locals,
          globals,
          builtins,
          io,
          config,
          loop_depth,
        )
        pop_active_span()
        match result {
          Ok(BlockFlow::Normal(value)) => last = value
          Ok(BlockFlow::Return(value)) => return Ok(BlockFlow::Return(value))
          Ok(BlockFlow::Break) => return Ok(BlockFlow::Break)
          Ok(BlockFlow::Continue) => return Ok(BlockFlow::Continue)
          Err(err) => return Err(err)
        }
      }
      Stmt::ExprStmt(expr) => {
        let _ = match eval_expr_with_env(expr, locals, globals, builtins, io) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        last = Value::None
      }
      Stmt::Assign(target~, value~) => {
        let eval_value = match
          eval_expr_with_env(value, locals, globals, builtins, io) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        let _ = match
          assign_target_with_env(
            target, eval_value, locals, globals, builtins, io,
          ) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        last = Value::None
      }
      Stmt::AnnAssign(target~, annotation~, value~) => {
        match value {
          Some(expr) => {
            let eval_value = match
              eval_expr_with_env(expr, locals, globals, builtins, io) {
              Ok(value) => value
              Err(err) => return Err(err)
            }
            let _ = match
              assign_target_with_env(
                target, eval_value, locals, globals, builtins, io,
              ) {
              Ok(v) => v
              Err(err) => return Err(err)
            }
          }
          None =>
            for expr in target_expressions(target) {
              match eval_expr_with_env(expr, locals, globals, builtins, io) {
                Ok(_) => ()
                Err(err) => return Err(err)
              }
            }
        }
        match target {
          Target::Name(name) => {
            // Only simple-name annotations contribute to __annotations__.
            let ann_value = match
              eval_annotation_with_env(
                annotation, locals, globals, builtins, io,
              ) {
              Ok(value) => value
              Err(err) => return Err(err)
            }
            let pairs = match
              get_scoped_value(locals, globals, "__annotations__") {
              Some(Value::Dict(pairs)) => pairs
              Some(_) =>
                return Err(
                  make_runtime_error(
                    RuntimeErrorKind::Type,
                    "__annotations__ must be a dict".to_string(),
                  ),
                )
              None => {
                let pairs : Array[(Value, Value)] = []
                set_scoped_value(
                  locals,
                  globals,
                  "__annotations__",
                  Value::Dict(pairs),
                )
                pairs
              }
            }
            let _ = match dict_set_item(pairs, Value::Str(name), ann_value) {
              Ok(v) => v
              Err(err) => return Err(err)
            }
          }
          _ =>
            if !(get_global_value(globals, "__mpython_future_annotations__")
              is Some(Value::Bool(true))) {
              match
                eval_expr_with_env(annotation, locals, globals, builtins, io) {
                Ok(_) => ()
                Err(err) => return Err(err)
              }
            }
        }
        last = Value::None
      }
      Stmt::AugAssign(target~, op~, value~) => {
        let right_val = match
          eval_expr_with_env(value, locals, globals, builtins, io) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        match target {
          Target::Name(name) => {
            let current = match
              lookup_name_value(name, locals, globals, builtins) {
              Ok(v) => v
              Err(err) => return Err(err)
            }
            let next = match
              eval_augassign_op_values(
                op, current, right_val, globals, builtins, io,
              ) {
              Ok(value) => value
              Err(err) => return Err(err)
            }
            set_scoped_value(locals, globals, name, next)
          }
          Target::Attribute(value~, attr~) => {
            let base = match
              eval_expr_with_env(value, locals, globals, builtins, io) {
              Ok(value) => value
              Err(err) => return Err(err)
            }
            let current = match
              get_attr_from_value(base, attr, globals, builtins, io) {
              Ok(value) => value
              Err(err) => return Err(err)
            }
            let next = match
              eval_augassign_op_values(
                op, current, right_val, globals, builtins, io,
              ) {
              Ok(value) => value
              Err(err) => return Err(err)
            }
            let _ = match
              set_attr_on_value(base, attr, next, globals, builtins, io) {
              Ok(v) => v
              Err(err) => return Err(err)
            }
          }
          Target::Subscript(value~, index~) => {
            let container = match
              eval_expr_with_env(value, locals, globals, builtins, io) {
              Ok(value) => value
              Err(err) => return Err(err)
            }
            match container {
              Value::List(values) =>
                match index {
                  Expr::Slice(start~, end~, step~) => {
                    let start_value = match start {
                      Some(expr) =>
                        match
                          eval_expr_with_env(
                            expr, locals, globals, builtins, io,
                          ) {
                          Ok(value) => value
                          Err(err) => return Err(err)
                        }
                      None => Value::None
                    }
                    let end_value = match end {
                      Some(expr) =>
                        match
                          eval_expr_with_env(
                            expr, locals, globals, builtins, io,
                          ) {
                          Ok(value) => value
                          Err(err) => return Err(err)
                        }
                      None => Value::None
                    }
                    let step_value = match step {
                      Some(expr) =>
                        match
                          eval_expr_with_env(
                            expr, locals, globals, builtins, io,
                          ) {
                          Ok(value) => value
                          Err(err) => return Err(err)
                        }
                      None => Value::None
                    }
                    let indices = match
                      slice_indices_from_values(
                        values.length(),
                        start_value,
                        end_value,
                        step_value,
                      ) {
                      Ok(v) => v
                      Err(err) => return Err(err)
                    }
                    let current = Value::List(
                      slice_values_by_indices(values, indices),
                    )
                    let next = match
                      eval_augassign_op_values(
                        op, current, right_val, globals, builtins, io,
                      ) {
                      Ok(v) => v
                      Err(err) => return Err(err)
                    }
                    let new_items = match iterable_values(next) {
                      Ok(v) => v
                      Err(err) => return Err(err)
                    }
                    let (start_i, end_i, step_i) = match
                      slice_params_from_values(
                        values.length(),
                        start_value,
                        end_value,
                        step_value,
                      ) {
                      Ok(v) => v
                      Err(err) => return Err(err)
                    }
                    if step_i == 1 {
                      let merged : Array[Value] = []
                      for i = 0; i < start_i; i = i + 1 {
                        merged.push(values[i])
                      }
                      for item in new_items {
                        merged.push(item)
                      }
                      for i = end_i; i < values.length(); i = i + 1 {
                        merged.push(values[i])
                      }
                      replace_array_values(values, merged)
                    } else {
                      if new_items.length() != indices.length() {
                        return Err(
                          make_runtime_error(
                            RuntimeErrorKind::Runtime,
                            "ValueError: attempt to assign sequence of size " +
                            new_items.length().to_string() +
                            " to extended slice of size " +
                            indices.length().to_string(),
                          ),
                        )
                      }
                      for i = 0; i < indices.length(); i = i + 1 {
                        values[indices[i]] = new_items[i]
                      }
                    }
                  }
                  _ => {
                    let index_value = match
                      eval_expr_with_env(index, locals, globals, builtins, io) {
                      Ok(value) => value
                      Err(err) => return Err(err)
                    }
                    let idx = match index_from_value(index_value, 0) {
                      Ok(v) => v
                      Err(err) => return Err(err)
                    }
                    let norm = normalize_index(idx, values.length())
                    if norm < 0 || norm >= values.length() {
                      return Err(
                        make_runtime_error(
                          RuntimeErrorKind::Index,
                          "index out of range".to_string(),
                        ),
                      )
                    }
                    let current = values[norm]
                    let next = match
                      eval_augassign_op_values(
                        op, current, right_val, globals, builtins, io,
                      ) {
                      Ok(value) => value
                      Err(err) => return Err(err)
                    }
                    values[norm] = next
                  }
                }
              Value::Dict(pairs) => {
                let index_value = match
                  eval_expr_with_env(index, locals, globals, builtins, io) {
                  Ok(value) => value
                  Err(err) => return Err(err)
                }
                let mut found : Int32? = None
                for i = 0; i < pairs.length(); i = i + 1 {
                  if eq_value(pairs[i].0, index_value) {
                    found = Some(i)
                    break
                  }
                }
                if found is None {
                  return Err(
                    make_runtime_error(
                      RuntimeErrorKind::Key,
                      "key not found".to_string(),
                    ),
                  )
                }
                let i = found.unwrap()
                let current = pairs[i].1
                let next = match
                  eval_augassign_op_values(
                    op, current, right_val, globals, builtins, io,
                  ) {
                  Ok(value) => value
                  Err(err) => return Err(err)
                }
                pairs[i] = (pairs[i].0, next)
              }
              _ =>
                return Err(
                  make_runtime_error(
                    RuntimeErrorKind::Type,
                    "object does not support item assignment".to_string(),
                  ),
                )
            }
          }
          _ =>
            return Err(
              make_runtime_error(
                RuntimeErrorKind::Type,
                "unsupported augmented assignment target".to_string(),
              ),
            )
        }
        last = Value::None
      }
      Stmt::TypeAlias(name~, type_params~, value~) => {
        let _ = type_params
        let template = FunctionValue::{
          name: "",
          params: [],
          defaults: [],
          body: [Stmt::Return(Some(value))],
          is_generator: false,
          is_async: false,
          closure: [],
        }
        let value_func = match
          make_function_from_template_with_env(
            template,
            [],
            [],
            locals,
            globals,
            builtins,
            io,
          ) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        set_scoped_value(
          locals,
          globals,
          name,
          Value::Instance(InstanceValue::{
            class: ClassValue::{ name: "TypeAlias", bases: [], dict: [], },
            dict: [
              ("__name__", Value::Str(name)),
              ("__value_func__", value_func),
            ],
          }),
        )
        last = Value::None
      }
      Stmt::Return(expr) => {
        if expr is Some(expr) {
          let value = match
            eval_expr_with_env(expr, locals, globals, builtins, io) {
            Ok(value) => value
            Err(err) => return Err(err)
          }
          return Ok(BlockFlow::Return(value))
        }
        return Ok(BlockFlow::Return(Value::None))
      }
      Stmt::Yield(_) | Stmt::YieldFrom(_) =>
        return Err(
          make_runtime_error(
            RuntimeErrorKind::Runtime,
            "SyntaxError: 'yield' outside function".to_string(),
          ),
        )
      Stmt::Function(
        name~,
        decorators~,
        type_params~,
        params~,
        defaults~,
        annotations~,
        return_annotation~,
        body~,
        is_generator~,
        is_async~
      ) => {
        let _ = type_params
        let decorator_values : Array[Value] = []
        for dec_expr in decorators {
          let dec_value = match
            eval_expr_with_env(dec_expr, locals, globals, builtins, io) {
            Ok(value) => value
            Err(err) => return Err(err)
          }
          decorator_values.push(dec_value)
        }
        let default_values : Array[Value] = []
        for expr in defaults {
          let value = match
            eval_expr_with_env(expr, locals, globals, builtins, io) {
            Ok(value) => value
            Err(err) => return Err(err)
          }
          default_values.push(value)
        }
        let ann_pairs : Array[(Value, Value)] = []
        for ann in annotations {
          let key = ann.0
          let expr = ann.1
          let value = match
            eval_annotation_with_env(expr, locals, globals, builtins, io) {
            Ok(v) => v
            Err(err) => return Err(err)
          }
          let _ = match dict_set_item(ann_pairs, Value::Str(key), value) {
            Ok(v) => v
            Err(err) => return Err(err)
          }
        }
        match return_annotation {
          Some(expr) => {
            let value = match
              eval_annotation_with_env(expr, locals, globals, builtins, io) {
              Ok(v) => v
              Err(err) => return Err(err)
            }
            let _ = match
              dict_set_item(ann_pairs, Value::Str("return".to_string()), value) {
              Ok(v) => v
              Err(err) => return Err(err)
            }
          }
          None => ()
        }
        let template = FunctionValue::{
          name,
          params,
          defaults: [],
          body,
          is_generator,
          is_async,
          closure: [],
        }
        let defined = match
          make_function_from_template_with_env(
            template, default_values, decorator_values, locals, globals, builtins,
            io,
          ) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        if ann_pairs.length() > 0 {
          match defined {
            Value::Function(func) => {
              let pairs = function_attr_dict(func)
              let _ = match
                dict_set_item(
                  pairs,
                  Value::Str("__annotations__".to_string()),
                  Value::Dict(ann_pairs),
                ) {
                Ok(v) => v
                Err(err) => return Err(err)
              }
            }
            _ => ()
          }
        }
        set_scoped_value(locals, globals, name, defined)
        last = Value::None
      }
      Stmt::Pass => last = Value::None
      Stmt::Global(_) => last = Value::None
      Stmt::Nonlocal(_) =>
        if !in_function_scope() {
          return Err(
            make_runtime_error(
              RuntimeErrorKind::Runtime,
              "SyntaxError: nonlocal declaration not allowed at module level".to_string(),
            ),
          )
        } else {
          last = Value::None
        }
      Stmt::Assert(condition~, message~) => {
        let value = match
          eval_expr_with_env(condition, locals, globals, builtins, io) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        let ok = match
          truthy_from_value_with_env(value, globals, builtins, io) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        if !ok {
          let text = match message {
            Some(expr) => {
              let msg_value = match
                eval_expr_with_env(expr, locals, globals, builtins, io) {
                Ok(value) => value
                Err(err) => return Err(err)
              }
              "AssertionError: " + value_to_string(msg_value)
            }
            None => "AssertionError: ".to_string()
          }
          return Err(make_runtime_error(RuntimeErrorKind::Runtime, text))
        }
        last = Value::None
      }
      Stmt::Raise(exc~, cause~) =>
        match exc {
          None =>
            match active_exception() {
              Some(err) => return Err(err)
              None =>
                return Err(
                  make_runtime_error(
                    RuntimeErrorKind::Runtime,
                    "RuntimeError: No active exception to reraise".to_string(),
                  ),
                )
            }
          Some(expr) => {
            let context_err_opt = active_exception()
            let raised_value = match
              eval_expr_with_env(expr, locals, globals, builtins, io) {
              Ok(value) => value
              Err(err) => return Err(err)
            }
            let inst_value = match
              ensure_exception_instance_from_value(
                raised_value, globals, builtins, io,
              ) {
              Ok(v) => v
              Err(err) => return Err(err)
            }
            let err0 = match inst_value {
              Value::Instance(inst) =>
                runtime_error_from_exception_instance(inst)
              _ =>
                return Err(
                  make_runtime_error(
                    RuntimeErrorKind::Runtime,
                    "RuntimeError: invalid exception".to_string(),
                  ),
                )
            }
            let err1 = match (context_err_opt, err0.exc_context) {
              (Some(ctx_err), None) => {
                let ctx_value = exception_value_from_runtime_error(
                  ctx_err, globals, builtins, io,
                )
                RuntimeError::{
                  kind: err0.kind,
                  message: err0.message,
                  span: err0.span,
                  traceback: err0.traceback,
                  traceback_envs: err0.traceback_envs,
                  exc_type: err0.exc_type,
                  exc_args: err0.exc_args,
                  exc_value: err0.exc_value,
                  exc_cause: err0.exc_cause,
                  exc_context: Some(ctx_value),
                  exc_suppress_context: err0.exc_suppress_context,
                }
              }
              _ => err0
            }
            let err2 = match cause {
              None => err1
              Some(cause_expr) => {
                let cause_value = match
                  eval_expr_with_env(cause_expr, locals, globals, builtins, io) {
                  Ok(value) => value
                  Err(e) => return Err(e)
                }
                let cause_inst = match cause_value {
                  Value::None => Value::None
                  _ =>
                    match
                      ensure_exception_instance_from_value(
                        cause_value, globals, builtins, io,
                      ) {
                      Ok(v) => v
                      Err(e) => return Err(e)
                    }
                }
                RuntimeError::{
                  kind: err1.kind,
                  message: err1.message,
                  span: err1.span,
                  traceback: err1.traceback,
                  traceback_envs: err1.traceback_envs,
                  exc_type: err1.exc_type,
                  exc_args: err1.exc_args,
                  exc_value: err1.exc_value,
                  exc_cause: Some(cause_inst),
                  exc_context: err1.exc_context,
                  exc_suppress_context: true,
                }
              }
            }
            // CPython updates the raised exception object's traceback eagerly,
            // even when the exception instance was created ahead of time.
            // Many stdlib helpers (e.g. contextlib/unittest) rely on
            // `exc.__traceback__` being populated at raise-time.
            match err2.exc_value {
              Some(Value::Instance(inst)) => {
                set_named_value(
                  inst.dict,
                  "__traceback__",
                  make_traceback_value_with_env(
                    err2.traceback,
                    err2.traceback_envs,
                    frame_builtins_dict_from_builtins(builtins),
                    err2.span,
                  ),
                )
                set_named_value(
                  inst.dict,
                  "__cause__",
                  match err2.exc_cause {
                    Some(v) => v
                    None => Value::None
                  },
                )
                set_named_value(
                  inst.dict,
                  "__context__",
                  match err2.exc_context {
                    Some(v) => v
                    None => Value::None
                  },
                )
                set_named_value(
                  inst.dict,
                  "__suppress_context__",
                  Value::Bool(err2.exc_suppress_context),
                )
              }
              _ => ()
            }
            return Err(err2)
          }
        }
      Stmt::Del(targets) => {
        for target in targets {
          let _ = match
            delete_target_with_env(target, locals, globals, builtins, io) {
            Ok(v) => v
            Err(err) => return Err(err)
          }
        }
        last = Value::None
      }
      Stmt::Import(module_name~, alias_name~) => {
        let module_value = match
          import_module(module_name, globals, builtins, io, config) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        let binding = match alias_name {
          Some(name) => name
          None => module_top_name(module_name)
        }
        let binding_value = match alias_name {
          Some(_) => module_value
          None =>
            if module_name.contains(".") {
              match module_cache_get(binding) {
                Some(value) => value
                None => module_value
              }
            } else {
              module_value
            }
        }
        set_scoped_value(locals, globals, binding, binding_value)
        last = Value::None
      }
      Stmt::FromImport(module_name~, names~) => {
        let resolved_name = if module_name.length() == 0 {
          module_name
        } else {
          match resolve_relative_module_name(module_name, globals) {
            Ok(v) => v
            Err(err) => return Err(err)
          }
        }
        if resolved_name == "__future__" {
          for item in names {
            if item.0 == "annotations" {
              set_global_value(
                globals,
                "__mpython_future_annotations__",
                Value::Bool(true),
              )
              break
            }
          }
        }
        let module_value = match
          import_module(resolved_name, globals, builtins, io, config) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        for item in names {
          let name = item.0
          let alias_opt = item.1
          if name == "*" {
            match module_value {
              Value::Instance(inst) => {
                // Honor `module.__all__` when present (CPython behavior), to avoid
                // importing unrelated public globals and clobbering package module
                // references (e.g. asyncio/__init__.py relies on `base_events`,
                // `coroutines`, ... referring to submodules).
                let mut imported_any = false
                match get_named_value(inst.dict, "__all__") {
                  Some(Value::List(items)) | Some(Value::Tuple(items)) =>
                    for item in items {
                      match item {
                        Value::Str(sym) => {
                          let value = match
                            get_attr_from_value(
                              module_value, sym, globals, builtins, io,
                            ) {
                            Ok(v) => v
                            Err(err) => return Err(err)
                          }
                          set_scoped_value(locals, globals, sym, value)
                          imported_any = true
                        }
                        _ => ()
                      }
                    }
                  _ => ()
                }
                if !imported_any {
                  for pair in inst.dict {
                    if pair.0 != "__name__" && !pair.0.has_prefix("_") {
                      set_scoped_value(locals, globals, pair.0, pair.1)
                    }
                  }
                }
              }
              _ => ()
            }
            continue
          }
          let imported = match
            get_attr_from_value(module_value, name, globals, builtins, io) {
            Ok(value) => value
            Err(err) =>
              match err.kind {
                RuntimeErrorKind::Attribute => {
                  let submodule = resolved_name + "." + name
                  match
                    import_module(submodule, globals, builtins, io, config) {
                    Ok(value) => value
                    Err(err) => {
                      // CPython shows the *original* import failure (not a generic
                      // "cannot import name") when the submodule exists but raises
                      // during import (common with circular imports).
                      let missing_submodule_message = "ModuleNotFoundError: No module named '" +
                        submodule +
                        "'"
                      if err.exc_type == "ModuleNotFoundError" &&
                        err.message == missing_submodule_message {
                        return Err(
                          make_runtime_error(
                            RuntimeErrorKind::Runtime,
                            "ImportError: cannot import name '" +
                            name +
                            "' from '" +
                            resolved_name +
                            "'",
                          ),
                        )
                      }
                      return Err(err)
                    }
                  }
                }
                _ => return Err(err)
              }
          }
          let binding = match alias_opt {
            Some(alias_name) => alias_name
            None => name
          }
          set_scoped_value(locals, globals, binding, imported)
        }
        last = Value::None
      }
      Stmt::With(context~, target~, body~) => {
        let ctx_value = match
          eval_expr_with_env(context, locals, globals, builtins, io) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        let enter_value = match
          get_attr_from_value(ctx_value, "__enter__", globals, builtins, io) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        let entered = match
          call_callable_with_env(enter_value, [], [], globals, builtins, io) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        match target {
          Some(name) => set_scoped_value(locals, globals, name, entered)
          None => ()
        }
        let exit_value = match
          get_attr_from_value(ctx_value, "__exit__", globals, builtins, io) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        let body_result = eval_block_flow(
          body, locals, globals, builtins, io, config, loop_depth,
        )
        match body_result {
          Ok(flow) => {
            let _ = match
              call_callable_with_env(
                exit_value,
                [Value::None, Value::None, Value::None],
                [],
                globals,
                builtins,
                io,
              ) {
              Ok(value) => value
              Err(err) => return Err(err)
            }
            match flow {
              BlockFlow::Normal(_) => {
                last = Value::None
                ()
              }
              BlockFlow::Return(value) => return Ok(BlockFlow::Return(value))
              BlockFlow::Break => return Ok(BlockFlow::Break)
              BlockFlow::Continue => return Ok(BlockFlow::Continue)
            }
          }
          Err(err) => {
            let exc_value = exception_value_from_runtime_error(
              err, globals, builtins, io,
            )
            let exc_type_value = match exc_value {
              Value::Instance(inst) => Value::Class(inst.class)
              _ => Value::Str(err.exc_type)
            }
            let tb_value = make_traceback_value(
              err.traceback,
              Value::Dict([]),
              err.span,
            )
            let exit_result = match
              call_callable_with_env(
                exit_value,
                [exc_type_value, exc_value, tb_value],
                [],
                globals,
                builtins,
                io,
              ) {
              Ok(value) => value
              Err(e) => return Err(e)
            }
            let suppress = match
              truthy_from_value_with_env(exit_result, globals, builtins, io) {
              Ok(v) => v
              Err(e) => return Err(e)
            }
            if suppress {
              last = Value::None
              ()
            } else {
              return Err(err)
            }
          }
        }
      }
      Stmt::AsyncWith(context~, target~, body~) => {
        // Roughly mirrors CPython:
        // mgr = (EXPR); aenter = type(mgr).__aenter__; aexit = type(mgr).__aexit__
        // value = await aenter(mgr); [TARGET = value];
        // try: SUITE; except: if not await aexit(mgr, *exc): raise; finally: await aexit(mgr, None, None, None)
        let mgr = match
          eval_expr_with_env(context, locals, globals, builtins, io) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        let aenter = match
          get_attr_from_value(mgr, "__aenter__", globals, builtins, io) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        let aexit = match
          get_attr_from_value(mgr, "__aexit__", globals, builtins, io) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        let entered = match
          call_callable_with_env(aenter, [], [], globals, builtins, io) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        let value = match coroutine_await(entered) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        match target {
          Some(name) => set_scoped_value(locals, globals, name, value)
          None => ()
        }
        let body_result = eval_block_flow(
          body, locals, globals, builtins, io, config, loop_depth,
        )
        match body_result {
          Ok(flow) => {
            // normal exit: await aexit(mgr, None, None, None)
            let exit_res = match
              call_callable_with_env(
                aexit,
                [Value::None, Value::None, Value::None],
                [],
                globals,
                builtins,
                io,
              ) {
              Ok(v) => v
              Err(err) => return Err(err)
            }
            let _ = match coroutine_await(exit_res) {
              Ok(v) => v
              Err(err) => return Err(err)
            }
            match flow {
              BlockFlow::Normal(_) => {
                last = Value::None
                ()
              }
              BlockFlow::Return(v) => return Ok(BlockFlow::Return(v))
              BlockFlow::Break => return Ok(BlockFlow::Break)
              BlockFlow::Continue => return Ok(BlockFlow::Continue)
            }
          }
          Err(err) => {
            let exc_value = exception_value_from_runtime_error(
              err, globals, builtins, io,
            )
            // For `__aexit__`, we currently pass the exception *type name* (string),
            // matching the expectations in `async_test.mbt`.
            let exc_type_value = Value::Str(err.exc_type)
            let tb_value = make_traceback_value(
              err.traceback,
              Value::Dict([]),
              err.span,
            )
            // exception exit: suppress if await aexit(...) is truthy
            let exit_call = match
              call_callable_with_env(
                aexit,
                [exc_type_value, exc_value, tb_value],
                [],
                globals,
                builtins,
                io,
              ) {
              Ok(v) => v
              Err(e) => return Err(e)
            }
            let exit_value = match coroutine_await(exit_call) {
              Ok(v) => v
              Err(e) => return Err(e)
            }
            let suppress = match
              truthy_from_value_with_env(exit_value, globals, builtins, io) {
              Ok(v) => v
              Err(e) => return Err(e)
            }
            if suppress {
              last = Value::None
              ()
            } else {
              return Err(err)
            }
          }
        }
      }
      Stmt::Try(body~, handlers~, else_body~, finally_body~) => {
        let outcome : Result[BlockFlow, RuntimeError] = match
          eval_block_flow(
            body, locals, globals, builtins, io, config, loop_depth,
          ) {
          Ok(flow) =>
            if flow is BlockFlow::Normal(_) && else_body.length() > 0 {
              eval_block_flow(
                else_body, locals, globals, builtins, io, config, loop_depth,
              )
            } else {
              Ok(flow)
            }
          Err(err) => {
            let mut has_star = false
            for handler in handlers {
              if handler.is_star {
                has_star = true
                break
              }
            }
            if !has_star {
              let mut handled = false
              let mut handled_result : Result[BlockFlow, RuntimeError] = Err(
                err,
              )
              for handler in handlers {
                let exc_expr_opt = handler.exc
                let name_opt = handler.name
                let handler_body = handler.body
                let mut matches = false
                match exc_expr_opt {
                  None => matches = true
                  Some(expr) => {
                    let exc_value = match
                      eval_expr_with_env(expr, locals, globals, builtins, io) {
                      Ok(value) => value
                      Err(e) => {
                        handled = true
                        handled_result = Err(e)
                        break
                      }
                    }
                    let handler_classes = match
                      handler_classes_from_value(exc_value, builtins) {
                      Ok(v) => v
                      Err(e) => {
                        handled = true
                        handled_result = Err(e)
                        break
                      }
                    }
                    let mut match_error : RuntimeError? = None
                    for klass in handler_classes {
                      match exception_matches_handler(err, klass, builtins) {
                        Ok(true) => {
                          matches = true
                          break
                        }
                        Ok(false) => ()
                        Err(e) => {
                          match_error = Some(e)
                          break
                        }
                      }
                    }
                    if match_error is Some(e) {
                      handled = true
                      handled_result = Err(e)
                      break
                    }
                  }
                }
                if !matches {
                  continue
                }
                handled = true
                match name_opt {
                  Some(name) =>
                    set_scoped_value(
                      locals,
                      globals,
                      name,
                      exception_value_from_runtime_error(
                        trim_runtime_error_for_active_handler(err),
                        globals,
                        builtins,
                        io,
                      ),
                    )
                  None => ()
                }
                active_exception_stack.val.push(
                  trim_runtime_error_for_active_handler(err),
                )
                let next_result = eval_block_flow(
                  handler_body, locals, globals, builtins, io, config, loop_depth,
                )
                let _ = active_exception_stack.val.pop()
                handled_result = next_result
                break
              }
              if handled {
                handled_result
              } else {
                Err(err)
              }
            } else {
              let mut remaining_group : Value? = None
              let mut handled = false
              let mut last_flow : BlockFlow = BlockFlow::Normal(Value::None)
              let mut handler_error : RuntimeError? = None
              for handler in handlers {
                let handler_classes = match handler.exc {
                  Some(expr) => {
                    let exc_value = match
                      eval_expr_with_env(expr, locals, globals, builtins, io) {
                      Ok(value) => value
                      Err(e) => {
                        handler_error = Some(e)
                        break
                      }
                    }
                    match handler_classes_from_value(exc_value, builtins) {
                      Ok(v) => v
                      Err(e) => {
                        handler_error = Some(e)
                        break
                      }
                    }
                  }
                  None => []
                }
                let group_value = match remaining_group {
                  Some(v) => v
                  None => {
                    let base_group = match
                      exception_group_from_runtime_error(
                        err, globals, builtins, io,
                      ) {
                      Ok(v) => v
                      Err(e) => {
                        handler_error = Some(e)
                        break
                      }
                    }
                    remaining_group = Some(base_group)
                    base_group
                  }
                }
                let (matched_opt, remaining_opt) = match
                  exception_group_split_by_classes(
                    group_value, handler_classes, globals, builtins, io,
                  ) {
                  Ok(v) => v
                  Err(e) => {
                    handler_error = Some(e)
                    break
                  }
                }
                let matched_group = match matched_opt {
                  Some(v) => v
                  None => continue
                }
                handled = true
                match handler.name {
                  Some(name) =>
                    set_scoped_value(locals, globals, name, matched_group)
                  None => ()
                }
                let match_err = match matched_group {
                  Value::Instance(inst) =>
                    runtime_error_from_exception_instance(inst)
                  _ => err
                }
                active_exception_stack.val.push(match_err)
                let next_result = eval_block_flow(
                  handler.body,
                  locals,
                  globals,
                  builtins,
                  io,
                  config,
                  loop_depth,
                )
                let _ = active_exception_stack.val.pop()
                match next_result {
                  Ok(flow) => last_flow = flow
                  Err(e) => {
                    handler_error = Some(e)
                    break
                  }
                }
                remaining_group = remaining_opt
                if remaining_group is None {
                  break
                }
                match last_flow {
                  BlockFlow::Normal(_) => ()
                  _ => break
                }
              }
              if handler_error is Some(e) {
                Err(e)
              } else if remaining_group is Some(rem) {
                match rem {
                  Value::Instance(inst) =>
                    Err(runtime_error_from_exception_instance(inst))
                  _ => Err(err)
                }
              } else if handled {
                Ok(last_flow)
              } else {
                Err(err)
              }
            }
          }
        }

        // Always run finally when leaving the try statement.
        if finally_body.length() > 0 {
          match
            eval_block_flow(
              finally_body, locals, globals, builtins, io, config, loop_depth,
            ) {
            Ok(BlockFlow::Normal(_)) => ()
            Ok(BlockFlow::Return(value)) => return Ok(BlockFlow::Return(value))
            Ok(BlockFlow::Break) => return Ok(BlockFlow::Break)
            Ok(BlockFlow::Continue) => return Ok(BlockFlow::Continue)
            Err(err) => return Err(err)
          }
        }
        match outcome {
          Ok(flow) =>
            match flow {
              BlockFlow::Normal(_) => {
                last = Value::None
                ()
              }
              BlockFlow::Return(value) => return Ok(BlockFlow::Return(value))
              BlockFlow::Break => return Ok(BlockFlow::Break)
              BlockFlow::Continue => return Ok(BlockFlow::Continue)
            }
          Err(err) => return Err(err)
        }
      }
      Stmt::Class(name~, decorators~, type_params~, bases~, keywords~, body~) => {
        let _ = type_params
        let decorator_values : Array[Value] = []
        for dec_expr in decorators {
          let dec_value = match
            eval_expr_with_env(dec_expr, locals, globals, builtins, io) {
            Ok(value) => value
            Err(err) => return Err(err)
          }
          decorator_values.push(dec_value)
        }
        let raw_base_values : Array[Value] = []
        for base in bases {
          match base {
            Expr::Starred(value~) => {
              let iter_value = match
                eval_expr_with_env(value, locals, globals, builtins, io) {
                Ok(v) => v
                Err(err) => return Err(err)
              }
              let items = match iterable_values(iter_value) {
                Ok(v) => v
                Err(err) => return Err(err)
              }
              for item in items {
                raw_base_values.push(item)
              }
            }
            _ => {
              let value = match
                eval_expr_with_env(base, locals, globals, builtins, io) {
                Ok(value) => value
                Err(err) => return Err(err)
              }
              raw_base_values.push(value)
            }
          }
        }
        let base_values : Array[Value] = []
        for base_value in raw_base_values {
          let expanded = match base_value {
            Value::Instance(_) =>
              match
                get_attr_from_value(
                  base_value, "__mro_entries__", globals, builtins, io,
                ) {
                Ok(mro_entries) => {
                  let bases_tuple : Array[Value] = []
                  for item in raw_base_values {
                    bases_tuple.push(item)
                  }
                  let entries = match
                    call_callable_with_env(
                      mro_entries,
                      [Value::Tuple(bases_tuple)],
                      [],
                      globals,
                      builtins,
                      io,
                    ) {
                    Ok(value) => value
                    Err(err) => return Err(err)
                  }
                  match entries {
                    Value::Tuple(values) => values
                    Value::List(values) => values
                    _ =>
                      return Err(
                        make_runtime_error(
                          RuntimeErrorKind::Type,
                          "base class must be a class".to_string(),
                        ),
                      )
                  }
                }
                Err(_) =>
                  return Err(
                    make_runtime_error(
                      RuntimeErrorKind::Type,
                      "base class must be a class".to_string(),
                    ),
                  )
              }
            _ => [base_value]
          }
          for value in expanded {
            match value {
              Value::Class(_) => base_values.push(value)
              Value::Function(func) =>
                if func.body.length() == 0 && is_builtin_class_name(func.name) {
                  base_values.push(
                    Value::Class(builtin_class_from_name(func.name, builtins)),
                  )
                } else {
                  return Err(
                    make_runtime_error(
                      RuntimeErrorKind::Type,
                      "base class must be a class".to_string(),
                    ),
                  )
                }
              other => base_values.push(other)
            }
          }
        }
        let class_kwargs : Array[(String, Value)] = []
        let mut metaclass_value : Value? = None
        for keyword in keywords {
          if keyword.0 == "**" {
            let mapping = match
              eval_expr_with_env(keyword.1, locals, globals, builtins, io) {
              Ok(value) => value
              Err(err) => return Err(err)
            }
            let pairs = match dict_pairs_from_value("class", mapping) {
              Ok(v) => v
              Err(err) => return Err(err)
            }
            for pair in pairs {
              match pair.0 {
                Value::Str(key) =>
                  if key == "metaclass" {
                    metaclass_value = Some(pair.1)
                  } else {
                    set_global_value(class_kwargs, key, pair.1)
                  }
                _ =>
                  return Err(
                    make_runtime_error(
                      RuntimeErrorKind::Type,
                      "keywords must be strings".to_string(),
                    ),
                  )
              }
            }
            continue
          }
          if keyword.0 == "metaclass" {
            let value = match
              eval_expr_with_env(keyword.1, locals, globals, builtins, io) {
              Ok(value) => value
              Err(err) => return Err(err)
            }
            metaclass_value = Some(value)
            continue
          }
          let value = match
            eval_expr_with_env(keyword.1, locals, globals, builtins, io) {
            Ok(value) => value
            Err(err) => return Err(err)
          }
          set_global_value(class_kwargs, keyword.0, value)
        }
        if base_values.length() == 0 {
          for pair in builtins {
            if pair.0 == "object" {
              match pair.1 {
                Value::Class(object_class) =>
                  base_values.push(Value::Class(object_class))
                _ => ()
              }
              break
            }
          }
        }
        for base_value in base_values {
          match base_value {
            Value::Class(base_class) =>
              match get_named_value(base_class.dict, "__module__") {
                Some(Value::Str(module_name)) =>
                  if module_name == "builtins" && base_class.name == "bool" {
                    return Err(
                      make_runtime_error(
                        RuntimeErrorKind::Type,
                        "type 'bool' is not an acceptable base type".to_string(),
                      ),
                    )
                  }
                _ => ()
              }
            _ => ()
          }
        }
        if get_scoped_value(locals, globals, "__class__") is Some(_) &&
          stmts_have_class_body_comprehension_name(body, "__class__") {
          return Err(
            make_runtime_error(
              RuntimeErrorKind::Runtime,
              "SystemError: compiler_lookup_arg(name='__class__') with reftype=1 failed in ; freevars of code " +
              name +
              ": ('__class__',)",
            ),
          )
        }
        let module_name_value = get_global_value(globals, "__name__")
        if module_name_value is None {
          return Err(
            make_runtime_error(
              RuntimeErrorKind::Name,
              "name '__name__' is not defined",
            ),
          )
        }
        let module_name = match module_name_value.unwrap() {
          Value::Str(text) => text
          other => value_to_string(other)
        }
        let class_dict : Array[(String, Value)] = []
        let mut qualname = name
        match get_global_value(locals, "__qualname__") {
          Some(Value::Str(parent)) => qualname = parent + "." + name
          _ => ()
        }
        class_dict.push(("__module__", Value::Str(module_name)))
        class_dict.push(("__qualname__", Value::Str(qualname)))

        // Best-effort support for zero-arg super(): functions created in this
        // class body can close over a __class__ cell.
        ensure_local_cell(class_dict, "__class__", Value::None)
        let mut pushed_closure = false
        if has_active_closure_env() {
          for i = 0; i < locals.length(); i = i + 1 {
            let name = locals[i].0
            let value = locals[i].1
            if !is_cell_value(value) {
              ensure_local_cell(locals, name, value)
            }
          }
          let class_closure = merge_cell_closures(locals, current_closure_env())
          push_closure_env(class_closure)
          pushed_closure = true
        }
        let flow = match
          eval_block_flow(
            body, class_dict, globals, builtins, io, config, loop_depth,
          ) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        if pushed_closure {
          pop_closure_env()
        }
        match flow {
          BlockFlow::Normal(_) => ()
          _ =>
            return Err(
              make_runtime_error(
                RuntimeErrorKind::Runtime,
                "SyntaxError: invalid class body".to_string(),
              ),
            )
        }
        // Best-effort metaclass support: select a metaclass and call it
        // (via `type.__new__` or a custom `__new__`) to produce the class.
        let mut selected_meta = Value::Class(
          builtin_class_from_name("type", builtins),
        )
        match metaclass_value {
          Some(v) => selected_meta = v
          None =>
            for base_value in base_values {
              match base_value {
                Value::Class(base_class) =>
                  match
                    get_named_value(base_class.dict, "__mpython_metaclass__") {
                    Some(Value::Class(meta)) => {
                      selected_meta = Value::Class(meta)
                      break
                    }
                    _ => ()
                  }
                _ => ()
              }
            }
        }
        let namespace_pairs : Array[(Value, Value)] = []
        for pair in class_dict {
          namespace_pairs.push((Value::Str(pair.0), pair.1))
        }
        let metaclass_args : Array[Value] = [
          Value::Str(name),
          Value::Tuple(base_values),
          Value::Dict(namespace_pairs),
        ]
        let created_value = match
          call_callable_with_env(
            selected_meta, metaclass_args, class_kwargs, globals, builtins, io,
          ) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        // CPython allows metaclasses to return non-type objects; in that case
        // the class statement binds the returned value.
        match created_value {
          Value::Class(klass) => {
            // Fill the __class__ cell so closures can resolve it (for super()).
            match get_named_value(class_dict, "__class__") {
              Some(cell) => cell_set_value(cell, Value::Class(klass))
              None => ()
            }
            // Give each Python-defined class a stable identity so `super()` can
            // disambiguate classes that share the same `__name__` (common in tests).
            if get_named_value(klass.dict, "hashvalue") is None {
              set_named_value(
                klass.dict,
                "hashvalue",
                Value::Int(fresh_object_hashvalue()),
              )
            }
            // Track the metaclass if the metaclass protocol didn't already.
            if get_named_value(klass.dict, "__mpython_metaclass__") is None {
              match selected_meta {
                Value::Class(meta) =>
                  set_named_value(
                    klass.dict,
                    "__mpython_metaclass__",
                    Value::Class(meta),
                  )
                _ => ()
              }
            }
            let _ = match class_mro(klass) {
              Ok(_) => ()
              Err(err) => return Err(err)
            }
            // Descriptor hook: record the attribute name on built-in property
            // instances so AttributeError messages can include it.
            for pair in klass.dict {
              match pair.1 {
                Value::Instance(desc_inst) =>
                  if class_has_base_name(desc_inst.class, "property") {
                    if get_named_value(desc_inst.dict, "__name__") is None {
                      set_named_value(
                        desc_inst.dict,
                        "__name__",
                        Value::Str(pair.0),
                      )
                    }
                  }
                _ => ()
              }
            }
            fn value_is_abstract(
              value : Value,
              globals : Array[(String, Value)],
              builtins : Array[(String, Value)],
              io : MockIO,
            ) -> Result[Bool, RuntimeError] {
              match
                get_attr_from_value(
                  value, "__isabstractmethod__", globals, builtins, io,
                ) {
                Ok(flag) =>
                  truthy_from_value_with_env(flag, globals, builtins, io)
                Err(err) =>
                  match err.kind {
                    RuntimeErrorKind::Attribute => Ok(false)
                    _ =>
                      if err.exc_type == "AttributeError" {
                        Ok(false)
                      } else {
                        Err(err)
                      }
                  }
              }
            }

            let abstract_names : Array[Value] = []
            for pair in class_dict {
              let is_abstract = match
                value_is_abstract(pair.1, globals, builtins, io) {
                Ok(v) => v
                Err(err) => return Err(err)
              }
              if is_abstract {
                match set_add_unique(abstract_names, Value::Str(pair.0)) {
                  Ok(_) => ()
                  Err(_) => ()
                }
              }
            }
            let mut has_abstractmethods_attr = false
            for base_value in base_values {
              match base_value {
                Value::Class(base_class) => {
                  let base_abs = get_named_value(
                    base_class.dict,
                    "__abstractmethods__",
                  )
                  if base_abs is Some(_) {
                    has_abstractmethods_attr = true
                  }
                  match base_abs {
                    Some(Value::Set(items)) | Some(Value::Tuple(items)) =>
                      for item in items {
                        match item {
                          Value::Str(name) =>
                            match lookup_class_attr(klass, name) {
                              Ok(Some(value)) => {
                                let is_abstract = match
                                  value_is_abstract(
                                    value, globals, builtins, io,
                                  ) {
                                  Ok(v) => v
                                  Err(err) => return Err(err)
                                }
                                if is_abstract {
                                  match
                                    set_add_unique(
                                      abstract_names,
                                      Value::Str(name),
                                    ) {
                                    Ok(_) => ()
                                    Err(_) => ()
                                  }
                                }
                              }
                              _ => ()
                            }
                          _ => ()
                        }
                      }
                    _ => ()
                  }
                }
                _ => ()
              }
            }
            if abstract_names.length() > 0 || has_abstractmethods_attr {
              // Override inherited __abstractmethods__ (even when empty) so concrete
              // subclasses of ABCs don't appear abstract.
              set_named_value(
                klass.dict,
                "__abstractmethods__",
                Value::Set(abstract_names),
              )
            }
            // Call `__init_subclass__` on the *base* (PEP 487). When defining a class
            // that itself defines `__init_subclass__`, CPython does not call the new
            // class' own hook for its creation; it calls the inherited hook via `super()`.
            let inherited_init_subclass : Value? = match class_mro(klass) {
              Ok(mro) => {
                let mut found : Value? = None
                for i = 1; i < mro.length(); i = i + 1 {
                  match get_named_value(mro[i].dict, "__init_subclass__") {
                    Some(Value::Function(func)) => {
                      found = Some(
                        Value::BoundMethod(BoundMethodValue::{
                          function: func,
                          self: Value::Class(klass),
                        }),
                      )
                      break
                    }
                    _ => ()
                  }
                }
                found
              }
              Err(_) => None
            }
            match inherited_init_subclass {
              Some(hook) => {
                let _ = match
                  call_callable_with_env(
                    hook,
                    [],
                    class_kwargs,
                    globals,
                    builtins,
                    io,
                  ) {
                  Ok(value) => value
                  Err(err) => return Err(err)
                }
              }
              None => ()
            }
            let mut defined : Value = Value::Class(klass)
            for i = decorator_values.length(); i > 0; i = i - 1 {
              let decorator_value = decorator_values[i - 1]
              defined = match decorator_value {
                Value::Function(dec_func) =>
                  if dec_func.body.length() == 0 {
                    match
                      eval_builtin_call(
                        dec_func.name,
                        [defined],
                        [],
                        locals,
                        globals,
                        builtins,
                        io,
                      ) {
                      Ok(Some(value)) => value
                      Ok(None) =>
                        match
                          call_callable_with_env(
                            decorator_value,
                            [defined],
                            [],
                            globals,
                            builtins,
                            io,
                          ) {
                          Ok(value) => value
                          Err(err) => return Err(err)
                        }
                      Err(err) => return Err(err)
                    }
                  } else {
                    match
                      call_callable_with_env(
                        decorator_value,
                        [defined],
                        [],
                        globals,
                        builtins,
                        io,
                      ) {
                      Ok(value) => value
                      Err(err) => return Err(err)
                    }
                  }
                _ =>
                  match
                    call_callable_with_env(
                      decorator_value,
                      [defined],
                      [],
                      globals,
                      builtins,
                      io,
                    ) {
                    Ok(value) => value
                    Err(err) => return Err(err)
                  }
              }
            }
            set_scoped_value(locals, globals, name, defined)
            last = Value::None
          }
          other => {
            let mut defined : Value = other
            for i = decorator_values.length(); i > 0; i = i - 1 {
              let decorator_value = decorator_values[i - 1]
              defined = match decorator_value {
                Value::Function(dec_func) =>
                  if dec_func.body.length() == 0 {
                    match
                      eval_builtin_call(
                        dec_func.name,
                        [defined],
                        [],
                        locals,
                        globals,
                        builtins,
                        io,
                      ) {
                      Ok(Some(value)) => value
                      Ok(None) =>
                        match
                          call_callable_with_env(
                            decorator_value,
                            [defined],
                            [],
                            globals,
                            builtins,
                            io,
                          ) {
                          Ok(value) => value
                          Err(err) => return Err(err)
                        }
                      Err(err) => return Err(err)
                    }
                  } else {
                    match
                      call_callable_with_env(
                        decorator_value,
                        [defined],
                        [],
                        globals,
                        builtins,
                        io,
                      ) {
                      Ok(value) => value
                      Err(err) => return Err(err)
                    }
                  }
                _ =>
                  match
                    call_callable_with_env(
                      decorator_value,
                      [defined],
                      [],
                      globals,
                      builtins,
                      io,
                    ) {
                    Ok(value) => value
                    Err(err) => return Err(err)
                  }
              }
            }
            set_scoped_value(locals, globals, name, defined)
            last = Value::None
          }
        }
      }
      Stmt::While(condition~, body~, else_body~) => {
        let mut broke = false
        while true {
          let cond_value = match
            eval_expr_with_env(condition, locals, globals, builtins, io) {
            Ok(value) => value
            Err(err) => return Err(err)
          }
          let ok = match
            truthy_from_value_with_env(cond_value, globals, builtins, io) {
            Ok(v) => v
            Err(err) => return Err(err)
          }
          if !ok {
            break
          }
          match
            eval_block_flow(
              body,
              locals,
              globals,
              builtins,
              io,
              config,
              loop_depth + 1,
            ) {
            Ok(BlockFlow::Normal(_)) => ()
            Ok(BlockFlow::Continue) => continue
            Ok(BlockFlow::Break) => {
              broke = true
              break
            }
            Ok(BlockFlow::Return(value)) => return Ok(BlockFlow::Return(value))
            Err(err) => return Err(err)
          }
        }
        if !broke && else_body.length() > 0 {
          match
            eval_block_flow(
              else_body, locals, globals, builtins, io, config, loop_depth,
            ) {
            Ok(BlockFlow::Normal(_)) => ()
            Ok(BlockFlow::Return(value)) => return Ok(BlockFlow::Return(value))
            Ok(BlockFlow::Break) => return Ok(BlockFlow::Break)
            Ok(BlockFlow::Continue) => return Ok(BlockFlow::Continue)
            Err(err) => return Err(err)
          }
        }
        last = Value::None
      }
      Stmt::For(target~, iter~, body~, else_body~) => {
        let iter_value = match
          eval_expr_with_env(iter, locals, globals, builtins, io) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        let mut broke = false
        let iterator = match
          iter_value_to_iterator(iter_value, globals, builtins, io) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        while true {
          let item = match
            iterator_next(iterator, None, globals, builtins, io) {
            Ok(value) => value
            Err(err) =>
              if err.exc_type == "StopIteration" {
                break
              } else {
                return Err(err)
              }
          }
          let _ = match
            assign_target_with_env(target, item, locals, globals, builtins, io) {
            Ok(v) => v
            Err(err) => return Err(err)
          }
          match
            eval_block_flow(
              body,
              locals,
              globals,
              builtins,
              io,
              config,
              loop_depth + 1,
            ) {
            Ok(BlockFlow::Normal(_)) => ()
            Ok(BlockFlow::Continue) => continue
            Ok(BlockFlow::Break) => {
              broke = true
              break
            }
            Ok(BlockFlow::Return(value)) => return Ok(BlockFlow::Return(value))
            Err(err) => return Err(err)
          }
        }
        if !broke && else_body.length() > 0 {
          match
            eval_block_flow(
              else_body, locals, globals, builtins, io, config, loop_depth,
            ) {
            Ok(BlockFlow::Normal(_)) => ()
            Ok(BlockFlow::Return(value)) => return Ok(BlockFlow::Return(value))
            Ok(BlockFlow::Break) => return Ok(BlockFlow::Break)
            Ok(BlockFlow::Continue) => return Ok(BlockFlow::Continue)
            Err(err) => return Err(err)
          }
        }
        last = Value::None
      }
      Stmt::AsyncFor(target~, iter~, body~, else_body~) => {
        // Minimal async-for semantics:
        // it = (ITER); it = type(it).__aiter__(it)
        // loop: value = await type(it).__anext__(it); assign target; exec body
        // StopAsyncIteration => break; optional else_body if not broken
        let iter_value = match
          eval_expr_with_env(iter, locals, globals, builtins, io) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        let aiter_value = match
          get_attr_from_value(iter_value, "__aiter__", globals, builtins, io) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        let iterator0 = match
          call_callable_with_env(aiter_value, [], [], globals, builtins, io) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        let iterator = iterator0
        let mut broke = false
        while true {
          let anext_value = match
            get_attr_from_value(iterator, "__anext__", globals, builtins, io) {
            Ok(v) => v
            Err(err) => return Err(err)
          }
          let next_awaitable = match
            call_callable_with_env(anext_value, [], [], globals, builtins, io) {
            Ok(v) => v
            Err(err) => return Err(err)
          }
          let item = match coroutine_await(next_awaitable) {
            Ok(v) => v
            Err(err) => {
              if err.exc_type == "StopAsyncIteration" {
                break
              }
              return Err(err)
            }
          }
          let _ = match
            assign_target_with_env(target, item, locals, globals, builtins, io) {
            Ok(v) => v
            Err(err) => return Err(err)
          }
          match
            eval_block_flow(
              body,
              locals,
              globals,
              builtins,
              io,
              config,
              loop_depth + 1,
            ) {
            Ok(BlockFlow::Normal(_)) => ()
            Ok(BlockFlow::Continue) => continue
            Ok(BlockFlow::Break) => {
              broke = true
              break
            }
            Ok(BlockFlow::Return(value)) => return Ok(BlockFlow::Return(value))
            Err(err) => return Err(err)
          }
        }
        if !broke && else_body.length() > 0 {
          match
            eval_block_flow(
              else_body, locals, globals, builtins, io, config, loop_depth,
            ) {
            Ok(BlockFlow::Normal(_)) => ()
            Ok(BlockFlow::Return(value)) => return Ok(BlockFlow::Return(value))
            Ok(BlockFlow::Break) => return Ok(BlockFlow::Break)
            Ok(BlockFlow::Continue) => return Ok(BlockFlow::Continue)
            Err(err) => return Err(err)
          }
        }
        last = Value::None
      }
      Stmt::Break => {
        if loop_depth > 0 {
          return Ok(BlockFlow::Break)
        }
        return Err(
          make_runtime_error(
            RuntimeErrorKind::Runtime,
            "SyntaxError: 'break' outside loop".to_string(),
          ),
        )
      }
      Stmt::Continue => {
        if loop_depth > 0 {
          return Ok(BlockFlow::Continue)
        }
        return Err(
          make_runtime_error(
            RuntimeErrorKind::Runtime,
            "SyntaxError: 'continue' not properly in loop".to_string(),
          ),
        )
      }
      Stmt::Match(subject~, cases~) => {
        let subject_value = match
          eval_expr_with_env(subject, locals, globals, builtins, io) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        for case in cases {
          let pattern = case.0
          let guard_expr_opt : Expr? = case.1
          let case_body = case.2
          match match_pattern(subject_value, pattern) {
            Some(bindings) => {
              let previous : Array[(String, Value?)] = []
              for binding in bindings {
                previous.push(
                  (binding.0, get_scoped_value(locals, globals, binding.0)),
                )
                set_scoped_value(locals, globals, binding.0, binding.1)
              }
              let mut guard_ok = true
              if guard_expr_opt is Some(guard_expr) {
                let guard_value = match
                  eval_expr_with_env(guard_expr, locals, globals, builtins, io) {
                  Ok(value) => value
                  Err(err) => return Err(err)
                }
                guard_ok = match
                  truthy_from_value_with_env(guard_value, globals, builtins, io) {
                  Ok(v) => v
                  Err(err) => return Err(err)
                }
              }
              if guard_ok {
                match
                  eval_block_flow(
                    case_body, locals, globals, builtins, io, config, loop_depth,
                  ) {
                  Ok(BlockFlow::Normal(_)) => ()
                  Ok(BlockFlow::Return(value)) =>
                    return Ok(BlockFlow::Return(value))
                  Ok(BlockFlow::Break) => return Ok(BlockFlow::Break)
                  Ok(BlockFlow::Continue) => return Ok(BlockFlow::Continue)
                  Err(err) => return Err(err)
                }
                break
              }
              for prev in previous {
                if prev.1 is Some(value) {
                  set_scoped_value(locals, globals, prev.0, value)
                } else {
                  let _ = delete_scoped_value(locals, globals, prev.0)
                }
              }
            }
            None => ()
          }
        }
        last = Value::None
      }
      Stmt::If(condition~, body~, else_body~) => {
        let cond_value = match
          eval_expr_with_env(condition, locals, globals, builtins, io) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        let ok = match
          truthy_from_value_with_env(cond_value, globals, builtins, io) {
          Ok(v) => v
          Err(err) => return Err(err)
        }
        if ok {
          match
            eval_block_flow(
              body, locals, globals, builtins, io, config, loop_depth,
            ) {
            Ok(BlockFlow::Normal(_)) => ()
            Ok(BlockFlow::Return(value)) => return Ok(BlockFlow::Return(value))
            Ok(BlockFlow::Break) => return Ok(BlockFlow::Break)
            Ok(BlockFlow::Continue) => return Ok(BlockFlow::Continue)
            Err(err) => return Err(err)
          }
        } else {
          match
            eval_block_flow(
              else_body, locals, globals, builtins, io, config, loop_depth,
            ) {
            Ok(BlockFlow::Normal(_)) => ()
            Ok(BlockFlow::Return(value)) => return Ok(BlockFlow::Return(value))
            Ok(BlockFlow::Break) => return Ok(BlockFlow::Break)
            Ok(BlockFlow::Continue) => return Ok(BlockFlow::Continue)
            Err(err) => return Err(err)
          }
        }
        last = Value::None
      }
    }
  }
  Ok(BlockFlow::Normal(last))
}

///|
fn eval_block(
  body : Array[Stmt],
  locals : Array[(String, Value)],
  globals : Array[(String, Value)],
  builtins : Array[(String, Value)],
  io : MockIO,
  config : Config,
) -> Result[Value, RuntimeError] {
  push_active_config(config)
  let result = eval_block_flow(body, locals, globals, builtins, io, config, 0)
  pop_active_config()
  match result {
    Ok(BlockFlow::Normal(value)) => Ok(value)
    Ok(BlockFlow::Return(value)) => Ok(value)
    Ok(BlockFlow::Break) =>
      Err(
        make_runtime_error(
          RuntimeErrorKind::Runtime,
          "SyntaxError: 'break' outside loop".to_string(),
        ),
      )
    Ok(BlockFlow::Continue) =>
      Err(
        make_runtime_error(
          RuntimeErrorKind::Runtime,
          "SyntaxError: 'continue' not properly in loop".to_string(),
        ),
      )
    Err(err) => Err(err)
  }
}