aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2025-06-12 17:14:12 +0200
committerMel <mel@rnrd.eu>2025-06-12 17:14:12 +0200
commit9676d1ca61b025af9e52506f8aec1f9c0792f51c (patch)
tree1992a522d86019e87363c2b0de2ce37368d27789
parent8b655fe0f1d6589c9216a6244c067aec1d866be7 (diff)
downloadcatskill-9676d1ca61b025af9e52506f8aec1f9c0792f51c.tar.zst
catskill-9676d1ca61b025af9e52506f8aec1f9c0792f51c.zip
Parse most primitive types into tree
Signed-off-by: Mel <mel@rnrd.eu>
-rw-r--r--.gitignore3
-rw-r--r--boot/common.c6
-rw-r--r--boot/lex.c36
-rw-r--r--boot/parse.c309
-rw-r--r--boot/tree.c296
5 files changed, 583 insertions, 67 deletions
diff --git a/.gitignore b/.gitignore
index d52979a..80e8eae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
build/
.direnv/
.idea
-.vscode \ No newline at end of file
+.vscode
+input.csk \ No newline at end of file
diff --git a/boot/common.c b/boot/common.c
index 65dc780..6851312 100644
--- a/boot/common.c
+++ b/boot/common.c
@@ -121,6 +121,12 @@ string_empty()
};
}
+bool
+string_is_empty(struct String s)
+{
+ return s.data == nil || s.length == 0;
+}
+
// allocates a new string in the global string region,
// taking the data from a null-terminated C string.
struct String
diff --git a/boot/lex.c b/boot/lex.c
index 6b6badc..52760e9 100644
--- a/boot/lex.c
+++ b/boot/lex.c
@@ -98,6 +98,8 @@ enum Token_Kind
TOKEN_WORD_RETURN,
TOKEN_WORD_VAR,
TOKEN_WORD_TYPE,
+ TOKEN_WORD_VARIANT,
+ TOKEN_WORD_CLASS,
TOKEN_WORD_TRUE,
TOKEN_WORD_FALSE,
@@ -200,6 +202,10 @@ token_kind_to_string(enum Token_Kind kind)
return "WORD_VAR";
case TOKEN_WORD_TYPE:
return "WORD_TYPE";
+ case TOKEN_WORD_VARIANT:
+ return "WORD_VARIANT";
+ case TOKEN_WORD_CLASS:
+ return "WORD_CLASS";
case TOKEN_WORD_TRUE:
return "WORD_TRUE";
case TOKEN_WORD_FALSE:
@@ -375,6 +381,32 @@ token_ends_statement(const struct Token* t)
}
bool
+token_can_begin_type(const struct Token* t)
+{
+ switch (t->kind) {
+ // named type reference
+ case TOKEN_NAME:
+
+ // keyword that introduces a type
+ case TOKEN_WORD_TYPE:
+ case TOKEN_WORD_VARIANT:
+ case TOKEN_WORD_CLASS:
+ case TOKEN_WORD_FUN:
+
+ // arrays, tuples, maps and inline structures
+ case TOKEN_CURLY_OPEN:
+ case TOKEN_SQUARE_OPEN:
+ case TOKEN_ROUND_OPEN:
+
+ // references
+ case TOKEN_AMPERSAND:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool
ascii_in_range(ascii c, ascii from, ascii to)
{
return c >= from && c <= to;
@@ -763,6 +795,10 @@ lexer_word_from_name(struct Lexer* l, struct String word_or_name)
return TOKEN_WORD_VAR;
case 91700392: // "type"
return TOKEN_WORD_TYPE;
+ case 3267162257: // "variant"
+ return TOKEN_WORD_VARIANT;
+ case 2938290531: // "class"
+ return TOKEN_WORD_CLASS;
case 2588936279: // "true"
return TOKEN_WORD_TRUE;
case 1548710142: // "false"
diff --git a/boot/parse.c b/boot/parse.c
index 94b1e91..a0dd12c 100644
--- a/boot/parse.c
+++ b/boot/parse.c
@@ -166,6 +166,19 @@ parser_probe(struct Parser* p, enum Token_Kind kind)
return token_is(&token, kind);
}
+// skip all consecutive newlines and other non-interrupting
+// tokens in the token stream.
+// necessary for parsing statements and expressions that are
+// split-able over multiple lines.
+// TODO: this needs to be used in many more places and
+// is currently mostly redundant, due to only skipping newlines,
+// acting mostly as a marking for `the tokens don't have to follow precisely`.
+void
+parser_unglue(struct Parser* p)
+{
+ while (parser_probe(p, TOKEN_NEWLINE)) parser_next(p);
+}
+
struct Statement* parser_statement(struct Parser* p, struct Parser_Error* error);
struct Expression* parser_expression(struct Parser* p, struct Parser_Error* error);
@@ -180,6 +193,7 @@ parser_end_statement(struct Parser* p, struct Parser_Error* error)
parser_next(p);
}
+// checks if the next 2 tokens could begin a variable declaration.
bool
parser_could_be_variable_declaration(struct Parser* p)
{
@@ -189,11 +203,13 @@ parser_could_be_variable_declaration(struct Parser* p)
// otherwise without a type, it is instead counted as an assignment:
// a = "hi!"
+ // NOTE: maybe move this into `lex.c`?
+ // or change the API a bit, this isn't really a parser method.
struct Token first = parser_peek(p);
struct Token second = parser_peek_further(p);
bool first_matches = token_is(&first, TOKEN_NAME);
- bool second_matches = token_is(&second, TOKEN_COMMA) || token_is(&second, TOKEN_NAME);
+ bool second_matches = token_is(&second, TOKEN_COMMA) || token_can_begin_type(&second);
return first_matches && second_matches;
}
@@ -203,6 +219,8 @@ parser_block_node(struct Parser* p, struct Parser_Error* error)
struct Token start_token =
CHECK_RETURN(parser_need(p, TOKEN_CURLY_OPEN, error), struct Block_Node);
+ parser_unglue(p);
+
struct Statement* head = nil;
struct Statement* current = nil;
@@ -229,24 +247,237 @@ parser_block_node(struct Parser* p, struct Parser_Error* error)
};
}
-struct Type_Node
-parser_node_type(struct Parser* p, struct Parser_Error* error)
+struct Type_Node* parser_node_type(struct Parser* p, struct Parser_Error* error);
+
+struct Function_Header_Node
+parser_function_header_node(struct Parser* p, struct Parser_Error* error)
{
- struct Token token = parser_need(p, TOKEN_NAME, error);
- if (token_is_empty(&token)) {
+ struct Token open_parameters_token =
+ CHECK_RETURN(parser_need(p, TOKEN_ROUND_OPEN, error), struct Function_Header_Node);
+
+ struct Function_Header_Node header = { 0 };
+ while (!parser_probe(p, TOKEN_ROUND_CLOSE)) {
+ struct Token name_token =
+ CHECK_RETURN(parser_need(p, TOKEN_NAME, error), struct Function_Header_Node);
+ struct String name = name_token.value.name;
+
+ struct Type_Node* type;
+ if (!parser_probe(p, TOKEN_ROUND_CLOSE) && !parser_probe(p, TOKEN_COMMA)) {
+ type = CHECK_RETURN(parser_node_type(p, error), struct Function_Header_Node);
+ } else {
+ type = type_node_none(name_token.span, name_token.location);
+ }
+
+ type->value_name = name;
+ if (!header.parameters_type_and_name)
+ header.parameters_type_and_name = type;
+ else
+ header.parameters_type_and_name->next = type;
+
+ if (parser_probe(p, TOKEN_COMMA)) parser_next(p);
+ }
+ struct Token close_parameters_token = parser_next(p);
+
+ header.span = span_merge(open_parameters_token.span, close_parameters_token.span);
+
+ struct Token next = parser_peek(p);
+ if (token_can_begin_type(&next)) {
+ header.return_type = CHECK_RETURN(parser_node_type(p, error), struct Function_Header_Node);
+ header.span = span_merge(header.span, header.return_type->span);
+ }
+
+ return header;
+}
+
+struct Type_Node*
+parser_node_type_name(struct Parser* p, struct Parser_Error* error)
+{
+ struct Token name_token = CHECK(parser_need(p, TOKEN_NAME, error));
+ return type_node_new(
+ TYPE_NODE_NAME, (union Type_Node_Value){ .name = { name_token.value.name } },
+ name_token.span, name_token.location);
+}
+
+struct Type_Node*
+parser_node_type_structure(struct Parser* p, struct Parser_Error* error)
+{
+ struct Token open_token = CHECK(parser_need(p, TOKEN_CURLY_OPEN, error));
+
+ parser_unglue(p);
+
+ struct Type_Node* head = nil;
+ struct Type_Node* current = nil;
+ while (!parser_probe(p, TOKEN_CURLY_CLOSE)) {
+ struct Token field_name_token = CHECK(parser_need(p, TOKEN_NAME, error));
+
+ struct Type_Node* field_type = CHECK(parser_node_type(p, error));
+ if (!field_type) {
+ *error = parser_error(PARSER_ERROR_EXPECTED_TYPE);
+ return nil;
+ }
+ field_type->value_name = field_name_token.value.name;
+
+ if (!head)
+ head = field_type;
+ else
+ current->next = field_type;
+ current = field_type;
+
+ if (parser_probe(p, TOKEN_COMMA) || parser_probe(p, TOKEN_NEWLINE)) parser_next(p);
+ }
+
+ parser_unglue(p);
+
+ struct Token close_token = CHECK(parser_need(p, TOKEN_CURLY_CLOSE, error));
+ struct Span span = span_merge(open_token.span, close_token.span);
+
+ return type_node_new(
+ TYPE_NODE_STRUCTURE, (union Type_Node_Value){ .structure = { head } }, span,
+ open_token.location);
+}
+
+struct Type_Node*
+parser_node_type_variant(struct Parser* p, struct Parser_Error* error)
+{
+ // TODO
+ return nil;
+}
+
+struct Type_Node*
+parser_node_type_function(struct Parser* p, struct Parser_Error* error)
+{
+ struct Token fun_token = CHECK(parser_need(p, TOKEN_WORD_FUN, error));
+ struct Function_Header_Node header = CHECK(parser_function_header_node(p, error));
+
+ struct Span span = span_merge(fun_token.span, header.span);
+ return type_node_new(
+ TYPE_NODE_FUNCTION, (union Type_Node_Value){ .function = { header } }, span,
+ fun_token.location);
+}
+
+struct Type_Node*
+parser_node_type_class(struct Parser* p, struct Parser_Error* error)
+{
+ // TODO
+ return nil;
+}
+
+struct Type_Node*
+parser_node_type_tuple(struct Parser* p, struct Parser_Error* error)
+{
+ struct Token open_token = CHECK(parser_need(p, TOKEN_ROUND_OPEN, error));
+
+ struct Type_Node* head = nil;
+ struct Type_Node* current = nil;
+ while (!parser_probe(p, TOKEN_ROUND_CLOSE)) {
+ struct Type_Node* type = CHECK(parser_node_type(p, error));
+ if (!type) {
+ *error = parser_error(PARSER_ERROR_EXPECTED_TYPE);
+ return nil;
+ }
+
+ if (!head)
+ head = type;
+ else
+ current->next = type;
+ current = type;
+
+ if (parser_probe(p, TOKEN_COMMA))
+ parser_next(p);
+ else
+ break;
+ }
+
+ struct Token close_token = CHECK(parser_need(p, TOKEN_ROUND_CLOSE, error));
+ struct Span span = span_merge(open_token.span, close_token.span);
+ return type_node_new(
+ TYPE_NODE_TUPLE, (union Type_Node_Value){ .tuple = { head } }, span, open_token.location);
+}
+
+struct Type_Node*
+parser_node_type_array_or_map(struct Parser* p, struct Parser_Error* error)
+{
+ struct Token open_token = CHECK(parser_need(p, TOKEN_SQUARE_OPEN, error));
+
+ struct Type_Node* element_or_key_type = CHECK(parser_node_type(p, error));
+ if (!element_or_key_type) {
*error = parser_error(PARSER_ERROR_EXPECTED_TYPE);
- return (struct Type_Node){ 0 };
+ return nil;
}
- struct String type_name = token.value.name;
- // for now, we only support a single type name.
- // in the future, we might want to support more complex types.
- return (struct Type_Node){
- .type = TYPE_NAME,
- .name = type_name,
- .span = token.span,
- .location = token.location,
- };
+ enum Type_Node_Type type;
+ union Type_Node_Value value;
+ if (parser_probe(p, TOKEN_ASSIGN)) {
+ // this is a map type, e.g. `[string = int]`
+ parser_next(p); // consume the assignment token
+
+ struct Type_Node* key_type = element_or_key_type;
+ struct Type_Node* value_type = CHECK(parser_node_type(p, error));
+ if (!value_type) {
+ *error = parser_error(PARSER_ERROR_EXPECTED_TYPE);
+ return nil;
+ }
+
+ type = TYPE_NODE_MAP;
+ value.map = (struct Type_Node_Map){
+ .key_type = key_type,
+ .value_type = value_type,
+ };
+ } else {
+ // this is an array type, e.g. `[int]`
+ type = TYPE_NODE_ARRAY;
+ value.array = (struct Type_Node_Array){ .element_type = element_or_key_type };
+ }
+
+ struct Token close_token = CHECK(parser_need(p, TOKEN_SQUARE_CLOSE, error));
+
+ struct Span span = span_merge(open_token.span, close_token.span);
+ return type_node_new(type, value, span, open_token.location);
+}
+
+struct Type_Node*
+parser_node_type_reference(struct Parser* p, struct Parser_Error* error)
+{
+ struct Token ampersand_token = CHECK(parser_need(p, TOKEN_AMPERSAND, error));
+
+ struct Type_Node* referenced_type = CHECK(parser_node_type(p, error));
+ if (!referenced_type) {
+ *error = parser_error(PARSER_ERROR_EXPECTED_TYPE);
+ return nil;
+ }
+
+ struct Span span = span_merge(ampersand_token.span, referenced_type->span);
+ return type_node_new(
+ TYPE_NODE_REFERENCE, (union Type_Node_Value){ .reference = { referenced_type } }, span,
+ ampersand_token.location);
+}
+
+struct Type_Node*
+parser_node_type(struct Parser* p, struct Parser_Error* error)
+{
+ // TODO: maybe, variant, class
+ struct Token token = parser_peek(p);
+ switch (token.kind) {
+ case TOKEN_NAME:
+ return parser_node_type_name(p, error);
+ case TOKEN_WORD_VARIANT:
+ return parser_node_type_variant(p, error);
+ case TOKEN_WORD_CLASS:
+ return parser_node_type_class(p, error);
+ case TOKEN_WORD_FUN:
+ return parser_node_type_function(p, error);
+ case TOKEN_CURLY_OPEN:
+ return parser_node_type_structure(p, error);
+ case TOKEN_ROUND_OPEN:
+ return parser_node_type_tuple(p, error);
+ case TOKEN_SQUARE_OPEN:
+ return parser_node_type_array_or_map(p, error);
+ case TOKEN_AMPERSAND:
+ return parser_node_type_reference(p, error);
+ default:
+ *error = parser_error(PARSER_ERROR_EXPECTED_TYPE);
+ return nil;
+ }
}
struct Expression*
@@ -308,31 +539,9 @@ struct Expression*
parser_expression_function(struct Parser* p, struct Parser_Error* error)
{
struct Token fun_token = CHECK(parser_need(p, TOKEN_WORD_FUN, error));
- CHECK(parser_need(p, TOKEN_ROUND_OPEN, error));
-
struct Expression_Function fun = { 0 };
- while (!parser_probe(p, TOKEN_ROUND_CLOSE)) {
- struct Token name_token = CHECK(parser_need(p, TOKEN_NAME, error));
- struct String name = name_token.value.name;
-
- struct Type_Node type = { 0 };
-
- struct Token next = parser_peek(p);
- if (!token_is(&next, TOKEN_ROUND_CLOSE) && !token_is(&next, TOKEN_COMMA))
- type = CHECK(parser_node_type(p, error));
-
- if (parser_probe(p, TOKEN_COMMA)) parser_next(p);
-
- check(fun.parameter_count < EXPRESSION_FUNCTION_MAX_PARAMS, "too many function parameters");
- fun.parameters[fun.parameter_count++] = (struct Expression_Function_Parameter){
- .name = name,
- .type = type,
- };
- }
- parser_next(p);
-
- if (!parser_probe(p, TOKEN_CURLY_OPEN)) fun.return_type = CHECK(parser_node_type(p, error));
+ fun.header = CHECK(parser_function_header_node(p, error));
fun.body = CHECK(parser_block_node(p, error));
return expression_new(
@@ -603,7 +812,7 @@ parser_statement_declaration(struct Parser* p, struct Parser_Error* error)
}
// for now, type is always required.
- struct Type_Node type = CHECK(parser_node_type(p, error));
+ struct Type_Node* type = CHECK(parser_node_type(p, error));
CHECK(parser_need(p, TOKEN_ASSIGN, error));
struct Expression* initializer = CHECK(parser_expression(p, error));
@@ -771,6 +980,23 @@ parser_statement_defer(struct Parser* p, struct Parser_Error* error)
}
struct Statement*
+parser_statement_type(struct Parser* p, struct Parser_Error* error)
+{
+ struct Token type_token = CHECK(parser_need(p, TOKEN_WORD_TYPE, error));
+
+ // TODO: parse type paths, i.e. `Thing.SubType`
+ struct Token name_token = CHECK(parser_need(p, TOKEN_NAME, error));
+ CHECK(parser_need(p, TOKEN_ASSIGN, error));
+ struct String name = name_token.value.name;
+
+ struct Type_Node* type = CHECK(parser_node_type(p, error));
+
+ struct Span span = span_merge(type_token.span, type->span);
+ union Statement_Value value = { .type = { type, name } };
+ return statement_new(STATEMENT_TYPE, value, span, type_token.location);
+}
+
+struct Statement*
parser_statement(struct Parser* p, struct Parser_Error* error)
{
struct Token token = parser_peek(p);
@@ -798,7 +1024,10 @@ parser_statement(struct Parser* p, struct Parser_Error* error)
return parser_statement_continue(p, error);
case TOKEN_WORD_DEFER:
return parser_statement_defer(p, error);
- default: break;
+ case TOKEN_WORD_TYPE:
+ return parser_statement_type(p, error);
+ default:
+ break;
}
struct Expression* expression = CHECK(parser_expression(p, error));
diff --git a/boot/tree.c b/boot/tree.c
index 39a1de4..449f4e6 100644
--- a/boot/tree.c
+++ b/boot/tree.c
@@ -394,24 +394,247 @@ struct Block_Node
void block_node_print(const struct Block_Node* block);
-enum Type_Type
+// a function header, describing the parameters and return type of function.
+// used both as a type and in full function definitions.
+struct Function_Header_Node
{
- TYPE_NONE,
- TYPE_NAME,
+ // linked list of parameters.
+ // name, if given, is included in the type node.
+ struct Type_Node* parameters_type_and_name;
+ struct Type_Node* return_type;
+
+ struct Span span;
+};
+
+void function_header_node_print(const struct Function_Header_Node* header);
+
+enum Type_Node_Type
+{
+ TYPE_NODE_NONE,
+
+ TYPE_NODE_NAME,
+ TYPE_NODE_ARRAY, // an array of a type, `[int]`.
+ TYPE_NODE_REFERENCE, // a reference to a type, `&int`.
+ TYPE_NODE_MAYBE, // a type that may be null, `int?`.
+ TYPE_NODE_TUPLE, // a tuple type, `(int string)`.
+ TYPE_NODE_MAP, // a map type, `[string = int]`.
+
+ TYPE_NODE_FUNCTION, // a function type, `fun (int) int`.
+ TYPE_NODE_STRUCTURE, // a struct, invoked either with `type` or with `{}` when a type is inline.
+ TYPE_NODE_VARIANT, // a tagged union.
+ TYPE_NODE_CLASS, // a class of types, a.k.a. an interface.
+};
+
+struct Type_Node_Name
+{
+ struct String name;
+};
+
+struct Type_Node_Array
+{
+ struct Type_Node* element_type;
+};
+
+struct Type_Node_Reference
+{
+ struct Type_Node* referenced_type;
+};
+
+struct Type_Node_Maybe
+{
+ struct Type_Node* inner_type;
+};
+
+struct Type_Node_Tuple
+{
+ struct Type_Node* head; // the first type in the tuple, if any.
+};
+
+struct Type_Node_Map
+{
+ struct Type_Node* key_type;
+ struct Type_Node* value_type;
+};
+
+struct Type_Node_Function
+{
+ struct Function_Header_Node header;
+};
+
+struct Type_Node_Structure
+{
+ // the fields of the structure, linked list of types and (required) names.
+ struct Type_Node* fields;
+};
+
+struct Type_Node_Variant
+{
+ // the variants of the tagged union, linked list of (required) variant names and backing types.
+ // if a variant has no backing type, it is TYPE_NODE_NONE.
+ struct Type_Node* variants;
+};
+
+struct Type_Node_Class
+{
+ // linked list of the types of methods required to implement the class.
+ // each node is required to have a name and be of TYPE_NODE_FUNCTION.
+ struct Type_Node* methods;
+};
+
+union Type_Node_Value
+{
+ struct Type_Node_Name name;
+ struct Type_Node_Array array;
+ struct Type_Node_Reference reference;
+ struct Type_Node_Maybe maybe;
+ struct Type_Node_Tuple tuple;
+ struct Type_Node_Map map;
+ struct Type_Node_Function function;
+ struct Type_Node_Structure structure;
+ struct Type_Node_Variant variant;
+ struct Type_Node_Class class;
};
// a type node represents a type in the syntax tree.
// currently, we only support types that are simple names.
// or null types.
+// also includes the name of the field, member, parameter, etc., for which
+// the type is defined.
struct Type_Node
{
- enum Type_Type type;
+ enum Type_Node_Type type;
+ union Type_Node_Value value;
// note: we could also just include the token here i think?
- struct String name;
struct Span span;
struct Cursor location;
+
+ // usually a type is preceded by the name of the binding
+ // 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;
+
+ // if type is within a group of multiple types,
+ // points to the next type within the group.
+ struct Type_Node* next;
};
+REGION(struct Type_Node, type_node)
+
+// allocates a new type node in the global type node region.
+struct Type_Node*
+type_node_new(
+ enum Type_Node_Type type, union Type_Node_Value value, struct Span span, struct Cursor location)
+{
+ check(region_type_node_cursor < REGION_SIZE, "out of type node memory");
+ struct Type_Node* type_node = &region_type_node[region_type_node_cursor++];
+ *type_node = (struct Type_Node){
+ .type = type,
+ .value = value,
+ .span = span,
+ .location = location,
+
+ .value_name = string_empty(),
+ .next = nil,
+ };
+ return type_node;
+}
+
+// allocates a new type node with no value, used for `none` types.
+// this is used for types that are not specified, note that it is still
+// fully allocated and can be used in the syntax tree.
+struct Type_Node*
+type_node_none(struct Span span, struct Cursor location)
+{
+ return type_node_new(TYPE_NODE_NONE, (union Type_Node_Value){ 0 }, span, location);
+}
+
+bool
+type_node_is_none(const struct Type_Node* type_node)
+{
+ return type_node->type == TYPE_NODE_NONE;
+}
+
+void
+type_node_print(const struct Type_Node* type_node)
+{
+ printf("(type ");
+ switch (type_node->type) {
+ case TYPE_NODE_NONE:
+ printf("none");
+ break;
+ case TYPE_NODE_NAME:
+ printf("name %s", type_node->value.name.name.data);
+ break;
+ case TYPE_NODE_ARRAY:
+ printf("array of ");
+ type_node_print(type_node->value.array.element_type);
+ break;
+ case TYPE_NODE_REFERENCE:
+ printf("reference to ");
+ type_node_print(type_node->value.reference.referenced_type);
+ break;
+ case TYPE_NODE_MAYBE:
+ printf("maybe ");
+ type_node_print(type_node->value.maybe.inner_type);
+ break;
+ case TYPE_NODE_TUPLE: {
+ printf("tuple");
+ FOR_EACH(struct Type_Node*, current, type_node->value.tuple.head)
+ {
+ printf(" ");
+ type_node_print(current);
+ }
+ break;
+ }
+ case TYPE_NODE_MAP:
+ printf("map ");
+ type_node_print(type_node->value.map.key_type);
+ printf(" = ");
+ type_node_print(type_node->value.map.value_type);
+ break;
+ case TYPE_NODE_FUNCTION: {
+ printf("function ");
+ function_header_node_print(&type_node->value.function.header);
+ break;
+ }
+ case TYPE_NODE_STRUCTURE: {
+ printf("structure");
+ FOR_EACH(struct Type_Node*, current, type_node->value.structure.fields)
+ {
+ printf(" (field %s) ", current->value_name.data);
+ type_node_print(current);
+ }
+ break;
+ }
+ case TYPE_NODE_VARIANT: {
+ printf("variant");
+ FOR_EACH(struct Type_Node*, current, type_node->value.variant.variants)
+ {
+ if (type_node_is_none(current)) {
+ printf(" (variant %s)", current->value_name.data);
+ } else {
+ printf(" (variant %s of ", current->value_name.data);
+ type_node_print(current);
+ printf(")");
+ }
+ }
+ break;
+ }
+ case TYPE_NODE_CLASS:
+ printf("class");
+ FOR_EACH(struct Type_Node*, current, type_node->value.class.methods)
+ {
+ check(current->type == TYPE_NODE_FUNCTION,
+ "expected class method type node to be a function type");
+ printf(" (method %s ", current->value_name.data);
+ function_header_node_print(&current->value.function.header);
+ printf(")");
+ }
+ break;
+ }
+ printf(")");
+}
+
enum Expression_Kind
{
EXPRESSION_NONE,
@@ -503,18 +726,9 @@ struct Expression_Increment_Decrement
enum Increment_Decrement_Operation operation;
};
-#define EXPRESSION_FUNCTION_MAX_PARAMS 32
-
struct Expression_Function
{
- struct Expression_Function_Parameter
- {
- struct String name;
- struct Type_Node type;
- } parameters[EXPRESSION_FUNCTION_MAX_PARAMS];
- uint parameter_count;
-
- struct Type_Node return_type;
+ struct Function_Header_Node header;
struct Block_Node body;
};
@@ -568,6 +782,28 @@ expression_new(
}
void
+function_header_node_print(const struct Function_Header_Node* header)
+{
+ FOR_EACH(struct Type_Node*, current, header->parameters_type_and_name)
+ {
+ if (current != header->parameters_type_and_name) printf(" ");
+
+ if (!string_is_empty(current->value_name))
+ printf("(param %s) ", current->value_name.data);
+ else
+ printf("(param) ");
+
+ type_node_print(current);
+ }
+
+ if (header->return_type) {
+ printf(" (returns ");
+ type_node_print(header->return_type);
+ printf(")");
+ }
+}
+
+void
expression_print(const struct Expression* expression)
{
printf("(expr ");
@@ -641,13 +877,8 @@ expression_print(const struct Expression* expression)
}
case EXPRESSION_FUNCTION: {
const struct Expression_Function* fun = &expression->value.function;
- printf("(function");
- for (uint i = 0; i < fun->parameter_count; ++i) {
- const struct Expression_Function_Parameter* param = &fun->parameters[i];
- printf(" (param %s)", param->name.data);
- if (param->type.type != TYPE_NONE) { printf(" (type %s)", param->type.name.data); }
- }
- if (fun->return_type.type != TYPE_NONE) printf(" (returns %s)", fun->return_type.name.data);
+ printf("(function ");
+ function_header_node_print(&fun->header);
printf(" ");
block_node_print(&fun->body);
printf(")");
@@ -673,6 +904,7 @@ enum Statement_Kind
STATEMENT_BREAK,
STATEMENT_CONTINUE,
STATEMENT_DEFER,
+ STATEMENT_TYPE,
};
struct Statement_Value_Expression
@@ -684,7 +916,7 @@ struct Statement_Value_Declaration
{
struct String_Array names;
struct Expression* initializer;
- struct Type_Node type;
+ struct Type_Node* type;
};
struct Statement_Value_Block
@@ -731,6 +963,12 @@ struct Statement_Value_Defer
struct Block_Node block;
};
+struct Statement_Value_Type
+{
+ struct Type_Node* type;
+ struct String name;
+};
+
union Statement_Value
{
struct Statement_Value_Expression expression;
@@ -740,6 +978,7 @@ union Statement_Value
struct Statement_Value_Loop loop;
struct Statement_Value_Return return_value;
struct Statement_Value_Defer defer;
+ struct Statement_Value_Type type;
};
struct Statement
@@ -807,9 +1046,9 @@ statement_print(const struct Statement* statement)
{
printf("%s ", name.data);
}
- if (statement->value.declaration.type.type != TYPE_NONE) {
- printf("(type %s) ", statement->value.declaration.type.name.data);
- }
+ if (type_node_is_none(statement->value.declaration.type))
+ type_node_print(statement->value.declaration.type);
+
if (statement->value.declaration.initializer) {
printf("(initializer ");
expression_print(statement->value.declaration.initializer);
@@ -889,6 +1128,11 @@ statement_print(const struct Statement* statement)
printf(")");
break;
}
+ case STATEMENT_TYPE: {
+ printf("(type name %s) ", statement->value.type.name.data);
+ type_node_print(statement->value.type.type);
+ break;
+ }
default:
failure("unexpected statement kind passed to `statement_print`");
break;