Include the filename in the error messages

This commit is contained in:
Eugen Wissner 2024-03-30 00:21:58 +01:00
parent e04a816024
commit 3c19bf1504
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
8 changed files with 137 additions and 51 deletions

4
TODO
View File

@ -7,14 +7,13 @@
- Move constants to the symbol table, so we can check at parse time for duplicates. - Move constants to the symbol table, so we can check at parse time for duplicates.
- Don't pass raw pointers to the visitor methods. - Don't pass raw pointers to the visitor methods.
- While loop. - While loop.
- Comparision operators. - Type checking.
- Procedures. - Procedures.
- Calculate additional stack space needed for subexpressions in the allocator - Calculate additional stack space needed for subexpressions in the allocator
visitor and not in the backend. visitor and not in the backend.
- Support immediates greater than 12 bits. - Support immediates greater than 12 bits.
- It seems instructions are correctly encoded only if the compiler is running - It seems instructions are correctly encoded only if the compiler is running
on a little endian architecture. on a little endian architecture.
- Print filename in the error message.
- Merge declaration and definition nodes. - Merge declaration and definition nodes.
# Shell # Shell
@ -33,4 +32,3 @@
## Appearance ## Appearance
- Add a bar with additional information under the prompt (edit_bar), like the hostname. - Add a bar with additional information under the prompt (edit_bar), like the hostname.
- Show current time in the prompt.

View File

