Skip to main content

cl_interpret/
interpret.rs

1//! A work-in-progress tree walk interpreter for Conlang
2//!
3//! Currently, major parts of the interpreter are not yet implemented, and major parts will never be
4//! implemented in its current form. Namely, since no [ConValue] has a stable location, it's
5//! meaningless to get a pointer to one, and would be undefined behavior to dereference a pointer to
6//! one in any situation.
7#![expect(unused, reason = "Work in progress")]
8
9use super::*;
10use crate::{
11    function::Function,
12    place::Place,
13    typeinfo::{Model, TypeInfo},
14};
15use cl_ast::{
16    types::{Literal, Path},
17    *,
18};
19use cl_structures::intern::interned::Interned;
20use std::{collections::HashMap, iter, rc::Rc, slice};
21
22macro trace($($t:tt)*) {{
23    #[cfg(debug_assertions)]
24    if std::env::var("CONLANG_TRACE").is_ok() {
25        eprintln!($($t)*)
26    }
27}}
28
29/// Turns a `todo!` invocation into an [Error::Panic]
30pub macro cl_todo {
31    () => {
32        Err(Error::Panic(format!("Not yet implemented (at {}:{}:{})", file!(), line!(), column!())))
33    },
34    ($($t:tt)*) => {
35        Err(Error::Panic(format!("Not yet implemented: {} (at {}:{}:{})", format_args!($($t)*), file!(), line!(), column!())))
36    }
37}
38
39/// Turns an `unimplemented!` invocation into an [Error::Panic]
40pub macro cl_unimplemented {
41    () => {
42        Err(Error::Panic(format!("Not implemented (at {}:{}:{})", file!(), line!(), column!())))
43    },
44    ($($t:tt)*) => {
45        Err(Error::Panic(format!("Not implemented: {} (at {}:{}:{})", format_args!($($t)*), file!(), line!(), column!())))
46    }
47}
48
49pub use self::{cl_todo as todo, cl_unimplemented as unimplemented};
50
51/// A work-in-progress tree walk interpreter for Conlang
52pub trait Interpret {
53    /// Interprets this thing in the given [`Environment`].
54    ///
55    /// Everything returns a value!™
56    fn interpret(&self, env: &mut Environment) -> IResult<ConValue>;
57}
58
59impl<T: Annotation + Interpret> Interpret for At<T> {
60    fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
61        self.0.interpret(env).map_err(|e| e.with_span(self.1))
62    }
63}
64
65impl Interpret for Expr<DefaultTypes> {
66    fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
67        match self {
68            Self::Omitted => Ok(ConValue::Empty),
69            Self::Id(path) => match path.parts.as_slice() {
70                &[name] => env.get(name),
71                [first, names @ ..] => {
72                    let mut value = env.get(*first)?;
73                    for name in names {
74                        value = match value {
75                            ConValue::Module(values) => {
76                                values.get(name).cloned().ok_or(Error::NotDefined(*name))?
77                            }
78                            ConValue::TypeInfo(ty) => ty.getattr(*name)?,
79                            _ => todo!("{self}")?,
80                        };
81                    }
82                    Ok(value)
83                }
84                [] => unimplemented!("Empty paths"),
85                _ => todo!("Extract value at {path}"),
86            },
87            Self::MetId(_) => cl_todo!("Meta-identifiers are not allowed here"),
88            Self::Lit(Literal::Bool(v)) => Ok(ConValue::Bool(*v)),
89            Self::Lit(Literal::Char(v)) => Ok(ConValue::Char(*v)),
90            Self::Lit(Literal::Int(v, _)) => Ok(ConValue::Int(*v as _)),
91            Self::Lit(Literal::Str(v)) => Ok(ConValue::Str(v.as_str().into())),
92            Self::Use(_) => cl_todo!("Use `{self}`"),
93            Self::Bind(bind) => bind.interpret(env),
94            Self::Make(make) => make.interpret(env),
95            Self::Match(mtch) => mtch.interpret(env),
96            Self::Op(op, exprs) => {
97                if self.is_place()
98                    && let Ok(place) = Place::new(self, env)
99                {
100                    place.get_mut(env).cloned()
101                } else {
102                    (*op, exprs.as_slice()).interpret(env)
103                }
104            }
105        }
106    }
107}
108
109impl Interpret for (Op, &[At<Expr>]) {
110    fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
111        match self {
112            (Op::Do, []) => Ok(ConValue::Empty),
113            (Op::Do, [ats @ .., ret]) => {
114                for at in ats {
115                    at.0.interpret(env)?;
116                }
117                ret.0.interpret(env)
118            }
119            (Op::As, [value, ty]) => match ty.interpret(env)? {
120                ConValue::TypeInfo(ty) => value.interpret(env).map(|v| v.cast(&ty)),
121                other => Err(Error::TypeError("type", other.typename())),
122            },
123            (Op::As, [value, ty]) => cl_todo!("{value} as {ty} operator"),
124            (Op::Block, []) => Ok(ConValue::Empty),
125            (Op::Block, [expr]) => expr.interpret(&mut env.frame("block", None)),
126            (Op::Array, []) => Ok(ConValue::Array(Box::new([]))),
127            (Op::Array, exprs) => Ok(ConValue::Array(
128                exprs
129                    .iter()
130                    .map(|e| e.interpret(env))
131                    .collect::<Result<Box<_>, _>>()?,
132            )),
133            (Op::ArRep, [v, rep]) => {
134                let v = v.interpret(env)?;
135                match rep.interpret(env)? {
136                    ConValue::Int(rep) => {
137                        Ok(ConValue::Array(vec![v; rep as usize].into_boxed_slice()))
138                    }
139                    rep => Err(Error::TypeError("int", rep.typename())),
140                }
141            }
142            (Op::Group, [expr]) => expr.interpret(env),
143            (Op::Tuple, []) => Ok(ConValue::Empty),
144            (Op::Tuple, exprs) => Ok(ConValue::Tuple(
145                exprs
146                    .iter()
147                    .map(|e| e.interpret(env))
148                    .collect::<Result<Box<_>, _>>()?,
149            )),
150            (Op::MetaInner, [_, expr]) => expr.interpret(env),
151            (Op::MetaOuter, [_, expr]) => expr.interpret(env),
152            (Op::Try, [expr]) => expr.interpret(env),
153            (Op::Index, [expr, idx]) => expr.interpret(env)?.index(&idx.interpret(env)?, env),
154            (Op::Call, [expr, arg]) => {
155                let callee = expr.interpret(env)?;
156                match arg.interpret(env)? {
157                    ConValue::Empty => callee.call(env, &[]),
158                    ConValue::Tuple(args) => callee.call(env, &args),
159                    arg => callee.call(env, slice::from_ref(&arg)),
160                }
161            }
162
163            // Annotation operators
164            (Op::Pub, [expr]) => expr.interpret(env),
165            (Op::Const, [expr]) => expr.interpret(env), // we are const
166            (Op::Static, [expr]) => expr.interpret(env), // TODO: "static"
167
168            // Control-flow
169            (Op::Macro, _) => cl_todo!("Macros are not supported in the interpreter"),
170            (Op::Loop, [expr]) => loop {
171                match expr.interpret(env) {
172                    Ok(_) => {}
173                    Err(Error { kind: ErrorKind::Break(v), .. }) => break Ok(v),
174                    Err(Error { kind: ErrorKind::Continue, .. }) => continue,
175                    Err(e) => Err(e)?,
176                }
177            },
178            (Op::If, [cond, pass, fail]) => {
179                let mut scope = env.frame("if", None);
180                if cond.interpret(&mut scope)?.truthy()? {
181                    return pass.interpret(&mut scope);
182                }
183                drop(scope);
184                fail.interpret(env)
185            }
186            (Op::While, [cond, pass, fail]) => {
187                loop {
188                    let mut scope = env.frame("while", None);
189                    if cond.interpret(&mut scope)?.truthy()? {
190                        match pass.interpret(&mut scope) {
191                            Ok(_) => {}
192                            Err(Error { kind: ErrorKind::Break(value), .. }) => return Ok(value),
193                            Err(Error { kind: ErrorKind::Continue, .. }) => continue,
194                            Err(e) => Err(e)?,
195                        }
196                    } else {
197                        break;
198                    }
199                }
200                fail.interpret(env)
201            }
202            (Op::Break, [expr]) => Err(Error::Break(expr.interpret(env)?)),
203            (Op::Return, [expr]) => Err(Error::Return(expr.interpret(env)?)),
204            (Op::Continue, []) => Err(Error::Continue()),
205
206            // Dot projection
207            (Op::Dot, [scrutinee, At(Expr::Op(Op::Call, args), _)]) => {
208                let [callee, args] = args.as_slice() else {
209                    cl_todo!("Interpret non-call {args:?}")?
210                };
211                let scrutinee = Place::new_or_temporary(scrutinee.value(), env)?;
212                let function = callee.interpret(env)?;
213                let args = args.interpret(env)?;
214                match args {
215                    ConValue::Empty => function.call(env, &[ConValue::Ref(scrutinee)]),
216                    ConValue::Tuple(args) => function.call(
217                        env,
218                        &iter::once(ConValue::Ref(scrutinee))
219                            .chain(args) // TODO: remove allocation
220                            .collect::<Box<_>>(),
221                    ),
222                    other => function.call(env, &[ConValue::Ref(scrutinee), other]),
223                }
224            }
225            (
226                Op::Dot,
227                [
228                    At(Expr::Lit(Literal::Int(whole, _)), _),
229                    At(Expr::Lit(Literal::Int(frac, _)), _),
230                ],
231            ) => Ok(ConValue::Float(format!("{whole}.{frac}").parse().unwrap())),
232            (Op::Dot, [scrutinee, At(Expr::Lit(Literal::Int(idx, _)), _)]) => {
233                let place = Place::new_or_temporary(scrutinee.value(), env)?;
234                Ok(ConValue::Ref(place.dot_idx(*idx as _)))
235            }
236            (Op::Dot, [scrutinee, proj]) => cl_todo!("dot: {scrutinee}.{proj}"),
237
238            // Range operators
239            (Op::RangeEx, [lhs, rhs]) => Ok(ConValue::TupleStruct(
240                env.get_type("RangeExc".into())
241                    .ok_or_else(|| Error::NotDefined("RangeExc".into()))?,
242                Box::new([lhs.interpret(env)?, rhs.interpret(env)?]),
243            )),
244            (Op::RangeIn, [lhs, rhs]) => Ok(ConValue::TupleStruct(
245                env.get_type("RangeInc".into())
246                    .ok_or_else(|| Error::NotDefined("RangeInc".into()))?,
247                Box::new([lhs.interpret(env)?, rhs.interpret(env)?]),
248            )),
249            (Op::RangeEx, [rhs]) => Ok(ConValue::TupleStruct(
250                env.get_type("RangeTo".into())
251                    .ok_or_else(|| Error::NotDefined("RangeTo".into()))?,
252                Box::new([rhs.interpret(env)?]),
253            )),
254            (Op::RangeIn, [rhs]) => Ok(ConValue::TupleStruct(
255                env.get_type("RangeToInc".into())
256                    .ok_or_else(|| Error::NotDefined("RangeToInc".into()))?,
257                Box::new([rhs.interpret(env)?]),
258            )),
259
260            // Unary operators
261            (Op::Neg, [expr]) => {
262                let value = expr.interpret(env)?;
263                env.get("neg".into())?.call(env, &[value])
264            }
265            (Op::Not, [expr]) => {
266                let value = expr.interpret(env)?;
267                env.get("not".into())?.call(env, &[value]) // why is this a builtin
268            }
269            (Op::Identity, [expr]) => expr.interpret(env),
270
271            // Reference manipulation operators
272            (Op::Refer, [expr]) => Ok(ConValue::Ref(
273                Place::new(expr.value(), env).map_err(|e| Error::Panic("asfd".into()))?,
274            )),
275            (Op::Deref, [expr]) => match expr.interpret(env)? {
276                ConValue::Ref(place) => place.get(env).cloned(),
277                other => Ok(other),
278            },
279            (Op::Deref, [expr]) => cl_todo!("References: *{expr}"),
280
281            // Binary computation operators
282            (Op::Mul, [lhs, rhs]) => lhs.interpret(env)? * rhs.interpret(env)?,
283            (Op::Div, [lhs, rhs]) => lhs.interpret(env)? / rhs.interpret(env)?,
284            (Op::Rem, [lhs, rhs]) => lhs.interpret(env)? % rhs.interpret(env)?,
285            (Op::Add, [lhs, rhs]) => lhs.interpret(env)? + rhs.interpret(env)?,
286            (Op::Sub, [lhs, rhs]) => lhs.interpret(env)? - rhs.interpret(env)?,
287            (Op::Shl, [lhs, rhs]) => lhs.interpret(env)? << rhs.interpret(env)?,
288            (Op::Shr, [lhs, rhs]) => lhs.interpret(env)? >> rhs.interpret(env)?,
289            (Op::And, [lhs, rhs]) => lhs.interpret(env)? & rhs.interpret(env)?,
290            (Op::Xor, [lhs, rhs]) => lhs.interpret(env)? ^ rhs.interpret(env)?,
291            (Op::Or, [lhs, rhs]) => lhs.interpret(env)? | rhs.interpret(env)?,
292
293            // Comparison operators
294            (Op::Lt, [lhs, rhs]) => lhs.interpret(env)?.lt(&rhs.interpret(env)?),
295            (Op::Leq, [lhs, rhs]) => lhs.interpret(env)?.lt_eq(&rhs.interpret(env)?),
296            (Op::Eq, [lhs, rhs]) => lhs.interpret(env)?.eq(&rhs.interpret(env)?),
297            (Op::Neq, [lhs, rhs]) => lhs.interpret(env)?.neq(&rhs.interpret(env)?),
298            (Op::Geq, [lhs, rhs]) => lhs.interpret(env)?.gt_eq(&rhs.interpret(env)?),
299            (Op::Gt, [lhs, rhs]) => lhs.interpret(env)?.gt(&rhs.interpret(env)?),
300
301            // Logical (control flow) operators
302            (Op::LogAnd, [lhs, rhs]) => {
303                let lhs = lhs.interpret(env)?;
304                if lhs.truthy()? {
305                    rhs.interpret(env)
306                } else {
307                    Ok(lhs)
308                }
309            }
310            (Op::LogXor, [lhs, rhs]) => todo!(),
311            (Op::LogOr, [lhs, rhs]) => {
312                let lhs = lhs.interpret(env)?;
313                if lhs.truthy()? {
314                    Ok(lhs)
315                } else {
316                    rhs.interpret(env)
317                }
318            }
319
320            // Assignment operators
321            (Op::Set, [target, value]) => {
322                let place = Place::new(target.value(), env)?;
323                let value = value.interpret(env)?;
324                *(place.get_mut(env)?) = value;
325                Ok(ConValue::Empty)
326            }
327            (Op::MulSet, [target, value]) => {
328                let place = Place::new(target.value(), env)?;
329                let value = value.interpret(env)?;
330                place.get_mut(env)?.mul_assign(value)?;
331                Ok(ConValue::Empty)
332            }
333            (Op::DivSet, [target, value]) => {
334                let place = Place::new(target.value(), env)?;
335                let value = value.interpret(env)?;
336                place.get_mut(env)?.div_assign(value)?;
337                Ok(ConValue::Empty)
338            }
339            (Op::RemSet, [target, value]) => {
340                let place = Place::new(target.value(), env)?;
341                let value = value.interpret(env)?;
342                place.get_mut(env)?.rem_assign(value)?;
343                Ok(ConValue::Empty)
344            }
345            (Op::AddSet, [target, value]) => {
346                let place = Place::new(target.value(), env)?;
347                let value = value.interpret(env)?;
348                place.get_mut(env)?.add_assign(value)?;
349                Ok(ConValue::Empty)
350            }
351            (Op::SubSet, [target, value]) => {
352                let place = Place::new(target.value(), env)?;
353                let value = value.interpret(env)?;
354                place.get_mut(env)?.sub_assign(value)?;
355                Ok(ConValue::Empty)
356            }
357            (Op::ShlSet, [target, value]) => {
358                let place = Place::new(target.value(), env)?;
359                let value = value.interpret(env)?;
360                place.get_mut(env)?.shl_assign(value)?;
361                Ok(ConValue::Empty)
362            }
363            (Op::ShrSet, [target, value]) => {
364                let place = Place::new(target.value(), env)?;
365                let value = value.interpret(env)?;
366                place.get_mut(env)?.shr_assign(value)?;
367                Ok(ConValue::Empty)
368            }
369            (Op::AndSet, [target, value]) => {
370                let place = Place::new(target.value(), env)?;
371                let value = value.interpret(env)?;
372                place.get_mut(env)?.bitand_assign(value)?;
373                Ok(ConValue::Empty)
374            }
375            (Op::XorSet, [target, value]) => {
376                let place = Place::new(target.value(), env)?;
377                let value = value.interpret(env)?;
378                place.get_mut(env)?.bitxor_assign(value)?;
379                Ok(ConValue::Empty)
380            }
381            (Op::OrSet, [target, value]) => {
382                let place = Place::new(target.value(), env)?;
383                let mut value = value.interpret(env)?;
384                place.get_mut(env)?.bitor_assign(value)?;
385                Ok(ConValue::Empty)
386            }
387            (op, exprs) => cl_unimplemented!("Evaluate {op:?} {exprs:#?}"),
388        }
389    }
390}
391
392impl Interpret for Bind<DefaultTypes> {
393    fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
394        let Bind(op, _generics, pat, exprs) = self;
395        match (op, pat, exprs.as_slice()) {
396            (BindOp::Let, _, []) => cl_todo!("let {pat}"),
397            (BindOp::Let, _, [scrutinee]) => {
398                let mut bind = HashMap::new();
399                let out = pat.matches(
400                    scrutinee.interpret(env)?,
401                    &mut MatchEnv::new(env, &mut bind),
402                );
403
404                Ok(ConValue::Bool(match out {
405                    Ok(_) => {
406                        for (name, value) in bind {
407                            env.bind(name, value);
408                        }
409                        true
410                    }
411                    Err(e) => false,
412                }))
413            }
414            (BindOp::Let, _, [scrutinee, default]) => {
415                let mut bind = HashMap::new();
416                let out = pat.matches(
417                    scrutinee.interpret(env)?,
418                    &mut MatchEnv::new(env, &mut bind),
419                );
420
421                if out.is_ok() {
422                    for (name, value) in bind {
423                        env.bind(name, value);
424                    }
425                    return Ok(ConValue::Empty);
426                }
427
428                bind.clear();
429                pat.matches(default.interpret(env)?, &mut MatchEnv::new(env, &mut bind))?;
430                for (name, value) in bind {
431                    env.bind(name, value);
432                }
433
434                Ok(ConValue::Empty)
435            }
436            (BindOp::Type, _, []) => cl_todo!("type {pat}"),
437            (BindOp::Type, _, [body]) => cl_todo!("type {pat} = {body}"),
438            (BindOp::Fn, _, [body]) => {
439                let func = Rc::new(Function::new(self));
440                if let Some(name) = func.name() {
441                    env.bind(name, ConValue::Function(func.clone()))
442                }
443                Ok(ConValue::Function(func))
444            }
445            (BindOp::Mod, _, [At(Expr::Op(Op::Block, exprs), ..)]) => {
446                let [body] = exprs.as_slice() else {
447                    todo!("{exprs:?}")?
448                };
449                body.interpret(env)
450            }
451            (BindOp::Mod, _, [body]) => body.interpret(env),
452            (BindOp::Impl, _, [body]) => cl_todo!("impl {pat} {body}"),
453            (BindOp::Struct, pat, []) => {
454                let (name, model) = bind_struct(pat, env)?;
455                if let Some(name) = name {
456                    let typeid = env.def_type(name, model);
457                    env.bind(name, ConValue::TypeInfo(typeid));
458                    Ok(ConValue::TypeInfo(typeid))
459                } else {
460                    Err(Error::MatchNonexhaustive()) // todo: make sense
461                }
462            }
463            (BindOp::Struct, Pat::Name(name), []) => {
464                let typeid = env.def_type(*name, typeinfo::Model::Unit(0));
465                env.bind(*name, ConValue::TypeInfo(typeid));
466                Ok(ConValue::TypeInfo(typeid))
467            }
468            (BindOp::Struct, pat, []) => cl_todo!("struct {pat}"),
469            (BindOp::Enum, Pat::Name(name), []) => {
470                let typeid = env.def_type(*name, typeinfo::Model::Never);
471                env.bind(*name, ConValue::TypeInfo(typeid));
472                Ok(ConValue::TypeInfo(typeid))
473            }
474            (BindOp::Enum, pat, []) => bind_enum(pat, env),
475            (BindOp::Enum, _, []) => cl_todo!("enum {pat}"),
476            (BindOp::For, _, [iter, pass, fail]) => {
477                let iter: Box<dyn Iterator<Item = ConValue>> = match iter.interpret(env)? {
478                    ConValue::Array(values) | ConValue::Tuple(values) => {
479                        Box::new(values.into_iter())
480                    }
481                    ConValue::String(str) => Box::new(str.into_chars().map(ConValue::Char)),
482                    ConValue::Str(str) => Box::new(str.to_ref().chars().map(ConValue::Char)),
483                    ConValue::TupleStruct(
484                        Interned(TypeInfo { ident: Interned("RangeExc", ..), .. }, ..),
485                        bounds,
486                    ) => match *bounds {
487                        [ConValue::Int(start), ConValue::Int(end)] => {
488                            Box::new((start..end).map(ConValue::Int))
489                        }
490                        _ => Err(Error::NotIterable())?,
491                    },
492                    ConValue::TupleStruct(
493                        Interned(TypeInfo { ident: Interned("RangeInc", ..), .. }, ..),
494                        bounds,
495                    ) => match *bounds {
496                        [ConValue::Int(start), ConValue::Int(end)] => {
497                            Box::new((start..=end).map(ConValue::Int))
498                        }
499                        _ => Err(Error::NotIterable())?,
500                    },
501                    _ => Err(Error::NotIterable())?,
502                };
503                for item in iter {
504                    let mut bind = HashMap::new();
505                    pat.matches(item, &mut MatchEnv::new(env, &mut bind))?;
506
507                    let mut scope = env.with_frame("for-loop", bind);
508                    match pass.interpret(&mut scope) {
509                        Ok(_) => {}
510                        Err(Error { kind: ErrorKind::Break(value), .. }) => return Ok(value),
511                        Err(Error { kind: ErrorKind::Continue, .. }) => continue,
512                        Err(e) => Err(e)?,
513                    }
514                }
515                fail.interpret(env)
516            }
517            _ => cl_unimplemented!("{self}"),
518        }
519    }
520}
521
522fn bind_struct(pat: &Pat, env: &mut Environment) -> IResult<(Option<Sym>, Model)> {
523    fn bind_struct_op(
524        op: PatOp,
525        pats: &[Pat],
526        env: &mut Environment,
527    ) -> IResult<(Option<Sym>, Model)> {
528        match (op, pats) {
529            (PatOp::Pub | PatOp::Mut, [expr]) => bind_struct(expr, env),
530            (PatOp::Ref, []) => todo!("Ref in bind_struct_op?"),
531            (PatOp::Ptr, []) => todo!("Ptr in bind_struct_op?"),
532            (PatOp::Rest, []) => todo!("Rest in bind_struct_op?"),
533            (PatOp::RangeEx, []) => todo!("RangeEx in bind_struct_op?"),
534            (PatOp::RangeIn, []) => todo!("RangeIn in bind_struct_op?"),
535            (PatOp::Record, elements) => {
536                let mut members = Vec::new();
537                let mut exhaustive = true;
538                for (idx, member) in elements.iter().enumerate() {
539                    if let Pat::Op(PatOp::Rest, _) = member {
540                        exhaustive = false;
541                        continue;
542                    }
543                    if let (Some(name), model) = bind_struct(member, env)? {
544                        members.push((name, env.get_type("_".into()).unwrap()));
545                    }
546                }
547                Ok((None, Model::Struct(members.into_boxed_slice(), exhaustive)))
548            }
549            (PatOp::Tuple, elements) => {
550                let members = vec![env.get_type("_".into()).unwrap(); elements.len()];
551                Ok((None, Model::Tuple(members.into_boxed_slice())))
552            }
553            (PatOp::Typed, [name, _ty]) => bind_struct(name, env),
554            (PatOp::TypePrefixed, [name, ty]) => match bind_struct(ty, env)? {
555                (None, model) => Ok((name.name(), model)),
556                (Some(name), model) => todo!("Typeprefixed {name} :: {model:?}"),
557            },
558            (PatOp::Generic, [first, ..]) => bind_struct(first, env),
559            _ => todo!("{op} ({pats:?})"),
560        }
561    }
562    Ok(match pat {
563        Pat::Ignore => todo!("Pat::Ignore in struct binding")?,
564        Pat::Never => todo!("Pat::Never in struct binding")?,
565        Pat::MetId(_) => todo!("Pat::MetId in struct binding")?,
566        Pat::Name(name) => (Some(*name), typeinfo::Model::Unit(0)),
567        Pat::Value(at) => todo!("Pat::Value in struct binding")?,
568        Pat::Op(pat_op, pats) => bind_struct_op(*pat_op, pats, env)?,
569    })
570}
571
572fn bind_enum(pat: &Pat, env: &mut Environment) -> IResult<ConValue> {
573    let name = pat
574        .name()
575        .ok_or_else(|| Error::PatFailed(Box::new(pat.clone())))?;
576    let mut variants = vec![];
577    if let Pat::Op(PatOp::TypePrefixed, pats) = pat
578        && let [_prefix, pats] = pats.as_slice()
579        && let Pat::Op(PatOp::Record, pats) = pats
580    {
581        for (idx, pat) in pats.iter().enumerate() {
582            if let (Some(name), model) = bind_struct(pat, env)? {
583                let model = match model {
584                    Model::Unit(_) => Model::Unit(idx),
585                    _ => model,
586                };
587                let typeid = env.def_type(name, model);
588                variants.push((name, typeid));
589            }
590        }
591    } else {
592        todo!("Bind other enum: {pat}")?
593    };
594
595    let typeid = env.def_type(name, Model::Enum(variants.into_boxed_slice()));
596    env.bind(name, ConValue::TypeInfo(typeid));
597    Ok(ConValue::TypeInfo(typeid))
598}
599
600impl Interpret for Make<DefaultTypes> {
601    fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
602        let Self(ty, entries) = self;
603
604        let tyinfo = match ty.interpret(env)? {
605            ConValue::TypeInfo(info) => info,
606            other => Err(Error::TypeError("type", other.typename()))?,
607        };
608
609        let mut members = HashMap::new();
610
611        for MakeArm(name, value) in entries {
612            // todo: disallow redefinition?
613            members.insert(
614                *name,
615                match value {
616                    Some(value) => value.interpret(env),
617                    None => env.get(*name),
618                }?,
619            );
620        }
621
622        tyinfo.make_struct(members)
623    }
624}
625
626impl Interpret for cl_ast::ast::Match<DefaultTypes> {
627    fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
628        let Self(scrutinee, arms) = self;
629        let scrutinee = scrutinee.interpret(env)?;
630        for MatchArm(pat, expr) in arms {
631            let mut bind = HashMap::new();
632            if pat
633                .matches(scrutinee.clone(), &mut MatchEnv::new(env, &mut bind))
634                .is_ok()
635            {
636                return expr.interpret(&mut env.with_frame("match-arm", bind));
637            }
638        }
639        Err(Error::MatchNonexhaustive())
640    }
641}
642
643pub enum BindingMode {
644    Value,
645    Ref,
646    Ptr,
647}
648
649#[derive(Debug)]
650pub struct MatchEnv<'env> {
651    env: &'env mut Environment,
652    bind: &'env mut HashMap<Sym, ConValue>,
653    public: bool,
654    mutable: bool,
655}
656
657impl MatchEnv<'_> {
658    pub fn new<'e>(env: &'e mut Environment, bind: &'e mut HashMap<Sym, ConValue>) -> MatchEnv<'e> {
659        MatchEnv { env, bind, public: false, mutable: false } // TODO: env.public
660    }
661    pub fn public(&mut self) -> MatchEnv<'_> {
662        let Self { ref mut env, ref mut bind, public: _, mutable } = *self;
663        MatchEnv { env, bind, public: true, mutable }
664    }
665
666    pub fn mutable(&mut self) -> MatchEnv<'_> {
667        let Self { ref mut env, ref mut bind, public, mutable: _ } = *self;
668        MatchEnv { env, bind, public, mutable: true }
669    }
670}
671
672pub trait Match<Value = ConValue> {
673    fn matches<'env>(&self, value: Value, in_env: &mut MatchEnv<'env>) -> IResult<()>;
674}
675
676impl Match for Pat {
677    fn matches<'env>(&self, value: ConValue, in_env: &mut MatchEnv<'env>) -> IResult<()> {
678        match (self) {
679            Self::Ignore => Ok(()),
680            Self::Never => todo!("Never"),
681            Self::MetId(_) => todo!("Meta-identifiers are not allowed here"),
682            &Self::Name(name) => {
683                in_env.bind.insert(name, value);
684                Ok(())
685            }
686            Self::Value(at) => {
687                let truth = at.interpret(in_env.env)?;
688                if truth.neq(&value)?.truthy()? {
689                    Err(Error::PatFailed(self.clone().into()))
690                } else {
691                    Ok(())
692                }
693            }
694            Self::Op(pat_op, pats) => (*pat_op, pats.as_slice()).matches(value, in_env),
695        }
696    }
697}
698
699impl Match for (PatOp, &[Pat]) {
700    fn matches<'env>(&self, value: ConValue, in_env: &mut MatchEnv<'env>) -> IResult<()> {
701        match self {
702            (PatOp::Pub, [pat]) => pat.matches(value, &mut in_env.public()),
703            (PatOp::Pub, _) => unimplemented!(),
704            (PatOp::Mut, [pat]) => pat.matches(value, &mut in_env.mutable()),
705            (PatOp::Mut, _) => unimplemented!(),
706            (PatOp::Ref, [pat]) => match value {
707                ConValue::Ref(place) => {
708                    let value = place
709                        .get(in_env.env)
710                        .cloned()
711                        .map_err(|_| Error::PatFailed(pat.clone().into()))?;
712                    pat.matches(value, in_env)
713                }
714                // Auto-referencing in patterns..?
715                other => pat.matches(other, in_env),
716            },
717            (PatOp::Ref, _) => unimplemented!("Ref patterns!"),
718            (PatOp::Ptr, _) => todo!("Raw pointer/deref patterns?"),
719            (PatOp::Rest, []) => Ok(()),
720            // Rest pattern with const value is upper-bounded exclusive range
721            (PatOp::Rest, [Pat::Value(end)]) => {
722                if end.interpret(in_env.env)?.lt_eq(&value)?.truthy()? {
723                    return Err(Error::MatchNonexhaustive());
724                }
725                Ok(())
726            }
727            (PatOp::Rest, [rest]) => rest.matches(value, in_env),
728            (PatOp::Rest, _) => unimplemented!("rest pattern with more than one arg"),
729            (PatOp::RangeEx, [Pat::Value(start)]) => {
730                // RangeEx pattern with const value is lower-bounded exclusive range
731                if start.interpret(in_env.env)?.gt(&value)?.truthy()? {
732                    return Err(Error::MatchNonexhaustive());
733                }
734                Ok(())
735            }
736            (PatOp::RangeEx, [Pat::Value(start), Pat::Value(end)]) => {
737                if start.interpret(in_env.env)?.gt(&value)?.truthy()? {
738                    return Err(Error::MatchNonexhaustive());
739                }
740                if end.interpret(in_env.env)?.lt_eq(&value)?.truthy()? {
741                    return Err(Error::MatchNonexhaustive());
742                }
743                Ok(())
744            }
745            (PatOp::RangeEx, pats) => todo!("RangeEx patterns: {pats:?}"),
746            (PatOp::RangeIn, [Pat::Value(start), Pat::Value(end)]) => {
747                if start.interpret(in_env.env)?.gt(&value)?.truthy()? {
748                    return Err(Error::MatchNonexhaustive());
749                }
750                if end.interpret(in_env.env)?.lt(&value)?.truthy()? {
751                    return Err(Error::MatchNonexhaustive());
752                }
753                Ok(())
754            }
755            (PatOp::RangeIn, pats) => todo!("Range patterns: {pats:?}"),
756            (PatOp::Record, pats) => match_pat_for_struct(pats, value, in_env),
757            (PatOp::Tuple, pats) => match value {
758                ConValue::Empty if pats.is_empty() => Ok(()),
759                ConValue::Tuple(values) if pats.len() <= values.len() => {
760                    (SliceMode::Tuple, *pats).matches(values, in_env)
761                }
762                _ => todo!("Match {pats:?} against {value}"),
763            },
764            (PatOp::Slice, pats) => match value {
765                ConValue::Array(values) if pats.len() <= values.len() => {
766                    (SliceMode::Slice, *pats).matches(values, in_env)
767                }
768                _ => todo!("Match {pats:?} against {value}"),
769            },
770            (PatOp::ArRep, _) => todo!(),
771            (PatOp::Typed, [pat, _ty @ ..]) => pat.matches(value, in_env),
772            (PatOp::Typed, _) => todo!(),
773            (PatOp::TypePrefixed, [Pat::Value(e), pat]) => {
774                let ty = match e.interpret(in_env.env)? {
775                    ConValue::TypeInfo(ty) => ty,
776                    other => Err(Error::TypeError("type", other.typename()))?,
777                };
778                match value {
779                    ConValue::Struct(value_ty, _) | ConValue::TupleStruct(value_ty, _)
780                        if ty != value_ty =>
781                    {
782                        Err(Error::TypeError(ty.ident.to_ref(), value_ty.ident.to_ref()))?
783                    }
784                    ConValue::TupleStruct(value_ty, values) => {
785                        pat.matches(ConValue::Tuple(values), in_env)
786                    }
787                    _ => pat.matches(value, in_env),
788                }
789            }
790            (PatOp::TypePrefixed, [pat_name, pat]) => match value {
791                ConValue::TupleStruct(value_type, values) => {
792                    let Some(pat_name) = pat_name.name() else {
793                        todo!("{pat_name}({pat})")?
794                    };
795                    if in_env.env.get_type(pat_name) != Some(value_type) {
796                        Err(Error::TypeError(
797                            pat_name.to_ref(),
798                            value_type.ident.to_ref(),
799                        ))?
800                    }
801                    pat.matches(ConValue::Tuple(values), in_env)
802                }
803                ConValue::Struct(ty, _) => {
804                    let Some(pat_name) = pat_name.name() else {
805                        todo!("{pat_name}({pat})")?
806                    };
807                    if in_env.env.get_type(pat_name) != Some(ty) {
808                        Err(Error::TypeError(pat_name.to_ref(), ty.ident.to_ref()))?
809                    }
810                    pat.matches(value, in_env)
811                }
812                _ => pat.matches(value, in_env),
813            },
814            (PatOp::TypePrefixed, _) => todo!("TypePrefixed {self:?}"),
815            (PatOp::Generic, [pat, _subs @ ..]) => pat.matches(value, in_env),
816            (PatOp::Generic, _) => todo!(),
817            (PatOp::Fn, [args, _]) => args.matches(value, in_env),
818            (PatOp::Fn, _) => todo!(),
819            &(PatOp::Alt, alts) if value.is_cheap_to_copy() => {
820                let mut bind = HashMap::new();
821                for alt in alts {
822                    alt.matches(value.clone(), &mut MatchEnv::new(in_env.env, &mut bind))?;
823                }
824                Ok(())
825            }
826            (PatOp::Alt, alts) => todo!(
827                "Alternate patterns require trying the same value multiple times, which is expensive."
828            ),
829        }
830    }
831}
832
833fn match_pat_for_struct<'env>(
834    pats: &[Pat],
835    value: ConValue,
836    in_env: &mut MatchEnv<'env>,
837) -> IResult<()> {
838    fn match_typed<'env>(
839        pats: &[Pat],
840        values: &mut HashMap<Sym, ConValue>,
841        in_env: &mut MatchEnv<'env>,
842    ) -> IResult<()> {
843        let [Pat::Name(name), dest] = pats else {
844            todo!("match_typed could not find name in typed pattern")?
845        };
846        let Some(value) = values.remove(name) else {
847            todo!("Struct {values:?} has no value at {name}")?
848        };
849        in_env.bind.insert(*name, value);
850        Ok(())
851    }
852
853    let ConValue::Struct(_, mut values) = value else {
854        todo!("match_pat_for_struct called on {}", value)?
855    };
856
857    for pat in pats {
858        match pat {
859            Pat::Name(name) => {
860                let Some(value) = values.remove(name) else {
861                    todo!("Struct {values:?} has no value at {name}")?
862                };
863                in_env.bind.insert(*name, value);
864            }
865            Pat::Op(PatOp::Typed, pats) => match_typed(pats, &mut values, in_env)?,
866            Pat::Op(PatOp::Rest, pats) if pats.is_empty() => break,
867            pat => todo!("{pat}")?,
868        }
869    }
870    Ok(())
871}
872
873enum SliceMode {
874    Slice,
875    Tuple,
876}
877
878impl Match<Box<[ConValue]>> for (SliceMode, &[Pat]) {
879    fn matches<'env>(&self, values: Box<[ConValue]>, in_env: &mut MatchEnv<'env>) -> IResult<()> {
880        let (mode, pats) = self;
881
882        let mut values = values.into_iter();
883        let mut pats = pats.iter().peekable();
884        while !matches!(pats.peek(), None | Some(Pat::Op(PatOp::Rest, _))) {
885            let (Some(pat), Some(value)) = (pats.next(), values.next()) else {
886                break;
887            };
888            pat.matches(value, in_env)?;
889        }
890
891        let mut values = values.rev();
892        let mut pats = pats.rev().peekable();
893        while !matches!(pats.peek(), None | Some(Pat::Op(PatOp::Rest, _))) {
894            let (Some(pat), Some(value)) = (pats.next(), values.next()) else {
895                break;
896            };
897            pat.matches(value, in_env)?;
898        }
899
900        if let Some(Pat::Op(PatOp::Rest, pats)) = pats.next() {
901            if let [pat] = pats.as_slice() {
902                let values = values.into_inner().collect();
903                match mode {
904                    SliceMode::Slice => pat.matches(ConValue::Array(values), in_env)?,
905                    SliceMode::Tuple => pat.matches(ConValue::Tuple(values), in_env)?,
906                };
907            }
908        } else if values.next().is_some() {
909            Err(Error::MatchNonexhaustive())?
910        }
911
912        match pats.next() {
913            Some(_) => unimplemented!("Match against multiple rest-patterns"),
914            None => Ok(()),
915        }
916    }
917}