///|
pub(all) enum Value {
  // PKL-150: Pkl's `Int` is i64. We stored it in MoonBit's `Int` (i32)
  // through PKL-149; everything from Unix-ns timestamps to bitwise ops on
  // the upper 32 bits silently truncated. Carrying Int64 in the variant
  // keeps the runtime aligned with the language reference and is the
  // load-bearing change for the api/int / api/dataSize / api/duration
  // gold-match.
  IntValue(Int64)
  // PKL-092: Float numeric value backed by MoonBit's `Double`. Arithmetic
  // between `IntValue` and `FloatValue` promotes to `FloatValue`; the
  // dedicated `Number` type sits above both in the typechecker hierarchy.
  FloatValue(Double)
  BoolValue(Bool)
  StringValue(String)
  NullValue
  DeferredImportValue(String)
  // PKL-161/166: opaque, self-contained property thunk. The cell owns its
  // memo state and computation, so runtime Values do not depend on a global
  // evaluator registry and release captured environments with normal GC.
  ThunkValue(EvalThunkCell)
  ObjectValue(Array[ValueMember])
  ListingValue(Array[Value])
  // PKL-148h: `List(...)` constructor result, kept distinct from
  // `ListingValue` (the `new Listing { ... }` block form). The two
  // share most method-dispatch surface but diverge on PCF rendering
  // (`List(a, b, c)` vs `new { a; b; c }`), runtime type name
  // (`List` vs `Listing`), and derived equality (List == Listing is
  // false even when element arrays match — Apple Pkl treats them as
  // distinct types).
  ListValue(Array[Value])
  MappingValue(Array[ValueEntry])
  // Collection values that carry a Pkl `default` member. `raw_*`
  // preserves the explicitly declared elements / entries; `materialized_*`
  // is the user-visible projection after applying the current default.
  // Keeping both lets `(xs) { default { ... } }` reapply a new default to
  // old entries without treating values supplied by the previous default
  // as explicit user fields.
  DefaultedListingValue(Array[Value], Array[Value], Value)
  DefaultedMappingValue(Array[ValueEntry], Array[ValueEntry], Value)
  // PKL-119a: Pair carries two ordered values with member access via
  // `.first` / `.second`. Previously collapsed into `ListingValue` of
  // size 2 (per the PKL-139 stop-gap), which lost the member-access
  // shape and the type-level distinction from `Listing`. The
  // dedicated variant lets consumers using pkl-mbt as a library keep
  // the upstream type semantics for `Pair`.
  PairValue(Value, Value)
  // PKL-119b: lazy integer sequence (start, end, step). `start` and
  // `end` are inclusive in Apple Pkl; `step` must be non-zero and
  // defaults to 1 (-1 when called as `IntSeq(start, end).step(-1)`
  // for descending iteration). Empty sequences (e.g. ascending
  // `IntSeq(5, 2)`) carry the original endpoints so the renderer can
  // round-trip the source form. The variant is intentionally lazy —
  // materialization happens at `.toList()` / `.toListing()` / `.map`
  // / `.fold` / non-PCF renderer projection time so a million-element
  // IntSeq doesn't allocate a million-slot Array up front.
  // PKL-150: IntSeq carries Int64 to match the widened Pkl `Int`.
  IntSeqValue(Int64, Int64, Int64)
  // PKL-119c: ordered set of unique values, insertion-order preserved.
  // `Set(a, b, c)` constructs one (duplicates dropped at the
  // constructor); the dedicated variant means PCF renders the
  // upstream `Set(a, b, c)` form rather than `new Listing { a; b; c }`,
  // and the typechecker keeps `Set` separate from `Listing`.
  SetValue(Array[Value])
  // PKL-119d: immutable functional map (Apple Pkl's `Map`),
  // distinct from `MappingValue` (the object-style `new Mapping
  // { ... }` form). `Map(k1, v1, k2, v2, ...)` builds one; later
  // keys with the same value overwrite earlier ones. The dedicated
  // variant lets PCF round-trip through `Map(k, v, ...)` rather
  // than the Mapping block form, and keeps `Map` separate
  // from `Mapping` at the typechecker.
  MapValue(Array[ValueEntry])
  // PKL-148g: identity stamp so `(() -> 1) == (() -> 1)` is false
  // (distinct LambdaExpr evaluations) while `local f = () -> 1; f == f`
  // is true (the same cached instance). MoonBit's derived `==` includes
  // the id, so structural-only payloads no longer falsely equate.
  FunctionValue(
    Array[FunctionParameter],
    Expr,
    String?,
    Array[ValueBinding],
    Int
  )
  // PKL-082: magnitude in the named unit. Mixed-unit arithmetic
  // normalizes to the smaller of the two units (the base unit of each
  // family — `ns` for Duration, `b` for DataSize — when no common
  // larger unit divides both). PKL-121 widened the magnitude from
  // `Int` to `Double` so Float-magnitude literals (`1.5.s`, `2.5.gib`)
  // round-trip without precision loss; Int-magnitude call sites
  // promote on the way in.
  DurationValue(Double, String)
  DataSizeValue(Double, String)
  // PKL-081: Regex value created via `Regex("")`. Stores the
  // source pattern so methods can recompile via `moonbitlang/regexp`
  // and renderers can round-trip the value through `Regex("...")`.
  RegexValue(String)
  // PKL-083: Bytes value backed by MoonBit's `Bytes`. Constructed via
  // `Bytes()`, `Bytes(...)`, or
  // `Bytes.fromBase64("...")`; PCF round-trips through the varargs
  // constructor and JSON / YAML / Properties project the base64 string.
  BytesValue(Bytes)
} derive(Eq, Debug)

