Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add visit(node, visitor) to 'yaml' #225

Merged
merged 13 commits into from Jan 31, 2021
Merged

Add visit(node, visitor) to 'yaml' #225

merged 13 commits into from Jan 31, 2021

Conversation

eemeli
Copy link
Owner

@eemeli eemeli commented Jan 30, 2021

Closes #190, so ping at least @amadare42 and @privatenumber.

This adds the following new export to 'yaml/util':

/**
 * Apply a visitor to an AST node or document.
 *
 * Walks through the tree (depth-first) starting from `node`, calling each of
 * the visitor functions (if defined) according to the current node type.
 */
export function visit(node: Node | Document, visitor: {
  Document?: (doc: Document) => void
  Map?: (map: YAMLMap) => void
  Pair?: (pair: Pair) => void
  Seq?: (seq: YAMLSeq) => void
  Scalar?: (scalar: Scalar) => void
}): void

To start with, this is pretty minimal and just walks the tree. In other words, it does not provide the node's current path, it does not provide control methods for early exits from the walk, and it does not do anything with the value returned from a visitor function.

I'm quite open to developing this API further, but I'd like to see example use cases or other arguments for more complex features.

EDIT: See next comment for updated API.

@eemeli
Copy link
Owner Author

eemeli commented Jan 31, 2021

Okay, fine, I gave it more features. Looked at a number of different AST visitor implementations, ended up taking quite a bit of inspiration from GraphQL, as well as elsewhere. Here's the new API:

/**
 * Apply a visitor to an AST node or document.
 *
 * Walks through the tree (depth-first) starting from `node`, calling a
 * `visitor` function with three arguments:
 *   - `key`: For sequence values and map `Pair`, the node's index in the
 *     collection. Within a `Pair`, `'key'` or `'value'`, correspondingly.
 *     `null` for the root node.
 *   - `node`: The current node.
 *   - `path`: The ancestry of the current node.
 *
 * The return value of the visitor may be used to control the traversal:
 *   - `undefined` (default): Do nothing and continue
 *   - `visit.SKIP`: Do not visit the children of this node, continue with next
 *     sibling
 *   - `visit.BREAK`: Terminate traversal completely
 *   - `visit.REMOVE`: Remove the current node, then continue with the next one
 *   - `Node`: Replace the current node, then continue by visiting it
 *   - `number`: While iterating the items of a sequence or map, set the index
 *     of the next step. This is useful especially if the index of the current
 *     node has changed.
 *
 * If `visitor` is a single function, it will be called with all values
 * encountered in the tree, including e.g. `null` values. Alternatively,
 * separate visitor functions may be defined for each `Map`, `Pair`, `Seq`,
 *  `Alias` and `Scalar` node.
 */
export declare const visit: visit

export type visitor<T> = (
  key: number | 'key' | 'value' | null,
  node: T,
  path: Node[]
) => void | symbol | number | Node

export interface visit {
  (
    node: Node | Document,
    visitor:
      | visitor<any>
      | {
          Alias?: visitor<Alias>
          Map?: visitor<YAMLMap>
          Pair?: visitor<Pair>
          Scalar?: visitor<Scalar>
          Seq?: visitor<YAMLSeq>
        }
  ): void

  /** Terminate visit traversal completely */
  BREAK: symbol

  /** Remove the current node */
  REMOVE: symbol

  /** Do not visit the children of the current node */
  SKIP: symbol
}

@eemeli eemeli changed the title Add visit(node, visitor) to 'yaml/util' Add visit(node, visitor) to 'yaml' Jan 31, 2021
@eemeli
Copy link
Owner Author

eemeli commented Jan 31, 2021

On further thought, it doesn't make sense to hide this behind 'yaml/util'. Also updated documentation.

@eemeli eemeli merged commit ca7a705 into master Jan 31, 2021
@eemeli eemeli deleted the visitor branch January 31, 2021 19:08
eemeli added a commit that referenced this pull request Jan 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Document visitor
1 participant