在本章中,我们将实现 Engine 引擎,它负责:
- 协调所有组件(Parser、Codegen、Interpreter)
- 提供统一的执行接口
- 管理全局状态
- 处理端到端的执行流程
Engine 是整个 JavaScript 引擎的入口点和协调者。它将各个独立的组件串联起来,形成完整的执行流程。
执行流程:
Source Code → Parser → AST → Codegen → Bytecode → Interpreter → Result
pub struct Engine {
interpreter: Ignition,
global_scope: Scope,
}字段说明:
-
interpreter:字节码解释器
- 执行生成的字节码
- 管理运行时状态
-
global_scope:全局作用域
- 存储全局变量
- 在多次执行间保持状态
impl Engine {
pub fn new() -> Self {
Self {
interpreter: Ignition::new(),
global_scope: Scope::global(),
}
}
}为什么需要全局作用域?
- 支持多次执行
- 保持全局变量的状态
- 例如:
engine.execute("let x = 10;"); engine.execute("let y = x + 5;"); // y 可以访问 x
pub fn execute(&mut self, source: &str) -> Result<Value, Error> {
// 1. 解析源代码
let ast = self.parse(source)?;
// 2. 生成字节码
let bytecode = self.generate_bytecode(&ast);
// 3. 解释执行
let result = self.interpret(bytecode)?;
Ok(result)
}三个阶段:
- Parse:源代码 → AST
- Codegen:AST → 字节码
- Interpret:字节码 → 结果
fn parse(&self, source: &str) -> Result<AST, ParseError> {
let mut parser = Parser::new(source.to_string());
parser.parse()
}错误处理:
- 语法错误会在这里被捕获
- 返回 ParseError 给调用者
fn generate_bytecode(&mut self, ast: &AST) -> BytecodeChunk {
let mut generator = BytecodeGenerator::new(self.global_scope.clone());
generator.generate(&ast.root)
}为什么克隆 global_scope?
- BytecodeGenerator 需要修改作用域
- 但我们希望保持 Engine 的作用域不变
- 生成完成后,作用域的修改会被丢弃
改进方案:
fn generate_bytecode(&mut self, ast: &AST) -> BytecodeChunk {
let mut generator = BytecodeGenerator::new(self.global_scope.clone());
let chunk = generator.generate(&ast.root);
// 更新全局作用域
self.global_scope = generator.scope;
chunk
}fn interpret(&mut self, bytecode: BytecodeChunk) -> Result<Value, RuntimeError> {
self.interpreter.execute(bytecode)
}状态管理:
- 解释器维护自己的状态
- 每次执行都是独立的
pub fn execute(&mut self, source: &str) -> Result<Value, Error> {
let ast = self.parse(source)?; // ParseError → Error
let bytecode = self.generate_bytecode(&ast);
let result = self.interpret(bytecode)?; // RuntimeError → Error
Ok(result)
}自动转换:
?操作符自动调用Fromtrait- ParseError 和 RuntimeError 都可以转换为 Error
pub fn execute(&mut self, source: &str) -> Result<Value, Error> {
let ast = self.parse(source).map_err(|e| {
eprintln!("Parse error in source: {}", source);
e
})?;
// ...
}fn main() {
let mut engine = Engine::new();
match engine.execute("10 + 20") {
Ok(result) => println!("Result: {:?}", result),
Err(err) => eprintln!("Error: {}", err),
}
}fn main() {
let mut engine = Engine::new();
engine.execute("let x = 10;").unwrap();
engine.execute("let y = 20;").unwrap();
let result = engine.execute("x + y").unwrap();
println!("Result: {:?}", result); // Number(30.0)
}fn main() {
let mut engine = Engine::new();
match engine.execute("10 / 0") {
Ok(result) => println!("Result: {:?}", result),
Err(Error::RuntimeError(RuntimeError::DivisionByZero)) => {
eprintln!("Cannot divide by zero!");
}
Err(err) => eprintln!("Error: {}", err),
}
}impl Engine {
pub fn new() -> Self {
let mut engine = Self {
interpreter: Ignition::new(),
global_scope: Scope::global(),
};
// 注册内置函数
engine.register_builtin("print", 0);
engine.register_builtin("sqrt", 1);
engine
}
fn register_builtin(&mut self, name: &str, func_id: FunctionId) {
self.global_scope.declare(name.to_string());
// 将函数 ID 存储到某个地方
}
}pub struct Engine {
interpreter: Ignition,
global_scope: Scope,
debug_mode: bool,
}
impl Engine {
pub fn set_debug(&mut self, enabled: bool) {
self.debug_mode = enabled;
}
pub fn execute(&mut self, source: &str) -> Result<Value, Error> {
if self.debug_mode {
println!("Executing: {}", source);
}
let ast = self.parse(source)?;
if self.debug_mode {
println!("AST: {:?}", ast);
}
let bytecode = self.generate_bytecode(&ast);
if self.debug_mode {
println!("Bytecode: {:?}", bytecode);
}
let result = self.interpret(bytecode)?;
if self.debug_mode {
println!("Result: {:?}", result);
}
Ok(result)
}
}pub struct Engine {
interpreter: Ignition,
global_scope: Scope,
stats: ExecutionStats,
}
struct ExecutionStats {
parse_time: Duration,
codegen_time: Duration,
interpret_time: Duration,
}
impl Engine {
pub fn execute(&mut self, source: &str) -> Result<Value, Error> {
let start = Instant::now();
let ast = self.parse(source)?;
self.stats.parse_time += start.elapsed();
let start = Instant::now();
let bytecode = self.generate_bytecode(&ast);
self.stats.codegen_time += start.elapsed();
let start = Instant::now();
let result = self.interpret(bytecode)?;
self.stats.interpret_time += start.elapsed();
Ok(result)
}
pub fn print_stats(&self) {
println!("Parse time: {:?}", self.stats.parse_time);
println!("Codegen time: {:?}", self.stats.codegen_time);
println!("Interpret time: {:?}", self.stats.interpret_time);
}
}#[test]
fn test_execute_number() {
let mut engine = Engine::new();
let result = engine.execute("42").unwrap();
assert_eq!(result, Value::Number(42.0));
}#[test]
fn test_execute_addition() {
let mut engine = Engine::new();
let result = engine.execute("10 + 20").unwrap();
assert_eq!(result, Value::Number(30.0));
}#[test]
fn test_execute_complex_expression() {
let mut engine = Engine::new();
let result = engine.execute("(5 + 3) * 2").unwrap();
assert_eq!(result, Value::Number(16.0));
}#[test]
fn test_execute_parse_error() {
let mut engine = Engine::new();
let result = engine.execute("let = 10");
assert!(result.is_err());
}
#[test]
fn test_execute_division_by_zero() {
let mut engine = Engine::new();
let result = engine.execute("10 / 0");
assert!(result.is_err());
}Input → Stage1 → Stage2 → Stage3 → Output
每个阶段独立处理,输出作为下一阶段的输入。
Engine 隐藏了内部复杂性,提供简单的接口:
// 用户只需要调用一个方法
engine.execute(source)
// 而不是
let ast = parser.parse(source);
let bytecode = codegen.generate(ast);
let result = interpreter.execute(bytecode);lazy_static! {
static ref GLOBAL_ENGINE: Mutex<Engine> = Mutex::new(Engine::new());
}
pub fn execute(source: &str) -> Result<Value, Error> {
GLOBAL_ENGINE.lock().unwrap().execute(source)
}// 不好:每次都克隆
fn generate_bytecode(&mut self, ast: &AST) -> BytecodeChunk {
let generator = BytecodeGenerator::new(self.global_scope.clone());
// ...
}
// 更好:使用引用
fn generate_bytecode(&mut self, ast: &AST) -> BytecodeChunk {
let generator = BytecodeGenerator::new(&mut self.global_scope);
// ...
}pub struct Engine {
// ...
ast_cache: HashMap<String, AST>,
}
impl Engine {
pub fn execute(&mut self, source: &str) -> Result<Value, Error> {
let ast = if let Some(cached) = self.ast_cache.get(source) {
cached.clone()
} else {
let ast = self.parse(source)?;
self.ast_cache.insert(source.to_string(), ast.clone());
ast
};
// ...
}
}pub struct Engine {
// ...
bytecode_cache: HashMap<String, BytecodeChunk>,
}答:
pub struct Engine {
// ...
modules: HashMap<String, Module>,
}
impl Engine {
pub fn load_module(&mut self, name: &str, source: &str) -> Result<(), Error> {
let ast = self.parse(source)?;
let bytecode = self.generate_bytecode(&ast);
self.modules.insert(name.to_string(), Module {
bytecode,
exports: HashMap::new(),
});
Ok(())
}
pub fn import(&mut self, module_name: &str) -> Result<Value, Error> {
// 加载并执行模块
// 返回模块的导出
}
}答:
fn repl() {
let mut engine = Engine::new();
let stdin = io::stdin();
loop {
print!("> ");
io::stdout().flush().unwrap();
let mut input = String::new();
stdin.read_line(&mut input).unwrap();
match engine.execute(&input) {
Ok(result) => println!("{:?}", result),
Err(err) => eprintln!("Error: {}", err),
}
}
}答:
pub async fn execute_async(&mut self, source: &str) -> Result<Value, Error> {
// 在单独的线程中执行
let ast = self.parse(source)?;
let bytecode = self.generate_bytecode(&ast);
tokio::task::spawn_blocking(move || {
let mut interpreter = Ignition::new();
interpreter.execute(bytecode)
}).await.unwrap()
}在下一章中,我们将编写端到端测试,验证整个引擎的功能。
- 添加对多个文件的支持
- 实现简单的模块系统
- 添加性能分析工具
- 实现 REPL 交互式环境
本章的完整代码在 src/engine.rs 文件中。