Skip to content

Commit

Permalink
feat: string literals
Browse files Browse the repository at this point in the history
  • Loading branch information
ZakFarmer committed Oct 22, 2023
1 parent 57f178f commit f3b6f7d
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 13 deletions.
20 changes: 20 additions & 0 deletions src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ impl<'a> Lexer<'a> {
(TokenType::Assign, "=".to_string())
}
}
Some('"') => {
self.read_char();

let literal = self.read_string();

(TokenType::String, literal)
}
Some(';') => (TokenType::Semicolon, ";".to_string()),
Some('(') => (TokenType::LParen, "(".to_string()),
Some(')') => (TokenType::RParen, ")".to_string()),
Expand Down Expand Up @@ -147,6 +154,19 @@ impl<'a> Lexer<'a> {
self.input[position..self.position].to_owned()
}

fn read_string(&mut self) -> String {
let position = self.position;

while match self.ch {
Some(ch) => ch != '"',
_ => false,
} {
self.read_char();
}

self.input[position..self.position].to_owned()
}

fn skip_whitespace(&mut self) -> () {
while match self.ch {
Some(ch) => ch.is_whitespace(),
Expand Down
49 changes: 43 additions & 6 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
ast::{
Assignment, BlockStatement, Boolean, CallExpression, Expression, FunctionLiteral,
Identifier, IfExpression, InfixExpression, Integer, Literal, PrefixExpression,
Program, ReturnStatement, Statement,
Program, ReturnStatement, Statement, StringLiteral,
},
lexer::Lexer,
token::{Token, TokenType},
Expand Down Expand Up @@ -116,10 +116,15 @@ impl<'a> Parser<'a> {
parser.register_prefix(TokenType::If, |p| Parser::parse_if_expression(p));
parser.register_prefix(TokenType::Bang, |p| Parser::parse_prefix_expression(p));
parser.register_prefix(TokenType::Minus, |p| Parser::parse_prefix_expression(p));

parser.register_prefix(TokenType::Dollar, |p| {
Parser::parse_variable_reference_expression(p)
});

parser.register_prefix(TokenType::String, |p| {
Parser::parse_string_literal(p)
});

parser.register_infix(TokenType::LParen, |p, left| {
Parser::parse_call_expression(p, left)
});
Expand Down Expand Up @@ -214,6 +219,17 @@ impl<'a> Parser<'a> {
})))
}

fn parse_string_literal(&mut self) -> Result<Expression> {
let current_token = self.current_token.clone().unwrap();

let value = self.current_token.as_ref().unwrap().to_string();

Ok(Expression::Literal(Literal::String(StringLiteral {
token: current_token,
value,
})))
}

pub fn parse_program(&mut self) -> Result<Program> {
let mut program = Program::default();

Expand Down Expand Up @@ -992,7 +1008,7 @@ mod tests {
}

#[test]
fn test_parsing_prefix_expressions() -> Result<(), Error> {
fn test_prefix_expressions() -> Result<(), Error> {
let prefix_tests: [(&str, &str, i64); 2] = [("!5;", "!", 5), ("-15;", "-", 15)];

for (input, _operator, value) in prefix_tests.iter() {
Expand All @@ -1014,13 +1030,34 @@ mod tests {
Ok(())
}

fn assert_boolean_literal(expression: Expression, value: bool) -> Result<(), Error> {
#[test]
fn test_strings() -> Result<(), Error> {
let input = r#""hello world";"#;

let lexer = Lexer::new(input);
let mut parser = Parser::new(lexer);

let program = parser.parse_program()?;
parser.check_errors()?;

assert_eq!(1, program.statements.len());

if let Statement::Expr(expression) = &program.statements[0] {
assert_string_literal(&expression, "hello world")?;
} else {
assert!(false, "Expected ExpressionStatement");
}

Ok(())
}

fn assert_string_literal(expression: &Expression, value: &str) -> Result<(), Error> {
match expression {
Expression::Literal(Literal::Boolean(boolean)) => {
assert_eq!(value, boolean.value);
Expression::Literal(Literal::String(string_literal)) => {
assert_eq!(value, string_literal.value);
}
_ => {
assert!(false, "Expected BooleanLiteral");
assert!(false, "Expected StringLiteral");
}
}

Expand Down
23 changes: 16 additions & 7 deletions src/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,25 @@ pub fn init_repl() -> Result<(), Error> {
Ok(line) => {
rl.add_history_entry(line.as_str())?;

let lexer = Lexer::new(&line);
let mut parser = Parser::new(lexer);
let result: Result<(), Error> = (|| {
let lexer = Lexer::new(&line);
let mut parser = Parser::new(lexer);

let program = parser.parse_program()?;
parser.check_errors()?;
let program = parser.parse_program()?;

let evaluated = evaluator::eval_statements(&program.statements, &env)?;
dbg!(&program);

println!("{}", evaluated);
parser.check_errors()?;

let evaluated = evaluator::eval_statements(&program.statements, &env)?;

println!("{}", evaluated);
Ok(())
})();

if let Err(e) = result {
println!("Error: {}", e);
}
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
Expand All @@ -51,7 +61,6 @@ pub fn init_repl() -> Result<(), Error> {
}
Err(err) => {
println!("Error: {:?}", err);
break;
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub enum TokenType {
Gt,
Bang,

String,

If,
Else,
Return,
Expand Down Expand Up @@ -89,6 +91,7 @@ impl std::fmt::Display for TokenType {
TokenType::If => "If",
TokenType::Else => "Else",
TokenType::Return => "Return",
TokenType::String => "String",
};

write!(f, "{}", token_type)
Expand Down

0 comments on commit f3b6f7d

Please sign in to comment.