Skip to content

Commit f538149

Browse files
Merge pull request #20 from samtkit/type-aliases
Type aliases
2 parents a8ea530 + 700ad43 commit f538149

File tree

17 files changed

+427
-77
lines changed

17 files changed

+427
-77
lines changed

cli/src/main/kotlin/tools/samt/cli/CliDumper.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,9 @@ internal fun dump(command: DumpCommand, terminal: Terminal, controller: Diagnost
6161
}
6262

6363
// build up the semantic model from the AST
64-
SemanticModelBuilder.build(fileNodes, controller)
64+
val samtPackage = SemanticModelBuilder.build(fileNodes, controller)
6565

6666
if (dumpAll || command.dumpTypes) {
67-
terminal.println("Types:")
68-
terminal.println("Not yet implemented")
69-
// Type dumper will be added here
67+
terminal.println(TypePrinter.dump(samtPackage))
7068
}
7169
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package tools.samt.cli
2+
3+
import com.github.ajalt.mordant.rendering.TextColors.*
4+
import com.github.ajalt.mordant.rendering.TextStyles.bold
5+
import tools.samt.semantic.Package
6+
7+
internal object TypePrinter {
8+
fun dump(samtPackage: Package): String = buildString {
9+
appendLine(blue(samtPackage.name.ifEmpty { "<root>" }))
10+
for (enum in samtPackage.enums) {
11+
appendLine(" ${bold("enum")} ${yellow(enum.name)}")
12+
}
13+
for (record in samtPackage.records) {
14+
appendLine(" ${bold("record")} ${yellow(record.name)}")
15+
}
16+
for (alias in samtPackage.aliases) {
17+
appendLine(" ${bold("alias")} ${yellow(alias.name)} = ${gray(alias.fullyResolvedType?.humanReadableName ?: "Unknown")}")
18+
}
19+
for (service in samtPackage.services) {
20+
appendLine(" ${bold("service")} ${yellow(service.name)}")
21+
}
22+
for (provider in samtPackage.providers) {
23+
appendLine(" ${bold("provider")} ${yellow(provider.name)}")
24+
}
25+
for (consumer in samtPackage.consumers) {
26+
appendLine(" ${bold("consumer")} for ${yellow(consumer.provider.humanReadableName)}")
27+
}
28+
29+
val childDumps: List<String> = samtPackage.subPackages.map { dump(it) }
30+
31+
childDumps.forEachIndexed { childIndex, child ->
32+
var firstLine = true
33+
child.lineSequence().forEach { line ->
34+
if (line.isNotEmpty()) {
35+
if (childIndex != childDumps.lastIndex) {
36+
if (firstLine) {
37+
append("${white("├─")}$line")
38+
} else {
39+
append("${white("")}$line")
40+
}
41+
} else {
42+
if (firstLine) {
43+
append("${white("└─")}$line")
44+
} else {
45+
append(" $line")
46+
}
47+
}
48+
49+
appendLine()
50+
}
51+
52+
firstLine = false
53+
}
54+
}
55+
}
56+
}

