Compare commits

..

21 Commits

Author SHA1 Message Date
95789ec303 proper constant handling 2020-12-30 10:45:35 +08:00
03712e3762 code formatting 2020-12-30 09:40:56 +08:00
b98117a049 removed assumptions 2020-12-29 17:04:45 +08:00
7027135d6b finished expressions 2020-12-29 16:43:08 +08:00
a439ce61f7 ownwership and none type 2020-12-29 14:47:41 +08:00
1cc4c3f8d4 added operators 2020-12-29 14:28:18 +08:00
7b1ea58bc0 implemented some more expressions 2020-12-29 12:28:18 +08:00
06af1623a8 fixed errors 2020-12-29 11:42:16 +08:00
1990486cc2 started expression checking 2020-12-29 11:31:00 +08:00
a9827da70b moved nac3type to nac3core 2020-12-29 10:33:21 +08:00
94ea2c7a9d fixed assumption bug 2020-12-29 09:43:49 +08:00
422b92f686 use slice for function parameters 2020-12-28 16:26:10 +08:00
0200ea1458 formatting 2020-12-28 16:00:02 +08:00
75183c39fd virtual class tests 2020-12-28 15:58:28 +08:00
87dd0ee3cb more tests 2020-12-28 15:03:57 +08:00
929b7e1d92 inference and tests 2020-12-28 14:33:18 +08:00
7f09596bcb primitives and some lifetime issue 2020-12-28 13:15:46 +08:00
81f4be60c7 start adding primitives 2020-12-28 12:45:35 +08:00
fd3e1d4923 implemented inference
rc nightmare...
2020-12-28 11:06:32 +08:00
fa02dc8271 implementing inference 2020-12-26 11:45:57 +08:00
0bca238642 init type checking module 2020-12-24 16:06:16 +08:00
18 changed files with 1167 additions and 2988 deletions

33
Cargo.lock generated
View File

@ -288,15 +288,6 @@ dependencies = [
"proc-macro-hack", "proc-macro-hack",
] ]
[[package]]
name = "indoc"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136"
dependencies = [
"unindent",
]
[[package]] [[package]]
name = "indoc-impl" name = "indoc-impl"
version = "0.3.6" version = "0.3.6"
@ -458,12 +449,10 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
name = "nac3core" name = "nac3core"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"indoc 1.0.3",
"inkwell", "inkwell",
"num-bigint", "num-bigint",
"num-traits", "num-traits",
"rustpython-parser", "rustpython-parser",
"thiserror",
] ]
[[package]] [[package]]
@ -624,7 +613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf6bbbe8f70d179260b3728e5d04eb012f4f0c7988e58c11433dd689cecaa72e" checksum = "bf6bbbe8f70d179260b3728e5d04eb012f4f0c7988e58c11433dd689cecaa72e"
dependencies = [ dependencies = [
"ctor", "ctor",
"indoc 0.3.6", "indoc",
"inventory", "inventory",
"libc", "libc",
"parking_lot", "parking_lot",
@ -841,26 +830,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "thiserror"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.0.1" version = "1.0.1"

View File

@ -7,10 +7,5 @@ edition = "2018"
[dependencies] [dependencies]
num-bigint = "0.3" num-bigint = "0.3"
num-traits = "0.2" num-traits = "0.2"
thiserror = "1.0"
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm10-0"] } inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm10-0"] }
rustpython-parser = { git = "https://github.com/RustPython/RustPython", branch = "master" } rustpython-parser = { git = "https://github.com/RustPython/RustPython", branch = "master" }
[dev-dependencies]
indoc = "1.0"

377
nac3core/src/expression.rs Normal file
View File

@ -0,0 +1,377 @@
use crate::inference::resolve_call;
use crate::operators::*;
use crate::primitives::*;
use crate::typedef::{GlobalContext, Type, Type::*};
use rustpython_parser::ast::{
Comparison, Comprehension, ComprehensionKind, Expression, ExpressionType, Operator,
UnaryOperator,
};
use std::collections::HashMap;
use std::convert::TryInto;
use std::rc::Rc;
type SymTable<'a> = HashMap<&'a str, Rc<Type>>;
type ParserResult = Result<Option<Rc<Type>>, String>;
pub fn parse_expr(ctx: &GlobalContext, sym_table: &SymTable, expr: &Expression) -> ParserResult {
match &expr.node {
ExpressionType::Number { value } => parse_constant(ctx, sym_table, value),
ExpressionType::Identifier { name } => parse_identifier(ctx, sym_table, name),
ExpressionType::List { elements } => parse_list(ctx, sym_table, elements),
ExpressionType::Tuple { elements } => parse_tuple(ctx, sym_table, elements),
ExpressionType::Attribute { value, name } => parse_attribute(ctx, sym_table, value, name),
ExpressionType::BoolOp { values, .. } => parse_bool_ops(ctx, sym_table, values),
ExpressionType::Binop { a, b, op } => parse_bin_ops(ctx, sym_table, op, a, b),
ExpressionType::Unop { op, a } => parse_unary_ops(ctx, sym_table, op, a),
ExpressionType::Compare { vals, ops } => parse_compare(ctx, sym_table, vals, ops),
ExpressionType::Call {
args,
function,
keywords,
} => {
if keywords.len() > 0 {
Err("keyword is not supported".into())
} else {
parse_call(ctx, sym_table, &args, &function)
}
}
ExpressionType::Subscript { a, b } => parse_subscript(ctx, sym_table, a, b),
ExpressionType::IfExpression { test, body, orelse } => {
parse_if_expr(ctx, sym_table, &test, &body, orelse)
}
ExpressionType::Comprehension { kind, generators } => match kind.as_ref() {
ComprehensionKind::List { element } => {
if generators.len() == 1 {
parse_list_comprehension(ctx, sym_table, element, &generators[0])
} else {
Err("only 1 generator statement is supported".into())
}
}
_ => Err("only list comprehension is supported".into()),
},
ExpressionType::True | ExpressionType::False => Ok(Some(PrimitiveType(BOOL_TYPE).into())),
_ => Err("not supported".into()),
}
}
fn parse_constant(
_: &GlobalContext,
_: &SymTable,
value: &rustpython_parser::ast::Number,
) -> ParserResult {
use rustpython_parser::ast::Number;
match value {
Number::Integer { value } => {
let int32: Result<i32, _> = value.try_into();
if int32.is_ok() {
Ok(Some(PrimitiveType(INT32_TYPE).into()))
} else {
let int64: Result<i64, _> = value.try_into();
if int64.is_ok() {
Ok(Some(PrimitiveType(INT64_TYPE).into()))
} else {
Err("integer out of range".into())
}
}
}
Number::Float { .. } => Ok(Some(PrimitiveType(FLOAT_TYPE).into())),
_ => Err("not supported".into()),
}
}
fn parse_identifier(_: &GlobalContext, sym_table: &SymTable, name: &str) -> ParserResult {
match sym_table.get(name) {
Some(v) => Ok(Some(v.clone())),
None => Err("unbounded variable".into()),
}
}
fn parse_list(ctx: &GlobalContext, sym_table: &SymTable, elements: &[Expression]) -> ParserResult {
if elements.len() == 0 {
return Ok(Some(ParametricType(LIST_TYPE, vec![BotType.into()]).into()));
}
let mut types = elements.iter().map(|v| parse_expr(&ctx, sym_table, v));
let head = types.next().unwrap()?;
if head.is_none() {
return Err("list elements must have some type".into());
}
for v in types {
if v? != head {
return Err("inhomogeneous list is not allowed".into());
}
}
Ok(Some(ParametricType(LIST_TYPE, vec![head.unwrap()]).into()))
}
fn parse_tuple(ctx: &GlobalContext, sym_table: &SymTable, elements: &[Expression]) -> ParserResult {
let types: Result<Option<Vec<_>>, String> = elements
.iter()
.map(|v| parse_expr(&ctx, sym_table, v))
.collect();
if let Some(t) = types? {
Ok(Some(ParametricType(TUPLE_TYPE, t).into()))
} else {
Err("tuple elements must have some type".into())
}
}
fn parse_attribute(
ctx: &GlobalContext,
sym_table: &SymTable,
value: &Expression,
name: &String,
) -> ParserResult {
let value = parse_expr(ctx, sym_table, value)?.ok_or("no value".to_string())?;
if let TypeVariable(id) = value.as_ref() {
let v = ctx.get_variable(*id);
if v.bound.len() == 0 {
return Err("no fields on unbounded type variable".into());
}
let ty = v.bound[0]
.get_base(ctx)
.and_then(|v| v.fields.get(name.as_str()));
if ty.is_none() {
return Err("unknown field".into());
}
for x in v.bound[1..].iter() {
let ty1 = x.get_base(ctx).and_then(|v| v.fields.get(name.as_str()));
if ty1 != ty {
return Err("unknown field (type mismatch between variants)".into());
}
}
return Ok(Some(ty.unwrap().clone()));
}
match value.get_base(ctx) {
Some(b) => match b.fields.get(name.as_str()) {
Some(t) => Ok(Some(t.clone())),
None => Err("no such field".into()),
},
None => Err("this object has no fields".into()),
}
}
fn parse_bool_ops(
ctx: &GlobalContext,
sym_table: &SymTable,
values: &[Expression],
) -> ParserResult {
assert_eq!(values.len(), 2);
let left = parse_expr(ctx, sym_table, &values[0])?.ok_or("no value".to_string())?;
let right = parse_expr(ctx, sym_table, &values[1])?.ok_or("no value".to_string())?;
let b = PrimitiveType(BOOL_TYPE);
if left.as_ref() == &b && right.as_ref() == &b {
Ok(Some(b.into()))
} else {
Err("bool operands must be bool".into())
}
}
fn parse_bin_ops(
ctx: &GlobalContext,
sym_table: &SymTable,
op: &Operator,
left: &Expression,
right: &Expression,
) -> ParserResult {
let left = parse_expr(ctx, sym_table, left)?.ok_or("no value".to_string())?;
let right = parse_expr(ctx, sym_table, right)?.ok_or("no value".to_string())?;
let fun = binop_name(op);
resolve_call(ctx, Some(left), fun, &[right])
}
fn parse_unary_ops(
ctx: &GlobalContext,
sym_table: &SymTable,
op: &UnaryOperator,
obj: &Expression,
) -> ParserResult {
let ty = parse_expr(ctx, sym_table, obj)?.ok_or("no value".to_string())?;
if let UnaryOperator::Not = op {
if ty.as_ref() == &PrimitiveType(BOOL_TYPE) {
Ok(Some(ty))
} else {
Err("logical not must be applied to bool".into())
}
} else {
resolve_call(ctx, Some(ty), unaryop_name(op), &[])
}
}
fn parse_compare(
ctx: &GlobalContext,
sym_table: &SymTable,
vals: &[Expression],
ops: &[Comparison],
) -> ParserResult {
let types: Result<Option<Vec<_>>, _> =
vals.iter().map(|v| parse_expr(ctx, sym_table, v)).collect();
let types = types?;
if types.is_none() {
return Err("comparison operands must have type".into());
}
let types = types.unwrap();
let boolean = PrimitiveType(BOOL_TYPE);
let left = &types[..types.len() - 1];
let right = &types[1..];
for ((a, b), op) in left.iter().zip(right.iter()).zip(ops.iter()) {
let fun = comparison_name(op).ok_or("unsupported comparison".to_string())?;
let ty = resolve_call(ctx, Some(a.clone()), fun, &[b.clone()])?;
if ty.is_none() || ty.unwrap().as_ref() != &boolean {
return Err("comparison result must be boolean".into());
}
}
Ok(Some(boolean.into()))
}
fn parse_call(
ctx: &GlobalContext,
sym_table: &SymTable,
args: &[Expression],
function: &Expression,
) -> ParserResult {
let types: Result<Option<Vec<_>>, _> =
args.iter().map(|v| parse_expr(ctx, sym_table, v)).collect();
let types = types?;
if types.is_none() {
return Err("function params must have type".into());
}
let (obj, fun) = match &function.node {
ExpressionType::Identifier { name } => (None, name),
ExpressionType::Attribute { value, name } => (
Some(parse_expr(ctx, sym_table, &value)?.ok_or("no value".to_string())?),
name,
),
_ => return Err("not supported".into()),
};
resolve_call(ctx, obj, fun.as_str(), &types.unwrap())
}
fn parse_subscript(
ctx: &GlobalContext,
sym_table: &SymTable,
a: &Expression,
b: &Expression,
) -> ParserResult {
let a = parse_expr(ctx, sym_table, a)?.ok_or("no value".to_string())?;
let t = if let ParametricType(LIST_TYPE, ls) = a.as_ref() {
ls[0].clone()
} else {
return Err("subscript is not supported for types other than list".into());
};
match &b.node {
ExpressionType::Slice { elements } => {
let types: Result<Option<Vec<_>>, _> = elements
.iter()
.map(|v| parse_expr(ctx, sym_table, v))
.collect();
let types = types?.ok_or("slice must have type".to_string())?;
let int32 = PrimitiveType(INT32_TYPE);
if types.iter().all(|v| v.as_ref() == &int32) {
Ok(Some(a))
} else {
Err("slice must be int32 type".into())
}
}
_ => {
let b = parse_expr(ctx, sym_table, b)?.ok_or("no value".to_string())?;
if b.as_ref() == &PrimitiveType(INT32_TYPE) {
Ok(Some(t))
} else {
Err("index must be either slice or int32".into())
}
}
}
}
fn parse_if_expr(
ctx: &GlobalContext,
sym_table: &SymTable,
test: &Expression,
body: &Expression,
orelse: &Expression,
) -> ParserResult {
let test = parse_expr(ctx, sym_table, test)?.ok_or("no value".to_string())?;
if test.as_ref() != &PrimitiveType(BOOL_TYPE) {
return Err("test should be bool".into());
}
let body = parse_expr(ctx, sym_table, body)?.ok_or("no value".to_string())?;
let orelse = parse_expr(ctx, sym_table, orelse)?.ok_or("no value".to_string())?;
if body.as_ref() == orelse.as_ref() {
Ok(Some(body))
} else {
Err("divergent type".into())
}
}
fn parse_simple_binding<'a: 'b, 'b>(
sym_table: &mut SymTable<'b>,
name: &'a Expression,
ty: Rc<Type>,
) -> Result<(), String> {
match &name.node {
ExpressionType::Identifier { name } => {
if name == "_" {
Ok(())
} else if sym_table.get(name.as_str()).is_some() {
Err("duplicated naming".into())
} else {
sym_table.insert(name.as_str(), ty);
Ok(())
}
}
ExpressionType::Tuple { elements } => {
if let ParametricType(TUPLE_TYPE, ls) = ty.as_ref() {
if elements.len() == ls.len() {
for (a, b) in elements.iter().zip(ls.iter()) {
parse_simple_binding(sym_table, a, b.clone())?;
}
Ok(())
} else {
Err("different length".into())
}
} else {
Err("not supported".into())
}
}
_ => Err("not supported".into()),
}
}
fn parse_list_comprehension(
ctx: &GlobalContext,
sym_table: &SymTable,
element: &Expression,
comprehension: &Comprehension,
) -> ParserResult {
if comprehension.is_async {
return Err("async is not supported".into());
}
// TODO: it may be more efficient to use multi-level table
// but it would better done in a whole program level
let iter = parse_expr(ctx, sym_table, &comprehension.iter)?.ok_or("no value".to_string())?;
if let ParametricType(LIST_TYPE, ls) = iter.as_ref() {
let mut local_sym = sym_table.clone();
parse_simple_binding(&mut local_sym, &comprehension.target, ls[0].clone())?;
let boolean = PrimitiveType(BOOL_TYPE);
for test in comprehension.ifs.iter() {
let result =
parse_expr(ctx, &local_sym, test)?.ok_or("no value in test".to_string())?;
if result.as_ref() != &boolean {
return Err("test must be bool".into());
}
}
parse_expr(ctx, &local_sym, element)
} else {
Err("iteration is supported for list only".into())
}
}

View File

