Просмотр сообщений

В этом разделе можно просмотреть все сообщения, сделанные этим пользователем.


Темы - vlad

Страницы: [1] 2 3
1
Чего-то мне воображение рисует какие-то сложные схемы. Особенно для случая вызова внешних процедур (level - n) из локальных. Кто-нибудь разбирался как оно по Вирту должно быть?

2
Общий раздел / Oberon-07/13: заметки
« : Январь 13, 2014, 05:12:43 pm »
Буду кидать в эту тему впечатления от переписывания компилятора на оберон. С целью пересмотреть потом на предмет улучшений языка.

3
Общий раздел / Задачка на сортировку
« : Ноябрь 26, 2013, 09:10:57 pm »
Вобщем ничего такого сложного, но поскольку я на нее потратил сильно больше времени, чем казалось она того стоит (может просто тупил, да), то решил поделиться. ИМХО как вариант для собеседования тоже подойдет.

Дано: дерево в виде таблицы id'шек:
parent_id    id
--------------
10          1
10          2
20          3
-1          20
20          10
parent_id = -1 означает корень, корней может быть больше одного

Плучить: таблицу отсортированную так, что каждому следующему parent_id предшествует его "объявление" в виде id. Т.е., для данного примера это будет:
parent_id  id
--------------
-1          20
20          3
20          10
10          1
10          2

Решаем на любом языке (оберон как всегда интереснее всего :)
Решение на чистом SQL (из него все пошло) тоже интересно :)

4
Общий раздел / Добавить методы в О7/13
« : Ноябрь 13, 2013, 03:26:16 am »
Буду пробовать добавить методы в текущую реализацию. Предлагаю пообсуждать - кто в каком виде их хочет видеть.
Примерные вопросы:
- виртуальность (всегда или могут быть невиртуальные методы)
- абстрактные методы (можно попробовать без них)
- интерфейсы (возможно стоит отделить чисто методы от чисто данных)
- синтаксис (где объявлять - в TYPE или потом вместе с остальными процедурами)

За основу можно взять проверенное решение из ББ.

5
Общий раздел / Раскручиваем компилятор O7
« : Октябрь 27, 2013, 06:36:14 am »
Итак, свершилось: кусочек компилятора был переписан на обероне и скомпилирован самим компилятором. Надо сказать, что в этом что-то есть :) Типа непосредственного прикосновения к вечной проблеме курицы и яйца.

Код приведен в самом конце. Текущие впечатления от кодирования:
- В отсутствие методов запись "Stream.pos(stream)" конечно многословнее и воспринимается хуже, чем "stream.pos()".
- Дублирование в конце имени процедуры - жестокое и беспощадное. Программер должен страдать. Особенно если захочет переименовать. Смысла - 0. Если уж боремся за читабельность, то лучше запретить процедуры длиннее 10 строк...
- Точка с запятой после RETURN. Точнее требование ее отсутствия. Вот уж действительно мелочь, но вот чисто с эргономической точки зрения напрягает страшно. Удобство принесенное в жертву формализму.
- Тяжелый синтаксис. Даже если постараться не смотреть на КАПС. Т.е. я конечно в курсе незабвенного "синтаксического оверхеда", но вот в случае тривиальных процедур - обероновский код выглядит если не многословным, то многобуквенным. И даже "int" vs "INTEGER" начинает напрягать. Т.е. пока пишешь "int" в каком-нибудь С++ это кажется и не таким важным. Но вот пописав "INTGER" начинаешь ценить более короткий вариант.
MODULE Stream;
IMPORT JsString;

TYPE
    Type = POINTER TO RECORD
        s: JsString.Type;
        pos: INTEGER
    END;

    ReaderProc = PROCEDURE(c: CHAR): BOOLEAN;

PROCEDURE make*(text: JsString.Type): Type;
VAR result: Type;
BEGIN
    NEW(result);
    result.s := text;
    RETURN result
END make;

PROCEDURE eof*(self: Type): BOOLEAN;
    RETURN self.pos = JsString.len(self.s)
END eof;

PROCEDURE pos*(self: Type): INTEGER;
    RETURN self.pos
END pos;

