Better errors, comments

This commit is contained in:
2025-12-25 22:37:40 +11:00
parent aa5ef0e664
commit 9a311c3cb8
6 changed files with 283 additions and 128 deletions

View File

@@ -4,14 +4,37 @@
namespace Solstice { namespace Solstice {
namespace Error { namespace Error {
[[noreturn]] void syntaxError(std::string what) { // ANSI Color Codes
std::cout << "\033[31mSyntax error\n"; const std::string RED = "\033[31m";
std::cout << "What: " << what << "\n"; 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); exit(1);
} }
[[noreturn]] void typingError(std::string what) { [[noreturn]] void typingError(std::string what, int line, std::string lineContent) {
std::cout << "\033[31mTyping error\n"; std::cout << BOLD << RED << "Typing Error" << RESET;
std::cout << "What: " << what << "\n"; 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); exit(1);
} }
} }

View File

@@ -1,8 +1,13 @@
#ifndef ERROR_H
#define ERROR_H
#include <string> #include <string>
namespace Solstice { namespace Solstice {
namespace Error { namespace Error {
[[noreturn]] void syntaxError(std::string what); [[noreturn]] void syntaxError(std::string what, int line = 0, std::string lineContent = "");
[[noreturn]] void typingError(std::string what); [[noreturn]] void typingError(std::string what, int line = 0, std::string lineContent = "");
} }
} }
#endif

View File

@@ -1,6 +1,7 @@
#include <string> #include <string>
#include <optional> #include <optional>
#include <vector> #include <vector>
#include <sstream>
#include "lexer.h" #include "lexer.h"
namespace Solstice { namespace Solstice {
@@ -21,45 +22,102 @@ namespace Solstice {
} }
} }
Lexer::Lexer(std::string in) : input(in), size(in.size()) {} Lexer::Lexer(std::string in) : input(in), size(in.size()) {
std::vector<std::string> Lexer::lex() { 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; current = 0;
std::vector<std::string> tokens; std::vector<Token> tokens;
std::string buf; std::string buf;
bool inString = false; 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()) { while (auto copt = consume()) {
char c = copt.value(); char c = copt.value();
if (inString) { if (inString) {
buf.push_back(c); buf.push_back(c);
if (c == '"') { if (c == '"') {
tokens.push_back(buf); addToken(buf);
buf.clear(); buf.clear();
inString = false; inString = false;
} }
if (c == '\n') currentLine++;
continue; continue;
} }
switch (c) { switch (c) {
// double quotes are special // double quotes are special
case '"': { case '"': {
if (!buf.empty()) { if (!buf.empty()) {
tokens.push_back(buf); addToken(buf);
buf.clear(); buf.clear();
} }
buf.push_back(c); buf.push_back(c);
inString = true; inString = true;
break; 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 // tokens which are not followed by anything
case '\n': case '\n':
{
if (!buf.empty()) {
addToken(buf);
buf.clear();
}
addToken(std::string(1, c));
currentLine++;
break;
}
case '(': case '(':
case ')': case ')':
case '}': case '}':
{ {
if (!buf.empty()) { if (!buf.empty()) {
tokens.push_back(buf); addToken(buf);
buf.clear(); buf.clear();
} }
tokens.push_back(std::string(1, c)); addToken(std::string(1, c));
break; break;
} }
// tokens which may be followed by either themselves // tokens which may be followed by either themselves
@@ -77,16 +135,15 @@ namespace Solstice {
} }
} }
if (!buf.empty()) { if (!buf.empty()) {
tokens.push_back(buf); addToken(buf);
buf.clear(); buf.clear();
} }
tokens.push_back(newToken); addToken(newToken);
break; break;
} }
// tokens which may be followed by an equals sign // tokens which may be followed by an equals sign
case '!': case '!':
case '*': case '*':
case '/':
case '=': case '=':
case '>': case '>':
case '<': case '<':
@@ -101,27 +158,29 @@ namespace Solstice {
} }
} }
if (!buf.empty()) { if (!buf.empty()) {
tokens.push_back(buf); addToken(buf);
buf.clear(); buf.clear();
} }
tokens.push_back(newToken); addToken(newToken);
break; break;
} }
// tokens which need a newline inserted for them // tokens which need a newline inserted for them
case '{': case '{':
{ {
if (!buf.empty()) { if (!buf.empty()) {
tokens.push_back(buf); addToken(buf);
buf.clear(); buf.clear();
} }
tokens.push_back("\n"); addToken("\n");
tokens.push_back(std::string(1, c)); addToken(std::string(1, c));
break;
} }
// tokens which do not need to be included // tokens which do not need to be included
case ' ': case ' ':
case '\r':
{ {
if (!buf.empty()) { if (!buf.empty()) {
tokens.push_back(buf); addToken(buf);
buf.clear(); buf.clear();
} }
break; break;
@@ -134,7 +193,7 @@ namespace Solstice {
} }
if (!buf.empty()) { if (!buf.empty()) {
tokens.push_back(buf); addToken(buf);
} }
return tokens; return tokens;
} }

