forked from calder-rh/cs1710-calculator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcode_generator.py
153 lines (130 loc) · 4.81 KB
/
code_generator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import re
# Scroll down to the bottom for an explanation
def indent_lines(s, num_spaces):
return re.sub('^', ' ' * num_spaces, s, flags=re.M)
def stack_state(nums):
state = ''
for i in range(len(nums)):
state += f't.tstack[sing[{i}]] = sing[{nums[i]}]\n'
state += f'getTopFrameIndex[t] = sing[{len(nums) - 1}]'
return state
def thread_start(start_stack):
return f'''some t : Thread | {{
{indent_lines(stack_state(start_stack), 4)}
}}'''
def thread_start_end(start_stack, end_stack):
return f'''some t : Thread | {{
{indent_lines(stack_state(start_stack), 4)}
eventually {{
some t.done
{indent_lines(stack_state(end_stack), 8)}
}}
}}'''
def start_pred(pred_name, stacks):
num_non_push_operations = 12
thread_list = [thread_start(stack) for stack in stacks]
threads = '\n\n'.join(thread_list)
return f'''pred {pred_name} {{
init
transitionStates
{indent_lines(threads, 4)}
}}
'''
# Math operations:
symbols = {'+': 'Addition',
'*': 'Multiplication',
'-': 'Subtraction',
'/': 'Division',
'%': 'Remainder',
# Stack manipulation:
'b': 'Bring',
's': 'Send',
'c': 'Copy',
'r': 'Remove',
'x': 'Swap',
'd': 'Drop',
# Comparison:
'=': 'Equal',
'>': 'Greater',
'<': 'Less',
']': 'GreaterEqual',
'[': 'LessEqual',
# Control flow:
'?': 'If',
'!': 'Jump',
'.': 'END'}
num_non_push_operations = len(symbols)
def run_threads_start_end(pred_name, stacks, max_operations, max_pushes):
thread_list = [thread_start_end(start, stacks[start]) for start in stacks]
threads = '\n\n'.join(thread_list)
return f'''pred {pred_name} {{
init
transitionStates
maxOperations[{max_operations}]
{indent_lines(threads, 4)}
}}
run {pred_name} for exactly {len(stacks)} Thread, {num_non_push_operations + max_pushes} Operation
'''
def run_function(pred_name, commands, num_threads):
code = f'''pred {pred_name} {{
init
transitionStates
'''
line_number = 0
num_pushes = 0
while commands != '':
if commands[0] == ' ':
continue
elif commands[0] in symbols:
line = f'OperationList.list[sing[{line_number}]] = {symbols[commands[0]]}'
code += indent_lines(line, 4) + '\n'
commands = commands[1:]
else:
match = re.match(r'~?\d+,?', commands)
if match is None:
break
num_pushes += 1
n_str = match.group().strip(',')
if n_str[0] == '~':
n_str = '-' + n_str[1:]
commands = commands[match.end():]
line = f'OperationList.list[sing[{line_number}]] in Push && (OperationList.list[sing[{line_number}]]).num = sing[{n_str}]'
code += indent_lines(line, 4) + '\n'
commands.strip()
line_number += 1
if commands != '':
raise Exception('could not parse')
code += f''' #list = {line_number}
}}
run {{{pred_name}}} for exactly {num_threads} Thread, {num_non_push_operations + num_pushes} Operation'''
return code
pred_name, stacks, max_operations, max_pushes
# start_pred: Specify how each thread should start.
# Inputs:
# - the name of the predicate
# - a list of tuples representing stacks
# run_threads_start_end: Specify how each thread should start and end, with a run statement.
# Inputs:
# - the name of the predicate
# - a dict of tuples->tuples representing how each stack starts and ends
# - the maximum number of operations allowed
# - the maximum number of push operations allowed
# run_function: specify what commands should happen, with run statement.
# Inputs:
# - the name of the predicate specifying the function
# - the commands (see note)
# - the number of threads to run the command for
# Command syntax:
# - Each command other than Push is represented by one character
# - See the “symbols” dict for a full list
# - Any sequence of digits is a Push of that number
# - To separate two successive Pushes, use a comma
# - (For example, 11+ pushes 11 and adds it to the number before, while 1,1+ pushes 1 and 1 and adds them)
# - To push a negative number, use ~ for the minus sign since - is for subtraction
# output = run_threads_start_end('twentyFour', {(4, 7, 8, 8): (24,)}, 7, 2)
# output = run_function('simpleIf', '3,4?1.', 3)
# output = start_pred('simpleIfStartValues', [(-3,), (0,), (2,)])
# output = run_function('absoluteValue', '0<5,9?d~1*.d.', 1)
# output = start_pred('absoluteValueStartValues', [(-5,), (-1,), (0,), (1,), (5,)])
output = run_threads_start_end('nPlus1TimesN', {(0,): (0,), (1,): (2,), (2,): (6,), (-3,): (6,)}, 5, 1)
print(output)