Compare commits
5 Commits
12dc457b99
...
95b3ffe8ef
Author | SHA1 | Date | |
---|---|---|---|
95b3ffe8ef | |||
22737f022e | |||
a7292f4719 | |||
9b68ecb614 | |||
a366d22470 |
@ -5,40 +5,40 @@ use crate::{
|
|||||||
TerminatorKind, Type, util::match_types,
|
TerminatorKind, Type, util::match_types,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)]
|
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||||
pub struct ModuleValue(usize);
|
pub struct ModuleValue(pub(crate) usize);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)]
|
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||||
pub struct FunctionValue(ModuleValue, usize);
|
pub struct FunctionValue(pub(crate) ModuleValue, pub(crate) usize);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)]
|
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||||
pub struct BlockValue(FunctionValue, usize);
|
pub struct BlockValue(pub(crate) FunctionValue, pub(crate) usize);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, Copy, PartialEq, Eq)]
|
#[derive(Clone, Hash, Copy, PartialEq, Eq)]
|
||||||
pub struct InstructionValue(pub(crate) BlockValue, usize);
|
pub struct InstructionValue(pub(crate) BlockValue, pub(crate) usize);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ModuleHolder {
|
pub struct ModuleHolder {
|
||||||
pub(crate) value: ModuleValue,
|
pub(crate) value: ModuleValue,
|
||||||
pub(crate) data: ModuleData,
|
pub(crate) data: ModuleData,
|
||||||
pub(crate) functions: Vec<FunctionHolder>,
|
pub(crate) functions: Vec<FunctionHolder>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FunctionHolder {
|
pub struct FunctionHolder {
|
||||||
pub(crate) value: FunctionValue,
|
pub(crate) value: FunctionValue,
|
||||||
pub(crate) data: FunctionData,
|
pub(crate) data: FunctionData,
|
||||||
pub(crate) blocks: Vec<BlockHolder>,
|
pub(crate) blocks: Vec<BlockHolder>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BlockHolder {
|
pub struct BlockHolder {
|
||||||
pub(crate) value: BlockValue,
|
pub(crate) value: BlockValue,
|
||||||
pub(crate) data: BlockData,
|
pub(crate) data: BlockData,
|
||||||
pub(crate) instructions: Vec<InstructionHolder>,
|
pub(crate) instructions: Vec<InstructionHolder>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct InstructionHolder {
|
pub struct InstructionHolder {
|
||||||
pub(crate) value: InstructionValue,
|
pub(crate) value: InstructionValue,
|
||||||
pub(crate) data: InstructionData,
|
pub(crate) data: InstructionData,
|
||||||
@ -190,54 +190,6 @@ impl Builder {
|
|||||||
self.modules.clone()
|
self.modules.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub(crate) fn get_functions(&self, module: ModuleValue) -> Vec<(FunctionValue, FunctionData)> {
|
|
||||||
// unsafe {
|
|
||||||
// self.modules
|
|
||||||
// .borrow()
|
|
||||||
// .get_unchecked(module.0)
|
|
||||||
// .2
|
|
||||||
// .iter()
|
|
||||||
// .map(|h| (h.0, h.1.clone()))
|
|
||||||
// .collect()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub(crate) fn get_blocks(&self, function: FunctionValue) -> Vec<(BlockValue, BlockData)> {
|
|
||||||
// unsafe {
|
|
||||||
// self.modules
|
|
||||||
// .borrow()
|
|
||||||
// .get_unchecked(function.0.0)
|
|
||||||
// .2
|
|
||||||
// .get_unchecked(function.1)
|
|
||||||
// .2
|
|
||||||
// .iter()
|
|
||||||
// .map(|h| (h.0, h.1.clone()))
|
|
||||||
// .collect()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub(crate) fn get_instructions(
|
|
||||||
// &self,
|
|
||||||
// block: BlockValue,
|
|
||||||
// ) -> (
|
|
||||||
// Vec<(InstructionValue, InstructionData)>,
|
|
||||||
// Option<TerminatorKind>,
|
|
||||||
// ) {
|
|
||||||
// unsafe {
|
|
||||||
// let modules = self.modules.borrow();
|
|
||||||
// let block = modules
|
|
||||||
// .get_unchecked(block.0.0.0)
|
|
||||||
// .2
|
|
||||||
// .get_unchecked(block.0.1)
|
|
||||||
// .2
|
|
||||||
// .get_unchecked(block.1);
|
|
||||||
// (
|
|
||||||
// block.2.iter().map(|h| (h.0, h.1.clone())).collect(),
|
|
||||||
// block.1.terminator.clone(),
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn check_instruction(&self, instruction: &InstructionValue) -> Result<(), ()> {
|
pub fn check_instruction(&self, instruction: &InstructionValue) -> Result<(), ()> {
|
||||||
use super::InstructionKind::*;
|
use super::InstructionKind::*;
|
||||||
unsafe {
|
unsafe {
|
||||||
|
106
reid-llvm-lib/src/debug.rs
Normal file
106
reid-llvm-lib/src/debug.rs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use crate::{InstructionData, InstructionKind, IntPredicate, TerminatorKind, builder::*};
|
||||||
|
|
||||||
|
impl Debug for ModuleHolder {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple(&format!("Module({})", self.data.name))
|
||||||
|
.field(&self.value)
|
||||||
|
.field(&self.functions)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for FunctionHolder {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple(&format!(
|
||||||
|
"{}({:?}) -> {:?}",
|
||||||
|
self.data.name, self.data.params, self.data.ret
|
||||||
|
))
|
||||||
|
.field(&self.blocks)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for BlockHolder {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple(&format!("Block({})", self.data.name))
|
||||||
|
.field(&self.value)
|
||||||
|
.field(&self.instructions)
|
||||||
|
.field(&self.data.terminator)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for InstructionHolder {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?} = {:?}", &self.value, &self.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for InstructionData {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.kind.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for ModuleValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "M[{}]", &self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for FunctionValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "F[{}, {}]", &self.0.0, &self.1,)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for BlockValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "B[{}, {}, {}]", &self.0.0.0, &self.0.1, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for InstructionValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"I<{}-{}-{}-{}>",
|
||||||
|
&self.0.0.0.0, &self.0.0.1, &self.0.1, self.1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for InstructionKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Param(nth) => write!(f, "Param({})", &nth),
|
||||||
|
Self::Constant(c) => write!(f, "{:?}", &c),
|
||||||
|
Self::Add(lhs, rhs) => write!(f, "{:?} + {:?}", &lhs, &rhs),
|
||||||
|
Self::Sub(lhs, rhs) => write!(f, "{:?} + {:?}", &lhs, &rhs),
|
||||||
|
Self::Phi(val) => write!(f, "Phi: {:?}", &val),
|
||||||
|
Self::ICmp(cmp, lhs, rhs) => write!(f, "{:?} {:?} {:?}", &lhs, &cmp, &rhs),
|
||||||
|
Self::FunctionCall(fun, params) => write!(f, "{:?}({:?})", &fun, ¶ms),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for IntPredicate {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::LessThan => write!(f, "<"),
|
||||||
|
Self::GreaterThan => write!(f, ">"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for TerminatorKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Ret(val) => write!(f, "Ret {:?}", &val),
|
||||||
|
Self::Branch(val) => write!(f, "Br {:?}", &val),
|
||||||
|
Self::CondBr(cond, b1, b2) => write!(f, "CondBr {:?} ? {:?} : {:?}", &cond, &b1, &b2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue}
|
|||||||
|
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
pub mod compile;
|
pub mod compile;
|
||||||
|
mod debug;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
// pub struct InstructionValue(BlockValue, usize);
|
// pub struct InstructionValue(BlockValue, usize);
|
||||||
@ -130,18 +131,18 @@ impl<'builder> Block<'builder> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
pub struct InstructionData {
|
pub struct InstructionData {
|
||||||
kind: InstructionKind,
|
kind: InstructionKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Hash)]
|
#[derive(Clone, Copy, Hash)]
|
||||||
pub enum IntPredicate {
|
pub enum IntPredicate {
|
||||||
LessThan,
|
LessThan,
|
||||||
GreaterThan,
|
GreaterThan,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
pub enum InstructionKind {
|
pub enum InstructionKind {
|
||||||
Param(usize),
|
Param(usize),
|
||||||
Constant(ConstValue),
|
Constant(ConstValue),
|
||||||
@ -171,7 +172,7 @@ pub enum ConstValue {
|
|||||||
U32(u32),
|
U32(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
pub enum TerminatorKind {
|
pub enum TerminatorKind {
|
||||||
Ret(InstructionValue),
|
Ret(InstructionValue),
|
||||||
Branch(BlockValue),
|
Branch(BlockValue),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Main
|
// Main
|
||||||
fn main() {
|
fn main() -> i32 {
|
||||||
return fibonacci(3);
|
let a = fibonacci(3);
|
||||||
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fibonacci
|
// Fibonacci
|
||||||
|
@ -7,6 +7,7 @@ fn main() {
|
|||||||
|
|
||||||
let fibonacci = FunctionDefinition {
|
let fibonacci = FunctionDefinition {
|
||||||
name: fibonacci_name.clone(),
|
name: fibonacci_name.clone(),
|
||||||
|
return_type: TypeKind::I32,
|
||||||
parameters: vec![(fibonacci_n.clone(), TypeKind::I32)],
|
parameters: vec![(fibonacci_n.clone(), TypeKind::I32)],
|
||||||
kind: FunctionDefinitionKind::Local(
|
kind: FunctionDefinitionKind::Local(
|
||||||
Block {
|
Block {
|
||||||
@ -126,6 +127,7 @@ fn main() {
|
|||||||
|
|
||||||
let main = FunctionDefinition {
|
let main = FunctionDefinition {
|
||||||
name: "main".to_owned(),
|
name: "main".to_owned(),
|
||||||
|
return_type: TypeKind::I32,
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
kind: FunctionDefinitionKind::Local(
|
kind: FunctionDefinitionKind::Local(
|
||||||
Block {
|
Block {
|
||||||
|
@ -12,56 +12,34 @@ pub enum InferredType {
|
|||||||
Static(mir::TypeKind),
|
Static(mir::TypeKind),
|
||||||
OneOf(Vec<InferredType>),
|
OneOf(Vec<InferredType>),
|
||||||
Void,
|
Void,
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InferredType {
|
impl InferredType {
|
||||||
fn collapse(&self, scope: &VirtualScope) -> mir::TypeKind {
|
fn collapse(&self) -> mir::TypeKind {
|
||||||
match self {
|
match self {
|
||||||
InferredType::FromVariable(name) => {
|
InferredType::FromVariable(_) => mir::TypeKind::Vague(mir::VagueType::Unknown),
|
||||||
if let Some(inferred) = scope.get_var(name) {
|
InferredType::FunctionReturn(_) => mir::TypeKind::Vague(mir::VagueType::Unknown),
|
||||||
inferred.collapse(scope)
|
|
||||||
} else {
|
|
||||||
mir::TypeKind::Vague(mir::VagueType::Unknown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InferredType::FunctionReturn(name) => {
|
|
||||||
if let Some(type_kind) = scope.get_return_type(name) {
|
|
||||||
type_kind.clone()
|
|
||||||
} else {
|
|
||||||
mir::TypeKind::Vague(mir::VagueType::Unknown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InferredType::Static(type_kind) => type_kind.clone(),
|
InferredType::Static(type_kind) => type_kind.clone(),
|
||||||
InferredType::OneOf(inferred_types) => {
|
InferredType::OneOf(inferred_types) => {
|
||||||
let list: Vec<mir::TypeKind> =
|
let list: Vec<mir::TypeKind> =
|
||||||
inferred_types.iter().map(|t| t.collapse(scope)).collect();
|
inferred_types.iter().map(|t| t.collapse()).collect();
|
||||||
if let Some(first) = list.first() {
|
if let Some(first) = list.first() {
|
||||||
if list.iter().all(|i| i == first) {
|
if list.iter().all(|i| i == first) {
|
||||||
first.clone().into()
|
first.clone().into()
|
||||||
} else {
|
} else {
|
||||||
// IntoMIRError::ConflictingType(self.get_range())
|
mir::TypeKind::Vague(mir::VagueType::Unknown)
|
||||||
mir::TypeKind::Void
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mir::TypeKind::Void
|
mir::TypeKind::Void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InferredType::Void => mir::TypeKind::Void,
|
InferredType::Void => mir::TypeKind::Void,
|
||||||
|
InferredType::Unknown => mir::TypeKind::Vague(mir::VagueType::Unknown),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VirtualVariable {
|
|
||||||
name: String,
|
|
||||||
inferred: InferredType,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VirtualFunctionSignature {
|
|
||||||
name: String,
|
|
||||||
return_type: mir::TypeKind,
|
|
||||||
parameter_types: Vec<mir::TypeKind>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VirtualStorage<T> {
|
pub struct VirtualStorage<T> {
|
||||||
storage: HashMap<String, Vec<T>>,
|
storage: HashMap<String, Vec<T>>,
|
||||||
}
|
}
|
||||||
@ -88,73 +66,8 @@ impl<T> Default for VirtualStorage<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VirtualScope {
|
|
||||||
variables: VirtualStorage<VirtualVariable>,
|
|
||||||
functions: VirtualStorage<VirtualFunctionSignature>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VirtualScope {
|
|
||||||
pub fn set_var(&mut self, variable: VirtualVariable) {
|
|
||||||
self.variables.set(variable.name.clone(), variable);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_fun(&mut self, function: VirtualFunctionSignature) {
|
|
||||||
self.functions.set(function.name.clone(), function)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_var(&self, name: &String) -> Option<InferredType> {
|
|
||||||
self.variables.get(name).and_then(|v| {
|
|
||||||
if v.len() > 1 {
|
|
||||||
Some(InferredType::OneOf(
|
|
||||||
v.iter().map(|v| v.inferred.clone()).collect(),
|
|
||||||
))
|
|
||||||
} else if let Some(v) = v.first() {
|
|
||||||
Some(v.inferred.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_return_type(&self, name: &String) -> Option<mir::TypeKind> {
|
|
||||||
self.functions.get(name).and_then(|v| {
|
|
||||||
if v.len() > 1 {
|
|
||||||
Some(mir::TypeKind::Vague(mir::VagueType::Unknown))
|
|
||||||
} else if let Some(v) = v.first() {
|
|
||||||
Some(v.return_type.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for VirtualScope {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
variables: Default::default(),
|
|
||||||
functions: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ast::Module {
|
impl ast::Module {
|
||||||
pub fn process(&self) -> mir::Module {
|
pub fn process(&self) -> mir::Module {
|
||||||
let mut scope = VirtualScope::default();
|
|
||||||
|
|
||||||
for stmt in &self.top_level_statements {
|
|
||||||
match stmt {
|
|
||||||
FunctionDefinition(ast::FunctionDefinition(signature, _, _)) => {
|
|
||||||
scope.set_fun(VirtualFunctionSignature {
|
|
||||||
name: signature.name.clone(),
|
|
||||||
return_type: signature.return_type.into(),
|
|
||||||
parameter_types: signature.args.iter().map(|p| p.1.into()).collect(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut imports = Vec::new();
|
let mut imports = Vec::new();
|
||||||
let mut functions = Vec::new();
|
let mut functions = Vec::new();
|
||||||
|
|
||||||
@ -167,13 +80,6 @@ impl ast::Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
FunctionDefinition(ast::FunctionDefinition(signature, block, range)) => {
|
FunctionDefinition(ast::FunctionDefinition(signature, block, range)) => {
|
||||||
for (name, ptype) in &signature.args {
|
|
||||||
scope.set_var(VirtualVariable {
|
|
||||||
name: name.clone(),
|
|
||||||
inferred: InferredType::Static((*ptype).into()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let def = mir::FunctionDefinition {
|
let def = mir::FunctionDefinition {
|
||||||
name: signature.name.clone(),
|
name: signature.name.clone(),
|
||||||
return_type: signature
|
return_type: signature
|
||||||
@ -186,10 +92,7 @@ impl ast::Module {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.map(|p| (p.0, p.1.into()))
|
.map(|p| (p.0, p.1.into()))
|
||||||
.collect(),
|
.collect(),
|
||||||
kind: mir::FunctionDefinitionKind::Local(
|
kind: mir::FunctionDefinitionKind::Local(block.into_mir(), (*range).into()),
|
||||||
block.into_mir(&mut scope),
|
|
||||||
(*range).into(),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
functions.push(def);
|
functions.push(def);
|
||||||
}
|
}
|
||||||
@ -207,41 +110,33 @@ impl ast::Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::Block {
|
impl ast::Block {
|
||||||
pub fn into_mir(&self, scope: &mut VirtualScope) -> mir::Block {
|
pub fn into_mir(&self) -> mir::Block {
|
||||||
let mut mir_statements = Vec::new();
|
let mut mir_statements = Vec::new();
|
||||||
|
|
||||||
for statement in &self.0 {
|
for statement in &self.0 {
|
||||||
let (kind, range) = match statement {
|
let (kind, range) = match statement {
|
||||||
ast::BlockLevelStatement::Let(s_let) => {
|
ast::BlockLevelStatement::Let(s_let) => {
|
||||||
let t = s_let.1.infer_return_type().collapse(scope);
|
let t = s_let.1.infer_return_type().collapse();
|
||||||
let inferred = InferredType::Static(t.clone());
|
let inferred = InferredType::Static(t.clone());
|
||||||
scope.set_var(VirtualVariable {
|
|
||||||
name: s_let.0.clone(),
|
|
||||||
inferred,
|
|
||||||
});
|
|
||||||
|
|
||||||
(
|
(
|
||||||
mir::StmtKind::Let(
|
mir::StmtKind::Let(
|
||||||
mir::VariableReference(t, s_let.0.clone(), s_let.2.into()),
|
mir::VariableReference(t, s_let.0.clone(), s_let.2.into()),
|
||||||
s_let.1.process(scope),
|
s_let.1.process(),
|
||||||
),
|
),
|
||||||
s_let.2,
|
s_let.2,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ast::BlockLevelStatement::Import(_) => todo!(),
|
ast::BlockLevelStatement::Import(_) => todo!(),
|
||||||
ast::BlockLevelStatement::Expression(e) => {
|
ast::BlockLevelStatement::Expression(e) => (StmtKind::Expression(e.process()), e.1),
|
||||||
(StmtKind::Expression(e.process(scope)), e.1)
|
ast::BlockLevelStatement::Return(_, e) => (StmtKind::Expression(e.process()), e.1),
|
||||||
}
|
|
||||||
ast::BlockLevelStatement::Return(_, e) => {
|
|
||||||
(StmtKind::Expression(e.process(scope)), e.1)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mir_statements.push(mir::Statement(kind, range.into()));
|
mir_statements.push(mir::Statement(kind, range.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let return_expression = if let Some(r) = &self.1 {
|
let return_expression = if let Some(r) = &self.1 {
|
||||||
Some((r.0.into(), Box::new(r.1.process(scope))))
|
Some((r.0.into(), Box::new(r.1.process())))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -271,40 +166,32 @@ impl From<ast::ReturnType> for mir::ReturnKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::Expression {
|
impl ast::Expression {
|
||||||
fn process(&self, scope: &mut VirtualScope) -> mir::Expression {
|
fn process(&self) -> mir::Expression {
|
||||||
let kind = match &self.0 {
|
let kind = match &self.0 {
|
||||||
ast::ExpressionKind::VariableName(name) => mir::ExprKind::Variable(VariableReference(
|
ast::ExpressionKind::VariableName(name) => mir::ExprKind::Variable(VariableReference(
|
||||||
if let Some(ty) = scope.get_var(name) {
|
mir::TypeKind::Vague(mir::VagueType::Unknown),
|
||||||
ty.collapse(scope)
|
|
||||||
} else {
|
|
||||||
mir::TypeKind::Vague(mir::VagueType::Unknown)
|
|
||||||
},
|
|
||||||
name.clone(),
|
name.clone(),
|
||||||
self.1.into(),
|
self.1.into(),
|
||||||
)),
|
)),
|
||||||
ast::ExpressionKind::Literal(literal) => mir::ExprKind::Literal(literal.mir()),
|
ast::ExpressionKind::Literal(literal) => mir::ExprKind::Literal(literal.mir()),
|
||||||
ast::ExpressionKind::Binop(binary_operator, lhs, rhs) => mir::ExprKind::BinOp(
|
ast::ExpressionKind::Binop(binary_operator, lhs, rhs) => mir::ExprKind::BinOp(
|
||||||
binary_operator.mir(),
|
binary_operator.mir(),
|
||||||
Box::new(lhs.process(scope)),
|
Box::new(lhs.process()),
|
||||||
Box::new(rhs.process(scope)),
|
Box::new(rhs.process()),
|
||||||
),
|
),
|
||||||
ast::ExpressionKind::FunctionCall(fn_call_expr) => {
|
ast::ExpressionKind::FunctionCall(fn_call_expr) => {
|
||||||
mir::ExprKind::FunctionCall(mir::FunctionCall {
|
mir::ExprKind::FunctionCall(mir::FunctionCall {
|
||||||
name: fn_call_expr.0.clone(),
|
name: fn_call_expr.0.clone(),
|
||||||
return_type: if let Some(r_type) = scope.get_return_type(&fn_call_expr.0) {
|
return_type: mir::TypeKind::Vague(mir::VagueType::Unknown),
|
||||||
r_type
|
parameters: fn_call_expr.1.iter().map(|e| e.process()).collect(),
|
||||||
} else {
|
|
||||||
mir::TypeKind::Vague(mir::VagueType::Unknown)
|
|
||||||
},
|
|
||||||
parameters: fn_call_expr.1.iter().map(|e| e.process(scope)).collect(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ast::ExpressionKind::BlockExpr(block) => mir::ExprKind::Block(block.into_mir(scope)),
|
ast::ExpressionKind::BlockExpr(block) => mir::ExprKind::Block(block.into_mir()),
|
||||||
ast::ExpressionKind::IfExpr(if_expression) => {
|
ast::ExpressionKind::IfExpr(if_expression) => {
|
||||||
let cond = if_expression.0.process(scope);
|
let cond = if_expression.0.process();
|
||||||
let then_block = if_expression.1.into_mir(scope);
|
let then_block = if_expression.1.into_mir();
|
||||||
let else_block = if let Some(el) = &if_expression.2 {
|
let else_block = if let Some(el) = &if_expression.2 {
|
||||||
Some(el.into_mir(scope))
|
Some(el.into_mir())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -371,12 +258,3 @@ impl From<ast::Type> for mir::TypeKind {
|
|||||||
value.0.into()
|
value.0.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Option<ast::Type>> for mir::TypeKind {
|
|
||||||
fn from(value: Option<ast::Type>) -> Self {
|
|
||||||
match value {
|
|
||||||
Some(v) => v.into(),
|
|
||||||
None => mir::TypeKind::Void,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -183,6 +183,7 @@ impl mir::Expression {
|
|||||||
pub fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
pub fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
mir::ExprKind::Variable(varref) => {
|
mir::ExprKind::Variable(varref) => {
|
||||||
|
varref.0.is_known().expect("variable type unknown");
|
||||||
let v = scope
|
let v = scope
|
||||||
.stack_values
|
.stack_values
|
||||||
.get(&varref.1)
|
.get(&varref.1)
|
||||||
@ -191,6 +192,17 @@ impl mir::Expression {
|
|||||||
}
|
}
|
||||||
mir::ExprKind::Literal(lit) => Some(lit.as_const(&mut scope.block)),
|
mir::ExprKind::Literal(lit) => Some(lit.as_const(&mut scope.block)),
|
||||||
mir::ExprKind::BinOp(binop, lhs_exp, rhs_exp) => {
|
mir::ExprKind::BinOp(binop, lhs_exp, rhs_exp) => {
|
||||||
|
lhs_exp
|
||||||
|
.return_type()
|
||||||
|
.expect("No ret type in lhs?")
|
||||||
|
.is_known()
|
||||||
|
.expect("lhs ret type is unknown");
|
||||||
|
rhs_exp
|
||||||
|
.return_type()
|
||||||
|
.expect("No ret type in rhs?")
|
||||||
|
.is_known()
|
||||||
|
.expect("rhs ret type is unknown");
|
||||||
|
|
||||||
let lhs = lhs_exp.codegen(scope).expect("lhs has no return value");
|
let lhs = lhs_exp.codegen(scope).expect("lhs has no return value");
|
||||||
let rhs = rhs_exp.codegen(scope).expect("rhs has no return value");
|
let rhs = rhs_exp.codegen(scope).expect("rhs has no return value");
|
||||||
Some(match binop {
|
Some(match binop {
|
||||||
@ -202,16 +214,17 @@ impl mir::Expression {
|
|||||||
}
|
}
|
||||||
mir::BinaryOperator::Mult => todo!(),
|
mir::BinaryOperator::Mult => todo!(),
|
||||||
mir::BinaryOperator::And => todo!(),
|
mir::BinaryOperator::And => todo!(),
|
||||||
mir::BinaryOperator::Logic(l) => {
|
mir::BinaryOperator::Logic(l) => scope
|
||||||
let ret_type = lhs_exp.return_type().expect("No ret type in lhs?");
|
.block
|
||||||
scope
|
.build(InstructionKind::ICmp(l.int_predicate(), lhs, rhs))
|
||||||
.block
|
.unwrap(),
|
||||||
.build(InstructionKind::ICmp(l.int_predicate(), lhs, rhs))
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
mir::ExprKind::FunctionCall(call) => {
|
mir::ExprKind::FunctionCall(call) => {
|
||||||
|
call.return_type
|
||||||
|
.is_known()
|
||||||
|
.expect("function return type unknown");
|
||||||
|
|
||||||
let params = call
|
let params = call
|
||||||
.parameters
|
.parameters
|
||||||
.iter()
|
.iter()
|
||||||
@ -293,6 +306,7 @@ impl TypeKind {
|
|||||||
match &self {
|
match &self {
|
||||||
TypeKind::I32 => Type::I32,
|
TypeKind::I32 => Type::I32,
|
||||||
TypeKind::I16 => Type::I16,
|
TypeKind::I16 => Type::I16,
|
||||||
|
TypeKind::Bool => Type::Bool,
|
||||||
TypeKind::Void => panic!("Void not a supported type"),
|
TypeKind::Void => panic!("Void not a supported type"),
|
||||||
TypeKind::Vague(_) => panic!("Tried to compile a vague type!"),
|
TypeKind::Vague(_) => panic!("Tried to compile a vague type!"),
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ mod codegen;
|
|||||||
mod lexer;
|
mod lexer;
|
||||||
pub mod mir;
|
pub mod mir;
|
||||||
mod token_stream;
|
mod token_stream;
|
||||||
|
mod util;
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// 1. Make it so that TopLevelStatement can only be import or function def
|
// 1. Make it so that TopLevelStatement can only be import or function def
|
||||||
@ -20,6 +21,8 @@ pub enum ReidError {
|
|||||||
LexerError(#[from] lexer::Error),
|
LexerError(#[from] lexer::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ParserError(#[from] token_stream::Error),
|
ParserError(#[from] token_stream::Error),
|
||||||
|
#[error("Errors during typecheck: {0:?}")]
|
||||||
|
TypeCheckErrors(Vec<mir::typecheck::Error>),
|
||||||
// #[error(transparent)]
|
// #[error(transparent)]
|
||||||
// CodegenError(#[from] codegen::Error),
|
// CodegenError(#[from] codegen::Error),
|
||||||
}
|
}
|
||||||
@ -45,7 +48,15 @@ pub fn compile(source: &str) -> Result<String, ReidError> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
dbg!(&ast_module);
|
dbg!(&ast_module);
|
||||||
let mir_module = ast_module.process();
|
let mut mir_module = ast_module.process();
|
||||||
|
|
||||||
|
dbg!(&mir_module);
|
||||||
|
|
||||||
|
let state = mir_module.typecheck();
|
||||||
|
dbg!(&state);
|
||||||
|
if !state.errors.is_empty() {
|
||||||
|
return Err(ReidError::TypeCheckErrors(state.errors));
|
||||||
|
}
|
||||||
|
|
||||||
dbg!(&mir_module);
|
dbg!(&mir_module);
|
||||||
|
|
||||||
|
@ -3,46 +3,85 @@
|
|||||||
/// type-checked beforehand.
|
/// type-checked beforehand.
|
||||||
use crate::token_stream::TokenRange;
|
use crate::token_stream::TokenRange;
|
||||||
|
|
||||||
|
pub mod typecheck;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
pub range: TokenRange,
|
pub range: TokenRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Metadata {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?}", self.range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Add for Metadata {
|
||||||
|
type Output = Metadata;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
Metadata {
|
||||||
|
range: self.range + rhs.range,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<TokenRange> for Metadata {
|
impl From<TokenRange> for Metadata {
|
||||||
fn from(value: TokenRange) -> Self {
|
fn from(value: TokenRange) -> Self {
|
||||||
Metadata { range: value }
|
Metadata { range: value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Metadata {
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
|
||||||
fn default() -> Self {
|
pub enum TypeKind {
|
||||||
Metadata {
|
#[error("i32")]
|
||||||
range: Default::default(),
|
I32,
|
||||||
|
#[error("i16")]
|
||||||
|
I16,
|
||||||
|
#[error("bool")]
|
||||||
|
Bool,
|
||||||
|
#[error("void")]
|
||||||
|
Void,
|
||||||
|
#[error(transparent)]
|
||||||
|
Vague(#[from] VagueType),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
|
||||||
|
pub enum VagueType {
|
||||||
|
#[error("Unknown")]
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeKind {
|
||||||
|
pub fn is_known(&self) -> Result<TypeKind, VagueType> {
|
||||||
|
if let TypeKind::Vague(vague) = self {
|
||||||
|
Err(*vague)
|
||||||
|
} else {
|
||||||
|
Ok(*self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum TypeKind {
|
|
||||||
I32,
|
|
||||||
I16,
|
|
||||||
Void,
|
|
||||||
Vague(VagueType),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum VagueType {
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeKind {
|
impl TypeKind {
|
||||||
pub fn signed(&self) -> bool {
|
pub fn signed(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
TypeKind::Void => false,
|
||||||
|
TypeKind::Vague(_) => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_maths(&self) -> bool {
|
||||||
|
use TypeKind::*;
|
||||||
|
match &self {
|
||||||
|
I32 => true,
|
||||||
|
I16 => true,
|
||||||
|
Bool => true,
|
||||||
|
Vague(_) => false,
|
||||||
|
Void => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@ -126,6 +165,22 @@ pub enum FunctionDefinitionKind {
|
|||||||
Extern,
|
Extern,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FunctionDefinition {
|
||||||
|
fn block_meta(&self) -> Metadata {
|
||||||
|
match &self.kind {
|
||||||
|
FunctionDefinitionKind::Local(block, _) => block.meta,
|
||||||
|
FunctionDefinitionKind::Extern => Metadata::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Metadata {
|
||||||
|
match &self.kind {
|
||||||
|
FunctionDefinitionKind::Local(_, metadata) => *metadata,
|
||||||
|
FunctionDefinitionKind::Extern => Metadata::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
/// List of non-returning statements
|
/// List of non-returning statements
|
||||||
|
382
reid/src/mir/typecheck.rs
Normal file
382
reid/src/mir/typecheck.rs
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
use std::{collections::HashMap, convert::Infallible, iter};
|
||||||
|
|
||||||
|
/// This module contains code relevant to doing a type checking pass on the MIR.
|
||||||
|
use crate::{mir::*, util::try_all};
|
||||||
|
use TypeKind::*;
|
||||||
|
use VagueType::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Error {
|
||||||
|
metadata: Metadata,
|
||||||
|
kind: ErrorKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Error at {}: {}", self.metadata, self.kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
self.kind.source()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug, Clone)]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
#[error("NULL error, should never occur!")]
|
||||||
|
Null,
|
||||||
|
#[error("Type is vague: {0}")]
|
||||||
|
TypeIsVague(VagueType),
|
||||||
|
#[error("Types {0} and {1} are incompatible")]
|
||||||
|
TypesIncompatible(TypeKind, TypeKind),
|
||||||
|
#[error("Variable not defined: {0}")]
|
||||||
|
VariableNotDefined(String),
|
||||||
|
#[error("Function not defined: {0}")]
|
||||||
|
FunctionNotDefined(String),
|
||||||
|
#[error("Type is vague: {0}")]
|
||||||
|
ReturnTypeMismatch(TypeKind, TypeKind),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TypeStorage<T>(HashMap<String, T>);
|
||||||
|
|
||||||
|
impl<T> Default for TypeStorage<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Collapsable> TypeStorage<T> {
|
||||||
|
fn set(&mut self, key: String, value: T) -> Result<T, ErrorKind> {
|
||||||
|
if let Some(inner) = self.0.get(&key) {
|
||||||
|
match value.collapse_into(inner) {
|
||||||
|
Ok(collapsed) => {
|
||||||
|
self.0.insert(key, collapsed.clone());
|
||||||
|
Ok(collapsed)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.0.insert(key, value.clone());
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, key: &String) -> Option<&T> {
|
||||||
|
self.0.get(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct State {
|
||||||
|
pub errors: Vec<Error>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn new() -> State {
|
||||||
|
State {
|
||||||
|
errors: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn or_else<T: Into<Metadata> + Clone + Copy>(
|
||||||
|
&mut self,
|
||||||
|
result: Result<TypeKind, ErrorKind>,
|
||||||
|
default: TypeKind,
|
||||||
|
meta: T,
|
||||||
|
) -> TypeKind {
|
||||||
|
match result {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(e) => {
|
||||||
|
self.errors.push(Error {
|
||||||
|
metadata: meta.into(),
|
||||||
|
kind: e,
|
||||||
|
});
|
||||||
|
default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ok<T: Into<Metadata> + Clone + Copy, U>(&mut self, result: Result<U, ErrorKind>, meta: T) {
|
||||||
|
if let Err(e) = result {
|
||||||
|
self.errors.push(Error {
|
||||||
|
metadata: meta.into(),
|
||||||
|
kind: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct Scope {
|
||||||
|
function_returns: TypeStorage<ScopeFunction>,
|
||||||
|
variables: TypeStorage<TypeKind>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ScopeFunction {
|
||||||
|
ret: TypeKind,
|
||||||
|
params: Vec<TypeKind>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scope {
|
||||||
|
fn inner(&self) -> Scope {
|
||||||
|
Scope {
|
||||||
|
function_returns: self.function_returns.clone(),
|
||||||
|
variables: self.variables.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum Inferred {
|
||||||
|
Type(TypeKind),
|
||||||
|
Unresolved(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Module {
|
||||||
|
pub fn typecheck(&mut self) -> State {
|
||||||
|
let mut state = State::new();
|
||||||
|
let mut scope = Scope::default();
|
||||||
|
|
||||||
|
for function in &self.functions {
|
||||||
|
state.ok(
|
||||||
|
scope.function_returns.set(
|
||||||
|
function.name.clone(),
|
||||||
|
ScopeFunction {
|
||||||
|
ret: function.return_type,
|
||||||
|
params: function.parameters.iter().map(|v| v.1).collect(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
function.signature(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for function in &mut self.functions {
|
||||||
|
let res = function.typecheck(&mut state, &mut scope);
|
||||||
|
state.ok(res, function.block_meta());
|
||||||
|
}
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionDefinition {
|
||||||
|
fn typecheck(&mut self, state: &mut State, scope: &mut Scope) -> Result<TypeKind, ErrorKind> {
|
||||||
|
for param in &self.parameters {
|
||||||
|
let param_t = state.or_else(param.1.assert_known(), Vague(Unknown), self.signature());
|
||||||
|
state.ok(
|
||||||
|
scope.variables.set(param.0.clone(), param_t),
|
||||||
|
self.signature(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let return_type = self.return_type.clone();
|
||||||
|
let inferred = match &mut self.kind {
|
||||||
|
FunctionDefinitionKind::Local(block, _) => block.typecheck(state, scope),
|
||||||
|
FunctionDefinitionKind::Extern => Ok(Vague(Unknown)),
|
||||||
|
};
|
||||||
|
|
||||||
|
match inferred {
|
||||||
|
Ok(t) => try_collapse(&return_type, &t)
|
||||||
|
.or(Err(ErrorKind::ReturnTypeMismatch(return_type, t))),
|
||||||
|
Err(e) => Ok(state.or_else(Err(e), return_type, self.block_meta())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Block {
|
||||||
|
fn typecheck(&mut self, state: &mut State, scope: &mut Scope) -> Result<TypeKind, ErrorKind> {
|
||||||
|
let mut scope = scope.inner();
|
||||||
|
|
||||||
|
for statement in &mut self.statements {
|
||||||
|
match &mut statement.0 {
|
||||||
|
StmtKind::Let(variable_reference, expression) => {
|
||||||
|
let res = expression.typecheck(state, &mut scope);
|
||||||
|
|
||||||
|
// If expression resolution itself was erronous, resolve as
|
||||||
|
// Unknown.
|
||||||
|
let res = state.or_else(res, Vague(Unknown), expression.1);
|
||||||
|
|
||||||
|
// Make sure the expression and variable type really is the same
|
||||||
|
let res_t = state.or_else(
|
||||||
|
res.collapse_into(&variable_reference.0),
|
||||||
|
Vague(Unknown),
|
||||||
|
variable_reference.2 + expression.1,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Make sure expression/variable type is NOT vague anymore
|
||||||
|
let res_t =
|
||||||
|
state.or_else(res_t.assert_known(), Vague(Unknown), variable_reference.2);
|
||||||
|
|
||||||
|
// Update typing to be more accurate
|
||||||
|
variable_reference.0 = res_t;
|
||||||
|
|
||||||
|
// Variable might already be defined, note error
|
||||||
|
state.ok(
|
||||||
|
scope
|
||||||
|
.variables
|
||||||
|
.set(variable_reference.1.clone(), variable_reference.0),
|
||||||
|
variable_reference.2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
StmtKind::Import(_) => todo!(),
|
||||||
|
StmtKind::Expression(expression) => {
|
||||||
|
let res = expression.typecheck(state, &mut scope);
|
||||||
|
state.ok(res, expression.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((_, expr)) = &mut self.return_expression {
|
||||||
|
let res = expr.typecheck(state, &mut scope);
|
||||||
|
Ok(state.or_else(res, Vague(Unknown), expr.1))
|
||||||
|
} else {
|
||||||
|
Ok(Void)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Expression {
|
||||||
|
fn typecheck(&mut self, state: &mut State, scope: &mut Scope) -> Result<TypeKind, ErrorKind> {
|
||||||
|
match &mut self.0 {
|
||||||
|
ExprKind::Variable(var_ref) => {
|
||||||
|
let existing = state.or_else(
|
||||||
|
scope
|
||||||
|
.variables
|
||||||
|
.get(&var_ref.1)
|
||||||
|
.copied()
|
||||||
|
.ok_or(ErrorKind::VariableNotDefined(var_ref.1.clone())),
|
||||||
|
Vague(Unknown),
|
||||||
|
var_ref.2,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update typing to be more accurate
|
||||||
|
var_ref.0 = state.or_else(
|
||||||
|
var_ref.0.collapse_into(&existing),
|
||||||
|
Vague(Unknown),
|
||||||
|
var_ref.2,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(var_ref.0)
|
||||||
|
}
|
||||||
|
ExprKind::Literal(literal) => Ok(literal.as_type()),
|
||||||
|
ExprKind::BinOp(op, lhs, rhs) => {
|
||||||
|
// TODO make sure lhs and rhs can actually do this binary
|
||||||
|
// operation once relevant
|
||||||
|
let lhs_res = lhs.typecheck(state, scope);
|
||||||
|
let rhs_res = rhs.typecheck(state, scope);
|
||||||
|
let lhs_type = state.or_else(lhs_res, Vague(Unknown), lhs.1);
|
||||||
|
let rhs_type = state.or_else(rhs_res, Vague(Unknown), rhs.1);
|
||||||
|
lhs_type.binop_type(&op, &rhs_type)
|
||||||
|
}
|
||||||
|
ExprKind::FunctionCall(function_call) => {
|
||||||
|
let true_function = scope
|
||||||
|
.function_returns
|
||||||
|
.get(&function_call.name)
|
||||||
|
.cloned()
|
||||||
|
.ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone()));
|
||||||
|
|
||||||
|
if let Ok(f) = true_function {
|
||||||
|
if function_call.parameters.len() != f.params.len() {
|
||||||
|
state.ok::<_, Infallible>(Err(ErrorKind::Null), self.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let true_params_iter = f.params.into_iter().chain(iter::repeat(Vague(Unknown)));
|
||||||
|
|
||||||
|
for (param, true_param_t) in
|
||||||
|
function_call.parameters.iter_mut().zip(true_params_iter)
|
||||||
|
{
|
||||||
|
let param_res = param.typecheck(state, scope);
|
||||||
|
let param_t = state.or_else(param_res, Vague(Unknown), param.1);
|
||||||
|
state.ok(param_t.collapse_into(&true_param_t), param.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure function return type is the same as the claimed
|
||||||
|
// return type
|
||||||
|
let ret_t = try_collapse(&f.ret, &function_call.return_type)?;
|
||||||
|
// Update typing to be more accurate
|
||||||
|
function_call.return_type = ret_t;
|
||||||
|
Ok(ret_t)
|
||||||
|
} else {
|
||||||
|
Ok(function_call.return_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::If(IfExpression(cond, lhs, rhs)) => {
|
||||||
|
// TODO make sure cond_res is Boolean here
|
||||||
|
let cond_res = cond.typecheck(state, scope);
|
||||||
|
let cond_t = state.or_else(cond_res, Vague(Unknown), cond.1);
|
||||||
|
state.ok(cond_t.collapse_into(&Bool), cond.1);
|
||||||
|
|
||||||
|
let lhs_res = lhs.typecheck(state, scope);
|
||||||
|
let lhs_type = state.or_else(lhs_res, Vague(Unknown), lhs.meta);
|
||||||
|
let rhs_type = if let Some(rhs) = rhs {
|
||||||
|
let res = rhs.typecheck(state, scope);
|
||||||
|
state.or_else(res, Vague(Unknown), rhs.meta)
|
||||||
|
} else {
|
||||||
|
Vague(Unknown)
|
||||||
|
};
|
||||||
|
lhs_type.collapse_into(&rhs_type)
|
||||||
|
}
|
||||||
|
ExprKind::Block(block) => block.typecheck(state, scope),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeKind {
|
||||||
|
fn assert_known(&self) -> Result<TypeKind, ErrorKind> {
|
||||||
|
self.is_known().map_err(ErrorKind::TypeIsVague)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn binop_type(&self, op: &BinaryOperator, other: &TypeKind) -> Result<TypeKind, ErrorKind> {
|
||||||
|
let res = self.collapse_into(other)?;
|
||||||
|
Ok(match op {
|
||||||
|
BinaryOperator::Add => res,
|
||||||
|
BinaryOperator::Minus => res,
|
||||||
|
BinaryOperator::Mult => res,
|
||||||
|
BinaryOperator::And => res,
|
||||||
|
BinaryOperator::Logic(_) => Bool,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_collapse(lhs: &TypeKind, rhs: &TypeKind) -> Result<TypeKind, ErrorKind> {
|
||||||
|
lhs.collapse_into(rhs)
|
||||||
|
.or(rhs.collapse_into(lhs))
|
||||||
|
.or(Err(ErrorKind::TypesIncompatible(*lhs, *rhs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Collapsable: Sized + Clone {
|
||||||
|
fn collapse_into(&self, other: &Self) -> Result<Self, ErrorKind>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collapsable for TypeKind {
|
||||||
|
fn collapse_into(&self, other: &TypeKind) -> Result<TypeKind, ErrorKind> {
|
||||||
|
if self == other {
|
||||||
|
return Ok(self.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
match (self, other) {
|
||||||
|
(Vague(Unknown), other) | (other, Vague(Unknown)) => Ok(other.clone()),
|
||||||
|
_ => Err(ErrorKind::TypesIncompatible(*self, *other)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collapsable for ScopeFunction {
|
||||||
|
fn collapse_into(&self, other: &ScopeFunction) -> Result<ScopeFunction, ErrorKind> {
|
||||||
|
Ok(ScopeFunction {
|
||||||
|
ret: self.ret.collapse_into(&other.ret)?,
|
||||||
|
params: try_all(
|
||||||
|
self.params
|
||||||
|
.iter()
|
||||||
|
.zip(&other.params)
|
||||||
|
.map(|(p1, p2)| p1.collapse_into(&p2))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
.map_err(|e| e.first().unwrap().clone())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -156,7 +156,7 @@ impl Drop for TokenStream<'_, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Default, Clone, Copy)]
|
||||||
pub struct TokenRange {
|
pub struct TokenRange {
|
||||||
pub start: usize,
|
pub start: usize,
|
||||||
pub end: usize,
|
pub end: usize,
|
||||||
@ -168,15 +168,6 @@ impl std::fmt::Debug for TokenRange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TokenRange {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
start: Default::default(),
|
|
||||||
end: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Add for TokenRange {
|
impl std::ops::Add for TokenRange {
|
||||||
type Output = TokenRange;
|
type Output = TokenRange;
|
||||||
|
|
||||||
|
17
reid/src/util.rs
Normal file
17
reid/src/util.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
pub fn try_all<U, E>(list: Vec<Result<U, E>>) -> Result<Vec<U>, Vec<E>> {
|
||||||
|
let mut successes = Vec::with_capacity(list.len());
|
||||||
|
let mut failures = Vec::with_capacity(list.len());
|
||||||
|
|
||||||
|
for item in list {
|
||||||
|
match item {
|
||||||
|
Ok(s) => successes.push(s),
|
||||||
|
Err(e) => failures.push(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if failures.len() > 0 {
|
||||||
|
Err(failures)
|
||||||
|
} else {
|
||||||
|
Ok(successes)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user