Source

Configs / filter_python_exceptions.cpp

Full commit
// I feel slightly retarded for writing this, and in C++ of all languages

#include <vector>
#include <string>
#include <iostream>

using std::vector;
using std::string;

const int MAX_INPUT_LINE_LENGTH = 1023;
const int MAX_ERROR_LOCATIONS = 100;
const string ERROR_PREFIX = "!ERROR! ";

bool eof_encountered = false;

inline bool read(string & dest)
{
    // We are OK with reading incomplete lines, so here's a function that works
    // like that.

    dest.clear();
    if (eof_encountered) return false;
    
    for (int i = 0; i < MAX_INPUT_LINE_LENGTH; i++)
    {
        int c = getc(stdin);
        if (c == EOF)
        {
            eof_encountered = true;
            fflush(stdout);
            return dest.length() > 0;
        }
        dest += c;
        putc(c, stdout);
        if (c == '\n') break;
    }
    fflush(stdout);
    return true;
}

string trim_ws(const string & s)
{
    static const char whitespace[] = " \t\r\n";
    size_t first = s.find_first_not_of(whitespace, 0, sizeof(whitespace) - 1);
    if (first == string::npos) return "";
    size_t last = s.find_last_not_of(whitespace, string::npos, sizeof(whitespace) - 1); 
    if (last == string::npos) return "";
    return s.substr(first, last - first + 1);
}

template <int len>
inline bool begins_with_const(const string & s, const char (&prefix)[len])
{
    return s.compare(0, len - 1, prefix) == 0;
}

struct error_location
{
    string file;
    int line;
    string context;
};

inline bool consume_substring(const string & s, int & start, const string & substring)
{
    if (start + substring.length() > s.length()) return false;
    if (s.compare(start, substring.length(), substring) != 0) return false;
    start += substring.length();
    return true;
}

bool parse_error_location(string s, error_location & dest)
{
    //  File "test.py", line 3, in <module>
    int start = 0;
    if (!consume_substring(s, start, "  File \"")) return false;

    // Yes, this wouldn't work on files with quotes in names, but I'm not sure that Vim would either.
    int end = s.find_first_of("\"", start);
    if (end == string::npos) return false;
    dest.file = trim_ws(s.substr(start, end - start));
    start = end;

    if (!consume_substring(s, start, "\", line ")) return false;
    end = s.find_first_not_of("0123456789", start);

    char * end2;
    dest.line = strtol(&s[start], &end2, 10);
    if (end2 != &s[end]) return false;

    return true;
}

void print_error_msg(const error_location & loc, const string & msg)
{
    std::cout << ERROR_PREFIX << loc.file << ":" << loc.line << ": " <<
        msg << " (\"" << loc.context << "\")" << std::endl;
}

void parse_traceback(string & s)
{
    // parse error message
    vector<error_location> error_locations;
    string error_message;

    int skipped = 0;

    while (true)
    {
        if (begins_with_const(s, "    "))
        {
            // context line.
            if (!error_locations.size())
            {
                // something is really wrong.
                break;
            }

            if (!error_locations.back().context.length())
            {
                error_locations.back().context = trim_ws(s);
            }
        }
        else if (begins_with_const(s, "  "))
        {
            // location line.
            if (error_locations.size() < MAX_ERROR_LOCATIONS)
            {
                error_locations.resize(error_locations.size() + 1);
            }
            else 
            {
                error_locations.back() = error_location();
                skipped++;
            }

            if (!parse_error_location(s, error_locations.back()))
            {
                error_locations.pop_back();
                break;
            }
        }
        else
        {
            // yay, the error message itself!
            if (!error_locations.size()) break; // wat.
            error_message = trim_ws(s);
            break;
        }

        s.clear();
        if (!read(s)) break;
    }

    if (error_locations.size())
    {
        // Error parsing error locations.
        vector<error_location>::reverse_iterator it(error_locations.rbegin());
        char * rest_of_the_messages = "";

        if (error_message.length())
        {
            print_error_msg(*it, error_message);
            it++;
            rest_of_the_messages = "called from here.";
        }
        else
        {
            std::cout << ERROR_PREFIX << "Incomplete traceback (most recent call last):" << std::endl;
        }

        if (skipped)
        {
            std::cout << ERROR_PREFIX << "[" << skipped << " locations skipped]" << std::endl;
        }

        for (; it != error_locations.rend(); ++it)
        {
            print_error_msg(*it, rest_of_the_messages);
        }

        error_locations.clear();
    }
}

int main()
{
    string s;
    s.reserve(MAX_INPUT_LINE_LENGTH);
    bool firstline = true;

    if (!read(s)) return 0;

    // raw syntax errors don't get a 'Traceback' header.
    if (begins_with_const(s, "  File \""))
    {
        parse_traceback(s);
    }

    do
    {
        if (begins_with_const(s, "Traceback (most recent call last):"))
        {
            if (!read(s)) return 0;
            parse_traceback(s);
        }
    } while (read(s));

    return 0;
}