/
old_anoky.py
231 lines (230 loc) · 10.5 KB
/
old_anoky.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
import anoky.importer as __akyimp__
import anoky.module as __aky__
__macros__ = {}
__id_macros__ = {}
__special_forms__ = {}
from astformatter import ASTFormatter
from anoky.common.globals import G
from anoky.common.record import Record
from anoky.expansion.expander import DefaultExpander
from anoky.generation.generator import DefaultGenerator
from anoky.parsers.anoky_parser import AnokyParser
from anoky.streams.file_stream import FileStream
from anoky.syntax.lisp_printer import indented_lisp_printer
from anoky.common.errors import CompilerError, TokenizingError
from anoky.streams.string_stream import StringStream
from anoky.generation.default_special_forms_table import default_special_forms_table
from anoky.expansion.default_macro_table import default_macro_table, default_id_macro_table
from anoky.syntax.token import is_token
from prompt_toolkit.history import InMemoryHistory
from prompt_toolkit import prompt
import argparse
import ast
import astpp
import sys
import traceback
import os
import anoky.syntax.tokens as Tokens
__parser__ = AnokyParser()
__macros__ = default_macro_table()
__id_macros__ = default_id_macro_table()
__special_forms__ = default_special_forms_table()
code_expander = DefaultExpander()
code_generator = DefaultGenerator()
def anoky_tokenize(stream,options):
tokenized_node = __parser__.tokenize_into_node(stream, emmit_restart_tokens=False)
if options.print_tokens:
print('\n——›– Tokenized source –‹——')
for token in tokenized_node:
print(str(token))
errors = []
for token in tokenized_node:
if is_token(token, Tokens.ERROR):
errors.append(token)
if len(errors) > 0:
message = ''
for token in errors:
if token.message is not None and token.message != '':
message += '%s: %s\n' % (token.range, token.message)
raise TokenizingError(None, message)
return tokenized_node
def anoky_transduce(node,options):
__parser__.transduce(node)
if options.print_parse:
print('\n——›– Parsed source before macro expansion –‹——', end='')
print(indented_lisp_printer(node))
def anoky_expand(parsed_node,options):
code_expander.expand_unit(parsed_node, macros=__macros__, id_macros=__id_macros__)
if options.print_macro_expanded_code:
print('\n——›– Parsed source after macro expansion –‹——', end='')
print(indented_lisp_printer(parsed_node))
def print_ast(py_ast,message='\n——›– Generated Python AST –‹——'):
print(message)
try:
astpp.parseprint(py_ast)
except Exception:
print('\n!–›– Failed to print Python AST! –‹–!')
traceback.print_exc()
def print_python_code(py_ast,message='\n——›– Generated Python Source Code –‹——'):
try:
if isinstance(py_ast, ast.Interactive):
py_ast = ast.Module(body=py_ast.body)
python_source = ASTFormatter().format(py_ast)
except Exception:
print('\n!–›– Failed to generate Python Source Code! –‹–!')
traceback.print_exc()
else:
print(message)
print(python_source)
def anoky_generate(parsed_node,options,CG):
py_ast = code_generator.generate_elements(parsed_node, CG)
return py_ast
def interactive_anoky(options):
options.filename = '<interactive>'
sys.path = [''] + sys.path
(CG, init_code) = code_generator.begin(interactive=True, special_forms=__special_forms__, macros=__macros__, id_macros=__id_macros__)
interactive_history = InMemoryHistory()
try:
while True:
written_code = prompt('>>> ', history=interactive_history, multiline=True)
stream = StringStream(written_code, '<interactive>')
try:
node = anoky_tokenize(stream, options)
if not options.arrange_tokens:
continue
anoky_transduce(node, options)
if not options.expand_macros:
continue
anoky_expand(node, options)
if not options.generate_code:
continue
py_ast = anoky_generate(node, options, CG)
py_ast = code_generator.end(py_ast, CG)
if options.print_python_ast:
print_ast(py_ast)
if options.print_python_code:
print_python_code(py_ast)
except CompilerError as e:
print(e.trace)
except Exception:
print('\n!—›– Compiler raised unhandled exception (this is not supposed to happen)!!! –‹—!')
traceback.print_exc()
else:
ast.fix_missing_locations(py_ast)
try:
compiled_ast = compile(py_ast, filename='<interactive>', mode='single')
except Exception:
print('\n——›– AST compilation failed !!! –‹——')
traceback.print_exc()
print_ast(py_ast)
print_python_code(py_ast)
else:
if options.execute:
try:
exec(compiled_ast)
except Exception as e:
traceback.print_exc()
except EOFError:
return
except KeyboardInterrupt:
return
def non_interactive_anoky(options):
(CG, init_code) = code_generator.begin(interactive=False)
try:
filename = options.filename
filedir = os.path.split(filename)[0]
sys.path = [filedir] + sys.path
stream = FileStream(filename)
node = anoky_tokenize(stream, options)
if not options.arrange_tokens:
return
anoky_transduce(node, options)
if not options.expand_macros:
return
anoky_expand(node, options)
if not options.generate_code:
return
py_ast = init_code + anoky_generate(node, options, CG)
py_ast = code_generator.end(py_ast, CG)
if options.print_python_ast:
print_ast(py_ast)
if options.print_python_code:
print_python_code(py_ast)
except CompilerError as e:
print(e.trace, file=sys.stderr)
else:
if options.compile_to_python:
try:
python_source = ASTFormatter().format(py_ast)
except Exception:
print('\n!–›– Failed to generate Python Source Code! –‹–!', file=sys.stderr)
traceback.print_exc()
return
else:
out_file = open(filename + '.py', 'w')
print(python_source, file=out_file)
out_file.close()
if options.execute:
ast.fix_missing_locations(py_ast)
compiled_module = compile(py_ast, filename=os.path.abspath(filename), mode='exec')
exec(compiled_module)
def main():
parser = argparse.ArgumentParser(prefix_chars='-')
parser.add_argument('--print-tokens', action='store_true', help='Prints the tokenizer output.')
parser.add_argument('--print-tree-transducer-outputs', action='store_true', help='Prints the output of each transducer in the chain.')
parser.add_argument('--print-arrangement-outputs', action='store_true', help='Prints the output of each arrangement within each arrangement-based transducer in the chain.')
parser.add_argument('--print-parse', action='store_true', help='Prints the outcome of parsing the written code (the forms, seqs, etc).')
parser.add_argument('--print-macro-expanded-code', action='store_true', help='Prints the code after macro-expansion.')
parser.add_argument('--print-python-ast', action='store_true', help='Prints the generated Python AST.')
parser.add_argument('--print-python-code', action='store_true', help='Prints the generated Python source.')
parser.add_argument('--skip-transducing', action='store_true', help='Stops the compiler immediately after tokenization.')
parser.add_argument('--skip-macroexpansion', action='store_true', help='Stops the compiler immediately after parsing.')
parser.add_argument('--skip-codegen', action='store_true', help='Stops the compiler immediately after macro-expansion.')
parser.add_argument('--stdout', action='store_true')
parser.add_argument('-I', '--include', help='A colon-separated list of source paths where to search for imported modules.')
parser.add_argument('-E', '--toggle-exec', action='store_true', help='Executes the compiled code even in non-interactive mode, and does not execute in interactive mode.')
parser.add_argument('-CP', '--compile-to-python', action='store_true', help='Compiles to Python, outputing the result into a .py file on the same path.')
parser.add_argument('--version', action='version', version='Anoky α.1')
parser.add_argument('file', nargs='*')
parse = parser.parse_args()
options = Record()
options.print_tokens = parse.print_tokens
if parse.print_tree_transducer_outputs:
G.Options.PRINT_TREE_TRANSDUCER_OUTPUTS = True
if parse.print_arrangement_outputs:
G.Options.PRINT_ARRANGEMENT_OUTPUTS = True
options.print_parse = parse.print_parse
options.print_macro_expanded_code = parse.print_macro_expanded_code
options.include_dirs = parse.include.split(':') if parse.include else ['.']
if len(parse.file) > 1:
raise NotImplementedError()
elif len(parse.file) == 0:
options.interactive = True
options.compile_to_python = False
options.execute = not parse.toggle_exec
else:
options.interactive = False
options.filename = parse.file[0]
options.compile_to_python = parse.compile_to_python
options.execute = parse.toggle_exec
options.arrange_tokens = not parse.skip_transducing
if not options.arrange_tokens:
options.print_tokens = True
options.expand_macros = not parse.skip_macroexpansion and not parse.skip_transducing
if not options.expand_macros:
options.print_parse = True
options.generate_code = not parse.skip_macroexpansion and not parse.skip_transducing and not parse.skip_codegen
if not options.generate_code:
options.print_macro_expanded_code = True
options.print_python_code = parse.print_python_code or not options.execute
options.print_python_ast = parse.print_python_ast
if parse.stdout:
options.output = '<stdout>'
else:
options.output = '.'
if options.interactive:
interactive_anoky(options)
else:
non_interactive_anoky(options)
if __name__ == '__main__':
main()