use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;
use crate::objects::{
read_instance_variable, write_instance_variable, Arg, Class, Datum, Eval, Foolang, Object,
Source, Vtable,
};
use crate::parse::{
Array, Assign, Bind, Block, Cascade, Chain, ClassDefinition, ClassExtension, Const, Dictionary,
Eq, Expr, Global, Import, InterfaceDefinition, Literal, Message, Parser, Raise, Return, Seq,
Typecheck, Var,
};
use crate::tokenstream::Span;
use crate::unwind::Unwind;
#[derive(Debug, Clone, PartialEq)]
pub struct Binding {
vtable: Option<Rc<Vtable>>,
pub value: Object,
}
impl Binding {
pub fn untyped(init: Object) -> Binding {
Binding {
vtable: None,
value: init,
}
}
pub fn typed(vtable: Rc<Vtable>, init: Object) -> Binding {
Binding {
vtable: Some(vtable),
value: init,
}
}
pub fn assign(&mut self, value: Object) -> Eval {
if let Some(vtable) = &self.vtable {
value.typecheck(vtable)?;
}
self.value = value.clone();
Ok(value)
}
}
type SymbolTable = HashMap<String, Binding>;
#[derive(Debug)]
pub struct EnvFrame {
depth: u32,
symbols: SymbolTable,
parent: Option<EnvRef>,
home: Option<EnvRef>,
receiver: Option<Object>,
}
#[derive(Debug, Clone)]
pub struct EnvRef {
frame: Rc<RefCell<EnvFrame>>,
}
impl PartialEq for EnvRef {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(&*self.frame, &*other.frame)
}
}
impl EnvRef {
pub fn new() -> EnvRef {
EnvRef {
frame: Rc::new(RefCell::new(EnvFrame {
depth: 0,
symbols: HashMap::new(),
parent: None,
home: None,
receiver: None,
})),
}
}
pub fn enclose(&self) -> EnvRef {
EnvRef {
frame: Rc::new(RefCell::new(EnvFrame {
depth: self.depth() + 1,
symbols: HashMap::new(),
parent: Some(self.clone()),
home: None,
receiver: None,
})),
}
}
fn extend(&self, symbols: SymbolTable, receiver: Option<&Object>) -> EnvRef {
let env_ref = EnvRef {
frame: Rc::new(RefCell::new(EnvFrame {
depth: self.depth() + 1,
symbols,
parent: Some(self.clone()),
home: self.home(),
receiver: receiver.map(|obj| obj.clone()),
})),
};
if env_ref.home().is_none() {
env_ref.frame.borrow_mut().home = Some(env_ref.clone());
}
env_ref
}
fn depth(&self) -> u32 {
self.frame.borrow().depth
}
fn parent(&self) -> Option<EnvRef> {
self.frame.borrow().parent.clone()
}
fn is_toplevel(&self) -> bool {
self.depth() <= 1
}
fn has_definition(&self, name: &str) -> bool {
let frame = self.frame.borrow();
match frame.symbols.get(name) {
Some(_) => true,
None => match &frame.parent {
None => false,
Some(parent) => parent.has_definition(name),
},
}
}
fn receiver(&self) -> Option<Object> {
let frame = self.frame.borrow();
match &frame.receiver {
Some(receiver) => Some(receiver.clone()),
None => match &frame.parent {
Some(parent) => parent.receiver(),
None => None,
},
}
}
fn home(&self) -> Option<EnvRef> {
let frame = self.frame.borrow();
match &frame.home {
Some(home) => Some(home.clone()),
None => match &frame.parent {
Some(parent) => parent.home(),
None => None,
},
}
}
fn add_binding(&self, name: &str, binding: Binding) {
self.frame.borrow_mut().symbols.insert(String::from(name), binding);
}
pub fn define(&self, name: &str, value: Object) {
self.add_binding(name, Binding::untyped(value));
}
fn set(&self, name: &str, value: Object) -> Option<Eval> {
let mut frame = self.frame.borrow_mut();
match frame.symbols.get_mut(name) {
Some(binding) => Some(binding.assign(value)),
None => match &frame.parent {
Some(parent) => parent.set(name, value),
None => None,
},
}
}
fn get(&self, name: &str) -> Option<Object> {
match self.get_binding(name) {
None => None,
Some(binding) => return Some(binding.value),
}
}
fn get_binding(&self, name: &str) -> Option<Binding> {
let frame = self.frame.borrow();
match frame.symbols.get(name) {
Some(binding) => return Some(binding.clone()),
None => match &frame.parent {
Some(parent) => parent.get_binding(name),
None => None,
},
}
}
pub fn find_global(&self, name: &str) -> Option<Object> {
if self.is_toplevel() {
self.get(name)
} else {
self.parent().unwrap().find_global(name)
}
}
pub fn import_name(&self, module: &EnvRef, name: &str) -> Result<(), Unwind> {
match module.get_binding(name) {
None => {
return Unwind::error(&format!("Cannot import {}: not defined in module", &name))
}
Some(binding) => {
if let Some(old_binding) = self.get_binding(&name) {
if &old_binding != &binding {
return Unwind::error(&format!("Name conflict: {} already defined", &name));
}
}
self.add_binding(&name, binding.clone());
}
}
Ok(())
}
pub fn import_everything(&self, module: &EnvRef) -> Result<(), Unwind> {
let mut todo = vec![];
for (name, binding) in module.frame.borrow().symbols.iter() {
if name.contains(".") {
continue;
}
if let Some(old_binding) = self.get_binding(&name) {
if &old_binding != binding {
return Unwind::error(&format!("Name conflict: {} already defined", &name));
}
} else {
todo.push((name.to_string(), binding.clone()));
}
}
for (name, binding) in todo.into_iter() {
self.add_binding(&name, binding);
}
Ok(())
}
pub fn import_prefixed(&self, module: &EnvRef, prefix: &str) -> Result<(), Unwind> {
let mut todo = vec![];
for (name, binding) in module.frame.borrow().symbols.iter() {
if name.contains(".") {
continue;
}
let alias = format!("{}.{}", prefix, name);
if let Some(old_binding) = self.get_binding(&alias) {
if &old_binding != binding {
return Unwind::error(&format!("Name conflict: {} already defined", &name));
}
} else {
todo.push((alias, binding.clone()));
}
}
for (name, binding) in todo.into_iter() {
self.add_binding(&name, binding);
}
Ok(())
}
}
#[derive(Clone)]
pub struct Env {
pub env_ref: EnvRef,
pub foo: Rc<Foolang>,
}
impl PartialEq for Env {
fn eq(&self, other: &Self) -> bool {
self.env_ref.eq(&other.env_ref)
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "#<Env {}>", self.env_ref.depth())
}
}
impl Env {
#[cfg(test)]
pub fn new() -> Env {
Foolang::here().toplevel_env()
}
fn is_toplevel(&self) -> bool {
self.env_ref.is_toplevel()
}
fn has_definition(&self, name: &str) -> bool {
self.env_ref.has_definition(name)
}
fn receiver(&self) -> Option<Object> {
self.env_ref.receiver()
}
fn home(&self) -> Option<EnvRef> {
self.env_ref.home()
}
fn enclose(&self) -> Env {
Env {
env_ref: self.env_ref.enclose(),
foo: self.foo.clone(),
}
}
fn bind(&self, name: &str, binding: Binding) -> Env {
let child = self.enclose();
child.add_binding(name, binding);
child
}
pub fn extend(&self, symbols: SymbolTable, receiver: Option<&Object>) -> Env {
Env {
env_ref: self.env_ref.extend(symbols, receiver),
foo: self.foo.clone(),
}
}
pub fn define(&self, name: &str, value: Object) {
self.env_ref.define(name, value);
}
pub fn add_binding(&self, name: &str, binding: Binding) {
self.env_ref.add_binding(name, binding);
}
pub fn set(&self, name: &str, value: Object) -> Option<Eval> {
self.env_ref.set(name, value)
}
pub fn get(&self, name: &str) -> Option<Object> {
self.env_ref.get(name)
}
pub fn eval_all(&self, source: &str) -> Eval {
let mut parser = Parser::new(source, self.foo.root());
loop {
let expr = match parser.parse() {
Err(unwind) => {
return Err(unwind.with_context(source));
}
Ok(expr) => expr,
};
let object = match self.eval(&expr) {
Err(unwind) => {
return Err(unwind.with_context(source));
}
Ok(object) => object,
};
if parser.at_eof() {
return Ok(object);
}
}
}
pub fn eval(&self, expr: &Expr) -> Eval {
use Expr::*;
match expr {
Array(array) => self.eval_array(array),
Assign(assign) => self.eval_assign(assign),
Bind(bind) => self.eval_bind(bind),
Block(block) => self.eval_block(block),
Cascade(cascade) => self.eval_cascade(cascade),
ClassDefinition(definition) => self.eval_class_definition(definition),
ClassExtension(extension) => self.eval_class_extension(extension),
Const(constant) => self.eval_constant(constant),
Dictionary(dictionary) => self.eval_dictionary(dictionary),
Eq(eq) => self.eval_eq(eq),
Global(global) => self.eval_global(global),
Import(import) => self.eval_import(import),
InterfaceDefinition(interface) => self.eval_interface(interface),
Raise(raise) => self.eval_raise(raise),
Return(ret) => self.eval_return(ret),
Chain(chain) => self.eval_chain(chain),
Seq(seq) => self.eval_seq(&seq),
Typecheck(typecheck) => self.eval_typecheck(typecheck),
Var(var) => self.eval_var(var),
}
}
fn eval_array(&self, array: &Array) -> Eval {
let mut data = Vec::new();
let array_env = self.enclose();
for elt in &array.data {
data.push(array_env.eval(elt)?);
}
Ok(self.foo.into_array(data))
}
fn eval_bind(&self, bind: &Bind) -> Eval {
let value = self.eval(&bind.value)?;
let binding = match bind.typename {
None => Binding::untyped(value),
Some(ref typename) => {
let vt = self.find_type(typename)?;
value.typecheck(&vt).source(&bind.value.span())?;
Binding::typed(vt, value)
}
};
let tmp = binding.value.clone();
let env = if self.is_toplevel() {
self.add_binding(&bind.name, binding);
self.clone()
} else {
self.bind(&bind.name, binding)
};
match &bind.body {
None => Ok(tmp),
Some(body) => env.eval(&body),
}
}
fn eval_block(&self, block: &Block) -> Eval {
let mut args = vec![];
let mut parameter_types = vec![];
for p in &block.params {
args.push(Arg::new(p.span.clone(), p.name.clone()));
parameter_types.push(&p.typename);
}
self.foo.make_closure(
self.clone(),
args,
(*block.body).clone(),
parameter_types,
&block.rtype,
)
}
fn eval_cascade(&self, cascade: &Cascade) -> Eval {
let receiver = self.eval(&cascade.receiver)?;
let mut res = receiver.clone();
for messages in &cascade.chains {
res = self.eval_sends(receiver.clone(), messages)?;
}
Ok(res)
}
fn check_toplevel(&self, span: &Span, what: &str) -> Result<(), Unwind> {
if self.is_toplevel() {
Ok(())
} else {
Unwind::error_at(span.clone(), &format!("{} not at toplevel", what))
}
}
fn check_not_defined(&self, name: &str, span: &Span, what: &str) -> Result<(), Unwind> {
if self.has_definition(name) {
return Unwind::error_at(span.clone(), &format!("Cannot redefine {}", what));
};
Ok(())
}
fn eval_class_definition(&self, definition: &ClassDefinition) -> Eval {
self.check_toplevel(&definition.span, "Class definition")?;
let name = &definition.name;
self.check_not_defined(name, &definition.span, "Class")?;
let class = self.foo.make_class(definition, self)?;
self.define(name, class.clone());
Ok(class)
}
fn eval_class_extension(&self, extension: &ClassExtension) -> Eval {
if !self.is_toplevel() {
return Unwind::error_at(extension.span.clone(), "Class extension not at toplevel");
}
let class = self.find_global_or_unwind(&extension.name)?;
class.extend_class(extension, self)
}
fn eval_dictionary(&self, dictionary: &Dictionary) -> Eval {
let mut data = HashMap::new();
for (k, v) in dictionary.assoc.iter() {
data.insert(self.eval(k)?, self.eval(v)?);
}
Ok(self.foo.into_dictionary(data))
}
fn eval_eq(&self, eq: &Eq) -> Eval {
if self.eval(&eq.left) == self.eval(&eq.right) {
Ok(self.foo.make_boolean(true))
} else {
Ok(self.foo.make_boolean(false))
}
}
pub fn maybe_type(&self, maybe_name: &Option<String>) -> Result<Option<Rc<Vtable>>, Unwind> {
match maybe_name {
None => return Ok(None),
Some(name) => Ok(Some(self.find_type(name)?)),
}
}
pub fn find_type(&self, name: &str) -> Result<Rc<Vtable>, Unwind> {
match self.find_global(name) {
None => Unwind::error(&format!("Undefined type: {}", name)),
Some(obj) => match &obj.datum {
Datum::Class(ref class) => Ok(class.instance_vtable.clone()),
_ => Unwind::error(&format!("Not a type: {}", name)),
},
}
}
pub fn find_class(&self, name: &str) -> Result<Rc<Class>, Unwind> {
match self.find_global(name) {
None => Unwind::error(&format!("Undefined class: {}", name)),
Some(obj) => match &obj.datum {
Datum::Class(ref class) if !class.interface => Ok(class.clone()),
_ => panic!("Interface, not class: {}", name),
},
}
}
pub fn find_interface(&self, name: &str) -> Result<Rc<Class>, Unwind> {
match self.find_global(name) {
None => Unwind::error(&format!("Undefined interface: {}", name)),
Some(obj) => match &obj.datum {
Datum::Class(ref class) if class.interface => Ok(class.clone()),
_ => Unwind::error(&format!("Class, not interface: {}", name)),
},
}
}
pub fn find_global(&self, name: &str) -> Option<Object> {
self.env_ref.find_global(name)
}
pub fn find_global_or_unwind(&self, name: &str) -> Eval {
match self.find_global(name) {
Some(obj) => Ok(obj),
None => Unwind::error(&format!("Undefined global: {}", name)),
}
}
fn eval_global(&self, global: &Global) -> Eval {
self.find_global_or_unwind(&global.name).source(&global.span)
}
fn eval_constant(&self, constant: &Const) -> Eval {
match &constant.literal {
Literal::Boolean(value) => Ok(self.foo.make_boolean(*value)),
Literal::Integer(value) => Ok(self.foo.make_integer(*value)),
Literal::Float(value) => Ok(self.foo.make_float(*value)),
Literal::String(value) => Ok(self.foo.make_string(value)),
}
}
pub fn import_everything(&self, module: &Env) -> Eval {
self.env_ref.import_everything(&module.env_ref)?;
Ok(self.foo.make_boolean(true))
}
fn eval_import(&self, import: &Import) -> Eval {
let module = &self.foo.load_module(&import.path)?.env_ref;
let res = match &import.name {
None => self.env_ref.import_prefixed(&module, &import.prefix),
Some(name) if name == "*" => self.env_ref.import_everything(&module),
Some(name) => self.env_ref.import_name(&module, name),
};
if let Err(mut unwind) = res {
unwind.add_span(&import.span);
return Err(unwind);
}
match import.body {
None => Ok(self.foo.make_string(&import.path.to_string_lossy())),
Some(ref expr) => self.eval(expr),
}
}
fn eval_interface(&self, interface: &InterfaceDefinition) -> Eval {
self.check_toplevel(&interface.span, "Interface definition")?;
let name = &interface.name;
self.check_not_defined(&interface.name, &interface.span, "Interface")?;
let interface = self.foo.make_interface(interface, self)?;
self.define(name, interface.clone());
Ok(interface)
}
fn eval_raise(&self, raise: &Raise) -> Eval {
Unwind::error_at(raise.value.span(), self.eval(&raise.value)?.string_as_str())
}
fn eval_return(&self, ret: &Return) -> Eval {
match self.home() {
None => Unwind::error_at(ret.span.clone(), "Nothing to return from"),
Some(env) => Unwind::return_from(env, self.eval(&ret.value)?),
}
}
fn eval_sends(&self, mut receiver: Object, messages: &Vec<Message>) -> Eval {
for message in messages {
let mut values = Vec::new();
for arg in &message.args {
values.push(self.eval(arg)?);
}
receiver = receiver.send(message.selector.as_str(), &values[..], &self)?
}
return Ok(receiver);
}
fn eval_chain(&self, chain: &Chain) -> Eval {
self.eval_sends(self.eval(&chain.receiver)?, &chain.messages)
}
fn eval_seq(&self, seq: &Seq) -> Eval {
let mut result = self.foo.make_boolean(false);
for expr in &seq.exprs {
result = self.eval(expr)?;
}
Ok(result)
}
fn eval_typecheck(&self, typecheck: &Typecheck) -> Eval {
let expr = &typecheck.expr;
let value = self.eval(expr)?;
value.typecheck(&self.find_type(&typecheck.typename)?).source(&expr.span())?;
Ok(value)
}
fn eval_assign(&self, assign: &Assign) -> Eval {
let value = self.eval(&assign.value)?;
match self.set(&assign.name, value.clone()) {
Some(res) => res.source(&assign.span),
None => {
if let Some(receiver) = self.receiver() {
if let Some(slot) = receiver.slots().get(&assign.name) {
return write_instance_variable(&receiver, slot, value)
.source(&assign.span);
}
}
Unwind::error_at(assign.span.clone(), "Cannot assign to an unbound variable")
}
}
}
fn eval_var(&self, var: &Var) -> Eval {
if &var.name == "self" {
match self.receiver() {
None => Unwind::error_at(var.span.clone(), "self outside method context"),
Some(receiver) => Ok(receiver.clone()),
}
} else {
match self.get(&var.name) {
Some(value) => return Ok(value),
None => {
if let Some(receiver) = self.receiver() {
if let Some(slot) = receiver.slots().get(&var.name) {
return read_instance_variable(&receiver, slot.index);
}
}
}
}
Unwind::error_at(var.span.clone(), &format!("Unbound variable: {}", &var.name))
}
}
}
#[cfg(test)]
pub mod utils {
use crate::eval::*;
pub fn eval_exception(source: &str) -> (Unwind, Env) {
let env = Env::new();
match env.eval_all(source) {
Err(unwind) => match &unwind {
Unwind::Exception(..) => (unwind, env),
_ => panic!("Expected exception, got: {:?}", unwind),
},
Ok(value) => panic!("Expected exception, got: {:?}", value),
}
}
pub fn eval_str(source: &str) -> Eval {
let env = Env::new();
env.eval_all(source)
}
pub fn eval_obj(source: &str) -> (Object, Env) {
let env = Env::new();
match env.eval_all(source) {
Err(unwind) => panic!("Unexpected unwind:\n{:?}", unwind),
Ok(obj) => (obj, env),
}
}
pub fn eval_ok(source: &str) -> Object {
match eval_str(source) {
Ok(obj) => obj,
Err(Unwind::Exception(error, location)) => {
panic!("Exception in eval_ok: {}:\n{}", error.what(), location.context());
}
Err(Unwind::ReturnFrom(..)) => panic!("Unexpected return-from in eval_ok"),
}
}
}