Skip to content

Commit 95f9164

Browse files
committed
move solution to SOLUTION, scaffold to src
1 parent 1758b3a commit 95f9164

File tree

12 files changed

+87
-87
lines changed

12 files changed

+87
-87
lines changed
Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def string_of_expanded_arg(arg: list[AST.ArgChar]):
2323
return s.strip("\"'") # stripping quotes because sh_expand leaves them in
2424

2525
def command_prepender(exp_state: expand.ExpansionState, unsafe_commands=None):
26+
try_prefix_args = [string_to_argchars("try")] # REMOVE
2627
unsafe_commands = list(unsafe_commands or [])
2728

2829
def replace(node):
@@ -41,7 +42,12 @@ def replace(node):
4142
#
4243
# Only fill in this part once you have the rest of the JIT working.
4344
#
44-
pass # FILL IN OPTIMIZATION HERE
45+
expand.expand_command(node, exp_state) # REPLACE pass # FILL IN OPTIMIZATION HERE
46+
# REMOVE
47+
cmd_name = string_of_expanded_arg(node.arguments[0]) # REMOVE
48+
# is it a known-safe command? # REMOVE
49+
if cmd_name not in unsafe_commands: # REMOVE
50+
return None # REMOVE
4551
except (expand.ImpureExpansion, expand.StuckExpansion, expand.Unimplemented,) as exc:
4652
# if expansion fails, we should be conservative and prepend
4753
pass
@@ -51,7 +57,12 @@ def replace(node):
5157
# with no arguments at all. In that case, we don't want to add a `try`!
5258
#
5359
# Hint: don't forget `string_to_argchars`
54-
# return # FILL IN HERE with a new `CommandNode` that prepends the command with `try`
60+
return AST.CommandNode( # REPLACE # return # FILL IN HERE with a new `CommandNode` that prepends the command with `try`
61+
arguments=try_prefix_args + node.arguments if len(node.arguments) > 0 else [], # REMOVE
62+
assignments=node.assignments, # REMOVE
63+
redir_list=node.redir_list, # REMOVE
64+
line_number=node.line_number # REMOVE
65+
) # REMOVE
5566
case _:
5667
return None
5768

scaffold/jit.sh renamed to SOLUTION/jit.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ declare -p >"$__saved_env"
3030

3131
# !!! expand the script
3232
__expanded=$__input".expanded"
33-
python3 src/expand.py "$__input" "$BASH_VERSION" >"$__expanded"
33+
python3 SOLUTION/expand.py "$__input" "$BASH_VERSION" >"$__expanded"
3434

3535
# !!! run the expanded script
3636
. "$__expanded"
Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def step1_parse_script(input_script):
4242
)
4343

4444
# set ast to a list of parsed commands using `parse_shell_to_asts`
45-
ast = 'FILL IN A CALL HERE'
45+
ast = list(parse_shell_to_asts(input_script)) # REPLACE ast = 'FILL IN A CALL HERE'
4646
print(ast)
4747

4848
return ast
@@ -57,8 +57,8 @@ def step1_parse_script(input_script):
5757
def step2_walk_print(ast):
5858
show_step("2: visiting with walk_ast")
5959

60-
# look in `utils.py` for more code for you to write!
61-
# FILL IN A CALL HERE to `walk_ast` with `print` as the `visit` function
60+
# REPLACE # look in `utils.py` for more code for you to write!
61+
walk_ast(ast, visit=print) # REPLACE # FILL IN A CALL HERE to `walk_ast` with `print` as the `visit` function
6262

6363

6464
##
@@ -79,7 +79,7 @@ def step3_unparse(ast):
7979
show_step("3: unparse using `ast_to_code`")
8080

8181
# convert the AST back using `ast_to_code`
82-
unparsed_code = 'FILL IN A CALL HERE'
82+
unparsed_code = ast_to_code([node for (node, _, _, _) in ast]) # REPLACE unparsed_code = 'FILL IN A CALL HERE'
8383
print(unparsed_code)
8484

8585
return unparsed_code
@@ -182,8 +182,9 @@ def step4_feature_counter(ast):
182182
show_step("4: counting shell features")
183183

184184
(counter, counts) = feature_counter()
185-
# FILL IN HERE WITH CALL to `walk_ast` with the `counter` as `visit`
186-
# FILL IN HERE WITH LOOP to print out the features (HINT: use the `dict.items` method)
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
187188

188189
##
189190
## Step 5:
@@ -215,7 +216,17 @@ def check_for_effects(n):
215216
return
216217

