1 //---------------------------------------------------------------------
2 // <copyright file="CqlParserHelpers.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System
.Data
.Common
.EntitySql
13 using System
.Collections
.Generic
;
14 using System
.Data
.Common
.EntitySql
.AST
;
15 using System
.Data
.Entity
;
16 using System
.Globalization
;
19 /// Represents the Cql Parser engine. Also, implements helpers and util routines.
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";
29 /// Contains inclusive count of method expressions.
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
;
48 /// Main entry point for parsing cql.
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
);
66 _methodExprCounter
= 0;
67 _methodExprCounterStack
= new Stack
<uint>();
68 internalParseEntryPoint();
73 /// Returns query string
77 get { return _query; }
80 #if ENTITYSQL_PARSER_YYDEBUG
82 /// Enables/Disables yacc debugging.
84 internal bool EnableDebug
86 get { return yydebug; }
87 set { yydebug = value; }
92 /// Returns ParserOptions used
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; }
101 /// Internal entry point
103 private void internalParseEntryPoint()
105 _lexer
= new CqlLexer(Query
, ParserOptions
);
106 #if ENTITYSQL_PARSER_YYDEBUG
107 CqlLexer
.Token tk
= lexer
.yylex();
110 Console
.WriteLine("{0} := {1}", tk
.TokenId
, lexer
.yytext());
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();
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
,
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();