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,84 @@
|
||||
//! 标准库:core(len, typeof, push, pop)
|
||||
|
||||
use crate::error::RuntimeError;
|
||||
use crate::interpreter::{Interpreter, Value};
|
||||
|
||||
pub fn len(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.is_empty() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "len() expects 1 argument".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
match &args[0] {
|
||||
Value::String(s) => Ok(Value::Number(s.chars().count() as f64)),
|
||||
Value::Array(arr) => Ok(Value::Number(arr.borrow().len() as f64)),
|
||||
Value::Object(obj) => Ok(Value::Number(obj.borrow().len() as f64)),
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: "len() expects a string, array, or object".into(),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn typeof_fn(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.is_empty() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "typeof() expects 1 argument".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
let s = match &args[0] {
|
||||
Value::Number(_) => "number",
|
||||
Value::String(_) => "string",
|
||||
Value::Bool(_) => "bool",
|
||||
Value::Nil => "nil",
|
||||
Value::Object(_) => "object",
|
||||
Value::Array(_) => "array",
|
||||
Value::Function(_) => "function",
|
||||
Value::NativeFunction(_) => "native_function",
|
||||
};
|
||||
Ok(Value::String(s.into()))
|
||||
}
|
||||
|
||||
pub fn push(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.len() < 2 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "push() expects 2 arguments (array, value)".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
let val = args[1].clone();
|
||||
match &args[0] {
|
||||
Value::Array(arr) => {
|
||||
arr.borrow_mut().push(val.clone());
|
||||
Ok(val)
|
||||
}
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: "push() expects an array as first argument".into(),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.is_empty() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "pop() expects 1 argument (array)".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
match &args[0] {
|
||||
Value::Array(arr) => arr
|
||||
.borrow_mut()
|
||||
.pop()
|
||||
.ok_or_else(|| RuntimeError::RuntimeError {
|
||||
message: "pop() on empty array".into(),
|
||||
token: None,
|
||||
}),
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: "pop() expects an array as first argument".into(),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//! 标准库:io(print, input)
|
||||
|
||||
use crate::error::RuntimeError;
|
||||
use crate::interpreter::{Interpreter, Value};
|
||||
|
||||
pub fn print(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
for arg in args.iter() {
|
||||
print!("{}", arg);
|
||||
}
|
||||
println!();
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
pub fn input(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if let Some(prompt) = args.get(0) {
|
||||
print!("{}", prompt);
|
||||
std::io::Write::flush(&mut std::io::stdout()).unwrap();
|
||||
}
|
||||
let mut buffer = String::new();
|
||||
std::io::stdin().read_line(&mut buffer).unwrap();
|
||||
Ok(Value::String(buffer.trim_end().to_string()))
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//! 内置函数模块:按功能分组注册,便于扩展和维护。
|
||||
|
||||
mod core;
|
||||
mod io;
|
||||
mod os;
|
||||
pub mod string;
|
||||
|
||||
use crate::interpreter::{Env, Value};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// 向全局环境注册所有标准库(io、os 等)
|
||||
pub fn register_all(env: &Rc<RefCell<Env>>) {
|
||||
let mut e = env.borrow_mut();
|
||||
|
||||
// io
|
||||
let mut io = HashMap::new();
|
||||
io.insert("print".into(), Value::NativeFunction(Rc::new(io::print)));
|
||||
io.insert("input".into(), Value::NativeFunction(Rc::new(io::input)));
|
||||
e.define("io".into(), Value::Object(Rc::new(RefCell::new(io))), true);
|
||||
// input and print can be used as global functions for convenience
|
||||
e.define("print".into(), Value::NativeFunction(Rc::new(io::print)), true);
|
||||
e.define("input".into(), Value::NativeFunction(Rc::new(io::input)), true);
|
||||
|
||||
// os
|
||||
let mut os = HashMap::new();
|
||||
os.insert("clock".into(), Value::NativeFunction(Rc::new(os::clock)));
|
||||
e.define("os".into(), Value::Object(Rc::new(RefCell::new(os))), true);
|
||||
// clock can also be used as a global function for convenience
|
||||
e.define("clock".into(), Value::NativeFunction(Rc::new(os::clock)), true);
|
||||
|
||||
// core — len, typeof, push, pop
|
||||
e.define("len".into(), Value::NativeFunction(Rc::new(core::len)), true);
|
||||
e.define("typeof".into(), Value::NativeFunction(Rc::new(core::typeof_fn)), true);
|
||||
e.define("push".into(), Value::NativeFunction(Rc::new(core::push)), true);
|
||||
e.define("pop".into(), Value::NativeFunction(Rc::new(core::pop)), true);
|
||||
|
||||
// require — module loader
|
||||
e.define("require".into(), Value::NativeFunction(Rc::new(super::module::require_fn)), true);
|
||||
|
||||
// string — split, trim, substring, replace, contains, upper, lower, starts_with, ends_with
|
||||
e.define("split".into(), Value::NativeFunction(Rc::new(string::split)), true);
|
||||
e.define("trim".into(), Value::NativeFunction(Rc::new(string::trim)), true);
|
||||
e.define("substring".into(), Value::NativeFunction(Rc::new(string::substring)), true);
|
||||
e.define("replace".into(), Value::NativeFunction(Rc::new(string::replace)), true);
|
||||
e.define("contains".into(), Value::NativeFunction(Rc::new(string::contains)), true);
|
||||
e.define("upper".into(), Value::NativeFunction(Rc::new(string::upper)), true);
|
||||
e.define("lower".into(), Value::NativeFunction(Rc::new(string::lower)), true);
|
||||
e.define("starts_with".into(), Value::NativeFunction(Rc::new(string::starts_with)), true);
|
||||
e.define("ends_with".into(), Value::NativeFunction(Rc::new(string::ends_with)), true);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//! 标准库:os(clock)
|
||||
|
||||
use crate::error::RuntimeError;
|
||||
use crate::interpreter::{Interpreter, Value};
|
||||
|
||||
pub fn clock(_interp: &mut Interpreter, _args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
Ok(Value::Number(
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs_f64(),
|
||||
))
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
//! 标准库:string(split, trim, substring, replace, contains, upper, lower, starts_with, ends_with)
|
||||
|
||||
use crate::error::RuntimeError;
|
||||
use crate::interpreter::{Interpreter, Value};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
fn require_string(val: &Value, arg_name: &str) -> Result<String, RuntimeError> {
|
||||
match val {
|
||||
Value::String(s) => Ok(s.clone()),
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: format!("{} must be a string", arg_name),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// split(str, delim) → array of substrings
|
||||
// ============================================================================
|
||||
|
||||
pub fn split(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.len() < 2 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "split() expects 2 arguments (string, delimiter)".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
let s = require_string(&args[0], "First argument")?;
|
||||
let delim = require_string(&args[1], "Second argument (delimiter)")?;
|
||||
|
||||
let parts: Vec<Value> = if delim.is_empty() {
|
||||
// Split by empty string → characters
|
||||
s.chars().map(|c| Value::String(c.to_string())).collect()
|
||||
} else {
|
||||
s.split(&delim).map(|p| Value::String(p.to_string())).collect()
|
||||
};
|
||||
|
||||
Ok(Value::Array(Rc::new(RefCell::new(parts))))
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// trim(str) → string with leading/trailing whitespace removed
|
||||
// ============================================================================
|
||||
|
||||
pub fn trim(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.is_empty() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "trim() expects 1 argument (string)".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
let s = require_string(&args[0], "Argument")?;
|
||||
Ok(Value::String(s.trim().to_string()))
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// substring(str, start, len?) → substring
|
||||
// ============================================================================
|
||||
|
||||
pub fn substring(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.is_empty() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "substring() expects at least 2 arguments (string, start, len?)".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
let s = require_string(&args[0], "First argument")?;
|
||||
let start = as_usize(&args[1], "Second argument (start)")?;
|
||||
let chars: Vec<char> = s.chars().collect();
|
||||
let start = start.min(chars.len());
|
||||
let end = if args.len() >= 3 {
|
||||
(start + as_usize(&args[2], "Third argument (length)")?).min(chars.len())
|
||||
} else {
|
||||
chars.len()
|
||||
};
|
||||
Ok(Value::String(chars[start..end].iter().collect()))
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// replace(str, old, new) → string with all occurrences replaced
|
||||
// ============================================================================
|
||||
|
||||
pub fn replace(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.len() < 3 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "replace() expects 3 arguments (string, old, new)".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
let s = require_string(&args[0], "First argument")?;
|
||||
let old = require_string(&args[1], "Second argument (old)")?;
|
||||
let new = require_string(&args[2], "Third argument (new)")?;
|
||||
|
||||
if old.is_empty() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "replace(): 'old' must not be empty".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Value::String(s.replace(&old, &new)))
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// contains(str, needle) → bool
|
||||
// ============================================================================
|
||||
|
||||
pub fn contains(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.len() < 2 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "contains() expects 2 arguments (string, needle)".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
let s = require_string(&args[0], "First argument")?;
|
||||
let needle = require_string(&args[1], "Second argument (needle)")?;
|
||||
Ok(Value::Bool(s.contains(&needle)))
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// upper(str) → uppercase (ASCII)
|
||||
// ============================================================================
|
||||
|
||||
pub fn upper(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.is_empty() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "upper() expects 1 argument (string)".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
let s = require_string(&args[0], "Argument")?;
|
||||
Ok(Value::String(s.to_ascii_uppercase()))
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// lower(str) → lowercase (ASCII)
|
||||
// ============================================================================
|
||||
|
||||
pub fn lower(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.is_empty() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "lower() expects 1 argument (string)".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
let s = require_string(&args[0], "Argument")?;
|
||||
Ok(Value::String(s.to_ascii_lowercase()))
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// starts_with(str, prefix) → bool
|
||||
// ============================================================================
|
||||
|
||||
pub fn starts_with(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.len() < 2 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "starts_with() expects 2 arguments (string, prefix)".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
let s = require_string(&args[0], "First argument")?;
|
||||
let prefix = require_string(&args[1], "Second argument (prefix)")?;
|
||||
Ok(Value::Bool(s.starts_with(&prefix)))
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ends_with(str, suffix) → bool
|
||||
// ============================================================================
|
||||
|
||||
pub fn ends_with(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if args.len() < 2 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "ends_with() expects 2 arguments (string, suffix)".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
let s = require_string(&args[0], "First argument")?;
|
||||
let suffix = require_string(&args[1], "Second argument (suffix)")?;
|
||||
Ok(Value::Bool(s.ends_with(&suffix)))
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Helper
|
||||
// ============================================================================
|
||||
|
||||
fn as_usize(val: &Value, arg_name: &str) -> Result<usize, RuntimeError> {
|
||||
match val {
|
||||
Value::Number(n) => {
|
||||
if *n < 0.0 || n.fract() != 0.0 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: format!("{} must be a non-negative integer, got {}", arg_name, n),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
Ok(*n as usize)
|
||||
}
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: format!("{} must be a number", arg_name),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
use super::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Env {
|
||||
/// (value, is_mutable) — `is_mutable` is true for `let`, false for `const`.
|
||||
pub values: HashMap<String, (Value, bool)>,
|
||||
pub parent: Option<Rc<RefCell<Env>>>,
|
||||
}
|
||||
|
||||
impl Env {
|
||||
pub fn new(parent: Option<Rc<RefCell<Env>>>) -> Self {
|
||||
Self {
|
||||
values: HashMap::new(),
|
||||
parent,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn define(&mut self, name: String, val: Value, mutable: bool) {
|
||||
self.values.insert(name, (val, mutable));
|
||||
}
|
||||
|
||||
/// Assign a new value to an existing binding. Returns `Err(msg)` if the
|
||||
/// binding exists but was declared with `const` (immutable).
|
||||
pub fn assign(&mut self, name: &str, val: Value) -> Result<bool, String> {
|
||||
if let Some((_, false)) = self.values.get(name) {
|
||||
return Err(format!("Cannot reassign constant '{}'", name));
|
||||
}
|
||||
if self.values.contains_key(name) {
|
||||
self.values.insert(name.to_string(), (val, true));
|
||||
Ok(true)
|
||||
} else if let Some(parent) = &self.parent {
|
||||
parent.borrow_mut().assign(name, val)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> Option<Value> {
|
||||
if let Some((val, _)) = self.values.get(name) {
|
||||
Some(val.clone())
|
||||
} else if let Some(parent) = &self.parent {
|
||||
parent.borrow().get(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
use crate::ast::*;
|
||||
use crate::error::RuntimeError;
|
||||
use crate::interpreter::Signal;
|
||||
use super::{Value, Env, Function};
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
impl super::Interpreter {
|
||||
// ========================================================================
|
||||
// execute — dispatcher
|
||||
// ========================================================================
|
||||
|
||||
pub fn execute(&mut self, stmt: Stmt) -> Result<Signal, RuntimeError> {
|
||||
match stmt {
|
||||
Stmt::Let { name, initializer, mutable } => self.exec_let(name, initializer, mutable),
|
||||
Stmt::ExprStmt(expr) => self.exec_expr_stmt(expr),
|
||||
Stmt::Block(stmts) => self.exec_block(stmts),
|
||||
Stmt::If { condition, then_branch, else_branch } => {
|
||||
self.exec_if(condition, *then_branch, else_branch.map(|b| *b))
|
||||
}
|
||||
Stmt::While { condition, body } => self.exec_while(condition, *body),
|
||||
Stmt::For { initializer, condition, step, body } => {
|
||||
self.exec_for(initializer, condition, step, *body)
|
||||
}
|
||||
Stmt::ForIn { var_name, iterable, body } => self.exec_for_in(var_name, iterable, *body),
|
||||
Stmt::Function { name, params, body } => self.exec_function(name, params, body),
|
||||
Stmt::Return(expr_opt) => self.exec_return(expr_opt),
|
||||
Stmt::Break => Ok(Signal::Break),
|
||||
Stmt::Continue => Ok(Signal::Continue),
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// exec_* methods
|
||||
// ========================================================================
|
||||
|
||||
fn exec_let(&mut self, name: String, initializer: Expr, mutable: bool) -> Result<Signal, RuntimeError> {
|
||||
let val = self.evaluate(initializer)?;
|
||||
self.env.borrow_mut().define(name, val, mutable);
|
||||
Ok(Signal::None)
|
||||
}
|
||||
|
||||
fn exec_expr_stmt(&mut self, expr: Expr) -> Result<Signal, RuntimeError> {
|
||||
self.evaluate(expr)?;
|
||||
Ok(Signal::None)
|
||||
}
|
||||
|
||||
fn exec_block(&mut self, stmts: Vec<Stmt>) -> Result<Signal, RuntimeError> {
|
||||
let previous = Rc::clone(&self.env);
|
||||
self.env = Rc::new(RefCell::new(Env::new(Some(previous))));
|
||||
let mut signal = Signal::None;
|
||||
for s in stmts {
|
||||
signal = self.execute(s)?;
|
||||
if !matches!(signal, Signal::None) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let parent = self.env.borrow().parent.as_ref().unwrap().clone();
|
||||
self.env = parent;
|
||||
Ok(signal)
|
||||
}
|
||||
|
||||
fn exec_if(
|
||||
&mut self,
|
||||
condition: Expr,
|
||||
then_branch: Stmt,
|
||||
else_branch: Option<Stmt>,
|
||||
) -> Result<Signal, RuntimeError> {
|
||||
let cond_val = self.evaluate(condition)?;
|
||||
if self.is_truthy(&cond_val) {
|
||||
self.execute(then_branch)
|
||||
} else if let Some(else_branch) = else_branch {
|
||||
self.execute(else_branch)
|
||||
} else {
|
||||
Ok(Signal::None)
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_while(&mut self, condition: Expr, body: Stmt) -> Result<Signal, RuntimeError> {
|
||||
loop {
|
||||
let cond_val = self.evaluate(condition.clone())?;
|
||||
if !self.is_truthy(&cond_val) {
|
||||
break;
|
||||
}
|
||||
match self.execute(body.clone())? {
|
||||
Signal::Break => break,
|
||||
Signal::Continue => continue,
|
||||
sig @ Signal::Return(_) => return Ok(sig),
|
||||
Signal::None => {}
|
||||
}
|
||||
}
|
||||
Ok(Signal::None)
|
||||
}
|
||||
|
||||
fn exec_for(
|
||||
&mut self,
|
||||
initializer: Option<Box<Stmt>>,
|
||||
condition: Option<Expr>,
|
||||
step: Option<Expr>,
|
||||
body: Stmt,
|
||||
) -> Result<Signal, RuntimeError> {
|
||||
if let Some(init) = initializer {
|
||||
self.execute(*init)?;
|
||||
}
|
||||
loop {
|
||||
if let Some(cond) = &condition {
|
||||
let cond_val = self.evaluate(cond.clone())?;
|
||||
if !self.is_truthy(&cond_val) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
match self.execute(body.clone())? {
|
||||
Signal::Break => break,
|
||||
Signal::Continue => {
|
||||
if let Some(step) = &step {
|
||||
self.evaluate(step.clone())?;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
sig @ Signal::Return(_) => return Ok(sig),
|
||||
Signal::None => {}
|
||||
}
|
||||
if let Some(step) = &step {
|
||||
self.evaluate(step.clone())?;
|
||||
}
|
||||
}
|
||||
Ok(Signal::None)
|
||||
}
|
||||
|
||||
fn exec_for_in(
|
||||
&mut self,
|
||||
var_name: String,
|
||||
iterable: Expr,
|
||||
body: Stmt,
|
||||
) -> Result<Signal, RuntimeError> {
|
||||
let iter_val = self.evaluate(iterable)?;
|
||||
let items: Vec<Value> = match &iter_val {
|
||||
Value::Array(arr) => arr.borrow().clone(),
|
||||
Value::Object(obj) => obj.borrow().keys().map(|k| Value::String(k.clone())).collect(),
|
||||
Value::String(s) => s.chars().map(|c| Value::String(c.to_string())).collect(),
|
||||
_ => {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "for-in requires an array, object, or string".into(),
|
||||
token: None,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let mut signal = Signal::None;
|
||||
for item in items {
|
||||
let previous = Rc::clone(&self.env);
|
||||
self.env = Rc::new(RefCell::new(Env::new(Some(previous))));
|
||||
self.env.borrow_mut().define(var_name.clone(), item, true);
|
||||
|
||||
signal = self.execute(body.clone())?;
|
||||
let parent = self.env.borrow().parent.as_ref().unwrap().clone();
|
||||
self.env = parent;
|
||||
|
||||
match signal {
|
||||
Signal::Break => { signal = Signal::None; break; }
|
||||
Signal::Continue => { signal = Signal::None; continue; }
|
||||
sig @ Signal::Return(_) => return Ok(sig),
|
||||
Signal::None => {}
|
||||
}
|
||||
}
|
||||
Ok(signal)
|
||||
}
|
||||
|
||||
fn exec_function(
|
||||
&mut self,
|
||||
name: String,
|
||||
params: Vec<String>,
|
||||
body: Vec<Stmt>,
|
||||
) -> Result<Signal, RuntimeError> {
|
||||
let func = Value::Function(Rc::new(Function {
|
||||
params,
|
||||
body,
|
||||
env: Rc::clone(&self.env),
|
||||
name: Some(name.clone()),
|
||||
}));
|
||||
self.env.borrow_mut().define(name, func, true);
|
||||
Ok(Signal::None)
|
||||
}
|
||||
|
||||
fn exec_return(&mut self, expr_opt: Option<Expr>) -> Result<Signal, RuntimeError> {
|
||||
if let Some(expr) = expr_opt {
|
||||
Ok(Signal::Return(self.evaluate(expr)?))
|
||||
} else {
|
||||
Ok(Signal::Return(Value::Nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
use crate::ast::*;
|
||||
use crate::error::RuntimeError;
|
||||
use super::{Env, Value};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
pub struct Interpreter {
|
||||
/// 当前作用域(用户代码所在 env,parent 指向 builtins_env)
|
||||
pub env: Rc<RefCell<Env>>,
|
||||
/// 内置函数根作用域(parent = None,所有内置函数注册在此)
|
||||
pub builtins_env: Rc<RefCell<Env>>,
|
||||
/// 模块缓存:规范路径 → exports 对象
|
||||
pub module_cache: RefCell<HashMap<String, Value>>,
|
||||
/// 当前执行文件的目录,用于 require() 解析相对路径
|
||||
pub current_dir: String,
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
pub fn new() -> Self {
|
||||
// 根层:仅包含内置函数
|
||||
let builtins_env = Rc::new(RefCell::new(Env::new(None)));
|
||||
super::builtins::register_all(&builtins_env);
|
||||
|
||||
// 用户层:parent 指向 builtins_env,用户定义的变量都在这层
|
||||
let script_env = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&builtins_env)))));
|
||||
|
||||
Self {
|
||||
env: script_env,
|
||||
builtins_env,
|
||||
module_cache: RefCell::new(HashMap::new()),
|
||||
current_dir: std::env::current_dir()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|_| ".".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// 指定当前目录的构造器,用于 `run_file` 时设置脚本所在目录
|
||||
pub fn with_current_dir(dir: String) -> Self {
|
||||
let mut interp = Self::new();
|
||||
interp.current_dir = dir;
|
||||
interp
|
||||
}
|
||||
|
||||
pub fn interpret(&mut self, statements: Vec<Stmt>) -> Result<(), RuntimeError> {
|
||||
for stmt in statements {
|
||||
self.execute(stmt)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "tests.rs"]
|
||||
mod tests;
|
||||
@@ -0,0 +1,124 @@
|
||||
pub mod builtins;
|
||||
pub mod env;
|
||||
pub mod eval;
|
||||
pub mod exec;
|
||||
pub mod interpreter;
|
||||
pub mod module;
|
||||
|
||||
pub use env::Env;
|
||||
pub use interpreter::Interpreter;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
|
||||
pub type NativeFn = Rc<dyn Fn(&mut super::Interpreter, Vec<Value>) -> Result<Value, crate::error::RuntimeError>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Value {
|
||||
Number(f64),
|
||||
String(String),
|
||||
Bool(bool),
|
||||
Nil,
|
||||
Object(Rc<RefCell<HashMap<String, Value>>>),
|
||||
Array(Rc<RefCell<Vec<Value>>>),
|
||||
Function(Rc<Function>),
|
||||
NativeFunction(NativeFn),
|
||||
}
|
||||
|
||||
pub enum Signal {
|
||||
None, // 正常执行
|
||||
Return(Value), // return 语句携带的返回值
|
||||
Break, // break 信号
|
||||
Continue, // continue 信号
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn get(&self, key: &str) -> Option<Value> {
|
||||
match self {
|
||||
Value::Object(obj) => obj.borrow().get(key).cloned(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&self, key: &str, val: Value) -> Result<(), crate::error::RuntimeError> {
|
||||
match self {
|
||||
Value::Object(obj) => {
|
||||
obj.borrow_mut().insert(key.to_string(), val);
|
||||
Ok(())
|
||||
},
|
||||
_ => Err(crate::error::RuntimeError::RuntimeError {
|
||||
message: "Only objects have properties".to_string(),
|
||||
token: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Value::Number(n) => write!(f, "Number({:?})", n),
|
||||
Value::String(s) => write!(f, "String({:?})", s),
|
||||
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(...)"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
/// 容器内值的格式化:字符串加引号以区分类型,其余类型用 Display
|
||||
fn fmt_element(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Value::String(s) => write!(f, "\"{}\"", s),
|
||||
other => write!(f, "{}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Value::Number(n) => write!(f, "{}", n),
|
||||
Value::String(s) => write!(f, "{}", s),
|
||||
Value::Bool(b) => write!(f, "{}", b),
|
||||
Value::Nil => write!(f, "nil"),
|
||||
Value::Object(obj) => {
|
||||
let obj = obj.borrow();
|
||||
write!(f, "{{ ")?;
|
||||
for (key, value) in obj.iter() {
|
||||
write!(f, "{}: ", key)?;
|
||||
value.fmt_element(f)?;
|
||||
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, ", ")?;
|
||||
}
|
||||
val.fmt_element(f)?;
|
||||
}
|
||||
write!(f, "]")
|
||||
},
|
||||
Value::Function(_) => write!(f, "<function>"),
|
||||
Value::NativeFunction(_) => write!(f, "<native function>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Function {
|
||||
pub params: Vec<String>,
|
||||
pub body: Vec<crate::ast::Stmt>,
|
||||
pub env: Rc<RefCell<Env>>, // 闭包捕获环境
|
||||
pub name: Option<String>,
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
//! 模块系统:require() 加载器
|
||||
//!
|
||||
//! `require("path/to/module.ast")` 加载并执行指定的 Aster 文件,
|
||||
//! 返回一个包含模块所有顶层定义的 `Value::Object`。
|
||||
//! 模块在自己的作用域中执行,只能访问内置函数,无法访问调用者的变量。
|
||||
//! 第二次 require 同一文件会返回缓存的对象。
|
||||
|
||||
use crate::error::RuntimeError;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::parser::Parser;
|
||||
use super::{Interpreter, Env, Value, Signal};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// require() 的内置函数实现
|
||||
pub fn require_fn(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
// 1. 参数验证
|
||||
let path_str = match args.first() {
|
||||
Some(Value::String(s)) => s.clone(),
|
||||
Some(other) => return Err(runtime_error(format!(
|
||||
"require() expects a string argument, got {}", other))),
|
||||
None => return Err(runtime_error(
|
||||
"require() expects 1 argument (string path)")),
|
||||
};
|
||||
|
||||
// 2. 路径解析
|
||||
let resolved = resolve_path(&interp.current_dir, &path_str)?;
|
||||
|
||||
// 3. 缓存查找
|
||||
if let Some(cached) = interp.module_cache.borrow().get(&resolved) {
|
||||
return Ok(cached.clone());
|
||||
}
|
||||
|
||||
// 4. 读取文件
|
||||
let src = std::fs::read_to_string(&resolved)
|
||||
.map_err(|e| runtime_error(format!(
|
||||
"Module '{}' not found: {}", path_str, e)))?;
|
||||
|
||||
// 5. 词法分析
|
||||
let (tokens, lex_errors) = Lexer::new(&src).tokenize();
|
||||
if !lex_errors.is_empty() {
|
||||
return Err(runtime_error(format!(
|
||||
"Lex error in module '{}': {}", path_str, lex_errors[0])));
|
||||
}
|
||||
|
||||
// 6. 语法分析
|
||||
let mut parser = Parser::new(tokens);
|
||||
let (stmts, parse_errors) = parser.parse();
|
||||
if !parse_errors.is_empty() {
|
||||
return Err(runtime_error(format!(
|
||||
"Parse error in module '{}': {}", path_str, parse_errors[0])));
|
||||
}
|
||||
|
||||
// 7. 创建隔离的模块 env(父级 = builtins_env,看不到调用者的变量)
|
||||
let module_env = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&interp.builtins_env)))));
|
||||
|
||||
// 8. 在缓存中插入占位符(支持循环 require)
|
||||
let exports_map = Rc::new(RefCell::new(HashMap::new()));
|
||||
let exports = Value::Object(Rc::clone(&exports_map));
|
||||
interp.module_cache.borrow_mut().insert(resolved.clone(), exports.clone());
|
||||
|
||||
// 9. 保存调用者状态
|
||||
let previous_env = Rc::clone(&interp.env);
|
||||
let previous_dir = interp.current_dir.clone();
|
||||
|
||||
// 10. 切换到模块上下文
|
||||
interp.env = module_env.clone();
|
||||
interp.current_dir = module_dir(&resolved);
|
||||
|
||||
// 11. 执行模块
|
||||
for stmt in &stmts {
|
||||
match interp.execute(stmt.clone()) {
|
||||
Ok(Signal::Break) | Ok(Signal::Continue) => {
|
||||
// 恢复状态后返回错误
|
||||
interp.env = previous_env;
|
||||
interp.current_dir = previous_dir;
|
||||
interp.module_cache.borrow_mut().remove(&resolved);
|
||||
return Err(runtime_error(
|
||||
"break/continue outside of loop in module"));
|
||||
}
|
||||
Err(e) => {
|
||||
// 恢复状态后传播错误
|
||||
interp.env = previous_env;
|
||||
interp.current_dir = previous_dir;
|
||||
interp.module_cache.borrow_mut().remove(&resolved);
|
||||
return Err(e);
|
||||
}
|
||||
Ok(Signal::None) | Ok(Signal::Return(_)) => {
|
||||
// 正常执行或顶层 return,继续
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 12. 恢复调用者状态
|
||||
interp.env = previous_env;
|
||||
interp.current_dir = previous_dir;
|
||||
|
||||
// 13. 收集 exports(模块 env 中的所有直接绑定)
|
||||
for (name, (val, _mutable)) in module_env.borrow().values.clone() {
|
||||
exports_map.borrow_mut().insert(name, val);
|
||||
}
|
||||
|
||||
Ok(exports)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Helpers
|
||||
// ============================================================================
|
||||
|
||||
/// 解析模块路径:相对路径基于 current_dir,用 canonicalize 规范化
|
||||
fn resolve_path(current_dir: &str, path_str: &str) -> Result<String, RuntimeError> {
|
||||
let path = Path::new(path_str);
|
||||
let resolved = if path.is_absolute() {
|
||||
path.to_path_buf()
|
||||
} else {
|
||||
Path::new(current_dir).join(path)
|
||||
};
|
||||
std::fs::canonicalize(&resolved)
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.map_err(|_| runtime_error(format!(
|
||||
"Module '{}' not found (resolved to '{}')",
|
||||
path_str, resolved.display())))
|
||||
}
|
||||
|
||||
/// 提取模块文件所在目录
|
||||
fn module_dir(resolved: &str) -> String {
|
||||
Path::new(resolved)
|
||||
.parent()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|| ".".to_string())
|
||||
}
|
||||
|
||||
fn runtime_error(msg: impl Into<String>) -> RuntimeError {
|
||||
RuntimeError::RuntimeError {
|
||||
message: msg.into(),
|
||||
token: None,
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user