BS(1)

НАЗВАНИЕ
bs - компилятор/интерпретатор программ умеренных размеров

СИНТАКСИС


bs  [файл [аргумент ...]]

ОПИСАНИЕ
Язык bs - отдаленный потомок Бейсика и Снобола-4 с некоторыми дополнениями из языка C. Язык bs создан для тех задач программирования, в которых время разработки так же важно, как и полученная в результате скорость выполнения. Формальности об явления данных и манипуляции с файлами и процессами минимизированы. Построчная отладка, операторы trace и dump, а также подробные сообщения об ошибках выполнения упрощают тестирование программ. Более того, можно отлаживать незавершенные программы; внутренние функции могут тестироваться до того, как написаны внешние функции, и наоборот.

Если в командной строке указан аргумент файл, ввод начинается из него и продолжается с терминала. По умолчанию, операторы, прочитанные из файла, компилируются для последующего выполнения. Операторы, введенные с терминала, обычно выполняются немедленно (см. ниже операторы compile и execute). Если последняя операция - не присваивание, результат оператора-выражения печатается.

Программы на языке bs состоят из строк. Символом продолжения является \ в конце строки. Язык допускает строки следующего вида:


     оператор

     метка  оператор

Метка - это имя (см. ниже), за которым следует двоеточие. Метки и переменные могут иметь одни и те же имена.

Оператор языка bs - это выражение или ключевое слово, за которым следует 0 или более выражений. Некоторые ключевые слова (clear, compile, !, execute, include, ibase, obase и run) всегда выполняются, как только они откомпилированы.

Синтаксис операторов

выражение
Целью выполнения выражения являются его побочные эффекты (значение, присваивание или вызов функции). Детали приводятся ниже, после описания типов операторов.
break
Выйти из самого внутреннего for/while цикла.
clear
Выполняется немедленно. Очистить таблицу имен и удалить скомпилированные операторы.
compile [выражение]
Выполняется немедленно. Откомпилировать последующие операторы (не принимая во внимание установленный режим немедленного выполнения). Необязательное выражение вычисляется и используется в качестве имени файла для последующего ввода. В последнем случае выполняется clear.
continue
Перейти к следующей итерации текущего for/while цикла.
dump [имя]
Напечатать имена и текущие значения всех нелокальных переменных. Если указана опция имя, сообщается только о данной переменной. После ошибки или прерывания выводится номер последнего оператора и (возможно) трассировка пользовательских функций.
exit [выражение]
Вернуться на системный уровень. Выражение возвращается в качестве кода завершения.
execute
Изменить режим выполнения на немедленный (прерывание дает аналогичный эффект). Эта команда не вызывает выполнения запомненных операторов (см. ниже команду run).
for имя = выражение выражение оператор
for имя = выражение выражение
...
next

