~phablet-team/sync-monitor/trunk

« back to all changes in this revision

Viewing changes to 3rd_party/gmock/gtest/scripts/pump.py

  • Committer: Renato Araujo Oliveira Filho
  • Date: 2014-04-07 19:53:07 UTC
  • mto: (17.3.2 initial-release)
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: renato.filho@canonical.com-20140407195307-1jdcln7nz4ulxf0r
Added gmock source code. (necessary in tests)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
#
 
3
# Copyright 2008, Google Inc.
 
4
# All rights reserved.
 
5
#
 
6
# Redistribution and use in source and binary forms, with or without
 
7
# modification, are permitted provided that the following conditions are
 
8
# met:
 
9
#
 
10
#     * Redistributions of source code must retain the above copyright
 
11
# notice, this list of conditions and the following disclaimer.
 
12
#     * Redistributions in binary form must reproduce the above
 
13
# copyright notice, this list of conditions and the following disclaimer
 
14
# in the documentation and/or other materials provided with the
 
15
# distribution.
 
16
#     * Neither the name of Google Inc. nor the names of its
 
17
# contributors may be used to endorse or promote products derived from
 
18
# this software without specific prior written permission.
 
19
#
 
20
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
21
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
22
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
23
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
24
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
25
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
26
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
27
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
28
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
29
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
30
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
31
 
 
32
"""pump v0.2.0 - Pretty Useful for Meta Programming.
 
33
 
 
34
A tool for preprocessor meta programming.  Useful for generating
 
35
repetitive boilerplate code.  Especially useful for writing C++
 
36
classes, functions, macros, and templates that need to work with
 
37
various number of arguments.
 
38
 
 
39
USAGE:
 
40
       pump.py SOURCE_FILE
 
41
 
 
42
EXAMPLES:
 
43
       pump.py foo.cc.pump
 
44
         Converts foo.cc.pump to foo.cc.
 
45
 
 
46
GRAMMAR:
 
47
       CODE ::= ATOMIC_CODE*
 
48
       ATOMIC_CODE ::= $var ID = EXPRESSION
 
49
           | $var ID = [[ CODE ]]
 
50
           | $range ID EXPRESSION..EXPRESSION
 
51
           | $for ID SEPARATOR [[ CODE ]]
 
52
           | $($)
 
53
           | $ID
 
54
           | $(EXPRESSION)
 
55
           | $if EXPRESSION [[ CODE ]] ELSE_BRANCH
 
56
           | [[ CODE ]]
 
57
           | RAW_CODE
 
58
       SEPARATOR ::= RAW_CODE | EMPTY
 
59
       ELSE_BRANCH ::= $else [[ CODE ]]
 
60
           | $elif EXPRESSION [[ CODE ]] ELSE_BRANCH
 
61
           | EMPTY
 
62
       EXPRESSION has Python syntax.
 
63
"""
 
64
 
 
65
__author__ = 'wan@google.com (Zhanyong Wan)'
 
66
 
 
67
import os
 
68
import re
 
69
import sys
 
70
 
 
71
 
 
72
TOKEN_TABLE = [
 
73
    (re.compile(r'\$var\s+'), '$var'),
 
74
    (re.compile(r'\$elif\s+'), '$elif'),
 
75
    (re.compile(r'\$else\s+'), '$else'),
 
76
    (re.compile(r'\$for\s+'), '$for'),
 
77
    (re.compile(r'\$if\s+'), '$if'),
 
78
    (re.compile(r'\$range\s+'), '$range'),
 
79
    (re.compile(r'\$[_A-Za-z]\w*'), '$id'),
 
80
    (re.compile(r'\$\(\$\)'), '$($)'),
 
81
    (re.compile(r'\$'), '$'),
 
82
    (re.compile(r'\[\[\n?'), '[['),
 
83
    (re.compile(r'\]\]\n?'), ']]'),
 
84
    ]
 
85
 
 
86
 
 
87
class Cursor:
 
88
  """Represents a position (line and column) in a text file."""
 
89
 
 
90
  def __init__(self, line=-1, column=-1):
 
91
    self.line = line
 
92
    self.column = column
 
93
 
 
94
  def __eq__(self, rhs):
 
95
    return self.line == rhs.line and self.column == rhs.column
 
96
 
 
97
  def __ne__(self, rhs):
 
98
    return not self == rhs
 
