Oberon space
General Category => Общий раздел => Тема начата: Geniepro от Июль 04, 2013, 08:58:57 pm
-
"Man or boy?" Donald Knuth (http://www.chilton-computing.org.uk/acl/applications/algol/p006.htm)
http://rosettacode.org/wiki/Man_or_boy_test
Попробовал решение для онлайн-компилятора Оберона-7:
MODULE test;
IMPORT JS;
TYPE Function = PROCEDURE ():INTEGER;
PROCEDURE F0(): INTEGER; BEGIN RETURN 0 END F0;
PROCEDURE F1(): INTEGER; BEGIN RETURN 1 END F1;
PROCEDURE Fn1(): INTEGER; BEGIN RETURN -1 END Fn1;
PROCEDURE A(ink: INTEGER; x1, x2, x3, x4, x5: Function): INTEGER;
VAR
res : INTEGER;
k : INTEGER;
PROCEDURE B(): INTEGER;
BEGIN
DEC(k);
RETURN A(k, B, x1, x2, x3, x4)
END B;
BEGIN
k := ink;
IF k <= 0 THEN
res := x4() + x5();
ELSE
res := B();
END;
RETURN res
END A;
BEGIN
JS.alert(A(10, F1, Fn1, Fn1, F1, F0))
END test.
а он мне раз и заявил: "Нельзя, мол, ссылаться на локальную процедуру"!
Компилятор от akron1 с ним солидарен...
Я что-то запямятовал -- в Обероне-7 действительно есть такое ограничение?
Если есть, значит для Оберона-7 нельзя сделать "взрослый" компилятор (Man)? Язык вынуждает компиляторы быть "детскими" (Boy)?
ЗЫ. Ну и моё решение для хацкеля (зря что ли его делал):
import Data.IORef
import Control.Monad
main = do
k <- newIORef 10
print =<< a k (return 1) (return (-1)) (return (-1)) (return 1) (return 0)
a in_k x1 x2 x3 x4 x5 = do
k <- newIORef =<< readIORef in_k
let b = do
k `modifyIORef` (\x -> x-1)
a k b x1 x2 x3 x4
k' <- readIORef k
if k' <= 0 then liftM2 (+) x4 x5 else b
-
Есть такое ограничение:
6.5. Procedure types
Variables of a procedure type T have a procedure (or NIL) as value. If a procedure P is assigned to
a procedure variable of type T, the (types of the) formal parameters of P must be the same as those
indicated in the formal parameters of T. The same holds for the result type in the case of a function
procedure (see 10.1). P must not be declared local to another procedure, and neither can it be a
standard procedure.
-
Я что-то запямятовал -- в Обероне-7 действительно есть такое ограничение?
Оно всегда так было в паскалях. Типа - скажи нет скрытой неэффективности. Не говоря об усложнении компилятора :) В общем случае связанный с локальной процедурой контекст (локальные переменные) надо где-то хранить (в куче). Кроме того, есть еще VAR-параметры... Короче не работает это для оберонов.
P.S. Хотя с точки зрения транслятора в жабаскрипт никаких проблем нет, т.е. это чисто искусственное ограничение, чтобы остаться верным букве репорта.
-
Я что-то запямятовал -- в Обероне-7 действительно есть такое ограничение?
Оно всегда так было в паскалях. Типа - скажи нет скрытой неэффективности. Не говоря об усложнении компилятора :) В общем случае связанный с локальной процедурой контекст (локальные переменные) надо где-то хранить (в куче). Кроме того, есть еще VAR-параметры... Короче не работает это для оберонов.
При этом в Модуле-3 это работает...
-
Думаю обсуждение пойдет бодрее, если ты сформулируешь задачу на русском.
-
Думаю обсуждение пойдет бодрее, если ты сформулируешь задачу на русском.
На русском она описана тут: "О распределении памяти при выполнении теста Кнута" (http://rsdn.ru/article/pl1/PL1ex7/pl1ex7.xml)
Там суть в том, что бы вызвать взаимную рекурсию двух процедур, одна из которых является локальной для другой и имеет доступ к объемлющему контексту текущего вызова (замыкание с мутабельной переменной).
При этом уже на небольших параметрах происходит большая нагрузка на стек...
-
Я что-то запямятовал -- в Обероне-7 действительно есть такое ограничение?
P.S. Хотя с точки зрения транслятора в жабаскрипт никаких проблем нет, т.е. это чисто искусственное ограничение, чтобы остаться верным букве репорта.
А может стоит убрать это ограничение? Ведь данный транслятор не предназначен для генерации кода для мелкоконтроллеров, и в нём оно не имеет особого смысла...
-
Я что-то запямятовал -- в Обероне-7 действительно есть такое ограничение?
P.S. Хотя с точки зрения транслятора в жабаскрипт никаких проблем нет, т.е. это чисто искусственное ограничение, чтобы остаться верным букве репорта.
А может стоит убрать это ограничение? Ведь данный транслятор не предназначен для генерации кода для мелкоконтроллеров, и в нём оно не имеет особого смысла...
Это нарушит совместимость и создаст еще один диалект. Кроме того, современный Оберон, как и раньше, позиционируется как язык общего назначения, а не только для микроконтроллеров
.
-
Я что-то запямятовал -- в Обероне-7 действительно есть такое ограничение?
P.S. Хотя с точки зрения транслятора в жабаскрипт никаких проблем нет, т.е. это чисто искусственное ограничение, чтобы остаться верным букве репорта.
А может стоит убрать это ограничение? Ведь данный транслятор не предназначен для генерации кода для мелкоконтроллеров, и в нём оно не имеет особого смысла...
Это нарушит совместимость и создаст еще один диалект. Кроме того, современный Оберон, как и раньше, позиционируется как язык общего назначения, а не только для микроконтроллеров .
Да кому нужна эта совместимость оберонов при таком неполном их описании?
-
Нам нужен. Что бы наши компиляторы были совместимы хотя бы на базовом уровне.
-
Нам нужен. Что бы наши компиляторы были совместимы хотя бы на базовом уровне.
Ты рассуждаешь как схоласт, а не как истинный оберонщик, последователь Вирта.
Ведь что говорят оберонщики: "Вот вам минимальное языковое ядро, для своей задачи расширяйте его как хотите..."
-
Ведь что говорят оберонщики: "Вот вам минимальное языковое ядро, для своей задачи расширяйте его как хотите..."
полный п..ж они говорят.. "Вот вам минимальное языковое ядро... и на все ваши задачи его вполне хватит"
-
Попробовал решение для онлайн-компилятора Оберона-7:
...
а он мне раз и заявил: "Нельзя, мол, ссылаться на локальную процедуру"
Может, получится выкрутиться средствами модуля JS?
-
Попробовал решение для онлайн-компилятора Оберона-7:
...
а он мне раз и заявил: "Нельзя, мол, ссылаться на локальную процедуру"
Может, получится выкрутиться средствами модуля JS?
Проще уж сразу на JS сделать. На JS тут проблемы не возникает:
var JS = function(){return this;}();
var test = function (){
function F0(){
return 0;
}
function F1(){
return 1;
}
function Fn1(){
return -1;
}
function A(ink/*INTEGER*/, x1/*Function*/, x2/*Function*/, x3/*Function*/, x4/*Function*/, x5/*Function*/){
var res = 0;
var k = 0;
function B(){
--k;
return A(k, B, x1, x2, x3, x4);
}
k = ink;
if (k <= 0){
res = x4() + x5();
}
else {
res = B();
}
return res;
}
JS.alert(A(10, F1, Fn1, Fn1, F1, F0));
}();
-
Ты рассуждаешь как схоласт, а не как истинный оберонщик, последователь Вирта.
Ведь что говорят оберонщики: "Вот вам минимальное языковое ядро, для своей задачи расширяйте его как хотите..."
Минимальное "расширение", которое практически не повлияет на эффективность (не требует GC и кучи) и на другие аспекты языка, а также сохранит пресловутую "герметичность", я представляю так:
- Вводится маркер для параметра процедуры (по аналогии с VAR), что этот параметр процедурного типа может быть локальной процедурой из процедуры выше по стеку. Например некий LOCAL:
PROCEDURE p(f: LOCAL PROCEDURE);
- Такая "LOCAL f" может быть только вызвана или передана в другую процедуру, но не может быть приведена к обычной процедурной переменной и не может быть передана по ссылке (VAR).
- Обычная процедурная переменная или процедура может быть передана как LOCAL (хотя можно сделать более интересно, но это отдельная тема).
Такое расширение позволит решить оригинальную задачу. Кроме того, можно попытаться написать хоть какую-нибудь библиотеку алгоритмов - тот же sort/find/for_each без тех ужасов, которые надо городить сейчас. И вообще появятся хоть какие-то зачатки функционального программирования в обероне. В оригинальном виде локальные процедуры в обероне имеют совсем мало смысла (не зря их Вирт собирается выпилить).
-
Народ, сабжевая задача, мягко говоря, не является приоритетом.
До лепления расширений нужно сделать две вещи:
1) реализовать язык полностью.
2) написать уточненный репорт.
PS. Полагаю решить эту задачу на Оберон-07/11 можно. Вечером подумаю.
-
Задача Кнута показывает, что Оберон-07, вроде бы продолжающий алголовскую традицию языков, является тем не менее "детским" (Boy) языком, и это при том что уже в 60-х гг. прошлого века были "взрослые" (Man) реализации Алгола-60...
-
Код из первого поста в А2 отработал 11 раз (каждая процедура) и трапнулся с access violation
-
Код из первого поста в А2 отработал 11 раз (каждая процедура) и трапнулся с access violation
Не совсем понял, что это значит, но там явно что-то не то в А2.
Количество вызовов процедуры А должно быть равно 722 раз, результат = -67.
-
ну что происходит - при вызове A из B происходит реальный вызов A
-
ну что происходит - при вызове A из B происходит реальный вызов A
А версия контекста процедуры А при этом какая? Новая для каждого вызова В или одна общая для всех вызовов А?
-
Я взял из статьи код для М3, адаптировал для запуска в А2, и у меня получился совсем другой результат, чем при использовании кода из первого поста.
результат - stack overflow
проверь таки код
-
Я взял из статьи код для М3, адаптировал для запуска в А2, и у меня получился совсем другой результат, чем при использовании кода из первого поста.
результат - stack overflow
проверь таки код
Какой код проверить? Мой Обероновский (переделанный из варианта для Модулы-3 из той статьи на http://rosettacode.org/wiki/Man_or_boy_test#Modula-3 (http://rosettacode.org/wiki/Man_or_boy_test#Modula-3))? Так он невалиден для Оберона-07.
Я приводил код на JS http://oberspace.dyndns.org/index.php/topic,518.msg17329.html#msg17329 (http://oberspace.dyndns.org/index.php/topic,518.msg17329.html#msg17329), который я получил с помощью небольшого трюка Владовским онлайн-компилятором -- его результат совпал с результатом моего варианта на хаскелле и кучей других вариантов этого теста.
Приведи свой код для A2, посмотрим, в чём отличия...
-
Если есть, значит для Оберона-7 нельзя сделать "взрослый" компилятор (Man)? Язык вынуждает компиляторы быть "детскими" (Boy)?
Вопрос поставлен не корректно. Man or Boy - это про реализации Алгола-60. По сути, это тест на корректность реализации того языка. А конкретно - проверка на вполне конкретную его фенечку.
Собственно ни Man ни Boy компилятор Оберона быть не может.
Про что тест, и в чем его соль, хорошо описано в Вики: http://en.wikipedia.org/wiki/Man_or_boy_test
Собственно реализовать то что там есть, можно хоть на Обероне-07/11 хоть на Си. Проблем не вижу. Но, еще раз, тест не про это. Тест про корректность компилятора Алгола-60.
-
Собственно реализовать то что там есть, можно хоть на Обероне-07/11 хоть на Си. Проблем не вижу. Но, еще раз, тест не про это. Тест про корректность компилятора Алгола-60.
Не могу согласиться. Да, в те годы у Кнута выбор был небольшой, поэтому он и ограничился Алголом. Однако это не значит, что нельзя использовать этот тест для оценки реализаций других языков, имеющих необходимые для этого теста средства -- передачу процедур (в том числе локальных) как параметры с сохранением их контекста.
Другое дело, что Оберон-07 (а также Компонентный Паскаль) такой возможности не имеет, поэтому для него этот тест не имеет смысла...
-
ну что происходит - при вызове A из B происходит реальный вызов A
А версия контекста процедуры А при этом какая? Новая для каждого вызова В или одна общая для всех вызовов А?
Вложенная процедура B - логически является частью процедуры А, поэтому при вызове B используется общий фрейм с А. Вызовы А из А являются рекурсивными, при этом создаётся новый фрейм, а так как B это составная часть A, то вызов A из B тоже рекурсивен, и иное было бы странно.
-
Подумал над задачкой. Самое смешное, что на Обероне (Oberon/Oberon-07/Oberon-2/CP) похоже не удастся обойтись без использования динамической памяти (кучи), стека не достаточно. Ведь в Обероне нельзя получить указатель на переменную, что на стеке. Поэтому решение в стиле Си не прокатит.
Ну, или использовать SYSTEM, но в случае как минимум Oberon-07/11 наличие SYSTEM'а не гарантировано.
Так что NEW в зубы и руками городить рядом с нормальным стеком, стек своих фреймов, в куче.
-
Какой код проверить? Мой Обероновский (переделанный из варианта для Модулы-3 из той статьи на http://rosettacode.org/wiki/Man_or_boy_test#Modula-3 (http://rosettacode.org/wiki/Man_or_boy_test#Modula-3))? Так он невалиден для Оберона-07.
Вариант с локальной переменной не идентичен варианту М3.
Приведи свой код для A2, посмотрим, в чём отличия...
Он полностью соответствует коду М3 (за исключением исправленного синтаксиса, замены INTEGER->LONGINT и наличия счетчиков
MODULE TestM;
IMPORT Commands;
TYPE Function = PROCEDURE ():LONGINT;
VAR ACount, BCount : LONGINT;
PROCEDURE A(k: LONGINT; x1, x2, x3, x4, x5: Function): LONGINT ;
PROCEDURE B(): LONGINT ;
BEGIN
INC(BCount);
DEC(k);
RETURN A(k, B, x1, x2, x3, x4);
END B;
BEGIN
INC(ACount);
IF k <= 0 THEN
RETURN x4() + x5();
ELSE
RETURN B();
END;
END A;
PROCEDURE F0(): LONGINT ; BEGIN RETURN 0; END F0;
PROCEDURE F1(): LONGINT ; BEGIN RETURN 1; END F1;
PROCEDURE Fn1(): LONGINT ; BEGIN RETURN -1; END Fn1;
PROCEDURE Do*( context : Commands.Context );
VAR nums : LONGINT;
BEGIN
ACount := 0; BCount := 0;
IF context.arg.GetInteger( nums, FALSE ) THEN
(* context.out.Int( A(nums, F1, Fn1, Fn1, F1, F0), 10 );*)
TRACE(A(nums, F1, Fn1, Fn1, F1, F0), ACount, BCount)
END;
END Do;
PROCEDURE ShowCounts*;
BEGIN
TRACE(ACount, BCount);
END ShowCounts;
BEGIN
END TestM.
SystemTools.Free TestM ~
TestM.Do 0 ~
TestM.ShowCounts ~
Максимальное значение параметра, при котором оно работает( и выдаёт результаты идентичные контрольной таблице) - 3, больше - трап от переполнения стека активности (потока).
PS: TRACE выводит результат в KernelLog
-
Чуть изменил процедуру запуска теста - теперь принимается список параметров:
PROCEDURE Do*( context : Commands.Context );
VAR nums : LONGINT;
BEGIN
WHILE context.arg.GetInteger( nums, FALSE ) DO
ACount := 0; BCount := 0;
TRACE(A(nums, F1, Fn1, Fn1, F1, F0), ACount, BCount)
END;
END Do;
Соответственно, коммандер теперь выглядит так:
TestM.Do 0 1 2 3 ~
-
Вариант с локальной переменной не идентичен варианту М3.
С этой локальной переменной компиляторо- (или языково-) зависимые сложности -- где-то параметр k передаётся по ссылке, и тогда нужно заводить её локальную копию, где-то по значению, и тогда этого не нужно делать, а в Аде, например, она не изменяемая и поэтому опять нужно заводить её копию...
Попробуй всё же вот так:
PROCEDURE A(ink: LONGINT; x1, x2, x3, x4, x5: Function): LONGINT ;
VAR k: LONGINT;
PROCEDURE B(): LONGINT ;
BEGIN
INC(BCount);
DEC(k);
RETURN A(k, B, x1, x2, x3, x4);
END B;
BEGIN
INC(ACount);
k := ink;
IF k <= 0 THEN
RETURN x4() + x5();
ELSE
RETURN B();
END;
END A;
Таблица результатов:
A( 1, 1, -1, -1, 1, 0) = 0
A( 2, 1, -1, -1, 1, 0) = -2
A( 3, 1, -1, -1, 1, 0) = 0
A( 4, 1, -1, -1, 1, 0) = 1
A( 5, 1, -1, -1, 1, 0) = 0
A( 6, 1, -1, -1, 1, 0) = 1
A( 7, 1, -1, -1, 1, 0) = -1
A( 8, 1, -1, -1, 1, 0) = -10
A( 9, 1, -1, -1, 1, 0) = -30
A(10, 1, -1, -1, 1, 0) = -67
A(11, 1, -1, -1, 1, 0) = -138
A(12, 1, -1, -1, 1, 0) = -291
A(13, 1, -1, -1, 1, 0) = -642
A(14, 1, -1, -1, 1, 0) = -1446
A(15, 1, -1, -1, 1, 0) = -3250
A(16, 1, -1, -1, 1, 0) = -7244
A(17, 1, -1, -1, 1, 0) = -16065
A(18, 1, -1, -1, 1, 0) = -35601
A(19, 1, -1, -1, 1, 0) = -78985
A(20, 1, -1, -1, 1, 0) = -175416
-
Попробуй всё же вот так:
Результат тот же - при параметре больше 3-х трап по переполнению стека активности (в нативе стек активности 128К, в WinAos - по умолчанию для CreateThread).
-
Попробуй всё же вот так:
Результат тот же - при параметре больше 3-х трап по переполнению стека активности (в нативе стек активности 128К, в WinAos - по умолчанию для CreateThread).
Странно, при k = 4 происходит всего 8 вызовов A и 7 вызовов B -- как может стек переполниться???
-
Подумал над задачкой. Самое смешное, что на Обероне (Oberon/Oberon-07/Oberon-2/CP) похоже не удастся обойтись без использования динамической памяти (кучи), стека не достаточно.
Да, я чего-то тоже не могу придумать как такое на обероне07 без NEW сделать...
-
Подумал над задачкой. Самое смешное, что на Обероне (Oberon/Oberon-07/Oberon-2/CP) похоже не удастся обойтись без использования динамической памяти (кучи), стека не достаточно.
Да, я чего-то тоже не могу придумать как такое на обероне07 без NEW сделать...
Да и на КП тоже. На всей линейке "классических" оберонов.
-
а он мне раз и заявил: "Нельзя, мол, ссылаться на локальную процедуру"!
Компилятор от akron1 с ним солидарен...
Совершенно правильное ограничение.... дабы отсекать сами попытки ваять такую замудрённую и двусмысленную чушь, как протаскивание контекстов процедур и т.п.
В том же JS "эти ваши замыкания" используются направо и налево от отсутствия модульности и удобного "не-ассемблерного" ООП (как и в ФП их главная роль - быть альтернативой ООП).
Я в JS сначала поигрался с замыканиями, когда обнаружил, что для удержания этого клубка в голове и его объяснения кому-то нужны дополнительные усилия, послал их в..., кроме совсем ограниченных случаев, типа обработки событий и т.п.
Хочется таскать контекст - задекларируй явно объект для этого контекста, как тип данных, - и таскай...
Неявщину, умолчания, запутанные клубки связей - в.... топку :)
-
а он мне раз и заявил: "Нельзя, мол, ссылаться на локальную процедуру"!
Компилятор от akron1 с ним солидарен...
Совершенно правильное ограничение.... дабы отсекать сами попытки ваять такую замудрённую и двусмысленную чушь, как протаскивание контекстов процедур и т.п.
В том же JS "эти ваши замыкания" используются направо и налево от отсутствия модульности и удобного "не-ассемблерного" ООП (как и в ФП их главная роль - быть альтернативой ООП).
Я в JS сначала поигрался с замыканиями, когда обнаружил, что для удержания этого клубка в голове и его объяснения кому-то нужны дополнительные усилия, послал их в..., кроме совсем ограниченных случаев, типа обработки событий и т.п.
Хочется таскать контекст - задекларируй явно объект для этого контекста, как тип данных, - и таскай...
Неявщину, умолчания, запутанные клубки связей - в.... топку :)
Ну, если в качестве эталона языка для использования замыканий брать js, то действительно, замыкания - богомерзкая дрянь :-) (впрочем, это к любой концепции относится - динамическая типизация, синтаксис, сборщик мусора, прототипное наследование - что ни рассматривай на примере js, всё дрянью покажется) А вот если брать что-то более вменяемое, ну там haskell например, то становится очевидно что это классная штука, которая делает код яснее, и не столь запутанным, как в случае ОО-языков.
И нет, у замыканий роль не в "замене" ООП. Программирование не крутится вокруг объектной методологии.
PS. А модульность вооще тут ортогональна. Замыкание контекста штука динамическая, а модуль штука статическая.
PPS. А ООП в Обероне, таки ассемблерного типа.
-
Странно, при k = 4 происходит всего 8 вызовов A и 7 вызовов B -- как может стек переполниться???
Нет, оказывается результаты моего варианта и твоего несколько отличаются:
Если взять к = 100,
то в твоём варианте:
ink = 0
k = 0
A called = 101
B called = 101
результат - TRAP access violation.
В моём же варианте:
k=94200383
A called=23547
B called=23547
и результат - TRAP stack overflow
в диапазоне 0..3 оба диапазона ведут себя одинаково и выдают правильные результаты.
-
Странно, при k = 4 происходит всего 8 вызовов A и 7 вызовов B -- как может стек переполниться???
Нет, оказывается результаты моего варианта и твоего несколько отличаются:
Если взять к = 100,
то в твоём варианте:
ink = 0
k = 0
A called = 101
B called = 101
результат - TRAP access violation.
В моём же варианте:
k=94200383
A called=23547
B called=23547
и результат - TRAP stack overflow
в диапазоне 0..3 оба диапазона ведут себя одинаково и выдают правильные результаты.
Боюсь, для того что бы процедура A выполнилась при k = 100, не хватит времени существования вселенной, ну и памяти тоже. Теоретический предел на 32-битной архитектуре -- k = 32, практический предел на этой архитектуре -- что-то около k = 27... Для того, чтобы получить результат для k = 100, нужно уже делать мемоизацию процедуры A...
Что-то не то генерирует компилятор в A2...
-
Что-то не то генерирует компилятор в A2...
Об этом и речь
-
Что-то не то генерирует компилятор в A2...
Об этом и речь
А что там вообще сказано в описании активного оберона про передачу ссылок на локальные прцедуры? Если в репорте сказано что это нельзя, то надо бы им багрепорт отправить )
Вапще, скачал на днях эту A2, запустил -- и увидел пустой сиреневый экран, а по краям какие-то непонятные серые полоски... Так что я не удивлён, что компилятор что-то не то генерирует у них )))
-
Хочется таскать контекст - задекларируй явно объект для этого контекста, как тип данных, - и таскай...
Неявщину, умолчания, запутанные клубки связей - в.... топку :)
Ага, ООП тоже вреден -- всякие хрупкие классы там, только для ГУИ годится, а для всего остального -- в топку...
И ваще назад в машкоды пора вернуться...
-
А что там вообще сказано в описании активного оберона про передачу ссылок на локальные прцедуры? Если в репорте сказано что это нельзя, то надо бы им багрепорт отправить )
Репорт давно устарел.
По трассировке вызовов видно, что при каждом вызове A стек опускается на 44 байта (стек для A - 32 байта и - 12 для B ), при вызове B адрес k для A и B одинаков(как и адреса всех параметров) и так продолжается до 5-го вызова B, после чего стек вдруг не минусуется на -12, а плюсуется +20, и всё, фрейм данных потерян, и дальше пошла полная фигня.
Вапще, скачал на днях эту A2, запустил -- и увидел пустой сиреневый экран, а по краям какие-то непонятные серые полоски... Так что я не удивлён, что компилятор что-то не то генерирует у них )))
у меня всё работает, загружается с флешки, никаких странных серых полосок нету. Но можешь WinAos попробовать.
-
А что там вообще сказано в описании активного оберона про передачу ссылок на локальные прцедуры? Если в репорте сказано что это нельзя, то надо бы им багрепорт отправить )
Репорт давно устарел.
По трассировке вызовов видно, что при каждом вызове A стек опускается на 44 байта (стек для A - 32 байта и - 12 для B ), при вызове B адрес k для A и B одинаков(как и адреса всех параметров) и так продолжается до 5-го вызова B, после чего стек вдруг не минусуется на -12, а плюсуется +20, и всё, фрейм данных потерян, и дальше пошла полная фигня.
Вапще, скачал на днях эту A2, запустил -- и увидел пустой сиреневый экран, а по краям какие-то непонятные серые полоски... Так что я не удивлён, что компилятор что-то не то генерирует у них )))
у меня всё работает, загружается с флешки, никаких странных серых полосок нету. Но можешь WinAos попробовать.
Народ, правильно ли я понял, что сий замечательный тест Кнута позволил выявить странный баг в компиляторе АО? То есть тест оказался таки полезным для одного из потомков Алгола-60?
-
Но можешь WinAos попробовать.
Я как раз WinAOS и пробовал...
-
Народ, правильно ли я понял, что сий замечательный тест Кнута позволил выявить странный баг в компиляторе АО? То есть тест оказался таки полезным для одного из потомков Алгола-60?
Пока неясно, может проблема связана с выравниванием в процессорах AMD, надо на интеле проверять
-
Народ, правильно ли я понял, что сий замечательный тест Кнута позволил выявить странный баг в компиляторе АО? То есть тест оказался таки полезным для одного из потомков Алгола-60?
Пока неясно, может проблема связана с выравниванием в процессорах AMD, надо на интеле проверять
Наврятли это баг процессора. Так что, опять таки, даже если это из за специфического выравнивания, то это баг компилятора.
-
Совершенно правильное ограничение.... дабы отсекать сами попытки ваять такую замудрённую и двусмысленную чушь, как протаскивание контекстов процедур и т.п.
Ну будет у тебя оформлено замыкание в виде кошерного объекта (хотя я ни разу не согласен, что это всегда нагляднее). Конкретно данный алгоритм все равно требует NEW, потому что в обероне нельзя получить указатель/ссылку на локальную RECORD. Или я что-то пропустил? Вообще хотелось бы увидеть решение на обероне - у меня с наскоку не получилось - слишком много кода получается и страшно. Можно начать с решения для ББ, потому что в О7 с "оформлением в виде объекта" возникает ненужный мусор.
В том же JS "эти ваши замыкания" используются направо и налево от отсутствия модульности и удобного "не-ассемблерного" ООП
Ой, только не надо начинать про жабаскрипт...
-
Наврятли это баг процессора. Так что, опять таки, даже если это из за специфического выравнивания, то это баг компилятора.
Ну код-то не самомодифицирующийся ) он на протяжении всех 100 вызовов один и тот же и на всем протяжении стек опускается правильно - на 32 и 12 байт соответственно, и только на 5-м вызове B он вместо 12 байт опускается на 8 (и BP соответственно)
-
вот как это выглядит:
TestMR@763 - процедура A
TestMR@497 - процедура B
TestMR@1652:currentSP= 67502080; (currentSP DIV 1024)= 65920;
TestMR@763:ADDRESSOF(k)= 0405FF38H; currentSP= 67501852; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FF38H; currentSP= 67501840; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FF0CH; currentSP= 67501808; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FF0CH; currentSP= 67501796; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FEE0H; currentSP= 67501764; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FEE0H; currentSP= 67501752; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FEB4H; currentSP= 67501720; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FEB4H; currentSP= 67501708; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FE88H; currentSP= 67501676; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FEA8H; currentSP= 67501668; (prevSP - currentSP)= 8;
TestMR@763:ADDRESSOF(k)= 0405FE60H; currentSP= 67501636; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FE60H; currentSP= 67501624; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FE34H; currentSP= 67501592; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FE34H; currentSP= 67501580; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FE08H; currentSP= 67501548; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FE08H; currentSP= 67501536; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FDDCH; currentSP= 67501504; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FDDCH; currentSP= 67501492; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FDB0H; currentSP= 67501460; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FDB0H; currentSP= 67501448; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FD84H; currentSP= 67501416; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FD84H; currentSP= 67501404; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FD58H; currentSP= 67501372; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FD58H; currentSP= 67501360; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FD2CH; currentSP= 67501328; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FD2CH; currentSP= 67501316; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FD00H; currentSP= 67501284; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FD00H; currentSP= 67501272; (prevSP - currentSP)= 12;
TestMR@763:ADDRESSOF(k)= 0405FCD4H; currentSP= 67501240; (prevSP - currentSP)= 32;
TestMR@497:ADDRESSOF(k)= 0405FCD4H; currentSP= 67501228; (prevSP - currentSP)= 12;
...
-
блин, тормоз, там, где стек опустился на 8-м - k стало равно 0, поэтому вызова B небыло.
-
ыыыы, целые в Оберонах = знаковые, вот тут она проблема и вылезла, твою мать
-
хотя, не, это я не подумавши
-
Но можешь WinAos попробовать.
Я как раз WinAOS и пробовал...
Перезалил полное дерево исходников А2 на на GitHub (https://github.com/metacore/A2OS). Это точно работает, ибо это одна из моих рабочих копий. Основана на ревизии 5348 (текущая 5355).
По сравнению с оригиналом поправлено 4 файла(поправлены сигнатуры нескольких процедур в UDP.Mod для синхронизации с изменениями интерфейса, в оригинале изменения внести видимо забыли, поправлен мелкий баг в FoxCompiler.Mod - перепутан порядок следования параметров, убрана зависимость от Streams в FATVolumes и заменен pci.ids на свежий). Эти изменения не затрагивают WinAos.
Ну и полностью пересобраны модули WinAos.
-
Итак, работающее решение на O7. Обратите внимание на приседание с дополнительной процедурной переменной pB - из-за невозможности предварительно задекларировать B. Может кто-нибудь предложит более элегантное решение, ибо текущее просто ужасно.
MODULE test;
IMPORT JS;
TYPE
PState = POINTER TO State;
Function = PROCEDURE(s: PState): INTEGER;
State = RECORD
f: Function;
k: INTEGER;
x1, x2, x3, x4, x5: PState
END;
VAR
pB: Function;
init: PState;
PROCEDURE call(s: PState): INTEGER;
RETURN s.f(s)
END call;
PROCEDURE makeEmptyState(f: Function): PState;
VAR
result: PState;
BEGIN
NEW(result);
result.f := f;
RETURN result
END makeEmptyState;
PROCEDURE makeState(f: Function; k: INTEGER; x1, x2, x3, x4, x5: PState): PState;
VAR
result: PState;
BEGIN
result := makeEmptyState(f);
result.k := k;
result.x1 := x1;
result.x2 := x2;
result.x3 := x3;
result.x4 := x4;
result.x5 := x5;
RETURN result
END makeState;
PROCEDURE F0(s: PState): INTEGER; BEGIN RETURN 0 END F0;
PROCEDURE F1(s: PState): INTEGER; BEGIN RETURN 1 END F1;
PROCEDURE Fn1(s: PState): INTEGER; BEGIN RETURN -1 END Fn1;
PROCEDURE A(s: PState): INTEGER;
VAR
res: INTEGER;
BEGIN
IF s.k <= 0 THEN
res := call(s.x4) + call(s.x5);
ELSE
res := call(makeState(pB, s.k, s.x1, s.x2, s.x3, s.x4, s.x5));
END;
RETURN res
END A;
PROCEDURE B(s: PState): INTEGER;
BEGIN
DEC(s.k);
RETURN call(makeState(A, s.k, s, s.x1, s.x2, s.x3, s.x4))
END B;
BEGIN
pB := B;
JS.alert(call(makeState(
A,
10,
makeEmptyState(F1),
makeEmptyState(Fn1),
makeEmptyState(Fn1),
makeEmptyState(F1),
makeEmptyState(F0))))
END test.
-
Итак, работающее решение на O7. Обратите внимание на приседание с дополнительной процедурной переменной pB - из-за невозможности предварительно задекларировать B.
А что, Вирт убрал из Оберона forward declarations? о_О
-
Итак, работающее решение на O7. Обратите внимание на приседание с дополнительной процедурной переменной pB - из-за невозможности предварительно задекларировать B.
А что, Вирт убрал из Оберона forward declarations? о_О
Да. Как лишнюю сущность, которая дублирует уже имеющиеся процедурные типы.
-
Итак, работающее решение на O7. Обратите внимание на приседание с дополнительной процедурной переменной pB - из-за невозможности предварительно задекларировать B.
А что, Вирт убрал из Оберона forward declarations? о_О
Да. Как лишнюю сущность, которая дублирует уже имеющиеся процедурные типы.
Я понимаю там в хаскелле/сишарпе forward declarations не нужны -- весь модуль/класс просматривается при поиске нужной процедуры, переменной или типа , но в обероне...
Жестокое упрощение языка...
-
Итак, работающее решение на O7. Обратите внимание на приседание с дополнительной процедурной переменной pB - из-за невозможности предварительно задекларировать B.
А что, Вирт убрал из Оберона forward declarations? о_О
Да. Как лишнюю сущность, которая дублирует уже имеющиеся процедурные типы.
Я понимаю там в хаскелле/сишарпе forward declarations не нужны -- весь модуль/класс просматривается при поиске нужной процедуры, переменной или типа , но в обероне...
Жестокое упрощение языка...
Дык лишняя сущность же!
Ну, на самом деле, если ослабить вот это требование: "The scope extends textually from the point of the declaration to the end of the block (procedure or module) to which the declaration belongs and hence to which the object is local." до "whole block (procedure or module)", то получится язык поприятнее для кодописателя (и чуть менее приятный для компилятороклепателя, хотя и тут выигрыш таки в итоге будет, ибо компилятор железно перестает быть однопроходным).
-
Да. Как лишнюю сущность, которая дублирует уже имеющиеся процедурные типы.
Лишняя сущность - это дополнительная переменная. Вобщем да, O7 это уже за гранью "но не проще".
P.S. Конечно же я ее забыл проинитить сначала.
P.S.S. Да, надо просто ослабить требование и искать во всем блоке.
-
- Вводится маркер для параметра процедуры (по аналогии с VAR), что этот параметр процедурного типа может быть локальной процедурой из процедуры выше по стеку. Например некий LOCAL:
Можно расширить эту идею и на указатели. Т.е., локальная переменная может быть приведена к LOCAL POINTER TO с теми же ограничениями и совместимостями. Тогда оригинальная задача тоже быть решена без обращения к куче и GC.
-
- Вводится маркер для параметра процедуры (по аналогии с VAR), что этот параметр процедурного типа может быть локальной процедурой из процедуры выше по стеку. Например некий LOCAL:
Можно расширить эту идею и на указатели. Т.е., локальная переменная может быть приведена к LOCAL POINTER TO с теми же ограничениями и совместимостями. Тогда оригинальная задача тоже быть решена без обращения к куче и GC.
Мне не кажется правильным менять (расширять) язык для решения искусственных задач. Ну, то есть, как минимум такие изменения не приоритетны.
-
Ну, на самом деле, если ослабить вот это требование: "The scope extends textually from the point of the declaration to the end of the block (procedure or module) to which the declaration belongs and hence to which the object is local." до "whole block (procedure or module)", то получится язык поприятнее для кодописателя (и чуть менее приятный для компилятороклепателя, хотя и тут выигрыш таки в итоге будет, ибо компилятор железно перестает быть однопроходным).
Утверждение, что в этом случае "компилятор железно перестаёт быть однопроходным" не верно.
-
Ну, на самом деле, если ослабить вот это требование: "The scope extends textually from the point of the declaration to the end of the block (procedure or module) to which the declaration belongs and hence to which the object is local." до "whole block (procedure or module)", то получится язык поприятнее для кодописателя (и чуть менее приятный для компилятороклепателя, хотя и тут выигрыш таки в итоге будет, ибо компилятор железно перестает быть однопроходным).
Утверждение, что в этом случае "компилятор железно перестаёт быть однопроходным" не верно.
Да? А как? Вот у меня процедура Foo, которая использует другую процедуру которая определена где-то далеко внизу. Вопрос - как за один проход такое компилировать?
-
Да? А как? Вот у меня процедура Foo, которая использует другую процедуру которая определена где-то далеко внизу. Вопрос - как за один проход такое компилировать?
Никак. Просто потому, что в общем случае нельзя "угадать" сигнатуру - оберон допускает неявные преобразования типов аргументов. Например f('A') может быть как вызовом "PROCEDURE f(c: CHAR)" так и "PROCEDURE(s: ARRAY OF CHAR)". Кроме того, передача VAR-аргументов тоже влияет на генерируемый код в точке вызова.
-
Да? А как? Вот у меня процедура Foo, которая использует другую процедуру которая определена где-то далеко внизу. Вопрос - как за один проход такое компилировать?
Никак. Просто потому, что в общем случае нельзя "угадать" сигнатуру - оберон допускает неявные преобразования типов аргументов. Например f('A') может быть как вызовом "PROCEDURE f(c: CHAR)" так и "PROCEDURE(s: ARRAY OF CHAR)". Кроме того, передача VAR-аргументов тоже влияет на генерируемый код в точке вызова.
Нет, ну можно конечно, как в древних языках (том же алголе) сделать единицей компиляции не модуль, а функцию, и, соответственно, всю эту работу свалить на компоновщик. Но это как-то на подтасовку смахивает :-)
-
Утверждение, что в этом случае "компилятор железно перестаёт быть однопроходным" не верно.
Для сомневающихся хочу дать некоторые пояснения. Я использовал специальную структуру данных, которую назвал "буфер отложенных объявлений". Основная идея в упрощенном виде выглядит так. Парсер делает один проход по исходному тексту. Если парсер в операторной последовательности встречает неизвестный идентификатор, то он панику не разводит, а помещает этот идентификатор в буфер отложенных объявлений. Затем, если далее по тексту встречается объявление этого идентификатора, то парсер переносит его из буфера в таблицу имён. К тому моменту, когда будет достигнут конец модуля, буфер должен быть пуст, иначе ошибка. На самом деле всё несколько сложнее из-за вложенности областей видимости, но в моём случае (обероноподобный язык, но не Оберон) задача алгоритмически вполне решабельна (проверено на практике).
-
Ну и для полноты картины решение на С++. Решение "в лоб", без придумок, с использованием библиотечных замыканий:
#include <boost/bind.hpp>
#include <boost/function/function0.hpp>
#include <iostream>
typedef boost::function0<int> function_t;
int f0(){return 0;}
int f1(){return 1;}
int fn1(){return -1;}
int b(int& k,
function_t const& x1,
function_t const& x2,
function_t const& x3,
function_t const& x4);
int a(int k,
function_t const& x1,
function_t const& x2,
function_t const& x3,
function_t const& x4,
function_t const& x5)
{
return k <= 0 ? x4() + x5() : b(k, x1, x2, x3, x4);
}
int b(int& k,
function_t const& x1,
function_t const& x2,
function_t const& x3,
function_t const& x4)
{
--k;
return a(k,
boost::bind(&b, boost::ref(k), x1, x2, x3, x4),
x1,
x2,
x3,
x4);
}
int main()
{
std::cout << a(10, f1, fn1, fn1, f1, f0) << std::endl;
return 0;
}
-
И в довесок "велосипедное" (замыкания вручную) решение на С++. Работает сильно быстрее, не лезет в кучу и кушает меньше стека.
#include <iostream>
struct function_t
{
virtual int operator() () const = 0;
};
struct f0 : function_t
{
virtual int operator() () const {return 0;}
};
struct f1 : function_t
{
virtual int operator() () const {return 1;}
};
struct fn1 : function_t
{
virtual int operator() () const {return -1;}
};
struct b : function_t
{
b(int k,
function_t const& x1,
function_t const& x2,
function_t const& x3,
function_t const& x4)
: k(k), x1(x1), x2(x2), x3(x3), x4(x4)
{}
virtual int operator() () const;
private:
mutable int k;
function_t const& x1;
function_t const& x2;
function_t const& x3;
function_t const& x4;
};
int a(int k,
function_t const& x1,
function_t const& x2,
function_t const& x3,
function_t const& x4,
function_t const& x5)
{
return k <= 0 ? x4() + x5() : b(k, x1, x2, x3, x4)();
}
int
b::operator() () const
{
--this->k;
return a(this->k,
*this,
this->x1,
this->x2,
this->x3,
this->x4);
}
int main()
{
std::cout << a(10, f1(), fn1(), fn1(), f1(), f0()) << std::endl;
return 0;
}
-
Парсер делает один проход по исходному тексту. Если парсер в операторной последовательности встречает неизвестный идентификатор, то он панику не разводит, а помещает этот идентификатор в буфер отложенных объявлений. Затем, если далее по тексту встречается объявление этого идентификатора, то парсер переносит его из буфера в таблицу имён. К тому моменту, когда будет достигнут конец модуля, буфер должен быть пуст, иначе ошибка.
Хорошо, парсер однопроходный. А код кто генерить будет? В текущей реализации моего компилятора все далется за один проход - и парсинг, и проверка семантики и генерация кода (никаких AST не строится). Почему нельзя сгенерировать код для вызова необъявленной процедуры - я написал выше.
Каких-то особых проблем в реализации многопроходности я не вижу - Вирт держится за однопроходность сугубо из-за своих личных предпочтений. Нет там никаких страшных усложнений компиялятора.
-
Утверждение, что в этом случае "компилятор железно перестаёт быть однопроходным" не верно.
Для сомневающихся хочу дать некоторые пояснения. Я использовал специальную структуру данных, которую назвал "буфер отложенных объявлений". Основная идея в упрощенном виде выглядит так. Парсер делает один проход по исходному тексту. Если парсер в операторной последовательности встречает неизвестный идентификатор, то он панику не разводит, а помещает этот идентификатор в буфер отложенных объявлений. Затем, если далее по тексту встречается объявление этого идентификатора, то парсер переносит его из буфера в таблицу имён. К тому моменту, когда будет достигнут конец модуля, буфер должен быть пуст, иначе ошибка. На самом деле всё несколько сложнее из-за вложенности областей видимости, но в моём случае (обероноподобный язык, но не Оберон) задача алгоритмически вполне решабельна (проверено на практике).
Если Вы генерируете маш. код уже после прохода по тексту по какой-то внутренней структуре данных -- то вот уже второй проход, компилятор двухпроходным стал...
-
Под многопроходностью я понимаю несколько проходов по одному и тому же представлению программы. Проходы по другому представлению программы - это следующий этап обработки. Front-end и back-end обычно разделены в коде. Это же не означает, что компилятор стал двухпроходным? Или означает? Вопрос терминологии.
-
Классический однопроходный компилятор (например, для виртовских языков Паскаль/Оберон) считывает строку исходного кода и сразу же генерирует для неё маш. код, если там есть что генерировать -- выделяет место под переменные, транслирует операторы.
Транслятор с фронт-эндом и бэк-эндом -- уже как минимум двухпроходные...
-
Вот у меня процедура Foo, которая использует другую процедуру которая определена где-то далеко внизу. Вопрос - как за один проход такое компилировать?
Никак. Просто потому, что в общем случае нельзя "угадать" сигнатуру - оберон допускает неявные преобразования типов аргументов. Например f('A') может быть как вызовом "PROCEDURE f(c: CHAR)" так и "PROCEDURE(s: ARRAY OF CHAR)".
В моём компиляторе переднего плана часть необходимых проверок производится отложенно, после того, как в исходном тексте встретится объявление неизвестного идентификатора. Конечно, при таком подходе генерировать машинный код на лету было бы проблематично, но такой задачи я себе и не ставил.
-
Под многопроходностью я понимаю несколько проходов по одному и тому же представлению программы. Проходы по другому представлению программы - это следующий этап обработки. Front-end и back-end обычно разделены в коде. Это же не означает, что компилятор стал двухпроходным? Или означает? Вопрос терминологии.
Угу, означает. Это и есть классический многопроходный компилятор. С созданием промежуточного представления и так далее. Переход на такой подход дает множество профитов, например дает возможность передать промежуточное представление стороннему приложению, которое делает что-то более интересное нежели кодогенерация. Компилятор превращается из компилятора в SDK для разаработки инструментария поддержки языка (всяко-разны статические анализаторы кода, рефакторинги, аналитика и так далее).
По такой схеме писан например GPCP. И это круто. Классическая Виртовская однопроходность компиляторов штука хорошая только в плане скорости компиляции на слабом железе.
-
Под многопроходностью я понимаю несколько проходов по одному и тому же представлению программы. Проходы по другому представлению программы - это следующий этап обработки. Front-end и back-end обычно разделены в коде. Это же не означает, что компилятор стал двухпроходным? Или означает? Вопрос терминологии.
Угу, означает. Это и есть классический многопроходный компилятор. С созданием промежуточного представления и так далее.
Вот, и Geniepro то же самое говорит. Хорошо, буду знать (не зря всё-таки я этот разговор затеял).
Кстати, Свердлов в своей книге упоминает о 16-проходном компиляторе (один из ранних). Неужели там использовалось 16 различных представлений одной и той же программы? :) Хотя, нет конечно. Вот там действительно по каждому из представлений делалось по несколько проходов. Памяти не хватало даже на то, чтобы хранить код самого компилятора, загружался и выполнялся по частям.
-
Кстати, Свердлов в своей книге упоминает о 16-проходном компиляторе (один из ранних). Неужели там использовалось 16 различных представлений одной и той же программы? :) Хотя, нет конечно. Вот там действительно по каждому из представлений делалось по несколько проходов. Памяти не хватало даже на то, чтобы хранить код самого компилятора, загружался и выполнялся по частям.
Вот воспомминания об одном из первых трансляторов расширенного Алгола-60, первого сильно-оптимизирующего транслятора в мире:
"ИСТОРИЯ АЛЬФА-ПРОЕКТА" (http://www.computer-museum.ru/books/n_mozaika/alfa.htm) проф. И. В. Поттосин
Общая структура транслятора состоит из четырех этапов: получение промежуточного представления, превращение его во внутренний язык и оптимизации на внутреннем языке построение машинных команд, экономия памяти и формирование машинной программы. Все эти этапы разбивались на блоки. Разбиение определялось как функциональными действиями, так и необходимостью «вписаться» в малую (4К 45-разрядных слов) память компьютера.
Окончательная структура блоков такова:
Ввод и первичный синтаксический контроль (блок 1) разработчик и реализатор — Загацкий.
Обработка описаний (блок 2) — Кожухин.
Завершение идентификации (блок 3) — Бабецкий.
Декомпозиция выражений (блоки 4 и 5), разработчик — Кожухин, реализатор — Кожухина.
Анализ и программирование процедур (блоки б и 7) — Загацкий.
Перевод из промежуточного представления во внутренний язык (блок 8) — Ершов.
Обработка геометрических операций над массивами и реализация действий с многомерными массивами (блоки 9, 10) — Волошин.
Анализ и чистка циклов (блоки 11-13) — Бежанова (при участии в разработке Поттосина).
Экономия выражений (блок 14) — Поттосин.
Построение машинных команд (блок 15) — Кожухина (при участии в разработке Бабецкого).
Программирование индекс-регистров и циклов (блоки 16-18) — разработчик Поттосин, реализатор Кожухина.
Экономия констант (блок 19) — Змиевская (при участии в разработке Ершова).
Построение операторной схемы и графа несовместимости (блоки 20, 21) — Мишкович (при участии в разработке Ершова).
Распределение памяти (блок 22) — Трохан (при участии в разработке Ершова).
Чистка и компоновка программы (блоки 23, 24) — Трохан, Змиевская.
Потом они портировали этот транслятор с М-20 на БЭСМ-6 и сильно упростили, уменьшив количество проходов в два раза...
-
Я понимаю там в хаскелле/сишарпе forward declarations не нужны -- весь модуль/класс просматривается при поиске нужной процедуры, переменной или типа , но в обероне...
Жестокое упрощение языка...
Дык лишняя сущность же!
Ну, на самом деле, если ослабить вот это требование: "The scope extends textually from the point of the declaration to the end of the block (procedure or module) to which the declaration belongs and hence to which the object is local." до "whole block (procedure or module)", то получится язык поприятнее для кодописателя (и чуть менее приятный для компилятороклепателя, хотя и тут выигрыш таки в итоге будет, ибо компилятор железно перестает быть однопроходным).
Даже если этого не делать, не следует по крайней мере разрешать видимость в процедуре внешнего к ней идентификатора, до того места где внутри процедуры будет (если будет) объявлен локальный идентификатор с тем же именем. Все идентификаторы внешние к процедуре следует импортировать в процедуру явно, возможно по псевдониму, и объявлений в процедуре идентификаторов с теми же именами/псевдонимами быть не должно. Хотя такой импорт расширяет синтаксис.
Да? А как? Вот у меня процедура Foo, которая использует другую процедуру которая определена где-то далеко внизу. Вопрос - как за один проход такое компилировать?
Никак. Просто потому, что в общем случае нельзя "угадать" сигнатуру - оберон допускает неявные преобразования типов аргументов. Например f('A') может быть как вызовом "PROCEDURE f(c: CHAR)" так и "PROCEDURE f(s: ARRAY OF CHAR)". Кроме того, передача VAR-аргументов тоже влияет на генерируемый код в точке вызова.
Неуверен, что невозможность без заглядывания вперед "угадать" сигнатуру вызываемой процедуры влечет невозможность генерировать готовый код такого вызова. Для ссылочных типов это получается.
Почему только вызовы процедур делают невозможным генерировать код с одного прохода при таких правилах видимости? Ведь есть еще константы и типы. Когда объявляется константа, тип или переменная, это тоже требует генерации кода. И если это объявление сделано до того как объявлены используемые им другие константы или не стоящие под ссыкой типы, то немедленная генерация готового кода здесь тоже будет невозможна. Конечно, у констант находящихся в вызовах процедур тип всегда будет известен без заглядывания вперед.
Начиная с версии 2011 литералы литер (тип CHAR) и строк (тип Srtring) проходят одним нетерминалом и задаются через литерные цепочки только с двойными кавычками либо через число с суффиксом "X". Но неясно, можно ли однолитерные литерные цепочки использовать как литеры.
В версии 07 литералы литер, помимо числа с суффиксом "X", задаются через литерные цепочки с одинарными кавычками (в переводе Бурцева в репорте написано: CharConst = """ character """ | digit {hexDigit} "X", но примеры идут: 'A' '%' 22X), но строковые литералы задаются через литерные цепочки и с двойными кавычками и с одинарными. В версии 1 литералы и литер и строк могут задаваться литерными цепочками только с двойными кавычками (в репорте для литер нет примеров).
Есть и другой пример невозможности угадать сигнатуру без заглядывания вперед, если идентификаторы видны всюду в блоке, а не только впереди от точки своего объявления.
Фактический параметр может быть не только литералом неясного типа, тип которого требуется уточнять типом формального параметра, но и объектом, чей тип объявлен позже чем сделано обращение к объекту. Поскольку, вызов процедуры "f", происходящий когда обработаны еще не все декларативные области модуля (включая локальные), возможен только внутри другой процедуры "h", т.е. локально, и поскольку после такого вызова далее объявляются лишь процедуры (внешние по отношению к "h"), указанный пример возможен, если фактический параметр есть идентификатор некоторой нелокальной процедуры "g" или вычисляется на основе таких процедур.
То есть, если в теле некоторой процедуры "h" идет вызов "f(g)" или например "f(g(0)+g(1))" некоторой процедуры "PROCEDURE f(a: T)", где процедуры "f" и "g" объявлены после точки этого вызова, то в этой точке тоже невозможно угадать сигнатуру "f" без заглядывания вперед.
-
Даже если этого не делать, не следует по крайней мере разрешать видимость в процедуре внешнего к ней идентификатора, до того места где внутри процедуры будет (если будет) объявлен локальный идентификатор с тем же именем. Все идентификаторы внешние к процедуре следует импортировать в процедуру явно, возможно по псевдониму, и объявлений в процедуре идентификаторов с теми же именами/псевдонимами быть не должно. Хотя такой импорт расширяет синтаксис.
Это оверкилл. Не пишите процедур на 1000 строк и не заводите кучу глобальных переменных - и будет вам счастье. Хотя если как средство отбить желание заводить глобальные переменные...
Неуверен, что невозможность без заглядывания вперед "угадать" сигнатуру вызываемой процедуры влечет невозможность генерировать готовый код такого вызова. Для ссылочных типов это получается.
...
То есть, если в теле некоторой процедуры "h" идет вызов "f(g)" или например "f(g(0)+g(1))" некоторой процедуры "PROCEDURE f(a: T)", где процедуры "f" и "g" объявлены после точки этого вызова, то в этой точке тоже невозможно угадать сигнатуру "f" без заглядывания вперед.
Взаимоисключающие параграфы детектед.
P.S. На самом деле посыл был в том, что в современном компиляторостроении однопроходность (без дополнительных оговорок) можно считать недостатком, а не достоинством. Не дает выигрыша ни пользователю языка ни создателю компилятора. А железо с компилируемой программой на ленте и невозможностью считать ее всю в память ушло в прошлое.
-
Это оверкилл. Не пишите процедур на 1000 строк и не заводите кучу глобальных переменных - и будет вам счастье.
Если во всех вызовах параметр-переменная ссылается на один и тот же объект/подобъект и даже обязана ссылаться по логике задачи, что плохого если она импортируется один раз при декларации процедуры, а не много раз как фактический параметр.
Что значит кучу, больше 10, больше 100? Если запрещать в процедурах внешние переменные (не как параметры), то совсем.
Хотя если как средство отбить желание заводить глобальные переменные...
Это средство запретить использовать разные объекты под одним именем в одном блоке (за вычетом подблоков), область видимости блока должна быть однородной. Поэтому оператор FOR тоже нужно запретить, он тоже делает область видимости блока неоднородной, можно оформить FOR как спецконструктор процедур со своими областями видимости.
Мое предложение не относится к преопределенным идентификаторам, которые должны импортироваться неявно всюду и их нельзя перекрывать.
Неуверен, что невозможность без заглядывания вперед "угадать" сигнатуру вызываемой процедуры влечет невозможность генерировать готовый код такого вызова. Для ссылочных типов это получается.
...
То есть, если в теле некоторой процедуры "h" идет вызов "f(g)" или например "f(g(0)+g(1))" некоторой процедуры "PROCEDURE f(a: T)", где процедуры "f" и "g" объявлены после точки этого вызова, то в этой точке тоже невозможно угадать сигнатуру "f" без заглядывания вперед.
Взаимоисключающие параграфы детектед.
Во втором процитированном предложении я утверждаю, что если локальные идентификаторы процедуры/модуля видимы в ней всюду (в декларативной и операторной области) т.е. их можно использовать до точки объявления, то без заглядывания вперед будет невозможно в общем случае угадать сигнатуру вызываемой процедуры.
В первом предложении я лишь высказываю сомнение в том, что "если невозможно угадать сигнатуру вызываемой процедуры, то из этого следует невозможность сгенерировать готовый код такого вызова".
Что здесь взаимоисключающего?
То есть, я высказал сомнение в том что из видимости локальных идентификаторов всюду в процедуре/модуле следует в общем случае невозможность сгенерировать готовый код вызова процедур без заглядывания вперед (невозможность однопроходного компилятора). Однопроходность скорее свойство компилятора чем языка. Конечно, при видимости идентификаторов до точки объявления однопроходный компилятор будет сложнее, а генерируемый им код неоптимальнее, но компилятор будет.
-
hi,I have done some work to make bb can work with this example.
so far, I don't think the closure is useful in bb programing, just do a half work only.
does we need this feature to built in compiler?
-
hi,I have done some work to make bb can work with this example.
Wow! SYSTEM and a little bit of machine codes :) Still it traps on my BB 1.6-rc6...
so far, I don't think the closure is useful in bb programing, just do a half work only.
does we need this feature to built in compiler?
Do you know how to do it (with GC awareness and so on)?
P.S. Yes, I think a modern general purpose language with GC must have a feature like full fledged closures.
-
Still IT traps on My BB 1.6-rc6 ...
it needs to make some changes of compiler, I say it's half work,only a draft.
if it needed, can continue change compiler to let original example compiled and work without any change.
It is only a local procedure be packed as a normal procedure, not a true closure.
for my little knownledge of closure reason,I dont known what syntax should be add to cp if we want it.
closure reference, example and suggestion is welcome!
-
Про синтаксис я не понял -- у замыканий синтаксис такой же как у обычной процедуры. Может быть maliya имел в виду лямбды?
-
Про синтаксис я не понял -- у замыканий синтаксис такой же как у обычной процедуры. Может быть maliya имел в виду лямбды?
да нет, как вы поймете по существующему синтаксису в каком случае нужно сохранять локальный контент процедуры, в каком нет?
-
Про синтаксис я не понял -- у замыканий синтаксис такой же как у обычной процедуры. Может быть maliya имел в виду лямбды?
да нет, как вы поймете по существующему синтаксису в каком случае нужно сохранять локальный контент процедуры, в каком нет?
Так его сохранять нужно в любом случае. Если Вы передадите куда-то процедуру, имеющую доступ к какому-то внешнему для неё контексту, то что случится, если этот контекст не будет сохранён? Crash boom bang -- вот что случится...
Если обращается процедура к внешним переменным -- значит эти переменные должны быть сохранены в её контексте. И синтаксис тут ни при чём...
-
Про синтаксис я не понял -- у замыканий синтаксис такой же как у обычной процедуры. Может быть maliya имел в виду лямбды?
При чем тут синтаксис? Имелась ввиду естественно семантика замыкания контекста. Лямбды тут вообще не в тему.
-
Про синтаксис я не понял -- у замыканий синтаксис такой же как у обычной процедуры. Может быть maliya имел в виду лямбды?
да нет, как вы поймете по существующему синтаксису в каком случае нужно сохранять локальный контент процедуры, в каком нет?
Так его сохранять нужно в любом случае. Если Вы передадите куда-то процедуру, имеющую доступ к какому-то внешнему для неё контексту, то что случится, если этот контекст не будет сохранён? Crash boom bang -- вот что случится...
Если обращается процедура к внешним переменным -- значит эти переменные должны быть сохранены в её контексте. И синтаксис тут ни при чём...
смотрите внимательно - я говорил о сохранении локального контента (на который она замыкается) между вызовами.
-
смотрите внимательно - я говорил о сохранении локального контента (на который она замыкается) между вызовами.
А какой такой локальный контент, сохраняемый между вызовами, может быть у процедуры в обероне? Статические переменные как в сях? Так их же не было никогда в паскалях...
Для замыканий не нужны статические переменные процедур:
http://ru.wikipedia.org/wiki/Замыкание_(программирование) (http://ru.wikipedia.org/wiki/Замыкание_(программирование))
Замыкание (англ. closure) в программировании — функция, в теле которой присутствуют ссылки на переменные, объявленные вне тела этой функции и не в качестве её параметров (а в окружающем коде). Говоря другим языком, замыкание — функция, которая ссылается на свободные переменные в своём контексте.
-
А какой такой локальный контент, сохраняемый между вызовами, может быть у процедуры в обероне? Статические переменные как в сях? Так их же не было никогда в паскалях...
тот который я хочу сохранить (по каким причинам, для простоты, неважно) - в самом простом случае я хочу сохранить переменную (ее содержимое) лексически связанную с функцией. Почти как в сях (есть нюансы). Ну и что , в делфях есть.
Для замыканий не нужны статические переменные процедур:
....
давайте договоримся какого эффекта мы хотим добиться их введением(как минимум, как максимум- для чего) а уж с помощью чего добиваться этого дело пятое-десятое.
-
It is only a local procedure be packed as a normal procedure, not a true closure.
I don't think it worth efforts to do it in this way. I'd like to have true closures, not something I can use only with SYSTEM. This particular task can be resolved without closures supported by language (see my solution for O7).
No special syntax needed for CP to support closures. It just semantic change - you can pass and return a local function and this local function can refer to outer variables (including outer function variables).
But compiler will definitely demand some work - procedure variable cannot be just "address" any more, it became "address + pointer to context".
closure reference, example and suggestion is welcome!
You can see how closures work in JavaScript (there was example in this topic for discussed task).
-
как вы поймете по существующему синтаксису в каком случае нужно сохранять локальный контент процедуры, в каком нет?
Очевидно, что надо сохранять то, на что есть ссылки из локальной процедуры. Нет?
-
Про синтаксис я не понял -- у замыканий синтаксис такой же как у обычной процедуры. Может быть maliya имел в виду лямбды?
При чем тут синтаксис? Имелась ввиду естественно семантика замыкания контекста. Лямбды тут вообще не в тему.
Я, конечно, знаю, что в английском пишется "Манчестер", читается "Ливерпуль", но не настолько же.
maliya чётко написал:
for my little knownledge of closure reason,I dont known what syntax should be add to cp if we want it.
Вот я и удивился, не понял, что он имел в виду...
-
Промелькнуло в одной из рассылок http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html (http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html), "Достижение сатори с помощью замыканий":
* * *
The venerable master Qc Na was walking with his student, Anton. Hoping to
prompt the master into a discussion, Anton said "Master, I have heard that
objects are a very good thing - is this true?" Qc Na looked pityingly at
his student and replied, "Foolish pupil - objects are merely a poor man's
closures."
Chastised, Anton took his leave from his master and returned to his cell,
intent on studying closures. He carefully read the entire "Lambda: The
Ultimate..." series of papers and its cousins, and implemented a small
Scheme interpreter with a closure-based object system. He learned much, and
looked forward to informing his master of his progress.
On his next walk with Qc Na, Anton attempted to impress his master by
saying "Master, I have diligently studied the matter, and now understand
that objects are truly a poor man's closures." Qc Na responded by hitting
Anton with his stick, saying "When will you learn? Closures are a poor man's
object." At that moment, Anton became enlightened.
:)
-
как вы поймете по существующему синтаксису в каком случае нужно сохранять локальный контент процедуры, в каком нет?
Очевидно, что надо сохранять то, на что есть ссылки из локальной процедуры. Нет?
Да (т.е. локальные переменные допустимых типов внешней процедуры), но меня беспокоит другой вопрос.. а если по замыслу эти внешние переменные не являются обьектами замыкания (являются обычными глобальными переменными относительно этой процедуры), сохранять их в любом случае?
-
More trouble than benefit! keep language and compiler as simple as possible,though any problem can be solved by reference to other language's compiler,I think there is no need to continue to do: we are not good
at function programming just now, isn't it?
-
Очевидно, что надо сохранять то, на что есть ссылки из локальной процедуры. Нет?
Да (т.е. локальные переменные допустимых типов внешней процедуры), но меня беспокоит другой вопрос.. а если по замыслу эти внешние переменные не являются обьектами замыкания (являются обычными глобальными переменными относительно этой процедуры), сохранять их в любом случае?
Все "внешние" переменные, используемые в локальной процедуре являются объектами замыкания. Иначе оно не будет работать в общем случае. Если какую-то внешнюю переменную надо исключить из замыкания, но при этом ее значение используется в локальной функции, то такую переменную всегда можно передать как аргумент (т.е., скопировать и запомнить).
-
More trouble than benefit! keep language and compiler as simple as possible,though any problem can be solved by reference to other language's compiler,I think there is no need to continue to do: we are not good
at function programming just now, isn't it?
Yeah, adding closures to original CP/Oberon requires a big piece of work from compiler/runtime implementation stand point. Although closures do not open something new comparing to classical "objects" (qp's quote has a good reflection on this) they can make source code much more clearer. So eventually I'd like to see them in some oberon implementation.
-
Все "внешние" переменные, используемые в локальной процедуре являются объектами замыкания. Иначе оно не будет работать в общем случае. Если какую-то внешнюю переменную надо исключить из замыкания, но при этом ее значение используется в локальной функции, то такую переменную всегда можно передать как аргумент (т.е., скопировать и запомнить).
можно то можно, но не всегда удобно. Ладно, а ради чего все это делается в Обероне - с одной стороны, есть возможности создания обьектов с почти такими же свойствами (временем существования, степенью изолированности), с другой стороны, семантика Оберона ограничена...- более точно формулирую вопрос следующим образом: имеет ли смысл вводить замыкания не расширяя Оберон (по крайней мере его семантику)?
-
Ладно, а ради чего все это делается в Обероне - с одной стороны, есть возможности создания обьектов с почти такими же свойствами (временем существования, степенью изолированности), с другой стороны, семантика Оберона ограничена...- более точно формулирую вопрос следующим образом: имеет ли смысл вводить замыкания не расширяя Оберон (по крайней мере его семантику)?
Да, именно поэтому и бродила раньше такая шутка: "Объекты -- это замыкания для бедных. Замыкания -- это объекты для бедных".
Не знаю, честно говоря, есть ли смысл вводить замыкания в Оберон -- там куда полезнее были бы шаблоны. Но просто это выглядит как-то искусственно -- есть все возможности что бы ввести замыкания, которые могут быть кому-то полезными, но для упрощения транслятора от этой возможности отказываются.
Впрочем, после отказа Вирта от перечислимого типа и от "forward declarations" я уже ничему не удивляюсь... :-\
-
Ладно, а ради чего все это делается в Обероне - с одной стороны, есть возможности создания обьектов с почти такими же свойствами (временем существования, степенью изолированности), с другой стороны, семантика Оберона ограничена...- более точно формулирую вопрос следующим образом: имеет ли смысл вводить замыкания не расширяя Оберон (по крайней мере его семантику)?
Э... Не расширив семантику ввести не получится - там явно прописано, что с локальными процедурами нельзя ничего делать :) По поводу смысла я там писал выше для maliya - меньше городить на ровном месте. Представь простейший, классический и часто употребимый случай - фабрика. Ты спускаешь фабрику какой-то компоненте, чтоб она могла создавать другие компоненты ничего не зная о них. В текущем состоянии языка (ББ) это делается через абстрактную запись с методом Create. Запись надо описать, потом расширить, создать... Горка кода. Если бы были полноценные замыкания, то потребовалось бы только процедурная переменная.
P.S. Это на ББ горка кода. На обероне-07 - вообще ужос-ужос. Запись с процедурным полем + последующие касты... В общем переборщил Вирт с простотой, переборщил...
-
Although closures do not open something new comparing to classical "objects" (qp's quote has a good reflection on this) they can make source code much more clearer.
Что за "qp's quote"?
-
Что за "qp's quote"?
http://oberspace.dyndns.org/index.php/topic,518.msg17552.html#msg17552
-
P.S. Это на ББ горка кода. На обероне-07 - вообще ужос-ужос. Запись с процедурным полем + последующие касты... В общем переборщил Вирт с простотой, переборщил...
"Язык, который был проще, чем нужно"
Давным-давно кто-то из людей, делавший компилятор из Оберона в байт-код.
-
P.S. Это на ББ горка кода. На обероне-07 - вообще ужос-ужос. Запись с процедурным полем + последующие касты... В общем переборщил Вирт с простотой, переборщил...
Поэтому если пилить О7 в сторону большей человечности, то добавление замыканий залечит вот такие вот проблемы с недо-ООП (можно будет хотя бы писать "object.method()"). При этом описание языка практически не пострадает. Правда эффективность с точки зрения машины ухудшится, да.
-
"Язык, который был проще, чем нужно"
Угу - это хороший эпиграф к O7 :)
-
При этом описание языка практически не пострадает. Правда эффективность с точки зрения машины ухудшится, да.
На самом деле еще VAR параметры перестанут быть эффективными. Нельзя будет передавать стековые пемеменные как VAR - они могут попасть в closure (ниже по стеку) и поэтому обязаны быть в куче.
С другой стороны, если внизу жабаскрипт, а не микроконтроллер, то все эти замечания об эффективность не актуальны...
-
При этом описание языка практически не пострадает. Правда эффективность с точки зрения машины ухудшится, да.
На самом деле еще VAR параметры перестанут быть эффективными. Нельзя будет передавать стековые пемеменные как VAR - они могут попасть в closure (ниже по стеку) и поэтому обязаны быть в куче.
Кстати, в сишарпе out-параметры нельзя передавать в замыкание, в эфшарпе, если не ошибаюсь, вообще мутабельные переменные елльзя замыкать...
-
"Язык, который был проще, чем нужно"
Угу - это хороший эпиграф к O7 :)
Скорее -- эпитафия ))