OILS / doc / command-vs-expression-mode.md View on Github | oils.pub

218 lines, 142 significant
1---
2default_highlighter: oils-sh
3---
4
5Command vs. Expression Mode
6===========================
7
8[YSH][] extends the shell **command** language with a Python-like
9**expression** language.
10
11Commands and expressions each have a **lexer mode**, which is an essential
12[syntactic concept](syntactic-concepts.html) in YSH.
13
14This doc lists the places where [YSH][] switches between modes.
15
16[YSH]: $xref
17
18<div id="toc">
19</div>
20
21## Summary
22
23A main difference is whether you write strings like `unquoted` or `'quoted'`,
24and whether you write variables like `$dollar` or `unquoted`:
25
26<style>
27thead { text-align: left; }
28table {
29 width: 100%;
30 margin-left: 2em; /* match */
31}
32</style>
33
34<table>
35
36- thead
37 - Description
38 - Lexing Mode
39 - String
40 - Variable
41 - Example
42- tr
43 - Shell-Like
44 - Command
45 - `unquoted`
46 - `$dollar`
47 - ```
48 ls foo/bar $myvar
49 ```
50- tr
51 - Python-like
52 - Expression
53 - `'quoted'`
54 - `unquoted`
55 - ```
56 var s = myfunc('str', myvar)
57 ```
58
59</table>
60
61More examples:
62
63 ls foo/bar # foo and bar are strings - command
64 var x = foo / bar # foo and bar are the names of variables - expression
65
66And:
67
68 echo $filename.py # $filename is a var - command
69 var x = filename ++ '.py' # filename is a var - expression
70
71<!--
72Shell has a similar difference:
73
74 ls foo/bar # foo and bar are strings
75 a=$(( foo/bar )) # foo and bar are the names of variables
76-->
77
78
79## From Command Mode to Expression Mode
80
81### RHS of Assignments
82
83Everything after `=` is parsed in expression mode:
84
85 var x = 42 + f(x) # RHS of var/setvar
86 setvar x += g(y)
87
88 setvar x = obj.method()
89
90This includes *bare assignments* in Hay blocks:
91
92 Rule {
93 x = 42 + a[i]
94 }
95
96### `=` and `call` keywords
97
98Likewise, everything after `=` or `call` is in expression mode:
99
100 = 42 + f(x)
101
102Throw away the value:
103
104 call mylist->append(x)
105
106### YSH `for while if case`:
107
108Expressions are surrounded by `( )`:
109
110 for k, v in (mydict) {
111 echo "$k $v"
112 }
113
114 while (x > 0) {
115 setvar x -= 1
116 }
117
118 if (x > 0) {
119 echo 'positive'
120 }
121
122 case (len(x)) {
123 (1) { echo one }
124 (2) { echo two }
125 (else) { echo other }
126 }
127
128### Expression Sub and Splice
129
130The `$[]` construct converts an expression to a string:
131
132 echo $[42 + a[i]]
133
134The `@[]` construct converts a list to an array of strings:
135
136 echo @[arrayfunc('three', 'four', f(x))]
137
138### Typed Arguments to Procs
139
140Typed arguments are surrounded by `( )`:
141
142 json write (['three', 'four'])
143 # =>
144 [ "three", "four" ]
145
146Lazy arguments:
147
148 assert [42 === x]
149
150### Proc and Func Parameter Lists
151
152Parameters aren't expressions, but they're parsed with the same lexer:
153
154 proc p (x, y) { # what's between () is in expression mode
155 echo "$x $y" # back to command mode
156 }
157
158 func f(x) {
159 return (x)
160 }
161
162## From Expression Mode to Command Mode
163
164### Array Literals
165
166 var myarray = :| /tmp/foo ${var} $(echo hi) @myarray |
167
168### Command Sub, Command Literals
169
170Everything in between sigil pairs is in command mode:
171
172 var x = $(hostname | tr a-z A-Z)
173
174 var y = @(seq 3) # Split command sub
175
176This is a command literal:
177
178 var b = ^(echo $PWD)
179
180## More Examples
181
182### How Are Glob Patterns Written in Each Mode?
183
184No:
185
186 echo '*.py' # a literal string, not a glob
187
188 echo @[glob(*.py)] # syntax error, * is an operator in
189 # expression mode
190
191 var x = myfunc(*.py) # ditto, syntax error
192
193Yes:
194
195 echo *.py # expanded as a glob
196
197 echo @[glob('*.py')] # A literal string passed to the builtin
198 # glob function
199
200 var x = f('*.py') # Just a string
201
202 var x = f(glob('*.py')) # Now it's expanded
203
204Another way to say this is that YSH works like Python:
205
206```python
207from glob import glob
208glob('*.py') # this is a glob
209os.listdir('*.py') # no glob because it's not how listdir() works
210```
211
212Also note that YSH has a builtin operator that uses glob aka `fnmatch()`
213syntax:
214
215 if (x ~~ '*.py') {
216 echo 'Python'
217 }
218