Inferno Programmer's Guide / Руководство программиста по ОС Inferno: Chapter 2 - Limbo Basics

Английский оригинал Перевод на русский

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

Основы языка Limbo

Начало

rapidfx 16.04.09 в 11:49

1

Эта глава представляет вам язык программирования для Inferno для тех кто не знаком с Limbo.
Этот раздел рабирает простую программу на Limbo, которая иллюстрирует основы каждой Limbo-программы. Далее следует раздел посвященный языковой базе, который углубляется в язык Limbo как таковой. В нем освещены основные моменты языка и включены множество примеров программ на Limbo.
Для определний формата языка Limbo обращайтесь к Приложению А.

rapidfx 16.04.09 в 11:55

0

Приступаем

rapidfx 16.04.09 в 11:55

0

Программа на Limbo
Для иллюстрации языка Limbo, здесь приведен простой пример программы. Это обычная рпограмма выдающая приветствие (вариация древнейшей “Hello, World!”). Она показывает ключевые части Limbo-программы.

rapidfx 16.04.09 в 11:58

0

Пронумерованный построчно исходный текст можно скомпилировать Inferno'вским компилятором Limbo и затем запустить из оболочки Inferno (командной строки).

rapidfx 16.04.09 в 12:04

0

Листинг 2-1 greet.b (возможно содержит ошибки OCR)

rapidfx 16.04.09 в 12:01

0

1 implement command;
2
3 include "sys.m";
4 include "draw.m"; 5
6 sys: sys;
7
8 Command: module {
9 init: fn(ni~l: ref Draw->context, argv: list of string)
10 };
77
12 init(ni~l: ref Draw->context, argv: list of string) {
13 sys = load sys Sys->PATH;
14 # check for command-line arguments 75 if ((tl argv) != nil) {
16 sys->print("He"l"lo, %s! ", hd (tl argv));
17 } else
18 sys->print("He"l"lo, wor1d!");
19 }

rapidfx 16.04.09 в 12:04

0

1 implement command;
2
3 include "sys.m";
4 include "draw.m";
5
6 sys: sys;
7
8 Command: module {
9 init: fn(nil: ref Draw->Context, argv: list of string)
10 };
11
12 init(nil: ref Draw->Context, argv: list of string) {
13 sys = load sys Sys->PATH;
14 # проверяем наличие аргументов командной строки
15 if ((tl argv) != nil) {
16 sys->print("Hello, %s! ", hd (tl argv));
17 } else
18 sys->print("Hello, world!");
19 }

j1m 17.04.09 в 17:07

2
+1

Напоминание: номера строк указаны исключительно для ссылки на них.

rapidfx 16.04.09 в 12:05

0

Строка 1 говорит о том, что этот файл реализация модуля Command. В этом простом примере, как в обычной не сложнлй программе Limbo, интерфейс и реализация находятся в одном и том же файле.Имя файла не обязательно должно совпадать с именем модуля или именем реализации,обычно они одинаковы. В этом примере, реализуется интерфейс Command - специальный интерфейс модулей (программ) запущенных в интерпретаторе Inferno.
Строки 3 и 4 включают объявление интерфейса двух модулей которые этот модуль использует. Строка 3 содержит интерфейс к модулю System (sys. m), а Строка 4 интерфейс к модулю Draw (draw. m). В Limbo нет препроцессора в отличие от C. Поэтому слово include - ключевое слово, а не директива препроцессора. В Limbo имя файла за словом include всегда заключается в двойные кавычки ("mod.m").
Строка 6 объявляет переменную sys типом Sys (в Limbo переменные чувствительны к регистру). Тип Sys это тип модуля, определенный в файле sys.m :

rapidfx 16.04.09 в 18:28

0

sys: module
{
...
};

wuffur 17.04.09 в 18:18

0

Переменная sys объявлена для обращения к функциям и данным модуля Sys. На этом этапе, значение ni будет 1.
Строки с 8 по 10 содержат объявление модуля Command, который мы начали реализовавать в нашем файле на Строке 1. Единственная функция определенная в этом модуле - init. Функции передается два аргумента ref Draw->Context и список строк.

rapidfx 17.04.09 в 11:41

0
+1

Функция init и два ее аргумента ожидают что программа будет запущена интерпретатором командной строки Inferno. Во многом, это аналогично функции main() языка C, указывающей где начинать исполнение в модуле и берущей графический контекст и аргументы командной строки если они есть.
ref Draw->Context используется для захвата контекста дисплея и определена в draw. m, который был включен в Строке 4. Этот аргумент нужен только в том случае, если программа не работает с графикой. В нашем случае аргумент может быть назван ni 1.
Этому модулю может быть передан в качестве аргумента список строк из командной строки . Его общепринятое название argv. Если программа не ожидаетникаких аргументов от командной строки, оно может быть названо nil.

rapidfx 17.04.09 в 12:15

0

Строки с 12 по 19 содержат определение функции init или выражения которые будут выполнены.
Ранее, в 6 строке, мы упоминали, что значение переменной sys было nil. В строке 13, через ввод с клавиатуры, мы создали ссылку на определение модуля Sys, заданным в sys. Это соединяет программу с модулем Sys из библиотеки и дает программе доступ к функциям и данным заданным в модуле Sys.

wuffur 17.04.09 в 17:59

0

