| Crates.io | ADA_Standards |
| lib.rs | ADA_Standards |
| version | 1.2.2 |
| created_at | 2025-03-07 16:33:46.263388+00 |
| updated_at | 2025-11-25 04:46:22.77636+00 |
| description | A library to help you handle checks on your ADA projects, especially good to build scripts to check coding standards conformity. |
| homepage | |
| repository | https://github.com/frontinus/ADA_Standards_Lib |
| max_upload_size | |
| id | 1583328 |
| size | 220,287 |
A powerful, lightweight regex-based Ada parser written in Rust. Extract packages, procedures, types, control flow, and more from Ada source code into a traversable Abstract Syntax Tree (AST) for analysis, linting, and coding standards enforcement.
if/elsif/else, case, loop, while, for, exit when, declare blocksindextree for efficient parent-child relationshipsArgumentData structsConditionExpr with support for:
and, or, and then, or else, xor<, >, <=, >=, =, /=in, not innotend statements to their corresponding blockscase alternatives and exit when conditions after initial parseAdd to your Cargo.toml:
[dependencies]
ADA_Standards = "1.2.2"
Or use cargo:
cargo add ADA_Standards
use ADA_Standards::{AST, ASTError};
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Read Ada source file
let code_text = fs::read_to_string("my_ada_file.adb")?;
// 2. Clean the code (removes comments, preserves structure)
let cleaned_code = AST::clean_code(&code_text);
// 3. Extract all nodes
let nodes = AST::extract_all_nodes(&cleaned_code)?;
// 4. Build the AST
let mut ast = AST::new(nodes);
ast.build(&cleaned_code)?;
// 5. Run post-processing
ast.populate_cases(&cleaned_code)?;
ast.populate_simple_loop_conditions(&cleaned_code)?;
// 6. Analyze the tree
ast.print_tree();
// Find a specific procedure
if let Some(proc_id) = ast.find_node_by_name_and_type("My_Procedure", "ProcedureNode") {
println!("\nAnalyzing 'My_Procedure':");
// Access node data
let proc_node = ast.arena().get(proc_id).unwrap().get();
println!(" Lines: {} to {}",
proc_node.start_line.unwrap(),
proc_node.end_line.unwrap()
);
// Check if it's a body or spec
if proc_node.is_body == Some(true) {
println!(" This is a procedure body");
}
// Inspect parameters
if let Some(args) = &proc_node.arguments {
println!(" Parameters:");
for arg in args {
println!(" - {}: {} (mode: {})",
arg.name, arg.data_type, arg.mode
);
}
}
// Traverse children
println!("\n Children:");
for child_id in proc_id.children(ast.arena()) {
let child = ast.arena().get(child_id).unwrap().get();
println!(" - {} ({})", child.name, child.node_type);
}
}
Ok(())
}
// Extract only packages
let packages = AST::extract_packages(&cleaned_code)?;
for pkg in &packages {
println!("Package: {}", pkg.name);
if pkg.is_body == Some(true) {
println!(" (body)");
}
}
// Extract procedures and functions
let subprograms = AST::extract_procedures_functions(&cleaned_code)?;
// Extract control flow
let loops = AST::extract_simple_loops(&cleaned_code)?;
let while_loops = AST::extract_while_loops(&cleaned_code)?;
let for_loops = AST::extract_for_loops(&cleaned_code)?;
// Parse a condition expression
let condition = AST::parse_condition_expression("X > 10 and Y < 20");
// Access the expression tree
if let Some(root) = &condition.albero {
AST::leggitree(root, 0, "Root: "); // Print tree structure
}
// Access the flat list
for expr in &condition.list {
match expr {
Expression::Binary(bin) => {
println!("Binary op: {:?}", bin.op);
}
Expression::Literal(lit) => {
println!("Literal: {}", lit);
}
_ => {}
}
}
use indextree::NodeId;
// Example: Find procedures without documentation comments
fn check_undocumented_procedures(ast: &AST, code: &str) {
for node_id in ast.root_id().descendants(ast.arena()) {
let node = ast.arena().get(node_id).unwrap().get();
if node.node_type == "ProcedureNode" && node.is_body == Some(true) {
if let Some(start_line) = node.start_line {
// Check if previous line is a comment
let lines: Vec<&str> = code.lines().collect();
if start_line > 1 {
let prev_line = lines[start_line - 2].trim();
if !prev_line.starts_with("--") {
println!("Warning: Procedure '{}' at line {} lacks documentation",
node.name, start_line);
}
}
}
}
}
}
// Visit all nodes in tree order
for node_id in ast.root_id().descendants(ast.arena()) {
let node = ast.arena().get(node_id).unwrap().get();
let depth = node_id.ancestors(ast.arena()).count() - 1;
println!("{}{} - {}",
" ".repeat(depth),
node.node_type,
node.name
);
}
// Find all children of a node
if let Some(pkg_id) = ast.find_node_by_name_and_type("MyPackage", "PackageNode") {
for child_id in pkg_id.children(ast.arena()) {
let child = ast.arena().get(child_id).unwrap().get();
println!("Child: {} ({})", child.name, child.node_type);
}
}
// Get parent of a node
if let Some(parent_id) = some_node_id.ancestors(ast.arena()).nth(1) {
let parent = ast.arena().get(parent_id).unwrap().get();
println!("Parent: {}", parent.name);
}
NodeDataRepresents a single Ada construct with fields like:
name: Identifier (e.g., "MyProcedure")node_type: Type of construct (e.g., "ProcedureNode", "IfStatement")start_line, end_line: Location in sourceis_body: Whether it's a body or specarguments: Parsed parametersconditions: Parsed expressionscases: Case alternatives (for case statements)ASTThe main tree structure with methods:
new(): Create from node listbuild(): Construct parent-child relationshipspopulate_cases(): Extract when clausespopulate_simple_loop_conditions(): Extract exit when conditionsfind_node_by_name_and_type(): Search helperConditionExprParsed condition with:
list: Flat list of all sub-expressionsalbero: Root of expression tree| Construct | Node Type | Notes |
|---|---|---|
| Package spec/body | PackageNode |
Nested packages supported |
| Procedure spec/body | ProcedureNode |
Generic instantiations |
| Function spec/body | FunctionNode |
Return types parsed |
| Task spec/body | TaskNode |
|
| Entry spec/body | EntryNode |
Guard conditions supported |
| Type declaration | TypeDeclaration |
Records, arrays, derived, enums |
| Subtype | TypeDeclaration |
Category: "subtype" |
| Representation clause | TypeDeclaration |
for...use record, for...use at |
| Variable | VariableDeclaration |
Multiple per line, with defaults |
| If/elsif/else | IfStatement, ElsifStatement, ElseStatement |
|
| Case | CaseStatement |
when clauses extracted |
| Simple loop | SimpleLoop |
exit when parsed |
| While loop | WhileLoop |
Condition parsed |
| For loop | ForLoop |
Range/reverse/discrete types |
| Declare block | DeclareNode |
The project includes comprehensive tests:
# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Run specific test
cargo test test_full_integration_on_blob_ada
The test suite validates:
blop.ada)NodeData with metadataend statements to opening blocksContributions are welcome! To contribute:
git checkout -b feature/amazing-feature)cargo test)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Please ensure:
Licensed under the MIT License. See LICENSE-MIT for details.
Francesco Abate
Built with:
Note: This is a parser for analysis purposes, not a full Ada compiler. It's designed to be fast and flexible for tooling, but may not handle all edge cases of the Ada language specification.