No codegen doubly-linked list #8

Merged
abdelrahman merged 9 commits from no-codegen-dbl-list into main 2025-12-17 03:53:13 +00:00
18 changed files with 3 additions and 1104 deletions
Showing only changes of commit d36812e632 - Show all commits

View File

@@ -1,4 +1,4 @@
.PHONY: help full base os prng testing uuid all clean builddir build-test run-test codegen install build-lib ccodegen .PHONY: help full base os prng testing uuid all clean builddir build-test run-test install build-lib ccodegen
# External variables # External variables
CC = clang CC = clang
@@ -7,7 +7,6 @@ AR = ar
BUILD_TYPE = Debug BUILD_TYPE = Debug
BUILD_DIR = libwapp-build/$(PLATFORM)-$(BUILD_TYPE) BUILD_DIR = libwapp-build/$(PLATFORM)-$(BUILD_TYPE)
INSTALL_PREFIX = dist INSTALL_PREFIX = dist
CODEGEN_INPUT = ""
RUNTIME_ASSERT = true RUNTIME_ASSERT = true
# Internal variables # Internal variables
@@ -95,7 +94,7 @@ ifeq ($(KERNEL), Darwin)
ECHO_E = echo ECHO_E = echo
endif endif
all: clean builddir codegen run-c-test full run-cc-test all: clean builddir run-c-test full run-cc-test
help: help:
@$(ECHO_E) "$(BOLD)$(BLUE)Available build variables:$(RESET)" @$(ECHO_E) "$(BOLD)$(BLUE)Available build variables:$(RESET)"
@@ -105,7 +104,6 @@ help:
@$(ECHO_E) " $(GREEN)BUILD_TYPE$(RESET) Build type $(MAGENTA)[Debug | RelWithDebInfo | Release] $(YELLOW)(Default: Debug)$(RESET)." @$(ECHO_E) " $(GREEN)BUILD_TYPE$(RESET) Build type $(MAGENTA)[Debug | RelWithDebInfo | Release] $(YELLOW)(Default: Debug)$(RESET)."
@$(ECHO_E) " $(GREEN)BUILD_DIR$(RESET) Directory where build files will be written." @$(ECHO_E) " $(GREEN)BUILD_DIR$(RESET) Directory where build files will be written."
@$(ECHO_E) " $(GREEN)INSTALL_PREFIX$(RESET) Prefix where library and include files will be installed." @$(ECHO_E) " $(GREEN)INSTALL_PREFIX$(RESET) Prefix where library and include files will be installed."
@$(ECHO_E) " $(GREEN)CODEGEN_INPUT$(RESET) Input file for code generation (See $(CYAN)codegen_custom_data_example.json$(RESET) for an example)."
@$(ECHO_E) " $(GREEN)RUNTIME_ASSERT$(RESET) Whether runtime asserts are enabled $(MAGENTA)[true | false] $(YELLOW)(Default: true)$(RESET)." @$(ECHO_E) " $(GREEN)RUNTIME_ASSERT$(RESET) Whether runtime asserts are enabled $(MAGENTA)[true | false] $(YELLOW)(Default: true)$(RESET)."
@$(ECHO_E) " $(GREEN)$(RESET) $(BOLD)$(BG_RED)DISCLAIMER:$(RESET) Using this flag is not recommended as it disables safety checks" @$(ECHO_E) " $(GREEN)$(RESET) $(BOLD)$(BG_RED)DISCLAIMER:$(RESET) Using this flag is not recommended as it disables safety checks"
@$(ECHO_E) " $(GREEN)$(RESET) potentially leading to Undefined Behaviour." @$(ECHO_E) " $(GREEN)$(RESET) potentially leading to Undefined Behaviour."
@@ -167,10 +165,7 @@ run-cc-test: build-cc-test
@export LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:"$(BUILD_DIR)" && "$(TEST_CXX_OUT)" @export LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:"$(BUILD_DIR)" && "$(TEST_CXX_OUT)"
@rm "$(TEST_CXX_OUT)" @rm "$(TEST_CXX_OUT)"
codegen: install: build-lib
python3 -m codegen -f $(CODEGEN_INPUT)
install: codegen build-lib
@mkdir -p "$(LIB_INSTALL)" @mkdir -p "$(LIB_INSTALL)"
@cp -v "$(LIB_OUT)" "$(LIB_INSTALL)" @cp -v "$(LIB_OUT)" "$(LIB_INSTALL)"
@mkdir -p "$(INCLUDE_INSTALL)" @mkdir -p "$(INCLUDE_INSTALL)"

