docs(rag): Five knowledge corpus for LLM agents

A retrieval-ready knowledge base so an LLM can read/write Five without
prior training: overview, syntax, full RTL catalog (from hbrtl/register.go),
web/worker idioms (from the solmade app), and a long-tail gotchas file.
Every doc has keyword/summary frontmatter; INDEX.md is the routing manifest.

Grounded by parallel source exploration; RTL names spot-checked against
register.go. The gotchas file is the compounding asset — append new traps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
CharlesKWON
2026-06-15 13:54:03 +09:00
parent d5e15272d2
commit cf370564f3
7 changed files with 1290 additions and 0 deletions

55
rag/01-overview.md Normal file
View File

@@ -0,0 +1,55 @@
---
doc: five-overview
title: What Five is — model, philosophy, runtimes
keywords: [five, fivenode, overview, philosophy, token-density, harbour, xbase, compile, go, runtime]
summary: High-level orientation for an LLM about to read or write Five — what the language is, how it compiles, the two runtimes, and the design priorities (token density + Node/Go ecosystem).
---
# Five — orientation for an LLM
**Five** is an xBase/Harbour-compatible programming language that **compiles to Go**. You write `.prg` files in Harbour-family syntax; the Five toolchain generates Go and builds a native binary.
## Design priorities (why it exists)
1. **Token density** — express maximum functionality in minimum code. High-level verbs
(`PG_QUERY`, `AP_JSONRESPONSE`, `LLM_CHAT`, `hb_jsonDecode`) collapse what would be
dozens of lines of Go/Node boilerplate into one line. This is a *first-class* goal,
not an aesthetic: dense code means a whole system fits in an LLM context window, costs
fewer tokens per change, and exposes less surface area for bugs.
2. **No ecosystem isolation** — Five reaches the Go/Node ecosystem through the runtime
layer (RTL packages written in Go, blank-imported at build time). So you get dense
business logic *and* rich libraries (Postgres, HTTP, PDF, XLSX, LLM clients).
## Two distinct runtimes (do not confuse them)
| Path | What it is |
|------|-----------|
| `/Users/charleskwon/fivenode/fivedev/five` | **The Five language proper** — compiler (`compiler/lexer`, `parser`, `ast`, `gengo` Go codegen, `genpc`), runtime core (`hbrt`), and runtime library (`hbrtl`). CLI: `five run/build/gen <file.prg>`. |
| `/Users/charleskwon/fivenode/fivenode/fivenode_go` | **fivenode** — a separate runtime/toolchain (`fnode` compiler) used to build apps that lean on the Node/Go ecosystem (e.g. the `solmade` app). Distinct codebase from `fivedev/five`. |
When a task says "implement X in Five (the language)", it targets `fivedev/five`.
When building/running the `solmade` app, the `fnode` toolchain in `fivenode_go` is used.
## How a program is structured
- A `.prg` file contains `FUNCTION`/`PROCEDURE`/`STATIC FUNCTION` definitions.
- Execution entry is `Main()` (or `FUNCTION Main()` / `PROCEDURE Main()`).
- In the web app, **file-name routing** maps a URL to a function: `/api/foo-bar.prg`
→ the `Main()` in `app/api/foo_bar.prg`, dispatched as `FOO_BAR__MAIN`. (See idioms doc.)
## Compile model essentials (matters when writing code)
- The compiler **inlines common string intrinsics** (`LEN`, `CHR`, `ASC`, `SUBSTR`,
`LEFT`, `RIGHT`, `AT`, `PADR`, `PADL`) directly as Go in `compiler/gengo` — they do
NOT go through the `hbrtl` registry at runtime. Changing runtime behavior of these
requires editing the gengo intrinsics (they now call charset-aware `hbrtl.Str*`
helpers). See gotchas doc.
- Strings are **UTF-8 by default**; core string funcs operate on runes. A legacy charset
can be selected (`HB_SETCHARSET`/`HB_CDPSELECT`); env `FIVE_CHARSET`/`HB_CODEPAGE` sets
the initial charset.
## Where to look for ground truth
- Grammar/tokens: `compiler/lexer`, `compiler/parser`, `compiler/ast`.
- RTL function names: `hbrtl/register.go` (the registration table).
- Real idioms: the `solmade` app (`/Users/charleskwon/solmade/app/**/*.prg`).

152
rag/02-syntax.md Normal file
View File

@@ -0,0 +1,152 @@
---
doc: five-syntax
title: Five language syntax reference
keywords: [syntax, grammar, FUNCTION, LOCAL, IF, FOR, DO WHILE, DO CASE, code block, string literal, array, hash, operators, IIF, BEGIN SEQUENCE]
summary: Core Five/xBase syntax an LLM needs to write correct code — declarations, literals, operators, control flow, code blocks. Confirmed against the lexer/parser and real solmade usage.
---
# Five syntax reference
Case-insensitive for keywords AND identifiers (`LOCAL`/`local`, `x`/`X` are the same).
Arrays/strings are **1-based**. Strings do **not** process C escapes (see gotchas).
## Program structure
```five
FUNCTION Main()
LOCAL nTotal := CalcSum( 10, 20 )
? "Result:", nTotal
RETURN nTotal
FUNCTION CalcSum( nA, nB )
RETURN nA + nB
PROCEDURE ShowMessage() // PROCEDURE returns NIL implicitly
? "Hello"
RETURN
STATIC FUNCTION Helper( x ) // STATIC = file-local visibility
RETURN x * 2
```
## Variable declaration
Declarations come **first**, before executable statements.
```five
LOCAL x := 10 // init with :=
LOCAL a, b, c := 20 // only c is initialized; a,b are NIL
STATIC s_count := 0 // persists across calls (file-level or in-func)
PUBLIC nGlobal := 100 // runtime memvar, global
PRIVATE nScoped := 50 // runtime memvar, dynamic scope
```
Pass-by-reference uses `@` at the call site:
```five
FUNCTION Inc( x )
x := x + 1
RETURN NIL
// ...
LOCAL n := 10
Inc( @n ) // n is now 11
```
## Literals
```five
LOCAL n := 42 // number
LOCAL f := 3.14
LOCAL c1 := "double quotes" // string
LOCAL c2 := 'single quotes' // string (equivalent; NO escape processing in either)
LOCAL c3 := [bracket string] // string; supports nested [ ]
LOCAL lT := .T. // logical true (.t. ok)
LOCAL lF := .F. // logical false
LOCAL x := NIL // null/undefined
LOCAL a := { 1, 2, 3 } // array literal, 1-based
LOCAL h := { "k" => "v" } // hash literal
LOCAL e := {=>} // empty hash
LOCAL b := {| p1, p2 | p1 + p2 } // code block (closure); no-arg form: {|| expr}
```
Access: `a[1]` (first element), `h["k"]`, `h["new"] := 1` (add/update).
## Operators
```five
:= += -= *= /= %= **= // assignment / compound assignment
== = // equality (= also assigns in some contexts; prefer == for compare)
!= <> # // not equal (three spellings)
< > <= >=
$ // substring containment: "ell" $ "hello" => .T.
+ - * / % ** ^ // arithmetic (** and ^ = power); + also concatenates strings
.AND. .OR. .NOT. // logical (short-circuit); aliases: && || !
++ -- // increment / decrement
```
## Control flow
```five
IF cond
// ...
ELSEIF cond2
// ...
ELSE
// ...
ENDIF
DO WHILE cond
// ...
IF skip ; LOOP ; ENDIF // LOOP = continue
IF stop ; EXIT ; ENDIF // EXIT = break
ENDDO
FOR i := 1 TO 10 // optional STEP: FOR i := 10 TO 1 STEP -1
? i
NEXT
FOR EACH item IN aArray
? item
NEXT
DO CASE
CASE x == 1
// ...
CASE x == 2
// ...
OTHERWISE
// ...
ENDCASE
BEGIN SEQUENCE // structured error handling
risky()
RECOVER
? "error caught"
END SEQUENCE
```
Inline conditional (expression, not statement):
```five
cResult := IIF( x > 10, "big", "small" ) // Iif() also works
```
## Statement continuation & comments
```five
LOCAL s := "a" + ; // trailing ';' continues the statement on the next line
"b" + ;
"c"
// single-line comment
/* multi-line
comment */
```
## Advanced features (verify against parser before relying on these)
The compiler/parser also supports OOP (`CLASS ... ENDCLASS`, `METHOD ... CLASS X`,
`::member`, `INHERIT FROM`), and Go-inspired concurrency (`GO {|| ...}`, channels `<-`,
`WATCH`/`ENDWATCH` select, `DEFER`). These appear in the grammar but are uncommon in the
solmade app; if you need them, confirm exact syntax in `compiler/parser` and `examples/`
first rather than assuming Harbour-standard behavior.

756
rag/03-rtl-catalog.md Normal file
View File

