cl-interpret: Try having separate globals again?
This commit is contained in:
parent
fdf076c272
commit
dcdb100a8a
@ -19,6 +19,21 @@ use std::{
|
|||||||
|
|
||||||
type StackFrame = HashMap<Sym, Option<ConValue>>;
|
type StackFrame = HashMap<Sym, Option<ConValue>>;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Place {
|
||||||
|
Global(Sym),
|
||||||
|
Local(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Place {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Place::Global(name) => name.fmt(f),
|
||||||
|
Place::Local(id) => id.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct EnvFrame {
|
struct EnvFrame {
|
||||||
/// The length of the array when this stack frame was constructed
|
/// The length of the array when this stack frame was constructed
|
||||||
@ -30,6 +45,7 @@ struct EnvFrame {
|
|||||||
/// Implements a nested lexical scope
|
/// Implements a nested lexical scope
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
|
global: HashMap<Sym, Option<ConValue>>,
|
||||||
values: Vec<Option<ConValue>>,
|
values: Vec<Option<ConValue>>,
|
||||||
frames: Vec<EnvFrame>,
|
frames: Vec<EnvFrame>,
|
||||||
}
|
}
|
||||||
@ -56,17 +72,9 @@ impl Display for Environment {
|
|||||||
}
|
}
|
||||||
impl Default for Environment {
|
impl Default for Environment {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let mut values = Vec::new();
|
let mut this = Self::no_builtins();
|
||||||
|
this.add_builtins(Builtins).add_builtins(Math);
|
||||||
let mut builtins = EnvFrame { name: Some("builtin"), base: 0, binds: HashMap::new() };
|
this
|
||||||
for bu in Builtins.iter().chain(Math.iter()) {
|
|
||||||
builtins.binds.insert(bu.name(), values.len());
|
|
||||||
values.push(Some(bu.into()));
|
|
||||||
}
|
|
||||||
let globals =
|
|
||||||
EnvFrame { name: Some("globals"), base: values.len(), binds: HashMap::new() };
|
|
||||||
|
|
||||||
Self { values, frames: vec![builtins, globals] }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,9 +84,7 @@ impl Environment {
|
|||||||
}
|
}
|
||||||
/// Creates an [Environment] with no [builtins](super::builtin)
|
/// Creates an [Environment] with no [builtins](super::builtin)
|
||||||
pub fn no_builtins() -> Self {
|
pub fn no_builtins() -> Self {
|
||||||
let globals = EnvFrame { name: Some("globals"), base: 0, binds: HashMap::new() };
|
Self { values: Vec::new(), global: HashMap::new(), frames: vec![] }
|
||||||
|
|
||||||
Self { values: Vec::new(), frames: vec![Default::default(), globals] }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reflexively evaluates a node
|
/// Reflexively evaluates a node
|
||||||
@ -98,17 +104,12 @@ impl Environment {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Will panic if globals table is non-empty!
|
/// Will panic if globals table is non-empty!
|
||||||
pub fn add_builtins(&mut self, builtins: &'static [Builtin]) {
|
pub fn add_builtins(&mut self, builtins: &'static [Builtin]) -> &mut Self {
|
||||||
let Self { values, frames } = self;
|
let Self { global, .. } = self;
|
||||||
// Globals must be EMPTY to add builtins
|
|
||||||
assert_eq!(frames[1].binds.len(), 0);
|
|
||||||
for builtin in builtins {
|
for builtin in builtins {
|
||||||
if let Some(EnvFrame { name: _, base: _, binds }) = frames.first_mut() {
|
global.insert(builtin.name(), Some(builtin.into()));
|
||||||
binds.insert(builtin.name(), values.len());
|
|
||||||
values.push(Some(builtin.into()));
|
|
||||||
}
|
}
|
||||||
}
|
self
|
||||||
frames[1].base = values.len();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) {
|
pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) {
|
||||||
@ -139,8 +140,8 @@ impl Environment {
|
|||||||
///
|
///
|
||||||
/// Returns a mutable reference to the variable's record, if it exists.
|
/// Returns a mutable reference to the variable's record, if it exists.
|
||||||
pub fn get_mut(&mut self, name: Sym) -> IResult<&mut Option<ConValue>> {
|
pub fn get_mut(&mut self, name: Sym) -> IResult<&mut Option<ConValue>> {
|
||||||
let id = self.id_of(name)?;
|
let at = self.id_of(name)?;
|
||||||
self.values.get_mut(id).ok_or(Error::NotDefined(name))
|
self.get_id_mut(at).ok_or(Error::NotDefined(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves a variable immutably.
|
/// Resolves a variable immutably.
|
||||||
@ -148,47 +149,63 @@ impl Environment {
|
|||||||
/// Returns a reference to the variable's contents, if it is defined and initialized.
|
/// Returns a reference to the variable's contents, if it is defined and initialized.
|
||||||
pub fn get(&self, name: Sym) -> IResult<ConValue> {
|
pub fn get(&self, name: Sym) -> IResult<ConValue> {
|
||||||
let id = self.id_of(name)?;
|
let id = self.id_of(name)?;
|
||||||
match self.values.get(id).ok_or(Error::NotDefined(name))? {
|
let res = match id {
|
||||||
|
Place::Global(name) => self.global.get(&name),
|
||||||
|
Place::Local(id) => self.values.get(id),
|
||||||
|
};
|
||||||
|
match res.ok_or(Error::NotDefined(name))? {
|
||||||
Some(value) => Ok(value.clone()),
|
Some(value) => Ok(value.clone()),
|
||||||
None => Err(Error::NotInitialized(name)),
|
None => Err(Error::NotInitialized(name)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_local(&self, id: Sym) -> IResult<ConValue> {
|
pub(crate) fn get_local(&self, name: Sym) -> IResult<ConValue> {
|
||||||
for EnvFrame { binds, .. } in self.frames.iter().skip(2).rev() {
|
for EnvFrame { binds, .. } in self.frames.iter().skip(2).rev() {
|
||||||
if let Some(var) = binds.get(&id) {
|
if let Some(var) = binds.get(&name) {
|
||||||
if let Some(var) = self.values.get(*var) {
|
if let Some(var) = self.values.get(*var) {
|
||||||
return match var {
|
return match var {
|
||||||
Some(value) => Ok(value.clone()),
|
Some(value) => Ok(value.clone()),
|
||||||
None => Err(Error::NotInitialized(id)),
|
None => Err(Error::NotInitialized(name)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(Error::NotDefined(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn id_of(&self, name: Sym) -> IResult<usize> {
|
|
||||||
for EnvFrame { binds, .. } in self.frames.iter().rev() {
|
|
||||||
if let Some(id) = binds.get(&name).copied() {
|
|
||||||
return Ok(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(Error::NotDefined(name))
|
Err(Error::NotDefined(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_id(&self, id: usize) -> Option<&ConValue> {
|
pub fn id_of(&self, name: Sym) -> IResult<Place> {
|
||||||
self.values.get(id)?.as_ref()
|
for EnvFrame { binds, .. } in self.frames.iter().rev() {
|
||||||
|
if let Some(id) = binds.get(&name).copied() {
|
||||||
|
return Ok(Place::Local(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.global
|
||||||
|
.contains_key(&name)
|
||||||
|
.then_some(Place::Global(name))
|
||||||
|
.ok_or(Error::NotDefined(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_id_mut(&mut self, id: usize) -> Option<&mut Option<ConValue>> {
|
pub fn get_id(&self, at: Place) -> Option<&ConValue> {
|
||||||
self.values.get_mut(id)
|
let res = match at {
|
||||||
|
Place::Global(name) => self.global.get(&name),
|
||||||
|
Place::Local(id) => self.values.get(id),
|
||||||
|
}?;
|
||||||
|
res.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_id_mut(&mut self, at: Place) -> Option<&mut Option<ConValue>> {
|
||||||
|
match at {
|
||||||
|
Place::Global(name) => self.global.get_mut(&name),
|
||||||
|
Place::Local(id) => self.values.get_mut(id),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a new [ConValue] into this [Environment]
|
/// Inserts a new [ConValue] into this [Environment]
|
||||||
pub fn insert(&mut self, k: Sym, v: Option<ConValue>) {
|
pub fn insert(&mut self, k: Sym, v: Option<ConValue>) {
|
||||||
if self.bind_raw(k, self.values.len()).is_some() {
|
if self.bind_raw(k, self.values.len()).is_some() {
|
||||||
self.values.push(v);
|
self.values.push(v);
|
||||||
|
} else {
|
||||||
|
self.global.insert(k, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,8 +218,8 @@ impl Environment {
|
|||||||
function.lift_upvars(self);
|
function.lift_upvars(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a temporary/anonymous variable, and returns its address
|
/// Allocates a local variable
|
||||||
pub fn insert_temporary(&mut self, value: ConValue) -> IResult<usize> {
|
pub fn stack_alloc(&mut self, value: ConValue) -> IResult<usize> {
|
||||||
let adr = self.values.len();
|
let adr = self.values.len();
|
||||||
self.values.push(Some(value));
|
self.values.push(Some(value));
|
||||||
Ok(adr)
|
Ok(adr)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use cl_ast::{Pattern, Sym};
|
use cl_ast::{Pattern, Sym};
|
||||||
use cl_structures::span::Span;
|
use cl_structures::span::Span;
|
||||||
|
|
||||||
use super::convalue::ConValue;
|
use super::{convalue::ConValue, env::Place};
|
||||||
|
|
||||||
pub type IResult<T> = Result<T, Error>;
|
pub type IResult<T> = Result<T, Error>;
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ pub enum Error {
|
|||||||
/// Underflowed the stack
|
/// Underflowed the stack
|
||||||
StackUnderflow,
|
StackUnderflow,
|
||||||
/// Overflowed the stack
|
/// Overflowed the stack
|
||||||
StackOverflow(usize),
|
StackOverflow(Place),
|
||||||
/// Exited the last scope
|
/// Exited the last scope
|
||||||
ScopeExit,
|
ScopeExit,
|
||||||
/// Type incompatibility
|
/// Type incompatibility
|
||||||
|
@ -822,8 +822,8 @@ impl Interpret for AddrOf {
|
|||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let value = expr.interpret(env)?;
|
let value = expr.interpret(env)?;
|
||||||
let temp = env.insert_temporary(value)?;
|
let temp = env.stack_alloc(value)?;
|
||||||
Ok(ConValue::Ref(temp))
|
Ok(ConValue::Ref(env::Place::Local(temp)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user