feat(asm): add did-you-mean suggestions for undefined labels
Assisted-by: DeepSeek V4 Pro
This commit is contained in:
+68
-4
@@ -162,7 +162,7 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
||||
target := labelFromOperand(ops[0])
|
||||
targetOff, ok := offsets[target]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("undefined label %q", target)
|
||||
return nil, fmt.Errorf("undefined label %q%s", target, suggestLabel(target, offsets))
|
||||
}
|
||||
offset := int32(targetOff - pc)
|
||||
// AUIPC X1, upper 20 bits
|
||||
@@ -187,7 +187,7 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
||||
}
|
||||
targetOff, ok := offsets[target]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("undefined label %q", target)
|
||||
return nil, fmt.Errorf("undefined label %q%s", target, suggestLabel(target, offsets))
|
||||
}
|
||||
offset := int32(targetOff - pc)
|
||||
// C.J: funct3=0x5, offset in ±2 KB, bit 0 must be 0.
|
||||
@@ -208,7 +208,7 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
||||
}
|
||||
targetOff, ok := offsets[target]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("undefined label %q", target)
|
||||
return nil, fmt.Errorf("undefined label %q%s", target, suggestLabel(target, offsets))
|
||||
}
|
||||
offset := int32(targetOff - pc)
|
||||
// JAL X0, target → C.J when offset fits.
|
||||
@@ -435,7 +435,7 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
||||
target := labelFromOperand(ops[2])
|
||||
targetOff, ok := offsets[target]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("undefined label %q", target)
|
||||
return nil, fmt.Errorf("undefined label %q%s", target, suggestLabel(target, offsets))
|
||||
}
|
||||
offset := int32(targetOff - pc)
|
||||
if rs1 < 0 || rs2 < 0 {
|
||||
@@ -1064,3 +1064,67 @@ func labelFromOperand(op *ast.Operand) string {
|
||||
}
|
||||
return op.Raw
|
||||
}
|
||||
|
||||
// suggestLabel returns a "did you mean" suggestion for an undefined label.
|
||||
func suggestLabel(target string, offsets map[string]int) string {
|
||||
if len(offsets) == 0 {
|
||||
return ""
|
||||
}
|
||||
// Find the closest matching label using Levenshtein distance.
|
||||
bestDist := len(target) + 1
|
||||
var best string
|
||||
for name := range offsets {
|
||||
dist := levenshtein(target, name)
|
||||
if dist < bestDist {
|
||||
bestDist = dist
|
||||
best = name
|
||||
}
|
||||
}
|
||||
// Only suggest if the distance is small enough.
|
||||
if bestDist <= 3 && bestDist < len(target)/2+1 {
|
||||
return fmt.Sprintf(" — did you mean %q?", best)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// levenshtein computes the Levenshtein distance between two strings.
|
||||
func levenshtein(a, b string) int {
|
||||
la, lb := len(a), len(b)
|
||||
if la == 0 {
|
||||
return lb
|
||||
}
|
||||
if lb == 0 {
|
||||
return la
|
||||
}
|
||||
// Create a matrix of distances.
|
||||
prev := make([]int, lb+1)
|
||||
curr := make([]int, lb+1)
|
||||
for j := 0; j <= lb; j++ {
|
||||
prev[j] = j
|
||||
}
|
||||
for i := 1; i <= la; i++ {
|
||||
curr[0] = i
|
||||
for j := 1; j <= lb; j++ {
|
||||
cost := 1
|
||||
if a[i-1] == b[j-1] {
|
||||
cost = 0
|
||||
}
|
||||
curr[j] = min3(curr[j-1]+1, prev[j]+1, prev[j-1]+cost)
|
||||
}
|
||||
prev, curr = curr, prev
|
||||
}
|
||||
return prev[lb]
|
||||
}
|
||||
|
||||
func min3(a, b, c int) int {
|
||||
if a < b {
|
||||
if a < c {
|
||||
return a
|
||||
}
|
||||
return c
|
||||
}
|
||||
if b < c {
|
||||
return b
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user