OILS / doc / command-vs-expression-mode.md View on Github | oilshell.org

171 lines, 103 significant
1---
2default_highlighter: oils-sh
3---
4
5Command vs. Expression Mode
6===========================
7
8This is an essential [syntactic concept](syntactic-concepts.html) in YSH.
9
10YSH extends the shell **command** language with a Python-like **expression**
11language.
12
13To implement that, the lexer enters "expression mode".
14
15The key difference is that when lexing commands, `unquoted` is a string, while
16`$dollar` is a variable:
17
18 ls /bin/str $myvar
19
20On the other hand, when lexing expressions, `'quoted'` is a string, while
21`unquoted` is a variable:
22
23 var s = myfunc('str', myvar)
24
25This 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
34Everything 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
41This includes *bare assignments* in Hay blocks:
42
43 Rule {
44 x = 42 + a[i]
45 }
46
47### `=` and `call` keywords
48
49Likewise, everything after `=` or `::` is in expression mode:
50
51 = 42 + f(x)
52
53Throw away the value:
54
55 call mylist->append(x)
56
57### YSH `for while if case`:
58
59Expressions 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
81The `$[]` construct converts an expression to a string:
82
83 echo $[42 + a[i]]
84
85The `@[]` construct converts a list to an array of strings:
86
87 echo @[arrayfunc('three', 'four', f(x))]
88
89### Typed Arguments to Procs
90
91Typed arguments are surrounded by `( )`:
92
93 json write (['three', 'four'])
94 # =>
95 [ "three", "four" ]
96
97Lazy arguments:
98
99 assert [42 === x]
100
101### Proc and Func Parameter Lists
102
103Parameters 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
121Everything 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
127This is a command literal:
128
129 var b = ^(echo $PWD)
130
131## Examples
132
133### How Are Glob Patterns Written in Each Mode?
134
135No:
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
144Yes:
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
155Another way to say this is that YSH works like Python:
156
157```python
158from glob import glob
159glob('*.py') # this is a glob
160os.listdir('*.py') # no glob because it's not how listdir() works
161```
162
163Also note that YSH has a builtin operator that uses glob aka `fnmatch()`
164syntax:
165
166 if (x ~~ '*.py') {
167 echo 'Python'
168 }
169
170
171## vim: sw=2