feat: Add assignment operator and recursive scope assignment
Introduces the `Assign` AST node and a corresponding `assign` method on the `Scope` struct. This allows for updating existing variables within the current scope or any parent scope. The `assign` method recursively traverses the scope chain until it finds the variable or determines it's undefined. This enables reassigning variables defined in outer scopes.
This commit is contained in:
+33
-5
@@ -35,6 +35,18 @@ impl Scope {
|
|||||||
self.values.insert(name.to_string(), value);
|
self.values.insert(name.to_string(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Recursively finds and updates an existing variable
|
||||||
|
pub fn assign(&mut self, name: &str, value: Value) -> Result<(), String> {
|
||||||
|
if self.values.contains_key(name) {
|
||||||
|
self.values.insert(name.to_string(), value);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if let Some(parent) = &self.parent {
|
||||||
|
return parent.lock().unwrap().assign(name, value);
|
||||||
|
}
|
||||||
|
Err(format!("Cannot assign to undefined variable '{}'", name))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resolve(&self, name: &str) -> Option<Value> {
|
pub fn resolve(&self, name: &str) -> Option<Value> {
|
||||||
if let Some(val) = self.values.get(name) {
|
if let Some(val) = self.values.get(name) {
|
||||||
return Some(val.clone());
|
return Some(val.clone());
|
||||||
@@ -115,11 +127,15 @@ pub enum UntypedKind {
|
|||||||
then_br: Box<Node<UntypedKind>>,
|
then_br: Box<Node<UntypedKind>>,
|
||||||
else_br: Option<Box<Node<UntypedKind>>>,
|
else_br: Option<Box<Node<UntypedKind>>>,
|
||||||
},
|
},
|
||||||
// DEFINITION (new)
|
|
||||||
Def {
|
Def {
|
||||||
name: Arc<str>,
|
name: Arc<str>,
|
||||||
value: Box<Node<UntypedKind>>,
|
value: Box<Node<UntypedKind>>,
|
||||||
},
|
},
|
||||||
|
// ASSIGN (Update existing variable)
|
||||||
|
Assign {
|
||||||
|
target: Box<Node<UntypedKind>>,
|
||||||
|
value: Box<Node<UntypedKind>>,
|
||||||
|
},
|
||||||
Lambda {
|
Lambda {
|
||||||
params: Vec<Arc<str>>,
|
params: Vec<Arc<str>>,
|
||||||
body: Arc<Node<UntypedKind>>,
|
body: Arc<Node<UntypedKind>>,
|
||||||
@@ -128,7 +144,6 @@ pub enum UntypedKind {
|
|||||||
callee: Box<Node<UntypedKind>>,
|
callee: Box<Node<UntypedKind>>,
|
||||||
args: Vec<Node<UntypedKind>>,
|
args: Vec<Node<UntypedKind>>,
|
||||||
},
|
},
|
||||||
// BLOCK (do ...) to evaluate multiple expressions
|
|
||||||
Block {
|
Block {
|
||||||
exprs: Vec<Node<UntypedKind>>,
|
exprs: Vec<Node<UntypedKind>>,
|
||||||
},
|
},
|
||||||
@@ -158,15 +173,28 @@ impl Node<UntypedKind> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// --- DEF (Assignment) ---
|
|
||||||
UntypedKind::Def { name, value } => {
|
UntypedKind::Def { name, value } => {
|
||||||
let val = value.eval(ctx)?;
|
let val = value.eval(ctx)?;
|
||||||
let mut scope = ctx.scope.lock().unwrap();
|
let mut scope = ctx.scope.lock().unwrap();
|
||||||
scope.define(name, val.clone());
|
scope.define(name, val.clone());
|
||||||
Ok(val) // Return value of definition
|
Ok(val)
|
||||||
|
},
|
||||||
|
|
||||||
|
// --- ASSIGN IMPLEMENTATION ---
|
||||||
|
UntypedKind::Assign { target, value } => {
|
||||||
|
let val = value.eval(ctx)?;
|
||||||
|
|
||||||
|
// We currently only support assigning to identifiers: (assign x 10)
|
||||||
|
if let UntypedKind::Identifier(name) = &target.kind {
|
||||||
|
let mut scope = ctx.scope.lock().unwrap();
|
||||||
|
scope.assign(name, val.clone())?;
|
||||||
|
Ok(val)
|
||||||
|
} else {
|
||||||
|
// Later: Support (assign (get arr 0) val) via set-element logic
|
||||||
|
Err("Assignment target must be an identifier".to_string())
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// --- BLOCK (do) ---
|
|
||||||
UntypedKind::Block { exprs } => {
|
UntypedKind::Block { exprs } => {
|
||||||
let mut last_val = Value::Void;
|
let mut last_val = Value::Void;
|
||||||
for expr in exprs {
|
for expr in exprs {
|
||||||
|
|||||||
+47
-3
@@ -28,6 +28,7 @@ impl<'a> Parser<'a> {
|
|||||||
match self.peek() {
|
match self.peek() {
|
||||||
TokenKind::LeftParen => self.parse_list(),
|
TokenKind::LeftParen => self.parse_list(),
|
||||||
TokenKind::LeftBracket => self.parse_vector_literal(),
|
TokenKind::LeftBracket => self.parse_vector_literal(),
|
||||||
|
TokenKind::LeftBrace => self.parse_map_literal(),
|
||||||
TokenKind::Quote => {
|
TokenKind::Quote => {
|
||||||
let identity = Arc::new(NodeIdentity { location: self.current_token.location });
|
let identity = Arc::new(NodeIdentity { location: self.current_token.location });
|
||||||
self.advance()?; // consume '
|
self.advance()?; // consume '
|
||||||
@@ -82,6 +83,7 @@ impl<'a> Parser<'a> {
|
|||||||
"if" => self.parse_if(identity),
|
"if" => self.parse_if(identity),
|
||||||
"fn" => self.parse_fn(identity),
|
"fn" => self.parse_fn(identity),
|
||||||
"def" => self.parse_def(identity),
|
"def" => self.parse_def(identity),
|
||||||
|
"assign" => self.parse_assign(identity),
|
||||||
"do" => self.parse_do(identity),
|
"do" => self.parse_do(identity),
|
||||||
_ => self.parse_call(head, identity),
|
_ => self.parse_call(head, identity),
|
||||||
}
|
}
|
||||||
@@ -109,7 +111,6 @@ 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> {
|
||||||
// (def name value)
|
|
||||||
let name_node = self.parse_expression()?;
|
let name_node = self.parse_expression()?;
|
||||||
let name = match name_node.kind {
|
let name = match name_node.kind {
|
||||||
UntypedKind::Identifier(s) => s,
|
UntypedKind::Identifier(s) => s,
|
||||||
@@ -124,6 +125,17 @@ impl<'a> Parser<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_assign(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||||
|
// (assign target value)
|
||||||
|
let target = Box::new(self.parse_expression()?);
|
||||||
|
let value = Box::new(self.parse_expression()?);
|
||||||
|
|
||||||
|
Ok(Node {
|
||||||
|
identity,
|
||||||
|
kind: UntypedKind::Assign { target, value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_do(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
fn parse_do(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||||
let mut exprs = Vec::new();
|
let mut exprs = Vec::new();
|
||||||
while *self.peek() != TokenKind::RightParen && *self.peek() != TokenKind::EOF {
|
while *self.peek() != TokenKind::RightParen && *self.peek() != TokenKind::EOF {
|
||||||
@@ -152,7 +164,7 @@ impl<'a> Parser<'a> {
|
|||||||
if *self.peek() != TokenKind::LeftBracket {
|
if *self.peek() != TokenKind::LeftBracket {
|
||||||
return Err(format!("Expected parameter vector [...] for fn, found {:?}", self.peek()));
|
return Err(format!("Expected parameter vector [...] for fn, found {:?}", self.peek()));
|
||||||
}
|
}
|
||||||
self.advance()?; // consume '['
|
self.advance()?;
|
||||||
|
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
while *self.peek() != TokenKind::RightBracket {
|
while *self.peek() != TokenKind::RightBracket {
|
||||||
@@ -180,7 +192,7 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_vector_literal(&mut self) -> Result<Node<UntypedKind>, String> {
|
fn parse_vector_literal(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||||
let token = self.advance()?; // consume '['
|
let token = self.advance()?;
|
||||||
let mut elements = Vec::new();
|
let mut elements = Vec::new();
|
||||||
let mut temp_ctx = Context::new();
|
let mut temp_ctx = Context::new();
|
||||||
|
|
||||||
@@ -200,6 +212,38 @@ impl<'a> Parser<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
while *self.peek() != TokenKind::RightBrace {
|
||||||
|
if *self.peek() == TokenKind::EOF {
|
||||||
|
return Err("Unexpected EOF in map literal".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
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()),
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
self.expect(TokenKind::RightBrace)?;
|
||||||
|
|
||||||
|
Ok(Node {
|
||||||
|
identity: Arc::new(NodeIdentity { location: token.location }),
|
||||||
|
kind: UntypedKind::Constant(Value::Record(Arc::new(map))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn expect(&mut self, kind: TokenKind) -> Result<(), String> {
|
fn expect(&mut self, kind: TokenKind) -> Result<(), String> {
|
||||||
let token = self.advance()?;
|
let token = self.advance()?;
|
||||||
if token.kind == kind {
|
if token.kind == kind {
|
||||||
|
|||||||
Reference in New Issue
Block a user