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_val = 0
15
16 if arg.isdigit():
17 arg_val = int(arg)
18 elif arg.startswith("L"):
19 arg_val = values[int(arg[1:])]
20 elif arg.startswith("A"):
21 arg_val = args[int(arg[1:])]
22
23 return arg_val
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 var1, var2 = line[2], line[3]
38 var1_val, var2_val = parse(args, values, var1), parse(args, values, var2)
39 if op == "+":
40 values[out_var] = var1_val + var2_val
41 elif op == "-":
42 values[out_var] = var1_val - var2_val
43 else:
44 raise ValueError(f"Invalid Operator: {op}")
45
46
47def handleTargetError(target: int, lines: List[List[str]]) -> int | None:
48 if target < 0 or target >= len(lines):
49 raise ValueError(f"Invalid Jump Target: {target}")
50 return target
51
52
53def runJMP(args: List[int], values: List[int], line: List[str], lines) -> int | None:
54 """Return the target line number or None for a JMP command"""
55 command = line[0]
56 if command == "JMP":
57 jump_targ = line[1]
58 if jump_targ.isdigit():
59 target = int(line[1])
60 return handleTargetError(target, lines)
61 else:
62 target = findLabel(lines, line[1])
63 return handleTargetError(target, lines)
64 else:
65 expr = line[1]
66 if command[3] == "+":
67 var = int(expr[1])
68 if values[var] > 0:
69 target = findLabel(lines, line[2])
70 return handleTargetError(target, lines)
71 return None
72 elif command[3] == "0":
73 var = int(expr[1])
74 if values[var] == 0:
75 target = findLabel(lines, line[2])
76 return handleTargetError(target, lines)
77 return None
78 else:
79 raise ValueError(f"Invalid Jump Target: {line[0]}")
80
81
82def runSimpleProgram(program: str, args: List[int]):
83 """Run a simple program with given arguments."""
84 lines = program.splitlines()
85 lines = [line.strip() for line in lines if line.strip()]
86 lines = [line.split() for line in lines if not line.startswith("!")]
87
88 values: List[int] = []
89 i = 0
90 return_value = None
91 while not return_value:
92 line = lines[i]
93 i += 1
94 # print(f"executing {i}: {line}, L: {values}, A: {args}")
95 command = line[0]
96 if ":" in line:
97 continue
98 if command.startswith("L"):
99 runL(args, values, line)
100 elif command.startswith("JMP"):
101 jump_to = runJMP(args, values, line, lines)
102 if jump_to != None:
103 i = jump_to
104 elif command == "RTN":
105 val = line[1]
106 # print(f"RTN {val} from A: {args}, L: {values}")
107 return_value = parse(args, values, val)
108 break
109 return return_value
110
111
112def testRunSimpleProgram():
113 print("Testing runSimpleProgram()...", end="")
114 largest = """! largest: Returns max(A0, A1)
115 L0 - A0 A1
116 JMP+ L0 a0
117 RTN A1
118 a0:
119 RTN A0"""
120 assert runSimpleProgram(largest, [5, 6]) == 6
121 assert runSimpleProgram(largest, [6, 5]) == 6
122
123 sumToN = """! SumToN: Returns 1 + ... + A0
124 ! L0 is a counter, L1 is the result
125 L0 0
126 L1 0
127 loop:
128 L2 - L0 A0
129 JMP0 L2 done
130 L0 + L0 1
131 L1 + L1 L0
132 JMP loop
133 done:
134 RTN L1"""
135 assert runSimpleProgram(sumToN, [5]) == 1 + 2 + 3 + 4 + 5
136 assert runSimpleProgram(sumToN, [10]) == 10 * 11 // 2
137 print("Passed!")
138
139
140testRunSimpleProgram()