Print test summary

This commit is contained in:
2022-06-05 23:43:45 +02:00
parent 5490f6ce1c
commit f37700a02d
47 changed files with 4362 additions and 1184 deletions

28
include/elna/history.hpp Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <boost/core/noncopyable.hpp>
#include <string>
#include <list>
namespace elna
{
struct editor_history : public boost::noncopyable
{
using const_iterator = std::list<std::string>::const_iterator;
editor_history();
void push(const std::string& entry);
void clear();
const_iterator next() noexcept;
const_iterator prev() noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
const_iterator current() const noexcept;
private:
std::list<std::string> commands;
std::list<std::string>::const_iterator current_pointer;
};
}

View File

@ -0,0 +1,89 @@
#pragma once
#include <boost/core/noncopyable.hpp>
#include <stdexcept>
#include <string>
#include <optional>
#include "elna/state.hpp"
#include "elna/history.hpp"
#define BOOST_PROCESS_USE_STD_FS
namespace elna
{
/**
* Runtime exception for non recoverable errors.
*/
struct interactive_exception : public std::runtime_error
{
/**
* Constructor.
*/
interactive_exception();
};
/**
* Main loop.
*/
void loop();
/**
* Reads the next line.
* Returns no value upon receiving end of file.
*
* \param history Shell history.
* \return The read line.
*/
std::optional<editor_state> read_line(editor_history& history);
/**
* Runs a built-in or a command.
*
* \param line The command and arguments.
* \return Whether the input shoud continued (no exit requested).
*/
bool execute(const std::string& line);
/**
* Runs the binary specified in its argument.
*
* \param program Program name in PATH.
*/
void launch(const std::string& program);
/**
* Enables the raw mode.
*
* \return Whether the operation was successful.
*/
bool enable_raw_mode();
/**
* Disables the raw mode.
*/
void disable_raw_mode();
/**
* Reads a key.
*
* \return Read character.
*/
key read_key();
/**
* Calls autocompletion.
*
* \param User input.
* \param Cursor position in the user input.
* \return Selected item.
*/
std::string complete(const std::string& input, const std::size_t position);
/**
* Prints the message from the exception.
*
* \param exception The exception to print.
*/
void print_exception(const std::exception& exception);
}

95
include/elna/ir.hpp Normal file
View File

@ -0,0 +1,95 @@
#pragma once
#include "elna/parser.hpp"
#include <cstddef>
#include <cstdint>
namespace elna::ir
{
class Node;
class Definition;
class Operand;
class BinaryExpression;
class Variable;
class VariableDeclaration;
class Number;
struct IRVisitor
{
virtual void visit(Node *) = 0;
virtual void visit(Definition *) = 0;
virtual void visit(Operand *) = 0;
virtual void visit(BinaryExpression *) = 0;
virtual void visit(Variable *) = 0;
virtual void visit(Number *) = 0;
};
/**
* AST node.
*/
class Node
{
public:
virtual void accept(IRVisitor *) = 0;
};
/**
* Definition.
*/
class Definition : public Node
{
public:
BinaryExpression **statements;
std::size_t statementsLength;
Operand *result;
virtual void accept(IRVisitor *visitor) override;
};
class Statement : public Node
{
};
class Operand : public Node
{
public:
virtual void accept(IRVisitor *visitor) override;
};
class Number : public Operand
{
public:
std::int32_t value;
virtual void accept(IRVisitor *visitor) override;
};
class Variable : public Operand
{
public:
std::size_t counter;
virtual void accept(IRVisitor *visitor) override;
};
class BinaryExpression : public Statement
{
public:
Operand *lhs, *rhs;
BinaryOperator _operator;
BinaryExpression(Operand *lhs, Operand *rhs, BinaryOperator _operator);
virtual void accept(IRVisitor *visitor) override;
};
class BangExpression : public Statement
{
Operand *operand;
public:
BangExpression(Operand *operand);
virtual void accept(IRVisitor *visitor) override;
};
}

109
include/elna/lexer.hpp Normal file
View File

@ -0,0 +1,109 @@
#pragma once
#include <string>
#include "elna/result.hpp"
namespace elna
{
/**
* Range over the source text that keeps track of the current position.
*/
struct source
{
class const_iterator
{
std::string::const_iterator m_buffer;
Position m_position;
const_iterator(std::string::const_iterator buffer,
const Position position = Position());
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = ptrdiff_t;
using value_type = char;
using pointer = const value_type *;
using reference = const value_type&;
const Position& position() const noexcept;
reference operator*() const noexcept;
pointer operator->() const noexcept;
const_iterator& operator++();
const_iterator& operator++(int);
bool operator==(const const_iterator& that) const noexcept;
bool operator!=(const const_iterator& that) const noexcept;
friend source;
};
source(const std::string& buffer);
const_iterator begin() const;
const_iterator end() const;
private:
const std::string m_buffer;
};
/**
* Union type representing a single token.
*/
struct Token
{
/**
* Token type.
*/
enum Type : std::uint16_t
{
TOKEN_NUMBER = 0,
TOKEN_OPERATOR = 1,
TOKEN_LET = 2,
TOKEN_IDENTIFIER = 3,
TOKEN_EQUALS = 4,
TOKEN_VAR = 5,
TOKEN_SEMICOLON = 6,
TOKEN_LEFT_PAREN = 7,
TOKEN_RIGHT_PAREN = 8,
TOKEN_BANG = 9,
TOKEN_DOT = 10,
TOKEN_COMMA = 11,
};
/**
* Type of the token value.
*/
union Value
{
std::int32_t number;
const char *identifier;
};
Token(Type of, Position position);
Token(Type of, std::int32_t value, Position position);
Token(Type of, const char *value, Position position);
Token(const Token& that);
Token(Token&& that);
~Token();
Token& operator=(const Token& that);
Token& operator=(Token&& that);
Type of() const noexcept;
const char *identifier() const noexcept;
std::int32_t number() const noexcept;
const Position& position() const noexcept;
private:
Type m_type;
Value m_value;
Position m_position;
};
/**
* Split the source into tokens.
*
* \return Tokens or error.
*/
Token *lex(const char *buffer, CompileError *compile_error, std::size_t *length);
}