Строка 14 - комментарий. Комментарии в Limbo начинаются с решетки(#) и заканчиваются в конце строки. Для многострочных комментариев нужно ставить решетку в начали каждой строчки.

wuffur 17.04.09 в 18:02

0

В строках с 15 по 18 содержится код, печатающий сообщение, использующее аргумент командной строки, которая передаются в init через argv. Второй аргумент функции init это список строк. Списки в Limbo - группа последовательных, упорядоченных элементов. Вы проходите по элементам списка используя операторы hd(head - "голова") и tl(tail - "хвост"). Первый элементы списка argv, его голова - это имя программы. Так как нам интересен только остаток списка, его хвост, мы проверяем, не nil ли это(хвост имеет какое-то значение), затем мы печатаем сообщение и голову оставшегося хвоста(второй элемент списка). Если там нет никаких аргументов, печатаем сообщение “Hello, World!”.
В параграфе Основы Языка, которая начинается на страницах 2-7, очень подробно объясняется семантика.

wuffur 17.04.09 в 18:14

0

Компиляция Limbo-программы
Запустите компилятор Limbo с помощью следующей строки в окне командного интерпритатора:
limbo greet.b
Если компилятор не найдет ошибок, то в текущем каталоге появится исполняемый файл, называемый Dis-файлом, в данном случае greet.dis.
Обратитесь к главе 4 чтобы узнать больше о компиляции Limbo-программ и соглашениях об именовании файлов. Опции комплятора описаны в man-странице limbo(1).

j1m 17.04.09 в 10:22

2

Запуск Limbo-программы
Чтобы запустить программу просто наберите ее имя в окне командного интерпритатора, опустив расширение .dis. Например:
> greet Reader
Hello, Reader!
Любые аргументы командной строки Inferno передает функции init() как элементы списка строк. Имя программы первый элемент этого списка.

j1m 17.04.09 в 11:31

0

Запуск Limbo-программы
Чтобы запустить программу просто наберите ее имя в окне командного интерпритатора, опустив расширение .dis:
% greet Reader
Hello, Reader!
Любые аргументы командной строки Inferno передает функции init() как элементы списка строк. Имя программы первый элемент этого списка.

j1m 17.04.09 в 11:32

0

Далее будут рассмотрены:
* Ключевые слова
* Операторы
* Типы данных
* Контроль программы (исполнения?)
* Функции
Для полного описания языка смотрите Приложение А, Описание языка Limbo

j1m 17.04.09 в 11:37

0

Ключевые слова
Limbo включает небольшой, но полный набор зарезервированных слов. Следующие идентификаторы зарезервированы для использования в качестве ключевых слов и не могут быть использованы не для чего больше:
adt alt
byte case
cyclic do
for hd
include int
module nil
real ref
string tagof
while

j1m 17.04.09 в 11:45

0

array big break
chan con continue
else exit fn
if implement import
Ten list load
of or pick
return self spawn
tl to type

Операторы
Limbo включает три класса операторов: унарные, бинарные и операторы присваивания.
Унарные операторы
++ Инкремент
-- Декремент
+ Унарный плюс
- Унарный минус
! Логическое отрицание (NOT)
* Косвенная ссылка ADT
<- Оператор канала (отправка и получение)
8 One's complement, bitwise negation (NOT)
ref Ссылка на объект ADT
hd Голова списка
tl Хвост списка
tagof Индекс элемента pick в ADT
len Возвращает размер чего-либо

j1m 17.04.09 в 11:54

0
+1

Бинарные операторы
* Умножение
/ Деление
% Деление по модулю
+ Сложение
- Вычитание
<< Сдвиг влево
>> Сдвиг вправо
== Равенство
< Меньше чем
> Больше чем
< Меньше или равно
> Больше или равно
!= Неравенство
: Побитовое И (AND)
| Побитовое ИЛИ (OR)
< Побитовое исключающее или (XOR)
:: Конструктор списков
&& Логическое И (AND)
|| Логическое ИЛИ (OR)

j1m 17.04.09 в 12:00

0
+1

Операторы объявления и присваивания.
Таблица 2-3 описывает операторы объявлений и присваивания языка Limbo.
= простое присваивание
: объявление (создание нового)
:= произвести объявление и присвоить - инициализация (сокращение)
+= произвести инкремент и присвоить
-= произвести декремент и присвоить
*= Умножить и присвоить
\= разделить и присвоить
%= взять остаток от деления и присвоить
&= Произвести побитовое И (AND) и присвоить
|= Произвести побитовое ИЛИ (OR) и присвоить
^= Произвести побитовое исключающее или (XOR) и присвоить
<<= произвести побитовый сдвиг влево и присвоить
>>= произвести побитовый сдвиг вправо и присвоить

wuffur 17.04.09 в 18:53

0
+1

Типы данных
В Limbo есть две категории типов данных:
* Примитивные: byte, integer, big, real, string
* Ссылочные: array, list, turple
Кроме того, вы можете определить собственный тип данных с помощью абстрактного типа данных (ADT).
Примитивные типы
Примитивные типы в Limbo имеют фиксированное определение, то есть их размер одинаков на всех платформах. Это нужна для портабельности кода.
В следующей таблице перечислены эти типы данных, диапазан их значений и meaning.

j1m 17.04.09 в 12:58

0

byte Беззнаковой 8-битное целое, длиной 1 байт
int 32-битное целое со знаком
big 64-битное целое со знаком
real 64-битно число с плавающей точкой двойной точности стандарта IEEE

wuffur 17.04.09 в 19:10

0

Строки
В Limbo строковые константы заключаются в двойные ковычки. Например:
s := "inferno";
объявляет строку s и присваивает ей значение inferno.
Inferno использует кодировку Unicode вместо ASCII, так что строковый тип в Limbo - это вектор (массив) Unicode-символов, каждый длиной в 16 бит.
В Limbo нет специального типа данных для хранения одиночных символов, поэтому символ, заключенный в одинарные, представляет собой целое число. Например:
c := 'I';
Объявляет переменную c, и присваивает ей зачение 73 (код символа I).
Из строки можно извлеч подстроку, называемую срезом (slice). Смотрите раздел Срезы массивов чтобы узнать как это сделать.
Константы
Идентификатор, имеющий константное значение примитивного типа, объявляется с использованием ключевого слова con:
data: con "/data/user.dat"; # константа типа string
block: con 512; # константа типа int
pi: con 3.1415926535897932; # константа типа real

j1m 17.04.09 в 13:55

0

Перечисление констант
Вы можете использовать специальное значение iota вместе с ключевым словом con чтобы присвоить константе значение, которое на один больше предыдущего в группе идентификаторов. Это похоже на печечисления в языке Си.
Пример:
Mon, Tue, wed, Thu, Fri, sat, sun: con iota;
Эта строка объявляет константу Mon со значением 0, Tue со значением 1, Web - 2 и т.д.
Вы можете использовать другие операторы совместно с iota чтобы изменять начальное значение и увеличивать значения. Наиболее используемые операторы это сложение, умножение и побитвый сдвиг.
Пример:
Ten, Eleven, Twelve: con iota+10
Приведенный код устанавливает стартовое значение значение в 10 и увеличивает последующие значения на единицу.
Вы можете использовать оператор умножения чтобы изменить диапазон увеличения значения:
zero, Five, Ten, Fifteen: con iota*5;
В также можете использовать операторы побитового сдвига:
Two, Four, Eight, sixteen, ThirtyTwo: con 2«iota;
Используя различные комбинации операторов вы сможете получить множество различных вариантов изменения значения.

j1m 17.04.09 в 15:14

0

Ссылочные типы
Ссылки в Limbo подобны указателям в языках Си Си++.
Осовное различие между ними заключается в том, что Limbo не позволяет математически манипулировать ссылками. Это обеспечивает “pointer-safety” в Limbo, так как ссылка никогда не укажет на незаконный адрес. Однако она может иметь значение nil.
Значение nil
Ссылочным типам можно присвоить специальное значение, представленное ключевым словом nil. В результате ссылка будет удалена. Если это последняя сслыка к данным, то они будут автоматически и немедленно утилизированы сборщиком мусора. Сразу после объявления ссылочные типы ни на что не указывают. Значение должно быть присвоено им перед использованием.
Массивы
Массивы состоят из последовательно пронумерованных однотипных данных. Нумерация начинается с 0.
Простейшее объявление массива выглядит так:
buf : array of byte;
В примере объявляется переменная buf, являющаяся массивом элементов типа byte.
Конструктор, заранее выделящий место для массива в памяти выглядит так:
array[80] of byte
Это выражение создает новый массив из 80 элементов типа byte. Чтобы присвоить хранилище этого массива переменной, используйте оператор присваивания:
buf = array[80] of byte;
Следующий блок печатает число и квадрат этого числа:
count := array[5] of int; i := 0; do {
count[i] = i ;
sys->print("%d\t%d\n" , i, count[i]*i++);
} while (i < 5);
Вывод выглядит примерно так:
0 0
1 1
2 4
3 9
4 16
Срезы массивов
Срез - это последовательный диапазон элементов массива или строки (в Limbo строки представляют собой массив символов Unicode). Диапазон задается как индекс в квадратных скобках. В диапазоне указывается начальное и конечное значения, разделенные дветочием.
Пример:
s := "inferno";
Начало строки Конец строки
| |

j1m 17.04.09 в 17:02

0

_I__n__f__e__r__n__o
[0] [1] [2] [3] [4] [5] [6]

Срез строки s со второгоого по шестой элемент:
s2:=s[2:6]; значение s2 - строка fern.

j1m 19.04.09 в 17:17

0

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

Начало списка___________________________________Конец списка
[элемент]-►[элемент]-►[элемент]-►[элемент]-►...-►[элемент]
Голова_/ \_________________хвост______________________/

wuffur 17.04.09 в 19:27

0

.

wuffur 17.04.09 в 19:34

0

.

wuffur 17.04.09 в 19:34

0

Рисунок 2-3

Элементы списка.

Например:
line : list of string;
Она объявляет идентификатору line тип list of string. Элементы в line - типа string.
Элементы списка доступны, изменяемы и управляемы с помощью операторов списка. Таблица 2-5 определяет операторы списка.

wuffur 17.04.09 в 19:33

0

Таблица 2-5 Операторы списка
оператор | Имя | Значение
:: |конструктор | конструктор значения
tl | хвост | список без первого элемента
hd | голова | первый элемент

wuffur 17.04.09 в 19:39

0

Оператор создания списка (::)
Для создания списка используйте ::, оператор создания списка, например так:
line = "First" :: line;
Это похоже на то, как кладутся элементы в стек. Конструктор списка правоассоциативный. Правый операнд - это один элемент и он должен иметь тот же тип, что и элементы списка. Идентификатор списка левый операнд.
Оператор взятия головного элемента списка (hd)
Голова списка - это один элемент, и имеет тот же тип, что и элементы списка. Взять головной элемент списка, используйте оператор hd. Он не будет создавать новый список или менять исходный.
Например, опишем использование списка сделанного выше:
sys->print("The list head is: %s", hd line);
This prints:
The list head is: First
Оператор взятия хвоста списка (tl).
Хвост списка сам список, того же типа, что и исходный. Присвоение результата использование оператора его исходному списку, убирает головной элемент, например так:
line = tl line;
Новый список можно сделать, присвоив ему хвостовой список от исходного, например так:
args := tl line;

wuffur 17.04.09 в 20:16

0

Кортежи / Тьюплы (фиксированные списки)
Кортеж - это последовательный, заключенный в скобки список типов данных, которые обрабатываются как один объект.
Например:
retcode := (0, success);
Так cоздается кортеж retcode, в котором хранится целое и строка.
Кортеж распаковывается через присваивание. Например:
err : int;
msg : string;
(err, msg) = retcode;
Присваивается число 0 переменной Err, и строку “Success” переменной msg.
Используя сокращение объявить и распоковать, та же распаковка может находиться в одном выражении.
(err, msg) := retcode;
Этот код объявляет переменные err и msg и присваивает им значение из кортежа retcode. Используя ключевое слово nil можно игнорировать при присваивании часть кортежа. Например:
(err, nil) = retcode;
Этот код присваивает целое значение из кортежа retcode, но игнорирует его строковую часть.

wuffur 17.04.09 в 21:07

0
+2

Абстрактные типы данных (ADT)
В языке Limbo абстрактный тип данных (ADT) - это определенный пользователем тип данных, который энкапсулирует данные и функции внутри проименованного типа. Он похож на структуры языка Си, но может включать так же и функции, на манер классов C++. TODO: наследование и полиморфизм
Объявление ADT
У объявления ADT, есть основная форма:
identifier: adt {
список-членов-ADT
};
Объявление членов типа ADT очень похоже на объявление данных или функций в других местах программы на Limbo.
Например, следующий код - это объявление простого ADT Inventory:
inventory: adt {
id: string;
onhand: int;
cost: real;
value: fn(item: inventory): real;
};
Это объявление содержит три члена данных и один член функцию.
После объявления, Inventory становится типом данных, таким как int или real. Обычно объявление типа ADT находятся в отдельном файле модуля (. m).
Объявление функции члена типа ADT обычно находятся в файле реализации (. b). Объявление функции члена типа ADT происходит также как и обычной функции, с добавлением спецификатора в имени функции, используя оператор точка(.).
Например:
inventory.value(item: inventory): real {
return (real(item.onhand) * item.cost);
}
Создание экземпляров ADT
Создание экземпляров ADT - это объявление переменной с конкретным типом ADT. Например:
part1: inventory;
Объявляет переменную part1 типа Inventory.
Доступ к членами ADT
К членам переменной типа ADT обращаются через точку (.), используя уточняющую запись. С ними можно работать, как с любой другой переменной или функцией.
Например:
part1.id = "widget";
part1.onhand = 250;
part1.cost = 4.23;
Присваивает значения членам id, onhand и cost переменной part1
Члены-функции вызываются похожим образом. Например:
sys->print("Количество денег на руках: $%5.2f\n", part1.value(part1));
Используя присвоенные ранее значения, выражение печатает:
Количество денег на руках: $1057.50

j1m 22.04.09 в 14:57

1

В этом примере, чтобы член-функция могла работать с членом данных, экземпляр ADT должен быть явно передан как аргумент функции. Но передачу экземпляра ADT можно поизвести и неявно, испольуя ключевое слово self.
Неявный аргумент: self
Изменив опредление члена-функции value() так, чтобы она использовала ключевое слово self, мы сможем передать ADT как неявный аргумент:
inventory: adt {
id: string;
onhand: int;
cost: real;
value: fn(item: self inventory): real;
};
inventory.value(itetn: self inventory): real {
return (real(item.onhand) * item.cost);
}
Добавление аргумента self объявлению и определению функции члена self позволяет вызвать функцию-член без явной передачи экземпляра ADT. Следующий код отражает изменения в выражении, которое вызывает функцию:
sys->print("Количество денег на руках: $%5.2f\n", part1.value());
Используя значения, определенные ранее, выражение печатает:
Количество денег на руках: $1057.50

j1m 22.04.09 в 15:15

1

ref ADT(ссылочные абстрактные типы данных)
Как говорилось ранее, абстрактные типы данных могут быть представлены конкретным типом со значением или ссылочным типом. Те ADT, которые мы видели были представлены как value ADT. Если ADT должно быть ссылочного типа, так называемого ref ADT, к нему нужно приписываться ключевое слово ref.
Например, чтобы представить inventory ADT как ссылочный тип:
part2:= ref inventory;
Так создается новая переменная типа inventory и ссылка на нее располагается в part2. Ссылкой part2 можно пользоваться как обычным ADT. Например, присвоение значений членам данным может быть в точности таким:
part2.id = "whatsit";
part2.onhand = 1000;
part2.cost = 0.17;
Важное замечание, что тип part2 отличается от part1. Это значит part2 не может быть присвоена переменной inventory или передана непосредственно функциям членам текущего ADT Inventory. Например, следующие выражение на этапе компиляции вызовут ошибки:
part2 = part1; # конфликт типов аргументов, не будет компилироваться
part2.value(); # не соответствует тип аргумента, не будет компилироваться
Чтобы получить доступ к ref ADT используйте префиксный оператор звездочка (*):
part1=*part2; #правильно
(*part2).value(); #правильно
Функции члены ADT могут допускать обращение к ref ADT. Для этого нужно добавить ключевое слово self в определении функции:
clear: fn(item: self ref inventory);
Определение функции члена похоже на это:
inventory.clear(item: self ref inventory) {
item.onhand = 0;
}
ref ADT против Value ADT.
Основной преимущество ref ADT в том, что можно передавать значения между функциями без копирования. Это более эффективно, особенно для большого ADT. Функции члены, которые принимают ref ADT могут переписывать содержимое своих аргументов. Это нельзя сделать, используя функции члены получающие доступ к value ADT.
Однако интерфейс доступа к ref Adt может стать менее ясным, в основном из-за необходимости переписывать значения переменных. Интерфейс для доступа к ADT по значению более прям и прост,он более естественный для большинства ситуаций.

wuffur 19.04.09 в 19:10

0

pick(подборка) ADT
На Limbo pick ADT подобна объединению на Си. Она позволяет разделять экземпляры ADT для хранения элементов данных различных типов. Таким образом, одно объявление ADT может определить ADT различных типов. Pick ADT может иметь обычные данные и функции члены и члены данные, которые определены для экземпляра ADT.
В pick ADT две части:
1) его объявление
2) и выражение pick ADT, где происходит доступ к элементам в pick ADT.
Объявление подборки(pick)
Основная форма pick ADT выглядит так:
adtidentifier: adt { pick {
element => variable: type;
}
};
element это идентификатор подборки(pick), который определяет специфическую переменную, чтобы она имела некоторый тип. Например:
Apick: adt { str: string;
pick {
# string element
# int element
# real element
string => val: string;
int => val: int;
Real => val: real; }
};
Обычный элемент этого ADT - это переменная str типа string. pick-элементы это
String, Int, and Real. Они определяют три различных Apick ADT.
Если у pick ADT обычные данные члены, pick-блок будет заключительным сегментом данных. Однако, функции члены следуют за pick-блоком.
pick ADT должен быть объявлен как ref ADT. Этот специфичный pick-элемент должен быть определен. Например:
r := ref Apick.Real;
Когда вы объявляете значение членов данных ADT используйте переменные, определенные через pick-структуру:
r. str = "Pi";
r.val = 3.141593;
Вы также можете объявлять и инициализировать экземпляр ADT. Например:
s := ref Apick.string("Greeting", "Hello, world!");
Оператор tagof
Оператор tagof возвращает индекс pick-элемента ADT в конкретный экземпляр ADT.
Индексация элементов начинается с 0.
Например:
tagof r
Возвращает индекс экземпляра r подборки Apick ADT сделанной ранее, который равен 2(третий элемент).
Оператор pick
Оператор pick позволяет выбирать операторы, на том основании, что типы pick ADT различны. Основная форма оператора pick такая:
pick localinst : = refadtinst {
element => statements
...
}
Оператор pick использует локальную копию localinst ссылки на ADT refadtinst.
element(s) - в pick ADT можно объявлять один или несколько элементов.
Например:
t : ref Apick = s;
pick u := t {
String => sys->print("%s is %s\n", u.str, u.val);
Int => sys->print("%s is %d\n", u.str, u.val);
Real => sys->print("%s is %f\n", u.str, u.val);
}
Во-первых, что важнее для оператора pick,экземпляр s типа ref ADT инициализирован.
Потом оператор pick выбирает оператор для выполнение в зависимости от различных вариантов типа переменной t типа ADT.
Другой путь обычно используемый для того, чтобы сделать это - это поставить оператор pick в функцию, которая примет аргумент ref ADT.
Например:
printval(a: ref Apick) {
pick t := a {
String => sys->print("%d: %s\n", tagof t, t.val);
Int => sys->print("%d: %d\n", tagof t, t.val);
Real => sys->print("%d: %f\n", tagof t, t.val);
} }
Для того, чтобы вызвать эту функцию из ADT, которая определяет элемент подборки (pick), передающийся как аргумент:
printval(s);
Функция также может быть членом ADT. Таким образом, ADT может передаваться через неявный аргумент. Например:
Apick.printval (this: self ref Apick) {
pick t := this {
String => sys->print("%d: %s\n", tagof t, t.val);
Int => sys->print("%d: %d\n", tagof t, t.val);
Real => sys->print("%d: %f\n", tagof t, t.val);
} }
Тогда вызов этой функции будет выглядеть примерно так:
s.printval();
2-2 Листинг программы - простая программа использующая pick ADT.

