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:
@@ -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},
|
||||
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,127 +107,18 @@ 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()
|
||||
if library {
|
||||
g.emitInitModule()
|
||||
} else {
|
||||
g.emitInitModule()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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"):
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "2.0",
|
||||
"lastUpdated": "2026-04-01T01:50:29.561Z",
|
||||
"lastUpdated": "2026-04-01T02:46:45.142Z",
|
||||
"activeFeatures": [
|
||||
"hbrt",
|
||||
"hbrtl",
|
||||
@@ -46,9 +46,9 @@
|
||||
"documents": {},
|
||||
"timestamps": {
|
||||
"started": "2026-03-27T11:15:10.675Z",
|
||||
"lastUpdated": "2026-04-01T01:16:33.700Z"
|
||||
"lastUpdated": "2026-04-01T02:36:49.307Z"
|
||||
},
|
||||
"lastFile": "/mnt/d/charles/five/hbrtl/register.go"
|
||||
"lastFile": "/mnt/d/charles/five/hbrtl/strings.go"
|
||||
},
|
||||
"tests": {
|
||||
"phase": "do",
|
||||
@@ -85,7 +85,7 @@
|
||||
"documents": {},
|
||||
"timestamps": {
|
||||
"started": "2026-03-27T11:24:36.853Z",
|
||||
"lastUpdated": "2026-03-30T15:46:43.429Z"
|
||||
"lastUpdated": "2026-04-01T02:32:31.594Z"
|
||||
},
|
||||
"lastFile": "/mnt/d/charles/five/compiler/lexer/lexer.go"
|
||||
},
|
||||
@@ -124,7 +124,7 @@
|
||||
"documents": {},
|
||||
"timestamps": {
|
||||
"started": "2026-03-27T11:45:21.266Z",
|
||||
"lastUpdated": "2026-03-30T15:35:58.397Z"
|
||||
"lastUpdated": "2026-04-01T02:46:45.142Z"
|
||||
},
|
||||
"lastFile": "/mnt/d/charles/five/compiler/gengo/gengo.go"
|
||||
},
|
||||
@@ -137,7 +137,7 @@
|
||||
"documents": {},
|
||||
"timestamps": {
|
||||
"started": "2026-03-27T11:50:31.420Z",
|
||||
"lastUpdated": "2026-04-01T01:27:55.511Z"
|
||||
"lastUpdated": "2026-04-01T02:43:37.339Z"
|
||||
},
|
||||
"lastFile": "/mnt/d/charles/five/cmd/five/main.go"
|
||||
},
|
||||
@@ -202,7 +202,7 @@
|
||||
"documents": {},
|
||||
"timestamps": {
|
||||
"started": "2026-03-27T23:11:55.802Z",
|
||||
"lastUpdated": "2026-03-30T13:03:57.551Z"
|
||||
"lastUpdated": "2026-04-01T02:35:12.240Z"
|
||||
},
|
||||
"lastFile": "/mnt/d/charles/five/compiler/pp/pp.go"
|
||||
},
|
||||
@@ -280,7 +280,7 @@
|
||||
"session": {
|
||||
"startedAt": "2026-03-27T06:06:49.620Z",
|
||||
"onboardingCompleted": false,
|
||||
"lastActivity": "2026-04-01T01:50:29.561Z"
|
||||
"lastActivity": "2026-04-01T02:46:45.142Z"
|
||||
},
|
||||
"history": [
|
||||
{
|
||||
@@ -5628,6 +5628,72 @@
|
||||
"feature": "hbrt",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-01T02:32:31.594Z",
|
||||
"feature": "lexer",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-01T02:34:45.039Z",
|
||||
"feature": "pp",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-01T02:35:12.240Z",
|
||||
"feature": "pp",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-01T02:36:49.307Z",
|
||||
"feature": "hbrtl",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-01T02:39:29.824Z",
|
||||
"feature": "gengo",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-01T02:41:45.871Z",
|
||||
"feature": "five",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-01T02:42:23.943Z",
|
||||
"feature": "five",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-01T02:42:49.102Z",
|
||||
"feature": "five",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-01T02:43:37.339Z",
|
||||
"feature": "five",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-01T02:44:43.098Z",
|
||||
"feature": "gengo",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-01T02:46:45.142Z",
|
||||
"feature": "gengo",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -15,7 +15,8 @@ import (
|
||||
// Str converts a numeric value to a string.
|
||||
// Harbour: Str(nValue [, nWidth [, nDec]]) → cString
|
||||
func Str(t *hbrt.Thread) {
|
||||
t.Frame(1, 0) // at least 1 param
|
||||
nParams := t.ParamCount()
|
||||
t.Frame(nParams, 0)
|
||||
defer t.EndProc()
|
||||
|
||||
v := t.Local(1)
|
||||
@@ -26,9 +27,18 @@ func Str(t *hbrt.Thread) {
|
||||
}
|
||||
|
||||
d := v.AsNumDouble()
|
||||
|
||||
// Width and decimals: use caller's args if provided, else Value metadata
|
||||
width := int(v.Length())
|
||||
dec := int(v.Decimal())
|
||||
|
||||
if nParams >= 2 && !t.Local(2).IsNil() {
|
||||
width = t.Local(2).AsInt()
|
||||
}
|
||||
if nParams >= 3 && !t.Local(3).IsNil() {
|
||||
dec = t.Local(3).AsInt()
|
||||
}
|
||||
|
||||
if width == 0 || width == 255 {
|
||||
width = 10 // default width
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user