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 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);
}
}

View File

@@ -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

View File

@@ -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':
{
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;
}

View File

@@ -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

View File

@@ -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;
}
}
} else {
std::cout << "I want a code block instead of a " + tokenopt.value() + "\n";
exit(1);
if (brackets != 0) {
Error::syntaxError("Unclosed code block", tokenopt.value().line, tokenopt.value().lineContent);
}
} else {
std::cout << "FEED ME MORE TOKENSSSSS\n";
exit(1);
Error::syntaxError("I want a code block instead of a " + tokenopt.value().value, tokenopt.value().line, tokenopt.value().lineContent);
}
} else {
Error::syntaxError("Expected code block after if condition", ifNode.line, ifNode.lineContent);
}
auto childCodeBlock = Parser(tokens).parse();
ifNode.addNode(childCodeBlock.children[0]);
@@ -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;
}
}
} else {
std::cout << "I want a code block instead of a " + tokenopt.value() + "\n";
exit(1);
if (brackets != 0) {
Error::syntaxError("Unclosed code block", tokenopt.value().line, tokenopt.value().lineContent);
}
} else {
std::cout << "FEED ME MORE TOKENSSSSS\n";
exit(1);
Error::syntaxError("I want a code block instead of a " + tokenopt.value().value, tokenopt.value().line, tokenopt.value().lineContent);
}
} else {
Error::syntaxError("Expected code block after while condition", whileNode.line, whileNode.lineContent);
}
auto childCodeBlock = Parser(tokens).parse();
whileNode.addNode(childCodeBlock.children[0]);
@@ -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) {

View File

@@ -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,7 +42,7 @@
#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); \
} \
}
@@ -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();
};