about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-05-28 00:36:13 +0200
committerMel <mel@rnrd.eu>2026-05-28 00:36:13 +0200
commit1aef923b312f2bc9c0963381eae8619c187081c6 (patch)
tree2e324f09960c31b13f72b3961fc7abaf0fa2865e
parent7db79f43b431b94d525308699a87a3daeff31d92 (diff)
downloadcatskill-1aef923b312f2bc9c0963381eae8619c187081c6.tar.zst
catskill-1aef923b312f2bc9c0963381eae8619c187081c6.zip
Use semicolon tokens as statement separators
Signed-off-by: Mel <mel@rnrd.eu>
-rw-r--r--boot/lex.c8
-rw-r--r--boot/tests/parse/semicolon_separators.cskt25
-rw-r--r--docs/grammar.md4
3 files changed, 34 insertions, 3 deletions
diff --git a/boot/lex.c b/boot/lex.c
index e585e1a..6c3e86f 100644
--- a/boot/lex.c
+++ b/boot/lex.c
@@ -53,6 +53,7 @@ enum Token_Kind
     TOKEN_SQUARE_CLOSE,
 
     TOKEN_COMMA,
+    TOKEN_SEMICOLON,
     TOKEN_AMPERSAND,
     TOKEN_DOT,
     TOKEN_DOT_DOT,
@@ -176,6 +177,8 @@ token_kind_to_string(enum Token_Kind kind)
 
     case TOKEN_COMMA:
         return "COMMA";
+    case TOKEN_SEMICOLON:
+        return "SEMICOLON";
     case TOKEN_AMPERSAND:
         return "AMPERSAND";
     case TOKEN_DOT:
@@ -331,7 +334,8 @@ token_is_empty(const struct Token* t)
 bool
 token_ends_statement(const struct Token* t)
 {
-    return token_is(t, TOKEN_END_OF_FILE) || token_is(t, TOKEN_NEWLINE);
+    return token_is(t, TOKEN_END_OF_FILE) || token_is(t, TOKEN_NEWLINE)
+        || token_is(t, TOKEN_SEMICOLON);
 }
 
 bool
@@ -574,6 +578,8 @@ lexer_symbol_token(struct Lexer* l, struct Lexer_Char current)
 
     case ',':
         RET{ TOKEN_COMMA, 1 };
+    case ';':
+        RET{ TOKEN_SEMICOLON, 1 };
     case '&': {
         lexer_match_chars(l, '&', '=', &a, &b);
 
diff --git a/boot/tests/parse/semicolon_separators.cskt b/boot/tests/parse/semicolon_separators.cskt
new file mode 100644
index 0000000..384ca71
--- /dev/null
+++ b/boot/tests/parse/semicolon_separators.cskt
@@ -0,0 +1,25 @@
+a `;` terminates the current statement just like a newline,
+so multiple statements can sit on the same source line without
+forcing a line break between them.
+
+<<<
+
+var a int = 1; var b int = 2
+a = a + b; b = a - b
+a; b; c; d; e; f; g;
+;;;;0;;;;
+
+>>>
+
+(variable (declaration a (type name int) (initializer (expr 1))))
+(variable (declaration b (type name int) (initializer (expr 2))))
+(expr (binary = (expr (name a)) (expr (binary + (expr (name a)) (expr (name b))))))
+(expr (binary = (expr (name b)) (expr (binary - (expr (name a)) (expr (name b))))))
+(expr (name a))
+(expr (name b))
+(expr (name c))
+(expr (name d))
+(expr (name e))
+(expr (name f))
+(expr (name g))
+(expr 0)
diff --git a/docs/grammar.md b/docs/grammar.md
index 2fca56e..18a8550 100644
--- a/docs/grammar.md
+++ b/docs/grammar.md
@@ -72,7 +72,7 @@ STRING_LITERAL = '"' { not_quote } '"'
 digit = "0".."9"
 
 # multi-character symbols are matched eagerly, e.g. "<<=" before "<<".
-symbol = "(" | ")" | "{" | "}" | "[" | "]" | ","
+symbol = "(" | ")" | "{" | "}" | "[" | "]" | "," | ";"
 	| "." | ".." | "..."
 	| "!" | "?" | "~" | "|" | "^"
 	| "+" | "-" | "*" | "/" | "%" | "&" | "="
@@ -90,7 +90,7 @@ symbol = "(" | ")" | "{" | "}" | "[" | "]" | ","
 ```ebnf
 # a translation unit is a stream of statements at the top level.
 unit = { statement statement_end }
-statement_end = NEWLINE | EOF
+statement_end = NEWLINE | ";" | EOF
 
 statement = statement_declaration
 	| statement_conditional