summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/codegen/codegen.c123
-rw-r--r--src/parser/parser_stmt.c38
-rw-r--r--tests/control_flow/test_match.zc52
3 files changed, 184 insertions, 29 deletions
diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c
index 9539338..644e9f5 100644
--- a/src/codegen/codegen.c
+++ b/src/codegen/codegen.c
@@ -10,6 +10,104 @@
#include "ast.h"
#include "zprep_plugin.h"
+// Helper: emit a single pattern condition (either a value, or a range)
+static void emit_single_pattern_cond(const char *pat, int id, FILE *out)
+{
+ // Check for range pattern: "start..end" or "start..=end"
+ char *range_incl = strstr(pat, "..=");
+ char *range_excl = strstr(pat, "..");
+
+ if (range_incl)
+ {
+ // Inclusive range: start..=end -> _m_id >= start && _m_id <= end
+ int start_len = (int)(range_incl - pat);
+ char *start = xmalloc(start_len + 1);
+ strncpy(start, pat, start_len);
+ start[start_len] = 0;
+ char *end = xstrdup(range_incl + 3);
+ fprintf(out, "(_m_%d >= %s && _m_%d <= %s)", id, start, id, end);
+ free(start);
+ free(end);
+ }
+ else if (range_excl)
+ {
+ // Exclusive range: start..end -> _m_id >= start && _m_id < end
+ int start_len = (int)(range_excl - pat);
+ char *start = xmalloc(start_len + 1);
+ strncpy(start, pat, start_len);
+ start[start_len] = 0;
+ char *end = xstrdup(range_excl + 2);
+ fprintf(out, "(_m_%d >= %s && _m_%d < %s)", id, start, id, end);
+ free(start);
+ free(end);
+ }
+ else if (pat[0] == '"')
+ {
+ // String pattern
+ fprintf(out, "strcmp(_m_%d, %s) == 0", id, pat);
+ }
+ else if (pat[0] == '\'')
+ {
+ // Char literal pattern
+ fprintf(out, "_m_%d == %s", id, pat);
+ }
+ else
+ {
+ // Numeric or simple pattern
+ fprintf(out, "_m_%d == %s", id, pat);
+ }
+}
+
+// Helper: emit condition for a pattern (may contain OR patterns with '|')
+static void emit_pattern_condition(ParserContext *ctx, const char *pattern, int id, FILE *out)
+{
+ // Check if pattern contains '|' for OR patterns
+ if (strchr(pattern, '|'))
+ {
+ // Split by '|' and emit OR conditions
+ char *pattern_copy = xstrdup(pattern);
+ char *saveptr;
+ char *part = strtok_r(pattern_copy, "|", &saveptr);
+ int first = 1;
+ fprintf(out, "(");
+ while (part)
+ {
+ if (!first)
+ {
+ fprintf(out, " || ");
+ }
+
+ // Check if part is an enum variant
+ EnumVariantReg *reg = find_enum_variant(ctx, part);
+ if (reg)
+ {
+ fprintf(out, "_m_%d.tag == %d", id, reg->tag_id);
+ }
+ else
+ {
+ emit_single_pattern_cond(part, id, out);
+ }
+ first = 0;
+ part = strtok_r(NULL, "|", &saveptr);
+ }
+ fprintf(out, ")");
+ free(pattern_copy);
+ }
+ else
+ {
+ // Single pattern (may be a range)
+ EnumVariantReg *reg = find_enum_variant(ctx, pattern);
+ if (reg)
+ {
+ fprintf(out, "_m_%d.tag == %d", id, reg->tag_id);
+ }
+ else
+ {
+ emit_single_pattern_cond(pattern, id, out);
+ }
+ }
+}
+
// static function for internal use.
static char *g_current_func_ret_type = NULL;
static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, int use_result)
@@ -138,29 +236,8 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out,
}
else
{
- EnumVariantReg *reg = find_enum_variant(ctx, c->match_case.pattern);
- if (reg)
- {
- fprintf(out, "_m_%d.tag == %d", id, reg->tag_id);
- }
- else if (c->match_case.pattern[0] == '"')
- {
- fprintf(out, "strcmp(_m_%d, %s) == 0", id, c->match_case.pattern);
- }
- else if (isdigit(c->match_case.pattern[0]) || c->match_case.pattern[0] == '-')
- {
- // Numeric pattern
- fprintf(out, "_m_%d == %s", id, c->match_case.pattern);
- }
- else if (c->match_case.pattern[0] == '\'')
- {
- // Char literal pattern
- fprintf(out, "_m_%d == %s", id, c->match_case.pattern);
- }
- else
- {
- fprintf(out, "1");
- }
+ // Use helper for OR patterns, range patterns, and simple patterns
+ emit_pattern_condition(ctx, c->match_case.pattern, id, out);
}
fprintf(out, ") { ");
if (c->match_case.binding_name)
diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c
index bfb45d8..d78a9e8 100644
--- a/src/parser/parser_stmt.c
+++ b/src/parser/parser_stmt.c
@@ -217,7 +217,11 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l)
break;
}
- // --- 1. Parse Comma-Separated Patterns ---
+ // --- 1. Parse Patterns (with OR and range support) ---
+ // Patterns can be:
+ // - Single value: 1
+ // - OR patterns: 1 || 2 or 1 or 2
+ // - Range patterns: 1..5 or 1..=5
char patterns_buf[1024];
patterns_buf[0] = 0;
int pattern_count = 0;
@@ -238,20 +242,42 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l)
p_str = tmp;
}
+ // Check for range pattern: value..end or value..=end
+ if (lexer_peek(l).type == TOK_DOTDOT || lexer_peek(l).type == TOK_DOTDOT_EQ)
+ {
+ int is_inclusive = (lexer_peek(l).type == TOK_DOTDOT_EQ);
+ lexer_next(l); // eat .. or ..=
+ Token end_tok = lexer_next(l);
+ char *end_str = token_strdup(end_tok);
+
+ // Build range pattern: "start..end" or "start..=end"
+ char *range_str = xmalloc(strlen(p_str) + strlen(end_str) + 4);
+ sprintf(range_str, "%s%s%s", p_str, is_inclusive ? "..=" : "..", end_str);
+ free(p_str);
+ free(end_str);
+ p_str = range_str;
+ }
+
if (pattern_count > 0)
{
- strcat(patterns_buf, ",");
+ strcat(patterns_buf, "|");
}
strcat(patterns_buf, p_str);
free(p_str);
pattern_count++;
- Lexer lookahead = *l;
- skip_comments(&lookahead);
- if (lexer_peek(&lookahead).type == TOK_COMMA)
+ // Check for OR continuation: ||, 'or', or comma (legacy)
+ Token next = lexer_peek(l);
+ skip_comments(l);
+ int is_or = (next.type == TOK_OR) ||
+ (next.type == TOK_OP && next.len == 2 && next.start[0] == '|' &&
+ next.start[1] == '|') ||
+ (next.type == TOK_COMMA); // Legacy comma support
+ if (is_or)
{
- lexer_next(l); // eat comma
+ lexer_next(l); // eat ||, 'or', or comma
skip_comments(l);
+ continue;
}
else
{
diff --git a/tests/control_flow/test_match.zc b/tests/control_flow/test_match.zc
index 7646256..bf757b1 100644
--- a/tests/control_flow/test_match.zc
+++ b/tests/control_flow/test_match.zc
@@ -20,6 +20,16 @@ fn get_sign(n: int) -> string {
}
}
+fn classify_extended(n: int) -> int {
+ match n {
+ 1 || 2 => { return 100; }, // OR pattern with ||
+ 3 or 4 => { return 200; }, // OR pattern with 'or'
+ 5..8 => { return 300; }, // Range exclusive (5, 6, 7)
+ 10..=15 => { return 400; }, // Range inclusive (10-15)
+ _ => { return -1; }
+ }
+}
+
test "test_match" {
println "Testing match expressions...";
@@ -86,6 +96,48 @@ test "test_match" {
exit(1);
}
+ // OR pattern with ||
+ var or1 = classify_extended(1);
+ var or2 = classify_extended(2);
+ if (or1 == 100 && or2 == 100) {
+ println " -> match OR (||): Passed";
+ } else {
+ println " -> match OR (||): Failed";
+ exit(1);
+ }
+
+ // OR pattern with 'or'
+ var or3 = classify_extended(3);
+ var or4 = classify_extended(4);
+ if (or3 == 200 && or4 == 200) {
+ println " -> match OR (or): Passed";
+ } else {
+ println " -> match OR (or): Failed";
+ exit(1);
+ }
+
+ // Range exclusive (5..8 matches 5, 6, 7)
+ var r5 = classify_extended(5);
+ var r7 = classify_extended(7);
+ var r8 = classify_extended(8); // Should NOT match
+ if (r5 == 300 && r7 == 300 && r8 == -1) {
+ println " -> match range exclusive: Passed";
+ } else {
+ println " -> match range exclusive: Failed";
+ exit(1);
+ }
+
+ // Range inclusive (10..=15 matches 10-15)
+ var r10 = classify_extended(10);
+ var r15 = classify_extended(15);
+ var r16 = classify_extended(16); // Should NOT match
+ if (r10 == 400 && r15 == 400 && r16 == -1) {
+ println " -> match range inclusive: Passed";
+ } else {
+ println " -> match range inclusive: Failed";
+ exit(1);
+ }
+
println "All match tests passed!";
}