PROCEDURE setPos*(self: Type; pos: INTEGER);
BEGIN
    ASSERT(pos <= JsString.len(self.s));
    self.pos := pos
END setPos;

PROCEDURE next*(self: Type; n: INTEGER);
BEGIN
    ASSERT(self.pos + n <= JsString.len(self.s));
    self.pos := self.pos + n;
END next;

PROCEDURE peekChar*(self: Type): CHAR;
BEGIN
    ASSERT(~eof(self));
    RETURN JsString.at(self.s, self.pos)
END peekChar;

PROCEDURE getChar*(self: Type): CHAR;
VAR result: CHAR;
BEGIN
    ASSERT(~eof(self));
    result := JsString.at(self.s, self.pos);
    INC(self.pos);
    RETURN result
END getChar;

PROCEDURE peekStr*(self: Type; len: INTEGER): JsString.Type;
VAR max: INTEGER;
BEGIN
    max := JsString.len(self.s) - self.pos;
    IF len > max THEN
        len := max;
    END
    RETURN JsString.substr(self.s, self.pos, len)
END peekStr;

PROCEDURE read*(self: Type; f: ReaderProc): BOOLEAN;
BEGIN
    WHILE ~eof(self) & f(peekChar(self)) DO
        next(self, 1);
    END
    RETURN ~eof(self)
END read;

PROCEDURE lineNumber*(self: Type): INTEGER;
VAR
    line: INTEGER;
    lastPos: INTEGER;
