1 | ---
|
2 | default_highlighter: oils-sh
|
3 | ---
|
4 |
|
5 | Command vs. Expression Mode
|
6 | ===========================
|
7 |
|
8 | [YSH][] extends the shell **command** language with a Python-like
|
9 | **expression** language.
|
10 |
|
11 | Commands and expressions each have a **lexer mode**, which is an essential
|
12 | [syntactic concept](syntactic-concepts.html) in YSH.
|
13 |
|
14 | This doc lists the places where [YSH][] switches between modes.
|
15 |
|
16 | [YSH]: $xref
|
17 |
|
18 | <div id="toc">
|
19 | </div>
|
20 |
|
21 | ## Summary
|
22 |
|
23 | A main difference is whether you write strings like `unquoted` or `'quoted'`,
|
24 | and whether you write variables like `$dollar` or `unquoted`:
|
25 |
|
26 | <style>
|
27 | thead { text-align: left; }
|
28 | table {
|
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 |
|
61 | More 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 |
|
66 | And:
|
67 |
|
68 | echo $filename.py # $filename is a var - command
|
69 | var x = filename ++ '.py' # filename is a var - expression
|
70 |
|
71 | <!--
|
72 | Shell 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 |
|
83 | Everything 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 |
|
90 | This includes *bare assignments* in Hay blocks:
|
91 |
|
92 | Rule {
|
93 | x = 42 + a[i]
|
94 | }
|
95 |
|
96 | ### `=` and `call` keywords
|
97 |
|
98 | Likewise, everything after `=` or `call` is in expression mode:
|
99 |
|
100 | = 42 + f(x)
|
101 |
|
102 | Throw away the value:
|
103 |
|
104 | call mylist->append(x)
|
105 |
|
106 | ### YSH `for while if case`:
|
107 |
|
108 | Expressions 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 |
|
130 | The `$[]` construct converts an expression to a string:
|
131 |
|
132 | echo $[42 + a[i]]
|
133 |
|
134 | The `@[]` construct converts a list to an array of strings:
|
135 |
|
136 | echo @[arrayfunc('three', 'four', f(x))]
|
137 |
|
138 | ### Typed Arguments to Procs
|
139 |
|
140 | Typed arguments are surrounded by `( )`:
|
141 |
|
142 | json write (['three', 'four'])
|
143 | # =>
|
144 | [ "three", "four" ]
|
145 |
|
146 | Lazy arguments:
|
147 |
|
148 | assert [42 === x]
|
149 |
|
150 | ### Proc and Func Parameter Lists
|
151 |
|
152 | Parameters 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 |
|
170 | Everything 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 |
|
176 | This 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 |
|
184 | No:
|
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 |
|
193 | Yes:
|
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 |
|
204 | Another way to say this is that YSH works like Python:
|
205 |
|
206 | ```python
|
207 | from glob import glob
|
208 | glob('*.py') # this is a glob
|
209 | os.listdir('*.py') # no glob because it's not how listdir() works
|
210 | ```
|
211 |
|
212 | Also note that YSH has a builtin operator that uses glob aka `fnmatch()`
|
213 | syntax:
|
214 |
|
215 | if (x ~~ '*.py') {
|
216 | echo 'Python'
|
217 | }
|
218 |
|