Tasmota/tools/lv_berry/preprocessor.py

231 lines
7.6 KiB
Python

import re
import sys
import glob
# https://stackoverflow.com/a/241506
def comment_remover(text):
def replacer(match):
s = match.group(0)
if s.startswith('/'):
return " " # note: a space and not an empty string
else:
return s
pattern = re.compile(
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
re.DOTALL | re.MULTILINE
)
return re.sub(pattern, replacer, text)
# compute a sorted list of files from a prefix and a list of glob patterns
def list_files(prefix, glob_list):
r = []
for g in glob_list:
r += glob.glob(prefix + g, recursive=True)
return sorted(r)
def clean_source(raw):
raw = comment_remover(raw) # remove comments
# convert cr/lf or cr to lf
raw = re.sub('\r\n ', '\n', raw)
raw = re.sub('\r', '\n', raw)
# group multilines into a single line, i.e. if line ends with '\', put in a single line
raw = re.sub('\\\\\n', ' ', raw)
# remove preprocessor directives
raw = re.sub('\n[ \t]*#[^\n]*(?=\n)', '', raw)
raw = re.sub('^[ \t]*#[^\n]*\n', '', raw)
raw = re.sub('\n[ \t]*#[^\n]*$', '', raw)
# remove extern "C" {}
raw = re.sub('extern\s+"C"\s+{(.*)}', '\\1', raw, flags=re.DOTALL)
# remove empty lines
raw = re.sub('\n[ \t]*(?=\n)', '', raw)
raw = re.sub('^[ \t]*\n', '', raw) # remove first empty line
raw = re.sub('\n[ \t]*$', '', raw) # remove last empty line
return raw
# ################################################################################
# Parse function signatures
# ################################################################################
lv_src_prefix = "../../lib/libesp32_lvgl/LVGL8/src/"
lv_fun_globs = [
"widgets/*.h", # all widgets
# "extra/widgets/*/*.h",
"extra/widgets/chart/*.h",
"extra/widgets/colorwheel/*.h",
"extra/widgets/imgbtn/*.h",
"extra/widgets/led/*.h",
"extra/widgets/meter/*.h",
"extra/widgets/msgbox/*.h",
"extra/widgets/spinbox/*.h",
"core/*.h",
"draw/*.h",
"misc/lv_style_gen.h",
#"misc/lv_area.h",
#"**/*.h",
]
headers_names = list_files(lv_src_prefix, lv_fun_globs)
# headers_names += ["lv_pre_style.h"] # for LVGL v7, add pre-generated style functions from C preprocessor
# unit test
# headers_names = [ '../../lib/libesp32_lvgl/LVGL/src/lv_widgets/lv_btn.h' ]
# headers_names = [ '../../lib/libesp32_lvgl/LVGL/src/lv_api_map.h' ]
output_filename = "lv_funcs.h"
sys.stdout = open(output_filename, 'w')
print("""
// Automatically generated from LVGL source with `python3 preprocessor.py`
// Extract function signatures from LVGL APIs in headers
// Custom Tasmota functions
void lv_img_set_tasmota_logo(lv_obj_t * img);
// ======================================================================
// Artificial
// ======================================================================
lv_color_t lv_color_mix(lv_color_t c1, lv_color_t c2, uint8_t mix);
// ======================================================================
// LV top level functions
// ======================================================================
// resolution
lv_coord_t lv_get_hor_res(void);
lv_coord_t lv_get_ver_res(void);
// layers
//static inline lv_obj_t * lv_layer_sys(void);
//static inline lv_obj_t * lv_layer_top(void);
// ======================================================================
// Generated from headers
// ======================================================================
""")
for header_name in headers_names:
with open(header_name) as f:
print("// " + header_name)
raw = clean_source(f.read())
# remove anything in '{' '}'
while True:
(raw, repl) = re.subn('\{[^{]*?\}', ';', raw, flags=re.DOTALL) # replace with ';' to make pattern matching still work
if (repl == 0): break # no more replace, stop
raw_f = re.findall('(^|;|})\s*([^;{}]+\(.*?\))\s*(?=(;|{))', raw, flags=re.DOTALL)
fun_defs = [ x[1] for x in raw_f]
# remove any CRLF or multi-space
fun_defs = [ re.sub('[ \t\r\n]+', ' ', x) for x in fun_defs]
# parse individual
for fun in fun_defs:
# remove LV_ATTRIBUTE_FAST_MEM
fun = re.sub('LV_ATTRIBUTE_FAST_MEM ', '', fun)
exclude = False
for exclude_prefix in ["typedef", "_LV_", "LV_"]:
if fun.startswith(exclude_prefix): exclude = True
if exclude: continue
# extrac the function name
fun_name = re.search('\s(\w+)\([^\(]*$', fun)
if fun_name != None:
fun_name = fun_name.group(1) # we now have the function name
# exclude specific names
for exclude_pattern in [
"^_", # skip if function name starts with '_'
"^lv_debug", # all debug functions
"^lv_init", "^lv_deinit",
"^lv_templ_",
"^lv_imgbtn_get_src_", # LV_IMGBTN_TILED == 0
"^lv_imgbtn_set_src_tiled",# !LV_IMGBTN_TILED
"^lv_disp_",
"^lv_refr_get_fps_", # no LV_USE_PERF_MONITOR
"^lv_img_cache_",
"^lv_img_decoder_",
"^lv_img_cf_",
"^lv_img_buf_",
"^lv_indev_scroll_",
"^lv_keyboard_def_event_cb", # need to fix conditional include
"^lv_event_get_", # event_getters not needed
]:
if re.search(exclude_pattern, fun_name): exclude = True
if exclude: continue
print(fun)
print()
sys.stdout.close()
# ################################################################################
# Parse 'enum'
# ################################################################################
lv_src_prefix = "../../lib/libesp32_lvgl/LVGL8/src/"
lv_fun_globs = [ "**/*.h" ]
headers_names = list_files(lv_src_prefix, lv_fun_globs)
output_filename = "lv_enum.h"
sys.stdout = open(output_filename, 'w')
print("""
// LV Colors - we store in 24 bits format and will convert at runtime
// This is specific treatment because we keep colors in 24 bits format
COLOR_WHITE=0xFFFFFF
COLOR_SILVER=0xC0C0C0
COLOR_GRAY=0x808080
COLOR_BLACK=0x000000
COLOR_RED=0xFF0000
COLOR_MAROON=0x800000
COLOR_YELLOW=0xFFFF00
COLOR_OLIVE=0x808000
COLOR_LIME=0x00FF00
COLOR_GREEN=0x008000
COLOR_CYAN=0x00FFFF
COLOR_AQUA=0x00FFFF
COLOR_TEAL=0x008080
COLOR_BLUE=0x0000FF
COLOR_NAVY=0x000080
COLOR_MAGENTA=0xFF00FF
COLOR_PURPLE=0x800080
// following are #define, not enum
LV_RADIUS_CIRCLE
LV_TEXTAREA_CURSOR_LAST
LV_STYLE_PROP_ANY
LV_SIZE_CONTENT
""")
for header_name in headers_names:
with open(header_name) as f:
raw = clean_source(f.read())
# extract enums
enums = re.findall('enum\s+{(.*?)}', raw, flags=re.DOTALL)
for enum in enums: # iterate on all matches
# remove enums defined via a macro
enum = re.sub('\S+\((.*?),.*?\),', '\\1,', enum) # turn 'LV_STYLE_PROP_INIT(LV_STYLE_SIZE, 0x0, LV_STYLE_ID_VALUE + 3, LV_STYLE_ATTR_NONE),' into 'LV_STYLE_SIZE'
#
enum_elt = enum.split(",")
for enum_item in enum_elt:
# remove any space
enum_item = re.sub('[ \t\n]', '', enum_item)
# remove anything after '='
enum_item = re.sub('=.*$', '', enum_item)
# item is ready
exclude = False
for exclude_prefix in ["_", "LV_BIDI_DIR_", "LV_FONT_", "LV_IMG_CF_RESERVED_", "LV_IMG_CF_USER_",
"LV_SIGNAL_", "LV_TEMPL_", "LV_TASK_PRIO_", "LV_THEME_", "LV_KEYBOARD_"]:
if enum_item.startswith(exclude_prefix): exclude = True
if exclude: continue
print(enum_item)
sys.stdout.close()