Monday, June 22, 2015

I have nothing to say, but it's OK, because now I can say it.

Apologies to The Beatles.

A bit ago, I copied the sourcecode for my Pong game into a new directory, as a way to start work on my next game in my language. I proceeded to comment out a large chunk of code.

Tangent: languages often don't have good facilities for commenting out chunks of code that might already include commented out code. In C++, say, you've got your single line comments

// like this

And there are multiple-line comments

/*
 * that can go on for many
 * lines, like this
 */

And if you want to disable a chunk of code that has a single-line comment, it's fine:

  for (int i=0; i<10; i++) {
    if (i < 3) {
/*
      // show small numbers
      printf("small number %d", i);
*/
      process_data(i);
  }

But it gets tricky if I wanted to disable that entire loop because */ terminates a comment, even if the comment was itself in a comment.

C and C++ have one more tool, the preprocessor, which helps some, so I could just do this:

#if 0
  for (int i=0; i<10; i++) {
    if (i < 3) {
/*
      // show small numbers
      printf("small number %d", i);
*/
      process_data(i);
  }
#endif //0


but that's still not terrific. Maybe disabling code isn't what a compiler is for. Still.


But that's not what I came here to talk about today. As I was saying at the top, I commented out a bunch of code from my Pong game, and stuff wouldn't compile. Ok, I thought, perhaps I was sloppy and commented out half of a function, and the remaining bits are causing problems. That may well have been part of it, but I cleaned up the obvious errors like that, and still I was running into issues.

One thing that doesn't get a lot of coverage in compilers tutorials is how to deal with errors - lexing errors, parse errors - there's perhaps a paragraph talking about inserting a special error token into your language and that's what gets called when things go wrong, which is a start, but then what do you do when that production gets called? How do you present enough information to the user of your compiler to fix the problem?

Right now, I print a warning with a line number and a little more information, which isn't currently sufficient. For one thing, the line number doesn't line up with the actual line number of the file being compiled. I think part of that comes from my compiler starting at line number 0 versus emacs, which starts at 1. Also, if I #include files, those throw off the numbering. Whoops. So, that's stuff to fix.

It'd also be nice to know what column the error was on, if I know that.

I've looked a little bit at pycparser, which seems to do a better job than I am for this sort of thing. TODO: roll in more of pycparser's approach to error handling.


And even that isn't what I meant to be talking about.

So, I waded through the output of my compiler, complaining about unexpected closing braces, and I ultimately determined that code like this was causing trouble:

void display_char(char c) {
# sdl_show_char(c);
}

Seems innocuous - a call to an external function that I commented out, since I'm rewriting my graphics library. Except, whoops, aha. The body of display_char is empty. And, whaddayaknow, my compiler doesn't like empty bodies for functions. Or, as it turns out for if/elif/else bodies, either:

if (phase == PLAY_GAME) {
#  draw_game();
} elif (phase == SHOW_TITLE) {
# draw_title();
} elif (phase == SHOW_CREDITS) {
# ...
}

As it turned out, that was super easy to fix - I just extended my grammar to allow statementlists to be 0 or more statements, instead of 1 or more, which is how they were before.

So, now I can have functions that do nothing, and blocks of code that are empty. Progress!    

No comments:

Post a Comment