github.com/jxskiss/gopkg@v0.17.3/json/extparser/parser.go (about) 1 package extparser 2 3 import ( 4 "errors" 5 "io/ioutil" 6 "path/filepath" 7 "strings" 8 "unsafe" 9 ) 10 11 //go:generate peg json.peg 12 13 const maxImportDepth = 10 14 15 func Parse(data []byte, importRoot string) ([]byte, error) { 16 return parse(data, importRoot, 0) 17 } 18 19 func parse(data []byte, importRoot string, depth int) ([]byte, error) { 20 if depth > maxImportDepth { 21 return nil, errors.New("max import depth exceeded") 22 } 23 24 doc := &JSON{ 25 Buffer: b2s(data), 26 } 27 if err := doc.Init(); err != nil { 28 return nil, err 29 } 30 if err := doc.Parse(); err != nil { 31 return nil, err 32 } 33 if !doc.hasExtendedFeature() { 34 return data, nil 35 } 36 37 parser := &parser{ 38 doc: doc, 39 buf: make([]byte, 0, len(data)), 40 root: importRoot, 41 depth: depth, 42 } 43 return parser.rewrite() 44 } 45 46 type parser struct { 47 doc *JSON 48 buf []byte 49 50 root string 51 depth int 52 } 53 54 func (p *parser) text(n *node32) string { 55 return p.doc.text(n.token32) 56 } 57 58 func (p *parser) rewrite() ([]byte, error) { 59 root := p.doc.AST() 60 if root.pegRule != ruleDocument { 61 return nil, errors.New("invalid JSON document") 62 } 63 64 for n := root.up; n != nil; n = n.next { 65 switch n.pegRule { 66 case ruleSpacing: 67 continue 68 case ruleJSON: 69 if err := p.parseJSON(n); err != nil { 70 return nil, err 71 } 72 } 73 } 74 return p.buf, nil 75 } 76 77 func (p *parser) parseJSON(n *node32) (err error) { 78 n = n.up 79 switch n.pegRule { 80 case ruleObject: 81 if err = p.parseObject(n); err != nil { 82 return 83 } 84 case ruleArray: 85 if err = p.parseArray(n); err != nil { 86 return 87 } 88 case ruleString: 89 p.buf = append(p.buf, p.parseString(n)...) 90 case ruleTrue: 91 p.buf = append(p.buf, "true"...) 92 case ruleFalse: 93 p.buf = append(p.buf, "false"...) 94 case ruleNull: 95 p.buf = append(p.buf, "null"...) 96 case ruleNumber: 97 p.buf = append(p.buf, p.text(n)...) 98 case ruleImport: 99 if err = p.parseImport(n); err != nil { 100 return 101 } 102 } 103 return nil 104 } 105 106 func (p *parser) parseObject(n *node32) (err error) { 107 var preRule pegRule 108 for n := n.up; n != nil; n = n.next { 109 switch n.pegRule { 110 case ruleLWING: 111 p.buf = append(p.buf, '{') 112 case ruleRWING: 113 if preRule == ruleCOMMA { 114 p.buf = p.buf[:len(p.buf)-1] 115 } 116 p.buf = append(p.buf, '}') 117 case ruleCOLON: 118 p.buf = append(p.buf, ':') 119 case ruleCOMMA: 120 p.buf = append(p.buf, ',') 121 case ruleObjectKey: 122 p.buf = append(p.buf, p.parseObjectKey(n)...) 123 case ruleJSON: 124 err = p.parseJSON(n) 125 if err != nil { 126 return 127 } 128 } 129 preRule = n.pegRule 130 } 131 return nil 132 } 133 134 func (p *parser) parseObjectKey(n *node32) string { 135 n = n.up 136 switch n.pegRule { 137 case ruleSimpleIdentifier: 138 return `"` + string(p.doc.buffer[n.begin:n.end]) + `"` 139 case ruleString: 140 return p.parseString(n) 141 } 142 return "" 143 } 144 145 func (p *parser) parseArray(n *node32) (err error) { 146 var preRule pegRule 147 for n := n.up; n != nil; n = n.next { 148 switch n.pegRule { 149 case ruleLBRK: 150 p.buf = append(p.buf, '[') 151 case ruleRBRK: 152 if preRule == ruleCOMMA { 153 p.buf = p.buf[:len(p.buf)-1] 154 } 155 p.buf = append(p.buf, ']') 156 case ruleCOMMA: 157 p.buf = append(p.buf, ',') 158 case ruleJSON: 159 err = p.parseJSON(n) 160 if err != nil { 161 return 162 } 163 } 164 preRule = n.pegRule 165 } 166 return nil 167 } 168 169 var singleQuoteReplacer = strings.NewReplacer(`\'`, `'`, `"`, `\"`) 170 171 func (p *parser) parseString(n *node32) string { 172 n = n.up 173 switch n.pegRule { 174 case ruleSingleQuoteLiteral: 175 text := string(p.doc.buffer[n.begin+1 : n.end-1]) 176 text = singleQuoteReplacer.Replace(text) 177 return `"` + text + `"` 178 case ruleDoubleQuoteLiteral: 179 return p.text(n) 180 } 181 return "" 182 } 183 184 func (p *parser) parseImport(n *node32) (err error) { 185 n = n.up 186 importPath := p.parseString(n) 187 importPath = filepath.Join(p.root, importPath[1:len(importPath)-1]) 188 included, err := ioutil.ReadFile(importPath) 189 if err != nil { 190 return 191 } 192 included, err = parse(included, p.root, p.depth+1) 193 if err != nil { 194 return 195 } 196 p.buf = append(p.buf, included...) 197 return nil 198 } 199 200 func (p *JSON) hasExtendedFeature() bool { 201 var preRule pegRule 202 for _, n := range p.Tokens() { 203 switch n.pegRule { 204 case ruleSingleQuoteLiteral, 205 ruleImport, 206 ruleLongComment, ruleLineComment, rulePragma: 207 return true 208 case ruleRWING, ruleRBRK: 209 if preRule == ruleCOMMA { 210 return true 211 } 212 case ruleTrue: 213 if p.text(n) != "true" { 214 return true 215 } 216 case ruleFalse: 217 if p.text(n) != "false" { 218 return true 219 } 220 case ruleNull: 221 if p.text(n) != "null" { 222 return true 223 } 224 } 225 preRule = n.pegRule 226 } 227 return false 228 } 229 230 func (p *JSON) text(n token32) string { 231 return string(p.buffer[n.begin:n.end]) 232 } 233 234 func b2s(b []byte) string { 235 return *(*string)(unsafe.Pointer(&b)) 236 }