for выражение, выражение, выражение оператор
for выражение, выражение, выражение
...
next Циклически выполнять оператор (первая форма) или группу операторов (вторая форма) под управлением переменной с указанным именем. Переменная принимает значение первого выражения, затем она увеличивается на единицу в каждом цикле, пока не превзойдет значения второго выражения. Третьей и четвертой формам требуются три выражения, разделенные запятыми. Первое из них - инициализация, второе
условие (если истинно, то продолжать), и третье
действие при переходе к следующей итерации (обычно приращение).
fun f([a, ...]) [v, ...]
...
nuf
Определить имя, аргументы и локальные переменные написанной пользователем функции. Допускается до 10 аргументов и локальных переменных. Эти имена не могут быть массивами, а также не могут быть связаны по вводу и выводу. Определения функций не могут быть вложенными.
freturn
Сигнализировать о неудачном завершении пользовательской функции. См. ниже операцию опроса (?). Если опрос отсутствует, freturn возвращает 0. Когда опрос активен, freturn возвращается к соответствующему выражению (возможно, выполняя возвраты из вложенных функций).
goto имя
Передать управление на хранящийся в памяти оператор с соответствующей меткой.
ibase N
Установить основание системы счисления при вводе чисел равным N. Поддерживаются только следующие значения N: 8, 10 (по умолчанию) и 16. Шестнадцатеричные значения 10-15 вводятся как a-f. Первой должна стоять цифра (то есть f0a следует вводить как 0f0a). Ibase (и, ниже, obase) выполняются немедленно.
if выражение оператор
if выражение
...
[else
...]
fi
Выполнить оператор (первая форма) или группу операторов (вторая форма), если результат вычисления выражения ненулевой. Цепочки символов 0 и "" (пустая) считаются нулевыми. Во второй форме допускается дополнительная группа операторов, которая должна выполняться, когда не выполняется первая группа. Единственный оператор, допустимый в той же строке, что и else, - это if; только другие fi могут быть в той же строке, что и fi. Поддерживается сокращение else и if в elif. Чтобы закрыть последовательность if ... elif ... [ else ... ], требуется только одно fi.
include выражение
Выражение должно определять имя файла. Файл должен содержать bs-операторы. Такие операторы становятся частью компилируемой программы. Операторы include не могут быть вложенными.
obase N
Установить основание системы счисления при выводе чисел равным N (см. выше ibase).
onintr метка
onintr
Программная обработка прерываний. В первой форме после прерывания управление передается на указанную метку, в точности так, как если бы в момент прерывания был выполнен оператор goto. После начала обработки прерывания установленная реакция отменяется. Во второй форме прерывание вызывает завершение выполнения.
return [выражение]
Вычислить выражение и возвратить его значение в качестве результата вызова функции. Если выражение не указано, возвращается 0.
run Перезапустить генератор случайных чисел. Передать управление на первый скомпилированный оператор. Если оператор run содержится в файле, он должен быть последним оператором.
stop Прекратить выполнение скомпилированных операторов. Bs возвращается в режим немедленного выполнения.
trace [выражение]
Управление трассировкой функций. Если выражение не указано (или имеет нулевое значение), трассировка выключается; в противном случае печатается протокол вызовов пользовательских функций и возвратов из них. Каждый возврат уменьшает на единицу значение выражения, указанного в операторе trace.
while выражение оператор
while выражение
...
next
Оператор while аналогичен for за исключением того, что указывается условие продолжения цикла.
! команда shell'а
Выполнить команду shell'а.
# ...
Этот оператор игнорируется. Он используется для включения в программу комментариев.

Синтаксис выражений

имя Имя используется для того, чтобы указать переменную. Имена состоят из буквы (прописной или строчной), за которой могут следовать буквы и цифры. Только первые шесть символов в имени являются значащими. За исключением имен, об явленных в операторах fun, все имена являются глобальными. Значениями имен могут быть числа (вещественные двойной точности) и цепочки символов; кроме того, имена могут быть связаны по вводу/выводу (см. ниже встроенную функцию open).
имя ([выражение [, выражение] ...])
К функциям можно обращаться по имени, за которым следуют аргументы в скобках, разделенные запятыми. За исключением встроенных функций (описанных ниже), имя должно быть определено при помощи оператора fun. Аргументы функций передаются по значению.
имя [выражение [, выражение] ...]
Такой синтаксис используется для обращения к массивам или таблицам (см. ниже описание встроенных функций для работы с таблицами). Для массивов каждое выражение урезается до целого и используется как спецификатор для имени. Результирующее обращение синтаксически эквивалентно имени (то есть может употребляться в тех же местах, что и имя); записи a[1,2] и a[1][2] обозначают одно и то же. Значения урезанных выражений должны быть в пределах от 0 до 32767.
число
Число используется для представления константного значения. Число записывается в стиле Фортрана и содержит цифры, десятичную точку (не обязательно) и, возможно, масштабный множитель, состоящий из символа e, за которым может следовать характеристика со знаком.
цепочка_символов
Цепочки_символов ограничиваются знаками ". Знак \ позволяет вставлять в цепочку следующие символы: кавычка (\"), перевод строки (\n), возврат каретки (\r), пробел (\b) и табуляция (\t). В остальных случаях символ \ обозначает сам себя.
(выражение)
Скобки используются для того, чтобы изменить порядок вычислений.
(выражение, выражение [, выражение ...]) [выражение]
Последнее выражение используется как индекс, чтобы выбрать один элемент из взятого в круглые скобки списка выражений, разделенных запятыми. Элементы списка нумеруются слева, начиная с нуля. Выражение:
(False, True)[a == b]
имеет значение True, если a равно b.
? выражение
Операция опроса проверяет "успех" вычисления выражения, а не его значение. Ее целесообразно использовать для проверки выхода на конец файла (см. ниже ПРИМЕРЫ), результата вычисления встроенной функции и для проверки возврата из определенных пользователем функций (см. freturn). "Реакция на прерывание", устанавливаемая данной операцией (например, конец файла), приводит к немедленному переходу к самому последнему опросу с возможным пропуском операторов присваивания или переходом через уровни вложенности функций.
- выражение
Результатом является значение выражения с обратным знаком.
++ имя
Увеличивает на единицу значение переменной (или элемента массива). Результатом является новое значение.
-- имя
Уменьшает на единицу значение переменной. Результатом является новое значение.
! выражение
Логическое отрицание значения выражения. Будьте осторожны, такое выражение может быть воспринято как запрос на выполнение команды shell'а.
выражение операция выражение
Обращения к общеупотребимым функциям двух аргументов обозначаются двумя аргументами, которые разделяются операцией, указывающей функцию. За исключением присваивания, конкатенации и операций сравнения, перед тем, как выполняется операция, оба операнда преобразуются к целому типу.

