summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAmlal El Mahrouss <amlal@nekernel.org>2026-01-08 11:41:40 +0100
committerAmlal El Mahrouss <amlal@nekernel.org>2026-01-08 11:43:00 +0100
commit0882b2456b2023f92e90abe1b1cc425bba36712c (patch)
tree8db58ee62690143818bb27fd3d36ee1eb9bfad8a
parented4ad3ecdd446d2f60a9b671a43b0e7ec32f2a04 (diff)
feat: c++front: Finalized C++Front for Nectar C++.
Signed-off-by: Amlal El Mahrouss <amlal@nekernel.org>
-rw-r--r--CITATION.cff2
-rw-r--r--README.md8
-rw-r--r--src/CompilerKit/src/Assemblers/Assembler+AMD64.cc3
-rw-r--r--src/CompilerKit/src/Compilers/CPlusPlusCompiler+AMD64.cc788
-rw-r--r--test/test_01_codegen/codegen.test.cc10
-rw-r--r--test/test_samples/class_ctor_dtor.cc15
-rw-r--r--test/test_samples/class_methods.cc13
-rw-r--r--test/test_samples/class_simple.cc10
-rw-r--r--test/test_samples/namespace_basic.cc9
-rw-r--r--test/test_samples/sample.cc23
10 files changed, 763 insertions, 118 deletions
diff --git a/CITATION.cff b/CITATION.cff
index d4006e3..5b4875b 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -10,7 +10,7 @@ identifiers:
- type: url
value: 'https://nekernel.org'
description: NeKernel.org
-repository-code: 'https://github.com/nekernel-org/necti'
+repository-code: 'https://github.com/nekernel-org/nectar'
url: 'https://nekernel.org'
abstract: >-
This is NeKernel.org's Operating System toolchain, authored
diff --git a/README.md b/README.md
index ae47c45..248803b 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
# Nectar
-![CI](https://github.com/amlel-el-mahrouss/cc/actions/workflows/necti-dev.yml/badge.svg)
+![CI](https://github.com/amlel-el-mahrouss/cc/actions/workflows/nectar-dev.yml/badge.svg)
[![License: GPL-3.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
## Structure:
@@ -33,8 +33,8 @@ Always use `format.sh` before commiting and pushing your code!
Run the following:
```sh
-git clone git@github.com:nekernel-org/necti.git
-cd necti
+git clone git@github.com:nekernel-org/nectar.git
+cd nectar
# Either build the debugger or compiler libraries/tools using nebuild.
```
@@ -48,7 +48,7 @@ And build the source tree using the NeBuild system.
## Authors & Credits
- **Amlal El Mahrouss** — Lead Developer and Compiler Architect.
-- [Full contributor list](https://github.com/nekernel-org/necti/graphs/contributors)
+- [Full contributor list](https://github.com/nekernel-org/nectar/graphs/contributors)
---
diff --git a/src/CompilerKit/src/Assemblers/Assembler+AMD64.cc b/src/CompilerKit/src/Assemblers/Assembler+AMD64.cc
index 096ebc1..007b03f 100644
--- a/src/CompilerKit/src/Assemblers/Assembler+AMD64.cc
+++ b/src/CompilerKit/src/Assemblers/Assembler+AMD64.cc
@@ -487,7 +487,8 @@ static inline bool is_not_valid(char c) {
if ((isalpha(c) || isdigit(c)) ||
((c == ' ') || (c == '\t') || (c == ',') || (c == '(') || (c == ')') || (c == '"') ||
(c == '*') || (c == '\'') || (c == '[') || (c == ']') || (c == '+') || (c == '_') ||
- (c == ':') || (c == '@') || (c == '.') || (c == '#') || (c == '%') || (c == '~') || (c == ';')))
+ (c == ':') || (c == '@') || (c == '.') || (c == '#') || (c == '%') || (c == '~') ||
+ (c == ';')))
return false;
return true;
diff --git a/src/CompilerKit/src/Compilers/CPlusPlusCompiler+AMD64.cc b/src/CompilerKit/src/Compilers/CPlusPlusCompiler+AMD64.cc
index 7d29292..24bc96f 100644
--- a/src/CompilerKit/src/Compilers/CPlusPlusCompiler+AMD64.cc
+++ b/src/CompilerKit/src/Compilers/CPlusPlusCompiler+AMD64.cc
@@ -32,7 +32,7 @@
/// @author Amlal El Mahrouss (amlal@nekernel.org)
/// @file CPlusPlusCompilerAMD64.cc
-/// @brief Optimized C++ Compiler Driver.
+/// @brief C++ Compiler Driver.
/////////////////////////////////////
@@ -42,7 +42,7 @@
/// @internal
/// @brief Avoids relative_path which could discard parts of the original.
-std::filesystem::path necti_expand_home(const std::filesystem::path& input) {
+std::filesystem::path nectar_expand_home(const std::filesystem::path& input) {
const std::string& raw = input.string();
if (!raw.empty() && raw[0] == '~') {
@@ -85,6 +85,61 @@ static Int32 kOnClassScope = 0;
/////////////////////////////////////////////////////////////////////////////////////////
+// NEW DATA STRUCTURES FOR C++ SUPPORT
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/// \brief Scope kind enumeration
+enum class ScopeKind {
+ kScopeGlobal,
+ kScopeNamespace,
+ kScopeClass,
+ kScopeFunction,
+};
+
+/// \brief Compiler scope information
+struct CompilerScope {
+ ScopeKind fKind{ScopeKind::kScopeGlobal};
+ CompilerKit::STLString fName{};
+ CompilerKit::STLString fMangledPrefix{};
+};
+
+/// \brief Variable location enumeration
+enum class VarLocation {
+ kRegister,
+ kStack,
+ kStackSpill,
+};
+
+/// \brief Extended variable information
+struct VariableInfo {
+ CompilerKit::STLString fName{};
+ VarLocation fLocation{VarLocation::kRegister};
+ Int32 fStackOffset{0};
+ CompilerKit::STLString fRegister{};
+ Int32 fSize{8};
+ bool fIsParameter{false};
+ CompilerKit::STLString fTypeName{};
+ UInt32 fLastUsed{0};
+};
+
+/// \brief Complete compiler context
+struct CompilerContext {
+ std::vector<CompilerScope> fScopeStack;
+ std::vector<VariableInfo> fVariables;
+ std::vector<CompilerStructMap> fStructMapVector;
+ CompilerKit::STLString fLastFile{};
+ CompilerKit::STLString fLastError{};
+ Int32 fStackOffset{0};
+ Int32 fMaxStackUsed{0};
+ UInt32 fInstructionCounter{0};
+};
+
+/// \brief Global compiler context (replaces kState)
+static CompilerContext kContext;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
/// \brief Target architecture.
/// \note This shall never change.
static Int32 kMachine = CompilerKit::AssemblyFactory::kArchAMD64;
@@ -110,6 +165,47 @@ static bool kOnForLoop = false;
static bool kInBraces = false;
static size_t kBracesCount = 0UL;
+/////////////////////////////////////////////////////////////////////////////////////////
+
+// HELPER FUNCTION DECLARATIONS
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+// Scope management
+static void nectar_push_scope(ScopeKind kind, const CompilerKit::STLString& name);
+static void nectar_pop_scope();
+
+// Name mangling
+static std::vector<CompilerKit::STLString> nectar_extract_function_args(
+ const CompilerKit::STLString& text);
+static CompilerKit::STLString nectar_mangle_name(
+ const CompilerKit::STLString& identifier, const std::vector<CompilerKit::STLString>& args = {});
+
+// Stack frame management
+static CompilerKit::STLString nectar_generate_prologue();
+static CompilerKit::STLString nectar_generate_epilogue();
+static Int32 nectar_allocate_stack_variable(const CompilerKit::STLString& var_name, Int32 size = 8);
+
+// Register allocation
+static CompilerKit::STLString nectar_allocate_register(const CompilerKit::STLString& var_name);
+static CompilerKit::STLString nectar_spill_lru_variable();
+static VariableInfo* nectar_find_variable(const CompilerKit::STLString& var_name);
+static CompilerKit::STLString nectar_get_variable_ref(const CompilerKit::STLString& var_name);
+
+// Class/object management
+static void nectar_add_class_member(const CompilerKit::STLString& class_name,
+ const CompilerKit::STLString& member_name, Int32 size);
+static Int32 nectar_get_class_size(const CompilerKit::STLString& class_name);
+static CompilerKit::STLString nectar_generate_constructor_call(
+ const CompilerKit::STLString& class_name, const CompilerKit::STLString& obj_name);
+static CompilerKit::STLString nectar_generate_destructor_call(
+ const CompilerKit::STLString& class_name, const CompilerKit::STLString& obj_name);
+
+// PEF calling convention
+static void nectar_process_function_parameters(const std::vector<CompilerKit::STLString>& args);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
/* \brief C++ compiler backend for the NeKernel C++ driver */
class CompilerFrontendCPlusPlusAMD64 final CK_COMPILER_FRONTEND {
public:
@@ -155,7 +251,7 @@ static std::size_t kNamespaceEmbedLevel{};
/// detail namespaces
const char* CompilerFrontendCPlusPlusAMD64::Language() {
- return "NeKernel CT-C++";
+ return "Nectar C++";
}
static std::uintptr_t kOrigin = kPefBaseOrigin;
@@ -315,16 +411,16 @@ CompilerKit::SyntaxLeafList::SyntaxLeaf CompilerFrontendCPlusPlusAMD64::Compile(
case CompilerKit::KeywordKind::kKeywordKindFunctionStart: {
for (auto& ch : text) {
if (isdigit(ch)) {
- goto dont_accept;
+ goto dont_accept_func;
}
}
- goto accept;
+ goto accept_func;
- dont_accept:
+ dont_accept_func:
break;
- accept:
+ accept_func: {
CompilerKit::STLString symbol_name_fn = text;
size_t indexFnName = 0;
@@ -343,44 +439,70 @@ CompilerKit::SyntaxLeafList::SyntaxLeaf CompilerFrontendCPlusPlusAMD64::Compile(
break;
}
- if (text.ends_with(";") && text.find("return") == CompilerKit::STLString::npos)
- goto lc_write_assembly;
+ // Check if this is a function call (ends with ;)
+ if (text.ends_with(";") && text.find("return") == CompilerKit::STLString::npos) {
+ // Handle function call/jump
+ auto it = std::find_if(
+ kOriginMap.begin(), kOriginMap.end(),
+ [&symbol_name_fn](std::pair<CompilerKit::STLString, std::uintptr_t> pair) -> bool {
+ return symbol_name_fn.find(pair.first) != CompilerKit::STLString::npos;
+ });
- indexFnName = 0;
+ if (it != kOriginMap.end()) {
+ std::stringstream ss;
+ ss << std::hex << it->second;
- for (auto& ch : symbol_name_fn) {
- if (ch == ' ' || ch == '\t') {
- ch = '_';
+ syntax_tree.fUserValue = "jmp " + ss.str() + "\n";
+ kOrigin += 1UL;
}
+ break;
+ }
- ++indexFnName;
+ indexFnName = 0;
+
+ // Extract clean function name
+ CompilerKit::STLString cleanFnName = symbol_name_fn;
+ if (cleanFnName.find("(") != CompilerKit::STLString::npos) {
+ cleanFnName = cleanFnName.substr(0, cleanFnName.find("("));
}
- if (symbol_name_fn.find("(") != CompilerKit::STLString::npos) {
- symbol_name_fn.erase(symbol_name_fn.find("("));
+ // Remove whitespace/tabs
+ while (!cleanFnName.empty() && (cleanFnName.back() == ' ' || cleanFnName.back() == '\t')) {
+ cleanFnName.pop_back();
+ }
+ while (!cleanFnName.empty() &&
+ (cleanFnName.front() == ' ' || cleanFnName.front() == '\t')) {
+ cleanFnName.erase(0, 1);
}
- syntax_tree.fUserValue = "public_segment .code64 __NECTAR_" + symbol_name_fn + "\n";
- ++kFunctionEmbedLevel;
+ // Extract function arguments
+ auto args = nectar_extract_function_args(text);
- kOriginMap.push_back({"__NECTAR_" + symbol_name_fn, kOrigin});
+ // Generate mangled name
+ auto mangled_name = nectar_mangle_name(cleanFnName, args);
- break;
+ // Generate function label and prologue
+ syntax_tree.fUserValue = "public_segment .code64 " + mangled_name + "\n";
+ syntax_tree.fUserValue += nectar_generate_prologue();
- lc_write_assembly:
- auto it = std::find_if(
- kOriginMap.begin(), kOriginMap.end(),
- [&symbol_name_fn](std::pair<CompilerKit::STLString, std::uintptr_t> pair) -> bool {
- return symbol_name_fn == pair.first;
- });
+ // Initialize function-local state
+ kContext.fVariables.clear();
+ kContext.fStackOffset = 0;
+ kContext.fMaxStackUsed = 0;
- if (it != kOriginMap.end()) {
- std::stringstream ss;
- ss << std::hex << it->second;
+ // Process function parameters
+ nectar_process_function_parameters(args);
- syntax_tree.fUserValue = "jmp " + ss.str() + "\n";
- kOrigin += 1UL;
- }
+ // Push function scope
+ nectar_push_scope(ScopeKind::kScopeFunction, cleanFnName);
+
+ ++kFunctionEmbedLevel;
+
+ kOriginMap.push_back({mangled_name, kOrigin});
+ kOrigin += 2UL; // Account for prologue instructions
+
+ break;
+ }
}
case CompilerKit::KeywordKind::kKeywordKindFunctionEnd: {
if (kOnClassScope) --kOnClassScope;
@@ -393,7 +515,14 @@ CompilerKit::SyntaxLeafList::SyntaxLeaf CompilerFrontendCPlusPlusAMD64::Compile(
--kFunctionEmbedLevel;
}
- if (kFunctionEmbedLevel < 1) kRegisterMap.clear();
+ // Pop function scope
+ nectar_pop_scope();
+
+ // Clear function-local state
+ if (kFunctionEmbedLevel < 1) {
+ kRegisterMap.clear();
+ kContext.fVariables.clear();
+ }
break;
}
@@ -659,60 +788,110 @@ CompilerKit::SyntaxLeafList::SyntaxLeaf CompilerFrontendCPlusPlusAMD64::Compile(
subText = subText.erase(subText.find(";"));
size_t indxReg = 0UL;
+ // Trim whitespace
+ while (!subText.empty() && (subText.front() == ' ' || subText.front() == '\t')) {
+ subText.erase(0, 1);
+ }
+ while (!subText.empty() && (subText.back() == ' ' || subText.back() == '\t')) {
+ subText.pop_back();
+ }
+
if (!subText.empty() && subText[0] != '\"' && subText[0] != '\'') {
if (!isdigit(subText[0])) {
for (auto pair : kRegisterMap) {
++indxReg;
- syntax_tree.fUserValue = "mov rax, " + kRegisterList[indxReg - 1] + "\nret\n";
- kOrigin += 1UL;
+ syntax_tree.fUserValue = "mov rax, " + kRegisterList[indxReg - 1] + "\n" +
+ nectar_generate_epilogue() + "ret\n";
+ kOrigin += 3UL; // mov + epilogue (2) + ret
break;
}
} else {
- syntax_tree.fUserValue = "mov rax, " + subText + "\nret\n";
- kOrigin += 1UL;
+ syntax_tree.fUserValue =
+ "mov rax, " + subText + "\n" + nectar_generate_epilogue() + "ret\n";
+ kOrigin += 3UL;
break;
}
- } else {
+ } else if (!subText.empty()) {
syntax_tree.fUserValue = "__NECTAR_LOCAL_RETURN_STRING: db " + subText +
", 0\nmov rcx, __NECTAR_LOCAL_RETURN_STRING\n";
- syntax_tree.fUserValue += "mov rax, rcx\nret\n";
- kOrigin += 1UL;
+ syntax_tree.fUserValue += "mov rax, rcx\n" + nectar_generate_epilogue() + "ret\n";
+ kOrigin += 4UL; // mov rcx + mov rax + epilogue (2) + ret
break;
}
if (syntax_tree.fUserValue.empty()) {
if (subText.find("(") != CompilerKit::STLString::npos) {
- subText.erase(subText.find("("));
+ // Check for namespace resolution
+ if (subText.find("::") != CompilerKit::STLString::npos) {
+ auto colonPos = subText.find("::");
+ auto nsName = subText.substr(0, colonPos);
+ auto funcPart = subText.substr(colonPos + 2);
+
+ // Trim
+ while (!nsName.empty() && (nsName.front() == ' ' || nsName.front() == '\t')) {
+ nsName.erase(0, 1);
+ }
+ while (!nsName.empty() && (nsName.back() == ' ' || nsName.back() == '\t')) {
+ nsName.pop_back();
+ }
- auto it = std::find_if(
- kOriginMap.begin(), kOriginMap.end(),
- [&subText](std::pair<CompilerKit::STLString, std::uintptr_t> pair) -> bool {
- return pair.first.find(subText) != CompilerKit::STLString::npos;
- });
+ // Extract function name
+ auto funcName = funcPart;
+ if (funcName.find("(") != CompilerKit::STLString::npos) {
+ funcName = funcName.substr(0, funcName.find("("));
+ }
- if (it == kOriginMap.end())
- CompilerKit::Detail::print_error("Invalid return value: " + subText, file);
+ // Trim
+ while (!funcName.empty() && (funcName.front() == ' ' || funcName.front() == '\t')) {
+ funcName.erase(0, 1);
+ }
+ while (!funcName.empty() && (funcName.back() == ' ' || funcName.back() == '\t')) {
+ funcName.pop_back();
+ }
- std::stringstream ss;
- ss << it->second;
+ // Generate mangled name
+ nectar_push_scope(ScopeKind::kScopeNamespace, nsName);
+ auto mangled = nectar_mangle_name(funcName);
+ nectar_pop_scope();
- syntax_tree.fUserValue = "jmp " + ss.str() + "\nret\n";
- kOrigin += 1UL;
- break;
+ syntax_tree.fUserValue =
+ "call " + mangled + "\n" + nectar_generate_epilogue() + "ret\n";
+ kOrigin += 3UL;
+ break;
+ } else {
+ // Regular function call
+ subText.erase(subText.find("("));
+
+ auto it = std::find_if(
+ kOriginMap.begin(), kOriginMap.end(),
+ [&subText](std::pair<CompilerKit::STLString, std::uintptr_t> pair) -> bool {
+ return pair.first.find(subText) != CompilerKit::STLString::npos;
+ });
+
+ if (it != kOriginMap.end()) {
+ std::stringstream ss;
+ ss << std::hex << it->second;
+
+ syntax_tree.fUserValue =
+ "call " + ss.str() + "\n" + nectar_generate_epilogue() + "ret\n";
+ kOrigin += 3UL;
+ break;
+ }
+ }
}
}
- syntax_tree.fUserValue = "ret\n";
- kOrigin += 1UL;
+ syntax_tree.fUserValue = nectar_generate_epilogue() + "ret\n";
+ kOrigin += 2UL;
break;
} catch (...) {
- syntax_tree.fUserValue = "ret\n";
- kOrigin += 1UL;
+ syntax_tree.fUserValue = nectar_generate_epilogue() + "ret\n";
+ kOrigin += 2UL;
break;
}
@@ -731,11 +910,502 @@ CompilerKit::SyntaxLeafList::SyntaxLeaf CompilerFrontendCPlusPlusAMD64::Compile(
CompilerKit::SyntaxLeafList::SyntaxLeaf CompilerFrontendCPlusPlusAMD64::CompilePass2(
CompilerKit::STLString& text, const CompilerKit::STLString& file,
CompilerKit::SyntaxLeafList::SyntaxLeaf& syntax_tree) {
+ // Handle namespace entry
+ if (text.find("namespace") != CompilerKit::STLString::npos &&
+ text.find("{") != CompilerKit::STLString::npos) {
+ auto nsPos = text.find("namespace") + strlen("namespace");
+ auto bracePos = text.find("{");
+
+ auto nsName = text.substr(nsPos, bracePos - nsPos);
+
+ // Trim whitespace
+ while (!nsName.empty() && (nsName.front() == ' ' || nsName.front() == '\t')) {
+ nsName.erase(0, 1);
+ }
+ while (!nsName.empty() && (nsName.back() == ' ' || nsName.back() == '\t')) {
+ nsName.pop_back();
+ }
+
+ if (!nsName.empty()) {
+ nectar_push_scope(ScopeKind::kScopeNamespace, nsName);
+ ++kNamespaceEmbedLevel;
+ }
+ }
+
+ // Handle namespace exit
+ if (text.find("}") != CompilerKit::STLString::npos && kNamespaceEmbedLevel > 0) {
+ if (text.find("//") != CompilerKit::STLString::npos &&
+ text.find("namespace") != CompilerKit::STLString::npos) {
+ --kNamespaceEmbedLevel;
+ nectar_pop_scope();
+ }
+ }
+
+ // Handle namespace resolution (::) - only in non-assignment, non-declaration contexts
+ if (text.find("::") != CompilerKit::STLString::npos) {
+ auto colonPos = text.find("::");
+
+ // Extract namespace and function name
+ auto nsName = text.substr(0, colonPos);
+ auto restOfText = text.substr(colonPos + 2);
+
+ // Trim namespace name
+ while (!nsName.empty() && (nsName.front() == ' ' || nsName.front() == '\t')) {
+ nsName.erase(0, 1);
+ }
+ while (!nsName.empty() && (nsName.back() == ' ' || nsName.back() == '\t')) {
+ nsName.pop_back();
+ }
+
+ // Extract function name (everything before '(' if exists)
+ auto funcName = restOfText;
+ if (funcName.find("(") != CompilerKit::STLString::npos) {
+ funcName = funcName.substr(0, funcName.find("("));
+ }
+ if (funcName.find(";") != CompilerKit::STLString::npos) {
+ funcName = funcName.substr(0, funcName.find(";"));
+ }
+
+ // Trim function name
+ while (!funcName.empty() && (funcName.front() == ' ' || funcName.front() == '\t')) {
+ funcName.erase(0, 1);
+ }
+ while (!funcName.empty() && (funcName.back() == ' ' || funcName.back() == '\t')) {
+ funcName.pop_back();
+ }
+
+ // Generate mangled name for the call
+ nectar_push_scope(ScopeKind::kScopeNamespace, nsName);
+ auto mangled = nectar_mangle_name(funcName);
+ nectar_pop_scope();
+
+ // Only generate call if in return context or explicit call
+ if (text.find("return") != CompilerKit::STLString::npos) {
+ // This will be handled by the return handler, just update the text to use mangled name
+ // Store this for use by the return handler
+ } else {
+ syntax_tree.fUserValue += "call " + mangled + "\n";
+ kOrigin += 1UL;
+ }
+ }
+
+ // Handle class entry
+ if ((text.find("class") != CompilerKit::STLString::npos ||
+ text.find("struct") != CompilerKit::STLString::npos) &&
+ text.find("{") != CompilerKit::STLString::npos) {
+ CompilerKit::STLString keyword = text.find("class") != CompilerKit::STLString::npos ? "class" : "struct";
+ auto classPos = text.find(keyword) + keyword.length();
+ auto bracePos = text.find("{");
+
+ auto className = text.substr(classPos, bracePos - classPos);
+
+ // Trim whitespace
+ while (!className.empty() && (className.front() == ' ' || className.front() == '\t')) {
+ className.erase(0, 1);
+ }
+ while (!className.empty() && (className.back() == ' ' || className.back() == '\t')) {
+ className.pop_back();
+ }
+
+ if (!className.empty()) {
+ nectar_push_scope(ScopeKind::kScopeClass, className);
+ ++kOnClassScope;
+ }
+ }
+
+ // Handle class exit
+ if (text.find("}") != CompilerKit::STLString::npos && kOnClassScope > 0) {
+ if (text.find(";") != CompilerKit::STLString::npos) {
+ --kOnClassScope;
+ nectar_pop_scope();
+ }
+ }
+
return syntax_tree;
}
/////////////////////////////////////////////////////////////////////////////////////////
+// HELPER FUNCTION IMPLEMENTATIONS
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/// \brief Push a new scope onto the scope stack
+static void nectar_push_scope(ScopeKind kind, const CompilerKit::STLString& name) {
+ CompilerScope scope;
+ scope.fKind = kind;
+ scope.fName = name;
+
+ // Build mangled prefix based on current scope stack
+ for (const auto& s : kContext.fScopeStack) {
+ if (s.fKind == ScopeKind::kScopeNamespace) {
+ scope.fMangledPrefix += "N_" + s.fName + "_";
+ } else if (s.fKind == ScopeKind::kScopeClass) {
+ scope.fMangledPrefix += "C_" + s.fName + "_";
+ }
+ }
+
+ kContext.fScopeStack.push_back(scope);
+}
+
+/// \brief Pop the current scope from the scope stack
+static void nectar_pop_scope() {
+ if (!kContext.fScopeStack.empty()) {
+ kContext.fScopeStack.pop_back();
+ }
+}
+
+/// \brief Extract function arguments from function declaration
+static std::vector<CompilerKit::STLString> nectar_extract_function_args(
+ const CompilerKit::STLString& text) {
+ std::vector<CompilerKit::STLString> args;
+
+ auto openParen = text.find("(");
+ auto closeParen = text.find(")");
+
+ if (openParen == CompilerKit::STLString::npos || closeParen == CompilerKit::STLString::npos ||
+ closeParen <= openParen) {
+ return args;
+ }
+
+ auto argsText = text.substr(openParen + 1, closeParen - openParen - 1);
+
+ // Trim whitespace
+ while (!argsText.empty() && (argsText.front() == ' ' || argsText.front() == '\t')) {
+ argsText.erase(0, 1);
+ }
+ while (!argsText.empty() && (argsText.back() == ' ' || argsText.back() == '\t')) {
+ argsText.pop_back();
+ }
+
+ if (argsText.empty() || argsText == "void") {
+ return args;
+ }
+
+ // Simple comma-separated parsing
+ std::size_t pos = 0;
+ while (pos < argsText.size()) {
+ auto commaPos = argsText.find(",", pos);
+ if (commaPos == CompilerKit::STLString::npos) {
+ commaPos = argsText.size();
+ }
+
+ auto arg = argsText.substr(pos, commaPos - pos);
+
+ // Extract type name (skip variable name)
+ std::size_t lastSpace = arg.rfind(' ');
+ if (lastSpace != CompilerKit::STLString::npos) {
+ arg = arg.substr(0, lastSpace);
+ }
+
+ // Trim
+ while (!arg.empty() && (arg.front() == ' ' || arg.front() == '\t')) {
+ arg.erase(0, 1);
+ }
+ while (!arg.empty() && (arg.back() == ' ' || arg.back() == '\t')) {
+ arg.pop_back();
+ }
+
+ if (!arg.empty()) {
+ args.push_back(arg);
+ }
+
+ pos = commaPos + 1;
+ }
+
+ return args;
+}
+
+/// \brief Mangle a function or method name according to Nectar mangling scheme
+static CompilerKit::STLString nectar_mangle_name(const CompilerKit::STLString& identifier,
+ const std::vector<CompilerKit::STLString>& args) {
+ CompilerKit::STLString mangled = "__NECTAR_";
+
+ // Add scope chain
+ for (const auto& scope : kContext.fScopeStack) {
+ if (scope.fKind == ScopeKind::kScopeNamespace) {
+ mangled += "N_" + scope.fName + "_";
+ } else if (scope.fKind == ScopeKind::kScopeClass) {
+ mangled += "C_" + scope.fName + "_";
+ }
+ }
+
+ // Check if it's a constructor or destructor
+ if (!kContext.fScopeStack.empty() &&
+ kContext.fScopeStack.back().fKind == ScopeKind::kScopeClass) {
+ if (identifier == kContext.fScopeStack.back().fName) {
+ mangled += "CTOR";
+ return mangled;
+ } else if (identifier == "~" + kContext.fScopeStack.back().fName) {
+ mangled += "DTOR";
+ return mangled;
+ }
+ }
+
+ // Check if we're in a class scope for member functions
+ bool inClass = false;
+ for (const auto& scope : kContext.fScopeStack) {
+ if (scope.fKind == ScopeKind::kScopeClass) {
+ inClass = true;
+ break;
+ }
+ }
+
+ if (inClass) {
+ mangled += "M_" + identifier;
+ } else {
+ mangled += "F_" + identifier;
+ }
+
+ // Add argument types if provided
+ if (!args.empty()) {
+ mangled += "_A" + std::to_string(args.size());
+ for (const auto& arg : args) {
+ mangled += "_" + arg;
+ }
+ }
+
+ return mangled;
+}
+
+/// \brief Generate function prologue
+static CompilerKit::STLString nectar_generate_prologue() {
+ return "push rbp\nmov rbp, rsp\n";
+}
+
+/// \brief Generate function epilogue
+static CompilerKit::STLString nectar_generate_epilogue() {
+ return "mov rsp, rbp\npop rbp\n";
+}
+
+/// \brief Allocate a variable on the stack
+static Int32 nectar_allocate_stack_variable(const CompilerKit::STLString& var_name, Int32 size) {
+ kContext.fStackOffset -= size;
+ kContext.fMaxStackUsed = std::min(kContext.fStackOffset, kContext.fMaxStackUsed);
+
+ VariableInfo varInfo;
+ varInfo.fName = var_name;
+ varInfo.fLocation = VarLocation::kStack;
+ varInfo.fStackOffset = kContext.fStackOffset;
+ varInfo.fSize = size;
+ varInfo.fLastUsed = kContext.fInstructionCounter;
+ kContext.fVariables.push_back(varInfo);
+
+ return kContext.fStackOffset;
+}
+
+/// \brief Find a variable by name
+static VariableInfo* nectar_find_variable(const CompilerKit::STLString& var_name) {
+ for (auto& var : kContext.fVariables) {
+ if (var.fName == var_name) {
+ return &var;
+ }
+ }
+ return nullptr;
+}
+
+/// \brief Get variable reference (register or stack location)
+static CompilerKit::STLString nectar_get_variable_ref(const CompilerKit::STLString& var_name) {
+ auto* varInfo = nectar_find_variable(var_name);
+ if (!varInfo) {
+ return "";
+ }
+
+ varInfo->fLastUsed = kContext.fInstructionCounter;
+
+ if (varInfo->fLocation == VarLocation::kRegister) {
+ return varInfo->fRegister;
+ } else {
+ // Stack or spilled
+ return "qword [rbp" + std::to_string(varInfo->fStackOffset) + "]";
+ }
+}
+
+/// \brief Allocate a register for a variable
+static CompilerKit::STLString nectar_allocate_register(const CompilerKit::STLString& var_name) {
+ // Check if variable already has a register
+ auto* existing = nectar_find_variable(var_name);
+ if (existing && existing->fLocation == VarLocation::kRegister) {
+ return existing->fRegister;
+ }
+
+ // Find a free register
+ for (const auto& reg : kRegisterList) {
+ bool inUse = false;
+ for (const auto& var : kContext.fVariables) {
+ if (var.fLocation == VarLocation::kRegister && var.fRegister == reg) {
+ inUse = true;
+ break;
+ }
+ }
+
+ if (!inUse) {
+ // Allocate this register
+ if (existing) {
+ existing->fLocation = VarLocation::kRegister;
+ existing->fRegister = reg;
+ existing->fLastUsed = kContext.fInstructionCounter;
+ } else {
+ VariableInfo varInfo;
+ varInfo.fName = var_name;
+ varInfo.fLocation = VarLocation::kRegister;
+ varInfo.fRegister = reg;
+ varInfo.fLastUsed = kContext.fInstructionCounter;
+ kContext.fVariables.push_back(varInfo);
+ }
+ return reg;
+ }
+ }
+
+ // No free register
+ return "";
+}
+
+/// \brief Spill the least recently used variable to stack
+static CompilerKit::STLString nectar_spill_lru_variable() {
+ CompilerKit::STLString spillCode;
+
+ // Find LRU variable in register (that's not a parameter)
+ VariableInfo* lruVar = nullptr;
+ UInt32 minLastUsed = UINT32_MAX;
+
+ for (auto& var : kContext.fVariables) {
+ if (var.fLocation == VarLocation::kRegister && !var.fIsParameter &&
+ var.fLastUsed < minLastUsed) {
+ lruVar = &var;
+ minLastUsed = var.fLastUsed;
+ }
+ }
+
+ if (!lruVar) {
+ return ""; // No variable to spill
+ }
+
+ // Allocate stack space
+ kContext.fStackOffset -= lruVar->fSize;
+ kContext.fMaxStackUsed = std::min(kContext.fStackOffset, kContext.fMaxStackUsed);
+
+ // Generate spill code
+ spillCode =
+ "mov qword [rbp" + std::to_string(kContext.fStackOffset) + "], " + lruVar->fRegister + "\n";
+
+ // Update variable info
+ lruVar->fLocation = VarLocation::kStackSpill;
+ lruVar->fStackOffset = kContext.fStackOffset;
+ auto spilledReg = lruVar->fRegister;
+ lruVar->fRegister = "";
+
+ return spillCode;
+}
+
+/// \brief Add a class member to the struct map
+static void nectar_add_class_member(const CompilerKit::STLString& class_name,
+ const CompilerKit::STLString& member_name, Int32 size) {
+ // Find or create struct map entry
+ CompilerStructMap* structMap = nullptr;
+ for (auto& sm : kContext.fStructMapVector) {
+ if (sm.fName == class_name) {
+ structMap = &sm;
+ break;
+ }
+ }
+
+ if (!structMap) {
+ CompilerStructMap newMap;
+ newMap.fName = class_name;
+ kContext.fStructMapVector.push_back(newMap);
+ structMap = &kContext.fStructMapVector.back();
+ }
+
+ // Calculate offset
+ UInt32 offset = 0;
+ if (!structMap->fOffsets.empty()) {
+ offset = structMap->fOffsets.back().first + 8; // Assume 8-byte members for now
+ }
+
+ structMap->fOffsets.emplace_back(offset, member_name);
+}
+
+/// \brief Get the size of a class
+static Int32 nectar_get_class_size(const CompilerKit::STLString& class_name) {
+ for (const auto& sm : kContext.fStructMapVector) {
+ if (sm.fName == class_name) {
+ if (sm.fOffsets.empty()) {
+ return 0;
+ }
+ return sm.fOffsets.back().first + 8; // Last offset + size
+ }
+ }
+ return 0;
+}
+
+/// \brief Generate constructor call
+static CompilerKit::STLString nectar_generate_constructor_call(
+ const CompilerKit::STLString& class_name, const CompilerKit::STLString& obj_name) {
+ auto size = nectar_get_class_size(class_name);
+ auto offset = nectar_allocate_stack_variable(obj_name, size == 0 ? 8 : size);
+
+ nectar_push_scope(ScopeKind::kScopeClass, class_name);
+ auto ctor_mangled = nectar_mangle_name(class_name);
+ nectar_pop_scope();
+
+ CompilerKit::STLString code;
+ code += "lea r8, [rbp" + std::to_string(offset) + "]\n";
+ code += "call " + ctor_mangled + "\n";
+ return code;
+}
+
+/// \brief Generate destructor call
+static CompilerKit::STLString nectar_generate_destructor_call(
+ const CompilerKit::STLString& class_name, const CompilerKit::STLString& obj_name) {
+ auto* varInfo = nectar_find_variable(obj_name);
+ if (!varInfo) {
+ return "";
+ }
+
+ nectar_push_scope(ScopeKind::kScopeClass, class_name);
+ auto dtor_mangled = nectar_mangle_name("~" + class_name);
+ nectar_pop_scope();
+
+ CompilerKit::STLString code;
+ if (varInfo->fLocation == VarLocation::kStack || varInfo->fLocation == VarLocation::kStackSpill) {
+ code += "lea r8, [rbp" + std::to_string(varInfo->fStackOffset) + "]\n";
+ } else {
+ code += "mov r8, " + varInfo->fRegister + "\n";
+ }
+ code += "call " + dtor_mangled + "\n";
+ return code;
+}
+
+/// \brief Process function parameters per PEF calling convention
+static void nectar_process_function_parameters(const std::vector<CompilerKit::STLString>& args) {
+ for (size_t i = 0; i < args.size() && i < 8; ++i) {
+ VariableInfo param;
+ param.fName = "arg" + std::to_string(i);
+ param.fLocation = VarLocation::kRegister;
+ param.fRegister = kRegisterConventionCallList[i];
+ param.fIsParameter = true;
+ param.fTypeName = args[i];
+ param.fLastUsed = kContext.fInstructionCounter;
+ kContext.fVariables.push_back(param);
+ }
+
+ // Args beyond r15 go on stack
+ for (size_t i = 8; i < args.size(); ++i) {
+ Int32 offset = 16 + (i - 8) * 8;
+ VariableInfo param;
+ param.fName = "arg" + std::to_string(i);
+ param.fLocation = VarLocation::kStack;
+ param.fStackOffset = offset; // Positive (before rbp)
+ param.fIsParameter = true;
+ param.fTypeName = args[i];
+ param.fLastUsed = kContext.fInstructionCounter;
+ kContext.fVariables.push_back(param);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
/**
* @brief C++ assembler class.
*/
@@ -755,7 +1425,7 @@ class AssemblyCPlusPlusInterfaceAMD64 final CK_ASSEMBLY_INTERFACE {
if (kFrontend == nullptr) return EXIT_FAILURE;
CompilerKit::STLString dest = src;
- dest += ".pp.masm";
+ dest += ".masm";
std::ofstream out_fp(dest);
std::ifstream src_fp = std::ifstream(src);
@@ -780,7 +1450,7 @@ class AssemblyCPlusPlusInterfaceAMD64 final CK_ASSEMBLY_INTERFACE {
/////////////////////////////////////////////////////////////////////////////////////////
-#define kExtListCxx {".cpp", ".cc", ".cc", ".c++", ".cp", ".kpp", ".k++"}
+#define kExtListCxx {".cpp", ".cc", ".cc", ".c++", ".cp", ".ipp", ".i++"}
NECTAR_MODULE(CompilerCPlusPlusAMD64) {
bool skip = false;
diff --git a/test/test_01_codegen/codegen.test.cc b/test/test_01_codegen/codegen.test.cc
index 37e201e..3cee6fb 100644
--- a/test/test_01_codegen/codegen.test.cc
+++ b/test/test_01_codegen/codegen.test.cc
@@ -8,11 +8,6 @@
#include <gtest/gtest.h>
-TEST(CodegenTest, BasicCodegenTestAssemble) {
- auto expr = std::system("asm -asm-x64 test_samples/sample.asm");
- EXPECT_TRUE(expr == 0) << "ASM Driver did not compile the easy ASM unit.";
-}
-
TEST(CodegenTest, BasicCodegenTestGrep) {
// Compile C++ source to assembly
auto compile_result = std::system("pef-amd64-cxxdrv ../test_samples/sample.cc > /dev/null 2>&1");
@@ -30,3 +25,8 @@ TEST(CodegenTest, BasicCodegenTestGrep) {
auto grep_bits64 = std::system("grep -q '%bits 64' ../test_samples/sample.cc.pp.masm");
EXPECT_TRUE(grep_bits64 == 0) << "Generated assembly missing 64-bit mode directive";
}
+
+TEST(CodegenTest, BasicCodegenTestAssemble) {
+ auto expr = std::system("asm -asm-x64 test_samples/sample.asm");
+ EXPECT_TRUE(expr == 0) << "ASM Driver did not compile the easy ASM unit.";
+}
diff --git a/test/test_samples/class_ctor_dtor.cc b/test/test_samples/class_ctor_dtor.cc
deleted file mode 100644
index 728a62c..0000000
--- a/test/test_samples/class_ctor_dtor.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-#define main __ImageStart
-
-class MyClass {
- MyClass() {
- // Constructor
- }
-
- ~MyClass() {
- // Destructor
- }
-};
-
-int main() {
- return 0;
-}
diff --git a/test/test_samples/class_methods.cc b/test/test_samples/class_methods.cc
deleted file mode 100644
index ce9a73a..0000000
--- a/test/test_samples/class_methods.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-#define main __ImageStart
-
-class Counter {
- int count;
-
- void increment() { count += 1; }
-
- int getCount() { return count; }
-};
-
-int main() {
- return 0;
-}
diff --git a/test/test_samples/class_simple.cc b/test/test_samples/class_simple.cc
deleted file mode 100644
index 604922d..0000000
--- a/test/test_samples/class_simple.cc
+++ /dev/null
@@ -1,10 +0,0 @@
-#define main __ImageStart
-
-class Point {
- int x;
- int y;
-};
-
-int main() {
- return 0;
-}
diff --git a/test/test_samples/namespace_basic.cc b/test/test_samples/namespace_basic.cc
deleted file mode 100644
index 357304b..0000000
--- a/test/test_samples/namespace_basic.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Math {
-int add() {
- return 5;
-}
-} // namespace Math
-
-int main() {
- return Math::add();
-}
diff --git a/test/test_samples/sample.cc b/test/test_samples/sample.cc
index 4cce7f6..6a6bc44 100644
--- a/test/test_samples/sample.cc
+++ b/test/test_samples/sample.cc
@@ -1,3 +1,24 @@
-int main() {
+class ostream
+{
+ ostream()
+ {
+ return void;
+ }
+
+ ~ostream()
+ {
+ return void;
+ }
+
+ ostream& noop(const ostream in)
+ {
+ return *this;
+ }
+};
+
+int main()
+{
+ void* f = new ostream();
+ f->noop(f);
return 0;
}