BEGIN
    lastPos := JsString.indexOf(self.s, 0DX);
    WHILE (lastPos # -1) & (lastPos < self.pos) DO
        INC(line);
        lastPos := JsString.indexOfFrom(self.s, 0DX, lastPos + 1);
    END;
    RETURN line + 1
END lineNumber;

END Stream.

6
Общий раздел / Допилить массивы в O7
« : Октябрь 12, 2013, 06:23:49 am »
По мотивам обсуждения строк в O7. Чего не хватает в языке, чтобы использовать ARRAY OF CHAR как строку (дабы не плодить дополнительных сущностей в виде типа STRING или еще чего). Все уперлось прежде всего в то, что нельзя создать массив заранее неизвестного размера. Вобщем изначально было понятно, что с динамическими массивами придется что-то придумывать, поэтому текущая идея вызревала давно. А конкретная проблема со строками позволяет проверить идею на практике.

Итак, предложение по расширению O7 динамическими массивами.
1. Массив может быть объявлен без указания размера, в этом случае считается, что он динамический:
TYPE
    A = ARRAY OF CHAR;
    R = RECORD a: ARRAY OF INTEGER END;
VAR
    a: ARRAY OF BOOLEAN;

2. Размер динамического массива по умолчанию - 0. Размер можно увеличить или уменьшить с помощью предопределенной функции RESIZE. Оставшиеся элементы после RESIZE сохраняют свое значение. Новые элементы имеют значение по умолчанию.

3. Присваивание массивов.
- Динамический массив присваивается другому динамическому массиву (конечно при сохранении типов элементов). После присваивания оба массива имеют одинаковый размер и содержимое.
- Обычный массив или открытый массив присваивается в динамический.
- Нельзя присвоить динамический массив в обычный или открытый (ошибка компиляции).

4. Открытые массивы (в формальных параметрах).
Синтаксис открытых массивов меняется - добавляется ключевое слово ANY, иначе они выглядят как динамические, хотя семантика различается (см. 3).
PROCEDURE p(VAR a1: ARRAY OF INTEGER; a2: ANY ARRAY OF CHAR; VAR a3: ANY ARRAY OF BOOLEAN);
a1 - динамический массив (содержимое и размер может меняться).
a2 - открытый массив (неизменяемый).
a3 - открытый массив (содержимое может меняться, но не размер).

Динамический массив в формальных параметрах может быть использован только с VAR. Без VAR он не имеет смысла, так как его размер не может быть изменен.

При подстановке фактических параметров:
- только динамический массив может быть использован если формальный параметр - динамический массив
- любой массив может быть использован если формальный параметр - открытый массив

5. Многомерные массивы.
Если динамический массив объявлен в качестве элемента другого массива, то он по-прежнему может иметь произвольный размер независимо от размера других элементов.
VAR a: ARRAY 2 OF ARRAY OF INTEGER;
...
RESIZE(a[0], 3);
RESIZE(a[1], 5);
ASSERT(LEN(a[0] = 3);
ASSERT(LEN(a[1] = 5);

Уфф. Вроде все. Критикуйте :)

7
Общий раздел / Делаем строку на O7
« : Октябрь 11, 2013, 05:54:37 am »
Итак, pure Oberon-07 (без динамических массивов). Пытаемся написать модуль для работы со строками любого размера. Минимальный варинт:
- создать строку из ARRAY OF CHAR
- получить размер за O(1)
- получить CHAR по индексу (не знаю как сделать за O(1), но можно попытаться сделать меньше, чем за O(n))
Вот, что у меня получилось:
MODULE String;
CONST
    offs8 = 8;
    offs16 = 8 + 16;
    offs32 = offs16 + 32;
    offs64 = offs32 + 64;
    offs128 = offs64 + 128;
    offs256 = offs128 + 256;
    offs512 = offs256 + 512;
    offs1024 = offs512 + 1024;
    chunkSize = 4096;
TYPE
    Type* = POINTER TO RECORD
        size: INTEGER;
        data: ARRAY 8 OF CHAR;
        next: POINTER TO Data16
    END;
    Data16 = RECORD
        data: ARRAY 16 OF CHAR;
        next: POINTER TO Data32
    END;
    Data32 = RECORD
        data: ARRAY 32 OF CHAR;
        next: POINTER TO Data64
    END;
    Data64 = RECORD
        data: ARRAY 64 OF CHAR;
        next: POINTER TO Data128
    END;
    Data128 = RECORD
        data: ARRAY 128 OF CHAR;
        next: POINTER TO Data256
    END;
    Data256 = RECORD
        data: ARRAY 256 OF CHAR;
        next: POINTER TO Data512
    END;
    Data512 = RECORD
        data: ARRAY 512 OF CHAR;
        next: POINTER TO Data1024
    END;
    Data1024 = RECORD
        data: ARRAY 1024 OF CHAR;
        next: POINTER TO Chunk
    END;
    Chunk = RECORD
        data: ARRAY chunkSize OF CHAR;
        next: POINTER TO Chunk
    END;

PROCEDURE fromArray*(a: ARRAY OF CHAR): Type;
VAR
    result: Type;
   
    PROCEDURE copy(from: INTEGER; VAR dst: ARRAY OF CHAR): BOOLEAN;
    VAR
        i: INTEGER;
    BEGIN
        WHILE (i < LEN(dst)) & (i + from < LEN(a)) DO
            dst[i] := a[i + from];
            INC(i);
        END;
        RETURN i + from < LEN(a)
    END copy;

    PROCEDURE copyToChunks();
    VAR
        i: INTEGER;
        from: INTEGER;
        chunk: POINTER TO Chunk;
    BEGIN
        from := offs1024;
        NEW(chunk);
        result.next.next.next.next.next.next.next.next := chunk;
        WHILE from < LEN(a) DO
            IF i = LEN(chunk.data) THEN
                NEW(chunk.next);
                chunk := chunk.next;
                i := 0;
            END;
            chunk.data[i] := a[from];
            INC(i);
            INC(from);
        END;
    END copyToChunks;

BEGIN
    NEW(result);
    IF copy(0, result.data) THEN
        NEW(result.next);
        IF copy(offs8, result.next.data) THEN
            NEW(result.next.next);
            IF copy(offs16, result.next.next.data) THEN
                NEW(result.next.next.next);
                IF copy(offs32, result.next.next.next.data) THEN
                    NEW(result.next.next.next.next);
                    IF copy(offs64, result.next.next.next.next.data) THEN
                        NEW(result.next.next.next.next.next);
                        IF copy(offs128, result.next.next.next.next.next.data) THEN
                            NEW(result.next.next.next.next.next.next);
                            IF copy(offs256, result.next.next.next.next.next.next.data) THEN
                                NEW(result.next.next.next.next.next.next.next);
                                IF copy(offs512, result.next.next.next.next.next.next.next.data) THEN
                                    copyToChunks();
                                END;
                            END;
                        END;
                    END;
                END;
            END;
        END;
    END;

    result.size := LEN(a);
    RETURN result
END fromArray;   

PROCEDURE size*(s: Type): INTEGER;
    RETURN s.size
END size;

PROCEDURE at*(s: Type; index: INTEGER): CHAR;
VAR
    result: CHAR;

    PROCEDURE chunkAt(): CHAR;
    VAR
        i: INTEGER;
        chunk: POINTER TO Chunk;
    BEGIN
        chunk := s.next.next.next.next.next.next.next.next;
        FOR i := 0 TO ((index - offs1024) DIV chunkSize) - 1 DO
            chunk := chunk.next;
        END;
        RETURN chunk.data[(index - offs1024) MOD chunkSize]
    END chunkAt;

BEGIN
    IF index < offs8 THEN
        result := s.data[index];
    ELSIF index < offs16 THEN
        result := s.next.data[index - offs8];
    ELSIF index < offs32 THEN
        result := s.next.next.data[index - offs16];
    ELSIF index < offs64 THEN
        result := s.next.next.next.data[index - offs32];
    ELSIF index < offs128 THEN
        result := s.next.next.next.next.data[index - offs64];
    ELSIF index < offs256 THEN
        result := s.next.next.next.next.next.data[index - offs128];
    ELSIF index < offs512 THEN
        result := s.next.next.next.next.next.next.data[index - offs256];
    ELSIF index < offs1024 THEN
        result := s.next.next.next.next.next.next.next.data[index - offs512];
    ELSE
        result := chunkAt();
    END
    RETURN result
END at;

END String.

Тестируем это безобразие:
MODULE TestString;
IMPORT String;
CONST
    longStringSize = 1024 * 100 + 1;
VAR
    s: String.Type;
    testArray: ARRAY longStringSize OF CHAR;
    i: INTEGER;
BEGIN
    s := String.fromArray("abc");
    ASSERT(String.size(s) = 3);
    ASSERT(String.at(s, 0) = "a");
    ASSERT(String.at(s, 1) = "b");
    ASSERT(String.at(s, 2) = "c");

    FOR i := 0 TO LEN(testArray) - 1 DO
        testArray[i] := CHR(i);
    END;
    s := String.fromArray(testArray);
    ASSERT(String.size(s) = LEN(testArray));
    FOR i := 0 TO LEN(testArray) - 1 DO
        ASSERT(testArray[i] = String.at(s, i));
    END;

END TestString.

8
Сейчас на гитхабе оформил релиз 1.0 (https://github.com/vladfolts/oberonjs/releases) и решил здесь подытожить:

- Старт проекта: 2012/07/01 21:20:00. Т.е., в разработке немного больше года. С одной стороны довольно долго, с другой - процесс был исключительно ленивый в виде "если почитать перед сном особо нечего, то можно пописать компилятор". Я бы оценил время полноценной разработки ~1 человеко-месяц. Надо признать, что это действительно мизер (оберонщики правы). Хотя я и был под впечатлением, что будет еще быстрее.

- Размер получившегося .js - 142,883 байт (с нормальным форматированием, читаемым идентификаторами и пробелами вместо табов). Тут я тоже ошибся - изначально думал, что уложусь в 64кб. Но если посмотреть другие js-проекты, то это и немного. Например, jshint (это lint для жабаскрипта) - 169,462 байт.

- Виртовский репорт. Гордится/похвалятся 16 страницами - это кощунство. А сравнивать с описаниям других языков - невежество. Репорт категорически требует уточнений. Много уточнений. В текущем виде он годится только как подсказка студентам, сдающим лабы на обероне (и в таком применении он может быть даже ценнее полноценного стандарта, но тем не менее).

- Никто не кинулся переписывать жабаскрипт на обероне. Это ожидаемо, просто отмечаю, что в случае оберона чуда тоже не произошло. Есть куча языков, компилируемых в жабаскрипт. Но, к сожалению, язык не главное. Нужна инфраструктура. И вот тут надо усиленно пилить всем тем, кто хочет писать на обероне под веб :)

- Дальнейшие планы. Собираюсь достать напильник и начать смотреть где пилить в процессе переписывания компилятора с жабаскрипта на оберон другой язык. Пока с обратной совместимостью с оригинальными обероном. Это никак не поможет в популяризации "нового языка для веба" (см. про инфраструктуру), но зато интересно :)

9
Общий раздел / Открытые массивы в O7
« : Сентябрь 20, 2013, 06:29:32 am »
Итак, продолжаем угадывать, что имел ввиду Вирт.
Открытыем массивы присутствуют только в описании формальных параметров процедур:
FormalType = {ARRAY OF} qualident.

Причем неоткрытый массив прописать нельзя. Соответственно вот такой код невалиден:
PROCEDURE p(a: ARRAY 3 OF INTEGER); (* валидно в ББ *)

Но. Можно написать вот так:
TYPE A: ARRAY 3 OF INTEGER;
PPROCEDURE p(a: A);

Что это - ошибка или так и было задумано.

Дальше. Можно написать вот так:
PROCEDURE p(a: ARRAY OF ARRAY OF INTEGER); (* невалидно в ББ *)

С точки зрения компилятора тут присутствует дополнительное усложнение в вычислении нужного смещения в памяти (если массив реализован через кусок памяти). Поэтому, видимо, оно и запрещено в ББ. Тем не менее Вирт решил это разрешить...

10
Общий раздел / Пробуем писать на O7
« : Август 27, 2013, 05:04:25 pm »
В порядке разминки - давно правильны циклов не было :)
Итак, на входе массив массивов и разделитель. Надо все это объединить в одну последовательность. Т.е.: [[1, 2, 3], [4,5]], 10 -> [1, 2, 3, 10, 4, 5]
Экспериментировать можно прямо здесь: http://oberspace.dyndns.org/oberonjs.html

