Ch 2 (up to function literals)

This commit is contained in:
Devon Tingley 2023-03-06 17:44:33 -05:00
parent 218c98dc8a
commit 4ad37f9b71
4 changed files with 73 additions and 6 deletions

View file

@ -92,6 +92,7 @@ pub enum PrefixOperator {
Bang,
Minus,
If,
Function,
}
impl TryFrom<&Token> for PrefixOperator {
@ -102,6 +103,7 @@ impl TryFrom<&Token> for PrefixOperator {
Token::Bang => Self::Bang,
Token::Minus => Self::Minus,
Token::If => Self::If,
Token::Function => Self::Function,
_ => return Err(LexerError::InvalidToken),
};
@ -166,7 +168,6 @@ impl TryFrom<&Token> for InfixOperator {
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub enum Keyword {
Let,
Function,
Return,
}
@ -182,7 +183,6 @@ impl TryFrom<&Token> for Keyword {
fn try_from(token: &Token) -> Result<Self, Self::Error> {
let term = match token {
Token::Let => Self::Let,
Token::Function => Self::Function,
Token::Return => Self::Return,
_ => return Err(LexerError::InvalidToken),

View file

@ -82,6 +82,11 @@ pub enum Expression {
consequence: Box<Node>,
alternative: Option<Box<Node>>,
},
Function {
parameters: Vec<String>,
body: Box<Node>,
},
}
impl Expression {
@ -150,6 +155,10 @@ impl Display for Expression {
None => "N/A".to_string(),
}
),
Expression::Function { parameters, body } => {
write!(f, "fn({}) {}", parameters.join(", "), body)
}
}
}
}

View file

@ -10,6 +10,7 @@ pub enum ParserError {
ExpectedBoolean,
ExpectedNumeric,
ExpectedBlock,
ExpectedLeftParenthesis,
ExpectedRightParenthesis,
}
@ -24,6 +25,7 @@ impl Display for ParserError {
ParserError::ExpectedBoolean => write!(f, "expected boolean expression"),
ParserError::ExpectedNumeric => write!(f, "expected numeric expression"),
ParserError::ExpectedBlock => write!(f, "expected block"),
ParserError::ExpectedLeftParenthesis => write!(f, "expected left parenthesis"),
ParserError::ExpectedRightParenthesis => write!(f, "expected right parenthesis"),
}
}

View file

@ -45,7 +45,6 @@ fn next_node(tokens: &mut Tokens) -> Option<Result<Node, ParserError>> {
match keyword {
Keyword::Let => parse_let_statement(tokens),
Keyword::Return => parse_return_statement(tokens),
_ => panic!("not implemented"),
}
}
@ -237,16 +236,21 @@ fn parse_prefix_operator(
_ => return Err(ParserError::ExpectedNumeric),
},
// It feels weird that 'if' is a prefix operator, but it is. If operators on the expression
// by converting the expressions output into something different.
// It feels weird that 'if' is a prefix operator, but it is. If operators on the
// expression by converting the expressions output into something different.
PrefixOperator::If => {
let condition = parse_expression(tokens, Precedence::Lowest)?;
// Conditions in if statements must evaluate to booleans
if !condition.is_bool() {
return Err(ParserError::ExpectedBoolean);
}
let consequence = parse_block_statement(tokens)?;
let alternative = if tokens.peek() == Some(&Token::Else) {
// Eat else
tokens.next();
Some(Box::new(parse_block_statement(tokens)?))
} else {
None
@ -258,6 +262,48 @@ fn parse_prefix_operator(
alternative,
}
}
// Once again, strange to think of it as such, but definitely is a prefix operator.
// We're not quite at LISP levels of "everything is an expression!!" but we're not
// that far away
PrefixOperator::Function => {
if Some(Token::LeftParenthesis) != tokens.next() {
return Err(ParserError::ExpectedLeftParenthesis);
}
let mut parameters = Vec::new();
// Because of how we're looping, `fn(x,,,,y)` is completely valid and will parse
// to Expression::Function{ parameters: ["x", "y"], body: ... } which is fine but
// technically different than the Monkey spec. We could fix this by simply checking
// if the next token after each comma is an identifier
loop {
let ident = match tokens.next() {
Some(Token::Ident(ident)) => ident,
Some(Token::Comma) => continue,
Some(Token::RightParenthesis) => break,
// Unexpected tokens
Some(tok) => {
return Err(ParserError::UnexpectedToken(
"identifier, comma, or right parenthesis",
tok,
))
}
// Unexpected EOF
None => return Err(ParserError::EOF),
};
parameters.push(ident);
}
let body = parse_block_statement(tokens)?;
Expression::Function {
parameters,
body: Box::new(body),
}
}
};
Ok(expr)
@ -368,4 +414,14 @@ mod tests {
let res = parse_expression(&mut tokens, Precedence::Lowest).unwrap();
assert_eq!(&res.to_string(), expected);
}
#[rstest]
#[case("fn() {true}", "fn() { true }")]
#[case("fn(x) {x + 3}", "fn(x) { (x + 3) }")]
#[case("fn(x, y) {x + y}", "fn(x, y) { (x + y) }")]
fn test_function_expression(#[case] input: &str, #[case] expected: &str) {
let mut tokens = lexer::tokenize(input).unwrap();
let res = parse_expression(&mut tokens, Precedence::Lowest).unwrap();
assert_eq!(&res.to_string(), expected);
}
}