воскресенье, 12 марта 2023 г.

Снова буду переделывать

 Понял, что я опять делаю не так.

Допустим у нас есть выражение:

Integer myvar1 = Integer myvar2 = some_func param1 param2

И предположим, что вызов функции some_func вызывает ошибку из-за неправильного параметра param2

Если просто сперва создать переменные, а затем вызвать функцию, то получится, что исправив параметр, мы не сможем выполнить ту же самую строку, поскольку переменные уже созданы.

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

четверг, 9 марта 2023 г.

Сделал функцию calc_hash

 Сделал функцию calc_hash. Теперь она вызывается с параметром - строкой:

calc_hash "some text"

Возращаемый параметр имеет тип Hash и в данном варианте он помещается на стек.

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

Hash myhash = calc_hash "some text"

При вызове функции параметр может быть обязательным или опциональным. Нужно только добавить значение по умолчанию.

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

Пока это у меня ещё не сделано, но раз я собираюсь добавить присваивание значения переменной, то нужно будет это реализовать.

Таким образом в функции появляется оператор return со списком возвращаемых параметров.

Значит мне нужно сделать такой синтаксис:

Type1 var1 Type2 var2 Type3 var3 = some_func param1 param2 param3

И тогда, если функция возвращает несколько значений, а переменных меньше, то лишние значения надо либо игнорировать, либо помещать на стек. Наверное для совместимости лучше игнорировать. Ведь, если число результатов изменится, то это никак не повлияет на вызывающий код.


вторник, 28 февраля 2023 г.

Таблица-список готова

 Задача оказалась проще, чем я предполагал, однако потратил на эти несколько функций не один день.

Итак, шаблон таблицы упорядоченного по хэшам списка, можно считать готовым. Значит можно создавать таблицу функций.

суббота, 25 февраля 2023 г.

Связанные списки, написанные в панике

 Задался вопросом, как хранить данные в таблицах приложения. 

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

Придумал следующее решение. Хранить данные в списках например по 16 элементов на список. А указатели на списки хранить в массиве, память в котором тоже выделять блоками.

Тогда при поиске элемента можно будет посмотреть первый и последний элементы в списке - попадает ли искомый хэш в данный диапазон или нет. И если попадает, то дальше искать элемент уже в этом списке, а если нет - то, искать в меньшую или большую сторону.

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

Но могу ли использовать контейнеры C++? - спросил я самого себя, и решил, что там вряд ли используются самые эффективные алгоритмы. Ещё до этого меня неприятно поразило, насколько в общем виде там всё реализовано, когда я увидел их исходник в отладчике.

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

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

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

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

Долго я исследовал причины, пока наконец не понял, в чём вопрос.

Чтобы начать новый проект, требуется погрузиться в зону комфорта. Расслабиться, успокоиться, ни о чём не беспокоиться.

Но затем, начав писать, требуется работать, ни на что не отвлекаясь, по возможности быстро. И вот это "быстро" пугает, потому что становится страшно сделать ошибку. Чувство комфорта и защищённости рассеивается, и вместе с усталостью возникает паника.

Чего я только не перепробовал, чтобы сохранять чувство комфорта во время работы. Пока не открыл метод самозапугивания.

Ведь если я настолько боюсь чего-то не слишком реального, каких-то иллюзий, то нужно просто напугать себя ещё сильнее.

Например: а что, если за мной шпионят? - такой страх например посещает.

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

А что, если именно я когда-то посоветовал им попробовать наркотики, но давно об этом забыл? - ещё страшнее вариант.

И так далее. В какой-то момент мозг начинает протестовать против подобной ахинеи, и страх уходит.

Но сейчас я вспомнил причину. Причина в том, что по мере роста объёма текста или кода начинаешь забывать, что было до этого. И панику вызывает именно эта потеря определённости.

Ведь наши воспоминания как бы формируют для нас основу уверенности в себе. Если вам только что стёрли память, то это не страшно, потому что вы не помните своих иллюзий, питавших ваши страхи.

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

При этом как мы боремся со страхами? Да просто спрашиваем себя: а чего я боюсь в данный момент?

А тут вы можете ответить себе только: я не помню, я слишком устал.

