#include #include #include #include #include namespace regexis024 { struct ParseCall{ virtual ~ParseCall() = default; virtual std::unique_ptr afterReceive(REGEX_IS024_MeaningContext& ctx) { assert(false); } virtual std::unique_ptr firstTime(REGEX_IS024_MeaningContext& ctx) { assert(false); } }; struct Top_ParseCall: public ParseCall{ Command& res; explicit Top_ParseCall(Command &res) : res(res) {} std::unique_ptr firstTime(REGEX_IS024_MeaningContext &ctx) override; std::unique_ptr afterReceive(REGEX_IS024_MeaningContext &ctx) override; }; struct Bracker_ParseCall: public ParseCall{ std::vector& res; bool closingBraceEnded = false; explicit Bracker_ParseCall(std::vector &res) : res(res) {} std::unique_ptr argReadProc(REGEX_IS024_MeaningContext& ctx); std::unique_ptr firstTime(REGEX_IS024_MeaningContext &ctx) override; std::unique_ptr afterReceive(REGEX_IS024_MeaningContext &ctx) override; }; #define call_ERROR_CHECK do { if (ctx.error) { return NULL; } } while (0) #define call_THROW(str) do { report(ctx, "command expression: " str); return NULL; } while (0) std::unique_ptr Top_ParseCall::firstTime(REGEX_IS024_MeaningContext &ctx) { assert(readChar(ctx) == U'!'); int32_t ch = peep(ctx); call_ERROR_CHECK; if (ch == U'~'){ /* I assume during construction I received reference to newly initialized struct */ res.tilda = true; return NULL; } res.name = tryRead_REGEX024_name(ctx); call_ERROR_CHECK; if (res.name.empty()) call_THROW("top lvl: no command name specified"); ch = peep(ctx); call_ERROR_CHECK; if (ch == U';'){ readChar(ctx); return NULL; } if (ch == U'{'){ return std::make_unique(res.arguments); } call_THROW("top lvl: command call should be ended with ';' or '{...}'"); } std::unique_ptr Top_ParseCall::afterReceive(REGEX_IS024_MeaningContext &ctx) { return NULL; } std::unique_ptr Bracker_ParseCall::firstTime(REGEX_IS024_MeaningContext &ctx) { assert(readChar(ctx) == U'{'); return argReadProc(ctx); } std::unique_ptr Bracker_ParseCall::afterReceive(REGEX_IS024_MeaningContext &ctx) { closingBraceEnded = true; return argReadProc(ctx); } std::unique_ptr Bracker_ParseCall::argReadProc(REGEX_IS024_MeaningContext &ctx) { repeat: int32_t ch = peep(ctx); call_ERROR_CHECK; if (ch == U';'){ res.emplace_back(); readChar(ctx); closingBraceEnded = false; goto repeat; } else if (ch == U'}'){ readChar(ctx); if (!closingBraceEnded){ res.emplace_back(); } return NULL; } else if (is_REGEX024_nameConstituent(ch)){ res.emplace_back(); res.back().is_empty = false; res.back().name = tryRead_REGEX024_name(ctx); int32_t eCh = peep(ctx); call_ERROR_CHECK; if (eCh == U';'){ readChar(ctx); closingBraceEnded = false; goto repeat; } else if (eCh == U'{'){ return std::make_unique(res.back().arguments); } else if (eCh == U'}'){ readChar(ctx); return NULL; } call_THROW("brace lvl: argument ends with ';' or {...}"); } call_THROW("brace lvl: argument starts with ';' or it's name"); } Command command_expr_parse(REGEX_IS024_MeaningContext &ctx) { std::vector> callStack; Command res; callStack.push_back(std::make_unique(res)); bool first_time = true; while (!callStack.empty()){ if (ctx.error) return {}; auto nxt = first_time ? callStack.back()->firstTime(ctx) : callStack.back()->afterReceive(ctx); if (nxt){ callStack.push_back(std::move(nxt)); first_time = true; } else { callStack.pop_back(); first_time = false; } } return res; } const char* commands_for_codesets[] = {"word", "space", "digit", "variable", "any", "A", NULL}; bool is_command_for_charset(const Command &cmd) { return !cmd.tilda && cmd.arguments.empty() && is_string_in_stringset(cmd.name.c_str(), commands_for_codesets); } void interpret_command_as_charset_giving(const CommonCodesets& cc, const Command &cmd, codeset_t& ret) { if (cmd.name == "word") ret = cc.word_constituents; else if (cmd.name == "space") ret = cc.spaces; else if (cmd.name == "digit") ret = cc.digits; else if (cmd.name == "variable") ret = cc.variable_constituents; else if (cmd.name == "any" || cmd.name == "A") ret = codeset_of_all; else assert(false); } }