Skip to content

Commit

Permalink
feat: table declarations (#4126)
Browse files Browse the repository at this point in the history
  • Loading branch information
aljazerzen committed Jan 25, 2024
1 parent 359c573 commit 461741a
Show file tree
Hide file tree
Showing 21 changed files with 334 additions and 85 deletions.
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
cargo-release
pkg-config
openssl
cargo-llvm-cov

# actions
go-task
Expand Down
19 changes: 11 additions & 8 deletions prqlc/Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,23 @@ tasks:
- cmd: cargo clippy --all-targets {{.packages_core}}

test:
desc: A full test of prqlc
desc: |
A full test of prqlc (excluding --test-dbs-external).
Generates coverage report.
env:
# Use a different target dir so we don't poison the cache
CARGO_LLVM_COV_TARGET_DIR: ../target-cov
cmds:
- cmd:
cargo nextest run {{.packages_core}} {{.packages_addon}}
{{.packages_bindings}}
- cmd: |
cargo \
llvm-cov --lcov --output-path lcov.info \
nextest --features=test-dbs \
{{.packages_core}} {{.packages_addon}} {{.packages_bindings}}
- cmd:
cargo clippy --all-targets {{.packages_core}} {{.packages_addon}}
{{.packages_bindings}} -- -D warnings

- cmd:
cargo test --package prqlc --features=default,test-dbs
--test=integration -- queries::results

pull-request:
desc: Most checks that run within GH actions for a pull request
cmds:
Expand Down
27 changes: 22 additions & 5 deletions prqlc/prqlc-ast/src/expr/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ impl Ident {
}
}

pub fn len(&self) -> usize {
self.path.len() + 1
}

pub fn is_empty(&self) -> bool {
false
}

