hara-native 0.1.21

HAL-free native host runtime and package launcher for Hara
Documentation
use super::*;

pub(super) fn slot_head(
    values: &[VmSlot],
    limit: usize,
    display_chars: usize,
) -> (Vec<ValueSnapshot>, usize) {
    let retained = values.len().min(limit);
    (
        values[..retained]
            .iter()
            .map(|value| slot_snapshot(value, display_chars))
            .collect(),
        values.len().saturating_sub(retained),
    )
}

pub(super) fn slot_tail(
    values: &[VmSlot],
    limit: usize,
    display_chars: usize,
) -> (Vec<ValueSnapshot>, usize) {
    let start = values.len().saturating_sub(limit);
    (
        values[start..]
            .iter()
            .map(|value| slot_snapshot(value, display_chars))
            .collect(),
        start,
    )
}

pub(super) fn slot_snapshot(value: &VmSlot, display_chars: usize) -> ValueSnapshot {
    match value {
        VmSlot::Number(value) => bounded_value("number", value.to_string(), display_chars),
        VmSlot::Bool(value) => bounded_value("boolean", value.to_string(), display_chars),
        VmSlot::Nil => bounded_value("nil", "nil".to_string(), display_chars),
        VmSlot::Value(value) => value_snapshot(value, display_chars),
        VmSlot::InlineClosure {
            prototype,
            identity,
        } => bounded_value(
            "closure",
            format!("<closure prototype={prototype} identity={identity}>"),
            display_chars,
        ),
        VmSlot::Closure(closure) => bounded_value(
            "closure",
            format!(
                "<closure prototype={} captures={}",
                closure.prototype,
                closure.captures.len()
            ) + ">",
            display_chars,
        ),
        VmSlot::MultiArity(dispatch) => bounded_value(
            "multi-arity",
            format!(
                "<multi-arity {} clauses={}",
                dispatch.name,
                dispatch.clauses.len()
            ) + ">",
            display_chars,
        ),
    }
}

pub(super) fn value_snapshot(value: &Value, display_chars: usize) -> ValueSnapshot {
    let kind = match value {
        Value::Number(_) => "number",
        Value::Bool(_) => "boolean",
        Value::Nil => "nil",
        Value::String(_) => "string",
        Value::Promise(_) => "promise",
        _ => "value",
    };
    bounded_value(kind, value.display(), display_chars)
}

pub(super) fn bounded_value(kind: &'static str, display: String, limit: usize) -> ValueSnapshot {
    let mut chars = display.chars();
    let mut bounded: String = chars.by_ref().take(limit).collect();
    let truncated = chars.next().is_some();
    if truncated {
        bounded.push('…');
    }
    ValueSnapshot {
        kind,
        display: bounded,
        truncated,
    }
}

pub(super) fn position_snapshot(position: Position) -> SourcePositionSnapshot {
    SourcePositionSnapshot {
        offset: position.offset,
        line: position.line,
        column: position.column,
    }
}