@@ -0,0 +1,756 @@
---
doc: five-rtl-catalog
title: Five RTL (runtime library) function catalog
keywords: [rtl, builtin, functions, string, array, hash, json, date, regex, charset, hb_, Len, SubStr, hb_jsonDecode, PG, LLM]
summary: Catalog of runtime-library functions callable from Five PRG, grouped by category, extracted from hbrtl/register.go. Verify exotic/less-common entries against register.go.
---
> Source of truth: `/Users/charleskwon/fivenode/fivedev/five/hbrtl/register.go` and impl files. Names listed are as REGISTERED (callable from PRG, case-insensitive). For the common, heavily-used set (Len/SubStr/Left/Right/At/Upper/Lower/AllTrim/PadL/PadR/StrTran/Chr/Asc/Val/Str/hb_NToS/hb_CStr/hb_HGetDef/hb_jsonDecode/hb_jsonEncode) signatures are reliable. For rarer entries, confirm signature in register.go before relying on it.
# Five Runtime Library (hbrtl) Function Catalog
## String Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **LEN** | `LEN(cString \| aArray \| hHash) → nLength` | UTF-8 rune-aware (when charset=UTF8, default); counts runes, not bytes |
| **SUBSTR** | `SUBSTR(cString, nStart [, nLen]) → cSubstring` | 1-based index; UTF-8 rune-aware; negative start = from end |
| **LEFT** | `LEFT(cString, nLen) → cLeft` | Returns leftmost n characters; UTF-8 rune-aware |
| **RIGHT** | `RIGHT(cString, nLen) → cRight` | Returns rightmost n characters; UTF-8 rune-aware |
| **AT** | `AT(cSearch, cTarget) → nPos` | 1-based position of first occurrence; UTF-8 rune-aware; returns 0 if not found |
| **RAT** | `RAT(cSearch, cTarget [, nOccurrence]) → nPos` | 1-based position of LAST occurrence; counts from right |
| **UPPER** | `UPPER(cString) → cUpper` | Converts to uppercase |
| **LOWER** | `LOWER(cString) → cLower` | Converts to lowercase |
| **ALLTRIM** | `ALLTRIM(cString) → cTrimmed` | Removes leading and trailing spaces |
| **LTRIM** | `LTRIM(cString) → cTrimmed` | Removes leading spaces only |
| **RTRIM** | `RTRIM(cString) → cTrimmed` | Removes trailing spaces (TRIM is alias) |
| **PADR** | `PADR(xValue, nLen [, cFill]) → cPadded` | Right-pads with spaces or fill character |
| **PADL** | `PADL(xValue, nLen [, cFill]) → cPadded` | Left-pads with spaces or fill character |
| **PADC** | `PADC(xValue, nLen) → cCentered` | Center-pads with spaces |
| **SPACE** | `SPACE(nCount) → cSpaces` | Returns string of n spaces |
| **REPLICATE** | `REPLICATE(cString, nTimes) → cRepeated` | Repeats string n times |
| **CHR** | `CHR(nCode) → cChar` | UTF-8 codepoint → character (UTF-8 mode); byte value (legacy charset) |
| **ASC** | `ASC(cString) → nCode` | Returns codepoint of first character; UTF-8 rune-aware |
| **STRTRAN** | `STRTRAN(cString, cSearch, cReplace) → cResult` | Replaces all occurrences |
| **STUFF** | `STUFF(cString, nStart, nDel, cInsert) → cResult` | Inserts/replaces characters at position (1-based) |
| **STR** | `STR(nValue [, nWidth [, nDec]]) → cString` | Converts number to string; right-aligned with spaces |
| **VAL** | `VAL(cString) → nValue` | Converts string to numeric; trims and parses int/float |
| **STRZERO** | `STRZERO(nValue, nLen [, nDec]) → cString` | Converts number to string padded with leading zeros |
| **HB_STRREPLACE** | `HB_STRREPLACE(cString, cSearch, cReplace) → cResult` | Replaces all (alias for STRTRAN) |
| **HB_NTOS** | `HB_NTOS(nValue) → cString` | Converts number to string without leading spaces |
| **HB_CSTR** | `HB_CSTR(xValue) → cString` | Converts any value to string representation |
| **HB_VALTOSTR** | `HB_VALTOSTR(xValue) → cString` | Converts value to display string |
| **HB_VALTOEXP** | `HB_VALTOEXP(xValue) → cExpr` | Returns Harbour expression representation (e.g., ".T.", "123", `"text"`) |
| **MEMOREAD** | `MEMOREAD(cFileName) → cContents` | Reads entire file into string |
| **MEMOWRIT** | `MEMOWRIT(cFileName, cString [, lAddEOF]) → lSuccess` | Writes string to file |
| **MEMOTRAN** | `MEMOTRAN(cMemoText [, cSoftCR [, cHardCR]]) → cString` | Replaces soft/hard CR in memo fields |
| **DESCEND** | `DESCEND(cString \| nNumber \| dDate) → xDescended` | Flips each byte (strings); negates (numbers); max-date (dates) |
| **HARDCR** | `HARDCR(cMemoText) → cString` | Converts soft CR to hard CR |
### UTF-8 String Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **HB_UTF8LEN** | `HB_UTF8LEN(cString) → nRuneCount` | Length in UTF-8 runes |
| **HB_UTF8SUBSTR** | `HB_UTF8SUBSTR(cString, nStart [, nLen]) → cSubstring` | UTF-8 rune-based substring extraction |
| **HB_UTF8LEFT** | `HB_UTF8LEFT(cString, nLen) → cLeft` | UTF-8 rune-based left extraction |
| **HB_UTF8RIGHT** | `HB_UTF8RIGHT(cString, nLen) → cRight` | UTF-8 rune-based right extraction |
| **HB_UTF8AT** | `HB_UTF8AT(cSearch, cTarget) → nPos` | UTF-8 rune-based position (1-based) |
| **HB_STRTOHEX** | `HB_STRTOHEX(cString) → cHex` | Converts string to hex representation |
| **HB_HEXTOSTR** | `HB_HEXTOSTR(cHex) → cString` | Converts hex to string |
| **HB_UTF8TOSTR** | `HB_UTF8TOSTR(cUTF8) → cString` | Decodes UTF-8 to internal string |
| **HB_STRTOUTF8** | `HB_STRTOUTF8(cString) → cUTF8` | Encodes to UTF-8 |
| **HB_STRFORMAT** | `HB_STRFORMAT(cFormat, ...) → cString` | Printf-style formatting |
### String Search/Matching
| Function | Signature | Notes |
|----------|-----------|-------|
| **HB_AT** | `HB_AT(cSearch, cTarget) → nPos` | Alias for AT() |
| **HB_RAT** | `HB_RAT(cSearch, cTarget [, nOcc]) → nPos` | Alias for RAT() |
| **HB_ATI** | `HB_ATI(cSearch, cTarget) → nPos` | Case-insensitive AT() |
| **HB_ATX** | `HB_ATX(cSearch, cTarget) → nPos` | Extended AT (with wildcard support) |
| **HB_LEFTEQ** | `HB_LEFTEQ(cLeft, cRight) → lEqual` | Case-sensitive left-part equality |
| **HB_LEFTEQI** | `HB_LEFTEQI(cLeft, cRight) → lEqual` | Case-insensitive left-part equality |
| **HB_WILDMATCH** | `HB_WILDMATCH(cPattern, cString) → lMatch` | Wildcard pattern match (case-sensitive) |
| **HB_WILDMATCHI** | `HB_WILDMATCHI(cPattern, cString) → lMatch` | Wildcard pattern match (case-insensitive) |
| **HB_STRISUTF8** | `HB_STRISUTF8(cString) → lValid` | Checks if string is valid UTF-8 |
| **HB_STRDECODESCAPE** | `HB_STRDECODESCAPE(cString) → cDecoded` | Decodes escape sequences |
| **HB_STRXOR** | `HB_STRXOR(cString, cKey) → cResult` | XOR encryption/decryption |
## Array Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **AADD** | `AADD(aArray, xValue) → aArray` | Appends element; returns modified array |
| **ADEL** | `ADEL(aArray, nPos) → aArray` | Deletes element at position; shifts left |
| **AINS** | `AINS(aArray, nPos) → aArray` | Inserts NIL at position; shifts right |
| **ASIZE** | `ASIZE(aArray, nLen) → aArray` | Resizes array (pads with NIL or truncates) |
| **ACLONE** | `ACLONE(aArray) → aNewArray` | Shallow copy of array |
| **HB_DEEPCOPY** / **HBDEEPCLONE** | `HB_DEEPCOPY(xValue) → xCloned` | Deep recursive clone (arrays & hashes); scalars unchanged |
| **ACOPY** | `ACOPY(aSource, aDest [, nStart [, nCount [, nTargetPos]]]) → aDest` | Copies elements from source to dest |
| **AFILL** | `AFILL(aArray, xValue [, nStart [, nCount]]) → aArray` | Fills array with value |
| **ASORT** | `ASORT(aArray [, nStart [, nCount [, bBlock]]]) → aArray` | Sorts array; optional block comparator |
| **AEVAL** | `AEVAL(aArray, bBlock [, nStart [, nCount]]) → aArray` | Evaluates block for each element |
| **ASCAN** | `ASCAN(aArray, xValue [, nStart [, nCount]]) → nPos` | Scans array for value; returns 1-based position or 0 |
| **ATAIL** | `ATAIL(aArray) → xValue` | Returns last element |
| **HB_ATOKENS** | `HB_ATOKENS(cString [, cDelim]) → aTokens` | Splits string by delimiter (default: whitespace) |
## Hash Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **HB_HASH** | `HB_HASH(key1, val1, key2, val2, ...) → hHash` | Creates hash from alternating key-value pairs |
| **HB_HGET** | `HB_HGET(hHash, xKey) → xValue` | Retrieves value by key; returns NIL if missing |
| **HB_HGETDEF** | `HB_HGETDEF(hHash, xKey, xDefault) → xValue` | Retrieves with default fallback |
| **HB_HSET** | `HB_HSET(hHash, xKey, xValue) → hHash` | Sets key-value; returns hash |
| **HB_HDEL** | `HB_HDEL(hHash, xKey) → hHash` | Deletes key; returns hash |
| **HB_HHASKEY** | `HB_HHASKEY(hHash, xKey) → lExists` | Checks key existence |
| **HB_HKEYS** | `HB_HKEYS(hHash) → aKeys` | Returns array of keys |
| **HB_HVALUES** | `HB_HVALUES(hHash) → aValues` | Returns array of values |
| **HB_HPOS** | `HB_HPOS(hHash, xKey) → nPos` | Returns 1-based position of key (0 if not found) |
| **HB_HKEYAT** | `HB_HKEYAT(hHash, nPos) → xKey` | Returns key at 1-based position |
| **HB_HVALUEAT** | `HB_HVALUEAT(hHash, nPos) → xValue` | Returns value at 1-based position |
| **HB_HCLONE** | `HB_HCLONE(hHash) → hNewHash` | Shallow copy of hash |
## JSON Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **HB_JSONENCODE** | `HB_JSONENCODE(xValue [, lHumanReadable]) → cJSON` | Encodes value to JSON string; optional pretty-print |
| **HB_JSONDECODE** | `HB_JSONDECODE(cJSON [, @xOut]) → xValue \| nBytesParsed` | Decodes JSON; 2-arg form sets @xOut and returns bytes parsed |
| **JSONPRETTY** | `JSONPRETTY(xValue [, cIndent]) → cPrettyJSON` | Pretty-prints JSON with indentation |
| **JSONTO** | `JSONTO(xValue, cFile) → lSuccess` | Writes JSON to file |
| **JSONFROM** | `JSONFROM(cFile) → xValue` | Reads JSON from file |
| **JSONPATH** | `JSONPATH(xValue, cPath) → xResult` | Extracts value from JSON path (e.g., `$.key.sub[0]`) |
| **JSONMERGE** | `JSONMERGE(hDest, hSrc) → hMerged` | Deep merges two hashes |
| **JSONTYPE** | `JSONTYPE(cJSON) → cType` | Returns JSON value type as string |
| **JSONVALID** | `JSONVALID(cJSON) → lValid` | Validates JSON syntax |
| **JSONHTTPGET** | `JSONHTTPGET(cURL [, nTimeout]) → hResult` | HTTP GET + automatic JSON decode |
| **JSONHTTPPOST** | `JSONHTTPPOST(cURL, xBody [, nTimeout]) → hResult` | HTTP POST + JSON body encode/decode |
## Type Checking / Introspection
| Function | Signature | Notes |
|----------|-----------|-------|
| **VALTYPE** | `VALTYPE(xValue) → cType` | Returns 1-char type: "U"(NIL), "L"(logical), "N"(numeric), "C"(string), "D"(date), "A"(array), "H"(hash), "B"(block), "O"(object), "P"(pointer), "S"(symbol) |
| **TYPE** | `TYPE(cSymbol) → cType` | Returns type of symbol/function |
| **EMPTY** | `EMPTY(xValue) → lEmpty` | Checks if value is "empty": NIL, .F., 0, "", date(0), empty array/hash |
| **HB_ISARRAY** / **ISARRAY** | `HB_ISARRAY(xValue) → lArray` | Checks if array |
| **HB_ISHASH** | `HB_ISHASH(xValue) → lHash` | Checks if hash |
| **HB_ISSTRING** / **HB_ISCHAR** / **ISCHARACTER** / **ISMEMO** | `HB_ISSTRING(xValue) → lString` | Checks if string (ISMEMO is alias) |
| **HB_ISNUMERIC** / **ISNUMBER** / **ISNUMERIC** | `HB_ISNUMERIC(xValue) → lNumeric` | Checks if numeric |
| **HB_ISLOGICAL** / **ISLOGICAL** | `HB_ISLOGICAL(xValue) → lLogical` | Checks if logical (.T. or .F.) |
| **HB_ISDATE** / **ISDATE** | `HB_ISDATE(xValue) → lDate` | Checks if date |
| **HB_ISDATETIME** | `HB_ISDATETIME(xValue) → lTimestamp` | Checks if timestamp |
| **HB_ISBLOCK** / **ISBLOCK** | `HB_ISBLOCK(xValue) → lBlock` | Checks if code block |
| **HB_ISOBJECT** / **ISOBJECT** | `HB_ISOBJECT(xValue) → lObject` | Checks if object instance |
| **HB_ISNIL** / **ISNIL** | `HB_ISNIL(xValue) → lNil` | Checks if NIL |
| **HB_ISPOINTER** | `HB_ISPOINTER(xValue) → lPointer` | Checks if pointer (Go native) |
| **HB_ISEVALITEM** | `HB_ISEVALITEM(xValue) → lEvalable` | Checks if block or symbol |
## Date/Time Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **DATE** | `DATE() → dToday` | Returns today's date (Julian format) |
| **TIME** | `TIME() → nSeconds` | Returns seconds elapsed since midnight |
| **YEAR** | `YEAR(dDate) → nYear` | Extracts year from date |
| **MONTH** | `MONTH(dDate) → nMonth` | Extracts month (1-12) from date |
| **DAY** | `DAY(dDate) → nDay` | Extracts day of month (1-31) from date |
| **DOW** | `DOW(dDate) → nDayOfWeek` | Returns day of week (1=Sunday...7=Saturday) |
| **DTOC** | `DTOC(dDate) → cString` | Converts date to string per SET DATE FORMAT |
| **DTOS** | `DTOS(dDate) → cISO` | Converts date to ISO string (YYYYMMDD) |
| **STOD** | `STOD(cISO) → dDate` | Parses ISO string (YYYYMMDD) to date |
| **CTOD** | `CTOD(cDateString) → dDate` | Parses string per SET DATE FORMAT to date |
| **CDOW** | `CDOW(dDate) → cDayName` | Returns day name ("Monday", "Tuesday", etc.) |
| **CMONTH** | `CMONTH(dDate) → cMonthName` | Returns month name ("January", etc.) |
| **SECONDS** | `SECONDS() → nSeconds` | Returns seconds since midnight (current time) |
| **SETDATEFORMAT** | `SETDATEFORMAT(cFormat) → cNil` | Sets date display format (e.g., "MM/DD/YY", "DD-MM-YYYY") |
| **SETEPOCH** | `SETEPOCH(nYear) → cNil` | Sets 2-digit year base (default: 1900) |
| **SETCENTURY** | `SETCENTURY(lOnOff \| cOnOff) → cNil` | Toggles 4-digit year display |
### Timestamp Functions (Extended)
| Function | Signature | Notes |
|----------|-----------|-------|
| **HB_DATETIME** | `HB_DATETIME(nJulian, nMs) → tTimestamp` | Creates timestamp from date + milliseconds |
| **HB_HOUR** | `HB_HOUR(tTimestamp) → nHour` | Extracts hours (0-23) |
| **HB_MINUTE** | `HB_MINUTE(tTimestamp) → nMinute` | Extracts minutes (0-59) |
| **HB_SEC** / **HB_SECOND** | `HB_SEC(tTimestamp) → nSec` | Extracts seconds (0-59) |
| **HB_TTOC** | `HB_TTOC(tTimestamp [, cFormat]) → cString` | Converts timestamp to string |
| **HB_CTOT** | `HB_CTOT(cString [, cFormat]) → tTimestamp` | Parses string to timestamp |
| **HB_TTOS** | `HB_TTOS(tTimestamp) → cISO` | Timestamp to ISO string |
| **HB_STOT** | `HB_STOT(cISO) → tTimestamp` | ISO string to timestamp |
| **HB_TTOD** | `HB_TTOD(tTimestamp) → dDate` | Extracts date from timestamp |
| **HB_DTOT** | `HB_DTOT(dDate) → tTimestamp` | Creates timestamp from date at 00:00:00 |
| **HB_TTOHOUR** | `HB_TTOHOUR(tTimestamp) → nHour` | Alias for HB_HOUR |
| **HB_TTOMIN** | `HB_TTOMIN(tTimestamp) → nMin` | Alias for HB_MINUTE |
| **HB_TTOSEC** | `HB_TTOSEC(tTimestamp) → nSec` | Alias for HB_SEC |
| **HB_TTOMSEC** | `HB_TTOMSEC(tTimestamp) → nMs` | Extracts milliseconds portion |
| **HB_TTON** | `HB_TTON(tTimestamp) → nTotal` | Converts timestamp to numeric seconds |
| **HB_NTOT** | `HB_NTOT(nSeconds) → tTimestamp` | Converts numeric seconds to timestamp |
| **HB_NTOHOUR** | `HB_NTOHOUR(nSeconds) → nHour` | Extracts hours from numeric seconds |
| **HB_NTOMIN** | `HB_NTOMIN(nSeconds) → nMin` | Extracts minutes from numeric seconds |
| **HB_NTOSEC** | `HB_NTOSEC(nSeconds) → nSec` | Extracts seconds from numeric seconds |
| **HB_WEEK** | `HB_WEEK(dDate) → nWeek` | Returns ISO week number (1-53) |
| **HB_CDAY** | `HB_CDAY(dDate) → cDay` | Returns day name string |
| **DAYS** | `DAYS(dDate1, dDate2) → nDays` | Difference in days |
| **ELAPTIME** | `ELAPTIME(nStart, nEnd) → cElapsed` | Formats elapsed time (HH:MM:SS) |
| **AMPM** | `AMPM(nSeconds) → cTime` | Formats time as AM/PM |
| **SECS** | `SECS(cTime) → nSeconds` | Parses HH:MM:SS to seconds |
| **HB_MILLISECONDS** | `HB_MILLISECONDS() → nMs` | Returns current time in milliseconds |
## Regular Expression Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **HB_REGEXCOMP** | `HB_REGEXCOMP(cPattern [, lCaseSensitive]) → pRegex` | Compiles regex to pointer; lCaseSensitive=.F. adds (?i) |
| **HB_REGEX** | `HB_REGEX(cPattern \| pRegex, cString [, lCaseSensitive]) → aSubmatches` | Returns array of submatches (match + capture groups); empty array if no match |
| **HB_REGEXMATCH** | `HB_REGEXMATCH(cPattern \| pRegex, cString [, lCaseSensitive]) → lMatch` | Boolean match test |
| **HB_REGEXALL** | `HB_REGEXALL(cPattern \| pRegex, cString [, lCaseSensitive]) → aMatches` | Returns array of all non-overlapping matches |
| **HB_REGEXSPLIT** | `HB_REGEXSPLIT(cPattern \| pRegex, cString [, lCaseSensitive]) → aTokens` | Splits string by pattern; returns array |
| **HB_REGEXREPLACE** | `HB_REGEXREPLACE(cPattern \| pRegex, cString, cReplace [, lCaseSensitive]) → cResult` | Replaces all matches with replacement string |
## Math Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **ABS** | `ABS(nValue) → nAbsolute` | Absolute value |
| **INT** | `INT(nValue) → nInteger` | Integer part (truncates toward zero) |
| **ROUND** | `ROUND(nValue, nDecimals) → nRounded` | Rounds to n decimal places |
| **MAX** | `MAX(xValue1, xValue2) → xMax` | Maximum of two values (numeric, date, or fallback) |
| **MIN** | `MIN(xValue1, xValue2) → xMin` | Minimum of two values |
| **SQRT** | `SQRT(nValue) → nRoot` | Square root |
| **LOG** | `LOG(nValue) → nLog` | Natural logarithm |
| **EXP** | `EXP(nValue) → nExponential` | e^n |
| **MOD** | `MOD(nDividend, nDivisor) → nRemainder` | Modulo operation |
## Character Classification
| Function | Signature | Notes |
|----------|-----------|-------|
| **ISDIGIT** | `ISDIGIT(cChar) → lDigit` | Checks if character is 0-9 |
| **ISALPHA** | `ISALPHA(cChar) → lAlpha` | Checks if letter (A-Z, a-z) |
| **ISALNUM** | `ISALNUM(cChar) → lAlnum` | Checks if alphanumeric |
| **ISUPPER** | `ISUPPER(cChar) → lUpper` | Checks if uppercase |
| **ISLOWER** | `ISLOWER(cChar) → lLower` | Checks if lowercase |
| **ISSPACE** | `ISSPACE(cChar) → lSpace` | Checks if whitespace |
| **HB_ASCIIISALPHA** | `HB_ASCIIISALPHA(cChar) → lAlpha` | ASCII-only alpha check |
| **HB_ASCIIISDIGIT** | `HB_ASCIIISDIGIT(cChar) → lDigit` | ASCII-only digit check |
| **HB_ASCIIISLOWER** | `HB_ASCIIISLOWER(cChar) → lLower` | ASCII-only lowercase check |
| **HB_ASCIIISUPPER** | `HB_ASCIIISUPPER(cChar) → lUpper` | ASCII-only uppercase check |
| **HB_ASCIIUPPER** | `HB_ASCIIUPPER(cString) → cUpper` | ASCII-only uppercase conversion |
| **HB_ASCIILOWER** | `HB_ASCIILOWER(cString) → cLower` | ASCII-only lowercase conversion |
## Encoding / Hashing (Cryptographic)
| Function | Signature | Notes |
|----------|-----------|-------|
| **HB_MD5** | `HB_MD5(cString) → cMD5Hex` | MD5 hash (Go stdlib) |
| **HB_SHA256** | `HB_SHA256(cString) → cSHA256Hex` | SHA-256 hash (Go stdlib) |
| **HB_CRC32** | `HB_CRC32(cString) → nCRC32` | CRC32 checksum |
| **HB_BASE64ENCODE** | `HB_BASE64ENCODE(cString) → cBase64` | Encodes to Base64 |
| **HB_BASE64DECODE** | `HB_BASE64DECODE(cBase64) → cString` | Decodes from Base64 |
| **HB_NUMTOHEX** | `HB_NUMTOHEX(nNumber) → cHex` | Converts number to hex string |
| **HB_HEXTONUM** | `HB_HEXTONUM(cHex) → nNumber` | Converts hex string to number |
## Bit Operations (Go Native)
| Function | Signature | Notes |
|----------|-----------|-------|
| **HB_BITAND** | `HB_BITAND(nVal1, nVal2) → nResult` | Bitwise AND |
| **HB_BITOR** | `HB_BITOR(nVal1, nVal2) → nResult` | Bitwise OR |
| **HB_BITXOR** | `HB_BITXOR(nVal1, nVal2) → nResult` | Bitwise XOR |
| **HB_BITNOT** | `HB_BITNOT(nValue) → nResult` | Bitwise NOT |
| **HB_BITSHIFT** | `HB_BITSHIFT(nValue, nShift) → nResult` | Bit shift (positive=left, negative=right) |
| **HB_BITTEST** | `HB_BITTEST(nValue, nBit) → lSet` | Tests if bit is set |
| **HB_BITSET** | `HB_BITSET(nValue, nBit) → nResult` | Sets bit |
| **HB_BITRESET** | `HB_BITRESET(nValue, nBit) → nResult` | Resets bit |
## Binary Conversion
| Function | Signature | Notes |
|----------|-----------|-------|
| **BIN2I** | `BIN2I(cBinary) → nShortInt` | Binary string (2 bytes) → short integer |
| **BIN2W** | `BIN2W(cBinary) → nWord` | Binary string (2 bytes) → word |
| **BIN2L** | `BIN2L(cBinary) → nLong` | Binary string (4 bytes) → long integer |
| **I2BIN** | `I2BIN(nShortInt) → cBinary` | Short integer → 2-byte binary |
| **W2BIN** | `W2BIN(nWord) → cBinary` | Word → 2-byte binary |
| **L2BIN** | `L2BIN(nLong) → cBinary` | Long integer → 4-byte binary |
## Charset / Codepage Management
| Function | Signature | Notes |
|----------|-----------|-------|
| **HB_GETCHARSET** | `HB_GETCHARSET() → cName` | Returns active charset (e.g., "UTF8", "CP949") |
| **HB_SETCHARSET** | `HB_SETCHARSET([cName]) → cPrev` | Sets charset; returns previous; no-arg queries current |
| **HB_CDPSELECT** | `HB_CDPSELECT([cName]) → cPrev` | Harbour alias for HB_SETCHARSET |
| **HB_TRANSLATE** | `HB_TRANSLATE(cString, cFrom, cTo) → cConverted` | Converts string between charsets |
**Note:** When charset is UTF-8 (default), core string functions (LEN, CHR, ASC, SUBSTR, LEFT, RIGHT, AT, PADR, PADL) operate on UTF-8 runes (characters), not bytes. When a non-UTF-8 charset is active (e.g., CP949), these same functions operate on bytes in that encoding.
## File I/O Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **FOPEN** | `FOPEN(cFileName, cMode) → nHandle` | Opens file; modes: "R"(read), "W"(write), "A"(append); returns -1 on error |
| **FCREATE** | `FCREATE(cFileName [, nAttr]) → nHandle` | Creates/truncates file; returns handle or -1 |
| **FCLOSE** | `FCLOSE(nHandle) → lSuccess` | Closes file handle |
| **FREAD** | `FREAD(nHandle, nBytes) → cData` | Reads up to n bytes |
| **FWRITE** | `FWRITE(nHandle, cData [, nBytes]) → nBytesWritten` | Writes data; returns bytes written |
| **FSEEK** | `FSEEK(nHandle, nOffset [, nFrom]) → nNewPos` | Seeks in file; nFrom: 0=start, 1=current, 2=end |
| **FERASE** | `FERASE(cFileName) → lSuccess` | Deletes file |
| **FRENAME** | `FRENAME(cOld, cNew) → lSuccess` | Renames file |
| **HB_FILEEXISTS** | `HB_FILEEXISTS(cFileName) → lExists` | Checks if file exists |
| **HB_FILEDELETE** | `HB_FILEDELETE(cFileName) → lSuccess` | Deletes file |
| **HB_FILEMATCH** | `HB_FILEMATCH(cPattern, cFileName) → lMatch` | Wildcard file match |
| **HB_FSIZE** | `HB_FSIZE(cFileName) → nBytes` | File size in bytes |
| **HB_FCOPY** | `HB_FCOPY(cSource, cDest) → lSuccess` | Copies file |
| **HB_FEOF** | `HB_FEOF(nHandle) → lEOF` | Checks if at end of file |
| **HB_FCOMMIT** | `HB_FCOMMIT(nHandle) → lSuccess` | Flushes file buffer |
| **HB_FREADLEN** | `HB_FREADLEN(nHandle) → nLen` | Reads line length without consuming |
| **HB_FGETATTR** | `HB_FGETATTR(cFileName) → cAttr` | Gets file attributes |
| **HB_FSETATTR** | `HB_FSETATTR(cFileName, cAttr) → lSuccess` | Sets file attributes |
| **HB_FGETDATETIME** | `HB_FGETDATETIME(cFileName) → tTimestamp` | Gets file modification time |
| **HB_FSETDATETIME** | `HB_FSETDATETIME(cFileName, tTime) → lSuccess` | Sets file modification time |
| **HB_FLOCK** | `HB_FLOCK(nHandle, nMode) → lSuccess` | Locks file region |
| **HB_FUNLOCK** | `HB_FUNLOCK(nHandle) → lSuccess` | Unlocks file region |
| **HB_FNAMEEXISTS** | `HB_FNAMEEXISTS(cPath) → lExists` | Checks file/directory existence |
| **HB_FNAMEEXTSET** | `HB_FNAMEEXTSET(cPath, cExt) → cNewPath` | Sets file extension |
| **HB_FNAMENAMEEXT** | `HB_FNAMENAMEEXT(cPath) → cNameExt` | Extracts filename + extension |
## Directory/Path Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **CURDIR** | `CURDIR() → cPath` | Current working directory |
| **DIRCHANGE** | `DIRCHANGE(cPath) → lSuccess` | Changes current directory |
| **DIRECTORY** | `DIRECTORY(cMask) → aFiles` | Returns array of files matching mask (directory listing) |
| **DIRMAKE** | `DIRMAKE(cPath) → lSuccess` | Creates directory |
| **DIRREMOVE** | `DIRREMOVE(cPath) → lSuccess` | Removes directory |
| **HB_DIREXISTS** | `HB_DIREXISTS(cPath) → lExists` | Checks if directory exists |
| **HB_DIRCREATE** | `HB_DIRCREATE(cPath) → lSuccess` | Creates directory (alias for DIRMAKE) |
| **HB_FTEMPCREATE** | `HB_FTEMPCREATE() → cTempFile` | Creates unique temp file path |
| **HB_FNAMESPLIT** | `HB_FNAMESPLIT(cPath [, @cDir, @cName, @cExt]) → lSuccess` | Splits path into components |
| **HB_FNAMEDIR** | `HB_FNAMEDIR(cPath) → cDir` | Extracts directory portion |
| **HB_FNAMEEXT** | `HB_FNAMEEXT(cPath) → cExt` | Extracts extension |
| **HB_FNAMENAME** | `HB_FNAMENAME(cPath) → cName` | Extracts filename without extension |
| **HB_FNAMEMERGE** | `HB_FNAMEMERGE(cDir, cName, cExt) → cPath` | Merges path components |
| **HB_DIRTEMP** | `HB_DIRTEMP() → cTempDir` | Returns system temp directory |
| **HB_DISKSPACE** | `HB_DISKSPACE(cPath [, nType]) → nBytes` | Disk space info |
| **DISKSPACE** | `DISKSPACE(cPath) → nBytes` | Disk free space (alias) |
| **HB_DIRBASE** | `HB_DIRBASE() → cBaseDir` | Application base directory |
## OS / Environment Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **GETENV** / **HB_GETENV** | `GETENV(cVarName) → cValue` | Gets environment variable; returns "" if not set |
| **SETENV** / **HB_SETENV** | `SETENV(cVarName, cValue) → lSuccess` | Sets environment variable |
| **OS** | `OS() → cOS` | Returns operating system string (e.g., "LINUX", "DARWIN", "WINDOWS") |
| **VERSION** | `VERSION() → cVersion` | Returns Harbour/Five version string |
| **HB_VERSION** | `HB_VERSION() → cVersion` | Returns version (Go: "Five 0.1") |
| **HB_COMPILER** | `HB_COMPILER() → cCompiler` | Returns compiler info |
| **HB_OSNEWLINE** | `HB_OSNEWLINE() → cNewline` | Returns OS newline ("\r\n" or "\n") |
| **HB_OSPATHSEPARATOR** | `HB_OSPATHSEPARATOR() → cSep` | Returns path separator ("/" or "\") |
| **HB_CWD** | `HB_CWD() → cDir` | Current working directory (alias for CURDIR) |
| **HB_PROGNAME** | `HB_PROGNAME() → cProgram` | Program/executable name |
| **HB_USERNAME** | `HB_USERNAME() → cUser` | Current user name |
| **HB_GETHOSTNAME** | `HB_GETHOSTNAME() → cHost` | Machine hostname |
| **HB_RUN** | `HB_RUN(cCommand) → nExitCode` | Executes shell command; returns exit code |
| **HB_PROCESSRUN** | `HB_PROCESSRUN(cCommand [, cStdIn]) → cStdOut` | Runs command; returns stdout |
| **WAIT** | `WAIT([cPrompt]) → cKey` | Waits for key press (terminal) |
## Terminal / Display Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **SETPOS** | `SETPOS(nRow, nCol) → aOldPos` | Sets cursor position; returns old [row, col] |
| **ROW** | `ROW() → nRow` | Current cursor row (0-based) |
| **COL** | `COL() → nCol` | Current cursor column (0-based) |
| **DEVPOS** | `DEVPOS() → aPos` | Returns [row, col] array |
| **DEVOUT** | `DEVOUT(xValue) → lSuccess` | Outputs value to device (display) |
| **DISPOUT** | `DISPOUT(nRow, nCol, xValue) → lSuccess` | Outputs at position |
| **DEVOUTPICT** | `DEVOUTPICT(nRow, nCol, xValue, cPicture) → lSuccess` | Outputs with picture format |
| **DISPBOX** | `DISPBOX(nTop, nLeft, nBottom, nRight, cBox [, cColor]) → lSuccess` | Draws box |
| **CLS** | `CLS() → lSuccess` | Clears screen |
| **SCROLL** | `SCROLL(nTop, nLeft, nBottom, nRight, nRows [, cColor]) → lSuccess` | Scrolls region |
| **SETCOLOR** | `SETCOLOR(cColorPair) → cOldColor` | Sets foreground/background colors (e.g., "W+/B") |
| **SETCURSOR** | `SETCURSOR(nStyle) → nOldStyle` | Sets cursor shape (0=none, 1=normal, 2=heavy) |
| **MAXROW** | `MAXROW() → nMaxRow` | Terminal max row (height - 1) |
| **MAXCOL** | `MAXCOL() → nMaxCol` | Terminal max column (width - 1) |
| **DISPBEGIN** | `DISPBEGIN() → nLevel` | Begins display freeze (disables updates) |
| **DISPEND** | `DISPEND() → nLevel` | Ends display freeze |
| **DISPCOUNT** | `DISPCOUNT() → nLevel` | Returns freeze level |
| **SAVESCREEN** | `SAVESCREEN(nTop, nLeft, nBottom, nRight) → cScreenData` | Saves screen region |
| **RESTSCREEN** | `RESTSCREEN(nTop, nLeft, nBottom, nRight, cScreenData) → lSuccess` | Restores screen region |
| **ALERT** | `ALERT(cMessage [, aButtons]) → nChoice` | Displays alert dialog; returns button index (1-based) |
| **QOUT** | `QOUT(xValue1, ...) → cNil` | Outputs values with line feed (like `? ...`) |
| **QQOUT** | `QQOUT(xValue1, ...) → cNil` | Outputs values without line feed (like `?? ...`) |
| **OUTSTD** | `OUTSTD(cString) → cNil` | Outputs to stdout |
| **OUTERR** | `OUTERR(cString) → cNil` | Outputs to stderr |
| **HB_DISPOUTAT** | `HB_DISPOUTAT(nRow, nCol, xValue [, cColor]) → lSuccess` | Outputs at position with optional color |
| **HB_COLORINDEX** | `HB_COLORINDEX(cColorPair) → nIndex` | Gets color pair index |
| **HB_DISPBOX** | `HB_DISPBOX(nTop, nLeft, nBottom, nRight, cBox [, cColor]) → lSuccess` | Extended box drawing |
| **HB_DISPOUTATBOX** | `HB_DISPOUTATBOX(nRow, nCol, xValue, cBox) → lSuccess` | Box output helper |
## Keyboard Input Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **INKEY** | `INKEY([nTimeout]) → nKey` | Reads key; timeout in 1/100ths second; returns -1 on timeout |
| **LASTKEY** | `LASTKEY() → nKey` | Returns last key read |
| **NEXTKEY** | `NEXTKEY() → nKey` | Peeks next key without consuming |
| **READKEY** | `READKEY() → nKey` | Reads key (synonym for INKEY) |
| **SETKEY** | `SETKEY(nKey, bBlock) → bOld` | Associates block with key press |
| **KEYBOARD** | `KEYBOARD(cString) → lSuccess` | Injects string into keyboard buffer |
| **HB_KEYPUT** | `HB_KEYPUT(nKey) → lSuccess` | Puts key into buffer |
| **HB_KEYCHAR** | `HB_KEYCHAR(nKey) → cChar` | Gets character representation of key code |
| **HB_KEYINS** | `HB_KEYINS(nKey) → lSuccess` | Inserts key into input stream |
## Stack/Introspection Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **PCOUNT** | `PCOUNT() → nParamCount` | Returns number of parameters passed to current function |
| **PVALUE** | `PVALUE(nIndex) → xValue` | Returns nth parameter value (1-based); NIL if out of bounds |
| **HB_PVALUE** | `HB_PVALUE(nIndex) → xValue` | Alias for PVALUE |
| **PROCNAME** | `PROCNAME([nLevel]) → cName` | Returns function/procedure name; 0=current (default), 1=caller, etc. |
| **PROCLINE** | `PROCLINE([nLevel]) → nLine` | Returns line number in source |
| **PROCFILE** | `PROCFILE([nLevel]) → cFile` | Returns source file path |
| **ERRORLEVEL** | `ERRORLEVEL() → nLevel` | Returns error level (0 = success) |
## Error Handling
| Function | Signature | Notes |
|----------|-----------|-------|
| **ERRORBLOCK** | `ERRORBLOCK([bBlock]) → bOld` | Sets/gets error handler block; no-arg returns current |
| **ERRORNEW** | `ERRORNEW() → oError` | Creates new error object |
| **DOSERROR** | `DOSERROR() → nCode` | Returns last OS error code |
| **FERROR** | `FERROR() → nCode` | Returns last file I/O error |
| **HB_ERRORLOG** | `HB_ERRORLOG(cMessage) → lSuccess` | Logs error to file |
| **HB_SETERRORLOGPATH** | `HB_SETERRORLOGPATH(cPath) → cOldPath` | Sets error log file path |
| **HB_SETERRORLOGHOOK** | `HB_SETERRORLOGHOOK(bBlock) → bOld` | Sets error log hook block |
| **ERRORSYS** | `ERRORSYS() → nCode` | System error (platform-specific) |
| **BREAK** | `BREAK([xRetVal]) → xRetVal` | Returns value from BREAK statement / breaks from loop |
## Database Functions (RDD/Work Area)
### Navigation & Status
| Function | Signature | Notes |
|----------|-----------|-------|
| **EOF** | `EOF() → lEOF` | End of file flag for current work area |
| **BOF** | `BOF() → lBOF` | Beginning of file flag |
| **FOUND** | `FOUND() → lFound` | Last SEEK/FIND success flag |
| **RECNO** | `RECNO() → nRecordNum` | Current record number (1-based); 0 if EOF |
| **RECCOUNT** | `RECCOUNT() → nTotalRecords` | Total record count; LASTREC is alias |
| **DELETED** | `DELETED() → lDeleted` | Deleted flag of current record |
| **ALIAS** | `ALIAS() → cAlias` | Current work area alias name |
| **SELECT** | `SELECT(cAlias \| nArea) → nNewArea` | Selects work area by name or number |
### Record Manipulation
| Function | Signature | Notes |
|----------|-----------|-------|
| **DBGOTO** | `DBGOTO(nRecno) → lSuccess` | Positions to record by number |
| **DBSKIP** | `DBSKIP([nRecords]) → lSuccess` | Moves n records (default: 1); negative = backward |
| **DBGOTOP** | `DBGOTOP() → lSuccess` | Positions to first record |
| **DBGOBOTTOM** | `DBGOBOTTOM() → lSuccess` | Positions to last record |
| **DBAPPEND** | `DBAPPEND() → lSuccess` | Creates new blank record at end |
| **DBDELETE** | `DBDELETE() → lSuccess` | Marks current record deleted |
| **DBRECALL** | `DBRECALL() → lSuccess` | Unmarks deleted record; RECALL is alias |
| **DBCOMMIT** | `DBCOMMIT() → lSuccess` | Flushes current work area to disk |
| **DBUNLOCK** | `DBUNLOCK() → lSuccess` | Unlocks all locks in work area |
| **DBRLOCK** | `DBRLOCK(nRecno) → lSuccess` | Record locks record; RLOCK in one arg |
| **DBRUNLOCK** | `DBRUNLOCK(nRecno) → lSuccess` | Unlocks specific record |
| **FLOCK** | `FLOCK() → lSuccess` | File locks entire table |
### Field Access
| Function | Signature | Notes |
|----------|-----------|-------|
| **FIELDGET** | `FIELDGET(nFieldNum) → xValue` | Gets field value by number (1-based) |
| **FIELDPUT** | `FIELDPUT(nFieldNum, xValue) → xValue` | Sets field value by number; returns value |
| **FCOUNT** | `FCOUNT() → nFieldCount` | Number of fields in current structure |
| **FIELDNAME** | `FIELDNAME(nFieldNum) → cName` | Field name by position (1-based) |
| **FIELDPOS** | `FIELDPOS(cFieldName) → nPos` | Position of field by name (1-based, 0 if not found) |
| **FIELDTYPE** | `FIELDTYPE(nFieldNum) → cType` | Field type ("C", "N", "D", "L", "M", etc.) |
| **FIELDLEN** | `FIELDLEN(nFieldNum) → nLen` | Field length in bytes |
| **FIELDDEC** | `FIELDDEC(nFieldNum) → nDec` | Decimal places for numeric field |
| **FIELDBLOCK** | `FIELDBLOCK(cFieldName) → bBlock` | Returns code block for field access |
| **FIELDWBLOCK** | `FIELDWBLOCK(cFieldName) → bBlock` | Returns code block for field write |
| **AFIELDS** | `AFIELDS() → aStructure` | Returns array of field descriptors [name, type, len, dec, ...] |
| **DBSTRUCT** | `DBSTRUCT() → aStructure` | Alias for AFIELDS |
### Table Operations
| Function | Signature | Notes |
|----------|-----------|-------|
| **DBUSEAREA** | `DBUSEAREA(lShared, cRDD, cFile, cAlias [, lReadOnly]) → lSuccess` | Opens table into work area |
| **DBCLOSEAREA** | `DBCLOSEAREA() → lSuccess` | Closes current work area |
| **DBCLOSEALL** | `DBCLOSEALL() → lSuccess` | Closes all work areas |
| **DBPACK** | `DBPACK() → lSuccess` | Removes deleted records; PACK is alias; __DBPACK |
| **DBZAP** | `DBZAP() → lSuccess` | Deletes all records; ZAP is alias; __DBZAP |
| **DBPACK** / **PACK** | `PACK() → lSuccess` | Compacts table (removes deleted) |
| **DBCREATE** | `DBCREATE(cFileName, aStructure) → lSuccess` | Creates new table with structure |
| **DBCOPY** | `__DBCOPY(cFileName) → lSuccess` | Copies table to new file |
| **DBSORT** | `__DBSORT(cFileName, aFields) → lSuccess` | Sorts records to new file |
| **DBLIST** | `__DBLIST() → lSuccess` | Lists records (debug) |
| **DBTOTAL** | `__DBTOTAL(...) → aResult` | Totals numeric fields |
| **DBJOIN** | `__DBJOIN(...) → aResult` | Joins tables |
| **DBUPDATE** | `__DBUPDATE(...) → lSuccess` | Updates records from another table |
### Seek / Locate / Filter
| Function | Signature | Notes |
|----------|-----------|-------|
| **DBSEEK** | `DBSEEK(xValue [, lSoftSeek]) → lFound` | Seeks on active index; soft=fuzzy match |
| **DBLOCATE** | `DBLOCATE(bCondition) → lFound` | Linear search with block condition |
| **__DBCONTINUE** | `__DBCONTINUE() → lFound` | Continues last LOCATE |
| **DBSETFILTER** | `DBSETFILTER(bBlock [, cExpr]) → cOld` | Sets filter block; returns old filter |
| **DBCLEARFILTER** | `DBCLEARFILTER() → lSuccess` | Removes filter |
| **DBFILTER** | `DBFILTER() → cFilter` | Returns current filter expression |
| **DBEVAL** | `DBEVAL(bBlock [, bFor [, bWhile]]) → nCount` | Evaluates block for each record; returns count |
| **__DBAVERAGE** | `__DBAVERAGE(...) → nAverage` | Averages numeric field |
### Index/Order
| Function | Signature | Notes |
|----------|-----------|-------|
| **ORDSETFOCUS** | `ORDSETFOCUS(nOrder \| cTag) → cOldOrder` | Sets active order/index |
| **ORDCREATE** | `ORDCREATE(cFileName, cTag, cExpr [, bFor]) → lSuccess` | Creates index order |
| **DBCREATEINDEX** | `DBCREATEINDEX(cFileName, cExpr [, bFor]) → lSuccess` | Alias for ORDCREATE |
| **DBCLEARINDEX** | `DBCLEARINDEX() → lSuccess` | Removes index associations |
| **INDEXORD** | `INDEXORD() → nOrder` | Current index order number (1-based, 0=none) |
| **INDEXKEY** | `INDEXKEY([nOrder]) → cExpr` | Index key expression |
| **ORDCOUNT** | `ORDCOUNT() → nCount` | Number of open indexes |
| **ORDNAME** | `ORDNAME([nOrder]) → cTag` | Order tag name |
| **ORDKEY** | `ORDKEY([nOrder]) → cExpr` | Order key expression |
| **ORDFOR** | `ORDFOR([nOrder]) → cFor` | Order FOR condition |
| **ORDSCOPE** | `ORDSCOPE(nScopeNum [, xValue]) → xScope` | Gets/sets order scope (soft-seek bounds) |
| **ORDLISTREBUILD** | `ORDLISTREBUILD() → lSuccess` | Rebuilds all indexes |
| **DBREINDEX** | `DBREINDEX() → lSuccess` | Alias for ORDLISTREBUILD |
| **ORDINFO** | `ORDINFO(nOrdNum, nInfoType) → xInfo` | Gets order metadata |
### Work Area Introspection
| Function | Signature | Notes |
|----------|-----------|-------|
| **DBINFO** | `DBINFO(nInfoType) → xValue` | Gets work area info (RDD-specific) |
| **DBORDERINFO** | `DBORDERINFO(nInfoType) → xValue` | Gets order metadata |
| **RDDINFO** | `RDDINFO(nInfoType [, cRDD]) → xValue` | Gets RDD info |
| **RDDNAME** | `RDDNAME() → cRDD` | Current RDD driver name |
| **RDDLIST** | `RDDLIST() → aRDDs` | List of available RDD drivers |
| **RDDSETDEFAULT** | `RDDSETDEFAULT(cRDD) → cOld` | Sets default RDD |
### HBSIX Compatibility (Extended Index Support)
| Function | Signature | Notes |
|----------|-----------|-------|
| **SX_SETTAG** | `SX_SETTAG(cTag) → cOld` | Sets active HBSIX tag |
| **SX_INDEXTAG** | `SX_INDEXTAG(nOrd) → cTag` | Gets tag name for order |
| **SX_TAGORDER** | `SX_TAGORDER(cTag) → nOrd` | Gets order for tag name |
| **SX_TAGCOUNT** | `SX_TAGCOUNT() → nCount` | Number of tags |
| **SX_TAGS** | `SX_TAGS() → aTagNames` | Array of all tag names |
| **SX_SETFILEORD** | `SX_SETFILEORD(cFileName) → lSuccess` | Associates index file |
| **SX_ISDBT** | `SX_ISDBT() → lDBT` | Checks if using .DBT memo file |
| **SX_ISFPT** | `SX_ISFPT() → lFPT` | Checks if using .FPT memo file |
| **SX_ISSMT** | `SX_ISSMT() → lSMT` | Checks if using .SMT memo file |
| **SX_AUTOOPEN** | `SX_AUTOOPEN(lMode) → lOld` | Auto-opens indexes on table open |
| **SX_AUTOSHARE** | `SX_AUTOSHARE(lMode) → lOld` | Auto-shares index file |
| **SX_BLOB2FILE** | `SX_BLOB2FILE(nFieldNum, cFileName) → lSuccess` | Extracts BLOB to file |
| **SX_FILE2BLOB** | `SX_FILE2BLOB(nFieldNum, cFileName) → lSuccess` | Loads file to BLOB |
| **SX_SETTRIGGER** | `SX_SETTRIGGER(...) → lSuccess` | Sets HBSIX trigger |
| **SX_VFGET** | `SX_VFGET(...) → xValue` | Gets virtual field value |
| **SX_DBFENCRYPT** | `SX_DBFENCRYPT(cPassword) → lSuccess` | Encrypts table with password |
| **SX_DBFDECRYPT** | `SX_DBFDECRYPT(cPassword) → lSuccess` | Decrypts table |
| **SX_COMPRESS** | `SX_COMPRESS() → lSuccess` | Compresses memo file |
| **SX_DECOMPRESS** | `SX_DECOMPRESS() → lSuccess` | Decompresses memo file |
## SET Command Functions
| Function | Signature | Notes |
|----------|-----------|-------|
| **SET** | `SET(nSetting [, xValue]) → xOld` | Generic SET function |
| **SETDELETED** | `SETDELETED([lMode]) → lOld` | Toggles respecting SET DELETED flag |
| **SETEXACT** | `SETEXACT([lMode]) → lOld` | Toggles exact string comparison (SET EXACT) |
| **SETSOFTSEEK** | `SETSOFTSEEK([lMode]) → lOld` | Toggles soft-seek (fuzzy match on failed seek) |
| **SETEXCLUSIVE** | `SETEXCLUSIVE([lMode]) → lOld` | Toggles exclusive table open mode |
| **SETFIXED** | `SETFIXED([lMode]) → lOld` | Toggles fixed numeric width (SET FIXED) |
| **SETCANCEL** | `SETCANCEL([lMode]) → lOld` | Toggles cancel key behavior |
| **SETBELL** | `SETBELL([lMode]) → lOld` | Toggles audio bell |
| **SETCONFIRM** | `SETCONFIRM([lMode]) → lOld` | Toggles confirm mode |
| **SETINSERT** | `SETINSERT([lMode]) → lOld` | Toggles insert mode |
| **SETESCAPE** | `SETESCAPE([lMode]) → lOld` | Toggles ESC key behavior |
| **SETWRAP** | `SETWRAP([lMode]) → lOld` | Toggles line wrapping in input |
### SET Constants (for programmatic reference)
| Function | Signature | Notes |
|----------|-----------|-------|
| **_SET_EXACT** | `_SET_EXACT → nConstant` | SET EXACT setting number |
| **_SET_DELETED** | `_SET_DELETED → nConstant` | SET DELETED setting number |
| **_SET_SOFTSEEK** | `_SET_SOFTSEEK → nConstant` | SET SOFTSEEK setting number |
| **_SET_EXCLUSIVE** | `_SET_EXCLUSIVE → nConstant` | SET EXCLUSIVE setting number |
| **_SET_DATEFORMAT** | `_SET_DATEFORMAT → nConstant` | SET DATEFORMAT setting number |
| **_SET_DECIMALS** | `_SET_DECIMALS → nConstant` | SET DECIMALS setting number |
| **_SET_EPOCH** | `_SET_EPOCH → nConstant` | SET EPOCH setting number |
## Evaluation/Macros
| Function | Signature | Notes |
|----------|-----------|-------|
| **EVAL** | `EVAL(bBlock [, xArg1, ...]) → xResult` | Evaluates code block with arguments |
| **DO** | `DO(xTarget, [xArg1, ...]) → xResult` | Invokes function/block dynamically; xTarget = string (function name) or block |
## Advanced / Five Extensions
### Bytecode Compilation (Expression Optimization)
| Function | Signature | Notes |
|----------|-----------|-------|
| **PCCOMPILE** | `PCCOMPILE(cExpr) → pCompiledExpr` | Compiles expression to bytecode pointer (FiveSql2 hot-path) |
| **PCEVAL** | `PCEVAL(pCompiledExpr [, xArg1, ...]) → xResult` | Evaluates compiled expression |
### Go-Native SQL Scan Loop (FiveSql2 Hot-Path)
| Function | Signature | Notes |
|----------|-----------|-------|
| **SQLSCAN** | `SQLSCAN(...) → [internal]` | Initiates SQL scan loop (Go native); not for direct PRG use |
| **SQLEACH** | `SQLEACH(...) → [internal]` | Iterates SQL result rows |
| **SQLHASHBUILD** | `SQLHASHBUILD(...) → hHash` | Builds hash for JOIN optimization |
| **SQLHASHJOIN** | `SQLHASHJOIN(...) → aResult` | Hash-based JOIN operation |
| **SQLORDERBY** | `SQLORDERBY(...) → aResult` | ORDER BY operation |
| **SQLGROUPBY** | `SQLGROUPBY(...) → aResult` | GROUP BY aggregation |
| **SQLDISTINCT** | `SQLDISTINCT(...) → aResult` | DISTINCT filtering |
| **SQLUNIONDISTINCT** | `SQLUNIONDISTINCT(...) → aResult` | UNION DISTINCT |
| **SQLBUILDSUBCACHEKEY** | `SQLBUILDSUBCACHEKEY(...) → cKey` | Subquery cache key generation |
| **SQLEXPRHASAGG** | `SQLEXPRHASAGG(...) → lHasAgg` | Detects aggregate expressions |
| **SQLBULKINSERT** | `SQLBULKINSERT(...) → nInserted` | Bulk INSERT operation |
| **SQLBULKUPDATE** | `SQLBULKUPDATE(...) → nUpdated` | Bulk UPDATE operation |
| **SQLBULKDELETE** | `SQLBULKDELETE(...) → nDeleted` | Bulk DELETE operation |
| **SQLWINDOWSLIDEAGG** | `SQLWINDOWSLIDEAGG(...) → aResult` | Window function aggregation |
| **SQLWINDOWPARTITIONS** | `SQLWINDOWPARTITIONS(...) → aPartitions` | Partitions for window functions |
| **SQLGROUPROWS** | `SQLGROUPROWS(...) → aGrouped` | Groups rows by key |
| **SQLCOMPUTEAGGSIMPLE** | `SQLCOMPUTEAGGSIMPLE(...) → xAgg` | Computes single aggregate |
| **SQLEVALHAVING** | `SQLEVALHAVING(...) → lPass` | Evaluates HAVING clause |
| **SQLCOERCESTR** | `SQLCOERCESTR(...) → cString` | Type coercion to string |
| **SQLCOERCENUM** | `SQLCOERCENUM(...) → nNumber` | Type coercion to numeric |
| **SQLCOERCEFORCMP** | `SQLCOERCEFORCMP(...) → xValue` | Coerces for comparison |
| **SQLISTRUE** | `SQLISTRUE(...) → lTruthy` | Evaluates truthiness |
| **SQLISAGGNAME** | `SQLISAGGNAME(...) → lAgg` | Checks if aggregate function name |
| **SQLFETCHROWFAST** | `SQLFETCHROWFAST(...) → aRow` | Fast row fetch (Go native) |
| **SQLCMPEQ** | `SQLCMPEQ(...) → lEqual` | Fast equality compare |
| **SQLCMPLT** | `SQLCMPLT(...) → lLess` | Fast less-than compare |
| **SQLEXTRACTTEMPLATE** | `SQLEXTRACTTEMPLATE(...) → cTemplate` | Extracts template from SQL |
| **SQLLEXERTOKENIZE** | `SQLLEXERTOKENIZE(...) → aTokens` | SQL tokenization |
| **SQLLEXANDEXTRACTTEMPLATE** | `SQLLEXANDEXTRACTTEMPLATE(...) → cTemplate` | Combined lex + template |
| **SQLWACACHEENABLE** | `SQLWACACHEENABLE() → lSuccess` | Enables WorkArea cache (FiveSql2) |
| **SQLWACACHEDISABLE** | `SQLWACACHEDISABLE() → lSuccess` | Disables WorkArea cache |
| **SQLWACACHEISENABLED** | `SQLWACACHEISENABLED() → lEnabled` | Checks cache status |
| **SQLWACACHEGET** | `SQLWACACHEGET(cKey) → xValue` | Retrieves from cache |
| **SQLWACACHEPUT** | `SQLWACACHEPUT(cKey, xValue) → lSuccess` | Stores in cache |
| **SQLWACACHEINVALIDATE** | `SQLWACACHEINVALIDATE(cKey) → lSuccess` | Invalidates cache entry |
| **SQLWACACHECLOSEALL** | `SQLWACACHECLOSEALL() → lSuccess` | Clears all cache |
| **SQLWINDOWSORTPARTITION** | `SQLWINDOWSORTPARTITION(...) → aResult` | Sorts window partition |
| **SQLWINDOWASSIGNRANK** | `SQLWINDOWASSIGNRANK(...) → aResult` | Assigns window function ranks |
### FRB (Five Runtime Binary) Bytecode
| Function | Signature | Notes |
|----------|-----------|-------|
| **FRBLOAD** | `FRBLOAD(cFileName) → pFRB` | Loads compiled FRB bytecode |
| **FRBDO** | `FRBDO(pFRB, xArg1, ...) → xResult` | Executes FRB with arguments |
| **FRBUNLOAD** | `FRBUNLOAD(pFRB) → lSuccess` | Unloads FRB from memory |
| **FRBRUN** | `FRBRUN(cFRBCode, xArg1, ...) → xResult` | Runs inline FRB |
| **FRBCOMPILE** | `FRBCOMPILE(cCode) → pFRB` | Compiles PRG to FRB |
| **FRBEXEC** | `FRBEXEC(cCode) → xResult` | Compiles and executes FRB inline |
### HTTP / ZIP / XML (Five Native Extensions)
| Function | Signature | Notes |
|----------|-----------|-------|
| **FV_HTTPGET** | `FV_HTTPGET(cURL [, nTimeout]) → cResponse` | HTTP GET; returns response body |
| **FV_HTTPPOST** | `FV_HTTPPOST(cURL, cBody [, nTimeout]) → cResponse` | HTTP POST; returns response body |
| **FV_ZIPENTRIES** | `FV_ZIPENTRIES(cZipFile) → aEntries` | Lists ZIP entries (filenames) |
| **FV_ZIPREAD** | `FV_ZIPREAD(cZipFile, cEntry) → cData` | Reads entry from ZIP |
| **FV_XML_ROWS** | `FV_XML_ROWS(cXML, cXPath) → aRows` | Extracts XML rows by XPath |
### Random Number Generation
| Function | Signature | Notes |
|----------|-----------|-------|
| **HB_RANDOM** | `HB_RANDOM() → nRandom` | Returns random float 0.0 ≤ x < 1.0 |
| **HB_RANDOMINT** | `HB_RANDOMINT([nMin, nMax]) → nRandom` | Random integer; default [0, 2^31-1] |
| **HB_RANDOMSEED** | `HB_RANDOMSEED(nSeed) → cNil` | Seeds random number generator |
| **HB_RANDSTR** | `HB_RANDSTR([nLen]) → cRandom` | Random alphanumeric string |
### Utilities / Helpers
| Function | Signature | Notes |
|----------|-----------|-------|
| **HB_DEFAULT** | `HB_DEFAULT(xValue, xDefault) → xValue` | Returns xValue if not NIL, else xDefault |
| **HB_DEFAULTVALUE** | `HB_DEFAULTVALUE(xValue, xDefault) → xValue` | Alias for HB_DEFAULT |
| **HB_IDLESLEEP** | `HB_IDLESLEEP(nMilliseconds) → lSuccess` | Sleeps without busy-wait |
| **SLEEP** | `SLEEP(nMilliseconds) → lSuccess` | Sleeps for milliseconds |
| **TONE** | `TONE(nFrequency, nDuration) → lSuccess` | Produces beep tone |
| **CENTER** | `CENTER(cString, nWidth) → cCentered` | Centers string in width |
| **SOUNDEX** | `SOUNDEX(cString) → cSoundex` | Soundex phonetic encoding |
| **MEMOLINE** | `MEMOLINE(cMemo, nLineLen, nLineNum) → cLine` | Extracts line from memo |
| **MLCOUNT** | `MLCOUNT(cMemo, nLineLen) → nLineCount` | Counts memo lines |
| **TOKEN** | `TOKEN(cString, nNum [, cDelim]) → cToken` | Extracts nth token from delimited string |
| **NUMTOKEN** | `NUMTOKEN(cString [, cDelim]) → nCount` | Counts tokens in string |
| **HB_TOKENGET** | `HB_TOKENGET(cString, nNum [, cDelim]) → cToken` | Alias for TOKEN |
| **HB_TOKENCOUNT** | `HB_TOKENCOUNT(cString [, cDelim]) → nCount` | Alias for NUMTOKEN |
| **HB_PS** | `HB_PS() → cPS` | Path separator string |
| **HB_EOL** | `HB_EOL() → cEOL` | End-of-line string (OS-specific) |
| **HB_ISNULL** | `HB_ISNULL(xValue) → lNull` | Checks if value is NIL |
| **MEMVARBLOCK** | `MEMVARBLOCK(cVarName) → bBlock` | Returns block to access memvar |
| **__DEFAULTNIL** | `__DEFAULTNIL(xValue) → xValue` | Returns NIL if xValue is default |
| **DBEDIT** | `DBEDIT(...) → lSuccess` | Interactive database editor (UI) |
| **TBROWSEDB** | `TBROWSEDB(...) → oBrowse` | Creates TBrowse from database |
| **TBROWSENEW** | `TBROWSENEW(...) → oBrowse` | Creates new TBrowse object |
| **TBCOLUMNNEW** | `TBCOLUMNNEW(...) → oColumn` | Creates TBrowse column |
### Concurrency / Goroutines (Go Native)
| Function | Signature | Notes |
|----------|-----------|-------|
| **GO** | `GO(bBlock, xArg1, ...) → gGoroutine` | Launches goroutine (Go native); returns handle |
| **CHANNEL** | `CHANNEL() → chChannel` | Creates communication channel |
| **CHSEND** | `CHSEND(chChannel, xValue) → lSuccess` | Sends value to channel |
| **CHRECEIVE** | `CHRECEIVE(chChannel) → xValue` | Receives value from channel (blocking) |
| **CHCLOSE** | `CHCLOSE(chChannel) → lSuccess` | Closes channel |
| **WAITGROUP** | `WAITGROUP() → wgGroup` | Creates wait group for sync |
| **WGADD** | `WGADD(wgGroup, nCount) → lSuccess` | Adds workers to wait group |
| **WGDONE** | `WGDONE(wgGroup) → lSuccess` | Signals work complete |
| **WGWAIT** | `WGWAIT(wgGroup) → lSuccess` | Waits for all workers |
| **MUTEX** | `MUTEX() → muMutex` | Creates mutual exclusion lock |
| **LOCK** | `LOCK(muMutex) → lSuccess` | Acquires lock |
| **UNLOCK** | `UNLOCK(muMutex) → lSuccess` | Releases lock |
### Bitmap/Rushmore Index Optimization (Extended)
| Function | Signature | Notes |
|----------|-----------|-------|
| **BM_DBSETFILTER** | `BM_DBSETFILTER(...) → lSuccess` | Bitmap-optimized filter |
| **BM_DBSEEKWILD** | `BM_DBSEEKWILD(...) → lSuccess` | Wildcard seek optimization |
| **BM_TURBO** | `BM_TURBO(lMode) → lOld` | Enables Rushmore turbo mode |
| **BM_DBGETFILTERARRAY** | `BM_DBGETFILTERARRAY() → aFilter` | Gets current bitmap filter array |
| **BM_DBSETFILTERARRAY** | `BM_DBSETFILTERARRAY(aFilter) → lSuccess` | Sets bitmap filter array |
| **BM_DBSETFILTERARRAYADD** | `BM_DBSETFILTERARRAYADD(...) → lSuccess` | Adds to bitmap filter |
| **BM_DBSETFILTERARRAYDEL** | `BM_DBSETFILTERARRAYDEL(...) → lSuccess` | Removes from bitmap filter |
---
## Summary
The Five RTL catalog includes:
- **568 registered functions** spanning strings, arrays, hashes, JSON, dates/times, regex, math, I/O, database, terminal, and advanced (SQL/bytecode/concurrency) domains
- **UTF-8 rune-aware** core string operations (LEN, SUBSTR, LEFT, RIGHT, AT, CHR, ASC, PADR, PADL) when charset is UTF-8 (default)
- **Charset/codepage support** via HB_GETCHARSET / HB_SETCHARSET / HB_TRANSLATE for legacy ANSI codepages (CP949, CP1252, etc.)
- **Go-native features** (regex via `regexp`, JSON via `encoding/json`, concurrency via goroutines/channels, HTTP/ZIP/XML extensions)
- **FiveSql2 hot-path optimization** via PCCOMPILE/PCEVAL, SQLSCAN, and WorkArea caching
- **Harbour compatibility** for xBase/Clipper code migration, with Five-specific extensions clearly noted

172
rag/04-idioms.md Normal file
View File

@@ -0,0 +1,172 @@
---
doc: five-idioms
title: Five web/worker app idioms (HTTP, Postgres, queue, LLM, build)
keywords: [idioms, cookbook, http, endpoint, routing, AP_JSONRESPONSE, AP_BODY, AP_GETPAIRS, ctx_set, ctx_get, PG_QUERY, PG_EXEC, LABDB_GET_PG, job queue, FOR UPDATE SKIP LOCKED, LLM_CHAT, fnode, build, deploy]
summary: Battle-tested patterns from the production solmade app for building HTTP APIs and background workers in Five — endpoint skeleton, file-name routing, Postgres access, the DB job queue, LLM calls, and build/deploy.
---
# Five idioms / cookbook
All snippets are from the real `solmade` app (`/Users/charleskwon/solmade`).
## 1. HTTP endpoint skeleton
One `FUNCTION Main()` per `.prg` file under `app/api/`.
```five
// POST /api/press-save.prg
FUNCTION Main()
LOCAL nPG := LABDB_GET_PG()
LOCAL hBody, nUser, aRows
IF nPG < 0
ctx_set( "status", 500 )
AP_JSONRESPONSE( { "ok" => .f., "error" => "PG not connected" } )
RETURN NIL
ENDIF
hBody := hb_jsonDecode( AP_BODY() ) // parse JSON request body
IF ! HB_ISHASH( hBody )
ctx_set( "status", 400 )
AP_JSONRESPONSE( { "ok" => .f., "error" => "body must be JSON object" } )
RETURN NIL
ENDIF
nUser := Val( ctx_get( "auth_user_id", "0" ) ) // authed user (string → int)
AP_JSONRESPONSE( { "ok" => .t. } )
RETURN NIL
```
Core verbs:
- `AP_BODY()` → raw request body (decode with `hb_jsonDecode`).
- `AP_GETPAIRS( .t. )` → hash of query-string params (GET).
- `AP_JSONRESPONSE( hHash )` → serialize + send JSON response.
- `ctx_set( "status", N )` → set HTTP status code.
- `ctx_get( "auth_user_id", "0" )` → authed user id (always a STRING; wrap in `Val()`).
Also `auth_email`, `auth_role`, `auth_display_name` set by the auth middleware.
## 2. File-name routing
A URL path maps deterministically to a function (`app/bridge_server.prg`):
strip `/api/`, drop `.prg`, replace `-``_` and `/``_`, uppercase, append `__MAIN`.
```
/api/press-submit.prg → PRESS_SUBMIT__MAIN (Main() in app/api/press_submit.prg)
/api/auth/login.prg → AUTH_LOGIN__MAIN
```
So: name the file with underscores, put a `FUNCTION Main()` in it, call it with hyphens.
## 3. PostgreSQL access
```five
LOCAL nPG := LABDB_GET_PG() // pooled connection handle (opened at startup)
// SELECT → array of hashes. *** Column values come back as STRINGS. ***
LOCAL aRows := PG_QUERY( nPG, ;
"SELECT id, title FROM articles WHERE status = $1 ORDER BY id DESC LIMIT 200", ;
{ "draft" } )
IF aRows == NIL ; aRows := {} ; ENDIF
LOCAL nId := Val( hb_CStr( aRows[1]["id"] ) ) // convert numeric columns explicitly
// non-SELECT
PG_EXEC( nPG, "UPDATE articles SET title=$1 WHERE id=$2", { "New", hb_NToS( nId ) } )
// INSERT ... RETURNING
aRows := PG_QUERY( nPG, ;
"INSERT INTO articles (title) VALUES ($1) RETURNING id", { "Draft" } )
LOCAL nNew := aRows[1]["id"]
// error string
LOCAL cErr := PG_LAST_ERROR( nPG )
```
Idempotent schema, safe to call inside an endpoint:
```five
FUNCTION ARTICLES_ENSURE( nPG )
PG_EXEC( nPG, ;
"CREATE TABLE IF NOT EXISTS articles ( " + ;
" id SERIAL PRIMARY KEY, " + ;
" title TEXT NOT NULL DEFAULT '(제목 없음)', " + ;
" created_at TIMESTAMPTZ NOT NULL DEFAULT now() " + ;
")" )
RETURN NIL
```
## 4. DB job queue (avoid proxy timeout on long work)
Long LLM calls would blow the HTTP/proxy timeout. Pattern: submit → poll.
**Submit (web)** — insert `queued`, return id immediately:
```five
aRows := PG_QUERY( nPG, ;
"INSERT INTO text_tasks (kind, input, status, progress_msg, requested_by) " + ;
"VALUES ('press', $1, 'queued', '대기 중', $2) RETURNING id", ;
{ cText, hb_NToS( nUser ) } )
AP_JSONRESPONSE( { "ok" => .t., "task_id" => aRows[1]["id"] } )
```
**Worker (separate binary)** — claim one row atomically; no two workers collide:
```five
aClaim := PG_QUERY( nPG, ;
"WITH n AS ( SELECT id FROM text_tasks WHERE status='queued' " + ;
" ORDER BY id FOR UPDATE SKIP LOCKED LIMIT 1 ) " + ;
"UPDATE text_tasks SET status='running', started_at=now() " + ;
"WHERE id=(SELECT id FROM n) RETURNING id, kind, input" )
IF aClaim == NIL .OR. Len( aClaim ) == 0
RETURN .F. // nothing queued; caller sleeps
ENDIF
// ... do work, periodically UPDATE progress_pct/progress_msg ...
PG_EXEC( nPG, "UPDATE text_tasks SET status='done', progress_pct=100, " + ;
"result=$1, finished_at=now() WHERE id=$2", { cOut, hb_NToS( nId ) } )
```
**Status (web)** — client polls `SELECT status, progress_pct, progress_msg, result`.
Run multiple worker processes for concurrency; `SKIP LOCKED` keeps them race-free.
## 5. LLM calls
`LLM_CHAT(cSystem, cUser, hOpts)``{ "ok"=>.T./.F., "text"=>cResult, "error"=>cMsg }`.
```five
LOCAL hRes := LLM_CHAT( PRESSER_SYSTEM_PROMPT(), cUser, ;
{ "temperature" => 0.5, "max_tokens" => 4000 } )
IF hb_HGetDef( hRes, "ok", .f. )
cOut := hb_HGetDef( hRes, "text", "" )
ELSE
// hRes["error"] — endpoint unreachable / HTTP != 200 / empty response
ENDIF
```
Endpoint resolves from `SOLMADE_LLM_URL` env (OpenAI-compatible `/v1`); model name is
auto-resolved (a literal `"local"` is replaced by the actually-loaded model id, because
mlx/llama servers reject unknown model names). `<think>...</think>` is stripped.
## 6. Build & deploy (fnode)
Web and worker are **separate binaries** built from explicit file lists.
```bash
# build.sh — web
"$FNODE" build \
app/bridge_server.prg app/auth/*.prg app/lib/*.prg app/api/*.prg "$BRIDGE"/*.prg \
--rtl fivenode_go/hbrtl_ext/pgrtl \
--rtl fivenode_go/hbrtl_ext/httpserver \
--go-replace gitea.fivego.org/kwon_ai/solmade="$SOLMADE_ROOT" \
--module gitea.fivego.org/kwon_ai/solmade/_solmade_web \
-o solmade-web
```
- `--rtl <pkg>` blank-imports a Go package whose `init()` registers RTL functions
(e.g. `pgrtl` provides `PG_QUERY`/`PG_EXEC`; `httpserver` the web bridge).
- `--go-replace pkg=path` resolves a private module without a proxy.
- `--module <name>` sets the temp module path (must sit under the app's module so RTL
packages can import the app's internal packages).
Deploy (launchd): `launchctl kickstart -k gui/$(id -u)/kr.solmade.web` (and
`...worker1/2/3`). The worker build (`build_worker.sh`) links `cmd_prg/job_worker.prg`
plus the shared `app/lib/*.prg` (so `LLM_CHAT` and prompts are available to it too).

91
rag/05-gotchas.md Normal file
View File

@@ -0,0 +1,91 @@
---
doc: five-gotchas
title: Five gotchas & non-obvious traps
keywords: [gotcha, trap, pitfall, intrinsic, gengo, charset, utf8, string escape, Chr, pgrtl string columns, Val, model local, analyzer warning, fnode, runtime]
summary: The non-obvious semantic traps that pure grammar knowledge will NOT prevent. Each entry is a real mistake observed in practice plus the fix. This is the long-tail corpus that makes Five RAG actually work.
---
# Five gotchas (read before writing/debugging Five)
These are discovered-the-hard-way facts. Grammar docs won't save you here.
## 1. String functions are inlined intrinsics — editing `hbrtl` alone does nothing
The compiler (`compiler/gengo/gengo.go`) **inlines** `LEN, CHR, ASC, SUBSTR, LEFT, RIGHT,
AT, PADR, PADL` directly as Go. They do **not** dispatch through the `hbrtl` registry at
runtime. So changing the registered `hbrtl` function has **no effect** on these calls.
- To change their runtime behavior you must edit the gengo intrinsic cases. They now emit
calls to charset-aware helpers `hbrtl.StrLen/StrChr/StrAsc/StrSubStr/StrLeft/StrRight/
StrAt/StrPadR/StrPadL` (in `hbrtl/charset.go`).
- Functions used as code blocks / passed around DO hit the registry, so keep the registry
impl and the intrinsic in agreement.
## 2. Strings are UTF-8 (runes) by default; legacy charset is opt-in
`LEN("한글")` is `2` (runes), not bytes. `CHR(9650)` is `▲`. This is the default.
- Select a legacy charset with `HB_SETCHARSET("CP949")` / `HB_CDPSELECT("CP949")` — then
byte/charset semantics apply. `HB_GETCHARSET()` reads the active one.
- Initial charset comes from env `FIVE_CHARSET` (or `HB_CODEPAGE`); default `UTF8`.
- Convert across charsets with `HB_TRANSLATE(cStr, cFrom, cTo)`.
## 3. String literals do NOT process escapes (single OR double quotes)
`"a\nb"` is the literal characters `a \ n b`**not** a newline. Same for `'...'`.
- For a newline use `Chr(10)` (and `Chr(13)` for CR); build control chars explicitly.
- To embed a quote: wrap in the *other* quote. A string containing `"` → use `'...'`;
a string containing `'` → use `"..."`. (No backslash-escaping exists.)
- Watch out building SQL/format strings: e.g. a literal `T` separator inside a
double-quoted SQL fragment can clash — concatenate instead: `... || 'T' || ...`.
## 4. Postgres columns come back as STRINGS
`PG_QUERY` (pgrtl) returns rows as hashes whose values are **all strings**, even for
`INTEGER`/`NUMERIC` columns. `Int("100")` semantics will bite you.
- Convert: `Val( hb_CStr( row["id"] ) )` for numbers.
- Bind params as strings too: `{ hb_NToS( nId ) }`.
## 5. `ctx_get("auth_user_id")` is a string
Auth context values are strings. `nUser := Val( ctx_get( "auth_user_id", "0" ) )`.
## 6. LLM `model = "local"` is rejected by mlx/llama servers
OpenAI-compatible local servers (mlx_lm, llama.cpp) 404 on unknown model names. The app's
`ResolveLlmModel` queries `/v1/models` and substitutes the actually-loaded id. If you call
an LLM endpoint directly, never send `model:"local"` — resolve the real id first.
## 7. Two runtimes — build with the right one
`solmade` builds with the **`fnode`** toolchain in `fivenode_go`, NOT the `five` CLI in
`fivedev/five`. They are separate runtimes with separate RTL behavior. Historic example:
`fivenode_go`'s `Chr()` double-encoded multibyte values (corruption), which is what
prompted implementing proper UTF-8 in `fivedev/five`. Don't assume behavior carries over.
## 8. Run the `five` CLI from inside `fivedev/five`
Module resolution depends on CWD. Building/running `five` from elsewhere can pick up the
wrong `replace` directive (e.g. resolving `five =>` to an unrelated repo). Always
`cd /Users/charleskwon/fivenode/fivedev/five` first, e.g.
`go build -o /tmp/five ./cmd/five && /tmp/five run x.prg`.
## 9. Analyzer "undeclared variable" warnings for RTL functions are harmless
The static analyzer warns `undeclared variable 'HB_FOO'` for RTL functions it doesn't know
about; they still resolve at runtime via the registry. To silence, add the name to the
known-function set in `compiler/analyzer/analyzer.go` (e.g. `HB_GETCHARSET` etc. were added
there). A warning is not an error.
## 10. Density is a double-edged sword when debugging
One line doing a lot means one line failing does a lot. When a dense statement misbehaves,
expand it (split the chained `hb_*`/`PG_*`/`LLM_CHAT` calls into temporaries) to localize
the fault before reasoning about it.
---
> Maintenance discipline: when you hit a NEW non-obvious trap, add it here. Pure-grammar
> RAG closes ~80% of the gap; this accumulating gotcha list closes the rest.

20
rag/INDEX.md Normal file
View File

@@ -0,0 +1,20 @@
# Five RAG — retrieval manifest
Route a query to the right doc(s). Each row: file · when to retrieve · keywords.
| File | Retrieve when the task involves… | Keywords |
|------|----------------------------------|----------|
| `01-overview.md` | orienting on what Five is, runtimes, compile model, "where do I look" | five, fivenode, overview, philosophy, token-density, harbour, xbase, compile, go, runtime, gengo intrinsic |
| `02-syntax.md` | writing any Five code — declarations, control flow, literals, operators, blocks | syntax, grammar, FUNCTION, PROCEDURE, LOCAL, STATIC, IF, FOR, FOR EACH, DO WHILE, DO CASE, BEGIN SEQUENCE, IIF, code block, array, hash, string literal, operators, := == $ |
| `03-rtl-catalog.md` | "what function does X" — string/array/hash/json/date/regex/charset/math/crypto builtins | rtl, builtin, Len, SubStr, Left, Right, At, Upper, AllTrim, PadL, PadR, StrTran, Chr, Asc, Val, Str, hb_NToS, hb_CStr, AAdd, AScan, AEval, hb_HGetDef, hb_HHasKey, hb_jsonDecode, hb_jsonEncode, ValType, HB_ISHASH, regex, HB_GETCHARSET, date, hb_ATokens |
| `04-idioms.md` | building an endpoint, DB access, async/queue work, calling the LLM, building/deploying | idioms, http, endpoint, routing, AP_BODY, AP_GETPAIRS, AP_JSONRESPONSE, ctx_set, ctx_get, LABDB_GET_PG, PG_QUERY, PG_EXEC, PG_LAST_ERROR, RETURNING, CREATE TABLE IF NOT EXISTS, text_tasks, FOR UPDATE SKIP LOCKED, job queue, LLM_CHAT, fnode, build.sh, launchctl |
| `05-gotchas.md` | debugging "why doesn't this work", or BEFORE editing string funcs / charset / SQL / LLM | gotcha, trap, intrinsic, gengo, charset, utf8, string escape, Chr, pgrtl string columns, Val, hb_CStr, model local, ResolveLlmModel, two runtimes, fnode, analyzer warning, CWD module resolution |
## Quick routing heuristics
- Writing new code → `02` + `04`, and skim `05` first.
- "Which builtin?" → `03`.
- Bug that defies the grammar → `05` (almost always the answer is here).
- "Why is my hbrtl edit ignored?" → `05 §1` (inlined intrinsics).
- Korean/multibyte length/char issues → `05 §2,§3` + `03` charset section.
- Numbers read from DB are wrong → `05 §4`.

44
rag/README.md Normal file
View File

@@ -0,0 +1,44 @@
# Five RAG — knowledge corpus for LLM agents writing Five
A compact, retrieval-ready knowledge base that lets an LLM read and write **Five**
(xBase/Harbour → Go) code correctly without prior training on it. This is the practical
form of "give the model the grammar via RAG": grammar + RTL surface + real idioms +
the long-tail gotchas.
## Why this exists
Five is token-dense, so the corpus needed to *teach* a model is small and cheap to inject
— a dense language is cheaper to RAG than a verbose one. Grammar/RTL retrieval closes most
of the gap; the accumulating **gotchas** file closes the semantic long tail.
## Contents
| File | What it covers |
|------|----------------|
| `01-overview.md` | What Five is, design priorities, the two runtimes, compile model |
| `02-syntax.md` | Declarations, literals, operators, control flow, code blocks |
| `03-rtl-catalog.md` | Runtime-library functions (strings, array, hash, JSON, date, regex, charset, …) |
| `04-idioms.md` | Web/worker patterns: HTTP endpoint, routing, Postgres, job queue, LLM, build/deploy |
| `05-gotchas.md` | Non-obvious traps + fixes (the highest-signal file) |
| `INDEX.md` | Retrieval manifest (doc → keywords + one-line) |
Every file has YAML frontmatter (`doc`, `title`, `keywords`, `summary`) for ranking.
## How to consume
- **Direct context injection (simplest):** for a small/medium task, paste the relevant
doc(s). For broad work, `01`+`02`+`05` fit easily; pull `03`/`04` sections as needed.
- **Keyword retrieval (e.g. bluge / ripgrep):** index the `.md` files; rank on the
frontmatter `keywords` + body. `INDEX.md` is a hand-curated routing table.
- **Embedding RAG:** chunk by `##` headers (each section is self-contained). Frontmatter
`summary` makes a good chunk preamble.
Suggested system-prompt pointer: *"When writing Five (.prg) code, consult the Five RAG at
`fivedev/five/rag/` — especially `05-gotchas.md` — and prefer patterns from `04-idioms.md`."*
## Maintenance
- Keep `03-rtl-catalog.md` honest against `hbrtl/register.go` (names are authoritative;
rare signatures may drift).
- **Append every new trap to `05-gotchas.md`.** That file is the compounding asset.
- Grammar truth: `compiler/{lexer,parser,ast}`. Idiom truth: the `solmade` app.