123456789_123456789_123456789_123456789_123456789_

config.yml

config.yml is the definition of data structure for parser results -- AST.

It defines the data structure for the parser implementation in C and Rust node modules.

C parser code

$ rake templates

Our C parser consists of two components:

  1. Pure C Parser
  2. Translator from the pure C AST to Ruby object

config.yml defines the AST for pure C parser in ast.h/ast.c and translator from the C AST to Ruby objects in ast_translation.h/ast_translation.c.

ruby-rbs crate

$ cd rust; cargo build

The build.rs in ruby-rbs crate defines the data structure derived from config.yml definitions under node module.

nodes

nodes defines node data types in C or Rust.

nodes:
  - name: RBS::AST::Declarations::Class
    rust_name: ClassNode
    fields:
      - name: name
        c_type: rbs_type_name
      - name: type_params
        c_type: rbs_node_list
      - name: super_class
        c_type: rbs_ast_declarations_class_super
        optional: true  # NULL when no superclass (e.g., `class Foo end` vs `class Foo < Bar end`)
      - name: members
        c_type: rbs_node_list
      - name: annotations
        c_type: rbs_node_list
      - name: comment
        c_type: rbs_ast_comment
        optional: true  # NULL when no comment precedes the declaration

This defines rbs_ast_declarations_class struct so that the parser constructs the AST using the structs.

typedef struct rbs_ast_declarations_class {
    rbs_node_t base;

    struct rbs_type_name *name;
    struct rbs_node_list *type_params;
    struct rbs_ast_declarations_class_super *super_class; /* Optional */
    struct rbs_node_list *members;
    struct rbs_node_list *annotations;
    struct rbs_ast_comment *comment; /* Optional */

    rbs_location_range keyword_range;     /* Required */
    rbs_location_range name_range;        /* Required */
    rbs_location_range end_range;         /* Required */
    rbs_location_range type_params_range; /* Optional */
    rbs_location_range lt_range;          /* Optional */
} rbs_ast_declarations_class_t;

The rbs_ast_declarations_class struct is a pure C AST, and ast_translation.c defines translation into a Ruby object of ::RBS::AST::Declarations::Class class.

    case RBS_AST_DECLARATIONS_CLASS: {
        rbs_ast_declarations_class_t *node = (rbs_ast_declarations_class_t *) instance;

        VALUE h = rb_hash_new();
        VALUE location = rbs_location_range_to_ruby_location(ctx, node->base.location);
        rbs_loc *loc = rbs_check_location(location);
        rbs_loc_legacy_alloc_children(loc, 5);
        rbs_loc_legacy_add_required_child(loc, rb_intern("keyword"), (rbs_loc_range) { .start = node->keyword_range.start_char, .end = node->keyword_range.end_char });
        rbs_loc_legacy_add_required_child(loc, rb_intern("name"), (rbs_loc_range) { .start = node->name_range.start_char, .end = node->name_range.end_char });
        rbs_loc_legacy_add_required_child(loc, rb_intern("end"), (rbs_loc_range) { .start = node->end_range.start_char, .end = node->end_range.end_char });
        rbs_loc_legacy_add_optional_child(loc, rb_intern("type_params"), (rbs_loc_range) { .start = node->type_params_range.start_char, .end = node->type_params_range.end_char });
        rbs_loc_legacy_add_optional_child(loc, rb_intern("lt"), (rbs_loc_range) { .start = node->lt_range.start_char, .end = node->lt_range.end_char });
        rb_hash_aset(h, ID2SYM(rb_intern("location")), location);
        rb_hash_aset(h, ID2SYM(rb_intern("name")), rbs_struct_to_ruby_value(ctx, (rbs_node_t *) node->name)); // rbs_type_name
        rb_hash_aset(h, ID2SYM(rb_intern("type_params")), rbs_node_list_to_ruby_array(ctx, node->type_params));
        rb_hash_aset(h, ID2SYM(rb_intern("super_class")), rbs_struct_to_ruby_value(ctx, (rbs_node_t *) node->super_class)); // rbs_ast_declarations_class_super
        rb_hash_aset(h, ID2SYM(rb_intern("members")), rbs_node_list_to_ruby_array(ctx, node->members));
        rb_hash_aset(h, ID2SYM(rb_intern("annotations")), rbs_node_list_to_ruby_array(ctx, node->annotations));
        rb_hash_aset(h, ID2SYM(rb_intern("comment")), rbs_struct_to_ruby_value(ctx, (rbs_node_t *) node->comment)); // rbs_ast_comment

        rb_funcall(
            RBS_AST_TypeParam,
            rb_intern("resolve_variables"),
            1,
            rb_hash_lookup(h, ID2SYM(rb_intern("type_params")))
        );
        return CLASS_NEW_INSTANCE(
            RBS_AST_Declarations_Class,
            1,
            &h
        );
    }

enums

enums defines enum data types in C or Rust.

enums:
  attribute_visibility:
    optional: true
    symbols:
      - unspecified
      - public
      - private

For example, the attribute_visibility enum is a data type for visibility attribute of attr_reader, attr_writer, and attr_accessor definitions. The visibility attribute can be one of unspecified, public, and private.

Symbol enums

Enum definition with symbols: attribute defines enum data that is mapped to Ruby symbols.

enums:
  attribute_visibility:
    optional: true
    symbols:
      - unspecified
      - public
      - private

It defines an enum in C AST definition.

enum RBS_ATTRIBUTE_VISIBILITY_TAG {
    RBS_ATTRIBUTE_VISIBILITY_TAG_UNSPECIFIED,
    RBS_ATTRIBUTE_VISIBILITY_TAG_PUBLIC,
    RBS_ATTRIBUTE_VISIBILITY_TAG_PRIVATE,
};

The C extension also defines a translation:

VALUE rbs_attribute_visibility_to_ruby(enum rbs_attribute_visibility value) {
    switch (value) {
    case RBS_ATTRIBUTE_VISIBILITY_UNSPECIFIED:
        return Qnil;
    case RBS_ATTRIBUTE_VISIBILITY_PUBLIC:
        return rb_id2sym(rb_intern("public"));
    case RBS_ATTRIBUTE_VISIBILITY_PRIVATE:
        return rb_id2sym(rb_intern("private"));
    default:
        rb_fatal("unknown enum rbs_attribute_visibility value: %d", value);
    }
}

RBS_ATTRIBUTE_VISIBILITY_PUBLIC and RBS_ATTRIBUTE_VISIBILITY_PRIVATE are translated to Ruby symbols :public and :private respectively.

Note that the first RBS_ATTRIBUTE_VISIBILITY_UNSPECIFIED is translated to nil in Ruby. This is specified by the optional: true attribute in YAML. When optional: true is set, the first enum value is translated to nil.