diff --git a/examples/input.ast b/examples/input.ast index 1cf4b76..b4f3cb2 100644 --- a/examples/input.ast +++ b/examples/input.ast @@ -1,2 +1,2 @@ -let name = input("Your name: "); -print("Hello, " + name) \ No newline at end of file +let name = io.input("Your name: "); +io.print("Hello, " + name) diff --git a/examples/object.ast b/examples/object.ast new file mode 100644 index 0000000..0f2e5da --- /dev/null +++ b/examples/object.ast @@ -0,0 +1,4 @@ +let obj = { name: "aster", version: 1 } +print(obj.name) +obj.version = obj.version + 1 +print(obj.version) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 836a405..98fc2ed 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -14,6 +14,24 @@ pub enum Expr { value: Box, }, + /// 属性访问表达式:object.name + Get { + object: Box, + name: String, + }, + + /// 属性赋值表达式:object.name = value + Set { + object: Box, + name: String, + value: Box, + }, + + /// 对象字面量:{ key: value, ... } + ObjectLiteral { + properties: Vec<(String, Expr)>, + }, + /// 一元运算:!expr / -expr Unary { op: UnaryOp, diff --git a/src/interpreter/builtins/io.rs b/src/interpreter/builtins/io.rs new file mode 100644 index 0000000..35756f5 --- /dev/null +++ b/src/interpreter/builtins/io.rs @@ -0,0 +1,24 @@ +//! 标准库:io(print, input) + +use crate::interpreter::Value; + +pub fn print(args: Vec) -> Value { + for (i, arg) in args.iter().enumerate() { + if i > 0 { + print!(" "); + } + print!("{}", arg); + } + println!(); + Value::Nil +} + +pub fn input(args: Vec) -> Value { + 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(); + Value::String(buffer.trim_end().to_string()) +} diff --git a/src/interpreter/builtins/mod.rs b/src/interpreter/builtins/mod.rs new file mode 100644 index 0000000..9f5ff94 --- /dev/null +++ b/src/interpreter/builtins/mod.rs @@ -0,0 +1,28 @@ +//! 内置函数模块:按功能分组注册,便于扩展和维护。 + +mod io; +mod os; + +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>) { + let mut e = env.borrow_mut(); + + // io + let mut io = HashMap::new(); + io.insert("print".into(), Value::NativeFunction(io::print)); + io.insert("input".into(), Value::NativeFunction(io::input)); + e.define("io".into(), Value::Object(Rc::new(RefCell::new(io)))); + // input and print can be used as global functions for convenience + e.define("print".into(), Value::NativeFunction(io::print)); + e.define("input".into(), Value::NativeFunction(io::input)); + + // os + let mut os = HashMap::new(); + os.insert("clock".into(), Value::NativeFunction(os::clock)); + e.define("os".into(), Value::Object(Rc::new(RefCell::new(os)))); +} diff --git a/src/interpreter/builtins/os.rs b/src/interpreter/builtins/os.rs new file mode 100644 index 0000000..7213b5b --- /dev/null +++ b/src/interpreter/builtins/os.rs @@ -0,0 +1,12 @@ +//! 标准库:os(clock) + +use crate::interpreter::Value; + +pub fn clock(_args: Vec) -> Value { + Value::Number( + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs_f64(), + ) +} diff --git a/src/interpreter/interpreter.rs b/src/interpreter/interpreter.rs index 2f1de77..2e44408 100644 --- a/src/interpreter/interpreter.rs +++ b/src/interpreter/interpreter.rs @@ -1,6 +1,7 @@ use crate::ast::*; use crate::error::RuntimeError; use super::{Value, Env, Function}; +use std::collections::HashMap; use std::rc::Rc; use std::cell::RefCell; @@ -11,27 +12,13 @@ pub struct Interpreter { impl Interpreter { pub fn new() -> Self { let env = Rc::new(RefCell::new(Env::new(None))); - - let mut env_mut = env.borrow_mut(); - - // 注册内置函数 - env_mut.define("print".to_string(), Value::NativeFunction(native_print)); - env_mut.define("input".to_string(), Value::NativeFunction(native_input)); - env_mut.define("clock".to_string(), Value::NativeFunction(native_clock)); - - drop(env_mut); - + super::builtins::register_all(&env); Self { env } } pub fn interpret(&mut self, statements: Vec) -> Result<(), RuntimeError> { for stmt in statements { - if let Some(val) = self.execute(stmt.clone())? { - // 如果是表达式语句且结果不是 Nil,打印结果 - if matches!(stmt, Stmt::ExprStmt(_)) && !matches!(val, Value::Nil) { - println!("{}", val); - } - } + self.execute(stmt)?; } Ok(()) } @@ -124,6 +111,46 @@ impl Interpreter { } Ok(val) } + Expr::Get { object, name } => { + let obj = self.evaluate(*object)?; + match obj { + Value::Object(map) => { + match map.borrow().get(&name) { + Some(val) => Ok(val.clone()), + None => Err(RuntimeError::RuntimeError { + message: format!("Undefined property '{}'", name), + token: None, + }), + } + } + _ => Err(RuntimeError::RuntimeError { + message: "Only objects have properties".to_string(), + token: None, + }), + } + } + Expr::Set { object, name, value } => { + let obj = self.evaluate(*object)?; + match obj { + Value::Object(map) => { + let val = self.evaluate(*value)?; + map.borrow_mut().insert(name, val.clone()); + Ok(val) + } + _ => Err(RuntimeError::RuntimeError { + message: "Only objects have properties".to_string(), + token: None, + }), + } + } + Expr::ObjectLiteral { properties } => { + 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)))) + } Expr::Unary { op, right } => { let val = self.evaluate(*right)?; match op { @@ -276,30 +303,3 @@ impl Interpreter { } } -// 内置函数实现 -fn native_print(args: Vec) -> Value { - for (i, arg) in args.iter().enumerate() { - if i > 0 { - print!(" "); - } - print!("{}", arg); - } - println!(); - Value::Nil -} - -fn native_input(args: Vec) -> Value { - if let Some(prompt) = args.get(0) { - print!("{}", prompt); - use std::io::Write; - std::io::stdout().flush().unwrap(); - } - let mut buffer = String::new(); - std::io::stdin().read_line(&mut buffer).unwrap(); - Value::String(buffer.trim_end().to_string()) -} - -// 获取当前时间 -fn native_clock(_args: Vec) -> Value { - Value::Number(std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs_f64()) -} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index f30b5c1..b617e1c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,9 +1,11 @@ +pub mod builtins; pub mod env; pub mod interpreter; pub use env::Env; pub use interpreter::Interpreter; +use std::collections::HashMap; use std::rc::Rc; use std::cell::RefCell; use std::fmt; @@ -16,6 +18,7 @@ pub enum Value { String(String), Bool(bool), Nil, + Object(Rc>>), Function(Rc), NativeFunction(NativeFn), } @@ -27,6 +30,7 @@ impl std::fmt::Debug for Value { 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::Function(_) => write!(f, "Function(...)"), Value::NativeFunction(_) => write!(f, "NativeFunction(...)"), } @@ -40,6 +44,7 @@ impl fmt::Display for Value { Value::String(s) => write!(f, "{}", s), Value::Bool(b) => write!(f, "{}", b), Value::Nil => write!(f, "nil"), + Value::Object(_) => write!(f, ""), Value::Function(_) => write!(f, ""), Value::NativeFunction(_) => write!(f, ""), } diff --git a/src/lexer/lexer.rs b/src/lexer/lexer.rs index c7c7fb8..21eef96 100644 --- a/src/lexer/lexer.rs +++ b/src/lexer/lexer.rs @@ -59,6 +59,8 @@ impl Lexer { '+' => TokenKind::Plus, '-' => TokenKind::Minus, '*' => TokenKind::Star, + '.' => TokenKind::Dot, + ':' => TokenKind::Colon, '/' => { if self.match_char('/') { diff --git a/src/lexer/token.rs b/src/lexer/token.rs index b33e882..18b087c 100644 --- a/src/lexer/token.rs +++ b/src/lexer/token.rs @@ -4,7 +4,8 @@ pub enum TokenKind { // 单字符 LeftParen, RightParen, LeftBrace, RightBrace, - Comma, Semicolon, + Comma, Semicolon, Dot, + Colon, // 运算符 Plus, Minus, Star, Slash, diff --git a/src/parser/parser.rs b/src/parser/parser.rs index d02dae3..b7f3f31 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -154,14 +154,11 @@ impl Parser { if self.match_kind(&[TokenKind::Equal]) { let value = self.assignment(); - if let Expr::Variable(name) = expr { - return Expr::Assign { - name, - value: Box::new(value), - }; - } else { - panic!("Invalid assignment target."); - } + return match expr { + Expr::Variable(name) => Expr::Assign { name, value: Box::new(value) }, + Expr::Get { object, name } => Expr::Set { object, name, value: Box::new(value) }, + _ => panic!("Invalid assignment target."), + }; } expr @@ -299,21 +296,31 @@ impl Parser { fn call(&mut self) -> Expr { let mut expr = self.primary(); - while self.match_kind(&[TokenKind::LeftParen]) { - let mut arguments = Vec::new(); - if !self.check(&TokenKind::RightParen) { - loop { - arguments.push(self.expression()); - if !self.match_kind(&[TokenKind::Comma]) { - break; + loop { + if self.match_kind(&[TokenKind::LeftParen]) { + let mut arguments = Vec::new(); + if !self.check(&TokenKind::RightParen) { + loop { + arguments.push(self.expression()); + if !self.match_kind(&[TokenKind::Comma]) { + break; + } } } + self.consume(TokenKind::RightParen, "Expected ')' after arguments.").unwrap(); + expr = Expr::Call { + callee: Box::new(expr), + arguments, + }; + } else if self.match_kind(&[TokenKind::Dot]) { + let name = self.consume_ident("Expected property name after '.'.").unwrap(); + expr = Expr::Get { + object: Box::new(expr), + name, + }; + } else { + break; } - self.consume(TokenKind::RightParen, "Expected ')' after arguments.").unwrap(); - expr = Expr::Call { - callee: Box::new(expr), - arguments, - }; } expr @@ -363,6 +370,10 @@ impl Parser { return expr; } + if self.match_kind(&[TokenKind::LeftBrace]) { + return self.object_literal(); + } + panic!("Expected expression at line {}", self.peek().line); } @@ -428,4 +439,21 @@ impl Parser { fn previous(&self) -> &Token { &self.tokens[self.current - 1] } + + fn object_literal(&mut self) -> Expr { + let mut properties = Vec::new(); + if !self.check(&TokenKind::RightBrace) { + loop { + let name = self.consume_ident("Expected property name in object literal").unwrap(); + self.consume(TokenKind::Colon, "Expected ':' after property name in object literal").unwrap(); + let value = self.expression(); + properties.push((name, value)); + if !self.match_kind(&[TokenKind::Comma]) { + break; + } + } + } + self.consume(TokenKind::RightBrace, "Expected '}' after object literal").unwrap(); + Expr::ObjectLiteral { properties } + } } \ No newline at end of file