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.
179 lines
5.7 KiB
179 lines
5.7 KiB
/* |
|
This file is part of Konsole, a terminal emulator for KDE. |
|
|
|
Copyright 2018 by Mariusz Glebocki <mglb@arccos-1.net> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program; if not, write to the Free Software |
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
02110-1301 USA. |
|
*/ |
|
|
|
#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
|
|
|