wuffur 20.04.09 в 19:45

0
+1

Листинг 2-2: pick.b
1 implement command;
2
3 include "sys.m";
4 include "draw.m"; 5
6 sys: sys;
7
8 Apick: adt {
9 pick {
10 string => val: string;
11 int => val: int;
12 Real => val: real;
13 }
14 printval: fn(this: self ref Apick);
15 };
16
17 Apick.printval (this: self ref Apick) {
18 pick t := this {
19 string => sys->print ("%s: %s\n", tag(t), t.val);
20 int => sys->print ("%s: %d\n", tag(t), t.val);
21 Real => sys->print ("%s: %f\n", tag(t), t.val);
22 }
23 }
24
25 tag(t: ref Apick): string {
26 r: string;
27 case tagof t {
28 tagof Apick.string => r = "string";
29 tagof Apick.int => r = "int";
30 tagof Apick.Real => r = "Real";
31 }
32 return r;
33 }
34
35 Command: module {
36 init: fn(ctxt: ref Draw->context, argv: list of string);
37 };
38
39 init(ctxt: ref Draw->context, argv: list of string) {
40 sys = load sys Sys->PATH;
41
42 r := ref Apick.Real;
43 r.val = 3.141593;
44 sys->print("%d\n", tagof r); 45
46 s := ref Apick.string("Hello, world!");
47
48 s.printval();
49 r.printval() ;
50 }
Результат выполнения этой программы:
2
string: Hello, world!
Real: 3.141593

