python-cc 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pcc/__init__.py +0 -0
- pcc/__main__.py +3 -0
- pcc/ast/__init__.py +0 -0
- pcc/ast/ast.py +179 -0
- pcc/ast/ast_transforms.py +106 -0
- pcc/ast/c_ast.py +800 -0
- pcc/codegen/__init__.py +0 -0
- pcc/codegen/c_codegen.py +4177 -0
- pcc/evaluater/__init__.py +0 -0
- pcc/evaluater/c_evaluator.py +238 -0
- pcc/generator/__init__.py +0 -0
- pcc/generator/c_generator.py +399 -0
- pcc/lex/__init__.py +0 -0
- pcc/lex/c_lexer.py +495 -0
- pcc/lex/lexer.py +68 -0
- pcc/lex/token.py +24 -0
- pcc/parse/__init__.py +0 -0
- pcc/parse/c_parser.py +1700 -0
- pcc/parse/file_parser.py +82 -0
- pcc/parse/parser.py +300 -0
- pcc/parse/plyparser.py +56 -0
- pcc/pcc.py +38 -0
- pcc/ply/__init__.py +5 -0
- pcc/ply/cpp.py +908 -0
- pcc/ply/ctokens.py +133 -0
- pcc/ply/lex.py +1097 -0
- pcc/ply/yacc.py +3471 -0
- pcc/ply/ygen.py +74 -0
- pcc/preprocessor.py +509 -0
- pcc/project.py +78 -0
- pcc/util.py +121 -0
- python_cc-0.0.2.dist-info/METADATA +182 -0
- python_cc-0.0.2.dist-info/RECORD +36 -0
- python_cc-0.0.2.dist-info/WHEEL +4 -0
- python_cc-0.0.2.dist-info/entry_points.txt +2 -0
- python_cc-0.0.2.dist-info/licenses/LICENSE +25 -0
pcc/parse/file_parser.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
|
|
2
|
+
from subprocess import Popen, PIPE
|
|
3
|
+
from .c_parser import CParser
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def preprocess_file(filename, cpp_path='cpp', cpp_args=''):
|
|
7
|
+
""" Preprocess a file using cpp.
|
|
8
|
+
|
|
9
|
+
filename:
|
|
10
|
+
Name of the file you want to preprocess.
|
|
11
|
+
|
|
12
|
+
cpp_path:
|
|
13
|
+
cpp_args:
|
|
14
|
+
Refer to the documentation of parse_file for the meaning of these
|
|
15
|
+
arguments.
|
|
16
|
+
|
|
17
|
+
When successful, returns the preprocessed file's contents.
|
|
18
|
+
Errors from cpp will be printed out.
|
|
19
|
+
"""
|
|
20
|
+
path_list = [cpp_path]
|
|
21
|
+
if isinstance(cpp_args, list):
|
|
22
|
+
path_list += cpp_args
|
|
23
|
+
elif cpp_args != '':
|
|
24
|
+
path_list += [cpp_args]
|
|
25
|
+
path_list += [filename]
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
# Note the use of universal_newlines to treat all newlines
|
|
29
|
+
# as \n for Python's purpose
|
|
30
|
+
#
|
|
31
|
+
pipe = Popen( path_list,
|
|
32
|
+
stdout=PIPE,
|
|
33
|
+
universal_newlines=True)
|
|
34
|
+
text = pipe.communicate()[0]
|
|
35
|
+
except OSError as e:
|
|
36
|
+
raise RuntimeError("Unable to invoke 'cpp'. " +
|
|
37
|
+
'Make sure its path was passed correctly\n' +
|
|
38
|
+
('Original error: %s' % e))
|
|
39
|
+
|
|
40
|
+
return text
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def parse_file(filename, use_cpp=False, cpp_path='cpp', cpp_args='',
|
|
44
|
+
parser=None):
|
|
45
|
+
""" Parse a C file using pycparser.
|
|
46
|
+
|
|
47
|
+
filename:
|
|
48
|
+
Name of the file you want to parse.
|
|
49
|
+
|
|
50
|
+
use_cpp:
|
|
51
|
+
Set to True if you want to execute the C pre-processor
|
|
52
|
+
on the file prior to parsing it.
|
|
53
|
+
|
|
54
|
+
cpp_path:
|
|
55
|
+
If use_cpp is True, this is the path to 'cpp' on your
|
|
56
|
+
system. If no path is provided, it attempts to just
|
|
57
|
+
execute 'cpp', so it must be in your PATH.
|
|
58
|
+
|
|
59
|
+
cpp_args:
|
|
60
|
+
If use_cpp is True, set this to the command line arguments strings
|
|
61
|
+
to cpp. Be careful with quotes - it's best to pass a raw string
|
|
62
|
+
(r'') here. For example:
|
|
63
|
+
r'-I../utils/fake_libc_include'
|
|
64
|
+
If several arguments are required, pass a list of strings.
|
|
65
|
+
|
|
66
|
+
parser:
|
|
67
|
+
Optional parser object to be used instead of the default CParser
|
|
68
|
+
|
|
69
|
+
When successful, an AST is returned. ParseError can be
|
|
70
|
+
thrown if the file doesn't parse successfully.
|
|
71
|
+
|
|
72
|
+
Errors from cpp will be printed out.
|
|
73
|
+
"""
|
|
74
|
+
if use_cpp:
|
|
75
|
+
text = preprocess_file(filename, cpp_path, cpp_args)
|
|
76
|
+
else:
|
|
77
|
+
with open(filename, 'r') as f:
|
|
78
|
+
text = f.read()
|
|
79
|
+
|
|
80
|
+
if parser is None:
|
|
81
|
+
parser = CParser()
|
|
82
|
+
return parser.parse(text, filename)
|
pcc/parse/parser.py
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
from pcc.lex.token import TokenKind
|
|
2
|
+
from pcc.lex.lexer import Lexer
|
|
3
|
+
from pcc.ast.ast import *
|
|
4
|
+
|
|
5
|
+
class ParseError(Exception):
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Parser(object):
|
|
10
|
+
"""Parser for the Kaleidoscope language.
|
|
11
|
+
|
|
12
|
+
After the parser is created, invoke parse_toplevel multiple times to parse
|
|
13
|
+
Kaleidoscope source into an AST.
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.token_generator = None
|
|
17
|
+
self.cur_tok = None
|
|
18
|
+
|
|
19
|
+
# toplevel ::= definition | external | expression | ';'
|
|
20
|
+
def parse_toplevel(self, buf):
|
|
21
|
+
"""Given a string, returns an AST node representing it."""
|
|
22
|
+
self.token_generator = Lexer(buf).tokens()
|
|
23
|
+
self.cur_tok = None
|
|
24
|
+
self._get_next_token()
|
|
25
|
+
|
|
26
|
+
if self.cur_tok.kind == TokenKind.EXTERN:
|
|
27
|
+
return self._parse_external()
|
|
28
|
+
elif self.cur_tok.kind == TokenKind.DEF:
|
|
29
|
+
return self._parse_definition()
|
|
30
|
+
elif self._cur_tok_is_operator(';'):
|
|
31
|
+
self._get_next_token()
|
|
32
|
+
return None
|
|
33
|
+
else:
|
|
34
|
+
return self._parse_toplevel_expression()
|
|
35
|
+
|
|
36
|
+
def _get_next_token(self):
|
|
37
|
+
self.cur_tok = next(self.token_generator)
|
|
38
|
+
|
|
39
|
+
def _match(self, expected_kind, expected_value=None):
|
|
40
|
+
"""Consume the current token; verify that it's of the expected kind.
|
|
41
|
+
|
|
42
|
+
If expected_kind == TokenKind.OPERATOR, verify the operator's value.
|
|
43
|
+
"""
|
|
44
|
+
if (expected_kind == TokenKind.OPERATOR and
|
|
45
|
+
not self._cur_tok_is_operator(expected_value)):
|
|
46
|
+
raise ParseError('Expected "{0}"'.format(expected_value))
|
|
47
|
+
elif expected_kind != self.cur_tok.kind:
|
|
48
|
+
raise ParseError('Expected "{0}"'.format(expected_kind))
|
|
49
|
+
self._get_next_token()
|
|
50
|
+
|
|
51
|
+
_precedence_map = {'=': 2, '<': 10, '+': 20, '-': 20, '*': 40}
|
|
52
|
+
|
|
53
|
+
def _cur_tok_precedence(self):
|
|
54
|
+
"""Get the operator precedence of the current token."""
|
|
55
|
+
try:
|
|
56
|
+
return self._precedence_map[self.cur_tok.value]
|
|
57
|
+
except KeyError:
|
|
58
|
+
return -1
|
|
59
|
+
|
|
60
|
+
def _cur_tok_is_operator(self, op):
|
|
61
|
+
"""Query whether the current token is the operator op"""
|
|
62
|
+
return (self.cur_tok.kind == TokenKind.OPERATOR and
|
|
63
|
+
self.cur_tok.value == op)
|
|
64
|
+
|
|
65
|
+
# identifierexpr
|
|
66
|
+
# ::= identifier
|
|
67
|
+
# ::= identifier '(' expression* ')'
|
|
68
|
+
def _parse_identifier_expr(self):
|
|
69
|
+
id_name = self.cur_tok.value
|
|
70
|
+
self._get_next_token()
|
|
71
|
+
# If followed by a '(' it's a call; otherwise, a simple variable ref.
|
|
72
|
+
if not self._cur_tok_is_operator('('):
|
|
73
|
+
return VariableExprAST(id_name)
|
|
74
|
+
|
|
75
|
+
self._get_next_token()
|
|
76
|
+
args = []
|
|
77
|
+
if not self._cur_tok_is_operator(')'):
|
|
78
|
+
while True:
|
|
79
|
+
args.append(self._parse_expression())
|
|
80
|
+
if self._cur_tok_is_operator(')'):
|
|
81
|
+
break
|
|
82
|
+
self._match(TokenKind.OPERATOR, ',')
|
|
83
|
+
|
|
84
|
+
self._get_next_token() # consume the ')'
|
|
85
|
+
return CallExprAST(id_name, args)
|
|
86
|
+
|
|
87
|
+
# numberexpr ::= number
|
|
88
|
+
def _parse_number_expr(self):
|
|
89
|
+
result = NumberExprAST(self.cur_tok.value)
|
|
90
|
+
self._get_next_token() # consume the number
|
|
91
|
+
return result
|
|
92
|
+
|
|
93
|
+
# parenexpr ::= '(' expression ')'
|
|
94
|
+
def _parse_paren_expr(self):
|
|
95
|
+
self._get_next_token() # consume the '('
|
|
96
|
+
expr = self._parse_expression()
|
|
97
|
+
self._match(TokenKind.OPERATOR, ')')
|
|
98
|
+
return expr
|
|
99
|
+
|
|
100
|
+
# primary
|
|
101
|
+
# ::= identifierexpr
|
|
102
|
+
# ::= numberexpr
|
|
103
|
+
# ::= parenexpr
|
|
104
|
+
# ::= ifexpr
|
|
105
|
+
# ::= forexpr
|
|
106
|
+
def _parse_primary(self):
|
|
107
|
+
if self.cur_tok.kind == TokenKind.IDENTIFIER:
|
|
108
|
+
return self._parse_identifier_expr()
|
|
109
|
+
elif self.cur_tok.kind == TokenKind.NUMBER:
|
|
110
|
+
return self._parse_number_expr()
|
|
111
|
+
elif self._cur_tok_is_operator('('):
|
|
112
|
+
return self._parse_paren_expr()
|
|
113
|
+
elif self.cur_tok.kind == TokenKind.IF:
|
|
114
|
+
return self._parse_if_expr()
|
|
115
|
+
elif self.cur_tok.kind == TokenKind.FOR:
|
|
116
|
+
return self._parse_for_expr()
|
|
117
|
+
elif self.cur_tok.kind == TokenKind.VAR:
|
|
118
|
+
return self._parse_var_expr()
|
|
119
|
+
else:
|
|
120
|
+
raise ParseError('Unknown token when expecting an expression')
|
|
121
|
+
|
|
122
|
+
# ifexpr ::= 'if' expression 'then' expression 'else' expression
|
|
123
|
+
def _parse_if_expr(self):
|
|
124
|
+
self._get_next_token() # consume the 'if'
|
|
125
|
+
cond_expr = self._parse_expression()
|
|
126
|
+
self._match(TokenKind.THEN)
|
|
127
|
+
then_expr = self._parse_expression()
|
|
128
|
+
self._match(TokenKind.ELSE)
|
|
129
|
+
else_expr = self._parse_expression()
|
|
130
|
+
return IfExprAST(cond_expr, then_expr, else_expr)
|
|
131
|
+
|
|
132
|
+
# forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in' expr
|
|
133
|
+
def _parse_for_expr(self):
|
|
134
|
+
self._get_next_token() # consume the 'for'
|
|
135
|
+
id_name = self.cur_tok.value
|
|
136
|
+
self._match(TokenKind.IDENTIFIER)
|
|
137
|
+
self._match(TokenKind.OPERATOR, '=')
|
|
138
|
+
start_expr = self._parse_expression()
|
|
139
|
+
self._match(TokenKind.OPERATOR, ',')
|
|
140
|
+
end_expr = self._parse_expression()
|
|
141
|
+
|
|
142
|
+
# The step part is optional
|
|
143
|
+
if self._cur_tok_is_operator(','):
|
|
144
|
+
self._get_next_token()
|
|
145
|
+
step_expr = self._parse_expression()
|
|
146
|
+
else:
|
|
147
|
+
step_expr = None
|
|
148
|
+
self._match(TokenKind.IN)
|
|
149
|
+
body = self._parse_expression()
|
|
150
|
+
return ForExprAST(id_name, start_expr, end_expr, step_expr, body)
|
|
151
|
+
|
|
152
|
+
# varexpr ::= 'var' identifier ('=' expr)?
|
|
153
|
+
# (',' identifier ('=' expr)?)* 'in' expr
|
|
154
|
+
def _parse_var_expr(self):
|
|
155
|
+
self._get_next_token() # consume the 'var'
|
|
156
|
+
vars = []
|
|
157
|
+
|
|
158
|
+
# At least one variable name is required
|
|
159
|
+
if self.cur_tok.kind != TokenKind.IDENTIFIER:
|
|
160
|
+
raise ParseError('expected identifier after "var"')
|
|
161
|
+
while True:
|
|
162
|
+
name = self.cur_tok.value
|
|
163
|
+
self._get_next_token() # consume the identifier
|
|
164
|
+
|
|
165
|
+
# Parse the optional initializer
|
|
166
|
+
if self._cur_tok_is_operator('='):
|
|
167
|
+
self._get_next_token() # consume the '='
|
|
168
|
+
init = self._parse_expression()
|
|
169
|
+
else:
|
|
170
|
+
init = None
|
|
171
|
+
vars.append((name, init))
|
|
172
|
+
|
|
173
|
+
# If there are no more vars in this declaration, we're done.
|
|
174
|
+
if not self._cur_tok_is_operator(','):
|
|
175
|
+
break
|
|
176
|
+
self._get_next_token() # consume the ','
|
|
177
|
+
if self.cur_tok.kind != TokenKind.IDENTIFIER:
|
|
178
|
+
raise ParseError('expected identifier in "var" after ","')
|
|
179
|
+
|
|
180
|
+
self._match(TokenKind.IN)
|
|
181
|
+
body = self._parse_expression()
|
|
182
|
+
return VarExprAST(vars, body)
|
|
183
|
+
|
|
184
|
+
# unary
|
|
185
|
+
# ::= primary
|
|
186
|
+
# ::= <op> unary
|
|
187
|
+
def _parse_unary(self):
|
|
188
|
+
# no unary operator before a primary
|
|
189
|
+
if (not self.cur_tok.kind == TokenKind.OPERATOR or
|
|
190
|
+
self.cur_tok.value in ('(', ',')):
|
|
191
|
+
return self._parse_primary()
|
|
192
|
+
|
|
193
|
+
# unary operator
|
|
194
|
+
op = self.cur_tok.value
|
|
195
|
+
self._get_next_token()
|
|
196
|
+
return UnaryExprAST(op, self._parse_unary())
|
|
197
|
+
|
|
198
|
+
# binoprhs ::= (<binop> primary)*
|
|
199
|
+
def _parse_binop_rhs(self, expr_prec, lhs):
|
|
200
|
+
"""Parse the right-hand-side of a binary expression.
|
|
201
|
+
|
|
202
|
+
expr_prec: minimal precedence to keep going (precedence climbing).
|
|
203
|
+
lhs: AST of the left-hand-side.
|
|
204
|
+
"""
|
|
205
|
+
while True:
|
|
206
|
+
cur_prec = self._cur_tok_precedence()
|
|
207
|
+
# If this is a binary operator with precedence lower than the
|
|
208
|
+
# currently parsed sub-expression, bail out. If it binds at least
|
|
209
|
+
# as tightly, keep going.
|
|
210
|
+
# Note that the precedence of non-operators is defined to be -1,
|
|
211
|
+
# so this condition handles cases when the expression ended.
|
|
212
|
+
if cur_prec < expr_prec:
|
|
213
|
+
return lhs
|
|
214
|
+
op = self.cur_tok.value
|
|
215
|
+
self._get_next_token() # consume the operator
|
|
216
|
+
rhs = self._parse_unary()
|
|
217
|
+
|
|
218
|
+
next_prec = self._cur_tok_precedence()
|
|
219
|
+
# There are three options:
|
|
220
|
+
# 1. next_prec > cur_prec: we need to make a recursive call
|
|
221
|
+
# 2. next_prec == cur_prec: no need for a recursive call, the next
|
|
222
|
+
# iteration of this loop will handle it.
|
|
223
|
+
# 3. next_prec < cur_prec: no need for a recursive call, combine
|
|
224
|
+
# lhs and the next iteration will immediately bail out.
|
|
225
|
+
if cur_prec < next_prec:
|
|
226
|
+
rhs = self._parse_binop_rhs(cur_prec + 1, rhs)
|
|
227
|
+
|
|
228
|
+
# Merge lhs/rhs
|
|
229
|
+
lhs = BinaryExprAST(op, lhs, rhs)
|
|
230
|
+
|
|
231
|
+
# expression ::= primary binoprhs
|
|
232
|
+
def _parse_expression(self):
|
|
233
|
+
lhs = self._parse_unary()
|
|
234
|
+
# Start with precedence 0 because we want to bind any operator to the
|
|
235
|
+
# expression at this point.
|
|
236
|
+
return self._parse_binop_rhs(0, lhs)
|
|
237
|
+
|
|
238
|
+
# prototype
|
|
239
|
+
# ::= id '(' id* ')'
|
|
240
|
+
# ::= 'binary' LETTER number? '(' id id ')'
|
|
241
|
+
def _parse_prototype(self):
|
|
242
|
+
prec = 30
|
|
243
|
+
if self.cur_tok.kind == TokenKind.IDENTIFIER:
|
|
244
|
+
name = self.cur_tok.value
|
|
245
|
+
self._get_next_token()
|
|
246
|
+
elif self.cur_tok.kind == TokenKind.UNARY:
|
|
247
|
+
self._get_next_token()
|
|
248
|
+
if self.cur_tok.kind != TokenKind.OPERATOR:
|
|
249
|
+
raise ParseError('Expected operator after "unary"')
|
|
250
|
+
name = 'unary{0}'.format(self.cur_tok.value)
|
|
251
|
+
self._get_next_token()
|
|
252
|
+
elif self.cur_tok.kind == TokenKind.BINARY:
|
|
253
|
+
self._get_next_token()
|
|
254
|
+
if self.cur_tok.kind != TokenKind.OPERATOR:
|
|
255
|
+
raise ParseError('Expected operator after "binary"')
|
|
256
|
+
name = 'binary{0}'.format(self.cur_tok.value)
|
|
257
|
+
self._get_next_token()
|
|
258
|
+
|
|
259
|
+
# Try to parse precedence
|
|
260
|
+
if self.cur_tok.kind == TokenKind.NUMBER:
|
|
261
|
+
prec = int(self.cur_tok.value)
|
|
262
|
+
if not (0 < prec < 101):
|
|
263
|
+
raise ParseError('Invalid precedence', prec)
|
|
264
|
+
self._get_next_token()
|
|
265
|
+
|
|
266
|
+
# Add the new operator to our precedence table so we can properly
|
|
267
|
+
# parse it.
|
|
268
|
+
self._precedence_map[name[-1]] = prec
|
|
269
|
+
|
|
270
|
+
self._match(TokenKind.OPERATOR, '(')
|
|
271
|
+
argnames = []
|
|
272
|
+
while self.cur_tok.kind == TokenKind.IDENTIFIER:
|
|
273
|
+
argnames.append(self.cur_tok.value)
|
|
274
|
+
self._get_next_token()
|
|
275
|
+
self._match(TokenKind.OPERATOR, ')')
|
|
276
|
+
|
|
277
|
+
if name.startswith('binary') and len(argnames) != 2:
|
|
278
|
+
raise ParseError('Expected binary operator to have 2 operands')
|
|
279
|
+
elif name.startswith('unary') and len(argnames) != 1:
|
|
280
|
+
raise ParseError('Expected unary operator to have one operand')
|
|
281
|
+
|
|
282
|
+
return PrototypeAST(
|
|
283
|
+
name, argnames, name.startswith(('unary', 'binary')), prec)
|
|
284
|
+
|
|
285
|
+
# external ::= 'extern' prototype
|
|
286
|
+
def _parse_external(self):
|
|
287
|
+
self._get_next_token() # consume 'extern'
|
|
288
|
+
return self._parse_prototype()
|
|
289
|
+
|
|
290
|
+
# definition ::= 'def' prototype expression
|
|
291
|
+
def _parse_definition(self):
|
|
292
|
+
self._get_next_token() # consume 'def'
|
|
293
|
+
proto = self._parse_prototype()
|
|
294
|
+
expr = self._parse_expression()
|
|
295
|
+
return FunctionAST(proto, expr)
|
|
296
|
+
|
|
297
|
+
# toplevel ::= expression
|
|
298
|
+
def _parse_toplevel_expression(self):
|
|
299
|
+
expr = self._parse_expression()
|
|
300
|
+
return FunctionAST.create_anonymous(expr)
|
pcc/parse/plyparser.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#-----------------------------------------------------------------
|
|
2
|
+
# plyparser.py
|
|
3
|
+
#
|
|
4
|
+
# PLYParser class and other utilites for simplifying programming
|
|
5
|
+
# parsers with PLY
|
|
6
|
+
#
|
|
7
|
+
# Copyright (C) 2008-2015, Eli Bendersky
|
|
8
|
+
# License: BSD
|
|
9
|
+
#-----------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Coord(object):
|
|
13
|
+
""" Coordinates of a syntactic element. Consists of:
|
|
14
|
+
- File name
|
|
15
|
+
- Line number
|
|
16
|
+
- (optional) column number, for the Lexer
|
|
17
|
+
"""
|
|
18
|
+
__slots__ = ('file', 'line', 'column', '__weakref__')
|
|
19
|
+
def __init__(self, file, line, column=None):
|
|
20
|
+
self.file = file
|
|
21
|
+
self.line = line
|
|
22
|
+
self.column = column
|
|
23
|
+
|
|
24
|
+
def __str__(self):
|
|
25
|
+
str = "%s:%s" % (self.file, self.line)
|
|
26
|
+
if self.column: str += ":%s" % self.column
|
|
27
|
+
return str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ParseError(Exception): pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class PLYParser(object):
|
|
34
|
+
def _create_opt_rule(self, rulename):
|
|
35
|
+
""" Given a rule name, creates an optional ply.yacc rule
|
|
36
|
+
for it. The name of the optional rule is
|
|
37
|
+
<rulename>_opt
|
|
38
|
+
"""
|
|
39
|
+
optname = rulename + '_opt'
|
|
40
|
+
|
|
41
|
+
def optrule(self, p):
|
|
42
|
+
p[0] = p[1]
|
|
43
|
+
|
|
44
|
+
optrule.__doc__ = '%s : empty\n| %s' % (optname, rulename)
|
|
45
|
+
optrule.__name__ = 'p_%s' % optname
|
|
46
|
+
setattr(self.__class__, optrule.__name__, optrule)
|
|
47
|
+
|
|
48
|
+
def _coord(self, lineno, column=None):
|
|
49
|
+
return Coord(
|
|
50
|
+
file=self.clex.filename,
|
|
51
|
+
line=lineno,
|
|
52
|
+
column=column)
|
|
53
|
+
|
|
54
|
+
def _parse_error(self, msg, coord):
|
|
55
|
+
raise ParseError("%s: %s" % (coord, msg))
|
|
56
|
+
|
pcc/pcc.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
from .evaluater.c_evaluator import CEvaluator
|
|
3
|
+
from .project import collect_project
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command(context_settings={"ignore_unknown_options": True})
|
|
10
|
+
@click.option("--llvmdump", is_flag=True, default=False, help="Dump LLVM IR to temp files")
|
|
11
|
+
@click.argument('path')
|
|
12
|
+
@click.argument('prog_args', nargs=-1, type=click.UNPROCESSED)
|
|
13
|
+
def main(path, llvmdump, prog_args):
|
|
14
|
+
"""Pcc - a C compiler built on Python and LLVM.
|
|
15
|
+
|
|
16
|
+
PATH can be a .c file or a directory containing .c files.
|
|
17
|
+
Any arguments after PATH (or after --) are passed to the compiled program.
|
|
18
|
+
|
|
19
|
+
\b
|
|
20
|
+
Examples:
|
|
21
|
+
pcc hello.c # compile and run a single file
|
|
22
|
+
pcc myproject/ # compile all .c files in directory
|
|
23
|
+
pcc --llvmdump test.c # also dump LLVM IR
|
|
24
|
+
pcc myproject/ -- script.lua # pass args to compiled program
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
source, base_dir = collect_project(path)
|
|
28
|
+
except (FileNotFoundError, ValueError) as e:
|
|
29
|
+
click.echo(f"Error: {e}", err=True)
|
|
30
|
+
sys.exit(1)
|
|
31
|
+
|
|
32
|
+
pcc = CEvaluator()
|
|
33
|
+
ret = pcc.evaluate(source, llvmdump=llvmdump, base_dir=base_dir, prog_args=list(prog_args))
|
|
34
|
+
sys.exit(ret if isinstance(ret, int) else 0)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
main()
|