added strings! applied clippy recs

This commit is contained in:
Ishan Jain 2024-05-26 12:09:49 +05:30
parent ffe56c86ae
commit 6607d1db02
Signed by: ishan
GPG Key ID: 0506DB2A1CC75C27
6 changed files with 110 additions and 42 deletions

View File

@ -54,6 +54,7 @@ impl Environment {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Object { pub enum Object {
Integer(i64), Integer(i64),
String(String),
Boolean(bool), Boolean(bool),
ReturnValue(Box<Object>), ReturnValue(Box<Object>),
Error(String), Error(String),
@ -69,6 +70,7 @@ impl Object {
pub fn inspect(&self) -> String { pub fn inspect(&self) -> String {
match self { match self {
Object::Integer(v) => v.to_string(), Object::Integer(v) => v.to_string(),
Object::String(s) => s.to_string(),
Object::Boolean(v) => v.to_string(), Object::Boolean(v) => v.to_string(),
Object::ReturnValue(ret) => ret.inspect(), Object::ReturnValue(ret) => ret.inspect(),
Object::Error(s) => s.to_string(), Object::Error(s) => s.to_string(),
@ -79,7 +81,7 @@ impl Object {
out.write_fmt(format_args!( out.write_fmt(format_args!(
"fn({}) {{ {} }}", "fn({}) {{ {} }}",
s.parameters.iter().map(|x| x.to_string()).join(", "), s.parameters.iter().map(|x| x.to_string()).join(", "),
s.body.to_string() s.body
)) ))
.unwrap(); .unwrap();
@ -93,6 +95,7 @@ impl Display for Object {
fn fmt(&self, f: &mut Formatter) -> FmtResult { fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_str(match self { f.write_str(match self {
Object::Integer(_) => "INTEGER", Object::Integer(_) => "INTEGER",
Object::String(_) => "STRING",
Object::Boolean(_) => "BOOLEAN", Object::Boolean(_) => "BOOLEAN",
Object::ReturnValue(_) => "RETURN_VALUE", Object::ReturnValue(_) => "RETURN_VALUE",
Object::Error(_) => "ERROR", Object::Error(_) => "ERROR",
@ -195,6 +198,23 @@ mod tests {
run_test_cases(&test_cases); run_test_cases(&test_cases);
} }
#[test]
fn eval_string_literal_expression() {
let test_cases = [("\"Hello \"", Some(Object::String("Hello ".to_owned())))];
run_test_cases(&test_cases);
}
#[test]
fn eval_string_concatenation() {
let test_cases = [(
"\"Hello \" + \"World\"",
Some(Object::String("Hello World".to_owned())),
)];
run_test_cases(&test_cases);
}
#[test] #[test]
fn eval_if_else_expression() { fn eval_if_else_expression() {
let test_cases = [ let test_cases = [
@ -280,6 +300,10 @@ mod tests {
"foobar", "foobar",
Some(Object::Error("identifier not found: foobar".into())), Some(Object::Error("identifier not found: foobar".into())),
), ),
(
"\"Hello\" - \"World\"",
Some(Object::Error("unknown operator: STRING - STRING".into())),
),
]; ];
run_test_cases(&test_cases); run_test_cases(&test_cases);
@ -304,7 +328,7 @@ mod tests {
fn test_function_object() { fn test_function_object() {
let test_case = "fn(x) { x + 2;};"; let test_case = "fn(x) { x + 2;};";
let lexer = Lexer::new(&test_case); let lexer = Lexer::new(test_case);
let mut parser = Parser::new(lexer); let mut parser = Parser::new(lexer);
let program = parser.parse_program(); let program = parser.parse_program();
assert!(program.is_some()); assert!(program.is_some());
@ -373,7 +397,7 @@ mod tests {
// even though it is never used. // even though it is never used.
let test_cases = [( let test_cases = [(
"let counter = fn(x) { "let counter = fn(x) {
if (x > 100 ) { if (x > 30 ) {
return true; return true;
} else { } else {
let foobar = 9999; let foobar = 9999;

View File

@ -46,6 +46,7 @@ impl Evaluator for TreeWalker {
Node::Expression(expr) => match expr { Node::Expression(expr) => match expr {
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::BooleanExpression(b) => Some(Object::Boolean(b.value)), Expression::BooleanExpression(b) => Some(Object::Boolean(b.value)),
Expression::PrefixExpression(p) => { Expression::PrefixExpression(p) => {
let expr = self.eval(Node::Expression(*p.right), env)?; let expr = self.eval(Node::Expression(*p.right), env)?;
@ -56,13 +57,11 @@ impl Evaluator for TreeWalker {
let right = self.eval(Node::Expression(*ie.right), env)?; let right = self.eval(Node::Expression(*ie.right), env)?;
self.eval_infix_expression(left, ie.operator, right) self.eval_infix_expression(left, ie.operator, right)
} }
Expression::FunctionExpression(fnl) => { Expression::FunctionExpression(fnl) => Some(Object::Function(Function {
return Some(Object::Function(Function { body: fnl.body,
body: fnl.body, parameters: fnl.parameters,
parameters: fnl.parameters, env,
env: 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())?;
// Resolve function arguments and update the environment // Resolve function arguments and update the environment
@ -179,6 +178,9 @@ impl TreeWalker {
TokenType::LessThan => Object::Boolean(l < r), TokenType::LessThan => Object::Boolean(l < r),
_ => Object::Error(format!("unknown operator: {} {} {}", l, operator, r)), _ => Object::Error(format!("unknown operator: {} {} {}", l, operator, r)),
}, },
(Object::String(l), Object::String(r)) if operator == TokenType::Plus => {
Object::String(l.to_owned() + &r)
}
(o1, o2) if o1.to_string() != o2.to_string() => { (o1, o2) if o1.to_string() != o2.to_string() => {
Object::Error(format!("type mismatch: {} {} {}", o1, operator, o2)) Object::Error(format!("type mismatch: {} {} {}", o1, operator, o2))
} }

View File

@ -31,6 +31,7 @@ pub enum TokenType {
// by other variants of this enum. // by other variants of this enum.
Ident, Ident,
Int, Int,
String,
// Operators // Operators
Assign, Assign,
Plus, Plus,
@ -91,6 +92,7 @@ impl Display for TokenType {
TokenType::Illegal => "illegal", TokenType::Illegal => "illegal",
TokenType::Ident => "ident", TokenType::Ident => "ident",
TokenType::Int => "int", TokenType::Int => "int",
TokenType::String => "string",
}) })
} }
} }
@ -199,6 +201,19 @@ impl<'a> Lexer<'a> {
} }
number.into_iter().collect() number.into_iter().collect()
} }
fn read_string(&mut self) -> String {
let mut out = String::new();
while let Some(c) = self.read_char() {
if c == '"' {
break;
}
out.push(c);
}
out
}
} }
impl<'a> Iterator for Lexer<'a> { impl<'a> Iterator for Lexer<'a> {
@ -210,10 +225,7 @@ impl<'a> Iterator for Lexer<'a> {
match ch { match ch {
Some('=') => { Some('=') => {
let is_e = match self.input.peek() { let is_e = matches!(self.input.peek(), Some(v) if *v == '=');
Some(v) if *v == '=' => true,
_ => false,
};
if is_e { if is_e {
self.read_char(); self.read_char();
Some(token!(TokenType::Equals)) Some(token!(TokenType::Equals))
@ -232,10 +244,7 @@ impl<'a> Iterator for Lexer<'a> {
Some('{') => Some(token!(TokenType::LBrace)), Some('{') => Some(token!(TokenType::LBrace)),
Some('}') => Some(token!(TokenType::RBrace)), Some('}') => Some(token!(TokenType::RBrace)),
Some('!') => { Some('!') => {
let is_ne = match self.input.peek() { let is_ne = matches!(self.input.peek(), Some(v) if *v == '=');
Some(v) if *v == '=' => true,
_ => false,
};
if is_ne { if is_ne {
self.read_char(); self.read_char();
Some(token!(TokenType::NotEquals)) Some(token!(TokenType::NotEquals))
@ -245,6 +254,10 @@ impl<'a> Iterator for Lexer<'a> {
} }
Some('>') => Some(token!(TokenType::GreaterThan)), Some('>') => Some(token!(TokenType::GreaterThan)),
Some('<') => Some(token!(TokenType::LessThan)), Some('<') => Some(token!(TokenType::LessThan)),
Some('"') => {
let str = self.read_string();
Some(token!(TokenType::String, &str))
}
Some(ch) if is_letter(ch) => { Some(ch) if is_letter(ch) => {
let ident = self.read_identifier(ch); let ident = self.read_identifier(ch);
Some(lookup_ident(&ident)) Some(lookup_ident(&ident))
@ -364,6 +377,9 @@ mod tests {
10 == 10; 10 == 10;
9 != 10; 9 != 10;
\"foobar\"
\"foo bar\"
" "
) )
.collect::<Vec<Token>>(), .collect::<Vec<Token>>(),
@ -415,6 +431,8 @@ mod tests {
token!(TokenType::NotEquals), token!(TokenType::NotEquals),
token!(TokenType::Int, "10"), token!(TokenType::Int, "10"),
token!(TokenType::Semicolon), token!(TokenType::Semicolon),
token!(TokenType::String, "foobar"),
token!(TokenType::String, "foo bar"),
token!(TokenType::EOF), token!(TokenType::EOF),
], ],
); );

View File

@ -1,3 +1,4 @@
#![feature(assert_matches)]
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;

View File

@ -102,7 +102,7 @@ impl LetStatement {
impl Display for LetStatement { impl Display for LetStatement {
fn fmt(&self, f: &mut Formatter) -> FmtResult { fn fmt(&self, f: &mut Formatter) -> FmtResult {
let mut out = format!("{} {} = ", TokenType::Let.to_string(), self.name.value); let mut out = format!("{} {} = ", TokenType::Let, self.name.value);
if let Some(v) = &self.value { if let Some(v) = &self.value {
let a: String = v.into(); let a: String = v.into();
@ -188,6 +188,7 @@ pub enum ExpressionPriority {
pub enum Expression { pub enum Expression {
Identifier(Identifier), Identifier(Identifier),
IntegerLiteral(IntegerLiteral), IntegerLiteral(IntegerLiteral),
StringLiteral(StringLiteral),
PrefixExpression(PrefixExpression), PrefixExpression(PrefixExpression),
InfixExpression(InfixExpression), InfixExpression(InfixExpression),
BooleanExpression(BooleanExpression), BooleanExpression(BooleanExpression),
@ -242,6 +243,7 @@ impl Display for Expression {
let value = match self { let value = match self {
Expression::Identifier(v) => v.to_string(), Expression::Identifier(v) => v.to_string(),
Expression::IntegerLiteral(v) => v.value.to_string(), Expression::IntegerLiteral(v) => v.value.to_string(),
Expression::StringLiteral(v) => v.value.to_string(),
Expression::PrefixExpression(v) => v.to_string(), Expression::PrefixExpression(v) => v.to_string(),
Expression::InfixExpression(v) => v.to_string(), Expression::InfixExpression(v) => v.to_string(),
Expression::BooleanExpression(v) => v.to_string(), Expression::BooleanExpression(v) => v.to_string(),
@ -313,6 +315,24 @@ impl IntegerLiteral {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct StringLiteral {
pub value: String,
}
impl StringLiteral {
pub fn new(v: &str) -> Self {
Self {
value: v.to_string(),
}
}
pub fn parse(_parser: &mut Parser, token: Token) -> Option<Expression> {
Some(Expression::StringLiteral(StringLiteral::new(
&token.literal?,
)))
}
}
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct PrefixExpression { pub struct PrefixExpression {
pub operator: TokenType, pub operator: TokenType,
@ -339,11 +359,7 @@ impl PrefixExpression {
impl Display for PrefixExpression { impl Display for PrefixExpression {
fn fmt(&self, f: &mut Formatter) -> FmtResult { fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_fmt(format_args!( f.write_fmt(format_args!("({}{})", self.operator, self.right))
"({}{})",
self.operator,
self.right.to_string()
))
} }
} }
@ -376,9 +392,7 @@ impl Display for InfixExpression {
fn fmt(&self, f: &mut Formatter) -> FmtResult { fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_fmt(format_args!( f.write_fmt(format_args!(
"({} {} {})", "({} {} {})",
self.left.to_string(), self.left, self.operator, self.right,
self.operator,
self.right.to_string(),
)) ))
} }
} }
@ -449,13 +463,9 @@ impl IfExpression {
impl Display for IfExpression { impl Display for IfExpression {
fn fmt(&self, f: &mut Formatter) -> FmtResult { fn fmt(&self, f: &mut Formatter) -> FmtResult {
let mut out = format!( let mut out = format!("if {} {{ {} }}", self.condition, self.consequence);
"if {} {{ {} }}",
self.condition.to_string(),
self.consequence.to_string()
);
if let Some(alternative) = &self.alternative { if let Some(alternative) = &self.alternative {
out += &format!(" else {{ {} }}", alternative.to_string()); out += &format!(" else {{ {} }}", alternative);
} }
f.write_str(&out) f.write_str(&out)
} }

View File

@ -48,6 +48,7 @@ impl<'a> Parser<'a> {
infix_parse_fns: HashMap::new(), infix_parse_fns: HashMap::new(),
}; };
parser.register_prefix(TokenType::String, StringLiteral::parse);
parser.register_prefix(TokenType::Ident, Identifier::parse); parser.register_prefix(TokenType::Ident, Identifier::parse);
parser.register_prefix(TokenType::Int, IntegerLiteral::parse); parser.register_prefix(TokenType::Int, IntegerLiteral::parse);
parser.register_prefix(TokenType::Bang, PrefixExpression::parse); parser.register_prefix(TokenType::Bang, PrefixExpression::parse);
@ -101,10 +102,7 @@ impl<'a> Parser<'a> {
if self.peek_token_is(token) { if self.peek_token_is(token) {
self.lexer.next() self.lexer.next()
} else { } else {
let got_token = match self.lexer.peek() { let got_token = self.lexer.peek().map(|v| v.name);
Some(v) => Some(v.name),
None => None,
};
self.peek_error(token, got_token); self.peek_error(token, got_token);
None None
} }
@ -113,10 +111,7 @@ impl<'a> Parser<'a> {
fn peek_error(&mut self, et: TokenType, gt: Option<TokenType>) { fn peek_error(&mut self, et: TokenType, gt: Option<TokenType>) {
let msg = match gt { let msg = match gt {
Some(v) => format!("expected next token to be {:?}, Got {:?} instead", et, v), Some(v) => format!("expected next token to be {:?}, Got {:?} instead", et, v),
None => format!( None => format!("expected next token to be {}, Got None instead", et),
"expected next token to be {}, Got None instead",
et.to_string()
),
}; };
self.errors.push(Error { reason: msg }); self.errors.push(Error { reason: msg });
} }
@ -131,7 +126,7 @@ impl<'a> Parser<'a> {
fn no_prefix_parse_fn_error(&mut self, token: TokenType) { fn no_prefix_parse_fn_error(&mut self, token: TokenType) {
self.errors.push(Error { self.errors.push(Error {
reason: format!("no prefix parse function for {} found", token.to_string()), reason: format!("no prefix parse function for {} found", token),
}); });
} }
@ -288,6 +283,24 @@ mod tests {
); );
} }
#[test]
fn string_literal_expression() {
let lexer = Lexer::new("\"hello world\";");
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some());
assert_eq!(
program.unwrap().statements,
vec![Statement::ExpressionStatement(ExpressionStatement::new(
token!(TokenType::String, "hello world"),
Expression::StringLiteral(StringLiteral::new("hello world"))
))]
);
}
#[test] #[test]
fn prefix_expressions() { fn prefix_expressions() {
let test_cases = [ let test_cases = [