1
2
3
4
5 package astutil
6
7 import (
8 "fmt"
9 "go/ast"
10 "go/token"
11 "strconv"
12 "unicode/utf8"
13 )
14
15
16
17 func RangeInStringLiteral(lit *ast.BasicLit, start, end int) (token.Pos, token.Pos, error) {
18 startPos, err := PosInStringLiteral(lit, start)
19 if err != nil {
20 return 0, 0, fmt.Errorf("start: %v", err)
21 }
22 endPos, err := PosInStringLiteral(lit, end)
23 if err != nil {
24 return 0, 0, fmt.Errorf("end: %v", err)
25 }
26 return startPos, endPos, nil
27 }
28
29
30
31
32 func PosInStringLiteral(lit *ast.BasicLit, offset int) (token.Pos, error) {
33 raw := lit.Value
34
35 value, err := strconv.Unquote(raw)
36 if err != nil {
37 return 0, err
38 }
39 if !(0 <= offset && offset <= len(value)) {
40 return 0, fmt.Errorf("invalid offset")
41 }
42
43
44 quote := raw[0]
45 raw = raw[1 : len(raw)-1]
46
47 var (
48 i = 0
49 pos = lit.ValuePos + 1
50 )
51 for raw != "" && i < offset {
52 r, _, rest, _ := strconv.UnquoteChar(raw, quote)
53 sz := len(raw) - len(rest)
54 pos += token.Pos(sz)
55 raw = raw[sz:]
56 i += utf8.RuneLen(r)
57 }
58 return pos, nil
59 }
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 func PreorderStack(root ast.Node, stack []ast.Node, f func(n ast.Node, stack []ast.Node) bool) {
75 before := len(stack)
76 ast.Inspect(root, func(n ast.Node) bool {
77 if n != nil {
78 if !f(n, stack) {
79
80 return false
81 }
82 stack = append(stack, n)
83 } else {
84 stack = stack[:len(stack)-1]
85 }
86 return true
87 })
88 if len(stack) != before {
89 panic("push/pop mismatch")
90 }
91 }
92
View as plain text