comments and line on error messages.

main
bog 2024-01-30 20:27:30 +01:00
parent 8959950c2c
commit eb95032dfa
9 changed files with 99 additions and 17 deletions

View File

@ -98,7 +98,7 @@ namespace muz
void Compiler::check_cmd_arity(Node const& node, int arity) void Compiler::check_cmd_arity(Node const& node, int arity)
{ {
if (node.size() - 1 != arity) if (node.size() - 1 != static_cast<size_t>(arity))
{ {
throw compile_error { throw compile_error {
std::string() std::string()

View File

@ -18,6 +18,7 @@ namespace muz
{ {
m_source = source; m_source = source;
m_cursor = 0; m_cursor = 0;
m_line = 1;
} }
std::vector<std::shared_ptr<Node>> Lexer::all() std::vector<std::shared_ptr<Node>> Lexer::all()
@ -44,10 +45,18 @@ namespace muz
std::shared_ptr<Node> Lexer::next() std::shared_ptr<Node> Lexer::next()
{ {
// consume spaces // consume spaces
skip_spaces();
while (m_cursor < m_source.size() while (m_cursor < m_source.size()
&& isspace(m_source[m_cursor])) && m_source[m_cursor] == '#')
{ {
m_cursor++; while (m_cursor < m_source.size()
&& m_source[m_cursor] != '\n')
{
m_cursor++;
}
skip_spaces();
} }
// check word // check word
@ -61,7 +70,7 @@ namespace muz
if (tok_info && f(tok_info->value)) if (tok_info && f(tok_info->value))
{ {
auto node = std::make_shared<Node>(type, tok_info->value); auto node = std::make_shared<Node>(type, m_line, tok_info->value);
m_cursor = tok_info->position; m_cursor = tok_info->position;
return node; return node;
} }
@ -69,17 +78,16 @@ namespace muz
return nullptr; return nullptr;
}; };
if (tok_info && tok_info->value == "[") if (tok_info && tok_info->value == "[")
{ {
auto node = std::make_shared<Node>(NODE_OSQUARE); auto node = std::make_shared<Node>(NODE_OSQUARE, m_line);
m_cursor = tok_info->position; m_cursor = tok_info->position;
return node; return node;
} }
if (tok_info && tok_info->value == "]") if (tok_info && tok_info->value == "]")
{ {
auto node = std::make_shared<Node>(NODE_CSQUARE); auto node = std::make_shared<Node>(NODE_CSQUARE, m_line);
m_cursor = tok_info->position; m_cursor = tok_info->position;
return node; return node;
} }
@ -102,9 +110,29 @@ namespace muz
return res; return res;
} }
if (m_cursor < m_source.size())
{
format_error<lexical_error>(m_line,
"unknown token <" + tok_info->value + ">");
}
return nullptr; return nullptr;
} }
void Lexer::skip_spaces()
{
while (m_cursor < m_source.size()
&& isspace(m_source[m_cursor]))
{
if (m_source[m_cursor] == '\n')
{
m_line++;
}
m_cursor++;
}
}
std::optional<TokenInfo> Lexer::next_word() std::optional<TokenInfo> Lexer::next_word()
{ {
size_t cursor = m_cursor; size_t cursor = m_cursor;

View File

@ -6,6 +6,8 @@
namespace muz namespace muz
{ {
MUZ_ERROR(lexical_error);
struct TokenInfo struct TokenInfo
{ {
size_t position; size_t position;
@ -30,8 +32,11 @@ namespace muz
private: private:
std::string m_source; std::string m_source;
size_t m_cursor = 0; size_t m_cursor = 0;
int m_line = 1;
std::vector<char> m_seps; std::vector<char> m_seps;
void skip_spaces();
std::optional<TokenInfo> next_word(); std::optional<TokenInfo> next_word();
bool is_sep(size_t index) const; bool is_sep(size_t index) const;
bool is_num(std::string const& word) const; bool is_num(std::string const& word) const;

View File

@ -3,8 +3,10 @@
namespace muz namespace muz
{ {
/*explicit*/ Node::Node(NodeType type, /*explicit*/ Node::Node(NodeType type,
int line,
std::string const& value) std::string const& value)
: m_type { type } : m_type { type }
, m_line { line }
, m_value { value } , m_value { value }
{ {
} }

View File

@ -21,12 +21,14 @@ namespace muz
{ {
public: public:
explicit Node(NodeType type, explicit Node(NodeType type,
int line,
std::string const& value=""); std::string const& value="");
virtual ~Node(); virtual ~Node();
// properties // properties
// ---------- // ----------
inline NodeType type() const { return m_type; } inline NodeType type() const { return m_type; }
inline int line() const { return m_line; }
inline std::string value() const { return m_value; } inline std::string value() const { return m_value; }
// children // children
@ -39,6 +41,7 @@ namespace muz
private: private:
NodeType m_type; NodeType m_type;
int m_line;
std::string m_value; std::string m_value;
std::vector<std::shared_ptr<Node>> m_children; std::vector<std::shared_ptr<Node>> m_children;
}; };

View File

@ -19,14 +19,26 @@ namespace muz
return parse_prog(); return parse_prog();
} }
int Parser::current_line()
{
if (m_cursor < m_tokens.size())
{
return m_tokens[m_cursor]->line();
}
return 0;
}
std::shared_ptr<Node> Parser::consume(std::optional<NodeType> type) std::shared_ptr<Node> Parser::consume(std::optional<NodeType> type)
{ {
if (m_cursor >= m_tokens.size()) if (m_cursor >= m_tokens.size())
{ {
std::string ty_desired = NodeTypeStr[*type] + strlen("NODE_"); std::string ty_desired = NodeTypeStr[*type] + strlen("NODE_");
throw syntax_error {"unexpected end: expected <"
+ ty_desired format_error<syntax_error>(current_line(),
+ ">, got nothing."}; "unexpected end: expected <"
+ ty_desired
+ ">, got nothing.");
} }
auto node = m_tokens[m_cursor]; auto node = m_tokens[m_cursor];
@ -35,10 +47,12 @@ namespace muz
{ {
std::string ty_got = NodeTypeStr[node->type()] + strlen("NODE_"); std::string ty_got = NodeTypeStr[node->type()] + strlen("NODE_");
std::string ty_desired = NodeTypeStr[*type] + strlen("NODE_"); std::string ty_desired = NodeTypeStr[*type] + strlen("NODE_");
throw syntax_error {"expected <"
+ ty_desired format_error<syntax_error>(current_line(),
+ ">, got <" "expected <"
+ ty_got + ">."}; + ty_desired
+ ">, got <"
+ ty_got + ">.");
} }
m_cursor++; m_cursor++;
@ -59,7 +73,7 @@ namespace muz
std::shared_ptr<Node> Parser::parse_prog() std::shared_ptr<Node> Parser::parse_prog()
{ {
auto node = std::make_shared<Node>(NODE_PROG); auto node = std::make_shared<Node>(NODE_PROG, current_line());
while (m_cursor < m_tokens.size()) while (m_cursor < m_tokens.size())
{ {
@ -81,7 +95,7 @@ namespace muz
std::shared_ptr<Node> Parser::parse_dir() std::shared_ptr<Node> Parser::parse_dir()
{ {
auto node = std::make_shared<Node>(NODE_DIR); auto node = std::make_shared<Node>(NODE_DIR, current_line());
node->add_child(consume(NODE_DIR_IDENT)); node->add_child(consume(NODE_DIR_IDENT));
node->add_child(parse_cmd()); node->add_child(parse_cmd());
@ -92,7 +106,7 @@ namespace muz
{ {
consume(NODE_OSQUARE); consume(NODE_OSQUARE);
auto node = std::make_shared<Node>(NODE_CMD); auto node = std::make_shared<Node>(NODE_CMD, current_line());
node->add_child(consume(NODE_IDENT)); node->add_child(consume(NODE_IDENT));
while (!next_is(NODE_CSQUARE)) while (!next_is(NODE_CSQUARE))

View File

@ -25,6 +25,7 @@ namespace muz
std::vector<std::shared_ptr<Node>> m_tokens; std::vector<std::shared_ptr<Node>> m_tokens;
size_t m_cursor = 0; size_t m_cursor = 0;
int current_line();
std::shared_ptr<Node> consume(std::optional<NodeType> type=std::nullopt); std::shared_ptr<Node> consume(std::optional<NodeType> type=std::nullopt);
NodeType peek(size_t lookahead=0) const; NodeType peek(size_t lookahead=0) const;
bool next_is(NodeType type, size_t lookahead=0) const; bool next_is(NodeType type, size_t lookahead=0) const;

View File

@ -29,5 +29,12 @@
enum Prefix {Macro(MUZ_ENUM_IDENT)}; \ enum Prefix {Macro(MUZ_ENUM_IDENT)}; \
constexpr char const* Prefix ## Str [] = {Macro(MUZ_ENUM_STRING)}; constexpr char const* Prefix ## Str [] = {Macro(MUZ_ENUM_STRING)};
template <typename T>
void format_error(int line, std::string const& what)
{
std::stringstream ss;
ss << "line " << line << ": " << what;
throw T { ss.str() };
}
#endif #endif

View File

@ -22,6 +22,11 @@ static std::string next_val(muz::Lexer& lexer)
return ""; return "";
} }
static void next_val_err(muz::Lexer& lexer)
{
REQUIRE_THROWS_AS(lexer.next(), muz::lexical_error);
}
TEST_CASE_METHOD(LexerTest, "Lexer_num") TEST_CASE_METHOD(LexerTest, "Lexer_num")
{ {
muz::Lexer lexer; muz::Lexer lexer;
@ -60,3 +65,20 @@ TEST_CASE_METHOD(LexerTest, "Lexer_commands")
REQUIRE("" == next_val(lexer)); REQUIRE("" == next_val(lexer));
} }
TEST_CASE_METHOD(LexerTest, "Lexer_unknown_sym_error")
{
muz::Lexer lexer;
lexer.scan(" § [[ \n ]");
next_val_err(lexer);
}
TEST_CASE_METHOD(LexerTest, "Lexer_comments")
{
muz::Lexer lexer;
lexer.scan(" # [[ \n ]");
REQUIRE("CSQUARE" == next_val(lexer));
REQUIRE("" == next_val(lexer));
}