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