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])
|
target := labelFromOperand(ops[0])
|
||||||
targetOff, ok := offsets[target]
|
targetOff, ok := offsets[target]
|
||||||
if !ok {
|
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)
|
offset := int32(targetOff - pc)
|
||||||
// AUIPC X1, upper 20 bits
|
// 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]
|
targetOff, ok := offsets[target]
|
||||||
if !ok {
|
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)
|
offset := int32(targetOff - pc)
|
||||||
// C.J: funct3=0x5, offset in ±2 KB, bit 0 must be 0.
|
// 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]
|
targetOff, ok := offsets[target]
|
||||||
if !ok {
|
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)
|
offset := int32(targetOff - pc)
|
||||||
// JAL X0, target → C.J when offset fits.
|
// 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])
|
target := labelFromOperand(ops[2])
|
||||||
targetOff, ok := offsets[target]
|
targetOff, ok := offsets[target]
|
||||||
if !ok {
|
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)
|
offset := int32(targetOff - pc)
|
||||||
if rs1 < 0 || rs2 < 0 {
|
if rs1 < 0 || rs2 < 0 {
|
||||||
@@ -1064,3 +1064,67 @@ func labelFromOperand(op *ast.Operand) string {
|
|||||||
}
|
}
|
||||||
return op.Raw
|
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