Смекни!
smekni.com

О возможности универсального кода внутреннего представления программы (стр. 1 из 4)

Михаил Козлов

Введение

Статья Андерсона [1], которому, видимо, принадлежит сама идея схемной реализации языка программирования высокого уровня, появилась практически одновременно (1961г.) если не с зарождением этих языков (FORTRAN, 1949), то с выходом их «на широкую арену» (COBOL, 1959, ALGOL-60 как попытка «официального языка публикаций алгоритмов» и т.п.). В действительности же предыстория проблемы охватывает бóльшие временные интервалы, а сама проблема глубже, чем это может представиться на первый взгляд. Неслучайно строгое формулирование понятия алгоритма непосредственно предшествовало появлению первых вычислительных машин. Очевидно, что этот факт есть следствие тесной связи алгоритма с тем «субъектом», который этот алгоритм реализует. Поэтому первым строгим определением алгоритма явилось именно понятие машины (автомата) Тьюринга (1936г.). В отличие от этой чисто математической абстракции в основе подавляющего большинства когда-либо созданных до сих пор компьютеров лежит более богатая деталями схема, являющаяся всё же в том или ином смысле эквивалентным автомату Тьюринга определением алгоритма – фон Неймановская архитектура вычислительной машины (1945г.), с которой непосредственно связано представление вычислительных алгоритмов командными программами.

Вывод, который можно извлечь из сказанного, состоит в некой нетривиальности взаимоотношения между «хардом» и «софтом». При этом использование алгоритмического языка высокого уровня можно рассматривать как моделирование более сложной машины средствами простой, что соответствует обычному понятию о «языковом процессоре». Ясно, что такая многоступенчатость делает компьютер более загадочным и недоступным, чем он представлялся изначально, и чем он является на самом деле. Среди практических попыток все же «образумить» и «очеловечить хард» можно выделить получившие некоторое распространение ЭВМ серии «Мир» (СССР) [2, 3] со схемной реализацией алголоподобного входного языка и «Систему 432» фирмы Intel [4], являющуюся, по-видимому, последним по времени (80-е годы) экспериментом в этом направлении. Однако в целом, как очевидно, эти попытки не оказались продуктивными.

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

Тем не менее, в течение всего периода существования электронной вычислительной техники протекает процесс накопления практического опыта, одним из аспектов которого служит продолжающаяся отработка и самого понятия алгоритма и естественное развитие изначально искусственно созданного «языка компьютера» в его многообразных «диалектах». Здесь можно выделить как один из ярких ранних (1968г.) примеров принцип программирования без оператора перехода (go to) [5], эффективность которого объяснить и обосновать было достаточно затруднительно.

В начальных этапах развития вычислительной техники ситуация, когда набор командных кодов процессора менялся параллельно с разработкой его математического обеспечения была нередкой. Коды вводились и вычищались из системы команд в соответствие с мнением программиста об их необходимости или ненужности. Массовое распространение компьютеров и стандартизация «софта» сделало животрепещущей проблему совместимости кодовых таблиц и переносимости матобеспечения, и оснащение очередного Pentium'а дополнительным набором графических команд реализуется как длительно подготавливаемая акция, «рыночная» ответственность которой несопоставима с почти хулиганским по сегодняшним меркам отношением к кодовым наборам полууникальных ЭВМ 60-х годов.

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

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

Используемое в качестве внутреннего кода представление программы последовательностью операторов со списками аргументов с одной стороны, универсально близко практически любому алгоритмическому языку, с другой – при всей своей «языковости» сохраняет наибольшее подобие командной программе с присущей или приписываемой ей особой гибкостью.

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

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

Соответствующее символьное представление алгоритма почти не содержит специфических элементов и весьма близко к распространенной общематематической символике. Операторно-формульный код, таким образом, имеет некую «объективную» основу, снижающую разнообразие возможных его вариантов, в противовес системам команд обычных процессоров, многообразие которых может быть приведено к некоторому единству лишь более или менее принудительной либо вынужденной стандартизацией.

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

Алгоритмические формулы

Основным видом элементов практически любого языка высокого уровня являются оператор или функция в следующей записи:

имя оператора или функции (аргумент1,..., аргументn).

Инструкция (команда) машинного языка чаще всего выглядит примерно так:

код команды | операнд1 | операнд2.

Как видим, в наиболее обобщенной форме эти конструкции достаточно сходны. Наибольшее отличие состоит в том, что аргументы оператора могут представлять собой формульные выражения, включающие в себя множество переменных, знаков операций, скобок и идентификаторов функций, в то время как операнды команды могут быть лишь индивидуальными адресными ссылками на ячейки оперативной памяти. Но именно наличие формул в языке составляет самую «неприятную», если не сложную для обработки составляющую часть символьной программы, так как при их трансляции нарушается естественное линейное соответствие между командами объектной программы и лингвистическими конструкциями исходной записи алгоритма.

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

Как известно [6], «наименьший» универсальный язык может быть сведен к простым переменным, операторам присваивания и операторам условного и безусловного перехода. Этот набор соответствует языку логических схем Ляпунова – Янова (ЯЛС) [7...9], являющемуся одной из ранних математических моделей языка программирования высокого уровня. Отметим, что первые из получивших наибольшее распространение языков высокого уровня (FORTRAN, COBOL, ALGOL...) были далеко не минимальны по составу своих изобразительных средств и при этом значительно отличались друг от друга. Однако с течением времени сформировалось достаточно определенное ядро из набора операторов, систематически воспроизводящихся в большинстве современных языков.

Таблица 1

Соответствие операторно-формульного представления алгоритма операторам языка BASIC

Символ и его название Операторы BASIC
(f) загрузка контекста SELECT CASE f
(x, f) присваивание (загрузка) x = f
(x, f, ..., g) циклическая загрузка
└(M) отсылающая полускобка Янова GOTO M
└(t, M) условная полускобка Янова IF t THEN GOTO M
┘(M) принимающая полускобка Янова M:
[(t) открывающая условная скобка IF t THEN
][ else-скобка ELSE
] закрывающая условная скобка END IF
{ открывающая цикловая скобка DO
{(t) открывающая цикловая скобка <DO> WHILE t
{(n) открывающая цикловая скобка FOR loop = 1 TO n
{(M, k) открывающая цикловая скобка FOR loop = M TO k
{(M, k, l) открывающая цикловая скобка FOR loop = M TO k STEP l
} закрывающая цикловая скобка NEXT или LOOP
}(t) закрывающая цикловая скобка <LOOP> WHILE t
C[ контекстная открывающая условная скобка (при логическом, арифметическом или строковом контексте) CASE –1,
CASE IS<>0 или
CASE IS<>""
C[(g) контекстная открывающая у.с. CASE g
E[ контекстная else-скобка CASEELSE

Здесь t – логическое выражение; n – целое неотрицательное выражение; m, k, l – арифметические (т.е. целые либо вещественные) выражения; s – строковое; f, g – произвольные выражения; x – переменная, M – метка.