home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 275 / DPCS0111DVD.ISO / Toolkit / Audio-Visual / VirtualDub / Source / VirtualDub-1.9.10-src.7z / src / Lina / source / parser.cpp < prev   
Encoding:
C/C++ Source or Header  |  2009-09-14  |  5.7 KB  |  308 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.     // backwards compatibility
  210.     std::string::size_type pos = 0;
  211.     if (closed)
  212.         pos = 1;
  213.  
  214.     if (!tag.mName.compare(pos, 2, "w:"))
  215.         tag.mName.replace(pos, 2, "lina:");
  216.  
  217.     while(c != '>') {
  218.         if (c == '/' || c=='?') {
  219.             closed = true;
  220.             c = NextRequired();
  221.         } else if (istagchar(c)) {
  222.             tag.mAttribs.push_back(TreeAttribute());
  223.             TreeAttribute& att = tag.mAttribs.back();
  224.  
  225.             do {
  226.                 att.mName += tolower(c);
  227.                 c = NextRequired();
  228.             } while(istagchar(c));
  229.  
  230.             while(isspace(c))
  231.                 c = NextRequired();
  232.  
  233.             att.mbNoValue = true;
  234.  
  235.             if (c == '=') {
  236.                 att.mbNoValue = false;
  237.                 do {
  238.                     c = NextRequired();
  239.                 } while(isspace(c));
  240.  
  241.                 if (c == '"') {
  242.                     c = NextRequired();
  243.                     while(c != '"') {
  244.                         att.mValue += c;
  245.                         c = NextRequired();
  246.                     }
  247.                     c = NextRequired();
  248.                 } else {
  249.                     do {
  250.                         att.mValue += c;
  251.                         c = NextRequired();
  252.                     } while(istagchar(c));
  253.                 }
  254.             }
  255.  
  256.         } else if (isspace(c)) {
  257.             c = NextRequired();
  258.         } else
  259.             unexpected(c);
  260.     }
  261.  
  262.     if (!closed) {
  263.         c = NextRequired();
  264.         for(;;) {
  265.             TreeNode *p;
  266.             if (c == '<') {
  267.                 p = ParseTag();
  268.  
  269.                 if (p && !p->mName.empty() && p->mName[0] == '/') {
  270.                     if ((std::string("/") + tag.mName) != p->mName)
  271.                         error("closing tag <%s> doesn't match opening tag <%s> on line %d", p->mName.c_str(), tag.mName.c_str(), tag.mLineno);
  272.                     break;
  273.                 }
  274.                 c = NextRequired();
  275.             } else {
  276.                 p = ParseInline(c);
  277.             }
  278.  
  279.             if (p)
  280.                 tag.mChildren.push_back(p);
  281.         }
  282.     }
  283.  
  284.     // Check for a macro or include and whisk it away if so.
  285.  
  286.     if (tag.mName == "lina:macro") {
  287.         const TreeAttribute *a = tag.Attrib("name");
  288.  
  289.         if (!a)
  290.             error("macro definition must have NAME attribute");
  291.  
  292.         mpDocument->mMacros[a->mValue] = &tag;
  293.  
  294.         return NULL;
  295.     } else if (tag.mName == "lina:include") {
  296.         const TreeAttribute *a = tag.Attrib("file");
  297.  
  298.         if (!a)
  299.             error("<lina:include> must specify FILE");
  300.  
  301.         PushFile(a->mValue.c_str());
  302.  
  303.         return NULL;
  304.     }
  305.  
  306.     return &tag;
  307. }
  308.