@ -3,55 +3,70 @@
#include "elna/source/semantic.hpp" #include "elna/source/semantic.hpp"
#include <cstddef> #include <cstddef>
#include <fstream> #include <fstream>
#include <sstream>
#include <iostream> #include <iostream>
namespace elna::cli namespace elna::cli
{ {
char *readSource(const char *source) std::string read_source(const char *source)
{ {
const std::size_t bufferSize = 255; constexpr std::size_t buffer_size = 4096;
std::ifstream input_stream{ source }; std::ifstream input_stream{ source, std::ios::binary | std::ios::in };
std::stringstream buffer; std::string output;
buffer << input_stream.rdbuf(); if (input_stream.fail())
input_stream.close(); {
std::string contents = buffer.str(); throw std::ios_base::failure("File does not exist");
char *result = reinterpret_cast<char *>(malloc(contents.size() + 1)); }
std::copy(std::cbegin(contents), std::cend(contents), result); while (true)
result[contents.size()] = '\0'; {
const std::size_t old_size = output.size();
output.resize(old_size + buffer_size);
input_stream.read(&output[old_size], buffer_size);
return result; if (input_stream.eof())
{
output.resize(old_size + input_stream.gcount());
break;
}
else if (input_stream.fail())
{
throw std::ios_base::failure("Unable to complete reading the source file");
}
}
return output;
}
void print_error(const std::unique_ptr<source::error>& compile_error)
{
std::cerr << compile_error->path().string() << ":"
<< compile_error->line() << ':' << compile_error->column()
<< ": " << compile_error->what() << std::endl;
} }
int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file) int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file)
{ {
auto sourceText = readSource(in_file.c_str()); std::string source_text;
if (sourceText == nullptr) try
{
source_text = read_source(in_file.c_str());
}
catch (std::ios_base::failure&)
{ {
return 3; return 3;
} }
size_t tokensCount{ 0 }; size_t tokensCount{ 0 };
auto lex_result = source::lex(sourceText); auto lex_result = source::lex(source_text, in_file);
free(sourceText);
if (lex_result.has_errors()) if (lex_result.has_errors())
{ {
for (const auto& compile_error : lex_result.errors()) print_errors(lex_result.errors().cbegin(), lex_result.errors().cend());
{
std::cerr << compile_error->line() << ':' << compile_error->column()
<< ": " << compile_error->what() << std::endl;
}
return 1; return 1;
} }
source::parser parser{ std::move(lex_result.success()) }; source::parser parser{ std::move(lex_result.success()) };
auto ast = parser.parse(); auto ast = parser.parse();
if (ast == nullptr) if (ast == nullptr)
{ {
for (const auto& compile_error : parser.errors()) print_errors(parser.errors().cbegin(), parser.errors().cend());
{
std::cerr << compile_error->line() << ':' << compile_error->column()
<< ": " << compile_error->what() << std::endl;
}
return 2; return 2;
} }
auto global_scope = std::make_shared<source::symbol_table>(); auto global_scope = std::make_shared<source::symbol_table>();

View File

@ -1,9 +1,46 @@
#pragma once #pragma once
#include <algorithm>
#include <filesystem> #include <filesystem>
#include "elna/source/result.hpp"
namespace elna::cli namespace elna::cli
{ {
char *readSource(const char *source); /**
* Reads an input file and returns its contents.
*
* \param source Input file.
*
* \return File contents.
*/
std::string read_source(const char *source);
/**
* Formats and prints the given error.
*
* \param compile_error The error to print.
*/
void print_error(const std::unique_ptr<source::error>& compile_error);
/**
* Prints the given errors to the standard output.
*
* \param begin Pointer to the first error.
* \param end Pointer pass the last error.
*/
template<typename I>
void print_errors(I begin, I end)
{
std::for_each(begin, end, &print_error);
}
/**
* Compiles \a in_file and writes the generated code into \a out_file.
*
* \param in_file Input file.
* \param out_file Output file.
*
* \return Exit status.
*/
int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file); int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file);
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <filesystem>
#include <optional> #include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
@ -123,7 +124,13 @@ namespace elna::source
std::string character; std::string character;
public: public:
unexpected_character(const std::string& character, const source::position position); /**
* \param character Unexpected character.
* \param path Source file name.
* \param position Unexpected token position.
*/
unexpected_character(const std::string& character, const std::filesystem::path& path,
const source::position position);
std::string what() const override; std::string what() const override;
}; };
@ -133,14 +140,18 @@ namespace elna::source
token m_token; token m_token;
public: public:
explicit unexpected_token(const token& token); /**
* \param token Unexpected token.
* \param path Source file name.
*/
unexpected_token(const token& token, const std::filesystem::path& path);
std::string what() const override; std::string what() const override;
}; };
struct lexer struct lexer
{ {
lexer(std::vector<token>&& tokens, const position last_position); lexer(std::vector<token>&& tokens, const position last_position, const std::filesystem::path& path);
lexer(const lexer&) = delete; lexer(const lexer&) = delete;
lexer(lexer&&) = default; lexer(lexer&&) = default;
@ -217,6 +228,7 @@ namespace elna::source
std::vector<token> tokens; std::vector<token> tokens;
std::vector<token>::const_iterator iterator; std::vector<token>::const_iterator iterator;
std::list<std::unique_ptr<error>> m_errors; std::list<std::unique_ptr<error>> m_errors;
std::filesystem::path source_file;
token eof; token eof;
}; };
@ -224,7 +236,8 @@ namespace elna::source
* Splits the source text into tokens. * Splits the source text into tokens.
* *
* \param buffer Source text. * \param buffer Source text.
* \param path Source file location.
* \return Tokens or error. * \return Tokens or error.
*/ */
elna::source::result<lexer> lex(const std::string& buffer); elna::source::result<lexer> lex(const std::string& buffer, const std::filesystem::path& path);
} }

View File

@ -2,6 +2,7 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <filesystem>
#include <variant> #include <variant>
#include <list> #include <list>
#include <memory> #include <memory>
@ -30,12 +31,16 @@ namespace elna::source
class error class error
{ {
position m_position; position m_position;
std::filesystem::path m_path;
protected: protected:
/** /**
* Constructs an error.
*
* \param path Source file name.
* \param position Error position in the source text. * \param position Error position in the source text.
*/ */
error(const position position); error(const std::filesystem::path& path, const position position);
public: public:
/// Error text. /// Error text.
@ -46,6 +51,9 @@ namespace elna::source
/// Error column in the source text. /// Error column in the source text.
std::size_t column() const noexcept; std::size_t column() const noexcept;
/// Source file name.
const std::filesystem::path& path() const noexcept;
}; };
template<typename T> template<typename T>
@ -106,7 +114,14 @@ namespace elna::source
std::string name; std::string name;
public: public:
name_collision(const std::string& name, const position current, const position previous); /**
* \param name Symbol name.
* \param path Source file name.
* \param current Current symbol position.
* \param previous Position of the previously defined symbol.
*/
name_collision(const std::string& name, const std::filesystem::path& path,
const position current, const position previous);
std::string what() const override; std::string what() const override;
}; };

