-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathstring.go
148 lines (128 loc) · 3.5 KB
/
string.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright (c) 2017 Opsidian Ltd.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package terminal
import (
"fmt"
"strconv"
"unicode/utf8"
"github.com/conflowio/parsley/data"
"github.com/conflowio/parsley/parser"
"github.com/conflowio/parsley/parsley"
"github.com/conflowio/parsley/text"
)
// StringNode is a leaf node in the AST
type StringNode struct {
schema interface{}
value string
pos parsley.Pos
readerPos parsley.Pos
}
// NewStringNode creates a new StringNode instance
func NewStringNode(schema interface{}, value string, pos parsley.Pos, readerPos parsley.Pos) *StringNode {
return &StringNode{
schema: schema,
value: value,
pos: pos,
readerPos: readerPos,
}
}
// Token returns with the node token
func (s *StringNode) Token() string {
return "STRING"
}
// Schema returns the schema for the node's value
func (s *StringNode) Schema() interface{} {
return s.schema
}
// Value returns with the value of the node
func (s *StringNode) Value() interface{} {
return s.value
}
// Pos returns the position
func (s *StringNode) Pos() parsley.Pos {
return s.pos
}
// ReaderPos returns the position of the first character immediately after this node
func (s *StringNode) ReaderPos() parsley.Pos {
return s.readerPos
}
// SetReaderPos changes the reader position
func (s *StringNode) SetReaderPos(fun func(parsley.Pos) parsley.Pos) {
s.readerPos = fun(s.readerPos)
}
// String returns with a string representation of the node
func (s *StringNode) String() string {
return fmt.Sprintf("%s{%v, %d..%d}", s.Token(), s.value, s.pos, s.readerPos)
}
// String matches a string literal enclosed in double quotes
func String(schema interface{}, allowBackquote bool) parser.Func {
notFoundErr := parsley.NotFoundError("string literal")
return parser.Func(func(ctx *parsley.Context, leftRecCtx data.IntMap, pos parsley.Pos) (parsley.Node, data.IntSet, parsley.Error) {
tr := ctx.Reader().(*text.Reader)
quote := '"'
readerPos, found := tr.ReadRune(pos, quote)
if !found {
if allowBackquote {
quote = '`'
readerPos, found = tr.ReadRune(pos, quote)
}
}
if !found {
return nil, data.EmptyIntSet, parsley.NewError(pos, notFoundErr)
}
// check for empty string
readerPos, found = tr.ReadRune(readerPos, quote)
if found {
return NewStringNode(schema, "", pos, readerPos), data.EmptyIntSet, nil
}
var value []byte
if quote == '`' {
readerPos, value = tr.ReadRegexp(readerPos, "[^`]+")
} else {
readerPos, value = tr.Readf(readerPos, unquoteString)
}
readerPos, found = tr.ReadRune(readerPos, quote)
if !found {
return nil, data.EmptyIntSet, parsley.NewErrorf(readerPos, "was expecting '%s'", string(quote))
}
return NewStringNode(schema, string(value), pos, readerPos), data.EmptyIntSet, nil
})
}
func unquoteString(b []byte) ([]byte, int) {
i := 0
for {
if i >= len(b) {
return b, len(b)
}
if b[i] == '\r' || b[i] == '\n' {
return b[0:i], i
}
if b[i] == '"' {
return b[0:i], i
} else if b[i] == '\\' || b[i] >= utf8.RuneSelf {
break
}
i++
}
str := string(b[i:])
var tail string
var res = make([]byte, 0, i)
res = append(res, b[0:i]...)
var err error
var ch rune
for {
if str == "" {
break
}
ch, _, tail, err = strconv.UnquoteChar(str, '"')
if err != nil {
break
}
res = append(res, string(ch)...)
str = tail
}
return res, len(b) - len(str)
}