github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/yaml/parser/parser_test.go (about)

     1  package parser_test
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/bingoohuang/gg/pkg/yaml/ast"
    10  	"github.com/bingoohuang/gg/pkg/yaml/lexer"
    11  	"github.com/bingoohuang/gg/pkg/yaml/parser"
    12  )
    13  
    14  func TestParser(t *testing.T) {
    15  	sources := []string{
    16  		"null\n",
    17  		"{}\n",
    18  		"v: hi\n",
    19  		"v: \"true\"\n",
    20  		"v: \"false\"\n",
    21  		"v: true\n",
    22  		"v: false\n",
    23  		"v: 10\n",
    24  		"v: -10\n",
    25  		"v: 42\n",
    26  		"v: 4294967296\n",
    27  		"v: \"10\"\n",
    28  		"v: 0.1\n",
    29  		"v: 0.99\n",
    30  		"v: -0.1\n",
    31  		"v: .inf\n",
    32  		"v: -.inf\n",
    33  		"v: .nan\n",
    34  		"v: null\n",
    35  		"v: \"\"\n",
    36  		"v:\n- A\n- B\n",
    37  		"a: '-'\n",
    38  		"123\n",
    39  		"hello: world\n",
    40  		"a: null\n",
    41  		"v:\n- A\n- 1\n- B:\n  - 2\n  - 3\n",
    42  		"a:\n  b: c\n",
    43  		"a: {x: 1}\n",
    44  		"t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n",
    45  		"a: [1, 2]\n",
    46  		"a: {b: c, d: e}\n",
    47  		"a: 3s\n",
    48  		"a: <foo>\n",
    49  		"a: \"1:1\"\n",
    50  		"a: 1.2.3.4\n",
    51  		"a: \"2015-02-24T18:19:39Z\"\n",
    52  		"a: 'b: c'\n",
    53  		"a: 'Hello #comment'\n",
    54  		"a: abc <<def>> ghi",
    55  		"a: <<abcd",
    56  		"a: <<:abcd",
    57  		"a: <<  :abcd",
    58  		"a: 100.5\n",
    59  		"a: bogus\n",
    60  		"a: \"\\0\"\n",
    61  		"b: 2\na: 1\nd: 4\nc: 3\nsub:\n  e: 5\n",
    62  		"       a       :          b        \n",
    63  		"a: b # comment\nb: c\n",
    64  		"---\na: b\n",
    65  		"a: b\n...\n",
    66  		"%YAML 1.2\n---\n",
    67  		"a: !!binary gIGC\n",
    68  		"a: !!binary |\n  " + strings.Repeat("kJCQ", 17) + "kJ\n  CQ\n",
    69  		"- !tag\n  a: b\n  c: d\n",
    70  		"v:\n- A\n- |-\n  B\n  C\n",
    71  		"v:\n- A\n- >-\n  B\n  C\n",
    72  		"v: |-\n  0\n",
    73  		"v: |-\n  0\nx: 0",
    74  		`"a\n1\nb"`,
    75  		`{"a":"b"}`,
    76  		`!!map {
    77    ? !!str "explicit":!!str "entry",
    78    ? !!str "implicit" : !!str "entry",
    79    ? !!null "" : !!null "",
    80  }`,
    81  	}
    82  	for _, src := range sources {
    83  		if _, err := parser.Parse(lexer.Tokenize(src), 0); err != nil {
    84  			t.Fatalf("parse error: source [%s]: %+v", src, err)
    85  		}
    86  	}
    87  }
    88  
    89  func TestParseComplicatedDocument(t *testing.T) {
    90  	tests := []struct {
    91  		source string
    92  		expect string
    93  	}{
    94  		{
    95  			`
    96  american:
    97    - Boston Red Sox
    98    - Detroit Tigers
    99    - New York Yankees
   100  national:
   101    - New York Mets
   102    - Chicago Cubs
   103    - Atlanta Braves
   104  `, `
   105  american:
   106    - Boston Red Sox
   107    - Detroit Tigers
   108    - New York Yankees
   109  national:
   110    - New York Mets
   111    - Chicago Cubs
   112    - Atlanta Braves
   113  `,
   114  		},
   115  		{
   116  			`
   117  a:
   118    b: c
   119    d: e
   120    f: g
   121  h:
   122    i: j
   123    k:
   124      l: m
   125      n: o
   126    p: q
   127  r: s
   128  `, `
   129  a:
   130    b: c
   131    d: e
   132    f: g
   133  h:
   134    i: j
   135    k:
   136      l: m
   137      n: o
   138    p: q
   139  r: s
   140  `,
   141  		},
   142  		{
   143  			`
   144  - a:
   145    - b
   146    - c
   147  - d
   148  `, `
   149  - a:
   150    - b
   151    - c
   152  - d
   153  `,
   154  		},
   155  		{
   156  			`
   157  - a
   158  - b
   159  - c
   160   - d
   161   - e
   162  - f
   163  `, `
   164  - a
   165  - b
   166  - c - d - e
   167  - f
   168  `,
   169  		},
   170  		{
   171  			`
   172  a: 0 - 1
   173  `,
   174  			`
   175  a: 0 - 1
   176  `,
   177  		},
   178  		{
   179  			`
   180  - a:
   181     b: c
   182     d: e
   183  - f:
   184    g: h
   185  `,
   186  			`
   187  - a:
   188     b: c
   189     d: e
   190  - f: null
   191    g: h
   192  `,
   193  		},
   194  		{
   195  			`
   196  a:
   197   b
   198   c
   199  d: e
   200  `, `
   201  a: b c
   202  d: e
   203  `,
   204  		},
   205  		{
   206  			`
   207  a
   208  b
   209  c
   210  `, `
   211  a b c
   212  `,
   213  		},
   214  		{
   215  			`
   216  a:
   217   - b
   218   - c
   219  `, `
   220  a:
   221   - b
   222   - c
   223  `,
   224  		},
   225  		{
   226  			`
   227  -     a     :
   228        b: c
   229  `, `
   230  - a: null
   231    b: c
   232  `,
   233  		},
   234  		{
   235  			`
   236  - a:
   237     b
   238     c
   239     d
   240    hoge: fuga
   241  `, `
   242  - a: b c d
   243    hoge: fuga
   244  `,
   245  		},
   246  		{
   247  			`
   248  - a # ' " # - : %
   249  - b # " # - : % '
   250  - c # # - : % ' "
   251  - d # - : % ' " #
   252  - e # : % ' " # -
   253  - f # % ' : # - :
   254  `,
   255  			`
   256  - a
   257  - b
   258  - c
   259  - d
   260  - e
   261  - f
   262  `,
   263  		},
   264  		{
   265  			`
   266  # comment
   267  a: # comment
   268  # comment
   269   b: c # comment
   270   # comment
   271  d: e # comment
   272  # comment
   273  `,
   274  			`
   275  a:
   276   b: c
   277  d: e
   278  `,
   279  		},
   280  		{
   281  			`
   282  a: b#notcomment
   283  `,
   284  			`
   285  a: b#notcomment
   286  `,
   287  		},
   288  		{
   289  			`
   290  anchored: &anchor foo
   291  aliased: *anchor
   292  `,
   293  			`
   294  anchored: &anchor foo
   295  aliased: *anchor
   296  `,
   297  		},
   298  		{
   299  			`
   300  ---
   301  - &CENTER { x: 1, y: 2 }
   302  - &LEFT { x: 0, y: 2 }
   303  - &BIG { r: 10 }
   304  - &SMALL { r: 1 }
   305  
   306  # All the following maps are equal:
   307  
   308  - # Explicit keys
   309    x: 1
   310    y: 2
   311    r: 10
   312    label: center/big
   313  
   314  - # Merge one map
   315    << : *CENTER
   316    r: 10
   317    label: center/big
   318  
   319  - # Merge multiple maps
   320    << : [ *CENTER, *BIG ]
   321    label: center/big
   322  
   323  - # Override
   324    << : [ *BIG, *LEFT, *SMALL ]
   325    x: 1
   326    label: center/big
   327  `,
   328  			`
   329  ---
   330  - &CENTER {x: 1, y: 2}
   331  - &LEFT {x: 0, y: 2}
   332  - &BIG {r: 10}
   333  - &SMALL {r: 1}
   334  - x: 1
   335    y: 2
   336    r: 10
   337    label: center/big
   338  - <<: *CENTER
   339    r: 10
   340    label: center/big
   341  - <<: [*CENTER, *BIG]
   342    label: center/big
   343  - <<: [*BIG, *LEFT, *SMALL]
   344    x: 1
   345    label: center/big
   346  `,
   347  		},
   348  		{
   349  			`
   350  a:
   351  - - b
   352  - - c
   353    - d
   354  `,
   355  			`
   356  a:
   357  - - b
   358  - - c
   359    - d
   360  `,
   361  		},
   362  		{
   363  			`
   364  a:
   365    b:
   366      c: d
   367    e:
   368      f: g
   369      h: i
   370  j: k
   371  `,
   372  			`
   373  a:
   374    b:
   375      c: d
   376    e:
   377      f: g
   378      h: i
   379  j: k
   380  `,
   381  		},
   382  		{
   383  			`
   384  ---
   385  a: 1
   386  b: 2
   387  ...
   388  ---
   389  c: 3
   390  d: 4
   391  ...
   392  `,
   393  			`
   394  ---
   395  a: 1
   396  b: 2
   397  ...
   398  ---
   399  c: 3
   400  d: 4
   401  ...
   402  `,
   403  		},
   404  		{
   405  			`
   406  a:
   407    b: |
   408      {
   409        [ 1, 2 ]
   410      }
   411    c: d
   412  `,
   413  			`
   414  a:
   415    b: |
   416      {
   417        [ 1, 2 ]
   418      }
   419    c: d
   420  `,
   421  		},
   422  		{
   423  			`
   424  |
   425      hoge
   426      fuga
   427      piyo`,
   428  			`
   429  |
   430      hoge
   431      fuga
   432      piyo
   433  `,
   434  		},
   435  		{
   436  			`
   437  a: |
   438     bbbbbbb
   439  
   440  
   441     ccccccc
   442  d: eeeeeeeeeeeeeeeee
   443  `,
   444  			`
   445  a: |
   446     bbbbbbb
   447  
   448  
   449     ccccccc
   450  d: eeeeeeeeeeeeeeeee
   451  `,
   452  		},
   453  		{
   454  			`
   455  a: b    
   456    c
   457  `,
   458  			`
   459  a: b c
   460  `,
   461  		},
   462  		{
   463  			`
   464  a:    
   465    b: c
   466  `,
   467  			`
   468  a:
   469    b: c
   470  `,
   471  		},
   472  		{
   473  			`
   474  a: b    
   475  c: d
   476  `,
   477  			`
   478  a: b
   479  c: d
   480  `,
   481  		},
   482  		{
   483  			`
   484  - ab - cd
   485  - ef - gh
   486  `,
   487  			`
   488  - ab - cd
   489  - ef - gh
   490  `,
   491  		},
   492  		{
   493  			`
   494  - 0 - 1
   495   - 2 - 3
   496  `,
   497  			`
   498  - 0 - 1 - 2 - 3
   499  `,
   500  		},
   501  		{
   502  			`
   503  a - b - c: value
   504  `,
   505  			`
   506  a - b - c: value
   507  `,
   508  		},
   509  		{
   510  			`
   511  a:
   512  -
   513    b: c
   514    d: e
   515  -
   516    f: g
   517    h: i
   518  `,
   519  			`
   520  a:
   521  - b: c
   522    d: e
   523  - f: g
   524    h: i
   525  `,
   526  		},
   527  		{
   528  			`
   529  a: |-
   530    value
   531  b: c
   532  `,
   533  			`
   534  a: |-
   535    value
   536  b: c
   537  `,
   538  		},
   539  		{
   540  			`
   541  a:  |+
   542    value
   543  b: c
   544  `,
   545  			`
   546  a: |+
   547    value
   548  b: c
   549  `,
   550  		},
   551  	}
   552  
   553  	for _, test := range tests {
   554  		tokens := lexer.Tokenize(test.source)
   555  		f, err := parser.Parse(tokens, 0)
   556  		if err != nil {
   557  			t.Fatalf("%+v", err)
   558  		}
   559  		var v Visitor
   560  		for _, doc := range f.Docs {
   561  			ast.Walk(&v, doc.Body)
   562  		}
   563  		expect := fmt.Sprintf("\n%+v\n", f)
   564  		if test.expect != expect {
   565  			tokens.Dump()
   566  			t.Fatalf("unexpected output: [%s] != [%s]", test.expect, expect)
   567  		}
   568  	}
   569  }
   570  
   571  func TestNewLineChar(t *testing.T) {
   572  	for _, f := range []string{
   573  		"lf.yml",
   574  		"cr.yml",
   575  		"crlf.yml",
   576  	} {
   577  		ast, err := parser.ParseFile(filepath.Join("testdata", f), 0)
   578  		if err != nil {
   579  			t.Fatalf("%+v", err)
   580  		}
   581  		actual := fmt.Sprintf("%v\n", ast)
   582  		expect := `a: "a"
   583  b: 1
   584  `
   585  		if expect != actual {
   586  			t.Fatal("unexpected result")
   587  		}
   588  	}
   589  }
   590  
   591  func TestSyntaxError(t *testing.T) {
   592  	tests := []struct {
   593  		source string
   594  		expect string
   595  	}{
   596  		{
   597  			`
   598  a:
   599  - b
   600    c: d
   601    e: f
   602    g: h`,
   603  			`
   604  [3:3] unexpected key name
   605     2 | a:
   606  >  3 | - b
   607     4 |   c: d
   608           ^
   609     5 |   e: f
   610     6 |   g: h`,
   611  		},
   612  		{
   613  			`
   614  a
   615  - b: c`,
   616  			`
   617  [2:1] unexpected key name
   618  >  2 | a
   619     3 | - b: c
   620         ^
   621  `,
   622  		},
   623  		{
   624  			`%YAML 1.1 {}`,
   625  			`
   626  [1:2] unexpected directive value. document not started
   627  >  1 | %YAML 1.1 {}
   628          ^
   629  `,
   630  		},
   631  		{
   632  			`{invalid`,
   633  			`
   634  [1:2] unexpected map
   635  >  1 | {invalid
   636          ^
   637  `,
   638  		},
   639  		{
   640  			`{ "key": "value" `,
   641  			`
   642  [1:1] unterminated flow mapping
   643  >  1 | { "key": "value"
   644         ^
   645  `,
   646  		},
   647  	}
   648  	for _, test := range tests {
   649  		t.Run(test.source, func(t *testing.T) {
   650  			_, err := parser.ParseBytes([]byte(test.source), 0)
   651  			if err == nil {
   652  				t.Fatal("cannot catch syntax error")
   653  			}
   654  			actual := "\n" + err.Error()
   655  			if test.expect != actual {
   656  				t.Fatalf("expected: [%s] but got [%s]", test.expect, actual)
   657  			}
   658  		})
   659  	}
   660  }
   661  
   662  func TestComment(t *testing.T) {
   663  	tests := []struct {
   664  		name string
   665  		yaml string
   666  	}{
   667  		{
   668  			name: "map with comment",
   669  			yaml: `
   670  # commentA
   671  a: #commentB
   672    # commentC
   673    b: c # commentD
   674    # commentE
   675    d: e # commentF
   676    # commentG
   677    f: g # commentH
   678  # commentI
   679  f: g # commentJ
   680  # commentK
   681  `,
   682  		},
   683  		{
   684  			name: "sequence with comment",
   685  			yaml: `
   686  # commentA
   687  - a # commentB
   688  # commentC
   689  - b: # commentD
   690    # commentE
   691    - d # commentF
   692    - e # commentG
   693  # commentH
   694  `,
   695  		},
   696  		{
   697  			name: "anchor and alias",
   698  			yaml: `
   699  a: &x b # commentA
   700  c: *x # commentB
   701  `,
   702  		},
   703  		{
   704  			name: "multiline",
   705  			yaml: `
   706  # foo comment
   707  # foo comment2
   708  foo: # map key comment
   709    # bar above comment
   710    # bar above comment2
   711    bar: 10 # comment for bar
   712    # baz above comment
   713    # baz above comment2
   714    baz: bbbb # comment for baz
   715    piyo: # sequence key comment
   716    # sequence1 above comment 1
   717    # sequence1 above comment 2
   718    - sequence1 # sequence1
   719    # sequence2 above comment 1
   720    # sequence2 above comment 2
   721    - sequence2 # sequence2
   722    # sequence3 above comment 1
   723    # sequence3 above comment 2
   724    - false # sequence3
   725  # foo2 comment
   726  # foo2 comment2
   727  foo2: &anchor text # anchor comment
   728  # foo3 comment
   729  # foo3 comment2
   730  foo3: *anchor # alias comment
   731  `,
   732  		},
   733  		{
   734  			name: "literal",
   735  			yaml: `
   736  foo: | # comment
   737    x: 42
   738  `,
   739  		},
   740  		{
   741  			name: "folded",
   742  			yaml: `
   743  foo: > # comment
   744    x: 42
   745  `,
   746  		},
   747  	}
   748  	for _, test := range tests {
   749  		t.Run(test.name, func(t *testing.T) {
   750  			f, err := parser.ParseBytes([]byte(test.yaml), parser.ParseComments)
   751  			if err != nil {
   752  				t.Fatalf("%+v", err)
   753  			}
   754  			got := "\n" + f.String() + "\n"
   755  			if test.yaml != got {
   756  				t.Fatalf("expected:%s\ngot:%s", test.yaml, got)
   757  			}
   758  		})
   759  	}
   760  }
   761  
   762  type Visitor struct{}
   763  
   764  func (v *Visitor) Visit(node ast.Node) ast.Visitor {
   765  	tk := node.GetToken()
   766  	tk.Prev = nil
   767  	tk.Next = nil
   768  	return v
   769  }