vessel/web/temple-expr.c
2025-09-06 22:03:31 +03:00

285 lines
9.4 KiB
C

#include "include/conf.h"
#include <ctype.h>
#include "include/temple-expr.h"
#include "include/temple-tokens.h"
static inline vw_TempleASTNode *vw_TempleParser_parse_and_check(
vw_TempleASTNode *ast,
vw_TempleASTNode *(*parse_func)(vw_TempleASTNode *, vs_Logger *, const char **, const char *),
vs_Logger *logger,
const char **start,
const char *end);
static inline vw_TempleASTNode *
vw_TempleParser_parse_expr_lhs(vs_Logger *logger, const char **startp, const char *end);
static inline vw_TempleASTNode *vw_TempleParser_parse_expr_rhs(uint8_t min_prec,
vw_TempleASTNode *ast,
vs_Logger *logger,
const char **startp,
const char *end);
static inline vw_TempleASTNode *vw_TempleParser_parse_expr_cond(vw_TempleASTNode *ast,
vs_Logger *logger,
const char **startp,
const char *end);
static inline vw_TempleASTNode *vw_TempleParser_parse_expr_range(vw_TempleASTNode *ast,
vs_Logger *logger,
const char **startp,
const char *end);
static inline vw_TempleASTNode *
vw_TempleParser_parse_expr_unary(vs_Logger *logger, const char **startp, const char *end);
static inline vw_TempleASTNode *
vw_TempleParser_parse_expr_primary(vs_Logger *logger, const char **startp, const char *end);
static inline vw_TempleASTNode *vw_TempleParser_parse_expr_postfix(vw_TempleASTNode *operand,
vs_Logger *logger,
const char **startp,
const char *end);
vw_TempleASTNode *
vw_TempleParser_parse_expr(vs_Logger *logger, const char *start, const char *end) {
if (!logger || !start || !*start || !end || !*end || start >= end) {
return NULL;
}
vw_TempleASTNode *ast = vw_TempleParser_parse_expr_lhs(logger, &start, end);
if (!ast) {
return NULL;
}
vw_TempleASTNode *aux = vw_TempleParser_parse_expr_rhs(1, ast, logger, &start, end);
if (!aux) {
vw_TempleASTNode_free(ast);
return NULL;
}
ast = aux;
ast =
vw_TempleParser_parse_and_check(ast, vw_TempleParser_parse_expr_cond, logger, &start, end);
if (!ast) {
return NULL;
}
ast =
vw_TempleParser_parse_and_check(ast, vw_TempleParser_parse_expr_range, logger, &start, end);
if (!ast) {
return NULL;
}
if (start != end) {
vw_TempleASTNode_free(ast);
return NULL;
}
return ast;
}
static inline vw_TempleASTNode *vw_TempleParser_parse_and_check(
vw_TempleASTNode *ast,
vw_TempleASTNode *(*parse_func)(vw_TempleASTNode *, vs_Logger *, const char **, const char *),
vs_Logger *logger,
const char **start,
const char *end) {
vw_TempleASTNode *aux = parse_func(ast, logger, start, end);
if (!aux) {
vw_TempleASTNode_free(ast);
return NULL;
}
return aux;
}
static inline vw_TempleASTNode *
vw_TempleParser_parse_expr_lhs(vs_Logger *logger, const char **startp, const char *end) {
vw_TempleASTNode *node = vw_TempleParser_parse_expr_unary(logger, startp, end);
if (node) {
return node;
}
node = vw_TempleParser_parse_expr_primary(logger, startp, end);
if (!node) {
vs_flog_error(
logger, "Failed to find primary expression at position %zu", (size_t)(end - *startp));
return NULL;
}
vw_TempleASTNode *lhs = vw_TempleParser_parse_expr_postfix(node, logger, startp, end);
if (!lhs) {
vs_flog_error(
logger, "Failed to parse postfix expression at position %zu", (size_t)(end - *startp));
vw_TempleASTNode_free(node);
return NULL;
}
return lhs;
}
static inline vw_TempleASTNode *vw_TempleParser_parse_expr_rhs(uint8_t min_prec,
vw_TempleASTNode *ast,
vs_Logger *logger,
const char **startp,
const char *end) {
(void)min_prec;
(void)ast;
(void)logger;
(void)startp;
(void)end;
return NULL;
}
static inline vw_TempleASTNode *vw_TempleParser_parse_expr_cond(vw_TempleASTNode *ast,
vs_Logger *logger,
const char **startp,
const char *end) {
(void)ast;
(void)logger;
(void)startp;
(void)end;
return NULL;
}
static inline vw_TempleASTNode *vw_TempleParser_parse_expr_range(vw_TempleASTNode *ast,
vs_Logger *logger,
const char **startp,
const char *end) {
(void)ast;
(void)logger;
(void)startp;
(void)end;
return NULL;
}
static inline vw_TempleASTNode *
vw_TempleParser_parse_expr_unary(vs_Logger *logger, const char **startp, const char *end) {
if (!vs_lstrip_spaces(startp, end)) {
vs_flog_error(logger,
"Failed to left strip unary expression at position %zu",
(size_t)(end - *startp));
return NULL;
}
const char *start = *startp;
bool is_unary = true;
vw_TempleASTUnaryOperator operator = 0;
switch (*start) {
case '-':
operator = vw_TempleASTUnaryOperator_neg;
++start;
break;
case '+':
operator = vw_TempleASTUnaryOperator_pos;
++start;
break;
case '!':
operator = vw_TempleASTUnaryOperator_not;
++start;
break;
case '~':
operator = vw_TempleASTUnaryOperator_bnot;
++start;
break;
case '*':
operator = vw_TempleASTUnaryOperator_spread;
++start;
if (start < end && *start == '*') {
operator = vw_TempleASTUnaryOperator_kspread;
++start;
}
break;
default:
is_unary = false;
break;
}
if (!is_unary) {
return NULL;
}
vw_TempleASTNode *operand = vw_TempleParser_parse_expr_lhs(logger, &start, end);
if (!operand) {
vs_flog_error(logger,
"Failed to parse left-hand unary operand at position %zu",
(size_t)(end - *startp));
return NULL;
}
vw_TempleASTNode *node = vw_TempleASTNode_new_unary_expr(operator, operand);
if (node) {
vs_flog_error(logger,
"Failed to parse unary postfix expression at position %zu",
(size_t)(end - *startp));
*startp = start;
}
return node;
}
static inline vw_TempleASTNode *
vw_TempleParser_parse_expr_primary(vs_Logger *logger, const char **startp, const char *end) {
if (!vs_lstrip_spaces(startp, end)) {
vs_flog_error(logger,
"Failed to left strip primary expression at position %zu",
(size_t)(end - *startp));
return NULL;
}
const char *start = *startp;
if (*start == ':') {
return parse_range(NULL, startp, end);
}
if (isdigit(*start)) {
return parse_number(startp, end);
}
if (*start == '[') {
return parse_array(startp, end);
}
if (*start == '{') {
return parse_hashmap(startp, end);
}
if (*start == '"' || ((**p == 'r' || **p == 'f' || **p == 'R' || **p == 'F') && (*p)[1] == '"')) {
return parse_string(startp, end);
}
if (isalpha(*start) || *start == '_' || *start == '$') {
return parse_identifier(startp, end);
}
/* Parenthesised expression */
if (*start == '(') {
++start; /* Skip initial '(' */
int paren_depth = 1;
while (*start < end && paren_depth > 0) {
if (**p == '(') {
++paren_depth;
} else if (**p == ')') {
--paren_depth;
}
++(*p);
}
if (paren_depth != 0) {
return NULL; /* Unbalanced parentheses */
}
const char *subexpr_end = *p - 1; /* Points to the matching ')' */
/* Parse the subexpression */
const char *subexpr_ptr = start;
ASTNode *expr = parse_expression(&subexpr_ptr, subexpr_end);
if (!expr) {
return NULL;
}
skip_whitespace(p, end);
return expr;
}
return NULL;
}