// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com) // All rights reserved. // Console I/O functions for the Five runtime library. // Implements Harbour's QOut (?), QQOut (??), and related output functions. package hbrtl import ( "five/hbrt" "fmt" "strings" ) // QOut implements the ? command. Prints newline then values separated by space. // Harbour: QOut() / hb_conOutStd() func QOut(t *hbrt.Thread) { t.Frame(0, 0) // variadic — args are already consumed by caller defer t.EndProc() // The caller pushes args before calling. We need a different approach: // In generated code, ? a, b, c becomes: // PushSymbol(QOUT); PushNil; Push(a); Push(b); Push(c); Function(3) // But our Frame(0,0) means no locals. We need to accept variadic args. // For now, this is called directly by the test harness. t.RetNil() } // qoutImpl is the actual implementation called with pre-collected args. func qoutImpl(args []hbrt.Value) { parts := make([]string, len(args)) for i, v := range args { parts[i] = valueToDisplay(v) } fmt.Print("\r\n" + strings.Join(parts, " ")) } // qqoutImpl prints without leading newline (??). func qqoutImpl(args []hbrt.Value) { parts := make([]string, len(args)) for i, v := range args { parts[i] = valueToDisplay(v) } fmt.Print(strings.Join(parts, " ")) } // valueToDisplay converts a Value to its display string. // Harbour: hb_itemString() func valueToDisplay(v hbrt.Value) string { switch { case v.IsNil(): return "NIL" case v.IsLogical(): if v.AsBool() { return ".T." } return ".F." case v.IsInt(): return fmt.Sprintf("%d", v.AsInt()) case v.IsLong(): return fmt.Sprintf("%d", v.AsLong()) case v.IsDouble(): dec := v.Decimal() if dec == 255 { return fmt.Sprintf("%g", v.AsDouble()) } return fmt.Sprintf("%.*f", dec, v.AsDouble()) case v.IsString(): return v.AsString() case v.IsDate(): return julianToDateStr(v.AsJulian()) case v.IsArray(): return fmt.Sprintf("{Array(%d)}", len(v.AsArray().Items)) default: return v.String() } } // julianToDateStr and date formatting moved to datetime.go