feat: 新增对象字面量和属性访问表达式
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
let name = input("Your name: ");
|
let name = io.input("Your name: ");
|
||||||
print("Hello, " + name)
|
io.print("Hello, " + name)
|
||||||
|
|||||||
4
examples/object.ast
Normal file
4
examples/object.ast
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
let obj = { name: "aster", version: 1 }
|
||||||
|
print(obj.name)
|
||||||
|
obj.version = obj.version + 1
|
||||||
|
print(obj.version)
|
||||||
@@ -14,6 +14,24 @@ pub enum Expr {
|
|||||||
value: Box<Expr>,
|
value: Box<Expr>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// 属性访问表达式:object.name
|
||||||
|
Get {
|
||||||
|
object: Box<Expr>,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 属性赋值表达式:object.name = value
|
||||||
|
Set {
|
||||||
|
object: Box<Expr>,
|
||||||
|
name: String,
|
||||||
|
value: Box<Expr>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 对象字面量:{ key: value, ... }
|
||||||
|
ObjectLiteral {
|
||||||
|
properties: Vec<(String, Expr)>,
|
||||||
|
},
|
||||||
|
|
||||||
/// 一元运算:!expr / -expr
|
/// 一元运算:!expr / -expr
|
||||||
Unary {
|
Unary {
|
||||||
op: UnaryOp,
|
op: UnaryOp,
|
||||||
|
|||||||
24
src/interpreter/builtins/io.rs
Normal file
24
src/interpreter/builtins/io.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
//! 标准库:io(print, input)
|
||||||
|
|
||||||
|
use crate::interpreter::Value;
|
||||||
|
|
||||||
|
pub fn print(args: Vec<Value>) -> Value {
|
||||||
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
print!(" ");
|
||||||
|
}
|
||||||
|
print!("{}", arg);
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
Value::Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn input(args: Vec<Value>) -> 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())
|
||||||
|
}
|
||||||
28
src/interpreter/builtins/mod.rs
Normal file
28
src/interpreter/builtins/mod.rs
Normal file
@@ -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<RefCell<Env>>) {
|
||||||
|
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))));
|
||||||
|
}
|
||||||
12
src/interpreter/builtins/os.rs
Normal file
12
src/interpreter/builtins/os.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//! 标准库:os(clock)
|
||||||
|
|
||||||
|
use crate::interpreter::Value;
|
||||||
|
|
||||||
|
pub fn clock(_args: Vec<Value>) -> Value {
|
||||||
|
Value::Number(
|
||||||
|
std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs_f64(),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
use crate::error::RuntimeError;
|
use crate::error::RuntimeError;
|
||||||
use super::{Value, Env, Function};
|
use super::{Value, Env, Function};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
@@ -11,27 +12,13 @@ pub struct Interpreter {
|
|||||||
impl Interpreter {
|
impl Interpreter {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let env = Rc::new(RefCell::new(Env::new(None)));
|
let env = Rc::new(RefCell::new(Env::new(None)));
|
||||||
|
super::builtins::register_all(&env);
|
||||||
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);
|
|
||||||
|
|
||||||
Self { env }
|
Self { env }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interpret(&mut self, statements: Vec<Stmt>) -> Result<(), RuntimeError> {
|
pub fn interpret(&mut self, statements: Vec<Stmt>) -> Result<(), RuntimeError> {
|
||||||
for stmt in statements {
|
for stmt in statements {
|
||||||
if let Some(val) = self.execute(stmt.clone())? {
|
self.execute(stmt)?;
|
||||||
// 如果是表达式语句且结果不是 Nil,打印结果
|
|
||||||
if matches!(stmt, Stmt::ExprStmt(_)) && !matches!(val, Value::Nil) {
|
|
||||||
println!("{}", val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -124,6 +111,46 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
Ok(val)
|
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 } => {
|
Expr::Unary { op, right } => {
|
||||||
let val = self.evaluate(*right)?;
|
let val = self.evaluate(*right)?;
|
||||||
match op {
|
match op {
|
||||||
@@ -276,30 +303,3 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 内置函数实现
|
|
||||||
fn native_print(args: Vec<Value>) -> Value {
|
|
||||||
for (i, arg) in args.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
print!(" ");
|
|
||||||
}
|
|
||||||
print!("{}", arg);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
Value::Nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fn native_input(args: Vec<Value>) -> 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 {
|
|
||||||
Value::Number(std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs_f64())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
pub mod builtins;
|
||||||
pub mod env;
|
pub mod env;
|
||||||
pub mod interpreter;
|
pub mod interpreter;
|
||||||
|
|
||||||
pub use env::Env;
|
pub use env::Env;
|
||||||
pub use interpreter::Interpreter;
|
pub use interpreter::Interpreter;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -16,6 +18,7 @@ pub enum Value {
|
|||||||
String(String),
|
String(String),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Nil,
|
Nil,
|
||||||
|
Object(Rc<RefCell<HashMap<String, Value>>>),
|
||||||
Function(Rc<Function>),
|
Function(Rc<Function>),
|
||||||
NativeFunction(NativeFn),
|
NativeFunction(NativeFn),
|
||||||
}
|
}
|
||||||
@@ -27,6 +30,7 @@ impl std::fmt::Debug for Value {
|
|||||||
Value::String(s) => write!(f, "String({:?})", s),
|
Value::String(s) => write!(f, "String({:?})", s),
|
||||||
Value::Bool(b) => write!(f, "Bool({:?})", b),
|
Value::Bool(b) => write!(f, "Bool({:?})", b),
|
||||||
Value::Nil => write!(f, "Nil"),
|
Value::Nil => write!(f, "Nil"),
|
||||||
|
Value::Object(_) => write!(f, "Object(...)"),
|
||||||
Value::Function(_) => write!(f, "Function(...)"),
|
Value::Function(_) => write!(f, "Function(...)"),
|
||||||
Value::NativeFunction(_) => write!(f, "NativeFunction(...)"),
|
Value::NativeFunction(_) => write!(f, "NativeFunction(...)"),
|
||||||
}
|
}
|
||||||
@@ -40,6 +44,7 @@ impl fmt::Display for Value {
|
|||||||
Value::String(s) => write!(f, "{}", s),
|
Value::String(s) => write!(f, "{}", s),
|
||||||
Value::Bool(b) => write!(f, "{}", b),
|
Value::Bool(b) => write!(f, "{}", b),
|
||||||
Value::Nil => write!(f, "nil"),
|
Value::Nil => write!(f, "nil"),
|
||||||
|
Value::Object(_) => write!(f, "<object>"),
|
||||||
Value::Function(_) => write!(f, "<function>"),
|
Value::Function(_) => write!(f, "<function>"),
|
||||||
Value::NativeFunction(_) => write!(f, "<native function>"),
|
Value::NativeFunction(_) => write!(f, "<native function>"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ impl Lexer {
|
|||||||
'+' => TokenKind::Plus,
|
'+' => TokenKind::Plus,
|
||||||
'-' => TokenKind::Minus,
|
'-' => TokenKind::Minus,
|
||||||
'*' => TokenKind::Star,
|
'*' => TokenKind::Star,
|
||||||
|
'.' => TokenKind::Dot,
|
||||||
|
':' => TokenKind::Colon,
|
||||||
|
|
||||||
'/' => {
|
'/' => {
|
||||||
if self.match_char('/') {
|
if self.match_char('/') {
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ pub enum TokenKind {
|
|||||||
// 单字符
|
// 单字符
|
||||||
LeftParen, RightParen,
|
LeftParen, RightParen,
|
||||||
LeftBrace, RightBrace,
|
LeftBrace, RightBrace,
|
||||||
Comma, Semicolon,
|
Comma, Semicolon, Dot,
|
||||||
|
Colon,
|
||||||
|
|
||||||
// 运算符
|
// 运算符
|
||||||
Plus, Minus, Star, Slash,
|
Plus, Minus, Star, Slash,
|
||||||
|
|||||||
@@ -154,14 +154,11 @@ impl Parser {
|
|||||||
|
|
||||||
if self.match_kind(&[TokenKind::Equal]) {
|
if self.match_kind(&[TokenKind::Equal]) {
|
||||||
let value = self.assignment();
|
let value = self.assignment();
|
||||||
if let Expr::Variable(name) = expr {
|
return match expr {
|
||||||
return Expr::Assign {
|
Expr::Variable(name) => Expr::Assign { name, value: Box::new(value) },
|
||||||
name,
|
Expr::Get { object, name } => Expr::Set { object, name, value: Box::new(value) },
|
||||||
value: Box::new(value),
|
_ => panic!("Invalid assignment target."),
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
panic!("Invalid assignment target.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expr
|
expr
|
||||||
@@ -299,21 +296,31 @@ impl Parser {
|
|||||||
fn call(&mut self) -> Expr {
|
fn call(&mut self) -> Expr {
|
||||||
let mut expr = self.primary();
|
let mut expr = self.primary();
|
||||||
|
|
||||||
while self.match_kind(&[TokenKind::LeftParen]) {
|
loop {
|
||||||
let mut arguments = Vec::new();
|
if self.match_kind(&[TokenKind::LeftParen]) {
|
||||||
if !self.check(&TokenKind::RightParen) {
|
let mut arguments = Vec::new();
|
||||||
loop {
|
if !self.check(&TokenKind::RightParen) {
|
||||||
arguments.push(self.expression());
|
loop {
|
||||||
if !self.match_kind(&[TokenKind::Comma]) {
|
arguments.push(self.expression());
|
||||||
break;
|
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
|
expr
|
||||||
@@ -363,6 +370,10 @@ impl Parser {
|
|||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.match_kind(&[TokenKind::LeftBrace]) {
|
||||||
|
return self.object_literal();
|
||||||
|
}
|
||||||
|
|
||||||
panic!("Expected expression at line {}", self.peek().line);
|
panic!("Expected expression at line {}", self.peek().line);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,4 +439,21 @@ impl Parser {
|
|||||||
fn previous(&self) -> &Token {
|
fn previous(&self) -> &Token {
|
||||||
&self.tokens[self.current - 1]
|
&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 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user