cli/src/test/kotlin/tools/samt/cli/ASTPrinterTest.kt

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,21 @@ class ASTPrinterTest {
2626
2727
enum E { A, B, C }
2828
29+
alias B : E
30+
2931
@Description("This is a service")
3032
service MyService {
3133
testmethod(foo: A): B
3234
}
35+
36+
provide MyEndpoint {
37+
implements MyService
38+
transport HTTP
39+
}
40+
41+
consume MyEndpoint {
42+
uses MyService
43+
}
3344
""".trimIndent())
3445

3546
val dump = ASTPrinter.dump(fileNode)
@@ -80,19 +91,36 @@ class ASTPrinterTest {
8091
│ ├─IdentifierNode A <11:10>
8192
│ ├─IdentifierNode B <11:13>
8293
│ └─IdentifierNode C <11:16>
83-
└─ServiceDeclarationNode <14:1>
84-
├─IdentifierNode MyService <14:9>
85-
├─RequestResponseOperationNode <15:3>
86-
│ ├─IdentifierNode testmethod <15:3>
87-
│ ├─OperationParameterNode <15:14>
88-
│ │ ├─IdentifierNode foo <15:14>
89-
│ │ └─BundleIdentifierNode A <15:19>
90-
│ │ └─IdentifierNode A <15:19>
91-
│ └─BundleIdentifierNode B <15:23>
92-
│ └─IdentifierNode B <15:23>
93-
└─AnnotationNode <13:1>
94-
├─IdentifierNode Description <13:2>
95-
└─StringNode "This is a service" <13:14>
94+
├─TypeAliasNode <13:1>
95+
│ ├─IdentifierNode B <13:7>
96+
│ └─BundleIdentifierNode E <13:11>
97+
│ └─IdentifierNode E <13:11>
98+
├─ServiceDeclarationNode <16:1>
99+
│ ├─IdentifierNode MyService <16:9>
100+
│ ├─RequestResponseOperationNode <17:3>
101+
│ │ ├─IdentifierNode testmethod <17:3>
102+
│ │ ├─OperationParameterNode <17:14>
103+
│ │ │ ├─IdentifierNode foo <17:14>
104+
│ │ │ └─BundleIdentifierNode A <17:19>
105+
│ │ │ └─IdentifierNode A <17:19>
106+
│ │ └─BundleIdentifierNode B <17:23>
107+
│ │ └─IdentifierNode B <17:23>
108+
│ └─AnnotationNode <15:1>
109+
│ ├─IdentifierNode Description <15:2>
110+
│ └─StringNode "This is a service" <15:14>
111+
├─ProviderDeclarationNode <20:1>
112+
│ ├─IdentifierNode MyEndpoint <20:9>
113+
│ ├─ProviderImplementsNode <21:3>
114+
│ │ └─BundleIdentifierNode MyService <21:14>
115+
│ │ └─IdentifierNode MyService <21:14>
116+
│ └─ProviderTransportNode <22:3>
117+
│ └─IdentifierNode HTTP <22:13>
118+
└─ConsumerDeclarationNode <25:1>
119+
├─BundleIdentifierNode MyEndpoint <25:9>
120+
│ └─IdentifierNode MyEndpoint <25:9>
121+
└─ConsumerUsesNode <26:3>
122+
└─BundleIdentifierNode MyService <26:8>
123+
└─IdentifierNode MyService <26:8>
96124
""".trimIndent().trim(), dumpWithoutColorCodes.trimIndent().trim())
97125
}
98126

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package tools.samt.cli
2+
3+
import tools.samt.common.DiagnosticController
4+
import tools.samt.common.SourceFile
5+
import tools.samt.lexer.Lexer
6+
import tools.samt.parser.FileNode
7+
import tools.samt.parser.Parser
8+
import tools.samt.semantic.SemanticModelBuilder
9+
import java.net.URI
10+
import kotlin.test.Test
11+
import kotlin.test.assertEquals
12+
import kotlin.test.assertFalse
13+
14+
class TypePrinterTest {
15+
@Test
16+
fun `correctly formats an AST dump`() {
17+
val stuffPackage = parse("""
18+
package test.stuff
19+
20+
record A {
21+
name: String (10..20, pattern("hehe"))
22+
age: Int (0..150)
23+
}
24+
25+
enum E { A, B, C }
26+
27+
@Description("This is a service")
28+
service MyService {
29+
testmethod(foo: A): E
30+
}
31+
32+
provide MyEndpoint {
33+
implements MyService
34+
transport HTTP
35+
}
36+
""".trimIndent())
37+
val consumerPackage = parse("""
38+
package test.other.company
39+
40+
record Empty
41+
42+
consume test.stuff.MyEndpoint {
43+
uses test.stuff.MyService
44+
}
45+
""".trimIndent())
46+
47+
val controller = DiagnosticController(URI("file:///tmp"))
48+
val samtPackage = SemanticModelBuilder.build(listOf(stuffPackage, consumerPackage), controller)
49+
assertFalse(controller.hasErrors())
50+
51+
val dump = TypePrinter.dump(samtPackage)
52+
val dumpWithoutColorCodes = dump.replace(Regex("\u001B\\[[;\\d]*m"), "")
53+
54+
assertEquals("""
55+
<root>
56+
└─test
57+
├─stuff
58+
│ enum E
59+
│ record A
60+
│ service MyService
61+
│ provider MyEndpoint
62+
└─other
63+
└─company
64+
record Empty
65+
consumer for MyEndpoint
66+
""".trimIndent().trim(), dumpWithoutColorCodes.trimIndent().trim())
67+
}
68+
69+
private fun parse(source: String): FileNode {
70+
val filePath = URI("file:///tmp/ASTPrinterTest.samt")
71+
val sourceFile = SourceFile(filePath, source)
72+
val diagnosticController = DiagnosticController(URI("file:///tmp"))
73+
val diagnosticContext = diagnosticController.getOrCreateContext(sourceFile)
74+
val stream = Lexer.scan(source.reader(), diagnosticContext)
75+
val fileTree = Parser.parse(sourceFile, stream, diagnosticContext)
76+
assertFalse(diagnosticContext.hasErrors(), "Expected no errors, but had errors")
77+
return fileTree
78+
}
79+
}

