Implement subtraction assignment
This commit is contained in:
parent
5c8e72a6f7
commit
64a3ce4cd3
@ -14,6 +14,20 @@ pub struct AbstractSyntaxTree {
|
||||
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)]
|
||||
pub struct Node<T> {
|
||||
pub inner: T,
|
||||
|
@ -103,7 +103,13 @@ impl Lexer {
|
||||
match c {
|
||||
'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)?
|
||||
} else if "-Infinity" == self.peek_chars(source, 9) {
|
||||
self.position += 9;
|
||||
@ -499,6 +505,33 @@ impl Display for LexError {
|
||||
mod tests {
|
||||
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]
|
||||
fn unit_struct() {
|
||||
let input = "struct Foo";
|
||||
|
@ -72,6 +72,31 @@ pub fn parse(source: &str) -> Result<AbstractSyntaxTree, DustError> {
|
||||
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.
|
||||
///
|
||||
/// # Examples
|
||||
@ -133,8 +158,12 @@ impl<'src> Parser<'src> {
|
||||
fn parse_statement(&mut self, mut precedence: u8) -> Result<Node<Statement>, ParseError> {
|
||||
// Parse a statement starting from the current node.
|
||||
let mut left = if self.current.0.is_prefix() {
|
||||
log::trace!("Parsing {} as prefix operator", self.current.0);
|
||||
|
||||
self.parse_prefix()?
|
||||
} else {
|
||||
log::trace!("Parsing {} as primary", self.current.0);
|
||||
|
||||
self.parse_primary()?
|
||||
};
|
||||
|
||||
@ -142,6 +171,8 @@ impl<'src> Parser<'src> {
|
||||
while precedence < self.current.0.precedence() {
|
||||
// Give precedence to postfix operations
|
||||
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
|
||||
let statement = self.parse_postfix(left)?;
|
||||
|
||||
@ -149,6 +180,8 @@ impl<'src> Parser<'src> {
|
||||
|
||||
statement
|
||||
} else {
|
||||
log::trace!("Parsing {} as infix operator", self.current.0);
|
||||
|
||||
// Replace the left-hand side with the infix operation
|
||||
self.parse_infix(left)?
|
||||
};
|
||||
@ -735,13 +768,13 @@ impl<'src> Parser<'src> {
|
||||
let left_start = left.position.0;
|
||||
|
||||
if let Token::Equal | Token::PlusEqual | Token::MinusEqual = &self.current.0 {
|
||||
let operator_position = self.current.1;
|
||||
let operator = match self.current.0 {
|
||||
Token::Equal => AssignmentOperator::Assign,
|
||||
Token::PlusEqual => AssignmentOperator::AddAssign,
|
||||
Token::MinusEqual => AssignmentOperator::SubtractAssign,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let operator_position = self.current.1;
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
@ -891,10 +924,10 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
let right_end = self.current.1 .1;
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
let right_end = self.current.1 .1;
|
||||
|
||||
Node::new(
|
||||
Statement::Invokation {
|
||||
invokee: Box::new(left),
|
||||
@ -1147,6 +1180,47 @@ mod tests {
|
||||
|
||||
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]
|
||||
fn fields_struct_instantiation() {
|
||||
let input = "Foo { a = 42, b = 4.0 }";
|
||||
|
@ -237,7 +237,7 @@ impl<'src> Token<'src> {
|
||||
| Token::GreaterEqual => 5,
|
||||
Token::DoubleAmpersand => 4,
|
||||
Token::DoublePipe => 3,
|
||||
Token::Equal | Token::PlusEqual => 2,
|
||||
Token::Equal | Token::MinusEqual | Token::PlusEqual => 2,
|
||||
Token::DoubleDot | Token::Semicolon => 1,
|
||||
_ => 0,
|
||||
}
|
||||
@ -507,12 +507,13 @@ impl Display for TokenKind {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
||||
fn all_tokens<'src>() -> [Token<'src>; 42] {
|
||||
pub fn all_tokens<'src>() -> [Token<'src>; 46] {
|
||||
[
|
||||
Token::Bang,
|
||||
Token::Bool,
|
||||
Token::Boolean("true"),
|
||||
Token::Colon,
|
||||
Token::Comma,
|
||||
@ -524,12 +525,14 @@ mod tests {
|
||||
Token::Else,
|
||||
Token::Eof,
|
||||
Token::Equal,
|
||||
Token::Float("42.0"),
|
||||
Token::Float("0.0"),
|
||||
Token::FloatKeyword,
|
||||
Token::Greater,
|
||||
Token::GreaterEqual,
|
||||
Token::Identifier("foobar"),
|
||||
Token::Identifier(""),
|
||||
Token::If,
|
||||
Token::Integer("42"),
|
||||
Token::Int,
|
||||
Token::Integer("0"),
|
||||
Token::IsEven,
|
||||
Token::IsOdd,
|
||||
Token::LeftCurlyBrace,
|
||||
@ -539,6 +542,7 @@ mod tests {
|
||||
Token::Less,
|
||||
Token::LessEqual,
|
||||
Token::Minus,
|
||||
Token::MinusEqual,
|
||||
Token::Percent,
|
||||
Token::Plus,
|
||||
Token::PlusEqual,
|
||||
@ -548,8 +552,8 @@ mod tests {
|
||||
Token::RightSquareBrace,
|
||||
Token::Semicolon,
|
||||
Token::Star,
|
||||
Token::Slash,
|
||||
Token::String("foobar"),
|
||||
Token::Str,
|
||||
Token::String(""),
|
||||
Token::Struct,
|
||||
Token::ToString,
|
||||
Token::While,
|
||||
|
@ -147,7 +147,30 @@ impl Vm {
|
||||
Ok(None)
|
||||
}
|
||||
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 {
|
||||
@ -1032,6 +1055,13 @@ mod tests {
|
||||
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]
|
||||
fn add_assign() {
|
||||
let input = "x = 1; x += 1; x";
|
||||
|
Loading…
Reference in New Issue
Block a user