Initial instance
This commit is contained in:
commit
48e1c48579
65 files changed
+6233
No files matched your search
@@ -0,0 +1 @@
|
||||
#include "assert.h"
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef ASSERT_H
|
||||
#define ASSERT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
class AtlasException : public std::exception {
|
||||
|
||||
const std::string fileName;
|
||||
const int fileLine;
|
||||
|
||||
const std::string message;
|
||||
|
||||
protected:
|
||||
virtual void buildString(std::ostream& stream) const;
|
||||
std::string fullMessage;
|
||||
|
||||
public:
|
||||
explicit AtlasException(const std::string& file, int line,
|
||||
const std::string& msg);
|
||||
|
||||
virtual ~AtlasException() {
|
||||
}
|
||||
|
||||
virtual std::string buildMessage() const;
|
||||
|
||||
virtual std::string toString() const {
|
||||
return fullMessage;
|
||||
}
|
||||
|
||||
virtual const char* what() const noexcept {
|
||||
return fullMessage.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ASSERT_H
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "astnode.h"
|
||||
|
||||
AstNode::~AstNode() = default;
|
||||
|
||||
NumberNode::NumberNode(std::string expression) {
|
||||
if(expression.size() > 2) {
|
||||
if(expression.substr(0, 2) == "0x") {
|
||||
this->value = std::stoll(expression, 0, 16);
|
||||
} else if(expression.substr(0, 2) == "0b") {
|
||||
this->value = std::stoll(expression, 0, 2);
|
||||
} else {
|
||||
this->value = std::stoll(expression, 0, 10);
|
||||
}
|
||||
} else {
|
||||
this->value = std::stoll(expression, 0, 10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef ASTNODE_H
|
||||
#define ASTNODE_H
|
||||
|
||||
#include "token.h"
|
||||
|
||||
/**
|
||||
* AST base class, all AST node classes extand this class. Class contains basic
|
||||
* information about a nodes location in file.
|
||||
*/
|
||||
class AstNode {
|
||||
public:
|
||||
|
||||
Span span;
|
||||
|
||||
AstNode() = default;
|
||||
|
||||
virtual ~AstNode(); // this is defined in Ast.cpp just so that the
|
||||
// compiler doesn't complain about not knowing which
|
||||
// object file to include the vtable in
|
||||
AstNode(AstNode&&) = default;
|
||||
|
||||
AstNode& operator=(AstNode&&) = default;
|
||||
AstNode(const AstNode&) = default;
|
||||
|
||||
AstNode& operator=(const AstNode&) = default;
|
||||
|
||||
};
|
||||
|
||||
class StringNode: public AstNode {
|
||||
public:
|
||||
std::string value;
|
||||
};
|
||||
|
||||
|
||||
class IdentifierNode: public AstNode {
|
||||
public:
|
||||
std::string value;
|
||||
};
|
||||
|
||||
class NumberNode: public AstNode {
|
||||
public:
|
||||
long long int value;
|
||||
NumberNode(std::string expression);
|
||||
NumberNode(): value(0) {}
|
||||
};
|
||||
|
||||
class CountNode: public AstNode {
|
||||
public:
|
||||
NumberNode first;
|
||||
NumberNode second;
|
||||
|
||||
CountNode(NumberNode first, NumberNode second): first(first), second(second) {}
|
||||
CountNode() {}
|
||||
};
|
||||
|
||||
#endif // ASTNODE_H
|
||||
@@ -0,0 +1,443 @@
|
||||
#include "comdellexer.h"
|
||||
#include "token.h"
|
||||
#include "tokenstype.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <cctype>
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Auxiliary functions - recognizing category of characters
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
inline bool identifierStart(char ch) {
|
||||
return isalpha(ch) || ch == '_';
|
||||
}
|
||||
|
||||
inline bool identifierContinue(char ch) {
|
||||
return isalnum(ch) || ch == '_';
|
||||
}
|
||||
|
||||
inline bool numberStart(char ch) {
|
||||
return isdigit(ch);
|
||||
}
|
||||
|
||||
inline bool isWhitespace(char ch) {
|
||||
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* ComdelLexer class public methods
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
ComdelLexer::ComdelLexer(std::string fileName, std::string source,
|
||||
ParseContext* parseContext)
|
||||
: source(std::move(source)),
|
||||
parseContext(parseContext),
|
||||
// qqq zamijeniti poziv path_absolute sa Qt-ovim pozivom
|
||||
fileId(this->parseContext->addFile(fileName, this->source)),
|
||||
position(this->fileId, 1, 1, 0),
|
||||
ch(this->source[0])
|
||||
{}
|
||||
|
||||
|
||||
LexerResult ComdelLexer::tokenize() {
|
||||
while (!eof()) {
|
||||
tokenBegin = position;
|
||||
|
||||
auto tokenType = nextTokenType();
|
||||
if(!tokenType) {
|
||||
errors.push_back(tokenType.error());
|
||||
continue;
|
||||
}
|
||||
if (tokenType == TokenType::WHITESPACE || tokenType == TokenType::COMMENT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string text = source.substr(tokenBegin.offset,
|
||||
position.offset - tokenBegin.offset);
|
||||
|
||||
tokens.push_back(Token(*tokenType, Span(tokenBegin, position), text));
|
||||
}
|
||||
|
||||
tokens.push_back(Token( TokenType::END_OF_FILE, Span(position), ""));
|
||||
|
||||
return LexerResult { tokens, errors };
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* ComdelLexer class private methods
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
|
||||
// Numbers are sequences of digits and underscores
|
||||
// with AT LEAST ONE digit.
|
||||
|
||||
unsigned ComdelLexer::takeNumberInRadix(Radix radix) {
|
||||
unsigned digitsTaken = 0;
|
||||
|
||||
while(true) {
|
||||
if (digitIsValid(ch, radix)) { // skip and count real digits
|
||||
bump();
|
||||
digitsTaken++;
|
||||
} else if(ch == '_') { // skip underscores
|
||||
bump();
|
||||
} else { // some other character => end of number
|
||||
return digitsTaken;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ComdelLexer::digitIsValid(char ch, Radix radix) {
|
||||
|
||||
// Check digits in normal numbers
|
||||
//digit is 0..1
|
||||
if(ch == '1' || ch == '0') {
|
||||
// OK for all radixes
|
||||
return true;
|
||||
}
|
||||
//digit is 2..9
|
||||
if(isdigit(ch)) {
|
||||
// OK for decimal and hex radixes
|
||||
return(radix != Radix::BIN_NUMBER);
|
||||
}
|
||||
// digit is a..f A..F
|
||||
else if(isxdigit(ch)) {
|
||||
// OK only for hex radix
|
||||
return(radix ==Radix::HEX_NUMBER);
|
||||
}
|
||||
// NOT 0..9 a..f A..F
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
ComdelLexer::Radix ComdelLexer::takeRadix(){
|
||||
if (ch == '0') {
|
||||
char nextChar = peek();
|
||||
if (nextChar == 'x' || nextChar == 'X') {
|
||||
bump(2);
|
||||
return Radix::HEX_NUMBER;
|
||||
} else if (nextChar == 'b' || nextChar == 'B') {
|
||||
bump(2);
|
||||
return Radix::BIN_NUMBER;
|
||||
}
|
||||
}
|
||||
return Radix::DEC_NUMBER;
|
||||
}
|
||||
|
||||
|
||||
// color is sequence of hex-digits (preceeded with # which is already consumed)
|
||||
unsigned ComdelLexer::takeHexColor() {
|
||||
unsigned digitsTaken = 0;
|
||||
|
||||
while(true) {
|
||||
if (digitIsValid(ch, Radix::HEX_NUMBER)) { // skip and count hex digits
|
||||
bump();
|
||||
digitsTaken++;
|
||||
} else { // some other character => end of number
|
||||
return digitsTaken;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PResult<TokenType> ComdelLexer::takeString() {
|
||||
Position lo = position;
|
||||
|
||||
if (ch != '"')
|
||||
return PError({Span(lo), "expected string here"});
|
||||
bump(); // skip starting "
|
||||
|
||||
// Check ecsape-sequences \t \n \\ \" but leave them in string.
|
||||
// They will be replaced in the constructor of StringLiteral AST-node.
|
||||
while (ch != '"' && ch != '\n' && !eof()) {
|
||||
if(ch == '"') {
|
||||
// possible start of escape-sequence
|
||||
char nextCh = peek();
|
||||
if(nextCh == '\\' || nextCh == '\"' || nextCh == 't' || nextCh == 'n')
|
||||
bump(); // legal escape-sequence: skip backslash
|
||||
else
|
||||
return PError({Span(lo, position), "illegal escape-sequence (allowed: \\n \\\" \\\\ \\t"});
|
||||
}
|
||||
bump(); // skip normal char OR skip second char in escape-sequence
|
||||
}
|
||||
|
||||
if (eof() || ch == '\n') {
|
||||
return PError({Span(lo, position), "unterminated string"});
|
||||
}
|
||||
bump(); // skip closing "
|
||||
return TokenType::STRING;
|
||||
}
|
||||
|
||||
|
||||
PResult<TokenType> ComdelLexer::takeRawString()
|
||||
{
|
||||
Position lo = position;
|
||||
|
||||
if (ch != '`')
|
||||
return PError({Span(lo), "expected string here"});
|
||||
bump(); // skip starting '
|
||||
|
||||
// Ignore ecsape-sequences - take all characters until closing '
|
||||
while (ch != '`' && !eof()) {
|
||||
bump(); // skip characters
|
||||
}
|
||||
|
||||
if (eof() || ch == '\n') {
|
||||
return PError({Span(lo, position), "unterminated string"});
|
||||
}
|
||||
bump(); // skip closing '
|
||||
return TokenType::STRING;
|
||||
}
|
||||
|
||||
|
||||
void ComdelLexer::skipWhitespace() {
|
||||
while (!eof()) {
|
||||
if (isWhitespace(ch)) {
|
||||
bump();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ComdelLexer::skipComment() {
|
||||
while (!eof() && ch != '\n') {
|
||||
bump();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// returns false if the comment is unterminated
|
||||
bool ComdelLexer::skipMultilineComment() {
|
||||
while (!eof()) {
|
||||
if (ch == '*') {
|
||||
bump();
|
||||
if (ch == '/') {
|
||||
bump();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
bump();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
PResult<TokenType> ComdelLexer::nextTokenType() {
|
||||
if (isWhitespace(ch))
|
||||
{
|
||||
skipWhitespace();
|
||||
return TokenType::WHITESPACE;
|
||||
}
|
||||
else if (identifierStart(ch))
|
||||
{
|
||||
bump();
|
||||
while (identifierContinue(ch))
|
||||
bump();
|
||||
return TokenType::IDENTIFIER;
|
||||
}
|
||||
else if (numberStart(ch))
|
||||
{
|
||||
unsigned takenDigits;
|
||||
Radix radix = takeRadix();
|
||||
takenDigits = takeNumberInRadix(radix);
|
||||
if (takenDigits == 0) {
|
||||
return PError({Span(tokenBegin, position),
|
||||
"no digits found for number, or misspelled number"});
|
||||
}
|
||||
if( isalnum(ch) ) {
|
||||
return PError({Span(tokenBegin, position),
|
||||
"illegal digit or letter found at the end of number"});
|
||||
}
|
||||
return TokenType::NUMBER;
|
||||
}
|
||||
else if (ch == '!')
|
||||
{
|
||||
bump();
|
||||
return TokenType::NOT;
|
||||
}
|
||||
|
||||
else if (ch == '<')
|
||||
{
|
||||
bump();
|
||||
return TokenType::LT;
|
||||
}
|
||||
|
||||
else if (ch == '>')
|
||||
{
|
||||
bump();
|
||||
return TokenType::GT;
|
||||
}
|
||||
|
||||
else if (ch == '#')
|
||||
{
|
||||
bump();
|
||||
|
||||
if (digitIsValid(ch, Radix::HEX_NUMBER)) {
|
||||
unsigned takenDigits = takeHexColor();
|
||||
if (takenDigits != 6 && takenDigits != 8) {
|
||||
return PError({Span(tokenBegin, position),
|
||||
"hex-color must have 6 or 8 hex-digits"});
|
||||
}
|
||||
if( isalnum(ch) ) {
|
||||
return PError({Span(tokenBegin, position),
|
||||
"illegal letter found at the end of hex-color"});
|
||||
}
|
||||
return TokenType::COLOR;
|
||||
} else {
|
||||
return PError({Span(tokenBegin, position),
|
||||
"unexpected #"});;
|
||||
}
|
||||
}
|
||||
else if (ch == '@')
|
||||
{
|
||||
bump();
|
||||
while (identifierContinue(ch))
|
||||
bump();
|
||||
return TokenType::KEYWORD;
|
||||
}
|
||||
else if (ch == '"')
|
||||
{
|
||||
return takeString();
|
||||
}
|
||||
else if (ch == '`')
|
||||
{
|
||||
return takeRawString();
|
||||
}
|
||||
else if (ch == '(')
|
||||
{
|
||||
bump();
|
||||
return TokenType::LPAREN;
|
||||
}
|
||||
else if (ch == ')')
|
||||
{
|
||||
bump();
|
||||
return TokenType::RPAREN;
|
||||
}
|
||||
else if (ch == '[')
|
||||
{
|
||||
bump();
|
||||
return TokenType::LBRACKET;
|
||||
}
|
||||
else if (ch == ']')
|
||||
{
|
||||
bump();
|
||||
return TokenType::RBRACKET;
|
||||
}
|
||||
else if (ch == '{')
|
||||
{
|
||||
bump();
|
||||
return TokenType::LBRACE;
|
||||
}
|
||||
else if (ch == '}')
|
||||
{
|
||||
bump();
|
||||
return TokenType::RBRACE;
|
||||
}
|
||||
else if (ch == '/')
|
||||
{
|
||||
bump();
|
||||
if (ch == '/')
|
||||
{
|
||||
bump();
|
||||
skipComment();
|
||||
return TokenType::COMMENT;
|
||||
} else if (ch == '*')
|
||||
{
|
||||
bump();
|
||||
if (!skipMultilineComment())
|
||||
{
|
||||
return PError({Span(tokenBegin, position),
|
||||
"unterminated multiline comment"});
|
||||
}
|
||||
return TokenType::COMMENT;
|
||||
}
|
||||
return PError({Span(tokenBegin, position),
|
||||
"unexpected /"});;
|
||||
}
|
||||
else if (ch == '.')
|
||||
{
|
||||
bump();
|
||||
return TokenType::DOT;
|
||||
}
|
||||
else if (ch == ':')
|
||||
{
|
||||
bump();
|
||||
return TokenType::COLON;
|
||||
}
|
||||
else if (ch == ';')
|
||||
{
|
||||
bump();
|
||||
return TokenType::SEMICOLON;
|
||||
}
|
||||
else if (ch == ',')
|
||||
{
|
||||
bump();
|
||||
return TokenType::COMMA;
|
||||
}
|
||||
else if (ch == '=')
|
||||
{
|
||||
bump();
|
||||
return TokenType::EQUALS;
|
||||
}
|
||||
else if (ch == '<')
|
||||
{
|
||||
bump();
|
||||
return TokenType::LT;
|
||||
}
|
||||
else if (ch == '>')
|
||||
{
|
||||
return TokenType::GT;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream message;
|
||||
message << "unexpected character `" << ch << "`";
|
||||
bump();
|
||||
return PError({Span(tokenBegin, position), message.str()});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Move position to the new character in input file.
|
||||
// Fetch the new character in 'ch'
|
||||
void ComdelLexer::bump(unsigned count) {
|
||||
for (unsigned i=0; i < count && !eof(); i++) {
|
||||
if (ch == '\n') {
|
||||
position.line += 1;
|
||||
position.col = 1;
|
||||
parseContext->getFile(fileId).addLineOffset(position.offset+1);
|
||||
} else {
|
||||
position.col += 1;
|
||||
}
|
||||
position.offset += 1;
|
||||
ch = source[position.offset];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fetch and return next character without moving position.
|
||||
// Fetch does not cross line boundary.
|
||||
// Returns \n when next char does not exists (end of line or end of file)
|
||||
char ComdelLexer::peek() {
|
||||
if(position.offset+1 == source.size()) // eof
|
||||
return '\n';
|
||||
|
||||
return source[position.offset+1];
|
||||
}
|
||||
|
||||
|
||||
bool ComdelLexer::eof() {
|
||||
return position.offset == source.size();
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef COMDELLEXER_H
|
||||
#define COMDELLEXER_H
|
||||
|
||||
#include "parsecontext.h"
|
||||
#include "presult.h"
|
||||
#include "sourceerror.h"
|
||||
#include "token.h"
|
||||
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct LexerResult {
|
||||
std::vector<Token> tokens;
|
||||
std::vector<SourceError> errors;
|
||||
};
|
||||
|
||||
class ComdelLexer
|
||||
{
|
||||
|
||||
enum Radix {
|
||||
BIN_NUMBER = 2,
|
||||
DEC_NUMBER = 10,
|
||||
HEX_NUMBER = 16
|
||||
};
|
||||
|
||||
std::string source;
|
||||
std::vector<Token> tokens;
|
||||
std::vector<SourceError> errors;
|
||||
ParseContext* parseContext;
|
||||
unsigned fileId;
|
||||
|
||||
public:
|
||||
ComdelLexer(std::string fileName, std::string source, ParseContext* parseContext);
|
||||
LexerResult tokenize();
|
||||
|
||||
private:
|
||||
void skipWhitespace();
|
||||
|
||||
unsigned takeNumberInRadix(Radix radix);
|
||||
unsigned takeHexColor();
|
||||
bool digitIsValid(char ch, Radix radix);
|
||||
Radix takeRadix();
|
||||
|
||||
PResult<TokenType> nextTokenType();
|
||||
|
||||
Position tokenBegin;
|
||||
Position position;
|
||||
char ch;
|
||||
|
||||
void skipComment();
|
||||
bool skipMultilineComment();
|
||||
|
||||
PResult<TokenType> takeString();
|
||||
PResult<TokenType> takeRawString();
|
||||
|
||||
void bump(unsigned count = 1);
|
||||
char peek();
|
||||
bool eof();
|
||||
};
|
||||
|
||||
#endif // COMDELLEXER_H
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,95 @@
|
||||
#ifndef COMDELPARSER_H
|
||||
#define COMDELPARSER_H
|
||||
|
||||
#include "sourceerror.h"
|
||||
#include "presult.h"
|
||||
#include "token.h"
|
||||
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <domain/library/library.h>
|
||||
|
||||
/// Records the current span and can later be called on
|
||||
/// an ast node to apply the entire span to that node
|
||||
///
|
||||
/// Spanner's call operator MOVES from the node so it should
|
||||
/// be always used last.
|
||||
class Spanner {
|
||||
const Span lo; // the low end of the span, beginning of the node
|
||||
|
||||
// REFERENCE to the parser's prevSpan. After parsing a node this will
|
||||
// "point" to the span of the last token contained in the node.
|
||||
const Span& prevSpan;
|
||||
|
||||
public:
|
||||
Spanner(Span lo, Span& prevSpan) : lo(lo), prevSpan(prevSpan) {}
|
||||
|
||||
template <typename T, typename = std::enable_if<std::is_base_of<AstNode, T>::value>>
|
||||
typename std::remove_reference_t<T> operator()(T&& astNode) const {
|
||||
//astNode.span = lo.to(prevSpan);
|
||||
return std::move(astNode);
|
||||
}
|
||||
};
|
||||
|
||||
class ComdelParser
|
||||
{
|
||||
private:
|
||||
std::vector<Token> tokens;
|
||||
std::set<TokenType> expectedTokens;
|
||||
|
||||
unsigned int position;
|
||||
std::vector<SourceError> errors;
|
||||
|
||||
Span &getPreviousSpan();
|
||||
|
||||
void bump();
|
||||
|
||||
bool consume(TokenType tokenType);
|
||||
bool check(TokenType tokenType);
|
||||
|
||||
Token ¤t();
|
||||
|
||||
[[nodiscard]] PError unexpected();
|
||||
|
||||
template<typename T>
|
||||
PResult<std::vector<T>> parseList(std::optional<TokenType> openDelim,
|
||||
TokenType closeDelim,
|
||||
std::optional<TokenType> separator,
|
||||
bool allowTrailing,
|
||||
const std::function<PResult<T> ()> &parse_f);
|
||||
|
||||
Spanner getSpanner();
|
||||
|
||||
void parseBlock();
|
||||
|
||||
PResult<StringNode> parseString();
|
||||
PResult<IdentifierNode> parseIdentifier();
|
||||
PResult<NumberNode> parseNumber();
|
||||
PResult<CountNode> parseCount();
|
||||
PResult<PropertyNode> parseProperty(std::optional<TokenType> valueType);
|
||||
PResult<EnumerationNode> parseEnumeration();
|
||||
|
||||
PResult<Component> parseComponent();
|
||||
PResult<AddressSpace> parseAddress();
|
||||
PResult<Pin> parsePin();
|
||||
PResult<Display> parseDisplay();
|
||||
PResult<PinConnection> parsePinConnection();
|
||||
PResult<Attribute> parseAttribute();
|
||||
PResult<Popup> parsePopup();
|
||||
PResult<Rule> parseRule();
|
||||
PResult<Bus> parseBus();
|
||||
PResult<Wire> parseWire();
|
||||
PResult<Connection> parseConnection();
|
||||
PResult<DisplayItem> parseDisplayItem();
|
||||
PResult<IfStmt> parseIfStatement();
|
||||
PResult<Value> parseValue();
|
||||
|
||||
public:
|
||||
ComdelParser(std::vector<Token> tokens);
|
||||
std::optional<Library> parse();
|
||||
const std::vector<SourceError>& getErrors();
|
||||
};
|
||||
|
||||
#endif // COMDELPARSER_H
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,128 @@
|
||||
#include "parsecontext.h"
|
||||
#include "assert.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
ParseContext::ParseContext()
|
||||
{
|
||||
applicationDir = std::string();
|
||||
}
|
||||
|
||||
unsigned ParseContext::addFile(const std::string& fileName,
|
||||
const std::string& source)
|
||||
{
|
||||
fileMap.push_back(SourceFile(fileName, source));
|
||||
return fileMap.size();
|
||||
}
|
||||
|
||||
SourceFile& ParseContext::getFile(unsigned fileId)
|
||||
{
|
||||
return fileMap[fileId-1];
|
||||
}
|
||||
|
||||
|
||||
struct pad {
|
||||
char ch; int count;
|
||||
|
||||
pad(char ch, int count) : ch(ch), count(count) {}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, const pad& p) {
|
||||
for (int i = 0; i < p.count; i++) {
|
||||
stream << p.ch;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
#define ATLAS_ASSERT(x) \
|
||||
do {} while(0)
|
||||
|
||||
void ParseContext::formatError(const SourceError& sourceError,
|
||||
std::ostream& stream,
|
||||
const std::string& ErrorOrWarning)
|
||||
{
|
||||
ATLAS_ASSERT(sourceError.span.lo.col != 0
|
||||
&& sourceError.span.hi.col != 0);
|
||||
|
||||
SourceFile sourceFile = getFile(sourceError.span.lo.fileId);
|
||||
|
||||
stream << ErrorOrWarning << sourceError.message << std::endl;
|
||||
|
||||
Span span = sourceError.span;
|
||||
|
||||
// figure out how much padding is required for line numbers in front of
|
||||
// printed source code
|
||||
int numberPad = 0;
|
||||
while (true) {
|
||||
numberPad = std::max(numberPad,
|
||||
static_cast<int>(ceil(log10(span.hi.line))) + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
span = sourceError.span;
|
||||
|
||||
// print all spans
|
||||
while (true) {
|
||||
SourceFile sourceFile = getFile(span.lo.fileId);
|
||||
|
||||
// check if the span spans across multiple lines or even files
|
||||
bool complex = span.lo.fileId != span.hi.fileId
|
||||
|| span.lo.line != span.hi.line;
|
||||
|
||||
stream << pad(' ', numberPad) << " -> " << sourceFile.getFileName()
|
||||
<< ":" << span.lo.line << ":" << span.lo.col << std::endl;
|
||||
stream << pad(' ', numberPad) << " |" << std::endl
|
||||
<< std::right << std::setw(numberPad) << span.lo.line << " | "
|
||||
<< sourceFile.getLine(span.lo.line) << std::endl
|
||||
<< pad(' ', numberPad) << " | ";
|
||||
for (unsigned i = 0; i < span.lo.col - 1; i++) {
|
||||
stream << ' ';
|
||||
}
|
||||
if (complex) {
|
||||
// just point to the beginning of the span
|
||||
stream << '^';
|
||||
} else {
|
||||
// underline the entire span
|
||||
for (unsigned i = 0; i < span.hi.col - span.lo.col; i++) {
|
||||
stream << '^';
|
||||
}
|
||||
}
|
||||
|
||||
stream << std::endl;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
stream << std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SourceFile::SourceFile(std::string fileName, std::string source)
|
||||
: fileName(std::move(fileName)), source(std::move(source))
|
||||
{
|
||||
lineOffsets.push_back(0);
|
||||
}
|
||||
|
||||
const std::string& SourceFile::getFileName() const
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
const std::string SourceFile::getLine(unsigned line) const
|
||||
{
|
||||
auto lineOffset = lineOffsets[line-1];
|
||||
auto nextLF = source.find('\n', lineOffset);
|
||||
auto nextCR = source.find('\r', lineOffset);
|
||||
auto nl = std::min(nextCR, nextLF);
|
||||
|
||||
return source.substr(lineOffset, nl - lineOffset);
|
||||
}
|
||||
|
||||
void SourceFile::addLineOffset(unsigned offset)
|
||||
{
|
||||
lineOffsets.push_back(offset);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef PARSECONTEXT_H
|
||||
#define PARSECONTEXT_H
|
||||
|
||||
|
||||
#include "sourceerror.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
class SourceFile {
|
||||
std::string fileName;
|
||||
std::string source;
|
||||
std::vector<unsigned> lineOffsets;
|
||||
|
||||
public:
|
||||
// qqq source je CIJELI comdel fajl,
|
||||
// za inicijalizaciju source se koristi std::move, a možda treba biti referenca???
|
||||
SourceFile(std::string fileName, std::string source);
|
||||
|
||||
const std::string& getFileName() const;
|
||||
const std::string getLine(unsigned line) const;
|
||||
void addLineOffset(unsigned offset);
|
||||
};
|
||||
|
||||
|
||||
class ParseContext
|
||||
{
|
||||
std::string applicationDir;
|
||||
std::vector<SourceFile> fileMap;
|
||||
|
||||
public:
|
||||
ParseContext();
|
||||
|
||||
unsigned addFile(const std::string& fileName, const std::string& source);
|
||||
SourceFile& getFile(unsigned file);
|
||||
|
||||
void formatError(const SourceError &sourceError, std::ostream &stream, const std::string& ErrorOrWarning);
|
||||
|
||||
};
|
||||
|
||||
#endif // PARSECONTEXT_H
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "parser_util.h"
|
||||
|
||||
parser_util::parser_util()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef PARSER_UTIL_H
|
||||
#define PARSER_UTIL_H
|
||||
|
||||
|
||||
class parser_util
|
||||
{
|
||||
public:
|
||||
parser_util();
|
||||
};
|
||||
|
||||
#endif // PARSER_UTIL_H
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "comdellexer.h"
|
||||
#include "comdelparser.h"
|
||||
#include "parserutil.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
std::optional<Library> loadLibraryFromFile(ParseContext * parseContext,
|
||||
const char* name,
|
||||
std::ostream& stream)
|
||||
{
|
||||
std::ifstream in(name, std::ios::in | std::ios::binary);
|
||||
if( ! in ) {
|
||||
stream << "ERROR: cannot open file '" << name
|
||||
<< "' (file does not exist or is unreadable)" << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string source((std::istreambuf_iterator<char>(in)),
|
||||
std::istreambuf_iterator<char>());
|
||||
ComdelLexer lexer(name, source, parseContext);
|
||||
LexerResult lexerResult = lexer.tokenize();
|
||||
|
||||
if (lexerResult.errors.size()) {
|
||||
for (auto& error : lexerResult.errors) {
|
||||
parseContext->formatError(error, stream, "ERROR: ");
|
||||
}
|
||||
return std::nullopt; // if lexer has found errors => don't parse
|
||||
}
|
||||
|
||||
ComdelParser parser(lexerResult.tokens);
|
||||
auto unit = parser.parse();
|
||||
if (!unit) {
|
||||
for (auto& error : parser.getErrors()) {
|
||||
parseContext->formatError(error, stream, "ERROR: ");
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return *unit;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef PARSERUTIL_H
|
||||
#define PARSERUTIL_H
|
||||
|
||||
#include "parsecontext.h"
|
||||
|
||||
#include <domain/library/library.h>
|
||||
|
||||
std::optional<Library> loadLibraryFromFile(ParseContext * parseContext,
|
||||
const char* name,
|
||||
std::ostream& stream);
|
||||
|
||||
#endif // PARSERUTIL_H
|
||||
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016 Jonathan B. Coe
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef POLY_H
|
||||
#define POLY_H
|
||||
|
||||
#include <cassert>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation detail classes
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class T>
|
||||
struct default_copy
|
||||
{
|
||||
T* operator()(const T& t) const
|
||||
{
|
||||
return new T(t);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct default_delete
|
||||
{
|
||||
void operator()(const T* t) const
|
||||
{
|
||||
delete t;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct control_block
|
||||
{
|
||||
virtual ~control_block() = default;
|
||||
|
||||
virtual std::unique_ptr<control_block> clone() const = 0;
|
||||
|
||||
virtual T* ptr() = 0;
|
||||
};
|
||||
|
||||
template <class T, class U = T>
|
||||
class direct_control_block : public control_block<T>
|
||||
{
|
||||
static_assert(!std::is_reference<U>::value, "");
|
||||
U u_;
|
||||
|
||||
public:
|
||||
template <class... Ts>
|
||||
explicit direct_control_block(Ts&&... ts) : u_(U(std::forward<Ts>(ts)...))
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<control_block<T>> clone() const override
|
||||
{
|
||||
return std::make_unique<direct_control_block>(*this);
|
||||
}
|
||||
|
||||
T* ptr() override
|
||||
{
|
||||
return std::addressof(u_);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class U, class C = default_copy<U>,
|
||||
class D = default_delete<U>>
|
||||
class pointer_control_block : public control_block<T>, public C
|
||||
{
|
||||
std::unique_ptr<U, D> p_;
|
||||
|
||||
public:
|
||||
explicit pointer_control_block(U* u, C c = C{}, D d = D{})
|
||||
: C(std::move(c)), p_(u, std::move(d))
|
||||
{
|
||||
}
|
||||
|
||||
explicit pointer_control_block(std::unique_ptr<U, D> p, C c = C{})
|
||||
: C(std::move(c)), p_(std::move(p))
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<control_block<T>> clone() const override
|
||||
{
|
||||
assert(p_);
|
||||
return std::make_unique<pointer_control_block>(
|
||||
C::operator()(*p_), static_cast<const C&>(*this), p_.get_deleter());
|
||||
}
|
||||
|
||||
T* ptr() override
|
||||
{
|
||||
return p_.get();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
class delegating_control_block : public control_block<T>
|
||||
{
|
||||
std::unique_ptr<control_block<U>> delegate_;
|
||||
|
||||
public:
|
||||
explicit delegating_control_block(std::unique_ptr<control_block<U>> b)
|
||||
: delegate_(std::move(b))
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<control_block<T>> clone() const override
|
||||
{
|
||||
return std::make_unique<delegating_control_block>(delegate_->clone());
|
||||
}
|
||||
|
||||
T* ptr() override
|
||||
{
|
||||
return static_cast<T*>(delegate_->ptr());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
class bad_poly_construction : std::exception
|
||||
{
|
||||
public:
|
||||
bad_poly_construction() noexcept = default;
|
||||
|
||||
const char* what() const noexcept override
|
||||
{
|
||||
return "Dynamic and static type mismatch in poly "
|
||||
"construction";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class poly;
|
||||
|
||||
template <class T>
|
||||
struct is_poly : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct is_poly<poly<T>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// `poly` class definition
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class T>
|
||||
class poly
|
||||
{
|
||||
static_assert(!std::is_union<T>::value, "");
|
||||
static_assert(std::is_class<T>::value, "");
|
||||
|
||||
template <class U>
|
||||
friend class poly;
|
||||
|
||||
template <class T_, class U, class... Ts>
|
||||
friend poly<T_> make_poly(Ts&&... ts);
|
||||
template <class T_, class... Ts>
|
||||
friend poly<T_> make_poly(Ts&&... ts);
|
||||
template <class T_, class U>
|
||||
friend poly<T_> poly_cast(poly<U> p);
|
||||
|
||||
T* ptr_ = nullptr;
|
||||
std::unique_ptr<detail::control_block<T>> cb_;
|
||||
|
||||
public:
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
|
||||
~poly() = default;
|
||||
|
||||
|
||||
//
|
||||
// Constructors
|
||||
//
|
||||
|
||||
poly()
|
||||
{
|
||||
}
|
||||
|
||||
template <class U, class C = detail::default_copy<U>,
|
||||
class D = detail::default_delete<U>,
|
||||
class V = std::enable_if_t<std::is_convertible<U*, T*>::value>>
|
||||
explicit poly(U* u, C copier = C{}, D deleter = D{})
|
||||
{
|
||||
if (!u)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::is_same<D, detail::default_delete<U>>::value &&
|
||||
std::is_same<C, detail::default_copy<U>>::value &&
|
||||
typeid(*u) != typeid(U))
|
||||
|
||||
throw bad_poly_construction();
|
||||
|
||||
std::unique_ptr<U, D> p(u, std::move(deleter));
|
||||
|
||||
cb_ = std::make_unique<detail::pointer_control_block<T, U, C, D>>(
|
||||
std::move(p), std::move(copier));
|
||||
ptr_ = u;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Copy-constructors
|
||||
//
|
||||
|
||||
poly(const poly& p)
|
||||
{
|
||||
if (!p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto tmp_cb = p.cb_->clone();
|
||||
ptr_ = tmp_cb->ptr();
|
||||
cb_ = std::move(tmp_cb);
|
||||
}
|
||||
|
||||
//
|
||||
// Move-constructors
|
||||
//
|
||||
|
||||
poly(poly&& p) noexcept
|
||||
{
|
||||
ptr_ = p.ptr_;
|
||||
cb_ = std::move(p.cb_);
|
||||
p.ptr_ = nullptr;
|
||||
}
|
||||
|
||||
//
|
||||
// Converting constructors
|
||||
//
|
||||
|
||||
template <class U,
|
||||
class V = std::enable_if_t<!std::is_same<T, U>::value &&
|
||||
std::is_convertible<U*, T*>::value>>
|
||||
poly(const poly<U>& p)
|
||||
{
|
||||
poly<U> tmp(p);
|
||||
ptr_ = tmp.ptr_;
|
||||
cb_ = std::make_unique<detail::delegating_control_block<T, U>>(
|
||||
std::move(tmp.cb_));
|
||||
}
|
||||
|
||||
template <class U,
|
||||
class V = std::enable_if_t<!std::is_same<T, U>::value &&
|
||||
std::is_convertible<U*, T*>::value>>
|
||||
poly(poly<U>&& p)
|
||||
{
|
||||
ptr_ = p.ptr_;
|
||||
cb_ = std::make_unique<detail::delegating_control_block<T, U>>(
|
||||
std::move(p.cb_));
|
||||
p.ptr_ = nullptr;
|
||||
}
|
||||
|
||||
//
|
||||
// Forwarding constructor
|
||||
//
|
||||
|
||||
template <class U, class V = std::enable_if_t<
|
||||
std::is_convertible<std::decay_t<U>*, T*>::value &&
|
||||
!is_poly<std::decay_t<U>>::value>>
|
||||
poly(U&& u)
|
||||
: cb_(std::make_unique<
|
||||
detail::direct_control_block<T, std::decay_t<U>>>(
|
||||
std::forward<U>(u)))
|
||||
{
|
||||
ptr_ = cb_->ptr();
|
||||
}
|
||||
|
||||
//
|
||||
// Assignment
|
||||
//
|
||||
|
||||
poly& operator=(const poly& p)
|
||||
{
|
||||
if (std::addressof(p) == this)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (!p)
|
||||
{
|
||||
cb_.reset();
|
||||
ptr_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto tmp_cb = p.cb_->clone();
|
||||
ptr_ = tmp_cb->ptr();
|
||||
cb_ = std::move(tmp_cb);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Move-assignment
|
||||
//
|
||||
|
||||
poly& operator=(poly&& p) noexcept
|
||||
{
|
||||
if (std::addressof(p) == this)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
cb_ = std::move(p.cb_);
|
||||
ptr_ = p.ptr_;
|
||||
p.ptr_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Modifiers
|
||||
//
|
||||
|
||||
void swap(poly& p) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
swap(ptr_, p.ptr_);
|
||||
swap(cb_, p.cb_);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Observers
|
||||
//
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return (bool)cb_;
|
||||
}
|
||||
|
||||
const T* operator->() const
|
||||
{
|
||||
assert(ptr_);
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
const T& operator*() const
|
||||
{
|
||||
assert(*this);
|
||||
return *ptr_;
|
||||
}
|
||||
|
||||
T* operator->()
|
||||
{
|
||||
assert(*this);
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
T& operator*()
|
||||
{
|
||||
assert(*this);
|
||||
return *ptr_;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// poly creation
|
||||
//
|
||||
template <class T, class... Ts>
|
||||
poly<T> make_poly(Ts&&... ts)
|
||||
{
|
||||
poly<T> p;
|
||||
p.cb_ = std::make_unique<detail::direct_control_block<T, T>>(
|
||||
std::forward<Ts>(ts)...);
|
||||
p.ptr_ = p.cb_->ptr();
|
||||
return std::move(p);
|
||||
}
|
||||
template <class T, class U, class... Ts>
|
||||
poly<T> make_poly(Ts&&... ts)
|
||||
{
|
||||
poly<T> p;
|
||||
p.cb_ = std::make_unique<detail::direct_control_block<T, U>>(
|
||||
std::forward<Ts>(ts)...);
|
||||
p.ptr_ = p.cb_->ptr();
|
||||
return std::move(p);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
const T* poly_view(const poly<U>& p) {
|
||||
if (p) {
|
||||
return dynamic_cast<const T*>(&*p);
|
||||
}
|
||||
else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
T* poly_view(poly<U>& p) {
|
||||
if (p) {
|
||||
return dynamic_cast<T*>(&*p);
|
||||
}
|
||||
else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
poly<T> poly_cast(poly<U> p) {
|
||||
poly<T> ret;
|
||||
if (T* ptr = dynamic_cast<T*>(&*p)) {
|
||||
ret.cb_ = std::make_unique<detail::delegating_control_block<T, U>>(
|
||||
std::move(p.cb_));
|
||||
ret.ptr_ = ret.cb_->ptr();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // POLY_H
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef PRESULT_H
|
||||
#define PRESULT_H
|
||||
|
||||
#include "expected.h"
|
||||
#include "sourceerror.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
/// Returns from the CURRENT function, the one this macro is used in,
|
||||
/// if PResult contains an error
|
||||
#define RETURN_IF_ERR(presult) \
|
||||
do { \
|
||||
auto&& return_if_err_temp_ = (presult); \
|
||||
if (!return_if_err_temp_) { \
|
||||
return PError(return_if_err_temp_.error()); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/// Works the same as RETURN_IF_ERR but can also assign the unwrapped result
|
||||
/// to a variable if successful
|
||||
#define ASSIGN_OR_RETURN_IF_ERR(var, presult) \
|
||||
do { \
|
||||
auto&& assign_or_return_if_err_temp_ = (presult); \
|
||||
if (!assign_or_return_if_err_temp_) { \
|
||||
return PError(assign_or_return_if_err_temp_.error()); \
|
||||
} else { \
|
||||
var = *assign_or_return_if_err_temp_; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
using PError = tl::unexpected<SourceError>;
|
||||
|
||||
/// Holds either an AST node or an error
|
||||
/// The value MUST be checked before usage, e.g. using RETURN_IF_ERR
|
||||
template <typename T>
|
||||
struct PResult : tl::expected<T, SourceError> {
|
||||
using tl::expected<T, SourceError>::expected;
|
||||
|
||||
/// Implicit conversion from PResult<U> to PResult<T>
|
||||
/// if U is convertible to T
|
||||
template <typename U, class V = std::enable_if_t<std::is_convertible<std::decay_t<U>, T>::value>>
|
||||
PResult(PResult<U> presult) :
|
||||
tl::expected<T, SourceError>(presult ? PResult<T>(*presult) : PError(presult.error()))
|
||||
{}
|
||||
};
|
||||
|
||||
/// Alternative to PResult that is returned from parse* functions
|
||||
/// that only consume one token so they can fail without affecting
|
||||
/// the parser state
|
||||
template<typename T>
|
||||
struct POptResult : public PResult<T> {
|
||||
using PResult<T>::PResult;
|
||||
|
||||
std::optional<T> optional() {
|
||||
if (*this) {
|
||||
return **this;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // PRESULT_H
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "sourceerror.h"
|
||||
|
||||
|
||||
SourceError::SourceError(Span span, std::string message)
|
||||
: span(std::move(span)), message(std::move(message))
|
||||
{}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef SOURCEERROR_H
|
||||
#define SOURCEERROR_H
|
||||
|
||||
#include "token.h"
|
||||
#include <string>
|
||||
|
||||
struct SourceError {
|
||||
Span span;
|
||||
std::string message;
|
||||
|
||||
SourceError(Span span, std::string message);
|
||||
};
|
||||
|
||||
#endif // SOURCEERROR_H
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "token.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
Position::Position()
|
||||
: fileId(0), line(0), col(0), offset(0)
|
||||
{}
|
||||
|
||||
Position::Position(unsigned file, unsigned line, unsigned col, unsigned offset)
|
||||
: fileId(file), line(line), col(col), offset(offset)
|
||||
{}
|
||||
|
||||
Span::Span()
|
||||
: lo(0, 0, 0, 0), hi(0, 0, 0, 0)
|
||||
{}
|
||||
|
||||
Span::Span(Position begin, Position end)
|
||||
: lo(begin), hi(end)
|
||||
{}
|
||||
|
||||
Span::Span(Position begin)
|
||||
: Span(begin, Position(begin.fileId, begin.line, begin.col+1, begin.offset+1))
|
||||
{}
|
||||
|
||||
Span Span::to(const Span &span) const
|
||||
{
|
||||
return Span(this->lo, span.hi);
|
||||
}
|
||||
|
||||
Token::Token(TokenType type, Span span, std::string text)
|
||||
: type(type)
|
||||
, span(std::move(span))
|
||||
, text(std::move(text))
|
||||
{}
|
||||
|
||||
|
||||
const std::map<std::string, KeywordType::Type> KeywordType::keywords = {
|
||||
{"@name", NAME},
|
||||
{"@info", INFO},
|
||||
{"@header", HEADER},
|
||||
{"@directory", DIRECTORY},
|
||||
{"@library", LIBRARY},
|
||||
{"@address", ADDRESS},
|
||||
{"@component", COMPONENT},
|
||||
{"@messages", MESSAGES},
|
||||
{"@instanceName", INSTANCE_NAME},
|
||||
{"@count", COUNT},
|
||||
{"@display", DISPLAY},
|
||||
{"@pin", PIN},
|
||||
{"@tooltip", TOOLTIP},
|
||||
{"@connection", CONNECTION},
|
||||
{"@attribute", ATTRIBUTE},
|
||||
{"@source", SOURCE},
|
||||
{"@popup", POPUP},
|
||||
{"@rule", RULE},
|
||||
{"@title", TITLE},
|
||||
{"@text", TEXT},
|
||||
{"@bus", BUS},
|
||||
{"@wires", WIRES},
|
||||
{"@enumerated", ENUMERATED},
|
||||
{"@wire", WIRE},
|
||||
{"@instance", INSTANCE},
|
||||
{"@schema", SCHEMA},
|
||||
{"@position", POSITION},
|
||||
{"@size", SIZE}
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
#ifndef TOKEN_H
|
||||
#define TOKEN_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
|
||||
struct Position {
|
||||
unsigned fileId;
|
||||
unsigned line;
|
||||
unsigned col;
|
||||
unsigned offset;
|
||||
|
||||
Position();
|
||||
Position(unsigned fileId, unsigned line, unsigned col, unsigned offset);
|
||||
};
|
||||
|
||||
/** Location of an ast node inside the source **/
|
||||
struct Span {
|
||||
/** beginning (low) position **/
|
||||
Position lo;
|
||||
/** end (high) position **/
|
||||
Position hi;
|
||||
|
||||
Span();
|
||||
Span(Position lo, Position hi);
|
||||
Span(Position lo);
|
||||
|
||||
Span to(const Span& span) const;
|
||||
};
|
||||
|
||||
enum class TokenType {
|
||||
COLON,
|
||||
SEMICOLON,
|
||||
LBRACE,
|
||||
RBRACE,
|
||||
LPAREN,
|
||||
RPAREN,
|
||||
LBRACKET,
|
||||
RBRACKET,
|
||||
LT,
|
||||
GT,
|
||||
|
||||
KEYWORD,
|
||||
IDENTIFIER,
|
||||
NUMBER,
|
||||
STRING,
|
||||
|
||||
NOT,
|
||||
COMMA,
|
||||
DOT,
|
||||
|
||||
COLOR,
|
||||
|
||||
EQUALS,
|
||||
COMMENT,
|
||||
WHITESPACE,
|
||||
|
||||
END_OF_FILE
|
||||
};
|
||||
|
||||
enum class ValueType {
|
||||
// TYPES
|
||||
BOOL,
|
||||
INT,
|
||||
STRING,
|
||||
COLOR,
|
||||
WIRE,
|
||||
ADDRESS_SPACE,
|
||||
};
|
||||
|
||||
class KeywordType {
|
||||
|
||||
public:
|
||||
|
||||
enum Type {
|
||||
// KEYWORDS
|
||||
NAME,
|
||||
INFO,
|
||||
HEADER,
|
||||
DIRECTORY,
|
||||
LIBRARY,
|
||||
ADDRESS,
|
||||
COMPONENT,
|
||||
MESSAGES,
|
||||
INSTANCE_NAME,
|
||||
COUNT,
|
||||
DISPLAY,
|
||||
PIN,
|
||||
TOOLTIP,
|
||||
CONNECTION,
|
||||
ATTRIBUTE,
|
||||
SOURCE,
|
||||
POPUP,
|
||||
RULE,
|
||||
TITLE,
|
||||
TEXT,
|
||||
BUS,
|
||||
WIRES,
|
||||
ENUMERATED,
|
||||
WIRE,
|
||||
INSTANCE,
|
||||
SCHEMA,
|
||||
POSITION,
|
||||
SIZE,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
|
||||
static KeywordType::Type determineType(std::string type) {
|
||||
if(keywords.count(type) == 0) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
return keywords.at(type);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const static std::map<std::string, KeywordType::Type> keywords;
|
||||
|
||||
};
|
||||
|
||||
struct Token {
|
||||
TokenType type;
|
||||
Span span;
|
||||
std::string text;
|
||||
|
||||
Token(TokenType type, Span span, std::string text);
|
||||
};
|
||||
|
||||
#endif // TOKEN_H
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "tokenstype.h"
|
||||
|
||||
enum TokenClass {
|
||||
BUILT_IN_FUNC = 1,
|
||||
DATA_TYPE = 2,
|
||||
KEYWORD_NAME = 4
|
||||
};
|
||||
|
||||
|
||||
struct TokenInfo {
|
||||
std::string text;
|
||||
unsigned short attributes;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct TokenTables {
|
||||
std::unordered_map<TokenType, TokenInfo> allTokensInfo;
|
||||
std::unordered_map<std::string, TokenType> keywords;
|
||||
|
||||
void add(TokenType tokenType, const std::string& txt,
|
||||
unsigned short attribs = 0);
|
||||
|
||||
TokenTables();
|
||||
};
|
||||
|
||||
|
||||
static TokenTables tokenTables;
|
||||
|
||||
void TokenTables::add (TokenType tokenType, const std::string& txt,
|
||||
unsigned short attribs)
|
||||
{
|
||||
allTokensInfo[tokenType] = TokenInfo{txt, attribs};
|
||||
if(attribs & KEYWORD_NAME)
|
||||
keywords[txt] = tokenType;
|
||||
}
|
||||
|
||||
TokenTables::TokenTables() {
|
||||
|
||||
add( TokenType::IDENTIFIER, "identifier" );
|
||||
add( TokenType::KEYWORD, "keyword" );
|
||||
|
||||
// Literals (bool is not here, it has two keywords: false and true)
|
||||
add( TokenType::NUMBER, "number" );
|
||||
add( TokenType::STRING, "string" );
|
||||
add( TokenType::COLOR, "color" );
|
||||
|
||||
// Parentheses of all kinds
|
||||
add( TokenType::LPAREN, "(" );
|
||||
add( TokenType::RPAREN, ")" );
|
||||
add( TokenType::LBRACKET, "[" );
|
||||
add( TokenType::RBRACKET, "]" );
|
||||
add( TokenType::LBRACE, "{" );
|
||||
add( TokenType::RBRACE, "}" );
|
||||
add( TokenType::LT, "<" );
|
||||
add( TokenType::GT, ">" );
|
||||
|
||||
// assignments and resizes
|
||||
add( TokenType::EQUALS, "=" );
|
||||
|
||||
// miscellaneous
|
||||
add( TokenType::DOT, "." );
|
||||
add( TokenType::COLON, ":" );
|
||||
add( TokenType::SEMICOLON, ";" );
|
||||
add( TokenType::COMMA, "," );
|
||||
add( TokenType::WHITESPACE, "whitespace" );
|
||||
add( TokenType::COMMENT, "comment" );
|
||||
|
||||
|
||||
// Built-in functions (they are also keywords)
|
||||
/*
|
||||
* TODO
|
||||
*/
|
||||
// Miscelaneous
|
||||
add( TokenType::END_OF_FILE, "eof" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
const std::string &tokenTypeToString(TokenType tokenType) {
|
||||
return tokenTables.allTokensInfo[tokenType].text;
|
||||
}
|
||||
|
||||
bool isBuiltInFunc(TokenType tokenType)
|
||||
{
|
||||
return tokenTables.allTokensInfo[tokenType].attributes & BUILT_IN_FUNC;
|
||||
}
|
||||
|
||||
bool isKeyword(TokenType tokenType)
|
||||
{
|
||||
return tokenTables.allTokensInfo[tokenType].attributes & KEYWORD_NAME;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef TOKENSTYPE_H
|
||||
#define TOKENSTYPE_H
|
||||
|
||||
#include "token.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
const std::string& tokenTypeToString(TokenType tokenType);
|
||||
|
||||
bool isBuiltInFunc(TokenType tokenType);
|
||||
bool isDataType(TokenType tokenType);
|
||||
|
||||
|
||||
#endif // TOKENSTYPE_H
|
||||
Reference in new issue
Block a user