///|
pub(all) struct ValueMember {
  name : String
  value : Value
  annotations : Array[Annotation]
  // PKL-148bb: original right-hand expression captured at eval time so an
  // amend overlay can re-evaluate the slot when a sibling it references
  // gets overridden (late binding — `x = y; y = 3` then `(base) { y = 4 }`
  // must propagate the new `y` into `x`). `None` for slots whose value
  // was never produced from a user-source Expr (synthetic sentinels,
  // method-built ObjectValue, etc.). Excluded from equality so existing
  // test fixtures that compare `ValueMember::{ name, value, source: None, annotations: [] }`
  // against an evaluator output (which now carries the captured Expr)
  // still match — the user-visible state is the (name, value) pair.
  source : Expr?
} derive(Debug)

///|
impl Eq for ValueMember with fn equal(a, b) {
  a.name == b.name && a.value == b.value
}

///|
fn append_annotations(
  left : Array[Annotation],
  right : Array[Annotation],
) -> Array[Annotation] {
  let out : Array[Annotation] = []
  for item in left {
    out.push(item)
  }
  for item in right {
    out.push(item)
  }
  out
}

///|
pub(all) struct ValueEntry {
  key : Value
  value : Value
} derive(Eq, Debug)

///|
pub(all) enum EvalResult {
  EvalOk(Value)
  EvalError(Array[Diagnostic])
} derive(Eq, Debug)

///|
pub(all) struct ValueBinding {
  name : String
  value : Value
} derive(Eq, Debug)

///|
fn deferred_error_value(message : String) -> Value {
  ObjectValue([
    {
      name: error_member_name("@deferred"),
      value: StringValue(message),
      source: None,
      annotations: [],
    },
  ])
}

///|
fn deferred_error_message(value : Value) -> String? {
  match force_eval_thunk(value) {
    ObjectValue(members) =>
      match lookup_member(members, error_member_name("@deferred")) {
        Some(StringValue(message)) => Some(message)
        _ => None
      }
    _ => None
  }
}

///|
priv struct ClassBinding {
  name : String
  parent_name : String?
  properties : Array[ClassProperty]
  methods : Array[FunctionDecl]
}

///|
priv struct EvalTypeAliasBinding {
  name : String
  target : String
}

///|
pub(all) struct ClassExport {
  name : String
  parent_name : String?
  properties : Array[ClassProperty]
  methods : Array[FunctionDecl]
} derive(Eq, Debug)

///|
fn operator_name(op : BinaryOp) -> String {
  match op {
    Add => "+"
    Subtract => "-"
    Multiply => "*"
    Divide => "/"
    IntDivide => "~/"
    Modulo => "%"
    Power => "**"
    Equal => "=="
    NotEqual => "!="
    LessThan => "<"
    LessOrEqual => "<="
    GreaterThan => ">"
    GreaterOrEqual => ">="
    And => "&&"
    Or => "||"
    NullCoalesce => "??"
    Is => "is"
    As => "as"
    Pipe => "|>"
  }
}