Бинарные операции (упорядочены по возрастанию приоритета):

=
Операция присваивания. Левый операнд должен быть именем или элементом массива. Результатом является правый операнд. Последовательные присваивания выполняются справа налево; все другие операции - слева направо.
_ (подчеркивание).
Операция конкатенации.
& |
Результатом & (логическое и) является 0, если какой-нибудь из ее аргументов - 0; результат равен 1, если оба аргумента ненулевые; результатом | (логическое или) является 0, если оба аргумента равны нулю; результат равен 1, если какой-нибудь из аргументов ненулевой. Обе операции трактуют пустую цепочку символов как 0.
< <= > >= == !=
Операции сравнения (меньше, меньше или равно, больше, больше или равно, равно, не равно) возвращают 1, если их аргументы находятся в указанном отношении. В противном случае они возвращают 0. Допустимы выражения, подобные a>b>c (что эквивалентно a>b & b>c). Если оба аргумента являются цепочками символов, проверяется лексикографическая упорядоченность.
+
Сложение и вычитание.
* / %
Умножение, деление и остаток.
^
Возведение в степень.

Встроенные функции
Работа с аргументами

arg(i)
Значение i-го фактического аргумента текущего уровня вызова функции. На нулевом уровне arg возвращает i-ый аргумент командной строки (arg (0) возвращает bs).
narg( )
Возвращает число переданных аргументов. На нулевом уровне возвращается число аргументов командной строки.

Математические функции

abs(x)
Абсолютная величина x.
atan(x)
Арктангенс x. Значение между - П/2 и П/2.
ceil(x)
Минимальное целое, не меньшее x.
cos(x)
Косинус x (Углы задаются в радианах).
exp(x)
Экспонента x.
floor(x)
Наибольшее целое число, не превосходящее x.
log(x)
Натуральный логарифм x.
rand( )
Равномерно распределенное между 0 и 1 случайное число.
sin(x)
Синус x.
sqrt(x)
Квадратный корень из x.

Операции с цепочками символов

