home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 242 / Issue 242 - April 2008 - DPCS0408DVD.ISO / Software Money Savers / VirtualDub / Source / VirtualDub-1.7.7-src.7z / src / Lina / source / parser.cpp < prev   
Encoding:
C/C++ Source or Header  |  2006-03-14  |  5.6 KB  |  300 lines

  1. #pragma warning(disable: 4786)    // STFU
  2.  
  3. #include <string>
  4. #include <list>
  5. #include <map>
  6. #include <stdarg.h>
  7. #include "document.h"
  8. #include "parser.h"
  9.  
  10. ///////////////////////////////////////////////////////////////////////////////
  11.  
  12. struct FileContext {
  13.     TreeLocation *tagloc;
  14.     FILE *f;
  15.     std::string name;
  16.     int lineno;
  17. };
  18.  
  19. std::string g_name;
  20. int g_line = 1;
  21. FILE *g_file;
  22. TreeLocation *g_pTagLocation;
  23. std::list<FileContext> g_fileStack;
  24. std::list<TreeLocation> g_tagLocations;
  25.  
  26. ///////////////////////////////////////////////////////////////////////////////
  27.  
  28. void error(const char *format, ...) {
  29.     va_list val;
  30.  
  31.     printf("%s(%d): Error! ", g_name.c_str(), g_line);
  32.  
  33.     va_start(val, format);
  34.     vprintf(format, val);
  35.     va_end(val);
  36.     putchar('\n');
  37.     exit(10);
  38. }
  39.  
  40. void unexpected(int c) {
  41.     error("unexpected character '%c'", (char)c);
  42. }
  43.  
  44. TreeParser::TreeParser(TreeDocument *doc)
  45.     : mpDocument(doc)
  46. {
  47. }
  48.  
  49. void TreeParser::ParseFile(const char *fname) {
  50.     int c;
  51.  
  52.     PushFile(fname);
  53.  
  54.     while((c = Next()) != EOF) {
  55.         if (c == '<') {
  56.             TreeNode *p = ParseTag();
  57.             if (p && !p->mbIsText) {
  58.                 if (!mpDocument->mpRoot)
  59.                     mpDocument->mpRoot = p;
  60.                 else
  61.                     error("multiple high-level tags detected (first is <%s>, second is <%s>)", mpDocument->mpRoot->mName.c_str(), p->mName.c_str());
  62.             }
  63.         }
  64.     }
  65. }
  66.  
  67. void TreeParser::PushFile(const char *fname) {
  68.     FILE *f = fopen(fname, "r");
  69.     if (!f)
  70.         error("cannot open \"%s\"", fname);
  71.  
  72.     if (g_file) {
  73.         FileContext fc;
  74.  
  75.         fc.f = g_file;
  76.         fc.name = g_name;
  77.         fc.lineno = g_line;
  78.         fc.tagloc = g_pTagLocation;
  79.         g_fileStack.push_back(fc);
  80.     }
  81.  
  82.     TreeLocation tl;
  83.     tl.mName = fname;
  84.     g_tagLocations.push_back(tl);
  85.  
  86.     g_pTagLocation = &g_tagLocations.back();
  87.     g_file = f;
  88.     g_name = fname;
  89.     g_line = 1;
  90.  
  91.     printf("Processing: %s\n", fname);
  92. }
  93.  
  94. bool TreeParser::PopFile() {
  95.     if (g_file)
  96.         fclose(g_file);
  97.  
  98.     if (g_fileStack.empty()) {
  99.         g_file = NULL;
  100.     } else {
  101.         const FileContext& fc = g_fileStack.back();
  102.         g_file = fc.f;
  103.         g_name = fc.name;
  104.         g_line = fc.lineno;
  105.         g_pTagLocation = fc.tagloc;
  106.         g_fileStack.pop_back();
  107.     }
  108.     return g_file!=0;
  109. }
  110.  
  111. int TreeParser::Next() {
  112.     int c;
  113.  
  114.     do {
  115.         c = getc(g_file);
  116.     } while(c == EOF && PopFile());
  117.  
  118.     if (c == '\n')
  119.         ++g_line;
  120.  
  121.     return c;
  122. }
  123.  
  124. int TreeParser::NextRequired() {
  125.     int c = Next();
  126.     if (c == EOF)
  127.         error("unexpected end of file");
  128.     return c;
  129. }
  130.  
  131. bool istagchar(int c) {
  132.     return isalnum(c) || c=='-' || c==':';
  133. }
  134.  
  135. bool issafevaluechar(int c) {
  136.     return isalnum(c) || c=='-';
  137. }
  138.  
  139. TreeNode *TreeParser::AllocNode() {
  140.     TreeNode *t = mpDocument->AllocNode();
  141.  
  142.     t->mpLocation = g_pTagLocation;
  143.     t->mLineno = g_line;
  144.     return t;
  145. }
  146.  
  147. TreeNode *TreeParser::ParseInline(int& c) {
  148.     TreeNode& tag = *AllocNode();
  149.     bool last_was_space = false;
  150.  
  151.     tag.mbIsText = true;
  152.     tag.mbIsControl = false;
  153.  
  154.     do {
  155.         tag.mName += c;
  156.         c = NextRequired();
  157.     } while(c != '<');
  158.  
  159.     return &tag;
  160. }
  161.  
  162. // parse_tag
  163. //
  164. // Assumes that starting < has already been parsed.
  165. TreeNode *TreeParser::ParseTag() {
  166.     TreeNode& tag = *AllocNode();
  167.     bool closed = false;
  168.     int c;
  169.  
  170.     tag.mbIsControl = false;
  171.     tag.mbIsText = false;
  172.  
  173.     c = NextRequired();
  174.     if (isspace(c))
  175.         do {
  176.             c = NextRequired();
  177.         } while(isspace(c));
  178.  
  179.     if (c=='?' || c=='!') {
  180.         tag.mbIsText = true;
  181.         tag.mbIsControl = true;
  182.         tag.mName = "<";
  183.         tag.mName += c;
  184.  
  185.         int bracket_count = 1;
  186.         do {
  187.             c = NextRequired();
  188.             tag.mName += c;
  189.  
  190.             if (c == '<')
  191.                 ++bracket_count;
  192.             else if (c == '>')
  193.                 --bracket_count;
  194.         } while(bracket_count);
  195.         return &tag;
  196.     } else if (c == '/') {
  197.         tag.mName += c;
  198.         c = NextRequired();
  199.     }
  200.  
  201.     do {
  202.         tag.mName += tolower(c);
  203.         c = NextRequired();
  204.     } while(istagchar(c));
  205.  
  206.     if (tag.mName[0] == '/')
  207.         closed = true;
  208.  
  209.     while(c != '>') {
  210.         if (c == '/' || c=='?') {
  211.             closed = true;
  212.             c = NextRequired();
  213.         } else if (istagchar(c)) {
  214.             tag.mAttribs.push_back(TreeAttribute());
  215.             TreeAttribute& att = tag.mAttribs.back();
  216.  
  217.             do {
  218.                 att.mName += tolower(c);
  219.                 c = NextRequired();
  220.             } while(istagchar(c));
  221.  
  222.             while(isspace(c))
  223.                 c = NextRequired();
  224.  
  225.             att.mbNoValue = true;
  226.  
  227.             if (c == '=') {
  228.                 att.mbNoValue = false;
  229.                 do {
  230.                     c = NextRequired();
  231.                 } while(isspace(c));
  232.  
  233.                 if (c == '"') {
  234.                     c = NextRequired();
  235.                     while(c != '"') {
  236.                         att.mValue += c;
  237.                         c = NextRequired();
  238.                     }
  239.                     c = NextRequired();
  240.                 } else {
  241.                     do {
  242.                         att.mValue += c;
  243.                         c = NextRequired();
  244.                     } while(istagchar(c));
  245.                 }
  246.             }
  247.  
  248.         } else if (isspace(c)) {
  249.             c = NextRequired();
  250.         } else
  251.             unexpected(c);
  252.     }
  253.  
  254.     if (!closed) {
  255.         c = NextRequired();
  256.         for(;;) {
  257.             TreeNode *p;
  258.             if (c == '<') {
  259.                 p = ParseTag();
  260.  
  261.                 if (p && !p->mName.empty() && p->mName[0] == '/') {
  262.                     if ((std::string("/") + tag.mName) != p->mName)
  263.                         error("closing tag <%s> doesn't match opening tag <%s> on line %d", p->mName.c_str(), tag.mName.c_str(), tag.mLineno);
  264.                     break;
  265.                 }
  266.                 c = NextRequired();
  267.             } else {
  268.                 p = ParseInline(c);
  269.             }
  270.  
  271.             if (p)
  272.                 tag.mChildren.push_back(p);
  273.         }
  274.     }
  275.  
  276.     // Check for a macro or include and whisk it away if so.
  277.  
  278.     if (tag.mName == "lina:macro") {
  279.         const TreeAttribute *a = tag.Attrib("name");
  280.  
  281.         if (!a)
  282.             error("macro definition must have NAME attribute");
  283.  
  284.         mpDocument->mMacros[a->mValue] = &tag;
  285.  
  286.         return NULL;
  287.     } else if (tag.mName == "lina:include") {
  288.         const TreeAttribute *a = tag.Attrib("file");
  289.  
  290.         if (!a)
  291.             error("<lina:include> must specify FILE");
  292.  
  293.         PushFile(a->mValue.c_str());
  294.  
  295.         return NULL;
  296.     }
  297.  
  298.     return &tag;
  299. }
  300.