%prefix "xce_select" %value "xce_select_node_t *" %auxil "select_context_t *" %source { #include #include #include #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 <- !.