Files
highground-fork/src/parser.cpp

1641 lines
85 KiB
C++

#include "parser.h"
#include "error.h"
#include "lexer.h"
#include "solstice_stdlib.h"
#include "argparser.h"
#include <groundvm.h>
#include <cstring>
#include <string>
#include <cstdlib>
#define parseOneToken(token) Parser({token}).parse().children[0]
namespace Solstice {
namespace Parser {
char* copyString(std::string s) {
char* str = (char*) malloc(s.size() + 1);
strcpy(str, s.c_str());
return str;
}
bool isTemp(std::string id) {
return id.rfind("tmp_", 0) == 0;
}
std::map<std::string, std::string> variables;
std::string checkNodeReturnType(SolNode i) {
switch (i.nodeType) {
case SolNodeType::Identifier: {
if (variables.find(i.outputId) != variables.end()) {
return variables[i.outputId];
} else {
Error::syntaxError("Unknown variable " + i.outputId, i.line, i.lineContent);
}
break;
}
case SolNodeType::Value: {
return i.data.getTypeString();
}
case SolNodeType::Equal:
case SolNodeType::Inequal:
case SolNodeType::Greater:
case SolNodeType::Lesser:
case SolNodeType::EqGreater:
case SolNodeType::EqLesser:
return "bool";
break;
case SolNodeType::Add:
{
if (checkNodeReturnType(i.children[0]) == "string" && checkNodeReturnType(i.children[1]) == "string") {
return "string";
}
}
case SolNodeType::Subtract:
case SolNodeType::Multiply:
case SolNodeType::Divide:
{
if (checkNodeReturnType(i.children[0]) == "double") {
return "double";
}
if (checkNodeReturnType(i.children[1]) == "double") {
return "double";
}
return "int";
break;
}
case SolNodeType::FunctionCall: {
std::string funcName = i.children[0].outputId;
if (variables.find(funcName) == variables.end()) {
Error::syntaxError("Unknown function " + funcName, i.line, i.lineContent);
}
std::string signature = variables[funcName];
// Parse signature: fun(arg1, arg2) retType
if (signature.rfind("fun(", 0) != 0) {
// Not a function signature, maybe it's a variable being called?
Error::typingError(funcName + " is not a function", i.line, i.lineContent);
}
size_t endParen = signature.find(')');
if (endParen == std::string::npos) {
Error::typingError("Invalid function signature for " + funcName, i.line, i.lineContent);
}
std::string argsStr = signature.substr(4, endParen - 4);
std::string retType = signature.substr(endParen + 2); // skip ") "
std::vector<std::string> expectedArgs;
if (!argsStr.empty()) {
size_t start = 0;
size_t end = argsStr.find(", ");
while (end != std::string::npos) {
expectedArgs.push_back(argsStr.substr(start, end - start));
start = end + 2;
end = argsStr.find(", ", start);
}
expectedArgs.push_back(argsStr.substr(start));
}
// Check arguments
// children[0] is function name, children[1..] are args
if (i.children.size() - 1 != expectedArgs.size()) {
Error::typingError("Expected " + std::to_string(expectedArgs.size()) + " arguments for " + funcName + ", got " + std::to_string(i.children.size() - 1), i.line, i.lineContent);
return "none";
}
for (size_t k = 0; k < expectedArgs.size(); ++k) {
std::string argType = checkNodeReturnType(i.children[k + 1]);
if (argType != expectedArgs[k]) {
Error::typingError("Expected argument " + std::to_string(k + 1) + " of " + funcName + " to be " + expectedArgs[k] + ", got " + argType, i.children[k + 1].line, i.children[k + 1].lineContent);
}
}
return retType;
break;
}
case SolNodeType::Puts:
case SolNodeType::If:
case SolNodeType::While:
case SolNodeType::CodeBlock:
case SolNodeType::Set:
case SolNodeType::Root:
case SolNodeType::None:
case SolNodeType::FunctionDef:
return "none";
break;
}
return "none";
}
// SolData Implementation
SolData::SolData(int64_t in) : data(in), type(SolDataType::Int) {}
SolData::SolData(double in) : data(in), type(SolDataType::Double) {}
SolData::SolData(std::string in) : data(in), type(SolDataType::String) {}
SolData::SolData(char in) : data(in), type(SolDataType::Char) {}
SolData::SolData(bool in) : data(in), type(SolDataType::Bool) {}
SolData::SolData(SolFunction in) : data(in), type(SolDataType::Function) {}
std::optional<int64_t> SolData::getInt() {
if (type == SolDataType::Int) {
return std::get<int64_t>(data);
} else {
return {};
}
}
std::optional<double> SolData::getDouble() {
if (type == SolDataType::Double) {
return std::get<double>(data);
} else {
return {};
}
}
std::optional<std::string> SolData::getString() {
if (type == SolDataType::String) {
return std::get<std::string>(data);
} else {
return {};
}
}
std::optional<char> SolData::getChar() {
if (type == SolDataType::Char) {
return std::get<char>(data);
} else {
return {};
}
}
std::optional<bool> SolData::getBool() {
if (type == SolDataType::Bool) {
return std::get<bool>(data);
} else {
return {};
}
}
std::optional<SolFunction> SolData::getFunction() {
if (type == SolDataType::Function) {
return std::get<SolFunction>(data);
} else {
return {};
}
}
std::string SolData::getTypeString() {
switch (type) {
case SolDataType::Int: return "int";
case SolDataType::Double: return "double";
case SolDataType::String: return "string";
case SolDataType::Char: return "char";
case SolDataType::Bool: return "bool";
case SolDataType::Function: return "function";
case SolDataType::None: return "none";
}
return "none";
}
// SolNode Implementation
SolNode::SolNode(SolNodeType nodeType) : nodeType(nodeType) {}
SolNode::SolNode(SolNodeType nodeType, SolData data) : data(data), nodeType(nodeType) {}
void SolNode::addNode(SolNode in) {
children.push_back(in);
}
void SolNode::setValue(SolData in) {
data = in;
}
std::string currentFunctionRetType = "";
const std::vector<SolGroundCodeBlock> SolNode::generateCode() {
std::vector<SolGroundCodeBlock> code;
if (nodeType == SolNodeType::Root) {
// Compile and insert standard library
if (!ArgParser::nostdlib) {
auto parsedStdlib = Parser(Lexer(getStdLib()).lex()).parse();
parsedStdlib.nodeType = SolNodeType::None;
code = parsedStdlib.generateCode();
}
}
if (nodeType != SolNodeType::If && nodeType != SolNodeType::While && nodeType != SolNodeType::Struct) for (auto& child : children) {
auto childCode = child.generateCode();
code.insert(code.end(), childCode.begin(), childCode.end());
}
switch (nodeType) {
case SolNodeType::Value: {
outputId = "tmp_" + std::to_string(tmpIdIterator++);
SolGroundCodeBlock codeBlock;
GroundInstruction gi = groundCreateInstruction(SET);
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
switch (data.type) {
case SolDataType::Int: {
auto dataopt = data.getInt();
if (dataopt) {
groundAddValueToInstruction(&gi, groundCreateValue(INT, dataopt.value()));
}
break;
}
case SolDataType::Double: {
auto dataopt = data.getDouble();
if (dataopt) {
groundAddValueToInstruction(&gi, groundCreateValue(DOUBLE, dataopt.value()));
}
break;
}
case SolDataType::String: {
auto dataopt = data.getString();
if (dataopt) {
char* str = (char*) malloc(dataopt.value().size() + 1);
strcpy(str, dataopt.value().c_str());
groundAddValueToInstruction(&gi, groundCreateValue(STRING, str));
}
break;
}
case SolDataType::Char: {
auto dataopt = data.getChar();
if (dataopt) {
groundAddValueToInstruction(&gi, groundCreateValue(CHAR, dataopt.value()));
}
break;
}
case SolDataType::Bool: {
auto dataopt = data.getBool();
if (dataopt) {
groundAddValueToInstruction(&gi, groundCreateValue(BOOL, dataopt.value()));
}
break;
}
}
codeBlock.code.push_back(gi);
code.push_back(codeBlock);
break;
}
case SolNodeType::Add: {
ensure3(children[0], "int", "double", "string", "operator '+'");
ensure3(children[1], "int", "double", "string", "operator '+'");
ensuresame(children[0], children[1], "operator '+'");
SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(ADD);
if (children.size() < 2) {
Error::typingError("Need more stuff to add", children[0].line, children[0].lineContent);
}
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[1].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
codeBlock.code.push_back(gi);
if (isTemp(children[0].outputId)) codeBlock.toBeDropped.push_back(children[0].outputId);
if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId);
code.push_back(codeBlock);
break;
}
case SolNodeType::Subtract: {
ensure2(children[0], "int", "double", "operator '-'");
ensure2(children[1], "int", "double", "operator '-'");
SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(SUBTRACT);
if (children.size() < 2) {
Error::typingError("Need more stuff to subtract", children[0].line, children[0].lineContent);
}
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[1].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
codeBlock.code.push_back(gi);
if (isTemp(children[0].outputId)) codeBlock.toBeDropped.push_back(children[0].outputId);
if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId);
code.push_back(codeBlock);
break;
}
case SolNodeType::Multiply: {
ensure2(children[0], "int", "double", "operator '*'");
ensure2(children[1], "int", "double", "operator '*'");
SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(MULTIPLY);
if (children.size() < 2) {
Error::typingError("Need more stuff to multiply", children[0].line, children[0].lineContent);
}
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[1].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
codeBlock.code.push_back(gi);
if (isTemp(children[0].outputId)) codeBlock.toBeDropped.push_back(children[0].outputId);
if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId);
code.push_back(codeBlock);
break;
}
case SolNodeType::Divide: {
ensure2(children[0], "int", "double", "operator '/'");
ensure2(children[1], "int", "double", "operator '/'");
SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(DIVIDE);
if (children.size() < 2) {
Error::typingError("Need more stuff to divide", children[0].line, children[0].lineContent);
}
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[1].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
codeBlock.code.push_back(gi);
if (isTemp(children[0].outputId)) codeBlock.toBeDropped.push_back(children[0].outputId);
if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId);
code.push_back(codeBlock);
break;
}
case SolNodeType::Equal: {
ensuresame(children[0], children[1], "operator '=='");
SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(EQUAL);
if (children.size() < 2) {
Error::typingError("Need more stuff to equal", children[0].line, children[0].lineContent);
}
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[1].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
codeBlock.code.push_back(gi);
if (isTemp(children[0].outputId)) codeBlock.toBeDropped.push_back(children[0].outputId);
if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId);
code.push_back(codeBlock);
break;
}
case SolNodeType::Inequal: {
ensuresame(children[0], children[1], "operator '!='");
SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(INEQUAL);
if (children.size() < 2) {
Error::typingError("Need more stuff to inequal", children[0].line, children[0].lineContent);
}
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[1].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
codeBlock.code.push_back(gi);
if (isTemp(children[0].outputId)) codeBlock.toBeDropped.push_back(children[0].outputId);
if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId);
code.push_back(codeBlock);
break;
}
case SolNodeType::Greater: {
ensuresame(children[0], children[1], "operator '>'");
SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(GREATER);
if (children.size() < 2) {
Error::typingError("Need more stuff to greater", children[0].line, children[0].lineContent);
}
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[1].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
codeBlock.code.push_back(gi);
if (isTemp(children[0].outputId)) codeBlock.toBeDropped.push_back(children[0].outputId);
if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId);
code.push_back(codeBlock);
break;
}
case SolNodeType::Lesser: {
ensuresame(children[0], children[1], "operator '<'");
SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(LESSER);
if (children.size() < 2) {
Error::typingError("Need more stuff to lesser", children[0].line, children[0].lineContent);
}
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[1].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
codeBlock.code.push_back(gi);
if (isTemp(children[0].outputId)) codeBlock.toBeDropped.push_back(children[0].outputId);
if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId);
code.push_back(codeBlock);
break;
}
case SolNodeType::EqGreater: {
ensuresame(children[0], children[1], "operator '>='");
SolGroundCodeBlock codeBlock;
if (children.size() < 2) {
Error::typingError("Need more stuff to inequal", children[0].line, children[0].lineContent);
}
outputId = "tmp_" + std::to_string(tmpIdIterator++);
std::string trueLabelIdString = "internal_true" + std::to_string(labelIterator++);
std::string falseLabelIdString = "internal_false" + std::to_string(labelIterator);
std::string endLabelIdString = "internal_end" + std::to_string(labelIterator);
char* trueLabelId = (char*) malloc(sizeof(char) * (trueLabelIdString.size() + 1));
strcpy(trueLabelId, trueLabelIdString.data());
char* falseLabelId = (char*) malloc(sizeof(char) * (falseLabelIdString.size() + 1));
strcpy(falseLabelId, falseLabelIdString.data());
char* endLabelId = (char*) malloc(sizeof(char) * (endLabelIdString.size() + 1));
strcpy(endLabelId, endLabelIdString.data());
// greater $tmp_0 $tmp_1 &cond
GroundInstruction gi = groundCreateInstruction(GREATER);
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[1].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, (char*) "internal_cond0"));
codeBlock.code.push_back(gi);
// if &cond %true
gi = groundCreateInstruction(IF);
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, (char*) "internal_cond0"));
groundAddReferenceToInstruction(&gi, groundCreateReference(LINEREF, trueLabelId));
codeBlock.code.push_back(gi);
// equal $tmp_0 $tmp_1 &cond
gi = groundCreateInstruction(EQUAL);
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[1].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, (char*) "internal_cond1"));
codeBlock.code.push_back(gi);
// if $cond %true
gi = groundCreateInstruction(IF);
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, (char*) "internal_cond1"));
groundAddReferenceToInstruction(&gi, groundCreateReference(LINEREF, trueLabelId));
codeBlock.code.push_back(gi);
// jump %false
gi = groundCreateInstruction(JUMP);
groundAddReferenceToInstruction(&gi, groundCreateReference(LINEREF, falseLabelId));
codeBlock.code.push_back(gi);
// @true
gi = groundCreateInstruction(CREATELABEL);
groundAddReferenceToInstruction(&gi, groundCreateReference(LABEL, trueLabelId));
codeBlock.code.push_back(gi);
// set &tmp_x true
gi = groundCreateInstruction(SET);
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
groundAddValueToInstruction(&gi, groundCreateValue(BOOL, true));
codeBlock.code.push_back(gi);
// jump %end
gi = groundCreateInstruction(JUMP);
groundAddReferenceToInstruction(&gi, groundCreateReference(LINEREF, endLabelId));
codeBlock.code.push_back(gi);
// @false
gi = groundCreateInstruction(CREATELABEL);
groundAddReferenceToInstruction(&gi, groundCreateReference(LABEL, falseLabelId));
codeBlock.code.push_back(gi);
// set &tmp_x false
gi = groundCreateInstruction(SET);
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
groundAddValueToInstruction(&gi, groundCreateValue(BOOL, false));
codeBlock.code.push_back(gi);
// @end
gi = groundCreateInstruction(CREATELABEL);
groundAddReferenceToInstruction(&gi, groundCreateReference(LABEL, endLabelId));
codeBlock.code.push_back(gi);
if (isTemp(children[0].outputId)) codeBlock.toBeDropped.push_back(children[0].outputId);
if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId);
code.push_back(codeBlock);
break;
}
case SolNodeType::EqLesser: {
ensuresame(children[0], children[1], "operator '<='");
SolGroundCodeBlock codeBlock;
if (children.size() < 2) {
Error::typingError("Need more stuff to inequal", children[0].line, children[0].lineContent);
}
outputId = "tmp_" + std::to_string(tmpIdIterator++);
std::string trueLabelIdString = "internal_true" + std::to_string(labelIterator++);
std::string falseLabelIdString = "internal_false" + std::to_string(labelIterator);
std::string endLabelIdString = "internal_end" + std::to_string(labelIterator);
char* trueLabelId = (char*) malloc(sizeof(char) * (trueLabelIdString.size() + 1));
strcpy(trueLabelId, trueLabelIdString.data());
char* falseLabelId = (char*) malloc(sizeof(char) * (falseLabelIdString.size() + 1));
strcpy(falseLabelId, falseLabelIdString.data());
char* endLabelId = (char*) malloc(sizeof(char) * (endLabelIdString.size() + 1));
strcpy(endLabelId, endLabelIdString.data());
// lesser $tmp_0 $tmp_1 &cond
GroundInstruction gi = groundCreateInstruction(LESSER);
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[1].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, (char*) "internal_cond0"));
codeBlock.code.push_back(gi);
// if &cond %true
gi = groundCreateInstruction(IF);
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, (char*) "internal_cond0"));
groundAddReferenceToInstruction(&gi, groundCreateReference(LINEREF, trueLabelId));
codeBlock.code.push_back(gi);
// equal $tmp_0 $tmp_1 &cond
gi = groundCreateInstruction(EQUAL);
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[1].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, (char*) "internal_cond1"));
codeBlock.code.push_back(gi);
// if $cond %true
gi = groundCreateInstruction(IF);
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, (char*) "internal_cond1"));
groundAddReferenceToInstruction(&gi, groundCreateReference(LINEREF, trueLabelId));
codeBlock.code.push_back(gi);
// jump %false
gi = groundCreateInstruction(JUMP);
groundAddReferenceToInstruction(&gi, groundCreateReference(LINEREF, falseLabelId));
codeBlock.code.push_back(gi);
// @true
gi = groundCreateInstruction(CREATELABEL);
groundAddReferenceToInstruction(&gi, groundCreateReference(LABEL, trueLabelId));
codeBlock.code.push_back(gi);
// set &tmp_x true
gi = groundCreateInstruction(SET);
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
groundAddValueToInstruction(&gi, groundCreateValue(BOOL, true));
codeBlock.code.push_back(gi);
// jump %end
gi = groundCreateInstruction(JUMP);
groundAddReferenceToInstruction(&gi, groundCreateReference(LINEREF, endLabelId));
codeBlock.code.push_back(gi);
// @false
gi = groundCreateInstruction(CREATELABEL);
groundAddReferenceToInstruction(&gi, groundCreateReference(LABEL, falseLabelId));
codeBlock.code.push_back(gi);
// set &tmp_x false
gi = groundCreateInstruction(SET);
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
groundAddValueToInstruction(&gi, groundCreateValue(BOOL, false));
codeBlock.code.push_back(gi);
// @end
gi = groundCreateInstruction(CREATELABEL);
groundAddReferenceToInstruction(&gi, groundCreateReference(LABEL, endLabelId));
codeBlock.code.push_back(gi);
if (isTemp(children[0].outputId)) codeBlock.toBeDropped.push_back(children[0].outputId);
if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId);
code.push_back(codeBlock);
break;
}
case SolNodeType::Puts: {
SolGroundCodeBlock codeBlock;
exists(children[0]);
GroundInstruction gi = groundCreateInstruction(PRINTLN);
if (children.size() < 1) {
Error::typingError("Need more stuff to puts", children[0].line, children[0].lineContent);
}
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
codeBlock.code.push_back(gi);
if (isTemp(children[0].outputId)) codeBlock.toBeDropped.push_back(children[0].outputId);
code.push_back(codeBlock);
break;
}
case SolNodeType::If: {
ensure(children[0], "bool", "if condition");
auto conditionCode = children[0].generateCode();
code.insert(code.end(), conditionCode.begin(), conditionCode.end());
outputId = "tmp_" + std::to_string(tmpIdIterator++);
SolGroundCodeBlock codeBlock;
codeBlock.toBeDropped.push_back(outputId);
if (isTemp(children[0].outputId)) codeBlock.toBeDropped.push_back(children[0].outputId);
GroundInstruction gi = groundCreateInstruction(NOT);
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
codeBlock.code.push_back(gi);
std::string labelIdString = "if_" + std::to_string(labelIterator++);
char* labelId = (char*) malloc(sizeof(char) * labelIdString.size() + 1);
strcpy(labelId, labelIdString.data());
GroundInstruction gi2 = groundCreateInstruction(IF);
groundAddReferenceToInstruction(&gi2, groundCreateReference(VALREF, copyString(outputId)));
groundAddReferenceToInstruction(&gi2, groundCreateReference(LINEREF, labelId));
codeBlock.code.push_back(gi2);
code.push_back(codeBlock);
for (size_t i = 1; i < children.size(); i++) {
auto childCode = children[i].generateCode();
code.insert(code.end(), childCode.begin(), childCode.end());
}
codeBlock.code.clear();
codeBlock.toBeDropped.clear();
GroundInstruction gi3 = groundCreateInstruction(CREATELABEL);
groundAddReferenceToInstruction(&gi3, groundCreateReference(LABEL, labelId));
codeBlock.code.push_back(gi3);
code.push_back(codeBlock);
break;
}
case SolNodeType::While: {
ensure(children[0], "bool", "while condition");
SolGroundCodeBlock startLabelBlock;
std::string startLabelIdString = "whilestart_" + std::to_string(labelIterator++);
std::string endLabelIdString = "whileend_" + std::to_string(labelIterator);
char* startLabelId = (char*) malloc(sizeof(char) * (startLabelIdString.size() + 1));
strcpy(startLabelId, startLabelIdString.data());
char* endLabelId = (char*) malloc(sizeof(char) * (endLabelIdString.size() + 1));
strcpy(endLabelId, endLabelIdString.data());
GroundInstruction startLabel = groundCreateInstruction(CREATELABEL);
groundAddReferenceToInstruction(&startLabel, groundCreateReference(LABEL, startLabelId));
startLabelBlock.code.push_back(startLabel);
code.push_back(startLabelBlock);
auto conditionCode = children[0].generateCode();
code.insert(code.end(), conditionCode.begin(), conditionCode.end());
SolGroundCodeBlock checkBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++);
checkBlock.toBeDropped.push_back(outputId);
if (isTemp(children[0].outputId)) checkBlock.toBeDropped.push_back(children[0].outputId);
GroundInstruction gi = groundCreateInstruction(NOT);
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(outputId)));
checkBlock.code.push_back(gi);
GroundInstruction gi2 = groundCreateInstruction(IF);
groundAddReferenceToInstruction(&gi2, groundCreateReference(VALREF, copyString(outputId)));
groundAddReferenceToInstruction(&gi2, groundCreateReference(LINEREF, endLabelId));
checkBlock.code.push_back(gi2);
code.push_back(checkBlock);
for (size_t i = 1; i < children.size(); i++) {
auto childCode = children[i].generateCode();
code.insert(code.end(), childCode.begin(), childCode.end());
}
SolGroundCodeBlock endBlock;
GroundInstruction gi3 = groundCreateInstruction(JUMP);
groundAddReferenceToInstruction(&gi3, groundCreateReference(LINEREF, startLabelId));
endBlock.code.push_back(gi3);
GroundInstruction gi4 = groundCreateInstruction(CREATELABEL);
groundAddReferenceToInstruction(&gi4, groundCreateReference(LABEL, endLabelId));
endBlock.code.push_back(gi4);
code.push_back(endBlock);
break;
}
case SolNodeType::Identifier: {
break;
}
case SolNodeType::Set: {
if (variables.find(children[0].outputId) != variables.end()) {
if (variables[children[0].outputId] != checkNodeReturnType(children[1])) {
Error::typingError("Cannot change type of this variable", children[0].line, children[0].lineContent);
}
}
exists(children[1]);
SolGroundCodeBlock codeBlock;
GroundInstruction setInstruction = groundCreateInstruction(SET);
groundAddReferenceToInstruction(&setInstruction, groundCreateReference(DIRREF, copyString(children[0].outputId)));
groundAddReferenceToInstruction(&setInstruction, groundCreateReference(VALREF, copyString(children[1].outputId)));
codeBlock.code.push_back(setInstruction);
if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId);
code.push_back(codeBlock);
// Make sure we know what the variable type is
variables[children[0].outputId] = checkNodeReturnType(children[1]);
break;
}
case SolNodeType::FunctionCall: {
checkNodeReturnType(*this);
exists(children[0]);
std::string fnToCall = children[0].outputId;
outputId = "tmp_" + std::to_string(tmpIdIterator++);
SolGroundCodeBlock codeBlock;
GroundInstruction callInstruction = groundCreateInstruction(CALL);
groundAddReferenceToInstruction(&callInstruction, groundCreateReference(FNREF, copyString(children[0].outputId)));
for (int i = 1; i < children.size(); i++) {
groundAddReferenceToInstruction(&callInstruction, groundCreateReference(VALREF, copyString(children[i].outputId)));
}
groundAddReferenceToInstruction(&callInstruction, groundCreateReference(DIRREF, copyString(outputId)));
codeBlock.code.push_back(callInstruction);
code.push_back(codeBlock);
break;
}
case SolNodeType::Return: {
std::string retType = checkNodeReturnType(children[0]);
if (currentFunctionRetType != "" && retType != currentFunctionRetType) {
Error::typingError("Expected return type " + currentFunctionRetType + " but got " + retType, children[0].line, children[0].lineContent);
}
SolGroundCodeBlock codeBlock;
GroundInstruction returnInstruction = groundCreateInstruction(RETURN);
groundAddReferenceToInstruction(&returnInstruction, groundCreateReference(VALREF, copyString(children[0].outputId)));
codeBlock.code.push_back(returnInstruction);
code.push_back(codeBlock);
break;
}
case SolNodeType::FunctionDef: {
auto functionopt = data.getFunction();
if (functionopt.has_value()) {
SolFunction function = functionopt.value();
SolGroundCodeBlock codeBlock;
GroundInstruction inst = groundCreateInstruction(FUN);
char* fnName = (char*) malloc(sizeof(char) * (function.name.size() + 1));
strcpy(fnName, function.name.c_str());
char* retType = (char*) malloc(sizeof(char) * (function.returnType.size() + 1));
strcpy(retType, function.returnType.c_str());
groundAddReferenceToInstruction(&inst, groundCreateReference(FNREF, fnName));
groundAddReferenceToInstruction(&inst, groundCreateReference(TYPEREF, retType));
// Scope Management
auto variablebackup = variables;
std::string oldRetType = currentFunctionRetType;
variables.clear();
currentFunctionRetType = function.returnType;
for (auto& pair : function.parameters) {
groundAddReferenceToInstruction(&inst, groundCreateReference(TYPEREF, copyString(pair.second)));
groundAddReferenceToInstruction(&inst, groundCreateReference(DIRREF, copyString(pair.first)));
variables[pair.first] = pair.second; // Correctly map Name -> Type
}
codeBlock.code.push_back(inst);
code.push_back(codeBlock);
codeBlock.code.clear();
// Generate body code with local variables visible
auto childCode = function.content->generateCode();
code.insert(code.end(), childCode.begin(), childCode.end());
codeBlock.code.push_back(groundCreateInstruction(ENDFUN));
code.push_back(codeBlock);
// Restore Scope
variables = variablebackup;
currentFunctionRetType = oldRetType;
}
break;
}
case SolNodeType::InlineGround: {
GroundProgram program = groundParseFile(ground.c_str());
SolGroundCodeBlock codeBlock;
for (size_t i = 0; i < program.size; i++) {
codeBlock.code.push_back(program.instructions[i]);
}
code.push_back(codeBlock);
break;
}
case SolNodeType::Struct: {
//SolGroundCodeBlock preProcessCodeBlock;
SolGroundCodeBlock codeBlock;
GroundInstruction gi = groundCreateInstruction(STRUCT);
// struct -name
groundAddReferenceToInstruction(&gi, groundCreateReference(TYPEREF, copyString(outputId)));
codeBlock.code.push_back(gi);
// contents of struct
for (SolNode& child : children) {
if (child.nodeType == SolNodeType::Identifier) {
gi = groundCreateInstruction(SET);
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, copyString(child.outputId)));
SolNode& valNode = child.children[0];
if (valNode.nodeType == SolNodeType::Value) {
GroundValue gv;
switch (valNode.data.type) {
case SolDataType::String: gv = groundCreateValue(STRING, copyString(valNode.data.getString().value())); break;
case SolDataType::Int: gv = groundCreateValue(INT, valNode.data.getInt().value()); break;
case SolDataType::Double: gv = groundCreateValue(DOUBLE, valNode.data.getDouble().value()); break;
case SolDataType::Char: gv = groundCreateValue(CHAR, valNode.data.getChar().value()); break;
case SolDataType::Bool: gv = groundCreateValue(BOOL, valNode.data.getBool().value()); break;
default: Error::syntaxError("Struct member must be assigned a value or identifier", valNode.line, valNode.lineContent); break;
}
groundAddValueToInstruction(&gi, gv);
} else if (child.nodeType == SolNodeType::Identifier) {
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, copyString(valNode.outputId)));
} else {
Error::syntaxError("Struct member must be assigned a value or identifier", valNode.line, valNode.lineContent);
}
codeBlock.code.push_back(gi);
}
if (child.nodeType == SolNodeType::FunctionDef) {
auto fnBlock = child.generateCode();
for (const auto& code : fnBlock) {
codeBlock.code.insert(codeBlock.code.end(), code.code.begin(), code.code.end());
}
}
}
codeBlock.code.push_back(groundCreateInstruction(ENDSTRUCT));
code.push_back(codeBlock);
break;
}
default: {}
}
return code;
}
// Parser Implementation
Parser::Parser(std::vector<Token> in) : tokensToParse(in) {}
std::optional<Token> Parser::peek(int ahead) {
if (current + ahead < size) {
return tokensToParse[current + ahead];
} else {
return {};
}
}
std::optional<Token> Parser::consume() {
if (current < size) {
return tokensToParse[current++];
} else {
return {};
}
}
// Helper functions
bool Parser::isInt(std::string in) {
for (const char& c : in) {
if (!std::isdigit(c)) {
return false;
}
}
return true;
}
bool Parser::isDouble(std::string in) {
bool foundDot = false;
for (const char& c : in) {
if (!std::isdigit(c)) {
if (!foundDot && c == '.') {
foundDot = true;
continue;
}
return false;
}
}
return true;
}
bool Parser::isString(std::string in) {
if (in.size() > 1 && in[0] == '"' && in.back() == '"') {
return true;
}
return false;
}
bool Parser::isChar(std::string in) {
if (in.size() == 3 && in[0] == '\'' && in.back() == '\'') {
return true;
}
return false;
}
bool Parser::isBool(std::string in) {
if (in == "true" || in == "false") {
return true;
}
return false;
}
SolDataType Parser::getDataType(std::string in) {
if (isInt(in)) {
return SolDataType::Int;
}
if (isDouble(in)) {
return SolDataType::Double;
}
if (isString(in)) {
return SolDataType::String;
}
if (isChar(in)) {
return SolDataType::Char;
}
if (isBool(in)) {
return SolDataType::Bool;
}
return SolDataType::None;
}
SolNodeType Parser::getNodeType(std::string in) {
if (getDataType(in) != SolDataType::None) {
return SolNodeType::Value;
}
if (in == "+") {
return SolNodeType::Add;
}
if (in == "-") {
return SolNodeType::Subtract;
}
if (in == "*") {
return SolNodeType::Multiply;
}
if (in == "/") {
return SolNodeType::Divide;
}
if (in == "=" || in == ":") {
return SolNodeType::Set;
}
if (in == "==") {
return SolNodeType::Equal;
}
if (in == "!=") {
return SolNodeType::Inequal;
}
if (in == ">=") {
return SolNodeType::EqGreater;
}
if (in == "<=") {
return SolNodeType::EqLesser;
}
if (in == ">") {
return SolNodeType::Greater;
}
if (in == "<") {
return SolNodeType::Lesser;
}
if (in == "puts") {
return SolNodeType::Puts;
}
if (in == "if") {
return SolNodeType::If;
}
if (in == "while") {
return SolNodeType::While;
}
if (in == "def") {
return SolNodeType::FunctionDef;
}
if (in == "return") {
return SolNodeType::Return;
}
if (in == "ground") {
return SolNodeType::InlineGround;
}
if (in == "struct") {
return SolNodeType::Struct;
}
if (in == "new") {
return SolNodeType::New;
}
if (in == "{") {
return SolNodeType::CodeBlockStart;
}
if (in == "}") {
return SolNodeType::CodeBlockEnd;
}
if (in == "(") {
return SolNodeType::BracketStart;
}
if (in == ")") {
return SolNodeType::BracketEnd;
}
return SolNodeType::Identifier;
}
int Parser::getPrecedence(std::string token) {
if (token == "*" || token == "/") return 2;
if (token == "+" || token == "-") return 1;
return 0;
}
SolNode Parser::parsePrimary() {
auto tokenopt = consume();
if (!tokenopt) {
Error::syntaxError("Unexpected end of input");
}
Token tokenObj = tokenopt.value();
std::string token = tokenObj.value;
SolNodeType type = getNodeType(token);
if (type == SolNodeType::Value) {
SolNode node(SolNodeType::Value);
node.line = tokenObj.line;
node.lineContent = tokenObj.lineContent;
switch (getDataType(token)) {
case SolDataType::Int: node.setValue((int64_t) std::stoll(token)); break;
case SolDataType::Double: node.setValue(std::stod(token)); break;
case SolDataType::String: node.setValue(token.substr(1, token.size() - 2)); break;
case SolDataType::Char: node.setValue(token[1]); break;
case SolDataType::Bool: node.setValue(token == "true"); break;
default: break;
}
return node;
}
if (type == SolNodeType::Identifier) {
SolNode idNode(SolNodeType::Identifier);
idNode.outputId = token;
idNode.line = tokenObj.line;
idNode.lineContent = tokenObj.lineContent;
auto peekOpt = peek();
if (peekOpt.has_value() && peekOpt.value().value == "(") {
consume(); // (
SolNode fnCallNode(SolNodeType::FunctionCall);
fnCallNode.line = tokenObj.line;
fnCallNode.lineContent = tokenObj.lineContent;
fnCallNode.addNode(idNode);
std::vector<Token> tokens;
size_t brackets = 1;
while (auto t = consume()) {
if (t.value().value == "(") brackets++;
if (t.value().value == ")") brackets--;
if (brackets == 0) break;
tokens.push_back(t.value());
}
if (brackets != 0) {
Error::syntaxError("Unclosed function call bracket", tokenObj.line, tokenObj.lineContent);
}
auto args = Parser(tokens).parse();
for(auto& child : args.children) fnCallNode.addNode(child);
return fnCallNode;
}
return idNode;
}
if (type == SolNodeType::BracketStart) {
std::vector<Token> tokens;
size_t brackets = 1;
while (auto t = consume()) {
if (t.value().value == "(") brackets++;
if (t.value().value == ")") brackets--;
if (brackets == 0) break;
tokens.push_back(t.value());
}
if (brackets != 0) {
Error::syntaxError("Unclosed bracket", tokenObj.line, tokenObj.lineContent);
}
auto inner = Parser(tokens).parse();
if (inner.children.size() > 0) return inner.children[0];
return SolNode(SolNodeType::None);
}
Error::syntaxError("Unexpected token in expression: " + token, tokenObj.line, tokenObj.lineContent);
return SolNode(SolNodeType::None);
}
SolNode Parser::parseExpression(int minPrec) {
SolNode lhs = parsePrimary();
while(true) {
auto opOpt = peek();
if (!opOpt) break;
Token opToken = opOpt.value();
std::string op = opToken.value;
int prec = getPrecedence(op);
if (prec < minPrec) break;
consume();
SolNode rhs = parseExpression(prec + 1);
SolNode newNode(getNodeType(op));
newNode.line = opToken.line;
newNode.lineContent = opToken.lineContent;
newNode.addNode(lhs);
newNode.addNode(rhs);
lhs = newNode;
}
return lhs;
}
SolNode Parser::parse() {
current = 0;
size = tokensToParse.size();
SolNode rootNode(SolNodeType::Root);
while (auto tokenopt = consume()) {
Token tokenObj = tokenopt.value();
std::string token = tokenObj.value;
switch (getNodeType(token)) {
case SolNodeType::Value: {
switch (getDataType(token)) {
case SolDataType::Int: {
SolNode intNode(SolNodeType::Value);
intNode.setValue((int64_t) std::stoll(token));
intNode.line = tokenObj.line;
intNode.lineContent = tokenObj.lineContent;
rootNode.addNode(intNode);
break;
}
case SolDataType::Double: {
SolNode doubleNode(SolNodeType::Value);
doubleNode.setValue(std::stod(token));
doubleNode.line = tokenObj.line;
doubleNode.lineContent = tokenObj.lineContent;
rootNode.addNode(doubleNode);
break;
}
case SolDataType::String: {
SolNode stringNode(SolNodeType::Value);
stringNode.setValue(token.substr(1, token.size() - 2));
stringNode.line = tokenObj.line;
stringNode.lineContent = tokenObj.lineContent;
rootNode.addNode(stringNode);
break;
}
case SolDataType::Char: {
SolNode charNode(SolNodeType::Value);
charNode.setValue(token[1]);
charNode.line = tokenObj.line;
charNode.lineContent = tokenObj.lineContent;
rootNode.addNode(charNode);
break;
}
case SolDataType::Bool: {
SolNode boolNode(SolNodeType::Value);
boolNode.setValue(token == "true");
boolNode.line = tokenObj.line;
boolNode.lineContent = tokenObj.lineContent;
rootNode.addNode(boolNode);
break;
}
}
break;
}
case SolNodeType::Add:
case SolNodeType::Subtract:
case SolNodeType::Multiply:
case SolNodeType::Divide:
{
SolNode opNode(getNodeType(token));
opNode.line = tokenObj.line;
opNode.lineContent = tokenObj.lineContent;
if (rootNode.children.empty()) {
Error::syntaxError("Expected operand before operator", tokenObj.line, tokenObj.lineContent);
}
opNode.addNode(rootNode.children.back());
rootNode.children.pop_back();
// Parse RHS with higher precedence
SolNode rhs = parseExpression(getPrecedence(token) + 1);
opNode.addNode(rhs);
rootNode.addNode(opNode);
break;
}
case SolNodeType::Equal:
case SolNodeType::Inequal:
case SolNodeType::Lesser:
case SolNodeType::Greater:
case SolNodeType::EqLesser:
case SolNodeType::EqGreater:
{
SolNode equalNode(getNodeType(token));
equalNode.line = tokenObj.line;
equalNode.lineContent = tokenObj.lineContent;
if (rootNode.children.empty()) {
Error::syntaxError("Expected operand before comparison", tokenObj.line, tokenObj.lineContent);
}
equalNode.addNode(rootNode.children.back());
rootNode.children.pop_back();
std::vector<Token> tokens;
while (auto tokenopt = consume()) {
if (tokenopt.value().value == "\n") {
break;
}
tokens.push_back(tokenopt.value());
}
auto children = Parser(tokens).parse();
equalNode.addNode(children.children[0]);
rootNode.addNode(equalNode);
break;
}
case SolNodeType::Puts: {
SolNode putsNode(SolNodeType::Puts);
putsNode.line = tokenObj.line;
putsNode.lineContent = tokenObj.lineContent;
std::vector<Token> tokens;
while (auto tokenopt = consume()) {
if (tokenopt.value().value == "\n") {
break;
}
tokens.push_back(tokenopt.value());
}
auto children = Parser(tokens).parse();
for (auto& child : children.children) {
putsNode.addNode(child);
}
rootNode.addNode(putsNode);
break;
}
case SolNodeType::If: {
SolNode ifNode(SolNodeType::If);
ifNode.line = tokenObj.line;
ifNode.lineContent = tokenObj.lineContent;
std::vector<Token> tokens;
while (auto tokenopt = consume()) {
if (tokenopt.value().value == "\n") {
break;
}
tokens.push_back(tokenopt.value());
}
auto children = Parser(tokens).parse();
ifNode.addNode(children.children[0]);
tokens.clear();
size_t brackets = 1;
auto tokenopt = consume();
if (tokenopt) {
if (tokenopt.value().value == "{") {
tokens.push_back(tokenopt.value());
while (auto tokenopt = consume()) {
tokens.push_back(tokenopt.value());
if (tokenopt.value().value == "{") {
brackets++;
}
if (tokenopt.value().value == "}") {
brackets--;
}
if (brackets == 0) {
break;
}
}
if (brackets != 0) {
Error::syntaxError("Unclosed code block", tokenopt.value().line, tokenopt.value().lineContent);
}
} else {
Error::syntaxError("I want a code block instead of a " + tokenopt.value().value, tokenopt.value().line, tokenopt.value().lineContent);
}
} else {
Error::syntaxError("Expected code block after if condition", ifNode.line, ifNode.lineContent);
}
auto childCodeBlock = Parser(tokens).parse();
ifNode.addNode(childCodeBlock.children[0]);
rootNode.addNode(ifNode);
break;
}
case SolNodeType::While: {
SolNode whileNode(SolNodeType::While);
whileNode.line = tokenObj.line;
whileNode.lineContent = tokenObj.lineContent;
std::vector<Token> tokens;
while (auto tokenopt = consume()) {
if (tokenopt.value().value == "\n") {
break;
}
tokens.push_back(tokenopt.value());
}
auto children = Parser(tokens).parse();
whileNode.addNode(children.children[0]);
tokens.clear();
size_t brackets = 1;
auto tokenopt = consume();
if (tokenopt) {
if (tokenopt.value().value == "{") {
tokens.push_back(tokenopt.value());
while (auto tokenopt = consume()) {
tokens.push_back(tokenopt.value());
if (tokenopt.value().value == "{") {
brackets++;
}
if (tokenopt.value().value == "}") {
brackets--;
}
if (brackets == 0) {
break;
}
}
if (brackets != 0) {
Error::syntaxError("Unclosed code block", tokenopt.value().line, tokenopt.value().lineContent);
}
} else {
Error::syntaxError("I want a code block instead of a " + tokenopt.value().value, tokenopt.value().line, tokenopt.value().lineContent);
}
} else {
Error::syntaxError("Expected code block after while condition", whileNode.line, whileNode.lineContent);
}
auto childCodeBlock = Parser(tokens).parse();
whileNode.addNode(childCodeBlock.children[0]);
rootNode.addNode(whileNode);
break;
}
case SolNodeType::CodeBlockStart: {
SolNode codeBlockNode(SolNodeType::CodeBlock);
codeBlockNode.line = tokenObj.line;
codeBlockNode.lineContent = tokenObj.lineContent;
size_t brackets = 1;
std::vector<Token> tokens;
while (auto tokenopt = consume()) {
if (tokenopt.value().value == "{") {
brackets++;
}
if (tokenopt.value().value == "}") {
brackets--;
}
if (brackets == 0) {
break;
}
tokens.push_back(tokenopt.value());
}
if (brackets != 0) {
Error::syntaxError("Unclosed code block", tokenObj.line, tokenObj.lineContent);
}
codeBlockNode.children = Parser(tokens).parse().children;
rootNode.addNode(codeBlockNode);
break;
}
case SolNodeType::Identifier: {
SolNode idNode(SolNodeType::Identifier);
idNode.outputId = token;
idNode.line = tokenObj.line;
idNode.lineContent = tokenObj.lineContent;
rootNode.addNode(idNode);
break;
}
case SolNodeType::Set: {
SolNode setNode(SolNodeType::Set);
setNode.line = tokenObj.line;
setNode.lineContent = tokenObj.lineContent;
setNode.addNode(rootNode.children.back());
rootNode.children.pop_back();
std::vector<Token> tokens;
while (auto tokenopt = consume()) {
if (tokenopt.value().value == "\n") {
break;
}
tokens.push_back(tokenopt.value());
}
auto children = Parser(tokens).parse();
setNode.addNode(children.children[0]);
rootNode.addNode(setNode);
break;
}
case SolNodeType::BracketStart: {
// function call
if (!rootNode.children.empty() && rootNode.children.back().nodeType == SolNodeType::Identifier) {
SolNode fnCallNode(SolNodeType::FunctionCall);
fnCallNode.line = tokenObj.line;
fnCallNode.lineContent = tokenObj.lineContent;
fnCallNode.addNode(rootNode.children.back());
rootNode.children.pop_back();
std::vector<Token> tokens;
size_t brackets = 1;
while (auto tokenopt = consume()) {
if (tokenopt.value().value == "(") {
brackets++;
}
if (tokenopt.value().value == ")") {
brackets--;
}
// comma, eval statement here
if (tokenopt.value().value == "," && brackets == 1) {
auto node = Parser(tokens).parse();
if (!node.children.empty()) fnCallNode.children.push_back(node.children[0]);
else {
Error::syntaxError("dingus");
}
tokens.clear();
continue;
}
if (brackets < 1) {
break;
}
tokens.push_back(tokenopt.value());
}
auto node = Parser(tokens).parse();
fnCallNode.children.insert(fnCallNode.children.end(), node.children.begin(), node.children.end());
rootNode.addNode(fnCallNode);
} else {
std::vector<Token> tokens;
size_t brackets = 1;
while (auto tokenopt = consume()) {
if (tokenopt.value().value == "(") {
brackets++;
}
if (tokenopt.value().value == ")") {
brackets--;
}
if (brackets < 1) {
break;
}
tokens.push_back(tokenopt.value());
}
auto node = Parser(tokens).parse();
if (!node.children.empty()) {
rootNode.addNode(node.children.back());
}
}
break;
}
case SolNodeType::FunctionDef: {
SolFunction fn;
auto nameTokenOpt = consume();
if (!nameTokenOpt || getNodeType(nameTokenOpt.value().value) != SolNodeType::Identifier) {
Error::syntaxError("Expected function name", tokenObj.line, tokenObj.lineContent);
}
fn.name = nameTokenOpt.value().value;
auto openParen = consume();
if (!openParen || openParen.value().value != "(") {
Error::syntaxError("Expected '(' after function name", tokenObj.line, tokenObj.lineContent);
}
std::string signature = "fun(";
bool first = true;
while (auto next = peek()) {
if (next.value().value == ")") {
consume();
break;
}
if (!first) {
auto comma = consume();
if (comma.value().value != ",") {
Error::syntaxError("Expected ',' between arguments", tokenObj.line, tokenObj.lineContent);
}
}
auto typeToken = consume();
if (!typeToken) Error::syntaxError("Expected type", tokenObj.line, tokenObj.lineContent);
std::string argType = typeToken.value().value;
auto argNameToken = consume();
if (!argNameToken) Error::syntaxError("Expected argument name", tokenObj.line, tokenObj.lineContent);
std::string argName = argNameToken.value().value;
fn.parameters.push_back({argName, argType});
if (!first) signature += ", ";
signature += argType;
first = false;
}
signature += ") ";
while (peek() && peek().value().value == "\n") consume();
auto next = peek();
if (next && next.value().value != "{") {
auto retTypeToken = consume();
fn.returnType = retTypeToken.value().value;
} else {
fn.returnType = "void";
}
signature += fn.returnType;
fn.signature = signature;
while (peek() && peek().value().value == "\n") consume();
auto brace = peek();
if (!brace || brace.value().value != "{") {
Error::syntaxError("Expected '{' starting function body", tokenObj.line, tokenObj.lineContent);
}
consume();
std::vector<Token> bodyTokens;
size_t brackets = 1;
while(auto t = consume()) {
if (t.value().value == "{") brackets++;
if (t.value().value == "}") brackets--;
if (brackets == 0) break;
bodyTokens.push_back(t.value());
}
if (brackets != 0) Error::syntaxError("Unclosed function body", tokenObj.line, tokenObj.lineContent);
auto parsedBody = Parser(bodyTokens).parse();
auto bodyNode = std::make_shared<SolNode>(SolNodeType::CodeBlock);
bodyNode->children = parsedBody.children;
fn.content = bodyNode;
SolNode fnNode(SolNodeType::FunctionDef);
fnNode.data = SolData(fn);
variables[fn.name] = signature;
rootNode.addNode(fnNode);
break;
}
case SolNodeType::Return: {
SolNode returnNode(SolNodeType::Return);
returnNode.line = tokenObj.line;
returnNode.lineContent = tokenObj.lineContent;
std::vector<Token> tokens;
while (auto tokenopt = consume()) {
if (tokenopt.value().value == "\n") {
break;
}
tokens.push_back(tokenopt.value());
}
auto children = Parser(tokens).parse();
for (auto& child : children.children) {
returnNode.addNode(child);
}
rootNode.addNode(returnNode);
break;
}
case SolNodeType::InlineGround: {
SolNode groundNode(SolNodeType::InlineGround);
consume(); // some funny token
consume(); // {
while (auto tokenopt = consume()) {
if (tokenopt.value().value == "}") {
break;
}
groundNode.ground += tokenopt.value().value + " ";
}
rootNode.addNode(groundNode);
break;
}
case SolNodeType::Struct: {
SolNode structNode(SolNodeType::Struct);
structNode.line = tokenObj.line;
structNode.lineContent = tokenObj.lineContent;
auto nameopt = consume();
if (nameopt) {
structNode.outputId = nameopt.value().value;
}
while (peek() && peek().value().value == "\n") {
consume();
}
auto openBrace = consume();
if (!openBrace || openBrace.value().value != "{") {
Error::syntaxError("Expected '{' after struct name", tokenObj.line, tokenObj.lineContent);
}
std::vector<Token> tokens;
int count = 1;
while (auto tokenopt = consume()) {
if (tokenopt.value().value == "{") {
count++;
}
if (tokenopt.value().value == "}") {
count--;
}
if (count < 1) {
break;
}
tokens.push_back(tokenopt.value());
}
if (count != 0) {
Error::syntaxError("Unclosed struct body", tokenObj.line, tokenObj.lineContent);
}
for (size_t i = 0; i < tokens.size(); i++) {
Token token = tokens[i];
if (token.value == "\n") {
continue;
}
if (getNodeType(token.value) == SolNodeType::Identifier) {
if (i + 2 >= tokens.size()) {
Error::syntaxError("Expecting a value after the identifier", token.line, token.lineContent);
continue;
}
if (getNodeType(tokens[i + 1].value) != SolNodeType::Set) {
Error::syntaxError("Expected a set keyword ('=' or ':')", token.line, token.lineContent);
continue;
}
if (getNodeType(tokens[i + 2].value) != SolNodeType::Identifier &&
getNodeType(tokens[i + 2].value) != SolNodeType::Value) {
Error::syntaxError("Expected a value or identifier for default value", token.line, token.lineContent);
continue;
}
SolNode idNode(SolNodeType::Identifier);
idNode.outputId = token.value;
idNode.line = token.line;
idNode.lineContent = token.lineContent;
SolNode valueNode = Parser({tokens[i + 2]}).parse().children[0];
idNode.addNode(valueNode);
structNode.addNode(idNode);
i += 2;
continue;
}
else if (getNodeType(token.value) == SolNodeType::FunctionDef) {
std::vector<Token> functionTokens;
functionTokens.push_back(token);
int funcCount = 0;
i++;
while (i < tokens.size()) {
functionTokens.push_back(tokens[i]);
if (tokens[i].value == "{") {
funcCount++;
}
if (tokens[i].value == "}") {
funcCount--;
if (funcCount == 0) {
break;
}
}
i++;
}
if (funcCount != 0) {
Error::syntaxError("Unclosed function in struct", token.line, token.lineContent);
}
SolNode functionNode = Parser(functionTokens).parse().children[0];
structNode.addNode(functionNode);
} else {
Error::syntaxError("Structs only support set statements and function definition", token.line, token.lineContent);
}
}
rootNode.addNode(structNode);
break;
}
case SolNodeType::New: {
SolNode putsNode(SolNodeType::New);
putsNode.line = tokenObj.line;
putsNode.lineContent = tokenObj.lineContent;
auto typeopt = consume();
rootNode.addNode(putsNode);
break;
}
}
}
return rootNode;
}
GroundProgram assembleProgram(SolNode& rootNode) {
GroundProgram gp = groundCreateProgram();
auto code = rootNode.generateCode();
for (int i = 0; i < code.size(); i++) {
for (const auto& inst : code[i].code) {
groundAddInstructionToProgram(&gp, inst);
}
for (auto& tmpVar : code[i].toBeDropped) {
GroundInstruction gi = groundCreateInstruction(DROP);
char* droppedVar = (char*) malloc(tmpVar.size() + 1);
strcpy(droppedVar, tmpVar.c_str());
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, droppedVar));
groundAddInstructionToProgram(&gp, gi);
}
}
return gp;
}
} // namespace Parser
}