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,
|
||||
}),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user