@ -1,29 +1,15 @@
use super::context::InferenceContext; use super::typedef::{Type::*, *};
use super::typedef::{TypeEnum::*, *};
use std::collections::HashMap; use std::collections::HashMap;
use thiserror::Error; use std::rc::Rc;
#[derive(Error, Debug)]
enum SubstError {
#[error("different type variables after substitution")]
DifferentSubstVar(VariableId, VariableId),
#[error("cannot substitute unbounded type variable into bounded one")]
UnboundedTypeVar(VariableId, VariableId),
#[error("incompatible bound for type variables")]
IncompatibleBound(VariableId, VariableId),
#[error("only subtype of virtual class can be substituted into virtual class type")]
NotVirtualClassSubtype(Type, ClassId),
#[error("different types")]
DifferentTypes(Type, Type),
}
fn find_subst( fn find_subst(
ctx: &InferenceContext, ctx: &GlobalContext,
valuation: &Option<(VariableId, Type)>, valuation: &Option<(VariableId, Rc<Type>)>,
sub: &mut HashMap<VariableId, Type>, sub: &mut HashMap<VariableId, Rc<Type>>,
mut a: Type, mut a: Rc<Type>,
mut b: Type, mut b: Rc<Type>,
) -> Result<(), SubstError> { ) -> Result<(), String> {
// TODO: fix error messages later
if let TypeVariable(id) = a.as_ref() { if let TypeVariable(id) = a.as_ref() {
if let Some((assumption_id, t)) = valuation { if let Some((assumption_id, t)) = valuation {
if assumption_id == id { if assumption_id == id {
@ -47,43 +33,43 @@ fn find_subst(
return if id_a == id_b { return if id_a == id_b {
Ok(()) Ok(())
} else { } else {
Err(SubstError::DifferentSubstVar(*id_a, *id_b)) Err("different variables".to_string())
}; };
} }
let v_a = ctx.get_variable_def(*id_a); let v_a = ctx.get_variable(*id_a);
let v_b = ctx.get_variable_def(*id_b); let v_b = ctx.get_variable(*id_b);
if !v_b.bound.is_empty() { if v_b.bound.len() > 0 {
if v_a.bound.is_empty() { if v_a.bound.len() == 0 {
return Err(SubstError::UnboundedTypeVar(*id_a, *id_b)); return Err("unbounded a".to_string());
} else { } else {
let diff: Vec<_> = v_a let diff: Vec<_> = v_a
.bound .bound
.iter() .iter()
.filter(|x| !v_b.bound.contains(x)) .filter(|x| !v_b.bound.contains(x))
.collect(); .collect();
if !diff.is_empty() { if diff.len() > 0 {
return Err(SubstError::IncompatibleBound(*id_a, *id_b)); return Err("different domain".to_string());
} }
} }
} }
sub.insert(*id_b, a.clone()); sub.insert(*id_b, a.clone().into());
Ok(()) Ok(())
} }
(TypeVariable(id_a), _) => { (TypeVariable(id_a), _) => {
let v_a = ctx.get_variable_def(*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() { if v_a.bound.len() == 1 && v_a.bound[0].as_ref() == b.as_ref() {
Ok(()) Ok(())
} else { } else {
Err(SubstError::DifferentTypes(a.clone(), b.clone())) Err("different domain".to_string())
} }
} }
(_, TypeVariable(id_b)) => { (_, TypeVariable(id_b)) => {
let v_b = ctx.get_variable_def(*id_b); let v_b = ctx.get_variable(*id_b);
if v_b.bound.is_empty() || v_b.bound.contains(&a) { if v_b.bound.len() == 0 || v_b.bound.contains(&a) {
sub.insert(*id_b, a.clone()); sub.insert(*id_b, a.clone().into());
Ok(()) Ok(())
} else { } else {
Err(SubstError::DifferentTypes(a.clone(), b.clone())) Err("different domain".to_string())
} }
} }
(_, VirtualClassType(id_b)) => { (_, VirtualClassType(id_b)) => {
@ -96,21 +82,21 @@ fn find_subst(
parents = [*id_a].to_vec(); parents = [*id_a].to_vec();
} }
_ => { _ => {
return Err(SubstError::NotVirtualClassSubtype(a.clone(), *id_b)); return Err("cannot substitute non-class type into virtual class".to_string());
} }
}; };
while !parents.is_empty() { while !parents.is_empty() {
if *id_b == parents[0] { if *id_b == parents[0] {
return Ok(()); return Ok(());
} }
let c = ctx.get_class_def(parents.remove(0)); let c = ctx.get_class(parents.remove(0));
parents.extend_from_slice(&c.parents); parents.extend_from_slice(&c.parents);
} }
Err(SubstError::NotVirtualClassSubtype(a.clone(), *id_b)) Err("not subtype".to_string())
} }
(ParametricType(id_a, param_a), ParametricType(id_b, param_b)) => { (ParametricType(id_a, param_a), ParametricType(id_b, param_b)) => {
if id_a != id_b || param_a.len() != param_b.len() { if id_a != id_b || param_a.len() != param_b.len() {
Err(SubstError::DifferentTypes(a.clone(), b.clone())) Err("different parametric types".to_string())
} else { } else {
for (x, y) in param_a.iter().zip(param_b.iter()) { for (x, y) in param_a.iter().zip(param_b.iter()) {
find_subst(ctx, valuation, sub, x.clone(), y.clone())?; find_subst(ctx, valuation, sub, x.clone(), y.clone())?;
@ -122,30 +108,30 @@ fn find_subst(
if a == b { if a == b {
Ok(()) Ok(())
} else { } else {
Err(SubstError::DifferentTypes(a.clone(), b.clone())) Err("not equal".to_string())
} }
} }
} }
} }
fn resolve_call_rec( fn resolve_call_rec(
ctx: &InferenceContext, ctx: &GlobalContext,
valuation: &Option<(VariableId, Type)>, valuation: &Option<(VariableId, Rc<Type>)>,
obj: Option<Type>, obj: Option<Rc<Type>>,
func: &str, func: &str,
args: &[Type], args: &[Rc<Type>],
) -> Result<Option<Type>, String> { ) -> Result<Option<Rc<Type>>, String> {
let mut subst = obj let mut subst = obj
.as_ref() .as_ref()
.map(|v| v.get_subst(ctx)) .map(|v| v.get_subst(ctx))
.unwrap_or_else(HashMap::new); .unwrap_or(HashMap::new());
let fun = match &obj { let fun = match &obj {
Some(obj) => { Some(obj) => {
let base = match obj.as_ref() { let base = match obj.as_ref() {
TypeVariable(id) => { TypeVariable(id) => {
let v = ctx.get_variable_def(*id); let v = ctx.get_variable(*id);
if v.bound.is_empty() { if v.bound.len() == 0 {
return Err("unbounded type var".to_string()); return Err("unbounded type var".to_string());
} }
let results: Result<Vec<_>, String> = v let results: Result<Vec<_>, String> = v
@ -167,7 +153,7 @@ fn resolve_call_rec(
} }
let mut results = results.iter().zip(v.bound.iter()).map(|(r, ins)| { let mut results = results.iter().zip(v.bound.iter()).map(|(r, ins)| {
r.as_ref() r.as_ref()
.map(|v| v.inv_subst(&[(ins.clone(), obj.clone())])) .map(|v| v.inv_subst(&[(ins.clone(), obj.clone().into())]))
}); });
let first = results.next().unwrap(); let first = results.next().unwrap();
if results.all(|v| v == first) { if results.all(|v| v == first) {
@ -176,22 +162,22 @@ fn resolve_call_rec(
return Err("divergent type after substitution".to_string()); return Err("divergent type after substitution".to_string());
} }
} }
PrimitiveType(id) => &ctx.get_primitive_def(*id), PrimitiveType(id) => &ctx.get_primitive(*id),
ClassType(id) | VirtualClassType(id) => &ctx.get_class_def(*id).base, ClassType(id) | VirtualClassType(id) => &ctx.get_class(*id).base,
ParametricType(id, _) => &ctx.get_parametric_def(*id).base, ParametricType(id, _) => &ctx.get_parametric(*id).base,
_ => return Err("not supported".to_string()), _ => return Err("not supported".to_string()),
}; };
base.methods.get(func) base.methods.get(func)
} }
None => ctx.get_fn_def(func), None => ctx.get_fn(func),
} }
.ok_or_else(|| "no such function".to_string())?; .ok_or("no such function".to_string())?;
if args.len() != fun.args.len() { if args.len() != fun.args.len() {
return Err("incorrect parameter number".to_string()); return Err("incorrect parameter number".to_string());
} }
for (a, b) in args.iter().zip(fun.args.iter()) { for (a, b) in args.iter().zip(fun.args.iter()) {
find_subst(ctx, valuation, &mut subst, a.clone(), b.clone()).map_err(|v| v.to_string())?; find_subst(ctx, valuation, &mut subst, a.clone(), b.clone())?;
} }
let result = fun.result.as_ref().map(|v| v.subst(&subst)); let result = fun.result.as_ref().map(|v| v.subst(&subst));
Ok(result.map(|result| { Ok(result.map(|result| {
@ -204,114 +190,109 @@ fn resolve_call_rec(
} }
pub fn resolve_call( pub fn resolve_call(
ctx: &InferenceContext, ctx: &GlobalContext,
obj: Option<Type>, obj: Option<Rc<Type>>,
func: &str, func: &str,
args: &[Type], args: &[Rc<Type>],
) -> Result<Option<Type>, String> { ) -> Result<Option<Rc<Type>>, String> {
resolve_call_rec(ctx, &None, obj, func, args) resolve_call_rec(ctx, &None, obj, func, args)
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{ use super::*;
super::{context::*, primitives::*}, use crate::primitives::*;
*,
};
use std::matches;
use std::rc::Rc;
fn get_inference_context(ctx: TopLevelContext) -> InferenceContext {
InferenceContext::new(ctx, Box::new(|_| Err("unbounded identifier".into())))
}
#[test] #[test]
fn test_simple_generic() { fn test_simple_generic() {
let mut ctx = basic_ctx(); 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 { let v1 = ctx.add_variable(VarDef {
name: "V1", name: "V1",
bound: vec![ctx.get_primitive(INT32_TYPE), ctx.get_primitive(FLOAT_TYPE)], bound: vec![
PrimitiveType(INT32_TYPE).into(),
PrimitiveType(FLOAT_TYPE).into(),
],
}); });
let v1 = ctx.get_variable(v1);
assert_eq!(
resolve_call(&ctx, None, "float", &[TypeVariable(v1).into()]),
Ok(Some(PrimitiveType(FLOAT_TYPE).into()))
);
let v2 = ctx.add_variable(VarDef { let v2 = ctx.add_variable(VarDef {
name: "V2", name: "V2",
bound: vec![ bound: vec![
ctx.get_primitive(BOOL_TYPE), PrimitiveType(BOOL_TYPE).into(),
ctx.get_primitive(INT32_TYPE), PrimitiveType(INT32_TYPE).into(),
ctx.get_primitive(FLOAT_TYPE), PrimitiveType(FLOAT_TYPE).into(),
], ],
}); });
let v2 = ctx.get_variable(v2);
let ctx = get_inference_context(ctx);
assert_eq!( assert_eq!(
resolve_call(&ctx, None, "int32", &[ctx.get_primitive(FLOAT_TYPE)]), resolve_call(&ctx, None, "float", &[TypeVariable(v2).into()]),
Ok(Some(ctx.get_primitive(INT32_TYPE))) Err("different domain".to_string())
); );
assert_eq!(
resolve_call(&ctx, None, "int32", &[ctx.get_primitive(INT32_TYPE)],),
Ok(Some(ctx.get_primitive(INT32_TYPE)))
);
assert_eq!(
resolve_call(&ctx, None, "float", &[ctx.get_primitive(INT32_TYPE)]),
Ok(Some(ctx.get_primitive(FLOAT_TYPE)))
);
assert!(matches!(
resolve_call(&ctx, None, "float", &[ctx.get_primitive(BOOL_TYPE)]),
Err(..)
));
assert!(matches!(
resolve_call(&ctx, None, "float", &[]),
Err(..)
));
assert_eq!(
resolve_call(&ctx, None, "float", &[v1]),
Ok(Some(ctx.get_primitive(FLOAT_TYPE)))
);
assert!(matches!(
resolve_call(&ctx, None, "float", &[v2]),
Err(..)
));
} }
#[test] #[test]
fn test_methods() { fn test_methods() {
let mut ctx = basic_ctx(); let mut ctx = basic_ctx();
let v0 = ctx.add_variable(VarDef { let v0 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
name: "V0", name: "V0",
bound: vec![], bound: vec![],
}); })));
let v0 = ctx.get_variable(v0); let v1 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
let v1 = ctx.add_variable(VarDef {
name: "V1", name: "V1",
bound: vec![ctx.get_primitive(INT32_TYPE), ctx.get_primitive(FLOAT_TYPE)], bound: vec![
}); PrimitiveType(INT32_TYPE).into(),
let v1 = ctx.get_variable(v1); PrimitiveType(FLOAT_TYPE).into(),
let v2 = ctx.add_variable(VarDef { ],
})));
let v2 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
name: "V2", name: "V2",
bound: vec![ctx.get_primitive(INT32_TYPE), ctx.get_primitive(FLOAT_TYPE)], bound: vec![
}); PrimitiveType(INT32_TYPE).into(),
let v2 = ctx.get_variable(v2); PrimitiveType(FLOAT_TYPE).into(),
let v3 = ctx.add_variable(VarDef { ],
})));
let v3 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
name: "V3", name: "V3",
bound: vec![ bound: vec![
ctx.get_primitive(BOOL_TYPE), PrimitiveType(BOOL_TYPE).into(),
ctx.get_primitive(INT32_TYPE), PrimitiveType(INT32_TYPE).into(),
ctx.get_primitive(FLOAT_TYPE), PrimitiveType(FLOAT_TYPE).into(),
], ],
}); })));
let v3 = ctx.get_variable(v3);
let int32 = ctx.get_primitive(INT32_TYPE); let int32 = Rc::new(PrimitiveType(INT32_TYPE));
let int64 = ctx.get_primitive(INT64_TYPE); let int64 = Rc::new(PrimitiveType(INT64_TYPE));
let ctx = get_inference_context(ctx);
// simple cases // simple cases
assert_eq!( assert_eq!(
@ -324,65 +305,61 @@ mod tests {
Ok(Some(int64.clone())) Ok(Some(int64.clone()))
); );
assert!(matches!( assert_eq!(
resolve_call(&ctx, Some(int32), "__add__", &[int64]), resolve_call(&ctx, Some(int32.clone()), "__add__", &[int64.clone()]),
Err(..) Err("not equal".to_string())
)); );
// with type variables // with type variables
assert_eq!( assert_eq!(
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v1.clone()]), resolve_call(&ctx, Some(v1.clone()), "__add__", &[v1.clone()]),
Ok(Some(v1.clone())) Ok(Some(v1.clone()))
); );
assert!(matches!( assert_eq!(
resolve_call(&ctx, Some(v0.clone()), "__add__", &[v2.clone()]), resolve_call(&ctx, Some(v0.clone()), "__add__", &[v2.clone()]),
Err(..) Err("unbounded type var".to_string())
)); );
assert!(matches!( assert_eq!(
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v0]), resolve_call(&ctx, Some(v1.clone()), "__add__", &[v0.clone()]),
Err(..) Err("different domain".to_string())
)); );
assert!(matches!( assert_eq!(
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v2]), resolve_call(&ctx, Some(v1.clone()), "__add__", &[v2.clone()]),
Err(..) Err("different domain".to_string())
)); );
assert!(matches!( assert_eq!(
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v3.clone()]), resolve_call(&ctx, Some(v1.clone()), "__add__", &[v3.clone()]),
Err(..) Err("different domain".to_string())
)); );
assert!(matches!( assert_eq!(
resolve_call(&ctx, Some(v3.clone()), "__add__", &[v1]), resolve_call(&ctx, Some(v3.clone()), "__add__", &[v1.clone()]),
Err(..) Err("no such function".to_string())
)); );
assert!(matches!( assert_eq!(
resolve_call(&ctx, Some(v3.clone()), "__add__", &[v3]), resolve_call(&ctx, Some(v3.clone()), "__add__", &[v3.clone()]),
Err(..) Err("no such function".to_string())
)); );
} }
#[test] #[test]
fn test_multi_generic() { fn test_multi_generic() {
let mut ctx = basic_ctx(); let mut ctx = basic_ctx();
let v0 = ctx.add_variable(VarDef { let v0 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
name: "V0", name: "V0",
bound: vec![], bound: vec![],
}); })));
let v0 = ctx.get_variable(v0); let v1 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
let v1 = ctx.add_variable(VarDef {
name: "V1", name: "V1",
bound: vec![], bound: vec![],
}); })));
let v1 = ctx.get_variable(v1); let v2 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
let v2 = ctx.add_variable(VarDef {
name: "V2", name: "V2",
bound: vec![], bound: vec![],
}); })));
let v2 = ctx.get_variable(v2); let v3 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
let v3 = ctx.add_variable(VarDef {
name: "V3", name: "V3",
bound: vec![], bound: vec![],
}); })));
let v3 = ctx.get_variable(v3);
ctx.add_fn( ctx.add_fn(
"foo", "foo",
@ -395,11 +372,12 @@ mod tests {
ctx.add_fn( ctx.add_fn(
"foo1", "foo1",
FnDef { FnDef {
args: vec![ParametricType(TUPLE_TYPE, vec![v0.clone(), v0.clone(), v1]).into()], args: vec![
result: Some(v0), ParametricType(TUPLE_TYPE, vec![v0.clone(), v0.clone(), v1.clone()]).into(),
],
result: Some(v0.clone()),
}, },
); );
let ctx = get_inference_context(ctx);
assert_eq!( assert_eq!(
resolve_call(&ctx, None, "foo", &[v2.clone(), v2.clone(), v2.clone()]), resolve_call(&ctx, None, "foo", &[v2.clone(), v2.clone(), v2.clone()]),
@ -409,10 +387,10 @@ mod tests {
resolve_call(&ctx, None, "foo", &[v2.clone(), v2.clone(), v3.clone()]), resolve_call(&ctx, None, "foo", &[v2.clone(), v2.clone(), v3.clone()]),
Ok(Some(v2.clone())) Ok(Some(v2.clone()))
); );
assert!(matches!( assert_eq!(
resolve_call(&ctx, None, "foo", &[v2.clone(), v3.clone(), v3.clone()]), resolve_call(&ctx, None, "foo", &[v2.clone(), v3.clone(), v3.clone()]),
Err(..) Err("different variables".to_string())
)); );
assert_eq!( assert_eq!(
resolve_call( resolve_call(
@ -432,22 +410,22 @@ mod tests {
), ),
Ok(Some(v2.clone())) Ok(Some(v2.clone()))
); );
assert!(matches!( assert_eq!(
resolve_call( resolve_call(
&ctx, &ctx,
None, None,
"foo1", "foo1",
&[ParametricType(TUPLE_TYPE, vec![v2, v3.clone(), v3]).into()] &[ParametricType(TUPLE_TYPE, vec![v2.clone(), v3.clone(), v3.clone()]).into()]
), ),
Err(..) Err("different variables".to_string())
)); );
} }
#[test] #[test]
fn test_class_generics() { fn test_class_generics() {
let mut ctx = basic_ctx(); let mut ctx = basic_ctx();
let list = ctx.get_parametric_def_mut(LIST_TYPE); let list = ctx.get_parametric_mut(LIST_TYPE);
let t = Rc::new(TypeVariable(list.params[0])); let t = Rc::new(TypeVariable(list.params[0]));
list.base.methods.insert( list.base.methods.insert(
"head", "head",
@ -459,22 +437,19 @@ mod tests {
list.base.methods.insert( list.base.methods.insert(
"append", "append",
FnDef { FnDef {
args: vec![t], args: vec![t.clone()],
result: None, result: None,
}, },
); );
let v0 = ctx.add_variable(VarDef { let v0 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
name: "V0", name: "V0",
bound: vec![], bound: vec![],
}); })));
let v0 = ctx.get_variable(v0); let v1 = Rc::new(TypeVariable(ctx.add_variable(VarDef {
let v1 = ctx.add_variable(VarDef {
name: "V1", name: "V1",
bound: vec![], bound: vec![],
}); })));
let v1 = ctx.get_variable(v1);
let ctx = get_inference_context(ctx);
assert_eq!( assert_eq!(
resolve_call( resolve_call(
@ -494,15 +469,15 @@ mod tests {
), ),
Ok(None) Ok(None)
); );
assert!(matches!( assert_eq!(
resolve_call( resolve_call(
&ctx, &ctx,
Some(ParametricType(LIST_TYPE, vec![v0]).into()), Some(ParametricType(LIST_TYPE, vec![v0.clone()]).into()),
"append", "append",
&[v1] &[v1.clone()]
), ),
Err(..) Err("different variables".to_string())
)); );
} }
#[test] #[test]
@ -559,7 +534,6 @@ mod tests {
result: None, result: None,
}, },
); );
let ctx = get_inference_context(ctx);
assert_eq!( assert_eq!(
resolve_call(&ctx, None, "foo", &[ClassType(foo).into()]), resolve_call(&ctx, None, "foo", &[ClassType(foo).into()]),
@ -576,10 +550,10 @@ mod tests {
Ok(None) Ok(None)
); );
assert!(matches!( assert_eq!(
resolve_call(&ctx, None, "foo", &[ClassType(bar).into()]), resolve_call(&ctx, None, "foo", &[ClassType(bar).into()]),
Err(..) Err("not subtype".to_string())
)); );
assert_eq!( assert_eq!(
resolve_call(&ctx, None, "foo1", &[ClassType(foo1).into()]), resolve_call(&ctx, None, "foo1", &[ClassType(foo1).into()]),
@ -591,10 +565,10 @@ mod tests {
Ok(None) Ok(None)
); );
assert!(matches!( assert_eq!(
resolve_call(&ctx, None, "foo1", &[ClassType(foo).into()]), resolve_call(&ctx, None, "foo1", &[ClassType(foo).into()]),
Err(..) Err("not subtype".to_string())
)); );
// virtual class substitution // virtual class substitution
assert_eq!( assert_eq!(
@ -609,9 +583,9 @@ mod tests {
resolve_call(&ctx, None, "foo", &[VirtualClassType(foo2).into()]), resolve_call(&ctx, None, "foo", &[VirtualClassType(foo2).into()]),
Ok(None) Ok(None)
); );
assert!(matches!( assert_eq!(
resolve_call(&ctx, None, "foo", &[VirtualClassType(bar).into()]), resolve_call(&ctx, None, "foo", &[VirtualClassType(bar).into()]),
Err(..) Err("not subtype".to_string())
)); );
} }
} }

