Skip to content

Commit

Permalink
Add error messages for out of range numeric literals (#188)
Browse files Browse the repository at this point in the history
* Add error messages for out of range numeric literals

* Add test
  • Loading branch information
marcauberer authored Aug 23, 2022
1 parent 52d2361 commit 529c03a
Show file tree
Hide file tree
Showing 14 changed files with 201 additions and 61 deletions.
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

0 comments on commit 529c03a

Please sign in to comment.