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 | |
31 | QT_BEGIN_NAMESPACE |
32 | |
33 | namespace DocumentModel { |
34 | |
35 | struct XmlLocation |
36 | { |
37 | int line; |
38 | int column; |
39 | |
40 | XmlLocation(int theLine, int theColumn): line(theLine), column(theColumn) {} |
41 | }; |
42 | |
43 | struct If; |
44 | struct Send; |
45 | struct Invoke; |
46 | struct Script; |
47 | struct AbstractState; |
48 | struct State; |
49 | struct Transition; |
50 | struct HistoryState; |
51 | struct Scxml; |
52 | class NodeVisitor; |
53 | struct 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 | |
70 | private: |
71 | Q_DISABLE_COPY(Node) |
72 | }; |
73 | |
74 | struct 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 | |
85 | struct 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 | |
95 | struct 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 | |
105 | struct Instruction: public Node |
106 | { |
107 | Instruction(const XmlLocation &xmlLocation): Node(xmlLocation) {} |
108 | virtual ~Instruction() {} |
109 | }; |
110 | |
111 | typedef QList<Instruction *> InstructionSequence; |
112 | typedef QList<InstructionSequence *> InstructionSequences; |
113 | |
114 | struct 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 | |
136 | struct ScxmlDocument; |
137 | struct 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 | |
157 | struct Raise: public Instruction |
158 | { |
159 | QString event; |
160 | |
161 | Raise(const XmlLocation &xmlLocation): Instruction(xmlLocation) {} |
162 | void accept(NodeVisitor *visitor) override; |
163 | }; |
164 | |
165 | struct Log: public Instruction |
166 | { |
167 | QString label, expr; |
168 | |
169 | Log(const XmlLocation &xmlLocation): Instruction(xmlLocation) {} |
170 | void accept(NodeVisitor *visitor) override; |
171 | }; |
172 | |
173 | struct 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 | |
183 | struct 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 | |
193 | struct 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 | |
203 | struct 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 | |
214 | struct 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 | |
223 | struct StateOrTransition: public Node |
224 | { |
225 | StateOrTransition(const XmlLocation &xmlLocation): Node(xmlLocation) {} |
226 | }; |
227 | |
228 | struct 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 | |
243 | struct AbstractState: public StateContainer |
244 | { |
245 | QString id; |
246 | |
247 | AbstractState *asAbstractState() override { return this; } |
248 | }; |
249 | |
250 | struct 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 | |
283 | struct 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 | |
304 | struct 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 | |
328 | struct 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 ; |
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 | |
371 | struct 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 | |
444 | class Q_SCXML_EXPORT NodeVisitor |
445 | { |
446 | public: |
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 *> ¶ms) |
509 | { |
510 | for (Param *param : params) { |
511 | Q_ASSERT(param); |
512 | param->accept(visitor: this); |
513 | } |
514 | } |
515 | }; |
516 | |
517 | } // DocumentModel namespace |
518 | |
519 | class Q_SCXML_EXPORT QScxmlCompilerPrivate |
520 | { |
521 | public: |
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 | |
551 | private: |
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 | |
620 | private: |
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 | |
669 | public: |
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 | |
679 | private: |
680 | bool checkAttributes(const QXmlStreamAttributes &attributes, QScxmlCompilerPrivate::ParserState::Kind kind); |
681 | ParserState ¤t(); |
682 | ParserState &previous(); |
683 | bool hasPrevious() const; |
684 | |
685 | private: |
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 | |
699 | QT_END_NAMESPACE |
700 | |
701 | #endif // QSCXMLCOMPILER_P_H |
702 | |