blob: 4052c1399c7688ea4490634a9875e45befabe089 [file] [log] [blame] [edit]
// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// verilog_diff compares the lexical contents of two Verilog source code
// texts. Inputs only need to be lexically valid, not necessarily syntactically
// valid. Use '-' to read from stdin.
// Differences are reported to stdout.
// The program exits 0 if no differences are found, else non-zero.
//
// Example usage:
// verilog_diff [options] file1 file2
#include <iostream>
#include <sstream> // IWYU pragma: keep // for ostringstream
#include <string> // for string, allocator, etc
#include <utility>
#include "absl/flags/flag.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "common/strings/obfuscator.h"
#include "common/util/enum_flags.h"
#include "common/util/file_util.h"
#include "common/util/init_command_line.h"
#include "verilog/analysis/verilog_equivalence.h"
// Enumeration type for selecting
enum class DiffMode {
// TODO(fangism): kNone: none of the existing presets, let the user compose
// the filter predicate and comparator independently.
kFormat,
kObfuscate,
};
static const std::initializer_list<std::pair<const absl::string_view, DiffMode>>
kDiffModeStringMap = {
{"format", DiffMode::kFormat},
{"obfuscate", DiffMode::kObfuscate},
};
std::ostream& operator<<(std::ostream& stream, DiffMode p) {
static const auto* flag_map =
verible::MakeEnumToStringMap(kDiffModeStringMap);
return stream << flag_map->find(p)->second;
}
bool AbslParseFlag(absl::string_view text, DiffMode* mode, std::string* error) {
static const auto* flag_map =
verible::MakeStringToEnumMap(kDiffModeStringMap);
return EnumMapParseFlag(*flag_map, text, mode, error);
}
std::string AbslUnparseFlag(const DiffMode& mode) {
std::ostringstream stream;
stream << mode;
return stream.str();
}
ABSL_FLAG(DiffMode, mode, DiffMode::kFormat,
R"(Defines difference functions.
format: ignore whitespaces, compare token texts.
This is useful for verifying formatter (e.g. verilog_format) output.
obfuscate: preserve whitespaces, compare token texts' lengths only.
This is useful for verifying verilog_obfuscate output.
)");
using EquivalenceFunctionType = std::function<verilog::DiffStatus(
absl::string_view, absl::string_view, std::ostream*)>;
static const std::map<DiffMode, EquivalenceFunctionType> diff_func_map({
{DiffMode::kFormat, verilog::FormatEquivalent},
{DiffMode::kObfuscate, verilog::ObfuscationEquivalent},
});
int main(int argc, char** argv) {
const auto usage = absl::StrCat("usage: ", argv[0],
" [options] file1 file2\n"
"Use - as a file name to read from stdin.");
const auto args = verible::InitCommandLine(usage, &argc, &argv);
enum {
// inputs differ or there is some lexical error in one of the inputs
kInputDifferenceErrorCode = 1,
// error with flags or opening/reading one of the files
kUserErrorCode = 2,
};
if (args.size() != 3) {
std::cerr << "Program requires 2 positional arguments for input files."
<< std::endl;
return kUserErrorCode;
}
// Open both files.
std::string content1;
absl::Status status = verible::file::GetContents(args[1], &content1);
if (!status.ok()) {
std::cerr << args[1] << ": " << status << std::endl;
return kUserErrorCode;
}
std::string content2;
status = verible::file::GetContents(args[2], &content2);
if (!status.ok()) {
std::cerr << args[1] << ": " << status << std::endl;
return kUserErrorCode;
}
// Selection diff-ing function.
const auto diff_mode = absl::GetFlag(FLAGS_mode);
const auto iter = diff_func_map.find(diff_mode);
CHECK(iter != diff_func_map.end());
const auto diff_func = iter->second;
// Compare.
std::ostringstream errstream;
const auto diff_status = diff_func(content1, content2, &errstream);
// Signal result of comparison.
switch (diff_status) {
case verilog::DiffStatus::kEquivalent: {
std::cout << "Inputs match." << std::endl;
break;
}
case verilog::DiffStatus::kDifferent: {
std::cout << "Inputs differ.\n" << errstream.str() << std::endl;
return kInputDifferenceErrorCode;
}
case verilog::DiffStatus::kLeftError: {
std::cout << "Lexical error in first file.\n"
<< errstream.str() << std::endl;
return kInputDifferenceErrorCode;
}
case verilog::DiffStatus::kRightError: {
std::cout << "Lexical error in second file.\n"
<< errstream.str() << std::endl;
return kInputDifferenceErrorCode;
}
}
return 0;
}