ВеткаКогда сделаю, предоставлю код — установка\сброс определенного бита, in-set и еще что-нибудь.
85 сообщений · –В \ возвращает 0 если бит с индексом i равен 0, иначе возвращает не 0 : contains ( u i -- u1 ) 1 swap lshift and ; \ установка значения указанного бита в нуль : remove ( u i -- u1 ) 1 swap lshift not and ; \ установка значения указанного бита в единицу : insert ( u i -- u1 ) 1 swap lshift or ; \ вывод битового вектора по элементам : outset ( u -- ) ." { " MAXSET 1+ 0 DO dup I contains \ u u1 IF I . THEN \ u LOOP drop ." } " ;В В В В
R А что такое bit ? Корректно ли для него такое определение? DataType( bit = 1|0 )? Или такое: DataType( bit = u2[# power-of-two[* u.exp -- u2 ] ] ) Второе — это пример уточнения через обитаемость другого типа, который есть подтип power-of-two. Т.е., формально: для каждого значения типа bit должна существовать функция-подтип power-of-two, которая возвращает это значение (при этом, входящий параметр функции не важен). Тип power-of-two можно определить следующим образом: DataType( power-of-two = xt[* u1 -- u[: ( u -- flag ) 1 u1 lshift = ;] ] ) Тут мы уточняем u через выражение-предикат, записанное на Форте.В R В R В Бит имеет две "координаты" значение(0,1) и позиция(0,,,31). Слово lo-bit выдает позицию единичного бита, который имеет минимальное значение позиции. Можно уточнить имя lo-bit на lo-bit1 и по аналогии определить слово lo-bit0 и тп. Мне это не надо было. И тогда будет хотя бы понятно, что возвращаться может только позиция бита.R
R R В R
R В В В То есть u - это число без знака? Обычно числа без знака используются когда самый старший разряд разрядной сетки используется не как знак, а как значащий разряд для положительного числа. Но в данном конкретном случае нам не нужен значащий разряд, так как число 32 или 64 гораздо меньше 2^32 или 2^64. А целые положительные числа в данном случае не подойдут?R В R В
В R Ссылка
нажмите — покажемДа, в математике ноль не является положительным числом (не входит в множество положительных чисел), и не является отрицательным числом (не входит в множество отрицательных чисел). Если бы ноль был одним из них, то возникла бы асимметрия. Выражения +0 и -0 используются для указания предела бесконечно малой величины, положительной и отрицательной соответственно. В некоторых вариантах представления целых чисел со знаком есть -0 и +0, например в One's complement и Sign–magnitude (see Signed number representations / Comparison table). Но, стандартные целочисленные арифметические операции в Форте никогда не возвращают -0, цитата из A.3.2.1 Numbers: Per 3.2.1.1 Internal number representation and 6.1.0270 0=, the implementor must ensure that no standard or supported word return negative zero for any numeric (non-Boolean or flag) result. В числах с плавающей точкой (в IEEE 754), при потери значимости, сохраняется знак числа. Поэтому там есть положительный ноль и отрицательный ноль. Но, -0 == +0. Разница проявляется при делении на ноль — может получится отрицательная бесконечность или положительная бесконечность. Если числа -0e и 0e кодируются по разному, слово f~ может их различать при побитовом сравнении. #math #forth #standard
R R Эта стековая диаграмма обозначает слишком широкий тип, т.к. она не говорит, что должно быть, если на входе 0 (т.е., в этом случае возвращаемое значение может быть любым). Варианты исправления: x -- +n.pos|-1 x\0 -- +n.pos ^^ 0 -- -1 Будет ли удобней возвращать -1 вместо 0 — надо смотреть по реальным применениям.R В В В R Вот, я читаю запись « |0» как эквивалент «0*x|0», и поэтому всю эту стековую диаграмму как эквивалент диаграммы: «( -- a u 0*x|0 )», которая эквивалентна: «( -- a u 0*x | a u 0 )», которая эквивалентна: «( -- a u | a u 0 )», которая эквивалентна: «( -- a u || -- a u 0 )». По-видимому, вы читаете исходную диаграмму по-другому. Поэтому, следующая причина "усложнения" (четкого описания) — это чтобы не было разночтений. #forth #stack #notation
В R Обычно, стековая диаграмма не задает семантику целиком, а лишь ограничивает ее. При этом, чем больше ограничивает, тем больше определяет семантику. Остальное детализируется в текстовом описании. «+n.pos|-1» говорит, что это либо -1, либо неотрицательное число. Что означает что, и в каких случаях — дополняется текстом.В R В Мы пишем определение для определенного типа входных параметров и хотим получить определенный тип выходных параметров. В этом случае контроль типов пропустит минимум ошибок когда? А максимум ошибок когда? Если мы выберем тип на входе шире чем надо, могут пролезть входные данные, которые не могут иметь место при правильном алгоритме работы(нет контроля входа), если мы выберем тип на входе шире заданного, то не будет корректного контроля на выходе. В форте контроль типов на входе и выходе можно втащить в само определение. То есть если определение способно работать с широкими типами, а надо чтобы оно работало с узкими типами(так задумано), то надо менять само определение. А если его не менять, то нужны отдельные фильтры на входе и на выходе ( будут работать в режиме компиляции). Входные фильтры будут одновременно выходными фильтрами определения, которое выдает свои выходные данные на вход рассматриваемого определения.R Да, все так. Поэтому, тип входа следует аннотировать как можно шире, но не более, чем мы умеем обрабатывать. А тип выхода — как можно уже, но не уже, чем мы можем выдавать. Это ведет к тому, что аннотируемый стрелочный тип в целом становиться уже. В моем утверждении "нельзя, чтобы аннотированный тип был более узким, чем реализованный тип", я имею ввиду целиком стрелочный тип, — что покрывает и вход и выход. А под реализованным типом я имею ввиду само Форт-определение, которое мы аннотируем.В R Стрелочный тип можно аннотировать шире, чем реализовано, в силу отношения подтипизации. Но, нельзя уже. Примеры, как делать нельзя (аннотированный стрелочный тип уже, чем реализованный): — Выход слова аннотирован как -1|1|0, а по факту иногда возвращается значение 2. Это приведет к неопределенному поведению программы, которая использует это слово. — Вход аннотирован как n, а корректно обрабатывается только +n. Это тоже приведет к неопределенному поведению, если подали на вход -1.В В R В R В R В R В R В Когда данные приходят из внешнего мира (файл, порт ввода и тп), у них нет типа: есть только биты/байты. В таком случае нужно аннотировать у первичного слова, принимающего данные извне, не самый широкий тип, который слово может обработать, а "правильный тип в системе" — то есть тот, который считается валидным для этой точки входа. Это конечно справедливо для контроля типов в рантайме. В некоторых случаях возможна валидация внешних данных, например программное подавление дребезга контактов.R R Тут идея в следующем. При чтение значения из памяти, его собственный тип не изменяется. Для слова «@» это можно формально выразить так: ( a-addr[+ x1] -- x1 ) Но, если значение в память попало извне, то для тайпчекера оно имеет лишь самый общий тип x (unspecified cell). И, к примеру, где-то между «read-file» и «@ dup 0<» возникнет статическая несовместимость типов, т.к. в контекст, где ожидается «a-addr[+ n]», передается значение типа «a-addr[+ x]». Оптимистичный или градуальный (gradual typing) тайпчекер может для значений типа x полученных извне (как пример, записанных в память), назначить тип dyn-x, который совместим с любыми подтипами x (или, как вариант, только с подтипами/надтипами u и n). И тогда в примере выше, статически будет передаваться значение типа «a-addr[+ dyn-x]», которое тайпчекер считает совместимым с «a-addr[+ n]». Конечно, этот подход допускает возникновение динамической несовместимости типов при выполнении. Еще, статически можно сужать тип значения (type narrowing) и ловить ситуацию, когда одно и тоже значение интерпретируется по разному в разных местах. В примере выше, тайпчекер может сузить тип значения dyn-x до dyn-n, которое совместимо только с подтипами и надтипами n. И если в другом месте это же значение используется в контексте, где ожидается u или addr, тайпчекер может указать на ошибку. #typesystem #typechecking #gradualR Ссылка
нажмите — покажемВ целом, такой код завязан на реализацию Форт-системы и предполагает совместимость бинарных форматов. В данном пример предполагается, что размер ячейки, порядок байт (endianness), и кодирование знаковых целых совпадают в данных извне и в Форт-системе. #forth #abi #compatibility
R В R R Даже простая стековая нотация должна быть четко описана. Без описания нету нотации. Сейчас стандартная стековая нотация недостаточно определена, и по факту в стековых диаграммах используются операции, которые формально не описаны. Начнем с того, что в описании типов данных Форта указана подтипность. Но, из отношения подтипности неизбежно вытекают операции пересечения и объединения типов. Операция объединения по факту используется в стековых диаграммах, хотя не имеет четкого описания. Более того, иногда операция объединения используется вместо операции пересечения. Поэтому, первое усложнение — это четкое описание операций пересечения и объединения, и того, как они записываются. #forth #stack #notationR По своей сути, стековая диаграмма обозначает стрелочный тип. Для слова «search-wordlist» в стандарте указана стековая диаграмма: ( c-addr u wid -- 0 | xt 1 | xt -1 ) В этой диаграмме символ «|» означает объединение типов данных. Для слова «1+» в стандарте указана следующая стековая диаграмма: ( n1|u1 -- n2|u2 ) Но тут — либо символ «|» не означает объединение типов данных, либо диаграмма неверна — потому что обозначает слишком широкий тип, несовместимый с практикой использования. Наиболее узкий стрелочный тип для слова «1+» выражается так: ( n -- n ^^ u -- u ^^ n -- u ^^ u -- n ) (индексы опускаю, т.к. тут они не имеют значения для нас) Чтобы не перечислять так много простых стрелок, мы можем ввести специальную нотацию, которая позволит обозначить этот же тип более короткой записью. Но, мы должны использовать другой символ вместо «|». Варианты: ( n1!u1 -- n2!u2 ) ( n1%u1 -- n2%u2 ) ( n1,u1 -- n2,u2 ) ( [n1,u1] -- [n2,u2] ) Что выбрать? #forth #stack #notation
В В В Ссылка
нажмите — покажемБитовый вектор — это число 32 или 64 бита. Можете называть битовый массив, но устоявшееся название — вектор. Биты машинного слова представляет множество целых чисел — номеров отдельных установленных битов. Заметка по теме от известного профессора: https://oberoncore.ru/library/wirth_setsВ Понятно, число как множество номеров значащих битов в его двоичном представлении. Я это использовал в реализации встроенного ассемблера, там номера меток это биты в 32 разрядном числе, если бит равен 0 - компилируется код короткого перехода (условного или безусловного), а если равен 1 - компилируется код длинного перехода. L6 J0= .... ............... L6: здесь в бите номер 6 (у меня нумерация разрядов не с 0 а с 1) хранится бит типа перехода для L6