Skip to content

Commit 4959834

Browse files
authored
feat: generic type arguments (#4122)
1 parent e5dcb79 commit 4959834

21 files changed

Lines changed: 256 additions & 51 deletions

File tree

prqlc/prqlc-ast/src/expr.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ pub struct Func {
102102

103103
/// Named function parameters.
104104
pub named_params: Vec<FuncParam>,
105+
106+
/// Generic type arguments within this function.
107+
pub generic_type_params: Vec<GenericTypeParam>,
105108
}
106109

107110
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
@@ -114,6 +117,17 @@ pub struct FuncParam {
114117
pub default_value: Option<Box<Expr>>,
115118
}
116119

120+
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
121+
pub struct GenericTypeParam {
122+
/// Assigned name of this generic type argument.
123+
pub name: String,
124+
125+
/// Possible values of this type argument.
126+
/// For a given instance of this function, the argument must be
127+
/// exactly one of types in the domain.
128+
pub domain: Vec<Ty>,
129+
}
130+
117131
/// A value and a series of functions that are to be applied to that value one after another.
118132
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
119133
pub struct Pipeline {

prqlc/prqlc-ast/src/types.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ pub enum TyKind {
4545

4646
/// Type that is the largest subtype of `base` while not a subtype of `exclude`.
4747
Difference { base: Box<Ty>, exclude: Box<Ty> },
48+
49+
/// A generic argument. Contains id of the function call node and generic type param name.
50+
GenericArg((usize, String)),
4851
}
4952

5053
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, EnumAsInner)]

prqlc/prqlc-parser/src/expr.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,24 +333,34 @@ where
333333
.map(ExprKind::Internal)
334334
.map_with_span(into_expr);
335335

