gcgen API

This page covers the user-facing API of gcgen. The public API is entirely contained in the gcgen.api module.

class gcgen.api.NodeTransformer(*args, **kwargs)

Base class with which to implement a transformer.

Implement a transformer, a type of class capable of traversing a tree and to replace some/all of the traversed nodes as it does so. Transformers are especially useful for implementing rewrite operations where specific nodes are replaced/modified to make subsequent parsing and eventually code generation easier.

To start traversal, pass the tree to the transform method. Traversal works by defining a transform method on the transformer for each type of node, which in turn calls transform on each of its child nodes to continue traversing down the tree. The return value of each transform handler should be the transformed sub-tree. In this way, parts or all of the tree may be transformed as part of the operation.

Note

For every node for which there is no specific handler, transform_default is called. The default behavior of transform_default is to raise a NotImplementedError. To install a transform handler, write a method taking 1 argument, node, and decorate it using the on_transform() decorator, passing the decorator one or more Type objects (~classes), marking the types of nodes to handle using the method.

Note

  • inheritance works (unlike functools.singledispatchmethod)
    • handlers are inherited, and can be overridden

  • method names of handlers MUST remain unique. If defining several handlers using the same method name, only the last handler is retained.

  • handlers must be decorated with on_transform as the _last_ decorator, wrapping handlers in other decorators breaks the ability to identify a method as a handler.

  • a single handler can handle multiple types of nodes, either by:
    • decorating the handler multiple times using @on_visit(…)

    • passing multiple arguments to @on_visit(…)

transform(node: Any) Any

Transform node node.

Transform node. If a specific handler is registered for the node’s exact type, use it. Otherwise, call transform_default.

Parameters:

node – the node to transform

Returns:

The result of calling the transform handler, may be a new, transformed node, may be the same node.

transform_default(node: Any) Any

Default transform function for nodes of types for which there is no specific transform function.

This method defines the default action when calling transform on a node for which handler has been defined for the node’s exact type. The default behavior is to raise an error, but this method may be overridden by a deriving class to e.g. return back the same object.

Parameters:

node – the node to transform. This node is of a type for which the transformer has no existing transform function.

Returns:

Raises the NotImplementedError

class gcgen.api.NodeVisitor(*args, **kwargs)

Base class with which to implement a visitor.

Implement a visitor, a type of class capable of traversing a tree by using a series of visit operations, one per type of node, to traverse the tree. Visitors are especially useful for implementing validation of nodes in the tree and for traversing trees as part of the code-generation step.

To start traversal, pass the tree to the visit method. Traversal works by defining a visit method on the visitor for each type of node, which in turn calls visit on each of its child nodes to continue traversing down the tree. This approach is named visitor after the GoF “Visitor” pattern.

Note

For every node for which there is no specific handler, visit_default is called. The default behavior of visit_default is to raise a NotImplementedError. To install a visit handler, write a method taking 1 argument, node, and decorate it using the on_visit() decorator, passing the decorator one or more Type objects (~classes), marking the types of nodes to handle using the method.

Note

  • inheritance works (unlike functools.singledispatchmethod)
    • handlers are inherited, and can be overridden

  • method names of handlers MUST remain unique. If defining several handlers using the same method name, only the last handler is retained.

  • handlers must be decorated with on_visit as the _last_ decorator, wrapping handlers in other decorators breaks the ability to identify a method as a handler.

  • a single handler can handle multiple types of nodes, either by:
    • decorating the handler multiple times using @on_visit(…)

    • passing multiple arguments to @on_visit(…)

visit(node: Any) None

Visit node node.

Visit node. If a specific handler is registered for the node’s exact type, use it. Otherwise, call visit_default.

Parameters:

node – the node to visit

Returns:

None

visit_default(node: Any) None

Default visit function for nodes of types for which there is no specific visit function.

This method defines the default action when calling visit on a node for which handler has been defined for the node’s exact type. The default behavior is to raise an error, but this method may be overridden by a deriving class to e.g. trigger a NOOP.

Parameters:

node – the node to visit. This node is of a type for which the visitor has no existing visit function.

Returns:

None

class gcgen.api.Scope

A scope is a form of layered dictionary, where derived/child scopes indirectly inherit all entries visible from the parent scope, with the ability to add new entries, override entries and delete entries, all without changing any of the parent scopes.

copy() Scope

Create a shallow copy of scope.

Returns:

A shallow copy of this scope.

derive() Scope

Create a child scope with this scope as its parent.

Returns:

The newly created child scope.

get(key, default=None)

Get entry in scope identified by key.

Parameters:
  • key – key identifying to value to get

  • default – the value to return if the entry is not found

Returns:

