added array literals, refactored CallExpression::parse
This commit is contained in:
parent
ad2d88e511
commit
3eeec60d3b
|
@ -47,6 +47,7 @@ impl Evaluator for TreeWalker {
|
|||
Expression::Identifier(v) => self.eval_identifier(v, env),
|
||||
Expression::IntegerLiteral(il) => Some(Object::Integer(il.value)),
|
||||
Expression::StringLiteral(s) => Some(Object::String(s.value)),
|
||||
Expression::ArrayLiteral(v) => unimplemented!(),
|
||||
Expression::BooleanExpression(b) => Some(Object::Boolean(b.value)),
|
||||
Expression::PrefixExpression(p) => {
|
||||
let expr = self.eval(Node::Expression(*p.right), env)?;
|
||||
|
|
13
src/lexer.rs
13
src/lexer.rs
|
@ -51,6 +51,8 @@ pub enum TokenType {
|
|||
RParen,
|
||||
LBrace,
|
||||
RBrace,
|
||||
LBracket,
|
||||
RBracket,
|
||||
|
||||
// Keywords
|
||||
Function,
|
||||
|
@ -81,6 +83,8 @@ impl Display for TokenType {
|
|||
TokenType::RParen => ")",
|
||||
TokenType::LBrace => "{",
|
||||
TokenType::RBrace => "}",
|
||||
TokenType::LBracket => "[",
|
||||
TokenType::RBracket => "]",
|
||||
TokenType::Function => "fn",
|
||||
TokenType::If => "if",
|
||||
TokenType::Else => "else",
|
||||
|
@ -241,6 +245,8 @@ impl<'a> Iterator for Lexer<'a> {
|
|||
Some(';') => Some(token!(TokenType::Semicolon)),
|
||||
Some('(') => Some(token!(TokenType::LParen)),
|
||||
Some(')') => Some(token!(TokenType::RParen)),
|
||||
Some('[') => Some(token!(TokenType::LBracket)),
|
||||
Some(']') => Some(token!(TokenType::RBracket)),
|
||||
Some('{') => Some(token!(TokenType::LBrace)),
|
||||
Some('}') => Some(token!(TokenType::RBrace)),
|
||||
Some('!') => {
|
||||
|
@ -379,6 +385,7 @@ mod tests {
|
|||
|
||||
\"foobar\"
|
||||
\"foo bar\"
|
||||
[1,2];
|
||||
|
||||
"
|
||||
)
|
||||
|
@ -433,6 +440,12 @@ mod tests {
|
|||
token!(TokenType::Semicolon),
|
||||
token!(TokenType::String, "foobar"),
|
||||
token!(TokenType::String, "foo bar"),
|
||||
token!(TokenType::LBracket),
|
||||
token!(TokenType::Int, "1"),
|
||||
token!(TokenType::Comma),
|
||||
token!(TokenType::Int, "2"),
|
||||
token!(TokenType::RBracket),
|
||||
token!(TokenType::Semicolon),
|
||||
token!(TokenType::EOF),
|
||||
],
|
||||
);
|
||||
|
|
|
@ -7,7 +7,7 @@ use {
|
|||
std::{
|
||||
cmp::PartialOrd,
|
||||
convert::From,
|
||||
fmt::{Display, Formatter, Result as FmtResult},
|
||||
fmt::{Display, Formatter, Result as FmtResult, Write},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -189,6 +189,7 @@ pub enum Expression {
|
|||
Identifier(Identifier),
|
||||
IntegerLiteral(IntegerLiteral),
|
||||
StringLiteral(StringLiteral),
|
||||
ArrayLiteral(ArrayLiteral),
|
||||
PrefixExpression(PrefixExpression),
|
||||
InfixExpression(InfixExpression),
|
||||
BooleanExpression(BooleanExpression),
|
||||
|
@ -198,7 +199,11 @@ pub enum Expression {
|
|||
}
|
||||
|
||||
impl Expression {
|
||||
fn parse(parser: &mut Parser, ctoken: Token, precedence: ExpressionPriority) -> Option<Self> {
|
||||
pub fn parse(
|
||||
parser: &mut Parser,
|
||||
ctoken: Token,
|
||||
precedence: ExpressionPriority,
|
||||
) -> Option<Self> {
|
||||
match parser.prefix_parse_fns.get(&ctoken.name) {
|
||||
Some(prefix) => {
|
||||
let mut left_expr = prefix(parser, ctoken);
|
||||
|
@ -244,6 +249,7 @@ impl Display for Expression {
|
|||
Expression::Identifier(v) => v.to_string(),
|
||||
Expression::IntegerLiteral(v) => v.value.to_string(),
|
||||
Expression::StringLiteral(v) => v.value.to_string(),
|
||||
Expression::ArrayLiteral(v) => v.to_string(),
|
||||
Expression::PrefixExpression(v) => v.to_string(),
|
||||
Expression::InfixExpression(v) => v.to_string(),
|
||||
Expression::BooleanExpression(v) => v.to_string(),
|
||||
|
@ -333,6 +339,33 @@ impl StringLiteral {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct ArrayLiteral {
|
||||
elements: Vec<Expression>,
|
||||
}
|
||||
|
||||
impl ArrayLiteral {
|
||||
pub fn new(expr: Vec<Expression>) -> Self {
|
||||
Self { elements: expr }
|
||||
}
|
||||
pub fn parse(parser: &mut Parser, _token: Token) -> Option<Expression> {
|
||||
let mut out = ArrayLiteral { elements: vec![] };
|
||||
|
||||
out.elements = parser.parse_expression_list(TokenType::RBracket)?;
|
||||
|
||||
Some(Expression::ArrayLiteral(out))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ArrayLiteral {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
f.write_char('[').unwrap();
|
||||
f.write_str(&self.elements.iter().map(|x| x.to_string()).join(", "))
|
||||
.unwrap();
|
||||
f.write_char(']')
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct PrefixExpression {
|
||||
pub operator: TokenType,
|
||||
|
@ -591,49 +624,14 @@ impl CallExpression {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse(parser: &mut Parser, ctoken: Token, expr: Expression) -> Option<Expression> {
|
||||
let args = Self::parse_call_arguments(parser, ctoken);
|
||||
pub fn parse(parser: &mut Parser, _ctoken: Token, expr: Expression) -> Option<Expression> {
|
||||
let args = parser.parse_expression_list(TokenType::RParen)?;
|
||||
|
||||
Some(Expression::CallExpression(Self {
|
||||
function: Box::new(expr),
|
||||
arguments: args?,
|
||||
arguments: args,
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_call_arguments(parser: &mut Parser, _ctoken: Token) -> Option<Vec<Expression>> {
|
||||
let mut expressions = vec![];
|
||||
if let Some(token) = parser.lexer.peek() {
|
||||
if token.name == TokenType::RParen {
|
||||
parser.lexer.next();
|
||||
return Some(expressions);
|
||||
}
|
||||
}
|
||||
let ntoken = match parser.lexer.next() {
|
||||
Some(token) => token,
|
||||
None => return Some(expressions),
|
||||
};
|
||||
|
||||
if let Some(expr) = Expression::parse(parser, ntoken, ExpressionPriority::Lowest) {
|
||||
expressions.push(expr);
|
||||
} else {
|
||||
return Some(expressions);
|
||||
}
|
||||
while parser.peek_token_is(TokenType::Comma) {
|
||||
parser.lexer.next();
|
||||
let token = match parser.lexer.next() {
|
||||
Some(v) => v,
|
||||
None => return Some(expressions),
|
||||
};
|
||||
if let Some(expr) = Expression::parse(parser, token, ExpressionPriority::Lowest) {
|
||||
expressions.push(expr);
|
||||
} else {
|
||||
return Some(expressions);
|
||||
}
|
||||
}
|
||||
parser.expect_peek(TokenType::RParen)?;
|
||||
|
||||
Some(expressions)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CallExpression {
|
||||
|
|
|
@ -58,6 +58,7 @@ impl<'a> Parser<'a> {
|
|||
parser.register_prefix(TokenType::LParen, Expression::parse_grouped_expression);
|
||||
parser.register_prefix(TokenType::If, IfExpression::parse);
|
||||
parser.register_prefix(TokenType::Function, FunctionLiteral::parse);
|
||||
parser.register_prefix(TokenType::LBracket, ArrayLiteral::parse);
|
||||
|
||||
// Neat trick!
|
||||
// Call expressions looks like <ident>(<args>).
|
||||
|
@ -146,6 +147,37 @@ impl<'a> Parser<'a> {
|
|||
None => ExpressionPriority::Lowest,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_expression_list(&mut self, end_token: TokenType) -> Option<Vec<Expression>> {
|
||||
let mut out = vec![];
|
||||
|
||||
if self.peek_token_is(end_token) {
|
||||
self.lexer.next();
|
||||
return Some(out);
|
||||
}
|
||||
|
||||
let next_token = self.lexer.next()?;
|
||||
out.push(Expression::parse(
|
||||
self,
|
||||
next_token,
|
||||
ExpressionPriority::Lowest,
|
||||
)?);
|
||||
|
||||
while self.peek_token_is(TokenType::Comma) {
|
||||
self.lexer.next();
|
||||
let next_token = self.lexer.next()?;
|
||||
|
||||
out.push(Expression::parse(
|
||||
self,
|
||||
next_token,
|
||||
ExpressionPriority::Lowest,
|
||||
)?);
|
||||
}
|
||||
|
||||
self.expect_peek(end_token)?;
|
||||
|
||||
Some(out)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
|
@ -892,4 +924,29 @@ mod tests {
|
|||
assert_eq!(program.unwrap().to_string(), test.1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_literals() {
|
||||
let test_cases = [(
|
||||
"[1, 2 * 2, 3 + 3]",
|
||||
vec![Statement::ExpressionStatement(ExpressionStatement::new(
|
||||
token!(TokenType::LBracket),
|
||||
Expression::ArrayLiteral(ArrayLiteral::new(vec![
|
||||
Expression::IntegerLiteral(IntegerLiteral::new(1)),
|
||||
Expression::InfixExpression(InfixExpression::new(
|
||||
Expression::IntegerLiteral(IntegerLiteral::new(2)),
|
||||
TokenType::Asterisk,
|
||||
Expression::IntegerLiteral(IntegerLiteral::new(2)),
|
||||
)),
|
||||
Expression::InfixExpression(InfixExpression::new(
|
||||
Expression::IntegerLiteral(IntegerLiteral::new(3)),
|
||||
TokenType::Plus,
|
||||
Expression::IntegerLiteral(IntegerLiteral::new(3)),
|
||||
)),
|
||||
])),
|
||||
))],
|
||||
)];
|
||||
|
||||
check_test_cases(&test_cases);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user