Whack gtest files with Python syntax errors
This commit is contained in:
parent
e0e01d53c2
commit
c0b79203f2
@ -1,240 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2009, 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.
|
||||
|
||||
"""fuse_gmock_files.py v0.1.0
|
||||
Fuses Google Mock and Google Test source code into two .h files and a .cc file.
|
||||
|
||||
SYNOPSIS
|
||||
fuse_gmock_files.py [GMOCK_ROOT_DIR] OUTPUT_DIR
|
||||
|
||||
Scans GMOCK_ROOT_DIR for Google Mock and Google Test source
|
||||
code, assuming Google Test is in the GMOCK_ROOT_DIR/../googletest
|
||||
directory, and generates three files:
|
||||
OUTPUT_DIR/gtest/gtest.h, OUTPUT_DIR/gmock/gmock.h, and
|
||||
OUTPUT_DIR/gmock-gtest-all.cc. Then you can build your tests
|
||||
by adding OUTPUT_DIR to the include search path and linking
|
||||
with OUTPUT_DIR/gmock-gtest-all.cc. These three files contain
|
||||
everything you need to use Google Mock. Hence you can
|
||||
"install" Google Mock by copying them to wherever you want.
|
||||
|
||||
GMOCK_ROOT_DIR can be omitted and defaults to the parent
|
||||
directory of the directory holding this script.
|
||||
|
||||
EXAMPLES
|
||||
./fuse_gmock_files.py fused_gmock
|
||||
./fuse_gmock_files.py path/to/unpacked/gmock fused_gmock
|
||||
|
||||
This tool is experimental. In particular, it assumes that there is no
|
||||
conditional inclusion of Google Mock or Google Test headers. Please
|
||||
report any problems to googlemock@googlegroups.com. You can read
|
||||
http://code.google.com/p/googlemock/wiki/CookBook for more
|
||||
information.
|
||||
"""
|
||||
|
||||
__author__ = 'wan@google.com (Zhanyong Wan)'
|
||||
|
||||
import os
|
||||
import re
|
||||
import sets
|
||||
import sys
|
||||
|
||||
# We assume that this file is in the scripts/ directory in the Google
|
||||
# Mock root directory.
|
||||
DEFAULT_GMOCK_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..')
|
||||
|
||||
# We need to call into googletest/scripts/fuse_gtest_files.py.
|
||||
sys.path.append(os.path.join(DEFAULT_GMOCK_ROOT_DIR, '../googletest/scripts'))
|
||||
import fuse_gtest_files
|
||||
gtest = fuse_gtest_files
|
||||
|
||||
# Regex for matching '#include "gmock/..."'.
|
||||
INCLUDE_GMOCK_FILE_REGEX = re.compile(r'^\s*#\s*include\s*"(gmock/.+)"')
|
||||
|
||||
# Where to find the source seed files.
|
||||
GMOCK_H_SEED = 'include/gmock/gmock.h'
|
||||
GMOCK_ALL_CC_SEED = 'src/gmock-all.cc'
|
||||
|
||||
# Where to put the generated files.
|
||||
GTEST_H_OUTPUT = 'gtest/gtest.h'
|
||||
GMOCK_H_OUTPUT = 'gmock/gmock.h'
|
||||
GMOCK_GTEST_ALL_CC_OUTPUT = 'gmock-gtest-all.cc'
|
||||
|
||||
|
||||
def GetGTestRootDir(gmock_root):
|
||||
"""Returns the root directory of Google Test."""
|
||||
|
||||
return os.path.join(gmock_root, '../googletest')
|
||||
|
||||
|
||||
def ValidateGMockRootDir(gmock_root):
|
||||
"""Makes sure gmock_root points to a valid gmock root directory.
|
||||
|
||||
The function aborts the program on failure.
|
||||
"""
|
||||
|
||||
gtest.ValidateGTestRootDir(GetGTestRootDir(gmock_root))
|
||||
gtest.VerifyFileExists(gmock_root, GMOCK_H_SEED)
|
||||
gtest.VerifyFileExists(gmock_root, GMOCK_ALL_CC_SEED)
|
||||
|
||||
|
||||
def ValidateOutputDir(output_dir):
|
||||
"""Makes sure output_dir points to a valid output directory.
|
||||
|
||||
The function aborts the program on failure.
|
||||
"""
|
||||
|
||||
gtest.VerifyOutputFile(output_dir, gtest.GTEST_H_OUTPUT)
|
||||
gtest.VerifyOutputFile(output_dir, GMOCK_H_OUTPUT)
|
||||
gtest.VerifyOutputFile(output_dir, GMOCK_GTEST_ALL_CC_OUTPUT)
|
||||
|
||||
|
||||
def FuseGMockH(gmock_root, output_dir):
|
||||
"""Scans folder gmock_root to generate gmock/gmock.h in output_dir."""
|
||||
|
||||
output_file = file(os.path.join(output_dir, GMOCK_H_OUTPUT), 'w')
|
||||
processed_files = sets.Set() # Holds all gmock headers we've processed.
|
||||
|
||||
def ProcessFile(gmock_header_path):
|
||||
"""Processes the given gmock header file."""
|
||||
|
||||
# We don't process the same header twice.
|
||||
if gmock_header_path in processed_files:
|
||||
return
|
||||
|
||||
processed_files.add(gmock_header_path)
|
||||
|
||||
# Reads each line in the given gmock header.
|
||||
for line in file(os.path.join(gmock_root, gmock_header_path), 'r'):
|
||||
m = INCLUDE_GMOCK_FILE_REGEX.match(line)
|
||||
if m:
|
||||
# It's '#include "gmock/..."' - let's process it recursively.
|
||||
ProcessFile('include/' + m.group(1))
|
||||
else:
|
||||
m = gtest.INCLUDE_GTEST_FILE_REGEX.match(line)
|
||||
if m:
|
||||
# It's '#include "gtest/foo.h"'. We translate it to
|
||||
# "gtest/gtest.h", regardless of what foo is, since all
|
||||
# gtest headers are fused into gtest/gtest.h.
|
||||
|
||||
# There is no need to #include gtest.h twice.
|
||||
if not gtest.GTEST_H_SEED in processed_files:
|
||||
processed_files.add(gtest.GTEST_H_SEED)
|
||||
output_file.write('#include "%s"\n' % (gtest.GTEST_H_OUTPUT,))
|
||||
else:
|
||||
# Otherwise we copy the line unchanged to the output file.
|
||||
output_file.write(line)
|
||||
|
||||
ProcessFile(GMOCK_H_SEED)
|
||||
output_file.close()
|
||||
|
||||
|
||||
def FuseGMockAllCcToFile(gmock_root, output_file):
|
||||
"""Scans folder gmock_root to fuse gmock-all.cc into output_file."""
|
||||
|
||||
processed_files = sets.Set()
|
||||
|
||||
def ProcessFile(gmock_source_file):
|
||||
"""Processes the given gmock source file."""
|
||||
|
||||
# We don't process the same #included file twice.
|
||||
if gmock_source_file in processed_files:
|
||||
return
|
||||
|
||||
processed_files.add(gmock_source_file)
|
||||
|
||||
# Reads each line in the given gmock source file.
|
||||
for line in file(os.path.join(gmock_root, gmock_source_file), 'r'):
|
||||
m = INCLUDE_GMOCK_FILE_REGEX.match(line)
|
||||
if m:
|
||||
# It's '#include "gmock/foo.h"'. We treat it as '#include
|
||||
# "gmock/gmock.h"', as all other gmock headers are being fused
|
||||
# into gmock.h and cannot be #included directly.
|
||||
|
||||
# There is no need to #include "gmock/gmock.h" more than once.
|
||||
if not GMOCK_H_SEED in processed_files:
|
||||
processed_files.add(GMOCK_H_SEED)
|
||||
output_file.write('#include "%s"\n' % (GMOCK_H_OUTPUT,))
|
||||
else:
|
||||
m = gtest.INCLUDE_GTEST_FILE_REGEX.match(line)
|
||||
if m:
|
||||
# It's '#include "gtest/..."'.
|
||||
# There is no need to #include gtest.h as it has been
|
||||
# #included by gtest-all.cc.
|
||||
pass
|
||||
else:
|
||||
m = gtest.INCLUDE_SRC_FILE_REGEX.match(line)
|
||||
if m:
|
||||
# It's '#include "src/foo"' - let's process it recursively.
|
||||
ProcessFile(m.group(1))
|
||||
else:
|
||||
# Otherwise we copy the line unchanged to the output file.
|
||||
output_file.write(line)
|
||||
|
||||
ProcessFile(GMOCK_ALL_CC_SEED)
|
||||
|
||||
|
||||
def FuseGMockGTestAllCc(gmock_root, output_dir):
|
||||
"""Scans folder gmock_root to generate gmock-gtest-all.cc in output_dir."""
|
||||
|
||||
output_file = file(os.path.join(output_dir, GMOCK_GTEST_ALL_CC_OUTPUT), 'w')
|
||||
# First, fuse gtest-all.cc into gmock-gtest-all.cc.
|
||||
gtest.FuseGTestAllCcToFile(GetGTestRootDir(gmock_root), output_file)
|
||||
# Next, append fused gmock-all.cc to gmock-gtest-all.cc.
|
||||
FuseGMockAllCcToFile(gmock_root, output_file)
|
||||
output_file.close()
|
||||
|
||||
|
||||
def FuseGMock(gmock_root, output_dir):
|
||||
"""Fuses gtest.h, gmock.h, and gmock-gtest-all.h."""
|
||||
|
||||
ValidateGMockRootDir(gmock_root)
|
||||
ValidateOutputDir(output_dir)
|
||||
|
||||
gtest.FuseGTestH(GetGTestRootDir(gmock_root), output_dir)
|
||||
FuseGMockH(gmock_root, output_dir)
|
||||
FuseGMockGTestAllCc(gmock_root, output_dir)
|
||||
|
||||
|
||||
def main():
|
||||
argc = len(sys.argv)
|
||||
if argc == 2:
|
||||
# fuse_gmock_files.py OUTPUT_DIR
|
||||
FuseGMock(DEFAULT_GMOCK_ROOT_DIR, sys.argv[1])
|
||||
elif argc == 3:
|
||||
# fuse_gmock_files.py GMOCK_ROOT_DIR OUTPUT_DIR
|
||||
FuseGMock(sys.argv[1], sys.argv[2])
|
||||
else:
|
||||
print __doc__
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,730 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2006, 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.
|
||||
|
||||
"""gen_gtest_pred_impl.py v0.1
|
||||
|
||||
Generates the implementation of Google Test predicate assertions and
|
||||
accompanying tests.
|
||||
|
||||
Usage:
|
||||
|
||||
gen_gtest_pred_impl.py MAX_ARITY
|
||||
|
||||
where MAX_ARITY is a positive integer.
|
||||
|
||||
The command generates the implementation of up-to MAX_ARITY-ary
|
||||
predicate assertions, and writes it to file gtest_pred_impl.h in the
|
||||
directory where the script is. It also generates the accompanying
|
||||
unit test in file gtest_pred_impl_unittest.cc.
|
||||
"""
|
||||
|
||||
__author__ = 'wan@google.com (Zhanyong Wan)'
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
# Where this script is.
|
||||
SCRIPT_DIR = os.path.dirname(sys.argv[0])
|
||||
|
||||
# Where to store the generated header.
|
||||
HEADER = os.path.join(SCRIPT_DIR, '../include/gtest/gtest_pred_impl.h')
|
||||
|
||||
# Where to store the generated unit test.
|
||||
UNIT_TEST = os.path.join(SCRIPT_DIR, '../test/gtest_pred_impl_unittest.cc')
|
||||
|
||||
|
||||
def HeaderPreamble(n):
|
||||
"""Returns the preamble for the header file.
|
||||
|
||||
Args:
|
||||
n: the maximum arity of the predicate macros to be generated.
|
||||
"""
|
||||
|
||||
# A map that defines the values used in the preamble template.
|
||||
DEFS = {
|
||||
'today' : time.strftime('%m/%d/%Y'),
|
||||
'year' : time.strftime('%Y'),
|
||||
'command' : '%s %s' % (os.path.basename(sys.argv[0]), n),
|
||||
'n' : n
|
||||
}
|
||||
|
||||
return (
|
||||
"""// Copyright 2006, 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.
|
||||
|
||||
// This file is AUTOMATICALLY GENERATED on %(today)s by command
|
||||
// '%(command)s'. DO NOT EDIT BY HAND!
|
||||
//
|
||||
// Implements a family of generic predicate assertion macros.
|
||||
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
|
||||
#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
|
||||
|
||||
// Makes sure this header is not included before gtest.h.
|
||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
|
||||
# error Do not include gtest_pred_impl.h directly. Include gtest.h instead.
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_H_
|
||||
|
||||
// This header implements a family of generic predicate assertion
|
||||
// macros:
|
||||
//
|
||||
// ASSERT_PRED_FORMAT1(pred_format, v1)
|
||||
// ASSERT_PRED_FORMAT2(pred_format, v1, v2)
|
||||
// ...
|
||||
//
|
||||
// where pred_format is a function or functor that takes n (in the
|
||||
// case of ASSERT_PRED_FORMATn) values and their source expression
|
||||
// text, and returns a testing::AssertionResult. See the definition
|
||||
// of ASSERT_EQ in gtest.h for an example.
|
||||
//
|
||||
// If you don't care about formatting, you can use the more
|
||||
// restrictive version:
|
||||
//
|
||||
// ASSERT_PRED1(pred, v1)
|
||||
// ASSERT_PRED2(pred, v1, v2)
|
||||
// ...
|
||||
//
|
||||
// where pred is an n-ary function or functor that returns bool,
|
||||
// and the values v1, v2, ..., must support the << operator for
|
||||
// streaming to std::ostream.
|
||||
//
|
||||
// We also define the EXPECT_* variations.
|
||||
//
|
||||
// For now we only support predicates whose arity is at most %(n)s.
|
||||
// Please email googletestframework@googlegroups.com if you need
|
||||
// support for higher arities.
|
||||
|
||||
// GTEST_ASSERT_ is the basic statement to which all of the assertions
|
||||
// in this file reduce. Don't use this in your code.
|
||||
|
||||
#define GTEST_ASSERT_(expression, on_failure) \\
|
||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \\
|
||||
if (const ::testing::AssertionResult gtest_ar = (expression)) \\
|
||||
; \\
|
||||
else \\
|
||||
on_failure(gtest_ar.failure_message())
|
||||
""" % DEFS)
|
||||
|
||||
|
||||
def Arity(n):
|
||||
"""Returns the English name of the given arity."""
|
||||
|
||||
if n < 0:
|
||||
return None
|
||||
elif n <= 3:
|
||||
return ['nullary', 'unary', 'binary', 'ternary'][n]
|
||||
else:
|
||||
return '%s-ary' % n
|
||||
|
||||
|
||||
def Title(word):
|
||||
"""Returns the given word in title case. The difference between
|
||||
this and string's title() method is that Title('4-ary') is '4-ary'
|
||||
while '4-ary'.title() is '4-Ary'."""
|
||||
|
||||
return word[0].upper() + word[1:]
|
||||
|
||||
|
||||
def OneTo(n):
|
||||
"""Returns the list [1, 2, 3, ..., n]."""
|
||||
|
||||
return range(1, n + 1)
|
||||
|
||||
|
||||
def Iter(n, format, sep=''):
|
||||
"""Given a positive integer n, a format string that contains 0 or
|
||||
more '%s' format specs, and optionally a separator string, returns
|
||||
the join of n strings, each formatted with the format string on an
|
||||
iterator ranged from 1 to n.
|
||||
|
||||
Example:
|
||||
|
||||
Iter(3, 'v%s', sep=', ') returns 'v1, v2, v3'.
|
||||
"""
|
||||
|
||||
# How many '%s' specs are in format?
|
||||
spec_count = len(format.split('%s')) - 1
|
||||
return sep.join([format % (spec_count * (i,)) for i in OneTo(n)])
|
||||
|
||||
|
||||
def ImplementationForArity(n):
|
||||
"""Returns the implementation of n-ary predicate assertions."""
|
||||
|
||||
# A map the defines the values used in the implementation template.
|
||||
DEFS = {
|
||||
'n' : str(n),
|
||||
'vs' : Iter(n, 'v%s', sep=', '),
|
||||
'vts' : Iter(n, '#v%s', sep=', '),
|
||||
'arity' : Arity(n),
|
||||
'Arity' : Title(Arity(n))
|
||||
}
|
||||
|
||||
impl = """
|
||||
|
||||
// Helper function for implementing {EXPECT|ASSERT}_PRED%(n)s. Don't use
|
||||
// this in your code.
|
||||
template <typename Pred""" % DEFS
|
||||
|
||||
impl += Iter(n, """,
|
||||
typename T%s""")
|
||||
|
||||
impl += """>
|
||||
AssertionResult AssertPred%(n)sHelper(const char* pred_text""" % DEFS
|
||||
|
||||
impl += Iter(n, """,
|
||||
const char* e%s""")
|
||||
|
||||
impl += """,
|
||||
Pred pred"""
|
||||
|
||||
impl += Iter(n, """,
|
||||
const T%s& v%s""")
|
||||
|
||||
impl += """) {
|
||||
if (pred(%(vs)s)) return AssertionSuccess();
|
||||
|
||||
""" % DEFS
|
||||
|
||||
impl += ' return AssertionFailure() << pred_text << "("'
|
||||
|
||||
impl += Iter(n, """
|
||||
<< e%s""", sep=' << ", "')
|
||||
|
||||
impl += ' << ") evaluates to false, where"'
|
||||
|
||||
impl += Iter(n, """
|
||||
<< "\\n" << e%s << " evaluates to " << v%s""")
|
||||
|
||||
impl += """;
|
||||
}
|
||||
|
||||
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT%(n)s.
|
||||
// Don't use this in your code.
|
||||
#define GTEST_PRED_FORMAT%(n)s_(pred_format, %(vs)s, on_failure)\\
|
||||
GTEST_ASSERT_(pred_format(%(vts)s, %(vs)s), \\
|
||||
on_failure)
|
||||
|
||||
// Internal macro for implementing {EXPECT|ASSERT}_PRED%(n)s. Don't use
|
||||
// this in your code.
|
||||
#define GTEST_PRED%(n)s_(pred, %(vs)s, on_failure)\\
|
||||
GTEST_ASSERT_(::testing::AssertPred%(n)sHelper(#pred""" % DEFS
|
||||
|
||||
impl += Iter(n, """, \\
|
||||
#v%s""")
|
||||
|
||||
impl += """, \\
|
||||
pred"""
|
||||
|
||||
impl += Iter(n, """, \\
|
||||
v%s""")
|
||||
|
||||
impl += """), on_failure)
|
||||
|
||||
// %(Arity)s predicate assertion macros.
|
||||
#define EXPECT_PRED_FORMAT%(n)s(pred_format, %(vs)s) \\
|
||||
GTEST_PRED_FORMAT%(n)s_(pred_format, %(vs)s, GTEST_NONFATAL_FAILURE_)
|
||||
#define EXPECT_PRED%(n)s(pred, %(vs)s) \\
|
||||
GTEST_PRED%(n)s_(pred, %(vs)s, GTEST_NONFATAL_FAILURE_)
|
||||
#define ASSERT_PRED_FORMAT%(n)s(pred_format, %(vs)s) \\
|
||||
GTEST_PRED_FORMAT%(n)s_(pred_format, %(vs)s, GTEST_FATAL_FAILURE_)
|
||||
#define ASSERT_PRED%(n)s(pred, %(vs)s) \\
|
||||
GTEST_PRED%(n)s_(pred, %(vs)s, GTEST_FATAL_FAILURE_)
|
||||
|
||||
""" % DEFS
|
||||
|
||||
return impl
|
||||
|
||||
|
||||
def HeaderPostamble():
|
||||
"""Returns the postamble for the header file."""
|
||||
|
||||
return """
|
||||
|
||||
#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
|
||||
"""
|
||||
|
||||
|
||||
def GenerateFile(path, content):
|
||||
"""Given a file path and a content string, overwrites it with the
|
||||
given content."""
|
||||
|
||||
print 'Updating file %s . . .' % path
|
||||
|
||||
f = file(path, 'w+')
|
||||
print >>f, content,
|
||||
f.close()
|
||||
|
||||
print 'File %s has been updated.' % path
|
||||
|
||||
|
||||
def GenerateHeader(n):
|
||||
"""Given the maximum arity n, updates the header file that implements
|
||||
the predicate assertions."""
|
||||
|
||||
GenerateFile(HEADER,
|
||||
HeaderPreamble(n)
|
||||
+ ''.join([ImplementationForArity(i) for i in OneTo(n)])
|
||||
+ HeaderPostamble())
|
||||
|
||||
|
||||
def UnitTestPreamble():
|
||||
"""Returns the preamble for the unit test file."""
|
||||
|
||||
# A map that defines the values used in the preamble template.
|
||||
DEFS = {
|
||||
'today' : time.strftime('%m/%d/%Y'),
|
||||
'year' : time.strftime('%Y'),
|
||||
'command' : '%s %s' % (os.path.basename(sys.argv[0]), sys.argv[1]),
|
||||
}
|
||||
|
||||
return (
|
||||
"""// Copyright 2006, 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.
|
||||
|
||||
// This file is AUTOMATICALLY GENERATED on %(today)s by command
|
||||
// '%(command)s'. DO NOT EDIT BY HAND!
|
||||
|
||||
// Regression test for gtest_pred_impl.h
|
||||
//
|
||||
// This file is generated by a script and quite long. If you intend to
|
||||
// learn how Google Test works by reading its unit tests, read
|
||||
// gtest_unittest.cc instead.
|
||||
//
|
||||
// This is intended as a regression test for the Google Test predicate
|
||||
// assertions. We compile it as part of the gtest_unittest target
|
||||
// only to keep the implementation tidy and compact, as it is quite
|
||||
// involved to set up the stage for testing Google Test using Google
|
||||
// Test itself.
|
||||
//
|
||||
// Currently, gtest_unittest takes ~11 seconds to run in the testing
|
||||
// daemon. In the future, if it grows too large and needs much more
|
||||
// time to finish, we should consider separating this file into a
|
||||
// stand-alone regression test.
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gtest/gtest-spi.h"
|
||||
|
||||
// A user-defined data type.
|
||||
struct Bool {
|
||||
explicit Bool(int val) : value(val != 0) {}
|
||||
|
||||
bool operator>(int n) const { return value > Bool(n).value; }
|
||||
|
||||
Bool operator+(const Bool& rhs) const { return Bool(value + rhs.value); }
|
||||
|
||||
bool operator==(const Bool& rhs) const { return value == rhs.value; }
|
||||
|
||||
bool value;
|
||||
};
|
||||
|
||||
// Enables Bool to be used in assertions.
|
||||
std::ostream& operator<<(std::ostream& os, const Bool& x) {
|
||||
return os << (x.value ? "true" : "false");
|
||||
}
|
||||
|
||||
""" % DEFS)
|
||||
|
||||
|
||||
def TestsForArity(n):
|
||||
"""Returns the tests for n-ary predicate assertions."""
|
||||
|
||||
# A map that defines the values used in the template for the tests.
|
||||
DEFS = {
|
||||
'n' : n,
|
||||
'es' : Iter(n, 'e%s', sep=', '),
|
||||
'vs' : Iter(n, 'v%s', sep=', '),
|
||||
'vts' : Iter(n, '#v%s', sep=', '),
|
||||
'tvs' : Iter(n, 'T%s v%s', sep=', '),
|
||||
'int_vs' : Iter(n, 'int v%s', sep=', '),
|
||||
'Bool_vs' : Iter(n, 'Bool v%s', sep=', '),
|
||||
'types' : Iter(n, 'typename T%s', sep=', '),
|
||||
'v_sum' : Iter(n, 'v%s', sep=' + '),
|
||||
'arity' : Arity(n),
|
||||
'Arity' : Title(Arity(n)),
|
||||
}
|
||||
|
||||
tests = (
|
||||
"""// Sample functions/functors for testing %(arity)s predicate assertions.
|
||||
|
||||
// A %(arity)s predicate function.
|
||||
template <%(types)s>
|
||||
bool PredFunction%(n)s(%(tvs)s) {
|
||||
return %(v_sum)s > 0;
|
||||
}
|
||||
|
||||
// The following two functions are needed to circumvent a bug in
|
||||
// gcc 2.95.3, which sometimes has problem with the above template
|
||||
// function.
|
||||
bool PredFunction%(n)sInt(%(int_vs)s) {
|
||||
return %(v_sum)s > 0;
|
||||
}
|
||||
bool PredFunction%(n)sBool(%(Bool_vs)s) {
|
||||
return %(v_sum)s > 0;
|
||||
}
|
||||
""" % DEFS)
|
||||
|
||||
tests += """
|
||||
// A %(arity)s predicate functor.
|
||||
struct PredFunctor%(n)s {
|
||||
template <%(types)s>
|
||||
bool operator()(""" % DEFS
|
||||
|
||||
tests += Iter(n, 'const T%s& v%s', sep=""",
|
||||
""")
|
||||
|
||||
tests += """) {
|
||||
return %(v_sum)s > 0;
|
||||
}
|
||||
};
|
||||
""" % DEFS
|
||||
|
||||
tests += """
|
||||
// A %(arity)s predicate-formatter function.
|
||||
template <%(types)s>
|
||||
testing::AssertionResult PredFormatFunction%(n)s(""" % DEFS
|
||||
|
||||
tests += Iter(n, 'const char* e%s', sep=""",
|
||||
""")
|
||||
|
||||
tests += Iter(n, """,
|
||||
const T%s& v%s""")
|
||||
|
||||
tests += """) {
|
||||
if (PredFunction%(n)s(%(vs)s))
|
||||
return testing::AssertionSuccess();
|
||||
|
||||
return testing::AssertionFailure()
|
||||
<< """ % DEFS
|
||||
|
||||
tests += Iter(n, 'e%s', sep=' << " + " << ')
|
||||
|
||||
tests += """
|
||||
<< " is expected to be positive, but evaluates to "
|
||||
<< %(v_sum)s << ".";
|
||||
}
|
||||
""" % DEFS
|
||||
|
||||
tests += """
|
||||
// A %(arity)s predicate-formatter functor.
|
||||
struct PredFormatFunctor%(n)s {
|
||||
template <%(types)s>
|
||||
testing::AssertionResult operator()(""" % DEFS
|
||||
|
||||
tests += Iter(n, 'const char* e%s', sep=""",
|
||||
""")
|
||||
|
||||
tests += Iter(n, """,
|
||||
const T%s& v%s""")
|
||||
|
||||
tests += """) const {
|
||||
return PredFormatFunction%(n)s(%(es)s, %(vs)s);
|
||||
}
|
||||
};
|
||||
""" % DEFS
|
||||
|
||||
tests += """
|
||||
// Tests for {EXPECT|ASSERT}_PRED_FORMAT%(n)s.
|
||||
|
||||
class Predicate%(n)sTest : public testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
expected_to_finish_ = true;
|
||||
finished_ = false;""" % DEFS
|
||||
|
||||
tests += """
|
||||
""" + Iter(n, 'n%s_ = ') + """0;
|
||||
}
|
||||
"""
|
||||
|
||||
tests += """
|
||||
virtual void TearDown() {
|
||||
// Verifies that each of the predicate's arguments was evaluated
|
||||
// exactly once."""
|
||||
|
||||
tests += ''.join(["""
|
||||
EXPECT_EQ(1, n%s_) <<
|
||||
"The predicate assertion didn't evaluate argument %s "
|
||||
"exactly once.";""" % (i, i + 1) for i in OneTo(n)])
|
||||
|
||||
tests += """
|
||||
|
||||
// Verifies that the control flow in the test function is expected.
|
||||
if (expected_to_finish_ && !finished_) {
|
||||
FAIL() << "The predicate assertion unexpactedly aborted the test.";
|
||||
} else if (!expected_to_finish_ && finished_) {
|
||||
FAIL() << "The failed predicate assertion didn't abort the test "
|
||||
"as expected.";
|
||||
}
|
||||
}
|
||||
|
||||
// true iff the test function is expected to run to finish.
|
||||
static bool expected_to_finish_;
|
||||
|
||||
// true iff the test function did run to finish.
|
||||
static bool finished_;
|
||||
""" % DEFS
|
||||
|
||||
tests += Iter(n, """
|
||||
static int n%s_;""")
|
||||
|
||||
tests += """
|
||||
};
|
||||
|
||||
bool Predicate%(n)sTest::expected_to_finish_;
|
||||
bool Predicate%(n)sTest::finished_;
|
||||
""" % DEFS
|
||||
|
||||
tests += Iter(n, """int Predicate%%(n)sTest::n%s_;
|
||||
""") % DEFS
|
||||
|
||||
tests += """
|
||||
typedef Predicate%(n)sTest EXPECT_PRED_FORMAT%(n)sTest;
|
||||
typedef Predicate%(n)sTest ASSERT_PRED_FORMAT%(n)sTest;
|
||||
typedef Predicate%(n)sTest EXPECT_PRED%(n)sTest;
|
||||
typedef Predicate%(n)sTest ASSERT_PRED%(n)sTest;
|
||||
""" % DEFS
|
||||
|
||||
def GenTest(use_format, use_assert, expect_failure,
|
||||
use_functor, use_user_type):
|
||||
"""Returns the test for a predicate assertion macro.
|
||||
|
||||
Args:
|
||||
use_format: true iff the assertion is a *_PRED_FORMAT*.
|
||||
use_assert: true iff the assertion is a ASSERT_*.
|
||||
expect_failure: true iff the assertion is expected to fail.
|
||||
use_functor: true iff the first argument of the assertion is
|
||||
a functor (as opposed to a function)
|
||||
use_user_type: true iff the predicate functor/function takes
|
||||
argument(s) of a user-defined type.
|
||||
|
||||
Example:
|
||||
|
||||
GenTest(1, 0, 0, 1, 0) returns a test that tests the behavior
|
||||
of a successful EXPECT_PRED_FORMATn() that takes a functor
|
||||
whose arguments have built-in types."""
|
||||
|
||||
if use_assert:
|
||||
assrt = 'ASSERT' # 'assert' is reserved, so we cannot use
|
||||
# that identifier here.
|
||||
else:
|
||||
assrt = 'EXPECT'
|
||||
|
||||
assertion = assrt + '_PRED'
|
||||
|
||||
if use_format:
|
||||
pred_format = 'PredFormat'
|
||||
assertion += '_FORMAT'
|
||||
else:
|
||||
pred_format = 'Pred'
|
||||
|
||||
assertion += '%(n)s' % DEFS
|
||||
|
||||
if use_functor:
|
||||
pred_format_type = 'functor'
|
||||
pred_format += 'Functor%(n)s()'
|
||||
else:
|
||||
pred_format_type = 'function'
|
||||
pred_format += 'Function%(n)s'
|
||||
if not use_format:
|
||||
if use_user_type:
|
||||
pred_format += 'Bool'
|
||||
else:
|
||||
pred_format += 'Int'
|
||||
|
||||
test_name = pred_format_type.title()
|
||||
|
||||
if use_user_type:
|
||||
arg_type = 'user-defined type (Bool)'
|
||||
test_name += 'OnUserType'
|
||||
if expect_failure:
|
||||
arg = 'Bool(n%s_++)'
|
||||
else:
|
||||
arg = 'Bool(++n%s_)'
|
||||
else:
|
||||
arg_type = 'built-in type (int)'
|
||||
test_name += 'OnBuiltInType'
|
||||
if expect_failure:
|
||||
arg = 'n%s_++'
|
||||
else:
|
||||
arg = '++n%s_'
|
||||
|
||||
if expect_failure:
|
||||
successful_or_failed = 'failed'
|
||||
expected_or_not = 'expected.'
|
||||
test_name += 'Failure'
|
||||
else:
|
||||
successful_or_failed = 'successful'
|
||||
expected_or_not = 'UNEXPECTED!'
|
||||
test_name += 'Success'
|
||||
|
||||
# A map that defines the values used in the test template.
|
||||
defs = DEFS.copy()
|
||||
defs.update({
|
||||
'assert' : assrt,
|
||||
'assertion' : assertion,
|
||||
'test_name' : test_name,
|
||||
'pf_type' : pred_format_type,
|
||||
'pf' : pred_format,
|
||||
'arg_type' : arg_type,
|
||||
'arg' : arg,
|
||||
'successful' : successful_or_failed,
|
||||
'expected' : expected_or_not,
|
||||
})
|
||||
|
||||
test = """
|
||||
// Tests a %(successful)s %(assertion)s where the
|
||||
// predicate-formatter is a %(pf_type)s on a %(arg_type)s.
|
||||
TEST_F(%(assertion)sTest, %(test_name)s) {""" % defs
|
||||
|
||||
indent = (len(assertion) + 3)*' '
|
||||
extra_indent = ''
|
||||
|
||||
if expect_failure:
|
||||
extra_indent = ' '
|
||||
if use_assert:
|
||||
test += """
|
||||
expected_to_finish_ = false;
|
||||
EXPECT_FATAL_FAILURE({ // NOLINT"""
|
||||
else:
|
||||
test += """
|
||||
EXPECT_NONFATAL_FAILURE({ // NOLINT"""
|
||||
|
||||
test += '\n' + extra_indent + """ %(assertion)s(%(pf)s""" % defs
|
||||
|
||||
test = test % defs
|
||||
test += Iter(n, ',\n' + indent + extra_indent + '%(arg)s' % defs)
|
||||
test += ');\n' + extra_indent + ' finished_ = true;\n'
|
||||
|
||||
if expect_failure:
|
||||
test += ' }, "");\n'
|
||||
|
||||
test += '}\n'
|
||||
return test
|
||||
|
||||
# Generates tests for all 2**6 = 64 combinations.
|
||||
tests += ''.join([GenTest(use_format, use_assert, expect_failure,
|
||||
use_functor, use_user_type)
|
||||
for use_format in [0, 1]
|
||||
for use_assert in [0, 1]
|
||||
for expect_failure in [0, 1]
|
||||
for use_functor in [0, 1]
|
||||
for use_user_type in [0, 1]
|
||||
])
|
||||
|
||||
return tests
|
||||
|
||||
|
||||
def UnitTestPostamble():
|
||||
"""Returns the postamble for the tests."""
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
def GenerateUnitTest(n):
|
||||
"""Returns the tests for up-to n-ary predicate assertions."""
|
||||
|
||||
GenerateFile(UNIT_TEST,
|
||||
UnitTestPreamble()
|
||||
+ ''.join([TestsForArity(i) for i in OneTo(n)])
|
||||
+ UnitTestPostamble())
|
||||
|
||||
|
||||
def _Main():
|
||||
"""The entry point of the script. Generates the header file and its
|
||||
unit test."""
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print __doc__
|
||||
print 'Author: ' + __author__
|
||||
sys.exit(1)
|
||||
|
||||
n = int(sys.argv[1])
|
||||
GenerateHeader(n)
|
||||
GenerateUnitTest(n)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
_Main()
|
||||
@ -1,855 +0,0 @@
|
||||
#!/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)
|
||||
@ -1,158 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2013 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.
|
||||
|
||||
"""Script for branching Google Test/Mock wiki pages for a new version.
|
||||
|
||||
SYNOPSIS
|
||||
release_docs.py NEW_RELEASE_VERSION
|
||||
|
||||
Google Test and Google Mock's external user documentation is in
|
||||
interlinked wiki files. When we release a new version of
|
||||
Google Test or Google Mock, we need to branch the wiki files
|
||||
such that users of a specific version of Google Test/Mock can
|
||||
look up documenation relevant for that version. This script
|
||||
automates that process by:
|
||||
|
||||
- branching the current wiki pages (which document the
|
||||
behavior of the SVN trunk head) to pages for the specified
|
||||
version (e.g. branching FAQ.wiki to V2_6_FAQ.wiki when
|
||||
NEW_RELEASE_VERSION is 2.6);
|
||||
- updating the links in the branched files to point to the branched
|
||||
version (e.g. a link in V2_6_FAQ.wiki that pointed to
|
||||
Primer.wiki#Anchor will now point to V2_6_Primer.wiki#Anchor).
|
||||
|
||||
NOTE: NEW_RELEASE_VERSION must be a NEW version number for
|
||||
which the wiki pages don't yet exist; otherwise you'll get SVN
|
||||
errors like "svn: Path 'V1_7_PumpManual.wiki' is not a
|
||||
directory" when running the script.
|
||||
|
||||
EXAMPLE
|
||||
$ cd PATH/TO/GTEST_SVN_WORKSPACE/trunk
|
||||
$ scripts/release_docs.py 2.6 # create wiki pages for v2.6
|
||||
$ svn status # verify the file list
|
||||
$ svn diff # verify the file contents
|
||||
$ svn commit -m "release wiki pages for v2.6"
|
||||
"""
|
||||
|
||||
__author__ = 'wan@google.com (Zhanyong Wan)'
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import common
|
||||
|
||||
|
||||
# Wiki pages that shouldn't be branched for every gtest/gmock release.
|
||||
GTEST_UNVERSIONED_WIKIS = ['DevGuide.wiki']
|
||||
GMOCK_UNVERSIONED_WIKIS = [
|
||||
'DesignDoc.wiki',
|
||||
'DevGuide.wiki',
|
||||
'KnownIssues.wiki'
|
||||
]
|
||||
|
||||
|
||||
def DropWikiSuffix(wiki_filename):
|
||||
"""Removes the .wiki suffix (if any) from the given filename."""
|
||||
|
||||
return (wiki_filename[:-len('.wiki')] if wiki_filename.endswith('.wiki')
|
||||
else wiki_filename)
|
||||
|
||||
|
||||
class WikiBrancher(object):
|
||||
"""Branches ..."""
|
||||
|
||||
def __init__(self, dot_version):
|
||||
self.project, svn_root_path = common.GetSvnInfo()
|
||||
if self.project not in ('googletest', 'googlemock'):
|
||||
sys.exit('This script must be run in a gtest or gmock SVN workspace.')
|
||||
self.wiki_dir = svn_root_path + '/wiki'
|
||||
# Turn '2.6' to 'V2_6_'.
|
||||
self.version_prefix = 'V' + dot_version.replace('.', '_') + '_'
|
||||
self.files_to_branch = self.GetFilesToBranch()
|
||||
page_names = [DropWikiSuffix(f) for f in self.files_to_branch]
|
||||
# A link to Foo.wiki is in one of the following forms:
|
||||
# [Foo words]
|
||||
# [Foo#Anchor words]
|
||||
# [http://code.google.com/.../wiki/Foo words]
|
||||
# [http://code.google.com/.../wiki/Foo#Anchor words]
|
||||
# We want to replace 'Foo' with 'V2_6_Foo' in the above cases.
|
||||
self.search_for_re = re.compile(
|
||||
# This regex matches either
|
||||
# [Foo
|
||||
# or
|
||||
# /wiki/Foo
|
||||
# followed by a space or a #, where Foo is the name of an
|
||||
# unversioned wiki page.
|
||||
r'(\[|/wiki/)(%s)([ #])' % '|'.join(page_names))
|
||||
self.replace_with = r'\1%s\2\3' % (self.version_prefix,)
|
||||
|
||||
def GetFilesToBranch(self):
|
||||
"""Returns a list of .wiki file names that need to be branched."""
|
||||
|
||||
unversioned_wikis = (GTEST_UNVERSIONED_WIKIS if self.project == 'googletest'
|
||||
else GMOCK_UNVERSIONED_WIKIS)
|
||||
return [f for f in os.listdir(self.wiki_dir)
|
||||
if (f.endswith('.wiki') and
|
||||
not re.match(r'^V\d', f) and # Excluded versioned .wiki files.
|
||||
f not in unversioned_wikis)]
|
||||
|
||||
def BranchFiles(self):
|
||||
"""Branches the .wiki files needed to be branched."""
|
||||
|
||||
print 'Branching %d .wiki files:' % (len(self.files_to_branch),)
|
||||
os.chdir(self.wiki_dir)
|
||||
for f in self.files_to_branch:
|
||||
command = 'svn cp %s %s%s' % (f, self.version_prefix, f)
|
||||
print command
|
||||
os.system(command)
|
||||
|
||||
def UpdateLinksInBranchedFiles(self):
|
||||
|
||||
for f in self.files_to_branch:
|
||||
source_file = os.path.join(self.wiki_dir, f)
|
||||
versioned_file = os.path.join(self.wiki_dir, self.version_prefix + f)
|
||||
print 'Updating links in %s.' % (versioned_file,)
|
||||
text = file(source_file, 'r').read()
|
||||
new_text = self.search_for_re.sub(self.replace_with, text)
|
||||
file(versioned_file, 'w').write(new_text)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
sys.exit(__doc__)
|
||||
|
||||
brancher = WikiBrancher(sys.argv[1])
|
||||
brancher.BranchFiles()
|
||||
brancher.UpdateLinksInBranchedFiles()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,100 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
"""A script to prepare version informtion for use the gtest Info.plist file.
|
||||
|
||||
This script extracts the version information from the configure.ac file and
|
||||
uses it to generate a header file containing the same information. The
|
||||
#defines in this header file will be included in during the generation of
|
||||
the Info.plist of the framework, giving the correct value to the version
|
||||
shown in the Finder.
|
||||
|
||||
This script makes the following assumptions (these are faults of the script,
|
||||
not problems with the Autoconf):
|
||||
1. The AC_INIT macro will be contained within the first 1024 characters
|
||||
of configure.ac
|
||||
2. The version string will be 3 integers separated by periods and will be
|
||||
surrounded by squre brackets, "[" and "]" (e.g. [1.0.1]). The first
|
||||
segment represents the major version, the second represents the minor
|
||||
version and the third represents the fix version.
|
||||
3. No ")" character exists between the opening "(" and closing ")" of
|
||||
AC_INIT, including in comments and character strings.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
|
||||
# Read the command line argument (the output directory for Version.h)
|
||||
if (len(sys.argv) < 3):
|
||||
print "Usage: versiongenerate.py input_dir output_dir"
|
||||
sys.exit(1)
|
||||
else:
|
||||
input_dir = sys.argv[1]
|
||||
output_dir = sys.argv[2]
|
||||
|
||||
# Read the first 1024 characters of the configure.ac file
|
||||
config_file = open("%s/configure.ac" % input_dir, 'r')
|
||||
buffer_size = 1024
|
||||
opening_string = config_file.read(buffer_size)
|
||||
config_file.close()
|
||||
|
||||
# Extract the version string from the AC_INIT macro
|
||||
# The following init_expression means:
|
||||
# Extract three integers separated by periods and surrounded by squre
|
||||
# brackets(e.g. "[1.0.1]") between "AC_INIT(" and ")". Do not be greedy
|
||||
# (*? is the non-greedy flag) since that would pull in everything between
|
||||
# the first "(" and the last ")" in the file.
|
||||
version_expression = re.compile(r"AC_INIT\(.*?\[(\d+)\.(\d+)\.(\d+)\].*?\)",
|
||||
re.DOTALL)
|
||||
version_values = version_expression.search(opening_string)
|
||||
major_version = version_values.group(1)
|
||||
minor_version = version_values.group(2)
|
||||
fix_version = version_values.group(3)
|
||||
|
||||
# Write the version information to a header file to be included in the
|
||||
# Info.plist file.
|
||||
file_data = """//
|
||||
// DO NOT MODIFY THIS FILE (but you can delete it)
|
||||
//
|
||||
// This file is autogenerated by the versiongenerate.py script. This script
|
||||
// is executed in a "Run Script" build phase when creating gtest.framework. This
|
||||
// header file is not used during compilation of C-source. Rather, it simply
|
||||
// defines some version strings for substitution in the Info.plist. Because of
|
||||
// this, we are not not restricted to C-syntax nor are we using include guards.
|
||||
//
|
||||
|
||||
#define GTEST_VERSIONINFO_SHORT %s.%s
|
||||
#define GTEST_VERSIONINFO_LONG %s.%s.%s
|
||||
|
||||
""" % (major_version, minor_version, major_version, minor_version, fix_version)
|
||||
version_file = open("%s/Version.h" % output_dir, 'w')
|
||||
version_file.write(file_data)
|
||||
version_file.close()
|
||||
Loading…
Reference in New Issue
Block a user