1
0

Begin adding type evaluation to the parser

This commit is contained in:
Jeff 2024-11-05 23:20:58 -05:00
parent 9294b8f7ed
commit 314913dbf5
4 changed files with 135 additions and 57 deletions

View File

@ -153,6 +153,46 @@ impl Chunk {
} }
} }
pub fn get_constant_type(&self, constant_index: u8) -> Option<Type> {
self.constants
.get(constant_index as usize)
.map(|value| value.r#type())
}
pub fn get_local_type(&self, local_index: u8) -> Option<Type> {
self.locals.get(local_index as usize)?.r#type.clone()
}
pub fn get_register_type(&self, register_index: u8) -> Option<Type> {
let local_type_option = self
.locals
.iter()
.find(|local| local.register_index == register_index)
.map(|local| local.r#type.clone());
if let Some(local_type) = local_type_option {
return local_type;
}
self.instructions.iter().find_map(|(instruction, _)| {
if instruction.yields_value() && instruction.a() == register_index {
instruction.yielded_type(self)
} else {
None
}
})
}
pub fn return_type(&self) -> Option<Type> {
self.instructions.iter().rev().find_map(|(instruction, _)| {
if instruction.yields_value() {
instruction.yielded_type(self)
} else {
None
}
})
}
pub fn disassembler(&self) -> ChunkDisassembler { pub fn disassembler(&self) -> ChunkDisassembler {
ChunkDisassembler::new(self) ChunkDisassembler::new(self)
} }
@ -259,8 +299,8 @@ impl<'a> ChunkDisassembler<'a> {
const INSTRUCTION_HEADER: [&'static str; 4] = [ const INSTRUCTION_HEADER: [&'static str; 4] = [
"Instructions", "Instructions",
"------------", "------------",
"INDEX BYTECODE OPERATION INFO POSITION ", "INDEX BYTECODE OPERATION INFO TYPE POSITION ",
"----- -------- --------------- ------------------------- -------------", "----- -------- ------------- ------------------------- --------- -----------",
]; ];
const CONSTANT_HEADER: [&'static str; 4] = const CONSTANT_HEADER: [&'static str; 4] =
@ -427,10 +467,14 @@ impl<'a> ChunkDisassembler<'a> {
self.push_header(&name_display); self.push_header(&name_display);
let info_line = format!( let info_line = format!(
"{} instructions, {} constants, {} locals", "{} instructions, {} constants, {} locals, returns {}",
self.chunk.instructions.len(), self.chunk.instructions.len(),
self.chunk.constants.len(), self.chunk.constants.len(),
self.chunk.locals.len() self.chunk.locals.len(),
self.chunk
.return_type()
.map(|r#type| r#type.to_string())
.unwrap_or("none".to_string())
); );
self.push(&info_line, true, false, false, true); self.push(&info_line, true, false, false, true);
@ -440,12 +484,18 @@ impl<'a> ChunkDisassembler<'a> {
} }
for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() { for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() {
let position = position.to_string();
let operation = instruction.operation().to_string();
let info = instruction.disassembly_info(Some(self.chunk));
let bytecode = u32::from(instruction); let bytecode = u32::from(instruction);
let instruction_display = let operation = instruction.operation().to_string();
format!("{index:<5} {bytecode:<08X} {operation:15} {info:25} {position:13}"); let info = instruction.disassembly_info(self.chunk);
let type_display = instruction
.yielded_type(self.chunk)
.map(|r#type| r#type.to_string())
.unwrap_or(String::with_capacity(0));
let position = position.to_string();
let instruction_display = format!(
"{index:<5} {bytecode:08X} {operation:13} {info:25} {type_display:9} {position:11}"
);
self.push_details(&instruction_display); self.push_details(&instruction_display);
} }

View File

@ -14,7 +14,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{Chunk, NativeFunction, Operation}; use crate::{Chunk, NativeFunction, Operation, Type};
/// An operation and its arguments for the Dust virtual machine. /// An operation and its arguments for the Dust virtual machine.
/// ///
@ -364,8 +364,7 @@ impl Instruction {
} }
pub fn yields_value(&self) -> bool { pub fn yields_value(&self) -> bool {
if matches!( match self.operation() {
self.operation(),
Operation::Add Operation::Add
| Operation::Call | Operation::Call
| Operation::Divide | Operation::Divide
@ -378,21 +377,46 @@ impl Instruction {
| Operation::Multiply | Operation::Multiply
| Operation::Negate | Operation::Negate
| Operation::Not | Operation::Not
| Operation::Subtract | Operation::Subtract => true,
) { Operation::CallNative => {
return true;
}
if matches!(self.operation(), Operation::CallNative) {
let native_function = NativeFunction::from(self.b()); let native_function = NativeFunction::from(self.b());
return native_function.r#type().return_type.is_some(); native_function.r#type().return_type.is_some()
}
_ => false,
}
} }
false pub fn yielded_type(&self, chunk: &Chunk) -> Option<Type> {
use Operation::*;
match self.operation() {
Add | Divide | Modulo | Multiply | Subtract => {
if self.b_is_constant() {
chunk.get_constant_type(self.b())
} else {
chunk.get_register_type(self.b())
}
}
Equal | Less | LessEqual | Test | Not | LoadBoolean => Some(Type::Boolean),
Negate => {
if self.b_is_constant() {
chunk.get_constant_type(self.b())
} else {
chunk.get_register_type(self.b())
}
}
GetLocal => chunk.get_local_type(self.b()),
CallNative => {
let native_function = NativeFunction::from(self.b());
native_function.r#type().return_type.map(|boxed| *boxed)
}
_ => None,
}
} }
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> String { pub fn disassembly_info(&self, chunk: &Chunk) -> String {
let format_arguments = || { let format_arguments = || {
let first_argument = if self.b_is_constant() { let first_argument = if self.b_is_constant() {
format!("C{}", self.b()) format!("C{}", self.b())
@ -448,26 +472,18 @@ impl Instruction {
Operation::LoadSelf => { Operation::LoadSelf => {
let to_register = self.a(); let to_register = self.a();
let name = chunk let name = chunk
.map(|chunk| {
chunk
.name() .name()
.map(|idenifier| idenifier.as_str()) .map(|idenifier| idenifier.as_str())
.unwrap_or("self") .unwrap_or("self");
})
.unwrap();
format!("R{to_register} = {name}") format!("R{to_register} = {name}")
} }
Operation::DefineLocal => { Operation::DefineLocal => {
let to_register = self.a(); let to_register = self.a();
let local_index = self.b(); let local_index = self.b();
let identifier_display = if let Some(chunk) = chunk { let identifier_display = match chunk.get_identifier(local_index) {
match chunk.get_identifier(local_index) {
Some(identifier) => identifier.to_string(), Some(identifier) => identifier.to_string(),
None => "???".to_string(), None => "???".to_string(),
}
} else {
"???".to_string()
}; };
let mutable_display = if self.c_as_boolean() { "mut" } else { "" }; let mutable_display = if self.c_as_boolean() { "mut" } else { "" };
@ -480,13 +496,9 @@ impl Instruction {
} }
Operation::SetLocal => { Operation::SetLocal => {
let local_index = self.b(); let local_index = self.b();
let identifier_display = if let Some(chunk) = chunk { let identifier_display = match chunk.get_identifier(local_index) {
match chunk.get_identifier(local_index) {
Some(identifier) => identifier.to_string(), Some(identifier) => identifier.to_string(),
None => "???".to_string(), None => "???".to_string(),
}
} else {
"???".to_string()
}; };
format!("L{} = R{} {}", local_index, self.a(), identifier_display) format!("L{} = R{} {}", local_index, self.a(), identifier_display)

View File

@ -74,7 +74,7 @@ impl Vm {
self.ip - 1, self.ip - 1,
position, position,
instruction.operation(), instruction.operation(),
instruction.disassembly_info(Some(&self.chunk)) instruction.disassembly_info(&self.chunk)
); );
match instruction.operation() { match instruction.operation() {

View File

@ -64,6 +64,22 @@ fn add_assign_expects_mutable_variable() {
); );
} }
#[test]
fn add_expects_integer_float_or_string() {
let source = "true + false";
assert_eq!(
parse(source),
Err(DustError::Parse {
error: ParseError::ExpectedIntegerFloatOrString {
found: Token::True,
position: Span(0, 3)
},
source
})
);
}
#[test] #[test]
fn divide() { fn divide() {
let source = "2 / 2"; let source = "2 / 2";