- 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>
137 lines
2.4 KiB
Go
137 lines
2.4 KiB
Go
package analyzer
|
|
|
|
import (
|
|
"five/compiler/parser"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func analyze(t *testing.T, source string) []Diagnostic {
|
|
t.Helper()
|
|
file, errs := parser.Parse("test.prg", source)
|
|
if len(errs) > 0 {
|
|
t.Fatalf("parse error: %s", errs[0])
|
|
}
|
|
return Analyze(file)
|
|
}
|
|
|
|
func TestCleanCode(t *testing.T) {
|
|
diags := analyze(t, `
|
|
PROCEDURE Main()
|
|
LOCAL cName, nAge
|
|
cName := "Charles"
|
|
nAge := 30
|
|
? cName, nAge
|
|
RETURN
|
|
`)
|
|
for _, d := range diags {
|
|
if d.Severity == SevError || d.Severity == SevWarning {
|
|
t.Errorf("unexpected diagnostic: %s", d)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUndeclaredVariable(t *testing.T) {
|
|
diags := analyze(t, `
|
|
PROCEDURE Main()
|
|
LOCAL cName
|
|
cName := "Charles"
|
|
? cName, nAge
|
|
RETURN
|
|
`)
|
|
found := false
|
|
for _, d := range diags {
|
|
if strings.Contains(d.Message, "undeclared") && strings.Contains(d.Message, "nAge") {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("expected 'undeclared variable nAge' warning")
|
|
}
|
|
}
|
|
|
|
func TestUnusedVariable(t *testing.T) {
|
|
diags := analyze(t, `
|
|
PROCEDURE Main()
|
|
LOCAL cUsed, cNeverTouched
|
|
cUsed := "hello"
|
|
? cUsed
|
|
RETURN
|
|
`)
|
|
found := false
|
|
for _, d := range diags {
|
|
if strings.Contains(d.Message, "unused") && strings.Contains(d.Message, "cNeverTouched") {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("expected 'unused variable cNeverTouched' hint")
|
|
}
|
|
}
|
|
|
|
func TestParamsDeclared(t *testing.T) {
|
|
diags := analyze(t, `
|
|
FUNCTION Add(a, b)
|
|
LOCAL nResult
|
|
nResult := a + b
|
|
RETURN nResult
|
|
`)
|
|
for _, d := range diags {
|
|
if d.Severity == SevError || d.Severity == SevWarning {
|
|
t.Errorf("unexpected: %s", d)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMultiFunction(t *testing.T) {
|
|
diags := analyze(t, `
|
|
PROCEDURE Main()
|
|
LOCAL n
|
|
n := GetValue()
|
|
? n
|
|
RETURN
|
|
|
|
FUNCTION GetValue()
|
|
LOCAL x
|
|
x := 42
|
|
RETURN x
|
|
`)
|
|
for _, d := range diags {
|
|
if d.Severity == SevWarning {
|
|
t.Errorf("unexpected warning: %s", d)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestForLoopVar(t *testing.T) {
|
|
diags := analyze(t, `
|
|
PROCEDURE Main()
|
|
LOCAL i, aData
|
|
aData := {1, 2, 3}
|
|
FOR i := 1 TO Len(aData)
|
|
? aData[i]
|
|
NEXT
|
|
RETURN
|
|
`)
|
|
for _, d := range diags {
|
|
if d.Severity == SevWarning {
|
|
t.Errorf("unexpected: %s", d)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMultiAssignDeclared(t *testing.T) {
|
|
diags := analyze(t, `
|
|
PROCEDURE Main()
|
|
LOCAL cName, nAge
|
|
cName, nAge := "Charles", 30
|
|
? cName, nAge
|
|
RETURN
|
|
`)
|
|
for _, d := range diags {
|
|
if d.Severity == SevWarning {
|
|
t.Errorf("unexpected: %s", d)
|
|
}
|
|
}
|
|
}
|