Skip to content

Commit

Permalink
feat: indirection parsing (#4356)
Browse files Browse the repository at this point in the history
  • Loading branch information
aljazerzen committed Mar 25, 2024
1 parent 51347e6 commit ee6b2ce
Show file tree
Hide file tree
Showing 12 changed files with 831 additions and 895 deletions.
19 changes: 12 additions & 7 deletions prqlc/prqlc-ast/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ pub struct Expr {

#[derive(Debug, EnumAsInner, PartialEq, Clone, Serialize, Deserialize, strum::AsRefStr)]
pub enum ExprKind {
Ident(Ident),
Ident(String),
Indirection {
base: Box<Expr>,
field: IndirectionKind,
},
Literal(Literal),
Pipeline(Pipeline),

Expand All @@ -65,6 +69,13 @@ pub enum ExprKind {
Internal(String),
}

#[derive(Debug, EnumAsInner, PartialEq, Clone, Serialize, Deserialize)]
pub enum IndirectionKind {
Name(String),
Position(i64),
Star,
}

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct BinaryExpr {
pub left: Box<Expr>,
Expand Down Expand Up @@ -144,12 +155,6 @@ impl From<Literal> for ExprKind {
}
}

impl From<Ident> for ExprKind {
fn from(value: Ident) -> Self {
ExprKind::Ident(value)
}
}

impl From<Func> for ExprKind {
fn from(value: Func) -> Self {
ExprKind::Func(Box::new(value))
Expand Down
27 changes: 23 additions & 4 deletions prqlc/prqlc-parser/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub fn expr() -> impl Parser<TokenKind, Expr, Error = PError> + Clone {
recursive(|expr| {
let literal = select! { TokenKind::Literal(lit) => ExprKind::Literal(lit) };

let ident_kind = ident().map(ExprKind::Ident);
let ident_kind = ident_part().map(ExprKind::Ident);

let nested_expr = pipeline(lambda_func(expr.clone()).or(func_call(expr.clone()))).boxed();

Expand Down Expand Up @@ -132,6 +132,26 @@ pub fn expr() -> impl Parser<TokenKind, Expr, Error = PError> + Clone {
.or(pipeline)
.boxed();

// indirections
let term = term
.then(
ctrl('.')
.ignore_then(choice((
ident_part().map(IndirectionKind::Name),
ctrl('*').to(IndirectionKind::Star),
select! {
TokenKind::Literal(Literal::Integer(i)) => IndirectionKind::Position(i)
},
)))
.map_with_span(|f, s| (f, s))
.repeated(),
)
.foldl(|base, (field, span)| {
let base = Box::new(base);
into_expr(ExprKind::Indirection { base, field }, span)
})
.boxed();

// Unary operators
let term = term
.clone()
Expand Down Expand Up @@ -385,10 +405,9 @@ where
}

pub fn ident() -> impl Parser<TokenKind, Ident, Error = PError> {
let star = ctrl('*').to("*".to_string());

ident_part()
.chain(ctrl('.').ignore_then(ident_part().or(star)).repeated())
.separated_by(ctrl('.'))
.at_least(1)
.map(Ident::from_path::<String>)
}

Expand Down
35 changes: 18 additions & 17 deletions prqlc/prqlc-parser/src/interpolation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,10 @@ fn parse_interpolate() {
Expr {
expr: Expr {
kind: Ident(
Ident {
path: [],
name: "a",
},
"a",
),
span: Some(
0:7-10,
0:8-9,
),
alias: None,
},
Expand Down Expand Up @@ -84,13 +81,10 @@ fn parse_interpolate() {
Expr {
expr: Expr {
kind: Ident(
Ident {
path: [],
name: "a",
},
"a",
),
span: Some(
0:13-16,
0:14-15,
),
alias: None,
},
Expand All @@ -105,21 +99,28 @@ fn parse_interpolate() {

fn parser(span_base: ParserSpan) -> impl Parser<char, Vec<InterpolateItem>, Error = Cheap<char>> {
let expr = ident_part()
.map_with_span(move |name, s| (name, offset_span(span_base, s)))
.separated_by(just('.'))
.at_least(1)
.map(|ident_parts| {
let mut parts = ident_parts.into_iter();

let (first, first_span) = parts.next().unwrap();
let mut base = Box::new(into_expr(ExprKind::Ident(first), first_span));

for (part, span) in parts {
let field = IndirectionKind::Name(part);
base = Box::new(into_expr(ExprKind::Indirection { base, field }, span));
}
base
})
.then(
just(':')
.ignore_then(filter(|c| *c != '}').repeated().collect::<String>())
.or_not(),
)
.delimited_by(just('{'), just('}'))
.map_with_span(move |(ident, format), s| {
let ident = ExprKind::Ident(Ident::from_path(ident));
let expr = into_expr(ident, offset_span(span_base, s));
let expr = Box::new(expr);

InterpolateItem::Expr { expr, format }
});
.map(|(expr, format)| InterpolateItem::Expr { expr, format });

// Convert double braces to single braces, and fail on any single braces.
let string = (just("{{").to('{'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,155 +10,123 @@ expression: "parse_single(r#\"\nfrom db.employees\nfilter country == \"USA\"
exprs:
- FuncCall:
name:
Ident:
- from
Ident: from
args:
- Ident:
- db
- employees
- Indirection:
base:
Ident: db
field:
Name: employees
- FuncCall:
name:
Ident:
- filter
Ident: filter
args:
- Binary:
left:
Ident:
- country
Ident: country
op: Eq
right:
Literal:
String: USA
- FuncCall:
name:
Ident:
- derive
Ident: derive
args:
- Tuple:
- Binary:
left:
Ident:
- salary
Ident: salary
op: Add
right:
Ident:
- payroll_tax
Ident: payroll_tax
alias: gross_salary
- Binary:
left:
Ident:
- gross_salary
Ident: gross_salary
op: Add
right:
Ident:
- benefits_cost
Ident: benefits_cost
alias: gross_cost
- FuncCall:
name:
Ident:
- filter
Ident: filter
args:
- Binary:
left:
Ident:
- gross_cost
Ident: gross_cost
op: Gt
right:
Literal:
Integer: 0
- FuncCall:
name:
Ident:
- group
Ident: group
args:
- Tuple:
- Ident:
- title
- Ident:
- country
- Ident: title
- Ident: country
- FuncCall:
name:
Ident:
- aggregate
Ident: aggregate
args:
- Tuple:
- FuncCall:
name:
Ident:
- average
Ident: average
args:
- Ident:
- salary
- Ident: salary
- FuncCall:
name:
Ident:
- average
Ident: average
args:
- Ident:
- gross_salary
- Ident: gross_salary
- FuncCall:
name:
Ident:
- sum
Ident: sum
args:
- Ident:
- salary
- Ident: salary
- FuncCall:
name:
Ident:
- sum
Ident: sum
args:
- Ident:
- gross_salary
- Ident: gross_salary
- FuncCall:
name:
Ident:
- average
Ident: average
args:
- Ident:
- gross_cost
- Ident: gross_cost
- FuncCall:
name:
Ident:
- sum
Ident: sum
args:
- Ident:
- gross_cost
- Ident: gross_cost
alias: sum_gross_cost
- FuncCall:
name:
Ident:
- count
Ident: count
args:
- Ident:
- salary
- Ident: salary
alias: ct
- FuncCall:
name:
Ident:
- sort
Ident: sort
args:
- Ident:
- sum_gross_cost
- Ident: sum_gross_cost
- FuncCall:
name:
Ident:
- filter
Ident: filter
args:
- Binary:
left:
Ident:
- ct
Ident: ct
op: Gt
right:
Literal:
Integer: 200
- FuncCall:
name:
Ident:
- take
Ident: take
args:
- Literal:
Integer: 20
span: "0:1-717"

17 changes: 14 additions & 3 deletions prqlc/prqlc-parser/src/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,20 @@ fn query_def() -> impl Parser<TokenKind, Stmt, Error = PError> {
// have this awkward construction in the meantime.
let other = args
.remove("target")
.map(|v| match v.kind {
ExprKind::Ident(value) => Ok(value.to_string()),
_ => Err("target must be a string literal".to_string()),
.map(|v| {
match v.kind {
ExprKind::Ident(name) => return Ok(name.to_string()),
ExprKind::Indirection {
base,
field: IndirectionKind::Name(field),
} => {
if let ExprKind::Ident(name) = base.kind {
return Ok(name.to_string() + "." + &field);
}
}
_ => {}
};
Err("target must be a string literal".to_string())
})
.transpose()
.map_err(|msg| Simple::custom(span, msg))?
Expand Down
Loading

0 comments on commit ee6b2ce

Please sign in to comment.