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  }