View File

@@ -1,47 +0,0 @@
import json
from typing import Dict
from pathlib import Path
from codegen.datatypes import CDataType, CStruct
from codegen.constants import WAPP_REPO_ROOT, DBL_LIST_DATA
from codegen.dbl_list.make_dbl_list import DblListData, make_dbl_list
def main(types_file: Path | None):
dbl_list_datatypes: Dict[CDataType, DblListData] = {}
if types_file is not None and types_file.is_file() and "json" in types_file.suffix.lower():
with types_file.open("r") as infile:
datatypes = json.load(infile)
dbl_list_data = datatypes.get(DBL_LIST_DATA)
if dbl_list_data is not None and isinstance(dbl_list_data, dict):
dbl_list_datatypes = {k: DblListData.from_dict(v) for k, v in dbl_list_data.items()}
make_dbl_list(dbl_list_datatypes)
# Save example types file
custom_struct = CStruct(name="custom_type", cargs=[], typedef_name="CustomType")
example = {
DBL_LIST_DATA: {
"CustomType": DblListData(
node_typename="CustomTypeNode",
list_typename="CustomTypeList",
hdr_decl_types=[custom_struct],
).to_dict()
},
}
example_file = WAPP_REPO_ROOT / "codegen_custom_data_example.json"
with example_file.open("w") as outfile:
json.dump(example, outfile, indent=2)
if __name__ == "__main__":
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-f", "--types-file", type=Path, help="JSON file containing custom types for codegen")
args = parser.parse_args()
main(args.types_file)

View File

@@ -1,9 +0,0 @@
from pathlib import Path
# Paths
PACKAGE_DIR = Path(__file__).parent.resolve()
WAPP_REPO_ROOT = PACKAGE_DIR.parent
WAPP_SRC_ROOT = WAPP_REPO_ROOT / "src"
# Dictionary Keys
DBL_LIST_DATA = "dbl_list_data"

View File

