Skip to content

Commit 54ce83c

Browse files
authored
Add a Node-builder API to the vhdl_syntax crate (#446)
1 parent 13d5671 commit 54ce83c

44 files changed

Lines changed: 21312 additions & 717 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
3+
// You can obtain one at http://mozilla.org/MPL/2.0/.
4+
//
5+
// Copyright (c) 2026, Lukas Scheller lukasscheller@icloud.com
6+
7+
//! This example uses the builder-API to show how to programatically create a testbench entity.
8+
//! The builder-API is feature-complete, but may not be ergonomic for common cases such as
9+
//! testbench generation.
10+
use vhdl_syntax::syntax::{
11+
builders::*, AstNode, LibraryUnitSyntax, NameDesignatorToken, NamePrefixSyntax,
12+
PrimaryUnitSyntax, SecondaryUnitSyntax,
13+
};
14+
15+
fn main() {
16+
// The builder API is used to programaticall build VHDL nodes.
17+
// Each SyntaxNode has an associated Builder Node. E.g., `EntityDeclarationSyntax` -> `EntityDeclarationBuilder`.
18+
// The constructor takes all required nodes and entities.
19+
// required keywords and nodes are auto-generated, but you may change them using `.with_*(token)` methods.
20+
21+
// An entity declaration syntax is split into multiple nodes, the most important being
22+
// the preamble ("entity foo is"), the declarations, concurrent statements and the epilogue ("end entity foo;").
23+
// Consult the EntityDeclarationSyntax struct for more information.
24+
// Of all these nodes, only the PreambleSyntax must be provided. The remaining nodes are either optional,
25+
// repeated or tokens (which will be default-initialized).
26+
// An entity as defined above will produce the VHDL code ` entity tb_foo is end ;`
27+
let mut builder = EntityDeclarationBuilder::new(EntityDeclarationPreambleBuilder::new(b"foo"));
28+
// In order to repeat the second identifier, we overwrite the default-generated `EntityDeclarationEpilogue`
29+
// and provide the name. The new epilogue is equally generated through a builder.
30+
builder = builder.with_entity_declaration_epilogue(
31+
EntityDeclarationEpilogueBuilder::new().with_identifier_token(b"foo"),
32+
);
33+
// transform the builder into syntax by calling `build()` on it
34+
let entity_declaration_node = builder.build();
35+
36+
// Names are highly complicated in VHDL and so is building them.
37+
// The ergonomics of this may change in the future, but currently this is the only way to build names.
38+
// `NameDesignatorToken::identifier` creates a token-choice wrapper from a plain identifier.
39+
let entity_name = NameBuilder::new(NamePrefixSyntax::NameDesignatorPrefix(
40+
NameDesignatorPrefixBuilder::new(NameDesignatorToken::identifier(b"foo")).build(),
41+
));
42+
43+
// Create an architecture syntax.
44+
let architecture_syntax =
45+
ArchitectureBodyBuilder::new(ArchitecturePreambleBuilder::new(b"arch", entity_name))
46+
.build();
47+
48+
// build the VHDL file with entity and architecture
49+
let file = DesignFileBuilder::new()
50+
.add_design_units(DesignUnitBuilder::new(LibraryUnitSyntax::PrimaryUnit(
51+
PrimaryUnitSyntax::EntityDeclaration(entity_declaration_node),
52+
)))
53+
.add_design_units(DesignUnitBuilder::new(LibraryUnitSyntax::SecondaryUnit(
54+
SecondaryUnitSyntax::ArchitectureBody(architecture_syntax),
55+
)))
56+
.build();
57+
58+
// The canonical output consists of tokens separated by a single space.
59+
// Formatters can be used to nicely display this to the user.
60+
assert_eq!(
61+
file.raw().to_string(),
62+
" entity foo is end foo ; architecture arch of foo is begin end ; "
63+
)
64+
}

vhdl_syntax/examples/source_refactoring.rs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ use vhdl_syntax::parser;
1717
use vhdl_syntax::syntax::node::SyntaxElement;
1818
use vhdl_syntax::syntax::rewrite::RewriteAction;
1919
use vhdl_syntax::syntax::AstNode;
20+
use vhdl_syntax::syntax::EntityDeclarationBuilder;
21+
use vhdl_syntax::syntax::EntityDeclarationEpilogueBuilder;
22+
use vhdl_syntax::syntax::EntityDeclarationPreambleBuilder;
2023
use vhdl_syntax::syntax::EntityDeclarationSyntax;
24+
use vhdl_syntax::tokens::Trivia;
25+
use vhdl_syntax::tokens::TriviaPiece;
2126

2227
fn main() {
2328
// The file to change
@@ -37,22 +42,24 @@ end foobar;
3742
"Did not expect diagnostics for correct VHDL"
3843
);
3944

40-
// The target: parse a full design file and take the entity declaration node.
41-
let (replacement_file, diagnostics) = parser::parse(
42-
"\
43-
entity no_longer_foo is
44-
end no_longer_foo;
45-
46-
",
47-
);
48-
assert!(
49-
diagnostics.is_empty(),
50-
"Did not expect diagnostics for correct VHDL"
51-
);
52-
let replacement_entity = replacement_file
53-
.design_units()
54-
.next()
55-
.expect("expected entity declaration in replacement file");
45+
// The target: build a replacement entity declaration.
46+
// In a realistic scenario, one would simply construct an entity using
47+
// `EntityDeclarationBuilder::new(EntityDeclarationPreambleBuilder::new(b"no_longer_foo"))`
48+
// and leave trivia formatting to a formatter.
49+
// This example directly formats the entity using the builder API.
50+
let replacement_entity = EntityDeclarationBuilder::new(
51+
EntityDeclarationPreambleBuilder::new(b"no_longer_foo")
52+
// Clear the default-inserted space before the `entity` token
53+
.with_entity_token_trivia(Trivia::new()),
54+
)
55+
.with_entity_declaration_epilogue(
56+
EntityDeclarationEpilogueBuilder::new()
57+
// Replace space between 'is' and 'end' with newline
58+
.with_end_token_trivia(Trivia::from([TriviaPiece::LineFeeds(1)]))
59+
.with_identifier_token(b"no_longer_foo")
60+
.with_semi_colon_token_trivia(Trivia::new()),
61+
)
62+
.build();
5663

5764
let new_file = file.raw().rewrite(|node| match node {
5865
SyntaxElement::Node(node) => match EntityDeclarationSyntax::cast(node.clone()) {

0 commit comments

Comments
 (0)