1 /* Copyright (c) 2001-2007, The HSQL Development Group
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the HSQL Development Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 
32 package org.hsqldb.util.preprocessor;
33 
34 /* $Id: Parser.java 610 2008-12-22 15:54:18Z unsaved $ */
35 
36 /**
37  * Simple preprocessor directive parser. <p>
38  *
39  * @author boucherb@users
40  * @version 1.8.1
41  * @since 1.8.1
42  */
43 class Parser  {
44 
45     Defines   defines;
46     Tokenizer tokenizer;
47 
Parser(Defines defines, Tokenizer tokenizer)48     Parser(Defines defines, Tokenizer tokenizer) {
49         this.defines   = defines;
50         this.tokenizer = tokenizer;
51     }
52 
parseExpression()53     boolean parseExpression() throws PreprocessorException {
54         boolean result = parseTerm();
55 
56         while (true) {
57             switch(this.tokenizer.getTokenType()) {
58                 case Token.OR : {
59                     this.tokenizer.next();
60 
61                     result = result | parseTerm();
62 
63                     break;
64                 }
65                 case Token.XOR : {
66                     this.tokenizer.next();
67 
68                     result = result ^ parseTerm();
69 
70                     break;
71                 }
72 
73                 default : {
74                     return result;
75                 }
76             }
77         }
78     }
79 
parseTerm()80     boolean parseTerm() throws PreprocessorException {
81         boolean result = parseFactor();
82 
83         while (this.tokenizer.isToken(Token.AND)) {
84             this.tokenizer.next();
85 
86             result = result & parseFactor();
87         }
88 
89         return result;
90     }
91 
parseFactor()92     boolean parseFactor() throws PreprocessorException {
93         boolean result;
94 
95         switch(this.tokenizer.getTokenType()) {
96             case Token.IDENT : {
97                 String ident = this.tokenizer.getIdent();
98                 int    type  = this.tokenizer.next();
99 
100                 if ((type == Token.EOI) || (type == Token.RPAREN) ||
101                         Token.isLogicalOperator(type)) {
102                     result = this.defines.isDefined(ident);
103                 } else if (Token.isComparisonOperator(type)) {
104                     result = parseComparison(ident, type);
105                 } else {
106                     throw new PreprocessorException("Logical or comparison "
107                             + "operator token required at position "
108                             + this.tokenizer.getStartIndex()
109                             + " in ["
110                             + this.tokenizer.getSource()
111                             + "]"); // NOI18N
112                 }
113 
114                 break;
115             }
116             case Token.NOT :{
117                 this.tokenizer.next();
118 
119                 result = !parseFactor();
120 
121                 break;
122             }
123             case Token.LPAREN : {
124                 this.tokenizer.next();
125 
126                 result = parseExpression();
127 
128                 if (!this.tokenizer.isToken(Token.RPAREN)) {
129                     throw new PreprocessorException("RPAREN token required at "
130                             + "position "
131                             + this.tokenizer.getStartIndex()
132                             + " in ["
133                             + this.tokenizer.getSource()
134                             + "]"); // NOI18N
135                 }
136 
137                 this.tokenizer.next();
138 
139                 break;
140             }
141             default : {
142                 throw new PreprocessorException("IDENT, NOT or LPAREN "
143                         + "token required at position "
144                         + this.tokenizer.getStartIndex()
145                         + " in ["
146                         + this.tokenizer.getSource()
147                         + "]"); // NOI18N
148             }
149         }
150 
151         return result;
152     }
153 
parseComparison(String ident, int opType)154     boolean parseComparison(String ident, int opType)
155     throws PreprocessorException {
156 //        checkIsComparisonOperator(opType);
157 
158         boolean result;
159         Object  lhs    = this.defines.getDefintion(ident);
160         int     pos    = this.tokenizer.getStartIndex();
161         Object  rhs    = parseValue();
162 
163         if (lhs == null) {
164             throw new PreprocessorException("IDENT " + ident
165                     + " is not defined at position"
166                     + pos
167                     + "in ["
168                     + this.tokenizer.getSource()
169                     + "]"); // NOI18N
170         }
171 
172         switch(opType) {
173             case Token.EQ :{
174                 result = (compare(lhs, rhs) == 0);
175 
176                 break;
177             }
178             case Token.LT : {
179                 result = (compare(lhs, rhs) < 0);
180 
181                 break;
182             }
183             case Token.LTE : {
184                 result = (compare(lhs, rhs) <= 0);
185 
186                 break;
187             }
188             case Token.GT : {
189                 result = (compare(lhs, rhs) > 0);
190 
191                 break;
192             }
193             case Token.GTE : {
194                 result = (compare(lhs, rhs) >= 0);
195 
196                 break;
197             }
198             default : {
199                 // Stupid compiler trick.
200                 // Can't actually happen because this case will cause an
201                 // exception to be thrown in method parseFactor (or in
202                 // method checkIsComparisonOperator when uncommented)
203                 throw new PreprocessorException("Internal error"); // NOI18N
204             }
205         }
206 
207         this.tokenizer.next();
208 
209         return result;
210     }
211 
212 //    void checkIsComparisonOperator(int opType) throws PreprocessorException {
213 //        if (!Token.isComparisonOperator(opType)) {
214 //                throw new PreprocessorException("Comparison "
215 //                        + "operator token required at position "
216 //                        + tokenizer.getBeginIndex()
217 //                        + " in ["
218 //                        + tokenizer.getSource()
219 //                        + "]"); // NOI18N
220 //        }
221 //    }
222 
compare(Object o1, Object o2)223     static int compare(Object o1, Object o2) {
224         // nulls are basically 'illegal' so yes:
225         // we want to throw NPE here if o1 or o2 is null
226         if (o1 instanceof Comparable) {
227             return (o1.getClass().isAssignableFrom(o2.getClass()))
228             ? ((Comparable)o1).compareTo(o2)
229             : String.valueOf(o1).compareTo(String.valueOf(o2));
230         } else {
231             return o1.toString().compareTo(o2.toString());
232         }
233     }
234 
parseValue()235     Object parseValue() throws PreprocessorException {
236         Object value;
237 
238         switch(this.tokenizer.next()) {
239             case Token.IDENT : {
240                 String ident = this.tokenizer.getIdent();
241 
242                 value = this.defines.getDefintion(ident);
243 
244                 if (value == null) {
245                     throw new PreprocessorException("IDENT " + ident
246                             + " is not defined at position"
247                             + this.tokenizer.getStartIndex()
248                             + "in ["
249                             + this.tokenizer.getSource()
250                             + "]"); // NOI18N
251                 }
252 
253                 break;
254             }
255             case Token.STRING : {
256                 value = this.tokenizer.getString();
257 
258                 break;
259             }
260             case Token.NUMBER : {
261                 value = this.tokenizer.getNumber();
262 
263                 break;
264             }
265             default :{
266                 throw new PreprocessorException("IDENT, STRING"
267                         + "or NUMBER token required at position "
268                         + this.tokenizer.getStartIndex()
269                         + " in: ["
270                         + this.tokenizer.getSource()
271                         + "]"); // NOI18N
272             }
273         }
274 
275         return value;
276     }
277 }
278