1 | #ifndef NVIM_VIML_PARSER_PARSER_H |
2 | #define NVIM_VIML_PARSER_PARSER_H |
3 | |
4 | #include <stdbool.h> |
5 | #include <stddef.h> |
6 | #include <assert.h> |
7 | |
8 | #include "nvim/lib/kvec.h" |
9 | #include "nvim/func_attr.h" |
10 | #include "nvim/mbyte.h" |
11 | #include "nvim/memory.h" |
12 | |
13 | /// One parsed line |
14 | typedef struct { |
15 | const char *data; ///< Parsed line pointer |
16 | size_t size; ///< Parsed line size |
17 | bool allocated; ///< True if line may be freed. |
18 | } ParserLine; |
19 | |
20 | /// Line getter type for parser |
21 | /// |
22 | /// Line getter must return {NULL, 0} for EOF. |
23 | typedef void (*ParserLineGetter)(void *cookie, ParserLine *ret_pline); |
24 | |
25 | /// Parser position in the input |
26 | typedef struct { |
27 | size_t line; ///< Line index in ParserInputReader.lines. |
28 | size_t col; ///< Byte index in the line. |
29 | } ParserPosition; |
30 | |
31 | /// Parser state item. |
32 | typedef struct { |
33 | enum { |
34 | kPTopStateParsingCommand = 0, |
35 | kPTopStateParsingExpression, |
36 | } type; |
37 | union { |
38 | struct { |
39 | enum { |
40 | kExprUnknown = 0, |
41 | } type; |
42 | } expr; |
43 | } data; |
44 | } ParserStateItem; |
45 | |
46 | /// Structure defining input reader |
47 | typedef struct { |
48 | /// Function used to get next line. |
49 | ParserLineGetter get_line; |
50 | /// Data for get_line function. |
51 | void *cookie; |
52 | /// All lines obtained by get_line. |
53 | kvec_withinit_t(ParserLine, 4) lines; |
54 | /// Conversion, for :scriptencoding. |
55 | vimconv_T conv; |
56 | } ParserInputReader; |
57 | |
58 | /// Highlighted region definition |
59 | /// |
60 | /// Note: one chunk may highlight only one line. |
61 | typedef struct { |
62 | ParserPosition start; ///< Start of the highlight: line and column. |
63 | size_t end_col; ///< End column, points to the start of the next character. |
64 | const char *group; ///< Highlight group. |
65 | } ParserHighlightChunk; |
66 | |
67 | /// Highlighting defined by a parser |
68 | typedef kvec_withinit_t(ParserHighlightChunk, 16) ParserHighlight; |
69 | |
70 | /// Structure defining parser state |
71 | typedef struct { |
72 | /// Line reader. |
73 | ParserInputReader reader; |
74 | /// Position up to which input was parsed. |
75 | ParserPosition pos; |
76 | /// Parser state stack. |
77 | kvec_withinit_t(ParserStateItem, 16) stack; |
78 | /// Highlighting support. |
79 | ParserHighlight *colors; |
80 | /// True if line continuation can be used. |
81 | bool can_continuate; |
82 | } ParserState; |
83 | |
84 | static inline void viml_parser_init( |
85 | ParserState *const ret_pstate, |
86 | const ParserLineGetter get_line, void *const cookie, |
87 | ParserHighlight *const colors) |
88 | REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1, 2); |
89 | |
90 | /// Initialize a new parser state instance |
91 | /// |
92 | /// @param[out] ret_pstate Parser state to initialize. |
93 | /// @param[in] get_line Line getter function. |
94 | /// @param[in] cookie Argument for the get_line function. |
95 | /// @param[in] colors Where to save highlighting. May be NULL if it is not |
96 | /// needed. |
97 | static inline void viml_parser_init( |
98 | ParserState *const ret_pstate, |
99 | const ParserLineGetter get_line, void *const cookie, |
100 | ParserHighlight *const colors) |
101 | { |
102 | *ret_pstate = (ParserState) { |
103 | .reader = { |
104 | .get_line = get_line, |
105 | .cookie = cookie, |
106 | .conv = MBYTE_NONE_CONV, |
107 | }, |
108 | .pos = { 0, 0 }, |
109 | .colors = colors, |
110 | .can_continuate = false, |
111 | }; |
112 | kvi_init(ret_pstate->reader.lines); |
113 | kvi_init(ret_pstate->stack); |
114 | } |
115 | |
116 | static inline void viml_parser_destroy(ParserState *const pstate) |
117 | REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE; |
118 | |
119 | /// Free all memory allocated by the parser on heap |
120 | /// |
121 | /// @param pstate Parser state to free. |
122 | static inline void viml_parser_destroy(ParserState *const pstate) |
123 | { |
124 | for (size_t i = 0; i < kv_size(pstate->reader.lines); i++) { |
125 | ParserLine pline = kv_A(pstate->reader.lines, i); |
126 | if (pline.allocated) { |
127 | xfree((void *)pline.data); |
128 | } |
129 | } |
130 | kvi_destroy(pstate->reader.lines); |
131 | kvi_destroy(pstate->stack); |
132 | } |
133 | |
134 | static inline void viml_preader_get_line(ParserInputReader *const preader, |
135 | ParserLine *const ret_pline) |
136 | REAL_FATTR_NONNULL_ALL; |
137 | |
138 | /// Get one line from ParserInputReader |
139 | static inline void viml_preader_get_line(ParserInputReader *const preader, |
140 | ParserLine *const ret_pline) |
141 | { |
142 | ParserLine pline; |
143 | preader->get_line(preader->cookie, &pline); |
144 | if (preader->conv.vc_type != CONV_NONE && pline.size) { |
145 | ParserLine cpline = { |
146 | .allocated = true, |
147 | .size = pline.size, |
148 | }; |
149 | cpline.data = (char *)string_convert(&preader->conv, |
150 | (char_u *)pline.data, |
151 | &cpline.size); |
152 | if (pline.allocated) { |
153 | xfree((void *)pline.data); |
154 | } |
155 | pline = cpline; |
156 | } |
157 | kvi_push(preader->lines, pline); |
158 | *ret_pline = pline; |
159 | } |
160 | |
161 | static inline bool viml_parser_get_remaining_line(ParserState *const pstate, |
162 | ParserLine *const ret_pline) |
163 | REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; |
164 | |
165 | /// Get currently parsed line, shifted to pstate->pos.col |
166 | /// |
167 | /// @param pstate Parser state to operate on. |
168 | /// |
169 | /// @return True if there is a line, false in case of EOF. |
170 | static inline bool viml_parser_get_remaining_line(ParserState *const pstate, |
171 | ParserLine *const ret_pline) |
172 | { |
173 | const size_t num_lines = kv_size(pstate->reader.lines); |
174 | if (pstate->pos.line == num_lines) { |
175 | viml_preader_get_line(&pstate->reader, ret_pline); |
176 | } else { |
177 | *ret_pline = kv_last(pstate->reader.lines); |
178 | } |
179 | assert(pstate->pos.line == kv_size(pstate->reader.lines) - 1); |
180 | if (ret_pline->data != NULL) { |
181 | ret_pline->data += pstate->pos.col; |
182 | ret_pline->size -= pstate->pos.col; |
183 | } |
184 | return ret_pline->data != NULL; |
185 | } |
186 | |
187 | static inline void viml_parser_advance(ParserState *const pstate, |
188 | const size_t len) |
189 | REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; |
190 | |
191 | /// Advance position by a given number of bytes |
192 | /// |
193 | /// At maximum advances to the next line. |
194 | /// |
195 | /// @param pstate Parser state to advance. |
196 | /// @param[in] len Number of bytes to advance. |
197 | static inline void viml_parser_advance(ParserState *const pstate, |
198 | const size_t len) |
199 | { |
200 | assert(pstate->pos.line == kv_size(pstate->reader.lines) - 1); |
201 | const ParserLine pline = kv_last(pstate->reader.lines); |
202 | if (pstate->pos.col + len >= pline.size) { |
203 | pstate->pos.line++; |
204 | pstate->pos.col = 0; |
205 | } else { |
206 | pstate->pos.col += len; |
207 | } |
208 | } |
209 | |
210 | static inline void viml_parser_highlight(ParserState *const pstate, |
211 | const ParserPosition start, |
212 | const size_t end_col, |
213 | const char *const group) |
214 | REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; |
215 | |
216 | /// Record highlighting of some region of text |
217 | /// |
218 | /// @param pstate Parser state to work with. |
219 | /// @param[in] start Start position of the highlight. |
220 | /// @param[in] len Highlighting chunk length. |
221 | /// @param[in] group Highlight group. |
222 | static inline void viml_parser_highlight(ParserState *const pstate, |
223 | const ParserPosition start, |
224 | const size_t len, |
225 | const char *const group) |
226 | { |
227 | if (pstate->colors == NULL || len == 0) { |
228 | return; |
229 | } |
230 | assert(kv_size(*pstate->colors) == 0 |
231 | || kv_Z(*pstate->colors, 0).start.line < start.line |
232 | || kv_Z(*pstate->colors, 0).end_col <= start.col); |
233 | kvi_push(*pstate->colors, ((ParserHighlightChunk) { |
234 | .start = start, |
235 | .end_col = start.col + len, |
236 | .group = group, |
237 | })); |
238 | } |
239 | |
240 | #ifdef INCLUDE_GENERATED_DECLARATIONS |
241 | # include "viml/parser/parser.h.generated.h" |
242 | #endif |
243 | |
244 | #endif // NVIM_VIML_PARSER_PARSER_H |
245 | |