type_checker.rs (9122B)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | use std::collections::HashMap;
use crate::issue;
use super::ast;
use ast::Nodes;
#[derive(Clone)]
pub struct TypeChecker {
pub source_line : usize,
pub source_file : String,
ident_map : HashMap<String, ast::StaticTypes>,
}
impl TypeChecker {
pub fn new() -> Self {
Self {
source_line: 0,
source_file: String::from("UNANNOUNCED_FILE"),
ident_map: HashMap::new(),
}
}
pub fn type_branch(&mut self, node : &Nodes) -> Nodes {
let mut clone = node.to_owned();
self.source_line = clone.location().line.unwrap();
match clone {
Nodes::File(f) => self.source_file = f.filename,
Nodes::Ident(ref mut i) => {
if let Some(annotation) = self.ident_map.get(&i.value) {
if let ast::StaticTypes::TSet(class) = annotation.clone() {
i.static_type = *class;
} else {
i.static_type = annotation.clone();
}
}
return Nodes::Ident(i.to_owned());
}
Nodes::Call(ref mut call) => {
if let Nodes::Call(ref mut callee) = *call.callee {
if let Nodes::Ident(ref binary_ident) = *callee.callee {
match binary_ident.value.as_str() {
":" => {
if let Nodes::Ident(ref mut annotatee) = callee.operands[0] {
let annotation = (
annotatee.value.to_owned(),
self.type_branch(&call.operands[0]).yield_type()
);
self.ident_map.insert(annotation.0.clone(), annotation.1.clone());
if let ast::StaticTypes::TSet(class) = annotation.1 {
annotatee.static_type = *class;
} else {
// Error, can only be element of set.
}
return clone;
} else {
// Error: We need the left to be an ident.
issue!(ParseError,
callee.operands[0].site().with_filename(&self.source_file),
"The left side of the member-of operator (`:`), must be an identifier.
You supplied a type of `{}'.
Only variable names can be declared as being members of sets.",
callee.operands[0].node_type())
.print();
}
},
"=" => {
// This is useful for checking variables in functions.
match &callee.operands[0] {
Nodes::Call(ref assignee) => {
// Check all the types in the annotation (A -> B -> C)
// and match them to the arguments found on the left side
// of the assignment (=). Compile these matches into a list
// and pass that list into a new TypeChecker object which checks
// the right hand side of the assignment, matching up the sub-scoped
// variables.
// A -> B -> C -> D
// f a b c = d
// <=>
// (A -> (B -> (C -> D)))
// ( ((=) ( (((f a) b) c) )) d)
let mut operands = assignee.collect_operands();
let mut func_checker = self.clone();
let base_node = operands.remove(0);
if base_node.ident().is_none() {
issue!(ParseError,
base_node.site().with_filename(&self.source_file),
"Function definitions must have the defining function's base caller
be an identifier! You're trying to define a function that has
`{}' as base caller...", base_node.node_type())
.print();
}
let maybe_type = self.ident_map.get(&base_node.ident().unwrap().value);
if maybe_type.is_none() {
#[cfg(feature="debug")] {
println!("{}", base_node);
println!("{:?}", self.ident_map);
}
issue!(TypeError,
base_node.site().with_filename(&self.source_file),
"Cannot find type annotation for the
function definition of `{}'.",
base_node.ident().unwrap().value)
.print();
}
let mut t = maybe_type.unwrap().clone();
for operand in operands {
if let Nodes::Ident(ident) = operand {
if let ast::StaticTypes::TSet(f) = &t {
if let ast::StaticTypes::TFunction(i, o) = *f.clone() {
func_checker.ident_map.insert(ident.value, *i.clone());
t = *o.clone();
}
}
}
}
call.operands[0] = func_checker.type_branch(&call.operands[0]);
return clone;
}
Nodes::Ident(_assignee) => {
// TODO:
// Here, if the ident exists in the ident_map, that means
// we need to check if both sides of the `=`'s types match up.
// If it does not exist, we need to infer its type by looking at
// the RHS and statically determine the RHS's type, and adding that
// type to the ident_map for the assignee.
}
_ => ()
}
}
_ => ()
}
}
}
// TODO HERE:
// We need to check to see if the function being called
// has a statically determined type, and if so, check that
// the operand to that function call has the exact same
// static type.
// If there is a type-mismatch, just throw an `issue!`.
// (If the function is statically typed, so
// must all the arguments be as well).
// The call must have a yield of type `function` and the
// input part of the function (input |-> output), must match
// the type of the operand. :^)
call.callee = Box::new(self.type_branch(&*call.callee));
call.operands = vec![self.type_branch(&call.operands[0])];
if let ast::StaticTypes::TFunction(_, o) = call.callee.yield_type() {
if let ast::StaticTypes::TSet(t) = *o {
call.return_type = *t;
} else {
call.return_type = *o;
}
}
return Nodes::Call(call.to_owned());
},
_ => ()
};
node.to_owned()
}
}
|