- 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>
264 lines
5.8 KiB
Go
264 lines
5.8 KiB
Go
// 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.")
|
|
}
|
|
}
|