///|
fn relation_kind_name(kind : ModuleRelationKind) -> String {
  match kind {
    ModuleAmends => "amends"
    ModuleExtends => "extends"
  }
}

///|
pub fn eval_source(source : String) -> EvalResult {
  // PKL-118: strip the hidden-prefixed function members the evaluator
  // adds for cross-module dispatch. `eval_source` is the test-facing
  // entry point and the existing tests assert on visible-binding
  // equality (`ValueMember::{ name: "result", value: IntValue(3), source: None, annotations: [] }`);
  // surfacing the synthetic function entries here would force every
  // test that declares a `function` to expand its expected ObjectValue
  // even though the user-visible render still hides them. The CLI's
  // `render_value` already skips hidden members, so this filter is the
  // only place the symmetry needs to be re-stated.
  match eval_source_with_imports(source, fn(_) { None }) {
    EvalOk(value) => {
      let stripped = strip_invisible_recursive(value)
      // `eval_source` is an eager, test-facing projection. Property thunks
      // rejected while stripping must preserve the historical EvalError
      // contract instead of leaking a DeferredErrorValue inside EvalOk.
      match first_rendered_deferred_error_message(stripped) {
        Some(message) => EvalError([diag(message)])
        None => EvalOk(stripped)
      }
    }
    other => other
  }
}

///|
/// PKL-148bh: recursively strip invisible members (hidden /
/// local / error sentinels / class tags) from an evaluation result.
/// The test harness compares evaluator output against
/// hand-constructed ObjectValue arrays that don't carry the runtime
/// markers; preserving them at depth 1+ broke fixtures that nest a
/// typed instance inside a module-level binding (universal class
/// tagging in `tag_object_with_class` started writing the
/// `@hidden$__class` marker for every typed body).
fn strip_invisible_recursive(value : Value) -> Value {
  let value = force_eval_thunk(value)
  // Preserve a rejected thunk's sentinel until the eager `eval_source`
  // boundary has converted it to EvalError. Stripping it here would turn
  // the failure into an indistinguishable empty object.
  if deferred_error_message(value) is Some(_) {
    return value
  }
  match value {
    ObjectValue(members) => {
      let kept : Array[ValueMember] = []
      let preserve_class_tag = match find_object_class_tag(members) {
        Some("RenderDirective") => true
        _ => false
      }
      for m in members {
        if is_invisible_member_name(m.name) &&
          !(preserve_class_tag && m.name == class_tag_member_name()) {
          continue
        }
        kept.push({
          name: m.name,
          value: strip_invisible_recursive(m.value),
          source: m.source,
          annotations: m.annotations,
        })
      }
      ObjectValue(kept)
    }
    ListingValue(elements) => {
      let kept : Array[Value] = []
      for e in elements {
        kept.push(strip_invisible_recursive(e))
      }
      ListingValue(kept)
    }
    DefaultedListingValue(raw, elements, default_value) => {
      let kept_raw : Array[Value] = []
      for e in raw {
        kept_raw.push(strip_invisible_recursive(e))
      }
      let kept_elements : Array[Value] = []
      for e in elements {
        kept_elements.push(strip_invisible_recursive(e))
      }
      DefaultedListingValue(
        kept_raw,
        kept_elements,
        strip_invisible_recursive(default_value),
      )
    }
    ListValue(elements) => {
      let kept : Array[Value] = []
      for e in elements {
        kept.push(strip_invisible_recursive(e))
      }
      ListValue(kept)
    }
    SetValue(elements) => {
      let kept : Array[Value] = []
      for e in elements {
        kept.push(strip_invisible_recursive(e))
      }
      SetValue(kept)
    }
    MappingValue(entries) => {
      let kept : Array[ValueEntry] = []
      for entry in entries {
        kept.push({
          key: strip_invisible_recursive(entry.key),
          value: strip_invisible_recursive(entry.value),
        })
      }
      MappingValue(kept)
    }
    DefaultedMappingValue(raw, entries, default_value) => {
      let kept_raw : Array[ValueEntry] = []
      for entry in raw {
        kept_raw.push({
          key: strip_invisible_recursive(entry.key),
          value: strip_invisible_recursive(entry.value),
        })
      }
      let kept_entries : Array[ValueEntry] = []
      for entry in entries {
        kept_entries.push({
          key: strip_invisible_recursive(entry.key),
          value: strip_invisible_recursive(entry.value),
        })
      }
      DefaultedMappingValue(
        kept_raw,
        kept_entries,
        strip_invisible_recursive(default_value),
      )
    }
    MapValue(entries) => {
      let kept : Array[ValueEntry] = []
      for entry in entries {
        kept.push({
          key: strip_invisible_recursive(entry.key),
          value: strip_invisible_recursive(entry.value),
        })
      }
      MapValue(kept)
    }
    PairValue(a, b) =>
      PairValue(strip_invisible_recursive(a), strip_invisible_recursive(b))
    _ => value
  }
}

