PageRenderTime 153ms CodeModel.GetById 132ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 1ms

/src/Manos/Manos.Template/TemplateParser.cs

http://github.com/jacksonh/manos
C# | 585 lines | 238 code | 77 blank | 270 comment | 45 complexity | 045b05ddf225dae1c38ddb42c89b6e87 MD5 | raw file
  1//
  2// Copyright (C) 2010 Jackson Harper (jackson@manosdemono.com)
  3//
  4// Permission is hereby granted, free of charge, to any person obtaining
  5// a copy of this software and associated documentation files (the
  6// "Software"), to deal in the Software without restriction, including
  7// without limitation the rights to use, copy, modify, merge, publish,
  8// distribute, sublicense, and/or sell copies of the Software, and to
  9// permit persons to whom the Software is furnished to do so, subject to
 10// the following conditions:
 11// 
 12// The above copyright notice and this permission notice shall be
 13// included in all copies or substantial portions of the Software.
 14// 
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 16// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 17// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 18// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 19// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 20// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 21// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 22//
 23//
 24
 25
 26using System;
 27using System.IO;
 28using System.Text;
 29
 30using System.Collections.Generic;
 31
 32
 33namespace Manos.Templates {
 34
 35	public class TemplateParser {
 36
 37	        private ITemplateCodegen codegen;
 38		private TemplateEnvironment environment;
 39
 40		public TemplateParser (TemplateEnvironment environment, ITemplateCodegen codegen)
 41		{
 42			this.codegen = codegen;
 43			this.environment = environment;
 44		}
 45
 46		public void ParsePage (string name, TextReader reader)
 47		{
 48			Console.WriteLine ("parsing page:  {0}", name);
 49			TemplateTokenizer tk = new TemplateTokenizer (environment, reader);
 50
 51			codegen.BeginPage (name);
 52
 53			Token tok = null;
 54			StringBuilder data = new StringBuilder ();
 55
 56			while (true) {
 57				tok = tk.GetNextToken ();
 58
 59				switch (tok.Type) {
 60				case TokenType.TOKEN_VARIABLE_BEGIN:
 61					FlushData (data);
 62					ParseVariable (tk);
 63					break;
 64				case TokenType.TOKEN_COMMENT_BEGIN:
 65					FlushData (data);
 66					ParseComment (tk);
 67					break;
 68				case TokenType.TOKEN_BLOCK_BEGIN:
 69					FlushData (data);
 70					ParseControlBlock (tk);
 71					break;
 72				case TokenType.TOKEN_EOF:
 73					FlushData (data);
 74					codegen.EndPage ();
 75					return;
 76				default:
 77					data.Append (tok.Value);
 78					break;
 79				}
 80			}
 81		}
 82
 83		public void FlushData (StringBuilder data)
 84		{
 85			codegen.AddData (data.ToString ());
 86			data.Length = 0;
 87		}
 88
 89		public void ParseVariable (TemplateTokenizer tk)
 90		{
 91			Expression exp = ParseExpression (tk, TokenType.TOKEN_BLOCK_END);
 92
 93			if (tk.Current.Type != TokenType.TOKEN_VARIABLE_END)
 94				RaiseFailure (tk, String.Format ("Invalid variable statement found, '{0}' token found when a {1} was expected.",
 95						tk.Current.Value, environment.VariableEndString));
 96						
 97			codegen.EmitSinglePrint (exp);
 98		}
 99
100		public void ParseComment (TemplateTokenizer tk)
101		{
102			Token tok;
103			StringBuilder builder = new StringBuilder ();
104			do {
105				tok = tk.GetNextToken ();
106
107				if (tok.Type == TokenType.TOKEN_COMMENT_END) {
108					return;
109				}
110					
111				builder.Append (tok.Value);
112
113			} while (tok.Type != TokenType.TOKEN_EOF);
114
115			// FAIL
116		}
117
118		public void ParseControlBlock (TemplateTokenizer tk)
119		{
120			Token tok;
121			StringBuilder builder = new StringBuilder ();
122
123			ParseStatement (tk);
124			tok = tk.Current;
125
126			do {
127				if (tok.Type == TokenType.TOKEN_BLOCK_END) {
128					return;
129				}
130					
131				builder.Append (tok.Value);
132
133				tok = tk.GetNextToken ();
134
135			} while (tok.Type != TokenType.TOKEN_EOF);
136		}
137
138		public void ParseStatement (TemplateTokenizer tk)
139		{
140			Token tok = NextNonWhiteSpaceToken (tk);
141
142			if (tok.Type != TokenType.TOKEN_NAME) {
143				// fail
144				Console.WriteLine ("INVALID BLOCK TOKEN TYPE");
145			}
146
147			switch (tok.Value) {
148//			case "print":
149//				ParsePrint (tk);
150//				break;
151			case "foreach":
152				ParseForeachLoop (tk);
153				break;
154			case "endforeach":
155				ParseEndForeachLoop (tk);
156				break;
157			case "if":
158				ParseIf (tk);
159				break;
160			case "else":
161				ParseElse (tk);
162				break;
163			case "endif":
164				ParseEndIf (tk);
165				break;
166			case "block":
167				ParseBlock (tk);
168				break;
169			case "endblock":
170				ParseEndBlock (tk);
171				break;
172			case "extends":
173				ParseExtends (tk);
174				break;
175//			case "macro":
176//				ParseMacro (tk);
177//				break;
178//			case "endmacro":
179//				ParseEndMacro (tk);
180//				break;			
181//			case "set":
182//				ParseSet (tk);
183//				break;
184			case "include":
185			case "from":
186			case "import":
187			case "call":
188			case "filter":
189			default:
190				throw new InvalidOperationException (String.Format ("Unsupported operation {0}", tok.Value));
191				break;
192			}
193		}
194
195/*
196		public void ParsePrint (TemplateTokenizer tk)
197		{
198			Token tok;
199			bool first = true;
200			List<Expression> expressions = new List<Expression> ();
201
202			do {
203				Expression exp = ParseExpression (tk, TokenType.TOKEN_BLOCK_END);
204
205				if (exp != null)
206					expressions.Add (exp);
207				
208				tok = tk.Current;
209				if (tok.Type == TokenType.TOKEN_BLOCK_END)
210					break;
211
212				if (!first && tok.Type != TokenType.TOKEN_COMMA)
213					RaiseFailure (tk, "Invalid token found in print statement '{0}'");
214
215			} while (tok.Type != TokenType.TOKEN_EOF);
216
217			if (tok.Type == TokenType.TOKEN_EOF)
218				RaiseFailure (tk, "Unexpected end of file.");
219
220			codegen.EmitPrint (expressions);
221		}
222
223*/
224/*
225		public void ParseSet (TemplateTokenizer tk)
226		{
227			Token tok = NextNonWhiteSpaceToken (tk);
228			if (tok.Type != TokenType.TOKEN_NAME)
229				RaiseFailure (tk, String.Format ("Invalid token found in set statement, expected a name got a {0}", tok.Value));
230
231			NamedTarget target = new NamedTarget (tok.Value);
232
233			tok = NextNonWhiteSpaceToken (tk);
234			if (tok.Type != TokenType.TOKEN_ASSIGN)
235				RaiseFailure (tk, String.Format ("Invalid token found in set statement, expected an = got a {0}", tok.Value));
236			
237			Expression expression = ParseExpression (tk, TokenType.TOKEN_BLOCK_END);
238
239			codegen.EmitSet (target, expression);
240		}
241*/
242		public void ParseIf (TemplateTokenizer tk)
243		{
244			Expression expression = ParseExpression (tk, TokenType.TOKEN_BLOCK_END);
245
246			codegen.EmitIf (expression);
247		}
248
249		public void ParseElse (TemplateTokenizer tk)
250		{
251			Expression condition = null;
252			Token tok = NextNonWhiteSpaceToken (tk);
253			if (tok.Type != TokenType.TOKEN_BLOCK_END)
254			   condition = ParseExpression (tk, TokenType.TOKEN_BLOCK_END);
255
256			codegen.EmitElse (null);
257		}
258
259		public void ParseEndIf (TemplateTokenizer tk)
260		{
261			Token tok = NextNonWhiteSpaceToken (tk);
262			while (tok.Type != TokenType.TOKEN_BLOCK_END) {
263
264				tok = NextNonWhiteSpaceToken (tk);
265			}
266
267			codegen.EmitEndIf ();
268		}
269
270		public void ParseBlock (TemplateTokenizer tk)
271		{
272			Token tok = NextNonWhiteSpaceToken (tk);
273
274			if (tok.Type != TokenType.TOKEN_NAME)
275				RaiseFailure (tk, String.Format ("Invalid '{0}' token found in block statement.", tok.Value));
276			
277			string name = tok.Value;
278
279			/*
280			List<ArgumentDefinition> args = null;
281
282			tok = NextNonWhiteSpaceToken (tk);
283			if (tok.Type == TokenType.TOKEN_LPAREN) {
284				args = ParseArgumentDefinitions (tk);
285				tok = NextNonWhiteSpaceToken (tk);
286			}
287			*/
288
289			codegen.BeginBlock (name);
290
291			tok = NextNonWhiteSpaceToken (tk);
292			if (tok.Type != TokenType.TOKEN_BLOCK_END)
293				RaiseFailure (tk, String.Format ("Invalid '{0}' token found in block statement.", tok.Value));
294		}
295
296		public void ParseEndBlock (TemplateTokenizer tk)
297		{
298			Token tok = NextNonWhiteSpaceToken (tk);
299
300			string name = null;
301			if (tok.Type == TokenType.TOKEN_NAME) {
302				name = tok.Value;
303				tok = NextNonWhiteSpaceToken (tk);
304			}
305
306			// Name matching is optional, we pass null if no name is supplied
307			codegen.EndBlock (name);
308
309			if (tok.Type != TokenType.TOKEN_BLOCK_END)
310				RaiseFailure (tk, String.Format ("Invalid '{0}' token found in endblock statement.", tok.Value));
311		}
312
313		public void ParseExtends (TemplateTokenizer tk)
314		{
315			Token tok = NextNonWhiteSpaceToken (tk);
316
317			if (tok.Type != TokenType.TOKEN_QUOTED_STRING)
318				RaiseFailure (tk, String.Format ("Invalid '{0}' token found in extends statement.", tok.Value));
319
320			codegen.EmitExtends (ValueOfQuotedString (tok.Value));
321
322			tok = NextNonWhiteSpaceToken (tk);
323
324			if (tok.Type != TokenType.TOKEN_BLOCK_END)
325				RaiseFailure (tk, String.Format ("Invalid '{0}' token found in extends statement.", tok.Value));
326		}
327/*
328		public void ParseMacro (TemplateTokenizer tk)
329		{
330			Token tok = NextNonWhiteSpaceToken (tk);
331
332			if (tok.Type != TokenType.TOKEN_NAME)
333				RaiseFailure (tk, String.Format ("Invalid macro definition, expected name got a '{0}'.", tok.Value));
334
335			string name = tok.Value;
336			List<ArgumentDefinition> args = null;
337
338			tok = NextNonWhiteSpaceToken (tk);
339
340			if (tok.Type == TokenType.TOKEN_LPAREN) {
341				args = ParseArgumentDefinitions (tk);
342				tok = NextNonWhiteSpaceToken (tk);
343			}
344
345			codegen.BeginMacro (name, args);
346
347			if (tok.Type != TokenType.TOKEN_BLOCK_END)
348				RaiseFailure (tk, String.Format ("Invalid macro definition, expect block end got a '{0}'.", tok.Value));
349		}
350
351		public void ParseEndMacro (TemplateTokenizer tk)
352		{
353			string name = null;
354			Token tok = NextNonWhiteSpaceToken (tk);
355
356			if (tok.Type == TokenType.TOKEN_NAME) {
357				name = tok.Value;
358				tok = NextNonWhiteSpaceToken (tk);
359			}
360
361			if (tok.Type != TokenType.TOKEN_BLOCK_END)
362				RaiseFailure (tk, String.Format ("Invalid endmacro definition, expected a block end got a '{0}'.", tok.Value));
363			
364			current_page.EndMacro (name);
365		}
366*/
367		public Expression ParseExpression (TemplateTokenizer tk, TokenType end_token_type, bool allow_conditionals=true)
368		{
369			Expression expression = null;
370			/*
371			Value target_value = ParseRValue (tk);
372
373			Token tok = NextNonWhiteSpaceToken (tk);
374			if (tok.Type == TokenType.TOKEN_DOT) {
375				tok = NextNonWhiteSpaceToken (tk);
376				if (tok.Type != TokenType.TOKEN_NAME)
377					RaiseFailure (tk, String.Format ("Invalid expression, token '{0}' found where a name was expected.", tok.Value));
378				expression = new Expression (new PropertyAccessValue (target_value, tok.Value));
379				NextNonWhiteSpaceToken (tk);
380			} else if (tok.Type == TokenType.TOKEN_LBRACKET) {
381				string prop_name = ParseSubscript (tk);
382				expression = new Expression (new PropertyAccessValue (target_value, prop_name));
383				NextNonWhiteSpaceToken (tk);
384			} else if (tok.Type == TokenType.TOKEN_LPAREN) {
385
386				VariableValue target = target_value as VariableValue;
387
388				if (target == null)
389					RaiseFailure (tk, String.Format ("Invalid invoke expression, expected a name got a {0}", target_value));
390
391				List<Expression> args = ParseArguments (tk);
392
393				if (tk.Current.Type != TokenType.TOKEN_RPAREN)
394					RaiseFailure (tk, String.Format ("Invalid invoke expression, token '{0}' where a ) was expected.", tk.Current.Value));
395
396				expression = new Expression (new InvokeValue (target.Name.Name, args));
397				NextNonWhiteSpaceToken (tk);
398			} else
399				expression = new Expression (target_value);
400
401			while (tok.Type != end_token_type && tok.Type != TokenType.TOKEN_EOF) {
402
403				if (tok.Type == TokenType.TOKEN_PIPE) {
404					TemplateFilter filter = ParseFilter (tk);
405					expression.AddFilter (filter);
406				} else if (allow_conditionals && IsConditionalToken (tok)) {
407					CompareOperator compare = CompareOperatorFromToken (tok);
408					// expression = new ConditionalExpression (expression, compare, ParseExpression (tk, end_token_type));
409				} else 
410					break;
411
412				tok = tk.Current;
413			}
414
415			if (tok.Type == TokenType.TOKEN_EOF)
416				RaiseFailure (tk, "Unexpected eof of file found while parsing expression.");
417			*/
418			return expression;
419			
420		}
421
422		public void ParseForeachLoop (TemplateTokenizer tk)
423		{
424			Token tok = NextNonWhiteSpaceToken (tk);
425
426			if (tok.Type != TokenType.TOKEN_NAME)
427				RaiseFailure (tk, String.Format ("Invalid for loop, expected a name got token '{0}'", tok.Value));
428
429			string variable_name = tok.Value;
430
431			Expect (tk, TokenType.TOKEN_NAME, "in", "Invalid for loop, no 'in' statement found. '{0}' found instead.");
432
433			Expression iter = ParseExpression (tk, TokenType.TOKEN_BLOCK_END);
434
435			codegen.BeginForeachLoop (variable_name, iter);
436		}
437
438		public void ParseEndForeachLoop (TemplateTokenizer tk)
439		{
440			Token tok = NextNonWhiteSpaceToken (tk);
441			while (tok.Type != TokenType.TOKEN_BLOCK_END) {
442
443				tok = NextNonWhiteSpaceToken (tk);
444			}
445
446			codegen.EndForeachLoop ();
447		}
448
449		public string ParseSubscript (TemplateTokenizer tk)
450		{
451			Token tok = NextNonWhiteSpaceToken (tk);
452
453			if (tok.Type != TokenType.TOKEN_QUOTED_STRING)
454				RaiseFailure (tk, "Invalid subscript expression, token '{0}' found where a quoted string was expected.");
455
456			string value = ValueOfQuotedString (tok.Value);
457
458			tok = NextNonWhiteSpaceToken (tk);
459
460			if (tok.Type != TokenType.TOKEN_RBRACKET)
461				RaiseFailure (tk, "Invalid subscript expression, token '{0}' found where a ] was expected.");
462
463			return value;
464		}
465
466		/*
467		public TemplateFilter ParseFilter (TemplateTokenizer tk)
468		{
469			Token tok = NextNonWhiteSpaceToken (tk);
470
471			if (tok.Type != TokenType.TOKEN_NAME)
472				RaiseFailure (tk, String.Format ("Invalid filter expression, token '{0}' found where a name was expected.", tok.Value));
473
474			string name = tok.Value;
475			List<Expression> args = new List<Expression> ();
476
477			tok = NextNonWhiteSpaceToken (tk);
478			if (tok.Type == TokenType.TOKEN_LPAREN) {
479				args = ParseArguments (tk);
480
481				if (tk.Current.Type != TokenType.TOKEN_RPAREN)
482					RaiseFailure (tk, String.Format ("Invalid filter expression, token '{0}' where a ) was expected.", tk.Current.Value));
483
484				// Advance pass the RPAREN
485				NextNonWhiteSpaceToken (tk);
486			}
487
488			return new TemplateFilter (name, args);
489		}
490		*/
491/*
492		public List<Expression> ParseArguments (TemplateTokenizer tk)
493		{
494			List<Expression> expressions = new List<Expression> ();
495
496			Token tok = null;
497			do {
498				Expression expression = ParseExpression (tk, TokenType.TOKEN_COMMA);
499				expressions.Add (expression);
500
501				tok = tk.Current;
502				if (tok.Type == TokenType.TOKEN_RPAREN)
503					break;
504
505				if (tok.Type != TokenType.TOKEN_COMMA)
506					RaiseFailure (tk, String.Format ("Invalid argument list, expected comma got a {0}", tk.Current.Value));
507			} while (tok.Type != TokenType.TOKEN_EOF);
508
509			if (tk.Current.Type == TokenType.TOKEN_EOF)
510				RaiseFailure (tk, String.Format ("Unexpected end of file."));
511
512			return expressions;
513		}
514
515		public List<ArgumentDefinition> ParseArgumentDefinitions (TemplateTokenizer tk)
516		{
517			Token tok = NextNonWhiteSpaceToken (tk);
518			List<ArgumentDefinition> args = new List<ArgumentDefinition> ();
519
520			do {
521				if (tok.Type == TokenType.TOKEN_RPAREN)
522					break;
523
524				if (tok.Type != TokenType.TOKEN_NAME)
525					RaiseFailure (tk, String.Format ("Invalid argument definition, expected a name got a '{0}'", tok.Value));
526
527				string name = tok.Value;
528				ConstantValue default_value = null;
529
530				tok = NextNonWhiteSpaceToken (tk);
531
532				if (tok.Type == TokenType.TOKEN_ASSIGN) {
533					default_value = ParseConstantValue (tk);
534					tok = NextNonWhiteSpaceToken (tk);
535				}
536
537				args.Add (new ArgumentDefinition (name, default_value));
538
539				if (tok.Type == TokenType.TOKEN_RPAREN)
540					break;
541				
542				if (tok.Type != TokenType.TOKEN_COMMA)
543					RaiseFailure (tk, String.Format ("Invalid argument list, expected comma got a {0}", tk.Current.Type));
544
545				tok = NextNonWhiteSpaceToken (tk);
546			} while (tok.Type != TokenType.TOKEN_EOF);
547
548			if (tk.Current.Type == TokenType.TOKEN_EOF)
549				RaiseFailure (tk, String.Format ("Unexpected end of file."));
550
551			return args;
552		}
553*/
554		public Token NextNonWhiteSpaceToken (TemplateTokenizer tk)
555		{
556			Token tok;
557
558			do {
559				tok = tk.GetNextToken ();
560			} while (tok.Type == TokenType.TOKEN_WHITESPACE);
561
562			return tok;
563		}
564
565		private static string ValueOfQuotedString (string str)
566		{
567			string res = str.Substring (1, str.Length - 2);
568			return res;
569		}
570
571		private void Expect (TemplateTokenizer tk, TokenType type, string value, string error="Expected symbol {0} not found.")
572		{
573			Token tok = NextNonWhiteSpaceToken (tk);
574
575			if (tok.Type != type || tok.Value != value)
576				RaiseFailure (tk, String.Format (error, value));
577		}
578			
579		private void RaiseFailure (TemplateTokenizer tk, string error)
580		{
581			throw new Exception (String.Format ("({0}:{1}) FAILURE: {2}", tk.Line, tk.Column, error));
582		}
583	}
584}
585