Создание простого транслятора forth
Участников: 3
Страница 3 из 4
Страница 3 из 4 • 1, 2, 3, 4
Re: Создание простого транслятора forth
Вроде работает
Компилируется, работает, выделяет слова
- Код:
import java.util.*;
class HelloWorld {
////////////////////////////////реализация WORD//////////////////////////////////////////
static int _IN=0;
static boolean ifblank(char ch) {
boolean ret=false;
if(ch == ' ' || ch == 'n' || ch == '\t' ) ret = true;
return ret;
}
- Код:
static private int skipBlank(String s, int _in) {
int position;
position=_in;
boolean log=true;
while (log)
if ( (position)==s.length() ) {position= -1; log = false; } // достигнут конец потока возвращает -1
else
{
if ( ifblank( s.charAt(position)) ) position++; else log=false;
} //else
return position;
} //endfunk
- Код:
static private int skipUntilBlank(String s, int _in) {
int position;
position=_in;
boolean log=true;
while (log)
if ( (position)==s.length() ) {/* position= -1; */ log = false; } // достигнут конец
else
{
if ( ! ifblank( s.charAt(position)) ) position++; else log=false; //если не бланк
} //else
return position;
} //endfunk
- Код:
public static String WORD( String s ) {
String w = null;
int start, ends;
start=skipBlank(s, _IN);
if (start==-1) w=""; // достигнут конец потока, возвращает слово нулевой длинны
else {
ends=skipUntilBlank(s, start);
_IN=ends;
w=s.substring(start,ends);
}
return w;
}
- Код:
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String test="aa";
System.out.println(" введите строку или end для выхода");
while (test.compareTo("end")!=0) {
test=null;
test=in.nextLine();
_IN=0;
boolean log = true;
while ( log ) {
String st = null; // Обнуляем строку перед присваиванием в яве так положено
st = WORD(test);
System.out.println(st);
if (st=="") log = false;
}
}//while
}
}
Компилируется, работает, выделяет слова
vikt144- Сообщения : 128
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
vikt144 пишет:...
В ifblank():
Точно 'n', а не '\n'?
И еще, обычно, '\r'.
В skipBlank()/skipUntilBlank():
Вы забыли про РАЗДЕЛИТЕЛЬ.
Вообще, обе эти процедуры избыточны - Вы дольше в них параметры передаете, чем они работают.
В WORD():
Зачем ему параметр СЛОВО ?
w=null; и w=""; - выберите что-нибудь одно.
Никакой работы с _IN в WORD быть не должно (процедура чтение-литеры должна его брать из объекта файла/буфера).
w=s.substring(start,ends); - неужели нельзя просто писать литеры, без второго прохода?
В main():
Почему не обозначены вкл/выкл сигналов (да и вся вторая машина)?
А где чтение файла? Консоль, это, конечно, хорошо, но Вы, ведь, сами просили "файл скриптов"...
Плюс, с консолью Вы раньше времени огребете проблем с асинхронностью ввода/распознавания, о которых писал коллега _KROL.
Использовать здесь "end"/log в корне неверно. Цикл должен быть бесконечным, прерываемым изнутри. Т.к. число слов/ошибок, прерывающих цикл чтения будет варьироваться по ходу разработки.
Впрочем, Вам виднее. Делайте, как удобнее.
***
Следующий шаг: поиск СЛОВО в СЛОВАРЕ (слово FIND), который надо прицепить к хвосту WORD. Сначала надо придумать структуру для СЛОВО (ENTRY), в которой кроме самого слова обычно входят его длина и флаги (минимум, SMUDGE и IMMEDIATE). Потом надо придумать, как связать эти ENTRY в структуру поиска (обычно, это список-стек: т.е. новый добавляется в конец, и ссылается на предыдущий).
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
На всякий случай напишу что делать дальше:
Ну, во-первых, поиграть с тем, что уже есть:
Теперь к концу FIND в случае ненахождения слова в СЛОВАРЕ (это наш случай; СЛОВАРЬ-то пуст) цепляем NUMBER - распознаватель чисел (для микрофортеров) или регулярных выражений (то же самое, но для нас - программистов).
Для начала проще всего взять обычный Java-преобразователь строки в число. Свой напишете позже, когда победите шитый код.
Важно: если NUMBER тоже не способен распознать слово, наша первая машина должна правильно зациклиться: закрыть файл, закрыть программу/попросить новый файл/перезапустить консоль...
Не двигайтесь дальше, пока не будете уверены, что отвечаете за все три цикла: всей программы, первой и второй машин.
***
Теперь Вы должны понять, что окончательно запутались в том, что является программой, словом, словарной статьей... Если этого не случилось, значит надо еще раз провести Java-программе "рефакторинг".
Ну, во-первых, поиграть с тем, что уже есть:
- Спойлер:
- Погонять искалку-вставлялку слов: слова из первой строчки - вставляем, из второй ищем; или первое - вставляем, второе - ищем; или вставляем все, что после слова "двоеточие"... Любые, показавшиеся Вам интересными варианты.
Только не забудьте по окончании вернуть все на место: пустой СЛОВАРЬ и очевидно ничего не находящий FIND...
Теперь к концу FIND в случае ненахождения слова в СЛОВАРЕ (это наш случай; СЛОВАРЬ-то пуст) цепляем NUMBER - распознаватель чисел (для микрофортеров) или регулярных выражений (то же самое, но для нас - программистов).
Для начала проще всего взять обычный Java-преобразователь строки в число. Свой напишете позже, когда победите шитый код.
Важно: если NUMBER тоже не способен распознать слово, наша первая машина должна правильно зациклиться: закрыть файл, закрыть программу/попросить новый файл/перезапустить консоль...
Не двигайтесь дальше, пока не будете уверены, что отвечаете за все три цикла: всей программы, первой и второй машин.
***
Теперь Вы должны понять, что окончательно запутались в том, что является программой, словом, словарной статьей... Если этого не случилось, значит надо еще раз провести Java-программе "рефакторинг".
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Совершенно верно, моя невнимательностьGudleifr пишет:
В ifblank():
Точно 'n', а не '\n'?
И еще, обычно, '\r'.
это java премудрости. при работе со значением null в этом примере генерировалась бы ошибка.
В WORD():
Зачем ему параметр СЛОВО ?
w=null; и w=""; - выберите что-нибудь одно.
Еще в java нельзя присваивать чего-либо строковым переменным, если их значение
не равно null. Именно поэтому оно тут.
В main():
Почему не обозначены вкл/выкл сигналов (да и вся вторая машина)?
Я пока не хочу так далеко забираться. Возможно в ходе разработки
разумно будет отказаться от векторов или от портов или чего-нибудь еще.
Поэтому пока простейший вариант - строка для интерпретации уже сформирована.
Чтобы меньше кода переделывать
Спасибо.
Следующий шаг: поиск СЛОВО в СЛОВАРЕ (слово FIND), который надо прицепить к хвосту WORD. Сначала надо придумать структуру для СЛОВО (ENTRY), в которой кроме самого слова обычно входят его длина и флаги (минимум, SMUDGE и IMMEDIATE). Потом надо придумать, как связать эти ENTRY в структуру поиска (обычно, это список-стек: т.е. новый добавляется в конец, и ссылается на предыдущий).
Сейчас распределяю код по модулям, возникли проблемы со статическими/нестатическими методами.
Вроде разобрался, продолжаю работу.
Написал вспомогательное временное слово cre(String s, boolean immediate)
Оно создает структуру в памяти из трех чисел и добавляет строку s в вектор,
устанавливает новое значение переменной latest
- Код:
link | length | index_in_vector
если immediate = true, длинна слова length записывается как отрицательное число
который начиная со стуктуры, на которую ссылается переменная latest, проходит
по структурам, созданным "cre", сравнивает строки s и строки, которая хранится в векторе
и возвращает
0 - если не найдено
положительное число - index в векторе если не immediate
отрицательное число - index в векторе если immediate
latest по-моему присутствует у Баранова и Ноздрунова,
но не нашел в стандарте форт 83
Программа скомпилирована, оттестирована - работает правильно.
Код приводить не буду, прикреплю стек,
перепишу слова с учетом наличия стека,
добавлю NUMBER и потом опубликую.
vikt144- Сообщения : 128
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
vikt144 пишет:Код приводить не буду, прикреплю стек
Погодите, погодите, до перехода к СТЕКУ нужно еще одно лирическое отступление:
Вокруг чего вертится программа?
Ну, очевидно, ПРОГРАММА может содержать цикл сама по себе. Например, в первоначальном эскизе Вашего проекта мы по очереди, в бесконечном цикле, вызывали машину чтения новых скриптов и машину их исполнения. Загрузили - некоторое время радуемся, что они работают, загрузили новые - опять радуемся...
Но любой фортер знает о существовании еще двух циклов: цикла интерпретации и "виртуальной машине"...
Цикл ИНТЕРПРЕТАЦИИ перебирает и исполняет слова из входного потока одно за другим, пока поток не иссякнет. В Вашем проекте есть два таких цикла: чтения файла скриптов (в первой машине) и чтение внешних сигналов (во второй машине).
Цикл "виртуальной машины" (абсолютно неправильное название) - это перебор слов в ШИТОМ коде. FORTH же, все-таки. Сложность тут (кроме того, что мы еще не дошли до шитых слов) в том, что очень часто этот цикл организуется не конструкцией for(), как те, что мы видели раньше, а путем записи конструкций goto и/или return внутрь самих шитых слов. Т.е. цикл перебора слов образуется сцеплением слов в цепочку "самовызова".
И главная задача FORTH-системы, повторю, решить, вокруг чего крутиться.
Например, Мур изначально предлагал считать главным цикл ИНТЕРПРЕТАЦИИ, имея в нем флаг чтения потока/шитого кода. Т.е. и слова "в символах" и слова уже скомпилированные в шитый код проходили через одну и ту же обработку (только, понятно, для шитых не надо было запускать FIND).
Позднее, нормой стало считать главным ШИТЫЙ цикл, а цикл ИНТЕРПРЕТАЦИИ реализовать одним из шитых слов,вызывая по надобности.
Но мы видим, что и в ПРОГРАММНОМ цикле все прекрасно работает (по крайней мере, в нем легко найти место для yield() во второй машине).
Так что вопрос, действительно, особенно в виду того, что мы должны согласовать работу разных циклов. Допустим, файл ввода исчерпан. Что делаем? Ждем новый (как в случае с консолью)? Или переходим к следующей стадии работы? Во многих FORTH-системах можно наблюдать жуткую МАТРЕШКУ циклов вокруг слова WORD, призванную обеспечить "наиболее правильную интерпретацию". А стандарт, так, вообще, рекомендует иметь стек "источников ввода": закрываем исчерпавшийся - возвращаемся к предыдущему... Это все плата за первородство ШИТОГО цикла.
И больше всего я боюсь, что гонясь за соблюдением "FORTH-структуры", Вы сейчас в в этих циклах запутаетесь. Поэтому старайтесь пока, по возможности, использовать ПРОГРАММНЫЕ, строго по потребности, не старайтесь учесть заранее гипотетические случаи.
Причем же тут СТЕК? Дело в том, что СТЕК не нужен большинству из этих циклов. И ПРОГРАММНЫЙ у нас прекрасно без него обходился и ШИТОМУ он, в общем-то, до лампочки. Он нужен циклу ИНТЕРПРЕТАЦИИ? Да, но тоже не всякому. Например, Ваша вторая машина интерпретирует сигналы железа. Сигналы-то поступают в случайном порядке, что будет твориться на стеке, если они там будут что-то сохранять. Нет, СТЕК нужен только для ИНТЕРПРЕТАЦИИ ввода на языке программирования (с простейшей грамматикой) и его компиляции. Только когда мы включим в нашу программу подпрограмму КОМПИЛИРОВАТЬ, только тогда нам точно будет нужен СТЕК.
Конечно, фортеры любят поговорить, что в стек - вся прелесть шитого кода, без него пришлось бы параметры передавать. Но тут, скорее, обратный процесс: раз уж мы все равно включили в программу стек, то почему бы его не использовать при любых вычислениях? (У Дейкстры же получилось).
Те же самые фортеры потом ноют, что стека им мало и подавай нормальные локальные переменные...
P.S. На данном этапе Вы, например, можете написать простой калькулятор (без стека и обратной польской записи). Тупо: число - в регистр, операция - переключение на второй регистр, вычисление - переключение обратно на первый регистр... Писать не надо, просто представьте, как это ляжет на Ваши циклы.
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Все таки решил стек добавить, что бы разобраться с разными орг вопросами, которые могли бы внестиGudleifr пишет:Погодите, погодите, до перехода к СТЕКУ нужно еще одно лирическое отступление:
свои сюрпризы. Потом пришлось бы разбираться, какие произошли ошибки, неправильное подключение java классов
или ошибки при реализации форта.
Заодно реализовал number и оттестировал написанные компоненты.
Теперь непонятное, какую структуру должна иметь словарная статья,
например дпя слова dup (ассемблерный код = 2), чтобы
1) во время компиляции вписывал 2 в последовательность кодов?
2) во время исполнения в диалоговом режиме, после выполнения
возвращал бы управление в диалог?
Из шитого кода, функции написанные на яве, такие как word, find и др. (которые будут пронумерованы),
будут получать управление через какой-нибудь порт, в который будет записываться номер вызываемой ф-ции.
nomber port out
Вызывать эти ф-ции будет обработчик порта, примерно как https://gudleifr.forum2x2.ru/t57-topic#1128
Запуск ВМ: установка переменной регистра и запуск, конкретно эту машину, которая указана в начале темы
proccessImage(StartAddress)
vikt144- Сообщения : 128
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
> Теперь Вы должны понять, что окончательно запутались в том, что является программой, словом, словарной статьей...
И разбор этого будет последним прыжком в написании FORTH-ядра. После этого Вы спокойно возьмете Баранова и Ноздрунова и будете добавлять в свою систему все, что нужно.
Прыжок этот состоит в осознании того, что слова бывают КОДОВЫМИ (оболочками для обычных Java-функций) и ШИТЫМИ (последовательностями других слов). Слова обоих типов должны запускаться одинаково внутри обоих циклов - ИНТЕРПРЕТАЦИИ и ШИТОГО. И сделано это должно быть самым тупым из возможных способов...
Пропасть эту будем преодолевать в три чисто технических мелких шага. Осознание отложим до последнего кульбита, когда техническое решение будет готово и останется только убедить себя, что ничего большего и не требуется...
1. Первое, что нам надо - это научиться вызывать функцию по номеру. Решение со switch, которое Вы предлагали, слишком тяжеловесно, придется все четыре варианта: КОДОВЫЕ слова - цикл ИНТЕРПРЕТАЦИИ, ШИТЫЕ - ИНТЕРПРЕТАЦИИ, КОДОВЫЕ - ШИТЫЙ цикл, ШИТЫЕ - ШИТЫЙ рассматривать по отдельности...
Вам надо забыть обо всех циклах и типах слов и просто спросить: как в Java создать массив функций, и как их вызывать по индексу из этого массива?
Конечно, в языках низкого уровня это легко - просто берется указатель на ф-ию (или даже ее адрес). А как в Java? Я что-то слышал, про объекты - обертки функций... Но, из нас двоих Java знаете один Вы...
Так что, этот шаг - за Вами. Когда научитесь, добавьте поле номера (CFA) в структуры ENTRY, из которых вы строили свой СЛОВАРЬ... И проверьте на описанном ранее "калькуляторе".
И разбор этого будет последним прыжком в написании FORTH-ядра. После этого Вы спокойно возьмете Баранова и Ноздрунова и будете добавлять в свою систему все, что нужно.
Прыжок этот состоит в осознании того, что слова бывают КОДОВЫМИ (оболочками для обычных Java-функций) и ШИТЫМИ (последовательностями других слов). Слова обоих типов должны запускаться одинаково внутри обоих циклов - ИНТЕРПРЕТАЦИИ и ШИТОГО. И сделано это должно быть самым тупым из возможных способов...
Пропасть эту будем преодолевать в три чисто технических мелких шага. Осознание отложим до последнего кульбита, когда техническое решение будет готово и останется только убедить себя, что ничего большего и не требуется...
1. Первое, что нам надо - это научиться вызывать функцию по номеру. Решение со switch, которое Вы предлагали, слишком тяжеловесно, придется все четыре варианта: КОДОВЫЕ слова - цикл ИНТЕРПРЕТАЦИИ, ШИТЫЕ - ИНТЕРПРЕТАЦИИ, КОДОВЫЕ - ШИТЫЙ цикл, ШИТЫЕ - ШИТЫЙ рассматривать по отдельности...
Вам надо забыть обо всех циклах и типах слов и просто спросить: как в Java создать массив функций, и как их вызывать по индексу из этого массива?
Конечно, в языках низкого уровня это легко - просто берется указатель на ф-ию (или даже ее адрес). А как в Java? Я что-то слышал, про объекты - обертки функций... Но, из нас двоих Java знаете один Вы...
Так что, этот шаг - за Вами. Когда научитесь, добавьте поле номера (CFA) в структуры ENTRY, из которых вы строили свой СЛОВАРЬ... И проверьте на описанном ранее "калькуляторе".
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
[quote="Gudleifr"как в Java создать массив функций, и как их вызывать по индексу из этого массива?][/quote]
В яве это невозможно в принципе, getProcedureAddress там не существуют.
Или это такие бездны, что лучше не заглядывать.
Единственная надежда, что switch вычисляется с чем то вроде использования кешей
В яве это невозможно в принципе, getProcedureAddress там не существуют.
Или это такие бездны, что лучше не заглядывать.
Единственная надежда, что switch вычисляется с чем то вроде использования кешей
vikt144- Сообщения : 128
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
А если мы создадим кучу объектов - DUP, SWAP, "плюс", "двоеточие"... - с единственным методом func()? А уже ссылки на эти объекты загоним в массив?vikt144 пишет:В яве это невозможно в принципе
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Ну, ладно, попробуем рассуждать так, как будто вызов ф-ии по указателю (обозначим его EXEC(CFA)) для нас невозможен.
(Описываемые здесь "функции" являются "чисто алгоритмическими", пока мы не знаем, как они будут оформлены - в виде подпрограмм, слов, еще как).
Рассмотрим все случаи:
Цикл ИНТЕРПРЕТАЦИИ, режим КОМПИЛЯЦИИ:
FIND-CI(WORD) - ищем кодовое-иммедиате слово и исполняем его
FIND-C(WORD) - ищем кодовое слово и получаем его CFA
FIND-PI(WORD) - ищем шитое-иммедиате слово и исполняем для него CALL(PFA)
FIND-P(WORD)- ищем шитое слово и получаем его PFA
Цикл ИНТЕРПРЕТАЦИИ, режим ИСПОЛНЕНИЯ:
FIND-CI(WORD) - ищем кодовое-иммедиате слово и исполняем его
FIND-CE(WORD) - ищем кодовое слово и исполняем его
FIND-PI(WORD) - ищем шитое-иммедиате слово и исполняем для него CALL(PFA)
FIND-PE(WORD)- ищем шитое слово и исполняем для него CALL(PFA)
В отсутствие EXEC(CFA) словари нужны только для шитых слов. "Словари" CI, C и CE - это switch. Скорость здесь не особенно важна (все равно читаем из файла и ищем слова), так что мы здесь почти ничего не теряем. Тем более, что кодовые слова мы, все равно, прописываем еще на этапе Java-программирования. Главное не запутаться в словарях/флагах.
ШИТЫЙ цикл:
EXECUTE-C(CFA) - ищем кодовое слово и исполняем его
EXECUTE-P(CFA) - исполняем CALL(PFA) для соответствующего слова (искать не надо, на PFA указывает CFA)
Здесь, очевидно, тоже имеет смысл "разделить словари". Например, все слова с CFA менее 100 - кодовые. Здесь потери от отсутствия EXEC(CFA) будут велики.
***
Вторая неприятность от отсутствия EXEC(CFA) будет в необходимости разделить "исполняем кодовое слово" и "вызываем CALL(PFA)". Но об этом - на втором "шаге через пропасть".
(Описываемые здесь "функции" являются "чисто алгоритмическими", пока мы не знаем, как они будут оформлены - в виде подпрограмм, слов, еще как).
Рассмотрим все случаи:
Цикл ИНТЕРПРЕТАЦИИ, режим КОМПИЛЯЦИИ:
FIND-CI(WORD) - ищем кодовое-иммедиате слово и исполняем его
FIND-C(WORD) - ищем кодовое слово и получаем его CFA
FIND-PI(WORD) - ищем шитое-иммедиате слово и исполняем для него CALL(PFA)
FIND-P(WORD)- ищем шитое слово и получаем его PFA
Цикл ИНТЕРПРЕТАЦИИ, режим ИСПОЛНЕНИЯ:
FIND-CI(WORD) - ищем кодовое-иммедиате слово и исполняем его
FIND-CE(WORD) - ищем кодовое слово и исполняем его
FIND-PI(WORD) - ищем шитое-иммедиате слово и исполняем для него CALL(PFA)
FIND-PE(WORD)- ищем шитое слово и исполняем для него CALL(PFA)
В отсутствие EXEC(CFA) словари нужны только для шитых слов. "Словари" CI, C и CE - это switch. Скорость здесь не особенно важна (все равно читаем из файла и ищем слова), так что мы здесь почти ничего не теряем. Тем более, что кодовые слова мы, все равно, прописываем еще на этапе Java-программирования. Главное не запутаться в словарях/флагах.
ШИТЫЙ цикл:
EXECUTE-C(CFA) - ищем кодовое слово и исполняем его
EXECUTE-P(CFA) - исполняем CALL(PFA) для соответствующего слова (искать не надо, на PFA указывает CFA)
Здесь, очевидно, тоже имеет смысл "разделить словари". Например, все слова с CFA менее 100 - кодовые. Здесь потери от отсутствия EXEC(CFA) будут велики.
***
Вторая неприятность от отсутствия EXEC(CFA) будет в необходимости разделить "исполняем кодовое слово" и "вызываем CALL(PFA)". Но об этом - на втором "шаге через пропасть".
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Допустим, с местом EXECUTE разобрались. Осталось выяснить как оно устроено.
2. Оборачивание процедур в слова, а слов - в процедуры.
Все что описано до сих пор, работало на обычных процедурах, т.к. использовались обычные ПРОГРАММНЫЕ циклы (даже в качестве цикла ИНТЕРПРЕТАЦИИ). Однако, при переходе к ШИТОМУ циклу нужно перейти от процедур к словам.
Что отличает процедуру (или слово) от "просто кода"? Наличие ОТКРЫВАШКИ и ЗАКРЫВАШКИ. Первая объясняет интерпретатору/процессору, что мы подразумеваем под кодом, вторая - возвращает управление внешнему коду. Очевидно, ОТКРЫВАШКА ставится в начале процедуры, ЗАКРЫВАШКА - в конце.
Чем в этом плане отличаются КОДОВЫЕ и ШИТЫЕ слова? В идеале, с точки зрения процессора - ничем. Он просто исполняет ОТКРЫВАШКУ, написанную на своем языке. Однако, у Вас КОДОВЫЕ слова - это просто куски кода внутри switch (ОТКРЫВАШКИ нет, ЗАКРЫВАШКА - break;), а ШИТЫЕ - просто цепочки CFA - вызываются специальной процедурой CALL(PFA).
В качестве параметра PFA используется адрес массива, содержащего "стартовые адреса" (CFA) слов, из которых состоит ШИТОЕ слово. Для КОДОВЫХ составляющих - это просто номер из switch, а для ШИТЫХ - 100 + PFA (100 добавляю, чтобы отличить КОДОВЫЕ слова от ШИТЫХ). Очевидно, в качестве хранилища шитого кода удобнее всего использовать один большой массив на все слова (это, в общем-то, традиция FORTH). В этом случае PFA - просто индекс в массиве начала цепочки для этого слова. ОТКРЫВАШКА опять оказывается ненужной... ЗАКРЫВАШКОЙ же может быть просто нулевое значение CFA. Однако, цена такой простоты - жуткое замедление исполнения ШИТОГО кода (из-за switch).
Процедура CALL(PFA) в этом случае может быть примитивной. Просто перебираем всю цепочку, рекурсивно вызывая EXECUTE-P (или EXECUTE-C, если CFA менее 100). Стек возвратов FORTH в этом случае эмулируется самой Java.
***
Однако, это не всегда удобно: хочется иногда и выйти из охватывающего слова, и к стеку возвратов напрямую обратиться. Поэтому, удобнее отвести еще один массив под свой стек возвратов. И не передавать PFA параметром, а хранить его в переменной (SI) - указателе на текущий CFA, проталкивая его в стек, при вызове каждого нового слова. И эта маленькая поправочка вызовет тяжелейшие последствия.
Т.е. сначала мы просто выносим SI "за скобки". CALL сохраняет текущее SI (от вызвавшего его слова) на стеке возвратов и делаем навым текущим указатель на первое слово в текущем. Вызываем для него EXECCUTE-C или -P. По возвращению увеличиваем SI на 1. Дошли нулевого CFA? Извлекаем из стека старый SI и возвращаем управление вызвавшему слову.
А зачем возвращать-то? После организации стека возвратов нам не нужен нормальный стек Java-процедур. Достаточно одной процедуры CALL, которая будет крутить ШИТЫЙ цикл, вместо возврата/рекурсивного вызова просто переходя в начало с новым SI... Ну разве, завершиться по исчерпании стека возвратов.
Как теперь компоновать наши ПОДПРОГРАММНЫЕ, ИНТЕРПРЕТАЦИОННЫЕ и ШИТЫЕ циклы? По мере совершенствования FORT-системы обычно переходят к главенству ШИТОГО цикла, переписывая процедуры ПРОГРАММНЫХ в слова, и вызывая ИНТЕРПРЕТАЦИЮ только по мере надобности.
Но это не обязательно. Для Вашей второй машины (обработки сигналов) очевидно более естественным будет ожидание сигналов в ПРОГРАММНОМ цикле, с запуском ШИТОГО только на время обработки скрипта.
***
Но это еще не все. ШИТЫЙ цикл можно "размазать по словам" (для придания большей гибкости всяким FORTH-овским вкусностям). Процедура CALL разбивается на три части: запуска первого слова, перехода к следующему и возврата. Первую оставляют в CALL (в системах, где можно писать в кодах, забивают в ОТКРЫВАШКУ слова), вторую (NEXT) фигачат в ЗАКРЫВАШКУ каждого КОДОВОГО слова, для третьей создают специальное КОДОВОЕ слово EXIT, которое прописывают в конец ШИТОЙ цепочки вместо нулевого CFA.
У Баранова и Ноздрунова это подробно описано в начале 2-й главы.
Я же еще раз напомню смысл этой фичи: ШИТЫЙ цикл реализуется не в виде отдельной процедуры, а в виде передачи управления между отдельными словами. Что это дает, кроме вхождения мозга программиста в бесконечную рекурсию? Возможность бесконечного самосовершенствования. Раз нет никакой написанной за пределами шитого кода управляющей процедуры, значит, может быть прошито и перепрошито все!
***
С одной стороны, кажется ненужной сложностью оборачивание всех наших уже готовых процедур чтения/распознавания ПОТОКА в виде КОДОВЫХ слов (да еще, в ЗАКРЫВАШКУ всех наших уже готовых КОДОВЫХ слов надо добавить NEXT). Но это окупается тем, что мы можем трассировать/отлаживать нашу систему на недостижимую для обычных языков глубину.
Прежде чем резать ПОДПРОГРАММНЫЙ цикл в ШИТЫЙ, семь раз отмерьте. Порисуйте на бумажке, почитайте про разные виды ШИТОГО кода. Поймите, как CALL, NEXT и EXIT ложатся на Ваши switch и CFA-массивы...
Сначала создайте несколько CFA-массивов в Java-коде и погоняйте их разными CALL... Попробуйте привязать к FIND.
2. Оборачивание процедур в слова, а слов - в процедуры.
Все что описано до сих пор, работало на обычных процедурах, т.к. использовались обычные ПРОГРАММНЫЕ циклы (даже в качестве цикла ИНТЕРПРЕТАЦИИ). Однако, при переходе к ШИТОМУ циклу нужно перейти от процедур к словам.
Что отличает процедуру (или слово) от "просто кода"? Наличие ОТКРЫВАШКИ и ЗАКРЫВАШКИ. Первая объясняет интерпретатору/процессору, что мы подразумеваем под кодом, вторая - возвращает управление внешнему коду. Очевидно, ОТКРЫВАШКА ставится в начале процедуры, ЗАКРЫВАШКА - в конце.
Чем в этом плане отличаются КОДОВЫЕ и ШИТЫЕ слова? В идеале, с точки зрения процессора - ничем. Он просто исполняет ОТКРЫВАШКУ, написанную на своем языке. Однако, у Вас КОДОВЫЕ слова - это просто куски кода внутри switch (ОТКРЫВАШКИ нет, ЗАКРЫВАШКА - break;), а ШИТЫЕ - просто цепочки CFA - вызываются специальной процедурой CALL(PFA).
В качестве параметра PFA используется адрес массива, содержащего "стартовые адреса" (CFA) слов, из которых состоит ШИТОЕ слово. Для КОДОВЫХ составляющих - это просто номер из switch, а для ШИТЫХ - 100 + PFA (100 добавляю, чтобы отличить КОДОВЫЕ слова от ШИТЫХ). Очевидно, в качестве хранилища шитого кода удобнее всего использовать один большой массив на все слова (это, в общем-то, традиция FORTH). В этом случае PFA - просто индекс в массиве начала цепочки для этого слова. ОТКРЫВАШКА опять оказывается ненужной... ЗАКРЫВАШКОЙ же может быть просто нулевое значение CFA. Однако, цена такой простоты - жуткое замедление исполнения ШИТОГО кода (из-за switch).
Процедура CALL(PFA) в этом случае может быть примитивной. Просто перебираем всю цепочку, рекурсивно вызывая EXECUTE-P (или EXECUTE-C, если CFA менее 100). Стек возвратов FORTH в этом случае эмулируется самой Java.
***
Однако, это не всегда удобно: хочется иногда и выйти из охватывающего слова, и к стеку возвратов напрямую обратиться. Поэтому, удобнее отвести еще один массив под свой стек возвратов. И не передавать PFA параметром, а хранить его в переменной (SI) - указателе на текущий CFA, проталкивая его в стек, при вызове каждого нового слова. И эта маленькая поправочка вызовет тяжелейшие последствия.
Т.е. сначала мы просто выносим SI "за скобки". CALL сохраняет текущее SI (от вызвавшего его слова) на стеке возвратов и делаем навым текущим указатель на первое слово в текущем. Вызываем для него EXECCUTE-C или -P. По возвращению увеличиваем SI на 1. Дошли нулевого CFA? Извлекаем из стека старый SI и возвращаем управление вызвавшему слову.
А зачем возвращать-то? После организации стека возвратов нам не нужен нормальный стек Java-процедур. Достаточно одной процедуры CALL, которая будет крутить ШИТЫЙ цикл, вместо возврата/рекурсивного вызова просто переходя в начало с новым SI... Ну разве, завершиться по исчерпании стека возвратов.
Как теперь компоновать наши ПОДПРОГРАММНЫЕ, ИНТЕРПРЕТАЦИОННЫЕ и ШИТЫЕ циклы? По мере совершенствования FORT-системы обычно переходят к главенству ШИТОГО цикла, переписывая процедуры ПРОГРАММНЫХ в слова, и вызывая ИНТЕРПРЕТАЦИЮ только по мере надобности.
Но это не обязательно. Для Вашей второй машины (обработки сигналов) очевидно более естественным будет ожидание сигналов в ПРОГРАММНОМ цикле, с запуском ШИТОГО только на время обработки скрипта.
***
Но это еще не все. ШИТЫЙ цикл можно "размазать по словам" (для придания большей гибкости всяким FORTH-овским вкусностям). Процедура CALL разбивается на три части: запуска первого слова, перехода к следующему и возврата. Первую оставляют в CALL (в системах, где можно писать в кодах, забивают в ОТКРЫВАШКУ слова), вторую (NEXT) фигачат в ЗАКРЫВАШКУ каждого КОДОВОГО слова, для третьей создают специальное КОДОВОЕ слово EXIT, которое прописывают в конец ШИТОЙ цепочки вместо нулевого CFA.
У Баранова и Ноздрунова это подробно описано в начале 2-й главы.
Я же еще раз напомню смысл этой фичи: ШИТЫЙ цикл реализуется не в виде отдельной процедуры, а в виде передачи управления между отдельными словами. Что это дает, кроме вхождения мозга программиста в бесконечную рекурсию? Возможность бесконечного самосовершенствования. Раз нет никакой написанной за пределами шитого кода управляющей процедуры, значит, может быть прошито и перепрошито все!
***
С одной стороны, кажется ненужной сложностью оборачивание всех наших уже готовых процедур чтения/распознавания ПОТОКА в виде КОДОВЫХ слов (да еще, в ЗАКРЫВАШКУ всех наших уже готовых КОДОВЫХ слов надо добавить NEXT). Но это окупается тем, что мы можем трассировать/отлаживать нашу систему на недостижимую для обычных языков глубину.
Прежде чем резать ПОДПРОГРАММНЫЙ цикл в ШИТЫЙ, семь раз отмерьте. Порисуйте на бумажке, почитайте про разные виды ШИТОГО кода. Поймите, как CALL, NEXT и EXIT ложатся на Ваши switch и CFA-массивы...
Сначала создайте несколько CFA-массивов в Java-коде и погоняйте их разными CALL... Попробуйте привязать к FIND.
Последний раз редактировалось: Gudleifr (Вт Фев 04, 2020 1:05 am), всего редактировалось 1 раз(а)
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Gudleifr пишет:А если мы создадим кучу объектов - DUP, SWAP, "плюс", "двоеточие"... - с единственным методом func()? А уже ссылки на эти объекты загоним в массив?vikt144 пишет:В яве это невозможно в принципе
Спасибо! Великолепная идея. Возьму на вооружение.
Сейчас как раз ломаю голову над всем этим.
Сейчас описанная выше временная процедура "cre0(String name,boolean immediate)" создает статью в словаре,
например
cre0("+",false);
затем сохраняем адресс на следущую ячейку, это cfa { для find; n =stack.pop(); exec(stack.pop() ) }
затем добавляется 16 \ это код для add
затем 9 \ это код для ret инструкция фвм, которая снимает со
стека возвратов значение, и туда передает управление.
Таким образом, если слово получит управление через call, то вернет управление обычным образом ,
через ret.
Чтобы вызвать слово "+" через execute.
Для этого предварительно определенно слово exit,
которое останавливает FVM.
Затем получаем адрес слова exit с помощью find, и сохраняем в специальной переменной
перед вызовом execute значение переменной перенесем на стек возвратов,
тогда после выполнения "+" инструкция ret вернет управление назад в диалог.
vikt144- Сообщения : 128
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Пока рано (к тому же, эта ф-я никому не нужна).vikt144 пишет:cre0("+",false)
Сначала попробуйте создавать массив шитых слов руками и попробуйте разные варианты CALL, как я описал.
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Уже.Gudleifr пишет:Пока рано (к тому же, эта ф-я никому не нужна).vikt144 пишет:cre0("+",false)
Сначала попробуйте создавать массив шитых слов руками .
И даже автоматизировал
- Код:
String ini = as.initwords;
as._IN=0;
boolean enddo=false; //для выхода из цикла
do {
String name = as.WORD(ini);
// System.out.print(name);
if (name.compareTo("end_") != 0) {
String codeString = as.WORD(ini);
String immString = as.WORD(ini);
int code=0;
try { code=Integer.parseInt(codeString);} catch (Exception e){ System.out.print("huynia code");}
int imm=0;
try {imm=Integer.parseInt(immString);} catch (Exception e){ System.out.print("huynia imme");}
boolean bool = imm>0;
short[] arrays={0,9};// "nop" ";" {0,1,0,1,0,29};// последовательность " nop lit 0 lit 0 out " для exit
arrays[0]=(short)code; // нулевой элемент заменяется кодом операции
as.cre1(name,bool,arrays);
http://System.out.print(name+code + " " +imm);
}
else enddo = true;
} while (! enddo)
as.initwords; - строка вида
- Код:
public String initwords =
" NOP 0 -1 LIT 1 -1 DUP 2 -1 DROP 3 -1 SWAP 4 -1 "+
" PUSH 5 -1 POP 6 -1 CALL 7 -1 JUMP 8 -1 ; 9 1 " +
" GT_JUMP 10 -1 LT_JUMP 11 -1 NE_JUMP 12 -1 EQ_JUMP 13 -1 " +
" FETCH 14 -1 STORE 15 -1 " +
" + 16 -1 - 17 -1 * 18 -1 DIVMOD 19 -1 " +
" AND 20 -1 OR 21 -1 XOR 22 -1 SHL 23 -1 SHR 24 -1 "+
" ZERO_EXIT 25 -1 INC 26 -1 DEC 27 -1 "+
" IN 28 -1 OUT 29 -1 WAIT 30 -1 "+
" LIT32 31 -1 "+
"end_ "
;
as - это
fas as = new fas();
fas - это класс "форт ассемблер"
строка имеет формат
name cod признак immediate (-1 или 1)
dup 2 -1
все работает, пока только как кулькулятор, может компилировать, но пока запутался с ";"
По идее, во время компиляции, должен перейти в режим исполнения, вкомпилировать ret
и уйти в диалоговый режим. То, что у меня пока получается, хоть работает, но выглядит коряво
vikt144- Сообщения : 128
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Ну, пока это на уровне конца предыдущей страницы.vikt144 пишет:И даже автоматизировал
Догоняйте!
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
С Вашего позволения закончу разговор о написании FORTH-ядра.
3. Обороты.
После того, как мы всесторонне рассмотрели вызов функций, рассмотрим передачу им параметров. Нет, нет, я не о СТЕКЕ, это было бы скучно.
Введем термин ОБОРОТ (в смысле "речевой оборот"). Он одинаково применим и к словам в ПОТОКЕ ввода и к словам в ШИТОМ коде. Означает, что ввод очередного слова/кода может потребовать чтения следующего (или нескольких). Например, считанное из потока слово "открывающая скобка" требует ввода комментария (до закрывающей скобки), прежде чем продолжить интерпретацию, или, уже в шитом коде, команда ветвления кроме самой себя содержит и адрес перехода - в следующей ячейке. В потоке обороты используются обычно для ввода текстовых литералов (в первую очередь - имен создаваемых слов). В шитом коде - для хранения литералов (не только текстовых) и адресов (как и в обычных машинных кодах).
В ПОТОКЕ для обработки оборотов требуется дополнительный вызов WORD (PARSE). Это может вызвать проблему в синхронизации циклов ИНТЕРПРЕТАЦИИ и ШИТОГО/ПРОГРАММНОГО. Буфер чтения, читаемый ШИТЫМ/ПРОГРАММНЫМ циклом, может закончиться посреди оборота. Тут программист должен рассмотреть все возможные варианты. Некоторые даже засовывают чтение буфера внутрь WORD.
В ШИТОМ коде обороты читать проще (но только при условии, что Вы научились работать с переменной SI). Иначе придется учитывать, что переменная цикла может меняться посреди шага.
***
Примеры ОБОРОТОВ можете посмотреть в Баранове и Ноздрунове, как впрочем и остальные нужные слова. Проблем больше возникнуть не должно. И как только Вы победите слова INTERPRET, "двоеточие"и "СОХРАНИТЬ-СЛОВАРЬ-В-ФАЙЛ", можно будет считать, что FORTH-система готова.
3. Обороты.
После того, как мы всесторонне рассмотрели вызов функций, рассмотрим передачу им параметров. Нет, нет, я не о СТЕКЕ, это было бы скучно.
Введем термин ОБОРОТ (в смысле "речевой оборот"). Он одинаково применим и к словам в ПОТОКЕ ввода и к словам в ШИТОМ коде. Означает, что ввод очередного слова/кода может потребовать чтения следующего (или нескольких). Например, считанное из потока слово "открывающая скобка" требует ввода комментария (до закрывающей скобки), прежде чем продолжить интерпретацию, или, уже в шитом коде, команда ветвления кроме самой себя содержит и адрес перехода - в следующей ячейке. В потоке обороты используются обычно для ввода текстовых литералов (в первую очередь - имен создаваемых слов). В шитом коде - для хранения литералов (не только текстовых) и адресов (как и в обычных машинных кодах).
В ПОТОКЕ для обработки оборотов требуется дополнительный вызов WORD (PARSE). Это может вызвать проблему в синхронизации циклов ИНТЕРПРЕТАЦИИ и ШИТОГО/ПРОГРАММНОГО. Буфер чтения, читаемый ШИТЫМ/ПРОГРАММНЫМ циклом, может закончиться посреди оборота. Тут программист должен рассмотреть все возможные варианты. Некоторые даже засовывают чтение буфера внутрь WORD.
В ШИТОМ коде обороты читать проще (но только при условии, что Вы научились работать с переменной SI). Иначе придется учитывать, что переменная цикла может меняться посреди шага.
***
Примеры ОБОРОТОВ можете посмотреть в Баранове и Ноздрунове, как впрочем и остальные нужные слова. Проблем больше возникнуть не должно. И как только Вы победите слова INTERPRET, "двоеточие"и "СОХРАНИТЬ-СЛОВАРЬ-В-ФАЙЛ", можно будет считать, что FORTH-система готова.
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Почему то заблокирован сайт нашим провайдером - приходиться
заходить через прокси, что очень неудобно.
Сейчас успешно пишу навигационную программу, чтоб было чем пользоваться и
в которую будет имплантирован форт.
Из вариантов шитого кода, хочется попробовать классический байт код,
просто много такого кода написано и просто интересно. Хоть и вызовет серьезные
затруднения: вкомпилировать, например, запятой "," адрес уже не получится,
надо будет call < адрес >
Но тут я вовсе фанатично не упорствую, если уж все будет совсем плохо,
перепишу виртуальную машину стандартным образом.
По причине массового блокирования нашим провайдером чуть ли не половины
интернета возникли вопросы по самой деликатной части форта, по create.
(имеются реализации create только на ассемблерах, причем на дохлых)
Заблокированы или издохли многие ссылки.
Правильно ли я понял create ?
create - слово немедленного исполнения, создающее слово,
которое кладет на стек адрес своего pfa.
псевдокод
тут cre0() - условная вспомогательная ф-ция, добавляющая слово, возвращенное word, в словарь
Слово, реализующее компиляцию такой статьи, должно вкомпилировать фрагмент, начиная от body, до ret-1
то есть сканировать поле параметров, пока не повстречается ret ( ";" )
констукция вида : name create -- ; - очевидно должны реализовываться по другому.
заходить через прокси, что очень неудобно.
Сейчас успешно пишу навигационную программу, чтоб было чем пользоваться и
в которую будет имплантирован форт.
Из вариантов шитого кода, хочется попробовать классический байт код,
просто много такого кода написано и просто интересно. Хоть и вызовет серьезные
затруднения: вкомпилировать, например, запятой "," адрес уже не получится,
надо будет call < адрес >
Но тут я вовсе фанатично не упорствую, если уж все будет совсем плохо,
перепишу виртуальную машину стандартным образом.
По причине массового блокирования нашим провайдером чуть ли не половины
интернета возникли вопросы по самой деликатной части форта, по create.
(имеются реализации create только на ассемблерах, причем на дохлых)
Заблокированы или издохли многие ссылки.
Правильно ли я понял create ?
create - слово немедленного исполнения, создающее слово,
которое кладет на стек адрес своего pfa.
псевдокод
тут cre0() - условная вспомогательная ф-ция, добавляющая слово, возвращенное word, в словарь
- Код:
void create() { //слово немедленного исполнения
if ( state !=0 ) {
тут комманды, которые,
если состояние компиляции, то вкомпилируют вызов процедуры на саму себя -
в этой реализации, просто добавит комманду CALL CREATE
}
else {
WORD();
cre0();
вкомпилировать адрес body ; // cfa
вкомпилировать LIT ;
вкомпилировать адрес body ;
вкомпилировать ";"
}
}
- Код:
link // поле связи
name // указатель на строку
cfa // тут адрес body
body { lit
body
ret
}
Слово, реализующее компиляцию такой статьи, должно вкомпилировать фрагмент, начиная от body, до ret-1
то есть сканировать поле параметров, пока не повстречается ret ( ";" )
констукция вида : name create -- ; - очевидно должны реализовываться по другому.
vikt144- Сообщения : 128
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Ну, во-первых, CREATE - не слово немедленного исполнения. Если бы оно было немедленного исполнения, было бы трудно создавать новые слова для создания слов. Оно тупо создает структуру ENTRY (см. выше) и фигачит в ее cfa код (адрес кода), выдающего pfa. Создание ENTRY (cre0()) вполне "прошиваемое слово", занимающееся адресной арифметикой СЛОВАРЯ. Да и зафигачивание кода - тоже. "Бинарность" необходима только самому прошиваемому коду:
Например, в моем старом DOS-FOBOS (понятно, что до определения "двоеточия", первые определения вбивались руками прямо в шитый код):
: (CREATE) LATEST >R HERE CURRENT @ ! BL WORD C@ 1+ ALLOT R> , ;
: : (CREATE) (:1) >MARK (:2) >RESOLVE ] SMUDGE ;
: <BUILDS (CREATE) (VAR1) >MARK (DOES1) (:1) >MARK (:2) SWAP >RESOLVE ASM <кусочек кода> ;
: CREATE (CREATE) (VAR1) >MARK (VAR2) >RESOLVE ;
: VARIABLE CREATE 0, ;
: CONSTANT (CREATE) (CON1) , (CON2) ;
Т.е. вся "бинарность" сосредоточена в кубиках (:1), (:2)... (CON2). Единственный <кусочек кода> - это общий для всех DOES> "запускатель pfa". Страшные >MARK и >RESOLVE - это для втюхивания в код ссылки вперед (из cfa на pfa). Т.о. почти для каждого создающего слова мы имеем прошивку (в прямом шитом коде):
<кубик1> ссылка на pfa <кубик2>
Тут, пардон, в DOS-FOBOS я немного смухлевал и совместил в кубиках и код, и его прошивку. Это легко пишется, но тяжело читается. Для наглядности следовало бы оформить прошиваемый код кубика константой и фигачить его через "запятую"...
Каков же прошиваемый код кубиков для CREATE ? Для подпрограммного кода - это call (в смысле процессорной команды) на процедуру посылки на стек следующего адреса СЛОВАРЯ (на чем слово завершается). Для косвенного кода - тоже самое, но без call ("запускатель слов" просто передает адрес процедуры на исполнение). Для прямого - сложнее всего (как у меня выше): код этой процедуры тупо дописывается в ENTRY. Поэтому и приходится иметь слово <BUILDS, т.к. "просто поменять адрес кода" невозможно, надо переписывать сам код.
Можно заметить, что "честно" передавать pfa в процедуру помещения на стек нет никакой надобности: при подпрограммном шитом коде он попадает в стек возвратов процессора при выполнении call, при прямом - прошивается к код, как адрес операнда, при косвенном - "запускатель" знает адрес, откуда читает.
Не пытайтесь скопировать структуру кубиков "один в один". И нее пытайтесь понять "философию бесконечной рекурсии самовызывания". Просто пройдите по пунктам 1-3 в постах этой страницы (#1172, #1178, #1184). (На самом деле, Вы уже умеете писать CREATE , только об этом еще не знаете).
P.S. Да для случая подпрограммного кода предусмотрена стандартная разновидность "запятой" - COMPILE, ("COMPILE с запятой"), помещающая в СЛОВАРЬ не только адрес, но и процессорный call ...
P.P.S. Дело идет к тому, то людей, использующих Сеть для общения, скоро опять вытеснят в FIDO, а компьютеры придется паять втихаря на кухне...
Например, в моем старом DOS-FOBOS (понятно, что до определения "двоеточия", первые определения вбивались руками прямо в шитый код):
: (CREATE) LATEST >R HERE CURRENT @ ! BL WORD C@ 1+ ALLOT R> , ;
: : (CREATE) (:1) >MARK (:2) >RESOLVE ] SMUDGE ;
: <BUILDS (CREATE) (VAR1) >MARK (DOES1) (:1) >MARK (:2) SWAP >RESOLVE ASM <кусочек кода> ;
: CREATE (CREATE) (VAR1) >MARK (VAR2) >RESOLVE ;
: VARIABLE CREATE 0, ;
: CONSTANT (CREATE) (CON1) , (CON2) ;
Т.е. вся "бинарность" сосредоточена в кубиках (:1), (:2)... (CON2). Единственный <кусочек кода> - это общий для всех DOES> "запускатель pfa". Страшные >MARK и >RESOLVE - это для втюхивания в код ссылки вперед (из cfa на pfa). Т.о. почти для каждого создающего слова мы имеем прошивку (в прямом шитом коде):
<кубик1> ссылка на pfa <кубик2>
Тут, пардон, в DOS-FOBOS я немного смухлевал и совместил в кубиках и код, и его прошивку. Это легко пишется, но тяжело читается. Для наглядности следовало бы оформить прошиваемый код кубика константой и фигачить его через "запятую"...
Каков же прошиваемый код кубиков для CREATE ? Для подпрограммного кода - это call (в смысле процессорной команды) на процедуру посылки на стек следующего адреса СЛОВАРЯ (на чем слово завершается). Для косвенного кода - тоже самое, но без call ("запускатель слов" просто передает адрес процедуры на исполнение). Для прямого - сложнее всего (как у меня выше): код этой процедуры тупо дописывается в ENTRY. Поэтому и приходится иметь слово <BUILDS, т.к. "просто поменять адрес кода" невозможно, надо переписывать сам код.
Можно заметить, что "честно" передавать pfa в процедуру помещения на стек нет никакой надобности: при подпрограммном шитом коде он попадает в стек возвратов процессора при выполнении call, при прямом - прошивается к код, как адрес операнда, при косвенном - "запускатель" знает адрес, откуда читает.
Не пытайтесь скопировать структуру кубиков "один в один". И нее пытайтесь понять "философию бесконечной рекурсии самовызывания". Просто пройдите по пунктам 1-3 в постах этой страницы (#1172, #1178, #1184). (На самом деле, Вы уже умеете писать CREATE , только об этом еще не знаете).
P.S. Да для случая подпрограммного кода предусмотрена стандартная разновидность "запятой" - COMPILE, ("COMPILE с запятой"), помещающая в СЛОВАРЬ не только адрес, но и процессорный call ...
P.P.S. Дело идет к тому, то людей, использующих Сеть для общения, скоро опять вытеснят в FIDO, а компьютеры придется паять втихаря на кухне...
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
В нашем случае, это скорее ошибка в настройках сервера, на одном из узлов,Дело идет к тому, то людей, использующих Сеть для общения, скоро опять вытеснят в FIDO
через который проходит траффик. Заблокированы некоторые официальные СМИ.
Сегодня сайт разблокировали.
Ну, во-первых, CREATE - не слово немедленного исполнения.
Ну тогда CREATE возможно надо оформить как процедуру ":"
Беда только, что для того, чтобы определить create,
нужно определение ":", а для определения двоеточия - нужен create.
Но попробую.
псевдокод
- Код:
void docolon() { // немедленного исполнения, строит ":" на стеке cfa
получить pfa из cfa;
if ( state ==0 ) { // если режим исполнения
запустить VM с этого адреса pfa ; // в данной реализации VM.processImage(pfa)
}
else { //если компиляция
вкомпилировать CALL;
вкомпилировать pfa ;
}
}
- Код:
void ret() { // ";" немедленного исполнения
вкомпилировать ret; // инструкцию ret виртуального процессора
вызвать процедуру "]" ; //перевести в режим исполнения
}
void proc() { //":"
create();
заменить значение cfa на адрес docolon;
вызвать процедуру "[" ; //перевести в режим компиляции
}
vikt144- Сообщения : 128
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Слишком много непонятных для меня слов и действий.vikt144 пишет:Но попробую.
Повторяю, все просто:
1. Как запустить ф-ию по переданному извне имени/адресу/номеру?
2. Как обернуть эти вызовы в кодовые и шитые слова?
3. Как и зачем осуществляется забегание по коду/ПОТОКУ вперед?
От того, как Вы реализуете эти "мелочи", будет зависеть "как делать CREATE".
А от того, сможете ли Вы объяснить мне, как Вы это реализовали, зависит, смогу ли я помочь.
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Итак, по порядку.
1. Как победили?
1. Как победили?
vikt144 пишет:В яве это невозможно в принципе, getProcedureAddress там не существуют.
Или это такие бездны, что лучше не заглядывать.
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Только что попробывал написать тестирующую программу,
которая сравнивает Object и switch
Столкнулся с затруднениями
Пытался сделать вот так
Object[] OA = new Object[10];
class C0 { public void one() { x = x+5; } ; } OA[0]=new C0();
class C1 { void one() { x = x+5; } ; } OA[1]=new C1();
class C2 { void one() { x = x+5; } ; } OA[2]=new C2();
итд.
чтобы прогнать циклом и сравнить производительность
Не компилируется конструкция
OA[n].one();
Сходу не получилось.
которая сравнивает Object и switch
Столкнулся с затруднениями
Пытался сделать вот так
Object[] OA = new Object[10];
class C0 { public void one() { x = x+5; } ; } OA[0]=new C0();
class C1 { void one() { x = x+5; } ; } OA[1]=new C1();
class C2 { void one() { x = x+5; } ; } OA[2]=new C2();
итд.
чтобы прогнать циклом и сравнить производительность
Не компилируется конструкция
OA[n].one();
Сходу не получилось.
vikt144- Сообщения : 128
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
А разве все эти классы не должны быть унаследованы от одного-абстрактного? Интерфейса какого-нибудь? В Java я - ни ухом, ни рылом...vikt144 пишет:Не компилируется конструкция
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Пока решил отложить исследование java. Жара, сосредоточится трудно.
К тому же реализации java разные. И более менее гладкий switch
не трудно реализовать массивом адресов и скорее всего на низком уровне
примерно так и сделано.
Тут интересно другое. Раз в моей реализации используется
виртуальный стековый процессор, ассемблер которого совпадает с основными
форт словами, то разумно, что бы поле кода совпадало с полем параметров.
Тогда разумно сделать так, чтобы cfa содержало информацию о режиме
компиляции, например
вкомпилировать lit <body
или call <body
или переписать содержимое pfa в код ...
К тому же реализации java разные. И более менее гладкий switch
не трудно реализовать массивом адресов и скорее всего на низком уровне
примерно так и сделано.
Тут интересно другое. Раз в моей реализации используется
виртуальный стековый процессор, ассемблер которого совпадает с основными
форт словами, то разумно, что бы поле кода совпадало с полем параметров.
Тогда разумно сделать так, чтобы cfa содержало информацию о режиме
компиляции, например
вкомпилировать lit <body
или call <body
или переписать содержимое pfa в код ...
vikt144- Сообщения : 128
Дата регистрации : 2017-03-29
Re: Создание простого транслятора forth
Как я писал выше, это вызовет жуткие потери при чтении шитого кода.vikt144 пишет:И более менее гладкий switch
не трудно реализовать массивом адресов и скорее всего на низком уровне
примерно так и сделано.
FORTH в этом не нуждается. Нет, конечно, Вы можете повернуть свои мозги таким образом, но так Вы только сами себя запутаете.vikt144 пишет:Раз в моей реализации используется
виртуальный стековый процессор, ассемблер которого совпадает с основными
форт словами
Это совершенно не разумно. Эти поля используются по-разному.vikt144 пишет:разумно, что бы поле кода совпадало с полем параметров.
Gudleifr- Admin
- Сообщения : 3219
Дата регистрации : 2017-03-29
Страница 3 из 4 • 1, 2, 3, 4
Страница 3 из 4
Права доступа к этому форуму:
Вы не можете отвечать на сообщения
|
|