Request: Add an example of how to use this library to rewrite the AST?
I think the following would make a good example:
Create an example function which traverses the SyntaxTree, and renames all signals it encounters to be suffixed with "_visited".
It could even be written in the style of a unit test, with the following input and output:
Example Input
module RefModule (
input in1,
input in2,
output logic out
);
assign out = in1 & ~in2;
endmodule
Example Output
module RefModule (
input in1_visited,
input in2_visited,
output logic out_visited
);
assign out_visited = in1_visited & ~in2_visited;
endmodule
Additional context I'd be happy to help finish this in a PR if you can just give a crude example of how this is accomplished.
Unfortunately, rewriting AST is difficult in the current implementation.
This is because all node is based on Locate including only positional information in the source code.
#[derive(Copy, Clone, Default, Debug, PartialEq)]
pub struct Locate {
pub offset: usize,
pub line: u32,
pub len: usize,
}
So modifying output code can be achieved like below, but I think it is different with modifying AST.
let mut idents = Vec::new();
// Gather identifier
for node in &syntax_tree {
match node {
RefNode::Identifier(x) => {
let locate = unwrap_node!(x, Locate).unwrap();
if let RefNode::Locate(x) = locate {
idents.push(x);
}
}
_ => (),
}
}
// Output renamed code
for node in &syntax_tree {
match node {
RefNode::Locate(x) => {
let text = if idents.iter().any(|y| *y == x) {
format!("{}_visited", syntax_tree.get_str(x).unwrap().to_string())
} else {
syntax_tree.get_str(x).unwrap().to_string()
};
print!("{text}");
}
_ => (),
}
}
Any advice on how to create a system that would modify/add/remove syntax nodes, built on top of sv-parser's syntax definitions? I guess it would live in a separate crate. Perhaps one day it would be worthy to be merged into this project
I have no idea about building new modifiable syntax tree crate on sv-parser. Copying all syntax definition may be required.
Instead of it, extending sv-parser may be reasonable.
For example, text field to Locate can be added.
The StrId is a unique ID to point the actual String.
The String is stored HashMap on thread local storage.
#[derive(Copy, Clone, Default, Debug, PartialEq)]
pub struct Locate {
pub offset: usize,
pub line: u32,
pub len: usize,
pub text: StrId,
}
And Locate generation code is probably like below:
https://github.com/dalance/sv-parser/blob/master/sv-parser-parser/src/utils.rs
pub(crate) fn into_locate(s: Span) -> Locate {
let text = string_table.insert(s.fragment()); // Insert &str to string_table, and return StrId
Locate {
offset: s.location_offset(),
line: s.location_line(),
len: s.fragment().len(),
text,
}
}
The actual example of such string table is below:
https://github.com/veryl-lang/veryl/blob/master/crates/parser/src/resource_table.rs