/// <summary> /// Parses text that can have escape characters and cleans up the string /// by removing double \t, \n and spaces /// </summary> /// <param name="stream"></param> /// <returns></returns> public string ParseAndCleanTextWithEscape(TomeStream stream) { StringBuilder sb = new StringBuilder(); // this will get all the text up to a star which is the delimiter while (stream.PeekChar() != '*') { // just in case i'll keep this here but i may remove it later // i actually need this to delimit the star if (stream.PeekChar() == '\\') { sb.Append(stream.NextChar()); // add whatever is next regardless of what it is sb.Append(stream.NextChar()); } else { sb.Append(stream.NextChar()); } } // this is slower but also safer string clean = CleanString(sb.ToString()); string decoded = DecodeString(clean); return(decoded); }
private bool AssertChar(TomeStream stream, char c) { if (stream.PeekChar() != c) { String msg = String.Format("Expected {0} symbol but found {1}, at position {2}", c, stream.PeekChar(), stream.Position); throw new Exception(msg); } stream.NextChar(); return(true); }
public static void EatWhitespace(TomeStream stream) { while (Char.IsWhiteSpace(stream.PeekChar())) { stream.NextChar(); } }
private bool ParseArrow(TomeStream stream) { if (stream.PeekChar() == '-') { stream.NextChar(); return(AssertChar(stream, '>')); } return(false); }
private string FetchNonStarSubString() { StringBuilder sb = new StringBuilder(); while (Stream.PeekChar() != '*') { sb.Append(Stream.NextChar()); } return(sb.ToString()); }
private SpeechText ParseSpeechText(TomeStream stream, ref ParsingState state) { SpeechText st = new SpeechText(); state = ParsingState.ExpectingMore; // parse text here string text = ParseAndCleanTextWithEscape(stream); // make sure the text ends in a star if (stream.PeekChar() == '*') { stream.NextChar(); } else { String msg = String.Format("Expected * to end speech parsing but found {0} at {1}", stream.PeekChar(), stream.Position); throw new Exception(msg); } // parse the attribute if (stream.PeekChar() == '[') { // this closes the paren for us if there is any st.Attributes = ParseAttributes(stream); } EatWhitespace(stream); // check if it ended // at this point we know there is at least 1 star // if this is true then this will be the last speech text if (stream.PeekChar() == '}') { stream.NextChar(); state = ParsingState.Complete; } st.Text = text; return(st); }
public Node ParseDialog(TomeStream stream) { Node node = new Node(); var data = new Dialog(); // first things first, parse character name and then semicolon data.Character = ParseCharacterName(stream); // check for colon if (stream.PeekChar() != '{') { String msg = String.Format("Expected : or {, found {0}, at {1}", stream.PeekChar(), stream.Position); throw new Exception(msg); } else { stream.NextChar(); } // parsing the text then attributes of the text // getting rid of the starting whitespace EatWhitespace(stream); ParsingState state = ParsingState.ExpectingMore; while (state == ParsingState.ExpectingMore) { var text = ParseSpeechText(stream, ref state); data.Text.Add(text); } if (stream.PeekChar() == '[') { data.Attributes = ParseAttributes(stream); } // everything has been completed for this section. // ParseSpeechText will eat the final * node.Data = data; return(node); }
// only parses a-zA-Z and returns it as a string /// <summary> /// Parses a-zA-Z0-9 but nothing else /// </summary> /// <param name="stream"></param> /// <returns></returns> public string ParseOnlyTextNoEscape(TomeStream stream) { StringBuilder sb = new StringBuilder(); while (Char.IsLetterOrDigit(stream.PeekChar())) { sb.Append(stream.NextChar()); } return(sb.ToString()); }
private static string GrabSubstringToParse(TomeStream stream) { StringBuilder sb = new StringBuilder(); while (stream.PeekChar() != ']') { sb.Append(stream.NextChar()); } return(sb.ToString()); }
/// <summary> /// Parse a character name. Allows for spaces, dashes, single quotes and dots /// </summary> /// <param name="stream"></param> /// <returns></returns> public string ParseCharacterName(TomeStream stream) { StringBuilder sb = new StringBuilder(); HashSet <char> charsToAllow = new HashSet <char>(new char[] { '.', '\'', '-', '(', ')', ' ' }); while (stream.PeekChar() != '{') { if (Char.IsLetterOrDigit(stream.PeekChar()) || charsToAllow.Contains(stream.PeekChar())) { sb.Append(stream.NextChar()); } else { throw new Exceptions.InvalidCharacterNameException(String.Format("Character name cannot contain: '{0}'. Location: {1}", stream.PeekChar(), stream.Position)); } } // for now just remove spaces at the end of the string if (!char.IsLetterOrDigit(sb[sb.Length - 1])) { } return(sb.ToString()); }
// public for testing purpose // this chooses what is parsed next, IE a branch, a dialog or descision public BrigitGraph ParseBrigitGraph(TomeStream stream) { BrigitGraph ll = new BrigitGraph(); while (!(stream.Complete() || stream.PeekChar() == '}')) { // getting rid of some beginning whitespace EatWhitespace(stream); // the real parsing starts here // can't use switch need to use if elses char c = stream.PeekChar(); if (Char.IsLetterOrDigit(c)) { // start parsing as a dialog // this one is simple. parse the dialog. then add // it to the list Node n = ParseDialog(stream); // for the new AddInBetween function // this will only work for whatever comes next. This isn't very good // if there's multiple branches to place // TODO make this functionality work better with dummy tail in the subgraphs foreach (KeyValuePair <string, OpenChoice> kvp in BranchesToPlace) { if (kvp.Value.TailNode == null) { kvp.Value.TailNode = n; break; } } ll.AddNode(n); } else if (c == '@') { // naybe use a struct here? Dictionary <string, OpenChoice> branchesToNode = new Dictionary <string, OpenChoice>(); // TODO change signature of ParseDescision to (obj stream, Dict brachesToNode) BrigitGraph subGraph = ParseDescision(stream, branchesToNode); foreach (KeyValuePair <string, OpenChoice> kvp in branchesToNode) { BranchesToPlace.Add(kvp.Key, kvp.Value); } // adding the dictionary entries to this ll.AddGraph(subGraph); } else if (c == '{') { // this is a branch selector // we can just pass in the big dictionary BrigitGraph subGraph = ParseBranchSelector(BranchesToPlace); ll.AddGraph(subGraph); } else if (c == '>') { // this is a branch name // TODO change signature of ParseBranch to ParseBranch(stream, ref string branchName) // TODO i'll probably need a wrapper for the Node and Ch entires string branchName = String.Empty; BrigitGraph subGraph = ParseBranch(stream, ref branchName); if (BranchesToPlace.ContainsKey(branchName)) { OpenChoice openCh = BranchesToPlace[branchName]; Node n = openCh.EnclosingNode; Choice ch = openCh.BranchingChoice; ll.AddInBetween(n, new List <Node>() { openCh.TailNode }, subGraph); ch.NextNode = n.Next.Count - 1; } else { String msg = String.Format("{0} not found in dictionary, could the name be mispelled?", branchName); throw new Exception(msg); } } else { // panic here String msg = String.Format("Expected beginning of character name, branch or chioce but found {0} at {1}", stream.PeekChar(), stream.Position); throw new Exception(msg); } EatWhitespace(stream); } if (!stream.Complete()) { AssertChar(stream, '}'); } return(ll); }
public BrigitGraph ParseDescision(TomeStream stream, Dictionary <string, OpenChoice> branchEndings) { Decision descision = new Decision(); BrigitGraph ll = new BrigitGraph(); Node root = new Node() { Data = descision }; ll.AddNode(root); // first parse away the @player: AssertChar(stream, '@'); AssertAlphaDigitString(stream, "player"); AssertChar(stream, '{'); EatWhitespace(stream); ParsingState state = ParsingState.ExpectingMore; while (state == ParsingState.ExpectingMore) { // parse the choice (same as parse text with esacape) // parse attributes if there are any Choice ch = ParseChoice(stream, ref state); // -1 is the place holder for now. will be set to an actual number // or to the size of list ch.NextNode = -1; descision.Choices.Add(ch); // at this point either the parsing is complete // or there is an arrow pointing to a sub branch or a branch name // Parseing.Expecting more implies that the arrow maybe still be here // Some branches have multiple "nexts" where the next either points to the same node // or to two different ones within the same branching pattern. // It's the addition of the nodes that's wrong if (ParseArrow(stream)) { // whitespace EatWhitespace(stream); // either it's a branch if (stream.PeekChar() == '{') { AssertChar(stream, '{'); // parse the subbranch if there is one // parse the branch name is there is one, and we didn't parse a sub branch // add the subbranch to the next list if there is none set their "next" to -1 BrigitGraph subGraph = ParseBrigitGraph(stream); ll.AddBranch(root, subGraph); ch.NextNode = root.Next.Count - 1; } // or a branch name else { string BranchName = ParseOnlyTextNoEscape(stream); OpenChoice openCh = new OpenChoice(root, ch); branchEndings.Add(BranchName, openCh); } // this means it has reach the end if (stream.PeekChar() == '}') { state = ParsingState.Complete; stream.NextChar(); } } } // any next nodes in descision that are -1 should be set // to the count of the list foreach (Choice c in descision.Choices) { if (c.NextNode == -1) { c.NextNode = root.Next.Count; } } return(ll); }