From f950cb07845a41d9218e198ed85dca81bbd2fdf4 Mon Sep 17 00:00:00 2001 From: Charles KWON OhJun Date: Wed, 1 Apr 2026 11:47:26 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20Phase=202=20=E2=80=94=20HIGH=20#6,9,10,1?= =?UTF-8?q?1,12,19,23,32,46,47?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- compiler/gengo/gengo.go | 142 +++++++--------------------------------- compiler/lexer/lexer.go | 3 - compiler/pp/pp.go | 11 ++-- docs/.pdca-status.json | 82 ++++++++++++++++++++--- hbrtl/strings.go | 12 +++- 5 files changed, 115 insertions(+), 135 deletions(-) diff --git a/compiler/gengo/gengo.go b/compiler/gengo/gengo.go index 7cce3f5..8e21f5b 100644 --- a/compiler/gengo/gengo.go +++ b/compiler/gengo/gengo.go @@ -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) { diff --git a/compiler/lexer/lexer.go b/compiler/lexer/lexer.go index 66b21bb..383670f 100644 --- a/compiler/lexer/lexer.go +++ b/compiler/lexer/lexer.go @@ -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"): diff --git a/compiler/pp/pp.go b/compiler/pp/pp.go index 10984db..2fa9f8d 100644 --- a/compiler/pp/pp.go +++ b/compiler/pp/pp.go @@ -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 } diff --git a/docs/.pdca-status.json b/docs/.pdca-status.json index 0eadc99..a68d0bf 100644 --- a/docs/.pdca-status.json +++ b/docs/.pdca-status.json @@ -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" } ] } \ No newline at end of file diff --git a/hbrtl/strings.go b/hbrtl/strings.go index b30e4a3..0023771 100644 --- a/hbrtl/strings.go +++ b/hbrtl/strings.go @@ -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 }