ObjoScript: Looking for implementation advice

I’ve decided to add a switch (aka select case) statement to ObjoScript, my scripting language written in Xojo.

When I implemented foreach loops, I simply coded the compiler to de-sugar the foreach loop into a while loop (since I had already implemented while loops).

I was thinking about doing something similar by de-sugaring a switch statement to multiple if...else statements (since the compiler can already successfully compile if statements).

Something like this (consider is the condition to evaluate):

switch consider {
  case a, b {
    // First case.
  }
  case is < 10 {
    // Second case.
  }
  else {
    // Default case.
  }
}

would become:

{
  var consider* = consider
  if (a == consider*) or (b == consider*) {
    // First case.
  } else if consider* < 10 {
    // Second case.
  } else {
    // Default case.
  }
}

Notice how I would compile a secret local variable (consider*) that the if statements would reference. The inclusion of a * means the user would never be able to directly access this (since the parser would recognise that identifier as invalid).

I’m not sure if this is the correct approach however. Is there another one?

I’m not sure the point of the second variable, what advantage does it provide? Also, it depends upon you intention of the switch system. In C it is possible for more than one switch block to activate, if you don’t use break statements. Because of that modifications to the original variable “consider” would not show up in the consider* value and would thus affect the results of the code.

If you don’t have reentrant switch then doing a pre assignment makes no difference, other than using more memory and more operations. Slowing the process down.

To me switch kinda implies a requirement for break at the end of blocks. One which can’t be captured in an if then else. It is a completely different beast the select case, which only ever executes the first block that matches the pattern.

C switch is more:

{
  AnyDone* = false
  if (a == consider) or (b == consider) {
    // First case.
    AnyDone* = true
    if break goto EndSwitch
  }
  if consider < 10 {
    // Second case.
    AnyDone = true
    if break then goto EndSwitch
  } 
  if (not AnyDone) {
    // Default case.
  }
}
:EndSwitch

By “second variable” do you mean the consider* variable? If so, that’t there for two reasons:

  1. Performance. Why evaluate the switch value for each if statement?
  2. Immutability. If the switch value is a method that mutates a value then you don’t want it to run multiple times when the user only intended it to run once.

As for re-entrancy and breaking, I think I am trying to keep it as simple as possible. To that extent, it will not be like a loop and so won’t support exiting early from a case. If it did support that it makes implementation much harder.

If it is a function then yes, you are correct. As you had it it was a variable. In which case it is fixed. I do agree select case is by far my preferred system. I would use a term other than switch in that case, if you want to keep to single keywords perhaps just “select”. It removes the C style baggage that comes with switch.

select consider {
  case a, b {
    // First case.
  }
  case is < 10 {
    // Second case.
  }
  else {
    // Default case.
  }
}

if you also want to avoid “else” as it is to do with “If… then… else”, then I’ve seen “default” used instead. I’ve also seen “otherwise” used. “is” is redundant.

1 Like

Using is simplifies parsing a lot. I can reuse the binary parsers and simply replace is with consider*.

You can simply replace case with if consider*? is has always been optional in just about every language I’ve ever used.

Does “de-sugaring” negatively affect the ability to step through the code while debugging and be able to recognize it?

1 Like

That’s a great question. The answer is yes and thank you for making me aware of that as I hadn’t considered that side effect.

I can mitigate it though. The debugger uses the location of each token in the script to know where to highlight in the code. I’ll just synthesise tokens in the compiler that correspond to known segments of the code that can be stopped on. For instance, each if token generated on the compiler will need to map to a case token.

A great point. Thanks.