Add support for destructuring def
This commit introduces the `DefDestructure` bound kind and modifies the binder, analyzer, type checker, and VM to support destructuring in `def` statements. This allows for pattern matching on the right-hand side of a `def` to bind multiple variables. The parser has been updated to accept patterns in `def` statements. The binder now handles `UntypedKind::Def` with a `target` pattern, rather than a simple `name`. This enables destructuring. The `Gemini.md` documentation has been updated to include a new rule for incremental development.
This commit is contained in:
@@ -37,6 +37,7 @@ Wichtig: zentraler Einstiegspunkt ist Delphi/Myc.Ast.Environment.pasund die dort
|
|||||||
* Ich kann Rust noch nicht so gut. Erkläre mir, was du machst und welche Konzepte du nutzt. Ich will was dazulernen.
|
* Ich kann Rust noch nicht so gut. Erkläre mir, was du machst und welche Konzepte du nutzt. Ich will was dazulernen.
|
||||||
* Wenn clippy etwas vorschlägt, von dem du glaubst, dass es nicht stabil ist, dann überprüfe die aktuelle Version. Wahrscheinlich ist das Feature mittlerweile im trunk. Wir gehen immer davon aus, dass clippy recht hat.
|
* Wenn clippy etwas vorschlägt, von dem du glaubst, dass es nicht stabil ist, dann überprüfe die aktuelle Version. Wahrscheinlich ist das Feature mittlerweile im trunk. Wir gehen immer davon aus, dass clippy recht hat.
|
||||||
* Keine Interaktion mit GIT. Kein Commit, oder ähnliches. Schlage auch nichts dergleichen vor.
|
* Keine Interaktion mit GIT. Kein Commit, oder ähnliches. Schlage auch nichts dergleichen vor.
|
||||||
|
* Gehe immer schrittweise vor. Zeige mir, was du vor hast, bevor du Code erzeugst.
|
||||||
|
|
||||||
## Testing & Debugging
|
## Testing & Debugging
|
||||||
|
|
||||||
|
|||||||
@@ -178,6 +178,18 @@ impl<'a> Analyzer<'a> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoundKind::DefDestructure { pattern, value } => {
|
||||||
|
let pat_m = self.visit(Rc::new((**pattern).clone()));
|
||||||
|
let val_m = self.visit(Rc::new((**value).clone()));
|
||||||
|
(
|
||||||
|
BoundKind::DefDestructure {
|
||||||
|
pattern: Box::new(pat_m),
|
||||||
|
value: Box::new(val_m),
|
||||||
|
},
|
||||||
|
Purity::Impure,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BoundKind::Call { callee, args } => {
|
BoundKind::Call { callee, args } => {
|
||||||
let callee_m = self.visit(Rc::new((**callee).clone()));
|
let callee_m = self.visit(Rc::new((**callee).clone()));
|
||||||
let args_m = self.visit(Rc::new((**args).clone()));
|
let args_m = self.visit(Rc::new((**args).clone()));
|
||||||
|
|||||||
+104
-56
@@ -128,23 +128,8 @@ impl Binder {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
UntypedKind::Parameter(sym) => {
|
UntypedKind::Parameter(_) => {
|
||||||
// Parameters are ONLY allowed if we are inside a function (Binder stack > 1)
|
Err("Unexpected 'Parameter' node in general binder context. This should be handled via 'bind_pattern'.".to_string())
|
||||||
if self.functions.len() <= 1 {
|
|
||||||
return Err(format!(
|
|
||||||
"Parameter '{}' is not allowed in root scope.",
|
|
||||||
sym.name
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let current_fn = self.functions.last_mut().unwrap();
|
|
||||||
let slot = current_fn.scope.define(sym)?;
|
|
||||||
Ok(self.make_node(
|
|
||||||
node.identity.clone(),
|
|
||||||
BoundKind::Parameter {
|
|
||||||
name: sym.clone(),
|
|
||||||
slot,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UntypedKind::If {
|
UntypedKind::If {
|
||||||
@@ -169,52 +154,66 @@ impl Binder {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
UntypedKind::Def { name, value } => {
|
UntypedKind::Def { target, value } => {
|
||||||
// 1. Pre-declare name to support recursion
|
// Special case: Single identifier (to support recursion)
|
||||||
let slot_or_idx = if self.functions.len() == 1 {
|
if let UntypedKind::Parameter(ref name) = target.kind {
|
||||||
let mut globals = self.globals.borrow_mut();
|
let slot_or_idx = if self.functions.len() == 1 {
|
||||||
if globals.contains_key(name) {
|
let mut globals = self.globals.borrow_mut();
|
||||||
return Err(format!(
|
if globals.contains_key(name) {
|
||||||
"Global variable '{}' is already defined.",
|
return Err(format!(
|
||||||
name.name
|
"Global variable '{}' is already defined.",
|
||||||
));
|
name.name
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let idx = globals.len() as u32;
|
||||||
|
globals.insert(name.clone(), idx);
|
||||||
|
idx
|
||||||
|
} else {
|
||||||
|
let current_fn = self.functions.last_mut().unwrap();
|
||||||
|
current_fn.scope.define(name)?
|
||||||
|
};
|
||||||
|
|
||||||
|
let val_node = self.bind(value)?;
|
||||||
|
|
||||||
|
if self.functions.len() == 1 {
|
||||||
|
Ok(self.make_node(
|
||||||
|
node.identity.clone(),
|
||||||
|
BoundKind::DefGlobal {
|
||||||
|
name: name.clone(),
|
||||||
|
global_index: slot_or_idx,
|
||||||
|
value: Box::new(val_node),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
let captured_by = self
|
||||||
|
.capture_map
|
||||||
|
.get(&target.identity)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
Ok(self.make_node(
|
||||||
|
node.identity.clone(),
|
||||||
|
BoundKind::DefLocal {
|
||||||
|
name: name.clone(),
|
||||||
|
slot: slot_or_idx,
|
||||||
|
value: Box::new(val_node),
|
||||||
|
captured_by,
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
let idx = globals.len() as u32;
|
|
||||||
globals.insert(name.clone(), idx);
|
|
||||||
idx
|
|
||||||
} else {
|
} else {
|
||||||
let current_fn = self.functions.last_mut().unwrap();
|
// Complex Destructuring Pattern
|
||||||
current_fn.scope.define(name)?
|
// NOTE: Destructuring definitions are NOT recursive by default
|
||||||
};
|
// (the variables are only available AFTER the definition)
|
||||||
|
let val_node = self.bind(value)?;
|
||||||
|
let target_node = self.bind_pattern(target)?;
|
||||||
|
|
||||||
// 2. Bind Value (now 'name' is visible)
|
|
||||||
let val_node = self.bind(value)?;
|
|
||||||
|
|
||||||
// 3. Return Node
|
|
||||||
if self.functions.len() == 1 {
|
|
||||||
Ok(self.make_node(
|
Ok(self.make_node(
|
||||||
node.identity.clone(),
|
node.identity.clone(),
|
||||||
BoundKind::DefGlobal {
|
BoundKind::DefDestructure {
|
||||||
name: name.clone(),
|
pattern: Box::new(target_node),
|
||||||
global_index: slot_or_idx,
|
|
||||||
value: Box::new(val_node),
|
value: Box::new(val_node),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
} else {
|
|
||||||
let captured_by = self
|
|
||||||
.capture_map
|
|
||||||
.get(&node.identity)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default();
|
|
||||||
Ok(self.make_node(
|
|
||||||
node.identity.clone(),
|
|
||||||
BoundKind::DefLocal {
|
|
||||||
name: name.clone(),
|
|
||||||
slot: slot_or_idx,
|
|
||||||
value: Box::new(val_node),
|
|
||||||
captured_by,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +239,7 @@ impl Binder {
|
|||||||
self.functions.push(FunctionCompiler::new());
|
self.functions.push(FunctionCompiler::new());
|
||||||
|
|
||||||
// 1. Bind the parameter pattern/tuple
|
// 1. Bind the parameter pattern/tuple
|
||||||
let params_bound = self.bind(params)?;
|
let params_bound = self.bind_pattern(params)?;
|
||||||
|
|
||||||
// 2. Bind the body
|
// 2. Bind the body
|
||||||
let body_bound = self.bind(body)?;
|
let body_bound = self.bind(body)?;
|
||||||
@@ -405,6 +404,55 @@ impl Binder {
|
|||||||
Err(format!("Undefined variable '{}'", sym.name))
|
Err(format!("Undefined variable '{}'", sym.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bind_pattern(&mut self, node: &Node<UntypedKind>) -> Result<BoundNode, String> {
|
||||||
|
match &node.kind {
|
||||||
|
UntypedKind::Parameter(sym) => {
|
||||||
|
if self.functions.len() == 1 {
|
||||||
|
// Global Definition in pattern
|
||||||
|
let mut globals = self.globals.borrow_mut();
|
||||||
|
if globals.contains_key(sym) {
|
||||||
|
return Err(format!("Global variable '{}' is already defined.", sym.name));
|
||||||
|
}
|
||||||
|
let idx = globals.len() as u32;
|
||||||
|
globals.insert(sym.clone(), idx);
|
||||||
|
// For Global Destructuring, we return a DefGlobal with Nop value as it's a pattern part
|
||||||
|
Ok(self.make_node(
|
||||||
|
node.identity.clone(),
|
||||||
|
BoundKind::DefGlobal {
|
||||||
|
name: sym.clone(),
|
||||||
|
global_index: idx,
|
||||||
|
value: Box::new(self.make_node(node.identity.clone(), BoundKind::Nop)),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
// Local Parameter/Definition in pattern
|
||||||
|
let current_fn = self.functions.last_mut().unwrap();
|
||||||
|
let slot = current_fn.scope.define(sym)?;
|
||||||
|
Ok(self.make_node(
|
||||||
|
node.identity.clone(),
|
||||||
|
BoundKind::Parameter {
|
||||||
|
name: sym.clone(),
|
||||||
|
slot,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UntypedKind::Tuple { elements } => {
|
||||||
|
let mut bound_elems = Vec::new();
|
||||||
|
for e in elements {
|
||||||
|
bound_elems.push(self.bind_pattern(e)?);
|
||||||
|
}
|
||||||
|
Ok(self.make_node(
|
||||||
|
node.identity.clone(),
|
||||||
|
BoundKind::Tuple {
|
||||||
|
elements: bound_elems,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => Err(format!("Invalid node in pattern: {:?}", node.kind)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn make_node(&self, identity: Identity, kind: BoundKind<()>) -> BoundNode {
|
fn make_node(&self, identity: Identity, kind: BoundKind<()>) -> BoundNode {
|
||||||
Node {
|
Node {
|
||||||
identity,
|
identity,
|
||||||
|
|||||||
@@ -82,6 +82,12 @@ pub enum BoundKind<T = ()> {
|
|||||||
value: Box<BoundNode<T>>,
|
value: Box<BoundNode<T>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// A destructuring definition (can be local or global depending on the pattern)
|
||||||
|
DefDestructure {
|
||||||
|
pattern: Box<BoundNode<T>>,
|
||||||
|
value: Box<BoundNode<T>>,
|
||||||
|
},
|
||||||
|
|
||||||
Lambda {
|
Lambda {
|
||||||
params: Rc<BoundNode<T>>,
|
params: Rc<BoundNode<T>>,
|
||||||
// The list of variables captured from enclosing scopes
|
// The list of variables captured from enclosing scopes
|
||||||
@@ -187,6 +193,16 @@ where
|
|||||||
value: vb,
|
value: vb,
|
||||||
},
|
},
|
||||||
) => na == nb && ga == gb && va == vb,
|
) => na == nb && ga == gb && va == vb,
|
||||||
|
(
|
||||||
|
BoundKind::DefDestructure {
|
||||||
|
pattern: pa,
|
||||||
|
value: va,
|
||||||
|
},
|
||||||
|
BoundKind::DefDestructure {
|
||||||
|
pattern: pb,
|
||||||
|
value: vb,
|
||||||
|
},
|
||||||
|
) => pa == pb && va == vb,
|
||||||
(
|
(
|
||||||
BoundKind::Lambda {
|
BoundKind::Lambda {
|
||||||
params: pa,
|
params: pa,
|
||||||
@@ -249,6 +265,7 @@ impl<T> BoundKind<T> {
|
|||||||
BoundKind::DefGlobal {
|
BoundKind::DefGlobal {
|
||||||
name, global_index, ..
|
name, global_index, ..
|
||||||
} => format!("DEF_GLOBAL({}, Idx:{})", name.name, global_index),
|
} => format!("DEF_GLOBAL({}, Idx:{})", name.name, global_index),
|
||||||
|
BoundKind::DefDestructure { .. } => "DEF_DESTRUCTURE".to_string(),
|
||||||
BoundKind::Lambda {
|
BoundKind::Lambda {
|
||||||
params, upvalues, ..
|
params, upvalues, ..
|
||||||
} => {
|
} => {
|
||||||
|
|||||||
@@ -120,6 +120,18 @@ impl Dumper {
|
|||||||
self.indent -= 1;
|
self.indent -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoundKind::DefDestructure { pattern, value } => {
|
||||||
|
self.log("DefDestructure", node);
|
||||||
|
self.indent += 1;
|
||||||
|
self.write_indent();
|
||||||
|
self.output.push_str("Pattern:\n");
|
||||||
|
self.visit(pattern);
|
||||||
|
self.write_indent();
|
||||||
|
self.output.push_str("Value:\n");
|
||||||
|
self.visit(value);
|
||||||
|
self.indent -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
BoundKind::If {
|
BoundKind::If {
|
||||||
cond,
|
cond,
|
||||||
then_br,
|
then_br,
|
||||||
|
|||||||
@@ -186,12 +186,13 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
UntypedKind::Def { name, value } => {
|
UntypedKind::Def { target, value } => {
|
||||||
|
let target = self.expand_recursive(*target)?;
|
||||||
let value = self.expand_recursive(*value)?;
|
let value = self.expand_recursive(*value)?;
|
||||||
Ok(Node {
|
Ok(Node {
|
||||||
identity: node.identity,
|
identity: node.identity,
|
||||||
kind: UntypedKind::Def {
|
kind: UntypedKind::Def {
|
||||||
name,
|
target: Box::new(target),
|
||||||
value: Box::new(value),
|
value: Box::new(value),
|
||||||
},
|
},
|
||||||
ty: (),
|
ty: (),
|
||||||
@@ -379,13 +380,13 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
|||||||
Ok(self.value_to_node(val, node.identity))
|
Ok(self.value_to_node(val, node.identity))
|
||||||
}
|
}
|
||||||
|
|
||||||
UntypedKind::Def { mut name, value } => {
|
UntypedKind::Def { target, value } => {
|
||||||
name.context = Some(state.expansion_id.clone());
|
let target = self.expand_template(*target, state)?;
|
||||||
let val = self.expand_template(*value, state)?;
|
let val = self.expand_template(*value, state)?;
|
||||||
Ok(Node {
|
Ok(Node {
|
||||||
identity: node.identity,
|
identity: node.identity,
|
||||||
kind: UntypedKind::Def {
|
kind: UntypedKind::Def {
|
||||||
name,
|
target: Box::new(target),
|
||||||
value: Box::new(val),
|
value: Box::new(val),
|
||||||
},
|
},
|
||||||
ty: (),
|
ty: (),
|
||||||
@@ -613,9 +614,13 @@ mod tests {
|
|||||||
call,
|
call,
|
||||||
} = &exprs[1].kind
|
} = &exprs[1].kind
|
||||||
{
|
{
|
||||||
if let UntypedKind::Def { name, .. } = &result.kind {
|
if let UntypedKind::Def { target, .. } = &result.kind {
|
||||||
assert_eq!(name.context, Some(call.identity.clone()));
|
if let UntypedKind::Parameter(sym) = &target.kind {
|
||||||
assert_eq!(name.name.as_ref(), "y");
|
assert_eq!(sym.context, Some(call.identity.clone()));
|
||||||
|
assert_eq!(sym.name.as_ref(), "y");
|
||||||
|
} else {
|
||||||
|
panic!("Expected Parameter target, got {:?}", target.kind);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected Def result, got {:?}", result.kind);
|
panic!("Expected Def result, got {:?}", result.kind);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,10 @@ impl TCO {
|
|||||||
global_index: *global_index,
|
global_index: *global_index,
|
||||||
value: Box::new(Self::transform(Rc::new((**value).clone()), false)),
|
value: Box::new(Self::transform(Rc::new((**value).clone()), false)),
|
||||||
},
|
},
|
||||||
|
BoundKind::DefDestructure { pattern, value } => BoundKind::DefDestructure {
|
||||||
|
pattern: Box::new(Self::transform(Rc::new((**pattern).clone()), false)),
|
||||||
|
value: Box::new(Self::transform(Rc::new((**value).clone()), false)),
|
||||||
|
},
|
||||||
BoundKind::Record { fields } => {
|
BoundKind::Record { fields } => {
|
||||||
let new_fields = fields
|
let new_fields = fields
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -144,6 +144,27 @@ impl TypeChecker {
|
|||||||
ctx.set_local_type(slot, specialized_ty.clone());
|
ctx.set_local_type(slot, specialized_ty.clone());
|
||||||
(BoundKind::Parameter { name, slot }, specialized_ty.clone())
|
(BoundKind::Parameter { name, slot }, specialized_ty.clone())
|
||||||
}
|
}
|
||||||
|
BoundKind::DefGlobal {
|
||||||
|
name,
|
||||||
|
global_index,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
self.global_types
|
||||||
|
.borrow_mut()
|
||||||
|
.insert(global_index, specialized_ty.clone());
|
||||||
|
(
|
||||||
|
BoundKind::DefGlobal {
|
||||||
|
name,
|
||||||
|
global_index,
|
||||||
|
value: Box::new(Node {
|
||||||
|
identity: node.identity.clone(),
|
||||||
|
kind: BoundKind::Nop,
|
||||||
|
ty: StaticType::Void,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
specialized_ty.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
BoundKind::Tuple { elements } => {
|
BoundKind::Tuple { elements } => {
|
||||||
let mut typed_elements = Vec::new();
|
let mut typed_elements = Vec::new();
|
||||||
let mut elem_types = Vec::new();
|
let mut elem_types = Vec::new();
|
||||||
@@ -263,6 +284,19 @@ impl TypeChecker {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoundKind::DefDestructure { pattern, value } => {
|
||||||
|
let val_typed = self.check_node(*value, ctx)?;
|
||||||
|
let pat_typed = self.check_params(*pattern, &val_typed.ty, ctx)?;
|
||||||
|
let ty = val_typed.ty.clone();
|
||||||
|
(
|
||||||
|
BoundKind::DefDestructure {
|
||||||
|
pattern: Box::new(pat_typed),
|
||||||
|
value: Box::new(val_typed),
|
||||||
|
},
|
||||||
|
ty,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BoundKind::If {
|
BoundKind::If {
|
||||||
cond,
|
cond,
|
||||||
then_br,
|
then_br,
|
||||||
|
|||||||
@@ -58,10 +58,10 @@ impl UpvalueAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UntypedKind::Def { name, value } => {
|
UntypedKind::Def { target, value } => {
|
||||||
Self::visit(value, scopes, capture_map, current_lambda.clone());
|
Self::visit(value, scopes, capture_map, current_lambda.clone());
|
||||||
if let Some(current) = scopes.last_mut() {
|
if let Some(current) = scopes.last_mut() {
|
||||||
current.insert(name.clone(), node.identity.clone());
|
Self::visit_params(target, current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UntypedKind::Lambda { params, body } => {
|
UntypedKind::Lambda { params, body } => {
|
||||||
|
|||||||
+1
-1
@@ -71,7 +71,7 @@ pub enum UntypedKind {
|
|||||||
else_br: Option<Box<Node<UntypedKind>>>,
|
else_br: Option<Box<Node<UntypedKind>>>,
|
||||||
},
|
},
|
||||||
Def {
|
Def {
|
||||||
name: Symbol,
|
target: Box<Node<UntypedKind>>,
|
||||||
value: Box<Node<UntypedKind>>,
|
value: Box<Node<UntypedKind>>,
|
||||||
},
|
},
|
||||||
Assign {
|
Assign {
|
||||||
|
|||||||
+41
-41
@@ -185,17 +185,12 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_def(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
fn parse_def(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||||
let name_node = self.parse_expression()?;
|
let target = Box::new(self.parse_pattern()?);
|
||||||
let name = match name_node.kind {
|
|
||||||
UntypedKind::Identifier(sym) => sym,
|
|
||||||
_ => return Err("Expected identifier for def name".to_string()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let value = Box::new(self.parse_expression()?);
|
let value = Box::new(self.parse_expression()?);
|
||||||
|
|
||||||
Ok(Node {
|
Ok(Node {
|
||||||
identity,
|
identity,
|
||||||
kind: UntypedKind::Def { name, value },
|
kind: UntypedKind::Def { target, value },
|
||||||
ty: (),
|
ty: (),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -264,44 +259,49 @@ impl<'a> Parser<'a> {
|
|||||||
self.peek()
|
self.peek()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let token = self.advance()?;
|
self.parse_pattern()
|
||||||
let identity = Rc::new(NodeIdentity {
|
}
|
||||||
location: token.location,
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut elements = Vec::new();
|
fn parse_pattern(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||||
while *self.peek() != TokenKind::RightBracket {
|
let next = self.peek();
|
||||||
let next_peek = self.peek();
|
match next {
|
||||||
match next_peek {
|
TokenKind::Identifier(_) => {
|
||||||
TokenKind::Identifier(name) => {
|
let token = self.advance()?;
|
||||||
let name = name.clone();
|
let sym: Symbol = match token.kind {
|
||||||
let token = self.advance()?;
|
TokenKind::Identifier(s) => s.into(),
|
||||||
let p_identity = Rc::new(NodeIdentity {
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
Ok(Node {
|
||||||
|
identity: Rc::new(NodeIdentity {
|
||||||
location: token.location,
|
location: token.location,
|
||||||
});
|
}),
|
||||||
elements.push(Node {
|
kind: UntypedKind::Parameter(sym),
|
||||||
identity: p_identity,
|
ty: (),
|
||||||
kind: UntypedKind::Parameter(name.into()),
|
})
|
||||||
ty: (),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
TokenKind::LeftBracket => {
|
|
||||||
elements.push(self.parse_param_vector()?);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(format!(
|
|
||||||
"Expected identifier or nested parameter vector, found {:?}",
|
|
||||||
next_peek
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
TokenKind::LeftBracket => {
|
||||||
|
let token = self.advance()?;
|
||||||
|
let identity = Rc::new(NodeIdentity {
|
||||||
|
location: token.location,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut elements = Vec::new();
|
||||||
|
while *self.peek() != TokenKind::RightBracket {
|
||||||
|
elements.push(self.parse_pattern()?);
|
||||||
|
}
|
||||||
|
self.expect(TokenKind::RightBracket)?;
|
||||||
|
|
||||||
|
Ok(Node {
|
||||||
|
identity,
|
||||||
|
kind: UntypedKind::Tuple { elements },
|
||||||
|
ty: (),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(format!(
|
||||||
|
"Expected identifier or pattern vector [...] for definition, found {:?}",
|
||||||
|
next
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
self.expect(TokenKind::RightBracket)?;
|
|
||||||
Ok(Node {
|
|
||||||
identity,
|
|
||||||
kind: UntypedKind::Tuple { elements },
|
|
||||||
ty: (),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_call(
|
fn parse_call(
|
||||||
|
|||||||
+25
-9
@@ -281,6 +281,18 @@ impl VM {
|
|||||||
globals[idx] = val.clone();
|
globals[idx] = val.clone();
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
|
BoundKind::DefDestructure { pattern, value } => {
|
||||||
|
let val = self.eval_internal(obs, value)?;
|
||||||
|
let mut offset = 0;
|
||||||
|
|
||||||
|
// Destructuring works on tuples/vectors, or single values wrapped in a slice
|
||||||
|
if let Some(vals) = val.as_slice() {
|
||||||
|
self.unpack(pattern, vals, &mut offset)?;
|
||||||
|
} else {
|
||||||
|
self.unpack(pattern, &[val.clone()], &mut offset)?;
|
||||||
|
}
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
BoundKind::Get { addr, .. } => self.get_value(*addr),
|
BoundKind::Get { addr, .. } => self.get_value(*addr),
|
||||||
BoundKind::Set { addr, value } => {
|
BoundKind::Set { addr, value } => {
|
||||||
let val = self.eval_internal(obs, value)?;
|
let val = self.eval_internal(obs, value)?;
|
||||||
@@ -622,9 +634,7 @@ impl VM {
|
|||||||
|
|
||||||
fn flatten_value(val: Value, into: &mut Vec<Value>) {
|
fn flatten_value(val: Value, into: &mut Vec<Value>) {
|
||||||
if let Some(values) = val.as_slice() {
|
if let Some(values) = val.as_slice() {
|
||||||
for item in values.iter() {
|
into.extend_from_slice(values);
|
||||||
Self::flatten_value(item.clone(), into);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
into.push(val);
|
into.push(val);
|
||||||
}
|
}
|
||||||
@@ -654,12 +664,7 @@ impl VM {
|
|||||||
into: &mut Vec<Value>,
|
into: &mut Vec<Value>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
for e in elements {
|
for e in elements {
|
||||||
match &e.kind {
|
into.push(self.eval_internal(obs, e)?);
|
||||||
BoundKind::Tuple { elements: sub } => {
|
|
||||||
self.eval_and_flatten_internal(obs, sub, into)?
|
|
||||||
}
|
|
||||||
_ => into.push(self.eval_internal(obs, e)?),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -685,6 +690,17 @@ impl VM {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
BoundKind::DefGlobal { global_index, .. } => {
|
||||||
|
let val = values.get(*offset).cloned().unwrap_or(Value::Void);
|
||||||
|
*offset += 1;
|
||||||
|
let idx = *global_index as usize;
|
||||||
|
let mut globals = self.globals.borrow_mut();
|
||||||
|
if idx >= globals.len() {
|
||||||
|
globals.resize(idx + 1, Value::Void);
|
||||||
|
}
|
||||||
|
globals[idx] = val;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
BoundKind::Tuple { elements } => {
|
BoundKind::Tuple { elements } => {
|
||||||
if let Some(sub_values) = values.get(*offset).and_then(|v| v.as_slice()) {
|
if let Some(sub_values) = values.get(*offset).and_then(|v| v.as_slice()) {
|
||||||
*offset += 1;
|
*offset += 1;
|
||||||
|
|||||||
@@ -312,4 +312,28 @@ mod tests {
|
|||||||
dump_record
|
dump_record
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_def_destructuring() {
|
||||||
|
let env = Environment::new();
|
||||||
|
|
||||||
|
// 1. Global destructuring
|
||||||
|
let source_global = "(do (def [a b] [1 2]) (+ a b))";
|
||||||
|
assert_eq!(format!("{}", env.run_script(source_global).unwrap()), "3");
|
||||||
|
|
||||||
|
// 2. Local nested destructuring inside a function
|
||||||
|
let source_local = "((fn [x] (do (def [a [[b c] d]] x) (+ a (+ b (+ c d))))) [1 [[2 3] 4]])";
|
||||||
|
assert_eq!(format!("{}", env.run_script(source_local).unwrap()), "10");
|
||||||
|
|
||||||
|
// 3. Verify 'def' returns the assigned value
|
||||||
|
let source_return = "(def [x y] [7 8])";
|
||||||
|
let res = env.run_script(source_return).unwrap();
|
||||||
|
if let Value::Tuple(vals) = res {
|
||||||
|
assert_eq!(vals.len(), 2);
|
||||||
|
assert_eq!(format!("{}", vals[0]), "7");
|
||||||
|
assert_eq!(format!("{}", vals[1]), "8");
|
||||||
|
} else {
|
||||||
|
panic!("Expected tuple return from def, got {:?}", res);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user