Files
regorus/src/parser.rs
antmhs 898643129e feat: make policy length limits configurable per engine (#624)
- Add PolicyLengthConfig struct with max_col, max_file_bytes, and
  max_lines fields, replacing hardcoded constants in the lexer.
- Add Engine::set_policy_length_config and clear_policy_length_config
  to allow callers to override the default limits.
- Add Source::from_contents_with_limits and from_file_with_limits for
  direct Source construction with custom limits; existing from_contents
  and from_file signatures are preserved using defaults.
- Add tests for default rejection, custom limits, and engine plumbing.
- Add bindings for C, C++, Python, WASM/JS, Java, Ruby, C#, Go
2026-03-13 12:19:57 -05:00

2155 lines
71 KiB
Rust

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#![allow(
clippy::arithmetic_side_effects,
clippy::indexing_slicing,
clippy::shadow_unrelated,
clippy::missing_const_for_fn,
clippy::semicolon_if_nothing_returned,
clippy::unused_self,
clippy::print_stderr,
clippy::as_conversions,
clippy::unused_trait_names,
clippy::pattern_type_mismatch
)]
#![allow(missing_debug_implementations)] // parser types are internal
use crate::ast::*;
use crate::lexer::*;
use crate::number::*;
use crate::value::*;
use crate::*;
use alloc::collections::BTreeMap;
use core::num::NonZeroU32;
use core::str::FromStr;
use anyhow::{anyhow, bail, Result};
#[inline]
fn check_memory_limit() -> Result<()> {
crate::utils::limits::check_memory_limit_if_needed().map_err(|err| anyhow!(err))
}
#[derive(Clone)]
pub struct Parser<'source> {
source: Source,
lexer: Lexer<'source>,
tok: Token,
line: u32,
end: u32,
future_keywords: BTreeMap<String, Option<Span>>,
rego_v1: bool,
// Tracks current expression/comprehension/query nesting to enforce a recursion limit.
expr_depth: usize,
max_expr_depth: usize,
expr_depth_overflow: 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"];
const DEFAULT_MAX_EXPR_DEPTH: usize = 32;
impl<'source> Parser<'source> {
pub fn new(source: &'source Source) -> Result<Self> {
let mut lexer = Lexer::new(source);
let tok = lexer.next_token()?;
Ok(Self {
source: source.clone(),
lexer,
tok,
line: 0,
end: 0,
future_keywords: BTreeMap::new(),
rego_v1: false,
expr_depth: 0,
max_expr_depth: DEFAULT_MAX_EXPR_DEPTH,
expr_depth_overflow: 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)
}
fn turn_on_rego_v1(&mut self, span: &Option<Span>) -> Result<()> {
self.rego_v1 = true;
for kw in FUTURE_KEYWORDS {
self.set_future_keyword(kw, span)?;
}
Ok(())
}
pub fn token_text(&self) -> &str {
match self.tok.0 {
TokenKind::String | TokenKind::RawString => "",
_ => self.tok.1.text(),
}
}
pub fn next_token(&mut self) -> Result<()> {
self.line = self.tok.1.line;
self.end = self.tok.1.end;
self.tok = self.lexer.next_token()?;
Ok(())
}
fn with_expr_depth<T>(&mut self, f: impl FnOnce(&mut Self) -> Result<T>) -> Result<T> {
// Increment expression depth.
self.expr_depth = self.expr_depth.saturating_add(1);
let current_depth = self.expr_depth;
// Enforce recursion limit.
if self.expr_depth > self.max_expr_depth {
self.expr_depth = current_depth.saturating_sub(1);
self.expr_depth_overflow = true;
bail!(self.tok.1.error(&format!(
"expression nesting too deep (>{})",
self.max_expr_depth
)));
}
let res = f(self);
// Upon return, ensure that expression depth is still current_depth.
if self.expr_depth != current_depth {
bail!("internal error: expression depth imbalance");
}
self.expr_depth = current_depth.saturating_sub(1);
res
}
fn expect(&mut self, text: &str, context: &str) -> Result<()> {
if self.token_text() == text {
self.next_token()
} else {
let msg = format!("expecting `{text}` {context}");
Err(self.source.error(self.tok.1.line, self.tok.1.col, &msg))
}
}
fn is_imported_future_keyword(&self, kw: &str) -> bool {
self.future_keywords.contains_key(kw)
}
pub fn warn_future_keyword(&self) {
#[cfg(feature = "std")]
{
let kw = self.token_text();
let msg = format!(
"`{kw}` will be treated as identifier due to missing `import future.keywords.{kw}`"
);
std::eprintln!(
"{}",
self.source
.message(self.tok.1.line, self.tok.1.col, "warning", &msg)
);
}
}
pub fn set_future_keyword(&mut self, kw: &str, span: &Option<Span>) -> Result<()> {
match (span, self.future_keywords.get(kw)) {
(Some(span), Some(Some(s))) if self.rego_v1 => Err(self.source.error(
span.line,
span.col,
format!(
"this import shadows previous import of `{kw}` defined at:{}",
s.message("", "this import is shadowed.")
)
.as_str(),
)),
_ => {
self.future_keywords.insert(kw.to_string(), span.clone());
if kw == "every" && !self.rego_v1 {
//rego.v1 explicitly adds each keyword.
self.future_keywords.insert("in".to_string(), span.clone());
}
Ok(())
}
}
}
pub fn set_max_col(&mut self, max_col: NonZeroU32) {
self.lexer.set_max_col(max_col);
}
pub fn get_path_ref_components_into(refr: &Ref<Expr>, comps: &mut Vec<Span>) -> Result<()> {
match refr.as_ref() {
Expr::RefDot { refr, field, .. } => {
Self::get_path_ref_components_into(refr, comps)?;
comps.push(field.0.clone());
}
Expr::RefBrack { refr, index, .. } => {
Self::get_path_ref_components_into(refr, comps)?;
Self::get_path_ref_components_into(index, comps)?;
}
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 span.text() == value.to_json_str()? {
comps.push(span.clone());
} else {
bail!(refr.span().error("not a valid ref"));
}
}
_ => bail!(refr.span().error("not a valid ref")),
}
Ok(())
}
pub fn get_path_ref_components(refr: &Ref<Expr>) -> Result<Vec<Span>> {
let mut comps = vec![];
Self::get_path_ref_components_into(refr, &mut comps)?;
Ok(comps)
}
fn handle_import_future_keywords(&mut self, comps: &[Span]) -> Result<bool> {
if comps.len() >= 2 && comps[0].text() == "future" && comps[1].text() == "keywords" {
match comps.len().saturating_sub(2) {
1 if comps.len() >= 3 => {
self.set_future_keyword(comps[2].text(), &Some(comps[2].clone()))?
}
0 => {
let span = &comps[1];
for kw in FUTURE_KEYWORDS.iter() {
self.set_future_keyword(kw, &Some(span.clone()))?;
}
}
_ if comps.len() >= 4 => {
let s = &comps[3];
return Err(self.source.error(
s.line,
s.col.saturating_sub(1),
"invalid future keyword",
));
}
_ => {
let s = &comps[1];
return Err(self.source.error(s.line, s.col, "invalid future keyword"));
}
}
Ok(true)
} else if !comps.is_empty() && comps[0].text() == "future" {
let s = &comps[0];
Err(self
.source
.error(s.line, s.col, "invalid import, must be `future.keywords`"))
} else {
Ok(false)
}
}
pub fn parse_future_keyword(
&mut self,
kw: &str,
is_optional: bool,
context: &str,
) -> Result<()> {
if self.token_text() == kw {
match &self.future_keywords.get(kw) {
Some(_) => self.next_token(),
None => {
self.warn_future_keyword();
Ok(())
}
}
} else if !is_optional {
// Required future keyword is missing.
self.expect(kw, context)
} else {
// Keyword is optional.
Ok(())
}
}
fn is_keyword(&self, ident: &str) -> bool {
matches!(
ident,
"as" | "default"
| "else"
| "false"
| "import"
| "package"
| "not"
| "null"
| "some"
| "true"
| "with"
)
}
fn parse_ident(&mut self) -> Result<Span> {
let span = self.tok.1.clone();
match self.tok.0 {
TokenKind::Ident if self.is_keyword(span.text()) => Err(self.source.error(
self.tok.1.line,
self.tok.1.col,
&format!("unexpected keyword `{}`", span.text()),
)),
TokenKind::Ident => {
self.next_token()?;
Ok(span)
}
_ => Err(self
.source
.error(self.tok.1.line, self.tok.1.col, "expecting identifier")),
}
}
fn parse_var(&mut self) -> Result<Span> {
let span = self.tok.1.clone();
match self.tok.0 {
TokenKind::Ident
if self.is_keyword(span.text())
|| (self.is_imported_future_keyword(span.text())
// contains can be the name of a builtin even when a keyword
&& span.text() != "contains") =>
{
Err(self.source.error(
self.tok.1.line,
self.tok.1.col,
&format!("unexpected keyword `{}`", span.text()),
))
}
TokenKind::Ident => {
self.next_token()?;
Ok(span)
}
_ => Err(self
.source
.error(self.tok.1.line, self.tok.1.col, "expecting identifier")),
}
}
fn read_number(&mut self, span: Span) -> Result<Expr> {
match Number::from_str(span.text()) {
Ok(v) => Ok(Expr::Number {
span,
value: Value::Number(v),
eidx: self.next_eidx(),
}),
Err(_) => bail!(span.error("could not parse number")),
}
}
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::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,
value: v,
eidx: self.next_eidx(),
}
}
TokenKind::RawString => {
let v = Value::from(span.text().to_string());
Expr::RawString {
span,
value: v,
eidx: self.next_eidx(),
}
}
TokenKind::Ident => match self.token_text() {
"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 value = Value::from(ident.text());
return Ok(Expr::Var {
span: ident,
value,
eidx: self.next_eidx(),
});
}
},
_ => {
return Err(self.source.error(
self.tok.1.line,
self.tok.1.col,
"expecting expression",
))
}
};
self.next_token()?;
Ok(node)
}
fn parse_compr(&mut self, delim: &str) -> Result<(Expr, Query)> {
// Save the state.
let state = self.clone();
let mut span = self.tok.1.clone();
// Parse the first expression as a ref.
let term = match self.parse_ref() {
Ok(e) if self.token_text() == "|" => e,
_ => {
// Not a comprehension. Restore state.
*self = state;
bail!("internal error: not a compr");
}
};
let query_span = self.tok.1.clone();
self.next_token()?;
let pos = self.end;
match self.parse_query(query_span, delim) {
Ok(query) => {
span.end = self.end;
Ok((term, query))
}
Err(err) if self.end == pos => {
// Propagate depth overflow error if any.
if self.expr_depth_overflow {
return Err(err);
}
// No progress was made in parsing the query.
// Restore state and try parsing as set, array or object.
*self = state;
bail!("internal error: not a compr");
}
Err(err) => Err(err),
}
}
fn parse_compr_or_array(&mut self) -> Result<Expr> {
// Save the state.
let mut span = self.tok.1.clone();
self.expect("[", "while parsing array comprehension or array")?;
let pos = self.end;
match self.parse_compr("]") {
Ok((term, query)) => {
span.end = self.end;
Ok(Expr::ArrayCompr {
span,
term: Ref::new(term),
query: Ref::new(query),
eidx: self.next_eidx(),
})
}
Err(err) if self.end == pos => {
// Propagate depth overflow error if any.
if self.expr_depth_overflow {
return Err(err);
}
// No progress was made in parsing comprehension.
// Parse as array.
let mut items = vec![];
if self.token_text() != "]" {
items.push(Ref::new(self.parse_in_expr()?));
// Guard array literal growth against allocator limits.
check_memory_limit()?;
while self.token_text() == "," {
self.next_token()?;
match self.token_text() {
"]" => break,
"" if self.tok.0 == TokenKind::Eof => break,
_ => {
items.push(Ref::new(self.parse_in_expr()?));
// Guard array literal growth against allocator limits.
check_memory_limit()?;
}
}
}
}
self.expect("]", "while parsing array")?;
span.end = self.end;
Ok(Expr::Array {
span,
items,
eidx: self.next_eidx(),
})
}
Err(err) => Err(err),
}
}
fn parse_compr_set_or_object(&mut self) -> Result<Expr> {
let mut span = self.tok.1.clone();
self.expect("{", "while parsing set, object or comprehension")?;
let pos = self.end;
match self.parse_compr("}") {
Ok((term, query)) => {
span.end = self.end;
return Ok(Expr::SetCompr {
span,
term: Ref::new(term),
query: Ref::new(query),
eidx: self.next_eidx(),
});
}
Err(err) if self.end != pos => {
// Propagate depth overflow error if any.
if self.expr_depth_overflow {
return Err(err);
}
// Some progress was made parsing the set comprehension.
// Report errors.
return Err(err);
}
Err(err) => {
// Propagate depth overflow error if any.
if self.expr_depth_overflow {
return Err(err);
}
}
}
// It could be a set, object or object comprehension.
// In all the cases, the first expression must parse successfully.
if self.token_text() == "}" {
self.next_token()?;
span.end = self.end;
return Ok(Expr::Object {
span,
fields: vec![],
eidx: self.next_eidx(),
});
}
let mut item_span = self.tok.1.clone();
let first = self.parse_in_expr()?;
if self.token_text() != ":" {
// Parse as set.
let mut items = vec![Ref::new(first)];
// Guard set literal growth against allocator limits.
check_memory_limit()?;
while self.token_text() == "," {
self.next_token()?;
match self.token_text() {
"}" => break,
"" if self.tok.0 == TokenKind::Eof => break,
_ => {
items.push(Ref::new(self.parse_in_expr()?));
// Guard set literal growth against allocator limits.
check_memory_limit()?;
}
}
}
self.expect("}", "while parsing set")?;
span.end = self.end;
return Ok(Expr::Set {
span,
items,
eidx: self.next_eidx(),
});
}
// Parse as object.
self.next_token()?;
let pos = self.end;
match self.parse_compr("}") {
Ok((term, query)) => {
span.end = self.end;
return Ok(Expr::ObjectCompr {
span,
key: Ref::new(first),
value: Ref::new(term),
query: Ref::new(query),
eidx: self.next_eidx(),
});
}
Err(err) if self.end != pos => {
// Propagate depth overflow error if any.
if self.expr_depth_overflow {
return Err(err);
}
// Some progress was made parsing the object comprehension.
// Report errors.
return Err(err);
}
Err(err) => {
// Propagate depth overflow error if any.
if self.expr_depth_overflow {
return Err(err);
}
}
}
// Parse object
let mut items = vec![];
let value = self.parse_in_expr()?;
item_span.end = self.end;
items.push((item_span, Ref::new(first), Ref::new(value)));
// Guard object literal growth against allocator limits.
check_memory_limit()?;
while self.token_text() == "," {
self.next_token()?;
let item_start = self.tok.1.start;
let key = match self.token_text() {
"}" => break,
"" if self.tok.0 == TokenKind::Eof => break,
_ => self.parse_in_expr()?,
};
let mut item_span = self.tok.1.clone();
span.start = item_start;
self.expect(":", "while parsing object item")?;
let value = self.parse_in_expr()?;
item_span.end = self.end;
items.push((item_span, Ref::new(key), Ref::new(value)));
// Guard object literal growth against allocator limits.
check_memory_limit()?;
}
self.expect("}", "while parsing object")?;
span.end = self.end;
Ok(Expr::Object {
span,
fields: items,
eidx: self.next_eidx(),
})
}
fn parse_empty_set(&mut self) -> Result<Expr> {
let mut span = self.tok.1.clone();
self.expect("set(", "while parsing empty set")?;
self.expect(")", "while parsing empty set")?;
span.end = self.tok.1.end;
Ok(Expr::Set {
span,
items: vec![],
eidx: self.next_eidx(),
})
}
fn parse_parens_expr(&mut self) -> Result<Expr> {
self.next_token()?;
let expr = self.parse_expr()?;
self.expect(")", "while parsing parenthesized expression")?;
//TODO: if needed introduce a parens-expr node or adjust expr's span.
Ok(expr)
}
fn parse_unary_expr(&mut self) -> Result<Expr> {
let mut span = self.tok.1.clone();
self.next_token()?;
let expr = self.parse_in_expr()?;
span.end = self.end;
Ok(Expr::UnaryExpr {
span,
expr: Ref::new(expr),
eidx: self.next_eidx(),
})
}
fn parse_ref(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut term = match self.token_text() {
"[" if self.tok.0 == TokenKind::Symbol => self.parse_compr_or_array()?,
"{" => self.parse_compr_set_or_object()?,
"set(" => self.parse_empty_set()?,
"(" => return self.parse_parens_expr(),
"-" => return self.parse_unary_expr(),
_ => self.parse_scalar_or_var()?,
};
let mut possible_fcn = true;
let mut expr = &term;
while possible_fcn {
match expr {
Expr::Var { .. } => break,
Expr::RefDot { refr, .. } => expr = refr,
Expr::RefBrack { refr, index, .. } => {
expr = refr;
possible_fcn = matches!(index.as_ref(), Expr::String { .. });
}
_ => {
possible_fcn = false;
}
}
}
matches!(&term, Expr::Var { .. });
loop {
let mut span = self.tok.1.clone();
let sep_pos = span.start;
span.start = start;
match self.token_text() {
"." | "[" if self.tok.1.start != self.end => {
if self.line != self.tok.1.line {
// Newline encountered. This could be a separate
// literal.
break;
}
bail!(
"{}",
self.source.error(
self.tok.1.line,
self.tok.1.col,
format!("invalid whitespace before {}", self.token_text()).as_str()
)
);
}
"." => {
// Read identifier.
self.next_token()?;
let field = self.parse_var()?;
span.end = self.end;
// Disallow any whitespace between . and identifier.
if field.start != sep_pos + 1 {
bail!(
"{}",
self.source.error(
field.line,
field.col - 1,
"invalid whitespace between . and identifier"
)
);
}
let fieldv = Value::from(field.text());
term = Expr::RefDot {
span,
refr: Ref::new(term),
field: (field, fieldv),
eidx: self.next_eidx(),
};
}
"[" => {
self.next_token()?;
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 { .. });
self.expect("]", "while parsing bracketed reference")?;
span.end = self.end;
term = Expr::RefBrack {
span,
refr: Ref::new(term),
index: Ref::new(index),
eidx: self.next_eidx(),
};
}
"(" if possible_fcn => {
self.next_token()?;
let mut args = vec![];
if self.token_text() != ")" {
args.push(Ref::new(self.parse_in_expr()?));
// Guard call argument list against allocator limits.
check_memory_limit()?;
while self.token_text() == "," {
self.next_token()?;
match self.token_text() {
")" => break,
"" if self.tok.0 == TokenKind::Eof => break,
_ => {
args.push(Ref::new(self.parse_in_expr()?));
// Guard call argument list against allocator limits.
check_memory_limit()?;
}
}
}
}
self.expect(")", "while parsing call expr")?;
span.end = self.end;
term = Expr::Call {
span,
fcn: Ref::new(term),
params: args,
eidx: self.next_eidx(),
};
// The expression can no longer be a function after the call.
possible_fcn = false;
}
_ => break,
}
}
Ok(term)
}
fn parse_term(&mut self) -> Result<Expr> {
self.parse_ref()
}
fn parse_mul_div_mod_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_term()?;
loop {
let mut span = self.tok.1.clone();
span.start = start;
let op = match self.token_text() {
"*" => ArithOp::Mul,
"/" => ArithOp::Div,
"%" => ArithOp::Mod,
_ => return Ok(expr),
};
self.next_token()?;
let right = self.parse_term()?;
span.end = self.end;
expr = Expr::ArithExpr {
span,
op,
lhs: Ref::new(expr),
rhs: Ref::new(right),
eidx: self.next_eidx(),
};
}
}
fn parse_arith_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_mul_div_mod_expr()?;
loop {
let mut span = self.tok.1.clone();
span.start = start;
let op = match self.token_text() {
"+" => ArithOp::Add,
"-" => ArithOp::Sub,
n if n.starts_with('-') && self.tok.0 == TokenKind::Number => ArithOp::Sub,
_ => return Ok(expr),
};
let right = if self.token_text().len() > 1 {
// Treat the - as a separate token
let mut rhs_span = self.tok.1.clone();
rhs_span.start += 1;
rhs_span.col += 1;
self.next_token()?;
self.read_number(rhs_span)?
} else {
self.next_token()?;
self.parse_mul_div_mod_expr()?
};
span.end = self.end;
expr = Expr::ArithExpr {
span,
op,
lhs: Ref::new(expr),
rhs: Ref::new(right),
eidx: self.next_eidx(),
};
}
}
fn parse_set_intersection_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_arith_expr()?;
while self.token_text() == "&" {
let mut span = self.tok.1.clone();
span.start = start;
self.next_token()?;
let right = self.parse_arith_expr()?;
span.end = self.end;
expr = Expr::BinExpr {
span,
op: BinOp::Intersection,
lhs: Ref::new(expr),
rhs: Ref::new(right),
eidx: self.next_eidx(),
};
}
Ok(expr)
}
fn parse_set_union_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_set_intersection_expr()?;
while self.token_text() == "|" {
let mut span = self.tok.1.clone();
span.start = start;
self.next_token()?;
let right = self.parse_set_intersection_expr()?;
span.end = self.end;
expr = Expr::BinExpr {
span,
op: BinOp::Union,
lhs: Ref::new(expr),
rhs: Ref::new(right),
eidx: self.next_eidx(),
};
}
Ok(expr)
}
fn parse_bool_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_set_union_expr()?;
loop {
let mut span = self.tok.1.clone();
span.start = start;
let op = match self.token_text() {
"<" => BoolOp::Lt,
"<=" => BoolOp::Le,
"==" => BoolOp::Eq,
">=" => BoolOp::Ge,
">" => BoolOp::Gt,
"!=" => BoolOp::Ne,
_ => break,
};
self.next_token()?;
let right = self.parse_set_union_expr()?;
span.end = self.end;
expr = Expr::BoolExpr {
span,
op,
lhs: Ref::new(expr),
rhs: Ref::new(right),
eidx: self.next_eidx(),
};
}
Ok(expr)
}
fn parse_membership_tail(
&mut self,
start: u32,
mut expr1: Expr,
mut expr2: Option<Expr>,
) -> Result<Expr> {
loop {
let mut span = self.tok.1.clone();
span.start = start;
self.parse_future_keyword("in", false, "while parsing membership expression")?;
let expr3 = self.parse_bool_expr()?;
span.end = self.end;
let (key, value) = match expr2 {
Some(e) => (Some(Ref::new(expr1)), Ref::new(e)),
None => (None, Ref::new(expr1)),
};
expr1 = Expr::Membership {
span,
key,
value,
collection: Ref::new(expr3),
eidx: self.next_eidx(),
};
expr2 = None;
if self.token_text() != "in" {
break;
}
}
Ok(expr1)
}
fn parse_in_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_bool_expr()?;
while self.token_text() == "in" && self.future_keywords.contains_key("in") {
expr = self.parse_membership_tail(start, expr, None)?;
}
Ok(expr)
}
pub fn parse_expr(&mut self) -> Result<Expr> {
self.with_expr_depth(|this| {
#[cfg(feature = "rego-extensions")]
{
this.parse_or_expr()
}
#[cfg(not(feature = "rego-extensions"))]
{
this.parse_membership_expr()
}
})
}
#[cfg(feature = "rego-extensions")]
pub fn parse_or_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_membership_expr()?;
while self.token_text() == "or" {
let mut span = self.tok.1.clone();
span.start = start;
self.next_token()?;
let rhs = self.parse_membership_expr()?;
expr = Expr::OrExpr {
span,
lhs: Ref::new(expr),
rhs: Ref::new(rhs),
eidx: self.next_eidx(),
};
}
Ok(expr)
}
pub fn parse_membership_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_bool_expr()?;
if self.token_text() == "," {
self.next_token()?;
let value = self.parse_bool_expr()?;
expr = self.parse_membership_tail(start, expr, Some(value))?;
}
while self.token_text() == "in" && self.is_imported_future_keyword("in") {
expr = self.parse_membership_tail(start, expr, None)?;
}
Ok(expr)
}
pub fn parse_assign_expr(&mut self) -> Result<Expr> {
let state = self.clone();
let start = self.tok.1.start;
let expr = self.parse_ref()?;
let mut span = self.tok.1.clone();
span.start = start;
let op = match self.token_text() {
"=" => AssignOp::Eq,
":=" if self.rego_v1 => {
if let Expr::Var { span: v, .. } = &expr {
if v.text() == "input" {
bail!(span.error("input cannot be shadowed"));
}
if v.text() == "data" {
bail!(span.error("data cannot be shadowed"));
}
}
AssignOp::ColEq
}
":=" => AssignOp::ColEq,
_ => {
*self = state;
return self.parse_expr();
}
};
self.next_token()?;
let right = self.parse_expr()?;
span.end = self.end;
Ok(Expr::AssignExpr {
span,
op,
lhs: Ref::new(expr),
rhs: Ref::new(right),
eidx: self.next_eidx(),
})
}
fn parse_with_modifiers(&mut self) -> Result<Vec<WithModifier>> {
let mut modifiers = vec![];
while self.token_text() == "with" {
let mut span = self.tok.1.clone();
self.next_token()?;
let refr = self.parse_path_ref()?;
self.expect("as", "while parsing with-modifier expression")?;
let r#as = self.parse_in_expr()?;
span.end = self.end;
modifiers.push(WithModifier {
span,
refr: Ref::new(refr),
r#as: Ref::new(r#as),
});
// Guard with-modifier list against allocator limits.
check_memory_limit()?;
}
Ok(modifiers)
}
fn parse_every_stmt(&mut self) -> Result<Literal> {
let mut span = self.tok.1.clone();
let context = "Failed to parse `every` statement.";
self.parse_future_keyword("every", false, context)?;
let ident = self.parse_var()?;
let (key, value) = match self.token_text() {
"," => {
self.next_token()?;
match self.parse_var() {
Ok(v) => (Some(ident), v),
Err(e) => {
return Err(self.source.error(
span.line,
span.col,
format!("Failed to parse `every` statement.\n{e}").as_str(),
))
}
}
}
_ => (None, ident),
};
self.parse_future_keyword("in", false, context)?;
let domain = Ref::new(self.parse_bool_expr()?);
let query_span = self.tok.1.clone();
self.expect("{", context)?;
let query = Ref::new(self.parse_query(query_span, "}")?);
span.end = self.end;
Ok(Literal::Every {
span,
key,
value,
domain,
query,
})
}
fn parse_some_stmt(&mut self) -> Result<Literal> {
let mut span = self.tok.1.clone();
self.expect("some", "while parsing some-decl")?;
// parse any vars.
let first_var = self.tok.1.clone();
let first_ref = Ref::new(self.parse_ref()?);
let mut vars = vec![first_var];
let mut refs = vec![first_ref];
// Guard some-statement bindings against allocator limits.
check_memory_limit()?;
while self.token_text() == "," {
self.next_token()?;
let mut span = self.tok.1.clone();
refs.push(Ref::new(self.parse_ref()?));
span.end = self.end;
vars.push(span);
// Guard some-statement bindings against allocator limits.
check_memory_limit()?;
}
if self.token_text() != "in" || !self.is_imported_future_keyword("in") {
if self.token_text() == "in" {
self.warn_future_keyword();
}
// All the refs must be identifiers
for (idx, ref_expr) in refs.iter().enumerate() {
let span = &vars[idx];
match ref_expr.as_ref() {
Expr::Var { .. } => (),
_ => {
return Err(anyhow!(
"{}:{}:{} error: encountered `{}` while expecting identifier",
span.source.file(),
span.line,
span.col,
span.text()
));
}
}
}
span.end = self.end;
// Since exprs are discarded, adjust the expression index counter (saturating to avoid underflow).
self.eidx = self.eidx.saturating_sub(vars.len() as u32);
return Ok(Literal::SomeVars { span, vars });
}
if refs.len() >= 3 {
// Too many identifiers before `in`.
if let Some(span) = vars.get(2).or_else(|| vars.last()) {
return Err(anyhow!(
"{}:{}:{} error: encountered `{}` while expecting `in`",
span.source.file(),
span.line,
span.col,
span.text()
));
}
return Err(anyhow!(
"invalid some-decl: expected `in` after variable names"
));
}
let (key, value) = match refs.len() {
2 => (Some(refs[0].clone()), refs[1].clone()),
1 => (None, refs[0].clone()),
_ => {
// We always parse at least one identifier before `in`; guard defensively.
// parse_ident rejects `in` when no vars are present, so this is effectively unreachable.
return Err(anyhow!(
"invalid some-decl: expected variable names before `in`"
));
}
};
self.parse_future_keyword("in", false, "while parsing some-decl")?;
let collection = Ref::new(self.parse_bool_expr()?); // TODO: check this
Ok(Literal::SomeIn {
span,
key,
value,
collection,
})
}
fn parse_literal(&mut self) -> Result<Literal> {
match self.token_text() {
"some" => return self.parse_some_stmt(),
"every" => {
if self.future_keywords.contains_key("every") {
return self.parse_every_stmt();
}
self.warn_future_keyword();
}
_ => (),
}
let mut span = self.tok.1.clone();
let not_expr = if self.token_text() == "not" {
self.next_token()?;
true
} else {
false
};
let expr = Ref::new(self.parse_assign_expr()?);
span.end = self.end;
if not_expr {
Ok(Literal::NotExpr { span, expr })
} else {
Ok(Literal::Expr { span, expr })
}
}
pub fn parse_literal_stmt(&mut self) -> Result<LiteralStmt> {
let mut span = self.tok.1.clone();
let literal = self.parse_literal()?;
let with_mods = self.parse_with_modifiers()?;
span.end = self.end;
Ok(LiteralStmt {
span,
literal,
with_mods,
sidx: self.next_sidx(),
})
}
fn parse_query(&mut self, mut span: Span, end_delim: &str) -> Result<Query> {
let state = self.clone();
let is_definite_query = matches!(self.token_text(), "some" | "every");
// TODO: empty query?
let mut literals = vec![];
let stmt = match self.parse_literal_stmt() {
Ok(_) if self.token_text() == ":" => {
// This is likely an object comprehension.
// Restore the state and return.
*self = state;
bail!("try parsing as comprehension");
}
Ok(stmt) if self.token_text() == end_delim => {
// Treat { 1 | 1 } as a comprehension instead of a
// set of 1 element.
if let Literal::Expr { expr: e, .. } = &stmt.literal {
if matches!(
e.as_ref(),
Expr::BinExpr {
op: BinOp::Union,
..
}
) {
*self = state;
bail!("try parse as comprehension");
}
}
stmt
}
Ok(stmt) => stmt,
Err(e) if is_definite_query => return Err(e),
Err(e) if matches!(self.token_text(), "=" | ":=") => return Err(e),
Err(_) => {
// There was error parsing the first literal
// Restore the state and return.
*self = state;
bail!(span.error(format!("expecting {end_delim}").as_str()));
}
};
if self.token_text() == "," {
// This is likely an array or set.
// Restore the state.
*self = state;
return Err(anyhow!("encountered , when expecting {}", end_delim));
}
literals.push(stmt);
// Guard query literal accumulation against allocator limits.
check_memory_limit()?;
loop {
match self.token_text() {
t if t == end_delim => break,
"" if self.tok.0 == TokenKind::Eof => break,
";" => self.next_token()?,
_ => {
// Next literal must be on a new line.
if self.line == self.tok.1.line {
break;
}
}
}
let stmt = self.parse_literal_stmt()?;
literals.push(stmt);
// Guard query literal accumulation against allocator limits.
check_memory_limit()?;
}
if !end_delim.is_empty() {
self.expect(end_delim, "while parsing query")?;
}
span.end = self.end;
Ok(Query {
span,
stmts: literals,
qidx: self.next_qidx(),
})
}
pub fn parse_rule_assign(&mut self) -> Result<Option<RuleAssign>> {
let mut span = self.tok.1.clone();
let op = match self.token_text() {
"=" => {
self.next_token()?;
AssignOp::Eq
}
":=" => {
self.next_token()?;
AssignOp::ColEq
}
_ => return Ok(None),
};
let expr = Ref::new(self.parse_expr()?);
span.end = self.end;
Ok(Some(RuleAssign {
span,
op,
value: expr,
}))
}
fn span_and_value(s: Span) -> (Span, Value) {
let v = Value::from(s.text());
(s, v)
}
fn parse_path_ref(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let var = self.parse_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;
span.start = start;
match self.token_text() {
"." | "[" if self.tok.1.start != self.end => {
bail!(
"{}",
self.source.error(
self.tok.1.line,
self.tok.1.col - 1,
format!("invalid whitespace before {}", self.token_text()).as_str()
)
);
}
"." => {
// Read identifier.
self.next_token()?;
let field = self.parse_ident()?;
span.end = self.end;
// Disallow any whitespace between . and identifier.
if field.start != sep_pos + 1 {
bail!(
"{}",
self.source.error(
field.line,
field.col - 1,
"invalid whitespace between . and identifier"
)
);
}
refr = Expr::RefDot {
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 => {
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,
self.tok.1.col,
"expected string",
));
}
};
self.next_token()?;
self.expect("]", "while parsing bracketed reference")?;
span.end = self.end;
refr = Expr::RefBrack {
span,
refr: Ref::new(refr),
index: Ref::new(index),
eidx: self.next_eidx(),
};
}
_ => break,
}
}
Ok(refr)
}
fn parse_rule_ref(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let span = self.tok.1.clone();
let mut term = if self.tok.0 == TokenKind::Ident {
let v = self.parse_var()?;
if self.rego_v1 {
if v.text() == "input" {
bail!(span.error("input cannot be shadowed"));
}
if v.text() == "data" {
bail!(span.error("data cannot be shadowed"));
}
}
let (span, value) = Self::span_and_value(v);
Expr::Var {
span,
value,
eidx: self.next_eidx(),
}
} else {
return Err(self.source.error(
span.line,
span.col,
"expecting identifier. Failed to parse rule-ref.",
));
};
loop {
let mut span = self.tok.1.clone();
span.start = start;
match self.token_text() {
// . and [ must not have any space between the previous token.
"." | "[" if self.tok.1.start != self.end => {
bail!(
"{}",
self.source.error(
self.tok.1.line,
self.tok.1.col - 1,
format!("invalid whitespace before {}", self.token_text()).as_str()
)
);
}
"." => {
let sep_pos = self.tok.1.start;
self.next_token()?;
let field = self.parse_var()?;
span.end = self.end;
// Disallow any whitespace between . and identifier.
if field.start != sep_pos + 1 {
bail!(
"{}",
self.source.error(
field.line,
field.col - 1,
"invalid whitespace between . and identifier"
)
);
}
term = Expr::RefDot {
span,
refr: Ref::new(term),
field: Self::span_and_value(field),
eidx: self.next_eidx(),
};
}
"[" => {
self.next_token()?;
let index = self.parse_expr()?;
span.end = self.end;
self.expect("]", "while parsing bracketed reference")?;
term = Expr::RefBrack {
span,
refr: Ref::new(term),
index: Ref::new(index),
eidx: self.next_eidx(),
};
}
_ => break,
}
}
Ok(term)
}
pub fn parse_rule_head(&mut self) -> Result<RuleHead> {
let mut span = self.tok.1.clone();
let rule_ref = Ref::new(self.parse_rule_ref()?);
match self.token_text() {
"(" => {
self.next_token()?;
let mut args = vec![];
if self.token_text() != ")" {
args.push(Ref::new(self.parse_term()?));
// Guard rule head arguments against allocator limits.
check_memory_limit()?;
while self.token_text() == "," {
self.next_token()?;
match self.token_text() {
")" => break,
"" if self.tok.0 == TokenKind::Eof => break,
_ => {
args.push(Ref::new(self.parse_term()?));
// Guard rule head arguments against allocator limits.
check_memory_limit()?;
}
}
}
}
self.expect(")", "while parsing function rule args")?;
let assign = self.parse_rule_assign()?;
span.end = self.end;
Ok(RuleHead::Func {
span,
refr: rule_ref,
args,
assign,
})
}
"contains" => {
self.next_token()?;
let key = Ref::new(self.parse_expr()?);
span.end = self.end;
Ok(RuleHead::Set {
span,
refr: rule_ref,
key: Some(key),
})
}
_ => {
let assign = self.parse_rule_assign()?;
span.end = self.end;
// Determine whether to create a set or a compr
let is_set_follower = !self.is_keyword(self.token_text())
&& !self.is_imported_future_keyword(self.token_text());
if assign.is_none() && is_set_follower {
match rule_ref.as_ref() {
Expr::RefBrack { refr, index, .. }
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 { .. }) => {
return Ok(RuleHead::Set {
span,
refr: rule_ref,
key: None,
});
}
_ => (),
}
}
// Default to a compr rule.
Ok(RuleHead::Compr {
span,
refr: rule_ref,
assign,
})
}
}
}
pub fn if_is_keyword(&self) -> bool {
self.future_keywords.contains_key("if")
}
pub fn parse_query_or_literal_stmt(&mut self) -> Result<Query> {
let state = self.clone();
let mut span = self.tok.1.clone();
if self.token_text() == "{" {
self.next_token()?;
let pos = self.end;
match self.parse_query(span.clone(), "}") {
Ok(query) => return Ok(query),
Err(e) if pos != self.end => {
// Error encountered while parsing query.
return Err(e);
}
_ => (),
}
}
// Restore state.
*self = state;
let stmts = vec![self.parse_literal_stmt()?];
span.end = self.end;
Ok(Query {
span,
stmts,
qidx: self.next_qidx(),
})
}
pub fn parse_rule_bodies(&mut self) -> Result<Vec<RuleBody>> {
let mut span = self.tok.1.clone();
let mut bodies = vec![];
let assign = None;
let has_query = match self.token_text() {
"if" if self.if_is_keyword() => {
self.next_token()?;
let query = Ref::new(self.parse_query_or_literal_stmt()?);
span.end = self.end;
bodies.push(RuleBody {
span,
assign,
query,
});
// Guard rule body accumulation against allocator limits.
check_memory_limit()?;
true
}
"if" => {
self.warn_future_keyword();
false
}
"{" => {
if self.rego_v1 {
bail!(span.error("`if` keyword is required before rule body"));
}
self.next_token()?;
let query = Ref::new(self.parse_query(span.clone(), "}")?);
span.end = self.end;
bodies.push(RuleBody {
span,
assign,
query,
});
// Guard rule body accumulation against allocator limits.
check_memory_limit()?;
true
}
_ => false,
};
match self.token_text() {
"{" if has_query => self.parse_query_blocks(&mut bodies)?,
"else" if has_query => self.parse_else_blocks(&mut bodies)?,
_ => (),
}
Ok(bodies)
}
pub fn parse_query_blocks(&mut self, bodies: &mut Vec<RuleBody>) -> Result<()> {
while self.token_text() == "{" {
let mut span = self.tok.1.clone();
self.next_token()?;
let query = Ref::new(self.parse_query(span.clone(), "}")?);
span.end = self.end;
bodies.push(RuleBody {
span,
assign: None,
query,
});
// Guard rule body accumulation against allocator limits.
check_memory_limit()?;
}
Ok(())
}
pub fn parse_else_blocks(&mut self, bodies: &mut Vec<RuleBody>) -> Result<()> {
loop {
let mut span = self.tok.1.clone();
match self.token_text() {
"{" => {
return Err(self.source.error(
self.tok.1.line,
self.tok.1.col,
"expected `else` keyword",
))
}
"else" => self.next_token()?,
_ => break,
}
let assign = self.parse_rule_assign()?;
match self.token_text() {
"if" if self.if_is_keyword() => {
self.next_token()?;
let query = Ref::new(self.parse_query_or_literal_stmt()?);
span.end = self.end;
bodies.push(RuleBody {
span,
assign,
query,
});
// Guard rule body accumulation against allocator limits.
check_memory_limit()?;
}
"{" => {
if self.rego_v1 {
bail!(span.error("`if` keyword is required before rule body"));
}
self.next_token()?;
let query = Ref::new(self.parse_query(span.clone(), "}")?);
span.end = self.end;
bodies.push(RuleBody {
span,
assign,
query,
});
// Guard rule body accumulation against allocator limits.
check_memory_limit()?;
}
_ if assign.is_none() => {
if self.token_text() == "if" {
self.warn_future_keyword();
}
return Err(self.source.error(
self.tok.1.line,
self.tok.1.col,
"expected assignment or query after `else`",
));
}
_ => {
let mut query_span = span.clone();
query_span.end = query_span.start;
let query = Ref::new(Query {
span: query_span,
stmts: vec![],
qidx: self.next_qidx(),
});
span.end = self.end;
bodies.push(RuleBody {
span,
assign,
query,
});
// Guard rule body accumulation against allocator limits.
check_memory_limit()?;
break;
}
}
}
Ok(())
}
pub fn parse_default_rule(&mut self) -> Result<Rule> {
let mut span = self.tok.1.clone();
self.expect("default", "while parsing default rule")?;
let rule_ref = Ref::new(self.parse_rule_ref()?);
let mut args = vec![];
if self.token_text() == "(" {
self.next_token()?;
if self.token_text() != ")" {
loop {
let arg = self.parse_ident()?;
if arg.text() != "_" && args.iter().any(|a: &Span| *a.text() == *arg.text()) {
bail!(arg.error("repeating parameter name"));
}
args.push(arg);
// Guard default rule parameters against allocator limits.
check_memory_limit()?;
if self.token_text() == ")" || self.tok.0 == TokenKind::Eof {
break;
}
self.expect(",", "while parsing default rule parameters")?;
}
}
self.expect(")", "while parsing default rule parameters")?;
}
let op = match self.token_text() {
"=" => AssignOp::Eq,
":=" => AssignOp::ColEq,
_ => {
self.expect(":=", "while parsing default rule")?;
// Should never reach here.
AssignOp::Eq
}
};
self.next_token()?;
// todo: Rego errors for binary expressions here, but they are
// somehow valid in a comprehension
let value = Ref::new(self.parse_term()?);
span.end = self.end;
Ok(Rule::Default {
span,
refr: rule_ref,
args: args
.into_iter()
.map(|a| {
let (span, value) = Self::span_and_value(a);
Ref::new(Expr::Var {
span,
value,
eidx: self.next_eidx(),
})
})
.collect(),
op,
value,
})
}
pub fn parse_rule(&mut self) -> Result<Rule> {
let pos = self.end;
match self.parse_default_rule() {
Ok(r) => return Ok(r),
Err(e) if pos != self.end => return Err(e),
_ => (),
}
let mut span = self.tok.1.clone();
let head = self.parse_rule_head()?;
let bodies = self.parse_rule_bodies()?;
span.end = self.end;
if let RuleHead::Set { .. } = &head {
if let Some(RuleBody { span, .. }) = bodies.get(1) {
bail!(span.error("else cannot be used with set rules"));
}
}
if self.rego_v1 && bodies.is_empty() {
match &head {
RuleHead::Compr { assign, .. } | RuleHead::Func { assign, .. }
if assign.is_none() =>
{
bail!(span.error("rule must have a body or assignment"));
}
RuleHead::Set { refr, key, .. } if key.is_none() => {
if Self::get_path_ref_components(refr)?.len() == 2 {
bail!(span.error("`contains` keyword is required for partial set rules"));
} else {
bail!(span.error("rule must have a body or assignment"));
}
}
_ => (),
}
}
Ok(Rule::Spec { span, head, bodies })
}
pub fn parse_package(&mut self) -> Result<Package> {
let mut span = self.tok.1.clone();
self.expect("package", "Missing package declaration.")?;
let name = self.parse_path_ref()?;
span.end = self.end;
Ok(Package {
span,
refr: Ref::new(name),
})
}
fn check_and_add_import(&self, import: Import, imports: &mut Vec<Import>) -> Result<()> {
let ref_comps = Self::get_path_ref_components(&import.refr)?;
let comps: Vec<&str> = ref_comps.iter().map(|s| s.text()).collect();
if comps.len() >= 2 && comps[0] == "future" && comps[1] == "keywords" {
imports.push(import);
// Guard import accumulation against allocator limits.
check_memory_limit()?;
return Ok(());
}
for imp in imports.iter() {
let imp_comps = Self::get_path_ref_components(&imp.refr)?;
let imp_comps: Vec<&str> = imp_comps.iter().map(|s| s.text()).collect();
let shadow = match (&imp.r#as, &import.r#as) {
(Some(i1), Some(i2)) if i1.text() == i2.text() => true,
(None, None) if imp_comps == comps => true,
_ => false,
};
if shadow {
return Err(self.source.error(
import.span.line,
import.span.col,
format!(
"import shadows following import defined earlier:{}",
self.source.message(
imp.span.line,
imp.span.col,
"",
"this import is shadowed"
)
)
.as_str(),
));
}
}
imports.push(import);
// Guard import accumulation against allocator limits.
check_memory_limit()?;
Ok(())
}
fn parse_imports(&mut self) -> Result<Vec<Import>> {
let mut imports = vec![];
while self.token_text() == "import" {
let mut span = self.tok.1.clone();
self.next_token()?;
let refr = Ref::new(self.parse_path_ref()?);
let comps = Self::get_path_ref_components(&refr)?;
span.end = self.end;
if !matches!(comps[0].text(), "data" | "future" | "input" | "rego") {
return Err(self.source.error(
comps[0].line,
comps[0].col,
"import path must begin with one of: {data, future, input, rego}",
));
}
let is_future_kw =
if comps.len() == 2 && comps[0].text() == "rego" && comps[1].text() == "v1" {
self.turn_on_rego_v1(&Some(span.clone()))?;
true
} else {
self.handle_import_future_keywords(&comps)?
};
let var = if self.token_text() == "as" {
if is_future_kw {
return Err(self.source.error(
self.tok.1.line,
self.tok.1.col,
"`future` imports cannot be aliased",
));
}
self.next_token()?;
let var = self.parse_var()?;
if var.text() == "_" {
return Err(self.source.error(
var.line,
var.col,
"`_` cannot be used as alias",
));
}
Some(var)
} else {
None
};
span.end = self.end;
// TODO: interpreter must check that all the imports are used.
// future.keywords don't have to be used.
self.check_and_add_import(
Import {
span,
refr,
r#as: var,
},
&mut imports,
)?;
}
Ok(imports)
}
fn parse_string_literal(&mut self) -> Result<String> {
if self.tok.0 != TokenKind::String {
bail!(self.tok.1.error("expected string literal"));
}
let string_span = self.tok.1.clone();
let target_value =
match serde_json::from_str::<Value>(format!("\"{}\"", string_span.text()).as_str()) {
Ok(v) => v,
Err(e) => {
bail!(string_span.error(&format!("invalid string literal: {}", e)));
}
};
self.next_token()?;
match target_value.as_string() {
Ok(s) => Ok(s.as_ref().to_string()),
Err(_) => {
bail!(string_span.error("invalid string value"));
}
}
}
fn parse_target_rule(&mut self) -> Result<Option<String>> {
if self.tok.0 == TokenKind::Ident && self.token_text() == "__target__" {
// Parse __target__
self.next_token()?;
// Expect := operator
if self.token_text() != ":=" {
bail!(self.tok.1.error("expected ':=' after __target__"));
}
self.next_token()?;
// Parse the target name string using the helper function
let target_string = self.parse_string_literal()?;
Ok(Some(target_string))
} else {
Ok(None)
}
}
pub fn parse(&mut self) -> Result<Module> {
let package = self.parse_package()?;
let imports = self.parse_imports()?;
let target = self.parse_target_rule()?;
if target.is_some() {
self.rego_v1 = true;
}
let mut policy = vec![];
while self.tok.0 != TokenKind::Eof {
policy.push(Ref::new(self.parse_rule()?));
// Guard policy rule accumulation against allocator limits.
check_memory_limit()?;
if self.token_text() == "__target__" {
bail!(self
.tok
.1
.error("__target__ must be defined before any rules"));
}
}
let m = Module {
package,
imports,
target,
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>> {
let span = self.tok.1.clone();
let query = Ref::new(self.parse_query(span, "")?);
if self.tok.0 != TokenKind::Eof {
bail!(self.tok.1.error("expecting EOF"));
}
Ok(query)
}
pub fn num_expressions(&self) -> u32 {
self.eidx
}
pub fn num_statements(&self) -> u32 {
self.sidx
}
pub fn num_queries(&self) -> u32 {
self.qidx
}
}