j1m 19.04.09 в 17:22

0

Видимость данных
Видимость данных зависит от того где они объявлены:
- Если данные объявлены в объявлении модуля, они доступны во всех функциях этого модуля. На них ссылаются как на данные млдуля и они являются глобальными в контексте модуля. Данные модуля инициализируются 0 или null. Если они объявляются в файле-интерфейса модуля, то они публичные - доступны всем программам которые загружают этот модуль.
- если данные объявлены за пределами функции и не в объявлении модуля, они будут глобальными в модуле, но приватными.
- если данные объявлены или инициализированы в функции, они будут доступны в этой функции. Они не доступны другим программам до тех пор, пока не будут явно переданы им.
- если данные объявлены или инициализированы внутри блока выражения, тогда они будут доступными только в этом блоке.

rapidfx 21.04.09 в 15:21

0

Рисунок 2-4 показывает зависимость места объявления и видимости данных.
[Figure 2-4]

rapidfx 21.04.09 в 15:22

0

Структуры управления
В Limbo имеется несколько выражений управляющих ходом выполнения программы которые могут быть разделены на группы:
- выбор
- итерация
- переход
- выход

Выражения выбора
Главными типами выражений выбора в Limbo являются: if, case и alt.
(Примечание: смотрите Главу 3 раздел Channels/Каналы для подробной информации об alt)

