Читайте данную работу прямо на сайте или скачайте
Языки программирования Оберон и Оберон-2
МОСКОВСКИЙ ТЕХНИКУМ КОСМИЧЕСКОГО ПРИБОРОСТРОЕНИЯ
Реферат по предмету
"Технология разработки
программных продуктов"
по теме:
"Языки программирования
Оберон и Оберон-2"
Выполнил Знобищев А. В.
группа МП31-03
Проверил Сидорова Н. А.
2005 г
Содержание
1 История создания и развития языка..........................3
1.1 Предшественники языка...............................3
1.2 Проект Oberon.......................................4
1.3 Затмение Оберона....................................5
1.4 Ренессанс Оберона...................................6
2 Краткие сведения об авторах языка..........................9
Никлаус Вирт...........................................9
Ханспетер Мессенбок....................................9
3 Краткая характеристика основных языков Оберон-семейства...10
3.1 Оберон.............................................10
3.2 Оберон-2...........................................10
3.3 Компонентный паскаль...............................10
3.4 Active Oberon......................................10
3.5 Zonnon.............................................10
4 Отличия от Паскаля........................................11
5 Различия между языками Oberon и Oberon-2..................12
5.1 Типизированные процедуры...........................12
5.2 Замороженный экспорт (READ-ONLY EXPORT)...........13
5.3 Открытые массивы...................................13
5.4 Оператор WITH......................................14
5.5 Оператор FOR......................................14
6 Сравнение Оберона с другими языками по сложности.........15
Приложение: Описание языка программирования Оберон-2........16
Пример программы............................................36
Литература..................................................39
1 История создания и развития языка.
Судьба Оберона
1.1 Предшественники Оберона (Романтические 1970-е)
Паскаль умер. Эту фразу приходится слышать довольно часто. Одни произносят ее с ностальгией, с налетом грусти, мом понимая, что романтические 1970-е годы же не вернуть. Другие - без тени эмоций, спокойно, как констатацию приговора, вынесенного самой судьбой. Начало 1970-х ознаменовалось рождением трех языков, роль которых в развитии современного программирования переоценить тяжело. Паскаль (Никлаус Вирт, 1970; ETH, Швейцария), Си (Деннис Ритчи, 1971; AT&T Bell Labs, США) и Smalltalk (Алан Кей, 1972; Xerox PARC, США) - эта великая тройка дала путевку в жизнь трем важнейшим направлениям: структурному, системному и объектно-ориентированному программированию (ООП). Она определила и разные языковые ветви с непохожим синтаксисом и существенно отличающейся языковой культурой. Паскаль продолжил строгую линию Алгола-60, брав все наносное и порядочив его фундамент. Три кита структурного программирования (последовательность операторов, ветвление и цикл), закрепленные Чарльзом Хором и Никлаусом Виртом в аксиоматическом описании Паскаля, стали столь же незыблемыми в информатике, как законы Ньютона в классической механике. Для математиков, физиков и представителей других наук Паскаль стал нифицированной языковой средой, своего рода латынью программирования. Как известно, в истории латинского языка выделяют три ярких периода: золотая латынь (Цицерон, Цезарь, Вергилий, Гораций, Овидий), серебряная латынь и поздняя латынь. Язык Паскаль также прошел три этапа: золотой (197Ч1985, ETH Pascal, UCSD Pascal), серебряный (198Ч1995, Turbo/Borland Pascal) и поздний (с 1995, Delphi).
Язык Си, этот бунтарь-анархист, в начале 1970-х бросил дерзкий вызов всем классическим языкам. Си не хотел ни внешне, ни внутренне походить на то, что до него знали. Машинная арифметика, ассемблерный стиль программирования, странный синтаксис. Язык получился незамысловатый, хотя и весьма "огнеопасный". Но для тех, кто искал приключений, любил ходить по лезвию ножа, он оказался родным и просто незаменимым. Си имел мощный
практический фундамент - на нем была написана ОС UNIX, причем росли и чились жизни они бок о бок, в одной комнате, в стенах AT&T Bell Labs.
Smalltalk почти все 1970-е годы был малоизвестен. В лабораториях Xerox только-только зарождались первые ростки персональных компьютеров, об исследованиях знали лишь посвященные. Да и в отличие от Паскаля и Си язык сразу сросся со своей инструментальной средой. Это была маленькая селенная, особый мир, в котором творить можно было буквально все. Рай для детей и взрослых. Но он слишком опередил времяЕ C++, Java, C# спустя десятилетия шли по его стопам, но же куда более жестким, размеренным, прагматичным шагом.
В этой большой тройке Паскаль выделялся ярче остальных. Он появился в период кризиса языков Алгол семейства и противопоставил имперскому величию Алгола-68 и ПЛ/1 изысканность и простоту. Мир больших машин становился все более тесным и душным для воплощения новых идей. Человечество смутно искало выход, и революция микрокомпьютеров, где Паскаль блестяще солировал, стала сильнейшим катализатором популярности самого известного языка профессора Вирта. Паскаль открывал широкие перспективы не только перед высшей школой и научным миром, ибо благодаря нификации резко силился обмен идеями, но и перед бизнесменами новой волны, рванувшим на "золотые прииски" Кремниевой долины. Практичные американцы подхватили знамя Паскаля и с присущим им размахом стали водружать его на ключевых высотах. Марка Паскаля была сильна, но в реальности под ней чего только не выпускали. Рынок сначала подмял под себя язык, затем и размыл его на множество несовместимых диалектов. Так от блестящих образцов "золотой латыни" остались одни лишь смутные воспоминания. А что же Вирт? Он старался не отвлекаться на всю ту мишуру и шумиху, которая окружила такой корыстной заботой его детище. Вдохновленный поездкой в Xerox PARC (197Ч1977), где был создан Mesa, один из лучших языков модульного программирования, он приходит к мысли, что всю триаду "компьютер-язык-ОС" надо проектировать одновременно. Только тогда можно добиться гармонии в этом трудном инженерном деле - конструировании удобной и надежной среды для персональной работы. Один за другим он создает языки Modula (1976) и Modula-2 (1979). Причем на этот раз переносит акцент на аппаратную составляющую - компьютер Lilith (197Ч1981) стал первым в Европе персональным 16-разрядным компьютером с растровым дисплеем, винчестером, мышью, лазерным принтером и локальной сетью, при этом был создан силами небольшой группы исследователей из ниверситетского центра. Он работал под правлением собственной модульной ОС - Medos (автор - Свен Кнудсен), полностью написанной на новом языке Вирта (Modula-2). Более того, для эффективности выполнения на аппаратном ровне была реализована поддержка M-кода, дальнейшего развития знаменитого P-кода Вирта (кода виртуальной Паскаль-машины), который спустя почти четверть века после своего появления был положен в основу платформы Java. За эти работы Никлаус Вирт в 1984 г. был представлен ассоциацией ACM к высшей награде - премии Алана Тьюринга (Alan Turing Award), которая в компьютерном научном мире эквивалентна Нобелевской премии. 1980-е годы прошли под знаком колоссального интереса к персональным компьютерам. Apple и IBM начали свое великое противостояние в мире ПК. Но как же далеки были эти компьютеры от того, что было сделано в лабораториях Xerox и учебных корпусах ETH ! Отрасли потребовалось еще целое десятилетие, чтобы подобраться к идеям, реализованным вдохновенными инженерами по обе стороны Атлантики. На страницах PC World, PC Magazine, BYTE не только замелькала реклама новинок рынка, но и стали появляться статьи, определявшие пути развития компьютерного мира. Благодаря феномену языка Smalltalk (точнее, системы Smalltalk-80) интерес к ООП стал расти как снежный ком. Стало очевидно, что новый подход к программированию не обойдет стороной и другие языки.
1.2 Проект Oberon
Осенью 1985 г. Вирт и его коллега Юрг Гуткнехт начинают проект Oberon. Вновь создавалась триада "язык-компьютер-ОС". На этот раз акцент переносился на ОС и произошло более четкое разделение труда. Вирт проектировал язык и писал компилятор, Ганс Эберле конструировал компьютер, Гуткнехт создавал систему Oberon. На самом деле работы над новым компьютером начались немного раньше. Дальнейшее развитие Lilith поставило, по словам Вирта, задачу создания "современной, гибкой и эффективной операционной системы для однопользовательской рабочей станции". Поначалу казалось, что этого можно было добиться в рамках Medos и Modula-2. Лишь затем стало ясно, что нужны более кардинальные шаги. Если Lilith (архитектор - Ричард Оран) создавался на основе микропроцессоров AMD2901 фирмы Advanced MicroDevices, то Ceres (так звали новый компьютер, в честь богини Цереры)
использовал 32-разрядный процессор NS32032 фирмы National Semiconductor (затем жеNS32532 и NS32GX32). Вслед за серией Ceres в ETH был разработан компьютер Chameleon (Хееби Пфистер). Первой ОС была Medos, да и первый компилятор Оберона создавался на языке Modula-2 (Вирт).
Системе Oberon предшествовали исследования, которые проводились в ETH в 198Ч1985 гг. на Lilith Юргом Гуткнехтом и его коллегами, Винигером и Шером. Сначала появился редактор Andra с поддержкой всевозможных окон, шрифтов, средств композиции страницы, затем и редактор Lara. Примерно в тот же период Вирт разработал графический редактор и завершил работы над новым однопроходным компилятором языка Modula-2.
К середине 1988 г. же был готов компилятор для экспериментальной рабочей станции Ceres-2 (на базе NS-32532) и прошли первые публикации Вирта (в апреле и июле; сначала "Type Extensions" в ACM Transactions on Programming Languages and Systems, затем "From Modula to Oberon" в Software Practice & Experience).
"Компилятор Оберона был реализован для процессоров семейства NS32 и был встроен в операционную среду Oberon. Этот компилятор требует менее 50 Кбайт памяти, состоит из 6 модулей общим размером около 4 строк исходного текста и сам себя компилирует примерно за 15 секунд на рабочей станции с 25 Гц процессором типа NS32532". Никлаус Вирт (1988)
Вторая стадия проекта Oberon наступила летом 1991 г., когда было положено начало так
называемой Oberon System 3.
"Графические интерфейсы ныне используются повсеместно, и игнорировать это проявление духа времени не может себе позволить ни одна серьезная операционная система. Поэтому летом 1991 г. мы приступили ко второй стадии проекта Oberon. Однако, в ту пору мы хотели не просто облачить по сути "голую" систему Oberon в нарядные GUI-одежды, но и сделать важный шаг вперед. Несколько раздвигая границы стоявшихся представлений, мы сразу стали рассматривать графический интерфейс как набор истинных визуальных объектов".Ю. Гуткнехт (1994)
О системе Oberon следует говорить куда более обстоятельно, чем это позволяют рамки данной статьи. Здесь же помяну, что многие ключевые идеи взяты на вооружение современной ИТ-индустрией из системы Oberon: динамическая компиляция и аплеты, смарт-теги и Digital Dashboard, концепция интеллектуального документа и веб-службы.
Одно из наиболее перспективных направлений дальнейшего развития Oberon - превращение его в совершенствованную общую платформу для предоставления слуг даленным пользователям; слово "усовершенствованную" мы понимаем в данном случае как гибкую, высоко интегрированную и индивидуализированную. К настоящему времени мы провели эксперименты по организации следующих слуг даленным пользователям (и реализовали доступ к ним через Ethernet и TCP/IP):
Х электронный телефонный справочник;
Х электронный справочник пассажира железной дороги;
Х электронный словарь;
Х фотосервис Digital Kodak;
Х информационная система по географии Швейцарии;
Х служба символьных вычислений Maple;
Х служба TrueType-шрифтов;
Х FTP;
Х электронная почта;
Х правляющая служба Telnet;
Х поддержка World Wide Web;
Х Teletext и Telenews.
Ю. Гуткнехт (1994)
Подобно тому, как в свое время ОС UNIX начала ветвиться (System V и BSD), в ETH возникли две конкурирующие группы - Oberon V4 и Oberon System 3. Да и сама система Oberon оказалась непростой для самостоятельного изучения: интерклики мышкой - почти азбука Морзе. Спроектирована она была хорошо, вот реализация подкачала (главным архитектором проекта был Гуткнехт; главного инженера, вы, не было). добной становится после длительного освоения и только для посвященных - тех, кто сумел набраться терпения и преодолеть все издержки экспериментального программирования. Система Oberon оказалась Великой Китайской стеной, отделяющей новаторов-затворников от остального мира. Будучи хорошей базой для исследований Oberon System сыграла роковую роль в судьбе самих языков Оберон-семейства. Она затмила собой одноименный язык, созданный Виртом с участием Гуткнехта, окружив его "теплом и заботой".
"Перечислить все идеи, на основе которых сформировалось то, что сегодня называется
Обероном, Ч писал Вирт, - просто нет возможности. Большинство этих идей родилось в ходе
применения или изучения существующих языков (таких, как Modula-2, Ada, Smalltalk, Cedar),
которые часто показывали нам, каких решений следует избегать". На сегодняшний день компиляторы канонического Оберона доступны только внутри системы Oberon и системы Juice
(Oberon System для Netscape и Internet Explorer), также представлены транслятором COP2
(трансляция в Си). В контакт с агрессивным внешним миром коммерческих систем вступили
последователи Оберона: Oberon-2 и Component Pascal, затем Active Oberon и Zonnon. Силы в ETH в 1990-е годы были сосредоточены на создании переносимых и перенацеливаемых компиляторов для разных платформ. Этим занимались преимущественно аспиранты Вирта и
Гуткнехта. Кадры выросли очень сильные, апробированные идеи можно плодотворно изучать до сих пор (диссертации почти все как на подбор), вот результат их работы почти неизвестен
даже специалистам.
К февралю 1990 г. был готов переносимый компилятор OP2. Кстати, именно его брали за основу при начале работ над компилятором Oberon-2 Алексей Недоря и его новосибирские коллеги по XDS. В июне 1989 г. Ханспетер Мессенбок спроектировал Object Oberon, добавив ООП-расширения в канонический Оберон. Именно Мессенбока, пожалуй, и следует считать автором Oberon-2, в который вылился Object Oberon. Вирт не возражал против совторства в языке, поскольку расхождения по сравнению с Обероном были не фатальными, хотя и не лежали в рамках того критического взгляда на ООП, который исповедовал сам Вирт. Весной 1991 г. в журнале Structured Programming (издательство Springer-Verlag, в редколлегии были Н. Вирт и Д. Кнут) в одном номере вышли две статьи, давшие публичную жизнь языку Oberon-2: "The Programming Language Oberon-2" и "Differences between Oberon and Oberon-2", под которыми стояли фамилии Мессенбока и Вирта.
К моменту появления Oberon-2 в 1991 г. информация об Обероне только-только стала выходить из стен ETH. Сторонние разработчики компиляторов сделали ставку именно на Oberon-2, считая, что это просто лучшенный Оберон. При этом важно отметить, что распространение пошло не по линии Open Source - едва ли не каждый сторонний разработчик компиляторов хотел сделать на новом языке свой бизнес.
В июне 1993 г. в Кройдоне, что в предместье Лондона, в отеле Oakwood собрались заинтересованные лица, многие из которых получили горький опыт затяжной промышленной
ISO-стандартизации Modula-2. Были там и главные архитекторы лучших Оберон-компиляторов:
лексей Недоря (XDS), Гюнтер Дотцель (ModulaWare), Куно Пфистер (Oberon microsystems),
Джон Гуг (John Gough, QUT, GPCP). В ходе дискуссий были выработаны рекомендации и
стандартная библиотека для Oberon-2. Ее следы вы теперь найдете в компиляторах XDS и JOB.
1.3 Затмение Оберона
В 1993 г. в ETH приехали представители Sun Microsystems во главе с Биллом Джоем. Они
приобрели лицензию на систему Oberon и пригласили с ответным визитом выступить у них лучших учеников Вирта - Микаэль Франц сразу после защиты соответствующей диссертации в
ETH делал доклад по динамической кодогенерации в Sun Labs в марте 1994 г., за 14 месяцев до
выхода Java и за полгода до разработки браузера HotJava.
Согласно информации Sun [JavaSoft], идея переориентации Java на World Wide Web возникла в 1994 г. и принадлежала Биллу Джою, одному из основателей фирмы. Осенью 1994 г. Началась работа по реализации Web-браузера HotJava с возможностью выполнения аплетов. В марте 1995 г. браузер был представлен избранному кругу стратегических партнеров Sun, включая руководство корпорации Netscape Communications, чья публичная поддержка впоследствии немало способствовала спеху новой технологии. В августе 1995 г. состоялся триумфальный выпуск Java, который происходил в довольно необычной для базовых технологий форме - в виде серии пресс-конференций.Е может быть интересно то обстоятельство, что в марте 1994 г. автором был прочитан в Калифорнии ряд докладов по теме диссертации, причем один из них - в Sun Laboratories, Inc. Кроме того, помянутый выше Билл Джой, который переориентировал проект Java на, стал одним из первых обладателей лицензии на Oberon System из ETH, и в конце 1994 - начале 1995 г. он неоднократно связывался с ETH; в процессе контактов выяснилось, что он читал мою диссертацию.
Микаэль Франц (1996)
В 1994 г. Франц, разрабатывавший ранее кодогенератор Оберона для MC680x0 (Macintosh), завершил кодогенератор в промежуточный код - OMI (Oberon Module Interchange). Впервые на русском языке информация об этом была опубликована в альманахе Технология
программирования (1995, №1). Идея Франца была проста - вместо традиционной схемы компилятор Ч компоновщик - загрузчик получить схему компилятор - кодогенерирующий
загрузчик, иными словами, совместить генерацию кода, компоновщик и загрузчик в одном
флаконе.
Концепция Уcode-generation on-the-flyФ (динамическая кодогенерация, кодогенерация на лету) с использованием компактного древовидного представления вместо классического байт-кода была положена в основу одноименной диссертации М. Франца, которую он защищал в ETH в феврале 1994 г. Его научными руководителями были Никлаус Вирт и Юрг Гуткнехт. Крайне интересная диссертация. Помню, как о ней в среде Modula- и Оберон-сообщества только и говорили (почти с придыханием). Редкий случай - в Цюрихе в марте 1994 г. она была переиздана в виде книги.
В Sun не рискнули сразу копировать все из Oberon (идеи браузерной среды языка, аплетов и трансляции в мобильный код взяли, вот путь реализации мобильного кода выбрали свой). В 1991 г. автор Java Джеймс Гослинг при реализации Oak (прототипа языка Java) взял старую идею P-кода, которую хорошо знал: в 1975 г. Гослинг вместе с Недом Китлицем и Бобом Сайдботемом частвовал в построении среды программирования Pyxis/Multics Pascal, способной по быстродействию кода и добству интеграции на равных конкурировать в Multics c родным для этой ОС языком ПЛ/1. А начинали они с поддержки компилятора ETH/Zurich Pascal, разработанного в Цюрихе группой профессора Вирта. В 1979 г. Гослинг реализовал PERQ - транслятор с P-кода в машинный код DEC VAX.
В 1994 г. Sun не стали рисковать включением новейшей хитроумной реализации мобильного кода в древовидном представлении, что предлагал в диссертации Франц, а сохранили готовый подход Гослинга. Для всей отрасли модель Sun на долгие годы стала эталоном. К тому моменту широкой аудитории доступ к Оберон-компиляторам был заказан: даже при нынешнем ровне пиратского тиражирования столь специфичный инструментарий, имеющий единичные продажи, заполучить нереально.
Единственный игрок, кто мог cделать массовый коммерческий компилятор Оберона - компания TopSpeed (JPI) - в те годы шел к закату. Их последним движением в сторону новых языков Вирта было включение в Modula-2 собственных ООП-расширений. (Напомню, что фирма
JPI/TopSpeed вышла из недр Borland. Ее, вы, короткая жизнь началась после того, как в знак
протеста против замораживания проекта Turbo Modula-2 в году рыночным перспективам раскрученного Turbo Pascal компанию Borland покинула группа вице-президента Йенсена.)
Брюс Баррингтон, основатель и глава Clarion Software, приобрел компанию TopSpeed, гдеосновным рабочим языком был Modula-2, для перевода своего детища, языка Clarion, на рельсы лучших в ПК-отрасли компиляторов TopSpeed (Assembler, Modula-2, Pascal, C/C++). В связи с этим был заморожен проект TopSpeed Ada, а затем и все остальные компиляторы (они остались на 16-разрядной платформе). Единицы знают, что внутри среды Clarion (теперь этот бизнес ни шатко ни валко ведет фирма SoftVelocity) до сих пор запрятан 32-разрядный компилятор TopSpeed Modula-2, так никогда и не вышедший на рынок.
Это был еще один дар по Оберонам.
Но самой мощной волной, накрывшей Обероны с головой и на долгие годы предавшей их
забвению, стала Java. Она стремительно ворвалась в ту нишу, которую себе подготовил Оберон, нахраписто и без какого-либо поминания вырвала многие его идеи, высосала лучшие кадры. Но если бы еще это делалось с мом. А так... С другой стороны, прошло 10 лет с момента появления Java, и теперь после ревизии ошибок прошлого можно взглянуть на Обероны по-новому. Как и Smalltalk, cлишком ж они опередили свое время.
1.4 Ренессанс Оберона
Продолжим краткий рассказ об истории языков Оберон-семейства. Новый виток в их жизни начался в том момент, когда в ETH созрела идея создания небольшой компании (Oberon
microsystems), ориентированной на внутренний швейцарский рынок (в основном, это были
промышленные роботы). Главным мотором стал Куно Пфистер, архитектором Component
Pascal Ч Клеменс Шиперски.
К концу 1990-х годов на волне очевидного негатива к Sun в ETH пришла Microsoft. Начались совместные проекты (по линии Microsoft Research), совпавшие по времени с проектированием платформы.NET. Клеменс Шиперски из Oberon microsystems перешел в Microsoft Research. К тому моменту он же завоевал огромный авторитет в мире компонентно-ориентированного проектирования и программирования благодаря книге "Component Software - Beyond Object- Oriented Programming". Там есть отдельная глава, посвященная системе BlackBox (Component Pascal). Книга вышла в 1997 г., затем дважды стереотипно переиздавалась в 1998 г., затем дважды в 1 г., наконец второе ее издание вышло в 2002 г.
Компонентный Паскаль проектировался так, чтобы, с одной стороны, иметь возможность работать с основной компонентной моделью - COM (Oberon/F, предвестник BlackBox, получил в конце 1990-х годов на CeBIT приз за технологическое совершенство - за реализацию Direct-to-COM Compiler), с другой - с миром Java. При этом в Oberon microsystems (где, кстати, был написан по заказу Borland JIT-компилятор для JBuilder) начались работы по использованию Component Pascal для мира встроенных систем (ОС Portos, ныне JBed). Именно эти работы положили начало зарождению компании Esmertec - мирового лидера заказных решений для Java 2 Micro Edition.
Среди критиков Оберона можно слышать слова о том, что если он обладал столь совершенными технологиями, то почему его не видно на рынке. Честно говоря, трудно привести примеры, когда за последние четверть века технологическое совершенство в инфраструктурном или инструментальном программном обеспечении предопределяло безусловное лидерство на рынке. Обратных примеров сколько угодно.
Чтобы понять ту пропасть, которая отделяет нынешнюю ИТ-индустрию от взвешенного научно- технологического подхода, стоит задаться вопросами:
1. Зачем Borland в 1980-х годах топила в своих недрах технологически наиболее совершенную среду Turbo Modula-2? Правильно, в году своего рыночного положения, дабы этим не бить свой же Turbo Pascal - курицу, несущую золотые яйца.
2. Почему в 1990-х годах элегантный Smalltalk так и не смог поколебать позиции родливого C++, неумело поставившего крайне модные тогда идеи ООП на рельсы Си? Верно, не в интересах Microsoft и Borland было восстанавливать былую мощь IBM в области инструментария.
3. Почему Microsoft не вывела на орбиту очень приличный Eiffel Бертрана Мейера? Помните, как Билл Гейтс лично хлопал мэтра по плечу и всячески расхваливал компилятор Eiffel for.NET? Книгу Мейера о.NET превозносили до небес. Но... мавр сделал свое дело - мавр должен йти. Профессор Мейер был нужен Гейтсу лишь для раскрутки.NET. Мейер шел в затворничество в швейцарский ETH - альма-матер виртовских языков.
4. Почему компания Esmertec, отпочковавшаяся от Oberon microsystems и сделавшая в конце 1990-х годов свою дуальную ОС Portos для встроенных систем (Оберон+Java), вынуждена была практически полностью перевести свою продукцию на рельсы Java? Точно, если рынок предпочитает для себя худший язык (Java) и худшую среду (Java 2 Micro Edition) - пожалуйста, они сделали это просто лучше всех в отрасли и стали здесь лидерами. А вслед за этим
реализовали и компактный Smalltalk для систем реального времени.
В последние годы ИТ-индустрия насильно превращает ниверситеты в ремесленные училища. При этом Россия благодаря своей защитной инертности мудрилась во многом сохранить озис неремесленного программирования. И Delphi явился той палочкой-выручалочкой, которая этому во многом способствовала. За это Delphi большое спасибо. Но сейчас перспективы Delphi в маркетинговом плане выглядят весьма туманно. На платформе Linux инструментарий Kylix заморожен. Borland стала по сути филиалом Microsoft и нужна редмондскому гиганту, чтобы достойно противостоять линейке IBM Rational. Но дело не только в более чем реальном уходе Delphi с рынка. же сейчас специалисты по Delphi остаются в индустрии невостребованными. А поскольку ниверситеты стремительно сращиваются с индустрией, то Delphi будет вымываться из учебного процесса. На этом может быть поставлена жирная точка в судьбе языков Паскаль- семейства.
Мир инструментария все больше становится двухполюсным: Microsoft - IBM. Компания Sun Microsystems вопреки огромной шумихи не только почти ничего серьезного не сделала в Java за 10 лет (весь воз корпоративной Java почти в одиночку тащила IBM), но и своим апрельским соглашением 2004 г. с Microsoft фактически объявила войну своему бывшему союзнику.
На рынке ИТ все более силивается пропагандистская война, битва за мы. Здесь неуместны рассуждения о технологическом совершенстве и, паси Боже, о какой-то там науке!
Причины забвения Оберона выглядят так:
Х распыление сил и средств внутри ETH (V4 против System 3, Оберон против Oberon-2);
Х распространение лучших оптимизирующих компиляторов единичными тиражами на
коммерческой основе;
Х отсутствие групп пользователей (User Groups) и собственных изданий;
Х слабая интернет-активность;
Х мощная волна Java-пропаганды;
Х отток специалистов в коммерческий мир Java и.NET.
И все же самыми главными стали три момента:
1. Отсутствие четкой программы популяризации/вывода языков на рынок.
2. Колоссальный информационный голод.
3. Игнорирование резко выросшего потенциала OpenSource-движения.
Но язык не поворачивается обвинить во всем этом ETH. Он вел интенсивные исследования и совершенствовал свой учебный процесс, в чем достиг выдающихся результатов, вот поддержка "народных масс" в Швейцарии и близлежащих европейских странах оказалась слабоватой. Америке же Оберон был чужд и, наверное, наивно было бы полагать, что его подобно Паскалю начнут активно продвигать американцы, у которых был теперь свой "золотой телец" в лице Java.
И все же Оберон отнюдь не мер. Люди, занимавшиеся и продолжающие заниматься им,
стараются не особо это афишировать. За примерами далеко ходить не надо. ченик Вирта,
Микаэль Франц, создал внутри ниверситета Калифорнии в Ирвайне свою маленькую Оберон-
империю. Вот только он об этом не кричит на каждом перекрестке. Все финансирование проектов идет для его группы только по линии Java. Ныне его команда - один из мировых лидеров в исследовании мобильного кода и проблем ИТ-безопасности для Java. Ирония судьбы, ярый противник Java ведет перспективные Java-проекты для американского ВПК.
Критики Оберона не ставят под сомнение элегантность языка (да и перспективность всего семейства), а считают старевшим доступный для него инструментарий и неустойчивое
положение компаний-разработчиков. С позиций таких IDE-систем, как Visual Studio, Delphi и
Eclipse, он в самом деле выглядит по-спартански скромным, без "свисточков и звоночков". В то
же время благодаря Клеменсу Шиперски (Microsoft Research) и группе Пола Ро в Квинслендском
университете (Австралия) ориентированный на компонентное программирование диалект
Оберона в лице Component Pascal имеет три актуальных реализации для Visual Studio (.NET 1.x и 2.0), Java (JVM) и Eclipse (JVM). Юрг Гуткнехт совместно с Евгением Зуевым (ETH) продолжают развивать компилятор Zonnon для.NET с интеграцией в Visual Studio. Наконец, в России ведутся работы по переносу, пожалуй, основной инструментальной среды для Оберонов - BlackBox (Component Pascal) с платформы Win32 на Linux. Направление микромира - браузеров и встроенных систем (не только военного назначения, чисто бытового: мобильные телефоны, коммуникаторы, КПК) - крайне выгодно отличает Oberon от всех остальных языков. И хотя в связи с коммерческой активностью на этом рынке компании Esmertec Оберон-технологии носят закрытый характер, часть инструментария публично доступна для дальнейшего развития. Потенциал миниатюрного компилятора JOB (Oberon-2 для JVM), созданный Сергеем Свердловым, вполне пригодился бы для этого рынка. В области браузеров есть технология Juice (Oberon System для Netscape и IE), реализованная Микаэлем Францем и приостановленная в связи с переключением его группы на работы для Министерства обороны США по безопасности мобильного Java-кода. На ее основе можно делать замечательные вещи. Только представьте, что в любом современном браузере можно иметь Оберон-движок, который является полноценным компилятором с динамическим кодогенерирующим загрузчиком, имеет полный доступ к ОС и модули которого внешне (с точки зрения HTML) оформляются как Java-аплеты. Код аплетов получается примерно в 1,5-2 раза меньше, чем у Java, скорость исполнения - практически одинаковая. Все это выполнено еще в 1996 г. в виде подключаемого модуля для Netscape и Internet Explorer и занимает около 400 Кбайт. Есть все исходные тексты (на C++ реализована прослойка для ОС и браузера, на языке Оберон - все остальное). Очень перспективная вещь для задач, ориентированных на технологию тонких клиентов и требующих нешаблонных решений.
Поворотный этап в развитии Оберона начался с переводом BlackBox в категорию freeware и Open Source (конец 2004 г.). В мае 2005 г. новосибирская фирма Excelsior (XDS) перевела свою линейку Modula-2/Oberon-2 компиляторов в разряд freeware, вслед за началом разработки нового компилятора Modula-2 для НПО ПМ им. Решетнева, ведущего российского центра по созданию спутников связи. Одновременно с этим профессор Никлаус Вирт начал работы по адаптации своих бестселлеров для языка Оберон и перевод в открытый доступ своих лучших книг. Важнейшую роль в возрождении интереса к Оберону сыграла растущая активность нарождающегося Оберон-сообщества в России, которую стимулировал проект "Информатика-21", стартовавший на физфаке в МГУ в 2001 г. (автор - Ф. В. Ткачев). Вокруг проекта сформировалась панель консультантов, представляющих аэрокосмическую индустрию, Российскую академию наук и МГУ. Весной 2004 г. на базе швейцарского CERN (Европейский центр ядерных исследований, Женева), откуда вышел World Wide Web, с частием России были инициированы работы по активному применению Component Pascal в фундаментальных научных исследованиях. С 2003 г. в рамках "Студии программирования" журнала "Мир ПК" и CD- приложения "Мир ПК-диск" начались регулярные публикации работ по развитию Оберона и размещение соответствующего инструментария.
В сентябре-октябре 2005 г. с большим визитом по нашим крупнейшим ниверситетским центрам (Москва, С.-Петербург, Нижний Новгород, Новосибирск, Екатеринбург, Томск) Россию посеетил Никлаус Вирт и Юрг Гуткнехт. Осенью этого года начнет выходить электронный журнал "Школа Оберона", ближе к концу года (к 35-летию Паскаля и 25-летию выпуска Lilith) запланировано открытие сайта, в котором будет собрана и систематизирована информация о прошлом, настоящем и будущем языков Вирта, в том числе о новых проектах в ETH и за его пределами.
Если говорить об интересе к Оберону в остальном мире, то сейчас он более чем сдержанный. Те, кто разобрались, в чем его плюсы, - особенно не афишируют, используя его как конкурентное преимущество в своем бизнесе.
Бытует мнение, что Оберон сродни объектным СУБД: выглядит красиво, реально его почти не используют. Но разве то, что используют немногие, говорит об щербности или непрактичности? Конечно же, нет. Вопрос в том, сумеем ли мы вопреки шумной рекламе и сложившемуся статускво ИТ-индустрии приподняться над ветреной модой и суетой рынка и взять Оберон на вооружение. Это зависит лишь от нас самих.
И если ж проводить параллели, то куда корректнее сравнивать Оберон с путеводной звездой (хоть и самый дальний спутник рана). Тихо светит и другим не мешает...
2 Сведения об авторах языка
Никлаус Вирт (Niklaus Wirth) Ч профессор Швейцарского Федерального технологического института (ETH) в Цюрихе, который Вирт закончил в 1958 г. и где получил специальность инженера в области электроники. Затем он продолжил свое обучение в Лавальском ниверситете (Laval University) в Квебеке (Канада). В ниверситете Калифорнии в Беркли (University of California at Berkeley, США) в 1963 г. Вирт защитил докторскую диссертацию. До 1967 г. работал доцентом на вновь образованном факультете компьютерных наук в Стэнфордском ниверситете (Stanford University, США), где он разработал язык PL360 и, в сотрудничестве с рабочей группой IFIP Working Group 2.1, язык Algol-W. В том же 1967 г. становится доцентом в ниверситете Цюриха (University of Zurich), в 1968 г. переходит в ETH, где в период с 1968 по 1970 годы разрабатывает язык Pascal. Среди последующих проектов - разработка и реализация персонального компьютера Lilith, высокопроизводительной 16-разрядной рабочей станции с растровым дисплеем, создание языка Modula-2 (1978-1982 г.), и 32-разрядной рабочей станции Ceres (1984-1986 г.). Затем им были созданы языки Oberon и Oberon-2 (совместно с профессором Х.Мессенбоком), также операционная система Oberon (1986-1989 г.). В 1984 г. профессор Вирт был достоен почетной премии Алана Тьюринга (Turing Award), в 1989 г. - премии Max Petitpierre Prize, а также премии Science and Technology Prize от IBM Europe. Один из последних его проектов - система Lola System для разработки электронных схем (1995 г.).
Ханспетер Мессенбок (Hanspeter Moessenboeck) - известный специалист в области компиляторов, языков и систем программирования, объектно-ориентированного программирования, программной инженерии; разработчик экспериментального языка Object Oberon, вместе с Никлаусом Виртом (Niklaus Wirth) является создателем языка Oberon-2. Он частвовал в ряде проектов, проводимых в Швейцарском Федеральном технологическом институте (Swiss Federal Institute of Technology), был профессором в департаменте компьютерных наук (Computer Science Department) в ETH (Цюрих, Швейцария). Он автор известной книги Object-Oriented Programming in Oberon-2 (Springer-Verlag, 1993). В настоящее время работает профессором в ниверсите Йоганна Кеплера в Линце (Johannes Kepler University, Linz, Austria).
3 Краткая характеристика основных языков
Оберон-семейства
1. Оберон (Никлаус Вирт, 1988) - изящный компактный язык (меньше и проще Паскаля), идеален для преподавания основ информатики (computer science), концепций структурного, модульного и объектно-ориентированного программирования (ООП). Хорошо подходит для реализации малых и средних проектов. Имеет компиляторы внутри Oberon System и Juice. Блестящий кандидат на роль эсперанто программирования.
2. Oberon-2 (Ханспетер Мессенбок, Никлаус Вирт, 1991) - развитие Оберона в сторону привычного ООП, в связке с Modula-2 (как языка системного программирования) создает хорошую основу для реализации крупных проектов из макромира и микромира (встроенных систем, систем реального времени). Двуязыковая связка отлично реализована в системе XDS (Excelsior). Позволяет осуществлять кросс-разработку в Win32/Linux за счет трансляторов промышленного качества в Си и C++. Если нужно обобщение алгоритмов (задействование ООП), написание автономных программ, устойчивая работа с ОС на ровне системных вызовов, использование внешних библиотек на других языках, перенос на другие платформы через Cи/C++, эффективная реализация (оптимальный объектный код), то подходит Oberon-2 в исполнении XDS.
3. Компонентный Паскаль/Component Pascal (Клеменс Шиперски, Куно Пфистер, 1997) - развитие Оберона и Oberon-2 в сторону компонентно-ориентированного рограммирования (КОП). Хорошо проявляет себя для программирования в большом. Если требуется строить расширяемую систему с использованием КОП, подходов программной инженерии, иметь прямой выход на современные наработки для Win32,.NET и Java Platform, то нужен Component Pascal в реализациях BlackBox и GPCP. В реализации BlackBox обладает никальной особенностью динамического расширения систем ("на лету") за счет поддержки Оберон-компонентов, легко настраивается на решение задач любого ровня сложности (от преподавания информатики в школах до сложных исследовательских систем), имеет средство формирования COM-компонентов со сборкой мусора (Direct-To-COM Compiler), получившее на CeBIT приз за технологическое совершество.
4. Active Oberon (Юрг Гуткнехт, Патрик Реали, 2) - воплощение в Обероне идеи активных объектов (мультипроцессные системы для многопроцессорных конфигураций). Исследовательский проект, выполняемый группой проф. Гуткнехта (ETH, Цюрих). Базовый язык для реализации ОС Bluebottle Чдальнейшего развития системы Oberon. Поддерживает работу в Win32, на голой машине (PC), сосуществует с Java и.NET. Показал отличную эффективность в специфических областях (мультимедиа, повсеместный компьютинг).
5. Zonnon (Юрг Гуткнехт, Евгений Зуев, 2003) - ревизия Modula-2 и Оберона сквозь призму идей языка Mesa (Xerox PARC) и платформу Microsoft.NET. Исследовательский проект группы проф. Гуткнехта. Ориентирован в большей степени на особенности реализации новых языков и компиляторов для платформы.NET с последующей интеграцией в Microsoft Visual Studio.NET (2005). Представляет интерес в преподавании на старших курсах вузов, несколько сложен для начинающих программистов, но достаточно красиво решает проблемы "мирного сосуществования" модульного программирования, ООП и КОП. Представитель композиционного программирования.
4 Отличия от Паскаля
A. Синтаксис
1. Важен регистр букв в названии идентификаторов.
2. Зарезервированные идентификаторы языка всегда пишутся большими буквами.
3. браны скобки begin-end. Начало оператора определяется его именем, окончание - словом END. Для модулей и процедур требуется в конце повторять свое имя после слова END. Комментарии обозначаются связкой (* *).
B. Типы данных
1. браны перечисления, диапазоны. Тип SET определяет набор (множество) целых в диапазоне от 0 до MAX(SET). Введен процедурный тип (PROCEDURE) с операциями вызова и присваивания.
2. Индексы массивов нумеруются с нуля. Введены многомерные открытые массивы (ARRAY OF).
3. браны вариантные записи. Они заменяются расширяемыми комбинированными типами (см. E1).
C. Процедурное программирование
1. брано деление на процедуры и функции: процедуры могут возвращать значения через свое имя.
2. бран оператор FOR как источник ошибок.
3. Добавлен универсальный цикл LOOP-END с оператором выхода EXIT.
D. Модульное программирование
1. Исключено понятие программы (Паскаль), как и деление на программные и библиотечные модули (Modula-2). Есть единое понятие модуля - единицы компиляции и исполнения (загрузки). Он может экспортировать сущности: константы, типы, поля типов, переменные, процедуры. Вложенные (локальные) модули запрещены. Экспортируемые процедуры без параметров называются командами и определяют точки вызова модуля (программы).
2. Для экспорта используется значок * (звездочка) при определении сущности сразу после ее имени. Интерфейсный (описательный) модуль строится автоматически компилятором.
3. Импортируются только модули (IMPORT). Допустимо введение псевдонима модуля. Импортируемые идентификаторы в тексте обязательно квалифицируются именем соответствующего модуля.
E. Объектно-ориентированное программирование
1. Комбинированный тип (RECORD) может расширяться путем добавления полей записи (в том числе и процедурных типов). Это определяет механизм проекции u1090 типов, аналогичный наследованию в традиционном ООП.
2. Для работы с расширяемыми типами введена операция принадлежности типу (IS), также оператор WITH (привратник типа).
3. Язык предусматривает механизм автоматической сборки мусора.
F. Системное программирование
1. Введен псевдомодуль SYSTEM, который является интерфейсом между языком и низкоуровневыми средствами. Импортирование SYSTEM сигнализирует о привязке к конкретной операционной платформе.
2. Введены процедуры адресной арифметики (ADR, BIT, LSH, ROT, CC), преобразования типа (VAL), работы с памятью (GET, PUT, MOVE) и резервирования области памяти (NEW).
3. Введен тип BYTE. Формальный параметр вида ARRAY OF BYTE, передаваемый по ссылке, совместим с любым типом.
5 Различия между языками Oberon и Oberon-2
Язык Oberon-2 является чистым расширением языка Oberon [Wir88]. В этом работе представлены сделанные расширения языка и в ней предпринята попытка осветить причины этих изменений. Таким образом, мы надеемся простить читателю задачу классификации языка Oberon-2. За более детальной информацией о языке читателю нужно обратиться к описанию непосредственно языка.
Одной из важнейших целей языка Oberon-2 было стремление простить объектно-ориентировванное программирование без нарушения концептуальной простоты языка Oberon. После трех лет использования OberonТа и его экспериментального диалекта - языка Object Oberon [MeT89] - мы воплотили наш опыт в единую точненную версию OberonТа.
Новыми особенностями языка Oberon-2 служат типизированные процедуры (type-bound procedures), экспорт переменных и полей записи только на чтение (read-only export), открытые массивы в роли ссылочных базовых типов, также оператор WITH с вариантами. Помимо этого в язык был возвращен оператор FOR, который первоначально при переходе от Modula-2 к Oberon был изъят из языка.
Язык Oberon-2 является плодом многочисленных дискуссий, которые велись среди всех сотрудников Института компьютерных систем в ETH (Institute for Computer System at ETH). Особенно плодотворные идеи были высказаны Юргом Гуткнехтом (Juerg Gutknecht) и Йозефом Темплом (Josef Templ).
5.1 Типизированные процедуры
Процедуры могут быть связаны с типом запись или со ссылочным типом. Они эквивалентны методам в объектно-ориентированной терминологии. Присоединение процедуры выражается с помощью дополнительного параметра (играющего роль операнда, к которому применяется процедура, или же лполучателя, как это называется в объектно-ориентированной терминологии).
TYPE
Figure = POINTER TO FigureDesc;
FigureDesc = RECORD
x,y,w,h:INTEGER
END;
PROCEDURE(f:Figure)Draw;
BEGIN...
ENDDraw;
PROCEDURE (f: Figure) Move (dx,dy: INTEGER); BEGIN... END Move;
Draw и Move связаны с типом Figure, из чего следует, что обе они могут быть применимы к объектам типа Figure. Эти процедуры рассматриваются как локальные по отношению к FigureDesc и к ним можно обращаться как к обычным полям записи, например:
f.Move(10,10), если f - это переменная типа Figure.
Любая процедура, связанная с типом T, неявно также связана и со всеми расширениями типа T. Она может быть переопределена (перегружена) процедурой с тем же самым именем и точно таким же списком формальных параметров, как и та, что явным образом связана с расширением T. Взгляните на пример:
TYPE
Circle = POINTER TO CircleDesc;
CircleDesc = RECORD (FigureDesc)
radius: INTEGER
END;
PROCEDURE (c: Circle) Move (dx,dy: INTEGER); BEGIN... END Move;
Тип Circle является расширением типа Figure. Процедура Move явным образом связана с Circle и переопределяет процедуру Move, которая была лунаследована от Figure. Пусть f - это переменная типа Figure, c Ч переменная типа Circle, тогда присваивание f := c меняет у переменной f ее динамический тип (ее тип) с Figure на Circle. В вызове
f.Move(10,10)
переменная f выполняет две функции: во-первых, она передается в качестве параметра-получателя процедуре Move, во-вторых, ее динамический тип определяет то, какой вариант Move в действительности вызывается. Так как после присваивания f := c динамический тип переменной f уже Circle, то вызывается процедура Move, которая связана с типом Circle, не та, что связана с типом Figure. Этот механизм называется динамическим связыванием (dynamic binding), поскольку динамический тип получателя используется для связывания имени процедуры с реальной процедурой.
Внутри переопределяющей (redefining) процедуры переопределяемая (redefined) процедура может вызываться с использованием значка ^, например: f.Move^(dx,dy).
ргументация. Мы отказались от введения концепции класса, вместо этого заменили его на хорошо известную концепцию записи. Таким образом, класс - это просто тип запись со связанными с ним процедурами. Мы также отказались от дублирования в самой записи заголовков типизированных процедур, как это сделано в других языках типа C++ и Object Pascal. Это позволяет сохранить записи короткими и избежать избыточности описаний. Ведь изменение в заголовке тогда повлекло бы за собой корректировку в двух разных местах программы, к тому же компилятору пришлось бы еще и проверять идентичность обоих заголовков. Если программист захочет видеть конкретную запись со всеми связанными с ней процедурами, то он может для получения информации на экране или на листе бумаги воспользоваться специальным инструментом, который называется навигатор модулей (browser).
Процедуры, привязанные к типу, могут быть объявлены в произвольном порядке. Их можно даже перемежать процедурами, привязанными к другому типу. В языке Object Oberon, где все методы должны быть объявлены внутри соответствующего объявления класса, обнаружилось, что косвенная рекурсия между методами различных классов может поставить в затруднительное положение предварительные описания целых классов.
В языках типа Object Pascal и C++ переменные экземпляра (instance variable) для объекта-получателя self могут быть доступны как с квалификацией, так и без нее (например, можно написать и x, и лself.x). В этих языках подчас трудно определить, что перед вами - обычная переменная или же переменная экземпляра. Бывает еще сложнее, когда имя обозначает переменную экземпляра, которая наследована от базового класса. По этой причине мы решили, что переменные экземпляра в языке Oberon-2 должны всегда квалифицироваться. Таким образом, дается избежать выбора между двумя семантически эквивалентными конструкциями, что на наш взгляд нежелательно для языков программирования.
В языке Oberon-2 получатель представляет собой явный параметр, потому программист может выбирать для него характерное имя, которое обычно выглядит куда более выразительнее, чем предопределенное имя self, что используется в других объектно-ориентированных языках. Явное объявление получателя проясняет ту картину, что объект, к которому применяется данная операция, передается этой операции в качестве параметра. Это обычно нельзя выразить в других объектно-ориентированных языках. К тому же такой подход четко следует духу OberonТа - избегать любых скрытых механизмов.
В языке Object Oberon методы имеют в точности такой же синтаксис, как и обычные процедуры. В больших классах, где заголовка класса не видно рядом с заголовком метода, невозможно понять, является ли данная процедура обычной процедурой или же перед нами метод. В этом случае трудно также понять, к какому классу данный метод относится. В языке Oberon-2 тип параметра-получателя типизированной процедуры обозначает тип, с которым процедура связана, поэтому никаких сомнений не возникает.
5.2 Замороженный экспорт (READ-ONLY EXPORT)
В то время как в языке Oberon все экспортируемые переменные и поля записей могут быть изменены любым клиентным модулем, в языке Oberon-2 появилась возможность ограничивать использование экспортированной переменной или поля записи доступом только на чтение. Этот факт отмечается объявлением с использованием знака -, не *. Знак лминус говорит об ограниченном использовании данной переменной.
TYPE
Rec* = RECORD
f0* : INTEGER;
f1- : INTEGER;
f2 : INTEGER;
END;
VAR
a* : INTEGER;
b- : Rec;
c : INTEGER;
Клиентные модули могут читать переменные a и b, а также поля f0 и f1, поскольку все эти объекты экспортируются. Однако, изменять они могут лишь a и f0, в то время как значения b и f1 модифицировать не дастся. Только тот модуль, который экспортирует эти объекты, и может изменять их значения. (Даже если модуль-клиент объявляет приватную переменную типа Rec, ее поле f1 остается с доступом только на чтение.) Поскольку переменная b доступна только на чтение, то и все ее компоненты также доступны только на чтение.
Причины введения замороженного экспорта заключаются в желании обеспечить более тонкую настройку в плане инкапсуляции информации. Инкапсуляция преследует две цели: во-первых, она помогает избавить клиентов от ненужных деталей, и во-вторых, гарантировать, что значения скрытых переменных изменяются лишь процедурами доступа внутри содержащего их модуля. Замороженный экспорт как раз и преследует вторую цель.
5.3 Открытые массивы
И в языке Modula-2, и в языке Oberon можно использовать открытые массивы в качестве параметров. Длина такого массива определяется длиной фактического параметра процедуры. В языке Oberon-2 открытые массивы могут не только быть объявлены в качестве типа формального параметра, но и как ссылочные базовые типы (pointer base type). В этом случае для размещения в динамической памяти открытого массива с произвольной длиной используется предопределенная процедура NEW.
VAR v: POINTER TO ARRAY OF INTEGER;... NEW(v,100)
При этом массив v^ на этапе выполнения программы размещается в динамической памяти, причем длина его составляет 100 элементов: от v[0] до v[99].
5.4 Оператор WITH
В языке Oberon оператор WITH представляет собой локального охранника типа в виде:
WITH v: T DO S END;
Если динамический тип переменной v - это T, то выполняется последовательность операторов S, причем охранник типа v(T) применяется к каждому появлению переменной v, другими словами, переменная v рассматривается так, как если бы она имела статический тип T. Если динамический тип переменной v не T, то программа аварийно завершается. В языке Oberon-2 оператор WITH может быть записан с разными вариантами:
WITH
v: T0 DO S0 | v: T1 DO S1 ELSE S2 END;
Если динамический тип переменной v - это T0, то выполняется последовательность операторов S0, сама переменная v рассматривается так, как если бы она имела статический тип T0. Если динамический тип переменной v - это T1, то выполняется последовательность операторов S1, переменная v рассматривается так, как если бы она имела статический тип T1. В противном случае выполняется последовательность S2. Если ни один из вариантов не был выполнен и если ELSE-часть отстутствует, то программа аварийно завершается.
5.5 Оператор FOR
Хотя оператор FOR можно всегда выразить через оператор WHILE, все же иногда он более добен, поскольку имеет лаконичную форму и завершение цикла в нем четко выражено. Его добно использовать, когда требуется выполнить фиксированное число итераций подобно тому, как это часто нужно в случае работы с массивами. Оператор FOR записывается в виде:
FOR i := a TO b BY step DO statements END
Этот оператор эквивалентен следующей последовательности операторов:
temp := b; i := a; IF step > 0 THEH
WHILE i <= temp DO statements; i := i + step END ELSE
WHILE i >= temp DO statements; i := i + step END END;
6 Сравнение Оберона с другими языками
по сложности
Линия Вирта
Это линия языков, начинающаяся от Алгола-60 и образованная языками Паскаль, Модула-2,
Оберон и Оберон-2, автором которых является Никлаус Вирт, и, что самое главное, линия,
которой Вирт неуклонно придерживается: наращивание мощи языка без его сложнения.
Паскаль намного богаче Алгола, но не сложнее его. Модула существенно мощнее и совершеннее
Паскаля, но проще. Оберон обогатил Модулу средствами объектно-ориентированного
программирования - расширяемыми записями, и при этом не только не стал более сложным, но
заметно прощен.
Удивительным выглядит то, что Оберон-2 оказался проще Оберона, расширением которого
является. В отношении размера определения синтаксиса так оно и есть. Да и по существу
нововведения Оберона-2 оформлены очень экономно. Кроме того авторы языка объединили
отдельные правила для каждой разновидности операторов в одно правило для лексемы
"Оператор". То же сделано в отношении правил для типов. По-другому, более компактно,
определен синтаксис некоторых конструкций. И хотя получившееся прощение отчасти
формальное, но экономия понятий - это именно то, к чему и следует стремиться, как заметил
еще У.Оккам почти 700 лет назад.
Сокращения:
ТП - Турбо Паскаль
ОП - Объектный Паскаль (Делфи)
Приложение
Описание языка программирования Оберон-2
Примечание: все ссылки казывают на информацию внутри приложения.
The Programming Language Oberon-2H.Moessenboeck, N.WirthInstitut fur Computersysteme, ETH Zurich July 1996
Язык программирования Оберон-2
Х.Мёссенбёк, Н.Вирт
Институт компьютерных систем, ETH, Цюрих
Июль 1996
Перевод с английского С.Свердлова
1 Введение
Оберон-2 - язык программирования общего назначения, продолжающий традицию языков Паскаль и Modula-2. Его основные черты - блочная структура, модульность, раздельная компиляция, статическая типизация со строгим контролем соответствия типов (в том числе межмодульным), также расширение типов и связанные с типами процедуры. Расширение типов делает Оберон-2 объектно-ориентированным языком. Объект - это переменная абстрактного типа, содержащая данные (состояние объекта) и процедуры, которые оперируют этими данными. Абстрактные типы данных определены как расширяемые записи. Оберон-2 перекрывает большинство терминов объектно-ориентированных языков привычным словарем языков императивных, обходясь минимумом понятий в рамках тех же концепций. Этот документ не является учебником программирования. Он преднамеренно краток. Его назначение - служить справочником для программистов, разработчиков компиляторов и авторов руководств. Если о чем-то не сказано, то обычно сознательно: или потому, что zзык программирования Оберон-2 (перевод с английского) это следует из других правил языка, или потому, что потребовалось бы определять то, что фиксировать для общего случая представляется неразумным. В приложении A определены некоторые термины, которые используются при описании правил соответствия типов Оберона-2. В тексте эти термины выделены курсивом, чтобы подчеркнуть их специальное значение (например, одинаковый тип).
2 Синтаксис
Для описания синтаксиса Оберона-2 используются Расширенные Бэкуса-Наура Формы (РБНФ). Варианты разделяются знаком |. Квадратные скобки [ и ] означают необязательность записанного внутри них выражения, фигурные скобки { и } означают его повторение (возможно 0 раз). Нетерминальные символы начинаются с заглавной буквы (например, Оператор). Терминальные символы или начинаются малой буквой (например, идент), или записываются целиком заглавными буквами (например, BEGIN), или заключаются в кавычки (например, ":=").
3 Словарь и представление
Для представления терминальных символов предусматривается использование набора знаков ASCII. Слова языка - это идентификаторы, числа, строки, операции и разделители. Должны соблюдаться следующие лексические правила. Пробелы и концы строк не должны встречаться внутри слов (исключая комментарии и пробелы в символьных строках). Пробелы и концы строк игнорируются, если они не существенны для отделения двух последовательных слов. Заглавные и строчные буквы считаются различными.
1. Идентификаторы - последовательности букв и цифр. Первый символ должен быть буквой.
идент = буква {буква | цифра}.
Примеры: x Scan Oberon2 GetSymbol firstLetter
2. Числа - целые или вещественные (без знака) константы. Типом целочисленной константы считается минимальный тип, которому принадлежит ее значение (см. 6.1). Если константа заканчивается буквой H, она является шестнадцатеричной, иначе - десятичной.
Вещественное число всегда содержит десятичную точку. Оно может также содержать десятичный порядок. Буква E (или D) означает "умножить на десять в степени". Вещественное число относится к типу REAL кроме случая, когда у него есть порядок, содержащий букву D. В этом случае оно относится к типу LONGREAL.
число |
= |
целое | вещественное. |
целое |
= |
Цифра {цифра} | цифра {шестнЦифра} "H". |
вещественное |
= |
Цифра {цифра} "." {цифра} [Порядок]. |
Порядок |
= |
("E" | "D") ["+" | "-"] цифра {цифра}. |
шестнЦифра |
= |
Цифра | "A" | "B" | "C" | "D" | "E" | "F". |
цифра |
= |
"0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9". |
Примеры:
1991 |
INTEGER |
1991 |
0DH |
SHORTINT |
13 |
12.3 |
REAL |
12.3 |
4.567E8 |
REAL |
4567 |
0.57712566D-6 |
LONGREAL |
0.57712566 |
3. Символьные константы обозначаются порядковым номером символа в шестнадцатеричной записи, оканчивающейся буквой X.
символ = цифра {шестнЦифра} "X".
4. Строки - последовательности символов, заключенные в одиночные (') или двойные (") кавычки. Открывающая кавычка должна быть такой же, что и закрывающая и не должна встречаться внутри строки. Число символов в строке называется ее длиной. Строка длины 1 может использоваться везде, где допустима символьная константа и наоборот.
строка = ' " ' {символ} ' " ' | " ' " {символ} " ' ".
Примеры: "Oberon-2" "Don't worry!" "x"
5. Операции и разделители - это специальные символы, пары символов или зарезервированные слова, перечисленные ниже. Зарезервированные слова состоят исключительно из заглавных букв и не могут использоваться как идентификаторы.
+ |
:= |
IMPORT |
ARRAY |
RETURN |
|
- |
^ |
BEGIN |
IN |
THEN |
|
* |
= |
BY |
IS |
TO |
|
/ |
# |
CASE |
LOOP |
TYPE |
|
~ |
< |
CONST |
MOD |
UNTIL |
|
& |
> |
DIV |
MODULE |
VAR |
|
. |
<= |
DO |
NIL |
WHILE |
|
, |
>= |
ELSE |
OF |
WITH |
|
; |
.. |
ELSIF |
OR |
||
| |
: |
END |
POINTER |
||
( |
) |
EXIT |
PROCEDURE |
||
[ |
] |
FOR |
RECORD |
||
{ |
} |
IF |
REPEAT |
||
6. Комментарии могут быть вставлены между любыми двумя словами программы. Это произвольные последовательности символов, начинающиеся скобкой (* и оканчивающиеся *). Комментарии могут быть вложенными. Они не влияют на смысл программы.
4 Объявления и области действия
Каждый идентификатор, встречающийся в программе, должен быть объявлен, если это не стандартный идентификатор. Объявления задают некоторые постоянные свойства объекта, например, является ли он константой, типом, переменной или процедурой. После объявления идентификатор используется для ссылки на соответствующий объект.
Область действия объекта x распространяется текстуально от точки его объявления до конца блока (модуля, процедуры или записи), в котором находится объявление. Для этого блока объект является локальным. Это разделяет области действия одинаково именованных объектов, которые объявлены во вложенных блоках. Правила для областей действия таковы:
1 Идентификатор не может обозначать больше чем один объект внутри данной области действия (то есть один и тот же идентификатор не может быть объявлен в блоке дважды);
2 Ссылаться на объект можно только изнутри его области действия;
3 Тип T вида POINTER TO T1 (см. 6.4) может быть объявлен в точке, где T1 еще неизвестен. Объявление T1 должно следовать в том же блоке, в котором T является локальным;
4 Идентификаторы, обозначающие поля записи (см. 6.3) или процедуры, связанные с типом, (см. 10.2) могут потребляться только в обозначениях записи.
Идентификатор, объявленный в блоке модуля, может сопровождаться при своем объявлении экспортной меткой ("*" или "-"), чтобы казать, что он экспортируется. Идентификатор x, экспортируемый модулем M, может использоваться в других модулях, если они импортируют M (см. гл. 11). Тогда идентификатор обозначается в этих модулях М.x и называется точненным идентификатором. Переменные и поля записей, помеченные знаком "-" в их объявлении, предназначены только для чтения в модулях-импортерах.
УточнИдент |
= |
[идент "."] идент. |
ИдентОпр |
= |
идент ["*" | "-"]. |
Следующие идентификаторы являются стандартными; их значение определено в казанных разделах:
ABS |
(10.3) |
LEN |
(10.3) |
ASH |
(10.3) |
LONG |
(10.3) |
BOOLEAN |
(6.1) |
LONGINT |
(6.1) |
CAP |
(10.3) |
LONGREAL |
(6.1) |
CHAR |
(6.1) |
MAX |
(10.3) |
CHR |
(10.3) |
MIN |
(10.3) |
COPY |
(10.3) |
NEW |
(10.3) |
DEC |
(10.3) |
ODD |
(10.3) |
ENTIER |
(10.3) |
ORD |
(10.3) |
EXCL |
(10.3) |
REAL |
(6.1) |
FALSE |
(6.1) |
SET |
(6.1) |
HALT |
(10.3) |
SHORT |
(10.3) |
INC |
(10.3) |
SHORTINT |
(6.1) |
INCL |
(10.3) |
SIZE |
(10.3) |
INTEGER |
(6.1) |
TRUE |
(6.1) |
5 Объявления констант
Объявление константы связывает ее идентификатор с ее значением.
ОбъявлениеКонстанты |
= |
ИдентОпр "=" КонстантноеВыражение. |
КонстантноеВыражение |
= |
Выражение. |
Константное выражение - это выражение, которое может быть вычислено по его тексту без фактического выполнения программы. Его операнды - константы (Гл. 8) или стандартные функции (Гл. 10.3), которые могут быть вычислены во время компиляции. Примеры объявлений констант:
N = 100 limit = 2*N - 1 fullSet = {MIN(SET).. MAX(SET)}
6. Объявления типа
Тип данных определяет набор значений, которые могут принимать переменные этого типа, и набор применимых операций. Объявление типа связывает идентификатор с типом. В случае структурированных типов (массивы и записи) объявление также определяет структуру переменных этого типа. Структурированный тип не может содержать сам себя.
ОбъявлениеТипа |
= |
ИдентОпр "=" Тип. |
Тип |
= |
УточнИдент | ТипМассив | ТипЗапись | ТипУказатель | ПроцедурныйТип. |
Примеры:
Table = ARRAY N OF REAL
Tree = POINTER TO Node
Node = RECORD
key : INTEGER;
left, right: Tree
END
CenterTree = POINTER TO CenterNode
CenterNode = RECORD (Node)
width: INTEGER;
subnode: Tree
END
Function = PROCEDURE(x: INTEGER): INTEGER
6.1 Основные типы
Основные типы обозначаются стандартными идентификаторами. Соответствующие операции определены в 8.2, стандартные функции в 10.3. Предусмотрены следующие основные типы:
1. |
BOOLEAN |
логические значения TRUE и FALSE |
2. |
CHAR |
символы расширенного набора ASCII (0X .. 0FFX) |
3. |
SHORTINT |
целые в интервале от MIN(SHORTINT) до MAX(SHORTINT) |
4. |
INTEGER |
целые в интервале от MIN(INTEGER) до MAX(INTEGER) |
5. |
LONGINT |
целые в интервале от MIN(LONGINT) до MAX(LONGINT) |
6. |
REAL |
вещественные числа в интервале от MIN(REAL) до MAX(REAL) |
7. |
LONGREAL |
вещественные числа от MIN(LONGREAL) до MAX(LONGREAL) |
8. |
SET |
множество из целых от 0 до MAX(SET) |
Типы от 3 до 5 - целые типы, типы 6 и 7 - вещественные типы, вместе они называются числовыми типами. Эти типы образуют иерархию; больший тип поглощает меньший тип:
LONGREAL >= REAL >= LONGINT >= INTEGER >= SHORTINT
6.2 Тип массив
Массив - структура, состоящая из определенного количества элементов одинакового типа, называемого типом элементов. Число элементов массива называется его длиной. Элементы массива обозначаются индексами, которые являются целыми числами от 0 до длины массива минус 1.
ТипМассив |
= |
ARRAY [Длина {"," Длина}] OF Тип. |
Длина |
= |
КонстантноеВыражение. |
Тип вида
ARRAY L0, L1,..., Ln OF T
понимается как сокращение
ARRAY L0 OF
ARRAY L1 OF
...ARRAY Ln OF T
Массивы, объявленные без казания длины, называются открытыми массивами. Они могут использоваться только в качестве базового типа казателя (см. 6.4), типа элементов открытых массивов и типа формального параметра (см. 10.1). Примеры:
ARRAY 10, N OF INTEGER
ARRAY OF CHAR
6.3 Тип запись
Тип запись - структура, состоящая из фиксированного числа элементов, которые могут быть различных типов и называются полями. Объявление типа запись определяет имя и тип каждого поля. Область действия идентификаторов полей простирается от точки их объявления до конца объявления типа запись, но они также видимы внутри обозначений, ссылающихся на элементы переменных-записей (см. 8.1). Если тип запись экспортируется, то идентификаторы полей, которые должны быть видимы вне модуля, в котором объявлены, должны быть помечены. Они называются доступными полями; непомеченные элементы называются скрытыми полями.
ТипЗапись |
= |
RECORD ["(" БазовыйТип ")"] СписокПолей {";" СписокПолей} END. |
БазовыйТип |
= |
УточнИдент. |
СписокПолей |
= |
[СписокИдент ":" Тип]. |
Тип запись может быть объявлен как расширение другого типа запись. В примере
T0 = RECORD x: INTEGER END
T1 = RECORD (T0) y: REAL END
T1 - (непосредственное) расширение T0, T0 - (непосредственный) базовый тип T1 (см. Прил. A). Расширенный тип T1 состоит из полей своего базового типа и полей, которые объявлены в T1. Все идентификаторы, объявленные в расширенной записи, должны быть отличны от идентификаторов, объявленных в записи(записях) ее базового типа.
Примеры объявлений типа запись:
RECORD day, month, year: INTEGER END
RECORD name, firstname: ARRAY 32 OF CHAR;
age: INTEGER;
salary: REAL END
6.4 Тип казатель
Переменные-указатели типа P принимают в качестве значений казатели на переменные некоторого типа T. T называется базовым типом казателя типа P и должен быть типом массив или запись. Типы казатель заимствуют отношение расширения своих базовых типов: если тип T1 - расширение T и P1 - это тип POINTER TO T1, то P1 также является расширением P.
ТипУказатель = POINTER TO Тип.
Если p - переменная типа P = POINTER TO T, вызов стандартной процедуры NEW(p) (см. 10.3) размещает переменную типа T в свободной памяти. Если T - тип запись или тип массив с фиксированной длиной, размещение должно быть выполнено вызовом NEW(p); если тип T - n-мерный открытый массив, размещение должно быть выполнено вызовом NEW(p, e0,..., en-1), чтобы T был размещен с длинами, заданными выражениями e0,..., en-1. В любом случае казатель на размещенную переменную присваивается p. Переменная p имеет тип P. Переменная p^ (динамическая переменная), на которую ссылается p, имеет тип T.
Любая переменная-указатель может принимать значение NIL, которое не казывает ни на какую переменную вообще.
6.5 Процедурные типы
Переменные процедурного типа T, имеют значением процедуру (или NIL). Если процедура P присваивается переменной типа T, списки формальных параметров (см. Гл. 10.1) P и T должны совпадать (см. Прил. A). P не должна быть стандартной или связанной с типом процедурой, и не может быть локальной в другой процедуре.
ПроцедурныйТип = PROCEDURE [ФормальныеПараметры].
7. Объявления переменных
Объявления переменных дают описание переменных, определяя идентификатор и тип данных для них.
ОбъявлениеПеременных = СписокИдент ":" Тип.
Переменные типа запись и казатель имеют как статический тип (тип, с которым они объявлены - называемый просто их типом), так и динамический тип (тип их значения во время выполнения). Для казателей и параметров-переменных типа запись динамический тип может быть расширением их статического типа. Статический тип определяет какие поля записи доступны. Динамический тип используется для вызова связанных с типом процедур (см. 10.2).
Примеры объявлений переменных (со ссылками на примеры из Гл. 6):
i, j, k: INTEGER
x, y: REAL
p, q: BOOLEAN
s: SET
F: Function
a: ARRAY 100 OF REAL
w: ARRAY 16 OF RECORD
name:ARRAY 32 OF CHAR;
count: INTEGER END
t, c: Tree
8. Выражения
Выражения - конструкции, задающие правила вычисления по значениям констант и текущим значениям переменных других значений путем применения операций и процедур-функций. Выражения состоят из операндов и операций. Круглые скобки могут использоваться для группировки операций и операндов.
8.1 Операнды
За исключением конструкторов множества и литералов (чисел, символьных констант или строк), операнды представлены обозначениями. Обозначение содержит идентификатор константы, переменной или процедуры. Этот идентификатор может быть точнен именем модуля (см. Гл. 4 и 11) и может сопровождаться селекторами, если обозначенный объект - элемент структуры.
Обозначение |
= |
УточнИдент { "." идент | "[" СписокВыражений "]" | "^" | "(" точнИдент ")" }. |
СписокВыражений |
= |
Выражение {"," Выражение}. |
Если а - обозначение массива, a[e] означает элемент а, чей индекс - текущее значение выражения e. Тип e должен быть целым типом. Обозначение вида a[e0, e1,..., en] применимо вместо a[e0] [e1]... [en]. Если r обозначает запись, то r.f означает поле f записи r или процедуру f, связанную с динамическим типом r (Гл. 10.2). Если p обозначаета указатель, p^ означает переменную, на которую ссылается p. Обозначения p^.f и p^[e] могут быть сокращены до p.f и p[e], то есть запись и индекс массива подразумевают разыменование. Если a или r доступны только для чтения, то a[e] и r.f также предназначены только для чтения.
Охрана типа v(T) требует, чтобы динамическим типом v был T (или расширение T), то есть выполнение программы прерывается, если динамический тип v - не T (или расширение T). В пределах такого обозначения v воспринимается как имеющая статический тип T. Охрана применима, если
1 v - параметр-переменная типа запись, или v - казатель, и если
2 T - расширение статического типа v
Если обозначенный объект - константа или переменная, то обозначение ссылается на их текущее значение. Если он - процедура, то обозначение ссылается на эту процедуру, если только обозначение не сопровождается (возможно пустым) списком параметров. В последнем случае подразумевается активация процедуры и подстановка значения результата, полученного при ее исполнении. Фактические параметры должны соответствовать формальным параметрам как и при вызовах собственно процедуры (см. 10.1).
Примеры обозначений (со ссылками на примеры из Гл. 7):
I |
(INTEGER) |
a[i] |
(REAL) |
w[3].name[i] |
(CHAR) |
t.left.right |
(Tree) |
t(CenterTree).subnode |
(Tree) |
8.2 Операции
В выражениях синтаксически различаются четыре класса операций с разными приоритетами (порядком выполнения). Операция ~ имеет самый высокий приоритет, далее следуют операции типа множения, операции типа сложения и отношения. Операции одного приоритета выполняются слева направо. Например, x-y-z означает (x- y) -z.
Выражение |
= |
ПростоеВыражение [Отношение ПростоеВыражение]. |
|
ПростоеВыражение |
= |
["+" | "-"] Слагаемое {ОперацияСложения Слагаемое}. |
|
Слагаемое |
= |
Множитель {ОперацияУмножения Множитель}. |
|
Множитель |
= |
Обозначение [ФактическиеПараметры] | число | символ | строка | NIL | Множество | "(" Выражение ")" | "~" Множитель. |
|
Множество |
= |
"{" [Элемент {"," Элемент}] "}". |
|
Элемент |
= |
Выражение [".." Выражение]. |
|
ФактическиеПараметры |
= |
"(" [СписокВыражений] ")". |
|
Отношение |
= |
"=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS. |
|
ОперацияСложения |
= |
"+" | "-" | OR. |
|
ОперацияУмножения |
= |
"*" | "/" | DIV | MOD | "&". |
Предусмотренные операции перечислены в следующих таблицах. Некоторые операции применимы к операндам различных типов, обозначая разные действия. В этих случаях фактическая операция определяется типом операндов. Операнды должны быть совместимыми выражениями для данной операции (см. Прил. A).
8.2.1 Логические операции
OR |
логическая дизъюнкция |
p OR q |
"если p, то TRUE, иначе q" |
& |
логическая конъюнкция |
p & q |
"если p то q, иначе FALSE" |
~ |
отрицание |
~p |
"не p" |
Эти операции применимы к операндам типа BOOLEAN и дают результат типа BOOLEAN.
8.2.2 Арифметические операции
+ |
сумма - разность |
* |
произведение |
/ |
вещественное деление |
DIV |
деление нацело |
MOD |
остаток |
Операции +, -, *, и / применимы к операндам числовых типов. Тип их результата - тип того операнда, который поглощает тип другого операнда, кроме деления (/), чей результат - наименьший вещественный тип, который поглощает типы обоих операндов. При использовании в качестве одноместной операции "-" обозначает перемену знака, "+" - тождественную операцию. Операции DIV и MOD применимы только к целочисленным операндам. Они связаны следующими формулами, определенными для любого x и положительного делителя y:
x = (x DIV y) * y + (x MOD y) 0 < = (x MOD y) < y
Примеры:
x |
y |
x DIV y |
x MOD y |
5 |
3 |
1 |
2 |
-5 |
3 |
-2 |
1 |
8.2.3 Операции над множествами
+ объединение
- разность (x - y = x * (-y))
* пересечение
/ симметрическая разность множеств (x / y = (x-y) + (y-x))
Эти операции применимы к операндам типа SET и дают результат типа SET. Одноместный "минус" обозначает дополнение x, то есть -x это множество целых между 0 и MAX(SET), которые не являются элементами x. Операции с множествами не ассоциативны ((a+b)-c # a+(b-c)). Конструктор множества задает значение множества списком элементов, заключенным в фигурные скобки. Элементы должны быть целыми в диапазоне 0..MAX(SET). Диапазон a..b обозначает все целые числа в интервале [a, b].
8.2.4 Отношения
= |
равно |
# |
не равно |
< |
меньше |
<= |
меньшее или равно |
> |
больше |
>= |
больше или равно |
IN |
принадлежность множеству |
IS |
проверка типа |
Отношения дают результат типа BOOLEAN. Отношения =, #, <, <=, >, и >= применимы к числовым типам, типу CHAR, строкам и символьным массивам, содержащим в конце 0X. Отношения = и # кроме того применимы к типам BOOLEAN и SET, также к указателям и процедурным типам (включая значение NIL). x IN s означает "x является элементом s". x должен быть целого типа, s - типа SET. v IS T означает "динамический тип v есть T (или расширение T)" и называется проверкой типа. Проверка типа применима, если
1 v - параметр-переменная типа запись, или v - казатель, и если
2 T - расширение статического типа v
Примеры выражений (со ссылками на примеры из Гл. 7):
1991 |
INTEGER |
i DIV 3 |
INTEGER |
~p OR q |
BOOLEAN |
(i+j) * (i-j) |
INTEGER |
s - {8, 9, 13} |
SET |
i + x |
REAL |
a[i+j] * a[i-j] |
REAL |
(0<=i) & (i<100) |
BOOLEAN |
t.key = 0 |
BOOLEAN |
k IN {i..j-1} |
BOOLEAN |
w[i].name <= "John" |
BOOLEAN |
t IS CenterTree |
BOOLEAN |
9. Операторы
Операторы обозначают действия. Есть простые и структурные операторы. Простые операторы не содержат в себе никаких частей, которые являются самостоятельными операторами. Простые операторы - присваивание, вызов процедуры, операторы возврата и выхода. Структурные операторы состоят из частей, которые являются самостоятельными операторами. Они используются, чтобы выразить последовательное и словное, выборочное и повторное исполнение. Оператор также может быть пустым, в этом случае он не обозначает никакого действия. Пустой оператор добавлен, чтобы упростить правила пунктуации в последовательности операторов.
Оператор = [Присваивание | ВызовПроцедуры | ОператорIf | ОператорCase | ОператорWhile | ОператорRepeat | ОператорFor | ОператорLoop | ОператорWith | EXIT | RETURN [Выражение]].
9.1 Присваивания
Присваивание заменяет текущее значение переменной новым значением, определяемым выражением. Выражение должно быть совместимо по присваиванию с переменной (см. Приложение. A). Знаком операции присваивания является ":=", который читается "присвоить".
Присваивание = Обозначение ":=" Выражение.
Если выражение e типа Te присваивается переменной v типа Tv, имеет место следующее:
1 Если Tv и Te - записи, то в присваивании участвуют только те поля Te, которые также имеются в Тv (проецирование); динамический тип v и статический тип v должны быть одинаковы, и не изменяются присваиванием;
2 Если Tv и Te - типы казатель, динамическим типом v становится динамический тип e;
3 Если Тv это ARRAY n OF CHAR, e - строка длины m < n, v[i] присваиваются значения ei для i = 0.. m-1, v[m] получает значение 0X.
Примеры присваиваний (со ссылками на примеры из Гл. 7):
i := 0
p := i = j
x := i + 1
k := log2(i+j
F := log2 (* см. 10.1 *)
s := {2, 3, 5, 7, 11, 13}
a[i] := (x+y) * (x-y)
t.key := i w[i+1]
name := "John" t := c
9.2 Вызовы процедур
Вызов процедуры активирует процедуру. Он может содержать список фактических параметров, которые заменяют соответствующие формальные параметры, определенные в объявлении процедуры (см. Гл. 10). Соответствие станавливается в порядке следования параметров в списках фактических и формальных параметров. Имеются два вида параметров: параметры-переменные и параметры-значения. Если формальный параметр - параметр-переменная, соответствующий фактический параметр должен быть обозначением переменной. Если фактический параметр обозначает элемент структурной переменной, селекторы компонент вычисляются, когда происходит замена формальных параметров фактическими, то есть перед выполнением процедуры. Если формальный параметр - параметр-значение, соответствующий фактический параметр должен быть выражением. Это выражение вычисляется перед вызовом процедуры, полученное в результате значение присваивается формальному параметру (см. также 10.1).
ВызовПроцедуры = Обозначение [ФактическиеПараметры].
Примеры:
WriteInt(i*2+1) (* см. 10.1 *)
INC(w[k].count)
t.Insert("John") (* см. 11 *)
9.3 Последовательность операторов
Последовательность операторов, разделенных точкой с запятой, означает поочередное выполнение действий, заданных составляющими операторами.
ПоследовательностьОператоров = Оператор {";" Оператор}.
9.4 Операторы If
ОператорIf =
IF Выражение THEN ПоследовательностьОператоров
{ELSIF Выражение THEN ПоследовательностьОператоров}
[ELSE ПоследовательностьОператоров] END.
Операторы if задают условное выполнение входящих в них последовательностей операторов. Логическое выражение, предшествующие последовательности операторов, будем называть условием (в оригинале - guard. Прим. перев.) словия проверяются последовательно одно за другим, пока очередное не окажется равным TRUE, после чего выполняется связанная с этим словием последовательность операторов. Если ни одно словие не довлетворено, выполняется последовательность операторов, записанная после слова ELSE, если оно имеется.
Пример:
IF (ch >= "A") & (ch <= "Z") THEN ReadIdentifier
ELSIF (ch >= "0") & (ch <= "9") THEN ReadNumber
ELSIF (ch = " ' ") OR (ch = ' " ') THEN ReadString
ELSE SpecialCharacter END
9.5 Операторы Case
Операторы case определяют выбор и выполнение последовательности операторов по значению выражения. Сначала вычисляется выбирающее выражение, затем выполняется та последовательность операторов, чей список меток варианта содержит полученное значение. Выбирающее выражение должно быть такого целого типа, который поглощает типы всех меток вариантов, или и выбирающее выражение и метки вариантов должны иметь тип CHAR. Метки варианта - константы, и ни одно из их значений не должно потребляться больше одного раза. Если значение выражения не совпадает с меткой ни одного из вариантов, выбирается последовательность операторов после слова ELSE, если оно есть, иначе программа прерывается.
ОператорCase |
= |
CASE Выражение OF Вариант {" | " Вариант} [ELSE ПоследовательностьОператоров ] END. |
Вариант |
= |
[СписокМетокВарианта ":" ПоследовательностьОператоров]. |
СписокМетокВарианта |
= |
МеткиВарианта {"," МеткиВарианта }. |
МеткиВарианта |
= |
КонстантноеВыражение [".." КонстантноеВыражение]. |
Пример:
CASE ch OF
"A" .. "Z": ReadIdentifier
| "0".. "9": ReadNumber
| "'", '"' : ReadString
ELSE SpecialCharacter END
9.6 Операторы While
Операторы while задают повторное выполнение последовательности операторов, пока логическое выражение (условие) остается равным TRUE. словие проверяется перед каждым выполнением последовательности операторов.
ОператорWhile = WHILE Выражение DO ПоследовательностьОператоров END.
Примеры:
WHILE i > 0 DO i := i DIV 2; k := k + 1 END
WHILE (t # NIL) & (t.key # i) DO t := t.left END
9.7 Операторы Repeat
Оператор repeat определяет повторное выполнение последовательности операторов пока условие, заданное логическим выражением, не довлетворено. Последовательность операторов выполняется по крайней мере один раз.
ОператорRepeat = REPEAT ПоследовательностьОператоров UNTIL Выражение.
9.8 Операторы For
Оператор for определяет повторное выполнение последовательности операторов фиксированное число раз для прогрессии значений целочисленной переменной, называемой правляющей переменной оператора for.
ОператорFor |
= |
FOR идент ":=" Выражение TO Выражение [BY КонстантноеВыражение] DO ПоследовательностьОператоров END. |
Оператор
FOR v := beg TO end BY step DO statements END
эквивалентен
temp := end; v := beg;
IF step > 0 THEN
WHILE v <= temp DO statements; v := v + step END
ELSE
WHILE v >= temp DO statements; v := v + step END
END
temp и v имеют одинаковый тип. Шаг (step) должен быть отличным от нуля константным выражением. Если шаг не казан, он предполагается равным 1.
Примеры:
FOR i := 0 TO 79 DO k := k + a[i] END
FOR i := 79 TO 1 BY -1 DO a[i] := a[i-1] END
9.9 Операторы Loop
Оператор loop определяет повторное выполнение последовательности операторов. Он завершается после выполнения оператора выхода внутри этой последовательности (см. 9.10).
ОператорLoop = LOOP ПоследовательностьОператоров END.
Пример:
LOOP ReadInt(i); IF i < 0 THEN EXIT END; WriteInt(i) END
Операторы loop полезны, чтобы выразить повторения с несколькими точками выхода, или в случаях, когда условие выхода находится в середине повторяемой последовательности операторов.
9.10 Операторы возврата и выхода
Оператор возврата выполняет завершение процедуры. Он обозначается словом RETURN, за которым следует выражение, если процедура является процедурой-функцией. Тип выражения должен быть совместим по присваиванию (см. Приложение A) с типом результата, определенным в заголовке процедуры (см. Гл. 10). Процедуры-функции должны быть завершены оператором возврата, задающим значение результата. В собственно процедурах оператор возврата подразумевается в конце тела процедуры. Любой явный оператор появляется, следовательно, как дополнительная (вероятно, для исключительной ситуации) точка завершения. Оператор выхода обозначается словом EXIT. Он определяет завершение охватывающего оператора loop и продолжение выполнения программы с оператора, следующего за оператором loop. Оператор выхода связан с содержащим его оператором loop контекстуально, не синтаксически.
9.11 Операторы With
Операторы with выполняют последовательность операторов в зависимости от результата проверки типа и применяют охрану типа к каждому вхождению проверяемой переменной внутри этой последовательности операторов.
ОператорWith |
= |
WITH Охрана DO ПоследовательностьОператоров {"|" Охрана DO ПоследовательностьОператоров} [ELSE ПоследовательностьОператоров] END. |
Охрана |
= |
УточнИдент ":" точнИдент. |
Если v - параметр-переменная типа запись или переменная-указатель, и если ее статический тип T0, оператор
WITH v: T1 DO S1 | v: T2 DO S2 ELSE S3 END
имеет следующий смысл: если динамический тип v - T1, то выполняется последовательность операторов S1 в которой v воспринимается так, будто она имеет статический тип T1; иначе, если динамический тип v - T2, выполняется S2, где v воспринимается как имеющая статический тип T2; иначе выполняется S3. T1 и T2 должны быть расширениями T0. Если ни одна проверка типа не довлетворена, ELSE отсутствует, программа прерывается.
Пример:
WITH t: CenterTree DO i := t.width; c := t.subnode END
10. Объявления процедур
Объявление процедуры состоит из заголовка процедуры и тела процедуры. Заголовок определяет имя процедуры и формальные параметры. Для связанных с типом процедур в объявлении также определяется параметр-приемник. Тело содержит объявления и операторы. Имя процедуры повторяется в конце объявления процедуры. Имеются два вида процедур: собственно процедуры и процедуры- функции. Последние активизируются обозначением функции как часть выражения и возвращают результат, который является операндом выражения. Собственно процедуры активизируются вызовом процедуры. Процедура является процедурой-функцией, если ее формальные параметры задают тип результата. Тело процедуры-функции должно содержать оператор возврата, который определяет результат. Все константы, переменные, типы и процедуры, объявленные внутри тела процедуры, локальны в процедуре. Поскольку процедуры тоже могут быть объявлены как локальные объекты, объявления процедур могут быть вложенными. Вызов процедуры изнутри ее объявления подразумевает рекурсивную активацию. Объекты, объявленные в окружении процедуры, также видимы в тех частях процедуры, в которых они не перекрыты локально объявленным объектом с тем же самым именем.
ОбъявлениеПроцедуры |
= |
ЗаголовокПроцедуры ";" ТелоПроцедуры идент. |
|
ЗаголовокПроцедуры |
= |
PROCEDURE [Приемник] ИдентОпр [ФормальныеПараметры]. |
|
ТелоПроцедуры |
= |
ПоследовательностьОбъявлений [BEGIN ПоследовательностьОператоров] END. |
|
ПосОбъявлений |
= |
{CONST {ОбъявлениеКонстант ";"} | TYPE{ОбъявлениеТипов ";"} | VAR {ОбъявлениеПеременных ";"}} {ОбъявлениеПроцедуры ";" | ОпережающееОбъявление";"}. |
|
ОпережающееОбъявление |
= |
PROCEDURE"^" [Приемник] ИдентОпр [ФормальныеПараметры]. |
|
Если объявление процедуры содержит параметр-приемник, процедура рассматривается как связанная с типом (см. 10.2). Опережающее объявление служит чтобы разрешить ссылки на процедуру, чье фактическое объявление появляется в тексте позже. Списки формальных параметров опережающего объявления и фактического объявления должны быть идентичны.
10.1 Формальные параметры
Формальные параметры - идентификаторы, объявленные в списке формальных параметров процедуры. Им соответствуют фактические параметры, которые задаются при вызове процедуры. Подстановка фактических параметров вместо формальных происходит при вызове процедуры. Имеются два вида параметров: параметры-значения и параметры-переменные, обозначаемые в списке формальных параметров отсутствием или наличием ключевого слова VAR. Параметры-значения это локальные переменные, которым в качестве начального присваивается значение соответствующего фактического параметра. Параметры-переменные соответствуют фактическим параметрам, которые являются переменными, и означают эти переменные. Область действия формального параметра простирается от его объявления до конца блока процедуры, в котором он объявлен. Процедура-функция без параметров должна иметь пустой список параметров. Она должна вызываться обозначением функции, чей список фактических параметров также пуст. Тип результата процедуры не может быть ни записью, ни массивом.
ФормальныеПараметры |
= |
"(" [СекцияП {";" СекцияП }] ")" [":" точненныйИдент]. |
СекцияП |
= |
[VAR] идент {"," идент} ":" Тип. |
Пусть Tf - тип формального параметра f (не открытого массива) и Ta - тип соответствующего фактического параметра a. Для параметров-переменных Ta и Tf должны быть одинаковыми типами или Tf должен быть типом запись, Ta - расширением Tf. Для параметров-значений должен быть совместим по присваиванию с f (см. Прил. A). Если Tf - открытый массив, то a должен быть совместимым массивом для f (см. Прил. A). Длина f становится равной длине a.
Примеры объявлений процедур:
PROCEDURE ReadInt (VAR x: INTEGER);
VAR i: INTEGER; ch: CHAR;
BEGIN i := 0; Read(ch);
WHILE ("0" <= ch) & (ch <= "9") DO
i := 10*i + (ORD(ch)-ORD("0")); Read(ch)
END;
x := i
END ReadInt
PROCEDURE WriteInt (x: INTEGER); (*0 <= x <1*)
VAR i: INTEGER; buf: ARRAY 5 OF INTEGER;
BEGIN i := 0;
REPEAT buf[i] := x MOD 10; x := x DIV 10; INC(i) UNTIL x = 0;
REPEAT DEC(i); Write(CHR(buf[i] + ORD("0"))) UNTIL i = 0
END WriteInt
PROCEDURE WriteString (s: ARRAY OF CHAR);
VAR i: INTEGER;
BEGIN i := 0;
WHILE (i < LEN(s)) & (s[i] # 0X) DO Write(s[i]); INC(i) END
END WriteString;
PROCEDURE log2 (x: INTEGER): INTEGER;
VAR y: INTEGER; (*предполагается x>0*)
BEGIN
y := 0; WHILE x > 1 DO x := x DIV 2; INC(y) END;
RETURN y
END log2
10.2 Процедуры, связанные с типом
Глобально объявленные процедуры могут быть ассоциированы с типом запись, объявленным в том же самом модуле. В этом случае говорится, что процедуры связаны с типом запись. Связь выражается типом приемника в заголовке объявления процедуры. Приемник может быть или параметром-переменной типа Т, если Т - тип запись, или параметром-значением типа POINTER TO T (где T - тип запись). Процедура, связанная с типом T, рассматривается как локальная для него.
ЗаголовокПроцедуры |
= |
PROCEDURE [Приемник] ИдентОпр [ФормальныеПараметры]. |
Приемник |
= |
"(" [VAR] имя ":" имя ")". |
Если процедура P связана с типом T0, она неявно также связана с любым типом T1, который является расширением T0. Однако процедура P' (с тем же самым именем что и P) может быть явно связана с T1, перекрывая в этом случае связывание c P. P' рассматривается как переопределение P для T1. Формальные параметры P и P' должны совпадать (см. Прил. A). Если P и T1 экспортируются (см. Главу 4), P' также должна экспортироваться. Если v - обозначение, P - связанная процедура, то v.P обозначает процедуру P, связанную с динамическим типом v. Заметим, что это может быть процедура, отличная от той, что связана со статическим типом v. v передается приемнику процедуры P согласно правилам передачи параметров, определенным в Главе 10.1. Если r - параметр-приемник, объявленный с типом T, r.P^ обозначает (переопределенную) процедуру P, связанную с базовым для T типом. В опережающем объявлении связанной процедуры и в фактическом объявлении процедуры параметр-приемник должен иметь одинаковый тип. Списки формальных параметров в обоих объявлениях должны быть идентичны.
Примеры:
PROCEDURE (t: Tree) Insert (node: Tree);
VAR p, father: Tree;
BEGIN p := t;
REPEAT father := p;
IF node.key = p.key THEN RETURN END;
IF node.key < p.key THEN p := p.left
ELSE p := p.right END
UNTIL p = NIL;
IF node.key < father.key THEN father.left := node
ELSE father.right := node END;
node.left := NIL;
node.right := NIL
END Insert;
PROCEDURE (t: CenterTree) Insert (node: Tree); (*переопределение*)
BEGIN
WriteInt(node(CenterTree).width);
t.Insert^ (node) (* вызывает процедуру Insert, связанную с Tree *)
END Insert;
10.3 Стандартные процедуры
Следующая таблица содержит список стандартных процедур. Некоторые процедуры - обобщенные, то есть они применимы к операндам нескольких типов. Буква v обозначает переменную, x и n - выражения, T - тип.
Процедуры-функции
Название |
Тип аргумента |
Тип результата |
Функция |
ABS(x) |
числовой тип |
совпадает с типом x |
бсолютное значение |
ASH(x, n) |
x, n: целый тип |
LONGINT |
рифметический сдвиг (x*2n) |
CAP(x) |
CHAR |
CHAR |
x - буква: соответствующая заглавная буква |
CHR(x) |
целый тип |
CHAR |
символ с порядковым номером x |
ENTIER(x) |
вещественный тип |
LONGINT |
наибольшее целое, не превосходящее x |
LEN(v, n) |
v: массив; n: целая константа |
LONGINT |
длина v в измерении n (первое измерение = 0) |
LEN(v) |
v: массив |
LONGINT |
равносильно LEN(v, 0) |
LONG(x) |
SHORTINTINTEGERREAL |
INTEGERLONGINTLONGREAL |
тождество |
MAX(T) |
T = основной типT = SET |
T INTEGER |
наибольшее значение типа Tнаибольший элемент множества |
MIN(T) |
T = основной типT = SET |
TINTEGER |
наименьшее значение типа T0 |
ODD(x) |
целый тип |
BOOLEAN |
x MOD 2 = 1 |
ORD(x) |
CHAR |
INTEGER |
порядковый номер x |
SHORT(x) |
LONGINTINTEGERLONGREAL |
INTEGERSHORTINTREAL |
тождествотождествотождество (возможно сечение) |
SIZE(T) |
любой тип |
целый тип |
число байт, занимаемых T |
Собственно процедуры
Название |
Типы аргументов |
Функция |
ASSERT(x) |
x: логическое выражение |
прерывает выполнение программы, если не x |
ASSERT(x, n) |
x: логическое выражение; n:целая константа |
прерывает выполнение программы, если не x |
COPY(x, v) |
x: символьный массив, строка; v: символьный массив |
v := x |
DEC(v) |
целый тип |
v := v - 1 |
DEC(v, n) |
v, n: целый тип |
v := v - n |
Продолжение таблицы |
||
Название |
Типы аргументов |
Функция |
EXCL(v, x) |
v: SET; x: целый тип |
v := v - {x} |
HALT(n) |
целая константа |
прерывает выполнение программы |
INC(v) |
целый тип |
v := v + 1 |
INC(v, n) |
v, n: целый тип |
v := v + n |
INCL(v, x) |
v: SET; x: целый тип |
v := v + {x} |
NEW(v) |
указатель на запись или массив фиксированной длины |
размещает v ^ |
NEW(v, x0,..., xn) |
v: казатель на открытый массив; xi: целый тип |
размещает v^ с длинами x0.. xn |
COPY разрешает присваивание строки или символьного массива, содержащего ограничитель 0X, другому символьному массиву. В случае необходимости, присвоенное значение секается до длины получателя минус один. Получатель всегда будет содержать 0X как ограничитель. В ASSERT(x, n) и HALT(n), интерпретация n зависит от реализации основной системы.
11. Модули
Модуль - совокупность объявлений констант, типов, переменных и процедур вместе с последовательностью операторов, предназначенных для присваивания начальных значений переменным. Модуль представляет собой текст, который является единицей компиляции.
Модуль |
= |
MODULE идент ";" [СписокИмпорта] ПоследовательностьОбъявлений [BEGIN ПоследовательностьОператоров] END идент ".". |
СписокИмпорта |
= |
IMPORT Импорт {"," Импорт} ";". |
Импорт |
= |
[идент ":="] идент. |
Список импорта определяет имена импортируемых модулей. Если модуль A импортируется модулем M, и A экспортирует идентификатор x, то x поминается внутри M как A.x. Если A импортируется как B:=A, объект x должен вызываться как B.x. Это позволяет использовать короткие имена-псевдонимы в точненных идентификаторах. Модуль не должен импортировать себя. Идентификаторы, которые экспортируются (то есть должны быть видимы в модулях-импортерах) нужно отметить экспортной меткой в их объявлении (см. Главу 4). Последовательность операторов после символа BEGIN выполняется, когда модуль добавляется к системе (загружается). Это происходит после загрузки импортируемых модулей. Отсюда следует, тот циклический импорт модулей запрещен. Отдельные (не имеющие параметров и экспортированные) процедуры могут быть активированы из системы. Эти процедуры служат командами (см. Приложение D1).
MODULE Trees; (* экспорт: Tree, Node, Insert, Search, Write, Init *)
IMPORT Texts, Oberon; (* экспорт только для чтения: Node.name *)
TYPE Tree* = POINTER TO Node;
Node* = RECORD
name-: POINTER TO ARRAY OF CHAR;
left, right: Tree
END;
VAR w: Texts.Writer;
PROCEDURE (t: Tree) Insert* (name: ARRAY OF CHAR);
VAR p, father: Tree;
BEGIN p := t;
REPEAT father := p;
IF name = p.name^ THEN RETURN END;
IF name < p.name^ THEN p := p.left
ELSE p := p.right END
UNTIL p = NIL;
NEW(p); p.left := NIL; p.right := NIL;
NEW(p.name, LEN(name)+1); COPY(name, p.name^);
IF name < father.name^ THEN father.left := p
ELSE father.right := p END
END Insert;
PROCEDURE (t: Tree) Search* (name: ARRAY OF CHAR): Tree;
VAR p: Tree;
BEGIN p := t;
WHILE (p # NIL) & (name # p.name^) DO
IF name < p.name^ THEN p := p.left
ELSE p := p.right END
END;
RETURN p
END Search;
PROCEDURE (t: Tree) Write*;
BEGIN
IF t.left # NIL THEN t.left.Write END;
Texts.WriteString(w, t.name^);
Texts.WriteLn(w);
Texts.Append(Oberon.Log, w.buf);
IF t.right # NIL THEN t.right.Write END
END Write;
PROCEDURE Init* (t: Tree);
BEGIN NEW(t.name, 1); t.name[0] := 0X; t.left := NIL; t.right := NIL
END Init;
BEGIN Texts.OpenWriter(w)
END Trees.
Приложение A: Определение терминов
Целые типы |
SHORTINT, INTEGER, LONGINT |
Вещественные типы |
REAL, LONGREAL |
Числовые типы |
Целые типы, вещественные типы |
Одинаковые типы
Две переменные a и b с типами Ta и Tb имеют одинаковый тип, если
1 Ta и Tb оба обозначены одним и тем же идентификатором типа, или
2 Ta объявлен равным Tb в объявлении типа вида Ta = Tb, или
3 a и b появляются в одном и том же списке идентификаторов переменных, полей записи или объявлении формальных параметров и не являются открытыми массивами.
Равные типы
Два типа Ta, и Tb равны, если
1 Ta и Tb - одинаковые типы, или
2 Ta и Tb - типы открытый массив с равными типами элементов, или
3 Ta и Tb - процедурные типы, чьи списки формальных параметров совпадают.
Поглощение типов
Числовые типы поглощают (значения) меньших числовых типов согласно следующей иерархии: LONGREAL >= REAL >= LONGINT >= INTEGER >= SHORTINT
Расширение типов (базовый тип)
В объявлении типа Tb = RECORD (Ta)... END, Tb - непосредственное расширение Ta, Ta - непосредственный базовый тип Tb. Тип Tb есть расширение типа Ta (Ta есть базовый тип Tb) если
1 Ta и Tb - одинаковые типы, или
2 Tb - непосредственное расширение типа, являющегося расширением Ta
Если Pa = POINTER TO Ta и Pb = POINTER TO Tb, то Pb есть расширение Pa (Pa есть базовый тип Pb), если Tb есть расширение Ta.
Совместимость по присваиванию
Выражение e типа Te совместимо по присваиванию с переменной v типа Tv, если выполнено одно из следующих словий:
1 Te и Tv - одинаковые типы;
2 Te и Tv - числовые типы и Tv поглощает Te;
3 Te и Tv - типы запись, Te есть расширение Tv, v имеет динамический тип Tv;
4 Te и Tv - типы казатель и Te - расширение Tv;
5 Tv - тип казатель или процедурный тип, e - NIL;
6 Tv - ARRAY n OF CHAR, e - строковая константа из m символов и m < n;
7 Tv - процедурный тип, e - имя процедуры, чьи формальные параметры совпадают с параметрами Tv.
Совместимость массивов
Фактический параметр a типа Ta является совместимым массивом для формального параметра f типа Tf если
1 Tf и Ta - одинаковые типы или
2 Tf - открытый массив, Ta - любой массив, типы их элементов - совместимые массивы или
3 f - параметр-значение типа ARRAY OF CHAR, фактический параметр a - строка.
Совместимость выражений
Для данной операции операнды являются совместимыми выражениями, если их типы соответствуют следующей таблице (в который казан также тип результата выражения). Символьные массивы, которые сравниваются, должны содержать в качестве ограничителя 0X. Тип T1 должен быть расширением типа T0:
операция |
первый операнд |
второй операнд |
тип результата |
+ - * |
числовой |
числовой |
наименьший числовой тип, поглощающий оба операнда |
/ |
числовой |
числовой |
наименьший вещественный тип, поглощающий оба операнда |
+ - * / |
SET |
SET |
SET |
DIV MOD |
целый |
целый |
наименьший целый тип, поглощающий оба операнда |
OR & ~ |
BOOLEAN |
BOOLEAN |
BOOLEAN |
= # < <= > >= |
числовойCHAR символьный массив, строка |
числовойCHAR символьный массив, строка |
BOOLEANBOOLEANBOOLEAN |
= # |
BOOLEANSET NIL, тип казатель T0 или T1процедурный тип T, NIL |
BOOLEANSET NIL, тип казатель T0 или T1процедурный тип T, NIL |
BOOLEANBOOLEANBOOLEANBOOLEAN |
IN |
целый |
SET |
BOOLEAN |
IS |
тип T0 |
тип T1 |
BOOLEAN |
Совпадение списков формальных параметров
Два списка формальных параметров совпадают если
1 они имеют одинаковое количество параметров, и
2 они имеют или одинаковый тип результата функции или не имеют никакого, и
3 параметры в соответствующих позициях имеют равные типы, и
4 параметры в соответствующих позициях - оба или параметры-значения или параметры-переменные.
Приложение B: Синтаксис Оберона-2
Модуль |
= |
MODULE идент ";" [СписокИмпорта] ПосОбъявл [BEGIN ПосОператоров] END идент ".". |
|
СписокИмпорта |
= |
IMPORT [идент ":="] идент {"," [идент ":="] идент} ";". |
|
ПосОбъявл |
= |
{ CONST {ОбъявКонст ";" } | TYPE {ОбъявТипа ";" } | VAR {ОбъявПерем ";" }} {ОбъявПроц ";" | ОпережающееОбъяв";"}. |
|
ОбъявКонст |
= |
ИдентОпр "=" КонстВыраж. |
|
ОбъявТипа |
= |
ИдентОпр "=" Тип. |
|
ОбъявПерем |
= |
СписокИдент ":" Тип. |
|
ОбъявПроц |
= |
PROCEDURE [Приемник] ИдентОпр [ФормальныеПарам]";" ПосОбъявл [BEGIN ПосОператоров] END идент. |
|
Опережающее Объявление |
= |
PROCEDURE "^" [Приемник] ИдентОпр [ФормальныеПарам]. |
|
ФормальныеПарам |
= |
"(" [СекцияП {";" СекцияП}] ")" [":" точнИдент]. |
|
СекцияП |
= |
[VAR] идент {"," идент} ":" Тип. |
|
Приемник |
= |
"(" [VAR] идент ":" идент ")". |
|
Тип |
= |
УточнИдент | ARRAY [КонстВыраж {"," КонстВыраж}] OF Тип | RECORD ["("УточнИдент")"] СписокПолей {";" СписокПолей} END | POINTER TO Тип | PROCEDURE [ФормальныеПарам]. |
|
СписокПолей |
= |
[СписокИдент ":" Тип]. |
|
ПосОператоров |
= |
Оператор {";" Оператор}. |
|
Оператор |
= |
[ Обозначение ":=" Выраж | Обозначение ["(" [СписокВыраж] ")"] | IF Выраж THEN ПосОператоров {ELSIF Выраж THEN ПосОператоров} [ELSE ПосОператоров] END | CASE Выраж OF Вариант {"|" Вариант} [ELSE ПосОператоров] END | WHILE Выраж DO ПосОператоров END | REPEAT ПосОператоров UNTIL Выраж | FOR идент ":=" Выраж TO Выраж [BY КонстВыраж] DO ПосОператоров END | LOOP ПосОператоров END | WITH Охрана DO ПосОператоров {"|" Охрана DO ПосОператоров} [ELSE ПосОператоров] END | EXIT | RETURN [Выраж] ]. |
|
Вариант |
= |
[МеткиВарианта {"," МеткиВарианта} ":" ПосОператоров]. |
|
МеткиВарианта |
= |
КонстВыраж [".." КонстВыраж]. |
|
Охрана |
= |
УточнИдент ":" точнИдент. |
|
КонстВыраж |
= |
Выраж. |
|
Выраж |
= |
ПростоеВыраж [Отношение ПростоеВыраж]. |
|
ПростоеВыраж |
= |
["+" | "-"] Слагаемое {ОперСлож Слагаемое}. |
|
Слагаемое |
= |
Множитель {ОперУмн Множитель}. |
|
Множитель |
= |
Обозначение ["(" [СписокВыраж] ")"] | число | символ | строка | NIL | Множество | "(" Выраж ")" | " ~ " Множитель. |
|
Множество |
= |
"{" [Элемент {"," Элемент}] "}". |
|
Элемент |
= |
Выраж [".." Выраж]. |
|
Отношение |
= |
"=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS. |
|
ОперСлож |
= |
"+" | "-" | OR. |
|
ОперУмн |
= |
"*" | "/" | DIV | MOD | "&". |
|
Обозначение |
= |
УточнИдент {"." идент | "[" СписокВыраж "]" | "^" | "(" точнИдент ")"}. |
|
СписокВыраж |
= |
Выраж {"," Выраж}. СписокИдент = ИдентОпр {"," ИдентОпр}. |
|
УточнИдент |
= |
[идент "."] идент. ИдентОпр = идент [ "*" | "-" ]. |
|
Приложение C: Модуль SYSTEM
Модуль SYSTEM содержит некоторые типы и процедуры, которые необходимы для реализации операций низкого ровня, специфичных для данного компьютера и/или реализации. Они включают, например, средства для доступа к стройствам, которые управляются компьютером, и средства, позволяющие обойти правила совместимости типов, наложенные определением языка. Настоятельно рекомендуется ограничить использование этих средств специфическими модулями (модулями низкого ровня). Такие модули непременно являются непереносимыми, но легко распознаются по идентификатору SYSTEM, появляющемуся в их списке импорта. Следующие спецификации действительны для реализации Оберон-2 на компьютере Ceres. Модуль SYSTEM экспортирует тип BYTE со следующими характеристиками: переменным типа BYTE можно присваивать значения переменных типа CHAR или SHORTINT. Если формальный параметр-переменная имеет тип ARRAY OF BYTE, то соответствующий фактический параметр может иметь любой тип. Другой тип, экспортируемый модулем SYSTEM, - тип PTR. Переменным типа PTR могут быть присвоены значения переменных-указателей любого типа. Если формальный параметр-переменная имеет тип PTR, фактический параметр может быть казателем любого типа. Процедуры, содержащиеся в модуле SYSTEM, перечислены в таблицах. Большинство их соответствует одиночным командам и компилируются непосредственно в машинный код. О деталях читатель может справиться в описании процессора. В таблице v обозначает переменную, x, y, a, и n - выражения, T - тип.
Процедуры-функции
Название |
Типы аргументов |
Тип результата |
Функция |
ADR(v) |
любой |
LONGINT |
дрес переменной v |
BIT(a,n) |
a: LONGINT; n:целый |
BOOLEAN |
n-й бит Память[a] |
CC(n) |
целая константа |
BOOLEAN |
условие n (0 <= n <= 15) |
LSH(x,n) |
x: целый, CHAR, BYTE; n: целый |
совпадает с типом x |
логический сдвиг |
ROT(x,n) |
x: целый, CHAR, BYTE; n: целый |
совпадает с типом x |
циклический сдвиг |
VAL(T,x) |
T, x: любого типа |
T |
x интерпретируется как значение типа T |
Собственно процедуры
Название |
Типы аргументов |
Функция |
GET(a, v) |
a: LONGINT; v: любой основной тип, казатель, процедурный тип |
v := Память[a] |
PUT(a, x) |
a: LONGINT; x: любой основной тип, казатель, процедурный тип |
Память[a] := x |
GETREG(n, v) |
n: целая константа; v: любой основной тип, казатель, процедурный тип |
v := Регистр n |
PUTREG(n, x) |
n: целая константа; x: любой основной тип, казатель, процедурный тип |
Регистр n := x |
MOVE(a0,a1,n) |
a0, a1: LONGINT; n: целый |
Память[a1..a1+n-1] := Память[a0..a0+n-1] |
NEW(v, n) |
v: любой казатель; n: целый |
размещает блок памяти размером n байт; присваивает его адрес переменной v |
Приложение D: Среда Оберон
Программы на Обероне-2 обычно выполняются в среде, которая обеспечивает активацию команд, сбор мусора, динамическую загрузку модулей и определенные структуры данных времени выполнения. Не являясь частью языка, эта среда способствует увеличению мощности Оберона-2 и до некоторой степени подразумевается при определении языка. В приложении D описаны существенные особенности типичной Оберон-среды и даны советы по peализации. Подробности можно найти в [1], [2], и [3].
D1. Команды
Команда - это любая процедура P, которая экспортируется модулем M и не имеет параметров. Она обозначается M.P и может быть активирована под таким именем из оболочки операционной системы. В Обероне пользователь вызывает команды вместо программ или модулей. Это дает лучшую структуру правления и предоставляет модули с несколькими точками входа. Когда вызывается команда M.P, модуль M динамически загружается, если он же не был в памяти (см. D2) и выполняется процедура P. Когда P завершается, M остается загруженным. Все глобальные переменные и структуры данных, которые могут быть достигнуты через глобальные переменные-указатели в M, сохраняют значения. Когда P (или другая команда M) вызывается снова, она может продолжать использовать эти значения. Следующий модуль демонстрирует использование команд. Он реализует абстрактную структуру данных Counter, которая содержит переменную-счетчик и обеспечивает команды для величения и печати его значения.
MODULE Counter;
IMPORT Texts, Oberon;
VAR counter: LONGINT; w: Texts.Writer;
PROCEDURE Add*; (* получает числовой аргумент из командной строки *)
VAR s: Texts.Scanner;
BEGIN Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos);
Texts.Scan(s);
IF s.class = Texts.Int THEN INC(counter, s.i) END
END Add;
PROCEDURE Write*;
BEGIN Texts.WriteInt(w, counter, 5); Texts.WriteLn(w);
Texts.Append(Oberon.Log, w.buf)
END Write;
BEGIN counter := 0; Texts.OpenWriter(w) END Counter.
Пользователь может выполнить следующие две команды:
Counter.Add n Добавляет значение n к переменной counter Counter.Write Выводит текущее значение counter на экран
Так как команды не содержат параметров, они должны получать свои аргументы из операционной системы. Вообще команды вольны брать параметры отовсюду (например из текста после команды, из текущего выбранного фрагмента или из отмеченного окна просмотра). Команда Add использует сканер (тип данных, обеспечиваемый Оберон-системой) чтобы читать значение, которое следует за нею в командной строке. Когда Counter.Add вызывается впервые, модуль Counter загружается и выполняется его тело. Каждое обращение Counter.Add n величивает переменную counter на n. Каждое обращение Counter.Write выводит текущее значение counter на экран. Поскольку модуль остается загруженным после выполнения его команд, должен существовать явный способ выгрузить его (например, когда пользователь хочет заменить загруженную версию перекомпилированной версией). Оберон-система содержит команду, позволяющую это сделать.
D2. Динамическая загрузка модулей
Загруженный модуль может вызывать команду незагруженного модуля, задавая ее имя как строку. Специфицированный модуль при этом динамически загружается и выполняется заданная команда. Динамическая загрузка позволяет пользователю запустить программу как небольшой набор базисных модулей и расширять ее, добавляя последующие модули во время выполнения по мере необходимости. Модуль M0 может вызвать динамическую загрузку модуля M1 без того, чтобы импортировать его. M1 может, конечно, импортировать и использовать M0, но M0 не должен знать о существовании M1. M1 может быть модулем, который спроектирован и реализован намного позже M0.
D3. Сбор мусора
В Обероне-2 стандартная процедура NEW используется, чтобы распределить блоки данных в свободной памяти. Нет, однако, никакого способа явно освободить распределенный блок. Взамен Оберон-среда использует сборщик мусора чтобы найти блоки, которые больше не используются и сделать их снова доступными для распределения. Блок считается используемым только если он может быть достигнут через глобальную переменную-указатель по цепочке казателей. Разрыв этой цепочки (например, становкой казателя в NIL) делает блок тилизируемым. Сборщик мусора освобождает программиста от нетривиальной задачи правильного освобождения структур данных и таким образом помогает избегать ошибок. Возникает, однако, необходимость иметь информацию о динамических данных во время выполнения (см. D5).
D4. Смотритель
Интерфейс модуля (объявления экспортируемых объектов) извлекается из модуля так называемым смотрителем, который является отдельным инструментом среды Оберон. Например, смотритель производит следующий интерфейс модуля Trees из Гл. 11.
DEFINITION Trees;
TYPE
Tree = POINTER TO Node;
Node = RECORD
name: POINTER TO ARRAY OF CHAR;
PROCEDURE (t: Tree) Insert (name: ARRAY OF CHAR);
PROCEDURE (t: Tree) Search (name: ARRAY OF CHAR): Tree;
PROCEDURE (t: Tree) Write;
END;
PROCEDURE Init (VAR t: Tree);
END Trees.
Для типа запись смотритель также собирает все процедуры, связанные с этим типом, и показывает их заголовки в объявлении типа запись.
D5. Структуры данных времени выполнения
Некоторая информация о записях должна быть доступна во время выполнения: Динамический тип записей необходим для проверки и охраны типа. Таблица с адресами процедур, связанных с записью, необходима для их вызова. Наконец, сборщик мусора нуждается в информации о расположении казателей в динамически распределенных записях. Вся эта информация сохраняется в так называемых дескрипторах типа. Один дескриптор необходим во время выполнения для каждого типа записи. Ниже показана возможная реализация дескрипторов типа. Динамический тип записи соответствует адресу дескриптора типа. Для динамически распределенных записей этот адрес сохраняется в так называемом теге типа, который предшествует фактическим данным записи и является невидимым для программиста. Если t - переменная типа CenterTree (см. пример в Гл. 6), рисунок D5.1 показывает одну из возможных реализаций структур данных времени выполнения.
Рис. D5.1 переменная t типа CenterTree, запись t^, на которую она казывает, и дескриптор типа
Поскольку и таблица адресов процедур и таблица смещений казателей должны иметь фиксированное смещение относительно адреса дескриптора типа, и поскольку они могут расти, когда тип расширяется и добавляются новые процедуры и казатели, то таблицы размещены в противоположных концах дескриптора типа и растут в разных направлениях. Связанная с типом процедура t.P вызывается как t^.tag^.ProcTab[IndexP]. Индекс таблицы процедур для каждой связанной с типом процедуры известен во время компиляции. Проверка типа v IS T транслируется в v^.tag^.BaseTypes [ExtensionLevelT] = TypeDescrAdrT. И ровень расширения типа запись (ExtensionLevelT), и адрес описателя типа (TypeDescrAdrT) известны во время компиляции. Например, ровень расширения Node - 0 (этот тип не имеет базового типа), а ровень расширения CenterNode равен 1.
Пример программы
(* Print first 'PRINT' digits of 'e'.
*
* Originally written in Pascal by Scott Hemphill
* Rewritten in Oberon-2 and modified by Andrew Cadach
*
*)
<*- CHECKINDEX *>
<*+ MAIN *>
MODULE exp;
IMPORT InOut;
CONST
PRINT = 1024;
DIGITS = PRINT + (PRINT+31) DIV 32;
TYPE Number = ARRAY DIGITS+1 OF INTEGER;
VAR
s, x: POINTER TO Number;
xs, i: INTEGER;
PROCEDURE init (VAR x: Number; n: INTEGER);
VAR i: INTEGER;
BEGIN
x[0] := n;
FOR i := 1 TO DIGITS DO x[i] := 0; END
END init;
PROCEDURE divide (VAR x: Number; xs, n: INTEGER;
VAR y: Number; VAR ys: INTEGER);
VAR
i, c: INTEGER;
BEGIN
c := 0;
FOR i := xs TO DIGITS DO
c := 10*c + x[i];
y[i] := c DIV n;
c := c MOD n
END;
ys := xs;
WHILE (ys <= DIGITS) & (y[ys] = 0) DO INC (ys) END
END divide;
PROCEDURE add (VAR s, x: Number; xs: INTEGER);
VAR
i, c: INTEGER;
BEGIN
c := 0;
FOR i := DIGITS TO xs BY -1 DO
INC (c, s[i] + x[i]);
IF c >= 10 THEN
s[i] := c - 10;
c := 1
ELSE
s[i] := c;
c := 0
END
END;
i := xs;
WHILE c # 0 DO
DEC (i);
INC (c, s[i]);
IF c >= 10 THEN
s[i] := c - 10;
c := 1
ELSE
s[i] := c;
c := 0
END
END
END add;
BEGIN
NEW (s);
NEW (x);
init (s^, 0);
init (x^, 1);
xs := 0;
add (s^, x^, xs);
i := 0;
REPEAT
INC (i);
divide (x^, xs, i, x^, xs);
add (s^, x^, xs);
UNTIL xs > DIGITS;
InOut.WriteLn;
InOut.WriteString (" e = ");
InOut.Write (CHR (s^[0]+ORD ('0')));
InOut.Write ('.');
FOR i := 1 TO PRINT DO
InOut.Write (CHR (s^[i]+ORD ('0')));
IF i MOD 64 = 0 THEN
InOut.WriteLn;
InOut.WriteInt (i, 5);
InOut.WriteString (" ")
END;
END;
InOut.WriteLn;
InOut.WriteLn;
END exp.
Литература:
1 Руслан Богатырев
Судьба Оберона
МирПК, сентябрь 2005;
2 Сергей Свердлов
Арифметика синтаксиса
PC Week/RE, 1998, №42-43;
3 Руслан Богатырев
Язык Оберон. Краткий путеводитель
МирПК, сентябрь 2005;
4 Ханспетер Мессенбок, Никлаус Вирт
Различия между языками Oberon и Oberon-2
Institute for Computer Systems, ETH, Zurich, Technical Paper, July 1993
Перевод Р. Богатырев, 1995;
5 Х.Мёссенбёк, Н.Вирт
Язык программирования Оберон-2
Институт компьютерных систем, ETH, Цюрих Июль 1996
Перевод с английского С.Свердлова.