WIP: Hash evaluator

This commit is contained in:
Ishan Jain 2024-05-27 16:26:52 +05:30
parent 37306989fe
commit f51ead1641
Signed by: ishan
GPG Key ID: 0506DB2A1CC75C27
5 changed files with 285 additions and 48 deletions

View File

@ -3,8 +3,9 @@ use {
itertools::Itertools, itertools::Itertools,
std::{ std::{
cell::RefCell, cell::RefCell,
collections::HashMap, collections::BTreeMap,
fmt::{self, Display, Formatter, Result as FmtResult, Write}, fmt::{self, Display, Formatter, Result as FmtResult, Write},
hash::{Hash, Hasher},
rc::Rc, rc::Rc,
}, },
}; };
@ -15,16 +16,25 @@ pub trait Evaluator {
fn eval(&self, node: Node, env: Rc<RefCell<Environment>>) -> Option<Object>; fn eval(&self, node: Node, env: Rc<RefCell<Environment>>) -> Option<Object>;
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Environment { pub struct Environment {
store: HashMap<String, Object>, store: BTreeMap<String, Object>,
outer: Option<Rc<RefCell<Environment>>>, outer: Option<OuterEnvironment>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct OuterEnvironment(Rc<RefCell<Environment>>);
impl Hash for OuterEnvironment {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.borrow().hash(state);
}
} }
impl Environment { impl Environment {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
store: HashMap::new(), store: BTreeMap::new(),
outer: None, outer: None,
} }
} }
@ -33,7 +43,7 @@ impl Environment {
Some(v) => Some(v.clone()), Some(v) => Some(v.clone()),
None => match &self.outer { None => match &self.outer {
Some(outer) => { Some(outer) => {
let outer = outer.borrow(); let outer = outer.0.borrow();
outer.get(name) outer.get(name)
} }
None => None, None => None,
@ -44,15 +54,15 @@ impl Environment {
self.store.insert(name, val); self.store.insert(name, val);
} }
pub fn new_enclosed(env: Rc<RefCell<Environment>>) -> Self { pub fn new_enclosed(env: OuterEnvironment) -> Self {
Self { Self {
store: HashMap::new(), store: BTreeMap::new(),
outer: Some(env), outer: Some(env),
} }
} }
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Hash, Ord, PartialOrd, Eq)]
pub enum Object { pub enum Object {
Integer(i64), Integer(i64),
String(String), String(String),
@ -62,6 +72,7 @@ pub enum Object {
Function(Function), Function(Function),
Builtin(BuiltinFunction), Builtin(BuiltinFunction),
Array(Array), Array(Array),
Hash(HashObject),
Null, Null,
} }
@ -90,6 +101,15 @@ impl Object {
out out
} }
Object::Hash(h) => {
let mut pairs = vec![];
for (k, v) in h.pairs.iter() {
pairs.push(format!("{}: {}", k.inspect(), v.inspect()));
}
format!("{{ {} }}", pairs.join(", "))
}
} }
} }
} }
@ -106,20 +126,21 @@ impl Display for Object {
Object::Null => "NULL", Object::Null => "NULL",
Object::Builtin(_) => "BUILTIN", Object::Builtin(_) => "BUILTIN",
Object::Array(_) => "ARRAY", Object::Array(_) => "ARRAY",
Object::Hash(_) => "HASH",
}) })
} }
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct Function { pub struct Function {
parameters: Vec<Identifier>, parameters: Vec<Identifier>,
body: BlockStatement, body: BlockStatement,
env: Rc<RefCell<Environment>>, env: OuterEnvironment,
} }
type Builtin = fn(Vec<Object>) -> Object; type Builtin = fn(Vec<Object>) -> Object;
#[derive(Clone)] #[derive(Clone, Hash, Ord, PartialOrd, PartialEq, Eq)]
pub struct BuiltinFunction { pub struct BuiltinFunction {
func: Box<Builtin>, func: Box<Builtin>,
} }
@ -132,17 +153,22 @@ impl fmt::Debug for BuiltinFunction {
} }
} }
impl PartialEq for BuiltinFunction { #[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
fn eq(&self, _: &Self) -> bool {
false
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Array { pub struct Array {
elements: Vec<Object>, elements: Vec<Object>,
} }
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub struct HashObject {
pairs: BTreeMap<Object, Object>,
}
impl HashObject {
fn new(ip: impl Into<BTreeMap<Object, Object>>) -> Self {
Self { pairs: ip.into() }
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{assert_matches::assert_matches, cell::RefCell, rc::Rc}; use std::{assert_matches::assert_matches, cell::RefCell, rc::Rc};
@ -153,7 +179,7 @@ mod tests {
parser::{ast::Node, Parser}, parser::{ast::Node, Parser},
}; };
use super::Array; use super::{Array, HashObject};
const TRUE: Object = Object::Boolean(true); const TRUE: Object = Object::Boolean(true);
const FALSE: Object = Object::Boolean(false); const FALSE: Object = Object::Boolean(false);
const NULL: Object = Object::Null; const NULL: Object = Object::Null;
@ -340,6 +366,10 @@ mod tests {
"\"Hello\" - \"World\"", "\"Hello\" - \"World\"",
Some(Object::Error("unknown operator: STRING - STRING".into())), Some(Object::Error("unknown operator: STRING - STRING".into())),
), ),
(
"{\"name\": \"Monkey\"}[fn(x) {x}];",
Some(Object::Error("unusable as hash key: FUNCTION".into())),
),
]; ];
run_test_cases(&test_cases); run_test_cases(&test_cases);
@ -527,4 +557,49 @@ mod tests {
run_test_cases(&test_cases); run_test_cases(&test_cases);
} }
#[test]
fn hash_literals() {
let test_cases = [(
"let two = \"two\";
{
\"one\": 10 - 9,
two: 1 + 1,
\"thr\" + \"ee\": 6 / 2,
4: 4,
true: 5,
false: 6
}
",
Some(Object::Hash(HashObject::new([
(Object::String("one".to_string()), Object::Integer(1)),
(Object::String("two".to_string()), Object::Integer(2)),
(Object::String("three".to_string()), Object::Integer(3)),
(Object::Integer(4), Object::Integer(4)),
(Object::Boolean(true), Object::Integer(5)),
(Object::Boolean(false), Object::Integer(6)),
]))),
)];
run_test_cases(&test_cases);
}
#[test]
fn hash_index_expressions() {
let test_cases = [
("{\"foo\": 5}[\"foo\"]", Some(Object::Integer(5))),
("{\"foo\": 5}[\"var\"]", Some(Object::Null)),
(
"let key = \"foo\"; {\"foo\": 5}[key]",
Some(Object::Integer(5)),
),
("{}[\"foo\"]", Some(Object::Null)),
("{5: 5}[5]", Some(Object::Integer(5))),
("{true: 5}[true]", Some(Object::Integer(5))),
("{false: 5}[false]", Some(Object::Integer(5))),
("{true: 5}[false]", Some(Object::Null)),
];
run_test_cases(&test_cases);
}
} }