if
Выражение if выбирает выражения (последовательность операций/функций) для выполнения основываясь на вычислении условия. Существует два формата записи if:
if {условие) выражение
if {условие) выражение else выражение
Условие должно вернуть результат типа int. Если результат true (не ноль), тогда выполняется первое выражение (или блок). В противном случае, будет выполнено выражение (или блок) следующее за ключевым словом else, если else существует.
Например:
if (guess == randnum)
sys->print("correct!\n"); else
sys->print("oh, sorry.\n");
Выражения if могут вложенными. В Limbo выражение else всегда относится к ближайшему условию if, расположенному в том же блоке что и else и который еще не ассоциирован с else.
Например: // поправить форматирование у исходника
if (i == j) {
if (k == 1) {...} if (m == n) {
# this else is associated with
# 'if (m == n)'
# this else is associated with
# 'if (i ==j)'
} else {
} } else {
}

case
Выражение case выбирает из множества выражений на основании вычисления условия. Общая форма записи case:
case условие { qualifier => выражение
}

rapidfx 22.04.09 в 07:02

0

Условие должно вернуть результат типа int или string. Квалификатор (строка сравнения/определитель) может быть простой константой или же диапазоном значений. Они должны вычисляться уникально, так чтобы диапазоны или константы не перекрывались. Если соответствующий квалификатор не найден, и существует квалификатор по-умолчанию '*', тогда выбирается квалификатор по-умолчению.
Каждый квалификатор и выражения следующие за ним до следующего квалификатора образуют блок. Каждый блок прерывается (breaks) по умолчанию, нет нужды в явном указании выражения break.
В природе Limbo выражения case не существует "проваливания". Квалификатор не обязан иметь выражение, в этом случае выполнение не проваливается вниз и не продолжается у выражения следующего квалификатора.
Общий синтаксис case в Limbo похож на тот, что используется в выражении switch в C/ C++.
Например:
case d recti on { "north" or "east" => // опять форматирование
"south" =>
* =>}

