Tasmota/tools/lv_berry/convert.py

732 lines
24 KiB
Python

import re
import sys
lv_widgets_file = "lv_funcs.h"
lv_module_file = "lv_enum.h"
out_prefix = "../../tasmota/lvgl_berry/"
lvgl_prefix = "../../lib/libesp32/Berry/default/"
be_lv_defines = "be_lv_defines.h"
be_lv_c_mapping = "be_lv_c_mapping.h"
be_lv_widgets_libs = "be_lvgl_widgets_lib.c"
be_lv_lvgl_module = "be_lvgl_module.c"
# detect a function definition all
# Ex: 'void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent);'
# Group 1: 'void'
# Group 2: 'lv_obj_set_parent'
# Group 3: 'lv_obj_t * obj, lv_obj_t * parent'
parse_func_def = re.compile("(.*?)\s(\w+)\((.*?)\)")
# parse call argument type
# Ex: 'const lv_obj_t * parent' -> 'const ', 'lv_obj_t', ' * ', 'parent'
# Ex: 'bool auto_fit' -> '', 'bool', ' ', 'auto_fit'
parse_arg = re.compile("(\w+\s+)?(\w+)([\*\s]+)(\w+)")
return_types = {
"void": "",
"bool": "b",
"uint8_t": "i",
"uint16_t": "i",
"int16_t": "i",
"uint32_t": "i",
"int32_t": "i",
"void *": ".",
"const void *": ".",
"char *": "s",
"const char *": "s",
"lv_obj_user_data_t": "i",
"lv_objmask_mask_t *": ".",
"lv_coord_t": "i",
"lv_res_t": "i",
"lv_style_int_t": "i",
"lv_opa_t": "i",
"lv_drag_dir_t": "i",
"lv_bidi_dir_t": "i",
"lv_state_t": "i",
"lv_arc_type_t":"i",
"lv_chart_axis_t":"i",
"lv_bar_type_t":"i",
"lv_btn_state_t":"i",
"lv_point_t":"i",
"lv_chart_type_t":"i",
"lv_fit_t":"i",
"lv_cpicker_color_mode_t":"i",
"lv_dropdown_dir_t":"i",
"lv_layout_t":"i",
"lv_color_hsv_t":"i",
"lv_label_long_mode_t": "i",
"lv_label_align_t": "i",
"lv_scrollbar_mode_t": "i",
"lv_spinbox_part_t": "i",
"lv_spinner_style_t": "i",
"lv_anim_value_t": "i",
"lv_switch_part_t": "i",
"lv_label_align_t": "i",
"lv_tabview_btns_pos_t": "i",
"lv_textarea_style_t": "i",
"lv_slider_type_t": "i",
"lv_spinner_type_t": "i",
"lv_spinner_dir_t": "i",
"lv_blend_mode_t": "i",
"lv_grad_dir_t": "i",
"lv_border_side_t": "i",
"lv_align_t": "i",
"lv_keyboard_mode_t": "i",
"lv_indev_type_t": "i",
"lv_event_t": "i",
"lv_signal_t": "i",
"lv_img_cf_t": "i",
"lv_draw_mask_line_side_t": "i",
"lv_style_property_t": "i",
"lv_dir_t": "i",
"lv_part_t": "i",
"lv_base_dir_t": "i",
"lv_text_decor_t": "i",
"lv_text_align_t": "i",
"lv_arc_mode_t": "i",
"lv_bar_mode_t": "i",
"lv_event_code_t": "i",
"lv_obj_flag_t": "i",
"lv_slider_mode_t": "i",
"lv_scroll_snap_t": "i",
"lv_style_value_t": "i",
"lv_img_src_t": "i",
"lv_colorwheel_mode_t": "i",
"lv_scr_load_anim_t": "i",
"_lv_event_dsc_t *": "i",
# "lv_signal_cb_t": "c",
# "lv_design_cb_t": "c",
# "lv_event_cb_t": "c",
# "lv_group_focus_cb_t": "c",
# ctypes objects
"lv_area_t *": "lv_area",
"lv_meter_scale_t *": "lv_meter_scale",
"lv_meter_indicator_t *": "lv_meter_indicator",
"lv_obj_class_t *": "lv_obj_class",
"_lv_obj_t *": "lv_obj",
"lv_obj_t *": "lv_obj",
"lv_color_t": "lv_color",
"lv_style_t *": "lv_style",
"lv_group_t *": "lv_group",
"lv_font_t *": "lv_font",
#"lv_disp_t*": "lv_disp",
#"lv_style_list_t*": "",
# callbacks
"lv_group_focus_cb_t": "lv_group_focus_cb",
"lv_event_cb_t": "lv_event_cb",
# "lv_signal_cb_t": "lv_signal_cb", # removed in LVGL8
# "lv_design_cb_t": "lv_design_cb", # removed in LVGL8
# "lv_gauge_format_cb_t": "lv_gauge_format_cb", # removed in LVGL8
}
lv = {}
lvs = [] # special case for case for lv_style
lv0 = [] # function in lvlg module
lv_module = []
lv_cb_types = ['lv_group_focus_cb', 'lv_event_cb',
'lv_constructor_cb', # 'constructor_cb', addition to LVGL8, also works for 'destructor_cb'
]
# list of callback types that will need each a separate C callback
# For LVGL8, need to add synthetic lv_style, lv_font, lv_color
lv['style'] = []
lv['font'] = []
lv['color'] = []
# standard widgets
lv_widgets = ['arc', 'bar', 'btn', 'btnmatrix', 'canvas', 'checkbox',
'dropdown', 'img', 'label', 'line', 'roller', 'slider',
'switch', 'table', 'textarea' ]
# extra widgets
lv_widgets = lv_widgets + [ 'chart', 'colorwheel', 'imgbtn', 'led', 'meter', 'msgbox', 'spinbox' ]
lv_prefix = ['obj', 'group', 'style', 'indev', ] + lv_widgets
def try_int(s):
try:
v = int(s)
return v
except ValueError:
return None
# convert a C return type to the string representation for the Berry parser
# using return_types
def c_convert_ret_type(c_ret):
return return_types.get(c_ret, "")
# parse widgets files containing most function calls
with open(lv_widgets_file) as f:
for l_raw in f:
l_raw = re.sub('//.*$', '', l_raw) # remove trailing comments
l_raw = re.sub('LV_ATTRIBUTE_FAST_MEM ', '', l_raw) # remove LV_ATTRIBUTE_FAST_MEM marker
l_raw = re.sub('\s+', ' ', l_raw) # replace any multi-space with a single space
l_raw = l_raw.strip(" \t\n\r") # remove leading or trailing spaces
l_raw = re.sub('static ', '', l_raw)
l_raw = re.sub('inline ', '', l_raw)
l_raw = re.sub('const ', '', l_raw)
l_raw = re.sub('struct ', '', l_raw)
if (len(l_raw) == 0): continue
g = parse_func_def.search(l_raw)
if g:
# if match, we parse the line
# Ex: 'void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent);'
ret_type = g.group(1) # return type of the function
# Ex: ret_type -> 'void'
ret_type_without_t = re.sub(r"_t$", "", ret_type)
if ret_type in return_types:
# convert return type
c_ret = c_convert_ret_type(ret_type)
elif ret_type_without_t in lv_cb_types:
c_ret = "c" # general callback, if not already captured by explicit type
else:
print(f" // Skipping unsupported return type: {ret_type}")
continue
# convert arguments
c_args = ""
args_raw = [ x.strip(" \t\n\r") for x in g.group(3).split(",") ] # split by comma and strip
for arg_raw in args_raw:
# Ex: 'const lv_obj_t * parent' -> 'const ', 'lv_obj_t', ' * ', 'parent'
# Ex: 'bool auto_fit' -> '', 'bool', ' ', 'auto_fit'
ga = parse_arg.search(arg_raw)
if ga: # parsing ok?
ga_type = ga.group(2)
ga_ptr = ( ga.group(3).strip(" \t\n\r") == "*" ) # boolean
ga_name = ga.group(4)
ga_type_ptr = ga_type
if ga_ptr: ga_type_ptr += " *"
if ga_type_ptr in return_types:
ga_type = return_types[ga_type_ptr]
else:
# remove the trailing '_t' of type name if any
ga_type = re.sub(r"_t$", "", ga_type)
# if the type is a single letter, we just add it
if len(ga_type) == 1 and ga_type != 'c': # callbacks are different
c_args += ga_type
else:
if ga_type.endswith("_cb"):
# it's a callback type, we encode it differently
if ga_type not in lv_cb_types:
lv_cb_types.append(ga_type)
c_args += "^" + ga_type + "^"
else:
# we have a high-level type that we treat as a class name, enclose in parenthesis
c_args += "(" + "lv." + ga_type + ")"
# analyze function name and determine if it needs to be assigned to a specific class
func_name = g.group(2)
# Ex: func_name -> 'lv_obj_set_parent'
if func_name.startswith("_"): continue # skip low-level
if func_name.startswith("lv_debug_"): continue # skip debug
c_func_name = ""
if func_name == "lv_style_init":
continue # no need for init as it would collied with native init (and called behind the scene anyways)
found = False
for subtype in lv_prefix:
if func_name.startswith("lv_" + subtype + "_"):
be_name = re.sub("^lv_" + subtype + "_", '', func_name)
c_func_name = "lvbe_" + subtype + "_" + be_name
if subtype not in lv: lv[subtype] = [] # add entry
is_right_type = c_args.startswith(f"(lv.lv_{subtype})") # check if first arg matches class
is_obj_arg1 = c_args.startswith(f"(lv.lv_obj)") # or first arg is lv_obj
is_group_create = (subtype == 'group') and (func_name == 'lv_group_create')
if is_right_type or is_obj_arg1 or is_group_create:
lv[subtype].append( [ c_func_name, c_ret, c_args, func_name, be_name ] )
found = True
break
if found: continue
# not found, we treat it as lv top level function
be_name = re.sub("^lv_", '', func_name)
lv0.append( [ func_name, c_ret, c_args, func_name, be_name ] )
# print(lv0[-1])
print("| callback types"+str(lv_cb_types))
# parse module file containing numerical constants
with open(lv_module_file) as f:
for l_raw in f:
l_raw = l_raw.strip(" \t\n\r") # remove leading or trailing spaces
if l_raw.startswith("//"):
lv_module.append( [ None, l_raw ] ) # if key in None then add comment line
l_raw = re.sub('//.*$', '', l_raw) # remove trailing comments
l_raw = re.sub('\s+', '', l_raw) # remove all spaces
l_raw = re.sub(',.*$', '', l_raw) # remove comma and anything after it
if (len(l_raw) == 0): continue
k_v = l_raw.split("=")
if len(k_v) > 2:
print(f"Error: cannot match {l_raw}")
continue
# extract the key name
k = k_v[0]
if k.startswith("_"):
continue # skip any label starting with '_'
k = re.sub('^LV_', '', k) # remove remove any LV_ prefix
v = None
if len(k_v) == 2: # value is included
v = k_v[1]
if k is None or v is None: continue # we skip if key is void or value is void
if not k.isidentifier():
print(f"Error: {k} is not an identifier")
continue
lv_module.append( [k, v] ) # keep as string or None
sys.stdout = open(out_prefix + be_lv_c_mapping, 'w')
print("""
/********************************************************************
* Generated code, don't edit
*******************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
#include "be_ctypes.h"
""")
for subtype, flv in lv.items():
print(f"/* `lv_{subtype}` methods */")
if subtype in lv_widgets:
print(f"#ifdef BE_LV_WIDGET_{subtype.upper()}")
print(f"const lvbe_call_c_t lv_{subtype}_func[] = {{")
func_out = {} # used to sort output
for f in flv:
c_func_name = f[0]
c_ret_type = f[1]
c_argc = f[2]
if c_argc is not None: c_argc = "\"" + c_argc + "\""
orig_func_name = f[3]
be_name = f[4]
# if c_ret_type is an object, prefix with `lv.`
if len(c_ret_type) > 1: c_ret_type = "lv." + c_ret_type
if c_func_name.endswith("_create"):
pass
# c_ret_type = f"+lv_{subtype}"
else:
func_out[be_name] = f" {{ \"{be_name}\", (void*) &{orig_func_name}, \"{c_ret_type}\", { c_argc if c_argc else 'nullptr'} }},"
for be_name in sorted(func_out):
print(func_out[be_name])
print(f"}};")
if subtype in lv_widgets:
print(f"#endif // BE_LV_WIDGET_{subtype.upper()}")
print(f"")
# extern classes
for subtype in sorted(lv):
print(f"extern const bclass be_class_lv_{subtype};");
print()
# print the global map of classes
print(f"""
// map of clases
const lvbe_call_c_classes_t lv_classes[] = {{""")
for subtype in sorted(lv):
# for subtype, flv in lv.items():
if subtype in lv_widgets:
print(f"#ifdef BE_LV_WIDGET_{subtype.upper()}")
print(f" {{ \"lv_{subtype}\", &be_class_lv_{subtype}, lv_{subtype}_func, sizeof(lv_{subtype}_func) / sizeof(lv_{subtype}_func[0]) }},")
if subtype in lv_widgets:
print(f"#endif // BE_LV_WIDGET_{subtype.upper()}")
print(f"""}};
const size_t lv_classes_size = sizeof(lv_classes) / sizeof(lv_classes[0]);
""")
# previous generation calls
# keep only create
for subtype, flv in lv.items():
print(f" /* `lv_{subtype}` methods */")
for f in flv:
c_func_name = f[0]
c_ret_type = f[1]
c_argc = f[2]
if c_argc is not None: c_argc = "\"" + c_argc + "\""
orig_func_name = f[3]
# if c_ret_type is an object, prefix with `lv.`
if len(c_ret_type) > 1: c_ret_type = "lv." + c_ret_type
if c_func_name.endswith("_create"):
c_ret_type = f"+lv.lv_{subtype}"
if subtype in lv_widgets:
print(f"#ifdef BE_LV_WIDGET_{subtype.upper()}")
print(f" int be_ntv_lv_{subtype}_init(bvm *vm) {{ return be_call_c_func(vm, (void*) &{orig_func_name}, \"{c_ret_type}\", { c_argc if c_argc else 'nullptr'}); }}")
print(f"#endif // BE_LV_WIDGET_{subtype.upper()}")
else:
print(f" int be_ntv_lv_{subtype}_init(bvm *vm) {{ return be_call_c_func(vm, (void*) &{orig_func_name}, \"{c_ret_type}\", { c_argc if c_argc else 'nullptr'}); }}")
print("""
// create font either empty or from parameter on stack
int lvbe_font_create(bvm *vm) { return be_call_c_func(vm, NULL, "+lv_group", ""); }
""")
print()
print("#ifdef __cplusplus")
print("} /* extern \"C\" */")
print("#endif")
print("/********************************************************************/")
sys.stdout.close()
sys.stdout = open(lvgl_prefix + be_lv_widgets_libs, 'w')
print("""
/********************************************************************
* Generated code, don't edit
*******************************************************************/
/********************************************************************
* Tasmota LVGL classes for widgets
*******************************************************************/
#include "be_constobj.h"
#ifdef USE_LVGL
#include "lvgl.h"
extern int lv0_init(bvm *vm);
extern int lco_init(bvm *vm); // generic function
extern int lco_tostring(bvm *vm); // generic function
extern int lco_toint(bvm *vm); // generic function
extern int lvx_init_ctor(bvm *vm, void * func);
extern int lvx_member(bvm *vm);
extern int lvx_tostring(bvm *vm); // generic function
extern int lvs_init(bvm *vm);
extern int lvs_tostring(bvm *vm);
BE_EXPORT_VARIABLE extern const bclass be_class_lv_obj;
extern int lvbe_font_create(bvm *vm);
""")
# expose all extern definitions:
# Ex:
#
# /* `lv_canvas` external functions definitions */
# extern int lvbe_canvas_create(bvm *vm);
# extern int lvbe_canvas_set_buffer(bvm *vm);
# ...
#
for subtype, flv in lv.items():
print(f"/* `lv_{subtype}` external functions definitions */")
for f in flv:
c_func_name = f[0]
print(f"extern int {c_func_name}(bvm *vm);")
print()
for subtype, flv in lv.items():
print(f"""extern int be_ntv_lv_{subtype}_init(bvm *vm);""")
print()
# Define specific classes for lv_obj
#
print("""
/********************************************************************
** Solidified class: lv_style
********************************************************************/
be_local_class(lv_style,
1,
NULL,
be_nested_map(4,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_nested_key("init", 380752755, 4, -1), be_const_func(lvs_init) },
{ be_nested_key("tostring", -1995258651, 8, -1), be_const_func(lvs_tostring) },
{ be_nested_key("_p", 1594591802, 2, -1), be_const_var(0) },
{ be_nested_key("member", 719708611, 6, 0), be_const_func(lvx_member) },
})),
(be_nested_const_str("lv_style", -143355747, 8))
);
/*******************************************************************/
/********************************************************************
** Solidified class: lv_obj
********************************************************************/
be_local_class(lv_obj,
1,
NULL,
be_nested_map(5,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_nested_key("tostring", -1995258651, 8, 3), be_const_func(lvx_tostring) },
{ be_nested_key("member", 719708611, 6, -1), be_const_func(lvx_member) },
{ be_nested_key("_p", 1594591802, 2, -1), be_const_var(0) },
{ be_nested_key("init", 380752755, 4, 4), be_const_func(be_ntv_lv_obj_init) },
{ be_nested_key("_class", -1562820946, 6, -1), be_const_comptr(&lv_obj_class) },
})),
(be_nested_const_str("lv_obj", -37134147, 6))
);
/*******************************************************************/
/********************************************************************
** Solidified class: lv_group
********************************************************************/
be_local_class(lv_group,
1,
NULL,
be_nested_map(4,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_nested_key("init", 380752755, 4, -1), be_const_func(be_ntv_lv_group_init) },
{ be_nested_key("tostring", -1995258651, 8, -1), be_const_func(lvx_tostring) },
{ be_nested_key("_p", 1594591802, 2, -1), be_const_var(0) },
{ be_nested_key("member", 719708611, 6, 0), be_const_func(lvx_member) },
})),
(be_nested_const_str("lv_group", -442928277, 8))
);
/*******************************************************************/
/********************************************************************
** Solidified class: lv_indev
********************************************************************/
be_local_class(lv_indev,
1,
NULL,
be_nested_map(4,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_nested_key("init", 380752755, 4, -1), be_const_func(lv0_init) },
{ be_nested_key("tostring", -1995258651, 8, -1), be_const_func(lvx_tostring) },
{ be_nested_key("_p", 1594591802, 2, -1), be_const_var(0) },
{ be_nested_key("member", 719708611, 6, 0), be_const_func(lvx_member) },
})),
(be_nested_const_str("lv_indev", 225602374, 8))
);
/*******************************************************************/
/********************************************************************
** Solidified class: lv_font
********************************************************************/
be_local_class(lv_font,
1,
NULL,
be_nested_map(3,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_nested_key("init", 380752755, 4, -1), be_const_func(lvbe_font_create) },
{ be_nested_key("tostring", -1995258651, 8, -1), be_const_func(lvx_tostring) },
{ be_nested_key("_p", 1594591802, 2, -1), be_const_var(0) },
})),
(be_nested_const_str("lv_font", 1550958453, 7))
);
/*******************************************************************/
/********************************************************************
** Solidified class: lv_color
********************************************************************/
be_local_class(lv_color,
1,
NULL,
be_nested_map(4,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_nested_key("toint", -681784387, 5, -1), be_const_func(lco_toint) },
{ be_nested_key("tostring", -1995258651, 8, 0), be_const_func(lco_tostring) },
{ be_nested_key("_p", 1594591802, 2, -1), be_const_var(0) },
{ be_nested_key("init", 380752755, 4, -1), be_const_func(lco_init) },
})),
(be_nested_const_str("lv_color", 1419148319, 8))
);
/*******************************************************************/
""")
for subtype, flv in lv.items():
# special version for widgets
if subtype in lv_widgets:
print(f"""/********************************************************************
** Solidified class: lv_{subtype}
********************************************************************/
extern const bclass be_class_lv_obj;
be_local_class(lv_{subtype},
0,
&be_class_lv_obj,
be_nested_map(2,
( (struct bmapnode*) &(const bmapnode[]) {{
{{ be_nested_key("_class", -1562820946, 6, -1), be_const_comptr(&lv_{subtype}_class) }},
{{ be_nested_key("init", 380752755, 4, -1), be_const_func(be_ntv_lv_{subtype}_init) }},
}})),
(be_nested_const_str("lv_{subtype}", 1612829968, 6))
);
/*******************************************************************/
""")
# class definitions
print(f"""void be_load_lv_{subtype}_class(bvm *vm) {{
be_pushntvclass(vm, &be_class_lv_{subtype});
be_setglobal(vm, \"lv_{subtype}\");
be_pop(vm, 1);
}}
""")
print("""
#endif // USE_LVGL
""")
sys.stdout.close()
sys.stdout = open(lvgl_prefix + be_lv_lvgl_module, 'w')
print("/********************************************************************")
print(" * Generated code, don't edit")
print(" *******************************************************************/")
print("""/********************************************************************
* LVGL Module
*******************************************************************/
#include "be_constobj.h"
#ifdef USE_LVGL
#include "lvgl.h"
#include "be_lvgl.h"
extern int lv0_member(bvm *vm); // resolve virtual members
extern int lv0_start(bvm *vm);
extern int lv0_register_button_encoder(bvm *vm); // add buttons with encoder logic
extern int lv0_load_montserrat_font(bvm *vm);
extern int lv0_load_seg7_font(bvm *vm);
extern int lv0_load_font(bvm *vm);
extern int lv0_load_freetype_font(bvm *vm);
extern int lv0_screenshot(bvm *vm);
static int lv_get_hor_res(void) {
return lv_disp_get_hor_res(lv_disp_get_default());
}
static int lv_get_ver_res(void) {
return lv_disp_get_ver_res(lv_disp_get_default());
}
/* `lv` methods */
const lvbe_call_c_t lv_func[] = {
""")
func_out = {} # used to sort output
for f in lv0:
c_func_name = f[0]
c_ret_type = f[1]
c_argc = f[2]
if c_argc is not None: c_argc = "\"" + c_argc + "\""
orig_func_name = f[3]
be_name = f[4]
# if c_ret_type is an object, prefix with `lv.`
if len(c_ret_type) > 1: c_ret_type = "lv." + c_ret_type
func_out[be_name] = f" {{ \"{be_name}\", (void*) &{orig_func_name}, \"{c_ret_type}\", { c_argc if c_argc else 'nullptr'} }},"
for be_name in sorted(func_out):
print(func_out[be_name])
# // resolution
# { "get_hor_res", (void*) &lv_get_hor_res, "i", "" },
# { "get_ver_res", (void*) &lv_get_ver_res, "i", "" },
# // layers
# { "layer_sys", (void*) &lv_layer_sys, "lv_obj", "" },
# { "layer_top", (void*) &lv_layer_top, "lv_obj", "" },
# // screens
# { "scr_act", (void*) &lv_scr_act, "lv_obj", "" },
# { "scr_load", (void*) &lv_scr_load, "", "(lv_obj)" },
# { "scr_load_anim", (void*) &lv_scr_load_anim, "", "(lv_obj)iiib" },
print("""
};
const size_t lv_func_size = sizeof(lv_func) / sizeof(lv_func[0]);
""")
print("""
typedef struct be_constint_t {
const char * name;
int32_t value;
} be_constint_t;
const be_constint_t lv0_constants[] = {
""")
lv_module2 = {}
for k_v in lv_module:
(k,v) = k_v
if k is not None:
lv_module2[k] = v
# print the enums, symbols and functions
# Ex: { "ALIGN_BOTTOM_LEFT", LV_ALIGN_BOTTOM_LEFT },
#
# Encoding:
# 1. `COLOR_WHITE=0xFFFFFF` enum with explicit value
# 2. `LV_EVENT_ALL` enum with implicit value with same name resolved by C compiler
# 3. `$SYMBOL_OK="\xef\x80\x8c"` if starts with `$` then it's a string
# 4. `&seg7_font=lv0_load_seg7_font` if starts with `&` then it's a native function
#
# We need to sort ignoring the first char if it's not a letter
for k in sorted(lv_module2):
v = lv_module2[k]
# check the type of the value
# if first char is '"' then it's a string and prefix with '$'
# if first char is '&' then it's a function and prefix with '&'
# if no value, then it's an enum, prefix with `LV_`
# otherwise it's an int, leave if unchanged
if v is not None:
v_prefix = ""
if v[0] == '"': v_prefix = "$"
if v[0] == '&': v_prefix = "&"
print(f" {{ \"{v_prefix}{k}\", (int32_t) {v} }},")
else:
print(f" {{ \"{k}\", LV_{k} }},")
print("""
};
const size_t lv0_constants_size = sizeof(lv0_constants)/sizeof(lv0_constants[0]);
/* generated */
be_local_module(lv,
"lv",
be_nested_map(2,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_nested_key("member", 719708611, 6, -1), be_const_func(lv0_member) },
{ be_nested_key("start", 1697318111, 5, 0), be_const_func(lv0_start) },
}))
);
BE_EXPORT_VARIABLE be_define_const_native_module(lv, NULL);
#endif // USE_LVGL
""")
print("/********************************************************************/")
sys.stdout.close()