use std::fmt;
use synoema_parser::{Pat, Expr, Equation};
#[derive(Debug, Clone)]
pub enum Value {
Int(i64),
Float(f64),
Bool(bool),
Str(String),
Char(char),
List(Vec<Value>),
Con(String, Vec<Value>),
Closure {
params: Vec<Pat>,
body: Expr,
env: Env,
},
Func {
name: String,
equations: Vec<Equation>,
env: Env,
},
Builtin(String, usize), PartialBuiltin(String, usize, Vec<Value>), Record(Vec<(String, Value)>),
Unit,
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Int(a), Value::Int(b)) => a == b,
(Value::Float(a), Value::Float(b)) => a == b,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Str(a), Value::Str(b)) => a == b,
(Value::Char(a), Value::Char(b)) => a == b,
(Value::List(a), Value::List(b)) => a == b,
(Value::Con(na, fa), Value::Con(nb, fb)) => na == nb && fa == fb,
(Value::Record(a), Value::Record(b)) => a == b,
(Value::Unit, Value::Unit) => true,
_ => false,
}
}
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (self, other) {
(Value::Int(a), Value::Int(b)) => a.partial_cmp(b),
(Value::Float(a), Value::Float(b)) => a.partial_cmp(b),
(Value::Str(a), Value::Str(b)) => a.partial_cmp(b),
(Value::Char(a), Value::Char(b)) => a.partial_cmp(b),
(Value::Bool(a), Value::Bool(b)) => a.partial_cmp(b),
_ => None,
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Int(n) => write!(f, "{}", n),
Value::Float(n) => {
if *n == (*n as i64) as f64 {
write!(f, "{:.1}", n)
} else {
write!(f, "{}", n)
}
}
Value::Bool(b) => write!(f, "{}", b),
Value::Str(s) => write!(f, "{}", s),
Value::Char(c) => write!(f, "{}", c),
Value::List(elems) => {
write!(f, "[")?;
for (i, e) in elems.iter().enumerate() {
if i > 0 { write!(f, " ")?; }
write!(f, "{}", e)?;
}
write!(f, "]")
}
Value::Con(name, fields) => {
write!(f, "{}", name)?;
for fld in fields {
write!(f, " ")?;
match fld {
Value::Con(_, fs) if !fs.is_empty() => write!(f, "({})", fld)?,
_ => write!(f, "{}", fld)?,
}
}
Ok(())
}
Value::Record(fields) => {
write!(f, "{{")?;
for (i, (name, val)) in fields.iter().enumerate() {
if i > 0 { write!(f, ", ")?; }
write!(f, "{} = {}", name, val)?;
}
write!(f, "}}")
}
Value::Closure { .. } => write!(f, "<closure>"),
Value::Func { name, .. } => write!(f, "<fn {}>", name),
Value::Builtin(name, _) => write!(f, "<builtin {}>", name),
Value::PartialBuiltin(name, _, _) => write!(f, "<partial {}>", name),
Value::Unit => write!(f, "()"),
}
}
}
#[derive(Debug, Clone)]
pub struct Env {
frames: Vec<std::collections::HashMap<String, Value>>,
}
impl Env {
pub fn new() -> Self {
Env { frames: vec![std::collections::HashMap::new()] }
}
pub fn lookup(&self, name: &str) -> Option<&Value> {
for frame in self.frames.iter().rev() {
if let Some(v) = frame.get(name) {
return Some(v);
}
}
None
}
pub fn insert(&mut self, name: String, val: Value) {
if let Some(frame) = self.frames.last_mut() {
frame.insert(name, val);
}
}
pub fn push_scope(&mut self) {
self.frames.push(std::collections::HashMap::new());
}
pub fn pop_scope(&mut self) {
if self.frames.len() > 1 {
self.frames.pop();
}
}
pub fn child(&self) -> Self {
let mut e = self.clone();
e.push_scope();
e
}
pub fn iter_all(&self) -> Vec<(String, Value)> {
let mut seen = std::collections::HashSet::new();
let mut result = Vec::new();
for frame in self.frames.iter().rev() {
for (k, v) in frame {
if seen.insert(k.clone()) {
result.push((k.clone(), v.clone()));
}
}
}
result
}
}