Starting to split project into multiple crates
This commit is contained in:
@@ -16,13 +16,17 @@ extern crate rocket_contrib;
|
||||
extern crate includedir;
|
||||
extern crate phf;
|
||||
|
||||
|
||||
extern crate schala_lang;
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::process::exit;
|
||||
use std::default::Default;
|
||||
|
||||
mod schala_lang;
|
||||
|
||||
mod schala_language;
|
||||
mod maaru_lang;
|
||||
mod robo_lang;
|
||||
|
||||
@@ -366,3 +370,6 @@ fn program_options() -> getopts::Options {
|
||||
options
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
use schala_lang::parsing::{AST, Statement, Declaration, Expression, Variant, ExpressionType, Operation};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct ReplState {
|
||||
values: HashMap<Rc<String>, ValueEntry>,
|
||||
}
|
||||
|
||||
enum ValueEntry {
|
||||
Binding {
|
||||
val: FullyEvaluatedExpr,
|
||||
},
|
||||
Function {
|
||||
body: Vec<Statement>,
|
||||
}
|
||||
}
|
||||
|
||||
type EvalResult<T> = Result<T, String>;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum FullyEvaluatedExpr {
|
||||
UnsignedInt(u64),
|
||||
SignedInt(i64),
|
||||
Float(f64),
|
||||
Str(String),
|
||||
Bool(bool),
|
||||
Custom {
|
||||
string_rep: Rc<String>,
|
||||
},
|
||||
Tuple(Vec<FullyEvaluatedExpr>),
|
||||
}
|
||||
|
||||
impl ReplState {
|
||||
pub fn new() -> ReplState {
|
||||
ReplState { values: HashMap::new() }
|
||||
}
|
||||
|
||||
pub fn evaluate(&mut self, ast: AST) -> Vec<String> {
|
||||
let mut acc = vec![];
|
||||
for statement in ast.0 {
|
||||
match self.eval_statement(statement) {
|
||||
Ok(output) => {
|
||||
if let Some(s) = output {
|
||||
acc.push(s);
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
acc.push(format!("Eval error: {}", error));
|
||||
return acc;
|
||||
},
|
||||
}
|
||||
}
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
impl ReplState {
|
||||
fn eval_statement(&mut self, statement: Statement) -> EvalResult<Option<String>> {
|
||||
use self::FullyEvaluatedExpr::*;
|
||||
match statement {
|
||||
Statement::ExpressionStatement(expr) => {
|
||||
self.eval_expr(expr).map( |eval| {
|
||||
match eval {
|
||||
UnsignedInt(n) => Some(format!("{}", n)),
|
||||
SignedInt(n) => Some(format!("{}", n)),
|
||||
Float(f) => Some(format!("{}", f)),
|
||||
Str(s) => Some(format!("\"{}\"", s)),
|
||||
Bool(b) => Some(format!("{}", b)),
|
||||
Custom { string_rep } => Some(format!("{}", string_rep)),
|
||||
Tuple(_items) => Some(format!("(tuple to be defined later)")),
|
||||
}
|
||||
})
|
||||
},
|
||||
Statement::Declaration(decl) => {
|
||||
self.eval_decl(decl).map(|_| None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_decl(&mut self, decl: Declaration) -> EvalResult<()> {
|
||||
use self::Declaration::*;
|
||||
use self::Variant::*;
|
||||
|
||||
match decl {
|
||||
FuncDecl(signature, statements) => {
|
||||
let name = signature.name;
|
||||
self.values.insert(name, ValueEntry::Function { body: statements.clone() });
|
||||
},
|
||||
TypeDecl(_name, body) => {
|
||||
for variant in body.0.iter() {
|
||||
match variant {
|
||||
&UnitStruct(ref name) => self.values.insert(name.clone(),
|
||||
ValueEntry::Binding { val: FullyEvaluatedExpr::Custom { string_rep: name.clone() } }),
|
||||
&TupleStruct(ref name, ref args) => unimplemented!(),
|
||||
&Record(ref name, ref fields) => unimplemented!(),
|
||||
};
|
||||
}
|
||||
},
|
||||
_ => return Err(format!("Declaration evaluation not yet implemented"))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn eval_expr(&mut self, expr: Expression) -> EvalResult<FullyEvaluatedExpr> {
|
||||
use self::ExpressionType::*;
|
||||
use self::FullyEvaluatedExpr::*;
|
||||
|
||||
let expr_type = expr.0;
|
||||
match expr_type {
|
||||
IntLiteral(n) => Ok(UnsignedInt(n)),
|
||||
FloatLiteral(f) => Ok(Float(f)),
|
||||
StringLiteral(s) => Ok(Str(s.to_string())),
|
||||
BoolLiteral(b) => Ok(Bool(b)),
|
||||
PrefixExp(op, expr) => self.eval_prefix_exp(op, expr),
|
||||
BinExp(op, lhs, rhs) => self.eval_binexp(op, lhs, rhs),
|
||||
Value(name, _) => self.eval_value(name),
|
||||
TupleLiteral(expressions) => {
|
||||
let mut evals = Vec::new();
|
||||
for expr in expressions {
|
||||
match self.eval_expr(expr) {
|
||||
Ok(fully_evaluated) => evals.push(fully_evaluated),
|
||||
error => return error,
|
||||
}
|
||||
}
|
||||
Ok(Tuple(evals))
|
||||
}
|
||||
x => Err(format!("Unimplemented thing {:?}", x)),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_value(&mut self, name: Rc<String>) -> EvalResult<FullyEvaluatedExpr> {
|
||||
use self::ValueEntry::*;
|
||||
match self.values.get(&name) {
|
||||
None => return Err(format!("Value {} not found", *name)),
|
||||
Some(lookup) => {
|
||||
match lookup {
|
||||
&Binding { ref val } => Ok(val.clone()),
|
||||
&Function { ref body } => {
|
||||
Ok(FullyEvaluatedExpr::Custom {
|
||||
string_rep: Rc::new(format!("<function {}>", *name))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_binexp(&mut self, op: Operation, lhs: Box<Expression>, rhs: Box<Expression>) -> EvalResult<FullyEvaluatedExpr> {
|
||||
use self::FullyEvaluatedExpr::*;
|
||||
let evaled_lhs = self.eval_expr(*lhs)?;
|
||||
let evaled_rhs = self.eval_expr(*rhs)?;
|
||||
let opstr: &str = &op.0;
|
||||
Ok(match (opstr, evaled_lhs, evaled_rhs) {
|
||||
("+", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l + r),
|
||||
("++", Str(s1), Str(s2)) => Str(format!("{}{}", s1, s2)),
|
||||
("-", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l - r),
|
||||
("*", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l * r),
|
||||
("/", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l / r),
|
||||
("%", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l % r),
|
||||
_ => return Err(format!("Runtime error: not yet implemented")),
|
||||
})
|
||||
}
|
||||
|
||||
fn eval_prefix_exp(&mut self, op: Operation, expr: Box<Expression>) -> EvalResult<FullyEvaluatedExpr> {
|
||||
use self::FullyEvaluatedExpr::*;
|
||||
let evaled_expr = self.eval_expr(*expr)?;
|
||||
let opstr: &str = &op.0;
|
||||
|
||||
Ok(match (opstr, evaled_expr) {
|
||||
("!", Bool(true)) => Bool(false),
|
||||
("!", Bool(false)) => Bool(true),
|
||||
("-", UnsignedInt(n)) => SignedInt(-1*(n as i64)),
|
||||
("-", SignedInt(n)) => SignedInt(-1*(n as i64)),
|
||||
_ => return Err(format!("Runtime error: not yet implemented")),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
use itertools::Itertools;
|
||||
use language::{ProgrammingLanguageInterface, EvalOptions, TraceArtifact, ReplOutput};
|
||||
|
||||
mod parsing;
|
||||
mod type_check;
|
||||
mod eval;
|
||||
|
||||
use self::type_check::{TypeContext};
|
||||
|
||||
pub struct Schala {
|
||||
state: eval::ReplState,
|
||||
type_context: TypeContext
|
||||
}
|
||||
|
||||
impl Schala {
|
||||
pub fn new() -> Schala {
|
||||
Schala {
|
||||
state: eval::ReplState::new(),
|
||||
type_context: TypeContext::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProgrammingLanguageInterface for Schala {
|
||||
fn get_language_name(&self) -> String {
|
||||
"Schala".to_string()
|
||||
}
|
||||
|
||||
fn get_source_file_suffix(&self) -> String {
|
||||
format!("schala")
|
||||
}
|
||||
|
||||
fn evaluate_in_repl(&mut self, input: &str, options: &EvalOptions) -> ReplOutput {
|
||||
let mut output = ReplOutput::default();
|
||||
let tokens = parsing::tokenize(input);
|
||||
if options.debug_tokens {
|
||||
let token_string = tokens.iter().map(|t| format!("{:?}<{}>", t.token_type, t.offset)).join(", ");
|
||||
output.add_artifact(TraceArtifact::new("tokens", format!("{:?}", token_string)));
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
let token_errors: Vec<&String> = tokens.iter().filter_map(|t| t.get_error()).collect();
|
||||
if token_errors.len() != 0 {
|
||||
output.add_output(format!("Tokenization error: {:?}\n", token_errors));
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
let ast = match parsing::parse(tokens) {
|
||||
(Ok(ast), trace) => {
|
||||
if options.debug_parse {
|
||||
output.add_artifact(TraceArtifact::new_parse_trace(trace));
|
||||
output.add_artifact(TraceArtifact::new("ast", format!("{:?}", ast)));
|
||||
}
|
||||
ast
|
||||
},
|
||||
(Err(err), trace) => {
|
||||
output.add_artifact(TraceArtifact::new_parse_trace(trace));
|
||||
output.add_output(format!("Parse error: {:?}\n", err.msg));
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
self.type_context.add_symbols(&ast);
|
||||
|
||||
if options.debug_symbol_table {
|
||||
let text = self.type_context.debug_symbol_table();
|
||||
output.add_artifact(TraceArtifact::new("symbol_table", text));
|
||||
}
|
||||
|
||||
match self.type_context.type_check(&ast) {
|
||||
Ok(ty) => {
|
||||
output.add_artifact(TraceArtifact::new("type_check", format!("type: {:?}", ty)));
|
||||
},
|
||||
Err(msg) => {
|
||||
output.add_artifact(TraceArtifact::new("type_check", msg));
|
||||
output.add_output(format!("Type error"));
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
let evaluation_output = self.state.evaluate(ast);
|
||||
let mut acc = String::new();
|
||||
let mut iter = evaluation_output.iter().peekable();
|
||||
while let Some(s) = iter.next() {
|
||||
acc.push_str(&s);
|
||||
if let Some(_) = iter.peek() {
|
||||
acc.push_str("\n");
|
||||
}
|
||||
}
|
||||
output.add_output(acc);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,361 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
//SKOLEMIZATION - how you prevent an unassigned existential type variable from leaking!
|
||||
|
||||
use schala_lang::parsing::{AST, Statement, Declaration, Signature, Expression, ExpressionType, Operation, Variant, TypeName};
|
||||
|
||||
// from Niko's talk
|
||||
/* fn type_check(expression, expected_ty) -> Ty {
|
||||
let ty = bare_type_check(expression, expected_type);
|
||||
if ty icompatible with expected_ty {
|
||||
try_coerce(expression, ty, expected_ty)
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
}
|
||||
|
||||
fn bare_type_check(exprssion, expected_type) -> Ty { ... }
|
||||
*/
|
||||
|
||||
// from https://www.youtube.com/watch?v=il3gD7XMdmA
|
||||
// typeInfer :: Expr a -> Matching (Type a)
|
||||
// unify :: Type a -> Type b -> Matching (Type c)
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Type {
|
||||
TVar(TypeVar),
|
||||
TConst(TypeConst),
|
||||
TFunc(Box<Type>, Box<Type>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum TypeVar {
|
||||
Univ(Rc<String>),
|
||||
Exist(u64),
|
||||
}
|
||||
impl TypeVar {
|
||||
fn univ(label: &str) -> TypeVar {
|
||||
TypeVar::Univ(Rc::new(label.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum TypeConst {
|
||||
UserT(Rc<String>),
|
||||
Integer,
|
||||
Float,
|
||||
StringT,
|
||||
Boolean,
|
||||
Unit,
|
||||
Bottom,
|
||||
}
|
||||
|
||||
type TypeCheckResult = Result<Type, String>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
struct PathSpecifier(Rc<String>);
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
struct TypeContextEntry {
|
||||
ty: Type,
|
||||
constant: bool
|
||||
}
|
||||
|
||||
pub struct TypeContext {
|
||||
symbol_table: HashMap<PathSpecifier, TypeContextEntry>,
|
||||
evar_table: HashMap<u64, Type>,
|
||||
existential_type_label_count: u64
|
||||
}
|
||||
|
||||
impl TypeContext {
|
||||
pub fn new() -> TypeContext {
|
||||
TypeContext {
|
||||
symbol_table: HashMap::new(),
|
||||
evar_table: HashMap::new(),
|
||||
existential_type_label_count: 0,
|
||||
}
|
||||
}
|
||||
pub fn add_symbols(&mut self, ast: &AST) {
|
||||
use self::Declaration::*;
|
||||
use self::Type::*;
|
||||
use self::TypeConst::*;
|
||||
|
||||
for statement in ast.0.iter() {
|
||||
match *statement {
|
||||
Statement::ExpressionStatement(_) => (),
|
||||
Statement::Declaration(ref decl) => {
|
||||
match *decl {
|
||||
FuncSig(_) => (),
|
||||
Impl { .. } => (),
|
||||
TypeDecl(ref type_constructor, ref body) => {
|
||||
for variant in body.0.iter() {
|
||||
let (spec, ty) = match variant {
|
||||
&Variant::UnitStruct(ref data_constructor) => {
|
||||
let spec = PathSpecifier(data_constructor.clone());
|
||||
let ty = TConst(UserT(type_constructor.clone()));
|
||||
(spec, ty)
|
||||
},
|
||||
&Variant::TupleStruct(ref data_construcor, ref args) => {
|
||||
//TODO fix
|
||||
let arg = args.get(0).unwrap();
|
||||
let type_arg = self.from_anno(arg);
|
||||
let spec = PathSpecifier(data_construcor.clone());
|
||||
let ty = TFunc(Box::new(type_arg), Box::new(TConst(UserT(type_constructor.clone()))));
|
||||
(spec, ty)
|
||||
|
||||
},
|
||||
&Variant::Record(_, _) => unimplemented!(),
|
||||
};
|
||||
let entry = TypeContextEntry { ty, constant: true };
|
||||
self.symbol_table.insert(spec, entry);
|
||||
}
|
||||
},
|
||||
TypeAlias { .. } => (),
|
||||
Binding {ref name, ref constant, ref expr} => {
|
||||
let spec = PathSpecifier(name.clone());
|
||||
let ty = expr.1.as_ref()
|
||||
.map(|ty| self.from_anno(ty))
|
||||
.unwrap_or_else(|| { self.alloc_existential_type() }); // this call to alloc_existential is OK b/c a binding only ever has one type, so if the annotation is absent, it's fine to just make one de novo
|
||||
let entry = TypeContextEntry { ty, constant: *constant };
|
||||
self.symbol_table.insert(spec, entry);
|
||||
},
|
||||
FuncDecl(ref signature, _) => {
|
||||
let spec = PathSpecifier(signature.name.clone());
|
||||
let ty = self.from_signature(signature);
|
||||
let entry = TypeContextEntry { ty, constant: true };
|
||||
self.symbol_table.insert(spec, entry);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn lookup(&mut self, binding: &Rc<String>) -> Option<TypeContextEntry> {
|
||||
let key = PathSpecifier(binding.clone());
|
||||
self.symbol_table.get(&key).map(|entry| entry.clone())
|
||||
}
|
||||
pub fn debug_symbol_table(&self) -> String {
|
||||
format!("Symbol table:\n {:?}\nEvar table:\n{:?}", self.symbol_table, self.evar_table)
|
||||
}
|
||||
fn alloc_existential_type(&mut self) -> Type {
|
||||
let ret = Type::TVar(TypeVar::Exist(self.existential_type_label_count));
|
||||
self.existential_type_label_count += 1;
|
||||
ret
|
||||
}
|
||||
|
||||
fn from_anno(&mut self, anno: &TypeName) -> Type {
|
||||
use self::Type::*;
|
||||
use self::TypeConst::*;
|
||||
|
||||
match anno {
|
||||
&TypeName::Singleton { ref name, .. } => {
|
||||
match name.as_ref().as_ref() {
|
||||
"Int" => TConst(Integer),
|
||||
"Float" => TConst(Float),
|
||||
"Bool" => TConst(Boolean),
|
||||
"String" => TConst(StringT),
|
||||
s => TVar(TypeVar::Univ(Rc::new(format!("{}",s)))),
|
||||
}
|
||||
},
|
||||
&TypeName::Tuple(ref items) => {
|
||||
if items.len() == 1 {
|
||||
TConst(Unit)
|
||||
} else {
|
||||
TConst(Bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_signature(&mut self, sig: &Signature) -> Type {
|
||||
use self::Type::*;
|
||||
use self::TypeConst::*;
|
||||
|
||||
//TODO this won't work properly until you make sure that all (universal) type vars in the function have the same existential type var
|
||||
// actually this should never even put existential types into the symbol table at all
|
||||
|
||||
//this will crash if more than 5 arg function is used
|
||||
let names = vec!["a", "b", "c", "d", "e", "f"];
|
||||
let mut idx = 0;
|
||||
|
||||
let mut get_type = || { let q = TVar(TypeVar::Univ(Rc::new(format!("{}", names.get(idx).unwrap())))); idx += 1; q };
|
||||
|
||||
let return_type = sig.type_anno.as_ref().map(|anno| self.from_anno(&anno)).unwrap_or_else(|| { get_type() });
|
||||
if sig.params.len() == 0 {
|
||||
TFunc(Box::new(TConst(Unit)), Box::new(return_type))
|
||||
} else {
|
||||
let mut output_type = return_type;
|
||||
for p in sig.params.iter() {
|
||||
let p_type = p.1.as_ref().map(|anno| self.from_anno(anno)).unwrap_or_else(|| { get_type() });
|
||||
output_type = TFunc(Box::new(p_type), Box::new(output_type));
|
||||
}
|
||||
output_type
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_check(&mut self, ast: &AST) -> TypeCheckResult {
|
||||
use self::Type::*;
|
||||
use self::TypeConst::*;
|
||||
|
||||
let mut last = TConst(Unit);
|
||||
|
||||
for statement in ast.0.iter() {
|
||||
match statement {
|
||||
&Statement::Declaration(ref _decl) => {
|
||||
//return Err(format!("Declarations not supported"));
|
||||
},
|
||||
&Statement::ExpressionStatement(ref expr) => {
|
||||
last = self.infer(expr)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(last)
|
||||
}
|
||||
fn infer(&mut self, expr: &Expression) -> TypeCheckResult {
|
||||
match (&expr.0, &expr.1) {
|
||||
(exprtype, &Some(ref anno)) => {
|
||||
let tx = self.infer_no_anno(exprtype)?;
|
||||
let ty = self.from_anno(anno);
|
||||
self.unify(tx, ty)
|
||||
},
|
||||
(exprtype, &None) => self.infer_no_anno(exprtype),
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_no_anno(&mut self, ex: &ExpressionType) -> TypeCheckResult {
|
||||
use self::ExpressionType::*;
|
||||
use self::Type::*;
|
||||
use self::TypeConst::*;
|
||||
|
||||
Ok(match ex {
|
||||
&IntLiteral(_) => TConst(Integer),
|
||||
&FloatLiteral(_) => TConst(Float),
|
||||
&StringLiteral(_) => TConst(StringT),
|
||||
&BoolLiteral(_) => TConst(Boolean),
|
||||
&Value(ref name, _) => {
|
||||
self.lookup(name)
|
||||
.map(|entry| entry.ty)
|
||||
.ok_or(format!("Couldn't find {}", name))?
|
||||
},
|
||||
&BinExp(ref op, ref lhs, ref rhs) => {
|
||||
let t_lhs = self.infer(lhs)?;
|
||||
match self.infer_op(op)? {
|
||||
TFunc(t1, t2) => {
|
||||
let _ = self.unify(t_lhs, *t1)?;
|
||||
let t_rhs = self.infer(rhs)?;
|
||||
let x = *t2;
|
||||
match x {
|
||||
TFunc(t3, t4) => {
|
||||
let _ = self.unify(t_rhs, *t3)?;
|
||||
*t4
|
||||
},
|
||||
_ => return Err(format!("Not a function type either")),
|
||||
}
|
||||
},
|
||||
_ => return Err(format!("Op {:?} is not a function type", op)),
|
||||
}
|
||||
},
|
||||
&Call { ref f, ref arguments } => {
|
||||
let tf = self.infer(f)?;
|
||||
let targ = self.infer(arguments.get(0).unwrap())?;
|
||||
match tf {
|
||||
TFunc(box t1, box t2) => {
|
||||
let _ = self.unify(t1, targ)?;
|
||||
t2
|
||||
},
|
||||
_ => return Err(format!("Not a function!")),
|
||||
}
|
||||
},
|
||||
_ => TConst(Bottom),
|
||||
})
|
||||
}
|
||||
|
||||
fn infer_op(&mut self, op: &Operation) -> TypeCheckResult {
|
||||
use self::Type::*;
|
||||
use self::TypeConst::*;
|
||||
macro_rules! binoptype {
|
||||
($lhs:expr, $rhs:expr, $out:expr) => { TFunc(Box::new($lhs), Box::new(TFunc(Box::new($rhs), Box::new($out)))) };
|
||||
}
|
||||
|
||||
Ok(match (*op.0).as_ref() {
|
||||
"+" => binoptype!(TConst(Integer), TConst(Integer), TConst(Integer)),
|
||||
"++" => binoptype!(TConst(StringT), TConst(StringT), TConst(StringT)),
|
||||
"-" => binoptype!(TConst(Integer), TConst(Integer), TConst(Integer)),
|
||||
"*" => binoptype!(TConst(Integer), TConst(Integer), TConst(Integer)),
|
||||
"/" => binoptype!(TConst(Integer), TConst(Integer), TConst(Integer)),
|
||||
"%" => binoptype!(TConst(Integer), TConst(Integer), TConst(Integer)),
|
||||
_ => TConst(Bottom)
|
||||
})
|
||||
}
|
||||
|
||||
fn unify(&mut self, t1: Type, t2: Type) -> TypeCheckResult {
|
||||
use self::Type::*;
|
||||
use self::TypeVar::*;
|
||||
|
||||
println!("Calling unify with `{:?}` and `{:?}`", t1, t2);
|
||||
|
||||
match (&t1, &t2) {
|
||||
(&TConst(ref c1), &TConst(ref c2)) if c1 == c2 => Ok(TConst(c1.clone())),
|
||||
(&TFunc(ref t1, ref t2), &TFunc(ref t3, ref t4)) => {
|
||||
let t5 = self.unify(*t1.clone().clone(), *t3.clone().clone())?;
|
||||
let t6 = self.unify(*t2.clone().clone(), *t4.clone().clone())?;
|
||||
Ok(TFunc(Box::new(t5), Box::new(t6)))
|
||||
},
|
||||
(&TVar(Univ(ref a)), &TVar(Univ(ref b))) => {
|
||||
if a == b {
|
||||
Ok(TVar(Univ(a.clone())))
|
||||
} else {
|
||||
Err(format!("Couldn't unify universal types {} and {}", a, b))
|
||||
}
|
||||
},
|
||||
//the interesting case!!
|
||||
(&TVar(Exist(ref a)), ref t2) => {
|
||||
let x = self.evar_table.get(a).map(|x| x.clone());
|
||||
match x {
|
||||
Some(ref t1) => self.unify(t1.clone().clone(), t2.clone().clone()),
|
||||
None => {
|
||||
self.evar_table.insert(*a, t2.clone().clone());
|
||||
Ok(t2.clone().clone())
|
||||
}
|
||||
}
|
||||
},
|
||||
(ref t1, &TVar(Exist(ref a))) => {
|
||||
let x = self.evar_table.get(a).map(|x| x.clone());
|
||||
match x {
|
||||
Some(ref t2) => self.unify(t2.clone().clone(), t1.clone().clone()),
|
||||
None => {
|
||||
self.evar_table.insert(*a, t1.clone().clone());
|
||||
Ok(t1.clone().clone())
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => Err(format!("Types {:?} and {:?} don't unify", t1, t2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Type, TypeVar, TypeConst, TypeContext};
|
||||
use super::Type::*;
|
||||
use super::TypeConst::*;
|
||||
use schala_lang::parsing::{parse, tokenize};
|
||||
|
||||
macro_rules! type_test {
|
||||
($input:expr, $correct:expr) => {
|
||||
{
|
||||
let mut tc = TypeContext::new();
|
||||
let ast = parse(tokenize($input)).0.unwrap() ;
|
||||
tc.add_symbols(&ast);
|
||||
assert_eq!($correct, tc.type_check(&ast).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_inference() {
|
||||
type_test!("30", TConst(Integer));
|
||||
type_test!("fn x(a: Int): Bool {}; x(1)", TConst(Boolean));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user