Added sub-expression coloring.

This commit is contained in:
Anthony Stewart 2025-07-06 21:47:12 -05:00 committed by xxAtrain223
commit 3729f4dc5d
3 changed files with 118 additions and 21 deletions

View file

@ -168,7 +168,7 @@ class Parser {
if (Match("!")) {
expr = std::make_shared<LogicExpression::Impl>();
expr->type = LogicExpression::Impl::Type::Not;
expr->type = LogicExpression::Type::Not;
expr->children.emplace_back(ParsePrimary());
expr->children.back()->parent = expr.get();
} else if (Peek().Type == LETokenType::ParenOpen) {
@ -188,7 +188,7 @@ class Parser {
--pos;
std::string id = Next().Text;
Next(); // consume '('
expr->type = LogicExpression::Impl::Type::FunctionCall;
expr->type = LogicExpression::Type::FunctionCall;
expr->functionName = id;
while (Peek().Type != LETokenType::ParenClose) {
if (!expr->children.empty()) {
@ -203,7 +203,7 @@ class Parser {
}
Next(); // consume ')'
} else {
expr->type = LogicExpression::Impl::Type::Value;
expr->type = LogicExpression::Type::Value;
expr->value = token.Text;
if (token.Type == LETokenType::Boolean)
expr->valueType = LogicExpression::Impl::ValueType::Boolean;
@ -229,7 +229,7 @@ class Parser {
template <typename LowerFunc>
std::shared_ptr<LogicExpression::Impl>
ParseBinaryOp(size_t& pos, const std::vector<Token>& tokens, LowerFunc lowerFunc,
const std::vector<std::pair<std::string, LogicExpression::Impl::Type>>& operators) {
const std::vector<std::pair<std::string, LogicExpression::Type>>& operators) {
size_t initial_pos = pos;
auto left = (this->*lowerFunc)();
@ -240,9 +240,7 @@ class Parser {
auto right = ParseBinaryOp(pos, tokens, lowerFunc, operators);
auto expr = std::make_shared<LogicExpression::Impl>();
expr->type = exprType;
if (exprType == LogicExpression::Impl::Type::Comparison) {
expr->operation = op;
}
expr->operation = op;
expr->children.emplace_back(left);
expr->children.back()->parent = expr.get();
expr->children.emplace_back(right);
@ -259,33 +257,33 @@ class Parser {
std::shared_ptr<LogicExpression::Impl> ParseMulDiv() {
return ParseBinaryOp(
pos, tokens, &Parser::ParsePrimary,
{ { "*", LogicExpression::Impl::Type::Multiply }, { "/", LogicExpression::Impl::Type::Divide } });
{ { "*", LogicExpression::Type::Multiply }, { "/", LogicExpression::Type::Divide } });
}
std::shared_ptr<LogicExpression::Impl> ParseAddSub() {
return ParseBinaryOp(
pos, tokens, &Parser::ParseMulDiv,
{ { "+", LogicExpression::Impl::Type::Add }, { "-", LogicExpression::Impl::Type::Subtract } });
{ { "+", LogicExpression::Type::Add }, { "-", LogicExpression::Type::Subtract } });
}
std::shared_ptr<LogicExpression::Impl> ParseComparison() {
return ParseBinaryOp(pos, tokens, &Parser::ParseAddSub,
{ { "==", LogicExpression::Impl::Type::Comparison },
{ "!=", LogicExpression::Impl::Type::Comparison },
{ ">=", LogicExpression::Impl::Type::Comparison },
{ "<=", LogicExpression::Impl::Type::Comparison },
{ ">", LogicExpression::Impl::Type::Comparison },
{ "<", LogicExpression::Impl::Type::Comparison } });
{ { "==", LogicExpression::Type::Comparison },
{ "!=", LogicExpression::Type::Comparison },
{ ">=", LogicExpression::Type::Comparison },
{ "<=", LogicExpression::Type::Comparison },
{ ">", LogicExpression::Type::Comparison },
{ "<", LogicExpression::Type::Comparison } });
}
std::shared_ptr<LogicExpression::Impl> ParseAnd() {
return ParseBinaryOp(pos, tokens, &Parser::ParseComparison,
{ { "&&", LogicExpression::Impl::Type::And } });
{ { "&&", LogicExpression::Type::And } });
}
std::shared_ptr<LogicExpression::Impl> ParseOr() {
return ParseBinaryOp(pos, tokens, &Parser::ParseAnd,
{ { "||", LogicExpression::Impl::Type::Or } });
{ { "||", LogicExpression::Type::Or } });
}
std::shared_ptr<LogicExpression::Impl> ParseTernary() {
@ -305,7 +303,7 @@ class Parser {
auto falseExpr = ParseTernary();
auto expr = std::make_shared<LogicExpression::Impl>();
expr->type = LogicExpression::Impl::Type::Ternary;
expr->type = LogicExpression::Type::Ternary;
expr->children.emplace_back(cond);
expr->children.back()->parent = expr.get();
@ -368,6 +366,18 @@ const std::vector<std::shared_ptr<LogicExpression>>& LogicExpression::GetChildre
return children;
}
LogicExpression::Type LogicExpression::GetType() const {
return impl->type;
}
std::string LogicExpression::GetOperation() const {
return impl->operation;
}
std::string LogicExpression::GetFunctionName() const {
return impl->functionName;
}
std::string LogicExpression::ToString() const {
if (impl->expressionString != nullptr) {
return *impl->expressionString;

View file

@ -14,11 +14,17 @@ class LogicExpression {
public:
using ValueVariant = std::variant<bool, int, uint8_t, uint16_t>;
// Define callback type for evaluation tracing
using EvaluationCallback = std::function<void(const std::shared_ptr<LogicExpression>&, const std::string&, int, const std::string&, const ValueVariant&)>;
using EvaluationCallback = std::function<void(const std::shared_ptr<LogicExpression>&, const std::string&, int,
const std::string&, const ValueVariant&)>;
enum class Type { And, Or, Not, Comparison, FunctionCall, Value, Add, Subtract, Multiply, Divide, Ternary };
static std::shared_ptr<LogicExpression> Parse(const std::string& exprStr);
std::string ToString() const;
const std::vector<std::shared_ptr<LogicExpression>>& GetChildren() const;
Type GetType() const;
std::string GetOperation() const;
std::string GetFunctionName() const;
// Add optional callback parameter to Evaluate
template <typename T> T Evaluate(const EvaluationCallback& callback = nullptr) const {
@ -54,7 +60,6 @@ class LogicExpression {
private:
struct Impl {
enum class Type { And, Or, Not, Comparison, FunctionCall, Value, Add, Subtract, Multiply, Divide, Ternary };
enum class ValueType { Identifier, Boolean, Number, Enum };

View file

@ -175,6 +175,88 @@ static std::string ToString(const std::optional<LogicExpression::ValueVariant>&
return ToString(value.value());
}
static void DrawColoredWrappedText(const std::vector<std::pair<ImVec4, std::string>>& segments) {
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 pos = ImGui::GetCursorScreenPos();
float wrapWidth = ImGui::GetContentRegionAvail().x;
float x = pos.x;
float y = pos.y;
float startX = x;
ImFont* font = ImGui::GetFont();
float fontSize = ImGui::GetFontSize();
for (const auto& [color, text] : segments) {
std::istringstream iss(text + " ");
std::string word;
bool firstWordInSegment = true;
while (std::getline(iss, word, ' ')) {
std::string toDraw = word;
if (!firstWordInSegment) {
toDraw = " " + word;
}
ImVec2 textSize = ImGui::CalcTextSize(toDraw.c_str());
if (x + textSize.x > startX + wrapWidth && x > startX) {
x = startX;
y += fontSize;
toDraw = word;
textSize = ImGui::CalcTextSize(toDraw.c_str());
}
draw_list->AddText(font, fontSize, ImVec2(x, y), ImGui::ColorConvertFloat4ToU32(color), toDraw.c_str());
x += textSize.x;
firstWordInSegment = false;
}
}
ImGui::SetCursorScreenPos(ImVec2(x, y + fontSize));
}
static void DrawCondition(const LogicExpression& expression) {
static ImVec4 fontColors[] = {
ImVec4(0.5f, 0.5f, 1.0f, 1.0f), // Blue
ImVec4(0.5f, 1.0f, 0.5f, 1.0f), // Green
ImVec4(1.0f, 0.5f, 0.5f, 1.0f), // Red
ImVec4(1.0f, 1.0f, 0.5f, 1.0f), // Yellow
ImVec4(1.0f, 0.5f, 1.0f, 1.0f), // Magenta
ImVec4(0.5f, 1.0f, 1.0f, 1.0f), // Cyan
};
static size_t fontColorsLength = std::size(fontColors);
static ImVec4 defaultColor = ImGui::GetStyleColorVec4(ImGuiCol_Text);
LogicExpression::Type type = expression.GetType();
if (type == LogicExpression::Type::And || type == LogicExpression::Type::Or ||
type == LogicExpression::Type::Comparison || type == LogicExpression::Type::Add ||
type == LogicExpression::Type::Subtract || type == LogicExpression::Type::Multiply ||
type == LogicExpression::Type::Divide) {
DrawColoredWrappedText({ { fontColors[0], expression.GetChildren()[0]->ToString() },
{ defaultColor, " " + expression.GetOperation() + " " },
{ fontColors[1], expression.GetChildren()[1]->ToString() } });
} else if (type == LogicExpression::Type::Not) {
DrawColoredWrappedText({ { defaultColor, "!" },
{ fontColors[0], expression.GetChildren()[0]->ToString() } });
} else if (type == LogicExpression::Type::FunctionCall) {
std::vector<std::pair<ImVec4, std::string>> segments;
segments.emplace_back(fontColors[0], expression.GetFunctionName());
segments.emplace_back(defaultColor, "(");
for (size_t i = 0; i < expression.GetChildren().size(); ++i) {
segments.emplace_back(fontColors[i + 1 % fontColorsLength], expression.GetChildren()[i]->ToString());
if (i < expression.GetChildren().size() - 1) {
segments.emplace_back(defaultColor, ", ");
}
}
segments.emplace_back(defaultColor, ")");
DrawColoredWrappedText(segments);
} else if (type == LogicExpression::Type::Ternary) {
DrawColoredWrappedText({ { fontColors[0], expression.GetChildren()[0]->ToString() },
{ defaultColor, " ? " },
{ fontColors[1], expression.GetChildren()[1]->ToString() },
{ defaultColor, " : " },
{ fontColors[2], expression.GetChildren()[2]->ToString() } });
} else if (type == LogicExpression::Type::Value) {
DrawColoredWrappedText({ { fontColors[0], expression.ToString() } });
} else {
DrawColoredWrappedText({ { defaultColor, expression.ToString() } });
}
}
static void DrawExpressionRow(const LogicTrackerCheck::Region& region, LogicTrackerCheck::Region::ExpressionRow& row,
int level) {
ImGui::TableNextRow();
@ -196,7 +278,7 @@ static void DrawExpressionRow(const LogicTrackerCheck::Region& region, LogicTrac
}
ImGui::PopID();
ImGui::TableNextColumn();
ImGui::TextWrapped("%s", row.Expression->ToString().c_str());
DrawCondition(*row.Expression);
ImGui::TableNextColumn();
ImGui::TextUnformatted(ToString(row.ChildDay).c_str());