Сегодня конечно был сложный день. Я не выспался и написал не очень много кода. Спать вроде не хочу, а работать уже не могу - надо как-то отдыхать. Видимо придётся выпить бокальчик рома с кока-колой. 

Не особо хочется, потому что алкоголь меня бодрит.

пятница, 24 февраля 2023 г.

Четыре потока

 Не так-то просто начать не с функции calc_hash - ведь она используется для поиска по таблицам объектов и функций.

Немножко повозился с консолью терминала. Новый алгоритм работы в терминале потребовал изменить логику потоков. Которая получается отличной от концепции терминала в языке C.

В C мы имеем три потока: 

  • поток ввода, 
  • поток вывода, 
  • поток вывода сообщений об ошибках.

Новый алгоритм предлагает четыре потока:

  • поток ввода команд,
  • поток ввода текста, выводимого в поток вывода,
  • поток вывода текста,
  • поток вывода сообщений об ошибках.
Вроде бы я составил алгоритм работы при вводе текста и команд из консоли и выводе текста в консоль, но теперь нужно начать выводить текст в файл. Так что не уверен, не придётся ли написанный код править ещё.

Теперь надо сделать функции открытия и закрытия файла для вывода текста. И команд в том числе.

Вижу себе это так:

text_out_file myfile = "~/my_texts/text01.txt" // Задаём имя файла
myfile rewrite // Открываем файл для перезаписи данных
               // Или append - чтобы открыть файл для добавления данных
out_stream = myfile  // Делаем myfile потоком вывода
myfile close    // Закрываем файл (потоком вывода снова становится cout

Значит нужно сделать:
  • таблицу объектов,
  • объект типа text_out_file
  • объект out_stream.
Насчёт объекта out_stream я пока не уверен, ведь это имя свойства консоли. Наверное правильнее сделать объект console, и обращаться к нему явно. Но это нужно придумать, как пользователь будет управлять консолями ввода-вывода.

Неявно консоль создаётся при запуске программы или при выполнении команды exec. Но правильно ли это?

Пока не хочу забегать вперёд.

Новый алгоритм работы в терминале

 Хотел поработать много, но поработал мало, а день таки прошёл.

Неожиданно для себя я увидел следующий алгоритм работы в терминале.

Пользователь вводит текст, и этот текст записывается в файл. Но если он ввёдет на конце строки некий код (например ===), то терминал переключится в режим ввода управляющих команд. А затем, введя ===, можно будет продолжить ввод текста или данных.

Гениально, - сказал я самому себе. - Это почти как вставки кода в веб-страницах.

Поэтому вместо ставших уже традицией функций exec и calc_hash, я решил написать функции чтения текста из файла и записи текста в файл. 


Стоило ли спешить?

 Почитал, что я тут написал в блоге. Честно говоря, про это устаёшь даже читать - не то, что этим заниматься.

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

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

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

Не то, чтобы я совсем терял время впустую весь этот период. Я начал изучать JavaScript и освежил в памяти сведения по нейросетям.

К проекту своего языка мне с одной стороны вроде бы хочется вернуться, поскольку по своему опыту я знаю, что так или иначе всё упрётся в необходимость этой программы. Но с другой стороны мне хочется попрограммировать нечто более развлекательное - какую-нибудь 3D-графику или нейросети.

Парадокс нейросетей я бы сформулировал так. Ими можно управлять, только если добавить классификатор.

Например, логический элемент A & B -> C. Результат его работы - классификация входящих данных: A и B.

Мы можем добавить класс D - получится A & B -> C, D. Пока что всё ОК.

Но что, если A и B - это два пикселя картинки, C и D - тоже два пикселя, а нейросеть - это такой фильтр: почему A не B, как это изменит C и D?

И вот тут получается, что нейросеть, инициализированная случайными значениями, даёт случайный результат. Чтобы определить закономерность для пикселей C и D, нам требуется ввести классификацию, с помощью которой управлять работой нейросети.

Но ведь мозг человека каким-то образом имеет классификатор, который сообщает нам, что яблоко не должно быть фиолетового цвета. Или почему бы и нет, и всё дело только в среде, в которую нас помещает предназначение?

