aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2025-07-06 03:32:07 +0200
committerMel <mel@rnrd.eu>2025-07-06 03:32:07 +0200
commitebd176b8e7eb14060375a28d6ac50500d9d2c808 (patch)
tree6c79ac631b5b8a4c025c09940a4a0fb1c71dd129
parent620d82a5e314a82784e02b4af387a67d53242149 (diff)
downloadcatskill-ebd176b8e7eb14060375a28d6ac50500d9d2c808.tar.zst
catskill-ebd176b8e7eb14060375a28d6ac50500d9d2c808.zip
Parse variadic parameters in function definitions
Signed-off-by: Mel <mel@rnrd.eu>
-rw-r--r--boot/lex.c9
-rw-r--r--boot/parse.c9
-rw-r--r--boot/tests/parse/variadic_parameters.cskt13
-rw-r--r--boot/tree.c3
-rw-r--r--boot/visit.c5
5 files changed, 36 insertions, 3 deletions
diff --git a/boot/lex.c b/boot/lex.c
index f450798..82d3567 100644
--- a/boot/lex.c
+++ b/boot/lex.c
@@ -123,6 +123,7 @@ enum Token_Kind
TOKEN_AMPERSAND,
TOKEN_DOT,
TOKEN_DOT_DOT,
+ TOKEN_DOT_DOT_DOT,
TOKEN_BANG,
TOKEN_QUESTION,
TOKEN_PERCENT,
@@ -244,6 +245,8 @@ token_kind_to_string(enum Token_Kind kind)
return "DOT";
case TOKEN_DOT_DOT:
return "DOT_DOT";
+ case TOKEN_DOT_DOT_DOT:
+ return "DOT_DOT_DOT";
case TOKEN_BANG:
return "BANG";
case TOKEN_QUESTION:
@@ -618,10 +621,12 @@ lexer_symbol_token(struct Lexer* l, struct Lexer_Char current)
if (a.got_match) RET{ TOKEN_ASSIGN_AMPERSAND, 2 };
RET{ TOKEN_AMPERSAND, 1 };
}
- case '.':
- a = lexer_match_char(l, '.');
+ case '.': {
+ lexer_match_chars(l, '.', '.', &a, &b);
+ if (a.got_match && b.got_match) RET{ TOKEN_DOT_DOT_DOT, 3 };
if (a.got_match) RET{ TOKEN_DOT_DOT, 2 };
RET{ TOKEN_DOT, 1 };
+ }
case '!': {
a = lexer_match_char(l, '=');
if (a.got_match) RET{ TOKEN_NOT_EQUAL, 2 };
diff --git a/boot/parse.c b/boot/parse.c
index 5d839a8..cd5f019 100644
--- a/boot/parse.c
+++ b/boot/parse.c
@@ -254,6 +254,13 @@ parser_function_header_node(struct Parser* p, struct Parser_Error* error)
struct Function_Header_Node header = { 0 };
while (!parser_probe(p, TOKEN_ROUND_CLOSE)) {
+ // TODO: correctly output parameter spans
+ bool variadic = false;
+ if (parser_probe(p, TOKEN_DOT_DOT_DOT)) {
+ variadic = true;
+ parser_next(p);
+ }
+
struct Token name_token =
CHECK_RETURN(parser_need(p, TOKEN_NAME, error), struct Function_Header_Node);
struct String name = name_token.value.name;
@@ -266,6 +273,8 @@ parser_function_header_node(struct Parser* p, struct Parser_Error* error)
}
type->value_name = name;
+ type->variadic = variadic;
+
if (!header.parameters_type_and_name)
header.parameters_type_and_name = type;
else
diff --git a/boot/tests/parse/variadic_parameters.cskt b/boot/tests/parse/variadic_parameters.cskt
new file mode 100644
index 0000000..3b718a1
--- /dev/null
+++ b/boot/tests/parse/variadic_parameters.cskt
@@ -0,0 +1,13 @@
+correct parsing of variadic parameters in function
+definitions
+
+<<<
+
+printf = fun (format string, ...args any) {
+ # some implementation here
+}
+
+>>>
+
+(expr (binary = (expr (name printf)) (expr (function (param format) (type name string) (param args) (type variadic name any) (block
+))))) \ No newline at end of file
diff --git a/boot/tree.c b/boot/tree.c
index 3c808f8..9ea9e6f 100644
--- a/boot/tree.c
+++ b/boot/tree.c
@@ -530,6 +530,9 @@ struct Type_Node
// it is assigned to, e.g. `x int`, `string y`, etc.
// this is the name of that binding, for ease of listing.
struct String value_name;
+ // true if the type is used as a variadic parameter.
+ // e.g. `fun (format string, ...args string)`.
+ bool variadic;
// if type is within a group of multiple types,
// points to the next type within the group.
diff --git a/boot/visit.c b/boot/visit.c
index c1dd797..781c227 100644
--- a/boot/visit.c
+++ b/boot/visit.c
@@ -864,6 +864,8 @@ printer_visit_type_node(struct Visit* visit, struct Type_Node* node)
{
TREE_PRINTER_PREAMBLE
PRINT("(type ");
+ if (node->variadic)
+ PRINT("variadic ");
walk_type_node(visit, node);
PRINT(")");
}
@@ -1020,7 +1022,8 @@ printer_visit_function_header(struct Visit* visit, struct Function_Header_Node*
TREE_PRINTER_PREAMBLE
FOR_EACH (struct Type_Node*, current, header->parameters_type_and_name) {
- if (current != header->parameters_type_and_name) PRINT(" ");
+ // if (current != header->parameters_type_and_name) PRINT(" ");
+ PRINT_WHITESPACE_IF_NEEDED(" ");
if (current->value_name.data && current->value_name.data[0] != '\0')
PRINT("(param %s) ", current->value_name.data);