| 1 | ---
 | 
| 2 | default_highlighter: oils-sh
 | 
| 3 | ---
 | 
| 4 | 
 | 
| 5 | Command vs. Expression Mode
 | 
| 6 | ===========================
 | 
| 7 | 
 | 
| 8 | This is an essential [syntactic concept](syntactic-concepts.html) in YSH.
 | 
| 9 | 
 | 
| 10 | YSH extends the shell **command** language with a Python-like **expression**
 | 
| 11 | language.
 | 
| 12 | 
 | 
| 13 | To implement that, the lexer enters "expression mode".
 | 
| 14 | 
 | 
| 15 | The key difference is that when lexing commands, `unquoted` is a string, while
 | 
| 16 | `$dollar` is a variable:
 | 
| 17 | 
 | 
| 18 |     ls /bin/str $myvar
 | 
| 19 | 
 | 
| 20 | On the other hand, when lexing expressions, `'quoted'` is a string, while
 | 
| 21 | `unquoted` is a variable:
 | 
| 22 | 
 | 
| 23 |     var s = myfunc('str', myvar)
 | 
| 24 | 
 | 
| 25 | This doc lists the places where we switch modes.
 | 
| 26 | 
 | 
| 27 | <div id="toc">
 | 
| 28 | </div>
 | 
| 29 | 
 | 
| 30 | ## From Command Mode to Expression Mode
 | 
| 31 | 
 | 
| 32 | ### RHS of Assignments
 | 
| 33 | 
 | 
| 34 | Everything after `=` is parsed in expression mode:
 | 
| 35 | 
 | 
| 36 |     var x = 42 + f(x)    # RHS of var/setvar
 | 
| 37 |     setvar x += g(y)
 | 
| 38 | 
 | 
| 39 |     setvar x = obj.method()   
 | 
| 40 | 
 | 
| 41 | This includes *bare assignments* in Hay blocks:
 | 
| 42 | 
 | 
| 43 |     Rule {
 | 
| 44 |       x = 42 + a[i]
 | 
| 45 |     }
 | 
| 46 | 
 | 
| 47 | ### `=` and `call` keywords
 | 
| 48 | 
 | 
| 49 | Likewise, everything after `=` or `::` is in expression mode:
 | 
| 50 | 
 | 
| 51 |     = 42 + f(x)
 | 
| 52 | 
 | 
| 53 | Throw away the value:
 | 
| 54 | 
 | 
| 55 |     call mylist->append(x)
 | 
| 56 | 
 | 
| 57 | ### YSH `for while if case`:
 | 
| 58 | 
 | 
| 59 | Expressions are surrounded by `( )`:
 | 
| 60 | 
 | 
| 61 |     for k, v in (mydict) { 
 | 
| 62 |       echo "$k $v"
 | 
| 63 |     }
 | 
| 64 | 
 | 
| 65 |     while (x > 0) {
 | 
| 66 |       setvar x -= 1
 | 
| 67 |     }
 | 
| 68 |     
 | 
| 69 |     if (x > 0) { 
 | 
| 70 |       echo 'positive'
 | 
| 71 |     }
 | 
| 72 | 
 | 
| 73 |     case (len(x)) {
 | 
| 74 |       (1)    { echo one }
 | 
| 75 |       (2)    { echo two }
 | 
| 76 |       (else) { echo other }
 | 
| 77 |     }
 | 
| 78 | 
 | 
| 79 | ### Expression Sub and Splice
 | 
| 80 | 
 | 
| 81 | The `$[]` construct converts an expression to a string:
 | 
| 82 | 
 | 
| 83 |     echo $[42 + a[i]]
 | 
| 84 | 
 | 
| 85 | The `@[]` construct converts a list to an array of strings:
 | 
| 86 | 
 | 
| 87 |     echo @[arrayfunc('three', 'four', f(x))]
 | 
| 88 | 
 | 
| 89 | ### Typed Arguments to Procs
 | 
| 90 | 
 | 
| 91 | Typed arguments are surrounded by `( )`:
 | 
| 92 | 
 | 
| 93 |     json write (['three', 'four'])
 | 
| 94 |     # =>
 | 
| 95 |     [ "three", "four" ]
 | 
| 96 | 
 | 
| 97 | Lazy arguments:
 | 
| 98 | 
 | 
| 99 |     assert [42 === x]
 | 
| 100 | 
 | 
| 101 | ### Proc and Func Parameter Lists
 | 
| 102 | 
 | 
| 103 | Parameters aren't expressions, but they're parsed with the same lexer:
 | 
| 104 | 
 | 
| 105 |     proc p(x, y) {    # what's between () is in expression mode
 | 
| 106 |       echo "$x $y"    # back to command mode
 | 
| 107 |     }
 | 
| 108 | 
 | 
| 109 |     func f(x) {
 | 
| 110 |       return (x)
 | 
| 111 |     }
 | 
| 112 | 
 | 
| 113 | ## From Expression Mode to Command Mode
 | 
| 114 | 
 | 
| 115 | ### Array Literals
 | 
| 116 | 
 | 
| 117 |     var myarray = :| /tmp/foo ${var} $(echo hi) @myarray |
 | 
| 118 | 
 | 
| 119 | ### Command Sub, Command Literals
 | 
| 120 | 
 | 
| 121 | Everything in between sigil pairs is in command mode:
 | 
| 122 | 
 | 
| 123 |     var x = $(hostname | tr a-z A-Z) 
 | 
| 124 | 
 | 
| 125 |     var y = @(seq 3)   # Split command sub
 | 
| 126 | 
 | 
| 127 | This is a command literal:
 | 
| 128 | 
 | 
| 129 |     var b = ^(echo $PWD)
 | 
| 130 | 
 | 
| 131 | ## Examples
 | 
| 132 | 
 | 
| 133 | ### How Are Glob Patterns Written in Each Mode?
 | 
| 134 | 
 | 
| 135 | No:
 | 
| 136 | 
 | 
| 137 |     echo '*.py'              # a literal string, not a glob
 | 
| 138 | 
 | 
| 139 |     echo @[glob(*.py)]       # syntax error, * is an operator in 
 | 
| 140 |                              # expression mode
 | 
| 141 | 
 | 
| 142 |     var x = myfunc(*.py)     # ditto, syntax error
 | 
| 143 | 
 | 
| 144 | Yes:
 | 
| 145 | 
 | 
| 146 |     echo *.py                # expanded as a glob
 | 
| 147 | 
 | 
| 148 |     echo @[glob('*.py')]     # A literal string passed to the builtin
 | 
| 149 |                              # glob function
 | 
| 150 | 
 | 
| 151 |     var x = f('*.py')        # Just a string
 | 
| 152 | 
 | 
| 153 |     var x = f(glob('*.py'))  # Now it's expanded
 | 
| 154 | 
 | 
| 155 | Another way to say this is that YSH works like Python:
 | 
| 156 | 
 | 
| 157 | ```python
 | 
| 158 | from glob import glob
 | 
| 159 | glob('*.py')             # this is a glob
 | 
| 160 | os.listdir('*.py')       # no glob because it's not how listdir() works
 | 
| 161 | ```
 | 
| 162 | 
 | 
| 163 | Also note that YSH has a builtin operator that uses glob aka `fnmatch()`
 | 
| 164 | syntax:
 | 
| 165 | 
 | 
| 166 |     if (x ~~ '*.py') {
 | 
| 167 |       echo 'Python'
 | 
| 168 |     }
 | 
| 169 | 
 | 
| 170 | 
 | 
| 171 | ## vim: sw=2
 |