| 1 | #!/usr/bin/env bash
 | 
| 2 | #
 | 
| 3 | # Usage:
 | 
| 4 | #   test/ysh-parse-errors.sh <function name>
 | 
| 5 | 
 | 
| 6 | set -o nounset
 | 
| 7 | set -o pipefail
 | 
| 8 | set -o errexit
 | 
| 9 | 
 | 
| 10 | source test/common.sh
 | 
| 11 | source test/sh-assert.sh  # _assert-sh-status
 | 
| 12 | 
 | 
| 13 | #
 | 
| 14 | # Cases
 | 
| 15 | #
 | 
| 16 | 
 | 
| 17 | test-return-args() {
 | 
| 18 |   _ysh-should-parse '
 | 
| 19 |   func foo(x) {
 | 
| 20 |     return (x)
 | 
| 21 |   }
 | 
| 22 |   '
 | 
| 23 | 
 | 
| 24 |   _ysh-parse-error '
 | 
| 25 |   func foo(x) {
 | 
| 26 |     return ()
 | 
| 27 |   }
 | 
| 28 |   '
 | 
| 29 | 
 | 
| 30 |   _ysh-parse-error '
 | 
| 31 |   func foo(x) {
 | 
| 32 |     return (named=x)
 | 
| 33 |   }
 | 
| 34 |   '
 | 
| 35 | 
 | 
| 36 |   _ysh-parse-error '
 | 
| 37 |   func foo(x) {
 | 
| 38 |     return (x, named=x)
 | 
| 39 |   }
 | 
| 40 |   '
 | 
| 41 | 
 | 
| 42 |   _ysh-parse-error '
 | 
| 43 |   func foo(x) {
 | 
| 44 |     return (x, x)
 | 
| 45 |   }
 | 
| 46 |   '
 | 
| 47 | }
 | 
| 48 | 
 | 
| 49 | test-func-var-checker() {
 | 
| 50 |   _ysh-should-parse '
 | 
| 51 |   func f(x) {
 | 
| 52 |     setvar x = true
 | 
| 53 |   }
 | 
| 54 |   '
 | 
| 55 | 
 | 
| 56 |   _ysh-parse-error '
 | 
| 57 |   func f() {
 | 
| 58 |     setvar x = true
 | 
| 59 |   }
 | 
| 60 |   '
 | 
| 61 | }
 | 
| 62 | 
 | 
| 63 | test-arglist() {
 | 
| 64 |   _ysh-parse-error 'json write ()'
 | 
| 65 | 
 | 
| 66 |   # named args allowed in first group
 | 
| 67 |   _ysh-should-parse 'json write (42, indent=1)'
 | 
| 68 |   _ysh-should-parse 'json write (42; indent=2)'
 | 
| 69 | 
 | 
| 70 |   _ysh-should-parse '= toJson(42, indent=1)'
 | 
| 71 |   _ysh-should-parse '= toJson(42; indent=2)'
 | 
| 72 | 
 | 
| 73 |   # Named group only
 | 
| 74 |   _ysh-should-parse 'p (; n=true)'
 | 
| 75 |   _ysh-should-parse '= f(; n=true)'
 | 
| 76 | 
 | 
| 77 |   # Empty named group
 | 
| 78 |   _ysh-should-parse 'p (;)'
 | 
| 79 |   _ysh-should-parse '= f(;)'
 | 
| 80 | 
 | 
| 81 |   _ysh-should-parse 'p (42;)'
 | 
| 82 |   _ysh-should-parse '= f(42;)'
 | 
| 83 | 
 | 
| 84 |   # No block group in func arg lists
 | 
| 85 |   _ysh-parse-error '= f(42; n=true; block)'
 | 
| 86 |   _ysh-parse-error '= f(42; ; block)'
 | 
| 87 | 
 | 
| 88 |   # Block expressions in proc arg lists
 | 
| 89 |   _ysh-should-parse 'p (42; n=true; block)'
 | 
| 90 |   _ysh-should-parse 'p (42; ; block)'
 | 
| 91 | 
 | 
| 92 |   _ysh-parse-error 'p (42; n=42; bad=3)'
 | 
| 93 |   _ysh-parse-error 'p (42; n=42; ...bad)'
 | 
| 94 | 
 | 
| 95 |   # Positional args can't appear in the named section
 | 
| 96 |   _ysh-parse-error '= f(; 42)'
 | 
| 97 |   _ysh-parse-error '= f(; name)'
 | 
| 98 |   _ysh-parse-error '= f(; x for x in y)'
 | 
| 99 | }
 | 
| 100 | 
 | 
| 101 | 
 | 
| 102 | # Extra constraints on param groups:
 | 
| 103 | # - word arg types can only be Str or Ref
 | 
| 104 | # - no constraints on positional or keyword args?
 | 
| 105 | #   - they have optional types, and optional default vals
 | 
| 106 | # - block param:
 | 
| 107 | #   - there can only be one
 | 
| 108 | #   - no rest param either
 | 
| 109 | #   - default value is null only?
 | 
| 110 | 
 | 
| 111 | test-proc-sig() {
 | 
| 112 |   _ysh-should-parse 'proc p () { echo hi }'
 | 
| 113 |   _ysh-should-parse 'proc p (a) { echo hi }'
 | 
| 114 |   _ysh-should-parse 'proc p (out Ref) { echo hi }'
 | 
| 115 | 
 | 
| 116 |   # doesn't make sense I think -- they're all strings.  Types don't do any
 | 
| 117 |   # dynamic validation, except 'out Ref' does change semantics
 | 
| 118 |   _ysh-parse-error 'proc p (a Int) { echo hi }'
 | 
| 119 | 
 | 
| 120 |   _ysh-parse-error 'proc p (w, ...) { echo hi }'
 | 
| 121 | 
 | 
| 122 |   _ysh-should-parse 'proc p (w, ...rest) { echo hi }'
 | 
| 123 | 
 | 
| 124 |   # Hm I guess this is fine
 | 
| 125 |   _ysh-should-parse 'proc p (; n Int=3) { echo hi }'
 | 
| 126 | 
 | 
| 127 |   _ysh-should-parse 'proc p (out Ref; n Int=3) { echo hi }'
 | 
| 128 | 
 | 
| 129 |   _ysh-should-parse 'proc p (; ; n Int=3) { echo hi }'
 | 
| 130 | 
 | 
| 131 |   _ysh-should-parse 'proc p ( ; ; ; block) { echo hi }'
 | 
| 132 | 
 | 
| 133 |   _ysh-should-parse 'proc p (w, ...rest) { echo hi }'
 | 
| 134 |   _ysh-should-parse 'proc p (w, ...rest; t) { echo hi }'
 | 
| 135 | 
 | 
| 136 |   _ysh-should-parse 'func p (p, ...rest) { echo hi }'
 | 
| 137 | 
 | 
| 138 |   _ysh-should-parse 'func p (p, ...rest; n, ...named) { echo hi }'
 | 
| 139 |   _ysh-should-parse 'func p (p, ...rest; n, ...named,) { echo hi }'
 | 
| 140 | 
 | 
| 141 |   _ysh-parse-error 'func p (p, ...rest; n, ...named, z) { echo hi }'
 | 
| 142 |   _ysh-parse-error 'func p (p, ...rest; n, ...named; ) { echo hi }'
 | 
| 143 | 
 | 
| 144 |   _ysh-should-parse 'proc p (w, ...rest; pos, ...named) { echo hi }'
 | 
| 145 | 
 | 
| 146 |   _ysh-should-parse 'proc p (w, ...rest; pos, ...args; named=3, ...named) { echo hi }'
 | 
| 147 | 
 | 
| 148 |   _ysh-should-parse 'proc p (w=1, v=2; p=3, q=4; n=5, m=6) { echo hi }'
 | 
| 149 | 
 | 
| 150 |   _ysh-parse-error 'proc p (w Int Int) { echo hi }'
 | 
| 151 | 
 | 
| 152 |   _ysh-should-parse 'proc p (w=1, v=2; p Int=3, q List[Int] = [3, 4]; n Int=5, m Int = 6) { echo hi }'
 | 
| 153 | 
 | 
| 154 |   _ysh-should-parse 'proc p (w, ...rest; t, ...args; n, ...named; block) { echo hi }'
 | 
| 155 | 
 | 
| 156 |   _ysh-parse-error 'proc p ( ; ; ; b1, b2) { echo hi }'
 | 
| 157 |   _ysh-parse-error 'proc p ( ; ; ; b1, ...rest) { echo hi }'
 | 
| 158 |   _ysh-parse-error 'proc p ( ; ; ; b1 Str) { echo hi }'
 | 
| 159 | 
 | 
| 160 |   # Only Command type
 | 
| 161 |   _ysh-should-parse 'proc p ( ; ; ; b Command) { echo hi }'
 | 
| 162 | 
 | 
| 163 |   # bad param
 | 
| 164 |   _ysh-parse-error 'proc p ( ; ; ; b Command[Int]) { echo hi }'
 | 
| 165 | 
 | 
| 166 |   _ysh-should-parse 'proc p ( ; ; ; ) { echo hi }'
 | 
| 167 | }
 | 
| 168 | 
 | 
| 169 | test-proc-def() {
 | 
| 170 |   _ysh-parse-error 'proc p(w) { var w = foo }'
 | 
| 171 |   _ysh-parse-error 'proc p(w; p) { var p = foo }'
 | 
| 172 |   _ysh-parse-error 'proc p(w; p; n, n2) { var n2 = foo }'
 | 
| 173 |   _ysh-parse-error 'proc p(w; p; n, n2; b) { var b = foo }'
 | 
| 174 | }
 | 
| 175 | 
 | 
| 176 | test-typed-proc() {
 | 
| 177 |   _ysh-should-parse 'typed proc p(words) { echo hi }'
 | 
| 178 |   _ysh-parse-error 'typed zzz p(words) { echo hi }'
 | 
| 179 |   _ysh-parse-error 'typed p(words) { echo hi }'
 | 
| 180 | }
 | 
| 181 | 
 | 
