forked from august0815/forthos
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathforth2s.py
executable file
·255 lines (211 loc) · 7.24 KB
/
forth2s.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#! /usr/bin/python
# program: forth2s.py
# Compile a .forth file to a .s file.
# License: GPL
# Jose Dinuncio <[email protected]>, 12/2009
import re
import commands
import string
# Option parser
from optparse import OptionParser
parser = OptionParser()
parser.add_option('-i', dest='finname', default='/dev/stdin',
help='Name of the input file')
parser.add_option('-o', dest='foutname', default='/dev/stdout',
help='Name of the output file')
# ============================================================================
# States of the translator
# ============================================================================
def copy_lines(fin, fout):
'''
function: copy_lines
Copy lines from fin to fout.
If the line starts with ':' then start to translate the lines from forth
to nasm.
Params:
fin - file to read.
fout - file to write.
'''
for line in fin:
if line.startswith(':'):
defword = translate_forth_def(line)
fout.write(defword)
fout.write('\n')
translate_lines(fin, fout)
else:
fout.write(line)
def translate_lines(fin, fout):
'''
function: translate_lines
Translate lines of forth code to nasm assembler.
The forth code must end in a line beginning with a semicolon.
The only comments accepted are line comments. They start with a '#'.
Params:
fin - file to read.
fout - file to write.
'''
for line in fin:
if line.startswith(';'):
fout.write(' dd exit\n')
return
for assembly in translate(line):
fout.write(' %s\n' % assembly)
# ============================================================================
# Scanner for the translate_lines state
# ============================================================================
def forth_comment(scanner, token):
'''
function: forth_comment
Translate a forth comment into an assembly comment.
A forth comment starts with the '(' token and end with the ')' token.
It must end in the same line it started.
'''
return ['; %s' % token[1:-1]]
def line_comment(scanner, token):
'''
function: line_comment
Translate a forth line comment into an assembly comment.
In this forth, a line comment starts with ';' and ends with the line.
'''
return ['; %s' % token[1:]]
def asm_literal(scanner, token):
'''
function: asm_literal
Insert assembly code in a forth word.
The assembly code is limited by '{' and '}'. Each line of nasm assembly
is separated by ';'. The assembly literal must end in the same line.
'''
asm = token[1:-1].split(';')
return asm
def literal(scanner, token):
'''
function: literal
Translate a literal word to assembly.
'''
return ['litn %s' % token]
def word_literal(scanner, token):
'''
function: word_literal
Translate a ['] forth expression to assembly.
In this forth we use [`] instead, for the syntax highlighting.
'''
return ['litn %s' % token.split()[1]]
def word1_literal(scanner, token):
'''
function: word_literal
Translate a ['] forth expression to assembly.
In this forth we use [`] instead, for the syntax highlighting.
'''
return ['dd %s' % token.split()[1]]
def word(scanner, token):
'''
function: word
Translate a forth word.
The forth word can be a translate to a literal, a macro or a forth word.
'''
if token in MACROS:
return [token]
elif token in LITERALS:
return ['litn %s' % token]
elif token in SYMBOLS:
return ['dd %s' % SYMBOLS[token]]
else:
return ['dd %s' % token]
scanner = re.Scanner([
(r'\(\s.*\s\)', forth_comment),
(r';.*', line_comment),
(r'\{\s.*\}', asm_literal),
(r'[-+][0-9A-Fa-f]+', literal),
(r"'.'", literal),
(r'0[xX][0-9A-Fa-f]+', literal),
(r'\d+\s', literal),
(r"\[`\]\s+\S+", word_literal),
(r"\[#\]\s+\S+", word1_literal),
(r'\S+', word),
(r'\s+', None),
])
def translate(line):
trans, remainder = scanner.scan(line)
return sum([ts for ts in trans], [])
# ============================================================================
# Support functions
# ============================================================================
def translate_forth_def(line):
'''
function: translate_forth_def
Translate the definition of a forth word to nasm assembly.
The forth definition must start at the begining of line, and must have the
following structure:
: name -> defword "name", name, 0
Where:
name - The name of the forth word, as seen for other forth words.
label - The name of the forth word, as seen by assembly code.
flags - Flags of this forth word. See forth_core.s and forth_macros.s
for more details.
Params:
line - The first line of a forth word definition
Returns:
string - A line of text with the defword of the forth word being defined.
Stripping whitespace from end of line
'''
tmp = line.strip()
defword = 'defword "' + tmp[2:] + '", ' + tmp[2:] + ', 0'
return defword
def get_symbols():
'''
function: get_symbols
Returns a dict wich associate forth words with its assembly labels. It
is used to translate forth words with symbols in it.
'''
dct = {}
lines = commands.getoutput("grep '^def[vcw]' *.s *.fth").splitlines()
for line in lines:
parts = line.split()
parts = ''.join(parts[1:]).split(',')
key = parts[0][1:-1]
val = parts[1]
dct[key] = val
lines=commands.getoutput("grep '^: ' *.fth").splitlines()
for line in lines:
parts = line.split()
key = parts[1]
val = parts[1]
dct[key] = val
return dct
def get_literals():
'''
function: get_literals
Return a list with the names of the %defines and labels found in assembly
or forth files. It is used to translate literals words.
'''
# Get 'define' literals
defs = commands.getoutput("grep '^%define ' *.s *.fth").splitlines()
defs = [x.split()[1] for x in defs]
# Get labels
labels = commands.getoutput(
"grep '^[:space:]*[A-Za-z0-9_]\+:' *.s *.fth").splitlines()
labels = [x.split(':')[1] for x in labels]
return defs + labels
def get_macros():
'''
function: get_macros
Returns a list with the name of all the macros found in assembly or
forth files. It is used to translate macro words.
'''
return commands.getoutput(
"grep -r '%macro' *.s *.fth| awk '{print $2}'").split()
MACROS = get_macros()
SYMBOLS = get_symbols()
LITERALS = get_literals()
def main():
'''
function: main
Translate forth code in a file to nasm assembler and stores in other
file.
'''
opts, args = parser.parse_args()
fin = open(opts.finname)
fout = open(opts.foutname, 'w')
copy_lines(fin, fout)
if __name__ == '__main__' :
main()