data types chapter
This commit is contained in:
+113
-3
@@ -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
@@ -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>>,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
Reference in New Issue
Block a user