Rust: add Drop impl for p_context_t
This commit is contained in:
parent
5afb3599f9
commit
450c2f1cff
@ -297,32 +297,36 @@ pub fn <%= @grammar.prefix %>context_new(input: &[u8]) -> <%= @grammar.prefix %>
|
|||||||
context
|
context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<% free_token_node_used = @grammar.tree && @grammar.free_token_node != "" %>
|
||||||
|
<% if free_token_node_used %>
|
||||||
|
impl Drop for <%= @grammar.prefix %>context_t {
|
||||||
|
/* Run the free_token_node user code block for every token node in the tree. */
|
||||||
|
fn drop(&mut self) {
|
||||||
|
/* Named so that ${context.<field>} expansions resolve here. */
|
||||||
|
let context = self;
|
||||||
|
for i in 0..context.<%= @grammar.prefix %>tree_nodes.len() {
|
||||||
|
if context.<%= @grammar.prefix %>tree_nodes[i].is_token {
|
||||||
|
let token_node_id = i;
|
||||||
|
<%= expand_code(@grammar.free_token_node, false, nil, nil).gsub(/\btoken_tree_node\b/, "context.#{@grammar.prefix}tree_nodes[token_node_id]") %>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<% end %>
|
||||||
/**
|
/**
|
||||||
* Deinitialize and deallocate lexer/parser context structure.
|
* Deinitialize and deallocate lexer/parser context structure.
|
||||||
*
|
*
|
||||||
|
* The memory owned by the context is released when the context is dropped, so
|
||||||
|
* this function only has to consume it. It is provided for symmetry with
|
||||||
|
* <%= @grammar.prefix %>context_new() and with the other target languages;
|
||||||
|
* letting the context go out of scope has the same effect.
|
||||||
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* Lexer/parser context structure.
|
* Lexer/parser context structure.
|
||||||
*/
|
*/
|
||||||
<% free_token_node_used = @grammar.tree && @grammar.free_token_node != "" %>
|
|
||||||
<% if free_token_node_used %>
|
|
||||||
/* The context is taken as mutable for the benefit of the free_token_node user
|
|
||||||
* code block below, which is permitted but not required to modify the node it
|
|
||||||
* is freeing. */
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
<% else %>
|
|
||||||
/* Without a free_token_node code block this function has nothing to do. The
|
|
||||||
* context is consumed and the memory it owns is released when it is dropped. */
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
<% end %>
|
pub fn <%= @grammar.prefix %>context_delete(context: <%= @grammar.prefix %>context_t) {
|
||||||
pub fn <%= @grammar.prefix %>context_delete(<%= free_token_node_used ? "mut " : "" %>context: <%= @grammar.prefix %>context_t) {
|
|
||||||
<% if free_token_node_used %>
|
|
||||||
for i in 0..context.<%= @grammar.prefix %>tree_nodes.len() {
|
|
||||||
if context.<%= @grammar.prefix %>tree_nodes[i].is_token {
|
|
||||||
let token_node_id = i;
|
|
||||||
<%= expand_code(@grammar.free_token_node, false, nil, nil).gsub(/\btoken_tree_node\b/, "context.#{@grammar.prefix}tree_nodes[token_node_id]") %>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
<% end %>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
|
|||||||
@ -530,15 +530,16 @@ Start -> a:a b:b;
|
|||||||
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.
|
||||||
|
|
||||||
The code block is emitted for the Rust target, where it runs from
|
The code block is emitted for the Rust target, where it is run from a `Drop`
|
||||||
`p_context_delete()`.
|
implementation generated for `p_context_t`.
|
||||||
|
It therefore runs exactly once however the context is disposed of, whether that
|
||||||
|
is by calling `p_context_delete()` or by simply letting the context go out of
|
||||||
|
scope.
|
||||||
A `ptype` or token user field which owns its memory (a `String`, a `Vec`, a
|
A `ptype` or token user field which owns its memory (a `String`, a `Vec`, a
|
||||||
`Box`, and so on) is released when the context is dropped and does not need a
|
`Box`, and so on) is released when the context is dropped and does not need a
|
||||||
`free_token_node` code block.
|
`free_token_node` code block.
|
||||||
The statement is only needed for memory which Rust does not track, such as a
|
The statement is only needed for memory which Rust does not track, such as a
|
||||||
raw pointer obtained from `Box::into_raw()`.
|
raw pointer obtained from `Box::into_raw()`.
|
||||||
Note that the generated `p_context_t` does not implement `Drop`, so a
|
|
||||||
`free_token_node` code block only runs if `p_context_delete()` is called.
|
|
||||||
|
|
||||||
##> `lex_fn` statement - specifying a custom lexer function
|
##> `lex_fn` statement - specifying a custom lexer function
|
||||||
|
|
||||||
@ -1691,9 +1692,9 @@ provide a code block which frees that memory; if specified, the
|
|||||||
|
|
||||||
For Rust targets, `p_context_delete()` takes the context by value and consumes
|
For Rust targets, `p_context_delete()` takes the context by value and consumes
|
||||||
it.
|
it.
|
||||||
The memory owned by the context is released when the context is dropped, so the
|
Everything the context owns is released when it is dropped, including running
|
||||||
call is only strictly required when the grammar supplies a `free_token_node`
|
any `free_token_node` code block, so calling this function is optional for a
|
||||||
code block, which runs from `p_context_delete()`.
|
Rust target; letting the context go out of scope has the same effect.
|
||||||
|
|
||||||
Rust example:
|
Rust example:
|
||||||
|
|
||||||
|
|||||||
@ -10,8 +10,18 @@ fn main() {
|
|||||||
assert!(start.b().valid());
|
assert!(start.b().valid());
|
||||||
assert_eq!(2, unsafe { *start.b().pvalue() });
|
assert_eq!(2, unsafe { *start.b().pvalue() });
|
||||||
|
|
||||||
/* The free_token_node code block runs from p_context_delete(), not before. */
|
/* The free_token_node code block runs when the context is disposed of, not
|
||||||
|
* before, and frees each of the two token nodes exactly once. */
|
||||||
assert_eq!(0, FREED.load(Ordering::SeqCst));
|
assert_eq!(0, FREED.load(Ordering::SeqCst));
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
assert_eq!(2, FREED.load(Ordering::SeqCst));
|
assert_eq!(2, FREED.load(Ordering::SeqCst));
|
||||||
|
|
||||||
|
/* Letting the context go out of scope runs the code block too, so a caller
|
||||||
|
* which never calls p_context_delete() does not leak. */
|
||||||
|
{
|
||||||
|
let mut context = p_context_new(b"ab");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
assert_eq!(2, FREED.load(Ordering::SeqCst));
|
||||||
|
}
|
||||||
|
assert_eq!(4, FREED.load(Ordering::SeqCst));
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user