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