forked from solstice/solstice
Refactor type system, print all errors in file
This commit is contained in:
@@ -11,7 +11,9 @@ namespace Solstice {
|
||||
const std::string RESET = "\033[0m";
|
||||
const std::string BOLD = "\033[1m";
|
||||
|
||||
[[noreturn]] void syntaxError(std::string what, int line, std::string lineContent) {
|
||||
int amountOfErrors = 0;
|
||||
|
||||
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";
|
||||
@@ -22,9 +24,9 @@ namespace Solstice {
|
||||
std::cout << "\n";
|
||||
}
|
||||
std::cout << YELLOW << "-> " << what << RESET << "\n";
|
||||
exit(1);
|
||||
amountOfErrors++;
|
||||
}
|
||||
[[noreturn]] void typingError(std::string what, int line, std::string lineContent) {
|
||||
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";
|
||||
@@ -35,7 +37,13 @@ namespace Solstice {
|
||||
std::cout << "\n";
|
||||
}
|
||||
std::cout << YELLOW << "-> " << what << RESET << "\n";
|
||||
exit(1);
|
||||
amountOfErrors++;
|
||||
}
|
||||
void summariseErrors() {
|
||||
if (amountOfErrors > 0) {
|
||||
std::cout << amountOfErrors << " errors generated.\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
|
||||
namespace Solstice {
|
||||
namespace Error {
|
||||
[[noreturn]] void syntaxError(std::string what, int line = 0, std::string lineContent = "");
|
||||
[[noreturn]] void typingError(std::string what, int line = 0, std::string lineContent = "");
|
||||
void syntaxError(std::string what, int line = 0, std::string lineContent = "");
|
||||
void typingError(std::string what, int line = 0, std::string lineContent = "");
|
||||
void summariseErrors();
|
||||
extern int amountOfErrors;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -111,7 +111,8 @@ namespace Solstice {
|
||||
}
|
||||
case '(':
|
||||
case ')':
|
||||
case '}':
|
||||
case '}':
|
||||
case ',':
|
||||
{
|
||||
if (!buf.empty()) {
|
||||
addToken(buf);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "argparser.h"
|
||||
#include "error.h"
|
||||
|
||||
#define parseOneToken(token) Parser({token.value()}).parse().children[0]
|
||||
|
||||
@@ -28,6 +29,7 @@ int main(int argc, char** argv) {
|
||||
auto lexed = Solstice::Lexer(ss.str()).lex();
|
||||
auto parsed = Solstice::Parser::Parser(lexed).parse();
|
||||
GroundProgram program = Solstice::Parser::assembleProgram(parsed);
|
||||
Solstice::Error::summariseErrors();
|
||||
if (args.output.has_value()) {
|
||||
std::FILE* originalStdout = stdout;
|
||||
std::FILE* tmp = std::tmpfile();
|
||||
|
||||
167
src/parser.cpp
167
src/parser.cpp
@@ -1,7 +1,6 @@
|
||||
#include "parser.h"
|
||||
#include "error.h"
|
||||
#include <groundvm.h>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#define parseOneToken(token) Parser({token}).parse().children[0]
|
||||
@@ -13,9 +12,9 @@ namespace Solstice {
|
||||
return id.rfind("tmp_", 0) == 0;
|
||||
}
|
||||
|
||||
std::map<std::string, SolDataType> variables;
|
||||
std::map<std::string, std::string> variables;
|
||||
|
||||
SolDataType checkNodeReturnType(SolNode i) {
|
||||
std::string checkNodeReturnType(SolNode i) {
|
||||
switch (i.nodeType) {
|
||||
case SolNodeType::Identifier: {
|
||||
if (variables.find(i.outputId) != variables.end()) {
|
||||
@@ -26,7 +25,7 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Value: {
|
||||
return i.data.type;
|
||||
return i.data.getTypeString();
|
||||
}
|
||||
case SolNodeType::Equal:
|
||||
case SolNodeType::Inequal:
|
||||
@@ -34,25 +33,25 @@ namespace Solstice {
|
||||
case SolNodeType::Lesser:
|
||||
case SolNodeType::EqGreater:
|
||||
case SolNodeType::EqLesser:
|
||||
return SolDataType::Bool;
|
||||
return "bool";
|
||||
break;
|
||||
case SolNodeType::Add:
|
||||
{
|
||||
if (checkNodeReturnType(i.children[0]) == SolDataType::String && checkNodeReturnType(i.children[1]) == SolDataType::String) {
|
||||
return SolDataType::String;
|
||||
if (checkNodeReturnType(i.children[0]) == "string" && checkNodeReturnType(i.children[1]) == "string") {
|
||||
return "string";
|
||||
}
|
||||
}
|
||||
case SolNodeType::Subtract:
|
||||
case SolNodeType::Multiply:
|
||||
case SolNodeType::Divide:
|
||||
{
|
||||
if (checkNodeReturnType(i.children[0]) == SolDataType::Double) {
|
||||
return SolDataType::Double;
|
||||
if (checkNodeReturnType(i.children[0]) == "double") {
|
||||
return "double";
|
||||
}
|
||||
if (checkNodeReturnType(i.children[1]) == SolDataType::Double) {
|
||||
return SolDataType::Double;
|
||||
if (checkNodeReturnType(i.children[1]) == "double") {
|
||||
return "double";
|
||||
}
|
||||
return SolDataType::Int;
|
||||
return "int";
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Puts:
|
||||
@@ -62,10 +61,11 @@ namespace Solstice {
|
||||
case SolNodeType::Set:
|
||||
case SolNodeType::Root:
|
||||
case SolNodeType::None:
|
||||
return SolDataType::None;
|
||||
case SolNodeType::FunctionDef:
|
||||
return "none";
|
||||
break;
|
||||
}
|
||||
return SolDataType::None;
|
||||
return "none";
|
||||
}
|
||||
|
||||
// SolData Implementation
|
||||
@@ -74,6 +74,7 @@ namespace Solstice {
|
||||
SolData::SolData(std::string in) : data(in), type(SolDataType::String) {}
|
||||
SolData::SolData(char in) : data(in), type(SolDataType::Char) {}
|
||||
SolData::SolData(bool in) : data(in), type(SolDataType::Bool) {}
|
||||
SolData::SolData(SolFunction in) : data(in), type(SolDataType::Function) {}
|
||||
|
||||
std::optional<int64_t> SolData::getInt() {
|
||||
if (type == SolDataType::Int) {
|
||||
@@ -110,6 +111,26 @@ namespace Solstice {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
std::optional<SolFunction> SolData::getFunction() {
|
||||
if (type == SolDataType::Function) {
|
||||
return std::get<SolFunction>(data);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::string SolData::getTypeString() {
|
||||
switch (type) {
|
||||
case SolDataType::Int: return "int";
|
||||
case SolDataType::Double: return "double";
|
||||
case SolDataType::String: return "string";
|
||||
case SolDataType::Char: return "char";
|
||||
case SolDataType::Bool: return "bool";
|
||||
case SolDataType::Function: return "function";
|
||||
case SolDataType::None: return "none";
|
||||
}
|
||||
return "none";
|
||||
}
|
||||
|
||||
// SolNode Implementation
|
||||
SolNode::SolNode(SolNodeType nodeType) : nodeType(nodeType) {}
|
||||
@@ -178,8 +199,8 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Add: {
|
||||
ensure3(children[0], SolDataType::Int, SolDataType::Double, SolDataType::String, "operator '+'");
|
||||
ensure3(children[1], SolDataType::Int, SolDataType::Double, SolDataType::String, "operator '+'");
|
||||
ensure3(children[0], "int", "double", "string", "operator '+'");
|
||||
ensure3(children[1], "int", "double", "string", "operator '+'");
|
||||
ensuresame(children[0], children[1], "operator '+'");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
@@ -197,8 +218,8 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Subtract: {
|
||||
ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '-'");
|
||||
ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '-'");
|
||||
ensure2(children[0], "int", "double", "operator '-'");
|
||||
ensure2(children[1], "int", "double", "operator '-'");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
GroundInstruction gi = groundCreateInstruction(SUBTRACT);
|
||||
@@ -215,8 +236,8 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Multiply: {
|
||||
ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '*'");
|
||||
ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '*'");
|
||||
ensure2(children[0], "int", "double", "operator '*'");
|
||||
ensure2(children[1], "int", "double", "operator '*'");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
GroundInstruction gi = groundCreateInstruction(MULTIPLY);
|
||||
@@ -233,8 +254,8 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::Divide: {
|
||||
ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '/'");
|
||||
ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '/'");
|
||||
ensure2(children[0], "int", "double", "operator '/'");
|
||||
ensure2(children[1], "int", "double", "operator '/'");
|
||||
SolGroundCodeBlock codeBlock;
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
GroundInstruction gi = groundCreateInstruction(DIVIDE);
|
||||
@@ -478,7 +499,7 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::If: {
|
||||
ensure(children[0], SolDataType::Bool, "if condition");
|
||||
ensure(children[0], "bool", "if condition");
|
||||
auto conditionCode = children[0].generateCode();
|
||||
code.insert(code.end(), conditionCode.begin(), conditionCode.end());
|
||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
||||
@@ -510,7 +531,7 @@ namespace Solstice {
|
||||
break;
|
||||
}
|
||||
case SolNodeType::While: {
|
||||
ensure(children[0], SolDataType::Bool, "while condition");
|
||||
ensure(children[0], "bool", "while condition");
|
||||
SolGroundCodeBlock startLabelBlock;
|
||||
std::string startLabelIdString = "whilestart_" + std::to_string(labelIterator++);
|
||||
std::string endLabelIdString = "whileend_" + std::to_string(labelIterator);
|
||||
@@ -574,13 +595,14 @@ namespace Solstice {
|
||||
if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId);
|
||||
code.push_back(codeBlock);
|
||||
// Make sure we know what the variable type is
|
||||
variables[children[0].outputId] = children[1].data.type;
|
||||
variables[children[0].outputId] = children[1].data.getTypeString();
|
||||
break;
|
||||
}
|
||||
case SolNodeType::FunctionCall: {
|
||||
// Take care of in built functions
|
||||
// This will be removed when inline ground is added
|
||||
if (children[0].outputId == "input") {
|
||||
ensure(children[1], SolDataType::String, "input function argument");
|
||||
ensure(children[1], "string", "input function argument");
|
||||
SolGroundCodeBlock inputCodeBlock;
|
||||
if (children.size() > 1) {
|
||||
GroundInstruction printInstruction = groundCreateInstruction(PRINT);
|
||||
@@ -595,6 +617,7 @@ namespace Solstice {
|
||||
code.push_back(inputCodeBlock);
|
||||
break;
|
||||
}
|
||||
std::string fnToCall = children[0].outputId;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -728,6 +751,9 @@ namespace Solstice {
|
||||
if (in == "while") {
|
||||
return SolNodeType::While;
|
||||
}
|
||||
if (in == "def") {
|
||||
return SolNodeType::FunctionDef;
|
||||
}
|
||||
if (in == "{") {
|
||||
return SolNodeType::CodeBlockStart;
|
||||
}
|
||||
@@ -1154,6 +1180,93 @@ namespace Solstice {
|
||||
rootNode.addNode(node);
|
||||
}
|
||||
}
|
||||
case SolNodeType::FunctionDef: {
|
||||
SolFunction fn;
|
||||
auto nameTokenOpt = consume();
|
||||
if (!nameTokenOpt || getNodeType(nameTokenOpt.value().value) != SolNodeType::Identifier) {
|
||||
Error::syntaxError("Expected function name", tokenObj.line, tokenObj.lineContent);
|
||||
}
|
||||
std::string fnName = nameTokenOpt.value().value;
|
||||
|
||||
auto openParen = consume();
|
||||
if (!openParen || openParen.value().value != "(") {
|
||||
Error::syntaxError("Expected '(' after function name", tokenObj.line, tokenObj.lineContent);
|
||||
}
|
||||
|
||||
std::string signature = "fun(";
|
||||
|
||||
bool first = true;
|
||||
while (auto next = peek()) {
|
||||
if (next.value().value == ")") {
|
||||
consume();
|
||||
break;
|
||||
}
|
||||
if (!first) {
|
||||
auto comma = consume();
|
||||
if (comma.value().value != ",") {
|
||||
Error::syntaxError("Expected ',' between arguments", tokenObj.line, tokenObj.lineContent);
|
||||
}
|
||||
}
|
||||
|
||||
auto typeToken = consume();
|
||||
if (!typeToken) Error::syntaxError("Expected type", tokenObj.line, tokenObj.lineContent);
|
||||
std::string argType = typeToken.value().value;
|
||||
|
||||
auto argNameToken = consume();
|
||||
if (!argNameToken) Error::syntaxError("Expected argument name", tokenObj.line, tokenObj.lineContent);
|
||||
std::string argName = argNameToken.value().value;
|
||||
|
||||
fn.parameters.push_back({argName, argType});
|
||||
|
||||
if (!first) signature += ", ";
|
||||
signature += argType;
|
||||
first = false;
|
||||
}
|
||||
signature += ") ";
|
||||
|
||||
while (peek() && peek().value().value == "\n") consume();
|
||||
|
||||
auto next = peek();
|
||||
if (next && next.value().value != "{") {
|
||||
auto retTypeToken = consume();
|
||||
fn.returnType = retTypeToken.value().value;
|
||||
} else {
|
||||
fn.returnType = "void";
|
||||
}
|
||||
signature += fn.returnType;
|
||||
fn.signature = signature;
|
||||
|
||||
while (peek() && peek().value().value == "\n") consume();
|
||||
|
||||
auto brace = peek();
|
||||
if (!brace || brace.value().value != "{") {
|
||||
Error::syntaxError("Expected '{' starting function body", tokenObj.line, tokenObj.lineContent);
|
||||
}
|
||||
|
||||
consume();
|
||||
std::vector<Token> bodyTokens;
|
||||
size_t brackets = 1;
|
||||
while(auto t = consume()) {
|
||||
if (t.value().value == "{") brackets++;
|
||||
if (t.value().value == "}") brackets--;
|
||||
if (brackets == 0) break;
|
||||
bodyTokens.push_back(t.value());
|
||||
}
|
||||
if (brackets != 0) Error::syntaxError("Unclosed function body", tokenObj.line, tokenObj.lineContent);
|
||||
|
||||
auto parsedBody = Parser(bodyTokens).parse();
|
||||
auto bodyNode = std::make_shared<SolNode>(SolNodeType::CodeBlock);
|
||||
bodyNode->children = parsedBody.children;
|
||||
|
||||
fn.content = bodyNode;
|
||||
|
||||
SolNode fnNode(SolNodeType::FunctionDef);
|
||||
fnNode.data = SolData(fn);
|
||||
variables[fnName] = signature;
|
||||
|
||||
rootNode.addNode(fnNode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rootNode;
|
||||
@@ -1179,4 +1292,4 @@ namespace Solstice {
|
||||
|
||||
} // namespace Parser
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
43
src/parser.h
43
src/parser.h
@@ -7,34 +7,35 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <memory>
|
||||
#include "lexer.h"
|
||||
#include "error.h"
|
||||
|
||||
#define ensure(node, datatype, op) \
|
||||
if (checkNodeReturnType(node) != datatype) { \
|
||||
Error::typingError("Expected " #datatype " for " op, node.line, node.lineContent); \
|
||||
Error::typingError("Expected " + std::string(datatype) + " for " + op, node.line, node.lineContent); \
|
||||
}
|
||||
|
||||
#define ensure2(node, datatype1, datatype2, op) { \
|
||||
SolDataType dataType = checkNodeReturnType(node); \
|
||||
std::string dataType = checkNodeReturnType(node); \
|
||||
if (!(dataType == datatype1 || dataType == datatype2)) { \
|
||||
Error::typingError("Expected either " #datatype1 " or " #datatype2 " for " op, node.line, node.lineContent); \
|
||||
Error::typingError("Expected either " + std::string(datatype1) + " or " + std::string(datatype2) + " for " + op, node.line, node.lineContent); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ensure3(node, datatype1, datatype2, datatype3, op) { \
|
||||
SolDataType dataType = checkNodeReturnType(node); \
|
||||
#define ensure3(node, datatype1, datatype2, datatype3, op) { \
|
||||
std::string dataType = checkNodeReturnType(node); \
|
||||
if (!(dataType == datatype1 || dataType == datatype2 || dataType == datatype3)) { \
|
||||
Error::typingError("Expected either " #datatype1 ", " #datatype2 ", or " #datatype3 " for " op, node.line, node.lineContent); \
|
||||
Error::typingError("Expected either " + std::string(datatype1) + ", " + std::string(datatype2) + ", or " + std::string(datatype3) + " for " + op, node.line, node.lineContent); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ensuresame(node1, node2, op) { \
|
||||
SolDataType n1t = checkNodeReturnType(node1); \
|
||||
SolDataType n2t = checkNodeReturnType(node2); \
|
||||
std::string n1t = checkNodeReturnType(node1); \
|
||||
std::string 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 for " op, node1.line, node1.lineContent); \
|
||||
if (!(n1t == "int" && n2t == "double" || n1t == "double" && n2t == "int")) { \
|
||||
Error::typingError("Expected types to be the same for " + std::string(op), node1.line, node1.lineContent); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
@@ -56,14 +57,14 @@ namespace Solstice {
|
||||
|
||||
|
||||
enum class SolNodeType {
|
||||
Add, Subtract, Multiply, Divide, Equal, Inequal, Greater, Lesser, EqGreater, EqLesser, Set, While, If, Value, Identifier, None, Root, CodeBlock, CodeBlockStart, CodeBlockEnd, FunctionCall, Expression, BracketStart, BracketEnd, Puts
|
||||
Add, Subtract, Multiply, Divide, Equal, Inequal, Greater, Lesser, EqGreater, EqLesser, Set, While, If, Value, Identifier, None, Root, CodeBlock, CodeBlockStart, CodeBlockEnd, FunctionDef, FunctionCall, Expression, BracketStart, BracketEnd, Puts
|
||||
};
|
||||
|
||||
enum class SolDataType {
|
||||
Int, String, Double, Bool, Char, None
|
||||
Int, String, Double, Bool, Char, Function, None
|
||||
};
|
||||
|
||||
extern std::map<std::string, SolDataType> variables;
|
||||
extern std::map<std::string, std::string> variables;
|
||||
|
||||
class SolNode;
|
||||
|
||||
@@ -74,8 +75,17 @@ namespace Solstice {
|
||||
SolGroundCodeBlock() = default;
|
||||
};
|
||||
|
||||
class SolFunction {
|
||||
public:
|
||||
std::string signature;
|
||||
std::vector<std::pair<std::string, std::string>> parameters; // name, type
|
||||
std::string returnType;
|
||||
std::shared_ptr<SolNode> content;
|
||||
SolFunction() = default;
|
||||
};
|
||||
|
||||
class SolData {
|
||||
typedef std::variant<int64_t, std::string, double, bool, char> varData;
|
||||
typedef std::variant<int64_t, std::string, double, bool, char, SolFunction> varData;
|
||||
varData data;
|
||||
public:
|
||||
SolDataType type = SolDataType::Int;
|
||||
@@ -85,11 +95,14 @@ namespace Solstice {
|
||||
SolData(std::string in);
|
||||
SolData(char in);
|
||||
SolData(bool in);
|
||||
SolData(SolFunction in);
|
||||
std::optional<int64_t> getInt();
|
||||
std::optional<double> getDouble();
|
||||
std::optional<std::string> getString();
|
||||
std::optional<char> getChar();
|
||||
std::optional<bool> getBool();
|
||||
std::optional<SolFunction> getFunction();
|
||||
std::string getTypeString();
|
||||
};
|
||||
|
||||
class SolNode {
|
||||
@@ -134,7 +147,7 @@ namespace Solstice {
|
||||
};
|
||||
|
||||
GroundProgram assembleProgram(SolNode& rootNode);
|
||||
SolDataType checkNodeReturnType(SolNode in);
|
||||
std::string checkNodeReturnType(SolNode in);
|
||||
|
||||
} // namespace Parser
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user