[mathicgb] 321/393: Added program for checking proper format of code files.
Doug Torrance
dtorrance-guest at moszumanska.debian.org
Fri Apr 3 15:59:28 UTC 2015
This is an automated email from the git hooks/post-receive script.
dtorrance-guest pushed a commit to branch upstream
in repository mathicgb.
commit 6b201a7a02b9e7cef0b493232e44ffc006e0385a
Author: Bjarke Hammersholt Roune <bjarkehr.code at gmail.com>
Date: Mon May 13 17:44:37 2013 +0200
Added program for checking proper format of code files.
---
.gitignore | 1 +
src/checksource/CheckSource.cpp | 178 ++++++++++++++++++++++++++++
src/checksource/Scanner.cpp | 171 +++++++++++++++++++++++++++
src/checksource/Scanner.hpp | 255 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 605 insertions(+)
diff --git a/.gitignore b/.gitignore
index aa5a783..85ef426 100755
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
*.slo
*.lo
*.o
+*.exe
# Compiled Dynamic libraries
*.so
diff --git a/src/checksource/CheckSource.cpp b/src/checksource/CheckSource.cpp
new file mode 100755
index 0000000..94773c5
--- /dev/null
+++ b/src/checksource/CheckSource.cpp
@@ -0,0 +1,178 @@
+// Program for checking format of code, with respect to proper
+// copyright header, no tabs, order of includes and so on.
+//
+// This code is not pretty, especially not the duplication of Scanner,
+// but this makes this whole thing easy to compile and this is VERY
+// quick-and-dirty code anyway. Anyone who wants to clean up thing up
+// with proper integration into the build system is more than welcome
+// to do so.
+#include "Scanner.cpp"
+
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+#include <fstream>
+#include <string>
+
+void error(std::string str) {
+ throw std::runtime_error(str);
+}
+
+bool endsWith(const std::string& a, const std::string& b) {
+ return a.size() >= b.size() && a.substr(a.size() - b.size()) == b;
+}
+
+bool matchInclude(Scanner& in, bool& system) {
+ if (!in.match("#include "))
+ return false;
+
+ char delim = '\"';
+ system = false;
+ if (!in.match('\"')) {
+ if (in.match('<')) {
+ system = true;
+ delim = '>';
+ } else
+ in.reportError("Expected < or \" after #include.");
+ }
+
+ in.readUntil(delim);
+ in.expect(delim);
+ return true;
+}
+
+std::string stripEnding(const std::string& str) {
+ const auto index = str.find_last_of('.');
+ if (index == std::string::npos)
+ return str;
+ else
+ return str.substr(0, index);
+}
+
+
+
+void checkCopyrightHeader(Scanner& in) {
+ const char* const l1 =
+ "// MathicGB copyright 2012 all rights reserved. "
+ "MathicGB comes with ABSOLUTELY\n";
+ const char* const l2 =
+ "// NO WARRANTY and is licensed as GPL v2.0 or later - see LICENSE.txt.\n";
+ in.match(l1);
+ in.match(l2);
+}
+
+void checkStdInc(Scanner& in) {
+ in.eatWhite();
+ in.expect("#include \"stdinc.h\"");
+}
+
+void checkIncludes(Scanner& in) {
+ bool sawSystem = false;
+ bool system;
+ while (matchInclude(in, system)) {
+ if (sawSystem && !system)
+ in.reportError("#include < > must come after all #include \" \".");
+ sawSystem = system;
+ }
+}
+
+void checkInclusionGuard(Scanner& in, const std::string& filename) {
+ auto normalize = [](const std::string& str) {
+ std::string norm;
+ for (size_t i = 0; i < str.size(); ++i)
+ if (str[i] != '_')
+ norm += std::toupper(str[i]);
+ return norm;
+ };
+
+ in.expect("#ifndef ");
+ const auto macroName = in.readString();
+ const auto fileNorm = normalize
+ ("MATHICGB_" + stripEnding(filename) + "_GUARD");
+
+ if (fileNorm != normalize(macroName)) {
+ std::ostringstream err;
+ err << "Inclusion guard name does not match file name.\n";
+ err << "Normalization removes _ and converts to upper case.\n";
+ err << " Filename normalizes to: " << fileNorm << '\n';
+ err << "macro name normalizes to: " << normalize(macroName) << '\n';
+ in.reportError(err.str());
+ }
+
+ in.expect("#define ");
+ std::string macro2;
+ macro2 = in.readString();
+ if (macroName != macro2) {
+ std::ostringstream out;
+ out << "Inclusion guard #ifndef and #define macro name mismatch.\n";
+ out << "#ifndef macro name: " << macroName << '\n';
+ out << "#define macro name: " << macro2 << '\n';
+ in.reportError(out.str());
+ }
+}
+
+void checkOther(const std::string& filename) {
+ std::ifstream file(filename.c_str(), std::ios_base::binary);
+ Scanner in(file);
+ bool mgbNamespace = false;
+ while (!in.matchEOF()) {
+ if (in.peek() == '\r')
+ in.reportError("File contains dos/windows line ending character \\r.");
+ if (in.peek() == '\t')
+ in.reportError("File contains a tab.");
+ if (
+ in.match("Local Variables:") ||
+ in.match("compile-command:") ||
+ in.match("indent-tabs-mode:")
+ )
+ in.reportError("File contains emacs-specific command comments.");
+ if (in.match("namespace mgb") || in.match("MATHICGB_NAMESPACE_BEGIN"))
+ mgbNamespace = true;
+ else
+ in.get();
+ }
+ if (!mgbNamespace)
+ in.reportError("MATHICGB_NAMESPACE_BEGIN does not appear in file");
+}
+
+void checkFile(std::string filename) {
+ try {
+ std::cout << "Checking file " << filename << std::endl;
+ const bool hpp = endsWith(filename, ".hpp");
+ const bool cpp = endsWith(filename, ".cpp");
+ if (!hpp && !cpp)
+ return;
+
+ std::ifstream file(filename.c_str());
+ if (!file)
+ error("could not open file");
+ Scanner in(file);
+
+ if (in.peekWhite())
+ in.reportError
+ ("File should start with copyright header, not whitespace.");
+
+ checkCopyrightHeader(in);
+ if (in.peekWhite())
+ in.reportError
+ ("There should not be whitespace after the copyright header.");
+
+ if (cpp)
+ checkStdInc(in);
+ else
+ checkInclusionGuard(in, filename);
+ checkIncludes(in);
+ file.close();
+
+ checkOther(filename);
+ } catch (std::exception& e) {
+ std::cout << "*** ERROR in file " << filename << " ***\n"
+ << e.what() << std::endl;
+ }
+}
+
+int main(int argc, char* argv[]) {
+ for (int i = 1; i < argc; ++i)
+ checkFile(argv[i]);
+ return 0;
+}
diff --git a/src/checksource/Scanner.cpp b/src/checksource/Scanner.cpp
new file mode 100755
index 0000000..9f8ec2b
--- /dev/null
+++ b/src/checksource/Scanner.cpp
@@ -0,0 +1,171 @@
+#include "Scanner.hpp"
+
+#include <limits>
+#include <sstream>
+#include <cstring>
+#include <stdexcept>
+
+static const size_t BufferSize = 10 * 1024;
+
+void reportSyntaxError(std::string s, uint64 lineNumber) {
+ std::ostringstream out;
+ out << "Syntax error on line " << lineNumber << ": " << s;
+ throw std::runtime_error(out.str());
+}
+
+void Scanner::reportError(std::string msg) const {
+ reportSyntaxError(msg, lineCount());
+}
+
+Scanner::Scanner(FILE* input):
+ mFile(input),
+ mStream(0),
+ mLineCount(1),
+ mChar(' '),
+ mBuffer(BufferSize),
+ mBufferPos(mBuffer.end())
+{
+ get();
+}
+
+Scanner::Scanner(std::istream& input):
+ mFile(0),
+ mStream(&input),
+ mLineCount(1),
+ mChar(' '),
+ mBuffer(BufferSize),
+ mBufferPos(mBuffer.end())
+{
+ get();
+}
+
+Scanner::Scanner(const char* const input):
+ mFile(0),
+ mStream(0),
+ mLineCount(1),
+ mChar(' '),
+ mBuffer(input, input + std::strlen(input)),
+ mBufferPos(mBuffer.begin())
+{
+ get();
+}
+
+Scanner::Scanner(const std::string& input):
+ mFile(0),
+ mStream(0),
+ mLineCount(1),
+ mChar(' '),
+ mBuffer(input.begin(), input.end()),
+ mBufferPos(mBuffer.begin())
+{
+ get();
+}
+
+bool Scanner::match(const char* const str) {
+ eatWhite();
+ const auto size = std::strlen(str);
+ if (!ensureBuffer(size))
+ return false;
+ if (size == 0)
+ return true;
+ if (peek() != *str)
+ return false;
+ if (std::strncmp(&*mBufferPos, str + 1, size - 1) != 0)
+ return false;
+ ignore(size);
+ return true;
+}
+
+bool Scanner::ensureBuffer(size_t min) {
+ const auto got = std::distance(mBufferPos, mBuffer.end()) + 1;
+ return got >= min || readBuffer(min - got);
+}
+
+void Scanner::expect(const char* str) {
+ eatWhite();
+
+ const char* it = str;
+ while (*it != '\0') {
+ int character = get();
+ if (*it == character) {
+ ++it;
+ continue;
+ }
+
+ // Read the rest of what is there to improve error message.
+ // TODO: read at least one char in total even if not alnum.
+ std::ostringstream got;
+ if (character == EOF && it == str)
+ got << "no more input";
+ else {
+ got << '\"' << std::string(str, it);
+ if (isalnum(character))
+ got << static_cast<char>(character);
+ while (isalnum(peek()))
+ got << static_cast<char>(get());
+ got << '\"';
+ }
+
+ reportErrorUnexpectedToken(str, got.str());
+ }
+}
+
+void Scanner::expectEOF() {
+ eatWhite();
+ if (get() != EOF)
+ reportErrorUnexpectedToken("no more input", "");
+}
+
+void Scanner::errorExpectTwo(char a, char b, int got) {
+ std::ostringstream err;
+ err << '\'' << a << "' or '" << b << '\'';
+ reportErrorUnexpectedToken(err.str(), got);
+}
+
+void Scanner::errorExpectOne(char expected, int got) {
+ std::ostringstream err;
+ err << '\'' << expected << '\'';
+ reportErrorUnexpectedToken(err.str(), got);
+}
+
+void Scanner::reportErrorUnexpectedToken(const std::string& expected, int got) {
+ std::ostringstream gotDescription;
+ if (got == EOF)
+ gotDescription << "no more input";
+ else
+ gotDescription << '\'' << static_cast<char>(got) << '\'';
+ reportErrorUnexpectedToken(expected, gotDescription.str());
+}
+
+void Scanner::reportErrorUnexpectedToken(
+ const std::string& expected,
+ const std::string& got
+) {
+ std::ostringstream errorMsg;
+ errorMsg << "Expected " << expected;
+ if (got != "")
+ errorMsg << ", but got " << got;
+ errorMsg << '.';
+ reportError(errorMsg.str());
+}
+
+bool Scanner::readBuffer(size_t minRead) {
+ auto saveCount = std::distance(mBufferPos, mBuffer.end());
+ if (mBufferPos != mBuffer.begin() && mBufferPos != mBuffer.end())
+ std::copy(mBufferPos, mBuffer.end(), mBuffer.begin());
+ mBuffer.resize(std::max(saveCount + minRead, mBuffer.capacity()));
+ auto readInto = reinterpret_cast<char*>(mBuffer.data() + saveCount);
+ auto readCount = mBuffer.size() - saveCount;
+
+ size_t didReadCount = 0;
+ if (mFile != 0) {
+ didReadCount = fread(readInto, 1, readCount, mFile);
+ } else if (mStream != 0) {
+ mStream->read(readInto, readCount);
+ didReadCount = mStream->gcount();
+ }
+ mBuffer.resize(saveCount + didReadCount);
+ mBufferPos = mBuffer.begin();
+
+ return didReadCount >= minRead;
+}
diff --git a/src/checksource/Scanner.hpp b/src/checksource/Scanner.hpp
new file mode 100755
index 0000000..c3c0076
--- /dev/null
+++ b/src/checksource/Scanner.hpp
@@ -0,0 +1,255 @@
+#ifndef MATHICGB_SCANNER_GUARD
+#define MATHICGB_SCANNER_GUARD
+
+#include <string>
+#include <cstdio>
+#include <vector>
+#include <type_traits>
+#include <limits>
+#include <sstream>
+#include <istream>
+
+typedef unsigned long long uint64;
+
+/// This class offers an input interface which is more convenient and
+/// often more efficient than dealing with a FILE* or std::istream
+/// directly. It keeps track of the current line number to report
+/// better error messages. Only one Scanner should be reading from a
+/// given FILE* or std::istream due to buffering and line number counting.
+///
+/// All input methods whose documentation does not specifically say
+/// otherwise skip whitespace.
+///
+/// There are four concepts for consuming input through a Scanner:
+///
+/// Read X: Require an X to be in the input, and return what is read.
+///
+/// Expect X: Require the exact value X to be in the input and skip past it.
+///
+/// Match X: Return true if the exact value X is in the input, and in that case
+/// skip past it. Otherwise return false and do nothing else.
+///
+/// MatchRead X: Return true of an X is in the input. In that case,
+/// read the X into a reference parameter and return true.
+///
+/// Peek X: Return true if X is the next thing int he input. Do not skip
+/// past anything. May or may not skip whitespace depending on what X is.
+///
+/// If a requirement is not met, Scanner reports a syntax error.
+class Scanner {
+public:
+ /// Construct a Scanner object reading from the input FILE*.
+ Scanner(FILE* input);
+
+ /// Construct a Scanner object reading from the input std::istream.
+ Scanner(std::istream& input);
+
+ /// Construct a Scanner object reading from the input string.
+ Scanner(const char* const input);
+
+ /// Construct a Scanner object reading from the input string.
+ Scanner(const std::string& input);
+
+ /// Reads a single character from the stream.
+ int get();
+
+ /// Takes count characters off the stream.
+ void ignore(size_t count);
+
+ /// Return true if the next character is c, and in that case skip
+ /// past it.
+ bool match(char c);
+
+ /// Return true if no more input.
+ bool matchEOF();
+
+ bool match(const char* const str);
+
+ /// Require the next character to be equal to expected. This
+ /// character is skipped past.
+ void expect(char expected);
+
+ /// Require the next character to be equal to a or b. This character
+ /// is skipped past.
+ void expect(char a, char b);
+
+ /// Require the following characters to be equal to str. These
+ /// characters are skipped past.
+ void expect(const char* str);
+
+ /// Require the following characters to be equal to str. These
+ /// characters are skipped past.
+ void expect(const std::string& str) {expect(str.c_str());}
+
+ /// Require that there is no more input.
+ void expectEOF();
+
+ /// Reads a T. T must be an integer type with a std::numeric_limits
+ /// specialization. Negative numbers are allows if T is signed.
+ template<class T>
+ T readInteger(bool negate = false);
+
+ /// Reads a T if it is there. Does not recognize + or - as the start
+ /// of an integer.
+ template<class T>
+ bool matchReadIntegerNoSign(T& t, bool negate = false);
+
+ void readUntil(char delimiter) {
+ while (peek() != delimiter && !matchEOF())
+ get();
+ }
+
+ std::string readString() {
+ std::string str;
+ while (!peekWhite() && !matchEOF())
+ str += get();
+ return str;
+ }
+
+ /// Returns the next character or EOF. Does not skip whitespace.
+ int peek() {return mChar;}
+
+ /// Returns true if the next character is a digit. Does not skip
+ /// whitespace.
+ bool peekDigit() {return std::isdigit(peek());}
+
+ bool peekAlpha() {return std::isalpha(peek());}
+
+ /// Returns true if the next character is whitespace. Does not skip
+ /// whitespace. Whitespace is defined by std::isspace().
+ bool peekWhite() {return isspace(peek());}
+
+ /// Returns true if the next character is + or -. Does not skip
+ /// whitespace.
+ bool peekSign() {return peek() == '+' || peek() == '-';}
+
+ /// Returns the number of newlines seen plus one. Does not skip
+ /// whitespace.
+ uint64 lineCount() const {return mLineCount;}
+
+ /// Reads past any whitespace.
+ inline void eatWhite();
+
+ void reportError(std::string msg) const;
+
+private:
+ void errorExpectTwo(char a, char b, int got);
+ void errorExpectOne(char expected, int got);
+
+ void reportErrorUnexpectedToken(const std::string& expected, int got);
+ void reportErrorUnexpectedToken
+ (const std::string& expected, const std::string& got);
+
+ bool ensureBuffer(size_t min);
+ bool readBuffer(size_t minRead);
+
+ FILE* mFile;
+ std::istream* mStream;
+ uint64 mLineCount;
+ int mChar; // next character on stream
+
+ std::vector<char> mBuffer;
+ std::vector<char>::iterator mBufferPos;
+};
+
+inline bool Scanner::matchEOF() {
+ eatWhite();
+ return peek() == EOF;
+}
+
+inline bool Scanner::match(char c) {
+ eatWhite();
+ if (c == peek()) {
+ get();
+ return true;
+ } else
+ return false;
+}
+
+inline void Scanner::expect(char a, char b) {
+ eatWhite();
+ int got = get();
+ if (got != a && got != b)
+ errorExpectTwo(a, b, got);
+}
+
+inline void Scanner::expect(char expected) {
+ eatWhite();
+ int got = get();
+ if (got != expected)
+ errorExpectOne(expected, got);
+}
+
+inline void Scanner::eatWhite() {
+ while (peekWhite())
+ get();
+}
+
+inline int Scanner::get() {
+ if (mChar == '\n')
+ ++mLineCount;
+ int oldChar = mChar;
+ if (mBufferPos == mBuffer.end() && !readBuffer(1))
+ mChar = EOF;
+ else {
+ mChar = *mBufferPos;
+ ++mBufferPos;
+ }
+ return oldChar;
+}
+
+inline void Scanner::ignore(size_t count) {
+ for (size_t i = 0; i < count; ++i)
+ get();
+}
+
+
+
+template<class T>
+T Scanner::readInteger(const bool negate) {
+ static_assert(std::numeric_limits<T>::is_integer, "");
+
+ eatWhite();
+ const bool minus = !match('+') && match('-');
+ const bool positive = minus == negate;
+ if (!peekDigit())
+ reportErrorUnexpectedToken("an integer", "");
+ // Skip leading zeroes and return if the number is zero.
+ if (peek() == '0') {
+ while (peek() == '0')
+ get();
+ if (!peekDigit())
+ return static_cast<T>(0);
+ }
+
+ // Checking this here allows us to recognize -0 as non-negative.
+ if (!positive && !std::numeric_limits<T>::is_signed)
+ reportErrorUnexpectedToken("a positive integer", "");
+
+ const auto min = std::numeric_limits<T>::min();
+ const auto max = std::numeric_limits<T>::max();
+ auto t = static_cast<T>(0);
+ while (peekDigit()) {
+ const auto c = static_cast<char>(get());
+ const auto d = positive ? c - '0' : -(c - '0');
+ if (positive ? t > (max - d) / 10 : t < (min - d) / 10) {
+ std::ostringstream err;
+ err << "an integer in the range [" << min
+ << ", " << max << ']';
+ reportErrorUnexpectedToken(err.str(), "");
+ }
+ t = t * 10 + d;
+ }
+ return t;
+}
+
+template<class T>
+bool Scanner::matchReadIntegerNoSign(T& t, bool negate) {
+ if (peekDigit()) {
+ t = readInteger<T>(negate);
+ return true;
+ } else
+ return false;
+}
+
+#endif
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/mathicgb.git
More information about the debian-science-commits
mailing list