/* ** Calculator power extension — shared library plugin. ** ** Exports a CalcPowerPlugin struct via calc_power_entry(). The host ** program loads this .so with dlopen, calls calc_power_entry() to get ** the plugin descriptor, and uses the eval() function to parse ** expressions that include the ^ operator. ** ** Also provides GrammarModification descriptors following the pattern ** from examples/jsonb_extension.c, so the extension can describe its ** grammar changes to the Lime extension system. */ #include "tokenize.h" #include "calc_power.h" #include #include #include /* ** Lime parser interface functions (generated by Lime). ** Prefixed with the %name directive value: CalcPower. */ void *CalcPowerAlloc(void *(*mallocProc)(size_t)); void CalcPowerFree(void *p, void (*freeProc)(void *)); void CalcPower(void *yyp, int yymajor, int yyminor, int *result); /* ------------------------------------------------------------------ */ /* Extension descriptor (GrammarModification pattern) */ /* ------------------------------------------------------------------ */ /* ** We define extension types inline so the .so doesn't need to link ** against the full Lime extension system. */ typedef enum { MOD_ADD_RULE = 0, MOD_ADD_TOKEN = 1, MOD_MODIFY_PRECEDENCE = 2, } CalcGrammarModType; typedef struct { CalcGrammarModType 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; /* 0=none, 1=left, 2=right, 3=nonassoc */ } modify_prec; } u; } CalcGrammarModification; static const char *rhs_caret[] = { "expr", "CARET", "expr", NULL }; static CalcGrammarModification power_mods[] = { { .type = MOD_ADD_TOKEN, .description = "Caret (exponentiation) operator", .u.add_token = { .name = "CARET", .lexeme = "^", .token_code = 8, }, }, { .type = MOD_ADD_RULE, .description = "expr -> expr CARET expr", .u.add_rule = { .lhs = "expr", .rhs = rhs_caret, .nrhs = 3, .code = "A = int_pow(B, C);", .precedence = -1, }, }, { .type = MOD_MODIFY_PRECEDENCE, .description = "Set CARET precedence (right-associative, above * /)", .u.modify_prec = { .symbol = "CARET", .new_precedence = 3, .new_assoc = 2, /* right */ }, }, }; #define POWER_MOD_COUNT (sizeof(power_mods) / sizeof(power_mods[0])) /* ------------------------------------------------------------------ */ /* eval() — tokenize and parse with the CalcPower parser */ /* ------------------------------------------------------------------ */ static int power_eval(const char *expr, int *out) { void *parser = CalcPowerAlloc(malloc); if (!parser) return -1; int result = 0; 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; CalcPower(parser, tok, value, &result); } /* Signal end of input */ CalcPower(parser, 0, 0, &result); CalcPowerFree(parser, free); *out = result; return 0; } /* ------------------------------------------------------------------ */ /* Plugin entry point */ /* ------------------------------------------------------------------ */ typedef struct CalcPowerPlugin { const char *name; const char *version; int (*eval)(const char *expr, int *result); const CalcGrammarModification *mods; uint32_t nmods; } CalcPowerPlugin; static const CalcPowerPlugin plugin = { .name = "power_operator", .version = "1.0", .eval = power_eval, .mods = power_mods, .nmods = POWER_MOD_COUNT, }; const CalcPowerPlugin *calc_power_entry(void) { return &plugin; }