/*
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 bash_ast.cpp
/// \brief a wrapper class that helps interpret from istream and string
///
#include "core/bash_ast.h"
#include
#include
#include
#include
#include
#include
#include "core/interpreter.h"
#include "exceptions.h"
#include "libbashLexer.h"
#include "libbashParser.h"
#include "libbashWalker.h"
void bash_ast::read_script(const std::istream& source, bool trim)
{
std::stringstream stream;
stream << source.rdbuf();
script = stream.str();
boost::algorithm::erase_all(script, "\\\n");
if(trim)
boost::trim_if(script, boost::is_any_of(" \t\n"));
}
bash_ast::bash_ast(const std::istream& source,
std::function p,
bool trim): parse(p)
{
read_script(source, trim);
init_parser("unknown source");
}
bash_ast::bash_ast(const std::string& script_path,
std::function p,
bool trim): parse(p)
{
std::stringstream stream;
std::ifstream file_stream(script_path);
if(!file_stream)
throw libbash::parse_exception(script_path + " can't be read");
read_script(file_stream, trim);
init_parser(script_path);
}
namespace
{
std::mutex string_mutex;
pANTLR3_STRING locked_newRaw8(pANTLR3_STRING_FACTORY factory)
{
std::lock_guard l(string_mutex);
pANTLR3_STRING_FACTORY pristine = antlr3StringFactoryNew();
pANTLR3_STRING result = pristine->newRaw(factory);
pristine->close(pristine);
return result;
}
void locked_destroy(pANTLR3_STRING_FACTORY factory, pANTLR3_STRING string)
{
std::lock_guard l(string_mutex);
pANTLR3_STRING_FACTORY pristine = antlr3StringFactoryNew();
pristine->destroy(factory, string);
pristine->close(pristine);
}
}
void bash_ast::init_parser(const std::string& script_path)
{
input.reset(antlr3NewAsciiStringInPlaceStream(
reinterpret_cast(const_cast(script.c_str())),
// We do not support strings longer than the max value of ANTLR3_UNIT32
boost::numeric_cast(script.size()),
NULL));
if(!input)
throw libbash::parse_exception("Unable to open file " + script + " due to malloc() failure");
input->fileName = input->strFactory->newStr(
input->strFactory,
reinterpret_cast(const_cast(script_path.c_str())));
lexer.reset(libbashLexerNew(input.get()));
if(!lexer)
throw libbash::parse_exception("Unable to create the lexer due to malloc() failure");
token_stream.reset(antlr3CommonTokenStreamSourceNew(
ANTLR3_SIZE_HINT, lexer->pLexer->rec->state->tokSource));
if(!token_stream)
throw libbash::parse_exception("Out of memory trying to allocate token stream");
parser.reset(libbashParserNew(token_stream.get()));
if(!parser)
throw libbash::parse_exception("Out of memory trying to allocate parser");
ast = parse(parser.get());
ast->strFactory->newRaw = &locked_newRaw8;
ast->strFactory->destroy = &locked_destroy;
if(parser->pParser->rec->getNumberOfSyntaxErrors(parser->pParser->rec))
throw libbash::parse_exception("Something wrong happened while parsing");
}
std::string bash_ast::get_dot_graph()
{
antlr_pointer nodes(
antlr3CommonTreeNodeStreamNewTree(ast, ANTLR3_SIZE_HINT));
pANTLR3_STRING graph = nodes->adaptor->makeDot(nodes->adaptor, ast);
return std::string(reinterpret_cast(graph->chars));
}
std::string bash_ast::get_string_tree()
{
return std::string(reinterpret_cast(ast->toStringTree(ast)->chars));
}
namespace
{
void print_line_counter(std::stringstream& result,
pANTLR3_COMMON_TOKEN token,
int& line_counter,
int pos)
{
char* text = reinterpret_cast(token->getText(token)->chars);
for(int i = pos; text[i] == '\n'; ++i)
result << '\n' << line_counter++ << "\t";
}
}
std::string bash_ast::get_parser_tokens(antlr_pointer& token_stream,
std::function token_map)
{
std::stringstream result;
int line_counter = 1;
// output line number for the first line
result << line_counter++ << "\t";
pANTLR3_VECTOR token_list = token_stream->getTokens(token_stream.get());
unsigned token_size = token_list->size(token_list);
for(unsigned i = 0u; i != token_size; ++i)
{
pANTLR3_COMMON_TOKEN token = reinterpret_cast
(token_list->get(token_list, i));
std::string tokenName = token_map(token->getType(token));
if(tokenName != "EOL" && tokenName != "COMMENT" && tokenName != "CONTINUE_LINE")
{
result << tokenName << " ";
}
// Output \n and line number before each COMMENT token for better readability
else if(tokenName == "COMMENT")
{
print_line_counter(result, token, line_counter, 0);
result << tokenName;
}
// Output \n and line number after each CONTINUE_LINE/EOL token for better readability
// omit the last \n and line number
else if(i + 1 != token_size)
{
result << tokenName;
print_line_counter(result, token, line_counter, tokenName == "CONTINUE_LINE"? 1 : 0);
}
}
return result.str();
}
std::string bash_ast::get_walker_tokens(std::function token_map)
{
std::stringstream result;
antlr_pointer nodes(
antlr3CommonTreeNodeStreamNewTree(ast, ANTLR3_SIZE_HINT));
pANTLR3_INT_STREAM istream = nodes->tnstream->istream;
auto istream_size = istream->size(istream);
for(ANTLR3_UINT32 i = 1; i <= istream_size; ++i)
{
ANTLR3_UINT32 token = istream->_LA(istream, boost::numeric_cast(i));
if(token == 2)
result << "DOWN ";
else if(token == 3)
result << "UP ";
else
result << token_map(istream->_LA(istream, boost::numeric_cast(i))) << " ";
}
result << std::endl;
return result.str();
}
void bash_ast::walker_start(plibbashWalker tree_parser)
{
tree_parser->start(tree_parser);
}
long bash_ast::walker_arithmetics(plibbashWalker tree_parser)
{
return tree_parser->arithmetics(tree_parser);
}
std::string bash_ast::walker_string_expr(libbashWalker_Ctx_struct* tree_parser)
{
return tree_parser->string_expr(tree_parser).libbash_value;
}
pANTLR3_BASE_TREE bash_ast::parser_start(plibbashParser parser)
{
return parser->start(parser).tree;
}
pANTLR3_BASE_TREE bash_ast::parser_arithmetics(plibbashParser parser)
{
return parser->arithmetics(parser).tree;
}
pANTLR3_BASE_TREE bash_ast::parser_all_expansions(libbashParser_Ctx_struct* parser)
{
return parser->all_expansions(parser).tree;
}
pANTLR3_BASE_TREE bash_ast::parser_builtin_variable_definitions(libbashParser_Ctx_struct* parser, bool local)
{
return parser->builtin_variable_definitions(parser, local).tree;
}
void bash_ast::call_function(plibbashWalker ctx,
ANTLR3_MARKER index)
{
auto INPUT = ctx->pTreeParser->ctnstream;
// Initialize the input stream
auto ISTREAM = ctx->pTreeParser->ctnstream->tnstream->istream;
ISTREAM->size(ISTREAM);
// Push function index into INPUT
// The actual type of ANTLR3_MARKER is ANTLR3_INT32
INPUT->push(INPUT, boost::numeric_cast(index));
// Execute function body
ctx->compound_command(ctx);
}
bash_ast::walker_pointer bash_ast::create_walker(interpreter& walker,
antlr_pointer& nodes)
{
set_interpreter(&walker);
walker.push_current_ast(this);
auto deleter = [&](plibbashWalker tree_parser)
{
tree_parser->free(tree_parser);
walker.pop_current_ast();
};
return walker_pointer(libbashWalkerNew(nodes.get()), deleter);
}