pub(super) fn instruction_snapshot(instruction: &Instruction) -> InstructionSnapshot {
    use InstructionOperand::Unsigned;

    let (opcode, operands) = match instruction {
        Instruction::Constant(index) => ("constant", vec![Unsigned(*index as u64)]),
        Instruction::Nil => ("nil", vec![]),
        Instruction::True => ("true", vec![]),
        Instruction::False => ("false", vec![]),
        Instruction::LoadLocal(slot) => ("load-local", vec![Unsigned(*slot as u64)]),
        Instruction::StoreLocal(slot) => ("store-local", vec![Unsigned(*slot as u64)]),
        Instruction::Pop => ("pop", vec![]),
        Instruction::Dup => ("dup", vec![]),
        Instruction::IntrinsicCall { target, argc } => (
            "intrinsic-call",
            vec![Unsigned(*target as u64), Unsigned(*argc as u64)],
        ),
        Instruction::Jump(target) => ("jump", vec![Unsigned(*target as u64)]),
        Instruction::JumpIfFalse(target) => ("jump-if-false", vec![Unsigned(*target as u64)]),
        Instruction::Closure {
            prototype,
            captures,
        } => (
            "closure",
            vec![Unsigned(*prototype as u64), Unsigned(*captures as u64)],
        ),
        Instruction::Call { argc } => ("call", vec![Unsigned(*argc as u64)]),
        Instruction::CallStatic { prototype, argc } => (
            "call-static",
            vec![Unsigned(*prototype as u64), Unsigned(*argc as u64)],
        ),
        Instruction::Throw => ("throw", vec![]),
        Instruction::Rethrow => ("rethrow", vec![]),
        Instruction::GetGlobal(index) => ("get-global", vec![Unsigned(*index as u64)]),
        Instruction::DefGlobal { name, metadata } => {
            let mut operands = vec![Unsigned(*name as u64)];
            if let Some(metadata) = metadata {
                operands.push(Unsigned(*metadata as u64));
            }
            ("def-global", operands)
        }
        Instruction::SetGlobal(index) => ("set-global", vec![Unsigned(*index as u64)]),
        Instruction::VarGlobal(index) => ("var-global", vec![Unsigned(*index as u64)]),
        Instruction::DeclareGlobal(index) => ("declare-global", vec![Unsigned(*index as u64)]),
        Instruction::MutableFieldGet(index) => ("mutable-field-get", vec![Unsigned(*index as u64)]),
        Instruction::MutableFieldSet(index) => ("mutable-field-set", vec![Unsigned(*index as u64)]),
        Instruction::InstanceOf => ("instance-of", vec![]),
        Instruction::MakeMultiArity { name, count } => (
            "make-multi-arity",
            vec![Unsigned(*name as u64), Unsigned(*count as u64)],
        ),
        Instruction::BuildVector(count) => ("build-vector", vec![Unsigned(*count as u64)]),
        Instruction::BuildMap(pairs) => ("build-map", vec![Unsigned(*pairs as u64)]),
        Instruction::BuildSet(count) => ("build-set", vec![Unsigned(*count as u64)]),
        Instruction::BuildList(count) => ("build-list", vec![Unsigned(*count as u64)]),
        Instruction::ConcatList(count) => ("concat-list", vec![Unsigned(*count as u64)]),
        Instruction::ToVector => ("to-vector", vec![]),
        Instruction::DefMacro { name, metadata } => {
            let mut operands = vec![Unsigned(*name as u64)];
            if let Some(metadata) = metadata {
                operands.push(Unsigned(*metadata as u64));
            }
            ("def-macro", operands)
        }
        Instruction::IntrinsicValue(target) => ("intrinsic-value", vec![Unsigned(*target as u64)]),
        Instruction::BuiltinValue(index) => ("builtin-value", vec![Unsigned(*index as u64)]),
        Instruction::NamespaceValue(index) => ("namespace-value", vec![Unsigned(*index as u64)]),
        Instruction::NamespaceOperation(index) => {
            ("namespace-operation", vec![Unsigned(*index as u64)])
        }
        Instruction::DynamicBind(index) => ("dynamic-bind", vec![Unsigned(*index as u64)]),
        Instruction::DynamicUnbind(index) => ("dynamic-unbind", vec![Unsigned(*index as u64)]),
        Instruction::Await => ("await", vec![]),
        Instruction::HostCall => ("host-call", vec![]),
        Instruction::DotCall { method, argc } => (
            "dot-call",
            vec![Unsigned(*method as u64), Unsigned(*argc as u64)],
        ),
        Instruction::ProtocolCall { target, argc } => (
            "protocol-call",
            vec![Unsigned(*target as u64), Unsigned(*argc as u64)],
        ),
        Instruction::Yield => ("yield", vec![]),
        Instruction::Return => ("return", vec![]),
    };

    InstructionSnapshot {
        opcode,
        operands,
        display: instruction.to_string(),
    }
}