322 lines
9.2 KiB
Python
322 lines
9.2 KiB
Python
# XF Template Generator
|
|
__author__ = "Louis Heredero"
|
|
__copyright__ = "Copyright 2023, Louis Heredero"
|
|
__license__ = "GPL3.0"
|
|
__version__ = "1.2.0"
|
|
|
|
import json
|
|
import locale
|
|
import os
|
|
import re
|
|
import time
|
|
|
|
def ucfirst(s):
|
|
return s[0].upper() + s[1:]
|
|
|
|
def get_input(title, default=None, non_null=False):
|
|
"""Prompts user for an input
|
|
|
|
Args:
|
|
title (str): Name of value
|
|
default (Any, optional): Default value. Defaults to None.
|
|
non_null (bool, optional): Whether the can be null. Defaults to False.
|
|
|
|
Returns:
|
|
str|None: The value
|
|
"""
|
|
|
|
while True:
|
|
txt = title
|
|
if default is not None:
|
|
txt += f" [{default}]"
|
|
txt += ": "
|
|
value = input(txt).strip()
|
|
if value:
|
|
return value
|
|
|
|
elif default:
|
|
return default
|
|
|
|
elif not non_null:
|
|
return ""
|
|
|
|
def fill_template(dir_, variables, filename):
|
|
"""Replaces values in a template file
|
|
|
|
Args:
|
|
dir_ (str): Path of src directory
|
|
variables (dict): Dictionary of values to replace
|
|
filename (str): Template file name
|
|
|
|
Returns:
|
|
str: Template content with replaced values
|
|
"""
|
|
|
|
with open(os.path.join(dir_, "templates", filename), "r") as f:
|
|
content = f.read()
|
|
|
|
def replace(m):
|
|
indent = m.group(1)
|
|
key = m.group(2)
|
|
txt = variables[key]
|
|
|
|
if indent:
|
|
lines = txt.split("\n")
|
|
lines = [indent+line for line in lines]
|
|
txt = "\n".join(lines)
|
|
|
|
return txt
|
|
|
|
return re.sub("([ \t]*)\$\{(.+?)\}", replace, content)
|
|
|
|
def output_file(dir_, filename, content):
|
|
"""Writes content to a given file. Checks before overwriting
|
|
|
|
Args:
|
|
dir_ (str): Path of src directory
|
|
filename (str): Output file name
|
|
content (str): Content to write
|
|
"""
|
|
|
|
outdir = os.path.join(dir_, "out")
|
|
if not os.path.isdir(outdir):
|
|
os.mkdir(outdir)
|
|
|
|
outpath = os.path.join(outdir, filename)
|
|
if os.path.exists(outpath):
|
|
replace = input(f"The file {filename} already exists. Overwrite it ? y/N: ")
|
|
if replace.lower() != "y":
|
|
return
|
|
|
|
with open(outpath, "w") as f:
|
|
f.write(content)
|
|
|
|
def main():
|
|
locale.setlocale(locale.LC_TIME, "en_GB.utf8")
|
|
dir_ = os.path.dirname(__file__)
|
|
|
|
# Load config if it exists
|
|
conf_path = os.path.join(dir_, "config.json")
|
|
if os.path.exists(conf_path):
|
|
with open(conf_path, "r") as f:
|
|
variables = json.load(f)
|
|
|
|
else:
|
|
variables = {}
|
|
|
|
variables["date"] = time.strftime("%B %Y")
|
|
|
|
variables["author"] = get_input("Author", variables.get("author", ""), True)
|
|
variables["filename"] = get_input("Filename", None, True).upper()
|
|
variables["filename_lc"] = variables["filename"].lower()
|
|
variables["fn"] = get_input("Filename (short)", None, True).upper()
|
|
|
|
fn_uc = variables["filename"]
|
|
|
|
##########
|
|
# States #
|
|
##########
|
|
|
|
states = []
|
|
|
|
print("States (leave empty to end):")
|
|
print("> INIT")
|
|
while True:
|
|
state = input("> ").upper()
|
|
if state:
|
|
states.append(state)
|
|
else:
|
|
break
|
|
|
|
prefix = "ST"+variables["fn"]+"_"
|
|
variables["STATES_ENUM"] = ",\n".join([prefix+s for s in states])
|
|
|
|
states_cases = []
|
|
states_cases_entry = []
|
|
states_cbs_struct = []
|
|
states_cbs_init = []
|
|
|
|
for state in states:
|
|
case_ = "case {STATE}:\n"
|
|
case_ += " break;"
|
|
case_ = case_.replace("{STATE}", prefix+state)
|
|
states_cases.append(case_)
|
|
|
|
case_ = "case {STATE}:\n"
|
|
case_ += " if (me->{state}.f != NULL) {\n"
|
|
case_ += " me->{state}.f(me->{state}.p);\n"
|
|
case_ += " }\n"
|
|
case_ += " break;"
|
|
case_ = case_.replace("{state}", state.lower()).replace("{STATE}", prefix+state)
|
|
states_cases_entry.append(case_)
|
|
|
|
states_cbs_struct.append(f"{fn_uc}_CALLBACK {state.lower()};")
|
|
states_cbs_init.append(f"me->{state.lower()}.f = NULL;")
|
|
|
|
variables["STATES_CASES"] = "\n\n".join(states_cases)
|
|
variables["STATES_CASES_ENTRY"] = "\n\n".join(states_cases_entry)
|
|
variables["STATES_CBS_STRUCT"] = "\n".join(states_cbs_struct)
|
|
variables["STATES_CBS_INIT"] = "\n".join(states_cbs_init)
|
|
|
|
#############
|
|
# Callbacks #
|
|
#############
|
|
|
|
callbacks_dec = []
|
|
|
|
callback_dec = ""
|
|
callback_dec += "/**\n"
|
|
callback_dec += " * Set the callback function to call when the {filename} is entering state {state}\n"
|
|
callback_dec += " * @param me the {filename} itself\n"
|
|
callback_dec += " * @param f the function to call\n"
|
|
callback_dec += " * @param p the param(s) to pass to the function\n"
|
|
callback_dec += " */\n"
|
|
callback_dec += "void {filename}_on{State}({filename}* me, {filename}_CALLBACK_FUNCTION f, void* p);"
|
|
callback_dec = callback_dec.replace("{filename}", fn_uc)
|
|
|
|
for state in states:
|
|
callbacks_dec.append(
|
|
callback_dec.replace("{state}", state.lower())
|
|
.replace("{State}", state.capitalize())
|
|
)
|
|
|
|
variables["CALLBACKS_DEC"] = "\n\n".join(callbacks_dec)
|
|
|
|
callbacks_def = []
|
|
|
|
callback_def = ""
|
|
callback_def += "void {filename}_on{State}({filename}* me, {filename}_CALLBACK_FUNCTION f, void* p) {\n"
|
|
callback_def += " me->{state}.f = f;\n"
|
|
callback_def += " me->{state}.p = p;\n"
|
|
callback_def += "}"
|
|
callback_def = callback_def.replace("{filename}", fn_uc)
|
|
|
|
for state in states:
|
|
callbacks_def.append(
|
|
callback_def.replace("{state}", state.lower())
|
|
.replace("{State}", state.capitalize())
|
|
)
|
|
|
|
variables["CALLBACKS_DEF"] = "\n\n".join(callbacks_def)
|
|
|
|
##########
|
|
# Events #
|
|
##########
|
|
|
|
events = ["init = 100"]
|
|
|
|
print("Events (leave empty to end):")
|
|
print("> init")
|
|
while True:
|
|
event = input("> ").lower()
|
|
if event:
|
|
events.append(event)
|
|
|
|
else:
|
|
break
|
|
|
|
events_enum = ["ev"+variables["fn"]+e for e in events]
|
|
events_enum = ",\n".join(events_enum).split("\n")
|
|
events_enum[0] += " // TODO change this number (< 256)"
|
|
variables["EVENTS_ENUM"] = "\n".join(events_enum)
|
|
|
|
events_emits_def = []
|
|
|
|
emit_def = ""
|
|
emit_def += "void {filename}_emit{Event}({filename}* me, uint16_t t) {\n"
|
|
emit_def += " POST(me, &{filename}_processEvent, ev{fn}{event}, t, 0);\n"
|
|
emit_def += "}"
|
|
emit_def = emit_def.replace("{filename}", fn_uc).replace("{fn}", variables["fn"])
|
|
|
|
for event in events[1:]:
|
|
events_emits_def.append(
|
|
emit_def.replace("{event}", event).replace("{Event}", event.capitalize())
|
|
)
|
|
|
|
variables["EVENTS_EMITS_DEF"] = "\n\n".join(events_emits_def)
|
|
|
|
events_emits_dec = []
|
|
emit_dec = ""
|
|
emit_dec += "/**\n"
|
|
emit_dec += " * Emit the {event} event\n"
|
|
emit_dec += " * @param me the {filename} itself\n"
|
|
emit_dec += " * @param t time to wait in ms before triggering event\n"
|
|
emit_dec += " */"
|
|
emit_dec += "void {filename}_emit{Event}({filename}* me, uint16_t t);"
|
|
emit_dec = emit_dec.replace("{filename}", fn_uc)
|
|
|
|
for event in events[1:]:
|
|
events_emits_dec.append(
|
|
emit_dec.replace("{event}", event).replace("{Event}", event.capitalize())
|
|
)
|
|
|
|
variables["EVENTS_EMITS_DEC"] = "\n\n".join(events_emits_dec)
|
|
|
|
#############
|
|
# Variables #
|
|
#############
|
|
|
|
vars_init = []
|
|
vars_struct = []
|
|
vars_setters_def = []
|
|
vars_setters_dec = []
|
|
|
|
setter_def = ""
|
|
setter_def += "void {filename}_set{Name}({filename}* me, {type} v) {\n"
|
|
setter_def += " me->{name} = v;\n"
|
|
setter_def += "}"
|
|
setter_def = setter_def.replace("{filename}", fn_uc)
|
|
|
|
setter_dec = "void {filename}_set{Name}({filename}* me, {type} v);"
|
|
setter_dec = setter_dec.replace("{filename}", fn_uc)
|
|
|
|
print("Variables:")
|
|
print("(leave name empty to end)")
|
|
while True:
|
|
name = input("Name: ").strip()
|
|
if not name: break
|
|
|
|
type_ = input("Type: ").strip()
|
|
default = input("Default value: ").strip()
|
|
print()
|
|
|
|
vars_struct.append(f"{type_} {name};")
|
|
|
|
if default:
|
|
vars_init.append(f"me->{name} = {default};")
|
|
|
|
vars_setters_def.append(
|
|
setter_def.replace("{type}", type_)
|
|
.replace("{name}", name)
|
|
.replace("{Name}", ucfirst(name))
|
|
)
|
|
|
|
vars_setters_dec.append(
|
|
setter_dec.replace("{type}", type_)
|
|
.replace("{Name}", ucfirst(name))
|
|
)
|
|
|
|
variables["VARS_INIT"] = "\n".join(vars_init)
|
|
variables["VARS_STRUCT"] = "\n".join(vars_struct)
|
|
variables["VARS_SETTERS_DEF"] = "\n\n".join(vars_setters_def)
|
|
variables["VARS_SETTERS_DEC"] = "\n\n".join(vars_setters_dec)
|
|
|
|
##################
|
|
# Fill templates #
|
|
##################
|
|
|
|
file_c = fill_template(dir_, variables, "file.c")
|
|
file_h = fill_template(dir_, variables, "file.h")
|
|
|
|
output_file(dir_, variables["filename_lc"]+".c", file_c)
|
|
output_file(dir_, variables["filename_lc"]+".h", file_h)
|
|
|
|
# Save config
|
|
with open(conf_path, "w") as f:
|
|
conf = {
|
|
"author": variables["author"]
|
|
}
|
|
json.dump(conf, f)
|
|
|
|
if __name__ == "__main__":
|
|
main() |