blob: ee77a13899bfe3116325303356c7518eb8b6c5e4 [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
Benjamin Petersond481e3d2009-05-09 19:42:23 +000019import codecs
Martin v. Löwisef04c442008-03-19 05:04:44 +000020import os
21import logging
22import sys
23
24# Pgen imports
Martin v. Löwis3faa84f2008-03-22 00:07:09 +000025from . import grammar, parse, token, tokenize, pgen
Martin v. Löwisef04c442008-03-19 05:04:44 +000026
27
28class Driver(object):
29
30 def __init__(self, grammar, convert=None, logger=None):
31 self.grammar = grammar
32 if logger is None:
33 logger = logging.getLogger()
34 self.logger = logger
35 self.convert = convert
36
37 def parse_tokens(self, tokens, debug=False):
38 """Parse a series of tokens and return the syntax tree."""
39 # XXX Move the prefix computation into a wrapper around tokenize.
40 p = parse.Parser(self.grammar, self.convert)
41 p.setup()
42 lineno = 1
43 column = 0
44 type = value = start = end = line_text = None
45 prefix = ""
46 for quintuple in tokens:
47 type, value, start, end, line_text = quintuple
48 if start != (lineno, column):
49 assert (lineno, column) <= start, ((lineno, column), start)
50 s_lineno, s_column = start
51 if lineno < s_lineno:
52 prefix += "\n" * (s_lineno - lineno)
53 lineno = s_lineno
54 column = 0
55 if column < s_column:
56 prefix += line_text[column:s_column]
57 column = s_column
58 if type in (tokenize.COMMENT, tokenize.NL):
59 prefix += value
60 lineno, column = end
61 if value.endswith("\n"):
62 lineno += 1
63 column = 0
64 continue
65 if type == token.OP:
66 type = grammar.opmap[value]
67 if debug:
68 self.logger.debug("%s %r (prefix=%r)",
69 token.tok_name[type], value, prefix)
70 if p.addtoken(type, value, (prefix, start)):
71 if debug:
72 self.logger.debug("Stop.")
73 break
74 prefix = ""
75 lineno, column = end
76 if value.endswith("\n"):
77 lineno += 1
78 column = 0
79 else:
80 # We never broke out -- EOF is too soon (how can this happen???)
Benjamin Peterson28d88b42009-01-09 03:03:23 +000081 raise parse.ParseError("incomplete input",
82 type, value, (prefix, start))
Martin v. Löwisef04c442008-03-19 05:04:44 +000083 return p.rootnode
84
85 def parse_stream_raw(self, stream, debug=False):
86 """Parse a stream and return the syntax tree."""
87 tokens = tokenize.generate_tokens(stream.readline)
88 return self.parse_tokens(tokens, debug)
89
90 def parse_stream(self, stream, debug=False):
91 """Parse a stream and return the syntax tree."""
92 return self.parse_stream_raw(stream, debug)
93
Benjamin Petersond481e3d2009-05-09 19:42:23 +000094 def parse_file(self, filename, encoding=None, debug=False):
Martin v. Löwisef04c442008-03-19 05:04:44 +000095 """Parse a file and return the syntax tree."""
Benjamin Petersond481e3d2009-05-09 19:42:23 +000096 stream = codecs.open(filename, "r", encoding)
Martin v. Löwisef04c442008-03-19 05:04:44 +000097 try:
98 return self.parse_stream(stream, debug)
99 finally:
100 stream.close()
101
102 def parse_string(self, text, debug=False):
103 """Parse a string and return the syntax tree."""
Martin v. Löwis8a5f8ca2008-03-19 05:33:36 +0000104 tokens = tokenize.generate_tokens(generate_lines(text).__next__)
Martin v. Löwisef04c442008-03-19 05:04:44 +0000105 return self.parse_tokens(tokens, debug)
106
107
108def generate_lines(text):
109 """Generator that behaves like readline without using StringIO."""
110 for line in text.splitlines(True):
111 yield line
112 while True:
113 yield ""
114
115
116def load_grammar(gt="Grammar.txt", gp=None,
117 save=True, force=False, logger=None):
118 """Load the grammar (maybe from a pickle)."""
119 if logger is None:
120 logger = logging.getLogger()
121 if gp is None:
122 head, tail = os.path.splitext(gt)
123 if tail == ".txt":
124 tail = ""
125 gp = head + tail + ".".join(map(str, sys.version_info)) + ".pickle"
126 if force or not _newer(gp, gt):
127 logger.info("Generating grammar tables from %s", gt)
Martin v. Löwisef04c442008-03-19 05:04:44 +0000128 g = pgen.generate_grammar(gt)
129 if save:
130 logger.info("Writing grammar tables to %s", gp)
Martin v. Löwis346c9212008-05-25 17:22:03 +0000131 try:
132 g.dump(gp)
133 except IOError as e:
134 logger.info("Writing failed:"+str(e))
Martin v. Löwisef04c442008-03-19 05:04:44 +0000135 else:
136 g = grammar.Grammar()
137 g.load(gp)
138 return g
139
140
141def _newer(a, b):
142 """Inquire whether file a was written since file b."""
143 if not os.path.exists(a):
144 return False
145 if not os.path.exists(b):
146 return True
147 return os.path.getmtime(a) >= os.path.getmtime(b)