Московский государственный индустриальный университет
Опубликован: 27.09.2006 | Доступ: свободный | Студентов: 3338 / 384 | Оценка: 4.17 / 3.79 | Длительность: 24:17:00
Специальности: Программист
Лекция 12:

Проект "Компилятор формул"

Интерпретатор арифметических выражений

Если компилятор осуществляет перевод с одного языка на другой, то интерпретатор вычисляет значение арифметической формулы, в которой вместо имен переменных содержатся записанные тем или иным способом числа.

После применения компилятора (называемого также транслятором ) полученная программа на выходном языке обычно выполняется с помощью специальной программы. Интерпретатор совмещает в себе обе эти стадии — компиляцию и выполнение.

В случае программ на языках C и C++ компилятор позволяет получить файл, который содержит машинные команды и, следовательно, может быть выполнен непосредственно. Для языка Java компилятор строит так называемый байт-код, для исполнения которого необходима специальная программа (запускаемая с помощью команды java ).

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

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

Выше уже отмечалось, что наряду с десятичной системой счисления в программировании часто используют двоичную, восьмеричную и шестнадцатеричную. В целом ряде языков программирования принято соглашение, согласно которому числа, запись которых начинается с нуля, считаются восьмеричными, а те, запись которых начинается с 0x или 0X, — шестнадцатеричными. Таким образом, запись 0458 является некорректной (так как восьмеричная система счисления не содержит цифры 8), а в записи чисел, начинающихся с 0x или 0X, могут использоваться буквы от A до F, обозначающие шестнадцатеричные цифры со значениями от 10 до~15.

Таблица 12.1. Запись чисел в римской системе счисления
1 2 3 4 5
I II III IV V
6 7 8 9 10
VI VII VIII IX X
11 13 18 19 22
XI XIII XVIII XIX XXII
34 39 40 60 99
XXXIV XXXIX XL LX XCIX
200 438 649 999 1207
CC CDXXXVIII DCXLIX CMXCIX MCCVII
2045 3555 3678 3900 3999
MMXLV MMMDLV MMMDCLXXVIII MMMCM MMMCMXCIX

Хорошо известным примером непозиционной системы счисления являются римская. В этой системе цифры I, V, X, L, C, D и M всегда обозначают 1, 5, 10, 50, 100, 500 и 1000 соответственно, вне зависимости от позиции цифры в записи числа. При записи чисел в римской системе счисления значением числа является алгебраическая сумма цифр, в него входящих. При этом цифры в записи числа следуют, как правило, в порядке убывания их значений, и не разрешается записывать рядом более трех одинаковых цифр. В том случае, когда за цифрой с большим значением следует цифра с меньшим, ее вклад в значение числа в целом является отрицательным. Таблица 12.1 содержит типичные примеры, иллюстрирующие общие правила записи чисел в римской система счисления (не превосходящих 3999).

Перейдем теперь к построению интерпретатора, ограничившись случаем, когда в качестве чисел могут использоваться только однозначные положительные числа.

Задача 12.2. Напишите программу (интерпретатор формул), которая для любой правильной арифметической формулы (элемента языка L(G_0) ), содержащей цифры от 0 до 9, вычисляет и печатает значение этой формулы.

Для того чтобы реализовать стековый калькулятор, конечно, необходим стек целых чисел. Его непрерывная реализация будет осуществляться классом StackInt. Класс Calc, который будет осуществлять интерпретацию арифметических формул, во многом похож на класс Compf. Одним из отличий является то, что в формуле присутствуют числа, а не идентификаторы переменных. Вторым и более существенным — выполнение операций вместо их печати в виде программы для стекового компилятора.

Последнее означает, что обработка переменной должна быть заменена на помещение числа в стек калькулятора, а печать арифметической операции следует заменить на извлечение двух верхних элементов из стека и выполнение этой операции с последующей записью ее результата обратно в стек.

Из вышесказанного следует, что класс Calc целесообразно сделать дочерним по отношению к ранее нами созданному классы Compf, включив в него private -компоненту s (стек целых чисел) и переопределив методы symOther, nextOther и nextOper:

protected int symOther(char c) {
        if (c < '0' || c > '9') {
            Xterm.println("Недопустимый символ: " + c);
            System.exit(0);
        }
        return SYM_OTHER;
    }	
    protected void nextOper(char c) {	
        int second = s.pop();
        int first  = s.pop();
        switch (c) {
          case '+':
            s.push(first + second); break;
          case '-':	  
            s.push(first - second); break;
          case '*':
            s.push(first * second); break;
          case '/':
            s.push(first / second); break;
        }
    }
    protected void nextOther(char c) {	
        s.push(char2int(c));
    }

Метод char2int позволяет преобразовать символ в соответствующее ему целое число:

private static int char2int(char c) {
        return (int)c - (int)'0';
    }

Конструктор класса Calc должен создать стек целых чисел, а метод compile после вызова одноименного метода родительского класса обязан напечатать результат вычислений, находящийся на вершине стека. Итоговый текст интерпретатора вместе с содержимом файлa Makefile, предусматривающим работу как с компилятором формул, так и с интерпретатором, приведены в последней секции параграфа.

Анастасия Халудорова
Анастасия Халудорова
екатерина яковлева
екатерина яковлева