#region License /* ********************************************************************************** * Copyright (c) Roman Ivantsov * This source code is subject to terms and conditions of the MIT License * for Irony. A copy of the license can be found in the License.txt file * at the root of this distribution. * By using this source code in any fashion, you are agreeing to be bound by the terms of the * MIT License. * You must not remove this notice from this software. * **********************************************************************************/ #endregion using System; using System.Collections.Generic; using System.Text; namespace Irony.Compiler { public class CommentTerminal : Terminal { public CommentTerminal(string name, string startSymbol, params string[] endSymbols) : base(name, TokenCategory.Comment) { this.StartSymbol = startSymbol; this.EndSymbols = new StringList(); EndSymbols.AddRange(endSymbols); } public string StartSymbol; public StringList EndSymbols; private char[] _endSymbolsFirsts; private bool _isLineComment; //true if NewLine is one of EndSymbols; if yes, EOF is also considered a valid end symbol #region overrides public override void Init(Grammar grammar) { base.Init(grammar); //_endSymbolsFirsts char array is used for fast search for end symbols using String's method IndexOfAny(...) _endSymbolsFirsts = new char[EndSymbols.Count]; for (int i = 0; i < EndSymbols.Count; i++) { string sym = EndSymbols[i]; _endSymbolsFirsts[i] = sym[0]; _isLineComment |= sym.Contains("\n"); } } public override Token TryMatch(CompilerContext context, ISourceStream source) { bool ignoreCase = !Grammar.CaseSensitive; //Check starting symbol if (!source.MatchSymbol(StartSymbol, ignoreCase)) return null; //Find end symbol source.Position += StartSymbol.Length; while(!source.EOF()) { int firstCharPos; if (EndSymbols.Count == 1) firstCharPos = source.Text.IndexOf(EndSymbols[0], source.Position); else firstCharPos = source.Text.IndexOfAny(_endSymbolsFirsts, source.Position); if (firstCharPos < 0) { source.Position = source.Text.Length; if (_isLineComment) //if it is LineComment, it is ok to hit EOF without final line-break; just return all until end. return Token.Create(this, context, source.TokenStart, source.GetLexeme()); else return Grammar.CreateSyntaxErrorToken(context, source.TokenStart, "Unclosed comment block"); } //We found a character that might start an end symbol; let's see if it is true. source.Position = firstCharPos; foreach (string endSymbol in EndSymbols) if (source.MatchSymbol(endSymbol, ignoreCase)) { //We found end symbol source.Position += endSymbol.Length; return Token.Create(this, context, source.TokenStart, source.GetLexeme()); }//if source.Position++; //move to the next char and try again }//while return null; //never happens }//method public override IList GetFirsts() { return new string[] { StartSymbol }; } #endregion }//CommentTerminal class }