added statement tests

signature
pca006132 2021-01-05 13:35:44 +08:00
parent 007843c1ef
commit b51168a5ab
3 changed files with 348 additions and 2 deletions

12
Cargo.lock generated
View File

@ -288,6 +288,15 @@ dependencies = [
"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]]
name = "indoc-impl"
version = "0.3.6"
@ -449,6 +458,7 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
name = "nac3core"
version = "0.1.0"
dependencies = [
"indoc 1.0.3",
"inkwell",
"num-bigint",
"num-traits",
@ -613,7 +623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf6bbbe8f70d179260b3728e5d04eb012f4f0c7988e58c11433dd689cecaa72e"
dependencies = [
"ctor",
"indoc",
"indoc 0.3.6",
"inventory",
"libc",
"parking_lot",

View File

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

View File

@ -19,7 +19,9 @@ pub fn check_stmts<'b: 'a, 'a>(
check_aug_assign(ctx, &target, op, &value)?;
}
StatementType::If { test, body, orelse } => {
check_if(ctx, test, body.as_slice(), 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)?;
@ -239,3 +241,333 @@ fn check_for_stmt<'b: 'a, 'a>(
Err("only list can be iterated over".to_string())
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::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())
);
});
}
}