@@ -1,503 +0,0 @@
from enum import Enum
from pathlib import Path
from typing import Optional, Union, List, Dict, Type, Any, TypeVar, cast
from dataclasses import dataclass, asdict, field, fields
from codegen.constants import WAPP_SRC_ROOT
from codegen.utils import convert_to_relative
E = TypeVar("E", bound="Enum")
S = TypeVar("S", bound="SerialisableDataclass")
F = TypeVar("F", bound="CFile")
@dataclass
class SerialisableDataclass:
def to_dict(self) -> Dict[str, Any]:
d = asdict(self)
for f in fields(self):
member = getattr(self, f.name)
if isinstance(member, list):
d[f.name] = [self.__serialise_member(i) for i in member]
else:
d[f.name] = self.__serialise_member(member)
return d
def __serialise_member(self, member: Any) -> Any:
if isinstance(member, Enum):
return member.value
elif isinstance(member, SerialisableDataclass):
return member.to_dict()
return member
@staticmethod
def to_enum_value(value: Any, _type: Type[E]) -> "E":
if isinstance(value, _type):
return value
return _type(value)
@staticmethod
def to_c_usertype(value: dict[str, Any]) -> "CUserType":
try:
output = CStruct.from_dict(value)
except TypeError:
output = CEnum.from_dict(value)
return output
@staticmethod
def to_cdatatype(value: Any) -> "CDataType":
if isinstance(value, dict):
output = SerialisableDataclass.to_c_usertype(value)
else:
try:
output = CType(value)
except ValueError:
output = value
return output
@classmethod
def from_dict(cls: Type[S], d: Dict[str, Any]) -> "S":
return cls(**d)
class CType(Enum):
VOID = "void"
BOOL = "b8"
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 = "wapp_extern "
INTERNAL = "wapp_intern "
PERSISTENT = "wapp_persist "
def __str__(self) -> str:
return self.value
class CPointerType(Enum):
NONE = ""
SINGLE = "*"
DOUBLE = "**"
def __str__(self) -> str:
return self.value
@dataclass
class CPointer(SerialisableDataclass):
_type: CPointerType = CPointerType.NONE
qualifier: CQualifier = CQualifier.NONE
def __str__(self) -> str:
return str(self._type) + str(self.qualifier)
@classmethod
def from_dict(cls: Type["CPointer"], d: Dict[str, Any]) -> "CPointer":
ptr = CPointer(**d)
ptr._type = CPointer.to_enum_value(ptr._type, CPointerType)
ptr.qualifier = CPointer.to_enum_value(ptr.qualifier, CQualifier)
return ptr
@dataclass
class CEnumVal(SerialisableDataclass):
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(SerialisableDataclass):
name: str
values: List[CEnumVal]
typedef: bool = False
def __str__(self) -> str:
if self.typedef:
header = "typedef enum {\n"
footer = f"}} {self.name};\n"
else:
header = f"enum {self.name} {{\n"
footer = "};\n"
values = ""
for value in self.values:
values += f" {str(value)},\n"
return header + values + footer
@classmethod
def from_dict(cls: Type["CEnum"], d: Dict[str, Any]) -> "CEnum":
e = CEnum(**d)
e.values = [CEnumVal.from_dict(v) for v in e.values if isinstance(v, dict)]
return e
@dataclass
class CMacro(SerialisableDataclass):
name: str
value: str
def __str__(self) -> str:
return f"#define {self.name} {self.value}\n"
@dataclass
class CStruct(SerialisableDataclass):
name: str
cargs: List["CArg"]
typedef_name: Optional[str] = None
def __str__(self) -> str:
return self.declare() + self.define()
def declare(self) -> str:
declaration = f"typedef struct {self.name} {self.typedef_name if self.typedef_name is not None else self.name};\n"
return declaration
def define(self):
definition = f"struct {self.name} {{\n"
args = ""
for arg in self.cargs:
args += f" {str(arg)};\n"
footer = "};\n"
return definition + args + footer;
@classmethod
def from_dict(cls: Type["CStruct"], d: Dict[str, Any]) -> "CStruct":
s = CStruct(**d)
s.cargs = [CArg.from_dict(v) for v in s.cargs if isinstance(v, dict)]
return s
CUserType = Union[CStruct, CEnum]
CDataType = Union[CType, CUserType, str]
@dataclass
class CArg(SerialisableDataclass):
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_datatype_string(self._type) + " "
pointer = str(self.pointer)
array = "[]" if self.array else ""
return qualifier + _type + pointer + self.name + array
@classmethod
def from_dict(cls: Type["CArg"], d: Dict[str, Any]) -> "CArg":
arg = CArg(**d)
arg._type = CArg.to_cdatatype(arg._type)
if isinstance(arg.pointer, dict):
arg.pointer = CPointer.from_dict(arg.pointer)
arg.qualifier = CArg.to_enum_value(arg.qualifier, CQualifier)
return arg
@dataclass
class CFunc(SerialisableDataclass):
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 self.qualifiers:
if qualifier == CQualifier.NONE:
continue
if len(qualifiers) > 0:
qualifiers += " "
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 + get_datatype_string(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\n"
@classmethod
def from_dict(cls: Type["CFunc"], d: Dict[str, Any]) -> "CFunc":
f = CFunc(**d)
f.ret_type = CFunc.to_cdatatype(f.ret_type)
f.args = [CArg.from_dict(v) for v in f.args if isinstance(v, dict)]
f.qualifiers = [CFunc.to_enum_value(v, CQualifier) for v in f.qualifiers]
if isinstance(f.pointer, dict):
f.pointer = CPointer.from_dict(f.pointer)
return f
@dataclass
class CInclude(SerialisableDataclass):
header: Union[str, "CHeader"]
local: bool = False
same_dir: bool = False
def __str__(self) -> str:
if isinstance(self.header, CHeader):
name = f"{self.header.name}.{self.header.extension}"
else:
name = self.header
if self.local:
open_symbol = '"'
close_symbol = '"'
if self.same_dir:
name = f"./{name}"
else:
open_symbol = '<'
close_symbol = '>'
return f"#include {open_symbol}{name}{close_symbol}\n"
@classmethod
def from_dict(cls: Type["CInclude"], d: Dict[str, Any]) -> "CInclude":
inc = CInclude(**d)
if isinstance(inc.header, dict):
inc.header = CHeader.from_dict(inc.header)
return inc
@dataclass
class CFile(SerialisableDataclass):
name: str
extension: str
includes: List[CInclude] = field(default_factory=list)
types: List[CUserType] = field(default_factory=list)
funcs: List[CFunc] = field(default_factory=list)
decl_types: List[CStruct] = field(default_factory=list)
macros: List[CMacro] = field(default_factory=list)
c_macros: List[CMacro] = field(default_factory=list)
cpp_macros: List[CMacro] = field(default_factory=list)
def save(self, output_dir: Path):
self.includes.extend(
[
CInclude(
header=str(convert_to_relative(WAPP_SRC_ROOT / "common" / "aliases" / "aliases.h", output_dir)).replace("\\", "/"),
local=True,
),
CInclude(
header=str(convert_to_relative(WAPP_SRC_ROOT / "common" / "platform" / "platform.h", output_dir)).replace("\\", "/"),
local=True,
)
]
)
output_file = output_dir / f"{self.name}.{self.extension}"
with open(output_file, "w+") as outfile:
outfile.write(str(self))
def __str__(self) -> str:
return """\
/**
* THIS FILE IS AUTOMATICALLY GENERATED. ANY MODIFICATIONS TO IT WILL BE OVERWRITTEN.
*/
"""
@classmethod
def from_dict(cls: Type["CFile"], d: Dict[str, Any]) -> "CFile":
f = CFile(**d)
f.deserialise_c_file_data()
return f
def deserialise_c_file_data(self) -> None:
self.includes = [CInclude.from_dict(v) for v in self.includes if isinstance(v, dict)]
self.types = [CFile.to_c_usertype(v) for v in self.types if isinstance(v, dict)]
self.funcs = [CFunc.from_dict(v) for v in self.funcs if isinstance(v, dict)]
self.decl_types = [CStruct.from_dict(v) for v in self.decl_types if isinstance(v, dict)]
self.macros = [CMacro.from_dict(v) for v in self.macros if isinstance(v, dict)]
self.c_macros = [CMacro.from_dict(v) for v in self.c_macros if isinstance(v, dict)]
self.cpp_macros = [CMacro.from_dict(v) for v in self.cpp_macros if isinstance(v, dict)]
@dataclass
class CHeader(CFile):
extension: str = "h"
def __str__(self) -> str:
name_upper = self.name.upper()
header_guard_name = f"{name_upper}_H"
header_guard_open = f"#ifndef {header_guard_name}\n#define {header_guard_name}\n\n"
header_guard_close = f"#endif // !{header_guard_name}\n"
c_linkage_open = "#ifdef WAPP_PLATFORM_CPP\nBEGIN_C_LINKAGE\n#endif // !WAPP_PLATFORM_CPP\n\n"
c_linkage_close = "\n#ifdef WAPP_PLATFORM_CPP\nEND_C_LINKAGE\n#endif // !WAPP_PLATFORM_CPP\n\n"
includes = _get_includes_string(self.includes)
macros = ""
for macro in self.macros:
macros += str(macro)
if len(macros) > 0:
macros += "\n"
if len(self.cpp_macros) > 0:
macros += "#ifdef WAPP_PLATFORM_CPP\n"
for macro in self.cpp_macros:
macros += str(macro)
macros += "#else\n"
for macro in self.c_macros:
macros += str(macro)
macros += "#endif // !WAPP_PLATFORM_CPP\n\n"
forward_declarations = ""
for _type in self.decl_types:
forward_declarations += _type.declare()
if len(forward_declarations) > 0:
forward_declarations += "\n"
types = ""
for _type in self.types:
types += str(_type) + "\n"
funcs = ""
for func in self.funcs:
funcs += func.declare()
return (
super().__str__() +
header_guard_open +
includes +
c_linkage_open +
macros +
forward_declarations +
types +
funcs +
c_linkage_close +
header_guard_close
)
@classmethod
def from_dict(cls: Type["CHeader"], d: Dict[str, Any]) -> "CHeader":
return cast("CHeader", super().from_dict(d))
@dataclass
class CSource(CFile):
extension: str = "c"
internal_funcs: List[CFunc] = field(default_factory=list)
def __str__(self) -> str:
includes = _get_includes_string(self.includes)
macros = ""
for macro in self.macros:
macros += str(macro)
if len(macros) > 0:
macros += "\n"
forward_declarations = ""
for _type in self.decl_types:
forward_declarations += _type.declare()
if len(forward_declarations) > 0:
forward_declarations += "\n"
types = ""
for _type in self.types:
types += str(_type) + "\n"
internal_funcs_decl = ""
internal_funcs_def = ""
for func in self.internal_funcs:
internal_funcs_decl += func.declare()
internal_funcs_def += func.define()
if len(internal_funcs_decl) > 0:
internal_funcs_decl += "\n"
funcs = ""
for func in self.funcs:
funcs += func.define()
return (
super().__str__() +
includes +
macros +
forward_declarations +
types +
internal_funcs_decl +
funcs +
internal_funcs_def
)
@classmethod
def from_dict(cls: Type["CSource"], d: Dict[str, Any]) -> "CSource":
s = CSource(**d)
s.deserialise_c_file_data()
s.internal_funcs = [CFunc.from_dict(v) for v in s.funcs if isinstance(v, dict)]
return s
def get_datatype_string(_type: CDataType) -> str:
if isinstance(_type, CType):
return str(_type)
elif isinstance(_type, CStruct) or isinstance(_type, CEnum):
return _type.name
elif isinstance(_type, str):
return _type
def _get_includes_string(includes: List[CInclude]) -> str:
output = ""
for include in sorted(includes, key=lambda inc: inc.local, reverse=True):
output += str(include)
if len(output) > 0:
output += "\n"
return output

View File

@@ -1,339 +0,0 @@
from pathlib import Path
from dataclasses import dataclass, field
from typing import List, Dict, Any, Type
from codegen.constants import WAPP_SRC_ROOT
from codegen.utils import load_func_body_from_file, convert_to_relative
from codegen.datatypes import (
CDataType,
CMacro,
CStruct,
CFunc,
CHeader,
CSource,
CArg,
CType,
CPointer,
CPointerType,
CQualifier,
CInclude,
SerialisableDataclass,
get_datatype_string,
)
@dataclass
class DblListData(SerialisableDataclass):
node_typename: str
list_typename: str
hdr_decl_types: List[CStruct] = field(default_factory=list)
src_decl_types: List[CStruct] = field(default_factory=list)
@classmethod
def from_dict(cls: Type["DblListData"], d: Dict[str, Any]) -> "DblListData":
data = DblListData(**d)
data.hdr_decl_types = [CStruct.from_dict(v) for v in data.hdr_decl_types if isinstance(v, dict)]
data.src_decl_types = [CStruct.from_dict(v) for v in data.src_decl_types if isinstance(v, dict)]
return data
def make_dbl_list(user_datatypes: Dict[CDataType, DblListData] = {}):
def __format_func_body(
filename: Path,
type_string: str,
type_string_upper: str,
type_string_lower: str,
node_typename: str,
list_typename: str
):
return load_func_body_from_file(filename).format(
T=type_string,
NodeType=node_typename,
ListType=list_typename,
Tupper=type_string_upper,
Tlower=type_string_lower,
)
out_dir = WAPP_SRC_ROOT / "base" / "dbl_list"
out_dir.mkdir(parents=True, exist_ok=True)
common_decl_types: List[CStruct] = []
datatypes: dict[CDataType, DblListData] = {
CType.VOID: DblListData(node_typename="GenericNode", list_typename="GenericList"),
"void *": DblListData(node_typename="VoidPNode", list_typename="VoidPList"),
"Str8": DblListData(
node_typename="Str8Node",
list_typename="Str8List",
hdr_decl_types=[
CStruct(name="str8", cargs=[], typedef_name="Str8"),
],
),
}
for _type in CType:
if _type == CType.VOID:
continue
type_title = _type.value.title()
datatypes[_type] = DblListData(
node_typename=f"{type_title}Node",
list_typename=f"{type_title}List",
)
datatypes.update(user_datatypes)
snippets_dir = Path(__file__).parent / "snippets"
header = CHeader(
name="dbl_list",
decl_types=[*common_decl_types],
includes=[],
types=[],
funcs=[]
)
source = CSource(
name=header.name,
decl_types=[*common_decl_types],
includes=[
CInclude(header, local=True, same_dir=True),
CInclude(
header=str(convert_to_relative(WAPP_SRC_ROOT / "common" / "assert" / "assert.h", out_dir)).replace("\\", "/"),
local=True
),
CInclude(header="stddef.h"),
],
internal_funcs=[],
funcs=header.funcs
)
for _type, dbl_list_data in datatypes.items():
type_string = get_datatype_string(_type)
clean_type_string = type_string.replace(" ", "").replace("*", "_ptr")
type_string_upper = clean_type_string.upper()
type_string_lower = clean_type_string.lower()
node = CStruct(
name=dbl_list_data.node_typename,
cargs=[
CArg(name="item", _type=type_string, pointer=CPointer(_type=CPointerType.SINGLE)),
],
)
node.cargs.extend([
CArg(name="prev", _type=node, pointer=CPointer(_type=CPointerType.SINGLE)),
CArg(name="next", _type=node, pointer=CPointer(_type=CPointerType.SINGLE)),
])
dl_list = CStruct(
name=dbl_list_data.list_typename,
cargs=[
CArg(name="first", _type=node, pointer=CPointer(_type=CPointerType.SINGLE)),
CArg(name="last", _type=node, pointer=CPointer(_type=CPointerType.SINGLE)),
CArg(name="node_count", _type=CType.U64),
],
)
header.types.extend([node, dl_list])
header.decl_types.extend(dbl_list_data.hdr_decl_types)
source.decl_types.extend(dbl_list_data.src_decl_types)
if isinstance(_type, CType) and _type == CType.VOID:
# Don't define any functions for the generic node and list
continue
node_cmacro = CMacro(
name=f"wapp_{type_string_lower}_list_node(ITEM_PTR)",
value=__format_func_body(
filename=snippets_dir / "list_node",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
)
node_cppmacro = CMacro(
name=f"wapp_{type_string_lower}_list_node(ITEM_PTR)",
value=__format_func_body(
filename=snippets_dir / "list_node_cpp",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
)
get_func = CFunc(
name=f"wapp_{type_string_lower}_list_get",
ret_type=node,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE), qualifier=CQualifier.CONST),
CArg(name="index", _type=CType.U64),
],
body=__format_func_body(
filename=snippets_dir / "list_get",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
pointer=CPointer(CPointerType.SINGLE),
)
push_front_func = CFunc(
name=f"wapp_{type_string_lower}_list_push_front",
ret_type=CType.VOID,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="node", _type=node, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "list_push_front",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
)
push_back_func = CFunc(
name=f"wapp_{type_string_lower}_list_push_back",
ret_type=CType.VOID,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="node", _type=node, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "list_push_back",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
)
insert_func = CFunc(
name=f"wapp_{type_string_lower}_list_insert",
ret_type=CType.VOID,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="node", _type=node, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="index", _type=CType.U64),
],
body=__format_func_body(
filename=snippets_dir / "list_insert",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
)
pop_front_func = CFunc(
name=f"wapp_{type_string_lower}_list_pop_front",
ret_type=node,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "list_pop_front",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
pointer=CPointer(CPointerType.SINGLE),
)
pop_back_func = CFunc(
name=f"wapp_{type_string_lower}_list_pop_back",
ret_type=node,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "list_pop_back",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
pointer=CPointer(CPointerType.SINGLE),
)
remove_func = CFunc(
name=f"wapp_{type_string_lower}_list_remove",
ret_type=node,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="index", _type=CType.U64),
],
body=__format_func_body(
filename=snippets_dir / "list_remove",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
pointer=CPointer(CPointerType.SINGLE),
)
empty_func = CFunc(
name=f"wapp_{type_string_lower}_list_empty",
ret_type=CType.VOID,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "list_empty",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
)
node_to_list_func = CFunc(
name=f"{type_string_lower}_node_to_list",
ret_type=dl_list,
args=[
CArg(name="node", _type=node, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "node_to_list",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
qualifiers=[CQualifier.INTERNAL],
)
header.c_macros.append(node_cmacro)
header.cpp_macros.append(node_cppmacro)
header.funcs.extend([
get_func,
push_front_func,
push_back_func,
insert_func,
pop_front_func,
pop_back_func,
remove_func,
empty_func,
])
source.internal_funcs.append(node_to_list_func)
source.funcs = header.funcs
header.save(out_dir)
source.save(out_dir)

View File

@@ -1,6 +0,0 @@
wapp_debug_assert(list != NULL, "`list` should not be NULL");
u64 count = list->node_count;
for (u64 i = 0; i < count; ++i) {{
wapp_{Tlower}_list_pop_back(list);
}}

View File

@@ -1,11 +0,0 @@
wapp_runtime_assert(index < list->node_count, "`index` is out of bounds");
{NodeType} *output = NULL;
{NodeType} *current = list->first;
for (u64 i = 1; i <= index; ++i) {{
current = current->next;
}}
output = current;
return output;

View File

@@ -1,26 +0,0 @@
wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");
if (index == 0) {{
wapp_{Tlower}_list_push_front(list, node);
return;
}} else if (index == list->node_count) {{
wapp_{Tlower}_list_push_back(list, node);
return;
}}
{NodeType} *dst_node = wapp_{Tlower}_list_get(list, index);
if (!dst_node) {{
return;
}}
{ListType} node_list = {Tlower}_node_to_list(node);
list->node_count += node_list.node_count;
{NodeType} *prev = dst_node->prev;
dst_node->prev = node_list.last;
prev->next = node_list.first;
node_list.first->prev = prev;
node_list.last->next = dst_node;

View File

@@ -1 +0,0 @@
(({NodeType}){{.item = ITEM_PTR}})

View File

@@ -1 +0,0 @@
{NodeType}{{ITEM_PTR, nullptr, nullptr}}

View File

@@ -1,22 +0,0 @@
wapp_debug_assert(list != NULL, "`list` should not be NULL");
{NodeType} *output = NULL;
if (list->node_count == 0) {{
goto RETURN_{Tupper}_LIST_POP_BACK;
}}
output = list->last;
if (list->node_count == 1) {{
*list = ({ListType}){{0}};
goto RETURN_{Tupper}_LIST_POP_BACK;
}}
--(list->node_count);
list->last = output->prev;
output->prev = output->next = NULL;
RETURN_{Tupper}_LIST_POP_BACK:
return output;

View File

@@ -1,22 +0,0 @@
wapp_debug_assert(list != NULL, "`list` should not be NULL");
{NodeType} *output = NULL;
if (list->node_count == 0) {{
goto RETURN_{Tupper}_LIST_POP_FRONT;
}}
output = list->first;
if (list->node_count == 1) {{
*list = ({ListType}){{0}};
goto RETURN_{Tupper}_LIST_POP_FRONT;
}}
--(list->node_count);
list->first = output->next;
output->prev = output->next = NULL;
RETURN_{Tupper}_LIST_POP_FRONT:
return output;

View File

@@ -1,18 +0,0 @@
wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");
{ListType} node_list = {Tlower}_node_to_list(node);
if (list->node_count == 0) {{
*list = node_list;
return;
}}
list->node_count += node_list.node_count;
{NodeType} *last = list->last;
if (last) {{
last->next = node_list.first;
}}
list->last = node_list.last;
node_list.first->prev = last;

View File

@@ -1,18 +0,0 @@
wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");
{ListType} node_list = {Tlower}_node_to_list(node);
if (list->node_count == 0) {{
*list = node_list;
return;
}}
list->node_count += node_list.node_count;
{NodeType} *first = list->first;
if (first) {{
first->prev = node_list.last;
}}
list->first = node_list.first;
node_list.last->next = first;

View File

@@ -1,26 +0,0 @@
wapp_debug_assert(list != NULL, "`list` should not be NULL");
{NodeType} *output = NULL;
if (index == 0) {{
output = wapp_{Tlower}_list_pop_front(list);
goto RETURN_{Tupper}_LIST_REMOVE;
}} else if (index == list->node_count) {{
output = wapp_{Tlower}_list_pop_back(list);
goto RETURN_{Tupper}_LIST_REMOVE;
}}
output = wapp_{Tlower}_list_get(list, index);
if (!output) {{
goto RETURN_{Tupper}_LIST_REMOVE;
}}
output->prev->next = output->next;
output->next->prev = output->prev;
--(list->node_count);
output->prev = output->next = NULL;
RETURN_{Tupper}_LIST_REMOVE:
return output;

View File

@@ -1,13 +0,0 @@
{ListType} output = {{.first = node, .last = node, .node_count = 1}};
while (output.first->prev != NULL) {{
output.first = output.first->prev;
++(output.node_count);
}}
while (output.last->next != NULL) {{
output.last = output.last->next;
++(output.node_count);
}}
return output;

View File

@@ -1,18 +0,0 @@
import os
import sys
from pathlib import Path
def load_func_body_from_file(filename: Path) -> str:
with open(filename, "r") as infile:
return infile.read().rstrip()
def convert_to_relative(path: Path, target: Path) -> Path:
major = sys.version_info.major
minor = sys.version_info.minor
if major >= 3 and minor >= 12:
return path.relative_to(target, walk_up=True)
else:
return Path(os.path.relpath(str(path), start=str(target)))

View File

@@ -1,16 +0,0 @@
{
"dbl_list_data": {
"CustomType": {
"node_typename": "CustomTypeNode",
"list_typename": "CustomTypeList",
"hdr_decl_types": [
{
"name": "custom_type",
"cargs": [],
"typedef_name": "CustomType"
}
],
"src_decl_types": []
}
}
}