Support custom lex function
This commit is contained in:
parent
e992e6344a
commit
51b09a5799
@ -981,7 +981,7 @@ static size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t start
|
|||||||
{
|
{
|
||||||
if (token == INVALID_TOKEN_ID)
|
if (token == INVALID_TOKEN_ID)
|
||||||
{
|
{
|
||||||
size_t lexer_result = <%= @grammar.prefix %>lex(context, &token_info);
|
size_t lexer_result = <%= lex_fn %>(context, &token_info);
|
||||||
if (lexer_result != P_SUCCESS)
|
if (lexer_result != P_SUCCESS)
|
||||||
{
|
{
|
||||||
result = lexer_result;
|
result = lexer_result;
|
||||||
|
|||||||
@ -86,6 +86,14 @@ public union <%= @grammar.prefix %>value_t
|
|||||||
<%= typestring %> v_<%= name %>;
|
<%= typestring %> v_<%= name %>;
|
||||||
<% end %>
|
<% end %>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Parser value constructor(s). */
|
||||||
|
<% @grammar.ptypes.each do |name, typestring| %>
|
||||||
|
public <%= @grammar.prefix %>value_t <%= @grammar.prefix %>value<%= name == "default" ? "" : "_#{name}" %>(T)(T v)
|
||||||
|
{
|
||||||
|
return <%= @grammar.prefix %>value_t(v_<%= name %>: v);
|
||||||
|
}
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
@ -1037,7 +1045,7 @@ private size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t star
|
|||||||
{
|
{
|
||||||
if (token == INVALID_TOKEN_ID)
|
if (token == INVALID_TOKEN_ID)
|
||||||
{
|
{
|
||||||
size_t lexer_result = <%= @grammar.prefix %>lex(context, &token_info);
|
size_t lexer_result = <%= lex_fn %>(context, &token_info);
|
||||||
if (lexer_result != P_SUCCESS)
|
if (lexer_result != P_SUCCESS)
|
||||||
{
|
{
|
||||||
return lexer_result;
|
return lexer_result;
|
||||||
|
|||||||
@ -69,6 +69,14 @@ typedef union
|
|||||||
<%= typestring %> v_<%= name %>;
|
<%= typestring %> v_<%= name %>;
|
||||||
<% end %>
|
<% end %>
|
||||||
} <%= @grammar.prefix %>value_t;
|
} <%= @grammar.prefix %>value_t;
|
||||||
|
|
||||||
|
/** Parser value constructor(s). */
|
||||||
|
<% @grammar.ptypes.each do |name, typestring| %>
|
||||||
|
static inline <%= @grammar.prefix %>value_t <%= @grammar.prefix %>value<%= name == "default" ? "" : "_#{name}" %>(<%= typestring %> v)
|
||||||
|
{
|
||||||
|
return (<%= @grammar.prefix %>value_t){.v_<%= name %> = v};
|
||||||
|
}
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
|
|||||||
@ -316,6 +316,61 @@ free_token_node <<
|
|||||||
The `free_token_node` statement user code block is not emitted for D language
|
The `free_token_node` statement user code block is not emitted for D language
|
||||||
since D has a garbage collector.
|
since D has a garbage collector.
|
||||||
|
|
||||||
|
##> `lex_fn` statement - specifying a custom lexer function
|
||||||
|
|
||||||
|
Propane generates both a lexer and a parser.
|
||||||
|
By default, the parser uses the generated `p_lex()` function directly to
|
||||||
|
return information for the next lexed token from the input stream.
|
||||||
|
|
||||||
|
However, the user can specify a custom lex function.
|
||||||
|
This function may or may not use the Propane generated `p_lex()` function under
|
||||||
|
the hood.
|
||||||
|
For example, a token sequence could be injected or repeated from a previously
|
||||||
|
saved macro definition.
|
||||||
|
|
||||||
|
Example (C/C++):
|
||||||
|
|
||||||
|
```
|
||||||
|
<<
|
||||||
|
static size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
|
||||||
|
{
|
||||||
|
static size_t count;
|
||||||
|
size_t result = P_SUCCESS;
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
out_token_info->token = TOKEN_a;
|
||||||
|
out_token_info->pvalue = p_value(count);
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = p_lex(context, out_token_info);
|
||||||
|
if (out_token_info->token == TOKEN_c)
|
||||||
|
{
|
||||||
|
count = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
>>
|
||||||
|
|
||||||
|
lex_fn mylexfn;
|
||||||
|
```
|
||||||
|
|
||||||
|
The `lex_fn` statement takes one argument specifying the name of the custom
|
||||||
|
lexer function.
|
||||||
|
The user must supply a value for the `token` field of the `p_token_info_t`
|
||||||
|
output structure so that the parser knows what token was lexed.
|
||||||
|
|
||||||
|
Additionally, if the parser user code makes use of the token's pvalue, then
|
||||||
|
the lexer function must supply a value for the `pvalue` field of the
|
||||||
|
`p_token_info_t` structure.
|
||||||
|
The `p_value()` generated API function could be useful for specifying
|
||||||
|
parser values to associate with the lexed token when tree generation is not
|
||||||
|
enabled.
|
||||||
|
When tree generation is enabled, the `pvalue` field can be set to an instance
|
||||||
|
of whatever type the user has defined as the `ptype` type.
|
||||||
|
|
||||||
##> `module` statement - specifying the generated parser module name
|
##> `module` statement - specifying the generated parser module name
|
||||||
|
|
||||||
The `module` statement can be used to specify the module name for a generated
|
The `module` statement can be used to specify the module name for a generated
|
||||||
@ -1007,6 +1062,17 @@ For C targets, the `p_position_t` structure can be checked for validity by
|
|||||||
calling `p_position_valid(pos)` where `pos` is a `p_position_t` structure
|
calling `p_position_valid(pos)` where `pos` is a `p_position_t` structure
|
||||||
instance.
|
instance.
|
||||||
|
|
||||||
|
### `p_value_t`
|
||||||
|
|
||||||
|
If tree generation mode is enabled, the `p_value_t` type is defined to be the
|
||||||
|
type given to the `ptype` statement in the grammar file.
|
||||||
|
|
||||||
|
If tree generation mode is not enabled, there could be more than one `ptype`
|
||||||
|
given, so the `p_value_t` type is a union of all possible `ptype` types.
|
||||||
|
In this case, the API functions `p_value()` and `p_value_XXX()` for each given
|
||||||
|
`ptype` name `XXX` are generated to return `p_value_t` instances holding the
|
||||||
|
corresponding `ptype`.
|
||||||
|
|
||||||
### `p_token_info_t`
|
### `p_token_info_t`
|
||||||
|
|
||||||
The `p_token_info_t` structure contains the following fields:
|
The `p_token_info_t` structure contains the following fields:
|
||||||
@ -1336,6 +1402,20 @@ p_tree_delete_Statement(statement_tree);
|
|||||||
In this case, Propane will free a `Statement` tree structure returned by the
|
In this case, Propane will free a `Statement` tree structure returned by the
|
||||||
`p_parse_Statement(context)` function.
|
`p_parse_Statement(context)` function.
|
||||||
|
|
||||||
|
### `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
|
||||||
|
default member set to the value of `v`.
|
||||||
|
|
||||||
|
A `p_value_XXX(v)` function is set 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.
|
||||||
|
|
||||||
##> Data
|
##> Data
|
||||||
|
|
||||||
### `p_token_names`
|
### `p_token_names`
|
||||||
|
|||||||
@ -354,6 +354,14 @@ class Propane
|
|||||||
code
|
code
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Get the lex function to use.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# Lex function to use.
|
||||||
|
def lex_fn
|
||||||
|
@grammar.lex_fn || "#{@grammar.prefix}lex"
|
||||||
|
end
|
||||||
|
|
||||||
# Get the parser value type for the start rule.
|
# Get the parser value type for the start rule.
|
||||||
#
|
#
|
||||||
# @return [Array<String>]
|
# @return [Array<String>]
|
||||||
|
|||||||
@ -6,6 +6,7 @@ class Propane
|
|||||||
IDENTIFIER_REGEX = /(?:[a-zA-Z]|_[a-zA-Z0-9])[a-zA-Z_0-9]*/
|
IDENTIFIER_REGEX = /(?:[a-zA-Z]|_[a-zA-Z0-9])[a-zA-Z_0-9]*/
|
||||||
|
|
||||||
attr_reader :context_user_fields
|
attr_reader :context_user_fields
|
||||||
|
attr_reader :lex_fn
|
||||||
attr_reader :tree
|
attr_reader :tree
|
||||||
attr_reader :tree_prefix
|
attr_reader :tree_prefix
|
||||||
attr_reader :tree_suffix
|
attr_reader :tree_suffix
|
||||||
@ -69,6 +70,7 @@ class Propane
|
|||||||
elsif parse_comment_line!
|
elsif parse_comment_line!
|
||||||
elsif @modeline.nil? && parse_mode_label!
|
elsif @modeline.nil? && parse_mode_label!
|
||||||
elsif parse_context_user_fields_statement!
|
elsif parse_context_user_fields_statement!
|
||||||
|
elsif parse_lex_fn!
|
||||||
elsif parse_tree_statement!
|
elsif parse_tree_statement!
|
||||||
elsif parse_tree_prefix_statement!
|
elsif parse_tree_prefix_statement!
|
||||||
elsif parse_tree_suffix_statement!
|
elsif parse_tree_suffix_statement!
|
||||||
@ -117,6 +119,12 @@ class Propane
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def parse_lex_fn!
|
||||||
|
if md = consume!(/lex_fn\b\s*(\w+)\s*;/)
|
||||||
|
@lex_fn = md[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def parse_tree_statement!
|
def parse_tree_statement!
|
||||||
if consume!(/tree\s*;/)
|
if consume!(/tree\s*;/)
|
||||||
@tree = true
|
@tree = true
|
||||||
|
|||||||
@ -1699,6 +1699,88 @@ EOF
|
|||||||
results = run_test(language: language)
|
results = run_test(language: language)
|
||||||
expect(results.status).to eq 0
|
expect(results.status).to eq 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "allows a custom lex function" do
|
||||||
|
if language == "d"
|
||||||
|
write_grammar <<EOF
|
||||||
|
<<
|
||||||
|
private size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
|
||||||
|
{
|
||||||
|
static size_t count;
|
||||||
|
size_t result = P_SUCCESS;
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
out_token_info.token = TOKEN_a;
|
||||||
|
out_token_info.pvalue = p_value(count);
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = p_lex(context, out_token_info);
|
||||||
|
if (out_token_info.token == TOKEN_c)
|
||||||
|
{
|
||||||
|
count = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
>>
|
||||||
|
|
||||||
|
ptype size_t;
|
||||||
|
lex_fn mylexfn;
|
||||||
|
|
||||||
|
token a << $$ = 7; >>
|
||||||
|
token b << $$ = 8; >>
|
||||||
|
token c << $$ = 9; >>
|
||||||
|
Start -> << $$ = 0; >>
|
||||||
|
Start -> Start ID << $$ = ($1 << 4) | $2; >>
|
||||||
|
ID -> a << $$ = $1; >>
|
||||||
|
ID -> b << $$ = $1; >>
|
||||||
|
ID -> c << $$ = $1; >>
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
write_grammar <<EOF
|
||||||
|
<<
|
||||||
|
static size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
|
||||||
|
{
|
||||||
|
static size_t count;
|
||||||
|
size_t result = P_SUCCESS;
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
out_token_info->token = TOKEN_a;
|
||||||
|
out_token_info->pvalue = p_value(count);
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = p_lex(context, out_token_info);
|
||||||
|
if (out_token_info->token == TOKEN_c)
|
||||||
|
{
|
||||||
|
count = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
>>
|
||||||
|
|
||||||
|
ptype size_t;
|
||||||
|
lex_fn mylexfn;
|
||||||
|
|
||||||
|
token a << $$ = 7; >>
|
||||||
|
token b << $$ = 8; >>
|
||||||
|
token c << $$ = 9; >>
|
||||||
|
Start -> << $$ = 0; >>
|
||||||
|
Start -> Start ID << $$ = ($1 << 4) | $2; >>
|
||||||
|
ID -> a << $$ = $1; >>
|
||||||
|
ID -> b << $$ = $1; >>
|
||||||
|
ID -> c << $$ = $1; >>
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
run_propane(language: language)
|
||||||
|
compile("spec/test_custom_lex_fn.#{language}", language: language)
|
||||||
|
results = run_test(language: language)
|
||||||
|
expect(results.status).to eq 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
15
spec/test_custom_lex_fn.c
Normal file
15
spec/test_custom_lex_fn.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include "testparser.h"
|
||||||
|
#include "testutils.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char const * input = "cbacba";
|
||||||
|
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
|
size_t result = p_result(context);
|
||||||
|
assert_eq(0x932187932187, result);
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
18
spec/test_custom_lex_fn.d
Normal file
18
spec/test_custom_lex_fn.d
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import testparser;
|
||||||
|
import std.stdio;
|
||||||
|
import testutils;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
string input = "cbacba";
|
||||||
|
p_context_t * context = p_context_new(input);
|
||||||
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
|
size_t result = p_result(context);
|
||||||
|
assert_eq(0x932187932187, result);
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user