PROCEDURE do(input: ARRAY OF ARRAY OF INTEGER; separator: INTEGER);
...

P.S. Чувствую ЦД где-то рядом ;)

11
Согласно репорту указатели можно сравнивать только на равенство. На больше/меньше - нельзя. В чем глубокий смысл такого ограничения? Недостаток вполне очевиден - указатели нельзя использовать в качестве ключа поиска.

12
Общий раздел / Строки в О7
« : Март 29, 2013, 03:59:38 pm »
Смотрим оригинальный репорт и пытаемся понять, что можно делать со строками (строковыми литералами).
1. объявить CONST
2. использовать в CASE (односимвольные)
3. присвоить в ARRAY N OF CHAR (забив нулями лишние ячейки)

Но нигде не вижу, что строку можно отдать в вызов процедуры. Т.е., WriteLn("Hello, World!") ну никак не написать. Только вот так:
VAR s: ARRAY 16 OF CHAR;
...
s := "Hello, World!";
WriteLn(s);

Можно предположить (из общих соображений), что строки должны автоматом конвертится в открытый ARRAY OF CHAR, причем константный (не VAR). Но в репорте даже намека на это нет (или я не вижу?).

13
I suggest to collect here the questions not covered by original language report (mostly raised during implementation of Oberon-07 compiler). Later this list will be used to ask Wirth for clarification.

1. Report revision 22.9.2011. ARRAY or RECORD as a value parameter. Original report quote: "If a value parameter is structured (of array or record type), no assignment to it or to its elements are
permitted."
What about pointer elements/fields? Is it possible to change records pointed by array elements or record fields. E.g.:
PROCEDURE p(record: RecordType);
...
record.pointer.field := 123;

14
Не нашел в репорте ни слова про возможность/невозможность рекурсивного описания:
TYPE ProcType = PROCEDURE(): ProcType; (* В ББ работает *)

Кроме того, Вирт выпилил (по сравнению с ББ) возможность описания типа "по месту", оставив только qualident:
TYPE ProcType = PROCEDURE(): PROCEDURE; (* ошибка синтаксиса О7/11 *)

15
Сабж применительно к современному универсальному ЯП (и оберону в частности)? Потому как мороки с обходом такого контроля (см. параллельную ветку) может быть больше, чем пользы (поймать ошибку в арифметических вычислениях). Особенно если речь идет о чем-то довольно низкоуровневом типа О-7. Ну и сделать такие проверки эффективными довольно сложно (без наворачивания оптимизатора).

Страницы: [1] 2 3