private static void FlushMarble(StringBuilder marbleBuilder, List <Moment> retVal, int marbleTime, int time) { if (marbleBuilder.Length <= 0) { return; } var marble = marbleBuilder.ToString(); marbleBuilder.Clear(); retVal.Add(Moment.Single(marbleTime, marble)); AddEmptyMoments(retVal, time); }
private static IEnumerable <Moment> ParseSequence(string sequence, out int?timeOffset) { var retVal = new List <Moment>(); var time = 0; var groupTime = 0; timeOffset = null; List <string> groupMarbles = null; List <string> unorderedGroupMarbles = null; var parsingState = ParsingState.NotInGroup; var marbleTime = 0; var marbleBuilder = new StringBuilder(); foreach (var character in sequence.Trim()) { if (parsingState == ParsingState.NotInGroup) { switch (character) { case ' ': case '-': FlushMarble(marbleBuilder, retVal, marbleTime, time); retVal.Add(Moment.Empty(time)); break; case '^': FlushMarble(marbleBuilder, retVal, marbleTime, time); if (timeOffset.HasValue) { throw new ArgumentException("Only one ^ character allowed", nameof(sequence)); } timeOffset = -time; retVal.Add(Moment.Single(time, character.ToString())); break; case '(': FlushMarble(marbleBuilder, retVal, marbleTime, time); groupTime = time; parsingState = ParsingState.InOrderedGroup; groupMarbles = new List <string>(); break; case '<': FlushMarble(marbleBuilder, retVal, marbleTime, time); groupTime = time; parsingState = ParsingState.InUnorderedGroup; unorderedGroupMarbles = new List <string>(); break; case ')': throw new ArgumentException("Closing parentheses without opening parentheses", nameof(sequence)); case '>': throw new ArgumentException("Closing brace without opening brace", nameof(sequence)); case ',': throw new ArgumentException("Comma should not occur outside of a group", nameof(sequence)); default: GrowMarble(marbleBuilder, ref marbleTime, time, character); break; } } else if (parsingState == ParsingState.InOrderedGroup) { switch (character) { case ',': case ' ': FlushMarbleToGroup(marbleBuilder, groupMarbles); break; case '-': throw new ArgumentException("Cannot use - within a group", nameof(sequence)); case '^': if (timeOffset.HasValue) { throw new ArgumentException("Only one ^ character allowed", nameof(sequence)); } FlushMarbleToGroup(marbleBuilder, groupMarbles); timeOffset = -groupTime; groupMarbles.Add(character.ToString()); break; case '(': throw new ArgumentException("Cannot have nested parentheses", nameof(sequence)); case ')': FlushMarbleToGroup(marbleBuilder, groupMarbles); if (groupMarbles.Count <= 1) { throw new ArgumentException("Only groups with multiple marbles are allowed", nameof(sequence)); } retVal.Add(Moment.OrderedGroup(groupTime, groupMarbles.ToArray())); groupMarbles = null; parsingState = ParsingState.NotInGroup; AddEmptyMoments(retVal, time + 1); break; default: GrowMarble(marbleBuilder, ref marbleTime, groupTime, character); break; } } else if (parsingState == ParsingState.InUnorderedGroup) { switch (character) { case ',': case ' ': FlushMarbleToGroup(marbleBuilder, unorderedGroupMarbles); break; case '-': throw new ArgumentException("Cannot use - within a group", nameof(sequence)); case '^': if (timeOffset.HasValue) { throw new ArgumentException("Only one ^ character allowed", nameof(sequence)); } FlushMarbleToGroup(marbleBuilder, unorderedGroupMarbles); timeOffset = -groupTime; unorderedGroupMarbles.Add(character.ToString()); break; case '>': FlushMarbleToGroup(marbleBuilder, unorderedGroupMarbles); if (unorderedGroupMarbles.Count <= 1) { throw new ArgumentException("Only groups with multiple marbles are allowed", nameof(sequence)); } retVal.Add(Moment.UnorderedGroup(groupTime, unorderedGroupMarbles.ToArray())); unorderedGroupMarbles = null; parsingState = ParsingState.NotInGroup; AddEmptyMoments(retVal, time + 1); break; case '<': throw new ArgumentException("Cannot have braces nested in an unordered group", nameof(sequence)); case '(': throw new ArgumentException("Cannot have parentheses nested in an unordered group", nameof(sequence)); case ')': throw new ArgumentException("Cannot have parentheses nested in an unordered group", nameof(sequence)); default: GrowMarble(marbleBuilder, ref marbleTime, groupTime, character); break; } } time++; } FlushMarble(marbleBuilder, retVal, marbleTime, time); if (parsingState == ParsingState.InOrderedGroup) { throw new ArgumentException("Opening parentheses without closing parentheses", nameof(sequence)); } if (parsingState == ParsingState.InUnorderedGroup) { throw new ArgumentException("Opening brace without closing brace", nameof(sequence)); } return(retVal); }