| 182 | test-func-sig() {
 | 
| 183 |   _ysh-parse-error 'func f { echo hi }'
 | 
| 184 | 
 | 
| 185 |   _ysh-should-parse 'func f () { echo hi }'
 | 
| 186 | 
 | 
| 187 |   _ysh-should-parse 'func f (a List[Int] = [3,4]) { echo hi }'
 | 
| 188 |   _ysh-should-parse 'func f (a, b, ...rest; c) { echo hi }'
 | 
| 189 |   _ysh-should-parse 'func f (a, b, ...rest; c, ...named) { echo hi }'
 | 
| 190 |   _ysh-parse-error 'func f (a, b, ...rest; c, ...named;) { echo hi }'
 | 
| 191 | }
 | 
| 192 | 
 | 
| 193 | test-func-def() {
 | 
| 194 |   _ysh-parse-error 'func f(p) { var p = foo }'
 | 
| 195 |   _ysh-parse-error 'func f(p; n) { var n = foo }'
 | 
| 196 | }
 | 
| 197 | 
 | 
| 198 | test-sh-assign() {
 | 
| 199 |   _ysh-should-parse 'x=y'
 | 
| 200 |   _ysh-should-parse 'x=y echo hi'
 | 
| 201 |   _ysh-should-parse 'f() { x=y; }'
 | 
| 202 | 
 | 
| 203 |   # Disallowed in YSH
 | 
| 204 |   _ysh-parse-error 'func f() { x=y; }'
 | 
| 205 |   _ysh-parse-error 'proc p { x=y; }'
 | 
| 206 | 
 | 
| 207 |   # Only proc and func disallow it
 | 
| 208 |   _ysh-should-parse '{ x=y; }'
 | 
| 209 |   _ysh-should-parse '( x=y; )'
 | 
| 210 | 
 | 
| 211 |   _assert-sh-status 0 $YSH 'Expected it to parse' \
 | 
| 212 |     -o ysh:upgrade -n -c 'x=y'
 | 
| 213 | }
 | 
| 214 | 
 | 
| 215 | test-ysh-var() {
 | 
| 216 |   # Unterminated
 | 
| 217 |   _ysh-parse-error 'var x = 1 + '
 | 
| 218 | 
 | 
| 219 |   _ysh-parse-error 'var x = * '
 | 
| 220 | 
 | 
| 221 |   _ysh-parse-error 'var x = @($(cat <<EOF
 | 
| 222 | here doc
 | 
| 223 | EOF
 | 
| 224 | ))'
 | 
| 225 | 
 | 
| 226 |   # Hm we need a ; after var or setvar
 | 
| 227 |   _ysh-should-parse 'var x = $(var x = 1; )'
 | 
| 228 |   _ysh-should-parse '
 | 
| 229 |   var x = $(var x = 1
 | 
| 230 | )'
 | 
| 231 |   # This doesn't have it
 | 
| 232 |   _ysh-parse-error 'var x = $(var x = 1)'
 | 
| 233 | 
 | 
| 234 |   # Extra )
 | 
| 235 |   _ysh-parse-error 'var x = $(var x = 1; ))'
 | 
| 236 |   _ysh-parse-error 'var x = $(var x = 1; ) )'
 | 
| 237 | }
 | 
| 238 | 
 | 
| 239 | test-ysh-expr() {
 | 
| 240 |   # old syntax
 | 
| 241 |   _ysh-parse-error '= 5 mod 3'
 | 
| 242 | 
 | 
| 243 |   _ysh-parse-error '= >>='
 | 
| 244 |   _ysh-parse-error '= %('
 | 
| 245 | 
 | 
| 246 |   # Singleton tuples
 | 
| 247 |   _ysh-parse-error '= 42,'
 | 
| 248 |   _ysh-parse-error '= (42,)'
 | 
| 249 | 
 | 
| 250 |   # Disallowed unconditionally
 | 
| 251 |   _ysh-parse-error '=a'
 | 
| 252 | 
 | 
| 253 |   _ysh-parse-error '
 | 
| 254 |     var d = {}
 | 
| 255 |     = d["foo", "bar"]
 | 
| 256 |   '
 | 
| 257 | }
 | 
| 258 | 
 | 
| 259 | test-ysh-expr-more() {
 | 
| 260 |   # user must choose === or ~==
 | 
| 261 |   _ysh-parse-error 'if (5 == 5) { echo yes }'
 | 
| 262 | 
 | 
| 263 |   _ysh-should-parse 'echo $[join(x)]'
 | 
| 264 | 
 | 
| 265 |   _ysh-parse-error 'echo $join(x)'
 | 
| 266 | 
 | 
| 267 |   _ysh-should-parse 'echo @[split(x)]'
 | 
| 268 |   _ysh-should-parse 'echo @[split(x)] two'
 | 
| 269 | 
 | 
| 270 |   _ysh-parse-error 'echo @[split(x)]extra'
 | 
| 271 | 
 | 
| 272 |   # Old syntax that's now invalid
 | 
| 273 |   _ysh-parse-error 'echo @split("a")'
 | 
| 274 | }
 | 
| 275 | 
 | 
| 276 | test-blocks() {
 | 
| 277 |   _ysh-parse-error '>out { echo hi }'
 | 
| 278 |   _ysh-parse-error 'a=1 b=2 { echo hi }'
 | 
| 279 |   _ysh-parse-error 'break { echo hi }'
 | 
| 280 |   # missing semicolon
 | 
| 281 |   _ysh-parse-error 'cd / { echo hi } cd /'
 | 
| 282 | }
 | 
| 283 | 
 | 
| 284 | test-parse-brace() {
 | 
| 285 |   # missing space
 | 
| 286 |   _ysh-parse-error 'if test -f foo{ echo hi }'
 | 
| 287 | }
 | 
| 288 | 
 | 
| 289 | test-proc-sig() {
 | 
| 290 |   _ysh-parse-error 'proc f[] { echo hi }'
 | 
| 291 |   _ysh-parse-error 'proc : { echo hi }'
 | 
| 292 |   _ysh-parse-error 'proc foo::bar { echo hi }'
 | 
| 293 | }
 | 
| 294 | 
 | 
| 295 | test-regex-literals() {
 | 
| 296 |   _ysh-parse-error 'var x = / ! /'
 | 
| 297 |   _ysh-should-parse 'var x = / ![a-z] /'
 | 
| 298 | 
 | 
| 299 |   _ysh-should-parse 'var x = / !d /'
 | 
| 300 | 
 | 
| 301 |   _ysh-parse-error 'var x = / !! /'
 | 
| 302 | 
 | 
| 303 |   # missing space between rangfes
 | 
| 304 |   _ysh-parse-error 'var x = /[a-zA-Z]/'
 | 
| 305 |   _ysh-parse-error 'var x = /[a-z0-9]/'
 | 
| 306 | 
 | 
| 307 |   _ysh-parse-error 'var x = /[a-zz]/'
 | 
| 308 | 
 | 
| 309 |   # can't have multichar ranges
 | 
| 310 |   _ysh-parse-error "var x = /['ab'-'z']/"
 | 
| 311 | 
 | 
| 312 |   # range endpoints must be constants
 | 
| 313 |   _ysh-parse-error 'var x = /[$a-${z}]/'
 | 
| 314 | 
 | 
| 315 |   # These are too long too
 | 
| 316 |   _ysh-parse-error 'var x = /[abc]/'
 | 
| 317 | 
 | 
| 318 |   # Single chars not allowed, should be /['%_']/
 | 
| 319 |   _ysh-parse-error 'var x = /[% _]/'
 | 
| 320 | 
 | 
| 321 | }
 | 
| 322 | 
 | 
| 323 | test-hay-assign() {
 | 
| 324 |   _ysh-parse-error '
 | 
| 325 | name = val
 | 
| 326 | '
 | 
| 327 | 
 | 
| 328 |   _ysh-parse-error '
 | 
| 329 | rule {
 | 
| 330 |   x = 42
 | 
| 331 | }
 | 
| 332 | '
 | 
| 333 | 
 | 
| 334 |   _ysh-parse-error '
 | 
| 335 | RULE {
 | 
| 336 |   x = 42
 | 
| 337 | }
 | 
| 338 | '
 | 
| 339 | 
 | 
| 340 |   _ysh-should-parse '
 | 
| 341 | Rule {
 | 
| 342 |   x = 42
 | 
| 343 | }
 | 
| 344 | '
 | 
| 345 | 
 | 
| 346 |   _ysh-should-parse '
 | 
| 347 | Rule X Y {
 | 
| 348 |   x = 42
 | 
| 349 | }
 | 
| 350 | '
 | 
| 351 | 
 | 
| 352 |   _ysh-should-parse '
 | 
| 353 | RULe {   # inconsistent but OK
 | 
| 354 |   x = 42
 | 
| 355 | }
 | 
| 356 | '
 | 
| 357 | 
 | 
| 358 |   _ysh-parse-error '
 | 
| 359 | hay eval :result {
 | 
| 360 | 
 | 
| 361 |   Rule {
 | 
| 362 |     foo = 42
 | 
| 363 |   }
 | 
| 364 | 
 | 
| 365 |   bar = 43   # parse error here
 | 
| 366 | }
 | 
| 367 | '
 | 
| 368 | 
 | 
| 369 |   _ysh-parse-error '
 | 
| 370 | hay define TASK
 | 
| 371 | 
 | 
| 372 | TASK build {
 | 
| 373 |   foo = 42
 | 
| 374 | }
 | 
| 375 | '
 | 
| 376 | 
 | 
| 377 |   # CODE node nested inside Attr node.
 | 
| 378 |   _ysh-parse-error '
 | 
| 379 | hay define Package/TASK
 | 
| 380 | 
 | 
| 381 | Package libc {
 | 
| 382 |   TASK build {
 | 
| 383 |     # this is not an attribute, should not be valid
 | 
| 384 |     foo = 42
 | 
| 385 |   }
 | 
| 386 | }
 | 
| 387 | '
 | 
| 388 | 
 | 
| 389 |   _ysh-parse-error '
 | 
| 390 | hay define Rule
 | 
| 391 | 
 | 
| 392 | Rule {
 | 
| 393 |   return (x)
 | 
| 394 | }
 | 
| 395 | '
 | 
| 396 | 
 | 
| 397 |   return
 | 
| 398 |   # This is currently allowed, arguably shouldn't be
 | 
| 399 | 
 | 
| 400 |   _ysh-parse-error '
 | 
| 401 | hay define Rule
 | 
| 402 | 
 | 
| 403 | Rule {
 | 
| 404 |   return 42
 | 
| 405 | }
 | 
| 406 | '
 | 
| 407 | }
 | 
