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; +}