Возвращение в пятницу: перенумеровать мой список ZX BASIC

15

Первым языком программирования, с которым я столкнулся, был Sinclair BASIC . Как и многие другие диалекты BASIC, он требует нумерации всех строк исходного кода .

В результате использование GO TOкоманды было идиоматичным и переходит к выполнению на заданный номер строки (без меток).

Также есть связанная GO SUBкоманда, которая может использоваться как элементарный вызов функции. Опять же, выполнение переходит к заданному номеру строки, но когда RETURNкоманда достигнута, выполнение переходит обратно к следующей инструкции после GO SUB.

Аналогично, RUNкоманда перезапустит выполнение программы с заданной строки.

Любой, кто провел какое-то время в переводчике с базовым номером BASIC, научится использовать схему нумерации с пробелами в ней. Это так, чтобы легче было вставлять новые строки кода. Однако даже в этом случае вам может понадобиться вставить новые строки между последовательно пронумерованными строками.


Если в качестве входных данных указан список BASIC с номерами строк, выведите ту же программу, но перенумеруйте ее так, чтобы номера строк начинались с 10 и увеличивались с шагом 10. Список ввода может содержать команды GO TOили GO SUBкоманды, поэтому числа, связанные с ними, также должны быть скорректированы.

  • GO TOи GO SUBкоманды находятся либо в своих собственных строках, либо в конце IF THENстрок. Можно с уверенностью сказать, ^(\d+) .*GO (TO|SUB) (\d+)$чтобы соответствовать таким линиям. Эти команды в кавычках следует игнорировать.

  • RUNКоманды всегда будут на своих линиях. В этом случае номер строки не является обязательным. Если он отсутствует, то интерпретатор просто запускается в начале программы.

  • Если через GO TO, GO SUBили RUNссылку команды несуществующей строку, то вместо этого перейти к следующей определенной линии. Ваша запись должна иметь дело с этим и убедиться, что любые такие ссылки на строки исправлены, поэтому они указывают на правильную строку. Поведение может быть неопределенным, если в одной из этих команд указан номер строки после завершения программы.

  • Номера строк всегда будут положительными целыми числами от 1 до 9999 (согласно инструкции). Это означает, что входные программы никогда не будут иметь более 999 строк.

  • Входные строки всегда будут пронумерованы в порядке возрастания номеров.

  • Для целей этой задачи входные списки будут содержать только печатный ASCII. Вам не нужно беспокоиться о наборе символов ZX. Сказав , что, если ваша запись на самом деле написано в ZX BASIC или соответствующие z80 сборки / машинный код (и есть эмуляторы из там ), то вы можете выбрать для вашего ввода , чтобы быть закодировано в наборе символов ZX вместо.

  • Вы не имеете права использовать библиотеки или утилиты перенумерации, специально предназначенные для этой цели.

Пример ввода:

1 REM "A rearranged guessing game"
2 INPUT A: CLS
3 INPUT "Guess the number ", B
10 IF A=B THEN PRINT "Correct": STOP
100 IF A<B THEN GO SUB 125
120 IF A>B THEN GO SUB 122
121 GO TO 3
125 PRINT "Try again"
126 RETURN
127 REM "An example of GO TO 7 and GO SUB 13 in quotes"

Пример вывода:

10 REM "A rearranged guessing game"
20 INPUT A: CLS
30 INPUT "Guess the number ", B
40 IF A=B THEN PRINT "Correct": STOP
50 IF A<B THEN GO SUB 80
60 IF A>B THEN GO SUB 80
70 GO TO 30
80 PRINT "Try again"
90 RETURN
100 REM "An example of GO TO 7 and GO SUB 13 in quotes"

Я хотел дать ссылку на руководство по ZX BASIC. Лучшее, что я смог найти, - это http://www.worldofspectrum.org/ZXBasicManual/index.html, но, похоже, это мертвая ссылка. У машины обратного хода есть копия, хотя .