217218
match n:
218-
# FILL IN HERE with the checks described in the comment above
219+
# REPLACE # FILL IN HERE with the checks described in the comment above
220+
case AST.AssignNode() | AST.DefunNode(): # REMOVE
221+
safe = False # REMOVE
222+
case AST.CommandNode() if len(n.assignments) > 0: # REMOVE
223+
safe = False # REMOVE
224+
case AST.VArgChar() if n.fmt == "Assign": # REMOVE
225+
safe = False # REMOVE
226+
case AST.AArgChar(): # REMOVE
227+
safe = False # REMOVE
228+
case _: # REMOVE
229+
pass # REMOVE
219230

220231
walk_ast_node(node, visit=check_for_effects)
221232
return safe
@@ -273,7 +284,12 @@ def replace(node: AST.AstNode):
273284
handle.write("\n")
274285

275286
# replacement command
276-
# return # FILL IN HERE with a `CommandNode` that will `cat` the file at `stub_path` (hint: checkout `string_to_argchars`)
287+
return AST.CommandNode( # REPLACE # return # FILL IN HERE with a `CommandNode` that will `cat` the file at `stub_path` (hint: checkout `string_to_argchars`)
288+
assignments = [], # guaranteed by safety to have no assignments # REMOVE
289+
line_number = getattr(node, "line_number", -1), # REMOVE
290+
arguments = [string_to_argchars("cat"), string_to_argchars(stub_path)], # REMOVE
291+
redir_list = [], # REMOVE
292+
) # REMOVE
277293

278294
case _:
279295
return None
@@ -326,9 +342,9 @@ def replace(node: AST.AstNode):
326342
return AST.CommandNode(
327343
line_number = getattr(node, "line_number", -1),
328344
assignments = [ # no original assignments (safe to expand!)
329-
# FILL IN HERE WITH an assignment of `JIT_INPUT` to the `stub_path` (hint: you need to build an `AssignNode`; use `string_of_argchars`)
345+
AST.AssignNode(var="JIT_INPUT", val=string_to_argchars(stub_path)), # REPLACE # FILL IN HERE WITH an assignment of `JIT_INPUT` to the `stub_path` (hint: you need to build an `AssignNode`; use `string_of_argchars`)
330346
],
331-
arguments = [] # FILL IN HERE WITH sourcing (via `.`) the `src/debug_jit.sh` JIT script (hint: use `string_of_argchars`)
347+
arguments = [string_to_argchars("."), string_to_argchars("SOLUTION/debug_jit.sh"),], # REPLACE arguments = [] # FILL IN HERE WITH sourcing (via `.`) the `src/debug_jit.sh` JIT script (hint: use `string_of_argchars`)
332348
redir_list = [],
333349
)
334350
case _:
@@ -388,7 +404,7 @@ def replace(node: AST.AstNode):
388404
assignments = [ # no original assignments (safe to expand!)
389405
AST.AssignNode(var="JIT_INPUT", val=string_to_argchars(stub_path)),
390406
],
391-
arguments = [string_to_argchars("."), string_to_argchars("src/jit.sh"),],
407+
arguments = [string_to_argchars("."), string_to_argchars("SOLUTION/jit.sh"),],
392408
redir_list = [],
393409
)
394410
case _:

scaffold/test.sh renamed to SOLUTION/test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ check_script() {
2424
fi
2525
}
2626

27-
python3 src/solution.py sh/simple.sh || exit 1 # generates will create sh/simple.sh.preprocessed.3
27+
python3 SOLUTION/solution.py sh/simple.sh || exit 1 # generates will create sh/simple.sh.preprocessed.3
2828

