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

feat: table declarations #4126

Merged
merged 8 commits into from
Jan 25, 2024
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
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]
aljazerzen marked this conversation as resolved.
Show resolved Hide resolved
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