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::Identifier(v) => self.eval_identifier(v, env),
|
||||||
Expression::IntegerLiteral(il) => Some(Object::Integer(il.value)),
|
Expression::IntegerLiteral(il) => Some(Object::Integer(il.value)),
|
||||||
Expression::StringLiteral(s) => Some(Object::String(s.value)),
|
Expression::StringLiteral(s) => Some(Object::String(s.value)),
|
||||||
|
Expression::ArrayLiteral(v) => unimplemented!(),
|
||||||
Expression::BooleanExpression(b) => Some(Object::Boolean(b.value)),
|
Expression::BooleanExpression(b) => Some(Object::Boolean(b.value)),
|
||||||
Expression::PrefixExpression(p) => {
|
Expression::PrefixExpression(p) => {
|
||||||
let expr = self.eval(Node::Expression(*p.right), env)?;
|
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,
|
RParen,
|
||||||
LBrace,
|
LBrace,
|
||||||
RBrace,
|
RBrace,
|
||||||
|
LBracket,
|
||||||
|
RBracket,
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
Function,
|
Function,
|
||||||
|
@ -81,6 +83,8 @@ impl Display for TokenType {
|
||||||
TokenType::RParen => ")",
|
TokenType::RParen => ")",
|
||||||
TokenType::LBrace => "{",
|
TokenType::LBrace => "{",
|
||||||
TokenType::RBrace => "}",
|
TokenType::RBrace => "}",
|
||||||
|
TokenType::LBracket => "[",
|
||||||
|
TokenType::RBracket => "]",
|
||||||
TokenType::Function => "fn",
|
TokenType::Function => "fn",
|
||||||
TokenType::If => "if",
|
TokenType::If => "if",
|
||||||
TokenType::Else => "else",
|
TokenType::Else => "else",
|
||||||
|
@ -241,6 +245,8 @@ impl<'a> Iterator for Lexer<'a> {
|
||||||
Some(';') => Some(token!(TokenType::Semicolon)),
|
Some(';') => Some(token!(TokenType::Semicolon)),
|
||||||
Some('(') => Some(token!(TokenType::LParen)),
|
Some('(') => Some(token!(TokenType::LParen)),
|
||||||
Some(')') => Some(token!(TokenType::RParen)),
|
Some(')') => Some(token!(TokenType::RParen)),
|
||||||
|
Some('[') => Some(token!(TokenType::LBracket)),
|
||||||
|
Some(']') => Some(token!(TokenType::RBracket)),
|
||||||
Some('{') => Some(token!(TokenType::LBrace)),
|
Some('{') => Some(token!(TokenType::LBrace)),
|
||||||
Some('}') => Some(token!(TokenType::RBrace)),
|
Some('}') => Some(token!(TokenType::RBrace)),
|
||||||
Some('!') => {
|
Some('!') => {
|
||||||
|
@ -379,6 +385,7 @@ mod tests {
|
||||||
|
|
||||||
\"foobar\"
|
\"foobar\"
|
||||||
\"foo bar\"
|
\"foo bar\"
|
||||||
|
[1,2];
|
||||||
|
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
|
@ -433,6 +440,12 @@ mod tests {
|
||||||
token!(TokenType::Semicolon),
|
token!(TokenType::Semicolon),
|
||||||
token!(TokenType::String, "foobar"),
|
token!(TokenType::String, "foobar"),
|
||||||
token!(TokenType::String, "foo bar"),
|
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),
|
token!(TokenType::EOF),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,7 +7,7 @@ use {
|
||||||
std::{
|
std::{
|
||||||
cmp::PartialOrd,
|
cmp::PartialOrd,
|
||||||
convert::From,
|
convert::From,
|
||||||
fmt::{Display, Formatter, Result as FmtResult},
|
fmt::{Display, Formatter, Result as FmtResult, Write},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -189,6 +189,7 @@ pub enum Expression {
|
||||||
Identifier(Identifier),
|
Identifier(Identifier),
|
||||||
IntegerLiteral(IntegerLiteral),
|
IntegerLiteral(IntegerLiteral),
|
||||||
StringLiteral(StringLiteral),
|
StringLiteral(StringLiteral),
|
||||||
|
ArrayLiteral(ArrayLiteral),
|
||||||
PrefixExpression(PrefixExpression),
|
PrefixExpression(PrefixExpression),
|
||||||
InfixExpression(InfixExpression),
|
InfixExpression(InfixExpression),
|
||||||
BooleanExpression(BooleanExpression),
|
BooleanExpression(BooleanExpression),
|
||||||
|
@ -198,7 +199,11 @@ pub enum Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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) {
|
match parser.prefix_parse_fns.get(&ctoken.name) {
|
||||||
Some(prefix) => {
|
Some(prefix) => {
|
||||||
let mut left_expr = prefix(parser, ctoken);
|
let mut left_expr = prefix(parser, ctoken);
|
||||||
|
@ -244,6 +249,7 @@ impl Display for Expression {
|
||||||
Expression::Identifier(v) => v.to_string(),
|
Expression::Identifier(v) => v.to_string(),
|
||||||
Expression::IntegerLiteral(v) => v.value.to_string(),
|
Expression::IntegerLiteral(v) => v.value.to_string(),
|
||||||
Expression::StringLiteral(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::PrefixExpression(v) => v.to_string(),
|
||||||
Expression::InfixExpression(v) => v.to_string(),
|
Expression::InfixExpression(v) => v.to_string(),
|
||||||
Expression::BooleanExpression(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)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct PrefixExpression {
|
pub struct PrefixExpression {
|
||||||
pub operator: TokenType,
|
pub operator: TokenType,
|
||||||
|
@ -591,49 +624,14 @@ impl CallExpression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(parser: &mut Parser, ctoken: Token, expr: Expression) -> Option<Expression> {
|
pub fn parse(parser: &mut Parser, _ctoken: Token, expr: Expression) -> Option<Expression> {
|
||||||
let args = Self::parse_call_arguments(parser, ctoken);
|
let args = parser.parse_expression_list(TokenType::RParen)?;
|
||||||
|
|
||||||
Some(Expression::CallExpression(Self {
|
Some(Expression::CallExpression(Self {
|
||||||
function: Box::new(expr),
|
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 {
|
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::LParen, Expression::parse_grouped_expression);
|
||||||
parser.register_prefix(TokenType::If, IfExpression::parse);
|
parser.register_prefix(TokenType::If, IfExpression::parse);
|
||||||
parser.register_prefix(TokenType::Function, FunctionLiteral::parse);
|
parser.register_prefix(TokenType::Function, FunctionLiteral::parse);
|
||||||
|
parser.register_prefix(TokenType::LBracket, ArrayLiteral::parse);
|
||||||
|
|
||||||
// Neat trick!
|
// Neat trick!
|
||||||
// Call expressions looks like <ident>(<args>).
|
// Call expressions looks like <ident>(<args>).
|
||||||
|
@ -146,6 +147,37 @@ impl<'a> Parser<'a> {
|
||||||
None => ExpressionPriority::Lowest,
|
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)]
|
#[derive(PartialEq, Debug)]
|
||||||
|
@ -892,4 +924,29 @@ mod tests {
|
||||||
assert_eq!(program.unwrap().to_string(), test.1);
|
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