| 408 | 
 | 
| 409 | test-hay-shell-assign() {
 | 
| 410 |   _ysh-parse-error '
 | 
| 411 | hay define Package
 | 
| 412 | 
 | 
| 413 | Package foo {
 | 
| 414 |   version=1
 | 
| 415 | }
 | 
| 416 | '
 | 
| 417 | 
 | 
| 418 |   _ysh-parse-error '
 | 
| 419 | hay define Package/User
 | 
| 420 | 
 | 
| 421 | Package foo {
 | 
| 422 |   User bob {
 | 
| 423 |     sudo=1
 | 
| 424 |   }
 | 
| 425 | }
 | 
| 426 | '
 | 
| 427 | 
 | 
| 428 |   _ysh-should-parse '
 | 
| 429 | hay define Package/SHELL/User
 | 
| 430 | 
 | 
| 431 | Package foo {
 | 
| 432 |   SHELL bob {
 | 
| 433 |     sudo=1
 | 
| 434 |     User {
 | 
| 435 |       name = "z"
 | 
| 436 |     }
 | 
| 437 |   }
 | 
| 438 | }
 | 
| 439 | '
 | 
| 440 | 
 | 
| 441 |   _ysh-parse-error '
 | 
| 442 | hay define Package/SHELL/User
 | 
| 443 | 
 | 
| 444 | Package foo {
 | 
| 445 |   SHELL bob {
 | 
| 446 |     # Disallowed
 | 
| 447 |     # a = b
 | 
| 448 |     User {
 | 
| 449 |       x=1
 | 
| 450 |     }
 | 
| 451 |   }
 | 
| 452 | }
 | 
| 453 | '
 | 
| 454 | 
 | 
| 455 |   return
 | 
| 456 | 
 | 
| 457 |   # It's OK that this parses, we didn't use the CapsWord style
 | 
| 458 | 
 | 
| 459 |   _ysh-parse-error '
 | 
| 460 | hay define package user TASK
 | 
| 461 | 
 | 
| 462 | hay eval :result {
 | 
| 463 |   package foo {
 | 
| 464 |     version=1
 | 
| 465 |   }
 | 
| 466 | }
 | 
| 467 | '
 | 
| 468 | }
 | 
| 469 | 
 | 
| 470 | test-parse-at() {
 | 
| 471 |   _ysh-parse-error 'echo @'
 | 
| 472 |   _ysh-parse-error 'echo @@'
 | 
| 473 |   _ysh-parse-error 'echo @{foo}'
 | 
| 474 |   _ysh-parse-error 'echo @/foo/'
 | 
| 475 |   _ysh-parse-error 'echo @"foo"'
 | 
| 476 | }
 | 
| 477 | 
 | 
| 478 | test-ysh-nested-proc-func() {
 | 
| 479 |   _ysh-parse-error 'proc p { echo 1; proc f { echo f }; echo 2 }'
 | 
| 480 |   _ysh-parse-error 'func f() { echo 1; proc f { echo f }; echo 2 }'
 | 
| 481 |   _ysh-parse-error 'proc p { echo 1; func f() { echo f }; echo 2 }'
 | 
| 482 |   _ysh-parse-error 'func f() { echo 1; func f2() { echo f }; echo 2 }'
 | 
| 483 | 
 | 
| 484 |   _ysh-parse-error 'proc p { echo 1; +weird() { echo f; }; echo 2 }'
 | 
| 485 | 
 | 
| 486 |   # ksh function
 | 
| 487 |   _ysh-parse-error 'proc p { echo 1; function f { echo f; }; echo 2 }'
 | 
| 488 | 
 | 
| 489 |   _ysh-parse-error 'f() { echo 1; proc inner { echo inner; }; echo 2; }'
 | 
| 490 | 
 | 
| 491 |   # shell nesting is still allowed
 | 
| 492 |   _ysh-should-parse 'f() { echo 1; g() { echo g; }; echo 2; }'
 | 
| 493 | 
 | 
| 494 |   _ysh-should-parse 'proc p() { shopt --unset errexit { false hi } }'
 | 
| 495 | }
 | 
| 496 | 
 | 
| 497 | test-int-literals() {
 | 
| 498 |   _ysh-should-parse '= 42'
 | 
| 499 |   _ysh-should-parse '= 42_0'
 | 
| 500 |   _ysh-parse-error '= 42_'
 | 
| 501 |   _ysh-parse-error '= 42_0_'
 | 
| 502 | 
 | 
| 503 |   # this is a var name
 | 
| 504 |   _ysh-should-parse '= _42'
 | 
| 505 | }
 | 
| 506 | 
 | 
| 507 | test-float-literals() {
 | 
| 508 |   _ysh-should-parse '= 42.0'
 | 
| 509 |   _ysh-should-parse '= 42_0.0'
 | 
| 510 |   _ysh-parse-error '= 42_.0'
 | 
| 511 | 
 | 
| 512 |   _ysh-parse-error '= 42.'
 | 
| 513 |   _ysh-parse-error '= .333'
 | 
| 514 | 
 | 
| 515 |   _ysh-parse-error '= _42.0'
 | 
| 516 | }
 | 
| 517 | 
 | 
| 518 | test-lhs-expr() {
 | 
| 519 |   _ysh-should-parse 'setvar x.y[z] = 42'
 | 
| 520 |   _ysh-should-parse 'setvar a[i][j] = 42'
 | 
| 521 | 
 | 
| 522 |   _ysh-should-parse 'setvar a[i], d.key = 42, 43'
 | 
| 523 |   _ysh-parse-error 'setvar a[i], 3 = 42, 43'
 | 
| 524 |   _ysh-parse-error 'setvar a[i], {}["key"] = 42, 43'
 | 
| 525 | 
 | 
| 526 |   _ysh-parse-error 'setvar x+y = 42'
 | 
| 527 | 
 | 
| 528 |   # method call
 | 
| 529 |   _ysh-parse-error 'setvar x->y = 42'
 | 
| 530 | 
 | 
| 531 |   # this is allowed
 | 
| 532 |   _ysh-should-parse 'setglobal a[{k:3}["k"]]  = 42'
 | 
| 533 | 
 | 
| 534 |   _ysh-parse-error 'setglobal {}["k"] = 42'
 | 
| 535 |   _ysh-parse-error 'setglobal [1,2][0] = 42'
 | 
| 536 | }
 | 
| 537 | 
 | 
| 538 | test-destructure() {
 | 
| 539 |   _ysh-parse-error '
 | 
| 540 |   func f() {
 | 
| 541 |     const x, y = 3, 4
 | 
| 542 | 
 | 
| 543 |     #setvar x = 5
 | 
| 544 | 
 | 
| 545 |     setvar y = 6
 | 
| 546 |   }'
 | 
| 547 | 
 | 
| 548 |   _ysh-parse-error '
 | 
| 549 |   func f() {
 | 
| 550 |     var x, y = 3, 4
 | 
| 551 | 
 | 
| 552 |     var y = 6
 | 
| 553 |   }'
 | 
| 554 | 
 | 
| 555 |   _ysh-parse-error '
 | 
| 556 |   func f() {
 | 
| 557 |     var x, y = 3, 4
 | 
| 558 | 
 | 
| 559 |     const y = 6
 | 
| 560 |   }'
 | 
| 561 | }
 | 
| 562 | 
 | 
| 563 | test-lazy-arg-list() {
 | 
| 564 |   _ysh-should-parse 'assert [42 === x]'
 | 
| 565 | 
 | 
| 566 |   _ysh-should-parse 'assert [ 42 === x ]'
 | 
| 567 |   _ysh-should-parse 'assert [42, 43]'
 | 
| 568 |   _ysh-should-parse 'assert [42, named=true]'
 | 
| 569 |   _ysh-should-parse 'assert [42, named=true]; echo hi'
 | 
| 570 | 
 | 
| 571 |   _ysh-should-parse 'assert [42, named=true] { echo hi }'
 | 
| 572 | 
 | 
| 573 |   # Seems fine
 | 
| 574 |   _ysh-should-parse 'assert [42, named=true]{ echo hi }'
 | 
| 575 | 
 | 
| 576 |   # I guess this legacy is still valid?  Or disallow explicitly
 | 
| 577 |   _ysh-should-parse 'assert *.[ch]'
 | 
| 578 |   _ysh-should-parse 'assert 42[ch]'
 | 
| 579 |   _ysh-should-parse 'echo[]'
 | 
| 580 | 
 | 
| 581 |   _ysh-parse-error 'assert [4'
 | 
| 582 |   _ysh-parse-error 'assert [ 4'
 | 
| 583 | 
 | 
| 584 |   _ysh-should-parse 'json write (42) >out'
 | 
| 585 | 
 | 
| 586 |   # I guess this is OK
 | 
| 587 |   _ysh-should-parse 'json write >out (42)'
 | 
| 588 | 
 | 
| 589 |   # BUG
 | 
| 590 |   #_ysh-parse-error 'when (42) >out { echo hi }'
 | 
| 591 | 
 | 
| 592 |   #_ysh-should-parse 'when (42) { echo hi } >out'
 | 
| 593 | 
 | 
| 594 |   # How to support this?  Maybe the CommandParser can test for i == 0 when it
 | 
| 595 |   # gets Op_LBracket
 | 
| 596 | 
 | 
| 597 |   # legacy
 | 
| 598 |   _ysh-should-parse '[ x = y ]'
 | 
| 599 | }
 | 
| 600 | 
 | 
