From 63d9ec66e18b499487adeb691e78ff69f4625dae Mon Sep 17 00:00:00 2001 From: Nathan Braswell Date: Thu, 26 Jun 2014 01:45:44 -0700 Subject: [PATCH] Added "Init Position Call" (takes the place of implicit constructors) and the this keyword! This was the structure needed for more sensable memory management. At least delete will need some updating before it becomes very usable, though. (Figuring out the types for function template instantiation) Anyway, good progress here! --- include/ASTTransformation.h | 3 +- krakenGrammer.kgm | 2 +- src/ASTTransformation.cpp | 109 +++++++++++++++--------- src/CGenerator.cpp | 23 +++-- stdlib/io.krak | 18 +++- tests/declarationsTest.expected_results | 3 + tests/declarationsTest.krak | 26 ++++++ 7 files changed, 134 insertions(+), 50 deletions(-) create mode 100644 tests/declarationsTest.expected_results create mode 100644 tests/declarationsTest.krak diff --git a/include/ASTTransformation.h b/include/ASTTransformation.h index dfaa553..955ccca 100644 --- a/include/ASTTransformation.h +++ b/include/ASTTransformation.h @@ -45,7 +45,8 @@ class ASTTransformation: public NodeTransformation { NodeTree* findOrInstantiateFunctionTemplate(std::vector*> children, NodeTree* scope, std::vector types, std::map templateTypeReplacements); private: Importer * importer; - std::map*>> languageLevelScope; + std::map*>> languageLevelReservedWords; + std::map*>> languageLevelOperators; NodeTree* topScope; //maintained for templates that need to add themselves to the top scope no matter where they are instantiated }; diff --git a/krakenGrammer.kgm b/krakenGrammer.kgm index 8fdfba6..85406ee 100644 --- a/krakenGrammer.kgm +++ b/krakenGrammer.kgm @@ -81,7 +81,7 @@ number = integer | float | double ; access_operation = unarad "." identifier | unarad "->" identifier ; assignment_statement = factor WS "=" WS boolean_expression | factor WS "\+=" WS boolean_expression | factor WS "-=" WS boolean_expression | factor WS "\*=" WS boolean_expression | factor WS "/=" WS boolean_expression ; -declaration_statement = type WS identifier WS "=" WS boolean_expression | type WS identifier ; +declaration_statement = type WS identifier WS "=" WS boolean_expression | type WS identifier | type WS identifier WS "." WS identifier WS "\(" WS opt_parameter_list WS "\)" ; alphanumeric = alphanumeric numeric | alphanumeric alpha | numeric | alpha ; hexadecimal = "0x(1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)+" ; diff --git a/src/ASTTransformation.cpp b/src/ASTTransformation.cpp index bb7a322..160e14c 100644 --- a/src/ASTTransformation.cpp +++ b/src/ASTTransformation.cpp @@ -3,30 +3,34 @@ ASTTransformation::ASTTransformation(Importer *importerIn) { importer = importerIn; topScope = NULL; - //Set up language level special scope. (the final scope checked) + + //Set up language level reserved identifier scope (only this, right now) + languageLevelReservedWords["this"].push_back(new NodeTree("identifier", ASTData(identifier, Symbol("this", true), NULL))); + + //Set up language level special scope. (the final scope checked) //Note the NULL type - languageLevelScope["+"].push_back( new NodeTree("function", ASTData(function, Symbol("+", true), NULL))); - languageLevelScope["-"].push_back(new NodeTree("function", ASTData(function, Symbol("-", true), NULL))); - languageLevelScope["*"].push_back(new NodeTree("function", ASTData(function, Symbol("*", true), NULL))); - languageLevelScope["/"].push_back(new NodeTree("function", ASTData(function, Symbol("/", true), NULL))); - languageLevelScope["%"].push_back(new NodeTree("function", ASTData(function, Symbol("%", true), NULL))); - languageLevelScope["&"].push_back(new NodeTree("function", ASTData(function, Symbol("&", true), NULL))); - languageLevelScope["--"].push_back(new NodeTree("function", ASTData(function, Symbol("--", true), NULL))); - languageLevelScope["++"].push_back(new NodeTree("function", ASTData(function, Symbol("++", true), NULL))); - languageLevelScope["=="].push_back(new NodeTree("function", ASTData(function, Symbol("==", true), NULL))); - languageLevelScope["<="].push_back(new NodeTree("function", ASTData(function, Symbol("<=", true), NULL))); - languageLevelScope[">="].push_back(new NodeTree("function", ASTData(function, Symbol(">=", true), NULL))); - languageLevelScope["<"].push_back(new NodeTree("function", ASTData(function, Symbol("<", true), NULL))); - languageLevelScope[">"].push_back(new NodeTree("function", ASTData(function, Symbol(">", true), NULL))); - languageLevelScope["&&"].push_back(new NodeTree("function", ASTData(function, Symbol("&&", true), NULL))); - languageLevelScope["||"].push_back(new NodeTree("function", ASTData(function, Symbol("||", true), NULL))); - languageLevelScope["!"].push_back(new NodeTree("function", ASTData(function, Symbol("!", true), NULL))); - languageLevelScope["*="].push_back(new NodeTree("function", ASTData(function, Symbol("*=", true), NULL))); - languageLevelScope["+="].push_back(new NodeTree("function", ASTData(function, Symbol("+=", true), NULL))); - languageLevelScope["-="].push_back(new NodeTree("function", ASTData(function, Symbol("-=", true), NULL))); - languageLevelScope["."].push_back(new NodeTree("function", ASTData(function, Symbol(".", true), NULL))); - languageLevelScope["->"].push_back(new NodeTree("function", ASTData(function, Symbol("->", true), NULL))); - languageLevelScope["[]"].push_back(new NodeTree("function", ASTData(function, Symbol("[]", true), NULL))); + languageLevelOperators["+"].push_back(new NodeTree("function", ASTData(function, Symbol("+", true), NULL))); + languageLevelOperators["-"].push_back(new NodeTree("function", ASTData(function, Symbol("-", true), NULL))); + languageLevelOperators["*"].push_back(new NodeTree("function", ASTData(function, Symbol("*", true), NULL))); + languageLevelOperators["/"].push_back(new NodeTree("function", ASTData(function, Symbol("/", true), NULL))); + languageLevelOperators["%"].push_back(new NodeTree("function", ASTData(function, Symbol("%", true), NULL))); + languageLevelOperators["&"].push_back(new NodeTree("function", ASTData(function, Symbol("&", true), NULL))); + languageLevelOperators["--"].push_back(new NodeTree("function", ASTData(function, Symbol("--", true), NULL))); + languageLevelOperators["++"].push_back(new NodeTree("function", ASTData(function, Symbol("++", true), NULL))); + languageLevelOperators["=="].push_back(new NodeTree("function", ASTData(function, Symbol("==", true), NULL))); + languageLevelOperators["<="].push_back(new NodeTree("function", ASTData(function, Symbol("<=", true), NULL))); + languageLevelOperators[">="].push_back(new NodeTree("function", ASTData(function, Symbol(">=", true), NULL))); + languageLevelOperators["<"].push_back(new NodeTree("function", ASTData(function, Symbol("<", true), NULL))); + languageLevelOperators[">"].push_back(new NodeTree("function", ASTData(function, Symbol(">", true), NULL))); + languageLevelOperators["&&"].push_back(new NodeTree("function", ASTData(function, Symbol("&&", true), NULL))); + languageLevelOperators["||"].push_back(new NodeTree("function", ASTData(function, Symbol("||", true), NULL))); + languageLevelOperators["!"].push_back(new NodeTree("function", ASTData(function, Symbol("!", true), NULL))); + languageLevelOperators["*="].push_back(new NodeTree("function", ASTData(function, Symbol("*=", true), NULL))); + languageLevelOperators["+="].push_back(new NodeTree("function", ASTData(function, Symbol("+=", true), NULL))); + languageLevelOperators["-="].push_back(new NodeTree("function", ASTData(function, Symbol("-=", true), NULL))); + languageLevelOperators["."].push_back(new NodeTree("function", ASTData(function, Symbol(".", true), NULL))); + languageLevelOperators["->"].push_back(new NodeTree("function", ASTData(function, Symbol("->", true), NULL))); + languageLevelOperators["[]"].push_back(new NodeTree("function", ASTData(function, Symbol("[]", true), NULL))); } ASTTransformation::~ASTTransformation() { @@ -132,7 +136,8 @@ void ASTTransformation::secondPassDoClassInsides(NodeTree* typeDef, std //This function may need to partially instantiate a class template NodeTree* ASTTransformation::secondPassDeclaration(NodeTree* from, NodeTree* scope, std::map templateTypeReplacements) { - NodeTree* decStmt = new NodeTree("declaration_statement", ASTData(declaration_statement)); + //Check here for method call (an error here) + NodeTree* decStmt = new NodeTree("declaration_statement", ASTData(declaration_statement)); std::string newIdentifierStr = concatSymbolTree(from->getChildren()[1]); Type* identifierType = typeFromTypeNode(from->getChildren()[0], scope, templateTypeReplacements, false); std::cout << "Declaring an identifier " << newIdentifierStr << " to be of type " << identifierType->toString() << std::endl; @@ -576,9 +581,33 @@ NodeTree* ASTTransformation::transform(NodeTree* from, NodeTree newNode->getDataRef()->scope["~enclosing_scope"].push_back(scope); newNode->addChild(newIdentifier); + if (children.size() > 2 && concatSymbolTree(children[2]) == ".") { + //A bit of a special case for declarations - if there's anything after just the normal 1 node declaration, it's either + //an expression that is assigned to the declaration (int a = 4;) or a member call (Object a.constructAThing()) + //This code is a simplified version of the code in function_call with respect to access_operation. + //Note that in this case, what is lhs there is our newIdentifier here (the declaration of the left side of the access operation) + auto sliced = slice(children, 4, -1); + std::vector*> initPositionFuncParams = transformChildren(sliced, std::set(), scope, types, templateTypeReplacements, instantiateTemplates); + NodeTree* rhs = transform(children[3], identifierType->typeDefinition, mapNodesToTypes(initPositionFuncParams), templateTypeReplacements, instantiateTemplates); //If an access operation, then the right side will be in the lhs's type's scope + std::vector*> transformedChildren; transformedChildren.push_back(newIdentifier); transformedChildren.push_back(rhs); + NodeTree* accessFuncCall = doFunction(scope, ".", transformedChildren, templateTypeReplacements); + accessFuncCall->getDataRef()->valueType = rhs->getDataRef()->valueType; + //Now we borrow a bit of code from function_call below to actually use our new accessFuncCall to setup a "initPosition" function call + //that will follow the identifier in this declaration node + std::string initPosFuncName = newIdentifierStr + "." + concatSymbolTree(children[3]); + NodeTree* initPositionFuncCall = new NodeTree(initPosFuncName, ASTData(function_call, Symbol(initPosFuncName, true))); + initPositionFuncCall->addChild(accessFuncCall); + initPositionFuncCall->getDataRef()->valueType = accessFuncCall->getDataRef()->valueType; + initPositionFuncCall->addChildren(initPositionFuncParams); + newNode->addChild(initPositionFuncCall); + return newNode; + } + skipChildren.insert(0); //These, the type and the identifier, have been taken care of. skipChildren.insert(1); - } else if (name == "if_comp") { + newNode->addChildren(transformChildren(children, skipChildren, scope, types, templateTypeReplacements, instantiateTemplates)); + return newNode; + } else if (name == "if_comp") { newNode = new NodeTree(name, ASTData(if_comp)); newNode->addChild(new NodeTree("identifier", ASTData(identifier, Symbol(concatSymbolTree(children[0]),true)))); skipChildren.insert(0); //Don't do the identifier. The identifier lookup will fail. That's why we do it here. @@ -627,15 +656,7 @@ NodeTree* ASTTransformation::transform(NodeTree* from, NodeTree } //Do all children but the ones we skip - for (int i = 0; i < children.size(); i++) { - if (skipChildren.find(i) == skipChildren.end()) { - NodeTree* transChild = transform(children[i], scope, types, templateTypeReplacements, instantiateTemplates); - if (transChild->getDataRef()->type) //Only add the children that have a real ASTData::ASTType, that is, legit ASTData. - newNode->addChild(transChild); - else - delete transChild; - } - } + newNode->addChildren(transformChildren(children, skipChildren, scope, types, templateTypeReplacements, instantiateTemplates)); return newNode; } @@ -680,9 +701,9 @@ std::string ASTTransformation::concatSymbolTree(NodeTree* root) { //We pass in the actual children (parameters) to allow us to handle overloaded operator methods (where a parameter is actually the scope of the method) NodeTree* ASTTransformation::doFunction(NodeTree* scope, std::string lookup, std::vector*> nodes, std::map templateTypeReplacements) { - auto LLElementIterator = languageLevelScope.find(lookup); + auto LLElementIterator = languageLevelOperators.find(lookup); NodeTree* newNode; - if (LLElementIterator != languageLevelScope.end()) { + if (LLElementIterator != languageLevelOperators.end()) { std::cout << "Checking for early method level operator overload" << std::endl; std::string lookupOp = "operator" + lookup; for (auto i : nodes) @@ -697,7 +718,7 @@ NodeTree* ASTTransformation::doFunction(NodeTree* scope, std:: //return operatorMethod; NodeTree* newNode = new NodeTree(lookupOp, ASTData(function_call, Symbol(lookupOp, true))); NodeTree* dotFunctionCall = new NodeTree(".", ASTData(function_call, Symbol(".", true))); - dotFunctionCall->addChild(languageLevelScope["."][0]); //function definition + dotFunctionCall->addChild(languageLevelOperators["."][0]); //function definition dotFunctionCall->addChild(nodes[0]); // The object whose method we're calling dotFunctionCall->addChild(operatorMethod); //The method we're calling newNode->addChild(dotFunctionCall); // First child of function call is a link to the function definition @@ -740,9 +761,15 @@ NodeTree* ASTTransformation::doFunction(NodeTree* scope, std:: //Search recursively through levels of scope (each ASTData, that is, every node, has its own scope) //We pass in types so that if we're searching for a function we can find the right overloaded one NodeTree* ASTTransformation::scopeLookup(NodeTree* scope, std::string lookup, std::vector types) { - //We first search the languageLevelScope to see if it's an operator. If so, we modifiy the lookup with a preceding "operator" - auto LLElementIterator = languageLevelScope.find(lookup); - if (LLElementIterator != languageLevelScope.end()) + //We first check to see if it's one of the special reserved identifiers (only this, for now) and return early if it is. + auto LLElementIterator = languageLevelReservedWords.find(lookup); + if (LLElementIterator != languageLevelReservedWords.end()) { + std::cout << "found it at language level as reserved word." << std::endl; + return LLElementIterator->second[0]; + } + //We search the languageLevelOperators to see if it's an operator. If so, we modifiy the lookup with a preceding "operator" + LLElementIterator = languageLevelOperators.find(lookup); + if (LLElementIterator != languageLevelOperators.end()) lookup = "operator" + lookup; //Search the map auto scopeMap = scope->getDataRef()->scope; @@ -802,7 +829,7 @@ NodeTree* ASTTransformation::scopeLookup(NodeTree* scope, std: std::cout << "could not find " << lookup << " in standard scope, checking for operator" << std::endl; //Note that we don't check for types. At some point we should, as we don't know how to add objects/structs without overloaded operators, etc //Also, we've already searched for the element because this is also how we keep track of operator overloading - if (LLElementIterator != languageLevelScope.end()) { + if (LLElementIterator != languageLevelOperators.end()) { std::cout << "found it at language level as operator." << std::endl; return LLElementIterator->second[0]; } diff --git a/src/CGenerator.cpp b/src/CGenerator.cpp index 5a117ed..44472b3 100644 --- a/src/CGenerator.cpp +++ b/src/CGenerator.cpp @@ -49,7 +49,7 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc Poset*> typedefPoset; for (int i = 0; i < children.size(); i++) { if (children[i]->getDataRef()->type == type_def) { - typedefPoset.addVertex(children[i]); //We add this definition by itself just in case there are no dependencies. + typedefPoset.addVertex(children[i]); //We add this definition by itthis just in case there are no dependencies. //If it has dependencies, there's no harm in adding it here //Go through every child in the class looking for declaration statements. For each of these that is not a primitive type //we will add a dependency from this definition to that definition in the poset. @@ -129,10 +129,16 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc //return "#include <" + data.symbol.getName() + ">\n"; case identifier: { - //If we're in an object method, and our enclosing scope is that object, we're a member of the object and should use the self reference. + //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"; + else + std::cout << "Error: this used in non-object scope" << std::endl; + //If we're in an object method, and our enclosing scope is that object, we're a member of the object and should use the this reference. std::string preName; if (enclosingObject && enclosingObject->getDataRef()->scope.find(data.symbol.getName()) != enclosingObject->getDataRef()->scope.end()) - preName += "self->"; + preName += "this->"; if (false) for (int j = 0; j < children.size()-1; j++) preName += ValueTypeToCType(children[j]->getData().valueType) + "_"; @@ -213,7 +219,12 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc case declaration_statement: if (children.size() == 1) return ValueTypeToCType(children[0]->getData().valueType) + " " + generate(children[0], enclosingObject) + ";"; - else + 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) + "; " + generate(children[1]) + "/*Init Position Call*/"; + } else return ValueTypeToCType(children[0]->getData().valueType) + " " + generate(children[0], enclosingObject) + " = " + generate(children[1], enclosingObject) + ";"; case if_comp: if (generate(children[0], enclosingObject) == generatorString) @@ -288,7 +299,7 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc output += enclosingObject->getDataRef()->symbol.getName() +"__"; /*HERE*/ output += CifyName(name + nameDecoration) + "("; if (isSelfObjectMethod) - output += children.size() > 1 ? "self," : "self"; + output += children.size() > 1 ? "this," : "this"; } } else { //This part handles cases where our definition isn't the function definition (that is, it is probabally the return from another function) @@ -337,7 +348,7 @@ std::string CGenerator::generateObjectMethod(NodeTree* enclosingObject, } output += "\n" + ValueTypeToCType(data.valueType) + " " + CifyName(enclosingObject->getDataRef()->symbol.getName()) +"__" + CifyName(data.symbol.getName()) + nameDecoration + "(" + ValueTypeToCType(&enclosingObjectType) - + " self" + parameters + ")\n" + generate(children[children.size()-1], enclosingObject); //Pass in the object so we can properly handle access to member stuff + + " this" + parameters + ")\n" + generate(children[children.size()-1], enclosingObject); //Pass in the object so we can properly handle access to member stuff return output; } diff --git a/stdlib/io.krak b/stdlib/io.krak index 44c72a2..1d079b7 100644 --- a/stdlib/io.krak +++ b/stdlib/io.krak @@ -11,6 +11,11 @@ void print(char* toPrint) { return; } +void println(char* toPrint) { + print(toPrint); + print("\n"); +} + void print(int toPrint) { __if_comp__ __C__ { __simple_passthrough__ """ @@ -20,6 +25,11 @@ void print(int toPrint) { return; } +void println(int toPrint) { + print(toPrint); + print("\n"); +} + void print(float toPrint) { __if_comp__ __C__ { __simple_passthrough__ """ @@ -27,4 +37,10 @@ void print(float toPrint) { """ } return; -} \ No newline at end of file +} + +void println(float toPrint) { + print(toPrint); + print("\n"); +} + diff --git a/tests/declarationsTest.expected_results b/tests/declarationsTest.expected_results new file mode 100644 index 0000000..e26fda4 --- /dev/null +++ b/tests/declarationsTest.expected_results @@ -0,0 +1,3 @@ +4 +8 +11 diff --git a/tests/declarationsTest.krak b/tests/declarationsTest.krak new file mode 100644 index 0000000..9bfe547 --- /dev/null +++ b/tests/declarationsTest.krak @@ -0,0 +1,26 @@ +import io; +import mem; + +typedef ClassWithConstructor { + int data; + ClassWithConstructor* construct(int inData) { + data = inData; + return this; + } + void printData() { + println(data); + } +}; + +int main() { + ClassWithConstructor object.construct(4); + //ClassWithConstructor object; + //object.construct(4); + object.printData(); + int a = 8; + println(a); + ClassWithConstructor* objPtr = new()->construct(11); + objPtr->printData(); + delete(objPtr); + return 0; +}