Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
CompoundTerminalBase.cs
Go to the documentation of this file.
1 #region License
2 /* **********************************************************************************
3  * Copyright (c) Roman Ivantsov
4  * This source code is subject to terms and conditions of the MIT License
5  * for Irony. A copy of the license can be found in the License.txt file
6  * at the root of this distribution.
7  * By using this source code in any fashion, you are agreeing to be bound by the terms of the
8  * MIT License.
9  * You must not remove this notice from this software.
10  * **********************************************************************************/
11 #endregion
12 
13 using System;
14 using System.Collections.Generic;
15 using System.Text;
16 
17 namespace Irony.Parsing {
18  #region About compound terminals
19  /*
20  As it turns out, many terminal types in real-world languages have 3-part structure: prefix-body-suffix
21  The body is essentially the terminal "value", while prefix and suffix are used to specify additional
22  information (options), while not being a part of the terminal itself.
23  For example:
24  1. c# numbers, may have 0x prefix for hex representation, and suffixes specifying
25  the exact data type of the literal (f, l, m, etc)
26  2. c# string may have "@" prefix which disables escaping inside the string
27  3. c# identifiers may have "@" prefix and escape sequences inside - just like strings
28  4. Python string may have "u" and "r" prefixes, "r" working the same way as @ in c# strings
29  5. VB string literals may have "c" suffix identifying that the literal is a character, not a string
30  6. VB number literals and identifiers may have suffixes identifying data type
31 
32  So it seems like all these terminals have the format "prefix-body-suffix".
33  The CompoundTerminalBase base class implements base functionality supporting this multi-part structure.
34  The IdentifierTerminal, NumberLiteral and StringLiteral classes inherit from this base class.
35  The methods in TerminalFactory static class demonstrate that with this architecture we can define the whole
36  variety of terminals for c#, Python and VB.NET languages.
37 */
38  #endregion
39 
40 
41  public class EscapeTable : Dictionary<char, string> { }
42 
43  public abstract class CompoundTerminalBase : Terminal {
44 
45  #region Nested classes
46  protected class ScanFlagTable : Dictionary<string, short> { }
47  protected class TypeCodeTable : Dictionary<string, TypeCode[]> { }
48 
49  public class CompoundTokenDetails {
50  public string Prefix;
51  public string Body;
52  public string Suffix;
53  public string Sign;
54  public short Flags; //need to be short, because we need to save it in Scanner state for Vs integration
55  public string Error;
56  public TypeCode[] TypeCodes;
57  public string ExponentSymbol; //exponent symbol for Number literal
58  public string StartSymbol; //string start and end symbols
59  public string EndSymbol;
60  public object Value;
61  //partial token info, used by VS integration
62  public bool PartialOk;
63  public bool IsPartial;
64  public bool PartialContinues;
65  public byte SubTypeIndex; //used for string literal kind
66  //Flags helper method
67  public bool IsSet(short flag) {
68  return (Flags & flag) != 0;
69  }
70  public string Text { get { return Prefix + Body + Suffix; } }
71  }
72 
73  #endregion
74 
75  #region constructors and initialization
76  public CompoundTerminalBase(string name) : this(name, TermFlags.None) { }
77  public CompoundTerminalBase(string name, TermFlags flags) : base(name) {
78  SetFlag(flags);
79  Escapes = GetDefaultEscapes();
80  }
81 
82  protected void AddPrefixFlag(string prefix, short flags) {
83  PrefixFlags.Add(prefix, flags);
84  Prefixes.Add(prefix);
85  }
86  public void AddSuffix(string suffix, params TypeCode[] typeCodes) {
87  SuffixTypeCodes.Add(suffix, typeCodes);
88  Suffixes.Add(suffix);
89  }
90  #endregion
91 
92  #region public Properties/Fields
93  public Char EscapeChar = '\\';
94  public EscapeTable Escapes = new EscapeTable();
95  #endregion
96 
97 
98  #region private fields
99  protected readonly ScanFlagTable PrefixFlags = new ScanFlagTable();
100  protected readonly TypeCodeTable SuffixTypeCodes = new TypeCodeTable();
101  protected StringList Prefixes = new StringList();
102  protected StringList Suffixes = new StringList();
103  protected bool CaseSensitive; //case sensitivity for prefixes and suffixes
104  string _prefixesFirsts; //first chars of all prefixes, for fast prefix detection
105  string _suffixesFirsts; //first chars of all suffixes, for fast suffix detection
106  #endregion
107 
108 
109  #region overrides: Init, TryMatch
110  public override void Init(GrammarData grammarData) {
111  base.Init(grammarData);
112  //collect all suffixes, prefixes in lists and create strings of first chars for both
113  Prefixes.Sort(StringList.LongerFirst);
114  _prefixesFirsts = string.Empty;
115  foreach (string pfx in Prefixes)
116  _prefixesFirsts += pfx[0];
117 
118  Suffixes.Sort(StringList.LongerFirst);
119  _suffixesFirsts = string.Empty;
120  foreach (string sfx in Suffixes)
121  _suffixesFirsts += sfx[0]; //we don't care if there are repetitions
122  if (!CaseSensitive) {
123  _prefixesFirsts = _prefixesFirsts.ToLower() + _prefixesFirsts.ToUpper();
124  _suffixesFirsts = _suffixesFirsts.ToLower() + _suffixesFirsts.ToUpper();
125  }
126  }//method
127 
128  public override IList<string> GetFirsts() {
129  return Prefixes;
130  }
131 
132  public override Token TryMatch(ParsingContext context, ISourceStream source) {
133  Token token;
134  //Try quick parse first, but only if we're not continuing
135  if (context.VsLineScanState.Value == 0) {
136  token = QuickParse(context, source);
137  if (token != null) return token;
138  source.PreviewPosition = source.Location.Position; //revert the position
139  }
140 
142  InitDetails(context, details);
143 
144  if (context.VsLineScanState.Value == 0)
145  ReadPrefix(source, details);
146  if (!ReadBody(source, details))
147  return null;
148  if (details.Error != null)
149  return source.CreateErrorToken(details.Error);
150  if (details.IsPartial) {
151  details.Value = details.Body;
152  } else {
153  ReadSuffix(source, details);
154 
155  if(!ConvertValue(details)) {
156  if (string.IsNullOrEmpty(details.Error))
157  details.Error = Resources.ErrInvNumber;
158  return source.CreateErrorToken(details.Error); // "Failed to convert the value: {0}"
159  }
160  }
161  token = CreateToken(context, source, details);
162 
163  if (details.IsPartial) {
164  //Save terminal state so we can continue
165  context.VsLineScanState.TokenSubType = (byte)details.SubTypeIndex;
167  context.VsLineScanState.TerminalIndex = this.MultilineIndex;
168  } else
169  context.VsLineScanState.Value = 0;
170  return token;
171  }
172 
173  protected virtual Token CreateToken(ParsingContext context, ISourceStream source, CompoundTokenDetails details) {
174  var token = source.CreateToken(this.OutputTerminal, details.Value);
175  token.Details = details;
176  if (details.IsPartial)
177  token.Flags |= TokenFlags.IsIncomplete;
178  return token;
179  }
180 
181  protected virtual void InitDetails(ParsingContext context, CompoundTokenDetails details) {
182  details.PartialOk = (context.Mode == ParseMode.VsLineScan);
183  details.PartialContinues = (context.VsLineScanState.Value != 0);
184  }
185 
186  protected virtual Token QuickParse(ParsingContext context, ISourceStream source) {
187  return null;
188  }
189 
190  protected virtual void ReadPrefix(ISourceStream source, CompoundTokenDetails details) {
191  if (_prefixesFirsts.IndexOf(source.PreviewChar) < 0)
192  return;
193  foreach (string pfx in Prefixes) {
194  if (!source.MatchSymbol(pfx, !CaseSensitive)) continue;
195  //We found prefix
196  details.Prefix = pfx;
197  source.PreviewPosition += pfx.Length;
198  //Set flag from prefix
199  short pfxFlags;
200  if (!string.IsNullOrEmpty(details.Prefix) && PrefixFlags.TryGetValue(details.Prefix, out pfxFlags))
201  details.Flags |= (short) pfxFlags;
202  return;
203  }//foreach
204  }//method
205 
206  protected virtual bool ReadBody(ISourceStream source, CompoundTokenDetails details) {
207  return false;
208  }
209 
210  protected virtual void ReadSuffix(ISourceStream source, CompoundTokenDetails details) {
211  if (_suffixesFirsts.IndexOf(source.PreviewChar) < 0) return;
212  foreach (string sfx in Suffixes) {
213  if (!source.MatchSymbol(sfx, !CaseSensitive)) continue;
214  //We found suffix
215  details.Suffix = sfx;
216  source.PreviewPosition += sfx.Length;
217  //Set TypeCode from suffix
218  TypeCode[] codes;
219  if (!string.IsNullOrEmpty(details.Suffix) && SuffixTypeCodes.TryGetValue(details.Suffix, out codes))
220  details.TypeCodes = codes;
221  return;
222  }//foreach
223  }//method
224 
225  protected virtual bool ConvertValue(CompoundTokenDetails details) {
226  details.Value = details.Body;
227  return false;
228  }
229 
230 
231  #endregion
232 
233  #region utils: GetDefaultEscapes
234  public static EscapeTable GetDefaultEscapes() {
235  EscapeTable escapes = new EscapeTable();
236  escapes.Add('a', "\u0007");
237  escapes.Add('b', "\b");
238  escapes.Add('t', "\t");
239  escapes.Add('n', "\n");
240  escapes.Add('v', "\v");
241  escapes.Add('f', "\f");
242  escapes.Add('r', "\r");
243  escapes.Add('"', "\"");
244  escapes.Add('\'', "\'");
245  escapes.Add('\\', "\\");
246  escapes.Add(' ', " ");
247  escapes.Add('\n', "\n"); //this is a special escape of the linebreak itself,
248  // when string ends with "\" char and continues on the next line
249  return escapes;
250  }
251  #endregion
252 
253  }//class
254 
255 }//namespace
virtual void InitDetails(ParsingContext context, CompoundTokenDetails details)
virtual bool ReadBody(ISourceStream source, CompoundTokenDetails details)
char PreviewChar
Gets a char at preview position
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ DXGI_FORMAT _In_ DWORD flags
Definition: DirectXTexP.h:170
virtual Token CreateToken(ParsingContext context, ISourceStream source, CompoundTokenDetails details)
void AddSuffix(string suffix, params TypeCode[] typeCodes)
Interface for Terminals to access the source stream and produce tokens.
Flags
Enumeration of the new Assimp's flags.
virtual bool ConvertValue(CompoundTokenDetails details)
void AddPrefixFlag(string prefix, short flags)
virtual void ReadSuffix(ISourceStream source, CompoundTokenDetails details)
override IList< string > GetFirsts()
CompoundTerminalBase(string name, TermFlags flags)
static string ErrInvNumber
Looks up a localized string similar to Invalid number..
virtual Token QuickParse(ParsingContext context, ISourceStream source)
VsScannerStateMap VsLineScanState
virtual void ReadPrefix(ISourceStream source, CompoundTokenDetails details)
override void Init(GrammarData grammarData)
override Token TryMatch(ParsingContext context, ISourceStream source)
Tokens are produced by scanner and fed to parser, optionally passing through Token filters in between...
Definition: Token.cs:74