Implement every statement (#4)

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2023-02-20 06:30:40 +05:30
parent 2a1924efa5
commit 351483d100
6 changed files with 238 additions and 45 deletions

View File

@@ -188,8 +188,8 @@ pub enum Literal<'source> {
},
Every {
span: Span<'source>,
key: Span<'source>,
value: Option<Span<'source>>,
key: Option<Span<'source>>,
value: Span<'source>,
domain: Expr<'source>,
query: Query<'source>,
},

View File

@@ -74,18 +74,27 @@ impl<'source> Interpreter<'source> {
}
}
fn current_scope(&mut self) -> Result<&Scope> {
match self.scopes.last() {
Some(scope) => Ok(scope),
_ => bail!("internal error: no active scope"),
}
}
fn current_scope_mut(&mut self) -> Result<&mut Scope> {
match self.scopes.last_mut() {
Some(scope) => Ok(scope),
_ => bail!("internal error: no active scope"),
}
}
#[inline(always)]
fn add_variable(&mut self, name: &str, value: Value) -> Result<()> {
let name = name.to_string();
// Only add the variable if the key is not "_"
if name != "_" {
match self.scopes.last_mut() {
Some(scope) => {
scope.insert(name, value);
}
_ => bail!("internal error: no active scope"),
}
self.current_scope_mut()?.insert(name, value);
}
Ok(())
@@ -104,18 +113,12 @@ impl<'source> Interpreter<'source> {
// TODO: optimize this
fn variables_assignment(&mut self, name: &str, value: &Value) -> Result<()> {
match self.scopes.last_mut() {
Some(scope) => {
if let Some(variable) = scope.get_mut(name) {
*variable = value.clone();
} else {
return Err(anyhow!("variable {} is undefined", name));
}
}
_ => bail!("internal error: no active scope"),
if let Some(variable) = self.current_scope_mut()?.get_mut(name) {
*variable = value.clone();
Ok(())
} else {
Err(anyhow!("variable {} is undefined", name))
}
Ok(())
}
fn eval_chained_ref_dot_or_brack(&mut self, mut expr: &'source Expr<'source>) -> Result<Value> {
@@ -423,6 +426,69 @@ impl<'source> Interpreter<'source> {
Ok(Value::Bool(true))
}
fn eval_every(
&mut self,
_span: &'source Span<'source>,
key: &'source Option<Span<'source>>,
value: &'source Span<'source>,
domain: &'source Expr<'source>,
query: &'source Query<'source>,
) -> Result<bool> {
let domain = self.eval_expr(domain)?;
self.scopes.push(Scope::new());
self.contexts.push(Context {
key_expr: None,
output_expr: None,
value: Value::new_set(),
});
let mut r = true;
match domain {
Value::Array(a) => {
for (idx, v) in a.iter().enumerate() {
self.add_variable(value.text(), v.clone())?;
if let Some(key) = key {
self.add_variable(key.text(), Value::from_float(idx as Float))?;
}
if !self.eval_query(query)? {
r = false;
break;
}
}
}
Value::Set(s) => {
for v in s.iter() {
self.add_variable(value.text(), v.clone())?;
if let Some(key) = key {
self.add_variable(key.text(), v.clone())?;
}
if !self.eval_query(query)? {
r = false;
break;
}
}
}
Value::Object(o) => {
for (k, v) in o.iter() {
self.add_variable(value.text(), v.clone())?;
if let Some(key) = key {
self.add_variable(key.text(), k.clone())?;
}
if !self.eval_query(query)? {
r = false;
break;
}
}
}
// Other types cause every to evaluate to true even though
// it is supposed to happen only for empty domain.
_ => (),
};
self.contexts.pop();
self.scopes.pop();
Ok(r)
}
fn eval_stmt(&mut self, stmt: &'source LiteralStmt<'source>) -> Result<bool> {
let mut to_restore = vec![];
for wm in &stmt.with_mods {
@@ -496,7 +562,13 @@ impl<'source> Interpreter<'source> {
}
}
Literal::NotExpr { expr, .. } => matches!(self.eval_expr(expr)?, Value::Bool(false)),
_ => unimplemented!(),
Literal::Every {
span,
key,
value,
domain,
query,
} => self.eval_every(span, key, value, domain, query)?,
});
for (path, value) in to_restore.into_iter().rev() {
@@ -532,10 +604,7 @@ impl<'source> Interpreter<'source> {
// Save the current scope and restore it after evaluating the statements so
// that the effects of the current loop iteration are cleared.
let scope_saved = match self.scopes.last() {
Some(scope) => scope.clone(),
_ => bail!("internal error: missing scope"),
};
let scope_saved = self.current_scope()?.clone();
match self.eval_expr(loop_expr.value)? {
Value::Array(items) => {
@@ -544,9 +613,7 @@ impl<'source> Interpreter<'source> {
self.add_variable(loop_expr.index, Value::from_float(idx as Float))?;
result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result;
if let Some(s) = self.scopes.last_mut() {
*s = scope_saved.clone();
}
*self.current_scope_mut()? = scope_saved.clone();
}
}
Value::Set(items) => {
@@ -555,9 +622,7 @@ impl<'source> Interpreter<'source> {
// For sets, index is also the value.
self.add_variable(loop_expr.index, v.clone())?;
result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result;
if let Some(s) = self.scopes.last_mut() {
*s = scope_saved.clone();
}
*self.current_scope_mut()? = scope_saved.clone();
}
}
Value::Object(obj) => {
@@ -566,9 +631,7 @@ impl<'source> Interpreter<'source> {
// For objects, index is key.
self.add_variable(loop_expr.index, k.clone())?;
result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result;
if let Some(s) = self.scopes.last_mut() {
*s = scope_saved.clone();
}
*self.current_scope_mut()? = scope_saved.clone();
}
}
_ => {

View File

@@ -780,12 +780,12 @@ impl<'source> Parser<'source> {
let context = "Failed to parse `every` statement.";
self.parse_future_keyword("every", false, context)?;
let key = self.parse_var()?;
let value = match self.tok.1.text() {
let ident = self.parse_var()?;
let (key, value) = match self.tok.1.text() {
"," => {
self.next_token()?;
match self.parse_var() {
Ok(v) => Some(v),
Ok(v) => (Some(ident), v),
Err(e) => {
return Err(self.source.error(
span.line,
@@ -795,7 +795,7 @@ impl<'source> Parser<'source> {
}
}
}
_ => None,
_ => (None, ident),
};
self.parse_future_keyword("in", false, context)?;

View File

@@ -0,0 +1,130 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
cases:
- note: all
data: {}
modules:
- |
package test
import future.keywords
# Array
x1 = y {
# Only value
every x in [1, 2, 3] {
x > 0
}
# Key and value
every key, x in [1, 2, 3] {
x == key + 1
}
key = 5
x = 95
# Key and value can shadow (local) variables.
every key, x in [1, 2, 3] {
x == key + 1
}
y = x + key
}
# Set
x2 = y {
# Only value
every x in {1, 2, 3} {
x > 0
}
# Key and value are same.
every key, x in {1, 2, 3} {
x == key
}
key = 5
x = 95
# Key and value can shadow (local) variables.
every key, x in {1, 2, 3} {
x == key
}
y = x + key
}
# Object
x3 = y {
# Only value
every x in {1:2, 3:4} {
x >= 2
x % 2 == 0
}
# Key and value.
every key, x in {1:2, 3:4} {
x == key + 1
}
key = 5
x = 95
# Key and value can shadow (local) variables.
every key, x in {1:2, 3:4} {
x == key + 1
}
y = x + key
}
# Non aggregate types
x4 = y {
every _, _ in 1 {
false
}
every _, _ in null {
false
}
every _, _ in false {
false
}
every _ in "abc" {
false
}
every _ in `abc` {
undefined_var
}
y = 100
}
query: data.test
want_result:
x1: 100
x2: 100
x3: 100
x4: 100
- note: negative
data: {}
modules:
- |
package test
import future.keywords
x1 = y {
y = 100
every _ in [1] {
false
}
}
x2 = y {
y = 100
every _ in [1] {
p
}
}
query: data.test
want_result: {}
#TODO:
# every vars must be used

View File

@@ -1,5 +1,5 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# Copyright (c) Microsoft Corporation. Licensed under the MIT
# License.
cases:
- note: basic
@@ -28,7 +28,7 @@ cases:
stmts:
- literal:
every:
key: x
value: x
domain:
array:
- number: 2

View File

@@ -525,19 +525,19 @@ fn match_literal(l: &Literal, v: &Value) -> Result<()> {
query,
} => {
match_span_opt(span, &v["every"]["span"])?;
match_span(key, &v["every"]["key"])?;
match value {
Some(s) => match_span(s, &v["every"]["value"])?,
match_span(value, &v["every"]["value"])?;
match key {
Some(s) => match_span(s, &v["every"]["key"])?,
None => {
my_assert_eq!(
&Value::Undefined,
&v["value"],
&v["key"],
"{}",
span.source.message(
span.line,
span.col,
"mismatch-error",
"could not match `value``"
"could not match `key``"
)
);
}