# 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("> ") 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}", ucfirst(event)) ) 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 += " */\n" 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}", ucfirst(event)) ) 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()