Implement subtraction assignment
This commit is contained in:
parent
5c8e72a6f7
commit
64a3ce4cd3
@ -14,6 +14,20 @@ pub struct AbstractSyntaxTree {
|
|||||||
pub nodes: VecDeque<Node<Statement>>,
|
pub nodes: VecDeque<Node<Statement>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AbstractSyntaxTree {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
nodes: VecDeque::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AbstractSyntaxTree {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct Node<T> {
|
pub struct Node<T> {
|
||||||
pub inner: T,
|
pub inner: T,
|
||||||
|
@ -103,7 +103,13 @@ impl Lexer {
|
|||||||
match c {
|
match c {
|
||||||
'0'..='9' => self.lex_number(source)?,
|
'0'..='9' => self.lex_number(source)?,
|
||||||
'-' => {
|
'-' => {
|
||||||
if let Some('0'..='9') = self.peek_second_char(source) {
|
let second_char = self.peek_second_char(source);
|
||||||
|
|
||||||
|
if let Some('=') = second_char {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(Token::MinusEqual, (self.position - 2, self.position))
|
||||||
|
} else if let Some('0'..='9') = second_char {
|
||||||
self.lex_number(source)?
|
self.lex_number(source)?
|
||||||
} else if "-Infinity" == self.peek_chars(source, 9) {
|
} else if "-Infinity" == self.peek_chars(source, 9) {
|
||||||
self.position += 9;
|
self.position += 9;
|
||||||
@ -499,6 +505,33 @@ impl Display for LexError {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn all_keywords() {
|
||||||
|
let input = "bool else false float if int is_even is_odd length read_line struct to_string true while write_line";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lex(input),
|
||||||
|
Ok(vec![
|
||||||
|
(Token::Bool, (0, 4)),
|
||||||
|
(Token::Else, (5, 9)),
|
||||||
|
(Token::Boolean("false"), (10, 15)),
|
||||||
|
(Token::FloatKeyword, (16, 21)),
|
||||||
|
(Token::If, (22, 24)),
|
||||||
|
(Token::Int, (25, 28)),
|
||||||
|
(Token::IsEven, (29, 36)),
|
||||||
|
(Token::IsOdd, (37, 43)),
|
||||||
|
(Token::Length, (44, 50)),
|
||||||
|
(Token::ReadLine, (51, 60)),
|
||||||
|
(Token::Struct, (61, 67)),
|
||||||
|
(Token::ToString, (68, 77)),
|
||||||
|
(Token::Boolean("true"), (78, 82)),
|
||||||
|
(Token::While, (83, 88)),
|
||||||
|
(Token::WriteLine, (89, 99)),
|
||||||
|
(Token::Eof, (99, 99)),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unit_struct() {
|
fn unit_struct() {
|
||||||
let input = "struct Foo";
|
let input = "struct Foo";
|
||||||
|
@ -72,6 +72,31 @@ pub fn parse(source: &str) -> Result<AbstractSyntaxTree, DustError> {
|
|||||||
Ok(AbstractSyntaxTree { nodes })
|
Ok(AbstractSyntaxTree { nodes })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_into<'src>(
|
||||||
|
source: &'src str,
|
||||||
|
tree: &mut AbstractSyntaxTree,
|
||||||
|
) -> Result<(), DustError<'src>> {
|
||||||
|
let lexer = Lexer::new();
|
||||||
|
let mut parser = Parser::new(source, lexer);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let node = parser
|
||||||
|
.parse()
|
||||||
|
.map_err(|parse_error| DustError::ParseError {
|
||||||
|
parse_error,
|
||||||
|
source,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
tree.nodes.push_back(node);
|
||||||
|
|
||||||
|
if let Token::Eof = parser.current.0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Low-level tool for parsing the input a statement at a time.
|
/// Low-level tool for parsing the input a statement at a time.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
@ -133,8 +158,12 @@ impl<'src> Parser<'src> {
|
|||||||
fn parse_statement(&mut self, mut precedence: u8) -> Result<Node<Statement>, ParseError> {
|
fn parse_statement(&mut self, mut precedence: u8) -> Result<Node<Statement>, ParseError> {
|
||||||
// Parse a statement starting from the current node.
|
// Parse a statement starting from the current node.
|
||||||
let mut left = if self.current.0.is_prefix() {
|
let mut left = if self.current.0.is_prefix() {
|
||||||
|
log::trace!("Parsing {} as prefix operator", self.current.0);
|
||||||
|
|
||||||
self.parse_prefix()?
|
self.parse_prefix()?
|
||||||
} else {
|
} else {
|
||||||
|
log::trace!("Parsing {} as primary", self.current.0);
|
||||||
|
|
||||||
self.parse_primary()?
|
self.parse_primary()?
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -142,6 +171,8 @@ impl<'src> Parser<'src> {
|
|||||||
while precedence < self.current.0.precedence() {
|
while precedence < self.current.0.precedence() {
|
||||||
// Give precedence to postfix operations
|
// Give precedence to postfix operations
|
||||||
left = if self.current.0.is_postfix() {
|
left = if self.current.0.is_postfix() {
|
||||||
|
log::trace!("Parsing {} as postfix operator", self.current.0);
|
||||||
|
|
||||||
// Replace the left-hand side with the postfix operation
|
// Replace the left-hand side with the postfix operation
|
||||||
let statement = self.parse_postfix(left)?;
|
let statement = self.parse_postfix(left)?;
|
||||||
|
|
||||||
@ -149,6 +180,8 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
statement
|
statement
|
||||||
} else {
|
} else {
|
||||||
|
log::trace!("Parsing {} as infix operator", self.current.0);
|
||||||
|
|
||||||
// Replace the left-hand side with the infix operation
|
// Replace the left-hand side with the infix operation
|
||||||
self.parse_infix(left)?
|
self.parse_infix(left)?
|
||||||
};
|
};
|
||||||
@ -735,13 +768,13 @@ impl<'src> Parser<'src> {
|
|||||||
let left_start = left.position.0;
|
let left_start = left.position.0;
|
||||||
|
|
||||||
if let Token::Equal | Token::PlusEqual | Token::MinusEqual = &self.current.0 {
|
if let Token::Equal | Token::PlusEqual | Token::MinusEqual = &self.current.0 {
|
||||||
let operator_position = self.current.1;
|
|
||||||
let operator = match self.current.0 {
|
let operator = match self.current.0 {
|
||||||
Token::Equal => AssignmentOperator::Assign,
|
Token::Equal => AssignmentOperator::Assign,
|
||||||
Token::PlusEqual => AssignmentOperator::AddAssign,
|
Token::PlusEqual => AssignmentOperator::AddAssign,
|
||||||
Token::MinusEqual => AssignmentOperator::SubtractAssign,
|
Token::MinusEqual => AssignmentOperator::SubtractAssign,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
let operator_position = self.current.1;
|
||||||
|
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
@ -891,10 +924,10 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let right_end = self.current.1 .1;
|
|
||||||
|
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
|
let right_end = self.current.1 .1;
|
||||||
|
|
||||||
Node::new(
|
Node::new(
|
||||||
Statement::Invokation {
|
Statement::Invokation {
|
||||||
invokee: Box::new(left),
|
invokee: Box::new(left),
|
||||||
@ -1147,6 +1180,47 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tuple_struct_access() {
|
||||||
|
let input = "Foo(42, 'bar').0";
|
||||||
|
let mut tree = AbstractSyntaxTree::new();
|
||||||
|
|
||||||
|
if parse_into(input, &mut tree).is_err() {
|
||||||
|
println!("{:?}", tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(input),
|
||||||
|
Ok(AbstractSyntaxTree {
|
||||||
|
nodes: [Node::new(
|
||||||
|
Statement::BinaryOperation {
|
||||||
|
left: Box::new(Node::new(
|
||||||
|
Statement::Invokation {
|
||||||
|
invokee: Box::new(Node::new(
|
||||||
|
Statement::Identifier(Identifier::new("Foo")),
|
||||||
|
(0, 3)
|
||||||
|
)),
|
||||||
|
type_arguments: None,
|
||||||
|
value_arguments: Some(vec![
|
||||||
|
Node::new(Statement::Constant(Value::integer(42)), (4, 6)),
|
||||||
|
Node::new(Statement::Constant(Value::string("bar")), (8, 11))
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
(0, 12)
|
||||||
|
)),
|
||||||
|
operator: Node::new(BinaryOperator::FieldAccess, (13, 14)),
|
||||||
|
right: Box::new(Node::new(
|
||||||
|
Statement::Constant(Value::integer(0)),
|
||||||
|
(15, 16)
|
||||||
|
))
|
||||||
|
},
|
||||||
|
(0, 16)
|
||||||
|
)]
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fields_struct_instantiation() {
|
fn fields_struct_instantiation() {
|
||||||
let input = "Foo { a = 42, b = 4.0 }";
|
let input = "Foo { a = 42, b = 4.0 }";
|
||||||
|
@ -237,7 +237,7 @@ impl<'src> Token<'src> {
|
|||||||
| Token::GreaterEqual => 5,
|
| Token::GreaterEqual => 5,
|
||||||
Token::DoubleAmpersand => 4,
|
Token::DoubleAmpersand => 4,
|
||||||
Token::DoublePipe => 3,
|
Token::DoublePipe => 3,
|
||||||
Token::Equal | Token::PlusEqual => 2,
|
Token::Equal | Token::MinusEqual | Token::PlusEqual => 2,
|
||||||
Token::DoubleDot | Token::Semicolon => 1,
|
Token::DoubleDot | Token::Semicolon => 1,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
@ -507,12 +507,13 @@ impl Display for TokenKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
pub(crate) mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn all_tokens<'src>() -> [Token<'src>; 42] {
|
pub fn all_tokens<'src>() -> [Token<'src>; 46] {
|
||||||
[
|
[
|
||||||
Token::Bang,
|
Token::Bang,
|
||||||
|
Token::Bool,
|
||||||
Token::Boolean("true"),
|
Token::Boolean("true"),
|
||||||
Token::Colon,
|
Token::Colon,
|
||||||
Token::Comma,
|
Token::Comma,
|
||||||
@ -524,12 +525,14 @@ mod tests {
|
|||||||
Token::Else,
|
Token::Else,
|
||||||
Token::Eof,
|
Token::Eof,
|
||||||
Token::Equal,
|
Token::Equal,
|
||||||
Token::Float("42.0"),
|
Token::Float("0.0"),
|
||||||
|
Token::FloatKeyword,
|
||||||
Token::Greater,
|
Token::Greater,
|
||||||
Token::GreaterEqual,
|
Token::GreaterEqual,
|
||||||
Token::Identifier("foobar"),
|
Token::Identifier(""),
|
||||||
Token::If,
|
Token::If,
|
||||||
Token::Integer("42"),
|
Token::Int,
|
||||||
|
Token::Integer("0"),
|
||||||
Token::IsEven,
|
Token::IsEven,
|
||||||
Token::IsOdd,
|
Token::IsOdd,
|
||||||
Token::LeftCurlyBrace,
|
Token::LeftCurlyBrace,
|
||||||
@ -539,6 +542,7 @@ mod tests {
|
|||||||
Token::Less,
|
Token::Less,
|
||||||
Token::LessEqual,
|
Token::LessEqual,
|
||||||
Token::Minus,
|
Token::Minus,
|
||||||
|
Token::MinusEqual,
|
||||||
Token::Percent,
|
Token::Percent,
|
||||||
Token::Plus,
|
Token::Plus,
|
||||||
Token::PlusEqual,
|
Token::PlusEqual,
|
||||||
@ -548,8 +552,8 @@ mod tests {
|
|||||||
Token::RightSquareBrace,
|
Token::RightSquareBrace,
|
||||||
Token::Semicolon,
|
Token::Semicolon,
|
||||||
Token::Star,
|
Token::Star,
|
||||||
Token::Slash,
|
Token::Str,
|
||||||
Token::String("foobar"),
|
Token::String(""),
|
||||||
Token::Struct,
|
Token::Struct,
|
||||||
Token::ToString,
|
Token::ToString,
|
||||||
Token::While,
|
Token::While,
|
||||||
|
@ -147,7 +147,30 @@ impl Vm {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
AssignmentOperator::SubtractAssign => {
|
AssignmentOperator::SubtractAssign => {
|
||||||
todo!()
|
let left_value = if let Some(value) = self.context.get_value(&identifier.inner)
|
||||||
|
{
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Err(VmError::UndefinedVariable { identifier });
|
||||||
|
};
|
||||||
|
let value_position = value.position;
|
||||||
|
let right_value = if let Some(value) = self.run_statement(*value)? {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Err(VmError::ExpectedValue {
|
||||||
|
position: value_position,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
let new_value = left_value.subtract(&right_value).map_err(|value_error| {
|
||||||
|
VmError::ValueError {
|
||||||
|
error: value_error,
|
||||||
|
position: (identifier.position.0, value_position.1),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.context.set_value(identifier.inner, new_value);
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Statement::BinaryOperation {
|
Statement::BinaryOperation {
|
||||||
@ -1032,6 +1055,13 @@ mod tests {
|
|||||||
assert_eq!(run(input), Ok(Some(Value::integer(5))));
|
assert_eq!(run(input), Ok(Some(Value::integer(5))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subtract_assign() {
|
||||||
|
let input = "x = 1; x -= 1; x";
|
||||||
|
|
||||||
|
assert_eq!(run(input), Ok(Some(Value::integer(0))));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_assign() {
|
fn add_assign() {
|
||||||
let input = "x = 1; x += 1; x";
|
let input = "x = 1; x += 1; x";
|
||||||
|
Loading…
Reference in New Issue
Block a user