size(s)
Размер (длина в байтах) цепочки s.
format(f, a)
Возвращает отформатированное значение a. Предполагается, что f - это спецификация формата в смысле printf(3S). Рекомендуется использовать только следующие типы спецификаторов формата: %...f, %...e и %...s.
index(x, y)
Номер первого символа в x, совпадающего с каким
либо символом из y. Если такого нет, возвращается 0.
trans(s, f, t)
Сопоставляет символы источника s с символами f, и заменяет их на символы, стоящие в той же позиции цепочки t. Символы источника, которые не встречаются в f, копируются в результат. Если f длиннее, чем t, символы источника, которые сопоставляются с избыточной частью f, не помещаются в результат.
substr(s, начало, ширина)
Подцепочка s с заданными началом и шириной.
match(цепочка_символов, шаблон)
mstring(n)
Шаблон имеет синтаксис, аналогичный регулярным выражениям команды ed(1). Символы ., [, ] (внутренние скобки), ^, * и $ являются специальными. Функция mstring возвращает n-ую (1 n 10) подцепочку обрабатываемой цепочки_символов, которая успешно сопоставлена с фрагментом шаблона, заключенного между парами символов \( и \), из последнего обращения к match. Шаблоны сопоставляются с началом цепочки_символов (как если бы все они начинались символом ^). Функция match возвращает число успешно сопоставленных символов. Пример:

match("a123ab123", ".*\([a-z]\)") == 6

mstring(1) == "b"

Работа с файлами

open(имя, файл, тип)
close(имя)
Аргумент имя должен быть именем переменной в смысле bs (переданным как цепочка символов). При обращении к функции open в качестве аргумента файл можно задавать:
  1. 0, 1, или 2, что означает стандартный ввод, стандартный вывод и стандартный протокол соответственно.
  2. Цепочку символов, представляющую имя файла.
  3. Цепочку символов с ! в начале, представляющую команду, которая должна быть выполнена (посредством sh -c).
Аргумент тип должен быть одним из следующих: r (читать), w (писать), W (писать без перевода строки), a (добавлять). После того как выполнена функция close, имя становится обычной переменной. Начальные связывания таковы:

open("get", 0, "r")

open("put", 1, "w")

open("puterr", 2, "w")

Примеры приведены ниже.
access(s, m)
Выполняет вызов access(2).
ftype(s)
Возвращает односимвольный индикатор типа файла: f для обычного файла, p для именованного канала, d для каталога, b для блочного устройства, c для символьного устройства.

Таблицы

table(имя, размер)
Таблица в bs - это одномерный массив с ассоциативным доступом. "Индексами" (их называют также ключами) являются цепочки символов (числа преобразуются в цепочки). Аргумент имя должен быть именем переменной в смысле bs (переданным как цепочка символов). Аргумент размер устанавливает минимальное число элементов, которое должно быть размещено. Bs печатает сообщение об ошибке и завершает работу, когда таблица переполняется.
item(имя, номер)
key( )
Функция item осуществляет доступ к элементам таблицы по их номерам (обычно множество значений ключей не допускает систематического перебора). После того, как функция item возвратит значение элемента таблицы, результатом функции key будет "индекс" (ключ) этого элемента. Аргумент имя нельзя брать в кавычки. Так как точные размеры таблицы не определены, надо использовать операцию опроса, чтобы обнаружить конец таблицы, например:

table("t", 100)

 ...

 

# Если значением переменной word является слово,

# следующее выражение добавляет единицу

# к счетчику этого слова:

++t[word]

 ...

 

# Напечатать пары ключ/значение:

for i = 0, ?(s = item(t, i)), ++i  if key() \

 put = key()_":"_s

iskey(имя, слово)
Проверяет, есть ли ключ слово в таблице имя; возвращает 1, если есть, и 0, если нет.

Прочие функции

eval(цепочка_символов)
Вычисляет свой аргумент как выражение в смысле bs. Эта функция удобна для преобразования числовых строк во внутреннюю числовую форму. Eval также может использоваться как грубый метод косвенной адресации. Например, после выполнения операторов

name = "xyz"

eval("++"_name)

значение переменной xyz увеличится на 1. Кроме того, функция eval с предшествующей операцией опроса позволяет управлять обработкой ошибочных ситуаций. Результатом выполнения оператора

?eval("open(\"X\", \"XXX\", \"r\")")

будет 0, если файла с именем "XXX" нет (вместо аварийного завершения программы пользователя). Следующий фрагмент выполняет переход на метку L (если она существует):

label="L"

if !(?eval("goto "_label)) puterr = "no label"

