GOTO (etc) para uma linha inexistente?
Escrever um interpretador BASIC revelou uma série de bits interessantes de informação que tendem a não ser mencionados na documentação. Por exemplo:
10 PRINT"ONE";:IF 1=2 THEN PRINT"TWO":PRINT"THREE"
Imprimirá ONE
em BASICs derivados da Microsoft, enquanto Dartmouth produzirá ONETHREE
. Ou seja, o MS trata todo o resto da linha como parte do THEN
, o que é ... estranho (e IMHO errado). Só percebi isso porque o código de exemplo que executei na última instrução, que causou a falha de Super Star Trek.
Encontrei outro exemplo que gostaria de abrir para o hoi polloi. Considere este programa:
10 PRINT"HELLO"
20 GOTO 25
30 PRINT"WORLD"
O código de exemplo que tenho pesquisaria a linha 25 ou a próxima instrução superior . Portanto, nesse código, a linha 30 seria executada. Este definitivamente não é o caso do Commodore BASIC, que retorna "UNDEFN'D STATEMENT".
Então ... alguém conhece uma versão do BASIC que funcione dessa maneira, ou isso (como eu fortemente suspeito) é simplesmente um bug no código de exemplo?
Respostas
Sinclair BASIC no ZX Spectrum pularia para o próximo número de linha disponível. O manual diz
Se o número da linha em um comando IR PARA referir-se a uma linha inexistente, o salto é para a próxima linha após o número fornecido. O mesmo vale para RUN; na verdade, RUN sozinho significa RUN 0.
Os dialetos BASIC são conhecidos por variar muito em detalhes. Uma das versões mais definitivas é BBC BASIC, que faz o seguinte:

Observe a construção IF-THEN-ELSE, que justifica o uso de corpos IF-THEN com várias instruções - que são de fato úteis na prática.
BBC BASIC V adicionou uma palavra-chave ENDIF e a facilidade para blocos IF-THEN-ELSE-ENDIF de várias linhas. Em geral, o BBC BASIC é voltado para tornar a programação estruturada mais fácil do que a maioria dos BASICs de microcomputadores anteriores.
Os exemplos não são bugs, mas comportamento indefinido, que também é comum em outras linguagens. Se você espera compatibilidade entre plataformas, simplesmente não faça coisas que resultem em comportamento indefinido.
Além disso, a intenção do programador no primeiro exemplo não é clara, o que pode levar a bugs que são difíceis de corrigir. Novamente, não faça isso. O segundo exemplo é melhor porque funciona como o programador pretendia ou o analisador reclama, tornando mais fácil encontrar e corrigir o bug rapidamente.
10 PRINT"ONE";:IF 1=2 THEN PRINT"TWO":PRINT"THREE"
Irá imprimir ONE em BASICs derivados da Microsoft, enquanto Dartmouth produzirá ONETHREE. Ou seja, o MS trata todo o resto da linha como parte do ENTÃO, o que é ... estranho (e IMHO errado).
Bem, eu acho que não há certo ou errado, mas cada BASIC é do seu jeito. A maneira da MS essencialmente permite a criação de um bloco de código dentro de uma cláusula THEN sem a necessidade de GOTO. Com Dartmouth, que funcionava naquele ponto como FORTRAN antes, o THEN precisa pular para o bloco de código, seguido por um GOTO para contornar:
10 PRINT"ONE";
20 IF 1=2 THEN GOTO 40
30 GOTO 50
40 PRINT"TWO"
50 PRINT"THREE"
60 REM
Bem, ou use uma cláusula invertida para pular o bloco de código. Nem uma construção realmente ótima.
Deve-se notar que permitir declarações arbitrárias após THEN é um add-on posterior, não presente no Dartmouth BASIC. O mesmo acontece com várias instruções separadas por dois pontos.
Com a maneira de MS de tratar toda a linha (o resto da) linha como parte do bloco then permite que isso construa sem muita movimentação cerebral.
Mas não foi inventado pela MS, eles apenas o retiraram do DEC BASIC-PLUS de 1972 (afinal, MS BASIC é um clone do DEC BASIC) conforme descrito na p.3-12 do manual:

