Implement "let" and "let mut" lexing/parsing

This commit is contained in:
Jeff 2024-08-16 07:09:46 -04:00
parent 84429ef187
commit fedefdb29f
4 changed files with 161 additions and 31 deletions

View File

@ -10,7 +10,7 @@ use super::{Expression, Node};
pub enum Statement { pub enum Statement {
Expression(Expression), Expression(Expression),
ExpressionNullified(Node<Expression>), ExpressionNullified(Node<Expression>),
Let(Node<Let>), Let(Node<LetStatement>),
StructDefinition(Node<StructDefinition>), StructDefinition(Node<StructDefinition>),
} }
@ -52,14 +52,51 @@ impl Display for Statement {
} }
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Let { pub enum LetStatement {
pub identifier: Node<Identifier>, Let {
pub value: Node<Expression>, identifier: Node<Identifier>,
value: Expression,
},
LetMut {
identifier: Node<Identifier>,
value: Expression,
},
LetType {
identifier: Node<Identifier>,
r#type: Node<Type>,
value: Expression,
},
LetMutType {
identifier: Node<Identifier>,
r#type: Node<Type>,
value: Expression,
},
} }
impl Display for Let { impl Display for LetStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "let {} = {}", self.identifier, self.value) match self {
LetStatement::Let { identifier, value } => {
write!(f, "let {identifier} = {value}")
}
LetStatement::LetMut { identifier, value } => {
write!(f, "let mut {identifier} = {value}")
}
LetStatement::LetType {
identifier,
r#type,
value,
} => {
write!(f, "let {identifier}: {type} = {value}")
}
LetStatement::LetMutType {
identifier,
r#type,
value,
} => {
write!(f, "let mut {identifier}: {type} = {value}")
}
}
} }
} }

View File

