feat(pp): detect Harbour inline C in #pragma BEGINDUMP and fail fast
Harbour's #pragma BEGINDUMP ... #pragma ENDDUMP blocks carry C source
that the Harbour toolchain embeds verbatim. Five takes the same
directive but targets Go — any `.prg` ported from Harbour that ships
inline C gets its C shoveled into the Go codegen pipeline and fails
with opaque errors like "invalid character U+0023 '#'" from the Go
compiler, dozens of lines downstream of the actual cause.
Detect the C shape at PP time and report a clear, actionable error:
pp: file.prg:N: #pragma BEGINDUMP contains C code — Five accepts
inline Go only. Port the block to Go (or use an RTL function),
then wrap in #pragma BEGINDUMP ... #pragma ENDDUMP.
looksLikeInlineC uses conservative signals that don't false-positive
on legitimate inline Go (which calls `hbrt.HB_FUNC("NAME", fn)` with
a package prefix and a quoted string, distinct from C's bare
`HB_FUNC(NAME)` macro). Signals:
- `#include <...>` / `#include "..."` — unambiguous C preprocessor
- line-starting `HB_FUNC(` / `HB_FUNC_STATIC(` — C FFI macro
- `typedef ` / `struct ` / `int main(` / `void main(` at line start
main.go now aborts the build when PP returns errors (previously
printed but continued — same behavior the parser already had for
its own errors). Keeps build output short: one pp line + one
summary line, no gengo noise.
Verified:
- harbour-core/tests/inline_c.prg → clean PP error, exit 1
- examples/godump_demo.prg (legitimate inline Go) → passes PP
(hits a separate pre-existing gengo import-ordering bug, not
related to this change)
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:
@@ -326,6 +326,9 @@ func parsePRGFile(prgFile string) *ast.File {
|
||||
for _, e := range ppErrors {
|
||||
fmt.Fprintf(os.Stderr, "pp: %s\n", e)
|
||||
}
|
||||
if len(ppErrors) > 0 {
|
||||
fatal(fmt.Sprintf("%d preprocessor error(s) in %s", len(ppErrors), prgFile))
|
||||
}
|
||||
|
||||
file, errs := parser.ParseWithGoDumps(prgFile, processed, pre.GoDumps)
|
||||
if len(errs) > 0 {
|
||||
@@ -371,6 +374,9 @@ func compilePRGMode(prgFile string, isLibrary bool) string {
|
||||
for _, e := range ppErrors {
|
||||
fmt.Fprintf(os.Stderr, "pp: %s\n", e)
|
||||
}
|
||||
if len(ppErrors) > 0 {
|
||||
fatal(fmt.Sprintf("%d preprocessor error(s) in %s", len(ppErrors), prgFile))
|
||||
}
|
||||
|
||||
file, errs := parser.ParseWithGoDumps(prgFile, processed, pre.GoDumps)
|
||||
if len(errs) > 0 {
|
||||
@@ -429,6 +435,9 @@ func compilePRG(prgFile string) string {
|
||||
for _, e := range ppErrors {
|
||||
fmt.Fprintf(os.Stderr, "pp: %s\n", e)
|
||||
}
|
||||
if len(ppErrors) > 0 {
|
||||
fatal(fmt.Sprintf("%d preprocessor error(s) in %s", len(ppErrors), prgFile))
|
||||
}
|
||||
|
||||
// Phase 2: Parse
|
||||
file, errs := parser.ParseWithGoDumps(prgFile, processed, pre.GoDumps)
|
||||
@@ -543,6 +552,9 @@ func buildFRB(prgFile, outputFile string) {
|
||||
for _, e := range ppErrors {
|
||||
fmt.Fprintf(os.Stderr, "pp: %s\n", e)
|
||||
}
|
||||
if len(ppErrors) > 0 {
|
||||
fatal(fmt.Sprintf("%d preprocessor error(s) in %s", len(ppErrors), prgFile))
|
||||
}
|
||||
|
||||
// Phase 2: Parse
|
||||
file, parseErrors := parser.ParseWithGoDumps(prgFile, processed, pre.GoDumps)
|
||||
@@ -657,6 +669,9 @@ func debugPRG(prgFile string) {
|
||||
for _, e := range ppErrors {
|
||||
fmt.Fprintf(os.Stderr, "pp: %s\n", e)
|
||||
}
|
||||
if len(ppErrors) > 0 {
|
||||
fatal(fmt.Sprintf("%d preprocessor error(s) in %s", len(ppErrors), prgFile))
|
||||
}
|
||||
|
||||
// Phase 2: Parse
|
||||
file, parseErrors := parser.ParseWithGoDumps(prgFile, processed, pre.GoDumps)
|
||||
|
||||
Reference in New Issue
Block a user