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

View File

@ -46,6 +46,7 @@ impl Evaluator for TreeWalker {
Node::Expression(expr) => match expr {
Expression::Identifier(v) => self.eval_identifier(v, env),
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::PrefixExpression(p) => {
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)?;
self.eval_infix_expression(left, ie.operator, right)
}
Expression::FunctionExpression(fnl) => {
return Some(Object::Function(Function {
Expression::FunctionExpression(fnl) => Some(Object::Function(Function {
body: fnl.body,
parameters: fnl.parameters,
env: env,
}));
}
env,
})),
Expression::CallExpression(v) => {
let function = self.eval(Node::Expression(*v.function), env.clone())?;
// Resolve function arguments and update the environment
@ -179,6 +178,9 @@ impl TreeWalker {
TokenType::LessThan => Object::Boolean(l < 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() => {
Object::Error(format!("type mismatch: {} {} {}", o1, operator, o2))
}

View File

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

View File

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

View File

@ -102,7 +102,7 @@ impl LetStatement {
impl Display for LetStatement {
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 {
let a: String = v.into();
@ -188,6 +188,7 @@ pub enum ExpressionPriority {
pub enum Expression {
Identifier(Identifier),
IntegerLiteral(IntegerLiteral),
StringLiteral(StringLiteral),
PrefixExpression(PrefixExpression),
InfixExpression(InfixExpression),
BooleanExpression(BooleanExpression),
@ -242,6 +243,7 @@ impl Display for Expression {
let value = match self {
Expression::Identifier(v) => v.to_string(),
Expression::IntegerLiteral(v) => v.value.to_string(),
Expression::StringLiteral(v) => v.value.to_string(),
Expression::PrefixExpression(v) => v.to_string(),
Expression::InfixExpression(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)]
pub struct PrefixExpression {
pub operator: TokenType,
@ -339,11 +359,7 @@ impl PrefixExpression {
impl Display for PrefixExpression {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_fmt(format_args!(
"({}{})",
self.operator,
self.right.to_string()
))
f.write_fmt(format_args!("({}{})", self.operator, self.right))
}
}
@ -376,9 +392,7 @@ impl Display for InfixExpression {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_fmt(format_args!(
"({} {} {})",
self.left.to_string(),
self.operator,
self.right.to_string(),
self.left, self.operator, self.right,
))
}
}
@ -449,13 +463,9 @@ impl IfExpression {
impl Display for IfExpression {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
let mut out = format!(
"if {} {{ {} }}",
self.condition.to_string(),
self.consequence.to_string()
);
let mut out = format!("if {} {{ {} }}", self.condition, self.consequence);
if let Some(alternative) = &self.alternative {
out += &format!(" else {{ {} }}", alternative.to_string());
out += &format!(" else {{ {} }}", alternative);
}
f.write_str(&out)
}

View File

@ -48,6 +48,7 @@ impl<'a> Parser<'a> {
infix_parse_fns: HashMap::new(),
};
parser.register_prefix(TokenType::String, StringLiteral::parse);
parser.register_prefix(TokenType::Ident, Identifier::parse);
parser.register_prefix(TokenType::Int, IntegerLiteral::parse);
parser.register_prefix(TokenType::Bang, PrefixExpression::parse);
@ -101,10 +102,7 @@ impl<'a> Parser<'a> {
if self.peek_token_is(token) {
self.lexer.next()
} else {
let got_token = match self.lexer.peek() {
Some(v) => Some(v.name),
None => None,
};
let got_token = self.lexer.peek().map(|v| v.name);
self.peek_error(token, got_token);
None
}
@ -113,10 +111,7 @@ impl<'a> Parser<'a> {
fn peek_error(&mut self, et: TokenType, gt: Option<TokenType>) {
let msg = match gt {
Some(v) => format!("expected next token to be {:?}, Got {:?} instead", et, v),
None => format!(
"expected next token to be {}, Got None instead",
et.to_string()
),
None => format!("expected next token to be {}, Got None instead", et),
};
self.errors.push(Error { reason: msg });
}
@ -131,7 +126,7 @@ impl<'a> Parser<'a> {
fn no_prefix_parse_fn_error(&mut self, token: TokenType) {
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]
fn prefix_expressions() {
let test_cases = [