99
 
 
100
  def __lt__(self, rhs):
 
101
    return self.line < rhs.line or (
 
102
        self.line == rhs.line and self.column < rhs.column)
 
103
 
 
104
  def __le__(self, rhs):
 
105
    return self < rhs or self == rhs
 
106
 
 
107
  def __gt__(self, rhs):
 
108
    return rhs < self
 
109
 
 
110
  def __ge__(self, rhs):
 
111
    return rhs <= self
 
112
 
 
113
  def __str__(self):
 
114
    if self == Eof():
 
115
      return 'EOF'
 
116
    else:
 
117
      return '%s(%s)' % (self.line + 1, self.column)
 
118
 
 
119
  def __add__(self, offset):
 
120
    return Cursor(self.line, self.column + offset)
 
121
 
 
122
  def __sub__(self, offset):
 
123
    return Cursor(self.line, self.column - offset)
 
124
 
 
125
  def Clone(self):
 
126
    """Returns a copy of self."""
 
127
 
 
128
    return Cursor(self.line, self.column)
 
129
 
 
130
 
 
131
# Special cursor to indicate the end-of-file.
 
132
def Eof():
 
133
  """Returns the special cursor to denote the end-of-file."""
 
134
  return Cursor(-1, -1)
 
135
 
 
136
 
 
137
class Token:
 
138
  """Represents a token in a Pump source file."""
 
139
 
 
140
  def __init__(self, start=None, end=None, value=None, token_type=None):
 
141
    if start is None:
 
142
      self.start = Eof()
 
143
    else:
 
144
      self.start = start
 
145
    if end is None:
 
146
      self.end = Eof()
 
147
    else:
 
148
      self.end = end
 
149
    self.value = value
 
150
    self.token_type = token_type
 
151
 
 
152
  def __str__(self):
 
153
    return 'Token @%s: \'%s\' type=%s' % (
 
154
        self.start, self.value, self.token_type)
 
155
 
 
156
  def Clone(self):
 
157
    """Returns a copy of self."""
 
158
 
 
159
    return Token(self.start.Clone(), self.end.Clone(), self.value,
 
160
                 self.token_type)
 
161
 
 
162
 
 
163
def StartsWith(lines, pos, string):
 
164
  """Returns True iff the given position in lines starts with 'string'."""
 
165
 
 
166
  return lines[pos.line][pos.column:].startswith(string)
 
167
 
 
168
 
 
169
def FindFirstInLine(line, token_table):
 
170
  best_match_start = -1
 
171
  for (regex, token_type) in token_table:
 
172
    m = regex.search(line)
 
173
    if m:
 
174
      # We found regex in lines
 
175
      if best_match_start < 0 or m.start() < best_match_start:
 
176
        best_match_start = m.start()
 
177
        best_match_length = m.end() - m.start()
 
178
        best_match_token_type = token_type
 
179
 
 
180
  if best_match_start < 0:
 
181
    return None
 
182
 
 
183
  return (best_match_start, best_match_length, best_match_token_type)
 
184
 
 
185
 
 
186
def FindFirst(lines, token_table, cursor):
 
187
  """Finds the first occurrence of any string in strings in lines."""
 
188
 
 
189
  start = cursor.Clone()
 
190
  cur_line_number = cursor.line
 
191
  for line in lines[start.line:]:
 
192
    if cur_line_number == start.line:
 
193
      line = line[start.column:]
 
194
    m = FindFirstInLine(line, token_table)
 
195
    if m:
 
196
      # We found a regex in line.
 
197
      (start_column, length, token_type) = m
 
198
      if cur_line_number == start.line:
 
199
        start_column += start.column
 
200
      found_start = Cursor(cur_line_number, start_column)
 
201
      found_end = found_start + length
 
202
      return MakeToken(lines, found_start, found_end, token_type)
 
203
    cur_line_number += 1
 
204
  # We failed to find str in lines
 
205
  return None
 
206
 
 
207
 
 
208
def SubString(lines, start, end):
 
209
  """Returns a substring in lines."""
 
210
 
 
211
  if end == Eof():
 
212
    end = Cursor(len(lines) - 1, len(lines[-1]))
 
213
 
 
214
  if start >= end:
 
215
    return ''
 
216
 
 
217
  if start.line == end.line:
 
218
    return lines[start.line][start.column:end.column]
 