10
include/elna/parser.hpp Normal file
View File

@ -0,0 +1,10 @@
#pragma once
namespace elna
{
enum class BinaryOperator
{
sum,
subtraction
};
}

62
include/elna/result.hpp Normal file
View File

@ -0,0 +1,62 @@
#pragma once
#include <cstddef>
#include <boost/outcome.hpp>
namespace elna
{
/**
* Position in the source text.
*/
struct Position
{
/// Line.
std::size_t line = 1;
/// Column.
std::size_t column = 1;
};
/**
* A compilation error consists of an error message and position.
*/
struct CompileError
{
private:
char const *message;
Position position;
public:
/**
* @param message Error text.
* @param position Error position in the source text.
*/
CompileError(char const *message, const Position position) noexcept;
/// Error text.
const char *what() const noexcept;
/// Error line in the source text.
std::size_t line() const noexcept;
/// Error column in the source text.
std::size_t column() const noexcept;
};
template<typename T>
using result = boost::outcome_v2::result<T, CompileError>;
enum class Target
{
text,
high20,
lower12i
};
struct Reference
{
const char* name;
size_t offset;
Target target;
};
}

148
include/elna/riscv.hpp Normal file
View File

@ -0,0 +1,148 @@
#pragma once
#include <cstdint>
#include "elna/ir.hpp"
#include "elna/result.hpp"
namespace elna
{
enum class XRegister : std::uint8_t
{
zero = 0,
ra = 1,
sp = 2,
gp = 3,
tp = 4,
t0 = 5,
t1 = 6,
t2 = 7,
s0 = 8,
s1 = 9,
a0 = 10,
a1 = 11,
a2 = 12,
a3 = 13,
a4 = 14,
a5 = 15,
a6 = 16,
a7 = 17,
s2 = 18,
s3 = 19,
s4 = 20,
s5 = 21,
s6 = 22,
s7 = 23,
s8 = 24,
s9 = 25,
s10 = 26,
s11 = 27,
t3 = 28,
t4 = 29,
t5 = 30,
t6 = 31,
};
enum class Funct3 : std::uint8_t
{
addi = 0b000,
slti = 0b001,
sltiu = 0b011,
andi = 0b111,
ori = 0b110,
xori = 0b100,
slli = 0b000,
srli = 0b101,
srai = 0b101,
add = 0b000,
slt = 0b010,
sltu = 0b011,
_and = 0b111,
_or = 0b110,
_xor = 0b100,
sll = 0b001,
srl = 0b101,
sub = 0b000,
sra = 0b101,
beq = 0b000,
bne = 0b001,
blt = 0b100,
bltu = 0b110,
bge = 0b101,
bgeu = 0b111,
fence = 0b000,
fenceI = 0b001,
csrrw = 0b001,
csrrs = 0b010,
csrrc = 0b011,
csrrwi = 0b101,
csrrsi = 0b110,
csrrci = 0b111,
priv = 0b000,
sb = 0b000,
sh = 0b001,
sw = 0b010,
lb = 0b000,
lh = 0b001,
lw = 0b010,
lbu = 0b100,
lhu = 0b101,
jalr = 0b000,
};
enum class Funct12 : std::uint8_t
{
ecall = 0b000000000000,
ebreak = 0b000000000001,
};
enum class Funct7 : std::uint8_t
{
none = 0,
sub = 0b0100000
};
enum class BaseOpcode : std::uint8_t
{
opImm = 0b0010011,
lui = 0b0110111,
auipc = 0b0010111,
op = 0b0110011,
jal = 0b1101111,
jalr = 0b1100111,
branch = 0b1100011,
load = 0b0000011,
store = 0b0100011,
miscMem = 0b0001111,
system = 0b1110011,
};
struct Instruction
{
Instruction(BaseOpcode opcode);
Instruction& i(XRegister rd, Funct3 funct3, XRegister rs1, std::uint32_t immediate);
Instruction& s(std::uint32_t imm1, Funct3 funct3, XRegister rs1, XRegister rs2);
Instruction& r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7 = Funct7::none);
Instruction& u(XRegister rd, std::uint32_t imm);
std::uint8_t *encode();
private:
std::uint32_t instruction{ 0 };
};
class RiscVVisitor : public ir::IRVisitor
{
Instruction *instructions;
std::size_t instructionsLength;
bool registerInUse;
std::uint32_t variableCounter = 1;
Reference references[3];
virtual void visit(ir::Node *) override;
virtual void visit(ir::Definition *definition) override;
virtual void visit(ir::Operand *operand) override;
virtual void visit(ir::Variable *variable) override;
virtual void visit(ir::Number *number) override;
virtual void visit(ir::BinaryExpression *expression) override;
};
}

