diff --git a/compiler/pp/pp.go b/compiler/pp/pp.go index d689899..bc1cef2 100644 --- a/compiler/pp/pp.go +++ b/compiler/pp/pp.go @@ -659,6 +659,10 @@ func (pp *Preprocessor) applyRules(line string) string { // stripBlockComments removes /* ... */ comments from a line. // If a /* is found without closing */, sets inBlock to true. +// +// `//` and `&&` line-comment markers are detected first so a `/*` +// substring inside one of them (e.g. `// see app/api/*.prg`) doesn't +// start a runaway block comment that eats subsequent lines. func stripBlockComments(line string, inBlock *bool) string { var out strings.Builder i := 0 @@ -679,6 +683,20 @@ func stripBlockComments(line string, inBlock *bool) string { i++ continue } + // // line comment — copy the rest of the line through verbatim + // (the lexer/`#command` machinery still needs to see it) but + // don't scan it for `/*` so an embedded `/*` substring (e.g. + // `// see app/api/*.prg`) can't open a runaway block comment + // that eats subsequent lines. + if i+1 < len(line) && line[i] == '/' && line[i+1] == '/' { + out.WriteString(line[i:]) + return out.String() + } + // && Harbour-style line comment — same rule. + if i+1 < len(line) && line[i] == '&' && line[i+1] == '&' { + out.WriteString(line[i:]) + return out.String() + } // Block comment start if i+1 < len(line) && line[i] == '/' && line[i+1] == '*' { // Find closing */ diff --git a/compiler/pp/pp_test.go b/compiler/pp/pp_test.go index 99e2660..c3ea659 100644 --- a/compiler/pp/pp_test.go +++ b/compiler/pp/pp_test.go @@ -262,3 +262,34 @@ func TestMissingInclude(t *testing.T) { t.Error("code after missing include should continue") } } + +// TestSlashStarInsideLineComment locks in that a `/*` substring inside +// a `//` line comment does NOT open a runaway block comment. Before +// the fix, a comment like "// see app/api/*.prg" would start a block +// comment from `/*.prg` that ate every subsequent line until EOF or +// the next `*/`, silently dropping FUNCTION declarations. +func TestSlashStarInsideLineComment(t *testing.T) { + p := New() + src := `// see app/api/*.prg for the glob +FUNCTION Foo() +RETURN 1 +` + result, _ := p.Process("test.prg", src) + if !strings.Contains(result, "FUNCTION Foo") { + t.Errorf("FUNCTION Foo() should survive a // line comment that contains /*; got:\n%s", result) + } +} + +// TestDoubleAmpInsideLineComment — same protection for Harbour's `&&` +// line comment marker. +func TestDoubleAmpInsideLineComment(t *testing.T) { + p := New() + src := `&& glob /*.prg +FUNCTION Foo() +RETURN 1 +` + result, _ := p.Process("test.prg", src) + if !strings.Contains(result, "FUNCTION Foo") { + t.Errorf("FUNCTION Foo() should survive a && line comment that contains /*; got:\n%s", result) + } +}