Refactor: Implement block scoping and statement separation

This commit introduces fundamental changes to Myc's language structure
to enhance type safety and scoping clarity.

Key changes include:
- Introducing local scopes for `do` blocks.
- Separating statements (`def`, `assign`) from expressions, where
  statements now have no return value and are only allowed within
  blocks.
- A block now consists of multiple statements followed by a single final
  expression.
- Stricter validation rules are enforced, such as disallowing
  redefinition of local symbols in the same scope while allowing
  shadowing of outer symbols.
- Compiler errors are generated for statements used in expression
  contexts.

The implementation involved modifications to the AST, parser, binder
(scope-stack), and various compiler passes, along with VM runtime
optimizations.
This commit is contained in:
Michael Schimmel
2026-03-09 16:38:44 +01:00
parent 8339ee413e
commit beb693a068
26 changed files with 1057 additions and 948 deletions
+25 -7
View File
@@ -1,4 +1,4 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot, UpvalueIdx};
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot, UpvalueIdx, UpvalueSource};
use crate::ast::nodes::Node;
use crate::ast::types::Value;
use std::collections::{HashMap, HashSet};
@@ -88,6 +88,21 @@ impl SubstitutionMap {
}
}
fn reindex_source(&self, source: UpvalueSource, mapping: &[Option<u32>]) -> UpvalueSource {
match source {
UpvalueSource::Upvalue(idx) => {
if let Some(res) = mapping.get(idx.0 as usize)
&& let Some(new_idx) = res
{
UpvalueSource::Upvalue(UpvalueIdx(*new_idx))
} else {
source
}
}
_ => source
}
}
pub fn reindex_upvalues(&self, node_rc: Rc<AnalyzedNode>, mapping: &[Option<u32>]) -> Rc<AnalyzedNode> {
let node = &*node_rc;
let (new_kind, metrics) = match &node.kind {
@@ -105,8 +120,8 @@ impl SubstitutionMap {
positional_count,
} => {
let mut next_upvalues = Vec::new();
for addr in upvalues {
next_upvalues.push(self.reindex_addr(*addr, mapping));
for source in upvalues {
next_upvalues.push(self.reindex_source(*source, mapping));
}
(
BoundKind::Lambda {
@@ -135,12 +150,13 @@ impl SubstitutionMap {
node.ty.clone(),
)
}
BoundKind::Block { exprs } => {
let exprs = exprs
BoundKind::Block { statements, result } => {
let t_statements = statements
.iter()
.map(|e| self.reindex_upvalues(e.clone(), mapping))
.map(|s| self.reindex_upvalues(s.clone(), mapping))
.collect();
(BoundKind::Block { exprs }, node.ty.clone())
let t_result = self.reindex_upvalues(result.clone(), mapping);
(BoundKind::Block { statements: t_statements, result: t_result }, node.ty.clone())
}
BoundKind::Call { callee, args } => {
let callee = self.reindex_upvalues(callee.clone(), mapping);
@@ -152,6 +168,7 @@ impl SubstitutionMap {
addr,
kind,
value,
identity,
captured_by,
} => {
let value = self.reindex_upvalues(value.clone(), mapping);
@@ -161,6 +178,7 @@ impl SubstitutionMap {
addr: *addr,
kind: *kind,
value,
identity: identity.clone(),
captured_by: captured_by.clone(),
},
node.ty.clone(),