forked from solstice/solstice
Better errors, comments
This commit is contained in:
@@ -4,14 +4,37 @@
|
||||
|
||||
namespace Solstice {
|
||||
namespace Error {
|
||||
[[noreturn]] void syntaxError(std::string what) {
|
||||
std::cout << "\033[31mSyntax error\n";
|
||||
std::cout << "What: " << what << "\n";
|
||||
// ANSI Color Codes
|
||||
const std::string RED = "\033[31m";
|
||||
const std::string YELLOW = "\033[33m";
|
||||
const std::string CYAN = "\033[36m";
|
||||
const std::string RESET = "\033[0m";
|
||||
const std::string BOLD = "\033[1m";
|
||||
|
||||
[[noreturn]] void syntaxError(std::string what, int line, std::string lineContent) {
|
||||
std::cout << BOLD << RED << "Syntax Error" << RESET;
|
||||
if (line > 0) std::cout << " on line " << BOLD << line << RESET;
|
||||
std::cout << ":\n";
|
||||
|
||||
if (!lineContent.empty()) {
|
||||
std::cout << "\n " << CYAN << lineContent << RESET << "\n\n";
|
||||
} else {
|
||||
std::cout << "\n";
|
||||
}
|
||||
std::cout << YELLOW << "-> " << what << RESET << "\n";
|
||||
exit(1);
|
||||
}
|
||||
[[noreturn]] void typingError(std::string what) {
|
||||
std::cout << "\033[31mTyping error\n";
|
||||
std::cout << "What: " << what << "\n";
|
||||
[[noreturn]] void typingError(std::string what, int line, std::string lineContent) {
|
||||
std::cout << BOLD << RED << "Typing Error" << RESET;
|
||||
if (line > 0) std::cout << " on line " << BOLD << line << RESET;
|
||||
std::cout << ":\n";
|
||||
|
||||
if (!lineContent.empty()) {
|
||||
std::cout << "\n " << CYAN << lineContent << RESET << "\n\n";
|
||||
} else {
|
||||
std::cout << "\n";
|
||||
}
|
||||
std::cout << YELLOW << "-> " << what << RESET << "\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#ifndef ERROR_H
|
||||
#define ERROR_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Solstice {
|
||||
namespace Error {
|
||||
[[noreturn]] void syntaxError(std::string what);
|
||||
[[noreturn]] void typingError(std::string what);
|
||||
[[noreturn]] void syntaxError(std::string what, int line = 0, std::string lineContent = "");
|
||||
[[noreturn]] void typingError(std::string what, int line = 0, std::string lineContent = "");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include "lexer.h"
|
||||
|
||||
namespace Solstice {
|
||||
@@ -21,45 +22,102 @@ namespace Solstice {
|
||||
}
|
||||
}
|
||||
|
||||
Lexer::Lexer(std::string in) : input(in), size(in.size()) {}
|
||||
std::vector<std::string> Lexer::lex() {
|
||||
Lexer::Lexer(std::string in) : input(in), size(in.size()) {
|
||||
std::stringstream ss(in);
|
||||
std::string line;
|
||||
while (std::getline(ss, line)) {
|
||||
lines.push_back(line);
|
||||
}
|
||||
if (lines.empty()) lines.push_back("");
|
||||
}
|
||||
|
||||
std::vector<Token> Lexer::lex() {
|
||||
current = 0;
|
||||
std::vector<std::string> tokens;
|
||||
std::vector<Token> tokens;
|
||||
std::string buf;
|
||||
bool inString = false;
|
||||
size_t currentLine = 1;
|
||||
|
||||
auto addToken = [&](std::string val) {
|
||||
std::string content = "";
|
||||
if (currentLine <= lines.size()) {
|
||||
content = lines[currentLine - 1];
|
||||
}
|
||||
tokens.push_back({val, currentLine, content});
|
||||
};
|
||||
|
||||
while (auto copt = consume()) {
|
||||
char c = copt.value();
|
||||
|
||||
if (inString) {
|
||||
buf.push_back(c);
|
||||
if (c == '"') {
|
||||
tokens.push_back(buf);
|
||||
addToken(buf);
|
||||
buf.clear();
|
||||
inString = false;
|
||||
}
|
||||
if (c == '\n') currentLine++;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
// double quotes are special
|
||||
case '"': {
|
||||
if (!buf.empty()) {
|
||||
tokens.push_back(buf);
|
||||
addToken(buf);
|
||||
buf.clear();
|
||||
}
|
||||
buf.push_back(c);
|
||||
inString = true;
|
||||
break;
|
||||
}
|
||||
// slash is special
|
||||
case '/': {
|
||||
if (peek(0).has_value() && peek(0).value() == '/') {
|
||||
while (auto copt = consume()) {
|
||||
char c = copt.value();
|
||||
if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
std::string newToken(1, c);
|
||||
auto tokenopt = peek(0);
|
||||
if (tokenopt) {
|
||||
char token = tokenopt.value();
|
||||
if (token == '=') {
|
||||
newToken += token;
|
||||
consume();
|
||||
}
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
addToken(buf);
|
||||
buf.clear();
|
||||
}
|
||||
addToken(newToken);
|
||||
break;
|
||||
}
|
||||
// tokens which are not followed by anything
|
||||
case '\n':
|
||||
case '(':
|
||||
{
|
||||
if (!buf.empty()) {
|
||||
addToken(buf);
|
||||
buf.clear();
|
||||
}
|
||||
addToken(std::string(1, c));
|
||||
currentLine++;
|
||||
break;
|
||||
}
|
||||
case '(':
|
||||
case ')':
|
||||
case '}':
|
||||
{
|
||||
if (!buf.empty()) {
|
||||
tokens.push_back(buf);
|
||||
addToken(buf);
|
||||
buf.clear();
|
||||
}
|
||||
tokens.push_back(std::string(1, c));
|
||||
addToken(std::string(1, c));
|
||||
break;
|
||||
}
|
||||
// tokens which may be followed by either themselves
|
||||
@@ -77,16 +135,15 @@ namespace Solstice {
|
||||
}
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
tokens.push_back(buf);
|
||||
addToken(buf);
|
||||
buf.clear();
|
||||
}
|
||||
tokens.push_back(newToken);
|
||||
addToken(newToken);
|
||||
break;
|
||||
}
|
||||
// tokens which may be followed by an equals sign
|
||||
case '!':
|
||||
case '*':
|
||||
case '/':
|
||||
case '=':
|
||||
case '>':
|
||||
case '<':
|
||||
@@ -101,27 +158,29 @@ namespace Solstice {
|
||||
}
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
tokens.push_back(buf);
|
||||
addToken(buf);
|
||||
buf.clear();
|
||||
}
|
||||
tokens.push_back(newToken);
|
||||
addToken(newToken);
|
||||
break;
|
||||
}
|
||||
// tokens which need a newline inserted for them
|
||||
case '{':
|
||||
{
|
||||
if (!buf.empty()) {
|
||||
tokens.push_back(buf);
|
||||
addToken(buf);
|
||||
buf.clear();
|
||||
}
|
||||
tokens.push_back("\n");
|
||||
tokens.push_back(std::string(1, c));
|
||||
addToken("\n");
|
||||
addToken(std::string(1, c));
|
||||
break;
|
||||
}
|
||||
// tokens which do not need to be included
|
||||
case ' ':
|
||||
case '\r':
|
||||
{
|
||||
if (!buf.empty()) {
|
||||
tokens.push_back(buf);
|
||||
addToken(buf);
|
||||
buf.clear();
|
||||
}
|
||||
break;
|
||||
@@ -134,7 +193,7 @@ namespace Solstice {
|
||||
}
|
||||
|
||||
if (!buf.empty()) {
|
||||
tokens.push_back(buf);
|
||||
addToken(buf);
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
14
src/lexer.h
14
src/lexer.h
@@ -1,17 +1,29 @@
|
||||
#ifndef LEXER_H
|
||||
#define LEXER_H
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace Solstice {
|
||||
struct Token {
|
||||
std::string value;
|
||||
int line;
|
||||
std::string lineContent;
|
||||
};
|
||||
|
||||
class Lexer {
|
||||
std::string input;
|
||||
std::vector<std::string> lines;
|
||||
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();
|
||||
std::vector<Token> lex();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
225
src/parser.cpp
225
src/parser.cpp
@@ -4,7 +4,7 @@
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#define parseOneToken(token) Parser({token.value()}).parse().children[0]
|
||||
#define parseOneToken(token) Parser({token}).parse().children[0]
|
||||
|
||||
namespace Solstice {
|
||||
namespace Parser {
|
||||
@@ -21,7 +21,7 @@ namespace Solstice {
|
||||
if (variables.find(i.outputId) != variables.end()) {
|
||||
return variables[i.outputId];
|
||||
} else {
|
||||
Error::syntaxError("Unknown variable");
|
||||
Error::syntaxError("Unknown variable", i.line, i.lineContent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -178,14 +178,14 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Add: {
|
||||
ensure3(children[0], SolDataType::Int, SolDataType::Double, SolDataType::String);
|
||||
ensure3(children[1], SolDataType::Int, SolDataType::Double, SolDataType::String);
|
||||
ensuresame(children[0], children[1]);
|
||||
ensure3(children[0], SolDataType::Int, SolDataType::Double, SolDataType::String, "operator '+'");
|
||||
ensure3(children[1], SolDataType::Int, SolDataType::Double, SolDataType::String, "operator '+'");
|
||||
ensuresame(children[0], children[1], "operator '+'");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
GroundInstruction gi = groundCreateInstruction(ADD);
|
||||
if (children.size() < 2) {
|
||||
std::cout << "Need more stuff to add\n";
|
||||
Error::typingError("Need more stuff to add", children[0].line, children[0].lineContent);
|
||||
}
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data()));
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
|
||||
@@ -197,13 +197,13 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Subtract: {
|
||||
ensure2(children[0], SolDataType::Int, SolDataType::Double);
|
||||
ensure2(children[1], SolDataType::Int, SolDataType::Double);
|
||||
ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '-'");
|
||||
ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '-'");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
GroundInstruction gi = groundCreateInstruction(SUBTRACT);
|
||||
if (children.size() < 2) {
|
||||
std::cout << "Need more stuff to add\n";
|
||||
Error::typingError("Need more stuff to subtract", children[0].line, children[0].lineContent);
|
||||
}
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data()));
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
|
||||
@@ -215,13 +215,13 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Multiply: {
|
||||
ensure2(children[0], SolDataType::Int, SolDataType::Double);
|
||||
ensure2(children[1], SolDataType::Int, SolDataType::Double);
|
||||
ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '*'");
|
||||
ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '*'");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
GroundInstruction gi = groundCreateInstruction(MULTIPLY);
|
||||
if (children.size() < 2) {
|
||||
std::cout << "Need more stuff to add\n";
|
||||
Error::typingError("Need more stuff to multiply", children[0].line, children[0].lineContent);
|
||||
}
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data()));
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
|
||||
@@ -233,13 +233,13 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Divide: {
|
||||
ensure2(children[0], SolDataType::Int, SolDataType::Double);
|
||||
ensure2(children[1], SolDataType::Int, SolDataType::Double);
|
||||
ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '/'");
|
||||
ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '/'");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
GroundInstruction gi = groundCreateInstruction(DIVIDE);
|
||||
if (children.size() < 2) {
|
||||
std::cout << "Need more stuff to add\n";
|
||||
Error::typingError("Need more stuff to divide", children[0].line, children[0].lineContent);
|
||||
}
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data()));
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
|
||||
@@ -251,12 +251,12 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Equal: {
|
||||
ensuresame(children[0], children[1]);
|
||||
ensuresame(children[0], children[1], "operator '=='");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
GroundInstruction gi = groundCreateInstruction(EQUAL);
|
||||
if (children.size() < 2) {
|
||||
std::cout << "Need more stuff to equal\n";
|
||||
Error::typingError("Need more stuff to equal", children[0].line, children[0].lineContent);
|
||||
}
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data()));
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
|
||||
@@ -268,12 +268,12 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Inequal: {
|
||||
ensuresame(children[0], children[1]);
|
||||
ensuresame(children[0], children[1], "operator '!='");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
GroundInstruction gi = groundCreateInstruction(INEQUAL);
|
||||
if (children.size() < 2) {
|
||||
std::cout << "Need more stuff to inequal\n";
|
||||
Error::typingError("Need more stuff to inequal", children[0].line, children[0].lineContent);
|
||||
}
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data()));
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
|
||||
@@ -285,12 +285,12 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Greater: {
|
||||
ensuresame(children[0], children[1]);
|
||||
ensuresame(children[0], children[1], "operator '>'");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
GroundInstruction gi = groundCreateInstruction(GREATER);
|
||||
if (children.size() < 2) {
|
||||
std::cout << "Need more stuff to greater\n";
|
||||
Error::typingError("Need more stuff to greater", children[0].line, children[0].lineContent);
|
||||
}
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data()));
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
|
||||
@@ -302,12 +302,12 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Lesser: {
|
||||
ensuresame(children[0], children[1]);
|
||||
ensuresame(children[0], children[1], "operator '<'");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
GroundInstruction gi = groundCreateInstruction(LESSER);
|
||||
if (children.size() < 2) {
|
||||
std::cout << "Need more stuff to lesser\n";
|
||||
Error::typingError("Need more stuff to lesser", children[0].line, children[0].lineContent);
|
||||
}
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data()));
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
|
||||
@@ -319,10 +319,10 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::EqGreater: {
|
||||
ensuresame(children[0], children[1]);
|
||||
ensuresame(children[0], children[1], "operator '>='");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
if (children.size() < 2) {
|
||||
std::cout << "Need more stuff to inequal\n";
|
||||
Error::typingError("Need more stuff to inequal", children[0].line, children[0].lineContent);
|
||||
}
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
std::string trueLabelIdString = "internal_true" + std::to_string(labelIterator++);
|
||||
@@ -392,10 +392,10 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::EqLesser: {
|
||||
ensuresame(children[0], children[1]);
|
||||
ensuresame(children[0], children[1], "operator '<='");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
if (children.size() < 2) {
|
||||
std::cout << "Need more stuff to inequal\n";
|
||||
Error::typingError("Need more stuff to inequal", children[0].line, children[0].lineContent);
|
||||
}
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
std::string trueLabelIdString = "internal_true" + std::to_string(labelIterator++);
|
||||
@@ -469,7 +469,7 @@ namespace Solstice {
|
||||
exists(children[0]);
|
||||
GroundInstruction gi = groundCreateInstruction(PRINTLN);
|
||||
if (children.size() < 1) {
|
||||
std::cout << "Need more stuff to puts\n";
|
||||
Error::typingError("Need more stuff to puts", children[0].line, children[0].lineContent);
|
||||
}
|
||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data()));
|
||||
codeBlock.code.push_back(gi);
|
||||
@@ -478,7 +478,7 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::If: {
|
||||
ensure(children[0], SolDataType::Bool);
|
||||
ensure(children[0], SolDataType::Bool, "if condition");
|
||||
auto conditionCode = children[0].generateCode();
|
||||
code.insert(code.end(), conditionCode.begin(), conditionCode.end());
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
@@ -510,7 +510,7 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::While: {
|
||||
ensure(children[0], SolDataType::Bool);
|
||||
ensure(children[0], SolDataType::Bool, "while condition");
|
||||
SolGroundCodeBlock startLabelBlock;
|
||||
std::string startLabelIdString = "whilestart_" + std::to_string(labelIterator++);
|
||||
std::string endLabelIdString = "whileend_" + std::to_string(labelIterator);
|
||||
@@ -562,7 +562,7 @@ namespace Solstice {
|
||||
case SolNodeType::Set: {
|
||||
if (variables.find(children[0].outputId) != variables.end()) {
|
||||
if (variables[children[0].outputId] != checkNodeReturnType(children[1])) {
|
||||
Error::typingError("Cannot change type of this variable");
|
||||
Error::typingError("Cannot change type of this variable", children[0].line, children[0].lineContent);
|
||||
}
|
||||
}
|
||||
exists(children[1]);
|
||||
@@ -580,7 +580,7 @@ namespace Solstice {
|
||||
case SolNodeType::FunctionCall: {
|
||||
// Take care of in built functions
|
||||
if (children[0].outputId == "input") {
|
||||
ensure(children[1], SolDataType::String);
|
||||
ensure(children[1], SolDataType::String, "input function argument");
|
||||
SolGroundCodeBlock inputCodeBlock;
|
||||
if (children.size() > 1) {
|
||||
GroundInstruction printInstruction = groundCreateInstruction(PRINT);
|
||||
@@ -604,9 +604,9 @@ namespace Solstice {
|
||||
}
|
||||
|
||||
// Parser Implementation
|
||||
Parser::Parser(std::vector<std::string> in) : tokensToParse(in) {}
|
||||
Parser::Parser(std::vector<Token> in) : tokensToParse(in) {}
|
||||
|
||||
std::optional<std::string> Parser::peek(int ahead) {
|
||||
std::optional<Token> Parser::peek(int ahead) {
|
||||
if (current + ahead < size) {
|
||||
return tokensToParse[current + ahead];
|
||||
} else {
|
||||
@@ -614,7 +614,7 @@ namespace Solstice {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> Parser::consume() {
|
||||
std::optional<Token> Parser::consume() {
|
||||
if (current < size) {
|
||||
return tokensToParse[current++];
|
||||
} else {
|
||||
@@ -752,14 +752,16 @@ namespace Solstice {
|
||||
SolNode Parser::parsePrimary() {
|
||||
auto tokenopt = consume();
|
||||
if (!tokenopt) {
|
||||
std::cout << "Unexpected end of input\n";
|
||||
exit(1);
|
||||
Error::syntaxError("Unexpected end of input");
|
||||
}
|
||||
std::string token = tokenopt.value();
|
||||
Token tokenObj = tokenopt.value();
|
||||
std::string token = tokenObj.value;
|
||||
SolNodeType type = getNodeType(token);
|
||||
|
||||
if (type == SolNodeType::Value) {
|
||||
SolNode node(SolNodeType::Value);
|
||||
node.line = tokenObj.line;
|
||||
node.lineContent = tokenObj.lineContent;
|
||||
switch (getDataType(token)) {
|
||||
case SolDataType::Int: node.setValue((int64_t) std::stoll(token)); break;
|
||||
case SolDataType::Double: node.setValue(std::stod(token)); break;
|
||||
@@ -774,19 +776,28 @@ namespace Solstice {
|
||||
if (type == SolNodeType::Identifier) {
|
||||
SolNode idNode(SolNodeType::Identifier);
|
||||
idNode.outputId = token;
|
||||
if (peek().value_or("") == "(") {
|
||||
idNode.line = tokenObj.line;
|
||||
idNode.lineContent = tokenObj.lineContent;
|
||||
|
||||
auto peekOpt = peek();
|
||||
if (peekOpt.has_value() && peekOpt.value().value == "(") {
|
||||
consume(); // (
|
||||
SolNode fnCallNode(SolNodeType::FunctionCall);
|
||||
fnCallNode.line = tokenObj.line;
|
||||
fnCallNode.lineContent = tokenObj.lineContent;
|
||||
fnCallNode.addNode(idNode);
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
std::vector<Token> tokens;
|
||||
size_t brackets = 1;
|
||||
while (auto t = consume()) {
|
||||
if (t.value() == "(") brackets++;
|
||||
if (t.value() == ")") brackets--;
|
||||
if (t.value().value == "(") brackets++;
|
||||
if (t.value().value == ")") brackets--;
|
||||
if (brackets == 0) break;
|
||||
tokens.push_back(t.value());
|
||||
}
|
||||
if (brackets != 0) {
|
||||
Error::syntaxError("Unclosed function call bracket", tokenObj.line, tokenObj.lineContent);
|
||||
}
|
||||
auto args = Parser(tokens).parse();
|
||||
for(auto& child : args.children) fnCallNode.addNode(child);
|
||||
return fnCallNode;
|
||||
@@ -795,21 +806,23 @@ namespace Solstice {
|
||||
}
|
||||
|
||||
if (type == SolNodeType::BracketStart) {
|
||||
std::vector<std::string> tokens;
|
||||
std::vector<Token> tokens;
|
||||
size_t brackets = 1;
|
||||
while (auto t = consume()) {
|
||||
if (t.value() == "(") brackets++;
|
||||
if (t.value() == ")") brackets--;
|
||||
if (t.value().value == "(") brackets++;
|
||||
if (t.value().value == ")") brackets--;
|
||||
if (brackets == 0) break;
|
||||
tokens.push_back(t.value());
|
||||
}
|
||||
if (brackets != 0) {
|
||||
Error::syntaxError("Unclosed bracket", tokenObj.line, tokenObj.lineContent);
|
||||
}
|
||||
auto inner = Parser(tokens).parse();
|
||||
if (inner.children.size() > 0) return inner.children[0];
|
||||
return SolNode(SolNodeType::None);
|
||||
}
|
||||
|
||||
std::cout << "Unexpected token in expression: " << token << "\n";
|
||||
exit(1);
|
||||
Error::syntaxError("Unexpected token in expression: " + token, tokenObj.line, tokenObj.lineContent);
|
||||
}
|
||||
|
||||
SolNode Parser::parseExpression(int minPrec) {
|
||||
@@ -817,13 +830,16 @@ namespace Solstice {
|
||||
while(true) {
|
||||
auto opOpt = peek();
|
||||
if (!opOpt) break;
|
||||
std::string op = opOpt.value();
|
||||
Token opToken = opOpt.value();
|
||||
std::string op = opToken.value;
|
||||
int prec = getPrecedence(op);
|
||||
if (prec < minPrec) break;
|
||||
|
||||
consume();
|
||||
SolNode rhs = parseExpression(prec + 1);
|
||||
SolNode newNode(getNodeType(op));
|
||||
newNode.line = opToken.line;
|
||||
newNode.lineContent = opToken.lineContent;
|
||||
newNode.addNode(lhs);
|
||||
newNode.addNode(rhs);
|
||||
lhs = newNode;
|
||||
@@ -836,37 +852,48 @@ namespace Solstice {
|
||||
size = tokensToParse.size();
|
||||
SolNode rootNode(SolNodeType::Root);
|
||||
while (auto tokenopt = consume()) {
|
||||
std::string token = tokenopt.value();
|
||||
Token tokenObj = tokenopt.value();
|
||||
std::string token = tokenObj.value;
|
||||
switch (getNodeType(token)) {
|
||||
case SolNodeType::Value: {
|
||||
switch (getDataType(token)) {
|
||||
case SolDataType::Int: {
|
||||
SolNode intNode(SolNodeType::Value);
|
||||
intNode.setValue((int64_t) std::stoll(token));
|
||||
intNode.line = tokenObj.line;
|
||||
intNode.lineContent = tokenObj.lineContent;
|
||||
rootNode.addNode(intNode);
|
||||
break;
|
||||
}
|
||||
case SolDataType::Double: {
|
||||
SolNode doubleNode(SolNodeType::Value);
|
||||
doubleNode.setValue(std::stod(token));
|
||||
doubleNode.line = tokenObj.line;
|
||||
doubleNode.lineContent = tokenObj.lineContent;
|
||||
rootNode.addNode(doubleNode);
|
||||
break;
|
||||
}
|
||||
case SolDataType::String: {
|
||||
SolNode stringNode(SolNodeType::Value);
|
||||
stringNode.setValue(token.substr(1, token.size() - 2));
|
||||
stringNode.line = tokenObj.line;
|
||||
stringNode.lineContent = tokenObj.lineContent;
|
||||
rootNode.addNode(stringNode);
|
||||
break;
|
||||
}
|
||||
case SolDataType::Char: {
|
||||
SolNode charNode(SolNodeType::Value);
|
||||
charNode.setValue(token[1]);
|
||||
charNode.line = tokenObj.line;
|
||||
charNode.lineContent = tokenObj.lineContent;
|
||||
rootNode.addNode(charNode);
|
||||
break;
|
||||
}
|
||||
case SolDataType::Bool: {
|
||||
SolNode boolNode(SolNodeType::Value);
|
||||
boolNode.setValue(token == "true");
|
||||
boolNode.line = tokenObj.line;
|
||||
boolNode.lineContent = tokenObj.lineContent;
|
||||
rootNode.addNode(boolNode);
|
||||
break;
|
||||
}
|
||||
@@ -879,9 +906,11 @@ namespace Solstice {
|
||||
case SolNodeType::Divide:
|
||||
{
|
||||
SolNode opNode(getNodeType(token));
|
||||
opNode.line = tokenObj.line;
|
||||
opNode.lineContent = tokenObj.lineContent;
|
||||
|
||||
if (rootNode.children.empty()) {
|
||||
std::cout << "Expected operand before operator\n";
|
||||
exit(1);
|
||||
Error::syntaxError("Expected operand before operator", tokenObj.line, tokenObj.lineContent);
|
||||
}
|
||||
opNode.addNode(rootNode.children.back());
|
||||
rootNode.children.pop_back();
|
||||
@@ -901,15 +930,17 @@ namespace Solstice {
|
||||
case SolNodeType::EqGreater:
|
||||
{
|
||||
SolNode equalNode(getNodeType(token));
|
||||
equalNode.line = tokenObj.line;
|
||||
equalNode.lineContent = tokenObj.lineContent;
|
||||
|
||||
if (rootNode.children.empty()) {
|
||||
std::cout << "ah dingus\n";
|
||||
exit(1);
|
||||
Error::syntaxError("Expected operand before comparison", tokenObj.line, tokenObj.lineContent);
|
||||
}
|
||||
equalNode.addNode(rootNode.children.back());
|
||||
rootNode.children.pop_back();
|
||||
std::vector<std::string> tokens;
|
||||
std::vector<Token> tokens;
|
||||
while (auto tokenopt = consume()) {
|
||||
if (tokenopt.value() == "\n") {
|
||||
if (tokenopt.value().value == "\n") {
|
||||
break;
|
||||
}
|
||||
tokens.push_back(tokenopt.value());
|
||||
@@ -921,9 +952,11 @@ namespace Solstice {
|
||||
}
|
||||
case SolNodeType::Puts: {
|
||||
SolNode putsNode(SolNodeType::Puts);
|
||||
std::vector<std::string> tokens;
|
||||
putsNode.line = tokenObj.line;
|
||||
putsNode.lineContent = tokenObj.lineContent;
|
||||
std::vector<Token> tokens;
|
||||
while (auto tokenopt = consume()) {
|
||||
if (tokenopt.value() == "\n") {
|
||||
if (tokenopt.value().value == "\n") {
|
||||
break;
|
||||
}
|
||||
tokens.push_back(tokenopt.value());
|
||||
@@ -937,9 +970,11 @@ namespace Solstice {
|
||||
}
|
||||
case SolNodeType::If: {
|
||||
SolNode ifNode(SolNodeType::If);
|
||||
std::vector<std::string> tokens;
|
||||
ifNode.line = tokenObj.line;
|
||||
ifNode.lineContent = tokenObj.lineContent;
|
||||
std::vector<Token> tokens;
|
||||
while (auto tokenopt = consume()) {
|
||||
if (tokenopt.value() == "\n") {
|
||||
if (tokenopt.value().value == "\n") {
|
||||
break;
|
||||
}
|
||||
tokens.push_back(tokenopt.value());
|
||||
@@ -950,27 +985,28 @@ namespace Solstice {
|
||||
size_t brackets = 1;
|
||||
auto tokenopt = consume();
|
||||
if (tokenopt) {
|
||||
if (tokenopt.value() == "{") {
|
||||
if (tokenopt.value().value == "{") {
|
||||
tokens.push_back(tokenopt.value());
|
||||
while (auto tokenopt = consume()) {
|
||||
tokens.push_back(tokenopt.value());
|
||||
if (tokenopt.value() == "{") {
|
||||
if (tokenopt.value().value == "{") {
|
||||
brackets++;
|
||||
}
|
||||
if (tokenopt.value() == "}") {
|
||||
if (tokenopt.value().value == "}") {
|
||||
brackets--;
|
||||
}
|
||||
if (brackets == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (brackets != 0) {
|
||||
Error::syntaxError("Unclosed code block", tokenopt.value().line, tokenopt.value().lineContent);
|
||||
}
|
||||
} else {
|
||||
std::cout << "I want a code block instead of a " + tokenopt.value() + "\n";
|
||||
exit(1);
|
||||
Error::syntaxError("I want a code block instead of a " + tokenopt.value().value, tokenopt.value().line, tokenopt.value().lineContent);
|
||||
}
|
||||
} else {
|
||||
std::cout << "FEED ME MORE TOKENSSSSS\n";
|
||||
exit(1);
|
||||
Error::syntaxError("Expected code block after if condition", ifNode.line, ifNode.lineContent);
|
||||
}
|
||||
auto childCodeBlock = Parser(tokens).parse();
|
||||
ifNode.addNode(childCodeBlock.children[0]);
|
||||
@@ -979,9 +1015,11 @@ namespace Solstice {
|
||||
}
|
||||
case SolNodeType::While: {
|
||||
SolNode whileNode(SolNodeType::While);
|
||||
std::vector<std::string> tokens;
|
||||
whileNode.line = tokenObj.line;
|
||||
whileNode.lineContent = tokenObj.lineContent;
|
||||
std::vector<Token> tokens;
|
||||
while (auto tokenopt = consume()) {
|
||||
if (tokenopt.value() == "\n") {
|
||||
if (tokenopt.value().value == "\n") {
|
||||
break;
|
||||
}
|
||||
tokens.push_back(tokenopt.value());
|
||||
@@ -992,27 +1030,28 @@ namespace Solstice {
|
||||
size_t brackets = 1;
|
||||
auto tokenopt = consume();
|
||||
if (tokenopt) {
|
||||
if (tokenopt.value() == "{") {
|
||||
if (tokenopt.value().value == "{") {
|
||||
tokens.push_back(tokenopt.value());
|
||||
while (auto tokenopt = consume()) {
|
||||
tokens.push_back(tokenopt.value());
|
||||
if (tokenopt.value() == "{") {
|
||||
if (tokenopt.value().value == "{") {
|
||||
brackets++;
|
||||
}
|
||||
if (tokenopt.value() == "}") {
|
||||
if (tokenopt.value().value == "}") {
|
||||
brackets--;
|
||||
}
|
||||
if (brackets == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (brackets != 0) {
|
||||
Error::syntaxError("Unclosed code block", tokenopt.value().line, tokenopt.value().lineContent);
|
||||
}
|
||||
} else {
|
||||
std::cout << "I want a code block instead of a " + tokenopt.value() + "\n";
|
||||
exit(1);
|
||||
Error::syntaxError("I want a code block instead of a " + tokenopt.value().value, tokenopt.value().line, tokenopt.value().lineContent);
|
||||
}
|
||||
} else {
|
||||
std::cout << "FEED ME MORE TOKENSSSSS\n";
|
||||
exit(1);
|
||||
Error::syntaxError("Expected code block after while condition", whileNode.line, whileNode.lineContent);
|
||||
}
|
||||
auto childCodeBlock = Parser(tokens).parse();
|
||||
whileNode.addNode(childCodeBlock.children[0]);
|
||||
@@ -1021,13 +1060,15 @@ namespace Solstice {
|
||||
}
|
||||
case SolNodeType::CodeBlockStart: {
|
||||
SolNode codeBlockNode(SolNodeType::CodeBlock);
|
||||
codeBlockNode.line = tokenObj.line;
|
||||
codeBlockNode.lineContent = tokenObj.lineContent;
|
||||
size_t brackets = 1;
|
||||
std::vector<std::string> tokens;
|
||||
std::vector<Token> tokens;
|
||||
while (auto tokenopt = consume()) {
|
||||
if (tokenopt.value() == "{") {
|
||||
if (tokenopt.value().value == "{") {
|
||||
brackets++;
|
||||
}
|
||||
if (tokenopt.value() == "}") {
|
||||
if (tokenopt.value().value == "}") {
|
||||
brackets--;
|
||||
}
|
||||
if (brackets == 0) {
|
||||
@@ -1035,6 +1076,9 @@ namespace Solstice {
|
||||
}
|
||||
tokens.push_back(tokenopt.value());
|
||||
}
|
||||
if (brackets != 0) {
|
||||
Error::syntaxError("Unclosed code block", tokenObj.line, tokenObj.lineContent);
|
||||
}
|
||||
codeBlockNode.children = Parser(tokens).parse().children;
|
||||
rootNode.addNode(codeBlockNode);
|
||||
break;
|
||||
@@ -1042,16 +1086,21 @@ namespace Solstice {
|
||||
case SolNodeType::Identifier: {
|
||||
SolNode idNode(SolNodeType::Identifier);
|
||||
idNode.outputId = token;
|
||||
idNode.line = tokenObj.line;
|
||||
idNode.lineContent = tokenObj.lineContent;
|
||||
rootNode.addNode(idNode);
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Set: {
|
||||
SolNode setNode(SolNodeType::Set);
|
||||
setNode.line = tokenObj.line;
|
||||
setNode.lineContent = tokenObj.lineContent;
|
||||
|
||||
setNode.addNode(rootNode.children.back());
|
||||
rootNode.children.pop_back();
|
||||
std::vector<std::string> tokens;
|
||||
std::vector<Token> tokens;
|
||||
while (auto tokenopt = consume()) {
|
||||
if (tokenopt.value() == "\n") {
|
||||
if (tokenopt.value().value == "\n") {
|
||||
break;
|
||||
}
|
||||
tokens.push_back(tokenopt.value());
|
||||
@@ -1064,15 +1113,17 @@ namespace Solstice {
|
||||
case SolNodeType::BracketStart: {
|
||||
if (rootNode.children.back().nodeType == SolNodeType::Identifier) {
|
||||
SolNode fnCallNode(SolNodeType::FunctionCall);
|
||||
fnCallNode.line = tokenObj.line;
|
||||
fnCallNode.lineContent = tokenObj.lineContent;
|
||||
fnCallNode.addNode(rootNode.children.back());
|
||||
rootNode.children.pop_back();
|
||||
std::vector<std::string> tokens;
|
||||
std::vector<Token> tokens;
|
||||
size_t brackets = 1;
|
||||
while (auto tokenopt = consume()) {
|
||||
if (tokenopt.value() == "(") {
|
||||
if (tokenopt.value().value == "(") {
|
||||
brackets++;
|
||||
}
|
||||
if (tokenopt.value() == ")") {
|
||||
if (tokenopt.value().value == ")") {
|
||||
brackets--;
|
||||
}
|
||||
if (brackets < 1) {
|
||||
@@ -1084,13 +1135,13 @@ namespace Solstice {
|
||||
fnCallNode.children.insert(fnCallNode.children.end(), node.children.begin(), node.children.end());
|
||||
rootNode.addNode(fnCallNode);
|
||||
} else {
|
||||
std::vector<std::string> tokens;
|
||||
std::vector<Token> tokens;
|
||||
size_t brackets = 1;
|
||||
while (auto tokenopt = consume()) {
|
||||
if (tokenopt.value() == "(") {
|
||||
if (tokenopt.value().value == "(") {
|
||||
brackets++;
|
||||
}
|
||||
if (tokenopt.value() == ")") {
|
||||
if (tokenopt.value().value == ")") {
|
||||
brackets--;
|
||||
}
|
||||
if (brackets < 1) {
|
||||
@@ -1128,4 +1179,4 @@ namespace Solstice {
|
||||
|
||||
} // namespace Parser
|
||||
|
||||
}
|
||||
}
|
||||
33
src/parser.h
33
src/parser.h
@@ -7,32 +7,34 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include "lexer.h"
|
||||
#include "error.h"
|
||||
|
||||
#define ensure(node, datatype) \
|
||||
#define ensure(node, datatype, op) \
|
||||
if (checkNodeReturnType(node) != datatype) { \
|
||||
Error::typingError("Expected " #datatype); \
|
||||
Error::typingError("Expected " #datatype " for " op, node.line, node.lineContent); \
|
||||
}
|
||||
|
||||
#define ensure2(node, datatype1, datatype2) { \
|
||||
#define ensure2(node, datatype1, datatype2, op) { \
|
||||
SolDataType dataType = checkNodeReturnType(node); \
|
||||
if (!(dataType == datatype1 || dataType == datatype2)) { \
|
||||
Error::typingError("Expected either " #datatype1 " or " #datatype2); \
|
||||
Error::typingError("Expected either " #datatype1 " or " #datatype2 " for " op, node.line, node.lineContent); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ensure3(node, datatype1, datatype2, datatype3) { \
|
||||
#define ensure3(node, datatype1, datatype2, datatype3, op) { \
|
||||
SolDataType dataType = checkNodeReturnType(node); \
|
||||
if (!(dataType == datatype1 || dataType == datatype2 || dataType == datatype3)) { \
|
||||
Error::typingError("Expected either " #datatype1 ", " #datatype2 ", or " #datatype3); \
|
||||
Error::typingError("Expected either " #datatype1 ", " #datatype2 ", or " #datatype3 " for " op, node.line, node.lineContent); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ensuresame(node1, node2) { \
|
||||
#define ensuresame(node1, node2, op) { \
|
||||
SolDataType n1t = checkNodeReturnType(node1); \
|
||||
SolDataType n2t = checkNodeReturnType(node2); \
|
||||
if (n1t != n2t) { \
|
||||
if (!(n1t == SolDataType::Int && n2t == SolDataType::Double || n1t == SolDataType::Double && n2t == SolDataType::Int)) { \
|
||||
Error::typingError("Expected types to be the same"); \
|
||||
Error::typingError("Expected types to be the same for " op, node1.line, node1.lineContent); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
@@ -40,11 +42,11 @@
|
||||
#define exists(node) \
|
||||
if (node.nodeType == SolNodeType::Identifier) { \
|
||||
if (variables.find(node.outputId) == variables.end()) { \
|
||||
Error::syntaxError("Variable does not exist"); \
|
||||
Error::syntaxError("Variable does not exist", node.line, node.lineContent); \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace Solstice {
|
||||
namespace Solstice {
|
||||
|
||||
// External variables referenced in parser logic
|
||||
extern int tmpIdIterator;
|
||||
@@ -96,6 +98,9 @@ namespace Solstice {
|
||||
SolNodeType nodeType = SolNodeType::None;
|
||||
std::vector<SolNode> children;
|
||||
std::string outputId;
|
||||
int line = 0;
|
||||
std::string lineContent = "";
|
||||
|
||||
SolNode(SolNodeType nodeType);
|
||||
SolNode(SolNodeType nodeType, SolData data);
|
||||
SolNode() = default;
|
||||
@@ -105,12 +110,12 @@ namespace Solstice {
|
||||
};
|
||||
|
||||
class Parser {
|
||||
std::vector<std::string> tokensToParse;
|
||||
std::vector<Token> tokensToParse;
|
||||
size_t current;
|
||||
size_t size;
|
||||
|
||||
std::optional<std::string> peek(int ahead = 0);
|
||||
std::optional<std::string> consume();
|
||||
std::optional<Token> peek(int ahead = 0);
|
||||
std::optional<Token> consume();
|
||||
bool isInt(std::string in);
|
||||
bool isDouble(std::string in);
|
||||
bool isString(std::string in);
|
||||
@@ -124,7 +129,7 @@ namespace Solstice {
|
||||
int getPrecedence(std::string token);
|
||||
|
||||
public:
|
||||
Parser(std::vector<std::string> in);
|
||||
Parser(std::vector<Token> in);
|
||||
SolNode parse();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user