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 }