Простой декларативный DSL средствами GNU Make
kayo — Сб, 11/06/2016 - 09:15
Мы, программисты, всегда стремимся к простоте, краткости и лаконичности нашего кода. Это связано с тем, что чаще приходится его именно читать, а не писать. Именно по этой причине мы предпочитаем декларативные языки императивным, если постановка задачи даёт нам такую возможность. А если же нет? И всё непременно должно быть написано на языке C? Тогда мы изобретаем различные прикладные языки, так называемые DSL. В этой статье мы реализуем один из таких прикладных декларативных языков для генерации низкоуровневого кода на языке C.
Введение
Для начала краткое введение. Есть у нас в uprojects такая библиотека libparam. Она позволяет управлять устройствами на микроконтроллерах унифицированным способом через различные интерфейсы путём считывания и установки значений параметров. Каждый параметр имеет дескриптор, который представляет собой экземпляр структуры языка C, объявленный как статическая константа. Описывать эти дескрипторы скудными средствами языка C то ещё удовольствие: очень просто что-то забыть или перепутать, тогда как на деле в них представлено не так уж много информации и это должно быть простым механическим процессом без всяких премудростей.
Проблема описаний встала ещё более остро при разработке libparam2, усовершенствованной версии данной библиотеки, которая пишется с нуля. Здесь дескрипторы ещё более хитрые, что связано с потребностью экономии ПЗУ микроконтроллеров и расширением возможностей новой системы типов. Теперь то уж точно было бы куда более приятно описывать параметры на неком DSL, который бы всегда корректно компилировался в код на C.
Решение
Я противник написания велосипедов: если этого можно избежать, то это нужно избегать. Посему применять мы будем GNU Make, вместо разработки своего компилятора как отдельной программы.
Конструируем DSL
Способы объявления переменных в Makefile помогут нам в разработке удачного DSL. Их существует как минимум два, и рассмотрим мы их на примере:
# Объявление в одну строку # краткий способ, более # подходит для атомарных # однострочных значений flat_var=flat value # Объявление в виде блока # расширенный способ, более # подходит для сложных # многострочных значений define space_var space value endef
Средствами make мы можем обрабатывать эти переменные, извлекая из них нужную информацию для последующей обработки.
Итак, набросаем структуру нашего DSL:
define параметры define имя_параметра_1 type=тип_параметра_1 info=Полное описание параметра 1 unit=Единицы значений параметра 1 def=Значение по умолчанию field1=значение дополнительного поля 1 ... fieldN=значение дополнительного поля N endef ... define имя_параметра_N ... endef endef
Как видим, всё довольно таки кратко и понятно, ничего лишнего. Вот как может выглядеть типичное описание параметров на таком языке:
define params define u8_num type=uint size=8 cell=num_u8 flag=getable info[en]=The readonly 8-bit unsigned integer with unit info[ru]=8-битовое беззнаковое целое только для чтения с единицами unit[en]=pcs unit[ru]=шт. def=11 endef define some_str type=cstr cell=str size=32 flag=getable setable info[en]=The readable/writable null-terminated C-String info[ru]=Строка в Си стиле для чтения и записи def[en]=Default value def[ru]=Значение по-умолчанию endef define real_num type=real size=32 get=real_get set=real_set flag=getable setable persist info=The positive floating point number with getter and setter def=0.0 min=-1.0 max=1.0 step=0.1 endef define ctl_mode type=enum info=Control mode info[ru]=Режим управления opts=off auto man off.info=Disabled off.info[ru]=Выключено auto.info=Automatic auto.info[ru]=Автоматически man.info=Manual endef define aes_key type=hbin size=16 def=E3 E5 3E C1 96 E0 3F 28 E3 22 17 30 DD E2 B4 3C flag=getable setable persist endef define ip_addr type=ipv4 def=192.168.1.1 flag=getable setable persist endef define mac_addr type=mac def=12:34:56:AB:CD:EF info=ARP mac address of interface. endef endef
Это примерно в трое короче, чем то же самое описание в C, и на порядок боле читаемое. В качестве приятного бонуса у нас почти бесплатно присутствует локализация, позволяющая собирать локализованные версии прошивки устройства.
Реализуем компилятор
Компилятор, а точнее будет сказать препроцессор, получится достаточно прост. Нет необходимости в разборе языка, поскольку он реализован исключительно конструкциями GNU Make. Потребуется разве что некоторая обработка кода перед вычислением. Итак, перед нами стоит задача трансформации кода на нашем импровизированном DSL в код на C, содержащий дескрипторы параметров в порядке их объявления.
Получаем список параметров
В GNU Make как во всяком порядочном функциональном интерпретируемом языке имеется злая функция eval, вычисляющая код на самом себе. Если мы скормим значение переменной params из примера описания выше, то в среде выполнения появятся новые переменные с именами параметров. Существует специальная переменная .VARIABLES, в которой содержится список всех переменных в текущем состоянии среды выполнения. Существует простой способ получить список наших параметров:
# подключаем исходник include params.lp2 # получаем текущий набор переменных # и добавляем к нему нашу новую переменную param.list:=$(.VARIABLES) param.list # вычисляем параметры $(eval $(params)) # берём новый набор переменных и убираем те, # что были известны до вычисления param.list:=$(filter-out $(param.list),$(.VARIABLES)) # выводим список параметров $(info $(param.list))
Запускаем и смотрим, что получилось:
$ make -f param_pp.mk str aes_key u8_num real
Список переменных то мы получили, однако порядок объявлений не сохранился. Такое поведение обусловлено тем, что переменные в GNU Make хранятся в хэш-таблице, и это именно набор, а не список. Так как же быть в таком случае? А у нас всегда есть коварный запасной план:
# подключаем исходник include params.mk # заменяем все "define имя_параметра" # на "define param/имя_параметра" # затем фильтруем по шаблону "param/%" # и убираем префикс param/ param.list:=$(patsubst param/%,%,$(strip $(filter param/%,$(subst define ,define param/,$(params))))) # выводим список параметров $(info $(param.list))
Запускаем и видим, что теперь всё работает, как и планировалось, и для этого было достаточно обработать исходник без вычисления:
$ make -f test.mk u8_num str real aes_key
Вычисление описания параметров
Далее нам нужно как-то получать информацию о каждом параметре, чтобы формировать вывод. Мы могли бы просто вычислить значение переменной params, как проделывали это раньше, однако тут нас ожидает неприятный момент, связанный с тем, что имена новых переменных могут совпадать с уже имеющимися в текущем контексте.
Решить проблему можно двумя способами:
- Как-то сохранить текущие значения совпадающих переменных и восстановить после обработки
- Обеспечить уникальность имён переменных
Мы пойдём вторым путём, просто добавим префиксы/суффиксы к именам в исходнике с помощью функции subst:
# Добавляем к именам параметров префикс param/ # а к именам полей в описаниях суффикс .param param.text:=$$(subst =,.param=,$$(subst define ,define param/,$$($(1))))
Префикс и суффикс можно сделать более сложными, чтобы наверняка избежать пересечений.
После вычисления и обработки каждый раз у нас остаётся переменные, которые нам больше не требуются и могут даже мешать. Поэтому требуется удалить их из контекста. Сделать это можно както так:
# Разобъявление переменной define undef-var undefine $(1) endef # Разобъявление всех переменных undef-vars=$(foreach v,$(1),$(eval $(call undef-var,$(v)))) # Удаляем из контекста все поля параметра $(call undef-vars,$(filter %.param,$(.VARIABLES))) # Удаляем из контекста все параметры $(call undef-vars,$(patsubst %,param/%,$(param.list)))
Локализация
Достаточно просто организовать локализацию. В примере выше к каждому полю, которое мы хотели бы перевести, мы добавили суффикс [langcode], с буквенным кодом языка. Теперь, чтобы скомпилировать описания под конкретный язык, достаточно просто убрать этот суффикс для нужного языка, и соответствующая переменная будет использована:
# локализация ifneq (,$$(param.proc.lang)) param.text := $$(subst [$$(param.proc.lang)]=,=,$$(param.text)) endif
Собираем всё вместе
Осталось собрать все описанные выше идеи воедино. Посему далее пойдёт исходник с комментариями:
# разобъявление переменной define undef-var undefine $(1) endef # разобъявление переменных undef-vars = $(foreach v,$(1),$(eval $(call undef-var,$(v)))) # символ запятая comma:=, # вывод отладочных сообщений # по умолчанию включен param.proc.debug ?= on # язык для локализации # по умолчанию en param.proc.lang ?= en # поля параметров param.base.fields := type name flag # основные param.bind.fields := cell get set # привязка param.cons.fields := def # ограничения param.text.fields := info unit item hint # текстовые param.all.fields := $(foreach g,base bind cons text,$(param.$(g).fields)) # все вместе # поддерживаемые типы параметров param.types := uint sint real enum cstr hbin mac ipv4 # поддерживаемые флаги param.flags := getable setable persist # переопределения привязок param.bind.ext = extern param_$(1)_f $($(1).param) param.cell.ext = extern $(param.$(type.param).ctype) $($(1).param)$(if $(param.$(type.param).ptype),[$(param-csize)]) param.bind.def = param_$(1)_f *$(1) param.cell.def = $(param.$(type.param).ctype) *$(1) param.bind.val = $($(1).param) param.cell.val = &$($(1).param) # комментарии для генерируемого исходника param.def.cmt:=parameter definition param.dat.cmt:=parameter data param.base.cmt:=basic fields param.bind.cmt:=binding fields param.cons.cmt:=constraint fields param.text.cmt:=textual fields param.type.cmt:=type param.name.cmt:=name param.flag.cmt:=flags param.size.cmt:=size of value param.cell.cmt:=pointer to data cell param.get.cmt:=getter function param.set.cmt:=setter function param.def.cmt:=default value param.min.cmt:=minimum param.max.cmt:=maximum param.step.cmt:=step param.info.cmt:=short description param.unit.cmt:=measurement units param.item.cmt:=item for menu param.hint.cmt:=hint for menu # типо-специфичные переопределения param.atom.size = 32$(warning Because no size field is provided for "$(name.param)" using 32 bits by default.) param.atom.csize = sizeof($(param.$(type.param).ctype)) param.atom.ctype = $(if $(filter $(1),$(size.param)),$(2),$(error Invalid size of param $(name.param)! You can use $(subst $(eval) ,$(comma) , $(1)) bits.)) param.num.cons.fields = $(param.cons.fields) min max step param.uint.size = $(param.atom.size) param.uint.csize = $(param.atom.csize) param.uint.ctype = $(call param.atom.ctype,8 16 32 64,uint$(size.param)_t) param.uint.cons.fields = $(param.num.cons.fields) param.sint.size = $(param.atom.size) param.sint.csize = $(param.atom.csize) param.sint.ctype = $(call param.atom.ctype,8 16 32 64,int$(size.param)_t) param.sint.cons.fields = $(param.num.cons.fields) param.real.size = $(param.atom.size) param.real.csize = $(param.atom.csize) param.real.ctype = $(call param.atom.ctype,32 64,$(if $(filter 32,$(size.param)),float,double)) param.real.cons.fields = $(param.num.cons.fields) param.enum.size = $(param.uint.size) param.enum.csize = $(param.uint.csize) param.enum.ctype = $(param.uint.ctype) param.enum.cons.fields = $(param.cons.fields) max #param.enum.cons.extra.def = $(opts.param) param.enum.cons.extra = $(foreach o,$(opts.param),$(o).val) #param.enum.text.extra.def = $(foreach t,name $(call param-fields,text),$(opts.param)) param.enum.text.extra = $(foreach o,$(opts.param),$(foreach t,name $(call param-fields,text),$(o).$(t))) # препроцессинг опции перечисления define param.enum.pre-proc.opt $(1).name.param=$(1)# ifeq (,$($(1).val.param)) $(1).val.param=$(1)# endif $(foreach t,$(call param-fields,text),\ $(if $($(1).$(t).param),,\ $$(error No text field "$(t)" for option "$(1)" of enum "$(name.param)"))) endef # препроцессинг перечисления define param.enum.pre-proc ifneq (,$(opts.param)) ifeq (1,$(words $(opts.param))) $$(warning Enumeration "$(name.param)" has only one option!) endif max.param=$(words $(opts.param)) $(foreach o,$(opts.param),$(call param.enum.pre-proc.opt,$(o))) else $$(error Enumeration "$(name.param)" must have the "opts" field with available values!) endif endef param.comp.size = $(if $(def.param),$(1),$(error Because no default value is provided for "$(name.param)" the size must be set explicitly.)) param.comp.csize = $(if $(param.$(type.param).size),$(param.$(type.param).size),$(error No size is provided for param "$(name.param)"!)) param.comp.ptype = $(param.$(type.param).ctype) *const param.cstr.size = $(call param.comp.size,(sizeof($(call param-val,def))-1)) param.cstr.csize = ($(param.comp.csize)+1) param.cstr.ctype = char param.cstr.ptype = $(param.comp.ptype) param.cstr.val = "$($(1).param)" param.hbin.size = $(call param.comp.size,sizeof($(call param.hbin.val,def))) param.hbin.csize = $(param.comp.csize) param.hbin.ctype = uint8_t param.hbin.ptype = $(param.comp.ptype) param.hbin.def = static const uint8_t param_$(1)_val[] = {$(subst $(eval) $(eval),$(comma),$(patsubst %,0x%,$(strip $(subst $(comma), ,$(subst :, ,$($(1).param))))))}; param.hbin.val = param_$(1)_val param.mac.csize = sizeof($(param.mac.ctype)) param.mac.ctype = param_mac_t param.mac.val = {$(subst $(eval) ,$(comma),$(patsubst %,0x%,$(subst :, ,$($(1).param))))} param.ipv4.csize = sizeof($(param.ipv4.ctype)) param.ipv4.ctype = param_ipv4_t param.ipv4.val = {$(subst .,$(comma),$($(1).param))} Q?=@ # Форматирование строки исходника define param-out $(Q)echo$(if $(1), '$(1)',) $(if $(2),>,>>)-@- endef # форматирования комментария param-cmt=$(if $(param.$(1).cmt), /* $(param.$(1).cmt) */,) # форматирование значения param-val=$(if $(param.$(type.param).val),\ $(call param.$(type.param).val,$(1)),\ $($(1).param)) # размер типа в байтах param-csize=$(param.$(type.param).csize) # форматирование определения и значения привязки param-bind-def=$(call $(if $(param.$(1).def),param.$(1).def,param.bind.def),$(1)) param-bind-val=$(call $(if $(param.$(1).val),param.$(1).val,param.bind.val),$(1)) # получение списка всех полей описания параметра param-field-list=$(patsubst %.param,%,$(filter-out $(param.pre.vars) param.pre.vars,$(.VARIABLES))) # получение полей описания параметра по классу # например bind, cons и text param-fields=$(filter $(or $(param.$(type.param).$(1).fields),\ $(param.$(1).fields)),$(param-field-list)) # получение имён элементов полей по классу param-entries=$(strip $(call param-fields,$(1)) $(param.$(type.param).$(1).extra)) # форматирование исходника на C с описанием параметра define param-def define param/$(name.param) # определения привязок $(if $(call param-fields,bind),\ $(foreach f,$(call param-fields,bind),\ $(call param-out,$(call param.$(if $(param.$(f).def),$(f),bind).ext,$(f),$(type.param),$(name.param),);))\ $(call param-out)) # определения ограничений $(if $(and $(param.$(type.param).def),$(call param-fields,cons)),\ $(foreach f,$(call param-fields,cons),\ $(call param-out,$(call param.$(type.param).def,$(f))))\ $(call param-out)) # определение структуры с описанием $(call param-out,struct {$(call param-cmt,def)) # базовые поля $(call param-out, param_def_t base;$(call param-cmt,base)) # поля привязок $(if $(call param-fields,bind),\ $(call param-out, struct {$(call param-cmt,bind))\ $(foreach f,$(call param-fields,bind),\ $(call param-out, $(call param-bind-def,$(f));$(call param-cmt,$(f))))\ $(call param-out, } bind;)) # поля ограничений $(if $(call param-entries,cons),\ $(call param-out, const $(or $(param.$(type.param).ptype),$(param.$(type.param).ctype)) \ cons[$(words $(call param-entries,cons))];$(call param-cmt,cons))) # текстовые поля $(if $(call param-entries,text),\ $(call param-out, const char *const \ text[$(words $(call param-entries,text))];$(call param-cmt,text))) # наполнение структуры описания $(call param-out,} param_$(name.param)_def = {$(call param-cmt,dat)) # начало базовых полей $(call param-out, {$(call param-cmt,base)) # поле флагов $(call param-out, $(subst $(eval) $(eval),|,$(strip \ $(patsubst %,param_bind_%,$(call param-fields,bind)) \ $(patsubst %,param_cons_%,$(call param-fields,cons)) \ $(patsubst %,param_text_%,$(call param-fields,text)) \ $(patsubst %,param_%,$(filter $(param.flags),$(flag.param)))\ ))$(comma)$(call param-cmt,flag)) # поле размера $(call param-out, $(call param-csize)$(comma)$(call param-cmt,size)) # поле типа $(call param-out, ¶m_$(type.param)$(comma)$(call param-cmt,type)) # поле имени $(call param-out, "$(name.param)"$(call param-cmt,name)) # конец базовых полей $(call param-out, }$(comma)) # поля привязок (если есть) $(if $(call param-fields,bind),\ $(call param-out, {$(call param-cmt,bind))\ $(foreach f,$(call param-fields,bind),\ $(call param-out, $(call param-bind-val,$(f))$(comma)$(call param-cmt,$(f))))\ $(call param-out, }$(comma))) # поля ограничений (если есть) $(if $(call param-entries,cons),\ $(call param-out, {$(call param-cmt,cons))\ $(foreach f,$(call param-entries,cons),\ $(call param-out, $(if $(param.$(type.param).val),\ $(call param.$(type.param).val,$(f)),$($(f).param))$(comma)$(call param-cmt,$(f))))\ $(call param-out, }$(comma))) # текстовые поля (если есть) $(if $(call param-entries,text),\ $(call param-out, {$(call param-cmt,text))\ $(foreach f,$(call param-entries,text),\ $(call param-out, "$($(f).param)"$(comma)$(call param-cmt,$(f))))\ $(call param-out, })) # конец описания параметра $(call param-out,};) $(call param-out) endef endef define param-proc # подготовка списка полей параметра param.pre.vars := $$(.VARIABLES) # добавим поле имени параметра name.param=$(2) # вычислим объявление параметра $$(eval $$(param/$(2))) # проверим поле типа ifndef type.param $$(error No type is provided for "$$(name.param)"!) endif $$(if $$(filter $$(type.param),$$(param.types)),,\ $$(error Unsupported type "$$(type.param)" of "$$(name.param)"!)) # проверим поля привязок $$(if $$(filter getable,$$(flag.param)),\ $$(if $$(or $$(cell.param),$$(get.param)),,\ $$(error Parameter "$$(name.param)" getable but has no cell or getter provided!))) $$(if $$(filter setable,$$(flag.param)),\ $$(if $$(or $$(cell.param),$$(set.param)),,\ $$(error Parameter "$$(name.param)" setable but has no cell or setter provided!))) # проверим поле размера ifndef size.param ifdef param.$$(type.param).size size.param=$$(param.$$(type.param).size) endif endif # вызываем крюк препроцессинга $$(if $$(param.$$(type.param).pre-proc),\ $$(eval $$(param.$$(type.param).pre-proc))) # выводим список полей для отладки $$(if $$(param.proc.debug),$$(info Param "$(2)" fields: $$(param-field-list))) # создаём правила определения параметра на С $$(eval $$(call param-def,$(1))) # очищаем переменные полей параметра $$(call undef-vars,$$(filter-out $$(param.pre.vars),$$(.VARIABLES))) endef # правила генерирования заголовочного файла define param-h -@-: param-dsl.mk $(call param-out,#include "param.h",t) $(call param-out) $(foreach p,$(param.list),$(call param-out,extern const param_def_t param_$(p)_def;)) #$(call param-out) endef # правила генерирования файла исходника define param-c -@-: $(param.h) $(call param-out,#include "param.h",t) $(foreach h,$(header.param),\ $(call param-out,#include "$(h)")) $(call param-out) $(foreach p,$(param.list),$(param/$(p))) endef # обработка определений параметров в переменной $(1) define param-proc-all param.h := include/$(1).h # имя заголовочного файла param.c := src/$(1).c # имя файла исходника param.text := $$($(1)) # текстовое описание параметров # локализация ifneq (,$$(param.proc.lang)) param.text := $$(subst [$$(param.proc.lang)]=,=,$$(param.text)) endif # подготовка исходника param.text := $$(subst =,.param=,$$(subst define ,define param/,$$(param.text))) # получение списка параметров param.list := $$(patsubst param/%,%,$$(strip $$(filter param/%,$$(param.text)))) # вывод списка параметров для отладки $$(if $$(param.proc.debug),$$(info Found params: $$(param.list))) # генерирование заголовка с определениями $$(eval $$(subst -@-,$$(param.h),$$(param-h))) # вычисление определений параметров $$(eval $$(param.text)) # обработка определений параметров $$(foreach p,$$(param.list),$$(eval $$(call param-proc,$$(param.c),$$(p)))) # генерирование исходника с определениями параметров $$(eval $$(subst -@-,$$(param.c),$$(param-c))) # очистка временных переменных $$(call undef-vars,$$(patsubst %,param/%,$$(param.list)) param.h param.c param.text param.list) endef # упрощённый возов компилятора param-rules=$(eval $(call param-proc-all,$(1))) # правила для исходного файла на DSL define param-dsl-rules param.dsl.vars:=$$(.VARIABLES) param.dsl.vars # подключаем исходник include $(1) # получаем список переменных param.dsl.vars:=$$(filter-out $$(param.dsl.vars),$$(.VARIABLES)) # применяем правила для всех переменных $$(foreach g,$$(param.dsl.vars),$$(call param-rules,$$(g))) # очистка переменных $$(call undef-vars,$$(param.dsl.vars) param.dsl.vars) endef # упрощённый вызов компилятора для исходников param-dsl=$(foreach f,$(1),$(eval $(call param-dsl-rules,$(f))))
Теперь мы можем загрузить наш компилятор и сгенерировать исходник на C из DSL:
# подключаем модуль DSL include param-dsl.mk # вызываем компилятор для исходника params.lp2 $(call param-dsl,params.lp2) # добавим правило по умолчанию для проверки all: src/params.c
Вот так будут выглядеть сгенерированные нашим компилятором исходники (params.h):
uint8_t num_u8; char str[32]; param_set_f real_set; param_get_f real_get; enum { ctl_off, ctl_auto, ctl_man, }; param_ipv4_t ip_addr; param_mac_t mac_addr;
(params.c):
#include "param.h" #include "params.h" extern uint8_t num_u8; struct { /* default value */ param_def_t base; /* basic fields */ struct { /* binding fields */ uint8_t *cell; /* pointer to data cell */ } bind; const uint8_t cons[1]; /* constraint fields */ const char *const text[2]; /* textual fields */ } param_u8_num_def = { /* parameter data */ { /* basic fields */ param_bind_cell|param_cons_def|param_text_unit|param_text_info|param_getable, /* flags */ sizeof(uint8_t), /* size of value */ ¶m_uint, /* type */ "u8_num" /* name */ }, { /* binding fields */ &num_u8, /* pointer to data cell */ }, { /* constraint fields */ 11, /* default value */ }, { /* textual fields */ "шт.", /* measurement units */ "8-битовое беззнаковое целое только для чтения с единицами", /* short description */ } }; extern char str[((sizeof( "Значение по-умолчанию")-1)+1)]; struct { /* default value */ param_def_t base; /* basic fields */ struct { /* binding fields */ char *cell; /* pointer to data cell */ } bind; const char *const cons[1]; /* constraint fields */ const char *const text[1]; /* textual fields */ } param_some_str_def = { /* parameter data */ { /* basic fields */ param_bind_cell|param_cons_def|param_text_info|param_getable|param_setable, /* flags */ ((sizeof( "Значение по-умолчанию")-1)+1), /* size of value */ ¶m_cstr, /* type */ "some_str" /* name */ }, { /* binding fields */ &str, /* pointer to data cell */ }, { /* constraint fields */ "Значение по-умолчанию", /* default value */ }, { /* textual fields */ "Строка в Си стиле для чтения и записи", /* short description */ } }; extern param_set_f real_set; extern param_get_f real_get; struct { /* default value */ param_def_t base; /* basic fields */ struct { /* binding fields */ param_set_f *set; /* setter function */ param_get_f *get; /* getter function */ } bind; const float cons[4]; /* constraint fields */ const char *const text[1]; /* textual fields */ } param_real_num_def = { /* parameter data */ { /* basic fields */ param_bind_set|param_bind_get|param_cons_def|param_cons_max|param_cons_step|param_cons_min|param_text_info|param_getable|param_setable|param_persist, /* flags */ sizeof(float), /* size of value */ ¶m_real, /* type */ "real_num" /* name */ }, { /* binding fields */ real_set, /* setter function */ real_get, /* getter function */ }, { /* constraint fields */ 0.0, /* default value */ 1.0, /* maximum */ 0.1, /* step */ -1.0, /* minimum */ }, { /* textual fields */ "The positive floating point number with getter and setter", /* short description */ } }; struct { /* default value */ param_def_t base; /* basic fields */ const uint8_t cons[4]; /* constraint fields */ const char *const text[7]; /* textual fields */ } param_ctl_mode_def = { /* parameter data */ { /* basic fields */ param_cons_max|param_text_info, /* flags */ sizeof(uint8_t), /* size of value */ ¶m_enum, /* type */ "ctl_mode" /* name */ }, { /* constraint fields */ 3, /* maximum */ ctl_off, ctl_auto, ctl_man, }, { /* textual fields */ "Режим управления", /* short description */ "off", "Выключено", "auto", "Автоматически", "man", "Manual", } }; extern uint8_t aes_key[sizeof(param_def_val)]; static const uint8_t param_def_val[] = {0xE3,0xE5,0x3E,0xC1,0x96,0xE0,0x3F,0x28,0xE3,0x22,0x17,0x30,0xDD,0xE2,0xB4,0x3C}; struct { /* default value */ param_def_t base; /* basic fields */ struct { /* binding fields */ uint8_t *cell; /* pointer to data cell */ } bind; const uint8_t *const cons[1]; /* constraint fields */ } param_aes_key_def = { /* parameter data */ { /* basic fields */ param_bind_cell|param_cons_def|param_getable|param_setable|param_persist, /* flags */ sizeof(param_def_val), /* size of value */ ¶m_hbin, /* type */ "aes_key" /* name */ }, { /* binding fields */ &aes_key, /* pointer to data cell */ }, { /* constraint fields */ param_def_val, /* default value */ }, }; extern param_ipv4_t ip_addr; struct { /* default value */ param_def_t base; /* basic fields */ struct { /* binding fields */ param_ipv4_t *cell; /* pointer to data cell */ } bind; const param_ipv4_t cons[1]; /* constraint fields */ } param_ip_addr_def = { /* parameter data */ { /* basic fields */ param_bind_cell|param_cons_def|param_getable|param_setable|param_persist, /* flags */ sizeof(param_ipv4_t), /* size of value */ ¶m_ipv4, /* type */ "ip_addr" /* name */ }, { /* binding fields */ &ip_addr, /* pointer to data cell */ }, { /* constraint fields */ {192,168,1,1}, /* default value */ }, }; extern param_mac_t mac_addr; struct { /* default value */ param_def_t base; /* basic fields */ struct { /* binding fields */ param_mac_t *cell; /* pointer to data cell */ } bind; const param_mac_t cons[1]; /* constraint fields */ const char *const text[1]; /* textual fields */ } param_mac_addr_def = { /* parameter data */ { /* basic fields */ param_bind_cell|param_cons_def|param_text_info, /* flags */ sizeof(param_mac_t), /* size of value */ ¶m_mac, /* type */ "mac_addr" /* name */ }, { /* binding fields */ &mac_addr, /* pointer to data cell */ }, { /* constraint fields */ {0x12,0x34,0x56,0xAB,0xCD,0xEF}, /* default value */ }, { /* textual fields */ "ARP mac address of interface.", /* short description */ } };
На этом всё. Счастливого make хакинга.

Отправить комментарий