libropkg: implement package list expression parsing

This commit is contained in:
2025-08-06 22:05:35 +01:00
parent 46c6a66e44
commit f35ade11d6
3 changed files with 719 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
#ifndef ROPKG_PKG_EXPR_H_
#define ROPKG_PKG_EXPR_H_
#include <ropkg/misc.h>
#include <ropkg/status.h>
enum ropkg_pkg_expr_type {
ROPKG_PKG_EXPR_NODE_PKG,
ROPKG_PKG_EXPR_NODE_AND,
ROPKG_PKG_EXPR_NODE_OR,
ROPKG_PKG_EXPR_NODE_GROUP,
};
enum ropkg_comparison_op;
struct ropkg_version;
struct ropkg_pkg_expr;
struct ropkg_pkg_expr_pkg;
struct ropkg_pkg_expr_and;
struct ropkg_pkg_expr_or;
ROPKG_API void ropkg_pkg_expr_destroy(struct ropkg_pkg_expr *expr);
ROPKG_API b_result
ropkg_pkg_expr_parse(const char *s, struct ropkg_pkg_expr **out);
ROPKG_API enum ropkg_pkg_expr_type ropkg_pkg_expr_get_type(
const struct ropkg_pkg_expr *expr);
ROPKG_API struct ropkg_pkg_expr_pkg *ropkg_pkg_expr_pkg_create(void);
ROPKG_API const char *ropkg_pkg_expr_pkg_get_name(
const struct ropkg_pkg_expr_pkg *expr);
ROPKG_API enum ropkg_comparison_op ropkg_pkg_expr_pkg_get_version_predicate(
const struct ropkg_pkg_expr_pkg *expr);
ROPKG_API const struct ropkg_version *ropkg_pkg_expr_pkg_get_version(
const struct ropkg_pkg_expr_pkg *expr);
ROPKG_API struct ropkg_pkg_expr_and *ropkg_pkg_expr_and_create(void);
ROPKG_API const struct ropkg_pkg_expr *ropkg_pkg_expr_and_get_left_node(
const struct ropkg_pkg_expr_and *expr);
ROPKG_API const struct ropkg_pkg_expr *ropkg_pkg_expr_and_get_right_node(
const struct ropkg_pkg_expr_and *expr);
ROPKG_API struct ropkg_pkg_expr_or *ropkg_pkg_expr_or_create(void);
ROPKG_API const struct ropkg_pkg_expr *ropkg_pkg_expr_or_get_left_node(
const struct ropkg_pkg_expr_or *expr);
ROPKG_API const struct ropkg_pkg_expr *ropkg_pkg_expr_or_get_right_node(
const struct ropkg_pkg_expr_or *expr);
#endif

639
libropkg/pkg-expr.c Normal file
View File

