Как передать значение из красного / системного в красный?
Мне нужно передать значение, которое я генерирую в Red/System, в Red. Я обнаружил документы, но не нашел примера, как им пользоваться. Вот мой код:
Red []
#system [
data!: alias struct! [
a [integer!]
b [c-string!]
]
data: declare data!
_foo: func [return: [data!]]
[
data/a: 123
data/b: "Hello"
return data
]
]
sqlite: context
[
my-red-block: []; I want to place here: 123 "Hello"
foo: routine [
/local
x [data!]
]
[
x: _foo
; next line do now work
; push my-red-block x/a
]
]
view [button "Select" [sqlite/foo]]
my-red-block
вот красный block
которую я хочу заполнить данными из Red/System part.
https://github.com/meijeru/red.specs-public/blob/master/specs.adoc
1 ответ
вступление
Красный использует стек данных для передачи аргументов и возврата результата. Каждое значение в стеке представляет собой коробочную структуру размером 4 указателя платформы и может содержать ссылки на внешние буферы; это означает, что вам необходимо создать их и поместить в стек, хотя некоторые примитивные типы Red/System (например,logic!
или integer!
) продвигаются автоматически, если вы их вернете.
Однако в вашем случае использование стека не обязательно, поскольку вы хотите распределять значения непосредственно в блоке. Опыт низкоуровневого программирования и знание Red/System с Red Runtime API являются необходимыми предпосылками для выполнения этой задачи. Итак, давайте рассмотрим ваш пример шаг за шагом.
Распаковка
- У вас есть блок, и вы хотите добавить к нему два значения,
123
а также"Hello"
. Предположим, вы хотите сделать это из Red/System. Для этого нам нужно написать процедуру.list: [] foo: routine [][...]
Внутри этой процедуры вам необходимо получить блок, на который ссылается
list
слово. Сложный способ сделать это - создать экземпляр символа и найти значение в глобальном контексте по его идентификатору:list: [] foo: routine [ /local blk [red-block!] ][ blk: as red-block! _context/get-global symbol/make "list" ]
Проходящий
list
в качестве аргумента было бы более разумным, но я оставлю его как есть в образовательных целях.Теперь мы хотим добавить
123
в этот блок. Естьblock/rs-append
функция, которая делает именно это, но принимает аргумент в рамке. Итак, нам нужно боксировать123
мы в первую очередь.- Вот как выглядит упакованное целое число; как видите, это просто 32-битный
123
значение + заголовок слота и отступ. Мы можем сами построить и инициализировать такую структуру:
К счастью, среда выполнения Red уже покрывает этоint: stack/push* ; allocate slot on data stack int/header: TYPE_INTEGER ; set datatype int/value: 123 ; set value
integer/box
функция, которая принимает Красный / Системныйinteger!
и возвращает упакованныйred-integer!
структура:integer/box 123
- Теперь нам нужно добавить это целое число в рамку к блоку. Интуитивно мы можем проверить
block.reds
определения и найтиblock/rs-append
что соответствует нашим требованиям:
В конце этого шага у нас есть:block/rs-append blk as red-value! integer/box 123
list: [] foo: routine [ /local blk [red-block!] ][ blk: as red-block! _context/get-global symbol/make "list" block/rs-append blk as red-value! integer/box 123 ]
- Вот как выглядит упакованное целое число; как видите, это просто 32-битный
Теперь мы хотим добавить
"Hello"
строка, но сначала нам нужно ее построить. Красные строки поддерживают UTF-8 и используют внутреннюю кодировку фиксированного размера (1, 2 или 4 байта на символ, в зависимости от максимального размера кодовой точки); это много деталей, которые нужно исправить вручную, поэтому типичный способ создания такой строки - преобразовать ее изc-string!
.list: [] foo: routine [ /local blk [red-block!] str [c-string!] ][ blk: as red-block! _context/get-global symbol/make "list" block/rs-append blk as red-value! integer/box 123 str: "Hello" ]
Изучение
string!
определения времени выполнения типов данных, вы заметите несколько удобных оболочек с префиксомload
; это соглашение, указывающее, что такая функция может быть использована для построения (то есть "загрузки") высокоуровневого значения Red из низкоуровневых частей Red/System, в нашем случаеred-string!
изc-string!
. Поскольку мы хотим построить его в хвосте блока, мы можем использоватьstring/load-in
:str: "Hello" string/load-in str length? str blk UTF-8
Обратите внимание, что я использую
length?
вместо тогоsize?
для исключения байта, завершающегося NUL.
Вывод
Это оно. В конце концов, мы можем немного привести код в порядок и проверить, работает ли он вообще:
Red [Note: "compile in release mode (-r flag)"]
list: []
foo: routine [
/local
blk [red-block!]
int [integer!]
str [c-string!]
][
blk: as red-block! _context/get-global symbol/make "list"
int: 123
str: "Hello"
block/rs-append blk as red-value! integer/box int
string/load-in str length? str blk UTF-8
]
foo
probe list
Компиляция этого сценария в режиме выпуска и выполнение полученного двоичного файла из оболочки дает ожидаемый результат:
[123 "Hello"]
Излишне говорить, что все это может показаться новичкам довольно ошеломляющим: хотя и Red, и Red/System имеют приличную документацию и учебные ресурсы, их переход через взаимодействие во время выполнения - неизведанная территория. Причина в том, что проект развивается, а API еще не стабилизирован, поэтому в настоящий момент не время задокументировать его и отразить проектные решения. Однако опытные разработчики могут довольно быстро сориентироваться, но для этого требуется твердое концептуальное понимание оценочной модели Red - эти основы - это то, что вам нужно освоить в первую очередь.
Также существует множество привязок библиотек, из которых вы можете поучиться - судя по исходному примеру, вы пытаетесь создать интерфейс CRUD View поверх SQLite.