///| Render a runtime value in the canonical Pkl Configuration Format

///| (PCF). Primitives use the same lexical form as Apple Pkl's

///| `pkl eval` default output, so the result can be reparsed by this

///| evaluator and by upstream Pkl. The JSON / YAML / Properties

///| renderers (PKL-072..074) will live alongside this as separate

///|
/// entry points.
fn eval_float_binary(
  op : BinaryOp,
  a : Double,
  b : Double,
  diagnostics : Array[Diagnostic],
) -> Value? {
  // PKL-092: Float-side arithmetic and comparison. Division-by-zero
  // surfaces a diagnostic to match the Int-side path; Apple Pkl returns
  // `Infinity` / `NaN` for floating point zero division, which is
  // implementable later but a separate slice — staying strict here keeps
  // the failure mode consistent across numeric types until then.
  match op {
    Add => Some(FloatValue(a + b))
    Subtract => Some(FloatValue(a - b))
    Multiply => Some(FloatValue(a * b))
    Divide => Some(FloatValue(a / b))
    LessThan => Some(BoolValue(a < b))
    LessOrEqual => Some(BoolValue(a <= b))
    GreaterThan => Some(BoolValue(a > b))
    GreaterOrEqual => Some(BoolValue(a >= b))
    Equal => Some(BoolValue(a == b))
    NotEqual => Some(BoolValue(a != b))
    // PKL-111: Apple Pkl widens these Int-domain operators to Float
    // operands. `**` (Power) returns a Float exponent; `~/` (IntDivide)
    // truncates toward zero and returns an Int even on Float operands
    // (matching Apple's `5.1 ~/ 3.1 == 1`); `%` (Modulo) returns the
    // truncated remainder as a Float so it composes with Float division.
    Power => Some(FloatValue(@math.pow(a, b)))
    IntDivide =>
      if b == 0.0 {
        diagnostics.push(diag("division by zero"))
        None
      } else {
        Some(IntValue(double_trunc(a / b).to_int64()))
      }
    Modulo =>
      if b == 0.0 {
        diagnostics.push(diag("division by zero"))
        None
      } else {
        Some(FloatValue(a - b * double_trunc(a / b)))
      }
    And | Or | NullCoalesce | Is | As | Pipe => panic()
  }
}

///|
fn double_trunc(x : Double) -> Double {
  // PKL-111: truncation toward zero. MoonBit's core does not expose
  // `Double::trunc` directly across both JS and native targets, so do it
  // by converting through Int round-trip on finite values; Infinity / NaN
  // pass through (used only for divisions where `b == 0` short-circuits
  // before reaching here).
  if x >= 0.0 {
    x.to_int64().to_double()
  } else {
    -(-x).to_int64().to_double()
  }
}

///|
/// Runtime matcher for the `is` operator. This checks generic collection
/// arguments and user-class tags; unlike the assignment/callable rejection
/// helpers, unions succeed when any branch fully matches.
fn eval_value_matches_type_annotation(
  type_name : String,
  value : Value,
  class_env : Array[ClassBinding],
  declarations : Array[Declaration],
) -> Bool {
  let aliases = eval_type_alias_bindings(declarations)
  let resolved = eval_resolved_type_alias(type_name, aliases)
  eval_value_matches_resolved_type_annotation(
    pkl_constraint_trim(resolved),
    value,
    class_env,
    declarations,
  )
}

