2020-12-17 19:37:28 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "common.hpp"
|
|
|
|
#include "errors.hpp"
|
|
|
|
#include "tags/types.hpp"
|
|
|
|
|
|
|
|
POLYBAR_NS
|
|
|
|
|
|
|
|
namespace tags {
|
|
|
|
|
|
|
|
static constexpr char EOL = '\0';
|
|
|
|
|
|
|
|
class error : public application_error {
|
|
|
|
public:
|
|
|
|
using application_error::application_error;
|
|
|
|
|
|
|
|
explicit error(const string& msg) : application_error(msg), msg(msg) {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Context string that contains the text region where the parser error
|
|
|
|
* happened.
|
|
|
|
*/
|
|
|
|
void set_context(const string& ctxt) {
|
|
|
|
msg.append(" (Context: '" + ctxt + "')");
|
|
|
|
}
|
|
|
|
|
2021-01-04 09:38:43 +00:00
|
|
|
virtual const char* what() const noexcept override {
|
2020-12-17 19:37:28 +00:00
|
|
|
return msg.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
string msg;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define DEFINE_INVALID_ERROR(class_name, name) \
|
|
|
|
class class_name : public error { \
|
|
|
|
public: \
|
|
|
|
explicit class_name(const string& val) : error("Invalid " name ": '" + val + "'") {} \
|
|
|
|
explicit class_name(const string& val, const string& what) \
|
|
|
|
: error("Invalid " name ": '" + val + "' (reason: '" + what + "')") {} \
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_INVALID_ERROR(color_error, "color");
|
|
|
|
DEFINE_INVALID_ERROR(font_error, "font index");
|
|
|
|
DEFINE_INVALID_ERROR(control_error, "control tag");
|
|
|
|
DEFINE_INVALID_ERROR(offset_error, "offset");
|
|
|
|
DEFINE_INVALID_ERROR(btn_error, "button id");
|
|
|
|
#undef DEFINE_INVALID_ERROR
|
|
|
|
|
|
|
|
class token_error : public error {
|
|
|
|
public:
|
|
|
|
explicit token_error(char token, char expected) : token_error(string{token}, string{expected}) {}
|
|
|
|
explicit token_error(char token, const string& expected) : token_error(string{token}, expected) {}
|
|
|
|
explicit token_error(const string& token, const string& expected)
|
|
|
|
: error("Expected '" + expected + "' but found '" +
|
|
|
|
(token.size() == 1 && token.at(0) == EOL ? "<End Of Line>" : token) + "'") {}
|
|
|
|
};
|
|
|
|
|
|
|
|
class unrecognized_tag : public error {
|
|
|
|
public:
|
|
|
|
explicit unrecognized_tag(char tag) : error("Unrecognized formatting tag '%{" + string{tag} + "}'") {}
|
|
|
|
};
|
|
|
|
|
|
|
|
class unrecognized_attr : public error {
|
|
|
|
public:
|
|
|
|
explicit unrecognized_attr(char attr) : error("Unrecognized attribute '" + string{attr} + "'") {}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Thrown when we expect the end of a tag (either } or a space in a compound
|
|
|
|
* tag.
|
|
|
|
*/
|
|
|
|
class tag_end_error : public error {
|
|
|
|
public:
|
|
|
|
explicit tag_end_error(char token)
|
|
|
|
: error("Expected the end of a tag ('}' or ' ') but found '" +
|
|
|
|
(token == EOL ? "<End Of Line>" : string{token}) + "'") {}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recursive-descent parser for polybar's formatting tags.
|
|
|
|
*
|
|
|
|
* An input string is parsed into a list of elements, each element is either
|
|
|
|
* a piece of text or a single formatting tag.
|
|
|
|
*
|
|
|
|
* The elements can either be retrieved one-by-one with next_element() or all
|
|
|
|
* at once with parse().
|
|
|
|
*/
|
|
|
|
class parser {
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* Resets the parser state and sets the new string to parse
|
|
|
|
*/
|
|
|
|
void set(const string&& input);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether a call to next_element() suceeds.
|
|
|
|
*/
|
|
|
|
bool has_next_element();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses at least one element (if available) and returns the first parsed
|
|
|
|
* element.
|
|
|
|
*/
|
|
|
|
element next_element();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses the remaining string and returns all parsed elements.
|
|
|
|
*/
|
|
|
|
format_string parse();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void parse_step();
|
|
|
|
|
|
|
|
bool has_next() const;
|
|
|
|
char next();
|
|
|
|
char peek() const;
|
|
|
|
void revert();
|
|
|
|
|
|
|
|
void consume(char c);
|
|
|
|
void consume_space();
|
|
|
|
|
|
|
|
void parse_tag();
|
|
|
|
|
|
|
|
void parse_single_tag_content();
|
|
|
|
|
|
|
|
color_value parse_color();
|
|
|
|
int parse_fontindex();
|
|
|
|
int parse_offset();
|
|
|
|
controltag parse_control();
|
|
|
|
std::pair<action_value, string> parse_action();
|
|
|
|
mousebtn parse_action_btn();
|
|
|
|
string parse_action_cmd();
|
|
|
|
attribute parse_attribute();
|
|
|
|
|
|
|
|
void push_char(char c);
|
|
|
|
void push_text(string&& text);
|
|
|
|
|
|
|
|
string get_tag_value();
|
|
|
|
|
|
|
|
private:
|
|
|
|
string input;
|
|
|
|
size_t pos = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Buffers elements that have been parsed but not yet returned to the user.
|
|
|
|
*/
|
|
|
|
format_string buf{};
|
|
|
|
/**
|
|
|
|
* Index into buf so that we don't have to call vector.erase everytime.
|
|
|
|
*
|
|
|
|
* Only buf[buf_pos, buf.end()) contain valid elements
|
|
|
|
*/
|
|
|
|
size_t buf_pos;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace tags
|
|
|
|
|
|
|
|
POLYBAR_NS_END
|