language-server/src/main/kotlin/tools/samt/ls/SamtDeclarationLookup.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ class SamtDeclarationLookup private constructor() : SamtSemanticLookup<Location,
4545
this[operation.declaration.name.location] = operation
4646
}
4747

48+
override fun markTypeAliasDeclaration(aliasType: AliasType) {
49+
super.markTypeAliasDeclaration(aliasType)
50+
this[aliasType.declaration.name.location] = aliasType
51+
}
52+
4853
companion object {
4954
fun analyze(fileNode: FileNode, samtPackage: Package) =
5055
SamtDeclarationLookup().also { it.analyze(fileNode, samtPackage) }

language-server/src/main/kotlin/tools/samt/ls/SamtSemanticLookup.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ abstract class SamtSemanticLookup<TKey, TValue> protected constructor() {
3232
markTypeReference(type.valueType)
3333
}
3434

35+
is AliasType,
3536
is ConsumerType,
3637
is EnumType,
3738
is ProviderType,
@@ -61,12 +62,17 @@ abstract class SamtSemanticLookup<TKey, TValue> protected constructor() {
6162
is EnumDeclarationNode -> markEnumDeclaration(samtPackage.getTypeByNode(statement))
6263
is RecordDeclarationNode -> markRecordDeclaration(samtPackage.getTypeByNode(statement))
6364
is ServiceDeclarationNode -> markServiceDeclaration(samtPackage.getTypeByNode(statement))
64-
is TypeAliasNode -> Unit
65+
is TypeAliasNode -> markTypeAliasDeclaration(samtPackage.getTypeByNode(statement))
6566
is PackageDeclarationNode -> markPackageDeclaration(statement)
6667
is ImportNode -> markImport(statement,samtPackage.typeByNode[statement] ?: UnknownType)
6768
}
6869
}
6970

71+
protected open fun markTypeAliasDeclaration(aliasType: AliasType) {
72+
markAnnotations(aliasType.declaration.annotations)
73+
markTypeReference(aliasType.aliasedType)
74+
}
75+
7076
protected open fun markServiceDeclaration(serviceType: ServiceType) {
7177
markAnnotations(serviceType.declaration.annotations)
7278
for (operation in serviceType.operations) {

language-server/src/main/kotlin/tools/samt/ls/SamtSemanticTokens.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class SamtSemanticTokens private constructor() : SamtSemanticLookup<Location, Sa
2828
Metadata(TokenType.type, TokenModifier.defaultLibrary)
2929
}
3030

31+
is AliasType -> this[location] = Metadata(TokenType.type)
3132
is ProviderType -> this[location] = Metadata(TokenType.type)
3233
is RecordType -> this[location] = Metadata(TokenType.`class`)
3334
is ServiceType -> this[location] = Metadata(TokenType.`interface`)

semantic/src/main/kotlin/tools/samt/semantic/Package.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ class Package(val name: String) {
77

88
val records: MutableList<RecordType> = mutableListOf()
99

10-
@Suppress("MemberVisibilityCanBePrivate")
11-
val enums: MutableList<EnumType> = mutableListOf() // Will be read in the future
10+
val enums: MutableList<EnumType> = mutableListOf()
1211
val services: MutableList<ServiceType> = mutableListOf()
1312
val providers: MutableList<ProviderType> = mutableListOf()
1413
val consumers: MutableList<ConsumerType> = mutableListOf()
14+
val aliases: MutableList<AliasType> = mutableListOf()
1515

1616
val typeByNode: MutableMap<Node, Type> = mutableMapOf()
1717

@@ -73,6 +73,12 @@ class Package(val name: String) {
7373
typeByNode[consumer.declaration] = consumer
7474
}
7575

76+
operator fun plusAssign(alias: AliasType) {
77+
aliases.add(alias)
78+
types[alias.name] = alias
79+
typeByNode[alias.declaration] = alias
80+
}
81+
7682
operator fun contains(identifier: IdentifierNode): Boolean =
7783
types.containsKey(identifier.name)
7884

0 commit comments

Comments
 (0)