219
 
 
220
  result_lines = ([lines[start.line][start.column:]] +
 
221
                  lines[start.line + 1:end.line] +
 
222
                  [lines[end.line][:end.column]])
 
223
  return ''.join(result_lines)
 
224
 
 
225
 
 
226
def StripMetaComments(str):
 
227
  """Strip meta comments from each line in the given string."""
 
228
 
 
229
  # First, completely remove lines containing nothing but a meta
 
230
  # comment, including the trailing \n.
 
231
  str = re.sub(r'^\s*\$\$.*\n', '', str)
 
232
 
 
233
  # Then, remove meta comments from contentful lines.
 
234
  return re.sub(r'\s*\$\$.*', '', str)
 
235
 
 
236
 
 
237
def MakeToken(lines, start, end, token_type):
 
238
  """Creates a new instance of Token."""
 
239
 
 
240
  return Token(start, end, SubString(lines, start, end), token_type)
 
241
 
 
242
 
 
243
def ParseToken(lines, pos, regex, token_type):
 
244
  line = lines[pos.line][pos.column:]
 
245
  m = regex.search(line)
 
246
  if m and not m.start():
 
247
    return MakeToken(lines, pos, pos + m.end(), token_type)
 
248
  else:
 
249
    print 'ERROR: %s expected at %s.' % (token_type, pos)
 
250
    sys.exit(1)
 
251
 
 
252
 
 
253
ID_REGEX = re.compile(r'[_A-Za-z]\w*')
 
254
EQ_REGEX = re.compile(r'=')
 
255
REST_OF_LINE_REGEX = re.compile(r'.*?(?=$|\$\$)')
 
256
OPTIONAL_WHITE_SPACES_REGEX = re.compile(r'\s*')
 
257
WHITE_SPACE_REGEX = re.compile(r'\s')
 
258
DOT_DOT_REGEX = re.compile(r'\.\.')
 
259
 
 
260
 
 
261
def Skip(lines, pos, regex):
 
262
  line = lines[pos.line][pos.column:]
 
263
  m = re.search(regex, line)
 
264
  if m and not m.start():
 
265
    return pos + m.end()
 
266
  else:
 
267
    return pos
 
268
 
 
269
 
 
270
def SkipUntil(lines, pos, regex, token_type):
 
271
  line = lines[pos.line][pos.column:]
 
272
  m = re.search(regex, line)
 
273
  if m:
 
274
    return pos + m.start()
 
275
  else:
 
276
    print ('ERROR: %s expected on line %s after column %s.' %
 
277
           (token_type, pos.line + 1, pos.column))
 
278
    sys.exit(1)
 
279
 
 
280
 
 
281
def ParseExpTokenInParens(lines, pos):
 
282
  def ParseInParens(pos):
 
283
    pos = Skip(lines, pos, OPTIONAL_WHITE_SPACES_REGEX)
 
284
    pos = Skip(lines, pos, r'\(')
 
285
    pos = Parse(pos)
 
286
    pos = Skip(lines, pos, r'\)')
 
287
    return pos
 
288
 
 
289
  def Parse(pos):
 
290
    pos = SkipUntil(lines, pos, r'\(|\)', ')')
 
291
    if SubString(lines, pos, pos + 1) == '(':
 
292
      pos = Parse(pos + 1)
 
293
      pos = Skip(lines, pos, r'\)')
 
294
      return Parse(pos)
 
295
    else:
 
296
      return pos
 
297
 
 
298
  start = pos.Clone()
 
299
  pos = ParseInParens(pos)
 
300
  return MakeToken(lines, start, pos, 'exp')
 
301
 
 
302
 
 
303
def RStripNewLineFromToken(token):
 
304
  if token.value.endswith('\n'):
 
305
    return Token(token.start, token.end, token.value[:-1], token.token_type)
 
306
  else:
 
307
    return token
 
308
 
 
309
 
 
310
def TokenizeLines(lines, pos):
 
311
  while True:
 
312
    found = FindFirst(lines, TOKEN_TABLE, pos)
 
313
    if not found:
 
314
      yield MakeToken(lines, pos, Eof(), 'code')
 
315
      return
 
316
 
 
317
    if found.start == pos:
 
318
      prev_token = None
 
319
      prev_token_rstripped = None
 
320
    else:
 
321
      prev_token = MakeToken(lines, pos, found.start, 'code')
 
