CMU Coding Bootcamp
1from typing import List
2
3
4def findLabel(lines: List[List[str]], label: str) -> int:
5 """Find the line number of a label"""
6 for i, line in enumerate(lines):
7 if line[0] == label + ":":
8 return i
9 raise ValueError(f"Label '{label}' not found")
10
11
12def parse(args: List[int], values: List[int], arg: str) -> int:
13 """Parse an argument string into an integer value"""
14 arg_var = 0
15
16 if arg.isdigit():
17 arg_var = int(arg)
18 elif arg.startswith("L"):
19 arg_var = values[int(arg[1:])]
20 elif arg.startswith("A"):
21 arg_var = args[int(arg[1:])]
22
23 return arg_var
24
25
26def runL(args: List[int], values: List[int], line: List[str]):
27 """Run a Set command"""
28 out_var = int(line[0][1:])
29 op = line[1]
30
31 if out_var >= len(values):
32 values.extend([0] * (out_var - len(values) + 1))
33
34 if op.isdigit():
35 values[out_var] = int(op)
36 else:
37 op1, op2 = line[2], line[3]
38 op1_val, op2_val = parse(args, values, op1), parse(args, values, op2)
39 if op == "+":
40 values[out_var] = op1_val + op2_val
41 elif op == "-":
42 values[out_var] = op1_val - op2_val
43 else:
44 raise ValueError(f"Invalid Operator: {op}")
45
46
47def runJMP(args: List[int], values: List[int], line: List[str], lines) -> int | None:
48 """Return the target line number or None for a JMP command"""
49 command = line[0]
50 if command == "JMP":
51 jump_targ = line[1]
52 if jump_targ.isdigit():
53 target = int(line[1])
54 if target < 0 or target >= len(lines):
55 raise ValueError(f"Invalid Jump Target: {target}")
56 return target
57 else:
58 target = findLabel(lines, line[1])
59 if target < 0 or target >= len(lines):
60 raise ValueError(f"Invalid Jump Target: {target}")
61 return target
62 else:
63 expr = line[1]
64 if command[3] == "+":
65 var = int(expr[1])
66 if values[var] > 0:
67 target = findLabel(lines, line[2])
68 if target < 0 or target >= len(lines):
69 raise ValueError(f"Invalid Jump Target: {target}")
70 return target
71 return
72 elif command[3] == "0":
73 var = int(expr[1])
74 if values[var] == 0:
75 target = findLabel(lines, line[2])
76 if target < 0 or target >= len(lines):
77 raise ValueError(f"Invalid Jump Target: {target}")
78 return target
79 return
80 else:
81 raise ValueError(f"Invalid Jump Target: {line[0]}")
82
83
84def runSimpleProgram(program: str, args: List[int]):
85 """Run a simple program with given arguments."""
86 lines = program.splitlines()
87 lines = [line.strip() for line in lines if line.strip()]
88 lines = [line.split() for line in lines if not line.startswith("!")]
89
90 values: List[int] = []
91 target = None
92 i = 0
93 return_value = None
94 while not return_value:
95 line = lines[i]
96 # print(f"executing {i}: {line}, L: {values}, A: {args}")
97 command = line[0]
98 if ":" in line or (target and target != i):
99 i += 1
100 continue
101 if target == i:
102 target = None
103 i += 1
104 if command.startswith("L"):
105 runL(args, values, line)
106 elif command.startswith("JMP"):
107 jump_to = runJMP(args, values, line, lines)
108 if jump_to != None:
109 i = jump_to
110 elif command == "RTN":
111 val = line[1]
112 # print(f"RTN {val} from A: {args}, L: {values}")
113 return_value = parse(args, values, val)
114 break
115 return return_value
116
117
118def testRunSimpleProgram():
119 print("Testing runSimpleProgram()...", end="")
120 largest = """! largest: Returns max(A0, A1)
121 L0 - A0 A1
122 JMP+ L0 a0
123 RTN A1
124 a0:
125 RTN A0"""
126 assert runSimpleProgram(largest, [5, 6]) == 6
127 assert runSimpleProgram(largest, [6, 5]) == 6
128
129 sumToN = """! SumToN: Returns 1 + ... + A0
130 ! L0 is a counter, L1 is the result
131 L0 0
132 L1 0
133 loop:
134 L2 - L0 A0
135 JMP0 L2 done
136 L0 + L0 1
137 L1 + L1 L0
138 JMP loop
139 done:
140 RTN L1"""
141 assert runSimpleProgram(sumToN, [5]) == 1 + 2 + 3 + 4 + 5
142 assert runSimpleProgram(sumToN, [10]) == 10 * 11 // 2
143 print("Passed!")
144
145
146testRunSimpleProgram()