Ярославский Государственный Университет им. П.Г. Демидова
Опубликован: 06.11.2008 | Доступ: свободный | Студентов: 918 / 31 | Оценка: 4.50 / 4.00 | Длительность: 10:47:00
Лекция 4:

Язык РЕФАЛ: первичные функции и примеры составления программ

< Лекция 3 || Лекция 4: 12 || Лекция 5 >

Функции лексического анализа

Функции предназначены для лексического разбора выражений. В табл. 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.1.
Имя функции Выражение аргумента Назначение функции Возвращаемое значение
first <N> <E> отщепляет от начала выражения E часть, имеющую длину N термов (E1) E2, где E = E1E2, указанную или '*'E, если длины мало
last <N> <E> отщепляет от конца выражения E часть, имеющую указанную длину N термов E1 (E2), где E = E1E2, или E'*', если длины мало
lengw <E> определяет длину выражения в термах N E, где Nмакроцифра
lengr <E> определяет длину выражения в символах вместе со скобками N E, где Nмакроцифра
multe <N> <E> размножает выражение E в N экземплярах EE...E если N = 0, то пустое выражен.

Функции для работы с символами-метками

Иногда требуется превратить символ-метку в цепочку символов, из которой она состоит, и при этом, если одну и ту же символ-метку превращать в цепочки символов несколько раз, то требуется, чтобы получались одинаковые цепочки. Наоборот, иногда требуется превратить цепочку символов в символ-метку. При этом одинаковые цепочки должны превращаться в одну и ту же символ-метку. Наконец, полученную из цепочки символ-метку иногда требуется зарегистрировать в качестве имени функции. Этим целям служат приведенные в табл. 4.2 функции. В качестве примеров приведем следующие вызовы функции, которые дадут такие результаты:

k/ftochar/  /aaaa3434/.  ->  'aaaa3434'
k/ftochar/  /ABCD/.      ->      'ABCD'
k/chartof/  'aaaa3434'.  ->  /aaaa3434/

   k/chartof/   'ABCD'.   -> /ABCD/
   k/functab/   /func1/.  ->
Таблица 4.2.
Имя функции Выражение аргумента Назначение функции Возвращаемое значение
ftochar /<F>/ превращает символ-метку /<F>/ в цепочку символов цепочка символов <F>
chartof <F> превращает цепочку символов F в символ-метку символ-метка /<F>/
functab /<F>/ регистрирует символ-метку /<F>/ <пусто>

Примеры программ

Рефал является металингвистическим языком для формализации синтаксиса алгоритмических языков. В этом его основное назначение, но благодаря первичным арифметическим функциям возможна реализация и вычислительных алгоритмов. Мы рассмотрим 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 суммирования:

  1. должна закончить вычисления, если предыдущее введенное число - нуль;
  2. в противном случае должна добавить к сумме предыдущее введенное число и ввести очередное число.

Эти 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 - преобразует полученный результат к выводимой цепочке символов.

Упражнения

  1. Разработайте программу на Рефале-2 проверки синтаксической правильности условного выражения языка С++, в котором все его части могут содержать условные выражения, идентификаторы переменных и целые числа без знаков между ними других операций.
  2. Разработайте программу на Рефале-2 вычисления максимума последовательности целых ненулевых чисел, заканчивающейся нулем.
  3. Разработайте программу на Рефале-2 вычисления первого номера максимума последовательности целых ненулевых чисел, заканчивающейся нулем.
  4. Разработайте программу на Рефале-2 вычисления произведения последовательности целых ненулевых чисел, ограниченной нулем.
  5. Разработайте программу на Рефале-2 вычисления последнего номера минимума последовательности целых ненулевых чисел, заканчивающейся нулем.
< Лекция 3 || Лекция 4: 12 || Лекция 5 >
Сергей Шумихин
Сергей Шумихин
Украина
Александр Петухов
Александр Петухов
Россия, Екатеринбург