aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2025-06-14 00:14:58 +0200
committerMel <mel@rnrd.eu>2025-06-14 00:14:58 +0200
commit4a49045f77b5be5d82e84c6e3e57721686ec5a0a (patch)
tree3143861a9a6d66e6acbe8c7273aaa0bbc0e486bc
parent3e7acb02962b05db8cf83e5b6b082ac368c8c88e (diff)
downloadcatskill-4a49045f77b5be5d82e84c6e3e57721686ec5a0a.tar.zst
catskill-4a49045f77b5be5d82e84c6e3e57721686ec5a0a.zip
Disambiguate variable declaration and call/array access syntax with `var` + `let`
Signed-off-by: Mel <mel@rnrd.eu>
-rw-r--r--boot/lex.c11
-rw-r--r--boot/parse.c41
-rw-r--r--boot/tree.c27
3 files changed, 54 insertions, 25 deletions
diff --git a/boot/lex.c b/boot/lex.c
index 52760e9..f09e5e3 100644
--- a/boot/lex.c
+++ b/boot/lex.c
@@ -97,6 +97,7 @@ enum Token_Kind
TOKEN_WORD_SWITCH,
TOKEN_WORD_RETURN,
TOKEN_WORD_VAR,
+ TOKEN_WORD_LET,
TOKEN_WORD_TYPE,
TOKEN_WORD_VARIANT,
TOKEN_WORD_CLASS,
@@ -200,6 +201,8 @@ token_kind_to_string(enum Token_Kind kind)
return "WORD_RETURN";
case TOKEN_WORD_VAR:
return "WORD_VAR";
+ case TOKEN_WORD_LET:
+ return "WORD_LET";
case TOKEN_WORD_TYPE:
return "WORD_TYPE";
case TOKEN_WORD_VARIANT:
@@ -407,6 +410,12 @@ token_can_begin_type(const struct Token* t)
}
bool
+token_can_begin_declaration(const struct Token* t)
+{
+ return token_is(t, TOKEN_WORD_VAR) || token_is(t, TOKEN_WORD_LET);
+}
+
+bool
ascii_in_range(ascii c, ascii from, ascii to)
{
return c >= from && c <= to;
@@ -793,6 +802,8 @@ lexer_word_from_name(struct Lexer* l, struct String word_or_name)
return TOKEN_WORD_RETURN;
case 1662845996: // "var"
return TOKEN_WORD_VAR;
+ case 860722406:
+ return TOKEN_WORD_LET;
case 91700392: // "type"
return TOKEN_WORD_TYPE;
case 3267162257: // "variant"
diff --git a/boot/parse.c b/boot/parse.c
index a0dd12c..6129950 100644
--- a/boot/parse.c
+++ b/boot/parse.c
@@ -193,26 +193,6 @@ 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)
-{
- // NOTE: these can be a variable declaration:
- // x uint = 123
- // me, them Obj = create()
- // 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_can_begin_type(&second);
- return first_matches && second_matches;
-}
-
struct Block_Node
parser_block_node(struct Parser* p, struct Parser_Error* error)
{
@@ -795,6 +775,12 @@ parser_expression(struct Parser* p, struct Parser_Error* error)
struct Statement*
parser_statement_declaration(struct Parser* p, struct Parser_Error* error)
{
+ struct Token declaration_token = parser_next(p);
+
+ enum Statement_Declaration_Kind declaration_kind =
+ statement_declaration_kind_from_token(&declaration_token);
+ check(declaration_kind, "expected valid declaration token");
+
struct String_Array names = string_array_new();
struct Span span = { 0 };
@@ -819,6 +805,7 @@ parser_statement_declaration(struct Parser* p, struct Parser_Error* error)
span = span_merge(span, initializer->span);
union Statement_Value value = {
.declaration = {
+ .kind = declaration_kind,
.names = names,
.type = type,
.initializer = initializer,
@@ -882,16 +869,20 @@ parser_statement_loop(struct Parser* p, struct Parser_Error* error)
struct Token for_token = CHECK(parser_need(p, TOKEN_WORD_FOR, error));
// these are the possible for loop variants:
- // * `for String name = collection {}`, as iteration over container
+ // * `for var name String = collection {}`, as iteration over container
// * `for check() {}`, as a simple while-style loop
- // * `for u8 i = 0, i < 10, i++ {}`, as a c-style semi-semi loop
+ // * `for var i u8 = 0, i < 10, i++ {}`, as a c-style semi-semi loop
// * `for {}`, as an infinite loop
struct Statement* declaration = nil;
struct Expression *condition = nil, *iteration = nil;
// c-style or iterator-style
- if (parser_could_be_variable_declaration(p)) {
+ struct Token next = parser_peek(p);
+ // TODO: i do not like the `for var` combination, it is too verbose.
+ // it would be beneficial to bring back `while`, so that declarations without `var`
+ // are no longer ambiguous.
+ if (token_can_begin_declaration(&next)) {
declaration = CHECK(parser_statement_declaration(p, error));
// c-style
@@ -905,7 +896,7 @@ parser_statement_loop(struct Parser* p, struct Parser_Error* error)
}
// while-style
- if (!parser_probe(p, TOKEN_CURLY_OPEN)) condition = CHECK(parser_expression(p, error));
+ if (!token_is(&next, TOKEN_CURLY_OPEN)) condition = CHECK(parser_expression(p, error));
struct Block_Node body = CHECK(parser_block_node(p, error));
struct Span span = span_merge(for_token.span, body.span);
@@ -1007,7 +998,7 @@ parser_statement(struct Parser* p, struct Parser_Error* error)
token = parser_peek(p);
}
- if (parser_could_be_variable_declaration(p)) return parser_statement_declaration(p, error);
+ if (token_can_begin_declaration(&token)) return parser_statement_declaration(p, error);
switch (token.kind) {
case TOKEN_WORD_IF:
diff --git a/boot/tree.c b/boot/tree.c
index 449f4e6..dff0858 100644
--- a/boot/tree.c
+++ b/boot/tree.c
@@ -912,8 +912,30 @@ struct Statement_Value_Expression
struct Expression* inner;
};
+enum Statement_Declaration_Kind
+{
+ STATEMENT_DECLARATION_NONE,
+ STATEMENT_DECLARATION_VARIABLE,
+ STATEMENT_DECLARATION_CONSTANT,
+};
+
+enum Statement_Declaration_Kind
+statement_declaration_kind_from_token(const struct Token* token)
+{
+ switch (token->kind) {
+ case TOKEN_WORD_VAR:
+ return STATEMENT_DECLARATION_VARIABLE;
+ case TOKEN_WORD_LET:
+ return STATEMENT_DECLARATION_CONSTANT;
+
+ default:
+ return STATEMENT_DECLARATION_NONE;
+ }
+}
+
struct Statement_Value_Declaration
{
+ enum Statement_Declaration_Kind kind;
struct String_Array names;
struct Expression* initializer;
struct Type_Node* type;
@@ -1042,6 +1064,11 @@ statement_print(const struct Statement* statement)
}
case STATEMENT_DECLARATION: {
printf("(declaration ");
+ if (statement->value.declaration.kind == STATEMENT_DECLARATION_VARIABLE)
+ printf("variable ");
+ else if (statement->value.declaration.kind == STATEMENT_DECLARATION_CONSTANT)
+ printf("constant ");
+
STRING_ARRAY_FOR_EACH(i, name, statement->value.declaration.names)
{
printf("%s ", name.data);