forked from M-Labs/nac3
Compare commits
19 Commits
Author | SHA1 | Date |
---|---|---|
pca006132 | f691fbb4dc | |
pca006132 | 44f4b7cfc7 | |
pca006132 | 584d8a490f | |
pca006132 | 67963fc21f | |
pca006132 | 2b2e220723 | |
pca006132 | fa2fc1db54 | |
pca006132 | 8ade8c7b1f | |
pca006132 | dea987dfde | |
pca006132 | 231bcd95fc | |
pca006132 | 44ccae6959 | |
pca006132 | 0d20b36974 | |
pca006132 | 0269dcee84 | |
pca006132 | 8fd164bda2 | |
pca006132 | e625bd4ad2 | |
pca006132 | f1b8e73703 | |
pca006132 | 38a3240e6e | |
pca006132 | 9cba777b3b | |
pca006132 | bd67b3f387 | |
pca006132 | 59453d3785 |
|
@ -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"
|
||||||
|
|
|
@ -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"
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::super::typedef::*;
|
|
||||||
use super::TopLevelContext;
|
use super::TopLevelContext;
|
||||||
|
use crate::typedef::*;
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
@ -22,14 +22,14 @@ pub struct InferenceContext<'a> {
|
||||||
primitives: Vec<Type>,
|
primitives: Vec<Type>,
|
||||||
/// list of variable instances
|
/// list of variable instances
|
||||||
variables: Vec<Type>,
|
variables: Vec<Type>,
|
||||||
/// identifier to type mapping.
|
/// identifier to (type, readable) mapping.
|
||||||
sym_table: HashMap<&'a str, Type>,
|
/// an identifier might be defined earlier but has no value (for some code path), thus not
|
||||||
|
/// readable.
|
||||||
|
sym_table: HashMap<&'a str, (Type, bool)>,
|
||||||
/// resolution function reference, that may resolve unbounded identifiers to some type
|
/// resolution function reference, that may resolve unbounded identifiers to some type
|
||||||
resolution_fn: Box<dyn FnMut(&str) -> Result<Type, String>>,
|
resolution_fn: Box<dyn FnMut(&str) -> Result<Type, String>>,
|
||||||
/// stack
|
/// stack
|
||||||
stack: ContextStack<'a>,
|
stack: ContextStack<'a>,
|
||||||
/// return type
|
|
||||||
result: Option<Type>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// non-trivial implementations here
|
// non-trivial implementations here
|
||||||
|
@ -56,7 +56,6 @@ impl<'a> InferenceContext<'a> {
|
||||||
var_defs: Vec::new(),
|
var_defs: Vec::new(),
|
||||||
sym_def: Vec::new(),
|
sym_def: Vec::new(),
|
||||||
},
|
},
|
||||||
result: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +63,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
/// variable assignment would be limited within the scope (not readable outside), and type
|
/// variable assignment would be limited within the scope (not readable outside), and type
|
||||||
/// variable type guard would be limited within the scope.
|
/// variable type guard would be limited within the scope.
|
||||||
/// returns the list of variables assigned within the scope, and the result of the function
|
/// 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)
|
pub fn with_scope<F, R>(&mut self, f: F) -> (Vec<&'a str>, R)
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Self) -> R,
|
F: FnOnce(&mut Self) -> R,
|
||||||
{
|
{
|
||||||
|
@ -85,8 +84,8 @@ impl<'a> InferenceContext<'a> {
|
||||||
let (_, level) = self.stack.sym_def.last().unwrap();
|
let (_, level) = self.stack.sym_def.last().unwrap();
|
||||||
if *level > self.stack.level {
|
if *level > self.stack.level {
|
||||||
let (name, _) = self.stack.sym_def.pop().unwrap();
|
let (name, _) = self.stack.sym_def.pop().unwrap();
|
||||||
let ty = self.sym_table.remove(name).unwrap();
|
self.sym_table.remove(name).unwrap();
|
||||||
poped_names.push((name, ty));
|
poped_names.push(name);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -97,15 +96,19 @@ impl<'a> InferenceContext<'a> {
|
||||||
/// assign a type to an identifier.
|
/// assign a type to an identifier.
|
||||||
/// may return error if the identifier was defined but with different type
|
/// 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> {
|
pub fn assign(&mut self, name: &'a str, ty: Type) -> Result<Type, String> {
|
||||||
if let Some(t) = self.sym_table.get_mut(name) {
|
if let Some((t, x)) = self.sym_table.get_mut(name) {
|
||||||
if t == &ty {
|
if t == &ty {
|
||||||
|
if !*x {
|
||||||
|
self.stack.sym_def.push((name, self.stack.level));
|
||||||
|
}
|
||||||
|
*x = true;
|
||||||
Ok(ty)
|
Ok(ty)
|
||||||
} else {
|
} else {
|
||||||
Err("different types".into())
|
Err("different types".into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.stack.sym_def.push((name, self.stack.level));
|
self.stack.sym_def.push((name, self.stack.level));
|
||||||
self.sym_table.insert(name, ty.clone());
|
self.sym_table.insert(name, (ty.clone(), true));
|
||||||
Ok(ty)
|
Ok(ty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,8 +122,12 @@ impl<'a> InferenceContext<'a> {
|
||||||
/// may return error if the identifier is not defined, and cannot be resolved with the
|
/// may return error if the identifier is not defined, and cannot be resolved with the
|
||||||
/// resolution function.
|
/// resolution function.
|
||||||
pub fn resolve(&mut self, name: &str) -> Result<Type, String> {
|
pub fn resolve(&mut self, name: &str) -> Result<Type, String> {
|
||||||
if let Some(t) = self.sym_table.get(name) {
|
if let Some((t, x)) = self.sym_table.get(name) {
|
||||||
Ok(t.clone())
|
if *x {
|
||||||
|
Ok(t.clone())
|
||||||
|
} else {
|
||||||
|
Err("may not have value".into())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.resolution_fn.as_mut()(name)
|
self.resolution_fn.as_mut()(name)
|
||||||
}
|
}
|
||||||
|
@ -132,10 +139,6 @@ impl<'a> InferenceContext<'a> {
|
||||||
std::mem::swap(self.top_level.var_defs.get_mut(id.0).unwrap(), &mut def);
|
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));
|
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:
|
// trivial getters:
|
||||||
|
@ -165,9 +168,6 @@ impl<'a> InferenceContext<'a> {
|
||||||
pub fn get_type(&self, name: &str) -> Option<Type> {
|
pub fn get_type(&self, name: &str) -> Option<Type> {
|
||||||
self.top_level.get_type(name)
|
self.top_level.get_type(name)
|
||||||
}
|
}
|
||||||
pub fn get_result(&self) -> Option<Type> {
|
|
||||||
self.result.clone()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeEnum {
|
impl TypeEnum {
|
||||||
|
@ -215,7 +215,7 @@ impl TypeEnum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_base<'a>(&'a self, ctx: &'a InferenceContext) -> Option<&'a TypeDef> {
|
pub fn get_base<'b: 'a, 'a>(&'a self, ctx: &'b InferenceContext) -> Option<&'b TypeDef> {
|
||||||
match self {
|
match self {
|
||||||
TypeEnum::PrimitiveType(id) => Some(ctx.get_primitive_def(*id)),
|
TypeEnum::PrimitiveType(id) => Some(ctx.get_primitive_def(*id)),
|
||||||
TypeEnum::ClassType(id) | TypeEnum::VirtualClassType(id) => {
|
TypeEnum::ClassType(id) | TypeEnum::VirtualClassType(id) => {
|
|
@ -1,4 +1,4 @@
|
||||||
use super::super::typedef::*;
|
use crate::typedef::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
@ -130,9 +130,7 @@ impl<'a> TopLevelContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_type(&self, name: &str) -> Option<Type> {
|
pub fn get_type(&self, name: &str) -> Option<Type> {
|
||||||
// TODO: handle name visibility
|
// TODO: handle parametric types
|
||||||
// possibly by passing a function from outside to tell what names are allowed, and what are
|
|
||||||
// not...
|
|
||||||
self.sym_table.get(name).cloned()
|
self.sym_table.get(name).cloned()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
use super::context::InferenceContext;
|
use crate::context::InferenceContext;
|
||||||
use super::inference_core::resolve_call;
|
use crate::inference_core::resolve_call;
|
||||||
use super::magic_methods::*;
|
use crate::magic_methods::*;
|
||||||
use super::primitives::*;
|
use crate::primitives::*;
|
||||||
use super::typedef::{Type, TypeEnum::*};
|
use crate::typedef::{Type, TypeEnum::*};
|
||||||
use rustpython_parser::ast::{
|
use rustpython_parser::ast::{
|
||||||
Comparison, Comprehension, ComprehensionKind, Expression, ExpressionType, Operator,
|
Comparison, Comprehension, ComprehensionKind, Expression, ExpressionType, Operator,
|
||||||
UnaryOperator,
|
UnaryOperator,
|
||||||
|
@ -11,9 +11,9 @@ use std::convert::TryInto;
|
||||||
|
|
||||||
type ParserResult = Result<Option<Type>, String>;
|
type ParserResult = Result<Option<Type>, String>;
|
||||||
|
|
||||||
pub fn infer_expr<'a>(
|
pub fn infer_expr<'b: 'a, 'a>(
|
||||||
ctx: &mut InferenceContext<'a>,
|
ctx: &mut InferenceContext<'a>,
|
||||||
expr: &'a Expression,
|
expr: &'b Expression,
|
||||||
) -> ParserResult {
|
) -> ParserResult {
|
||||||
match &expr.node {
|
match &expr.node {
|
||||||
ExpressionType::Number { value } => infer_constant(ctx, value),
|
ExpressionType::Number { value } => infer_constant(ctx, value),
|
||||||
|
@ -83,9 +83,9 @@ fn infer_identifier(ctx: &mut InferenceContext, name: &str) -> ParserResult {
|
||||||
Ok(Some(ctx.resolve(name)?))
|
Ok(Some(ctx.resolve(name)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_list<'a>(
|
fn infer_list<'b: 'a, 'a>(
|
||||||
ctx: &mut InferenceContext<'a>,
|
ctx: &mut InferenceContext<'a>,
|
||||||
elements: &'a [Expression],
|
elements: &'b [Expression],
|
||||||
) -> ParserResult {
|
) -> ParserResult {
|
||||||
if elements.is_empty() {
|
if elements.is_empty() {
|
||||||
return Ok(Some(ParametricType(LIST_TYPE, vec![BotType.into()]).into()));
|
return Ok(Some(ParametricType(LIST_TYPE, vec![BotType.into()]).into()));
|
||||||
|
@ -105,9 +105,9 @@ fn infer_list<'a>(
|
||||||
Ok(Some(ParametricType(LIST_TYPE, vec![head.unwrap()]).into()))
|
Ok(Some(ParametricType(LIST_TYPE, vec![head.unwrap()]).into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_tuple<'a>(
|
fn infer_tuple<'b: 'a, 'a>(
|
||||||
ctx: &mut InferenceContext<'a>,
|
ctx: &mut InferenceContext<'a>,
|
||||||
elements: &'a [Expression],
|
elements: &'b [Expression],
|
||||||
) -> ParserResult {
|
) -> ParserResult {
|
||||||
let types: Result<Option<Vec<_>>, String> =
|
let types: Result<Option<Vec<_>>, String> =
|
||||||
elements.iter().map(|v| infer_expr(ctx, v)).collect();
|
elements.iter().map(|v| infer_expr(ctx, v)).collect();
|
||||||
|
@ -164,11 +164,11 @@ fn infer_bool_ops<'a>(ctx: &mut InferenceContext<'a>, values: &'a [Expression])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_bin_ops<'a>(
|
fn infer_bin_ops<'b: 'a, 'a>(
|
||||||
ctx: &mut InferenceContext<'a>,
|
ctx: &mut InferenceContext<'a>,
|
||||||
op: &Operator,
|
op: &Operator,
|
||||||
left: &'a Expression,
|
left: &'b Expression,
|
||||||
right: &'a Expression,
|
right: &'b Expression,
|
||||||
) -> ParserResult {
|
) -> ParserResult {
|
||||||
let left = infer_expr(ctx, left)?.ok_or_else(|| "no value".to_string())?;
|
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 right = infer_expr(ctx, right)?.ok_or_else(|| "no value".to_string())?;
|
||||||
|
@ -176,10 +176,10 @@ fn infer_bin_ops<'a>(
|
||||||
resolve_call(ctx, Some(left), fun, &[right])
|
resolve_call(ctx, Some(left), fun, &[right])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_unary_ops<'a>(
|
fn infer_unary_ops<'b: 'a, 'a>(
|
||||||
ctx: &mut InferenceContext<'a>,
|
ctx: &mut InferenceContext<'a>,
|
||||||
op: &UnaryOperator,
|
op: &UnaryOperator,
|
||||||
obj: &'a Expression,
|
obj: &'b Expression,
|
||||||
) -> ParserResult {
|
) -> ParserResult {
|
||||||
let ty = infer_expr(ctx, obj)?.ok_or_else(|| "no value".to_string())?;
|
let ty = infer_expr(ctx, obj)?.ok_or_else(|| "no value".to_string())?;
|
||||||
if let UnaryOperator::Not = op {
|
if let UnaryOperator::Not = op {
|
||||||
|
@ -193,10 +193,10 @@ fn infer_unary_ops<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_compare<'a>(
|
fn infer_compare<'b: 'a, 'a>(
|
||||||
ctx: &mut InferenceContext<'a>,
|
ctx: &mut InferenceContext<'a>,
|
||||||
vals: &'a [Expression],
|
vals: &'b [Expression],
|
||||||
ops: &'a [Comparison],
|
ops: &'b [Comparison],
|
||||||
) -> ParserResult {
|
) -> ParserResult {
|
||||||
let types: Result<Option<Vec<_>>, _> = vals.iter().map(|v| infer_expr(ctx, v)).collect();
|
let types: Result<Option<Vec<_>>, _> = vals.iter().map(|v| infer_expr(ctx, v)).collect();
|
||||||
let types = types?;
|
let types = types?;
|
||||||
|
@ -218,10 +218,10 @@ fn infer_compare<'a>(
|
||||||
Ok(Some(boolean))
|
Ok(Some(boolean))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_call<'a>(
|
fn infer_call<'b: 'a, 'a>(
|
||||||
ctx: &mut InferenceContext<'a>,
|
ctx: &mut InferenceContext<'a>,
|
||||||
args: &'a [Expression],
|
args: &'b [Expression],
|
||||||
function: &'a Expression,
|
function: &'b Expression,
|
||||||
) -> ParserResult {
|
) -> ParserResult {
|
||||||
let types: Result<Option<Vec<_>>, _> = args.iter().map(|v| infer_expr(ctx, v)).collect();
|
let types: Result<Option<Vec<_>>, _> = args.iter().map(|v| infer_expr(ctx, v)).collect();
|
||||||
let types = types?;
|
let types = types?;
|
||||||
|
@ -240,10 +240,10 @@ fn infer_call<'a>(
|
||||||
resolve_call(ctx, obj, fun.as_str(), &types.unwrap())
|
resolve_call(ctx, obj, fun.as_str(), &types.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_subscript<'a>(
|
fn infer_subscript<'b: 'a, 'a>(
|
||||||
ctx: &mut InferenceContext<'a>,
|
ctx: &mut InferenceContext<'a>,
|
||||||
a: &'a Expression,
|
a: &'b Expression,
|
||||||
b: &'a Expression,
|
b: &'b Expression,
|
||||||
) -> ParserResult {
|
) -> ParserResult {
|
||||||
let a = infer_expr(ctx, a)?.ok_or_else(|| "no value".to_string())?;
|
let a = infer_expr(ctx, a)?.ok_or_else(|| "no value".to_string())?;
|
||||||
let t = if let ParametricType(LIST_TYPE, ls) = a.as_ref() {
|
let t = if let ParametricType(LIST_TYPE, ls) = a.as_ref() {
|
||||||
|
@ -283,11 +283,11 @@ fn infer_subscript<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_if_expr<'a>(
|
fn infer_if_expr<'b: 'a, 'a>(
|
||||||
ctx: &mut InferenceContext<'a>,
|
ctx: &mut InferenceContext<'a>,
|
||||||
test: &'a Expression,
|
test: &'b Expression,
|
||||||
body: &'a Expression,
|
body: &'b Expression,
|
||||||
orelse: &'a Expression,
|
orelse: &'b Expression,
|
||||||
) -> ParserResult {
|
) -> ParserResult {
|
||||||
let test = infer_expr(ctx, test)?.ok_or_else(|| "no value".to_string())?;
|
let test = infer_expr(ctx, test)?.ok_or_else(|| "no value".to_string())?;
|
||||||
if test != ctx.get_primitive(BOOL_TYPE) {
|
if test != ctx.get_primitive(BOOL_TYPE) {
|
||||||
|
@ -303,8 +303,8 @@ fn infer_if_expr<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn infer_simple_binding<'a>(
|
fn infer_simple_binding<'a: 'b, 'b>(
|
||||||
ctx: &mut InferenceContext<'a>,
|
ctx: &mut InferenceContext<'b>,
|
||||||
name: &'a Expression,
|
name: &'a Expression,
|
||||||
ty: Type,
|
ty: Type,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
|
@ -337,10 +337,10 @@ pub fn infer_simple_binding<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_list_comprehension<'a>(
|
fn infer_list_comprehension<'b: 'a, 'a>(
|
||||||
ctx: &mut InferenceContext<'a>,
|
ctx: &mut InferenceContext<'a>,
|
||||||
element: &'a Expression,
|
element: &'b Expression,
|
||||||
comprehension: &'a Comprehension,
|
comprehension: &'b Comprehension,
|
||||||
) -> ParserResult {
|
) -> ParserResult {
|
||||||
if comprehension.is_async {
|
if comprehension.is_async {
|
||||||
return Err("async is not supported".into());
|
return Err("async is not supported".into());
|
||||||
|
@ -370,10 +370,9 @@ fn infer_list_comprehension<'a>(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{
|
use super::*;
|
||||||
super::{context::*, typedef::*},
|
use crate::context::*;
|
||||||
*,
|
use crate::typedef::*;
|
||||||
};
|
|
||||||
use rustpython_parser::parser::parse_expression;
|
use rustpython_parser::parser::parse_expression;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -705,7 +704,7 @@ mod test {
|
||||||
|
|
||||||
let ast = parse_expression("a == a == 1").unwrap();
|
let ast = parse_expression("a == a == 1").unwrap();
|
||||||
let result = infer_expr(&mut ctx, &ast);
|
let result = infer_expr(&mut ctx, &ast);
|
||||||
assert_eq!(result, Err("different types".into()));
|
assert_eq!(result, Err("not equal".into()));
|
||||||
|
|
||||||
let ast = parse_expression("True > False").unwrap();
|
let ast = parse_expression("True > False").unwrap();
|
||||||
let result = infer_expr(&mut ctx, &ast);
|
let result = infer_expr(&mut ctx, &ast);
|
|
@ -1,21 +1,6 @@
|
||||||
use super::context::InferenceContext;
|
use crate::context::InferenceContext;
|
||||||
use super::typedef::{TypeEnum::*, *};
|
use crate::typedef::{TypeEnum::*, *};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[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: &InferenceContext,
|
||||||
|
@ -23,7 +8,8 @@ fn find_subst(
|
||||||
sub: &mut HashMap<VariableId, Type>,
|
sub: &mut HashMap<VariableId, Type>,
|
||||||
mut a: Type,
|
mut a: Type,
|
||||||
mut b: Type,
|
mut b: 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,14 +33,14 @@ 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_def(*id_a);
|
||||||
let v_b = ctx.get_variable_def(*id_b);
|
let v_b = ctx.get_variable_def(*id_b);
|
||||||
if !v_b.bound.is_empty() {
|
if !v_b.bound.is_empty() {
|
||||||
if v_a.bound.is_empty() {
|
if v_a.bound.is_empty() {
|
||||||
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
|
||||||
|
@ -62,7 +48,7 @@ fn find_subst(
|
||||||
.filter(|x| !v_b.bound.contains(x))
|
.filter(|x| !v_b.bound.contains(x))
|
||||||
.collect();
|
.collect();
|
||||||
if !diff.is_empty() {
|
if !diff.is_empty() {
|
||||||
return Err(SubstError::IncompatibleBound(*id_a, *id_b));
|
return Err("different domain".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +60,7 @@ fn find_subst(
|
||||||
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)) => {
|
||||||
|
@ -83,7 +69,7 @@ fn find_subst(
|
||||||
sub.insert(*id_b, a.clone());
|
sub.insert(*id_b, a.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(SubstError::DifferentTypes(a.clone(), b.clone()))
|
Err("different domain".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_, VirtualClassType(id_b)) => {
|
(_, VirtualClassType(id_b)) => {
|
||||||
|
@ -96,7 +82,7 @@ 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() {
|
||||||
|
@ -106,11 +92,11 @@ fn find_subst(
|
||||||
let c = ctx.get_class_def(parents.remove(0));
|
let c = ctx.get_class_def(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,7 +108,7 @@ 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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,7 +177,7 @@ fn resolve_call_rec(
|
||||||
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| {
|
||||||
|
@ -214,11 +200,9 @@ pub fn resolve_call(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{
|
use super::*;
|
||||||
super::{context::*, primitives::*},
|
use crate::context::TopLevelContext;
|
||||||
*,
|
use crate::primitives::*;
|
||||||
};
|
|
||||||
use std::matches;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
fn get_inference_context(ctx: TopLevelContext) -> InferenceContext {
|
fn get_inference_context(ctx: TopLevelContext) -> InferenceContext {
|
||||||
|
@ -259,25 +243,25 @@ mod tests {
|
||||||
Ok(Some(ctx.get_primitive(FLOAT_TYPE)))
|
Ok(Some(ctx.get_primitive(FLOAT_TYPE)))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert_eq!(
|
||||||
resolve_call(&ctx, None, "float", &[ctx.get_primitive(BOOL_TYPE)]),
|
resolve_call(&ctx, None, "float", &[ctx.get_primitive(BOOL_TYPE)]),
|
||||||
Err(..)
|
Err("different domain".to_string())
|
||||||
));
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert_eq!(
|
||||||
resolve_call(&ctx, None, "float", &[]),
|
resolve_call(&ctx, None, "float", &[]),
|
||||||
Err(..)
|
Err("incorrect parameter number".to_string())
|
||||||
));
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve_call(&ctx, None, "float", &[v1]),
|
resolve_call(&ctx, None, "float", &[v1]),
|
||||||
Ok(Some(ctx.get_primitive(FLOAT_TYPE)))
|
Ok(Some(ctx.get_primitive(FLOAT_TYPE)))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert_eq!(
|
||||||
resolve_call(&ctx, None, "float", &[v2]),
|
resolve_call(&ctx, None, "float", &[v2]),
|
||||||
Err(..)
|
Err("different domain".to_string())
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -324,40 +308,40 @@ 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), "__add__", &[int64]),
|
||||||
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]),
|
||||||
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]),
|
||||||
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]),
|
||||||
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]),
|
||||||
Err(..)
|
Err("no such function".to_string())
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -409,10 +393,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,15 +416,15 @@ 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, v3.clone(), v3]).into()]
|
||||||
),
|
),
|
||||||
Err(..)
|
Err("different variables".to_string())
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -494,15 +478,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]).into()),
|
||||||
"append",
|
"append",
|
||||||
&[v1]
|
&[v1]
|
||||||
),
|
),
|
||||||
Err(..)
|
Err("different variables".to_string())
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -576,10 +560,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 +575,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 +593,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())
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,7 +5,12 @@ extern crate num_bigint;
|
||||||
extern crate inkwell;
|
extern crate inkwell;
|
||||||
extern crate rustpython_parser;
|
extern crate rustpython_parser;
|
||||||
|
|
||||||
pub mod type_check;
|
pub mod expression_inference;
|
||||||
|
pub mod inference_core;
|
||||||
|
mod magic_methods;
|
||||||
|
pub mod primitives;
|
||||||
|
pub mod typedef;
|
||||||
|
pub mod context;
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -229,7 +234,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
},
|
},
|
||||||
ast::ExpressionType::Identifier { name } => {
|
ast::ExpressionType::Identifier { name } => {
|
||||||
match self.namespace.get(name) {
|
match self.namespace.get(name) {
|
||||||
Some(value) => Ok(self.builder.build_load(*value, name).into()),
|
Some(value) => Ok(self.builder.build_load(*value, name)),
|
||||||
None => Err(self.compile_error(CompileErrorKind::UnboundIdentifier))
|
None => Err(self.compile_error(CompileErrorKind::UnboundIdentifier))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -411,10 +416,10 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
_ => Err(self.compile_error(CompileErrorKind::Unsupported("unrecognized call")))
|
_ => Err(self.compile_error(CompileErrorKind::Unsupported("unrecognized call")))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(self.compile_error(CompileErrorKind::Unsupported("function must be an identifier")))
|
Err(self.compile_error(CompileErrorKind::Unsupported("function must be an identifier")))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => return Err(self.compile_error(CompileErrorKind::Unsupported("unimplemented expression"))),
|
_ => Err(self.compile_error(CompileErrorKind::Unsupported("unimplemented expression"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,7 +525,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Return { value: None } => {
|
Return { value: None } => {
|
||||||
if !return_type.is_none() {
|
if return_type.is_some() {
|
||||||
return Err(self.compile_error(CompileErrorKind::IncompatibleTypes));
|
return Err(self.compile_error(CompileErrorKind::IncompatibleTypes));
|
||||||
}
|
}
|
||||||
self.builder.build_return(None);
|
self.builder.build_return(None);
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use super::context::*;
|
|
||||||
use super::typedef::{TypeEnum::*, *};
|
use super::typedef::{TypeEnum::*, *};
|
||||||
|
use crate::context::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
@ -19,11 +17,8 @@ fn impl_math(def: &mut TypeDef, ty: &Type) {
|
||||||
result: result.clone(),
|
result: result.clone(),
|
||||||
};
|
};
|
||||||
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(
|
def.methods.insert(
|
||||||
"__neg__",
|
"__neg__",
|
||||||
FnDef {
|
FnDef {
|
||||||
|
@ -38,21 +33,9 @@ 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);
|
||||||
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: &Type) {
|
|
@ -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;
|
|
||||||
|
|
|
@ -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"]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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())
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
32
todo.txt
32
todo.txt
|
@ -11,24 +11,18 @@ Errors:
|
||||||
- Type not equal
|
- Type not equal
|
||||||
- Incorrect number of parameters
|
- Incorrect number of parameters
|
||||||
|
|
||||||
Symbol Resolution:
|
GlobalContext:
|
||||||
- Add all files with annotated class/functions.
|
- Separate from typedefs
|
||||||
- Find class references, load them all in TopLevelContext.
|
- Interact with python intepreter to get data
|
||||||
- Find unbounded identifiers in the functions.
|
- Primitive Type Instance List
|
||||||
- If it is a function/class name, record its object ID.
|
- Symbol Table (readable, ever defined)
|
||||||
- Otherwise, load its value. (check to see if specified with `global`)
|
- TypeVar definition stack
|
||||||
(Function implemented in python, with rust binding to add value to global
|
- Provide subst, inv_subst, blablabla
|
||||||
variable dictionary)
|
- Cache type var method lookup (dropped when related assumptions are changed)
|
||||||
|
- Responsible for printing the error (lookup module/type info, handle line number offset)
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
|
Name Resolution:
|
||||||
|
- Get class/methods, track module via `inspect.getmodule`
|
||||||
|
- GlobalContext store function/class - module association, perform name
|
||||||
|
resolution in the module when identifier is unbounded, and check its type
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue