github.com/jmigpin/editor@v1.6.0/util/parseutil/lrparser/lrparser_test.go (about)

     1  package lrparser
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"go/ast"
     7  	"go/parser"
     8  	"go/token"
     9  	"os"
    10  	"os/exec"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/jmigpin/editor/util/astut"
    15  	"github.com/jmigpin/editor/util/testutil"
    16  	"golang.org/x/tools/go/ast/astutil"
    17  )
    18  
    19  func TestLrparser1(t *testing.T) {
    20  	gram := `
    21  		^S = C C;
    22  		C = "c" C | "d";
    23  	`
    24  	in := "●ccdd"
    25  	out := `
    26  		-> ^S: "ccdd"
    27  			-> C: "ccd"
    28  				-> "c": "c"
    29  				-> C: "cd"
    30  					-> "c": "c"
    31  					-> C: "d"
    32  						-> "d": "d"
    33  			-> C: "d"
    34  				-> "d": "d"
    35  `
    36  	testLrparserMode1(t, gram, in, out)
    37  }
    38  func TestLrparser2(t *testing.T) {
    39  	gram := `
    40  		^id = "a" id | "a";
    41  	`
    42  	in := "●aaa"
    43  	out := `
    44  		-> ^id: "aaa"
    45  			-> "a": "a"
    46  			-> ^id: "aa"
    47  				-> "a": "a"
    48  				-> ^id: "a"
    49  					-> "a": "a"
    50  `
    51  	testLrparserMode1(t, gram, in, out)
    52  }
    53  func TestLrparser3(t *testing.T) {
    54  	gram := `
    55  		^id = id "a" | "a";
    56  	`
    57  	in := "●aaa"
    58  	out := `
    59  		-> ^id: "aaa"
    60  			-> ^id: "aa"
    61  				-> ^id: "a"
    62  					-> "a": "a"
    63  				-> "a": "a"
    64  			-> "a": "a"
    65  `
    66  	testLrparserMode1(t, gram, in, out)
    67  }
    68  func TestLrparser4(t *testing.T) {
    69  	gram := `
    70  		^id = (digit)?;
    71  	`
    72  	in := "●1"
    73  	out := `
    74  		-> ^id: "1"
    75  			-> (digit)?: "1"
    76  				-> digit: "1"
    77  `
    78  	testLrparserMode1(t, gram, in, out)
    79  }
    80  func TestLrparser5(t *testing.T) {
    81  	gram := `
    82  		#^id = letter id2 letter;
    83  		#id2 = digit | nil;
    84  		^id = letter (digit)? letter;	
    85  	`
    86  	in := "●aa"
    87  	out := `
    88  		-> ^id: "aa"
    89  			-> letter: "a"
    90  			-> (digit)?: ""
    91  			-> letter: "a"
    92  `
    93  	testLrparserMode1(t, gram, in, out)
    94  }
    95  func TestLrparser6(t *testing.T) {
    96  	gram := `		
    97  		^id = letter (letter|digit)* digit;
    98  	`
    99  	in := "●a11"
   100  	out := `
   101  		-> ^id: "a11"
   102  			-> letter: "a"
   103  			-> ([letter|digit])*: "1"
   104  				-> ([letter|digit])*: ""
   105  				-> [letter|digit]: "1"
   106  					-> digit: "1"
   107  			-> digit: "1"
   108  `
   109  	testLrparserMode1(t, gram, in, out)
   110  }
   111  func TestLrparser7(t *testing.T) {
   112  	gram := `
   113  		^id = (letter|digit)*;
   114  	`
   115  	in := "●a1"
   116  	out := `
   117  		-> ^id: "a1"
   118  			-> ([letter|digit])*: "a1"
   119  				-> ([letter|digit])*: "a"
   120  					-> ([letter|digit])*: ""
   121  					-> [letter|digit]: "a"
   122  						-> letter: "a"
   123  				-> [letter|digit]: "1"
   124  					-> digit: "1"
   125  `
   126  	testLrparserMode1(t, gram, in, out)
   127  }
   128  func TestLrparser7b(t *testing.T) {
   129  	gram := `
   130  		^id = (letter|digit)+;
   131  	`
   132  	in := "●a1"
   133  	out := `
   134  		-> ^id: "a1"
   135  			-> ([letter|digit])+: "a1"
   136  				-> ([letter|digit])*: "a"
   137  					-> ([letter|digit])*: ""
   138  					-> [letter|digit]: "a"
   139  						-> letter: "a"
   140  				-> [letter|digit]: "1"
   141  					-> digit: "1"
   142  `
   143  	testLrparserMode1(t, gram, in, out)
   144  }
   145  func TestLrparser8(t *testing.T) {
   146  	gram := `
   147  		#^S = "a" ("a"|"1")*;
   148  		
   149  		^S = "a" s2;
   150  		s2 = "a" s2 | "1" s2 | nil;
   151  	`
   152  	in := "●aa1"
   153  	out := `
   154  		-> ^S: "aa1"
   155  			-> "a": "a"
   156  			-> s2: "a1"
   157  				-> "a": "a"
   158  				-> s2: "1"
   159  					-> "1": "1"
   160  					-> s2: ""
   161  `
   162  	testLrparserMode1(t, gram, in, out)
   163  }
   164  func TestLrparser9a(t *testing.T) {
   165  	gram := `
   166  		^S = letter (letter)*;
   167  	`
   168  	in := "●aaa"
   169  	out := `
   170  		-> ^S: "aaa"
   171  			-> letter: "a"
   172  			-> (letter)*: "aa"
   173  				-> (letter)*: "a"
   174  					-> (letter)*: ""
   175  					-> letter: "a"
   176  				-> letter: "a"
   177  `
   178  	testLrparserMode1(t, gram, in, out)
   179  }
   180  func TestLrparser9b(t *testing.T) {
   181  	gram := `
   182  		^S = letter (letter)*;
   183  	`
   184  	in := "●a"
   185  	out := `
   186  		-> ^S: "a"
   187  			-> letter: "a"
   188  			-> (letter)*: ""
   189  `
   190  	testLrparserMode1(t, gram, in, out)
   191  }
   192  func TestLrparser9c(t *testing.T) {
   193  	gram := `
   194  		^S = letter (letter)+;
   195  	`
   196  	in := "●aaaa"
   197  	out := `
   198  		-> ^S: "aaaa"
   199  			-> letter: "a"
   200  			-> (letter)+: "aaa"
   201  				-> (letter)*: "aa"
   202  					-> (letter)*: "a"
   203  						-> (letter)*: ""
   204  						-> letter: "a"
   205  					-> letter: "a"
   206  				-> letter: "a"
   207  `
   208  	testLrparserMode1(t, gram, in, out)
   209  }
   210  func TestLrparser10(t *testing.T) {
   211  	gram := `
   212  		^S = (letter|digit)?;
   213  	`
   214  	in := "●1"
   215  	out := `
   216  		-> ^S: "1"
   217  			-> ([letter|digit])?: "1"
   218  				-> [letter|digit]: "1"
   219  					-> digit: "1"
   220  `
   221  	testLrparserMode1(t, gram, in, out)
   222  }
   223  func TestLrparser11(t *testing.T) {
   224  	gram := `
   225  		^S = (letter digit)+;
   226  	`
   227  	in := "●a1b2c3"
   228  	out := `
   229  		-> ^S: "a1b2c3"
   230  			-> ([letter digit])+: "a1b2c3"
   231  				-> ([letter digit])*: "a1b2"
   232  					-> ([letter digit])*: "a1"
   233  						-> ([letter digit])*: ""
   234  						-> [letter digit]: "a1"
   235  							-> letter: "a"
   236  							-> digit: "1"
   237  					-> [letter digit]: "b2"
   238  						-> letter: "b"
   239  						-> digit: "2"
   240  				-> [letter digit]: "c3"
   241  					-> letter: "c"
   242  					-> digit: "3"
   243  `
   244  	testLrparserMode1(t, gram, in, out)
   245  }
   246  func TestLrparser11b(t *testing.T) {
   247  	gram := `
   248  		^S = (letter digit)*;
   249  	`
   250  	in := "●a1b2c3"
   251  	out := `
   252  		-> ^S: "a1b2c3"
   253  			-> ([letter digit])*: "a1b2c3"
   254  				-> ([letter digit])*: "a1b2"
   255  					-> ([letter digit])*: "a1"
   256  						-> ([letter digit])*: ""
   257  						-> [letter digit]: "a1"
   258  							-> letter: "a"
   259  							-> digit: "1"
   260  					-> [letter digit]: "b2"
   261  						-> letter: "b"
   262  						-> digit: "2"
   263  				-> [letter digit]: "c3"
   264  					-> letter: "c"
   265  					-> digit: "3"
   266  `
   267  	testLrparserMode1(t, gram, in, out)
   268  }
   269  func TestLrparser11c(t *testing.T) {
   270  	gram := `
   271  		^S = letter digit;
   272  	`
   273  	in := "●a1"
   274  	out := `
   275  		-> ^S: "a1"
   276  			-> letter: "a"
   277  			-> digit: "1"
   278  `
   279  	testLrparserMode1(t, gram, in, out)
   280  }
   281  func TestLrparser12(t *testing.T) {
   282  	gram := `
   283  		^S = ((":\"'")%)+;
   284  	`
   285  	in := "●:\":"
   286  	out := `
   287  		-> ^S: ":\":"
   288  	        	-> (":\"'"%)+: ":\":"
   289  	        		-> (":\"'"%)*: ":\""
   290  	        			-> (":\"'"%)*: ":"
   291  	        				-> (":\"'"%)*: ""
   292  	        				-> ":\"'"%: ":"
   293  	        			-> ":\"'"%: "\""
   294  	        		-> ":\"'"%: ":"
   295  `
   296  	testLrparserMode1(t, gram, in, out)
   297  }
   298  func TestLrparser13(t *testing.T) {
   299  	gram := `
   300  		^S = letter (letter|digit)* digit;
   301  	`
   302  	in := "●aa11"
   303  	out := `
   304  		-> ^S: "aa11"
   305  			-> letter: "a"
   306  			-> ([letter|digit])*: "a1"
   307  				-> ([letter|digit])*: "a"
   308  					-> ([letter|digit])*: ""
   309  					-> [letter|digit]: "a"
   310  						-> letter: "a"
   311  				-> [letter|digit]: "1"
   312  					-> digit: "1"
   313  			-> digit: "1"
   314  `
   315  	testLrparserMode1(t, gram, in, out)
   316  }
   317  func TestLrparser14(t *testing.T) {
   318  	gram := `
   319  		// reduce/reduce conflict
   320  		^S = (s2)+; 
   321  		s2 = (letter)+;		
   322  	`
   323  	in := "●aa"
   324  	out := `
   325  	`
   326  	// shift/reduce conflict: either make
   327  	// - several s2, each with one letter
   328  	// - or one s2 with many letters
   329  	//testLrparserMode1(t, gram, in, out) // conflict
   330  
   331  	// use shift by default
   332  	_, err := testLrparserMode3(t, gram, in, out, false, false, true)
   333  	if err == nil {
   334  		t.Fatal("expecting error")
   335  	}
   336  	if !strings.Contains(err.Error(), "conflict") {
   337  		t.Fatal("expecting conflict error")
   338  	}
   339  }
   340  func TestLrparser15(t *testing.T) {
   341  	gram := `
   342  		^S = (s2)~ s3;
   343  		s2 = "abc";
   344  		s3 = "de";
   345  	`
   346  	in := "ab●cde"
   347  	out := `
   348  		-> ^S: "cde"
   349  			-> "abc"~: "c"
   350  			-> s3: "de"
   351  				-> "de": "de"
   352  `
   353  	testLrparserMode2(t, gram, in, out, false, false, false)
   354  }
   355  func TestLrparser16(t *testing.T) {
   356  	gram := `
   357  		^S = (s2)~ ((s2)%)+;
   358  		s2 = "abc";
   359  	`
   360  	in := "ab●caa"
   361  	out := `
   362  		-> ^S: "caa"
   363  	        	-> "abc"~: "c"
   364  	        	-> ("abc"%)+: "aa"
   365  	        		-> ("abc"%)*: "a"
   366  	        			-> ("abc"%)*: ""
   367  	        			-> "abc"%: "a"
   368  	        		-> "abc"%: "a"
   369  `
   370  	testLrparserMode2(t, gram, in, out, false, false, false)
   371  }
   372  func TestLrparser17(t *testing.T) {
   373  	gram := `
   374  		^S = ("+")! "+";
   375  	`
   376  	in := "●0+"
   377  	out := `
   378  		-> ^S: "0+"
   379  	        	-> "+"!: "0"
   380  	        	-> "+": "+"
   381  `
   382  	testLrparserMode1(t, gram, in, out)
   383  }
   384  func TestLrparser18(t *testing.T) {
   385  	gram := `
   386  		^S = (s2)! "+";
   387  		s2 = ("abc")%|("defg")%;
   388  		//s2 = ("abc"|"defg")%;
   389  	`
   390  	in := "●h+"
   391  	out := `
   392  		-> ^S: "h+"
   393  	        	-> "abcdefg"!: "h"
   394  	        	-> "+": "+"
   395  `
   396  	testLrparserMode1(t, gram, in, out)
   397  }
   398  func TestLrparser19(t *testing.T) {
   399  	gram := `
   400  		^S = (@dropRunes((s2)%,("b")%))+;
   401  		s2 = "abc";
   402  	`
   403  	in := "●ac"
   404  	out := `
   405  		-> ^S: "ac"
   406  	        	-> ("ac"%)+: "ac"
   407  	        		-> ("ac"%)*: "a"
   408  	        			-> ("ac"%)*: ""
   409  	        			-> "ac"%: "a"
   410  	        		-> "ac"%: "c"
   411  `
   412  	testLrparserMode1(t, gram, in, out)
   413  }
   414  func TestLrparser20(t *testing.T) {
   415  	gram := `
   416  		^S = (s2|s3|s4)+;
   417  		s2 = "ab";
   418  		s3 = "c";
   419  		s4 = "d";
   420  	`
   421  	in := "●ababdabcd"
   422  	out := `
   423  		-> ^S: "ababdabcd"
   424  			-> ([s2|s3|s4])+: "ababdabcd"
   425  				-> ([s2|s3|s4])*: "ababdabc"
   426  					-> ([s2|s3|s4])*: "ababdab"
   427  						-> ([s2|s3|s4])*: "ababd"
   428  							-> ([s2|s3|s4])*: "abab"
   429  								-> ([s2|s3|s4])*: "ab"
   430  									-> ([s2|s3|s4])*: ""
   431  									-> [s2|s3|s4]: "ab"
   432  										-> s2: "ab"
   433  											-> "ab": "ab"
   434  								-> [s2|s3|s4]: "ab"
   435  									-> s2: "ab"
   436  										-> "ab": "ab"
   437  							-> [s2|s3|s4]: "d"
   438  								-> s4: "d"
   439  									-> "d": "d"
   440  						-> [s2|s3|s4]: "ab"
   441  							-> s2: "ab"
   442  								-> "ab": "ab"
   443  					-> [s2|s3|s4]: "c"
   444  						-> s3: "c"
   445  							-> "c": "c"
   446  				-> [s2|s3|s4]: "d"
   447  					-> s4: "d"
   448  						-> "d": "d"
   449  `
   450  	testLrparserMode1(t, gram, in, out)
   451  }
   452  func TestLrparser21(t *testing.T) {
   453  	gram := `
   454  		//^S = (sep)* arg args2;		
   455  		//args2 = (sep)+ arg args2 | (sep)+ | nil; // ok
   456  		//args2 = (sep)+ arg args2 | (sep)*; // ok (was conflict)		
   457  		
   458  		^S = (sep)* arg ((sep)+ arg)* (sep)*; // ok
   459  		
   460  		//^S = (sep)* arg ((sep)+ arg (sep)*)* ;
   461  		//^S = ((sep)* arg)+ (sep)*;
   462  		sep = " ";
   463  		arg = "a";
   464  	`
   465  	in := "●  a  a  "
   466  	out := `
   467  		-> ^S: "  a  a  "
   468  			-> (sep)*: "  "
   469  				-> (sep)*: " "
   470  					-> (sep)*: ""
   471  					-> sep: " "
   472  						-> " ": " "
   473  				-> sep: " "
   474  					-> " ": " "
   475  			-> arg: "a"
   476  				-> "a": "a"
   477  			-> ([(sep)+ arg])*: "  a"
   478  				-> ([(sep)+ arg])*: ""
   479  				-> [(sep)+ arg]: "  a"
   480  					-> (sep)+: "  "
   481  						-> (sep)*: " "
   482  							-> (sep)*: ""
   483  							-> sep: " "
   484  								-> " ": " "
   485  						-> sep: " "
   486  							-> " ": " "
   487  					-> arg: "a"
   488  						-> "a": "a"
   489  			-> (sep)*: "  "
   490  				-> (sep)*: " "
   491  					-> (sep)*: ""
   492  					-> sep: " "
   493  						-> " ": " "
   494  				-> sep: " "
   495  					-> " ": " "
   496  `
   497  	testLrparserMode1(t, gram, in, out)
   498  }
   499  
   500  //func TestLrparser22(t *testing.T) {
   501  //	gram := `
   502  //		^S = ((letter)!)+;
   503  //	`
   504  //	in := "●a "
   505  //	out := `
   506  //`
   507  //	testLrparserMode1(t, gram, in, out)
   508  //}
   509  
   510  //----------
   511  
   512  func TestLrparserStop1(t *testing.T) {
   513  	gram := `
   514  		^id =  digit (letter)*;		
   515  	`
   516  	in := "<<<●1ab>>>"
   517  	out := `
   518  		-> ^id: "1ab"
   519  			-> digit: "1"
   520  			-> (letter)*: "ab"
   521  				-> (letter)*: "a"
   522  					-> (letter)*: ""
   523  					-> letter: "a"
   524  				-> letter: "b"
   525  `
   526  	testLrparserMode2(t, gram, in, out, false, true, false)
   527  }
   528  func TestLrparserStop2(t *testing.T) {
   529  	gram := `
   530  		^S = letter (linecol)?;
   531  		linecol = entry (entry)?;
   532  		entry = ":" (digit)+;	
   533  	`
   534  	in := "●a:1:++"
   535  	out := `
   536  		-> ^S: "a:1"
   537  			-> letter: "a"
   538  			-> (linecol)?: ":1"
   539  				-> linecol: ":1"
   540  					-> entry: ":1"
   541  						-> ":": ":"
   542  						-> (digit)+: "1"
   543  							-> (digit)*: ""
   544  							-> digit: "1"
   545  					-> (entry)?: ""
   546  `
   547  
   548  	testLrparserMode2(t, gram, in, out, false, true, false)
   549  }
   550  func TestLrparserStop3(t *testing.T) {
   551  	gram := `
   552  		^S = (letter ":")+;
   553  	`
   554  	in := "●a:b:c:d"
   555  	out := `
   556  		-> ^S: "a:b:c:"
   557  			-> ([letter ":"])+: "a:b:c:"
   558  				-> ([letter ":"])*: "a:b:"
   559  					-> ([letter ":"])*: "a:"
   560  						-> ([letter ":"])*: ""
   561  						-> [letter ":"]: "a:"
   562  							-> letter: "a"
   563  							-> ":": ":"
   564  					-> [letter ":"]: "b:"
   565  						-> letter: "b"
   566  						-> ":": ":"
   567  				-> [letter ":"]: "c:"
   568  					-> letter: "c"
   569  					-> ":": ":"
   570  `
   571  
   572  	testLrparserMode2(t, gram, in, out, false, true, false)
   573  }
   574  func TestLrparserStop3b(t *testing.T) {
   575  	gram := `
   576  		^S = (letter ":")+ (digit)?;
   577  	`
   578  	in := "●a:a:b"
   579  	out := `
   580  		-> ^S: "a:a:"
   581  			-> ([letter ":"])+: "a:a:"
   582  				-> ([letter ":"])*: "a:"
   583  					-> ([letter ":"])*: ""
   584  					-> [letter ":"]: "a:"
   585  						-> letter: "a"
   586  						-> ":": ":"
   587  				-> [letter ":"]: "a:"
   588  					-> letter: "a"
   589  					-> ":": ":"
   590  			-> (digit)?: ""
   591  `
   592  
   593  	testLrparserMode2(t, gram, in, out, false, true, false)
   594  }
   595  func TestLrparserStop4(t *testing.T) {
   596  	gram := `
   597  		^S = (letter digit letter)+;	
   598  	`
   599  	in := "●a1ab2bc3"
   600  	out := `
   601  		-> ^S: "a1ab2b"
   602  			-> ([letter digit letter])+: "a1ab2b"
   603  				-> ([letter digit letter])*: "a1a"
   604  					-> ([letter digit letter])*: ""
   605  					-> [letter digit letter]: "a1a"
   606  						-> letter: "a"
   607  						-> digit: "1"
   608  						-> letter: "a"
   609  				-> [letter digit letter]: "b2b"
   610  					-> letter: "b"
   611  					-> digit: "2"
   612  					-> letter: "b"
   613  `
   614  
   615  	testLrparserMode2(t, gram, in, out, false, true, false)
   616  }
   617  func TestLrparserStop5(t *testing.T) {
   618  	gram := `
   619  		^S = ("a" "b" "c" "d")+;		
   620  	`
   621  	in := "●abcdab"
   622  	out := `
   623  		-> ^S: "abcd"
   624  			-> (["a" "b" "c" "d"])+: "abcd"
   625  				-> (["a" "b" "c" "d"])*: ""
   626  				-> ["a" "b" "c" "d"]: "abcd"
   627  					-> "a": "a"
   628  					-> "b": "b"
   629  					-> "c": "c"
   630  					-> "d": "d"
   631  `
   632  
   633  	testLrparserMode2(t, gram, in, out, false, true, false)
   634  }
   635  
   636  //----------
   637  
   638  func TestLrparserRev1(t *testing.T) {
   639  	gram := `
   640  		^rev =  digit (letter)*	;	
   641  		#^rev =  (letter)* digit;
   642  	`
   643  	in := "<<<1ab●>>>"
   644  	//in := "<<<●1ab>>>"
   645  	out := `
   646  		-> ^rev: "1ab"
   647  			-> digit: "1"
   648  			-> (letter)*: "ab"
   649  				-> (letter)*: "b"
   650  					-> (letter)*: ""
   651  					-> letter: "b"
   652  				-> letter: "a"
   653  `
   654  	testLrparserMode2(t, gram, in, out, true, true, false)
   655  	//testLrparserMode2(t, gram, in, out, false, true, false) // no rev
   656  }
   657  func TestLrparserRev2(t *testing.T) {
   658  	gram := `
   659  		^S = (s2)?;
   660  		s2 = (letter)+;
   661  	`
   662  	in := "aa●11"
   663  	out := `
   664  		-> ^S: "aa"
   665  			-> (s2)?: "aa"
   666  				-> s2: "aa"
   667  					-> (letter)+: "aa"
   668  						-> (letter)*: "a"
   669  							-> (letter)*: ""
   670  							-> letter: "a"
   671  						-> letter: "a"
   672  `
   673  	//testLrparserMode2(t, gram, in, out, true, true)
   674  	testLrparserMode2(t, gram, in, out, true, false, false)
   675  }
   676  func TestLrparserRev3(t *testing.T) {
   677  	gram := `
   678  		^S = (letter|esc)+;
   679  		esc = @escapeAny(0,"\\"); 
   680  	`
   681  	in := "a b\\ c●"
   682  	out := `
   683  		-> ^S: "b\\ c"
   684  	        	-> ([letter|esc])+: "b\\ c"
   685  	        		-> ([letter|esc])*: "\\ c"
   686  	        			-> ([letter|esc])*: "c"
   687  	        				-> ([letter|esc])*: ""
   688  	        				-> [letter|esc]: "c"
   689  	        					-> letter: "c"
   690  	        			-> [letter|esc]: "\\ "
   691  	        				-> esc: "\\ "
   692  	        					-> escapeAny('\\'): "\\ "
   693  	        		-> [letter|esc]: "b"
   694  	        			-> letter: "b"
   695  `
   696  
   697  	testLrparserMode2(t, gram, in, out, true, true, false)
   698  }
   699  func TestLrparserRev4(t *testing.T) {
   700  	gram := `
   701  		^S = (letter|esc)*;
   702  		esc = @escapeAny(0,"\\"); 
   703  	`
   704  	in := "aaa ●bbb"
   705  	out := `
   706  		-> ^S: ""
   707  			-> ([letter|esc])*: ""
   708  `
   709  
   710  	bnd := testLrparserMode2(t, gram, in, out, true, true, false)
   711  	if bnd.Pos() != 4 {
   712  		t.Fatalf("bad pos: %v", bnd.Pos())
   713  	}
   714  }
   715  func TestLrparserRev5(t *testing.T) {
   716  	gram := `
   717  		^S = (@escapeAny(0,esc))+ (esc)+;
   718  		esc = "\\";	
   719  	`
   720  	in := "aaa\\:\\ \\● bb"
   721  	out := `
   722  		-> ^S: "\\:\\ \\"
   723  	        	-> (escapeAny('\\'))+: "\\:\\ "
   724  	        		-> (escapeAny('\\'))*: "\\ "
   725  	        			-> (escapeAny('\\'))*: ""
   726  	        			-> escapeAny('\\'): "\\ "
   727  	        		-> escapeAny('\\'): "\\:"
   728  	        	-> (esc)+: "\\"
   729  	        		-> (esc)*: ""
   730  	        		-> esc: "\\"
   731  	        			-> "\\": "\\"
   732  `
   733  	bnd := testLrparserMode2(t, gram, in, out, true, true, false)
   734  	if bnd.End() != 3 {
   735  		t.Fatalf("bad pos: %v", bnd.End())
   736  	}
   737  }
   738  
   739  //----------
   740  
   741  func TestLrparserErr1(t *testing.T) {
   742  	gram := `
   743  		^S = (letter)? letter;	
   744  	`
   745  	in := "●a"
   746  	out := ``
   747  	_, err := testLrparserMode3(t, gram, in, out, false, true, true)
   748  	t.Log(err)
   749  	if err == nil {
   750  		t.Fatal("expecting error")
   751  	}
   752  }
   753  func TestLrparserErr2(t *testing.T) {
   754  	gram := `
   755  		^S = (digit)+ (letter)? digit; // was endless loop "●111+a"
   756  	`
   757  	in := "●111+a"
   758  	out := ``
   759  	_, err := testLrparserMode3(t, gram, in, out, false, true, true)
   760  	t.Log(err)
   761  	if err == nil {
   762  		t.Fatal("expecting error")
   763  	}
   764  }
   765  func TestLrparserErr3(t *testing.T) {
   766  	gram := `
   767  		^S = (digit)+ (letter)? ":";
   768  		//^S = (digit)+ (letter)?; // ok
   769  	`
   770  	in := "●111a"
   771  	out := ``
   772  	_, err := testLrparserMode3(t, gram, in, out, false, true, true)
   773  	t.Log(err)
   774  	if err == nil {
   775  		t.Fatal("expecting error")
   776  	}
   777  }
   778  func TestLrparserErr4(t *testing.T) {
   779  	gram := `
   780  		^S = ((letter)!)%;
   781  	`
   782  	in := "●aa"
   783  	out := ``
   784  	_, err := testLrparserMode3(t, gram, in, out, false, true, true)
   785  	t.Log(err)
   786  	if err == nil {
   787  		t.Fatal("expecting error")
   788  	}
   789  }
   790  
   791  //----------
   792  
   793  func TestLrparserBuild1(t *testing.T) {
   794  	gram := `
   795  		^S = (sep)* arg ((sep)+ arg)* (sep)*;
   796  		arg = (letter|digit)+;
   797  		sep = "-";
   798  	`
   799  	cp := testLrparserModeB(t, gram, false, true, true)
   800  
   801  	args := []string{}
   802  	cp.SetBuildNodeFn("S", func(d *BuildNodeData) error {
   803  		//d.PrintRuleTree(5)
   804  		if err := d.ChildLoop(2, func(d2 *BuildNodeData) error {
   805  			//d2.PrintRuleTree(5)
   806  			args = append(args, d2.ChildStr(1))
   807  			return nil
   808  		}); err != nil {
   809  			return err
   810  		}
   811  		return nil
   812  	})
   813  
   814  	in := "●a1--b2-c3---d4--"
   815  	bnd, _, err := testLrparserModeB2(t, cp, in)
   816  	if err != nil {
   817  		t.Fatal(err)
   818  	}
   819  	_ = bnd
   820  
   821  	r1 := fmt.Sprintf("%v", args)
   822  	if r1 != "[b2 c3 d4]" {
   823  		t.Fatal(r1)
   824  	}
   825  }
   826  
   827  func TestLrparserBuild2(t *testing.T) {
   828  	gram := `
   829  		^S = args;
   830  		args = (sep)+ arg args | nil;
   831  		arg = (letter|digit)+;
   832  		sep = "-";
   833  	`
   834  	cp := testLrparserModeB(t, gram, false, true, true)
   835  
   836  	args := []string{}
   837  	cp.SetBuildNodeFn("S", func(d *BuildNodeData) error {
   838  		//d.PrintRuleTree(5)
   839  		if err := d.ChildLoop2(0, 2, func(d2 *BuildNodeData) error {
   840  			// d2 is "args" rule
   841  			if d2.ChildsLen() == 3 {
   842  				args = append(args, d2.ChildStr(1))
   843  			}
   844  			return nil
   845  		}, nil); err != nil {
   846  			return err
   847  		}
   848  		return nil
   849  	})
   850  
   851  	in := "●-a1-b2-c3--d4"
   852  	bnd, _, err := testLrparserModeB2(t, cp, in)
   853  	if err != nil {
   854  		t.Fatal(err)
   855  	}
   856  	_ = bnd
   857  
   858  	r1 := fmt.Sprintf("%v", args)
   859  	if r1 != "[a1 b2 c3 d4]" {
   860  		t.Fatal(r1)
   861  	}
   862  }
   863  
   864  //----------
   865  //----------
   866  //----------
   867  
   868  func testLrparserMode1(t *testing.T, gram, in, out string) *BuildNodeData {
   869  	t.Helper()
   870  	return testLrparserMode2(t, gram, in, out, false, false, false)
   871  }
   872  func testLrparserMode2(t *testing.T, gram, in, out string, reverse, earlyStop, shiftOnSRConflict bool) *BuildNodeData {
   873  	t.Helper()
   874  	bnd, err := testLrparserMode3(t, gram, in, out, reverse, earlyStop, shiftOnSRConflict)
   875  	if err != nil {
   876  		t.Fatal(err)
   877  	}
   878  	return bnd
   879  }
   880  func testLrparserMode3(t *testing.T, gram, in, out string, reverse, earlyStop, shiftOnSRConflict bool) (*BuildNodeData, error) {
   881  	t.Helper()
   882  
   883  	//in = string(bytes.TrimRight(in, "\n"))
   884  	//out = string(bytes.TrimRight(out, "\n"))
   885  
   886  	in2, index, err := testutil.SourceCursor("●", string(in), 0)
   887  	if err != nil {
   888  		return nil, err
   889  	}
   890  
   891  	lrp, err := NewLrparserFromString(gram)
   892  	if err != nil {
   893  		return nil, err
   894  	}
   895  
   896  	opt := &CpOpt{
   897  		StartRule:         "",
   898  		Reverse:           reverse,
   899  		EarlyStop:         earlyStop,
   900  		ShiftOnSRConflict: shiftOnSRConflict,
   901  		VerboseError:      true,
   902  	}
   903  	cp, err := lrp.ContentParser(opt)
   904  	if err != nil {
   905  		return nil, err
   906  	}
   907  
   908  	bnd, cpr, err := cp.Parse([]byte(in2), index)
   909  	if err != nil {
   910  		return nil, err
   911  	}
   912  	res := bnd.SprintRuleTree(-1)
   913  
   914  	if *updateOutputFlag {
   915  		fmt.Printf("%v\n", sprintTaggedOut(res))
   916  	}
   917  
   918  	res2 := testutil.TrimLineSpaces(res)
   919  	expect2 := testutil.TrimLineSpaces(out)
   920  	if res2 != expect2 {
   921  		_ = cpr
   922  		return nil, fmt.Errorf("%v\n%s", res, cpr.Debug(cp))
   923  		//return nil, fmt.Errorf("%v", res)
   924  	}
   925  	return bnd, nil
   926  }
   927  
   928  //----------
   929  
   930  func testLrparserModeB(t *testing.T, gram string, reverse, earlyStop, shiftOnSRConflict bool) *ContentParser {
   931  	t.Helper()
   932  
   933  	lrp, err := NewLrparserFromString(gram)
   934  	if err != nil {
   935  		t.Fatal(err)
   936  	}
   937  
   938  	opt := &CpOpt{
   939  		StartRule:         "",
   940  		Reverse:           reverse,
   941  		EarlyStop:         earlyStop,
   942  		ShiftOnSRConflict: shiftOnSRConflict,
   943  		VerboseError:      true,
   944  	}
   945  	cp, err := lrp.ContentParser(opt)
   946  	if err != nil {
   947  		t.Fatal(err)
   948  	}
   949  	return cp
   950  }
   951  func testLrparserModeB2(t *testing.T, cp *ContentParser, in string) (*BuildNodeData, *cpRun, error) {
   952  	in2, index, err := testutil.SourceCursor("●", string(in), 0)
   953  	if err != nil {
   954  		t.Fatal(err)
   955  	}
   956  	return cp.Parse([]byte(in2), index)
   957  }
   958  
   959  //----------
   960  //----------
   961  //----------
   962  
   963  // WARNING: used for rewriting this file (yes, writes itself) with updated tests output when changing output formats
   964  func TestUpdateOutput(t *testing.T) {
   965  	return // MANUALLY DISABLED
   966  
   967  	if !*updateOutputFlag {
   968  		return
   969  	}
   970  	if err := updateOutput(t); err != nil {
   971  		t.Fatal(err)
   972  	}
   973  }
   974  func updateOutput(tt *testing.T) error {
   975  	// configuration
   976  	filename := "lrparser_test.go"
   977  	//testNamePrefix := "TestLrparser"
   978  	//testNamePrefix := "TestLrparserStop"
   979  	testNamePrefix := "TestLrparserRev"
   980  	contentVarName := "out"
   981  
   982  	src, err := os.ReadFile(filename)
   983  	if err != nil {
   984  		return err
   985  	}
   986  	fset := token.NewFileSet()
   987  	mode := parser.Mode(parser.ParseComments)
   988  	astFile, err := parser.ParseFile(fset, filename, src, mode)
   989  	if err != nil {
   990  		return err
   991  	}
   992  
   993  	vis := (func(c *astutil.Cursor) bool)(nil)
   994  	replace := (func(c *astutil.Cursor, val string) bool)(nil)
   995  
   996  	// find test and run it
   997  	vis = func(c *astutil.Cursor) bool {
   998  		switch t := c.Node().(type) {
   999  		case *ast.FuncDecl:
  1000  			funcName := t.Name.Name
  1001  			if strings.HasPrefix(funcName, testNamePrefix) {
  1002  				// run test
  1003  				funcName += "$" // ensure match
  1004  				fmt.Printf("running: %v\n", funcName)
  1005  				cmd := exec.Command("go", "test", fmt.Sprintf("-run=%v", funcName), "-update")
  1006  				out, err := cmd.Output()
  1007  				if err != nil {
  1008  					_ = err
  1009  					//tt.Fatal(err)
  1010  					//break
  1011  				}
  1012  				// parse wanted output
  1013  				out2, ok := parseTaggedOut(string(out))
  1014  				if !ok {
  1015  					//tt.Fatalf("unable to parse tagged output:\n%q", out2)
  1016  					//fmt.Printf("no tagged output:\n%v", out2)
  1017  					fmt.Printf("\tno tagged output\n")
  1018  					break
  1019  				}
  1020  				out3 := "`\n" + indentStr("\t\t", out2) + "`"
  1021  				//fmt.Printf("out: %s\n", out3)
  1022  
  1023  				// replace var content
  1024  				vis3 := func(c *astutil.Cursor) bool {
  1025  					if ok := replace(c, out3); ok {
  1026  						fmt.Printf("\treplaced\n")
  1027  						return false
  1028  					}
  1029  					return true
  1030  				}
  1031  				_ = astutil.Apply(c.Node(), vis3, nil)
  1032  			}
  1033  		}
  1034  		return true
  1035  	}
  1036  	// replace var content
  1037  	replace = func(c *astutil.Cursor, val string) bool {
  1038  		switch t := c.Node().(type) {
  1039  		case *ast.AssignStmt:
  1040  			if len(t.Lhs) >= 1 {
  1041  				if id, ok := t.Lhs[0].(*ast.Ident); ok {
  1042  					if id.Name == contentVarName {
  1043  						//fmt.Printf("***%v\n", id)
  1044  						if len(t.Rhs) == 1 {
  1045  							bl := &ast.BasicLit{
  1046  								Kind:  token.STRING,
  1047  								Value: val,
  1048  							}
  1049  							t.Rhs[0] = bl
  1050  							return true
  1051  						}
  1052  					}
  1053  				}
  1054  			}
  1055  		}
  1056  		return false
  1057  	}
  1058  
  1059  	node := astutil.Apply(astFile, vis, nil)
  1060  
  1061  	src2, err := astut.SprintNode2(fset, node)
  1062  	if err != nil {
  1063  		return err
  1064  	}
  1065  	//fmt.Println(src2)
  1066  	//filename += "AA" // TESTING
  1067  	if err := os.WriteFile(filename, []byte(src2), 0o644); err != nil {
  1068  		return err
  1069  	}
  1070  
  1071  	return nil
  1072  }
  1073  
  1074  //----------
  1075  //----------
  1076  //----------
  1077  
  1078  var updateOutputFlag = flag.Bool("update", false, "")
  1079  
  1080  var parseOutTag1 = "=[output:start]="
  1081  var parseOutTag2 = "=[output:end]="
  1082  
  1083  func sprintTaggedOut(s string) string {
  1084  	return fmt.Sprintf("%v\n%v\n%v", parseOutTag1, s, parseOutTag2)
  1085  }
  1086  func parseTaggedOut(s string) (string, bool) {
  1087  	i1 := strings.Index(s, parseOutTag1)
  1088  	i2 := strings.Index(s, parseOutTag2)
  1089  	if i1 < 0 || i2 < 0 {
  1090  		return s, false
  1091  	}
  1092  	return s[i1+len(parseOutTag1)+1 : i2-1], true // +1-1 for the newlines
  1093  }