Всё это связано с моей программой таким образом, что я так и не смог ответить самому себе на вопрос, что именно я собираюсь автоматизировать.

Вот, например, язык JavaScript. Есть браузер, задача которого - показать веб-страницу. И вот язык скриптов, который позволяет пользователю автоматизировать взаимодействие пользователя с этой веб-страницей.

Этот язык создан именно для этого, и больше нигде (разве что кроме плат Iskra Neo) не применяется.

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

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

Вот, допустим, мы хотим создать DSP на языке Verilog. Но средства этого языка не позволяют использовать классы и объекты. Это условный, хотя и наглядный пример.

Поэтому именно Форт меня этим привлёк. Тем, что предлагает инкапсулировать логику в простых функциях, не усложняя программу по мере роста её кодовой базы.

Но я приводил здесь пример работы со строками:

Дело в том, что элементы Форта были в ней привлекательной частью. Но работа со строками получилась очень странной из-за этого. Только представьте - как выглядит функция substr на объектном Форте:

"Hello World!" 0 5 rot sub_str "Hello" == if
    "How are you?" cout <<
;

Здесь мы берём слово "Hello", и если это "Hello", то отвечаем "How are you?". В целом неплохо, но были варианты просто дикие - например разбор аргументов командной строки.

Но вот, к чему я пришёл в итоге нескольких лет разработки:

create function add 2

param int x

param int y

result int

return math.add x y

end


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


Парадокс, который мне открылся, можно описать так: всё в программе - это набор таблиц. Данные - таблица, код - таблица, и чем лучше они упорядочены, тем лучше работает программа. Поэтому в моей программе нет смысла - ведь если я не могу упорядочить программу на C++, то и любой другой язык мне в этом не сможет помочь.


Тем не менее, сейчас затея снова кажется мне интересной.


Глядя на свой предыдущий код, я вижу, насколько он плохо структурирован. Даже зная, как это работает, невозможно отыскать ошибку.


Простой пример: пользователь вводит "hello и Enter. Консоль сообщает ошибку: глобальное имя "hell не найдено.


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


Но читая блог, я понял, в чём моя ошибка - я слишком спешил писать код.


И в самом деле - прошло 2 года, программа так и не написана - так стоило ли спешить?

суббота, 13 марта 2021 г.

Продолжаю размышлять о синтаксисе

 Я понял, в чём моя ошибка с массивами типа [] (ValueArray) - я "прячу" данные. Когда пользователь создаёт массив, я создаю новый фрейм на стеке, и данные доступны. Но затем я помещаю их в массив, и теперь пытаюсь придумать удобный способ их оттуда извлечь.

Получается как бы следующая иерархия адресации:

Стек Фрейм Массив Элемент_массива

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

На самом деле решение есть и оно состоит в том, что нужно оставить фрейм стека фреймом стека, просто добавив ссылку на этот массив в последний фрейм. Тогда каждый массив типа [] (ValueArray) будет храниться как фрейм на стеке, и к нему можно будет удобно обращаться с помощью адресации #x$y - где x - номер фрейма, а y - номер элемента.

суббота, 6 марта 2021 г.

Размышляю о синтаксисе

 Ввод массивов заставил меня задуматься о синтаксисе. Итак, изначальная идея состоит в том, чтобы сделать синтаксис таким:

Object Method Parameter

Если метод возвращает значение, то оно кладётся на стек.

С методом get всё прекрасно. Мы можем вызывать его явно:

my_array get 11 // Положить на стек 12-й элемент массива my_array

Но с методом set всё сложнее. Мы должны иметь возможность указать программе, что мы хотим изменить 12-й элемент массива my_array.

Теоретически это можно было бы сделать так:

my_array set 11 = "zzz"

Или даже проще - вызывать метод set неявно:

my_array 11 = "zzz"

Но, что если 12-й элемент массива my_array - тоже массив, и мы хотим изменить в нём третий элемент? Получается конструкция вида:

my_array 11 3 = "ddd"

Вроде всё прекрасно, сейчас у меня именно так и сделано. Но мне пришла замечательная на первый взгляд идея - чтобы метод get массива мог возвращать массив данных, если в качестве индекса использовать массив с индексами.