View File

@ -1,4 +1,4 @@
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, collections::BTreeMap, rc::Rc};
// TODO: This is all a mess. Almost certainly because right now, I don't know any better way to do this. // TODO: This is all a mess. Almost certainly because right now, I don't know any better way to do this.
// It's just constantly unwrapping enums from one place and rewrapping it to some other enum(or even the same enum) and returning it // It's just constantly unwrapping enums from one place and rewrapping it to some other enum(or even the same enum) and returning it
@ -7,12 +7,12 @@ use crate::{
evaluator::{Array, Evaluator, Object}, evaluator::{Array, Evaluator, Object},
lexer::TokenType, lexer::TokenType,
parser::ast::{ parser::ast::{
BlockStatement, Expression, ExpressionStatement, Identifier, LetStatement, Node, Program, BlockStatement, Expression, ExpressionStatement, HashLiteral, Identifier, LetStatement,
Statement, Node, Program, Statement,
}, },
}; };
use super::{builtins::BUILTINS, Environment, Function}; use super::{builtins::BUILTINS, Environment, Function, HashObject, OuterEnvironment};
pub struct TreeWalker; pub struct TreeWalker;
@ -47,9 +47,8 @@ impl Evaluator for TreeWalker {
Expression::Identifier(v) => self.eval_identifier(v, env), Expression::Identifier(v) => self.eval_identifier(v, env),
Expression::IntegerLiteral(il) => Some(Object::Integer(il.value)), Expression::IntegerLiteral(il) => Some(Object::Integer(il.value)),
Expression::StringLiteral(s) => Some(Object::String(s.value)), Expression::StringLiteral(s) => Some(Object::String(s.value)),
Expression::HashLiteral(h) => self.eval_hash_literal(h, env),
Expression::ArrayLiteral(v) => { Expression::ArrayLiteral(v) => {
println!("{:?}", v);
let args = match self.eval_expression(v.elements, env) { let args = match self.eval_expression(v.elements, env) {
Ok(v) => v, Ok(v) => v,
Err(e) => return Some(e), Err(e) => return Some(e),
@ -76,7 +75,7 @@ impl Evaluator for TreeWalker {
Expression::FunctionExpression(fnl) => Some(Object::Function(Function { Expression::FunctionExpression(fnl) => Some(Object::Function(Function {
body: fnl.body, body: fnl.body,
parameters: fnl.parameters, parameters: fnl.parameters,
env, env: OuterEnvironment(env),
})), })),
Expression::CallExpression(v) => { Expression::CallExpression(v) => {
let function = self.eval(Node::Expression(*v.function), env.clone())?; let function = self.eval(Node::Expression(*v.function), env.clone())?;
@ -305,8 +304,11 @@ impl TreeWalker {
} }
fn eval_index_expression(&self, left: Object, index: Object) -> Option<Object> { fn eval_index_expression(&self, left: Object, index: Object) -> Option<Object> {
match (&left, index) { match (&left, &index) {
(Object::Array(a), Object::Integer(i)) => Some(Self::eval_array_index_expression(a, i)), (Object::Array(a), Object::Integer(i)) => {
Some(Self::eval_array_index_expression(a, *i))
}
(Object::Hash(h), _) => self.eval_hash_index_expression(h, index),
_ => Some(Object::Error(format!( _ => Some(Object::Error(format!(
"index operator not supported: {}", "index operator not supported: {}",
@ -315,6 +317,12 @@ impl TreeWalker {
} }
} }
fn eval_hash_index_expression(&self, left: &HashObject, index: Object) -> Option<Object> {
Some(Object::Error(format!(
"index operator not supported: {:?}",
left
)))
}
fn eval_array_index_expression(array: &Array, index: i64) -> Object { fn eval_array_index_expression(array: &Array, index: i64) -> Object {
let max = array.elements.len() as i64; let max = array.elements.len() as i64;
if index < 0 || index >= max { if index < 0 || index >= max {
@ -322,4 +330,22 @@ impl TreeWalker {
} }
array.elements[index as usize].clone() array.elements[index as usize].clone()
} }
fn eval_hash_literal(
&self,
node: HashLiteral,
env: Rc<RefCell<Environment>>,
) -> Option<Object> {
let mut out = BTreeMap::new();
for (k, v) in node.pairs.into_iter() {
let k = self.eval(Node::Expression(k), env.clone())?;
let v = self.eval(Node::Expression(v), env.clone())?;
out.insert(k, v);
}
Some(Object::Hash(HashObject { pairs: out }))
}
} }

View File

@ -19,7 +19,7 @@ lazy_static! {
}; };
} }
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] #[derive(Debug, PartialEq, Eq, Copy, Clone, Ord, PartialOrd, Hash)]
pub enum TokenType { pub enum TokenType {
Illegal, Illegal,
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
@ -47,6 +47,7 @@ pub enum TokenType {
// Delimiter // Delimiter
Comma, Comma,
Semicolon, Semicolon,
Colon,
LParen, LParen,
RParen, RParen,
LBrace, LBrace,
@ -79,6 +80,7 @@ impl Display for TokenType {
TokenType::NotEquals => "!=", TokenType::NotEquals => "!=",
TokenType::Comma => ",", TokenType::Comma => ",",
TokenType::Semicolon => ";", TokenType::Semicolon => ";",
TokenType::Colon => ":",
TokenType::LParen => "(", TokenType::LParen => "(",
TokenType::RParen => ")", TokenType::RParen => ")",
TokenType::LBrace => "{", TokenType::LBrace => "{",
@ -101,7 +103,7 @@ impl Display for TokenType {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone, Hash)] #[derive(Debug, PartialEq, Eq, Clone, Ord, PartialOrd, Hash)]
pub struct Token { pub struct Token {
pub name: TokenType, pub name: TokenType,
pub literal: Option<String>, pub literal: Option<String>,
@ -243,6 +245,7 @@ impl<'a> Iterator for Lexer<'a> {
Some('-') => Some(token!(TokenType::Minus)), Some('-') => Some(token!(TokenType::Minus)),
Some(',') => Some(token!(TokenType::Comma)), Some(',') => Some(token!(TokenType::Comma)),
Some(';') => Some(token!(TokenType::Semicolon)), Some(';') => Some(token!(TokenType::Semicolon)),
Some(':') => Some(token!(TokenType::Colon)),
Some('(') => Some(token!(TokenType::LParen)), Some('(') => Some(token!(TokenType::LParen)),
Some(')') => Some(token!(TokenType::RParen)), Some(')') => Some(token!(TokenType::RParen)),
Some('[') => Some(token!(TokenType::LBracket)), Some('[') => Some(token!(TokenType::LBracket)),
@ -387,6 +390,8 @@ mod tests {
\"foo bar\" \"foo bar\"
[1,2]; [1,2];
{\"foo\": \"bar\"}
" "
) )
.collect::<Vec<Token>>(), .collect::<Vec<Token>>(),
@ -446,6 +451,11 @@ mod tests {
token!(TokenType::Int, "2"), token!(TokenType::Int, "2"),
token!(TokenType::RBracket), token!(TokenType::RBracket),
token!(TokenType::Semicolon), token!(TokenType::Semicolon),
token!(TokenType::LBrace),
token!(TokenType::String, "foo"),
token!(TokenType::Colon),
token!(TokenType::String, "bar"),
token!(TokenType::RBrace),
token!(TokenType::EOF), token!(TokenType::EOF),
], ],
); );

View File

@ -6,6 +6,7 @@ use {
itertools::Itertools, itertools::Itertools,
std::{ std::{
cmp::PartialOrd, cmp::PartialOrd,
collections::BTreeMap,
convert::From, convert::From,
fmt::{Display, Formatter, Result as FmtResult, Write}, fmt::{Display, Formatter, Result as FmtResult, Write},
}, },
@ -34,7 +35,7 @@ impl Display for Program {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub enum Statement { pub enum Statement {
Let(LetStatement), Let(LetStatement),
Return(ReturnStatement), Return(ReturnStatement),
@ -65,7 +66,7 @@ impl Display for Statement {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct LetStatement { pub struct LetStatement {
// name field is to store the identifier of the binding // name field is to store the identifier of the binding
pub name: Identifier, pub name: Identifier,
@ -113,7 +114,7 @@ impl Display for LetStatement {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct ReturnStatement { pub struct ReturnStatement {
pub value: Option<Expression>, pub value: Option<Expression>,
} }
@ -145,7 +146,7 @@ impl Display for ReturnStatement {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct ExpressionStatement { pub struct ExpressionStatement {
token: Token, token: Token,
pub expression: Expression, pub expression: Expression,
@ -185,12 +186,13 @@ pub enum ExpressionPriority {
Index = 7, Index = 7,
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, Hash, PartialEq, Eq, Ord, PartialOrd, Clone)]
pub enum Expression { pub enum Expression {
Identifier(Identifier), Identifier(Identifier),
IntegerLiteral(IntegerLiteral), IntegerLiteral(IntegerLiteral),
StringLiteral(StringLiteral), StringLiteral(StringLiteral),
ArrayLiteral(ArrayLiteral), ArrayLiteral(ArrayLiteral),
HashLiteral(HashLiteral),
IndexExpression(IndexExpression), IndexExpression(IndexExpression),
PrefixExpression(PrefixExpression), PrefixExpression(PrefixExpression),
InfixExpression(InfixExpression), InfixExpression(InfixExpression),
@ -261,6 +263,7 @@ impl Display for Expression {
Expression::IndexExpression(v) => { Expression::IndexExpression(v) => {
format!("({}[{}])", v.left, v.index) format!("({}[{}])", v.left, v.index)
} }
Expression::HashLiteral(v) => v.to_string(),
}; };
f.write_str(&value) f.write_str(&value)
@ -276,7 +279,7 @@ impl From<&Expression> for String {
// Identifier will be an expression // Identifier will be an expression
// Identifier in a let statement like, let x = 5; where `x` is an identifier doesn't produce a value // Identifier in a let statement like, let x = 5; where `x` is an identifier doesn't produce a value
// but an identifier *can* produce value when used on rhs, e.g. let x = y; Here `y` is producing a value // but an identifier *can* produce value when used on rhs, e.g. let x = y; Here `y` is producing a value
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct Identifier { pub struct Identifier {
token: TokenType, token: TokenType,
pub value: String, pub value: String,
@ -303,7 +306,7 @@ impl Display for Identifier {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct IntegerLiteral { pub struct IntegerLiteral {
pub value: i64, pub value: i64,
} }
@ -326,7 +329,7 @@ impl IntegerLiteral {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct StringLiteral { pub struct StringLiteral {
pub value: String, pub value: String,
} }
@ -344,7 +347,7 @@ impl StringLiteral {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct ArrayLiteral { pub struct ArrayLiteral {
pub elements: Vec<Expression>, pub elements: Vec<Expression>,
} }
@ -371,7 +374,7 @@ impl Display for ArrayLiteral {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct IndexExpression { pub struct IndexExpression {
pub left: Box<Expression>, pub left: Box<Expression>,
pub index: Box<Expression>, pub index: Box<Expression>,
@ -399,7 +402,62 @@ impl IndexExpression {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct HashLiteral {
pub pairs: BTreeMap<Expression, Expression>,
}
impl HashLiteral {
pub fn new(pairs: impl Into<BTreeMap<Expression, Expression>>) -> Self {
Self {
pairs: pairs.into(),
}
}
pub fn parse(parser: &mut Parser, _token: Token) -> Option<Expression> {
let mut map = BTreeMap::new();
while !parser.peek_token_is(TokenType::RBrace) {
let ctoken = parser.lexer.next()?;
let key = Expression::parse(parser, ctoken, ExpressionPriority::Lowest)?;
parser.expect_peek(TokenType::Colon)?;
let ctoken = parser.lexer.next()?;
let value = Expression::parse(parser, ctoken, ExpressionPriority::Lowest)?;
map.insert(key, value);
if !parser.peek_token_is(TokenType::RBrace)
&& parser.expect_peek(TokenType::Comma).is_none()
{
return None;
}
}
parser.expect_peek(TokenType::RBrace)?;
Some(Expression::HashLiteral(HashLiteral { pairs: map }))
}
}
impl Display for HashLiteral {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_char('{')?;
f.write_str(
&self
.pairs
.iter()
.map(|(k, v)| format!("{}:{}", k, v))
.join(", "),
)?;
f.write_char('}')
}
}
#[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct PrefixExpression { pub struct PrefixExpression {
pub operator: TokenType, pub operator: TokenType,
pub right: Box<Expression>, pub right: Box<Expression>,
@ -429,7 +487,7 @@ impl Display for PrefixExpression {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct InfixExpression { pub struct InfixExpression {
pub left: Box<Expression>, pub left: Box<Expression>,
pub operator: TokenType, pub operator: TokenType,
@ -463,7 +521,7 @@ impl Display for InfixExpression {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct BooleanExpression { pub struct BooleanExpression {
token: TokenType, token: TokenType,
pub value: bool, pub value: bool,
@ -489,7 +547,7 @@ impl Display for BooleanExpression {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct IfExpression { pub struct IfExpression {
pub condition: Box<Expression>, pub condition: Box<Expression>,
pub consequence: BlockStatement, pub consequence: BlockStatement,
@ -537,7 +595,7 @@ impl Display for IfExpression {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct BlockStatement { pub struct BlockStatement {
pub statements: Vec<Statement>, pub statements: Vec<Statement>,
} }
@ -577,7 +635,7 @@ impl Display for BlockStatement {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct FunctionLiteral { pub struct FunctionLiteral {
token: Token, token: Token,
pub parameters: Vec<Identifier>, pub parameters: Vec<Identifier>,
@ -643,7 +701,7 @@ impl Display for FunctionLiteral {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Clone)]
pub struct CallExpression { pub struct CallExpression {
pub function: Box<Expression>, pub function: Box<Expression>,
pub arguments: Vec<Expression>, pub arguments: Vec<Expression>,

View File

@ -60,6 +60,7 @@ impl<'a> Parser<'a> {
parser.register_prefix(TokenType::If, IfExpression::parse); parser.register_prefix(TokenType::If, IfExpression::parse);
parser.register_prefix(TokenType::Function, FunctionLiteral::parse); parser.register_prefix(TokenType::Function, FunctionLiteral::parse);
parser.register_prefix(TokenType::LBracket, ArrayLiteral::parse); parser.register_prefix(TokenType::LBracket, ArrayLiteral::parse);
parser.register_prefix(TokenType::LBrace, HashLiteral::parse);
// Neat trick! // Neat trick!
// Call expressions looks like <ident>(<args>). // Call expressions looks like <ident>(<args>).
@ -980,4 +981,71 @@ mod tests {
check_test_cases(&test_cases); check_test_cases(&test_cases);
} }
#[test]
fn hash_literal() {
let test_cases = [
(
"{\"one\": 1, \"two\": 2, \"three\": 3}",
vec![Statement::ExpressionStatement(ExpressionStatement::new(
token!(TokenType::LBrace),
Expression::HashLiteral(HashLiteral::new([
(
Expression::StringLiteral(StringLiteral::new("one")),
Expression::IntegerLiteral(IntegerLiteral::new(1)),
),
(
Expression::StringLiteral(StringLiteral::new("two")),
Expression::IntegerLiteral(IntegerLiteral::new(2)),
),
(
Expression::StringLiteral(StringLiteral::new("three")),
Expression::IntegerLiteral(IntegerLiteral::new(3)),
),
])),
))],
),
(
"{}",
vec![Statement::ExpressionStatement(ExpressionStatement::new(
token!(TokenType::LBrace),
Expression::HashLiteral(HashLiteral::new([])),
))],
),
(
"{\"one\": 0 + 1, \"two\": 10-8, \"three\": 15/5}",
vec![Statement::ExpressionStatement(ExpressionStatement::new(
token!(TokenType::LBrace),
Expression::HashLiteral(HashLiteral::new([
(
Expression::StringLiteral(StringLiteral::new("one")),
Expression::InfixExpression(InfixExpression::new(
Expression::IntegerLiteral(IntegerLiteral::new(0)),
TokenType::Plus,
Expression::IntegerLiteral(IntegerLiteral::new(1)),
)),
),
(
Expression::StringLiteral(StringLiteral::new("two")),
Expression::InfixExpression(InfixExpression::new(
Expression::IntegerLiteral(IntegerLiteral::new(10)),
TokenType::Minus,
Expression::IntegerLiteral(IntegerLiteral::new(8)),
)),
),
(
Expression::StringLiteral(StringLiteral::new("three")),
Expression::InfixExpression(InfixExpression::new(
Expression::IntegerLiteral(IntegerLiteral::new(15)),
TokenType::Slash,
Expression::IntegerLiteral(IntegerLiteral::new(5)),
)),
),
])),
))],
),
];
check_test_cases(&test_cases);
}
} }