From bbcebf7c17aa3ff9a364bd46f354842ef1d6c9ea Mon Sep 17 00:00:00 2001 From: Nathan Braswell Date: Sat, 30 May 2015 04:43:01 -0400 Subject: [PATCH] I'm pretty sure I missed cases and introduced bugs with the new CGenerator triplit system, but I finally got all normal tests passing, and it's almost 5 in the morning, so I'm calling it a night. We have destructors and copy constructors now\! I need to work out copy assignment, but again, another day --- CMakeLists.txt | 7 +- include/CCodeTriple.h | 25 ++ include/CGenerator.h | 11 +- include/util.h | 15 + src/ASTTransformation.cpp | 12 +- src/CCodeTriple.cpp | 45 +++ src/CGenerator.cpp | 258 +++++++++++++----- stdlib/mem.krak | 4 +- stdlib/vector.krak | 10 + ...structor_copy_constructor.expected_results | 47 ++++ tests/test_destructor_copy_constructor.krak | 105 +++++++ tests/test_vectorTest.expected_results | 3 + tests/test_vectorTest.krak | 6 + 13 files changed, 482 insertions(+), 66 deletions(-) create mode 100644 include/CCodeTriple.h create mode 100644 src/CCodeTriple.cpp create mode 100644 tests/test_destructor_copy_constructor.expected_results create mode 100644 tests/test_destructor_copy_constructor.krak diff --git a/CMakeLists.txt b/CMakeLists.txt index e429b9a..539cbad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,12 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set( MY_INCLUDES ${PROJECT_SOURCE_DIR}/include) -set( MY_SOURCES main.cpp src/Parser.cpp src/GraphStructuredStack.cpp src/RNGLRParser.cpp src/ParseAction.cpp src/ParseRule.cpp src/Symbol.cpp src/StringReader.cpp src/State.cpp src/util.cpp src/Lexer.cpp src/RegEx.cpp src/RegExState.cpp src/Table.cpp src/ASTData.cpp src/ASTTransformation.cpp src/CGenerator.cpp src/Type.cpp src/Importer.cpp src/Tester.cpp ) +set( MY_SOURCES main.cpp src/Parser.cpp src/GraphStructuredStack.cpp + src/RNGLRParser.cpp src/ParseAction.cpp src/ParseRule.cpp src/Symbol.cpp + src/StringReader.cpp src/State.cpp src/util.cpp src/Lexer.cpp + src/RegEx.cpp src/RegExState.cpp src/Table.cpp src/ASTData.cpp + src/ASTTransformation.cpp src/CGenerator.cpp src/Type.cpp src/Importer.cpp + src/Tester.cpp src/CCodeTriple.cpp) add_custom_target(STDLibCopy ALL) add_custom_command(TARGET STDLibCopy POST_BUILD diff --git a/include/CCodeTriple.h b/include/CCodeTriple.h new file mode 100644 index 0000000..95f46f1 --- /dev/null +++ b/include/CCodeTriple.h @@ -0,0 +1,25 @@ +#ifndef CCODETRIPLE_H +#define CCODETRIPLE_H + +#include +#include +#include "util.h" + +class CCodeTriple { + public: + CCodeTriple(std::string pre, std::string val, std::string post); + CCodeTriple(std::string val); + CCodeTriple(const char* val); + CCodeTriple(); + ~CCodeTriple(); + std::string oneString(); + CCodeTriple & operator=(const CCodeTriple &rhs); + CCodeTriple & operator+=(const CCodeTriple &rhs); + + std::string preValue; + std::string value; + std::string postValue; + private: +}; +CCodeTriple operator+(const CCodeTriple &a, const CCodeTriple &b); +#endif //CCODETRIPLE_H diff --git a/include/CGenerator.h b/include/CGenerator.h index 5d5645e..6fbfbc6 100644 --- a/include/CGenerator.h +++ b/include/CGenerator.h @@ -8,6 +8,7 @@ #include #include +#include "CCodeTriple.h" #include "NodeTree.h" #include "ASTData.h" #include "Type.h" @@ -26,7 +27,7 @@ class CGenerator { bool isUnderTranslationUnit(NodeTree* from, NodeTree* typeDefinition); NodeTree* highestScope(NodeTree* node); std::pair generateTranslationUnit(std::string name, std::map*> ASTs); - std::string generate(NodeTree* from, NodeTree* enclosingObject = NULL, bool justFuncName = false); + CCodeTriple generate(NodeTree* from, NodeTree* enclosingObject = NULL, bool justFuncName = false); std::string generateAliasChains(std::map*> ASTs, NodeTree* definition); std::string ValueTypeToCType(Type *type, std::string); std::string ValueTypeToCTypeDecoration(Type *type); @@ -35,12 +36,20 @@ class CGenerator { static std::string scopePrefix(NodeTree* from); std::string generateObjectMethod(NodeTree* enclosingObject, NodeTree* from, std::string *functionPrototype); NodeTree* getMethodsObjectType(NodeTree* scope, std::string functionName); + NodeTree* getMethod(Type* type, std::string method); + bool methodExists(Type* type, std::string method); + std::string generateMethodIfExists(Type* type, std::string method, std::string parameter); + std::string emitDestructors(std::vector*> possibleDeclarations, NodeTree* enclosingObject); std::string tabs(); + std::string getID(); int tabLevel; + int id; std::string generatorString; std::string linkerString; std::string functionTypedefString; + std::vector*>> distructDoubleStack; + std::stack loopDistructStackDepth; std::vector*>> deferDoubleStack; std::stack loopDeferStackDepth; private: diff --git a/include/util.h b/include/util.h index 8dca24f..ef84e60 100644 --- a/include/util.h +++ b/include/util.h @@ -31,6 +31,21 @@ bool contains(std::vector vec, T item) { return false; } +template +std::vector flatten(std::vector> vec) { + std::vector flat; + for (auto i : vec) + flat.insert(flat.end(), i.begin(), i.end()); + return flat; +} + +template +std::vector reverse(std::vector vec) { + std::vector flat; + flat.insert(flat.end(), vec.rbegin(), vec.rend()); + return flat; +} + template std::vector slice(std::vector vec, int begin, int end, int step = 1) { std::vector toReturn; diff --git a/src/ASTTransformation.cpp b/src/ASTTransformation.cpp index 8cd53e0..1a46072 100644 --- a/src/ASTTransformation.cpp +++ b/src/ASTTransformation.cpp @@ -1229,7 +1229,17 @@ std::vector*> ASTTransformation::scopeLookup(NodeTree auto LLElementIterator = languageLevelReservedWords.find(lookup); if (LLElementIterator != languageLevelReservedWords.end()) { std::cout << "found it at language level as reserved word." << std::endl; - return LLElementIterator->second; + NodeTree* identifier = LLElementIterator->second[0]; + if (lookup == "this") { + identifier = new NodeTree("identifier", identifier->getData()); + // if we're looking for this, traverse up until we find the declaration of this object and assign it's type to this + NodeTree* trans; + for (trans = scope; trans->getDataRef()->type != type_def; trans = trans->getDataRef()->scope["~enclosing_scope"][0]); + identifier->getDataRef()->valueType = trans->getDataRef()->valueType->clone(); + identifier->getDataRef()->valueType->increaseIndirection(); + } + std::vector*> thingy; thingy.push_back(identifier); + return thingy; } std::vector*> matches; // First, we check for scope operator (::) but only if occurs before a "<" as this would signal the beginning of a template instatiation inside type diff --git a/src/CCodeTriple.cpp b/src/CCodeTriple.cpp new file mode 100644 index 0000000..313d882 --- /dev/null +++ b/src/CCodeTriple.cpp @@ -0,0 +1,45 @@ +#include "CCodeTriple.h" + +CCodeTriple::CCodeTriple(std::string pre, std::string val, std::string post) { + preValue = pre; + value = val; + postValue = post; +} + +CCodeTriple::CCodeTriple(std::string val) { + value = val; +} +CCodeTriple::CCodeTriple(const char* val) { + value = val; +} + +CCodeTriple::CCodeTriple() { +} + +CCodeTriple::~CCodeTriple() { +} + +std::string CCodeTriple::oneString() { + return preValue + value + postValue; +} + +CCodeTriple & CCodeTriple::operator=(const CCodeTriple &rhs) { + preValue = rhs.preValue; + value = rhs.value; + postValue = rhs.postValue; + return *this; +} + +CCodeTriple & CCodeTriple::operator+=(const CCodeTriple &rhs) { + preValue += rhs.preValue; + //preValue = rhs.preValue + preValue; + value += rhs.value; + postValue = rhs.postValue + postValue; + return *this; +} + +CCodeTriple operator+(const CCodeTriple &a, const CCodeTriple &b) { + return CCodeTriple(a.preValue + b.preValue, a.value + b.value, b.postValue + a.postValue); + //return CCodeTriple(b.preValue + a.preValue, a.value + b.value, b.postValue + a.postValue); +} + diff --git a/src/CGenerator.cpp b/src/CGenerator.cpp index 141eb83..7df5e94 100644 --- a/src/CGenerator.cpp +++ b/src/CGenerator.cpp @@ -2,6 +2,7 @@ CGenerator::CGenerator() : generatorString("__C__") { tabLevel = 0; + id = 0; } CGenerator::~CGenerator() { } @@ -33,6 +34,7 @@ void CGenerator::generateCompSet(std::map*> ASTs, outputBuild.open(outputName + "/" + split(outputName, '/').back() + ".sh"); outputBuild << buildString; outputBuild.close(); + std::cout << "DEFER DOUBLE STACK " << deferDoubleStack.size() << std::endl; } std::string CGenerator::tabs() { @@ -42,6 +44,10 @@ std::string CGenerator::tabs() { return returnTabs; } +std::string CGenerator::getID() { + return intToString(id++); +} + std::string CGenerator::generateClassStruct(NodeTree* from) { auto data = from->getData(); auto children = from->getChildren(); @@ -50,7 +56,7 @@ std::string CGenerator::generateClassStruct(NodeTree* from) { for (int i = 0; i < children.size(); i++) { std::cout << children[i]->getName() << std::endl; if (children[i]->getName() != "function") - objectString += tabs() + generate(children[i], nullptr) + "\n"; + objectString += tabs() + generate(children[i], nullptr).oneString() + "\n"; } tabLevel--; objectString += "};"; @@ -131,8 +137,6 @@ std::pair CGenerator::generateTranslationUnit(std::str // it is emitted in the h file right before functionPrototypes - // And get the correct order for emiting classes, but not if they're not in our file, then they will get included - // Note that this is not sufsticated enough for some multiple file mutually recursive types, but I want to get this simple version working first Poset*> typedefPoset; for (auto trans : ASTs) { auto children = trans.second->getChildren(); @@ -168,7 +172,7 @@ std::pair CGenerator::generateTranslationUnit(std::str // First go through and emit all the passthroughs, etc for (auto i : trans.second->getChildren()) { if (i->getDataRef()->type == if_comp) - topLevelCPassthrough += generate(i, nullptr); + topLevelCPassthrough += generate(i, nullptr).oneString(); } for (auto i = trans.second->getDataRef()->scope.begin(); i != trans.second->getDataRef()->scope.end(); i++) { @@ -191,7 +195,7 @@ std::pair CGenerator::generateTranslationUnit(std::str for (int j = 0; j < decChildren.size()-1; j++) { if (j > 0) parameters += ", "; - parameters += ValueTypeToCType(decChildren[j]->getData().valueType, generate(decChildren[j], nullptr)); + parameters += ValueTypeToCType(decChildren[j]->getData().valueType, generate(decChildren[j], nullptr).oneString()); nameDecoration += "_" + ValueTypeToCTypeDecoration(decChildren[j]->getData().valueType); } functionPrototypes += "\n" + ValueTypeToCType(declarationData.valueType->returnType, ((declarationData.symbol.getName() == "main") ? "" : scopePrefix(declaration)) + @@ -200,7 +204,7 @@ std::pair CGenerator::generateTranslationUnit(std::str // generate function std::cout << "Generating " << scopePrefix(declaration) + CifyName(declarationData.symbol.getName()) << std::endl; - functionDefinitions += generate(declaration, nullptr); + functionDefinitions += generate(declaration, nullptr).oneString(); } } break; @@ -252,10 +256,11 @@ std::pair CGenerator::generateTranslationUnit(std::str } //The enclosing object is for when we're generating the inside of object methods. They allow us to check scope lookups against the object we're in -std::string CGenerator::generate(NodeTree* from, NodeTree* enclosingObject, bool justFuncName) { +CCodeTriple CGenerator::generate(NodeTree* from, NodeTree* enclosingObject, bool justFuncName) { ASTData data = from->getData(); std::vector*> children = from->getChildren(); - std::string output; + //std::string output; + CCodeTriple output; switch (data.type) { case translation_unit: { @@ -268,15 +273,13 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc //Do nothing break; case import: - return "/* never reached import? */\n"; - //return "include \"" + data.symbol.getName() + ".h\" //woo importing!\n"; - //return "#include <" + data.symbol.getName() + ">\n"; + return CCodeTriple("/* never reached import? */\n"); case identifier: { //but first, if we're this, we should just emit. (assuming enclosing object) (note that technically this would fall through, but for errors) if (data.symbol.getName() == "this") { if (enclosingObject) - return "this"; + return CCodeTriple("this"); else std::cerr << "Error: this used in non-object scope" << std::endl; } @@ -291,35 +294,54 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc { if (data.valueType->baseType == template_type) return "/* template function: " + data.symbol.getName() + " */"; + + // we push on a new vector to hold parameters that might need a destructor call + distructDoubleStack.push_back(std::vector*>()); + std::string nameDecoration, parameters; for (int j = 0; j < children.size()-1; j++) { if (j > 0) parameters += ", "; - parameters += ValueTypeToCType(children[j]->getData().valueType, generate(children[j], enclosingObject, justFuncName)); + parameters += ValueTypeToCType(children[j]->getData().valueType, generate(children[j], enclosingObject, justFuncName).oneString()); nameDecoration += "_" + ValueTypeToCTypeDecoration(children[j]->getData().valueType); + // add parameters to distructDoubleStack so that their destructors will be called at return (if they exist) + distructDoubleStack.back().push_back(children[j]); } // this is for using functions as values - if (justFuncName) - return ((data.symbol.getName() == "main") ? "" : scopePrefix(from)) + CifyName(data.symbol.getName() + nameDecoration); + if (justFuncName) { + output = ((data.symbol.getName() == "main") ? "" : scopePrefix(from)) + CifyName(data.symbol.getName() + nameDecoration); + } else { // Note that we always wrap out child in {}, as we now allow one statement functions without a codeblock - output += "\n" + ValueTypeToCType(data.valueType->returnType, ((data.symbol.getName() == "main") ? "" : scopePrefix(from)) + - CifyName(data.symbol.getName() + nameDecoration)) + "(" + parameters + ") {\n" + generate(children[children.size()-1], enclosingObject, justFuncName) + "}\n"; + output = "\n" + ValueTypeToCType(data.valueType->returnType, ((data.symbol.getName() == "main") ? "" : scopePrefix(from)) + + CifyName(data.symbol.getName() + nameDecoration)) + "(" + parameters + ") {\n" + generate(children[children.size()-1], enclosingObject, justFuncName).oneString(); + output += emitDestructors(reverse(distructDoubleStack.back()), enclosingObject); + output += "}\n"; + } + + distructDoubleStack.pop_back(); return output; } case code_block: { output += "{\n"; tabLevel++; + + // we push on a new vector to hold parameters that might need a destructor call + distructDoubleStack.push_back(std::vector*>()); // we push on a new vector to hold deferred statements deferDoubleStack.push_back(std::vector*>()); for (int i = 0; i < children.size(); i++) - output += generate(children[i], enclosingObject, justFuncName); + output += generate(children[i], enclosingObject, justFuncName).oneString(); // we pop off the vector and go through them in reverse emitting them for (auto iter = deferDoubleStack.back().rbegin(); iter != deferDoubleStack.back().rend(); iter++) - output += generate(*iter, enclosingObject, justFuncName); + output += generate(*iter, enclosingObject, justFuncName).oneString(); deferDoubleStack.pop_back(); + output += emitDestructors(reverse(distructDoubleStack.back()), enclosingObject); + distructDoubleStack.pop_back(); + tabLevel--; output += tabs() + "}"; + return output; } case expression: @@ -327,87 +349,147 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc case boolean_expression: output += " " + data.symbol.getName() + " "; case statement: - return tabs() + generate(children[0], enclosingObject, justFuncName) + ";\n"; + { + CCodeTriple stat = generate(children[0], enclosingObject, justFuncName); + return tabs() + stat.preValue + stat.value + ";\n" + stat.postValue ; + } case if_statement: - output += "if (" + generate(children[0], enclosingObject, true) + ")\n\t"; + output += "if (" + generate(children[0], enclosingObject, true).oneString() + ")\n\t"; // We have to see if the then statement is a regular single statement or a block. // If it's a block, because it's also a statement a semicolon will be emitted even though // we don't want it to be, as if (a) {b}; else {c}; is not legal C, but if (a) {b} else {c}; is. if (children[1]->getChildren()[0]->getDataRef()->type == code_block) { std::cout << "Then statement is a block, emitting the block not the statement so no trailing semicolon" << std::endl; - output += generate(children[1]->getChildren()[0], enclosingObject, justFuncName); + output += generate(children[1]->getChildren()[0], enclosingObject, justFuncName).oneString(); } else { // ALSO we always emit blocks now, to handle cases like defer when several statements need to be // run in C even though it is a single Kraken statement std::cout << "Then statement is a simple statement, regular emitting the statement so trailing semicolon" << std::endl; - output += "{ " + generate(children[1], enclosingObject, justFuncName) + " }"; + output += "{ " + generate(children[1], enclosingObject, justFuncName).oneString() + " }"; } // Always emit blocks here too if (children.size() > 2) - output += " else { " + generate(children[2], enclosingObject, justFuncName) + " }"; + output += " else { " + generate(children[2], enclosingObject, justFuncName).oneString() + " }"; return output; case while_loop: + // we push on a new vector to hold while stuff that might need a destructor call + loopDistructStackDepth.push(distructDoubleStack.size()); + distructDoubleStack.push_back(std::vector*>()); // keep track of the current size of the deferDoubleStack so that statements that // break or continue inside this loop can correctly emit all of the defers through // all of the inbetween scopes loopDeferStackDepth.push(deferDoubleStack.size()); - output += "while (" + generate(children[0], enclosingObject, true) + ")\n\t" + generate(children[1], enclosingObject, justFuncName); + output += "while (" + generate(children[0], enclosingObject, true).oneString() + ") {\n\t"; + output += generate(children[1], enclosingObject, justFuncName).oneString(); + output += emitDestructors(reverse(distructDoubleStack.back()),enclosingObject); + output += + "}"; + + distructDoubleStack.pop_back(); + loopDistructStackDepth.pop(); // and pop it off again loopDeferStackDepth.pop(); return output; case for_loop: + // we push on a new vector to hold for stuff that might need a destructor call + loopDistructStackDepth.push(distructDoubleStack.size()); + distructDoubleStack.push_back(std::vector*>()); // keep track of the current size of the deferDoubleStack so that statements that // break or continue inside this loop can correctly emit all of the defers through // all of the inbetween scopes loopDeferStackDepth.push(deferDoubleStack.size()); //The strSlice's are there to get ride of an unwanted return and an unwanted semicolon(s) - output += "for (" + strSlice(generate(children[0], enclosingObject, true),0,-3) + generate(children[1], enclosingObject, true) + ";" + strSlice(generate(children[2], enclosingObject, true),0,-3) + ")\n\t" + generate(children[3], enclosingObject, justFuncName); + output += "for (" + strSlice(generate(children[0], enclosingObject, true).oneString(),0,-3) + generate(children[1], enclosingObject, true).oneString() + ";" + strSlice(generate(children[2], enclosingObject, true).oneString(),0,-3) + ")"; + output += " {\n\t" + generate(children[3], enclosingObject, justFuncName).oneString(); + output += emitDestructors(reverse(distructDoubleStack.back()),enclosingObject); + output += + "}"; + distructDoubleStack.pop_back(); + loopDistructStackDepth.pop(); // and pop it off again loopDeferStackDepth.pop(); return output; case return_statement: + { // we pop off the vector and go through them in reverse emitting them, going // through all of both arrays, as return will go through all scopes for (auto topItr = deferDoubleStack.rbegin(); topItr != deferDoubleStack.rend(); topItr++) for (auto iter = (*topItr).rbegin(); iter != (*topItr).rend(); iter++) - output += generate(*iter, enclosingObject, justFuncName); - if (children.size()) - return "return " + generate(children[0], enclosingObject, true); - else - return "return"; + output += generate(*iter, enclosingObject, justFuncName).oneString(); + + std::string destructors = emitDestructors(reverse(flatten(distructDoubleStack)),enclosingObject); + if (children.size()) { + CCodeTriple expr = generate(children[0], enclosingObject, true); + output.preValue += expr.preValue; + std::string retTemp = "ret_temp" + getID(); + output.preValue += ValueTypeToCType(children[0]->getDataRef()->valueType, retTemp) + ";\n"; + if (methodExists(children[0]->getDataRef()->valueType, "copy_construct")) + output.preValue += generateMethodIfExists(children[0]->getDataRef()->valueType, "copy_construct", "&"+retTemp + ", &" + expr.value); + else + output.preValue += retTemp + " = " + expr.value + ";\n"; + // move expr post to before return + output.value += expr.postValue; + output.value += destructors; + output.value += "return " + retTemp; + } else { + output.value += destructors; + output += "return"; + } + return output; + } case break_statement: // handle everything that's been deferred all the way back to the loop's scope for (int i = deferDoubleStack.size()-1; i >= loopDeferStackDepth.top(); i--) for (auto iter = deferDoubleStack[i].rbegin(); iter != deferDoubleStack[i].rend(); iter++) - output += generate(*iter, enclosingObject, justFuncName); + output += generate(*iter, enclosingObject, justFuncName).oneString(); + // ok, emit destructors to where the loop ends + output += emitDestructors(reverse(flatten(slice(distructDoubleStack,loopDistructStackDepth.top(),-1))),enclosingObject); return output + "break"; case continue_statement: // handle everything that's been deferred all the way back to the loop's scope for (int i = deferDoubleStack.size()-1; i >= loopDeferStackDepth.top(); i--) for (auto iter = deferDoubleStack[i].rbegin(); iter != deferDoubleStack[i].rend(); iter++) - output += generate(*iter, enclosingObject, justFuncName); + output += generate(*iter, enclosingObject, justFuncName).oneString(); + // ok, emit destructors to where the loop ends + output += emitDestructors(reverse(flatten(slice(distructDoubleStack,loopDistructStackDepth.top(),-1))),enclosingObject); return output + "continue"; case defer_statement: deferDoubleStack.back().push_back(children[0]); - return "/*defer " + generate(children[0], enclosingObject, justFuncName) + "*/"; + return CCodeTriple("/*defer " + generate(children[0], enclosingObject, justFuncName).oneString() + "*/"); case assignment_statement: - return generate(children[0], enclosingObject, justFuncName) + " = " + generate(children[1], enclosingObject, true); + //if (methodExists(retType, "operator=")) { + return generate(children[0], enclosingObject, justFuncName).oneString() + " = " + generate(children[1], enclosingObject, true); case declaration_statement: + // adding declaration to the distructDoubleStack so that we can call their destructors when leaving scope (}, return, break, continue) + // but only if we're inside an actual doublestack + if ((distructDoubleStack.size())) + distructDoubleStack.back().push_back(children[0]); + if (children.size() == 1) - return ValueTypeToCType(children[0]->getData().valueType, generate(children[0], enclosingObject, justFuncName)) + ";"; + return ValueTypeToCType(children[0]->getData().valueType, generate(children[0], enclosingObject, justFuncName).oneString()) + ";"; else if (children[1]->getChildren().size() && children[1]->getChildren()[0]->getChildren().size() > 1 && children[1]->getChildren()[0]->getChildren()[1] == children[0]) { //That is, if we're a declaration with an init position call (Object a.construct()) //We can tell if our function call (children[1])'s access operation([0])'s lhs ([1]) is the thing we just declared (children[0]) - return ValueTypeToCType(children[0]->getData().valueType, generate(children[0], enclosingObject, justFuncName)) + "; " + generate(children[1], enclosingObject, true) + "/*Init Position Call*/"; - } else - return ValueTypeToCType(children[0]->getData().valueType, generate(children[0], enclosingObject, justFuncName)) + " = " + generate(children[1], enclosingObject, true) + ";"; + return ValueTypeToCType(children[0]->getData().valueType, generate(children[0], enclosingObject, justFuncName).oneString()) + "; " + generate(children[1], enclosingObject, true).oneString() + "/*Init Position Call*/"; + } else { + // copy constructor if of the same type + if (*children[0]->getDataRef()->valueType == *children[1]->getDataRef()->valueType && methodExists(children[1]->getDataRef()->valueType, "copy_construct")) { + CCodeTriple toAssign = generate(children[1], enclosingObject, true); + std::string assignedTo = generate(children[0], enclosingObject, justFuncName).oneString(); + output.value = toAssign.preValue; + output.value += ValueTypeToCType(children[0]->getData().valueType, assignedTo) + ";\n"; + output.value += generateMethodIfExists(children[0]->getDataRef()->valueType, "copy_construct", "&" + assignedTo + ", &" + toAssign.value) + ";\n" + output.postValue; + output.value += toAssign.postValue; + return output; + } else { + return ValueTypeToCType(children[0]->getData().valueType, generate(children[0], enclosingObject, justFuncName).oneString()) + " = " + generate(children[1], enclosingObject, true) + ";"; + } + } case if_comp: // Lol, this doesn't work because the string gets prefixed now //if (generate(children[0], enclosingObject) == generatorString) if (children[0]->getDataRef()->symbol.getName() == generatorString) return generate(children[1], enclosingObject, justFuncName); - return ""; + return CCodeTriple(""); case simple_passthrough: { // Stuff is bit more interesting now! XXX @@ -419,17 +501,17 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc for (auto assign : in_or_out->getChildren()) { auto assignChildren = assign->getChildren(); if (in_or_out->getDataRef()->type == in_passthrough_params) - pre_passthrough += ValueTypeToCType(assignChildren[0]->getDataRef()->valueType, assignChildren[1]->getDataRef()->symbol.getName()) + " = " + generate(assignChildren[0], enclosingObject) + ";\n"; + pre_passthrough += ValueTypeToCType(assignChildren[0]->getDataRef()->valueType, assignChildren[1]->getDataRef()->symbol.getName()) + " = " + generate(assignChildren[0], enclosingObject).oneString() + ";\n"; else if (in_or_out->getDataRef()->type == out_passthrough_params) - post_passthrough += generate(assignChildren[0], enclosingObject, justFuncName) + " = " + assignChildren[1]->getDataRef()->symbol.getName() + ";\n"; + post_passthrough += generate(assignChildren[0], enclosingObject, justFuncName).oneString() + " = " + assignChildren[1]->getDataRef()->symbol.getName() + ";\n"; else - linkerString += " " + strSlice(generate(in_or_out, enclosingObject, justFuncName), 1, -2) + " "; + linkerString += " " + strSlice(generate(in_or_out, enclosingObject, justFuncName).oneString(), 1, -2) + " "; } } } // The actual passthrough string is the last child now, as we might // have passthrough_params be the first child - return pre_passthrough + strSlice(generate(children.back(), enclosingObject, justFuncName), 3, -4) + post_passthrough; + return pre_passthrough + strSlice(generate(children.back(), enclosingObject, justFuncName).oneString(), 3, -4) + post_passthrough; } case function_call: { @@ -445,19 +527,19 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc //Test for special functions only if what we're testing is, indeed, the definition, not a function call that returns a callable function pointer if (funcType == function) { if (name == "++" || name == "--") - return generate(children[1], enclosingObject, true) + name; + return generate(children[1], enclosingObject, true).oneString() + name; if ( (name == "*" || name == "&" || name == "!" || name == "-" || name == "+" ) && children.size() == 2) //Is dereference, not multiplication, address-of, or other unary operator - return name + "(" + generate(children[1], enclosingObject, true) + ")"; + return name + "(" + generate(children[1], enclosingObject, true).oneString() + ")"; if (name == "[]") - return "(" + generate(children[1], enclosingObject, true) + ")[" +generate(children[2],enclosingObject, true) + "]"; + return "(" + generate(children[1], enclosingObject, true) + ")[" + generate(children[2],enclosingObject, true) + "]"; if (name == "+" || name == "-" || name == "*" || name == "/" || name == "==" || name == ">=" || name == "<=" || name == "!=" || name == "<" || name == ">" || name == "%" || name == "+=" || name == "-=" || name == "*=" || name == "/=" || name == "||" || name == "&&") { std::cout << "THIS IS IT NAME: " << name << std::endl; - return "((" + generate(children[1], enclosingObject, true) + ")" + name + "(" + generate(children[2], enclosingObject, true) + "))"; + return "((" + generate(children[1], enclosingObject, true).oneString() + ")" + name + "(" + generate(children[2], enclosingObject, true).oneString() + "))"; } else if (name == "." || name == "->") { if (children.size() == 1) - return "/*dot operation with one child*/" + generate(children[0], enclosingObject, true) + "/*end one child*/"; + return "/*dot operation with one child*/" + generate(children[0], enclosingObject, true).oneString() + "/*end one child*/"; //If this is accessing an actual function, find the function in scope and take the appropriate action. Probabally an object method if (children[2]->getDataRef()->type == function) { std::string functionName = children[2]->getDataRef()->symbol.getName(); @@ -473,20 +555,20 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc nameDecoration += "_" + ValueTypeToCTypeDecoration(functionDefChildren[i]->getData().valueType); // Note that we only add scoping to the object, as this specifies our member function too /*HERE*/ return scopePrefix(unaliasedTypeDef) + CifyName(unaliasedTypeDef->getDataRef()->symbol.getName()) +"__" + - CifyName(functionName + nameDecoration) + "(" + (name == "." ? "&" : "") + generate(children[1], enclosingObject, true) + ","; + CifyName(functionName + nameDecoration) + "(" + (name == "." ? "&" : "") + generate(children[1], enclosingObject, true).oneString() + ","; //The comma lets the upper function call know we already started the param list //Note that we got here from a function call. We just pass up this special case and let them finish with the perentheses } else { std::cout << "Is not in scope or not type" << std::endl; - return "((" + generate(children[1], enclosingObject, true) + ")" + name + functionName + ")"; + return "((" + generate(children[1], enclosingObject, true).oneString() + ")" + name + functionName + ")"; } } else { std::cout << "Is not in scope or not type" << std::endl; - return "((" + generate(children[1], enclosingObject, true) + ")" + name + functionName + ")"; + return "((" + generate(children[1], enclosingObject, true).oneString() + ")" + name + functionName + ")"; } } else { //return "((" + generate(children[1], enclosingObject) + ")" + name + generate(children[2], enclosingObject) + ")"; - return "((" + generate(children[1], enclosingObject, true) + ")" + name + generate(children[2], nullptr, true) + ")"; + return "((" + generate(children[1], enclosingObject, true).oneString() + ")" + name + generate(children[2], nullptr, true).oneString() + ")"; } } else { //It's a normal function call, not a special one or a method or anything. Name decorate. @@ -508,18 +590,37 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc } else { //This part handles cases where our definition isn't the function definition (that is, it is probabally the return from another function) //It's probabally the result of an access function call (. or ->) to access an object method. - std::string functionCallSource = generate(children[0], enclosingObject, true); + std::string functionCallSource = generate(children[0], enclosingObject, true).oneString(); if (functionCallSource[functionCallSource.size()-1] == ',') //If it's a member method, it's already started the parameter list. output += children.size() > 1 ? functionCallSource : functionCallSource.substr(0, functionCallSource.size()-1); else output += functionCallSource + "("; } - for (int i = 1; i < children.size(); i++) //children[0] is the declaration + // see if we should copy_construct all the parameters + for (int i = 1; i < children.size(); i++) { //children[0] is the declaration + if (methodExists(children[i]->getDataRef()->valueType, "copy_construct")) { + std::string tmpParamName = "param" + getID(); + CCodeTriple paramValue = generate(children[i], enclosingObject, true); + output.preValue += paramValue.preValue; + output.preValue += ValueTypeToCType(children[i]->getDataRef()->valueType, tmpParamName) + ";\n"; + output.preValue += generateMethodIfExists(children[i]->getDataRef()->valueType, "copy_construct", "&"+tmpParamName + ", &" + paramValue.value); + output.value += tmpParamName; + output.postValue += paramValue.postValue; + } else { + output += generate(children[i], enclosingObject, true); + } if (i < children.size()-1) - output += generate(children[i], enclosingObject, true) + ", "; - else - output += generate(children[i], enclosingObject, true); + output += ", "; + } output += ") "; + // see if we should add a destructer call to this postValue + Type* retType = children[0]->getDataRef()->valueType->returnType; + if (methodExists(retType, "destruct")) { + std::string retTempName = "return_temp" + getID(); + output.preValue += ValueTypeToCType(retType, retTempName) + " = " + output.value + ";\n"; + output.value = retTempName; + output.postValue = generateMethodIfExists(retType, "destruct", "&"+retTempName) + ";\n" + output.postValue; + } return output; } case value: @@ -529,7 +630,7 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc std::cout << "Nothing!" << std::endl; } for (int i = 0; i < children.size(); i++) - output += generate(children[i], enclosingObject, justFuncName); + output += generate(children[i], enclosingObject, justFuncName).oneString(); return output; } @@ -548,14 +649,49 @@ std::string CGenerator::generateObjectMethod(NodeTree* enclosingObject, std::vector*> children = from->getChildren(); std::string nameDecoration, parameters; for (int i = 0; i < children.size()-1; i++) { - parameters += ", " + ValueTypeToCType(children[i]->getData().valueType, generate(children[i])); + parameters += ", " + ValueTypeToCType(children[i]->getData().valueType, generate(children[i]).oneString()); nameDecoration += "_" + ValueTypeToCTypeDecoration(children[i]->getData().valueType); } std::string functionSignature = "\n" + ValueTypeToCType(data.valueType->returnType, scopePrefix(from) + CifyName(enclosingObject->getDataRef()->symbol.getName()) +"__" + CifyName(data.symbol.getName()) + nameDecoration) + "(" + ValueTypeToCType(&enclosingObjectType, "this") + parameters + ")"; *functionPrototype += functionSignature + ";\n"; // Note that we always wrap out child in {}, as we now allow one statement functions without a codeblock - return functionSignature + " {\n" + generate(children[children.size()-1], enclosingObject) + "}\n"; //Pass in the object so we can properly handle access to member stuff + return functionSignature + " {\n" + generate(children[children.size()-1], enclosingObject).oneString() + "}\n"; //Pass in the object so we can properly handle access to member stuff +} + +NodeTree* CGenerator::getMethod(Type* type, std::string method) { + if (type->getIndirection()) + return nullptr; + NodeTree *typeDefinition = type->typeDefinition; + if (typeDefinition) { + auto definitionItr = typeDefinition->getDataRef()->scope.find(method); + if (definitionItr != typeDefinition->getDataRef()->scope.end()) + return definitionItr->second[0]; + } + return nullptr; +} + +bool CGenerator::methodExists(Type* type, std::string method) { + return getMethod(type, method) != nullptr; +} + +std::string CGenerator::generateMethodIfExists(Type* type, std::string method, std::string parameter) { + NodeTree *typeDefinition = type->typeDefinition; + NodeTree *methodDef = getMethod(type, method); + if (methodDef) { + std::string nameDecoration; + for (Type *paramType : methodDef->getDataRef()->valueType->parameterTypes) + nameDecoration += "_" + ValueTypeToCTypeDecoration(paramType); + return scopePrefix(typeDefinition) + CifyName(typeDefinition->getDataRef()->symbol.getName()) + "__" + method + nameDecoration + "(" + parameter + ");\n"; + } + return ""; +} + +std::string CGenerator::emitDestructors(std::vector*> identifiers, NodeTree* enclosingObject) { + std::string destructorString = ""; + for (auto identifier : identifiers) + destructorString += tabs() + generateMethodIfExists(identifier->getDataRef()->valueType, "destruct", "&" + generate(identifier, enclosingObject).oneString()); + return destructorString; } diff --git a/stdlib/mem.krak b/stdlib/mem.krak index 2fd6fba..156711c 100644 --- a/stdlib/mem.krak +++ b/stdlib/mem.krak @@ -23,11 +23,11 @@ fun free(memPtr: T*): void { } fun sizeof(): int { - var testObj: T; + var testObj: T*; var result: int; __if_comp__ __C__ { simple_passthrough(testObj = testObj : result = result:) """ - int result = sizeof(testObj); + int result = sizeof(*testObj); """ } return result; diff --git a/stdlib/vector.krak b/stdlib/vector.krak index 963c888..1330e9c 100644 --- a/stdlib/vector.krak +++ b/stdlib/vector.krak @@ -22,6 +22,12 @@ obj vector (Destructable) { return this; } + fun copy_construct(old: vector*): void { + construct(old->size) + for (var i = 0; i < size; i++;) + set(i, old->get(i)) + } + fun destruct(): void { delete(data); } @@ -74,6 +80,10 @@ obj vector (Destructable) { resize(size*2); data[size-1] = dataIn; } + fun do(func: fun(T):void):void { + for (var i = 0; i < size; i++;) + func(data[i]) + } fun in_place(func: fun(T):T):void { for (var i = 0; i < size; i++;) data[i] = func(data[i]) diff --git a/tests/test_destructor_copy_constructor.expected_results b/tests/test_destructor_copy_constructor.expected_results new file mode 100644 index 0000000..6001961 --- /dev/null +++ b/tests/test_destructor_copy_constructor.expected_results @@ -0,0 +1,47 @@ +destroyed b : 0! +destroyed c : 0! +destroyed c : 0! +destroyed c : 0! +destroyed c : 0! +destroyed c : 0! +destroyed d : 0! +destroyed d : 0! +destroyed d : 0! +destroyed d : 0! +destroyed d : 0! +destroyed e : 0! +destroyed g : 0! +destroyed g : 0! +destroyed g : 0! +destroyed g : 0! +destroyed g : 0! +destroyed in_while_block : 0! +destroyed in_while_block : 0! +destroyed in_while_block : 0! +destroyed in_while_block : 0! +destroyed in_while_block : 0! +destroyed in_while_no_block : 0! +destroyed in_while_no_block : 0! +destroyed in_while_no_block : 0! +destroyed in_while_no_block : 0! +destroyed in_while_no_block : 0! +destroyed it_while_pre_break : 0! +destroyed it_while_pre_continue : 0! +destroyed it_while_pre_continue : 0! +destroyed it_while_pre_continue : 0! +destroyed it_while_pre_continue : 0! +destroyed it_while_pre_continue : 0! +destroyed j : 0! +destroyed i : 0! +copy_construct inFunc : 1! +in inOutFunc +copy_construct outFunc : 1! +destroyed outFunc : 0! +destroyed inFunc : 1! +copy_construct outFunc : 2! +destroyed outFunc : 1! +time for the end +destroyed outFunc : 2! +destroyed inFunc : 0! +destroyed a2 : 0! +destroyed a : 0! diff --git a/tests/test_destructor_copy_constructor.krak b/tests/test_destructor_copy_constructor.krak new file mode 100644 index 0000000..1e9c8cc --- /dev/null +++ b/tests/test_destructor_copy_constructor.krak @@ -0,0 +1,105 @@ +import io:* + +obj wDestructor { + var data: char* + var count: int + fun construct(dat:char*):wDestructor* { + data = dat + count = 0 + return this + } + fun copy_construct(other: wDestructor*):void { + data = other->data + count = other->count + 1 + print("copy_construct ") + print(data) + print(" : ") + print(count) + println("!") + } + fun operator=(other: wDestructor*):void { + data = other->data + count = other->count + 1 + print("copy assign ") + print(data) + print(" : ") + print(count) + println("!") + } + fun destruct():void { + print("destroyed ") + print(data) + print(" : ") + print(count) + println("!") + } +} + +fun inFunction():void { + { + var func_out.construct("i"): wDestructor + { + var func_in_block_pre_ret.construct("j"): wDestructor + return + var func_in_block_post_ret.construct("k"): wDestructor + } + var func_out_post_ret.construct("l"): wDestructor + } +} + +fun inOutFunc(thing: wDestructor):wDestructor { + println("in inOutFunc") + var toRet.construct("outFunc"): wDestructor + return toRet +} + +fun main():int { + var outside.construct("a"): wDestructor + var outside2.construct("a2"): wDestructor + { + var simple_block.construct("b"): wDestructor + } + for (var i = 0; i < 5; i++;) { + var in_for_block.construct("c"): wDestructor + } + for (var i = 0; i < 5; i++;) + var in_for_no_block.construct("d"): wDestructor + for (var i = 0; i < 5; i++;) { + var it_for_pre_break.construct("e"): wDestructor + break + var it_for_post_break.construct("f"): wDestructor + } + for (var i = 0; i < 5; i++;) { + var it_for_pre_continue.construct("g"): wDestructor + continue + var it_for_post_continue.construct("h"): wDestructor + } + /*for (var i.construct("in_for_dec"): wDestructor; i.data == "not_equal"; i.data;) println("should not print")*/ + + var idx = 0 + while (idx++ < 5) { + var in_while_block.construct("in_while_block"): wDestructor + } + idx = 0 + while (idx++ < 5) + var in_while_no_block.construct("in_while_no_block"): wDestructor + idx = 0 + while (idx++ < 5) { + var it_while_pre_break.construct("it_while_pre_break"): wDestructor + break + var it_while_post_break.construct("it_while_post_break"): wDestructor + } + idx = 0 + while (idx++ < 5) { + var it_while_pre_continue.construct("it_while_pre_continue"): wDestructor + continue + var it_while_post_continue.construct("it_while_post_continue"): wDestructor + } + + inFunction() + + var inFunc.construct("inFunc"):wDestructor + var outFunc = inOutFunc(inFunc) + println("time for the end") + return 0 +} diff --git a/tests/test_vectorTest.expected_results b/tests/test_vectorTest.expected_results index 5f4449b..9e443c1 100644 --- a/tests/test_vectorTest.expected_results +++ b/tests/test_vectorTest.expected_results @@ -1,6 +1,9 @@ 4 1337 26614 +9131321 15513 3.7000007.7000007.70000015.700000 +Copied! +Destroyed! Destroyed! diff --git a/tests/test_vectorTest.krak b/tests/test_vectorTest.krak index ac80972..f9213ee 100644 --- a/tests/test_vectorTest.krak +++ b/tests/test_vectorTest.krak @@ -3,6 +3,10 @@ import mem:*; import vector:*; obj AbleToBeDestroyed (Destructable) { + fun copy_construct(other:AbleToBeDestroyed*):void { + println("Copied!"); + } + fun destruct(): void { println("Destroyed!"); } @@ -24,6 +28,8 @@ fun main(): int { for (var i: int = 0; i < intVec.size; i++;) print(intVec.at(i)); println(); + intVec.do(fun(it:int):void print(it+7);) + println(); var subd = intVec.map(fun(it:int):int { return it-1; }) for (var i: int = 0; i < subd.size; i++;)