data types chapter

This commit is contained in:
YannAhlgrim
2026-07-12 16:24:05 +02:00
parent d8691cf178
commit bf2a7821dc
7 changed files with 822 additions and 6 deletions
+113 -3
View File
@@ -16,12 +16,16 @@ pub trait StatementTrait: NodeTrait {
pub enum Expression { pub enum Expression {
Identifier(Identifier), Identifier(Identifier),
IntegerLiteral(IntegerLiteral), IntegerLiteral(IntegerLiteral),
StringLiteral(StringLiteral),
Boolean(Boolean),
PrefixExpression(PrefixExpression), PrefixExpression(PrefixExpression),
InfixExpression(InfixExpression), InfixExpression(InfixExpression),
Boolean(Boolean),
IfExpression(IfExpression), IfExpression(IfExpression),
FunctionLiteral(FunctionLiteral), FunctionLiteral(FunctionLiteral),
CallExpression(CallExpression), CallExpression(CallExpression),
ArrayLiteral(ArrayLiteral),
HashLiteral(HashLiteral),
IndexExpression(IndexExpression),
} }
impl NodeTrait for Expression { impl NodeTrait for Expression {
@@ -29,12 +33,16 @@ impl NodeTrait for Expression {
match self { match self {
Expression::Identifier(i) => i.token_literal(), Expression::Identifier(i) => i.token_literal(),
Expression::IntegerLiteral(i) => i.token_literal(), Expression::IntegerLiteral(i) => i.token_literal(),
Expression::StringLiteral(i) => i.token_literal(),
Expression::Boolean(i) => i.token_literal(),
Expression::PrefixExpression(i) => i.token_literal(), Expression::PrefixExpression(i) => i.token_literal(),
Expression::InfixExpression(i) => i.token_literal(), Expression::InfixExpression(i) => i.token_literal(),
Expression::Boolean(i) => i.token_literal(),
Expression::IfExpression(i) => i.token_literal(), Expression::IfExpression(i) => i.token_literal(),
Expression::FunctionLiteral(i) => i.token_literal(), Expression::FunctionLiteral(i) => i.token_literal(),
Expression::CallExpression(i) => i.token_literal(), Expression::CallExpression(i) => i.token_literal(),
Expression::ArrayLiteral(i) => i.token_literal(),
Expression::HashLiteral(i) => i.token_literal(),
Expression::IndexExpression(i) => i.token_literal(),
} }
} }
@@ -42,12 +50,16 @@ impl NodeTrait for Expression {
match self { match self {
Expression::Identifier(i) => i.string(), Expression::Identifier(i) => i.string(),
Expression::IntegerLiteral(i) => i.string(), Expression::IntegerLiteral(i) => i.string(),
Expression::StringLiteral(i) => i.string(),
Expression::Boolean(i) => i.string(),
Expression::PrefixExpression(i) => i.string(), Expression::PrefixExpression(i) => i.string(),
Expression::InfixExpression(i) => i.string(), Expression::InfixExpression(i) => i.string(),
Expression::Boolean(i) => i.string(),
Expression::IfExpression(i) => i.string(), Expression::IfExpression(i) => i.string(),
Expression::FunctionLiteral(i) => i.string(), Expression::FunctionLiteral(i) => i.string(),
Expression::CallExpression(i) => i.string(), Expression::CallExpression(i) => i.string(),
Expression::ArrayLiteral(i) => i.string(),
Expression::HashLiteral(i) => i.string(),
Expression::IndexExpression(i) => i.string(),
} }
} }
@@ -380,3 +392,101 @@ impl NodeTrait for CallExpression {
self self
} }
} }
#[derive(Debug)]
pub struct StringLiteral {
pub token: Token,
pub value: String,
}
impl NodeTrait for StringLiteral {
fn token_literal(&self) -> String {
String::from(&self.token.literal)
}
fn string(&self) -> String {
self.value.clone()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
pub struct ArrayLiteral {
pub token: Token,
pub elements: Vec<Expression>,
}
impl NodeTrait for ArrayLiteral {
fn token_literal(&self) -> String {
String::from(&self.token.literal)
}
fn string(&self) -> String {
let mut out = String::new();
out.push_str("[");
let elements: Vec<String> = self.elements.iter().map(|e| e.string()).collect();
out.push_str(&elements.join(", "));
out.push_str("]");
out
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
pub struct HashLiteral {
pub token: Token,
pub pairs: Vec<(Expression, Expression)>,
}
impl NodeTrait for HashLiteral {
fn token_literal(&self) -> String {
String::from(&self.token.literal)
}
fn string(&self) -> String {
let mut out = String::new();
out.push_str("{");
let pairs: Vec<String> = self
.pairs
.iter()
.map(|(k, v)| format!("{}: {}", k.string(), v.string()))
.collect();
out.push_str(&pairs.join(", "));
out.push_str("}");
out
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
pub struct IndexExpression {
pub token: Token,
pub left: Box<Expression>,
pub index: Box<Expression>,
}
impl NodeTrait for IndexExpression {
fn token_literal(&self) -> String {
String::from(&self.token.literal)
}
fn string(&self) -> String {
let mut out = String::new();
out.push_str("(");
out.push_str(&self.left.string());
out.push_str("[");
out.push_str(&self.index.string());
out.push_str("])");
out
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
+185 -1
View File
@@ -1,8 +1,9 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use crate::ast::{self, NodeTrait}; use crate::ast::{self, NodeTrait};
use crate::object::{self, Environment, Null, Object, ObjectType}; use crate::object::{self, Environment, Hashable, Null, Object, ObjectType};
const NULL: Null = Null; const NULL: Null = Null;
const BOOL_TRUE: object::Boolean = object::Boolean { value: true }; const BOOL_TRUE: object::Boolean = object::Boolean { value: true };
@@ -85,6 +86,9 @@ fn eval_expression(
ast::Expression::IntegerLiteral(int) => { ast::Expression::IntegerLiteral(int) => {
Some(Box::new(object::Integer { value: int.value })) Some(Box::new(object::Integer { value: int.value }))
} }
ast::Expression::StringLiteral(s) => Some(Box::new(object::StringObj {
value: s.value.clone(),
})),
ast::Expression::Boolean(b) => Some(Box::new(native_bool_to_bool_object(b.value))), ast::Expression::Boolean(b) => Some(Box::new(native_bool_to_bool_object(b.value))),
ast::Expression::PrefixExpression(expr) => { ast::Expression::PrefixExpression(expr) => {
let right = eval_expression(expr.right.as_ref(), Rc::clone(&env))?; let right = eval_expression(expr.right.as_ref(), Rc::clone(&env))?;
@@ -121,6 +125,25 @@ fn eval_expression(
} }
apply_function(function, args) apply_function(function, args)
} }
ast::Expression::ArrayLiteral(arr) => {
let elements = eval_expressions(&arr.elements, Rc::clone(&env))?;
if elements.len() == 1 && is_error(elements[0].as_ref()) {
return Some(elements.into_iter().next().unwrap());
}
Some(Box::new(object::Array { elements }))
}
ast::Expression::HashLiteral(hash) => eval_hash_literal(hash, env),
ast::Expression::IndexExpression(idx) => {
let left = eval_expression(idx.left.as_ref(), Rc::clone(&env))?;
if is_error(left.as_ref()) {
return Some(left);
}
let index = eval_expression(idx.index.as_ref(), Rc::clone(&env))?;
if is_error(index.as_ref()) {
return Some(index);
}
Some(eval_index_expression(left, index))
}
} }
} }
@@ -168,6 +191,10 @@ fn apply_function(
return Some(Box::new(NULL)); return Some(Box::new(NULL));
} }
if let Some(builtin) = function.as_any().downcast_ref::<object::Builtin>() {
return Some((builtin.func)(args));
}
Some(new_error(format!( Some(new_error(format!(
"not a function: {:?}", "not a function: {:?}",
function.object_type() function.object_type()
@@ -237,6 +264,10 @@ fn eval_infix_expression(
return eval_boolean_infix_expression(operator, left, right); return eval_boolean_infix_expression(operator, left, right);
} }
if left.object_type() == ObjectType::STRING && right.object_type() == ObjectType::STRING {
return eval_string_infix_expression(operator, left, right);
}
if left.object_type() != right.object_type() { if left.object_type() != right.object_type() {
return Some(new_error(format!( return Some(new_error(format!(
"type mismatch: {:?} {} {:?}", "type mismatch: {:?} {} {:?}",
@@ -292,6 +323,37 @@ fn eval_integer_infix_expression(
} }
} }
fn eval_string_infix_expression(
operator: &str,
left: Box<dyn Object>,
right: Box<dyn Object>,
) -> Option<Box<dyn Object>> {
let left_val = left
.as_any()
.downcast_ref::<object::StringObj>()
.unwrap()
.value
.clone();
let right_val = right
.as_any()
.downcast_ref::<object::StringObj>()
.unwrap()
.value
.clone();
match operator {
"+" => Some(Box::new(object::StringObj {
value: format!("{}{}", left_val, right_val),
})),
_ => Some(new_error(format!(
"unknown operator: {:?} {} {:?}",
left.object_type(),
operator,
right.object_type()
))),
}
}
fn eval_boolean_infix_expression( fn eval_boolean_infix_expression(
operator: &str, operator: &str,
left: Box<dyn Object>, left: Box<dyn Object>,
@@ -316,6 +378,128 @@ fn eval_boolean_infix_expression(
} }
} }
fn eval_hash_literal(
hash: &ast::HashLiteral,
env: Rc<RefCell<Environment>>,
) -> Option<Box<dyn Object>> {
let mut pairs = HashMap::new();
for (k, v) in &hash.pairs {
let key = eval_expression(k, Rc::clone(&env))?;
if is_error(key.as_ref()) {
return Some(key);
}
let value = eval_expression(v, Rc::clone(&env))?;
if is_error(value.as_ref()) {
return Some(value);
}
let hash_key = match key.object_type() {
ObjectType::INTEGER => {
key.as_any()
.downcast_ref::<object::Integer>()
.unwrap()
.hash_key()
}
ObjectType::BOOLEAN => {
key.as_any()
.downcast_ref::<object::Boolean>()
.unwrap()
.hash_key()
}
ObjectType::STRING => {
key.as_any()
.downcast_ref::<object::StringObj>()
.unwrap()
.hash_key()
}
_ => return Some(new_error(format!("unusable as hash key: {:?}", key.object_type()))),
};
pairs.insert(
hash_key,
object::HashPair {
key: key.clone_box(),
value: value.clone_box(),
},
);
}
Some(Box::new(object::Hash { pairs }))
}
fn eval_index_expression(left: Box<dyn Object>, index: Box<dyn Object>) -> Box<dyn Object> {
match (left.object_type(), index.object_type()) {
(ObjectType::ARRAY, ObjectType::INTEGER) => eval_array_index_expression(left, index),
(ObjectType::HASH, _) => eval_hash_index_expression(left, index),
(ObjectType::STRING, ObjectType::INTEGER) => eval_string_index_expression(left, index),
_ => new_error(format!(
"index operator not supported: {:?}[{:?}]",
left.object_type(),
index.object_type()
)),
}
}
fn eval_array_index_expression(
array: Box<dyn Object>,
index: Box<dyn Object>,
) -> Box<dyn Object> {
let arr = array.as_any().downcast_ref::<object::Array>().unwrap();
let idx = index.as_any().downcast_ref::<object::Integer>().unwrap().value;
let max = arr.elements.len() as i64 - 1;
if idx < 0 || idx > max {
return Box::new(NULL);
}
arr.elements[idx as usize].clone_box()
}
fn eval_hash_index_expression(
hash: Box<dyn Object>,
index: Box<dyn Object>,
) -> Box<dyn Object> {
let hash_obj = hash.as_any().downcast_ref::<object::Hash>().unwrap();
let key = match index.object_type() {
ObjectType::INTEGER => index
.as_any()
.downcast_ref::<object::Integer>()
.unwrap()
.hash_key(),
ObjectType::BOOLEAN => index
.as_any()
.downcast_ref::<object::Boolean>()
.unwrap()
.hash_key(),
ObjectType::STRING => index
.as_any()
.downcast_ref::<object::StringObj>()
.unwrap()
.hash_key(),
_ => return new_error(format!("unusable as hash key: {:?}", index.object_type())),
};
match hash_obj.pairs.get(&key) {
Some(pair) => pair.value.clone_box(),
None => Box::new(NULL),
}
}
fn eval_string_index_expression(
string: Box<dyn Object>,
index: Box<dyn Object>,
) -> Box<dyn Object> {
let s = string.as_any().downcast_ref::<object::StringObj>().unwrap();
let idx = index.as_any().downcast_ref::<object::Integer>().unwrap().value;
let max = s.value.len() as i64 - 1;
if idx < 0 || idx > max {
return Box::new(NULL);
}
Box::new(object::StringObj {
value: s.value.chars().nth(idx as usize).unwrap().to_string(),
})
}
fn eval_if_expression( fn eval_if_expression(
expr: &ast::IfExpression, expr: &ast::IfExpression,
env: Rc<RefCell<Environment>>, env: Rc<RefCell<Environment>>,
+132
View File
@@ -30,6 +30,12 @@ fn test_null_object(obj: &dyn Object) {
assert_eq!(obj.object_type(), ObjectType::NULL); assert_eq!(obj.object_type(), ObjectType::NULL);
} }
fn test_string_object(obj: &dyn Object, expected: &str) {
assert_eq!(obj.object_type(), ObjectType::STRING);
let s = obj.as_any().downcast_ref::<object::StringObj>().unwrap();
assert_eq!(s.value, expected);
}
#[test] #[test]
fn test_eval_integer_expression() { fn test_eval_integer_expression() {
let tests = vec![("5", 5), ("10", 10), ("-5", -5), ("-10", -10)]; let tests = vec![("5", 5), ("10", 10), ("-5", -5), ("-10", -10)];
@@ -219,6 +225,8 @@ fn test_error_handling() {
("5; true + false; 5", "unknown operator: BOOLEAN + BOOLEAN"), ("5; true + false; 5", "unknown operator: BOOLEAN + BOOLEAN"),
("if (10 > 1) { true + false; }", "unknown operator: BOOLEAN + BOOLEAN"), ("if (10 > 1) { true + false; }", "unknown operator: BOOLEAN + BOOLEAN"),
("foobar", "identifier not found: foobar"), ("foobar", "identifier not found: foobar"),
("len(1)", "argument to `len` not supported"),
("first(1)", "argument to `first` must be ARRAY"),
]; ];
for (input, expected) in tests { for (input, expected) in tests {
@@ -228,3 +236,127 @@ fn test_error_handling() {
assert!(err.message.contains(expected), "expected '{}' to contain '{}'", err.message, expected); assert!(err.message.contains(expected), "expected '{}' to contain '{}'", err.message, expected);
} }
} }
#[test]
fn test_eval_string_expression() {
let input = "\"Hello World\"";
let evaluated = test_eval(input).unwrap();
test_string_object(evaluated.as_ref(), "Hello World");
}
#[test]
fn test_string_concatenation() {
let input = "\"Hello\" + \" \" + \"World!\"";
let evaluated = test_eval(input).unwrap();
test_string_object(evaluated.as_ref(), "Hello World!");
}
#[test]
fn test_string_indexing() {
let tests = vec![
("\"abc\"[0]", "a"),
("\"abc\"[1]", "b"),
("\"abc\"[2]", "c"),
];
for (input, expected) in tests {
let evaluated = test_eval(input).unwrap();
test_string_object(evaluated.as_ref(), expected);
}
}
#[test]
fn test_eval_array_expression() {
let input = "[1, 2 * 2, 3 + 3]";
let evaluated = test_eval(input).unwrap();
assert_eq!(evaluated.object_type(), ObjectType::ARRAY);
let arr = evaluated.as_any().downcast_ref::<object::Array>().unwrap();
assert_eq!(arr.elements.len(), 3);
test_integer_object(arr.elements[0].as_ref(), 1);
test_integer_object(arr.elements[1].as_ref(), 4);
test_integer_object(arr.elements[2].as_ref(), 6);
}
#[test]
fn test_eval_array_index() {
let tests = vec![
("[1, 2, 3][0]", 1),
("[1, 2, 3][1]", 2),
("[1, 2, 3][2]", 3),
("let i = 0; [1][i];", 1),
("[1, 2, 3][1 + 1];", 3),
("let myArray = [1, 2, 3]; myArray[2];", 3),
("let myArray = [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];", 6),
("let myArray = [1, 2, 3]; let i = myArray[0]; myArray[i];", 2),
];
for (input, expected) in tests {
let evaluated = test_eval(input).unwrap();
test_integer_object(evaluated.as_ref(), expected);
}
}
#[test]
fn test_eval_hash_expression() {
let input = r#"{"one": 1, "two": 2, "three": 3}"#;
let evaluated = test_eval(input).unwrap();
assert_eq!(evaluated.object_type(), ObjectType::HASH);
let hash = evaluated.as_any().downcast_ref::<object::Hash>().unwrap();
assert_eq!(hash.pairs.len(), 3);
}
#[test]
fn test_eval_hash_index() {
let tests = vec![
(r#"{"foo": 5}["foo"]"#, 5),
(r#"{"foo": 5}["bar"]"#, 0), // expect null, we'll check separately
(r#"let key = "foo"; {"foo": 5}[key]"#, 5),
("{true: 1}[true]", 1),
("{1: 1, 2: 2}[2]", 2),
];
for (input, expected) in tests {
let evaluated = test_eval(input).unwrap();
if input.contains("\"bar\"") {
test_null_object(evaluated.as_ref());
} else {
test_integer_object(evaluated.as_ref(), expected);
}
}
}
#[test]
fn test_builtin_functions() {
let tests = vec![
("len(\"\")", 0),
("len(\"four\")", 4),
("len(\"hello world\")", 11),
("len([1, 2, 3])", 3),
("len([])", 0),
("first([1, 2, 3])", 1),
("last([1, 2, 3])", 3),
];
for (input, expected) in tests {
let evaluated = test_eval(input).unwrap();
test_integer_object(evaluated.as_ref(), expected);
}
}
#[test]
fn test_builtin_array_functions() {
let input = "let a = [1, 2, 3]; push(a, 4);";
let evaluated = test_eval(input).unwrap();
assert_eq!(evaluated.object_type(), ObjectType::ARRAY);
let arr = evaluated.as_any().downcast_ref::<object::Array>().unwrap();
assert_eq!(arr.elements.len(), 4);
test_integer_object(arr.elements[3].as_ref(), 4);
let input = "rest([1, 2, 3]);";
let evaluated = test_eval(input).unwrap();
assert_eq!(evaluated.object_type(), ObjectType::ARRAY);
let arr = evaluated.as_any().downcast_ref::<object::Array>().unwrap();
assert_eq!(arr.elements.len(), 2);
test_integer_object(arr.elements[0].as_ref(), 2);
test_integer_object(arr.elements[1].as_ref(), 3);
}
+20
View File
@@ -12,6 +12,7 @@ pub trait LexerTraits {
fn read_char(&mut self); fn read_char(&mut self);
fn next_token(&mut self) -> Token; fn next_token(&mut self) -> Token;
fn read_identifier(&mut self) -> String; fn read_identifier(&mut self) -> String;
fn read_string(&mut self) -> String;
fn skip_whitespace(&mut self); fn skip_whitespace(&mut self);
fn read_number(&mut self) -> String; fn read_number(&mut self) -> String;
fn peek_char(&self) -> u8; fn peek_char(&self) -> u8;
@@ -81,6 +82,14 @@ impl LexerTraits for Lexer {
'>' => new_token(TokenType::Gt, lit), '>' => new_token(TokenType::Gt, lit),
'{' => new_token(TokenType::Lbrace, lit), '{' => new_token(TokenType::Lbrace, lit),
'}' => new_token(TokenType::Rbrace, lit), '}' => new_token(TokenType::Rbrace, lit),
'[' => new_token(TokenType::Lbracket, lit),
']' => new_token(TokenType::Rbracket, lit),
':' => new_token(TokenType::Colon, lit),
'"' => {
let s = self.read_string();
self.read_char();
return new_token_from_str(TokenType::String, s);
}
_ => { _ => {
if c.is_alphabetic() { if c.is_alphabetic() {
let lit = self.read_identifier(); let lit = self.read_identifier();
@@ -110,6 +119,17 @@ impl LexerTraits for Lexer {
String::from(res) String::from(res)
} }
fn read_string(&mut self) -> String {
let position = self.position.unwrap() + 1;
self.read_char();
while self.ch.unwrap() != b'"' && self.ch.unwrap() != 0 {
self.read_char();
}
let read_pos = self.position.unwrap() as usize;
let position = position as usize;
String::from(&self.input[position..read_pos])
}
fn skip_whitespace(&mut self) { fn skip_whitespace(&mut self) {
while char::from(self.ch.unwrap()) == ' ' while char::from(self.ch.unwrap()) == ' '
|| char::from(self.ch.unwrap()) == '\n' || char::from(self.ch.unwrap()) == '\n'
+279 -1
View File
@@ -13,6 +13,10 @@ pub enum ObjectType {
RETURN_VALUE, RETURN_VALUE,
ERROR, ERROR,
FUNCTION, FUNCTION,
STRING,
ARRAY,
HASH,
BUILTIN,
} }
pub trait Object { pub trait Object {
@@ -167,6 +171,151 @@ impl Object for Function {
} }
} }
pub struct StringObj {
pub value: String,
}
impl Object for StringObj {
fn object_type(&self) -> ObjectType {
ObjectType::STRING
}
fn inspect(&self) -> String {
self.value.clone()
}
fn clone_box(&self) -> Box<dyn Object> {
Box::new(StringObj {
value: self.value.clone(),
})
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
pub struct Array {
pub elements: Vec<Box<dyn Object>>,
}
impl Object for Array {
fn object_type(&self) -> ObjectType {
ObjectType::ARRAY
}
fn inspect(&self) -> String {
let elements: Vec<String> = self.elements.iter().map(|e| e.inspect()).collect();
format!("[{}]", elements.join(", "))
}
fn clone_box(&self) -> Box<dyn Object> {
Box::new(Array {
elements: self.elements.iter().map(|e| e.clone_box()).collect(),
})
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum HashKey {
Integer(i64),
Boolean(bool),
String(String),
}
pub trait Hashable {
fn hash_key(&self) -> HashKey;
}
impl Hashable for Integer {
fn hash_key(&self) -> HashKey {
HashKey::Integer(self.value)
}
}
impl Hashable for Boolean {
fn hash_key(&self) -> HashKey {
HashKey::Boolean(self.value)
}
}
impl Hashable for StringObj {
fn hash_key(&self) -> HashKey {
HashKey::String(self.value.clone())
}
}
pub struct HashPair {
pub key: Box<dyn Object>,
pub value: Box<dyn Object>,
}
pub struct Hash {
pub pairs: HashMap<HashKey, HashPair>,
}
impl Object for Hash {
fn object_type(&self) -> ObjectType {
ObjectType::HASH
}
fn inspect(&self) -> String {
let mut pairs: Vec<String> = self
.pairs
.values()
.map(|pair| format!("{}: {}", pair.key.inspect(), pair.value.inspect()))
.collect();
pairs.sort();
format!("{{{}}}", pairs.join(", "))
}
fn clone_box(&self) -> Box<dyn Object> {
let mut new_pairs = HashMap::new();
for (k, v) in &self.pairs {
new_pairs.insert(
k.clone(),
HashPair {
key: v.key.clone_box(),
value: v.value.clone_box(),
},
);
}
Box::new(Hash { pairs: new_pairs })
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
pub type BuiltinFunction = fn(Vec<Box<dyn Object>>) -> Box<dyn Object>;
pub struct Builtin {
pub func: BuiltinFunction,
}
impl Object for Builtin {
fn object_type(&self) -> ObjectType {
ObjectType::BUILTIN
}
fn inspect(&self) -> String {
"builtin function".to_string()
}
fn clone_box(&self) -> Box<dyn Object> {
Box::new(Builtin { func: self.func })
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
#[derive(Default)] #[derive(Default)]
pub struct Environment { pub struct Environment {
store: HashMap<String, Box<dyn Object>>, store: HashMap<String, Box<dyn Object>>,
@@ -175,10 +324,21 @@ pub struct Environment {
impl Environment { impl Environment {
pub fn new() -> Self { pub fn new() -> Self {
Self { let mut env = Self {
store: HashMap::new(), store: HashMap::new(),
outer: None, outer: None,
};
env.register_builtins();
env
} }
fn register_builtins(&mut self) {
self.set("len", Box::new(Builtin { func: builtin_len }));
self.set("first", Box::new(Builtin { func: builtin_first }));
self.set("last", Box::new(Builtin { func: builtin_last }));
self.set("rest", Box::new(Builtin { func: builtin_rest }));
self.set("push", Box::new(Builtin { func: builtin_push }));
self.set("puts", Box::new(Builtin { func: builtin_puts }));
} }
pub fn from_outer(outer: Rc<RefCell<Environment>>) -> Self { pub fn from_outer(outer: Rc<RefCell<Environment>>) -> Self {
@@ -202,3 +362,121 @@ impl Environment {
self.store.insert(name.to_string(), value); self.store.insert(name.to_string(), value);
} }
} }
fn builtin_len(args: Vec<Box<dyn Object>>) -> Box<dyn Object> {
if args.len() != 1 {
return Box::new(Error {
message: format!("wrong number of arguments. got={}, want=1", args.len()),
});
}
if let Some(s) = args[0].as_any().downcast_ref::<StringObj>() {
return Box::new(Integer {
value: s.value.len() as i64,
});
}
if let Some(arr) = args[0].as_any().downcast_ref::<Array>() {
return Box::new(Integer {
value: arr.elements.len() as i64,
});
}
Box::new(Error {
message: format!("argument to `len` not supported, got {:?}", args[0].object_type()),
})
}
fn builtin_first(args: Vec<Box<dyn Object>>) -> Box<dyn Object> {
if args.len() != 1 {
return Box::new(Error {
message: format!("wrong number of arguments. got={}, want=1", args.len()),
});
}
if let Some(arr) = args[0].as_any().downcast_ref::<Array>() {
if !arr.elements.is_empty() {
return arr.elements[0].clone_box();
}
return Box::new(Null);
}
Box::new(Error {
message: format!(
"argument to `first` must be ARRAY, got {:?}",
args[0].object_type()
),
})
}
fn builtin_last(args: Vec<Box<dyn Object>>) -> Box<dyn Object> {
if args.len() != 1 {
return Box::new(Error {
message: format!("wrong number of arguments. got={}, want=1", args.len()),
});
}
if let Some(arr) = args[0].as_any().downcast_ref::<Array>() {
if let Some(last) = arr.elements.last() {
return last.clone_box();
}
return Box::new(Null);
}
Box::new(Error {
message: format!(
"argument to `last` must be ARRAY, got {:?}",
args[0].object_type()
),
})
}
fn builtin_rest(args: Vec<Box<dyn Object>>) -> Box<dyn Object> {
if args.len() != 1 {
return Box::new(Error {
message: format!("wrong number of arguments. got={}, want=1", args.len()),
});
}
if let Some(arr) = args[0].as_any().downcast_ref::<Array>() {
if arr.elements.is_empty() {
return Box::new(Null);
}
let mut new_elements = Vec::new();
for (i, el) in arr.elements.iter().enumerate() {
if i != 0 {
new_elements.push(el.clone_box());
}
}
return Box::new(Array {
elements: new_elements,
});
}
Box::new(Error {
message: format!(
"argument to `rest` must be ARRAY, got {:?}",
args[0].object_type()
),
})
}
fn builtin_push(args: Vec<Box<dyn Object>>) -> Box<dyn Object> {
if args.len() != 2 {
return Box::new(Error {
message: format!("wrong number of arguments. got={}, want=2", args.len()),
});
}
if let Some(arr) = args[0].as_any().downcast_ref::<Array>() {
let mut new_elements: Vec<Box<dyn Object>> =
arr.elements.iter().map(|e| e.clone_box()).collect();
new_elements.push(args[1].clone_box());
return Box::new(Array {
elements: new_elements,
});
}
Box::new(Error {
message: format!(
"argument to `push` must be ARRAY, got {:?}",
args[0].object_type()
),
})
}
fn builtin_puts(args: Vec<Box<dyn Object>>) -> Box<dyn Object> {
for arg in args {
println!("{}", arg.inspect());
}
Box::new(Null)
}
+84
View File
@@ -51,7 +51,11 @@ pub fn new_parser(l: lexer::Lexer) -> Parser {
p.register_prefix(TokenType::Lparen, Parser::parse_grouped_expression); p.register_prefix(TokenType::Lparen, Parser::parse_grouped_expression);
p.register_prefix(TokenType::If, Parser::parse_if_expression); p.register_prefix(TokenType::If, Parser::parse_if_expression);
p.register_prefix(TokenType::Function, Parser::parse_function_literal); p.register_prefix(TokenType::Function, Parser::parse_function_literal);
p.register_prefix(TokenType::String, Parser::parse_string_literal);
p.register_prefix(TokenType::Lbracket, Parser::parse_array_literal);
p.register_prefix(TokenType::Lbrace, Parser::parse_hash_literal);
p.register_infix(TokenType::Lparen, Parser::parse_call_expression); p.register_infix(TokenType::Lparen, Parser::parse_call_expression);
p.register_infix(TokenType::Lbracket, Parser::parse_index_expression);
p.next_token(); p.next_token();
p.next_token(); p.next_token();
p p
@@ -96,6 +100,7 @@ impl Parser {
TokenType::Plus | TokenType::Minus => Precedence::Sum, TokenType::Plus | TokenType::Minus => Precedence::Sum,
TokenType::Slash | TokenType::Asterisk => Precedence::Product, TokenType::Slash | TokenType::Asterisk => Precedence::Product,
TokenType::Lparen => Precedence::Call, TokenType::Lparen => Precedence::Call,
TokenType::Lbracket => Precedence::Index,
_ => Precedence::Lowest, _ => Precedence::Lowest,
} }
} }
@@ -107,6 +112,7 @@ impl Parser {
TokenType::Plus | TokenType::Minus => Precedence::Sum, TokenType::Plus | TokenType::Minus => Precedence::Sum,
TokenType::Slash | TokenType::Asterisk => Precedence::Product, TokenType::Slash | TokenType::Asterisk => Precedence::Product,
TokenType::Lparen => Precedence::Call, TokenType::Lparen => Precedence::Call,
TokenType::Lbracket => Precedence::Index,
_ => Precedence::Lowest, _ => Precedence::Lowest,
} }
} }
@@ -304,6 +310,83 @@ impl Parser {
})) }))
} }
fn parse_string_literal(&mut self) -> Option<ast::Expression> {
Some(ast::Expression::StringLiteral(ast::StringLiteral {
token: self.cur_token.clone(),
value: self.cur_token.literal.clone(),
}))
}
fn parse_array_literal(&mut self) -> Option<ast::Expression> {
let token = self.cur_token.clone();
let elements = self.parse_expression_list(TokenType::Rbracket)?;
Some(ast::Expression::ArrayLiteral(ast::ArrayLiteral {
token,
elements,
}))
}
fn parse_hash_literal(&mut self) -> Option<ast::Expression> {
let token = self.cur_token.clone();
let mut pairs = Vec::new();
while !self.peek_token_is(TokenType::Rbrace) {
self.next_token();
let key = self.parse_expression(Precedence::Lowest)?;
if !self.expect_peek(TokenType::Colon) {
return None;
}
self.next_token();
let value = self.parse_expression(Precedence::Lowest)?;
pairs.push((key, value));
if !self.peek_token_is(TokenType::Rbrace) && !self.expect_peek(TokenType::Comma) {
return None;
}
}
if !self.expect_peek(TokenType::Rbrace) {
return None;
}
Some(ast::Expression::HashLiteral(ast::HashLiteral { token, pairs }))
}
fn parse_expression_list(&mut self, end: TokenType) -> Option<Vec<ast::Expression>> {
let mut list = Vec::new();
if self.peek_token_is(end) {
self.next_token();
return Some(list);
}
self.next_token();
list.push(self.parse_expression(Precedence::Lowest)?);
while self.peek_token_is(TokenType::Comma) {
self.next_token();
self.next_token();
list.push(self.parse_expression(Precedence::Lowest)?);
}
if !self.expect_peek(end) {
return None;
}
Some(list)
}
fn parse_index_expression(
&mut self,
left: ast::Expression,
) -> Option<ast::Expression> {
let token = self.cur_token.clone();
self.next_token();
let index = self.parse_expression(Precedence::Lowest)?;
if !self.expect_peek(TokenType::Rbracket) {
return None;
}
Some(ast::Expression::IndexExpression(ast::IndexExpression {
token,
left: Box::new(left),
index: Box::new(index),
}))
}
fn parse_grouped_expression(&mut self) -> Option<ast::Expression> { fn parse_grouped_expression(&mut self) -> Option<ast::Expression> {
self.next_token(); self.next_token();
let exp = self.parse_expression(Precedence::Lowest)?; let exp = self.parse_expression(Precedence::Lowest)?;
@@ -464,4 +547,5 @@ pub enum Precedence {
Product, Product,
Prefix, Prefix,
Call, Call,
Index,
} }
+8
View File
@@ -7,6 +7,7 @@ pub enum TokenType {
Eof, Eof,
Ident, Ident,
Int, Int,
String,
Assign, Assign,
Plus, Plus,
Minus, Minus,
@@ -17,10 +18,13 @@ pub enum TokenType {
Gt, Gt,
Comma, Comma,
Semicolon, Semicolon,
Colon,
Lparen, Lparen,
Rparen, Rparen,
Lbrace, Lbrace,
Rbrace, Rbrace,
Lbracket,
Rbracket,
Function, Function,
Let, Let,
True, True,
@@ -39,6 +43,7 @@ impl fmt::Display for TokenType {
TokenType::Eof => write!(f, "EOF"), TokenType::Eof => write!(f, "EOF"),
TokenType::Ident => write!(f, "IDENT"), TokenType::Ident => write!(f, "IDENT"),
TokenType::Int => write!(f, "INT"), TokenType::Int => write!(f, "INT"),
TokenType::String => write!(f, "STRING"),
TokenType::Assign => write!(f, "="), TokenType::Assign => write!(f, "="),
TokenType::Plus => write!(f, "+"), TokenType::Plus => write!(f, "+"),
TokenType::Minus => write!(f, "-"), TokenType::Minus => write!(f, "-"),
@@ -49,10 +54,13 @@ impl fmt::Display for TokenType {
TokenType::Gt => write!(f, ">"), TokenType::Gt => write!(f, ">"),
TokenType::Comma => write!(f, ","), TokenType::Comma => write!(f, ","),
TokenType::Semicolon => write!(f, ";"), TokenType::Semicolon => write!(f, ";"),
TokenType::Colon => write!(f, ":"),
TokenType::Lparen => write!(f, "("), TokenType::Lparen => write!(f, "("),
TokenType::Rparen => write!(f, ")"), TokenType::Rparen => write!(f, ")"),
TokenType::Lbrace => write!(f, "{{"), TokenType::Lbrace => write!(f, "{{"),
TokenType::Rbrace => write!(f, "}}"), TokenType::Rbrace => write!(f, "}}"),
TokenType::Lbracket => write!(f, "["),
TokenType::Rbracket => write!(f, "]"),
TokenType::Function => write!(f, "FUNCTION"), TokenType::Function => write!(f, "FUNCTION"),
TokenType::Let => write!(f, "LET"), TokenType::Let => write!(f, "LET"),
TokenType::True => write!(f, "TRUE"), TokenType::True => write!(f, "TRUE"),