File: parser.hpp

package info (click to toggle)
polybar 3.7.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,108 kB
  • sloc: cpp: 30,424; python: 3,750; sh: 284; makefile: 83
file content (159 lines) | stat: -rw-r--r-- 4,447 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#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 + "')");
    }

    virtual const char* what() const noexcept override {
      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();
    extent_val 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