SXML Parser

Oi, it’s done I guess. Beta phase though, but it seems to work rather well! This code parses the “SXML” of the post right before this one.

It’s tested with this file:

<window 100 456 789 120 windone SimpleXML>
  <button 101 258 369 123 buttone Ok 1337/>
  <button 102 258 369 123 buttwo Cancel 1338/>
</>

<window 103 456 789 120 windtwo SimpleXML1>
  <window 104 456 789 120 windthree SimpleXML2>
    <button 105 258 369 123 buttwo yes 1339/>
    <button 106 258 369 123 buttwo nay 1340/>
  </>
  <button 106 258 369 123 buttwo nay 1340/>
</>

/*Free to do anything (with this source file) but leave this comment block alone!
  Created by .simplicity's Nick Overdijk (nick@dotsimplicity.net)
*/
#include <iostream>
#include <cstdio>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <stack>
using namespace std;

/*Load file into memory as string
  create vector of element pointers
  parse the document, create elements on the fly
*/

class element {
public:
    element();

    element(string &amp;amp;amp;amp;type, unsigned int x, unsigned int y, unsigned int height, unsigned int width, element *parent) :
    type(type), x(x), y(y), height(height), width(width), parent(parent) {
    }

    element(string &amp;amp;amp;amp;attributes) :
    attributes(attributes) {
        attributeStream.str(attributes);
        parse();
    }

    virtual ~element(){ cout << "Finishing element" << endl; }

    virtual void printInfo() {
        cout << "---" << type << " @ " << this << "---" << endl;
        cout << "Width: " << width << endl;
        cout << "Height: " << height << endl;
        cout << "x: " << x << endl;
        cout << "y: " << y << endl;
        if(parent != NULL)
        cout << "Parent: " << parent << "(" << parent->type << "::" << parent->name << ")" << endl;
    }

    void printTree() {
        printInfo();
        if (parent != NULL) {
            parent->printTree();
        }
    }

    virtual void parse(){
        attributeStream >> type;
        attributeStream >> x;
        attributeStream >> y;
        attributeStream >> width;
        attributeStream >> height;
    }

    void setParent(element *parent){
        this->parent = parent;
    }

    string name;
    string type;
    string attributes;
    stringstream attributeStream;
    unsigned int x;
    unsigned int y;
    unsigned int height;
    unsigned int width;
    element *parent;
};

class button : public element {
    public:
    button(string &amp;amp;amp;amp;attributes) :
    element(attributes) {
        parse();
    }

    virtual ~button() { cout << endl; }

    virtual void parse(){
        attributeStream >> name;
        attributeStream >> caption;
        attributeStream >> style;
    }

    virtual void printInfo(){
        element::printInfo();
        cout << "Name: " << name << endl;
        cout << "Caption: " << caption << endl;
        cout << "Style: " << style << endl;
    }

    string caption;
    int style;
};

class window : public element {
    public:
    window(string &amp;amp;amp;amp;attributes) :
    element(attributes) {
        parse();
    }

    virtual ~window() { cout << endl; }

    virtual void parse(){
        attributeStream >> name;
        attributeStream >> title;
    }

    virtual void printInfo(){
        element::printInfo();
        cout << "Name: " << name << endl;
        cout << "Title: " << title << endl;
    }

    string title;
};

element *createElement(string &amp;amp;amp;amp;type, string &amp;amp;amp;amp;attributes){
#define \
createThis(x)\
if(type == #x) return new x(attributes)

    createThis(button);
    else createThis(window);
    else return NULL;
}

/*Each part of the code is divided into:
   Letting the user know what we're doing
   Do it
   Perform a check
   Tell the user fail or done
   exit or continue
  At any given time, the user knows what the program is doing and it helps debugging a lot.
*/

int main(int argc, char *argv[]) {
    vector<element*> elementList;

    cout << "Opening file: ";
    ifstream document;
    document.open("test.sxml", ios::binary);
    if (!document.is_open()) {
        cout << "[fail]" << endl;
        exit(-1);
    } else {
        cout << "[done]" << endl;
    }

    cout << "Getting filesize: ";
    document.seekg(0, ios::end);
    unsigned int docLen = document.tellg();
    document.seekg(0, ios::beg);
    cout << docLen << " bytes. [done]" << endl;

    cout << "Loading file into memory: ";

    char *buf = new char [docLen];
    document.read(buf, docLen);
    if (document.bad()) {
        cout << "[fail]" << endl;
    } else {
        cout << "[done]" << endl;
    }

    cout << "Parsing file for elements: ";
    /*Algorithm:
          Find a '<'
          if nextchar != '/''
              read in data until >
              parse data, create element
              if char before > != '/'
                  push this element as curent parent
          else if nextchar == '/'
              pop the last parent
    */

    stack <element*> parentStack;
    parentStack.push(NULL);
    unsigned int n, i;
    for (i = 0; i < docLen; i++) {
        if (buf[i] == '<') {
            if (buf[i+1] != '/') {
                for(n = i+1; buf[n] != '>' &amp;amp;amp;amp;&amp;amp;amp;amp; buf[n] != '/'; n++);
                unsigned int lineLength = n - i;
                string line(&amp;amp;amp;amp;buf[i+1], lineLength-1);
                //cout << "DEBUG LINE READ: " << line << endl;
                stringstream lineStream(line);
                string type;
                lineStream >> type;
                element *curElem = createElement(type, line);
                curElem->setParent(parentStack.top());

                for (n = i+2; buf[n] != '>'; n++);
                if (buf[n-1] != '/') {
                    parentStack.push(curElem);
                }
                elementList.push_back(curElem);
            } else {
                parentStack.pop();
            }
        }
    }
    cout << "[done]" << endl;

    for (unsigned int n = 0; n < elementList.size(); n++) {
        elementList[n]->printInfo();
    }
    return 0;
}

Sooo, feel free to comment on the code, but make it useful.

Leave a Reply