GOTO (ecc.) Su una linea inesistente?

Dec 07 2020

Scrivere un interprete BASIC ha rivelato una serie di informazioni interessanti che tendono a non essere menzionate nella documentazione. Per esempio:

10 PRINT"ONE";:IF 1=2 THEN PRINT"TWO":PRINT"THREE"

Stamperà ONEsu BASIC derivati ​​da Microsoft, mentre Dartmouth produrrà ONETHREE. Cioè, MS considera l'intero resto della linea come parte del THEN, il che è ... strano (e IMHO sbagliato). Ho notato solo questo, perché il codice di esempio che ho avuto ha eseguito l'ultima affermazione, che ha causato Super Star Trek al sicuro.

Ho trovato un altro esempio che vorrei aprire all'hoi polloi. Considera questo programma:

10 PRINT"HELLO"
20 GOTO 25
30 PRINT"WORLD"

Il codice di esempio che ho dovrebbe cercare la riga 25 o l'istruzione successiva superiore . Quindi in quel codice verrà eseguita la riga 30. Questo non è sicuramente il caso di Commodore BASIC, che restituisce "UNDEFN'D STATEMENT".

Quindi ... qualcuno conosce una versione di BASIC che funziona in questo modo, o questo (come sospetto fortemente) è semplicemente un bug nel codice di esempio?

Risposte

5 Brian Dec 09 2020 at 22:36

Sinclair BASIC su ZX Spectrum passerebbe al successivo numero di riga disponibile. Il manuale dice

Se il numero di riga in un comando VAI A fa riferimento a una riga inesistente, il salto è alla riga successiva dopo il numero specificato. Lo stesso vale per RUN; infatti ESEGUI da solo in realtà significa ESEGUI 0.

13 Chromatix Dec 07 2020 at 21:04

Si sa che i dialetti BASIC variano molto nei dettagli. Una delle versioni più definitive è BBC BASIC, che fa quanto segue:

Si noti il ​​costrutto IF-THEN-ELSE, che giustifica l'uso di corpi IF-THEN a più istruzioni, che sono di fatto utili nella pratica.

BBC BASIC V ha aggiunto una parola chiave ENDIF e la funzionalità per i blocchi IF-THEN-ELSE-ENDIF multilinea. In generale, BBC BASIC è progettato per rendere la programmazione strutturata più semplice rispetto alla maggior parte dei precedenti BASIC per microcomputer.

13 snips-n-snails Dec 08 2020 at 01:54

Gli esempi non sono bug ma comportamenti indefiniti, comune anche in altri linguaggi. Se ti aspetti la compatibilità multipiattaforma, semplicemente non fare cose che comportano un comportamento indefinito.

Inoltre, l'intenzione del programmatore nel primo esempio non è chiara, il che può portare a bug difficili da correggere. Di nuovo, non farlo. Il secondo esempio è migliore perché o funziona come previsto dal programmatore o il parser si lamenta, rendendo facile trovare e correggere rapidamente il bug.

10 Raffzahn Dec 07 2020 at 22:18

10 PRINT"ONE";:IF 1=2 THEN PRINT"TWO":PRINT"THREE"

Stamperà ONE su BASIC derivati ​​da Microsoft, mentre Dartmouth produrrà ONETHREE. Cioè, MS tratta l'intero resto della linea come parte del THEN, il che è ... strano (e IMHO sbagliato).

Beh, immagino che non ci sia giusto o sbagliato, ma ogni BASIC è a modo suo. Il modo in cui MS essenzialmente consente la creazione di un blocco di codice all'interno di una clausola THEN senza la necessità di GOTO. Con Dartmouth, che a quel punto funziona come FORTRAN prima, THEN deve saltare nel blocco di codice, seguito da un GOTO per aggirare:

10 PRINT"ONE";
20 IF 1=2 THEN GOTO 40
30 GOTO 50
40 PRINT"TWO"
50 PRINT"THREE"
60 REM

Bene, o usa una clausola invertita per saltare il blocco di codice. Nemmeno un ottimo costrutto.

Va notato che consentire dichiarazioni arbitrarie dopo THEN è un'aggiunta successiva, non presente in Dartmouth BASIC. Lo stesso vale per più istruzioni separate da due punti.

Con il modo in cui MS di trattare l'intera (resto della) linea come parte del blocco then consente questo costrutto senza molto jogging cerebrale e gotos.

Ma non è stato inventato da MS, lo hanno semplicemente preso da DEC BASIC-PLUS del 1972 (dopotutto, MS BASIC è un clone di DEC BASIC) come descritto a p.3-12 del manuale:

