0

I want to find out whether I can detect function call using regex. The basic case is easy: somefunction(1, 2);

But what if I had code:

somefunction(someotherfunction(), someotherotherfunction());

or

somefunction(function () { return 1; }, function() {return 2;});

or

caller_function(somefunction(function () { return 1; }, function() {return 2;}))

In this case I need to match same number of opening braces and closing braces so that I can find end of call to somefunction

Is it possible?

Thanks in advance.

  • 2
    you cannot do it reliably with regex. you need to do some syntactic analysis. At least counting parenthesizes and braces. – Serge Jul 02 '17 at 02:21

1 Answers1

0

Your question is misleading. It's not as simple as you think.

First, the grammar isn't regular. Regular expressions are not the right tool.

Second, you ask "detecting function call" but the samples show anonymous function definitions, a totally different ball game.

Here's a start using Boost Spirit:

start   = skip(space) [ fcall ];
fcall   = ident >> '(' >> -args >> ')';
args    = arg % ',';
arg     = fdef | fcall;
fdef    = lexeme["function"] >> '(' >> -formals >> ')' >> body;
formals = ident % ',';

identch = alpha | char_("_");
ident   = identch >> *(identch|digit);
body    = '{' >> *~char_('}') >> '}';

Which would map onto an AST like:

struct function_definition {
    std::vector<std::string> formal_arguments;
    std::string body;
};

struct function_call;

using argument = boost::variant<
    function_definition,
    boost::recursive_wrapper<function_call>
>;

struct function_call {
    std::string name;
    std::vector<argument> args;
};

DEMO

Live On Coliru

// #define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/fusion/adapted/struct.hpp>

struct function_definition {
    std::vector<std::string> formal_arguments;
    std::string body;
};

struct function_call;

using argument = boost::variant<
    function_definition,
    boost::recursive_wrapper<function_call>
>;

struct function_call {
    std::string name;
    std::vector<argument> args;
};

BOOST_FUSION_ADAPT_STRUCT(function_call, name, args)
BOOST_FUSION_ADAPT_STRUCT(function_definition, formal_arguments, body)

namespace qi = boost::spirit::qi;

template <typename It>
struct Parser : qi::grammar<It, function_call()> {
    Parser() : Parser::base_type(start) {
        using namespace qi;

        start   = skip(space) [ fcall ];
        fcall   = ident >> '(' >> -args >> ')';
        args    = arg % ',';
        arg     = fdef | fcall;
        fdef    = lexeme["function"] >> '(' >> -formals >> ')' >> body;
        formals = ident % ',';

        identch = alpha | char_("_");
        ident   = identch >> *(identch|digit);
        body    = '{' >> *~char_('}') >> '}';

        BOOST_SPIRIT_DEBUG_NODES((start)(fcall)(args)(arg)(fdef)(formals)(ident)(body))
    }
  private:
    using Skipper = qi::space_type;
    qi::rule<It, function_call()> start;
    qi::rule<It, function_call(), Skipper> fcall;
    qi::rule<It, argument(), Skipper> arg;
    qi::rule<It, std::vector<argument>(), Skipper> args;
    qi::rule<It, function_definition(), Skipper> fdef;
    qi::rule<It, std::vector<std::string>(), Skipper> formals;

    qi::rule<It, char()> identch;
    qi::rule<It, std::string()> ident, body;
};

// for debug:
#include <experimental/iterator>
static inline std::ostream& operator<<(std::ostream& os, function_definition const& v) {
    os << "function(";
    std::copy(v.formal_arguments.begin(), v.formal_arguments.end(), std::experimental::make_ostream_joiner(os, ", "));
    return os << ") {" << v.body << "}";
}

static inline std::ostream& operator<<(std::ostream& os, function_call const& v) {
    os << v.name << "(";
    std::copy(v.args.begin(), v.args.end(), std::experimental::make_ostream_joiner(os, ", "));
    return os << ")";
}

int main() {
    std::string const input = "caller_function(somefunction(function () { return 1; }, function() {return 2;}))";
    using It = std::string::const_iterator;
    Parser<It> const p;

    It f = input.begin(), l = input.end();
    function_call parsed;
    bool ok = parse(f, l, p, parsed);
    if (ok) {
        std::cout << "Parsed ok: " << parsed << "\n";
    } else {
        std::cout << "Parse failed\n";
    }

    if (f!=l)
        std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}

Prints

Parsed ok: caller_function(somefunction(function() { return 1; }, function() {return 2;}))
sehe
  • 374,641
  • 47
  • 450
  • 633