View File

@ -1,33 +1,33 @@
#![warn(clippy::all)]
#![allow(clippy::clone_double_ref)]
extern crate num_bigint;
extern crate inkwell; extern crate inkwell;
extern crate num_bigint;
extern crate rustpython_parser; extern crate rustpython_parser;
pub mod type_check; pub mod expression;
pub mod inference;
mod operators;
pub mod primitives;
pub mod typedef;
use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
use std::path::Path; use std::path::Path;
use std::collections::HashMap;
use num_traits::cast::ToPrimitive; use num_traits::cast::ToPrimitive;
use rustpython_parser::ast; use rustpython_parser::ast;
use inkwell::OptimizationLevel; use inkwell::basic_block;
use inkwell::builder::Builder; use inkwell::builder::Builder;
use inkwell::context::Context; use inkwell::context::Context;
use inkwell::module::Module; use inkwell::module::Module;
use inkwell::passes;
use inkwell::targets::*; use inkwell::targets::*;
use inkwell::types; use inkwell::types;
use inkwell::types::BasicType; use inkwell::types::BasicType;
use inkwell::values; use inkwell::values;
use inkwell::{IntPredicate, FloatPredicate}; use inkwell::OptimizationLevel;
use inkwell::basic_block; use inkwell::{FloatPredicate, IntPredicate};
use inkwell::passes;
#[derive(Debug)] #[derive(Debug)]
enum CompileErrorKind { enum CompileErrorKind {
@ -37,26 +37,25 @@ enum CompileErrorKind {
IncompatibleTypes, IncompatibleTypes,
UnboundIdentifier, UnboundIdentifier,
BreakOutsideLoop, BreakOutsideLoop,
Internal(&'static str) Internal(&'static str),
} }
impl fmt::Display for CompileErrorKind { impl fmt::Display for CompileErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
CompileErrorKind::Unsupported(feature) CompileErrorKind::Unsupported(feature) => write!(
=> write!(f, "The following Python feature is not supported by NAC3: {}", feature), f,
CompileErrorKind::MissingTypeAnnotation "The following Python feature is not supported by NAC3: {}",
=> write!(f, "Missing type annotation"), feature
CompileErrorKind::UnknownTypeAnnotation ),
=> write!(f, "Unknown type annotation"), CompileErrorKind::MissingTypeAnnotation => write!(f, "Missing type annotation"),
CompileErrorKind::IncompatibleTypes CompileErrorKind::UnknownTypeAnnotation => write!(f, "Unknown type annotation"),
=> write!(f, "Incompatible types"), CompileErrorKind::IncompatibleTypes => write!(f, "Incompatible types"),
CompileErrorKind::UnboundIdentifier CompileErrorKind::UnboundIdentifier => write!(f, "Unbound identifier"),
=> write!(f, "Unbound identifier"), CompileErrorKind::BreakOutsideLoop => write!(f, "Break outside loop"),
CompileErrorKind::BreakOutsideLoop CompileErrorKind::Internal(details) => {
=> write!(f, "Break outside loop"), write!(f, "Internal compiler error: {}", details)
CompileErrorKind::Internal(details) }
=> write!(f, "Internal compiler error: {}", details),
} }
} }
} }
@ -107,7 +106,9 @@ impl<'ctx> CodeGen<'ctx> {
module.add_function("output", fn_type, None); module.add_function("output", fn_type, None);
CodeGen { CodeGen {
context, module, pass_manager, context,
module,
pass_manager,
builder: context.create_builder(), builder: context.create_builder(),
current_source_location: ast::Location::default(), current_source_location: ast::Location::default(),
namespace: HashMap::new(), namespace: HashMap::new(),
@ -122,7 +123,7 @@ impl<'ctx> CodeGen<'ctx> {
fn compile_error(&self, kind: CompileErrorKind) -> CompileError { fn compile_error(&self, kind: CompileErrorKind) -> CompileError {
CompileError { CompileError {
location: self.current_source_location, location: self.current_source_location,
kind kind,
} }
} }
@ -133,7 +134,7 @@ impl<'ctx> CodeGen<'ctx> {
"int64" => Ok(self.context.i64_type().into()), "int64" => Ok(self.context.i64_type().into()),
"float32" => Ok(self.context.f32_type().into()), "float32" => Ok(self.context.f32_type().into()),
"float64" => Ok(self.context.f64_type().into()), "float64" => Ok(self.context.f64_type().into()),
_ => Err(self.compile_error(CompileErrorKind::UnknownTypeAnnotation)) _ => Err(self.compile_error(CompileErrorKind::UnknownTypeAnnotation)),
} }
} }
@ -147,37 +148,53 @@ impl<'ctx> CodeGen<'ctx> {
is_async: bool, is_async: bool,
) -> CompileResult<values::FunctionValue<'ctx>> { ) -> CompileResult<values::FunctionValue<'ctx>> {
if is_async { if is_async {
return Err(self.compile_error(CompileErrorKind::Unsupported("async functions"))) return Err(self.compile_error(CompileErrorKind::Unsupported("async functions")));
} }
for decorator in decorator_list.iter() { for decorator in decorator_list.iter() {
self.set_source_location(decorator.location); self.set_source_location(decorator.location);
if let ast::ExpressionType::Identifier { name } = &decorator.node { if let ast::ExpressionType::Identifier { name } = &decorator.node {
if name != "kernel" && name != "portable" { if name != "kernel" && name != "portable" {
return Err(self.compile_error(CompileErrorKind::Unsupported("custom decorators"))) return Err(
self.compile_error(CompileErrorKind::Unsupported("custom decorators"))
);
} }
} else { } else {
return Err(self.compile_error(CompileErrorKind::Unsupported("decorator must be an identifier"))) return Err(self.compile_error(CompileErrorKind::Unsupported(
"decorator must be an identifier",
)));
} }
} }
let args_type = args.args.iter().map(|val| { let args_type = args
self.set_source_location(val.location); .args
if let Some(annotation) = &val.annotation { .iter()
if let ast::ExpressionType::Identifier { name } = &annotation.node { .map(|val| {
Ok(self.get_basic_type(&name)?) self.set_source_location(val.location);
if let Some(annotation) = &val.annotation {
if let ast::ExpressionType::Identifier { name } = &annotation.node {
Ok(self.get_basic_type(&name)?)
} else {
Err(self.compile_error(CompileErrorKind::Unsupported(
"type annotation must be an identifier",
)))
}
} else { } else {
Err(self.compile_error(CompileErrorKind::Unsupported("type annotation must be an identifier"))) Err(self.compile_error(CompileErrorKind::MissingTypeAnnotation))
} }
} else { })
Err(self.compile_error(CompileErrorKind::MissingTypeAnnotation)) .collect::<CompileResult<Vec<types::BasicTypeEnum>>>()?;
}
}).collect::<CompileResult<Vec<types::BasicTypeEnum>>>()?;
let return_type = if let Some(returns) = returns { let return_type = if let Some(returns) = returns {
self.set_source_location(returns.location); self.set_source_location(returns.location);
if let ast::ExpressionType::Identifier { name } = &returns.node { if let ast::ExpressionType::Identifier { name } = &returns.node {
if name == "None" { None } else { Some(self.get_basic_type(name)?) } if name == "None" {
None
} else {
Some(self.get_basic_type(name)?)
}
} else { } else {
return Err(self.compile_error(CompileErrorKind::Unsupported("type annotation must be an identifier"))) return Err(self.compile_error(CompileErrorKind::Unsupported(
"type annotation must be an identifier",
)));
} }
} else { } else {
None None
@ -185,7 +202,7 @@ impl<'ctx> CodeGen<'ctx> {
let fn_type = match return_type { let fn_type = match return_type {
Some(ty) => ty.fn_type(&args_type, false), Some(ty) => ty.fn_type(&args_type, false),
None => self.context.void_type().fn_type(&args_type, false) None => self.context.void_type().fn_type(&args_type, false),
}; };
let function = self.module.add_function(name, fn_type, None); let function = self.module.add_function(name, fn_type, None);
@ -206,57 +223,74 @@ impl<'ctx> CodeGen<'ctx> {
fn compile_expression( fn compile_expression(
&mut self, &mut self,
expression: &ast::Expression expression: &ast::Expression,
) -> CompileResult<values::BasicValueEnum<'ctx>> { ) -> CompileResult<values::BasicValueEnum<'ctx>> {
self.set_source_location(expression.location); self.set_source_location(expression.location);
match &expression.node { match &expression.node {
ast::ExpressionType::True => Ok(self.context.bool_type().const_int(1, false).into()), ast::ExpressionType::True => Ok(self.context.bool_type().const_int(1, false).into()),
ast::ExpressionType::False => Ok(self.context.bool_type().const_int(0, false).into()), ast::ExpressionType::False => Ok(self.context.bool_type().const_int(0, false).into()),
ast::ExpressionType::Number { value: ast::Number::Integer { value } } => { ast::ExpressionType::Number {
value: ast::Number::Integer { value },
} => {
let mut bits = value.bits(); let mut bits = value.bits();
if value.sign() == num_bigint::Sign::Minus { if value.sign() == num_bigint::Sign::Minus {
bits += 1; bits += 1;
} }
match bits { match bits {
0..=32 => Ok(self.context.i32_type().const_int(value.to_i32().unwrap() as _, true).into()), 0..=32 => Ok(self
33..=64 => Ok(self.context.i64_type().const_int(value.to_i64().unwrap() as _, true).into()), .context
_ => Err(self.compile_error(CompileErrorKind::Unsupported("integers larger than 64 bits"))) .i32_type()
} .const_int(value.to_i32().unwrap() as _, true)
}, .into()),
ast::ExpressionType::Number { value: ast::Number::Float { value } } => { 33..=64 => Ok(self
Ok(self.context.f64_type().const_float(*value).into()) .context
}, .i64_type()
ast::ExpressionType::Identifier { name } => { .const_int(value.to_i64().unwrap() as _, true)
match self.namespace.get(name) { .into()),
Some(value) => Ok(self.builder.build_load(*value, name).into()), _ => Err(self.compile_error(CompileErrorKind::Unsupported(
None => Err(self.compile_error(CompileErrorKind::UnboundIdentifier)) "integers larger than 64 bits",
))),
} }
}
ast::ExpressionType::Number {
value: ast::Number::Float { value },
} => Ok(self.context.f64_type().const_float(*value).into()),
ast::ExpressionType::Identifier { name } => match self.namespace.get(name) {
Some(value) => Ok(self.builder.build_load(*value, name).into()),
None => Err(self.compile_error(CompileErrorKind::UnboundIdentifier)),
}, },
ast::ExpressionType::Unop { op, a } => { ast::ExpressionType::Unop { op, a } => {
let a = self.compile_expression(&a)?; let a = self.compile_expression(&a)?;
match (op, a) { match (op, a) {
(ast::UnaryOperator::Pos, values::BasicValueEnum::IntValue(a)) (ast::UnaryOperator::Pos, values::BasicValueEnum::IntValue(a)) => Ok(a.into()),
=> Ok(a.into()), (ast::UnaryOperator::Pos, values::BasicValueEnum::FloatValue(a)) => {
(ast::UnaryOperator::Pos, values::BasicValueEnum::FloatValue(a)) Ok(a.into())
=> Ok(a.into()), }
(ast::UnaryOperator::Neg, values::BasicValueEnum::IntValue(a)) (ast::UnaryOperator::Neg, values::BasicValueEnum::IntValue(a)) => {
=> Ok(self.builder.build_int_neg(a, "tmpneg").into()), Ok(self.builder.build_int_neg(a, "tmpneg").into())
(ast::UnaryOperator::Neg, values::BasicValueEnum::FloatValue(a)) }
=> Ok(self.builder.build_float_neg(a, "tmpneg").into()), (ast::UnaryOperator::Neg, values::BasicValueEnum::FloatValue(a)) => {
(ast::UnaryOperator::Inv, values::BasicValueEnum::IntValue(a)) Ok(self.builder.build_float_neg(a, "tmpneg").into())
=> Ok(self.builder.build_not(a, "tmpnot").into()), }
(ast::UnaryOperator::Inv, values::BasicValueEnum::IntValue(a)) => {
Ok(self.builder.build_not(a, "tmpnot").into())
}
(ast::UnaryOperator::Not, values::BasicValueEnum::IntValue(a)) => { (ast::UnaryOperator::Not, values::BasicValueEnum::IntValue(a)) => {
// boolean "not" // boolean "not"
if a.get_type().get_bit_width() != 1 { if a.get_type().get_bit_width() != 1 {
Err(self.compile_error(CompileErrorKind::Unsupported("unimplemented unary operation"))) Err(self.compile_error(CompileErrorKind::Unsupported(
"unimplemented unary operation",
)))
} else { } else {
Ok(self.builder.build_not(a, "tmpnot").into()) Ok(self.builder.build_not(a, "tmpnot").into())
} }
}, }
_ => Err(self.compile_error(CompileErrorKind::Unsupported("unimplemented unary operation"))), _ => Err(self.compile_error(CompileErrorKind::Unsupported(
"unimplemented unary operation",
))),
} }
}, }
ast::ExpressionType::Binop { a, op, b } => { ast::ExpressionType::Binop { a, op, b } => {
let a = self.compile_expression(&a)?; let a = self.compile_expression(&a)?;
let b = self.compile_expression(&b)?; let b = self.compile_expression(&b)?;
@ -265,27 +299,53 @@ impl<'ctx> CodeGen<'ctx> {
} }
use ast::Operator::*; use ast::Operator::*;
match (op, a, b) { match (op, a, b) {
(Add, values::BasicValueEnum::IntValue(a), values::BasicValueEnum::IntValue(b)) (
=> Ok(self.builder.build_int_add(a, b, "tmpadd").into()), Add,
(Sub, values::BasicValueEnum::IntValue(a), values::BasicValueEnum::IntValue(b)) values::BasicValueEnum::IntValue(a),
=> Ok(self.builder.build_int_sub(a, b, "tmpsub").into()), values::BasicValueEnum::IntValue(b),
(Mult, values::BasicValueEnum::IntValue(a), values::BasicValueEnum::IntValue(b)) ) => Ok(self.builder.build_int_add(a, b, "tmpadd").into()),
=> Ok(self.builder.build_int_mul(a, b, "tmpmul").into()), (
Sub,
values::BasicValueEnum::IntValue(a),
values::BasicValueEnum::IntValue(b),
) => Ok(self.builder.build_int_sub(a, b, "tmpsub").into()),
(
Mult,
values::BasicValueEnum::IntValue(a),
values::BasicValueEnum::IntValue(b),
) => Ok(self.builder.build_int_mul(a, b, "tmpmul").into()),
(Add, values::BasicValueEnum::FloatValue(a), values::BasicValueEnum::FloatValue(b)) (
=> Ok(self.builder.build_float_add(a, b, "tmpadd").into()), Add,
(Sub, values::BasicValueEnum::FloatValue(a), values::BasicValueEnum::FloatValue(b)) values::BasicValueEnum::FloatValue(a),
=> Ok(self.builder.build_float_sub(a, b, "tmpsub").into()), values::BasicValueEnum::FloatValue(b),
(Mult, values::BasicValueEnum::FloatValue(a), values::BasicValueEnum::FloatValue(b)) ) => Ok(self.builder.build_float_add(a, b, "tmpadd").into()),
=> Ok(self.builder.build_float_mul(a, b, "tmpmul").into()), (
Sub,
values::BasicValueEnum::FloatValue(a),
values::BasicValueEnum::FloatValue(b),
) => Ok(self.builder.build_float_sub(a, b, "tmpsub").into()),
(
Mult,
values::BasicValueEnum::FloatValue(a),
values::BasicValueEnum::FloatValue(b),
) => Ok(self.builder.build_float_mul(a, b, "tmpmul").into()),
(Div, values::BasicValueEnum::FloatValue(a), values::BasicValueEnum::FloatValue(b)) (
=> Ok(self.builder.build_float_div(a, b, "tmpdiv").into()), Div,
(FloorDiv, values::BasicValueEnum::IntValue(a), values::BasicValueEnum::IntValue(b)) values::BasicValueEnum::FloatValue(a),
=> Ok(self.builder.build_int_signed_div(a, b, "tmpdiv").into()), values::BasicValueEnum::FloatValue(b),
_ => Err(self.compile_error(CompileErrorKind::Unsupported("unimplemented binary operation"))), ) => Ok(self.builder.build_float_div(a, b, "tmpdiv").into()),
(
FloorDiv,
values::BasicValueEnum::IntValue(a),
values::BasicValueEnum::IntValue(b),
) => Ok(self.builder.build_int_signed_div(a, b, "tmpdiv").into()),
_ => Err(self.compile_error(CompileErrorKind::Unsupported(
"unimplemented binary operation",
))),
} }
}, }
ast::ExpressionType::Compare { vals, ops } => { ast::ExpressionType::Compare { vals, ops } => {
let mut vals = vals.iter(); let mut vals = vals.iter();
let mut ops = ops.iter(); let mut ops = ops.iter();
@ -298,43 +358,93 @@ impl<'ctx> CodeGen<'ctx> {
if a.get_type() != b.get_type() { if a.get_type() != b.get_type() {
return Err(self.compile_error(CompileErrorKind::IncompatibleTypes)); return Err(self.compile_error(CompileErrorKind::IncompatibleTypes));
} }
let this_result = match (a, b) { let this_result =
(values::BasicValueEnum::IntValue(a), values::BasicValueEnum::IntValue(b)) => { match (a, b) {
match op { (
ast::Comparison::Equal values::BasicValueEnum::IntValue(a),
=> self.builder.build_int_compare(IntPredicate::EQ, a, b, "tmpeq"), values::BasicValueEnum::IntValue(b),
ast::Comparison::NotEqual ) => {
=> self.builder.build_int_compare(IntPredicate::NE, a, b, "tmpne"), match op {
ast::Comparison::Less ast::Comparison::Equal => self.builder.build_int_compare(
=> self.builder.build_int_compare(IntPredicate::SLT, a, b, "tmpslt"), IntPredicate::EQ,
ast::Comparison::LessOrEqual a,
=> self.builder.build_int_compare(IntPredicate::SLE, a, b, "tmpsle"), b,
ast::Comparison::Greater "tmpeq",
=> self.builder.build_int_compare(IntPredicate::SGT, a, b, "tmpsgt"), ),
ast::Comparison::GreaterOrEqual ast::Comparison::NotEqual => self
=> self.builder.build_int_compare(IntPredicate::SGE, a, b, "tmpsge"), .builder
_ => return Err(self.compile_error(CompileErrorKind::Unsupported("special comparison"))), .build_int_compare(IntPredicate::NE, a, b, "tmpne"),
ast::Comparison::Less => self.builder.build_int_compare(
IntPredicate::SLT,
a,
b,
"tmpslt",
),
ast::Comparison::LessOrEqual => self
.builder
.build_int_compare(IntPredicate::SLE, a, b, "tmpsle"),
ast::Comparison::Greater => self.builder.build_int_compare(
IntPredicate::SGT,
a,
b,
"tmpsgt",
),
ast::Comparison::GreaterOrEqual => self
.builder
.build_int_compare(IntPredicate::SGE, a, b, "tmpsge"),
_ => {
return Err(self.compile_error(
CompileErrorKind::Unsupported("special comparison"),
))
}
}
} }
}, (
(values::BasicValueEnum::FloatValue(a), values::BasicValueEnum::FloatValue(b)) => { values::BasicValueEnum::FloatValue(a),
match op { values::BasicValueEnum::FloatValue(b),
ast::Comparison::Equal ) => match op {
=> self.builder.build_float_compare(FloatPredicate::OEQ, a, b, "tmpoeq"), ast::Comparison::Equal => self.builder.build_float_compare(
ast::Comparison::NotEqual FloatPredicate::OEQ,
=> self.builder.build_float_compare(FloatPredicate::UNE, a, b, "tmpune"), a,
ast::Comparison::Less b,
=> self.builder.build_float_compare(FloatPredicate::OLT, a, b, "tmpolt"), "tmpoeq",
ast::Comparison::LessOrEqual ),
=> self.builder.build_float_compare(FloatPredicate::OLE, a, b, "tmpole"), ast::Comparison::NotEqual => self.builder.build_float_compare(
ast::Comparison::Greater FloatPredicate::UNE,
=> self.builder.build_float_compare(FloatPredicate::OGT, a, b, "tmpogt"), a,
ast::Comparison::GreaterOrEqual b,
=> self.builder.build_float_compare(FloatPredicate::OGE, a, b, "tmpoge"), "tmpune",
_ => return Err(self.compile_error(CompileErrorKind::Unsupported("special comparison"))), ),
ast::Comparison::Less => self.builder.build_float_compare(
FloatPredicate::OLT,
a,
b,
"tmpolt",
),
ast::Comparison::LessOrEqual => self
.builder
.build_float_compare(FloatPredicate::OLE, a, b, "tmpole"),
ast::Comparison::Greater => self.builder.build_float_compare(
FloatPredicate::OGT,
a,
b,
"tmpogt",
),
ast::Comparison::GreaterOrEqual => self
.builder
.build_float_compare(FloatPredicate::OGE, a, b, "tmpoge"),
_ => {
return Err(self.compile_error(
CompileErrorKind::Unsupported("special comparison"),
))
}
},
_ => {
return Err(self.compile_error(CompileErrorKind::Unsupported(
"comparison of non-numerical types",
)))
} }
}, };
_ => return Err(self.compile_error(CompileErrorKind::Unsupported("comparison of non-numerical types"))),
};
match result { match result {
Some(last) => { Some(last) => {
result = Some(self.builder.build_and(last, this_result, "tmpand")); result = Some(self.builder.build_and(last, this_result, "tmpand"));
@ -345,15 +455,23 @@ impl<'ctx> CodeGen<'ctx> {
} }
a = b; a = b;
} else { } else {
return Ok(result.unwrap().into()) return Ok(result.unwrap().into());
} }
} }
}, }
ast::ExpressionType::Call { function, args, keywords } => { ast::ExpressionType::Call {
function,
args,
keywords,
} => {
if !keywords.is_empty() { if !keywords.is_empty() {
return Err(self.compile_error(CompileErrorKind::Unsupported("keyword arguments"))) return Err(
self.compile_error(CompileErrorKind::Unsupported("keyword arguments"))
);
} }
let args = args.iter().map(|val| self.compile_expression(val)) let args = args
.iter()
.map(|val| self.compile_expression(val))
.collect::<CompileResult<Vec<values::BasicValueEnum>>>()?; .collect::<CompileResult<Vec<values::BasicValueEnum>>>()?;
self.set_source_location(expression.location); self.set_source_location(expression.location);
if let ast::ExpressionType::Identifier { name } = &function.node { if let ast::ExpressionType::Identifier { name } = &function.node {
@ -361,67 +479,99 @@ impl<'ctx> CodeGen<'ctx> {
("int32", values::BasicValueEnum::IntValue(a)) => { ("int32", values::BasicValueEnum::IntValue(a)) => {
let nbits = a.get_type().get_bit_width(); let nbits = a.get_type().get_bit_width();
if nbits < 32 { if nbits < 32 {
Ok(self.builder.build_int_s_extend(a, self.context.i32_type(), "tmpsext").into()) Ok(self
.builder
.build_int_s_extend(a, self.context.i32_type(), "tmpsext")
.into())
} else if nbits > 32 { } else if nbits > 32 {
Ok(self.builder.build_int_truncate(a, self.context.i32_type(), "tmptrunc").into()) Ok(self
.builder
.build_int_truncate(a, self.context.i32_type(), "tmptrunc")
.into())
} else { } else {
Ok(a.into()) Ok(a.into())
} }
}, }
("int64", values::BasicValueEnum::IntValue(a)) => { ("int64", values::BasicValueEnum::IntValue(a)) => {
let nbits = a.get_type().get_bit_width(); let nbits = a.get_type().get_bit_width();
if nbits < 64 { if nbits < 64 {
Ok(self.builder.build_int_s_extend(a, self.context.i64_type(), "tmpsext").into()) Ok(self
.builder
.build_int_s_extend(a, self.context.i64_type(), "tmpsext")
.into())
} else { } else {
Ok(a.into()) Ok(a.into())
} }
}, }
("int32", values::BasicValueEnum::FloatValue(a)) => { ("int32", values::BasicValueEnum::FloatValue(a)) => Ok(self
Ok(self.builder.build_float_to_signed_int(a, self.context.i32_type(), "tmpfptosi").into()) .builder
}, .build_float_to_signed_int(a, self.context.i32_type(), "tmpfptosi")
("int64", values::BasicValueEnum::FloatValue(a)) => { .into()),
Ok(self.builder.build_float_to_signed_int(a, self.context.i64_type(), "tmpfptosi").into()) ("int64", values::BasicValueEnum::FloatValue(a)) => Ok(self
}, .builder
("float32", values::BasicValueEnum::IntValue(a)) => { .build_float_to_signed_int(a, self.context.i64_type(), "tmpfptosi")
Ok(self.builder.build_signed_int_to_float(a, self.context.f32_type(), "tmpsitofp").into()) .into()),
}, ("float32", values::BasicValueEnum::IntValue(a)) => Ok(self
("float64", values::BasicValueEnum::IntValue(a)) => { .builder
Ok(self.builder.build_signed_int_to_float(a, self.context.f64_type(), "tmpsitofp").into()) .build_signed_int_to_float(a, self.context.f32_type(), "tmpsitofp")
}, .into()),
("float64", values::BasicValueEnum::IntValue(a)) => Ok(self
.builder
.build_signed_int_to_float(a, self.context.f64_type(), "tmpsitofp")
.into()),
("float32", values::BasicValueEnum::FloatValue(a)) => { ("float32", values::BasicValueEnum::FloatValue(a)) => {
if a.get_type() == self.context.f64_type() { if a.get_type() == self.context.f64_type() {
Ok(self.builder.build_float_trunc(a, self.context.f32_type(), "tmptrunc").into()) Ok(self
.builder
.build_float_trunc(a, self.context.f32_type(), "tmptrunc")
.into())
} else { } else {
Ok(a.into()) Ok(a.into())
} }
}, }
("float64", values::BasicValueEnum::FloatValue(a)) => { ("float64", values::BasicValueEnum::FloatValue(a)) => {
if a.get_type() == self.context.f32_type() { if a.get_type() == self.context.f32_type() {
Ok(self.builder.build_float_ext(a, self.context.f64_type(), "tmpext").into()) Ok(self
.builder
.build_float_ext(a, self.context.f64_type(), "tmpext")
.into())
} else { } else {
Ok(a.into()) Ok(a.into())
} }
}, }
("output", values::BasicValueEnum::IntValue(a)) => { ("output", values::BasicValueEnum::IntValue(a)) => {
let fn_value = self.module.get_function("output").unwrap(); let fn_value = self.module.get_function("output").unwrap();
Ok(self.builder.build_call(fn_value, &[a.into()], "call") Ok(self
.try_as_basic_value().left().unwrap()) .builder
}, .build_call(fn_value, &[a.into()], "call")
_ => Err(self.compile_error(CompileErrorKind::Unsupported("unrecognized call"))) .try_as_basic_value()
.left()
.unwrap())
}
_ => {
Err(self
.compile_error(CompileErrorKind::Unsupported("unrecognized call")))
}
} }
} else { } else {
return Err(self.compile_error(CompileErrorKind::Unsupported("function must be an identifier"))) return Err(self.compile_error(CompileErrorKind::Unsupported(
"function must be an identifier",
)));
} }
}, }
_ => return Err(self.compile_error(CompileErrorKind::Unsupported("unimplemented expression"))), _ => {
return Err(
self.compile_error(CompileErrorKind::Unsupported("unimplemented expression"))
)
}
} }
} }
fn compile_statement( fn compile_statement(
&mut self, &mut self,
statement: &ast::Statement, statement: &ast::Statement,
return_type: Option<types::BasicTypeEnum> return_type: Option<types::BasicTypeEnum>,
) -> CompileResult<()> { ) -> CompileResult<()> {
self.set_source_location(statement.location); self.set_source_location(statement.location);
@ -433,29 +583,43 @@ impl<'ctx> CodeGen<'ctx> {
self.set_source_location(target.location); self.set_source_location(target.location);
if let ast::ExpressionType::Identifier { name } = &target.node { if let ast::ExpressionType::Identifier { name } = &target.node {
let builder = &self.builder; let builder = &self.builder;
let target = self.namespace.entry(name.clone()).or_insert_with( let target = self
|| builder.build_alloca(value.get_type(), name)); .namespace
if target.get_type() != value.get_type().ptr_type(inkwell::AddressSpace::Generic) { .entry(name.clone())
.or_insert_with(|| builder.build_alloca(value.get_type(), name));
if target.get_type()
!= value.get_type().ptr_type(inkwell::AddressSpace::Generic)
{
return Err(self.compile_error(CompileErrorKind::IncompatibleTypes)); return Err(self.compile_error(CompileErrorKind::IncompatibleTypes));
} }
builder.build_store(*target, value); builder.build_store(*target, value);
} else { } else {
return Err(self.compile_error(CompileErrorKind::Unsupported("assignment target must be an identifier"))) return Err(self.compile_error(CompileErrorKind::Unsupported(
"assignment target must be an identifier",
)));
} }
} }
}, }
Expression { expression } => { self.compile_expression(expression)?; }, Expression { expression } => {
self.compile_expression(expression)?;
}
If { test, body, orelse } => { If { test, body, orelse } => {
let test = self.compile_expression(test)?; let test = self.compile_expression(test)?;
if test.get_type() != self.context.bool_type().into() { if test.get_type() != self.context.bool_type().into() {
return Err(self.compile_error(CompileErrorKind::IncompatibleTypes)); return Err(self.compile_error(CompileErrorKind::IncompatibleTypes));
} }
let parent = self.builder.get_insert_block().unwrap().get_parent().unwrap(); let parent = self
.builder
.get_insert_block()
.unwrap()
.get_parent()
.unwrap();
let then_bb = self.context.append_basic_block(parent, "then"); let then_bb = self.context.append_basic_block(parent, "then");
let else_bb = self.context.append_basic_block(parent, "else"); let else_bb = self.context.append_basic_block(parent, "else");
let cont_bb = self.context.append_basic_block(parent, "ifcont"); let cont_bb = self.context.append_basic_block(parent, "ifcont");
self.builder.build_conditional_branch(test.into_int_value(), then_bb, else_bb); self.builder
.build_conditional_branch(test.into_int_value(), then_bb, else_bb);
self.builder.position_at_end(then_bb); self.builder.position_at_end(then_bb);
self.compile_suite(body, return_type)?; self.compile_suite(body, return_type)?;
@ -467,9 +631,14 @@ impl<'ctx> CodeGen<'ctx> {
} }
self.builder.build_unconditional_branch(cont_bb); self.builder.build_unconditional_branch(cont_bb);
self.builder.position_at_end(cont_bb); self.builder.position_at_end(cont_bb);
}, }
While { test, body, orelse } => { While { test, body, orelse } => {
let parent = self.builder.get_insert_block().unwrap().get_parent().unwrap(); let parent = self
.builder
.get_insert_block()
.unwrap()
.get_parent()
.unwrap();
let test_bb = self.context.append_basic_block(parent, "test"); let test_bb = self.context.append_basic_block(parent, "test");
self.builder.build_unconditional_branch(test_bb); self.builder.build_unconditional_branch(test_bb);
self.builder.position_at_end(test_bb); self.builder.position_at_end(test_bb);
@ -481,7 +650,8 @@ impl<'ctx> CodeGen<'ctx> {
let then_bb = self.context.append_basic_block(parent, "then"); let then_bb = self.context.append_basic_block(parent, "then");
let else_bb = self.context.append_basic_block(parent, "else"); let else_bb = self.context.append_basic_block(parent, "else");
let cont_bb = self.context.append_basic_block(parent, "ifcont"); let cont_bb = self.context.append_basic_block(parent, "ifcont");
self.builder.build_conditional_branch(test.into_int_value(), then_bb, else_bb); self.builder
.build_conditional_branch(test.into_int_value(), then_bb, else_bb);
self.break_bb = Some(cont_bb); self.break_bb = Some(cont_bb);
@ -497,11 +667,16 @@ impl<'ctx> CodeGen<'ctx> {
self.builder.position_at_end(cont_bb); self.builder.position_at_end(cont_bb);
self.break_bb = None; self.break_bb = None;
}, }
Break => { Break => {
if let Some(bb) = self.break_bb { if let Some(bb) = self.break_bb {
self.builder.build_unconditional_branch(bb); self.builder.build_unconditional_branch(bb);
let parent = self.builder.get_insert_block().unwrap().get_parent().unwrap(); let parent = self
.builder
.get_insert_block()
.unwrap()
.get_parent()
.unwrap();
let unreachable_bb = self.context.append_basic_block(parent, "unreachable"); let unreachable_bb = self.context.append_basic_block(parent, "unreachable");
self.builder.position_at_end(unreachable_bb); self.builder.position_at_end(unreachable_bb);
} else { } else {
@ -518,13 +693,13 @@ impl<'ctx> CodeGen<'ctx> {
} else { } else {
return Err(self.compile_error(CompileErrorKind::IncompatibleTypes)); return Err(self.compile_error(CompileErrorKind::IncompatibleTypes));
} }
}, }
Return { value: None } => { Return { value: None } => {
if !return_type.is_none() { if !return_type.is_none() {
return Err(self.compile_error(CompileErrorKind::IncompatibleTypes)); return Err(self.compile_error(CompileErrorKind::IncompatibleTypes));
} }
self.builder.build_return(None); self.builder.build_return(None);
}, }
Pass => (), Pass => (),
_ => return Err(self.compile_error(CompileErrorKind::Unsupported("special statement"))), _ => return Err(self.compile_error(CompileErrorKind::Unsupported("special statement"))),
} }
@ -534,7 +709,7 @@ impl<'ctx> CodeGen<'ctx> {
fn compile_suite( fn compile_suite(
&mut self, &mut self,
suite: &ast::Suite, suite: &ast::Suite,
return_type: Option<types::BasicTypeEnum> return_type: Option<types::BasicTypeEnum>,
) -> CompileResult<()> { ) -> CompileResult<()> {
for statement in suite.iter() { for statement in suite.iter() {
self.compile_statement(statement, return_type)?; self.compile_statement(statement, return_type)?;
@ -545,18 +720,22 @@ impl<'ctx> CodeGen<'ctx> {
pub fn compile_toplevel(&mut self, statement: &ast::Statement) -> CompileResult<()> { pub fn compile_toplevel(&mut self, statement: &ast::Statement) -> CompileResult<()> {
self.set_source_location(statement.location); self.set_source_location(statement.location);
if let ast::StatementType::FunctionDef { if let ast::StatementType::FunctionDef {
is_async, is_async,
name, name,
args, args,
body, body,
decorator_list, decorator_list,
returns, returns,
} = &statement.node { } = &statement.node
let function = self.compile_function_def(name, args, body, decorator_list, returns, *is_async)?; {
let function =
self.compile_function_def(name, args, body, decorator_list, returns, *is_async)?;
self.pass_manager.run_on(&function); self.pass_manager.run_on(&function);
Ok(()) Ok(())
} else { } else {
Err(self.compile_error(CompileErrorKind::Internal("top-level is not a function definition"))) Err(self.compile_error(CompileErrorKind::Internal(
"top-level is not a function definition",
)))
} }
} }
@ -567,8 +746,8 @@ impl<'ctx> CodeGen<'ctx> {
pub fn output(&self, filename: &str) { pub fn output(&self, filename: &str) {
//let triple = TargetTriple::create("riscv32-none-linux-gnu"); //let triple = TargetTriple::create("riscv32-none-linux-gnu");
let triple = TargetMachine::get_default_triple(); let triple = TargetMachine::get_default_triple();
let target = Target::from_triple(&triple) let target =
.expect("couldn't create target from target triple"); Target::from_triple(&triple).expect("couldn't create target from target triple");
let target_machine = target let target_machine = target
.create_target_machine( .create_target_machine(

View File

@ -1,8 +1,6 @@
use super::context::*; use super::typedef::{Type::*, *};
use super::typedef::{TypeEnum::*, *};
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc;
pub const PRIMITIVES: [&str; 6] = ["int32", "int64", "float", "bool", "list", "tuple"];
pub const TUPLE_TYPE: ParamId = ParamId(0); pub const TUPLE_TYPE: ParamId = ParamId(0);
pub const LIST_TYPE: ParamId = ParamId(1); pub const LIST_TYPE: ParamId = ParamId(1);
@ -12,25 +10,16 @@ pub const INT32_TYPE: PrimitiveId = PrimitiveId(1);
pub const INT64_TYPE: PrimitiveId = PrimitiveId(2); pub const INT64_TYPE: PrimitiveId = PrimitiveId(2);
pub const FLOAT_TYPE: PrimitiveId = PrimitiveId(3); pub const FLOAT_TYPE: PrimitiveId = PrimitiveId(3);
fn impl_math(def: &mut TypeDef, ty: &Type) { fn impl_math(def: &mut TypeDef, ty: &Rc<Type>) {
let result = Some(ty.clone()); let result = Some(ty.clone());
let fun = FnDef { let fun = FnDef {
args: vec![ty.clone()], args: vec![ty.clone()],
result: result.clone(), result,
}; };
def.methods.insert("__add__", fun.clone()); def.methods.insert("__add__", fun.clone());
def.methods.insert("__iadd__", fun.clone());
def.methods.insert("__sub__", fun.clone()); def.methods.insert("__sub__", fun.clone());
def.methods.insert("__isub__", fun.clone());
def.methods.insert("__mul__", fun.clone()); def.methods.insert("__mul__", fun.clone());
def.methods.insert("__imul__", fun.clone()); def.methods.insert("__neg__", fun.clone());
def.methods.insert(
"__neg__",
FnDef {
args: vec![],
result,
},
);
def.methods.insert( def.methods.insert(
"__truediv__", "__truediv__",
FnDef { FnDef {
@ -38,24 +27,12 @@ fn impl_math(def: &mut TypeDef, ty: &Type) {
result: Some(PrimitiveType(FLOAT_TYPE).into()), result: Some(PrimitiveType(FLOAT_TYPE).into()),
}, },
); );
if ty.as_ref() == &PrimitiveType(FLOAT_TYPE) {
def.methods.insert(
"__itruediv__",
FnDef {
args: vec![ty.clone()],
result: Some(PrimitiveType(FLOAT_TYPE).into()),
},
);
}
def.methods.insert("__floordiv__", fun.clone()); def.methods.insert("__floordiv__", fun.clone());
def.methods.insert("__ifloordiv__", fun.clone());
def.methods.insert("__mod__", fun.clone()); def.methods.insert("__mod__", fun.clone());
def.methods.insert("__imod__", fun.clone());
def.methods.insert("__pow__", fun.clone()); def.methods.insert("__pow__", fun.clone());
def.methods.insert("__ipow__", fun);
} }
fn impl_bits(def: &mut TypeDef, ty: &Type) { fn impl_bits(def: &mut TypeDef, ty: &Rc<Type>) {
let result = Some(ty.clone()); let result = Some(ty.clone());
let fun = FnDef { let fun = FnDef {
args: vec![PrimitiveType(INT32_TYPE).into()], args: vec![PrimitiveType(INT32_TYPE).into()],
@ -63,7 +40,7 @@ fn impl_bits(def: &mut TypeDef, ty: &Type) {
}; };
def.methods.insert("__lshift__", fun.clone()); def.methods.insert("__lshift__", fun.clone());
def.methods.insert("__rshift__", fun); def.methods.insert("__rshift__", fun.clone());
def.methods.insert( def.methods.insert(
"__xor__", "__xor__",
FnDef { FnDef {
@ -73,17 +50,17 @@ fn impl_bits(def: &mut TypeDef, ty: &Type) {
); );
} }
fn impl_eq(def: &mut TypeDef, ty: &Type) { fn impl_eq(def: &mut TypeDef, ty: &Rc<Type>) {
let fun = FnDef { let fun = FnDef {
args: vec![ty.clone()], args: vec![ty.clone()],
result: Some(PrimitiveType(BOOL_TYPE).into()), result: Some(PrimitiveType(BOOL_TYPE).into()),
}; };
def.methods.insert("__eq__", fun.clone()); def.methods.insert("__eq__", fun.clone());
def.methods.insert("__ne__", fun); def.methods.insert("__ne__", fun.clone());
} }
fn impl_order(def: &mut TypeDef, ty: &Type) { fn impl_order(def: &mut TypeDef, ty: &Rc<Type>) {
let fun = FnDef { let fun = FnDef {
args: vec![ty.clone()], args: vec![ty.clone()],
result: Some(PrimitiveType(BOOL_TYPE).into()), result: Some(PrimitiveType(BOOL_TYPE).into()),
@ -92,10 +69,10 @@ fn impl_order(def: &mut TypeDef, ty: &Type) {
def.methods.insert("__lt__", fun.clone()); def.methods.insert("__lt__", fun.clone());
def.methods.insert("__gt__", fun.clone()); def.methods.insert("__gt__", fun.clone());
def.methods.insert("__le__", fun.clone()); def.methods.insert("__le__", fun.clone());
def.methods.insert("__ge__", fun); def.methods.insert("__ge__", fun.clone());
} }
pub fn basic_ctx() -> TopLevelContext<'static> { pub fn basic_ctx() -> GlobalContext<'static> {
let primitives = [ let primitives = [
TypeDef { TypeDef {
name: "bool", name: "bool",
@ -119,25 +96,25 @@ pub fn basic_ctx() -> TopLevelContext<'static> {
}, },
] ]
.to_vec(); .to_vec();
let mut ctx = TopLevelContext::new(primitives); let mut ctx = GlobalContext::new(primitives);
let b = ctx.get_primitive(BOOL_TYPE); let b_def = ctx.get_primitive_mut(BOOL_TYPE);
let b_def = ctx.get_primitive_def_mut(BOOL_TYPE); let b = PrimitiveType(BOOL_TYPE).into();
impl_eq(b_def, &b); impl_eq(b_def, &b);
let int32 = ctx.get_primitive(INT32_TYPE); let int32_def = ctx.get_primitive_mut(INT32_TYPE);
let int32_def = ctx.get_primitive_def_mut(INT32_TYPE); let int32 = PrimitiveType(INT32_TYPE).into();
impl_math(int32_def, &int32); impl_math(int32_def, &int32);
impl_bits(int32_def, &int32); impl_bits(int32_def, &int32);
impl_order(int32_def, &int32); impl_order(int32_def, &int32);
impl_eq(int32_def, &int32); impl_eq(int32_def, &int32);
let int64 = ctx.get_primitive(INT64_TYPE); let int64_def = ctx.get_primitive_mut(INT64_TYPE);
let int64_def = ctx.get_primitive_def_mut(INT64_TYPE); let int64 = PrimitiveType(INT64_TYPE).into();
impl_math(int64_def, &int64); impl_math(int64_def, &int64);
impl_bits(int64_def, &int64); impl_bits(int64_def, &int64);
impl_order(int64_def, &int64); impl_order(int64_def, &int64);
impl_eq(int64_def, &int64); impl_eq(int64_def, &int64);
let float = ctx.get_primitive(FLOAT_TYPE); let float_def = ctx.get_primitive_mut(FLOAT_TYPE);
let float_def = ctx.get_primitive_def_mut(FLOAT_TYPE); let float = PrimitiveType(FLOAT_TYPE).into();
impl_math(float_def, &float); impl_math(float_def, &float);
impl_order(float_def, &float); impl_order(float_def, &float);
impl_eq(float_def, &float); impl_eq(float_def, &float);
@ -192,7 +169,7 @@ pub fn basic_ctx() -> TopLevelContext<'static> {
ctx.add_fn( ctx.add_fn(
"float", "float",
FnDef { FnDef {
args, args: args.clone(),
result: Some(PrimitiveType(FLOAT_TYPE).into()), result: Some(PrimitiveType(FLOAT_TYPE).into()),
}, },
); );

View File

@ -1,228 +0,0 @@
use super::super::typedef::*;
use super::TopLevelContext;
use std::boxed::Box;
use std::collections::HashMap;
struct ContextStack<'a> {
/// stack level, starts from 0
level: u32,
/// stack of variable definitions containing (id, def, level) where `def` is the original
/// definition in `level-1`.
var_defs: Vec<(usize, VarDef<'a>, u32)>,
/// stack of symbol definitions containing (name, level) where `level` is the smallest level
/// where the name is assigned a value
sym_def: Vec<(&'a str, u32)>,
}
pub struct InferenceContext<'a> {
/// top level context
top_level: TopLevelContext<'a>,
/// list of primitive instances
primitives: Vec<Type>,
/// list of variable instances
variables: Vec<Type>,
/// identifier to type mapping.
sym_table: HashMap<&'a str, Type>,
/// resolution function reference, that may resolve unbounded identifiers to some type
resolution_fn: Box<dyn FnMut(&str) -> Result<Type, String>>,
/// stack
stack: ContextStack<'a>,
/// return type
result: Option<Type>,
}
// non-trivial implementations here
impl<'a> InferenceContext<'a> {
/// return a new `InferenceContext` from `TopLevelContext` and resolution function.
pub fn new(
top_level: TopLevelContext,
resolution_fn: Box<dyn FnMut(&str) -> Result<Type, String>>,
) -> InferenceContext {
let primitives = (0..top_level.primitive_defs.len())
.map(|v| TypeEnum::PrimitiveType(PrimitiveId(v)).into())
.collect();
let variables = (0..top_level.var_defs.len())
.map(|v| TypeEnum::TypeVariable(VariableId(v)).into())
.collect();
InferenceContext {
top_level,
primitives,
variables,
sym_table: HashMap::new(),
resolution_fn,
stack: ContextStack {
level: 0,
var_defs: Vec::new(),
sym_def: Vec::new(),
},
result: None,
}
}
/// execute the function with new scope.
/// variable assignment would be limited within the scope (not readable outside), and type
/// variable type guard would be limited within the scope.
/// returns the list of variables assigned within the scope, and the result of the function
pub fn with_scope<F, R>(&mut self, f: F) -> (Vec<(&'a str, Type)>, R)
where
F: FnOnce(&mut Self) -> R,
{
self.stack.level += 1;
let result = f(self);
self.stack.level -= 1;
while !self.stack.var_defs.is_empty() {
let (_, _, level) = self.stack.var_defs.last().unwrap();
if *level > self.stack.level {
let (id, def, _) = self.stack.var_defs.pop().unwrap();
self.top_level.var_defs[id] = def;
} else {
break;
}
}
let mut poped_names = Vec::new();
while !self.stack.sym_def.is_empty() {
let (_, level) = self.stack.sym_def.last().unwrap();
if *level > self.stack.level {
let (name, _) = self.stack.sym_def.pop().unwrap();
let ty = self.sym_table.remove(name).unwrap();
poped_names.push((name, ty));
} else {
break;
}
}
(poped_names, result)
}
/// assign a type to an identifier.
/// may return error if the identifier was defined but with different type
pub fn assign(&mut self, name: &'a str, ty: Type) -> Result<Type, String> {
if let Some(t) = self.sym_table.get_mut(name) {
if t == &ty {
Ok(ty)
} else {
Err("different types".into())
}
} else {
self.stack.sym_def.push((name, self.stack.level));
self.sym_table.insert(name, ty.clone());
Ok(ty)
}
}
/// check if an identifier is already defined
pub fn defined(&self, name: &str) -> bool {
self.sym_table.get(name).is_some()
}
/// get the type of an identifier
/// may return error if the identifier is not defined, and cannot be resolved with the
/// resolution function.
pub fn resolve(&mut self, name: &str) -> Result<Type, String> {
if let Some(t) = self.sym_table.get(name) {
Ok(t.clone())
} else {
self.resolution_fn.as_mut()(name)
}
}
/// restrict the bound of a type variable by replacing its definition.
/// used for implementing type guard
pub fn restrict(&mut self, id: VariableId, mut def: VarDef<'a>) {
std::mem::swap(self.top_level.var_defs.get_mut(id.0).unwrap(), &mut def);
self.stack.var_defs.push((id.0, def, self.stack.level));
}
pub fn set_result(&mut self, result: Option<Type>) {
self.result = result;
}
}
// trivial getters:
impl<'a> InferenceContext<'a> {
pub fn get_primitive(&self, id: PrimitiveId) -> Type {
self.primitives.get(id.0).unwrap().clone()
}
pub fn get_variable(&self, id: VariableId) -> Type {
self.variables.get(id.0).unwrap().clone()
}
pub fn get_fn_def(&self, name: &str) -> Option<&FnDef> {
self.top_level.fn_table.get(name)
}
pub fn get_primitive_def(&self, id: PrimitiveId) -> &TypeDef {
self.top_level.primitive_defs.get(id.0).unwrap()
}
pub fn get_class_def(&self, id: ClassId) -> &ClassDef {
self.top_level.class_defs.get(id.0).unwrap()
}
pub fn get_parametric_def(&self, id: ParamId) -> &ParametricDef {
self.top_level.parametric_defs.get(id.0).unwrap()
}
pub fn get_variable_def(&self, id: VariableId) -> &VarDef {
self.top_level.var_defs.get(id.0).unwrap()
}
pub fn get_type(&self, name: &str) -> Option<Type> {
self.top_level.get_type(name)
}
pub fn get_result(&self) -> Option<Type> {
self.result.clone()
}
}
impl TypeEnum {
pub fn subst(&self, map: &HashMap<VariableId, Type>) -> TypeEnum {
match self {
TypeEnum::TypeVariable(id) => map.get(id).map(|v| v.as_ref()).unwrap_or(self).clone(),
TypeEnum::ParametricType(id, params) => TypeEnum::ParametricType(
*id,
params
.iter()
.map(|v| v.as_ref().subst(map).into())
.collect(),
),
_ => self.clone(),
}
}
pub fn inv_subst(&self, map: &[(Type, Type)]) -> Type {
for (from, to) in map.iter() {
if self == from.as_ref() {
return to.clone();
}
}
match self {
TypeEnum::ParametricType(id, params) => TypeEnum::ParametricType(
*id,
params.iter().map(|v| v.as_ref().inv_subst(map)).collect(),
),
_ => self.clone(),
}
.into()
}
pub fn get_subst(&self, ctx: &InferenceContext) -> HashMap<VariableId, Type> {
match self {
TypeEnum::ParametricType(id, params) => {
let vars = &ctx.get_parametric_def(*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<'a>(&'a self, ctx: &'a InferenceContext) -> Option<&'a TypeDef> {
match self {
TypeEnum::PrimitiveType(id) => Some(ctx.get_primitive_def(*id)),
TypeEnum::ClassType(id) | TypeEnum::VirtualClassType(id) => {
Some(&ctx.get_class_def(*id).base)
}
TypeEnum::ParametricType(id, _) => Some(&ctx.get_parametric_def(*id).base),
_ => None,
}
}
}

View File

@ -1,4 +0,0 @@
mod inference_context;
mod top_level_context;
pub use inference_context::InferenceContext;
pub use top_level_context::TopLevelContext;

View File

@ -1,138 +0,0 @@
use super::super::typedef::*;
use std::collections::HashMap;
use std::rc::Rc;
/// Structure for storing top-level type definitions.
/// Used for collecting type signature from source code.
/// Can be converted to `InferenceContext` for type inference in functions.
pub struct TopLevelContext<'a> {
/// List of primitive definitions.
pub(super) primitive_defs: Vec<TypeDef<'a>>,
/// List of class definitions.
pub(super) class_defs: Vec<ClassDef<'a>>,
/// List of parametric type definitions.
pub(super) parametric_defs: Vec<ParametricDef<'a>>,
/// List of type variable definitions.
pub(super) var_defs: Vec<VarDef<'a>>,
/// Function name to signature mapping.
pub(super) fn_table: HashMap<&'a str, FnDef>,
/// Type name to type mapping.
pub(super) sym_table: HashMap<&'a str, Type>,
primitives: Vec<Type>,
variables: Vec<Type>,
}
impl<'a> TopLevelContext<'a> {
pub fn new(primitive_defs: Vec<TypeDef<'a>>) -> TopLevelContext {
let mut sym_table = HashMap::new();
let mut primitives = Vec::new();
for (i, t) in primitive_defs.iter().enumerate() {
primitives.push(TypeEnum::PrimitiveType(PrimitiveId(i)).into());
sym_table.insert(t.name, TypeEnum::PrimitiveType(PrimitiveId(i)).into());
}
TopLevelContext {
primitive_defs,
class_defs: Vec::new(),
parametric_defs: Vec::new(),
var_defs: Vec::new(),
fn_table: HashMap::new(),
sym_table,
primitives,
variables: Vec::new(),
}
}
pub fn add_class(&mut self, def: ClassDef<'a>) -> ClassId {
self.sym_table.insert(
def.base.name,
TypeEnum::ClassType(ClassId(self.class_defs.len())).into(),
);
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(TypeEnum::TypeVariable(v)))
.collect();
self.sym_table.insert(
def.base.name,
TypeEnum::ParametricType(ParamId(self.parametric_defs.len()), params).into(),
);
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,
TypeEnum::TypeVariable(VariableId(self.var_defs.len())).into(),
);
self.add_variable_private(def)
}
pub fn add_variable_private(&mut self, def: VarDef<'a>) -> VariableId {
self.var_defs.push(def);
self.variables
.push(TypeEnum::TypeVariable(VariableId(self.var_defs.len() - 1)).into());
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_def(&self, name: &str) -> Option<&FnDef> {
self.fn_table.get(name)
}
pub fn get_primitive_def_mut(&mut self, id: PrimitiveId) -> &mut TypeDef<'a> {
self.primitive_defs.get_mut(id.0).unwrap()
}
pub fn get_primitive_def(&self, id: PrimitiveId) -> &TypeDef {
self.primitive_defs.get(id.0).unwrap()
}
pub fn get_class_def_mut(&mut self, id: ClassId) -> &mut ClassDef<'a> {
self.class_defs.get_mut(id.0).unwrap()
}
pub fn get_class_def(&self, id: ClassId) -> &ClassDef {
self.class_defs.get(id.0).unwrap()
}
pub fn get_parametric_def_mut(&mut self, id: ParamId) -> &mut ParametricDef<'a> {
self.parametric_defs.get_mut(id.0).unwrap()
}
pub fn get_parametric_def(&self, id: ParamId) -> &ParametricDef {
self.parametric_defs.get(id.0).unwrap()
}
pub fn get_variable_def_mut(&mut self, id: VariableId) -> &mut VarDef<'a> {
self.var_defs.get_mut(id.0).unwrap()
}
pub fn get_variable_def(&self, id: VariableId) -> &VarDef {
self.var_defs.get(id.0).unwrap()
}
pub fn get_primitive(&self, id: PrimitiveId) -> Type {
self.primitives.get(id.0).unwrap().clone()
}
pub fn get_variable(&self, id: VariableId) -> Type {
self.variables.get(id.0).unwrap().clone()
}
pub fn get_type(&self, name: &str) -> Option<Type> {
// TODO: handle name visibility
// possibly by passing a function from outside to tell what names are allowed, and what are
// not...
self.sym_table.get(name).cloned()
}
}

View File

@ -1,967 +0,0 @@
use super::context::InferenceContext;
use super::inference_core::resolve_call;
use super::magic_methods::*;
use super::primitives::*;
use super::typedef::{Type, TypeEnum::*};
use rustpython_parser::ast::{
Comparison, Comprehension, ComprehensionKind, Expression, ExpressionType, Operator,
UnaryOperator,
};
use std::convert::TryInto;
type ParserResult = Result<Option<Type>, String>;
pub fn infer_expr<'a>(
ctx: &mut InferenceContext<'a>,
expr: &'a Expression,
) -> ParserResult {
match &expr.node {
ExpressionType::Number { value } => infer_constant(ctx, value),
ExpressionType::Identifier { name } => infer_identifier(ctx, name),
ExpressionType::List { elements } => infer_list(ctx, elements),
ExpressionType::Tuple { elements } => infer_tuple(ctx, elements),
ExpressionType::Attribute { value, name } => infer_attribute(ctx, value, name),
ExpressionType::BoolOp { values, .. } => infer_bool_ops(ctx, values),
ExpressionType::Binop { a, b, op } => infer_bin_ops(ctx, op, a, b),
ExpressionType::Unop { op, a } => infer_unary_ops(ctx, op, a),
ExpressionType::Compare { vals, ops } => infer_compare(ctx, vals, ops),
ExpressionType::Call {
args,
function,
keywords,
} => {
if !keywords.is_empty() {
Err("keyword is not supported".into())
} else {
infer_call(ctx, &args, &function)
}
}
ExpressionType::Subscript { a, b } => infer_subscript(ctx, a, b),
ExpressionType::IfExpression { test, body, orelse } => {
infer_if_expr(ctx, &test, &body, orelse)
}
ExpressionType::Comprehension { kind, generators } => match kind.as_ref() {
ComprehensionKind::List { element } => {
if generators.len() == 1 {
infer_list_comprehension(ctx, element, &generators[0])
} else {
Err("only 1 generator statement is supported".into())
}
}
_ => Err("only list comprehension is supported".into()),
},
ExpressionType::True | ExpressionType::False => Ok(Some(ctx.get_primitive(BOOL_TYPE))),
_ => Err("not supported".into()),
}
}
fn infer_constant(
ctx: &mut InferenceContext,
value: &rustpython_parser::ast::Number,
) -> ParserResult {
use rustpython_parser::ast::Number;
match value {
Number::Integer { value } => {
let int32: Result<i32, _> = value.try_into();
if int32.is_ok() {
Ok(Some(ctx.get_primitive(INT32_TYPE)))
} else {
let int64: Result<i64, _> = value.try_into();
if int64.is_ok() {
Ok(Some(ctx.get_primitive(INT64_TYPE)))
} else {
Err("integer out of range".into())
}
}
}
Number::Float { .. } => Ok(Some(ctx.get_primitive(FLOAT_TYPE))),
_ => Err("not supported".into()),
}
}
fn infer_identifier(ctx: &mut InferenceContext, name: &str) -> ParserResult {
Ok(Some(ctx.resolve(name)?))
}
fn infer_list<'a>(
ctx: &mut InferenceContext<'a>,
elements: &'a [Expression],
) -> ParserResult {
if elements.is_empty() {
return Ok(Some(ParametricType(LIST_TYPE, vec![BotType.into()]).into()));
}
let mut types = elements.iter().map(|v| infer_expr(ctx, v));
let head = types.next().unwrap()?;
if head.is_none() {
return Err("list elements must have some type".into());
}
for v in types {
if v? != head {
return Err("inhomogeneous list is not allowed".into());
}
}
Ok(Some(ParametricType(LIST_TYPE, vec![head.unwrap()]).into()))
}
fn infer_tuple<'a>(
ctx: &mut InferenceContext<'a>,
elements: &'a [Expression],
) -> ParserResult {
let types: Result<Option<Vec<_>>, String> =
elements.iter().map(|v| infer_expr(ctx, v)).collect();
if let Some(t) = types? {
Ok(Some(ParametricType(TUPLE_TYPE, t).into()))
} else {
Err("tuple elements must have some type".into())
}
}
fn infer_attribute<'a>(
ctx: &mut InferenceContext<'a>,
value: &'a Expression,
name: &str,
) -> ParserResult {
let value = infer_expr(ctx, value)?.ok_or_else(|| "no value".to_string())?;
if let TypeVariable(id) = value.as_ref() {
let v = ctx.get_variable_def(*id);
if v.bound.is_empty() {
return Err("no fields on unbounded type variable".into());
}
let ty = v.bound[0].get_base(ctx).and_then(|v| v.fields.get(name));
if ty.is_none() {
return Err("unknown field".into());
}
for x in v.bound[1..].iter() {
let ty1 = x.get_base(ctx).and_then(|v| v.fields.get(name));
if ty1 != ty {
return Err("unknown field (type mismatch between variants)".into());
}
}
return Ok(Some(ty.unwrap().clone()));
}
match value.get_base(ctx) {
Some(b) => match b.fields.get(name) {
Some(t) => Ok(Some(t.clone())),
None => Err("no such field".into()),
},
None => Err("this object has no fields".into()),
}
}
fn infer_bool_ops<'a>(ctx: &mut InferenceContext<'a>, values: &'a [Expression]) -> ParserResult {
assert_eq!(values.len(), 2);
let left = infer_expr(ctx, &values[0])?.ok_or_else(|| "no value".to_string())?;
let right = infer_expr(ctx, &values[1])?.ok_or_else(|| "no value".to_string())?;
let b = ctx.get_primitive(BOOL_TYPE);
if left == b && right == b {
Ok(Some(b))
} else {
Err("bool operands must be bool".into())
}
}
fn infer_bin_ops<'a>(
ctx: &mut InferenceContext<'a>,
op: &Operator,
left: &'a Expression,
right: &'a Expression,
) -> ParserResult {
let left = infer_expr(ctx, left)?.ok_or_else(|| "no value".to_string())?;
let right = infer_expr(ctx, right)?.ok_or_else(|| "no value".to_string())?;
let fun = binop_name(op);
resolve_call(ctx, Some(left), fun, &[right])
}
fn infer_unary_ops<'a>(
ctx: &mut InferenceContext<'a>,
op: &UnaryOperator,
obj: &'a Expression,
) -> ParserResult {
let ty = infer_expr(ctx, obj)?.ok_or_else(|| "no value".to_string())?;
if let UnaryOperator::Not = op {
if ty == ctx.get_primitive(BOOL_TYPE) {
Ok(Some(ty))
} else {
Err("logical not must be applied to bool".into())
}
} else {
resolve_call(ctx, Some(ty), unaryop_name(op), &[])
}
}
fn infer_compare<'a>(
ctx: &mut InferenceContext<'a>,
vals: &'a [Expression],
ops: &'a [Comparison],
) -> ParserResult {
let types: Result<Option<Vec<_>>, _> = vals.iter().map(|v| infer_expr(ctx, v)).collect();
let types = types?;
if types.is_none() {
return Err("comparison operands must have type".into());
}
let types = types.unwrap();
let boolean = ctx.get_primitive(BOOL_TYPE);
let left = &types[..types.len() - 1];
let right = &types[1..];
for ((a, b), op) in left.iter().zip(right.iter()).zip(ops.iter()) {
let fun = comparison_name(op).ok_or_else(|| "unsupported comparison".to_string())?;
let ty = resolve_call(ctx, Some(a.clone()), fun, &[b.clone()])?;
if ty.is_none() || ty.unwrap() != boolean {
return Err("comparison result must be boolean".into());
}
}
Ok(Some(boolean))
}
fn infer_call<'a>(
ctx: &mut InferenceContext<'a>,
args: &'a [Expression],
function: &'a Expression,
) -> ParserResult {
let types: Result<Option<Vec<_>>, _> = args.iter().map(|v| infer_expr(ctx, v)).collect();
let types = types?;
if types.is_none() {
return Err("function params must have type".into());
}
let (obj, fun) = match &function.node {
ExpressionType::Identifier { name } => (None, name),
ExpressionType::Attribute { value, name } => (
Some(infer_expr(ctx, &value)?.ok_or_else(|| "no value".to_string())?),
name,
),
_ => return Err("not supported".into()),
};
resolve_call(ctx, obj, fun.as_str(), &types.unwrap())
}
fn infer_subscript<'a>(
ctx: &mut InferenceContext<'a>,
a: &'a Expression,
b: &'a Expression,
) -> ParserResult {
let a = infer_expr(ctx, a)?.ok_or_else(|| "no value".to_string())?;
let t = if let ParametricType(LIST_TYPE, ls) = a.as_ref() {
ls[0].clone()
} else {
return Err("subscript is not supported for types other than list".into());
};
match &b.node {
ExpressionType::Slice { elements } => {
let int32 = ctx.get_primitive(INT32_TYPE);
let types: Result<Option<Vec<_>>, _> = elements
.iter()
.map(|v| {
if let ExpressionType::None = v.node {
Ok(Some(int32.clone()))
} else {
infer_expr(ctx, v)
}
})
.collect();
let types = types?.ok_or_else(|| "slice must have type".to_string())?;
if types.iter().all(|v| v == &int32) {
Ok(Some(a))
} else {
Err("slice must be int32 type".into())
}
}
_ => {
let b = infer_expr(ctx, b)?.ok_or_else(|| "no value".to_string())?;
if b == ctx.get_primitive(INT32_TYPE) {
Ok(Some(t))
} else {
Err("index must be either slice or int32".into())
}
}
}
}
fn infer_if_expr<'a>(
ctx: &mut InferenceContext<'a>,
test: &'a Expression,
body: &'a Expression,
orelse: &'a Expression,
) -> ParserResult {
let test = infer_expr(ctx, test)?.ok_or_else(|| "no value".to_string())?;
if test != ctx.get_primitive(BOOL_TYPE) {
return Err("test should be bool".into());
}
let body = infer_expr(ctx, body)?;
let orelse = infer_expr(ctx, orelse)?;
if body.as_ref() == orelse.as_ref() {
Ok(body)
} else {
Err("divergent type".into())
}
}
pub fn infer_simple_binding<'a>(
ctx: &mut InferenceContext<'a>,
name: &'a Expression,
ty: Type,
) -> Result<(), String> {
match &name.node {
ExpressionType::Identifier { name } => {
if name == "_" {
Ok(())
} else if ctx.defined(name.as_str()) {
Err("duplicated naming".into())
} else {
ctx.assign(name.as_str(), ty)?;
Ok(())
}
}
ExpressionType::Tuple { elements } => {
if let ParametricType(TUPLE_TYPE, ls) = ty.as_ref() {
if elements.len() == ls.len() {
for (a, b) in elements.iter().zip(ls.iter()) {
infer_simple_binding(ctx, a, b.clone())?;
}
Ok(())
} else {
Err("different length".into())
}
} else {
Err("not supported".into())
}
}
_ => Err("not supported".into()),
}
}
fn infer_list_comprehension<'a>(
ctx: &mut InferenceContext<'a>,
element: &'a Expression,
comprehension: &'a Comprehension,
) -> ParserResult {
if comprehension.is_async {
return Err("async is not supported".into());
}
let iter = infer_expr(ctx, &comprehension.iter)?.ok_or_else(|| "no value".to_string())?;
if let ParametricType(LIST_TYPE, ls) = iter.as_ref() {
ctx.with_scope(|ctx| {
infer_simple_binding(ctx, &comprehension.target, ls[0].clone())?;
let boolean = ctx.get_primitive(BOOL_TYPE);
for test in comprehension.ifs.iter() {
let result =
infer_expr(ctx, test)?.ok_or_else(|| "no value in test".to_string())?;
if result != boolean {
return Err("test must be bool".into());
}
}
let result = infer_expr(ctx, element)?.ok_or_else(|| "no value")?;
Ok(Some(ParametricType(LIST_TYPE, vec![result]).into()))
})
.1
} else {
Err("iteration is supported for list only".into())
}
}
#[cfg(test)]
mod test {
use super::{
super::{context::*, typedef::*},
*,
};
use rustpython_parser::parser::parse_expression;
use std::collections::HashMap;
use std::rc::Rc;
fn get_inference_context(ctx: TopLevelContext) -> InferenceContext {
InferenceContext::new(ctx, Box::new(|_| Err("unbounded identifier".into())))
}
#[test]
fn test_constants() {
let ctx = basic_ctx();
let mut ctx = get_inference_context(ctx);
let ast = parse_expression("123").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT32_TYPE));
let ast = parse_expression("2147483647").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT32_TYPE));
let ast = parse_expression("2147483648").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT64_TYPE));
let ast = parse_expression("9223372036854775807").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT64_TYPE));
let ast = parse_expression("9223372036854775808").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("integer out of range".into()));
let ast = parse_expression("123.456").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(FLOAT_TYPE));
let ast = parse_expression("True").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(BOOL_TYPE));
let ast = parse_expression("False").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(BOOL_TYPE));
}
#[test]
fn test_identifier() {
let ctx = basic_ctx();
let mut ctx = get_inference_context(ctx);
ctx.assign("abc", ctx.get_primitive(INT32_TYPE)).unwrap();
let ast = parse_expression("abc").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT32_TYPE));
let ast = parse_expression("ab").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("unbounded identifier".into()));
}
#[test]
fn test_list() {
let mut ctx = basic_ctx();
ctx.add_fn(
"foo",
FnDef {
args: vec![],
result: None,
},
);
let mut ctx = get_inference_context(ctx);
ctx.assign("abc", ctx.get_primitive(INT32_TYPE)).unwrap();
// def is reserved...
ctx.assign("efg", ctx.get_primitive(INT32_TYPE)).unwrap();
ctx.assign("xyz", ctx.get_primitive(FLOAT_TYPE)).unwrap();
let ast = parse_expression("[]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(
result.unwrap().unwrap(),
ParametricType(LIST_TYPE, vec![BotType.into()]).into()
);
let ast = parse_expression("[abc]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(
result.unwrap().unwrap(),
ParametricType(LIST_TYPE, vec![ctx.get_primitive(INT32_TYPE)]).into()
);
let ast = parse_expression("[abc, efg]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(
result.unwrap().unwrap(),
ParametricType(LIST_TYPE, vec![ctx.get_primitive(INT32_TYPE)]).into()
);
let ast = parse_expression("[abc, efg, xyz]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("inhomogeneous list is not allowed".into()));
let ast = parse_expression("[foo()]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("list elements must have some type".into()));
}
#[test]
fn test_tuple() {
let mut ctx = basic_ctx();
ctx.add_fn(
"foo",
FnDef {
args: vec![],
result: None,
},
);
let mut ctx = get_inference_context(ctx);
ctx.assign("abc", ctx.get_primitive(INT32_TYPE)).unwrap();
ctx.assign("efg", ctx.get_primitive(FLOAT_TYPE)).unwrap();
let ast = parse_expression("(abc, efg)").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(
result.unwrap().unwrap(),
ParametricType(
TUPLE_TYPE,
vec![ctx.get_primitive(INT32_TYPE), ctx.get_primitive(FLOAT_TYPE)]
)
.into()
);
let ast = parse_expression("(abc, efg, foo())").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("tuple elements must have some type".into()));
}
#[test]
fn test_attribute() {
let mut ctx = basic_ctx();
ctx.add_fn(
"none",
FnDef {
args: vec![],
result: None,
},
);
let int32 = ctx.get_primitive(INT32_TYPE);
let float = ctx.get_primitive(FLOAT_TYPE);
let foo = ctx.add_class(ClassDef {
base: TypeDef {
name: "Foo",
fields: HashMap::new(),
methods: HashMap::new(),
},
parents: vec![],
});
let foo_def = ctx.get_class_def_mut(foo);
foo_def.base.fields.insert("a", int32.clone());
foo_def.base.fields.insert("b", ClassType(foo).into());
foo_def.base.fields.insert("c", int32.clone());
let bar = ctx.add_class(ClassDef {
base: TypeDef {
name: "Bar",
fields: HashMap::new(),
methods: HashMap::new(),
},
parents: vec![],
});
let bar_def = ctx.get_class_def_mut(bar);
bar_def.base.fields.insert("a", int32);
bar_def.base.fields.insert("b", ClassType(bar).into());
bar_def.base.fields.insert("c", float);
let v0 = ctx.add_variable(VarDef {
name: "v0",
bound: vec![],
});
let v1 = ctx.add_variable(VarDef {
name: "v1",
bound: vec![ClassType(foo).into(), ClassType(bar).into()],
});
let mut ctx = get_inference_context(ctx);
ctx.assign("foo", Rc::new(ClassType(foo))).unwrap();
ctx.assign("bar", Rc::new(ClassType(bar))).unwrap();
ctx.assign("foobar", Rc::new(VirtualClassType(foo)))
.unwrap();
ctx.assign("v0", ctx.get_variable(v0)).unwrap();
ctx.assign("v1", ctx.get_variable(v1)).unwrap();
ctx.assign("bot", Rc::new(BotType)).unwrap();
let ast = parse_expression("foo.a").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT32_TYPE));
let ast = parse_expression("foo.d").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("no such field".into()));
let ast = parse_expression("foobar.a").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT32_TYPE));
let ast = parse_expression("v0.a").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("no fields on unbounded type variable".into()));
let ast = parse_expression("v1.a").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT32_TYPE));
// shall we support this?
let ast = parse_expression("v1.b").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(
result,
Err("unknown field (type mismatch between variants)".into())
);
// assert_eq!(result.unwrap().unwrap(), TypeVariable(v1).into());
let ast = parse_expression("v1.c").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(
result,
Err("unknown field (type mismatch between variants)".into())
);
let ast = parse_expression("v1.d").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("unknown field".into()));
let ast = parse_expression("none().a").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("no value".into()));
let ast = parse_expression("bot.a").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("this object has no fields".into()));
}
#[test]
fn test_bool_ops() {
let mut ctx = basic_ctx();
ctx.add_fn(
"none",
FnDef {
args: vec![],
result: None,
},
);
let mut ctx = get_inference_context(ctx);
let ast = parse_expression("True and False").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(BOOL_TYPE));
let ast = parse_expression("True and none()").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("no value".into()));
let ast = parse_expression("True and 123").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("bool operands must be bool".into()));
}
#[test]
fn test_bin_ops() {
let mut ctx = basic_ctx();
let v0 = ctx.add_variable(VarDef {
name: "v0",
bound: vec![ctx.get_primitive(INT32_TYPE), ctx.get_primitive(INT64_TYPE)],
});
let mut ctx = get_inference_context(ctx);
ctx.assign("a", TypeVariable(v0).into()).unwrap();
let ast = parse_expression("1 + 2 + 3").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT32_TYPE));
let ast = parse_expression("a + a + a").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), TypeVariable(v0).into());
}
#[test]
fn test_unary_ops() {
let mut ctx = basic_ctx();
let v0 = ctx.add_variable(VarDef {
name: "v0",
bound: vec![ctx.get_primitive(INT32_TYPE), ctx.get_primitive(INT64_TYPE)],
});
let mut ctx = get_inference_context(ctx);
ctx.assign("a", TypeVariable(v0).into()).unwrap();
let ast = parse_expression("-(123)").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT32_TYPE));
let ast = parse_expression("-a").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), TypeVariable(v0).into());
let ast = parse_expression("not True").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(BOOL_TYPE));
let ast = parse_expression("not (1)").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("logical not must be applied to bool".into()));
}
#[test]
fn test_compare() {
let mut ctx = basic_ctx();
let v0 = ctx.add_variable(VarDef {
name: "v0",
bound: vec![ctx.get_primitive(INT32_TYPE), ctx.get_primitive(INT64_TYPE)],
});
let mut ctx = get_inference_context(ctx);
ctx.assign("a", TypeVariable(v0).into()).unwrap();
let ast = parse_expression("a == a == a").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(BOOL_TYPE));
let ast = parse_expression("a == a == 1").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("different types".into()));
let ast = parse_expression("True > False").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("no such function".into()));
let ast = parse_expression("True in False").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("unsupported comparison".into()));
}
#[test]
fn test_call() {
let mut ctx = basic_ctx();
ctx.add_fn(
"none",
FnDef {
args: vec![],
result: None,
},
);
let foo = ctx.add_class(ClassDef {
base: TypeDef {
name: "Foo",
fields: HashMap::new(),
methods: HashMap::new(),
},
parents: vec![],
});
let foo_def = ctx.get_class_def_mut(foo);
foo_def.base.methods.insert(
"a",
FnDef {
args: vec![],
result: Some(Rc::new(ClassType(foo))),
},
);
let bar = ctx.add_class(ClassDef {
base: TypeDef {
name: "Bar",
fields: HashMap::new(),
methods: HashMap::new(),
},
parents: vec![],
});
let bar_def = ctx.get_class_def_mut(bar);
bar_def.base.methods.insert(
"a",
FnDef {
args: vec![],
result: Some(Rc::new(ClassType(bar))),
},
);
let v0 = ctx.add_variable(VarDef {
name: "v0",
bound: vec![],
});
let v1 = ctx.add_variable(VarDef {
name: "v1",
bound: vec![ClassType(foo).into(), ClassType(bar).into()],
});
let v2 = ctx.add_variable(VarDef {
name: "v2",
bound: vec![
ClassType(foo).into(),
ClassType(bar).into(),
ctx.get_primitive(INT32_TYPE),
],
});
let mut ctx = get_inference_context(ctx);
ctx.assign("foo", Rc::new(ClassType(foo))).unwrap();
ctx.assign("bar", Rc::new(ClassType(bar))).unwrap();
ctx.assign("foobar", Rc::new(VirtualClassType(foo)))
.unwrap();
ctx.assign("v0", ctx.get_variable(v0)).unwrap();
ctx.assign("v1", ctx.get_variable(v1)).unwrap();
ctx.assign("v2", ctx.get_variable(v2)).unwrap();
ctx.assign("bot", Rc::new(BotType)).unwrap();
let ast = parse_expression("foo.a()").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ClassType(foo).into());
let ast = parse_expression("v1.a()").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), TypeVariable(v1).into());
let ast = parse_expression("foobar.a()").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ClassType(foo).into());
let ast = parse_expression("none().a()").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("no value".into()));
let ast = parse_expression("bot.a()").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("not supported".into()));
let ast = parse_expression("[][0].a()").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("not supported".into()));
let ast = parse_expression("v0.a()").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("unbounded type var".into()));
let ast = parse_expression("v2.a()").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("no such function".into()));
}
#[test]
fn infer_subscript() {
let mut ctx = basic_ctx();
ctx.add_fn(
"none",
FnDef {
args: vec![],
result: None,
},
);
let mut ctx = get_inference_context(ctx);
let ast = parse_expression("[1, 2, 3][0]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT32_TYPE));
let ast = parse_expression("[[1]][0][0]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT32_TYPE));
let ast = parse_expression("[1, 2, 3][1:2]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(
result.unwrap().unwrap(),
ParametricType(LIST_TYPE, vec![ctx.get_primitive(INT32_TYPE)]).into()
);
let ast = parse_expression("[1, 2, 3][1:2:2]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(
result.unwrap().unwrap(),
ParametricType(LIST_TYPE, vec![ctx.get_primitive(INT32_TYPE)]).into()
);
let ast = parse_expression("[1, 2, 3][1:1.2]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("slice must be int32 type".into()));
let ast = parse_expression("[1, 2, 3][1:none()]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("slice must have type".into()));
let ast = parse_expression("[1, 2, 3][1.2]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("index must be either slice or int32".into()));
let ast = parse_expression("[1, 2, 3][none()]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("no value".into()));
let ast = parse_expression("none()[1.2]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("no value".into()));
let ast = parse_expression("123[1]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(
result,
Err("subscript is not supported for types other than list".into())
);
}
#[test]
fn test_if_expr() {
let mut ctx = basic_ctx();
ctx.add_fn(
"none",
FnDef {
args: vec![],
result: None,
},
);
let mut ctx = get_inference_context(ctx);
let ast = parse_expression("1 if True else 0").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), ctx.get_primitive(INT32_TYPE));
let ast = parse_expression("none() if True else none()").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap(), None);
let ast = parse_expression("none() if 1 else none()").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("test should be bool".into()));
let ast = parse_expression("1 if True else none()").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("divergent type".into()));
}
#[test]
fn test_list_comp() {
let mut ctx = basic_ctx();
ctx.add_fn(
"none",
FnDef {
args: vec![],
result: None,
},
);
let int32 = ctx.get_primitive(INT32_TYPE);
let mut ctx = get_inference_context(ctx);
ctx.assign("z", int32.clone()).unwrap();
let ast = parse_expression("[x for x in [(1, 2), (2, 3), (3, 4)]][0]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(
result.unwrap().unwrap(),
ParametricType(TUPLE_TYPE, vec![int32.clone(), int32.clone()]).into()
);
let ast = parse_expression("[x for (x, y) in [(1, 2), (2, 3), (3, 4)]][0]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), int32);
let ast =
parse_expression("[x for (x, y) in [(1, 2), (2, 3), (3, 4)] if x > 0][0]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result.unwrap().unwrap(), int32);
let ast = parse_expression("[x for (x, y) in [(1, 2), (2, 3), (3, 4)] if x][0]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("test must be bool".into()));
let ast = parse_expression("[y for x in []][0]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("unbounded identifier".into()));
let ast = parse_expression("[none() for x in []][0]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("no value".into()));
let ast = parse_expression("[z for z in []][0]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("duplicated naming".into()));
let ast = parse_expression("[x for x in [] for y in []]").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(
result,
Err("only 1 generator statement is supported".into())
);
}
}

View File

@ -1,9 +0,0 @@
pub mod context;
pub mod expression_inference;
pub mod inference_core;
mod magic_methods;
pub mod primitives;
pub mod statement_check;
pub mod typedef;
pub mod signature;

View File

@ -1,503 +0,0 @@
/// obtain class and function signature from AST
use super::context::TopLevelContext;
use super::primitives::*;
use super::typedef::*;
use rustpython_parser::ast::{
ComprehensionKind, ExpressionType, Statement, StatementType, StringGroup,
};
use std::collections::HashMap;
// TODO: fix condition checking, return error message instead of panic...
fn typename_from_expr<'a>(typenames: &mut Vec<&'a str>, expr: &'a ExpressionType) {
match expr {
ExpressionType::Identifier { name } => typenames.push(&name),
ExpressionType::String { value } => match value {
StringGroup::Constant { value } => typenames.push(&value),
_ => unimplemented!(),
},
ExpressionType::Subscript { a, b } => {
typename_from_expr(typenames, &b.node);
typename_from_expr(typenames, &a.node)
}
_ => unimplemented!(),
}
}
fn typename_from_fn<'a>(typenames: &mut Vec<&'a str>, fun: &'a StatementType) {
match fun {
StatementType::FunctionDef { args, returns, .. } => {
for arg in args.args.iter() {
if let Some(ann) = &arg.annotation {
typename_from_expr(typenames, &ann.node);
}
}
if let Some(returns) = &returns {
typename_from_expr(typenames, &returns.node);
}
}
_ => unreachable!(),
}
}
fn name_from_expr<'a>(expr: &'a ExpressionType) -> &'a str {
match &expr {
ExpressionType::Identifier { name } => &name,
ExpressionType::String { value } => match value {
StringGroup::Constant { value } => &value,
_ => unimplemented!(),
},
_ => unimplemented!(),
}
}
fn type_from_expr<'a>(ctx: &'a TopLevelContext, expr: &'a ExpressionType) -> Result<Type, String> {
match expr {
ExpressionType::Identifier { name } => {
ctx.get_type(name).ok_or_else(|| "no such type".into())
}
ExpressionType::String { value } => match value {
StringGroup::Constant { value } => {
ctx.get_type(&value).ok_or_else(|| "no such type".into())
}
_ => unimplemented!(),
},
ExpressionType::Subscript { a, b } => {
if let ExpressionType::Identifier { name } = &a.node {
match name.as_str() {
"list" => {
let ty = type_from_expr(ctx, &b.node)?;
Ok(TypeEnum::ParametricType(LIST_TYPE, vec![ty]).into())
}
"tuple" => {
if let ExpressionType::Tuple { elements } = &b.node {
let ty_list: Result<Vec<_>, _> = elements
.iter()
.map(|v| type_from_expr(ctx, &v.node))
.collect();
Ok(TypeEnum::ParametricType(TUPLE_TYPE, ty_list?).into())
} else {
Err("unsupported format".into())
}
}
_ => Err("no such parameterized type".into()),
}
} else {
// we require a to be an identifier, for a[b]
Err("unsupported format".into())
}
}
_ => Err("unsupported format".into()),
}
}
pub fn get_typenames<'a>(stmts: &'a [Statement]) -> (Vec<&'a str>, Vec<&'a str>) {
let mut classes = Vec::new();
let mut typenames = Vec::new();
for stmt in stmts.iter() {
match &stmt.node {
StatementType::ClassDef {
name, body, bases, ..
} => {
// check if class is not duplicated...
// and annotations
classes.push(&name[..]);
for base in bases.iter() {
let name = name_from_expr(&base.node);
typenames.push(name);
}
// may check if fields/functions are not duplicated
for stmt in body.iter() {
match &stmt.node {
StatementType::AnnAssign { annotation, .. } => {
typename_from_expr(&mut typenames, &annotation.node)
}
StatementType::FunctionDef { .. } => {
typename_from_fn(&mut typenames, &stmt.node);
}
_ => unimplemented!(),
}
}
}
StatementType::FunctionDef { .. } => {
// may check annotations
typename_from_fn(&mut typenames, &stmt.node);
}
_ => (),
}
}
let mut unknowns = Vec::new();
for n in typenames {
if !PRIMITIVES.contains(&n) && !classes.contains(&n) && !unknowns.contains(&n) {
unknowns.push(n);
}
}
(classes, unknowns)
}
fn resolve_function<'a>(
ctx: &'a TopLevelContext,
fun: &'a StatementType,
method: bool,
) -> Result<FnDef, String> {
if let StatementType::FunctionDef { args, returns, .. } = &fun {
let args = if method {
args.args[1..].iter()
} else {
args.args.iter()
};
let args: Result<Vec<_>, _> = args
.map(|arg| type_from_expr(ctx, &arg.annotation.as_ref().unwrap().node))
.collect();
let args = args?;
let result = match returns {
Some(v) => Some(type_from_expr(ctx, &v.node)?),
None => None,
};
Ok(FnDef { args, result })
} else {
unreachable!()
}
}
fn get_expr_unknowns<'a>(
defined: &mut Vec<&'a str>,
unknowns: &mut Vec<&'a str>,
expr: &'a ExpressionType,
) {
match expr {
ExpressionType::BoolOp { values, .. } => {
for v in values.iter() {
get_expr_unknowns(defined, unknowns, &v.node)
}
}
ExpressionType::Binop { a, b, .. } => {
get_expr_unknowns(defined, unknowns, &a.node);
get_expr_unknowns(defined, unknowns, &b.node);
}
ExpressionType::Subscript { a, b } => {
get_expr_unknowns(defined, unknowns, &a.node);
get_expr_unknowns(defined, unknowns, &b.node);
}
ExpressionType::Unop { a, .. } => {
get_expr_unknowns(defined, unknowns, &a.node);
}
ExpressionType::Compare { vals, .. } => {
for v in vals.iter() {
get_expr_unknowns(defined, unknowns, &v.node)
}
}
ExpressionType::Attribute { value, .. } => {
get_expr_unknowns(defined, unknowns, &value.node);
}
ExpressionType::Call { function, args, .. } => {
get_expr_unknowns(defined, unknowns, &function.node);
for v in args.iter() {
get_expr_unknowns(defined, unknowns, &v.node)
}
}
ExpressionType::List { elements } => {
for v in elements.iter() {
get_expr_unknowns(defined, unknowns, &v.node)
}
}
ExpressionType::Tuple { elements } => {
for v in elements.iter() {
get_expr_unknowns(defined, unknowns, &v.node)
}
}
ExpressionType::Comprehension { kind, generators } => {
if generators.len() != 1 {
unimplemented!()
}
let g = &generators[0];
get_expr_unknowns(defined, unknowns, &g.iter.node);
let mut scoped = defined.clone();
get_expr_unknowns(defined, &mut scoped, &g.target.node);
for if_expr in g.ifs.iter() {
get_expr_unknowns(&mut scoped, unknowns, &if_expr.node);
}
match kind.as_ref() {
ComprehensionKind::List { element } => {
get_expr_unknowns(&mut scoped, unknowns, &element.node);
}
_ => unimplemented!(),
}
}
ExpressionType::Slice { elements } => {
for v in elements.iter() {
get_expr_unknowns(defined, unknowns, &v.node);
}
}
ExpressionType::Identifier { name } => {
if !defined.contains(&name.as_str()) && !unknowns.contains(&name.as_str()) {
unknowns.push(name);
}
}
ExpressionType::IfExpression { test, body, orelse } => {
get_expr_unknowns(defined, unknowns, &test.node);
get_expr_unknowns(defined, unknowns, &body.node);
get_expr_unknowns(defined, unknowns, &orelse.node);
}
_ => (),
};
}
struct ExprPattern<'a>(&'a ExpressionType, Vec<usize>, bool);
impl<'a> ExprPattern<'a> {
fn new(expr: &'a ExpressionType) -> ExprPattern {
let mut pattern = ExprPattern(expr, Vec::new(), true);
pattern.find_leaf();
pattern
}
fn pointed(&mut self) -> &'a ExpressionType {
let mut current = self.0;
for v in self.1.iter() {
if let ExpressionType::Tuple { elements } = current {
current = &elements[*v].node
} else {
unreachable!()
}
}
current
}
fn find_leaf(&mut self) {
let mut current = self.pointed();
while let ExpressionType::Tuple { elements } = current {
if elements.is_empty() {
break;
}
current = &elements[0].node;
self.1.push(0);
}
}
fn inc(&mut self) -> bool {
loop {
if self.1.is_empty() {
return false;
}
let ind = self.1.pop().unwrap() + 1;
let parent = self.pointed();
if let ExpressionType::Tuple { elements } = parent {
if ind < elements.len() {
self.1.push(ind);
self.find_leaf();
return true;
}
} else {
unreachable!()
}
}
}
}
impl<'a> Iterator for ExprPattern<'a> {
type Item = &'a ExpressionType;
fn next(&mut self) -> Option<Self::Item> {
if self.2 {
self.2 = false;
Some(self.pointed())
} else if self.inc() {
Some(self.pointed())
} else {
None
}
}
}
fn get_stmt_unknowns<'a>(
defined: &mut Vec<&'a str>,
unknowns: &mut Vec<&'a str>,
stmts: &'a [Statement],
) {
for stmt in stmts.iter() {
match &stmt.node {
StatementType::Return { value } => {
if let Some(value) = value {
get_expr_unknowns(defined, unknowns, &value.node);
}
}
StatementType::Assign { targets, value } => {
get_expr_unknowns(defined, unknowns, &value.node);
for target in targets.iter() {
for node in ExprPattern::new(&target.node).into_iter() {
if let ExpressionType::Identifier { name } = node {
let name = name.as_str();
if !defined.contains(&name) {
defined.push(name);
}
} else {
get_expr_unknowns(defined, unknowns, node);
}
}
}
}
StatementType::AugAssign { target, value, .. } => {
get_expr_unknowns(defined, unknowns, &target.node);
get_expr_unknowns(defined, unknowns, &value.node);
}
StatementType::AnnAssign { target, value, .. } => {
get_expr_unknowns(defined, unknowns, &target.node);
if let Some(value) = value {
get_expr_unknowns(defined, unknowns, &value.node);
}
}
StatementType::Expression { expression } => {
get_expr_unknowns(defined, unknowns, &expression.node);
}
StatementType::Global { names } => {
for name in names.iter() {
let name = name.as_str();
if !unknowns.contains(&name) {
unknowns.push(name);
}
}
}
StatementType::If { test, body, orelse }
| StatementType::While { test, body, orelse } => {
get_expr_unknowns(defined, unknowns, &test.node);
// we are not very strict at this point...
// some identifiers not treated as unknowns may not be resolved
// but should be checked during type inference
get_stmt_unknowns(defined, unknowns, body.as_slice());
if let Some(orelse) = orelse {
get_stmt_unknowns(defined, unknowns, orelse.as_slice());
}
}
StatementType::For { is_async, target, iter, body, orelse } => {
if *is_async {
unimplemented!()
}
get_expr_unknowns(defined, unknowns, &iter.node);
for node in ExprPattern::new(&target.node).into_iter() {
if let ExpressionType::Identifier { name } = node {
let name = name.as_str();
if !defined.contains(&name) {
defined.push(name);
}
} else {
get_expr_unknowns(defined, unknowns, node);
}
}
get_stmt_unknowns(defined, unknowns, body.as_slice());
if let Some(orelse) = orelse {
get_stmt_unknowns(defined, unknowns, orelse.as_slice());
}
}
_ => (),
}
}
}
pub fn resolve_signatures<'a>(ctx: &mut TopLevelContext<'a>, stmts: &'a [Statement]) {
for stmt in stmts.iter() {
match &stmt.node {
StatementType::ClassDef {
name, bases, body, ..
} => {
let mut parents = Vec::new();
for base in bases.iter() {
let name = name_from_expr(&base.node);
let c = ctx.get_type(name).unwrap();
let id = if let TypeEnum::ClassType(id) = c.as_ref() {
*id
} else {
unreachable!()
};
parents.push(id);
}
let mut fields = HashMap::new();
let mut functions = HashMap::new();
for stmt in body.iter() {
match &stmt.node {
StatementType::AnnAssign {
target, annotation, ..
} => {
let name = name_from_expr(&target.node);
let ty = type_from_expr(ctx, &annotation.node).unwrap();
fields.insert(name, ty);
}
StatementType::FunctionDef { name, .. } => {
functions.insert(
&name[..],
resolve_function(ctx, &stmt.node, true).unwrap(),
);
}
_ => unimplemented!(),
}
}
let class = ctx.get_type(name).unwrap();
let class = if let TypeEnum::ClassType(id) = class.as_ref() {
ctx.get_class_def_mut(*id)
} else {
unreachable!()
};
class.parents.extend_from_slice(&parents);
class.base.fields.clone_from(&fields);
class.base.methods.clone_from(&functions);
}
StatementType::FunctionDef { name, .. } => {
ctx.add_fn(&name[..], resolve_function(ctx, &stmt.node, false).unwrap());
}
_ => unimplemented!(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
use rustpython_parser::parser::{parse_program, parse_statement};
#[test]
fn test_get_classes() {
let ast = parse_program(indoc! {"
class Foo:
a: int32
b: Test
def test(self, a: int32) -> Test2:
return self.b
class Bar(Foo, 'FooBar'):
def test2(self, a: list[Foo]) -> Test2:
return self.b
def test3(self, a: list[FooBar2]) -> Test2:
return self.b
" })
.unwrap();
let (mut classes, mut unknowns) = get_typenames(&ast.statements);
let classes_count = classes.len();
let unknowns_count = unknowns.len();
classes.sort();
unknowns.sort();
assert_eq!(classes.len(), classes_count);
assert_eq!(unknowns.len(), unknowns_count);
assert_eq!(&classes, &["Bar", "Foo"]);
assert_eq!(&unknowns, &["FooBar", "FooBar2", "Test", "Test2"]);
}
#[test]
fn test_assignment() {
let ast = parse_statement(indoc! {"
((a, b), c[i]) = core.foo(x, get_y())
" })
.unwrap();
let mut defined = Vec::new();
let mut unknowns = Vec::new();
get_stmt_unknowns(&mut defined, &mut unknowns, ast.as_slice());
defined.sort();
unknowns.sort();
assert_eq!(defined.as_slice(), &["a", "b"]);
assert_eq!(unknowns.as_slice(), &["c", "core", "get_y", "i", "x"]);
}
}

View File

@ -1,572 +0,0 @@
use super::context::InferenceContext;
use super::expression_inference::{infer_expr, infer_simple_binding};
use super::inference_core::resolve_call;
use super::magic_methods::binop_assign_name;
use super::primitives::*;
use super::typedef::{Type, TypeEnum::*};
use rustpython_parser::ast::*;
pub fn check_stmts<'b: 'a, 'a>(
ctx: &mut InferenceContext<'a>,
stmts: &'b [Statement],
) -> Result<bool, String> {
for stmt in stmts.iter() {
match &stmt.node {
StatementType::Assign { targets, value } => {
check_assign(ctx, targets.as_slice(), &value)?;
}
StatementType::AugAssign { target, op, value } => {
check_aug_assign(ctx, &target, op, &value)?;
}
StatementType::If { test, body, orelse } => {
if check_if(ctx, test, body.as_slice(), orelse)? {
return Ok(true);
}
}
StatementType::While { test, body, orelse } => {
check_while_stmt(ctx, test, body.as_slice(), orelse)?;
}
StatementType::For {
is_async,
target,
iter,
body,
orelse,
} => {
if *is_async {
return Err("async for is not supported".to_string());
}
check_for_stmt(ctx, target, iter, body.as_slice(), orelse)?;
}
StatementType::Return { value } => {
let result = ctx.get_result();
let t = if let Some(value) = value {
infer_expr(ctx, value)?
} else {
None
};
return if t == result {
Ok(true)
} else {
Err("return type mismatch".to_string())
};
}
StatementType::Continue | StatementType::Break => {
continue;
}
_ => return Err("not supported".to_string()),
}
}
Ok(false)
}
fn get_target_type<'b: 'a, 'a>(
ctx: &mut InferenceContext<'a>,
target: &'b Expression,
) -> Result<Type, String> {
match &target.node {
ExpressionType::Subscript { a, b } => {
let int32 = ctx.get_primitive(INT32_TYPE);
if infer_expr(ctx, &a)? == Some(int32) {
let b = get_target_type(ctx, &b)?;
if let ParametricType(LIST_TYPE, t) = b.as_ref() {
Ok(t[0].clone())
} else {
Err("subscript is only supported for list".to_string())
}
} else {
Err("subscript must be int32".to_string())
}
}
ExpressionType::Attribute { value, name } => {
let t = get_target_type(ctx, &value)?;
let base = t.get_base(ctx).ok_or_else(|| "no attributes".to_string())?;
Ok(base
.fields
.get(name.as_str())
.ok_or_else(|| "no such attribute")?
.clone())
}
ExpressionType::Identifier { name } => Ok(ctx.resolve(name.as_str())?),
_ => Err("not supported".to_string()),
}
}
fn check_stmt_binding<'b: 'a, 'a>(
ctx: &mut InferenceContext<'a>,
target: &'b Expression,
ty: Type,
) -> Result<(), String> {
match &target.node {
ExpressionType::Identifier { name } => {
if name.as_str() == "_" {
Ok(())
} else {
match ctx.resolve(name.as_str()) {
Ok(t) if t == ty => Ok(()),
Err(_) => {
ctx.assign(name.as_str(), ty).unwrap();
Ok(())
}
_ => Err("conflicting type".into()),
}
}
}
ExpressionType::Tuple { elements } => {
if let ParametricType(TUPLE_TYPE, ls) = ty.as_ref() {
if ls.len() != elements.len() {
return Err("incorrect pattern length".into());
}
for (x, y) in elements.iter().zip(ls.iter()) {
check_stmt_binding(ctx, x, y.clone())?;
}
Ok(())
} else {
Err("pattern matching supports tuple only".into())
}
}
_ => {
let t = get_target_type(ctx, target)?;
if ty == t {
Ok(())
} else {
Err("type mismatch".into())
}
}
}
}
fn check_assign<'b: 'a, 'a>(
ctx: &mut InferenceContext<'a>,
targets: &'b [Expression],
value: &'b Expression,
) -> Result<(), String> {
let ty = infer_expr(ctx, value)?.ok_or_else(|| "no value".to_string())?;
for t in targets.iter() {
check_stmt_binding(ctx, t, ty.clone())?;
}
Ok(())
}
fn check_aug_assign<'b: 'a, 'a>(
ctx: &mut InferenceContext<'a>,
target: &'b Expression,
op: &'b Operator,
value: &'b Expression,
) -> Result<(), String> {
let left = infer_expr(ctx, target)?.ok_or_else(|| "no value".to_string())?;
let right = infer_expr(ctx, value)?.ok_or_else(|| "no value".to_string())?;
let fun = binop_assign_name(op);
resolve_call(ctx, Some(left), fun, &[right])?;
Ok(())
}
fn check_if<'b: 'a, 'a>(
ctx: &mut InferenceContext<'a>,
test: &'b Expression,
body: &'b [Statement],
orelse: &'b Option<Suite>,
) -> Result<bool, String> {
let boolean = ctx.get_primitive(BOOL_TYPE);
let t = infer_expr(ctx, test)?;
if t == Some(boolean) {
let (names, result) = ctx.with_scope(|ctx| check_stmts(ctx, body));
let returned = result?;
if let Some(orelse) = orelse {
let (names2, result) = ctx.with_scope(|ctx| check_stmts(ctx, orelse.as_slice()));
let returned = returned && result?;
for (name, ty) in names.iter() {
for (name2, ty2) in names2.iter() {
if *name == *name2 && ty == ty2 {
ctx.assign(name, ty.clone()).unwrap();
}
}
}
Ok(returned)
} else {
Ok(false)
}
} else {
Err("condition should be bool".to_string())
}
}
fn check_while_stmt<'b: 'a, 'a>(
ctx: &mut InferenceContext<'a>,
test: &'b Expression,
body: &'b [Statement],
orelse: &'b Option<Suite>,
) -> Result<bool, String> {
let boolean = ctx.get_primitive(BOOL_TYPE);
let t = infer_expr(ctx, test)?;
if t == Some(boolean) {
// to check what variables are defined, we would have to do a graph analysis...
// not implemented now
let (_, result) = ctx.with_scope(|ctx| check_stmts(ctx, body));
result?;
if let Some(orelse) = orelse {
let (_, result) = ctx.with_scope(|ctx| check_stmts(ctx, orelse.as_slice()));
result?;
}
// to check whether the loop returned on every possible path, we need to analyse the graph,
// not implemented now
Ok(false)
} else {
Err("condition should be bool".to_string())
}
}
fn check_for_stmt<'b: 'a, 'a>(
ctx: &mut InferenceContext<'a>,
target: &'b Expression,
iter: &'b Expression,
body: &'b [Statement],
orelse: &'b Option<Suite>,
) -> Result<bool, String> {
let ty = infer_expr(ctx, iter)?.ok_or_else(|| "no value".to_string())?;
if let ParametricType(LIST_TYPE, ls) = ty.as_ref() {
let (_, result) = ctx.with_scope(|ctx| {
infer_simple_binding(ctx, target, ls[0].clone())?;
check_stmts(ctx, body)
});
result?;
if let Some(orelse) = orelse {
let (_, result) = ctx.with_scope(|ctx| check_stmts(ctx, orelse.as_slice()));
result?;
}
// to check whether the loop returned on every possible path, we need to analyse the graph,
// not implemented now
Ok(false)
} else {
Err("only list can be iterated over".to_string())
}
}
#[cfg(test)]
mod test {
use super::{super::context::*, *};
use indoc::indoc;
use rustpython_parser::parser::parse_program;
fn get_inference_context(ctx: TopLevelContext) -> InferenceContext {
InferenceContext::new(ctx, Box::new(|_| Err("unbounded identifier".into())))
}
#[test]
fn test_assign() {
let ctx = basic_ctx();
let mut ctx = get_inference_context(ctx);
let ast = parse_program(indoc! {"
a = 1
b = a * 2
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
});
let ast = parse_program(indoc! {"
a = 1
b = b * 2
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(
Err("unbounded identifier".to_string()),
check_stmts(ctx, ast.statements.as_slice())
);
});
let ast = parse_program(indoc! {"
b = a = 1
b = b * 2
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
});
let ast = parse_program(indoc! {"
b = a = 1
b = [a]
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(
Err("conflicting type".to_string()),
check_stmts(ctx, ast.statements.as_slice())
);
});
}
#[test]
fn test_if() {
let ctx = basic_ctx();
let mut ctx = get_inference_context(ctx);
let ast = parse_program(indoc! {"
a = 1
b = a * 2
if b > a:
c = 1
else:
c = 0
d = c
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
});
let ast = parse_program(indoc! {"
a = 1
b = a * 2
if b > a:
c = 1
else:
d = 0
d = c
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(
Err("unbounded identifier".to_string()),
check_stmts(ctx, ast.statements.as_slice())
);
});
let ast = parse_program(indoc! {"
a = 1
b = a * 2
if b > a:
c = 1
d = c
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(
Err("unbounded identifier".to_string()),
check_stmts(ctx, ast.statements.as_slice())
);
});
let ast = parse_program(indoc! {"
a = 1
b = a * 2
if a:
b = 0
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(
Err("condition should be bool".to_string()),
check_stmts(ctx, ast.statements.as_slice())
);
});
let ast = parse_program(indoc! {"
a = 1
b = a * 2
if b > a:
c = 1
c = [1]
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
});
let ast = parse_program(indoc! {"
a = 1
b = a * 2
if b > a:
c = 1
else:
c = 0
c = [1]
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(
Err("conflicting type".to_string()),
check_stmts(ctx, ast.statements.as_slice())
);
});
}
#[test]
fn test_while() {
let ctx = basic_ctx();
let mut ctx = get_inference_context(ctx);
let ast = parse_program(indoc! {"
a = 1
b = 1
while a < 10:
a += 1
b *= a
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
});
let ast = parse_program(indoc! {"
a = 1
b = 1
while a < 10:
a += 1
b *= a
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
});
let ast = parse_program(indoc! {"
a = 1
b = 1
while a < 10:
a += 1
b *= a
else:
a += 1
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
});
let ast = parse_program(indoc! {"
a = 1
b = 1
while a:
a += 1
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(
Err("condition should be bool".to_string()),
check_stmts(ctx, ast.statements.as_slice())
);
});
let ast = parse_program(indoc! {"
a = 1
b = 1
while a < 10:
a += 1
c = a*2
else:
c = a*2
b = c
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(
Err("unbounded identifier".to_string()),
check_stmts(ctx, ast.statements.as_slice())
);
});
}
#[test]
fn test_for() {
let ctx = basic_ctx();
let mut ctx = get_inference_context(ctx);
let ast = parse_program(indoc! {"
b = 1
for a in [0, 1, 2, 3, 4, 5]:
b *= a
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
});
let ast = parse_program(indoc! {"
b = 1
for a, a1 in [(0, 1), (2, 3), (4, 5)]:
b *= a
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
});
}
#[test]
fn test_return() {
let ctx = basic_ctx();
let mut ctx = get_inference_context(ctx);
let ast = parse_program(indoc! {"
b = 1
return
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(true), check_stmts(ctx, ast.statements.as_slice()));
});
let ast = parse_program(indoc! {"
b = 1
if b > 0:
return
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
});
let ast = parse_program(indoc! {"
b = 1
if b > 0:
return
else:
return
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(true), check_stmts(ctx, ast.statements.as_slice()));
});
let ast = parse_program(indoc! {"
b = 1
while b > 0:
return
else:
return
" })
.unwrap();
ctx.with_scope(|ctx| {
// with sophisticated analysis, this one should be Ok(true)
// but with our simple implementation, this is Ok(false)
// as we don't analyse the control flow
assert_eq!(Ok(false), check_stmts(ctx, ast.statements.as_slice()));
});
ctx.set_result(Some(ctx.get_primitive(INT32_TYPE)));
let ast = parse_program(indoc! {"
b = 1
return 1
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(Ok(true), check_stmts(ctx, ast.statements.as_slice()));
});
ctx.set_result(Some(ctx.get_primitive(INT32_TYPE)));
let ast = parse_program(indoc! {"
b = 1
return [1]
" })
.unwrap();
ctx.with_scope(|ctx| {
assert_eq!(
Err("return type mismatch".to_string()),
check_stmts(ctx, ast.statements.as_slice())
);
});
}
}

View File

@ -1,60 +0,0 @@
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 TypeEnum {
BotType,
SelfType,
PrimitiveType(PrimitiveId),
ClassType(ClassId),
VirtualClassType(ClassId),
ParametricType(ParamId, Vec<Rc<TypeEnum>>),
TypeVariable(VariableId),
}
pub type Type = Rc<TypeEnum>;
#[derive(Clone)]
pub struct FnDef {
// we assume methods first argument to be SelfType,
// so the first argument is not contained here
pub args: Vec<Type>,
pub result: Option<Type>,
}
#[derive(Clone)]
pub struct TypeDef<'a> {
pub name: &'a str,
pub fields: HashMap<&'a str, 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<Type>,
}

223
nac3core/src/typedef.rs Normal file
View 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,
}
}
}

