mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Statement Scheduler Implementation
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
Anand Krishnamoorthi
parent
6738eeed3c
commit
7789de41b6
@@ -349,6 +349,7 @@ cases:
|
||||
- |
|
||||
package t
|
||||
import future.keywords
|
||||
x = true
|
||||
default a = [5 | not x]
|
||||
query: data.t.a
|
||||
want_result: []
|
||||
|
||||
@@ -26,10 +26,10 @@ cases:
|
||||
every key, x in [1, 2, 3] {
|
||||
x == key + 1
|
||||
}
|
||||
|
||||
|
||||
y = x + key
|
||||
}
|
||||
|
||||
|
||||
# Set
|
||||
x2 = y {
|
||||
# Only value
|
||||
@@ -48,7 +48,7 @@ cases:
|
||||
every key, x in {1, 2, 3} {
|
||||
x == key
|
||||
}
|
||||
|
||||
|
||||
y = x + key
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ cases:
|
||||
every key, x in {1:2, 3:4} {
|
||||
x == key + 1
|
||||
}
|
||||
|
||||
|
||||
y = x + key
|
||||
}
|
||||
|
||||
@@ -91,9 +91,11 @@ cases:
|
||||
}
|
||||
every _ in `abc` {
|
||||
undefined_var
|
||||
}
|
||||
}
|
||||
y = 100
|
||||
}
|
||||
|
||||
undefined_var { false }
|
||||
query: data.test
|
||||
want_result:
|
||||
x1: 100
|
||||
@@ -114,10 +116,13 @@ cases:
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
p { false }
|
||||
|
||||
x2 = y {
|
||||
y = 100
|
||||
every _ in [1] {
|
||||
# TODO: if p is an undefined var, raise error.
|
||||
p
|
||||
}
|
||||
}
|
||||
@@ -126,5 +131,3 @@ cases:
|
||||
|
||||
#TODO:
|
||||
# every vars must be used
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
cases:
|
||||
- note: basic
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import future.keywords
|
||||
|
||||
r1 = value {
|
||||
value = p + a[0] # p and a are defined later
|
||||
q = p # q depends on p; p depends on q
|
||||
q = t[0] # t is defined at end
|
||||
a = [ t[i] | # a uses a compr which depends on t
|
||||
i := 1
|
||||
]
|
||||
t = [8, 4]
|
||||
}
|
||||
|
||||
query: data.test
|
||||
want_result:
|
||||
r1: 12
|
||||
@@ -198,9 +198,12 @@ pub fn eval_file(
|
||||
modules_ref.push(m);
|
||||
}
|
||||
|
||||
let analyzer = Analyzer::new();
|
||||
let schedule = analyzer.analyze(&modules)?;
|
||||
|
||||
// First eval the modules.
|
||||
let mut interpreter = interpreter::Interpreter::new(modules_ref)?;
|
||||
interpreter.eval(&data, &input, enable_tracing)?;
|
||||
interpreter.eval(&data, &input, enable_tracing, Some(&schedule))?;
|
||||
|
||||
// Now eval the query.
|
||||
let source = Source {
|
||||
@@ -242,9 +245,18 @@ fn one_file() -> Result<()> {
|
||||
lines: contents.split('\n').collect(),
|
||||
};
|
||||
let mut parser = Parser::new(&source)?;
|
||||
let tree = parser.parse()?;
|
||||
let mut interpreter = interpreter::Interpreter::new(vec![&tree])?;
|
||||
let results = interpreter.eval(&None, &input, true)?;
|
||||
let modules = vec![parser.parse()?];
|
||||
|
||||
let analyzer = Analyzer::new();
|
||||
let schedule = analyzer.analyze(&modules)?;
|
||||
|
||||
let mut modules_ref = vec![];
|
||||
for m in &modules {
|
||||
modules_ref.push(m);
|
||||
}
|
||||
|
||||
let mut interpreter = interpreter::Interpreter::new(modules_ref)?;
|
||||
let results = interpreter.eval(&None, &input, true, Some(&schedule))?;
|
||||
println!("eval results:\n{}", serde_json::to_string_pretty(&results)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
cases:
|
||||
- note: basic
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
x = y {
|
||||
p = 1
|
||||
# var can appear on rhs
|
||||
2 = q
|
||||
y = p
|
||||
# No local var is created for r
|
||||
r = 1
|
||||
|
||||
# Nested scope
|
||||
x := [ k |
|
||||
a = k
|
||||
# A loop index var with same name as parent scope.
|
||||
# The outer var is used.
|
||||
[1,2,3][idx]
|
||||
r1 = { q |
|
||||
# := forces a local variable
|
||||
rrr = t
|
||||
q := [1, 2, 3][idx1]
|
||||
}
|
||||
]
|
||||
|
||||
[a, [b]] = [[p], q]
|
||||
|
||||
# an index var
|
||||
[1,2, 3][idx]
|
||||
}
|
||||
|
||||
r = 1
|
||||
rrr = "fun"
|
||||
scopes:
|
||||
- locals: ["p", "y", "q", "x", "a", "b", "idx"]
|
||||
inputs: ["r", "rrr"]
|
||||
- locals: ["k", "r1"]
|
||||
inputs: ["a", "idx", "rrr"]
|
||||
- locals: ["q", "idx1", "t"]
|
||||
inputs: ["rrr"]
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use regorus::scheduler::*;
|
||||
use regorus::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use test_generator::test_resources;
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct Scope {
|
||||
pub locals: BTreeSet<String>,
|
||||
pub inputs: BTreeSet<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct TestCase {
|
||||
modules: Vec<String>,
|
||||
note: String,
|
||||
scopes: Vec<Scope>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct YamlTest {
|
||||
cases: Vec<TestCase>,
|
||||
}
|
||||
|
||||
fn to_string_set(s: &BTreeSet<&str>) -> BTreeSet<String> {
|
||||
s.iter().map(|s| s.to_string()).collect()
|
||||
}
|
||||
|
||||
fn analyze_file(regos: &[String], expected_scopes: &[Scope]) -> Result<()> {
|
||||
let mut files = vec![];
|
||||
let mut sources = vec![];
|
||||
let mut modules = vec![];
|
||||
for (idx, _) in regos.iter().enumerate() {
|
||||
files.push(format!("rego_{idx}"));
|
||||
}
|
||||
|
||||
for (idx, file) in files.iter().enumerate() {
|
||||
let contents = regos[idx].as_str();
|
||||
sources.push(Source {
|
||||
file,
|
||||
contents,
|
||||
lines: contents.split('\n').collect(),
|
||||
});
|
||||
}
|
||||
|
||||
for source in &sources {
|
||||
let mut parser = Parser::new(source)?;
|
||||
modules.push(parser.parse()?);
|
||||
}
|
||||
|
||||
let analyzer = Analyzer::new();
|
||||
let schedule = analyzer.analyze(&modules)?;
|
||||
for (idx, (_, scope)) in schedule.scopes.iter().enumerate() {
|
||||
if idx > expected_scopes.len() {
|
||||
bail!("extra scope generated.")
|
||||
}
|
||||
assert_eq!(to_string_set(&scope.locals), expected_scopes[idx].locals);
|
||||
assert_eq!(to_string_set(&scope.inputs), expected_scopes[idx].inputs);
|
||||
println!("scope {idx} matched.")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn yaml_test_impl(file: &str) -> Result<()> {
|
||||
println!("\nrunning {file}");
|
||||
|
||||
let yaml_str = std::fs::read_to_string(file)?;
|
||||
let test: YamlTest = serde_yaml::from_str(&yaml_str)?;
|
||||
|
||||
for case in &test.cases {
|
||||
print!("\ncase {} ", case.note);
|
||||
analyze_file(&case.modules, &case.scopes)?;
|
||||
println!("passed");
|
||||
}
|
||||
|
||||
println!("{} cases passed.", test.cases.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn yaml_test(file: &str) -> Result<()> {
|
||||
match yaml_test_impl(file) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
// If Err is returned, it doesn't always get printed by cargo test.
|
||||
// Therefore, panic with the error.
|
||||
panic!("{}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test_resources("tests/scheduler/analyzer/**/*.yaml")]
|
||||
fn run(path: &str) {
|
||||
yaml_test(path).unwrap()
|
||||
}
|
||||
@@ -5,6 +5,8 @@ use anyhow::{bail, Result};
|
||||
|
||||
use regorus::scheduler::*;
|
||||
|
||||
mod analyzer;
|
||||
|
||||
fn make_info<'a>(definitions: &[(&'a str, &[&'a str])]) -> StmtInfo<'a> {
|
||||
StmtInfo {
|
||||
definitions: definitions
|
||||
@@ -17,9 +19,9 @@ fn make_info<'a>(definitions: &[(&'a str, &[&'a str])]) -> StmtInfo<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn print_stmts(stmts: &[&str], order: &[usize]) {
|
||||
fn print_stmts(stmts: &[&str], order: &[u16]) {
|
||||
for idx in order.iter().cloned() {
|
||||
println!("{}", stmts[idx]);
|
||||
println!("{}", stmts[idx as usize]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +30,7 @@ fn check_result(stmts: &[&str], expected: &[&str], r: SortResult) -> Result<()>
|
||||
SortResult::Order(order) => {
|
||||
print_stmts(stmts, &order);
|
||||
for (i, o) in order.iter().cloned().enumerate() {
|
||||
assert_eq!(stmts[o], expected[i]);
|
||||
assert_eq!(stmts[o as usize], expected[i]);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -153,6 +155,7 @@ fn case3() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "cycle needs to be detected"]
|
||||
fn case4_cycle() -> Result<()> {
|
||||
#[rustfmt::skip]
|
||||
let stmts = vec![
|
||||
|
||||
Reference in New Issue
Block a user