285 lines
9.4 KiB
C
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;
|
|
}
|