You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
5.4 KiB
240 lines
5.4 KiB
/* |
|
This file is part of Konsole, a terminal emulator for KDE. |
|
|
|
SPDX-FileCopyrightText: 2018 Mariusz Glebocki <mglb@arccos-1.net> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#ifndef TEMPLATE_H |
|
#define TEMPLATE_H |
|
|
|
#include <QMap> |
|
#include <QString> |
|
#include <QVector> |
|
|
|
// QVariant doesn't offer modification in place. Var does. |
|
class Var |
|
{ |
|
public: |
|
using Number = qint64; |
|
using String = QString; |
|
using Map = QMap<String, Var>; |
|
using Vector = QVector<Var>; |
|
|
|
enum class DataType { |
|
Invalid, |
|
Number, |
|
String, |
|
Vector, |
|
Map, |
|
}; |
|
|
|
const QString dataTypeAsString() const |
|
{ |
|
switch (dataType()) { |
|
case DataType::Invalid: |
|
return QStringLiteral("Invalid"); |
|
case DataType::Number: |
|
return QStringLiteral("Number"); |
|
case DataType::String: |
|
return QStringLiteral("String"); |
|
case DataType::Vector: |
|
return QStringLiteral("Vector"); |
|
case DataType::Map: |
|
return QStringLiteral("Map"); |
|
default: |
|
return QStringLiteral("Unknown?"); |
|
} |
|
} |
|
|
|
Var() |
|
: num(0) |
|
, _dataType(DataType::Invalid) |
|
{ |
|
} |
|
Var(const Var &other) |
|
{ |
|
*this = other; |
|
} |
|
|
|
Var(const Number &newNum) |
|
: _dataType(DataType::Number) |
|
{ |
|
new (&num) auto(newNum); |
|
} |
|
Var(const String &newStr) |
|
: _dataType(DataType::String) |
|
{ |
|
new (&str) auto(newStr); |
|
} |
|
Var(const Vector &newVec) |
|
: _dataType(DataType::Vector) |
|
{ |
|
new (&vec) auto(newVec); |
|
} |
|
Var(const Map &newMap) |
|
: _dataType(DataType::Map) |
|
{ |
|
new (&map) auto(newMap); |
|
} |
|
|
|
// Allow initialization without type name |
|
Var(const char *newStr) |
|
: _dataType(DataType::String) |
|
{ |
|
new (&str) String(QString::fromUtf8(newStr)); |
|
} |
|
Var(std::initializer_list<Var> newVec) |
|
: _dataType(DataType::Vector) |
|
{ |
|
new (&vec) Vector(newVec); |
|
} |
|
|
|
~Var() |
|
{ |
|
switch (dataType()) { |
|
case DataType::String: |
|
str.~String(); |
|
break; |
|
case DataType::Vector: |
|
vec.~Vector(); |
|
break; |
|
case DataType::Map: |
|
map.~Map(); |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
Var &operator=(const Var &other) |
|
{ |
|
_dataType = other.dataType(); |
|
switch (other.dataType()) { |
|
case DataType::Number: |
|
new (&num) auto(other.num); |
|
break; |
|
case DataType::String: |
|
new (&str) auto(other.str); |
|
break; |
|
case DataType::Vector: |
|
new (&vec) auto(other.vec); |
|
break; |
|
case DataType::Map: |
|
new (&map) auto(other.map); |
|
break; |
|
default: |
|
break; |
|
} |
|
return *this; |
|
} |
|
|
|
Var &operator[](unsigned index) |
|
{ |
|
Q_ASSERT(_dataType == DataType::Vector); |
|
return vec.data()[index]; |
|
} |
|
const Var &operator[](unsigned index) const |
|
{ |
|
Q_ASSERT(_dataType == DataType::Vector); |
|
return vec.constData()[index]; |
|
} |
|
Var &operator[](const String &key) |
|
{ |
|
Q_ASSERT(_dataType == DataType::Map); |
|
return map[key]; |
|
} |
|
const Var &operator[](const String &key) const |
|
{ |
|
Q_ASSERT(_dataType == DataType::Map); |
|
return *map.find(key); |
|
} |
|
|
|
DataType dataType() const |
|
{ |
|
return _dataType; |
|
} |
|
|
|
union { |
|
Number num; |
|
String str; |
|
Vector vec; |
|
Map map; |
|
}; |
|
|
|
private: |
|
DataType _dataType; |
|
}; |
|
|
|
class Template |
|
{ |
|
public: |
|
Template(const QString &text); |
|
void parse(); |
|
QString generate(const Var &data); |
|
|
|
struct Element { |
|
Element(const Element *parent = nullptr, const QString &name = QString()) |
|
: outer() |
|
, inner() |
|
, name(name) |
|
, fmt() |
|
, line(0) |
|
, column(0) |
|
, isComment(false) |
|
, children() |
|
, parent(parent) |
|
{ |
|
} |
|
|
|
Element(const Element &other) |
|
: outer(other.outer) |
|
, inner(other.inner) |
|
, name(other.name) |
|
, fmt(other.fmt) |
|
, line(other.line) |
|
, column(other.column) |
|
, isComment(other.isComment) |
|
, parent(other.parent) |
|
{ |
|
for (const auto &child : other.children) { |
|
children.append(child); |
|
} |
|
} |
|
|
|
const QString findFmt(Var::DataType type) const; |
|
QString path() const; |
|
bool isCommand() const |
|
{ |
|
return name.startsWith(QLatin1Char('!')); |
|
} |
|
bool hasName() const |
|
{ |
|
return !isCommand() && !name.isEmpty(); |
|
} |
|
|
|
static const QString defaultFmt(Var::DataType type); |
|
static bool isValidFmt(const QString &fmt, Var::DataType type); |
|
|
|
QStringRef outer; |
|
QStringRef inner; |
|
QString name; |
|
QString fmt; |
|
uint line; |
|
uint column; |
|
bool isComment; |
|
QList<Element> children; |
|
const Element *parent; |
|
}; |
|
|
|
private: |
|
void executeCommand(Element &element, const Element &childStub, const QStringList &argv); |
|
void parseRecursively(Element &element); |
|
int generateRecursively(QString &result, const Element &element, const Var &data, int consumed = 0); |
|
|
|
QString _text; // FIXME: make it pointer (?) |
|
Element _root; // FIXME: make it pointer |
|
}; |
|
|
|
#endif
|
|
|