feat: 添加赋值运算符支持,更新解析器、解释器和词法分析器以处理相关语法

This commit is contained in:
0264408
2026-06-16 08:55:21 +08:00
parent ae3060708d
commit a40c8d1e9e
6 changed files with 123 additions and 13 deletions
+15
View File
@@ -0,0 +1,15 @@
let a = 1
a += 2
print("a=", a)
let b = 2
b -= 1
print("b=", b)
let c = 5
c *= 5
print("c=", c)
let d = 10
c /= 5
print("d=", d)
+12
View File
@@ -11,6 +11,7 @@ pub enum Expr {
/// 赋值表达式:a = b /// 赋值表达式:a = b
Assign { Assign {
name: String, name: String,
op: AssignOp,
value: Box<Expr>, value: Box<Expr>,
}, },
@@ -24,6 +25,7 @@ pub enum Expr {
Set { Set {
object: Box<Expr>, object: Box<Expr>,
name: String, name: String,
op: AssignOp,
value: Box<Expr>, value: Box<Expr>,
}, },
@@ -47,6 +49,7 @@ pub enum Expr {
IndexSet { IndexSet {
array: Box<Expr>, array: Box<Expr>,
index: Box<Expr>, index: Box<Expr>,
op: AssignOp,
value: Box<Expr>, value: Box<Expr>,
}, },
@@ -118,3 +121,12 @@ pub enum LogicalOp {
And, And,
Or, Or,
} }
#[derive(Debug, Clone, Copy)]
pub enum AssignOp {
Equal, // =
PlusEqual, // +=
MinusEqual, // -=
StarEqual, // *=
SlashEqual, // /=
}
+54 -6
View File
@@ -141,8 +141,15 @@ impl Interpreter {
}), }),
} }
} }
Expr::Assign { name, value } => { Expr::Assign { name, op, value } => {
let val = self.evaluate(*value)?; let rhs = self.evaluate(*value)?;
let val = match op {
AssignOp::Equal => rhs,
_ => {
let current = self.env.borrow().get(&name).ok_or_else(|| RuntimeError::RuntimeError { message: format!("Undefined variable '{}'", name), token: None })?;
self.apply_assign_op(current.clone(), rhs, op)?
}
};
if !self.env.borrow_mut().assign(&name, val.clone()) { if !self.env.borrow_mut().assign(&name, val.clone()) {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: format!("Undefined variable '{}'", name), message: format!("Undefined variable '{}'", name),
@@ -169,11 +176,19 @@ impl Interpreter {
}), }),
} }
} }
Expr::Set { object, name, value } => { Expr::Set { object, name, op, value } => {
let obj = self.evaluate(*object)?; let obj = self.evaluate(*object)?;
let rhs = self.evaluate(*value)?;
match obj { match obj {
Value::Object(map) => { Value::Object(map) => {
let val = self.evaluate(*value)?; let val = match op {
AssignOp::Equal => rhs,
_ => {
let map_borrow = map.borrow();
let current = map_borrow.get(&name).ok_or_else(|| RuntimeError::RuntimeError { message: format!("Property '{}' does not exist", name), token: None })?;
self.apply_assign_op(current.clone(), rhs, op)?
}
};
map.borrow_mut().insert(name, val.clone()); map.borrow_mut().insert(name, val.clone());
Ok(val) Ok(val)
} }
@@ -208,10 +223,10 @@ impl Interpreter {
}), }),
} }
} }
Expr::IndexSet { array, index, value } => { Expr::IndexSet { array, index, op, value } => {
let arr = self.evaluate(*array)?; let arr = self.evaluate(*array)?;
let idx = self.evaluate(*index)?; let idx = self.evaluate(*index)?;
let val = self.evaluate(*value)?; let rhs = self.evaluate(*value)?;
let i = self.as_array_index(&idx)?; let i = self.as_array_index(&idx)?;
match arr { match arr {
Value::Array(vec) => { Value::Array(vec) => {
@@ -222,6 +237,13 @@ impl Interpreter {
token: None, token: None,
}); });
} }
let val = match op {
AssignOp::Equal => rhs,
_ => {
let current = vec[i].clone();
self.apply_assign_op(current, rhs, op)?
}
};
vec[i] = val.clone(); vec[i] = val.clone();
Ok(val) Ok(val)
} }
@@ -398,6 +420,32 @@ impl Interpreter {
} }
} }
fn apply_assign_op(&self, left: Value, right: Value, op: AssignOp) -> Result<Value, RuntimeError> {
match op {
AssignOp::Equal => Ok(right),
AssignOp::PlusEqual => {
let left_num = self.as_number(&left)?;
let right_num = self.as_number(&right)?;
Ok(Value::Number(left_num + right_num))
}
AssignOp::MinusEqual => {
let left_num = self.as_number(&left)?;
let right_num = self.as_number(&right)?;
Ok(Value::Number(left_num - right_num))
}
AssignOp::StarEqual => {
let left_num = self.as_number(&left)?;
let right_num = self.as_number(&right)?;
Ok(Value::Number(left_num * right_num))
}
AssignOp::SlashEqual => {
let left_num = self.as_number(&left)?;
let right_num = self.as_number(&right)?;
Ok(Value::Number(left_num / right_num))
}
}
}
fn as_number(&self, val: &Value) -> Result<f64, RuntimeError> { fn as_number(&self, val: &Value) -> Result<f64, RuntimeError> {
if let Value::Number(n) = val { if let Value::Number(n) = val {
Ok(*n) Ok(*n)
+23 -3
View File
@@ -56,9 +56,27 @@ impl Lexer {
',' => TokenKind::Comma, ',' => TokenKind::Comma,
';' => TokenKind::Semicolon, ';' => TokenKind::Semicolon,
'+' => TokenKind::Plus, '+' => {
'-' => TokenKind::Minus, if self.match_char('=') {
'*' => TokenKind::Star, TokenKind::PlusEqual
} else {
TokenKind::Plus
}
}
'-' => {
if self.match_char('=') {
TokenKind::MinusEqual
} else {
TokenKind::Minus
}
}
'*' => {
if self.match_char('=') {
TokenKind::StarEqual
} else {
TokenKind::Star
}
}
'.' => TokenKind::Dot, '.' => TokenKind::Dot,
':' => TokenKind::Colon, ':' => TokenKind::Colon,
'[' => TokenKind::LeftBracket, '[' => TokenKind::LeftBracket,
@@ -71,6 +89,8 @@ impl Lexer {
self.advance(); self.advance();
} }
return None; return None;
} else if self.match_char('=') {
TokenKind::SlashEqual
} else { } else {
TokenKind::Slash TokenKind::Slash
} }
+1
View File
@@ -16,6 +16,7 @@ pub enum TokenKind {
Less, LessEqual, Less, LessEqual,
EqualEqual, BangEqual, EqualEqual, BangEqual,
AndAnd, OrOr, AndAnd, OrOr,
PlusEqual, MinusEqual, StarEqual, SlashEqual,
// 字面量 // 字面量
Identifier(String), Identifier(String),
+18 -4
View File
@@ -202,12 +202,26 @@ impl Parser {
fn assignment(&mut self) -> Expr { fn assignment(&mut self) -> Expr {
let expr = self.logical_or(); let expr = self.logical_or();
if self.match_kind(&[TokenKind::Equal]) { let op = if self.match_kind(&[TokenKind::Equal]) {
Some(AssignOp::Equal)
} else if self.match_kind(&[TokenKind::PlusEqual]) {
Some(AssignOp::PlusEqual)
} else if self.match_kind(&[TokenKind::MinusEqual]) {
Some(AssignOp::MinusEqual)
} else if self.match_kind(&[TokenKind::StarEqual]) {
Some(AssignOp::StarEqual)
} else if self.match_kind(&[TokenKind::SlashEqual]) {
Some(AssignOp::SlashEqual)
} else {
None
};
if let Some(op) = op {
let value = self.assignment(); let value = self.assignment();
return match expr { return match expr {
Expr::Variable(name) => Expr::Assign { name, value: Box::new(value) }, Expr::Variable(name) => Expr::Assign { name, op, value: Box::new(value) },
Expr::Get { object, name } => Expr::Set { object, name, value: Box::new(value) }, Expr::Get { object, name } => Expr::Set { object, name, op, value: Box::new(value) },
Expr::IndexGet { array, index } => Expr::IndexSet { array, index, value: Box::new(value) }, Expr::IndexGet { array, index } => Expr::IndexSet { array, index, op, value: Box::new(value) },
_ => panic!("Invalid assignment target."), _ => panic!("Invalid assignment target."),
}; };
} }