use aver_rt::{AverList, AverVector};
use std::collections::HashMap;
use std::sync::Arc as Rc;
use thiserror::Error;
use crate::ast::FnBody;
use crate::nan_value::NanValue;
mod memo;
pub use memo::hash_memo_args;
#[derive(Debug, Error)]
pub enum RuntimeError {
#[error("Runtime error: {0}")]
Error(String),
#[error("Runtime error [line {line}]: {msg}")]
ErrorAt { msg: String, line: usize },
#[error("Error propagation")]
ErrProp(NanValue), #[error("Tail call")]
TailCall(Box<(String, Vec<NanValue>)>),
#[error("Replay mismatch at seq {seq}: expected '{expected}', got '{got}'")]
ReplayMismatch {
seq: u32,
expected: String,
got: String,
},
#[error(
"Replay args mismatch at seq {seq} for '{effect_type}': expected {expected}, got {got}"
)]
ReplayArgsMismatch {
seq: u32,
effect_type: String,
expected: String,
got: String,
},
#[error("Replay exhausted at position {position}: no recorded effect for call '{effect_type}'")]
ReplayExhausted {
effect_type: String,
position: usize,
},
#[error("Replay has {remaining} unconsumed effect(s)")]
ReplayUnconsumed { remaining: usize },
#[error("Replay serialization error: {0}")]
ReplaySerialization(String),
}
impl RuntimeError {
pub fn at_line(self, line: usize) -> Self {
if line == 0 {
return self;
}
match self {
RuntimeError::Error(msg) => RuntimeError::ErrorAt { msg, line },
other => other,
}
}
pub fn message(&self) -> &str {
match self {
RuntimeError::Error(msg) | RuntimeError::ErrorAt { msg, .. } => msg,
other => {
match other {
RuntimeError::Error(_) | RuntimeError::ErrorAt { .. } => unreachable!(),
_ => "",
}
}
}
}
pub fn source_line(&self) -> Option<usize> {
match self {
RuntimeError::ErrorAt { line, .. } if *line > 0 => Some(*line),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct FunctionValue {
pub name: Rc<String>,
pub params: Rc<Vec<(String, String)>>,
pub return_type: Rc<String>,
pub effects: Rc<Vec<String>>,
pub body: Rc<FnBody>,
pub(crate) lowered_body: Rc<crate::interpreter::lowered::LoweredFunctionBody>,
pub resolution: Option<crate::ast::FnResolution>,
pub memo_eligible: bool,
pub home_globals: Option<Rc<HashMap<String, NanValue>>>,
}
#[derive(Debug, Clone)]
pub enum Value {
Int(i64),
Float(f64),
Str(String),
Bool(bool),
Unit,
Ok(Box<Value>),
Err(Box<Value>),
Some(Box<Value>),
None,
List(AverList<Value>),
Vector(AverVector<Value>),
Tuple(Vec<Value>),
Map(HashMap<Value, Value>),
Fn(Rc<FunctionValue>),
Builtin(String),
Variant {
type_name: String,
variant: String,
fields: Rc<[Value]>,
},
Record {
type_name: String,
fields: Rc<[(String, Value)]>,
},
Namespace {
name: String,
members: HashMap<String, Value>,
},
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (list_view(self), list_view(other)) {
(Some(xs), Some(ys)) => return xs.iter().eq(ys.iter()),
(Some(_), None) | (None, Some(_)) => return false,
(None, None) => {}
}
match (self, other) {
(Value::Int(a), Value::Int(b)) => a == b,
(Value::Float(a), Value::Float(b)) => {
if a.is_nan() || b.is_nan() {
a.to_bits() == b.to_bits()
} else {
a == b
}
}
(Value::Str(a), Value::Str(b)) => a == b,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Unit, Value::Unit) => true,
(Value::Ok(a), Value::Ok(b)) => a == b,
(Value::Err(a), Value::Err(b)) => a == b,
(Value::Some(a), Value::Some(b)) => a == b,
(Value::None, Value::None) => true,
(Value::Vector(a), Value::Vector(b)) => a == b,
(Value::Tuple(a), Value::Tuple(b)) => a == b,
(Value::Map(a), Value::Map(b)) => a == b,
(Value::Fn(a), Value::Fn(b)) => {
a.name == b.name
&& a.params == b.params
&& a.return_type == b.return_type
&& a.effects == b.effects
&& a.body == b.body
}
(Value::Builtin(a), Value::Builtin(b)) => a == b,
(
Value::Variant {
type_name: t1,
variant: v1,
fields: f1,
},
Value::Variant {
type_name: t2,
variant: v2,
fields: f2,
},
) => t1 == t2 && v1 == v2 && f1 == f2,
(
Value::Record {
type_name: t1,
fields: f1,
},
Value::Record {
type_name: t2,
fields: f2,
},
) => t1 == t2 && f1 == f2,
(
Value::Namespace {
name: n1,
members: m1,
},
Value::Namespace {
name: n2,
members: m2,
},
) => n1 == n2 && m1 == m2,
_ => false,
}
}
}
impl Eq for Value {}
impl std::hash::Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
if let Some(items) = list_view(self) {
8u8.hash(state);
items.len().hash(state);
for item in items.iter() {
item.hash(state);
}
return;
}
match self {
Value::Int(i) => {
0u8.hash(state);
i.hash(state);
}
Value::Float(f) => {
1u8.hash(state);
let bits = if *f == 0.0 {
0.0f64.to_bits()
} else {
f.to_bits()
};
bits.hash(state);
}
Value::Str(s) => {
2u8.hash(state);
s.hash(state);
}
Value::Bool(b) => {
3u8.hash(state);
b.hash(state);
}
Value::Unit => {
4u8.hash(state);
}
Value::Ok(v) => {
5u8.hash(state);
v.hash(state);
}
Value::Err(v) => {
6u8.hash(state);
v.hash(state);
}
Value::Some(v) => {
7u8.hash(state);
v.hash(state);
}
Value::None => {
9u8.hash(state);
}
Value::Map(map) => {
10u8.hash(state);
let mut entries = map.iter().collect::<Vec<_>>();
entries.sort_by(|(k1, _), (k2, _)| aver_repr(k1).cmp(&aver_repr(k2)));
for (k, v) in entries {
k.hash(state);
v.hash(state);
}
}
Value::Vector(vec) => {
17u8.hash(state);
vec.hash(state);
}
Value::Tuple(items) => {
16u8.hash(state);
items.hash(state);
}
Value::Fn(function) => {
11u8.hash(state);
function.name.hash(state);
function.params.hash(state);
function.return_type.hash(state);
function.effects.hash(state);
format!("{:?}", function.body).hash(state);
}
Value::Builtin(name) => {
12u8.hash(state);
name.hash(state);
}
Value::Variant {
type_name,
variant,
fields,
} => {
13u8.hash(state);
type_name.hash(state);
variant.hash(state);
fields.hash(state);
}
Value::Record { type_name, fields } => {
14u8.hash(state);
type_name.hash(state);
fields.hash(state);
}
Value::Namespace { name, members } => {
15u8.hash(state);
name.hash(state);
let mut keys = members.keys().collect::<Vec<_>>();
keys.sort();
for key in keys {
key.hash(state);
if let Some(value) = members.get(key) {
value.hash(state);
}
}
}
Value::List(_) => unreachable!("list hashed above"),
}
}
}
#[derive(Debug, Clone)]
pub enum EnvFrame {
Owned(HashMap<String, NanValue>),
Shared(Rc<HashMap<String, NanValue>>),
Slots(Vec<NanValue>),
}
pub type Env = Vec<EnvFrame>;
pub(crate) type ListView<'a> = &'a AverList<Value>;
pub(crate) fn list_view(value: &Value) -> Option<ListView<'_>> {
match value {
Value::List(items) => Some(items),
_ => None,
}
}
pub fn list_slice(value: &Value) -> Option<&[Value]> {
list_view(value).and_then(AverList::as_slice)
}
pub fn list_from_vec(items: Vec<Value>) -> Value {
Value::List(AverList::from_vec(items))
}
pub fn list_to_vec(value: &Value) -> Option<Vec<Value>> {
list_view(value).map(AverList::to_vec)
}
pub fn list_len(value: &Value) -> Option<usize> {
list_view(value).map(AverList::len)
}
pub fn list_head(value: &Value) -> Option<Value> {
list_view(value).and_then(|items| items.first().cloned())
}
pub(crate) fn list_prepend(item: Value, list: &Value) -> Option<Value> {
list_view(list).map(|items| Value::List(AverList::prepend(item, items)))
}
pub(crate) fn list_concat(left: &Value, right: &Value) -> Option<Value> {
let left = list_view(left)?;
let right = list_view(right)?;
Some(Value::List(AverList::concat(left, right)))
}
pub(crate) fn list_reverse(list: &Value) -> Option<Value> {
list_view(list).map(|items| Value::List(items.reverse()))
}
pub fn aver_repr(val: &Value) -> String {
if let Some(items) = list_view(val) {
let parts: Vec<String> = items.iter().map(aver_repr_inner).collect();
return format!("[{}]", parts.join(", "));
}
match val {
Value::Int(i) => i.to_string(),
Value::Float(f) => f.to_string(),
Value::Str(s) => s.clone(),
Value::Bool(b) => if *b { "true" } else { "false" }.to_string(),
Value::Unit => "Unit".to_string(),
Value::Ok(v) => format!("Result.Ok({})", aver_repr_inner(v)),
Value::Err(v) => format!("Result.Err({})", aver_repr_inner(v)),
Value::Some(v) => format!("Option.Some({})", aver_repr_inner(v)),
Value::None => "Option.None".to_string(),
Value::Tuple(items) => {
let parts: Vec<String> = items.iter().map(aver_repr_inner).collect();
format!("({})", parts.join(", "))
}
Value::Vector(vec) => {
let parts: Vec<String> = vec.iter().map(aver_repr_inner).collect();
format!("Vector[{}]", parts.join(", "))
}
Value::List(_) => unreachable!("handled via list_view above"),
Value::Map(entries) => {
let mut pairs = entries
.iter()
.map(|(k, v)| (aver_repr_inner(k), aver_repr_inner(v)))
.collect::<Vec<_>>();
pairs.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
let parts = pairs
.into_iter()
.map(|(k, v)| format!("{}: {}", k, v))
.collect::<Vec<_>>();
format!("{{{}}}", parts.join(", "))
}
Value::Fn(function) => format!("<fn {}>", function.name),
Value::Builtin(name) => format!("<builtin {}>", name),
Value::Variant {
variant, fields, ..
} => {
if fields.is_empty() {
variant.clone()
} else {
let parts: Vec<String> = fields.iter().map(aver_repr_inner).collect();
format!("{}({})", variant, parts.join(", "))
}
}
Value::Record { type_name, fields } => {
let parts: Vec<String> = fields
.iter()
.map(|(k, v)| format!("{}: {}", k, aver_repr_inner(v)))
.collect();
format!("{}({})", type_name, parts.join(", "))
}
Value::Namespace { name, .. } => format!("<type {}>", name),
}
}
fn aver_repr_inner(val: &Value) -> String {
if let Some(items) = list_view(val) {
let parts: Vec<String> = items.iter().map(aver_repr_inner).collect();
return format!("[{}]", parts.join(", "));
}
match val {
Value::Str(s) => format!("\"{}\"", s),
Value::Tuple(items) => {
let parts: Vec<String> = items.iter().map(aver_repr_inner).collect();
format!("({})", parts.join(", "))
}
Value::Vector(vec) => {
let parts: Vec<String> = vec.iter().map(aver_repr_inner).collect();
format!("Vector[{}]", parts.join(", "))
}
Value::List(_) => unreachable!("handled via list_view above"),
other => aver_repr(other),
}
}
pub fn aver_display(val: &Value) -> Option<String> {
match val {
Value::Unit => None,
other => Some(aver_repr(other)),
}
}