blob: bef94a828f44788e33a1107140c52c10377da675 [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/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"
// TODO(hzeller): All these are constructing strings with prefix and postfix.
// And often, these constructs are used in fallback situations in which we
// couldn't parse something and try again in a different setting. That means
// it generates a new string copy and forces full lexing again.
//
// Investigate if we can just take the original token stream and add tokens
// in front and back (should work if these tokens don't bring the lexer into
// a specific state)
// https://github.com/chipsalliance/verible/issues/1519
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,
const VerilogPreprocess::Config &preprocess_config) {
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 = std::make_unique<VerilogAnalyzer>(analyze_text, filename,
preprocess_config);
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,
const VerilogPreprocess::Config &preprocess_config) {
return AnalyzeVerilogConstruct("module foo;\nproperty p;\n", text,
"\nendproperty;\nendmodule;\n", filename,
preprocess_config);
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogStatements(
absl::string_view text, absl::string_view filename,
const VerilogPreprocess::Config &preprocess_config) {
return AnalyzeVerilogConstruct("function foo();\n", text, "\nendfunction\n",
filename, preprocess_config);
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogExpression(
absl::string_view text, absl::string_view filename,
const VerilogPreprocess::Config &preprocess_config) {
return AnalyzeVerilogConstruct("module foo;\nif (", text,
" ) $error;\nendmodule\n", filename,
preprocess_config);
// $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,
const VerilogPreprocess::Config &preprocess_config) {
return AnalyzeVerilogConstruct("module foo;\n", text, "\nendmodule\n",
filename, preprocess_config);
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogClassBody(
absl::string_view text, absl::string_view filename,
const VerilogPreprocess::Config &preprocess_config) {
return AnalyzeVerilogConstruct("class foo;\n", text, "\nendclass\n", filename,
preprocess_config);
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogPackageBody(
absl::string_view text, absl::string_view filename,
const VerilogPreprocess::Config &preprocess_config) {
return AnalyzeVerilogConstruct("package foo;\n", text, "\nendpackage\n",
filename, preprocess_config);
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogLibraryMap(
absl::string_view text, absl::string_view filename,
const VerilogPreprocess::Config &preprocess_config) {
// The prolog/epilog strings come from verilog.lex as token enums:
// PD_LIBRARY_SYNTAX_BEGIN and PD_LIBRARY_SYNTAX_END.
// These are used in verilog.y to enclose the complete library_description
// grammar rule.
return AnalyzeVerilogConstruct(
"`____verible_verilog_library_begin____\n", text,
"\n`____verible_verilog_library_end____\n", filename, preprocess_config);
}
std::unique_ptr<VerilogAnalyzer> AnalyzeVerilogWithMode(
absl::string_view text, absl::string_view filename, absl::string_view mode,
const VerilogPreprocess::Config &preprocess_config) {
static const auto *func_map =
new std::map<absl::string_view,
std::function<std::unique_ptr<VerilogAnalyzer>(
absl::string_view, absl::string_view,
const VerilogPreprocess::Config &)>>{
{"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},
{"parse-as-library-map", &AnalyzeVerilogLibraryMap},
};
const auto *func_ptr = FindOrNull(*func_map, mode);
if (!func_ptr) return nullptr;
return (*func_ptr)(text, filename, preprocess_config);
}
} // namespace verilog