my_array [ 1 2 [ 6 8 ] [ 5 3 ]  9 ]

Выглядит прекрасно. Эта команда возвратит массив с элементами массива my_array в следующем порядке:

[ 1 2 6 7 8 5 4 3 9 ]

Но неявный вызов приводит к тому, что например мы вводим массивы:

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

Глядя на этот код можно подумать, что третья команда создаст массив

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

Но это не так. Она эквивалента командам

[
$$0 get $$1
]

И тут я понимаю, что неявный вызов get делает программу трудночитаемой. Например, что если число 11 мы будем брать со стека?

my_array $0 3 = "ddd"

Цифра 3 задаёт 4-й элемент. Но четвёртый элемент в массиве $0 (кто знает, вдруг это массив?) или 4-й элемент в массиве my_array $0 ?

Получается так, что мы должны оставить перечисление элементов быть перечислением (как оно и выглядит в коде), взятие элемента по индексу снабдить явным синтаксисом - что-нибудь вроде:

{ { my_array } 11 } 3

Выглядит громоздко, но это вполне читаемо.

Возможно возникнет вопрос: зачем городить огород, если можно разложить эту конструкцию на последовательность операций вида:

my_array select 11
$0 select 3
$1 = "zzz"

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

Второй недостаток - в том, что у нас появляется новый тип объекта - ссылка на элемент массива. $0 - это ссылка, $1 - это ссылка. Они кладутся на стек и там лежат, хотя изначально в этом нет необходимости.

Проблема элемента-ссылки на объект в массиве в том, что она может стать невалидной, если мы удалим этот элемент из массива. То есть это объект временного характера, и лучше всего ограничить его время существования пределами одной команды. 

Поэтому синтаксис с фигурными скобками выглядит логичнее и читабельнее. Ведь мы можем использовать разные варианты в одной строке:

{ { my_array } $0 } $1 = "zzz"
{ my_array } { $0 } $1 = "ddd"

Но лично меня как-то удручают эти фигурные скобки. Хотя они позволяют использовать их для создания выборок:

{ my_array1 my_array2 my_array3 } 256

Это интересная возможность. Пожалуй я так и сделаю. Раз идей получше нет.

Но я подумал вот о чём. Конструкция вида

{ my_array } [ 11 ]

должна возвращать на стек массив, содержащий 12-й элемент массива my_array. А вот конструкция вида

{ { my_array } 11 } 3 = "zzz"

она должна работать с массивом на стеке? Как если бы

{ my_array } 11
{ $0 } 3 = "zzz"

Или она должна работать с 12-м элементом массива my_array, как с массивом?

Вот, что я вижу. Мы имеем оператор определения диапазона данных. А нам нужен оператор указания адреса в массиве. То есть это две разных операции. 

Получается, что нужно писать код примерно так:

3 of { 11 of my_array }
[ 3 of $0 ] of my_array

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

И оператор задания выборки мне тоже нравится:

256 of { my_array1 my_array2 [ 1 2 3 ] of my_array3 }

Красота конечно, но мозг программиста вряд ли сможет понять, что имел в виду другой программист. Потребуется метод для просмотра подобных выборок.

Но я подумал вот о чём. Что, если мы захотим сделать выборку по условию? Например для всех элементов больше 100? Как это можно было бы задать?

Думаю, что логично сделать это конструкциями языка. Что-нибудь вроде:

for_each { my_array1 my_array2 } do
   if $0 > 100 then
      $0 = "zzz"
   ;
;

В данном случае на каждой итерации элемент $0 - это указатель на элемент выборки. То есть снова объект-ссылка, что нехорошо. 

С другой стороны, объект-выборку проще всего представить как массив объектов-ссылок. И в принципе было бы довольно удобно иметь возможность расположить его на стеке, чтобы затем ссылаться на него. Но нужно непременно снабдить его средствами инвалидации, а так же предоставить программисту возможность проверять валидность.

В целом это выглядит довольно просто. Если невалиден один из элементов, то невалидной становится вся выборка. Тогда не нужно будет при доступе к каждому элементу проверять его персональную валидность.

Но я спросил себя вот о чём. Должна ли запись вида

