1#pragma once
2
3#ifdef __cplusplus
4extern "C" {
5#endif
6
7#include <pw.h>
8
9#define MW_MAX_RECURSION_DEPTH 100
10
11#define MW_COMMENT '#'
12
13typedef struct {
14 // parser status
15 unsigned line_number; // 1-based
16 unsigned position; // 1-based
17} MwStatusData;
18
19#define _mw_status_data_ptr(value) ((MwStatusData*) _pw_get_struct_ptr((value), PwTypeId_MwStatus))
20
21
22extern uint16_t PwTypeId_MwStatus;
23/*
24 * Type ID for MwStatus value.
25 */
26
27/*
28 * MW error codes
29 */
30extern uint16_t MW_END_OF_BLOCK; // for internal use
31extern uint16_t MW_PARSE_ERROR;
32
33typedef struct {
34 _PwValue markup;
35 _PwValue current_line;
36 unsigned current_indent; // measured indentation of current line
37 unsigned line_number;
38 unsigned block_indent; // indent of current block
39 unsigned blocklevel; // recursion level
40 unsigned max_blocklevel;
41 unsigned json_depth; // recursion level for JSON
42 unsigned max_json_depth;
43 bool skip_comments; // initially true to skip leading comments in the block
44 bool eof;
45 _PwValue custom_parsers;
46} MwParser;
47
48
49MwParser* mw_create_parser(PwValuePtr markup);
50/*
51 * Create parser for `markup` which can be either File, StringIO, or any other value
52 * that supports line reader interface. See PetWay library.
53 *
54 * This function invokes pw_start_read_lines for markup.
55 *
56 * Return parser on success or nullptr if out of memory.
57 */
58
59void mw_delete_parser(MwParser** parser_ptr);
60/*
61 * Delete parser. The format of the argument is natural for gnu::cleanup attribute.
62 */
63
64typedef bool (*MwBlockParserFunc)(MwParser* parser, PwValuePtr result);
65
66[[nodiscard]] bool mw_set_custom_parser(MwParser* parser, char* convspec, MwBlockParserFunc parser_func);
67/*
68 * Set custom parser function for `convspec`.
69 */
70
71[[nodiscard]] bool mw_parse(PwValuePtr markup, PwValuePtr result);
72/*
73 * Parse `markup`.
74 *
75 * Return parsed value or error.
76 */
77
78[[nodiscard]] bool mw_parse_json(PwValuePtr markup, PwValuePtr result);
79/*
80 * Parse `markup` as pure JSON.
81 *
82 * Return parsed value or error.
83 */
84
85[[nodiscard]] bool _mw_json_parser_func(MwParser* parser, PwValuePtr result);
86/*
87 * JSON parser function for MW :json: conversion specifier.
88 */
89
90[[nodiscard]] bool _mw_read_block_line(MwParser* parser);
91/*
92 * Read line belonging to a block, until indent is less than `block_indent`.
93 * Skip comments with indentation less than `block_indent`.
94 *
95 * Return success if line is read, MW_END_OF_BLOCK if there's no more lines
96 * in the block, or any other error.
97 */
98
99bool _mw_end_of_block();
100/*
101 * Return true if current_task->status is MW_END_OF_BLOCK
102 */
103
104[[nodiscard]] bool _mw_read_block(MwParser* parser, PwValuePtr result);
105/*
106 * Read lines starting from current_line till the end of block.
107 */
108
109unsigned _mw_get_start_position(MwParser* parser);
110/*
111 * Return position of the first non-space character in the current block.
112 * The block may start inside `current_line` for nested values of list or map.
113 */
114
115bool _mw_comment_or_end_of_line(MwParser* parser, unsigned position);
116/*
117 * Check if current line ends at position or contains comment.
118 */
119
120_PwValue _mw_parser_error(MwParser* parser, char* source_file_name, unsigned source_line_number,
121 unsigned line_number, unsigned char_pos, char* description, ...);
122/*
123 * Set error in parser->status and return MW_PARSE_ERROR.
124 */
125
126#define mw_parser_error2(parser, line_number, char_pos, description, ...) \
127 _mw_parser_error((parser), __FILE__, __LINE__, (line_number), \
128 (char_pos), (description) __VA_OPT__(,) __VA_ARGS__)
129
130#define mw_parser_error(parser, char_pos, description, ...) \
131 mw_parser_error2((parser), (parser)->line_number, \
132 (char_pos), (description) __VA_OPT__(,) __VA_ARGS__)
133
134bool _mw_find_closing_quote(PwValuePtr line, char32_t quote, unsigned start_pos, unsigned* end_pos);
135/*
136 * Search for closing quotation mark in escaped line.
137 * If found, write its position to `end_pos` and return true;
138 */
139
140[[nodiscard]] bool _mw_unescape_line(MwParser* parser, PwValuePtr line, unsigned line_number,
141 char32_t quote, unsigned start_pos, unsigned end_pos, PwValuePtr result);
142/*
143 * Process escaped characters in the `line` from `start_pos` to `end_pos`.
144 */
145
146[[nodiscard]] bool _mw_parse_number(MwParser* parser, unsigned start_pos, int sign,
147 unsigned* end_pos, char32_t* allowed_terminators, PwValuePtr result);
148/*
149 * Parse number, either integer or float.
150 * `start_pos` points to the first digit in the `current_line`.
151 *
152 * Leading zeros in a non-zero decimal numbers are not allowed to avoid ambiguity.
153 *
154 * Optional single quote (') or underscores can be used as separators.
155 *
156 * Return numeric value on success. Set `end_pos` to a point where conversion has stopped.
157 */
158
159[[nodiscard]] bool _mw_parse_json_value(MwParser* parser, unsigned start_pos, unsigned* end_pos, PwValuePtr result);
160/*
161 * Parse JSON value starting from `start_pos`.
162 * On success write position where parsing stopped to `end_pos`.
163 */
164
165#ifdef __cplusplus
166}
167#endif