Язык РЕФАЛ: первичные функции и примеры составления программ
Функции лексического анализа
Функции предназначены для лексического разбора выражений. В табл. 4.1 приведены все 5 функций, их аргументы и назначение. В качестве примеров рассмотрим следующие вызовы функции, которые приведут к таким результатам:
k/first/ /2/ 'A'('B')'C'. -> ('A'('B')) 'C' k/first/ /5/ 'A'('B')'C'. -> '*A'('B')'C' k/last/ /2/ 'A'('B')'C'. -> 'A' (('B')'C') k/last/ /5/ 'A'('B')'C'. -> 'A'('B')'C*' k/lengw/ 'A' () ('A'). -> /3/ 'A' () ('A') k/lengw/ . -> /0/ k/lengr/ 'A' () ('A'). -> /6/ 'A' () ('A') k/lengr/ . -> /0/ k/multe/ /5/ 'A'. -> 'AAAAA' k/multe/ /2/ 'A'('B'). -> 'A'('B')'A'('B')
Функции для работы с символами-метками
Иногда требуется превратить символ-метку в цепочку символов, из которой она состоит, и при этом, если одну и ту же символ-метку превращать в цепочки символов несколько раз, то требуется, чтобы получались одинаковые цепочки. Наоборот, иногда требуется превратить цепочку символов в символ-метку. При этом одинаковые цепочки должны превращаться в одну и ту же символ-метку. Наконец, полученную из цепочки символ-метку иногда требуется зарегистрировать в качестве имени функции. Этим целям служат приведенные в табл. 4.2 функции. В качестве примеров приведем следующие вызовы функции, которые дадут такие результаты:
k/ftochar/ /aaaa3434/. -> 'aaaa3434' k/ftochar/ /ABCD/. -> 'ABCD' k/chartof/ 'aaaa3434'. -> /aaaa3434/ k/chartof/ 'ABCD'. -> /ABCD/ k/functab/ /func1/. ->
Примеры программ
Рефал является металингвистическим языком для формализации синтаксиса алгоритмических языков. В этом его основное назначение, но благодаря первичным арифметическим функциям возможна реализация и вычислительных алгоритмов. Мы рассмотрим 2 примера, показывающие возможности Рефала в этих двух направлениях.
Синтаксический анализатор для языка арифметических выражений
Рассмотрим упрощенный язык арифметических выражений, в котором введены всего 2 арифметические операции: сложение (+) и умножение (*), а также переменные, числа и скобки, определяющие порядок вычислений. Синтаксис языка определен следующими формами Бэкуса-Наура (БНФ):
<арифмвыр> ::= <арифмвыр>+<множитель>| <множитель> <множитель> ::= <множитель>*<первичное>| <первичное> <первичное> ::= (<арифмвыраж>)|<число>| <имяпеременной> <число> ::= <цифра>|<число><цифра> <имяпеременной> ::= <буква>|<имяпеременной><буква>| <имяпеременной><цифра> <цифра> ::= 0|1|2|3|4|5|6|7|8|9 <буква> ::= A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R| S|T|U|V|W|X|Y|Z|a|b|c|d|e|f|g|h|i|j|k|l| p|q|r|s|t|u|v|w|x|y|z
Например, правильным арифметическим выражением является следующее
a+(2+c*d)*(d+(a+c)*2),
a+(2+c*d*(d+)(a+c)*2)
не является правильным, так как его часть (d+)(a+c) не является множителем (выражение в первых скобках не является правильным арифметическим выражением - нет множителя после знака '+' и между первым и вторым первичными выражениями в скобках нет знака '*' ).
Необходимо построить для этого языка синтаксический анализатор, который бы проверял вводимое арифметическое выражение и в случае правильности выдавал бы в качестве результата на экран строку "Выражение верно", а в случае ошибочности указывал бы ошибочное место и диагностику ошибки (например, "ошибка: d+ - не имя переменной" ).
Программа анализатора на Рефале будет выглядеть следующим образом:
ANALYZE START ENTRY ArExpr EXTRN prout, card ArExpr = k/pr/ k/арифмвыр/ k/prout/'Введите: '. k/card/... арифмвыр R V1'+'V2 = k/арифмвыр/V1. k/множитель/V2. E1 = k/множитель/ E1. множитель R V1'*'V2 = k/множитель/V1. k/первичное/V2. V1 = k/первичное/ V1. = '?' – пропущен множитель первичное '('E1')' = k/арифмвыраж/ E1. S(D)1 E2 = k/число/ S1 E2. S(L)1 E2 = k/имяпеременной/ S1 E2. V1 = '?'V1 – не первичное выражение число V(D)1 = E1 = '?'E1 – не число имяпеременной S(L)1 E(LD)2 = E1 = '?'E1 – не имя переменной pr '?' E1 = к/prout/ 'ошибка: ' E1. E1 = k/prout/ – является выражением. END
В этой программе вслед за директивой начала START идет директива ENTRY, определяющая в качестве входа программы функцию ArExpr, и директива EXTRN, определяющая использование внешних модулей с первичными функциями ввода card и вывода prout.
Функция ArExpr, с которой начинается выполнение программы, выводит функцией prout приглашение "Введите: ", затем вводит функцией card строку выражения, производит анализ выражения функцией арифмвыр и, наконец, выводит функцией pr результат синтаксического анализа.
Описание функций арифмвыр, множитель и первичное соответствует БНФ языка арифметических выражений: их рефал-предложения в точности соответствуют альтернативам в правых частях соответствующих БНФ.
С БНФ буква и цифра языка мы не связываем функций программы, так как в Рефале им соответствуют множество L символов букв и множество D символов цифр. Поэтому функции число и имяпеременной определены рефал-предложениями с использованием этих множеств.
В описаниях функций, соответствующих БНФ языка, добавлены в конце рефал-предложения, которые диагностируют соответствующую ошибку и с этой целью помечают диагностику ошибки символом '?', позволяющим функции pr идентифицировать ошибку и вывести диагностику. Второе рефал-предложение функции pr выводит сообщение о верности арифметического выражения, т. е. соответствии с приведенным синтаксисом языка арифметических выражений.
Этот пример показывает, что для построения синтаксического анализатора любого языка надо записать все БНФ языка в виде функций и соответствующих им рефал-предложений. В этом и состоит основное назначение языка Рефал.
Суммирование последовательности чисел
Рассмотрим пример разработки программы суммирования последовательности чисел. Числа вводятся с клавиатуры и суммируются до тех пор, пока не будет введен нуль. В этом случае сумма чисел выдается на экран.
Из постановки задачи следует, что каждое вводимое число должно анализироваться на нулевое значение - признак окончания суммирования и, если оно не нуль, накапливаться в сумме. В качестве начального значения суммы возьмем нуль, а ввод очередного слагаемого будем делать в самой функции суммирования. Таким образом, функция sum суммирования:
- должна закончить вычисления, если предыдущее введенное число - нуль;
- в противном случае должна добавить к сумме предыдущее введенное число и ввести очередное число.
Эти 2 действия можно осуществить двумя рефал-предложениями функции, что и делает следующая программа.
SUMMA & START & & ENTRY & Summ & EXTRN & prout, card, numb, symb, add Summ = k/pr/ k/sum/(/0/) k/prout/'Добавьте число '. k/numb/ k/card/.... sum (S(N)1) /0/ = S1 (S(N)1) S(N)2 = k/sum/ (k/add/ (S1) S2.) k/prout/'Добавьте число '. k/numb/ k/card/... pr S(N)1 = к/prout/ 'Сумма=' k/symb/ S1. E1 = k/prout/ 'ошибка: 'E1. END
Первичная функция numb преобразует вводимое число из цепочки символов в символ-число, add - добавляет в накапливаемую сумму очередное число, а symb - преобразует полученный результат к выводимой цепочке символов.
Упражнения
- Разработайте программу на Рефале-2 проверки синтаксической правильности условного выражения языка С++, в котором все его части могут содержать условные выражения, идентификаторы переменных и целые числа без знаков между ними других операций.
- Разработайте программу на Рефале-2 вычисления максимума последовательности целых ненулевых чисел, заканчивающейся нулем.
- Разработайте программу на Рефале-2 вычисления первого номера максимума последовательности целых ненулевых чисел, заканчивающейся нулем.
- Разработайте программу на Рефале-2 вычисления произведения последовательности целых ненулевых чисел, ограниченной нулем.
- Разработайте программу на Рефале-2 вычисления последнего номера минимума последовательности целых ненулевых чисел, заканчивающейся нулем.