From bbcc85eec5ccccd7cd7573f15510ceccfe42cc0d Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 25 Aug 2025 01:07:14 -0400 Subject: Move suffix rules into their own file --- src/parse/binops.c | 8 +-- src/parse/containers.c | 1 + src/parse/parse.c | 172 +------------------------------------------- src/parse/parse.h | 8 --- src/parse/suffixes.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++++ src/parse/suffixes.h | 14 ++++ 6 files changed, 206 insertions(+), 186 deletions(-) create mode 100644 src/parse/suffixes.c create mode 100644 src/parse/suffixes.h (limited to 'src') diff --git a/src/parse/binops.c b/src/parse/binops.c index f8d0854d..cbbdd44d 100644 --- a/src/parse/binops.c +++ b/src/parse/binops.c @@ -2,17 +2,11 @@ #include #include "../ast.h" -#include "../stdlib/print.h" #include "../stdlib/util.h" -#include "containers.h" #include "context.h" #include "errors.h" -#include "files.h" -#include "functions.h" -#include "numbers.h" #include "parse.h" -#include "text.h" -#include "types.h" +#include "suffixes.h" #include "utils.h" int op_tightness[] = { diff --git a/src/parse/containers.c b/src/parse/containers.c index 5c39eb5b..b97dd141 100644 --- a/src/parse/containers.c +++ b/src/parse/containers.c @@ -9,6 +9,7 @@ #include "context.h" #include "errors.h" #include "parse.h" +#include "suffixes.h" #include "utils.h" ast_t *parse_list(parse_ctx_t *ctx, const char *pos) { diff --git a/src/parse/parse.c b/src/parse/parse.c index 77461525..a4462cd6 100644 --- a/src/parse/parse.c +++ b/src/parse/parse.c @@ -5,7 +5,6 @@ #include #include "../ast.h" -#include "../stdlib/print.h" #include "../stdlib/util.h" #include "binops.h" #include "containers.h" @@ -15,6 +14,7 @@ #include "functions.h" #include "numbers.h" #include "parse.h" +#include "suffixes.h" #include "text.h" #include "types.h" #include "utils.h" @@ -41,34 +41,6 @@ ast_t *parse_parens(parse_ctx_t *ctx, const char *pos) { return new (ast_t, .file = (ctx)->file, .start = start, .end = pos, .tag = expr->tag, .__data = expr->__data); } -ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs) { - if (!lhs) return NULL; - const char *pos = lhs->end; - whitespace(&pos); - if (!match(&pos, ".")) return NULL; - if (*pos == '.') return NULL; - whitespace(&pos); - bool dollar = match(&pos, "$"); - const char *field = get_id(&pos); - if (!field) return NULL; - if (dollar) field = String("$", field); - return NewAST(ctx->file, lhs->start, pos, FieldAccess, .fielded = lhs, .field = field); -} - -ast_t *parse_optional_suffix(parse_ctx_t *ctx, ast_t *lhs) { - if (!lhs) return NULL; - const char *pos = lhs->end; - if (match(&pos, "?")) return NewAST(ctx->file, lhs->start, pos, Optional, .value = lhs); - else return NULL; -} - -ast_t *parse_non_optional_suffix(parse_ctx_t *ctx, ast_t *lhs) { - if (!lhs) return NULL; - const char *pos = lhs->end; - if (match(&pos, "!")) return NewAST(ctx->file, lhs->start, pos, NonOptional, .value = lhs); - else return NULL; -} - ast_t *parse_reduction(parse_ctx_t *ctx, const char *pos) { const char *start = pos; if (!match(&pos, "(")) return NULL; @@ -107,70 +79,6 @@ ast_t *parse_reduction(parse_ctx_t *ctx, const char *pos) { return NewAST(ctx->file, start, pos, Reduction, .iter = iter, .op = op, .key = key); } -ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs) { - if (!lhs) return NULL; - const char *start = lhs->start; - const char *pos = lhs->end; - if (!match(&pos, "[")) return NULL; - whitespace(&pos); - ast_t *index = optional(ctx, &pos, parse_extended_expr); - whitespace(&pos); - bool unchecked = match(&pos, ";") && (spaces(&pos), match_word(&pos, "unchecked") != 0); - expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this index"); - return NewAST(ctx->file, start, pos, Index, .indexed = lhs, .index = index, .unchecked = unchecked); -} - -ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *expr) { - // "for" [,] "in" ["if" | "unless" ] - if (!expr) return NULL; - const char *start = expr->start; - const char *pos = expr->end; - whitespace(&pos); - if (!match_word(&pos, "for")) return NULL; - - ast_list_t *vars = NULL; - for (;;) { - ast_t *var = optional(ctx, &pos, parse_var); - if (var) vars = new (ast_list_t, .ast = var, .next = vars); - - spaces(&pos); - if (!match(&pos, ",")) break; - } - REVERSE_LIST(vars); - - expect_str(ctx, start, &pos, "in", "I expected an 'in' for this 'for'"); - ast_t *iter = expect(ctx, start, &pos, parse_expr, "I expected an iterable value for this 'for'"); - const char *next_pos = pos; - whitespace(&next_pos); - ast_t *filter = NULL; - if (match_word(&next_pos, "if")) { - pos = next_pos; - filter = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'if'"); - } else if (match_word(&next_pos, "unless")) { - pos = next_pos; - filter = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'unless'"); - filter = WrapAST(filter, Not, filter); - } - return NewAST(ctx->file, start, pos, Comprehension, .expr = expr, .vars = vars, .iter = iter, .filter = filter); -} - -ast_t *parse_optional_conditional_suffix(parse_ctx_t *ctx, ast_t *stmt) { - // "if" | "unless" - if (!stmt) return stmt; - const char *start = stmt->start; - const char *pos = stmt->end; - if (match_word(&pos, "if")) { - ast_t *condition = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'if'"); - return NewAST(ctx->file, start, pos, If, .condition = condition, .body = stmt); - } else if (match_word(&pos, "unless")) { - ast_t *condition = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'unless'"); - condition = WrapAST(condition, Not, condition); - return NewAST(ctx->file, start, pos, If, .condition = condition, .body = stmt); - } else { - return stmt; - } -} - ast_t *parse_if(parse_ctx_t *ctx, const char *pos) { // "if" ["then"] ["else" ] | "unless" ["else" ] const char *start = pos; @@ -500,84 +408,6 @@ ast_t *parse_term(parse_ctx_t *ctx, const char *pos) { return term; } -ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self) { - if (!self) return NULL; - - const char *start = self->start; - const char *pos = self->end; - - if (!match(&pos, ".")) return NULL; - if (*pos == ' ') return NULL; - const char *fn = get_id(&pos); - if (!fn) return NULL; - spaces(&pos); - if (!match(&pos, "(")) return NULL; - whitespace(&pos); - - arg_ast_t *args = NULL; - for (;;) { - const char *arg_start = pos; - const char *name = get_id(&pos); - whitespace(&pos); - if (!name || !match(&pos, "=")) { - name = NULL; - pos = arg_start; - } - - ast_t *arg = optional(ctx, &pos, parse_expr); - if (!arg) { - if (name) parser_err(ctx, arg_start, pos, "I expected an argument here"); - break; - } - args = new (arg_ast_t, .name = name, .value = arg, .next = args); - if (!match_separator(&pos)) break; - } - REVERSE_LIST(args); - - whitespace(&pos); - - if (!match(&pos, ")")) parser_err(ctx, start, pos, "This parenthesis is unclosed"); - - return NewAST(ctx->file, start, pos, MethodCall, .self = self, .name = fn, .args = args); -} - -ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn) { - if (!fn) return NULL; - - const char *start = fn->start; - const char *pos = fn->end; - - if (!match(&pos, "(")) return NULL; - - whitespace(&pos); - - arg_ast_t *args = NULL; - for (;;) { - const char *arg_start = pos; - const char *name = get_id(&pos); - whitespace(&pos); - if (!name || !match(&pos, "=")) { - name = NULL; - pos = arg_start; - } - - ast_t *arg = optional(ctx, &pos, parse_expr); - if (!arg) { - if (name) parser_err(ctx, arg_start, pos, "I expected an argument here"); - break; - } - args = new (arg_ast_t, .name = name, .value = arg, .next = args); - if (!match_separator(&pos)) break; - } - - whitespace(&pos); - - if (!match(&pos, ")")) parser_err(ctx, start, pos, "This parenthesis is unclosed"); - - REVERSE_LIST(args); - return NewAST(ctx->file, start, pos, FunctionCall, .fn = fn, .args = args); -} - ast_t *parse_expr(parse_ctx_t *ctx, const char *pos) { return parse_infix_expr(ctx, pos, 0); } ast_t *parse_declaration(parse_ctx_t *ctx, const char *pos) { diff --git a/src/parse/parse.h b/src/parse/parse.h index 8af16e1b..57d798ce 100644 --- a/src/parse/parse.h +++ b/src/parse/parse.h @@ -8,14 +8,6 @@ ast_t *parse(const char *str); ast_t *parse_expression(const char *str); -ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *expr); -ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs); -ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn); -ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs); -ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self); -ast_t *parse_non_optional_suffix(parse_ctx_t *ctx, ast_t *lhs); -ast_t *parse_optional_conditional_suffix(parse_ctx_t *ctx, ast_t *stmt); -ast_t *parse_optional_suffix(parse_ctx_t *ctx, ast_t *lhs); ast_t *parse_assignment(parse_ctx_t *ctx, const char *pos); ast_t *parse_block(parse_ctx_t *ctx, const char *pos); ast_t *parse_bool(parse_ctx_t *ctx, const char *pos); diff --git a/src/parse/suffixes.c b/src/parse/suffixes.c new file mode 100644 index 00000000..77c61830 --- /dev/null +++ b/src/parse/suffixes.c @@ -0,0 +1,189 @@ +// Logic for parsing various suffixes that can go after an expression + +#include +#include + +#include "../ast.h" +#include "../stdlib/print.h" +#include "../stdlib/util.h" +#include "context.h" +#include "errors.h" +#include "parse.h" +#include "utils.h" + +public +ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs) { + if (!lhs) return NULL; + const char *pos = lhs->end; + whitespace(&pos); + if (!match(&pos, ".")) return NULL; + if (*pos == '.') return NULL; + whitespace(&pos); + bool dollar = match(&pos, "$"); + const char *field = get_id(&pos); + if (!field) return NULL; + if (dollar) field = String("$", field); + return NewAST(ctx->file, lhs->start, pos, FieldAccess, .fielded = lhs, .field = field); +} + +public +ast_t *parse_optional_suffix(parse_ctx_t *ctx, ast_t *lhs) { + if (!lhs) return NULL; + const char *pos = lhs->end; + if (match(&pos, "?")) return NewAST(ctx->file, lhs->start, pos, Optional, .value = lhs); + else return NULL; +} + +public +ast_t *parse_non_optional_suffix(parse_ctx_t *ctx, ast_t *lhs) { + if (!lhs) return NULL; + const char *pos = lhs->end; + if (match(&pos, "!")) return NewAST(ctx->file, lhs->start, pos, NonOptional, .value = lhs); + else return NULL; +} + +public +ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs) { + if (!lhs) return NULL; + const char *start = lhs->start; + const char *pos = lhs->end; + if (!match(&pos, "[")) return NULL; + whitespace(&pos); + ast_t *index = optional(ctx, &pos, parse_extended_expr); + whitespace(&pos); + bool unchecked = match(&pos, ";") && (spaces(&pos), match_word(&pos, "unchecked") != 0); + expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this index"); + return NewAST(ctx->file, start, pos, Index, .indexed = lhs, .index = index, .unchecked = unchecked); +} + +public +ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *expr) { + // "for" [,] "in" ["if" | "unless" ] + if (!expr) return NULL; + const char *start = expr->start; + const char *pos = expr->end; + whitespace(&pos); + if (!match_word(&pos, "for")) return NULL; + + ast_list_t *vars = NULL; + for (;;) { + ast_t *var = optional(ctx, &pos, parse_var); + if (var) vars = new (ast_list_t, .ast = var, .next = vars); + + spaces(&pos); + if (!match(&pos, ",")) break; + } + REVERSE_LIST(vars); + + expect_str(ctx, start, &pos, "in", "I expected an 'in' for this 'for'"); + ast_t *iter = expect(ctx, start, &pos, parse_expr, "I expected an iterable value for this 'for'"); + const char *next_pos = pos; + whitespace(&next_pos); + ast_t *filter = NULL; + if (match_word(&next_pos, "if")) { + pos = next_pos; + filter = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'if'"); + } else if (match_word(&next_pos, "unless")) { + pos = next_pos; + filter = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'unless'"); + filter = WrapAST(filter, Not, filter); + } + return NewAST(ctx->file, start, pos, Comprehension, .expr = expr, .vars = vars, .iter = iter, .filter = filter); +} + +public +ast_t *parse_optional_conditional_suffix(parse_ctx_t *ctx, ast_t *stmt) { + // "if" | "unless" + if (!stmt) return stmt; + const char *start = stmt->start; + const char *pos = stmt->end; + if (match_word(&pos, "if")) { + ast_t *condition = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'if'"); + return NewAST(ctx->file, start, pos, If, .condition = condition, .body = stmt); + } else if (match_word(&pos, "unless")) { + ast_t *condition = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'unless'"); + condition = WrapAST(condition, Not, condition); + return NewAST(ctx->file, start, pos, If, .condition = condition, .body = stmt); + } else { + return stmt; + } +} + +public +ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self) { + if (!self) return NULL; + + const char *start = self->start; + const char *pos = self->end; + + if (!match(&pos, ".")) return NULL; + if (*pos == ' ') return NULL; + const char *fn = get_id(&pos); + if (!fn) return NULL; + spaces(&pos); + if (!match(&pos, "(")) return NULL; + whitespace(&pos); + + arg_ast_t *args = NULL; + for (;;) { + const char *arg_start = pos; + const char *name = get_id(&pos); + whitespace(&pos); + if (!name || !match(&pos, "=")) { + name = NULL; + pos = arg_start; + } + + ast_t *arg = optional(ctx, &pos, parse_expr); + if (!arg) { + if (name) parser_err(ctx, arg_start, pos, "I expected an argument here"); + break; + } + args = new (arg_ast_t, .name = name, .value = arg, .next = args); + if (!match_separator(&pos)) break; + } + REVERSE_LIST(args); + + whitespace(&pos); + + if (!match(&pos, ")")) parser_err(ctx, start, pos, "This parenthesis is unclosed"); + + return NewAST(ctx->file, start, pos, MethodCall, .self = self, .name = fn, .args = args); +} + +ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn) { + if (!fn) return NULL; + + const char *start = fn->start; + const char *pos = fn->end; + + if (!match(&pos, "(")) return NULL; + + whitespace(&pos); + + arg_ast_t *args = NULL; + for (;;) { + const char *arg_start = pos; + const char *name = get_id(&pos); + whitespace(&pos); + if (!name || !match(&pos, "=")) { + name = NULL; + pos = arg_start; + } + + ast_t *arg = optional(ctx, &pos, parse_expr); + if (!arg) { + if (name) parser_err(ctx, arg_start, pos, "I expected an argument here"); + break; + } + args = new (arg_ast_t, .name = name, .value = arg, .next = args); + if (!match_separator(&pos)) break; + } + + whitespace(&pos); + + if (!match(&pos, ")")) parser_err(ctx, start, pos, "This parenthesis is unclosed"); + + REVERSE_LIST(args); + return NewAST(ctx->file, start, pos, FunctionCall, .fn = fn, .args = args); +} diff --git a/src/parse/suffixes.h b/src/parse/suffixes.h new file mode 100644 index 00000000..da2f23a2 --- /dev/null +++ b/src/parse/suffixes.h @@ -0,0 +1,14 @@ +// Logic for parsing various suffixes that can go after an expression +#pragma once + +#include "../ast.h" +#include "context.h" + +ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *expr); +ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs); +ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn); +ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs); +ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self); +ast_t *parse_non_optional_suffix(parse_ctx_t *ctx, ast_t *lhs); +ast_t *parse_optional_conditional_suffix(parse_ctx_t *ctx, ast_t *stmt); +ast_t *parse_optional_suffix(parse_ctx_t *ctx, ast_t *lhs); -- cgit v1.2.3