///|
fn eval_value_matches_resolved_type_annotation(
  type_name : String,
  value : Value,
  class_env : Array[ClassBinding],
  declarations : Array[Declaration],
) -> Bool {
  let trimmed = pkl_strip_default_type_marker(pkl_constraint_trim(type_name))
  if trimmed == "" {
    return false
  }
  let choices = split_top_level_union_choices(trimmed)
  if choices.length() > 1 {
    for choice in choices {
      if eval_value_matches_type_annotation(
          choice, value, class_env, declarations,
        ) {
        return true
      }
    }
    return false
  }
  if trimmed.has_suffix("?") {
    if value is NullValue {
      return true
    }
    let inner = String::unsafe_substring(
      trimmed,
      start=0,
      end=trimmed.length() - 1,
    )
    return eval_value_matches_type_annotation(
      inner, value, class_env, declarations,
    )
  }
  match function_type_arity(trimmed) {
    Some(arity) =>
      return match value {
        FunctionValue(parameters, _, _, _, _) => parameters.length() == arity
        _ => false
      }
    None => ()
  }
  if trimmed.length() >= 2 &&
    trimmed.has_prefix("\"") &&
    trimmed.has_suffix("\"") {
    let literal = String::unsafe_substring(
      trimmed,
      start=1,
      end=trimmed.length() - 1,
    )
    return value is StringValue(s) && s == literal
  }
  let base = match pkl_constrained_type_base_name(trimmed) {
    Some(b) => b
    None => trimmed
  }
  match
    eval_value_matches_generic_type_annotation(
      base, value, class_env, declarations,
    ) {
    Some(ok) => return ok && eval_value_satisfies_constraint(trimmed, value)
    None => ()
  }
  if !eval_value_matches_bare_type(base, value, class_env) {
    return false
  }
  eval_value_satisfies_constraint(trimmed, value)
}

///|
fn eval_value_matches_generic_type_annotation(
  type_name : String,
  value : Value,
  class_env : Array[ClassBinding],
  declarations : Array[Declaration],
) -> Bool? {
  match generic_argument_text(type_name, "ref.Reference") {
    Some(_) =>
      return Some(
        match value {
          ObjectValue(members) => is_reference_value_members(members)
          _ => false
        },
      )
    None => ()
  }
  match generic_argument_text(type_name, "List") {
    Some(element_type) =>
      return match value {
        ListValue(elements) =>
          Some(
            eval_all_elements_match_type(
              elements, element_type, class_env, declarations,
            ),
          )
        _ => Some(false)
      }
    None => ()
  }
  match generic_argument_text(type_name, "Set") {
    Some(element_type) =>
      return match value {
        SetValue(elements) =>
          Some(
            eval_all_elements_match_type(
              elements, element_type, class_env, declarations,
            ),
          )
        _ => Some(false)
      }
    None => ()
  }
  match generic_argument_text(type_name, "Listing") {
    Some(element_type) =>
      return match value {
        ListingValue(elements) | DefaultedListingValue(_, elements, _) =>
          Some(
            eval_all_elements_match_type(
              elements, element_type, class_env, declarations,
            ),
          )
        _ => Some(false)
      }
    None => ()
  }
  match generic_argument_text(type_name, "Collection") {
    Some(element_type) =>
      return match value {
        ListValue(elements) | SetValue(elements) =>
          Some(
            eval_all_elements_match_type(
              elements, element_type, class_env, declarations,
            ),
          )
        _ => Some(false)
      }
    None => ()
  }
  match generic_argument_text(type_name, "Map") {
    Some(inner) =>
      return match value {
        MapValue(entries) =>
          Some(
            eval_all_entries_match_type(entries, inner, class_env, declarations),
          )
        _ => Some(false)
      }
    None => ()
  }
  match generic_argument_text(type_name, "Mapping") {
    Some(inner) =>
      return match value {
        MappingValue(entries) | DefaultedMappingValue(_, entries, _) =>
          Some(
            eval_all_entries_match_type(entries, inner, class_env, declarations),
          )
        _ => Some(false)
      }
    None => ()
  }
  match generic_argument_text(type_name, "Pair") {
    Some(inner) => {
      let parts = split_top_level_generic_arguments(inner)
      if parts.length() != 2 {
        return Some(false)
      }
      return match value {
        PairValue(first, second) =>
          Some(
            eval_value_matches_type_annotation(
              parts[0],
              first,
              class_env,
              declarations,
            ) &&
            eval_value_matches_type_annotation(
              parts[1],
              second,
              class_env,
              declarations,
            ),
          )
        _ => Some(false)
      }
    }
    None => ()
  }
  None
}

