From dd3f18da6f451de14518beefa830d8603b3dc934 Mon Sep 17 00:00:00 2001 From: 0264408 Date: Tue, 28 Apr 2026 10:14:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=95=B0=E7=BB=84?= =?UTF-8?q?=E5=AD=97=E9=9D=A2=E9=87=8F=E5=92=8C=E7=B4=A2=E5=BC=95=E8=AE=BF?= =?UTF-8?q?=E9=97=AE=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=9B=B4=E6=96=B0=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E5=99=A8=E5=92=8C=E8=A7=A3=E9=87=8A=E5=99=A8=E4=BB=A5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=95=B0=E7=BB=84=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/array.ast | 39 +++++++++++++++++ src/ast/expr.rs | 18 ++++++++ src/interpreter/interpreter.rs | 78 ++++++++++++++++++++++++++++++++++ src/interpreter/mod.rs | 13 ++++++ src/lexer/lexer.rs | 2 + src/lexer/token.rs | 1 + src/parser/parser.rs | 26 ++++++++++++ 7 files changed, 177 insertions(+) create mode 100644 examples/array.ast diff --git a/examples/array.ast b/examples/array.ast new file mode 100644 index 0000000..e47e4ee --- /dev/null +++ b/examples/array.ast @@ -0,0 +1,39 @@ +let arr = [10, 20, 30, 40, 50] +print(arr) + +print(arr[0] + arr[4]) + +let i = 1 +while(i < 4) { + print(arr[i]) + i = i + 1 +} + +arr[0] = 99 +print(arr) + +for (let j = 0; j < 3; j = j + 1) { + arr[j] = arr[j] * 2 +} +print(arr) + +let nested = [[1, 2], [3, 4], [5, 6]] +print(nested[1][0]) + +let empty = [] +print(empty) + +let mixed = [1, "hello", true, nil] +print(mixed) +print(mixed[1][0]) + +let same = [1, 2, 3] +let other = [1, 2, 3] +print(same == other) +print(same != [3, 2, 1]) + +let item = { name: "sword", price: 100 } +let items = [item, { name: "shield", price: 50 }] +print(items[0].name) +items[1].price = 75 +print(items[1].price) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 98fc2ed..f20ba39 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -32,6 +32,24 @@ pub enum Expr { properties: Vec<(String, Expr)>, }, + /// 数组字面量:[ expr, expr, ... ] + ArrayLiteral { + elements: Vec, + }, + + /// 索引访问:expr[index] + IndexGet { + array: Box, + index: Box, + }, + + /// 索引赋值:expr[index] = value + IndexSet { + array: Box, + index: Box, + value: Box, + }, + /// 一元运算:!expr / -expr Unary { op: UnaryOp, diff --git a/src/interpreter/interpreter.rs b/src/interpreter/interpreter.rs index ff31921..2dad083 100644 --- a/src/interpreter/interpreter.rs +++ b/src/interpreter/interpreter.rs @@ -159,6 +159,54 @@ impl Interpreter { }), } } + Expr::IndexGet { array, index } => { + let arr = self.evaluate(*array)?; + let idx = self.evaluate(*index)?; + let i = self.as_array_index(&idx)?; + match arr { + Value::Array(vec) => { + let vec = vec.borrow(); + vec.get(i).cloned().ok_or_else(|| RuntimeError::RuntimeError { + message: format!("Index {} out of bounds (len {})", i, vec.len()), + token: None, + }) + } + Value::String(s) => { + let chars: Vec = s.chars().collect(); + chars.get(i).map(|&c| Value::String(c.to_string())).ok_or_else(|| RuntimeError::RuntimeError { + message: format!("Index {} out of bounds (len {})", i, chars.len()), + token: None, + }) + } + _ => Err(RuntimeError::RuntimeError { + message: "Index access on non-array, non-string value".to_string(), + token: None, + }), + } + } + Expr::IndexSet { array, index, value } => { + let arr = self.evaluate(*array)?; + let idx = self.evaluate(*index)?; + let val = self.evaluate(*value)?; + let i = self.as_array_index(&idx)?; + match arr { + Value::Array(vec) => { + let mut vec = vec.borrow_mut(); + if i >= vec.len() { + return Err(RuntimeError::RuntimeError { + message: format!("Index {} out of bounds (len {})", i, vec.len()), + token: None, + }); + } + vec[i] = val.clone(); + Ok(val) + } + _ => Err(RuntimeError::RuntimeError { + message: "Index assignment on non-array value".to_string(), + token: None, + }), + } + } Expr::ObjectLiteral { properties } => { let mut map = HashMap::new(); for (key, value_expr) in properties { @@ -167,6 +215,13 @@ impl Interpreter { } Ok(Value::Object(Rc::new(RefCell::new(map)))) } + Expr::ArrayLiteral { elements } => { + let mut arr = Vec::new(); + for e in elements { + arr.push(self.evaluate(e)?); + } + Ok(Value::Array(Rc::new(RefCell::new(arr)))) + } Expr::Unary { op, right } => { let val = self.evaluate(*right)?; match op { @@ -303,6 +358,12 @@ impl Interpreter { (Value::Bool(x), Value::Bool(y)) => x==y, (Value::Number(x), Value::Number(y)) => x==y, (Value::String(x), Value::String(y)) => x==y, + (Value::Array(x), Value::Array(y)) => { + let x = x.borrow(); + let y = y.borrow(); + if x.len() != y.len() { return false; } + x.iter().zip(y.iter()).all(|(a,b)| self.is_equal(a,b)) + } _ => false, } } @@ -317,5 +378,22 @@ impl Interpreter { }) } } + + fn as_array_index(&self, val: &Value) -> Result { + if let Value::Number(n) = val { + if *n < 0.0 || n.fract() != 0.0 { + return Err(RuntimeError::RuntimeError { + message: format!("Index must be a non-negative integer, got {}", n), + token: None, + }); + } + Ok(*n as usize) + } else { + Err(RuntimeError::RuntimeError { + message: "Index must be a number".to_string(), + token: None, + }) + } + } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 5adaa9d..0f47eb0 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -19,6 +19,7 @@ pub enum Value { Bool(bool), Nil, Object(Rc>>), + Array(Rc>>), Function(Rc), NativeFunction(NativeFn), } @@ -31,6 +32,7 @@ impl std::fmt::Debug for Value { Value::Bool(b) => write!(f, "Bool({:?})", b), Value::Nil => write!(f, "Nil"), Value::Object(_) => write!(f, "Object(...)"), + Value::Array(_) => write!(f, "Array(...)"), Value::Function(_) => write!(f, "Function(...)"), Value::NativeFunction(_) => write!(f, "NativeFunction(...)"), } @@ -52,6 +54,17 @@ impl fmt::Display for Value { } write!(f, "}}") }, + Value::Array(arr) => { + let arr = arr.borrow(); + write!(f, "[")?; + for (i, val) in arr.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", val)?; + } + write!(f, "]") + }, Value::Function(_) => write!(f, ""), Value::NativeFunction(_) => write!(f, ""), } diff --git a/src/lexer/lexer.rs b/src/lexer/lexer.rs index 4509919..ce94db3 100644 --- a/src/lexer/lexer.rs +++ b/src/lexer/lexer.rs @@ -61,6 +61,8 @@ impl Lexer { '*' => TokenKind::Star, '.' => TokenKind::Dot, ':' => TokenKind::Colon, + '[' => TokenKind::LeftBracket, + ']' => TokenKind::RightBracket, '/' => { if self.match_char('/') { diff --git a/src/lexer/token.rs b/src/lexer/token.rs index 57a05f7..6640a53 100644 --- a/src/lexer/token.rs +++ b/src/lexer/token.rs @@ -4,6 +4,7 @@ pub enum TokenKind { // 单字符 LeftParen, RightParen, LeftBrace, RightBrace, + LeftBracket, RightBracket, Comma, Semicolon, Dot, Colon, diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 9a8543e..d107039 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -193,6 +193,7 @@ impl Parser { return match expr { Expr::Variable(name) => Expr::Assign { name, value: Box::new(value) }, Expr::Get { object, name } => Expr::Set { object, name, value: Box::new(value) }, + Expr::IndexGet { array, index } => Expr::IndexSet { array, index, value: Box::new(value) }, _ => panic!("Invalid assignment target."), }; } @@ -354,6 +355,13 @@ impl Parser { object: Box::new(expr), name, }; + } else if self.match_kind(&[TokenKind::LeftBracket]) { + let index = self.expression(); + self.consume(TokenKind::RightBracket, "Expected ']' after index.").unwrap(); + expr = Expr::IndexGet { + array: Box::new(expr), + index: Box::new(index), + }; } else { break; } @@ -410,6 +418,10 @@ impl Parser { return self.object_literal(); } + if self.match_kind(&[TokenKind::LeftBracket]) { + return self.array_literal(); + } + panic!("Expected expression at line {}", self.peek().line); } @@ -492,4 +504,18 @@ impl Parser { self.consume(TokenKind::RightBrace, "Expected '}' after object literal").unwrap(); Expr::ObjectLiteral { properties } } + + fn array_literal(&mut self) -> Expr { + let mut elements = Vec::new(); + if !self.check(&TokenKind::RightBracket) { + loop { + elements.push(self.expression()); + if !self.match_kind(&[TokenKind::Comma]) { + break; + } + } + } + self.consume(TokenKind::RightBracket, "Expected ']' after array literal").unwrap(); + Expr::ArrayLiteral { elements } + } } \ No newline at end of file