View File

@ -4,6 +4,6 @@ in
pkgs.stdenv.mkDerivation { pkgs.stdenv.mkDerivation {
name = "nac3-env"; name = "nac3-env";
buildInputs = with pkgs; [ buildInputs = with pkgs; [
llvm_10 clang_10 cargo rustc libffi libxml2 clippy llvm_10 clang_10 cargo rustc libffi libxml2
]; ];
} }

View File

@ -1,34 +0,0 @@
Errors:
- Not supported
- Only * is supported
- Expected * in *, but got *
- Divergent type in (construct), (location 1), (location 2)
- Unknown field
- Unbounded variable
- Different variable
- Different domain
- * is not subclass of *
- Type not equal
- Incorrect number of parameters
Symbol Resolution:
- Add all files with annotated class/functions.
- Find class references, load them all in TopLevelContext.
- Find unbounded identifiers in the functions.
- If it is a function/class name, record its object ID.
- Otherwise, load its value. (check to see if specified with `global`)
(Function implemented in python, with rust binding to add value to global
variable dictionary)
Global variable dictionary:
- Primitives, including integers, floats, bools, etc.
- Primitive lists.
- Numpy multi-dimensional array, with value + dimension vectors.
- Reference array, with integer index referring to other things.
- Symbol table: python id -> reference id.
TopLevelContext/InferenceContext:
- Restrict visibility by user defined function.