///|
fn eval_all_elements_match_type(
  elements : Array[Value],
  element_type : String,
  class_env : Array[ClassBinding],
  declarations : Array[Declaration],
) -> Bool {
  for element in elements {
    if !eval_value_matches_type_annotation(
        element_type, element, class_env, declarations,
      ) {
      return false
    }
  }
  true
}

///|
fn eval_all_entries_match_type(
  entries : Array[ValueEntry],
  inner : String,
  class_env : Array[ClassBinding],
  declarations : Array[Declaration],
) -> Bool {
  let parts = split_top_level_generic_arguments(inner)
  if parts.length() != 2 {
    return false
  }
  let key_type = parts[0]
  let value_type = parts[1]
  for entry in entries {
    if !eval_value_matches_type_annotation(
        key_type,
        entry.key,
        class_env,
        declarations,
      ) ||
      !eval_value_matches_type_annotation(
        value_type,
        entry.value,
        class_env,
        declarations,
      ) {
      return false
    }
  }
  true
}

///|
fn eval_value_matches_bare_type(
  type_name : String,
  value : Value,
  class_env : Array[ClassBinding],
) -> Bool {
  match type_name {
    "Any" | "unknown" => true
    "Int" => value is IntValue(_)
    "Float" => value is FloatValue(_)
    "Number" => value is IntValue(_) || value is FloatValue(_)
    "String" => value is StringValue(_)
    "Boolean" | "Bool" => value is BoolValue(_)
    "Null" => value is NullValue
    "Duration" => value is DurationValue(_, _)
    "DataSize" => value is DataSizeValue(_, _)
    "Regex" => value is RegexValue(_)
    "Bytes" => value is BytesValue(_)
    "List" => value is ListValue(_)
    "Set" => value is SetValue(_)
    "Collection" => value is ListValue(_) || value is SetValue(_)
    "Map" => value is MapValue(_)
    "Listing" =>
      value is ListingValue(_) || value is DefaultedListingValue(_, _, _)
    "Mapping" =>
      value is MappingValue(_) || value is DefaultedMappingValue(_, _, _)
    "Pair" => value is PairValue(_, _)
    "ref.Reference" | "Reference" =>
      match value {
        ObjectValue(members) => is_reference_value_members(members)
        _ => false
      }
    "IntSeq" => value is IntSeqValue(_, _, _)
    "Mixin" =>
      match value {
        ObjectValue(members) =>
          object_class_tag_matches(members, "Mixin") ||
          object_members_are_mixin_body(members)
        _ => false
      }
    "Object" | "Dynamic" | "Typed" => value is ObjectValue(_)
    "Class" =>
      match value {
        ObjectValue(members) => reflect_kind(members) is Some("Class")
        _ => false
      }
    "Function" => value is FunctionValue(_, _, _, _, _)
    _ =>
      match function_arity_from_type_name(type_name) {
        Some(arity) =>
          match value {
            FunctionValue(parameters, _, _, _, _) =>
              parameters.length() == arity
            _ => false
          }
        None => eval_value_matches_user_class_tag(type_name, value, class_env)
      }
  }
}

///|
fn eval_value_matches_user_class_tag(
  type_name : String,
  value : Value,
  class_env : Array[ClassBinding],
) -> Bool {
  match value {
    ObjectValue(members) =>
      match find_object_class_tag(members) {
        Some(tag) => {
          let chain = class_chain_for_tag(tag, class_env)
          for ancestor in chain {
            if name_matches_class_tag(type_name, ancestor) {
              return true
            }
          }
          false
        }
        None =>
          eval_untagged_object_matches_class_shape(
            members, type_name, class_env,
          )
      }
    _ => false
  }
}