plot(запрос, аргументы)
Формирует вывод для устройств, которые поддерживаются программой tplot(1G). Допустимы следующие запросы:
plot(0, терминал)
Определяет, что дальнейший вывод программы plot должен направляться программе tplot(1G) с аргументом -Tтерминал.
plot(4)
"Очищает" плоттер.
plot(2, метка)
Ставит метку на текущую точку.
plot(3, x1, y1, x2, y2)
Рисует отрезок между (x1,y1) и (x2,y2).
plot(4, x, y, r)
Рисует круг с центром (x,y) и радиусом r.
plot(5, x1, y1, x2, y2, x3, y3)
Рисует дугу с центром (x1,y1) и концами (x2,y2) и (x3,y3).
plot(6)
Не реализован.
plot(7, x, y)
Делает точку (x,y) текущей.
plot(8, x, y)
Рисует отрезок из текущей точки в (x,y).
plot(9, x, y)
Рисует точку в (x,y).
plot(10, режим)
Устанавливает заданный режим проведения линий.
plot(11, x1, y1, x2, y2)
Устанавливает левый нижний угол области рисования в (x1,y1), а правый верхний - в (x2,y2).
plot(12, x1, y1, x2, y2)
Определяет, что соответствующие x (y) координаты должны умножаться на x1 (y1) и затем складываться с x2 (y2) перед тем, как они изображаются. Первоначальный масштаб - plot(12, 1.0, 1.0, 0.0, 0.0).
Некоторые запросы применимы не ко всем плоттерам. Все запросы, кроме 0 и 12, реализованы на основе передачи символов программе tplot(1G). Чтобы получить дополнительную информацию, см. plot(4).
last( )
В режиме немедленного выполнения возвращает последнее вычисленное значение.

ПРИМЕРЫ

  1. Использование bs в качестве калькулятора:
    
    $ bs
    
    #  Расстояние (в дюймах), которое свет
    
    #  проходит за наносекунду.
    
    186000 * 5280 * 12 / 1e9
    
    11.78496
    
    
    
    #  Сложный процент (из 6% за 5 лет
    
    #  с 1000 долларов).
    
    int = .06 / 4
    
    bal = 1000
    
    for i = 1 5*4  bal = bal + bal*int
    
    bal - 1000
    
    346.855007
    
    
    
    
    exit
  2. Общий вид типичных bs-программ:
    
    #  Инициализация:
    
    var1 = 1
    
    open("read", "infile", "r")
    
    ...
    
    
    
    #  Вычисление:
    
    while  ?(str = read)
    
    ...
    
    next
    
    #  Завершение
    
    close("read")
    
    ...
    
    
    
    #  Последний выполняемый оператор (exit или stop):
    
    exit
    
    #  Последняя входная строка
    
    run
    
    
  3. Примеры ввода/вывода:
    
    #  Копировать "oldfile" в "newfile".
    
    open("read", "oldfile", "r")
    
    open("write", "newfile", "w")
    
    ...
    
    while ?(write = read) ...
    
    ...
    
    # закрыть "read" и "write":
    
    close("read")
    
    close("write")
    
    
    
    #  Канал между командами.
    
    open("ls", "!ls *", "r")
    
    open("pr", "!pr -2 -h 'List'", "w")
    
    while ?(pr = ls)  ...
    
    ...
    
    
    
    #  Закрыть файлы:
    
    close("ls")
    
    close("pr")
    
    

СМ. ТАКЖЕ
ed(1), sh(1), tplot(1G).
access(2), printf(3S), stdio(3S), plot(4) в Справочнике программиста.

Более полное описание математических функций см. в разделе 3M Справочника программиста (для возведения в степень используется функция pow, описанная в exp(3M); пакет ввода/вывода используется стандартный.

СЮРПРИЗЫ
"Индексами" неинициализированных элементов таблицы являются цепочки из двух символов с восьмеричными кодами 43 и 300, поэтому приведенный пример печати пар ключ/значение будет работать неверно.

В цепочке символов нельзя употребить знак #, поскольку он служит началом комментария.