From d2f295f46a665e75f75561a5ff54f492570b7ae0 Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Sun, 21 Dec 2025 12:06:55 +1100 Subject: [PATCH] refactor --- .gitignore | 1 + Makefile | 26 ++ README.md | 2 +- src/argparser.cpp | 81 ++++ src/argparser.h | 30 ++ src/lexer.cpp | 140 +++++++ src/lexer.h | 17 + src/main.cpp | 951 +--------------------------------------------- src/parser.cpp | 684 +++++++++++++++++++++++++++++++++ src/parser.h | 91 +++++ 10 files changed, 1075 insertions(+), 948 deletions(-) create mode 100644 Makefile create mode 100644 src/argparser.cpp create mode 100644 src/argparser.h create mode 100644 src/lexer.cpp create mode 100644 src/lexer.h create mode 100644 src/parser.cpp create mode 100644 src/parser.h diff --git a/.gitignore b/.gitignore index 7f5ba35..995fa49 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ solstice +build diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c90b75c --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +CXX = g++ +CXXFLAGS = -std=c++17 -Wall -Isrc +LDFLAGS = -lgroundvm + +BUILD_DIR = build +SRC_DIR = src + +SRCS = $(SRC_DIR)/main.cpp $(SRC_DIR)/argparser.cpp $(SRC_DIR)/lexer.cpp $(SRC_DIR)/parser.cpp +OBJS = $(patsubst $(SRC_DIR)/%.cpp, $(BUILD_DIR)/%.o, $(SRCS)) +TARGET = solstice + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CXX) $(OBJS) -o $(TARGET) $(LDFLAGS) + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp | $(BUILD_DIR) + $(CXX) $(CXXFLAGS) -c $< -o $@ + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +clean: + rm -rf $(BUILD_DIR) $(TARGET) + +.PHONY: all clean \ No newline at end of file diff --git a/README.md b/README.md index 673593e..ad1731e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Solstice is a programming language based on Ground. First, ensure CGround is installed on your system with `sudo make install`. Then, compile with ``` -g++ src/main.cpp -o solstice -lgroundvm +make ``` ## Usage diff --git a/src/argparser.cpp b/src/argparser.cpp new file mode 100644 index 0000000..764a671 --- /dev/null +++ b/src/argparser.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include "argparser.h" + +namespace ArgParser { + + void printHelp() { + std::cout << "Solstice compiler\n"; + std::cout << "This program takes a Solstice source file (.sols) and compiles it to Ground\n"; + std::cout << "Usage:\n"; + std::cout << " solstice inputFile [-h] [--help] [-o file] [--output file] [-t type] [--type type]\n"; + std::cout << "Options:\n"; + std::cout << " -h or --help\n"; + std::cout << " Shows this help message\n"; + std::cout << " -o file or --output file\n"; + std::cout << " Specifies where to output the compiled Ground program.\n"; + std::cout << " If this option is omitted, the program is automatically run.\n"; + std::cout << " -t type or --type type\n"; + std::cout << " Specifies the type of output.\n"; + std::cout << " Currently supported options:\n"; + std::cout << " text\n"; + } + + Args parseArgs(int argc, char** argv) { + if (argc < 2) { + printHelp(); + exit(1); + } + Args args; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + printHelp(); + exit(1); + } + if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--output") == 0) { + i++; + if (i < argc) { + if (args.output.has_value()) { + std::cout << "(error) Multiple output values provided\n"; + exit(1); + } + args.output = Argument(ArgType::OUTPUT, std::string(argv[i])); + continue; + } else { + std::cout << "(error) Please provide a filename to output to\n"; + exit(1); + } + } + else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--type") == 0) { + i++; + if (i < argc) { + if (args.output.has_value()) { + std::cout << "(error) Multiple type values provided\n"; + exit(1); + } + args.outputtype = Argument(ArgType::OUTPUTTYPE, std::string(argv[i])); + continue; + } else { + std::cout << "(error) Please provide an output type\n"; + exit(1); + } + } + else { + if (args.file.has_value()) { + std::cout << "(error) Multiple input files provided\n"; + exit(1); + } + args.file = Argument(ArgType::FILE, std::string(argv[i])); + } + } + if (!args.file.has_value()) { + std::cout << "(error) No input file provided\n"; + exit(1); + } + return args; + } + +} // namespace ArgParser diff --git a/src/argparser.h b/src/argparser.h new file mode 100644 index 0000000..1ebb625 --- /dev/null +++ b/src/argparser.h @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +namespace ArgParser { + + enum class ArgType { + FILE, OUTPUT, OUTPUTTYPE + }; + + class Argument { + public: + ArgType type; + std::string value; + Argument() = default; + Argument(ArgType type, std::string value) : type(type), value(value) {} + }; + + class Args { + public: + std::optional output = {}; + std::optional outputtype = {}; + std::optional file = {}; + }; + + void printHelp(); + Args parseArgs(int argc, char** argv); + +} // namespace ArgParser diff --git a/src/lexer.cpp b/src/lexer.cpp new file mode 100644 index 0000000..a138783 --- /dev/null +++ b/src/lexer.cpp @@ -0,0 +1,140 @@ +#include +#include +#include +#include "lexer.h" + +namespace Solstice { + + std::optional Lexer::peek(int ahead) { + if (current + ahead < size) { + return input[current + ahead]; + } else { + return {}; + } + } + + std::optional Lexer::consume() { + if (current < size) { + return input[current++]; + } else { + return {}; + } + } + + Lexer::Lexer(std::string in) : input(in), size(in.size()) {} + std::vector Lexer::lex() { + current = 0; + std::vector tokens; + std::string buf; + bool inString = false; + while (auto copt = consume()) { + char c = copt.value(); + if (inString) { + buf.push_back(c); + if (c == '"') { + tokens.push_back(buf); + buf.clear(); + inString = false; + } + continue; + } + switch (c) { + // double quotes are special + case '"': { + if (!buf.empty()) { + tokens.push_back(buf); + buf.clear(); + } + buf.push_back(c); + inString = true; + break; + } + // tokens which are not followed by anything + case '\n': + case '(': + case ')': + case '}': + { + if (!buf.empty()) { + tokens.push_back(buf); + buf.clear(); + } + tokens.push_back(std::string(1, c)); + break; + } + // tokens which may be followed by either themselves + // or an equals sign + case '+': + case '-': + { + std::string newToken(1, c); + auto tokenopt = peek(0); + if (tokenopt) { + char token = tokenopt.value(); + if (token == c || token == '=') { + newToken += token; + consume(); + } + } + if (!buf.empty()) { + tokens.push_back(buf); + buf.clear(); + } + tokens.push_back(newToken); + break; + } + // tokens which may be followed by an equals sign + case '!': + case '*': + case '/': + case '=': + { + std::string newToken(1, c); + auto tokenopt = peek(0); + if (tokenopt) { + char token = tokenopt.value(); + if (token == '=') { + newToken += token; + consume(); + } + } + if (!buf.empty()) { + tokens.push_back(buf); + buf.clear(); + } + tokens.push_back(newToken); + break; + } + // tokens which need a newline inserted for them + case '{': + { + if (!buf.empty()) { + tokens.push_back(buf); + buf.clear(); + } + tokens.push_back("\n"); + tokens.push_back(std::string(1, c)); + } + // tokens which do not need to be included + case ' ': + { + if (!buf.empty()) { + tokens.push_back(buf); + buf.clear(); + } + break; + } + default: + { + buf += c; + } + } + } + + if (!buf.empty()) { + tokens.push_back(buf); + } + return tokens; + } + +} diff --git a/src/lexer.h b/src/lexer.h new file mode 100644 index 0000000..c1f9fcf --- /dev/null +++ b/src/lexer.h @@ -0,0 +1,17 @@ +#include +#include +#include + +namespace Solstice { + class Lexer { + std::string input; + size_t size; + size_t current; + std::optional peek(int ahead = 1); + std::optional consume(); + public: + Lexer(std::string in); + std::vector lex(); + + }; +} diff --git a/src/main.cpp b/src/main.cpp index 1ffcc93..b45c466 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,15 +1,13 @@ -#include -#include #include #include -#include #include #include #include #include -#include -#include -#include + +#include "lexer.h" +#include "parser.h" +#include "argparser.h" #define parseOneToken(token) Parser({token.value()}).parse().children[0] @@ -18,950 +16,9 @@ namespace Solstice { int tmpIdIterator = 0; int labelIterator = 0; - namespace Parser { - - enum class SolNodeType { - Add, Subtract, Equal, Inequal, Set, While, If, Value, Identifier, None, Root, CodeBlock, CodeBlockStart, CodeBlockEnd, FunctionCall, Expression, BracketStart, BracketEnd, Puts - }; - - enum class SolDataType { - Int, String, Double, Bool, Char, None - }; - - class SolNode; - - class SolGroundCodeBlock { - public: - std::vector code; - SolGroundCodeBlock() = default; - }; - - class SolData { - typedef std::variant varData; - varData data; - public: - SolDataType type = SolDataType::Int; - SolData() = default; - SolData(int64_t in) : data(in), type(SolDataType::Int) {} - SolData(double in) : data(in), type(SolDataType::Double) {} - SolData(std::string in) : data(in), type(SolDataType::String) {} - SolData(char in) : data(in), type(SolDataType::Char) {} - SolData(bool in) : data(in), type(SolDataType::Bool) {} - std::optional getInt() { - if (type == SolDataType::Int) { - return std::get(data); - } else { - return {}; - } - } - std::optional getDouble() { - if (type == SolDataType::Double) { - return std::get(data); - } else { - return {}; - } - } - std::optional getString() { - if (type == SolDataType::String) { - return std::get(data); - } else { - return {}; - } - } - std::optional getChar() { - if (type == SolDataType::Char) { - return std::get(data); - } else { - return {}; - } - } - std::optional getBool() { - if (type == SolDataType::Bool) { - return std::get(data); - } else { - return {}; - } - } - }; - - class SolNode { - SolData data; - public: - SolNodeType nodeType = SolNodeType::None; - std::vector children; - std::string outputId; - SolNode(SolNodeType nodeType) : nodeType(nodeType) {} - SolNode(SolNodeType nodeType, SolData data) : nodeType(nodeType), data(data) {} - SolNode() = default; - void addNode(SolNode in) { - children.push_back(in); - } - void setValue(SolData in) { - data = in; - } - const std::vector generateCode() { - std::vector code; - if (nodeType != SolNodeType::If && nodeType != SolNodeType::While) 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, outputId.data())); - 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) { - groundAddValueToInstruction(&gi, groundCreateValue(STRING, dataopt.value().c_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: { - SolGroundCodeBlock codeBlock; - outputId = "tmp_" + std::to_string(tmpIdIterator++); - GroundInstruction gi = groundCreateInstruction(ADD); - if (children.size() < 2) { - std::cout << "Need more stuff to add\n"; - } - groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); - groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); - groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, outputId.data())); - codeBlock.code.push_back(gi); - code.push_back(codeBlock); - break; - } - case SolNodeType::Equal: { - SolGroundCodeBlock codeBlock; - outputId = "tmp_" + std::to_string(tmpIdIterator++); - GroundInstruction gi = groundCreateInstruction(EQUAL); - if (children.size() < 2) { - std::cout << "Need more stuff to equal\n"; - } - groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); - groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); - groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, outputId.data())); - codeBlock.code.push_back(gi); - code.push_back(codeBlock); - break; - } - case SolNodeType::Inequal: { - SolGroundCodeBlock codeBlock; - outputId = "tmp_" + std::to_string(tmpIdIterator++); - GroundInstruction gi = groundCreateInstruction(INEQUAL); - if (children.size() < 2) { - std::cout << "Need more stuff to inequal\n"; - } - groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); - groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); - groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, outputId.data())); - codeBlock.code.push_back(gi); - code.push_back(codeBlock); - break; - } - case SolNodeType::Puts: { - SolGroundCodeBlock codeBlock; - GroundInstruction gi = groundCreateInstruction(PRINTLN); - if (children.size() < 1) { - std::cout << "Need more stuff to puts\n"; - } - groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); - codeBlock.code.push_back(gi); - code.push_back(codeBlock); - break; - } - case SolNodeType::If: { - auto conditionCode = children[0].generateCode(); - code.insert(code.end(), conditionCode.begin(), conditionCode.end()); - outputId = "tmp_" + std::to_string(tmpIdIterator++); - SolGroundCodeBlock codeBlock; - GroundInstruction gi = groundCreateInstruction(NOT); - groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); - groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, outputId.data())); - 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, outputId.data())); - 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(); - GroundInstruction gi3 = groundCreateInstruction(CREATELABEL); - groundAddReferenceToInstruction(&gi3, groundCreateReference(LABEL, labelId)); - codeBlock.code.push_back(gi3); - code.push_back(codeBlock); - break; - } - case SolNodeType::While: { - 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++); - GroundInstruction gi = groundCreateInstruction(NOT); - groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); - groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, outputId.data())); - checkBlock.code.push_back(gi); - GroundInstruction gi2 = groundCreateInstruction(IF); - groundAddReferenceToInstruction(&gi2, groundCreateReference(VALREF, outputId.data())); - 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: { - SolGroundCodeBlock codeBlock; - GroundInstruction setInstruction = groundCreateInstruction(SET); - groundAddReferenceToInstruction(&setInstruction, groundCreateReference(DIRREF, children[0].outputId.data())); - groundAddReferenceToInstruction(&setInstruction, groundCreateReference(VALREF, children[1].outputId.data())); - codeBlock.code.push_back(setInstruction); - code.push_back(codeBlock); - break; - } - case SolNodeType::FunctionCall: { - // Take care of in built functions - if (children[0].outputId == "input") { - SolGroundCodeBlock inputCodeBlock; - if (children.size() > 1) { - GroundInstruction printInstruction = groundCreateInstruction(PRINT); - groundAddReferenceToInstruction(&printInstruction, groundCreateReference(VALREF, children[1].outputId.data())); - inputCodeBlock.code.push_back(printInstruction); - } - GroundInstruction inputInstruction = groundCreateInstruction(INPUT); - outputId = "tmp_" + std::to_string(tmpIdIterator++); - groundAddReferenceToInstruction(&inputInstruction, groundCreateReference(DIRREF, outputId.data())); - inputCodeBlock.code.push_back(inputInstruction); - code.push_back(inputCodeBlock); - break; - } - - break; - } - default: {} - } - return code; - } - - }; - - class Parser { - std::vector tokensToParse; - size_t current; - size_t size; - - std::optional peek(int ahead = 1) { - if (current + ahead < size) { - return tokensToParse[current + ahead]; - } else { - return {}; - } - } - - std::optional consume() { - if (current < size) { - return tokensToParse[current++]; - } else { - return {}; - } - } - - bool isInt(std::string in) { - for (const char& c : in) { - if (!std::isdigit(c)) { - return false; - } - } - return true; - } - bool 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 isString(std::string in) { - if (in.size() > 1 && in[0] == '"' && in.back() == '"') { - return true; - } - return false; - } - bool isChar(std::string in) { - if (in.size() == 3 && in[0] == '\'' && in.back() == '\'') { - return true; - } - return false; - } - bool isBool(std::string in) { - if (in == "true" || in == "false") { - return true; - } - return false; - } - - SolDataType 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 getNodeType(std::string in) { - if (getDataType(in) != SolDataType::None) { - return SolNodeType::Value; - } - if (in == "+") { - return SolNodeType::Add; - } - if (in == "=") { - return SolNodeType::Set; - } - if (in == "==") { - return SolNodeType::Equal; - } - if (in == "!=") { - return SolNodeType::Inequal; - } - if (in == "puts") { - return SolNodeType::Puts; - } - if (in == "if") { - return SolNodeType::If; - } - if (in == "while") { - return SolNodeType::While; - } - if (in == "{") { - return SolNodeType::CodeBlockStart; - } - if (in == "}") { - return SolNodeType::CodeBlockEnd; - } - if (in == "(") { - return SolNodeType::BracketStart; - } - if (in == ")") { - return SolNodeType::BracketEnd; - } - return SolNodeType::Identifier; - } - - public: - Parser(std::vector in) : tokensToParse(in) {} - - SolNode parse() { - current = 0; - size = tokensToParse.size(); - SolNode rootNode(SolNodeType::Root); - while (auto tokenopt = consume()) { - std::string token = tokenopt.value(); - switch (getNodeType(token)) { - case SolNodeType::Value: { - switch (getDataType(token)) { - case SolDataType::Int: { - SolNode intNode(SolNodeType::Value); - intNode.setValue((int64_t) std::stoll(token)); - rootNode.addNode(intNode); - break; - } - case SolDataType::Double: { - SolNode doubleNode(SolNodeType::Value); - doubleNode.setValue(std::stod(token)); - rootNode.addNode(doubleNode); - break; - } - case SolDataType::String: { - SolNode stringNode(SolNodeType::Value); - stringNode.setValue(token.substr(1, token.size() - 2)); - rootNode.addNode(stringNode); - break; - } - case SolDataType::Char: { - SolNode charNode(SolNodeType::Value); - charNode.setValue(token[1]); - rootNode.addNode(charNode); - break; - } - case SolDataType::Bool: { - SolNode boolNode(SolNodeType::Value); - boolNode.setValue(token == "true"); - rootNode.addNode(boolNode); - break; - } - } - break; - } - case SolNodeType::Add: { - SolNode addNode(SolNodeType::Add); - addNode.addNode(rootNode.children.back()); - rootNode.children.pop_back(); - auto tokenopt = consume(); - if (tokenopt) { - addNode.addNode(parseOneToken(tokenopt)); - } else { - std::cout << "FEED ME MORE TOKENS\n"; - exit(1); - } - rootNode.addNode(addNode); - break; - } - case SolNodeType::Equal: { - SolNode equalNode(SolNodeType::Equal); - equalNode.addNode(rootNode.children.back()); - rootNode.children.pop_back(); - auto tokenopt = consume(); - if (tokenopt) { - equalNode.addNode(parseOneToken(tokenopt)); - } else { - std::cout << "FEED ME MORE TOKENS\n"; - exit(1); - } - rootNode.addNode(equalNode); - break; - } - case SolNodeType::Inequal: { - SolNode inequalNode(SolNodeType::Inequal); - inequalNode.addNode(rootNode.children.back()); - rootNode.children.pop_back(); - auto tokenopt = consume(); - if (tokenopt) { - inequalNode.addNode(parseOneToken(tokenopt)); - } else { - std::cout << "FEED ME MORE TOKENS\n"; - exit(1); - } - rootNode.addNode(inequalNode); - break; - } - case SolNodeType::Puts: { - SolNode putsNode(SolNodeType::Puts); - std::vector tokens; - while (auto tokenopt = consume()) { - if (tokenopt.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); - std::vector tokens; - while (auto tokenopt = consume()) { - if (tokenopt.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() == "{") { - tokens.push_back(tokenopt.value()); - while (auto tokenopt = consume()) { - tokens.push_back(tokenopt.value()); - if (tokenopt.value() == "{") { - brackets++; - } - if (tokenopt.value() == "}") { - brackets--; - } - if (brackets == 0) { - break; - } - } - } else { - std::cout << "I want a code block instead of a " + tokenopt.value() + "\n"; - exit(1); - } - } else { - std::cout << "FEED ME MORE TOKENSSSSS\n"; - exit(1); - } - auto childCodeBlock = Parser(tokens).parse(); - ifNode.addNode(childCodeBlock.children[0]); - rootNode.addNode(ifNode); - break; - } - case SolNodeType::While: { - SolNode whileNode(SolNodeType::While); - std::vector tokens; - while (auto tokenopt = consume()) { - if (tokenopt.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() == "{") { - tokens.push_back(tokenopt.value()); - while (auto tokenopt = consume()) { - tokens.push_back(tokenopt.value()); - if (tokenopt.value() == "{") { - brackets++; - } - if (tokenopt.value() == "}") { - brackets--; - } - if (brackets == 0) { - break; - } - } - } else { - std::cout << "I want a code block instead of a " + tokenopt.value() + "\n"; - exit(1); - } - } else { - std::cout << "FEED ME MORE TOKENSSSSS\n"; - exit(1); - } - auto childCodeBlock = Parser(tokens).parse(); - whileNode.addNode(childCodeBlock.children[0]); - rootNode.addNode(whileNode); - break; - } - case SolNodeType::CodeBlockStart: { - SolNode codeBlockNode(SolNodeType::CodeBlock); - size_t brackets = 1; - std::vector tokens; - while (auto tokenopt = consume()) { - if (tokenopt.value() == "{") { - brackets++; - } - if (tokenopt.value() == "}") { - brackets--; - } - if (brackets == 0) { - break; - } - tokens.push_back(tokenopt.value()); - } - codeBlockNode.children = Parser(tokens).parse().children; - rootNode.addNode(codeBlockNode); - break; - } - case SolNodeType::Identifier: { - SolNode idNode(SolNodeType::Identifier); - idNode.outputId = token; - rootNode.addNode(idNode); - break; - } - case SolNodeType::Set: { - SolNode setNode(SolNodeType::Set); - setNode.addNode(rootNode.children.back()); - rootNode.children.pop_back(); - std::vector tokens; - while (auto tokenopt = consume()) { - if (tokenopt.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: { - if (rootNode.children.back().nodeType == SolNodeType::Identifier) { - SolNode fnCallNode(SolNodeType::FunctionCall); - fnCallNode.addNode(rootNode.children.back()); - rootNode.children.pop_back(); - std::vector tokens; - size_t brackets = 1; - while (auto tokenopt = consume()) { - if (tokenopt.value() == "(") { - brackets++; - } - if (tokenopt.value() == ")") { - brackets--; - } - 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 tokens; - size_t brackets = 1; - while (auto tokenopt = consume()) { - if (tokenopt.value() == "(") { - brackets++; - } - if (tokenopt.value() == ")") { - brackets--; - } - if (brackets < 1) { - break; - } - tokens.push_back(tokenopt.value()); - } - auto node = Parser(tokens).parse(); - node.nodeType = SolNodeType::Expression; - rootNode.addNode(node); - } - } - } - } - 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); - } - } - return gp; - } - - } // namespace Parser - - class Lexer { - std::string input; - size_t size; - size_t current; - - std::optional peek(int ahead = 1) { - if (current + ahead < size) { - return input[current + ahead]; - } else { - return {}; - } - } - - std::optional consume() { - if (current < size) { - return input[current++]; - } else { - return {}; - } - } - - public: - Lexer(std::string in) : input(in), size(in.size()) {}; - std::vector lex() { - current = 0; - std::vector tokens; - std::string buf; - bool inString = false; - while (auto copt = consume()) { - char c = copt.value(); - if (inString) { - buf.push_back(c); - if (c == '"') { - tokens.push_back(buf); - buf.clear(); - inString = false; - } - continue; - } - switch (c) { - // double quotes are special - case '"': { - if (!buf.empty()) { - tokens.push_back(buf); - buf.clear(); - } - buf.push_back(c); - inString = true; - break; - } - // tokens which are not followed by anything - case '\n': - case '(': - case ')': - case '}': - { - if (!buf.empty()) { - tokens.push_back(buf); - buf.clear(); - } - tokens.push_back(std::string(1, c)); - break; - } - // tokens which may be followed by either themselves - // or an equals sign - case '+': - case '-': - { - std::string newToken(1, c); - auto tokenopt = peek(0); - if (tokenopt) { - char token = tokenopt.value(); - if (token == c || token == '=') { - newToken += token; - consume(); - } - } - if (!buf.empty()) { - tokens.push_back(buf); - buf.clear(); - } - tokens.push_back(newToken); - break; - } - // tokens which may be followed by an equals sign - case '!': - case '*': - case '/': - case '=': - { - std::string newToken(1, c); - auto tokenopt = peek(0); - if (tokenopt) { - char token = tokenopt.value(); - if (token == '=') { - newToken += token; - consume(); - } - } - if (!buf.empty()) { - tokens.push_back(buf); - buf.clear(); - } - tokens.push_back(newToken); - break; - } - // tokens which need a newline inserted for them - case '{': - { - if (!buf.empty()) { - tokens.push_back(buf); - buf.clear(); - } - tokens.push_back("\n"); - tokens.push_back(std::string(1, c)); - } - // tokens which do not need to be included - case ' ': - { - if (!buf.empty()) { - tokens.push_back(buf); - buf.clear(); - } - break; - } - default: - { - buf += c; - } - } - } - - if (!buf.empty()) { - tokens.push_back(buf); - } - return tokens; - } - - }; - } // namespace Solstice -namespace ArgParser { - - enum class ArgType { - FILE, OUTPUT, OUTPUTTYPE - }; - - class Argument { - public: - ArgType type; - std::string value; - Argument() = default; - Argument(ArgType type, std::string value) : type(type), value(value) {} - }; - - class Args { - public: - std::optional output = {}; - std::optional outputtype = {}; - std::optional file = {}; - }; - - void printHelp() { - std::cout << "Solstice compiler\n"; - std::cout << "This program takes a Solstice source file (.sols) and compiles it to Ground\n"; - std::cout << "Usage:\n"; - std::cout << " solstice inputFile [-h] [--help] [-o file] [--output file] [-t type] [--type type]\n"; - std::cout << "Options:\n"; - std::cout << " -h or --help\n"; - std::cout << " Shows this help message\n"; - std::cout << " -o file or --output file\n"; - std::cout << " Specifies where to output the compiled Ground program.\n"; - std::cout << " If this option is omitted, the program is automatically run.\n"; - std::cout << " -t type or --type type\n"; - std::cout << " Specifies the type of output.\n"; - std::cout << " Currently supported options:\n"; - std::cout << " text\n"; - } - - Args parseArgs(int argc, char** argv) { - if (argc < 2) { - printHelp(); - exit(1); - } - Args args; - - for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { - printHelp(); - exit(1); - } - if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--output") == 0) { - i++; - if (i < argc) { - if (args.output.has_value()) { - std::cout << "(error) Multiple output values provided\n"; - exit(1); - } - args.output = Argument(ArgType::OUTPUT, std::string(argv[i])); - continue; - } else { - std::cout << "(error) Please provide a filename to output to\n"; - exit(1); - } - } - else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--type") == 0) { - i++; - if (i < argc) { - if (args.output.has_value()) { - std::cout << "(error) Multiple type values provided\n"; - exit(1); - } - args.outputtype = Argument(ArgType::OUTPUTTYPE, std::string(argv[i])); - continue; - } else { - std::cout << "(error) Please provide an output type\n"; - exit(1); - } - } - else { - if (args.file.has_value()) { - std::cout << "(error) Multiple input files provided\n"; - exit(1); - } - args.file = Argument(ArgType::FILE, std::string(argv[i])); - } - } - if (!args.file.has_value()) { - std::cout << "(error) No input file provided\n"; - exit(1); - } - return args; - } - -} // namespace ArgParser int main(int argc, char** argv) { auto args = ArgParser::parseArgs(argc, argv); diff --git a/src/parser.cpp b/src/parser.cpp new file mode 100644 index 0000000..b55b2bc --- /dev/null +++ b/src/parser.cpp @@ -0,0 +1,684 @@ +#include "parser.h" +#include +#include +#include + +#define parseOneToken(token) Parser({token.value()}).parse().children[0] + +namespace Solstice { + namespace Parser { + + // 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) {} + + std::optional SolData::getInt() { + if (type == SolDataType::Int) { + return std::get(data); + } else { + return {}; + } + } + std::optional SolData::getDouble() { + if (type == SolDataType::Double) { + return std::get(data); + } else { + return {}; + } + } + std::optional SolData::getString() { + if (type == SolDataType::String) { + return std::get(data); + } else { + return {}; + } + } + std::optional SolData::getChar() { + if (type == SolDataType::Char) { + return std::get(data); + } else { + return {}; + } + } + std::optional SolData::getBool() { + if (type == SolDataType::Bool) { + return std::get(data); + } else { + return {}; + } + } + + // 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; + } + + const std::vector SolNode::generateCode() { + std::vector code; + if (nodeType != SolNodeType::If && nodeType != SolNodeType::While) 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, outputId.data())); + 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) { + groundAddValueToInstruction(&gi, groundCreateValue(STRING, dataopt.value().c_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: { + SolGroundCodeBlock codeBlock; + outputId = "tmp_" + std::to_string(tmpIdIterator++); + GroundInstruction gi = groundCreateInstruction(ADD); + if (children.size() < 2) { + std::cout << "Need more stuff to add\n"; + } + groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); + groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); + groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, outputId.data())); + codeBlock.code.push_back(gi); + code.push_back(codeBlock); + break; + } + case SolNodeType::Equal: { + SolGroundCodeBlock codeBlock; + outputId = "tmp_" + std::to_string(tmpIdIterator++); + GroundInstruction gi = groundCreateInstruction(EQUAL); + if (children.size() < 2) { + std::cout << "Need more stuff to equal\n"; + } + groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); + groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); + groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, outputId.data())); + codeBlock.code.push_back(gi); + code.push_back(codeBlock); + break; + } + case SolNodeType::Inequal: { + SolGroundCodeBlock codeBlock; + outputId = "tmp_" + std::to_string(tmpIdIterator++); + GroundInstruction gi = groundCreateInstruction(INEQUAL); + if (children.size() < 2) { + std::cout << "Need more stuff to inequal\n"; + } + groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); + groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); + groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, outputId.data())); + codeBlock.code.push_back(gi); + code.push_back(codeBlock); + break; + } + case SolNodeType::Puts: { + SolGroundCodeBlock codeBlock; + GroundInstruction gi = groundCreateInstruction(PRINTLN); + if (children.size() < 1) { + std::cout << "Need more stuff to puts\n"; + } + groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); + codeBlock.code.push_back(gi); + code.push_back(codeBlock); + break; + } + case SolNodeType::If: { + auto conditionCode = children[0].generateCode(); + code.insert(code.end(), conditionCode.begin(), conditionCode.end()); + outputId = "tmp_" + std::to_string(tmpIdIterator++); + SolGroundCodeBlock codeBlock; + GroundInstruction gi = groundCreateInstruction(NOT); + groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); + groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, outputId.data())); + 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, outputId.data())); + 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(); + GroundInstruction gi3 = groundCreateInstruction(CREATELABEL); + groundAddReferenceToInstruction(&gi3, groundCreateReference(LABEL, labelId)); + codeBlock.code.push_back(gi3); + code.push_back(codeBlock); + break; + } + case SolNodeType::While: { + 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++); + GroundInstruction gi = groundCreateInstruction(NOT); + groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); + groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, outputId.data())); + checkBlock.code.push_back(gi); + GroundInstruction gi2 = groundCreateInstruction(IF); + groundAddReferenceToInstruction(&gi2, groundCreateReference(VALREF, outputId.data())); + 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: { + SolGroundCodeBlock codeBlock; + GroundInstruction setInstruction = groundCreateInstruction(SET); + groundAddReferenceToInstruction(&setInstruction, groundCreateReference(DIRREF, children[0].outputId.data())); + groundAddReferenceToInstruction(&setInstruction, groundCreateReference(VALREF, children[1].outputId.data())); + codeBlock.code.push_back(setInstruction); + code.push_back(codeBlock); + break; + } + case SolNodeType::FunctionCall: { + // Take care of in built functions + if (children[0].outputId == "input") { + SolGroundCodeBlock inputCodeBlock; + if (children.size() > 1) { + GroundInstruction printInstruction = groundCreateInstruction(PRINT); + groundAddReferenceToInstruction(&printInstruction, groundCreateReference(VALREF, children[1].outputId.data())); + inputCodeBlock.code.push_back(printInstruction); + } + GroundInstruction inputInstruction = groundCreateInstruction(INPUT); + outputId = "tmp_" + std::to_string(tmpIdIterator++); + groundAddReferenceToInstruction(&inputInstruction, groundCreateReference(DIRREF, outputId.data())); + inputCodeBlock.code.push_back(inputInstruction); + code.push_back(inputCodeBlock); + break; + } + + break; + } + default: {} + } + return code; + } + + // Parser Implementation + Parser::Parser(std::vector in) : tokensToParse(in) {} + + std::optional Parser::peek(int ahead) { + if (current + ahead < size) { + return tokensToParse[current + ahead]; + } else { + return {}; + } + } + + std::optional 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::Set; + } + if (in == "==") { + return SolNodeType::Equal; + } + if (in == "!=") { + return SolNodeType::Inequal; + } + if (in == "puts") { + return SolNodeType::Puts; + } + if (in == "if") { + return SolNodeType::If; + } + if (in == "while") { + return SolNodeType::While; + } + if (in == "{") { + return SolNodeType::CodeBlockStart; + } + if (in == "}") { + return SolNodeType::CodeBlockEnd; + } + if (in == "(") { + return SolNodeType::BracketStart; + } + if (in == ")") { + return SolNodeType::BracketEnd; + } + return SolNodeType::Identifier; + } + + SolNode Parser::parse() { + current = 0; + size = tokensToParse.size(); + SolNode rootNode(SolNodeType::Root); + while (auto tokenopt = consume()) { + std::string token = tokenopt.value(); + switch (getNodeType(token)) { + case SolNodeType::Value: { + switch (getDataType(token)) { + case SolDataType::Int: { + SolNode intNode(SolNodeType::Value); + intNode.setValue((int64_t) std::stoll(token)); + rootNode.addNode(intNode); + break; + } + case SolDataType::Double: { + SolNode doubleNode(SolNodeType::Value); + doubleNode.setValue(std::stod(token)); + rootNode.addNode(doubleNode); + break; + } + case SolDataType::String: { + SolNode stringNode(SolNodeType::Value); + stringNode.setValue(token.substr(1, token.size() - 2)); + rootNode.addNode(stringNode); + break; + } + case SolDataType::Char: { + SolNode charNode(SolNodeType::Value); + charNode.setValue(token[1]); + rootNode.addNode(charNode); + break; + } + case SolDataType::Bool: { + SolNode boolNode(SolNodeType::Value); + boolNode.setValue(token == "true"); + rootNode.addNode(boolNode); + break; + } + } + break; + } + case SolNodeType::Add: { + SolNode addNode(SolNodeType::Add); + addNode.addNode(rootNode.children.back()); + rootNode.children.pop_back(); + auto tokenopt = consume(); + if (tokenopt) { + addNode.addNode(parseOneToken(tokenopt)); + } else { + std::cout << "FEED ME MORE TOKENS\n"; + exit(1); + } + rootNode.addNode(addNode); + break; + } + case SolNodeType::Equal: { + SolNode equalNode(SolNodeType::Equal); + equalNode.addNode(rootNode.children.back()); + rootNode.children.pop_back(); + auto tokenopt = consume(); + if (tokenopt) { + equalNode.addNode(parseOneToken(tokenopt)); + } else { + std::cout << "FEED ME MORE TOKENS\n"; + exit(1); + } + rootNode.addNode(equalNode); + break; + } + case SolNodeType::Inequal: { + SolNode inequalNode(SolNodeType::Inequal); + inequalNode.addNode(rootNode.children.back()); + rootNode.children.pop_back(); + auto tokenopt = consume(); + if (tokenopt) { + inequalNode.addNode(parseOneToken(tokenopt)); + } else { + std::cout << "FEED ME MORE TOKENS\n"; + exit(1); + } + rootNode.addNode(inequalNode); + break; + } + case SolNodeType::Puts: { + SolNode putsNode(SolNodeType::Puts); + std::vector tokens; + while (auto tokenopt = consume()) { + if (tokenopt.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); + std::vector tokens; + while (auto tokenopt = consume()) { + if (tokenopt.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() == "{") { + tokens.push_back(tokenopt.value()); + while (auto tokenopt = consume()) { + tokens.push_back(tokenopt.value()); + if (tokenopt.value() == "{") { + brackets++; + } + if (tokenopt.value() == "}") { + brackets--; + } + if (brackets == 0) { + break; + } + } + } else { + std::cout << "I want a code block instead of a " + tokenopt.value() + "\n"; + exit(1); + } + } else { + std::cout << "FEED ME MORE TOKENSSSSS\n"; + exit(1); + } + auto childCodeBlock = Parser(tokens).parse(); + ifNode.addNode(childCodeBlock.children[0]); + rootNode.addNode(ifNode); + break; + } + case SolNodeType::While: { + SolNode whileNode(SolNodeType::While); + std::vector tokens; + while (auto tokenopt = consume()) { + if (tokenopt.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() == "{") { + tokens.push_back(tokenopt.value()); + while (auto tokenopt = consume()) { + tokens.push_back(tokenopt.value()); + if (tokenopt.value() == "{") { + brackets++; + } + if (tokenopt.value() == "}") { + brackets--; + } + if (brackets == 0) { + break; + } + } + } else { + std::cout << "I want a code block instead of a " + tokenopt.value() + "\n"; + exit(1); + } + } else { + std::cout << "FEED ME MORE TOKENSSSSS\n"; + exit(1); + } + auto childCodeBlock = Parser(tokens).parse(); + whileNode.addNode(childCodeBlock.children[0]); + rootNode.addNode(whileNode); + break; + } + case SolNodeType::CodeBlockStart: { + SolNode codeBlockNode(SolNodeType::CodeBlock); + size_t brackets = 1; + std::vector tokens; + while (auto tokenopt = consume()) { + if (tokenopt.value() == "{") { + brackets++; + } + if (tokenopt.value() == "}") { + brackets--; + } + if (brackets == 0) { + break; + } + tokens.push_back(tokenopt.value()); + } + codeBlockNode.children = Parser(tokens).parse().children; + rootNode.addNode(codeBlockNode); + break; + } + case SolNodeType::Identifier: { + SolNode idNode(SolNodeType::Identifier); + idNode.outputId = token; + rootNode.addNode(idNode); + break; + } + case SolNodeType::Set: { + SolNode setNode(SolNodeType::Set); + setNode.addNode(rootNode.children.back()); + rootNode.children.pop_back(); + std::vector tokens; + while (auto tokenopt = consume()) { + if (tokenopt.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: { + if (rootNode.children.back().nodeType == SolNodeType::Identifier) { + SolNode fnCallNode(SolNodeType::FunctionCall); + fnCallNode.addNode(rootNode.children.back()); + rootNode.children.pop_back(); + std::vector tokens; + size_t brackets = 1; + while (auto tokenopt = consume()) { + if (tokenopt.value() == "(") { + brackets++; + } + if (tokenopt.value() == ")") { + brackets--; + } + 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 tokens; + size_t brackets = 1; + while (auto tokenopt = consume()) { + if (tokenopt.value() == "(") { + brackets++; + } + if (tokenopt.value() == ")") { + brackets--; + } + if (brackets < 1) { + break; + } + tokens.push_back(tokenopt.value()); + } + auto node = Parser(tokens).parse(); + node.nodeType = SolNodeType::Expression; + rootNode.addNode(node); + } + } + } + } + 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); + } + } + return gp; + } + + } // namespace Parser + +} \ No newline at end of file diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..b872393 --- /dev/null +++ b/src/parser.h @@ -0,0 +1,91 @@ +#ifndef PARSER_H +#define PARSER_H + +#include +#include +#include +#include +#include + +namespace Solstice { + + // External variables referenced in parser logic + extern int tmpIdIterator; + extern int labelIterator; + + namespace Parser { + + enum class SolNodeType { + Add, Subtract, Equal, Inequal, Set, While, If, Value, Identifier, None, Root, CodeBlock, CodeBlockStart, CodeBlockEnd, FunctionCall, Expression, BracketStart, BracketEnd, Puts + }; + + enum class SolDataType { + Int, String, Double, Bool, Char, None + }; + + class SolNode; + + class SolGroundCodeBlock { + public: + std::vector code; + SolGroundCodeBlock() = default; + }; + + class SolData { + typedef std::variant varData; + varData data; + public: + SolDataType type = SolDataType::Int; + SolData() = default; + SolData(int64_t in); + SolData(double in); + SolData(std::string in); + SolData(char in); + SolData(bool in); + std::optional getInt(); + std::optional getDouble(); + std::optional getString(); + std::optional getChar(); + std::optional getBool(); + }; + + class SolNode { + SolData data; + public: + SolNodeType nodeType = SolNodeType::None; + std::vector children; + std::string outputId; + SolNode(SolNodeType nodeType); + SolNode(SolNodeType nodeType, SolData data); + SolNode() = default; + void addNode(SolNode in); + void setValue(SolData in); + const std::vector generateCode(); + }; + + class Parser { + std::vector tokensToParse; + size_t current; + size_t size; + + std::optional peek(int ahead = 1); + std::optional consume(); + bool isInt(std::string in); + bool isDouble(std::string in); + bool isString(std::string in); + bool isChar(std::string in); + bool isBool(std::string in); + SolDataType getDataType(std::string in); + SolNodeType getNodeType(std::string in); + + public: + Parser(std::vector in); + SolNode parse(); + }; + + GroundProgram assembleProgram(SolNode& rootNode); + + } // namespace Parser +} + +#endif