feat: IDE支持 — workspace拆分、LSP服务器、VS Code扩展
**Workspace 拆分** - aster-core: 纯库,zero-dependency,包含 lexer/parser/ast/interpreter/error/analysis - aster: REPL 二进制,薄封装 aster-core - aster-lsp: LSP 语言服务器 **VS Code 扩展 (vscode-ext/)** - TextMate 语法高亮 (.ast 文件) - 语言配置 (注释切换、括号配对、自动缩进) - LSP 客户端 (extension.js) **LSP 服务端功能** - Diagnostics: 实时显示 lex/parse 错误红色波浪线 - Completion: 关键字 + 内置函数 + 用户定义符号补全 - Hover: 悬停显示变量/函数信息 - Signature Help: 函数参数提示 - Goto Definition: Ctrl+Click 跳转到声明处 - Find References: 查找所有引用位置 - Rename: F2 重命名符号 **新增 analysis 模块** - collect_symbols: AST 遍历收集符号 - find_declaration/find_all_references: Token 扫描定位声明和引用
This commit is contained in:
@@ -0,0 +1,624 @@
|
||||
use crate::ast::*;
|
||||
use crate::ast::expr::{Literal, UnaryOp, BinaryOp, LogicalOp, AssignOp};
|
||||
use crate::error::RuntimeError;
|
||||
use crate::interpreter::Signal;
|
||||
use super::{Value, Env, Function};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
impl super::Interpreter {
|
||||
// ========================================================================
|
||||
// evaluate — dispatcher
|
||||
// ========================================================================
|
||||
|
||||
pub fn evaluate(&mut self, expr: Expr) -> Result<Value, RuntimeError> {
|
||||
match expr {
|
||||
Expr::Literal(lit) => self.eval_literal(lit),
|
||||
Expr::Variable(name) => self.eval_variable(name),
|
||||
Expr::Assign { name, op, value } => self.eval_assign(name, op, *value),
|
||||
Expr::Get { object, name } => self.eval_get(*object, name),
|
||||
Expr::Set { object, name, op, value } => self.eval_set(*object, name, op, *value),
|
||||
Expr::IndexGet { array, index } => self.eval_index_get(*array, *index),
|
||||
Expr::IndexSet { array, index, op, value } => self.eval_index_set(*array, *index, op, *value),
|
||||
Expr::ObjectLiteral { properties } => self.eval_object_literal(properties),
|
||||
Expr::ArrayLiteral { elements } => self.eval_array_literal(elements),
|
||||
Expr::Unary { op, right } => self.eval_unary(op, *right),
|
||||
Expr::Binary { left, op, right } => self.eval_binary(*left, op, *right),
|
||||
Expr::Logical { left, op, right } => self.eval_logical(*left, op, *right),
|
||||
Expr::Ternary { condition, then_branch, else_branch } => {
|
||||
self.eval_ternary(*condition, *then_branch, *else_branch)
|
||||
}
|
||||
Expr::Call { callee, arguments } => self.eval_call(*callee, arguments),
|
||||
Expr::Lambda { params, body } => self.eval_lambda(params, body),
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// eval_* methods
|
||||
// ========================================================================
|
||||
|
||||
fn eval_literal(&mut self, lit: Literal) -> Result<Value, RuntimeError> {
|
||||
Ok(match lit {
|
||||
Literal::Number(n) => Value::Number(n),
|
||||
Literal::String(s) => Value::String(s),
|
||||
Literal::Bool(b) => Value::Bool(b),
|
||||
Literal::Nil => Value::Nil,
|
||||
})
|
||||
}
|
||||
|
||||
fn eval_variable(&mut self, name: String) -> Result<Value, RuntimeError> {
|
||||
match self.env.borrow().get(&name) {
|
||||
Some(val) => Ok(val),
|
||||
None => Err(RuntimeError::RuntimeError {
|
||||
message: format!("Undefined variable '{}'", name),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_assign(&mut self, name: String, op: AssignOp, value: Expr) -> Result<Value, RuntimeError> {
|
||||
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)?
|
||||
}
|
||||
};
|
||||
match self.env.borrow_mut().assign(&name, val.clone()) {
|
||||
Ok(true) => {}
|
||||
Ok(false) => {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: format!("Undefined variable '{}'", name),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
Err(msg) => {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: msg,
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn eval_get(&mut self, object: Expr, name: String) -> Result<Value, RuntimeError> {
|
||||
let obj = self.evaluate(object)?;
|
||||
match obj {
|
||||
Value::Object(_) => {
|
||||
match obj.get(&name) {
|
||||
Some(val) => Ok(val),
|
||||
None => Err(RuntimeError::RuntimeError {
|
||||
message: format!("Undefined property '{}'", name),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
Value::Array(arr) => match name.as_str() {
|
||||
"length" => Ok(Value::Number(arr.borrow().len() as f64)),
|
||||
"push" => {
|
||||
let arr = Rc::clone(&arr);
|
||||
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, mut args: Vec<Value>| {
|
||||
let val = args.pop().unwrap_or(Value::Nil);
|
||||
arr.borrow_mut().push(val.clone());
|
||||
Ok(val)
|
||||
})))
|
||||
}
|
||||
"pop" => {
|
||||
let arr = Rc::clone(&arr);
|
||||
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, _args: Vec<Value>| {
|
||||
arr.borrow_mut()
|
||||
.pop()
|
||||
.ok_or_else(|| RuntimeError::RuntimeError {
|
||||
message: "pop() on empty array".into(),
|
||||
token: None,
|
||||
})
|
||||
})))
|
||||
}
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: format!("Array has no property '{}'", name),
|
||||
token: None,
|
||||
}),
|
||||
},
|
||||
Value::String(s) => match name.as_str() {
|
||||
"length" => Ok(Value::Number(s.chars().count() as f64)),
|
||||
"upper" => {
|
||||
let s = s.clone();
|
||||
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| {
|
||||
let mut all_args = vec![Value::String(s.clone())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::upper(_interp, all_args)
|
||||
})))
|
||||
}
|
||||
"lower" => {
|
||||
let s = s.clone();
|
||||
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| {
|
||||
let mut all_args = vec![Value::String(s.clone())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::lower(_interp, all_args)
|
||||
})))
|
||||
}
|
||||
"trim" => {
|
||||
let s = s.clone();
|
||||
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| {
|
||||
let mut all_args = vec![Value::String(s.clone())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::trim(_interp, all_args)
|
||||
})))
|
||||
}
|
||||
"substring" => {
|
||||
let s = s.clone();
|
||||
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| {
|
||||
let mut all_args = vec![Value::String(s.clone())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::substring(_interp, all_args)
|
||||
})))
|
||||
}
|
||||
"replace" => {
|
||||
let s = s.clone();
|
||||
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| {
|
||||
let mut all_args = vec![Value::String(s.clone())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::replace(_interp, all_args)
|
||||
})))
|
||||
}
|
||||
"contains" => {
|
||||
let s = s.clone();
|
||||
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| {
|
||||
let mut all_args = vec![Value::String(s.clone())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::contains(_interp, all_args)
|
||||
})))
|
||||
}
|
||||
"starts_with" => {
|
||||
let s = s.clone();
|
||||
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| {
|
||||
let mut all_args = vec![Value::String(s.clone())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::starts_with(_interp, all_args)
|
||||
})))
|
||||
}
|
||||
"ends_with" => {
|
||||
let s = s.clone();
|
||||
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| {
|
||||
let mut all_args = vec![Value::String(s.clone())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::ends_with(_interp, all_args)
|
||||
})))
|
||||
}
|
||||
"split" => {
|
||||
let s = s.clone();
|
||||
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| {
|
||||
let mut all_args = vec![Value::String(s.clone())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::split(_interp, all_args)
|
||||
})))
|
||||
}
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: format!("String has no property '{}'", name),
|
||||
token: None,
|
||||
}),
|
||||
},
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: "Only objects have properties".to_string(),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_set(&mut self, object: Expr, name: String, op: AssignOp, value: Expr) -> Result<Value, RuntimeError> {
|
||||
let obj = self.evaluate(object)?;
|
||||
let rhs = self.evaluate(value)?;
|
||||
match obj {
|
||||
Value::Object(_) => {
|
||||
let val = match op {
|
||||
AssignOp::Equal => rhs,
|
||||
_ => {
|
||||
let current = obj.get(&name).ok_or_else(|| RuntimeError::RuntimeError {
|
||||
message: format!("Property '{}' does not exist", name),
|
||||
token: None,
|
||||
})?;
|
||||
self.apply_assign_op(current, rhs, op)?
|
||||
}
|
||||
};
|
||||
obj.set(&name, val.clone())?;
|
||||
Ok(val)
|
||||
}
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: "Only objects have properties".to_string(),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_index_get(&mut self, array: Expr, index: Expr) -> Result<Value, RuntimeError> {
|
||||
let target = self.evaluate(array)?;
|
||||
let idx_val = self.evaluate(index)?;
|
||||
// Object index access: obj["key"]
|
||||
if let Value::Object(_) = &target {
|
||||
let key = match &idx_val {
|
||||
Value::String(s) => s.clone(),
|
||||
_ => return Err(RuntimeError::RuntimeError {
|
||||
message: "Object index must be a string".into(),
|
||||
token: None,
|
||||
}),
|
||||
};
|
||||
return target.get(&key).ok_or_else(|| RuntimeError::RuntimeError {
|
||||
message: format!("Undefined property '{}'", key),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
let i = self.as_array_index(&idx_val)?;
|
||||
match target {
|
||||
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, non-object value".to_string(),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_index_set(&mut self, array: Expr, index: Expr, op: AssignOp, value: Expr) -> Result<Value, RuntimeError> {
|
||||
let target = self.evaluate(array)?;
|
||||
let idx_val = self.evaluate(index)?;
|
||||
let rhs = self.evaluate(value)?;
|
||||
// Object index assignment: obj["key"] = value
|
||||
if let Value::Object(_) = &target {
|
||||
let key = match &idx_val {
|
||||
Value::String(s) => s.clone(),
|
||||
_ => return Err(RuntimeError::RuntimeError {
|
||||
message: "Object index must be a string".into(),
|
||||
token: None,
|
||||
}),
|
||||
};
|
||||
let val = match op {
|
||||
AssignOp::Equal => rhs,
|
||||
_ => {
|
||||
let current = target.get(&key).ok_or_else(|| RuntimeError::RuntimeError {
|
||||
message: format!("Property '{}' does not exist", key),
|
||||
token: None,
|
||||
})?;
|
||||
self.apply_assign_op(current, rhs, op)?
|
||||
}
|
||||
};
|
||||
target.set(&key, val.clone())?;
|
||||
return Ok(val);
|
||||
}
|
||||
let i = self.as_array_index(&idx_val)?;
|
||||
match target {
|
||||
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,
|
||||
});
|
||||
}
|
||||
let val = match op {
|
||||
AssignOp::Equal => rhs,
|
||||
_ => {
|
||||
let current = vec[i].clone();
|
||||
self.apply_assign_op(current, rhs, op)?
|
||||
}
|
||||
};
|
||||
vec[i] = val.clone();
|
||||
Ok(val)
|
||||
}
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: "Index assignment on non-array, non-object value".to_string(),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_object_literal(&mut self, properties: Vec<(String, Expr)>) -> Result<Value, RuntimeError> {
|
||||
let mut map = HashMap::new();
|
||||
for (key, value_expr) in properties {
|
||||
let value = self.evaluate(value_expr)?;
|
||||
map.insert(key, value);
|
||||
}
|
||||
Ok(Value::Object(Rc::new(RefCell::new(map))))
|
||||
}
|
||||
|
||||
fn eval_array_literal(&mut self, elements: Vec<Expr>) -> Result<Value, RuntimeError> {
|
||||
let mut arr = Vec::new();
|
||||
for e in elements {
|
||||
arr.push(self.evaluate(e)?);
|
||||
}
|
||||
Ok(Value::Array(Rc::new(RefCell::new(arr))))
|
||||
}
|
||||
|
||||
fn eval_unary(&mut self, op: UnaryOp, right: Expr) -> Result<Value, RuntimeError> {
|
||||
let val = self.evaluate(right)?;
|
||||
match op {
|
||||
UnaryOp::Negate => match val {
|
||||
Value::Number(n) => Ok(Value::Number(-n)),
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: "Unary '-' on non-number".to_string(),
|
||||
token: None,
|
||||
}),
|
||||
},
|
||||
UnaryOp::Not => Ok(Value::Bool(!self.is_truthy(&val))),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_binary(&mut self, left: Expr, op: BinaryOp, right: Expr) -> Result<Value, RuntimeError> {
|
||||
let l = self.evaluate(left)?;
|
||||
let r = self.evaluate(right)?;
|
||||
match op {
|
||||
BinaryOp::Add => self.eval_binary_add(l, r),
|
||||
BinaryOp::Sub => self.eval_binary_arith(l, r, |a, b| a - b, "-"),
|
||||
BinaryOp::Mul => self.eval_binary_arith(l, r, |a, b| a * b, "*"),
|
||||
BinaryOp::Div => self.eval_binary_div(l, r),
|
||||
BinaryOp::Mod => self.eval_binary_mod(l, r),
|
||||
BinaryOp::Greater => Ok(Value::Bool(self.as_number(&l)? > self.as_number(&r)?)),
|
||||
BinaryOp::GreaterEqual => Ok(Value::Bool(self.as_number(&l)? >= self.as_number(&r)?)),
|
||||
BinaryOp::Less => Ok(Value::Bool(self.as_number(&l)? < self.as_number(&r)?)),
|
||||
BinaryOp::LessEqual => Ok(Value::Bool(self.as_number(&l)? <= self.as_number(&r)?)),
|
||||
BinaryOp::Equal => Ok(Value::Bool(self.is_equal(&l, &r))),
|
||||
BinaryOp::NotEqual => Ok(Value::Bool(!self.is_equal(&l, &r))),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_binary_add(&mut self, l: Value, r: Value) -> Result<Value, RuntimeError> {
|
||||
if matches!(l, Value::String(_)) || matches!(r, Value::String(_)) {
|
||||
Ok(Value::String(format!("{}{}", l, r)))
|
||||
} else {
|
||||
match (l, r) {
|
||||
(Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: "Invalid '+' operands".to_string(),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_binary_arith(
|
||||
&mut self,
|
||||
l: Value,
|
||||
r: Value,
|
||||
op_fn: fn(f64, f64) -> f64,
|
||||
name: &str,
|
||||
) -> Result<Value, RuntimeError> {
|
||||
match (l, r) {
|
||||
(Value::Number(a), Value::Number(b)) => Ok(Value::Number(op_fn(a, b))),
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: format!("Invalid '{}' operands", name),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_binary_div(&mut self, l: Value, r: Value) -> Result<Value, RuntimeError> {
|
||||
match (l, r) {
|
||||
(Value::Number(_), Value::Number(b)) if b == 0.0 => Err(RuntimeError::RuntimeError {
|
||||
message: "Division by zero".to_string(),
|
||||
token: None,
|
||||
}),
|
||||
(Value::Number(a), Value::Number(b)) => Ok(Value::Number(a / b)),
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: "Invalid '/' operands".to_string(),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_binary_mod(&mut self, l: Value, r: Value) -> Result<Value, RuntimeError> {
|
||||
match (l, r) {
|
||||
(Value::Number(_), Value::Number(b)) if b == 0.0 => Err(RuntimeError::RuntimeError {
|
||||
message: "Modulo by zero".to_string(),
|
||||
token: None,
|
||||
}),
|
||||
(Value::Number(a), Value::Number(b)) => Ok(Value::Number(a % b)),
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: "Invalid '%' operands".to_string(),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_logical(&mut self, left: Expr, op: LogicalOp, right: Expr) -> Result<Value, RuntimeError> {
|
||||
let l = self.evaluate(left)?;
|
||||
match op {
|
||||
LogicalOp::And => {
|
||||
Ok(if !self.is_truthy(&l) { l } else { self.evaluate(right)? })
|
||||
}
|
||||
LogicalOp::Or => {
|
||||
Ok(if self.is_truthy(&l) { l } else { self.evaluate(right)? })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_ternary(&mut self, condition: Expr, then_branch: Expr, else_branch: Expr) -> Result<Value, RuntimeError> {
|
||||
let cond = self.evaluate(condition)?;
|
||||
if self.is_truthy(&cond) {
|
||||
self.evaluate(then_branch)
|
||||
} else {
|
||||
self.evaluate(else_branch)
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_call(&mut self, callee: Expr, arguments: Vec<Expr>) -> Result<Value, RuntimeError> {
|
||||
let func = self.evaluate(callee)?;
|
||||
let mut args = Vec::new();
|
||||
for e in arguments {
|
||||
args.push(self.evaluate(e)?);
|
||||
}
|
||||
self.call_function(func, args)
|
||||
}
|
||||
|
||||
fn eval_lambda(&mut self, params: Vec<String>, body: Vec<Stmt>) -> Result<Value, RuntimeError> {
|
||||
Ok(Value::Function(Rc::new(Function {
|
||||
params,
|
||||
body,
|
||||
env: Rc::clone(&self.env),
|
||||
name: None,
|
||||
})))
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// call_function
|
||||
// ========================================================================
|
||||
|
||||
pub fn call_function(&mut self, func_val: Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
match func_val {
|
||||
Value::NativeFunction(native_fn) => native_fn(self, args),
|
||||
Value::Function(f) => self.call_user_function(f, args),
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: "Attempt to call non-function".to_string(),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn call_user_function(&mut self, f: Rc<Function>, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
let env = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&f.env)))));
|
||||
|
||||
if let Some(name) = &f.name {
|
||||
env.borrow_mut().define(name.clone(), Value::Function(Rc::clone(&f)), true);
|
||||
}
|
||||
|
||||
for (i, param) in f.params.iter().enumerate() {
|
||||
let val = args.get(i).cloned().unwrap_or(Value::Nil);
|
||||
env.borrow_mut().define(param.clone(), val, true);
|
||||
}
|
||||
|
||||
let previous = Rc::clone(&self.env);
|
||||
self.env = env;
|
||||
let mut ret = Value::Nil;
|
||||
for stmt in &f.body {
|
||||
match self.execute(stmt.clone())? {
|
||||
Signal::Return(val) => { ret = val; break; }
|
||||
Signal::None => {}
|
||||
Signal::Break | Signal::Continue => {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "break/continue outside of loop".to_string(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
self.env = previous;
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Helpers
|
||||
// ========================================================================
|
||||
|
||||
pub fn is_truthy(&self, val: &Value) -> bool {
|
||||
match val {
|
||||
Value::Nil => false,
|
||||
Value::Bool(b) => *b,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_equal(&self, a: &Value, b: &Value) -> bool {
|
||||
match (a, b) {
|
||||
(Value::Nil, Value::Nil) => true,
|
||||
(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))
|
||||
}
|
||||
(Value::Object(x), Value::Object(y)) => {
|
||||
let x = x.borrow();
|
||||
let y = y.borrow();
|
||||
if x.len() != y.len() { return false; }
|
||||
x.iter().all(|(k, v)| {
|
||||
y.get(k).map_or(false, |yv| self.is_equal(v, yv))
|
||||
})
|
||||
}
|
||||
(Value::Function(x), Value::Function(y)) => Rc::ptr_eq(x, y),
|
||||
(Value::NativeFunction(x), Value::NativeFunction(y)) => Rc::ptr_eq(x, y),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_assign_op(&self, left: Value, right: Value, op: AssignOp) -> Result<Value, RuntimeError> {
|
||||
match op {
|
||||
AssignOp::Equal => Ok(right),
|
||||
AssignOp::PlusEqual => {
|
||||
if let Value::String(s) = &left {
|
||||
Ok(Value::String(format!("{}{}", s, right)))
|
||||
} else {
|
||||
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))
|
||||
}
|
||||
AssignOp::PercentEqual => {
|
||||
let left_num = self.as_number(&left)?;
|
||||
let right_num = self.as_number(&right)?;
|
||||
Ok(Value::Number(left_num % right_num))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_number(&self, val: &Value) -> Result<f64, RuntimeError> {
|
||||
if let Value::Number(n) = val {
|
||||
Ok(*n)
|
||||
} else {
|
||||
Err(RuntimeError::RuntimeError {
|
||||
message: "Expected number".to_string(),
|
||||
token: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user