feat: gasm-devkit 0.1.0 — GAsm lexer, parser, linter, formatter, LSP and amd64 assembler
Assisted-by: Qwen 3.8 Max Preview
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package format
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sourcedock.dev/petrbalvin/gasm-devkit/lexer"
|
||||
"sourcedock.dev/petrbalvin/gasm-devkit/parser"
|
||||
"sourcedock.dev/petrbalvin/gasm-devkit/token"
|
||||
)
|
||||
|
||||
func TestGolden(t *testing.T) {
|
||||
in := "#include \"textflag.h\"\n" +
|
||||
"\n" +
|
||||
"TEXT ·f(SB), NOSPLIT, $0\n" +
|
||||
"MOVQ swin_base+0(FP), SI\n" +
|
||||
"LEAQ (SI)(BX*4), R9\n" +
|
||||
"ANDQ $-8, R10\n" +
|
||||
"VFMADD231PD Z14, Z12, Z10\n" +
|
||||
"RET\n"
|
||||
|
||||
want := "#include \"textflag.h\"\n" +
|
||||
"\n" +
|
||||
"TEXT ·f(SB), NOSPLIT, $0\n" +
|
||||
"\tMOVQ swin_base+0(FP), SI\n" +
|
||||
"\tLEAQ (SI)(BX*4), R9\n" +
|
||||
"\tANDQ $-8, R10\n" +
|
||||
"\tVFMADD231PD Z14, Z12, Z10\n" +
|
||||
"\tRET\n"
|
||||
|
||||
got := Source("f_amd64.s", in)
|
||||
if got != want {
|
||||
t.Fatalf("formatting mismatch:\n--- got ---\n%q\n--- want ---\n%q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOperandSpacing(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"4(SI)": "4(SI)",
|
||||
"(SI)(BX*4)": "(SI)(BX*4)",
|
||||
"$-8": "$-8",
|
||||
"$0x80020100": "$0x80020100",
|
||||
"swin_base+0(FP)": "swin_base+0(FP)",
|
||||
"mask24<>(SB)": "mask24<>(SB)",
|
||||
"·idx16+0(SB)/4": "·idx16+0(SB)/4",
|
||||
}
|
||||
for in, want := range cases {
|
||||
toks := lexOperands(in)
|
||||
if got := renderOps(toks); got != want {
|
||||
t.Errorf("renderOps(%q) = %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lexOperands lexes a single operand string and drops the EOF token.
|
||||
func lexOperands(s string) []token.Token {
|
||||
toks := lexer.Tokenize(s)
|
||||
return toks[:len(toks)-1] // drop trailing EOF
|
||||
}
|
||||
|
||||
func TestIdempotent(t *testing.T) {
|
||||
src, err := os.ReadFile("../testdata/sample_amd64.s")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
once := Source("sample_amd64.s", string(src))
|
||||
twice := Source("sample_amd64.s", once)
|
||||
if once != twice {
|
||||
t.Fatal("formatting is not idempotent on the fixture")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRoundTrip checks that formatting produces source that still parses
|
||||
// cleanly, on the fixture and on the real go-flac kernels when present.
|
||||
func TestRoundTrip(t *testing.T) {
|
||||
files := []string{"../testdata/sample_amd64.s"}
|
||||
real, _ := filepath.Glob("../../go-libraries/go-*/*.s")
|
||||
files = append(files, real...)
|
||||
for _, path := range files {
|
||||
src, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
formatted := Source(path, string(src))
|
||||
if _, errs := parser.Parse(path, formatted); len(errs) > 0 {
|
||||
t.Errorf("formatted %s no longer parses: %v", path, errs)
|
||||
}
|
||||
if strings.TrimSpace(formatted) == "" {
|
||||
t.Errorf("formatted %s is empty", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user