private ParserState ParseFunction(ParserState state, string funcName) { var builder = Utils.GetTempStringBuilder(); var args = new List <Node>(); while (!(state.Char() == Sym.cLineBreak || state.Done())) { if (state.MatchAhead(Sym.CodeOpen)) { var codeState = new ParserState(new BlockNode(BlockMode.Code), state.ConsumeBlock(Sym.CodeOpen, Sym.CodeClose)); codeState = ParseCode(codeState); args.Add(codeState.RootNode); builder.Length = 0; } else if (state.MatchAhead(Sym.String)) { var str = state.ConsumeBlock(Sym.String, Sym.String); args.Add(new LiteralNode(str)); builder.Length = 0; } else if (state.Char() == ' ' && builder.Length > 0) { args.Add(StringToValueNode(builder.ToString().Trim())); builder.Length = 0; } else { builder.Append(state.Char()); } state.Step(); } if (builder.Length > 0) { args.Add(StringToValueNode(builder.ToString().Trim())); builder.Length = 0; } Utils.Release(builder); state.CurNode.AddChild(new FuncNode(funcName, args.ToArray())); return(state); }
private ParserState ParseCode(ParserState state) { string name; while (!state.Done()) { if (char.IsWhiteSpace(state.Char())) { state.Step(); } else if (state.MatchAhead(Sym.CodeOpen)) { state = ParseCodeBlock(state); } // NOTE: nested dialog blocks disabled for now //else if (state.MatchAhead(Sym.DialogOpen)) //{ // state = ParseDialogBlock(state); //} else if (state.Char() == Sym.cList && state.Peek("").IndexOf('?') >= 0) { state = ParseIf(state); } else if (HasFunction(name = state.Peek(" "))) { state.Step(name.Length); state = ParseFunction(state, name); } else if (IsSequence(name = state.Peek(" \n"))) { state.Step(name.Length); state = ParseSequence(state, name); } else { state = ParseExpression(state); } } return(state); }
private ParserState ParseSequence(ParserState state, string sequenceType) { bool isNewLine = false; List <StringBuilder> itemStrings = new List <StringBuilder>(); int curItemIndex = -1; //-1 indicates not reading an item yet int codeBlockCount = 0; while (!state.Done()) { if (state.Char() == Sym.cCodeOpen) { codeBlockCount++; } else if (state.Char() == Sym.cCodeClose) { codeBlockCount--; } bool isWhiteSpace = (state.Char() == ' ' || state.Char() == '\t'); bool isSkippableWhitespace = isNewLine && isWhiteSpace; bool isNewListItem = isNewLine && (codeBlockCount <= 0) && state.Char() == Sym.cList; if (isNewListItem) { curItemIndex++; itemStrings.Add(Utils.GetTempStringBuilder()); } else if (curItemIndex > -1) { if (!isSkippableWhitespace) { itemStrings[curItemIndex].Append(state.Char()); } } isNewLine = state.Char() == Sym.cLineBreak || isSkippableWhitespace || isNewListItem; state.Step(); } var options = new Node[itemStrings.Count]; for (int i = 0; i < itemStrings.Count; i++) { var str = Utils.Release(itemStrings[i]); var dialogState = new ParserState(new BlockNode(BlockMode.Dialog, false), str); dialogState = ParseDialog(dialogState); options[i] = dialogState.RootNode; } switch (sequenceType) { case "sequence": state.CurNode.AddChild(new SequenceNode(options)); break; case "cycle": state.CurNode.AddChild(new CycleNode(options)); break; case "shuffle": state.CurNode.AddChild(new ShuffleNode(options)); break; } return(state); }
private ParserState ParseIf(ParserState state) { List <StringBuilder> conditionStrings = new List <StringBuilder>(); List <StringBuilder> resultStrings = new List <StringBuilder>(); var curIndex = -1; bool isNewLine = true; bool isConditionDone = false; int codeBlockCount = 0; while (!state.Done()) { if (state.Char() == Sym.cCodeOpen) { codeBlockCount++; } else if (state.Char() == Sym.cCodeClose) { codeBlockCount--; } bool isWhiteSpace = (state.Char() == ' ' || state.Char() == '\t'); bool isSkippableWhitespace = isNewLine && isWhiteSpace; bool isNewListItem = isNewLine && (codeBlockCount <= 0) && state.Char() == Sym.cList; if (isNewListItem) { curIndex++; isConditionDone = false; conditionStrings.Add(Utils.GetTempStringBuilder()); resultStrings.Add(Utils.GetTempStringBuilder()); } else if (curIndex > -1) { if (!isConditionDone) { if (state.Char() == '?' || state.Char() == Sym.cLineBreak) { isConditionDone = true; } else { conditionStrings[curIndex].Append(state.Char()); } } else { if (!isSkippableWhitespace) { resultStrings[curIndex].Append(state.Char()); } } } isNewLine = (state.Char() == Sym.cLineBreak) || isSkippableWhitespace || isNewListItem; state.Step(); } var conditions = new Node[conditionStrings.Count]; for (int i = 0; i < conditionStrings.Count; i++) { var str = Utils.Release(conditionStrings[i]); if (str != null) { str = str.Trim(); } if (str == "else") { conditions[i] = new ElseNode(); } else { conditions[i] = CreateExpression(str); } } var results = new Node[resultStrings.Count]; for (int i = 0; i < resultStrings.Count; i++) { var str = Utils.Release(resultStrings[i]); if (str != null) { str = str.Trim(); } var dialogState = new ParserState(new BlockNode(BlockMode.Dialog), str); dialogState = ParseDialog(dialogState); results[i] = dialogState.RootNode; } state.CurNode.AddChild(new IfNode(conditions, results, false)); return(state); }
private ParserState ParseDialog(ParserState state) { bool hasBlock = false; bool hasDialog = false; bool isFirstLine = true; var builder = Utils.GetTempStringBuilder(); while (!state.Done()) { if (state.MatchAhead(Sym.CodeOpen)) { if (AddTextNode(builder, state)) { hasDialog = true; } state = ParseCodeBlock(state); int len = state.CurNode.Children.Count; if (len > 0 && state.CurNode.Children[len - 1] is NodeParent) { var block = state.CurNode.Children[len - 1] as NodeParent; if (block.IsMultilineListBlock()) { hasDialog = true; } } hasBlock = true; } // NOTE: nested dialog blocks disabled for now //else if(state.MatchAhead(Sym.DialogOpen)) //{ // if (AddTextNode(builder, state)) hasDialog = true; // state = ParseDialogBlock(state); // hasBlock = true; //} else { if (state.MatchAhead(Sym.LineBreak)) { if (AddTextNode(builder, state)) { hasDialog = true; } bool isLastLine = (state.Index + 1) == state.Count; bool isEmptyLine = !hasBlock && !hasDialog; bool isValidEmptyLine = isEmptyLine && !(isFirstLine || isLastLine); bool shouldAddLineBreak = (hasDialog || isValidEmptyLine) && !isLastLine; // last clause is a hack (but it works - why?) if (shouldAddLineBreak) { state.CurNode.AddChild(new FuncNode(FUNC_BR)); } isFirstLine = false; hasBlock = false; hasDialog = false; builder.Length = 0; } else { builder.Append(state.Char()); } state.Step(); } } AddTextNode(builder, state); return(state); }