/*
Please use git log for copyright holder and year information
This file is part of libbash.
libbash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
libbash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with libbash. If not, see .
*/
///
/// \file ast_printer.cpp
/// \brief helper program to visualize AST
///
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "core/bash_ast.h"
#include "exceptions.h"
#include "libbashLexer.h"
#include "libbashParser.h"
namespace po = boost::program_options;
namespace qi = boost::spirit::qi;
struct reversed_pair : std::pair
{};
BOOST_FUSION_ADAPT_STRUCT(
reversed_pair
, (std::string, second)
(ANTLR3_INT32, first)
)
static void print_ast(std::istream& input, bool silent, bool dot)
{
bash_ast ast(input);
if(silent)
return;
if(dot)
std::cout << ast.get_dot_graph() << std::endl;
else
std::cout << ast.get_string_tree() << std::endl;
}
static inline std::string token_mapper(std::unordered_map token_map,
ANTLR3_INT32 token)
{
return token_map[token];
}
static bool build_token_map(std::unordered_map& token_map,
const std::string& path)
{
std::ifstream token_file(path);
token_file.unsetf(std::ios::skipws);
typedef boost::spirit::istream_iterator iterator;
iterator first(token_file);
iterator last;
qi::rule line = +~qi::char_('=') >> '=' >> qi::int_parser();
return qi::parse(first, last, line % qi::eol >> qi::eol, token_map) && first == last;
}
static void print_parser_token(std::istream& input,
const std::string& token_path,
bool silent)
{
if(silent)
return;
std::stringstream script_stream;
script_stream << input.rdbuf();
std::string script(script_stream.str());
antlr_pointer input_stream(antlr3NewAsciiStringInPlaceStream(
reinterpret_cast(const_cast(script.c_str())),
boost::numeric_cast(script.size()),
NULL));
if(!input)
throw libbash::interpreter_exception("Unable to open the file due to malloc() failure");
antlr_pointer lexer(libbashLexerNew(input_stream.get()));
if(!lexer)
throw libbash::interpreter_exception("Unable to create the lexer due to malloc() failure");
antlr_pointer token_stream(
antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT, lexer->pLexer->rec->state->tokSource));
if(!token_stream)
throw libbash::interpreter_exception("Out of memory trying to allocate token stream");
std::unordered_map token_map;
if(build_token_map(token_map, token_path))
std::cout << bash_ast::get_parser_tokens(token_stream,
std::bind(&token_mapper,
token_map,
std::placeholders::_1))
<< std::endl;
else
std::cerr << "Building token map failed" << std::endl;
}
static void print_walker_token(std::istream& input,
const std::string& token_path,
bool silent)
{
if(silent)
return;
bash_ast ast(input);
std::unordered_map token_map;
if(build_token_map(token_map, token_path))
std::cout << ast.get_walker_tokens(std::bind(&token_mapper,
token_map,
std::placeholders::_1))
<< std::endl;
else
std::cerr << "Building token map failed" << std::endl;
}
static void print_files(const std::vector& files,
bool print_name,
std::function printer)
{
for(auto iter = files.begin(); iter != files.end(); ++iter)
{
if(print_name)
std::cout << "Interpreting " << *iter << std::endl;
std::ifstream input(iter->c_str());
printer(input);
}
}
static inline void print_expression(const std::string& expr,
std::function printer)
{
std::istringstream input(expr);
printer(input);
}
static inline void print_cin(std::function printer)
{
printer(std::cin);
}
int main(int argc, char** argv)
{
std::vector files;
std::string expr;
po::options_description desc("Allowed options");
desc.add_options()
("help,h", "produce help message")
("files,f", po::value>()->multitoken(),
"input scripts. If this option and -e are not specified, "
"will use standard input")
("expr,e", po::value(), "one line of script")
("dot,d", "print graphviz doc file instead of tree string if -s is not specified")
("token,t", po::value(), "Print all tokens instead of AST. "
"The argument is the path to libbash.tokens")
("walker-token,w", po::value(), "Print all tokens received by the walker. "
"The argument is the path to libbashWalker.tokens")
("name,n", "When using files as input scripts, print out file names")
("silent,s", "do not print any AST")
;
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if(vm.count("help"))
{
std::cout << desc << std::endl;
return EXIT_FAILURE;
}
std::function printer;
if(vm.count("token"))
printer = std::bind(&print_parser_token,
std::placeholders::_1,
vm["token"].as(),
vm.count("silent"));
else if(vm.count("walker-token"))
printer = std::bind(&print_walker_token,
std::placeholders::_1,
vm["walker-token"].as(),
vm.count("silent"));
else
printer = std::bind(&print_ast,
std::placeholders::_1,
vm.count("silent"),
vm.count("dot"));
try
{
if(vm.count("files"))
print_files(vm["files"].as>(),
vm.count("name"),
printer);
else if(vm.count("expr"))
print_expression(vm["expr"].as(), printer);
else
print_cin(printer);
}
catch(libbash::interpreter_exception& e)
{
if(!vm.count("silent"))
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}