Skip to main content

lux/
interpreter.rs

1//! The interpreter: walk the ast and run it.
2//!
3//! This is a tree-walking interpreter. It keeps a stack of scopes (one per
4//! block) holding the live bindings, plus a table of function definitions.
5//! lux is statically typed by design, but has no separate type checker
6//! yet — instead the interpreter enforces lux's no-coercion rule at the moment
7//! of each operation, so `"5" + 3` fails with a clear error rather than
8//! silently guessing. A real checker that catches these before the program
9//! runs is a later milestone.
10
11use std::collections::HashMap;
12use std::rc::Rc;
13
14use crate::ast::*;
15use crate::diagnostic::{LuxError, Span};
16
17#[derive(Debug, Clone, PartialEq)]
18pub enum Value {
19    Int(i64),
20    Float(f64),
21    Str(String),
22    Bool(bool),
23    Array(Vec<Value>),
24    /// A half-open range, the thing a `for i in 0..5` walks.
25    Range(i64, i64),
26    /// A struct value: a named record of labelled fields, in declared order.
27    Struct {
28        name: String,
29        fields: Vec<(String, Value)>,
30    },
31    /// An enum value: one named case of an enum, carrying its labelled values.
32    Enum {
33        enum_name: String,
34        variant: String,
35        fields: Vec<(String, Value)>,
36    },
37    /// The result of something done only for its effect, like `print`.
38    Unit,
39}
40
41impl Value {
42    pub fn type_name(&self) -> &'static str {
43        match self {
44            Value::Int(_) => "int",
45            Value::Float(_) => "float",
46            Value::Str(_) => "string",
47            Value::Bool(_) => "bool",
48            Value::Array(_) => "array",
49            Value::Range(..) => "range",
50            Value::Struct { .. } => "struct",
51            Value::Enum { .. } => "enum",
52            Value::Unit => "nothing",
53        }
54    }
55}
56
57struct Binding {
58    value: Value,
59    mutable: bool,
60}
61
62/// A user-defined function, stored once and shared (via `Rc`) so a call can
63/// hold onto it while the interpreter keeps mutating its scopes — which is what
64/// recursion needs.
65struct FuncData {
66    params: Vec<Param>,
67    ret: Option<TypeAnn>,
68    body: Vec<Stmt>,
69}
70
71/// A declared struct type: its fields, in declared order.
72struct StructData {
73    fields: Vec<FieldDef>,
74}
75
76/// A declared enum type: its cases, in declared order.
77struct EnumData {
78    variants: Vec<VariantDef>,
79}
80
81/// How a statement finished: either normally, or by hitting a `return`.
82enum Flow {
83    Normal,
84    Return(Value),
85}
86
87struct Interp {
88    scopes: Vec<HashMap<String, Binding>>,
89    funcs: HashMap<String, Rc<FuncData>>,
90    structs: HashMap<String, Rc<StructData>>,
91    enums: HashMap<String, Rc<EnumData>>,
92    /// What `args()` returns: the program's own command line, the program
93    /// itself at index 0. Supplied by the caller so the interpreter shows the
94    /// script's arguments, not `lux run`'s.
95    program_args: Vec<String>,
96}
97
98/// Run a parsed program. `program_args` is the program's command line —
99/// the script (or binary) at index 0, then anything the user passed after it.
100pub fn run(program: &[Stmt], program_args: &[String]) -> Result<(), LuxError> {
101    let mut interp = Interp {
102        scopes: vec![HashMap::new()],
103        funcs: HashMap::new(),
104        structs: HashMap::new(),
105        enums: HashMap::new(),
106        program_args: program_args.to_vec(),
107    };
108    // Option and Result are built-in enums, registered before anything else so
109    // they exist for type-checking and so a user can't redeclare their names.
110    interp.register_builtin_types();
111    // Collect every type and function up front, so a program can refer to them
112    // before they appear in the file. Types come first because the validation
113    // pass and the functions can mention them.
114    interp.register_types(program)?;
115    interp.validate_type_decls(program)?;
116    interp.register_funcs(program)?;
117    interp.exec_block(program)?;
118    Ok(())
119}
120
121impl Interp {
122    /// Register the built-in enums `Option` (`some`/`none`) and `Result`
123    /// (`ok`/`err`). They are ordinary enum values under the hood, so `match`,
124    /// exhaustiveness, and printing all work the same way they do for the
125    /// enums a program declares itself. Their cases carry no declared fields
126    /// here — the payload's type lives on each value, since these are generic.
127    fn register_builtin_types(&mut self) {
128        let variant = |name: &str| VariantDef {
129            name: name.to_string(),
130            fields: Vec::new(),
131            span: Span::new(0, 0),
132        };
133        self.enums.insert(
134            "Option".to_string(),
135            Rc::new(EnumData {
136                variants: vec![variant("some"), variant("none")],
137            }),
138        );
139        self.enums.insert(
140            "Result".to_string(),
141            Rc::new(EnumData {
142                variants: vec![variant("ok"), variant("err")],
143            }),
144        );
145        // `Output` is the one built-in struct: what `run` hands back on success.
146        // Registering it here reserves the name and lets a program spell it in a
147        // type annotation, the way `Option` and `Result` are reserved above.
148        let field = |name: &str, ty: &str| FieldDef {
149            name: name.to_string(),
150            ty: TypeAnn {
151                kind: TypeKind::Named(ty.to_string()),
152                span: Span::new(0, 0),
153            },
154            span: Span::new(0, 0),
155        };
156        self.structs.insert(
157            "Output".to_string(),
158            Rc::new(StructData {
159                fields: vec![
160                    field("status", "int"),
161                    field("stdout", "string"),
162                    field("stderr", "string"),
163                ],
164            }),
165        );
166    }
167
168    /// Collect every top-level `struct` and `enum`, checking for name clashes.
169    fn register_types(&mut self, program: &[Stmt]) -> Result<(), LuxError> {
170        for s in program {
171            match s {
172                Stmt::Struct { name, fields, span } => {
173                    if self.type_exists(name) {
174                        return Err(already_defined(name, *span));
175                    }
176                    self.structs.insert(
177                        name.clone(),
178                        Rc::new(StructData {
179                            fields: fields.clone(),
180                        }),
181                    );
182                }
183                Stmt::Enum {
184                    name,
185                    variants,
186                    span,
187                } => {
188                    if self.type_exists(name) {
189                        return Err(already_defined(name, *span));
190                    }
191                    self.enums.insert(
192                        name.clone(),
193                        Rc::new(EnumData {
194                            variants: variants.clone(),
195                        }),
196                    );
197                }
198                _ => {}
199            }
200        }
201        Ok(())
202    }
203
204    fn type_exists(&self, name: &str) -> bool {
205        self.structs.contains_key(name) || self.enums.contains_key(name)
206    }
207
208    /// Now that every type is registered, confirm each struct field and enum
209    /// case names a type that actually exists.
210    fn validate_type_decls(&self, program: &[Stmt]) -> Result<(), LuxError> {
211        for s in program {
212            match s {
213                Stmt::Struct { fields, .. } => {
214                    for f in fields {
215                        self.validate_type(&f.ty)?;
216                    }
217                }
218                Stmt::Enum { variants, .. } => {
219                    for v in variants {
220                        for f in &v.fields {
221                            self.validate_type(&f.ty)?;
222                        }
223                    }
224                }
225                _ => {}
226            }
227        }
228        Ok(())
229    }
230
231    /// Collect every top-level `func` up front, so a program can call a
232    /// function before it appears in the file.
233    fn register_funcs(&mut self, program: &[Stmt]) -> Result<(), LuxError> {
234        for s in program {
235            if let Stmt::Func {
236                name,
237                params,
238                ret,
239                body,
240                span,
241            } = s
242            {
243                if self.funcs.contains_key(name) {
244                    return Err(LuxError::new(
245                        format!("function `{}` is already defined", name),
246                        *span,
247                    ));
248                }
249                self.funcs.insert(
250                    name.clone(),
251                    Rc::new(FuncData {
252                        params: params.clone(),
253                        ret: ret.clone(),
254                        body: body.clone(),
255                    }),
256                );
257            }
258        }
259        Ok(())
260    }
261
262    fn push(&mut self) {
263        self.scopes.push(HashMap::new());
264    }
265
266    fn pop(&mut self) {
267        self.scopes.pop();
268    }
269
270    fn current_has(&self, name: &str) -> bool {
271        self.scopes.last().unwrap().contains_key(name)
272    }
273
274    fn lookup(&self, name: &str) -> Option<&Binding> {
275        self.scopes.iter().rev().find_map(|s| s.get(name))
276    }
277
278    fn lookup_mut(&mut self, name: &str) -> Option<&mut Binding> {
279        self.scopes.iter_mut().rev().find_map(|s| s.get_mut(name))
280    }
281
282    fn declare(&mut self, name: String, value: Value, mutable: bool) {
283        self.scopes
284            .last_mut()
285            .unwrap()
286            .insert(name, Binding { value, mutable });
287    }
288
289    // ----- statements -------------------------------------------------------
290
291    fn exec_block(&mut self, stmts: &[Stmt]) -> Result<Flow, LuxError> {
292        for s in stmts {
293            match self.exec_stmt(s)? {
294                Flow::Normal => {}
295                ret @ Flow::Return(_) => return Ok(ret),
296            }
297        }
298        Ok(Flow::Normal)
299    }
300
301    fn exec_stmt(&mut self, stmt: &Stmt) -> Result<Flow, LuxError> {
302        match stmt {
303            Stmt::Let {
304                name,
305                ty,
306                value,
307                span,
308            } => {
309                let v = self.eval(value)?;
310                match ty {
311                    Some(ann) => self.check_type(ann, &v)?,
312                    None => ensure_determined(&v, value.span())?,
313                }
314                if self.current_has(name) {
315                    return Err(LuxError::new(
316                        format!("`{}` is already declared in this scope", name),
317                        *span,
318                    ));
319                }
320                self.declare(name.clone(), v, false);
321                Ok(Flow::Normal)
322            }
323
324            Stmt::Var {
325                name,
326                ty,
327                value,
328                span,
329            } => {
330                let v = match value {
331                    Some(e) => {
332                        let v = self.eval(e)?;
333                        match ty {
334                            Some(ann) => self.check_type(ann, &v)?,
335                            None => ensure_determined(&v, e.span())?,
336                        }
337                        v
338                    }
339                    None => {
340                        let ann = ty.as_ref().ok_or_else(|| {
341                            LuxError::new(format!("`{}` needs a type or a value", name), *span)
342                                .with_note("write `var x: int` or `var x = 0`")
343                                .with_learn("variables", "a let holds still, a var can change")
344                        })?;
345                        self.zero_value(ann)?
346                    }
347                };
348                if self.current_has(name) {
349                    return Err(LuxError::new(
350                        format!("`{}` is already declared in this scope", name),
351                        *span,
352                    ));
353                }
354                self.declare(name.clone(), v, true);
355                Ok(Flow::Normal)
356            }
357
358            Stmt::Assign {
359                name,
360                name_span,
361                op,
362                value,
363                span,
364            } => {
365                let new = self.eval(value)?;
366                let binding = self.lookup(name).ok_or_else(|| {
367                    LuxError::new(format!("`{}` is not defined", name), *name_span)
368                        .with_note("declare it first with let or var")
369                        .with_learn("variables", "a name has to be made before it's used")
370                })?;
371                if !binding.mutable {
372                    return Err(LuxError::new(
373                        format!("cannot reassign `{}` — it was declared with let", name),
374                        *span,
375                    )
376                    .with_note("use `var` instead of `let` if it needs to change")
377                    .with_learn(
378                        "variables",
379                        "a let holds still on purpose — that's what keeps it safe",
380                    ));
381                }
382                let current = binding.value.clone();
383                let result = match op {
384                    AssignOp::Set => {
385                        if !same_type(&current, &new) {
386                            return Err(LuxError::new(
387                                format!(
388                                    "`{}` is {} but you assigned {}",
389                                    name,
390                                    value_type(&current),
391                                    value_type(&new)
392                                ),
393                                *span,
394                            )
395                            .with_learn("variables", "a name keeps the type it started with"));
396                        }
397                        new
398                    }
399                    AssignOp::Add => append_or_add(current, new, *span)?,
400                    AssignOp::Sub => sub(&current, &new, *span)?,
401                };
402                self.lookup_mut(name).unwrap().value = result;
403                Ok(Flow::Normal)
404            }
405
406            // Functions and types are registered up front; the declarations
407            // themselves do nothing when execution reaches them.
408            Stmt::Func { .. } | Stmt::Struct { .. } | Stmt::Enum { .. } => Ok(Flow::Normal),
409
410            Stmt::Return { value, .. } => {
411                let v = match value {
412                    Some(e) => self.eval(e)?,
413                    None => Value::Unit,
414                };
415                Ok(Flow::Return(v))
416            }
417
418            Stmt::If {
419                cond,
420                then_body,
421                else_body,
422                ..
423            } => {
424                if self.eval_bool(cond)? {
425                    self.run_scoped(then_body)
426                } else if let Some(eb) = else_body {
427                    self.run_scoped(eb)
428                } else {
429                    Ok(Flow::Normal)
430                }
431            }
432
433            Stmt::While { cond, body, .. } => {
434                while self.eval_bool(cond)? {
435                    match self.run_scoped(body)? {
436                        Flow::Normal => {}
437                        ret @ Flow::Return(_) => return Ok(ret),
438                    }
439                }
440                Ok(Flow::Normal)
441            }
442
443            Stmt::For {
444                var, iter, body, ..
445            } => {
446                let iterable = self.eval(iter)?;
447                match iterable {
448                    Value::Array(items) => {
449                        for item in items {
450                            match self.run_loop_body(var, item, body)? {
451                                Flow::Normal => {}
452                                ret @ Flow::Return(_) => return Ok(ret),
453                            }
454                        }
455                    }
456                    Value::Range(lo, hi) => {
457                        let mut i = lo;
458                        while i < hi {
459                            match self.run_loop_body(var, Value::Int(i), body)? {
460                                Flow::Normal => {}
461                                ret @ Flow::Return(_) => return Ok(ret),
462                            }
463                            i += 1;
464                        }
465                    }
466                    other => {
467                        return Err(LuxError::new(
468                            format!("cannot loop over {}", value_type(&other)),
469                            iter.span(),
470                        )
471                        .with_note("for ... in needs an array or a range like 0..10")
472                        .with_learn("for", "for walks an array or counts a range like 0..10"));
473                    }
474                }
475                Ok(Flow::Normal)
476            }
477
478            Stmt::Expr(e) => {
479                self.eval(e)?;
480                Ok(Flow::Normal)
481            }
482        }
483    }
484
485    /// Run a block in a fresh scope, always popping it even on error.
486    fn run_scoped(&mut self, body: &[Stmt]) -> Result<Flow, LuxError> {
487        self.push();
488        let r = self.exec_block(body);
489        self.pop();
490        r
491    }
492
493    /// One pass of a `for` loop: bind the loop variable in a fresh scope and
494    /// run the body. The loop variable is immutable, like Rust's and Swift's.
495    fn run_loop_body(&mut self, var: &str, item: Value, body: &[Stmt]) -> Result<Flow, LuxError> {
496        self.push();
497        self.declare(var.to_string(), item, false);
498        let r = self.exec_block(body);
499        self.pop();
500        r
501    }
502
503    // ----- expressions ------------------------------------------------------
504
505    fn eval(&mut self, e: &Expr) -> Result<Value, LuxError> {
506        match e {
507            Expr::Int(v, _) => Ok(Value::Int(*v)),
508            Expr::Float(v, _) => Ok(Value::Float(*v)),
509            Expr::Str(s, _) => Ok(Value::Str(s.clone())),
510            Expr::Bool(b, _) => Ok(Value::Bool(*b)),
511            Expr::Ident(name, span) => match self.lookup(name) {
512                Some(b) => Ok(b.value.clone()),
513                None if name == "none" => Ok(option_none()),
514                None => Err(LuxError::new(format!("`{}` is not defined", name), *span)
515                    .with_note("declare it with let or var before using it")
516                    .with_learn("scope", "a name lives only inside the { } where it's made")),
517            },
518            Expr::Array(elems, _) => {
519                let mut items = Vec::with_capacity(elems.len());
520                for e in elems {
521                    items.push(self.eval(e)?);
522                }
523                // Arrays are homogeneous: every element shares one type.
524                if let Some(first) = items.first() {
525                    for (v, e) in items.iter().zip(elems).skip(1) {
526                        if !same_type(first, v) {
527                            return Err(LuxError::new(
528                                format!(
529                                    "an array's elements must all be the same type, but found {} and {}",
530                                    value_type(first),
531                                    value_type(v)
532                                ),
533                                e.span(),
534                            )
535                            .with_learn("arrays", "an array holds many values of one type"));
536                        }
537                    }
538                }
539                Ok(Value::Array(items))
540            }
541            Expr::Unary { op, rhs, span } => {
542                let v = self.eval(rhs)?;
543                unary(*op, v, *span)
544            }
545            Expr::Binary { op, lhs, rhs, span } => {
546                // && and || short-circuit, so evaluate the right side lazily.
547                match op {
548                    BinOp::And => {
549                        if !self.eval_bool(lhs)? {
550                            return Ok(Value::Bool(false));
551                        }
552                        Ok(Value::Bool(self.eval_bool(rhs)?))
553                    }
554                    BinOp::Or => {
555                        if self.eval_bool(lhs)? {
556                            return Ok(Value::Bool(true));
557                        }
558                        Ok(Value::Bool(self.eval_bool(rhs)?))
559                    }
560                    _ => {
561                        let a = self.eval(lhs)?;
562                        let b = self.eval(rhs)?;
563                        binary_op(*op, &a, &b, *span)
564                    }
565                }
566            }
567            Expr::Index { base, index, span } => {
568                let collection = self.eval(base)?;
569                let idx = self.eval(index)?;
570                let items = match collection {
571                    Value::Array(items) => items,
572                    other => {
573                        return Err(LuxError::new(
574                            format!(
575                                "cannot index into {}; only arrays can be indexed",
576                                value_type(&other)
577                            ),
578                            base.span(),
579                        )
580                        .with_learn("arrays", "a numbered row of values, all one type, from 0"));
581                    }
582                };
583                let i = match idx {
584                    Value::Int(i) => i,
585                    other => {
586                        return Err(LuxError::new(
587                            format!(
588                                "an array index must be an int, but this is {}",
589                                value_type(&other)
590                            ),
591                            index.span(),
592                        )
593                        .with_learn(
594                            "arrays",
595                            "you reach an element by its position, counting from 0",
596                        ));
597                    }
598                };
599                if i < 0 || i as usize >= items.len() {
600                    let note = if items.is_empty() {
601                        "this array is empty".to_string()
602                    } else {
603                        format!("valid indices are 0 to {}", items.len() - 1)
604                    };
605                    return Err(LuxError::new(
606                        format!(
607                            "index {} is out of bounds for an array of length {}",
608                            i,
609                            items.len()
610                        ),
611                        *span,
612                    )
613                    .with_note(note)
614                    .with_learn(
615                        "arrays",
616                        "the first element is 0, so the last is length minus 1",
617                    ));
618                }
619                Ok(items[i as usize].clone())
620            }
621            Expr::Range { start, end, span } => {
622                let lo = self.eval(start)?;
623                let hi = self.eval(end)?;
624                match (lo, hi) {
625                    (Value::Int(a), Value::Int(b)) => Ok(Value::Range(a, b)),
626                    (a, b) => Err(LuxError::new(
627                        format!(
628                            "a range needs two ints, but got {} and {}",
629                            value_type(&a),
630                            value_type(&b)
631                        ),
632                        *span,
633                    )
634                    .with_note("write something like 0..10")
635                    .with_learn("for", "a range like 0..10 counts, end not included")),
636                }
637            }
638            Expr::Call { name, args, span } => self.call(name, args, *span),
639            Expr::StructLit { name, fields, span } => self.eval_struct_lit(name, fields, *span),
640            Expr::EnumLit {
641                enum_name,
642                variant,
643                fields,
644                span,
645            } => self.construct_enum(enum_name, variant, fields, *span),
646            Expr::Field { base, field, span } => self.eval_field(base, field, *span),
647            Expr::Match {
648                scrutinee,
649                arms,
650                span,
651            } => self.eval_match(scrutinee, arms, *span),
652        }
653    }
654
655    /// Build a struct value: evaluate each declared field in order, checking
656    /// that every field is supplied exactly once, with the right type.
657    fn eval_struct_lit(
658        &mut self,
659        name: &str,
660        provided: &[(String, Expr)],
661        span: Span,
662    ) -> Result<Value, LuxError> {
663        let data = match self.structs.get(name) {
664            Some(d) => Rc::clone(d),
665            None => {
666                return Err(LuxError::new(format!("unknown struct `{}`", name), span)
667                    .with_note("define it with `struct`, or check the spelling")
668                    .with_learn("structs", "a struct gathers a few values under one name"));
669            }
670        };
671        // Reject fields that aren't part of this struct, blaming the field.
672        for (k, e) in provided {
673            if !data.fields.iter().any(|f| &f.name == k) {
674                return Err(LuxError::new(
675                    format!("struct `{}` has no field `{}`", name, k),
676                    e.span(),
677                )
678                .with_learn("structs", "a struct's fields are fixed when you define it"));
679            }
680        }
681        let mut built = Vec::with_capacity(data.fields.len());
682        for f in &data.fields {
683            let value_expr = match provided.iter().find(|(k, _)| k == &f.name) {
684                Some((_, e)) => e,
685                None => {
686                    return Err(LuxError::new(
687                        format!("missing field `{}` for struct `{}`", f.name, name),
688                        span,
689                    )
690                    .with_note(format!(
691                        "`{}` has a field `{}: {}`",
692                        name,
693                        f.name,
694                        describe_type(&f.ty)
695                    ))
696                    .with_learn(
697                        "structs",
698                        "every field gets a value when you build a struct",
699                    ));
700                }
701            };
702            let v = self.eval(value_expr)?;
703            if !self.type_matches(&f.ty, &v) {
704                return Err(LuxError::new(
705                    format!(
706                        "field `{}` of `{}` should be {}, but got {}",
707                        f.name,
708                        name,
709                        describe_type(&f.ty),
710                        value_type(&v)
711                    ),
712                    value_expr.span(),
713                )
714                .with_learn(
715                    "structs",
716                    "each field has a type, set when you define the struct",
717                ));
718            }
719            built.push((f.name.clone(), v));
720        }
721        Ok(Value::Struct {
722            name: name.to_string(),
723            fields: built,
724        })
725    }
726
727    /// Build an enum value: find the case, then match the supplied labelled
728    /// values against the case's declared fields.
729    fn construct_enum(
730        &mut self,
731        enum_name: &str,
732        variant: &str,
733        provided: &[(String, Expr)],
734        span: Span,
735    ) -> Result<Value, LuxError> {
736        let data = match self.enums.get(enum_name) {
737            Some(d) => Rc::clone(d),
738            None => {
739                return Err(LuxError::new(format!("unknown enum `{}`", enum_name), span)
740                    .with_note("define it with `enum`, or check the spelling")
741                    .with_learn("enums", "an enum is one of a fixed set of shapes"));
742            }
743        };
744        let vdef = match data.variants.iter().find(|v| v.name == variant) {
745            Some(v) => v,
746            None => {
747                return Err(LuxError::new(
748                    format!("enum `{}` has no case `{}`", enum_name, variant),
749                    span,
750                )
751                .with_note(format!("cases are: {}", variant_names(&data)))
752                .with_learn(
753                    "enums",
754                    "an enum's cases are the fixed set of shapes it allows",
755                ));
756            }
757        };
758        if provided.len() != vdef.fields.len() {
759            return Err(LuxError::new(
760                format!(
761                    "`{}.{}` carries {}, but you gave {}",
762                    enum_name,
763                    variant,
764                    count(vdef.fields.len(), "value"),
765                    provided.len()
766                ),
767                span,
768            )
769            .with_learn("enums", "each case can carry its own values"));
770        }
771        let mut built = Vec::with_capacity(vdef.fields.len());
772        for f in &vdef.fields {
773            let value_expr = match provided.iter().find(|(k, _)| k == &f.name) {
774                Some((_, e)) => e,
775                None => {
776                    return Err(LuxError::new(
777                        format!("missing value `{}` for `{}.{}`", f.name, enum_name, variant),
778                        span,
779                    )
780                    .with_learn(
781                        "enums",
782                        "a case carries its values, named like a struct's fields",
783                    ));
784                }
785            };
786            let v = self.eval(value_expr)?;
787            if !self.type_matches(&f.ty, &v) {
788                return Err(LuxError::new(
789                    format!(
790                        "`{}` in `{}.{}` should be {}, but got {}",
791                        f.name,
792                        enum_name,
793                        variant,
794                        describe_type(&f.ty),
795                        value_type(&v)
796                    ),
797                    value_expr.span(),
798                )
799                .with_learn("enums", "each value a case carries has a type"));
800            }
801            built.push((f.name.clone(), v));
802        }
803        Ok(Value::Enum {
804            enum_name: enum_name.to_string(),
805            variant: variant.to_string(),
806            fields: built,
807        })
808    }
809
810    /// Read a struct field, or construct a payload-less enum case written as
811    /// `Shape.dot`. The two look identical, so the enum table decides: if the
812    /// thing before the dot is an enum name (and not a variable), it's a case.
813    fn eval_field(&mut self, base: &Expr, field: &str, span: Span) -> Result<Value, LuxError> {
814        if let Expr::Ident(n, nspan) = base {
815            if self.lookup(n).is_none() {
816                if self.enums.contains_key(n) {
817                    return self.construct_enum(n, field, &[], span);
818                }
819                // `Name.field` where `Name` is neither a value nor an enum: most
820                // likely a misspelled type or variable. Point at both fixes.
821                return Err(LuxError::new(format!("`{}` is not defined", n), *nspan)
822                    .with_note("if it's an enum, declare it with `enum`; otherwise declare the value with let or var")
823                    .with_learn("variables", "a name has to be made before it's used"));
824            }
825        }
826        let v = self.eval(base)?;
827        match v {
828            Value::Struct { name, fields } => match fields.iter().find(|(k, _)| k == field) {
829                Some((_, val)) => Ok(val.clone()),
830                None => Err(LuxError::new(
831                    format!("struct `{}` has no field `{}`", name, field),
832                    span,
833                )
834                .with_learn("structs", "a struct only has the fields you gave it")),
835            },
836            other => Err(LuxError::new(
837                format!(
838                    "cannot read field `{}` of {}; only structs have fields",
839                    field,
840                    value_type(&other)
841                ),
842                base.span(),
843            )
844            .with_learn(
845                "structs",
846                "only a struct has named fields to read with a dot",
847            )),
848        }
849    }
850
851    /// Evaluate a `match`: check it covers its cases, then run the one arm whose
852    /// pattern fits the scrutinee, binding any captured values.
853    fn eval_match(
854        &mut self,
855        scrutinee: &Expr,
856        arms: &[MatchArm],
857        span: Span,
858    ) -> Result<Value, LuxError> {
859        let v = self.eval(scrutinee)?;
860        match &v {
861            Value::Enum {
862                enum_name,
863                variant,
864                fields,
865            } => {
866                let data = Rc::clone(
867                    self.enums
868                        .get(enum_name)
869                        .expect("an enum value implies a registered enum"),
870                );
871                let has_wildcard = arms
872                    .iter()
873                    .any(|a| matches!(a.pattern, Pattern::Wildcard(_)));
874
875                // Every arm of an enum match must name a real case (or be `_`).
876                for a in arms {
877                    match &a.pattern {
878                        Pattern::Variant {
879                            name, span: psp, ..
880                        } => {
881                            if !data.variants.iter().any(|vd| &vd.name == name) {
882                                return Err(LuxError::new(
883                                    format!("enum `{}` has no case `{}`", enum_name, name),
884                                    *psp,
885                                )
886                                .with_note(format!("cases are: {}", variant_names(&data)))
887                                .with_learn(
888                                    "match",
889                                    "every arm names one of the enum's real cases",
890                                ));
891                            }
892                        }
893                        Pattern::Wildcard(_) => {}
894                        other => {
895                            return Err(LuxError::new(
896                                format!(
897                                    "this matches on enum `{}`, so each arm must be a case name or `_`",
898                                    enum_name
899                                ),
900                                other.span(),
901                            )
902                            .with_learn("match", "each arm is one case you handle"));
903                        }
904                    }
905                }
906
907                // Exhaustiveness: without a `_`, every case must be handled.
908                if !has_wildcard {
909                    let missing: Vec<String> = data
910                        .variants
911                        .iter()
912                        .filter(|vd| {
913                            !arms.iter().any(|a| {
914                                matches!(&a.pattern, Pattern::Variant { name, .. } if name == &vd.name)
915                            })
916                        })
917                        .map(|vd| vd.name.clone())
918                        .collect();
919                    if !missing.is_empty() {
920                        return Err(LuxError::new(
921                            format!("this match on `{}` doesn't handle every case", enum_name),
922                            span,
923                        )
924                        .with_learn("match", "covering every case is what makes match safe")
925                        .with_note(format!(
926                            "add an arm for: {} (or a `_` catch-all)",
927                            missing.join(", ")
928                        )));
929                    }
930                }
931
932                // Run the first arm that fits, top to bottom.
933                for a in arms {
934                    match &a.pattern {
935                        Pattern::Variant {
936                            name,
937                            bindings,
938                            span: psp,
939                        } if name == variant => {
940                            if bindings.len() != fields.len() {
941                                return Err(LuxError::new(
942                                    format!(
943                                        "case `{}` carries {}, but the pattern captures {}",
944                                        name,
945                                        count(fields.len(), "value"),
946                                        bindings.len()
947                                    ),
948                                    *psp,
949                                )
950                                .with_learn(
951                                    "match",
952                                    "a pattern binds the values its case carries",
953                                ));
954                            }
955                            self.push();
956                            for (b, (_, val)) in bindings.iter().zip(fields.iter()) {
957                                self.declare(b.clone(), val.clone(), false);
958                            }
959                            let r = self.eval(&a.body);
960                            self.pop();
961                            return r;
962                        }
963                        Pattern::Wildcard(_) => return self.eval(&a.body),
964                        _ => continue,
965                    }
966                }
967                unreachable!("exhaustiveness guarantees a matching arm")
968            }
969            other => self.match_value(other, arms, scrutinee.span(), span),
970        }
971    }
972
973    /// Match a plain value (int, string, bool) against literal patterns. These
974    /// domains are open, so a `_` catch-all is required.
975    fn match_value(
976        &mut self,
977        v: &Value,
978        arms: &[MatchArm],
979        scrutinee_span: Span,
980        span: Span,
981    ) -> Result<Value, LuxError> {
982        if !matches!(v, Value::Int(_) | Value::Str(_) | Value::Bool(_)) {
983            return Err(LuxError::new(
984                format!(
985                    "cannot match on {}; match works on enums, int, string, and bool",
986                    value_type(v)
987                ),
988                scrutinee_span,
989            )
990            .with_learn(
991                "match",
992                "match takes apart an enum or a plain int, string, or bool",
993            ));
994        }
995        // A value match needs a `_`, since int and string have endless values.
996        // bool is the exception: `true` and `false` together cover everything.
997        let has_wildcard = arms
998            .iter()
999            .any(|a| matches!(a.pattern, Pattern::Wildcard(_)));
1000        let bool_exhaustive = matches!(v, Value::Bool(_))
1001            && arms
1002                .iter()
1003                .any(|a| matches!(a.pattern, Pattern::Bool(true, _)))
1004            && arms
1005                .iter()
1006                .any(|a| matches!(a.pattern, Pattern::Bool(false, _)));
1007        if !has_wildcard && !bool_exhaustive {
1008            return Err(LuxError::new(
1009                format!("this match on {} needs a `_` case", value_type(v)),
1010                span,
1011            )
1012            .with_note("matching a value (not an enum) can't be exhaustive, so add `_ => ...`")
1013            .with_learn(
1014                "match",
1015                "`_` is the catch-all that covers every other value",
1016            ));
1017        }
1018        for a in arms {
1019            let fits = match (&a.pattern, v) {
1020                (Pattern::Wildcard(_), _) => true,
1021                (Pattern::Int(n, _), Value::Int(m)) => n == m,
1022                (Pattern::Str(s, _), Value::Str(t)) => s == t,
1023                (Pattern::Bool(b, _), Value::Bool(c)) => b == c,
1024                (Pattern::Variant { span: psp, .. }, _) => {
1025                    return Err(LuxError::new(
1026                        format!("this is {}, not an enum, so it has no cases", value_type(v)),
1027                        *psp,
1028                    )
1029                    .with_learn("enums", "only an enum has named cases to match"));
1030                }
1031                _ => false,
1032            };
1033            if fits {
1034                return self.eval(&a.body);
1035            }
1036        }
1037        unreachable!("the required `_` arm guarantees a match")
1038    }
1039
1040    fn eval_bool(&mut self, e: &Expr) -> Result<bool, LuxError> {
1041        match self.eval(e)? {
1042            Value::Bool(b) => Ok(b),
1043            other => Err(LuxError::new(
1044                format!(
1045                    "expected a true/false value, but this is {}",
1046                    named(other.type_name())
1047                ),
1048                e.span(),
1049            )
1050            .with_note("conditions and &&/|| operands must be bool")
1051            .with_learn("booleans", "if, while, and and/or all run on true or false")),
1052        }
1053    }
1054
1055    fn call(&mut self, name: &str, args: &[Expr], span: Span) -> Result<Value, LuxError> {
1056        match name {
1057            "print" => {
1058                let mut parts = Vec::with_capacity(args.len());
1059                for a in args {
1060                    parts.push(display(&self.eval(a)?));
1061                }
1062                println!("{}", parts.join(" "));
1063                Ok(Value::Unit)
1064            }
1065            "string" => {
1066                let v = self.one_arg(name, args, span)?;
1067                Ok(Value::Str(display(&v)))
1068            }
1069            "int" => {
1070                let v = self.one_arg(name, args, span)?;
1071                match v {
1072                    Value::Int(_) => Ok(v),
1073                    Value::Float(f) => Ok(Value::Int(f as i64)),
1074                    Value::Str(_) => Err(LuxError::new(
1075                        "int converts between numbers, not from text".to_string(),
1076                        span,
1077                    )
1078                    .with_note("to read a number from text use parseInt, which hands back an Option you match on")
1079                    .with_learn("conversions", "parseInt reads a number from text and gives back an Option")),
1080                    other => Err(LuxError::new(
1081                        format!("cannot convert {} to an int", named(other.type_name())),
1082                        span,
1083                    )),
1084                }
1085            }
1086            "float" => {
1087                let v = self.one_arg(name, args, span)?;
1088                match v {
1089                    Value::Float(_) => Ok(v),
1090                    Value::Int(n) => Ok(Value::Float(n as f64)),
1091                    Value::Str(_) => Err(LuxError::new(
1092                        "float converts between numbers, not from text".to_string(),
1093                        span,
1094                    )
1095                    .with_note("to read a number from text use parseFloat, which hands back an Option you match on")
1096                    .with_learn("conversions", "parseFloat reads a number from text and gives back an Option")),
1097                    other => Err(LuxError::new(
1098                        format!("cannot convert {} to a float", named(other.type_name())),
1099                        span,
1100                    )),
1101                }
1102            }
1103            "length" => {
1104                let v = self.one_arg(name, args, span)?;
1105                match v {
1106                    Value::Array(items) => Ok(Value::Int(items.len() as i64)),
1107                    // Count characters, not bytes, so length("café") is 4.
1108                    Value::Str(s) => Ok(Value::Int(s.chars().count() as i64)),
1109                    other => Err(LuxError::new(
1110                        format!(
1111                            "length expects an array or a string, but got {}",
1112                            value_type(&other)
1113                        ),
1114                        span,
1115                    )
1116                    .with_learn(
1117                        "arrays",
1118                        "length counts an array's items or a string's characters",
1119                    )),
1120                }
1121            }
1122            // ----- the outside world: files, arguments, the standard streams ---
1123            // Reading and writing can fail (a missing file, an unwritable path),
1124            // so they hand the failure back as a value — `Result` — instead of
1125            // crashing. The program decides what to do about it.
1126            "readFile" => {
1127                let path = self.one_str(name, args, span)?;
1128                match std::fs::read_to_string(&path) {
1129                    Ok(contents) => Ok(result_ok(Value::Str(contents))),
1130                    Err(e) => Ok(result_err(Value::Str(format!(
1131                        "could not read {}: {}",
1132                        path, e
1133                    )))),
1134                }
1135            }
1136            // Success here carries nothing — there's no value to hand back, only
1137            // the fact that it worked. That's `Result<Unit, string>`: you still
1138            // have to confirm it didn't fail, even when success is empty.
1139            "writeFile" => {
1140                let (path, contents) = self.two_str(name, args, span)?;
1141                match std::fs::write(&path, &contents) {
1142                    Ok(()) => Ok(result_ok(Value::Unit)),
1143                    Err(e) => Ok(result_err(Value::Str(format!(
1144                        "could not write {}: {}",
1145                        path, e
1146                    )))),
1147                }
1148            }
1149            // The command-line arguments, program name first — args()[0] is the
1150            // program itself, the way every shell hands it over.
1151            "args" => {
1152                self.no_args(name, args, span)?;
1153                let items = self.program_args.iter().cloned().map(Value::Str).collect();
1154                Ok(Value::Array(items))
1155            }
1156            // One line from standard input, with its newline removed. `none` at
1157            // end of input — so a loop reading until `none` works the same whether
1158            // it's a person typing or a file piped in.
1159            "readLine" => {
1160                self.no_args(name, args, span)?;
1161                let mut line = String::new();
1162                match std::io::stdin().read_line(&mut line) {
1163                    Ok(0) | Err(_) => Ok(option_none()),
1164                    Ok(_) => {
1165                        let text = line.strip_suffix('\n').unwrap_or(&line);
1166                        let text = text.strip_suffix('\r').unwrap_or(text);
1167                        Ok(option_some(Value::Str(text.to_string())))
1168                    }
1169                }
1170            }
1171            // Like `print`, but to standard error — the stream for diagnostics,
1172            // kept separate so the real output on stdout stays clean for whatever
1173            // program reads it next.
1174            "eprint" => {
1175                let mut parts = Vec::with_capacity(args.len());
1176                for a in args {
1177                    parts.push(display(&self.eval(a)?));
1178                }
1179                eprintln!("{}", parts.join(" "));
1180                Ok(Value::Unit)
1181            }
1182            // Run another program and capture what it produced. Two layers of
1183            // truth: the `Result` says whether it *launched* (the program might
1184            // not exist), and the `status` inside says whether the command itself
1185            // *succeeded* (it can run fine and still report failure with a
1186            // non-zero code). The arguments are a list, never a shell string, so
1187            // there is no shell to inject into. The child's input is empty.
1188            "run" => {
1189                let (program, arg_list) = self.program_and_args(name, args, span)?;
1190                match std::process::Command::new(&program)
1191                    .args(&arg_list)
1192                    .stdin(std::process::Stdio::null())
1193                    .output()
1194                {
1195                    Ok(out) => {
1196                        let status = out.status.code().unwrap_or(-1) as i64;
1197                        let stdout = String::from_utf8_lossy(&out.stdout).into_owned();
1198                        let stderr = String::from_utf8_lossy(&out.stderr).into_owned();
1199                        Ok(result_ok(output_value(status, stdout, stderr)))
1200                    }
1201                    Err(e) => Ok(result_err(Value::Str(format!(
1202                        "could not run {}: {}",
1203                        program, e
1204                    )))),
1205                }
1206            }
1207            // Reading a number from text can fail — the text might not be a
1208            // number — so unlike int/float these hand back an Option, never a
1209            // crash. `none` is the "that wasn't a number" answer.
1210            "parseInt" => match self.one_arg(name, args, span)? {
1211                Value::Str(s) => Ok(match s.trim().parse::<i64>() {
1212                    Ok(n) => option_some(Value::Int(n)),
1213                    Err(_) => option_none(),
1214                }),
1215                other => Err(LuxError::new(
1216                    format!("parseInt reads text, but got {}", named(other.type_name())),
1217                    span,
1218                )),
1219            },
1220            "parseFloat" => match self.one_arg(name, args, span)? {
1221                Value::Str(s) => Ok(match s.trim().parse::<f64>() {
1222                    Ok(f) => option_some(Value::Float(f)),
1223                    Err(_) => option_none(),
1224                }),
1225                other => Err(LuxError::new(
1226                    format!("parseFloat reads text, but got {}", named(other.type_name())),
1227                    span,
1228                )),
1229            },
1230            // The built-in enum constructors. `none` has no value, so it's a
1231            // bare name handled in `eval`, not a call.
1232            "some" => Ok(option_some(self.one_arg(name, args, span)?)),
1233            "ok" => Ok(result_ok(self.one_arg(name, args, span)?)),
1234            "err" => Ok(result_err(self.one_arg(name, args, span)?)),
1235            _ => self.call_user(name, args, span),
1236        }
1237    }
1238
1239    fn one_arg(&mut self, name: &str, args: &[Expr], span: Span) -> Result<Value, LuxError> {
1240        if args.len() != 1 {
1241            return Err(LuxError::new(
1242                format!("{} takes exactly one value, but got {}", name, args.len()),
1243                span,
1244            ));
1245        }
1246        self.eval(&args[0])
1247    }
1248
1249    /// A built-in that takes exactly one string, like `readFile(path)`.
1250    fn one_str(&mut self, name: &str, args: &[Expr], span: Span) -> Result<String, LuxError> {
1251        match self.one_arg(name, args, span)? {
1252            Value::Str(s) => Ok(s),
1253            other => Err(LuxError::new(
1254                format!("{} expects a string, but got {}", name, value_type(&other)),
1255                span,
1256            )),
1257        }
1258    }
1259
1260    /// A built-in that takes two strings, like `writeFile(path, contents)`.
1261    fn two_str(
1262        &mut self,
1263        name: &str,
1264        args: &[Expr],
1265        span: Span,
1266    ) -> Result<(String, String), LuxError> {
1267        if args.len() != 2 {
1268            return Err(LuxError::new(
1269                format!("{} takes exactly two values, but got {}", name, args.len()),
1270                span,
1271            ));
1272        }
1273        let first = self.eval(&args[0])?;
1274        let second = self.eval(&args[1])?;
1275        let path = match first {
1276            Value::Str(s) => s,
1277            other => {
1278                return Err(LuxError::new(
1279                    format!(
1280                        "{} expects the path as a string, but got {}",
1281                        name,
1282                        value_type(&other)
1283                    ),
1284                    span,
1285                ));
1286            }
1287        };
1288        let contents = match second {
1289            Value::Str(s) => s,
1290            other => {
1291                return Err(LuxError::new(
1292                    format!(
1293                        "{} expects the contents as a string, but got {}",
1294                        name,
1295                        value_type(&other)
1296                    ),
1297                    span,
1298                ));
1299            }
1300        };
1301        Ok((path, contents))
1302    }
1303
1304    /// A built-in that takes no values, like `args()` or `readLine()`.
1305    fn no_args(&self, name: &str, args: &[Expr], span: Span) -> Result<(), LuxError> {
1306        if !args.is_empty() {
1307            return Err(LuxError::new(
1308                format!("{} takes no values, but got {}", name, args.len()),
1309                span,
1310            ));
1311        }
1312        Ok(())
1313    }
1314
1315    /// `run`'s arguments: a program name and a list of string arguments. The
1316    /// arg-vector shape is deliberate — there is no shell string to inject into.
1317    fn program_and_args(
1318        &mut self,
1319        name: &str,
1320        args: &[Expr],
1321        span: Span,
1322    ) -> Result<(String, Vec<String>), LuxError> {
1323        if args.len() != 2 {
1324            return Err(LuxError::new(
1325                format!(
1326                    "{} takes a program name and a list of arguments, but got {}",
1327                    name,
1328                    args.len()
1329                ),
1330                span,
1331            ));
1332        }
1333        let program = match self.eval(&args[0])? {
1334            Value::Str(s) => s,
1335            other => {
1336                return Err(LuxError::new(
1337                    format!(
1338                        "{} expects the program name as a string, but got {}",
1339                        name,
1340                        value_type(&other)
1341                    ),
1342                    span,
1343                ));
1344            }
1345        };
1346        let arg_list = match self.eval(&args[1])? {
1347            Value::Array(items) => {
1348                let mut out = Vec::with_capacity(items.len());
1349                for it in items {
1350                    match it {
1351                        Value::Str(s) => out.push(s),
1352                        other => {
1353                            return Err(LuxError::new(
1354                                format!(
1355                                    "{} expects the arguments as a list of strings, but one was {}",
1356                                    name,
1357                                    value_type(&other)
1358                                ),
1359                                span,
1360                            ));
1361                        }
1362                    }
1363                }
1364                out
1365            }
1366            other => {
1367                return Err(LuxError::new(
1368                    format!(
1369                        "{} expects the arguments as a list of strings, but got {}",
1370                        name,
1371                        value_type(&other)
1372                    ),
1373                    span,
1374                ));
1375            }
1376        };
1377        Ok((program, arg_list))
1378    }
1379
1380    /// Call a user-defined function. Arguments are checked against the declared
1381    /// parameter types (no coercion), the body runs in its own fresh scope —
1382    /// it sees its parameters and other functions, but not the caller's
1383    /// locals — and the returned value is checked against the declared return
1384    /// type.
1385    fn call_user(&mut self, name: &str, args: &[Expr], span: Span) -> Result<Value, LuxError> {
1386        let func = match self.funcs.get(name) {
1387            Some(f) => Rc::clone(f),
1388            None => {
1389                return Err(LuxError::new(format!("unknown function `{}`", name), span).with_note(
1390                    "define it with `func`, or use a built-in: print, eprint, string, int, float, length, readFile, writeFile, readLine, args, run",
1391                )
1392                .with_learn("functions", "a function takes values in and hands one result back"));
1393            }
1394        };
1395
1396        if args.len() != func.params.len() {
1397            return Err(LuxError::new(
1398                format!(
1399                    "function `{}` expects {} but got {}",
1400                    name,
1401                    count(func.params.len(), "value"),
1402                    args.len()
1403                ),
1404                span,
1405            )
1406            .with_learn(
1407                "functions",
1408                "a function takes exactly the parameters it declares",
1409            ));
1410        }
1411
1412        // Evaluate and type-check the arguments in the caller's scope.
1413        let mut frame = HashMap::new();
1414        for (param, arg) in func.params.iter().zip(args) {
1415            let v = self.eval(arg)?;
1416            self.validate_type(&param.ty)?;
1417            if !self.type_matches(&param.ty, &v) {
1418                return Err(LuxError::new(
1419                    format!(
1420                        "`{}` expects `{}` to be {}, but got {}",
1421                        name,
1422                        param.name,
1423                        describe_type(&param.ty),
1424                        value_type(&v)
1425                    ),
1426                    arg.span(),
1427                )
1428                .with_learn("functions", "each parameter has a type the call must match"));
1429            }
1430            frame.insert(
1431                param.name.clone(),
1432                Binding {
1433                    value: v,
1434                    mutable: false,
1435                },
1436            );
1437        }
1438
1439        // Run the body with a scope stack of just this call's frame, then put
1440        // the caller's scopes back — even if the body errored.
1441        let saved = std::mem::replace(&mut self.scopes, vec![frame]);
1442        let outcome = self.exec_block(&func.body);
1443        self.scopes = saved;
1444        let returned = match outcome? {
1445            Flow::Return(v) => v,
1446            Flow::Normal => Value::Unit,
1447        };
1448
1449        match &func.ret {
1450            Some(ann) => {
1451                self.validate_type(ann)?;
1452                if matches!(returned, Value::Unit) {
1453                    return Err(LuxError::new(
1454                        format!(
1455                            "`{}` must return {}, but it ended without returning a value",
1456                            name,
1457                            describe_type(ann)
1458                        ),
1459                        span,
1460                    )
1461                    .with_learn("functions", "a `-> type` is a promise to hand that back"));
1462                }
1463                if !self.type_matches(ann, &returned) {
1464                    return Err(LuxError::new(
1465                        format!(
1466                            "`{}` should return {}, but returned {}",
1467                            name,
1468                            describe_type(ann),
1469                            value_type(&returned)
1470                        ),
1471                        span,
1472                    )
1473                    .with_learn("functions", "what comes back must match the `-> type`"));
1474                }
1475                Ok(returned)
1476            }
1477            None => {
1478                if !matches!(returned, Value::Unit) {
1479                    return Err(LuxError::new(
1480                        format!("`{}` has no return type, so it can't return a value", name),
1481                        span,
1482                    )
1483                    .with_note("add `-> type` to the signature if it should return something")
1484                    .with_learn(
1485                        "functions",
1486                        "no `-> type` means the function returns nothing",
1487                    ));
1488                }
1489                Ok(Value::Unit)
1490            }
1491        }
1492    }
1493
1494    // ----- types: annotations, matching, and zero values --------------------
1495
1496    /// Check that every name in an annotation is a real type: a built-in, or a
1497    /// struct or enum the program declares.
1498    fn validate_type(&self, ann: &TypeAnn) -> Result<(), LuxError> {
1499        match &ann.kind {
1500            TypeKind::Named(n) => {
1501                // Option and Result are real types, but they always need their
1502                // parameters — `Option` alone says nothing about what it holds.
1503                if matches!(n.as_str(), "Option" | "Result") {
1504                    let hint = if n == "Option" {
1505                        "write `Option<int>`"
1506                    } else {
1507                        "write `Result<int, string>`"
1508                    };
1509                    return Err(LuxError::new(
1510                        format!("`{}` needs a type in angle brackets", n),
1511                        ann.span,
1512                    )
1513                    .with_note(hint)
1514                    .with_learn(
1515                        "option",
1516                        "Option and Result say what they hold, like Option<int>",
1517                    ));
1518                }
1519                // `Unit` is the type of "nothing" — the success of a `writeFile`
1520                // that worked but has no value to return, as in `Result<Unit, string>`.
1521                if matches!(n.as_str(), "int" | "float" | "string" | "bool" | "Unit")
1522                    || self.type_exists(n)
1523                {
1524                    Ok(())
1525                } else {
1526                    Err(LuxError::new(format!("unknown type `{}`", n), ann.span).with_note(
1527                        "known types: int, float, string, bool, Unit, arrays like [int], and any struct or enum you define",
1528                    ))
1529                }
1530            }
1531            TypeKind::Array(elem) => self.validate_type(elem),
1532            TypeKind::Generic(name, args) => match name.as_str() {
1533                "Option" => {
1534                    if args.len() != 1 {
1535                        return Err(LuxError::new(
1536                            format!("`Option` takes one type, but got {}", args.len()),
1537                            ann.span,
1538                        )
1539                        .with_note("write `Option<int>`")
1540                        .with_learn(
1541                            "option",
1542                            "an Option holds one type — what's there when it's not none",
1543                        ));
1544                    }
1545                    self.validate_type(&args[0])
1546                }
1547                "Result" => {
1548                    if args.len() != 2 {
1549                        return Err(LuxError::new(
1550                            format!("`Result` takes two types, but got {}", args.len()),
1551                            ann.span,
1552                        )
1553                        .with_note(
1554                            "write `Result<int, string>` — the value type and the error type",
1555                        )
1556                        .with_learn(
1557                            "result",
1558                            "a Result holds two types: the value and the error",
1559                        ));
1560                    }
1561                    self.validate_type(&args[0])?;
1562                    self.validate_type(&args[1])
1563                }
1564                _ => Err(LuxError::new(
1565                    format!("`{}` is not a parameterized type", name),
1566                    ann.span,
1567                )
1568                .with_note("only Option and Result take a type in angle brackets, like Option<int>")
1569                .with_learn("option", "some(x) or none — a missing value with no null")),
1570            },
1571        }
1572    }
1573
1574    /// Does a runtime value satisfy a type annotation? Assumes the annotation's
1575    /// names are already known (see `validate_type`). An empty array satisfies
1576    /// any array type, since it has no elements to disagree.
1577    fn type_matches(&self, ann: &TypeAnn, v: &Value) -> bool {
1578        match (&ann.kind, v) {
1579            (TypeKind::Named(n), Value::Struct { name, .. }) => n == name,
1580            (TypeKind::Named(n), Value::Enum { enum_name, .. }) => n == enum_name,
1581            // The value prints as "nothing"; the type that describes it is `Unit`.
1582            (TypeKind::Named(n), Value::Unit) => n == "Unit",
1583            (TypeKind::Named(n), _) => n == v.type_name(),
1584            (TypeKind::Array(elem), Value::Array(items)) => {
1585                items.iter().all(|it| self.type_matches(elem, it))
1586            }
1587            // A generic annotation matches a built-in enum value when the value's
1588            // case fits and the payload it carries matches the right parameter.
1589            // `none` carries nothing, so it satisfies any `Option<T>` — the same
1590            // way an empty array satisfies any array type.
1591            (
1592                TypeKind::Generic(name, args),
1593                Value::Enum {
1594                    enum_name,
1595                    variant,
1596                    fields,
1597                },
1598            ) if name == enum_name => match (name.as_str(), variant.as_str()) {
1599                ("Option", "none") => true,
1600                ("Option", "some") => self.payload_matches(&args[0], fields),
1601                ("Result", "ok") => self.payload_matches(&args[0], fields),
1602                ("Result", "err") => self.payload_matches(&args[1], fields),
1603                _ => false,
1604            },
1605            _ => false,
1606        }
1607    }
1608
1609    /// Does a built-in enum's single carried value match the given type?
1610    fn payload_matches(&self, ann: &TypeAnn, fields: &[(String, Value)]) -> bool {
1611        match fields.first() {
1612            Some((_, v)) => self.type_matches(ann, v),
1613            None => false,
1614        }
1615    }
1616
1617    /// Validate an annotation and confirm a value matches it. Used by `let`/`var`
1618    /// type annotations, where the annotation is the thing to blame.
1619    fn check_type(&self, ann: &TypeAnn, v: &Value) -> Result<(), LuxError> {
1620        self.validate_type(ann)?;
1621        if !self.type_matches(ann, v) {
1622            return Err(LuxError::new(
1623                format!(
1624                    "type mismatch: annotated `{}` but the value is {}",
1625                    describe_type(ann),
1626                    value_type(v)
1627                ),
1628                ann.span,
1629            ));
1630        }
1631        Ok(())
1632    }
1633
1634    /// The starting value for a `var` declared with a type but no initializer.
1635    /// Structs and enums have no obvious zero, so they need an explicit value.
1636    fn zero_value(&self, ann: &TypeAnn) -> Result<Value, LuxError> {
1637        match &ann.kind {
1638            TypeKind::Named(n) => match n.as_str() {
1639                "int" => Ok(Value::Int(0)),
1640                "float" => Ok(Value::Float(0.0)),
1641                "string" => Ok(Value::Str(String::new())),
1642                "bool" => Ok(Value::Bool(false)),
1643                _ if self.type_exists(n) => Err(LuxError::new(
1644                    format!("a `var` of type `{}` needs a starting value", n),
1645                    ann.span,
1646                )
1647                .with_note(format!("write `var x = {}(...)`", n))),
1648                _ => Err(LuxError::new(format!("unknown type `{}`", n), ann.span)
1649                    .with_note("lux has int, float, string, bool, and arrays like [int]")),
1650            },
1651            TypeKind::Array(elem) => {
1652                self.validate_type(elem)?;
1653                Ok(Value::Array(Vec::new()))
1654            }
1655            TypeKind::Generic(name, _) => {
1656                self.validate_type(ann)?;
1657                if name == "Option" {
1658                    // The natural empty Option is `none`.
1659                    Ok(option_none())
1660                } else {
1661                    Err(LuxError::new(
1662                        format!(
1663                            "a `var` of type `{}` needs a starting value",
1664                            describe_type(ann)
1665                        ),
1666                        ann.span,
1667                    )
1668                    .with_note("a Result is either ok(...) or err(...) — there's no empty one")
1669                    .with_learn("result", "ok(x) or err(why) — failure as plain data"))
1670                }
1671            }
1672        }
1673    }
1674}
1675
1676// ----- operators (free functions: pure value -> value) ----------------------
1677
1678fn unary(op: UnOp, v: Value, span: Span) -> Result<Value, LuxError> {
1679    match (op, v) {
1680        (UnOp::Neg, Value::Int(n)) => Ok(Value::Int(-n)),
1681        (UnOp::Neg, Value::Float(f)) => Ok(Value::Float(-f)),
1682        (UnOp::Neg, other) => Err(LuxError::new(
1683            format!("cannot negate {}", named(other.type_name())),
1684            span,
1685        )),
1686        (UnOp::Not, Value::Bool(b)) => Ok(Value::Bool(!b)),
1687        (UnOp::Not, other) => Err(LuxError::new(
1688            format!("cannot apply ! to {}", named(other.type_name())),
1689            span,
1690        )
1691        .with_note("! works on bool values")
1692        .with_learn("booleans", "! flips true to false and back")),
1693    }
1694}
1695
1696fn binary_op(op: BinOp, a: &Value, b: &Value, span: Span) -> Result<Value, LuxError> {
1697    match op {
1698        BinOp::Add => add(a, b, span),
1699        BinOp::Sub => sub(a, b, span),
1700        BinOp::Mul => mul(a, b, span),
1701        BinOp::Div => div(a, b, span),
1702        BinOp::Mod => modulo(a, b, span),
1703        BinOp::Eq => equality(a, b, span, false),
1704        BinOp::Ne => equality(a, b, span, true),
1705        BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge => ordering(op, a, b, span),
1706        BinOp::And | BinOp::Or => unreachable!("&& and || are handled in eval"),
1707    }
1708}
1709
1710/// A type name with its article, like "an int" or "a string", so error
1711/// messages read as proper English.
1712fn named(type_name: &str) -> String {
1713    let article = match type_name.chars().next() {
1714        Some('a' | 'e' | 'i' | 'o' | 'u') => "an",
1715        _ => "a",
1716    };
1717    format!("{} {}", article, type_name)
1718}
1719
1720/// How a value's type reads in a message about types: scalars by name, arrays
1721/// as `[int]` so the element type shows. No article, so it composes next to a
1722/// `describe_type` annotation.
1723fn value_type(v: &Value) -> String {
1724    match v {
1725        Value::Array(items) => match items.first() {
1726            Some(first) => format!("[{}]", value_type(first)),
1727            None => "[]".to_string(),
1728        },
1729        Value::Struct { name, .. } => name.clone(),
1730        // For the built-in generics, show what's known about the parameters and
1731        // leave the unknown ones as `?`: `none` is `Option<?>`, `ok(5)` is
1732        // `Result<int, ?>`.
1733        Value::Enum {
1734            enum_name,
1735            variant,
1736            fields,
1737        } if enum_name == "Option" => match (variant.as_str(), fields.first()) {
1738            ("some", Some((_, v))) => format!("Option<{}>", value_type(v)),
1739            _ => "Option<?>".to_string(),
1740        },
1741        Value::Enum {
1742            enum_name,
1743            variant,
1744            fields,
1745        } if enum_name == "Result" => match (variant.as_str(), fields.first()) {
1746            ("ok", Some((_, v))) => format!("Result<{}, ?>", value_type(v)),
1747            ("err", Some((_, v))) => format!("Result<?, {}>", value_type(v)),
1748            _ => "Result".to_string(),
1749        },
1750        Value::Enum { enum_name, .. } => enum_name.clone(),
1751        other => other.type_name().to_string(),
1752    }
1753}
1754
1755/// `+=` does two different jobs: it appends to an array, or adds two scalars.
1756fn append_or_add(current: Value, new: Value, span: Span) -> Result<Value, LuxError> {
1757    match current {
1758        Value::Array(mut items) => {
1759            if let Some(first) = items.first() {
1760                if !same_type(first, &new) {
1761                    return Err(LuxError::new(
1762                        format!(
1763                            "cannot add {} to an array of {}",
1764                            value_type(&new),
1765                            value_type(first)
1766                        ),
1767                        span,
1768                    )
1769                    .with_learn("arrays", "an array holds one type, so += has to match it"));
1770                }
1771            }
1772            items.push(new);
1773            Ok(Value::Array(items))
1774        }
1775        scalar => add(&scalar, &new, span),
1776    }
1777}
1778
1779fn add(a: &Value, b: &Value, span: Span) -> Result<Value, LuxError> {
1780    match (a, b) {
1781        (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x + y)),
1782        (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)),
1783        (Value::Str(x), Value::Str(y)) => Ok(Value::Str(format!("{}{}", x, y))),
1784        _ => Err(mix_or_type_error("add", a, b, span)),
1785    }
1786}
1787
1788fn sub(a: &Value, b: &Value, span: Span) -> Result<Value, LuxError> {
1789    match (a, b) {
1790        (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x - y)),
1791        (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x - y)),
1792        _ => Err(mix_or_type_error("subtract", a, b, span)),
1793    }
1794}
1795
1796fn mul(a: &Value, b: &Value, span: Span) -> Result<Value, LuxError> {
1797    match (a, b) {
1798        (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)),
1799        (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * y)),
1800        _ => Err(mix_or_type_error("multiply", a, b, span)),
1801    }
1802}
1803
1804fn div(a: &Value, b: &Value, span: Span) -> Result<Value, LuxError> {
1805    match (a, b) {
1806        (Value::Int(_), Value::Int(0)) => Err(LuxError::new("division by zero", span)),
1807        (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x / y)),
1808        (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x / y)),
1809        _ => Err(mix_or_type_error("divide", a, b, span)),
1810    }
1811}
1812
1813fn modulo(a: &Value, b: &Value, span: Span) -> Result<Value, LuxError> {
1814    match (a, b) {
1815        (Value::Int(_), Value::Int(0)) => Err(LuxError::new("remainder by zero", span)),
1816        (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x % y)),
1817        _ => Err(LuxError::new(
1818            format!(
1819                "% needs two ints, but got {} and {}",
1820                named(a.type_name()),
1821                named(b.type_name())
1822            ),
1823            span,
1824        )),
1825    }
1826}
1827
1828fn equality(a: &Value, b: &Value, span: Span, negate: bool) -> Result<Value, LuxError> {
1829    if !same_type(a, b) {
1830        return Err(LuxError::new(
1831            format!("cannot compare {} with {}", value_type(a), value_type(b)),
1832            span,
1833        )
1834        .with_note("both sides of == and != must be the same type"));
1835    }
1836    let eq = a == b;
1837    Ok(Value::Bool(if negate { !eq } else { eq }))
1838}
1839
1840fn ordering(op: BinOp, a: &Value, b: &Value, span: Span) -> Result<Value, LuxError> {
1841    use std::cmp::Ordering;
1842    let ord: Ordering = match (a, b) {
1843        (Value::Int(x), Value::Int(y)) => x.cmp(y),
1844        (Value::Float(x), Value::Float(y)) => x
1845            .partial_cmp(y)
1846            .ok_or_else(|| LuxError::new("cannot compare with NaN", span))?,
1847        (Value::Str(x), Value::Str(y)) => x.cmp(y),
1848        (Value::Bool(_), Value::Bool(_)) => {
1849            return Err(LuxError::new("cannot order bool values with < or >", span)
1850                .with_note("use == or != to compare bools")
1851                .with_learn(
1852                    "booleans",
1853                    "true and false aren't ordered, only equal or not",
1854                ));
1855        }
1856        _ => {
1857            return Err(LuxError::new(
1858                format!(
1859                    "cannot compare {} with {}",
1860                    named(a.type_name()),
1861                    named(b.type_name())
1862                ),
1863                span,
1864            )
1865            .with_note("both sides must be the same type"));
1866        }
1867    };
1868    let result = match op {
1869        BinOp::Lt => ord == Ordering::Less,
1870        BinOp::Gt => ord == Ordering::Greater,
1871        BinOp::Le => ord != Ordering::Greater,
1872        BinOp::Ge => ord != Ordering::Less,
1873        _ => unreachable!(),
1874    };
1875    Ok(Value::Bool(result))
1876}
1877
1878/// Shared error for arithmetic: distinguishes "you mixed int and float" from a
1879/// plain type mismatch, because mixing is the common beginner mistake.
1880fn mix_or_type_error(verb: &str, a: &Value, b: &Value, span: Span) -> LuxError {
1881    let mixed = matches!(
1882        (a, b),
1883        (Value::Int(_), Value::Float(_)) | (Value::Float(_), Value::Int(_))
1884    );
1885    if mixed {
1886        LuxError::new("cannot mix int and float — convert one first", span)
1887            .with_note("wrap a value in float(...) or int(...)")
1888            .with_learn(
1889                "numbers",
1890                "there's a reason lux makes you say when a whole number becomes a fraction",
1891            )
1892    } else {
1893        // A string on either side of `+` is the classic "glue text to a number"
1894        // mistake, which the strings topic answers with `string(...)`; other
1895        // type mismatches are arithmetic, so they point at numbers.
1896        let (topic, lure) = if matches!(a, Value::Str(_)) || matches!(b, Value::Str(_)) {
1897            (
1898                "strings",
1899                "lux never turns a number into text for you — you ask",
1900            )
1901        } else {
1902            (
1903                "numbers",
1904                "arithmetic needs both sides to be the same number type",
1905            )
1906        };
1907        LuxError::new(
1908            format!(
1909                "cannot {} {} and {}",
1910                verb,
1911                named(a.type_name()),
1912                named(b.type_name())
1913            ),
1914            span,
1915        )
1916        .with_learn(topic, lure)
1917    }
1918}
1919
1920// ----- types: annotations, matching, and zero values ------------------------
1921
1922/// Render a type annotation the way the source wrote it: `int`, `[int]`.
1923fn describe_type(ann: &TypeAnn) -> String {
1924    match &ann.kind {
1925        TypeKind::Named(n) => n.clone(),
1926        TypeKind::Array(elem) => format!("[{}]", describe_type(elem)),
1927        TypeKind::Generic(name, args) => {
1928            let inner: Vec<String> = args.iter().map(describe_type).collect();
1929            format!("{}<{}>", name, inner.join(", "))
1930        }
1931    }
1932}
1933
1934/// Do two values share a type? For scalars this is just their type name; for
1935/// structs and enums it's the declared type name (a Point is not a Color); for
1936/// arrays it's the element type, with an empty array compatible with any.
1937fn same_type(a: &Value, b: &Value) -> bool {
1938    match (a, b) {
1939        (Value::Struct { name: x, .. }, Value::Struct { name: y, .. }) => x == y,
1940        (
1941            Value::Enum {
1942                enum_name: x,
1943                variant: vx,
1944                fields: fx,
1945            },
1946            Value::Enum {
1947                enum_name: y,
1948                variant: vy,
1949                fields: fy,
1950            },
1951        ) => {
1952            if x != y {
1953                return false;
1954            }
1955            // For the built-in generics, two values share a type only when their
1956            // known payloads agree. A `none` knows no payload, so it fits any
1957            // Option; an `ok` and an `err` constrain different parameters, so
1958            // they never conflict.
1959            match x.as_str() {
1960                "Option" => payloads_compatible(fx, fy),
1961                "Result" if vx == vy => payloads_compatible(fx, fy),
1962                "Result" => true,
1963                _ => true,
1964            }
1965        }
1966        (Value::Array(x), Value::Array(y)) => match (x.first(), y.first()) {
1967            (Some(a), Some(b)) => same_type(a, b),
1968            _ => true,
1969        },
1970        _ => a.type_name() == b.type_name(),
1971    }
1972}
1973
1974/// Do two built-in enum payloads agree? A missing payload (like `none`'s) is
1975/// compatible with anything, mirroring how an empty array fits any array type.
1976fn payloads_compatible(fx: &[(String, Value)], fy: &[(String, Value)]) -> bool {
1977    match (fx.first(), fy.first()) {
1978        (Some((_, a)), Some((_, b))) => same_type(a, b),
1979        _ => true,
1980    }
1981}
1982
1983// ----- the built-in generics: Option and Result -----------------------------
1984
1985/// The single field name carried inside a built-in enum value. It never shows
1986/// (these print positionally), and matching binds by position, so the name is
1987/// just internal bookkeeping.
1988const PAYLOAD: &str = "value";
1989
1990fn option_none() -> Value {
1991    Value::Enum {
1992        enum_name: "Option".to_string(),
1993        variant: "none".to_string(),
1994        fields: Vec::new(),
1995    }
1996}
1997
1998fn option_some(v: Value) -> Value {
1999    Value::Enum {
2000        enum_name: "Option".to_string(),
2001        variant: "some".to_string(),
2002        fields: vec![(PAYLOAD.to_string(), v)],
2003    }
2004}
2005
2006fn result_ok(v: Value) -> Value {
2007    Value::Enum {
2008        enum_name: "Result".to_string(),
2009        variant: "ok".to_string(),
2010        fields: vec![(PAYLOAD.to_string(), v)],
2011    }
2012}
2013
2014fn result_err(v: Value) -> Value {
2015    Value::Enum {
2016        enum_name: "Result".to_string(),
2017        variant: "err".to_string(),
2018        fields: vec![(PAYLOAD.to_string(), v)],
2019    }
2020}
2021
2022/// The `Output` struct `run` hands back: the exit status and the two captured
2023/// streams, in the order the built-in struct declares its fields.
2024fn output_value(status: i64, stdout: String, stderr: String) -> Value {
2025    Value::Struct {
2026        name: "Output".to_string(),
2027        fields: vec![
2028            ("status".to_string(), Value::Int(status)),
2029            ("stdout".to_string(), Value::Str(stdout)),
2030            ("stderr".to_string(), Value::Str(stderr)),
2031        ],
2032    }
2033}
2034
2035/// Reject a `let`/`var` with no annotation whose value can't pin its own type:
2036/// `none` could be any `Option`, an `ok`/`err` leaves Result's other half open.
2037/// lux can't infer what it won't run, so it asks for an annotation — the same
2038/// thing Rust and Swift do for a bare `None`/`nil`.
2039fn ensure_determined(v: &Value, span: Span) -> Result<(), LuxError> {
2040    if fully_determined(v) {
2041        Ok(())
2042    } else {
2043        Err(LuxError::new(
2044            format!(
2045                "can't tell what type this is — {} leaves it open",
2046                value_type(v)
2047            ),
2048            span,
2049        )
2050        .with_note("name the type, like `let x: Option<int> = none`")
2051        .with_learn(
2052            "option",
2053            "lux usually guesses the type, but an empty none needs you to say",
2054        ))
2055    }
2056}
2057
2058/// Is a value's full type knowable from the value alone? Everything is, except
2059/// a built-in generic that hasn't pinned all its parameters.
2060fn fully_determined(v: &Value) -> bool {
2061    match v {
2062        Value::Enum {
2063            enum_name,
2064            variant,
2065            fields,
2066        } if enum_name == "Option" => {
2067            variant == "some" && fields.first().is_some_and(|(_, x)| fully_determined(x))
2068        }
2069        // A Result value is always just one side, so the other parameter is
2070        // never known from the value — it needs an annotation.
2071        Value::Enum { enum_name, .. } if enum_name == "Result" => false,
2072        // One determined element fixes a homogeneous array; an empty array
2073        // stays as loose as it has always been.
2074        Value::Array(items) => items.is_empty() || items.iter().any(fully_determined),
2075        _ => true,
2076    }
2077}
2078
2079/// The comma-separated case names of an enum, for "did you mean" notes.
2080fn variant_names(data: &EnumData) -> String {
2081    data.variants
2082        .iter()
2083        .map(|v| v.name.clone())
2084        .collect::<Vec<_>>()
2085        .join(", ")
2086}
2087
2088/// The error for declaring two types with the same name.
2089fn already_defined(name: &str, span: Span) -> LuxError {
2090    LuxError::new(format!("type `{}` is already defined", name), span)
2091}
2092
2093/// "1 value" / "2 values" — small helper for argument-count errors.
2094fn count(n: usize, noun: &str) -> String {
2095    if n == 1 {
2096        format!("{} {}", n, noun)
2097    } else {
2098        format!("{} {}s", n, noun)
2099    }
2100}
2101
2102// ----- printing -------------------------------------------------------------
2103
2104/// How a value prints. Floats always show a decimal point so 3.0 reads as a
2105/// float, not an int. Arrays print their elements comma-separated in brackets.
2106fn display(v: &Value) -> String {
2107    match v {
2108        Value::Int(n) => n.to_string(),
2109        Value::Float(f) => format_float(*f),
2110        Value::Str(s) => s.clone(),
2111        Value::Bool(b) => b.to_string(),
2112        Value::Array(items) => {
2113            let parts: Vec<String> = items.iter().map(display).collect();
2114            format!("[{}]", parts.join(", "))
2115        }
2116        Value::Range(lo, hi) => format!("{}..{}", lo, hi),
2117        Value::Struct { name, fields } => {
2118            format!("{}({})", name, display_fields(fields))
2119        }
2120        // The built-in generics print the way they're written — `some(5)`,
2121        // `none`, `err("nope")` — not in the labelled `Enum.case(...)` form.
2122        Value::Enum {
2123            enum_name,
2124            variant,
2125            fields,
2126        } if enum_name == "Option" || enum_name == "Result" => match fields.first() {
2127            Some((_, payload)) => format!("{}({})", variant, display(payload)),
2128            None => variant.clone(),
2129        },
2130        Value::Enum {
2131            enum_name,
2132            variant,
2133            fields,
2134        } => {
2135            if fields.is_empty() {
2136                format!("{}.{}", enum_name, variant)
2137            } else {
2138                format!("{}.{}({})", enum_name, variant, display_fields(fields))
2139            }
2140        }
2141        Value::Unit => String::new(),
2142    }
2143}
2144
2145fn format_float(f: f64) -> String {
2146    if f.is_finite() && f == f.trunc() {
2147        format!("{:.1}", f)
2148    } else {
2149        format!("{}", f)
2150    }
2151}
2152
2153/// Render labelled fields as `name: value, name: value`, shared by structs and
2154/// enum cases so they print the way they were built.
2155fn display_fields(fields: &[(String, Value)]) -> String {
2156    fields
2157        .iter()
2158        .map(|(k, v)| format!("{}: {}", k, display(v)))
2159        .collect::<Vec<_>>()
2160        .join(", ")
2161}