XFTGenerator/XFTGenerator.py

323 lines
9.3 KiB
Python
Raw Normal View History

2023-07-11 13:10:15 +00:00
# XF Template Generator
__author__ = "Louis Heredero"
__copyright__ = "Copyright 2023, Louis Heredero"
__license__ = "GPL3.0"
2023-07-12 09:35:10 +00:00
__version__ = "1.2.0"
2023-07-11 13:10:15 +00:00
import json
import locale
import os
import re
import time
2023-07-11 13:46:42 +00:00
def ucfirst(s):
return s[0].upper() + s[1:]
2023-07-11 13:10:15 +00:00
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__)
2023-07-11 13:46:42 +00:00
# Load config if it exists
2023-07-11 13:10:15 +00:00
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()
2023-07-11 21:40:12 +00:00
fn_uc = variables["filename"]
2023-07-11 13:10:15 +00:00
##########
# States #
##########
states = []
print("States (leave empty to end):")
print("> INIT")
while True:
state = input("> ").upper()
if state:
states.append(state)
else:
break
2023-07-11 21:40:12 +00:00
prefix = "ST"+variables["fn"]+"_"
variables["STATES_ENUM"] = ",\n".join([prefix+s for s in states])
2023-07-11 13:10:15 +00:00
states_cases = []
2023-07-11 21:40:12 +00:00
states_cases_entry = []
states_cbs_struct = []
states_cbs_init = []
2023-07-11 13:10:15 +00:00
2023-07-11 13:46:42 +00:00
for state in states:
2023-07-11 21:40:12 +00:00
case_ = "case {STATE}:\n"
2023-07-11 13:10:15 +00:00
case_ += " break;"
2023-07-11 21:40:12 +00:00
case_ = case_.replace("{STATE}", prefix+state)
2023-07-11 13:10:15 +00:00
states_cases.append(case_)
2023-07-11 21:40:12 +00:00
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;")
2023-07-11 13:10:15 +00:00
variables["STATES_CASES"] = "\n\n".join(states_cases)
2023-07-11 21:40:12 +00:00
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)
2023-07-11 13:10:15 +00:00
##########
# Events #
##########
events = ["init = 100"]
print("Events (leave empty to end):")
print("> init")
while True:
2023-07-13 11:01:18 +00:00
event = input("> ")
2023-07-11 13:10:15 +00:00
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)
2023-07-11 13:46:42 +00:00
events_emits_def = []
2023-07-11 13:10:15 +00:00
emit_def = ""
2023-08-23 13:39:57 +00:00
emit_def += "void {filename}_emit{Event}({filename}* me, uint16_t t, int64_t data) {\n"
emit_def += " POST(me, &{filename}_processEvent, ev{fn}{event}, t, data);\n"
2023-07-11 13:10:15 +00:00
emit_def += "}"
2023-07-11 21:40:12 +00:00
emit_def = emit_def.replace("{filename}", fn_uc).replace("{fn}", variables["fn"])
2023-07-11 13:10:15 +00:00
for event in events[1:]:
2023-07-11 13:46:42 +00:00
events_emits_def.append(
2023-07-13 11:01:18 +00:00
emit_def.replace("{event}", event).replace("{Event}", ucfirst(event))
2023-07-11 13:10:15 +00:00
)
2023-07-11 13:46:42 +00:00
variables["EVENTS_EMITS_DEF"] = "\n\n".join(events_emits_def)
2023-07-11 13:10:15 +00:00
2023-07-11 13:46:42 +00:00
events_emits_dec = []
2023-07-11 13:10:15 +00:00
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"
2023-08-23 13:39:57 +00:00
emit_dec += " * @param data data to put on the event for XF\n"
2023-07-11 13:10:15 +00:00
emit_dec += " */"
2023-08-23 13:39:57 +00:00
emit_dec += "void {filename}_emit{Event}({filename}* me, uint16_t t, int64_t data);"
2023-07-11 21:40:12 +00:00
emit_dec = emit_dec.replace("{filename}", fn_uc)
2023-07-11 13:10:15 +00:00
for event in events[1:]:
2023-07-11 13:46:42 +00:00
events_emits_dec.append(
2023-07-13 12:48:07 +00:00
emit_dec.replace("{event}", event).replace("{Event}", ucfirst(event))
2023-07-11 13:10:15 +00:00
)
2023-07-11 13:46:42 +00:00
variables["EVENTS_EMITS_DEC"] = "\n\n".join(events_emits_dec)
2023-07-11 13:47:07 +00:00
#############
# 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 += "}"
2023-07-11 21:40:12 +00:00
setter_def = setter_def.replace("{filename}", fn_uc)
2023-07-11 13:47:07 +00:00
setter_dec = "void {filename}_set{Name}({filename}* me, {type} v);"
2023-07-11 21:40:12 +00:00
setter_dec = setter_dec.replace("{filename}", fn_uc)
2023-07-11 13:47:07 +00:00
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)
2023-07-11 13:46:42 +00:00
##################
# Fill templates #
##################
2023-07-11 13:10:15 +00:00
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)
2023-07-11 13:46:42 +00:00
# Save config
2023-07-11 13:10:15 +00:00
with open(conf_path, "w") as f:
conf = {
"author": variables["author"]
}
json.dump(conf, f)
if __name__ == "__main__":
main()