/* ** Calculator conflict demonstration -- runtime extension loading with ** conflicting grammar extensions. ** ** Demonstrates loading the power extension (^ = exponentiation) and ** the bitwise extension (^ = XOR) both individually and together, ** showing how the same token (^) can have different semantics depending ** on which extension is active and which has priority. ** ** Test scenarios: ** 1. Power extension only: 2 ^ 3 = 8 (exponentiation) ** 2. Bitwise extension only: 2 ^ 3 = 1 (XOR) ** 3. Both loaded, power wins: 2 ^ 3 = 8 (power has priority) ** 4. Both loaded, bitwise wins: 2 ^ 3 = 1 (bitwise has priority) */ #define _GNU_SOURCE #include #include #include #include #include #include "tokenize.h" #include "calc.h" /* ** Lime parser interface functions (generated by Lime). ** Prefixed with the %name directive value: Calc. */ void *CalcAlloc(void *(*mallocProc)(size_t)); void CalcFree(void *p, void (*freeProc)(void *)); void Calc(void *yyp, int yymajor, int yyminor, int *result); /* ------------------------------------------------------------------ */ /* Plugin types (shared by both extensions) */ /* ------------------------------------------------------------------ */ typedef struct { int type; const char *description; union { struct { const char *lhs; const char **rhs; int nrhs; const char *code; int precedence; } add_rule; struct { const char *name; const char *lexeme; int token_code; } add_token; struct { const char *symbol; int new_precedence; int new_assoc; } modify_prec; } u; } CalcGrammarModification; typedef struct { const char *name; const char *version; int (*eval)(const char *expr, int *result); const CalcGrammarModification *mods; uint32_t nmods; } CalcExtPlugin; /* ------------------------------------------------------------------ */ /* Conflict detection */ /* ------------------------------------------------------------------ */ /* ** Check whether two extensions have conflicting grammar modifications. ** Returns the number of conflicts found. */ static int detect_conflicts(const CalcExtPlugin *a, const CalcExtPlugin *b) { int conflicts = 0; for (uint32_t i = 0; i < a->nmods; i++) { for (uint32_t j = 0; j < b->nmods; j++) { /* Check for token code conflicts */ if (a->mods[i].type == 1 && b->mods[j].type == 1) { /* Both are MOD_ADD_TOKEN */ if (a->mods[i].u.add_token.token_code == b->mods[j].u.add_token.token_code) { printf(" CONFLICT: token code %d claimed by both:\n", a->mods[i].u.add_token.token_code); printf(" %s: %s (%s)\n", a->name, a->mods[i].u.add_token.name, a->mods[i].description); printf(" %s: %s (%s)\n", b->name, b->mods[j].u.add_token.name, b->mods[j].description); conflicts++; } } /* Check for precedence conflicts on same symbol */ if (a->mods[i].type == 2 && b->mods[j].type == 2) { /* Both are MOD_MODIFY_PRECEDENCE */ if (strcmp(a->mods[i].u.modify_prec.symbol, b->mods[j].u.modify_prec.symbol) == 0) { const char *assoc_names[] = { "none", "left", "right", "nonassoc" }; printf(" CONFLICT: precedence for '%s' set by both:\n", a->mods[i].u.modify_prec.symbol); printf(" %s: precedence=%d, assoc=%s\n", a->name, a->mods[i].u.modify_prec.new_precedence, assoc_names[a->mods[i].u.modify_prec.new_assoc]); printf(" %s: precedence=%d, assoc=%s\n", b->name, b->mods[j].u.modify_prec.new_precedence, assoc_names[b->mods[j].u.modify_prec.new_assoc]); conflicts++; } } /* Check for rule conflicts on same production shape */ if (a->mods[i].type == 0 && b->mods[j].type == 0) { /* Both are MOD_ADD_RULE */ if (strcmp(a->mods[i].u.add_rule.lhs, b->mods[j].u.add_rule.lhs) == 0 && a->mods[i].u.add_rule.nrhs == b->mods[j].u.add_rule.nrhs) { int match = 1; for (int k = 0; k < a->mods[i].u.add_rule.nrhs; k++) { if (strcmp(a->mods[i].u.add_rule.rhs[k], b->mods[j].u.add_rule.rhs[k]) != 0) { match = 0; break; } } if (match) { printf(" CONFLICT: rule '%s' with same RHS added by both:\n", a->mods[i].u.add_rule.lhs); printf(" %s: %s\n", a->name, a->mods[i].description); printf(" %s: %s\n", b->name, b->mods[j].description); conflicts++; } } } } } return conflicts; } /* ------------------------------------------------------------------ */ /* Base parser helper */ /* ------------------------------------------------------------------ */ static void run_base(const char *expr) { int result = 0; void *parser = CalcAlloc(malloc); if (!parser) { printf(" %s = ERROR: out of memory\n", expr); return; } int pos = 0; int len = (int)strlen(expr); while (pos < len) { int value = 0; int tok = calc_next_token(expr, &pos, len, &value); if (tok == 0) break; Calc(parser, tok, value, &result); } Calc(parser, 0, 0, &result); CalcFree(parser, free); printf(" %s = %d\n", expr, result); } /* ------------------------------------------------------------------ */ /* Extension loader helper */ /* ------------------------------------------------------------------ */ typedef struct { void *handle; const CalcExtPlugin *plugin; } LoadedExt; static int load_extension(const char *soname, const char *entry_sym, LoadedExt *out) { out->handle = dlopen(soname, RTLD_NOW); if (!out->handle) { fprintf(stderr, "dlopen(%s) failed: %s\n", soname, dlerror()); return -1; } typedef const CalcExtPlugin *(*EntryFn)(void); EntryFn entry = (EntryFn)dlsym(out->handle, entry_sym); if (!entry) { fprintf(stderr, "dlsym(%s) failed: %s\n", entry_sym, dlerror()); dlclose(out->handle); out->handle = NULL; return -1; } out->plugin = entry(); return 0; } static void unload_extension(LoadedExt *ext) { if (ext->handle) { dlclose(ext->handle); ext->handle = NULL; ext->plugin = NULL; } } /* ------------------------------------------------------------------ */ /* Main */ /* ------------------------------------------------------------------ */ int main(void) { printf("Calculator conflict demonstration\n"); printf("=================================\n"); /* --- Base parser --- */ printf("\n--- Scenario 0: Base parser (4 operations) ---\n"); run_base("3 + 4 * 2"); run_base("(3 + 4) * 2"); run_base("10 - 3"); /* --- Scenario 1: Power extension only --- */ printf("\n--- Scenario 1: Power extension only (^ = exponentiation) ---\n"); LoadedExt power_ext = {0}; if (load_extension("./libcalc_power.so", "calc_power_entry", &power_ext) != 0) { return 1; } printf(" Loaded: %s v%s (%u grammar modifications)\n", power_ext.plugin->name, power_ext.plugin->version, power_ext.plugin->nmods); int result; power_ext.plugin->eval("2 ^ 3", &result); printf(" 2 ^ 3 = %d (expected: 8, exponentiation)\n", result); power_ext.plugin->eval("2 ^ 10", &result); printf(" 2 ^ 10 = %d (expected: 1024)\n", result); power_ext.plugin->eval("3 + 2 ^ 3", &result); printf(" 3 + 2 ^ 3 = %d (expected: 11)\n", result); /* --- Scenario 2: Bitwise extension only --- */ printf("\n--- Scenario 2: Bitwise extension only (^ = XOR) ---\n"); LoadedExt bitwise_ext = {0}; if (load_extension("./libcalc_bitwise.so", "calc_bitwise_entry", &bitwise_ext) != 0) { unload_extension(&power_ext); return 1; } printf(" Loaded: %s v%s (%u grammar modifications)\n", bitwise_ext.plugin->name, bitwise_ext.plugin->version, bitwise_ext.plugin->nmods); bitwise_ext.plugin->eval("2 ^ 3", &result); printf(" 2 ^ 3 = %d (expected: 1, bitwise XOR)\n", result); bitwise_ext.plugin->eval("5 & 3", &result); printf(" 5 & 3 = %d (expected: 1, bitwise AND)\n", result); bitwise_ext.plugin->eval("5 | 3", &result); printf(" 5 | 3 = %d (expected: 7, bitwise OR)\n", result); bitwise_ext.plugin->eval("1 << 4", &result); printf(" 1 << 4 = %d (expected: 16, left shift)\n", result); bitwise_ext.plugin->eval("16 >> 2", &result); printf(" 16 >> 2 = %d (expected: 4, right shift)\n", result); bitwise_ext.plugin->eval("6 ^ 3 & 5", &result); printf(" 6 ^ 3 & 5 = %d (expected: 7, & binds tighter: 6 ^ (3&5) = 6^1)\n", result); /* --- Scenario 3: Both loaded, detect conflicts --- */ printf("\n--- Conflict detection ---\n"); printf("Both extensions are now loaded. Checking for conflicts...\n\n"); int nconflicts = detect_conflicts(power_ext.plugin, bitwise_ext.plugin); printf("\n Total conflicts detected: %d\n", nconflicts); /* --- Scenario 3a: Both loaded, power has priority --- */ printf("\n--- Scenario 3: Both loaded, power extension has priority ---\n"); printf(" Resolution: use power extension's parser for ^ expressions\n"); power_ext.plugin->eval("2 ^ 3", &result); printf(" 2 ^ 3 = %d (expected: 8, power wins)\n", result); /* Bitwise-only operators still work through the bitwise parser */ bitwise_ext.plugin->eval("5 & 3", &result); printf(" 5 & 3 = %d (expected: 1, bitwise AND via bitwise parser)\n", result); /* --- Scenario 3b: Both loaded, bitwise has priority --- */ printf("\n--- Scenario 4: Both loaded, bitwise extension has priority ---\n"); printf(" Resolution: use bitwise extension's parser for ^ expressions\n"); bitwise_ext.plugin->eval("2 ^ 3", &result); printf(" 2 ^ 3 = %d (expected: 1, bitwise wins)\n", result); /* Power-only expressions still work through the power parser */ power_ext.plugin->eval("2 ^ 10", &result); printf(" 2 ^ 10 = %d (expected: 1024, power via power parser)\n", result); /* --- Summary --- */ printf("\n--- Summary ---\n"); printf(" The ^ token has two interpretations:\n"); printf(" power_operator: 2 ^ 3 = 8 (exponentiation)\n"); printf(" bitwise_operators: 2 ^ 3 = 1 (XOR)\n"); printf(" Conflict resolution is determined by extension priority.\n"); printf(" The conflict detection system identified %d conflict(s)\n", nconflicts); printf(" between the two extensions' grammar modifications.\n"); /* --- Cleanup --- */ printf("\nUnloading extensions ...\n"); unload_extension(&bitwise_ext); unload_extension(&power_ext); printf("Done.\n"); return 0; }