escape.go

 1package syntax
 2
 3import (
 4	"bytes"
 5	"strconv"
 6	"strings"
 7	"unicode"
 8)
 9
10func 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
18const meta = `\.+*?()|[]{}^$# `
19
20func 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
57func 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}