diff --git a/include/ASTData.h b/include/ASTData.h index daa744a..5153779 100644 --- a/include/ASTData.h +++ b/include/ASTData.h @@ -15,9 +15,9 @@ class Type; enum ASTType {undef, translation_unit, interpreter_directive, import, identifier, type_def, function, code_block, typed_parameter, expression, boolean_expression, statement, - if_statement, while_loop, for_loop, return_statement, assignment_statement, declaration_statement, - if_comp, simple_passthrough, passthrough_params, in_passthrough_params, out_passthrough_params, - opt_string, param_assign, function_call, value}; + if_statement, while_loop, for_loop, return_statement, break_statement, continue_statement, defer_statement, + assignment_statement, declaration_statement, if_comp, simple_passthrough, passthrough_params, + in_passthrough_params, out_passthrough_params, opt_string, param_assign, function_call, value}; class ASTData { public: diff --git a/include/CGenerator.h b/include/CGenerator.h index c70ceac..8c8a91d 100644 --- a/include/CGenerator.h +++ b/include/CGenerator.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "NodeTree.h" @@ -34,10 +35,13 @@ 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); + std::string tabs(); + + int tabLevel; std::string generatorString; std::string linkerString; + std::vector*>> deferDoubleStack; + std::stack loopDeferStackDepth; private: - std::string tabs(); - int tabLevel; }; #endif diff --git a/krakenGrammer.kgm b/krakenGrammer.kgm index 28fcc5a..87e1073 100644 --- a/krakenGrammer.kgm +++ b/krakenGrammer.kgm @@ -87,7 +87,10 @@ return_statement = "return" | "return" WS boolean_expression ; code_block = "{" WS statement_list WS "}" | "{" WS "}" ; statement_list = statement_list WS statement | statement ; -statement = if_statement | while_loop | for_loop | return_statement line_end | boolean_expression line_end | assignment_statement line_end | declaration_statement line_end | code_block | if_comp | simple_passthrough ; +statement = if_statement | while_loop | for_loop | return_statement line_end | boolean_expression line_end | assignment_statement line_end | declaration_statement line_end | code_block | if_comp | simple_passthrough | break_statement | continue_statement | defer_statement ; +break_statement = "break" ; +continue_statement = "continue" ; +defer_statement = "defer" WS statement ; function_call = unarad "\(" WS opt_parameter_list WS "\)" ; boolean_expression = boolean_expression WS "\|\|" WS and_boolean_expression | and_boolean_expression ; diff --git a/src/ASTData.cpp b/src/ASTData.cpp index 8dd84ff..b4cbb3e 100644 --- a/src/ASTData.cpp +++ b/src/ASTData.cpp @@ -58,6 +58,12 @@ std::string ASTData::ASTTypeToString(ASTType type) { return "for_loop"; case return_statement: return "return_statement"; + case break_statement: + return "break_statement"; + case continue_statement: + return "continue_statement"; + case defer_statement: + return "defer_statement"; case assignment_statement: return "assignment_statement"; case declaration_statement: diff --git a/src/ASTTransformation.cpp b/src/ASTTransformation.cpp index 1b10cbf..f6db8fa 100644 --- a/src/ASTTransformation.cpp +++ b/src/ASTTransformation.cpp @@ -586,6 +586,12 @@ NodeTree* ASTTransformation::transform(NodeTree* from, NodeTree newNode = new NodeTree(name, ASTData(for_loop)); } else if (name == "return_statement") { newNode = new NodeTree(name, ASTData(return_statement)); + } else if (name == "break_statement") { + newNode = new NodeTree(name, ASTData(break_statement)); + } else if (name == "continue_statement") { + newNode = new NodeTree(name, ASTData(continue_statement)); + } else if (name == "defer_statement") { + newNode = new NodeTree(name, ASTData(defer_statement)); } else if (name == "assignment_statement") { newNode = new NodeTree(name, ASTData(assignment_statement)); std::string assignFuncName = concatSymbolTree(children[1]); @@ -609,16 +615,9 @@ NodeTree* ASTTransformation::transform(NodeTree* from, NodeTree return newNode; } else if (name == "declaration_statement") { newNode = new NodeTree(name, ASTData(declaration_statement)); - - // NodeTree* newIdentifier = transform(children[1], scope); //Transform the identifier - // newIdentifier->getDataRef()->valueType = Type(concatSymbolTree(children[0]));//set the type of the identifier std::string newIdentifierStr = concatSymbolTree(children[0]); NodeTree* typeSyntaxNode = getNode("type", children); Type* identifierType = typeSyntaxNode ? typeFromTypeNode(typeSyntaxNode, scope, templateTypeReplacements) : nullptr; - //if (children.size() > 1 && concatSymbolTree(children[1]) == ".") - //identifierType = typeFromTypeNode(children.back(), scope, templateTypeReplacements); - //else - //identifierType = typeFromTypeNode(children[2], scope, templateTypeReplacements); if (identifierType) std::cout << "Declaring an identifier " << newIdentifierStr << " to be of type " << identifierType->toString() << std::endl; @@ -652,9 +651,6 @@ NodeTree* ASTTransformation::transform(NodeTree* from, NodeTree return newNode; } - //skipChildren.insert(0); //These, the type and the identifier, have been taken care of. - //skipChildren.insert(2); - //auto transChildren = transformChildren(children, skipChildren, scope, types, templateTypeReplacements); auto boolExp = getNode("boolean_expression", children); NodeTree* toAssign = boolExp ? transform(boolExp, scope, types, templateTypeReplacements) : nullptr; // for type inferencing @@ -673,7 +669,6 @@ NodeTree* ASTTransformation::transform(NodeTree* from, NodeTree newNode->addChild(newIdentifier); if (toAssign) newNode->addChild(toAssign); - //newNode->addChildren(transChildren); return newNode; } else if (name == "if_comp") { newNode = new NodeTree(name, ASTData(if_comp)); diff --git a/src/CGenerator.cpp b/src/CGenerator.cpp index 817d012..2894780 100644 --- a/src/CGenerator.cpp +++ b/src/CGenerator.cpp @@ -304,31 +304,15 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc case code_block: { output += "{\n"; - std::string destructorString = ""; tabLevel++; - for (int i = 0; i < children.size(); i++) { - //std::cout << "Line " << i << std::endl; - std::string line = generate(children[i], enclosingObject); - //std::cout << line << std::endl; - output += line; - if (children[i]->getChildren().size() && children[i]->getChildren()[0]->getDataRef()->type == declaration_statement) { - NodeTree *identifier = children[i]->getChildren()[0]->getChildren()[0]; - Type* declarationType = identifier->getDataRef()->valueType; - if (declarationType->getIndirection()) - continue; - NodeTree *typeDefinition = declarationType->typeDefinition; - if (!typeDefinition) - continue; - if (typeDefinition->getDataRef()->scope.find("destruct") == typeDefinition->getDataRef()->scope.end()) - continue; - // *************************************************************************************** - // I've decided not to do the destructor thing. This will come back soon for defer though! - // *************************************************************************************** - //destructorString += tabs() + scopePrefix(from) + CifyName(typeDefinition->getDataRef()->symbol.getName()) - //+ "__" + "destruct" + "(&" + generate(identifier, enclosingObject) + ");\n";//Call the destructor - } - } - output += destructorString; + // 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); + // 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); + deferDoubleStack.pop_back(); tabLevel--; output += tabs() + "}"; return output; @@ -348,24 +332,59 @@ std::string CGenerator::generate(NodeTree* from, NodeTree* enc 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); } 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); + output += "{ " + generate(children[1], enclosingObject) + " }"; } + // Always emit blocks here too if (children.size() > 2) - output += " else " + generate(children[2], enclosingObject); + output += " else { " + generate(children[2], enclosingObject) + " }"; return output; case while_loop: + // 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) + ")\n\t" + generate(children[1], enclosingObject); + // and pop it off again + loopDeferStackDepth.pop(); return output; case for_loop: + // 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),0,-3) + generate(children[1], enclosingObject) + ";" + strSlice(generate(children[2], enclosingObject),0,-3) + ")\n\t" + generate(children[3], enclosingObject); + // 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); if (children.size()) return "return " + generate(children[0], enclosingObject); else return "return"; + 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); + 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); + return output + "continue"; + case defer_statement: + deferDoubleStack.back().push_back(children[0]); + return "/*defer " + generate(children[0], enclosingObject) + "*/"; case assignment_statement: return generate(children[0], enclosingObject) + " = " + generate(children[1], enclosingObject); case declaration_statement: diff --git a/tests/test_break_continue_defer.expected_results b/tests/test_break_continue_defer.expected_results new file mode 100644 index 0000000..c2e76b0 --- /dev/null +++ b/tests/test_break_continue_defer.expected_results @@ -0,0 +1,16 @@ +1 +happens every time, even when breaking or continueing +happens every time, even when breaking or continueing +3 +happens every time, even when breaking or continueing +happens every time, even when breaking or continueing +5 +happens every time, even when breaking or continueing +happens every time, even when breaking or continueing +7 +happens every time, even when breaking or continueing +happens every time, even when breaking or continueing +happens every time, even when breaking or continueing +first +last +deferred diff --git a/tests/test_break_continue_defer.krak b/tests/test_break_continue_defer.krak new file mode 100644 index 0000000..c143c8a --- /dev/null +++ b/tests/test_break_continue_defer.krak @@ -0,0 +1,20 @@ +import io:* + +fun main():int { + for (var i = 1; i < 10; i++;) { + { + defer println("happens every time, even when breaking or continueing") + if (i % 2 == 0) + continue + if (i == 9) + break + println(i) + } + } + { + println("first") + defer println("deferred") + println("last") + } + return 0 +}