Как передать значение из красного / системного в красный? - PullRequest
2 голосов
/ 16 июня 2020

Мне нужно передать значение, которое я генерирую в 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 вот Red block, который я хочу заполнить данными из Red / System.

https://github.com/meijeru/red.specs-public/blob/master/specs.adoc#routine -type

1 Ответ

3 голосов
/ 16 июня 2020

Intro

Red использует стек данных для передачи аргументов и возврата результата. Каждое значение в стеке представляет собой коробочную структуру размером 4 указателя платформы и может содержать ссылки на внешние буферы; это означает, что вам нужно создать их и поместить sh в стек, хотя некоторые примитивные типы Red / System (например, logic! или integer!) автоматически повышаются, если вы их возвращаете.

Однако в вашем случае использование стека не обязательно, поскольку вы хотите распределять значения непосредственно в блоке. Опыт программирования на низком уровне и знание Red / System с Red runtime API являются необходимыми предпосылками для этой задачи. Итак, давайте рассмотрим ваш пример и go шаг за шагом.

Распаковка

  1. У вас есть блок, и вы хотите добавить к нему два значения: 123 и "Hello". Предположим, вы хотите сделать это из Red / System. Для этого нам нужно написать процедуру.
    list: []
    foo: routine [][...]
    
  2. Внутри этой процедуры вам нужно получить блок, на который ссылается list word. Сложный способ сделать это - создать экземпляр символа и найти значение в глобальном контексте по его идентификатору:

    list: []
    
    foo: routine [
        /local
            blk [red-block!]
    ][
        blk: as red-block! _context/get-global symbol/make "list"
    ]
    

    Передача list в качестве аргумента была бы более разумной, но я оставлю его как есть для образовательных целей.

  3. Теперь мы хотим добавить 123 к этому блоку. Есть функция block/rs-append, которая делает именно это, но принимает аргумент в рамке. Итак, нам нужно сначала поместить себя в коробку 123.

    1. Это - вот как выглядит упакованное целое число; как видите, это просто 32-битное 123 значение + заголовок слота и отступы. Мы можем создать и инициализировать такую ​​структуру сами:
      int: stack/push*         ; allocate slot on data stack
      int/header: TYPE_INTEGER ; set datatype
      int/value: 123           ; set value
      
      К счастью, среда выполнения Red уже покрывает это с помощью integer/box функции, которая принимает Red / System integer! и возвращает упакованную red-integer! структуру:
      integer/box 123
      
    2. Теперь нам нужно добавить это целое число в рамку к блоку. Интуитивно мы можем проверить 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
    ]
    
  4. Теперь мы хотим добавить строку "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 еще не стабилизирован, поэтому в настоящий момент не время задокументировать его и отлить дизайнерские решения в камне. Опытные разработчики могут довольно быстро сориентироваться, но для этого требуется solid концептуальное понимание оценочной модели Red - эти основы - то, что вам нужно сначала освоить.

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

...