1 
2 //          Copyright Ferdinand Majerech 2011.
3 // Distributed under the Boost Software License, Version 1.0.
4 //    (See accompanying file LICENSE_1_0.txt or copy at
5 //          http://www.boost.org/LICENSE_1_0.txt)
6 
7 /**
8  * YAML events.
9  * Code based on PyYAML: http://www.pyyaml.org
10  */
11 module dyaml.event;
12 
13 import std.array;
14 import std.conv;
15 
16 import dyaml.exception;
17 import dyaml.reader;
18 import dyaml.tagdirective;
19 import dyaml.style;
20 
21 
22 package:
23 ///Event types.
24 enum EventID : ubyte
25 {
26     invalid = 0,     /// Invalid (uninitialized) event.
27     streamStart,     /// Stream start
28     streamEnd,       /// Stream end
29     documentStart,   /// Document start
30     documentEnd,     /// Document end
31     alias_,           /// Alias
32     scalar,          /// Scalar
33     sequenceStart,   /// Sequence start
34     sequenceEnd,     /// Sequence end
35     mappingStart,    /// Mapping start
36     mappingEnd       /// Mapping end
37 }
38 
39 /**
40  * YAML event produced by parser.
41  *
42  * 48 bytes on 64bit.
43  */
44 struct Event
45 {
46     @disable int opCmp(ref Event);
47 
48     ///Value of the event, if any.
49     string value;
50     ///Start position of the event in file/stream.
51     Mark startMark;
52     ///End position of the event in file/stream.
53     Mark endMark;
54     union
55     {
56         struct
57         {
58             ///Anchor of the event, if any.
59             string _anchor;
60             ///Tag of the event, if any.
61             string _tag;
62         }
63         ///Tag directives, if this is a DocumentStart.
64         //TagDirectives tagDirectives;
65         TagDirective[] _tagDirectives;
66     }
67     ///Event type.
68     EventID id = EventID.invalid;
69     ///Style of scalar event, if this is a scalar event.
70     ScalarStyle scalarStyle = ScalarStyle.invalid;
71     union
72     {
73         ///Should the tag be implicitly resolved?
74         bool implicit;
75         /**
76          * Is this document event explicit?
77          *
78          * Used if this is a DocumentStart or DocumentEnd.
79          */
80         bool explicitDocument;
81     }
82     ///Collection style, if this is a SequenceStart or MappingStart.
83     CollectionStyle collectionStyle = CollectionStyle.invalid;
84 
85     ///Is this a null (uninitialized) event?
86     @property bool isNull() const pure @safe nothrow {return id == EventID.invalid;}
87 
88     ///Get string representation of the token ID.
89     @property string idString() const @safe {return to!string(id);}
90 
91     auto ref anchor() inout @trusted pure {
92         assert(id != EventID.documentStart, "DocumentStart events cannot have anchors.");
93         return _anchor;
94     }
95 
96     auto ref tag() inout @trusted pure {
97         assert(id != EventID.documentStart, "DocumentStart events cannot have tags.");
98         return _tag;
99     }
100 
101     auto ref tagDirectives() inout @trusted pure {
102         assert(id == EventID.documentStart, "Only DocumentStart events have tag directives.");
103         return _tagDirectives;
104     }
105 }
106 
107 /**
108  * Construct a simple event.
109  *
110  * Params:  start    = Start position of the event in the file/stream.
111  *          end      = End position of the event in the file/stream.
112  *          anchor   = Anchor, if this is an alias event.
113  */
114 Event event(EventID id)(const Mark start, const Mark end, const string anchor = null)
115     @safe
116     in(!(id == EventID.alias_ && anchor == ""), "Missing anchor for alias event")
117 {
118     Event result;
119     result.startMark = start;
120     result.endMark   = end;
121     result.anchor    = anchor;
122     result.id        = id;
123     return result;
124 }
125 
126 /**
127  * Construct a collection (mapping or sequence) start event.
128  *
129  * Params:  start    = Start position of the event in the file/stream.
130  *          end      = End position of the event in the file/stream.
131  *          anchor   = Anchor of the sequence, if any.
132  *          tag      = Tag of the sequence, if specified.
133  *          implicit = Should the tag be implicitly resolved?
134  *          style = Style to use when outputting document.
135  */
136 Event collectionStartEvent(EventID id)
137     (const Mark start, const Mark end, const string anchor, const string tag,
138      const bool implicit, const CollectionStyle style) pure @safe nothrow
139 {
140     static assert(id == EventID.sequenceStart || id == EventID.sequenceEnd ||
141                   id == EventID.mappingStart || id == EventID.mappingEnd);
142     Event result;
143     result.startMark       = start;
144     result.endMark         = end;
145     result.anchor          = anchor;
146     result.tag             = tag;
147     result.id              = id;
148     result.implicit        = implicit;
149     result.collectionStyle = style;
150     return result;
151 }
152 
153 /**
154  * Construct a stream start event.
155  *
156  * Params:  start    = Start position of the event in the file/stream.
157  *          end      = End position of the event in the file/stream.
158  */
159 Event streamStartEvent(const Mark start, const Mark end)
160     pure @safe nothrow
161 {
162     Event result;
163     result.startMark = start;
164     result.endMark   = end;
165     result.id        = EventID.streamStart;
166     return result;
167 }
168 
169 ///Aliases for simple events.
170 alias streamEndEvent = event!(EventID.streamEnd);
171 alias aliasEvent = event!(EventID.alias_);
172 alias sequenceEndEvent = event!(EventID.sequenceEnd);
173 alias mappingEndEvent = event!(EventID.mappingEnd);
174 
175 ///Aliases for collection start events.
176 alias sequenceStartEvent = collectionStartEvent!(EventID.sequenceStart);
177 alias mappingStartEvent = collectionStartEvent!(EventID.mappingStart);
178 
179 /**
180  * Construct a document start event.
181  *
182  * Params:  start         = Start position of the event in the file/stream.
183  *          end           = End position of the event in the file/stream.
184  *          explicit      = Is this an explicit document start?
185  *          YAMLVersion   = YAML version string of the document.
186  *          tagDirectives = Tag directives of the document.
187  */
188 Event documentStartEvent(const Mark start, const Mark end, const bool explicit, string YAMLVersion,
189                          TagDirective[] tagDirectives) pure @safe nothrow
190 {
191     Event result;
192     result.value            = YAMLVersion;
193     result.startMark        = start;
194     result.endMark          = end;
195     result.id               = EventID.documentStart;
196     result.explicitDocument = explicit;
197     result.tagDirectives    = tagDirectives;
198     return result;
199 }
200 
201 /**
202  * Construct a document end event.
203  *
204  * Params:  start    = Start position of the event in the file/stream.
205  *          end      = End position of the event in the file/stream.
206  *          explicit = Is this an explicit document end?
207  */
208 Event documentEndEvent(const Mark start, const Mark end, const bool explicit) pure @safe nothrow
209 {
210     Event result;
211     result.startMark        = start;
212     result.endMark          = end;
213     result.id               = EventID.documentEnd;
214     result.explicitDocument = explicit;
215     return result;
216 }
217 
218 /// Construct a scalar event.
219 ///
220 /// Params:  start    = Start position of the event in the file/stream.
221 ///          end      = End position of the event in the file/stream.
222 ///          anchor   = Anchor of the scalar, if any.
223 ///          tag      = Tag of the scalar, if specified.
224 ///          implicit = Should the tag be implicitly resolved?
225 ///          value    = String value of the scalar.
226 ///          style    = Scalar style.
227 Event scalarEvent(const Mark start, const Mark end, const string anchor, const string tag,
228                   const bool implicit, const string value,
229                   const ScalarStyle style = ScalarStyle.invalid) @safe pure nothrow @nogc
230 {
231     Event result;
232     result.value       = value;
233     result.startMark   = start;
234     result.endMark     = end;
235 
236     result.anchor  = anchor;
237     result.tag     = tag;
238 
239     result.id          = EventID.scalar;
240     result.scalarStyle = style;
241     result.implicit    = implicit;
242     return result;
243 }