322
      prev_token_rstripped = RStripNewLineFromToken(prev_token)
 
323
 
 
324
    if found.token_type == '$var':
 
325
      if prev_token_rstripped:
 
326
        yield prev_token_rstripped
 
327
      yield found
 
328
      id_token = ParseToken(lines, found.end, ID_REGEX, 'id')
 
329
      yield id_token
 
330
      pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX)
 
331
 
 
332
      eq_token = ParseToken(lines, pos, EQ_REGEX, '=')
 
333
      yield eq_token
 
334
      pos = Skip(lines, eq_token.end, r'\s*')
 
335
 
 
336
      if SubString(lines, pos, pos + 2) != '[[':
 
337
        exp_token = ParseToken(lines, pos, REST_OF_LINE_REGEX, 'exp')
 
338
        yield exp_token
 
339
        pos = Cursor(exp_token.end.line + 1, 0)
 
340
    elif found.token_type == '$for':
 
341
      if prev_token_rstripped:
 
342
        yield prev_token_rstripped
 
343
      yield found
 
344
      id_token = ParseToken(lines, found.end, ID_REGEX, 'id')
 
345
      yield id_token
 
346
      pos = Skip(lines, id_token.end, WHITE_SPACE_REGEX)
 
347
    elif found.token_type == '$range':
 
348
      if prev_token_rstripped:
 
349
        yield prev_token_rstripped
 
350
      yield found
 
351
      id_token = ParseToken(lines, found.end, ID_REGEX, 'id')
 
352
      yield id_token
 
353
      pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX)
 
354
 
 
355
      dots_pos = SkipUntil(lines, pos, DOT_DOT_REGEX, '..')
 
356
      yield MakeToken(lines, pos, dots_pos, 'exp')
 
357
      yield MakeToken(lines, dots_pos, dots_pos + 2, '..')
 
358
      pos = dots_pos + 2
 
359
      new_pos = Cursor(pos.line + 1, 0)
 
360
      yield MakeToken(lines, pos, new_pos, 'exp')
 
361
      pos = new_pos
 
362
    elif found.token_type == '$':
 
363
      if prev_token:
 
364
        yield prev_token
 
365
      yield found
 
366
      exp_token = ParseExpTokenInParens(lines, found.end)
 
367
      yield exp_token
 
368
      pos = exp_token.end
 
369
    elif (found.token_type == ']]' or found.token_type == '$if' or
 
370
          found.token_type == '$elif' or found.token_type == '$else'):
 
371
      if prev_token_rstripped:
 
372
        yield prev_token_rstripped
 
373
      yield found
 
374
      pos = found.end
 
375
    else:
 
376
      if prev_token:
 
377
        yield prev_token
 
378
      yield found
 
379
      pos = found.end
 
380
 
 
381
 
 
382
def Tokenize(s):
 
383
  """A generator that yields the tokens in the given string."""
 
384
  if s != '':
 
385
    lines = s.splitlines(True)
 
386
    for token in TokenizeLines(lines, Cursor(0, 0)):
 
387
      yield token
 
388
 
 
389
 
 
390
class CodeNode:
 
391
  def __init__(self, atomic_code_list=None):
 
392
    self.atomic_code = atomic_code_list
 
393
 
 
394
 
 
395
class VarNode:
 
396
  def __init__(self, identifier=None, atomic_code=None):
 
397
    self.identifier = identifier
 
398
    self.atomic_code = atomic_code
 
399
 
 
400
 
 
401
class RangeNode:
 
402
  def __init__(self, identifier=None, exp1=None, exp2=None):
 
403
    self.identifier = identifier
 
404
    self.exp1 = exp1
 
405
    self.exp2 = exp2
 
406
 
 
407
 
 
408
class ForNode:
 
409
  def __init__(self, identifier=None, sep=None, code=None):
 
410
    self.identifier = identifier
 
411
    self.sep = sep
 
412
    self.code = code
 
413
 
 
414
 
 
415
class ElseNode:
 
416
  def __init__(self, else_branch=None):
 
417
    self.else_branch = else_branch
 
418
 
 
419
 
 
420
class IfNode:
 
421
  def __init__(self, exp=None, then_branch=None, else_branch=None):
 
422
    self.exp = exp
 
423
    self.then_branch = then_branch
 
424
    self.else_branch = else_branch
 
425
 
 
426
 
 
427
class RawCodeNode:
 
