Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / EntitySql / CqlParserHelpers.cs
blob9dcc68e183411b3cbac2b0dee24dc4054831e826
1 //---------------------------------------------------------------------
2 // <copyright file="CqlParserHelpers.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.Common.EntitySql
12 using System;
13 using System.Collections.Generic;
14 using System.Data.Common.EntitySql.AST;
15 using System.Data.Entity;
16 using System.Globalization;
18 /// <summary>
19 /// Represents the Cql Parser engine. Also, implements helpers and util routines.
20 /// </summary>
21 internal sealed partial class CqlParser
23 private Node _parsedTree;
24 private CqlLexer _lexer;
25 private string _query;
26 private ParserOptions _parserOptions;
27 private const string _internalYaccSyntaxErrorMessage = "syntax error";
28 /// <summary>
29 /// Contains inclusive count of method expressions.
30 /// </summary>
31 private uint _methodExprCounter;
32 private Stack<uint> _methodExprCounterStack;
34 private string _version = YYMAJOR.ToString(NumberFormatInfo.InvariantInfo) + '.' + YYMINOR.ToString(NumberFormatInfo.InvariantInfo);
36 internal CqlParser(ParserOptions parserOptions, bool debug)
38 // The common practice is to make the null check at the public surface,
39 // however this method is a convergence zone from multiple public entry points and it makes sense to
40 // check for null once, here.
41 EntityUtil.CheckArgumentNull(parserOptions, "parserOptions");
43 _parserOptions = parserOptions;
44 yydebug = debug;
47 /// <summary>
48 /// Main entry point for parsing cql.
49 /// </summary>
50 /// <param name="query">query text</param>
51 /// <exception cref="System.Data.EntityException">Thrown when Syntatic rules are violated and the query cannot be accepted</exception>
52 /// <returns>Abstract Syntax Tree</returns>
53 internal Node Parse(string query)
55 // The common practice is to make the null check at the public surface,
56 // however this method is a convergence zone from multiple public entry points and it makes sense to
57 // check for null once, here.
58 EntityUtil.CheckArgumentNull(query, "query");
59 if (String.IsNullOrEmpty(query) || query.Trim().Length == 0)
61 throw EntityUtil.Argument(System.Data.Entity.Strings.InvalidEmptyQueryTextArgument);
64 _query = query;
65 _parsedTree = null;
66 _methodExprCounter = 0;
67 _methodExprCounterStack = new Stack<uint>();
68 internalParseEntryPoint();
69 return _parsedTree;
72 /// <summary>
73 /// Returns query string
74 /// </summary>
75 internal string Query
77 get { return _query; }
80 #if ENTITYSQL_PARSER_YYDEBUG
81 /// <summary>
82 /// Enables/Disables yacc debugging.
83 /// </summary>
84 internal bool EnableDebug
86 get { return yydebug; }
87 set { yydebug = value; }
89 #endif
91 /// <summary>
92 /// Returns ParserOptions used
93 /// </summary>
94 /// <remarks>Once parse has been invoked, ParserOptions are frozen and cannot be changed. otherwise a EntityException exception will be thrown</remarks>
95 internal ParserOptions ParserOptions
97 get { return _parserOptions; }
100 /// <summary>
101 /// Internal entry point
102 /// </summary>
103 private void internalParseEntryPoint()
105 _lexer = new CqlLexer(Query, ParserOptions);
106 #if ENTITYSQL_PARSER_YYDEBUG
107 CqlLexer.Token tk = lexer.yylex();
108 while (null != tk)
110 Console.WriteLine("{0} := {1}", tk.TokenId, lexer.yytext());
111 tk = lexer.yylex();
113 #endif
114 yyparse();
118 // Conversion/Cast/Helpers
120 private static Node AstNode(object o) { return ((Node)o); }
121 private static int AstNodePos( object o ) { return ((Node)o).ErrCtx.InputPosition; }
122 private static CqlLexer.TerminalToken Terminal( object o ) { return ((CqlLexer.TerminalToken)o); }
123 private static int TerminalPos( object o ) { return ((CqlLexer.TerminalToken)o).IPos; }
124 private static NodeList<T> ToNodeList<T>(object o) where T : Node { return ((NodeList<T>)o); }
126 private short yylex()
128 CqlLexer.Token token = null;
129 token = _lexer.yylex();
130 if (null == token)
132 return 0;
134 _lexer.AdvanceIPos();
135 yylval = token.Value;
136 return token.TokenId;
139 private void yyerror_stackoverflow()
141 yyerror(System.Data.Entity.Strings.StackOverflowInParser);
144 private void yyerror( string s )
146 if (s.Equals(_internalYaccSyntaxErrorMessage, StringComparison.Ordinal))
148 int errorPosition = _lexer.IPos;
149 string syntaxContextInfo = null;
150 string term = _lexer.YYText;
151 if (!String.IsNullOrEmpty(term))
153 syntaxContextInfo = System.Data.Entity.Strings.LocalizedTerm;
154 ErrorContext errCtx = null;
155 Node astNode = yylval as Node;
156 if (null != astNode && (null != astNode.ErrCtx) && (!String.IsNullOrEmpty(astNode.ErrCtx.ErrorContextInfo)))
158 errCtx = astNode.ErrCtx;
159 errorPosition = Math.Min(errorPosition, errorPosition - term.Length);
162 if ((yylval is CqlLexer.TerminalToken) && CqlLexer.IsReservedKeyword(term) && !(astNode is Identifier))
164 syntaxContextInfo = System.Data.Entity.Strings.LocalizedKeyword;
165 term = term.ToUpperInvariant();
166 errorPosition = Math.Min(errorPosition, errorPosition - term.Length);
168 else if (null != errCtx)
170 syntaxContextInfo = EntityRes.GetString(errCtx.ErrorContextInfo);
173 syntaxContextInfo = String.Format(CultureInfo.CurrentCulture, "{0} '{1}'", syntaxContextInfo, term);
176 throw EntityUtil.EntitySqlError(_query,
177 System.Data.Entity.Strings.GenericSyntaxError,
178 errorPosition,
179 syntaxContextInfo,
180 false /* loadErrorContextInfoFromResource */);
182 throw EntityUtil.EntitySqlError(_query, s, _lexer.IPos);
186 // Error tracking helpers
188 private void SetErrCtx(Node astExpr, CqlLexer.TerminalToken tokenValue, string info)
190 SetErrCtx(astExpr, tokenValue.IPos, info);
193 private void SetErrCtx(Node astExpr, int inputPos, string info)
195 astExpr.ErrCtx.InputPosition = inputPos;
196 astExpr.ErrCtx.ErrorContextInfo = info;
197 astExpr.ErrCtx.CommandText = _query;
200 private void StartMethodExprCounting()
202 // Save the current counter value.
203 _methodExprCounterStack.Push(_methodExprCounter);
205 // Reset the counter for the current level.
206 _methodExprCounter = 0;
209 private void IncrementMethodExprCount()
211 ++_methodExprCounter;
214 private uint EndMethodExprCounting()
216 // Save number of method expressions on the current level.
217 uint count = _methodExprCounter;
219 // Restore upper level counter and adjust it with the number of method expressions on the current level.
220 _methodExprCounter += _methodExprCounterStack.Pop();
222 return count;