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
+14 -17
View File
@@ -1,7 +1,7 @@
use std::rc::Rc;
use crate::ast::lexer::{Lexer, Token, TokenKind};
use crate::ast::types::{Identity, NodeIdentity, Value, Keyword};
use crate::ast::nodes::{Node, UntypedKind, Context};
use crate::ast::nodes::{Node, UntypedKind};
pub struct Parser<'a> {
lexer: Lexer<'a>,
@@ -202,29 +202,24 @@ impl<'a> Parser<'a> {
fn parse_vector_literal(&mut self) -> Result<Node<UntypedKind>, String> {
let token = self.advance()?;
let mut elements = Vec::new();
let mut temp_ctx = Context::new();
while *self.peek() != TokenKind::RightBracket && *self.peek() != TokenKind::EOF {
let expr = self.parse_expression()?;
match expr.eval(&mut temp_ctx) {
Ok(val) => elements.push(val),
Err(e) => return Err(format!("Vector literal error: {}", e)),
}
elements.push(expr);
}
self.expect(TokenKind::RightBracket)?;
Ok(Node {
identity: Rc::new(NodeIdentity { location: token.location }),
kind: UntypedKind::Constant(Value::List(Rc::new(elements))),
kind: UntypedKind::Tuple { elements },
ty: (),
})
}
fn parse_map_literal(&mut self) -> Result<Node<UntypedKind>, String> {
let token = self.advance()?;
let mut map = std::collections::HashMap::new();
let mut temp_ctx = Context::new();
let mut entries = Vec::new();
while *self.peek() != TokenKind::RightBrace {
if *self.peek() == TokenKind::EOF {
@@ -232,24 +227,26 @@ impl<'a> Parser<'a> {
}
let key_node = self.parse_expression()?;
let key = match key_node.eval(&mut temp_ctx)? {
Value::Keyword(k) => k,
_ => return Err("Map keys must be keywords".to_string()),
};
// We check for keyword kind here (syntactically) to avoid ambiguity, but
// strictly we could allow any expression and check at runtime.
// Delphi enforces keywords. We can do minimal check here.
match &key_node.kind {
UntypedKind::Constant(Value::Keyword(_)) => {},
_ => return Err("Map keys must be keywords (syntactically)".to_string()),
}
if *self.peek() == TokenKind::RightBrace {
return Err("Map literal must have even number of forms".to_string());
}
let val_node = self.parse_expression()?;
let val = val_node.eval(&mut temp_ctx)?;
map.insert(key, val);
entries.push((key_node, val_node));
}
self.expect(TokenKind::RightBrace)?;
Ok(Node {
identity: Rc::new(NodeIdentity { location: token.location }),
kind: UntypedKind::Constant(Value::Record(Rc::new(map))),
kind: UntypedKind::Map { entries },
ty: (),
})
}