WIP: Hash evaluator
This commit is contained in:
parent
37306989fe
commit
f51ead1641
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
14
src/lexer.rs
14
src/lexer.rs
|
@ -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),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user