mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat!: Indexes for nodes in the AST (#414)
Indexes allow associating extra data with nodes in the AST using an array and then quickly looking up the array to fetch the extra data. - Index eidx for expressions - Index sidx for statements - Index qidx for queries. AST nodes are not cloneable. Therefore once a module is created, it is not possible to accidentally create two nodes with the same index inadvertently via clone. Also added IndexChecker in debug builds. When a module is parsed, it will assert that indexes have been constructed correctly. AST Cleanup - Make literal expressions (null, val, number, string etc) also structs to match all other expressions - Merge True and False nodes into a single Bool node. Also update dependencies. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
620f8a4547
commit
444b2970a1
+96
-10
@@ -106,29 +106,60 @@ pub type Ref<T> = NodeRef<T>;
|
||||
#[cfg_attr(feature = "ast", derive(serde::Serialize))]
|
||||
pub enum Expr {
|
||||
// Simple items that only have a span as content.
|
||||
String((Span, Value)),
|
||||
RawString((Span, Value)),
|
||||
Number((Span, Value)),
|
||||
True(Span),
|
||||
False(Span),
|
||||
Null(Span),
|
||||
Var((Span, Value)),
|
||||
String {
|
||||
span: Span,
|
||||
value: Value,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
RawString {
|
||||
span: Span,
|
||||
value: Value,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
Number {
|
||||
span: Span,
|
||||
value: Value,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
Bool {
|
||||
span: Span,
|
||||
value: Value,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
Null {
|
||||
span: Span,
|
||||
value: Value,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
Var {
|
||||
span: Span,
|
||||
value: Value,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
// array
|
||||
Array {
|
||||
span: Span,
|
||||
items: Vec<Ref<Expr>>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
// set
|
||||
Set {
|
||||
span: Span,
|
||||
items: Vec<Ref<Expr>>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
Object {
|
||||
span: Span,
|
||||
fields: Vec<(Span, Ref<Expr>, Ref<Expr>)>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
// Comprehensions
|
||||
@@ -136,12 +167,14 @@ pub enum Expr {
|
||||
span: Span,
|
||||
term: Ref<Expr>,
|
||||
query: Ref<Query>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
SetCompr {
|
||||
span: Span,
|
||||
term: Ref<Expr>,
|
||||
query: Ref<Query>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
ObjectCompr {
|
||||
@@ -149,17 +182,20 @@ pub enum Expr {
|
||||
key: Ref<Expr>,
|
||||
value: Ref<Expr>,
|
||||
query: Ref<Query>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
Call {
|
||||
span: Span,
|
||||
fcn: Ref<Expr>,
|
||||
params: Vec<Ref<Expr>>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
UnaryExpr {
|
||||
span: Span,
|
||||
expr: Ref<Expr>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
// ref
|
||||
@@ -167,12 +203,14 @@ pub enum Expr {
|
||||
span: Span,
|
||||
refr: Ref<Expr>,
|
||||
field: (Span, Value),
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
RefBrack {
|
||||
span: Span,
|
||||
refr: Ref<Expr>,
|
||||
index: Ref<Expr>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
// Infix expressions
|
||||
@@ -181,12 +219,15 @@ pub enum Expr {
|
||||
op: BinOp,
|
||||
lhs: Ref<Expr>,
|
||||
rhs: Ref<Expr>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
BoolExpr {
|
||||
span: Span,
|
||||
op: BoolOp,
|
||||
lhs: Ref<Expr>,
|
||||
rhs: Ref<Expr>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
ArithExpr {
|
||||
@@ -194,6 +235,7 @@ pub enum Expr {
|
||||
op: ArithOp,
|
||||
lhs: Ref<Expr>,
|
||||
rhs: Ref<Expr>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
AssignExpr {
|
||||
@@ -201,6 +243,7 @@ pub enum Expr {
|
||||
op: AssignOp,
|
||||
lhs: Ref<Expr>,
|
||||
rhs: Ref<Expr>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
Membership {
|
||||
@@ -208,6 +251,7 @@ pub enum Expr {
|
||||
key: Option<Ref<Expr>>,
|
||||
value: Ref<Expr>,
|
||||
collection: Ref<Expr>,
|
||||
eidx: u32,
|
||||
},
|
||||
|
||||
#[cfg(feature = "rego-extensions")]
|
||||
@@ -215,6 +259,7 @@ pub enum Expr {
|
||||
span: Span,
|
||||
lhs: Ref<Expr>,
|
||||
rhs: Ref<Expr>,
|
||||
eidx: u32,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -222,9 +267,13 @@ impl Expr {
|
||||
pub fn span(&self) -> &Span {
|
||||
use Expr::*;
|
||||
match self {
|
||||
String(s) | RawString(s) | Number(s) | Var(s) => &s.0,
|
||||
True(s) | False(s) | Null(s) => s,
|
||||
Array { span, .. }
|
||||
String { span, .. }
|
||||
| RawString { span, .. }
|
||||
| Number { span, .. }
|
||||
| Bool { span, .. }
|
||||
| Null { span, .. }
|
||||
| Var { span, .. }
|
||||
| Array { span, .. }
|
||||
| Set { span, .. }
|
||||
| Object { span, .. }
|
||||
| ArrayCompr { span, .. }
|
||||
@@ -243,6 +292,35 @@ impl Expr {
|
||||
OrExpr { span, .. } => span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eidx(&self) -> u32 {
|
||||
use Expr::*;
|
||||
match self {
|
||||
String { eidx, .. }
|
||||
| RawString { eidx, .. }
|
||||
| Number { eidx, .. }
|
||||
| Bool { eidx, .. }
|
||||
| Null { eidx, .. }
|
||||
| Var { eidx, .. }
|
||||
| Array { eidx, .. }
|
||||
| Set { eidx, .. }
|
||||
| Object { eidx, .. }
|
||||
| ArrayCompr { eidx, .. }
|
||||
| SetCompr { eidx, .. }
|
||||
| ObjectCompr { eidx, .. }
|
||||
| Call { eidx, .. }
|
||||
| UnaryExpr { eidx, .. }
|
||||
| RefDot { eidx, .. }
|
||||
| RefBrack { eidx, .. }
|
||||
| BinExpr { eidx, .. }
|
||||
| BoolExpr { eidx, .. }
|
||||
| ArithExpr { eidx, .. }
|
||||
| AssignExpr { eidx, .. }
|
||||
| Membership { eidx, .. } => *eidx,
|
||||
#[cfg(feature = "rego-extensions")]
|
||||
OrExpr { eidx, .. } => *eidx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -290,6 +368,7 @@ pub struct LiteralStmt {
|
||||
pub literal: Literal,
|
||||
#[cfg_attr(feature = "ast", serde(skip_serializing_if = "Vec::is_empty"))]
|
||||
pub with_mods: Vec<WithModifier>,
|
||||
pub sidx: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -297,6 +376,7 @@ pub struct LiteralStmt {
|
||||
pub struct Query {
|
||||
pub span: Span,
|
||||
pub stmts: Vec<LiteralStmt>,
|
||||
pub qidx: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -385,6 +465,12 @@ pub struct Module {
|
||||
#[cfg_attr(feature = "ast", serde(rename(serialize = "rules")))]
|
||||
pub policy: Vec<Ref<Rule>>,
|
||||
pub rego_v1: bool,
|
||||
// Number of expressions in the module.
|
||||
pub num_expressions: u32,
|
||||
// Number of statements in the module.
|
||||
pub num_statements: u32,
|
||||
// Number of queries in the module.
|
||||
pub num_queries: u32,
|
||||
}
|
||||
|
||||
pub type ExprRef = Ref<Expr>;
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#![cfg(debug_assertions)]
|
||||
|
||||
use crate::ast::*;
|
||||
use alloc::collections::BTreeSet;
|
||||
use alloc::format;
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
// Ensures that indexes are unique and continuous, starting from 0.
|
||||
#[derive(Default)]
|
||||
pub struct IndexChecker {
|
||||
eidx: BTreeSet<u32>,
|
||||
sidx: BTreeSet<u32>,
|
||||
qidx: BTreeSet<u32>,
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
impl IndexChecker {
|
||||
fn check_query(&mut self, query: &Query) -> Result<()> {
|
||||
let qidx = query.qidx;
|
||||
if !self.qidx.insert(qidx) {
|
||||
bail!(query
|
||||
.span
|
||||
.error(format!("query with qidx {qidx} already exists").as_str()));
|
||||
}
|
||||
|
||||
for stmt in &query.stmts {
|
||||
if !self.sidx.insert(stmt.sidx) {
|
||||
bail!(stmt
|
||||
.span
|
||||
.error(format!("statement with sidx {} already exists", stmt.sidx).as_str()));
|
||||
}
|
||||
match &stmt.literal {
|
||||
Literal::Every { domain, query, .. } => {
|
||||
self.check_eidx(domain)?;
|
||||
self.check_query(query.as_ref())?;
|
||||
}
|
||||
Literal::SomeVars { .. } => (),
|
||||
Literal::Expr { expr, .. } => self.check_eidx(expr.as_ref())?,
|
||||
Literal::SomeIn {
|
||||
key,
|
||||
value,
|
||||
collection,
|
||||
..
|
||||
} => {
|
||||
if let Some(key) = key {
|
||||
self.check_eidx(key.as_ref())?;
|
||||
}
|
||||
self.check_eidx(value.as_ref())?;
|
||||
self.check_eidx(collection.as_ref())?;
|
||||
}
|
||||
Literal::NotExpr { expr, .. } => {
|
||||
self.check_eidx(expr.as_ref())?;
|
||||
}
|
||||
}
|
||||
for with_mod in &stmt.with_mods {
|
||||
self.check_eidx(with_mod.refr.as_ref())?;
|
||||
self.check_eidx(with_mod.r#as.as_ref())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_eidx(&mut self, expr: &Expr) -> Result<()> {
|
||||
use Expr::*;
|
||||
let eidx = expr.eidx();
|
||||
if !self.eidx.insert(eidx) {
|
||||
bail!(expr
|
||||
.span()
|
||||
.error(format!("expression with eidx {eidx} already exists").as_str()));
|
||||
}
|
||||
|
||||
match expr {
|
||||
String { .. }
|
||||
| RawString { .. }
|
||||
| Number { .. }
|
||||
| Bool { .. }
|
||||
| Null { .. }
|
||||
| Var { .. } => (),
|
||||
|
||||
Array { items, .. } => {
|
||||
for elem in items {
|
||||
self.check_eidx(elem.as_ref())?;
|
||||
}
|
||||
}
|
||||
Set { items, .. } => {
|
||||
for elem in items {
|
||||
self.check_eidx(elem.as_ref())?;
|
||||
}
|
||||
}
|
||||
|
||||
Object { fields, .. } => {
|
||||
for pair in fields {
|
||||
self.check_eidx(pair.1.as_ref())?;
|
||||
self.check_eidx(pair.2.as_ref())?;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayCompr { term, query, .. } => {
|
||||
self.check_eidx(term.as_ref())?;
|
||||
self.check_query(query.as_ref())?;
|
||||
}
|
||||
|
||||
SetCompr { term, query, .. } => {
|
||||
self.check_eidx(term.as_ref())?;
|
||||
self.check_query(query.as_ref())?;
|
||||
}
|
||||
|
||||
ObjectCompr {
|
||||
key, value, query, ..
|
||||
} => {
|
||||
self.check_eidx(key.as_ref())?;
|
||||
self.check_eidx(value.as_ref())?;
|
||||
self.check_query(query.as_ref())?;
|
||||
}
|
||||
|
||||
Call { fcn, params, .. } => {
|
||||
self.check_eidx(fcn.as_ref())?;
|
||||
for param in params {
|
||||
self.check_eidx(param.as_ref())?;
|
||||
}
|
||||
}
|
||||
|
||||
UnaryExpr { expr, .. } => {
|
||||
self.check_eidx(expr.as_ref())?;
|
||||
}
|
||||
|
||||
RefBrack { refr, index, .. } => {
|
||||
self.check_eidx(refr.as_ref())?;
|
||||
self.check_eidx(index.as_ref())?;
|
||||
}
|
||||
RefDot { refr, .. } => {
|
||||
self.check_eidx(refr.as_ref())?;
|
||||
}
|
||||
|
||||
BinExpr { lhs, rhs, .. }
|
||||
| BoolExpr { lhs, rhs, .. }
|
||||
| ArithExpr { lhs, rhs, .. }
|
||||
| AssignExpr { lhs, rhs, .. } => {
|
||||
self.check_eidx(lhs.as_ref())?;
|
||||
self.check_eidx(rhs.as_ref())?;
|
||||
}
|
||||
|
||||
Membership {
|
||||
key,
|
||||
value,
|
||||
collection,
|
||||
..
|
||||
} => {
|
||||
if let Some(key) = key {
|
||||
self.check_eidx(key.as_ref())?;
|
||||
}
|
||||
self.check_eidx(value.as_ref())?;
|
||||
self.check_eidx(collection.as_ref())?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "rego-extensions")]
|
||||
OrExpr { lhs, rhs, .. } => {
|
||||
self.check_eidx(lhs.as_ref())?;
|
||||
self.check_eidx(rhs.as_ref())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_rule_assign(&mut self, assign: &RuleAssign) -> Result<()> {
|
||||
self.check_eidx(&assign.value)
|
||||
}
|
||||
|
||||
fn check_rule_body(&mut self, body: &RuleBody) -> Result<()> {
|
||||
if let Some(assign) = &body.assign {
|
||||
self.check_rule_assign(assign)?;
|
||||
}
|
||||
self.check_query(&body.query)
|
||||
}
|
||||
|
||||
fn check_rule_heade(&mut self, head: &RuleHead) -> Result<()> {
|
||||
match head {
|
||||
RuleHead::Compr { refr, assign, .. } => {
|
||||
self.check_eidx(refr.as_ref())?;
|
||||
if let Some(assign) = assign {
|
||||
self.check_rule_assign(assign)?;
|
||||
}
|
||||
}
|
||||
RuleHead::Func {
|
||||
refr, args, assign, ..
|
||||
} => {
|
||||
self.check_eidx(refr.as_ref())?;
|
||||
if let Some(assign) = assign {
|
||||
self.check_rule_assign(assign)?;
|
||||
}
|
||||
for arg in args {
|
||||
self.check_eidx(arg.as_ref())?;
|
||||
}
|
||||
}
|
||||
|
||||
RuleHead::Set { refr, key, .. } => {
|
||||
self.check_eidx(refr.as_ref())?;
|
||||
if let Some(key) = key {
|
||||
self.check_eidx(key.as_ref())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_gathered_indexes(
|
||||
&self,
|
||||
num_idx: u32,
|
||||
idx_set: &BTreeSet<u32>,
|
||||
idx_type: &str,
|
||||
) -> Result<()> {
|
||||
if num_idx == 0 {
|
||||
if !idx_set.is_empty() {
|
||||
bail!("no {idx_type} indexes should be collected when num_{idx_type}s is 0");
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if idx_set
|
||||
.first()
|
||||
.unwrap_or_else(|| panic!("no {idx_type} indexes collected"))
|
||||
!= &0
|
||||
{
|
||||
bail!("start {idx_type} index must be 0");
|
||||
}
|
||||
|
||||
let last_idx = idx_set
|
||||
.last()
|
||||
.unwrap_or_else(|| panic!("no {idx_type} indexes collected"));
|
||||
if last_idx != &(num_idx - 1) {
|
||||
bail!(
|
||||
"last {idx_type} index must be {} got {last_idx} instead",
|
||||
num_idx - 1
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn check_module(&mut self, module: &Module) -> Result<()> {
|
||||
self.check_eidx(module.package.refr.as_ref())?;
|
||||
for import in &module.imports {
|
||||
self.check_eidx(import.refr.as_ref())?;
|
||||
}
|
||||
|
||||
for rule in &module.policy {
|
||||
match rule.as_ref() {
|
||||
Rule::Spec { head, bodies, .. } => {
|
||||
self.check_rule_heade(head)?;
|
||||
for body in bodies {
|
||||
self.check_rule_body(body)?;
|
||||
}
|
||||
}
|
||||
Rule::Default {
|
||||
refr, args, value, ..
|
||||
} => {
|
||||
self.check_eidx(refr.as_ref())?;
|
||||
for arg in args {
|
||||
self.check_eidx(arg.as_ref())?;
|
||||
}
|
||||
self.check_eidx(value.as_ref())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if module.num_expressions == 0 {
|
||||
bail!("module must have at least one expression");
|
||||
}
|
||||
|
||||
self.check_gathered_indexes(module.num_expressions, &self.eidx, "expression")?;
|
||||
self.check_gathered_indexes(module.num_statements, &self.sidx, "statement")?;
|
||||
self.check_gathered_indexes(module.num_queries, &self.qidx, "query")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+92
-70
@@ -325,9 +325,9 @@ impl Interpreter {
|
||||
}
|
||||
match expr.as_ref() {
|
||||
// Stop path collection upon encountering the leading variable.
|
||||
Expr::Var(v) => {
|
||||
Expr::Var { span, .. } => {
|
||||
path.reverse();
|
||||
return self.lookup_var(&v.0, &path[..], false);
|
||||
return self.lookup_var(span, &path[..], false);
|
||||
}
|
||||
// Accumulate chained . field accesses.
|
||||
Expr::RefDot { refr, field, .. } => {
|
||||
@@ -336,9 +336,9 @@ impl Interpreter {
|
||||
}
|
||||
Expr::RefBrack { refr, index, .. } => match index.as_ref() {
|
||||
// refr["field"] is the same as refr.field
|
||||
Expr::String(s) => {
|
||||
Expr::String { span, .. } => {
|
||||
expr = refr;
|
||||
path.push(s.0.text());
|
||||
path.push(span.text());
|
||||
}
|
||||
// Handle other forms of refr.
|
||||
// Note, we have the choice to evaluate a non-string index
|
||||
@@ -410,7 +410,9 @@ impl Interpreter {
|
||||
fn hoist_loops_impl(&self, expr: &ExprRef, loops: &mut Vec<LoopExpr>) {
|
||||
use Expr::*;
|
||||
match expr.as_ref() {
|
||||
RefBrack { refr, index, span } => {
|
||||
RefBrack {
|
||||
refr, index, span, ..
|
||||
} => {
|
||||
// First hoist any loops in refr
|
||||
self.hoist_loops_impl(refr, loops);
|
||||
|
||||
@@ -420,8 +422,8 @@ impl Interpreter {
|
||||
// Then hoist the current bracket operation.
|
||||
let mut indices = Vec::with_capacity(1);
|
||||
let _ = traverse(index, &mut |e| match e.as_ref() {
|
||||
Var(ident) if self.is_loop_index_var(&ident.0.source_str()) => {
|
||||
indices.push(ident.0.source_str());
|
||||
Var { span: ident, .. } if self.is_loop_index_var(&ident.source_str()) => {
|
||||
indices.push(ident.source_str());
|
||||
Ok(false)
|
||||
}
|
||||
Array { .. } | Object { .. } => Ok(true),
|
||||
@@ -438,7 +440,12 @@ impl Interpreter {
|
||||
}
|
||||
|
||||
// Primitives
|
||||
String(_) | RawString(_) | Number(_) | True(_) | False(_) | Null(_) | Var(_) => (),
|
||||
String { .. }
|
||||
| RawString { .. }
|
||||
| Number { .. }
|
||||
| Bool { .. }
|
||||
| Null { .. }
|
||||
| Var { .. } => (),
|
||||
|
||||
// Recurse into expressions in other variants.
|
||||
Array { items, .. } | Set { items, .. } | Call { params: items, .. } => {
|
||||
@@ -595,17 +602,17 @@ impl Interpreter {
|
||||
let (name, value) = match op {
|
||||
AssignOp::Eq => {
|
||||
match (lhs.as_ref(), rhs.as_ref()) {
|
||||
(_, Expr::Var(var))
|
||||
if var.0.source_str().text() != "input"
|
||||
&& self.lookup_var(&var.0, &[], true)? == Value::Undefined =>
|
||||
(_, Expr::Var { span: var, .. })
|
||||
if var.source_str().text() != "input"
|
||||
&& self.lookup_var(var, &[], true)? == Value::Undefined =>
|
||||
{
|
||||
(var.0.source_str(), self.eval_expr(lhs)?)
|
||||
(var.source_str(), self.eval_expr(lhs)?)
|
||||
}
|
||||
(Expr::Var(var), _)
|
||||
if var.0.source_str().text() != "input"
|
||||
&& self.lookup_var(&var.0, &[], true)? == Value::Undefined =>
|
||||
(Expr::Var { span: var, .. }, _)
|
||||
if var.source_str().text() != "input"
|
||||
&& self.lookup_var(var, &[], true)? == Value::Undefined =>
|
||||
{
|
||||
(var.0.source_str(), self.eval_expr(rhs)?)
|
||||
(var.source_str(), self.eval_expr(rhs)?)
|
||||
}
|
||||
(
|
||||
Expr::Array {
|
||||
@@ -614,6 +621,7 @@ impl Interpreter {
|
||||
Expr::Array {
|
||||
items: rhs_items,
|
||||
span: rhs_span,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
if lhs_items.len() != rhs_items.len() {
|
||||
@@ -635,6 +643,7 @@ impl Interpreter {
|
||||
Expr::Object {
|
||||
fields: rhs_fields,
|
||||
span: rhs_span,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
if lhs_fields.len() != rhs_fields.len() {
|
||||
@@ -706,8 +715,8 @@ impl Interpreter {
|
||||
return Ok(rhs_value);
|
||||
}
|
||||
|
||||
let name = if let Expr::Var(s) = lhs.as_ref() {
|
||||
s.0.source_str()
|
||||
let name = if let Expr::Var { span: s, .. } = lhs.as_ref() {
|
||||
s.source_str()
|
||||
} else {
|
||||
let mut cache = BTreeMap::new();
|
||||
let mut type_match = BTreeSet::new();
|
||||
@@ -838,16 +847,16 @@ impl Interpreter {
|
||||
let raise_error = is_last && type_match.get(expr).is_none();
|
||||
|
||||
match (expr.as_ref(), value) {
|
||||
(Expr::Var(ident), _) if ident.0.text() == "_" => Ok(true),
|
||||
(Expr::Var(ident), _)
|
||||
(Expr::Var { span: ident, .. }, _) if ident.text() == "_" => Ok(true),
|
||||
(Expr::Var { span: ident, .. }, _)
|
||||
if check_existing_value
|
||||
&& self.lookup_local_var(&ident.0.source_str()) == Some(value.clone()) =>
|
||||
&& self.lookup_local_var(&ident.source_str()) == Some(value.clone()) =>
|
||||
{
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
(Expr::Var(ident), _) => {
|
||||
self.add_variable(&ident.0.source_str(), value.clone())?;
|
||||
(Expr::Var { span: ident, .. }, _) => {
|
||||
self.add_variable(&ident.source_str(), value.clone())?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
@@ -1076,7 +1085,9 @@ impl Interpreter {
|
||||
Ok(match &stmt.literal {
|
||||
Literal::Expr { span, expr, .. } => {
|
||||
let value = match expr.as_ref() {
|
||||
Expr::Call { span, fcn, params } => self.eval_call(
|
||||
Expr::Call {
|
||||
span, fcn, params, ..
|
||||
} => self.eval_call(
|
||||
span,
|
||||
expr,
|
||||
fcn,
|
||||
@@ -1116,7 +1127,9 @@ impl Interpreter {
|
||||
Literal::NotExpr { span, expr, .. } => {
|
||||
let value = match expr.as_ref() {
|
||||
// Extra parameter is allowed; but a return argument is not allowed.
|
||||
Expr::Call { span, fcn, params } => self.eval_call(
|
||||
Expr::Call {
|
||||
span, fcn, params, ..
|
||||
} => self.eval_call(
|
||||
span,
|
||||
expr,
|
||||
fcn,
|
||||
@@ -1368,7 +1381,9 @@ impl Interpreter {
|
||||
let (saved_state, _) = self.apply_with_modifiers(stmts[0])?;
|
||||
|
||||
let loop_expr_value = loop_expr.value();
|
||||
let loop_expr_value = if let Expr::Call { span, fcn, params } = loop_expr_value.as_ref()
|
||||
let loop_expr_value = if let Expr::Call {
|
||||
span, fcn, params, ..
|
||||
} = loop_expr_value.as_ref()
|
||||
{
|
||||
// Handle walk(obj, output_param)
|
||||
let extra_arg = get_extra_arg(
|
||||
@@ -1395,8 +1410,11 @@ impl Interpreter {
|
||||
// (this can happen if the same index is used for two different collections),
|
||||
// then evaluate statements only if the index applies to this collection.
|
||||
let loop_expr_index = loop_expr.index();
|
||||
if let Some(Expr::Var(index_var)) = loop_expr_index.as_ref().map(|r| r.as_ref()) {
|
||||
if let Some(idx) = self.lookup_local_var(&index_var.0.source_str()) {
|
||||
if let Some(Expr::Var {
|
||||
span: index_var, ..
|
||||
}) = loop_expr_index.as_ref().map(|r| r.as_ref())
|
||||
{
|
||||
if let Some(idx) = self.lookup_local_var(&index_var.source_str()) {
|
||||
if loop_expr_value[&idx] != Value::Undefined {
|
||||
result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result;
|
||||
return Ok(result);
|
||||
@@ -1517,8 +1535,8 @@ impl Interpreter {
|
||||
let mut expr = refr;
|
||||
loop {
|
||||
match expr.as_ref() {
|
||||
Expr::Var(v) => {
|
||||
comps.push(Value::String(v.0.text().into()));
|
||||
Expr::Var { span: v, .. } => {
|
||||
comps.push(Value::String(v.text().into()));
|
||||
break;
|
||||
}
|
||||
Expr::RefBrack { refr, index, .. } => {
|
||||
@@ -1613,7 +1631,7 @@ impl Interpreter {
|
||||
fn is_constant_ref(&self, mut expr: &Ref<Expr>) -> Result<bool> {
|
||||
loop {
|
||||
match expr.as_ref() {
|
||||
Expr::Var(_) => break,
|
||||
Expr::Var { .. } => break,
|
||||
Expr::RefDot { refr, .. } => expr = refr,
|
||||
Expr::RefBrack { refr, index, .. } if self.is_simple_literal(index)? => expr = refr,
|
||||
_ => return Ok(false),
|
||||
@@ -1625,12 +1643,11 @@ impl Interpreter {
|
||||
fn is_simple_literal(&self, expr: &Ref<Expr>) -> Result<bool> {
|
||||
Ok(matches!(
|
||||
expr.as_ref(),
|
||||
Expr::String(_)
|
||||
| Expr::RawString(_)
|
||||
| Expr::True(_)
|
||||
| Expr::False(_)
|
||||
| Expr::Null(_)
|
||||
| Expr::Number(_)
|
||||
Expr::String { .. }
|
||||
| Expr::RawString { .. }
|
||||
| Expr::Bool { .. }
|
||||
| Expr::Null { .. }
|
||||
| Expr::Number { .. }
|
||||
))
|
||||
}
|
||||
|
||||
@@ -2529,13 +2546,13 @@ impl Interpreter {
|
||||
// TODO: global var check; interop with `some var`
|
||||
if let Some(ea) = extra_arg {
|
||||
match ea.as_ref() {
|
||||
Expr::Var(var)
|
||||
if allow_return_arg && self.lookup_local_var(&var.0.source_str()).is_none() =>
|
||||
Expr::Var { span: var, .. }
|
||||
if allow_return_arg && self.lookup_local_var(&var.source_str()).is_none() =>
|
||||
{
|
||||
let value =
|
||||
self.eval_call_impl(span, expr, fcn, ¶ms[..params.len() - 1])?;
|
||||
if var.0.text() != "_" {
|
||||
self.add_variable(&var.0.source_str(), value)?;
|
||||
if var.text() != "_" {
|
||||
self.add_variable(&var.source_str(), value)?;
|
||||
}
|
||||
Ok(Value::Bool(true))
|
||||
}
|
||||
@@ -2799,15 +2816,14 @@ impl Interpreter {
|
||||
}
|
||||
|
||||
match expr.as_ref() {
|
||||
Expr::Null(_) => Ok(Value::Null),
|
||||
Expr::True(_) => Ok(Value::Bool(true)),
|
||||
Expr::False(_) => Ok(Value::Bool(false)),
|
||||
Expr::Number((_, v)) => Ok(v.clone()),
|
||||
Expr::Null { value: v, .. }
|
||||
| Expr::Bool { value: v, .. }
|
||||
| Expr::Number { value: v, .. } => Ok(v.clone()),
|
||||
// TODO: Handle string vs rawstring
|
||||
Expr::String((_, v)) => Ok(v.clone()),
|
||||
Expr::RawString((_, v)) => Ok(v.clone()),
|
||||
Expr::String { value: v, .. } => Ok(v.clone()),
|
||||
Expr::RawString { value: v, .. } => Ok(v.clone()),
|
||||
// TODO: Handle undefined variables
|
||||
Expr::Var(_) => self.eval_chained_ref_dot_or_brack(expr),
|
||||
Expr::Var { .. } => self.eval_chained_ref_dot_or_brack(expr),
|
||||
Expr::RefDot { .. } => self.eval_chained_ref_dot_or_brack(expr),
|
||||
Expr::RefBrack { .. } => self.eval_chained_ref_dot_or_brack(expr),
|
||||
|
||||
@@ -2843,8 +2859,10 @@ impl Interpreter {
|
||||
key, value, query, ..
|
||||
} => self.eval_object_compr(key, value, query),
|
||||
Expr::SetCompr { term, query, .. } => self.eval_set_compr(term, query),
|
||||
Expr::UnaryExpr { span, expr: uexpr } => match uexpr.as_ref() {
|
||||
Expr::Number(_) if !uexpr.span().text().starts_with('-') => {
|
||||
Expr::UnaryExpr {
|
||||
span, expr: uexpr, ..
|
||||
} => match uexpr.as_ref() {
|
||||
Expr::Number { .. } if !uexpr.span().text().starts_with('-') => {
|
||||
builtins::numbers::arithmetic_operation(
|
||||
span,
|
||||
&ArithOp::Sub,
|
||||
@@ -2859,9 +2877,9 @@ impl Interpreter {
|
||||
.span()
|
||||
.error("unary - can only be used with numeric literals")),
|
||||
},
|
||||
Expr::Call { span, fcn, params } => {
|
||||
self.eval_call(span, expr, fcn, params, None, false)
|
||||
}
|
||||
Expr::Call {
|
||||
span, fcn, params, ..
|
||||
} => self.eval_call(span, expr, fcn, params, None, false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3041,14 +3059,16 @@ impl Interpreter {
|
||||
comps.push(field.0.text());
|
||||
expr = Some(refr);
|
||||
}
|
||||
Expr::RefBrack { refr, index, .. } if matches!(index.as_ref(), Expr::String(_)) => {
|
||||
if let Expr::String(s) = index.as_ref() {
|
||||
comps.push(s.0.text());
|
||||
Expr::RefBrack { refr, index, .. }
|
||||
if matches!(index.as_ref(), Expr::String { .. }) =>
|
||||
{
|
||||
if let Expr::String { span: s, .. } = index.as_ref() {
|
||||
comps.push(s.text());
|
||||
expr = Some(refr);
|
||||
}
|
||||
}
|
||||
Expr::Var(v) => {
|
||||
comps.push(v.0.text());
|
||||
Expr::Var { span: v, .. } => {
|
||||
comps.push(v.text());
|
||||
expr = None;
|
||||
}
|
||||
_ => bail!(e.span().error("invalid ref expression")),
|
||||
@@ -3088,10 +3108,12 @@ impl Interpreter {
|
||||
use Expr::*;
|
||||
let (kind, span) = match expr.as_ref() {
|
||||
// Scalars are supported
|
||||
String(_) | RawString(_) | Number(_) | True(_) | False(_) | Null(_) => return Ok(()),
|
||||
String { .. } | RawString { .. } | Number { .. } | Bool { .. } | Null { .. } => {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// Uminus of number is treated as a single expression,
|
||||
UnaryExpr { expr, .. } if matches!(expr.as_ref(), Number(_)) => return Ok(()),
|
||||
UnaryExpr { expr, .. } if matches!(expr.as_ref(), Number { .. }) => return Ok(()),
|
||||
|
||||
// Comprehensions are supported since they won't evaluate to undefined.
|
||||
ArrayCompr { .. } | SetCompr { .. } | ObjectCompr { .. } => return Ok(()),
|
||||
@@ -3114,7 +3136,7 @@ impl Interpreter {
|
||||
}
|
||||
|
||||
// The following may evaluate to undefined.
|
||||
Var((span, _)) => ("var", span),
|
||||
Var { span, .. } => ("var", span),
|
||||
Call { span, .. } => ("call", span),
|
||||
UnaryExpr { span, .. } => ("unaryexpr", span),
|
||||
RefDot { span, .. } => ("ref", span),
|
||||
@@ -3169,7 +3191,7 @@ impl Interpreter {
|
||||
let (refr, index) = match refr.as_ref() {
|
||||
Expr::RefBrack { refr, index, .. } => (refr, Some(index.clone())),
|
||||
Expr::RefDot { .. } => (refr, None),
|
||||
Expr::Var(_) => (refr, None),
|
||||
Expr::Var { .. } => (refr, None),
|
||||
_ => bail!(refr.span().error(&format!(
|
||||
"invalid token {:?} with the default keyword",
|
||||
refr
|
||||
@@ -3489,13 +3511,13 @@ impl Interpreter {
|
||||
let mut components: Vec<Rc<str>> = vec![];
|
||||
loop {
|
||||
refr = match refr.as_ref() {
|
||||
Expr::Var(v) => {
|
||||
components.push(v.0.text().into());
|
||||
Expr::Var { span: v, .. } => {
|
||||
components.push(v.text().into());
|
||||
break;
|
||||
}
|
||||
Expr::RefBrack { refr, index, .. } => {
|
||||
if let Expr::String(s) = index.as_ref() {
|
||||
components.push(s.0.text().into());
|
||||
if let Expr::String { span: s, .. } = index.as_ref() {
|
||||
components.push(s.text().into());
|
||||
} else {
|
||||
components.clear();
|
||||
}
|
||||
@@ -3625,10 +3647,10 @@ impl Interpreter {
|
||||
_ => match import.refr.as_ref() {
|
||||
Expr::RefDot { field, .. } => field.0.text(),
|
||||
Expr::RefBrack { index, .. } => match index.as_ref() {
|
||||
Expr::String(s) => s.0.text(),
|
||||
Expr::String { span: s, .. } => s.text(),
|
||||
_ => "",
|
||||
},
|
||||
Expr::Var(v) if v.0.text() == "input" => {
|
||||
Expr::Var { span: v, .. } if v.text() == "input" => {
|
||||
// Warn redundant import of input. Ignore it.
|
||||
#[cfg(feature = "std")]
|
||||
std::eprintln!(
|
||||
@@ -3667,7 +3689,7 @@ impl Interpreter {
|
||||
// TODO: refactor.
|
||||
let refr = match refr.as_ref() {
|
||||
Expr::RefBrack { index, .. }
|
||||
if matches!(index.as_ref(), Expr::String(_)) =>
|
||||
if matches!(index.as_ref(), Expr::String { .. }) =>
|
||||
{
|
||||
refr
|
||||
}
|
||||
@@ -3681,7 +3703,7 @@ impl Interpreter {
|
||||
Expr::RefBrack { refr, index, .. } => {
|
||||
if !matches!(
|
||||
index.as_ref(),
|
||||
Expr::True(_) | Expr::False(_) | Expr::Number(_) | Expr::String(_)
|
||||
Expr::Bool { .. } | Expr::Number { .. } | Expr::String { .. }
|
||||
) {
|
||||
// OPA's behavior is ignoring the non-scalar index
|
||||
bail!(index.span().error("index is not a scalar value"));
|
||||
|
||||
@@ -22,6 +22,7 @@ extern crate std;
|
||||
mod ast;
|
||||
mod builtins;
|
||||
mod engine;
|
||||
mod indexchecker;
|
||||
mod interpreter;
|
||||
mod lexer;
|
||||
mod number;
|
||||
|
||||
+166
-36
@@ -21,6 +21,13 @@ pub struct Parser<'source> {
|
||||
end: u32,
|
||||
future_keywords: BTreeMap<String, Option<Span>>,
|
||||
rego_v1: bool,
|
||||
|
||||
// The index of the last expression that was parsed.
|
||||
eidx: u32,
|
||||
// The index of the last statement that was parsed.
|
||||
sidx: u32,
|
||||
// The index of the last query that was parsed.
|
||||
qidx: u32,
|
||||
}
|
||||
|
||||
const FUTURE_KEYWORDS: [&str; 4] = ["contains", "every", "if", "in"];
|
||||
@@ -37,9 +44,30 @@ impl<'source> Parser<'source> {
|
||||
end: 0,
|
||||
future_keywords: BTreeMap::new(),
|
||||
rego_v1: false,
|
||||
eidx: 0,
|
||||
sidx: 0,
|
||||
qidx: 0,
|
||||
})
|
||||
}
|
||||
|
||||
fn next_eidx(&mut self) -> u32 {
|
||||
let eidx = self.eidx;
|
||||
self.eidx += 1;
|
||||
eidx
|
||||
}
|
||||
|
||||
fn next_sidx(&mut self) -> u32 {
|
||||
let sidx = self.sidx;
|
||||
self.sidx += 1;
|
||||
sidx
|
||||
}
|
||||
|
||||
fn next_qidx(&mut self) -> u32 {
|
||||
let qidx = self.qidx;
|
||||
self.qidx += 1;
|
||||
qidx
|
||||
}
|
||||
|
||||
pub fn enable_rego_v1(&mut self) -> Result<()> {
|
||||
self.turn_on_rego_v1(&None)
|
||||
}
|
||||
@@ -129,13 +157,13 @@ impl<'source> Parser<'source> {
|
||||
Self::get_path_ref_components_into(refr, comps)?;
|
||||
Self::get_path_ref_components_into(index, comps)?;
|
||||
}
|
||||
Expr::Var(v) => comps.push(v.0.clone()),
|
||||
Expr::String(s) => comps.push(s.0.clone()),
|
||||
Expr::True(s) | Expr::False(s) | Expr::Null(s) => comps.push(s.clone()),
|
||||
Expr::Number(s) => {
|
||||
Expr::Var { span: v, .. } => comps.push(v.clone()),
|
||||
Expr::String { span: s, .. } => comps.push(s.clone()),
|
||||
Expr::Bool { span: s, .. } | Expr::Null { span: s, .. } => comps.push(s.clone()),
|
||||
Expr::Number { span, value, .. } => {
|
||||
// Ensure that the span will be the serialized representation.
|
||||
if *s.0.text() == s.1.to_json_str()? {
|
||||
comps.push(s.0.clone());
|
||||
if span.text() == value.to_json_str()? {
|
||||
comps.push(span.clone());
|
||||
} else {
|
||||
bail!(refr.span().error("not a valid ref"));
|
||||
}
|
||||
@@ -262,9 +290,13 @@ impl<'source> Parser<'source> {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_number(span: Span) -> Result<Expr> {
|
||||
fn read_number(&mut self, span: Span) -> Result<Expr> {
|
||||
match Number::from_str(span.text()) {
|
||||
Ok(v) => Ok(Expr::Number((span, Value::Number(v)))),
|
||||
Ok(v) => Ok(Expr::Number {
|
||||
span,
|
||||
value: Value::Number(v),
|
||||
eidx: self.next_eidx(),
|
||||
}),
|
||||
Err(_) => bail!(span.error("could not parse number")),
|
||||
}
|
||||
}
|
||||
@@ -272,27 +304,51 @@ impl<'source> Parser<'source> {
|
||||
fn parse_scalar_or_var(&mut self) -> Result<Expr> {
|
||||
let span = self.tok.1.clone();
|
||||
let node = match &self.tok.0 {
|
||||
TokenKind::Number => Self::read_number(span)?,
|
||||
TokenKind::Number => self.read_number(span)?,
|
||||
TokenKind::String => {
|
||||
let v = match serde_json::from_str::<Value>(format!("\"{}\"", span.text()).as_str())
|
||||
{
|
||||
Ok(v) => v,
|
||||
Err(e) => bail!(span.error(format!("invalid string literal. {e}").as_str())),
|
||||
};
|
||||
Expr::String((span, v))
|
||||
Expr::String {
|
||||
span,
|
||||
value: v,
|
||||
eidx: self.next_eidx(),
|
||||
}
|
||||
}
|
||||
TokenKind::RawString => {
|
||||
let v = Value::from(span.text().to_string());
|
||||
Expr::RawString((span, v))
|
||||
Expr::RawString {
|
||||
span,
|
||||
value: v,
|
||||
eidx: self.next_eidx(),
|
||||
}
|
||||
}
|
||||
TokenKind::Ident => match self.token_text() {
|
||||
"null" => Expr::Null(span),
|
||||
"true" => Expr::True(span),
|
||||
"false" => Expr::False(span),
|
||||
"null" => Expr::Null {
|
||||
span,
|
||||
value: Value::Null,
|
||||
eidx: self.next_eidx(),
|
||||
},
|
||||
"true" => Expr::Bool {
|
||||
span,
|
||||
value: Value::from(true),
|
||||
eidx: self.next_eidx(),
|
||||
},
|
||||
"false" => Expr::Bool {
|
||||
span,
|
||||
value: Value::from(false),
|
||||
eidx: self.next_eidx(),
|
||||
},
|
||||
_ => {
|
||||
let ident = self.parse_var()?;
|
||||
let v = Value::from(ident.text());
|
||||
return Ok(Expr::Var((ident, v)));
|
||||
let value = Value::from(ident.text());
|
||||
return Ok(Expr::Var {
|
||||
span: ident,
|
||||
value,
|
||||
eidx: self.next_eidx(),
|
||||
});
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
@@ -353,6 +409,7 @@ impl<'source> Parser<'source> {
|
||||
span,
|
||||
term: Ref::new(term),
|
||||
query: Ref::new(query),
|
||||
eidx: self.next_eidx(),
|
||||
})
|
||||
}
|
||||
Err(_) if self.end == pos => {
|
||||
@@ -372,7 +429,11 @@ impl<'source> Parser<'source> {
|
||||
}
|
||||
self.expect("]", "while parsing array")?;
|
||||
span.end = self.end;
|
||||
Ok(Expr::Array { span, items })
|
||||
Ok(Expr::Array {
|
||||
span,
|
||||
items,
|
||||
eidx: self.next_eidx(),
|
||||
})
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
@@ -390,6 +451,7 @@ impl<'source> Parser<'source> {
|
||||
span,
|
||||
term: Ref::new(term),
|
||||
query: Ref::new(query),
|
||||
eidx: self.next_eidx(),
|
||||
});
|
||||
}
|
||||
Err(err) if self.end != pos => {
|
||||
@@ -408,6 +470,7 @@ impl<'source> Parser<'source> {
|
||||
return Ok(Expr::Object {
|
||||
span,
|
||||
fields: vec![],
|
||||
eidx: self.next_eidx(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -427,7 +490,11 @@ impl<'source> Parser<'source> {
|
||||
}
|
||||
self.expect("}", "while parsing set")?;
|
||||
span.end = self.end;
|
||||
return Ok(Expr::Set { span, items });
|
||||
return Ok(Expr::Set {
|
||||
span,
|
||||
items,
|
||||
eidx: self.next_eidx(),
|
||||
});
|
||||
}
|
||||
|
||||
// Parse as object.
|
||||
@@ -442,6 +509,7 @@ impl<'source> Parser<'source> {
|
||||
key: Ref::new(first),
|
||||
value: Ref::new(term),
|
||||
query: Ref::new(query),
|
||||
eidx: self.next_eidx(),
|
||||
});
|
||||
}
|
||||
Err(err) if self.end != pos => {
|
||||
@@ -483,6 +551,7 @@ impl<'source> Parser<'source> {
|
||||
Ok(Expr::Object {
|
||||
span,
|
||||
fields: items,
|
||||
eidx: self.next_eidx(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -494,6 +563,7 @@ impl<'source> Parser<'source> {
|
||||
Ok(Expr::Set {
|
||||
span,
|
||||
items: vec![],
|
||||
eidx: self.next_eidx(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -513,6 +583,7 @@ impl<'source> Parser<'source> {
|
||||
Ok(Expr::UnaryExpr {
|
||||
span,
|
||||
expr: Ref::new(expr),
|
||||
eidx: self.next_eidx(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -531,18 +602,18 @@ impl<'source> Parser<'source> {
|
||||
let mut expr = &term;
|
||||
while possible_fcn {
|
||||
match expr {
|
||||
Expr::Var(_) => break,
|
||||
Expr::Var { .. } => break,
|
||||
Expr::RefDot { refr, .. } => expr = refr,
|
||||
Expr::RefBrack { refr, index, .. } => {
|
||||
expr = refr;
|
||||
possible_fcn = matches!(index.as_ref(), Expr::String(_));
|
||||
possible_fcn = matches!(index.as_ref(), Expr::String { .. });
|
||||
}
|
||||
_ => {
|
||||
possible_fcn = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
matches!(&term, Expr::Var(_));
|
||||
matches!(&term, Expr::Var { .. });
|
||||
|
||||
loop {
|
||||
let mut span = self.tok.1.clone();
|
||||
@@ -586,6 +657,7 @@ impl<'source> Parser<'source> {
|
||||
span,
|
||||
refr: Ref::new(term),
|
||||
field: (field, fieldv),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
}
|
||||
"[" => {
|
||||
@@ -593,7 +665,7 @@ impl<'source> Parser<'source> {
|
||||
let index = self.parse_in_expr()?;
|
||||
|
||||
// If the index is a string, the ref could be path to a function.
|
||||
possible_fcn = possible_fcn && matches!(&index, Expr::String(_));
|
||||
possible_fcn = possible_fcn && matches!(&index, Expr::String { .. });
|
||||
|
||||
self.expect("]", "while parsing bracketed reference")?;
|
||||
span.end = self.end;
|
||||
@@ -602,6 +674,7 @@ impl<'source> Parser<'source> {
|
||||
span,
|
||||
refr: Ref::new(term),
|
||||
index: Ref::new(index),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
}
|
||||
"(" if possible_fcn => {
|
||||
@@ -624,6 +697,7 @@ impl<'source> Parser<'source> {
|
||||
span,
|
||||
fcn: Ref::new(term),
|
||||
params: args,
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
|
||||
// The expression can no longer be a function after the call.
|
||||
@@ -661,6 +735,7 @@ impl<'source> Parser<'source> {
|
||||
op,
|
||||
lhs: Ref::new(expr),
|
||||
rhs: Ref::new(right),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -685,7 +760,7 @@ impl<'source> Parser<'source> {
|
||||
rhs_span.col += 1;
|
||||
|
||||
self.next_token()?;
|
||||
Self::read_number(rhs_span)?
|
||||
self.read_number(rhs_span)?
|
||||
} else {
|
||||
self.next_token()?;
|
||||
self.parse_mul_div_mod_expr()?
|
||||
@@ -696,6 +771,7 @@ impl<'source> Parser<'source> {
|
||||
op,
|
||||
lhs: Ref::new(expr),
|
||||
rhs: Ref::new(right),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -715,6 +791,7 @@ impl<'source> Parser<'source> {
|
||||
op: BinOp::Intersection,
|
||||
lhs: Ref::new(expr),
|
||||
rhs: Ref::new(right),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
}
|
||||
Ok(expr)
|
||||
@@ -735,6 +812,7 @@ impl<'source> Parser<'source> {
|
||||
op: BinOp::Union,
|
||||
lhs: Ref::new(expr),
|
||||
rhs: Ref::new(right),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
}
|
||||
Ok(expr)
|
||||
@@ -763,6 +841,7 @@ impl<'source> Parser<'source> {
|
||||
op,
|
||||
lhs: Ref::new(expr),
|
||||
rhs: Ref::new(right),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
}
|
||||
Ok(expr)
|
||||
@@ -789,6 +868,7 @@ impl<'source> Parser<'source> {
|
||||
key,
|
||||
value,
|
||||
collection: Ref::new(expr3),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
expr2 = None;
|
||||
|
||||
@@ -832,6 +912,7 @@ impl<'source> Parser<'source> {
|
||||
span,
|
||||
lhs: Ref::new(expr),
|
||||
rhs: Ref::new(rhs),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
}
|
||||
Ok(expr)
|
||||
@@ -864,11 +945,11 @@ impl<'source> Parser<'source> {
|
||||
let op = match self.token_text() {
|
||||
"=" => AssignOp::Eq,
|
||||
":=" if self.rego_v1 => {
|
||||
if let Expr::Var(v) = &expr {
|
||||
if v.0.text() == "input" {
|
||||
if let Expr::Var { span: v, .. } = &expr {
|
||||
if v.text() == "input" {
|
||||
bail!(span.error("input cannot be shadowed"));
|
||||
}
|
||||
if v.0.text() == "data" {
|
||||
if v.text() == "data" {
|
||||
bail!(span.error("data cannot be shadowed"));
|
||||
}
|
||||
}
|
||||
@@ -889,6 +970,7 @@ impl<'source> Parser<'source> {
|
||||
op,
|
||||
lhs: Ref::new(expr),
|
||||
rhs: Ref::new(right),
|
||||
eidx: self.next_eidx(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -973,7 +1055,7 @@ impl<'source> Parser<'source> {
|
||||
for (idx, ref_expr) in refs.iter().enumerate() {
|
||||
let span = &vars[idx];
|
||||
match ref_expr.as_ref() {
|
||||
Expr::Var(_) => (),
|
||||
Expr::Var { .. } => (),
|
||||
_ => {
|
||||
return Err(anyhow!(
|
||||
"{}:{}:{} error: encountered `{}` while expecting identifier",
|
||||
@@ -987,6 +1069,8 @@ impl<'source> Parser<'source> {
|
||||
}
|
||||
|
||||
span.end = self.end;
|
||||
// Since exprs are discarded, adjust the expression index counter.
|
||||
self.eidx -= vars.len() as u32;
|
||||
return Ok(Literal::SomeVars { span, vars });
|
||||
}
|
||||
|
||||
@@ -1053,6 +1137,7 @@ impl<'source> Parser<'source> {
|
||||
span,
|
||||
literal,
|
||||
with_mods,
|
||||
sidx: self.next_sidx(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1130,6 +1215,7 @@ impl<'source> Parser<'source> {
|
||||
Ok(Query {
|
||||
span,
|
||||
stmts: literals,
|
||||
qidx: self.next_qidx(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1166,7 +1252,12 @@ impl<'source> Parser<'source> {
|
||||
let start = self.tok.1.start;
|
||||
let var = self.parse_var()?;
|
||||
|
||||
let mut refr = Expr::Var(Self::span_and_value(var));
|
||||
let (span, value) = Self::span_and_value(var);
|
||||
let mut refr = Expr::Var {
|
||||
span,
|
||||
value,
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
loop {
|
||||
let mut span = self.tok.1.clone();
|
||||
let sep_pos = span.start;
|
||||
@@ -1203,12 +1294,20 @@ impl<'source> Parser<'source> {
|
||||
span,
|
||||
refr: Ref::new(refr),
|
||||
field: Self::span_and_value(field),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
}
|
||||
"[" => {
|
||||
self.next_token()?;
|
||||
let index = match &self.tok.0 {
|
||||
TokenKind::String => Expr::String(Self::span_and_value(self.tok.1.clone())),
|
||||
TokenKind::String => {
|
||||
let (span, value) = Self::span_and_value(self.tok.1.clone());
|
||||
Expr::String {
|
||||
span,
|
||||
value,
|
||||
eidx: self.next_eidx(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(self.source.error(
|
||||
self.tok.1.line,
|
||||
@@ -1224,6 +1323,7 @@ impl<'source> Parser<'source> {
|
||||
span,
|
||||
refr: Ref::new(refr),
|
||||
index: Ref::new(index),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
}
|
||||
_ => break,
|
||||
@@ -1247,7 +1347,12 @@ impl<'source> Parser<'source> {
|
||||
bail!(span.error("data cannot be shadowed"));
|
||||
}
|
||||
}
|
||||
Expr::Var(Self::span_and_value(v))
|
||||
let (span, value) = Self::span_and_value(v);
|
||||
Expr::Var {
|
||||
span,
|
||||
value,
|
||||
eidx: self.next_eidx(),
|
||||
}
|
||||
} else {
|
||||
return Err(self.source.error(
|
||||
span.line,
|
||||
@@ -1292,6 +1397,7 @@ impl<'source> Parser<'source> {
|
||||
span,
|
||||
refr: Ref::new(term),
|
||||
field: Self::span_and_value(field),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
}
|
||||
"[" => {
|
||||
@@ -1303,6 +1409,7 @@ impl<'source> Parser<'source> {
|
||||
span,
|
||||
refr: Ref::new(term),
|
||||
index: Ref::new(index),
|
||||
eidx: self.next_eidx(),
|
||||
};
|
||||
}
|
||||
_ => break,
|
||||
@@ -1362,15 +1469,17 @@ impl<'source> Parser<'source> {
|
||||
if assign.is_none() && is_set_follower {
|
||||
match rule_ref.as_ref() {
|
||||
Expr::RefBrack { refr, index, .. }
|
||||
if matches!(refr.as_ref(), Expr::Var(_)) =>
|
||||
if matches!(refr.as_ref(), Expr::Var { .. }) =>
|
||||
{
|
||||
// Adjust the expression counter since we are discarding the RefBrack expression.
|
||||
self.eidx -= 1;
|
||||
return Ok(RuleHead::Set {
|
||||
span,
|
||||
refr: refr.clone(),
|
||||
key: Some(index.clone()),
|
||||
});
|
||||
}
|
||||
Expr::RefDot { refr, .. } if matches!(refr.as_ref(), Expr::Var(_)) => {
|
||||
Expr::RefDot { refr, .. } if matches!(refr.as_ref(), Expr::Var { .. }) => {
|
||||
return Ok(RuleHead::Set {
|
||||
span,
|
||||
refr: rule_ref,
|
||||
@@ -1416,7 +1525,11 @@ impl<'source> Parser<'source> {
|
||||
*self = state;
|
||||
let stmts = vec![self.parse_literal_stmt()?];
|
||||
span.end = self.end;
|
||||
Ok(Query { span, stmts })
|
||||
Ok(Query {
|
||||
span,
|
||||
stmts,
|
||||
qidx: self.next_qidx(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_rule_bodies(&mut self) -> Result<Vec<RuleBody>> {
|
||||
@@ -1539,6 +1652,7 @@ impl<'source> Parser<'source> {
|
||||
let query = Ref::new(Query {
|
||||
span: query_span,
|
||||
stmts: vec![],
|
||||
qidx: self.next_qidx(),
|
||||
});
|
||||
span.end = self.end;
|
||||
bodies.push(RuleBody {
|
||||
@@ -1597,7 +1711,14 @@ impl<'source> Parser<'source> {
|
||||
refr: rule_ref,
|
||||
args: args
|
||||
.into_iter()
|
||||
.map(|a| Ref::new(Expr::Var(Self::span_and_value(a))))
|
||||
.map(|a| {
|
||||
let (span, value) = Self::span_and_value(a);
|
||||
Ref::new(Expr::Var {
|
||||
span,
|
||||
value,
|
||||
eidx: self.next_eidx(),
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
op,
|
||||
value,
|
||||
@@ -1769,12 +1890,21 @@ impl<'source> Parser<'source> {
|
||||
policy.push(Ref::new(self.parse_rule()?));
|
||||
}
|
||||
|
||||
Ok(Module {
|
||||
let m = Module {
|
||||
package,
|
||||
imports,
|
||||
policy,
|
||||
rego_v1: self.rego_v1,
|
||||
})
|
||||
num_expressions: self.eidx,
|
||||
num_statements: self.sidx,
|
||||
num_queries: self.qidx,
|
||||
};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
indexchecker::IndexChecker::default().check_module(&m)?;
|
||||
}
|
||||
Ok(m)
|
||||
}
|
||||
|
||||
pub fn parse_user_query(&mut self) -> Result<Ref<Query>> {
|
||||
|
||||
+30
-27
@@ -219,7 +219,12 @@ pub fn traverse(expr: &Ref<Expr>, f: &mut dyn FnMut(&Ref<Expr>) -> Result<bool>)
|
||||
return Ok(());
|
||||
}
|
||||
match expr.as_ref() {
|
||||
String(_) | RawString(_) | Number(_) | True(_) | False(_) | Null(_) | Var(_) => (),
|
||||
Expr::String { .. }
|
||||
| RawString { .. }
|
||||
| Number { .. }
|
||||
| Bool { .. }
|
||||
| Null { .. }
|
||||
| Var { .. } => (),
|
||||
|
||||
Array { items, .. } | Set { items, .. } => {
|
||||
for i in items {
|
||||
@@ -308,23 +313,23 @@ fn gather_assigned_vars(
|
||||
) -> Result<()> {
|
||||
traverse(expr, &mut |e| match e.as_ref() {
|
||||
// Ignore _, input, data.
|
||||
Var(v) if matches!(v.0.text(), "_" | "input" | "data") => Ok(false),
|
||||
Var { span: v, .. } if matches!(v.text(), "_" | "input" | "data") => Ok(false),
|
||||
|
||||
// Record local var that can shadow input var.
|
||||
Var(v) if can_shadow => {
|
||||
scope.locals.insert(v.0.source_str(), v.0.clone());
|
||||
Var { span: v, .. } if can_shadow => {
|
||||
scope.locals.insert(v.source_str(), v.clone());
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
// Record input vars.
|
||||
Var(v) if var_exists(&v.0, parent_scopes) => {
|
||||
scope.inputs.insert(v.0.source_str());
|
||||
Var { span: v, .. } if var_exists(v, parent_scopes) => {
|
||||
scope.inputs.insert(v.source_str());
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
// Record local var.
|
||||
Var(v) => {
|
||||
scope.unscoped.insert(v.0.source_str());
|
||||
Var { span: v, .. } => {
|
||||
scope.unscoped.insert(v.source_str());
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
@@ -336,10 +341,10 @@ fn gather_assigned_vars(
|
||||
|
||||
fn gather_input_vars(expr: &Ref<Expr>, parent_scopes: &[Scope], scope: &mut Scope) -> Result<()> {
|
||||
traverse(expr, &mut |e| match e.as_ref() {
|
||||
Var(v)
|
||||
if !scope.unscoped.contains(&v.0.source_str()) && var_exists(&v.0, parent_scopes) =>
|
||||
Var { span: v, .. }
|
||||
if !scope.unscoped.contains(&v.source_str()) && var_exists(v, parent_scopes) =>
|
||||
{
|
||||
scope.inputs.insert(v.0.source_str());
|
||||
scope.inputs.insert(v.source_str());
|
||||
Ok(false)
|
||||
}
|
||||
_ => Ok(true),
|
||||
@@ -555,8 +560,8 @@ impl Analyzer {
|
||||
RuleHead::Func { args, assign, .. } => {
|
||||
for a in args.iter() {
|
||||
traverse(a, &mut |e| {
|
||||
if let Var(v) = e.as_ref() {
|
||||
scope.unscoped.insert(v.0.source_str());
|
||||
if let Var { span: v, .. } = e.as_ref() {
|
||||
scope.unscoped.insert(v.source_str());
|
||||
}
|
||||
Ok(true)
|
||||
})?;
|
||||
@@ -647,10 +652,10 @@ impl Analyzer {
|
||||
let mut used_vars = vec![];
|
||||
let mut comprs = vec![];
|
||||
traverse(expr, &mut |e| match e.as_ref() {
|
||||
Var(v) if !matches!(v.0.text(), "_" | "input" | "data") => {
|
||||
let name = v.0.source_str();
|
||||
Var { span: v, .. } if !matches!(v.text(), "_" | "input" | "data") => {
|
||||
let name = v.source_str();
|
||||
let is_extra_arg = match assigned_vars {
|
||||
Some(vars) => vars.contains(&v.0.source_str()),
|
||||
Some(vars) => vars.contains(&v.source_str()),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
@@ -659,20 +664,18 @@ impl Analyzer {
|
||||
{
|
||||
if !is_extra_arg {
|
||||
used_vars.push(name.clone());
|
||||
first_use.entry(name).or_insert(v.0.clone());
|
||||
first_use.entry(name).or_insert(v.clone());
|
||||
}
|
||||
} else if !scope.inputs.contains(&name) {
|
||||
bail!(v
|
||||
.0
|
||||
.error(format!("use of undefined variable `{name}` is unsafe").as_str()));
|
||||
bail!(v.error(format!("use of undefined variable `{name}` is unsafe").as_str()));
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
RefBrack { refr, index, .. } => {
|
||||
traverse(index, &mut |e| match e.as_ref() {
|
||||
Var(v) => {
|
||||
let var = v.0.source_str();
|
||||
Var { span: v, .. } => {
|
||||
let var = v.source_str();
|
||||
if scope.locals.contains_key(&var) || scope.unscoped.contains(&var) {
|
||||
let (rb_used_vars, rb_comprs) =
|
||||
Self::gather_used_vars_comprs_index_vars(
|
||||
@@ -765,11 +768,11 @@ impl Analyzer {
|
||||
) -> Result<Vec<SourceStr>> {
|
||||
let mut vars = vec![];
|
||||
traverse(expr, &mut |e| match e.as_ref() {
|
||||
Var(v) => {
|
||||
let var = v.0.source_str();
|
||||
Var { span: v, .. } => {
|
||||
let var = v.source_str();
|
||||
if scope.locals.contains_key(&var) {
|
||||
if check_first_use {
|
||||
Self::check_first_use(&v.0, first_use)?;
|
||||
Self::check_first_use(v, first_use)?;
|
||||
}
|
||||
vars.push(var);
|
||||
} else if scope.unscoped.contains(&var) {
|
||||
@@ -955,8 +958,8 @@ impl Analyzer {
|
||||
non_vars: &mut Vec<Ref<Expr>>,
|
||||
) -> Result<()> {
|
||||
traverse(expr, &mut |e| match e.as_ref() {
|
||||
Var(v) if scope.locals.contains_key(&v.0.source_str()) => {
|
||||
vars.push(v.0.source_str());
|
||||
Var { span: v, .. } if scope.locals.contains_key(&v.source_str()) => {
|
||||
vars.push(v.source_str());
|
||||
Ok(false)
|
||||
}
|
||||
// TODO: Object key/value
|
||||
|
||||
+5
-5
@@ -19,13 +19,13 @@ pub fn get_path_string(refr: &Expr, document: Option<&str>) -> Result<String> {
|
||||
expr = Some(refr);
|
||||
}
|
||||
Some(Expr::RefBrack { refr, index, .. }) => {
|
||||
if let Expr::String(s) = index.as_ref() {
|
||||
comps.push(s.0.text());
|
||||
if let Expr::String { span: s, .. } = index.as_ref() {
|
||||
comps.push(s.text());
|
||||
}
|
||||
expr = Some(refr);
|
||||
}
|
||||
Some(Expr::Var(v)) => {
|
||||
comps.push(v.0.text());
|
||||
Some(Expr::Var { span: v, .. }) => {
|
||||
comps.push(v.text());
|
||||
expr = None;
|
||||
}
|
||||
_ => bail!("internal error: not a simple ref {expr:?}"),
|
||||
@@ -112,7 +112,7 @@ pub fn get_root_var(mut expr: &Expr) -> Result<SourceStr> {
|
||||
let empty = expr.span().source_str().clone_empty();
|
||||
loop {
|
||||
match expr {
|
||||
Expr::Var(v) => return Ok(v.0.source_str()),
|
||||
Expr::Var { span: v, .. } => return Ok(v.source_str()),
|
||||
Expr::RefDot { refr, .. } | Expr::RefBrack { refr, .. } => expr = refr,
|
||||
_ => return Ok(empty),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user