XPlor/libs/dsl/dsluischema.h

202 lines
6.8 KiB
C
Raw Normal View History

2026-01-01 22:18:25 -05:00
#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;
}
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, const Module* mod = nullptr);
2026-01-01 22:18:25 -05:00
inline void collectFieldsFromStmt(const Stmt& s, QMap<QString, UiField>& out, const Module* mod = nullptr) {
2026-01-01 22:18:25 -05:00
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;
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();
// Respect the parser's readOnly flag - use ui("name", edit) for editable fields
const bool ro = as.ui.readOnly;
2026-01-01 22:18:25 -05:00
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, mod);
collectFieldsFromBlock(iff.elseBody, out, mod);
2026-01-01 22:18:25 -05:00
return;
}
if (std::holds_alternative<Stmt::While>(s.node)) {
const auto& wh = std::get<Stmt::While>(s.node);
collectFieldsFromBlock(wh.body, out, mod);
2026-01-01 22:18:25 -05:00
return;
}
if (std::holds_alternative<Stmt::Repeat>(s.node)) {
const auto& rp = std::get<Stmt::Repeat>(s.node);
collectFieldsFromBlock(rp.body, out, mod);
2026-01-01 22:18:25 -05:00
return;
}
if (std::holds_alternative<Stmt::ForRange>(s.node)) {
const auto& fr = std::get<Stmt::ForRange>(s.node);
add(fr.var, UiFieldKind::GenericValue, false, true, "", {});
collectFieldsFromBlock(fr.body, out, mod);
return;
}
// Handle CallStmt for parse_here - merge UI fields from referenced type
if (std::holds_alternative<Stmt::CallStmt>(s.node)) {
const auto& cs = std::get<Stmt::CallStmt>(s.node);
if (cs.call && std::holds_alternative<Expr::Call>(cs.call->node)) {
const auto& call = std::get<Expr::Call>(cs.call->node);
// Handle parse_here - merge UI fields from referenced type
if (call.fn == "parse_here" && !call.args.isEmpty() && mod) {
if (std::holds_alternative<Expr::String>(call.args[0]->node)) {
const QString typeName = std::get<Expr::String>(call.args[0]->node).v;
if (mod->types.contains(typeName)) {
const TypeDef& childTd = mod->types[typeName];
collectFieldsFromBlock(childTd.body, out, mod);
}
}
}
}
2026-01-01 22:18:25 -05:00
return;
}
}
inline void collectFieldsFromBlock(const QVector<StmtPtr>& blk, QMap<QString, UiField>& out, const Module* mod) {
2026-01-01 22:18:25 -05:00
for (const auto& sp : blk) {
if (!sp) continue;
collectFieldsFromStmt(*sp, out, mod);
2026-01-01 22:18:25 -05:00
}
}
inline QMap<QString, UiField> buildUiSchemaForType(const TypeDef& td, const Module* mod = nullptr) {
2026-01-01 22:18:25 -05:00
QMap<QString, UiField> fields;
collectFieldsFromBlock(td.criteria, fields, mod);
collectFieldsFromBlock(td.body, fields, mod);
2026-01-01 22:18:25 -05:00
return fields;
}
#endif // DSLUISCHEMA_H