Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Change annotation syntax to use @ #2729

Merged
merged 4 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions prql-compiler/src/ast/pl/stmt.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{collections::HashMap, fmt::Display};

use anyhow::anyhow;
use anyhow::{anyhow, bail};
use enum_as_inner::EnumAsInner;
use semver::VersionReq;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -63,12 +63,24 @@ pub struct ModuleDef {

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Annotation {
pub name: Ident,
pub args: Vec<Expr>,

pub expr: Expr,
pub span: Option<Span>,
}

impl Annotation {
/// Find the items in a `@{a=b}`. We're only using annotations with tuples;
/// we can consider formalizing this constraint.
pub fn tuple_items(self) -> anyhow::Result<Vec<(String, ExprKind)>> {
match self.expr.kind {
ExprKind::Tuple(items) => items
.into_iter()
.map(|item| Ok((item.alias.clone().unwrap(), item.kind)))
.collect(),
_ => bail!("Annotation must be a tuple"),
}
}
}

impl From<StmtKind> for anyhow::Error {
// https://github.com/bluejekyll/enum-as-inner/issues/84
#[allow(unreachable_code)]
Expand Down
19 changes: 10 additions & 9 deletions prql-compiler/src/parser/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub enum Token {
Or, // ||
Coalesce, // ??
DivInt, // //
Annotate, // #[
Annotate, // @
}

pub fn lexer() -> impl Parser<char, Vec<(Token, std::ops::Range<usize>)>, Error = Cheap<char>> {
Expand All @@ -50,7 +50,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, std::ops::Range<usize>)>, Error
just("||").then_ignore(end_expr()).to(Token::Or),
just("??").to(Token::Coalesce),
just("//").to(Token::DivInt),
just("#[").to(Token::Annotate),
just("@").then(digits(1).not().rewind()).to(Token::Annotate),
));

let control = one_of("></%=+-*[]().,:|!{}").map(Token::Control);
Expand Down Expand Up @@ -94,9 +94,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, std::ops::Range<usize>)>, Error
))
.recover_with(skip_then_retry_until([]).skip_start());

let comment = just('#')
.then(just('[').not().rewind())
.then(none_of('\n').repeated());
let comment = just('#').then(none_of('\n').repeated());
let comments = comment
.separated_by(new_line.then(whitespace.clone().or_not()))
.at_least(1)
Expand Down Expand Up @@ -258,19 +256,22 @@ fn literal() -> impl Parser<char, Literal, Error = Cheap<char>> {
)
.boxed();

let date = just('@')
// Not an annotation
let dt_prefix = just('@').then(just('{').not().rewind());

let date = dt_prefix
.ignore_then(date_inner.clone())
.then_ignore(end_expr())
.collect::<String>()
.map(Literal::Date);

let time = just('@')
let time = dt_prefix
.ignore_then(time_inner.clone())
.then_ignore(end_expr())
.collect::<String>()
.map(Literal::Time);

let datetime = just('@')
let datetime = dt_prefix
.ignore_then(date_inner)
.chain(just('T'))
.chain::<char, _, _>(time_inner)
Expand Down Expand Up @@ -410,7 +411,7 @@ impl std::fmt::Display for Token {
Self::Or => f.write_str("||"),
Self::Coalesce => f.write_str("??"),
Self::DivInt => f.write_str("//"),
Self::Annotate => f.write_str("#["),
Self::Annotate => f.write_str("@{"),

Self::Param(id) => write!(f, "${id}"),

Expand Down
42 changes: 42 additions & 0 deletions prql-compiler/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2669,4 +2669,46 @@ join s=salaries (==id)
annotations: []
"###);
}

