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