Add optimizer
This commit is contained in:
parent
e2df823566
commit
c0b998c0d8
@ -7,6 +7,7 @@ pub mod instruction;
|
|||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod native_function;
|
pub mod native_function;
|
||||||
pub mod operation;
|
pub mod operation;
|
||||||
|
pub mod optimizer;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod token;
|
pub mod token;
|
||||||
pub mod r#type;
|
pub mod r#type;
|
||||||
@ -20,6 +21,7 @@ pub use crate::instruction::Instruction;
|
|||||||
pub use crate::lexer::{lex, LexError, Lexer};
|
pub use crate::lexer::{lex, LexError, Lexer};
|
||||||
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
||||||
pub use crate::operation::Operation;
|
pub use crate::operation::Operation;
|
||||||
|
pub use crate::optimizer::{optimize, Optimizer};
|
||||||
pub use crate::parser::{parse, ParseError};
|
pub use crate::parser::{parse, ParseError};
|
||||||
pub use crate::r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
|
pub use crate::r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
|
||||||
pub use crate::token::{Token, TokenKind, TokenOwned};
|
pub use crate::token::{Token, TokenKind, TokenOwned};
|
||||||
|
82
dust-lang/src/optimizer.rs
Normal file
82
dust-lang/src/optimizer.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
use std::{iter::Map, slice::Iter};
|
||||||
|
|
||||||
|
use crate::{Instruction, Operation, Span};
|
||||||
|
|
||||||
|
type MapToOperation = fn(&(Instruction, Span)) -> Operation;
|
||||||
|
|
||||||
|
type OperationIter<'iter> = Map<Iter<'iter, (Instruction, Span)>, MapToOperation>;
|
||||||
|
|
||||||
|
pub fn optimize(instructions: &mut [(Instruction, Span)]) {
|
||||||
|
Optimizer::new(instructions).optimize();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub struct Optimizer<'chunk> {
|
||||||
|
instructions: &'chunk mut [(Instruction, Span)],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'chunk> Optimizer<'chunk> {
|
||||||
|
pub fn new(instructions: &'chunk mut [(Instruction, Span)]) -> Self {
|
||||||
|
Self { instructions }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_instructions(&mut self, instructions: &'chunk mut [(Instruction, Span)]) {
|
||||||
|
self.instructions = instructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optimize(&mut self) {
|
||||||
|
if matches!(
|
||||||
|
self.get_operations(),
|
||||||
|
Some([
|
||||||
|
Operation::Equal | Operation::Less | Operation::LessEqual,
|
||||||
|
Operation::Jump,
|
||||||
|
Operation::LoadBoolean | Operation::LoadConstant,
|
||||||
|
Operation::LoadBoolean | Operation::LoadConstant,
|
||||||
|
])
|
||||||
|
) {
|
||||||
|
self.optimize_comparison();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn optimize_comparison(&mut self) {
|
||||||
|
log::trace!("Optimizing comparison");
|
||||||
|
|
||||||
|
let first_loader_register = {
|
||||||
|
let first_loader = &mut self.instructions[2].0;
|
||||||
|
|
||||||
|
first_loader.set_c_to_boolean(true);
|
||||||
|
first_loader.a()
|
||||||
|
};
|
||||||
|
|
||||||
|
let second_loader = &mut self.instructions[3].0;
|
||||||
|
let mut second_loader_new = Instruction::with_operation(second_loader.operation());
|
||||||
|
|
||||||
|
second_loader_new.set_a(first_loader_register);
|
||||||
|
second_loader_new.set_b(second_loader.b());
|
||||||
|
second_loader_new.set_c(second_loader.c());
|
||||||
|
second_loader_new.set_b_to_boolean(second_loader.b_is_constant());
|
||||||
|
second_loader_new.set_c_to_boolean(second_loader.c_is_constant());
|
||||||
|
|
||||||
|
*second_loader = second_loader_new;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn operations_iter(&self) -> OperationIter {
|
||||||
|
self.instructions
|
||||||
|
.iter()
|
||||||
|
.map(|(instruction, _)| instruction.operation())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_operations<const COUNT: usize>(&self) -> Option<[Operation; COUNT]> {
|
||||||
|
if self.instructions.len() < COUNT {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut n_operations = [Operation::Return; COUNT];
|
||||||
|
|
||||||
|
for (nth, operation) in n_operations.iter_mut().zip(self.operations_iter()) {
|
||||||
|
*nth = operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(n_operations)
|
||||||
|
}
|
||||||
|
}
|
@ -11,10 +11,9 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local,
|
optimize, AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local,
|
||||||
NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type, Value,
|
NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,12 +42,13 @@ pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
|||||||
/// Low-level tool for parsing the input a token at a time while assembling a chunk.
|
/// Low-level tool for parsing the input a token at a time while assembling a chunk.
|
||||||
///
|
///
|
||||||
/// See the [`parse`] function an example of how to create and use a Parser.
|
/// See the [`parse`] function an example of how to create and use a Parser.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize)]
|
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
struct Parser<'src> {
|
struct Parser<'src> {
|
||||||
lexer: Lexer<'src>,
|
|
||||||
chunk: Chunk,
|
chunk: Chunk,
|
||||||
|
lexer: Lexer<'src>,
|
||||||
|
optimization_count: u16,
|
||||||
|
|
||||||
current_is_expression: bool,
|
previous_is_expression: bool,
|
||||||
minimum_register: u8,
|
minimum_register: u8,
|
||||||
|
|
||||||
current_token: Token<'src>,
|
current_token: Token<'src>,
|
||||||
@ -61,6 +61,7 @@ struct Parser<'src> {
|
|||||||
impl<'src> Parser<'src> {
|
impl<'src> Parser<'src> {
|
||||||
pub fn new(mut lexer: Lexer<'src>) -> Result<Self, ParseError> {
|
pub fn new(mut lexer: Lexer<'src>) -> Result<Self, ParseError> {
|
||||||
let (current_token, current_position) = lexer.next_token()?;
|
let (current_token, current_position) = lexer.next_token()?;
|
||||||
|
let chunk = Chunk::new(None);
|
||||||
|
|
||||||
log::info!(
|
log::info!(
|
||||||
"Begin chunk with {} at {}",
|
"Begin chunk with {} at {}",
|
||||||
@ -69,9 +70,10 @@ impl<'src> Parser<'src> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Ok(Parser {
|
Ok(Parser {
|
||||||
|
chunk,
|
||||||
lexer,
|
lexer,
|
||||||
chunk: Chunk::new(None),
|
optimization_count: 0,
|
||||||
current_is_expression: false,
|
previous_is_expression: false,
|
||||||
minimum_register: 0,
|
minimum_register: 0,
|
||||||
current_token,
|
current_token,
|
||||||
current_position,
|
current_position,
|
||||||
@ -217,41 +219,6 @@ impl<'src> Parser<'src> {
|
|||||||
self.chunk.instructions_mut().push((instruction, position));
|
self.chunk.instructions_mut().push((instruction, position));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn optimize_statement(&mut self) {
|
|
||||||
if matches!(
|
|
||||||
self.get_last_instructions(),
|
|
||||||
Some([
|
|
||||||
Operation::LoadBoolean | Operation::LoadConstant,
|
|
||||||
Operation::LoadBoolean | Operation::LoadConstant,
|
|
||||||
Operation::Jump,
|
|
||||||
Operation::Equal | Operation::Less | Operation::LessEqual
|
|
||||||
],)
|
|
||||||
) {
|
|
||||||
log::trace!("Optimizing boolean comparison");
|
|
||||||
|
|
||||||
let mut instructions = self
|
|
||||||
.chunk
|
|
||||||
.instructions_mut()
|
|
||||||
.iter_mut()
|
|
||||||
.rev()
|
|
||||||
.map(|(instruction, _)| instruction);
|
|
||||||
let second_loader = instructions.next().unwrap();
|
|
||||||
let first_loader = instructions.next().unwrap();
|
|
||||||
|
|
||||||
first_loader.set_c_to_boolean(true);
|
|
||||||
|
|
||||||
let mut second_loader_new = Instruction::with_operation(second_loader.operation());
|
|
||||||
|
|
||||||
second_loader_new.set_a(first_loader.a());
|
|
||||||
second_loader_new.set_b(second_loader.b());
|
|
||||||
second_loader_new.set_c(second_loader.c());
|
|
||||||
second_loader_new.set_b_to_boolean(second_loader.b_is_constant());
|
|
||||||
second_loader_new.set_c_to_boolean(second_loader.c_is_constant());
|
|
||||||
|
|
||||||
*second_loader = second_loader_new;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_last_instruction(&mut self) -> Result<(Instruction, Span), ParseError> {
|
fn pop_last_instruction(&mut self) -> Result<(Instruction, Span), ParseError> {
|
||||||
self.chunk
|
self.chunk
|
||||||
.instructions_mut()
|
.instructions_mut()
|
||||||
@ -339,7 +306,7 @@ impl<'src> Parser<'src> {
|
|||||||
position,
|
position,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -363,7 +330,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -385,7 +352,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -413,7 +380,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -441,7 +408,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -463,7 +430,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -480,7 +447,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
self.expect(Token::RightParenthesis)?;
|
self.expect(Token::RightParenthesis)?;
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -531,7 +498,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.emit_instruction(instruction, operator_position);
|
self.emit_instruction(instruction, operator_position);
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -671,9 +638,9 @@ impl<'src> Parser<'src> {
|
|||||||
| Token::SlashEqual
|
| Token::SlashEqual
|
||||||
| Token::PercentEqual = operator
|
| Token::PercentEqual = operator
|
||||||
{
|
{
|
||||||
self.current_is_expression = false;
|
self.previous_is_expression = false;
|
||||||
} else {
|
} else {
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -770,7 +737,7 @@ impl<'src> Parser<'src> {
|
|||||||
operator_position,
|
operator_position,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -803,7 +770,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.emit_instruction(Instruction::jump(jump_distance, true), operator_position);
|
self.emit_instruction(Instruction::jump(jump_distance, true), operator_position);
|
||||||
self.parse_sub_expression(&rule.precedence)?;
|
self.parse_sub_expression(&rule.precedence)?;
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -832,7 +799,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.emit_instruction(Instruction::load_self(register), start_position);
|
self.emit_instruction(Instruction::load_self(register), start_position);
|
||||||
self.declare_local(identifier, None, false, scope, register);
|
self.declare_local(identifier, None, false, scope, register);
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
@ -901,9 +868,8 @@ impl<'src> Parser<'src> {
|
|||||||
Instruction::set_local(register, local_index),
|
Instruction::set_local(register, local_index),
|
||||||
start_position,
|
start_position,
|
||||||
);
|
);
|
||||||
self.optimize_statement();
|
|
||||||
|
|
||||||
self.current_is_expression = false;
|
self.previous_is_expression = false;
|
||||||
} else {
|
} else {
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
|
|
||||||
@ -912,7 +878,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.previous_position,
|
self.previous_position,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -982,7 +948,7 @@ impl<'src> Parser<'src> {
|
|||||||
Span(start, end),
|
Span(start, end),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1047,7 +1013,16 @@ impl<'src> Parser<'src> {
|
|||||||
let if_last_register = self.next_register().saturating_sub(1);
|
let if_last_register = self.next_register().saturating_sub(1);
|
||||||
|
|
||||||
self.parse_else(allowed, block_allowed)?;
|
self.parse_else(allowed, block_allowed)?;
|
||||||
self.optimize_statement();
|
|
||||||
|
if self.chunk.len() >= 4 {
|
||||||
|
let possible_comparison_statement = {
|
||||||
|
let start = self.chunk.len() - 4;
|
||||||
|
|
||||||
|
&mut self.chunk.instructions_mut()[start..]
|
||||||
|
};
|
||||||
|
|
||||||
|
optimize(possible_comparison_statement);
|
||||||
|
}
|
||||||
|
|
||||||
let else_last_register = self.next_register().saturating_sub(1);
|
let else_last_register = self.next_register().saturating_sub(1);
|
||||||
let else_end = self.chunk.len();
|
let else_end = self.chunk.len();
|
||||||
@ -1060,7 +1035,7 @@ impl<'src> Parser<'src> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.current_is_expression = if_block_is_expression && self.current_is_expression;
|
self.previous_is_expression = if_block_is_expression && self.previous_is_expression;
|
||||||
|
|
||||||
if jump_distance == 1 {
|
if jump_distance == 1 {
|
||||||
if let Some(skippable) = self.get_last_jumpable_mut() {
|
if let Some(skippable) = self.get_last_jumpable_mut() {
|
||||||
@ -1084,7 +1059,7 @@ impl<'src> Parser<'src> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.current_is_expression = false;
|
self.previous_is_expression = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1166,9 +1141,8 @@ impl<'src> Parser<'src> {
|
|||||||
let jump_back = Instruction::jump(jump_back_distance, false);
|
let jump_back = Instruction::jump(jump_back_distance, false);
|
||||||
|
|
||||||
self.emit_instruction(jump_back, self.current_position);
|
self.emit_instruction(jump_back, self.current_position);
|
||||||
self.optimize_statement();
|
|
||||||
|
|
||||||
self.current_is_expression = false;
|
self.previous_is_expression = false;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1199,7 +1173,7 @@ impl<'src> Parser<'src> {
|
|||||||
let end = self.previous_position.1;
|
let end = self.previous_position.1;
|
||||||
let to_register = self.next_register();
|
let to_register = self.next_register();
|
||||||
let argument_count = to_register - start_register;
|
let argument_count = to_register - start_register;
|
||||||
self.current_is_expression = function.r#type().return_type.is_some();
|
self.previous_is_expression = function.r#type().return_type.is_some();
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction::call_native(to_register, function, argument_count),
|
Instruction::call_native(to_register, function, argument_count),
|
||||||
@ -1237,7 +1211,7 @@ impl<'src> Parser<'src> {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if !self.current_is_expression || self.chunk.is_empty() {
|
if !self.previous_is_expression || self.chunk.is_empty() {
|
||||||
return Err(ParseError::ExpectedExpression {
|
return Err(ParseError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
position: self.current_position,
|
position: self.current_position,
|
||||||
@ -1281,9 +1255,8 @@ impl<'src> Parser<'src> {
|
|||||||
let end = self.current_position.1;
|
let end = self.current_position.1;
|
||||||
|
|
||||||
self.emit_instruction(Instruction::r#return(has_return_value), Span(start, end));
|
self.emit_instruction(Instruction::r#return(has_return_value), Span(start, end));
|
||||||
self.optimize_statement();
|
|
||||||
|
|
||||||
self.current_is_expression = false;
|
self.previous_is_expression = false;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1295,7 +1268,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.emit_instruction(Instruction::r#return(false), self.current_position);
|
self.emit_instruction(Instruction::r#return(false), self.current_position);
|
||||||
} else {
|
} else {
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction::r#return(self.current_is_expression),
|
Instruction::r#return(self.previous_is_expression),
|
||||||
self.current_position,
|
self.current_position,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1348,9 +1321,8 @@ impl<'src> Parser<'src> {
|
|||||||
Instruction::define_local(register, local_index, is_mutable),
|
Instruction::define_local(register, local_index, is_mutable),
|
||||||
position,
|
position,
|
||||||
);
|
);
|
||||||
self.optimize_statement();
|
|
||||||
|
|
||||||
self.current_is_expression = false;
|
self.previous_is_expression = false;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1469,14 +1441,12 @@ impl<'src> Parser<'src> {
|
|||||||
Instruction::define_local(register, local_index, false),
|
Instruction::define_local(register, local_index, false),
|
||||||
identifier_position,
|
identifier_position,
|
||||||
);
|
);
|
||||||
self.optimize_statement();
|
|
||||||
|
|
||||||
self.current_is_expression = false;
|
self.previous_is_expression = false;
|
||||||
} else {
|
} else {
|
||||||
self.emit_constant(function, Span(function_start, function_end))?;
|
self.emit_constant(function, Span(function_start, function_end))?;
|
||||||
self.optimize_statement();
|
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1530,7 +1500,7 @@ impl<'src> Parser<'src> {
|
|||||||
Span(start, end),
|
Span(start, end),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.previous_is_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1538,7 +1508,7 @@ impl<'src> Parser<'src> {
|
|||||||
fn parse_semicolon(&mut self, _: Allowed) -> Result<(), ParseError> {
|
fn parse_semicolon(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
self.current_is_expression = false;
|
self.previous_is_expression = false;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1629,12 +1599,6 @@ impl Display for Precedence {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum Context {
|
|
||||||
None,
|
|
||||||
Assignment,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct Allowed {
|
struct Allowed {
|
||||||
pub assignment: bool,
|
pub assignment: bool,
|
||||||
|
Loading…
Reference in New Issue
Block a user