#[test]
fn test_annotation() {
assert_yaml_snapshot!(parse_single(r#"
@{binding_strength=1}
let add = a b -> a + b
"#).unwrap(), @r###"
---
- name: add
VarDef:
value:
Func:
name_hint: ~
return_ty: ~
body:
Binary:
left:
Ident:
- a
op: Add
right:
Ident:
- b
params:
- name: a
default_value: ~
- name: b
default_value: ~
named_params: []
args: []
env: {}
ty_expr: ~
kind: Let
annotations:
- expr:
Tuple:
- Literal:
Integer: 1
alias: binding_strength
span: "0:7-29"
"###);
}
}
8 changes: 4 additions & 4 deletions prql-compiler/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@ fn query_def() -> impl Parser<Token, Stmt, Error = PError> {

fn var_def() -> impl Parser<Token, (Vec<Annotation>, (String, StmtKind)), Error = PError> {
let annotation = just(Token::Annotate)
.ignore_then(ident().then(expr().repeated()))
.then_ignore(ctrl(']').then(new_line()))
.map_with_span(|(name, args), span| {
.ignore_then(expr())
.then_ignore(new_line())
.map_with_span(|expr, span| {
let span = Some(span);
Annotation { name, args, span }
Annotation { expr, span }
});

let let_ = keyword("let")
Expand Down
16 changes: 10 additions & 6 deletions prql-compiler/src/sql/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::iter::zip;
use std::path::PathBuf;

use anyhow::Result;
use itertools::Itertools;
use once_cell::sync::Lazy;

use super::gen_expr::{translate_operand, ExprOrSource};
Expand Down Expand Up @@ -126,13 +127,16 @@ fn find_operator_impl(operator_name: &str, dialect: Dialect) -> Option<(&pl::Fun
let func_def = func_def.kind.as_func().unwrap();

let binding_strength = decl
.clone()
.annotations
.iter()
.find(|x| &x.name.name == "binding_strength")
.and_then(|ann| ann.args.get(0))
.and_then(|bs| bs.kind.as_literal())
.and_then(|bs| bs.as_integer())
.map(|x| *x as i32);
.into_iter()
.exactly_one()
.ok()
.and_then(|x| x.tuple_items().ok())
.and_then(|items| items.into_iter().find(|bs| bs.0 == "binding_strength"))
.and_then(|tuple| tuple.1.into_literal().ok())
.and_then(|literal| literal.into_integer().ok())
.map(|int| int as i32);

Some((func_def.as_ref(), binding_strength))
}
32 changes: 16 additions & 16 deletions prql-compiler/src/sql/std.sql.prql
Original file line number Diff line number Diff line change
Expand Up @@ -45,56 +45,56 @@ let upper = column -> s"UPPER({column:0})"
let read_parquet = source -> s"read_parquet({source:0})"
let read_csv = source -> s"read_csv_auto({source:0})"

#[binding_strength 11]
@{binding_strength=11}
let mul = l r -> null

#[binding_strength 100]
@{binding_strength=100}
let div_i = l r -> s"FLOOR(ABS({l:11} / {r:11})) * SIGN({l:0}) * SIGN({r:0})"

#[binding_strength 11]
@{binding_strength=11}
let div_f = l r -> s"({l} * 1.0 / {r})"

#[binding_strength 11]
@{binding_strength=11}
let mod = l r -> s"{l} % {r}"

#[binding_strength 10]
@{binding_strength=10}
let add = l r -> null

#[binding_strength 10]
@{binding_strength=10}
let sub = l r -> null

#[binding_strength 6]
@{binding_strength=6}
let eq = l r -> null

#[binding_strength 6]
@{binding_strength=6}
let ne = l r -> null

#[binding_strength 6]
@{binding_strength=6}
let gt = l r -> null

#[binding_strength 6]
@{binding_strength=6}
let lt = l r -> null

#[binding_strength 6]
@{binding_strength=6}
let gte = l r -> null

#[binding_strength 6]
@{binding_strength=6}
let lte = l r -> null

#[binding_strength 3]
@{binding_strength=3}
let and = l r -> null

#[binding_strength 2]
@{binding_strength=2}
let or = l r -> null

let coalesce = l r -> s"COALESCE({l:0}, {r:0})"

let regex_search = text pattern -> s"REGEXP({text:0}, {pattern:0})"

#[binding_strength 13]
@{binding_strength=13}
let neg = l -> s"-{l}"

#[binding_strength 4]
@{binding_strength=4}
let not = l -> s"NOT {l}"

module postgres {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ SELECT
FROM
tracks
WHERE
name ~ '\(I Can''t Help\) Falling'
(name) ~ ('\(I Can''t Help\) Falling')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes are intentional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I saw these too — I'm not sure why they happen...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry!

(I blame the comment syntax :D )


Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ SELECT
FROM
tracks
WHERE
name REGEXP 'With You'
(name) REGEXP ('With You')

Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ SELECT
FROM
tracks
WHERE
name REGEXP 'But Why Isn''t Your Syntax More Similar\?'
(name) REGEXP ('But Why Isn''t Your Syntax More Similar\?')