84bd12c667
This is a step towards commonizing the code generator between FIR and IR: KT-61970
Fir elements
- All fir elements are listed in
FirTreeBuilder.kt - Syntax of new element declaration:
element(elementName, elementKind: Kind, vararg parents: Element)elementNameis a name of declared element. IfelementName = Foothen it's type will beFirFoo- kind describes target package for element. Avaliable kinds:
Expression(packagefir.expression)Declaration(packagefir.declaration)Reference(packagefir.references)TypeRef(packagefir.types)Other(packagefir)
- if no one parent element was not declaraed than generated element will be direct inheritor of
FirElement
Types
- All types, used in elements and their implementations are described with object of class
Type Typeobjects are used for generating imports in generated files- Types commonly used in configuration are listed in
Types.kt - There is multiple ways to describe new type:
type(klass: KClass<*>)uses FQN of corresponding classtype(packageName: String, typeName: String, exactPackage: Boolean = false)- if
exactPackage = falseit's return type with default package prefix:org.jetbrains.kotlin.packageName.typeName - otherwise there is no default prefix:
packageName.typeName
- if
type(typeName: String)creates type with no package, used only for types of type parameters (Do not use it directly)generatedType([packageName: String], typeName: String)-- same astype(packageName, typeName)but withorg.jetbrains.kotlin.firprefix
Content of elements
- Fields of elements are described in
NodeConfigurator.kt - Syntax:
elementName.configure {
// node configuration
}
- Fields:
Fieldclass describes field of element- There is multiple ways of creating new fields, but they have similar syntax:
field(..., nullable: Boolean = false, withReplace: Boolean)- if
isNullableis true then field type will be nullable - if
withReplaceis true then in element will be generated methodreplace...for that field - in place of
...you can pass optional name (withStringtype) andTypeorElementobject - if no
namepassed then it will be generated based on type - if
TypeorElementhas type argumetns you want to specify then you can call methodType.withArgs(vararg types: String)orElement.withArgs(vararg replacements: Pair<String, String>)
- if
- Also you can create fields with lists of some types
- Lists can holds only fir element
- Syntax:
fieldList([name: String], element: Element)(if name no specified it will be generated based on type ofelement)
- And there are helper functions for fields of primitive types that takes name of field:
booleanField,intField,stringField - If you want generate
transform...function for field you should call methodwithTransform()on it - To add field to configuring node you should call infix
+operator:+fieldList("catches", catchClause).withTransform() - Also you can use method
symbol(symbolTypeName: String, [argument: String])to create field namedsymbolwith type lying inorg.jetbrains.kotlin.fir.symbolspackage - Some predefined fields are listed in
FieldSets.kt
- If your node has some
tansform...methods and you want to add methods for transforming all other children you should callneedTransformOtherChildren() - If your element has type parameters you should declare them using method
withArg(typeParameterName: String, [upperBound: Type/Element]) - If element inherits element with type parameters you should match that parameters with concrete types using method
parentArg(parent: Element, typeParameterName: String, typeArgument: String/Type/Element) - Note that if some element contains type parameters it should be configured before it's inheritors (will be fixed later)
Implementations
- If element has no inheritors then it will have default implementation. Otherwise you should declare implementation that you want
- All implementations are described in
ImplementationConfigurator.kt - Syntax:
impl(element: Element, [name: String]) {...}describes configuration of element with namename(if there is no name then it would beElementTypeImpl). Lambda with implementation configuration is optional. Note that this function returns object of typeImplementationnoImpl(element: Element)used when you don't want to generated implementation ofelement
- In configuration lambda you can:
- Describe kind of implementation --
FinalClass(default),OpenClass,AbstractClass,Interfaceusing syntaxkind = Interface - Add parents for implementation class
- syntax:
parents += parent parentcan be only implementation withkind = Interface
- syntax:
- Configure default values for fields:
default(fieldName: String) { ... }- in configuration lambda you can describe:
value = _defaultValue_withGetter = true/false(falseby default)delegate = delegateFieldName(used for generating such fields:val typeRef: FirTypeRef get() = expression.typeRef)delegateName = fieldNameInDelegateType(val expressionTypeRef: FirTypeRef get() = expression.typeRef)needAcceptAndTransform = true/false(trueby default) -- specify it if you don't want to accept field inacceptChildrencustomSetter = setterExpresison
- note that by default all fields with fir elements are mutable and others are immutable
- in configuration lambda you can describe:
- Also there is some aliases for that default:
default(fieldName, value)defaultNull(fieldName, [withGetter: Boolean])
- If some fields should be
lateinityou describe them in calllateinit(vararg fields: String) - If you use some types that shoub be imported list them in method
useTypes(vararg types: Type/Element)
- Describe kind of implementation --
Notes
- There is algorithm that automatically makes as most abstract classes instead of interfaces as possible. If you want to some
ElementorImplementationshould be always an interface you should:- call
shouldBeAnInterfacewhen configuring aElementinNodeConfigurator.kt - specify
kind = Interfacewhen configuring anImplementationinImplementationConfigurator.kt
- call