forked from solstice/solstice
refactor
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
solstice
|
solstice
|
||||||
|
build
|
||||||
|
|||||||
26
Makefile
Normal file
26
Makefile
Normal file
@@ -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
|
||||||
@@ -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
|
First, ensure CGround is installed on your system with `sudo make install`. Then, compile with
|
||||||
|
|
||||||
```
|
```
|
||||||
g++ src/main.cpp -o solstice -lgroundvm
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|||||||
81
src/argparser.cpp
Normal file
81
src/argparser.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
#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
|
||||||
30
src/argparser.h
Normal file
30
src/argparser.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
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<Argument> output = {};
|
||||||
|
std::optional<Argument> outputtype = {};
|
||||||
|
std::optional<Argument> file = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
void printHelp();
|
||||||
|
Args parseArgs(int argc, char** argv);
|
||||||
|
|
||||||
|
} // namespace ArgParser
|
||||||
140
src/lexer.cpp
Normal file
140
src/lexer.cpp
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
#include "lexer.h"
|
||||||
|
|
||||||
|
namespace Solstice {
|
||||||
|
|
||||||
|
std::optional<char> Lexer::peek(int ahead) {
|
||||||
|
if (current + ahead < size) {
|
||||||
|
return input[current + ahead];
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<char> Lexer::consume() {
|
||||||
|
if (current < size) {
|
||||||
|
return input[current++];
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer::Lexer(std::string in) : input(in), size(in.size()) {}
|
||||||
|
std::vector<std::string> Lexer::lex() {
|
||||||
|
current = 0;
|
||||||
|
std::vector<std::string> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
17
src/lexer.h
Normal file
17
src/lexer.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Solstice {
|
||||||
|
class Lexer {
|
||||||
|
std::string input;
|
||||||
|
size_t size;
|
||||||
|
size_t current;
|
||||||
|
std::optional<char> peek(int ahead = 1);
|
||||||
|
std::optional<char> consume();
|
||||||
|
public:
|
||||||
|
Lexer(std::string in);
|
||||||
|
std::vector<std::string> lex();
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
951
src/main.cpp
951
src/main.cpp
@@ -1,15 +1,13 @@
|
|||||||
#include <cctype>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <groundvm.h>
|
#include <groundvm.h>
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <optional>
|
|
||||||
#include <variant>
|
#include "lexer.h"
|
||||||
#include <cstring>
|
#include "parser.h"
|
||||||
|
#include "argparser.h"
|
||||||
|
|
||||||
#define parseOneToken(token) Parser({token.value()}).parse().children[0]
|
#define parseOneToken(token) Parser({token.value()}).parse().children[0]
|
||||||
|
|
||||||
@@ -18,950 +16,9 @@ namespace Solstice {
|
|||||||
int tmpIdIterator = 0;
|
int tmpIdIterator = 0;
|
||||||
int labelIterator = 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<GroundInstruction> code;
|
|
||||||
SolGroundCodeBlock() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SolData {
|
|
||||||
typedef std::variant<int64_t, std::string, double, bool, char> 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<int64_t> getInt() {
|
|
||||||
if (type == SolDataType::Int) {
|
|
||||||
return std::get<int64_t>(data);
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::optional<double> getDouble() {
|
|
||||||
if (type == SolDataType::Double) {
|
|
||||||
return std::get<double>(data);
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::optional<std::string> getString() {
|
|
||||||
if (type == SolDataType::String) {
|
|
||||||
return std::get<std::string>(data);
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::optional<char> getChar() {
|
|
||||||
if (type == SolDataType::Char) {
|
|
||||||
return std::get<char>(data);
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::optional<bool> getBool() {
|
|
||||||
if (type == SolDataType::Bool) {
|
|
||||||
return std::get<bool>(data);
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SolNode {
|
|
||||||
SolData data;
|
|
||||||
public:
|
|
||||||
SolNodeType nodeType = SolNodeType::None;
|
|
||||||
std::vector<SolNode> 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<SolGroundCodeBlock> generateCode() {
|
|
||||||
std::vector<SolGroundCodeBlock> 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<std::string> tokensToParse;
|
|
||||||
size_t current;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
std::optional<std::string> peek(int ahead = 1) {
|
|
||||||
if (current + ahead < size) {
|
|
||||||
return tokensToParse[current + ahead];
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<char> peek(int ahead = 1) {
|
|
||||||
if (current + ahead < size) {
|
|
||||||
return input[current + ahead];
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<char> consume() {
|
|
||||||
if (current < size) {
|
|
||||||
return input[current++];
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
Lexer(std::string in) : input(in), size(in.size()) {};
|
|
||||||
std::vector<std::string> lex() {
|
|
||||||
current = 0;
|
|
||||||
std::vector<std::string> 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 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<Argument> output = {};
|
|
||||||
std::optional<Argument> outputtype = {};
|
|
||||||
std::optional<Argument> 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) {
|
int main(int argc, char** argv) {
|
||||||
auto args = ArgParser::parseArgs(argc, argv);
|
auto args = ArgParser::parseArgs(argc, argv);
|
||||||
|
|||||||
684
src/parser.cpp
Normal file
684
src/parser.cpp
Normal file
@@ -0,0 +1,684 @@
|
|||||||
|
#include "parser.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#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<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 {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<SolGroundCodeBlock> SolNode::generateCode() {
|
||||||
|
std::vector<SolGroundCodeBlock> 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<std::string> in) : tokensToParse(in) {}
|
||||||
|
|
||||||
|
std::optional<std::string> Parser::peek(int ahead) {
|
||||||
|
if (current + ahead < size) {
|
||||||
|
return tokensToParse[current + ahead];
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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
|
||||||
|
|
||||||
|
}
|
||||||
91
src/parser.h
Normal file
91
src/parser.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#ifndef PARSER_H
|
||||||
|
#define PARSER_H
|
||||||
|
|
||||||
|
#include <groundvm.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
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<GroundInstruction> code;
|
||||||
|
SolGroundCodeBlock() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SolData {
|
||||||
|
typedef std::variant<int64_t, std::string, double, bool, char> 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<int64_t> getInt();
|
||||||
|
std::optional<double> getDouble();
|
||||||
|
std::optional<std::string> getString();
|
||||||
|
std::optional<char> getChar();
|
||||||
|
std::optional<bool> getBool();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SolNode {
|
||||||
|
SolData data;
|
||||||
|
public:
|
||||||
|
SolNodeType nodeType = SolNodeType::None;
|
||||||
|
std::vector<SolNode> 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<SolGroundCodeBlock> generateCode();
|
||||||
|
};
|
||||||
|
|
||||||
|
class Parser {
|
||||||
|
std::vector<std::string> tokensToParse;
|
||||||
|
size_t current;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
std::optional<std::string> peek(int ahead = 1);
|
||||||
|
std::optional<std::string> 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<std::string> in);
|
||||||
|
SolNode parse();
|
||||||
|
};
|
||||||
|
|
||||||
|
GroundProgram assembleProgram(SolNode& rootNode);
|
||||||
|
|
||||||
|
} // namespace Parser
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user