| 601 | test-place-expr() {
 | 
| 602 |   _ysh-should-parse 'read (&x)'
 | 
| 603 | 
 | 
| 604 |   # TODO: parse these into something
 | 
| 605 |   _ysh-parse-error 'read (&x[0])'
 | 
| 606 |   _ysh-parse-error 'read (&x[0][1])'
 | 
| 607 | 
 | 
| 608 |   _ysh-parse-error 'read (&x.key.other)'
 | 
| 609 | 
 | 
| 610 |   # This is a runtime error, not a parse time error
 | 
| 611 |   _ysh-should-parse 'read (&x + 1)'
 | 
| 612 | 
 | 
| 613 |   _ysh-parse-error 'read (&42)'
 | 
| 614 |   _ysh-parse-error 'read (&+)'
 | 
| 615 | 
 | 
| 616 |   # Place expressions aren't parenthesized expressions
 | 
| 617 |   _ysh-parse-error 'read (&(x))'
 | 
| 618 | }
 | 
| 619 | 
 | 
| 620 | test-units-suffix() {
 | 
| 621 |   _ysh-parse-error '= 100 M M'
 | 
| 622 | 
 | 
| 623 |   _ysh-parse-error '= 100 M; echo'
 | 
| 624 |   _ysh-parse-error '= 100 Mi; echo'
 | 
| 625 | 
 | 
| 626 |   _ysh-parse-error '= 9.9 Mi; echo'
 | 
| 627 | 
 | 
| 628 |   # This is confusing, could disallow, or just rely on users not to type it
 | 
| 629 |   _ysh-parse-error '= 9.9e-1 Mi; echo'
 | 
| 630 | 
 | 
| 631 |   # I don't like this, but it follows lexing rules I guess
 | 
| 632 |   _ysh-parse-error '= 100Mi'
 | 
| 633 | 
 | 
| 634 |   _ysh-parse-error '= [100 Mi, 200 Mi]'
 | 
| 635 | 
 | 
| 636 |   _ysh-parse-error '= {[42 Ki]: 43 Ki}'
 | 
| 637 | }
 | 
| 638 | 
 | 
| 639 | test-type-expr() {
 | 
| 640 |   # This is nicer
 | 
| 641 |   _ysh-should-parse 'var x: Int = f()'
 | 
| 642 | 
 | 
| 643 |   # But colon is optional
 | 
| 644 |   _ysh-should-parse 'var x Int = f()'
 | 
| 645 | 
 | 
| 646 |   # Colon is noisy here because we have semi-colons
 | 
| 647 |   _ysh-should-parse 'proc p (; x Int, y Int; ) { echo hi }'
 | 
| 648 | 
 | 
| 649 |   _ysh-should-parse 'func f (x Int, y Int; z Int = 0) { echo hi }'
 | 
| 650 | 
 | 
| 651 |   # Hm should these be allowed, but discouraged?
 | 
| 652 |   #_ysh-should-parse 'func f (x Int, y Int; z: Int = 0) { echo hi }'
 | 
| 653 |   #_ysh-should-parse 'proc p (; x: Int, y: Int;) { echo hi }'
 | 
| 654 | }
 | 
| 655 | 
 | 
| 656 | test-no-const() {
 | 
| 657 |   _ysh-should-parse 'const x = 42'
 | 
| 658 | 
 | 
| 659 |   # Must be at the top level
 | 
| 660 |   _ysh-parse-error '
 | 
| 661 |   proc p {
 | 
| 662 |     const x = 42
 | 
| 663 |   }'
 | 
| 664 | 
 | 
| 665 |   _ysh-parse-error '
 | 
| 666 |   func f() {
 | 
| 667 |     const x = 42
 | 
| 668 |   }'
 | 
| 669 | }
 | 
| 670 | 
 | 
| 671 | test-fat-arrow() {
 | 
| 672 |   _ysh-should-parse 'var x = s => trim()'
 | 
| 673 |   _ysh-should-parse 'func f(x Int) => List[Int] { echo hi }'
 | 
| 674 | }
 | 
| 675 | 
 | 
| 676 | # Backslash in UNQUOTED context
 | 
| 677 | test-parse-backslash() {
 | 
| 678 |   _ysh-should-parse 'echo \('
 | 
| 679 |   _ysh-should-parse 'echo \;'
 | 
| 680 |   _ysh-should-parse 'echo ~'
 | 
| 681 |   _ysh-should-parse 'echo \!'  # history?
 | 
| 682 | 
 | 
| 683 |   _ysh-should-parse 'echo \%'  # job ID?  I feel like '%' is better
 | 
| 684 |   _ysh-should-parse 'echo \#'  # comment
 | 
| 685 | 
 | 
| 686 |   _ysh-parse-error 'echo \.'
 | 
| 687 |   _ysh-parse-error 'echo \-'
 | 
| 688 |   _ysh-parse-error 'echo \/'
 | 
| 689 | 
 | 
| 690 |   _ysh-parse-error 'echo \a'
 | 
| 691 |   _ysh-parse-error 'echo \Z'
 | 
| 692 |   _ysh-parse-error 'echo \0'
 | 
| 693 |   _ysh-parse-error 'echo \9'
 | 
| 694 | 
 | 
| 695 |   _osh-should-parse 'echo \. \- \/ \a \Z \0 \9'
 | 
| 696 | }
 | 
| 697 | 
 | 
| 698 | test-make-these-nicer() {
 | 
| 699 |   # expects expression on right
 | 
| 700 |   _ysh-parse-error '='
 | 
| 701 |   _ysh-parse-error 'call'
 | 
| 702 | 
 | 
| 703 |   # What about \u{123} parse errors
 | 
| 704 |   # I get a warning now, but parse_backslash should give a syntax error
 | 
| 705 |   # _ysh-parse-error "x = c'\\uz'"
 | 
| 706 | 
 | 
| 707 |   # Dict pair split
 | 
| 708 |   _ysh-parse-error 'const d = { name:
 | 
| 709 | 42 }'
 | 
| 710 | 
 | 
| 711 |   #_ysh-parse-error ' d = %{}'
 | 
| 712 | }
 | 
| 713 | 
 | 
| 714 | test-var-decl() {
 | 
| 715 |   _ysh-parse-error '
 | 
| 716 |   proc p(x) {
 | 
| 717 |     echo hi
 | 
| 718 |     var x = 2  # Cannot redeclare param
 | 
| 719 |   }
 | 
| 720 |   '
 | 
| 721 | 
 | 
| 722 |   _ysh-parse-error '
 | 
| 723 |   proc p {
 | 
| 724 |     var x = 1
 | 
| 725 |     echo hi
 | 
| 726 |     var x = 2  # Cannot redeclare local
 | 
| 727 |   }
 | 
| 728 |   '
 | 
| 729 | 
 | 
| 730 |   _ysh-parse-error '
 | 
| 731 |   proc p(x, :out) {
 | 
| 732 |     var out = 2   # Cannot redeclare out param
 | 
| 733 |   }
 | 
| 734 |   '
 | 
| 735 | 
 | 
| 736 |   _ysh-parse-error '
 | 
| 737 |   proc p {
 | 
| 738 |     var out = 2   # Cannot redeclare out param
 | 
| 739 |     cd /tmp { 
 | 
| 740 |       var out = 3
 | 
| 741 |     }
 | 
| 742 |   }
 | 
| 743 |   '
 | 
| 744 | 
 | 
| 745 |   _ysh-should-parse '
 | 
| 746 |   var x = 1
 | 
| 747 |   proc p {
 | 
| 748 |     echo hi
 | 
| 749 |     var x = 2
 | 
| 750 |   }
 | 
| 751 | 
 | 
| 752 |   proc p2 {
 | 
| 753 |     var x = 3
 | 
| 754 |   }
 | 
| 755 |   '
 | 
| 756 | }
 | 
| 757 | 
 | 
| 758 | test-setvar() {
 | 
| 759 |   _ysh-should-parse '
 | 
| 760 |   proc p(x) {
 | 
| 761 |     var y = 1
 | 
| 762 |     setvar y = 42
 | 
| 763 |   }
 | 
| 764 |   '
 | 
| 765 | 
 | 
| 766 |   _ysh-parse-error '
 | 
| 767 |   proc p(x) {
 | 
| 768 |     var y = 1
 | 
| 769 |     setvar L = "L"  # ERROR: not declared
 | 
| 770 |   }
 | 
| 771 |   '
 | 
| 772 | 
 | 
| 773 |   _ysh-parse-error '
 | 
| 774 |   proc p(x) {
 | 
| 775 |     var y = 1
 | 
| 776 |     setvar L[0] = "L"  # ERROR: not declared
 | 
| 777 |   }
 | 
| 778 |   '
 | 
| 779 | 
 | 
| 780 |   _ysh-parse-error '
 | 
| 781 |   proc p(x) {
 | 
| 782 |     var y = 1
 | 
| 783 |     setvar d.key = "v"  # ERROR: not declared
 | 
| 784 |   }
 | 
| 785 |   '
 | 
| 786 | 
 | 
| 787 |   _ysh-should-parse '
 | 
| 788 |   proc p(x) {
 | 
| 789 |     setvar x = "X"  # is mutating params allowed?  I guess why not.
 | 
| 790 |   }
 | 
| 791 |   '
 | 
| 792 | }
 | 
| 793 | 
 | 