View File

@ -267,8 +267,9 @@ namespace elna::source
assert(false); assert(false);
} }
unexpected_character::unexpected_character(const std::string& character, const source::position position) unexpected_character::unexpected_character(const std::string& character, const std::filesystem::path& path,
: error(position), character(character) const source::position position)
: error(path, position), character(character)
{ {
} }
@ -281,8 +282,8 @@ namespace elna::source
return ss.str(); return ss.str();
} }
unexpected_token::unexpected_token(const token& token) unexpected_token::unexpected_token(const token& token, const std::filesystem::path& path)
: error(token.position()), m_token(token) : error(path, token.position()), m_token(token)
{ {
} }
@ -291,8 +292,9 @@ namespace elna::source
return "Unexpected token " + m_token.to_string(); return "Unexpected token " + m_token.to_string();
} }
lexer::lexer(std::vector<token>&& tokens, const position last_position) lexer::lexer(std::vector<token>&& tokens, const position last_position, const std::filesystem::path& path)
: tokens(std::move(tokens)), iterator(this->tokens.cbegin()), eof(token(token::type::eof, last_position)) : tokens(std::move(tokens)), iterator(this->tokens.cbegin()), eof(token(token::type::eof, last_position)),
source_file(path)
{ {
} }
@ -328,7 +330,7 @@ namespace elna::source
void lexer::add_error(const token& expected) void lexer::add_error(const token& expected)
{ {
m_errors.push_back(std::make_unique<unexpected_token>(expected)); m_errors.push_back(std::make_unique<unexpected_token>(expected, this->source_file));
} }
std::optional<std::reference_wrapper<const token>> lexer::advance(const token::type token_type) std::optional<std::reference_wrapper<const token>> lexer::advance(const token::type token_type)
@ -367,7 +369,7 @@ namespace elna::source
return m_errors; return m_errors;
} }
result<lexer> lex(const std::string& buffer) result<lexer> lex(const std::string& buffer, const std::filesystem::path& path)
{ {
std::vector<token> tokens; std::vector<token> tokens;
auto [iterator, text_end] = text_iterators(buffer); auto [iterator, text_end] = text_iterators(buffer);
@ -492,10 +494,10 @@ namespace elna::source
} }
else else
{ {
return result<lexer>(unexpected_character{ std::string{ *iterator }, iterator.position() }); return result<lexer>(unexpected_character{ std::string{ *iterator }, path, iterator.position() });
} }
++iterator; ++iterator;
} }
return result<lexer>(std::in_place, std::move(tokens), iterator.position()); return result<lexer>(std::in_place, std::move(tokens), iterator.position(), path);
} }
} }

View File

@ -2,8 +2,8 @@
namespace elna::source namespace elna::source
{ {
error::error(const position position) error::error(const std::filesystem::path& path, const position position)
: m_position(position) : m_position(position), m_path(path)
{ {
} }
@ -17,8 +17,14 @@ namespace elna::source
return this->m_position.column; return this->m_position.column;
} }
name_collision::name_collision(const std::string& name, const position current, const position previous) const std::filesystem::path& error::path() const noexcept
: error(current), name(name), previous(previous) {
return this->m_path;
}
name_collision::name_collision(const std::string& name, const std::filesystem::path& path,
const position current, const position previous)
: error(path, current), name(name), previous(previous)
{ {
} }

View File

@ -1 +1 @@
1:1: Unexpected token «identifier» tests/single_word_error.eln:1:1: Unexpected token «identifier»