428
  def __init__(self, token=None):
 
429
    self.raw_code = token
 
430
 
 
431
 
 
432
class LiteralDollarNode:
 
433
  def __init__(self, token):
 
434
    self.token = token
 
435
 
 
436
 
 
437
class ExpNode:
 
438
  def __init__(self, token, python_exp):
 
439
    self.token = token
 
440
    self.python_exp = python_exp
 
441
 
 
442
 
 
443
def PopFront(a_list):
 
444
  head = a_list[0]
 
445
  a_list[:1] = []
 
446
  return head
 
447
 
 
448
 
 
449
def PushFront(a_list, elem):
 
450
  a_list[:0] = [elem]
 
451
 
 
452
 
 
453
def PopToken(a_list, token_type=None):
 
454
  token = PopFront(a_list)
 
455
  if token_type is not None and token.token_type != token_type:
 
456
    print 'ERROR: %s expected at %s' % (token_type, token.start)
 
457
    print 'ERROR: %s found instead' % (token,)
 
458
    sys.exit(1)
 
459
 
 
460
  return token
 
461
 
 
462
 
 
463
def PeekToken(a_list):
 
464
  if not a_list:
 
465
    return None
 
466
 
 
467
  return a_list[0]
 
468
 
 
469
 
 
470
def ParseExpNode(token):
 
471
  python_exp = re.sub(r'([_A-Za-z]\w*)', r'self.GetValue("\1")', token.value)
 
472
  return ExpNode(token, python_exp)
 
473
 
 
474
 
 
475
def ParseElseNode(tokens):
 
476
  def Pop(token_type=None):
 
477
    return PopToken(tokens, token_type)
 
478
 
 
479
  next = PeekToken(tokens)
 
480
  if not next:
 
481
    return None
 
482
  if next.token_type == '$else':
 
483
    Pop('$else')
 
484
    Pop('[[')
 
485
    code_node = ParseCodeNode(tokens)
 
486
    Pop(']]')
 
487
    return code_node
 
488
  elif next.token_type == '$elif':
 
489
    Pop('$elif')
 
490
    exp = Pop('code')
 
491
    Pop('[[')
 
492
    code_node = ParseCodeNode(tokens)
 
493
    Pop(']]')
 
494
    inner_else_node = ParseElseNode(tokens)
 
495
    return CodeNode([IfNode(ParseExpNode(exp), code_node, inner_else_node)])
 
496
  elif not next.value.strip():
 
497
    Pop('code')
 
498
    return ParseElseNode(tokens)
 
499
  else:
 
500
    return None
 
501
 
 
502
 
 
503
def ParseAtomicCodeNode(tokens):
 
504
  def Pop(token_type=None):
 
505
    return PopToken(tokens, token_type)
 
506
 
 
507
  head = PopFront(tokens)
 
508
  t = head.token_type
 
509
  if t == 'code':
 
510
    return RawCodeNode(head)
 
511
  elif t == '$var':
 
512
    id_token = Pop('id')
 
513
    Pop('=')
 
514
    next = PeekToken(tokens)
 
515
    if next.token_type == 'exp':
 
516
      exp_token = Pop()
 
517
      return VarNode(id_token, ParseExpNode(exp_token))
 
518
    Pop('[[')
 
519
    code_node = ParseCodeNode(tokens)
 
520
    Pop(']]')
 
521
    return VarNode(id_token, code_node)
 
522
  elif t == '$for':
 
523
    id_token = Pop('id')
 
524
    next_token = PeekToken(tokens)
 
525
    if next_token.token_type == 'code':
 
526
      sep_token = next_token
 
527
      Pop('code')
 
528
    else:
 
529
      sep_token = None
 
530
    Pop('[[')
 
531
    code_node = ParseCodeNode(tokens)
 
532
    Pop(']]')
 
533
    return ForNode(id_token, sep_token, code_node)
 
534
  elif t == '$if':
 
535
    exp_token = Pop('code')
 
536
    Pop('[[')
 
537
    code_node = ParseCodeNode(tokens)
 
538
    Pop(']]')
 
539
    else_node = ParseElseNode(tokens)
 
540
    return IfNode(ParseExpNode(exp_token), code_node, else_node)
 
541
  elif t == '$range':
 
542
    id_token = Pop('id')
 
543
    exp1_token = Pop('exp')
 
