using System.Collections.Generic; using System.Linq; using Ink.Parsed; namespace Ink { public partial class InkParser { protected Sequence InnerSequence() { Whitespace (); // Default sequence type SequenceType seqType = SequenceType.Stopping; // Optional explicit sequence type SequenceType? parsedSeqType = (SequenceType?) Parse(SequenceTypeAnnotation); if (parsedSeqType != null) seqType = parsedSeqType.Value; var contentLists = Parse(InnerSequenceObjects); if (contentLists == null || contentLists.Count <= 1) { return null; } return new Sequence (contentLists, seqType); } protected object SequenceTypeAnnotation() { var annotation = (SequenceType?) Parse(SequenceTypeSymbolAnnotation); if(annotation == null) annotation = (SequenceType?) Parse(SequenceTypeWordAnnotation); if (annotation == null) return null; switch (annotation.Value) { case SequenceType.Once: case SequenceType.Cycle: case SequenceType.Stopping: case SequenceType.Shuffle: case (SequenceType.Shuffle | SequenceType.Stopping): case (SequenceType.Shuffle | SequenceType.Once): break; default: Error("Sequence type combination not supported: " + annotation.Value); return SequenceType.Stopping; } return annotation; } protected object SequenceTypeSymbolAnnotation() { if(_sequenceTypeSymbols == null ) _sequenceTypeSymbols = new CharacterSet("!&~$ "); var sequenceType = (SequenceType)0; var sequenceAnnotations = ParseCharactersFromCharSet(_sequenceTypeSymbols); if (sequenceAnnotations == null) return null; foreach(char symbolChar in sequenceAnnotations) { switch(symbolChar) { case '!': sequenceType |= SequenceType.Once; break; case '&': sequenceType |= SequenceType.Cycle; break; case '~': sequenceType |= SequenceType.Shuffle; break; case '$': sequenceType |= SequenceType.Stopping; break; } } if (sequenceType == (SequenceType)0) return null; return sequenceType; } CharacterSet _sequenceTypeSymbols = new CharacterSet("!&~$"); protected object SequenceTypeWordAnnotation() { var sequenceTypes = Interleave<SequenceType?>(SequenceTypeSingleWord, Exclude(Whitespace)); if (sequenceTypes == null || sequenceTypes.Count == 0) return null; if (ParseString (":") == null) return null; var combinedSequenceType = (SequenceType)0; foreach(var seqType in sequenceTypes) { combinedSequenceType |= seqType.Value; } return combinedSequenceType; } protected object SequenceTypeSingleWord() { SequenceType? seqType = null; var word = Parse(IdentifierWithMetadata); if (word != null) { switch (word.name) { case "once": seqType = SequenceType.Once; break; case "cycle": seqType = SequenceType.Cycle; break; case "shuffle": seqType = SequenceType.Shuffle; break; case "stopping": seqType = SequenceType.Stopping; break; } } if (seqType == null) return null; return seqType; } protected List<ContentList> InnerSequenceObjects() { var multiline = Parse(Newline) != null; List<ContentList> result = null; if (multiline) { result = Parse(InnerMultilineSequenceObjects); } else { result = Parse(InnerInlineSequenceObjects); } return result; } protected List<ContentList> InnerInlineSequenceObjects() { var interleavedContentAndPipes = Interleave<object> (Optional (MixedTextAndLogic), String ("|"), flatten:false); if (interleavedContentAndPipes == null) return null; var result = new List<ContentList> (); // The content and pipes won't necessarily be perfectly interleaved in the sense that // the content can be missing, but in that case it's intended that there's blank content. bool justHadContent = false; foreach (object contentOrPipe in interleavedContentAndPipes) { // Pipe/separator if (contentOrPipe as string == "|") { // Expected content, saw pipe - need blank content now if (!justHadContent) { // Add blank content result.Add (new ContentList ()); } justHadContent = false; } // Real content else { var content = contentOrPipe as List<Parsed.Object>; if (content == null) { Error ("Expected content, but got " + contentOrPipe + " (this is an ink compiler bug!)"); } else { result.Add (new ContentList (content)); } justHadContent = true; } } // Ended in a pipe? Need to insert final blank content if (!justHadContent) result.Add (new ContentList ()); return result; } protected List<ContentList> InnerMultilineSequenceObjects() { MultilineWhitespace (); var contentLists = OneOrMore (SingleMultilineSequenceElement); if (contentLists == null) return null; return contentLists.Cast<ContentList> ().ToList(); } protected ContentList SingleMultilineSequenceElement() { Whitespace (); // Make sure we're not accidentally parsing a divert if (ParseString ("->") != null) return null; if (ParseString ("-") == null) return null; Whitespace (); List<Parsed.Object> content = StatementsAtLevel (StatementLevel.InnerBlock); if (content == null) MultilineWhitespace (); // Add newline at the start of each branch else { content.Insert (0, new Parsed.Text ("\n")); } return new ContentList (content); } } }