1 #ifndef NVIM_VIML_PARSER_PARSER_H
2 #define NVIM_VIML_PARSER_PARSER_H
3
4 #include <assert.h>
5 #include <stdbool.h>
6 #include <stddef.h>
7
8 #include "nvim/func_attr.h"
9 #include "nvim/lib/kvec.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.
viml_parser_init(ParserState * const ret_pstate,const ParserLineGetter get_line,void * const cookie,ParserHighlight * const colors)97 static inline void viml_parser_init(ParserState *const ret_pstate, const ParserLineGetter get_line,
98 void *const cookie, ParserHighlight *const colors)
99 {
100 *ret_pstate = (ParserState) {
101 .reader = {
102 .get_line = get_line,
103 .cookie = cookie,
104 .conv = MBYTE_NONE_CONV,
105 },
106 .pos = { 0, 0 },
107 .colors = colors,
108 .can_continuate = false,
109 };
110 kvi_init(ret_pstate->reader.lines);
111 kvi_init(ret_pstate->stack);
112 }
113
114 static inline void viml_parser_destroy(ParserState *const pstate)
115 REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE;
116
117 /// Free all memory allocated by the parser on heap
118 ///
119 /// @param pstate Parser state to free.
viml_parser_destroy(ParserState * const pstate)120 static inline void viml_parser_destroy(ParserState *const pstate)
121 {
122 for (size_t i = 0; i < kv_size(pstate->reader.lines); i++) {
123 ParserLine pline = kv_A(pstate->reader.lines, i);
124 if (pline.allocated) {
125 xfree((void *)pline.data);
126 }
127 }
128 kvi_destroy(pstate->reader.lines);
129 kvi_destroy(pstate->stack);
130 }
131
132 static inline void viml_preader_get_line(ParserInputReader *const preader,
133 ParserLine *const ret_pline)
134 REAL_FATTR_NONNULL_ALL;
135
136 /// Get one line from ParserInputReader
viml_preader_get_line(ParserInputReader * const preader,ParserLine * const ret_pline)137 static inline void viml_preader_get_line(ParserInputReader *const preader,
138 ParserLine *const ret_pline)
139 {
140 ParserLine pline;
141 preader->get_line(preader->cookie, &pline);
142 if (preader->conv.vc_type != CONV_NONE && pline.size) {
143 ParserLine cpline = {
144 .allocated = true,
145 .size = pline.size,
146 };
147 cpline.data = (char *)string_convert(&preader->conv,
148 (char_u *)pline.data,
149 &cpline.size);
150 if (pline.allocated) {
151 xfree((void *)pline.data);
152 }
153 pline = cpline;
154 }
155 kvi_push(preader->lines, pline);
156 *ret_pline = pline;
157 }
158
159 static inline bool viml_parser_get_remaining_line(ParserState *const pstate,
160 ParserLine *const ret_pline)
161 REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL;
162
163 /// Get currently parsed line, shifted to pstate->pos.col
164 ///
165 /// @param pstate Parser state to operate on.
166 ///
167 /// @return True if there is a line, false in case of EOF.
viml_parser_get_remaining_line(ParserState * const pstate,ParserLine * const ret_pline)168 static inline bool viml_parser_get_remaining_line(ParserState *const pstate,
169 ParserLine *const ret_pline)
170 {
171 const size_t num_lines = kv_size(pstate->reader.lines);
172 if (pstate->pos.line == num_lines) {
173 viml_preader_get_line(&pstate->reader, ret_pline);
174 } else {
175 *ret_pline = kv_last(pstate->reader.lines);
176 }
177 assert(pstate->pos.line == kv_size(pstate->reader.lines) - 1);
178 if (ret_pline->data != NULL) {
179 ret_pline->data += pstate->pos.col;
180 ret_pline->size -= pstate->pos.col;
181 }
182 return ret_pline->data != NULL;
183 }
184
185 static inline void viml_parser_advance(ParserState *const pstate,
186 const size_t len)
187 REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
188
189 /// Advance position by a given number of bytes
190 ///
191 /// At maximum advances to the next line.
192 ///
193 /// @param pstate Parser state to advance.
194 /// @param[in] len Number of bytes to advance.
viml_parser_advance(ParserState * const pstate,const size_t len)195 static inline void viml_parser_advance(ParserState *const pstate, const size_t len)
196 {
197 assert(pstate->pos.line == kv_size(pstate->reader.lines) - 1);
198 const ParserLine pline = kv_last(pstate->reader.lines);
199 if (pstate->pos.col + len >= pline.size) {
200 pstate->pos.line++;
201 pstate->pos.col = 0;
202 } else {
203 pstate->pos.col += len;
204 }
205 }
206
207 static inline void viml_parser_highlight(ParserState *const pstate,
208 const ParserPosition start,
209 const size_t end_col,
210 const char *const group)
211 REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
212
213 /// Record highlighting of some region of text
214 ///
215 /// @param pstate Parser state to work with.
216 /// @param[in] start Start position of the highlight.
217 /// @param[in] len Highlighting chunk length.
218 /// @param[in] group Highlight group.
viml_parser_highlight(ParserState * const pstate,const ParserPosition start,const size_t len,const char * const group)219 static inline void viml_parser_highlight(ParserState *const pstate, const ParserPosition start,
220 const size_t len, const char *const group)
221 {
222 if (pstate->colors == NULL || len == 0) {
223 return;
224 }
225 assert(kv_size(*pstate->colors) == 0
226 || kv_Z(*pstate->colors, 0).start.line < start.line
227 || kv_Z(*pstate->colors, 0).end_col <= start.col);
228 kvi_push(*pstate->colors, ((ParserHighlightChunk) {
229 .start = start,
230 .end_col = start.col + len,
231 .group = group,
232 }));
233 }
234
235 #ifdef INCLUDE_GENERATED_DECLARATIONS
236 # include "viml/parser/parser.h.generated.h"
237 #endif
238
239 #endif // NVIM_VIML_PARSER_PARSER_H
240