Quindi qui dopo un THEN sono consentite più istruzioni, ma eseguite per intero (se la condizione è vera) o per niente.

Ora, quando si cercano i modi "giusti", di solito è meglio dare prima un'occhiata agli standard BASIC. Il primo qui potrebbe essere

  • ECMA 55 Minimal BASIC del 1978

    Questo descrive il minimo che ogni BASIC deve rispettare per essere portatile. Essenzialmente codifica Dartmouth BASIC (Thomas Kurtz era uno degli editori) nelle sue successive incarnazioni in modo chiaro e riproducibile. Qui le istruzioni THEN consentono solo a un numero di riga di saltare.

  • ANSI Minimal BASIC del 1979

    Essenzialmente la versione ANSI di ECMA-55.

Questo è BTW, il momento in cui MS-BASIC ha iniziato a diventare una forza normativa

  • ECMA 116 BASIC del 1986 , chiamato anche 'Full BASIC'

    Qui sono possibili costrutti multi-istruzione e multi-riga THEN e loro miscele. L'istruzione multipla funziona come il "modo MS", mentre la riga multipla richiede un'istruzione ENDIF (o ELSE / ELSEIF) per chiudere il blocco. (Ha anche molte altre caratteristiche conosciute dai BASIC moderni, solo con i numeri di riga)

  • BASIC completo ANSI / ISO / IEC del 1987

    Essenzialmente ECMA-116 con alcuni chiarimenti / estensioni.

Quindi MS segue ciò che dice ECMA-116 ... beh, o meglio forse lo standard codifica ciò che MS ha fatto prima e quindi è diventato di fatto standard. È stato fatto molto lavoro su questi standard per catturare un luogo comune funzionante per BASIC. Ciò include soprattutto casi limite di problemi apparentemente chiari. Considererei una buona pratica controllarli ogni volta che c'è qualcosa di aperto per la discussione. Soprattutto perché anche loro sottolineano questioni che non sono state decise / sono ancora aperte all'interpretazione.

Il codice di esempio che ho cercherebbe la riga 25 o la successiva istruzione superiore [...]

Quindi ... qualcuno conosce una versione di BASIC che funziona in questo modo, o questo (come sospetto fortemente) è semplicemente un bug nel codice di esempio?

Ricordo un TINY BASIC che consentiva di saltare "tra" le righe per facilitare il GOTO calcolato, ma guardando la fonte originale sembra che questa fosse una modifica.

Al contrario, ECMA-55 afferma sui bersagli usati come bersagli in THEN / GOTO / GOSUB:

All line-numbers in control-statements shall refer to lines in the program.
1 Davislor Dec 08 2020 at 07:35

In termini pragmatici,

  1. Decidi quale codice legacy desideri che il tuo interprete possa eseguire

  2. Decidi quali dialetti incompatibili, se ce ne sono, vuoi supportare come opzioni

  3. Fai la stessa cosa che fanno loro.

Come spiega Raffzahn, il comportamento di Microsoft è più comodo di quello di Dartmouth, perché Microsoft BASIC ti consente di scrivere un blocco condizionale con più istruzioni. Dici anche di voler eseguire programmi che si aspettano il comportamento di Microsoft.

Allo stesso modo, è molto improbabile che qualsiasi codice legacy sia intenzionalmente GOTOuna riga che non esiste, ma è possibile che alcuni programmi esistenti possano funzionare correttamente nonostante un errore di battitura come GOTO 24invece di GOTO 25.

Se è necessario eseguire anche codice che dipende da un comportamento incompatibile, è possibile fornirlo come opzione.

1 h22 Dec 09 2020 at 21:57

Il dialetto che ho usato passava il controllo alla linea esistente con il numero più grande più vicino, se esisteva. In caso contrario, questo era il modo legittimo per terminare il programma senza alcun messaggio di errore.

Passare il controllo al centro dell'intervallo consentiva di aggiungere linee su entrambi i lati del punto di ingresso. Questo ha aiutato molto perché anche il refactoring era molto noioso: non c'era la ricerca e l'unico modo per cambiare la riga era riscrivendolo per intero in completezza. Quella versione non prevedeva la rinumerazione automatica delle righe.

La macchina era una specie di "Elektronika" sovietica ma non ricordo con precisione. Sembrava più una calcolatrice di fascia alta con il proprio display LED a due righe sulla console, ma supportava già monitor e tastiera esterni.