Выражения case могуть быть вложенными. Например:
case x {
1 =>
case y {
0 =>
1 =>
}
2 =>

rapidfx 22.04.09 в 08:41

0

Итерация (выражения повтора)
В Limbo существуют три типа выражений повторяющих выполнение выражения или блока выражений до тех пор, пока не условие не будет верным: while, do, и цикл for.
Каждое выражение цикла может предворяться меткой, которая может быть использована в сочетании с выражениями перехода (jump), прерывания (break), продолжения (continue). Для детальной информации смотрите ниже раздел Метки/Labels.

Цикл for
Цикл for в Limbo, подобно C и другим языкам, гибкий и мощный.
Общий формат записи цикла следующий:
for {exp1; exp2; exp3} statement;
Внутри скобок расположены три выражения. Они могут являться любыми валидными (правильными) выражениями Limbo. Ниже приведены наиболее часто употребляемые выражения:
- exp1 - присвоение или инициализация индексной переменной;
- exp2 - тест на продолжение цикла;
- exp3 - изменение индексной переменной, такое как инкремент или декремент, для каждого шака цикла.
Выражение может быть простым одиночным выражением или заключенной в скобки ({. . . }) блоком выражений.
Например, следующий пример использует цикл для печати чисел от 1 до 100:

rapidfx 22.04.09 в 08:57

0

i: int;
for (i =1; i <= 100; i++) sys->print("%d ", i);
Обратите внимание на объявление целой переменной i, перед циклом for. Однако можно объявлять и присваивать (инициализировать) внутри выражения for в exp1:
for (i := 1; i <= 100; i++) sys->print("%d ", i);

Вариаации циклов for
Ни одно из трех выражений цикла for не являются обязательными. Например, вы можете создать бесконечный цикл, цикл создающий задержку во времени, цикл уравляемый несколькими переменными и многие другие.
Например, цикл for по соглашению используется для создания бесконечного цикла. Следующий пример постоянно считывает один символ из стандартного входа до тех пор пока не будет встречен перевод строки (нажатие клавиши Return или Enter):
stdin := sys->fi1des(0);
#get FD for stdin
buf := array[128] of byte;
n : int;
for(;;) {
n = sys->read(stdin, buf, 1);
#check for CR or AD (break, n=0)
if ((int(buf[0]) == 10) || (n <=0 )) break; }
Для информации об использовании оператора break для пррывания цикла смотрите Выражение break на странице 2-39.

Цикл while
Еще одно выражения часто упротребляемое в итерациях while. Общий формат записи цила while:
while (условие) выражение;
Если условие истинно (true) тогда выражение будет выполнено. Выражение может быть пустым выражением, или быть блоком выражений заключенных в скобки ({ . . . }).
Например, следующий фрагмент программы печатает числа от 1 до 100:
n := 1;
while (n <= 100)
sys->print("%d ", n++);

Цикл do...while
В отличии от for и while цикл do...while проверяет условие на истинность в конце итерации. Это означает что цикл do...while выполнится хотя бы раз. Общая форма записи do...while:
do
выражение;
while (условие) ;

Выражение может быть пустым выражением, или быть блоком выражений заключенных в скобки ({ . . . }). Выражение посвторяется до тех пор, пока условие истинно.
Следующий пример демонстрирует do...while версию счетчика:
do {
sys->print("%d ", n++);
}
while (n <= 100);

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

Переходы
В Limbo есть два выражения для выполнения безусловных переходов: break и continue.

Выражение break
Выражение break может использоваться двумя способами. Оно может прерывать выражения case и alt, и также оно может насильно прерывать выполнение циклов (for, while, do...while) пропуская проверку условия. Смотри раздел Метки/Labels на странице 2-40 об использовании break в goto-подобных конструкциях). Если циклы вложенные, break осуществляет выход из того цикла, где он вызван. Например:
for (t:=l; t<=5; t++) {
i := 1;
for (;;) {
sys->print("%d ", i++);
if (i == 10) break;
}
}

