XPlor/libs/dsl/dsluischema.h
2026-01-01 22:18:25 -05:00

188 lines
6.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef DSLUISCHEMA_H
#define DSLUISCHEMA_H
#include "ast.h"
#include <QMap>
enum class UiFieldKind {
ScalarU8, ScalarI8, ScalarU16, ScalarI16, ScalarU32, ScalarI32, ScalarU64, ScalarI64, GenericValue, Bool
};
struct UiField {
QString name;
QString displayName;
UiFieldKind kind = UiFieldKind::GenericValue;
bool visible = true;
bool readOnly = false;
int firstLine = 1;
int firstCol = 1;
bool isTable = false;
QString tableTitle;
QStringList columns;
QHash<QString, QString> formats;
};
inline UiFieldKind toUiKind(ScalarType t) {
switch (t) {
case ScalarType::U8: return UiFieldKind::ScalarU8;
case ScalarType::I8: return UiFieldKind::ScalarI8;
case ScalarType::U16: return UiFieldKind::ScalarU16;
case ScalarType::I16: return UiFieldKind::ScalarI16;
case ScalarType::U32: return UiFieldKind::ScalarU32;
case ScalarType::I32: return UiFieldKind::ScalarI32;
case ScalarType::U64: return UiFieldKind::ScalarU64;
case ScalarType::I64: return UiFieldKind::ScalarI64;
case ScalarType::Bool: return UiFieldKind::Bool;
}
return UiFieldKind::GenericValue;
}
inline bool isBoolExpr(const Expr& e) {
if (std::holds_alternative<Expr::Unary>(e.node)) {
const auto& u = std::get<Expr::Unary>(e.node);
if (u.op == "!") return true;
return isBoolExpr(*u.rhs);
}
if (std::holds_alternative<Expr::Binary>(e.node)) {
const auto& b = std::get<Expr::Binary>(e.node);
const QString& op = b.op;
if (op == "==" || op == "!=" || op == "<" || op == "<=" || op == ">" || op == ">=" ||
op == "&&" || op == "||") {
return true;
}
// allow nested boolean expressions too
return isBoolExpr(*b.lhs) || isBoolExpr(*b.rhs);
}
return false;
}
inline UiFieldKind inferKindFromAssignExpr(const ExprPtr& value) {
if (!value) return UiFieldKind::GenericValue;
if (isBoolExpr(*value)) return UiFieldKind::Bool;
return UiFieldKind::GenericValue;
}
inline QStringList splitCsvCols(const QString& s) {
QStringList out;
for (const QString& part : s.split(',', Qt::SkipEmptyParts)) {
const QString trimmed = part.trimmed();
if (!trimmed.isEmpty()) out.push_back(trimmed);
}
return out;
}
inline void collectFieldsFromBlock(const QVector<StmtPtr>& blk, QMap<QString, UiField>& out);
inline void collectFieldsFromStmt(const Stmt& s, QMap<QString, UiField>& out) {
auto add = [&](const QString& name,
UiFieldKind kind,
bool visible,
bool readOnly,
const QString& display,
const UiFlags* flags)
{
if (name.isEmpty()) return;
if (!visible) return;
if (!out.contains(name)) {
UiField f;
f.name = name;
f.displayName = display.isEmpty() ? name : display;
f.kind = kind;
f.visible = visible;
f.readOnly = readOnly;
f.firstLine = s.line;
f.firstCol = s.col;
if (flags && flags->isTable()) {
f.isTable = true;
f.tableTitle = flags->tableTitle;
f.columns = splitCsvCols(flags->columnsCsv);
f.formats = flags->formats;
}
out.insert(name, f);
} else {
if (!display.isEmpty())
out[name].displayName = display;
if (out[name].kind == UiFieldKind::GenericValue && kind != UiFieldKind::GenericValue)
out[name].kind = kind;
out[name].readOnly = out[name].readOnly || readOnly;
// allow upgrading to table if later statement provides it
if (flags && flags->isTable() && !out[name].isTable) {
out[name].isTable = true;
out[name].tableTitle = flags->tableTitle;
out[name].columns = splitCsvCols(flags->columnsCsv);
out[name].formats = flags->formats;
}
}
};
if (std::holds_alternative<Stmt::ReadScalar>(s.node)) {
const auto& rs = std::get<Stmt::ReadScalar>(s.node);
add(rs.name, toUiKind(rs.type), rs.ui.visible(), rs.ui.readOnly, rs.ui.display, &rs.ui);
return;
}
if (std::holds_alternative<Stmt::Assign>(s.node)) {
const auto& as = std::get<Stmt::Assign>(s.node);
const bool vis = as.ui.visible();
const bool ro = as.ui.readOnly || vis;
add(as.name, inferKindFromAssignExpr(as.value), vis, ro, as.ui.display, &as.ui);
return;
}
if (std::holds_alternative<Stmt::If>(s.node)) {
const auto& iff = std::get<Stmt::If>(s.node);
collectFieldsFromBlock(iff.thenBody, out);
collectFieldsFromBlock(iff.elseBody, out);
return;
}
if (std::holds_alternative<Stmt::While>(s.node)) {
const auto& wh = std::get<Stmt::While>(s.node);
collectFieldsFromBlock(wh.body, out);
return;
}
if (std::holds_alternative<Stmt::Repeat>(s.node)) {
const auto& rp = std::get<Stmt::Repeat>(s.node);
collectFieldsFromBlock(rp.body, out);
return;
}
if (std::holds_alternative<Stmt::ForRange>(s.node)) {
const auto& fr = std::get<Stmt::ForRange>(s.node);
// loop variable itself is user-visible sometimes
add(fr.var, UiFieldKind::GenericValue, false, true, "", {});
collectFieldsFromBlock(fr.body, out);
return;
}
}
inline void collectFieldsFromBlock(const QVector<StmtPtr>& blk, QMap<QString, UiField>& out) {
for (const auto& sp : blk) {
if (!sp) continue;
collectFieldsFromStmt(*sp, out);
}
}
inline QMap<QString, UiField> buildUiSchemaForType(const TypeDef& td) {
QMap<QString, UiField> fields;
collectFieldsFromBlock(td.criteria, fields);
collectFieldsFromBlock(td.body, fields);
// If you dont want criteria-only vars exposed, remove those by policy here.
// For now: keep them all (you can filter by prefix later if you want).
return fields;
}
#endif // DSLUISCHEMA_H