| EAA-dev Home |

WORK-IN-PROGRESS: - this material is still under development

Model Ignorant Generation

Generate code with all logic hard coded into the generated code so that there's no explicit representation of the Semantic Model.

How it Works

One of the advantages of code generation is that it allows you to produce code that would be too repetitive to write by hand in a controlled way. This opens up options for implementation that usually you would wisely shy away from because it would involve duplicating code. In particular this allows you to take behavior that would usually need to be represented through data structures and encode them in control flow.

To use Model Ignorant Generation I can start by writing an implementation of a particular DSL script in the tartet environment. I prefer to start with a very simple and minimal script. The implementation code should be clear but can freely intermingle generic and specific code and I don't have to worry about repetition in the specific elements since these will be generated. This means I don't have to think about clever data structures, usually prefering procedural code and simple structures.

When to use it

Target environments often involve languages with limited facilities for structuring programs and building a good model. In these situations it's not possible to use Model-Aware Generation so Model Ignorant Generation is pretty much the only option. The second main reason for using Model Ignorant Generation is when using Model-Aware Generation results in an implementation that demands too much run-time resources. Encoding logic in control may reduce memory needs or increase performance, if these are sufficiently critical then Model Ignorant Generation is a good way to get there.

On the whole, however, I prefer to see Model-Aware Generation if it's possible. It's usually easier to generate code with Model-Aware Generation which results in an simpler generation program to understand and modify. Having said that, using Model Ignorant Generation often makes the generated code easier to follow. This has the converse effect that it can be easier to figure out what to generate, although it can be harder to write the code to generate it.

Example: Secret Panel State Machine as Nested Conditionals (C)

Again, I'll turn to the secret panel state machine I used in the introductory chapter. One of the classic implementations of a state machine is using nested conditionals which allow you to evaluate your next step using conditional expressions based on your current state and the received event. For this example I'll show what a nested conditional implementation for Miss Grant's controller would look like. To see how I might generate this code, see the example in Templated Generation.

There's two conditions that I need evaluate, the incoming event and the current state. In this case I'll start with the current state.

#define STATE_idle 1
#define STATE_active 0
#define STATE_waitingForDraw 3
#define STATE_unlockedPanel 2
#define STATE_waitingForLight 4
void handle_event(char *code) {
  switch(current_state_id) {
  case STATE_idle: {
    handle_event_while_idle (code);
    return;
  }
  case STATE_active: {
    handle_event_while_active (code);
    return;
  }
  case STATE_waitingForDraw: {
    handle_event_while_waitingForDraw (code);
    return;
  }
  case STATE_unlockedPanel: {
    handle_event_while_unlockedPanel (code);
    return;
  }
  case STATE_waitingForLight: {
    handle_event_while_waitingForLight (code);
    return;
  }
  default: {
    printf("in impossible state");
    exit(2);
  }
  }
}

Testing the state involves a static variable which holds the current state.

#define ERROR_STATE -99
static int current_state_id = ERROR_STATE;
void init_controller() {
  current_state_id = STATE_idle;
}

Each subsidiary function now does a further conditional check based on the event that's received. Here's the case for the active state.

#define EVENT_drawOpened "D2OP"
#define EVENT_lightOn "L1ON"
#define EVENT_doorOpened "D1OP"
#define COMMAND_lockPanel "PNLK"
#define COMMAND_unlockPanel "PNUL"
void handle_event_while_active (char *code) {
  if (0 == strcmp(code, EVENT_lightOn)) {
    current_state_id = STATE_waitingForDraw;
  }
  if (0 == strcmp(code, EVENT_drawOpened)) {
    current_state_id = STATE_waitingForLight;
  }
  if (0 == strcmp(code, EVENT_doorOpened)) {
    current_state_id = STATE_idle;
    send_command(COMMAND_unlockDoor);
    send_command(COMMAND_lockPanel);
  }
}

The other subsidiary functions look very similar, so I won't repeat them.

While this code would be worryingly repetitive code to write by hand for different machines, when generated it provides code that's quite easy to follow.

Significant Revisions

23 May 08: First Stub