// Computes set of expected terms in a parser state. While there may be extended list of symbols expected at some point, // we want to reorganize and reduce it. For example, if the current state expects all arithmetic operators as an input, // it would be better to not list all operators (+, -, *, /, etc) but simply put "operator" covering them all. // To achieve this grammar writer can group operators (or any other terminals) into named groups using Grammar's methods // AddTermReportGroup, AddNoReportGroup etc. Then instead of reporting each operator separately, Irony would include // a single "group name" to represent them all. // The "expected report set" is not computed during parser construction (it would bite considerable time), but on demand during parsing, // when error is detected and the expected set is actually needed for error message. // Multi-threading concerns. When used in multi-threaded environment (web server), the LanguageData would be shared in // application-wide cache to avoid rebuilding the parser data on every request. The LanguageData is immutable, except // this one case - the expected sets are constructed late by CoreParser on the when-needed basis. // We don't do any locking here, just compute the set and on return from this function the state field is assigned. // We assume that this field assignment is an atomic, concurrency-safe operation. The worst thing that might happen // is "double-effort" when two threads start computing the same set around the same time, and the last one to finish would // leave its result in the state field. #endregion internal static StringSet ComputeGroupedExpectedSetForState(Grammar grammar, ParserState state) { var terms = new TerminalSet(); terms.UnionWith(state.ExpectedTerminals); var result = new StringSet(); //Eliminate no-report terminals foreach (var group in grammar.TermReportGroups) { if (group.GroupType == TermReportGroupType.Exclude) { terms.ExceptWith(group.Terminals); } } //Add normal and operator groups foreach (var group in grammar.TermReportGroups) { if (group.GroupType == TermReportGroupType.Normal || group.GroupType == TermReportGroupType.Operator && terms.Overlaps(group.Terminals)) { result.Add(group.Alias); terms.ExceptWith(group.Terminals); } } //Add remaining terminals "as is" foreach (var terminal in terms) { result.Add(terminal.ErrorAlias); } return(result); }
string _startSymbolsFirsts; //first chars of start-end symbols #endregion #region overrides: Init, GetFirsts, ReadBody, etc... public override void Init(GrammarData grammarData) { base.Init(grammarData); _startSymbolsFirsts = string.Empty; if (_subtypes.Count == 0) { grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrInvStrDef, this.Name); //"Error in string literal [{0}]: No start/end symbols specified." return; } //collect all start-end symbols in lists and create strings of first chars var allStartSymbols = new StringSet(); //to detect duplicate start symbols _subtypes.Sort(StringSubType.LongerStartFirst); bool isTemplate = false; foreach (StringSubType subType in _subtypes) { if (allStartSymbols.Contains(subType.Start)) { grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrDupStartSymbolStr, subType.Start, this.Name); //"Duplicate start symbol {0} in string literal [{1}]." } allStartSymbols.Add(subType.Start); _startSymbolsFirsts += subType.Start[0].ToString(); if ((subType.Flags & StringOptions.IsTemplate) != 0) { isTemplate = true; } } if (!CaseSensitive) { _startSymbolsFirsts = _startSymbolsFirsts.ToLower() + _startSymbolsFirsts.ToUpper(); } //Set multiline flag foreach (StringSubType info in _subtypes) { if ((info.Flags & StringOptions.AllowsLineBreak) != 0) { SetFlag(TermFlags.IsMultiline); break; } } //For templates only if (isTemplate) { //Check that template settings object is provided var templateSettings = this.AstNodeConfig as StringTemplateSettings; if (templateSettings == null) { grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrTemplNoSettings, this.Name); //"Error in string literal [{0}]: IsTemplate flag is set, but TemplateSettings is not provided." } else if (templateSettings.ExpressionRoot == null) { grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrTemplMissingExprRoot, this.Name); //"" } else if (!Grammar.SnippetRoots.Contains(templateSettings.ExpressionRoot)) { grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrTemplExprNotRoot, this.Name); //"" } }//if //Create editor info if (this.EditorInfo == null) { this.EditorInfo = new TokenEditorInfo(TokenType.String, TokenColor.String, TokenTriggers.None); } }//method
public override void Initialize(GrammarData grammarData) { base.Initialize(grammarData); _startSymbolsFirsts = new CharHashSet(CaseSensitivePrefixesSuffixes); if (_subtypes.Count == 0) { //"Error in string literal [{0}]: No start/end symbols specified." grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrInvStrDef, Name); return; } //collect all start-end symbols in lists and create strings of first chars var allStartSymbols = new StringSet(); //to detect duplicate start symbols _subtypes.Sort(StringSubType.LongerStartFirst); var isTemplate = false; foreach (var subType in _subtypes) { if (allStartSymbols.Contains(subType.Start)) { //"Duplicate start symbol {0} in string literal [{1}]." grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrDupStartSymbolStr, subType.Start, Name); } allStartSymbols.Add(subType.Start); _startSymbolsFirsts.Add(subType.Start[0]); if ((subType.Flags & StringOptions.IsTemplate) != 0) { isTemplate = true; } } //Set multiline flag if (_subtypes.Any(info => (info.Flags & StringOptions.AllowsLineBreak) != 0)) { SetFlag(TermFlags.IsMultiline); } //For templates only if (isTemplate) { //Check that template settings object is provided var templateSettings = AstConfig.Data as StringTemplateSettings; if (templateSettings == null) { //"Error in string literal [{0}]: IsTemplate flag is set, but TemplateSettings is not provided." grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrTemplNoSettings, Name); } else if (templateSettings.ExpressionRoot == null) { //"" grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrTemplMissingExprRoot, Name); } else if (!Grammar.SnippetRoots.Contains(templateSettings.ExpressionRoot)) { //"" grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrTemplExprNotRoot, Name); } } //Create editor info if (EditorInfo == null) { EditorInfo = new TokenEditorInfo(TokenType.String, TokenColor.String, TokenTriggers.None); } }