✨ comments and line on error messages.
parent
8959950c2c
commit
eb95032dfa
|
@ -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()
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace muz
|
|||
{
|
||||
m_source = source;
|
||||
m_cursor = 0;
|
||||
m_line = 1;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Node>> Lexer::all()
|
||||
|
@ -44,12 +45,20 @@ 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] == '#')
|
||||
{
|
||||
while (m_cursor < m_source.size()
|
||||
&& m_source[m_cursor] != '\n')
|
||||
{
|
||||
m_cursor++;
|
||||
}
|
||||
|
||||
skip_spaces();
|
||||
}
|
||||
|
||||
// check word
|
||||
auto tok_info = next_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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 }
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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 <"
|
||||
|
||||
format_error<syntax_error>(current_line(),
|
||||
"unexpected end: expected <"
|
||||
+ ty_desired
|
||||
+ ">, got nothing."};
|
||||
+ ">, 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 <"
|
||||
|
||||
format_error<syntax_error>(current_line(),
|
||||
"expected <"
|
||||
+ ty_desired
|
||||
+ ">, got <"
|
||||
+ ty_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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue