diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 842a2d5..2262fbf 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -17,7 +17,10 @@ char* createCodegenError(char* what) { ResultType(SolsType, charptr) getNodeType(SolsNode* node, SolsScope* scope) { switch (node->type) { - case SNT_PUTS: { + case SNT_PUTS: + case SNT_IF: + case SNT_WHILE: + case SNT_CODE_BLOCK: { return Error(SolsType, charptr, "Specified node does not return data"); } case SNT_OP_SET: { @@ -560,18 +563,117 @@ ResultType(GroundProgram, charptr) generateCodeBlockNode(SolsNode* node, SolsSco return Success(GroundProgram, charptr, groundCreateProgram()); } +static inline ResultType(GroundProgram, charptr) generateWhileNode(SolsNode* node, SolsScope* scope) { + GroundProgram gp = groundCreateProgram(); + + char* start_label = malloc(64); + snprintf(start_label, 64, "__SOLS_WHILE_START_%zu", scope->tmpCounter++); + + char* end_label = malloc(64); + snprintf(end_label, 64, "__SOLS_WHILE_END_%zu", scope->tmpCounter++); + + GroundInstruction start_label_inst = groundCreateInstruction(CREATELABEL); + groundAddReferenceToInstruction(&start_label_inst, groundCreateReference(LABEL, start_label)); + groundAddInstructionToProgram(&gp, start_label_inst); + + ResultType(GroundProgram, charptr) cond_code = generateCode(&node->children.at[0], scope); + if (cond_code.error) return cond_code; + for (size_t i = 0; i < cond_code.as.success.size; i++) { + groundAddInstructionToProgram(&gp, cond_code.as.success.instructions[i]); + } + + ResultType(SolsType, charptr) type = getNodeType(&node->children.at[0], scope); + if (type.error) return Error(GroundProgram, charptr, type.as.error); + if (type.as.success.type != STT_BOOL) { + return Error(GroundProgram, charptr, "While condition must be a boolean"); + } + + char* tmp_inverted_cond = malloc(64); + snprintf(tmp_inverted_cond, 64, "__SOLS_IF_COND_NOT_%zu", scope->tmpCounter++); + GroundInstruction not_inst = groundCreateInstruction(NOT); + groundAddReferenceToInstruction(¬_inst, node->children.at[0].accessArg); + groundAddReferenceToInstruction(¬_inst, groundCreateReference(DIRREF, tmp_inverted_cond)); + groundAddInstructionToProgram(&gp, not_inst); + + GroundInstruction if_inst = groundCreateInstruction(IF); + groundAddReferenceToInstruction(&if_inst, groundCreateReference(VALREF, tmp_inverted_cond)); + groundAddReferenceToInstruction(&if_inst, groundCreateReference(LINEREF, end_label)); + groundAddInstructionToProgram(&gp, if_inst); + + ResultType(GroundProgram, charptr) body_code = generateCode(&node->children.at[1], scope); + if (body_code.error) return body_code; + for (size_t i = 0; i < body_code.as.success.size; i++) { + groundAddInstructionToProgram(&gp, body_code.as.success.instructions[i]); + } + + GroundInstruction jump_inst = groundCreateInstruction(JUMP); + groundAddReferenceToInstruction(&jump_inst, groundCreateReference(LINEREF, start_label)); + groundAddInstructionToProgram(&gp, jump_inst); + + GroundInstruction end_label_inst = groundCreateInstruction(CREATELABEL); + groundAddReferenceToInstruction(&end_label_inst, groundCreateReference(LABEL, end_label)); + groundAddInstructionToProgram(&gp, end_label_inst); + + return Success(GroundProgram, charptr, gp); +} + +static inline ResultType(GroundProgram, charptr) generateIfNode(SolsNode* node, SolsScope* scope) { + GroundProgram gp = groundCreateProgram(); + + ResultType(GroundProgram, charptr) cond_code = generateCode(&node->children.at[0], scope); + if (cond_code.error) return cond_code; + for (size_t i = 0; i < cond_code.as.success.size; i++) { + groundAddInstructionToProgram(&gp, cond_code.as.success.instructions[i]); + } + + ResultType(SolsType, charptr) type = getNodeType(&node->children.at[0], scope); + if (type.error) return Error(GroundProgram, charptr, type.as.error); + if (type.as.success.type != STT_BOOL) { + return Error(GroundProgram, charptr, "If condition must be a boolean"); + } + + char* end_label = malloc(64); + snprintf(end_label, 64, "__SOLS_IF_END_%zu", scope->tmpCounter++); + + char* tmp_inverted_cond = malloc(64); + snprintf(tmp_inverted_cond, 64, "__SOLS_IF_COND_NOT_%zu", scope->tmpCounter++); + GroundInstruction not_inst = groundCreateInstruction(NOT); + groundAddReferenceToInstruction(¬_inst, node->children.at[0].accessArg); + groundAddReferenceToInstruction(¬_inst, groundCreateReference(DIRREF, tmp_inverted_cond)); + groundAddInstructionToProgram(&gp, not_inst); + + GroundInstruction if_inst = groundCreateInstruction(IF); + groundAddReferenceToInstruction(&if_inst, groundCreateReference(VALREF, tmp_inverted_cond)); + groundAddReferenceToInstruction(&if_inst, groundCreateReference(LINEREF, end_label)); + groundAddInstructionToProgram(&gp, if_inst); + + ResultType(GroundProgram, charptr) body_code = generateCode(&node->children.at[1], scope); + if (body_code.error) return body_code; + for (size_t i = 0; i < body_code.as.success.size; i++) { + groundAddInstructionToProgram(&gp, body_code.as.success.instructions[i]); + } + + GroundInstruction label_inst = groundCreateInstruction(CREATELABEL); + groundAddReferenceToInstruction(&label_inst, groundCreateReference(LABEL, end_label)); + groundAddInstructionToProgram(&gp, label_inst); + + return Success(GroundProgram, charptr, gp); +} + ResultType(GroundProgram, charptr) generateCode(SolsNode* node, SolsScope* scope) { GroundProgram program = groundCreateProgram(); - // Generate code for all children before generating this node's code - for (size_t i = 0; i < node->children.count; i++) { - ResultType(GroundProgram, charptr) generated = generateCode(&node->children.at[i], scope); - if (generated.error) { - return Error(GroundProgram, charptr, createCodegenError(generated.as.error)); - } - for (size_t j = 0; j < generated.as.success.size; j++) { - groundAddInstructionToProgram(&program, generated.as.success.instructions[j]); + if (node->type != SNT_IF && node->type != SNT_WHILE) { + // Generate code for all children before generating this node's code + for (size_t i = 0; i < node->children.count; i++) { + ResultType(GroundProgram, charptr) generated = generateCode(&node->children.at[i], scope); + if (generated.error) { + return Error(GroundProgram, charptr, createCodegenError(generated.as.error)); + } + for (size_t j = 0; j < generated.as.success.size; j++) { + groundAddInstructionToProgram(&program, generated.as.success.instructions[j]); + } } } @@ -591,6 +693,8 @@ ResultType(GroundProgram, charptr) generateCode(SolsNode* node, SolsScope* scope case SNT_OP_LESSER: generate(Lesser); case SNT_OP_EQLESSER: generate(EqLesser); case SNT_CODE_BLOCK: generate(CodeBlock); + case SNT_IF: generate(If); + case SNT_WHILE: generate(While); } return Success(GroundProgram, charptr, program); } diff --git a/src/parser/parser.c b/src/parser/parser.c index 4e6ebf6..fb8b54a 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -9,6 +9,8 @@ SolsTokenPrecedence getPrecedence(SolsToken *token) { switch (token->type) { case STT_LINE_END: return STP_NEWLINE; case STT_KW_PUTS: return STP_PUTS; + case STT_KW_IF: return STP_IF; + case STT_KW_WHILE: return STP_WHILE; case STT_OP_ADDTO: case STT_OP_SUBTO: case STT_OP_MULTO: @@ -998,6 +1000,130 @@ static inline ResultType(Nothing, charptr) parseCodeBlock(SolsParser* parser) { return Success(Nothing, charptr, {}); } +static inline ResultType(Nothing, charptr) parseWhile(SolsParser* parser) { + // Collect tokens for node + ResultType(SolsTokens, charptr) tokens = createSolsTokens(); + if (tokens.error) { + return Error(Nothing, charptr, tokens.as.error); + } + + ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); + if (peek.error) return Error(Nothing, charptr, "ruh roh"); + + for (;;) { + ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); + if (peek.error) break; + if (peek.as.success.type == STT_OPEN_CURLY) { + break; + } + parserConsume(parser); + addTokenToSolsTokens(&tokens.as.success, peek.as.success); + } + + // Create node + ResultType(SolsNode, charptr) node = createSolsNode(SNT_WHILE); + if (node.error) return Error(Nothing, charptr, node.as.error); + node.as.success.line = peek.as.success.line; + + // Parse selected tokens + ResultType(SolsParser, charptr) whileParser = createSolsParser(&tokens.as.success); + if (whileParser.error) return Error(Nothing, charptr, whileParser.as.error); + whileParser.as.success.currentParent = &whileParser.as.success.output; + ResultType(Nothing, charptr) parsed = parse(&whileParser.as.success); + + // Add any error messages from parsing + if (parsed.error) { + addToParserErrors(parser, parsed.as.error); + return Success(Nothing, charptr, {}); + } + + // Copy nodes into the sols node + for (size_t i = 0; i < whileParser.as.success.output.children.count; i++) { + addChildToSolsNode(&node.as.success, whileParser.as.success.output.children.at[i]); + } + + addChildToSolsNode(parser->currentParent, node.as.success); + + if(parserPeek(parser, 1).as.success.type != STT_OPEN_CURLY) { + return Error(Nothing, charptr, "Expecting opening curly brace for while loop body"); + } + + parserConsume(parser); + + ResultType(Nothing, charptr) res = parseCodeBlock(parser); + if(res.error) return res; + + // Last child of parent is code block, we need to move it + SolsNode codeBlock = parser->currentParent->children.at[parser->currentParent->children.count - 1]; + parser->currentParent->children.count--; + addChildToSolsNode(&node.as.success, codeBlock); + + + return Success(Nothing, charptr, {}); +} + +static inline ResultType(Nothing, charptr) parseIf(SolsParser* parser) { + // Collect tokens for node + ResultType(SolsTokens, charptr) tokens = createSolsTokens(); + if (tokens.error) { + return Error(Nothing, charptr, tokens.as.error); + } + + ResultType(SolsToken, Nothing) peek = parserPeek(parser, 0); + if (peek.error) return Error(Nothing, charptr, "ruh roh"); + + for (;;) { + ResultType(SolsToken, Nothing) peek = parserPeek(parser, 1); + if (peek.error) break; + if (peek.as.success.type == STT_OPEN_CURLY) { + break; + } + parserConsume(parser); + addTokenToSolsTokens(&tokens.as.success, peek.as.success); + } + + // Create node + ResultType(SolsNode, charptr) node = createSolsNode(SNT_IF); + if (node.error) return Error(Nothing, charptr, node.as.error); + node.as.success.line = peek.as.success.line; + + // Parse selected tokens + ResultType(SolsParser, charptr) ifParser = createSolsParser(&tokens.as.success); + if (ifParser.error) return Error(Nothing, charptr, ifParser.as.error); + ifParser.as.success.currentParent = &ifParser.as.success.output; + ResultType(Nothing, charptr) parsed = parse(&ifParser.as.success); + + // Add any error messages from parsing + if (parsed.error) { + addToParserErrors(parser, parsed.as.error); + return Success(Nothing, charptr, {}); + } + + // Copy nodes into the sols node + for (size_t i = 0; i < ifParser.as.success.output.children.count; i++) { + addChildToSolsNode(&node.as.success, ifParser.as.success.output.children.at[i]); + } + + addChildToSolsNode(parser->currentParent, node.as.success); + + if(parserPeek(parser, 1).as.success.type != STT_OPEN_CURLY) { + return Error(Nothing, charptr, "Expecting opening curly brace for if statement body"); + } + + parserConsume(parser); + + ResultType(Nothing, charptr) res = parseCodeBlock(parser); + if(res.error) return res; + + // Last child of parent is code block, we need to move it + SolsNode codeBlock = parser->currentParent->children.at[parser->currentParent->children.count - 1]; + parser->currentParent->children.count--; + addChildToSolsNode(&node.as.success, codeBlock); + + + return Success(Nothing, charptr, {}); +} + static inline ResultType(Nothing, charptr) parseCloseCurly(SolsParser* parser) { (void)parser; return Error(Nothing, charptr, "Extra closing curly brace"); @@ -1014,6 +1140,8 @@ ResultType(Nothing, charptr) parse(SolsParser* parser) { case STT_IDENTIFIER: PARSER_HANDLE(Identifier); case STT_LITERAL: PARSER_HANDLE(Literal); case STT_KW_PUTS: PARSER_HANDLE(Puts); + case STT_KW_IF: PARSER_HANDLE(If); + case STT_KW_WHILE: PARSER_HANDLE(While); case STT_OP_SET: PARSER_HANDLE(Set); case STT_OP_ADD: PARSER_HANDLE(Add); case STT_OP_SUB: PARSER_HANDLE(Sub); diff --git a/src/parser/parser.h b/src/parser/parser.h index 86b97dd..4968e4e 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -14,6 +14,8 @@ typedef enum SolsTokenPrecedence { STP_NEWLINE = 0, STP_PUTS = 1, + STP_IF = 1, + STP_WHILE = 1, STP_COMPARE = 2, STP_SET = 3, STP_FUNCTION = 4, diff --git a/test.sols b/test.sols index 1e7b0c0..eedb3a1 100644 --- a/test.sols +++ b/test.sols @@ -1,33 +1,15 @@ -// heheheha comment +a = 0 +b = 1 +n = 92 -x = 5 -puts x + 3 -puts 5 * 7 -puts 3.0 / 2 -puts 10 - 3 -puts "dingle" + "dongle" +i = 0 -// Uh oh it's the order of operations +while i != n { + temp = a + b + a = b + b = temp -puts 5 + 3 * 7 -puts 5 * 3 + 7 - -// Let's do some equalities -puts 5 == 5 -puts 3 == 2 -puts "dingus" == "dongus" -puts 3.14 == 3.14 -puts 3 + 5 == 3 + 5 - -// And now some of the other thingys -puts 3 > 5 -puts 3 < 5 - -puts 10 != 3 -puts 3 >= 2 -puts 3 >= 3 - -// Put stuff inside a code block, what could possibly go wrong -{ - puts "Hi from the code block!" + i = i + 1 } + +puts a