fix: Phase 2 — HIGH #6,9,10,11,12,19,23,32,46,47

Files modified (5):
  compiler/gengo/gengo.go — #6,#32: Deduplicate 3 Generate functions into 1
    doGenerate(file, debug, library) replaces 170 lines of copy-paste
    Dead GenerateDebug method removed
  cmd/five/main.go — #9,10,11,12: Fix 5 silently ignored errors
    filepath.Abs, tidy.Run, tidyCmd.CombinedOutput now checked
  hbrtl/strings.go — #19: Str() now reads caller's nWidth/nDec params
    Was ignoring explicit Str(123, 10, 2) arguments
  compiler/pp/pp.go — #46: Fix stale "NOT implemented" comment
    #47: Extract maxIncludeDepth constant
  compiler/lexer/lexer.go — #23: Remove unused LookupKeyword result

Issues resolved: 10 (HIGH: 7, MEDIUM: 3)
Total fixed: 26/53

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-01 11:47:26 +09:00
parent 207fa9f7dd
commit f950cb0784
5 changed files with 115 additions and 135 deletions

View File

@@ -48,9 +48,27 @@ type symbolEntry struct {
// Generate converts an AST File into Go source code.
func Generate(file *ast.File) string {
return doGenerate(file, false, false)
}
// GenerateWithDebug is like Generate but includes DebugLine calls.
func GenerateWithDebug(file *ast.File) string {
return doGenerate(file, true, false)
}
// GenerateLibrary generates Go code without main() — for multi-PRG builds.
func GenerateLibrary(file *ast.File) string {
return doGenerate(file, false, true)
}
// generate is the unified internal implementation.
// doGenerate is the unified internal implementation for all Generate* variants.
func doGenerate(file *ast.File, debug, library bool) string {
g := &Generator{
file: file,
imports: map[string]bool{"five/hbrt": true, "five/hbrtl": true},
file: file,
imports: map[string]bool{"five/hbrt": true, "five/hbrtl": true},
Debug: debug,
IsLibrary: library,
}
// Collect symbols from declarations
@@ -58,7 +76,7 @@ func Generate(file *ast.File) string {
switch decl := d.(type) {
case *ast.FuncDecl:
scope := "hbrt.FsPublic|hbrt.FsLocal"
if decl.Name == "Main" || decl.Name == "MAIN" {
if !library && (decl.Name == "Main" || decl.Name == "MAIN") {
scope += "|hbrt.FsFirst"
}
g.symbols = append(g.symbols, symbolEntry{
@@ -67,7 +85,6 @@ func Generate(file *ast.File) string {
fn: "HB_" + strings.ToUpper(decl.Name),
})
case *ast.ClassDecl:
// Class constructor symbol: Person() → HB_PERSON_CTOR
className := strings.ToUpper(decl.Name)
g.symbols = append(g.symbols, symbolEntry{
name: className,
@@ -77,13 +94,11 @@ func Generate(file *ast.File) string {
}
}
// Check if xBase commands are used — if so, add RDD imports
if hasXBaseCommands(file) {
g.imports["five/hbrdd"] = true
g.imports["five/hbrdd/dbf"] = true
}
// Collect user imports
g.importAlias = make(map[string]string)
for _, imp := range file.Imports {
g.imports[imp.Path] = true
@@ -92,130 +107,21 @@ func Generate(file *ast.File) string {
}
}
// Generate file
g.emitHeader()
g.emitSymbols()
for _, d := range file.Decls {
g.emitDecl(d)
}
g.emitFastFuncRegistrations()
if !g.IsLibrary {
g.emitMain()
} else {
if library {
g.emitInitModule()
} else {
g.emitMain()
}
return g.buf.String()
}
// GenerateDebug generates Go code with debug line hooks enabled.
func (g *Generator) GenerateDebug(file *ast.File) string {
g.file = file
g.imports = map[string]bool{"five/hbrt": true, "five/hbrtl": true}
g.Debug = true
return Generate(file) // Generate uses package-level, need to thread debug flag through
}
// GenerateWithDebug is like Generate but includes DebugLine calls.
func GenerateWithDebug(file *ast.File) string {
g := &Generator{
file: file,
imports: map[string]bool{"five/hbrt": true, "five/hbrtl": true},
Debug: true,
}
// Same logic as Generate but with debug flag
for _, d := range file.Decls {
switch decl := d.(type) {
case *ast.FuncDecl:
scope := "hbrt.FsPublic|hbrt.FsLocal"
if decl.Name == "Main" || decl.Name == "MAIN" {
scope += "|hbrt.FsFirst"
}
g.symbols = append(g.symbols, symbolEntry{
name: strings.ToUpper(decl.Name),
scope: scope,
fn: "HB_" + strings.ToUpper(decl.Name),
})
case *ast.ClassDecl:
className := strings.ToUpper(decl.Name)
g.symbols = append(g.symbols, symbolEntry{
name: className,
scope: "hbrt.FsPublic|hbrt.FsLocal",
fn: "HB_" + className + "_CTOR",
})
}
}
if hasXBaseCommands(file) {
g.imports["five/hbrdd"] = true
g.imports["five/hbrdd/dbf"] = true
}
g.importAlias = make(map[string]string)
for _, imp := range file.Imports {
g.imports[imp.Path] = true
if imp.Alias != "" {
g.importAlias[imp.Path] = imp.Alias
}
}
g.emitHeader()
g.emitSymbols()
for _, d := range file.Decls {
g.emitDecl(d)
}
g.emitFastFuncRegistrations()
g.emitMain()
return g.buf.String()
}
// GenerateLibrary generates Go code without main() — for multi-PRG builds.
func GenerateLibrary(file *ast.File) string {
g := &Generator{
file: file,
imports: map[string]bool{"five/hbrt": true, "five/hbrtl": true},
IsLibrary: true,
}
for _, d := range file.Decls {
switch decl := d.(type) {
case *ast.FuncDecl:
scope := "hbrt.FsPublic|hbrt.FsLocal"
g.symbols = append(g.symbols, symbolEntry{
name: strings.ToUpper(decl.Name),
scope: scope,
fn: "HB_" + strings.ToUpper(decl.Name),
})
case *ast.ClassDecl:
className := strings.ToUpper(decl.Name)
g.symbols = append(g.symbols, symbolEntry{
name: className,
scope: "hbrt.FsPublic|hbrt.FsLocal",
fn: "HB_" + className + "_CTOR",
})
}
}
if hasXBaseCommands(file) {
g.imports["five/hbrdd"] = true
g.imports["five/hbrdd/dbf"] = true
}
g.importAlias = make(map[string]string)
for _, imp := range file.Imports {
g.imports[imp.Path] = true
if imp.Alias != "" {
g.importAlias[imp.Path] = imp.Alias
}
}
g.emitHeader()
g.emitSymbols()
for _, d := range file.Decls {
g.emitDecl(d)
}
g.emitFastFuncRegistrations()
g.emitInitModule()
return g.buf.String()
}
// --- Emit infrastructure ---
func (g *Generator) write(s string) {

View File

@@ -666,9 +666,6 @@ func (l *Lexer) scanPreprocessor(start int) token.Token {
l.advance()
}
directive := l.src[kwStart:l.pos]
upper := token.LookupKeyword(directive)
_ = upper
full := l.src[start:l.pos]
switch {
case matchCI(directive, "include"):

View File

@@ -11,9 +11,8 @@
// #ifdef NAME / #ifndef NAME / #else / #endif — conditional compilation
// #pragma — compiler hints
//
// #command/#translate (used by hbclass.ch) is NOT implemented yet.
// Five handles CLASS syntax natively in the parser, so hbclass.ch
// is not strictly required. But #include is needed for user headers.
// #command/#translate is supported via command.go (pattern matching + substitution).
// Five also handles CLASS syntax natively in the parser.
package pp
import (
@@ -78,9 +77,11 @@ func (pp *Preprocessor) Process(filename, source string) (string, []string) {
return result, pp.errors
}
const maxIncludeDepth = 20
func (pp *Preprocessor) processLines(filename, source string, depth int) string {
if depth > 20 {
pp.errors = append(pp.errors, fmt.Sprintf("%s: #include depth exceeded (max 20)", filename))
if depth > maxIncludeDepth {
pp.errors = append(pp.errors, fmt.Sprintf("%s: #include depth exceeded (max %d)", filename, maxIncludeDepth))
return source
}