Implement postfix parsing

This commit is contained in:
Jeff 2024-08-09 18:14:46 -04:00
parent 60f8aab805
commit 82fbf796f3
6 changed files with 263 additions and 178 deletions

View File

@ -34,12 +34,6 @@ impl<T: Display> Display for Node<T> {
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Statement { pub enum Statement {
// Variable assignment
Assignment {
identifier: Node<Identifier>,
value_node: Box<Node<Statement>>,
},
// A sequence of statements // A sequence of statements
Block(Vec<Node<Statement>>), Block(Vec<Node<Statement>>),
@ -70,7 +64,7 @@ pub enum Statement {
// Value collection expressions // Value collection expressions
List(Vec<Node<Statement>>), List(Vec<Node<Statement>>),
Map(Vec<(Node<Identifier>, Node<Statement>)>), Map(Vec<(Node<Statement>, Node<Statement>)>),
// Hard-coded value // Hard-coded value
Constant(Value), Constant(Value),
@ -83,7 +77,6 @@ pub enum Statement {
impl Statement { impl Statement {
pub fn expected_type(&self, variables: &HashMap<Identifier, Value>) -> Option<Type> { pub fn expected_type(&self, variables: &HashMap<Identifier, Value>) -> Option<Type> {
match self { match self {
Statement::Assignment { .. } => None,
Statement::Block(nodes) => nodes.last().unwrap().inner.expected_type(variables), Statement::Block(nodes) => nodes.last().unwrap().inner.expected_type(variables),
Statement::BinaryOperation { left, .. } => left.inner.expected_type(variables), Statement::BinaryOperation { left, .. } => left.inner.expected_type(variables),
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(), Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(),
@ -104,10 +97,9 @@ impl Statement {
let mut types = BTreeMap::new(); let mut types = BTreeMap::new();
for (identifier, item) in nodes { for (identifier, item) in nodes {
types.insert( if let Statement::Identifier(identifier) = &identifier.inner {
identifier.inner.clone(), types.insert(identifier.clone(), item.inner.expected_type(variables)?);
item.inner.expected_type(variables)?, }
);
} }
Some(Type::Map(types)) Some(Type::Map(types))
@ -121,12 +113,6 @@ impl Statement {
impl Display for Statement { impl Display for Statement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Statement::Assignment {
identifier,
value_node: value,
} => {
write!(f, "{identifier} = {value}")
}
Statement::Block(statements) => { Statement::Block(statements) => {
write!(f, "{{ ")?; write!(f, "{{ ")?;
@ -270,12 +256,18 @@ pub enum BinaryOperator {
// Logic // Logic
And, And,
Or, Or,
// Assignment
Assign,
AddAssign,
} }
impl Display for BinaryOperator { impl Display for BinaryOperator {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
BinaryOperator::Add => write!(f, "+"), BinaryOperator::Add => write!(f, "+"),
BinaryOperator::AddAssign => write!(f, "+="),
BinaryOperator::Assign => write!(f, "="),
BinaryOperator::And => write!(f, "&&"), BinaryOperator::And => write!(f, "&&"),
BinaryOperator::Divide => write!(f, "/"), BinaryOperator::Divide => write!(f, "/"),
BinaryOperator::Equal => write!(f, "=="), BinaryOperator::Equal => write!(f, "=="),

View File

@ -77,18 +77,6 @@ impl<'a> Analyzer<'a> {
fn analyze_node(&self, node: &Node<Statement>) -> Result<(), AnalyzerError> { fn analyze_node(&self, node: &Node<Statement>) -> Result<(), AnalyzerError> {
match &node.inner { match &node.inner {
Statement::Assignment {
value_node: value, ..
} => {
self.analyze_node(value)?;
if value.inner.expected_type(self.variables).is_none() {
return Err(AnalyzerError::ExpectedValue {
actual: value.as_ref().clone(),
position: value.position,
});
}
}
Statement::BinaryOperation { Statement::BinaryOperation {
left, left,
operator, operator,
@ -97,6 +85,12 @@ impl<'a> Analyzer<'a> {
self.analyze_node(left)?; self.analyze_node(left)?;
self.analyze_node(right)?; self.analyze_node(right)?;
if let BinaryOperator::AddAssign | BinaryOperator::Assign = operator.inner {
if let Statement::Identifier(_) = left.inner {
return Ok(());
}
}
let left_type = left.inner.expected_type(self.variables); let left_type = left.inner.expected_type(self.variables);
let right_type = right.inner.expected_type(self.variables); let right_type = right.inner.expected_type(self.variables);
@ -157,11 +151,12 @@ impl<'a> Analyzer<'a> {
}); });
} }
} }
Statement::Identifier(_) => { Statement::Identifier(identifier) => {
return Err(AnalyzerError::UnexpectedIdentifier { if !self.variables.contains_key(identifier) {
identifier: node.clone(), return Err(AnalyzerError::UndefinedVariable {
position: node.position, identifier: node.clone(),
}); });
}
} }
Statement::List(statements) => { Statement::List(statements) => {
for statement in statements { for statement in statements {
@ -251,6 +246,9 @@ pub enum AnalyzerError {
actual: Node<Statement>, actual: Node<Statement>,
position: Span, position: Span,
}, },
UndefinedVariable {
identifier: Node<Statement>,
},
UnexpectedIdentifier { UnexpectedIdentifier {
identifier: Node<Statement>, identifier: Node<Statement>,
position: Span, position: Span,
@ -274,6 +272,7 @@ impl AnalyzerError {
AnalyzerError::ExpectedIntegerFloatOrString { position, .. } => *position, AnalyzerError::ExpectedIntegerFloatOrString { position, .. } => *position,
AnalyzerError::ExpectedSameType { position, .. } => *position, AnalyzerError::ExpectedSameType { position, .. } => *position,
AnalyzerError::ExpectedString { position, .. } => *position, AnalyzerError::ExpectedString { position, .. } => *position,
AnalyzerError::UndefinedVariable { identifier } => identifier.position,
AnalyzerError::UnexpectedIdentifier { position, .. } => *position, AnalyzerError::UnexpectedIdentifier { position, .. } => *position,
AnalyzerError::UnexectedString { position, .. } => *position, AnalyzerError::UnexectedString { position, .. } => *position,
} }
@ -315,6 +314,9 @@ impl Display for AnalyzerError {
AnalyzerError::ExpectedValue { actual, .. } => { AnalyzerError::ExpectedValue { actual, .. } => {
write!(f, "Expected value, found {}", actual) write!(f, "Expected value, found {}", actual)
} }
AnalyzerError::UndefinedVariable { identifier } => {
write!(f, "Undefined variable {}", identifier)
}
AnalyzerError::UnexpectedIdentifier { identifier, .. } => { AnalyzerError::UnexpectedIdentifier { identifier, .. } => {
write!(f, "Unexpected identifier {}", identifier) write!(f, "Unexpected identifier {}", identifier)
} }
@ -443,7 +445,7 @@ mod tests {
} }
#[test] #[test]
fn unexpected_identifier() { fn undefined_variable() {
let abstract_tree = AbstractSyntaxTree { let abstract_tree = AbstractSyntaxTree {
nodes: [Node::new( nodes: [Node::new(
Statement::Identifier(Identifier::new("x")), Statement::Identifier(Identifier::new("x")),
@ -456,9 +458,8 @@ mod tests {
assert_eq!( assert_eq!(
analyzer.analyze(), analyzer.analyze(),
Err(AnalyzerError::UnexpectedIdentifier { Err(AnalyzerError::UndefinedVariable {
identifier: Node::new(Statement::Identifier(Identifier::new("x")), (0, 1)), identifier: Node::new(Statement::Identifier(Identifier::new("x")), (0, 1))
position: (0, 1)
}) })
) )
} }

View File

@ -122,9 +122,15 @@ impl Lexer {
'"' => self.lex_string('"', source)?, '"' => self.lex_string('"', source)?,
'\'' => self.lex_string('\'', source)?, '\'' => self.lex_string('\'', source)?,
'+' => { '+' => {
self.position += 1; if let Some('=') = self.peek_second_char(source) {
self.position += 2;
(Token::Plus, (self.position - 1, self.position)) (Token::PlusEqual, (self.position - 2, self.position))
} else {
self.position += 1;
(Token::Plus, (self.position - 1, self.position))
}
} }
'*' => { '*' => {
self.position += 1; self.position += 1;
@ -452,6 +458,21 @@ impl Display for LexError {
mod tests { mod tests {
use super::*; use super::*;
#[test]
fn add_assign() {
let input = "x += 42";
assert_eq!(
lex(input),
Ok(vec![
(Token::Identifier("x"), (0, 1)),
(Token::PlusEqual, (2, 4)),
(Token::Integer("42"), (5, 7)),
(Token::Eof, (7, 7)),
])
)
}
#[test] #[test]
fn or() { fn or() {
let input = "true || false"; let input = "true || false";

View File

@ -202,25 +202,10 @@ impl<'src> Parser<'src> {
(Token::Identifier(text), position) => { (Token::Identifier(text), position) => {
self.next_token()?; self.next_token()?;
if let (Token::Equal, _) = self.current { Ok(Node::new(
self.next_token()?; Statement::Identifier(Identifier::new(text)),
position,
let value_node = self.parse_node(0)?; ))
let right_end = value_node.position.1;
Ok(Node::new(
Statement::Assignment {
identifier: Node::new(Identifier::new(text), position),
value_node: Box::new(value_node),
},
(position.0, right_end),
))
} else {
Ok(Node::new(
Statement::Identifier(Identifier::new(text)),
position,
))
}
} }
(Token::String(string), position) => { (Token::String(string), position) => {
self.next_token()?; self.next_token()?;
@ -259,13 +244,17 @@ impl<'src> Parser<'src> {
let next_node = self.parse_node(0)?; let next_node = self.parse_node(0)?;
// If the next node is an assignment, this might be a map // If the next node is an assignment, this might be a map
if let Statement::Assignment { if let Statement::BinaryOperation {
identifier, left,
value_node, operator:
Node {
inner: BinaryOperator::Assign,
..
},
right,
} = next_node.inner } = next_node.inner
{ {
// If the current token is a comma, right curly brace, or the new // If the current token is a comma, or the new statement is already a map
// statement is already a map
if self.current.0 == Token::Comma if self.current.0 == Token::Comma
|| statement || statement
.as_ref() .as_ref()
@ -281,38 +270,9 @@ impl<'src> Parser<'src> {
} }
// Add the new property to the map // Add the new property to the map
map_properties.push((identifier, *value_node)); map_properties.push((*left, *right));
}
// Otherwise, the new statement is a block
} else if let Statement::Block(statements) =
statement.get_or_insert_with(|| Statement::Block(Vec::new()))
{
if self.current.0 == Token::Semicolon {
self.next_token()?;
statements.push(Node::new(
Statement::Nil(Box::new(Node::new(
Statement::Assignment {
identifier,
value_node,
},
next_node.position,
))),
(next_node.position.0, self.current.1 .1),
));
continue;
} else {
statements.push(Node::new(
Statement::Assignment {
identifier,
value_node,
},
next_node.position,
));
continue;
} }
// Otherwise, the new statement is a block
} }
} else if let Statement::Block(statements) = } else if let Statement::Block(statements) =
statement.get_or_insert_with(|| Statement::Block(Vec::new())) statement.get_or_insert_with(|| Statement::Block(Vec::new()))
@ -443,27 +403,42 @@ impl<'src> Parser<'src> {
fn parse_infix(&mut self, left: Node<Statement>) -> Result<Node<Statement>, ParseError> { fn parse_infix(&mut self, left: Node<Statement>) -> Result<Node<Statement>, ParseError> {
let left_start = left.position.0; let left_start = left.position.0;
// Postfix operations
if let Token::Semicolon = &self.current.0 {
self.next_token()?;
let right_end = self.current.1 .1;
return Ok(Node::new(
Statement::Nil(Box::new(left)),
(left_start, right_end),
));
};
// Infix operations
let binary_operator = match &self.current { let binary_operator = match &self.current {
(Token::Dot, _) => { (Token::Dot, _) => {
self.next_token()?; self.next_token()?;
let right_node = self.parse_node(0)?; let right = self.parse_node(0)?;
let right_end = right_node.position.1; let right_end = right.position.1;
return Ok(Node::new( return Ok(Node::new(
Statement::PropertyAccess(Box::new(left), Box::new(right_node)), Statement::PropertyAccess(Box::new(left), Box::new(right)),
(left_start, right_end), (left_start, right_end),
)); ));
} }
(Token::DoubleAmpersand, _) => Node::new(BinaryOperator::And, self.current.1), (Token::DoubleAmpersand, _) => Node::new(BinaryOperator::And, self.current.1),
(Token::DoubleEqual, _) => Node::new(BinaryOperator::Equal, self.current.1), (Token::DoubleEqual, _) => Node::new(BinaryOperator::Equal, self.current.1),
(Token::DoublePipe, _) => Node::new(BinaryOperator::Or, self.current.1), (Token::DoublePipe, _) => Node::new(BinaryOperator::Or, self.current.1),
(Token::Equal, _) => Node::new(BinaryOperator::Assign, self.current.1),
(Token::Greater, _) => Node::new(BinaryOperator::Greater, self.current.1), (Token::Greater, _) => Node::new(BinaryOperator::Greater, self.current.1),
(Token::GreaterEqual, _) => Node::new(BinaryOperator::GreaterOrEqual, self.current.1), (Token::GreaterEqual, _) => Node::new(BinaryOperator::GreaterOrEqual, self.current.1),
(Token::Less, _) => Node::new(BinaryOperator::Less, self.current.1), (Token::Less, _) => Node::new(BinaryOperator::Less, self.current.1),
(Token::LessEqual, _) => Node::new(BinaryOperator::LessOrEqual, self.current.1), (Token::LessEqual, _) => Node::new(BinaryOperator::LessOrEqual, self.current.1),
(Token::Minus, _) => Node::new(BinaryOperator::Subtract, self.current.1), (Token::Minus, _) => Node::new(BinaryOperator::Subtract, self.current.1),
(Token::Plus, _) => Node::new(BinaryOperator::Add, self.current.1), (Token::Plus, _) => Node::new(BinaryOperator::Add, self.current.1),
(Token::PlusEqual, _) => Node::new(BinaryOperator::AddAssign, self.current.1),
(Token::Star, _) => Node::new(BinaryOperator::Multiply, self.current.1), (Token::Star, _) => Node::new(BinaryOperator::Multiply, self.current.1),
(Token::Slash, _) => Node::new(BinaryOperator::Divide, self.current.1), (Token::Slash, _) => Node::new(BinaryOperator::Divide, self.current.1),
(Token::Percent, _) => Node::new(BinaryOperator::Modulo, self.current.1), (Token::Percent, _) => Node::new(BinaryOperator::Modulo, self.current.1),
@ -479,6 +454,7 @@ impl<'src> Parser<'src> {
self.next_token()?; self.next_token()?;
let left_start = left.position.0;
let right = self.parse_node(0)?; let right = self.parse_node(0)?;
let right_end = right.position.1; let right_end = right.position.1;
@ -494,6 +470,8 @@ impl<'src> Parser<'src> {
fn current_precedence(&self) -> u8 { fn current_precedence(&self) -> u8 {
match self.current.0 { match self.current.0 {
Token::Semicolon => 10,
Token::Equal | Token::PlusEqual => 8,
Token::DoubleEqual => 7, Token::DoubleEqual => 7,
Token::DoubleAmpersand | Token::DoublePipe => 6, Token::DoubleAmpersand | Token::DoublePipe => 6,
Token::Greater | Token::GreaterEqual | Token::Less | Token::LessEqual => 5, Token::Greater | Token::GreaterEqual | Token::Less | Token::LessEqual => 5,
@ -506,6 +484,17 @@ impl<'src> Parser<'src> {
_ => 0, _ => 0,
} }
} }
fn peek_token(&mut self) -> Token {
self.lexer
.peek_token(self.source)
.map(|(token, _)| token)
.unwrap_or(Token::Eof)
}
fn next_is_postfix(&mut self) -> bool {
matches!(self.peek_token(), Token::Semicolon)
}
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -588,6 +577,29 @@ mod tests {
use super::*; use super::*;
#[test]
fn add_assign() {
let input = "a += 1";
assert_eq!(
parse(input),
Ok(AbstractSyntaxTree {
nodes: [Node::new(
Statement::BinaryOperation {
left: Box::new(Node::new(
Statement::Identifier(Identifier::new("a")),
(0, 1)
)),
operator: Node::new(BinaryOperator::AddAssign, (2, 4)),
right: Box::new(Node::new(Statement::Constant(Value::integer(1)), (5, 6))),
},
(0, 6)
)]
.into()
})
);
}
#[test] #[test]
fn or() { fn or() {
let input = "true || false"; let input = "true || false";
@ -667,37 +679,49 @@ mod tests {
Statement::Block(vec![ Statement::Block(vec![
Node::new( Node::new(
Statement::Nil(Box::new(Node::new( Statement::Nil(Box::new(Node::new(
Statement::Assignment { Statement::BinaryOperation {
identifier: Node::new(Identifier::new("foo"), (2, 5)), left: Box::new(Node::new(
value_node: Box::new(Node::new( Statement::Identifier(Identifier::new("foo")),
(2, 5)
)),
operator: Node::new(BinaryOperator::Assign, (6, 8)),
right: Box::new(Node::new(
Statement::Constant(Value::integer(42)), Statement::Constant(Value::integer(42)),
(8, 10) (9, 11)
)) )),
}, },
(2, 10) (2, 11)
),)), ),)),
(2, 15) (2, 15)
), ),
Node::new( Node::new(
Statement::Nil(Box::new(Node::new( Statement::Nil(Box::new(Node::new(
Statement::Assignment { Statement::BinaryOperation {
identifier: Node::new(Identifier::new("bar"), (12, 15)), left: Box::new(Node::new(
value_node: Box::new(Node::new( Statement::Identifier(Identifier::new("bar")),
(16, 19)
)),
operator: Node::new(BinaryOperator::Assign, (20, 22)),
right: Box::new(Node::new(
Statement::Constant(Value::integer(42)), Statement::Constant(Value::integer(42)),
(18, 20) (23, 25)
)) )),
}, },
(12, 20) (12, 20)
),)), ),)),
(12, 25) (12, 25)
), ),
Node::new( Node::new(
Statement::Assignment { Statement::BinaryOperation {
identifier: Node::new(Identifier::new("baz"), (22, 25)), left: Box::new(Node::new(
value_node: Box::new(Node::new( Statement::Identifier(Identifier::new("baz")),
(26, 29)
)),
operator: Node::new(BinaryOperator::Assign, (30, 32)),
right: Box::new(Node::new(
Statement::Constant(Value::string("42")), Statement::Constant(Value::string("42")),
(28, 32) (22, 32)
)) )),
}, },
(22, 32) (22, 32)
) )
@ -731,15 +755,15 @@ mod tests {
nodes: [Node::new( nodes: [Node::new(
Statement::Map(vec![ Statement::Map(vec![
( (
Node::new(Identifier::new("foo"), (2, 5)), Node::new(Statement::Identifier(Identifier::new("foo")), (2, 5)),
Node::new(Statement::Constant(Value::integer(42)), (8, 10)) Node::new(Statement::Constant(Value::integer(42)), (8, 10))
), ),
( (
Node::new(Identifier::new("bar"), (12, 15)), Node::new(Statement::Identifier(Identifier::new("bar")), (12, 15)),
Node::new(Statement::Constant(Value::integer(42)), (18, 20)) Node::new(Statement::Constant(Value::integer(42)), (18, 20))
), ),
( (
Node::new(Identifier::new("baz"), (22, 25)), Node::new(Statement::Identifier(Identifier::new("baz")), (22, 25)),
Node::new(Statement::Constant(Value::string("42")), (28, 32)) Node::new(Statement::Constant(Value::string("42")), (28, 32))
), ),
]), ]),
@ -760,11 +784,11 @@ mod tests {
nodes: [Node::new( nodes: [Node::new(
Statement::Map(vec![ Statement::Map(vec![
( (
Node::new(Identifier::new("x"), (2, 3)), Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)),
Node::new(Statement::Constant(Value::integer(42)), (6, 8)) Node::new(Statement::Constant(Value::integer(42)), (6, 8))
), ),
( (
Node::new(Identifier::new("y"), (10, 11)), Node::new(Statement::Identifier(Identifier::new("y")), (10, 11)),
Node::new(Statement::Constant(Value::string("foobar")), (14, 22)) Node::new(Statement::Constant(Value::string("foobar")), (14, 22))
) )
]), ]),
@ -784,7 +808,7 @@ mod tests {
Ok(AbstractSyntaxTree { Ok(AbstractSyntaxTree {
nodes: [Node::new( nodes: [Node::new(
Statement::Map(vec![( Statement::Map(vec![(
Node::new(Identifier::new("x"), (2, 3)), Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)),
Node::new(Statement::Constant(Value::integer(42)), (6, 8)) Node::new(Statement::Constant(Value::integer(42)), (6, 8))
)]), )]),
(0, 11) (0, 11)
@ -854,19 +878,6 @@ mod tests {
); );
} }
#[test]
fn malformed_assignment() {
let input = "false = 1";
assert_eq!(
parse(input),
Err(ParseError::UnexpectedToken {
actual: TokenOwned::Equal,
position: (6, 7)
})
);
}
#[test] #[test]
fn less_than() { fn less_than() {
let input = "1 < 2"; let input = "1 < 2";
@ -1278,9 +1289,13 @@ mod tests {
parse(input), parse(input),
Ok(AbstractSyntaxTree { Ok(AbstractSyntaxTree {
nodes: [Node::new( nodes: [Node::new(
Statement::Assignment { Statement::BinaryOperation {
identifier: Node::new(Identifier::new("a"), (0, 1)), left: Box::new(Node::new(
value_node: Box::new(Node::new( Statement::Identifier(Identifier::new("a")),
(0, 1)
)),
operator: Node::new(BinaryOperator::Assign, (2, 3)),
right: Box::new(Node::new(
Statement::BinaryOperation { Statement::BinaryOperation {
left: Box::new(Node::new( left: Box::new(Node::new(
Statement::Constant(Value::integer(1)), Statement::Constant(Value::integer(1)),

View File

@ -6,13 +6,13 @@ use serde::{Deserialize, Serialize};
/// Source code token. /// Source code token.
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum Token<'src> { pub enum Token<'src> {
// End of file
Eof, Eof,
Identifier(&'src str),
// Hard-coded values // Hard-coded values
Boolean(&'src str), Boolean(&'src str),
Float(&'src str), Float(&'src str),
Identifier(&'src str),
Integer(&'src str), Integer(&'src str),
String(&'src str), String(&'src str),
@ -40,6 +40,7 @@ pub enum Token<'src> {
Minus, Minus,
Percent, Percent,
Plus, Plus,
PlusEqual,
RightCurlyBrace, RightCurlyBrace,
RightParenthesis, RightParenthesis,
RightSquareBrace, RightSquareBrace,
@ -75,6 +76,7 @@ impl<'src> Token<'src> {
Token::Minus => TokenOwned::Minus, Token::Minus => TokenOwned::Minus,
Token::Percent => TokenOwned::Percent, Token::Percent => TokenOwned::Percent,
Token::Plus => TokenOwned::Plus, Token::Plus => TokenOwned::Plus,
Token::PlusEqual => TokenOwned::PlusEqual,
Token::ReadLine => TokenOwned::ReadLine, Token::ReadLine => TokenOwned::ReadLine,
Token::RightCurlyBrace => TokenOwned::RightCurlyBrace, Token::RightCurlyBrace => TokenOwned::RightCurlyBrace,
Token::RightParenthesis => TokenOwned::RightParenthesis, Token::RightParenthesis => TokenOwned::RightParenthesis,
@ -113,6 +115,7 @@ impl<'src> Token<'src> {
Token::Minus => "-", Token::Minus => "-",
Token::Percent => "%", Token::Percent => "%",
Token::Plus => "+", Token::Plus => "+",
Token::PlusEqual => "+=",
Token::ReadLine => "read_line", Token::ReadLine => "read_line",
Token::RightCurlyBrace => "}", Token::RightCurlyBrace => "}",
Token::RightParenthesis => ")", Token::RightParenthesis => ")",
@ -159,6 +162,7 @@ impl<'src> PartialEq for Token<'src> {
(Token::Minus, Token::Minus) => true, (Token::Minus, Token::Minus) => true,
(Token::Percent, Token::Percent) => true, (Token::Percent, Token::Percent) => true,
(Token::Plus, Token::Plus) => true, (Token::Plus, Token::Plus) => true,
(Token::PlusEqual, Token::PlusEqual) => true,
(Token::ReadLine, Token::ReadLine) => true, (Token::ReadLine, Token::ReadLine) => true,
(Token::RightCurlyBrace, Token::RightCurlyBrace) => true, (Token::RightCurlyBrace, Token::RightCurlyBrace) => true,
(Token::RightParenthesis, Token::RightParenthesis) => true, (Token::RightParenthesis, Token::RightParenthesis) => true,
@ -212,6 +216,7 @@ pub enum TokenOwned {
Minus, Minus,
Percent, Percent,
Plus, Plus,
PlusEqual,
RightCurlyBrace, RightCurlyBrace,
RightParenthesis, RightParenthesis,
RightSquareBrace, RightSquareBrace,
@ -247,6 +252,7 @@ impl Display for TokenOwned {
TokenOwned::Minus => Token::Minus.fmt(f), TokenOwned::Minus => Token::Minus.fmt(f),
TokenOwned::Percent => Token::Percent.fmt(f), TokenOwned::Percent => Token::Percent.fmt(f),
TokenOwned::Plus => Token::Plus.fmt(f), TokenOwned::Plus => Token::Plus.fmt(f),
TokenOwned::PlusEqual => Token::PlusEqual.fmt(f),
TokenOwned::ReadLine => Token::ReadLine.fmt(f), TokenOwned::ReadLine => Token::ReadLine.fmt(f),
TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.fmt(f), TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f), TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f),

View File

@ -52,28 +52,74 @@ impl Vm {
variables: &mut HashMap<Identifier, Value>, variables: &mut HashMap<Identifier, Value>,
) -> Result<Option<Value>, VmError> { ) -> Result<Option<Value>, VmError> {
match node.inner { match node.inner {
Statement::Assignment {
identifier,
value_node,
} => {
let value_node_position = value_node.position;
let value = if let Some(value) = self.run_node(*value_node, variables)? {
value
} else {
return Err(VmError::ExpectedValue {
position: value_node_position,
});
};
variables.insert(identifier.inner, value);
Ok(None)
}
Statement::BinaryOperation { Statement::BinaryOperation {
left, left,
operator, operator,
right, right,
} => { } => {
let right_position = right.position;
if let BinaryOperator::Assign = operator.inner {
let identifier = if let Statement::Identifier(identifier) = left.inner {
identifier
} else {
return Err(VmError::ExpectedIdentifier {
position: left.position,
});
};
let value = if let Some(value) = self.run_node(*right, variables)? {
value
} else {
return Err(VmError::ExpectedValue {
position: right_position,
});
};
variables.insert(identifier, value.clone());
return Ok(Some(value));
}
if let BinaryOperator::AddAssign = operator.inner {
let identifier = if let Statement::Identifier(identifier) = left.inner {
identifier
} else {
return Err(VmError::ExpectedIdentifier {
position: left.position,
});
};
let right_value = if let Some(value) = self.run_node(*right, variables)? {
value
} else {
return Err(VmError::ExpectedValue {
position: right_position,
});
};
let left_value =
variables
.get(&identifier)
.ok_or_else(|| VmError::UndefinedVariable {
identifier: Node::new(
Statement::Identifier(identifier.clone()),
left.position,
),
})?;
let new_value = left_value.add(&right_value).map_err(|value_error| {
VmError::ValueError {
error: value_error,
position: right_position,
}
})?;
variables.insert(identifier, new_value.clone());
return Ok(Some(new_value));
}
let left_position = left.position; let left_position = left.position;
let left_value = if let Some(value) = self.run_node(*left, variables)? { let left_value = if let Some(value) = self.run_node(*left, variables)? {
value value
@ -83,7 +129,6 @@ impl Vm {
}); });
}; };
let right_position = right.position;
let right_value = if let Some(value) = self.run_node(*right, variables)? { let right_value = if let Some(value) = self.run_node(*right, variables)? {
value value
} else { } else {
@ -92,7 +137,7 @@ impl Vm {
}); });
}; };
let result = match operator.inner { match operator.inner {
BinaryOperator::Add => left_value.add(&right_value), BinaryOperator::Add => left_value.add(&right_value),
BinaryOperator::And => left_value.and(&right_value), BinaryOperator::And => left_value.and(&right_value),
BinaryOperator::Divide => left_value.divide(&right_value), BinaryOperator::Divide => left_value.divide(&right_value),
@ -107,13 +152,13 @@ impl Vm {
BinaryOperator::Multiply => left_value.multiply(&right_value), BinaryOperator::Multiply => left_value.multiply(&right_value),
BinaryOperator::Or => left_value.or(&right_value), BinaryOperator::Or => left_value.or(&right_value),
BinaryOperator::Subtract => left_value.subtract(&right_value), BinaryOperator::Subtract => left_value.subtract(&right_value),
_ => unreachable!(),
} }
.map(Some)
.map_err(|value_error| VmError::ValueError { .map_err(|value_error| VmError::ValueError {
error: value_error, error: value_error,
position: node.position, position: node.position,
})?; })
Ok(Some(result))
} }
Statement::Block(statements) => { Statement::Block(statements) => {
let mut previous_value = None; let mut previous_value = None;
@ -206,9 +251,8 @@ impl Vm {
if let Some(value) = variables.get(&identifier) { if let Some(value) = variables.get(&identifier) {
Ok(Some(value.clone())) Ok(Some(value.clone()))
} else { } else {
Err(VmError::UndefinedIdentifier { Err(VmError::UndefinedVariable {
identifier, identifier: Node::new(Statement::Identifier(identifier), node.position),
position: node.position,
}) })
} }
} }
@ -231,6 +275,13 @@ impl Vm {
let mut values = BTreeMap::new(); let mut values = BTreeMap::new();
for (identifier, value_node) in nodes { for (identifier, value_node) in nodes {
let identifier = if let Statement::Identifier(identifier) = identifier.inner {
identifier
} else {
return Err(VmError::ExpectedIdentifier {
position: identifier.position,
});
};
let position = value_node.position; let position = value_node.position;
let value = if let Some(value) = self.run_node(value_node, variables)? { let value = if let Some(value) = self.run_node(value_node, variables)? {
value value
@ -238,7 +289,7 @@ impl Vm {
return Err(VmError::ExpectedValue { position }); return Err(VmError::ExpectedValue { position });
}; };
values.insert(identifier.inner, value); values.insert(identifier, value);
} }
Ok(Some(Value::map(values))) Ok(Some(Value::map(values)))
@ -347,9 +398,8 @@ pub enum VmError {
ExpectedValue { ExpectedValue {
position: Span, position: Span,
}, },
UndefinedIdentifier { UndefinedVariable {
identifier: Identifier, identifier: Node<Statement>,
position: Span,
}, },
} }
@ -366,7 +416,7 @@ impl VmError {
Self::ExpectedFunction { position, .. } => *position, Self::ExpectedFunction { position, .. } => *position,
Self::ExpectedList { position } => *position, Self::ExpectedList { position } => *position,
Self::ExpectedValue { position } => *position, Self::ExpectedValue { position } => *position,
Self::UndefinedIdentifier { position, .. } => *position, Self::UndefinedVariable { identifier } => identifier.position,
} }
} }
} }
@ -430,15 +480,8 @@ impl Display for VmError {
Self::ExpectedValue { position } => { Self::ExpectedValue { position } => {
write!(f, "Expected a value at position: {:?}", position) write!(f, "Expected a value at position: {:?}", position)
} }
Self::UndefinedIdentifier { Self::UndefinedVariable { identifier } => {
identifier, write!(f, "Undefined identifier: {}", identifier)
position,
} => {
write!(
f,
"Undefined identifier: {} at position: {:?}",
identifier, position
)
} }
} }
} }
@ -448,6 +491,13 @@ impl Display for VmError {
mod tests { mod tests {
use super::*; use super::*;
#[test]
fn add_assign() {
let input = "x = 1; x += 1; x";
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(2))));
}
#[test] #[test]
fn or() { fn or() {
let input = "true || false"; let input = "true || false";