From d6c26104c9145c393c59e1e307f3b56f745b02e1 Mon Sep 17 00:00:00 2001 From: CharlesKWON Date: Sat, 18 Apr 2026 17:01:50 +0900 Subject: [PATCH] =?UTF-8?q?feat(rtl):=20common.ch=20aliases=20=E2=80=94=20?= =?UTF-8?q?ISNIL/ISARRAY/ISNUMBER=20and=20friends?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Harbour's common.ch exposes classic Clipper type-check shorthands via #translate rules that map to HB_IS* RTL functions: #translate ISNIL() => (() == NIL) #translate ISARRAY() => HB_ISARRAY() #translate ISCHARACTER() => HB_ISSTRING() ... etc. Five's preprocessor currently supports #translate only for lines whose FIRST word is the rule keyword, not for substring matches inside expressions. Real usage like `IF ISNIL(x)` fails the keyword check (first word is IF, not ISNIL) and the rule never fires. Rather than rewrite the PP substring engine (A2 scope), register the nine short names as direct RTL symbols in register.go, each pointing at the same Go function as its HB_IS* twin. ISMEMO maps to HB_ISSTRING as a reasonable approximation for Five (no distinct memo type at the VM level). common.ch becomes a short stub that just #defines TRUE/FALSE/YES/NO and documents where the ISxxx aliases live. DEFAULT / UPDATE #xcommand forms remain unsupported pending A2. Verified with /tmp/test_common.prg — ISNUMBER(42), ISCHARACTER("x"), ISNIL(nilVar) all dispatch correctly. Analyzer still emits "undeclared variable" warnings for the short names (the static checker doesn't see runtime-registered RTL symbols) but the generated code links and runs. FiveSql2 43/43, Harbour compat 56/56, Go test ALL PASS. Co-Authored-By: Claude Opus 4.7 (1M context) --- hbrtl/register.go | 14 ++++++++++++++ include/common.ch | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 include/common.ch diff --git a/hbrtl/register.go b/hbrtl/register.go index daace7f..3ae1dfa 100644 --- a/hbrtl/register.go +++ b/hbrtl/register.go @@ -322,16 +322,30 @@ func RegisterRTL(vm *hbrt.VM) { // Type checking (HB_IS*) hbrt.Sym("HB_ISARRAY", hbrt.FsPublic, HbIsArray), + // Classic Clipper/Harbour aliases — `common.ch` #translate rules + // map these to HB_IS*. Registering the short names as direct + // symbols bypasses the PP altogether and makes them work even + // when code doesn't include common.ch. + hbrt.Sym("ISARRAY", hbrt.FsPublic, HbIsArray), hbrt.Sym("HB_ISBLOCK", hbrt.FsPublic, HbIsBlock), + hbrt.Sym("ISBLOCK", hbrt.FsPublic, HbIsBlock), hbrt.Sym("HB_ISCHAR", hbrt.FsPublic, HbIsChar), hbrt.Sym("HB_ISSTRING", hbrt.FsPublic, HbIsString), + hbrt.Sym("ISCHARACTER", hbrt.FsPublic, HbIsString), + hbrt.Sym("ISMEMO", hbrt.FsPublic, HbIsString), // memo ≈ string for Five hbrt.Sym("HB_ISDATE", hbrt.FsPublic, HbIsDate), + hbrt.Sym("ISDATE", hbrt.FsPublic, HbIsDate), hbrt.Sym("HB_ISDATETIME", hbrt.FsPublic, HbIsDateTime), hbrt.Sym("HB_ISLOGICAL", hbrt.FsPublic, HbIsLogical), + hbrt.Sym("ISLOGICAL", hbrt.FsPublic, HbIsLogical), hbrt.Sym("HB_ISNUMERIC", hbrt.FsPublic, HbIsNumeric), + hbrt.Sym("ISNUMBER", hbrt.FsPublic, HbIsNumeric), + hbrt.Sym("ISNUMERIC", hbrt.FsPublic, HbIsNumeric), hbrt.Sym("HB_ISOBJECT", hbrt.FsPublic, HbIsObject), + hbrt.Sym("ISOBJECT", hbrt.FsPublic, HbIsObject), hbrt.Sym("HB_ISHASH", hbrt.FsPublic, HbIsHash), hbrt.Sym("HB_ISNIL", hbrt.FsPublic, HbIsNil), + hbrt.Sym("ISNIL", hbrt.FsPublic, HbIsNil), hbrt.Sym("HB_ISPOINTER", hbrt.FsPublic, HbIsPointer), // OS/Environment diff --git a/include/common.ch b/include/common.ch new file mode 100644 index 0000000..22a2d61 --- /dev/null +++ b/include/common.ch @@ -0,0 +1,26 @@ +/* + * common.ch — Five compatibility shim for Harbour's common.ch + * + * Harbour ships #translate-based aliases for ISNIL / ISARRAY / + * ISNUMBER / ISCHARACTER / ISLOGICAL / ISDATE / ISBLOCK / ISMEMO / + * ISOBJECT. Five registers those as direct RTL symbols in + * hbrtl/register.go (each points at the same Go function as its + * HB_IS* twin), so they work without any preprocessor translation + * and without this include. + * + * This header stays as a stub so `#include "common.ch"` in ported + * Harbour code doesn't error. The DEFAULT / UPDATE #xcommand forms + * from Harbour's common.ch are not yet supported — use explicit + * `IF xVar == NIL ; xVar := default ; ENDIF` until the preprocessor's + * #xcommand marker handling is extended. + */ + +#ifndef HB_COMMON_CH_ +#define HB_COMMON_CH_ + +#define TRUE .T. +#define FALSE .F. +#define YES .T. +#define NO .F. + +#endif