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

Macro method API of type definitions #14059

Open
HertzDevil opened this issue Dec 6, 2023 · 2 comments
Open

Macro method API of type definitions #14059

HertzDevil opened this issue Dec 6, 2023 · 2 comments

Comments

@HertzDevil
Copy link
Contributor

HertzDevil commented Dec 6, 2023

This is the part of #3274 that deals with type definitions, i.e. AnnotationDef, ClassDef, CStructOrUnionDef, EnumDef, LibDef, and ModuleDef.

I am proposing a unified API across those nodes:

  • #kind : MacroId
    The keyword used in the definition. For example, ClassDef returns either class or struct, CStructOrUnionDef returns either struct or union, and the other node kinds always return the same value.
  • #name(*, generic_args : BoolLiteral = true) : Path | Generic
    Returns the type name, which will be a Generic for a generic type, or a Path otherwise. Passing generic_args: false strips the generic type parameters and always returns a Path. This parameter is supported but ignored on nodes that cannot be generic.
  • #body : ASTNode
    The body, except this will always be a Nop for AnnotationDef because currently nothing is supported inside. Nodes that internally store their bodies as Array(ASTNode) will run them through Crystal::Expressions.from.

All these nodes therefore have (mostly) the same identity interpolation:

macro itself(node)
  {{ node.kind }} {{ node.name }}
    {{ node.body }}
  end
end

Below are the extra methods for specific node kinds:

  • ModuleDef#type_vars : ArrayLiteral, ModuleDef#splat_index : NumberLiteral | NilLiteral
    Since internally they are stored separately, these methods allow direct access without going through #name. The same goes for ClassDef.
  • ClassDef#abstract? : BoolLiteral
    True if the class or struct is abstract.
  • ClassDef#struct? : BoolLiteral
    True if a struct is defined, false if it is a class.
  • ClassDef#superclass : Path | Generic | Self | Nop
    Superclass of the class or struct, or Nop if there is none. (Yes, you can inherit from self if your class is nested inside another one)
  • EnumDef#base_type : ASTNode
    The base type of an enum, or Nop if there is none. Note that syntactically the base type can be things like {x: Int32} or ->, even though in practice only Path nodes are semantically valid as the other node kinds cannot represent primitive integer types.
  • CStructOrUnionDef#union? : BoolLiteral
    True if this node defines an extern union, false if it is an extern struct.
@straight-shoota
Copy link
Member

I'm wondering if ClassDef#superclass should resolve self to the type at definition...? Otherwise you might not be able to resolve that later?

@HertzDevil
Copy link
Contributor Author

HertzDevil commented Dec 6, 2023

If you have:

macro m(x)
  {% p @type %}
end

class Foo
  m class Bar < self
  end
end

then @type == Foo, and that self will also resolve to Foo if m isn't there. But clearly this doesn't define Foo::Bar at all, or m could define something else entirely:

macro m(x)
  class Baz
    {{ x }}
  end
end

class Foo
  m class Bar < self
  end
end

{{ Foo::Baz::Bar.superclass }} # => Foo::Baz

So I don't think ClassDef#superclass should touch that, especially since it is required for the identity interpolation...?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants