public override string WriteParseCode(TextWriter writer, ScriptParserGenerator generator) { IList <string> firsts = _innerExpression.GetFirstTerminals(generator); IList <string> followings = this.GetFollowingTerminals(generator); HashSet <string> firstSet = new HashSet <string>(firsts); foreach (string following in followings) { if (firstSet.Contains(following)) { string context = string.Format("'{0}' の定義", this.RootDefinition.DefinitionName); string message = string.Format("省略可能な <{1}> の直後に <{0}> がありますが <{1}> の方が優先されます", following, this.ToString()); generator.Warn(context, message); } } ParameterSignature returnParam = this.GetReturnParameterSignature(generator); string returnType = returnParam.GetTypeName(); string returnVar; writer.WriteLine(generator.GetCodeOfDeclareDefault(returnType, out returnVar)); string peekVar; writer.WriteLine(generator.GetCodeOfPeek(out peekVar)); writer.WriteLine(generator.GetCodeOfIfOptionalLexisIn(peekVar, firsts)); string innerVar = _innerExpression.WriteParseCode(writer, generator); writer.WriteLine(generator.GetCodeOfSubstitution(returnVar, innerVar)); writer.WriteLine(generator.GetCodeOfCloseBlock()); return(returnVar); }
public static void writeParseCodeAux(TextWriter writer, ScriptParserGenerator generator, IList <SelectionSubCandidate> subCandidates, Action <SelectionSubCandidate, TextWriter, ScriptParserGenerator> returnCodeGenerate) { Debug.Assert(subCandidates.Count >= 1); DefinitionElement rootDefinition = subCandidates.First().RootElement; // 先頭のDefinitionContentが同じである選択候補の集合 Dictionary <DefinitionContent, List <SelectionSubCandidate> > sameHeadCandidates = new Dictionary <DefinitionContent, List <SelectionSubCandidate> >(new DefinitionContentComparer()); // 優先順位を保つために順番を覚えておく List <DefinitionContent> headOrder = new List <DefinitionContent>(); // 後続の要素がない選択候補の集合 List <SelectionSubCandidate> noElementCandidates = new List <SelectionSubCandidate>(); // 選択候補を先頭の要素ごとに振り分け foreach (SelectionSubCandidate sub in subCandidates) { List <SelectionSubCandidate> list; if (sub.IsEmpty) { noElementCandidates.Add(sub); } else { if (!sameHeadCandidates.TryGetValue(sub.Head, out list)) { sameHeadCandidates[sub.Head] = list = new List <SelectionSubCandidate>(); headOrder.Add(sub.Head); } list.Add(sub); } } Dictionary <string, string> usedFirsts = new Dictionary <string, string>(); Dictionary <DefinitionContent, string[]> firstTerminals = new Dictionary <DefinitionContent, string[]>(); foreach (DefinitionContent head in headOrder) { // 先頭が同じ候補の集合 List <SelectionSubCandidate> list; bool getListFromHeadSucceeded = sameHeadCandidates.TryGetValue(head, out list); Debug.Assert(getListFromHeadSucceeded); // 現在の先頭要素の中での頭にくる可能性のある終端記号 HashSet <string> firstsAtThisHead = new HashSet <string>(); foreach (SelectionSubCandidate sub in list) { IList <string> firsts = sub.GetFirstTerminals(generator); foreach (string first in firsts) { // もう追加した場合はスルー if (firstsAtThisHead.Contains(first)) { continue; } firstsAtThisHead.Add(first); // 他の先頭のときに使っている終端記号が頭にくる可能性がある場合は警告 string usingPoint; if (usedFirsts.TryGetValue(first, out usingPoint)) { string context = string.Format("'{0}' の定義", rootDefinition.DefinitionName); string message = string.Format("<{1}> 内の <{0}> は <{2}> によって隠されます", first, sub.OriginalElements.ToString(), usingPoint); generator.Warn(context, message); } else { usedFirsts[first] = sub.OriginalElements.ToString(); } } } firstTerminals[head] = firstsAtThisHead.ToArray(); } bool isSingleCandidate = sameHeadCandidates.Count == 1 && noElementCandidates.Count == 0; if (isSingleCandidate) { List <SelectionSubCandidate> list = sameHeadCandidates.Values.First(); string[] firstsAtThisHead = firstTerminals.Values.First(); DefinitionContent head = list.First().Head; string headVar = head.WriteParseCode(writer, generator); List <SelectionSubCandidate> next = new List <SelectionSubCandidate>(); foreach (SelectionSubCandidate sub in list) { next.Add(sub.NextElement(headVar)); } writeParseCodeAux(writer, generator, next, returnCodeGenerate); return; } bool needsBlockAtEmpty = headOrder.Count >= 1; bool usePeekOrThrow = noElementCandidates.Count == 0; // 後続の要素があるものは再帰的に処理 if (headOrder.Count >= 1) { string peek; if (usePeekOrThrow) { IList <string> validTerminals = firstTerminals.Values.SelectMany(terminals => terminals).Distinct().ToList(); writer.WriteLine(generator.GetCodeOfPeekOrThrow(rootDefinition.DefinitionName, validTerminals, out peek)); } else { // 後続の要素がない場合もあるのでPeekOrThrowではなくPeek writer.WriteLine(generator.GetCodeOfPeek(out peek)); } // 順番通りに foreach (DefinitionContent head in headOrder) { // 先頭が同じ候補の集合 List <SelectionSubCandidate> list; bool getListFromHeadSucceeded = sameHeadCandidates.TryGetValue(head, out list); Debug.Assert(getListFromHeadSucceeded); // 現在の先頭要素の中での頭にくる可能性のある終端記号 string[] firstsAtThisHead; bool getFirstsFromHeadSucceeded = firstTerminals.TryGetValue(head, out firstsAtThisHead); Debug.Assert(getFirstsFromHeadSucceeded); // 頭の終端記号が対象のであるか if (usePeekOrThrow) { writer.WriteLine(generator.GetCodeOfIfLexisIn(peek, firstsAtThisHead)); } else { writer.WriteLine(generator.GetCodeOfIfOptionalLexisIn(peek, firstsAtThisHead)); } // 先頭の要素をパース string headVar = head.WriteParseCode(writer, generator); // 後続を再帰的に処理 List <SelectionSubCandidate> next = new List <SelectionSubCandidate>(); foreach (SelectionSubCandidate sub in list) { next.Add(sub.NextElement(headVar)); } writeParseCodeAux(writer, generator, next, returnCodeGenerate); writer.Write(generator.GetCodeOfCloseBlock()); writer.Write(generator.GetCodeOfSingleElse()); } } // 同じ内容なのが二つあった時は警告を出す if (noElementCandidates.Count >= 2) { SelectionSubCandidate oneOfCandidate = noElementCandidates.First(); foreach (SelectionSubCandidate emptyElse in noElementCandidates.Skip(1)) { string context = string.Format("'{0}' の定義", rootDefinition.DefinitionName); string message = string.Format("<{0}> は <{1}> によって隠されます", emptyElse.OriginalElements.ToString(), oneOfCandidate.OriginalElements.ToString()); generator.Warn(context, message); } } if (needsBlockAtEmpty) { writer.WriteLine(generator.GetCodeOfOpenBlock()); } if (noElementCandidates.Count >= 1) { SelectionSubCandidate elements = noElementCandidates.First(); returnCodeGenerate(elements, writer, generator); } else { IList <string> availableTerminals = new List <string>(usedFirsts.Keys); writer.WriteLine(generator.GetCodeOfThrowError(rootDefinition.DefinitionName, availableTerminals)); } if (needsBlockAtEmpty) { writer.WriteLine(generator.GetCodeOfCloseBlock()); } }