feat: 添加赋值运算符支持,更新解析器、解释器和词法分析器以处理相关语法
This commit is contained in:
@@ -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)
|
||||||
@@ -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, // /=
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user