//===- MlirQueryMain.cpp - MLIR Query main --------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the general framework of the MLIR query tool. It
// parses the command line arguments, parses the MLIR file and outputs the query
// results.
//
//===----------------------------------------------------------------------===//

#include "mlir/Tools/mlir-query/MlirQueryMain.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/Parser/Parser.h"
#include "mlir/Query/Query.h"
#include "mlir/Query/QuerySession.h"
#include "mlir/Support/FileUtilities.h"
#include "llvm/LineEditor/LineEditor.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/SourceMgr.h"

//===----------------------------------------------------------------------===//
// Query Parser
//===----------------------------------------------------------------------===//

llvm::LogicalResult
mlir::mlirQueryMain(int argc, char **argv, MLIRContext &context,
                    const mlir::query::matcher::Registry &matcherRegistry) {

  // Override the default '-h' and use the default PrintHelpMessage() which
  // won't print options in categories.
  static llvm::cl::opt<bool> help("h", llvm::cl::desc("Alias for -help"),
                                  llvm::cl::Hidden);

  static llvm::cl::OptionCategory mlirQueryCategory("mlir-query options");

  static llvm::cl::list<std::string> commands(
      "c", llvm::cl::desc("Specify command to run"),
      llvm::cl::value_desc("command"), llvm::cl::cat(mlirQueryCategory));

  static llvm::cl::opt<std::string> inputFilename(
      llvm::cl::Positional, llvm::cl::desc("<input file>"), llvm::cl::init("-"),
      llvm::cl::cat(mlirQueryCategory));

  static llvm::cl::opt<bool> noImplicitModule{
      "no-implicit-module",
      llvm::cl::desc(
          "Disable implicit addition of a top-level module op during parsing"),
      llvm::cl::init(false)};

  static llvm::cl::opt<bool> allowUnregisteredDialects(
      "allow-unregistered-dialect",
      llvm::cl::desc("Allow operation with no registered dialects"),
      llvm::cl::init(false));

  llvm::cl::HideUnrelatedOptions(mlirQueryCategory);

  llvm::InitLLVM y(argc, argv);

  llvm::cl::ParseCommandLineOptions(argc, argv, "MLIR test case query tool.\n");

  if (help) {
    llvm::cl::PrintHelpMessage();
    return mlir::success();
  }

  // When reading from stdin and the input is a tty, it is often a user mistake
  // and the process "appears to be stuck". Print a message to let the user
  // know!
  if (inputFilename == "-" &&
      llvm::sys::Process::FileDescriptorIsDisplayed(fileno(stdin)))
    llvm::errs() << "(processing input from stdin now, hit ctrl-c/ctrl-d to "
                    "interrupt)\n";

  // Set up the input file.
  std::string errorMessage;
  auto file = openInputFile(inputFilename, &errorMessage);
  if (!file) {
    llvm::errs() << errorMessage << "\n";
    return mlir::failure();
  }

  auto sourceMgr = llvm::SourceMgr();
  auto bufferId = sourceMgr.AddNewSourceBuffer(std::move(file), SMLoc());

  context.allowUnregisteredDialects(allowUnregisteredDialects);

  // Parse the input MLIR file.
  OwningOpRef<Operation *> opRef =
      noImplicitModule ? parseSourceFile(sourceMgr, &context)
                       : parseSourceFile<mlir::ModuleOp>(sourceMgr, &context);
  if (!opRef)
    return mlir::failure();

  mlir::query::QuerySession qs(opRef.get(), sourceMgr, bufferId,
                               matcherRegistry);
  if (!commands.empty()) {
    for (auto &command : commands) {
      mlir::query::QueryRef queryRef = mlir::query::parse(command, qs);
      if (mlir::failed(queryRef->run(llvm::outs(), qs)))
        return mlir::failure();
    }
  } else {
    llvm::LineEditor le("mlir-query");
    le.setListCompleter([&qs](llvm::StringRef line, size_t pos) {
      return mlir::query::complete(line, pos, qs);
    });
    while (std::optional<std::string> line = le.readLine()) {
      mlir::query::QueryRef queryRef = mlir::query::parse(*line, qs);
      (void)queryRef->run(llvm::outs(), qs);
      llvm::outs().flush();
      if (qs.terminate)
        break;
    }
  }

  return mlir::success();
}
