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 {
Expression(Expression),
ExpressionNullified(Node<Expression>),
Let(Node<Let>),
Let(Node<LetStatement>),
StructDefinition(Node<StructDefinition>),
}
@ -52,14 +52,51 @@ impl Display for Statement {
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Let {
pub identifier: Node<Identifier>,
pub value: Node<Expression>,
pub enum LetStatement {
Let {
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 {
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_odd" => Token::IsOdd,
"length" => Token::Length,
"let" => Token::Let,
"mut" => Token::Mut,
"read_line" => Token::ReadLine,
"struct" => Token::Struct,
@ -507,6 +508,22 @@ impl Display for LexError {
mod tests {
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]
fn unit_struct() {
let input = "struct Foo";

View File

@ -145,6 +145,41 @@ impl<'src> Parser<'src> {
pub fn parse_statement(&mut self) -> Result<Statement, ParseError> {
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 {
self.next_token()?;
@ -1026,10 +1061,41 @@ mod tests {
use super::*;
#[test]
fn mutable_variable() {
let source = "mut x = false";
fn let_statement() {
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]

View File

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