Typed Abstract Syntax Tree (AST) for Bash.
This module defines structured types for representing parsed Bash code. Each AST node includes metadata for source location tracking and error reporting.
Design Principles
- Type Safety: Every node is a proper struct with
@typespecs - Source Tracking: All nodes include line/column for error messages
- Complete: Represents all Bash constructs we support
- Immutable: AST is built once during parsing, never modified
AST Hierarchy
Script (top level)
├── Statement (commands, assignments, control flow)
│ ├── Command (simple command with args)
│ ├── Pipeline (cmd1 | cmd2 | cmd3)
│ ├── Assignment (VAR=value)
│ ├── If (if/elif/else/fi)
│ ├── ForLoop (for var in items; do...; done)
│ ├── WhileLoop (while/until condition; do...; done)
│ ├── Case (case var in "pattern)" esac)
│ ├── Function (function name { ...; })
│ ├── Compound (subshells, groups, logical, sequential)
│ ├── Word (text with variable expansion)
│ └── Variable (variable reference with operators)
└── Expression (expandable text, tests, arithmetic)
├── TestExpression (`[[ ... ]]` expressions)
└── Arithmetic (`(( ... ))` arithmetic)Reference
Bash grammar: https://cgit.git.savannah.gnu.org/cgit/bash.git/plain/parse.y?h=bash-5.3
Summary
Functions
Extracts the variable name from an Assignment node.
Extracts the literal command name from a Command node.
Returns true if node is an Assignment with the given variable name.
Returns true if node is a Command with the given literal name.
Bottom-up transformation using the Walkable protocol.
Top-down transformation using the Walkable protocol.
Reduces over all nodes in the tree without modifying it.
Walks the AST with an accumulator, calling pre before descending
into children and post after.
Functions
Extracts the variable name from an Assignment node.
Usable in guards and pattern match when clauses.
Examples
import Bash.AST
AST.reduce(script, [], fn
node when is_assignment(node, "PATH") -> [:found | acc]
_, acc -> acc
end)
Extracts the literal command name from a Command node.
Usable in guards and pattern match when clauses.
Examples
import Bash.AST
# In a walker callback
AST.prewalk(script, fn
node when command_name(node) == "rm" -> nil
node -> node
end)
# In a function head
def handle_command(node) when command_name(node) == "sudo" do
# ...
end
Returns true if node is an Assignment with the given variable name.
Usable in guards and pattern match when clauses.
Returns true if node is a Command with the given literal name.
Usable in guards and pattern match when clauses.
Examples
import Bash.AST
AST.prewalk(script, fn
node when is_command(node, "rm") -> nil
node -> node
end)
@spec postwalk(Bash.AST.Walkable.t(), (Bash.AST.Walkable.t() -> Bash.AST.Walkable.t() | nil)) :: Bash.AST.Walkable.t() | nil
Bottom-up transformation using the Walkable protocol.
Applies fun to each node after processing its children.
Return nil to remove a node from its parent list.
@spec prewalk(Bash.AST.Walkable.t(), (Bash.AST.Walkable.t() -> Bash.AST.Walkable.t() | nil)) :: Bash.AST.Walkable.t() | nil
Top-down transformation using the Walkable protocol.
Applies fun to each node before descending into its children.
Return nil to remove a node from its parent list.
@spec reduce(Bash.AST.Walkable.t(), acc, (Bash.AST.Walkable.t(), acc -> acc)) :: acc when acc: term()
Reduces over all nodes in the tree without modifying it.
Visits each node depth-first (pre-order) and applies fun to
accumulate a result.
@spec walk_tree(node, acc, (node, acc -> {node, acc}), (node, acc -> {node, acc})) :: {node, acc} when node: Bash.AST.Walkable.t(), acc: term()
Walks the AST with an accumulator, calling pre before descending
into children and post after.
Both callbacks receive (node, acc) and must return {node, acc}.
Returning nil as the node removes it from parent lists.