commit 94cc6520490e11c3ad1e5ee37bdd17848ae9356f Author: David Date: Thu Jun 25 22:45:08 2026 +0300 Initial work on the WBT parser and its test suite diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b46da56 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 4.2) +project(wb) + +set(CMAKE_CXX_STANDARD 26) + +add_executable(wb main.cpp) +target_sources(wb + PUBLIC + FILE_SET CXX_MODULES FILES + wbt/wbt.cpp +) + +# Test Executables +add_executable(test_parse test_parse.cpp) +target_sources(test_parse + PUBLIC + FILE_SET CXX_MODULES FILES + wbt/wbt.cpp +) + +# Copy test files to build folder +add_custom_target(copy_test_files ALL + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/test + ${CMAKE_CURRENT_BINARY_DIR}/test + COMMENT "Copying test files to build directory" +) +add_dependencies(test_parse copy_test_files) + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..114bebe --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 David Gil de Gómez Pérez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..8393a13 --- /dev/null +++ b/main.cpp @@ -0,0 +1,6 @@ +import wbt; + +// WB is a site builder built from scratch +int main() { + return 0; +} \ No newline at end of file diff --git a/test/test_parse_invalid_character_expression.wbt b/test/test_parse_invalid_character_expression.wbt new file mode 100644 index 0000000..3505c6c --- /dev/null +++ b/test/test_parse_invalid_character_expression.wbt @@ -0,0 +1,6 @@ + + +

Hello World

+

{{ INVALID { EXPRESSION }}

+ + \ No newline at end of file diff --git a/test/test_parse_no_expressions.wbt b/test/test_parse_no_expressions.wbt new file mode 100644 index 0000000..da78911 --- /dev/null +++ b/test/test_parse_no_expressions.wbt @@ -0,0 +1,5 @@ + + +

Hello World

+ + \ No newline at end of file diff --git a/test/test_parse_single_expression.wbt b/test/test_parse_single_expression.wbt new file mode 100644 index 0000000..b7ce813 --- /dev/null +++ b/test/test_parse_single_expression.wbt @@ -0,0 +1,6 @@ + + +

Hello World

+

{{ VALID EXPRESSION }}

+ + \ No newline at end of file diff --git a/test/test_parse_unclosed_expression.wbt b/test/test_parse_unclosed_expression.wbt new file mode 100644 index 0000000..33967fc --- /dev/null +++ b/test/test_parse_unclosed_expression.wbt @@ -0,0 +1,6 @@ + + +

Hello World

+

{{ INVALID EXPRESSION }

+ + \ No newline at end of file diff --git a/test_parse.cpp b/test_parse.cpp new file mode 100644 index 0000000..69ab16f --- /dev/null +++ b/test_parse.cpp @@ -0,0 +1,45 @@ +#include +#include + +import wbt; + +void test_parse_no_expressions() { + std::ifstream i("test/test_parse_no_expressions.wbt", std::ios_base::in); + std::ostream o(std::cout.rdbuf()); + const WBT_Parser p(i, o); + p.parse(); +} + +void test_parse_single_expression() { + std::ifstream i("test/test_parse_single_expression.wbt", std::ios_base::in); + std::ostream o(std::cout.rdbuf()); + const WBT_Parser p(i, o); + p.parse(); +} + +void test_parse_invalid_character_expression() { + std::ifstream i("test/test_parse_invalid_character_expression.wbt", std::ios_base::in); + std::ostream o(std::cout.rdbuf()); + const WBT_Parser p(i, o); + p.parse(); +} + +void test_parse_unclosed_expression() { + std::ifstream i("test/test_parse_unclosed_expression.wbt", std::ios_base::in); + std::ostream o(std::cout.rdbuf()); + const WBT_Parser p(i, o); + p.parse(); +} + + +int main() { + std::cout << std::endl << "-----" << std::endl; + test_parse_no_expressions(); + std::cout << std::endl << "-----" << std::endl; + test_parse_single_expression(); + std::cout << std::endl << "-----" << std::endl; + test_parse_invalid_character_expression(); + std::cout << std::endl << "-----" << std::endl; + test_parse_unclosed_expression(); + std::cout << std::endl << "-----" << std::endl; +} diff --git a/wbt/wbt.cpp b/wbt/wbt.cpp new file mode 100644 index 0000000..cfa52bb --- /dev/null +++ b/wbt/wbt.cpp @@ -0,0 +1,93 @@ +module; +#include +#include + +// This module is the transpiler from WBT to HTML for site build +export module wbt; + +export enum WBT_States { + WBT_STATE_INIT, + WBT_STATE_OPEN, + WBT_STATE_EXPRESSION, + WBT_STATE_CLOSE, +}; + +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 = 1; + auto column = 1; + while (input.peek() != std::char_traits::eof()) { + char c; + input.get(c); + // State machine + switch (state) { + case WBT_STATE_INIT: + if (c == '{') { + 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++; + } + } + } +};