123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 | // Logic for parsing various suffixes that can go after an expression
#include <stdbool.h>
#include "../ast.h"
#include "../stdlib/print.h"
#include "../stdlib/util.h"
#include "context.h"
#include "errors.h"
#include "expressions.h"
#include "utils.h"
ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs) {
if (!lhs) return NULL;
const char *pos = lhs->end;
whitespace(ctx, &pos);
if (!match(&pos, ".")) return NULL;
if (*pos == '.') return NULL;
whitespace(ctx, &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_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_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(ctx, &pos);
ast_t *index = optional(ctx, &pos, parse_extended_expr);
whitespace(ctx, &pos);
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);
}
ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *expr) {
// <expr> "for" [<index>,]<var> "in" <iter> ["if" <condition> | "unless" <condition>]
if (!expr) return NULL;
const char *start = expr->start;
const char *pos = expr->end;
whitespace(ctx, &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(ctx, &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) {
// <statement> "if" <condition> | <statement> "unless" <condition>
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_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(ctx, &pos);
arg_ast_t *args = NULL;
for (;;) {
const char *arg_start = pos;
const char *name = get_id(&pos);
whitespace(ctx, &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, .start = arg_start, .end = arg->end, .name = name, .value = arg, .next = args);
if (!match_separator(ctx, &pos)) break;
}
REVERSE_LIST(args);
whitespace(ctx, &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(ctx, &pos);
arg_ast_t *args = NULL;
for (;;) {
const char *arg_start = pos;
const char *name = get_id(&pos);
whitespace(ctx, &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(ctx, &pos)) break;
}
whitespace(ctx, &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);
}
|