Codebase list golang-github-tdewolff-parse / ec00c42
Move parse back from tdewolff/minify/parse to tdewolff/parse Taco de Wolff 2 years ago
26 changed file(s) with 194 addition(s) and 204 deletion(s). Raw diff Collapse all Expand all
0 # DEPRECATED
0 # Parse [![API reference](https://img.shields.io/badge/godoc-reference-5272B4)](https://pkg.go.dev/github.com/tdewolff/parse/v2?tab=doc)
11
2 Use https://github.com/tdewolff/minify/tree/master/parse instead.
2 This package contains several lexers and parsers written in [Go][1]. All subpackages are built to be streaming, high performance and to be in accordance with the official (latest) specifications.
3
4 The lexers are implemented using `buffer.Lexer` in https://github.com/tdewolff/parse/buffer and the parsers work on top of the lexers. Some subpackages have hashes defined (using [Hasher](https://github.com/tdewolff/hasher)) that speed up common byte-slice comparisons.
5
6 ## Buffer
7 ### Reader
8 Reader is a wrapper around a `[]byte` that implements the `io.Reader` interface. It is comparable to `bytes.Reader` but has slightly different semantics (and a slightly smaller memory footprint).
9
10 ### Writer
11 Writer is a buffer that implements the `io.Writer` interface and expands the buffer as needed. The reset functionality allows for better memory reuse. After calling `Reset`, it will overwrite the current buffer and thus reduce allocations.
12
13 ### Lexer
14 Lexer is a read buffer specifically designed for building lexers. It keeps track of two positions: a start and end position. The start position is the beginning of the current token being parsed, the end position is being moved forward until a valid token is found. Calling `Shift` will collapse the positions to the end and return the parsed `[]byte`.
15
16 Moving the end position can go through `Move(int)` which also accepts negative integers. One can also use `Pos() int` to try and parse a token, and if it fails rewind with `Rewind(int)`, passing the previously saved position.
17
18 `Peek(int) byte` will peek forward (relative to the end position) and return the byte at that location. `PeekRune(int) (rune, int)` returns UTF-8 runes and its length at the given **byte** position. Upon an error `Peek` will return `0`, the **user must peek at every character** and not skip any, otherwise it may skip a `0` and panic on out-of-bounds indexing.
19
20 `Lexeme() []byte` will return the currently selected bytes, `Skip()` will collapse the selection. `Shift() []byte` is a combination of `Lexeme() []byte` and `Skip()`.
21
22 When the passed `io.Reader` returned an error, `Err() error` will return that error even if not at the end of the buffer.
23
24 ### StreamLexer
25 StreamLexer behaves like Lexer but uses a buffer pool to read in chunks from `io.Reader`, retaining old buffers in memory that are still in use, and re-using old buffers otherwise. Calling `Free(n int)` frees up `n` bytes from the internal buffer(s). It holds an array of buffers to accommodate for keeping everything in-memory. Calling `ShiftLen() int` returns the number of bytes that have been shifted since the previous call to `ShiftLen`, which can be used to specify how many bytes need to be freed up from the buffer. If you don't need to keep returned byte slices around, call `Free(ShiftLen())` after every `Shift` call.
26
27 ## Strconv
28 This package contains string conversion function much like the standard library's `strconv` package, but it is specifically tailored for the performance needs within the `minify` package.
29
30 For example, the floating-point to string conversion function is approximately twice as fast as the standard library, but it is not as precise.
31
32 ## CSS
33 This package is a CSS3 lexer and parser. Both follow the specification at [CSS Syntax Module Level 3](http://www.w3.org/TR/css-syntax-3/). The lexer takes an io.Reader and converts it into tokens until the EOF. The parser returns a parse tree of the full io.Reader input stream, but the low-level `Next` function can be used for stream parsing to returns grammar units until the EOF.
34
35 [See README here](https://github.com/tdewolff/minify/tree/master/parse/css).
36
37 ## HTML
38 This package is an HTML5 lexer. It follows the specification at [The HTML syntax](http://www.w3.org/TR/html5/syntax.html). The lexer takes an io.Reader and converts it into tokens until the EOF.
39
40 [See README here](https://github.com/tdewolff/minify/tree/master/parse/html).
41
42 ## JS
43 This package is a JS lexer (ECMA-262, edition 6.0). It follows the specification at [ECMAScript Language Specification](http://www.ecma-international.org/ecma-262/6.0/). The lexer takes an io.Reader and converts it into tokens until the EOF.
44
45 [See README here](https://github.com/tdewolff/minify/tree/master/parse/js).
46
47 ## JSON
48 This package is a JSON parser (ECMA-404). It follows the specification at [JSON](http://json.org/). The parser takes an io.Reader and converts it into tokens until the EOF.
49
50 [See README here](https://github.com/tdewolff/minify/tree/master/parse/json).
51
52 ## SVG
53 This package contains common hashes for SVG1.1 tags and attributes.
54
55 ## XML
56 This package is an XML1.0 lexer. It follows the specification at [Extensible Markup Language (XML) 1.0 (Fifth Edition)](http://www.w3.org/TR/xml/). The lexer takes an io.Reader and converts it into tokens until the EOF.
57
58 [See README here](https://github.com/tdewolff/minify/tree/master/parse/xml).
59
60 ## License
61 Released under the [MIT license](LICENSE.md).
62
63 [1]: http://golang.org/ "Go Language"
0 /*
1 Package buffer contains buffer and wrapper types for byte slices. It is useful for writing lexers or other high-performance byte slice handling.
2
3 The `Reader` and `Writer` types implement the `io.Reader` and `io.Writer` respectively and provide a thinner and faster interface than `bytes.Buffer`.
4 The `Lexer` type is useful for building lexers because it keeps track of the start and end position of a byte selection, and shifts the bytes whenever a valid token is found.
5 The `StreamLexer` does the same, but keeps a buffer pool so that it reads a limited amount at a time, allowing to parse from streaming sources.
6 */
0 // Package buffer contains buffer and wrapper types for byte slices. It is useful for writing lexers or other high-performance byte slice handling.
1 // The `Reader` and `Writer` types implement the `io.Reader` and `io.Writer` respectively and provide a thinner and faster interface than `bytes.Buffer`.
2 // The `Lexer` type is useful for building lexers because it keeps track of the start and end position of a byte selection, and shifts the bytes whenever a valid token is found.
3 // The `StreamLexer` does the same, but keeps a buffer pool so that it reads a limited amount at a time, allowing to parse from streaming sources.
74 package buffer
85
96 // defaultBufSize specifies the default initial length of internal buffers.
0 # CSS [![GoDoc](http://godoc.org/github.com/tdewolff/parse/css?status.svg)](http://godoc.org/github.com/tdewolff/parse/css)
0 # CSS [![API reference](https://img.shields.io/badge/godoc-reference-5272B4)](https://pkg.go.dev/github.com/tdewolff/minify/v2/parse/css?tab=doc)
11
22 This package is a CSS3 lexer and parser written in [Go][1]. Both follow the specification at [CSS Syntax Module Level 3](http://www.w3.org/TR/css-syntax-3/). The lexer takes an io.Reader and converts it into tokens until the EOF. The parser returns a parse tree of the full io.Reader input stream, but the low-level `Next` function can be used for stream parsing to returns grammar units until the EOF.
33
0 # HTML [![GoDoc](http://godoc.org/github.com/tdewolff/parse/html?status.svg)](http://godoc.org/github.com/tdewolff/parse/html)
0 # HTML [![API reference](https://img.shields.io/badge/godoc-reference-5272B4)](https://pkg.go.dev/github.com/tdewolff/minify/v2/parse/html?tab=doc)
11
22 This package is an HTML5 lexer written in [Go][1]. It follows the specification at [The HTML syntax](http://www.w3.org/TR/html5/syntax.html). The lexer takes an io.Reader and converts it into tokens until the EOF.
33
325325 l.text = parse.ToLower(l.r.Lexeme()[1:])
326326 if h := ToHash(l.text); h == Textarea || h == Title || h == Style || h == Xmp || h == Iframe || h == Script || h == Plaintext || h == Svg || h == Math {
327327 if h == Svg || h == Math {
328 data := l.shiftXml(h)
328 data := l.shiftXML(h)
329329 if l.err != nil {
330330 return ErrorToken, nil
331331 }
424424 return parse.ToLower(l.r.Shift())
425425 }
426426
427 // shiftXml parses the content of a svg or math tag according to the XML 1.1 specifications, including the tag itself.
427 // shiftXML parses the content of a svg or math tag according to the XML 1.1 specifications, including the tag itself.
428428 // So far we have already parsed `<svg` or `<math`.
429 func (l *Lexer) shiftXml(rawTag Hash) []byte {
429 func (l *Lexer) shiftXML(rawTag Hash) []byte {
430430 inQuote := false
431431 for {
432432 c := l.r.Peek(0)
3232 if len(b) > 1 && (b[0] == '"' || b[0] == '\'') && b[0] == b[len(b)-1] {
3333 b = b[1 : len(b)-1]
3434 }
35 val := EscapeAttrVal(&buf, orig, []byte(b), false)
35 val := EscapeAttrVal(&buf, orig, b, false)
3636 test.String(t, string(val), tt.expected)
3737 })
3838 }
5454 if len(b) > 1 && (b[0] == '"' || b[0] == '\'') && b[0] == b[len(b)-1] {
5555 b = b[1 : len(b)-1]
5656 }
57 val := EscapeAttrVal(&buf, orig, []byte(b), true)
57 val := EscapeAttrVal(&buf, orig, b, true)
5858 test.String(t, string(val), tt.expected)
5959 })
6060 }
0 # JS [![GoDoc](http://godoc.org/github.com/tdewolff/parse/js?status.svg)](http://godoc.org/github.com/tdewolff/parse/js)
0 # JS [![API reference](https://img.shields.io/badge/godoc-reference-5272B4)](https://pkg.go.dev/github.com/tdewolff/minify/v2/parse/js?tab=doc)
11
22 This package is a JS lexer (ECMAScript 2020) written in [Go][1]. It follows the specification at [ECMAScript 2020 Language Specification](https://tc39.es/ecma262/). The lexer takes an io.Reader and converts it into tokens until the EOF.
33
6161 // Var is a variable, where Decl is the type of declaration and can be var|function for function scoped variables, let|const|class for block scoped variables.
6262 type Var struct {
6363 Data []byte
64 Link *Var // is set when merging variable uses, as in: {a} {var a} where the first lins to the second
64 Link *Var // is set when merging variable uses, as in: {a} {var a} where the first links to the second, only used for undeclared variables
6565 Uses uint16
6666 Decl DeclType
6767 }
306306 s.Undeclared = s.Undeclared[:0]
307307 }
308308
309 // Unscope moves all declared variables of the current scope to the parent scope. Undeclared variables are already in the parent scope.
310 func (s *Scope) Unscope() {
311 for _, vorig := range s.Declared {
312 // no need to evaluate vorig.Link as vorig.Data stays the same, and Link is always nil in Declared
313 // vorig.Uses will be atleast 1
314 s.Parent.Declared = append(s.Parent.Declared, vorig)
315 }
316 s.Declared = s.Declared[:0]
317 s.Undeclared = s.Undeclared[:0]
318 }
319
309320 ////////////////////////////////////////////////////////////////
310321
311322 // IStmt is a dummy interface for statements.
403414 Init IExpr // can be nil
404415 Cond IExpr // can be nil
405416 Post IExpr // can be nil
406 Body BlockStmt
417 Body *BlockStmt
407418 }
408419
409420 func (n ForStmt) String() string {
426437 type ForInStmt struct {
427438 Init IExpr
428439 Value IExpr
429 Body BlockStmt
440 Body *BlockStmt
430441 }
431442
432443 func (n ForInStmt) String() string {
438449 Await bool
439450 Init IExpr
440451 Value IExpr
441 Body BlockStmt
452 Body *BlockStmt
442453 }
443454
444455 func (n ForOfStmt) String() string {
460471 type SwitchStmt struct {
461472 Init IExpr
462473 List []CaseClause
474 Scope
463475 }
464476
465477 func (n SwitchStmt) String() string {
535547
536548 // TryStmt is a try statement.
537549 type TryStmt struct {
538 Body BlockStmt
550 Body *BlockStmt
539551 Binding IBinding // can be nil
540552 Catch *BlockStmt // can be nil
541553 Finally *BlockStmt // can be nil
906918 Name *Var // can be nil
907919 Extends IExpr // can be nil
908920 Definitions []FieldDefinition
909 Methods []MethodDecl
921 Methods []*MethodDecl
910922 }
911923
912924 func (n ClassDecl) String() string {
210210
211211 switch tt := p.tt; tt {
212212 case OpenBraceToken:
213 blockStmt := p.parseBlockStmt("block statement")
214 stmt = &blockStmt
213 stmt = p.parseBlockStmt("block statement")
215214 case ConstToken, VarToken:
216215 if !allowDeclaration && tt == ConstToken {
217216 p.fail("statement")
325324 return
326325 }
327326
328 body := BlockStmt{}
327 body := &BlockStmt{}
329328 parent := p.enterScope(&body.Scope, false)
330329
331330 var init IExpr
426425 return
427426 }
428427
429 clauses := []CaseClause{}
428 switchStmt := &SwitchStmt{Init: init}
429 parent := p.enterScope(&switchStmt.Scope, false)
430430 for {
431431 if p.tt == ErrorToken {
432432 p.fail("switch statement")
455455 for p.tt != CaseToken && p.tt != DefaultToken && p.tt != CloseBraceToken && p.tt != ErrorToken {
456456 stmts = append(stmts, p.parseStmt(true))
457457 }
458 clauses = append(clauses, CaseClause{clause, list, stmts})
459 }
460 stmt = &SwitchStmt{init, clauses}
458 switchStmt.List = append(switchStmt.List, CaseClause{clause, list, stmts})
459 }
460 p.exitScope(parent)
461 stmt = switchStmt
461462 case FunctionToken:
462463 if !allowDeclaration {
463464 p.fail("statement")
464465 return
465466 }
466 funcDecl := p.parseFuncDecl()
467 stmt = &funcDecl
467 stmt = p.parseFuncDecl()
468468 case AsyncToken: // async function
469469 if !allowDeclaration {
470470 p.fail("statement")
473473 async := p.data
474474 p.next()
475475 if p.tt == FunctionToken && !p.prevLT {
476 funcDecl := p.parseAsyncFuncDecl()
477 stmt = &funcDecl
476 stmt = p.parseAsyncFuncDecl()
478477 } else {
479478 // expression
480479 stmt = &ExprStmt{p.parseAsyncExpression(OpExpr, async)}
488487 p.fail("statement")
489488 return
490489 }
491 classDecl := p.parseClassDecl()
492 stmt = &classDecl
490 stmt = p.parseClassDecl()
493491 case ThrowToken:
494492 p.next()
495493 var value IExpr
521519 }
522520 if p.tt == FinallyToken {
523521 p.next()
524 blockStmt := p.parseBlockStmt("try-finally statement")
525 finally = &blockStmt
522 finally = p.parseBlockStmt("try-finally statement")
526523 }
527524 stmt = &TryStmt{body, binding, catch, finally}
528525 case DebuggerToken:
586583 return
587584 }
588585
589 func (p *Parser) parseBlockStmt(in string) (blockStmt BlockStmt) {
586 func (p *Parser) parseBlockStmt(in string) (blockStmt *BlockStmt) {
587 blockStmt = &BlockStmt{}
590588 parent := p.enterScope(&blockStmt.Scope, false)
591589 blockStmt.List = p.parseStmtList(in)
592590 p.exitScope(parent)
732730 varDecl := p.parseVarDecl(tt)
733731 exportStmt.Decl = &varDecl
734732 } else if p.tt == FunctionToken {
735 funcDecl := p.parseFuncDecl()
736 exportStmt.Decl = &funcDecl
733 exportStmt.Decl = p.parseFuncDecl()
737734 } else if p.tt == AsyncToken { // async function
738735 p.next()
739736 if p.tt != FunctionToken || p.prevLT {
740737 p.fail("export statement", FunctionToken)
741738 return
742739 }
743 funcDecl := p.parseAsyncFuncDecl()
744 exportStmt.Decl = &funcDecl
740 exportStmt.Decl = p.parseAsyncFuncDecl()
745741 } else if p.tt == ClassToken {
746 classDecl := p.parseClassDecl()
747 exportStmt.Decl = &classDecl
742 exportStmt.Decl = p.parseClassDecl()
748743 } else if p.tt == DefaultToken {
749744 exportStmt.Default = true
750745 p.next()
751746 if p.tt == FunctionToken {
752 funcDecl := p.parseFuncExpr()
753 exportStmt.Decl = &funcDecl
747 exportStmt.Decl = p.parseFuncExpr()
754748 } else if p.tt == AsyncToken { // async function or async arrow function
755749 async := p.data
756750 p.next()
757751 if p.tt == FunctionToken && !p.prevLT {
758 funcDecl := p.parseAsyncFuncExpr()
759 exportStmt.Decl = &funcDecl
752 exportStmt.Decl = p.parseAsyncFuncExpr()
760753 } else {
761754 // expression
762755 exportStmt.Decl = p.parseAsyncExpression(OpExpr, async)
763756 }
764757 } else if p.tt == ClassToken {
765 classDecl := p.parseClassExpr()
766 exportStmt.Decl = &classDecl
758 exportStmt.Decl = p.parseClassExpr()
767759 } else {
768760 exportStmt.Decl = p.parseExpression(OpAssign)
769761 }
840832 return
841833 }
842834
843 func (p *Parser) parseFuncDecl() (funcDecl FuncDecl) {
835 func (p *Parser) parseFuncDecl() (funcDecl *FuncDecl) {
844836 return p.parseAnyFunc(false, false)
845837 }
846838
847 func (p *Parser) parseAsyncFuncDecl() (funcDecl FuncDecl) {
839 func (p *Parser) parseAsyncFuncDecl() (funcDecl *FuncDecl) {
848840 return p.parseAnyFunc(true, false)
849841 }
850842
851 func (p *Parser) parseFuncExpr() (funcDecl FuncDecl) {
843 func (p *Parser) parseFuncExpr() (funcDecl *FuncDecl) {
852844 return p.parseAnyFunc(false, true)
853845 }
854846
855 func (p *Parser) parseAsyncFuncExpr() (funcDecl FuncDecl) {
847 func (p *Parser) parseAsyncFuncExpr() (funcDecl *FuncDecl) {
856848 return p.parseAnyFunc(true, true)
857849 }
858850
859 func (p *Parser) parseAnyFunc(async, inExpr bool) (funcDecl FuncDecl) {
851 func (p *Parser) parseAnyFunc(async, inExpr bool) (funcDecl *FuncDecl) {
860852 // assume we're at function
861853 p.next()
854 funcDecl = &FuncDecl{}
862855 funcDecl.Async = async
863856 funcDecl.Generator = p.tt == MulToken
864857 if funcDecl.Generator {
899892 return
900893 }
901894
902 func (p *Parser) parseClassDecl() (classDecl ClassDecl) {
895 func (p *Parser) parseClassDecl() (classDecl *ClassDecl) {
903896 return p.parseAnyClass(false)
904897 }
905898
906 func (p *Parser) parseClassExpr() (classDecl ClassDecl) {
899 func (p *Parser) parseClassExpr() (classDecl *ClassDecl) {
907900 return p.parseAnyClass(true)
908901 }
909902
910 func (p *Parser) parseAnyClass(inExpr bool) (classDecl ClassDecl) {
903 func (p *Parser) parseAnyClass(inExpr bool) (classDecl *ClassDecl) {
911904 // assume we're at class
912905 p.next()
906 classDecl = &ClassDecl{}
913907 if IsIdentifier(p.tt) || p.tt == YieldToken || p.tt == AwaitToken {
914908 if !inExpr {
915909 var ok bool
948942 }
949943
950944 method, definition := p.parseClassElement()
951 if method.Name.IsSet() {
945 if method != nil {
952946 classDecl.Methods = append(classDecl.Methods, method)
953947 } else {
954948 classDecl.Definitions = append(classDecl.Definitions, definition)
957951 return
958952 }
959953
960 func (p *Parser) parseClassElement() (method MethodDecl, definition FieldDefinition) {
954 func (p *Parser) parseClassElement() (method *MethodDecl, definition FieldDefinition) {
955 method = &MethodDecl{}
961956 var data []byte
962957 if p.tt == StaticToken {
963958 method.Static = true
10191014 p.next()
10201015 definition.Init = p.parseExpression(OpAssign)
10211016 }
1022 method = MethodDecl{}
1017 method = nil
10231018 return
10241019 }
10251020
13911386 return
13921387 }
13931388
1394 func (p *Parser) parseAsyncArrowFunc() (arrowFunc ArrowFunc) {
1389 func (p *Parser) parseAsyncArrowFunc() (arrowFunc *ArrowFunc) {
13951390 // expect we're at Identifier or Yield or (
1391 arrowFunc = &ArrowFunc{}
13961392 parent := p.enterScope(&arrowFunc.Body.Scope, true)
13971393 parentAsync, parentGenerator := p.async, p.generator
13981394 p.async, p.generator = true, false
14031399 arrowFunc.Params.List = []BindingElement{{Binding: ref}}
14041400 } else {
14051401 arrowFunc.Params = p.parseFuncParams("arrow function")
1402
1403 // could be CallExpression of: async(params)
1404 if p.tt != ArrowToken {
1405 }
14061406 }
14071407
14081408 arrowFunc.Async = true
14131413 return
14141414 }
14151415
1416 func (p *Parser) parseIdentifierArrowFunc(v *Var) (arrowFunc ArrowFunc) {
1416 func (p *Parser) parseIdentifierArrowFunc(v *Var) (arrowFunc *ArrowFunc) {
14171417 // expect we're at =>
1418 arrowFunc = &ArrowFunc{}
14181419 parent := p.enterScope(&arrowFunc.Body.Scope, true)
14191420 parentAsync, parentGenerator := p.async, p.generator
14201421 p.async, p.generator = false, false
14751476 precLeft := OpPrimary
14761477 if !p.prevLT && p.tt == FunctionToken {
14771478 // primary expression
1478 funcDecl := p.parseAsyncFuncExpr()
1479 left = &funcDecl
1479 left = p.parseAsyncFuncExpr()
14801480 } else if !p.prevLT && prec <= OpAssign && (p.tt == OpenParenToken || IsIdentifier(p.tt) || !p.generator && p.tt == YieldToken || p.tt == AwaitToken) {
14811481 // async arrow function expression
14821482 if p.tt == AwaitToken {
14831483 p.fail("arrow function")
14841484 return nil
1485 }
1486 arrowFunc := p.parseAsyncArrowFunc()
1487 left = &arrowFunc
1485 } else if p.tt == OpenParenToken {
1486 return p.parseParenthesizedExpressionOrArrowFunc(prec, async)
1487 }
1488 left = p.parseAsyncArrowFunc()
14881489 precLeft = OpAssign
14891490 } else {
14901491 left = p.scope.Use(async)
14911492 }
1492 left = p.parseExpressionSuffix(left, prec, precLeft)
1493 return left
1493 return p.parseExpressionSuffix(left, prec, precLeft)
14941494 }
14951495
14961496 // parseExpression parses an expression that has a precedence of prec or higher.
15571557 }
15581558 break
15591559 }
1560 suffix := p.parseParenthesizedExpressionOrArrowFunc(prec)
1560 suffix := p.parseParenthesizedExpressionOrArrowFunc(prec, nil)
15611561 p.exprLevel--
15621562 return suffix
15631563 case NotToken, BitNotToken, TypeofToken, VoidToken, DeleteToken:
17021702 case ClassToken:
17031703 parentInFor := p.inFor
17041704 p.inFor = false
1705 classDecl := p.parseClassExpr()
1706 left = &classDecl
1705 left = p.parseClassExpr()
17071706 p.inFor = parentInFor
17081707 case FunctionToken:
17091708 parentInFor := p.inFor
17101709 p.inFor = false
1711 funcDecl := p.parseFuncExpr()
1712 left = &funcDecl
1710 left = p.parseFuncExpr()
17131711 p.inFor = parentInFor
17141712 case TemplateToken, TemplateStartToken:
17151713 parentInFor := p.inFor
20272025 return nil
20282026 }
20292027
2030 arrowFunc := p.parseIdentifierArrowFunc(v)
2031 left = &arrowFunc
2028 left = p.parseIdentifierArrowFunc(v)
20322029 precLeft = OpAssign
20332030 default:
20342031 return left
20612058 return p.parseExpression(OpAssign)
20622059 }
20632060
2064 func (p *Parser) parseParenthesizedExpressionOrArrowFunc(prec OpPrec) IExpr {
2061 func (p *Parser) parseParenthesizedExpressionOrArrowFunc(prec OpPrec, async []byte) IExpr {
20652062 var left IExpr
20662063 precLeft := OpPrimary
20672064
20682065 // expect to be at (
20692066 p.next()
20702067
2071 arrowFunc := ArrowFunc{}
2068 isAsync := async != nil
2069 arrowFunc := &ArrowFunc{}
20722070 parent := p.enterScope(&arrowFunc.Body.Scope, true)
20732071 parentAssumeArrowFunc, parentInFor := p.assumeArrowFunc, p.inFor
20742072 p.assumeArrowFunc, p.inFor = true, false
20752073
2076 // parse a parenthesized expression but assume we might be parsing an arrow function. If this is really an arrow function, parsing as a parenthesized expression cannot fail as AssignmentExpression, ArrayLiteral, and ObjectLiteral are supersets of SingleNameBinding, ArrayBindingPattern, and ObjectBindingPattern respectively. Any identifier that would be a BindingIdentifier in case of an arrow function, will be added as such. If finally this is not an arrow function, we will demote those variables an undeclared and merge them with the parent scope.
2074 // parse a parenthesized expression but assume we might be parsing an (async) arrow function. If this is really an arrow function, parsing as a parenthesized expression cannot fail as AssignmentExpression, ArrayLiteral, and ObjectLiteral are supersets of SingleNameBinding, ArrayBindingPattern, and ObjectBindingPattern respectively. Any identifier that would be a BindingIdentifier in case of an arrow function, will be added as such. If finally this is not an arrow function, we will demote those variables an undeclared and merge them with the parent scope.
20772075
20782076 var list []IExpr
20792077 var rest IExpr
20802078 for p.tt != CloseParenToken && p.tt != ErrorToken {
20812079 if p.tt == EllipsisToken && p.assumeArrowFunc {
20822080 p.next()
2083 if p.isIdentifierReference(p.tt) {
2081 if isAsync {
2082 rest = p.parseAssignmentExpression()
2083 if p.tt == CommaToken {
2084 p.next()
2085 }
2086 } else if p.isIdentifierReference(p.tt) {
20842087 rest, _ = p.scope.Declare(ArgumentDecl, p.data) // cannot fail
20852088 p.next()
20862089 } else if p.tt == OpenBracketToken {
21122115
21132116 if isArrowFunc {
21142117 parentAsync, parentGenerator := p.async, p.generator
2115 p.async, p.generator = false, false
2118 p.async, p.generator = isAsync, false
21162119
21172120 // arrow function
21182121 arrowFunc.Params = Params{List: make([]BindingElement, len(list))}
21192122 for i, item := range list {
21202123 arrowFunc.Params.List[i] = p.exprToBindingElement(item) // can not fail when assumArrowFunc is set
21212124 }
2125 arrowFunc.Async = isAsync
21222126 arrowFunc.Params.Rest = p.exprToBinding(rest)
21232127 arrowFunc.Body.List = p.parseArrowFuncBody()
21242128
21252129 p.async, p.generator = parentAsync, parentGenerator
21262130 p.exitScope(parent)
21272131
2128 left = &arrowFunc
2132 left = arrowFunc
21292133 precLeft = OpAssign
2130 } else if len(list) == 0 || rest != nil {
2134 } else if len(list) == 0 || !isAsync && rest != nil || isAsync && OpCall < prec {
21312135 p.fail("arrow function", ArrowToken)
21322136 return nil
21332137 } else {
21372141 // Here we move all declared ArgumentDecls (in case of an arrow function) to its parent scope as undeclared variables (identifiers used in a parenthesized expression).
21382142 arrowFunc.Body.Scope.UndeclareScope()
21392143
2140 // parenthesized expression
2141 left = list[0]
2142 for _, item := range list[1:] {
2143 left = &BinaryExpr{CommaToken, left, item}
2144 }
2145 left = &GroupExpr{left}
2144 if isAsync {
2145 // call expression
2146 args := Args{}
2147 for _, item := range list {
2148 args.List = append(args.List, Arg{Value: item, Rest: false})
2149 }
2150 if rest != nil {
2151 args.List = append(args.List, Arg{Value: rest, Rest: true})
2152 }
2153 left = p.scope.Use(async)
2154 left = &CallExpr{left, args}
2155 precLeft = OpCall
2156 } else {
2157 // parenthesized expression
2158 left = list[0]
2159 for _, item := range list[1:] {
2160 left = &BinaryExpr{CommaToken, left, item}
2161 }
2162 left = &GroupExpr{left}
2163 }
21462164 }
21472165 return p.parseExpressionSuffix(left, prec, precLeft)
21482166 }
146146 {"async\n= a", "Stmt(async=a)"},
147147 {"async a => b", "Stmt(async Params(Binding(a)) => Stmt({ Stmt(return b) }))"},
148148 {"async (a) => b", "Stmt(async Params(Binding(a)) => Stmt({ Stmt(return b) }))"},
149 {"async(a)", "Stmt(async(a))"},
150 {"async(a=6, ...b)", "Stmt(async((a=6), ...b))"},
151 {"async(function(){})", "Stmt(async(Decl(function Params() Stmt({ }))))"},
149152 {"async\nawait => b", "Stmt(async) Stmt(Params(Binding(await)) => Stmt({ Stmt(return b) }))"},
150153 {"a + async\nb", "Stmt(a+async) Stmt(b)"},
151154 {"a + async\nfunction f(){}", "Stmt(a+async) Decl(function f Params() Stmt({ }))"},
490493 {"x={a", "unexpected EOF in object literal"},
491494 {"x=a[b", "expected ] instead of EOF in index expression"},
492495 {"x=async a", "expected => instead of EOF in arrow function"},
493 {"x=async (a", "unexpected EOF in arrow function"},
494 {"x=async (a,", "unexpected EOF in arrow function"},
496 {"x=async (a", "unexpected EOF in expression"},
497 {"x=async (a,", "unexpected EOF in expression"},
495498 {"x=async function", "expected Identifier or ( instead of EOF in function declaration"},
496499 {"x=async function *", "expected Identifier or ( instead of EOF in function declaration"},
497500 {"x=async function a", "expected ( instead of EOF in function declaration"},
548551 {"function*a(){ (yield=5) => yield }", "unexpected = in expression"},
549552 {"function*a(){ (...yield) => yield }", "unexpected yield in arrow function"},
550553 {"x = await\n=> a++", "unexpected => in expression"},
551 {"x=async (await,", "unexpected await in binding"},
554 {"x=async (await,", "unexpected EOF in expression"},
552555 {"async function a() { class a extends await", "unexpected await in expression"},
553556 {"async function a() { await: var a", "unexpected : in expression"},
554557 {"async function a() { let await", "unexpected await in binding"},
841844 case *ThrowStmt:
842845 sv.AddExpr(stmt.Value)
843846 case *ForStmt:
844 sv.AddStmt(&stmt.Body)
847 sv.AddStmt(stmt.Body)
845848 case *ForInStmt:
846 sv.AddStmt(&stmt.Body)
849 sv.AddStmt(stmt.Body)
847850 case *ForOfStmt:
848 sv.AddStmt(&stmt.Body)
851 sv.AddStmt(stmt.Body)
849852 case *IfStmt:
850853 sv.AddStmt(stmt.Body)
851854 if stmt.Else != nil {
853856 }
854857 case *TryStmt:
855858 if 0 < len(stmt.Body.List) {
856 sv.AddStmt(&stmt.Body)
859 sv.AddStmt(stmt.Body)
857860 }
858861 if stmt.Catch != nil {
859862 sv.AddStmt(stmt.Catch)
0 # JSON [![GoDoc](http://godoc.org/github.com/tdewolff/parse/json?status.svg)](http://godoc.org/github.com/tdewolff/parse/json)
0 # JSON [![API reference](https://img.shields.io/badge/godoc-reference-5272B4)](https://pkg.go.dev/github.com/tdewolff/minify/v2/parse/json?tab=doc)
11
22 This package is a JSON lexer (ECMA-404) written in [Go][1]. It follows the specification at [JSON](http://json.org/). The lexer takes an io.Reader and converts it into tokens until the EOF.
33
44 To run the tests, install `go-fuzz`:
55
66 ```
7 GO111MODULE=off go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build
7 go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build
88
99 cd $GOPATH/github.com/tdewolff/parse/tests/number
1010
1111 go-fuzz-build
12 go-fuzz
12 go-fuzz -bin fuzz-fuzz.zip
1313 ```
1414
1515 If restarts is not close to `1/10000`, something is probably wrong. If not finding new corpus for a while, restart the fuzzer.
+0
-10
tests/css-token/go.mod less more
0 module github.com/tdewolff/parse/tests/css-token
1
2 go 1.13
3
4 replace github.com/tdewolff/parse/v2 => ../../../parse
5
6 require (
7 github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813 // indirect
8 github.com/tdewolff/parse/v2 v2.4.3
9 )
+0
-6
tests/css-token/go.sum less more
0 github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813 h1:NgO45/5mBLRVfiXerEFzH6ikcZ7DNRPS639xFg3ENzU=
1 github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
2 github.com/tdewolff/parse/v2 v2.4.3 h1:k24zHgTRGm7LkvbTEreuavyZTf0k8a/lIenggv62OiU=
3 github.com/tdewolff/parse/v2 v2.4.3/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
4 github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
5 github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
+0
-10
tests/data-uri/go.mod less more
0 module github.com/tdewolff/parse/tests/data-uri
1
2 go 1.13
3
4 replace github.com/tdewolff/parse/v2 => ../../../parse
5
6 require (
7 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681 // indirect
8 github.com/tdewolff/parse/v2 v2.3.10
9 )
+0
-7
tests/data-uri/go.sum less more
0 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681 h1:3WV5aRRj1ELP3RcLlBp/v0WJTuy47OQMkL9GIQq8QEE=
1 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
2 github.com/tdewolff/parse/v2 v2.3.10 h1:ipN/RjAeVaX7d3yQ+pKVeXKlTZOOEoWVQoC5UOrhPEY=
3 github.com/tdewolff/parse/v2 v2.3.10/go.mod h1:pclWRpgD95an4pJvzjbp1A+bl6e7R9DemblveSm/Zo4=
4 github.com/tdewolff/test v1.0.4 h1:ih38SXuQJ32Hng5EtSW32xqEsVeMnPp6nNNRPhBBDE8=
5 github.com/tdewolff/test v1.0.4/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
6 github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
+0
-10
tests/dimension/go.mod less more
0 module github.com/tdewolff/parse/tests/dimension
1
2 go 1.13
3
4 replace github.com/tdewolff/parse/v2 => ../../../parse
5
6 require (
7 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681 // indirect
8 github.com/tdewolff/parse/v2 v2.3.10
9 )
+0
-7
tests/dimension/go.sum less more
0 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681 h1:3WV5aRRj1ELP3RcLlBp/v0WJTuy47OQMkL9GIQq8QEE=
1 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
2 github.com/tdewolff/parse/v2 v2.3.10 h1:ipN/RjAeVaX7d3yQ+pKVeXKlTZOOEoWVQoC5UOrhPEY=
3 github.com/tdewolff/parse/v2 v2.3.10/go.mod h1:pclWRpgD95an4pJvzjbp1A+bl6e7R9DemblveSm/Zo4=
4 github.com/tdewolff/test v1.0.4 h1:ih38SXuQJ32Hng5EtSW32xqEsVeMnPp6nNNRPhBBDE8=
5 github.com/tdewolff/test v1.0.4/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
6 github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
+0
-10
tests/mediatype/go.mod less more
0 module github.com/tdewolff/parse/tests/mediatype
1
2 go 1.13
3
4 replace github.com/tdewolff/parse/v2 => ../../../parse
5
6 require (
7 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681 // indirect
8 github.com/tdewolff/parse/v2 v2.3.10
9 )
+0
-7
tests/mediatype/go.sum less more
0 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681 h1:3WV5aRRj1ELP3RcLlBp/v0WJTuy47OQMkL9GIQq8QEE=
1 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
2 github.com/tdewolff/parse/v2 v2.3.10 h1:ipN/RjAeVaX7d3yQ+pKVeXKlTZOOEoWVQoC5UOrhPEY=
3 github.com/tdewolff/parse/v2 v2.3.10/go.mod h1:pclWRpgD95an4pJvzjbp1A+bl6e7R9DemblveSm/Zo4=
4 github.com/tdewolff/test v1.0.4 h1:ih38SXuQJ32Hng5EtSW32xqEsVeMnPp6nNNRPhBBDE8=
5 github.com/tdewolff/test v1.0.4/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
6 github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
+0
-10
tests/number/go.mod less more
0 module github.com/tdewolff/parse/tests/number
1
2 go 1.13
3
4 replace github.com/tdewolff/parse/v2 => ../../../parse
5
6 require (
7 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681 // indirect
8 github.com/tdewolff/parse/v2 v2.3.10
9 )
+0
-7
tests/number/go.sum less more
0 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681 h1:3WV5aRRj1ELP3RcLlBp/v0WJTuy47OQMkL9GIQq8QEE=
1 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
2 github.com/tdewolff/parse/v2 v2.3.10 h1:ipN/RjAeVaX7d3yQ+pKVeXKlTZOOEoWVQoC5UOrhPEY=
3 github.com/tdewolff/parse/v2 v2.3.10/go.mod h1:pclWRpgD95an4pJvzjbp1A+bl6e7R9DemblveSm/Zo4=
4 github.com/tdewolff/test v1.0.4 h1:ih38SXuQJ32Hng5EtSW32xqEsVeMnPp6nNNRPhBBDE8=
5 github.com/tdewolff/test v1.0.4/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
6 github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
+0
-10
tests/replace-entities/go.mod less more
0 module github.com/tdewolff/parse/tests/replace-entities
1
2 go 1.13
3
4 replace github.com/tdewolff/parse/v2 => ../../../parse
5
6 require (
7 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681 // indirect
8 github.com/tdewolff/parse/v2 v2.3.10
9 )
+0
-7
tests/replace-entities/go.sum less more
0 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681 h1:3WV5aRRj1ELP3RcLlBp/v0WJTuy47OQMkL9GIQq8QEE=
1 github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
2 github.com/tdewolff/parse/v2 v2.3.10 h1:ipN/RjAeVaX7d3yQ+pKVeXKlTZOOEoWVQoC5UOrhPEY=
3 github.com/tdewolff/parse/v2 v2.3.10/go.mod h1:pclWRpgD95an4pJvzjbp1A+bl6e7R9DemblveSm/Zo4=
4 github.com/tdewolff/test v1.0.4 h1:ih38SXuQJ32Hng5EtSW32xqEsVeMnPp6nNNRPhBBDE8=
5 github.com/tdewolff/test v1.0.4/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
6 github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
0 # XML [![GoDoc](http://godoc.org/github.com/tdewolff/parse/xml?status.svg)](http://godoc.org/github.com/tdewolff/parse/xml)
0 # XML [![API reference](https://img.shields.io/badge/godoc-reference-5272B4)](https://pkg.go.dev/github.com/tdewolff/parse/v2/xml?tab=doc)
11
22 This package is an XML lexer written in [Go][1]. It follows the specification at [Extensible Markup Language (XML) 1.0 (Fifth Edition)](http://www.w3.org/TR/REC-xml/). The lexer takes an io.Reader and converts it into tokens until the EOF.
33
2626 if len(b) > 1 && (b[0] == '"' || b[0] == '\'') && b[0] == b[len(b)-1] {
2727 b = b[1 : len(b)-1]
2828 }
29 val := EscapeAttrVal(&buf, []byte(b))
29 val := EscapeAttrVal(&buf, b)
3030 test.String(t, string(val), tt.expected)
3131 })
3232 }