Add math-assignment operators
This commit is contained in:
parent
d857f42434
commit
437a6bf164
@ -159,9 +159,15 @@ impl<'src> Lexer<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
'*' => {
|
'*' => {
|
||||||
self.position += 1;
|
if let Some('=') = self.peek_second_char() {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
(Token::Star, Span(self.position - 1, self.position))
|
(Token::StarEqual, Span(self.position - 2, self.position))
|
||||||
|
} else {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Star, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
'(' => {
|
'(' => {
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
@ -261,9 +267,15 @@ impl<'src> Lexer<'src> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
'/' => {
|
'/' => {
|
||||||
self.position += 1;
|
if let Some('=') = self.peek_second_char() {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
(Token::Slash, Span(self.position - 1, self.position))
|
(Token::SlashEqual, Span(self.position - 2, self.position))
|
||||||
|
} else {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Slash, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
'%' => {
|
'%' => {
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
|
@ -348,12 +348,12 @@ impl<'src> Parser<'src> {
|
|||||||
) -> Result<(bool, bool, bool, u8), ParseError> {
|
) -> Result<(bool, bool, bool, u8), ParseError> {
|
||||||
let mut push_back = false;
|
let mut push_back = false;
|
||||||
let mut is_constant = false;
|
let mut is_constant = false;
|
||||||
let mut is_local = false;
|
let mut is_mutable_local = false;
|
||||||
let argument = match instruction.operation() {
|
let argument = match instruction.operation() {
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
is_local = true;
|
|
||||||
let local_index = instruction.b();
|
let local_index = instruction.b();
|
||||||
let local = self.chunk.get_local(local_index, self.current_position)?;
|
let local = self.chunk.get_local(local_index, self.current_position)?;
|
||||||
|
is_mutable_local = local.is_mutable;
|
||||||
|
|
||||||
if let Some(index) = local.register_index {
|
if let Some(index) = local.register_index {
|
||||||
index
|
index
|
||||||
@ -380,24 +380,44 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((push_back, is_constant, is_local, argument))
|
Ok((push_back, is_constant, is_mutable_local, argument))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_math_binary(&mut self) -> Result<(), ParseError> {
|
fn parse_math_binary(&mut self) -> Result<(), ParseError> {
|
||||||
let (left_instruction, left_position) =
|
let (left_instruction, left_position) =
|
||||||
self.chunk.pop_instruction(self.current_position)?;
|
self.chunk.pop_instruction(self.current_position)?;
|
||||||
let (push_back_left, left_is_constant, left_is_local, left) =
|
let (push_back_left, left_is_constant, left_is_mutable_local, left) =
|
||||||
self.handle_binary_argument(&left_instruction)?;
|
self.handle_binary_argument(&left_instruction)?;
|
||||||
|
|
||||||
let operator = self.current_token;
|
let operator = self.current_token;
|
||||||
let operator_position = self.current_position;
|
let operator_position = self.current_position;
|
||||||
let rule = ParseRule::from(&operator.kind());
|
let rule = ParseRule::from(&operator.kind());
|
||||||
|
|
||||||
|
if let TokenKind::PlusEqual
|
||||||
|
| TokenKind::MinusEqual
|
||||||
|
| TokenKind::StarEqual
|
||||||
|
| TokenKind::SlashEqual = operator.kind()
|
||||||
|
{
|
||||||
|
if !left_is_mutable_local {
|
||||||
|
return Err(ParseError::ExpectedMutableVariable {
|
||||||
|
found: self.previous_token.to_owned(),
|
||||||
|
position: left_position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.parse(rule.precedence.increment())?;
|
self.parse(rule.precedence.increment())?;
|
||||||
|
|
||||||
let register = if left_is_local {
|
let (right_instruction, right_position) =
|
||||||
|
self.chunk.pop_instruction(self.current_position)?;
|
||||||
|
let (push_back_right, right_is_constant, right_is_mutable_local, right) =
|
||||||
|
self.handle_binary_argument(&right_instruction)?;
|
||||||
|
|
||||||
|
let register = if left_is_mutable_local {
|
||||||
left
|
left
|
||||||
|
} else if right_is_mutable_local {
|
||||||
|
right
|
||||||
} else {
|
} else {
|
||||||
let current = self.current_register;
|
let current = self.current_register;
|
||||||
|
|
||||||
@ -406,18 +426,26 @@ impl<'src> Parser<'src> {
|
|||||||
current
|
current
|
||||||
};
|
};
|
||||||
let mut new_instruction = match operator.kind() {
|
let mut new_instruction = match operator.kind() {
|
||||||
TokenKind::Plus => Instruction::add(register, left, 0),
|
TokenKind::Plus => Instruction::add(register, left, right),
|
||||||
TokenKind::Minus => Instruction::subtract(register, left, 0),
|
TokenKind::PlusEqual => Instruction::add(register, left, right),
|
||||||
TokenKind::Star => Instruction::multiply(register, left, 0),
|
TokenKind::Minus => Instruction::subtract(register, left, right),
|
||||||
TokenKind::Slash => Instruction::divide(register, left, 0),
|
TokenKind::MinusEqual => Instruction::subtract(register, left, right),
|
||||||
TokenKind::Percent => Instruction::modulo(register, left, 0),
|
TokenKind::Star => Instruction::multiply(register, left, right),
|
||||||
|
TokenKind::StarEqual => Instruction::multiply(register, left, right),
|
||||||
|
TokenKind::Slash => Instruction::divide(register, left, right),
|
||||||
|
TokenKind::SlashEqual => Instruction::divide(register, left, right),
|
||||||
|
TokenKind::Percent => Instruction::modulo(register, left, right),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::ExpectedTokenMultiple {
|
return Err(ParseError::ExpectedTokenMultiple {
|
||||||
expected: &[
|
expected: &[
|
||||||
TokenKind::Plus,
|
TokenKind::Plus,
|
||||||
|
TokenKind::PlusEqual,
|
||||||
TokenKind::Minus,
|
TokenKind::Minus,
|
||||||
|
TokenKind::MinusEqual,
|
||||||
TokenKind::Star,
|
TokenKind::Star,
|
||||||
|
TokenKind::StarEqual,
|
||||||
TokenKind::Slash,
|
TokenKind::Slash,
|
||||||
|
TokenKind::SlashEqual,
|
||||||
TokenKind::Percent,
|
TokenKind::Percent,
|
||||||
],
|
],
|
||||||
found: operator.to_owned(),
|
found: operator.to_owned(),
|
||||||
@ -426,13 +454,6 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (right_instruction, right_position) =
|
|
||||||
self.chunk.pop_instruction(self.current_position)?;
|
|
||||||
let (push_back_right, right_is_constant, right_is_local, right) =
|
|
||||||
self.handle_binary_argument(&right_instruction)?;
|
|
||||||
|
|
||||||
new_instruction.set_c(right);
|
|
||||||
|
|
||||||
if left_is_constant {
|
if left_is_constant {
|
||||||
new_instruction.set_b_is_constant();
|
new_instruction.set_b_is_constant();
|
||||||
}
|
}
|
||||||
@ -1122,7 +1143,11 @@ impl From<&TokenKind> for ParseRule<'_> {
|
|||||||
infix: Some(Parser::parse_math_binary),
|
infix: Some(Parser::parse_math_binary),
|
||||||
precedence: Precedence::Term,
|
precedence: Precedence::Term,
|
||||||
},
|
},
|
||||||
TokenKind::MinusEqual => todo!(),
|
TokenKind::MinusEqual => ParseRule {
|
||||||
|
prefix: None,
|
||||||
|
infix: Some(Parser::parse_math_binary),
|
||||||
|
precedence: Precedence::Assignment,
|
||||||
|
},
|
||||||
TokenKind::Mut => ParseRule {
|
TokenKind::Mut => ParseRule {
|
||||||
prefix: None,
|
prefix: None,
|
||||||
infix: None,
|
infix: None,
|
||||||
@ -1138,7 +1163,11 @@ impl From<&TokenKind> for ParseRule<'_> {
|
|||||||
infix: Some(Parser::parse_math_binary),
|
infix: Some(Parser::parse_math_binary),
|
||||||
precedence: Precedence::Term,
|
precedence: Precedence::Term,
|
||||||
},
|
},
|
||||||
TokenKind::PlusEqual => todo!(),
|
TokenKind::PlusEqual => ParseRule {
|
||||||
|
prefix: None,
|
||||||
|
infix: Some(Parser::parse_math_binary),
|
||||||
|
precedence: Precedence::Assignment,
|
||||||
|
},
|
||||||
TokenKind::RightCurlyBrace => ParseRule {
|
TokenKind::RightCurlyBrace => ParseRule {
|
||||||
prefix: None,
|
prefix: None,
|
||||||
infix: None,
|
infix: None,
|
||||||
@ -1164,11 +1193,21 @@ impl From<&TokenKind> for ParseRule<'_> {
|
|||||||
infix: Some(Parser::parse_math_binary),
|
infix: Some(Parser::parse_math_binary),
|
||||||
precedence: Precedence::Factor,
|
precedence: Precedence::Factor,
|
||||||
},
|
},
|
||||||
|
TokenKind::SlashEqual => ParseRule {
|
||||||
|
prefix: None,
|
||||||
|
infix: Some(Parser::parse_math_binary),
|
||||||
|
precedence: Precedence::Assignment,
|
||||||
|
},
|
||||||
TokenKind::Star => ParseRule {
|
TokenKind::Star => ParseRule {
|
||||||
prefix: None,
|
prefix: None,
|
||||||
infix: Some(Parser::parse_math_binary),
|
infix: Some(Parser::parse_math_binary),
|
||||||
precedence: Precedence::Factor,
|
precedence: Precedence::Factor,
|
||||||
},
|
},
|
||||||
|
TokenKind::StarEqual => ParseRule {
|
||||||
|
prefix: None,
|
||||||
|
infix: Some(Parser::parse_math_binary),
|
||||||
|
precedence: Precedence::Assignment,
|
||||||
|
},
|
||||||
TokenKind::Str => todo!(),
|
TokenKind::Str => todo!(),
|
||||||
TokenKind::String => ParseRule {
|
TokenKind::String => ParseRule {
|
||||||
prefix: Some(Parser::parse_string),
|
prefix: Some(Parser::parse_string),
|
||||||
@ -1205,6 +1244,10 @@ pub enum ParseError {
|
|||||||
found: TokenOwned,
|
found: TokenOwned,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
ExpectedMutableVariable {
|
||||||
|
found: TokenOwned,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
InvalidAssignmentTarget {
|
InvalidAssignmentTarget {
|
||||||
found: TokenOwned,
|
found: TokenOwned,
|
||||||
position: Span,
|
position: Span,
|
||||||
@ -1250,6 +1293,7 @@ impl AnnotatedError for ParseError {
|
|||||||
Self::ExpectedExpression { .. } => "Expected an expression",
|
Self::ExpectedExpression { .. } => "Expected an expression",
|
||||||
Self::ExpectedToken { .. } => "Expected a specific token",
|
Self::ExpectedToken { .. } => "Expected a specific token",
|
||||||
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
|
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
|
||||||
|
Self::ExpectedMutableVariable { .. } => "Expected a mutable variable",
|
||||||
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
|
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
|
||||||
Self::UndefinedVariable { .. } => "Undefined variable",
|
Self::UndefinedVariable { .. } => "Undefined variable",
|
||||||
Self::RegisterOverflow { .. } => "Register overflow",
|
Self::RegisterOverflow { .. } => "Register overflow",
|
||||||
@ -1291,6 +1335,9 @@ impl AnnotatedError for ParseError {
|
|||||||
|
|
||||||
Some(details)
|
Some(details)
|
||||||
}
|
}
|
||||||
|
Self::ExpectedMutableVariable { found, .. } => {
|
||||||
|
Some(format!("Expected mutable variable, found \"{found}\""))
|
||||||
|
}
|
||||||
Self::InvalidAssignmentTarget { found, .. } => {
|
Self::InvalidAssignmentTarget { found, .. } => {
|
||||||
Some(format!("Invalid assignment target, found \"{found}\""))
|
Some(format!("Invalid assignment target, found \"{found}\""))
|
||||||
}
|
}
|
||||||
@ -1312,6 +1359,7 @@ impl AnnotatedError for ParseError {
|
|||||||
Self::ExpectedExpression { position, .. } => *position,
|
Self::ExpectedExpression { position, .. } => *position,
|
||||||
Self::ExpectedToken { position, .. } => *position,
|
Self::ExpectedToken { position, .. } => *position,
|
||||||
Self::ExpectedTokenMultiple { position, .. } => *position,
|
Self::ExpectedTokenMultiple { position, .. } => *position,
|
||||||
|
Self::ExpectedMutableVariable { position, .. } => *position,
|
||||||
Self::InvalidAssignmentTarget { position, .. } => *position,
|
Self::InvalidAssignmentTarget { position, .. } => *position,
|
||||||
Self::UndefinedVariable { position, .. } => *position,
|
Self::UndefinedVariable { position, .. } => *position,
|
||||||
Self::RegisterOverflow { position } => *position,
|
Self::RegisterOverflow { position } => *position,
|
||||||
|
@ -60,7 +60,9 @@ pub enum Token<'src> {
|
|||||||
RightSquareBrace,
|
RightSquareBrace,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Slash,
|
Slash,
|
||||||
|
SlashEqual,
|
||||||
Star,
|
Star,
|
||||||
|
StarEqual,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Token<'src> {
|
impl<'src> Token<'src> {
|
||||||
@ -116,7 +118,9 @@ impl<'src> Token<'src> {
|
|||||||
Token::RightSquareBrace => 1,
|
Token::RightSquareBrace => 1,
|
||||||
Token::Semicolon => 1,
|
Token::Semicolon => 1,
|
||||||
Token::Slash => 1,
|
Token::Slash => 1,
|
||||||
|
Token::SlashEqual => 2,
|
||||||
Token::Star => 1,
|
Token::Star => 1,
|
||||||
|
Token::StarEqual => 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +171,9 @@ impl<'src> Token<'src> {
|
|||||||
Token::RightSquareBrace => TokenOwned::RightSquareBrace,
|
Token::RightSquareBrace => TokenOwned::RightSquareBrace,
|
||||||
Token::Semicolon => TokenOwned::Semicolon,
|
Token::Semicolon => TokenOwned::Semicolon,
|
||||||
Token::Star => TokenOwned::Star,
|
Token::Star => TokenOwned::Star,
|
||||||
|
Token::StarEqual => TokenOwned::StarEqual,
|
||||||
Token::Slash => TokenOwned::Slash,
|
Token::Slash => TokenOwned::Slash,
|
||||||
|
Token::SlashEqual => TokenOwned::SlashEqual,
|
||||||
Token::String(text) => TokenOwned::String(text.to_string()),
|
Token::String(text) => TokenOwned::String(text.to_string()),
|
||||||
Token::Str => TokenOwned::Str,
|
Token::Str => TokenOwned::Str,
|
||||||
Token::Struct => TokenOwned::Struct,
|
Token::Struct => TokenOwned::Struct,
|
||||||
@ -222,7 +228,9 @@ impl<'src> Token<'src> {
|
|||||||
Token::RightSquareBrace => TokenKind::RightSquareBrace,
|
Token::RightSquareBrace => TokenKind::RightSquareBrace,
|
||||||
Token::Semicolon => TokenKind::Semicolon,
|
Token::Semicolon => TokenKind::Semicolon,
|
||||||
Token::Star => TokenKind::Star,
|
Token::Star => TokenKind::Star,
|
||||||
|
Token::StarEqual => TokenKind::StarEqual,
|
||||||
Token::Slash => TokenKind::Slash,
|
Token::Slash => TokenKind::Slash,
|
||||||
|
Token::SlashEqual => TokenKind::SlashEqual,
|
||||||
Token::Str => TokenKind::Str,
|
Token::Str => TokenKind::Str,
|
||||||
Token::String(_) => TokenKind::String,
|
Token::String(_) => TokenKind::String,
|
||||||
Token::Struct => TokenKind::Struct,
|
Token::Struct => TokenKind::Struct,
|
||||||
@ -279,7 +287,9 @@ impl<'src> Display for Token<'src> {
|
|||||||
Token::RightSquareBrace => write!(f, "]"),
|
Token::RightSquareBrace => write!(f, "]"),
|
||||||
Token::Semicolon => write!(f, ";"),
|
Token::Semicolon => write!(f, ";"),
|
||||||
Token::Slash => write!(f, "/"),
|
Token::Slash => write!(f, "/"),
|
||||||
|
Token::SlashEqual => write!(f, "/="),
|
||||||
Token::Star => write!(f, "*"),
|
Token::Star => write!(f, "*"),
|
||||||
|
Token::StarEqual => write!(f, "*="),
|
||||||
Token::Str => write!(f, "str"),
|
Token::Str => write!(f, "str"),
|
||||||
Token::String(value) => write!(f, "{value}"),
|
Token::String(value) => write!(f, "{value}"),
|
||||||
Token::Struct => write!(f, "struct"),
|
Token::Struct => write!(f, "struct"),
|
||||||
@ -348,8 +358,10 @@ pub enum TokenOwned {
|
|||||||
RightSquareBrace,
|
RightSquareBrace,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Star,
|
Star,
|
||||||
|
StarEqual,
|
||||||
Struct,
|
Struct,
|
||||||
Slash,
|
Slash,
|
||||||
|
SlashEqual,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for TokenOwned {
|
impl Display for TokenOwned {
|
||||||
@ -400,7 +412,9 @@ impl Display for TokenOwned {
|
|||||||
TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
||||||
TokenOwned::Semicolon => Token::Semicolon.fmt(f),
|
TokenOwned::Semicolon => Token::Semicolon.fmt(f),
|
||||||
TokenOwned::Star => Token::Star.fmt(f),
|
TokenOwned::Star => Token::Star.fmt(f),
|
||||||
|
TokenOwned::StarEqual => Token::StarEqual.fmt(f),
|
||||||
TokenOwned::Slash => Token::Slash.fmt(f),
|
TokenOwned::Slash => Token::Slash.fmt(f),
|
||||||
|
TokenOwned::SlashEqual => Token::SlashEqual.fmt(f),
|
||||||
TokenOwned::Str => Token::Str.fmt(f),
|
TokenOwned::Str => Token::Str.fmt(f),
|
||||||
TokenOwned::String(string) => Token::String(string).fmt(f),
|
TokenOwned::String(string) => Token::String(string).fmt(f),
|
||||||
TokenOwned::Struct => Token::Struct.fmt(f),
|
TokenOwned::Struct => Token::Struct.fmt(f),
|
||||||
@ -467,8 +481,10 @@ pub enum TokenKind {
|
|||||||
RightSquareBrace,
|
RightSquareBrace,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Star,
|
Star,
|
||||||
|
StarEqual,
|
||||||
Struct,
|
Struct,
|
||||||
Slash,
|
Slash,
|
||||||
|
SlashEqual,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for TokenKind {
|
impl Display for TokenKind {
|
||||||
@ -519,8 +535,10 @@ impl Display for TokenKind {
|
|||||||
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
||||||
TokenKind::Semicolon => Token::Semicolon.fmt(f),
|
TokenKind::Semicolon => Token::Semicolon.fmt(f),
|
||||||
TokenKind::Star => Token::Star.fmt(f),
|
TokenKind::Star => Token::Star.fmt(f),
|
||||||
|
TokenKind::StarEqual => Token::StarEqual.fmt(f),
|
||||||
TokenKind::Str => Token::Str.fmt(f),
|
TokenKind::Str => Token::Str.fmt(f),
|
||||||
TokenKind::Slash => Token::Slash.fmt(f),
|
TokenKind::Slash => Token::Slash.fmt(f),
|
||||||
|
TokenKind::SlashEqual => Token::SlashEqual.fmt(f),
|
||||||
TokenKind::String => write!(f, "string value"),
|
TokenKind::String => write!(f, "string value"),
|
||||||
TokenKind::Struct => Token::Struct.fmt(f),
|
TokenKind::Struct => Token::Struct.fmt(f),
|
||||||
TokenKind::While => Token::While.fmt(f),
|
TokenKind::While => Token::While.fmt(f),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use dust_lang::*;
|
use dust_lang::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
let source = "1 + 2";
|
let source = "1 + 2";
|
||||||
@ -23,6 +24,28 @@ fn add() {
|
|||||||
assert_eq!(run(source), Ok(Some(Value::integer(3))));
|
assert_eq!(run(source), Ok(Some(Value::integer(3))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_assign() {
|
||||||
|
let source = "let mut a = 1; a += 2; a";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(source),
|
||||||
|
Ok(Chunk::with_data(
|
||||||
|
vec![
|
||||||
|
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
||||||
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
|
(*Instruction::add(0, 0, 1).set_c_is_constant(), Span(17, 19)),
|
||||||
|
(Instruction::get_local(1, 0), Span(23, 24)),
|
||||||
|
(Instruction::r#return(), Span(24, 24))
|
||||||
|
],
|
||||||
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
|
vec![Local::new(Identifier::new("a"), true, 0, Some(0))]
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(run(source), Ok(Some(Value::integer(3))));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn and() {
|
fn and() {
|
||||||
let source = "true && false";
|
let source = "true && false";
|
||||||
@ -97,6 +120,7 @@ fn block_scope() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn constant() {
|
fn constant() {
|
||||||
let source = "42";
|
let source = "42";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(source),
|
parse(source),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
@ -115,6 +139,7 @@ fn constant() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn define_local() {
|
fn define_local() {
|
||||||
let source = "let x = 42;";
|
let source = "let x = 42;";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(source),
|
parse(source),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
@ -133,6 +158,7 @@ fn define_local() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn divide() {
|
fn divide() {
|
||||||
let source = "2 / 2";
|
let source = "2 / 2";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(source),
|
parse(source),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
@ -153,9 +179,35 @@ fn divide() {
|
|||||||
assert_eq!(run(source), Ok(Some(Value::integer(1))));
|
assert_eq!(run(source), Ok(Some(Value::integer(1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn divide_assign() {
|
||||||
|
let source = "let mut a = 2; a /= 2; a";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(source),
|
||||||
|
Ok(Chunk::with_data(
|
||||||
|
vec![
|
||||||
|
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
||||||
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
|
(
|
||||||
|
*Instruction::divide(0, 0, 1).set_c_is_constant(),
|
||||||
|
Span(17, 19)
|
||||||
|
),
|
||||||
|
(Instruction::get_local(1, 0), Span(23, 24)),
|
||||||
|
(Instruction::r#return(), Span(24, 24))
|
||||||
|
],
|
||||||
|
vec![Value::integer(2), Value::integer(2)],
|
||||||
|
vec![Local::new(Identifier::new("a"), true, 0, Some(0))]
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(run(source), Ok(Some(Value::integer(1))));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty() {
|
fn empty() {
|
||||||
let source = "";
|
let source = "";
|
||||||
|
|
||||||
assert_eq!(parse(source), Ok(Chunk::with_data(vec![], vec![], vec![])),);
|
assert_eq!(parse(source), Ok(Chunk::with_data(vec![], vec![], vec![])),);
|
||||||
assert_eq!(run(source), Ok(None));
|
assert_eq!(run(source), Ok(None));
|
||||||
}
|
}
|
||||||
@ -554,6 +606,7 @@ fn list_with_simple_expression() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn math_operator_precedence() {
|
fn math_operator_precedence() {
|
||||||
let source = "1 + 2 - 3 * 4 / 5";
|
let source = "1 + 2 - 3 * 4 / 5";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(source),
|
parse(source),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
@ -594,6 +647,7 @@ fn math_operator_precedence() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn multiply() {
|
fn multiply() {
|
||||||
let source = "1 * 2";
|
let source = "1 * 2";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(source),
|
parse(source),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
@ -614,6 +668,31 @@ fn multiply() {
|
|||||||
assert_eq!(run(source), Ok(Some(Value::integer(2))));
|
assert_eq!(run(source), Ok(Some(Value::integer(2))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiply_assign() {
|
||||||
|
let source = "let mut a = 2; a *= 3 a";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(source),
|
||||||
|
Ok(Chunk::with_data(
|
||||||
|
vec![
|
||||||
|
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
||||||
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
|
(
|
||||||
|
*Instruction::multiply(0, 0, 1).set_c_is_constant(),
|
||||||
|
Span(17, 19)
|
||||||
|
),
|
||||||
|
(Instruction::get_local(1, 0), Span(22, 23)),
|
||||||
|
(Instruction::r#return(), Span(23, 23))
|
||||||
|
],
|
||||||
|
vec![Value::integer(2), Value::integer(3)],
|
||||||
|
vec![Local::new(Identifier::new("a"), true, 0, Some(0)),]
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(run(source), Ok(Some(Value::integer(6))));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn negate() {
|
fn negate() {
|
||||||
let source = "-(42)";
|
let source = "-(42)";
|
||||||
@ -683,6 +762,7 @@ fn not_equal() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn or() {
|
fn or() {
|
||||||
let source = "true || false";
|
let source = "true || false";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(source),
|
parse(source),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
@ -704,6 +784,7 @@ fn or() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parentheses_precedence() {
|
fn parentheses_precedence() {
|
||||||
let source = "(1 + 2) * 3";
|
let source = "(1 + 2) * 3";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(source),
|
parse(source),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
@ -731,6 +812,7 @@ fn parentheses_precedence() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn set_local() {
|
fn set_local() {
|
||||||
let source = "let mut x = 41; x = 42; x";
|
let source = "let mut x = 41; x = 42; x";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(source),
|
parse(source),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
@ -753,6 +835,7 @@ fn set_local() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn subtract() {
|
fn subtract() {
|
||||||
let source = "1 - 2";
|
let source = "1 - 2";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(source),
|
parse(source),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
@ -773,9 +856,35 @@ fn subtract() {
|
|||||||
assert_eq!(run(source), Ok(Some(Value::integer(-1))));
|
assert_eq!(run(source), Ok(Some(Value::integer(-1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subtract_assign() {
|
||||||
|
let source = "let mut x = 42; x -= 2; x";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(source),
|
||||||
|
Ok(Chunk::with_data(
|
||||||
|
vec![
|
||||||
|
(Instruction::load_constant(0, 0, false), Span(12, 14)),
|
||||||
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
|
(
|
||||||
|
*Instruction::subtract(0, 0, 1).set_c_is_constant(),
|
||||||
|
Span(18, 20)
|
||||||
|
),
|
||||||
|
(Instruction::get_local(1, 0), Span(24, 25)),
|
||||||
|
(Instruction::r#return(), Span(25, 25)),
|
||||||
|
],
|
||||||
|
vec![Value::integer(42), Value::integer(2)],
|
||||||
|
vec![Local::new(Identifier::new("x"), true, 0, Some(0)),]
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(run(source), Ok(Some(Value::integer(40))));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn variable_and() {
|
fn variable_and() {
|
||||||
let source = "let a = true; let b = false; a && b";
|
let source = "let a = true; let b = false; a && b";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(source),
|
parse(source),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
|
65
dust-lang/tests/parse_errors.rs
Normal file
65
dust-lang/tests/parse_errors.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
use dust_lang::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_assign_expects_variable() {
|
||||||
|
let source = "1 += 2";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(source),
|
||||||
|
Err(DustError::Parse {
|
||||||
|
error: ParseError::ExpectedMutableVariable {
|
||||||
|
found: Token::Integer("1").to_owned(),
|
||||||
|
position: Span(0, 1)
|
||||||
|
},
|
||||||
|
source
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn divide_assign_expects_variable() {
|
||||||
|
let source = "1 -= 2";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(source),
|
||||||
|
Err(DustError::Parse {
|
||||||
|
error: ParseError::ExpectedMutableVariable {
|
||||||
|
found: Token::Integer("1").to_owned(),
|
||||||
|
position: Span(0, 1)
|
||||||
|
},
|
||||||
|
source
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiply_assign_expects_variable() {
|
||||||
|
let source = "1 *= 2";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(source),
|
||||||
|
Err(DustError::Parse {
|
||||||
|
error: ParseError::ExpectedMutableVariable {
|
||||||
|
found: Token::Integer("1").to_owned(),
|
||||||
|
position: Span(0, 1)
|
||||||
|
},
|
||||||
|
source
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subtract_assign_expects_variable() {
|
||||||
|
let source = "1 -= 2";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(source),
|
||||||
|
Err(DustError::Parse {
|
||||||
|
error: ParseError::ExpectedMutableVariable {
|
||||||
|
found: Token::Integer("1").to_owned(),
|
||||||
|
position: Span(0, 1)
|
||||||
|
},
|
||||||
|
source
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user