Next:1.4
Выражения
Up:1
Язык программирования Zonnon
Previous:1.2
Языковые символы и идентификаторы
Область действия идентификатора простирается текстуально от точки его объявления до конца блока, к которому принадлежит данное объявление и, следовательно, по отношению к которому оно локально. Она исключает области действия идентификаторов с одинаковыми именами, которые описаны во вложенных блоках. Правила областей действия имеют вид:
QualIdent = { ident "." } ident.
Примеры:
Month.Oct (* см 1.3.3.2 *)
NameSpace.Program
Объявление может иметь факультативный модификатор. Модификаторы
объявлений определяются следующим образом:
Пример:
var {private} flag: boolean;
var {public, immutable} refCount: integer; (*доступ только для чтения*)
Объявление константы связывает идентификатор с константным значением.
ConstantDeclaration = ident "=" ConstExpression.
ConstExpression = Expression.
Примеры:
const N = 10;
limit = 2*N
– 1; (* see 1.5.2.3*)
fullSet = {min(set)
.. max(set)}; (* см. 1.3.3.1*)
Константное выражение – это выражение, которое может быть вычислено при простом текстовом просмотре, без реального выполнения программы. Его операндами должны быть константы или вызовы предопределенных функций.
Тип данных определяет множество значений, которые допустимы у переменных
данного типа, и операций, которые к ним применимы. Объявление типа связывает
идентификатор с типом. В случае структурных типов (массивов и объектов)
оно также определяет структуру переменных этого типа. Типы объектов определяются
в 1.3.3.4 и 1.9.1
TypeDeclaration = ident "=" Type.
Type = ( TypeName [ Width ] | EnumType | ArrayType | ProcedureType
| InterfaceType ).
Width = "{" ConstExpression "}".
Базисные типы обозначаются предопределенными идентификаторами. Ассоциированные
знаки операций определены в 1.4.2, предопределенные функции и процедуры
в 1.7. Значения базисных типов следующие:
- object родовой тип, из которого выведены объектные типы
- boolean логические значения true и false
- char символы лежащего в основе реализации литерного множества
- integer целые числа из интервала от min(integer) до max(integer)
- fixed большие числа с фиксированной точностью между min(fixed) и max(fixed)
- real вещественные числа между min(real) и max(real)
- set множества чисел (целых или кардинальных) между 0 и max(set)
- string литерные строки
Заметим, что object является зарезервированным словом.
Для типов char, integer, real и set количество битов, необходимых для хранения значения, может быть специфицировано указанием целого числа битов в виде константного значения в скобках { } после имени типа после имени типа – так называемой ширины (width) типа. По умолчанию используются следующие следующие значения ширины для базисных типов:
char{16}, cardinal {32}, integer{32}, real {80}, set{32}, fixed {128}
О конвертации между различными типами см. 1.3.3.9.
Перечисление является типом, который состоит из именованного списка
идентификаторов, обозначающих значения, которые составляют тип. Эти идентификаторы
квалифицируются именем типа при использовании в качестве именованных констант
в программе. Значения упорядочены, и их отношение порядка определяется
их текстуальной последовательностью в перечислении. Нет других значений,
принадлежащих данному типу. Порядковый номер первого значения равен нулю
и увеличивается на единицу при переходе от любого элемента перечисления
к следующему идентификатору.
EnumType = "(" IdentList ")".
IdentList = ident { "," ident }.
Примеры:
type NumberKind = (Bin, Oct, Dec, Hex);
Month = (Jan, Feb, Mar,
Apr, May, Jun, July, Sep, Oct, Nov, Dec);
Имена в различных перечислениях не обязаны различаться, поскольку всегда квалифицируются при использовании. Так для рассмотренного примера NumberKind.Oct всегда отличается от Month.Oct.
Значения выражений могут конвертироваться к некоторому другому типу (см. 1.3.3.9).
Предопределенная функция pred возвращает значение предшественника значения
перечисления, указанного в качестве ее параметра, для всех значений, за
исключением первого значения перечисления. Предопределенная функция succ
возвращает значение преемника значения перечисления, указанного в качестве
ее параметра, для всех значений, отличных от последнего значения перечисления.
Массив является структурой, состоящей из некоторого числа элементов,
которые все имеют один и тот же тип, называемый типом элементов. Массивы
могут индексироваться либо положительным целым числом, либо значением перечисляемого
типа. В первом случае количество элементов в объявлении массива определяет
его длину. Элементы массива обозначаются индексами, которые представляют
собой целые числа между нулем и длиной массива минус единица. Во втором
случае имя перечисляемого типа используется в объявлении массива, а элементы
массива обозначаются значениями перечисляемого типа.
Синтаксические правила для типа массивов имеют вид:
ArrayType = array Length {"," Length} of Type.
Length = Expression | "*".
Массивы могут быть многомерными; это означает, что элементы массива сами могут быть массивами, и в принципе допустимо соединение форм спецификации различных длин. Но эта возможность вполне может быть ограничена реализацией (см. [Compiler]). Примером и контр примером являются:
type Acceptable = array * of array 42 of T;
(* array *, 42 of T *)
Jagged = array 42
of
array * of T; (* 'зубчатый' массив *)
Описание array m of array n of T is текстуально эквивалентно array m, n of T.
Например, array * of array 42 of T может быть записано
array
*, 42 of T
Выражение len(a, n) возвращает количество элементов по измерению n массива a. Выражение len(a) является сокращением для len(a, 0).
В массиве количество элементов по любому измерению может быть переменным и тогда оно обозначается звездочкой. Ответственность за отведения области памяти в куче с использованием зарезервированного слова new для каждого экземпляра массива ложится на программиста:
arrayVariable := new ArrayType(length0, length1, … );
Значение длины должно выражаться положительным выражением целого типа, и количество таких выражений должно соответствовать числу измерений данной переменной.
Примеры использования массивов:
type Vector = array * of integer;
procedure CreateAndReadVector(var a: Vector);
var i, n: integer;
begin
read(n);
a := new Vector(n);
for i := 0 to len(a) – 1 do
read(a[i])
end
end CreateAndReadVector;
procedure InitializeMatrix(var mat: array *, *
of
real);
var i, j: integer;
begin
for i := 0 to len(mat, 0) - 1 do
for j := 0 to
len(mat, 1) – 1 do
mat[i, j] := 0.0
end
end
end InitializeMatrix;
…
var m: array 10, 10 of real;
…
InitializeMatrix(m);
Переменная типа string представляет неизменные последовательности
литер. Строки можно сравнивать на равенство и неравенство с использованием
операций ‘=’ и ‘#’. Знак операции ‘+’ представляет конкатенацию, а ‘:=’
присваивание. Предопределенная процедура copy конвертирует значения строкового
типа в значения литерного массива и обратно.
Объект (object) является шаблоном типа данных, состоящим из полей,
методов и активностей. Поля представляют состояние объекта, методы – его
функциональность, а активности – его параллельные активности. Он может
раскрывать свой интерфейс системному окружению двумя способами. Во-первых,
посредством интерфейса его внутреннего типа (на который ссылаются, как
на его внутренний интерфейс), состоящего из множества всех элементов, которые
программист решил сделать общими, а не приватными, и, во-вторых, некоторым
числом определений, каждое из которых раскрывает некоторый свой аспект
служб объекта по отношению к его клиентам.
Object = object [ ObjModifier ] ObjectName [ FormalParameters
] [ ImplementationClause ] ";"
[ ImportDeclaration ]
Declarations
{ ActivityDeclaration }
( BlockStatement | end ) SimpleName.
ObjModifier = "{" ident "}". // value или ref
// private или public
ActivityDeclaration = activity ActivityName [ ImplementationClause
] ";"
Declarations ( BlockStatement | end SimpleName ).
ImplementationClause = implements DefinitionName { "," DefinitionName
}.
ImportDeclaration = import Import { "," Import } ";".
Import = ImportedName [ as Ident ].
ImportedName = ( ModuleName | ImplementationName | NamespaceName |
DefinitionName, ObjectName).
Объект образован объявлениями, включающими константы, типы, переменные (на которые ссылаются как на поля), и процедуры (на которые ссылаются как на методы). Модификаторы public и private можно использовать для объявления видимости содержимого объекта. Если модификатор не представлен, то умолчанию считается private.
Отдельные данные можно сделать общими явным использованием модификатора {public}, размещенным после их объявления. Сам объект тоже может иметь модификатор, который обозначает его либо как объект-значение, либо ссылочный объект, используя значения модификаторов value и ref соответственно. Если модификатор отсутствует, по умолчанию выбирается value.
Переменные, которые являются ссылочными объектами, представляют собой ссылки на объекты, которые создаются динамически во время выполнения программы с использованием new. Объект факультативно может иметь параметры, которые используются в теле объекта для инициализации полей, когда создается экземпляр с использованием new.
object {ref} Box(w, h: integer);
var width, height: integer;
procedure Area(): integer;
begin
return width * height
end Area;
begin
self.width := w; self.height := h (* self является
здесь необязательным в обоих случаях*)
end Box.
…
var box: Box;
…
box := new Box(3, 7); (*создает новый объект Box с шириной 3 и высотой
7 *)
См. 1.9.2 об объектах как программных единицах.
Запись является типом объектов значений. Она может использоваться
для инкапсуляции описаний констант, типов и переменных, но не методов и
активностей. Ключевое слово record эквивалентно object {value}.
Переменные, которые объявлены как объекты-значения, статически размещаются
во время компиляции
Примеры:
record Position; (*описание Position типа 'record'*)
var x, y: integer
end Position;
что эквивалентно
object {value} Position; (*описание Position типа 'record'*)
var x, y: integer
end Position;
record Date; (*описание Date типа 'record' *)
var year: integer{8};
month: Month;
day: integer{8}
end Date;
Интерфейс является постулированной реализацией для объекта, образованного
из одного или более определений. См. 1.3.3.8 и 1.9.2 для дальнейших деталей.
InterfaceType = object [ PostulatedInterface ].
PostulatedInterface = "{" DefinitionName { "," DefinitionName } "}".
Переменная процедурного типа T имеет некоторую процедуру или метод
P или nil в качестве своего значения. Если процедура P присвоена переменной
типа T, списки формальных параметров у P и T должны соответствовать друг
другу по множеству правил, описанных в 1.11.4. P не может быть предопределенной
процедурой или локальной по отношению к другой процедуре. Когда метод присваивается
переменной типа процедуры он должен иметь префиксом (обозначение) экземпляра
объекта, его содержащего.
ProcedureType = procedure [ ProcedureTypeFormals ].
ProcedureTypeFormals = "(" [ PTFSection { ";" PTFSection } ] ")" [
":" FormalType ].
PTFSection = [ var ] FormalType { "," FormalType }.
FormalType = { array "*" of } ( TypeName | InterfaceType
).
InterfaceType = object [ PostulatedInterface ].
PostulatedInterface = "{" DefinitionName { "," DefinitionName } "}".
В языке Zonnon преобразование (conversion) типа внутри одного ‘семейства’
(такого как integer) является неявным тогда, когда оно гарантированно является
безопасным. Однако любое преобразование типов между семействами должно
быть явным (поскольку оно вызывает преобразование внутреннего представления).
Обратные преобразования (например, integer{32} в integer{16}) внутри семейства
также всегда должны быть явными. Механизм исключительных ситуаций обнаруживает
конвертационные аномалии (см. 1.6.10.1). Взаимосвязь между типами базируется
на модели Общей Системы Типов ECMA , как она используется в .NET и суммирована
в следующей таблице:
Заметим, что отношение неявного преобразования типов является транзитивным и обратное отношение (противоположное направлению стрелки) требует явного преобразования и может привести к усечению или исключительной ситуации.
Чтобы осуществить преобразование типа, имя целевого типа может рассматриваться
как встроенная функция, которая берет значение выражения исходного типа
в качестве параметра и возвращает конвертированное значение. Факультативный
второй параметр функции этой функции указывает размер результата.
Синтаксис:
TypeConversion = TypeIdentifier "(" expression [ "," Width ] ")".
Примеры:
integer(x + e/f, 16)
является значением выражения x + e/f , представленным в виде 16-битового целого (может возникнуть исключительная ситуация, если преобразование типа не возможно).
integer(x + e/f)
является значением выражения x + e/f , представленным в виде 32-битового целого (предполагая, что 32 является размером по умолчанию для целого типа). Заметим, что целые не могут неявно быть приведены к вещественным и поэтому:
var count, sum: integer; mean: real;
…
mean := sum / count
синтаксически не допустимо и требует явного преобразования типа:
mean := real(sum) / real(count)
Тип простой целочисленной константы определяется путем описания
переменной, которой она присваивается. Так, например, при наличии описания:
var i: integer {16};
оператор присваивания
i := 1;
фактически рассматривается компилятором как присваивание
i := 1{16};
Если размер не специфицирован, то выбирается тот размер, который определен для данного типа по умолчанию.
Другое преобразование типа достигается посредством предопределенных
процедур (см. раздел 1.7).
Переменная обладает некоторым значением, которое она может получить
от выражения с помощью операции присваивания (см. 1.5.1). Переменная определяется
как имеющая некоторый тип, который не может быть изменен; тип определяет
то множество значений, которыми переменная может обладать. Описание переменной
вводит переменные путем определения для каждой из них идентификатора и
типа данных.
VariableDeclaration = IdentList ":" Type.
Примеры:
var i, j, k: integer;
x, y: real;Next:1.4 Выражения
p, q: boolean;
s: set {32};
a: array 100 of real;
name: array 32 of char;
size, count: integer;
mousePosition: Position;
dateOfBirth, today: Date;