use crate::ast;
use std::collections::HashMap;
use std::error;
use std::fmt;
use std::result;
#[derive(Clone, Debug, PartialEq)]
pub enum Object {
Null,
Integer(i64),
Float(f64),
Boolean(bool),
String(String),
ReturnValue(Box<Object>),
Function(Function),
Builtin(Builtin),
Array(Array),
Hash(Hash),
}
impl fmt::Display for Object {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Object::Null => write!(f, "null"),
Object::Integer(i) => i.fmt(f),
Object::Float(fl) => fl.fmt(f),
Object::Boolean(b) => b.fmt(f),
Object::String(s) => s.fmt(f),
Object::ReturnValue(r) => write!(f, "return({})", r),
Object::Function(func) => func.fmt(f),
Object::Builtin(b) => b.fmt(f),
Object::Array(a) => a.fmt(f),
Object::Hash(h) => h.fmt(f),
}
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Environment {
store: HashMap<String, Object>,
outer: Option<Box<Environment>>,
}
impl Environment {
pub fn new() -> Self {
Environment {
store: HashMap::new(),
outer: None,
}
}
pub fn new_enclosed(outer: Self) -> Self {
let mut env = Self::new();
env.outer = Some(Box::new(outer));
env
}
pub fn get(&self, name: &str) -> Option<&Object> {
match (self.store.get(name), &self.outer) {
(Some(obj), _) => Some(obj),
(None, Some(outer)) => outer.get(name),
(None, _) => None,
}
}
pub fn set(&mut self, name: String, obj: &Object) -> Object {
self.store.insert(name, obj.clone());
obj.clone()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Function {
pub parameters: Vec<String>,
pub body: ast::BlockStatement,
pub env: Environment,
}
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let params = self.parameters.join(", ");
write!(f, "fn({}) {{\n{}\n}}", params, self.body)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Builtin {
First,
Last,
Len,
Push,
Puts,
Rest,
}
impl Builtin {
pub fn lookup(name: &str) -> Option<Self> {
match name {
"first" => Some(Builtin::First),
"last" => Some(Builtin::Last),
"len" => Some(Builtin::Len),
"push" => Some(Builtin::Push),
"puts" => Some(Builtin::Puts),
"rest" => Some(Builtin::Rest),
_ => None,
}
}
pub fn apply(&self, args: &[Object]) -> Result<Object> {
match self {
Builtin::First => builtin_first(&args),
Builtin::Last => builtin_last(&args),
Builtin::Len => builtin_len(&args),
Builtin::Push => builtin_push(&args),
Builtin::Puts => builtin_puts(&args),
Builtin::Rest => builtin_rest(&args),
}
}
}
impl fmt::Display for Builtin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Builtin::First => write!(f, "first"),
Builtin::Last => write!(f, "last"),
Builtin::Len => write!(f, "len"),
Builtin::Push => write!(f, "push"),
Builtin::Puts => write!(f, "puts"),
Builtin::Rest => write!(f, "rest"),
}
}
}
fn builtin_first(args: &[Object]) -> Result<Object> {
if args.len() != 1 {
return Err(Error::Builtin(
Builtin::First,
format!("expected 1 argument, but got {}", args.len()),
));
}
if let Object::Array(a) = &args[0] {
if !a.elements.is_empty() {
Ok(a.elements.first().unwrap().clone())
} else {
Ok(Object::Null)
}
} else {
Err(Error::Builtin(
Builtin::First,
format!("argument {} is not an array", &args[0]),
))
}
}
fn builtin_last(args: &[Object]) -> Result<Object> {
if args.len() != 1 {
return Err(Error::Builtin(
Builtin::Last,
format!("expected 1 argument, but got {}", args.len()),
));
}
if let Object::Array(a) = &args[0] {
if !a.elements.is_empty() {
Ok(a.elements.last().unwrap().clone())
} else {
Ok(Object::Null)
}
} else {
Err(Error::Builtin(
Builtin::Last,
format!("argument {} is not an array", &args[0]),
))
}
}
fn builtin_len(args: &[Object]) -> Result<Object> {
if args.len() != 1 {
return Err(Error::Builtin(
Builtin::Len,
format!("expected 1 argument, but got {}", args.len()),
));
}
match &args[0] {
Object::Array(a) => Ok(Object::Integer(a.elements.len() as i64)),
Object::String(s) => Ok(Object::Integer(s.len() as i64)),
_ => Err(Error::Builtin(
Builtin::Len,
format!("argument {} cannot be used", &args[0]),
)),
}
}
fn builtin_push(args: &[Object]) -> Result<Object> {
if args.len() != 2 {
return Err(Error::Builtin(
Builtin::Push,
format!("expected 2 argument, but got {}", args.len()),
));
}
let arr = if let Object::Array(a) = &args[0] {
a
} else {
return Err(Error::Builtin(
Builtin::Push,
format!("first argument {} is not an array", &args[0]),
));
};
let mut out = arr.elements.clone();
out.push(args[1].clone());
Ok(Object::Array(Array { elements: out }))
}
fn builtin_puts(args: &[Object]) -> Result<Object> {
for a in args {
println!("{}", a);
}
Ok(Object::Null)
}
fn builtin_rest(args: &[Object]) -> Result<Object> {
if args.len() != 1 {
return Err(Error::Builtin(
Builtin::Rest,
format!("expected 1 argument, but got {}", args.len()),
));
}
if let Object::Array(a) = &args[0] {
if !a.elements.is_empty() {
Ok(Object::Array(Array {
elements: a.elements.iter().skip(1).cloned().collect(),
}))
} else {
Ok(Object::Null)
}
} else {
Err(Error::Builtin(
Builtin::Rest,
format!("argument {} is not an array", &args[0]),
))
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Array {
pub elements: Vec<Object>,
}
impl fmt::Display for Array {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"[{}]",
self.elements
.iter()
.map(|e| format!("{}", e))
.collect::<Vec<String>>()
.join(", ")
)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Hashable {
Integer(i64),
Boolean(bool),
String(String),
}
impl fmt::Display for Hashable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Hashable::Integer(i) => i.fmt(f),
Hashable::Boolean(b) => b.fmt(f),
Hashable::String(s) => s.fmt(f),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Hash {
pub pairs: HashMap<Hashable, Object>,
}
impl fmt::Display for Hash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut pairs = vec![];
for pair in &self.pairs {
pairs.push(format!(r#"{}: {}"#, pair.0, pair.1));
}
pairs.sort();
write!(f, "{{{}}}", pairs.join(", "))
}
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug, PartialEq)]
pub enum Error {
Builtin(Builtin, String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Builtin(b, err) => write!(f, "built-in {}: {}", b, err),
}
}
}
impl error::Error for Error {
fn cause(&self) -> Option<&dyn error::Error> {
None
}
}