/// Remove last part of the ident.
/// Result will generally refer to the parent of this ident.
pub fn pop(self) -> Option<Self> {
Expand Down Expand Up @@ -58,7 +66,7 @@ impl Ident {
}

pub fn starts_with(&self, prefix: &Ident) -> bool {
if prefix.path.len() > self.path.len() {
if prefix.len() > self.len() {
return false;
}
prefix
Expand All @@ -67,10 +75,19 @@ impl Ident {
.all(|(prefix_component, self_component)| prefix_component == self_component)
}

pub fn starts_with_path<S: AsRef<str>>(&self, prefix: &[S]) -> bool {
// self is an I
if prefix.len() > self.len() {
return false;
}
prefix
.iter()
.zip(self.iter())
.all(|(prefix_component, self_component)| prefix_component.as_ref() == self_component)
}

pub fn starts_with_part(&self, prefix: &str) -> bool {
self.iter()
.next()
.map_or(false, |self_component| self_component == prefix)
self.starts_with_path(&[prefix])
}
}

Expand Down Expand Up @@ -108,7 +125,7 @@ impl Serialize for Ident {
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.path.len() + 1))?;
let mut seq = serializer.serialize_seq(Some(self.len()))?;
for part in &self.path {
seq.serialize_element(part)?;
}
Expand Down
2 changes: 1 addition & 1 deletion prqlc/prqlc-ast/src/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub enum StmtKind {
pub struct VarDef {
pub kind: VarDefKind,
pub name: String,
pub value: Box<Expr>,
pub value: Option<Box<Expr>>,

#[serde(skip_serializing_if = "Option::is_none")]
pub ty: Option<Ty>,
Expand Down
5 changes: 2 additions & 3 deletions prqlc/prqlc-parser/src/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ fn var_def() -> impl Parser<Token, StmtKind, Error = PError> {
let let_ = keyword("let")
.ignore_then(ident_part())
.then(type_expr().delimited_by(ctrl('<'), ctrl('>')).or_not())
.then_ignore(ctrl('='))
.then(expr_call().map(Box::new))
.then(ctrl('=').ignore_then(expr_call()).map(Box::new).or_not())
.map(|((name, ty), value)| {
StmtKind::VarDef(VarDef {
name,
Expand All @@ -133,7 +132,7 @@ fn var_def() -> impl Parser<Token, StmtKind, Error = PError> {
StmtKind::VarDef(VarDef {
name,
kind,
value,
value: Some(value),
ty: None,
})
})
Expand Down
2 changes: 1 addition & 1 deletion prqlc/prqlc-parser/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn parse_expr(source: &str) -> Result<Expr, Vec<Error>> {

let stmts = parse_single(&source)?;
let stmt = stmts.into_iter().exactly_one().unwrap();
Ok(*stmt.kind.into_var_def().unwrap().value)
Ok(*stmt.kind.into_var_def().unwrap().value.unwrap())
}

#[test]
Expand Down
11 changes: 6 additions & 5 deletions prqlc/prqlc/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use clap::{CommandFactory, Parser, Subcommand, ValueHint};
use clio::has_extension;
use clio::Output;
use itertools::Itertools;
use prqlc::semantic::NS_DEFAULT_DB;
use prqlc_ast::stmt::StmtKind;
use std::collections::HashMap;
use std::env;
Expand Down Expand Up @@ -361,7 +362,7 @@ impl Command {
if let StmtKind::VarDef(def) = stmt.kind {
res += &format!("## {}\n", def.name);

let val = semantic::eval(*def.value)
let val = semantic::eval(*def.value.unwrap())
.map_err(downcast)
.map_err(|e| e.composed(sources))?;
res += &semantic::write_pl(val);
Expand All @@ -376,7 +377,7 @@ impl Command {
semantic::load_std_lib(sources);

let ast = prql_to_pl_tree(sources)?;
let ir = pl_to_rq_tree(ast, &main_path)?;
let ir = pl_to_rq_tree(ast, &main_path, &[NS_DEFAULT_DB.to_string()])?;

match format {
Format::Json => serde_json::to_string_pretty(&ir)?.into_bytes(),
Expand All @@ -397,7 +398,7 @@ impl Command {
.with_format(*format);

prql_to_pl_tree(sources)
.and_then(|pl| pl_to_rq_tree(pl, &main_path))
.and_then(|pl| pl_to_rq_tree(pl, &main_path, &[NS_DEFAULT_DB.to_string()]))
.and_then(|rq| rq_to_sql(rq, &opts))
.map_err(|e| e.composed(sources))?
.as_bytes()
Expand All @@ -408,15 +409,15 @@ impl Command {
semantic::load_std_lib(sources);

let ast = prql_to_pl_tree(sources)?;
let rq = pl_to_rq_tree(ast, &main_path)?;
let rq = pl_to_rq_tree(ast, &main_path, &[NS_DEFAULT_DB.to_string()])?;
let srq = prqlc::sql::internal::preprocess(rq)?;
format!("{srq:#?}").as_bytes().to_vec()
}
Command::SQLAnchor { format, .. } => {
semantic::load_std_lib(sources);

let ast = prql_to_pl_tree(sources)?;
let rq = pl_to_rq_tree(ast, &main_path)?;
let rq = pl_to_rq_tree(ast, &main_path, &[NS_DEFAULT_DB.to_string()])?;
let srq = prqlc::sql::internal::anchor(rq)?;

let json = serde_json::to_string_pretty(&srq)?;
Expand Down
110 changes: 96 additions & 14 deletions prqlc/prqlc/src/codegen/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,28 +346,40 @@ impl WriteSource for Stmt {
r += "\n";
}
StmtKind::VarDef(var_def) => match var_def.kind {
VarDefKind::Let => {
_ if var_def.value.is_none() || var_def.ty.is_some() => {
let typ = if let Some(ty) = &var_def.ty {
format!("<{}> ", ty.write(opt.clone())?)
} else {
"".to_string()
};

r += opt.consume(&format!("let {} {}= ", var_def.name, typ))?;
r += opt.consume(&format!("let {} {}", var_def.name, typ))?;

if let Some(val) = &var_def.value {
r += opt.consume("= ")?;
r += &val.write(opt)?;
}
r += "\n";
}

VarDefKind::Let => {
r += opt.consume(&format!("let {} = ", var_def.name))?;

r += &var_def.value.write(opt)?;
r += &var_def.value.as_ref().unwrap().write(opt)?;
r += "\n";
}
VarDefKind::Into | VarDefKind::Main => {
match &var_def.value.kind {
let val = var_def.value.as_ref().unwrap();
match &val.kind {
ExprKind::Pipeline(pipeline) => {
for expr in &pipeline.exprs {
r += &expr.write(opt.clone())?;
r += "\n";
}
}
_ => {
r += &var_def.value.write(opt)?;
r += &val.write(opt)?;
r += "\n";
}
}

Expand Down Expand Up @@ -433,14 +445,13 @@ impl WriteSource for SwitchCase {
#[cfg(test)]
mod test {
use insta::assert_snapshot;
use similar_asserts::assert_eq;

use super::*;

#[track_caller]
fn assert_is_formatted(input: &str) {
let stmt = format_single_stmt(input);

assert_eq!(input.trim(), stmt.trim());
let formatted = format_single_stmt(input);
similar_asserts::assert_eq!(input.trim(), formatted.trim());
}

fn format_single_stmt(query: &str) -> String {
Expand Down Expand Up @@ -511,7 +522,20 @@ mod test {
assert_is_formatted(r#"sort {-duration}"#);

assert_is_formatted(r#"select a = -b"#);
assert_is_formatted(r#"join `project-bar.dataset.table` (==col_bax)"#)
assert_is_formatted(r#"join `project-bar.dataset.table` (==col_bax)"#);
}

#[test]
fn test_binary() {
assert_is_formatted(r#"let a = 5 * (4 + 3) ?? (5 / 2) // 2 == 1 and true"#);

// TODO: associativity is not handled correctly
// assert_is_formatted(r#"let a = 5 / 2 / 2"#);
}

#[test]
fn test_func() {
assert_is_formatted(r#"let a = func x y:false -> x and y"#);
}

#[test]
Expand Down Expand Up @@ -543,15 +567,73 @@ group {title, country} (aggregate {
fn test_range() {
assert_is_formatted(
r#"
from foo
is_negative = -100..0
let negative = -100..0
"#,
);

assert_is_formatted(
r#"
let negative = -(100..0)
"#,
);

assert_is_formatted(
r#"
let negative = -100..
"#,
);

assert_is_formatted(
r#"
let negative = ..-100
"#,
);
}

#[test]
fn test_annotation() {
assert_is_formatted(
r#"
@deprecated
module hello {
}
"#,
);
}

#[test]
fn test_var_def() {
assert_is_formatted(
r#"
let a
"#,
);

assert_is_formatted(
r#"
from foo
is_negative = -(100..0)
let a <int>
"#,
);

assert_is_formatted(
r#"
let a = 5
"#,
);

assert_is_formatted(
r#"
5
into a
"#,
);
}

#[test]
fn test_query_def() {
assert_is_formatted(
r#"
prql version:"^0.9" target:sql.sqlite
"#,
);
}
Expand Down
2 changes: 1 addition & 1 deletion prqlc/prqlc/src/ir/pl/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ fn fold_module_def<F: ?Sized + PlFold>(fold: &mut F, module_def: ModuleDef) -> R
pub fn fold_var_def<F: ?Sized + PlFold>(fold: &mut F, var_def: VarDef) -> Result<VarDef> {
Ok(VarDef {
name: var_def.name,
value: Box::new(fold.fold_expr(*var_def.value)?),
value: fold_optional_box(fold, var_def.value)?,
ty: var_def.ty.map(|x| fold.fold_type(x)).transpose()?,
})
}
Expand Down
2 changes: 1 addition & 1 deletion prqlc/prqlc/src/ir/pl/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub enum StmtKind {
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct VarDef {
pub name: String,
pub value: Box<Expr>,
pub value: Option<Box<Expr>>,

#[serde(skip_serializing_if = "Option::is_none")]
pub ty: Option<Ty>,
Expand Down
Loading

0 comments on commit 461741a

Please sign in to comment.