Changes
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 924a0b1..05f3fe2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,7 +37,7 @@ include_directories(include)
# but for now we will just list them manually or add a library target.
# Let's create a core library for the logic to share between main and tests.
-add_library(macrodown_lib STATIC src/lib_placeholder.cpp src/macro_engine.cpp src/parser.cpp src/block_parser.cpp src/converter.cpp src/standard_library.cpp)
+add_library(macrodown_lib STATIC src/macro_engine.cpp src/parser.cpp src/block_parser.cpp src/converter.cpp src/standard_library.cpp)
target_include_directories(macrodown_lib PUBLIC include)
target_link_libraries(macrodown_lib PUBLIC uni-algo::uni-algo)
diff --git a/design.md b/design.md
index df10187..aa7d541 100644
--- a/design.md
+++ b/design.md
@@ -177,4 +177,15 @@ The system will boot with a "Prelude" of defined macros to support Markdown feat
* **Functions**:
* `camelCase` for multi-word names (e.g., `evaluateMacro`).
* `lower case` for single-word names (e.g., `type()`, `evaluate()`).
-
+* **Indentation**: Indent by 4 spaces. Left brace in new line.
+ (Unless there is nothing inside the brace.)
+* **Space before parenthesis: no space.
+ Example:
+ ```c++
+ if(...)
+ {
+ ...;
+ }
+
+ void f() {}
+ ```
diff --git a/include/block.h b/include/block.h
index 3645462..12c249b 100644
--- a/include/block.h
+++ b/include/block.h
@@ -5,9 +5,11 @@
#include <vector>
#include <memory>
-namespace macrodown {
+namespace macrodown
+{
-enum class BlockType {
+enum class BlockType
+{
Document,
Quote,
List,
@@ -20,7 +22,8 @@ enum class BlockType {
ThematicBreak
};
-struct Block {
+struct Block
+{
BlockType type;
std::vector<std::unique_ptr<Block>> children; // For container blocks
std::string literal_content; // For leaf blocks
@@ -32,4 +35,4 @@ struct Block {
} // namespace macrodown
-#endif // MACRODOWN_BLOCK_H
+#endif // MACRODOWN_BLOCK_H
\ No newline at end of file
diff --git a/include/block_parser.h b/include/block_parser.h
index 4a59447..779c2f1 100644
--- a/include/block_parser.h
+++ b/include/block_parser.h
@@ -6,15 +6,18 @@
#include <memory>
#include "block.h"
-namespace macrodown {
+namespace macrodown
+{
-class BlockParser {
+class BlockParser
+{
public:
// Main entry point: parses the entire document into a Block tree
static std::unique_ptr<Block> parse(const std::string& input);
private:
- struct BlockStackItem {
+ struct BlockStackItem
+ {
Block* block;
// Parsing state specific to this block type could go here
};
@@ -34,4 +37,4 @@ private:
} // namespace macrodown
-#endif // MACRODOWN_BLOCK_PARSER_H
+#endif // MACRODOWN_BLOCK_PARSER_H
\ No newline at end of file
diff --git a/include/converter.h b/include/converter.h
index 5ff3d48..f5adb52 100644
--- a/include/converter.h
+++ b/include/converter.h
@@ -6,9 +6,11 @@
#include "block.h"
#include "nodes.h"
-namespace macrodown {
+namespace macrodown
+{
-class Converter {
+class Converter
+{
public:
// Converts a Block tree into a list of AST Nodes (Macros)
static std::vector<std::unique_ptr<Node>> convert(const Block* root);
@@ -19,4 +21,4 @@ private:
} // namespace macrodown
-#endif // MACRODOWN_CONVERTER_H
+#endif // MACRODOWN_CONVERTER_H
\ No newline at end of file
diff --git a/include/macro_engine.h b/include/macro_engine.h
index 8f81a15..ae4882a 100644
--- a/include/macro_engine.h
+++ b/include/macro_engine.h
@@ -8,11 +8,13 @@
#include <functional>
#include "nodes.h"
-namespace macrodown {
+namespace macrodown
+{
using MacroCallback = std::function<std::string(const std::vector<std::string>&)>;
-struct MacroDefinition {
+struct MacroDefinition
+{
std::string name;
std::vector<std::string> arg_names;
std::string body; // Raw body for user-defined macros
@@ -30,7 +32,8 @@ struct MacroDefinition {
: name(std::move(n)), callback(std::move(cb)), is_intrinsic(true) {}
};
-class Evaluator {
+class Evaluator
+{
public:
Evaluator();
@@ -46,4 +49,4 @@ private:
} // namespace macrodown
-#endif // MACRODOWN_MACRO_ENGINE_H
+#endif // MACRODOWN_MACRO_ENGINE_H
\ No newline at end of file
diff --git a/include/nodes.h b/include/nodes.h
index 0477dde..3b44d2f 100644
--- a/include/nodes.h
+++ b/include/nodes.h
@@ -5,41 +5,55 @@
#include <vector>
#include <memory>
-namespace macrodown {
+namespace macrodown
+{
-enum class NodeType {
+enum class NodeType
+{
Text,
Macro,
Group
};
// Abstract Base Class
-struct Node {
+struct Node
+{
virtual ~Node() = default;
virtual NodeType type() const = 0;
};
// Represents a raw text segment
-struct TextNode : public Node {
+struct TextNode : public Node
+{
std::string content;
TextNode(std::string c) : content(std::move(c)) {}
- NodeType type() const override { return NodeType::Text; }
+
+ NodeType type() const override
+ {
+ return NodeType::Text;
+ }
};
// Represents a collection of nodes (used for macro arguments)
-struct GroupNode : public Node {
+struct GroupNode : public Node
+{
std::vector<std::unique_ptr<Node>> children;
-
- NodeType type() const override { return NodeType::Group; }
-
- void addChild(std::unique_ptr<Node> node) {
+
+ NodeType type() const override
+ {
+ return NodeType::Group;
+ }
+
+ void addChild(std::unique_ptr<Node> node)
+ {
children.push_back(std::move(node));
}
};
// Represents a macro call: %name{arg1}{arg2}...
-struct MacroNode : public Node {
+struct MacroNode : public Node
+{
std::string name;
std::vector<std::unique_ptr<Node>> arguments;
bool is_special = false; // Intrinsic macros like %def
@@ -47,7 +61,10 @@ struct MacroNode : public Node {
MacroNode(std::string n, bool special = false)
: name(std::move(n)), is_special(special) {}
- NodeType type() const override { return NodeType::Macro; }
+ NodeType type() const override
+ {
+ return NodeType::Macro;
+ }
};
} // namespace macrodown
diff --git a/include/parser.h b/include/parser.h
index e322a58..1e89532 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -6,9 +6,11 @@
#include <memory>
#include "nodes.h"
-namespace macrodown {
+namespace macrodown
+{
-class Parser {
+class Parser
+{
public:
// Parses a string into a list of nodes (Text and Macros)
// This handles the Inline Parsing phase (Phase 2 of the design)
@@ -17,4 +19,4 @@ public:
} // namespace macrodown
-#endif // MACRODOWN_PARSER_H
+#endif // MACRODOWN_PARSER_H
\ No newline at end of file
diff --git a/include/standard_library.h b/include/standard_library.h
index 134785f..e54950f 100644
--- a/include/standard_library.h
+++ b/include/standard_library.h
@@ -3,13 +3,15 @@
#include "macro_engine.h"
-namespace macrodown {
+namespace macrodown
+{
-class StandardLibrary {
+class StandardLibrary
+{
public:
- static void register_macros(Evaluator& evaluator);
+ static void registerMacros(Evaluator& evaluator);
};
} // namespace macrodown
-#endif // MACRODOWN_STANDARD_LIBRARY_H
+#endif // MACRODOWN_STANDARD_LIBRARY_H
\ No newline at end of file
diff --git a/prompts.md b/prompts.md
index a970cba..a3a2583 100644
--- a/prompts.md
+++ b/prompts.md
@@ -52,3 +52,22 @@ Unicode should be handled properly.
Use Cmake as the build system. You can see an exmaple cmake file at
https://github.com/MetroWind/planck-blog/blob/master/CMakeLists.txt.
+
+----
+
+Success Rate: 96.7%
+User Agreement: 100.0% (60 reviewed)
+Code Changes: +1244 -136
+
+Performance
+Wall Time: 38m 35s
+Agent Active: 32m 50s
+ » API Time: 10m 31s (32.1%)
+ » Tool Time: 22m 18s (67.9%)
+
+Model Usage Reqs Input Tokens Cache Reads Output Tokens
+────────────────────────────────────────────────────────────────────────────
+gemini-2.5-flash-lite 17 28,986 0 9,364
+gemini-3-flash-preview 14 175,114 348,017 1,187
+gemini-3-pro-preview 55 268,873 3,224,960 37,756
+gemini-2.5-flash 4 17,697 0 2,957
diff --git a/src/block_parser.cpp b/src/block_parser.cpp
index 107853a..5af4746 100644
--- a/src/block_parser.cpp
+++ b/src/block_parser.cpp
@@ -3,19 +3,24 @@
#include <iostream>
#include <algorithm>
-namespace macrodown {
+namespace macrodown
+{
-namespace {
+namespace
+{
// Helper: check if line is blank (only spaces)
-bool is_blank(const std::string& line) {
+bool is_blank(const std::string& line)
+{
return std::all_of(line.begin(), line.end(), [](unsigned char c){ return std::isspace(c); });
}
// Helper: count indentation
-size_t count_indent(const std::string& line, size_t offset) {
+size_t count_indent(const std::string& line, size_t offset)
+{
size_t count = 0;
- while (offset + count < line.size() && line[offset + count] == ' ') {
+ while(offset + count < line.size() && line[offset + count] == ' ')
+ {
count++;
}
return count;
@@ -23,19 +28,23 @@ size_t count_indent(const std::string& line, size_t offset) {
} // namespace
-BlockParser::BlockParser() {
+BlockParser::BlockParser()
+{
root = std::make_unique<Block>(BlockType::Document);
open_blocks.push_back({root.get()});
}
-std::unique_ptr<Block> BlockParser::parse(const std::string& input) {
+std::unique_ptr<Block> BlockParser::parse(const std::string& input)
+{
BlockParser parser;
std::istringstream stream(input);
std::string line;
- while (std::getline(stream, line)) {
+ while(std::getline(stream, line))
+ {
// Remove \r if present (Windows line endings)
- if (!line.empty() && line.back() == '\r') {
+ if(!line.empty() && line.back() == '\r')
+ {
line.pop_back();
}
parser.process_line(line);
@@ -49,7 +58,8 @@ std::unique_ptr<Block> BlockParser::parse(const std::string& input) {
return std::move(parser.root);
}
-bool BlockParser::is_container(BlockType type) {
+bool BlockParser::is_container(BlockType type)
+{
return type == BlockType::Document ||
type == BlockType::Quote ||
type == BlockType::List ||
@@ -58,41 +68,51 @@ bool BlockParser::is_container(BlockType type) {
// Determines if an open block matches the current line
// Updates offset to consume markers
-bool BlockParser::matches(Block* block, const std::string& line, size_t& offset) {
- if (block->type == BlockType::Document) {
+bool BlockParser::matches(Block* block, const std::string& line, size_t& offset)
+{
+ if(block->type == BlockType::Document)
+ {
return true; // Document always matches
}
- if (block->type == BlockType::Quote) {
+ if(block->type == BlockType::Quote)
+ {
size_t indent = count_indent(line, offset);
- if (indent < 4) {
- if (offset + indent < line.size() && line[offset + indent] == '>') {
+ if(indent < 4)
+ {
+ if(offset + indent < line.size() && line[offset + indent] == '>')
+ {
offset += indent + 1; // Consume indent and '>'
- if (offset < line.size() && line[offset] == ' ') offset++; // Optional space
+ if(offset < line.size() && line[offset] == ' ') offset++; // Optional space
return true;
}
}
return false;
}
- if (block->type == BlockType::Paragraph) {
- if (is_blank(line)) return false;
+ if(block->type == BlockType::Paragraph)
+ {
+ if(is_blank(line)) return false;
// Paragraphs match unless interrupted by a new block type
size_t indent = count_indent(line, offset);
- if (indent < 4) {
+ if(indent < 4)
+ {
// Check for BlockQuote
- if (offset + indent < line.size() && line[offset + indent] == '>') return false;
+ if(offset + indent < line.size() && line[offset + indent] == '>') return false;
// Check for ATX Heading
size_t check_pos = offset + indent;
- if (check_pos < line.size() && line[check_pos] == '#') {
+ if(check_pos < line.size() && line[check_pos] == '#')
+ {
// Confirm it's a heading (sequence of # followed by space or end)
size_t hash_count = 0;
- while (check_pos + hash_count < line.size() && line[check_pos + hash_count] == '#' && hash_count < 6) {
+ while(check_pos + hash_count < line.size() && line[check_pos + hash_count] == '#' && hash_count < 6)
+ {
hash_count++;
}
- if (hash_count > 0 && (check_pos + hash_count == line.size() || line[check_pos + hash_count] == ' ')) {
+ if(hash_count > 0 && (check_pos + hash_count == line.size() || line[check_pos + hash_count] == ' '))
+ {
return false;
}
}
@@ -105,16 +125,21 @@ bool BlockParser::matches(Block* block, const std::string& line, size_t& offset)
return false;
}
-void BlockParser::process_line(const std::string& line) {
+void BlockParser::process_line(const std::string& line)
+{
size_t offset = 0;
size_t matches_count = 0;
// 1. Find matches in open blocks
matches_count = 0; // Root always matches
- for (size_t i = 1; i < open_blocks.size(); ++i) {
- if (matches(open_blocks[i].block, line, offset)) {
+ for(size_t i = 1; i < open_blocks.size(); ++i)
+ {
+ if(matches(open_blocks[i].block, line, offset))
+ {
matches_count = i;
- } else {
+ }
+ else
+ {
break;
}
}
@@ -126,17 +151,21 @@ void BlockParser::process_line(const std::string& line) {
// Scan rest of line (at offset)
// Check for BlockQuote
- while (true) {
+ while(true)
+ {
size_t indent = count_indent(line, offset);
- if (indent < 4 && offset + indent < line.size() && line[offset + indent] == '>') {
+ if(indent < 4 && offset + indent < line.size() && line[offset + indent] == '>')
+ {
offset += indent + 1;
- if (offset < line.size() && line[offset] == ' ') offset++;
+ if(offset < line.size() && line[offset] == ' ') offset++;
auto new_block = std::make_unique<Block>(BlockType::Quote);
Block* ptr = new_block.get();
open_blocks.back().block->children.push_back(std::move(new_block));
open_blocks.push_back({ptr});
- } else {
+ }
+ else
+ {
break;
}
}
@@ -146,8 +175,10 @@ void BlockParser::process_line(const std::string& line) {
Block* tip = open_blocks.back().block;
// If tip is a Paragraph, check for blank line (closes it)
- if (tip->type == BlockType::Paragraph) {
- if (is_blank(line)) {
+ if(tip->type == BlockType::Paragraph)
+ {
+ if(is_blank(line))
+ {
close_unmatched_blocks(open_blocks.size() - 2); // Close paragraph
return;
}
@@ -159,17 +190,21 @@ void BlockParser::process_line(const std::string& line) {
// Check for ATX Heading
size_t indent = count_indent(line, offset);
- if (indent < 4) {
+ if(indent < 4)
+ {
size_t check_pos = offset + indent;
size_t hash_count = 0;
- while (check_pos + hash_count < line.size() && line[check_pos + hash_count] == '#' && hash_count < 6) {
+ while(check_pos + hash_count < line.size() && line[check_pos + hash_count] == '#' && hash_count < 6)
+ {
hash_count++;
}
- if (hash_count > 0 && (check_pos + hash_count == line.size() || line[check_pos + hash_count] == ' ')) {
+ if(hash_count > 0 && (check_pos + hash_count == line.size() || line[check_pos + hash_count] == ' '))
+ {
// Found Heading
// If we were in a paragraph, close it
- if (tip->type == BlockType::Paragraph) {
+ if(tip->type == BlockType::Paragraph)
+ {
close_unmatched_blocks(open_blocks.size() - 2);
tip = open_blocks.back().block;
}
@@ -178,7 +213,7 @@ void BlockParser::process_line(const std::string& line) {
heading->level = hash_count;
// Content is the rest of the line (trimmed)
size_t content_start = check_pos + hash_count;
- while (content_start < line.size() && line[content_start] == ' ') content_start++;
+ while(content_start < line.size() && line[content_start] == ' ') content_start++;
heading->literal_content = line.substr(content_start);
// Remove trailing hashes? CommonMark says yes. Optional for now.
heading->open = false; // Headings are single line
@@ -189,11 +224,13 @@ void BlockParser::process_line(const std::string& line) {
}
// 5. Finalize: Text or Paragraph
- if (is_blank(line)) {
+ if(is_blank(line))
+ {
return; // Ignore blank lines if not ending a paragraph
}
- if (tip->type == BlockType::Document || tip->type == BlockType::Quote || tip->type == BlockType::List || tip->type == BlockType::ListItem) {
+ if(tip->type == BlockType::Document || tip->type == BlockType::Quote || tip->type == BlockType::List || tip->type == BlockType::ListItem)
+ {
// Create new Paragraph
auto p = std::make_unique<Block>(BlockType::Paragraph);
Block* p_ptr = p.get();
@@ -205,7 +242,9 @@ void BlockParser::process_line(const std::string& line) {
// CommonMark: stripped.
size_t content_start = offset + count_indent(line, offset);
p_ptr->literal_content = line.substr(content_start);
- } else if (tip->type == BlockType::Paragraph) {
+ }
+ else if(tip->type == BlockType::Paragraph)
+ {
// Continuation
// Remove leading spaces up to indent? simplified: just add space + text
size_t content_start = offset + count_indent(line, offset);
@@ -213,12 +252,14 @@ void BlockParser::process_line(const std::string& line) {
}
}
-void BlockParser::close_unmatched_blocks(size_t last_matched_index) {
- while (open_blocks.size() > last_matched_index + 1) {
+void BlockParser::close_unmatched_blocks(size_t last_matched_index)
+{
+ while(open_blocks.size() > last_matched_index + 1)
+ {
Block* block = open_blocks.back().block;
block->open = false;
open_blocks.pop_back();
}
}
-} // namespace macrodown
+} // namespace macrodown
\ No newline at end of file
diff --git a/src/converter.cpp b/src/converter.cpp
index 0500257..8c3db2c 100644
--- a/src/converter.cpp
+++ b/src/converter.cpp
@@ -2,34 +2,43 @@
#include "parser.h"
#include <iostream>
-namespace macrodown {
+namespace macrodown
+{
-std::vector<std::unique_ptr<Node>> Converter::convert(const Block* root) {
+std::vector<std::unique_ptr<Node>> Converter::convert(const Block* root)
+{
std::vector<std::unique_ptr<Node>> nodes;
- if (root->type == BlockType::Document) {
- for (const auto& child : root->children) {
+ if(root->type == BlockType::Document)
+ {
+ for(const auto& child : root->children)
+ {
auto node = convert_block(child.get());
- if (node) {
+ if(node)
+ {
nodes.push_back(std::move(node));
}
}
- } else {
+ }
+ else
+ {
// Should not happen if root is Document, but handle single block
auto node = convert_block(root);
- if (node) nodes.push_back(std::move(node));
+ if(node) nodes.push_back(std::move(node));
}
return nodes;
}
-std::unique_ptr<Node> Converter::convert_block(const Block* block) {
- if (!block) return nullptr;
+std::unique_ptr<Node> Converter::convert_block(const Block* block)
+{
+ if(!block) return nullptr;
std::string macro_name;
std::vector<std::unique_ptr<Node>> args;
- switch (block->type) {
+ switch(block->type)
+ {
case BlockType::Paragraph:
macro_name = "p";
break;
@@ -46,21 +55,27 @@ std::unique_ptr<Node> Converter::convert_block(const Block* block) {
auto macro = std::make_unique<MacroNode>(macro_name);
// Handle Content
- if (block->children.empty()) {
+ if(block->children.empty())
+ {
// Leaf block: Parse literal content
// Wrap result in GroupNode
auto group = std::make_unique<GroupNode>();
auto inline_nodes = Parser::parse(block->literal_content);
- for (auto& n : inline_nodes) {
+ for(auto& n : inline_nodes)
+ {
group->addChild(std::move(n));
}
macro->arguments.push_back(std::move(group));
- } else {
+ }
+ else
+ {
// Container block: Recursively convert children
auto group = std::make_unique<GroupNode>();
- for (const auto& child : block->children) {
+ for(const auto& child : block->children)
+ {
auto child_node = convert_block(child.get());
- if (child_node) {
+ if(child_node)
+ {
group->addChild(std::move(child_node));
}
}
@@ -70,4 +85,4 @@ std::unique_ptr<Node> Converter::convert_block(const Block* block) {
return macro;
}
-} // namespace macrodown
+} // namespace macrodown
\ No newline at end of file
diff --git a/src/lib_placeholder.cpp b/src/lib_placeholder.cpp
deleted file mode 100644
index 4510c59..0000000
--- a/src/lib_placeholder.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "nodes.h"
-#include "block.h"
-
-namespace macrodown {
- // Initial placeholder for library sources
-}
diff --git a/src/macro_engine.cpp b/src/macro_engine.cpp
index 14d2816..06c00fe 100644
--- a/src/macro_engine.cpp
+++ b/src/macro_engine.cpp
@@ -147,4 +147,4 @@ std::string Evaluator::evaluateMacro(const MacroNode& macro) {
}
}
-} // namespace macrodown
+} // namespace macrodown
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 0b33586..6b0a83a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -8,19 +8,24 @@
using namespace macrodown;
-int main(int argc, char** argv) {
+int main(int argc, char** argv)
+{
std::string input;
-
- if (argc > 1) {
+
+ if(argc > 1)
+ {
std::ifstream file(argv[1]);
- if (!file) {
+ if(!file)
+ {
std::cerr << "Error: Could not open file " << argv[1] << std::endl;
return 1;
}
std::stringstream buffer;
buffer << file.rdbuf();
input = buffer.str();
- } else {
+ }
+ else
+ {
// Read from stdin
std::stringstream buffer;
buffer << std::cin.rdbuf();
@@ -29,7 +34,7 @@ int main(int argc, char** argv) {
// 1. Setup Environment
Evaluator evaluator;
- StandardLibrary::register_macros(evaluator);
+ StandardLibrary::registerMacros(evaluator);
// 2. Parse Blocks
auto root = BlockParser::parse(input);
@@ -38,9 +43,10 @@ int main(int argc, char** argv) {
auto macro_nodes = Converter::convert(root.get());
// 4. Evaluate and Output
- for (const auto& node : macro_nodes) {
+ for(const auto& node : macro_nodes)
+ {
std::cout << evaluator.evaluate(*node);
}
return 0;
-}
+}
\ No newline at end of file
diff --git a/src/parser.cpp b/src/parser.cpp
index 2b33ea3..c2bd69a 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -1,25 +1,31 @@
#include "parser.h"
#include <iostream>
-namespace macrodown {
+namespace macrodown
+{
-std::vector<std::unique_ptr<Node>> Parser::parse(const std::string& input) {
+std::vector<std::unique_ptr<Node>> Parser::parse(const std::string& input)
+{
std::vector<std::unique_ptr<Node>> nodes;
std::string current_text;
- auto flush_text = [&]() {
- if (!current_text.empty()) {
+ auto flush_text = [&]()
+ {
+ if(!current_text.empty())
+ {
nodes.push_back(std::make_unique<TextNode>(current_text));
current_text.clear();
}
};
size_t i = 0;
- while (i < input.length()) {
+ while(i < input.length())
+ {
char c = input[i];
// Escape handling
- if (c == '\\' && i + 1 < input.length()) {
+ if(c == '\\' && i + 1 < input.length())
+ {
// Append the escaped character
current_text += input[i+1];
i += 2;
@@ -27,17 +33,20 @@ std::vector<std::unique_ptr<Node>> Parser::parse(const std::string& input) {
}
// Macro: %name{args}...
- if (c == '%') {
+ if(c == '%')
+ {
flush_text();
i++; // skip %
size_t name_start = i;
- while (i < input.length() && (isalnum(input[i]) || input[i] == '_')) {
+ while(i < input.length() && (isalnum(input[i]) || input[i] == '_'))
+ {
i++;
}
std::string name = input.substr(name_start, i - name_start);
- if (name.empty()) {
+ if(name.empty())
+ {
current_text += "%";
continue;
}
@@ -45,22 +54,30 @@ std::vector<std::unique_ptr<Node>> Parser::parse(const std::string& input) {
auto macro = std::make_unique<MacroNode>(name);
// Parse Arguments
- while (i < input.length()) {
+ while(i < input.length())
+ {
char open = input[i];
- if (open == '{' || open == '[') {
+ if(open == '{' || open == '[')
+ {
char close = (open == '{') ? '}' : ']';
i++;
std::string arg_content;
int balance = 1;
- while (i < input.length() && balance > 0) {
- if (input[i] == open) {
+ while(i < input.length() && balance > 0)
+ {
+ if(input[i] == open)
+ {
balance++;
arg_content += input[i];
- } else if (input[i] == close) {
+ }
+ else if(input[i] == close)
+ {
balance--;
- if (balance > 0) arg_content += input[i];
- } else {
+ if(balance > 0) arg_content += input[i];
+ }
+ else
+ {
arg_content += input[i];
}
i++;
@@ -68,11 +85,14 @@ std::vector<std::unique_ptr<Node>> Parser::parse(const std::string& input) {
auto group = std::make_unique<GroupNode>();
std::vector<std::unique_ptr<Node>> sub_nodes = parse(arg_content);
- for (auto& n : sub_nodes) {
+ for(auto& n : sub_nodes)
+ {
group->addChild(std::move(n));
}
macro->arguments.push_back(std::move(group));
- } else {
+ }
+ else
+ {
break;
}
}
@@ -81,10 +101,12 @@ std::vector<std::unique_ptr<Node>> Parser::parse(const std::string& input) {
}
// Inline Code: `...`
- if (c == '`') {
+ if(c == '`')
+ {
size_t start = i + 1;
size_t end = input.find('`', start);
- if (end != std::string::npos) {
+ if(end != std::string::npos)
+ {
flush_text();
std::string content = input.substr(start, end - start);
auto macro = std::make_unique<MacroNode>("code");
@@ -98,26 +120,31 @@ std::vector<std::unique_ptr<Node>> Parser::parse(const std::string& input) {
}
// Link: [text](url)
- if (c == '[') {
+ if(c == '[')
+ {
// Find matching ]
// Simplified: doesn't handle nested brackets well yet
size_t label_start = i + 1;
// We need a helper for balanced search, but for now simple find
size_t j = label_start;
int bracket_bal = 1;
- while(j < input.length() && bracket_bal > 0) {
- if (input[j] == '[') bracket_bal++;
- else if (input[j] == ']') bracket_bal--;
- if (bracket_bal > 0) j++;
+ while(j < input.length() && bracket_bal > 0)
+ {
+ if(input[j] == '[') bracket_bal++;
+ else if(input[j] == ']') bracket_bal--;
+ if(bracket_bal > 0) j++;
}
- if (j < input.length() && bracket_bal == 0) {
+ if(j < input.length() && bracket_bal == 0)
+ {
size_t close_bracket = j;
// Check for (url)
- if (close_bracket + 1 < input.length() && input[close_bracket + 1] == '(') {
+ if(close_bracket + 1 < input.length() && input[close_bracket + 1] == '(')
+ {
size_t url_start = close_bracket + 2;
size_t url_end = input.find(')', url_start);
- if (url_end != std::string::npos) {
+ if(url_end != std::string::npos)
+ {
flush_text();
std::string label = input.substr(label_start, close_bracket - label_start);
std::string url = input.substr(url_start, url_end - url_start);
@@ -145,7 +172,8 @@ std::vector<std::unique_ptr<Node>> Parser::parse(const std::string& input) {
// Emphasis: * or **
// Simplified: Greedy scan for matching *
- if (c == '*') {
+ if(c == '*')
+ {
bool strong = (i + 1 < input.length() && input[i+1] == '*');
size_t start_content = i + (strong ? 2 : 1);
@@ -157,7 +185,8 @@ std::vector<std::unique_ptr<Node>> Parser::parse(const std::string& input) {
// Simplified: just find first match.
// This is NOT correct CommonMark but sufficient for prototype.
- if (end != std::string::npos) {
+ if(end != std::string::npos)
+ {
flush_text();
std::string content = input.substr(start_content, end - start_content);
auto macro = std::make_unique<MacroNode>(strong ? "strong" : "em");
@@ -180,7 +209,4 @@ std::vector<std::unique_ptr<Node>> Parser::parse(const std::string& input) {
return nodes;
}
-// Helper stub for compilation (not actually used since we inlined logic, but logic relied on start_pos_of_link_end which I removed)
-// I removed start_pos_of_link_end call in logic.
-
-} // namespace macrodown
\ No newline at end of file
+} // namespace macrodown
diff --git a/src/standard_library.cpp b/src/standard_library.cpp
index 2fac350..92e84fb 100644
--- a/src/standard_library.cpp
+++ b/src/standard_library.cpp
@@ -1,21 +1,25 @@
#include "standard_library.h"
-namespace macrodown {
+namespace macrodown
+{
-void StandardLibrary::register_macros(Evaluator& evaluator) {
+void StandardLibrary::registerMacros(Evaluator& evaluator)
+{
// Blocks
- evaluator.defineIntrinsic("p", [](const std::vector<std::string>& args) -> std::string {
- if (args.empty()) return "";
+ evaluator.defineIntrinsic("p", [](const std::vector<std::string>& args) -> std::string
+ {
+ if(args.empty()) return "";
std::string content = args[0];
// Check if content is empty or just whitespace
- if (content.find_first_not_of(" \t\n\r") == std::string::npos) {
+ if(content.find_first_not_of(" \t\n\r") == std::string::npos)
+ {
return "";
}
return "<p>" + content + "</p>\n";
});
evaluator.define("quote", {"content"}, "<blockquote>\n%content</blockquote>\n");
-
+
// Headings
evaluator.define("h1", {"content"}, "<h1>%content</h1>\n");
evaluator.define("h2", {"content"}, "<h2>%content</h2>\n");
@@ -23,7 +27,7 @@ void StandardLibrary::register_macros(Evaluator& evaluator) {
evaluator.define("h4", {"content"}, "<h4>%content</h4>\n");
evaluator.define("h5", {"content"}, "<h5>%content</h5>\n");
evaluator.define("h6", {"content"}, "<h6>%content</h6>\n");
-
+
// Inline
evaluator.define("em", {"content"}, "<em>%content</em>");
evaluator.define("strong", {"content"}, "<strong>%content</strong>");
@@ -33,4 +37,3 @@ void StandardLibrary::register_macros(Evaluator& evaluator) {
}
} // namespace macrodown
-
diff --git a/tests/test_block_parser.cpp b/tests/test_block_parser.cpp
index 8d1f6a3..91cf324 100644
--- a/tests/test_block_parser.cpp
+++ b/tests/test_block_parser.cpp
@@ -3,7 +3,8 @@
using namespace macrodown;
-TEST(BlockParserTest, SimpleParagraph) {
+TEST(BlockParserTest, SimpleParagraph)
+{
std::string input = "Hello\nWorld";
auto root = BlockParser::parse(input);
@@ -15,7 +16,8 @@ TEST(BlockParserTest, SimpleParagraph) {
EXPECT_EQ(p->literal_content, "Hello\nWorld");
}
-TEST(BlockParserTest, MultipleParagraphs) {
+TEST(BlockParserTest, MultipleParagraphs)
+{
std::string input = "Para 1\n\nPara 2";
auto root = BlockParser::parse(input);
@@ -24,7 +26,8 @@ TEST(BlockParserTest, MultipleParagraphs) {
EXPECT_EQ(root->children[1]->type, BlockType::Paragraph);
}
-TEST(BlockParserTest, Headers) {
+TEST(BlockParserTest, Headers)
+{
std::string input = "# H1\n## H2";
auto root = BlockParser::parse(input);
@@ -41,7 +44,8 @@ TEST(BlockParserTest, Headers) {
EXPECT_EQ(h2->literal_content, "H2");
}
-TEST(BlockParserTest, BlockQuote) {
+TEST(BlockParserTest, BlockQuote)
+{
std::string input = "> Hello\n> World";
auto root = BlockParser::parse(input);
@@ -55,7 +59,8 @@ TEST(BlockParserTest, BlockQuote) {
EXPECT_EQ(p->literal_content, "Hello\nWorld");
}
-TEST(BlockParserTest, NestedQuote) {
+TEST(BlockParserTest, NestedQuote)
+{
std::string input = "> Level 1\n>> Level 2";
auto root = BlockParser::parse(input);
@@ -64,25 +69,6 @@ TEST(BlockParserTest, NestedQuote) {
EXPECT_EQ(q1->type, BlockType::Quote);
// Structure: Quote -> [Paragraph("Level 1"), Quote -> [Paragraph("Level 2")]]
- // Wait, my parser closes paragraph on new block type if strict?
- // "Level 1" starts a paragraph.
- // ">> Level 2":
- // Line 1: "> Level 1". Matches Quote. Adds "Level 1" to P.
- // Line 2: ">> Level 2". Matches Quote (q1).
- // Scan rest: "> Level 2". Finds nested Quote (q2).
- // Scan rest: "Level 2". Adds to new P inside q2.
- // But what about the P inside q1? "Level 1".
- // Does it close?
- // My parser logic:
- // matches(q1) -> true. matches_count = 1.
- // Scan for new blocks: finds q2.
- // q1->children (was [P]) now gets q2 pushed?
- // Yes. P remains in children list. P is closed?
- // "close_unmatched_blocks" only closes if mismatch.
- // But Step 4 "Handle Leaf Blocks":
- // tip is now q2. (Since we opened q2).
- // So P (inside q1) is no longer the "tip" (back of open_blocks).
- // Correct.
ASSERT_EQ(q1->children.size(), 2); // P("Level 1") + Quote
@@ -94,4 +80,4 @@ TEST(BlockParserTest, NestedQuote) {
ASSERT_EQ(q2->children.size(), 1);
EXPECT_EQ(q2->children[0]->literal_content, "Level 2");
-}
+}
\ No newline at end of file
diff --git a/tests/test_integration.cpp b/tests/test_integration.cpp
index a80dc20..07f325a 100644
--- a/tests/test_integration.cpp
+++ b/tests/test_integration.cpp
@@ -59,7 +59,7 @@ TEST_F(IntegrationTest, Code) {
TEST_F(IntegrationTest, MacroInMarkdown) {
// Define custom macro
evaluator.define("greet", {"name"}, "Hello, %name!");
-
+
// Use it in Markdown
EXPECT_EQ(process("Say %greet{User}"), "<p>Say Hello, User!</p>");
}
@@ -81,16 +81,16 @@ TEST_F(IntegrationTest, BlockQuote) {
// %t is result of expanding arg.
// Arg is %p... -> <p>...</p>.
// Result: <blockquote><p>...</p></blockquote>.
-
+
// Note: My Converter for Quote:
// macro "quote".
// Args: GroupNode of children.
// Children: P -> %p.
-
+
// BUT: My generic "quote" definition takes "t".
// Does it capture all children?
// Converter produces: MacroNode("quote", args=[GroupNode(child1, child2...)])
// So it has 1 argument. Correct.
-
+
EXPECT_EQ(process(input), "<blockquote><p>Hello\nWorld</p></blockquote>");
}
diff --git a/tests/test_macro_engine.cpp b/tests/test_macro_engine.cpp
index 31a329f..10a295b 100644
--- a/tests/test_macro_engine.cpp
+++ b/tests/test_macro_engine.cpp
@@ -5,13 +5,15 @@
using namespace macrodown;
-class MacroEngineTest : public ::testing::Test {
+class MacroEngineTest : public ::testing::Test
+{
protected:
Evaluator evaluator;
};
// Test parsing of simple text
-TEST_F(MacroEngineTest, ParseText) {
+TEST_F(MacroEngineTest, ParseText)
+{
auto nodes = Parser::parse("Hello World");
ASSERT_EQ(nodes.size(), 1);
EXPECT_EQ(nodes[0]->type(), NodeType::Text);
@@ -19,7 +21,8 @@ TEST_F(MacroEngineTest, ParseText) {
}
// Test parsing of macro
-TEST_F(MacroEngineTest, ParseMacro) {
+TEST_F(MacroEngineTest, ParseMacro)
+{
auto nodes = Parser::parse("%m{arg}");
ASSERT_EQ(nodes.size(), 1);
EXPECT_EQ(nodes[0]->type(), NodeType::Macro);
@@ -36,21 +39,24 @@ TEST_F(MacroEngineTest, ParseMacro) {
}
// Test intrinsic %def and expansion
-TEST_F(MacroEngineTest, DefAndExpand) {
+TEST_F(MacroEngineTest, DefAndExpand)
+{
// Define %hello{name} -> "Hello %name!"
// We use the Parser to create the definition call
std::string input = "%def[hello]{name}{Hello %name!}";
auto nodes = Parser::parse(input);
// Evaluate the definition
- for (const auto& node : nodes) {
+ for(const auto& node : nodes)
+ {
evaluator.evaluate(*node);
}
// Now call it: %hello{World}
auto call_nodes = Parser::parse("%hello{World}");
std::string result;
- for (const auto& node : call_nodes) {
+ for(const auto& node : call_nodes)
+ {
result += evaluator.evaluate(*node);
}
@@ -58,7 +64,8 @@ TEST_F(MacroEngineTest, DefAndExpand) {
}
// Test nested macros
-TEST_F(MacroEngineTest, NestedMacros) {
+TEST_F(MacroEngineTest, NestedMacros)
+{
// %def[b]{t}{<b>%t</b>}
// %def[p]{t}{<p>%t</p>}
// Call: %p{Hello %b{World}} -> <p>Hello <b>World</b></p>
@@ -68,16 +75,18 @@ TEST_F(MacroEngineTest, NestedMacros) {
"%def[p]{t}{<p>%t</p>}"
};
- for (const auto& def : defs) {
+ for(const auto& def : defs)
+ {
auto nodes = Parser::parse(def);
- for (const auto& node : nodes) evaluator.evaluate(*node);
+ for(const auto& node : nodes) evaluator.evaluate(*node);
}
auto nodes = Parser::parse("%p{Hello %b{World}}");
std::string result;
- for (const auto& node : nodes) {
+ for(const auto& node : nodes)
+ {
result += evaluator.evaluate(*node);
}
EXPECT_EQ(result, "<p>Hello <b>World</b></p>");
-}
+}
\ No newline at end of file
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index ebf5a57..2387483 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -1,10 +1,12 @@
#include <gtest/gtest.h>
-TEST(SetupTest, BasicAssertions) {
+TEST(SetupTest, BasicAssertions)
+{
EXPECT_EQ(1 + 1, 2);
}
-int main(int argc, char **argv) {
+int main(int argc, char **argv)
+{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
-}
+}
\ No newline at end of file