blob: f9044a6c3a8bb2636341de63230d9874321f2346 [file] [log] [blame]
// 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.
#include "verilog/analysis/verilog_excerpt_parse.h"
#include <functional>
#include <map>
#include <memory>
#include <string>
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "common/text/text_structure.h"
#include "common/util/container_util.h"
#include "common/util/logging.h"
#include "verilog/analysis/verilog_analyzer.h"
namespace verilog {
using verible::container::FindOrNull;
// Function template to create any mini-parser for Verilog.
// 'prolog' and 'epilog' are text that wrap around the 'text' argument to
// form a whole Verilog source.
// The returned analyzer's text structure will discard parsed information
// about the prolog and epilog, leaving only the substructure of interest.
static std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogConstruct(
absl::string_view prolog, absl::string_view text, absl::string_view epilog,
absl::string_view filename) {
VLOG(2) << __FUNCTION__;
CHECK(epilog.empty() || absl::ascii_isspace(epilog[0]))
<< "epilog text must begin with a whitespace to prevent unintentional "
"token-joining and escaped-identifier extension.";
const std::string analyze_text = absl::StrCat(prolog, text, epilog);
// Disable parser directive comments because a specific parser
// is already being selected.
auto analyzer_ptr = absl::make_unique<VerilogAnalyzer>(
analyze_text, filename,
/* use_parser_directive_comments_ */ false);
if (!ABSL_DIE_IF_NULL(analyzer_ptr)->Analyze().ok()) {
VLOG(2) << __FUNCTION__ << ": Analyze() failed. code:\n" << analyze_text;
// Continue to processes, even if there's an error, so that token
// string_views can be properly rebased.
// There may or may not be a formed syntax tree.
}
// Trim off prolog and epilog from internal text structure to make it look
// as if only text were analyzed.
analyzer_ptr->MutableData().FocusOnSubtreeSpanningSubstring(prolog.length(),
text.length());
// TODO(b/129905498): Strengthen check on the resulting syntax tree root,
// by qualifying it against an expected nonterminal node type(s).
VLOG(2) << "end of " << __FUNCTION__;
return analyzer_ptr; // Let caller check analyzer_ptr's status.
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogPropertySpec(
absl::string_view text, absl::string_view filename) {
return AnalyzeVerilogConstruct("module foo;\nproperty p;\n", text,
"\nendproperty;\nendmodule;\n", filename);
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogStatements(
absl::string_view text, absl::string_view filename) {
return AnalyzeVerilogConstruct("function foo();\n", text, "\nendfunction\n",
filename);
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogExpression(
absl::string_view text, absl::string_view filename) {
return AnalyzeVerilogConstruct("module foo;\nif (", text,
" ) $error;\nendmodule\n", filename);
// $error in this context is an elaboration system task
// The space before the ) is critical to accommodate escaped identifiers.
// Without the space, lexing an escaped identifier would consume part
// of the epilog text.
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogModuleBody(
absl::string_view text, absl::string_view filename) {
return AnalyzeVerilogConstruct("module foo;\n", text, "\nendmodule\n",
filename);
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogClassBody(
absl::string_view text, absl::string_view filename) {
return AnalyzeVerilogConstruct("class foo;\n", text, "\nendclass\n",
filename);
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogPackageBody(
absl::string_view text, absl::string_view filename) {
return AnalyzeVerilogConstruct("package foo;\n", text, "\nendpackage\n",
filename);
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogWithMode(
absl::string_view text, absl::string_view filename,
absl::string_view mode) {
static const auto* func_map =
new std::map<absl::string_view,
std::function<std::unique_ptr<VerilogAnalyzer>(
absl::string_view, absl::string_view)>>{
{"parse-as-statements", &AnalyzeVerilogStatements},
{"parse-as-expression", &AnalyzeVerilogExpression},
{"parse-as-module-body", &AnalyzeVerilogModuleBody},
{"parse-as-class-body", &AnalyzeVerilogClassBody},
{"parse-as-package-body", &AnalyzeVerilogPackageBody},
{"parse-as-property-spec", &AnalyzeVerilogPropertySpec},
};
auto func_ptr = FindOrNull(*func_map, mode);
if (func_ptr == nullptr) return nullptr;
return (*func_ptr)(text, filename);
}
} // namespace verilog