| 794 | test-ysh-case() {
 | 
| 795 |   _ysh-should-parse '
 | 
| 796 |   case (x) {
 | 
| 797 |     (else) { = 1; }
 | 
| 798 |   }
 | 
| 799 |   '
 | 
| 800 | 
 | 
| 801 |   _ysh-should-parse '
 | 
| 802 |   var myexpr = ^[123]
 | 
| 803 | 
 | 
| 804 |   case (123) {
 | 
| 805 |     (myexpr) { echo 1 }
 | 
| 806 |   }
 | 
| 807 |   '
 | 
| 808 | 
 | 
| 809 |   _ysh-should-parse '
 | 
| 810 |   case (x) {
 | 
| 811 |     (else) { echo 1 }
 | 
| 812 |   }
 | 
| 813 |   '
 | 
| 814 | 
 | 
| 815 |   _ysh-should-parse '
 | 
| 816 |   case (x) {
 | 
| 817 |     (else) { = 1 }
 | 
| 818 |   }
 | 
| 819 |   '
 | 
| 820 | 
 | 
| 821 |   _ysh-should-parse '
 | 
| 822 |   case (x) {
 | 
| 823 |     (else) { = 1 } 
 | 
| 824 |  
 | 
| 825 |   }
 | 
| 826 |   '
 | 
| 827 | 
 | 
| 828 |   _ysh-should-parse '
 | 
| 829 |   case (x) {
 | 
| 830 |     (else) { = 1 }  # Comment
 | 
| 831 |   }
 | 
| 832 |   '
 | 
| 833 | 
 | 
| 834 |   _ysh-should-parse '
 | 
| 835 |   case (3) {
 | 
| 836 |     (3) { echo hi }
 | 
| 837 |     # comment line
 | 
| 838 |   }
 | 
| 839 |   '
 | 
| 840 | 
 | 
| 841 |   _ysh-should-parse '
 | 
| 842 |   case (x) {
 | 
| 843 |     (else) { echo 1 } 
 | 
| 844 |   }
 | 
| 845 |   '
 | 
| 846 | 
 | 
| 847 |   _ysh-should-parse '
 | 
| 848 |   case (foo) { (else) { echo } }
 | 
| 849 |   '
 | 
| 850 | 
 | 
| 851 |   _ysh-should-parse '
 | 
| 852 |   case (foo) {
 | 
| 853 |     *.py { echo "python" }
 | 
| 854 |   }
 | 
| 855 |   '
 | 
| 856 | 
 | 
| 857 |   _ysh-should-parse '
 | 
| 858 |   case (foo) {
 | 
| 859 |     (obj.attr) { echo "python" }
 | 
| 860 |   }
 | 
| 861 |   '
 | 
| 862 | 
 | 
| 863 |   _ysh-should-parse '
 | 
| 864 |   case (foo) {
 | 
| 865 |     (0) { echo "python" }
 | 
| 866 |   }
 | 
| 867 |   '
 | 
| 868 | 
 | 
| 869 |   _ysh-should-parse '
 | 
| 870 |   case (foo) {
 | 
| 871 |     ("main.py") { echo "python" }
 | 
| 872 |   }
 | 
| 873 |   '
 | 
| 874 | 
 | 
| 875 |   # Various multi-line cases
 | 
| 876 |   if false; then # TODO: fixme, this is in the vein of the `if(x)` error
 | 
| 877 |     _ysh-should-parse '
 | 
| 878 |     case (foo){("main.py"){ echo "python" } }
 | 
| 879 |     '
 | 
| 880 |   fi
 | 
| 881 |   _ysh-should-parse '
 | 
| 882 |   case (foo) { ("main.py") { echo "python" } }
 | 
| 883 |   '
 | 
| 884 |   _ysh-should-parse '
 | 
| 885 |   case (foo) {
 | 
| 886 |     ("main.py") {
 | 
| 887 |       echo "python" } }'
 | 
| 888 |   _ysh-should-parse '
 | 
| 889 |   case (foo) {
 | 
| 890 |     ("main.py") {
 | 
| 891 |       echo "python" }
 | 
| 892 |   }
 | 
| 893 |   '
 | 
| 894 |   _ysh-should-parse '
 | 
| 895 |   case (foo) {
 | 
| 896 |     ("main.py") { echo "python"
 | 
| 897 |     }
 | 
| 898 |   }
 | 
| 899 |   '
 | 
| 900 |   _ysh-should-parse '
 | 
| 901 |   case (foo) {
 | 
| 902 |     ("main.py") {
 | 
| 903 |       echo "python"
 | 
| 904 |     }
 | 
| 905 |   }
 | 
| 906 |   '
 | 
| 907 | 
 | 
| 908 |   # Example valid cases from grammar brain-storming
 | 
| 909 |   _ysh-should-parse '
 | 
| 910 |   case (add(10, 32)) {
 | 
| 911 |     (40 + 2) { echo Found the answer }
 | 
| 912 |     (else) { echo Incorrect
 | 
| 913 |     }
 | 
| 914 |   }
 | 
| 915 |   '
 | 
| 916 | 
 | 
| 917 |   _ysh-should-parse "
 | 
| 918 |   case (file) {
 | 
| 919 |     / dot* '.py' / {
 | 
| 920 |       echo Python
 | 
| 921 |     }
 | 
| 922 | 
 | 
| 923 |     / dot* ('.cc' | '.h') /
 | 
| 924 |     {
 | 
| 925 |       echo C++
 | 
| 926 |     }
 | 
| 927 |   }
 | 
| 928 |   "
 | 
| 929 |   _ysh-should-parse '
 | 
| 930 |   case (lang) {
 | 
| 931 |       en-US
 | 
| 932 |     | en-CA
 | 
| 933 |     | en-UK {
 | 
| 934 |       echo Hello
 | 
| 935 |     }
 | 
| 936 |     fr-FR |
 | 
| 937 |     fr-CA {
 | 
| 938 |       echo Bonjour
 | 
| 939 |     }
 | 
| 940 | 
 | 
| 941 | 
 | 
| 942 | 
 | 
| 943 | 
 | 
| 944 | 
 | 
| 945 |     (else) {
 | 
| 946 |       echo o/
 | 
| 947 |     }
 | 
| 948 |   }
 | 
| 949 |   '
 | 
| 950 | 
 | 
| 951 |   _ysh-should-parse '
 | 
| 952 |   case (num) {
 | 
| 953 |     (1) | (2) {
 | 
| 954 |       echo number
 | 
| 955 |     }
 | 
| 956 |   }
 | 
| 957 |   '
 | 
| 958 | 
 | 
| 959 |   _ysh-should-parse '
 | 
| 960 |   case (num) {
 | 
| 961 |       (1) | (2) | (3)
 | 
| 962 |     | (4) | (5) {
 | 
| 963 |       echo small
 | 
| 964 |     }
 | 
| 965 | 
 | 
| 966 |     (else) {
 | 
| 967 |       echo large
 | 
| 968 |     }
 | 
| 969 |   }
 | 
| 970 |   '
 | 
| 971 | 
 | 
| 972 |   # Example invalid cases from grammar brain-storming
 | 
| 973 |   _ysh-parse-error '
 | 
| 974 |   case
 | 
| 975 |   (add(10, 32)) {
 | 
| 976 |       (40 + 2) { echo Found the answer }
 | 
| 977 |       (else) { echo Incorrect }
 | 
| 978 |   }
 | 
| 979 |   '
 | 
| 980 |   _ysh-parse-error "
 | 
| 981 |   case (file)
 | 
| 982 |   {
 | 
| 983 |     ('README') | / dot* '.md' / { echo Markdown }
 | 
| 984 |   }
 | 
| 985 |   "
 | 
| 986 |   _ysh-parse-error '
 | 
| 987 |   case (file)
 | 
| 988 |   {
 | 
| 989 |     {
 | 
| 990 |       echo Python
 | 
| 991 |     }
 | 
| 992 |   }
 | 
| 993 |   '
 | 
| 994 |   _ysh-parse-error '
 | 
| 995 |   case (file)
 | 
| 996 |   {
 | 
| 997 |     cc h {
 | 
| 998 |       echo C++
 | 
| 999 |     }
 | 
| 1000 |   }
 | 
| 1001 |   '
 | 
| 1002 |   _ysh-parse-error "
 | 
| 1003 |   case (lang) {
 | 
| 1004 |       en-US
 | 
| 1005 |     | ('en-CA')
 | 
| 1006 |     | / 'en-UK' / {
 | 
| 1007 |       echo Hello
 | 
| 1008 |     }
 | 
| 1009 |   }
 | 
| 1010 |   "
 | 
| 1011 |   _ysh-parse-error '
 | 
| 1012 |   case (lang) {
 | 
| 1013 |     else) {
 | 
| 1014 |       echo o/
 | 
| 1015 |     }
 | 
| 1016 |   }
 | 
| 1017 |   '
 | 
| 1018 |   _ysh-parse-error '
 | 
| 1019 |   case (num) {
 | 
| 1020 |       (1) | (2) | (3)
 | 
| 1021 |     | (4) | (5) {
 | 
| 1022 |       echo small
 | 
| 1023 |     }
 | 
| 1024 | 
 | 
| 1025 |     (6) | (else) {
 | 
| 1026 |       echo large
 | 
| 1027 |     }
 | 
| 1028 |   }
 | 
| 1029 |   '
 | 
| 1030 | 
 | 
| 1031 |   _ysh-parse-error '
 | 
| 1032 |   case $foo {
 | 
| 1033 |     ("main.py") {
 | 
| 1034 |       echo "python"
 | 
| 1035 |     }
 | 
| 1036 |   }
 | 
| 1037 |   '
 | 
| 1038 | 
 | 
| 1039 |   # Newline not allowed, because it isn't in for, if, while, etc.
 | 
| 1040 |   _ysh-parse-error '
 | 
| 1041 |   case (x)
 | 
| 1042 |   {
 | 
| 1043 |     *.py { echo "python" }
 | 
| 1044 |   }
 | 
| 1045 |   '
 | 
| 1046 | 
 | 
| 1047 |   _ysh-parse-error '
 | 
| 1048 |   case (foo) in
 | 
| 1049 |     *.py {
 | 
| 1050 |       echo "python"
 | 
| 1051 |     }
 | 
| 1052 |   esac
 | 
| 1053 |   '
 | 
| 1054 | 
 | 
| 1055 |   _ysh-parse-error '
 | 
| 1056 |   case $foo {
 | 
| 1057 |     bar) {
 | 
| 1058 |       echo "python"
 | 
| 1059 |     }
 | 
| 1060 |   }
 | 
| 1061 |   '
 | 
| 1062 | 
 | 
| 1063 |   _ysh-parse-error '
 | 
| 1064 |   case (x) {
 | 
| 1065 |     {
 | 
| 1066 |       echo "python"
 | 
| 1067 |     }
 | 
| 1068 |   }
 | 
| 1069 |   '
 | 
| 1070 | 
 | 
| 1071 |   _ysh-parse-error '
 | 
