diff --git a/compiler/fir/tree/tree-generator/Readme.md b/compiler/fir/tree/tree-generator/Readme.md index 6967a813467..9a0ac95b7fe 100644 --- a/compiler/fir/tree/tree-generator/Readme.md +++ b/compiler/fir/tree/tree-generator/Readme.md @@ -1,32 +1,30 @@ # Fir elements -- All fir elements are listed in `FirTreeBuilder.kt` -- Syntax of new element declaration: `element(elementName, elementKind: Kind, vararg parents: Element)` - - `elementName` is a name of declared element. If `elementName = Foo` then it's type will be `FirFoo` - - kind describes target package for element. Avaliable kinds: +- All fir elements are listed in [`FirTreeBuilder.kt`](src/org/jetbrains/kotlin/fir/tree/generator/FirTreeBuilder.kt). +- The syntax for declaring a new element: `element(elementName, elementKind: Kind, vararg parents: Element)`. + - `elementName` is a name of the declared element. If `elementName = Foo` then it's class will be called `FirFoo`. + - `kind` describes target package of an element. Available kinds: - `Expression` (package `fir.expression`) - `Declaration` (package `fir.declaration`) - `Reference` (package `fir.references`) - `TypeRef` (package `fir.types`) - `Other` (package `fir`) - - if no one parent element was not declaraed than generated element will be direct inheritor of `FirElement` + - if not a single parent element was declared, then the generated element will be a direct inheritor of `FirElement`. # Types -- All types, used in elements and their implementations are described with object of class `Type` -- `Type` objects 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 class - - `type(packageName: String, typeName: String, exactPackage: Boolean = false)` - - if `exactPackage = false` it's return type with default package prefix: `org.jetbrains.kotlin.packageName.typeName` - - otherwise there is no default prefix: `packageName.typeName` - - `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 as `type(packageName, typeName)` but with `org.jetbrains.kotlin.fir` prefix +- Types commonly used in configuration are listed in [`Types.kt`](src/org/jetbrains/kotlin/fir/tree/generator/Types.kt) +- There are multiple ways to describe a new type: + - `fun type()` uses FQN of the corresponding `T` class. + - `fun type(packageName: String, typeName: String, exactPackage: Boolean = false, kind: TypeKind = TypeKind.Interface)`. + - if `exactPackage = false`, its return type with default package prefix: `org.jetbrains.kotlin.packageName.typeName`. + - otherwise, there is no default prefix: `packageName.typeName` . + - `generatedType([packageName: String], typeName: String)` — same as `type(packageName, typeName)` but with + the `org.jetbrains.kotlin.fir` prefix . # Content of elements -- Fields of elements are described in `NodeConfigurator.kt` +- Fields of elements are described in [`NodeConfigurator.kt`](src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt). - Syntax: ``` elementName.configure { @@ -34,56 +32,68 @@ elementName.configure { } ``` - **Fields:** - - `Field` class describes field of element - - There is multiple ways of creating new fields, but they have similar syntax: `field(..., nullable: Boolean = false, withReplace: Boolean)` - - if `isNullable` is true then field type will be nullable - - if `withReplace` is true then in element will be generated method `replace...` for that field - - in place of `...` you can pass optional name (with `String` type) and `Type` or `Element` object - - if no `name` passed then it will be generated based on type - - if `Type` or `Element` has type argumetns you want to specify then you can call method `Type.withArgs(vararg types: String)` or `Element.withArgs(vararg replacements: Pair)` - - 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 of `element`) - - 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 method `withTransform()` 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 named `symbol` with type lying in `org.jetbrains.kotlin.fir.symbols` package - - 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 call `needTransformOtherChildren()` -- 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) + - The `Field` class describes a field of an element. + - There are multiple ways of creating new fields, but they have similar syntax: + `field(..., nullable: Boolean = false, withReplace: Boolean)`. + - if `nullable` is true, then the type of the field will be nullable. + - if `withReplace` is true, then in the element the `replace...` method will be generated for that field. + - in place of `...` you can pass an optional name (with `String` type), and `TypeRef` or `Element` object + - if no `name` is passed, then it will be inferred based on the type. + - if `TypeRef` or `Element` have type arguments, then you can use `TypeRef.withArgs(vararg types: TypeRef)`. + - Also, you can create fields with lists of some types. + - Syntax: `fieldList([name: String], element: ElementOrRef)` (if no name is specified, it will be inferred based on the type of + `element`). + - And there are helper functions for fields of primitive types: `booleanField`, `intField`, `stringField`. + - If you want to generate a separate `transform...` function for the field, you should call `withTransform()` on it. + - To add the field to the node being configured, you should call the infix `+` operator: + `+fieldList("catches", catchClause).withTransform()`. + - Also, you can use `symbol(symbolTypeName: String)` to create a field named `symbol` with a lying in + the `org.jetbrains.kotlin.fir.symbols` package. + - Some predefined fields are listed in [`FieldSets.kt`](src/org/jetbrains/kotlin/fir/tree/generator/FieldSets.kt). +- If your node has some `transform...` methods, and you want to add methods for transforming all other children, you should call + `needTransformOtherChildren()`. +- If an element has type parameters, you should declare them using `withArg(typeParameterName: String, [upperBound: TypeRef])`. +- If an element inherits another element with type parameters, you should match those parameters with concrete types using + `parentArgs(parent: Element, typeParameterName: String, vararg arguments: Pair)`. +- Note that if some element contains type parameters, it should be configured before its 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 +- If an element has no inheritors, then it will have a default implementation. Otherwise, you should declare an implementation that you + want. - All implementations are described in `ImplementationConfigurator.kt` - Syntax: - - `impl(element: Element, [name: String]) {...}` describes configuration of element with name `name` (if there is no name then it would be `ElementTypeImpl`). Lambda with implementation configuration is optional. Note that this function returns object of type `Implementation` - - `noImpl(element: Element)` used when you don't want to generated implementation of `element` -- In configuration lambda you can: - - Describe kind of implementation -- `FinalClass` (default), `OpenClass`, `AbstractClass`, `Interface` using syntax `kind = Interface` - - Add parents for implementation class - - syntax: `parents += parent` - - `parent` can be only implementation with `kind = Interface` + - `impl(element: Element, [name: String]) {...}` describes the configuration of the element with name `name` (if there is no name, + then it would be `ElementTypeImpl`). + Lambda with implementation configuration is optional. + Note that this function returns an object of type `Implementation`. + - `noImpl(element: Element)` used when you don't want to generate any implementation for `element`/ +- In the configuration lambda you can: + - Describe the kind of the implementation — `FinalClass` (default), `OpenClass`, `AbstractClass`, `Interface` using the syntax + `kind = Interface` + - Add parents for the implementation class + - syntax: `parents += parent`. + - `parent` can be only implementation with `kind = Interface`. - Configure default values for fields: - `default(fieldName: String) { ... }` - in configuration lambda you can describe: - - `value = _defaultValue_` - - `withGetter = true/false` (`false` by 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` (`true` by default) -- specify it if you don't want to accept field in `acceptChildren` - - `customSetter = setterExpresison` - - note that by default all fields with fir elements are mutable and others are immutable - - Also there is some aliases for that default: + - `value = _defaultValue_`. + - `withGetter = true/false` (`false` by 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` (`true` by default) -- specify it if you don't want to accept field in + `acceptChildren`. + - `customSetter = setterExpresison`. + - note that by default, all fields with fir elements are mutable, and others are immutable. + - Also, there are some aliases for that default: - `default(fieldName, value)` - `defaultNull(fieldName, [withGetter: Boolean])` - - If some fields should be `lateinit` you describe them in call `lateinit(vararg fields: String)` - - If you use some types that shoub be imported list them in method `useTypes(vararg types: Type/Element)` + - If some fields should be `lateinit`, you describe them in the `lateinit(vararg fields: String)` call. + - If you use some types that should be imported, list them by calling `additionalImports(vararg types: Importable)` # Notes -- There is algorithm that automatically makes as most abstract classes instead of interfaces as possible. If you want to some `Element` or `Implementation` should be always an interface you should: - - call `shouldBeAnInterface` when configuring a `Element` in `NodeConfigurator.kt` - - specify `kind = Interface` when configuring an `Implementation` in `ImplementationConfigurator.kt` \ No newline at end of file +- There is an algorithm that automatically makes as most abstract classes instead of interfaces as possible. + If you want to some `Element` or `Implementation` should be always an interface you should: + - call `shouldBeAnInterface()` when configuring a `Element` in `NodeConfigurator.kt` + - specify `kind = Interface` when configuring an `Implementation` in `ImplementationConfigurator.kt`