OpenCores
URL https://opencores.org/ocsvn/s80186/s80186/trunk

Subversion Repositories s80186

[/] [s80186/] [trunk/] [vendor/] [googletest/] [googletest/] [scripts/] [pump.py] - Blame information for rev 2

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 jamieiles
#!/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)

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.