feat: 添加数组字面量和索引访问功能,更新解析器和解释器以支持数组操作
This commit is contained in:
39
examples/array.ast
Normal file
39
examples/array.ast
Normal file
@@ -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)
|
||||||
@@ -32,6 +32,24 @@ pub enum Expr {
|
|||||||
properties: Vec<(String, Expr)>,
|
properties: Vec<(String, Expr)>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// 数组字面量:[ expr, expr, ... ]
|
||||||
|
ArrayLiteral {
|
||||||
|
elements: Vec<Expr>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 索引访问:expr[index]
|
||||||
|
IndexGet {
|
||||||
|
array: Box<Expr>,
|
||||||
|
index: Box<Expr>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 索引赋值:expr[index] = value
|
||||||
|
IndexSet {
|
||||||
|
array: Box<Expr>,
|
||||||
|
index: Box<Expr>,
|
||||||
|
value: Box<Expr>,
|
||||||
|
},
|
||||||
|
|
||||||
/// 一元运算:!expr / -expr
|
/// 一元运算:!expr / -expr
|
||||||
Unary {
|
Unary {
|
||||||
op: UnaryOp,
|
op: UnaryOp,
|
||||||
|
|||||||
@@ -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<char> = 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 } => {
|
Expr::ObjectLiteral { properties } => {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
for (key, value_expr) in properties {
|
for (key, value_expr) in properties {
|
||||||
@@ -167,6 +215,13 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
Ok(Value::Object(Rc::new(RefCell::new(map))))
|
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 } => {
|
Expr::Unary { op, right } => {
|
||||||
let val = self.evaluate(*right)?;
|
let val = self.evaluate(*right)?;
|
||||||
match op {
|
match op {
|
||||||
@@ -303,6 +358,12 @@ impl Interpreter {
|
|||||||
(Value::Bool(x), Value::Bool(y)) => x==y,
|
(Value::Bool(x), Value::Bool(y)) => x==y,
|
||||||
(Value::Number(x), Value::Number(y)) => x==y,
|
(Value::Number(x), Value::Number(y)) => x==y,
|
||||||
(Value::String(x), Value::String(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,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,5 +378,22 @@ impl Interpreter {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_array_index(&self, val: &Value) -> Result<usize, RuntimeError> {
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ pub enum Value {
|
|||||||
Bool(bool),
|
Bool(bool),
|
||||||
Nil,
|
Nil,
|
||||||
Object(Rc<RefCell<HashMap<String, Value>>>),
|
Object(Rc<RefCell<HashMap<String, Value>>>),
|
||||||
|
Array(Rc<RefCell<Vec<Value>>>),
|
||||||
Function(Rc<Function>),
|
Function(Rc<Function>),
|
||||||
NativeFunction(NativeFn),
|
NativeFunction(NativeFn),
|
||||||
}
|
}
|
||||||
@@ -31,6 +32,7 @@ impl std::fmt::Debug for Value {
|
|||||||
Value::Bool(b) => write!(f, "Bool({:?})", b),
|
Value::Bool(b) => write!(f, "Bool({:?})", b),
|
||||||
Value::Nil => write!(f, "Nil"),
|
Value::Nil => write!(f, "Nil"),
|
||||||
Value::Object(_) => write!(f, "Object(...)"),
|
Value::Object(_) => write!(f, "Object(...)"),
|
||||||
|
Value::Array(_) => write!(f, "Array(...)"),
|
||||||
Value::Function(_) => write!(f, "Function(...)"),
|
Value::Function(_) => write!(f, "Function(...)"),
|
||||||
Value::NativeFunction(_) => write!(f, "NativeFunction(...)"),
|
Value::NativeFunction(_) => write!(f, "NativeFunction(...)"),
|
||||||
}
|
}
|
||||||
@@ -52,6 +54,17 @@ impl fmt::Display for Value {
|
|||||||
}
|
}
|
||||||
write!(f, "}}")
|
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, "<function>"),
|
Value::Function(_) => write!(f, "<function>"),
|
||||||
Value::NativeFunction(_) => write!(f, "<native function>"),
|
Value::NativeFunction(_) => write!(f, "<native function>"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ impl Lexer {
|
|||||||
'*' => TokenKind::Star,
|
'*' => TokenKind::Star,
|
||||||
'.' => TokenKind::Dot,
|
'.' => TokenKind::Dot,
|
||||||
':' => TokenKind::Colon,
|
':' => TokenKind::Colon,
|
||||||
|
'[' => TokenKind::LeftBracket,
|
||||||
|
']' => TokenKind::RightBracket,
|
||||||
|
|
||||||
'/' => {
|
'/' => {
|
||||||
if self.match_char('/') {
|
if self.match_char('/') {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ pub enum TokenKind {
|
|||||||
// 单字符
|
// 单字符
|
||||||
LeftParen, RightParen,
|
LeftParen, RightParen,
|
||||||
LeftBrace, RightBrace,
|
LeftBrace, RightBrace,
|
||||||
|
LeftBracket, RightBracket,
|
||||||
Comma, Semicolon, Dot,
|
Comma, Semicolon, Dot,
|
||||||
Colon,
|
Colon,
|
||||||
|
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ impl Parser {
|
|||||||
return match expr {
|
return match expr {
|
||||||
Expr::Variable(name) => Expr::Assign { name, value: Box::new(value) },
|
Expr::Variable(name) => Expr::Assign { name, value: Box::new(value) },
|
||||||
Expr::Get { object, name } => Expr::Set { object, 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."),
|
_ => panic!("Invalid assignment target."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -354,6 +355,13 @@ impl Parser {
|
|||||||
object: Box::new(expr),
|
object: Box::new(expr),
|
||||||
name,
|
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 {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -410,6 +418,10 @@ impl Parser {
|
|||||||
return self.object_literal();
|
return self.object_literal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.match_kind(&[TokenKind::LeftBracket]) {
|
||||||
|
return self.array_literal();
|
||||||
|
}
|
||||||
|
|
||||||
panic!("Expected expression at line {}", self.peek().line);
|
panic!("Expected expression at line {}", self.peek().line);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,4 +504,18 @@ impl Parser {
|
|||||||
self.consume(TokenKind::RightBrace, "Expected '}' after object literal").unwrap();
|
self.consume(TokenKind::RightBrace, "Expected '}' after object literal").unwrap();
|
||||||
Expr::ObjectLiteral { properties }
|
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 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user