tree-sitter-stack-graphs 0.10.0

Create stack graphs using tree-sitter parsers
Documentation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Sequential Definitions                                                     ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Sequential definitions is a pattern where definitions cannot be refered to
;; before they are defined, and definitions are shadowed by later definitions.
;;
;; These rules implement the following name binding behavior:
;; - Definitions are cannot be refered to before they are defined.
;; - Definitions are shadowed by later definitions in the same block.
;;
;; The supported Python syntax is:
;; - Top-level function definitions without parameters.
;; - Function calls without arguments.
;; - Pass statements.
;; - Comments.
;;
;; The following nodes are used:
;; - @node.lexical_scope nodes form a tree connecting "upwards" to the lexical
;;   scope, and are used to resolve references in.
;; - @node.lexical_defs nodes are connected "downwards" to the definitions
;;   introduced by statements. Lexical scopes are connected to them to make the
;;   definitions available when resolving references.

;;;;;;;;;;;;;;;;;;;;;;;
;; Attribute Shorthands

attribute node_definition = node => type = "pop_symbol", node_symbol = node, is_definition
attribute node_reference = node  => type = "push_symbol", node_symbol = node, is_reference
attribute node_symbol = node     => symbol = (source-text node), source_node = node

;;;;;;;;;;
;; Modules

(module)@mod {
    node @mod.lexical_scope
}

(module (_)@left_stmt . (_)@right_stmt)@mod {
    ;; Every statements in the module can reach all definitions from previous statements in the module
    edge @right_stmt.lexical_scope -> @left_stmt.lexical_scope

    ;; The definitions from a statement are visible from the next statement on
    edge @right_stmt.lexical_scope -> @left_stmt.lexical_defs
    attr (@right_stmt.lexical_scope -> @left_stmt.lexical_defs) precedence = 1
}

;;;;;;;;;;;;;
;; Statements

[
    (expression_statement)
    (function_definition)
    (pass_statement)
]@stmt {
    node @stmt.lexical_scope
    node @stmt.lexical_defs
}

(expression_statement (_)@expr)@expr_stmt {
    ;; The expression can reach all definitions visible in the enclosing scope
    edge @expr.lexical_scope -> @expr_stmt.lexical_scope
}

(function_definition name:(identifier)@name)@fun_def {
    ;; A definition with the name @name is introduced
    node def
    attr (def) node_definition = @name

    ;; The definition is exposed to the surrounding block or module
    edge @fun_def.lexical_defs -> def
}

;;;;;;;;;;;;;;
;; Expressions

[
    (call)
]@expr {
    node @expr.lexical_scope
}

(call function:(identifier)@name)@call_expr {
    ;; A reference for the name @name is introduced
    node ref
    attr (ref) node_reference = @name

    ;; The reference is resolved in the the enclosing scope
    edge ref -> @call_expr.lexical_scope
}

;;;;;;;;;;;
;; Comments

(comment)@comment {
    ;; Because comments can appear everywhere, we define all possible nodes on
    ;; them to prevent undefined errors
    node @comment.lexical_scope
    node @comment.lexical_defs
}