66
|
1 package syntax
|
|
2
|
|
3 import (
|
|
4 "bytes"
|
|
5 "strconv"
|
|
6 "strings"
|
|
7 "unicode"
|
|
8 )
|
|
9
|
|
10 func Escape(input string) string {
|
|
11 b := &bytes.Buffer{}
|
|
12 for _, r := range input {
|
|
13 escape(b, r, false)
|
|
14 }
|
|
15 return b.String()
|
|
16 }
|
|
17
|
|
18 const meta = `\.+*?()|[]{}^$# `
|
|
19
|
|
20 func escape(b *bytes.Buffer, r rune, force bool) {
|
|
21 if unicode.IsPrint(r) {
|
|
22 if strings.IndexRune(meta, r) >= 0 || force {
|
|
23 b.WriteRune('\\')
|
|
24 }
|
|
25 b.WriteRune(r)
|
|
26 return
|
|
27 }
|
|
28
|
|
29 switch r {
|
|
30 case '\a':
|
|
31 b.WriteString(`\a`)
|
|
32 case '\f':
|
|
33 b.WriteString(`\f`)
|
|
34 case '\n':
|
|
35 b.WriteString(`\n`)
|
|
36 case '\r':
|
|
37 b.WriteString(`\r`)
|
|
38 case '\t':
|
|
39 b.WriteString(`\t`)
|
|
40 case '\v':
|
|
41 b.WriteString(`\v`)
|
|
42 default:
|
|
43 if r < 0x100 {
|
|
44 b.WriteString(`\x`)
|
|
45 s := strconv.FormatInt(int64(r), 16)
|
|
46 if len(s) == 1 {
|
|
47 b.WriteRune('0')
|
|
48 }
|
|
49 b.WriteString(s)
|
|
50 break
|
|
51 }
|
|
52 b.WriteString(`\u`)
|
|
53 b.WriteString(strconv.FormatInt(int64(r), 16))
|
|
54 }
|
|
55 }
|
|
56
|
|
57 func Unescape(input string) (string, error) {
|
|
58 idx := strings.IndexRune(input, '\\')
|
|
59 // no slashes means no unescape needed
|
|
60 if idx == -1 {
|
|
61 return input, nil
|
|
62 }
|
|
63
|
|
64 buf := bytes.NewBufferString(input[:idx])
|
|
65 // get the runes for the rest of the string -- we're going full parser scan on this
|
|
66
|
|
67 p := parser{}
|
|
68 p.setPattern(input[idx+1:])
|
|
69 for {
|
|
70 if p.rightMost() {
|
|
71 return "", p.getErr(ErrIllegalEndEscape)
|
|
72 }
|
|
73 r, err := p.scanCharEscape()
|
|
74 if err != nil {
|
|
75 return "", err
|
|
76 }
|
|
77 buf.WriteRune(r)
|
|
78 // are we done?
|
|
79 if p.rightMost() {
|
|
80 return buf.String(), nil
|
|
81 }
|
|
82
|
|
83 r = p.moveRightGetChar()
|
|
84 for r != '\\' {
|
|
85 buf.WriteRune(r)
|
|
86 if p.rightMost() {
|
|
87 // we're done, no more slashes
|
|
88 return buf.String(), nil
|
|
89 }
|
|
90 // keep scanning until we get another slash
|
|
91 r = p.moveRightGetChar()
|
|
92 }
|
|
93 }
|
|
94 }
|