Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add error messages for out of range numeric literals #188

Merged
merged 3 commits into from
Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 67 additions & 3 deletions media/test-project/os-test.spice
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,72 @@ f<int> main() {
printf("Hello %s!", p1.getSecond());
}*/

f<int> main() {
foreach dyn item : { 1, 2, 3, 4, 5 } {
printf("Item %d: %d\n", idx, item);
import "std/type/int" as integer;

const int vertexCount = 9;

f<int> minDistance(int[] dist, bool[] sptSet) {
int min = integer.MAX_VALUE;
int minIndex;

for int v = 0; v < vertexCount; v++ {
if !sptSet[v] && dist[v] <= min {
min = dist[v];
minIndex = v;
}
}

return minIndex;
}

p printSolution(int[] dist) {
printf("Vertex \t\t Distance from source\n");
for int i = 0; i < vertexCount; i++ {
printf("%d \t\t %d\n", i, dist[i]);
}
}

p dijkstra(int[vertexCount][vertexCount] graph, int src) {
int[vertexCount] dist;
bool[vertexCount] sptSet;

// Fill with default values
for int i = 0; i < vertexCount; i++ {
dist[i] = integer.MAX_VALUE;
sptSet[i] = false;
}

// Set distance to starting node to 0
dist[src] = 0;

for int count = 0; count < vertexCount - 1; count++ {
int u = minDistance(dist, sptSet);
sptSet[u] = true;
for int v = 0; v < vertexCount; v++ {
if (!sptSet[v] && graph[u][v] != 0 && dist[u] != integer.MAX_VALUE &&
dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
}
}
}

// Print the solution
printSolution(dist);
}

f<int> main() {
int[vertexCount][vertexCount] graph = {};
graph[0] = { 0, 4, 0, 0, 0, 0, 0, 8, 0 };
graph[1] = { 4, 0, 8, 0, 0, 0, 0, 11, 0 };
graph[2] = { 0, 8, 0, 7, 0, 4, 0, 0, 2 };
graph[3] = { 0, 0, 7, 0, 9, 14, 0, 0, 0 };
graph[4] = { 0, 0, 0, 9, 0, 10, 0, 0, 0 };
graph[5] = { 0, 0, 4, 14, 10, 0, 2, 0, 0 };
graph[6] = { 0, 0, 0, 0, 0, 2, 0, 1, 6 };
graph[7] = { 8, 11, 0, 0, 0, 0, 1, 0, 7 };
graph[8] = { 0, 0, 2, 0, 0, 0, 6, 7, 0 };

printf("Computing shortest paths with Dijkstra's algorithm ...\n");
dijkstra(graph, 0);
printf("Done.\n");
}
10 changes: 4 additions & 6 deletions src/exception/AntlrThrowingErrorListener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

#include "AntlrThrowingErrorListener.h"

#include "LexerParserError.h"
#include <exception/LexerParserError.h>
#include <util/CodeLoc.h>

void AntlrThrowingErrorListener::syntaxError(antlr4::Recognizer *recognizer, antlr4::Token *offendingSymbol, size_t line,
size_t charPositionInLine, const std::string &msg, std::exception_ptr e) {
// Create message fragments
std::string errorType = mode == LEXER ? "Lexer error" : "Parser error";
std::string codePos = std::to_string(line) + ":" + std::to_string(charPositionInLine);
// Throw error
throw LexerParserError(errorType + " at line " + codePos + " " + msg);
CodeLoc codeLoc = CodeLoc("", line, charPositionInLine);
throw LexerParserError(codeLoc, mode == LEXER ? TOKENIZING_FAILED : PARSING_FAILED, msg);
}
4 changes: 4 additions & 0 deletions src/exception/ErrorFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

#include <util/CodeLoc.h>

LexerParserError ErrorFactory::get(const CodeLoc &codeLoc, LexerParserErrorType type, const std::string &message) {
return LexerParserError(codeLoc, type, message);
}

SemanticError ErrorFactory::get(const CodeLoc &codeLoc, SemanticErrorType type, const std::string &message) {
return SemanticError(codeLoc, type, message);
}
Expand Down
8 changes: 5 additions & 3 deletions src/exception/ErrorFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <exception/CliError.h>
#include <exception/IRError.h>
#include <exception/LexerParserError.h>
#include <exception/LinkerError.h>
#include <exception/SemanticError.h>

Expand All @@ -19,9 +20,10 @@ class ErrorFactory {
ErrorFactory() = default;

// Public methods
[[nodiscard]] static SemanticError get(const CodeLoc &codeLoc, SemanticErrorType type, const std::string &message) ;
[[nodiscard]] static IRError get(const CodeLoc &codeLoc, IRErrorType type, const std::string &message) ;
[[nodiscard]] static IRError get(IRErrorType type, const std::string &message) ;
[[nodiscard]] static LexerParserError get(const CodeLoc &codeLoc, LexerParserErrorType type, const std::string &message);
[[nodiscard]] static SemanticError get(const CodeLoc &codeLoc, SemanticErrorType type, const std::string &message);
[[nodiscard]] static IRError get(const CodeLoc &codeLoc, IRErrorType type, const std::string &message);
[[nodiscard]] static IRError get(IRErrorType type, const std::string &message);
[[nodiscard]] static CliError get(CliErrorType type, const std::string &message);
[[nodiscard]] static LinkerError get(LinkerErrorType type, const std::string &message);
};
35 changes: 34 additions & 1 deletion src/exception/LexerParserError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,42 @@

#include "LexerParserError.h"

#include <util/CodeLoc.h>

/**
* Constructor: Used in case that the exact code position where the error occurred is known
*
* @param codeLoc Code location where the error occurred
* @param type Type of the error
* @param message Error message suffix
*/
LexerParserError::LexerParserError(const CodeLoc &codeLoc, const LexerParserErrorType &type, const std::string &message) {
errorMessage = "Error in " + codeLoc.toPrettyString() + ": " + getMessagePrefix(type) + ": " + message;
}

/**
* Get the message for this particular error instance
*
* @return Error message in form of a char array
*/
const char *LexerParserError::what() const noexcept { return errorMessage.c_str(); }
const char *LexerParserError::what() const noexcept { return errorMessage.c_str(); }

/**
* Get the prefix of the error message for a particular error
*
* @param type Type of the error
* @return Prefix string for the error type
*/
std::string LexerParserError::getMessagePrefix(LexerParserErrorType type) {
switch (type) {
case TOKENIZING_FAILED:
return "Tokenizing failed";
case PARSING_FAILED:
return "Parsing failed";
case NUMBER_OUT_OF_RANGE:
return "Number is out of range";
case INVALID_CHAR_LITERAL:
return "Invalid char literal";
}
return "Unknown error"; // GCOV_EXCL_LINE
}
10 changes: 9 additions & 1 deletion src/exception/LexerParserError.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,26 @@
#include <string>
#include <utility>

// Forward declarations
struct CodeLoc;

enum LexerParserErrorType { TOKENIZING_FAILED, PARSING_FAILED, NUMBER_OUT_OF_RANGE, INVALID_CHAR_LITERAL };

/**
* Custom exception for errors, occurring in the semantic analysis phase
*/
class LexerParserError : public std::exception {
public:
// Constructors
explicit LexerParserError(std::string message) : errorMessage(std::move(message)) {} // GCOV_EXCL_LINE
explicit LexerParserError(const CodeLoc &codeLoc, const LexerParserErrorType &type, const std::string &message);

// Public methods
[[nodiscard]] const char *what() const noexcept override;

private:
// Members
std::string errorMessage;

// Private methods
[[nodiscard]] static std::string getMessagePrefix(LexerParserErrorType errorType);
};
1 change: 1 addition & 0 deletions src/exception/SemanticError.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,6 @@ class SemanticError : public std::exception {
// Members
std::string errorMessage;

// Private methods
[[nodiscard]] static std::string getMessagePrefix(SemanticErrorType errorType);
};
99 changes: 61 additions & 38 deletions src/parser/AstBuilderVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
#include <ast/AstNodes.h>
#include <util/CommonUtil.h>

AstBuilderVisitor::AstBuilderVisitor(AstNode *rootNode, std::string fileName)
: currentNode(rootNode), fileName(std::move(fileName)) {
// Create error factory for this specific file
this->err = std::make_unique<ErrorFactory>();
}

std::any AstBuilderVisitor::visitEntry(SpiceParser::EntryContext *ctx) {
auto entryNode = dynamic_cast<EntryNode *>(currentNode);
for (const auto &subTree : ctx->children) {
Expand Down Expand Up @@ -1197,17 +1203,17 @@ std::any AstBuilderVisitor::visitPrimitiveValue(SpiceParser::PrimitiveValueConte
primitiveValueNode->data.doubleValue = std::stod(t->toString());
} else if (auto t = dynamic_cast<antlr4::tree::TerminalNode *>(subTree); t->getSymbol()->getType() == SpiceParser::INT_LIT) {
primitiveValueNode->type = PrimitiveValueNode::TYPE_INT;
primitiveValueNode->data.intValue = parseInt(t->toString());
primitiveValueNode->data.intValue = parseInt(t);
} else if (auto t = dynamic_cast<antlr4::tree::TerminalNode *>(subTree);
t->getSymbol()->getType() == SpiceParser::SHORT_LIT) {
primitiveValueNode->type = PrimitiveValueNode::TYPE_SHORT;
primitiveValueNode->data.shortValue = parseShort(t->toString());
primitiveValueNode->data.shortValue = parseShort(t);
} else if (auto t = dynamic_cast<antlr4::tree::TerminalNode *>(subTree); t->getSymbol()->getType() == SpiceParser::LONG_LIT) {
primitiveValueNode->type = PrimitiveValueNode::TYPE_LONG;
primitiveValueNode->data.longValue = parseLong(t->toString());
primitiveValueNode->data.longValue = parseLong(t);
} else if (auto t = dynamic_cast<antlr4::tree::TerminalNode *>(subTree); t->getSymbol()->getType() == SpiceParser::CHAR_LIT) {
primitiveValueNode->type = PrimitiveValueNode::TYPE_CHAR;
primitiveValueNode->data.charValue = parseChar(ctx->CHAR_LIT()->toString());
primitiveValueNode->data.charValue = parseChar(ctx->CHAR_LIT());
} else if (auto t = dynamic_cast<antlr4::tree::TerminalNode *>(subTree);
t->getSymbol()->getType() == SpiceParser::STRING_LIT) {
primitiveValueNode->type = PrimitiveValueNode::TYPE_STRING;
Expand Down Expand Up @@ -1478,59 +1484,76 @@ void AstBuilderVisitor::replaceEscapeChars(std::string &string) {
CommonUtil::replaceAll(string, "\\?", "\?");
}

int32_t AstBuilderVisitor::parseInt(const std::string &input) {
std::function<int32_t(const std::string &, int)> cb = [](const std::string &substr, int base) {
int32_t AstBuilderVisitor::parseInt(antlr4::tree::TerminalNode *terminal) {
std::function<int32_t(const std::string &, int)> cb = [this](const std::string &substr, int base) {
return std::stoi(substr, nullptr, base);
};
return parseNumeric(input, cb);
return parseNumeric(terminal, cb);
}
int16_t AstBuilderVisitor::parseShort(const std::string &input) {
std::function<int16_t(const std::string &, int)> cb = [](const std::string &substr, int base) {
int16_t AstBuilderVisitor::parseShort(antlr4::tree::TerminalNode *terminal) {
std::function<int16_t(const std::string &, int)> cb = [this](const std::string &substr, int base) {
return (int16_t)std::stoi(substr, nullptr, base);
};
return parseNumeric(input, cb);
return parseNumeric(terminal, cb);
}

int64_t AstBuilderVisitor::parseLong(const std::string &input) {
std::function<int64_t(const std::string &, int)> cb = [](const std::string &substr, int base) {
int64_t AstBuilderVisitor::parseLong(antlr4::tree::TerminalNode *terminal) {
std::function<int64_t(const std::string &, int)> cb = [this](const std::string &substr, int base) {
return std::stoll(substr, nullptr, base);
};
return parseNumeric(input, cb);
return parseNumeric(terminal, cb);
}

int8_t AstBuilderVisitor::parseChar(const std::string &input) { return input[1]; }
int8_t AstBuilderVisitor::parseChar(antlr4::tree::TerminalNode *terminal) {
std::string input = terminal->toString();
if (input.length() != 3) {
CodeLoc codeLoc = CodeLoc(fileName, terminal->getSymbol());
throw err->get(codeLoc, PARSING_FAILED, "Invalid char literal " + input);
}
return input[1];
}

std::string AstBuilderVisitor::parseString(std::string input) {
input = input.substr(1, input.size() - 2);
replaceEscapeChars(input);
return input;
}

template <typename T> T AstBuilderVisitor::parseNumeric(const std::string &input, std::function<T(const std::string &, int)> cb) {
if (input.length() >= 3) {
char c1 = input[0];
char c2 = input[1];
std::string substr = input.substr(2);
if (c1 == '0') {
switch (c2) {
case 'd':
case 'D':
return cb(substr, 10);
case 'b':
case 'B':
return cb(substr, 2);
case 'h':
case 'H':
case 'x':
case 'X':
return cb(substr, 16);
case 'o':
case 'O':
return cb(substr, 8);
default:
return cb(input, 10);
template <typename T>
T AstBuilderVisitor::parseNumeric(antlr4::tree::TerminalNode *terminal, std::function<T(const std::string &, int)> cb) {
std::string input = terminal->toString();
try {
if (input.length() >= 3) {
char c1 = input[0];
char c2 = input[1];
std::string substr = input.substr(2);
if (c1 == '0') {
switch (c2) {
case 'd':
case 'D':
return cb(substr, 10);
case 'b':
case 'B':
return cb(substr, 2);
case 'h':
case 'H':
case 'x':
case 'X':
return cb(substr, 16);
case 'o':
case 'O':
return cb(substr, 8);
default:
return cb(input, 10);
}
}
}
return cb(input, 10);
} catch (std::out_of_range &e) {
CodeLoc codeLoc = CodeLoc(fileName, terminal->getSymbol());
throw err->get(codeLoc, NUMBER_OUT_OF_RANGE, "The provided number is out of range");
} catch (std::invalid_argument &e) {
CodeLoc codeLoc = CodeLoc(fileName, terminal->getSymbol());
throw err->get(codeLoc, NUMBER_OUT_OF_RANGE, "You tried to parse '" + input + "' as an integer, but it was no integer");
}
return cb(input, 10);
}
15 changes: 9 additions & 6 deletions src/parser/AstBuilderVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
#include <functional>
#include <utility>

#include <exception/ErrorFactory.h>

// Forward declarations
class AstNode;

class AstBuilderVisitor : public SpiceVisitor {
public:
// Constructors
explicit AstBuilderVisitor(AstNode *rootNode, std::string fileName) : currentNode(rootNode), fileName(std::move(fileName)) {}
explicit AstBuilderVisitor(AstNode *rootNode, std::string fileName);

// Public methods
std::any visitEntry(SpiceParser::EntryContext *ctx) override;
Expand Down Expand Up @@ -85,13 +87,14 @@ class AstBuilderVisitor : public SpiceVisitor {
// Members
AstNode *currentNode;
std::string fileName;
std::unique_ptr<ErrorFactory> err;

// Private methods
static int32_t parseInt(const std::string &input);
static int16_t parseShort(const std::string &input);
static int64_t parseLong(const std::string &input);
static int8_t parseChar(const std::string &input);
int32_t parseInt(antlr4::tree::TerminalNode *terminal);
int16_t parseShort(antlr4::tree::TerminalNode *terminal);
int64_t parseLong(antlr4::tree::TerminalNode *terminal);
int8_t parseChar(antlr4::tree::TerminalNode *terminal);
static std::string parseString(std::string input);
template <typename T> T static parseNumeric(const std::string &input, std::function<T(const std::string &, int)> cb);
template <typename T> T parseNumeric(antlr4::tree::TerminalNode *terminal, std::function<T(const std::string &, int)> cb);
static void replaceEscapeChars(std::string &string);
};
Loading