Five v0.9 — Harbour + Go fusion language

- Compiler: PP → Lexer → Parser → Analyzer → Gengo pipeline
- Parser: 232/236 (98%) Harbour compatibility, registry-based dispatch
- RTL: 351 Harbour-compatible functions
- RDD: DBF/NTX/CDX engines with Rushmore bitmap optimization
- Go Interop: IMPORT + pkg.Func() + obj:Method() with FastPath (15M calls/sec)
- HB_FUNC API: Full Harbour C API compatible Go bridge
- Concurrency: SPAWN/LAUNCH/GOROUTINE, <-, WATCH, PARALLEL FOR, ASYNC/AWAIT
- Extensions: Multi-return, DEFER, Slice, f-string, Nil-safe ?:, CONST
- Macro Compiler: Runtime AST parsing and evaluation
- Debugger: TUI debugger with source display, breakpoints, stepping
- FRB: Native + Pcode dual mode runtime binary
- Tests: 13 packages ALL PASS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-31 09:41:50 +09:00
commit 59568f3301
282 changed files with 66658 additions and 0 deletions

263
tests/hello_test.go Normal file
View File

@@ -0,0 +1,263 @@
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
// All rights reserved.
// Integration tests: simulates what generated Go code would look like.
// Each test is the equivalent of a PRG file compiled by Five.
package tests
import (
"five/hbrt"
"five/hbrtl"
"testing"
)
func newTestVM() *hbrt.VM {
vm := hbrt.NewVM()
hbrtl.RegisterRTL(vm)
return vm
}
// TestHelloWorld simulates:
//
// FUNCTION Main()
// ? "Hello, World!"
// ? 1 + 2
// RETURN NIL
func TestHelloWorld(t *testing.T) {
vm := newTestVM()
mod := hbrt.NewModule("HELLO",
hbrt.Sym("MAIN", hbrt.FsPublic|hbrt.FsLocal|hbrt.FsFirst, func(th *hbrt.Thread) {
th.Frame(0, 0)
defer th.EndProc()
th.PushSymbol(vm.FindSymbol("QOUT"))
th.PushNil()
th.PushString("Hello, World!")
th.Function(1)
th.PushSymbol(vm.FindSymbol("QOUT"))
th.PushNil()
th.PushInt(1)
th.PushInt(2)
th.Plus()
th.Function(1)
th.RetNil()
}),
)
vm.RegisterModule(mod)
result := vm.Run("MAIN")
if !result.IsNil() {
t.Errorf("Main should return NIL, got %v", result)
}
}
// TestSumLoop simulates:
//
// FUNCTION Main()
// LOCAL nSum := 0, i := 1
// DO WHILE i <= 10
// nSum += i
// i++
// ENDDO
// ? "Sum 1..10 =", nSum
// RETURN nSum
func TestSumLoop(t *testing.T) {
vm := newTestVM()
mod := hbrt.NewModule("SUMTEST",
hbrt.Sym("MAIN", hbrt.FsPublic|hbrt.FsLocal|hbrt.FsFirst, func(th *hbrt.Thread) {
th.Frame(0, 2)
defer th.EndProc()
th.LocalSetInt(1, 0)
th.LocalSetInt(2, 1)
lab_for:
th.PushLocal(2)
th.PushInt(10)
th.LessEqual()
if !th.PopLogical() {
goto lab_endfor
}
th.PushLocal(2)
th.LocalAdd(1)
th.LocalAddInt(2, 1)
goto lab_for
lab_endfor:
th.PushSymbol(vm.FindSymbol("QOUT"))
th.PushNil()
th.PushString("Sum 1..10 =")
th.PushLocal(1)
th.Function(2)
th.PushLocal(1)
th.RetValue()
}),
)
vm.RegisterModule(mod)
result := vm.Run("MAIN")
if result.AsNumInt() != 55 {
t.Errorf("Sum 1..10 = %d, want 55", result.AsNumInt())
}
}
// TestStringConcat simulates:
//
// FUNCTION Main()
// LOCAL cName := "World"
// LOCAL cGreeting := "Hello, " + cName + "!"
// ? cGreeting
// RETURN cGreeting
func TestStringConcat(t *testing.T) {
vm := newTestVM()
mod := hbrt.NewModule("STRTEST",
hbrt.Sym("MAIN", hbrt.FsPublic|hbrt.FsLocal|hbrt.FsFirst, func(th *hbrt.Thread) {
th.Frame(0, 2)
defer th.EndProc()
th.PushString("World")
th.PopLocal(1)
th.PushString("Hello, ")
th.PushLocal(1)
th.Plus()
th.PushString("!")
th.Plus()
th.PopLocal(2)
th.PushSymbol(vm.FindSymbol("QOUT"))
th.PushNil()
th.PushLocal(2)
th.Function(1)
th.PushLocal(2)
th.RetValue()
}),
)
vm.RegisterModule(mod)
result := vm.Run("MAIN")
if result.AsString() != "Hello, World!" {
t.Errorf("greeting = %q, want %q", result.AsString(), "Hello, World!")
}
}
// TestFunctionCallWithSTR simulates:
//
// FUNCTION Main()
// LOCAL n := 42
// ? "Value: " + Str(n)
// RETURN n
func TestFunctionCallWithSTR(t *testing.T) {
vm := newTestVM()
mod := hbrt.NewModule("STRFUNC",
hbrt.Sym("MAIN", hbrt.FsPublic|hbrt.FsLocal|hbrt.FsFirst, func(th *hbrt.Thread) {
th.Frame(0, 1)
defer th.EndProc()
th.LocalSetInt(1, 42)
// Str(n)
th.PushSymbol(vm.FindSymbol("STR"))
th.PushNil()
th.PushLocal(1)
th.Function(1)
// "Value: " + Str(n)
strResult := th.GetRetValue()
th.Pop() // pop Function result from stack
th.PushString("Value: ")
th.PushValue(strResult)
th.Plus()
// ? result
concatResult := th.Pop2()
th.PushSymbol(vm.FindSymbol("QOUT"))
th.PushNil()
th.PushValue(concatResult)
th.Function(1)
th.PushLocal(1)
th.RetValue()
}),
)
vm.RegisterModule(mod)
result := vm.Run("MAIN")
if result.AsNumInt() != 42 {
t.Errorf("result = %d, want 42", result.AsNumInt())
}
}
// TestNestedFunctionCalls simulates:
//
// FUNCTION Double(n) → n * 2
// FUNCTION Main() → ? Double(Double(5))
func TestNestedFunctionCalls(t *testing.T) {
vm := newTestVM()
mod := hbrt.NewModule("NESTED",
hbrt.Sym("DOUBLE", hbrt.FsPublic|hbrt.FsLocal, func(th *hbrt.Thread) {
th.Frame(1, 0)
defer th.EndProc()
th.RetInt(th.Local(1).AsNumInt() * 2)
}),
hbrt.Sym("MAIN", hbrt.FsPublic|hbrt.FsLocal|hbrt.FsFirst, func(th *hbrt.Thread) {
th.Frame(0, 0)
defer th.EndProc()
// Double(5) → 10
th.PushSymbol(vm.FindSymbol("DOUBLE"))
th.PushNil()
th.PushInt(5)
th.Function(1)
// Double(10) → 20
inner := th.Pop2()
th.PushSymbol(vm.FindSymbol("DOUBLE"))
th.PushNil()
th.PushValue(inner)
th.Function(1)
// ? and return
outer := th.Pop2()
th.PushSymbol(vm.FindSymbol("QOUT"))
th.PushNil()
th.PushValue(outer)
th.Function(1)
th.PushValue(outer)
th.RetValue()
}),
)
vm.RegisterModule(mod)
result := vm.Run("MAIN")
if result.AsNumInt() != 20 {
t.Errorf("Double(Double(5)) = %d, want 20", result.AsNumInt())
}
}
// TestIfElse simulates:
//
// FUNCTION Main()
// LOCAL n := 55
// IF n > 50
// ? "Greater"
// RETURN .T.
// ELSE
// ? "Not greater"
// RETURN .F.
// ENDIF
func TestIfElse(t *testing.T) {
vm := newTestVM()
mod := hbrt.NewModule("IFTEST",
hbrt.Sym("MAIN", hbrt.FsPublic|hbrt.FsLocal|hbrt.FsFirst, func(th *hbrt.Thread) {
th.Frame(0, 1)
defer th.EndProc()
th.LocalSetInt(1, 55)
th.PushLocal(1)
th.PushInt(50)
th.Greater()
if !th.PopLogical() {
goto lab_else
}
th.PushSymbol(vm.FindSymbol("QOUT"))
th.PushNil()
th.PushString("Greater")
th.Function(1)
th.PushBool(true)
th.RetValue()
return
lab_else:
th.PushSymbol(vm.FindSymbol("QOUT"))
th.PushNil()
th.PushString("Not greater")
th.Function(1)
th.PushBool(false)
th.RetValue()
}),
)
vm.RegisterModule(mod)
result := vm.Run("MAIN")
if !result.AsBool() {
t.Error("55 > 50 should return .T.")
}
}