@@ -87,104 +87,45 @@ def step3_unparse(ast):
8787
8888##
8989## Step 4:
90- ## Use our simple feature counter to count different shell scripts.
91- ##
92- ## Find a (POSIX) shell script you use frequently (or pull one from GitHub) and see what features it uses.
93- ##
94-
95-
96- def feature_counter ():
97- features = [
98- "background" ,
99- "subshell" ,
100- "home_tilde" ,
101- "$((arithmetic))" ,
102- "eval" ,
103- "alias" ,
104- "while" ,
105- "for" ,
106- "case" ,
107- "if" ,
108- "and" ,
109- "or" ,
110- "negate" ,
111- "heredoc_redir" ,
112- "dup_redir" ,
113- "file_redir" ,
114- "$(substitution)" ,
115- "function" ,
116- "assignment" ,
117- "variable_use" ,
118- "pipeline" ,
119- "command" ,
120- ]
121- feature_counts = {name : 0 for name in features }
122-
123- def count_features (node ):
124- match node :
125- case AST .BackgroundNode ():
126- feature_counts ["background" ] += 1
127- case AST .PipeNode ():
128- feature_counts ["pipeline" ] += 1
129- if node .is_background :
130- feature_counts ["background" ] += 1
131- case AST .SubshellNode ():
132- feature_counts ["subshell" ] += 1
133- case AST .TArgChar ():
134- feature_counts ["home_tilde" ] += 1
135- case AST .AArgChar ():
136- feature_counts ["$((arithmetic))" ] += 1
137- case AST .BArgChar ():
138- feature_counts ["$(substitution)" ] += 1
139- case AST .VArgChar ():
140- feature_counts ["variable_use" ] += 1
141- case AST .AssignNode ():
142- feature_counts ["assignment" ] += 1
143- case AST .DefunNode ():
144- feature_counts ["function" ] += 1
145- case AST .WhileNode ():
146- feature_counts ["while" ] += 1
147- case AST .ForNode ():
148- feature_counts ["for" ] += 1
149- case AST .CaseNode ():
150- feature_counts ["case" ] += 1
151- case AST .IfNode ():
152- feature_counts ["if" ] += 1
153- case AST .AndNode ():
154- feature_counts ["and" ] += 1
155- case AST .OrNode ():
156- feature_counts ["or" ] += 1
157- case AST .NotNode ():
158- feature_counts ["negate" ] += 1
159- case AST .HeredocRedirNode ():
160- feature_counts ["heredoc_redir" ] += 1
161- case AST .DupRedirNode ():
162- feature_counts ["dup_redir" ] += 1
163- case AST .FileRedirNode ():
164- feature_counts ["file_redir" ] += 1
165- case AST .CommandNode ():
166- feature_counts ["command" ] += 1
167- if node .arguments :
168- cmd_name = AST .string_of_arg (node .arguments [0 ])
169- # NB this is conservative---we're detecting static uses of these constructs
170- if cmd_name == "eval" :
171- feature_counts ["eval" ] += 1
172- elif cmd_name == "alias" :
173- feature_counts ["alias" ] += 1
174- case _:
175- pass
176- return node
177-
178- return (count_features , feature_counts )
179-
90+ ## Create a simple analysis that returns the number of subshells a script will create:
91+ ## Four ways to create a subshell:
92+ ## - Asynchronous commands `&`
93+ ## - Pipes `|`
94+ ## - Subshells `(...)`
95+ ## - Command Substitution `$(...)`
96+ ##
97+ ## Find a (POSIX) shell script you use frequently (or pull one from binpash/koala) and see how many it creates.
98+ ##
99+
100+ class Counter :
101+ def __init__ (self ):
102+ self .cnt = 0
103+
104+ def add (self , n ):
105+ self .cnt += n
106+
107+ def get (self ):
108+ return self .cnt
180109
181110def step4_subshells (ast ):
182111 show_step ("4: counting shell features" )
183112
184- (counter , counts ) = feature_counter ()
185- walk_ast (ast , visit = counter ) # REPLACE # FILL IN HERE WITH CALL to `walk_ast` with the `counter` as `visit`
186- for (feature , count ) in (counts .items ()): # REPLACE # FILL IN HERE WITH LOOP to print out the features (HINT: use the `dict.items` method)
187- print (f"- { feature } : { count } " ) # REMOVE
113+ subshells = Counter ()
114+ def count_features (node ): # REMOVE
115+ match node : # REMOVE
116+ case AST .BackgroundNode (): # REMOVE
117+ subshells .add (1 ) # REMOVE
118+ case AST .PipeNode (): # REMOVE
119+ subshells .add (len (node .items )) # REMOVE
120+ case AST .SubshellNode (): # REMOVE
121+ subshells .add (1 ) # REMOVE
122+ case AST .BArgChar (): # REMOVE
123+ subshells .add (1 ) # REMOVE
124+ case _: # REMOVE
125+ pass # REMOVE
126+ return node # REMOVE
127+ walk_ast (ast , visit = count_features ) # REPLACE # FILL IN HERE WITH CALL to `walk_ast` that counts subshells
128+ print ("Number of subshells in script:" , subshells .get ())
188129
189130##
190131## Step 5:
0 commit comments