Tuesday, July 02, 2019

The case of a missing begin/end

Delphi never stops surprising me …
Did you know that this is a valid syntax?
case a of
  0: Writeln(0);
  else
    Writeln('else');
    Writeln(a);
end;
This code indeed compiles and works exactly as the following fragment.
case a of
  0: Writeln(0);
  else begin
    Writeln('else');
    Writeln(a);
  end;
end;
I personally would never drop begin/end inside a case/else statement, but at least someone must disagree. I found such example in a very (VERY!) old code (it was written for Delphi 2) and I was quite surprised that it compiles at all.

EDIT

Anton Alisov suggested formatting first example as:
case a of
  0: Writeln(0);
else
  Writeln('else');
  Writeln(a);
end;
I guess this makes more sense (but just an itsy bitsy teenie weenie bit more).

13 comments:

  1. Moz in Oz04:58

    I suspect it's to emulate the C style "no break means continue" that has been giving people nightmares since it was introduced. But it does that because it matches assembly/machine code. It's also a very compact and flexible way to do horrible things with state machines.

    I have inherited some embedded code at work that gives modern compilers fits because of all the creative use of case statements.

    ReplyDelete
  2. Dennis07:53

    Wow, haven't seen that in a while. And of course I totally discourage everyone using this - a multiple code line block after an else has to be beginended.

    ReplyDelete
  3. Our Delphi teen discovered this syntax years ago and we use it in our projects. But we use other indents:

    case expr of
    1:
    2:
    else
    command1;
    command2;
    end;

    ReplyDelete
    Replies
    1. Blogger killed whitespaces :( I tried to say, that we write Word "else" with same indent, as "case", not as 1: and 2:,which are indented with additinal spaces.

      Delete
  4. it is same style aspect as in try-except-[...]-end vs try-except-begin-[...]-end-end

    ReplyDelete
  5. The "originally" syntax for the CASE statement was:
    CASE x OF
    1: DoSomething;
    2: DoSomethingElse;
    OTHERWISE
    DoACompletelyDifferentThing
    END;

    so originally, it wasn't an "ELSE" but an "OTHERWISE", which in my opinion is better because it doesn't risk being interpreted as an ELSE to an IF in the last CASE.

    ReplyDelete
    Replies
    1. The REAL original CASE statement in Wirth's Pascal definition had no default clause. It was expected you'd exclude anything not among your case selectors prior to entering the CASE, e.g:

      if X not in [1,2] then
      DoACompletelyDifferentThing
      else
      case X of
      1: DoSomething;
      2: DoSomethingElse;
      end;

      Delete
  6. That's the original Borland Pascal case statement.
    It never occurred to me to add an additional begin/end for the else case.

    ReplyDelete
  7. Personally I use the following :

    case a of
    0:
    begin
    Writeln(0);
    end;
    else
    begin
    Writeln('else');
    Writeln(a);
    end;
    end;

    So, even if I only have a single line I will always wrap it in a Begin / end ... easier to format, easier to read, less confusion.

    ReplyDelete
  8. > I personally would never drop begin/end inside a case/else statement

    Whyy. What's the point of having "else begin end end"?

    Personally I'd drop begin/end for case labels too, since there's no fall-through anyway.

    case a of
    1:
    code
    code
    2:
    code
    code
    else
    code
    code
    end;

    But with case labels at least it's not that redundant and it helps remove uncertainties.

    ReplyDelete
  9. I think that this is working as specified. The Case statement is one of those rare instances in Pascal/Delphi where there is an END included as part of the syntax.

    So it should be :-

    Case X of
    1: //option 1 stuff
    2: //option 2 stuff
    else
    //alternate branch stuff if none of the options above are valid
    end;

    So it is just because we have omitted the final END in the CASE.....ELSE....END that this appears to be causing an error.

    Hope this helps and thanks for bringing this up. It made me think.


    ReplyDelete
  10. @himselfv It's personal preference ... even if no begin / end is needed I always use one. It doens't slow down anything and makes stuff easier to read / understand IMHO. Call it whatever you want, but I like uniformity. an if with a single statement followed by an else with a single statement will get begin / end around both when I write them. Just a matter of habit I think.

    I think I came to this habit because I had to correct way too many dangling else problems in my Delphi career. With a begin / end around it, those bugs don't happen. And since i do it for just about anything why not do it in a case ...

    ReplyDelete
  11. > Personally I'd drop begin/end for case labels too, since there's no fall-through anyway.

    how does it relate to fall-through? orthogonal issues. It is more about probability to screw when copy-pasting or editing code.

    ReplyDelete