-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbasicodify.py
139 lines (119 loc) · 4.39 KB
/
basicodify.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
# Hacky script to take a bunch of "high-level" basic files and convert it to a minimal BASICODE listing.
# Note: this does not tokenize the input: It just uses a bunch of regular expressions.
import re
from sys import stdout
from argparse import ArgumentParser, FileType
parser = ArgumentParser()
parser.add_argument("files", nargs="+", type=FileType("r"))
parser.add_argument("-o", "--output", action="store")
parsedArgs = parser.parse_args()
srcLines = []
for f in parsedArgs.files:
srcLines += f.readlines()
# Labels are GOTO/GOSUB targets or constant definitions.
labelTable = {}
whitespaceMatcher = re.compile(r"\s*$")
lineNumberMatcher = re.compile(r"\d+:\s*$")
labelMatcher = re.compile(r"_\w+_:\s*$")
lineMatcher = re.compile(r"\s.*")
commentMatcher = re.compile(r"\s*#.*$")
assignmentMatcher = re.compile(r"_\w+_\s*=.*$")
ifThenMatcher = re.compile(r".*IF.*THEN.*$")
# Basicode programs start at 1000
currentLine = 1000
# A mapping from fixed line numbers to lines of code which will follow from there.
# Each label or explicit line number in the code will start a entry.
codeLines = { currentLine: [] }
# Parse the contents.
for l in srcLines:
if whitespaceMatcher.match(l):
continue
elif lineNumberMatcher.match(l):
colonIndex = l.find(":")
currentLine = int(l[:colonIndex])
codeLines[currentLine] = []
elif labelMatcher.match(l):
colonIndex = l.find(":")
currentLine += 300
labelTable[l[:colonIndex]] = str(currentLine)
codeLines[currentLine] = []
elif commentMatcher.match(l):
continue
elif assignmentMatcher.match(l):
equalIndex = l.find("=")
labelTable[l[:equalIndex].strip()] = l[equalIndex+1:].strip()
elif lineMatcher.match(l):
codeLines[currentLine] = codeLines[currentLine] + [l[:-1].strip()]
def replaceLabels(l):
r = []
for i in l:
for (k,v) in labelTable.items():
i = i.replace(k, v)
r.append(i)
return r
codeLines = { n:replaceLabels(l) for (n, l) in codeLines.items() }
keywords = [ "PRINT", "INPUT", "GOTO", "GOSUB", "RETURN", "LET", "FOR", "TO",
"STEP", "NEXT", "IF", "THEN", "ON", "RUN", "STOP", "END", "DIM", "READ", "DATA", "RESTORE", "REM",
"TAB", "ABS", "SGN", "INT", "SQR", "SIN", "COS", "TAN", "ATN", "EXP", "LOG", "ASC", "VAL", "LEN", "CHR$", "LEFT$", "MID$", "RIGHT$",
"AND", "OR", "NOT" ]
# Remove spaces where they won't confuse the parser
def collapse(l):
r = []
for i in l:
s = ""
tokens = []
pretokens = i.split('\"')
for j in range(0, len(pretokens)):
if j%2 == 0:
tokens += pretokens[j].split()
else:
tokens.append('\"' + pretokens[j] + '\"')
for t in tokens:
endsWithToken = False
for k in keywords:
if s.endswith(k):
endsWithToken = True
break
if len(s) == 0 or endsWithToken or not s[-1].isalpha() or not t[0].isalpha():
s = s + t
else:
s = s + " " + t
if len(s) > 56:
raise Exception("Line " + str(len(s)-56) + " characters too long: " + s)
r.append(s)
return r
codeLines = { n:collapse(l) for (n, l) in codeLines.items() }
# THEN GOTO = THEN
def removeGotos(l):
return [i.replace("THENGOTO", "THEN") for i in l ]
codeLines = { n:removeGotos(l) for (n, l) in codeLines.items() }
result = []
# Pack code into lines of at most 60 characters
for (num,line) in codeLines.items():
currentLine = num
outline = str(currentLine)
separator= ""
for l in line:
if len(outline) + 1 + len(l) > 60:
result += [outline]
currentLine += 10
outline = str(currentLine)
separator = ""
if ifThenMatcher.match(l):
outline += separator + l
result += [outline]
currentLine += 10
outline = str(currentLine)
separator = ""
else:
outline += separator + l
separator = ":"
if len(outline) > 4:
result += [outline]
result.sort()
output = stdout
if parsedArgs.output:
output = open(parsedArgs.output, "w")
for l in result:
output.write(l)
output.write("\n")