@ -429,6 +429,7 @@ impl Lexer {
"is_even" => Token::IsEven, "is_even" => Token::IsEven,
"is_odd" => Token::IsOdd, "is_odd" => Token::IsOdd,
"length" => Token::Length, "length" => Token::Length,
"let" => Token::Let,
"mut" => Token::Mut, "mut" => Token::Mut,
"read_line" => Token::ReadLine, "read_line" => Token::ReadLine,
"struct" => Token::Struct, "struct" => Token::Struct,
@ -507,6 +508,22 @@ impl Display for LexError {
mod tests { mod tests {
use super::*; use super::*;
#[test]
fn let_statement() {
let input = "let x = 42";
assert_eq!(
lex(input),
Ok(vec![
(Token::Let, (0, 3)),
(Token::Identifier("x"), (4, 5)),
(Token::Equal, (6, 7)),
(Token::Integer("42"), (8, 10)),
(Token::Eof, (10, 10)),
])
);
}
#[test] #[test]
fn unit_struct() { fn unit_struct() {
let input = "struct Foo"; let input = "struct Foo";

View File

@ -145,6 +145,41 @@ impl<'src> Parser<'src> {
pub fn parse_statement(&mut self) -> Result<Statement, ParseError> { pub fn parse_statement(&mut self) -> Result<Statement, ParseError> {
let start_position = self.current_position; let start_position = self.current_position;
if let Token::Let = self.current_token {
self.next_token()?;
let is_mutable = if let Token::Mut = self.current_token {
self.next_token()?;
true
} else {
false
};
let identifier = self.parse_identifier()?;
if let Token::Equal = self.current_token {
self.next_token()?;
} else {
return Err(ParseError::ExpectedToken {
expected: TokenKind::Equal,
actual: self.current_token.to_owned(),
position: self.current_position,
});
}
let value = self.parse_expression(0)?;
let r#let = if is_mutable {
LetStatement::LetMut { identifier, value }
} else {
LetStatement::Let { identifier, value }
};
let position = (start_position.0, self.current_position.1);
return Ok(Statement::Let(Node::new(r#let, position)));
}
if let Token::Struct = self.current_token { if let Token::Struct = self.current_token {
self.next_token()?; self.next_token()?;
@ -1026,10 +1061,41 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn mutable_variable() { fn let_statement() {
let source = "mut x = false"; let source = "let x = 42";
assert_eq!(parse(source), todo!()); assert_eq!(
parse(source),
Ok(AbstractSyntaxTree {
statements: [Statement::Let(Node::new(
LetStatement::Let {
identifier: Node::new(Identifier::new("x"), (4, 5)),
value: Expression::literal(LiteralExpression::Integer(42), (8, 10)),
},
(0, 10),
))]
.into()
})
);
}
#[test]
fn let_mut_statement() {
let source = "let mut x = false";
assert_eq!(
parse(source),
Ok(AbstractSyntaxTree {
statements: [Statement::Let(Node::new(
LetStatement::LetMut {
identifier: Node::new(Identifier::new("x"), (8, 9)),
value: Expression::literal(LiteralExpression::Boolean(false), (12, 17)),
},
(0, 17),
))]
.into()
})
);
} }
#[test] #[test]

View File

@ -26,6 +26,7 @@ pub enum Token<'src> {
IsEven, IsEven,
IsOdd, IsOdd,
Length, Length,
Let,
Mut, Mut,
ReadLine, ReadLine,
Str, Str,
@ -97,6 +98,7 @@ impl<'src> Token<'src> {
Token::LeftParenthesis => TokenOwned::LeftParenthesis, Token::LeftParenthesis => TokenOwned::LeftParenthesis,
Token::LeftSquareBrace => TokenOwned::LeftSquareBrace, Token::LeftSquareBrace => TokenOwned::LeftSquareBrace,
Token::Length => TokenOwned::Length, Token::Length => TokenOwned::Length,
Token::Let => TokenOwned::Let,
Token::Less => TokenOwned::Less, Token::Less => TokenOwned::Less,
Token::LessEqual => TokenOwned::LessOrEqual, Token::LessEqual => TokenOwned::LessOrEqual,
Token::Minus => TokenOwned::Minus, Token::Minus => TokenOwned::Minus,
@ -153,6 +155,7 @@ impl<'src> Token<'src> {
Token::LeftCurlyBrace => "{", Token::LeftCurlyBrace => "{",
Token::LeftParenthesis => "(", Token::LeftParenthesis => "(",
Token::LeftSquareBrace => "[", Token::LeftSquareBrace => "[",
Token::Let => "let",
Token::Length => "length", Token::Length => "length",
Token::Less => "<", Token::Less => "<",
Token::LessEqual => "<=", Token::LessEqual => "<=",
@ -207,6 +210,7 @@ impl<'src> Token<'src> {
Token::LeftCurlyBrace => TokenKind::LeftCurlyBrace, Token::LeftCurlyBrace => TokenKind::LeftCurlyBrace,
Token::LeftParenthesis => TokenKind::LeftParenthesis, Token::LeftParenthesis => TokenKind::LeftParenthesis,
Token::LeftSquareBrace => TokenKind::LeftSquareBrace, Token::LeftSquareBrace => TokenKind::LeftSquareBrace,
Token::Let => TokenKind::Let,
Token::Length => TokenKind::Length, Token::Length => TokenKind::Length,
Token::Less => TokenKind::Less, Token::Less => TokenKind::Less,
Token::LessEqual => TokenKind::LessOrEqual, Token::LessEqual => TokenKind::LessOrEqual,
@ -314,6 +318,7 @@ pub enum TokenOwned {
Int, Int,
IsEven, IsEven,
IsOdd, IsOdd,
Let,
Length, Length,
Mut, Mut,
ReadLine, ReadLine,
@ -387,6 +392,7 @@ impl Display for TokenOwned {
TokenOwned::LeftParenthesis => Token::LeftParenthesis.fmt(f), TokenOwned::LeftParenthesis => Token::LeftParenthesis.fmt(f),
TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f), TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
TokenOwned::Length => Token::Length.fmt(f), TokenOwned::Length => Token::Length.fmt(f),
TokenOwned::Let => Token::Let.fmt(f),
TokenOwned::Less => Token::Less.fmt(f), TokenOwned::Less => Token::Less.fmt(f),
TokenOwned::LessOrEqual => Token::LessEqual.fmt(f), TokenOwned::LessOrEqual => Token::LessEqual.fmt(f),
TokenOwned::Minus => Token::Minus.fmt(f), TokenOwned::Minus => Token::Minus.fmt(f),
@ -435,6 +441,7 @@ pub enum TokenKind {
IsEven, IsEven,
IsOdd, IsOdd,
Length, Length,
Let,
ReadLine, ReadLine,
Str, Str,
ToString, ToString,
@ -506,6 +513,7 @@ impl Display for TokenKind {
TokenKind::LeftParenthesis => Token::LeftParenthesis.fmt(f), TokenKind::LeftParenthesis => Token::LeftParenthesis.fmt(f),
TokenKind::LeftSquareBrace => Token::LeftSquareBrace.fmt(f), TokenKind::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
TokenKind::Length => Token::Length.fmt(f), TokenKind::Length => Token::Length.fmt(f),
TokenKind::Let => Token::Let.fmt(f),
TokenKind::Less => Token::Less.fmt(f), TokenKind::Less => Token::Less.fmt(f),
TokenKind::LessOrEqual => Token::LessEqual.fmt(f), TokenKind::LessOrEqual => Token::LessEqual.fmt(f),
TokenKind::Minus => Token::Minus.fmt(f), TokenKind::Minus => Token::Minus.fmt(f),
@ -535,13 +543,31 @@ impl Display for TokenKind {
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
pub fn all_tokens<'src>() -> [Token<'src>; 49] { pub fn all_tokens<'src>() -> [Token<'src>; 51] {
[ [
Token::Async, Token::Eof,
Token::Bang, Token::Identifier("identifier"),
Token::BangEqual,
Token::Bool,
Token::Boolean("true"), Token::Boolean("true"),
Token::Float("1.0"),
Token::Integer("1"),
Token::String("string"),
Token::Async,
Token::Bool,
Token::Else,
Token::FloatKeyword,
Token::If,
Token::Int,
Token::IsEven,
Token::IsOdd,
Token::Length,
Token::Let,
Token::ReadLine,
Token::Str,
Token::ToString,
Token::While,
Token::WriteLine,
Token::BangEqual,
Token::Bang,
Token::Colon, Token::Colon,
Token::Comma, Token::Comma,
Token::Dot, Token::Dot,
@ -549,23 +575,12 @@ pub(crate) mod tests {
Token::DoubleDot, Token::DoubleDot,
Token::DoubleEqual, Token::DoubleEqual,
Token::DoublePipe, Token::DoublePipe,
Token::Else,
Token::Eof,
Token::Equal, Token::Equal,
Token::Float("0.0"),
Token::FloatKeyword,
Token::Greater, Token::Greater,
Token::GreaterEqual, Token::GreaterEqual,
Token::Identifier(""),
Token::If,
Token::Int,
Token::Integer("0"),
Token::IsEven,
Token::IsOdd,
Token::LeftCurlyBrace, Token::LeftCurlyBrace,
Token::LeftParenthesis, Token::LeftParenthesis,
Token::LeftSquareBrace, Token::LeftSquareBrace,
Token::Length,
Token::Less, Token::Less,
Token::LessEqual, Token::LessEqual,
Token::Minus, Token::Minus,
@ -574,18 +589,13 @@ pub(crate) mod tests {
Token::Percent, Token::Percent,
Token::Plus, Token::Plus,
Token::PlusEqual, Token::PlusEqual,
Token::ReadLine,
Token::RightCurlyBrace, Token::RightCurlyBrace,
Token::RightParenthesis, Token::RightParenthesis,
Token::RightSquareBrace, Token::RightSquareBrace,
Token::Semicolon, Token::Semicolon,
Token::Star, Token::Star,
Token::Str,
Token::String(""),
Token::Struct, Token::Struct,
Token::ToString, Token::Slash,
Token::While,
Token::WriteLine,
] ]
} }