Implement subtraction assignment

This commit is contained in:
Jeff 2024-08-13 21:24:56 -04:00
parent 5c8e72a6f7
commit 64a3ce4cd3
5 changed files with 168 additions and 13 deletions

View File

@ -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,

View File

@ -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";

View File

@ -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 }";

View File

@ -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,

View File

@ -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";