- 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>
157 lines
3.9 KiB
Go
157 lines
3.9 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
|
// All rights reserved.
|
|
|
|
package gengo
|
|
|
|
import (
|
|
"five/compiler/parser"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func generate(t *testing.T, source string) string {
|
|
t.Helper()
|
|
file, errs := parser.Parse("test.prg", source)
|
|
if len(errs) > 0 {
|
|
for _, e := range errs {
|
|
t.Errorf("parse error: %s", e)
|
|
}
|
|
t.FailNow()
|
|
}
|
|
return Generate(file)
|
|
}
|
|
|
|
func assertContains(t *testing.T, code, want string) {
|
|
t.Helper()
|
|
if !strings.Contains(code, want) {
|
|
t.Errorf("generated code missing %q\n--- code ---\n%s", want, code)
|
|
}
|
|
}
|
|
|
|
func TestGenerateHelloWorld(t *testing.T) {
|
|
code := generate(t, `FUNCTION Main()
|
|
? "Hello, World!"
|
|
RETURN NIL
|
|
`)
|
|
assertContains(t, code, "package main")
|
|
assertContains(t, code, `import (`)
|
|
assertContains(t, code, `"five/hbrt"`)
|
|
assertContains(t, code, "func HB_MAIN(t *hbrt.Thread)")
|
|
assertContains(t, code, "t.Frame(0, 0)")
|
|
assertContains(t, code, "defer t.EndProc()")
|
|
assertContains(t, code, `t.PushString("Hello, World!")`)
|
|
assertContains(t, code, "t.Function(1)")
|
|
assertContains(t, code, "t.PushNil()")
|
|
assertContains(t, code, "t.RetValue()")
|
|
assertContains(t, code, "func main()")
|
|
assertContains(t, code, `vm.Run("MAIN")`)
|
|
}
|
|
|
|
func TestGenerateArithmetic(t *testing.T) {
|
|
code := generate(t, `FUNCTION Main()
|
|
LOCAL n := 10
|
|
RETURN n + 5
|
|
`)
|
|
assertContains(t, code, "t.Frame(0, 1)")
|
|
assertContains(t, code, "t.PushInt(10)")
|
|
assertContains(t, code, "t.PopLocal(1)")
|
|
assertContains(t, code, "t.PushLocal(1)") // n
|
|
assertContains(t, code, "t.PushInt(5)")
|
|
assertContains(t, code, "t.Plus()")
|
|
assertContains(t, code, "t.RetValue()")
|
|
}
|
|
|
|
func TestGenerateIfElse(t *testing.T) {
|
|
code := generate(t, `FUNCTION Main()
|
|
LOCAL n := 10
|
|
IF n > 5
|
|
? "Big"
|
|
ELSE
|
|
? "Small"
|
|
ENDIF
|
|
RETURN NIL
|
|
`)
|
|
assertContains(t, code, "t.Greater()")
|
|
assertContains(t, code, "if t.PopLogical()")
|
|
assertContains(t, code, `t.PushString("Big")`)
|
|
assertContains(t, code, "} else {")
|
|
assertContains(t, code, `t.PushString("Small")`)
|
|
}
|
|
|
|
func TestGenerateDoWhile(t *testing.T) {
|
|
code := generate(t, `FUNCTION Main()
|
|
LOCAL i := 0
|
|
DO WHILE i < 10
|
|
i++
|
|
ENDDO
|
|
RETURN i
|
|
`)
|
|
assertContains(t, code, "for {")
|
|
assertContains(t, code, "t.Less()")
|
|
assertContains(t, code, "if !t.PopLogical() { break }")
|
|
assertContains(t, code, "t.LocalAddInt(1, 1)") // i++
|
|
}
|
|
|
|
func TestGenerateForNext(t *testing.T) {
|
|
code := generate(t, `FUNCTION Main()
|
|
LOCAL i, nSum := 0
|
|
FOR i := 1 TO 10
|
|
nSum += i
|
|
NEXT
|
|
RETURN nSum
|
|
`)
|
|
assertContains(t, code, "t.Frame(0, 2)")
|
|
assertContains(t, code, "for {")
|
|
assertContains(t, code, "t.LessEqual()")
|
|
assertContains(t, code, "t.LocalAdd(") // nSum += i
|
|
assertContains(t, code, "t.LocalAddInt(") // i += 1
|
|
}
|
|
|
|
func TestGenerateMultipleFunctions(t *testing.T) {
|
|
code := generate(t, `FUNCTION Double(n)
|
|
RETURN n * 2
|
|
|
|
FUNCTION Main()
|
|
? Double(21)
|
|
RETURN NIL
|
|
`)
|
|
assertContains(t, code, "func HB_DOUBLE(t *hbrt.Thread)")
|
|
assertContains(t, code, "func HB_MAIN(t *hbrt.Thread)")
|
|
assertContains(t, code, "t.Frame(1, 0)") // Double has 1 param
|
|
assertContains(t, code, "t.Mult()")
|
|
assertContains(t, code, `t.PushSymbol(t.VM().FindSymbol("DOUBLE"))`)
|
|
}
|
|
|
|
func TestGenerateStringConcat(t *testing.T) {
|
|
code := generate(t, `FUNCTION Main()
|
|
LOCAL cName := "World"
|
|
? "Hello, " + cName + "!"
|
|
RETURN NIL
|
|
`)
|
|
assertContains(t, code, `t.PushString("Hello, ")`)
|
|
assertContains(t, code, "t.PushLocal(1)")
|
|
assertContains(t, code, "t.Plus()")
|
|
assertContains(t, code, `t.PushString("!")`)
|
|
}
|
|
|
|
func TestGenerateSymbolTable(t *testing.T) {
|
|
code := generate(t, `FUNCTION Main()
|
|
RETURN NIL
|
|
|
|
FUNCTION Helper()
|
|
RETURN NIL
|
|
`)
|
|
assertContains(t, code, `hbrt.Sym("MAIN"`)
|
|
assertContains(t, code, `hbrt.Sym("HELPER"`)
|
|
assertContains(t, code, "hbrt.FsFirst")
|
|
}
|
|
|
|
func TestGenerateImport(t *testing.T) {
|
|
code := generate(t, `IMPORT "net/http"
|
|
|
|
FUNCTION Main()
|
|
RETURN NIL
|
|
`)
|
|
assertContains(t, code, `"net/http"`)
|
|
}
|