| 1072 |   case (x {
 | 
| 1073 |     *.py { echo "python" }
 | 
| 1074 |   }
 | 
| 1075 |   '
 | 
| 1076 | 
 | 
| 1077 |   _ysh-parse-error '
 | 
| 1078 |   case (x) {
 | 
| 1079 |     *.py) { echo "python" }
 | 
| 1080 |   }
 | 
| 1081 |   '
 | 
| 1082 | 
 | 
| 1083 |   _ysh-should-parse "case (x) { word { echo word; } (3) { echo expr; } /'eggex'/ { echo eggex; } }"
 | 
| 1084 | 
 | 
| 1085 |   _ysh-should-parse "
 | 
| 1086 | case (x) {
 | 
| 1087 |   word    { echo word; } (3)     { echo expr; } /'eggex'/ { echo eggex; } }"
 | 
| 1088 | 
 | 
| 1089 |   _ysh-should-parse "
 | 
| 1090 | case (x) {
 | 
| 1091 |   word    { echo word; }
 | 
| 1092 |   (3)     { echo expr; } /'eggex'/ { echo eggex; } }"
 | 
| 1093 | 
 | 
| 1094 |   _ysh-should-parse "
 | 
| 1095 | case (x) {
 | 
| 1096 |   word    { echo word; }
 | 
| 1097 |   (3)     { echo expr; }
 | 
| 1098 |   /'eggex'/ { echo eggex; } }"
 | 
| 1099 | 
 | 
| 1100 |   _ysh-should-parse "
 | 
| 1101 | case (x) {
 | 
| 1102 |   word    { echo word; }
 | 
| 1103 |   (3)     { echo expr; }
 | 
| 1104 |   /'eggex'/ { echo eggex; }
 | 
| 1105 | }"
 | 
| 1106 | 
 | 
| 1107 |   # No leading space
 | 
| 1108 |   _ysh-should-parse "
 | 
| 1109 | case (x) {
 | 
| 1110 | word    { echo word; }
 | 
| 1111 | (3)     { echo expr; }
 | 
| 1112 | /'eggex'/ { echo eggex; }
 | 
| 1113 | }"
 | 
| 1114 | }
 | 
| 1115 | 
 | 
| 1116 | test-ysh-for() {
 | 
| 1117 |   _ysh-should-parse '
 | 
| 1118 |   for x in (obj) {
 | 
| 1119 |     echo $x
 | 
| 1120 |   }
 | 
| 1121 |   '
 | 
| 1122 | 
 | 
| 1123 |   _ysh-parse-error '
 | 
| 1124 |   for x in (obj); do
 | 
| 1125 |     echo $x
 | 
| 1126 |   done
 | 
| 1127 |   '
 | 
| 1128 | 
 | 
| 1129 |   _ysh-should-parse '
 | 
| 1130 |   for x, y in SPAM EGGS; do
 | 
| 1131 |     echo $x
 | 
| 1132 |   done
 | 
| 1133 |   '
 | 
| 1134 | 
 | 
| 1135 |   # Bad loop variable name
 | 
| 1136 |   _ysh-parse-error '
 | 
| 1137 |   for x-y in SPAM EGGS; do
 | 
| 1138 |     echo $x
 | 
| 1139 |   done
 | 
| 1140 |   '
 | 
| 1141 | 
 | 
| 1142 |   # Too many indices
 | 
| 1143 |   _ysh-parse-error '
 | 
| 1144 |   for x, y, z in SPAM EGGS; do
 | 
| 1145 |     echo $x
 | 
| 1146 |   done
 | 
| 1147 |   '
 | 
| 1148 | 
 | 
| 1149 |   _ysh-parse-error '
 | 
| 1150 |   for w, x, y, z in SPAM EGGS; do
 | 
| 1151 |     echo $x
 | 
| 1152 |   done
 | 
| 1153 |   '
 | 
| 1154 | 
 | 
| 1155 |   # Old style
 | 
| 1156 |   _ysh-should-parse '
 | 
| 1157 |   for x, y in SPAM EGGS
 | 
| 1158 |   do
 | 
| 1159 |     echo $x
 | 
| 1160 |   done
 | 
| 1161 |   '
 | 
| 1162 | 
 | 
| 1163 |   # for shell compatibility, allow this
 | 
| 1164 |   _ysh-should-parse 'for const in (x) { echo $var }'
 | 
| 1165 | }
 | 
| 1166 | 
 | 
| 1167 | test-for-parse-bare-word() {
 | 
| 1168 |   _ysh-parse-error '
 | 
| 1169 |   for x in bare {
 | 
| 1170 |     echo $x
 | 
| 1171 |   }
 | 
| 1172 |   '
 | 
| 1173 | 
 | 
| 1174 |   _ysh-should-parse '
 | 
| 1175 |   for x in a b {
 | 
| 1176 |     echo $x
 | 
| 1177 |   }
 | 
| 1178 |   '
 | 
| 1179 | 
 | 
| 1180 |   _ysh-should-parse '
 | 
| 1181 |   for x in *.py {
 | 
| 1182 |     echo $x
 | 
| 1183 |   }
 | 
| 1184 |   '
 | 
| 1185 | 
 | 
| 1186 |   _ysh-should-parse '
 | 
| 1187 |   for x in "quoted" {
 | 
| 1188 |     echo $x
 | 
| 1189 |   }
 | 
| 1190 |   '
 | 
| 1191 | }
 | 
| 1192 | 
 | 
| 1193 | test-for() {
 | 
| 1194 |   # Technically we could switch to a different lexer mode here, but it seems
 | 
| 1195 |   # easy enough to reuse the Id.Redir_LessGreat token
 | 
| 1196 |   _ysh-parse-error '
 | 
| 1197 |   for x in <> {
 | 
| 1198 |     echo $x
 | 
| 1199 |   }
 | 
| 1200 |   '
 | 
| 1201 | 
 | 
| 1202 |   _ysh-parse-error '
 | 
| 1203 |   for x in <>
 | 
| 1204 |   {
 | 
| 1205 |     echo $x
 | 
| 1206 |   }
 | 
| 1207 |   '
 | 
| 1208 | 
 | 
| 1209 |   _ysh-parse-error '
 | 
| 1210 |   for x in < {
 | 
| 1211 |     echo $x
 | 
| 1212 |   }
 | 
| 1213 |   '
 | 
| 1214 | 
 | 
| 1215 |   # Space between < >
 | 
| 1216 |   _ysh-parse-error '
 | 
| 1217 |   for x in < > {
 | 
| 1218 |     echo $x
 | 
| 1219 |   }
 | 
| 1220 |   '
 | 
| 1221 | }
 | 
| 1222 | 
 | 
| 1223 | test-bug-1118() {
 | 
| 1224 |   # Originally pointed at 'for'
 | 
| 1225 |   _ysh-parse-error '
 | 
| 1226 |   var snippets = [{status: 42}]
 | 
| 1227 |   for snippet in (snippets) {
 | 
| 1228 |     if (snippet["status"] === 0) {
 | 
| 1229 |       echo hi
 | 
| 1230 |     }
 | 
| 1231 | 
 | 
| 1232 |     # The $ causes a weird error
 | 
| 1233 |     if ($snippet["status"] === 0) {
 | 
| 1234 |       echo hi
 | 
| 1235 |     }
 | 
| 1236 |   }
 | 
| 1237 |   '
 | 
| 1238 | 
 | 
| 1239 |   # Issue #1118
 | 
| 1240 |   # pointed at 'var' in count
 | 
| 1241 |   _ysh-parse-error '
 | 
| 1242 |   var content = [ 1, 2, 4 ]
 | 
| 1243 |   var count = 0
 | 
| 1244 | 
 | 
| 1245 |   # The $ causes a weird error
 | 
| 1246 |   while (count < $len(content)) {
 | 
| 1247 |     setvar count += 1
 | 
| 1248 |   }
 | 
| 1249 |   '
 | 
| 1250 | }
 | 
| 1251 | 
 | 
| 1252 | test-bug-1850() {
 | 
| 1253 |   _ysh-should-parse 'pp line (42); pp line (43)'
 | 
| 1254 |   #_osh-should-parse 'pp line (42); pp line (43)'
 | 
| 1255 | 
 | 
| 1256 |   # Extra word is bad
 | 
| 1257 |   _ysh-parse-error 'pp line (42) extra'
 | 
| 1258 | 
 | 
| 1259 |   # Bug -- newline or block should come after arg list
 | 
| 1260 |   _ysh-parse-error 'pp line (42), echo'
 | 
| 1261 | 
 | 
| 1262 |   # This properly checks a similar error.  It's in a word.
 | 
| 1263 |   _ysh-parse-error 'pp line @(echo), echo'
 | 
| 1264 | 
 | 
| 1265 |   # Common cases
 | 
| 1266 |   _ysh-should-parse 'pp line (42)'
 | 
| 1267 |   _ysh-should-parse 'pp line (42) '
 | 
| 1268 |   _ysh-should-parse 'pp line (42);'
 | 
| 1269 |   _ysh-should-parse 'pp line (42) { echo hi }'
 | 
| 1270 | 
 | 
| 1271 |   # Original bug
 | 
| 1272 | 
 | 
| 1273 |   # Accidental comma instead of ;
 | 
| 1274 |   # Wow this is parsed horribly - (42) replaced (43)
 | 
| 1275 |   _ysh-parse-error 'pp line (42), pp line (43)'
 | 
| 1276 | }
 | 
| 1277 | 
 | 
| 1278 | test-bug-1850-more() {
 | 
| 1279 |   ### Regression
 | 
| 1280 | 
 | 
| 1281 |   _ysh-parse-error 'assert (42)extra'
 | 
| 1282 |   _ysh-parse-error 'assert (42) extra'
 | 
| 1283 | 
 | 
| 1284 |   _ysh-parse-error 'assert [42]extra'
 | 
| 1285 |   _ysh-parse-error 'assert [42] extra'
 | 
| 1286 | }
 | 
| 1287 | 
 | 
