Fix some aspects of the compiler for logic and semicolons
This commit is contained in:
parent
72365cd399
commit
d7289414f4
@ -27,7 +27,7 @@ use std::io::Write;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::vm::{Record, Register, RunAction};
|
use crate::vm::{Record, RunAction};
|
||||||
use crate::{DustString, Function, FunctionType, Instruction, Span, Value};
|
use crate::{DustString, Function, FunctionType, Instruction, Span, Value};
|
||||||
|
|
||||||
/// Representation of a Dust program or function.
|
/// Representation of a Dust program or function.
|
||||||
|
@ -7,6 +7,17 @@
|
|||||||
//! - [`Compiler`] is created with a [`Lexer`] and protentially emits a [`CompileError`] or
|
//! - [`Compiler`] is created with a [`Lexer`] and protentially emits a [`CompileError`] or
|
||||||
//! [`LexError`] if the input is invalid. Allows passing a name for the main chunk when
|
//! [`LexError`] if the input is invalid. Allows passing a name for the main chunk when
|
||||||
//! [`Compiler::finish`] is called.
|
//! [`Compiler::finish`] is called.
|
||||||
|
//!
|
||||||
|
//! # Errors
|
||||||
|
//!
|
||||||
|
//! The compiler can return errors due to:
|
||||||
|
//! - Lexing errors
|
||||||
|
//! - Parsing errors
|
||||||
|
//! - Type conflicts
|
||||||
|
//!
|
||||||
|
//! It is a logic error to call [`Compiler::finish`] on a compiler that has emitted an error and
|
||||||
|
//! pass that chunk to the VM. Otherwise, if the compiler gives no errors and the VM encounters a
|
||||||
|
//! runtime error, it is the compiler's fault and the error should be fixed here.
|
||||||
mod error;
|
mod error;
|
||||||
mod optimize;
|
mod optimize;
|
||||||
|
|
||||||
@ -22,9 +33,7 @@ use optimize::control_flow_register_consolidation;
|
|||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
instruction::{
|
instruction::{CallNative, Close, GetLocal, Jump, LoadList, Negate, Not, Return, SetLocal},
|
||||||
CallNative, Close, GetLocal, Jump, LoadList, Negate, Not, Return, SetLocal, Test,
|
|
||||||
},
|
|
||||||
Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
|
Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
|
||||||
NativeFunction, Operation, Scope, Span, Token, TokenKind, Type, Value,
|
NativeFunction, Operation, Scope, Span, Token, TokenKind, Type, Value,
|
||||||
};
|
};
|
||||||
@ -192,9 +201,9 @@ impl<'src> Compiler<'src> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compiles the source while checking for errors and returning a [`CompileError`] if any are
|
/// Compiles the source (which is in the lexer) while checking for errors and returning a
|
||||||
/// found. After calling this function, check its return value for an error, then call
|
/// [`CompileError`] if any are found. After calling this function, check its return value for
|
||||||
/// [`Compiler::finish`] to get the compiled chunk.
|
/// an error, then call [`Compiler::finish`] to get the compiled chunk.
|
||||||
pub fn compile(&mut self) -> Result<(), CompileError> {
|
pub fn compile(&mut self) -> Result<(), CompileError> {
|
||||||
loop {
|
loop {
|
||||||
self.parse(Precedence::None)?;
|
self.parse(Precedence::None)?;
|
||||||
@ -351,15 +360,6 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_last_instruction(&mut self) -> Result<(Instruction, Type, Span), CompileError> {
|
|
||||||
self.instructions
|
|
||||||
.pop()
|
|
||||||
.ok_or_else(|| CompileError::ExpectedExpression {
|
|
||||||
found: self.previous_token.to_owned(),
|
|
||||||
position: self.previous_position,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_last_operations<const COUNT: usize>(&self) -> Option<[Operation; COUNT]> {
|
fn get_last_operations<const COUNT: usize>(&self) -> Option<[Operation; COUNT]> {
|
||||||
let mut n_operations = [Operation::RETURN; COUNT];
|
let mut n_operations = [Operation::RETURN; COUNT];
|
||||||
|
|
||||||
@ -628,7 +628,7 @@ impl<'src> Compiler<'src> {
|
|||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let (previous_instruction, previous_type, previous_position) =
|
let (previous_instruction, previous_type, previous_position) =
|
||||||
self.pop_last_instruction()?;
|
self.instructions.pop().unwrap();
|
||||||
let (argument, push_back) = self.handle_binary_argument(&previous_instruction)?;
|
let (argument, push_back) = self.handle_binary_argument(&previous_instruction)?;
|
||||||
|
|
||||||
if push_back {
|
if push_back {
|
||||||
@ -759,7 +759,7 @@ impl<'src> Compiler<'src> {
|
|||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.parse_sub_expression(&rule.precedence)?;
|
self.parse_sub_expression(&rule.precedence)?;
|
||||||
|
|
||||||
let (right_instruction, right_type, right_position) = self.pop_last_instruction()?;
|
let (right_instruction, right_type, right_position) = self.instructions.pop().unwrap();
|
||||||
let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?;
|
let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?;
|
||||||
|
|
||||||
match operator {
|
match operator {
|
||||||
@ -948,44 +948,17 @@ impl<'src> Compiler<'src> {
|
|||||||
fn parse_logical_binary(&mut self) -> Result<(), CompileError> {
|
fn parse_logical_binary(&mut self) -> Result<(), CompileError> {
|
||||||
let is_logic_chain = matches!(
|
let is_logic_chain = matches!(
|
||||||
self.get_last_operations(),
|
self.get_last_operations(),
|
||||||
Some([Operation::TEST, Operation::JUMP, _])
|
Some([_, Operation::TEST, Operation::JUMP, _])
|
||||||
);
|
);
|
||||||
|
let jump_over_index = self.instructions.len() - 2;
|
||||||
|
let jump_over = if is_logic_chain {
|
||||||
|
let (instruction, _, _) = self.instructions.remove(jump_over_index);
|
||||||
|
|
||||||
let (left_instruction, left_type, left_position) = self.pop_last_instruction()?;
|
Some(instruction)
|
||||||
let (left, _) = self.handle_binary_argument(&left_instruction)?;
|
} else {
|
||||||
|
None
|
||||||
// if is_logic_chain {
|
};
|
||||||
// let destination = self
|
let operand_register = self.next_register() - 1;
|
||||||
// .instructions
|
|
||||||
// .iter()
|
|
||||||
// .rev()
|
|
||||||
// .nth(2)
|
|
||||||
// .map_or(0, |(instruction, _, _)| instruction.a_field());
|
|
||||||
|
|
||||||
// left_instruction.set_a_field(destination);
|
|
||||||
// }
|
|
||||||
|
|
||||||
if !left_instruction.yields_value() {
|
|
||||||
return Err(CompileError::ExpectedExpression {
|
|
||||||
found: self.previous_token.to_owned(),
|
|
||||||
position: self.previous_position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// self.instructions
|
|
||||||
// .push((left_instruction, left_type.clone(), left_position));
|
|
||||||
|
|
||||||
// let short_circuit_jump_index = self.instructions.len();
|
|
||||||
// let mut short_circuit_jump_distance = if is_logic_chain {
|
|
||||||
// self.instructions.pop().map_or(0, |(jump, _, _)| {
|
|
||||||
// let Jump { offset, .. } = Jump::from(&jump);
|
|
||||||
|
|
||||||
// offset
|
|
||||||
// })
|
|
||||||
// } else {
|
|
||||||
// 1
|
|
||||||
// };
|
|
||||||
|
|
||||||
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);
|
let rule = ParseRule::from(&operator);
|
||||||
@ -1000,7 +973,7 @@ impl<'src> Compiler<'src> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let test = Instruction::test(left, test_boolean);
|
let test = Instruction::test(operand_register, test_boolean);
|
||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.emit_instruction(test, Type::None, operator_position);
|
self.emit_instruction(test, Type::None, operator_position);
|
||||||
@ -1015,9 +988,21 @@ impl<'src> Compiler<'src> {
|
|||||||
self.instructions
|
self.instructions
|
||||||
.insert(jump_index, (jump, Type::None, operator_position));
|
.insert(jump_index, (jump, Type::None, operator_position));
|
||||||
|
|
||||||
let (mut right_instruction, _, _) = self.instructions.last_mut().unwrap();
|
if let Some(jump_over) = jump_over {
|
||||||
|
let is_end_of_context = matches!(self.current_token, Token::RightBrace | Token::Eof);
|
||||||
|
let jump_over_distance =
|
||||||
|
jump_over.b_field() + (self.instructions.len() - jump_over_index) as u8 - {
|
||||||
|
if is_end_of_context {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let jump_over = Instruction::jump(jump_over_distance, true);
|
||||||
|
|
||||||
right_instruction.set_a_field(left_instruction.a_field());
|
self.instructions
|
||||||
|
.insert(jump_over_index, (jump_over, Type::None, operator_position));
|
||||||
|
}
|
||||||
|
|
||||||
// short_circuit_jump_distance += (self.instructions.len() - short_circuit_jump_index) as u8;
|
// short_circuit_jump_distance += (self.instructions.len() - short_circuit_jump_index) as u8;
|
||||||
// let jump = Instruction::jump(short_circuit_jump_distance, true);
|
// let jump = Instruction::jump(short_circuit_jump_distance, true);
|
||||||
@ -1201,20 +1186,8 @@ impl<'src> Compiler<'src> {
|
|||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let (last_instruction, _, _) = self.instructions.last().unwrap();
|
let operand_register = self.next_register() - 1;
|
||||||
let argument = match last_instruction.as_argument() {
|
let test = Instruction::test(operand_register, true);
|
||||||
Some(argument) => argument,
|
|
||||||
None => {
|
|
||||||
return Err(CompileError::ExpectedExpression {
|
|
||||||
found: self.previous_token.to_owned(),
|
|
||||||
position: self.previous_position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let test = Instruction::from(Test {
|
|
||||||
argument,
|
|
||||||
test_value: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.emit_instruction(test, Type::None, self.current_position);
|
self.emit_instruction(test, Type::None, self.current_position);
|
||||||
|
|
||||||
@ -1334,27 +1307,8 @@ impl<'src> Compiler<'src> {
|
|||||||
self.instructions.pop();
|
self.instructions.pop();
|
||||||
self.instructions.pop();
|
self.instructions.pop();
|
||||||
} else {
|
} else {
|
||||||
let (expression_instruction, expression_type, expression_position) =
|
let operand_register = self.next_register() - 1;
|
||||||
self.instructions.last().unwrap();
|
let test = Instruction::test(operand_register, true);
|
||||||
|
|
||||||
if expression_type != &Type::Boolean {
|
|
||||||
return Err(CompileError::ExpectedFunction {
|
|
||||||
found: self.previous_token.to_owned(),
|
|
||||||
actual_type: expression_type.clone(),
|
|
||||||
position: *expression_position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let test_argument = match expression_instruction.as_argument() {
|
|
||||||
Some(argument) => argument,
|
|
||||||
None => {
|
|
||||||
return Err(CompileError::ExpectedExpression {
|
|
||||||
found: self.previous_token.to_owned(),
|
|
||||||
position: *expression_position,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let test = Instruction::test(test_argument, true);
|
|
||||||
|
|
||||||
self.emit_instruction(test, Type::None, self.current_position);
|
self.emit_instruction(test, Type::None, self.current_position);
|
||||||
}
|
}
|
||||||
@ -1427,8 +1381,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
fn parse_semicolon(&mut self) -> Result<(), CompileError> {
|
fn parse_semicolon(&mut self) -> Result<(), CompileError> {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
self.parse(Precedence::None)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expression(&mut self) -> Result<(), CompileError> {
|
fn parse_expression(&mut self) -> Result<(), CompileError> {
|
||||||
|
@ -327,9 +327,9 @@ impl Instruction {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test(argument: Argument, value: bool) -> Instruction {
|
pub fn test(operand_register: u8, value: bool) -> Instruction {
|
||||||
Instruction::from(Test {
|
Instruction::from(Test {
|
||||||
argument,
|
operand_register,
|
||||||
test_value: value,
|
test_value: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -622,12 +622,12 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
Operation::TEST => {
|
Operation::TEST => {
|
||||||
let Test {
|
let Test {
|
||||||
argument,
|
operand_register,
|
||||||
test_value: value,
|
test_value,
|
||||||
} = Test::from(self);
|
} = Test::from(self);
|
||||||
let bang = if value { "" } else { "!" };
|
let bang = if test_value { "" } else { "!" };
|
||||||
|
|
||||||
format!("if {bang}{argument} {{ JUMP +1 }}",)
|
format!("if {bang}R{operand_register} {{ JUMP +1 }}",)
|
||||||
}
|
}
|
||||||
Operation::TEST_SET => {
|
Operation::TEST_SET => {
|
||||||
let TestSet {
|
let TestSet {
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
use crate::{Argument, Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
pub struct Test {
|
pub struct Test {
|
||||||
pub argument: Argument,
|
pub operand_register: u8,
|
||||||
pub test_value: bool,
|
pub test_value: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Instruction> for Test {
|
impl From<&Instruction> for Test {
|
||||||
fn from(instruction: &Instruction) -> Self {
|
fn from(instruction: &Instruction) -> Self {
|
||||||
let argument = instruction.b_as_argument();
|
let operand_register = instruction.b_field();
|
||||||
let test_value = instruction.c_field() != 0;
|
let test_value = instruction.c_field() != 0;
|
||||||
|
|
||||||
Test {
|
Test {
|
||||||
argument,
|
operand_register,
|
||||||
test_value,
|
test_value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -19,10 +19,14 @@ impl From<&Instruction> for Test {
|
|||||||
|
|
||||||
impl From<Test> for Instruction {
|
impl From<Test> for Instruction {
|
||||||
fn from(test: Test) -> Self {
|
fn from(test: Test) -> Self {
|
||||||
let operation = Operation::TEST;
|
Instruction::new(
|
||||||
let (b, b_is_constant) = test.argument.as_index_and_constant_flag();
|
Operation::TEST,
|
||||||
let c = test.test_value as u8;
|
0,
|
||||||
|
test.operand_register,
|
||||||
Instruction::new(operation, 0, b, c, b_is_constant, false, false)
|
test.test_value as u8,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,18 +356,15 @@ pub fn modulo(instruction_data: InstructionData, record: &mut Record) -> ThreadS
|
|||||||
|
|
||||||
pub fn test(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
pub fn test(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
b_field: b,
|
b_field, c_field, ..
|
||||||
b_is_constant,
|
|
||||||
c_field: c,
|
|
||||||
..
|
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let value = record.get_argument(b, b_is_constant);
|
let value = record.open_register(b_field);
|
||||||
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
||||||
*boolean
|
*boolean
|
||||||
} else {
|
} else {
|
||||||
panic!("VM Error: Expected boolean value for TEST operation",);
|
panic!("VM Error: Expected boolean value for TEST operation",);
|
||||||
};
|
};
|
||||||
let test_value = c != 0;
|
let test_value = c_field != 0;
|
||||||
|
|
||||||
if boolean == test_value {
|
if boolean == test_value {
|
||||||
record.ip += 1;
|
record.ip += 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user