[ [ 0 10] ] of my_array

создавать выборку, или выборка должна задаваться явно?

{ [ [ 0 10 ] ] of my_array }

Разница в том, что в первом случае мы можем копировать элементы, а во втором создавать массив-выборку. Ведь аналогичный синтаксис можно применять и для доступа к свойствам объектов:

x of my_point = 10
y of my_point = 20

Логично предположить, что в данном случае оператор of создаёт выборку, то есть указатель на объект-свойство. Это красиво выглядит и не нужно городить конструкцию из фигурных скобок. Но как тогда произвести копирование элементов из массива в массив на стеке?

Теоретически ничто не мешает использовать для этого метод get явным образом:

my_array get [ [ 0 10 ] ]

А для создания выборки оператор of:

[ [ 0 10 ] ] of my_array = "zzz"

Наверное я так и сделаю. Это выглядит читабельно и логика синтаксиса не нарушается. Да будет так.

Добавил массив типа ValueArray

 Добавил ввод массива типа [] (ValueArray). Хотел сегодня ещё добавить функцию get, но не успел.

Ввод массивов типа ValueArray

Поскольку ввод токенов работает довольно стабильно, я заменил отладочный ввод на красивое "ok".

Теперь думаю добавить типовые массивы, как-то [String], [Boolean], [Char] и т.п. 

И также нужно добавить инвалидацию указателей на данные в массиве. Этот метод показал себя с очень положительной стороны - если где-то забыл удалить объект-указатель, тут же получаю ошибку по завершении команды.

среда, 3 марта 2021 г.

Добавил конвертеры интегральных типов

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

Использование стековых индексов в качестве параметра

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

вторник, 2 марта 2021 г.

Новая версия, calc_hash и exec

 Несколько дней спал. Наконец вчера проснулся и стал делать новую версию. Перед тем, как добавить конвертеры интегральных типов, добавил функции calc_hash и exec.

Функция calc_hash

Функция exec

В этой версии вместо указателей при запросе элемента на стеке создаётся объект тип ValuePtr и возвращается указатель на него. Также этот указатель запоминается в векторе в классе фрейма, и при удалении элемента все указатели на этот элемент становятся невалидными. Соответственно при попытке получить указатель на значение, проверяется валидность и выдаётся ошибка, если указатель невалиден. 

При завершении команды все запомненные указатели ValuePtr удаляются, и если где-то в коде я забыл его удалить, это будет сразу видно.

четверг, 25 февраля 2021 г.

Собираюсь снова всё переделать

 Я решил добавить метод set в класс массива [] (ValueArray) и столкнулся с такой проблемой. Метод set - это метод класса, следовательно я должен выбрать объект-массив. Но при этом я должен указать ему индекс элемента, который нужно изменить. То есть нужно переделать адресацию объектов.

При этом адрес должен превратиться в указатель. Вот эта область памяти, и я хочу записать туда значение. Но сделать я этого не могу из-за неверно выбранного формата хранения данных. То есть это тоже нужно переделать.

ОК, придётся переделать. Не хотелось мне работать с указателями, но в данном случае без них просто не обойтись. Поэтому я придумал следующий способ работы с ними.

Когда программа запрашивает у класса стека адрес объекта, он возвращает не просто сишный указатель, а объект, хранящий указатель и признак валидности. Адрес этого объекта записывается с массив, и при удалении объекта, на который указатель ссылается, класс стека просто изменит флаг валидности, сообщив программе, что данный указатель больше не валиден.

Дело в том, что опыт использования таблицы подсчёта ссылок показал, что программа не может это всё контроллировать, поскольку за это отвечает код пользователя. И в результате вполне могут возникать ситуации, когда объект уже удалён, а указатели ещё нет. И они просто стали невалидными, но программа не может это отследить.

Я спросил: а что заставляет меня использовать именно сишные указатели? Пусть указатели станут умными - с признаком валидности.

Но я пока в раздумьях. Ведь если указатель стал невалиден, значит есть ошибка в коде пользователя. Как сообщить пользователю, что он удалил не тот объект, который стоило удалять, если сам объект уже удалён? Как он поймёт, о каком объекте идёт речь?

