use std::{cmp::Ordering, sync::Arc};
use sim_kernel::{
CORE_LIST_CLASS_ID, ClassRef, Cx, Error, Expr, LengthResult, ListValue, Object, ObjectEncode,
ObjectEncoding, Result, Symbol, Value, force_list_to_vec,
};
use crate::citizen::cons_list_class_symbol;
#[derive(Clone)]
pub struct ConsList {
car: Option<Value>,
cdr: Option<Rest>,
}
#[derive(Clone)]
enum Rest {
Cons(Arc<ConsList>),
Foreign(Value),
}
impl ConsList {
pub fn empty() -> Self {
Self {
car: None,
cdr: None,
}
}
pub fn cell(car: Value, cdr: Arc<ConsList>) -> Self {
Self {
car: Some(car),
cdr: Some(Rest::Cons(cdr)),
}
}
pub fn cell_foreign(car: Value, tail: Value) -> Self {
Self {
car: Some(car),
cdr: Some(Rest::Foreign(tail)),
}
}
pub fn from_vec(items: Vec<Value>) -> Arc<Self> {
let mut acc = Arc::new(Self::empty());
for item in items.into_iter().rev() {
acc = Arc::new(Self::cell(item, acc));
}
acc
}
}
fn foreign_as_list(value: &Value) -> Result<&dyn ListValue> {
value.object().as_list().ok_or(Error::TypeMismatch {
expected: "list",
found: "non-list",
})
}
impl Object for ConsList {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok("cons[...]".to_owned())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl sim_kernel::ObjectCompat for ConsList {
fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
let symbol = cons_list_class_symbol();
if let Some(value) = cx.registry().class_by_symbol(&symbol) {
return Ok(value.clone());
}
let symbol = Symbol::qualified("core", "List");
if let Some(value) = cx.registry().class_by_symbol(&symbol) {
return Ok(value.clone());
}
cx.factory().class_stub(CORE_LIST_CLASS_ID, symbol)
}
fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
Ok(Expr::List(
force_list_to_vec(cx, self, "list as_expr")?
.into_iter()
.map(|value| value.object().as_expr(cx))
.collect::<Result<Vec<_>>>()?,
))
}
fn truth(&self, _cx: &mut Cx) -> Result<bool> {
Ok(self.car.is_some())
}
fn as_list(&self) -> Option<&dyn ListValue> {
Some(self)
}
fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
Some(self)
}
}
impl ObjectEncode for ConsList {
fn object_encoding(&self, cx: &mut Cx) -> Result<ObjectEncoding> {
let items = force_list_to_vec(cx, self, "list/ConsList citizen")?
.into_iter()
.map(|value| value.object().as_expr(cx))
.collect::<Result<Vec<_>>>()?;
Ok(ObjectEncoding::Constructor {
class: cons_list_class_symbol(),
args: vec![
Expr::Symbol(Symbol::new("v0")),
crate::citizen::expr_items::encode(&items),
],
})
}
}
impl sim_citizen::Citizen for ConsList {
fn citizen_symbol() -> Symbol {
cons_list_class_symbol()
}
fn citizen_version() -> u32 {
0
}
fn citizen_arity() -> usize {
1
}
fn citizen_fields() -> &'static [&'static str] {
&["items"]
}
}
impl ListValue for ConsList {
fn is_empty(&self, _cx: &mut Cx) -> Result<bool> {
Ok(self.car.is_none())
}
fn car(&self, _cx: &mut Cx) -> Result<Option<Value>> {
Ok(self.car.clone())
}
fn cdr(&self, cx: &mut Cx) -> Result<Option<Value>> {
match &self.cdr {
Some(Rest::Cons(next)) => Ok(Some(cx.factory().opaque(next.clone())?)),
Some(Rest::Foreign(tail)) => Ok(Some(tail.clone())),
None => Ok(None),
}
}
fn len(&self, cx: &mut Cx) -> Result<LengthResult> {
if self.car.is_none() {
return Ok(LengthResult::Known(0));
}
let mut count = 1usize;
let mut rest = self.cdr.clone();
loop {
match rest {
None => return Ok(LengthResult::Known(count)),
Some(Rest::Cons(node)) => {
if node.car.is_none() {
return Ok(LengthResult::Known(count));
}
count += 1;
rest = node.cdr.clone();
}
Some(Rest::Foreign(tail)) => {
return Ok(match foreign_as_list(&tail)?.len(cx)? {
LengthResult::Known(k) => LengthResult::Known(count + k),
LengthResult::Unknown => LengthResult::Unknown,
});
}
}
}
}
fn len_cmp(&self, cx: &mut Cx, n: usize) -> Result<Ordering> {
if self.car.is_none() {
return Ok(0usize.cmp(&n));
}
let mut count = 1usize;
if count > n {
return Ok(Ordering::Greater);
}
let mut rest = self.cdr.clone();
loop {
match rest {
None => return Ok(count.cmp(&n)),
Some(Rest::Cons(node)) => {
if node.car.is_none() {
return Ok(count.cmp(&n));
}
count += 1;
if count > n {
return Ok(Ordering::Greater);
}
rest = node.cdr.clone();
}
Some(Rest::Foreign(tail)) => {
return foreign_as_list(&tail)?.len_cmp(cx, n - count);
}
}
}
}
fn get(&self, cx: &mut Cx, index: usize) -> Result<Option<Value>> {
let mut node_car = self.car.clone();
let mut rest = self.cdr.clone();
let mut i = index;
loop {
let Some(car) = node_car else {
return Ok(None);
};
if i == 0 {
return Ok(Some(car));
}
i -= 1;
match rest {
None => return Ok(None),
Some(Rest::Cons(node)) => {
node_car = node.car.clone();
rest = node.cdr.clone();
}
Some(Rest::Foreign(tail)) => {
return foreign_as_list(&tail)?.get(cx, i);
}
}
}
}
fn for_each(
&self,
cx: &mut Cx,
limit: Option<usize>,
visit: &mut dyn FnMut(&Value),
) -> Result<()> {
if matches!(limit, Some(0)) {
return Ok(());
}
let mut node_car = self.car.clone();
let mut rest = self.cdr.clone();
let mut count = 0usize;
loop {
let Some(car) = node_car else {
return Ok(());
};
if matches!(limit, Some(max) if count >= max) {
return Ok(());
}
visit(&car);
count += 1;
match rest {
None => return Ok(()),
Some(Rest::Cons(node)) => {
node_car = node.car.clone();
rest = node.cdr.clone();
}
Some(Rest::Foreign(tail)) => {
let remaining = limit.map(|max| max - count);
return foreign_as_list(&tail)?.for_each(cx, remaining, visit);
}
}
}
}
}