1use std::{cmp::Ordering, sync::Arc};
5
6use sim_kernel::{
7 CORE_LIST_CLASS_ID, ClassRef, Cx, Expr, LengthResult, ListValue, Object, ObjectEncode,
8 ObjectEncoding, Result, Symbol, Value, force_list_to_vec,
9};
10
11use crate::citizen::cons_list_class_symbol;
12
13#[derive(Clone)]
53pub struct ConsList {
54 car: Option<Value>,
56 cdr: Option<Arc<ConsList>>,
58}
59
60impl ConsList {
61 pub fn empty() -> Self {
63 Self {
64 car: None,
65 cdr: None,
66 }
67 }
68
69 pub fn cell(car: Value, cdr: Arc<ConsList>) -> Self {
71 Self {
72 car: Some(car),
73 cdr: Some(cdr),
74 }
75 }
76
77 pub fn from_vec(items: Vec<Value>) -> Arc<Self> {
79 let mut acc = Arc::new(Self::empty());
80 for item in items.into_iter().rev() {
81 acc = Arc::new(Self::cell(item, acc));
82 }
83 acc
84 }
85
86 fn count_cells(&self) -> usize {
87 let mut count = 0usize;
88 let mut cursor = self.cdr.as_ref().cloned();
89 if self.car.is_none() {
90 return 0;
91 }
92
93 count += 1;
94 while let Some(node) = cursor {
95 if node.car.is_none() {
96 break;
97 }
98 count += 1;
99 cursor = node.cdr.as_ref().cloned();
100 }
101 count
102 }
103
104 fn len_cmp_cells(&self, n: usize) -> Ordering {
105 if self.car.is_none() {
106 return 0usize.cmp(&n);
107 }
108
109 let mut count = 1usize;
110 if count > n {
111 return Ordering::Greater;
112 }
113
114 let mut cursor = self.cdr.as_ref().cloned();
115 while let Some(next) = cursor {
116 if next.car.is_none() {
117 break;
118 }
119 count += 1;
120 if count > n {
121 return Ordering::Greater;
122 }
123 cursor = next.cdr.as_ref().cloned();
124 }
125 count.cmp(&n)
126 }
127}
128
129impl Object for ConsList {
130 fn display(&self, _cx: &mut Cx) -> Result<String> {
131 Ok("cons[...]".to_owned())
132 }
133
134 fn as_any(&self) -> &dyn std::any::Any {
135 self
136 }
137}
138
139impl sim_kernel::ObjectCompat for ConsList {
140 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
141 let symbol = cons_list_class_symbol();
142 if let Some(value) = cx.registry().class_by_symbol(&symbol) {
143 return Ok(value.clone());
144 }
145 let symbol = Symbol::qualified("core", "List");
146 if let Some(value) = cx.registry().class_by_symbol(&symbol) {
147 return Ok(value.clone());
148 }
149 cx.factory().class_stub(CORE_LIST_CLASS_ID, symbol)
150 }
151 fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
152 Ok(Expr::List(
153 force_list_to_vec(cx, self, "list as_expr")?
154 .into_iter()
155 .map(|value| value.object().as_expr(cx))
156 .collect::<Result<Vec<_>>>()?,
157 ))
158 }
159 fn truth(&self, _cx: &mut Cx) -> Result<bool> {
160 Ok(self.car.is_some())
161 }
162 fn as_list(&self) -> Option<&dyn ListValue> {
163 Some(self)
164 }
165 fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
166 Some(self)
167 }
168}
169
170impl ObjectEncode for ConsList {
171 fn object_encoding(&self, cx: &mut Cx) -> Result<ObjectEncoding> {
172 let items = force_list_to_vec(cx, self, "list/ConsList citizen")?
173 .into_iter()
174 .map(|value| value.object().as_expr(cx))
175 .collect::<Result<Vec<_>>>()?;
176 Ok(ObjectEncoding::Constructor {
177 class: cons_list_class_symbol(),
178 args: vec![
179 Expr::Symbol(Symbol::new("v0")),
180 crate::citizen::expr_items::encode(&items),
181 ],
182 })
183 }
184}
185
186impl sim_citizen::Citizen for ConsList {
187 fn citizen_symbol() -> Symbol {
188 cons_list_class_symbol()
189 }
190
191 fn citizen_version() -> u32 {
192 0
193 }
194
195 fn citizen_arity() -> usize {
196 1
197 }
198
199 fn citizen_fields() -> &'static [&'static str] {
200 &["items"]
201 }
202}
203
204impl ListValue for ConsList {
205 fn is_empty(&self, _cx: &mut Cx) -> Result<bool> {
206 Ok(self.car.is_none())
207 }
208
209 fn car(&self, _cx: &mut Cx) -> Result<Option<Value>> {
210 Ok(self.car.clone())
211 }
212
213 fn cdr(&self, cx: &mut Cx) -> Result<Option<Value>> {
214 match &self.cdr {
215 Some(next) => Ok(Some(cx.factory().opaque(next.clone())?)),
216 None => Ok(None),
217 }
218 }
219
220 fn len(&self, _cx: &mut Cx) -> Result<LengthResult> {
221 Ok(LengthResult::Known(self.count_cells()))
222 }
223
224 fn len_cmp(&self, _cx: &mut Cx, n: usize) -> Result<Ordering> {
225 Ok(self.len_cmp_cells(n))
226 }
227
228 fn get(&self, _cx: &mut Cx, index: usize) -> Result<Option<Value>> {
229 let mut current = Some(Arc::new(self.clone()));
230 let mut i = index;
231 while let Some(node) = current {
232 let Some(car) = &node.car else {
233 return Ok(None);
234 };
235 if i == 0 {
236 return Ok(Some(car.clone()));
237 }
238 i -= 1;
239 current = node.cdr.as_ref().cloned();
240 }
241 Ok(None)
242 }
243
244 fn for_each(
245 &self,
246 _cx: &mut Cx,
247 limit: Option<usize>,
248 visit: &mut dyn FnMut(&Value),
249 ) -> Result<()> {
250 if matches!(limit, Some(0)) {
251 return Ok(());
252 }
253
254 let mut current = Some(Arc::new(self.clone()));
255 let mut count = 0usize;
256 while let Some(node) = current {
257 let Some(car) = &node.car else {
258 return Ok(());
259 };
260 if matches!(limit, Some(max) if count >= max) {
261 return Ok(());
262 }
263 visit(car);
264 count += 1;
265 current = node.cdr.as_ref().cloned();
266 }
267 Ok(())
268 }
269}