Add tuple and map literals

This commit introduces support for tuple and map literals in the AST.
Tuples are now represented by `UntypedKind::Tuple` and maps by
`UntypedKind::Map`.
The `Binder` has been updated to correctly handle these new node types
and infer their types.
The `VM` now also supports evaluating tuple and map literals.
This commit is contained in:
Michael Schimmel
2026-02-17 13:07:17 +01:00
parent d55422272b
commit 9afde5a301
5 changed files with 92 additions and 177 deletions
+40 -2
View File
@@ -1,9 +1,9 @@
use std::collections::HashMap;
use std::collections::{HashMap, BTreeMap};
use std::rc::Rc;
use std::cell::RefCell;
use crate::ast::nodes::{Node, UntypedKind};
use crate::ast::compiler::bound_nodes::{BoundKind, Address};
use crate::ast::types::{Identity, StaticType};
use crate::ast::types::{Identity, StaticType, Value};
#[derive(Debug, Clone)]
struct LocalInfo {
@@ -229,6 +229,44 @@ impl Binder {
}
Ok(self.make_node(node.identity.clone(), BoundKind::Block { exprs: bound_exprs }, last_ty))
},
UntypedKind::Tuple { elements } => {
let mut bound_elems = Vec::new();
for e in elements {
bound_elems.push(self.bind(e)?);
}
// For now, tuple type is List(Any)
let ty = StaticType::List(Box::new(StaticType::Any));
Ok(self.make_node(node.identity.clone(), BoundKind::Tuple { elements: bound_elems }, ty))
},
UntypedKind::Map { entries } => {
let mut bound_entries = Vec::new();
let mut key_types = BTreeMap::new(); // For Record type inference if keys are constant keywords
for (k, v) in entries {
// Keys must be compile-time constants for now (for Record type),
// or at least we enforce keywords.
// But `bind` processes runtime expressions too.
// Let's bind both.
let bound_k = self.bind(k)?;
let bound_v = self.bind(v)?;
// If key is a constant keyword, we can build a static record type.
if let BoundKind::Constant(Value::Keyword(kw)) = &bound_k.kind {
key_types.insert(*kw, bound_v.ty.clone());
}
bound_entries.push((bound_k, bound_v));
}
// If all keys are known, we produce a Record type. Else Map(Any, Any) which we don't have yet.
// We default to Record(Any) if keys are dynamic (not supported well yet) or known.
// Since our parser enforced keywords, we assume we have a Record.
let ty = StaticType::Record(Rc::new(key_types));
Ok(self.make_node(node.identity.clone(), BoundKind::Map { entries: bound_entries }, ty))
},
UntypedKind::Extension(_) => {
Err("Custom extensions not supported in Binder yet".to_string())
+8
View File
@@ -51,6 +51,14 @@ pub enum BoundKind {
exprs: Vec<Node<BoundKind, StaticType>>,
},
// NEW
Tuple {
elements: Vec<Node<BoundKind, StaticType>>,
},
Map {
entries: Vec<(Node<BoundKind, StaticType>, Node<BoundKind, StaticType>)>,
},
// Extension points (need to be adapted for bound nodes if they use variables)
// For now, we assume extensions are self-contained or handled dynamically.
}