В принципе можно было бы вообще не разрешить пользователю изменять объекты в массиве. Просто создать видоизменённую копию массива, а затем через метод set заменить оригинальный массив копией. Но этот способ хотя и логически верен, на практике приведёт к жутким тормозам и непомерным требованиям к памяти.

среда, 24 февраля 2021 г.

Переделал ValueArray

 Сегодня занялся переделкой хранения данных в массиве типа [] (ValueArray), и вот, таки переделал. По ощущениям, вроде даже стало быстрее, поскольку при копировании данных делается всего один resize() вместо new для каждого элемента в массиве.

Выборка элементов массива по индексу типа массив

Теперь думаю добавить массивы интегральных типов. В них данные представлены иначе - например для [Boolean] данные хранятся как bool data[].

Теоретически можно было бы создать специальный варианта класса StackValue, который бы брал данные из массива, на практике можно будет создать лишь специальный вариант функции get<typename>(index).

Обнаружил дичайшую вещь

Мы разбирали этот пример на курсах программирования, но я не придал этому особого значения тогда. А теперь вот вспомнил.

Сделал выборку элементов из массива с помощью массива в качестве индекса для функции get класса [] (ValueArray).

Выборка элементов массива с помощью массива как индекса

Так вот, дичайшая вещь состоит в том, что в данных массива типа ValueArray я храню указатели на объекты типа StackValue. И всё бы прекрасно, но чтобы не возиться с подсчётом ссылок, при изменении размеров фрейма стека я просто копирую эти значения, и таким образом, если внутри функции я вызываю другую функцию, которая изменяет стек, то старые указатели уже не работают. Поэтому я не могу использовать переменные-ссылки при работе с массивом типа ValueArray.

А дичь вот в чём. 

У меня возникла непонятная ошибка. Я ведь получаю текущие значения указателей в массиве. Но ошибка была в моём коде. Он выглядит так:

#define arr (stack->at(ctx->object))

#define idx (stack->at(ctx->param))

#define nval (idx.array_at(n))


result->array_push_back(arr.array_at(nval.get_size(ctx)));


Глядя на эту строку, можно подумать, что компилятор превращает это в код вида:


size_t nn = nval.get_size(ctx);

const StackValue& val = arr.array_at(nn);

result->array_push_back(val);


А на самом деле в код вида:


const StackValue& v1 = arr;

const StackValue& v2 = nval;

size_t nn = v2.get_size(ctx);

const StackValue& val = v1.array_at(nn);

result->array_push_back(val);


Получается дичайшая дичь, потому что функция get_size() изменяет стек. И первый пример кода работает правильно, а второй вызывает ошибку.


Память подсказывает, что я уже сталкивался с этим, работая с указателями, и мне пришлось упорядочить работу со стеком таким образом, что при вызове функции объекты, которые нужно со стека потом удалить, я удалял в конце функции. Не помню, как это всё в итоге работало, но сражался я с этой проблемой очень серьёзно. Ведь удалять эти объекты должна самая-самая первая функция.


В данном случае я думаю поступить иначе - буду хранить в данных не указатели, а сами сериализованные объекты. Чтобы при операциях со стеком не терялись указатели. Тогда можно будет использовать переменные-ссылки.

вторник, 23 февраля 2021 г.

Доделал ввод индексов параметра

 Доделал ввод индексов для массива как параметра. Это оказалось несложно - я просто скопировал код и внёс коррективы.

Ввод массива как параметра и конвертация значения из него в тип String

Мне нравится 11 версия программы. Она уже очень непохожа на объектный Форт.

Когда я анализировал, чем объектный Форт меня не устраивает как программиста, то первое, что я понял: я не так пишу программы, как мне это кажется. На самом деле я программирую компилятор, а не создаю код приложения.

Когда я определяю функцию, то мне кажется, что я определяю функцию в моей программе. А на самом деле я программирую компилятор: "создать функцию". И именно этого я ожидаю от средства разработки как программист.

Тогда я понял, что мне нужен именно такой язык программирования, а вовсе не объектный Форт. Форт на самом деле привлекает меня совсем другими вещами, а вовсе не возможностью двигать данные на стеке.

