feat(macro): &var / &(expr) runtime compilation
Harbour's macro operator was a stub: hbrt.MacroCompile only resolved
bare identifier names to memvars/functions and returned the source
string unchanged for any non-trivial expression. The gengo emit was
also broken — `t.MacroPush() + t.PushNil()` never pushed the inner
expression's value, so MacroPush popped whatever happened to be on
the stack.
Wire it up properly:
1. Gengo fix: `case *ast.MacroExpr` now emits `emitExpr(e.Expr);
t.MacroPush()`. The inner expression produces the source string;
MacroPush consumes it and pushes the evaluated result.
2. Hook pattern in hbrt: `SetMacroEvalHook(fn)` lets hbrtl install
the real evaluator without creating an import cycle (genpc
already imports hbrt). MacroPush delegates to the hook when
installed; otherwise falls back to the legacy stub for hbrt
unit tests.
3. hbrtl.init registers macroEval, which reuses compileExprSource
(factored out of PcCompile) so macro lookups share the same
sync.Map-backed pcode cache — repeat evaluations of the same
macro source are free after the first hit.
4. ExecPcode leaves the result in retVal; macroEval copies it to
the operand stack via PushRetValue.
Tested (/tmp/test_macro.prg):
&"10 + 20" → 30
&"Sqrt(16)" → 4
&"Upper('hello')" → HELLO
&("30 * " + Str(nX, 1)) → 210 (runtime-built source)
&"5 > 3 .AND. .T." → .T.
&("Str(" + Str(nX*10,2) + ",2)") → 70
FiveSql2 43/43, Harbour compat 56/56, Go test ALL PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2420,8 +2420,11 @@ func (g *Generator) emitExpr(expr ast.Expr) {
|
||||
// Already converted to fmt.Sprintf CallExpr by parser
|
||||
g.emitExpr(e.Parts[0]) // shouldn't reach here normally
|
||||
case *ast.MacroExpr:
|
||||
g.writeln("t.MacroPush() // runtime macro compilation")
|
||||
g.writeln("t.PushNil()")
|
||||
// &ident / &(expr) — evaluate the inner expression to get the
|
||||
// source string, then MacroPush compiles and runs it via the
|
||||
// hbrtl-installed evaluator hook.
|
||||
g.emitExpr(e.Expr)
|
||||
g.writeln("t.MacroPush()")
|
||||
case *ast.AliasExpr:
|
||||
g.emitAliasExpr(e)
|
||||
case *ast.RefExpr:
|
||||
|
||||
Reference in New Issue
Block a user