blob: a025b37292945c347fbc3e79d0b5b0b8846d3cd2 [file] [log] [blame]
Martin v. Löwisef04c442008-03-19 05:04:44 +00001# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
2# Licensed to PSF under a Contributor Agreement.
3
4# Modifications:
5# Copyright 2006 Google, Inc. All Rights Reserved.
6# Licensed to PSF under a Contributor Agreement.
7
8"""Parser driver.
9
10This provides a high-level interface to parse a file into a syntax tree.
11
12"""
13
14__author__ = "Guido van Rossum <guido@python.org>"
15
16__all__ = ["Driver", "load_grammar"]
17
18# Python imports
19import os
20import logging
21import sys
22
23# Pgen imports
Martin v. Löwis3faa84f2008-03-22 00:07:09 +000024from . import grammar, parse, token, tokenize, pgen
Martin v. Löwisef04c442008-03-19 05:04:44 +000025
26
27class Driver(object):
28
29 def __init__(self, grammar, convert=None, logger=None):
30 self.grammar = grammar
31 if logger is None:
32 logger = logging.getLogger()
33 self.logger = logger
34 self.convert = convert
35
36 def parse_tokens(self, tokens, debug=False):
37 """Parse a series of tokens and return the syntax tree."""
38 # XXX Move the prefix computation into a wrapper around tokenize.
39 p = parse.Parser(self.grammar, self.convert)
40 p.setup()
41 lineno = 1
42 column = 0
43 type = value = start = end = line_text = None
44 prefix = ""
45 for quintuple in tokens:
46 type, value, start, end, line_text = quintuple
47 if start != (lineno, column):
48 assert (lineno, column) <= start, ((lineno, column), start)
49 s_lineno, s_column = start
50 if lineno < s_lineno:
51 prefix += "\n" * (s_lineno - lineno)
52 lineno = s_lineno
53 column = 0
54 if column < s_column:
55 prefix += line_text[column:s_column]
56 column = s_column
57 if type in (tokenize.COMMENT, tokenize.NL):
58 prefix += value
59 lineno, column = end
60 if value.endswith("\n"):
61 lineno += 1
62 column = 0
63 continue
64 if type == token.OP:
65 type = grammar.opmap[value]
66 if debug:
67 self.logger.debug("%s %r (prefix=%r)",
68 token.tok_name[type], value, prefix)
69 if p.addtoken(type, value, (prefix, start)):
70 if debug:
71 self.logger.debug("Stop.")
72 break
73 prefix = ""
74 lineno, column = end
75 if value.endswith("\n"):
76 lineno += 1
77 column = 0
78 else:
79 # We never broke out -- EOF is too soon (how can this happen???)
Benjamin Peterson28d88b42009-01-09 03:03:23 +000080 raise parse.ParseError("incomplete input",
81 type, value, (prefix, start))
Martin v. Löwisef04c442008-03-19 05:04:44 +000082 return p.rootnode
83
84 def parse_stream_raw(self, stream, debug=False):
85 """Parse a stream and return the syntax tree."""
86 tokens = tokenize.generate_tokens(stream.readline)
87 return self.parse_tokens(tokens, debug)
88
89 def parse_stream(self, stream, debug=False):
90 """Parse a stream and return the syntax tree."""
91 return self.parse_stream_raw(stream, debug)
92
93 def parse_file(self, filename, debug=False):
94 """Parse a file and return the syntax tree."""
95 stream = open(filename)
96 try:
97 return self.parse_stream(stream, debug)
98 finally:
99 stream.close()
100
101 def parse_string(self, text, debug=False):
102 """Parse a string and return the syntax tree."""
Martin v. Löwis8a5f8ca2008-03-19 05:33:36 +0000103 tokens = tokenize.generate_tokens(generate_lines(text).__next__)
Martin v. Löwisef04c442008-03-19 05:04:44 +0000104 return self.parse_tokens(tokens, debug)
105
106
107def generate_lines(text):
108 """Generator that behaves like readline without using StringIO."""
109 for line in text.splitlines(True):
110 yield line
111 while True:
112 yield ""
113
114
115def load_grammar(gt="Grammar.txt", gp=None,
116 save=True, force=False, logger=None):
117 """Load the grammar (maybe from a pickle)."""
118 if logger is None:
119 logger = logging.getLogger()
120 if gp is None:
121 head, tail = os.path.splitext(gt)
122 if tail == ".txt":
123 tail = ""
124 gp = head + tail + ".".join(map(str, sys.version_info)) + ".pickle"
125 if force or not _newer(gp, gt):
126 logger.info("Generating grammar tables from %s", gt)
Martin v. Löwisef04c442008-03-19 05:04:44 +0000127 g = pgen.generate_grammar(gt)
128 if save:
129 logger.info("Writing grammar tables to %s", gp)
Martin v. Löwis346c9212008-05-25 17:22:03 +0000130 try:
131 g.dump(gp)
132 except IOError as e:
133 logger.info("Writing failed:"+str(e))
Martin v. Löwisef04c442008-03-19 05:04:44 +0000134 else:
135 g = grammar.Grammar()
136 g.load(gp)
137 return g
138
139
140def _newer(a, b):
141 """Inquire whether file a was written since file b."""
142 if not os.path.exists(a):
143 return False
144 if not os.path.exists(b):
145 return True
146 return os.path.getmtime(a) >= os.path.getmtime(b)