From c56b682750e9f2a911a841e89e4e51b7d0608ab5 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Sun, 12 Apr 2020 13:31:11 +0100 Subject: added support for parsing paths --- src/data/graph.h | 1 + src/data/graphelementdata.cpp | 8 ++++ src/data/graphelementdata.h | 1 + src/data/path.cpp | 6 +++ src/data/path.h | 19 ++++++++++ src/data/tikzassembler.cpp | 41 ++++++++++++++++++++ src/data/tikzassembler.h | 14 +++++++ src/data/tikzparser.y | 88 ++++++++++++++++++++++++++++--------------- src/data/tikzparserdefs.h | 2 + tikzit.pro | 2 + 10 files changed, 151 insertions(+), 31 deletions(-) create mode 100644 src/data/path.cpp create mode 100644 src/data/path.h diff --git a/src/data/graph.h b/src/data/graph.h index 286ccdc..a996bcb 100644 --- a/src/data/graph.h +++ b/src/data/graph.h @@ -25,6 +25,7 @@ #include "node.h" #include "edge.h" +#include "path.h" #include "graphelementdata.h" #include diff --git a/src/data/graphelementdata.cpp b/src/data/graphelementdata.cpp index cd09a6d..931f86a 100644 --- a/src/data/graphelementdata.cpp +++ b/src/data/graphelementdata.cpp @@ -110,6 +110,14 @@ int GraphElementData::indexOfKey(QString key) return -1; } +void GraphElementData::mergeData(GraphElementData *d) +{ + GraphElementProperty p; + foreach (p, d->properties()) { + if (!hasProperty(p.key())) add(p); + } +} + bool GraphElementData::removeRows(int row, int /*count*/, const QModelIndex &parent) { if (row >= 0 && row < _properties.length()) { diff --git a/src/data/graphelementdata.h b/src/data/graphelementdata.h index dce0d46..8a50a93 100644 --- a/src/data/graphelementdata.h +++ b/src/data/graphelementdata.h @@ -49,6 +49,7 @@ public: bool hasProperty(QString key); bool atom(QString atom); int indexOfKey(QString key); + void mergeData(GraphElementData *d); bool removeRows(int row, int count, const QModelIndex &parent) override; bool moveRows(const QModelIndex &sourceParent, int sourceRow, int, diff --git a/src/data/path.cpp b/src/data/path.cpp new file mode 100644 index 0000000..f213b22 --- /dev/null +++ b/src/data/path.cpp @@ -0,0 +1,6 @@ +#include "path.h" + +Path::Path(QObject *parent) : QObject(parent) +{ + +} diff --git a/src/data/path.h b/src/data/path.h new file mode 100644 index 0000000..381d486 --- /dev/null +++ b/src/data/path.h @@ -0,0 +1,19 @@ +#ifndef PATH_H +#define PATH_H + +#include "edge.h" + +#include + +class Path : public QObject +{ + Q_OBJECT +public: + explicit Path(QObject *parent = nullptr); + +private: + QVector _edges; + +}; + +#endif // PATH_H diff --git a/src/data/tikzassembler.cpp b/src/data/tikzassembler.cpp index cd0b517..ee75f7b 100644 --- a/src/data/tikzassembler.cpp +++ b/src/data/tikzassembler.cpp @@ -29,6 +29,7 @@ TikzAssembler::TikzAssembler(Graph *graph, QObject *parent) : { yylex_init(&scanner); yyset_extra(this, scanner); + _currentEdgeData = nullptr; } TikzAssembler::TikzAssembler(TikzStyles *tikzStyles, QObject *parent) : @@ -36,6 +37,7 @@ TikzAssembler::TikzAssembler(TikzStyles *tikzStyles, QObject *parent) : { yylex_init(&scanner); yyset_extra(this, scanner); + _currentEdgeData = nullptr; } void TikzAssembler::addNodeToMap(Node *n) { _nodeMap.insert(n->name(), n); } @@ -70,3 +72,42 @@ bool TikzAssembler::isTikzStyles() const return _tikzStyles != 0; } +Node *TikzAssembler::currentEdgeSource() const +{ + return _currentEdgeSource; +} + +void TikzAssembler::setCurrentEdgeSource(Node *currentEdgeSource) +{ + _currentEdgeSource = currentEdgeSource; +} + +GraphElementData *TikzAssembler::currentEdgeData() const +{ + return _currentEdgeData; +} + +void TikzAssembler::setCurrentEdgeData(GraphElementData *currentEdgeData) +{ + _currentEdgeData = currentEdgeData; +} + +QString TikzAssembler::currentEdgeSourceAnchor() const +{ + return _currentEdgeSourceAnchor; +} + +void TikzAssembler::setCurrentEdgeSourceAnchor(const QString ¤tEdgeSourceAnchor) +{ + _currentEdgeSourceAnchor = currentEdgeSourceAnchor; +} + +void TikzAssembler::finishCurrentPath() +{ + if (_currentEdgeData) { + delete _currentEdgeData; + _currentEdgeData = nullptr; + } + // TODO: create a path and add it to graph +} + diff --git a/src/data/tikzassembler.h b/src/data/tikzassembler.h index 7b32224..f3cabb6 100644 --- a/src/data/tikzassembler.h +++ b/src/data/tikzassembler.h @@ -46,6 +46,17 @@ public: bool isTikzStyles() const; + Node *currentEdgeSource() const; + void setCurrentEdgeSource(Node *currentEdgeSource); + + GraphElementData *currentEdgeData() const; + void setCurrentEdgeData(GraphElementData *currentEdgeData); + + QString currentEdgeSourceAnchor() const; + void setCurrentEdgeSourceAnchor(const QString ¤tEdgeSourceAnchor); + + void finishCurrentPath(); + signals: public slots: @@ -54,6 +65,9 @@ private: QHash _nodeMap; Graph *_graph; TikzStyles *_tikzStyles; + Node *_currentEdgeSource; + GraphElementData *_currentEdgeData; + QString _currentEdgeSourceAnchor; void *scanner; }; diff --git a/src/data/tikzparser.y b/src/data/tikzparser.y index 4473107..895f75d 100644 --- a/src/data/tikzparser.y +++ b/src/data/tikzparser.y @@ -32,13 +32,13 @@ /* we use features added to bison 2.4 */ %require "2.3" -%error-verbose +%define parse.error verbose /* enable maintaining locations for better error messages */ %locations /* the name of the header file */ /*%defines "common/tikzparser.h"*/ /* make it re-entrant (no global variables) */ -%pure-parser +%define api.pure /* We use a pure (re-entrant) lexer. This means yylex will take a void* (opaque) type to maintain its state */ %lex-param {void *scanner} @@ -214,10 +214,13 @@ noderef: "(" REFSTRING optanchor ")" $$.node = assembler->nodeWithName(QString($2)); free($2); $$.anchor = $3; + $$.loop = false; + $$.cycle = false; }; optnoderef: - noderef { $$ = $1; } - | "(" ")" { $$.node = 0; $$.anchor = 0; } + noderef { $$ = $1; } + | "(" ")" { $$.node = 0; $$.anchor = 0; $$.loop = true; $$.cycle = false; } + | "cycle" { $$.node = 0; $$.anchor = 0; $$.loop = false; $$.cycle = true; } optedgenode: { $$ = 0; } | "node" optproperties DELIMITEDSTRING @@ -228,45 +231,68 @@ optedgenode: $$->setLabel(QString($3)); free($3); } -edge: "\\draw" optproperties noderef "to" optedgenode optnoderef ";" - { - Node *s; + +edgesource: optproperties noderef { + assembler->setCurrentEdgeSource($2.node); + if ($2.anchor) { + assembler->setCurrentEdgeSourceAnchor(QString($2.anchor)); + free($2.anchor); + } else { + assembler->setCurrentEdgeSourceAnchor(QString()); + } + assembler->setCurrentEdgeData($1); + } + +optedgetargets: edgetarget optedgetargets | + +edgetarget: "to" optproperties optedgenode optnoderef { + Node *s = assembler->currentEdgeSource();; Node *t; - - s = $3.node; - if ($6.node) { - t = $6.node; + if ($4.loop) { + t = assembler->currentEdgeSource(); + } else if ($4.cycle) { + // TODO: should be source of first edge in path + t = assembler->currentEdgeSource(); } else { - t = s; + t = $4.node; } - // if the source or the target of the edge doesn't exist, quietly ignore it. - if (s != 0 && t != 0) { - Edge *edge = new Edge(s, t); - if ($2) { - edge->setData($2); - edge->setAttributesFromData(); - } + if (s != 0 && t != 0) { // if source or target don't exist, quietly ignore edge + Edge *e = new Edge(s, t); + assembler->setCurrentEdgeSource(t); - if ($5) - edge->setEdgeNode($5); - if ($3.anchor) { - edge->setSourceAnchor(QString($3.anchor)); - free($3.anchor); + if (!assembler->currentEdgeSourceAnchor().isEmpty()) { + e->setSourceAnchor(assembler->currentEdgeSourceAnchor()); } - if ($6.node) { - if ($6.anchor) { - edge->setTargetAnchor(QString($6.anchor)); - free($6.anchor); - } + if ($4.anchor) { + QString a($4.anchor); + free($4.anchor); + e->setTargetAnchor(a); + assembler->setCurrentEdgeSourceAnchor(a); } else { - edge->setTargetAnchor(edge->sourceAnchor()); + assembler->setCurrentEdgeSourceAnchor(QString()); } - assembler->graph()->addEdge(edge); + if ($3) e->setEdgeNode($3); + + GraphElementData *cd = assembler->currentEdgeData(); + if ($2) { + if (cd) $2->mergeData(cd); + e->setData($2); + } else { + if (cd) e->setData(cd->copy()); + } + e->setAttributesFromData(); + assembler->graph()->addEdge(e); } + } + + +edge: "\\draw" edgesource edgetarget optedgetargets ";" + { + assembler->finishCurrentPath(); }; ignoreprop: val | val "=" val; diff --git a/src/data/tikzparserdefs.h b/src/data/tikzparserdefs.h index 02743fe..da60bbf 100644 --- a/src/data/tikzparserdefs.h +++ b/src/data/tikzparserdefs.h @@ -33,6 +33,8 @@ struct noderef { Node *node; char *anchor; + bool cycle; + bool loop; }; inline int isatty(int) { return 0; } diff --git a/tikzit.pro b/tikzit.pro index e0f3544..878caaa 100644 --- a/tikzit.pro +++ b/tikzit.pro @@ -53,6 +53,7 @@ include(flex.pri) include(bison.pri) SOURCES += src/gui/mainwindow.cpp \ + src/data/path.cpp \ src/gui/toolpalette.cpp \ src/gui/tikzscene.cpp \ src/data/graph.cpp \ @@ -85,6 +86,7 @@ SOURCES += src/gui/mainwindow.cpp \ src/gui/preferencedialog.cpp HEADERS += src/gui/mainwindow.h \ + src/data/path.h \ src/gui/toolpalette.h \ src/gui/tikzscene.h \ src/data/graph.h \ -- cgit v1.2.3