275 lines
10 KiB
Text
275 lines
10 KiB
Text
%prefix "xce_select"
|
|
%value "xce_select_node_t *"
|
|
%auxil "select_context_t *"
|
|
%source {
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "xstring.h"
|
|
|
|
#define PCC_ERROR(auxil) xce_select_parse_error(auxil)
|
|
|
|
static void xce_select_parse_error(select_context_t *auxil) {
|
|
auxil->has_error = 1;
|
|
}
|
|
|
|
/* Resolve a resource name: clause-keyword check then resmgr lookup.
|
|
* Returns a SEL_RESNAME node on success, NULL with has_error set on failure. */
|
|
static xce_select_node_t* resolve_resname(select_context_t* auxil,
|
|
const char* name)
|
|
{
|
|
static const char* clause_keywords[] = {
|
|
"select", "rusage", "span", "same", "affinity", NULL
|
|
};
|
|
int i;
|
|
for (i = 0; clause_keywords[i]; i++) {
|
|
if (strcmp(name, clause_keywords[i]) == 0) {
|
|
snprintf(auxil->errmsg, sizeof(auxil->errmsg),
|
|
"resource name '%s' conflicts with a clause keyword — "
|
|
"use explicit select[%s ...] form if this is intentional",
|
|
name, name);
|
|
auxil->has_error = 1;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
xce_resource_t* r = NULL;
|
|
lock(auxil->resmgr, RTTI_LOCK_READ);
|
|
int rc = key_find(auxil->resmgr, (void*)name, (void**)&r);
|
|
unlock(auxil->resmgr);
|
|
if (rc <= 0 || r == NULL) {
|
|
if (strcmp(name, "true") == 0 || strcmp(name, "false") == 0) {
|
|
snprintf(auxil->errmsg, sizeof(auxil->errmsg),
|
|
"unknown resource '%s' — did you mean the boolean resource "
|
|
"name directly (e.g. 'gpu') or a numeric literal (1/0)?",
|
|
name);
|
|
} else {
|
|
snprintf(auxil->errmsg, sizeof(auxil->errmsg),
|
|
"unknown resource '%s'", name);
|
|
}
|
|
auxil->has_error = 1;
|
|
return NULL;
|
|
}
|
|
res_type_t res_type = valtype_to_res_type(r->valtype);
|
|
return (xce_select_node_t*)makeSelectNode(SEL_RESNAME,
|
|
(uintptr_t)r->idx,
|
|
(uintptr_t)res_type);
|
|
}
|
|
}
|
|
%common {
|
|
#include "kv.h"
|
|
#include "resreq.h"
|
|
|
|
#define PCC_GETCHAR(auxil) \
|
|
({ int c = (auxil->input[auxil->pos] != '\0') ? (int)auxil->input[auxil->pos ++]: -1; \
|
|
if (auxil->pos > auxil->err_pos) auxil->err_pos = auxil->pos; \
|
|
c; \
|
|
})
|
|
}
|
|
|
|
statement <- _ e:select_or _ EOF {
|
|
auxil->root = e;
|
|
$$ = e;
|
|
}
|
|
|
|
select_or <- l:select_and _ "||" _ r:select_or {
|
|
if (auxil->has_error || l == NULL || r == NULL) {
|
|
$$ = NULL;
|
|
} else {
|
|
$$ = (xce_select_node_t*)makeSelectNode(SEL_OR, (uintptr_t)l, (uintptr_t)r);
|
|
if ($$ == NULL) {
|
|
auxil->has_error = 1;
|
|
if (sel_get_optimize_level() == 0) {
|
|
sel_free_tree(l);
|
|
sel_free_tree(r);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/ e:select_and { $$ = e; }
|
|
|
|
select_and <- l:select_not _ "&&" _ r:select_and {
|
|
if (auxil->has_error || l == NULL || r == NULL) {
|
|
$$ = NULL;
|
|
} else {
|
|
$$ = (xce_select_node_t*)makeSelectNode(SEL_AND, (uintptr_t)l, (uintptr_t)r);
|
|
if ($$ == NULL) {
|
|
auxil->has_error = 1;
|
|
if (sel_get_optimize_level() == 0) {
|
|
sel_free_tree(l);
|
|
sel_free_tree(r);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/ e:select_not { $$ = e; }
|
|
|
|
select_not <- "!" _ e:select_primary {
|
|
if (auxil->has_error || e == NULL) {
|
|
$$ = NULL;
|
|
} else {
|
|
$$ = (xce_select_node_t*)makeSelectNode(SEL_NOT, (uintptr_t)e, 0);
|
|
if ($$ == NULL) {
|
|
auxil->has_error = 1;
|
|
if (sel_get_optimize_level() == 0)
|
|
sel_free_tree(e);
|
|
}
|
|
}
|
|
}
|
|
/ e:select_primary { $$ = e; }
|
|
|
|
select_primary <- "(" _ e:select_or _ ")" { $$ = e; }
|
|
/ e:comparison { $$ = e; }
|
|
/ e:resname_leaf { $$ = e; }
|
|
|
|
comparison <- l:resname_node _ op:cmp_op _ r:value_node {
|
|
/* SEL_DISCARD: free a speculatively allocated node that the grammar
|
|
* action decides not to use. Only frees in level-0 (non-CSE) mode;
|
|
* in CSE mode the intern table owns the node and GC reclaims it. */
|
|
#define SEL_DISCARD(n) do { \
|
|
if ((n) != NULL && sel_get_optimize_level() == 0) { \
|
|
if ((n)->type == SEL_STR && (n)->u.strval != NULL) \
|
|
dereference((n)->u.strval); \
|
|
free(n); \
|
|
(n) = NULL; \
|
|
} \
|
|
} while (0)
|
|
if (auxil->has_error || l == NULL || r == NULL) {
|
|
SEL_DISCARD(l);
|
|
SEL_DISCARD(r);
|
|
$$ = NULL;
|
|
} else {
|
|
sel_node_type_t op_type = (sel_node_type_t)(uintptr_t)op;
|
|
if (op_type == SEL_EQ || op_type == SEL_NE) {
|
|
/* 4b-2: EQ/NE type validation */
|
|
if (l->u.resname.res_type == RES_TYPE_BOOL &&
|
|
r->type == SEL_STR) {
|
|
/* BOOL resource vs string literal: reject */
|
|
snprintf(auxil->errmsg, sizeof(auxil->errmsg),
|
|
"type mismatch: boolean resource cannot be compared "
|
|
"with a string literal");
|
|
auxil->has_error = 1;
|
|
SEL_DISCARD(l);
|
|
SEL_DISCARD(r);
|
|
$$ = NULL;
|
|
} else if (l->u.resname.res_type == RES_TYPE_STRING &&
|
|
r->type == SEL_NUM) {
|
|
/* Coerce SEL_NUM → SEL_STR when LHS is STRING type */
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "%.17g", r->u.numval);
|
|
char* s = makeString(buf);
|
|
if (s == NULL) {
|
|
auxil->has_error = 1;
|
|
SEL_DISCARD(l);
|
|
SEL_DISCARD(r);
|
|
$$ = NULL;
|
|
} else {
|
|
if (sel_get_optimize_level() == 0) free(r);
|
|
r = (xce_select_node_t*)makeSelectNode(SEL_STR,
|
|
(uintptr_t)s,
|
|
0);
|
|
if (r == NULL) {
|
|
auxil->has_error = 1;
|
|
SEL_DISCARD(l);
|
|
$$ = NULL;
|
|
} else {
|
|
$$ = (xce_select_node_t*)makeSelectNode(op_type,
|
|
(uintptr_t)l,
|
|
(uintptr_t)r);
|
|
if ($$ == NULL) {
|
|
auxil->has_error = 1;
|
|
SEL_DISCARD(l);
|
|
SEL_DISCARD(r);
|
|
}
|
|
}
|
|
}
|
|
} else if (l->u.resname.res_type == RES_TYPE_NUMERIC &&
|
|
r->type == SEL_STR) {
|
|
/* Numeric resource vs string literal: reject — at eval time
|
|
* is_str_cmp() would return true and simple_str() would read
|
|
* h->strvals[numeric_idx] which is never populated for numeric
|
|
* resources (NULL or garbage), causing a strcmp NULL-deref. */
|
|
snprintf(auxil->errmsg, sizeof(auxil->errmsg),
|
|
"type mismatch: numeric resource cannot be compared "
|
|
"with a string literal");
|
|
auxil->has_error = 1;
|
|
SEL_DISCARD(l);
|
|
SEL_DISCARD(r);
|
|
$$ = NULL;
|
|
} else {
|
|
$$ = (xce_select_node_t*)makeSelectNode(op_type,
|
|
(uintptr_t)l,
|
|
(uintptr_t)r);
|
|
if ($$ == NULL) {
|
|
auxil->has_error = 1;
|
|
SEL_DISCARD(l);
|
|
SEL_DISCARD(r);
|
|
}
|
|
}
|
|
} else {
|
|
/* 4b-3: Ordered comparison (GT/LT/GE/LE) type validation */
|
|
if (l->u.resname.res_type != RES_TYPE_NUMERIC) {
|
|
snprintf(auxil->errmsg, sizeof(auxil->errmsg),
|
|
"type mismatch: ordered comparison requires a numeric "
|
|
"resource on the left-hand side");
|
|
auxil->has_error = 1;
|
|
SEL_DISCARD(l);
|
|
SEL_DISCARD(r);
|
|
$$ = NULL;
|
|
} else if (r->type != SEL_NUM) {
|
|
snprintf(auxil->errmsg, sizeof(auxil->errmsg),
|
|
"type mismatch: ordered comparison requires a numeric "
|
|
"literal on the right-hand side");
|
|
auxil->has_error = 1;
|
|
SEL_DISCARD(l);
|
|
SEL_DISCARD(r);
|
|
$$ = NULL;
|
|
} else {
|
|
$$ = (xce_select_node_t*)makeSelectNode(op_type,
|
|
(uintptr_t)l,
|
|
(uintptr_t)r);
|
|
if ($$ == NULL) {
|
|
auxil->has_error = 1;
|
|
SEL_DISCARD(l);
|
|
SEL_DISCARD(r);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#undef SEL_DISCARD
|
|
}
|
|
|
|
resname_node <- !("any" ![a-zA-Z_0-9]) < [a-zA-Z_][a-zA-Z_0-9]* > {
|
|
$$ = auxil->has_error ? NULL : resolve_resname(auxil, $0);
|
|
}
|
|
|
|
resname_leaf <- !("any" ![a-zA-Z_0-9]) < [a-zA-Z_][a-zA-Z_0-9]* > {
|
|
$$ = auxil->has_error ? NULL : resolve_resname(auxil, $0);
|
|
}
|
|
|
|
value_node <- "any" ![a-zA-Z_0-9] {
|
|
$$ = (xce_select_node_t*)makeSelectNode(SEL_ANY, 0, 0);
|
|
}
|
|
/ < [0-9]+ ("." [0-9]*)? > {
|
|
double v = strtod($0, NULL);
|
|
uintptr_t bits;
|
|
memcpy(&bits, &v, sizeof(bits));
|
|
$$ = (xce_select_node_t*)makeSelectNode(SEL_NUM, bits, 0);
|
|
}
|
|
/ < [a-zA-Z_][a-zA-Z_0-9]* > {
|
|
char* s = makeString((char*)$0);
|
|
if (s == NULL) { auxil->has_error = 1; $$ = NULL; }
|
|
else { $$ = (xce_select_node_t*)makeSelectNode(SEL_STR, (uintptr_t)s, 0); }
|
|
}
|
|
|
|
cmp_op <- ">=" { $$ = (xce_select_node_t*)(uintptr_t)SEL_GE; }
|
|
/ "<=" { $$ = (xce_select_node_t*)(uintptr_t)SEL_LE; }
|
|
/ "!=" { $$ = (xce_select_node_t*)(uintptr_t)SEL_NE; }
|
|
/ "==" { $$ = (xce_select_node_t*)(uintptr_t)SEL_EQ; }
|
|
/ ">" { $$ = (xce_select_node_t*)(uintptr_t)SEL_GT; }
|
|
/ "<" { $$ = (xce_select_node_t*)(uintptr_t)SEL_LT; }
|
|
|
|
_ <- [ \t]*
|
|
|
|
EOF <- !.
|