544
    Pop('..')
 
545
    exp2_token = Pop('exp')
 
546
    return RangeNode(id_token, ParseExpNode(exp1_token),
 
547
                     ParseExpNode(exp2_token))
 
548
  elif t == '$id':
 
549
    return ParseExpNode(Token(head.start + 1, head.end, head.value[1:], 'id'))
 
550
  elif t == '$($)':
 
551
    return LiteralDollarNode(head)
 
552
  elif t == '$':
 
553
    exp_token = Pop('exp')
 
554
    return ParseExpNode(exp_token)
 
555
  elif t == '[[':
 
556
    code_node = ParseCodeNode(tokens)
 
557
    Pop(']]')
 
558
    return code_node
 
559
  else:
 
560
    PushFront(tokens, head)
 
561
    return None
 
562
 
 
563
 
 
564
def ParseCodeNode(tokens):
 
565
  atomic_code_list = []
 
566
  while True:
 
567
    if not tokens:
 
568
      break
 
569
    atomic_code_node = ParseAtomicCodeNode(tokens)
 
570
    if atomic_code_node:
 
571
      atomic_code_list.append(atomic_code_node)
 
572
    else:
 
573
      break
 
574
  return CodeNode(atomic_code_list)
 
575
 
 
576
 
 
577
def ParseToAST(pump_src_text):
 
578
  """Convert the given Pump source text into an AST."""
 
579
  tokens = list(Tokenize(pump_src_text))
 
580
  code_node = ParseCodeNode(tokens)
 
581
  return code_node
 
582
 
 
583
 
 
584
class Env:
 
585
  def __init__(self):
 
586
    self.variables = []
 
587
    self.ranges = []
 
588
 
 
589
  def Clone(self):
 
590
    clone = Env()
 
591
    clone.variables = self.variables[:]
 
592
    clone.ranges = self.ranges[:]
 
593
    return clone
 
594
 
 
595
  def PushVariable(self, var, value):
 
596
    # If value looks like an int, store it as an int.
 
597
    try:
 
598
      int_value = int(value)
 
599
      if ('%s' % int_value) == value:
 
600
        value = int_value
 
601
    except Exception:
 
602
      pass
 
603
    self.variables[:0] = [(var, value)]
 
604
 
 
605
  def PopVariable(self):
 
606
    self.variables[:1] = []
 
607
 
 
608
  def PushRange(self, var, lower, upper):
 
609
    self.ranges[:0] = [(var, lower, upper)]
 
610
 
 
611
  def PopRange(self):
 
612
    self.ranges[:1] = []
 
613
 
 
614
  def GetValue(self, identifier):
 
615
    for (var, value) in self.variables:
 
616
      if identifier == var:
 
617
        return value
 
618
 
 
619
    print 'ERROR: meta variable %s is undefined.' % (identifier,)
 
620
    sys.exit(1)
 
621
 
 
622
  def EvalExp(self, exp):
 
623
    try:
 
624
      result = eval(exp.python_exp)
 
625
    except Exception, e:
 
626
      print 'ERROR: caught exception %s: %s' % (e.__class__.__name__, e)
 
627
      print ('ERROR: failed to evaluate meta expression %s at %s' %
 
628
             (exp.python_exp, exp.token.start))
 
629
      sys.exit(1)
 
630
    return result
 
631
 
 
632
  def GetRange(self, identifier):
 
633
    for (var, lower, upper) in self.ranges:
 
634
      if identifier == var:
 
635
        return (lower, upper)
 
636
 
 
637
    print 'ERROR: range %s is undefined.' % (identifier,)
 
638
    sys.exit(1)
 
639
 
 
640
 
 
641
class Output:
 
642
  def __init__(self):
 
643
    self.string = ''
 
644
 
 
645
  def GetLastLine(self):
 
646
    index = self.string.rfind('\n')
 
647
    if index < 0:
 
648
      return ''
 
649
 
 
650
    return self.string[index + 1:]
 
651
 
 
652
  def Append(self, s):
 
653
    self.string += s
 
654
 
 
655
 
 
656
def RunAtomicCode(env, node, output):
 
657
  if isinstance(node, VarNode):
 
658
    identifier = node.identifier.value.strip()
 
659
    result = Output()
 
660
    RunAtomicCode(env.Clone(), node.atomic_code, result)
 
661
    value = result.string
 
