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.
@@ -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()
pcc/ply/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ # PLY package
2
+ # Author: David Beazley (dave@dabeaz.com)
3
+
4
+ __version__ = '3.7'
5
+ __all__ = ['lex','yacc']