github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/utils/parser/parser_test.go (about)

     1  package parser_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"testing"
     6  
     7  	"github.com/lmorg/murex/test/count"
     8  	"github.com/lmorg/murex/utils/parser"
     9  )
    10  
    11  func quickJson(v interface{}) string {
    12  	b, _ := json.MarshalIndent(v, "", "    ")
    13  	return string(b)
    14  }
    15  
    16  func testParser(t *testing.T, block string, pos int,
    17  	Escaped bool,
    18  	Comment bool,
    19  	QuoteSingle bool,
    20  	QuoteDouble bool,
    21  	QuoteBrace int,
    22  	NestedBlock int,
    23  	SquareBracket bool,
    24  	ExpectFunc bool,
    25  	LastFuncName string,
    26  	FuncName string,
    27  	Parameters []string,
    28  	VarSigil string,
    29  	PipeToken parser.PipeToken) {
    30  
    31  	count.Tests(t, 1)
    32  	t.Helper()
    33  
    34  	pt, _ := parser.Parse([]rune(block), pos)
    35  	var e bool
    36  
    37  	if Escaped != pt.Escaped {
    38  		t.Errorf("Escaped mismatch: Escaped (%v) != pt.Escaped (%v)", Escaped, pt.Escaped)
    39  		e = true
    40  	}
    41  	if Comment != pt.Comment {
    42  		t.Errorf("Comment mismatch: Comment (%v) != pt.Comment (%v)", Comment, pt.Comment)
    43  		e = true
    44  	}
    45  	if QuoteSingle != pt.QuoteSingle {
    46  		t.Errorf("QuoteSingle mismatch: QuoteSingle (%v) != pt.QuoteSingle (%v)", QuoteSingle, pt.QuoteSingle)
    47  		e = true
    48  	}
    49  	if QuoteDouble != pt.QuoteDouble {
    50  		t.Errorf("QuoteDouble mismatch: QuoteDouble (%v) != pt.QuoteDouble (%v)", QuoteDouble, pt.QuoteDouble)
    51  		e = true
    52  	}
    53  	if QuoteBrace != pt.QuoteBrace {
    54  		t.Errorf("QuoteBrace mismatch: QuoteBrace (%d) != pt.QuoteBrace (%d)", QuoteBrace, pt.QuoteBrace)
    55  		e = true
    56  	}
    57  	if NestedBlock != pt.NestedBlock {
    58  		t.Errorf("NestedBlock mismatch: NestedBlock (%d) != pt.NestedBlock (%d)", NestedBlock, pt.NestedBlock)
    59  		e = true
    60  	}
    61  	if SquareBracket != pt.SquareBracket {
    62  		t.Errorf("SquareBracket mismatch: SquareBracket (%v) != pt.SquareBracket (%v)", SquareBracket, pt.SquareBracket)
    63  		e = true
    64  	}
    65  	if ExpectFunc != pt.ExpectFunc {
    66  		t.Errorf("ExpectFunc mismatch: ExpectFunc (%v) != pt.ExpectFunc (%v)", ExpectFunc, pt.ExpectFunc)
    67  		e = true
    68  	}
    69  	if LastFuncName != pt.LastFuncName {
    70  		t.Errorf("LastFuncName mismatch: LastFuncName (%s) != pt.LastFuncName (%s)", LastFuncName, pt.LastFuncName)
    71  		e = true
    72  	}
    73  	if FuncName != pt.FuncName {
    74  		t.Errorf("FuncName mismatch: FuncName (%s) != pt.FuncName (%s)", FuncName, pt.FuncName)
    75  		e = true
    76  	}
    77  	if VarSigil != pt.VarSigil {
    78  		t.Errorf("VarSigil mismatch: VarSigil (%s) != pt.VarSigil (%s)", VarSigil, pt.VarSigil)
    79  		e = true
    80  	}
    81  	if PipeToken != pt.PipeToken {
    82  		t.Errorf("PipeToken mismatch: PipeToken (%s) != pt.PipeToken (%s)", PipeToken, pt.PipeToken)
    83  		e = true
    84  	}
    85  	if len(Parameters) != len(pt.Parameters) {
    86  		t.Errorf("Parameters mismatch: Parameters (%s) != pt.Parameters (%s)", quickJson(Parameters), quickJson(pt.Parameters))
    87  		e = true
    88  	}
    89  	for i := range Parameters {
    90  		if Parameters[i] != pt.Parameters[i] {
    91  			t.Errorf("Parameters mismatch: Parameters (%s) != pt.Parameters (%s)", quickJson(Parameters), quickJson(pt.Parameters))
    92  			e = true
    93  			break
    94  		}
    95  	}
    96  
    97  	if e {
    98  		t.Logf("  Block: %s", block)
    99  	}
   100  }
   101  
   102  func TestParser(t *testing.T) {
   103  	testParser(t, "out ", 0,
   104  		false, false, false, false, 0, 0, false, false, "", "out", []string{}, "", parser.PipeTokenNone)
   105  
   106  	testParser(t, " out Hello ", 0,
   107  		false, false, false, false, 0, 0, false, false, "", "out", []string{"Hello"}, "", parser.PipeTokenNone)
   108  	testParser(t, "  out Hello ", 0,
   109  		false, false, false, false, 0, 0, false, false, "", "out", []string{"Hello"}, "", parser.PipeTokenNone)
   110  	testParser(t, "out Hello ", 0,
   111  		false, false, false, false, 0, 0, false, false, "", "out", []string{"Hello"}, "", parser.PipeTokenNone)
   112  	testParser(t, "out  Hello ", 0,
   113  		false, false, false, false, 0, 0, false, false, "", "out", []string{"Hello"}, "", parser.PipeTokenNone)
   114  	testParser(t, "out Hello  ", 0,
   115  		false, false, false, false, 0, 0, false, false, "", "out", []string{"Hello"}, "", parser.PipeTokenNone)
   116  	testParser(t, "out:Hello ", 0,
   117  		false, false, false, false, 0, 0, false, false, "", "out", []string{"Hello"}, "", parser.PipeTokenNone)
   118  	testParser(t, "out: Hello ", 0,
   119  		false, false, false, false, 0, 0, false, false, "", "out", []string{"Hello"}, "", parser.PipeTokenNone)
   120  }
   121  
   122  func TestParserEscaped(t *testing.T) {
   123  	// escaped chars
   124  	testParser(t, `out \`, 0,
   125  		true, false, false, false, 0, 0, false, false, "", "out", []string{``}, "", parser.PipeTokenNone)
   126  	testParser(t, `out \\`, 0,
   127  		false, false, false, false, 0, 0, false, false, "", "out", []string{`\`}, "", parser.PipeTokenNone)
   128  	testParser(t, `out \#`, 0,
   129  		false, false, false, false, 0, 0, false, false, "", "out", []string{`#`}, "", parser.PipeTokenNone)
   130  	testParser(t, `out \'`, 0,
   131  		false, false, false, false, 0, 0, false, false, "", "out", []string{`'`}, "", parser.PipeTokenNone)
   132  	testParser(t, `out \"`, 0,
   133  		false, false, false, false, 0, 0, false, false, "", "out", []string{`"`}, "", parser.PipeTokenNone)
   134  	testParser(t, `out \(`, 0,
   135  		false, false, false, false, 0, 0, false, false, "", "out", []string{`(`}, "", parser.PipeTokenNone)
   136  	testParser(t, `out \)`, 0,
   137  		false, false, false, false, 0, 0, false, false, "", "out", []string{`)`}, "", parser.PipeTokenNone)
   138  	testParser(t, `out \-`, 0,
   139  		false, false, false, false, 0, 0, false, false, "", "out", []string{`-`}, "", parser.PipeTokenNone)
   140  	testParser(t, `out \ `, 0,
   141  		false, false, false, false, 0, 0, false, false, "", "out", []string{` `}, "", parser.PipeTokenNone)
   142  	testParser(t, `out \:`, 0,
   143  		false, false, false, false, 0, 0, false, false, "", "out", []string{`:`}, "", parser.PipeTokenNone)
   144  	testParser(t, `out \>`, 0,
   145  		false, false, false, false, 0, 0, false, false, "", "out", []string{`>`}, "", parser.PipeTokenNone)
   146  	testParser(t, `out \|`, 0,
   147  		false, false, false, false, 0, 0, false, false, "", "out", []string{`|`}, "", parser.PipeTokenNone)
   148  	testParser(t, `out \;`, 0,
   149  		false, false, false, false, 0, 0, false, false, "", "out", []string{`;`}, "", parser.PipeTokenNone)
   150  	testParser(t, `out \r`, 0,
   151  		false, false, false, false, 0, 0, false, false, "", "out", []string{"\r"}, "", parser.PipeTokenNone)
   152  	testParser(t, `out \n`, 0,
   153  		false, false, false, false, 0, 0, false, false, "", "out", []string{"\n"}, "", parser.PipeTokenNone)
   154  	testParser(t, `out \s`, 0,
   155  		false, false, false, false, 0, 0, false, false, "", "out", []string{" "}, "", parser.PipeTokenNone)
   156  	testParser(t, `out \t`, 0,
   157  		false, false, false, false, 0, 0, false, false, "", "out", []string{"\t"}, "", parser.PipeTokenNone)
   158  	testParser(t, `out \?`, 0,
   159  		false, false, false, false, 0, 0, false, false, "", "out", []string{`?`}, "", parser.PipeTokenNone)
   160  	testParser(t, `out \{`, 0,
   161  		false, false, false, false, 0, 0, false, false, "", "out", []string{`{`}, "", parser.PipeTokenNone)
   162  	testParser(t, `out \}`, 0,
   163  		false, false, false, false, 0, 0, false, false, "", "out", []string{`}`}, "", parser.PipeTokenNone)
   164  	testParser(t, `out \[`, 0,
   165  		false, false, false, false, 0, 0, false, false, "", "out", []string{`[`}, "", parser.PipeTokenNone)
   166  	testParser(t, `out \]`, 0,
   167  		false, false, false, false, 0, 0, false, false, "", "out", []string{`]`}, "", parser.PipeTokenNone)
   168  	testParser(t, `out \$`, 0,
   169  		false, false, false, false, 0, 0, false, false, "", "out", []string{`$`}, "", parser.PipeTokenNone)
   170  	testParser(t, `out \@`, 0,
   171  		false, false, false, false, 0, 0, false, false, "", "out", []string{`@`}, "", parser.PipeTokenNone)
   172  	testParser(t, `out \<`, 0,
   173  		false, false, false, false, 0, 0, false, false, "", "out", []string{`<`}, "", parser.PipeTokenNone)
   174  
   175  	// escape inside quotes
   176  	testParser(t, `out '\\'`, 0,
   177  		false, false, false, false, 0, 0, false, false, "", "out", []string{`\\`}, "", parser.PipeTokenNone)
   178  	testParser(t, `out "\\"`, 0,
   179  		false, false, false, false, 0, 0, false, false, "", "out", []string{`\`}, "", parser.PipeTokenNone)
   180  	testParser(t, `out (\\)`, 0,
   181  		false, false, false, false, 0, 0, false, false, "", "out", []string{`\\`}, "", parser.PipeTokenNone)
   182  
   183  	// escaping a random char
   184  	testParser(t, `out \q`, 0,
   185  		false, false, false, false, 0, 0, false, false, "", "out", []string{`q`}, "", parser.PipeTokenNone)
   186  }
   187  
   188  func TestParserComment(t *testing.T) {
   189  	testParser(t, "#out Hello world", 0,
   190  		false, true, false, false, 0, 0, false, true, "", "", []string{}, "", parser.PipeTokenNone)
   191  	testParser(t, "out foo #out bar", 0,
   192  		false, true, false, false, 0, 0, false, false, "", "out", []string{"foo"}, "", parser.PipeTokenNone)
   193  	testParser(t, `out '#'`, 0,
   194  		false, false, false, false, 0, 0, false, false, "", "out", []string{`#`}, "", parser.PipeTokenNone)
   195  	testParser(t, `out "#"`, 0,
   196  		false, false, false, false, 0, 0, false, false, "", "out", []string{`#`}, "", parser.PipeTokenNone)
   197  	testParser(t, `out (#)`, 0,
   198  		false, false, false, false, 0, 0, false, false, "", "out", []string{`#`}, "", parser.PipeTokenNone)
   199  }
   200  
   201  func TestParserQuoteSingle(t *testing.T) {
   202  	// As a parameter
   203  	testParser(t, "out 'Hello world", 0,
   204  		false, false, true, false, 0, 0, false, false, "", "out", []string{"Hello world"}, "", parser.PipeTokenNone)
   205  	testParser(t, "out 'Hello world'", 0,
   206  		false, false, false, false, 0, 0, false, false, "", "out", []string{"Hello world"}, "", parser.PipeTokenNone)
   207  	testParser(t, `out 'Hello world"`, 0,
   208  		false, false, true, false, 0, 0, false, false, "", "out", []string{`Hello world"`}, "", parser.PipeTokenNone)
   209  
   210  	// As a function
   211  	testParser(t, `'out' foo bar`, 0,
   212  		false, false, false, false, 0, 0, false, false, "", "out", []string{"foo", "bar"}, "", parser.PipeTokenNone)
   213  	testParser(t, `'out foo bar`, 0,
   214  		false, false, true, false, 0, 0, false, true, "", "out foo bar", []string{}, "", parser.PipeTokenNone)
   215  	testParser(t, `'out foo bar'`, 0,
   216  		false, false, false, false, 0, 0, false, true, "", "out foo bar", []string{}, "", parser.PipeTokenNone)
   217  
   218  	// Other quotes
   219  	testParser(t, `out '"'`, 0,
   220  		false, false, false, false, 0, 0, false, false, "", "out", []string{`"`}, "", parser.PipeTokenNone)
   221  	testParser(t, `out '('`, 0,
   222  		false, false, false, false, 0, 0, false, false, "", "out", []string{`(`}, "", parser.PipeTokenNone)
   223  	testParser(t, `out ')'`, 0,
   224  		false, false, false, false, 0, 0, false, false, "", "out", []string{`)`}, "", parser.PipeTokenNone)
   225  }
   226  
   227  func TestParserQuoteDouble(t *testing.T) {
   228  	// As a parameter
   229  	testParser(t, `out "Hello world`, 0,
   230  		false, false, false, true, 0, 0, false, false, "", "out", []string{"Hello world"}, "", parser.PipeTokenNone)
   231  	testParser(t, `out "Hello world"`, 0,
   232  		false, false, false, false, 0, 0, false, false, "", "out", []string{"Hello world"}, "", parser.PipeTokenNone)
   233  	testParser(t, `out "Hello world'`, 0,
   234  		false, false, false, true, 0, 0, false, false, "", "out", []string{"Hello world'"}, "", parser.PipeTokenNone)
   235  
   236  	// As a function
   237  	testParser(t, `"out" foo bar`, 0,
   238  		false, false, false, false, 0, 0, false, false, "", "out", []string{"foo", "bar"}, "", parser.PipeTokenNone)
   239  	testParser(t, `"out foo bar`, 0,
   240  		false, false, false, true, 0, 0, false, true, "", "out foo bar", []string{}, "", parser.PipeTokenNone)
   241  	testParser(t, `"out foo bar"`, 0,
   242  		false, false, false, false, 0, 0, false, true, "", "out foo bar", []string{}, "", parser.PipeTokenNone)
   243  
   244  	// Other quotes
   245  	testParser(t, `out "'"`, 0,
   246  		false, false, false, false, 0, 0, false, false, "", "out", []string{`'`}, "", parser.PipeTokenNone)
   247  	testParser(t, `out "("`, 0,
   248  		false, false, false, false, 0, 0, false, false, "", "out", []string{`(`}, "", parser.PipeTokenNone)
   249  	testParser(t, `out ")"`, 0,
   250  		false, false, false, false, 0, 0, false, false, "", "out", []string{`)`}, "", parser.PipeTokenNone)
   251  }
   252  
   253  func TestParserQuoteBrace(t *testing.T) {
   254  	// As a parameter
   255  	testParser(t, "out (Hello world", 0,
   256  		false, false, false, false, 1, 0, false, false, "", "out", []string{"Hello world"}, "", parser.PipeTokenNone)
   257  	testParser(t, "out (Hello world)", 0,
   258  		false, false, false, false, 0, 0, false, false, "", "out", []string{"Hello world"}, "", parser.PipeTokenNone)
   259  	testParser(t, "out ((Hello world", 0,
   260  		false, false, false, false, 2, 0, false, false, "", "out", []string{"(Hello world"}, "", parser.PipeTokenNone)
   261  	testParser(t, "out ((Hello (world", 0,
   262  		false, false, false, false, 3, 0, false, false, "", "out", []string{"(Hello (world"}, "", parser.PipeTokenNone)
   263  	testParser(t, "out ((Hello world)", 0,
   264  		false, false, false, false, 1, 0, false, false, "", "out", []string{"(Hello world)"}, "", parser.PipeTokenNone)
   265  	testParser(t, "out ((Hello (world)", 0,
   266  		false, false, false, false, 2, 0, false, false, "", "out", []string{"(Hello (world)"}, "", parser.PipeTokenNone)
   267  
   268  	// As a function
   269  	testParser(t, `(out) foo bar`, 0,
   270  		false, false, false, false, 0, 0, false, false, "", "(", []string{"out", "foo", "bar"}, "", parser.PipeTokenNone)
   271  	testParser(t, `(out foo bar`, 0,
   272  		false, false, false, false, 1, 0, false, false, "", "(", []string{"out foo bar"}, "", parser.PipeTokenNone)
   273  	testParser(t, `(out foo bar)`, 0,
   274  		false, false, false, false, 0, 0, false, false, "", "(", []string{"out foo bar"}, "", parser.PipeTokenNone)
   275  
   276  	// Other quotes
   277  	testParser(t, `out (')`, 0,
   278  		false, false, false, false, 0, 0, false, false, "", "out", []string{`'`}, "", parser.PipeTokenNone)
   279  	testParser(t, `out (")`, 0,
   280  		false, false, false, false, 0, 0, false, false, "", "out", []string{`"`}, "", parser.PipeTokenNone)
   281  }
   282  
   283  func TestParserPipeBasic(t *testing.T) {
   284  	testParser(t, "foo | bar", 0,
   285  		false, false, false, false, 0, 0, false, true, "foo", "bar", []string{}, "", parser.PipeTokenPosix)
   286  	testParser(t, "foo -> bar", 0,
   287  		false, false, false, false, 0, 0, false, true, "foo", "bar", []string{}, "", parser.PipeTokenArrow)
   288  	testParser(t, "foo => bar", 0,
   289  		false, false, false, false, 0, 0, false, true, "foo", "bar", []string{}, "", parser.PipeTokenGeneric)
   290  	testParser(t, "foo ? bar", 0,
   291  		false, false, false, false, 0, 0, false, true, "foo", "bar", []string{}, "", parser.PipeTokenRedirect)
   292  }