///|
// A returned extVar call can enter its code in the evaluator's existing loop.
// Keep each real clean frame, while avoiding physical recursion for forwarding
// chains. Non-tail calls retain their expression-depth and busy-thunk checks.
fn Vm::forward_parameter(self : Vm, id : Int) -> (Expr, Env)? raise {
  let original = self.thunks[id]
  if original is RunningParameter(expression, environment) {
    return Some((expression, environment))
  }
  if !(original is Code(_, _) || original is ParsedParameter(_, _)) {
    return None
  }
  if self.persistent {
    self.retries[id] = original
  }
  let (expression, environment) = try {
    match original {
      Code(source, origin) => {
        if @utf8.encode(source).length() > self.limits.source_bytes {
          raise problem(Limit, origin, "parameter source byte limit exceeded")
        }
        let expression = parse(source, origin.file, self.limits.depth)
        (expression, self.base_env(file=origin.file, import_from=""))
      }
      ParsedParameter(program, origin) => {
        if program.source_bytes > self.limits.source_bytes {
          raise problem(Limit, origin, "parameter source byte limit exceeded")
        }
        check_ast_depth(program.expression, self.limits.depth)
        (
          program.expression,
          self.base_env(
            file=origin.file,
            import_from=if program.anonymous { "" } else { program.filename },
          ),
        )
      }
      _ => abort("parameter forwarding requires code")
    }
  } catch {
    error => self.failure(id, error)
  }
  self.thunks[id] = RunningParameter(expression, environment)
  Some((expression, environment))
}

///|
fn Vm::external_parameter(self : Vm, name : Value, loc : Location) -> Int raise {
  let name = text_value(name, loc)
  match self.ext.get(name) {
    Some(id) => id
    None => raise problem(Runtime, loc, "Undefined external variable: " + name)
  }
}