| 1288 | test-command-simple-more() {
 | 
| 1289 |   _ysh-should-parse 'foo=1'
 | 
| 1290 | 
 | 
| 1291 |   _ysh-parse-error 'foo=1 >out (42)'
 | 
| 1292 | 
 | 
| 1293 |   _ysh-parse-error 'foo=1 (42)'
 | 
| 1294 | 
 | 
| 1295 |   _ysh-should-parse 'foo=1 cmd (42)'
 | 
| 1296 | 
 | 
| 1297 |   _ysh-should-parse 'foo=1 cmd >out (42)'
 | 
| 1298 | }
 | 
| 1299 | 
 | 
| 1300 | test-proc-args() {
 | 
| 1301 |   _osh-should-parse 'json write (x)'
 | 
| 1302 | 
 | 
| 1303 |   _osh-should-parse 'echo $(json write (x))'  # relies on lexer.PushHint()
 | 
| 1304 | 
 | 
| 1305 |   # nested expr -> command -> expr
 | 
| 1306 |   _osh-should-parse 'var result = $(json write (x))'
 | 
| 1307 | 
 | 
| 1308 |   _osh-should-parse 'json write (x, y); echo hi'
 | 
| 1309 | 
 | 
| 1310 |   # named arg
 | 
| 1311 |   _osh-should-parse '
 | 
| 1312 | json write (x, name = "value")
 | 
| 1313 | echo hi
 | 
| 1314 | '
 | 
| 1315 | 
 | 
| 1316 |   # with block on same line
 | 
| 1317 |   _ysh-should-parse 'json write (x) { echo hi }'
 | 
| 1318 | 
 | 
| 1319 |   # with block
 | 
| 1320 |   _ysh-should-parse '
 | 
| 1321 | json write (x) {
 | 
| 1322 |   echo hi
 | 
| 1323 | }'
 | 
| 1324 | 
 | 
| 1325 |   # multiple lines
 | 
| 1326 |   _osh-should-parse 'json write (
 | 
| 1327 |     x,
 | 
| 1328 |     y,
 | 
| 1329 |     z
 | 
| 1330 |   )'
 | 
| 1331 | 
 | 
| 1332 |   # can't be empty
 | 
| 1333 |   _ysh-parse-error 'json write ()'
 | 
| 1334 |   _ysh-parse-error 'json write ( )'
 | 
| 1335 | 
 | 
| 1336 |   # should have a space
 | 
| 1337 |   _ysh-parse-error 'json write(x)'
 | 
| 1338 |   _ysh-parse-error 'json write()'
 | 
| 1339 |   _ysh-parse-error 'f(x)'  # test short name
 | 
| 1340 | }
 | 
| 1341 | 
 | 
| 1342 | test-eggex-capture() {
 | 
| 1343 |   _ysh-should-parse '= / d+ /'
 | 
| 1344 |   #_ysh-should-parse '= / <d+ : date> /'
 | 
| 1345 |   _ysh-should-parse '= / < capture d+ as date > /'
 | 
| 1346 |   _ysh-should-parse '= / < capture d+ as date: Int > /'
 | 
| 1347 | 
 | 
| 1348 |   # These keywords are taken in regular expressions, I guess that's OK.
 | 
| 1349 |   _ysh-parse-error 'var capture = 42'
 | 
| 1350 |   _ysh-parse-error 'var as = 42'
 | 
| 1351 | }
 | 
| 1352 | 
 | 
| 1353 | 
 | 
| 1354 | test-eggex-flags() {
 | 
| 1355 |   _ysh-should-parse '= / d+ ; reg_icase /'
 | 
| 1356 |   _ysh-should-parse '= / d+ ; i /'  # shortcut
 | 
| 1357 | 
 | 
| 1358 |   # can't negate these
 | 
| 1359 |   _ysh-parse-error '= / d+ ; !i /'
 | 
| 1360 | 
 | 
| 1361 |   # typo should be parse error
 | 
| 1362 |   _ysh-parse-error '= / d+ ; reg_oops /'
 | 
| 1363 | 
 | 
| 1364 |   # PCRE should not validate
 | 
| 1365 |   _ysh-should-parse '= / d+ ; !i; PCRE /'
 | 
| 1366 |   _ysh-should-parse '= / d+ ; reg_oops; PCRE /'
 | 
| 1367 | 
 | 
| 1368 |   # ERE means is the default; it's POSIX ERE
 | 
| 1369 |   # Other option is PCRE
 | 
| 1370 |   _ysh-should-parse '= / d+ ; i reg_newline ; ERE /'
 | 
| 1371 |   _ysh-should-parse '= / d+ ; ; ERE /'
 | 
| 1372 | 
 | 
| 1373 |   # trailing ; is OK
 | 
| 1374 |   _ysh-should-parse '= / d+ ; /'
 | 
| 1375 | 
 | 
| 1376 |   # doesn't make sense
 | 
| 1377 |   _ysh-parse-error '= / d+ ; ; /'
 | 
| 1378 |   _ysh-parse-error '= / d+ ; ; ; /'
 | 
| 1379 | }
 | 
| 1380 | 
 | 
| 1381 | test-string-literals() {
 | 
| 1382 |   _ysh-should-parse "echo r'hi';"
 | 
| 1383 |   #_ysh-parse-error "echo r'hi'bad"
 | 
| 1384 | 
 | 
| 1385 |   _ysh-should-parse "echo u'hi'"
 | 
| 1386 |   _ysh-should-parse "(echo u'hi')"
 | 
| 1387 | 
 | 
| 1388 |   _ysh-parse-error "echo b'hi'trailing"
 | 
| 1389 |   _ysh-parse-error "echo b'hi'#notcomment"
 | 
| 1390 | 
 | 
| 1391 |   # This is valid shell, but not a comment
 | 
| 1392 |   _ysh-should-parse "echo 'hi'#notcomment"
 | 
| 1393 | 
 | 
| 1394 | }
 | 
| 1395 | 
 | 