2929
testing 1
3030
echo "CODE"
Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def ast_to_code(ast: Iterable[AST.AstNode]) -> str:
3434
3535
:param ast: Description
3636
"""
37-
# return # FILL IN HERE with each node in `ast` pretty-printed, compiled into a single newline-separated string
37+
return "\n".join([node.pretty() for node in ast]) # REPLACE # return # FILL IN HERE with each node in `ast` pretty-printed, compiled into a single newline-separated string
3838

3939

4040
##
@@ -115,8 +115,28 @@ def walk_fd(fd):
115115
**{k: v for k, v in vars(node).items() if k != "items"},
116116
)
117117
case AST.CommandNode():
118-
# FILL IN HERE WITH the code for visiting a `CommandNode`
119-
return # FILL IN HERE WITH the recomputed `CommandNode`
118+
# REPLACE # FILL IN HERE WITH the code for visiting a `CommandNode`
119+
assignments = [ # REMOVE
120+
walk_ast_node(ass, visit=visit, replace=replace) # REMOVE
121+
for ass in node.assignments # REMOVE
122+
] # REMOVE
123+
arguments = [ # REMOVE
124+
walk_ast_node(arg, visit=visit, replace=replace) # REMOVE
125+
for arg in node.arguments # REMOVE
126+
] # REMOVE
127+
redirs = [ # REMOVE
128+
walk_ast_node(r, visit=visit, replace=replace) for r in node.redir_list # REMOVE
129+
] # REMOVE
130+
return AST.CommandNode( # REPLACE return # FILL IN HERE WITH the recomputed `CommandNode`
131+
arguments=arguments, # REMOVE
132+
assignments=assignments, # REMOVE
133+
redir_list=redirs, # REMOVE
134+
**{ # REMOVE
135+
k: v # REMOVE
136+
for k, v in vars(node).items() # REMOVE
137+
if k not in ("arguments", "assignments", "redir_list") # REMOVE
138+
}, # REMOVE
139+
) # REMOVE
120140
case AST.AssignNode():
121141
return AST.AssignNode(
122142
val=walk_ast_node(node.val, visit=visit, replace=replace),
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ then
1212
exit 2
1313
fi
1414

15-
[ -d scaffold ] && rm -r scaffold
16-
mkdir scaffold
15+
[ -d src ] && rm -r src
16+
mkdir src
1717

18-
for file in $(find src -type f)
18+
for file in $(find SOLUTION -type f)
1919
do
20-
if [ "$file" = test.sh ]
20+
if [ "$file" = test.sh ]
2121
then
2222
continue
2323
fi
2424

25-
redacted=scaffold/"${file##src/}"
25+
redacted=src/"${file##SOLUTION/}"
2626
sh/redact.sh "$file" >"$redacted"
2727
if [ -x "$file" ]
2828
then

sh/redact.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ fi
1212

1313
[ -f "$1" ] || { echo "redact.sh: '$1' is not a regular file"; exit 1; }
1414

15-
grep -v "# REMOVE" "$1" | sed -E 's/^(\s*).*# REPLACE (.*)$/\1\2/'
15+
grep -v "# REMOVE" "$1" | sed -E 's/^(\s*).*# REPLACE (.*)$/\1\2/' | sed -E 's/SOLUTION/src/g'
1616

src/expand.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ def string_of_expanded_arg(arg: list[AST.ArgChar]):
2323
return s.strip("\"'") # stripping quotes because sh_expand leaves them in
2424

2525
def command_prepender(exp_state: expand.ExpansionState, unsafe_commands=None):
26-
try_prefix_args = [string_to_argchars("try")] # REMOVE
2726
unsafe_commands = list(unsafe_commands or [])
2827

2928
def replace(node):
@@ -42,12 +41,7 @@ def replace(node):
4241
#
4342
# Only fill in this part once you have the rest of the JIT working.
4443
#
45-
expand.expand_command(node, exp_state) # REPLACE pass # FILL IN OPTIMIZATION HERE
46-
# REMOVE
47-
cmd_name = string_of_expanded_arg(node.arguments[0]) # REMOVE
48-
# is it a known-safe command? # REMOVE
49-
if cmd_name not in unsafe_commands: # REMOVE
50-
return None # REMOVE
44+
pass # FILL IN OPTIMIZATION HERE
5145
except (expand.ImpureExpansion, expand.StuckExpansion, expand.Unimplemented,) as exc:
5246
# if expansion fails, we should be conservative and prepend
5347
pass
@@ -57,12 +51,7 @@ def replace(node):
5751
# with no arguments at all. In that case, we don't want to add a `try`!
5852
#
5953
# Hint: don't forget `string_to_argchars`
60-
return AST.CommandNode( # REPLACE # return # FILL IN HERE with a new `CommandNode` that prepends the command with `try`
61-
arguments=try_prefix_args + node.arguments if len(node.arguments) > 0 else [], # REMOVE
62-
assignments=node.assignments, # REMOVE
63-
redir_list=node.redir_list, # REMOVE
64-
line_number=node.line_number # REMOVE
65-
) # REMOVE
54+
# return # FILL IN HERE with a new `CommandNode` that prepends the command with `try`
6655
case _:
6756
return None
6857

src/solution.py

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def step1_parse_script(input_script):
4242
)
4343

4444
# set ast to a list of parsed commands using `parse_shell_to_asts`
45-
ast = list(parse_shell_to_asts(input_script)) # REPLACE ast = 'FILL IN A CALL HERE'
45+
ast = 'FILL IN A CALL HERE'
4646
print(ast)
4747

4848
return ast
@@ -57,8 +57,8 @@ def step1_parse_script(input_script):
5757
def step2_walk_print(ast):
5858
show_step("2: visiting with walk_ast")
5959

60-
# REPLACE # look in `utils.py` for more code for you to write!
61-
walk_ast(ast, visit=print) # REPLACE # FILL IN A CALL HERE to `walk_ast` with `print` as the `visit` function
60+
# look in `utils.py` for more code for you to write!
61+
# FILL IN A CALL HERE to `walk_ast` with `print` as the `visit` function
6262

6363

6464
##
@@ -79,7 +79,7 @@ def step3_unparse(ast):
7979
show_step("3: unparse using `ast_to_code`")
8080

8181
# convert the AST back using `ast_to_code`
82-
unparsed_code = ast_to_code([node for (node, _, _, _) in ast]) # REPLACE unparsed_code = 'FILL IN A CALL HERE'
82+
unparsed_code = 'FILL IN A CALL HERE'
8383
print(unparsed_code)
8484

8585
return unparsed_code
@@ -182,9 +182,8 @@ def step4_feature_counter(ast):
182182
show_step("4: counting shell features")
183183

184184
(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
185+
# FILL IN HERE WITH CALL to `walk_ast` with the `counter` as `visit`
186+
# FILL IN HERE WITH LOOP to print out the features (HINT: use the `dict.items` method)
188187

189188
##
190189
## Step 5:
@@ -216,17 +215,7 @@ def check_for_effects(n):
216215
return
217216

218217
match n:
219-
# REPLACE # FILL IN HERE with the checks described in the comment above
220-
case AST.AssignNode() | AST.DefunNode(): # REMOVE
221-
safe = False # REMOVE
222-
case AST.CommandNode() if len(n.assignments) > 0: # REMOVE
223-
safe = False # REMOVE
224-
case AST.VArgChar() if n.fmt == "Assign": # REMOVE
225-
safe = False # REMOVE
226-
case AST.AArgChar(): # REMOVE
227-
safe = False # REMOVE
228-
case _: # REMOVE
229-
pass # REMOVE
218+
# FILL IN HERE with the checks described in the comment above
230219

231220
walk_ast_node(node, visit=check_for_effects)
232221
return safe
@@ -284,12 +273,7 @@ def replace(node: AST.AstNode):
284273
handle.write("\n")
285274

286275
# replacement command
287-
return AST.CommandNode( # REPLACE # return # FILL IN HERE with a `CommandNode` that will `cat` the file at `stub_path` (hint: checkout `string_to_argchars`)
288-
assignments = [], # guaranteed by safety to have no assignments # REMOVE
289-
line_number = getattr(node, "line_number", -1), # REMOVE
290-
arguments = [string_to_argchars("cat"), string_to_argchars(stub_path)], # REMOVE
291-
redir_list = [], # REMOVE
292-
) # REMOVE
276+
# return # FILL IN HERE with a `CommandNode` that will `cat` the file at `stub_path` (hint: checkout `string_to_argchars`)
293277

294278
case _:
295279
return None
@@ -342,9 +326,9 @@ def replace(node: AST.AstNode):
342326
return AST.CommandNode(
343327
line_number = getattr(node, "line_number", -1),
344328
assignments = [ # no original assignments (safe to expand!)
345-
AST.AssignNode(var="JIT_INPUT", val=string_to_argchars(stub_path)), # REPLACE # FILL IN HERE WITH an assignment of `JIT_INPUT` to the `stub_path` (hint: you need to build an `AssignNode`; use `string_of_argchars`)
329+
# FILL IN HERE WITH an assignment of `JIT_INPUT` to the `stub_path` (hint: you need to build an `AssignNode`; use `string_of_argchars`)
346330
],
347-
arguments = [string_to_argchars("."), string_to_argchars("src/debug_jit.sh"),], # REPLACE arguments = [] # FILL IN HERE WITH sourcing (via `.`) the `src/debug_jit.sh` JIT script (hint: use `string_of_argchars`)
331+
arguments = [] # FILL IN HERE WITH sourcing (via `.`) the `src/debug_jit.sh` JIT script (hint: use `string_of_argchars`)
348332
redir_list = [],
349333
)
350334
case _:

0 commit comments

Comments
 (0)