Bash.AST (Bash v0.6.0)

Copy Markdown View Source

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

  1. Type Safety: Every node is a proper struct with @type specs
  2. Source Tracking: All nodes include line/column for error messages
  3. Complete: Represents all Bash constructs we support
  4. 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

assignment_name(node)

(macro)

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)

command_name(node)

(macro)

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

is_assignment(node)

(macro)

Returns true if node is an Assignment with the given variable name.

Usable in guards and pattern match when clauses.

is_assignment(node, name)

(macro)

is_command(node)

(macro)

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)

is_command(node, name)

(macro)

postwalk(node, fun)

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.

prewalk(node, fun)

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.

reduce(node, acc, fun)

@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.

walk_tree(node, acc, pre, post)

@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.