forked from M-Labs/nac3
expression type check
This commit is contained in:
parent
dd29ae251a
commit
50ffded020
1023
nac3core/src/expression.rs
Normal file
1023
nac3core/src/expression.rs
Normal file
File diff suppressed because it is too large
Load Diff
591
nac3core/src/inference.rs
Normal file
591
nac3core/src/inference.rs
Normal file
@ -0,0 +1,591 @@
|
||||
use super::typedef::{Type::*, *};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
fn find_subst(
|
||||
ctx: &GlobalContext,
|
||||
valuation: &Option<(VariableId, Rc<Type>)>,
|
||||
sub: &mut HashMap<VariableId, Rc<Type>>,
|
||||
mut a: Rc<Type>,
|
||||
mut b: Rc<Type>,
|
||||
) -> Result<(), String> {
|
||||
// TODO: fix error messages later
|
||||
if let TypeVariable(id) = a.as_ref() {
|
||||
if let Some((assumption_id, t)) = valuation {
|
||||
if assumption_id == id {
|
||||
a = t.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut substituted = false;
|
||||
if let TypeVariable(id) = b.as_ref() {
|
||||
if let Some(c) = sub.get(&id) {
|
||||
b = c.clone();
|
||||
substituted = true;
|
||||
}
|
||||
}
|
||||
|
||||
match (a.as_ref(), b.as_ref()) {
|
||||
(BotType, _) => Ok(()),
|
||||
(TypeVariable(id_a), TypeVariable(id_b)) => {
|
||||
if substituted {
|
||||
return if id_a == id_b {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("different variables".to_string())
|
||||
};
|
||||
}
|
||||
let v_a = ctx.get_variable(*id_a);
|
||||
let v_b = ctx.get_variable(*id_b);
|
||||
if v_b.bound.len() > 0 {
|
||||
if v_a.bound.len() == 0 {
|
||||
return Err("unbounded a".to_string());
|
||||
} else {
|
||||
let diff: Vec<_> = v_a
|
||||
.bound
|
||||
.iter()
|
||||
.filter(|x| !v_b.bound.contains(x))
|
||||
.collect();
|
||||
if diff.len() > 0 {
|
||||
return Err("different domain".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
sub.insert(*id_b, a.clone().into());
|
||||
Ok(())
|
||||
}
|
||||
(TypeVariable(id_a), _) => {
|
||||
let v_a = ctx.get_variable(*id_a);
|
||||
if v_a.bound.len() == 1 && v_a.bound[0].as_ref() == b.as_ref() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("different domain".to_string())
|
||||
}
|
||||
}
|
||||
(_, TypeVariable(id_b)) => {
|
||||
let v_b = ctx.get_variable(*id_b);
|
||||
if v_b.bound.len() == 0 || v_b.bound.contains(&a) {
|
||||
sub.insert(*id_b, a.clone().into());
|
||||
Ok(())
|
||||
} else {
|
||||
Err("different domain".to_string())
|
||||
}
|
||||
}
|
||||
(_, VirtualClassType(id_b)) => {
|
||||
let mut parents;
|
||||
match a.as_ref() {
|
||||
ClassType(id_a) => {
|
||||
parents = [*id_a].to_vec();
|
||||
}
|
||||
VirtualClassType(id_a) => {
|
||||
parents = [*id_a].to_vec();
|
||||
}
|
||||
_ => {
|
||||
return Err("cannot substitute non-class type into virtual class".to_string());
|
||||
}
|
||||
};
|
||||
while !parents.is_empty() {
|
||||
if *id_b == parents[0] {
|
||||
return Ok(());
|
||||
}
|
||||
let c = ctx.get_class(parents.remove(0));
|
||||
parents.extend_from_slice(&c.parents);
|
||||
}
|
||||
Err("not subtype".to_string())
|
||||
}
|
||||
(ParametricType(id_a, param_a), ParametricType(id_b, param_b)) => {
|
||||
if id_a != id_b || param_a.len() != param_b.len() {
|
||||
Err("different parametric types".to_string())
|
||||
} else {
|
||||
for (x, y) in param_a.iter().zip(param_b.iter()) {
|
||||
find_subst(ctx, valuation, sub, x.clone(), y.clone())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
(_, _) => {
|
||||
if a == b {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("not equal".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_call_rec(
|
||||
ctx: &GlobalContext,
|
||||
valuation: &Option<(VariableId, Rc<Type>)>,
|
||||
obj: Option<Rc<Type>>,
|
||||
func: &str,
|
||||
args: &[Rc<Type>],
|
||||
) -> Result<Option<Rc<Type>>, String> {
|
||||
let mut subst = obj
|
||||
.as_ref()
|
||||
.map(|v| v.get_subst(ctx))
|
||||
.unwrap_or(HashMap::new());
|
||||
|
||||
let fun = match &obj {
|
||||
Some(obj) => {
|
||||
let base = match obj.as_ref() {
|
||||
TypeVariable(id) => {
|
||||
let v = ctx.get_variable(*id);
|
||||
if v.bound.len() == 0 {
|
||||
return Err("unbounded type var".to_string());
|
||||
}
|
||||
let results: Result<Vec<_>, String> = v
|
||||
.bound
|
||||
.iter()
|
||||
.map(|ins| {
|
||||
resolve_call_rec(
|
||||
ctx,
|
||||
&Some((*id, ins.clone())),
|
||||
Some(ins.clone()),
|
||||
func,
|
||||
args.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let results = results?;
|
||||
if results.iter().all(|v| v == &results[0]) {
|
||||
return Ok(results[0].clone());
|
||||
}
|
||||
let mut results = results.iter().zip(v.bound.iter()).map(|(r, ins)| {
|
||||
r.as_ref()
|
||||
.map(|v| v.inv_subst(&[(ins.clone(), obj.clone().into())]))
|
||||
});
|
||||
let first = results.next().unwrap();
|
||||
if results.all(|v| v == first) {
|
||||
return Ok(first);
|
||||
} else {
|
||||
return Err("divergent type after substitution".to_string());
|
||||
}
|
||||
}
|
||||
PrimitiveType(id) => &ctx.get_primitive(*id),
|
||||
ClassType(id) | VirtualClassType(id) => &ctx.get_class(*id).base,
|
||||
ParametricType(id, _) => &ctx.get_parametric(*id).base,
|
||||
_ => return Err("not supported".to_string()),
|
||||
};
|
||||
base.methods.get(func)
|
||||
}
|
||||
None => ctx.get_fn(func),
|
||||
}
|
||||
.ok_or("no such function".to_string())?;
|
||||
|
||||
if args.len() != fun.args.len() {
|
||||
return Err("incorrect parameter number".to_string());
|
||||
}
|
||||
for (a, b) in args.iter().zip(fun.args.iter()) {
|
||||
find_subst(ctx, valuation, &mut subst, a.clone(), b.clone())?;
|
||||
}
|
||||
let result = fun.result.as_ref().map(|v| v.subst(&subst));
|
||||
Ok(result.map(|result| {
|
||||
if let SelfType = result {
|
||||
obj.unwrap()
|
||||
} else {
|
||||
result.into()
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn resolve_call(
|
||||
ctx: &GlobalContext,
|
||||
obj: Option<Rc<Type>>,
|
||||
func: &str,
|
||||
args: &[Rc<Type>],
|
||||
) -> Result<Option<Rc<Type>>, String> {
|
||||
resolve_call_rec(ctx, &None, obj, func, args)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::primitives::*;
|
||||
|
||||
#[test]
|
||||
fn test_simple_generic() {
|
||||
let mut ctx = basic_ctx();
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "int32", &[PrimitiveType(FLOAT_TYPE).into()]),
|
||||
Ok(Some(PrimitiveType(INT32_TYPE).into()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "int32", &[PrimitiveType(INT32_TYPE).into()],),
|
||||
Ok(Some(PrimitiveType(INT32_TYPE).into()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "float", &[PrimitiveType(INT32_TYPE).into()]),
|
||||
Ok(Some(PrimitiveType(FLOAT_TYPE).into()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "float", &[PrimitiveType(BOOL_TYPE).into()]),
|
||||
Err("different domain".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "float", &[]),
|
||||
Err("incorrect parameter number".to_string())
|
||||
);
|
||||
|
||||
let v1 = ctx.add_variable(VarDef {
|
||||
name: "V1",
|
||||
bound: vec![
|
||||
PrimitiveType(INT32_TYPE).into(),
|
||||
PrimitiveType(FLOAT_TYPE).into(),
|
||||
],
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "float", &[TypeVariable(v1).into()]),
|
||||
Ok(Some(PrimitiveType(FLOAT_TYPE).into()))
|
||||
);
|
||||
|
||||
let v2 = ctx.add_variable(VarDef {
|
||||
name: "V2",
|
||||
bound: vec![
|
||||
PrimitiveType(BOOL_TYPE).into(),
|
||||
PrimitiveType(INT32_TYPE).into(),
|
||||
PrimitiveType(FLOAT_TYPE).into(),
|
||||
],
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "float", &[TypeVariable(v2).into()]),
|
||||
Err("different domain".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_methods() {
|
||||
let mut ctx = basic_ctx();
|
||||
|
||||
let v0 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
|
||||
name: "V0",
|
||||
bound: vec![],
|
||||
})));
|
||||
let v1 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
|
||||
name: "V1",
|
||||
bound: vec![
|
||||
PrimitiveType(INT32_TYPE).into(),
|
||||
PrimitiveType(FLOAT_TYPE).into(),
|
||||
],
|
||||
})));
|
||||
let v2 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
|
||||
name: "V2",
|
||||
bound: vec![
|
||||
PrimitiveType(INT32_TYPE).into(),
|
||||
PrimitiveType(FLOAT_TYPE).into(),
|
||||
],
|
||||
})));
|
||||
let v3 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
|
||||
name: "V3",
|
||||
bound: vec![
|
||||
PrimitiveType(BOOL_TYPE).into(),
|
||||
PrimitiveType(INT32_TYPE).into(),
|
||||
PrimitiveType(FLOAT_TYPE).into(),
|
||||
],
|
||||
})));
|
||||
|
||||
let int32 = Rc::new(PrimitiveType(INT32_TYPE));
|
||||
let int64 = Rc::new(PrimitiveType(INT64_TYPE));
|
||||
|
||||
// simple cases
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, Some(int32.clone()), "__add__", &[int32.clone()]),
|
||||
Ok(Some(int32.clone()))
|
||||
);
|
||||
|
||||
assert_ne!(
|
||||
resolve_call(&ctx, Some(int32.clone()), "__add__", &[int32.clone()]),
|
||||
Ok(Some(int64.clone()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, Some(int32.clone()), "__add__", &[int64.clone()]),
|
||||
Err("not equal".to_string())
|
||||
);
|
||||
|
||||
// with type variables
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v1.clone()]),
|
||||
Ok(Some(v1.clone()))
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, Some(v0.clone()), "__add__", &[v2.clone()]),
|
||||
Err("unbounded type var".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v0.clone()]),
|
||||
Err("different domain".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v2.clone()]),
|
||||
Err("different domain".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v3.clone()]),
|
||||
Err("different domain".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, Some(v3.clone()), "__add__", &[v1.clone()]),
|
||||
Err("no such function".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, Some(v3.clone()), "__add__", &[v3.clone()]),
|
||||
Err("no such function".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_generic() {
|
||||
let mut ctx = basic_ctx();
|
||||
let v0 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
|
||||
name: "V0",
|
||||
bound: vec![],
|
||||
})));
|
||||
let v1 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
|
||||
name: "V1",
|
||||
bound: vec![],
|
||||
})));
|
||||
let v2 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
|
||||
name: "V2",
|
||||
bound: vec![],
|
||||
})));
|
||||
let v3 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
|
||||
name: "V3",
|
||||
bound: vec![],
|
||||
})));
|
||||
|
||||
ctx.add_fn(
|
||||
"foo",
|
||||
FnDef {
|
||||
args: vec![v0.clone(), v0.clone(), v1.clone()],
|
||||
result: Some(v0.clone()),
|
||||
},
|
||||
);
|
||||
|
||||
ctx.add_fn(
|
||||
"foo1",
|
||||
FnDef {
|
||||
args: vec![
|
||||
ParametricType(TUPLE_TYPE, vec![v0.clone(), v0.clone(), v1.clone()]).into(),
|
||||
],
|
||||
result: Some(v0.clone()),
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo", &[v2.clone(), v2.clone(), v2.clone()]),
|
||||
Ok(Some(v2.clone()))
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo", &[v2.clone(), v2.clone(), v3.clone()]),
|
||||
Ok(Some(v2.clone()))
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo", &[v2.clone(), v3.clone(), v3.clone()]),
|
||||
Err("different variables".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(
|
||||
&ctx,
|
||||
None,
|
||||
"foo1",
|
||||
&[ParametricType(TUPLE_TYPE, vec![v2.clone(), v2.clone(), v2.clone()]).into()]
|
||||
),
|
||||
Ok(Some(v2.clone()))
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(
|
||||
&ctx,
|
||||
None,
|
||||
"foo1",
|
||||
&[ParametricType(TUPLE_TYPE, vec![v2.clone(), v2.clone(), v3.clone()]).into()]
|
||||
),
|
||||
Ok(Some(v2.clone()))
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(
|
||||
&ctx,
|
||||
None,
|
||||
"foo1",
|
||||
&[ParametricType(TUPLE_TYPE, vec![v2.clone(), v3.clone(), v3.clone()]).into()]
|
||||
),
|
||||
Err("different variables".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_class_generics() {
|
||||
let mut ctx = basic_ctx();
|
||||
|
||||
let list = ctx.get_parametric_mut(LIST_TYPE);
|
||||
let t = Rc::new(TypeVariable(list.params[0]));
|
||||
list.base.methods.insert(
|
||||
"head",
|
||||
FnDef {
|
||||
args: vec![],
|
||||
result: Some(t.clone()),
|
||||
},
|
||||
);
|
||||
list.base.methods.insert(
|
||||
"append",
|
||||
FnDef {
|
||||
args: vec![t.clone()],
|
||||
result: None,
|
||||
},
|
||||
);
|
||||
|
||||
let v0 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
|
||||
name: "V0",
|
||||
bound: vec![],
|
||||
})));
|
||||
let v1 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
|
||||
name: "V1",
|
||||
bound: vec![],
|
||||
})));
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(
|
||||
&ctx,
|
||||
Some(ParametricType(LIST_TYPE, vec![v0.clone()]).into()),
|
||||
"head",
|
||||
&[]
|
||||
),
|
||||
Ok(Some(v0.clone()))
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(
|
||||
&ctx,
|
||||
Some(ParametricType(LIST_TYPE, vec![v0.clone()]).into()),
|
||||
"append",
|
||||
&[v0.clone()]
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(
|
||||
&ctx,
|
||||
Some(ParametricType(LIST_TYPE, vec![v0.clone()]).into()),
|
||||
"append",
|
||||
&[v1.clone()]
|
||||
),
|
||||
Err("different variables".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_virtual_class() {
|
||||
let mut ctx = basic_ctx();
|
||||
|
||||
let foo = ctx.add_class(ClassDef {
|
||||
base: TypeDef {
|
||||
name: "Foo",
|
||||
methods: HashMap::new(),
|
||||
fields: HashMap::new(),
|
||||
},
|
||||
parents: vec![],
|
||||
});
|
||||
|
||||
let foo1 = ctx.add_class(ClassDef {
|
||||
base: TypeDef {
|
||||
name: "Foo1",
|
||||
methods: HashMap::new(),
|
||||
fields: HashMap::new(),
|
||||
},
|
||||
parents: vec![foo],
|
||||
});
|
||||
|
||||
let foo2 = ctx.add_class(ClassDef {
|
||||
base: TypeDef {
|
||||
name: "Foo2",
|
||||
methods: HashMap::new(),
|
||||
fields: HashMap::new(),
|
||||
},
|
||||
parents: vec![foo1],
|
||||
});
|
||||
|
||||
let bar = ctx.add_class(ClassDef {
|
||||
base: TypeDef {
|
||||
name: "bar",
|
||||
methods: HashMap::new(),
|
||||
fields: HashMap::new(),
|
||||
},
|
||||
parents: vec![],
|
||||
});
|
||||
|
||||
ctx.add_fn(
|
||||
"foo",
|
||||
FnDef {
|
||||
args: vec![VirtualClassType(foo).into()],
|
||||
result: None,
|
||||
},
|
||||
);
|
||||
ctx.add_fn(
|
||||
"foo1",
|
||||
FnDef {
|
||||
args: vec![VirtualClassType(foo1).into()],
|
||||
result: None,
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo", &[ClassType(foo).into()]),
|
||||
Ok(None)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo", &[ClassType(foo1).into()]),
|
||||
Ok(None)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo", &[ClassType(foo2).into()]),
|
||||
Ok(None)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo", &[ClassType(bar).into()]),
|
||||
Err("not subtype".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo1", &[ClassType(foo1).into()]),
|
||||
Ok(None)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo1", &[ClassType(foo2).into()]),
|
||||
Ok(None)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo1", &[ClassType(foo).into()]),
|
||||
Err("not subtype".to_string())
|
||||
);
|
||||
|
||||
// virtual class substitution
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo", &[VirtualClassType(foo).into()]),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo", &[VirtualClassType(foo1).into()]),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo", &[VirtualClassType(foo2).into()]),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_call(&ctx, None, "foo", &[VirtualClassType(bar).into()]),
|
||||
Err("not subtype".to_string())
|
||||
);
|
||||
}
|
||||
}
|
@ -2,6 +2,12 @@ extern crate num_bigint;
|
||||
extern crate inkwell;
|
||||
extern crate rustpython_parser;
|
||||
|
||||
pub mod expression;
|
||||
pub mod inference;
|
||||
mod operators;
|
||||
pub mod primitives;
|
||||
pub mod typedef;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
|
58
nac3core/src/operators.rs
Normal file
58
nac3core/src/operators.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use rustpython_parser::ast::{Comparison, Operator, UnaryOperator};
|
||||
|
||||
pub fn binop_name(op: &Operator) -> &'static str {
|
||||
match op {
|
||||
Operator::Add => "__add__",
|
||||
Operator::Sub => "__sub__",
|
||||
Operator::Div => "__truediv__",
|
||||
Operator::Mod => "__mod__",
|
||||
Operator::Mult => "__mul__",
|
||||
Operator::Pow => "__pow__",
|
||||
Operator::BitOr => "__or__",
|
||||
Operator::BitXor => "__xor__",
|
||||
Operator::BitAnd => "__and__",
|
||||
Operator::LShift => "__lshift__",
|
||||
Operator::RShift => "__rshift__",
|
||||
Operator::FloorDiv => "__floordiv__",
|
||||
Operator::MatMult => "__matmul__",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn binop_assign_name(op: &Operator) -> &'static str {
|
||||
match op {
|
||||
Operator::Add => "__iadd__",
|
||||
Operator::Sub => "__isub__",
|
||||
Operator::Div => "__itruediv__",
|
||||
Operator::Mod => "__imod__",
|
||||
Operator::Mult => "__imul__",
|
||||
Operator::Pow => "__ipow__",
|
||||
Operator::BitOr => "__ior__",
|
||||
Operator::BitXor => "__ixor__",
|
||||
Operator::BitAnd => "__iand__",
|
||||
Operator::LShift => "__ilshift__",
|
||||
Operator::RShift => "__irshift__",
|
||||
Operator::FloorDiv => "__ifloordiv__",
|
||||
Operator::MatMult => "__imatmul__",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unaryop_name(op: &UnaryOperator) -> &'static str {
|
||||
match op {
|
||||
UnaryOperator::Pos => "__pos__",
|
||||
UnaryOperator::Neg => "__neg__",
|
||||
UnaryOperator::Not => "__not__",
|
||||
UnaryOperator::Inv => "__inv__",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn comparison_name(op: &Comparison) -> Option<&'static str> {
|
||||
match op {
|
||||
Comparison::Less => Some("__lt__"),
|
||||
Comparison::LessOrEqual => Some("__le__"),
|
||||
Comparison::Greater => Some("__gt__"),
|
||||
Comparison::GreaterOrEqual => Some("__ge__"),
|
||||
Comparison::Equal => Some("__eq__"),
|
||||
Comparison::NotEqual => Some("__ne__"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
181
nac3core/src/primitives.rs
Normal file
181
nac3core/src/primitives.rs
Normal file
@ -0,0 +1,181 @@
|
||||
use super::typedef::{Type::*, *};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub const TUPLE_TYPE: ParamId = ParamId(0);
|
||||
pub const LIST_TYPE: ParamId = ParamId(1);
|
||||
|
||||
pub const BOOL_TYPE: PrimitiveId = PrimitiveId(0);
|
||||
pub const INT32_TYPE: PrimitiveId = PrimitiveId(1);
|
||||
pub const INT64_TYPE: PrimitiveId = PrimitiveId(2);
|
||||
pub const FLOAT_TYPE: PrimitiveId = PrimitiveId(3);
|
||||
|
||||
fn impl_math(def: &mut TypeDef, ty: &Rc<Type>) {
|
||||
let result = Some(ty.clone());
|
||||
let fun = FnDef {
|
||||
args: vec![ty.clone()],
|
||||
result: result.clone(),
|
||||
};
|
||||
def.methods.insert("__add__", fun.clone());
|
||||
def.methods.insert("__sub__", fun.clone());
|
||||
def.methods.insert("__mul__", fun.clone());
|
||||
def.methods.insert("__neg__", FnDef {
|
||||
args: vec![],
|
||||
result
|
||||
});
|
||||
def.methods.insert(
|
||||
"__truediv__",
|
||||
FnDef {
|
||||
args: vec![ty.clone()],
|
||||
result: Some(PrimitiveType(FLOAT_TYPE).into()),
|
||||
},
|
||||
);
|
||||
def.methods.insert("__floordiv__", fun.clone());
|
||||
def.methods.insert("__mod__", fun.clone());
|
||||
def.methods.insert("__pow__", fun.clone());
|
||||
}
|
||||
|
||||
fn impl_bits(def: &mut TypeDef, ty: &Rc<Type>) {
|
||||
let result = Some(ty.clone());
|
||||
let fun = FnDef {
|
||||
args: vec![PrimitiveType(INT32_TYPE).into()],
|
||||
result,
|
||||
};
|
||||
|
||||
def.methods.insert("__lshift__", fun.clone());
|
||||
def.methods.insert("__rshift__", fun.clone());
|
||||
def.methods.insert(
|
||||
"__xor__",
|
||||
FnDef {
|
||||
args: vec![ty.clone()],
|
||||
result: Some(ty.clone()),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn impl_eq(def: &mut TypeDef, ty: &Rc<Type>) {
|
||||
let fun = FnDef {
|
||||
args: vec![ty.clone()],
|
||||
result: Some(PrimitiveType(BOOL_TYPE).into()),
|
||||
};
|
||||
|
||||
def.methods.insert("__eq__", fun.clone());
|
||||
def.methods.insert("__ne__", fun.clone());
|
||||
}
|
||||
|
||||
fn impl_order(def: &mut TypeDef, ty: &Rc<Type>) {
|
||||
let fun = FnDef {
|
||||
args: vec![ty.clone()],
|
||||
result: Some(PrimitiveType(BOOL_TYPE).into()),
|
||||
};
|
||||
|
||||
def.methods.insert("__lt__", fun.clone());
|
||||
def.methods.insert("__gt__", fun.clone());
|
||||
def.methods.insert("__le__", fun.clone());
|
||||
def.methods.insert("__ge__", fun.clone());
|
||||
}
|
||||
|
||||
pub fn basic_ctx() -> GlobalContext<'static> {
|
||||
let primitives = [
|
||||
TypeDef {
|
||||
name: "bool",
|
||||
fields: HashMap::new(),
|
||||
methods: HashMap::new(),
|
||||
},
|
||||
TypeDef {
|
||||
name: "int32",
|
||||
fields: HashMap::new(),
|
||||
methods: HashMap::new(),
|
||||
},
|
||||
TypeDef {
|
||||
name: "int64",
|
||||
fields: HashMap::new(),
|
||||
methods: HashMap::new(),
|
||||
},
|
||||
TypeDef {
|
||||
name: "float",
|
||||
fields: HashMap::new(),
|
||||
methods: HashMap::new(),
|
||||
},
|
||||
]
|
||||
.to_vec();
|
||||
let mut ctx = GlobalContext::new(primitives);
|
||||
|
||||
let b_def = ctx.get_primitive_mut(BOOL_TYPE);
|
||||
let b = PrimitiveType(BOOL_TYPE).into();
|
||||
impl_eq(b_def, &b);
|
||||
let int32_def = ctx.get_primitive_mut(INT32_TYPE);
|
||||
let int32 = PrimitiveType(INT32_TYPE).into();
|
||||
impl_math(int32_def, &int32);
|
||||
impl_bits(int32_def, &int32);
|
||||
impl_order(int32_def, &int32);
|
||||
impl_eq(int32_def, &int32);
|
||||
let int64_def = ctx.get_primitive_mut(INT64_TYPE);
|
||||
let int64 = PrimitiveType(INT64_TYPE).into();
|
||||
impl_math(int64_def, &int64);
|
||||
impl_bits(int64_def, &int64);
|
||||
impl_order(int64_def, &int64);
|
||||
impl_eq(int64_def, &int64);
|
||||
let float_def = ctx.get_primitive_mut(FLOAT_TYPE);
|
||||
let float = PrimitiveType(FLOAT_TYPE).into();
|
||||
impl_math(float_def, &float);
|
||||
impl_order(float_def, &float);
|
||||
impl_eq(float_def, &float);
|
||||
|
||||
let t = ctx.add_variable_private(VarDef {
|
||||
name: "T",
|
||||
bound: vec![],
|
||||
});
|
||||
|
||||
ctx.add_parametric(ParametricDef {
|
||||
base: TypeDef {
|
||||
name: "tuple",
|
||||
fields: HashMap::new(),
|
||||
methods: HashMap::new(),
|
||||
},
|
||||
// we have nothing for tuple, so no param def
|
||||
params: vec![],
|
||||
});
|
||||
|
||||
ctx.add_parametric(ParametricDef {
|
||||
base: TypeDef {
|
||||
name: "list",
|
||||
fields: HashMap::new(),
|
||||
methods: HashMap::new(),
|
||||
},
|
||||
params: vec![t],
|
||||
});
|
||||
|
||||
let i = ctx.add_variable_private(VarDef {
|
||||
name: "I",
|
||||
bound: vec![
|
||||
PrimitiveType(INT32_TYPE).into(),
|
||||
PrimitiveType(INT64_TYPE).into(),
|
||||
PrimitiveType(FLOAT_TYPE).into(),
|
||||
],
|
||||
});
|
||||
let args = vec![TypeVariable(i).into()];
|
||||
ctx.add_fn(
|
||||
"int32",
|
||||
FnDef {
|
||||
args: args.clone(),
|
||||
result: Some(PrimitiveType(INT32_TYPE).into()),
|
||||
},
|
||||
);
|
||||
ctx.add_fn(
|
||||
"int64",
|
||||
FnDef {
|
||||
args: args.clone(),
|
||||
result: Some(PrimitiveType(INT64_TYPE).into()),
|
||||
},
|
||||
);
|
||||
ctx.add_fn(
|
||||
"float",
|
||||
FnDef {
|
||||
args: args.clone(),
|
||||
result: Some(PrimitiveType(FLOAT_TYPE).into()),
|
||||
},
|
||||
);
|
||||
|
||||
ctx
|
||||
}
|
223
nac3core/src/typedef.rs
Normal file
223
nac3core/src/typedef.rs
Normal file
@ -0,0 +1,223 @@
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
|
||||
pub struct PrimitiveId(pub(crate) usize);
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
|
||||
pub struct ClassId(pub(crate) usize);
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
|
||||
pub struct ParamId(pub(crate) usize);
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
|
||||
pub struct VariableId(pub(crate) usize);
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
|
||||
pub enum Type {
|
||||
BotType,
|
||||
SelfType,
|
||||
PrimitiveType(PrimitiveId),
|
||||
ClassType(ClassId),
|
||||
VirtualClassType(ClassId),
|
||||
ParametricType(ParamId, Vec<Rc<Type>>),
|
||||
TypeVariable(VariableId),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FnDef {
|
||||
// we assume methods first argument to be SelfType,
|
||||
// so the first argument is not contained here
|
||||
pub args: Vec<Rc<Type>>,
|
||||
pub result: Option<Rc<Type>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TypeDef<'a> {
|
||||
pub name: &'a str,
|
||||
pub fields: HashMap<&'a str, Rc<Type>>,
|
||||
pub methods: HashMap<&'a str, FnDef>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClassDef<'a> {
|
||||
pub base: TypeDef<'a>,
|
||||
pub parents: Vec<ClassId>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ParametricDef<'a> {
|
||||
pub base: TypeDef<'a>,
|
||||
pub params: Vec<VariableId>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VarDef<'a> {
|
||||
pub name: &'a str,
|
||||
pub bound: Vec<Rc<Type>>,
|
||||
}
|
||||
|
||||
pub struct GlobalContext<'a> {
|
||||
primitive_defs: Vec<TypeDef<'a>>,
|
||||
class_defs: Vec<ClassDef<'a>>,
|
||||
parametric_defs: Vec<ParametricDef<'a>>,
|
||||
var_defs: Vec<VarDef<'a>>,
|
||||
sym_table: HashMap<&'a str, Type>,
|
||||
fn_table: HashMap<&'a str, FnDef>,
|
||||
}
|
||||
|
||||
impl<'a> GlobalContext<'a> {
|
||||
pub fn new(primitives: Vec<TypeDef<'a>>) -> GlobalContext {
|
||||
let mut sym_table = HashMap::new();
|
||||
for (i, t) in primitives.iter().enumerate() {
|
||||
sym_table.insert(t.name, Type::PrimitiveType(PrimitiveId(i)));
|
||||
}
|
||||
return GlobalContext {
|
||||
primitive_defs: primitives,
|
||||
class_defs: Vec::new(),
|
||||
parametric_defs: Vec::new(),
|
||||
var_defs: Vec::new(),
|
||||
fn_table: HashMap::new(),
|
||||
sym_table,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add_class(&mut self, def: ClassDef<'a>) -> ClassId {
|
||||
self.sym_table.insert(
|
||||
def.base.name,
|
||||
Type::ClassType(ClassId(self.class_defs.len())),
|
||||
);
|
||||
self.class_defs.push(def);
|
||||
ClassId(self.class_defs.len() - 1)
|
||||
}
|
||||
|
||||
pub fn add_parametric(&mut self, def: ParametricDef<'a>) -> ParamId {
|
||||
let params = def
|
||||
.params
|
||||
.iter()
|
||||
.map(|&v| Rc::new(Type::TypeVariable(v)))
|
||||
.collect();
|
||||
self.sym_table.insert(
|
||||
def.base.name,
|
||||
Type::ParametricType(ParamId(self.parametric_defs.len()), params),
|
||||
);
|
||||
self.parametric_defs.push(def);
|
||||
ParamId(self.parametric_defs.len() - 1)
|
||||
}
|
||||
|
||||
pub fn add_variable(&mut self, def: VarDef<'a>) -> VariableId {
|
||||
self.sym_table.insert(
|
||||
def.name,
|
||||
Type::TypeVariable(VariableId(self.var_defs.len())),
|
||||
);
|
||||
self.add_variable_private(def)
|
||||
}
|
||||
|
||||
pub fn add_variable_private(&mut self, def: VarDef<'a>) -> VariableId {
|
||||
self.var_defs.push(def);
|
||||
VariableId(self.var_defs.len() - 1)
|
||||
}
|
||||
|
||||
pub fn add_fn(&mut self, name: &'a str, def: FnDef) {
|
||||
self.fn_table.insert(name, def);
|
||||
}
|
||||
|
||||
pub fn get_fn(&self, name: &str) -> Option<&FnDef> {
|
||||
self.fn_table.get(name)
|
||||
}
|
||||
|
||||
pub fn get_primitive_mut(&mut self, id: PrimitiveId) -> &mut TypeDef<'a> {
|
||||
self.primitive_defs.get_mut(id.0).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_primitive(&self, id: PrimitiveId) -> &TypeDef {
|
||||
self.primitive_defs.get(id.0).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_class_mut(&mut self, id: ClassId) -> &mut ClassDef<'a> {
|
||||
self.class_defs.get_mut(id.0).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_class(&self, id: ClassId) -> &ClassDef {
|
||||
self.class_defs.get(id.0).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_parametric_mut(&mut self, id: ParamId) -> &mut ParametricDef<'a> {
|
||||
self.parametric_defs.get_mut(id.0).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_parametric(&self, id: ParamId) -> &ParametricDef {
|
||||
self.parametric_defs.get(id.0).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_variable_mut(&mut self, id: VariableId) -> &mut VarDef<'a> {
|
||||
self.var_defs.get_mut(id.0).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_variable(&self, id: VariableId) -> &VarDef {
|
||||
self.var_defs.get(id.0).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_type(&self, name: &str) -> Option<Type> {
|
||||
// TODO: change this to handle import
|
||||
self.sym_table.get(name).map(|v| v.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn subst(&self, map: &HashMap<VariableId, Rc<Type>>) -> Type {
|
||||
match self {
|
||||
Type::TypeVariable(id) => map.get(id).map(|v| v.as_ref()).unwrap_or(self).clone(),
|
||||
Type::ParametricType(id, params) => Type::ParametricType(
|
||||
*id,
|
||||
params
|
||||
.iter()
|
||||
.map(|v| v.as_ref().subst(map).into())
|
||||
.collect(),
|
||||
),
|
||||
_ => self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inv_subst(&self, map: &[(Rc<Type>, Rc<Type>)]) -> Rc<Type> {
|
||||
for (from, to) in map.iter() {
|
||||
if self == from.as_ref() {
|
||||
return to.clone();
|
||||
}
|
||||
}
|
||||
match self {
|
||||
Type::ParametricType(id, params) => Type::ParametricType(
|
||||
*id,
|
||||
params
|
||||
.iter()
|
||||
.map(|v| v.as_ref().inv_subst(map).into())
|
||||
.collect(),
|
||||
),
|
||||
_ => self.clone(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn get_subst(&self, ctx: &GlobalContext) -> HashMap<VariableId, Rc<Type>> {
|
||||
match self {
|
||||
Type::ParametricType(id, params) => {
|
||||
let vars = &ctx.get_parametric(*id).params;
|
||||
vars.iter()
|
||||
.zip(params)
|
||||
.map(|(v, p)| (*v, p.as_ref().clone().into()))
|
||||
.collect()
|
||||
}
|
||||
// if this proves to be slow, we can use option type
|
||||
_ => HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_base<'b: 'a, 'a>(&'a self, ctx: &'b GlobalContext) -> Option<&'b TypeDef> {
|
||||
match self {
|
||||
Type::PrimitiveType(id) => Some(ctx.get_primitive(*id)),
|
||||
Type::ClassType(id) | Type::VirtualClassType(id) => Some(&ctx.get_class(*id).base),
|
||||
Type::ParametricType(id, _) => Some(&ctx.get_parametric(*id).base),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user