///|
fn eval_untagged_object_matches_class_shape(
  members : Array[ValueMember],
  type_name : String,
  class_env : Array[ClassBinding],
) -> Bool {
  let declared : Array[String] = []
  collect_class_property_names(declared, type_name, class_env)
  if declared.length() == 0 {
    return false
  }
  for name in declared {
    if lookup_member(members, name) is None {
      return false
    }
  }
  true
}

///|
fn function_arity_from_type_name(type_name : String) -> Int? {
  if !type_name.has_prefix("Function") {
    return None
  }
  if type_name == "Function" {
    return None
  }
  let suffix = String::unsafe_substring(
    type_name,
    start="Function".length(),
    end=type_name.length(),
  )
  parse_nonnegative_int(suffix)
}

///|
fn eval_value_satisfies_constraint(type_name : String, value : Value) -> Bool {
  pkl_constrained_type_annotation_value_rejection_message_from_source(
    type_name, type_name, value,
  )
  is None
}

///|
fn parse_nonnegative_int(text : String) -> Int? {
  if text == "" {
    return None
  }
  let mut value = 0
  for i = 0; i < text.length(); i = i + 1 {
    let c = text[i].to_int().unsafe_to_char()
    if c < '0' || c > '9' {
      return None
    }
    value = value * 10 + c.to_int() - '0'.to_int()
  }
  Some(value)
}

///|

///|
fn eval_source_with_imports(
  source : String,
  resolve_import : (String) -> EvalResult?,
) -> EvalResult {
  eval_source_with_import_details(source, resolve_import, fn(_) { None }, fn(
    _,
  ) {
    None
  })
}

///|
fn eval_source_with_import_details(
  source : String,
  resolve_import : (String) -> EvalResult?,
  resolve_import_classes : (String) -> Array[ClassExport]?,
  resolve_import_bindings : (String) -> Array[Binding]?,
) -> EvalResult {
  eval_source_with_import_details_named(
    source,
    None,
    resolve_import,
    resolve_import_classes,
    resolve_import_bindings,
  )
}

///|
/// PKL-148bh: extended form accepting a fallback module name (derived
/// from the file path when the source omits an explicit
/// `module X` header). Threaded through `eval_program` so reflect
/// mirrors can build the `#` qualified shape Apple
/// Pkl prints from Class.toString / TypeAlias.toString.
fn eval_source_with_import_details_named(
  source : String,
  fallback_module_name : String?,
  resolve_import : (String) -> EvalResult?,
  resolve_import_classes : (String) -> Array[ClassExport]?,
  resolve_import_bindings : (String) -> Array[Binding]?,
) -> EvalResult {
  eval_source_with_import_details_named_at(
    source,
    fallback_module_name,
    None,
    resolve_import,
    resolve_import_classes,
    resolve_import_bindings,
  )
}

///|
fn eval_source_with_import_details_named_at(
  source : String,
  fallback_module_name : String?,
  current_module_path : String?,
  resolve_import : (String) -> EvalResult?,
  resolve_import_classes : (String) -> Array[ClassExport]?,
  resolve_import_bindings : (String) -> Array[Binding]?,
) -> EvalResult {
  let parsed = parse_source(source)
  let diagnostics = parsed.diagnostics
  if diagnostics.length() > 0 {
    return EvalError(diagnostics)
  }
  let program = parsed.program
  let effective_name = match program.module_name {
    Some(_) => program.module_name
    None => fallback_module_name
  }
  let promoted_program : Program = if effective_name == program.module_name {
    program
  } else {
    {
      module_name: effective_name,
      module_relation: program.module_relation,
      imports: program.imports,
      declarations: program.declarations,
      bindings: program.bindings,
      body: program.body,
      module_annotations: program.module_annotations,
    }
  }
  match
    eval_program(
      promoted_program,
      current_module_path,
      Some(source),
      diagnostics,
      resolve_import,
      resolve_import_classes,
      resolve_import_bindings,
    ) {
    Some(value) =>
      if diagnostics.length() == 0 {
        EvalOk(value)
      } else {
        EvalError(diagnostics)
      }
    None => EvalError(diagnostics)
  }
}