И тогда я спросил себя: ты будешь писать объектный Форт или нечто, напоминающее PostScript?

Объектный Форт я уже написал, и по мере его написания, он стал видоизменяться. Причём меняться в сторону языка для работы со строками - в сторону PHP. Но в результате получилось что-то среднее между Фортом и PHP с синтаксисом, напоминающим Python.

И очень многое в этой программе меня как программиста не устраивало. Я спросил себя: получился ли шедевр? - Нет, на шедевр это точно было не похоже.

Дело в том, что элементы Форта были в ней привлекательной частью. Но работа со строками получилась очень странной из-за этого. Только представьте - как выглядит функция substr на объектном Форте:

"Hello World!" 0 5 rot sub_str "Hello" == if
    "How are you?" cout <<
;

Здесь мы берём слово "Hello", и если это "Hello", то отвечаем "How are you?". В целом неплохо, но были варианты просто дикие - например разбор аргументов командной строки.

И всё бы ничего, если бы не последний аргумент. Реальный опыт отладки алгоритма. Очень просто допустить ошибку, но потом очень сложно найти в программе то место, где она возникает. В какой-то момент просто понимаешь, что тебе как программисту требуется простой читаемый синтаксис, а не вот это вот всё - dup rot swap drop

Побаловаться - прикольно. Написать реальную программу - честно скажу, на ассемблере это ещё прикольнее, а работает в 1000 раз быстрее.

Возможно именно поэтому 11 версия по синтаксису чем-то схожа с ассемблером. Насколько просто с этим будет работать - пока не знаю. Но скоро мне предстоит это узнать. Пока что всё это похоже на какую-то магию - очень прикольно.

Делаю консоль

 Сделал ввод массивов в консоли как объектов и как параметров. Осталось сделать ввод индекса для массивов как параметров.

Вызов методов класса [] (ValueArray)

Попробовал использовать для работы с элементами массива промежуточный класс ArrayValue, но сразу отказался от этой затеи, поскольку получается масло масленное - мы вызываем метод get для массива, а он возвращает нам индексы объекта и элемента в виде класса ArrayValue. Которые мы и так должны знать, чтобы вызвать get.

Сегодня я задал себе вопрос, может ли быть значение метода get массива тоже массивом. И оказывается, что таким способом вполне можно делать выборки нескольких элементов из массива. Нужно будет это добавить. Мне нравится эта идея.

понедельник, 22 февраля 2021 г.

Добавил класс IStream

 Сегодня занялся добавлением класса IStream. Для этого выделил процедуры ввода токенов в отдельный класс с говорящим названием IStream, а также сделал обработку ошибок многоуровневой. Теперь при вызове ошибки внутри команды exec выдаётся сообщение об ошибке, и генерится новое исключение, и так далее - до самого исконного уровня вложенности. В данном случае это консоль ввода.

Обработка ошибки при выполнении команды exec

Теперь нужно перечитать ещё раз то, что я тут написал вчера. Вспомнить, на чём же я остановился. Ведь было что-то ещё.

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

воскресенье, 21 февраля 2021 г.

Добавил ввод массива

 Начал добавлять ввод массивов и заметил, что у меня работу консоли нужно видоизменить таким образом, чтобы входной поток был отделён от синтаксического разбора. Поскольку при вводе массива как параметра, а также при вызове команды exec входной поток как бы разделяется, и консоль инициализируется.

Ввод массивов и генерация ошибки в команде exec

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

Задумался о быстродействии программы. Массивы заставляют задуматься.

У нас как бы есть объект типа StackValue, но работа с объектом, а не с данными, приводит к тому, что в некоторых случаях приходится данные копировать, а этого хотелось бы избежать. Тут есть над чем подумать.

пятница, 19 февраля 2021 г.

Добавил литералы интегральных типов

 Преимущество компактного кода - скорость рефакторинга. То, что в прошлых версиях занимало наверное почти неделю, в этой версии заняло 1 (один) день. Который я посвятил добавлению литералов интегральных типов.

Вычисление хэшей для встроенных функций

Похоже, что всё прекрасно работает.

Конвертация значения типа LongLong в значение типа LongDouble и обратно

Следовательно теперь можно добавить массивы.