commit 514b383b6525fc5c1ec95cb0ce7d67447f46e904 Author: Abdelrahman Date: Fri Feb 7 22:30:10 2025 +0000 Implement codegen.py diff --git a/codegen.py b/codegen.py new file mode 100644 index 0000000..28a34a4 --- /dev/null +++ b/codegen.py @@ -0,0 +1,331 @@ +from enum import Enum +from pathlib import Path +from typing import Optional, Union +from dataclasses import dataclass, field + + +class CType(Enum): + VOID = "void " + BOOL = "bool " + CHAR = "char " + C8 = "c8 " + C16 = "c16 " + C32 = "c32 " + I8 = "i8 " + I16 = "i16 " + I32 = "i32 " + I64 = "i64 " + U8 = "u8 " + U16 = "u16 " + U32 = "u32 " + U64 = "u64 " + F32 = "f32 " + F64 = "f64 " + F128 = "f128 " + IPTR = "iptr " + UPTR = "uptr " + + def __str__(self) -> str: + return self.value + + +class CQualifier(Enum): + NONE = "" + CONST = "const " + EXTERNAL = "external " + INTERNAL = "internal " + PERSISTENT = "persistent " + + def __str__(self) -> str: + return self.value + + +class CPointerType(Enum): + NONE = "" + SINGLE = "*" + DOUBLE = "**" + + def __str__(self) -> str: + return self.value + + +@dataclass +class CPointer: + _type: CPointerType = CPointerType.NONE + qualifier: CQualifier = CQualifier.NONE + + def __str__(self) -> str: + return str(self._type) + str(self.qualifier) + + +@dataclass +class CEnumVal: + name: str + value: Optional[int] = None + + def __str__(self) -> str: + return self.name + "" if self.value is None else f" = {self.value}" + + +@dataclass +class CEnum: + name: str + values: list[CEnumVal] + typedef: bool = False + + +@dataclass +class CStruct: + name: str + cargs: list["CArg"] + + +CUserType = Union[CStruct, CEnum] +CDataType = Union[CType, CUserType] + +@dataclass +class CArg: + name: str + _type: CDataType + array: bool = False + pointer: CPointer = field(default_factory=CPointer) + qualifier: CQualifier = CQualifier.NONE + + def __str__(self) -> str: + qualifier = str(self.qualifier) + _type = get_arg_type_string(self._type) + pointer = str(self.pointer) + array = "[]" if self.array else "" + + return qualifier + _type + pointer + self.name + array + + +@dataclass +class CFunc: + name: str + ret_type: CDataType + args: list[CArg] + body: str + pointer: CPointer = field(default_factory=CPointer) + qualifiers: list[CQualifier] = field(default_factory=list) + + def __str__(self) -> str: + qualifiers = "" + for qualifier in qualifiers: + if qualifier == CQualifier.NONE: + continue + qualifiers += f"{str(qualifier)} " + + args = "" + for i, arg in enumerate(self.args): + args += f"{str(arg)}" + if i + 1 < len(self.args): + args += ", " + + return qualifiers + str(self.ret_type) + str(self.pointer) + self.name + f"({args})" + + def declare(self) -> str: + return f"{str(self)};\n" + + def define(self) -> str: + return f"{str(self)} {{\n{self.body}\n}}\n" + + +@dataclass +class CInclude: + header: Union[str, "CHeader"] + local: bool = False + + +@dataclass +class CFile: + name: str + + def save(self, output_dir: Path): + output_file = output_dir / self.name + with open(output_file, "w+") as outfile: + outfile.write(str(self)) + + def __str__(self) -> str: + return "" + + +@dataclass +class CHeader(CFile): + includes: list[CInclude] + types: list[CUserType] + funcs: list[CFunc] + + def __str__(self) -> str: + pragma = "#pragma once\n\n" + + includes = get_includes_string(self.includes) + + types = "" + for _type in self.types: + types += get_user_type_string(_type) + + funcs = "" + for func in self.funcs: + funcs += func.declare() + + return pragma + includes + types + funcs + + +@dataclass +class CSource(CFile): + includes: list[CInclude] + types: list[CUserType] + internal_funcs: list[CFunc] + funcs: list[CFunc] + + def __str__(self) -> str: + includes = get_includes_string(self.includes) + + types = "" + for _type in self.types: + types += get_user_type_string(_type) + + internal_funcs_decl = "" + internal_funcs_def = "" + for func in self.internal_funcs: + internal_funcs_decl += func.declare() + internal_funcs_def += func.define() + + funcs = "" + for func in self.funcs: + funcs += func.define() + + return includes + types + internal_funcs_decl + funcs + internal_funcs_def + + +def main(): + struct = CStruct( + name="Str8", + cargs=[ + CArg(name="size", _type=CType.U64), + CArg(name="capacity", _type=CType.U64), + CArg(name="buf", _type=CType.U8, pointer=CPointer(_type=CPointerType.SINGLE)), + ], + ) + + cenum = CEnum( + name="OS", + values=[ + CEnumVal(name="OS_LINUX"), + CEnumVal(name="OS_WINDOWS"), + CEnumVal(name="OS_MACOS"), + ] + ) + + typed_enum = CEnum( + name="Compiler", + values=[ + CEnumVal(name="COMPILER_GCC"), + CEnumVal(name="COMPILER_CLANG"), + CEnumVal(name="COMPILER_MSVC"), + ], + typedef=True, + ) + + main_func = CFunc( + name="my_custom_func", + ret_type=CType.I32, + args=[ + CArg(name="argc", _type=CType.I32), + CArg(name="argv", _type=CType.CHAR, pointer=CPointer(_type=CPointerType.DOUBLE), qualifier=CQualifier.CONST), + ], + body=" return 0;" + ) + + header = CHeader( + name="str.h", + includes=[ + CInclude(header="aliases.h", local=True), + ], + types=[struct, cenum, typed_enum], + funcs=[main_func], + ) + + source = CSource( + name="str.c", + includes=[ + CInclude(header=header, local=True), + CInclude(header="aliases.h", local=True), + ], + types=[], + internal_funcs=[], + funcs=[main_func] + ) + + header.save(Path(".")) + source.save(Path(".")) + + +def get_user_type_string(_type: Union[CStruct, CEnum]) -> str: + type_str = "" + if isinstance(_type, CStruct): + type_str += cstruct_to_string(_type) + "\n" + else: + type_str += cenum_to_string(_type) + "\n" + + return type_str + + +def cenum_to_string(cenum: CEnum) -> str: + if cenum.typedef: + header = "typedef enum {\n" + footer = f"}} {cenum.name};\n" + else: + header = f"enum {cenum.name} {{\n" + footer = "};\n" + + values = "" + for value in cenum.values: + values += f" {str(value)},\n" + + return header + values + footer + + +def cstruct_to_string(cstruct: CStruct) -> str: + typedef = f"typedef struct {cstruct.name} {cstruct.name};\n" + header = f"struct {cstruct.name} {{\n" + args = "" + for arg in cstruct.cargs: + args += f" {str(arg)};\n" + footer = "};\n" + + return typedef + header + args + footer; + + +def get_arg_type_string(_type: CDataType) -> str: + if isinstance(_type, CType): + return str(_type) + elif isinstance(_type, CStruct) or isinstance(_type, CEnum): + return _type.name + + +def get_includes_string(includes: list[CInclude]) -> str: + output = "" + for include in includes: + if isinstance(include.header, CHeader): + name = include.header.name + else: + name = include.header + + if include.local: + open_symbol = '"' + close_symbol = '"' + else: + open_symbol = '<' + close_symbol = '>' + + output += f"#include {open_symbol}{name}{close_symbol}\n" + if len(output) > 0: + output += "\n" + + return output + + +if __name__ == "__main__": + main()