Changeset View
Changeset View
Standalone View
Standalone View
google/googletest/dist/googletest/scripts/pump.py
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:executable | null | * \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
#!/usr/bin/env python | |||||
# | |||||
# Copyright 2008, Google Inc. | |||||
# All rights reserved. | |||||
# | |||||
# Redistribution and use in source and binary forms, with or without | |||||
# modification, are permitted provided that the following conditions are | |||||
# met: | |||||
# | |||||
# * Redistributions of source code must retain the above copyright | |||||
# notice, this list of conditions and the following disclaimer. | |||||
# * Redistributions in binary form must reproduce the above | |||||
# copyright notice, this list of conditions and the following disclaimer | |||||
# in the documentation and/or other materials provided with the | |||||
# distribution. | |||||
# * Neither the name of Google Inc. nor the names of its | |||||
# contributors may be used to endorse or promote products derived from | |||||
# this software without specific prior written permission. | |||||
# | |||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
"""pump v0.2.0 - Pretty Useful for Meta Programming. | |||||
A tool for preprocessor meta programming. Useful for generating | |||||
repetitive boilerplate code. Especially useful for writing C++ | |||||
classes, functions, macros, and templates that need to work with | |||||
various number of arguments. | |||||
USAGE: | |||||
pump.py SOURCE_FILE | |||||
EXAMPLES: | |||||
pump.py foo.cc.pump | |||||
Converts foo.cc.pump to foo.cc. | |||||
GRAMMAR: | |||||
CODE ::= ATOMIC_CODE* | |||||
ATOMIC_CODE ::= $var ID = EXPRESSION | |||||
| $var ID = [[ CODE ]] | |||||
| $range ID EXPRESSION..EXPRESSION | |||||
| $for ID SEPARATOR [[ CODE ]] | |||||
| $($) | |||||
| $ID | |||||
| $(EXPRESSION) | |||||
| $if EXPRESSION [[ CODE ]] ELSE_BRANCH | |||||
| [[ CODE ]] | |||||
| RAW_CODE | |||||
SEPARATOR ::= RAW_CODE | EMPTY | |||||
ELSE_BRANCH ::= $else [[ CODE ]] | |||||
| $elif EXPRESSION [[ CODE ]] ELSE_BRANCH | |||||
| EMPTY | |||||
EXPRESSION has Python syntax. | |||||
""" | |||||
__author__ = 'wan@google.com (Zhanyong Wan)' | |||||
import os | |||||
import re | |||||
import sys | |||||
TOKEN_TABLE = [ | |||||
(re.compile(r'\$var\s+'), '$var'), | |||||
(re.compile(r'\$elif\s+'), '$elif'), | |||||
(re.compile(r'\$else\s+'), '$else'), | |||||
(re.compile(r'\$for\s+'), '$for'), | |||||
(re.compile(r'\$if\s+'), '$if'), | |||||
(re.compile(r'\$range\s+'), '$range'), | |||||
(re.compile(r'\$[_A-Za-z]\w*'), '$id'), | |||||
(re.compile(r'\$\(\$\)'), '$($)'), | |||||
(re.compile(r'\$'), '$'), | |||||
(re.compile(r'\[\[\n?'), '[['), | |||||
(re.compile(r'\]\]\n?'), ']]'), | |||||
] | |||||
class Cursor: | |||||
"""Represents a position (line and column) in a text file.""" | |||||
def __init__(self, line=-1, column=-1): | |||||
self.line = line | |||||
self.column = column | |||||
def __eq__(self, rhs): | |||||
return self.line == rhs.line and self.column == rhs.column | |||||
def __ne__(self, rhs): | |||||
return not self == rhs | |||||
def __lt__(self, rhs): | |||||
return self.line < rhs.line or ( | |||||
self.line == rhs.line and self.column < rhs.column) | |||||
def __le__(self, rhs): | |||||
return self < rhs or self == rhs | |||||
def __gt__(self, rhs): | |||||
return rhs < self | |||||
def __ge__(self, rhs): | |||||
return rhs <= self | |||||
def __str__(self): | |||||
if self == Eof(): | |||||
return 'EOF' | |||||
else: | |||||
return '%s(%s)' % (self.line + 1, self.column) | |||||
def __add__(self, offset): | |||||
return Cursor(self.line, self.column + offset) | |||||
def __sub__(self, offset): | |||||
return Cursor(self.line, self.column - offset) | |||||
def Clone(self): | |||||
"""Returns a copy of self.""" | |||||
return Cursor(self.line, self.column) | |||||
# Special cursor to indicate the end-of-file. | |||||
def Eof(): | |||||
"""Returns the special cursor to denote the end-of-file.""" | |||||
return Cursor(-1, -1) | |||||
class Token: | |||||
"""Represents a token in a Pump source file.""" | |||||
def __init__(self, start=None, end=None, value=None, token_type=None): | |||||
if start is None: | |||||
self.start = Eof() | |||||
else: | |||||
self.start = start | |||||
if end is None: | |||||
self.end = Eof() | |||||
else: | |||||
self.end = end | |||||
self.value = value | |||||
self.token_type = token_type | |||||
def __str__(self): | |||||
return 'Token @%s: \'%s\' type=%s' % ( | |||||
self.start, self.value, self.token_type) | |||||
def Clone(self): | |||||
"""Returns a copy of self.""" | |||||
return Token(self.start.Clone(), self.end.Clone(), self.value, | |||||
self.token_type) | |||||
def StartsWith(lines, pos, string): | |||||
"""Returns True iff the given position in lines starts with 'string'.""" | |||||
return lines[pos.line][pos.column:].startswith(string) | |||||
def FindFirstInLine(line, token_table): | |||||
best_match_start = -1 | |||||
for (regex, token_type) in token_table: | |||||
m = regex.search(line) | |||||
if m: | |||||
# We found regex in lines | |||||
if best_match_start < 0 or m.start() < best_match_start: | |||||
best_match_start = m.start() | |||||
best_match_length = m.end() - m.start() | |||||
best_match_token_type = token_type | |||||
if best_match_start < 0: | |||||
return None | |||||
return (best_match_start, best_match_length, best_match_token_type) | |||||
def FindFirst(lines, token_table, cursor): | |||||
"""Finds the first occurrence of any string in strings in lines.""" | |||||
start = cursor.Clone() | |||||
cur_line_number = cursor.line | |||||
for line in lines[start.line:]: | |||||
if cur_line_number == start.line: | |||||
line = line[start.column:] | |||||
m = FindFirstInLine(line, token_table) | |||||
if m: | |||||
# We found a regex in line. | |||||
(start_column, length, token_type) = m | |||||
if cur_line_number == start.line: | |||||
start_column += start.column | |||||
found_start = Cursor(cur_line_number, start_column) | |||||
found_end = found_start + length | |||||
return MakeToken(lines, found_start, found_end, token_type) | |||||
cur_line_number += 1 | |||||
# We failed to find str in lines | |||||
return None | |||||
def SubString(lines, start, end): | |||||
"""Returns a substring in lines.""" | |||||
if end == Eof(): | |||||
end = Cursor(len(lines) - 1, len(lines[-1])) | |||||
if start >= end: | |||||
return '' | |||||
if start.line == end.line: | |||||
return lines[start.line][start.column:end.column] | |||||
result_lines = ([lines[start.line][start.column:]] + | |||||
lines[start.line + 1:end.line] + | |||||
[lines[end.line][:end.column]]) | |||||
return ''.join(result_lines) | |||||
def StripMetaComments(str): | |||||
"""Strip meta comments from each line in the given string.""" | |||||
# First, completely remove lines containing nothing but a meta | |||||
# comment, including the trailing \n. | |||||
str = re.sub(r'^\s*\$\$.*\n', '', str) | |||||
# Then, remove meta comments from contentful lines. | |||||
return re.sub(r'\s*\$\$.*', '', str) | |||||
def MakeToken(lines, start, end, token_type): | |||||
"""Creates a new instance of Token.""" | |||||
return Token(start, end, SubString(lines, start, end), token_type) | |||||
def ParseToken(lines, pos, regex, token_type): | |||||
line = lines[pos.line][pos.column:] | |||||
m = regex.search(line) | |||||
if m and not m.start(): | |||||
return MakeToken(lines, pos, pos + m.end(), token_type) | |||||
else: | |||||
print 'ERROR: %s expected at %s.' % (token_type, pos) | |||||
sys.exit(1) | |||||
ID_REGEX = re.compile(r'[_A-Za-z]\w*') | |||||
EQ_REGEX = re.compile(r'=') | |||||
REST_OF_LINE_REGEX = re.compile(r'.*?(?=$|\$\$)') | |||||
OPTIONAL_WHITE_SPACES_REGEX = re.compile(r'\s*') | |||||
WHITE_SPACE_REGEX = re.compile(r'\s') | |||||
DOT_DOT_REGEX = re.compile(r'\.\.') | |||||
def Skip(lines, pos, regex): | |||||
line = lines[pos.line][pos.column:] | |||||
m = re.search(regex, line) | |||||
if m and not m.start(): | |||||
return pos + m.end() | |||||
else: | |||||
return pos | |||||
def SkipUntil(lines, pos, regex, token_type): | |||||
line = lines[pos.line][pos.column:] | |||||
m = re.search(regex, line) | |||||
if m: | |||||
return pos + m.start() | |||||
else: | |||||
print ('ERROR: %s expected on line %s after column %s.' % | |||||
(token_type, pos.line + 1, pos.column)) | |||||
sys.exit(1) | |||||
def ParseExpTokenInParens(lines, pos): | |||||
def ParseInParens(pos): | |||||
pos = Skip(lines, pos, OPTIONAL_WHITE_SPACES_REGEX) | |||||
pos = Skip(lines, pos, r'\(') | |||||
pos = Parse(pos) | |||||
pos = Skip(lines, pos, r'\)') | |||||
return pos | |||||
def Parse(pos): | |||||
pos = SkipUntil(lines, pos, r'\(|\)', ')') | |||||
if SubString(lines, pos, pos + 1) == '(': | |||||
pos = Parse(pos + 1) | |||||
pos = Skip(lines, pos, r'\)') | |||||
return Parse(pos) | |||||
else: | |||||
return pos | |||||
start = pos.Clone() | |||||
pos = ParseInParens(pos) | |||||
return MakeToken(lines, start, pos, 'exp') | |||||
def RStripNewLineFromToken(token): | |||||
if token.value.endswith('\n'): | |||||
return Token(token.start, token.end, token.value[:-1], token.token_type) | |||||
else: | |||||
return token | |||||
def TokenizeLines(lines, pos): | |||||
while True: | |||||
found = FindFirst(lines, TOKEN_TABLE, pos) | |||||
if not found: | |||||
yield MakeToken(lines, pos, Eof(), 'code') | |||||
return | |||||
if found.start == pos: | |||||
prev_token = None | |||||
prev_token_rstripped = None | |||||
else: | |||||
prev_token = MakeToken(lines, pos, found.start, 'code') | |||||
prev_token_rstripped = RStripNewLineFromToken(prev_token) | |||||
if found.token_type == '$var': | |||||
if prev_token_rstripped: | |||||
yield prev_token_rstripped | |||||
yield found | |||||
id_token = ParseToken(lines, found.end, ID_REGEX, 'id') | |||||
yield id_token | |||||
pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX) | |||||
eq_token = ParseToken(lines, pos, EQ_REGEX, '=') | |||||
yield eq_token | |||||
pos = Skip(lines, eq_token.end, r'\s*') | |||||
if SubString(lines, pos, pos + 2) != '[[': | |||||
exp_token = ParseToken(lines, pos, REST_OF_LINE_REGEX, 'exp') | |||||
yield exp_token | |||||
pos = Cursor(exp_token.end.line + 1, 0) | |||||
elif found.token_type == '$for': | |||||
if prev_token_rstripped: | |||||
yield prev_token_rstripped | |||||
yield found | |||||
id_token = ParseToken(lines, found.end, ID_REGEX, 'id') | |||||
yield id_token | |||||
pos = Skip(lines, id_token.end, WHITE_SPACE_REGEX) | |||||
elif found.token_type == '$range': | |||||
if prev_token_rstripped: | |||||
yield prev_token_rstripped | |||||
yield found | |||||
id_token = ParseToken(lines, found.end, ID_REGEX, 'id') | |||||
yield id_token | |||||
pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX) | |||||
dots_pos = SkipUntil(lines, pos, DOT_DOT_REGEX, '..') | |||||
yield MakeToken(lines, pos, dots_pos, 'exp') | |||||
yield MakeToken(lines, dots_pos, dots_pos + 2, '..') | |||||
pos = dots_pos + 2 | |||||
new_pos = Cursor(pos.line + 1, 0) | |||||
yield MakeToken(lines, pos, new_pos, 'exp') | |||||
pos = new_pos | |||||
elif found.token_type == '$': | |||||
if prev_token: | |||||
yield prev_token | |||||
yield found | |||||
exp_token = ParseExpTokenInParens(lines, found.end) | |||||
yield exp_token | |||||
pos = exp_token.end | |||||
elif (found.token_type == ']]' or found.token_type == '$if' or | |||||
found.token_type == '$elif' or found.token_type == '$else'): | |||||
if prev_token_rstripped: | |||||
yield prev_token_rstripped | |||||
yield found | |||||
pos = found.end | |||||
else: | |||||
if prev_token: | |||||
yield prev_token | |||||
yield found | |||||
pos = found.end | |||||
def Tokenize(s): | |||||
"""A generator that yields the tokens in the given string.""" | |||||
if s != '': | |||||
lines = s.splitlines(True) | |||||
for token in TokenizeLines(lines, Cursor(0, 0)): | |||||
yield token | |||||
class CodeNode: | |||||
def __init__(self, atomic_code_list=None): | |||||
self.atomic_code = atomic_code_list | |||||
class VarNode: | |||||
def __init__(self, identifier=None, atomic_code=None): | |||||
self.identifier = identifier | |||||
self.atomic_code = atomic_code | |||||
class RangeNode: | |||||
def __init__(self, identifier=None, exp1=None, exp2=None): | |||||
self.identifier = identifier | |||||
self.exp1 = exp1 | |||||
self.exp2 = exp2 | |||||
class ForNode: | |||||
def __init__(self, identifier=None, sep=None, code=None): | |||||
self.identifier = identifier | |||||
self.sep = sep | |||||
self.code = code | |||||
class ElseNode: | |||||
def __init__(self, else_branch=None): | |||||
self.else_branch = else_branch | |||||
class IfNode: | |||||
def __init__(self, exp=None, then_branch=None, else_branch=None): | |||||
self.exp = exp | |||||
self.then_branch = then_branch | |||||
self.else_branch = else_branch | |||||
class RawCodeNode: | |||||
def __init__(self, token=None): | |||||
self.raw_code = token | |||||
class LiteralDollarNode: | |||||
def __init__(self, token): | |||||
self.token = token | |||||
class ExpNode: | |||||
def __init__(self, token, python_exp): | |||||
self.token = token | |||||
self.python_exp = python_exp | |||||
def PopFront(a_list): | |||||
head = a_list[0] | |||||
a_list[:1] = [] | |||||
return head | |||||
def PushFront(a_list, elem): | |||||
a_list[:0] = [elem] | |||||
def PopToken(a_list, token_type=None): | |||||
token = PopFront(a_list) | |||||
if token_type is not None and token.token_type != token_type: | |||||
print 'ERROR: %s expected at %s' % (token_type, token.start) | |||||
print 'ERROR: %s found instead' % (token,) | |||||
sys.exit(1) | |||||
return token | |||||
def PeekToken(a_list): | |||||
if not a_list: | |||||
return None | |||||
return a_list[0] | |||||
def ParseExpNode(token): | |||||
python_exp = re.sub(r'([_A-Za-z]\w*)', r'self.GetValue("\1")', token.value) | |||||
return ExpNode(token, python_exp) | |||||
def ParseElseNode(tokens): | |||||
def Pop(token_type=None): | |||||
return PopToken(tokens, token_type) | |||||
next = PeekToken(tokens) | |||||
if not next: | |||||
return None | |||||
if next.token_type == '$else': | |||||
Pop('$else') | |||||
Pop('[[') | |||||
code_node = ParseCodeNode(tokens) | |||||
Pop(']]') | |||||
return code_node | |||||
elif next.token_type == '$elif': | |||||
Pop('$elif') | |||||
exp = Pop('code') | |||||
Pop('[[') | |||||
code_node = ParseCodeNode(tokens) | |||||
Pop(']]') | |||||
inner_else_node = ParseElseNode(tokens) | |||||
return CodeNode([IfNode(ParseExpNode(exp), code_node, inner_else_node)]) | |||||
elif not next.value.strip(): | |||||
Pop('code') | |||||
return ParseElseNode(tokens) | |||||
else: | |||||
return None | |||||
def ParseAtomicCodeNode(tokens): | |||||
def Pop(token_type=None): | |||||
return PopToken(tokens, token_type) | |||||
head = PopFront(tokens) | |||||
t = head.token_type | |||||
if t == 'code': | |||||
return RawCodeNode(head) | |||||
elif t == '$var': | |||||
id_token = Pop('id') | |||||
Pop('=') | |||||
next = PeekToken(tokens) | |||||
if next.token_type == 'exp': | |||||
exp_token = Pop() | |||||
return VarNode(id_token, ParseExpNode(exp_token)) | |||||
Pop('[[') | |||||
code_node = ParseCodeNode(tokens) | |||||
Pop(']]') | |||||
return VarNode(id_token, code_node) | |||||
elif t == '$for': | |||||
id_token = Pop('id') | |||||
next_token = PeekToken(tokens) | |||||
if next_token.token_type == 'code': | |||||
sep_token = next_token | |||||
Pop('code') | |||||
else: | |||||
sep_token = None | |||||
Pop('[[') | |||||
code_node = ParseCodeNode(tokens) | |||||
Pop(']]') | |||||
return ForNode(id_token, sep_token, code_node) | |||||
elif t == '$if': | |||||
exp_token = Pop('code') | |||||
Pop('[[') | |||||
code_node = ParseCodeNode(tokens) | |||||
Pop(']]') | |||||
else_node = ParseElseNode(tokens) | |||||
return IfNode(ParseExpNode(exp_token), code_node, else_node) | |||||
elif t == '$range': | |||||
id_token = Pop('id') | |||||
exp1_token = Pop('exp') | |||||
Pop('..') | |||||
exp2_token = Pop('exp') | |||||
return RangeNode(id_token, ParseExpNode(exp1_token), | |||||
ParseExpNode(exp2_token)) | |||||
elif t == '$id': | |||||
return ParseExpNode(Token(head.start + 1, head.end, head.value[1:], 'id')) | |||||
elif t == '$($)': | |||||
return LiteralDollarNode(head) | |||||
elif t == '$': | |||||
exp_token = Pop('exp') | |||||
return ParseExpNode(exp_token) | |||||
elif t == '[[': | |||||
code_node = ParseCodeNode(tokens) | |||||
Pop(']]') | |||||
return code_node | |||||
else: | |||||
PushFront(tokens, head) | |||||
return None | |||||
def ParseCodeNode(tokens): | |||||
atomic_code_list = [] | |||||
while True: | |||||
if not tokens: | |||||
break | |||||
atomic_code_node = ParseAtomicCodeNode(tokens) | |||||
if atomic_code_node: | |||||
atomic_code_list.append(atomic_code_node) | |||||
else: | |||||
break | |||||
return CodeNode(atomic_code_list) | |||||
def ParseToAST(pump_src_text): | |||||
"""Convert the given Pump source text into an AST.""" | |||||
tokens = list(Tokenize(pump_src_text)) | |||||
code_node = ParseCodeNode(tokens) | |||||
return code_node | |||||
class Env: | |||||
def __init__(self): | |||||
self.variables = [] | |||||
self.ranges = [] | |||||
def Clone(self): | |||||
clone = Env() | |||||
clone.variables = self.variables[:] | |||||
clone.ranges = self.ranges[:] | |||||
return clone | |||||
def PushVariable(self, var, value): | |||||
# If value looks like an int, store it as an int. | |||||
try: | |||||
int_value = int(value) | |||||
if ('%s' % int_value) == value: | |||||
value = int_value | |||||
except Exception: | |||||
pass | |||||
self.variables[:0] = [(var, value)] | |||||
def PopVariable(self): | |||||
self.variables[:1] = [] | |||||
def PushRange(self, var, lower, upper): | |||||
self.ranges[:0] = [(var, lower, upper)] | |||||
def PopRange(self): | |||||
self.ranges[:1] = [] | |||||
def GetValue(self, identifier): | |||||
for (var, value) in self.variables: | |||||
if identifier == var: | |||||
return value | |||||
print 'ERROR: meta variable %s is undefined.' % (identifier,) | |||||
sys.exit(1) | |||||
def EvalExp(self, exp): | |||||
try: | |||||
result = eval(exp.python_exp) | |||||
except Exception, e: | |||||
print 'ERROR: caught exception %s: %s' % (e.__class__.__name__, e) | |||||
print ('ERROR: failed to evaluate meta expression %s at %s' % | |||||
(exp.python_exp, exp.token.start)) | |||||
sys.exit(1) | |||||
return result | |||||
def GetRange(self, identifier): | |||||
for (var, lower, upper) in self.ranges: | |||||
if identifier == var: | |||||
return (lower, upper) | |||||
print 'ERROR: range %s is undefined.' % (identifier,) | |||||
sys.exit(1) | |||||
class Output: | |||||
def __init__(self): | |||||
self.string = '' | |||||
def GetLastLine(self): | |||||
index = self.string.rfind('\n') | |||||
if index < 0: | |||||
return '' | |||||
return self.string[index + 1:] | |||||
def Append(self, s): | |||||
self.string += s | |||||
def RunAtomicCode(env, node, output): | |||||
if isinstance(node, VarNode): | |||||
identifier = node.identifier.value.strip() | |||||
result = Output() | |||||
RunAtomicCode(env.Clone(), node.atomic_code, result) | |||||
value = result.string | |||||
env.PushVariable(identifier, value) | |||||
elif isinstance(node, RangeNode): | |||||
identifier = node.identifier.value.strip() | |||||
lower = int(env.EvalExp(node.exp1)) | |||||
upper = int(env.EvalExp(node.exp2)) | |||||
env.PushRange(identifier, lower, upper) | |||||
elif isinstance(node, ForNode): | |||||
identifier = node.identifier.value.strip() | |||||
if node.sep is None: | |||||
sep = '' | |||||
else: | |||||
sep = node.sep.value | |||||
(lower, upper) = env.GetRange(identifier) | |||||
for i in range(lower, upper + 1): | |||||
new_env = env.Clone() | |||||
new_env.PushVariable(identifier, i) | |||||
RunCode(new_env, node.code, output) | |||||
if i != upper: | |||||
output.Append(sep) | |||||
elif isinstance(node, RawCodeNode): | |||||
output.Append(node.raw_code.value) | |||||
elif isinstance(node, IfNode): | |||||
cond = env.EvalExp(node.exp) | |||||
if cond: | |||||
RunCode(env.Clone(), node.then_branch, output) | |||||
elif node.else_branch is not None: | |||||
RunCode(env.Clone(), node.else_branch, output) | |||||
elif isinstance(node, ExpNode): | |||||
value = env.EvalExp(node) | |||||
output.Append('%s' % (value,)) | |||||
elif isinstance(node, LiteralDollarNode): | |||||
output.Append('$') | |||||
elif isinstance(node, CodeNode): | |||||
RunCode(env.Clone(), node, output) | |||||
else: | |||||
print 'BAD' | |||||
print node | |||||
sys.exit(1) | |||||
def RunCode(env, code_node, output): | |||||
for atomic_code in code_node.atomic_code: | |||||
RunAtomicCode(env, atomic_code, output) | |||||
def IsSingleLineComment(cur_line): | |||||
return '//' in cur_line | |||||
def IsInPreprocessorDirective(prev_lines, cur_line): | |||||
if cur_line.lstrip().startswith('#'): | |||||
return True | |||||
return prev_lines and prev_lines[-1].endswith('\\') | |||||
def WrapComment(line, output): | |||||
loc = line.find('//') | |||||
before_comment = line[:loc].rstrip() | |||||
if before_comment == '': | |||||
indent = loc | |||||
else: | |||||
output.append(before_comment) | |||||
indent = len(before_comment) - len(before_comment.lstrip()) | |||||
prefix = indent*' ' + '// ' | |||||
max_len = 80 - len(prefix) | |||||
comment = line[loc + 2:].strip() | |||||
segs = [seg for seg in re.split(r'(\w+\W*)', comment) if seg != ''] | |||||
cur_line = '' | |||||
for seg in segs: | |||||
if len((cur_line + seg).rstrip()) < max_len: | |||||
cur_line += seg | |||||
else: | |||||
if cur_line.strip() != '': | |||||
output.append(prefix + cur_line.rstrip()) | |||||
cur_line = seg.lstrip() | |||||
if cur_line.strip() != '': | |||||
output.append(prefix + cur_line.strip()) | |||||
def WrapCode(line, line_concat, output): | |||||
indent = len(line) - len(line.lstrip()) | |||||
prefix = indent*' ' # Prefix of the current line | |||||
max_len = 80 - indent - len(line_concat) # Maximum length of the current line | |||||
new_prefix = prefix + 4*' ' # Prefix of a continuation line | |||||
new_max_len = max_len - 4 # Maximum length of a continuation line | |||||
# Prefers to wrap a line after a ',' or ';'. | |||||
segs = [seg for seg in re.split(r'([^,;]+[,;]?)', line.strip()) if seg != ''] | |||||
cur_line = '' # The current line without leading spaces. | |||||
for seg in segs: | |||||
# If the line is still too long, wrap at a space. | |||||
while cur_line == '' and len(seg.strip()) > max_len: | |||||
seg = seg.lstrip() | |||||
split_at = seg.rfind(' ', 0, max_len) | |||||
output.append(prefix + seg[:split_at].strip() + line_concat) | |||||
seg = seg[split_at + 1:] | |||||
prefix = new_prefix | |||||
max_len = new_max_len | |||||
if len((cur_line + seg).rstrip()) < max_len: | |||||
cur_line = (cur_line + seg).lstrip() | |||||
else: | |||||
output.append(prefix + cur_line.rstrip() + line_concat) | |||||
prefix = new_prefix | |||||
max_len = new_max_len | |||||
cur_line = seg.lstrip() | |||||
if cur_line.strip() != '': | |||||
output.append(prefix + cur_line.strip()) | |||||
def WrapPreprocessorDirective(line, output): | |||||
WrapCode(line, ' \\', output) | |||||
def WrapPlainCode(line, output): | |||||
WrapCode(line, '', output) | |||||
def IsMultiLineIWYUPragma(line): | |||||
return re.search(r'/\* IWYU pragma: ', line) | |||||
def IsHeaderGuardIncludeOrOneLineIWYUPragma(line): | |||||
return (re.match(r'^#(ifndef|define|endif\s*//)\s*[\w_]+\s*$', line) or | |||||
re.match(r'^#include\s', line) or | |||||
# Don't break IWYU pragmas, either; that causes iwyu.py problems. | |||||
re.search(r'// IWYU pragma: ', line)) | |||||
def WrapLongLine(line, output): | |||||
line = line.rstrip() | |||||
if len(line) <= 80: | |||||
output.append(line) | |||||
elif IsSingleLineComment(line): | |||||
if IsHeaderGuardIncludeOrOneLineIWYUPragma(line): | |||||
# The style guide made an exception to allow long header guard lines, | |||||
# includes and IWYU pragmas. | |||||
output.append(line) | |||||
else: | |||||
WrapComment(line, output) | |||||
elif IsInPreprocessorDirective(output, line): | |||||
if IsHeaderGuardIncludeOrOneLineIWYUPragma(line): | |||||
# The style guide made an exception to allow long header guard lines, | |||||
# includes and IWYU pragmas. | |||||
output.append(line) | |||||
else: | |||||
WrapPreprocessorDirective(line, output) | |||||
elif IsMultiLineIWYUPragma(line): | |||||
output.append(line) | |||||
else: | |||||
WrapPlainCode(line, output) | |||||
def BeautifyCode(string): | |||||
lines = string.splitlines() | |||||
output = [] | |||||
for line in lines: | |||||
WrapLongLine(line, output) | |||||
output2 = [line.rstrip() for line in output] | |||||
return '\n'.join(output2) + '\n' | |||||
def ConvertFromPumpSource(src_text): | |||||
"""Return the text generated from the given Pump source text.""" | |||||
ast = ParseToAST(StripMetaComments(src_text)) | |||||
output = Output() | |||||
RunCode(Env(), ast, output) | |||||
return BeautifyCode(output.string) | |||||
def main(argv): | |||||
if len(argv) == 1: | |||||
print __doc__ | |||||
sys.exit(1) | |||||
file_path = argv[-1] | |||||
output_str = ConvertFromPumpSource(file(file_path, 'r').read()) | |||||
if file_path.endswith('.pump'): | |||||
output_file_path = file_path[:-5] | |||||
else: | |||||
output_file_path = '-' | |||||
if output_file_path == '-': | |||||
print output_str, | |||||
else: | |||||
output_file = file(output_file_path, 'w') | |||||
output_file.write('// This file was GENERATED by command:\n') | |||||
output_file.write('// %s %s\n' % | |||||
(os.path.basename(__file__), os.path.basename(file_path))) | |||||
output_file.write('// DO NOT EDIT BY HAND!!!\n\n') | |||||
output_file.write(output_str) | |||||
output_file.close() | |||||
if __name__ == '__main__': | |||||
main(sys.argv) |