| 1396 | test-multiline-string() {
 | 
| 1397 |   _ysh-should-parse "echo u'''
 | 
| 1398 | hi
 | 
| 1399 | '''
 | 
| 1400 | "
 | 
| 1401 |   _ysh-should-parse "echo b'''
 | 
| 1402 | hi
 | 
| 1403 | '''
 | 
| 1404 | "
 | 
| 1405 | 
 | 
| 1406 |   _ysh-parse-error "echo b'''
 | 
| 1407 | hi
 | 
| 1408 | ''
 | 
| 1409 | "
 | 
| 1410 | 
 | 
| 1411 |   _ysh-parse-error "echo r'''
 | 
| 1412 | hi
 | 
| 1413 | '''bad
 | 
| 1414 | "
 | 
| 1415 | 
 | 
| 1416 |   _ysh-parse-error "echo u'''
 | 
| 1417 | hi
 | 
| 1418 | '''bad
 | 
| 1419 | "
 | 
| 1420 | 
 | 
| 1421 |   _ysh-parse-error 'echo """
 | 
| 1422 | hi
 | 
| 1423 | """bad
 | 
| 1424 | '
 | 
| 1425 | }
 | 
| 1426 | 
 | 
| 1427 | test-bug-1826() {
 | 
| 1428 |   #return
 | 
| 1429 | 
 | 
| 1430 |   read -r code_str << 'EOF'
 | 
| 1431 | echo b'\xff'
 | 
| 1432 | EOF
 | 
| 1433 | 
 | 
| 1434 |   _ysh-parse-error "$code_str"
 | 
| 1435 | 
 | 
| 1436 |   read -r code_str << 'EOF'
 | 
| 1437 | var s = b'\xff'
 | 
| 1438 | EOF
 | 
| 1439 | 
 | 
| 1440 |   _ysh-parse-error "$code_str"
 | 
| 1441 | 
 | 
| 1442 |   # Backslash ending the file
 | 
| 1443 | 
 | 
| 1444 |   read -r code_str << 'EOF'
 | 
| 1445 | echo b'\
 | 
| 1446 | EOF
 | 
| 1447 |   echo "[$code_str]"
 | 
| 1448 | 
 | 
| 1449 |   _ysh-parse-error "$code_str"
 | 
| 1450 | 
 | 
| 1451 |   read -r code_str << 'EOF'
 | 
| 1452 | var s = b'\
 | 
| 1453 | EOF
 | 
| 1454 |   echo "[$code_str]"
 | 
| 1455 | 
 | 
| 1456 |   _ysh-parse-error "$code_str"
 | 
| 1457 | 
 | 
| 1458 |   read -r code_str << 'EOF'
 | 
| 1459 | var s = $'\
 | 
| 1460 | EOF
 | 
| 1461 |   echo "[$code_str]"
 | 
| 1462 | 
 | 
| 1463 |   _ysh-parse-error "$code_str"
 | 
| 1464 | }
 | 
| 1465 | 
 | 
| 1466 | test-ysh_c_strings() {
 | 
| 1467 |   # bash syntax
 | 
| 1468 |   _osh-should-parse-here <<'EOF'
 | 
| 1469 | echo $'\u03bc'
 | 
| 1470 | EOF
 | 
| 1471 | 
 | 
| 1472 |   # Extension not allowed
 | 
| 1473 |   _ysh-parse-error-here <<'EOF'
 | 
| 1474 | echo $'\u{03bc}'
 | 
| 1475 | EOF
 | 
| 1476 | 
 | 
| 1477 |   # Bad syntax
 | 
| 1478 |   _ysh-parse-error-here <<'EOF'
 | 
| 1479 | echo $'\u{03bc'
 | 
| 1480 | EOF
 | 
| 1481 | 
 | 
| 1482 |   # Expression mode
 | 
| 1483 |   _ysh-parse-error-here <<'EOF'
 | 
| 1484 | const bad = $'\u{03bc'
 | 
| 1485 | EOF
 | 
| 1486 | 
 | 
| 1487 |   # Test single quoted
 | 
| 1488 |   _osh-should-parse-here <<'EOF'
 | 
| 1489 | echo $'\z'
 | 
| 1490 | EOF
 | 
| 1491 |   _ysh-parse-error-here <<'EOF'
 | 
| 1492 | echo $'\z'
 | 
| 1493 | EOF
 | 
| 1494 |   # Expression mode
 | 
| 1495 |   _ysh-parse-error-here <<'EOF'
 | 
| 1496 | const bad = $'\z'
 | 
| 1497 | EOF
 | 
| 1498 | 
 | 
| 1499 |   # Octal not allowed
 | 
| 1500 |   _osh-should-parse-here <<'EOF'
 | 
| 1501 | echo $'\101'
 | 
| 1502 | EOF
 | 
| 1503 |   _ysh-parse-error-here <<'EOF'
 | 
| 1504 | const bad = $'\101'
 | 
| 1505 | EOF
 | 
| 1506 | 
 | 
| 1507 |   # \xH not allowed
 | 
| 1508 |   _ysh-parse-error-here <<'EOF'
 | 
| 1509 | const bad = c'\xf'
 | 
| 1510 | EOF
 | 
| 1511 | }
 | 
| 1512 | 
 | 
| 1513 | test-bug_1825_backslashes() {
 | 
| 1514 |   # Single backslash is accepted in OSH
 | 
| 1515 |   _osh-should-parse-here <<'EOF'
 | 
| 1516 | echo $'trailing\
 | 
| 1517 | '
 | 
| 1518 | EOF
 | 
| 1519 | 
 | 
| 1520 |   # Double backslash is right in YSH
 | 
| 1521 |   _ysh-should-parse-here <<'EOF'
 | 
| 1522 | echo $'trailing\\
 | 
| 1523 | '
 | 
| 1524 | EOF
 | 
| 1525 | 
 | 
| 1526 |   # Single backslash is wrong in YSH
 | 
| 1527 |   _ysh-parse-error-here <<'EOF'
 | 
| 1528 | echo $'trailing\
 | 
| 1529 | '
 | 
| 1530 | EOF
 | 
| 1531 | 
 | 
| 1532 |   # Also in expression mode
 | 
| 1533 |   _ysh-parse-error-here <<'EOF'
 | 
| 1534 | setvar x = $'trailing\
 | 
| 1535 | '
 | 
| 1536 | EOF
 | 
| 1537 | }
 | 
| 1538 | 
 | 
| 1539 | test-ysh_dq_strings() {
 | 
| 1540 |   # Double quoted is an error
 | 
| 1541 |   _osh-should-parse 'echo "\z"'
 | 
| 1542 | 
 | 
| 1543 |   # status, sh, message
 | 
| 1544 |   _assert-sh-status 2 "$OSH" $0 \
 | 
| 1545 |     +O parse_backslash -n -c 'echo test-parse_backslash "\z"'
 | 
| 1546 | 
 | 
| 1547 |   _ysh-parse-error 'echo "\z"'  # not in Oil
 | 
| 1548 |   _ysh-parse-error 'const bad = "\z"'  # not in expression mode
 | 
| 1549 | 
 | 
| 1550 |   # C style escapes not respected
 | 
| 1551 |   _osh-should-parse 'echo "\u1234"'  # ok in OSH
 | 
| 1552 |   _ysh-parse-error 'echo "\u1234"'  # not in Oil
 | 
| 1553 |   _ysh-parse-error 'const bad = "\u1234"'
 | 
| 1554 | 
 | 
| 1555 |   _osh-should-parse 'echo "`echo hi`"'
 | 
| 1556 |   _ysh-parse-error 'echo "`echo hi`"'
 | 
| 1557 |   _ysh-parse-error 'const bad = "`echo hi`"'
 | 
| 1558 | 
 | 
| 1559 |   _ysh-parse-error 'setvar x = "\z"'
 | 
| 1560 | }
 | 
| 1561 | 
 | 
| 1562 | test-ysh_bare_words() {
 | 
| 1563 |   _ysh-should-parse 'echo \$'
 | 
| 1564 |   _ysh-parse-error 'echo \z'
 | 
| 1565 | }
 | 
| 1566 | 
 | 
| 1567 | test-parse_dollar() {
 | 
| 1568 |   # The right way:
 | 
| 1569 |   #   echo \$
 | 
| 1570 |   #   echo \$:
 | 
| 1571 | 
 | 
| 1572 |   CASES=(
 | 
| 1573 |     'echo $'          # lex_mode_e.ShCommand
 | 
| 1574 |     'echo $:'
 | 
| 1575 | 
 | 
| 1576 |     'echo "$"'        # lex_mode_e.DQ
 | 
| 1577 |     'echo "$:"'
 | 
| 1578 | 
 | 
| 1579 |     'echo ${x:-$}'    # lex_mode_e.VSub_ArgUnquoted
 | 
| 1580 |     'echo ${x:-$:}'
 | 
| 1581 | 
 | 
| 1582 |     'echo "${x:-$}"'  # lex_mode_e.VSub_DQ
 | 
| 1583 |     'echo "${x:-$:}"'
 | 
| 1584 |   )
 | 
| 1585 |   for c in "${CASES[@]}"; do
 | 
| 1586 | 
 | 
| 1587 |     _osh-should-parse "$c"
 | 
| 1588 | 
 | 
| 1589 |     # status, sh, message
 | 
| 1590 |     _assert-sh-status 2 "$OSH" $0 \
 | 
| 1591 |       +O test-parse_dollar -n -c "$c"
 | 
| 1592 | 
 | 
| 1593 |     _ysh-parse-error "$c"
 | 
| 1594 |   done
 | 
| 1595 | }
 | 
| 1596 | 
 | 
| 1597 | test-parse-dparen() {
 | 
| 1598 |   # Bash (( construct
 | 
| 1599 |   local bad
 | 
| 1600 | 
 | 
| 1601 |   bad='((1 > 0 && 43 > 42))'
 | 
| 1602 |   _osh-should-parse "$bad"
 | 
| 1603 |   _ysh-parse-error "$bad"
 | 
| 1604 | 
 | 
| 1605 |   bad='if ((1 > 0 && 43 > 42)); then echo yes; fi'
 | 
| 1606 |   _osh-should-parse "$bad"
 | 
| 1607 |   _ysh-parse-error "$bad"
 | 
| 1608 | 
 | 
| 1609 |   bad='for ((x = 1; x < 5; ++x)); do echo $x; done'
 | 
| 1610 |   _osh-should-parse "$bad"
 | 
| 1611 |   _ysh-parse-error "$bad"
 | 
| 1612 | 
 | 
| 1613 |   _ysh-should-parse 'if (1 > 0 and 43 > 42) { echo yes }'
 | 
| 1614 | 
 | 
| 1615 |   # Accepted workaround: add space
 | 
| 1616 |   _ysh-should-parse 'if ( (1 > 0 and 43 > 42) ) { echo yes }'
 | 
| 1617 | }
 | 
| 1618 | 
 | 
| 1619 | test-invalid_parens() {
 | 
| 1620 | 
 | 
| 1621 |   # removed function sub syntax
 | 
| 1622 |   local s='write -- $f(x)'
 | 
| 1623 |   _osh-parse-error "$s"
 | 
| 1624 |   _ysh-parse-error "$s"
 | 
| 1625 | 
 | 
| 1626 |   # requires test-parse_at
 | 
| 1627 |   local s='write -- @[sorted(x)]'
 | 
| 1628 |   _osh-parse-error "$s"  # this is a parse error, but BAD message!
 | 
| 1629 |   _ysh-should-parse "$s"
 | 
| 1630 | 
 | 
| 1631 |   local s='
 | 
| 1632 | f() {
 | 
| 1633 |   write -- @[sorted(x)]
 | 
| 1634 | }
 | 
| 1635 | '
 | 
| 1636 |   _osh-parse-error "$s"
 | 
| 1637 |   _ysh-should-parse "$s"
 | 
| 1638 | 
 | 
| 1639 |   # Analogous bad bug
 | 
| 1640 |   local s='
 | 
| 1641 | f() {
 | 
| 1642 |   write -- @sorted (( z ))
 | 
| 1643 | }
 | 
| 1644 | '
 | 
| 1645 |   _osh-parse-error "$s"
 | 
| 1646 | }
 | 
| 1647 | 
 | 
| 1648 | test-eggex() {
 | 
| 1649 |   _osh-should-parse '= /%start dot %end \n \u{ff}/'
 | 
| 1650 |   _osh-parse-error '= /%star dot %end \n/'
 | 
| 1651 |   _osh-parse-error '= /%start do %end \n/'
 | 
| 1652 |   _osh-parse-error '= /%start dot %end \z/'
 | 
| 1653 |   _osh-parse-error '= /%start dot %end \n \u{}/'
 | 
| 1654 | 
 | 
| 1655 |   _osh-should-parse "= /['a'-'z']/"
 | 
| 1656 |   _osh-parse-error "= /['a'-'']/"
 | 
| 1657 |   _osh-parse-error "= /['a'-'zz']/"
 | 
| 1658 | 
 | 
| 1659 |   _osh-parse-error '= /dot{N *} /'
 | 
| 1660 | 
 | 
| 1661 |   # Could validate the Id.Expr_Name
 | 
| 1662 |   _osh-parse-error '= /dot{zzz *} /'
 | 
| 1663 | 
 | 
| 1664 |   # This could be allowed, but currently isn't
 | 
| 1665 |   _osh-parse-error '= /dot{*} /'
 | 
| 1666 | }
 | 
| 1667 | 
 | 
| 1668 | #
 | 
| 1669 | # Entry Points
 | 
| 1670 | #
 | 
| 1671 | 
 | 
| 1672 | soil-run-py() {
 | 
| 1673 |   run-test-funcs
 | 
| 1674 | }
 | 
| 1675 | 
 | 
| 1676 | soil-run-cpp() {
 | 
| 1677 |   ninja _bin/cxx-asan/osh
 | 
| 1678 |   OSH=_bin/cxx-asan/osh run-test-funcs
 | 
| 1679 | }
 | 
| 1680 | 
 | 
| 1681 | run-for-release() {
 | 
| 1682 |   run-other-suite-for-release ysh-parse-errors run-test-funcs
 | 
| 1683 | }
 | 
| 1684 | 
 | 
| 1685 | filename=$(basename $0)
 | 
| 1686 | if test $filename = 'ysh-parse-errors.sh'; then
 | 
| 1687 |   "$@"
 | 
| 1688 | fi
 | 
| 1689 | 
 |