diff --git a/assets/parser.d.erb b/assets/parser.d.erb index 99217ad..42ccf65 100644 --- a/assets/parser.d.erb +++ b/assets/parser.d.erb @@ -94,6 +94,14 @@ public <%= @grammar.prefix %>value_t <%= @grammar.prefix %>value<%= name == "def return <%= @grammar.prefix %>value_t(v_<%= name %>: v); } <% end %> + +/** Parser value accessor(s). */ +<% @grammar.ptypes.each do |name, typestring| %> +public <%= typestring %> <%= @grammar.prefix %>value_get<%= name == "default" ? "" : "_#{name}" %>(<%= @grammar.prefix %>value_t * pvalue) +{ + return pvalue.v_<%= name %>; +} +<% end %> <% end %> <% if @grammar.tree %> diff --git a/assets/parser.h.erb b/assets/parser.h.erb index 32f3139..7e3100b 100644 --- a/assets/parser.h.erb +++ b/assets/parser.h.erb @@ -77,6 +77,14 @@ static inline <%= @grammar.prefix %>value_t <%= @grammar.prefix %>value<%= name return (<%= @grammar.prefix %>value_t){.v_<%= name %> = v}; } <% end %> + +/** Parser value accessor(s). */ +<% @grammar.ptypes.each do |name, typestring| %> +static inline <%= typestring %> <%= @grammar.prefix %>value_get<%= name == "default" ? "" : "_#{name}" %>(<%= @grammar.prefix %>value_t const * pvalue) +{ + return pvalue->v_<%= name %>; +} +<% end %> <% end %> <% if @grammar.tree %> diff --git a/doc/user_guide.md b/doc/user_guide.md index fe4bf3a..70c1dc3 100644 --- a/doc/user_guide.md +++ b/doc/user_guide.md @@ -1082,6 +1082,9 @@ The `p_token_info_t` structure contains the following fields: * `length` (`size_t`) holds the number of input bytes used by the token. * `token` (`p_token_t`) holds the token ID of the lexed token * `pvalue` (`p_value_t`) holds the parser value associated with the token. + The actual user value can be extracted with `p_value_get(&token_info.pvalue)` + for the default value or `p_value_get_XXX(&token_info.pvalue)` for named + `ptype` values. ### Tree Node Types @@ -1404,18 +1407,35 @@ In this case, Propane will free a `Statement` tree structure returned by the ### `p_value` -When tree generation mode is not active, the `p_value_t` union can hold one of -several different possible value types. - -The `p_value(v)` function returns an instance of the `p_value_t` with the +The `p_value(v)` function builds an instance of the `p_value_t` with the default member set to the value of `v`. -A `p_value_XXX(v)` function is set for each user-defined `ptype` name with +A `p_value_XXX(v)` function is defined for each user-defined `ptype` name with the user-given name in place of the `XXX`. These functions are useful for custom lexer functions which need to return a parser value corresponding to a lexed token. +They are especially useful when tree generation mode is not active. +In that case, the `p_value_t` union can hold one of several different possible +value types. + +### `p_value_get` + +The `p_value_get()` accessor functions can be used to extract a user value +from a `p_value_t` union. + +The `p_value_get(pvalue)` function accepts a pointer to a `p_value_t` and +returns the value of its default member. + +A `p_value_get_XXX(pvalue)` function is generated for each user-defined `ptype` +name with the user-given name in place of the `XXX`, returning the value of the +corresponding member. + +These functions are the counterpart to `p_value()` constructor functions and +are useful for reading the parser value associated with a lexed token, for +example `p_value_get(&token_info.pvalue)`. + ##> Data ### `p_token_names` diff --git a/spec/propane_spec.rb b/spec/propane_spec.rb index 384d8d6..c068e9b 100644 --- a/spec/propane_spec.rb +++ b/spec/propane_spec.rb @@ -1781,6 +1781,39 @@ EOF results = run_test(language: language) expect(results.status).to eq 0 end + + it "provides accessor functions to extract user values from a parser value" do + if language == "d" + write_grammar <> +token flt (float) /f/ << $$ = 1.5; >> +token str (string) /s/ << $$ = "hello"; >> +Start -> num flt str; +EOF + else + write_grammar <> +token flt (float) /f/ << $$ = 1.5; >> +token str (string) /s/ << $$ = (char *)"hello"; >> +Start -> num flt str; +EOF + end + run_propane(language: language) + compile("spec/test_value_accessors.#{language}", language: language) + results = run_test(language: language) + expect(results.stderr).to eq "" + expect(results.status).to eq 0 + end end end end diff --git a/spec/test_value_accessors.c b/spec/test_value_accessors.c new file mode 100644 index 0000000..d442774 --- /dev/null +++ b/spec/test_value_accessors.c @@ -0,0 +1,29 @@ +#include "testparser.h" +#include +#include + +int main() +{ + p_token_info_t token_info; + char const * input = "42 f s"; + p_context_t * context; + context = p_context_new((uint8_t const *)input, strlen(input)); + + /* Default ptype value extracted with p_value_get(). */ + assert(p_lex(context, &token_info) == P_SUCCESS); + assert(token_info.token == TOKEN_num); + assert(p_value_get(&token_info.pvalue) == 42); + + /* Named ptype values extracted with p_value_get_XXX(). */ + assert(p_lex(context, &token_info) == P_SUCCESS); + assert(token_info.token == TOKEN_flt); + assert(p_value_get_float(&token_info.pvalue) == 1.5); + + assert(p_lex(context, &token_info) == P_SUCCESS); + assert(token_info.token == TOKEN_str); + assert(strcmp(p_value_get_string(&token_info.pvalue), "hello") == 0); + + p_context_delete(context); + + return 0; +} diff --git a/spec/test_value_accessors.d b/spec/test_value_accessors.d new file mode 100644 index 0000000..eb4893b --- /dev/null +++ b/spec/test_value_accessors.d @@ -0,0 +1,29 @@ +import testparser; +import std.stdio; + +int main() +{ + return 0; +} + +unittest +{ + p_token_info_t token_info; + string input = "42 f s"; + p_context_t * context; + context = p_context_new(input); + + /* Default ptype value extracted with p_value_get(). */ + assert(p_lex(context, &token_info) == P_SUCCESS); + assert(token_info.token == TOKEN_num); + assert(p_value_get(&token_info.pvalue) == 42); + + /* Named ptype values extracted with p_value_get_XXX(). */ + assert(p_lex(context, &token_info) == P_SUCCESS); + assert(token_info.token == TOKEN_flt); + assert(p_value_get_float(&token_info.pvalue) == 1.5); + + assert(p_lex(context, &token_info) == P_SUCCESS); + assert(token_info.token == TOKEN_str); + assert(p_value_get_string(&token_info.pvalue) == "hello"); +}