1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QSCXMLCOMPILER_P_H
5#define QSCXMLCOMPILER_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include "qscxmlcompiler.h"
19
20#include <QtCore/qdir.h>
21#include <QtCore/qfileinfo.h>
22#include <QtCore/qset.h>
23#include <QtCore/qsharedpointer.h>
24#include <QtCore/qstringlist.h>
25#include <QtCore/qstring.h>
26#include <QtCore/qxmlstream.h>
27#include <QtCore/private/qglobal_p.h>
28
29#include <memory>
30
31QT_BEGIN_NAMESPACE
32
33namespace DocumentModel {
34
35struct XmlLocation
36{
37 int line;
38 int column;
39
40 XmlLocation(int theLine, int theColumn): line(theLine), column(theColumn) {}
41};
42
43struct If;
44struct Send;
45struct Invoke;
46struct Script;
47struct AbstractState;
48struct State;
49struct Transition;
50struct HistoryState;
51struct Scxml;
52class NodeVisitor;
53struct Node {
54 XmlLocation xmlLocation;
55
56 Node(const XmlLocation &theLocation): xmlLocation(theLocation) {}
57 virtual ~Node();
58 virtual void accept(NodeVisitor *visitor) = 0;
59
60 virtual If *asIf() { return nullptr; }
61 virtual Send *asSend() { return nullptr; }
62 virtual Invoke *asInvoke() { return nullptr; }
63 virtual Script *asScript() { return nullptr; }
64 virtual State *asState() { return nullptr; }
65 virtual Transition *asTransition() { return nullptr; }
66 virtual HistoryState *asHistoryState() { return nullptr; }
67 virtual Scxml *asScxml() { return nullptr; }
68 AbstractState *asAbstractState();
69
70private:
71 Q_DISABLE_COPY(Node)
72};
73
74struct DataElement: public Node
75{
76 QString id;
77 QString src;
78 QString expr;
79 QString content;
80
81 DataElement(const XmlLocation &xmlLocation): Node(xmlLocation) {}
82 void accept(NodeVisitor *visitor) override;
83};
84
85struct Param: public Node
86{
87 QString name;
88 QString expr;
89 QString location;
90
91 Param(const XmlLocation &xmlLocation): Node(xmlLocation) {}
92 void accept(NodeVisitor *visitor) override;
93};
94
95struct DoneData: public Node
96{
97 QString contents;
98 QString expr;
99 QList<Param *> params;
100
101 DoneData(const XmlLocation &xmlLocation): Node(xmlLocation) {}
102 void accept(NodeVisitor *visitor) override;
103};
104
105struct Instruction: public Node
106{
107 Instruction(const XmlLocation &xmlLocation): Node(xmlLocation) {}
108 virtual ~Instruction() {}
109};
110
111typedef QList<Instruction *> InstructionSequence;
112typedef QList<InstructionSequence *> InstructionSequences;
113
114struct Send: public Instruction
115{
116 QString event;
117 QString eventexpr;
118 QString type;
119 QString typeexpr;
120 QString target;
121 QString targetexpr;
122 QString id;
123 QString idLocation;
124 QString delay;
125 QString delayexpr;
126 QStringList namelist;
127 QList<Param *> params;
128 QString content;
129 QString contentexpr;
130
131 Send(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
132 Send *asSend() override { return this; }
133 void accept(NodeVisitor *visitor) override;
134};
135
136struct ScxmlDocument;
137struct Invoke: public Instruction
138{
139 QString type;
140 QString typeexpr;
141 QString src;
142 QString srcexpr;
143 QString id;
144 QString idLocation;
145 QStringList namelist;
146 bool autoforward;
147 QList<Param *> params;
148 InstructionSequence finalize;
149
150 QSharedPointer<ScxmlDocument> content;
151
152 Invoke(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
153 Invoke *asInvoke() override { return this; }
154 void accept(NodeVisitor *visitor) override;
155};
156
157struct Raise: public Instruction
158{
159 QString event;
160
161 Raise(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
162 void accept(NodeVisitor *visitor) override;
163};
164
165struct Log: public Instruction
166{
167 QString label, expr;
168
169 Log(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
170 void accept(NodeVisitor *visitor) override;
171};
172
173struct Script: public Instruction
174{
175 QString src;
176 QString content;
177
178 Script(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
179 Script *asScript() override { return this; }
180 void accept(NodeVisitor *visitor) override;
181};
182
183struct Assign: public Instruction
184{
185 QString location;
186 QString expr;
187 QString content;
188
189 Assign(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
190 void accept(NodeVisitor *visitor) override;
191};
192
193struct If: public Instruction
194{
195 QStringList conditions;
196 InstructionSequences blocks;
197
198 If(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
199 If *asIf() override { return this; }
200 void accept(NodeVisitor *visitor) override;
201};
202
203struct Foreach: public Instruction
204{
205 QString array;
206 QString item;
207 QString index;
208 InstructionSequence block;
209
210 Foreach(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
211 void accept(NodeVisitor *visitor) override;
212};
213
214struct Cancel: public Instruction
215{
216 QString sendid;
217 QString sendidexpr;
218
219 Cancel(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
220 void accept(NodeVisitor *visitor) override;
221};
222
223struct StateOrTransition: public Node
224{
225 StateOrTransition(const XmlLocation &xmlLocation): Node(xmlLocation) {}
226};
227
228struct StateContainer
229{
230 StateContainer()
231 : parent(nullptr)
232 {}
233
234 StateContainer *parent;
235
236 virtual ~StateContainer() {}
237 virtual void add(StateOrTransition *s) = 0;
238 virtual AbstractState *asAbstractState() { return nullptr; }
239 virtual State *asState() { return nullptr; }
240 virtual Scxml *asScxml() { return nullptr; }
241};
242
243struct AbstractState: public StateContainer
244{
245 QString id;
246
247 AbstractState *asAbstractState() override { return this; }
248};
249
250struct State: public AbstractState, public StateOrTransition
251{
252 enum Type { Normal, Parallel, Final };
253
254 QStringList initial;
255 QList<DataElement *> dataElements;
256 QList<StateOrTransition *> children;
257 InstructionSequences onEntry;
258 InstructionSequences onExit;
259 DoneData *doneData;
260 QList<Invoke *> invokes;
261 Type type;
262
263 Transition *initialTransition; // when not set, it is filled during verification
264
265 State(const XmlLocation &xmlLocation)
266 : StateOrTransition(xmlLocation)
267 , doneData(nullptr)
268 , type(Normal)
269 , initialTransition(nullptr)
270 {}
271
272 void add(StateOrTransition *s) override
273 {
274 Q_ASSERT(s);
275 children.append(t: s);
276 }
277
278 State *asState() override { return this; }
279
280 void accept(NodeVisitor *visitor) override;
281};
282
283struct Transition: public StateOrTransition
284{
285 enum Type { Internal, External, Synthetic };
286 QStringList events;
287 QScopedPointer<QString> condition;
288 QStringList targets;
289 InstructionSequence instructionsOnTransition;
290 Type type;
291
292 QList<AbstractState *> targetStates; // when not set, it is filled during verification
293
294 Transition(const XmlLocation &xmlLocation)
295 : StateOrTransition(xmlLocation)
296 , type(External)
297 {}
298
299 Transition *asTransition() override { return this; }
300
301 void accept(NodeVisitor *visitor) override;
302};
303
304struct HistoryState: public AbstractState, public StateOrTransition
305{
306 enum Type { Deep, Shallow };
307 Type type;
308 QList<StateOrTransition *> children;
309
310 HistoryState(const XmlLocation &xmlLocation)
311 : StateOrTransition(xmlLocation)
312 , type(Shallow)
313 {}
314
315 void add(StateOrTransition *s) override
316 {
317 Q_ASSERT(s);
318 children.append(t: s);
319 }
320
321 Transition *defaultConfiguration()
322 { return children.isEmpty() ? nullptr : children.first()->asTransition(); }
323
324 HistoryState *asHistoryState() override { return this; }
325 void accept(NodeVisitor *visitor) override;
326};
327
328struct Scxml: public StateContainer, public Node
329{
330 enum DataModelType {
331 NullDataModel,
332 JSDataModel,
333 CppDataModel
334 };
335 enum BindingMethod {
336 EarlyBinding,
337 LateBinding
338 };
339
340 QStringList initial;
341 QString name;
342 DataModelType dataModel;
343 QString cppDataModelClassName;
344 QString cppDataModelHeaderName;
345 BindingMethod binding;
346 QList<StateOrTransition *> children;
347 QList<DataElement *> dataElements;
348 QScopedPointer<Script> script;
349 InstructionSequence initialSetup;
350
351 Transition *initialTransition;
352
353 Scxml(const XmlLocation &xmlLocation)
354 : Node(xmlLocation)
355 , dataModel(NullDataModel)
356 , binding(EarlyBinding)
357 , initialTransition(nullptr)
358 {}
359
360 void add(StateOrTransition *s) override
361 {
362 Q_ASSERT(s);
363 children.append(t: s);
364 }
365
366 Scxml *asScxml() override { return this; }
367
368 void accept(NodeVisitor *visitor) override;
369};
370
371struct ScxmlDocument
372{
373 const QString fileName;
374 Scxml *root;
375 QList<AbstractState *> allStates;
376 QList<Transition *> allTransitions;
377 QList<Node *> allNodes;
378 QList<InstructionSequence *> allSequences;
379 QList<ScxmlDocument *> allSubDocuments; // weak pointers
380 bool isVerified;
381
382 ScxmlDocument(const QString &fileName)
383 : fileName(fileName)
384 , root(nullptr)
385 , isVerified(false)
386 {}
387
388 ~ScxmlDocument()
389 {
390 delete root;
391 qDeleteAll(c: allNodes);
392 qDeleteAll(c: allSequences);
393 }
394
395 State *newState(StateContainer *parent, State::Type type, const XmlLocation &xmlLocation)
396 {
397 Q_ASSERT(parent);
398 State *s = newNode<State>(xmlLocation);
399 s->parent = parent;
400 s->type = type;
401 allStates.append(t: s);
402 parent->add(s);
403 return s;
404 }
405
406 HistoryState *newHistoryState(StateContainer *parent, const XmlLocation &xmlLocation)
407 {
408 Q_ASSERT(parent);
409 HistoryState *s = newNode<HistoryState>(xmlLocation);
410 s->parent = parent;
411 allStates.append(t: s);
412 parent->add(s);
413 return s;
414 }
415
416 Transition *newTransition(StateContainer *parent, const XmlLocation &xmlLocation)
417 {
418 Transition *t = newNode<Transition>(xmlLocation);
419 allTransitions.append(t);
420 if (parent != nullptr) {
421 parent->add(s: t);
422 }
423 return t;
424 }
425
426 template<typename T>
427 T *newNode(const XmlLocation &xmlLocation)
428 {
429 T *node = new T(xmlLocation);
430 allNodes.append(node);
431 return node;
432 }
433
434 InstructionSequence *newSequence(InstructionSequences *container)
435 {
436 Q_ASSERT(container);
437 InstructionSequence *is = new InstructionSequence;
438 allSequences.append(t: is);
439 container->append(t: is);
440 return is;
441 }
442};
443
444class Q_SCXML_EXPORT NodeVisitor
445{
446public:
447 virtual ~NodeVisitor();
448
449 virtual void visit(DataElement *) {}
450 virtual void visit(Param *) {}
451 virtual bool visit(DoneData *) { return true; }
452 virtual void endVisit(DoneData *) {}
453 virtual bool visit(Send *) { return true; }
454 virtual void endVisit(Send *) {}
455 virtual bool visit(Invoke *) { return true; }
456 virtual void endVisit(Invoke *) {}
457 virtual void visit(Raise *) {}
458 virtual void visit(Log *) {}
459 virtual void visit(Script *) {}
460 virtual void visit(Assign *) {}
461 virtual bool visit(If *) { return true; }
462 virtual void endVisit(If *) {}
463 virtual bool visit(Foreach *) { return true; }
464 virtual void endVisit(Foreach *) {}
465 virtual void visit(Cancel *) {}
466 virtual bool visit(State *) { return true; }
467 virtual void endVisit(State *) {}
468 virtual bool visit(Transition *) { return true; }
469 virtual void endVisit(Transition *) {}
470 virtual bool visit(HistoryState *) { return true; }
471 virtual void endVisit(HistoryState *) {}
472 virtual bool visit(Scxml *) { return true; }
473 virtual void endVisit(Scxml *) {}
474
475 void visit(InstructionSequence *sequence)
476 {
477 Q_ASSERT(sequence);
478 for (Instruction *instruction : std::as_const(t&: *sequence)) {
479 Q_ASSERT(instruction);
480 instruction->accept(visitor: this);
481 }
482 }
483
484 void visit(const QList<DataElement *> &dataElements)
485 {
486 for (DataElement *dataElement : dataElements) {
487 Q_ASSERT(dataElement);
488 dataElement->accept(visitor: this);
489 }
490 }
491
492 void visit(const QList<StateOrTransition *> &children)
493 {
494 for (StateOrTransition *child : children) {
495 Q_ASSERT(child);
496 child->accept(visitor: this);
497 }
498 }
499
500 void visit(const InstructionSequences &sequences)
501 {
502 for (InstructionSequence *sequence : sequences) {
503 Q_ASSERT(sequence);
504 visit(sequence);
505 }
506 }
507
508 void visit(const QList<Param *> &params)
509 {
510 for (Param *param : params) {
511 Q_ASSERT(param);
512 param->accept(visitor: this);
513 }
514 }
515};
516
517} // DocumentModel namespace
518
519class Q_SCXML_EXPORT QScxmlCompilerPrivate
520{
521public:
522 static QScxmlCompilerPrivate *get(QScxmlCompiler *compiler);
523
524 QScxmlCompilerPrivate(QXmlStreamReader *reader);
525
526 bool verifyDocument();
527 DocumentModel::ScxmlDocument *scxmlDocument() const;
528
529 QString fileName() const;
530 void setFileName(const QString &fileName);
531
532 QScxmlCompiler::Loader *loader() const;
533 void setLoader(QScxmlCompiler::Loader *loader);
534
535 bool readDocument();
536 void parseSubDocument(DocumentModel::Invoke *parentInvoke,
537 QXmlStreamReader *reader,
538 const QString &fileName);
539 bool parseSubElement(DocumentModel::Invoke *parentInvoke,
540 QXmlStreamReader *reader,
541 const QString &fileName);
542 QByteArray load(const QString &name, bool *ok);
543
544 QList<QScxmlError> errors() const;
545
546 void addError(const QString &msg);
547 void addError(const DocumentModel::XmlLocation &location, const QString &msg);
548 QScxmlStateMachine *instantiateStateMachine() const;
549 void instantiateDataModel(QScxmlStateMachine *stateMachine) const;
550
551private:
552 DocumentModel::AbstractState *currentParent() const;
553 DocumentModel::XmlLocation xmlLocation() const;
554 bool maybeId(const QXmlStreamAttributes &attributes, QString *id);
555 DocumentModel::If *lastIf();
556 bool checkAttributes(const QXmlStreamAttributes &attributes,
557 const QStringList &requiredNames,
558 const QStringList &optionalNames);
559
560 bool preReadElementScxml();
561 bool preReadElementState();
562 bool preReadElementParallel();
563 bool preReadElementInitial();
564 bool preReadElementTransition();
565 bool preReadElementFinal();
566 bool preReadElementHistory();
567 bool preReadElementOnEntry();
568 bool preReadElementOnExit();
569 bool preReadElementRaise();
570 bool preReadElementIf();
571 bool preReadElementElseIf();
572 bool preReadElementElse();
573 bool preReadElementForeach();
574 bool preReadElementLog();
575 bool preReadElementDataModel();
576 bool preReadElementData();
577 bool preReadElementAssign();
578 bool preReadElementDoneData();
579 bool preReadElementContent();
580 bool preReadElementParam();
581 bool preReadElementScript();
582 bool preReadElementSend();
583 bool preReadElementCancel();
584 bool preReadElementInvoke();
585 bool preReadElementFinalize();
586
587 bool postReadElementScxml();
588 bool postReadElementState();
589 bool postReadElementParallel();
590 bool postReadElementInitial();
591 bool postReadElementTransition();
592 bool postReadElementFinal();
593 bool postReadElementHistory();
594 bool postReadElementOnEntry();
595 bool postReadElementOnExit();
596 bool postReadElementRaise();
597 bool postReadElementIf();
598 bool postReadElementElseIf();
599 bool postReadElementElse();
600 bool postReadElementForeach();
601 bool postReadElementLog();
602 bool postReadElementDataModel();
603 bool postReadElementData();
604 bool postReadElementAssign();
605 bool postReadElementDoneData();
606 bool postReadElementContent();
607 bool postReadElementParam();
608 bool postReadElementScript();
609 bool postReadElementSend();
610 bool postReadElementCancel();
611 bool postReadElementInvoke();
612 bool postReadElementFinalize();
613
614 bool readElement();
615
616 void resetDocument();
617 void currentStateUp();
618 bool flushInstruction();
619
620private:
621 struct ParserState {
622 enum Kind {
623 Scxml,
624 State,
625 Parallel,
626 Transition,
627 Initial,
628 Final,
629 OnEntry,
630 OnExit,
631 History,
632 Raise,
633 If,
634 ElseIf,
635 Else,
636 Foreach,
637 Log,
638 DataModel,
639 Data,
640 Assign,
641 DoneData,
642 Content,
643 Param,
644 Script,
645 Send,
646 Cancel,
647 Invoke,
648 Finalize,
649 None
650 };
651 Kind kind;
652 QString chars;
653 DocumentModel::Instruction *instruction;
654 DocumentModel::InstructionSequence *instructionContainer;
655
656 bool collectChars();
657
658 ParserState(Kind someKind = None);
659 ~ParserState() { }
660
661 bool validChild(ParserState::Kind child) const;
662 static bool validChild(ParserState::Kind parent, ParserState::Kind child);
663 static bool isExecutableContent(ParserState::Kind kind);
664 static Kind nameToParserStateKind(QStringView name);
665 static QStringList requiredAttributes(Kind kind);
666 static QStringList optionalAttributes(Kind kind);
667 };
668
669public:
670 class DefaultLoader: public QScxmlCompiler::Loader
671 {
672 public:
673 DefaultLoader();
674 QByteArray load(const QString &name,
675 const QString &baseDir,
676 QStringList *errors) override final;
677 };
678
679private:
680 bool checkAttributes(const QXmlStreamAttributes &attributes, QScxmlCompilerPrivate::ParserState::Kind kind);
681 ParserState &current();
682 ParserState &previous();
683 bool hasPrevious() const;
684
685private:
686 QString m_fileName;
687 QSet<QString> m_allIds;
688
689 std::unique_ptr<DocumentModel::ScxmlDocument> m_doc;
690 DocumentModel::StateContainer *m_currentState;
691 DefaultLoader m_defaultLoader;
692 QScxmlCompiler::Loader *m_loader;
693
694 QXmlStreamReader *m_reader;
695 QList<ParserState> m_stack;
696 QList<QScxmlError> m_errors;
697};
698
699QT_END_NAMESPACE
700
701#endif // QSCXMLCOMPILER_P_H
702

source code of qtscxml/src/scxml/qscxmlcompiler_p.h