Declare pattern transform functions in KCL (#7057)

* Declare pattern transform using KCL

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Boolean function param defaults

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Parse empty record types in fn types

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-05-20 08:25:29 +12:00
committed by GitHub
parent b1d1d89ca5
commit cf303ebe97
25 changed files with 383 additions and 16487 deletions

View File

@ -436,7 +436,7 @@ fn pipe_expression(i: &mut TokenSlice) -> PResult<Node<PipeExpression>> {
))
}
fn bool_value(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> {
fn bool_value(i: &mut TokenSlice) -> PResult<Node<Literal>> {
let (value, token) = any
.try_map(|token: Token| match token.token_type {
TokenType::Keyword if token.value == "true" => Ok((true, token)),
@ -448,7 +448,7 @@ fn bool_value(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> {
})
.context(expected("a boolean literal (either true or false)"))
.parse_next(i)?;
Ok(Box::new(Node::new(
Ok(Node::new(
Literal {
value: LiteralValue::Bool(value),
raw: value.to_string(),
@ -457,11 +457,11 @@ fn bool_value(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> {
token.start,
token.end,
token.module_id,
)))
))
}
fn literal(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> {
alt((string_literal, unsigned_number_literal))
alt((string_literal, unsigned_number_literal, bool_value))
.map(Box::new)
.context(expected("a KCL literal, like 'myPart' or 3"))
.parse_next(i)
@ -2051,7 +2051,7 @@ fn unnecessarily_bracketed(i: &mut TokenSlice) -> PResult<Expr> {
fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> {
alt((
member_expression.map(Box::new).map(Expr::MemberExpression),
bool_value.map(Expr::Literal),
bool_value.map(Box::new).map(Expr::Literal),
tag.map(Box::new).map(Expr::TagDeclarator),
literal.map(Expr::Literal),
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),
@ -2070,7 +2070,7 @@ fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> {
fn possible_operands(i: &mut TokenSlice) -> PResult<Expr> {
let mut expr = alt((
unary_expression.map(Box::new).map(Expr::UnaryExpression),
bool_value.map(Expr::Literal),
bool_value.map(Box::new).map(Expr::Literal),
member_expression.map(Box::new).map(Expr::MemberExpression),
literal.map(Expr::Literal),
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),
@ -2780,27 +2780,31 @@ fn labeled_argument(i: &mut TokenSlice) -> PResult<LabeledArg> {
.parse_next(i)
}
fn record_ty_field(i: &mut TokenSlice) -> PResult<(Node<Identifier>, Node<Type>)> {
(identifier, colon, opt(whitespace), type_)
.map(|(id, _, _, ty)| (id, ty))
.parse_next(i)
}
/// Parse a type in various positions.
fn type_(i: &mut TokenSlice) -> PResult<Node<Type>> {
let type_ = alt((
// Object types
// TODO it is buggy to treat object fields like parameters since the parameters parser assumes a terminating `)`.
(open_brace, parameters, close_brace).try_map(|(open, params, close)| {
for p in &params {
if p.type_.is_none() {
return Err(CompilationError::fatal(
p.identifier.as_source_range(),
"Missing type for field in record type",
));
}
}
Ok(Node::new(
Type::Object { properties: params },
open.start,
close.end,
open.module_id,
))
}),
(
open_brace,
opt(whitespace),
separated(0.., record_ty_field, comma_sep),
opt(whitespace),
close_brace,
)
.try_map(|(open, _, params, _, close)| {
Ok(Node::new(
Type::Object { properties: params },
open.start,
close.end,
open.module_id,
))
}),
// Array types
array_type,
// Primitive or union types
@ -4866,6 +4870,15 @@ let myBox = box(p=[0,0], h=-3, l=-16, w=-10)
|> line(%, tag = $var01)"#;
assert_no_err(some_program_string);
}
#[test]
fn test_parse_param_bool_default() {
let some_program_string = r#"fn patternTransform(
use_original?: boolean = false,
) {}"#;
assert_no_err(some_program_string);
}
#[test]
fn parse_function_types() {
let code = r#"foo = x: fn
@ -4875,6 +4888,8 @@ fn foo(x: fn(a, b: number(mm), c: d): number(Angle)): fn { return 0 }
type fn
type foo = fn
type foo = fn(a: string, b: { f: fn(): any })
type foo = fn(a: string, b: {})
type foo = fn(a: string, b: { })
type foo = fn([fn])
type foo = fn(fn, f: fn(number(_))): [fn([any]): string]
"#;