The value associated key or the given default value.

to_dict() dict

flatten scopes out to a dict.

update(other: dict)

Update scope with all entries from other.

class gcgen.api.Section

A class for storing (buffered) text/code output.

A section is an abstraction of text writing which provides 2 capabilities: 1. It provides a way to control indentation 2. It provides a way to nest Section(s) within a Section

(1) The indentation abstraction facilitates composition and reuse of generator code, because each helper function only needs to call indent()/ dedent() to adjust line indentation, rather than embedding a fixed, absolute amount of indentation into the strings of each line written out.

(2) By allowing a section to contain other sections, it becomes possible to define ‘placeholders’ which can be filled out at a later time. Effectively making it possible to define the final output out of order, e.g. adding more variable definitions to the start of a function as it becomes necessary.

add_section(s: Section) Section

Add section to be filled in when desired.

Adds provided section object such that its contents will be preceded by the current contents of this section and superceded by any subsequently added contents to this section.

dedent() Section

Dedent lines by one.

Causes all future lines to be indented one level less.

Note: this function will cause a newline if the current line has any contents already written to it (using emit).

emit(*elems: str) Section

Emit one or more string elements.

emitln(*elems: str) Section

Emit one or more string elements followed by a newline.

emitln_l(*elems: str) Section

Emit line, then dedent.

NOTE: deprecated, use sec.emitln(…).dedent() instead.

emitln_r(*elems: str) Section

Write line, then indent.

NOTE: deprecated, use sec.emitln(…).indent() instead

ensure_padding_lines(nlines: int) Section

Ensure (at least) n empty lines of padding between two sections

NOTE: if this is the top-level buffer and it is empty, this becomes a NO-OP

fl() Section

emit newline iff. not currently at the beginning of a line.

freshline() Section

emit newline iff. not currently at the beginning of a line.

indent() Section

Indent subsequent lines.

Causes all future lines to be indented one level more.

Note: this function will cause a newline if the current line has any contents already written to it (using emit).

newline() Section

add a newline.

gcgen.api.generator(f)

Mark decorated callable as a generator.

This decorator does nothing except install an attribute on the callable, identifying it as a generator.

gcgen.api.get_snippet(scope: ~gcgen.scope.Scope, snippet: str, default=<object object>) Callable[[Section, Scope, None | int | str | bool | List[None | int | str | bool | List[Json] | Dict[str, Json]] | Dict[str, None | int | str | bool | List[Json] | Dict[str, Json]]], None]

dynamically resolve definition of snippet snippet.

NOTE: snippets resolving other snippets using this function allow configuration files in subdirectories to override those snippets in their local context. This can provide local contexts the freedom to reuse a global/ common snippet, while overriding specific parts refactored out as other snippets which are resolved dynamically.

gcgen.api.on_transform(node_type, *node_types)

Mark method as handler for transformations of nodes of type node_type or in node_types.

Use this decorator on methods in a NodeTransformer class to mark methods as handlers for calls to transform when providing node_type or any of the types in node_types.

Parameters:
  • node_type – node_type for which this handler should be used

  • *node_types – (optional) additional node_types for which to use this handler

Returns:

A decorated handler object.

gcgen.api.on_visit(node_type, *node_types)

Mark method as handler for visits of nodes of type node_type or in node_types.

Use this decorator on methods in a NodeVisitor class to mark methods as handlers for calls to visit when providing node_type or any of the types in node_types.

Parameters:
  • node_type – node_type for which this handler should be used

  • *node_types – (optional) additional node_types for which to use this handler

Returns:

A decorated handler object.

gcgen.api.parse_tree(pipeline: List[NodeVisitor | NodeTransformer], tree: Any) Any

Parse tree by applying a series of visitors and transformers.

Parameters:
  • pipeline – A series of operations to perform on the tree, each of which is a visitor or transformer.

  • tree – The input tree/model.

Returns:

The resulting tree/model from applying each operation in order.

gcgen.api.snippet(name: str)

Mark decorated callable as a snippet.

This decorator does nothing except install some attributes on the callable, identifying it as a snippet.

Parameters:

name – the name to identify the snippet by. (Note) can decorate multiple times to provide aliases

Returns:

A decorator function.

class gcgen.api.write_file(fpath: Path | str, indent_by: str = ' ')

Context manager to safely write a file using an Section.

If the context manager finishes successfully, then the file identified by fpath is replaced atomically by the temporary file which contains the new contents. If the context manager is exiting due to an exception, the temporary file is removed and the file at fpath (if any) is untouched.

Parameters:
  • fpath – path to the file to write. (recommended to only write files in the same directory)

  • indent_by – what to write for each level of indentation defaults to a single space (’ ‘).