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)
{
if (node.size() - 1 != arity)
if (node.size() - 1 != static_cast<size_t>(arity))
{
throw compile_error {
std::string()

View File

@ -18,6 +18,7 @@ namespace muz
{
m_source = source;
m_cursor = 0;
m_line = 1;
}
std::vector<std::shared_ptr<Node>> Lexer::all()
@ -44,10 +45,18 @@ namespace muz
std::shared_ptr<Node> Lexer::next()
{
// consume spaces
skip_spaces();
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
@ -61,7 +70,7 @@ namespace muz
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;
return node;
}
@ -69,17 +78,16 @@ namespace muz
return nullptr;
};
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;
return node;
}
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;
return node;
}
@ -102,9 +110,29 @@ namespace muz
return res;
}
if (m_cursor < m_source.size())
{
format_error<lexical_error>(m_line,
"unknown token <" + tok_info->value + ">");
}
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()
{
size_t cursor = m_cursor;

View File

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

View File

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

View File

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

View File

@ -19,14 +19,26 @@ namespace muz
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)
{
if (m_cursor >= m_tokens.size())
{
std::string ty_desired = NodeTypeStr[*type] + strlen("NODE_");
throw syntax_error {"unexpected end: expected <"
+ ty_desired
+ ">, got nothing."};
format_error<syntax_error>(current_line(),
"unexpected end: expected <"
+ ty_desired
+ ">, got nothing.");
}
auto node = m_tokens[m_cursor];
@ -35,10 +47,12 @@ namespace muz
{
std::string ty_got = NodeTypeStr[node->type()] + strlen("NODE_");
std::string ty_desired = NodeTypeStr[*type] + strlen("NODE_");
throw syntax_error {"expected <"
+ ty_desired
+ ">, got <"
+ ty_got + ">."};
format_error<syntax_error>(current_line(),
"expected <"
+ ty_desired
+ ">, got <"
+ ty_got + ">.");
}
m_cursor++;
@ -59,7 +73,7 @@ namespace muz
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())
{
@ -81,7 +95,7 @@ namespace muz
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(parse_cmd());
@ -92,7 +106,7 @@ namespace muz
{
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));
while (!next_is(NODE_CSQUARE))

View File

@ -25,6 +25,7 @@ namespace muz
std::vector<std::shared_ptr<Node>> m_tokens;
size_t m_cursor = 0;
int current_line();
std::shared_ptr<Node> consume(std::optional<NodeType> type=std::nullopt);
NodeType peek(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)}; \
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

View File

@ -22,6 +22,11 @@ static std::string next_val(muz::Lexer& lexer)
return "";
}
static void next_val_err(muz::Lexer& lexer)
{
REQUIRE_THROWS_AS(lexer.next(), muz::lexical_error);
}
TEST_CASE_METHOD(LexerTest, "Lexer_num")
{
muz::Lexer lexer;
@ -60,3 +65,20 @@ TEST_CASE_METHOD(LexerTest, "Lexer_commands")
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));
}