1
0
This commit is contained in:
Jeff 2024-10-09 12:16:46 -04:00
parent 259721f6cb
commit b6c3b1e5ba
7 changed files with 304 additions and 170 deletions

View File

@ -3,10 +3,7 @@ use std::fmt::{self, Debug, Display, Formatter};
use colored::Colorize;
use serde::{Deserialize, Serialize};
use crate::{
value::ValueKind, AnnotatedError, Function, Identifier, Instruction, Operation, Span, Type,
Value,
};
use crate::{AnnotatedError, Identifier, Instruction, Operation, Span, Type, Value};
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Chunk {
@ -222,6 +219,14 @@ impl Chunk {
register_index: u8,
position: Span,
) -> Result<u8, ChunkError> {
log::debug!(
"Declaring local: {:?} {:?} {:?} {:?}",
identifier,
r#type,
is_mutable,
register_index
);
let starting_length = self.locals.len();
if starting_length + 1 > (u8::MAX as usize) {

View File

@ -305,6 +305,39 @@ impl Instruction {
self
}
pub fn is_expression(&self) -> bool {
let operation = self.operation();
if operation.is_math() {
return if !self.b_is_constant() {
self.a() != self.b()
} else if !self.c_is_constant() {
self.a() != self.c()
} else {
true
};
}
matches!(
operation,
Operation::Not
| Operation::Negate
| Operation::Equal
| Operation::Less
| Operation::LessEqual
| Operation::Add
| Operation::Subtract
| Operation::Multiply
| Operation::Divide
| Operation::Modulo
| Operation::Test
| Operation::GetLocal
| Operation::LoadBoolean
| Operation::LoadConstant
| Operation::LoadList
)
}
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> (Option<String>, Option<isize>) {
let format_arguments = || {
let first_argument = if self.b_is_constant() {

View File

@ -4,6 +4,8 @@
//! - [`lex`], which lexes the entire input and returns a vector of tokens and their positions
//! - [`Lexer`], which lexes the input a token at a time
use serde::{Deserialize, Serialize};
use crate::{dust_error::AnnotatedError, Span, Token};
/// Lexes the input and return a vector of tokens and their positions.
@ -78,7 +80,7 @@ pub fn lex<'chars, 'src: 'chars>(
/// ]
/// )
/// ```
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Lexer<'src> {
source: &'src str,
position: usize,
@ -543,6 +545,7 @@ impl<'src> Lexer<'src> {
"loop" => Token::Loop,
"map" => Token::Map,
"mut" => Token::Mut,
"return" => Token::Return,
"str" => Token::Str,
"struct" => Token::Struct,
"true" => Token::Boolean("true"),

View File

@ -82,6 +82,17 @@ impl Operation {
| Operation::Modulo
)
}
pub fn is_comparison(&self) -> bool {
matches!(
self,
Operation::Equal | Operation::Less | Operation::LessEqual
)
}
pub fn is_test(&self) -> bool {
matches!(self, Operation::Test | Operation::TestSet)
}
}
impl From<u8> for Operation {

View File

@ -5,6 +5,7 @@ use std::{
};
use colored::Colorize;
use serde::{Deserialize, Serialize};
use crate::{
AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, LexError, Lexer,
@ -17,18 +18,21 @@ pub fn parse(source: &str) -> Result<Chunk, DustError> {
while !parser.is_eof() {
parser
.parse_statement(Allowed {
assignment: true,
explicit_return: false,
implicit_return: true,
})
.parse_statement(
Allowed {
assignment: true,
explicit_return: false,
implicit_return: true,
},
Context::None,
)
.map_err(|error| DustError::Parse { error, source })?;
}
Ok(parser.take_chunk())
}
#[derive(Debug)]
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize)]
pub struct Parser<'src> {
chunk: Chunk,
lexer: Lexer<'src>,
@ -106,7 +110,7 @@ impl<'src> Parser<'src> {
let (new_token, position) = self.lexer.next_token()?;
log::info!(
"{} at {}",
"Parsing {} at {}",
new_token.to_string().bold(),
position.to_string()
);
@ -140,6 +144,12 @@ impl<'src> Parser<'src> {
}
fn emit_instruction(&mut self, instruction: Instruction, position: Span) {
log::debug!(
"Emitting {} at {}",
instruction.operation().to_string().bold(),
position.to_string()
);
self.chunk.push_instruction(instruction, position);
}
@ -266,7 +276,14 @@ impl<'src> Parser<'src> {
fn parse_grouped(&mut self, _: Allowed) -> Result<(), ParseError> {
self.allow(TokenKind::LeftParenthesis)?;
self.parse(Precedence::Assignment)?; // Do not allow assignment
self.parse_statement(
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
Context::None,
)?;
self.expect(TokenKind::RightParenthesis)
}
@ -275,7 +292,14 @@ impl<'src> Parser<'src> {
let operator_position = self.current_position;
self.advance()?;
self.parse(Precedence::Assignment)?; // Do not allow assignment
self.parse_statement(
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
Context::None,
)?;
let (previous_instruction, previous_position) =
self.chunk.pop_instruction(self.current_position)?;
@ -389,7 +413,14 @@ impl<'src> Parser<'src> {
}
self.advance()?;
self.parse(rule.precedence.increment())?;
self.parse(
rule.precedence.increment(),
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
)?;
let (right_instruction, right_position) =
self.chunk.pop_instruction(self.current_position)?;
@ -495,13 +526,29 @@ impl<'src> Parser<'src> {
let operator = self.current_token;
let operator_position = self.current_position;
let rule = ParseRule::from(&operator.kind());
let mut instruction = match self.current_token.kind() {
TokenKind::DoubleEqual => Instruction::equal(true, left.saturating_sub(1), 0),
TokenKind::BangEqual => Instruction::equal(false, left.saturating_sub(1), 0),
TokenKind::Less => Instruction::less(true, left.saturating_sub(1), 0),
TokenKind::LessEqual => Instruction::less_equal(true, left.saturating_sub(1), 0),
TokenKind::Greater => Instruction::less_equal(false, left.saturating_sub(1), 0),
TokenKind::GreaterEqual => Instruction::less(false, left.saturating_sub(1), 0),
self.advance()?;
self.parse(
rule.precedence.increment(),
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
)?;
let (right_instruction, right_position) =
self.chunk.pop_instruction(self.current_position)?;
let (push_back_right, right_is_constant, _, right) =
self.handle_binary_argument(&right_instruction)?;
let mut instruction = match operator {
Token::DoubleEqual => Instruction::equal(true, left.saturating_sub(1), right),
Token::BangEqual => Instruction::equal(false, left.saturating_sub(1), right),
Token::Less => Instruction::less(true, left.saturating_sub(1), right),
Token::LessEqual => Instruction::less_equal(true, left.saturating_sub(1), right),
Token::Greater => Instruction::less_equal(false, left.saturating_sub(1), right),
Token::GreaterEqual => Instruction::less(false, left.saturating_sub(1), right),
_ => {
return Err(ParseError::ExpectedTokenMultiple {
expected: &[
@ -512,22 +559,12 @@ impl<'src> Parser<'src> {
TokenKind::Greater,
TokenKind::GreaterEqual,
],
found: self.current_token.to_owned(),
position: self.current_position,
found: operator.to_owned(),
position: operator_position,
})
}
};
self.advance()?;
self.parse(rule.precedence.increment())?;
let (right_instruction, right_position) =
self.chunk.pop_instruction(self.current_position)?;
let (push_back_right, right_is_constant, _, right) =
self.handle_binary_argument(&right_instruction)?;
instruction.set_c(right);
if left_is_constant {
instruction.set_b_is_constant();
}
@ -572,7 +609,14 @@ impl<'src> Parser<'src> {
self.increment_register()?;
self.advance()?;
self.parse(rule.precedence.increment())?;
self.parse(
rule.precedence.increment(),
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
)?;
let (right_instruction, right_position) =
self.chunk.pop_instruction(self.current_position)?;
@ -593,7 +637,14 @@ impl<'src> Parser<'src> {
let local_index = self.parse_identifier_from(token, start_position)?;
if allowed.assignment && self.allow(TokenKind::Equal)? {
if self.allow(TokenKind::Equal)? {
if !allowed.assignment {
return Err(ParseError::InvalidAssignmentTarget {
found: self.current_token.to_owned(),
position: self.current_position,
});
}
let is_mutable = self
.chunk
.get_local(local_index, start_position)?
@ -606,7 +657,14 @@ impl<'src> Parser<'src> {
});
}
self.parse(Precedence::Assignment)?; // Do not allow assignment
self.parse_statement(
Allowed {
assignment: false,
explicit_return: true,
implicit_return: false,
},
Context::Assignment,
)?;
let (mut previous_instruction, previous_position) =
self.chunk.pop_instruction(self.current_position)?;
@ -618,7 +676,7 @@ impl<'src> Parser<'src> {
.register_index;
if let Some(register_index) = previous_register {
log::trace!("Condensing SET_LOCAL to binary expression");
log::trace!("Condensing SET_LOCAL to binary math expression");
previous_instruction.set_a(register_index);
self.emit_instruction(previous_instruction, self.current_position);
@ -649,7 +707,7 @@ impl<'src> Parser<'src> {
if let Ok(local_index) = self.chunk.get_local_index(&identifier, position) {
Ok(local_index)
} else {
Err(ParseError::UndefinedVariable {
Err(ParseError::UndeclaredVariable {
identifier,
position,
})
@ -687,7 +745,7 @@ impl<'src> Parser<'src> {
self.chunk.begin_scope();
while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() {
self.parse_statement(allowed)?;
self.parse_statement(allowed, Context::None)?;
}
self.chunk.end_scope();
@ -705,7 +763,14 @@ impl<'src> Parser<'src> {
while !self.allow(TokenKind::RightSquareBrace)? && !self.is_eof() {
let next_register = self.current_register;
self.parse(Precedence::Assignment)?; // Do not allow assignment
self.parse_statement(
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
Context::None,
)?;
if let Operation::LoadConstant = self.chunk.get_last_operation()? {
self.increment_register()?;
@ -732,12 +797,21 @@ impl<'src> Parser<'src> {
Ok(())
}
fn parse_if(&mut self, mut allowed: Allowed) -> Result<(), ParseError> {
allowed.implicit_return = false;
fn parse_if(&mut self, allowed: Allowed) -> Result<(), ParseError> {
let length = self.chunk.len();
let expression_allowed = Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
};
let block_allowed = Allowed {
assignment: allowed.assignment,
explicit_return: allowed.explicit_return,
implicit_return: false,
};
self.advance()?;
self.parse(Precedence::Assignment)?; // Do not allow assignment
self.parse_statement(expression_allowed, Context::None)?;
let is_explicit_boolean =
matches!(self.previous_token, Token::Boolean(_)) && length == self.chunk.len() - 1;
@ -750,7 +824,7 @@ impl<'src> Parser<'src> {
}
if let Token::LeftCurlyBrace = self.current_token {
self.parse_block(allowed)?;
self.parse_block(block_allowed)?;
}
let last_operation = self.chunk.get_last_operation()?;
@ -772,21 +846,31 @@ impl<'src> Parser<'src> {
}
if let Token::LeftCurlyBrace = self.current_token {
self.parse_block(allowed)?;
self.parse_block(block_allowed)?;
}
}
Ok(())
}
fn parse_while(&mut self, mut allowed: Allowed) -> Result<(), ParseError> {
fn parse_while(&mut self, allowed: Allowed) -> Result<(), ParseError> {
self.advance()?;
allowed.implicit_return = false;
let jump_start = self.chunk.len();
self.parse(Precedence::Assignment)?; // Do not allow assignment
self.parse_block(allowed)?;
self.parse_statement(
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
Context::None,
)?;
self.parse_block(Allowed {
assignment: true,
explicit_return: allowed.explicit_return,
implicit_return: false,
})?;
let jump_end = self.chunk.len();
let jump_distance = jump_end.abs_diff(jump_start) as u8;
@ -807,8 +891,8 @@ impl<'src> Parser<'src> {
Ok(())
}
fn parse_expression(&mut self) -> Result<(), ParseError> {
self.parse(Precedence::None)?;
fn parse_statement(&mut self, allowed: Allowed, context: Context) -> Result<(), ParseError> {
self.parse(Precedence::None, allowed)?;
if let [Some((jump, _)), Some((comparison, comparison_position))] =
self.chunk.get_last_n_instructions()
@ -816,47 +900,55 @@ impl<'src> Parser<'src> {
if let (Operation::Jump, Operation::Equal | Operation::Less | Operation::LessEqual) =
(jump.operation(), comparison.operation())
{
let comparison_position = *comparison_position;
if matches!(self.current_token, Token::Eof) || context == Context::Assignment {
let comparison_position = *comparison_position;
self.emit_instruction(
Instruction::load_boolean(self.current_register, true, true),
comparison_position,
);
self.emit_instruction(
Instruction::load_boolean(self.current_register, false, false),
comparison_position,
);
self.emit_instruction(
Instruction::load_boolean(self.current_register, true, true),
comparison_position,
);
self.emit_instruction(
Instruction::load_boolean(self.current_register, false, false),
comparison_position,
);
}
}
}
let is_expression = self.chunk.get_last_instruction()?.0.is_expression();
let has_semicolon = self.allow(TokenKind::Semicolon)?;
if !has_semicolon && is_expression && allowed.implicit_return {
self.emit_instruction(Instruction::r#return(), self.current_position);
}
Ok(())
}
fn parse_statement(&mut self, allowed: Allowed) -> Result<(), ParseError> {
match self.current_token {
Token::Let => {
self.parse_let_statement(Allowed {
assignment: true,
fn parse_return(&mut self, allowed: Allowed) -> Result<(), ParseError> {
if !allowed.explicit_return {
return Err(ParseError::UnexpectedReturn {
position: self.current_position,
});
}
self.advance()?;
if !matches!(
self.current_token,
Token::Semicolon | Token::RightCurlyBrace
) {
self.parse_statement(
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
})?;
self.allow(TokenKind::Semicolon)?;
}
Token::LeftCurlyBrace => {
self.parse_block(allowed)?;
self.allow(TokenKind::Semicolon)?;
}
_ => {
self.parse_expression()?;
},
Context::None,
)?;
}
if allowed.implicit_return
&& !self.allow(TokenKind::Semicolon)?
&& (self.is_eof() || matches!(self.current_token, Token::RightCurlyBrace))
{
self.emit_instruction(Instruction::r#return(), self.current_position);
}
}
};
self.emit_instruction(Instruction::r#return(), self.current_position);
Ok(())
}
@ -869,7 +961,7 @@ impl<'src> Parser<'src> {
});
}
self.expect(TokenKind::Let)?;
self.advance()?;
let is_mutable = self.allow(TokenKind::Mut)?;
let position = self.current_position;
@ -881,43 +973,43 @@ impl<'src> Parser<'src> {
return Err(ParseError::ExpectedToken {
expected: TokenKind::Identifier,
found: self.current_token.to_owned(),
position: self.current_position,
position,
});
};
let explicit_type = if self.allow(TokenKind::Colon)? {
Some(self.parse_type_from(self.current_token, self.current_position)?)
let r#type = if self.allow(TokenKind::Colon)? {
let r#type = self.parse_type_from(self.current_token, self.current_position)?;
self.advance()?;
Some(r#type)
} else {
None
};
let register = self.current_register;
self.expect(TokenKind::Equal)?;
self.parse_expression()?;
self.parse_statement(
Allowed {
assignment: false,
explicit_return: true,
implicit_return: false,
},
Context::Assignment,
)?;
if self.current_register == register {
self.increment_register()?;
}
let (previous_instruction, previous_position) = self.chunk.get_last_instruction()?;
let local_index = self
.chunk
.declare_local(identifier, r#type, is_mutable, register, position)?;
if let Operation::LoadConstant | Operation::LoadBoolean | Operation::Add =
previous_instruction.operation()
{
let register = previous_instruction.a();
let local_index = self.chunk.declare_local(
identifier,
explicit_type,
is_mutable,
register,
*previous_position,
)?;
self.emit_instruction(
Instruction::define_local(register, local_index, is_mutable),
position,
);
}
self.emit_instruction(
Instruction::define_local(register, local_index, is_mutable),
position,
);
Ok(())
}
@ -974,11 +1066,14 @@ impl<'src> Parser<'src> {
function_parser.expect(TokenKind::LeftCurlyBrace)?;
while function_parser.current_token != Token::RightCurlyBrace {
function_parser.parse_statement(Allowed {
assignment: true,
explicit_return: true,
implicit_return: true,
})?;
function_parser.parse_statement(
Allowed {
assignment: true,
explicit_return: true,
implicit_return: true,
},
Context::None,
)?;
}
function_parser.advance()?;
@ -997,26 +1092,14 @@ impl<'src> Parser<'src> {
Ok(())
}
fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> {
let allow_assignment = precedence < Precedence::Assignment;
let allow_return = precedence == Precedence::None;
let mut parsed = false;
fn parse(&mut self, precedence: Precedence, allowed: Allowed) -> Result<(), ParseError> {
if let Some(prefix_parser) = ParseRule::from(&self.current_token.kind()).prefix {
log::debug!(
"{} is {precedence} prefix",
self.current_token.to_string().bold(),
);
prefix_parser(
self,
Allowed {
assignment: true,
explicit_return: allow_return,
implicit_return: allow_return,
},
)?;
parsed = true;
prefix_parser(self, allowed)?;
}
let mut infix_rule = ParseRule::from(&self.current_token.kind());
@ -1028,7 +1111,7 @@ impl<'src> Parser<'src> {
self.current_token.to_string().bold(),
);
if allow_assignment && self.current_token == Token::Equal {
if !allowed.assignment && self.current_token == Token::Equal {
return Err(ParseError::InvalidAssignmentTarget {
found: self.current_token.to_owned(),
position: self.current_position,
@ -1041,38 +1124,9 @@ impl<'src> Parser<'src> {
}
infix_rule = ParseRule::from(&self.current_token.kind());
parsed = true;
}
if parsed {
Ok(())
} else {
Err(ParseError::ExpectedTokenMultiple {
expected: &[
// This should list all infix operators and the semicolon token
TokenKind::BangEqual,
TokenKind::DoubleAmpersand,
TokenKind::DoublePipe,
TokenKind::DoubleEqual,
TokenKind::Greater,
TokenKind::GreaterEqual,
TokenKind::Less,
TokenKind::LessEqual,
TokenKind::Minus,
TokenKind::MinusEqual,
TokenKind::Percent,
TokenKind::Plus,
TokenKind::PlusEqual,
TokenKind::Star,
TokenKind::StarEqual,
TokenKind::Semicolon,
TokenKind::Slash,
TokenKind::SlashEqual,
],
found: self.current_token.to_owned(),
position: self.current_position,
})
}
Ok(())
}
}
@ -1117,18 +1171,24 @@ impl Display for Precedence {
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Context {
None,
Assignment,
}
#[derive(Debug, Clone, Copy)]
struct Allowed {
assignment: bool,
explicit_return: bool,
implicit_return: bool,
pub assignment: bool,
pub explicit_return: bool,
pub implicit_return: bool,
}
type PrefixFunction<'a> = fn(&mut Parser<'a>, Allowed) -> Result<(), ParseError>;
type InfixFunction<'a> = fn(&mut Parser<'a>) -> Result<(), ParseError>;
#[derive(Debug, Clone, Copy)]
pub struct ParseRule<'a> {
struct ParseRule<'a> {
pub prefix: Option<PrefixFunction<'a>>,
pub infix: Option<InfixFunction<'a>>,
pub precedence: Precedence,
@ -1302,6 +1362,11 @@ impl From<&TokenKind> for ParseRule<'_> {
infix: Some(Parser::parse_math_binary),
precedence: Precedence::Assignment,
},
TokenKind::Return => ParseRule {
prefix: Some(Parser::parse_return),
infix: None,
precedence: Precedence::None,
},
TokenKind::RightCurlyBrace => ParseRule {
prefix: None,
infix: None,
@ -1386,10 +1451,13 @@ pub enum ParseError {
found: TokenOwned,
position: Span,
},
UndefinedVariable {
UndeclaredVariable {
identifier: Identifier,
position: Span,
},
UnexpectedReturn {
position: Span,
},
RegisterOverflow {
position: Span,
},
@ -1429,7 +1497,8 @@ impl AnnotatedError for ParseError {
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
Self::ExpectedMutableVariable { .. } => "Expected a mutable variable",
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
Self::UndefinedVariable { .. } => "Undefined variable",
Self::UndeclaredVariable { .. } => "Undeclared variable",
Self::UnexpectedReturn { .. } => "Unexpected return",
Self::RegisterOverflow { .. } => "Register overflow",
Self::RegisterUnderflow { .. } => "Register underflow",
Self::ParseFloatError { .. } => "Failed to parse float",
@ -1475,9 +1544,10 @@ impl AnnotatedError for ParseError {
Self::InvalidAssignmentTarget { found, .. } => {
Some(format!("Invalid assignment target, found {found}"))
}
Self::UndefinedVariable { identifier, .. } => {
Some(format!("Undefined variable {identifier}"))
Self::UndeclaredVariable { identifier, .. } => {
Some(format!("Undeclared variable {identifier}"))
}
Self::UnexpectedReturn { .. } => None,
Self::RegisterOverflow { .. } => None,
Self::RegisterUnderflow { .. } => None,
Self::ParseFloatError { error, .. } => Some(error.to_string()),
@ -1495,7 +1565,8 @@ impl AnnotatedError for ParseError {
Self::ExpectedTokenMultiple { position, .. } => *position,
Self::ExpectedMutableVariable { position, .. } => *position,
Self::InvalidAssignmentTarget { position, .. } => *position,
Self::UndefinedVariable { position, .. } => *position,
Self::UndeclaredVariable { position, .. } => *position,
Self::UnexpectedReturn { position } => *position,
Self::RegisterOverflow { position } => *position,
Self::RegisterUnderflow { position } => *position,
Self::ParseFloatError { position, .. } => *position,

View File

@ -1,8 +1,10 @@
//! Token, TokenOwned and TokenKind types.
use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
/// Source code token.
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Token<'src> {
// End of file
Eof,
@ -29,6 +31,7 @@ pub enum Token<'src> {
Loop,
Map,
Mut,
Return,
Str,
Struct,
While,
@ -115,6 +118,7 @@ impl<'src> Token<'src> {
Token::Percent => 1,
Token::Plus => 1,
Token::PlusEqual => 2,
Token::Return => 6,
Token::RightCurlyBrace => 1,
Token::RightParenthesis => 1,
Token::RightSquareBrace => 1,
@ -169,6 +173,7 @@ impl<'src> Token<'src> {
Token::Percent => TokenOwned::Percent,
Token::Plus => TokenOwned::Plus,
Token::PlusEqual => TokenOwned::PlusEqual,
Token::Return => TokenOwned::Return,
Token::RightCurlyBrace => TokenOwned::RightCurlyBrace,
Token::RightParenthesis => TokenOwned::RightParenthesis,
Token::RightSquareBrace => TokenOwned::RightSquareBrace,
@ -227,6 +232,7 @@ impl<'src> Token<'src> {
Token::Percent => TokenKind::Percent,
Token::Plus => TokenKind::Plus,
Token::PlusEqual => TokenKind::PlusEqual,
Token::Return => TokenKind::Return,
Token::RightCurlyBrace => TokenKind::RightCurlyBrace,
Token::RightParenthesis => TokenKind::RightParenthesis,
Token::RightSquareBrace => TokenKind::RightSquareBrace,
@ -287,6 +293,7 @@ impl<'src> Display for Token<'src> {
Token::Percent => write!(f, "%"),
Token::Plus => write!(f, "+"),
Token::PlusEqual => write!(f, "+="),
Token::Return => write!(f, "return"),
Token::RightCurlyBrace => write!(f, "}}"),
Token::RightParenthesis => write!(f, ")"),
Token::RightSquareBrace => write!(f, "]"),
@ -332,6 +339,7 @@ pub enum TokenOwned {
Loop,
Map,
Mut,
Return,
Str,
While,
@ -414,6 +422,7 @@ impl Display for TokenOwned {
TokenOwned::Percent => Token::Percent.fmt(f),
TokenOwned::Plus => Token::Plus.fmt(f),
TokenOwned::PlusEqual => Token::PlusEqual.fmt(f),
TokenOwned::Return => Token::Return.fmt(f),
TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f),
TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f),
@ -457,6 +466,7 @@ pub enum TokenKind {
Let,
Loop,
Map,
Return,
Str,
While,
@ -539,6 +549,7 @@ impl Display for TokenKind {
TokenKind::Percent => Token::Percent.fmt(f),
TokenKind::Plus => Token::Plus.fmt(f),
TokenKind::PlusEqual => Token::PlusEqual.fmt(f),
TokenKind::Return => Token::Return.fmt(f),
TokenKind::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
TokenKind::RightParenthesis => Token::RightParenthesis.fmt(f),
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),

View File

@ -270,7 +270,7 @@ fn equality_assignment_long() {
#[test]
fn equality_assignment_short() {
let source = "let a = 4 == 4; a";
let source = "let a = 4 == 4 a";
assert_eq!(
parse(source),
@ -286,8 +286,8 @@ fn equality_assignment_short() {
(Instruction::load_boolean(0, true, true), Span(10, 12)),
(Instruction::load_boolean(0, false, false), Span(10, 12)),
(Instruction::define_local(0, 0, false), Span(4, 5)),
(Instruction::get_local(1, 0), Span(16, 17)),
(Instruction::r#return(), Span(17, 17)),
(Instruction::get_local(1, 0), Span(15, 16)),
(Instruction::r#return(), Span(16, 16)),
],
vec![Value::integer(4), Value::integer(4)],
vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))]