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
14typedef 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.
23typedef void (*ParserLineGetter)(void *cookie, ParserLine *ret_pline);
24
25/// Parser position in the input
26typedef 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.
32typedef 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
47typedef 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.
61typedef 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
68typedef kvec_withinit_t(ParserHighlightChunk, 16) ParserHighlight;
69
70/// Structure defining parser state
71typedef 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
84static 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.
97static 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
116static 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.
122static 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
134static 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
139static 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
161static 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.
170static 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
187static 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.
197static 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
210static 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.
222static 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