суббота, 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 удаляются, и если где-то в коде я забыл его удалить, это будет сразу видно.