код выше печатает пять раз числа от 1 до 9. Выражение break прерывает выполнение самого внутреннего бесконечного цикла.

rapidfx 22.04.09 в 10:48

0

Выражение continue
Выражение continue похоже на break. В отличии от принудительного прерывания оно начинает новую итерацию цикла. (Смотри раздел Метки/Labels для детальной информации об использовании меток для goto-подобных программ)
Например, следующий код поледовательно читает байты из файла, пока no bytes are read: // посмотреть исходник
for(;;) {
n = sys->read(fd, buf, Ten buf);
if(n <= 0) break;
if(int buf[0] != 'm' || n != 37) continue;
}

Метки
В Limbo метки ведут себя подобно выражениям goto из языка C, с той лишь разницей, что они могут использоваться только в выражениях break и continue внутри циклов for, do, while и в выражениях case и alt. Это передает управление в конец маркированного блока.
Есть два типичных способа использования меток в Limbo.
Один позволяет прерваться внутреннему циклу или продолжиться внешнему. Другой состоит в том, чтобы прервать внешний цикл изнутри case или alt. Рассмотрим следующий пример. Одна или больше опций могут передаваться и обрабатываться командой:
loop: for(i := 0; i < Ten in; i++){ case in[i] { 'v' =>
opt |= verbose; 's' =>
opt |= suppress; 'o' =>
opt |= outFile; * =>
break loop; } }
До тех пор, пока n-ый элемент в строке будет одной из валидных опций (v, s, или o), будет продолжаться обработка; цикл for начнет следующую итерацию. Как только появится другой символ, процесс прерывается вызовом break.
Если с break не используется метка, цикл for просто начинает новую итерацию. .следующий пример показывает использованиеметки для выхода из вложенных циклов.
u := 0;
line := "" URL := "";
outer: for (i := 0; i < Ten line; i++) { if Cline[i] == '<') {
while (line[++i] != '>') {
if (line[i:i+6] == "A HREF") { i += 6;
if (line[i] == '=') { i += 2;
while (line[i] != "") URL[u++] = 1ine[i++]; } } break outer;
} } }

Exit/Выход
Выражение exit прерывает исполнение нити и освобождает все ресурсы которые она занимала. В отличии от C функции exit() она не возвращает значение системе.

Функции
Функции в Limbo, служат тем же самым целям что и в других структурированных процедурных языках - они содержат выражения выполняемые в момент запуска программы. Общий формат записи функций следущий:
function_name (arguments) : return_type {
statements
}
Аргументы это разделенный запятыми список имен переменных и ассоциированных с ними типов которые передаются функции в момент вызова. Функция не обязана иметь параметры, но она должна обязательно иметь круглые скобки.
return_type указывает какой типа данных возвращает функция. Функция может возвращать любой тип. Она не обязана возвращать данные и компилятор Limbo не ожидает их возвращения.

Функция init
Когда программа выполняется в командном интерпретаторе Inferno, интерпретатор ищет специальную функцию с названием init. Эта функция сообщает Inferno где начинается выполнение программы. Это что-то аналогичное функции main() в C.
Функция init должна содержать два параметра: ref Draw->Context список строк