662
    env.PushVariable(identifier, value)
 
663
  elif isinstance(node, RangeNode):
 
664
    identifier = node.identifier.value.strip()
 
665
    lower = int(env.EvalExp(node.exp1))
 
666
    upper = int(env.EvalExp(node.exp2))
 
667
    env.PushRange(identifier, lower, upper)
 
668
  elif isinstance(node, ForNode):
 
669
    identifier = node.identifier.value.strip()
 
670
    if node.sep is None:
 
671
      sep = ''
 
672
    else:
 
673
      sep = node.sep.value
 
674
    (lower, upper) = env.GetRange(identifier)
 
675
    for i in range(lower, upper + 1):
 
676
      new_env = env.Clone()
 
677
      new_env.PushVariable(identifier, i)
 
678
      RunCode(new_env, node.code, output)
 
679
      if i != upper:
 
680
        output.Append(sep)
 
681
  elif isinstance(node, RawCodeNode):
 
682
    output.Append(node.raw_code.value)
 
683
  elif isinstance(node, IfNode):
 
684
    cond = env.EvalExp(node.exp)
 
685
    if cond:
 
686
      RunCode(env.Clone(), node.then_branch, output)
 
687
    elif node.else_branch is not None:
 
688
      RunCode(env.Clone(), node.else_branch, output)
 
689
  elif isinstance(node, ExpNode):
 
690
    value = env.EvalExp(node)
 
691
    output.Append('%s' % (value,))
 
692
  elif isinstance(node, LiteralDollarNode):
 
693
    output.Append('$')
 
694
  elif isinstance(node, CodeNode):
 
695
    RunCode(env.Clone(), node, output)
 
696
  else:
 
697
    print 'BAD'
 
698
    print node
 
699
    sys.exit(1)
 
700
 
 
701
 
 
702
def RunCode(env, code_node, output):
 
703
  for atomic_code in code_node.atomic_code:
 
704
    RunAtomicCode(env, atomic_code, output)
 
705
 
 
706
 
 
707
def IsSingleLineComment(cur_line):
 
708
  return '//' in cur_line
 
709
 
 
710
 
 
711
def IsInPreprocessorDirective(prev_lines, cur_line):
 
712
  if cur_line.lstrip().startswith('#'):
 
713
    return True
 
714
  return prev_lines and prev_lines[-1].endswith('\\')
 
715
 
 
716
 
 
717
def WrapComment(line, output):
 
718
  loc = line.find('//')
 
719
  before_comment = line[:loc].rstrip()
 
720
  if before_comment == '':
 
721
    indent = loc
 
722
  else:
 
723
    output.append(before_comment)
 
724
    indent = len(before_comment) - len(before_comment.lstrip())
 
725
  prefix = indent*' ' + '// '
 
726
  max_len = 80 - len(prefix)
 
727
  comment = line[loc + 2:].strip()
 
728
  segs = [seg for seg in re.split(r'(\w+\W*)', comment) if seg != '']
 
729
  cur_line = ''
 
730
  for seg in segs:
 
731
    if len((cur_line + seg).rstrip()) < max_len:
 
732
      cur_line += seg
 
733
    else:
 
734
      if cur_line.strip() != '':
 
735
        output.append(prefix + cur_line.rstrip())
 
736
      cur_line = seg.lstrip()
 
737
  if cur_line.strip() != '':
 
738
    output.append(prefix + cur_line.strip())
 
739
 
 
740
 
 
741
def WrapCode(line, line_concat, output):
 
742
  indent = len(line) - len(line.lstrip())
 
743
  prefix = indent*' '  # Prefix of the current line
 
744
  max_len = 80 - indent - len(line_concat)  # Maximum length of the current line
 
745
  new_prefix = prefix + 4*' '  # Prefix of a continuation line
 
746
  new_max_len = max_len - 4  # Maximum length of a continuation line
 
747
  # Prefers to wrap a line after a ',' or ';'.
 
748
  segs = [seg for seg in re.split(r'([^,;]+[,;]?)', line.strip()) if seg != '']
 
749
  cur_line = ''  # The current line without leading spaces.
 
750
  for seg in segs:
 
751
    # If the line is still too long, wrap at a space.
 
752
    while cur_line == '' and len(seg.strip()) > max_len:
 
753
      seg = seg.lstrip()
 
754
      split_at = seg.rfind(' ', 0, max_len)
 
