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 ///Should the tag be implicitly resolved? 72 bool implicit; 73 /** 74 * Is this document event explicit? 75 * 76 * Used if this is a DocumentStart or DocumentEnd. 77 */ 78 alias explicitDocument = implicit; 79 ///Collection style, if this is a SequenceStart or MappingStart. 80 CollectionStyle collectionStyle = CollectionStyle.invalid; 81 82 ///Is this a null (uninitialized) event? 83 @property bool isNull() const pure @safe nothrow {return id == EventID.invalid;} 84 85 ///Get string representation of the token ID. 86 @property string idString() const @safe {return to!string(id);} 87 88 auto ref anchor() inout @trusted pure { 89 assert(id != EventID.documentStart, "DocumentStart events cannot have anchors."); 90 return _anchor; 91 } 92 93 auto ref tag() inout @trusted pure { 94 assert(id != EventID.documentStart, "DocumentStart events cannot have tags."); 95 return _tag; 96 } 97 98 auto ref tagDirectives() inout @trusted pure { 99 assert(id == EventID.documentStart, "Only DocumentStart events have tag directives."); 100 return _tagDirectives; 101 } 102 void toString(W)(ref W writer) const 103 { 104 import std.algorithm.iteration : substitute; 105 import std.format : formattedWrite; 106 import std.range : put; 107 final switch (id) 108 { 109 case EventID.scalar: 110 put(writer, "=VAL "); 111 if (anchor != "") 112 { 113 writer.formattedWrite!"&%s " (anchor); 114 } 115 if (tag != "") 116 { 117 writer.formattedWrite!"<%s> " (tag); 118 } 119 final switch(scalarStyle) 120 { 121 case ScalarStyle.singleQuoted: 122 put(writer, "'"); 123 break; 124 case ScalarStyle.doubleQuoted: 125 put(writer, "\""); 126 break; 127 case ScalarStyle.literal: 128 put(writer, "|"); 129 break; 130 case ScalarStyle.folded: 131 put(writer, ">"); 132 break; 133 case ScalarStyle.invalid: //default to plain 134 case ScalarStyle.plain: 135 put(writer, ":"); 136 break; 137 } 138 if (value != "") 139 { 140 writer.formattedWrite!"%s"(value.substitute("\n", "\\n", `\`, `\\`, "\r", "\\r", "\t", "\\t", "\b", "\\b")); 141 } 142 break; 143 case EventID.streamStart: 144 put(writer, "+STR"); 145 break; 146 case EventID.documentStart: 147 put(writer, "+DOC"); 148 if (explicitDocument) 149 { 150 put(writer, " ---"); 151 } 152 break; 153 case EventID.mappingStart: 154 put(writer, "+MAP"); 155 if (collectionStyle == CollectionStyle.flow) 156 { 157 put(writer, " {}"); 158 } 159 if (anchor != "") 160 { 161 put(writer, " &"); 162 put(writer, anchor); 163 } 164 if (tag != "") 165 { 166 put(writer, " <"); 167 put(writer, tag); 168 put(writer, ">"); 169 } 170 break; 171 case EventID.sequenceStart: 172 put(writer, "+SEQ"); 173 if (collectionStyle == CollectionStyle.flow) 174 { 175 put(writer, " []"); 176 } 177 if (anchor != "") 178 { 179 put(writer, " &"); 180 put(writer, anchor); 181 } 182 if (tag != "") 183 { 184 put(writer, " <"); 185 put(writer, tag); 186 put(writer, ">"); 187 } 188 break; 189 case EventID.streamEnd: 190 put(writer, "-STR"); 191 break; 192 case EventID.documentEnd: 193 put(writer, "-DOC"); 194 if (explicitDocument) 195 { 196 put(writer, " ..."); 197 } 198 break; 199 case EventID.mappingEnd: 200 put(writer, "-MAP"); 201 break; 202 case EventID.sequenceEnd: 203 put(writer, "-SEQ"); 204 break; 205 case EventID.alias_: 206 put(writer, "=ALI *"); 207 put(writer, anchor); 208 break; 209 case EventID.invalid: 210 assert(0, "Invalid EventID produced"); 211 } 212 } 213 } 214 215 /** 216 * Construct a simple event. 217 * 218 * Params: start = Start position of the event in the file/stream. 219 * end = End position of the event in the file/stream. 220 * anchor = Anchor, if this is an alias event. 221 */ 222 Event event(EventID id)(const Mark start, const Mark end, const string anchor = null) 223 @safe 224 in(!(id == EventID.alias_ && anchor == ""), "Missing anchor for alias event") 225 { 226 Event result; 227 result.startMark = start; 228 result.endMark = end; 229 result.anchor = anchor; 230 result.id = id; 231 return result; 232 } 233 234 /** 235 * Construct a collection (mapping or sequence) start event. 236 * 237 * Params: start = Start position of the event in the file/stream. 238 * end = End position of the event in the file/stream. 239 * anchor = Anchor of the sequence, if any. 240 * tag = Tag of the sequence, if specified. 241 * implicit = Should the tag be implicitly resolved? 242 * style = Style to use when outputting document. 243 */ 244 Event collectionStartEvent(EventID id) 245 (const Mark start, const Mark end, const string anchor, const string tag, 246 const bool implicit, const CollectionStyle style) pure @safe nothrow 247 { 248 static assert(id == EventID.sequenceStart || id == EventID.sequenceEnd || 249 id == EventID.mappingStart || id == EventID.mappingEnd); 250 Event result; 251 result.startMark = start; 252 result.endMark = end; 253 result.anchor = anchor; 254 result.tag = tag; 255 result.id = id; 256 result.implicit = implicit; 257 result.collectionStyle = style; 258 return result; 259 } 260 261 /** 262 * Construct a stream start event. 263 * 264 * Params: start = Start position of the event in the file/stream. 265 * end = End position of the event in the file/stream. 266 */ 267 Event streamStartEvent(const Mark start, const Mark end) 268 pure @safe nothrow 269 { 270 Event result; 271 result.startMark = start; 272 result.endMark = end; 273 result.id = EventID.streamStart; 274 return result; 275 } 276 277 ///Aliases for simple events. 278 alias streamEndEvent = event!(EventID.streamEnd); 279 alias aliasEvent = event!(EventID.alias_); 280 alias sequenceEndEvent = event!(EventID.sequenceEnd); 281 alias mappingEndEvent = event!(EventID.mappingEnd); 282 283 ///Aliases for collection start events. 284 alias sequenceStartEvent = collectionStartEvent!(EventID.sequenceStart); 285 alias mappingStartEvent = collectionStartEvent!(EventID.mappingStart); 286 287 /** 288 * Construct a document start event. 289 * 290 * Params: start = Start position of the event in the file/stream. 291 * end = End position of the event in the file/stream. 292 * explicit = Is this an explicit document start? 293 * YAMLVersion = YAML version string of the document. 294 * tagDirectives = Tag directives of the document. 295 */ 296 Event documentStartEvent(const Mark start, const Mark end, const bool explicit, string YAMLVersion, 297 TagDirective[] tagDirectives) pure @safe nothrow 298 { 299 Event result; 300 result.value = YAMLVersion; 301 result.startMark = start; 302 result.endMark = end; 303 result.id = EventID.documentStart; 304 result.explicitDocument = explicit; 305 result.tagDirectives = tagDirectives; 306 return result; 307 } 308 309 /** 310 * Construct a document end event. 311 * 312 * Params: start = Start position of the event in the file/stream. 313 * end = End position of the event in the file/stream. 314 * explicit = Is this an explicit document end? 315 */ 316 Event documentEndEvent(const Mark start, const Mark end, const bool explicit) pure @safe nothrow 317 { 318 Event result; 319 result.startMark = start; 320 result.endMark = end; 321 result.id = EventID.documentEnd; 322 result.explicitDocument = explicit; 323 return result; 324 } 325 326 /// Construct a scalar event. 327 /// 328 /// Params: start = Start position of the event in the file/stream. 329 /// end = End position of the event in the file/stream. 330 /// anchor = Anchor of the scalar, if any. 331 /// tag = Tag of the scalar, if specified. 332 /// implicit = Should the tag be implicitly resolved? 333 /// value = String value of the scalar. 334 /// style = Scalar style. 335 Event scalarEvent(const Mark start, const Mark end, const string anchor, const string tag, 336 const bool implicit, const string value, 337 const ScalarStyle style = ScalarStyle.invalid) @safe pure nothrow @nogc 338 { 339 Event result; 340 result.value = value; 341 result.startMark = start; 342 result.endMark = end; 343 344 result.anchor = anchor; 345 result.tag = tag; 346 347 result.id = EventID.scalar; 348 result.scalarStyle = style; 349 result.implicit = implicit; 350 return result; 351 }