View Javadoc

1   /*
2    * This file is part of Pease Plate Template Engine.
3    * 
4    * Pease Plate Template Engine is free software: you can redistribute
5    * it and/or modify it under the terms of the GNU Lesser General 
6    * Public License as published by the Free Software Foundation, 
7    * either version 3 of the License, or any later version.
8    * 
9    * Pease Plate Template Engine is distributed in the hope that it 
10   * will be useful, but WITHOUT ANY WARRANTY; without even the implied
11   * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12   * See the GNU Lesser General Public License for more details.
13   * 
14   * You should have received a copy of the GNU Lesser General Public 
15   * License along with Pease Plate Template Engine. If not, see 
16   * <http://www.gnu.org/licenses/>.
17   * 
18   * Copyright (c) 2008 Manfred HANTSCHEL
19   */
20  package org.peaseplate.internal.lang;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.peaseplate.TemplateEngine;
26  import org.peaseplate.TemplateException;
27  import org.peaseplate.TemplateRuntimeException;
28  import org.peaseplate.internal.lang.command.AdditionCommand;
29  import org.peaseplate.internal.lang.command.AndCommand;
30  import org.peaseplate.internal.lang.command.BitwiseAndCommand;
31  import org.peaseplate.internal.lang.command.BitwiseExclusiveOrCommand;
32  import org.peaseplate.internal.lang.command.BitwiseInclusiveOrCommand;
33  import org.peaseplate.internal.lang.command.ComplementCommand;
34  import org.peaseplate.internal.lang.command.DivideCommand;
35  import org.peaseplate.internal.lang.command.EqualsCommand;
36  import org.peaseplate.internal.lang.command.GreaterEqualCommand;
37  import org.peaseplate.internal.lang.command.GreaterThanCommand;
38  import org.peaseplate.internal.lang.command.ICommand;
39  import org.peaseplate.internal.lang.command.InlineConditionCommand;
40  import org.peaseplate.internal.lang.command.InvocationCommand;
41  import org.peaseplate.internal.lang.command.LessEqualCommand;
42  import org.peaseplate.internal.lang.command.LessThanCommand;
43  import org.peaseplate.internal.lang.command.ModuloCommand;
44  import org.peaseplate.internal.lang.command.MultiplyCommand;
45  import org.peaseplate.internal.lang.command.NegativeCommand;
46  import org.peaseplate.internal.lang.command.NotCommand;
47  import org.peaseplate.internal.lang.command.NotEqualsCommand;
48  import org.peaseplate.internal.lang.command.OrCommand;
49  import org.peaseplate.internal.lang.command.PopCommand;
50  import org.peaseplate.internal.lang.command.PositiveCommand;
51  import org.peaseplate.internal.lang.command.QueryCommand;
52  import org.peaseplate.internal.lang.command.ShiftLeftCommand;
53  import org.peaseplate.internal.lang.command.ShiftRightCommand;
54  import org.peaseplate.internal.lang.command.SubstractCommand;
55  import org.peaseplate.internal.lang.command.ThisCommand;
56  import org.peaseplate.internal.lang.command.TransformerCommand;
57  import org.peaseplate.internal.lang.command.UnsignedShiftRightCommand;
58  import org.peaseplate.internal.lang.command.ValueCommand;
59  import org.peaseplate.internal.lang.command.VariableCommand;
60  import org.peaseplate.internal.model.CompileContext;
61  import org.peaseplate.lang.TemplateParserException;
62  
63  /**
64   * The default parser for the referece language.
65   * 
66   * Default Reference 		= Command
67   * Key						= KeyIdentifier | String
68   * Identifier				= Identifier
69   * 
70   * Command					= ("(" Command ")" ) | (Expression {AnyOperator Command} ["?" Command ":" Command] TransformerChain)
71   * Expression				= [UnaryOperator] (Invocation | Query | Variable | Number | String | "true" | "false" | "null" | "$this" | "$pop") {ExpressionChain}
72   * ExpressionChain          = Query | ("." ("$pop" | Invocation))
73   * Invocation				= Identifier ParameterList
74   * Query					= "[" Command "]" ParameterList
75   * ParameterList			= ["(" {Command {"," Command}} ")"]
76   * VariableList				= ["(" {Variable {"," Variable}} ")"]
77   * IdentifierList			= ["(" {Identifier {"," Identifier}} ")"]
78   * TransformerChain 		= {"->" Identifier ["." Identifier] ParameterList}
79   * IdentifierExtension		= ["." Identifier]
80   * 
81   * 
82   * AnyOperator				= MultiplicativeOperator | AdditiveOperator | ShiftOperator | RelationalOperator |
83   * 							  EqualityOperator | BitwiseOperator | ConditionalAndOperator | ConditionalOrOperator
84   * UnaryOperator			= "+" | "-" | "~" | "!"
85   * MultiplicativeOperator	= "*" | "/" | "%"
86   * AdditiveOperator			= "+" | "-"
87   * ShiftOperator			= "<<" | ">>" | ">>>"
88   * RelationalOperator		= "<" | "<=" | ">" | ">="
89   * EqualityOperator			= "==" | "!="
90   * BitwiseOperator			= "&" | "^" | "|"
91   * ConditionalAndOperator	= "&&"
92   * ConditionalOrOperator	= "||"
93   * 
94   * Identifier				= (LETTER | "_") {LETTER | DIGIT | "_"}
95   * Variable					= '$' (LETTER | DIGIT | "_") {LETTER | DIGIT | "_"}
96   * KeyIdentifier			= (LETTER | DIGIT | "_" | "-" | "." | "/") {LETTER | DIGIT | "_" | "-" | "." | "/"}
97   * AnyIdentifier			= ANY_EXCEPT_SPACE
98   * Number					= (DecimalNumber | RealNumber | HexNumber | OctalNumber)
99   * DecimalNumber			= DIGIT_EXCEPT_ZERO {DIGIT}
100  * RealNumber				= {DIGIT} ["." DIGIT {DIGIT}] [("e" | "E") ["-" | "+"] DIGIT {DIGIT}] ["d" | "D" | "f" | "F"]
101  * HexNumber				= ("#" | "0x") DIGIT {DIGIT}
102  * OctalNumber				= "0" {DIGIT}
103  * String					= "\"" {ANY_EXCEPT_QUOTES} "\""
104  * Boolean					= "true" | "false"
105  * Keyword					= "pop" | "this"
106  * Null						= "null"
107  * 
108  */
109 public class Parser {
110 
111 	private final TemplateEngine engine;
112 	private final CompileContext context;
113 	private final Tokenizer tokenizer;
114 	
115 	public Parser(TemplateEngine engine, CompileContext context, int line, int column, char code[], int offset, int length) {
116 		super();
117 		
118 		this.engine = engine;
119 		this.context = context;
120 		
121 		tokenizer = new Tokenizer(context.getLocator(), line, column, code, offset, length);
122 	}
123 	
124     public TemplateEngine getEngine() {
125     	return engine;
126     }
127 
128     public CompileContext getContext() {
129 		return context;
130 	}
131 
132 	public Tokenizer getTokenizer() {
133 		return tokenizer;
134 	}
135 
136 	/**
137      * Parses the code as default reference.
138      * Expects that the cursor is located before the first token.
139      * 
140      * Default Reference = Command
141      * 
142      * @return the command
143      * @throws TemplateException on occasion
144      */
145 	public ICommand readDefaultReference() throws TemplateException {
146 		ICommand result = null;
147 		
148 		try {
149 			tokenizer.readToken();
150 			result = parseCommand(null, 0);
151 			
152 			if (Tokenizer.Type.NONE != tokenizer.getTokenType()) {
153 				throw new TemplateParserException(
154 					context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(),
155 					"Exected end of code but found \"" + tokenizer.getTokenValue() + "\""
156 				);
157 			}
158 		}
159 		catch (TokenizerException e) {
160 			throw new TemplateParserException(context.getLocator(), e);
161 		}
162 		
163 		return result;
164 	}
165     
166 	/**
167 	 * Parses the code as key token.
168 	 * Expects that the cursor is located before the first token which must be a key or string
169 	 * 
170 	 * @return the key
171 	 * @throws TemplateException on occasion
172 	 */
173 	public String readKey() throws TemplateException {
174 		String key = null;
175 
176 		try {
177 			tokenizer.readKeyToken();
178 	
179 			int line = tokenizer.getTokenLine();
180 			int column = tokenizer.getTokenColumn();
181 			Tokenizer.Type type = tokenizer.getTokenType();
182 	
183 			if ((Tokenizer.Type.KEY != type) && (Tokenizer.Type.STRING != type)) 
184 				throw new TemplateParserException(
185 					context.getLocator(), line, column,
186 					"Key identifier expected, but \"" + tokenizer.getTokenValue() + "\" found"
187 				);
188 	
189 			key = String.valueOf(tokenizer.getTokenValue());
190 			
191 			tokenizer.readToken();
192 		}
193 		catch (TokenizerException e) {
194 			throw new TemplateParserException(context.getLocator(), e);
195 		}
196 
197 		return key;
198 	}
199 	
200 	/**
201 	 * Parses the code as a single identifier.
202 	 * Expects that the cursor is located before the first token with must be an identifier
203 	 * 
204 	 * @return the identifier
205 	 * @throws TemplateException on occasion
206 	 */
207 	public String readIdentifier() throws TemplateException {
208 		String identifier = null;
209 		
210 		try {
211 			tokenizer.readToken();
212 			
213 			int line = tokenizer.getTokenLine();
214 			int column = tokenizer.getTokenColumn();
215 			Tokenizer.Type type = tokenizer.getTokenType();
216 			
217 			if (Tokenizer.Type.IDENTIFIER != type) 
218 				throw new TemplateParserException(
219 					context.getLocator(), line, column,
220 					"Identifier expected, but \"" + tokenizer.getTokenValue() + "\" found"
221 				);
222 				
223 			identifier = String.valueOf(tokenizer.getTokenValue());
224 			
225 			tokenizer.readToken();
226 		}
227 		catch (TokenizerException e) {
228 			throw new TemplateParserException(context.getLocator(), e);
229 		}
230 
231 		return identifier;
232 	}
233 	
234     /**
235      * Parses the command notation
236      * 
237      * Command = ("(" Command ")" ) | (Expression {AnyOperator Command} ["?" Command ":" Command] {"->" Transformer})
238      * 
239      * @param operator the operator just prior to the current token
240      * @param parenthesisCount the count of parentheses 
241      * @return the command
242      * @throws TemplateException on occasion
243      */
244 	protected ICommand parseCommand(Tokenizer.Type operator, int parenthesisCount) throws TemplateException {
245 		ICommand result = null;
246 		int line = tokenizer.getTokenLine();
247 		int column = tokenizer.getTokenColumn();
248 		Tokenizer.Type type = tokenizer.getTokenType();
249 		
250 		try {
251 			if (Tokenizer.Type.PARENTHESIS_OPEN == type) {
252 				tokenizer.readToken();
253 				
254 				result = parseCommand(null, parenthesisCount+1);
255 			}
256 			else if (isExpression()) 
257 				result = parseExpression();
258 			else 
259 				throw new TemplateParserException(
260 					context.getLocator(), line, column, 
261 					"Expected (Invocation | Number | String | \"true\" | \"false\" | \"null\" | \"$this\" | \"$pop\" | \"(\")"
262 				);
263 	
264 			if (Tokenizer.Type.PARENTHESIS_CLOSE != tokenizer.getTokenType()) {
265 				while (isAnyOperator()) {
266 					// the next line is tricky
267 					// if the current operator is more important
268 					// than the operator used before this expression
269 					// then go on with parsing, otherwise let the
270 					// caller continue with parsing
271 					if ((operator == null) || (tokenizer.getTokenType().ordinal() < operator.ordinal())) {
272 						// the operator i'm on, is more important than the operator before
273 						// the last expression, so it's my turn now
274 						Tokenizer.Type currentOperator = tokenizer.getTokenType();
275 						Object currentValue = tokenizer.getTokenValue();
276 						int currentLine = tokenizer.getTokenLine();
277 						int currentColumn = tokenizer.getTokenColumn();
278 						
279 						tokenizer.readToken();
280 						
281 						ICommand right = parseCommand(currentOperator, parenthesisCount);
282 						
283 						result = createDoubleParameterCommand(
284 							currentOperator, currentValue, currentLine, currentColumn, 
285 							result, right
286 						);
287 					}
288 					else
289 						break;
290 				}
291 			}
292 			else if (parenthesisCount > 0)
293 				tokenizer.readToken();
294 	
295 			if (parenthesisCount == 0) {
296 				if (Tokenizer.Type.INLINE_CONDIDION == tokenizer.getTokenType()) {
297 					tokenizer.readToken();
298 					
299 					ICommand trueCommand = parseCommand(null, 0);
300 					
301 					if (Tokenizer.Type.INLINE_SEPARATOR != tokenizer.getTokenType())
302 						throw new TemplateParserException(
303 							context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(),
304 							"Expected \":\", but found \"" + tokenizer.getTokenValue() + "\""
305 						);
306 					
307 					tokenizer.readToken();
308 					
309 					ICommand falseCommand = parseCommand(null, 0);
310 					
311 					result = new InlineConditionCommand(
312 						context.getLocator(), line, column, 
313 						result, trueCommand, falseCommand
314 					);
315 				}
316 				
317 				result = parseTransformerChain(result);
318 			}
319 		}
320 		catch (TokenizerException e) {
321 			throw new TemplateParserException(context.getLocator(), e);
322 		}
323 
324 		return result;
325 	}
326 	
327 	protected boolean isAnyOperator() throws TemplateException {
328 		Tokenizer.Type type = tokenizer.getTokenType();
329 
330 		return 
331 			(Tokenizer.Type.MULTIPLICATIVE_OPERATOR == type) ||
332 			(Tokenizer.Type.ADDITIVE_OPERATOR == type) ||
333 			(Tokenizer.Type.SHIFT_OPERATOR == type) ||
334 			(Tokenizer.Type.RELATIONAL_OPERATOR == type) ||
335 			(Tokenizer.Type.EQUALITY_OPERATOR == type) ||
336 			(Tokenizer.Type.BITWISE_OPERATOR == type) ||
337 			(Tokenizer.Type.CONDITIONAL_AND_OPERATOR == type) ||
338 			(Tokenizer.Type.CONDITIONAL_OR_OPERATOR == type)
339 		;
340 	}
341 	
342 	protected boolean isExpression() {
343 		Tokenizer.Type type = tokenizer.getTokenType();
344 
345 		return 
346 			(Tokenizer.Type.UNARY_OPERATOR == type) || 
347 			(Tokenizer.Type.IDENTIFIER == type) ||
348 			(Tokenizer.Type.QUERY_OPEN == type) ||
349 			(Tokenizer.Type.VARIABLE == type) || 
350 			(Tokenizer.Type.NUMERIC == type) ||
351 			(Tokenizer.Type.STRING == type) ||
352 			(Tokenizer.Type.KEYWORD_TRUE == type) ||
353 			(Tokenizer.Type.KEYWORD_FALSE == type) ||
354 			(Tokenizer.Type.KEYWORD_NULL == type) ||
355 			(Tokenizer.Type.KEYWORD_THIS == type) ||
356 			(Tokenizer.Type.KEYWORD_POP == type)
357 		;
358 	}
359 	
360 	/**
361 	 * Parses the expression.
362 	 * Exprects that the cursor is on the first token. 
363 	 * 
364 	 * Expression = [UnaryOperator] (Invocation | Query | Variable | Number | String | "true" | "false" | "null" | "$this" | "$pop") {ExpressionChain}
365 	 * 
366 	 * @return the expression
367 	 * @throws TemplateException on occasion
368 	 */
369 	protected ICommand parseExpression() throws TemplateException {
370 		ICommand result = null;
371 		
372 		Tokenizer.Type type = tokenizer.getTokenType();
373 		
374 		try {
375 			if (Tokenizer.Type.UNARY_OPERATOR == type) {
376 				Tokenizer.Type unaryType = type;
377 				Object unaryValue = tokenizer.getTokenValue();
378 				int unaryLine = tokenizer.getTokenLine();
379 				int unaryColumn = tokenizer.getTokenColumn();
380 				
381 				tokenizer.readToken();
382 				
383 				result = createSingleParameterCommand(
384 					unaryType, unaryValue, 
385 					unaryLine, unaryColumn, parseSubExpression()
386 				);
387 				
388 			}
389 			else
390 				result = parseSubExpression();
391 		}
392 		catch (TokenizerException e) {
393 			throw new TemplateParserException(context.getLocator(), e);
394 		}
395 		
396 		return result;
397 	}
398 
399 			
400 	protected ICommand parseSubExpression() throws TemplateException {
401 		ICommand result = null;
402 		Tokenizer.Type type = tokenizer.getTokenType();
403 		Object value = tokenizer.getTokenValue();
404 		int line = tokenizer.getTokenLine();
405 		int column = tokenizer.getTokenColumn();
406 		
407 		try {
408 			switch (type) {
409 				case IDENTIFIER:
410 					result = parseInvocation(null);
411 					break;
412 				
413 				case QUERY_OPEN:
414 					result = parseQuery(null);
415 					break;
416 					
417 				case VARIABLE:
418 					if (!context.containsVariable(String.valueOf(value)))
419 						throw new TemplateParserException(
420 							context.getLocator(), line, column,
421 							"The variable " + value + " is not defined"
422 						);
423 					
424 					result = new VariableCommand(context.getLocator(), line, column, String.valueOf(value));
425 					tokenizer.readToken();
426 					break;
427 					
428 				case NUMERIC:
429 				case STRING:
430 					result = new ValueCommand(context.getLocator(), line, column, value);
431 					tokenizer.readToken();
432 					break;
433 					
434 				case KEYWORD_TRUE:
435 					result = new ValueCommand(context.getLocator(), line, column, Boolean.TRUE);
436 					tokenizer.readToken();
437 					break;
438 	
439 				case KEYWORD_FALSE:
440 					result = new ValueCommand(context.getLocator(), line, column, Boolean.FALSE);
441 					tokenizer.readToken();
442 					break;
443 	
444 				case KEYWORD_NULL:
445 					result = new ValueCommand(context.getLocator(), line, column, null);
446 					tokenizer.readToken();
447 					break;
448 	
449 				case KEYWORD_THIS:
450 					result = new ThisCommand(context.getLocator(), line, column);
451 					tokenizer.readToken();
452 					break;
453 	
454 				case KEYWORD_POP:
455 					tokenizer.readToken();
456 					
457 					if (isExpressionChain()) {
458 						ICommand command = parseExpressionChain(null);
459 						
460 						result = new PopCommand(context.getLocator(), line, column, command);
461 					}
462 					else
463 						result = new PopCommand(context.getLocator(), line, column, null);
464 					break;
465 					
466 				default:
467 					throw new TemplateParserException(
468 						context.getLocator(), line, column, 
469 						"Unexpected token"
470 					);
471 			}
472 			
473 			if (isExpressionChain())
474 				result = parseExpressionChain(result);
475 		}
476 		catch (TokenizerException e) {
477 			throw new TemplateParserException(context.getLocator(), e);
478 		}
479 
480 		return result;
481 	}
482 
483 	protected boolean isExpressionChain() {
484 		return 
485 			(Tokenizer.Type.DELEGATOR == tokenizer.getTokenType()) ||
486 			(Tokenizer.Type.QUERY_OPEN == tokenizer.getTokenType())
487 		;
488 	}
489 	
490 	protected ICommand parseExpressionChain(ICommand command) throws TemplateException {
491 		ICommand result = null;
492 		Tokenizer.Type type = tokenizer.getTokenType();
493 		
494 		try {
495 			if (Tokenizer.Type.DELEGATOR == type) {
496 				type = tokenizer.readToken();
497 				
498 				switch (type) {
499 					case IDENTIFIER:
500 						result = parseInvocation(command);
501 						break;
502 						
503 					case KEYWORD_POP:
504 						result = new PopCommand(context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(), command);
505 						tokenizer.readToken();
506 						break;
507 					
508 					default:
509 						throw new TemplateParserException(
510 							context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(),
511 							"Expected identifier or \"pop\""
512 						);
513 				}
514 			}
515 			else if (Tokenizer.Type.QUERY_OPEN == type) {
516 				result = parseQuery(command);
517 			}
518 			else
519 				throw new TemplateParserException(
520 					context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(), 
521 					"Expected \".\" or \"[\""
522 				);
523 			
524 			if (isExpressionChain())
525 				result = parseExpressionChain(result);
526 		}
527 		catch (TokenizerException e) {
528 			throw new TemplateParserException(context.getLocator(), e);
529 		}
530 
531 		return result;
532 	}
533 
534 	protected boolean isInvocation() {
535 		return (Tokenizer.Type.IDENTIFIER == tokenizer.getTokenType());
536 	}
537 	
538 	protected ICommand parseInvocation(ICommand command) throws TemplateException {
539 		int line = tokenizer.getTokenLine();
540 		int column = tokenizer.getTokenColumn();
541 		Object value = tokenizer.getTokenValue();
542 
543 		try {
544 			tokenizer.readToken();
545 			
546 			return new InvocationCommand(
547 				context.getLocator(), line, column, command, 
548 				String.valueOf(value), parseParameterList()
549 			);
550 		}
551 		catch (TokenizerException e) {
552 			throw new TemplateParserException(context.getLocator(), e);
553 		}
554 	}
555 	
556 	protected ICommand parseQuery(ICommand command) throws TemplateException {
557 		ICommand identifierCommand = null;
558 		int line = tokenizer.getTokenLine();
559 		int column = tokenizer.getTokenColumn();
560 		
561 		try {
562 			tokenizer.readToken();
563 			
564 			identifierCommand = parseCommand(null, 0);
565 			
566 			if (tokenizer.getTokenType() != Tokenizer.Type.QUERY_CLOSE)
567 				throw new TemplateRuntimeException(
568 					context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(), 
569 					"Exprected \"]\""
570 				);
571 			
572 			tokenizer.readToken();
573 			
574 			return new QueryCommand(
575 				context.getLocator(), line, column, command,
576 				identifierCommand, parseParameterList()
577 			);
578 		}
579 		catch (TokenizerException e) {
580 			throw new TemplateParserException(context.getLocator(), e);
581 		}
582 	}
583 	
584 	/**
585 	 * Parses the code as parameter list.
586 	 * Expects that the cursor is on the opening parentheses of the list.
587 	 * If this is not the case (no parameter list specified) then this 
588 	 * method returns null.
589 	 * 
590 	 * ParameterList = ["(" {Command {"," Command}} ")"]
591 	 * 
592 	 * @return the commands of the paramters or null
593 	 * @throws TemplateException on occasion
594 	 */
595 	public ICommand[] parseParameterList() throws TemplateException {
596 		List<ICommand> results = null;
597 
598 		try {
599 			if (Tokenizer.Type.PARENTHESIS_OPEN == tokenizer.getTokenType()) {
600 				// there is a parameters list
601 				tokenizer.readToken();
602 	
603 				results = new ArrayList<ICommand>();
604 				
605 				if (Tokenizer.Type.PARENTHESIS_CLOSE == tokenizer.getTokenType()) {
606 					// the parameter list has ended immediately. 
607 					// Skip the parenthesis but return an empty parameter list
608 					tokenizer.readToken();
609 				}
610 				else while (true) {
611 					// as long as there are more parameters, parse them
612 					results.add(parseCommand(null, 0));
613 		
614 					Tokenizer.Type type = tokenizer.getTokenType();
615 					
616 					if (Tokenizer.Type.PARENTHESIS_CLOSE == type) {
617 						// the parameter list has ended. Break the loop.
618 						tokenizer.readToken();
619 						break;
620 					}
621 					else if (Tokenizer.Type.SEPARATOR == type) {
622 						// the next parameter is following, skip the comma
623 						tokenizer.readToken();
624 					}
625 					else
626 						throw new TemplateParserException(
627 							context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(), 
628 							"Expected \")\" or \",\" but \"" + tokenizer.getTokenValue() + "\" found"
629 						);
630 				}
631 			}
632 		}
633 		catch (TokenizerException e) {
634 			throw new TemplateParserException(context.getLocator(), e);
635 		}
636 		
637 		return (results != null) ? (ICommand[])results.toArray(new ICommand[results.size()]) : null;
638 	}
639 
640 	/**
641 	 * Parses the code as variable list.
642 	 * Expects that the cursor is on the opening parentheses of the list.
643 	 * If this is not the case (no parameter list specified) then this 
644 	 * method returns null.
645 	 * 
646 	 * VariableList = ["(" {Variable {"," Variable}} ")"]
647 	 * 
648 	 * @return the variables or null
649 	 * @throws TemplateException on occasion
650 	 */
651 	public String[] parseVariableList() throws TemplateException {
652 		List<String> results = null;
653 
654 		try {
655 			if (Tokenizer.Type.PARENTHESIS_OPEN == tokenizer.getTokenType()) {
656 				// there is a variables list
657 				tokenizer.readToken();
658 	
659 				results = new ArrayList<String>();
660 				
661 				if (Tokenizer.Type.PARENTHESIS_CLOSE == tokenizer.getTokenType()) {
662 					// the variables list has ended immediately. 
663 					// Skip the parenthesis but return an empty variables list
664 					tokenizer.readToken();
665 				}
666 				else while (true) {
667 					// as long as there are more variables, parse them
668 					Tokenizer.Type type = tokenizer.getTokenType();
669 	
670 					if (Tokenizer.Type.VARIABLE != type)
671 						throw new TemplateParserException(
672 							context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(),
673 							"Expected a variable but \"" + tokenizer.getTokenValue() + "\" found"
674 						);
675 					
676 					results.add(String.valueOf(tokenizer.getTokenValue()));
677 		
678 					type = tokenizer.readToken();
679 					
680 					if (Tokenizer.Type.PARENTHESIS_CLOSE == type) {
681 						// the variables list has ended. Break the loop.
682 						tokenizer.readToken();
683 						break;
684 					}
685 					else if (Tokenizer.Type.SEPARATOR == type) {
686 						// the next variable is following, skip the comma
687 						tokenizer.readToken();
688 					}
689 					else
690 						throw new TemplateParserException(
691 							context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(), 
692 							"Expected \")\" or \",\" but \"" + tokenizer.getTokenValue() + "\" found"
693 						);
694 				}
695 			}
696 		}
697 		catch (TokenizerException e) {
698 			throw new TemplateParserException(context.getLocator(), e);
699 		}
700 		
701 		return (results != null) ? (String[])results.toArray(new String[results.size()]) : null;
702 	}
703 
704 	/**
705 	 * Parses the code as identifier list.
706 	 * Expects that the cursor is on the opening parentheses of the list.
707 	 * If this is not the case (no identifier list specified) then this 
708 	 * method returns null.
709 	 * 
710 	 * IdentifierList = ["(" {Identifier {"," Identifier}} ")"]
711 	 * 
712 	 * @return the identifiers or null
713 	 * @throws TemplateException on occasion
714 	 */
715 	public String[] parseIdentifierList() throws TemplateException {
716 		List<String> results = null;
717 
718 		try {
719 			if (Tokenizer.Type.PARENTHESIS_OPEN == tokenizer.getTokenType()) {
720 				// there is a identifier list
721 				tokenizer.readToken();
722 	
723 				results = new ArrayList<String>();
724 				
725 				if (Tokenizer.Type.PARENTHESIS_CLOSE == tokenizer.getTokenType()) {
726 					// the identifier list has ended immediately. 
727 					// Skip the parenthesis but return an empty identifier list
728 					tokenizer.readToken();
729 				}
730 				else while (true) {
731 					// as long as there are more identifiers, parse them
732 					if (Tokenizer.Type.IDENTIFIER != tokenizer.getTokenType())
733 						throw new TemplateParserException(
734 							context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(),
735 							"Expected identifier, but found \"" + tokenizer.getTokenValue() + "\""
736 						);
737 					
738 					results.add(String.valueOf(tokenizer.getTokenValue()));
739 	
740 					tokenizer.readToken();
741 					
742 					Tokenizer.Type type = tokenizer.getTokenType();
743 					
744 					if (Tokenizer.Type.PARENTHESIS_CLOSE == type) {
745 						// the identifier list has ended. Break the loop.
746 						tokenizer.readToken();
747 						break;
748 					}
749 					else if (Tokenizer.Type.SEPARATOR == type) {
750 						// the next identifier is following, skip the comma
751 						tokenizer.readToken();
752 					}
753 					else
754 						throw new TemplateParserException(
755 							context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(), 
756 							"Expected \")\" or \",\", but \"" + tokenizer.getTokenValue() + "\" found"
757 						);
758 				}
759 			}
760 		}
761 		catch (TokenizerException e) {
762 			throw new TemplateParserException(context.getLocator(), e);
763 		}
764 		
765 		return (results != null) ? (String[])results.toArray(new String[results.size()]) : null;
766 	}
767 
768 	/**
769 	 * Parses the code as identifier extension.
770 	 * Expects that the cursor is on the first token.
771 	 * If there is no extension, the method returns null.
772 	 * 
773 	 * IdentifierExtension = ["." Identifier]
774 	 * 
775 	 * @return the extension or null
776 	 */
777 	public String parseIdentifierExtension() throws TemplateException {
778 		String result = null;
779 		
780 		try {
781 			if (Tokenizer.Type.DELEGATOR == tokenizer.getTokenType()) {
782 				tokenizer.readToken();
783 				
784 				Tokenizer.Type type = tokenizer.getTokenType();
785 	
786 				if (Tokenizer.Type.IDENTIFIER != type)
787 					throw new TemplateParserException(
788 						context.getLocator(), tokenizer.getTokenColumn(), tokenizer.getTokenLine(),
789 						"Expected identifier but \"" + tokenizer.getTokenValue() + "\" found"
790 					);
791 				
792 				result = String.valueOf(tokenizer.getTokenValue());
793 			}
794 		}
795 		catch (TokenizerException e) {
796 			throw new TemplateParserException(context.getLocator(), e);
797 		}
798 		
799 		return result;
800 	}
801 	
802 	protected ICommand createSingleParameterCommand(Tokenizer.Type operator, Object value, int line, int column, ICommand command) throws TemplateException {
803 		if (Tokenizer.Type.UNARY_OPERATOR == operator) {
804 			if ("+".equals(value))
805 				return new PositiveCommand(context.getLocator(), line, column, command);
806 			else if ("-".equals(value))
807 				return new NegativeCommand(context.getLocator(), line, column, command);
808 			else if ("!".equals(value))
809 				return new NotCommand(context.getLocator(), line, column, command);
810 			else if ("~".equals(value))
811 				return new ComplementCommand(context.getLocator(), line, column, command);
812 		}
813 		
814 		throw new TemplateParserException(
815 			context.getLocator(), line, column, 
816 			"Unexpected symbol \"" + value + "\" in operation \"" + operator
817 		);
818 	}
819 
820 	protected ICommand createDoubleParameterCommand(Tokenizer.Type operator, Object value, int line, int column, ICommand left, ICommand right) throws TemplateException {
821 		switch (operator) {
822 			case MULTIPLICATIVE_OPERATOR:
823 				if ("*".equals(value))
824 					return new MultiplyCommand(context.getLocator(), line, column, left, right);
825 				else if ("/".equals(value))
826 					return new DivideCommand(context.getLocator(), line, column, left, right);
827 				else if ("%".equals(value))
828 					return new ModuloCommand(context.getLocator(), line, column, left, right);
829 				break;
830 				
831 			case ADDITIVE_OPERATOR: 
832 				if ("+".equals(value))
833 					return new AdditionCommand(context.getLocator(), line, column, left, right);
834 				else if ("-".equals(value))
835 					return new SubstractCommand(context.getLocator(), line, column, left, right);
836 				break;
837 				
838 			case SHIFT_OPERATOR:
839 				if ("<<".equals(value))
840 					return new ShiftLeftCommand(context.getLocator(), line, column, left, right);
841 				else if (">>".equals(value))
842 					return new ShiftRightCommand(context.getLocator(), line, column, left, right);
843 				else if (">>>".equals(value))
844 					return new UnsignedShiftRightCommand(context.getLocator(), line, column, left, right);
845 				break;
846 				
847 			case RELATIONAL_OPERATOR:
848 				if ("<".equals(value))
849 					return new LessThanCommand(context.getLocator(), line, column, left, right);
850 				else if ("<=".equals(value))
851 					return new LessEqualCommand(context.getLocator(), line, column, left, right);
852 				else if (">".equals(value))
853 					return new GreaterThanCommand(context.getLocator(), line, column, left, right);
854 				else if (">=".equals(value))
855 					return new GreaterEqualCommand(context.getLocator(), line, column, left, right);
856 				break;
857 				
858 			case EQUALITY_OPERATOR:
859 				if ("==".equals(value))
860 					return new EqualsCommand(context.getLocator(), line, column, left, right);
861 				else if ("!=".equals(value))
862 					return new NotEqualsCommand(context.getLocator(), line, column, left, right);
863 				break;
864 				
865 			case BITWISE_OPERATOR:
866 				if ("&".equals(value))
867 					return new BitwiseAndCommand(context.getLocator(), line, column, left, right);
868 				else if ("^".equals(value))
869 					return new BitwiseExclusiveOrCommand(context.getLocator(), line, column, left, right);
870 				else if ("|".equals(value))
871 					return new BitwiseInclusiveOrCommand(context.getLocator(), line, column, left, right);
872 				break;
873 			
874 			case CONDITIONAL_AND_OPERATOR:
875 				if ("&&".equals(value))
876 					return new AndCommand(context.getLocator(), line, column, left, right);
877 				break;
878 			
879 			case CONDITIONAL_OR_OPERATOR:
880 				if ("||".equals(value))
881 					return new OrCommand(context.getLocator(), line, column, left, right);
882 				break;
883 				
884 			default:
885 				throw new TemplateParserException(
886 					context.getLocator(), line, column, "Unexpected token"
887 				);
888 		}
889 		
890 		throw new TemplateParserException(
891 			context.getLocator(), line, column, 
892 			"Unexpected symbol \"" + value + "\" in operation \"" + operator
893 		);
894 	}
895 
896     /**
897      * Parses the transformer notation.
898      * Expects that the cursor is on the transformer symbol (->).
899      * If there is no such symbol, it assumes that there is no transformer appended
900      * and returns the command itself.
901      * 
902      * TransformerChain = {"->" Identifier ["." Identifier] ParameterList}
903      * 
904      * @param command the command to be sent to the transformer
905      * @return the command itself or wrapped by a transformer
906      * @throws TemplateException on occasion
907      */
908     public ICommand parseTransformerChain(ICommand command) throws TemplateException {
909     	ICommand result = command;
910     	
911     	try {
912 			while (Tokenizer.Type.TRANSFORMER_CALL == tokenizer.getTokenType()) {
913 				// there is a transformer in the chain; skip the transformer symbol
914 				tokenizer.readToken();
915 	    	
916 				Tokenizer.Type type = tokenizer.getTokenType();
917 				int transformerLine = tokenizer.getTokenLine();
918 				int transformerColumn = tokenizer.getTokenColumn();
919 				
920 				// must be: Identifier ... 
921 				if (Tokenizer.Type.IDENTIFIER != type)
922 					throw new TemplateParserException(
923 						context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(), 
924 						"Identifier expected"
925 					);
926 	
927 				String transformerName = String.valueOf(tokenizer.getTokenValue());
928 				Object transformerExtension = null;
929 			
930 				type = tokenizer.readToken();
931 			
932 				// in case: ... ["." Identifier] ...
933 				if (Tokenizer.Type.DELEGATOR == type) {
934 					type = tokenizer.readToken();
935 					
936 					// must be: ... Identifier ...
937 					if (Tokenizer.Type.IDENTIFIER != type)
938 						throw new TemplateParserException(
939 							context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(),
940 							"Identifier expected"
941 						);
942 					
943 					transformerExtension = tokenizer.getTokenValue();
944 						
945 					type = tokenizer.readToken();
946 				}
947 			
948 				result = new TransformerCommand(
949 					context.getLocator(), transformerLine, transformerColumn,
950 					engine.getTransformerService(), String.valueOf(transformerName), 
951 					(transformerExtension != null) ? String.valueOf(transformerExtension) : null,
952 					result, parseParameterList()
953 				);
954 			}
955 		}
956 		catch (TokenizerException e) {
957 			throw new TemplateParserException(context.getLocator(), e);
958 		}
959 		
960 		return result;
961     }
962 
963     public String parseAssignedKey() throws TemplateException {
964     	String key = null;
965     	
966     	try {
967 	    	if (Tokenizer.Type.ASSIGNMENT != tokenizer.getTokenType()) 
968 	    		throw new TemplateParserException(
969 	    			context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(),
970 	    			"Expected \"=\", but found \"" + tokenizer.getTokenValue() + "\""
971 	    		);
972 	    	
973 	    	tokenizer.readKeyToken();
974 	    	
975 			if ((Tokenizer.Type.KEY != tokenizer.getTokenType()) && (Tokenizer.Type.STRING != tokenizer.getTokenType())) 
976 				throw new TemplateParserException(
977 					context.getLocator(), tokenizer.getTokenLine(), tokenizer.getTokenColumn(),
978 					"Key identifier expected, but \"" + tokenizer.getTokenValue() + "\" found"
979 				);
980 	
981 			key = String.valueOf(tokenizer.getTokenValue());
982 			
983 			tokenizer.readToken();
984 		}
985 		catch (TokenizerException e) {
986 			throw new TemplateParserException(context.getLocator(), e);
987 		}
988 		
989 		return key;
990 
991     }
992     
993 	protected boolean isAnyOperator(Tokenizer.Type type) {
994 		return 
995 			(type == Tokenizer.Type.MULTIPLICATIVE_OPERATOR) || 
996 			(type == Tokenizer.Type.ADDITIVE_OPERATOR) ||
997 			(type == Tokenizer.Type.SHIFT_OPERATOR) ||
998 			(type == Tokenizer.Type.RELATIONAL_OPERATOR) ||
999 			(type == Tokenizer.Type.EQUALITY_OPERATOR) ||
1000 			(type == Tokenizer.Type.BITWISE_OPERATOR) ||
1001 			(type == Tokenizer.Type.CONDITIONAL_AND_OPERATOR) ||
1002 			(type == Tokenizer.Type.CONDITIONAL_OR_OPERATOR)
1003 		;
1004 	}
1005 	
1006 }