From ab3d54b742ad1601a75c112f3b7e226612db6007 Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 17:56:56 +0900 Subject: [PATCH 01/13] Introduce NodeSupplement --- ast/src/commonMain/kotlin/ktast/ast/Node.kt | 421 ++++++++++-------- .../kotlin/ktast/ast/NodeSupplement.kt | 16 + 2 files changed, 262 insertions(+), 175 deletions(-) create mode 100644 ast/src/commonMain/kotlin/ktast/ast/NodeSupplement.kt diff --git a/ast/src/commonMain/kotlin/ktast/ast/Node.kt b/ast/src/commonMain/kotlin/ktast/ast/Node.kt index 5e08c1293..0c12d40dd 100644 --- a/ast/src/commonMain/kotlin/ktast/ast/Node.kt +++ b/ast/src/commonMain/kotlin/ktast/ast/Node.kt @@ -5,9 +5,9 @@ package ktast.ast */ sealed interface Node { /** - * Property to store any extra data by the user. + * Supplemental data for the node. */ - var tag: Any? + val supplement: NodeSupplement /** * Common interface for AST nodes that have a simple text representation. @@ -141,7 +141,7 @@ sealed interface Node { override val packageDirective: PackageDirective?, override val importDirectives: List, override val declarations: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, KotlinEntry, WithDeclarations /** @@ -155,7 +155,7 @@ sealed interface Node { override val packageDirective: PackageDirective?, override val importDirectives: List, val expressions: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, KotlinEntry /** @@ -165,7 +165,7 @@ sealed interface Node { */ data class PackageDirective( val names: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node /** @@ -177,7 +177,7 @@ sealed interface Node { data class ImportDirective( val names: List, val aliasName: Expression.NameExpression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node /** @@ -206,7 +206,7 @@ sealed interface Node { val loopRange: Expression, val rPar: Keyword.RPar, val body: Expression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Statement /** @@ -232,7 +232,7 @@ sealed interface Node { override val condition: Expression, override val rPar: Keyword.RPar, override val body: Expression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : WhileStatementBase /** @@ -243,7 +243,7 @@ sealed interface Node { override val lPar: Keyword.LPar, override val condition: Expression, override val rPar: Keyword.RPar, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : WhileStatementBase } @@ -326,7 +326,7 @@ sealed interface Node { override val lPar: Keyword.LPar, override val arguments: List, override val rPar: Keyword.RPar, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : ClassParent { override val expression: Expression? = null } @@ -341,7 +341,7 @@ sealed interface Node { data class DelegationClassParent( override val type: Type, override val expression: Expression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : ClassParent { override val lPar: Keyword.LPar? = null override val arguments: List = listOf() @@ -359,7 +359,7 @@ sealed interface Node { */ data class TypeClassParent( override val type: Type, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : ClassParent { override val lPar: Keyword.LPar? = null override val arguments: List = listOf() @@ -376,7 +376,7 @@ sealed interface Node { data class ClassBody( val enumEntries: List, override val declarations: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, WithDeclarations { /** @@ -394,7 +394,7 @@ sealed interface Node { override val arguments: List, override val rPar: Keyword.RPar?, val classBody: ClassBody?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, WithModifiers, WithValueArguments /** @@ -404,7 +404,7 @@ sealed interface Node { */ data class Initializer( val block: Expression.BlockExpression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Declaration /** @@ -424,7 +424,7 @@ sealed interface Node { override val rPar: Keyword.RPar?, val delegationCall: Expression.CallExpression?, val block: Expression.BlockExpression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Declaration, WithModifiers, WithFunctionParameters } } @@ -454,7 +454,7 @@ sealed interface Node { override val parents: List, val typeConstraintSet: PostModifier.TypeConstraintSet?, override val body: ClassOrObject.ClassBody?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : ClassOrObject, WithTypeParameters { /** @@ -475,7 +475,7 @@ sealed interface Node { override val lPar: Keyword.LPar?, override val parameters: List, override val rPar: Keyword.RPar?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, WithModifiers, WithFunctionParameters } @@ -488,7 +488,7 @@ sealed interface Node { override val name: Expression.NameExpression?, override val parents: List, override val body: ClassOrObject.ClassBody?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : ClassOrObject /** @@ -516,7 +516,7 @@ sealed interface Node { val returnType: Type?, override val postModifiers: List, val body: Expression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Declaration, WithModifiers, WithTypeParameters, WithFunctionParameters, WithPostModifiers /** @@ -548,7 +548,7 @@ sealed interface Node { val initializerExpression: Expression?, val delegateExpression: Expression?, val accessors: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Declaration, WithModifiers, WithTypeParameters { init { require(variables.isNotEmpty()) { "variables must not be empty" } @@ -588,7 +588,7 @@ sealed interface Node { val type: Type?, override val postModifiers: List, override val body: Expression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Accessor /** @@ -606,7 +606,7 @@ sealed interface Node { override val rPar: Keyword.RPar?, override val postModifiers: List, override val body: Expression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Accessor { init { if (parameter == null) { @@ -633,7 +633,7 @@ sealed interface Node { override val typeParameters: List, override val rAngle: Keyword.Greater?, val type: Type, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Declaration, WithModifiers, WithTypeParameters } @@ -655,7 +655,7 @@ sealed interface Node { val lPar: Keyword.LPar, val innerType: Type, val rPar: Keyword.RPar, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Type /** @@ -669,7 +669,7 @@ sealed interface Node { override val modifiers: List, val innerType: Type, val questionMark: Keyword.Question, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Type private interface NameWithTypeArguments : WithTypeArguments { @@ -690,7 +690,7 @@ sealed interface Node { override val lAngle: Keyword.Less?, override val typeArguments: List, override val rAngle: Keyword.Greater?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Type, NameWithTypeArguments { /** * AST node that represents a qualifier of simple named type. The node corresponds to KtUserType used as a qualifier. @@ -702,7 +702,7 @@ sealed interface Node { override val lAngle: Keyword.Less?, override val typeArguments: List, override val rAngle: Keyword.Greater?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, NameWithTypeArguments } @@ -713,7 +713,7 @@ sealed interface Node { */ data class DynamicType( override val modifiers: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Type /** @@ -735,7 +735,7 @@ sealed interface Node { val parameters: List, val rPar: Keyword.RPar?, val returnType: Type, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Type { /** @@ -748,7 +748,7 @@ sealed interface Node { data class FunctionTypeParameter( val name: Expression.NameExpression?, val type: Type, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node } } @@ -782,7 +782,7 @@ sealed interface Node { val rPar: Keyword.RPar, val body: Expression, val elseBody: Expression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression /** @@ -796,7 +796,7 @@ sealed interface Node { val block: BlockExpression, val catchClauses: List, val finallyBlock: BlockExpression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression { /** * AST node that represents a catch clause. The node corresponds to KtCatchClause. @@ -809,7 +809,7 @@ sealed interface Node { override val parameters: List, override val rPar: Keyword.RPar, val block: BlockExpression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, WithFunctionParameters { init { require(parameters.isNotEmpty()) { "catch clause must have at least one parameter" } @@ -828,7 +828,7 @@ sealed interface Node { val whenKeyword: Keyword.When, val subject: WhenSubject?, val branches: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression { /** * AST node that represents a subject of when expression. The node corresponds to a part of KtWhenExpression. @@ -845,7 +845,7 @@ sealed interface Node { val variable: Variable?, val expression: Expression, val rPar: Keyword.RPar, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, WithAnnotationSets /** @@ -871,7 +871,7 @@ sealed interface Node { override val conditions: List, override val arrow: Keyword.Arrow, override val body: Expression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : WhenBranch { init { require(conditions.isNotEmpty()) { "conditions must not be empty" } @@ -887,7 +887,7 @@ sealed interface Node { data class ElseWhenBranch( override val arrow: Keyword.Arrow, override val body: Expression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : WhenBranch { override val conditions = listOf() } @@ -929,7 +929,7 @@ sealed interface Node { */ data class ExpressionWhenCondition( override val expression: Expression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : WhenCondition { override val operator = null override val type = null @@ -945,7 +945,7 @@ sealed interface Node { data class RangeWhenCondition( override val operator: WhenConditionRangeOperator, override val expression: Expression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : WhenCondition { override val type = null } @@ -960,7 +960,7 @@ sealed interface Node { data class TypeWhenCondition( override val operator: WhenConditionTypeOperator, override val type: Type, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : WhenCondition { override val expression = null } @@ -973,7 +973,7 @@ sealed interface Node { */ data class ThrowExpression( val expression: Expression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression /** @@ -985,7 +985,7 @@ sealed interface Node { data class ReturnExpression( override val label: NameExpression?, val expression: Expression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression, WithLabel /** @@ -995,7 +995,7 @@ sealed interface Node { */ data class ContinueExpression( override val label: NameExpression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression, WithLabel /** @@ -1005,7 +1005,7 @@ sealed interface Node { */ data class BreakExpression( override val label: NameExpression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression, WithLabel /** @@ -1015,7 +1015,7 @@ sealed interface Node { */ data class BlockExpression( override val statements: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression, WithStatements /** @@ -1035,7 +1035,7 @@ sealed interface Node { override val arguments: List, override val rPar: Keyword.RPar?, val lambdaArgument: Expression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression, WithTypeArguments, WithValueArguments { /** * Returns the lambda expression of the lambda argument if exists, otherwise `null`. @@ -1056,7 +1056,7 @@ sealed interface Node { val parameters: List, val arrow: Keyword.Arrow?, override val statements: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression, WithStatements /** @@ -1070,7 +1070,7 @@ sealed interface Node { val lhs: Expression, val operator: BinaryOperator, val rhs: Expression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression { /** * Common interface for AST nodes that represent binary operators. @@ -1104,7 +1104,7 @@ sealed interface Node { data class PrefixUnaryExpression( override val operator: UnaryExpression.UnaryOperator, override val expression: Expression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : UnaryExpression /** @@ -1116,7 +1116,7 @@ sealed interface Node { data class PostfixUnaryExpression( override val expression: Expression, override val operator: UnaryExpression.UnaryOperator, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : UnaryExpression /** @@ -1130,7 +1130,7 @@ sealed interface Node { val lhs: Expression, val operator: BinaryTypeOperator, val rhs: Type, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression { /** * Common interface for AST nodes that represent binary type operators. @@ -1172,7 +1172,7 @@ sealed interface Node { override val lhs: Expression?, override val questionMarks: List, val rhs: NameExpression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : DoubleColonExpression /** @@ -1184,7 +1184,7 @@ sealed interface Node { data class ClassLiteralExpression( override val lhs: Expression, override val questionMarks: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : DoubleColonExpression /** @@ -1194,7 +1194,7 @@ sealed interface Node { */ data class ParenthesizedExpression( val innerExpression: Expression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression /** @@ -1206,7 +1206,7 @@ sealed interface Node { data class StringLiteralExpression( val entries: List, val raw: Boolean, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression { /** * Common interface for string entries. The node corresponds to KtStringTemplateEntry. @@ -1220,7 +1220,7 @@ sealed interface Node { */ data class LiteralStringEntry( override val text: String, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : StringEntry, SimpleTextNode /** @@ -1230,7 +1230,7 @@ sealed interface Node { */ data class EscapeStringEntry( override val text: String, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : StringEntry, SimpleTextNode { init { require(text.startsWith('\\')) { @@ -1248,7 +1248,7 @@ sealed interface Node { data class TemplateStringEntry( val expression: Expression, val short: Boolean, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : StringEntry { init { require(!short || expression is NameExpression || expression is ThisExpression) { @@ -1274,7 +1274,7 @@ sealed interface Node { */ data class BooleanLiteralExpression( override val text: String, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : ConstantLiteralExpression { init { require(text == "true" || text == "false") { @@ -1290,7 +1290,7 @@ sealed interface Node { */ data class CharacterLiteralExpression( override val text: String, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : ConstantLiteralExpression { init { require(text.startsWith('\'') && text.endsWith('\'')) { @@ -1306,7 +1306,7 @@ sealed interface Node { */ data class IntegerLiteralExpression( override val text: String, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : ConstantLiteralExpression /** @@ -1317,14 +1317,14 @@ sealed interface Node { data class RealLiteralExpression( override val text: String, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : ConstantLiteralExpression /** * AST node that represents a null literal expression. The node corresponds to KtConstantExpression whose expressionType is KtNodeTypes.NULL. */ data class NullLiteralExpression( - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : ConstantLiteralExpression { override val text: String get() = "null" @@ -1337,7 +1337,7 @@ sealed interface Node { */ data class ObjectLiteralExpression( val declaration: Declaration.ObjectDeclaration, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression /** @@ -1347,7 +1347,7 @@ sealed interface Node { */ data class CollectionLiteralExpression( val expressions: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression /** @@ -1357,7 +1357,7 @@ sealed interface Node { */ data class ThisExpression( override val label: NameExpression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression, WithLabel /** @@ -1369,7 +1369,7 @@ sealed interface Node { data class SuperExpression( val typeArgument: TypeArgument?, override val label: NameExpression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression, WithLabel /** @@ -1379,7 +1379,7 @@ sealed interface Node { */ data class NameExpression( override val text: String, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression, BinaryExpression.BinaryOperator /** @@ -1391,7 +1391,7 @@ sealed interface Node { data class LabeledExpression( val label: NameExpression, val statement: Statement, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression /** @@ -1403,7 +1403,7 @@ sealed interface Node { data class AnnotatedExpression( override val annotationSets: List, val statement: Statement, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression, WithAnnotationSets /** @@ -1415,7 +1415,7 @@ sealed interface Node { data class IndexedAccessExpression( val expression: Expression, val indices: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression /** @@ -1425,7 +1425,7 @@ sealed interface Node { */ data class AnonymousFunctionExpression( val function: Declaration.FunctionDeclaration, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Expression } @@ -1440,7 +1440,7 @@ sealed interface Node { override val modifiers: List, val name: Expression.NameExpression, val type: Type?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, WithModifiers /** @@ -1458,7 +1458,7 @@ sealed interface Node { val name: Expression.NameExpression, val type: Type?, val defaultValue: Expression?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, WithModifiers /** @@ -1475,7 +1475,7 @@ sealed interface Node { val variables: List, val rPar: Keyword.RPar?, val destructuringType: Type?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node { init { if (variables.size >= 2) { @@ -1495,7 +1495,7 @@ sealed interface Node { override val annotationSets: List, val name: Expression.NameExpression, val type: Type?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, WithAnnotationSets /** @@ -1507,7 +1507,7 @@ sealed interface Node { data class TypeArgument( override val modifiers: List, val type: Type, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, WithModifiers /** @@ -1521,7 +1521,7 @@ sealed interface Node { val name: Expression.NameExpression?, val spreadOperator: Keyword.Asterisk?, val expression: Expression, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node /** @@ -1535,7 +1535,7 @@ sealed interface Node { val lPar: Keyword.LPar, val receiverTypes: List, val rPar: Keyword.RPar, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node /** @@ -1555,7 +1555,7 @@ sealed interface Node { val lBracket: Keyword.LBracket?, val annotations: List, val rBracket: Keyword.RBracket?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Modifier { /** * Common interface for annotation target keywords. @@ -1573,7 +1573,7 @@ sealed interface Node { override val lPar: Keyword.LPar?, override val arguments: List, override val rPar: Keyword.RPar?, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, WithValueArguments } @@ -1594,7 +1594,7 @@ sealed interface Node { */ data class TypeConstraintSet( val constraints: List, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : PostModifier { /** @@ -1608,7 +1608,7 @@ sealed interface Node { override val annotationSets: List, val name: Expression.NameExpression, val type: Type, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Node, WithAnnotationSets } @@ -1623,7 +1623,7 @@ sealed interface Node { val lBracket: Keyword.LBracket, val contractEffects: List, val rBracket: Keyword.RBracket, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : PostModifier } @@ -1636,358 +1636,429 @@ sealed interface Node { */ sealed interface ValOrVarKeyword : Keyword - data class Class(override var tag: Any? = null) : Keyword, + data class Class(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, Declaration.ClassDeclaration.ClassOrInterfaceKeyword { override val text = "class" } - data class Interface(override var tag: Any? = null) : Keyword, + data class Interface(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, Declaration.ClassDeclaration.ClassOrInterfaceKeyword { override val text = "interface" } - data class Object(override var tag: Any? = null) : Keyword, + data class Object(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, Declaration.ClassOrObject.ClassDeclarationKeyword { override val text = "object" } - data class Constructor(override var tag: Any? = null) : Keyword { + data class Constructor(override val supplement: NodeSupplement = NodeSupplement()) : Keyword { override val text = "constructor" } - data class Val(override var tag: Any? = null) : Keyword, ValOrVarKeyword { + data class Val(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, ValOrVarKeyword { override val text = "val" } - data class Var(override var tag: Any? = null) : Keyword, ValOrVarKeyword { + data class Var(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, ValOrVarKeyword { override val text = "var" } - data class When(override var tag: Any? = null) : Keyword { + data class When(override val supplement: NodeSupplement = NodeSupplement()) : Keyword { override val text = "when" } - data class Field(override var tag: Any? = null) : Keyword, Modifier.AnnotationSet.AnnotationTarget { + data class Field(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.AnnotationSet.AnnotationTarget { override val text = "field" } - data class File(override var tag: Any? = null) : Keyword, Modifier.AnnotationSet.AnnotationTarget { + data class File(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.AnnotationSet.AnnotationTarget { override val text = "file" } - data class Property(override var tag: Any? = null) : Keyword, Modifier.AnnotationSet.AnnotationTarget { + data class Property(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.AnnotationSet.AnnotationTarget { override val text = "property" } - data class Get(override var tag: Any? = null) : Keyword, Modifier.AnnotationSet.AnnotationTarget { + data class Get(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.AnnotationSet.AnnotationTarget { override val text = "get" } - data class Set(override var tag: Any? = null) : Keyword, Modifier.AnnotationSet.AnnotationTarget { + data class Set(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.AnnotationSet.AnnotationTarget { override val text = "set" } - data class Receiver(override var tag: Any? = null) : Keyword, Modifier.AnnotationSet.AnnotationTarget { + data class Receiver(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.AnnotationSet.AnnotationTarget { override val text = "receiver" } - data class Param(override var tag: Any? = null) : Keyword, Modifier.AnnotationSet.AnnotationTarget { + data class Param(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.AnnotationSet.AnnotationTarget { override val text = "param" } - data class SetParam(override var tag: Any? = null) : Keyword, Modifier.AnnotationSet.AnnotationTarget { + data class SetParam(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.AnnotationSet.AnnotationTarget { override val text = "setparam" } - data class Delegate(override var tag: Any? = null) : Keyword, Modifier.AnnotationSet.AnnotationTarget { + data class Delegate(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.AnnotationSet.AnnotationTarget { override val text = "delegate" } - data class LPar(override var tag: Any? = null) : Keyword { + data class LPar(override val supplement: NodeSupplement = NodeSupplement()) : Keyword { override val text = "(" } - data class RPar(override var tag: Any? = null) : Keyword { + data class RPar(override val supplement: NodeSupplement = NodeSupplement()) : Keyword { override val text = ")" } - data class LBracket(override var tag: Any? = null) : Keyword { + data class LBracket(override val supplement: NodeSupplement = NodeSupplement()) : Keyword { override val text = "[" } - data class RBracket(override var tag: Any? = null) : Keyword { + data class RBracket(override val supplement: NodeSupplement = NodeSupplement()) : Keyword { override val text = "]" } - data class Arrow(override var tag: Any? = null) : Keyword { + data class Arrow(override val supplement: NodeSupplement = NodeSupplement()) : Keyword { override val text = "->" } - data class Asterisk(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class Asterisk(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "*" } - data class Slash(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class Slash(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "/" } - data class Percent(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class Percent(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "%" } - data class Plus(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator, + data class Plus(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator, Expression.UnaryExpression.UnaryOperator { override val text = "+" } - data class Minus(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator, + data class Minus(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator, Expression.UnaryExpression.UnaryOperator { override val text = "-" } - data class In(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator, + data class In(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator, Modifier.KeywordModifier, Expression.WhenExpression.WhenConditionRangeOperator { override val text = "in" } - data class NotIn(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator, + data class NotIn(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator, Expression.WhenExpression.WhenConditionRangeOperator { override val text = "!in" } - data class Less(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class Less(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "<" } - data class LessEqual(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class LessEqual(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "<=" } - data class Greater(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class Greater(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = ">" } - data class GreaterEqual(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class GreaterEqual(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = ">=" } - data class EqualEqual(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class EqualEqual(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "==" } - data class NotEqual(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class NotEqual(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "!=" } - data class AsteriskEqual(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class AsteriskEqual(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "*=" } - data class SlashEqual(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class SlashEqual(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "/=" } - data class PercentEqual(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class PercentEqual(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "%=" } - data class PlusEqual(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class PlusEqual(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "+=" } - data class MinusEqual(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class MinusEqual(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "-=" } - data class OrOr(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class OrOr(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "||" } - data class AndAnd(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class AndAnd(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "&&" } - data class QuestionColon(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class QuestionColon(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "?:" } - data class DotDot(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class DotDot(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = ".." } - data class DotDotLess(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class DotDotLess(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "..<" } - data class Dot(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class Dot(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "." } - data class QuestionDot(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class QuestionDot(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "?." } - data class Question(override var tag: Any? = null) : Keyword, Expression.BinaryExpression.BinaryOperator { + data class Question(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryExpression.BinaryOperator { override val text = "?" } - data class PlusPlus(override var tag: Any? = null) : Keyword, Expression.UnaryExpression.UnaryOperator { + data class PlusPlus(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.UnaryExpression.UnaryOperator { override val text = "++" } - data class MinusMinus(override var tag: Any? = null) : Keyword, Expression.UnaryExpression.UnaryOperator { + data class MinusMinus(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.UnaryExpression.UnaryOperator { override val text = "--" } - data class Not(override var tag: Any? = null) : Keyword, Expression.UnaryExpression.UnaryOperator { + data class Not(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.UnaryExpression.UnaryOperator { override val text = "!" } - data class NotNot(override var tag: Any? = null) : Keyword, Expression.UnaryExpression.UnaryOperator { + data class NotNot(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.UnaryExpression.UnaryOperator { override val text = "!!" } - data class As(override var tag: Any? = null) : Keyword, Expression.BinaryTypeExpression.BinaryTypeOperator { + data class As(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryTypeExpression.BinaryTypeOperator { override val text = "as" } - data class AsQuestion(override var tag: Any? = null) : Keyword, + data class AsQuestion(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, Expression.BinaryTypeExpression.BinaryTypeOperator { override val text = "as?" } - data class Is(override var tag: Any? = null) : Keyword, Expression.BinaryTypeExpression.BinaryTypeOperator, + data class Is(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryTypeExpression.BinaryTypeOperator, Expression.WhenExpression.WhenConditionTypeOperator { override val text = "is" } - data class NotIs(override var tag: Any? = null) : Keyword, Expression.BinaryTypeExpression.BinaryTypeOperator, + data class NotIs(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Expression.BinaryTypeExpression.BinaryTypeOperator, Expression.WhenExpression.WhenConditionTypeOperator { override val text = "!is" } - data class Abstract(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Abstract(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "abstract" } - data class Final(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Final(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "final" } - data class Open(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Open(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "open" } - data class Annotation(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Annotation(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "annotation" } - data class Sealed(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Sealed(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "sealed" } - data class Data(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Data(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "data" } - data class Override(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Override(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "override" } - data class LateInit(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class LateInit(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "lateinit" } - data class Inner(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Inner(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "inner" } - data class Enum(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Enum(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "enum" } - data class Companion(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Companion(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "companion" } - data class Value(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Value(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "value" } - data class Private(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Private(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "private" } - data class Protected(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Protected(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "protected" } - data class Public(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Public(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "public" } - data class Internal(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Internal(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "internal" } - data class Out(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Out(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, Modifier.KeywordModifier { override val text = "out" } - data class Noinline(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Noinline(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "noinline" } - data class CrossInline(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class CrossInline(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "crossinline" } - data class Vararg(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Vararg(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "vararg" } - data class Reified(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Reified(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "reified" } - data class TailRec(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class TailRec(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "tailrec" } - data class Operator(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Operator(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "operator" } - data class Infix(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Infix(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "infix" } - data class Inline(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Inline(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "inline" } - data class External(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class External(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "external" } - data class Suspend(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Suspend(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "suspend" } - data class Const(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Const(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "const" } - data class Fun(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Fun(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, Modifier.KeywordModifier { override val text = "fun" } - data class Actual(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Actual(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "actual" } - data class Expect(override var tag: Any? = null) : Keyword, Modifier.KeywordModifier { + data class Expect(override val supplement: NodeSupplement = NodeSupplement()) : Keyword, + Modifier.KeywordModifier { override val text = "expect" } } @@ -2003,7 +2074,7 @@ sealed interface Node { */ data class Whitespace( override val text: String, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Extra /** @@ -2013,7 +2084,7 @@ sealed interface Node { */ data class Comment( override val text: String, - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Extra /** @@ -2022,7 +2093,7 @@ sealed interface Node { * @property text always be ";". */ data class Semicolon( - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Extra { override val text = ";" } @@ -2033,7 +2104,7 @@ sealed interface Node { * @property text always be ",". */ data class TrailingComma( - override var tag: Any? = null, + override val supplement: NodeSupplement = NodeSupplement(), ) : Extra { override val text = "," } diff --git a/ast/src/commonMain/kotlin/ktast/ast/NodeSupplement.kt b/ast/src/commonMain/kotlin/ktast/ast/NodeSupplement.kt new file mode 100644 index 000000000..01691e07d --- /dev/null +++ b/ast/src/commonMain/kotlin/ktast/ast/NodeSupplement.kt @@ -0,0 +1,16 @@ +package ktast.ast + +/** + * Supplemental data for a node. + * + * @property extrasBefore list of extra nodes before the node. + * @property extrasWithin list of extra nodes within the node. + * @property extrasAfter list of extra nodes after the node. + * @property tag can be used to store per-node state if desired. + */ +data class NodeSupplement( + val extrasBefore: List = listOf(), + val extrasWithin: List = listOf(), + val extrasAfter: List = listOf(), + var tag: Any? = null +) \ No newline at end of file From 1a86b5994021236316e4aef0e21a7c0c2d8cc3c8 Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 18:18:32 +0900 Subject: [PATCH 02/13] Now extrasXXXs of NodeSupplement are mutable --- ast/src/commonMain/kotlin/ktast/ast/NodeSupplement.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ast/src/commonMain/kotlin/ktast/ast/NodeSupplement.kt b/ast/src/commonMain/kotlin/ktast/ast/NodeSupplement.kt index 01691e07d..daafb8fa2 100644 --- a/ast/src/commonMain/kotlin/ktast/ast/NodeSupplement.kt +++ b/ast/src/commonMain/kotlin/ktast/ast/NodeSupplement.kt @@ -9,8 +9,8 @@ package ktast.ast * @property tag can be used to store per-node state if desired. */ data class NodeSupplement( - val extrasBefore: List = listOf(), - val extrasWithin: List = listOf(), - val extrasAfter: List = listOf(), + var extrasBefore: List = listOf(), + var extrasWithin: List = listOf(), + var extrasAfter: List = listOf(), var tag: Any? = null ) \ No newline at end of file From ed750de9005165920165c442f1b5ec8575ef93ac Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 18:29:20 +0900 Subject: [PATCH 03/13] Use supplement instead of ExtrasMap --- .../ktast/ast/psi/ConverterWithExtras.kt | 24 ++++++++----------- .../ast/psi/ConverterWithMutableExtras.kt | 24 ------------------- .../kotlin/ktast/ast/MutableVisitorTest.kt | 10 ++++---- .../src/test/kotlin/ktast/ast/WriterTest.kt | 6 ++--- .../kotlin/ktast/ast/psi/ConverterTest.kt | 2 +- .../psi/CorpusParseAndWriteWithExtrasTest.kt | 4 ++-- ast/src/commonMain/kotlin/ktast/ast/Dumper.kt | 17 ++++++------- .../commonMain/kotlin/ktast/ast/ExtrasMap.kt | 21 ---------------- .../kotlin/ktast/ast/MutableExtrasMap.kt | 11 --------- .../kotlin/ktast/ast/MutableVisitor.kt | 16 +++---------- ast/src/commonMain/kotlin/ktast/ast/Writer.kt | 22 +++++++---------- 11 files changed, 39 insertions(+), 118 deletions(-) delete mode 100644 ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithMutableExtras.kt delete mode 100644 ast/src/commonMain/kotlin/ktast/ast/ExtrasMap.kt delete mode 100644 ast/src/commonMain/kotlin/ktast/ast/MutableExtrasMap.kt diff --git a/ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithExtras.kt b/ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithExtras.kt index 2335be217..e009b7806 100644 --- a/ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithExtras.kt +++ b/ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithExtras.kt @@ -1,6 +1,5 @@ package ktast.ast.psi -import ktast.ast.ExtrasMap import ktast.ast.Node import org.jetbrains.kotlin.com.intellij.psi.PsiComment import org.jetbrains.kotlin.com.intellij.psi.PsiElement @@ -11,30 +10,21 @@ import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.psiUtil.allChildren import org.jetbrains.kotlin.psi.psiUtil.siblings -import java.util.* -import kotlin.collections.ArrayDeque /** * Converts PSI elements to AST nodes and keeps track of extras. */ -open class ConverterWithExtras : Converter(), ExtrasMap { +open class ConverterWithExtras : Converter() { // Sometimes many nodes are created from the same element, but we only want the last node, i.e. the most ancestor node. // We remove the previous nodes we've found for the same identity when we see a new one. So we don't have to // keep PSI elements around, we hold a map to the element's identity hash code. Then we use that number to tie // to the extras to keep duplicates out. Usually using identity hash codes would be problematic due to // potential reuse, we know the PSI objects are all around at the same time, so it's good enough. protected val psiIdentitiesToNodes = mutableMapOf() - protected val extrasBefore = IdentityHashMap>() - protected val extrasWithin = IdentityHashMap>() - protected val extrasAfter = IdentityHashMap>() // This keeps track of ws nodes we've seen before so we don't duplicate them private val seenExtraPsiIdentities = mutableSetOf() - override fun extrasBefore(node: Node) = extrasBefore[node] ?: emptyList() - override fun extrasWithin(node: Node) = extrasWithin[node] ?: emptyList() - override fun extrasAfter(node: Node) = extrasAfter[node] ?: emptyList() - override fun onNode(node: Node, element: PsiElement) { // We ignore whitespace and comments here to prevent recursion if (element is PsiWhiteSpace || element is PsiComment) return @@ -106,21 +96,27 @@ open class ConverterWithExtras : Converter(), ExtrasMap { private fun fillExtrasBefore(node: Node) { convertExtras(extraElementsSinceLastNode).also { - if (it.isNotEmpty()) extrasBefore[node] = (extrasBefore[node] ?: listOf()) + it + if (it.isNotEmpty()) { + node.supplement.extrasBefore += it + } } extraElementsSinceLastNode.clear() } private fun fillExtrasAfter(node: Node) { convertExtras(extraElementsSinceLastNode).also { - if (it.isNotEmpty()) extrasAfter[node] = (extrasAfter[node] ?: listOf()) + it + if (it.isNotEmpty()) { + node.supplement.extrasAfter += it + } } extraElementsSinceLastNode.clear() } private fun fillExtrasWithin(node: Node) { convertExtras(extraElementsSinceLastNode).also { - if (it.isNotEmpty()) extrasWithin[node] = (extrasWithin[node] ?: listOf()) + it + if (it.isNotEmpty()) { + node.supplement.extrasWithin += it + } } extraElementsSinceLastNode.clear() } diff --git a/ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithMutableExtras.kt b/ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithMutableExtras.kt deleted file mode 100644 index 7c11d86f1..000000000 --- a/ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithMutableExtras.kt +++ /dev/null @@ -1,24 +0,0 @@ -package ktast.ast.psi - -import ktast.ast.MutableExtrasMap -import ktast.ast.Node - -/** - * Converts PSI elements to AST nodes and keeps track of extras. It also supports moving extras from one node to another. - */ -class ConverterWithMutableExtras : ConverterWithExtras(), MutableExtrasMap { - override fun moveExtras(fromNode: Node, toNode: Node) { - // Compare two nodes by identity using triple equals. - if (fromNode === toNode) { - return // do nothing - } - - extrasBefore[toNode] = extrasBefore[fromNode] - extrasWithin[toNode] = extrasWithin[fromNode] - extrasAfter[toNode] = extrasAfter[fromNode] - - extrasBefore.remove(fromNode) - extrasWithin.remove(fromNode) - extrasAfter.remove(fromNode) - } -} \ No newline at end of file diff --git a/ast-psi/src/test/kotlin/ktast/ast/MutableVisitorTest.kt b/ast-psi/src/test/kotlin/ktast/ast/MutableVisitorTest.kt index 970d9a265..52ef905e4 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/MutableVisitorTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/MutableVisitorTest.kt @@ -1,6 +1,6 @@ package ktast.ast -import ktast.ast.psi.ConverterWithMutableExtras +import ktast.ast.psi.ConverterWithExtras import ktast.ast.psi.Parser import org.junit.Test @@ -53,12 +53,12 @@ private fun assertMutateAndWriteExact( fn: (path: NodePath<*>) -> Node, expectedCode: String ) { - val origExtrasConv = ConverterWithMutableExtras() + val origExtrasConv = ConverterWithExtras() val origFile = Parser(origExtrasConv).parseFile(origCode) - println("----ORIG AST----\n${Dumper.dump(origFile, origExtrasConv)}\n------------") + println("----ORIG AST----\n${Dumper.dump(origFile)}\n------------") - val newFile = MutableVisitor.traverse(origFile, origExtrasConv, fn) - val newCode = Writer.write(newFile, origExtrasConv) + val newFile = MutableVisitor.traverse(origFile, fn) + val newCode = Writer.write(newFile) kotlin.test.assertEquals( expectedCode.trim(), newCode.trim(), diff --git a/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt b/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt index 1fcc0aa14..92f0006ef 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt @@ -237,13 +237,13 @@ class WriterTest { val origExtrasConv = ConverterWithExtras() val origFile = Parser(origExtrasConv).parseFile(origCode) - println("----ORIG AST----\n${Dumper.dump(origFile, origExtrasConv)}\n------------") + println("----ORIG AST----\n${Dumper.dump(origFile)}\n------------") - val newCode = Writer.write(origFile, origExtrasConv) + val newCode = Writer.write(origFile) assertEquals(origCode.trim(), newCode.trim(), "Parse -> Write for original code, not equal") val identityNode = MutableVisitor.traverse(origFile) { path -> path.node } - val identityCode = Writer.write(identityNode, origExtrasConv) + val identityCode = Writer.write(identityNode) assertEquals( origCode.trim(), diff --git a/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt b/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt index 608a669ea..f6ac5a0e4 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt @@ -64,7 +64,7 @@ class ConverterTest { private fun assertParsedAs(code: String, expectedDump: String) { val converter = ConverterWithExtras() val node = Parser(converter).parseFile(code) - val actualDump = Dumper.dump(node, converter, verbose = false) + val actualDump = Dumper.dump(node, verbose = false) assertEquals(expectedDump.trim(), actualDump.trim()) } diff --git a/ast-psi/src/test/kotlin/ktast/ast/psi/CorpusParseAndWriteWithExtrasTest.kt b/ast-psi/src/test/kotlin/ktast/ast/psi/CorpusParseAndWriteWithExtrasTest.kt index 38a5eba4c..b99daf801 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/psi/CorpusParseAndWriteWithExtrasTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/psi/CorpusParseAndWriteWithExtrasTest.kt @@ -25,9 +25,9 @@ class CorpusParseAndWriteWithExtrasTest(private val unit: Corpus.Unit) { val origCode = StringUtilRt.convertLineSeparators(unit.read()) // println("----ORIG CODE----\n$origCode\n------------") val origFile = Parser(origExtrasConv).parseFile(origCode) - println("----ORIG AST----\n${Dumper.dump(origFile, origExtrasConv)}\n------------") + println("----ORIG AST----\n${Dumper.dump(origFile)}\n------------") - val newCode = Writer.write(origFile, origExtrasConv) + val newCode = Writer.write(origFile) // println("----NEW CODE----\n$newCode\n-----------") assertEquals(origCode, newCode) diff --git a/ast/src/commonMain/kotlin/ktast/ast/Dumper.kt b/ast/src/commonMain/kotlin/ktast/ast/Dumper.kt index 706892bf7..92a3db283 100644 --- a/ast/src/commonMain/kotlin/ktast/ast/Dumper.kt +++ b/ast/src/commonMain/kotlin/ktast/ast/Dumper.kt @@ -20,7 +20,6 @@ package ktast.ast */ class Dumper( private val appendable: Appendable = StringBuilder(), - private val extrasMap: ExtrasMap? = null, private val verbose: Boolean = true, ) : Visitor() { @@ -29,12 +28,11 @@ class Dumper( * Dumps the given AST node. * * @param node root AST node to dump. - * @param extrasMap optional extras map, defaults to null. * @param verbose whether to dump extra node attributes, defaults to true. */ - fun dump(node: Node, extrasMap: ExtrasMap? = null, verbose: Boolean = true): String { + fun dump(node: Node, verbose: Boolean = true): String { val builder = StringBuilder() - Dumper(builder, extrasMap, verbose).dump(node) + Dumper(builder, verbose).dump(node) return builder.toString() } } @@ -61,20 +59,19 @@ class Dumper( } private fun NodePath<*>.writeExtrasBefore() { - if (extrasMap == null || parent == null) return - val extraPaths = extrasMap.extrasBefore(node).map { parent.childPathOf(it) } + if (parent == null) return + val extraPaths = node.supplement.extrasBefore.map { parent.childPathOf(it) } writeExtras(extraPaths, ExtraType.BEFORE) } private fun NodePath<*>.writeExtrasWithin() { - if (extrasMap == null) return - val extraPaths = extrasMap.extrasWithin(node).map { childPathOf(it) } + val extraPaths = node.supplement.extrasWithin.map { childPathOf(it) } writeExtras(extraPaths, ExtraType.WITHIN) } private fun NodePath<*>.writeExtrasAfter() { - if (extrasMap == null || parent == null) return - val extraPaths = extrasMap.extrasAfter(node).map { parent.childPathOf(it) } + if (parent == null) return + val extraPaths = node.supplement.extrasAfter.map { parent.childPathOf(it) } writeExtras(extraPaths, ExtraType.AFTER) } diff --git a/ast/src/commonMain/kotlin/ktast/ast/ExtrasMap.kt b/ast/src/commonMain/kotlin/ktast/ast/ExtrasMap.kt deleted file mode 100644 index 7a5176c73..000000000 --- a/ast/src/commonMain/kotlin/ktast/ast/ExtrasMap.kt +++ /dev/null @@ -1,21 +0,0 @@ -package ktast.ast - -/** - * Interface for mapping extras to nodes. - */ -interface ExtrasMap { - /** - * Returns extras before the given node. - */ - fun extrasBefore(node: Node): List - - /** - * Returns extras within the given node. - */ - fun extrasWithin(node: Node): List - - /** - * Returns extras after the given node. - */ - fun extrasAfter(node: Node): List -} \ No newline at end of file diff --git a/ast/src/commonMain/kotlin/ktast/ast/MutableExtrasMap.kt b/ast/src/commonMain/kotlin/ktast/ast/MutableExtrasMap.kt deleted file mode 100644 index a9239d3d1..000000000 --- a/ast/src/commonMain/kotlin/ktast/ast/MutableExtrasMap.kt +++ /dev/null @@ -1,11 +0,0 @@ -package ktast.ast - -/** - * Interface for mapping extras to nodes and moving extras between nodes. - */ -interface MutableExtrasMap : ExtrasMap { - /** - * Moves extras from one node to another. - */ - fun moveExtras(fromNode: Node, toNode: Node) -} \ No newline at end of file diff --git a/ast/src/commonMain/kotlin/ktast/ast/MutableVisitor.kt b/ast/src/commonMain/kotlin/ktast/ast/MutableVisitor.kt index 27dfff264..4f07abcbf 100644 --- a/ast/src/commonMain/kotlin/ktast/ast/MutableVisitor.kt +++ b/ast/src/commonMain/kotlin/ktast/ast/MutableVisitor.kt @@ -2,27 +2,21 @@ package ktast.ast /** * Visitor for AST nodes that can mutate the nodes. - * - * @param extrasMap optional extras map, defaults to null. */ -open class MutableVisitor( - protected val extrasMap: MutableExtrasMap? = null -) { +open class MutableVisitor { companion object { /** * Traverse the given AST node and its descendants depth-first order and calls the given callback function for each node. When the callback function returns a different node, the node is replaced with the returned node. * * @param rootNode root AST node to traverse. - * @param extrasMap optional extras map, defaults to null. * @param callback callback function to be called for each node. * @return the modified root node. */ fun traverse( rootNode: T, - extrasMap: MutableExtrasMap? = null, callback: (path: NodePath<*>) -> Node ): T = - object : MutableVisitor(extrasMap) { + object : MutableVisitor() { override fun preVisit(path: NodePath): C = callback(path) as C }.traverse(rootNode) } @@ -484,11 +478,7 @@ open class MutableVisitor( protected fun NodePath<*>.visitChildren(v: T, ch: ChangedRef): T = if (v != null) { - visit(childPathOf(v), ch).also { new -> - if (ch.changed) { - extrasMap?.moveExtras(v, new) - } - } + visit(childPathOf(v), ch) } else { v } diff --git a/ast/src/commonMain/kotlin/ktast/ast/Writer.kt b/ast/src/commonMain/kotlin/ktast/ast/Writer.kt index bf91b909c..ab6e9646a 100644 --- a/ast/src/commonMain/kotlin/ktast/ast/Writer.kt +++ b/ast/src/commonMain/kotlin/ktast/ast/Writer.kt @@ -12,30 +12,27 @@ package ktast.ast * ``` */ open class Writer( - protected val appendable: Appendable = StringBuilder(), - protected val extrasMap: ExtrasMap? = null + protected val appendable: Appendable = StringBuilder() ) : Visitor() { companion object { /** * Converts the given AST node back to source code. * * @param rootNode root AST node to convert. - * @param extrasMap optional extras map, defaults to null. * @return source code. */ - fun write(rootNode: Node, extrasMap: ExtrasMap? = null): String = - write(rootNode, StringBuilder(), extrasMap).toString() + fun write(rootNode: Node): String = + write(rootNode, StringBuilder()).toString() /** * Converts the given AST node back to source code. * * @param rootNode root AST node to convert. * @param appendable appendable to write to. - * @param extrasMap optional extras map, defaults to null. * @return appendable. */ - fun write(rootNode: Node, appendable: T, extrasMap: ExtrasMap? = null): T = - appendable.also { Writer(it, extrasMap).write(rootNode) } + fun write(rootNode: Node, appendable: T): T = + appendable.also { Writer(it).write(rootNode) } } protected val extrasSinceLastNonSymbol = mutableListOf() @@ -701,18 +698,15 @@ open class Writer( } protected open fun NodePath<*>.writeExtrasBefore() { - if (extrasMap == null) return - writeExtras(extrasMap.extrasBefore(node)) + writeExtras(node.supplement.extrasBefore) } protected open fun NodePath<*>.writeExtrasWithin() { - if (extrasMap == null) return - writeExtras(extrasMap.extrasWithin(node)) + writeExtras(node.supplement.extrasWithin) } protected open fun NodePath<*>.writeExtrasAfter() { - if (extrasMap == null) return - writeExtras(extrasMap.extrasAfter(node)) + writeExtras(node.supplement.extrasAfter) } protected open fun NodePath<*>.writeExtras(extras: List) { From 4bdb35f24ddc6f009b0905b1cd30b45cdca9557b Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 19:45:30 +0900 Subject: [PATCH 04/13] Add withExtras option to Writer --- .../src/test/kotlin/ktast/ast/WriterTest.kt | 42 +++++++++++++++++++ ast/src/commonMain/kotlin/ktast/ast/Writer.kt | 22 ++++++---- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt b/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt index 92f0006ef..f09d77317 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt @@ -6,6 +6,39 @@ import org.junit.Test import kotlin.test.assertEquals class WriterTest { + + @Test + fun writeWithExtras() { + assertParseAndWrite( + """ + fun x() { + // do nothing + } + """.trimIndent(), + """ + fun x() { + // do nothing + } + """.trimIndent(), + withExtras = true + ) + } + + @Test + fun writeWithoutExtras() { + assertParseAndWrite( + """ + fun x() { + // do nothing + } + """.trimIndent(), + """ + fun x(){} + """.trimIndent(), + withExtras = false + ) + } + @Test fun testIdentifierUnderscoreEscape() { assertParseAndWriteExact("const val c = FOO_BAR") @@ -252,4 +285,13 @@ class WriterTest { ) } + private fun assertParseAndWrite(origCode: String, expectedCode: String, withExtras: Boolean) { + val origExtrasConv = ConverterWithExtras() + val origFile = Parser(origExtrasConv).parseFile(origCode) + println("----ORIG AST----\n${Dumper.dump(origFile)}\n------------") + + val newCode = Writer.write(origFile, withExtras) + assertEquals(expectedCode.trim(), newCode.trim(), "Parse -> Write for original code, not equal") + } + } \ No newline at end of file diff --git a/ast/src/commonMain/kotlin/ktast/ast/Writer.kt b/ast/src/commonMain/kotlin/ktast/ast/Writer.kt index ab6e9646a..4698a16fe 100644 --- a/ast/src/commonMain/kotlin/ktast/ast/Writer.kt +++ b/ast/src/commonMain/kotlin/ktast/ast/Writer.kt @@ -1,38 +1,41 @@ package ktast.ast /** - * Writer that converts AST nodes back to source code. When the [extrasMap] is provided, it will preserve the original whitespaces, comments, semicolons and trailing commas. Even when the [extrasMap] is not provided, it will still insert whitespaces and semicolons automatically to avoid compilation errors or loss of meaning. + * Writer that converts AST nodes back to source code. When the [withExtras] is true, which is the default, it will preserve the original whitespaces, comments, semicolons and trailing commas. Even when the [withExtras] is false, it will still insert minimal whitespaces and semicolons automatically to avoid compilation errors or loss of meaning. * * Example usage: * ``` - * // Without extras map + * // Write with extras * val source = Writer.write(node) - * // With extras map - * val sourceWithExtras = Writer.write(node, extrasMap) + * // Write without extras + * val sourceWithoutExtras = Writer.write(node, withExtras = false) * ``` */ open class Writer( - protected val appendable: Appendable = StringBuilder() + protected val appendable: Appendable = StringBuilder(), + protected val withExtras: Boolean = true, ) : Visitor() { companion object { /** * Converts the given AST node back to source code. * * @param rootNode root AST node to convert. + * @param withExtras whether to write extra nodes such as comments and whitespaces, defaults to true. * @return source code. */ - fun write(rootNode: Node): String = - write(rootNode, StringBuilder()).toString() + fun write(rootNode: Node, withExtras: Boolean = true): String = + write(rootNode, StringBuilder(), withExtras).toString() /** * Converts the given AST node back to source code. * * @param rootNode root AST node to convert. * @param appendable appendable to write to. + * @param withExtras whether to write extra nodes such as comments and whitespaces, defaults to true. * @return appendable. */ - fun write(rootNode: Node, appendable: T): T = - appendable.also { Writer(it).write(rootNode) } + fun write(rootNode: Node, appendable: T, withExtras: Boolean = true): T = + appendable.also { Writer(it, withExtras).write(rootNode) } } protected val extrasSinceLastNonSymbol = mutableListOf() @@ -710,6 +713,7 @@ open class Writer( } protected open fun NodePath<*>.writeExtras(extras: List) { + if (!withExtras) return extras.forEach { append(it.text) } From 5d26cea33bfbec8c0108011986f10cd724e958d3 Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 19:46:39 +0900 Subject: [PATCH 05/13] Refactor test cases to use string corpus --- .../src/test/kotlin/ktast/ast/WriterTest.kt | 246 ------------------ .../src/test/kotlin/ktast/ast/psi/Corpus.kt | 135 +++++++++- 2 files changed, 132 insertions(+), 249 deletions(-) diff --git a/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt b/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt index f09d77317..7739b8651 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt @@ -39,252 +39,6 @@ class WriterTest { ) } - @Test - fun testIdentifierUnderscoreEscape() { - assertParseAndWriteExact("const val c = FOO_BAR") - assertParseAndWriteExact("const val c = _FOOBAR") - assertParseAndWriteExact("const val c = FOOBAR_") - assertParseAndWriteExact("const val c = `___`") - } - - @Test - fun testTypeParameterModifiers() { - assertParseAndWriteExact( - """ - fun delete(p: Array?) {} - """.trimIndent() - ) - } - - @Test - fun testSimpleCharacterEscaping() { - assertParseAndWriteExact("""val x = "input\b\nTest\t\r\u3000\'\"\\\${'$'}"""") - } - - @Test - fun testSuperclassPrimaryConstructor() { - assertParseAndWriteExact("private object SubnetSorter : DefaultSorter()") - } - - @Test - fun testOpType() { - assertParseAndWriteExact("""val x = "" as String""") - } - - @Test - fun testComments() { - assertParseAndWriteExact("""val x = "" // x is empty""") - } - - @Test - fun testEmptyLines() { - assertParseAndWriteExact( - """ - val x = "" - - val y = 0 - """.trimIndent() - ) - } - - @Test - fun testFunctionBlock() { - assertParseAndWriteExact( - """ - fun setup() { - // do something - val x = "" - val y = 3 - // last - } - """.trimIndent() - ) - } - - @Test - fun testFunctionBlockHavingOnlyComment() { - assertParseAndWriteExact( - """ - fun setup() { - // do something - } - """.trimIndent() - ) - } - - @Test - fun testLambdaExpression() { - assertParseAndWriteExact( - """ - fun setup() { - run { - // do something - val x = "" - } - } - """.trimIndent() - ) - } - - @Test - fun testLambdaExpressionHavingOnlyComment() { - assertParseAndWriteExact( - """ - fun setup() { - run { - // do something - } - } - """.trimIndent() - ) - } - - @Test - fun testLongPackageName() { - assertParseAndWriteExact("package foo.bar.baz.buzz") - } - - @Test - fun testFunctionModifier() { - assertParseAndWriteExact( - """ - private fun setup() {} - """.trimIndent() - ) - } - - @Test - fun testSemicolonAfterIf() { - assertParseAndWriteExact( - """ - fun foo(a: Int): Int { var x = a; var y = x++; if (y+1 != x) return -1; return x; } - """.trimIndent() - ) - } - - @Test - fun testQuotedIdentifiers() { - assertParseAndWriteExact( - """ - @`return` fun `package`() { - `class`() - } - """.trimIndent() - ) - } - - @Test - fun testConstructorModifiers() { - assertParseAndWriteExact( - """ - class Foo @[foo] private @[bar()] () - """.trimIndent() - ) - } - - @Test - fun testSecondaryConstructor() { - assertParseAndWriteExact( - """ - class Foo { - @annot protected constructor(x: Int, y: Int) : this(1,2) {} - } - """.trimIndent() - ) - } - - @Test - fun testFunctionWithFunctionReceiver() { - assertParseAndWriteExact( - """ - fun (@[a] T.(A) -> Unit).foo() - fun @[a] (@[a] T.(A) -> R).foo() {} - """.trimIndent() - ) - } - - @Test - fun testTypeModifiers() { - assertParseAndWriteExact( - """ - val p1:suspend a - val p2: suspend (a) -> a - val p5: (suspend a).() -> a - val p6: a - val p15: suspend (suspend (() -> Unit)) -> Unit - @a fun @a a.f1() {} - """.trimIndent() - ) - } - - @Test - fun testBy() { - assertParseAndWriteExact( - """ - class Runnable(a : doo = 0) : foo(d=0), bar by x, bar { - } - """.trimIndent() - ) - } - - @Test - fun testContextReceivers() { - assertParseAndWriteExact( - """ - typealias f = context(T, X) (a: @[a] a) -> b - """.trimIndent() - ) - } - - @Test - fun testObjectLiteralExpression() { - assertParseAndWriteExact( - """ - val foo = object : Bar, Baz { - fun foo() {} - } - """.trimIndent() - ) - } - - @Test - fun testFuncTypeParentheses() { - assertParseAndWriteExact( - """ - val lambdaType: (@A() (() -> C)) - """.trimIndent() - ) - } - - @Test - fun testNullableTypeParentheses() { - assertParseAndWriteExact( - """ - val temp3: (suspend (String) -> Int)? = { 5 } - val temp4: ((((String) -> Int)?) -> Int)? = { 5 } - """.trimIndent() - ) - } - - private fun assertParseAndWriteExact(origCode: String) { - - val origExtrasConv = ConverterWithExtras() - val origFile = Parser(origExtrasConv).parseFile(origCode) - println("----ORIG AST----\n${Dumper.dump(origFile)}\n------------") - - val newCode = Writer.write(origFile) - assertEquals(origCode.trim(), newCode.trim(), "Parse -> Write for original code, not equal") - - val identityNode = MutableVisitor.traverse(origFile) { path -> path.node } - val identityCode = Writer.write(identityNode) - - assertEquals( - origCode.trim(), - identityCode.trim(), - "Parse -> Identity Transform -> Write for original code, not equal" - ) - } - private fun assertParseAndWrite(origCode: String, expectedCode: String, withExtras: Boolean) { val origExtrasConv = ConverterWithExtras() val origFile = Parser(origExtrasConv).parseFile(origCode) diff --git a/ast-psi/src/test/kotlin/ktast/ast/psi/Corpus.kt b/ast-psi/src/test/kotlin/ktast/ast/psi/Corpus.kt index d864614b6..a20c22c7a 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/psi/Corpus.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/psi/Corpus.kt @@ -11,7 +11,11 @@ object Corpus { Paths.get("kdoc", "Simple.kt") to listOf("Unclosed comment") ) - val default by lazy { localTestData + kotlinRepoTestData } + val default: List by lazy { localTestData + kotlinRepoTestData + stringTestData } + + private val localTestData by lazy { + loadTestDataFromDir(File(javaClass.getResource("/localTestData").toURI()).toPath(), canSkip = false) + } private val kotlinRepoTestData by lazy { // Recursive from $KOTLIN_REPO/compiler/testData/psi/**/*.kt @@ -23,8 +27,133 @@ object Corpus { loadTestDataFromDir(dir, canSkip = true) } - private val localTestData by lazy { - loadTestDataFromDir(File(javaClass.getResource("/localTestData").toURI()).toPath(), canSkip = false) + private val stringTestData by lazy { + listOf( + Unit.FromString("identifier including underscore", "const val c = FOO_BAR"), + Unit.FromString("identifier including underscore", "const val c = _FOOBAR"), + Unit.FromString("identifier including underscore", "const val c = FOOBAR_"), + Unit.FromString("identifier including underscore", "const val c = `___`"), + Unit.FromString("type parameter modifiers", "fun delete(p: Array?) {}"), + Unit.FromString("simple character escaping", """val x = "input\b\nTest\t\r\u3000\'\"\\\${'$'}""""), + Unit.FromString("superclass primary constructor", "private object SubnetSorter : DefaultSorter()"), + Unit.FromString("type operator", """val x = "" as String"""), + Unit.FromString("comments", """val x = "" // x is empty"""), + Unit.FromString( + "empty lines", + """ + val x = "" + + val y = 0 + """.trimIndent() + ), + Unit.FromString( + "function block", + + """ + fun setup() { + // do something + val x = "" + val y = 3 + // last + } + """.trimIndent() + ), + Unit.FromString( + "function block having only comment", + """ + fun setup() { + // do something + } + """.trimIndent() + ), + Unit.FromString( + "lambda expression", + """ + fun setup() { + run { + // do something + val x = "" + } + } + """.trimIndent() + ), + Unit.FromString( + "lambda expression having only comment", + """ + fun setup() { + run { + // do something + } + } + """.trimIndent() + ), + Unit.FromString("long package name", "package foo.bar.baz.buzz"), + Unit.FromString("function modifier", "private fun setup() {}"), + Unit.FromString( + "semicolon after if", + "fun foo(a: Int): Int { var x = a; var y = x++; if (y+1 != x) return -1; return x; }" + ), + Unit.FromString( + "quoted identifiers", + """ + @`return` fun `package`() { + `class`() + } + """.trimIndent() + ), + Unit.FromString("constructor modifiers", "class Foo @[foo] private @[bar()] ()"), + Unit.FromString( + "secondary constructor", + """ + class Foo { + @annot protected constructor(x: Int, y: Int) : this(1,2) {} + } + """.trimIndent() + ), + Unit.FromString( + "function receiver", + + """ + fun (@[a] T.(A) -> Unit).foo() + fun @[a] (@[a] T.(A) -> R).foo() {} + """.trimIndent() + ), + Unit.FromString( + "type modifiers", + """ + val p1:suspend a + val p2: suspend (a) -> a + val p5: (suspend a).() -> a + val p6: a + val p15: suspend (suspend (() -> Unit)) -> Unit + @a fun @a a.f1() {} + """.trimIndent() + ), + Unit.FromString( + "by", + """ + class Runnable(a : doo = 0) : foo(d=0), bar by x, bar { + } + """.trimIndent() + ), + Unit.FromString("context receivers", "typealias f = context(T, X) (a: @[a] a) -> b"), + Unit.FromString( + "object literal expression", + """ + val foo = object : Bar, Baz { + fun foo() {} + } + """.trimIndent() + ), + Unit.FromString("function type with parentheses", "val lambdaType: (@A() (() -> C))"), + Unit.FromString( + "nullable type parentheses", + """ + val temp3: (suspend (String) -> Int)? = { 5 } + val temp4: ((((String) -> Int)?) -> Int)? = { 5 } + """.trimIndent() + ) + ) } private fun loadTestDataFromDir(root: Path, canSkip: Boolean) = Files.walk(root) From 65b71e69ed9e869ba01eaccd69293f9c55d33dad Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 19:56:36 +0900 Subject: [PATCH 06/13] Rename Dumper.verbose to withProperties --- ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt | 2 +- ast/src/commonMain/kotlin/ktast/ast/Dumper.kt | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt b/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt index f6ac5a0e4..9cee04c48 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt @@ -64,7 +64,7 @@ class ConverterTest { private fun assertParsedAs(code: String, expectedDump: String) { val converter = ConverterWithExtras() val node = Parser(converter).parseFile(code) - val actualDump = Dumper.dump(node, verbose = false) + val actualDump = Dumper.dump(node, withProperties = false) assertEquals(expectedDump.trim(), actualDump.trim()) } diff --git a/ast/src/commonMain/kotlin/ktast/ast/Dumper.kt b/ast/src/commonMain/kotlin/ktast/ast/Dumper.kt index 92a3db283..592e967e0 100644 --- a/ast/src/commonMain/kotlin/ktast/ast/Dumper.kt +++ b/ast/src/commonMain/kotlin/ktast/ast/Dumper.kt @@ -20,7 +20,7 @@ package ktast.ast */ class Dumper( private val appendable: Appendable = StringBuilder(), - private val verbose: Boolean = true, + private val withProperties: Boolean = true, ) : Visitor() { companion object { @@ -28,11 +28,11 @@ class Dumper( * Dumps the given AST node. * * @param node root AST node to dump. - * @param verbose whether to dump extra node attributes, defaults to true. + * @param withProperties whether to dump node properties, defaults to true. */ - fun dump(node: Node, verbose: Boolean = true): String { + fun dump(node: Node, withProperties: Boolean = true): String { val builder = StringBuilder() - Dumper(builder, verbose).dump(node) + Dumper(builder, withProperties = withProperties).dump(node) return builder.toString() } } @@ -90,7 +90,7 @@ class Dumper( appendable.append(" ".repeat(level)) appendable.append(prefix) appendable.append(node::class.qualifiedName?.substring(10)) // 10 means length of "ktast.ast." - if (verbose) { + if (withProperties) { node.apply { when (this) { is Node.Expression.StringLiteralExpression -> mapOf("raw" to raw) From a9c117829ce4d7922c87fc5a2f498384eae069b3 Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 20:10:02 +0900 Subject: [PATCH 07/13] Add Dumper.withExtras --- .../src/test/kotlin/ktast/ast/DumperTest.kt | 103 ++++++++++++++++++ ast/src/commonMain/kotlin/ktast/ast/Dumper.kt | 11 +- 2 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 ast-psi/src/test/kotlin/ktast/ast/DumperTest.kt diff --git a/ast-psi/src/test/kotlin/ktast/ast/DumperTest.kt b/ast-psi/src/test/kotlin/ktast/ast/DumperTest.kt new file mode 100644 index 000000000..1f0ab5120 --- /dev/null +++ b/ast-psi/src/test/kotlin/ktast/ast/DumperTest.kt @@ -0,0 +1,103 @@ +package ktast.ast + +import ktast.ast.psi.ConverterWithExtras +import ktast.ast.psi.Parser +import org.junit.Test +import kotlin.test.assertEquals + +class DumperTest { + + private val code = """ + val x = { + // x is empty + } + """.trimIndent() + + @Test + fun testWithExtrasAndProperties() { + assertDumpedAs( + code, + """ + Node.KotlinFile + Node.Declaration.PropertyDeclaration + Node.Keyword.Val{text="val"} + Node.Variable + BEFORE: Node.Extra.Whitespace{text=" "} + Node.Expression.NameExpression{text="x"} + AFTER: Node.Extra.Whitespace{text=" "} + BEFORE: Node.Extra.Whitespace{text=" "} + Node.Expression.LambdaExpression + WITHIN: Node.Extra.Whitespace{text="\n "} + WITHIN: Node.Extra.Comment{text="// x is empty"} + WITHIN: Node.Extra.Whitespace{text="\n"} + """.trimIndent(), + withExtras = true, + withProperties = true, + ) + } + + @Test + fun testWithExtrasButWithoutProperties() { + assertDumpedAs( + code, + """ + Node.KotlinFile + Node.Declaration.PropertyDeclaration + Node.Keyword.Val + Node.Variable + BEFORE: Node.Extra.Whitespace + Node.Expression.NameExpression + AFTER: Node.Extra.Whitespace + BEFORE: Node.Extra.Whitespace + Node.Expression.LambdaExpression + WITHIN: Node.Extra.Whitespace + WITHIN: Node.Extra.Comment + WITHIN: Node.Extra.Whitespace + """.trimIndent(), + withExtras = true, + withProperties = false, + ) + } + + @Test + fun testWithoutExtrasButWithProperties() { + assertDumpedAs( + code, + """ + Node.KotlinFile + Node.Declaration.PropertyDeclaration + Node.Keyword.Val{text="val"} + Node.Variable + Node.Expression.NameExpression{text="x"} + Node.Expression.LambdaExpression + """.trimIndent(), + withExtras = false, + withProperties = true, + ) + } + + @Test + fun testWithoutExtrasOrProperties() { + assertDumpedAs( + code, + """ + Node.KotlinFile + Node.Declaration.PropertyDeclaration + Node.Keyword.Val + Node.Variable + Node.Expression.NameExpression + Node.Expression.LambdaExpression + """.trimIndent(), + withExtras = false, + withProperties = false, + ) + } + + private fun assertDumpedAs(code: String, expectedDump: String, withExtras: Boolean, withProperties: Boolean) { + val converter = ConverterWithExtras() + val node = Parser(converter).parseFile(code) + val actualDump = Dumper.dump(node, withExtras = withExtras, withProperties = withProperties) + assertEquals(expectedDump.trim(), actualDump.trim()) + } + +} \ No newline at end of file diff --git a/ast/src/commonMain/kotlin/ktast/ast/Dumper.kt b/ast/src/commonMain/kotlin/ktast/ast/Dumper.kt index 592e967e0..7a78baf29 100644 --- a/ast/src/commonMain/kotlin/ktast/ast/Dumper.kt +++ b/ast/src/commonMain/kotlin/ktast/ast/Dumper.kt @@ -20,6 +20,7 @@ package ktast.ast */ class Dumper( private val appendable: Appendable = StringBuilder(), + private val withExtras: Boolean = true, private val withProperties: Boolean = true, ) : Visitor() { @@ -28,11 +29,12 @@ class Dumper( * Dumps the given AST node. * * @param node root AST node to dump. + * @param withExtras whether to dump extra nodes, defaults to true. * @param withProperties whether to dump node properties, defaults to true. */ - fun dump(node: Node, withProperties: Boolean = true): String { + fun dump(node: Node, withExtras: Boolean = true, withProperties: Boolean = true): String { val builder = StringBuilder() - Dumper(builder, withProperties = withProperties).dump(node) + Dumper(builder, withExtras = withExtras, withProperties = withProperties).dump(node) return builder.toString() } } @@ -59,18 +61,19 @@ class Dumper( } private fun NodePath<*>.writeExtrasBefore() { - if (parent == null) return + if (!withExtras || parent == null) return val extraPaths = node.supplement.extrasBefore.map { parent.childPathOf(it) } writeExtras(extraPaths, ExtraType.BEFORE) } private fun NodePath<*>.writeExtrasWithin() { + if (!withExtras) return val extraPaths = node.supplement.extrasWithin.map { childPathOf(it) } writeExtras(extraPaths, ExtraType.WITHIN) } private fun NodePath<*>.writeExtrasAfter() { - if (parent == null) return + if (!withExtras || parent == null) return val extraPaths = node.supplement.extrasAfter.map { parent.childPathOf(it) } writeExtras(extraPaths, ExtraType.AFTER) } From 3b58c0f03d9cd0c1703e6d235f9150f079a2aba8 Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 20:14:46 +0900 Subject: [PATCH 08/13] Add test of Converter and ConverterWithExtras --- .../kotlin/ktast/ast/psi/ConverterTest.kt | 11 +-- .../ktast/ast/psi/ConverterWithExtrasTest.kt | 71 +++++++++++++++++++ 2 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 ast-psi/src/test/kotlin/ktast/ast/psi/ConverterWithExtrasTest.kt diff --git a/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt b/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt index 9cee04c48..daffcdea3 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterTest.kt @@ -17,10 +17,7 @@ class ConverterTest { Node.Declaration.PropertyDeclaration Node.Keyword.Val Node.Variable - BEFORE: Node.Extra.Whitespace Node.Expression.NameExpression - AFTER: Node.Extra.Whitespace - BEFORE: Node.Extra.Whitespace Node.Expression.StringLiteralExpression """.trimIndent() ) @@ -37,13 +34,8 @@ class ConverterTest { Node.Declaration.PropertyDeclaration Node.Keyword.Val Node.Variable - BEFORE: Node.Extra.Whitespace Node.Expression.NameExpression - AFTER: Node.Extra.Whitespace - BEFORE: Node.Extra.Whitespace Node.Expression.StringLiteralExpression - AFTER: Node.Extra.Whitespace - AFTER: Node.Extra.Comment """.trimIndent() ) } @@ -56,13 +48,12 @@ class ConverterTest { """.trimIndent(), """ Node.KotlinFile - WITHIN: Node.Extra.Comment """.trimIndent() ) } private fun assertParsedAs(code: String, expectedDump: String) { - val converter = ConverterWithExtras() + val converter = Converter() val node = Parser(converter).parseFile(code) val actualDump = Dumper.dump(node, withProperties = false) assertEquals(expectedDump.trim(), actualDump.trim()) diff --git a/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterWithExtrasTest.kt b/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterWithExtrasTest.kt new file mode 100644 index 000000000..cd9c46d70 --- /dev/null +++ b/ast-psi/src/test/kotlin/ktast/ast/psi/ConverterWithExtrasTest.kt @@ -0,0 +1,71 @@ +package ktast.ast.psi + +import ktast.ast.Dumper +import org.junit.Test +import kotlin.test.assertEquals + +class ConverterWithExtrasTest { + + @Test + fun testDeclaration() { + assertParsedAs( + """ + val x = "" + """.trimIndent(), + """ + Node.KotlinFile + Node.Declaration.PropertyDeclaration + Node.Keyword.Val + Node.Variable + BEFORE: Node.Extra.Whitespace + Node.Expression.NameExpression + AFTER: Node.Extra.Whitespace + BEFORE: Node.Extra.Whitespace + Node.Expression.StringLiteralExpression + """.trimIndent() + ) + } + + @Test + fun testInlineComment() { + assertParsedAs( + """ + val x = "" // x is empty + """.trimIndent(), + """ + Node.KotlinFile + Node.Declaration.PropertyDeclaration + Node.Keyword.Val + Node.Variable + BEFORE: Node.Extra.Whitespace + Node.Expression.NameExpression + AFTER: Node.Extra.Whitespace + BEFORE: Node.Extra.Whitespace + Node.Expression.StringLiteralExpression + AFTER: Node.Extra.Whitespace + AFTER: Node.Extra.Comment + """.trimIndent() + ) + } + + @Test + fun testCommentOnly() { + assertParsedAs( + """ + // file is empty + """.trimIndent(), + """ + Node.KotlinFile + WITHIN: Node.Extra.Comment + """.trimIndent() + ) + } + + private fun assertParsedAs(code: String, expectedDump: String) { + val converter = ConverterWithExtras() + val node = Parser(converter).parseFile(code) + val actualDump = Dumper.dump(node, withProperties = false) + assertEquals(expectedDump.trim(), actualDump.trim()) + } + +} \ No newline at end of file From 696b5834f393c87596d33c482d9edcbd282f2a3f Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 20:20:18 +0900 Subject: [PATCH 09/13] Now Parser use ConverterWithExtras by default --- ast-psi/src/main/kotlin/ktast/ast/psi/Parser.kt | 2 +- ast-psi/src/test/kotlin/ktast/ast/DumperTest.kt | 4 +--- ast-psi/src/test/kotlin/ktast/ast/MutableVisitorTest.kt | 4 +--- ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt | 4 +--- .../kotlin/ktast/ast/psi/CorpusParseAndWriteWithExtrasTest.kt | 3 +-- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/ast-psi/src/main/kotlin/ktast/ast/psi/Parser.kt b/ast-psi/src/main/kotlin/ktast/ast/psi/Parser.kt index 1250ab017..cd198ca10 100644 --- a/ast-psi/src/main/kotlin/ktast/ast/psi/Parser.kt +++ b/ast-psi/src/main/kotlin/ktast/ast/psi/Parser.kt @@ -17,7 +17,7 @@ import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType /** * Parses Kotlin source codes into PSI and converts it to AST. */ -open class Parser(protected val converter: Converter = Converter()) { +open class Parser(protected val converter: Converter = ConverterWithExtras()) { companion object : Parser() { init { // To hide annoying warning on Windows diff --git a/ast-psi/src/test/kotlin/ktast/ast/DumperTest.kt b/ast-psi/src/test/kotlin/ktast/ast/DumperTest.kt index 1f0ab5120..84e008280 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/DumperTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/DumperTest.kt @@ -1,6 +1,5 @@ package ktast.ast -import ktast.ast.psi.ConverterWithExtras import ktast.ast.psi.Parser import org.junit.Test import kotlin.test.assertEquals @@ -94,8 +93,7 @@ class DumperTest { } private fun assertDumpedAs(code: String, expectedDump: String, withExtras: Boolean, withProperties: Boolean) { - val converter = ConverterWithExtras() - val node = Parser(converter).parseFile(code) + val node = Parser.parseFile(code) val actualDump = Dumper.dump(node, withExtras = withExtras, withProperties = withProperties) assertEquals(expectedDump.trim(), actualDump.trim()) } diff --git a/ast-psi/src/test/kotlin/ktast/ast/MutableVisitorTest.kt b/ast-psi/src/test/kotlin/ktast/ast/MutableVisitorTest.kt index 52ef905e4..86cea72e8 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/MutableVisitorTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/MutableVisitorTest.kt @@ -1,6 +1,5 @@ package ktast.ast -import ktast.ast.psi.ConverterWithExtras import ktast.ast.psi.Parser import org.junit.Test @@ -53,8 +52,7 @@ private fun assertMutateAndWriteExact( fn: (path: NodePath<*>) -> Node, expectedCode: String ) { - val origExtrasConv = ConverterWithExtras() - val origFile = Parser(origExtrasConv).parseFile(origCode) + val origFile = Parser.parseFile(origCode) println("----ORIG AST----\n${Dumper.dump(origFile)}\n------------") val newFile = MutableVisitor.traverse(origFile, fn) diff --git a/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt b/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt index 7739b8651..e6eb9948b 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/WriterTest.kt @@ -1,6 +1,5 @@ package ktast.ast -import ktast.ast.psi.ConverterWithExtras import ktast.ast.psi.Parser import org.junit.Test import kotlin.test.assertEquals @@ -40,8 +39,7 @@ class WriterTest { } private fun assertParseAndWrite(origCode: String, expectedCode: String, withExtras: Boolean) { - val origExtrasConv = ConverterWithExtras() - val origFile = Parser(origExtrasConv).parseFile(origCode) + val origFile = Parser.parseFile(origCode) println("----ORIG AST----\n${Dumper.dump(origFile)}\n------------") val newCode = Writer.write(origFile, withExtras) diff --git a/ast-psi/src/test/kotlin/ktast/ast/psi/CorpusParseAndWriteWithExtrasTest.kt b/ast-psi/src/test/kotlin/ktast/ast/psi/CorpusParseAndWriteWithExtrasTest.kt index b99daf801..f883b12ed 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/psi/CorpusParseAndWriteWithExtrasTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/psi/CorpusParseAndWriteWithExtrasTest.kt @@ -18,13 +18,12 @@ class CorpusParseAndWriteWithExtrasTest(private val unit: Corpus.Unit) { // In order to test, we parse the test code (failing and validating errors if present), // convert to our AST, write out our AST, and compare try { - val origExtrasConv = ConverterWithExtras() if (unit is Corpus.Unit.FromFile) { println("Loading ${unit.fullPath}") } val origCode = StringUtilRt.convertLineSeparators(unit.read()) // println("----ORIG CODE----\n$origCode\n------------") - val origFile = Parser(origExtrasConv).parseFile(origCode) + val origFile = Parser.parseFile(origCode) println("----ORIG AST----\n${Dumper.dump(origFile)}\n------------") val newCode = Writer.write(origFile) From cb2f6f06d1cac1517ba89e19db93dbf1d7e064b8 Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 20:54:18 +0900 Subject: [PATCH 10/13] Update README and add test --- README.md | 63 ++++++++-------- .../kotlin/ktast/ast/psi/ReadmeExampleTest.kt | 74 +++++++++++++++++++ 2 files changed, 106 insertions(+), 31 deletions(-) create mode 100644 ast-psi/src/test/kotlin/ktast/ast/psi/ReadmeExampleTest.kt diff --git a/README.md b/README.md index 922b3d236..8443183c2 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Examples below are simple Kotlin scripts. #### Parsing code -In this example, we use the wrapper around the Kotlin compiler's parser: +In this example, we use the Parser, which is a wrapper around the Kotlin compiler's parser: ```kotlin import ktast.ast.psi.Parser @@ -88,25 +88,13 @@ val code = """ """.trimIndent() // Call the parser with the code val file = Parser.parseFile(code) -// The file var is now a ktast.ast.Node.KotlinFile that is used in future examples... ``` -The `file` variable has the full AST. Note, if you want to parse with blank line and comment information, you can create -a converter that holds the extras: - -```kotlin -import ktast.ast.psi.ConverterWithExtras - -// ... - -val extrasMap = ConverterWithExtras() -val file = Parser(extrasMap).parseFile(code) -// extrasMap is an instance of ktast.ast.ExtrasMap -``` +The `file` variable is now a `ktast.ast.Node.KotlinFile`. #### Writing code -To write the code created above, simply use the writer +To write the code from the node created above, simply use the Writer: ```kotlin import ktast.ast.Writer @@ -116,20 +104,23 @@ import ktast.ast.Writer println(Writer.write(file)) ``` -This outputs a string of the written code from the AST `file` object. To include the extra blank line and comment info -from the previous parse example, pass in the extras map: +This outputs the following code, which is exactly the same code as the input. This is because the AST nodes have blank +line and comment information. ```kotlin -// ... +package foo -println(Writer.write(file, extrasMap)) -``` +fun bar() { + // Print hello + println("Hello, World!") +} -This outputs the code with the comments. +fun baz() = println("Hello, again!") +``` #### Visiting nodes -This will get all strings: +To get all strings from the file, we can use the Visitor: ```kotlin import ktast.ast.Node @@ -150,11 +141,12 @@ println(strings) The parameter of the lambda is a [NodePath](https://orangain.github.io/ktast/latest/api/ast/ktast.ast/-node-path/index.html) object that has `node` -and `parent` NodePath. There is a `tag` var on each node that can be used to store per-node state if desired. +and `parent` NodePath. #### Modifying nodes -This will change "Hello, World!" and "Hello, again!" to "Howdy, World!" and "Howdy, again": +To modify the file, we can use the MutableVisitor. The following code will change "Hello, World!" and "Hello, again!" +to "Howdy, World!" and "Howdy, again": ```kotlin import ktast.ast.MutableVisitor @@ -166,16 +158,25 @@ val newFile = MutableVisitor.traverse(file) { path -> if (node !is Node.Expression.StringLiteralExpression.LiteralStringEntry) node else node.copy(text = node.text.replace("Hello", "Howdy")) } + +Writer.write(newFile) ``` -Now `newFile` is a transformed version of `file`. As before, the parameter of the lambda is a NodePath object. +Now `newFile` is a transformed version of `file`. As before, the parameter of the lambda is a NodePath object. The +output will be the following code: + +```kotlin +package foo + +fun bar() { + // Print hello + println("Howdy, World!") +} + +fun baz() = println("Howdy, again!") +``` -Note, since `extraMap` support relies on object identities and this creates entirely new objects in the immutable tree, -the extra map becomes invalid on this step. When you mutate an AST tree, it is recommended to -use [ConverterWithMutableExtras](https://orangain.github.io/ktast/latest/api/ast-psi/ktast.ast.psi/-converter-with-mutable-extras/index.html) -that -implements [MutableExtrasMap](https://orangain.github.io/ktast/latest/api/ast/ktast.ast/-mutable-extras-map/index.html) -interface. +Note that the comments and blank lines are preserved. ## Running tests diff --git a/ast-psi/src/test/kotlin/ktast/ast/psi/ReadmeExampleTest.kt b/ast-psi/src/test/kotlin/ktast/ast/psi/ReadmeExampleTest.kt new file mode 100644 index 000000000..e68121a8e --- /dev/null +++ b/ast-psi/src/test/kotlin/ktast/ast/psi/ReadmeExampleTest.kt @@ -0,0 +1,74 @@ +package ktast.ast.psi + +import ktast.ast.MutableVisitor +import ktast.ast.Node +import ktast.ast.Visitor +import ktast.ast.Writer +import org.junit.Test +import kotlin.test.assertEquals + +class ReadmeExampleTest { + @Test + fun readme() { + //// Parser + val code = """ + package foo + + fun bar() { + // Print hello + println("Hello, World!") + } + + fun baz() = println("Hello, again!") + """.trimIndent() + // Call the parser with the code + val file = Parser.parseFile(code) + + //// Writer + assertEquals( + """ + package foo + + fun bar() { + // Print hello + println("Hello, World!") + } + + fun baz() = println("Hello, again!") + """.trimIndent(), + Writer.write(file) + ) + + //// Visitor + val strings = mutableListOf() + Visitor.traverse(file) { path -> + val node = path.node + if (node is Node.Expression.StringLiteralExpression.LiteralStringEntry) { + strings.add(node.text) + } + } + // Prints [Hello, World!, Hello, again!] + assertEquals(listOf("Hello, World!", "Hello, again!"), strings) + + //// MutableVisitor + val newFile = MutableVisitor.traverse(file) { path -> + val node = path.node + if (node !is Node.Expression.StringLiteralExpression.LiteralStringEntry) node + else node.copy(text = node.text.replace("Hello", "Howdy")) + } + + assertEquals( + """ + package foo + + fun bar() { + // Print hello + println("Howdy, World!") + } + + fun baz() = println("Howdy, again!") + """.trimIndent(), + Writer.write(newFile) + ) + } +} \ No newline at end of file From c75ee264726c9d2cbeb27d14598a9a6d722d4dac Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 21:09:38 +0900 Subject: [PATCH 11/13] Fix test --- ast-psi/src/test/kotlin/ktast/ast/NodeTest.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ast-psi/src/test/kotlin/ktast/ast/NodeTest.kt b/ast-psi/src/test/kotlin/ktast/ast/NodeTest.kt index b8a8320ee..c25ee6992 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/NodeTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/NodeTest.kt @@ -1,5 +1,6 @@ package ktast.ast +import ktast.ast.psi.Converter import ktast.ast.psi.Parser import org.junit.Test import org.junit.runner.RunWith @@ -11,7 +12,7 @@ import kotlin.test.assertNull class DoubleColonExpressionTypeTest(private val code: String) { @Test fun testType() { - val node = Parser.parseFile(code) + val node = Parser(Converter()).parseFile(code) val properties = node.declarations.filterIsInstance() assertEquals(properties.size, 1) properties.forEach { property -> @@ -51,7 +52,7 @@ class DoubleColonExpressionTypeTest(private val code: String) { class DoubleColonExpressionExpressionTest(private val code: String) { @Test fun testExpression() { - val node = Parser.parseFile(code) + val node = Parser(Converter()).parseFile(code) val properties = node.declarations.filterIsInstance() assertEquals(properties.size, 1) properties.forEach { property -> @@ -80,7 +81,7 @@ class DoubleColonExpressionExpressionTest(private val code: String) { class LambdaArgLambdaExpressionTest(private val code: String) { @Test fun testLambdaExpression() { - val node = Parser.parseFile(code) + val node = Parser(Converter()).parseFile(code) val functionDeclaration = node.declarations.filterIsInstance().first() val callExpressions = (functionDeclaration.body as Node.Expression.BlockExpression).statements.filterIsInstance() From f02ad55bb539778d59c3afcfdd7315be643edd47 Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 21:10:14 +0900 Subject: [PATCH 12/13] Update README --- README.md | 11 ++++++++- .../kotlin/ktast/ast/psi/ReadmeExampleTest.kt | 24 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8443183c2..0bef82f95 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,16 @@ val code = """ val file = Parser.parseFile(code) ``` -The `file` variable is now a `ktast.ast.Node.KotlinFile`. +The `file` variable is now a `ktast.ast.Node.KotlinFile`. Each AST nodes have blank line and comment information. If you +don't need them, you can pass a `Converter` instance to the constructor argument of the `Parser` instead: + +```kotlin +import ktast.ast.psi.Parser +import ktast.ast.psi.Converter + +// ... +val fileWithoutExtras = Parser(Converter()).parseFile(code) +``` #### Writing code diff --git a/ast-psi/src/test/kotlin/ktast/ast/psi/ReadmeExampleTest.kt b/ast-psi/src/test/kotlin/ktast/ast/psi/ReadmeExampleTest.kt index e68121a8e..05bb0e97f 100644 --- a/ast-psi/src/test/kotlin/ktast/ast/psi/ReadmeExampleTest.kt +++ b/ast-psi/src/test/kotlin/ktast/ast/psi/ReadmeExampleTest.kt @@ -71,4 +71,28 @@ class ReadmeExampleTest { Writer.write(newFile) ) } + + @Test + fun readmeWithoutExtras() { + val code = """ + package foo + + fun bar() { + // Print hello + println("Hello, World!") + } + + fun baz() = println("Hello, again!") + """.trimIndent() + + val fileWithoutExtras = Parser(Converter()).parseFile(code) + + assertEquals( + """ + package foo fun bar(){println("Hello, World!")} + fun baz()=println("Hello, again!") + """.trimIndent(), + Writer.write(fileWithoutExtras) + ) + } } \ No newline at end of file From 3b0d7c2ba11139cc18aefc136a38b64fc5fbed7b Mon Sep 17 00:00:00 2001 From: orangain Date: Tue, 20 Jun 2023 21:41:37 +0900 Subject: [PATCH 13/13] Clear object before starting convert --- ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithExtras.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithExtras.kt b/ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithExtras.kt index e009b7806..c7d196086 100644 --- a/ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithExtras.kt +++ b/ast-psi/src/main/kotlin/ktast/ast/psi/ConverterWithExtras.kt @@ -34,6 +34,8 @@ open class ConverterWithExtras : Converter() { } override fun convert(v: KtFile): Node.KotlinFile { + psiIdentitiesToNodes.clear() + seenExtraPsiIdentities.clear() return super.convert(v).also { fillWholeExtras(it, v) }