Add p_set_position()
This commit is contained in:
parent
fb4288d5b9
commit
c0016f6d58
@ -7,6 +7,9 @@
|
|||||||
given start rule, but instead of expecting the rest of the input to match
|
given start rule, but instead of expecting the rest of the input to match
|
||||||
the start rule they allow specifying a set of tokens that may follow the
|
the start rule they allow specifying a set of tokens that may follow the
|
||||||
start rule.
|
start rule.
|
||||||
|
- Add `p_set_position()` API to set the current text position stored in the
|
||||||
|
context. Useful for setting the initial text position to something other
|
||||||
|
than `(1, 1)` for a nested parse operation.
|
||||||
|
|
||||||
## v4.7.0
|
## v4.7.0
|
||||||
|
|
||||||
|
|||||||
@ -1346,6 +1346,23 @@ size_t <%= @grammar.prefix %>parse_inner_<%= start_rule %>(<%= @grammar.prefix %
|
|||||||
return context->text_position;
|
return context->text_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current text input position.
|
||||||
|
*
|
||||||
|
* This can be used to set the initial text position to something other than
|
||||||
|
* (1, 1) for a nested parse operation so that error positions reported by
|
||||||
|
* subsequent lexer/parser calls are relative to a larger enclosing document.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* Lexer/parser context structure.
|
||||||
|
* @param position
|
||||||
|
* Text position to set.
|
||||||
|
*/
|
||||||
|
void <%= @grammar.prefix %>set_position(<%= @grammar.prefix %>context_t * context, <%= @grammar.prefix %>position_t position)
|
||||||
|
{
|
||||||
|
context->text_position = position;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the user terminate code.
|
* Get the user terminate code.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -1425,6 +1425,23 @@ public <%= @grammar.prefix %>position_t <%= @grammar.prefix %>position(<%= @gram
|
|||||||
return context.text_position;
|
return context.text_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current text input position.
|
||||||
|
*
|
||||||
|
* This can be used to set the initial text position to something other than
|
||||||
|
* (1, 1) for a nested parse operation so that error positions reported by
|
||||||
|
* subsequent lexer/parser calls are relative to a larger enclosing document.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* Lexer/parser context structure.
|
||||||
|
* @param position
|
||||||
|
* Text position to set.
|
||||||
|
*/
|
||||||
|
public void <%= @grammar.prefix %>set_position(<%= @grammar.prefix %>context_t * context, <%= @grammar.prefix %>position_t position)
|
||||||
|
{
|
||||||
|
context.text_position = position;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the user terminate code.
|
* Get the user terminate code.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -237,6 +237,8 @@ void <%= @grammar.prefix %>tree_delete_<%= start_rule %>(<%= @grammar.tree_prefi
|
|||||||
|
|
||||||
<%= @grammar.prefix %>position_t <%= @grammar.prefix %>position(<%= @grammar.prefix %>context_t * context);
|
<%= @grammar.prefix %>position_t <%= @grammar.prefix %>position(<%= @grammar.prefix %>context_t * context);
|
||||||
|
|
||||||
|
void <%= @grammar.prefix %>set_position(<%= @grammar.prefix %>context_t * context, <%= @grammar.prefix %>position_t position);
|
||||||
|
|
||||||
size_t <%= @grammar.prefix %>user_terminate_code(<%= @grammar.prefix %>context_t * context);
|
size_t <%= @grammar.prefix %>user_terminate_code(<%= @grammar.prefix %>context_t * context);
|
||||||
|
|
||||||
<%= @grammar.prefix %>token_t <%= @grammar.prefix %>token(<%= @grammar.prefix %>context_t * context);
|
<%= @grammar.prefix %>token_t <%= @grammar.prefix %>token(<%= @grammar.prefix %>context_t * context);
|
||||||
|
|||||||
@ -1474,6 +1474,25 @@ if (p_parse(context) == P_UNEXPECTED_TOKEN)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `p_set_position`
|
||||||
|
|
||||||
|
The `p_set_position()` function sets the current text position stored in the
|
||||||
|
context.
|
||||||
|
This is useful when performing a nested parse operation over a slice of a
|
||||||
|
larger enclosing document: by setting the initial text position to something
|
||||||
|
other than the default `(1, 1)`, any token positions or error positions
|
||||||
|
subsequently reported by the lexer/parser are relative to the enclosing
|
||||||
|
document rather than the slice.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
p_context_t * context = p_context_new(input, input_length);
|
||||||
|
p_position_t start = { .row = 5, .col = 20 };
|
||||||
|
p_set_position(context, start);
|
||||||
|
p_parse(context);
|
||||||
|
```
|
||||||
|
|
||||||
### `p_user_terminate_code`
|
### `p_user_terminate_code`
|
||||||
|
|
||||||
The `p_user_terminate_code()` function can be used to retrieve the user
|
The `p_user_terminate_code()` function can be used to retrieve the user
|
||||||
|
|||||||
@ -938,6 +938,19 @@ EOF
|
|||||||
expect(results.status).to eq 0
|
expect(results.status).to eq 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "allows setting the text position via p_set_position()" do
|
||||||
|
write_grammar <<EOF
|
||||||
|
token a;
|
||||||
|
token b;
|
||||||
|
Start -> a b;
|
||||||
|
EOF
|
||||||
|
run_propane(language: language)
|
||||||
|
compile("spec/test_set_position.#{language}", language: language)
|
||||||
|
results = run_test(language: language)
|
||||||
|
expect(results.stderr).to eq ""
|
||||||
|
expect(results.status).to eq 0
|
||||||
|
end
|
||||||
|
|
||||||
it "allows creating a JSON parser" do
|
it "allows creating a JSON parser" do
|
||||||
ext = language == "cpp" ? "c" : language
|
ext = language == "cpp" ? "c" : language
|
||||||
write_grammar(File.read("spec/json_parser.#{ext}.propane"))
|
write_grammar(File.read("spec/json_parser.#{ext}.propane"))
|
||||||
|
|||||||
81
spec/test_set_position.c
Normal file
81
spec/test_set_position.c
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#include "testparser.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
/* Grammar (simple):
|
||||||
|
* token a; token b;
|
||||||
|
* Start -> a b;
|
||||||
|
*
|
||||||
|
* Verifies that p_set_position() overrides the default (1, 1) starting
|
||||||
|
* position so that lexed tokens and error positions are reported
|
||||||
|
* relative to the caller-supplied position. */
|
||||||
|
|
||||||
|
/* Baseline: without p_set_position(), positions start at (1, 1). */
|
||||||
|
{
|
||||||
|
char const * input = "ab";
|
||||||
|
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
|
p_position_t pos = p_position(context);
|
||||||
|
assert_eq(1u, (size_t)pos.row);
|
||||||
|
assert_eq(1u, (size_t)pos.col);
|
||||||
|
p_token_info_t token_info;
|
||||||
|
assert(p_lex(context, &token_info) == P_SUCCESS);
|
||||||
|
assert_eq((size_t)TOKEN_a, (size_t)token_info.token);
|
||||||
|
assert_eq(1u, (size_t)token_info.position.row);
|
||||||
|
assert_eq(1u, (size_t)token_info.position.col);
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* p_set_position() overrides the initial position; subsequent lex calls
|
||||||
|
* report token positions relative to the set position. */
|
||||||
|
{
|
||||||
|
char const * input = "ab";
|
||||||
|
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
|
p_position_t initial = {5u, 20u};
|
||||||
|
p_set_position(context, initial);
|
||||||
|
p_position_t pos = p_position(context);
|
||||||
|
assert_eq(5u, (size_t)pos.row);
|
||||||
|
assert_eq(20u, (size_t)pos.col);
|
||||||
|
p_token_info_t token_info;
|
||||||
|
assert(p_lex(context, &token_info) == P_SUCCESS);
|
||||||
|
assert_eq((size_t)TOKEN_a, (size_t)token_info.token);
|
||||||
|
assert_eq(5u, (size_t)token_info.position.row);
|
||||||
|
assert_eq(20u, (size_t)token_info.position.col);
|
||||||
|
assert(p_lex(context, &token_info) == P_SUCCESS);
|
||||||
|
assert_eq((size_t)TOKEN_b, (size_t)token_info.token);
|
||||||
|
assert_eq(5u, (size_t)token_info.position.row);
|
||||||
|
assert_eq(21u, (size_t)token_info.position.col);
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* p_set_position() before a full parse: successful parse still works and
|
||||||
|
* text_position tracking is relative to the set starting point. */
|
||||||
|
{
|
||||||
|
char const * input = "ab";
|
||||||
|
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
|
p_position_t initial = {3u, 7u};
|
||||||
|
p_set_position(context, initial);
|
||||||
|
assert(p_parse_Start(context) == P_SUCCESS);
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* p_set_position() before a parse that fails: the reported error
|
||||||
|
* position is relative to the set starting point. */
|
||||||
|
{
|
||||||
|
char const * input = "aa";
|
||||||
|
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
|
p_position_t initial = {10u, 2u};
|
||||||
|
p_set_position(context, initial);
|
||||||
|
assert(p_parse_Start(context) == P_UNEXPECTED_TOKEN);
|
||||||
|
p_position_t err_pos = p_position(context);
|
||||||
|
/* Error is at the second `a`, which is one column past the initial
|
||||||
|
* column. */
|
||||||
|
assert_eq(10u, (size_t)err_pos.row);
|
||||||
|
assert_eq(3u, (size_t)err_pos.col);
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
66
spec/test_set_position.d
Normal file
66
spec/test_set_position.d
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import testparser;
|
||||||
|
import std.stdio;
|
||||||
|
import testutils;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
/* See test_set_position.c for details. */
|
||||||
|
|
||||||
|
/* Baseline: without p_set_position(), positions start at (1, 1). */
|
||||||
|
{
|
||||||
|
string input = "ab";
|
||||||
|
p_context_t * context = p_context_new(input);
|
||||||
|
p_position_t pos = p_position(context);
|
||||||
|
assert(pos.row == 1);
|
||||||
|
assert(pos.col == 1);
|
||||||
|
p_token_info_t token_info;
|
||||||
|
assert(p_lex(context, &token_info) == P_SUCCESS);
|
||||||
|
assert(token_info.token == TOKEN_a);
|
||||||
|
assert(token_info.position.row == 1);
|
||||||
|
assert(token_info.position.col == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* p_set_position() overrides the initial position. */
|
||||||
|
{
|
||||||
|
string input = "ab";
|
||||||
|
p_context_t * context = p_context_new(input);
|
||||||
|
p_set_position(context, p_position_t(5u, 20u));
|
||||||
|
p_position_t pos = p_position(context);
|
||||||
|
assert(pos.row == 5);
|
||||||
|
assert(pos.col == 20);
|
||||||
|
p_token_info_t token_info;
|
||||||
|
assert(p_lex(context, &token_info) == P_SUCCESS);
|
||||||
|
assert(token_info.token == TOKEN_a);
|
||||||
|
assert(token_info.position.row == 5);
|
||||||
|
assert(token_info.position.col == 20);
|
||||||
|
assert(p_lex(context, &token_info) == P_SUCCESS);
|
||||||
|
assert(token_info.token == TOKEN_b);
|
||||||
|
assert(token_info.position.row == 5);
|
||||||
|
assert(token_info.position.col == 21);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* p_set_position() before a full parse still parses successfully. */
|
||||||
|
{
|
||||||
|
string input = "ab";
|
||||||
|
p_context_t * context = p_context_new(input);
|
||||||
|
p_set_position(context, p_position_t(3u, 7u));
|
||||||
|
assert(p_parse_Start(context) == P_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* p_set_position() before a parse that fails: error position is
|
||||||
|
* relative to the set starting point. */
|
||||||
|
{
|
||||||
|
string input = "aa";
|
||||||
|
p_context_t * context = p_context_new(input);
|
||||||
|
p_set_position(context, p_position_t(10u, 2u));
|
||||||
|
assert(p_parse_Start(context) == P_UNEXPECTED_TOKEN);
|
||||||
|
p_position_t err_pos = p_position(context);
|
||||||
|
assert(err_pos.row == 10);
|
||||||
|
assert(err_pos.col == 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user