Jr Tim
Jr Tim

Reputation: 9

Help parsing S-Expression

I'm trying to make a simple drawing program that reads in translate (rect 10 10 10 10) 50 50. What I'm trying to do is split it so that the 50 50 goes with the translate and the rect keeps all the 10s.

This is a PostScript fill. I've heard of hash tables and stacks, but I'm not sure how to use them. I've done everything else (e.x. all the calculations for the shapes). I just don't understand how to parse the lines so that I can get the numbers pointing to the right variables.

Upvotes: 0

Views: 763

Answers (3)

Gene Bushuyev
Gene Bushuyev

Reputation: 5538

Here is how you can write this C++ parser using AXE library:

Rect r;
auto rect = "(rect " 
    & r_decimal(r.left) & space 
    & r_decimal(r.top) & space
    & r_decimal(r.right) & space
    & r_decimal(r.bottom) & space
    & ')';

Point t;
auto translate = "translate " & rect 
    & space & r_decimal(t.x) 
    & space & r_decimal(t.y);
// test it
std::string str("translate (rect 10 10 10 10) 50 50");
auto match = translate(str.begin(), str.end());

That will parse a single translate statement in PS file. If you need to parse all translate statements and don't care to write a full blown parser for postscript format, you can use *r_find(translate) rule to skip input that you don't care about. r_find(R) rule searches the input until the rule R is found. Now that's pretty easy and it will also generate very fast code, probably faster than hand-written with "if"-s and "else"-s.

Disclaimer: I din't test the code above, so minor errors are possible.

Upvotes: 0

Mike Dunlavey
Mike Dunlavey

Reputation: 40669

Well, this might be a little old-fashioned, but it's simple and there's nothing faster.

void scanWhite(char*& p){
  while(*p==' ') p++;
}

bool seeInt(char*& p, int& num){
  scanWhite(p);
  char* p1 = p;
  bool bNegative = false;
  if (*p=='-'){bNegative = true; p++;)
  if (!isdigit(*p){p = p1; return false;}
  num = 0;
  while(isdigit(*p)){
    num *= 10;
    num += (*p - '0');
    p++;
  }
  if (bNegative) num = - num;
  return true;
}

bool seeWord(char*& p, char* word){
  scanWhite(p);
  int len = strlen(word);
  if (strncmp(p, word, len)==0 && !isalphanumeric(p[len])){
    p += len;
    return true;
  }
  else return false;
}

bool seeChar(char*& p, char c){
  scanWhite(p);
  if (*p != c) return false;
  p++;
  return true;
}

bool parseTranslateRect(char*& p
  , int& x0, int& y0, int& x1, int& y1
  , int& dx, int& dy
  )
{
  if (!seeChar(p, '(')) return false;
  if (!seeWord(p, "translate")) return false;
  if (!seeChar(p, '(')) return false;
  if (!seeWord(p, "rect")) return false;
  if (!seeInt(p, &x0)) return false;
  if (!seeInt(p, &y0)) return false;
  if (!seeInt(p, &x1)) return false;
  if (!seeInt(p, &y1)) return false;
  if (!seeChar(p, ')')) return false;
  if (!seeInt(p, &dx)) return false;
  if (!seeInt(p, &dy)) return false;
  if (!seeChar(p, ')')) return false;
  return true;
}

If you've got many copies of "(translate (rect ...", just call the parse routine over and over until it returns false.

Upvotes: 0

Nemo
Nemo

Reputation: 71525

Your example looks like a Lisp s-expression, so try searching for "s-expression parser". A few hits come up.

If you want to go "whole hog", you could implement your shape routines as C++ classes, use SWIG to expose them to GNU Guile, and write your application in Scheme. That is probably not what you had in mind, though. :-)

Upvotes: 1

Related Questions