ref Draw->Context
ref Draw->Context используется для захвата контекста дисплея и определена в файле draw.m. Этот аргумент обязателен, даже если программа не использует графику. Если она не используется, то может быть названа nil.

list of string/ Список строк
Список строк это список аргументов переданных модулю от командной строки.Если программа не требует от командной строки никаких аргументов, то он может быть назван nil. Его фугкциональность эвивалентна переменной argv в C, и по принятому соглашению названа argv в Limbo.

Объявления функций
Все публичные функции модуля должны быть объявлены. Объявление функций в Limbo подобно прототипу функции в C и C++. Они предоставляет информацию о типа аргументов и о возвращаемых функцией значениях. Эта информация позволяет компилятору осуществлять проверку типов что обеспечивает безопасную передачу типов во время выполнения.
Общая форма объявления функции в Limbo:
function_name: fr\{arguments) : return_type;
Объявления функций подобны определению данных. Употребление ключевого слова fn указывает на то, что объект имеет тип "функция".
Функции не объявлены в модуле являются приватными. Внешние модули не могут получить к ним доступ. Компилятор продолжает осуществлять проверку типов и во время вызова функции и во время объявления.
Следующий пример модуля содержит две функции, публичную функцию init и приватную функцию.
Program Listing 2-3 funcx.b
1 implement command;
2
3 include "sys.m";
4 include "draw.m"; 5
6 sys: sys;
7
8 Command: module {
9 init: fn (nil: ref Draw->context, argv: list of string);
10 };
11
12 init (nil: ref Draw->context, argv: list of string) {
13 sys = load sys Sys->PATH;
14
15 for (i := 1; i <= 10; i++) {
16 sys->print(" %2d %4d\n", i, sqr(i));
17 }
18
19 sqr (n: int): int {
20 return (n*n);
21 }
Строка 9 содержит объявление функции init, Строки с 8 по 10 содержат объявление модуля.
Определение приватной функции приведено на Строках с 19 по 21, она не была объявлена в объявлении модуля.
К использованию публичных и приватных функций следует относится с величайшим вниманием особенно в отношении к моудлям которые загружают другие модули. Это подробнее обсуждается в Главе 3.

rapidfx 22.04.09 в 13:02

0

Функция init
Когда программа выполняется в командном интерпретаторе Inferno, интерпретатор ищет специальную функцию с названием init. Эта функция сообщает Inferno где начинается выполнение программы. Это что-то аналогичное функции main() в C.
Функция init должна содержать два параметра: ref Draw->Context список строк

ref Draw->Context
ref Draw->Context используется для захвата контекста дисплея и определена в файле draw.m. Этот аргумент обязателен, даже если программа не использует графику. Если она не используется, то может быть названа nil.

list of string/ Список строк
Список строк это список аргументов переданных модулю от командной строки.Если программа не требует от командной строки никаких аргументов, то он может быть назван nil. Его фугкциональность эвивалентна переменной argv в C, и по принятому соглашению названа argv в Limbo.

Объявления функций
Все публичные функции модуля должны быть объявлены. Объявление функций в Limbo подобно прототипу функции в C и C++. Они предоставляет информацию о типа аргументов и о возвращаемых функцией значениях. Эта информация позволяет компилятору осуществлять проверку типов что обеспечивает безопасную передачу типов во время выполнения.
Общая форма объявления функции в Limbo:
function_name: fr\{arguments) : return_type;
Объявления функций подобны определению данных. Употребление ключевого слова fn указывает на то, что объект имеет тип "функция".
Функции не объявлены в модуле являются приватными. Внешние модули не могут получить к ним доступ. Компилятор продолжает осуществлять проверку типов и во время вызова функции и во время объявления.
Следующий пример модуля содержит две функции, публичную функцию init и приватную функцию.
Program Listing 2-3 funcx.b
1 implement command;
2
3 include "sys.m";
4 include "draw.m"; 5
6 sys: sys;
7
8 Command: module {
9 init: fn (nil: ref Draw->context, argv: list of string);
10 };
11
12 init (nil: ref Draw->context, argv: list of string) {
13 sys = load sys Sys->PATH;
14
15 for (i := 1; i <= 10; i++) {
16 sys->print(" %2d %4d\n", i, sqr(i));
17 }
18
19 sqr (n: int): int {
20 return (n*n);
21 }
Строка 9 содержит объявление функции init, Строки с 8 по 10 содержат объявление модуля.
Определение приватной функции приведено на Строках с 19 по 21, она не была объявлена в объявлении модуля.
К использованию публичных и приватных функций следует относится с величайшим вниманием особенно в отношении к моудлям которые загружают другие модули. Это подробнее обсуждается в Главе 3.

rapidfx 24.04.09 в 04:50

0

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

Передача по Значению, Передача по Ссылке.
Из-за того, что в Limbo есть ограничения на использование указателей, не существует единого механизма вызова/передачи по ссылке. Ссылочные типы передаются по ссылке, все другие типы передаются по значению.
Когда данные передаются по значению, принимающая функция создает совершенно новую переменную содержащуюю значение переданного аргумента. Модификации переменнойне оказывают никакого воздействия значение в вызывающей функции.
Когда данные передаются по ссылке, принимающая функция берет ссылку, указатель, на значение переданного аргумента. Модификация информации в таком случае оказывает воздействие на значение в вызывающей функции.

Возвращаемые значения фунции
Как указывалось ранее, функция может возвращать, а может и не возвращать занчение некоторого типа.

rapidfx 20.04.09 в 12:38

0
Минутку...