diff --git a/assets/parser.c.erb b/assets/parser.c.erb index ded8ba7..dd9c179 100644 --- a/assets/parser.c.erb +++ b/assets/parser.c.erb @@ -3,6 +3,17 @@ #include #include +/************************************************************************** + * Public data + *************************************************************************/ + +/** Token names. */ +const char * <%= @grammar.prefix %>token_names[] = { +<% @grammar.tokens.each_with_index do |token, index| %> + "<%= token.name %>", +<% end %> +}; + /************************************************************************** * User code blocks *************************************************************************/ diff --git a/assets/parser.h.erb b/assets/parser.h.erb index 3b32836..1c2a95a 100644 --- a/assets/parser.h.erb +++ b/assets/parser.h.erb @@ -116,6 +116,13 @@ typedef struct size_t user_terminate_code; } <%= @grammar.prefix %>context_t; +/************************************************************************** + * Public data + *************************************************************************/ + +/** Token names. */ +extern const char * <%= @grammar.prefix %>token_names[]; + void <%= @grammar.prefix %>context_init(<%= @grammar.prefix %>context_t * context, uint8_t const * input, size_t input_length); size_t <%= @grammar.prefix %>decode_code_point(uint8_t const * input, size_t input_length, diff --git a/doc/user_guide.md b/doc/user_guide.md index cf539cb..53adf5d 100644 --- a/doc/user_guide.md +++ b/doc/user_guide.md @@ -726,6 +726,28 @@ if (p_parse(&context) == P_USER_TERMINATED) } ``` +##> Data + +### `p_token_names` + +The `p_token_names` array contains the grammar-specified token names. +It is indexed by the token ID. + +C example: + +``` +p_context_t context; +p_context_init(&context, input, input_length); +size_t result = p_parse(&context); +if (p_parse(&context) == P_UNEXPECTED_TOKEN) +{ + p_position_t error_position = p_position(&context); + fprintf(stderr, "Error: unexpected token `%s' at row %u column %u\n", + p_token_names[context.token], + error_position.row + 1, error_position.col + 1); +} +``` + #> License Propane is licensed under the terms of the MIT License: diff --git a/spec/test_error_positions.c b/spec/test_error_positions.c index b871449..15aaf01 100644 --- a/spec/test_error_positions.c +++ b/spec/test_error_positions.c @@ -35,5 +35,8 @@ int main() assert(p_position(&context).row == 5); assert(p_position(&context).col == 4); + assert(strcmp(p_token_names[TOKEN_a], "a") == 0); + assert(strcmp(p_token_names[TOKEN_num], "num") == 0); + return 0; } diff --git a/spec/test_error_positions.d b/spec/test_error_positions.d index 0282252..352b799 100644 --- a/spec/test_error_positions.d +++ b/spec/test_error_positions.d @@ -34,4 +34,7 @@ unittest p_context_init(&context, input); assert(p_parse(&context) == P_DECODE_ERROR); assert(p_position(&context) == p_position_t(5, 4)); + + assert(p_token_names[TOKEN_a] == "a"); + assert(p_token_names[TOKEN_num] == "num"); }