Цифровая травма
источник
7
Кроме того, задайте 5000-й вопрос!
FryAmTheEggman
1
Время ностальгии - мой первый ПК был Spectrum 48K, а одна из моих первых программ сборки была перенумерована
edc65
2
@ edc65 У вас еще есть код ассемблера? Если это так, вы можете опубликовать его в качестве ответа!
Цифровая травма
1
В тестовом примере должен быть хотя бы один goto / gosub в строковом литерале.
Питер Тейлор
1
Я нашел упоминание: ZX81 позволяет вычисленных GOTOS и GOSUBs , как вGOTO 100 + A*10 и Приложении C из ZX Spectrum Manual списков GO TOкак принимать числовое выражение (без ограничения на константы). Вот обсуждение достоинств вычислений GOTOна ZX80 и ZX81. Кстати, я понятия не имею, почему место было добавлено в версии Spectrum.
Тоби Спейт

Ответы:

5

JavaScript (ES6) 177

Редактировать Добавлено (дорогое) сканирование для поиска следующего номера строки

l=>l.split`
`.map((x,i)=>([,n,t]=x.match(/(\d+)(.*)/),l[n]=10*-~i,t),l=[]).map((x,i)=>10*-~i+x.replace(/(UN |GO TO |UB )(\d+)$/,(a,b,c)=>(l.some((v,i)=>i<c?0:a=b+v),a))).join`
`

ТЕСТОВОЕ ЗАДАНИЕ

f=l=>
  l.split`\n`
  .map((x,i)=>([,n,t]=x.match(/(\d+)(.*)/),l[n]=10*-~i,t),l=[])
  .map((x,i)=>10*-~i+x.replace(/(UN |GO TO |UB )(\d+)$/,(a,b,c)=>(l.some((v,i)=>i<c?0:a=b+v),a)))
  .join`\n`
        
//TEST
console.log=x=>O.textContent+=x+'\n'
  
test=`1 REM "A rearranged guessing game"
2 INPUT A: CLS
3 INPUT "Guess the number ", B
10 IF A=B THEN PRINT "Correct": STOP
100 IF A<B THEN GO SUB 125
120 IF A>B THEN GO SUB 122
121 GO TO 3
125 PRINT "Try again"
126 RETURN`
console.log(test+'\n\n'+f(test))
<pre id=O></pre>

edc65
источник
1
Выглядит хорошо. Мой +1 стоит :)
Цифровая травма
2

Perl 6, 147, 145, 144, 142 байта

{my%a;.trans(/^^(\d+)/=>{%a{$0}=$+=10}).trans(/:s<!after \"\N*>(UN |GO TO |UB )(\d+)<!before \N*\">/=>{$0~%a{%a.keys».Num.grep(*>=$1).min}})}

Это, вероятно, может быть в гольфе немного больше.

расширенный

my &f = -> $s { 
    my %line-map; # This will map the old line numbers to the new ones

    $s.trans(/^^(\d+)/                    # This .trans creates the line number map
             => { %line-map{$0} = $+=10 } # as well as replaces the actual line numbers
            )\
      # This .trans replaces all the line numbers for each GO TO, GO SUB, RUN
      .trans(/:s<!after \"\N*>(UN |GO TO |UB )(\d+)<!before \N*\">/ 
             => {$0 ~ %line-map{%line-map.keys».Num.grep(*>=$1).min} } 
            )
}
Клавиатурный
источник
нет причин использовать метод .min. используйте {min %line-map.keys».Num.grep:*>=$1вместо этого
Ven
1

Visual Basic для приложений, 288 байт

Я не мог удержаться, давая решение на бейсикском диалекте. Вероятно, работает с Visual Basic 6 / .NET или другими современными вариантами с небольшими изменениями.

Sub n(t,a)
f=Chr(10)
u=Chr(0)
Open t For Input As 1
a=f &Input(LOF(1),1)&f
Close
j=10
For i=1 To 9999
q=f &j &u
g=" GO TO "
w=i &f
m=j &f
a=Replace(Replace(Replace(Replace(a,g &w,g &m),f &i &" ",q),"B "&w,"B "&m),"UN "&w,"UN "&m)
If InStr(1,a,q)Then j=j+10
Next
a=Replace(a,u," ")
End Sub

Я использовал много однобуквенных переменных для краткости. Кроме того, я подавил все ненужные пробелы (VBE расширяет их автоматически при импорте). Число байт для окончательного файла .BAS с CHR (10) в качестве новой строки.

Подпрограмма, которая может быть вызвана из непосредственного окна VBE, открывает программу BASIC Sinclair (первым параметром является путь к файлу ASCII - с CHR (10) в качестве новой строки - содержащей программу), перенумеровывает строки и записывает результаты в переменную Variant (второй параметр).

Идея заключается в том , чтобы итерация по всем возможным номерам источник линии, порядок возрастания, и для каждого из них, заменить сразу все номера согласование линии, а также GO TO, GO SUBи RUNссылки с следующим доступным номером целевой линии. Используя этот подход, нам не нужны никакие таблицы перевода. Номер целевой строки увеличивается каждый раз, когда найдено совпадение с номером исходной строки, поэтому «неправильные» ссылки на строки автоматически подстраиваются под следующий допустимый номер. Символы новой строки используются в качестве маркеров начала и конца строки, а символ CHR (0), который никогда не использовался в программе, поскольку он не предназначен для печати, используется в качестве временного маркера, чтобы избежать перенумерации одной и той же строки несколько раз.

Некоторые замечания:

  • Для краткости мы используем меньшую возможную строку для совпадения с операторами прыжка. Используя конец строки в наших строках поиска, мы не рискуем включить в кавычки вхождения или пользовательские функции (которые всегда используют круглые скобки в Sinclair). GO TOтребует большую строку из-за FOR ... TOконструкции (например, сравнить 50 FOR X=AGO TO 100и 50 GO TO 100)

  • Код не поддерживает операторы в форме GO TO200(без пробелов), хотя руководство ZX подразумевает, что он является допустимым кодом в нескольких примерах (для его обработки потребуется десяток байт).

  • Код добавляет новую строку в начале и другую в конце программы. В конце концов, я мог бы это исправить (еще дюжину байтов), но подумал, что ZX, вероятно, будет игнорировать пустые строки.

Ниже более читаемая версия:

Sub Renumber(ByVal ProgramPath As String, ByRef Program As Variant)

    Open ProgramPath For Input As #1
    Program = Chr(10) & Input(LOF(1), 1) & Chr(10)
    Close

    NewNumber = 10
    For OldNumber = 1 To 9999
        Program = Replace(Program, " GO TO" & OldNumber & Chr(10), " GO TO" & NewNumber & Chr(10)) 'self-explaining
        Program = Replace(Program, Chr(10) & OldNumber & " ", Chr(10) & NewNumber & Chr(0)) 'matches line number (and replaces whistespace with Chr(0) to avoid re-replacing
        Program = Replace(Program, "B " & OldNumber & Chr(10), "B " & NewNumber & Chr(10)) 'matches GO SUB
        Program = Replace(Program, "UN " & OldNumber & Chr(10), "UN " & NewNumber & Chr(10)) 'matches RUN
        If InStr(1, Program, Chr(10) & NewNumber & Chr(0)) Then NewNumber = NewNumber + 10 'if there is such a line, increment NewNumber
Next
Program = Replace(Program, Chr(0), " ") 'replace back Chr(0) with whitespace
End Sub
dnep
источник
Кстати, решение QBasic будет намного длиннее, поскольку, насколько я помню, в QBasic нет встроенной функции замены строк.
DLosc
Я думаю, что вы правы ... забыл об этом
dnep
1

Пип -rn , 63 байта

Ygn:#{_<aFIy}*t+tgR`(RUN|GO (SUB|TO)) (\d+)$`{b.s.(nd)}R`^\d+`n

Попробуйте онлайн!

объяснение

Настроить

-rФлаг читает все и стандартного ввода сохраняет его в виде списка строк в локальной переменной g. Глобальная переменная tпредварительно инициализирована до 10, а глобальная переменная предварительно sинициализирована до " ".

Yg

Объединяет список строк gв глобальную переменную y, чтобы он был доступен внутри функции, которую мы собираемся определить.

Функция перевода номера строки

Мы строим функцию, которая сопоставляет любой номер строки в исходной схеме нумерации (включая несуществующую) с соответствующим номером строки в новой схеме нумерации.

Предположим, у нас есть эти строки:

1 INPUT A
4 PRINT A
9 IF A=1 THEN GO TO 3

Мы хотим отобразить от 1 до 10, от 2-4 до 20 и от 5-9 до 30. Если у нас есть список исходных номеров строк ( [1; 4; 9]), мы можем использовать операцию фильтра, чтобы узнать, сколько из этих чисел меньше чем номер строки, которую мы пытаемся преобразовать. Умножьте этот результат на 10 и добавьте 10, и мы получим желаемый ответ.

Например, при преобразовании 9 имеется два номера строки (1 и 4), меньшие 9. 2 * 10 + 10 дает 30. При преобразовании 3 один номер строки (1) меньше 3. 1 * 10 + 10 дает 20.

Вот код (слегка измененный, чтобы его было легче читать):

n:{#(_<aFIy)*t+t}
  {             }  Lambda function with parameter a:
        FIy         Filter y (the list of program lines) for
     _<a             lines that are numerically less than a
                    (In a numeric context, only the first run of digits on the line is considered)
   #(      )        Number of items in the filtered list
            *t+t    Times 10, plus 10
n:                 Assign that function to global variable n

Первая замена: GO TO, GO SUBиRUN

Остальная часть программы представляет собой одно выражение, которое принимает gи выполняет пару замен регулярных выражений (которые векторизуются, применяя к каждой строке в g).

Вот первая замена:

g R `(RUN|GO (SUB|TO)) (\d+)$` {b.s.(nd)}

Регулярное выражение соответствует любому из RUN, GO SUBи GO TO, после которого следует число, за которым следует конец строки. Это гарантирует, что он не совпадает внутри строк и не совпадает RUNбез номера строки.

Порядок групп захвата важен. Первая группа захватывает команду (один из RUN, GO SUB, или GO TO). Вторая группа, если используется, фиксирует либо SUBили TO. Нам не нужно захватывать эту часть, но для группы без захвата потребуются дополнительные байты. Затем третья группа фиксирует номер строки.

Мы используем функцию обратного вызова для замены. С обратным вызовом функцией в Пипе, весь матч является первым аргументом a, и захват группа в порядке , являются последующими аргументами b, c, dи e. Итак, у нас есть команда в первой группе, которая входит b, и номер строки в третьей группе, которая входит d. Единственное изменение , которое мы должны сделать это , чтобы передать номер строки через нашу функцию преобразования, которая называется Lisp-стиль: (nd). Затем мы объединяем это вместе с bпробелом и возвращаем его.

Вторая замена: номера строк

Все, что осталось преобразовать, это номера строк в начале строк.

(...) R `^\d+` n

Регулярное выражение соответствует серии цифр в начале строки. Мы снова используем функцию обратного вызова; на этот раз самой функции преобразования nдостаточно, поскольку все совпадение (первый аргумент a) - это число, которое мы хотим преобразовать.

Поскольку это последнее выражение в программе, Pip автоматически печатает результат. -nФлаг отделяет список результатов с новой строкой.

DLosc
источник