336+
let generic_args = ident_part()
337+
.then_ignore(ctrl(':'))
338+
.then(type_expr().separated_by(ctrl('|')))
339+
.map(|(name, domain)| GenericTypeParam { name, domain })
340+
.separated_by(ctrl(','))
341+
.at_least(1)
342+
.delimited_by(ctrl('<'), ctrl('>'))
343+
.or_not()
344+
.map(|x| x.unwrap_or_default());
345+
336346
choice((
337347
// func
338-
keyword("func").ignore_then(
348+
keyword("func").ignore_then(generic_args).then(
339349
param
340350
.clone()
341351
.separated_by(new_line().repeated())
342352
.allow_leading()
343353
.allow_trailing(),
344354
),
345355
// plain
346-
param.repeated(),
356+
param.repeated().map(|params| (Vec::new(), params)),
347357
))
348358
.then_ignore(just(Token::ArrowThin))
349359
// return type
350360
.then(type_expr().delimited_by(ctrl('<'), ctrl('>')).or_not())
351361
// body
352362
.then(choice((internal, func_call(expr))))
353-
.map(|((params, return_ty), body)| {
363+
.map(|(((generic_type_params, params), return_ty), body)| {
354364
let (pos, name) = params
355365
.into_iter()
356366
.map(|((name, ty), default_value)| FuncParam {
@@ -366,6 +376,7 @@ where
366376

367377
body: Box::new(body),
368378
return_ty,
379+
generic_type_params,
369380
})
370381
})
371382
.map(ExprKind::Func)

prqlc/prqlc-parser/src/test.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,7 @@ fn test_function() {
10651065
- name: x
10661066
default_value: ~
10671067
named_params: []
1068+
generic_type_params: []
10681069
span: "0:0-26"
10691070
"###);
10701071
assert_yaml_snapshot!(parse_single("let identity = x -> x\n").unwrap()
@@ -1083,6 +1084,7 @@ fn test_function() {
10831084
- name: x
10841085
default_value: ~
10851086
named_params: []
1087+
generic_type_params: []
10861088
span: "0:0-22"
10871089
"###);
10881090
assert_yaml_snapshot!(parse_single("let plus_one = x -> (x + 1)\n").unwrap()
@@ -1107,6 +1109,7 @@ fn test_function() {
11071109
- name: x
11081110
default_value: ~
11091111
named_params: []
1112+
generic_type_params: []
11101113
span: "0:0-28"
11111114
"###);
11121115
assert_yaml_snapshot!(parse_single("let plus_one = x -> x + 1\n").unwrap()
@@ -1131,6 +1134,7 @@ fn test_function() {
11311134
- name: x
11321135
default_value: ~
11331136
named_params: []
1137+
generic_type_params: []
11341138
span: "0:0-26"
11351139
"###);
11361140

@@ -1174,6 +1178,7 @@ fn test_function() {
11741178
- name: x
11751179
default_value: ~
11761180
named_params: []
1181+
generic_type_params: []
11771182
span: "0:0-51"
11781183
"###);
11791184

@@ -1192,6 +1197,7 @@ fn test_function() {
11921197
- name: return_constant
11931198
default_value: ~
11941199
named_params: []
1200+
generic_type_params: []
11951201
span: "0:0-28"
11961202
"###);
11971203

@@ -1217,6 +1223,7 @@ fn test_function() {
12171223
- name: X
12181224
default_value: ~
12191225
named_params: []
1226+
generic_type_params: []
12201227
span: "0:0-28"
12211228
"###);
12221229

@@ -1273,6 +1280,7 @@ fn test_function() {
12731280
- name: x
12741281
default_value: ~
12751282
named_params: []
1283+
generic_type_params: []
12761284
span: "0:13-147"
12771285
"###);
12781286

@@ -1301,6 +1309,7 @@ fn test_function() {
13011309
default_value:
13021310
Ident:
13031311
- a
1312+
generic_type_params: []
13041313
span: "0:0-27"
13051314
"###);
13061315
}
@@ -1686,6 +1695,7 @@ fn test_inline_pipeline() {
16861695
- name: x
16871696
default_value: ~
16881697
named_params: []
1698+
generic_type_params: []
16891699
span: "0:0-37"
16901700
"###);
16911701
}
@@ -2417,6 +2427,7 @@ fn test_annotation() {
24172427
- name: b
24182428
default_value: ~
24192429
named_params: []
2430+
generic_type_params: []
24202431
span: "0:9-61"
24212432
annotations:
24222433
- expr:

prqlc/prqlc/src/codegen/ast.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,23 @@ impl WriteSource for ExprKind {
137137
}
138138
Func(c) => {
139139
let mut r = "func ".to_string();
140+
if !c.generic_type_params.is_empty() {
141+
r += opt.consume("<")?;
142+
for generic_param in &c.generic_type_params {
143+
r += opt.consume(&write_ident_part(&generic_param.name))?;
144+
r += opt.consume(": ")?;
145+
r += &opt.consume(
146+
SeparatedExprs {
147+
exprs: &generic_param.domain,
148+
inline: " | ",
149+
line_end: "|",
150+
}
151+
.write(opt.clone())?,
152+
)?;
153+
}
154+
r += opt.consume("> ")?;
155+
}
156+
140157
for param in &c.params {
141158
r += opt.consume(&write_ident_part(&param.name))?;
142159
r += opt.consume(" ")?;

prqlc/prqlc/src/codegen/types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ impl WriteSource for TyKind {
8383
let exclude = exclude.write(opt.clone())?;
8484
Some(format!("{base} - {exclude}"))
8585
}
86+
GenericArg(_) => Some("?".to_string()),
8687
}
8788
}
8889
}

prqlc/prqlc/src/ir/pl/expr.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use enum_as_inner::EnumAsInner;
55
use serde::{Deserialize, Serialize};
66

77
use prqlc_ast::expr::generic;
8-
use prqlc_ast::{Ident, Literal, Span, Ty};
8+
use prqlc_ast::{GenericTypeParam, Ident, Literal, Span, Ty};
99

1010
use crate::codegen::write_ty;
1111

@@ -114,6 +114,9 @@ pub struct Func {
114114
/// Named function parameters.
115115
pub named_params: Vec<FuncParam>,
116116

117+
/// Generic type arguments within this function.
118+
pub generic_type_params: Vec<GenericTypeParam>,
119+
117120
/// Arguments that have already been provided.
118121
pub args: Vec<Expr>,
119122

prqlc/prqlc/src/ir/pl/fold.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,11 @@ pub fn fold_type<T: ?Sized + PlFold>(fold: &mut T, ty: Ty) -> Result<Ty> {
350350
base: Box::new(fold.fold_type(*base)?),
351351
exclude: Box::new(fold.fold_type(*exclude)?),
352352
},
353-
TyKind::Any | TyKind::Ident(_) | TyKind::Primitive(_) | TyKind::Singleton(_) => ty.kind,
353+
TyKind::Any
354+
| TyKind::Ident(_)
355+
| TyKind::Primitive(_)
356+
| TyKind::Singleton(_)
357+
| TyKind::GenericArg(_) => ty.kind,
354358
},
355359
span: ty.span,
356360
name: ty.name,

prqlc/prqlc/src/semantic/ast_expand.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub fn expand_expr(expr: Expr) -> Result<pl::Expr> {
4444
name_hint: None,
4545
args: Vec::new(),
4646
env: HashMap::new(),
47+
generic_type_params: v.generic_type_params,
4748
}
4849
.into(),
4950
),
@@ -295,6 +296,7 @@ fn restrict_expr_kind(value: pl::ExprKind) -> ExprKind {
295296
body: restrict_expr_box(v.body),
296297
params: restrict_func_params(v.params),
297298
named_params: restrict_func_params(v.named_params),
299+
generic_type_params: v.generic_type_params,
298300
}
299301
.into(),
300302
);

prqlc/prqlc/src/semantic/eval.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ fn new_func(name: &str, params: &[&str]) -> Expr {
425425
named_params: Default::default(),
426426
args: Default::default(),
427427
env: Default::default(),
428+
generic_type_params: Default::default(),
428429
}));
429430
Expr {
430431
alias: Some(name.to_string()),

0 commit comments

Comments
 (0)