@@ -0,0 +1,639 @@
#include "pkg-expr.h"
#include <blue/core/error.h>
#include <blue/object/string.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
enum parse_state {
PARSE_NONE = 0,
PARSE_PKG_NAME,
PARSE_PKG_MID,
PARSE_PKG_VERSION,
PARSE_PKG_VERSION_PREDICATE,
PARSE_PKG_VERSION_MID,
PARSE_PKG_VERSION_ID,
PARSE_PKG_END,
PARSE_AND_SEP,
PARSE_OR_SEP,
};
struct parse_ctx {
const char *ctx_str;
b_string *ctx_temp;
enum parse_state ctx_state;
size_t ctx_i;
b_queue ctx_stack, ctx_queue;
};
static struct ropkg_pkg_expr *parse_ctx_peek_stack(struct parse_ctx *ctx)
{
b_queue_entry *entry = b_queue_last(&ctx->ctx_stack);
if (!entry) {
return NULL;
}
return b_unbox(struct ropkg_pkg_expr, entry, expr_entry);
}
static struct ropkg_pkg_expr *parse_ctx_pop(struct parse_ctx *ctx)
{
b_queue_entry *entry = b_queue_pop_back(&ctx->ctx_stack);
if (!entry) {
return NULL;
}
return b_unbox(struct ropkg_pkg_expr, entry, expr_entry);
}
static void parse_ctx_push(struct parse_ctx *ctx, struct ropkg_pkg_expr *expr)
{
b_queue_push_back(&ctx->ctx_stack, &expr->expr_entry);
}
static struct ropkg_pkg_expr *parse_ctx_dequeue(struct parse_ctx *ctx)
{
b_queue_entry *entry = b_queue_pop_front(&ctx->ctx_queue);
if (!entry) {
return NULL;
}
return b_unbox(struct ropkg_pkg_expr, entry, expr_entry);
}
static void parse_ctx_enqueue(
struct parse_ctx *ctx,
struct ropkg_pkg_expr *expr)
{
b_queue_push_back(&ctx->ctx_queue, &expr->expr_entry);
}
static char parse_ctx_peek(struct parse_ctx *ctx)
{
return ctx->ctx_str[ctx->ctx_i];
}
static char parse_ctx_advance(struct parse_ctx *ctx)
{
if (!ctx->ctx_str[ctx->ctx_i]) {
return 0;
}
ctx->ctx_i++;
while (1) {
char c = parse_ctx_peek(ctx);
if (isspace(c)) {
ctx->ctx_i++;
continue;
}
break;
}
return ctx->ctx_str[ctx->ctx_i];
}
static void parse_ctx_cleanup(struct parse_ctx *ctx)
{
if (ctx->ctx_temp) {
b_string_release(ctx->ctx_temp);
}
struct ropkg_pkg_expr *expr = parse_ctx_dequeue(ctx);
while (expr) {
ropkg_pkg_expr_destroy(expr);
expr = parse_ctx_dequeue(ctx);
}
expr = parse_ctx_pop(ctx);
while (expr) {
ropkg_pkg_expr_destroy(expr);
expr = parse_ctx_pop(ctx);
}
}
enum ropkg_status ropkg_pkg_expr_create(struct ropkg_pkg_expr **out)
{
return ROPKG_SUCCESS;
}
void ropkg_pkg_expr_destroy(struct ropkg_pkg_expr *expr)
{
b_queue q = B_QUEUE_INIT;
b_queue_push_back(&q, &expr->expr_entry);
while (1) {
b_queue_entry *entry = b_queue_pop_front(&q);
if (!entry) {
break;
}
expr = b_unbox(struct ropkg_pkg_expr, entry, expr_entry);
switch (expr->expr_type) {
case ROPKG_PKG_EXPR_NODE_PKG: {
struct ropkg_pkg_expr_pkg *pkg_node
= (struct ropkg_pkg_expr_pkg *)expr;
if (pkg_node->pkg_name) {
free(pkg_node->pkg_name);
}
if (pkg_node->pkg_version) {
ropkg_version_destroy(pkg_node->pkg_version);
}
break;
case ROPKG_PKG_EXPR_NODE_AND: {
struct ropkg_pkg_expr_and *and_node
= (struct ropkg_pkg_expr_and *)expr;
if (and_node->and_left) {
b_queue_push_back(
&q,
&and_node->and_left->expr_entry);
}
if (and_node->and_right) {
b_queue_push_back(
&q,
&and_node->and_right->expr_entry);
}
break;
}
case ROPKG_PKG_EXPR_NODE_OR: {
struct ropkg_pkg_expr_or *or_node
= (struct ropkg_pkg_expr_or *)expr;
if (or_node->or_left) {
b_queue_push_back(
&q,
&or_node->or_left->expr_entry);
}
if (or_node->or_right) {
b_queue_push_back(
&q,
&or_node->or_right->expr_entry);
}
break;
}
default:
break;
}
}
free(expr);
}
}
static b_result parse_pkg_name(
struct parse_ctx *ctx,
struct ropkg_pkg_expr_pkg *pkg)
{
b_string_clear(ctx->ctx_temp);
while (1) {
char c = parse_ctx_peek(ctx);
if (!isalnum(c) && c != '+' && c != '-' && c != '_') {
break;
}
char s[] = {c, 0};
b_string_append_cstr(ctx->ctx_temp, s);
parse_ctx_advance(ctx);
}
pkg->pkg_name = b_string_steal(ctx->ctx_temp);
return B_RESULT_SUCCESS;
}
static b_result parse_pkg_version_predicate(
struct parse_ctx *ctx,
struct ropkg_pkg_expr_pkg *pkg)
{
b_string_clear(ctx->ctx_temp);
while (1) {
char c = parse_ctx_peek(ctx);
if (!ispunct(c)) {
break;
}
char s[] = {c, 0};
b_string_append_cstr(ctx->ctx_temp, s);
parse_ctx_advance(ctx);
}
const char *pred = b_string_ptr(ctx->ctx_temp);
if (!strcmp(pred, ">=")) {
pkg->pkg_version_predicate = ROPKG_OP_GREATER_EQUAL;
} else if (!strcmp(pred, ">")) {
pkg->pkg_version_predicate = ROPKG_OP_GREATER_THAN;
} else if (!strcmp(pred, "<=")) {
pkg->pkg_version_predicate = ROPKG_OP_LESS_EQUAL;
} else if (!strcmp(pred, "<")) {
pkg->pkg_version_predicate = ROPKG_OP_LESS_THAN;
} else if (!strcmp(pred, "==") || !strcmp(pred, "=")) {
pkg->pkg_version_predicate = ROPKG_OP_EQUAL;
} else {
return b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
}
return B_RESULT_SUCCESS;
}
static b_result parse_pkg_version_id(
struct parse_ctx *ctx,
struct ropkg_pkg_expr_pkg *pkg)
{
b_string_clear(ctx->ctx_temp);
while (1) {
char c = parse_ctx_peek(ctx);
if (!isalnum(c) && c != '.' && c != '-' && c != '~') {
break;
}
char s[] = {c, 0};
b_string_append_cstr(ctx->ctx_temp, s);
parse_ctx_advance(ctx);
}
struct ropkg_version *version = NULL;
enum ropkg_status status = ropkg_version_create(&version);
if (status != ROPKG_SUCCESS) {
return b_error_with_code(ROPKG_ERROR_VENDOR, status);
}
b_result result
= ropkg_version_parse(version, b_string_ptr(ctx->ctx_temp));
if (b_result_is_error(result)) {
ropkg_version_destroy(version);
version = NULL;
}
pkg->pkg_version = version;
return b_result_propagate(result);
}
static b_result parse_pkg_version(
struct parse_ctx *ctx,
struct ropkg_pkg_expr_pkg *pkg)
{
parse_ctx_advance(ctx);
b_result result = B_RESULT_SUCCESS;
char c = parse_ctx_peek(ctx);
if (c == '>' || c == '<' || c == '=') {
result = parse_pkg_version_predicate(ctx, pkg);
}
if (b_result_is_error(result)) {
return result;
}
c = parse_ctx_peek(ctx);
if (isalnum(c)) {
result = parse_pkg_version_id(ctx, pkg);
} else {
result = b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
}
c = parse_ctx_peek(ctx);
if (c == ')') {
parse_ctx_advance(ctx);
} else {
result = b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
}
return result;
}
static b_result parse_pkg(struct parse_ctx *ctx)
{
struct ropkg_pkg_expr_pkg *pkg = ropkg_pkg_expr_pkg_create();
b_result result = B_RESULT_SUCCESS;
char c = parse_ctx_peek(ctx);
if (isalpha(c)) {
result = parse_pkg_name(ctx, pkg);
}
if (b_result_is_error(result)) {
ropkg_pkg_expr_destroy((struct ropkg_pkg_expr *)pkg);
return b_result_propagate(result);
}
c = parse_ctx_peek(ctx);
if (c == '(') {
result = parse_pkg_version(ctx, pkg);
}
if (b_result_is_error(result)) {
ropkg_pkg_expr_destroy((struct ropkg_pkg_expr *)pkg);
return b_result_propagate(result);
}
parse_ctx_enqueue(ctx, &pkg->pkg_base);
return result;
}
static b_result parse_and(struct parse_ctx *ctx)
{
parse_ctx_advance(ctx);
struct ropkg_pkg_expr_and *and_node = ropkg_pkg_expr_and_create();
while (1) {
struct ropkg_pkg_expr *top = parse_ctx_peek_stack(ctx);
if (!top) {
break;
}
if (top->expr_type == ROPKG_PKG_EXPR_NODE_GROUP) {
break;
}
if (top->expr_type < and_node->and_base.expr_type) {
break;
}
top = parse_ctx_pop(ctx);
parse_ctx_enqueue(ctx, top);
}
parse_ctx_push(ctx, &and_node->and_base);
return B_RESULT_SUCCESS;
}
static b_result parse_or(struct parse_ctx *ctx)
{
parse_ctx_advance(ctx);
struct ropkg_pkg_expr_or *or_node = ropkg_pkg_expr_or_create();
while (1) {
struct ropkg_pkg_expr *top = parse_ctx_peek_stack(ctx);
if (!top) {
break;
}
if (top->expr_type < or_node->or_base.expr_type) {
break;
}
top = parse_ctx_pop(ctx);
parse_ctx_enqueue(ctx, top);
}
parse_ctx_push(ctx, &or_node->or_base);
return B_RESULT_SUCCESS;
}
static b_result parse_group_start(struct parse_ctx *ctx)
{
parse_ctx_advance(ctx);
struct ropkg_pkg_expr *group_node = malloc(sizeof *group_node);
if (!group_node) {
return b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_NO_MEMORY);
}
memset(group_node, 0x0, sizeof *group_node);
group_node->expr_type = ROPKG_PKG_EXPR_NODE_GROUP;
parse_ctx_push(ctx, group_node);
return B_RESULT_SUCCESS;
}
static b_result parse_group_end(struct parse_ctx *ctx)
{
parse_ctx_advance(ctx);
bool done = false;
while (!done) {
struct ropkg_pkg_expr *expr = parse_ctx_pop(ctx);
if (!expr) {
return b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
}
switch (expr->expr_type) {
case ROPKG_PKG_EXPR_NODE_GROUP:
free(expr);
done = true;
break;
default:
parse_ctx_enqueue(ctx, expr);
break;
}
}
return B_RESULT_SUCCESS;
}
b_result ropkg_pkg_expr_parse(const char *s, struct ropkg_pkg_expr **out)
{
while (isspace(*s)) {
s++;
}
struct parse_ctx ctx = {
.ctx_str = s,
.ctx_temp = b_string_create(),
};
b_result result = B_RESULT_SUCCESS;
while (!b_result_is_error(result)) {
char c = parse_ctx_peek(&ctx);
if (c == 0) {
break;
}
if (c == '(') {
result = parse_group_start(&ctx);
} else if (c == ')') {
result = parse_group_end(&ctx);
} else if (c == ',') {
result = parse_and(&ctx);
} else if (c == '|') {
result = parse_or(&ctx);
} else if (isalpha(c)) {
result = parse_pkg(&ctx);
} else {
result = b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
break;
}
}
if (b_result_is_error(result)) {
parse_ctx_cleanup(&ctx);
return result;
}
struct ropkg_pkg_expr *expr = parse_ctx_pop(&ctx);
while (expr) {
parse_ctx_enqueue(&ctx, expr);
expr = parse_ctx_pop(&ctx);
}
while (1) {
expr = parse_ctx_dequeue(&ctx);
if (!expr) {
break;
}
if (expr->expr_type == ROPKG_PKG_EXPR_NODE_PKG) {
parse_ctx_push(&ctx, expr);
continue;
}
if (expr->expr_type == ROPKG_PKG_EXPR_NODE_GROUP) {
parse_ctx_cleanup(&ctx);
return b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
}
struct ropkg_pkg_expr *left = NULL, *right = NULL;
right = parse_ctx_pop(&ctx);
left = parse_ctx_pop(&ctx);
if (!left || !right) {
parse_ctx_cleanup(&ctx);
return b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
}
switch (expr->expr_type) {
case ROPKG_PKG_EXPR_NODE_AND: {
struct ropkg_pkg_expr_and *and_node
= (struct ropkg_pkg_expr_and *)expr;
and_node->and_left = left;
and_node->and_right = right;
break;
}
case ROPKG_PKG_EXPR_NODE_OR: {
struct ropkg_pkg_expr_or *or_node
= (struct ropkg_pkg_expr_or *)expr;
or_node->or_left = left;
or_node->or_right = right;
break;
}
default:
break;
}
parse_ctx_push(&ctx, expr);
}
*out = parse_ctx_pop(&ctx);
parse_ctx_cleanup(&ctx);
return result;
}
enum ropkg_pkg_expr_type ropkg_pkg_expr_get_type(
const struct ropkg_pkg_expr *expr)
{
return expr->expr_type;
}
struct ropkg_pkg_expr_pkg *ropkg_pkg_expr_pkg_create(void)
{
struct ropkg_pkg_expr_pkg *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->pkg_base.expr_type = ROPKG_PKG_EXPR_NODE_PKG;
return out;
}
const char *ropkg_pkg_expr_pkg_get_name(const struct ropkg_pkg_expr_pkg *expr)
{
return expr->pkg_name;
}
enum ropkg_comparison_op ropkg_pkg_expr_pkg_get_version_predicate(
const struct ropkg_pkg_expr_pkg *expr)
{
return expr->pkg_version_predicate;
}
const struct ropkg_version *ropkg_pkg_expr_pkg_get_version(
const struct ropkg_pkg_expr_pkg *expr)
{
return expr->pkg_version;
}
struct ropkg_pkg_expr_and *ropkg_pkg_expr_and_create(void)
{
struct ropkg_pkg_expr_and *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->and_base.expr_type = ROPKG_PKG_EXPR_NODE_AND;
return out;
}
const struct ropkg_pkg_expr *ropkg_pkg_expr_and_get_left_node(
const struct ropkg_pkg_expr_and *expr)
{
return expr->and_left;
}
const struct ropkg_pkg_expr *ropkg_pkg_expr_and_get_right_node(
const struct ropkg_pkg_expr_and *expr)
{
return expr->and_right;
}
struct ropkg_pkg_expr_or *ropkg_pkg_expr_or_create(void)
{
struct ropkg_pkg_expr_or *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->or_base.expr_type = ROPKG_PKG_EXPR_NODE_OR;
return out;
}
const struct ropkg_pkg_expr *ropkg_pkg_expr_or_get_left_node(
const struct ropkg_pkg_expr_or *expr)
{
return expr->or_left;
}
const struct ropkg_pkg_expr *ropkg_pkg_expr_or_get_right_node(
const struct ropkg_pkg_expr_or *expr)
{
return expr->or_right;
}

29
libropkg/pkg-expr.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef _PKG_EXPR_H_
#define _PKG_EXPR_H_
#include <ropkg/pkg-expr.h>
#include <ropkg/version.h>
struct ropkg_pkg_expr {
enum ropkg_pkg_expr_type expr_type;
b_queue_entry expr_entry;
};
struct ropkg_pkg_expr_pkg {
struct ropkg_pkg_expr pkg_base;
char *pkg_name;
enum ropkg_comparison_op pkg_version_predicate;
struct ropkg_version *pkg_version;
};
struct ropkg_pkg_expr_and {
struct ropkg_pkg_expr and_base;
struct ropkg_pkg_expr *and_left, *and_right;
};
struct ropkg_pkg_expr_or {
struct ropkg_pkg_expr or_base;
struct ropkg_pkg_expr *or_left, *or_right;
};
#endif