aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Prebuild/src/Core/Parse/Preprocessor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Prebuild/src/Core/Parse/Preprocessor.cs')
-rw-r--r--Prebuild/src/Core/Parse/Preprocessor.cs272
1 files changed, 165 insertions, 107 deletions
diff --git a/Prebuild/src/Core/Parse/Preprocessor.cs b/Prebuild/src/Core/Parse/Preprocessor.cs
index eea5c30..013f8e1 100644
--- a/Prebuild/src/Core/Parse/Preprocessor.cs
+++ b/Prebuild/src/Core/Parse/Preprocessor.cs
@@ -23,18 +23,10 @@ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY O
23*/ 23*/
24#endregion 24#endregion
25 25
26#region CVS Information
27/*
28 * $Source$
29 * $Author: jendave $
30 * $Date: 2007-04-26 17:10:27 +0900 (Thu, 26 Apr 2007) $
31 * $Revision: 236 $
32 */
33#endregion
34
35using System; 26using System;
36using System.Collections; 27using System.Collections;
37using System.IO; 28using System.IO;
29using System.Text.RegularExpressions;
38using System.Xml; 30using System.Xml;
39 31
40namespace Prebuild.Core.Parse 32namespace Prebuild.Core.Parse
@@ -79,6 +71,16 @@ namespace Prebuild.Core.Parse
79 /// </summary> 71 /// </summary>
80 public class Preprocessor 72 public class Preprocessor
81 { 73 {
74 #region Constants
75
76 /// <summary>
77 /// Includes the regex to look for file tags in the <?include
78 /// ?> processing instruction.
79 /// </summary>
80 private static readonly Regex includeFileRegex = new Regex("file=\"(.+?)\"");
81
82 #endregion
83
82 #region Fields 84 #region Fields
83 85
84 XmlDocument m_OutDoc; 86 XmlDocument m_OutDoc;
@@ -138,10 +140,10 @@ namespace Prebuild.Core.Parse
138 return "Win32"; 140 return "Win32";
139 } 141 }
140 142
141 if (File.Exists("/System/Library/Frameworks/Cocoa.framework/Cocoa")) 143 if (File.Exists("/System/Library/Frameworks/Cocoa.framework/Cocoa"))
142 { 144 {
143 return "MACOSX"; 145 return "MACOSX";
144 } 146 }
145 147
146 /* 148 /*
147 * .NET 1.x, under Mono, the UNIX code is 128. Under 149 * .NET 1.x, under Mono, the UNIX code is 128. Under
@@ -236,7 +238,7 @@ namespace Prebuild.Core.Parse
236 OperatorSymbol oper = OperatorSymbol.None; 238 OperatorSymbol oper = OperatorSymbol.None;
237 bool inStr = false; 239 bool inStr = false;
238 char c; 240 char c;
239 241
240 for(int i = 0; i < exp.Length; i++) 242 for(int i = 0; i < exp.Length; i++)
241 { 243 {
242 c = exp[i]; 244 c = exp[i];
@@ -283,7 +285,7 @@ namespace Prebuild.Core.Parse
283 { 285 {
284 oper = OperatorSymbol.NotEqual; 286 oper = OperatorSymbol.NotEqual;
285 } 287 }
286 288
287 break; 289 break;
288 290
289 case '<': 291 case '<':
@@ -295,7 +297,7 @@ namespace Prebuild.Core.Parse
295 { 297 {
296 oper = OperatorSymbol.LessThan; 298 oper = OperatorSymbol.LessThan;
297 } 299 }
298 300
299 break; 301 break;
300 302
301 case '>': 303 case '>':
@@ -314,7 +316,7 @@ namespace Prebuild.Core.Parse
314 } 316 }
315 } 317 }
316 318
317 319
318 if(inStr) 320 if(inStr)
319 { 321 {
320 throw new WarningException("Expected end of string in expression"); 322 throw new WarningException("Expected end of string in expression");
@@ -392,9 +394,9 @@ namespace Prebuild.Core.Parse
392 /// <exception cref="ArgumentException">For invalid use of conditional expressions or for invalid XML syntax. If a XmlValidatingReader is passed, then will also throw exceptions for non-schema-conforming xml</exception> 394 /// <exception cref="ArgumentException">For invalid use of conditional expressions or for invalid XML syntax. If a XmlValidatingReader is passed, then will also throw exceptions for non-schema-conforming xml</exception>
393 /// <param name="reader"></param> 395 /// <param name="reader"></param>
394 /// <returns>the output xml </returns> 396 /// <returns>the output xml </returns>
395 public string Process(XmlReader reader) 397 public string Process(XmlReader initialReader)
396 { 398 {
397 if(reader == null) 399 if(initialReader == null)
398 { 400 {
399 throw new ArgumentException("Invalid XML reader to pre-process"); 401 throw new ArgumentException("Invalid XML reader to pre-process");
400 } 402 }
@@ -403,119 +405,175 @@ namespace Prebuild.Core.Parse
403 StringWriter xmlText = new StringWriter(); 405 StringWriter xmlText = new StringWriter();
404 XmlTextWriter writer = new XmlTextWriter(xmlText); 406 XmlTextWriter writer = new XmlTextWriter(xmlText);
405 writer.Formatting = Formatting.Indented; 407 writer.Formatting = Formatting.Indented;
406 while(reader.Read()) 408
409 // Create a queue of XML readers and add the initial
410 // reader to it. Then we process until we run out of
411 // readers which lets the <?include?> operation add more
412 // readers to generate a multi-file parser and not require
413 // XML fragments that a recursive version would use.
414 Stack readerStack = new Stack();
415 readerStack.Push(initialReader);
416
417 while(readerStack.Count > 0)
407 { 418 {
408 if(reader.NodeType == XmlNodeType.ProcessingInstruction) 419 // Pop off the next reader.
420 XmlReader reader = (XmlReader) readerStack.Pop();
421
422 // Process through this XML reader until it is
423 // completed (or it is replaced by the include
424 // operation).
425 while(reader.Read())
409 { 426 {
410 bool ignore = false; 427 // The prebuild file has a series of processing
411 switch(reader.LocalName) 428 // instructions which allow for specific
429 // inclusions based on operating system or to
430 // include additional files.
431 if(reader.NodeType == XmlNodeType.ProcessingInstruction)
412 { 432 {
413 case "if": 433 bool ignore = false;
414 m_IfStack.Push(context);
415 context = new IfContext(context.Keep & context.Active, ParseExpression(reader.Value), IfState.If);
416 ignore = true;
417 break;
418 434
419 case "elseif": 435 switch(reader.LocalName)
420 if(m_IfStack.Count == 0) 436 {
421 { 437 case "include":
422 throw new WarningException("Unexpected 'elseif' outside of 'if'"); 438 // use regular expressions to parse out the attributes.
423 } 439 MatchCollection matches = includeFileRegex.Matches(reader.Value);
424 else if(context.State != IfState.If && context.State != IfState.ElseIf) 440
425 { 441 // make sure there is only one file attribute.
426 throw new WarningException("Unexpected 'elseif' outside of 'if'"); 442 if(matches.Count > 1)
427 } 443 {
444 throw new WarningException("An <?include ?> node was found, but it specified more than one file.");
445 }
428 446
429 context.State = IfState.ElseIf; 447 if(matches.Count == 0)
430 if(!context.EverKept) 448 {
431 { 449 throw new WarningException("An <?include ?> node was found, but it did not specify the file attribute.");
432 context.Keep = ParseExpression(reader.Value); 450 }
433 } 451
434 else 452 // Pull the file out from the regex and make sure it is a valid file before using it.
435 { 453 string filename = matches[0].Groups[1].Value;
436 context.Keep = false; 454 FileInfo includeFile = new FileInfo(filename);
437 }
438 455
439 ignore = true; 456 if(!includeFile.Exists)
440 break; 457 {
458 throw new WarningException("Cannot include file: " + includeFile.FullName);
459 }
441 460
442 case "else": 461 // Create a new reader object for this file. Then put the old reader back on the stack and start
443 if(m_IfStack.Count == 0) 462 // processing using this new XML reader.
444 { 463 XmlReader newReader = new XmlTextReader(includeFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read));
445 throw new WarningException("Unexpected 'else' outside of 'if'");
446 }
447 else if(context.State != IfState.If && context.State != IfState.ElseIf)
448 {
449 throw new WarningException("Unexpected 'else' outside of 'if'");
450 }
451 464
452 context.State = IfState.Else; 465 readerStack.Push(reader);
453 context.Keep = !context.EverKept; 466 reader = newReader;
454 ignore = true; 467 ignore = true;
455 break; 468 break;
456 469
457 case "endif": 470 case "if":
458 if(m_IfStack.Count == 0) 471 m_IfStack.Push(context);
459 { 472 context = new IfContext(context.Keep & context.Active, ParseExpression(reader.Value), IfState.If);
460 throw new WarningException("Unexpected 'endif' outside of 'if'"); 473 ignore = true;
461 } 474 break;
462 475
463 context = (IfContext)m_IfStack.Pop(); 476 case "elseif":
464 ignore = true; 477 if(m_IfStack.Count == 0)
465 break; 478 {
466 } 479 throw new WarningException("Unexpected 'elseif' outside of 'if'");
480 }
481 else if(context.State != IfState.If && context.State != IfState.ElseIf)
482 {
483 throw new WarningException("Unexpected 'elseif' outside of 'if'");
484 }
467 485
468 if(ignore) 486 context.State = IfState.ElseIf;
469 { 487 if(!context.EverKept)
470 continue; 488 {
471 } 489 context.Keep = ParseExpression(reader.Value);
472 }//end pre-proc instruction 490 }
491 else
492 {
493 context.Keep = false;
494 }
473 495
474 if(!context.Active || !context.Keep) 496 ignore = true;
475 { 497 break;
476 continue;
477 }
478 498
479 switch(reader.NodeType) 499 case "else":
480 { 500 if(m_IfStack.Count == 0)
481 case XmlNodeType.Element: 501 {
482 bool empty = reader.IsEmptyElement; 502 throw new WarningException("Unexpected 'else' outside of 'if'");
483 writer.WriteStartElement(reader.Name); 503 }
504 else if(context.State != IfState.If && context.State != IfState.ElseIf)
505 {
506 throw new WarningException("Unexpected 'else' outside of 'if'");
507 }
484 508
485 while (reader.MoveToNextAttribute()) 509 context.State = IfState.Else;
486 { 510 context.Keep = !context.EverKept;
487 writer.WriteAttributeString(reader.Name, reader.Value); 511 ignore = true;
512 break;
513
514 case "endif":
515 if(m_IfStack.Count == 0)
516 {
517 throw new WarningException("Unexpected 'endif' outside of 'if'");
518 }
519
520 context = (IfContext)m_IfStack.Pop();
521 ignore = true;
522 break;
488 } 523 }
489 524
490 if(empty) 525 if(ignore)
491 { 526 {
492 writer.WriteEndElement(); 527 continue;
493 } 528 }
494 529 }//end pre-proc instruction
495 break;
496 530
497 case XmlNodeType.EndElement: 531 if(!context.Active || !context.Keep)
498 writer.WriteEndElement(); 532 {
499 break; 533 continue;
534 }
500 535
501 case XmlNodeType.Text: 536 switch(reader.NodeType)
502 writer.WriteString(reader.Value); 537 {
503 break; 538 case XmlNodeType.Element:
539 bool empty = reader.IsEmptyElement;
540 writer.WriteStartElement(reader.Name);
541
542 while (reader.MoveToNextAttribute())
543 {
544 writer.WriteAttributeString(reader.Name, reader.Value);
545 }
504 546
505 case XmlNodeType.CDATA: 547 if(empty)
506 writer.WriteCData(reader.Value); 548 {
507 break; 549 writer.WriteEndElement();
550 }
551
552 break;
508 553
509 default: 554 case XmlNodeType.EndElement:
510 break; 555 writer.WriteEndElement();
556 break;
557
558 case XmlNodeType.Text:
559 writer.WriteString(reader.Value);
560 break;
561
562 case XmlNodeType.CDATA:
563 writer.WriteCData(reader.Value);
564 break;
565
566 default:
567 break;
568 }
511 } 569 }
512 }
513 570
514 if(m_IfStack.Count != 0) 571 if(m_IfStack.Count != 0)
515 { 572 {
516 throw new WarningException("Mismatched 'if', 'endif' pair"); 573 throw new WarningException("Mismatched 'if', 'endif' pair");
574 }
517 } 575 }
518 576
519 return xmlText.ToString(); 577 return xmlText.ToString();
520 } 578 }
521 579