170
include/elna/state.hpp Normal file
View File

@ -0,0 +1,170 @@
#include <cstdint>
#include <cstring>
#include <string>
#include <variant>
namespace elna
{
constexpr const char *erase_line = "\x1b[2K";
constexpr const char *start_kitty_keybaord = "\x1b[>1u";
constexpr const char *end_kitty_keybaord = "\x1b[<u";
std::string cursor_to_column(const std::uint16_t position = 1);
enum class special_key
{
arrow_left,
arrow_right,
arrow_up,
arrow_down,
page_up,
page_down,
home_key,
end_key,
};
/**
* Key modifiers.
*/
enum class modifier : std::uint8_t
{
shift = 0b1, ///< 1
alt = 0b10, ///< 2
ctrl = 0b100, ///< 4
super = 0b1000, ///< 8
hyper = 0b10000, ///< 16
meta = 0b100000, ///< 32
caps_lock = 0b1000000, ///< 64
num_lock = 0b10000000, ///< 128
};
struct key
{
using char_t = std::uint32_t;
key();
explicit key(const char_t c, const std::uint8_t modifiers = 0);
explicit key(const char_t c, const modifier modifiers);
explicit key(const special_key control, const std::uint8_t modifiers = 0);
explicit key(const special_key control, const modifier modifiers);
bool operator==(const char_t c) const;
friend bool operator==(const char_t c, const key& that);
bool operator==(const special_key control) const;
friend bool operator==(const special_key control, const key& that);
bool operator==(const key& that) const;
bool operator!=(const char_t c) const;
friend bool operator!=(const char_t c, const key& that);
bool operator!=(const special_key control) const;
friend bool operator!=(const special_key control, const key& that);
bool operator!=(const key& that) const;
char_t character() const;
special_key control() const;
bool modified(const std::uint8_t modifiers) const noexcept;
bool modified(const modifier modifiers) const noexcept;
bool is_character() const noexcept;
bool is_control() const noexcept;
bool empty() const noexcept;
private:
std::variant<special_key, char_t> store;
std::uint8_t modifiers;
};
/**
* The action should be performed after updating the editor state.
*/
enum class action
{
redraw, ///< Redraw everything.
write, ///< Write a single character.
finalize, ///< Finalize command input.
move, ///< Move the cursor.
ignore, ///< Do nothing.
complete, ///< Complete the input.
history, ///< Navigate the history.
};
/**
* The line editor with its state.
*/
class editor_state
{
std::string input;
std::string prompt = "> ";
std::size_t position{ 1 };
public:
editor_state();
/**
* Returns the current input gathered by the lin editor.
*
* \return User input.
*/
const std::string& command_line() const noexcept;
/**
* Processes the next keypress by changing the line editor state.
*
* \param key Pressed key.
* \return Action to do after the key was pressed.
*/
action process_keypress(const key& press);
/**
* Clears editors working area and writes drawing sequences into the
* buffer.
*
* \param T Output range for bytes.
* \param output Output buffer.
*/
template<typename T>
void draw(T output) const
{
std::string code;
std::copy(erase_line, erase_line + strlen(erase_line), output);
code = cursor_to_column();
std::copy(std::cbegin(code), std::cend(code), output);
std::copy(std::cbegin(this->prompt), std::cend(this->prompt), output);
std::copy(std::cbegin(this->input), std::cend(this->input), output);
code = cursor_to_column(absolute_position());
std::copy(std::cbegin(code), std::cend(code), output);
}
/**
* Computes the position in the terminal line.
*
* \return Position in the terminal line.
*/
std::size_t absolute_position() const noexcept;
/**
* Cursor position relative to the start of the user input.
*
* \returns Cursor position.
*/
std::size_t relative_position() const noexcept;
/**
* Inserts text at cursor position.
*
* \param text Text to insert.
*/
void insert(const std::string& text);
void insert(const key::char_t text);
/**
* Replaces the user input with the given string.
*
* \param text New user input string.
*/
void load(const std::string& text);
};
}

22
include/elna/tester.hpp Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <cstdint>
namespace elna
{
class test_results final
{
std::uint32_t m_total{ 0 };
std::uint32_t m_passed{ 0 };
public:
test_results() = default;
std::uint32_t total() const noexcept;
std::uint32_t passed() const noexcept;
std::uint32_t failed() const noexcept;
int exit_code() const noexcept;
void add_exit_code(const int exit_code) noexcept;
};
}