Begin adding type evaluation to the parser
This commit is contained in:
parent
9294b8f7ed
commit
314913dbf5
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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() {
|
||||||
|
@ -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";
|
||||||
|
Loading…
Reference in New Issue
Block a user