View File

@@ -1,17 +1,29 @@
#ifndef LEXER_H
#define LEXER_H
#include <string> #include <string>
#include <optional> #include <optional>
#include <vector> #include <vector>
namespace Solstice { namespace Solstice {
struct Token {
std::string value;
int line;
std::string lineContent;
};
class Lexer { class Lexer {
std::string input; std::string input;
std::vector<std::string> lines;
size_t size; size_t size;
size_t current; size_t current;
std::optional<char> peek(int ahead = 1); std::optional<char> peek(int ahead = 1);
std::optional<char> consume(); std::optional<char> consume();
public: public:
Lexer(std::string in); Lexer(std::string in);
std::vector<std::string> lex(); std::vector<Token> lex();
}; };
} }
#endif

View File

@@ -4,7 +4,7 @@
#include <iostream> #include <iostream>
#include <cstring> #include <cstring>
#define parseOneToken(token) Parser({token.value()}).parse().children[0] #define parseOneToken(token) Parser({token}).parse().children[0]
namespace Solstice { namespace Solstice {
namespace Parser { namespace Parser {
@@ -21,7 +21,7 @@ namespace Solstice {
if (variables.find(i.outputId) != variables.end()) { if (variables.find(i.outputId) != variables.end()) {
return variables[i.outputId]; return variables[i.outputId];
} else { } else {
Error::syntaxError("Unknown variable"); Error::syntaxError("Unknown variable", i.line, i.lineContent);
} }
break; break;
} }
@@ -178,14 +178,14 @@ namespace Solstice {
break; break;
} }
case SolNodeType::Add: { case SolNodeType::Add: {
ensure3(children[0], SolDataType::Int, SolDataType::Double, SolDataType::String); ensure3(children[0], SolDataType::Int, SolDataType::Double, SolDataType::String, "operator '+'");
ensure3(children[1], SolDataType::Int, SolDataType::Double, SolDataType::String); ensure3(children[1], SolDataType::Int, SolDataType::Double, SolDataType::String, "operator '+'");
ensuresame(children[0], children[1]); ensuresame(children[0], children[1], "operator '+'");
SolGroundCodeBlock codeBlock; SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++); outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(ADD); GroundInstruction gi = groundCreateInstruction(ADD);
if (children.size() < 2) { 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[0].outputId.data()));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
@@ -197,13 +197,13 @@ namespace Solstice {
break; break;
} }
case SolNodeType::Subtract: { case SolNodeType::Subtract: {
ensure2(children[0], SolDataType::Int, SolDataType::Double); ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '-'");
ensure2(children[1], SolDataType::Int, SolDataType::Double); ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '-'");
SolGroundCodeBlock codeBlock; SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++); outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(SUBTRACT); GroundInstruction gi = groundCreateInstruction(SUBTRACT);
if (children.size() < 2) { 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[0].outputId.data()));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
@@ -215,13 +215,13 @@ namespace Solstice {
break; break;
} }
case SolNodeType::Multiply: { case SolNodeType::Multiply: {
ensure2(children[0], SolDataType::Int, SolDataType::Double); ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '*'");
ensure2(children[1], SolDataType::Int, SolDataType::Double); ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '*'");
SolGroundCodeBlock codeBlock; SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++); outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(MULTIPLY); GroundInstruction gi = groundCreateInstruction(MULTIPLY);
if (children.size() < 2) { 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[0].outputId.data()));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
@@ -233,13 +233,13 @@ namespace Solstice {
break; break;
} }
case SolNodeType::Divide: { case SolNodeType::Divide: {
ensure2(children[0], SolDataType::Int, SolDataType::Double); ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '/'");
ensure2(children[1], SolDataType::Int, SolDataType::Double); ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '/'");
SolGroundCodeBlock codeBlock; SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++); outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(DIVIDE); GroundInstruction gi = groundCreateInstruction(DIVIDE);
if (children.size() < 2) { 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[0].outputId.data()));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
@@ -251,12 +251,12 @@ namespace Solstice {
break; break;
} }
case SolNodeType::Equal: { case SolNodeType::Equal: {
ensuresame(children[0], children[1]); ensuresame(children[0], children[1], "operator '=='");
SolGroundCodeBlock codeBlock; SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++); outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(EQUAL); GroundInstruction gi = groundCreateInstruction(EQUAL);
if (children.size() < 2) { 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[0].outputId.data()));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
@@ -268,12 +268,12 @@ namespace Solstice {
break; break;
} }
case SolNodeType::Inequal: { case SolNodeType::Inequal: {
ensuresame(children[0], children[1]); ensuresame(children[0], children[1], "operator '!='");
SolGroundCodeBlock codeBlock; SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++); outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(INEQUAL); GroundInstruction gi = groundCreateInstruction(INEQUAL);
if (children.size() < 2) { 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[0].outputId.data()));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
@@ -285,12 +285,12 @@ namespace Solstice {
break; break;
} }
case SolNodeType::Greater: { case SolNodeType::Greater: {
ensuresame(children[0], children[1]); ensuresame(children[0], children[1], "operator '>'");
SolGroundCodeBlock codeBlock; SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++); outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(GREATER); GroundInstruction gi = groundCreateInstruction(GREATER);
if (children.size() < 2) { 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[0].outputId.data()));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
@@ -302,12 +302,12 @@ namespace Solstice {
break; break;
} }
case SolNodeType::Lesser: { case SolNodeType::Lesser: {
ensuresame(children[0], children[1]); ensuresame(children[0], children[1], "operator '<'");
SolGroundCodeBlock codeBlock; SolGroundCodeBlock codeBlock;
outputId = "tmp_" + std::to_string(tmpIdIterator++); outputId = "tmp_" + std::to_string(tmpIdIterator++);
GroundInstruction gi = groundCreateInstruction(LESSER); GroundInstruction gi = groundCreateInstruction(LESSER);
if (children.size() < 2) { 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[0].outputId.data()));
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
@@ -319,10 +319,10 @@ namespace Solstice {
break; break;
} }
case SolNodeType::EqGreater: { case SolNodeType::EqGreater: {
ensuresame(children[0], children[1]); ensuresame(children[0], children[1], "operator '>='");
SolGroundCodeBlock codeBlock; SolGroundCodeBlock codeBlock;
if (children.size() < 2) { 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++); outputId = "tmp_" + std::to_string(tmpIdIterator++);
std::string trueLabelIdString = "internal_true" + std::to_string(labelIterator++); std::string trueLabelIdString = "internal_true" + std::to_string(labelIterator++);
@@ -392,10 +392,10 @@ namespace Solstice {
break; break;
} }
case SolNodeType::EqLesser: { case SolNodeType::EqLesser: {
ensuresame(children[0], children[1]); ensuresame(children[0], children[1], "operator '<='");
SolGroundCodeBlock codeBlock; SolGroundCodeBlock codeBlock;
if (children.size() < 2) { 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++); outputId = "tmp_" + std::to_string(tmpIdIterator++);
std::string trueLabelIdString = "internal_true" + std::to_string(labelIterator++); std::string trueLabelIdString = "internal_true" + std::to_string(labelIterator++);
@@ -469,7 +469,7 @@ namespace Solstice {
exists(children[0]); exists(children[0]);
GroundInstruction gi = groundCreateInstruction(PRINTLN); GroundInstruction gi = groundCreateInstruction(PRINTLN);
if (children.size() < 1) { 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())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data()));
codeBlock.code.push_back(gi); codeBlock.code.push_back(gi);
@@ -478,7 +478,7 @@ namespace Solstice {
break; break;
} }
case SolNodeType::If: { case SolNodeType::If: {
ensure(children[0], SolDataType::Bool); ensure(children[0], SolDataType::Bool, "if condition");
auto conditionCode = children[0].generateCode(); auto conditionCode = children[0].generateCode();
code.insert(code.end(), conditionCode.begin(), conditionCode.end()); code.insert(code.end(), conditionCode.begin(), conditionCode.end());
outputId = "tmp_" + std::to_string(tmpIdIterator++); outputId = "tmp_" + std::to_string(tmpIdIterator++);
@@ -510,7 +510,7 @@ namespace Solstice {
break; break;
} }
case SolNodeType::While: { case SolNodeType::While: {
ensure(children[0], SolDataType::Bool); ensure(children[0], SolDataType::Bool, "while condition");
SolGroundCodeBlock startLabelBlock; SolGroundCodeBlock startLabelBlock;
std::string startLabelIdString = "whilestart_" + std::to_string(labelIterator++); std::string startLabelIdString = "whilestart_" + std::to_string(labelIterator++);
std::string endLabelIdString = "whileend_" + std::to_string(labelIterator); std::string endLabelIdString = "whileend_" + std::to_string(labelIterator);
@@ -562,7 +562,7 @@ namespace Solstice {
case SolNodeType::Set: { case SolNodeType::Set: {
if (variables.find(children[0].outputId) != variables.end()) { if (variables.find(children[0].outputId) != variables.end()) {
if (variables[children[0].outputId] != checkNodeReturnType(children[1])) { 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]); exists(children[1]);
@@ -580,7 +580,7 @@ namespace Solstice {
case SolNodeType::FunctionCall: { case SolNodeType::FunctionCall: {
// Take care of in built functions // Take care of in built functions
if (children[0].outputId == "input") { if (children[0].outputId == "input") {
ensure(children[1], SolDataType::String); ensure(children[1], SolDataType::String, "input function argument");
SolGroundCodeBlock inputCodeBlock; SolGroundCodeBlock inputCodeBlock;
if (children.size() > 1) { if (children.size() > 1) {
GroundInstruction printInstruction = groundCreateInstruction(PRINT); GroundInstruction printInstruction = groundCreateInstruction(PRINT);
@@ -604,9 +604,9 @@ namespace Solstice {
} }
// Parser Implementation // 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) { if (current + ahead < size) {
return tokensToParse[current + ahead]; return tokensToParse[current + ahead];
} else { } else {
@@ -614,7 +614,7 @@ namespace Solstice {
} }
} }
std::optional<std::string> Parser::consume() { std::optional<Token> Parser::consume() {
if (current < size) { if (current < size) {
return tokensToParse[current++]; return tokensToParse[current++];
} else { } else {
@@ -752,14 +752,16 @@ namespace Solstice {
SolNode Parser::parsePrimary() { SolNode Parser::parsePrimary() {
auto tokenopt = consume(); auto tokenopt = consume();
if (!tokenopt) { if (!tokenopt) {
std::cout << "Unexpected end of input\n"; Error::syntaxError("Unexpected end of input");
exit(1);
} }
std::string token = tokenopt.value(); Token tokenObj = tokenopt.value();
std::string token = tokenObj.value;
SolNodeType type = getNodeType(token); SolNodeType type = getNodeType(token);
if (type == SolNodeType::Value) { if (type == SolNodeType::Value) {
SolNode node(SolNodeType::Value); SolNode node(SolNodeType::Value);
node.line = tokenObj.line;
node.lineContent = tokenObj.lineContent;
switch (getDataType(token)) { switch (getDataType(token)) {
case SolDataType::Int: node.setValue((int64_t) std::stoll(token)); break; case SolDataType::Int: node.setValue((int64_t) std::stoll(token)); break;
case SolDataType::Double: node.setValue(std::stod(token)); break; case SolDataType::Double: node.setValue(std::stod(token)); break;
@@ -774,19 +776,28 @@ namespace Solstice {
if (type == SolNodeType::Identifier) { if (type == SolNodeType::Identifier) {
SolNode idNode(SolNodeType::Identifier); SolNode idNode(SolNodeType::Identifier);
idNode.outputId = token; 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(); // ( consume(); // (
SolNode fnCallNode(SolNodeType::FunctionCall); SolNode fnCallNode(SolNodeType::FunctionCall);
fnCallNode.line = tokenObj.line;
fnCallNode.lineContent = tokenObj.lineContent;
fnCallNode.addNode(idNode); fnCallNode.addNode(idNode);
std::vector<std::string> tokens; std::vector<Token> tokens;
size_t brackets = 1; size_t brackets = 1;
while (auto t = consume()) { while (auto t = consume()) {
if (t.value() == "(") brackets++; if (t.value().value == "(") brackets++;
if (t.value() == ")") brackets--; if (t.value().value == ")") brackets--;
if (brackets == 0) break; if (brackets == 0) break;
tokens.push_back(t.value()); tokens.push_back(t.value());
} }
if (brackets != 0) {
Error::syntaxError("Unclosed function call bracket", tokenObj.line, tokenObj.lineContent);
}
auto args = Parser(tokens).parse(); auto args = Parser(tokens).parse();
for(auto& child : args.children) fnCallNode.addNode(child); for(auto& child : args.children) fnCallNode.addNode(child);
return fnCallNode; return fnCallNode;
@@ -795,21 +806,23 @@ namespace Solstice {
} }
if (type == SolNodeType::BracketStart) { if (type == SolNodeType::BracketStart) {
std::vector<std::string> tokens; std::vector<Token> tokens;
size_t brackets = 1; size_t brackets = 1;
while (auto t = consume()) { while (auto t = consume()) {
if (t.value() == "(") brackets++; if (t.value().value == "(") brackets++;
if (t.value() == ")") brackets--; if (t.value().value == ")") brackets--;
if (brackets == 0) break; if (brackets == 0) break;
tokens.push_back(t.value()); tokens.push_back(t.value());
} }
if (brackets != 0) {
Error::syntaxError("Unclosed bracket", tokenObj.line, tokenObj.lineContent);
}
auto inner = Parser(tokens).parse(); auto inner = Parser(tokens).parse();
if (inner.children.size() > 0) return inner.children[0]; if (inner.children.size() > 0) return inner.children[0];
return SolNode(SolNodeType::None); return SolNode(SolNodeType::None);
} }
std::cout << "Unexpected token in expression: " << token << "\n"; Error::syntaxError("Unexpected token in expression: " + token, tokenObj.line, tokenObj.lineContent);
exit(1);
} }
SolNode Parser::parseExpression(int minPrec) { SolNode Parser::parseExpression(int minPrec) {
@@ -817,13 +830,16 @@ namespace Solstice {
while(true) { while(true) {
auto opOpt = peek(); auto opOpt = peek();
if (!opOpt) break; if (!opOpt) break;
std::string op = opOpt.value(); Token opToken = opOpt.value();
std::string op = opToken.value;
int prec = getPrecedence(op); int prec = getPrecedence(op);
if (prec < minPrec) break; if (prec < minPrec) break;
consume(); consume();
SolNode rhs = parseExpression(prec + 1); SolNode rhs = parseExpression(prec + 1);
SolNode newNode(getNodeType(op)); SolNode newNode(getNodeType(op));
newNode.line = opToken.line;
newNode.lineContent = opToken.lineContent;
newNode.addNode(lhs); newNode.addNode(lhs);
newNode.addNode(rhs); newNode.addNode(rhs);
lhs = newNode; lhs = newNode;
@@ -836,37 +852,48 @@ namespace Solstice {
size = tokensToParse.size(); size = tokensToParse.size();
SolNode rootNode(SolNodeType::Root); SolNode rootNode(SolNodeType::Root);
while (auto tokenopt = consume()) { while (auto tokenopt = consume()) {
std::string token = tokenopt.value(); Token tokenObj = tokenopt.value();
std::string token = tokenObj.value;
switch (getNodeType(token)) { switch (getNodeType(token)) {
case SolNodeType::Value: { case SolNodeType::Value: {
switch (getDataType(token)) { switch (getDataType(token)) {
case SolDataType::Int: { case SolDataType::Int: {
SolNode intNode(SolNodeType::Value); SolNode intNode(SolNodeType::Value);
intNode.setValue((int64_t) std::stoll(token)); intNode.setValue((int64_t) std::stoll(token));
intNode.line = tokenObj.line;
intNode.lineContent = tokenObj.lineContent;
rootNode.addNode(intNode); rootNode.addNode(intNode);
break; break;
} }
case SolDataType::Double: { case SolDataType::Double: {
SolNode doubleNode(SolNodeType::Value); SolNode doubleNode(SolNodeType::Value);
doubleNode.setValue(std::stod(token)); doubleNode.setValue(std::stod(token));
doubleNode.line = tokenObj.line;
doubleNode.lineContent = tokenObj.lineContent;
rootNode.addNode(doubleNode); rootNode.addNode(doubleNode);
break; break;
} }
case SolDataType::String: { case SolDataType::String: {
SolNode stringNode(SolNodeType::Value); SolNode stringNode(SolNodeType::Value);
stringNode.setValue(token.substr(1, token.size() - 2)); stringNode.setValue(token.substr(1, token.size() - 2));
stringNode.line = tokenObj.line;
stringNode.lineContent = tokenObj.lineContent;
rootNode.addNode(stringNode); rootNode.addNode(stringNode);
break; break;
} }
case SolDataType::Char: { case SolDataType::Char: {
SolNode charNode(SolNodeType::Value); SolNode charNode(SolNodeType::Value);
charNode.setValue(token[1]); charNode.setValue(token[1]);
charNode.line = tokenObj.line;
charNode.lineContent = tokenObj.lineContent;
rootNode.addNode(charNode); rootNode.addNode(charNode);
break; break;
} }
case SolDataType::Bool: { case SolDataType::Bool: {
SolNode boolNode(SolNodeType::Value); SolNode boolNode(SolNodeType::Value);
boolNode.setValue(token == "true"); boolNode.setValue(token == "true");
boolNode.line = tokenObj.line;
boolNode.lineContent = tokenObj.lineContent;
rootNode.addNode(boolNode); rootNode.addNode(boolNode);
break; break;
} }
@@ -879,9 +906,11 @@ namespace Solstice {
case SolNodeType::Divide: case SolNodeType::Divide:
{ {
SolNode opNode(getNodeType(token)); SolNode opNode(getNodeType(token));
opNode.line = tokenObj.line;
opNode.lineContent = tokenObj.lineContent;
if (rootNode.children.empty()) { if (rootNode.children.empty()) {
std::cout << "Expected operand before operator\n"; Error::syntaxError("Expected operand before operator", tokenObj.line, tokenObj.lineContent);
exit(1);
} }
opNode.addNode(rootNode.children.back()); opNode.addNode(rootNode.children.back());
rootNode.children.pop_back(); rootNode.children.pop_back();
@@ -901,15 +930,17 @@ namespace Solstice {
case SolNodeType::EqGreater: case SolNodeType::EqGreater:
{ {
SolNode equalNode(getNodeType(token)); SolNode equalNode(getNodeType(token));
equalNode.line = tokenObj.line;
equalNode.lineContent = tokenObj.lineContent;
if (rootNode.children.empty()) { if (rootNode.children.empty()) {
std::cout << "ah dingus\n"; Error::syntaxError("Expected operand before comparison", tokenObj.line, tokenObj.lineContent);
exit(1);
} }
equalNode.addNode(rootNode.children.back()); equalNode.addNode(rootNode.children.back());
rootNode.children.pop_back(); rootNode.children.pop_back();
std::vector<std::string> tokens; std::vector<Token> tokens;
while (auto tokenopt = consume()) { while (auto tokenopt = consume()) {
if (tokenopt.value() == "\n") { if (tokenopt.value().value == "\n") {
break; break;
} }
tokens.push_back(tokenopt.value()); tokens.push_back(tokenopt.value());
@@ -921,9 +952,11 @@ namespace Solstice {
} }
case SolNodeType::Puts: { case SolNodeType::Puts: {
SolNode putsNode(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()) { while (auto tokenopt = consume()) {
if (tokenopt.value() == "\n") { if (tokenopt.value().value == "\n") {
break; break;
} }
tokens.push_back(tokenopt.value()); tokens.push_back(tokenopt.value());
@@ -937,9 +970,11 @@ namespace Solstice {
} }
case SolNodeType::If: { case SolNodeType::If: {
SolNode ifNode(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()) { while (auto tokenopt = consume()) {
if (tokenopt.value() == "\n") { if (tokenopt.value().value == "\n") {
break; break;
} }
tokens.push_back(tokenopt.value()); tokens.push_back(tokenopt.value());
@@ -950,27 +985,28 @@ namespace Solstice {
size_t brackets = 1; size_t brackets = 1;
auto tokenopt = consume(); auto tokenopt = consume();
if (tokenopt) { if (tokenopt) {
if (tokenopt.value() == "{") { if (tokenopt.value().value == "{") {
tokens.push_back(tokenopt.value()); tokens.push_back(tokenopt.value());
while (auto tokenopt = consume()) { while (auto tokenopt = consume()) {
tokens.push_back(tokenopt.value()); tokens.push_back(tokenopt.value());
if (tokenopt.value() == "{") { if (tokenopt.value().value == "{") {
brackets++; brackets++;
} }
if (tokenopt.value() == "}") { if (tokenopt.value().value == "}") {
brackets--; brackets--;
} }
if (brackets == 0) { if (brackets == 0) {
break; break;
} }
} }
if (brackets != 0) {
Error::syntaxError("Unclosed code block", tokenopt.value().line, tokenopt.value().lineContent);
}
} else { } else {
std::cout << "I want a code block instead of a " + tokenopt.value() + "\n"; Error::syntaxError("I want a code block instead of a " + tokenopt.value().value, tokenopt.value().line, tokenopt.value().lineContent);
exit(1);
} }
} else { } else {
std::cout << "FEED ME MORE TOKENSSSSS\n"; Error::syntaxError("Expected code block after if condition", ifNode.line, ifNode.lineContent);
exit(1);
} }
auto childCodeBlock = Parser(tokens).parse(); auto childCodeBlock = Parser(tokens).parse();
ifNode.addNode(childCodeBlock.children[0]); ifNode.addNode(childCodeBlock.children[0]);
@@ -979,9 +1015,11 @@ namespace Solstice {
} }
case SolNodeType::While: { case SolNodeType::While: {
SolNode whileNode(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()) { while (auto tokenopt = consume()) {
if (tokenopt.value() == "\n") { if (tokenopt.value().value == "\n") {
break; break;
} }
tokens.push_back(tokenopt.value()); tokens.push_back(tokenopt.value());
@@ -992,27 +1030,28 @@ namespace Solstice {
size_t brackets = 1; size_t brackets = 1;
auto tokenopt = consume(); auto tokenopt = consume();
if (tokenopt) { if (tokenopt) {
if (tokenopt.value() == "{") { if (tokenopt.value().value == "{") {
tokens.push_back(tokenopt.value()); tokens.push_back(tokenopt.value());
while (auto tokenopt = consume()) { while (auto tokenopt = consume()) {
tokens.push_back(tokenopt.value()); tokens.push_back(tokenopt.value());
if (tokenopt.value() == "{") { if (tokenopt.value().value == "{") {
brackets++; brackets++;
} }
if (tokenopt.value() == "}") { if (tokenopt.value().value == "}") {
brackets--; brackets--;
} }
if (brackets == 0) { if (brackets == 0) {
break; break;
} }
} }
if (brackets != 0) {
Error::syntaxError("Unclosed code block", tokenopt.value().line, tokenopt.value().lineContent);
}
} else { } else {
std::cout << "I want a code block instead of a " + tokenopt.value() + "\n"; Error::syntaxError("I want a code block instead of a " + tokenopt.value().value, tokenopt.value().line, tokenopt.value().lineContent);
exit(1);
} }
} else { } else {
std::cout << "FEED ME MORE TOKENSSSSS\n"; Error::syntaxError("Expected code block after while condition", whileNode.line, whileNode.lineContent);
exit(1);
} }
auto childCodeBlock = Parser(tokens).parse(); auto childCodeBlock = Parser(tokens).parse();
whileNode.addNode(childCodeBlock.children[0]); whileNode.addNode(childCodeBlock.children[0]);
@@ -1021,13 +1060,15 @@ namespace Solstice {
} }
case SolNodeType::CodeBlockStart: { case SolNodeType::CodeBlockStart: {
SolNode codeBlockNode(SolNodeType::CodeBlock); SolNode codeBlockNode(SolNodeType::CodeBlock);
codeBlockNode.line = tokenObj.line;
codeBlockNode.lineContent = tokenObj.lineContent;
size_t brackets = 1; size_t brackets = 1;
std::vector<std::string> tokens; std::vector<Token> tokens;
while (auto tokenopt = consume()) { while (auto tokenopt = consume()) {
if (tokenopt.value() == "{") { if (tokenopt.value().value == "{") {
brackets++; brackets++;
} }
if (tokenopt.value() == "}") { if (tokenopt.value().value == "}") {
brackets--; brackets--;
} }
if (brackets == 0) { if (brackets == 0) {
@@ -1035,6 +1076,9 @@ namespace Solstice {
} }
tokens.push_back(tokenopt.value()); tokens.push_back(tokenopt.value());
} }
if (brackets != 0) {
Error::syntaxError("Unclosed code block", tokenObj.line, tokenObj.lineContent);
}
codeBlockNode.children = Parser(tokens).parse().children; codeBlockNode.children = Parser(tokens).parse().children;
rootNode.addNode(codeBlockNode); rootNode.addNode(codeBlockNode);
break; break;
@@ -1042,16 +1086,21 @@ namespace Solstice {
case SolNodeType::Identifier: { case SolNodeType::Identifier: {
SolNode idNode(SolNodeType::Identifier); SolNode idNode(SolNodeType::Identifier);
idNode.outputId = token; idNode.outputId = token;
idNode.line = tokenObj.line;
idNode.lineContent = tokenObj.lineContent;
rootNode.addNode(idNode); rootNode.addNode(idNode);
break; break;
} }
case SolNodeType::Set: { case SolNodeType::Set: {
SolNode setNode(SolNodeType::Set); SolNode setNode(SolNodeType::Set);
setNode.line = tokenObj.line;
setNode.lineContent = tokenObj.lineContent;
setNode.addNode(rootNode.children.back()); setNode.addNode(rootNode.children.back());
rootNode.children.pop_back(); rootNode.children.pop_back();
std::vector<std::string> tokens; std::vector<Token> tokens;
while (auto tokenopt = consume()) { while (auto tokenopt = consume()) {
if (tokenopt.value() == "\n") { if (tokenopt.value().value == "\n") {
break; break;
} }
tokens.push_back(tokenopt.value()); tokens.push_back(tokenopt.value());
@@ -1064,15 +1113,17 @@ namespace Solstice {
case SolNodeType::BracketStart: { case SolNodeType::BracketStart: {
if (rootNode.children.back().nodeType == SolNodeType::Identifier) { if (rootNode.children.back().nodeType == SolNodeType::Identifier) {
SolNode fnCallNode(SolNodeType::FunctionCall); SolNode fnCallNode(SolNodeType::FunctionCall);
fnCallNode.line = tokenObj.line;
fnCallNode.lineContent = tokenObj.lineContent;
fnCallNode.addNode(rootNode.children.back()); fnCallNode.addNode(rootNode.children.back());
rootNode.children.pop_back(); rootNode.children.pop_back();
std::vector<std::string> tokens; std::vector<Token> tokens;
size_t brackets = 1; size_t brackets = 1;
while (auto tokenopt = consume()) { while (auto tokenopt = consume()) {
if (tokenopt.value() == "(") { if (tokenopt.value().value == "(") {
brackets++; brackets++;
} }
if (tokenopt.value() == ")") { if (tokenopt.value().value == ")") {
brackets--; brackets--;
} }
if (brackets < 1) { if (brackets < 1) {
@@ -1084,13 +1135,13 @@ namespace Solstice {
fnCallNode.children.insert(fnCallNode.children.end(), node.children.begin(), node.children.end()); fnCallNode.children.insert(fnCallNode.children.end(), node.children.begin(), node.children.end());
rootNode.addNode(fnCallNode); rootNode.addNode(fnCallNode);
} else { } else {
std::vector<std::string> tokens; std::vector<Token> tokens;
size_t brackets = 1; size_t brackets = 1;
while (auto tokenopt = consume()) { while (auto tokenopt = consume()) {
if (tokenopt.value() == "(") { if (tokenopt.value().value == "(") {
brackets++; brackets++;
} }
if (tokenopt.value() == ")") { if (tokenopt.value().value == ")") {
brackets--; brackets--;
} }
if (brackets < 1) { if (brackets < 1) {

View File

@@ -7,32 +7,34 @@
#include <optional> #include <optional>
#include <string> #include <string>
#include <variant> #include <variant>
#include "lexer.h"
#include "error.h"
#define ensure(node, datatype) \ #define ensure(node, datatype, op) \
if (checkNodeReturnType(node) != datatype) { \ 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); \ SolDataType dataType = checkNodeReturnType(node); \
if (!(dataType == datatype1 || dataType == datatype2)) { \ 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); \ SolDataType dataType = checkNodeReturnType(node); \
if (!(dataType == datatype1 || dataType == datatype2 || dataType == datatype3)) { \ 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 n1t = checkNodeReturnType(node1); \
SolDataType n2t = checkNodeReturnType(node2); \ SolDataType n2t = checkNodeReturnType(node2); \
if (n1t != n2t) { \ if (n1t != n2t) { \
if (!(n1t == SolDataType::Int && n2t == SolDataType::Double || n1t == SolDataType::Double && n2t == SolDataType::Int)) { \ 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,7 +42,7 @@
#define exists(node) \ #define exists(node) \
if (node.nodeType == SolNodeType::Identifier) { \ if (node.nodeType == SolNodeType::Identifier) { \
if (variables.find(node.outputId) == variables.end()) { \ if (variables.find(node.outputId) == variables.end()) { \
Error::syntaxError("Variable does not exist"); \ Error::syntaxError("Variable does not exist", node.line, node.lineContent); \
} \ } \
} }
@@ -96,6 +98,9 @@ namespace Solstice {
SolNodeType nodeType = SolNodeType::None; SolNodeType nodeType = SolNodeType::None;
std::vector<SolNode> children; std::vector<SolNode> children;
std::string outputId; std::string outputId;
int line = 0;
std::string lineContent = "";
SolNode(SolNodeType nodeType); SolNode(SolNodeType nodeType);
SolNode(SolNodeType nodeType, SolData data); SolNode(SolNodeType nodeType, SolData data);
SolNode() = default; SolNode() = default;
@@ -105,12 +110,12 @@ namespace Solstice {
}; };
class Parser { class Parser {
std::vector<std::string> tokensToParse; std::vector<Token> tokensToParse;
size_t current; size_t current;
size_t size; size_t size;
std::optional<std::string> peek(int ahead = 0); std::optional<Token> peek(int ahead = 0);
std::optional<std::string> consume(); std::optional<Token> consume();
bool isInt(std::string in); bool isInt(std::string in);
bool isDouble(std::string in); bool isDouble(std::string in);
bool isString(std::string in); bool isString(std::string in);
@@ -124,7 +129,7 @@ namespace Solstice {
int getPrecedence(std::string token); int getPrecedence(std::string token);
public: public:
Parser(std::vector<std::string> in); Parser(std::vector<Token> in);
SolNode parse(); SolNode parse();
}; };