Recent

Author Topic: Pascal-style case and C-style break  (Read 1142 times)

MarkMLl

  • Hero Member
  • *****
  • Posts: 5582
Pascal-style case and C-style break
« on: September 29, 2022, 12:06:47 pm »
In a number of circumstances C's ability to fall-through to the next case in a switch statement is useful, and there is the occasional newcomer to Pascal who complains that it is unsupported.

Pascal does, obviously, have the break statement which can be used in a loop context, but I found myself wondering whether it had a convenient antithesis: it does, of course, which is constrained use of a goto.

I have the code below in a procedure which is interpreting a message received via some form of serial connection.

Code: Pascal  [Select][+][-]
  1. function TSerialThread.recvMessage(serHandle: TSerialHandle; var buffer: string;
  2.                                 timeout, limit: integer): boolean;
  3. label
  4.   handleBcc;
  5.  
  6. const
  7.   obeyTimeout= true;                    (* Normally true                        *)
  8.  
  9. type
  10.   tRecvState= (expectSOH, expectAddress, expectSeq, expectCtrl, expectPayload,
  11.                                 expectHandshake, expectOptionalEtx, expectBcc,
  12.                                                                     completed);
  13. ...
  14.  
  15.         expectOptionalEtx: if ch = ETX then begin
  16.                              buffer += ch;
  17.                              bccCount := BccLength;
  18.                              recvState := expectBcc (* To process next character *)
  19.                            end else begin
  20.                              bccCount := BccLength;
  21.                              recvState := expectBcc;
  22. { Works }              goto handleBcc     (* To process current character *)
  23. { Fails } //                goto expectBcc     (* To process current character *)
  24.                            end;
  25.         expectBcc:     begin
  26. handleBcc:
  27.                          if not (ch in ['!'..'~']) then
  28.                            break;
  29.                          buffer += ch;
  30.                          bccCount -= 1;
  31.                          if bccCount = 0 then
  32.                            recvState := completed
  33.                        end
  34.       otherwise
  35.         break                           (* Quite simply can't happen            *)
  36.       end
  37.   until obeyTimeout and Elapsed(limitTD, limit);
  38.  

Using a goto is entirely natural in that context, and in my opinion it is difficult to criticise since it is actually /inhibiting/ the transfer of control to some distant part of the program (Dijkstra himself complained about religious application of his "considered harmful" suggestion).

However, it does turn out to be necessary to declare a distinct label, simply using one of the label-like lines as the destination results in an "identifier isn't a label" error.

Would it be feasible for the compiler to allow a goto to target a (numeric) destination in this situation?

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Zvoni

  • Hero Member
  • *****
  • Posts: 1527
Re: Pascal-style case and C-style break
« Reply #1 on: September 29, 2022, 12:43:50 pm »
@Fallthrough:
Huh? Just use consecutive If-Then with an Else-Clause for the last if
If you need to exit the "switch" prematurely move the whole thing to a Function/Procedure
@Goto in your sample:
plain and simple: i avoid Goto like the plague.
In a case like yours i'd rather use a (nested) Function/Procedure instead of Goto SomeLabel
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

MarkMLl

  • Hero Member
  • *****
  • Posts: 5582
Re: Pascal-style case and C-style break
« Reply #2 on: September 29, 2022, 12:49:37 pm »
Looking through code, I've not used goto /except/ in this context in about 20 years. I'm considering whether it would be possible to eliminate it- or more specifically the label declaration- from the language.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

440bx

  • Hero Member
  • *****
  • Posts: 3160
Re: Pascal-style case and C-style break
« Reply #3 on: September 29, 2022, 01:04:35 pm »
I've needed the C switch fallthrough feature a number of times and, since "goto"s are unequivocally unacceptable to me, I found a solution that implements the fallthrough C switch using a Pascal case statement without any need for a separate function and or procedure, no goto(s) either of course and, all inline as it should be (and as it is in C/C++)

It's not quite as CPU efficient as the C switch but, for the extremely slight loss of performance, the construct is even more powerful and flexible than the C switch.   Jumps over C switches in a single case statement. :)

Say NO to "goto"
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 SP1 64bit.

MarkMLl

  • Hero Member
  • *****
  • Posts: 5582
Re: Pascal-style case and C-style break
« Reply #4 on: September 29, 2022, 01:19:20 pm »
since "goto"s are unequivocally unacceptable to me,

Why? I quote Dijkstra on this:

Quote
"Please don't fall into the trap of believing that I am terribly dogmatical about [the goto statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!"

For the specific situation where it is shifting between the states of a state machine without soliciting further input I thing it's the lesser of the several possible evils.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

440bx

  • Hero Member
  • *****
  • Posts: 3160
Re: Pascal-style case and C-style break
« Reply #5 on: September 29, 2022, 01:28:24 pm »
Why?
Because the target destination of a goto can be anywhere in the function/procedure.  IOW, its destination needs to be determined by searching for the target label (which can be anywhere in the function/procedure, not necessarily in the case statement, that's a problem.)  When a programmer reads a "goto xyz" statement, it provides no information whatsoever as to where the destination "xyz" is.

Additionally, a goto, requires defining labels and manually keeping the labels "in synch" with the cases.  That's makes maintenance more difficult and error prone, not to mention that declaring a bunch of labels is tedious.  Pascal has enough deterministic control flow features to make the "goto" statement completely unnecessary, therefore a deficient solution.
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 SP1 64bit.

MarkMLl

  • Hero Member
  • *****
  • Posts: 5582
Re: Pascal-style case and C-style break
« Reply #6 on: September 29, 2022, 01:53:55 pm »
Because the target destination of a goto can be anywhere in the function/procedure.  IOW, its destination needs to be determined by searching for the target label (which can be anywhere in the function/procedure, not necessarily in the case statement, that's a problem.)  When a programmer reads a "goto xyz" statement, it provides no information whatsoever as to where the destination "xyz" is.

Additionally, a goto, requires defining labels and manually keeping the labels "in synch" with the cases.  That's makes maintenance more difficult and error prone, not to mention that declaring a bunch of labels is tedious.  Pascal has enough deterministic control flow features to make the "goto" statement completely unnecessary, therefore a deficient solution.

Both of which I propose removing.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

PascalDragon

  • Hero Member
  • *****
  • Posts: 4759
  • Compiler Developer
Re: Pascal-style case and C-style break
« Reply #7 on: September 29, 2022, 01:59:41 pm »
Would it be feasible for the compiler to allow a goto to target a (numeric) destination in this situation?

The goto statement can already target numeric destinations, however these need to be declared as a label nevertheless and that will stay, because Pascal is a language where things need to be declared before use.
Also the branches of a case-statement are not labels, because they can be ranges as well and thus even if non-declared labels could be used you still can't target the branches of a case-statement with that.

Warfley

  • Hero Member
  • *****
  • Posts: 942
Re: Pascal-style case and C-style break
« Reply #8 on: September 29, 2022, 01:59:50 pm »
C was developed for system development, and all the decisions in C must be viewed through this lens. The C switch-case statement is not an if-then-else replacement like in Pascal, it is a jumping table. This is why the case statement can only have a single value, and why you have the break. When entering a switch statement, it jumps to the case with the respective value and continues from there.

When doing low level programming, jumping tables are essential, therefore this makes completely sense, and also in other development it can still be quite useful. For example when I worked on an emulator, that had many instructions with very similar effects, e.g. write to memory and increment and write to memory. In C this is simply:
Code: C  [Select][+][-]
  1. switch (instr) {
  2. ...
  3. case INC_STORE: reg++;
  4. case STORE:
  5.   memory[addr] = reg;
  6.   break;
  7. ...
  8. }
In Pascal it either requires nested ifs or goto.

But the more high level you get, the less you need such constructs, so on a high level language (what Pascal most likely tries to be), I don't see the need for it. Especially as it is very confusing for many beginners, who learn C's switch case statements as "alternative to if-then-else" (which to be clear, it isn't and was never intendet to be, it's a jumping table, nothing more nothing less), and I had more than once to answer the question "My switch statement is broken".
Because for some reason this jumping table concept somehow made it into languages like Java or C#, where you never need a jumping table
« Last Edit: September 29, 2022, 02:01:42 pm by Warfley »

440bx

  • Hero Member
  • *****
  • Posts: 3160
Re: Pascal-style case and C-style break
« Reply #9 on: September 29, 2022, 02:04:55 pm »
Both of which I propose removing.
I am unclear as to what things you are proposing removing.  Can you be explicit on what they are ?



In Pascal it either requires nested ifs or goto.
It can be done in FPC/Delphi without if(s) and without goto(s) but, "lesser" forms of Pascal may require either or both.
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 SP1 64bit.

MarkMLl

  • Hero Member
  • *****
  • Posts: 5582
Re: Pascal-style case and C-style break
« Reply #10 on: September 29, 2022, 02:18:43 pm »
When doing low level programming, jumping tables are essential,

Yes, and I have seen an ALGOL compiler which had several related variants (jump tables, format tables, substitution tables etc.) all of which had to be declared at the head of the program. To call it painful in an era where one can't just order a few extra printouts and spread them on a big desk is an understatement.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

jamie

  • Hero Member
  • *****
  • Posts: 4931
Re: Pascal-style case and C-style break
« Reply #11 on: September 29, 2022, 10:54:56 pm »
I have ~ two pages of a single CASE statement using a GOTO loopback

This process decodes a serial stream of digital data that can be incomplete with the current cached data and thus sets the GOTO loop to resume where it was.

 Also, this large CASE plays the role of a C style switch whereas it changes the CASE switch to process to the next in line switch and uses a GOTO back to the loop, again.

 If for some reason the current CASE index cannot be completed it just allows the case to exit as normal but keeps the current CASE index to pick up for the next time.

 This logic was very hard to process using IF statements and the like, it was much easier using a C style Case switch.
The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 12208
Re: Pascal-style case and C-style break
« Reply #12 on: September 29, 2022, 11:16:37 pm »
jump tables, format tables, substitution tables
You mean Birthday parties, Christmas tables and finding eggs in the garden?
Manuals, manuals, manuals first.
You have incompetence and sheer incompetence.

MarkMLl

  • Hero Member
  • *****
  • Posts: 5582
Re: Pascal-style case and C-style break
« Reply #13 on: September 30, 2022, 08:58:22 am »
I have ~ two pages of a single CASE statement using a GOTO loopback

This process decodes a serial stream of digital data that can be incomplete with the current cached data and thus sets the GOTO loop to resume where it was.

 Also, this large CASE plays the role of a C style switch whereas it changes the CASE switch to process to the next in line switch and uses a GOTO back to the loop, again.

 If for some reason the current CASE index cannot be completed it just allows the case to exit as normal but keeps the current CASE index to pick up for the next time.

 This logic was very hard to process using IF statements and the like, it was much easier using a C style Case switch.

Yes, very much my rationale. In effect goto is overloading the selector value and going back to the start of the case: very much like continue does with a while.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

y.ivanov

  • Hero Member
  • *****
  • Posts: 562
Re: Pascal-style case and C-style break
« Reply #14 on: September 30, 2022, 11:03:13 am »
I have ~ two pages of a single CASE statement using a GOTO loopback

This process decodes a serial stream of digital data that can be incomplete with the current cached data and thus sets the GOTO loop to resume where it was.

 Also, this large CASE plays the role of a C style switch whereas it changes the CASE switch to process to the next in line switch and uses a GOTO back to the loop, again.

 If for some reason the current CASE index cannot be completed it just allows the case to exit as normal but keeps the current CASE index to pick up for the next time.

 This logic was very hard to process using IF statements and the like, it was much easier using a C style Case switch.

Yes, very much my rationale. In effect goto is overloading the selector value and going back to the start of the case: very much like continue does with a while.

MarkMLl

BTW, I don't miss that particularity of the C-switch, besides one very special case of https://forum.lazarus.freepascal.org/index.php/topic,60218.msg450448.html#msg450448

With spending one more boolean:

Code: Pascal  [Select][+][-]
  1.     function TSerialThread.recvMessage(serHandle: TSerialHandle; var buffer: string;
  2.                                     timeout, limit: integer): boolean;
  3.     label
  4.       handleBcc;
  5.      
  6.     const
  7.       obeyTimeout= true;                    (* Normally true                        *)
  8.      
  9.     type
  10.       tRecvState= (expectSOH, expectAddress, expectSeq, expectCtrl, expectPayload,
  11.                                     expectHandshake, expectOptionalEtx, expectBcc,
  12.                                                                         completed);
  13.   done := false;
  14.   repeat case RecvState of  
  15.       ...
  16.      
  17.             expectOptionalEtx: if ch = ETX then begin
  18.                                  buffer += ch;
  19.                                  bccCount := BccLength;
  20.                                  recvState := expectBcc (* To process next character *)
  21.                                end else begin
  22.                                  bccCount := BccLength;
  23.                                  recvState := expectBcc;
  24.                                  continue;
  25.     { Works } //             goto handleBcc     (* To process current character *)
  26.     { Fails } //                goto expectBcc     (* To process current character *)
  27.                                end;
  28.             expectBcc:     begin
  29.     handleBcc:
  30.                              if not (ch in ['!'..'~']) then
  31.                                break;
  32.                              buffer += ch;
  33.                              bccCount -= 1;
  34.                              if bccCount = 0 then
  35.                                recvState := completed
  36.                            end
  37.           otherwise
  38.             break                           (* Quite simply can't happen            *)
  39.           end; done := true; until done;
  40.       until obeyTimeout and Elapsed(limitTD, limit);
  41.      

Or, as Zvoni wrote, in that particular case with an extra check:
Code: Pascal  [Select][+][-]
  1.      expectOptionalEtx, expectBcc:
  2.                              begin
  3.                                if (RecvState = expectOptionalEtx) then begin
  4. //           expectOptionalEtx: if ch = ETX then begin
  5.                                  bccCount := BccLength;
  6.                                  recvState := expectBcc;
  7.                                  if (ch = ETX) then begin
  8.                                    buffer += ch;
  9.                                    break;
  10.                                  end;
  11.                                end;
  12. //            expectBcc:     begin
  13.                              if not (ch in ['!'..'~']) then
  14.                                break;
  15.                              buffer += ch;
  16.                              bccCount -= 1;
  17.                              if bccCount = 0 then
  18.                                recvState := completed
  19.                            end
  20.           otherwise
  21. ...

"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

 

TinyPortal © 2005-2018