755
      output.append(prefix + seg[:split_at].strip() + line_concat)
 
756
      seg = seg[split_at + 1:]
 
757
      prefix = new_prefix
 
758
      max_len = new_max_len
 
759
 
 
760
    if len((cur_line + seg).rstrip()) < max_len:
 
761
      cur_line = (cur_line + seg).lstrip()
 
762
    else:
 
763
      output.append(prefix + cur_line.rstrip() + line_concat)
 
764
      prefix = new_prefix
 
765
      max_len = new_max_len
 
766
      cur_line = seg.lstrip()
 
767
  if cur_line.strip() != '':
 
768
    output.append(prefix + cur_line.strip())
 
769
 
 
770
 
 
771
def WrapPreprocessorDirective(line, output):
 
772
  WrapCode(line, ' \\', output)
 
773
 
 
774
 
 
775
def WrapPlainCode(line, output):
 
776
  WrapCode(line, '', output)
 
777
 
 
778
 
 
779
def IsMultiLineIWYUPragma(line):
 
780
  return re.search(r'/\* IWYU pragma: ', line)
 
781
 
 
782
 
 
783
def IsHeaderGuardIncludeOrOneLineIWYUPragma(line):
 
784
  return (re.match(r'^#(ifndef|define|endif\s*//)\s*[\w_]+\s*$', line) or
 
785
          re.match(r'^#include\s', line) or
 
786
          # Don't break IWYU pragmas, either; that causes iwyu.py problems.
 
787
          re.search(r'// IWYU pragma: ', line))
 
788
 
 
789
 
 
790
def WrapLongLine(line, output):
 
791
  line = line.rstrip()
 
792
  if len(line) <= 80:
 
793
    output.append(line)
 
794
  elif IsSingleLineComment(line):
 
795
    if IsHeaderGuardIncludeOrOneLineIWYUPragma(line):
 
796
      # The style guide made an exception to allow long header guard lines,
 
797
      # includes and IWYU pragmas.
 
798
      output.append(line)
 
799
    else:
 
800
      WrapComment(line, output)
 
801
  elif IsInPreprocessorDirective(output, line):
 
802
    if IsHeaderGuardIncludeOrOneLineIWYUPragma(line):
 
803
      # The style guide made an exception to allow long header guard lines,
 
804
      # includes and IWYU pragmas.
 
805
      output.append(line)
 
806
    else:
 
807
      WrapPreprocessorDirective(line, output)
 
808
  elif IsMultiLineIWYUPragma(line):
 
809
    output.append(line)
 
810
  else:
 
811
    WrapPlainCode(line, output)
 
812
 
 
813
 
 
814
def BeautifyCode(string):
 
815
  lines = string.splitlines()
 
816
  output = []
 
817
  for line in lines:
 
818
    WrapLongLine(line, output)
 
819
  output2 = [line.rstrip() for line in output]
 
820
  return '\n'.join(output2) + '\n'
 
821
 
 
822
 
 
823
def ConvertFromPumpSource(src_text):
 
824
  """Return the text generated from the given Pump source text."""
 
825
  ast = ParseToAST(StripMetaComments(src_text))
 
826
  output = Output()
 
827
  RunCode(Env(), ast, output)
 
828
  return BeautifyCode(output.string)
 
829
 
 
830
 
 
831
def main(argv):
 
832
  if len(argv) == 1:
 
833
    print __doc__
 
834
    sys.exit(1)
 
835
 
 
836
  file_path = argv[-1]
 
837
  output_str = ConvertFromPumpSource(file(file_path, 'r').read())
 
838
  if file_path.endswith('.pump'):
 
839
    output_file_path = file_path[:-5]
 
840
  else:
 
841
    output_file_path = '-'
 
842
  if output_file_path == '-':
 
843
    print output_str,
 
844
  else:
 
845
    output_file = file(output_file_path, 'w')
 
846
    output_file.write('// This file was GENERATED by command:\n')
 
847
    output_file.write('//     %s %s\n' %
 
848
                      (os.path.basename(__file__), os.path.basename(file_path)))
 
849
    output_file.write('// DO NOT EDIT BY HAND!!!\n\n')
 
850
    output_file.write(output_str)
 
851
    output_file.close()
 
852
 
 
853
 
 
854
if __name__ == '__main__':
 
855
  main(sys.argv)