Portanto, aqui depois de um THEN, várias instruções são permitidas, mas executadas como um todo (se a condição for verdadeira) ou nunca.
Agora, ao procurar os caminhos 'certos', geralmente é melhor primeiro dar uma olhada nos padrões BÁSICOS. O primeiro aqui pode ser
ECMA 55 Minimal BASIC de 1978
Isso descreve o mínimo que todo BASIC precisa cumprir para ser portátil. Essencialmente, ele codifica Dartmouth BASIC (Thomas Kurtz foi um dos editores) em suas encarnações posteriores de uma forma clara e reproduzível. Aqui, as instruções THEN permitem apenas o salto para um número de linha.
ANSI mínimo BASIC de 1979
Essencialmente, a versão ANSI do ECMA-55.
Este é o BTW, o momento em que o MS-BASIC começou a se tornar uma força normativa
ECMA 116 BASIC de 1986 , também chamado de 'Full BASIC'
Aqui são possíveis construções de instruções múltiplas e múltiplas linhas e suas misturas. A instrução multi funciona como o 'modo MS', enquanto a multi line precisa de uma instrução ENDIF (ou ELSE / ELSEIF) para fechar o bloco. (Ele também tem muitos outros recursos nie conhecidos do BASICs modernos, apenas com números de linha)
ANSI / ISO / IEC Full BASIC de 1987
Essencialmente ECMA-116 com alguns esclarecimentos / extensões.
Portanto, o MS segue o que o ECMA-116 diz ... bem, ou melhor, talvez o padrão codifique o que o MS fazia antes e, portanto, se tornou o padrão de fato. Muito trabalho foi colocado nesses padrões para capturar um lugar comum viável para o BASIC. Isso inclui especialmente casos extremos de problemas aparentemente claros. Eu consideraria uma prática recomendada verificá-los sempre que houver algo aberto para discussão. Principalmente porque também apontam questões que não foram decididas / ainda estão abertas a interpretações.
O código de exemplo que tenho procuraria a linha 25 ou a próxima instrução superior [...]
Então ... alguém conhece uma versão do BASIC que funcione dessa maneira, ou isso (como eu fortemente suspeito) é simplesmente um bug no código de exemplo?
Eu me lembro de um TINY BASIC que permitia pular 'entre' linhas para facilitar o GOTO computado, mas olhando para a fonte original parece que foi uma modificação.
Em contraste, o ECMA-55 afirma em alvos usados como alvos em THEN / GOTO / GOSUB:
All line-numbers in control-statements shall refer to lines in the program.
Em termos pragmáticos,
Decida qual código legado você deseja que seu interpretador seja capaz de executar
Decida quais dialetos incompatíveis, se houver, você deseja oferecer suporte como opções
Faça a mesma coisa que eles.
Como Raffzahn menciona, o comportamento da Microsoft é mais útil do que o de Dartmouth, porque o Microsoft BASIC permite que você escreva um bloco condicional com várias instruções. Você também disse que deseja executar programas que esperem o comportamento da Microsoft.
Da mesma forma, é muito improvável que qualquer código legado seja intencionalmente GOTO
uma linha que não existe, mas é possível que algum programa existente seja executado corretamente apesar de um erro de digitação como em GOTO 24
vez de GOTO 25
.
Se você também precisar executar um código que dependa de um comportamento incompatível, poderá fornecer isso como uma opção.
O dialeto que usei foi passar o controle para a linha existente com o maior número mais próximo, se tal existisse. Do contrário, essa era a maneira legítima de encerrar o programa sem nenhuma mensagem de erro.
Passar o controle para o meio do intervalo permitiu adicionar linhas em ambos os lados do ponto de entrada. Isso ajudou muito porque a refatoração também era muito tediosa: não havia nenhuma pesquisa e a única maneira de alterar a linha era redigitando-a por completo. Essa versão não possuía renumeração automática de linhas.
A máquina era uma espécie de "Elektronika" soviético, mas não me lembro exatamente. Parecia mais uma calculadora de ponta com o próprio display de LED de duas linhas no console, mas já suportava monitor externo e teclado.