mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
A block with a single or expression needs to be treated as a comprehension instead of a
set/array with 1 item. e.g.: {1 | 1 }, [2 | foo]
Allow successfully parsing object comprehensions as rule body
x if { 1:2 | 1 }
fixes #306, fixes #307
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
1751 lines
57 KiB
Rust
1751 lines
57 KiB
Rust
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
use crate::ast::*;
|
|
use crate::lexer::*;
|
|
use crate::number::*;
|
|
use crate::value::*;
|
|
use crate::*;
|
|
|
|
use alloc::collections::BTreeMap;
|
|
use core::str::FromStr;
|
|
|
|
use anyhow::{anyhow, bail, Result};
|
|
|
|
#[derive(Clone)]
|
|
pub struct Parser<'source> {
|
|
source: Source,
|
|
lexer: Lexer<'source>,
|
|
tok: Token,
|
|
line: u32,
|
|
end: u32,
|
|
future_keywords: BTreeMap<String, Span>,
|
|
rego_v1: bool,
|
|
}
|
|
|
|
const FUTURE_KEYWORDS: [&str; 4] = ["contains", "every", "if", "in"];
|
|
|
|
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,
|
|
})
|
|
}
|
|
|
|
pub fn enable_rego_v1(&mut self) -> Result<()> {
|
|
self.turn_on_rego_v1(self.tok.1.clone())
|
|
}
|
|
|
|
fn turn_on_rego_v1(&mut self, span: 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::Symbol | TokenKind::Number | TokenKind::Ident | TokenKind::Eof => {
|
|
self.tok.1.text()
|
|
}
|
|
TokenKind::String | TokenKind::RawString => "",
|
|
}
|
|
}
|
|
|
|
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 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::println!(
|
|
"{}",
|
|
self.source
|
|
.message(self.tok.1.line, self.tok.1.col, "warning", &msg)
|
|
);
|
|
}
|
|
}
|
|
|
|
pub fn set_future_keyword(&mut self, kw: &str, span: &Span) -> Result<()> {
|
|
match &self.future_keywords.get(kw) {
|
|
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 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(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) => {
|
|
// Ensure that the span will be the serialized representation.
|
|
if *s.0.text() == s.1.to_json_str()? {
|
|
comps.push(s.0.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() - 2 {
|
|
1 => self.set_future_keyword(comps[2].text(), &comps[2])?,
|
|
0 => {
|
|
let span = &comps[1];
|
|
for kw in FUTURE_KEYWORDS.iter() {
|
|
self.set_future_keyword(kw, span)?;
|
|
}
|
|
}
|
|
_ => {
|
|
let s = &comps[3];
|
|
return Err(self
|
|
.source
|
|
.error(s.line, s.col - 1, "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(span: Span) -> Result<Expr> {
|
|
match Number::from_str(span.text()) {
|
|
Ok(v) => Ok(Expr::Number((span, Value::Number(v)))),
|
|
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, v))
|
|
}
|
|
TokenKind::RawString => {
|
|
let v = Value::from(span.text().to_string());
|
|
Expr::RawString((span, v))
|
|
}
|
|
TokenKind::Ident => match self.token_text() {
|
|
"null" => Expr::Null(span),
|
|
"true" => Expr::True(span),
|
|
"false" => Expr::False(span),
|
|
_ => {
|
|
let ident = self.parse_var()?;
|
|
let v = Value::from(ident.text());
|
|
return Ok(Expr::Var((ident, v)));
|
|
}
|
|
},
|
|
_ => {
|
|
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(_) if self.end == pos => {
|
|
// 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),
|
|
})
|
|
}
|
|
Err(_) if self.end == pos => {
|
|
// 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()?));
|
|
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()?)),
|
|
}
|
|
}
|
|
}
|
|
self.expect("]", "while parsing array")?;
|
|
span.end = self.end;
|
|
Ok(Expr::Array { span, items })
|
|
}
|
|
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),
|
|
});
|
|
}
|
|
Err(err) if self.end != pos => {
|
|
// Some progress was made parsing the set comprehension.
|
|
// Report errors.
|
|
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![],
|
|
});
|
|
}
|
|
|
|
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)];
|
|
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()?)),
|
|
}
|
|
}
|
|
self.expect("}", "while parsing set")?;
|
|
span.end = self.end;
|
|
return Ok(Expr::Set { span, items });
|
|
}
|
|
|
|
// 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),
|
|
});
|
|
}
|
|
Err(err) if self.end != pos => {
|
|
// Some progress was made parsing the object comprehension.
|
|
// Report errors.
|
|
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)));
|
|
|
|
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)));
|
|
}
|
|
|
|
self.expect("}", "while parsing object")?;
|
|
span.end = self.end;
|
|
|
|
Ok(Expr::Object {
|
|
span,
|
|
fields: items,
|
|
})
|
|
}
|
|
|
|
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![],
|
|
})
|
|
}
|
|
|
|
fn parse_parens_expr(&mut self) -> Result<Expr> {
|
|
self.next_token()?;
|
|
let expr = self.parse_membership_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),
|
|
})
|
|
}
|
|
|
|
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),
|
|
};
|
|
}
|
|
"[" => {
|
|
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),
|
|
};
|
|
}
|
|
"(" if possible_fcn => {
|
|
self.next_token()?;
|
|
let mut args = vec![];
|
|
if self.token_text() != ")" {
|
|
args.push(Ref::new(self.parse_in_expr()?));
|
|
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()?)),
|
|
}
|
|
}
|
|
}
|
|
self.expect(")", "while parsing call expr")?;
|
|
span.end = self.end;
|
|
term = Expr::Call {
|
|
span,
|
|
fcn: Ref::new(term),
|
|
params: args,
|
|
};
|
|
|
|
// 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),
|
|
};
|
|
}
|
|
}
|
|
|
|
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),
|
|
};
|
|
}
|
|
}
|
|
|
|
fn parse_and_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::And,
|
|
lhs: Ref::new(expr),
|
|
rhs: Ref::new(right),
|
|
};
|
|
}
|
|
Ok(expr)
|
|
}
|
|
|
|
fn parse_or_expr(&mut self) -> Result<Expr> {
|
|
let start = self.tok.1.start;
|
|
let mut expr = self.parse_and_expr()?;
|
|
|
|
while self.token_text() == "|" {
|
|
let mut span = self.tok.1.clone();
|
|
span.start = start;
|
|
self.next_token()?;
|
|
let right = self.parse_and_expr()?;
|
|
span.end = self.end;
|
|
expr = Expr::BinExpr {
|
|
span,
|
|
op: BinOp::Or,
|
|
lhs: Ref::new(expr),
|
|
rhs: Ref::new(right),
|
|
};
|
|
}
|
|
Ok(expr)
|
|
}
|
|
|
|
fn parse_bool_expr(&mut self) -> Result<Expr> {
|
|
let start = self.tok.1.start;
|
|
let mut expr = self.parse_or_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_or_expr()?;
|
|
span.end = self.end;
|
|
expr = Expr::BoolExpr {
|
|
span,
|
|
op,
|
|
lhs: Ref::new(expr),
|
|
rhs: Ref::new(right),
|
|
};
|
|
}
|
|
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),
|
|
};
|
|
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_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(v) = &expr {
|
|
if v.0.text() == "input" {
|
|
bail!(span.error("input cannot be shadowed"));
|
|
}
|
|
if v.0.text() == "data" {
|
|
bail!(span.error("data cannot be shadowed"));
|
|
}
|
|
}
|
|
AssignOp::ColEq
|
|
}
|
|
":=" => AssignOp::ColEq,
|
|
_ => {
|
|
*self = state;
|
|
return self.parse_membership_expr();
|
|
}
|
|
};
|
|
|
|
self.next_token()?;
|
|
let right = self.parse_membership_expr()?;
|
|
span.end = self.end;
|
|
Ok(Expr::AssignExpr {
|
|
span,
|
|
op,
|
|
lhs: Ref::new(expr),
|
|
rhs: Ref::new(right),
|
|
})
|
|
}
|
|
|
|
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),
|
|
});
|
|
}
|
|
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 mut vars = vec![self.tok.1.clone()];
|
|
let mut refs = vec![Ref::new(self.parse_ref()?)];
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
return Ok(Literal::SomeVars { span, vars });
|
|
}
|
|
|
|
let (key, value) = match refs.len() {
|
|
2 => (Some(refs[0].clone()), refs[1].clone()),
|
|
1 => (None, refs[0].clone()),
|
|
_ => {
|
|
let span = &vars[2];
|
|
return Err(anyhow!(
|
|
"{}:{}:{} error: encountered `{}` while expecting `in`",
|
|
span.source.file(),
|
|
span.line,
|
|
span.col,
|
|
span.text()
|
|
));
|
|
}
|
|
};
|
|
|
|
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,
|
|
})
|
|
}
|
|
|
|
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::Or, .. }) {
|
|
*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);
|
|
|
|
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);
|
|
}
|
|
|
|
if !end_delim.is_empty() {
|
|
self.expect(end_delim, "while parsing query")?;
|
|
}
|
|
span.end = self.end;
|
|
Ok(Query {
|
|
span,
|
|
stmts: literals,
|
|
})
|
|
}
|
|
|
|
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_membership_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 mut refr = Expr::Var(Self::span_and_value(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 => {
|
|
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),
|
|
};
|
|
}
|
|
"[" => {
|
|
self.next_token()?;
|
|
let index = match &self.tok.0 {
|
|
TokenKind::String => Expr::String(Self::span_and_value(self.tok.1.clone())),
|
|
_ => {
|
|
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),
|
|
};
|
|
}
|
|
_ => 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"));
|
|
}
|
|
}
|
|
Expr::Var(Self::span_and_value(v))
|
|
} 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),
|
|
};
|
|
}
|
|
"[" => {
|
|
self.next_token()?;
|
|
let index = self.parse_membership_expr()?;
|
|
span.end = self.end;
|
|
self.expect("]", "while parsing bracketed reference")?;
|
|
term = Expr::RefBrack {
|
|
span,
|
|
refr: Ref::new(term),
|
|
index: Ref::new(index),
|
|
};
|
|
}
|
|
_ => 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()?));
|
|
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()?)),
|
|
}
|
|
}
|
|
}
|
|
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_membership_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(_)) =>
|
|
{
|
|
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 })
|
|
}
|
|
|
|
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,
|
|
});
|
|
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,
|
|
});
|
|
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,
|
|
});
|
|
}
|
|
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,
|
|
});
|
|
}
|
|
"{" => {
|
|
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,
|
|
});
|
|
}
|
|
_ 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![],
|
|
});
|
|
span.end = self.end;
|
|
bodies.push(RuleBody {
|
|
span,
|
|
assign,
|
|
query,
|
|
});
|
|
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);
|
|
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| Ref::new(Expr::Var(Self::span_and_value(a))))
|
|
.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 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);
|
|
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);
|
|
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(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)
|
|
}
|
|
|
|
pub fn parse(&mut self) -> Result<Module> {
|
|
let package = self.parse_package()?;
|
|
let imports = self.parse_imports()?;
|
|
|
|
let mut policy = vec![];
|
|
while self.tok.0 != TokenKind::Eof {
|
|
policy.push(Ref::new(self.parse_rule()?));
|
|
}
|
|
|
|
Ok(Module {
|
|
package,
|
|
imports,
|
|
policy,
|
|
rego_v1: self.rego_v1,
|
|
})
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|