Situation Handler in TADS 3 (Library Question)

I’m trying to see if I can build a situation handler in a TADS 3 library. An example of what I mean by situation handler is in my own Tapestry project (not TADS-based): github.com/jeffnyman/tapestry/b … tuation.rb

Tapestry is a library that someone would include in their script. So you can see there that certain situations are looked for at compile time for a script and, if those are found, a very specific problem message is generated.

So here’s an example, TADS-based, for a mainCommon() in a putative library. Let’s say someone doesn’t define gameMain in their own game source. A situation handler would specifically say that:

mainCommon(prop) {
  try {
    gameMain.(prop);
  } catch(ProgramException pe) {
    "PROBLEM: You must define a gameMain object in one of the source files in your project.";
  }
}

The problem there is the ‘catch’. ProgramException isn’t the right thing to put, I guess, but that said, nothing really is. I don’t thing anything will be caught because you’ll get an “error: undefined symbol ‘gameMain’.” In other words, I don’t think I’m in a situation where an Exception instance is being thrown. So then I thought I might try this:

mainCommon(prop) {
  if (propDefined(&gameMain) == nil) {
    "PROBLEM: You must define a gameMain object in one of the source files in your project.";
  }

The problem there, of course, is that I have to call propDefined on something. I can’t use self in that context. And there really is no other object I could apply the check to. Keep in mind, this is for a library, not for a game using a library.

And, again, the goal here is to provide a library that guides the user via recognition of the specific situation and then tailored output for that situation.

Interesting idea. You surely could build a library handling many beginners pitfalls, I could imagine for example using reflection services to enumerate objects and their properties and check that right kind of strings are assigned to right properties and so on. But this specific situation with omitted gameMain is in my opinion impossible to solve inside TADS because the problem is resolved during linking and undefined or multiply defined object lead to linking error before any library or logic can execute anything to check situation. On the other hand missing property is another story. Compiler issues a warning, but compiles game turning unknown property into nil, so you can check that well known properties are defined with value that looks good.

Yeah. A dynamic language (like my Ruby example) offers a lot of cool possibilities like that, so I’m trying to see which of those ideas I can port over. I did find one way to do this but it requires an artifact that isn’t so great.

Let’s say your library has a main.t file like this:

main(args) {
  mainCommon(&newGame);
}

mainCommon(prop) {
  if (gameMain.getSuperclassList != [Game]) {
    "You need to provide a gameMain class that is derived from Game.\n";
  }
  gameMain.(prop);
}

gameMain : object {
}

Notice the gameMain there, derived from the base object. If the author did not include a gameMain derived from Game, they would get my message as indicated.

However, this means the can’t just put a gameMain in their source. They would have to use a replace, like this:

replace gameMain : Game {
}

It does work such that if the above is in place, the situation handler text is not displayed. But at the cost of the author having to use a “replace” in this context. And that little bit of extra cognitive friction probably isn’t worth the use of the situation handler here.

But this is not so much helpful, because it negates what is taught in all the examples and books. Maybe it would be better to come with some solution external to TADS like running some utility automatically before compilation. Or rather invest the energy into more elaborate checks to situations which are syntactically correct and compile fine, but are harder to detect and understand for beginner, because they are about understanding, how world model works. Btw. here are some beginner mistakes from head and over the years this forum is full of similar ones, that could be identified:

  • Not including basic header files leads to “Expected a semicolon ‘;’ but found “{”.” in d/iobjFor which is symptom of not introducing propertyset definition in header file - compile time, cant detect from TADS.
  • Setting both location property and using plus syntax together - also.
  • Not including space before end of string, which can lead to missing space when another message is printed after.
  • Forget to call inherited() - sometimes it is intentional not call it, so it’s problematic to point this out.
  • Apply moveInto instead of moveIntoForTravel to Actor.
  • Checking that some kinds of objects are placed into container hierarchy on right places, like actor states in actors, topic in actors or actor states and so on.