Skip to content

Commit

Permalink
Merge pull request #6 from KPMGE/implement-strings
Browse files Browse the repository at this point in the history
Implement strings
  • Loading branch information
KPMGE authored Mar 23, 2024
2 parents 63b8454 + 149af0c commit 1130542
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub enum Expression {
Int(i32),
Identifier(String),
Boolean(bool),
String(String),
Prefix {
operator: Token, // Token::Bang, Token::Minus
right: Box<Expression>,
Expand Down
3 changes: 3 additions & 0 deletions src/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct Evaluator {
pub enum Object {
Integer(i32),
Boolean(bool),
String(String),
Return(Box<Object>),
Function {
parameters: Vec<Token>, // Token::Identifier
Expand Down Expand Up @@ -39,6 +40,7 @@ impl Evaluator {
match expression {
Expression::Int(value) => Object::Integer(value),
Expression::Boolean(value) => Object::Boolean(value),
Expression::String(value) => Object::String(value),
Expression::Prefix { operator, right } => {
let right = self.eval(AstNode::Expression(*right));
self.eval_prefix_expression(operator, right)
Expand Down Expand Up @@ -234,6 +236,7 @@ impl Object {
match self {
Object::Integer(value) => format!("{value}"),
Object::Boolean(value) => format!("{value}"),
Object::String(value) => value.to_string(),
Object::Return(value) => value.inspect(),
Object::Function { .. } => "function".to_string(),
Object::Null => "null".to_string(),
Expand Down
27 changes: 27 additions & 0 deletions src/lexer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

use std::collections::HashMap;

use crate::token::Token;
Expand Down Expand Up @@ -52,6 +53,7 @@ impl Lexer {
',' => Token::Comma,
'/' => Token::Slash,
';' => Token::Semicolon,
'"' => Token::String(self.read_string()),
'=' => match self.peek_char(self.read_position) {
Some('=') => {
self.read_char();
Expand Down Expand Up @@ -88,6 +90,31 @@ impl Lexer {
token
}

fn read_string(&mut self) -> String {
if self.current_char.unwrap() != '"' {
panic!(
"Unexpected start of string, expected: '\"', got: {:?}",
self.current_char
);
}

let mut str = String::new();

self.read_char();

while let Some(c) = self.current_char {
if c == '"' {
break;
}
str.push(c);
self.read_char();
}

self.read_char();

str
}

fn read_number(&mut self) -> String {
let start_pos = self.current_position;

Expand Down
8 changes: 8 additions & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ impl Parser {
false
}

fn parse_string(&mut self) -> Option<Expression> {
if let Token::String(s) = &self.current_token {
return Some(Expression::String(s.clone()));
}
None
}

fn parse_identifier(&mut self) -> Option<Expression> {
if let Token::Identifier(name) = &self.current_token {
return Some(Expression::Identifier(name.clone()));
Expand Down Expand Up @@ -399,6 +406,7 @@ impl Token {

fn prefix_parse_fn(&self) -> Option<fn(&mut Parser) -> Option<Expression>> {
match self {
Token::String(_) => Some(Parser::parse_string),
Token::Identifier(_) => Some(Parser::parse_identifier),
Token::Int(_) => Some(Parser::parse_int),
Token::Bang => Some(Parser::parse_prefix_expression),
Expand Down
5 changes: 3 additions & 2 deletions src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ pub enum Token {
Slash,
GreaterThan,
LessThan,
Int(String),
Identifier(String),
LeftParentesis,
Let,
True,
Expand All @@ -27,4 +25,7 @@ pub enum Token {
RightParentesis,
LeftBrace,
RightBrace,
Int(String),
Identifier(String),
String(String),
}
19 changes: 19 additions & 0 deletions tests/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,22 @@ fn given_return_statements_it_should_evaluate_correctly() {
assert_eq!(evaluated_obj, *expected_objects.get(idx).unwrap());
})
}

#[test]
fn given_a_string_expression_it_should_evaluate_correctly() {
let code = "\"kevin\"";
let expected_obj = Object::String("kevin".to_string());

let lexer = Lexer::new(code.to_string());
let mut parser = Parser::new(lexer);
let parsed_program = parser.parse_program();
let node = match parsed_program {
AstNode::Program { statements } => statements.first().unwrap().clone(),
_ => panic!("Unexpected AstNode!"),
};

let mut evaluator = Evaluator::new();
let evaluated_obj = evaluator.eval(node);

assert_eq!(evaluated_obj, expected_obj);
}
12 changes: 12 additions & 0 deletions tests/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,15 @@ fn given_code_with_integers_it_should_parse_correctly() {

assert!(token == expected_token);
}

#[test]
fn given_code_with_a_strinig_it_should_parse_correctly() {
let code = "\"kevin\"";

let mut lexer = Lexer::new(code.to_string());

let token = lexer.next_token();
let expected_token = Token::String("kevin".to_string());

assert!(token == expected_token);
}
26 changes: 26 additions & 0 deletions tests/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,32 @@ fn given_a_call_expression_it_should_parse_correctly() {
}
}

#[test]
fn given_a_string_expression_it_should_parse_correctly() {
let code = "\"kevin\"";
let expected_expression = Expression::String("kevin".to_string());

let lexer = Lexer::new(code.to_string());
let mut parser = Parser::new(lexer);
let parsed_program = parser.parse_program();

assert_eq!(parser.errors.len(), 0);

match parsed_program {
AstNode::Program { statements } => {
assert_eq!(statements.len(), 1);

let statement = statements.first().unwrap();

match statement {
AstNode::Expression(expression) => assert_eq!(*expression, expected_expression),
_ => panic!("Unexpected expression!"),
}
}
_ => panic!("Unexpected AstNode!"),
}
}

fn assert_boolean_expression(code: &str, expected_expression: &Expression) {
let lexer = Lexer::new(code.to_string());
let mut parser = Parser::new(lexer);
Expand Down

0 comments on commit 1130542

Please sign in to comment.