module; #include #include // This module is the transpiler from WBT to HTML for site build export module wbt; enum WBT_States { WBT_STATE_INIT, WBT_STATE_OPEN, WBT_STATE_EXPRESSION, WBT_STATE_CLOSE, }; struct WBT_Parser_Internal_Data { int32_t line = 1; int32_t column = 1; int32_t expr_start_line = 1; int32_t expr_start_column = 1; }; export class WBT_Parser { std::istream& input; std::ostream& output; public: explicit WBT_Parser( std::istream& i, std::ostream& o ) : input(i), output(o) { } void output_char(const char c) const { output << c; } static void error(const std::string& msg, const int line, const int column) { std::cerr << "Error (" << line << "," << column << "): " << msg << std::endl; } void eval(const std::string& expr) const { output << "[EXPR: " << expr << "]"; } void parse() const { auto state = WBT_STATE_INIT; std::string expr; auto [line, column, expr_start_line, expr_start_column] = WBT_Parser_Internal_Data(); while (input.peek() != std::char_traits::eof()) { char c; input.get(c); // State machine switch (state) { case WBT_STATE_INIT: if (c == '{') { expr_start_line = line; expr_start_column = column; state = WBT_STATE_OPEN; } else { output_char(c); } break; case WBT_STATE_OPEN: if (c == '{') { state = WBT_STATE_EXPRESSION; } else { output_char('{'); output_char(c); state = WBT_STATE_INIT; } break; case WBT_STATE_EXPRESSION: if (c == '}') { state = WBT_STATE_CLOSE; } else if (c == '{') { error("Invalid character inside of expression", line, column); return; } else { expr += c; } break; case WBT_STATE_CLOSE: if (c == '}') { eval(expr); expr.clear(); state = WBT_STATE_INIT; } else { error("Unclosed expression", line, column); return; } break; } if (c == '\n') { line++; column = 1; } else { column++; } } // Final state cannot be WBT_STATE_EXPRESSION if (state == WBT_STATE_EXPRESSION) { error("Unclosed expression", expr_start_line, expr_start_column); } } };