Skip to content

Commit

Permalink
Merge pull request #20 from samtkit/type-aliases
Browse files Browse the repository at this point in the history
Type aliases
  • Loading branch information
PascalHonegger authored May 9, 2023
2 parents a8ea530 + 700ad43 commit f538149
Show file tree
Hide file tree
Showing 17 changed files with 427 additions and 77 deletions.
6 changes: 2 additions & 4 deletions cli/src/main/kotlin/tools/samt/cli/CliDumper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,9 @@ internal fun dump(command: DumpCommand, terminal: Terminal, controller: Diagnost
}

// build up the semantic model from the AST
SemanticModelBuilder.build(fileNodes, controller)
val samtPackage = SemanticModelBuilder.build(fileNodes, controller)

if (dumpAll || command.dumpTypes) {
terminal.println("Types:")
terminal.println("Not yet implemented")
// Type dumper will be added here
terminal.println(TypePrinter.dump(samtPackage))
}
}
56 changes: 56 additions & 0 deletions cli/src/main/kotlin/tools/samt/cli/TypePrinter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package tools.samt.cli

import com.github.ajalt.mordant.rendering.TextColors.*
import com.github.ajalt.mordant.rendering.TextStyles.bold
import tools.samt.semantic.Package

internal object TypePrinter {
fun dump(samtPackage: Package): String = buildString {
appendLine(blue(samtPackage.name.ifEmpty { "<root>" }))
for (enum in samtPackage.enums) {
appendLine(" ${bold("enum")} ${yellow(enum.name)}")
}
for (record in samtPackage.records) {
appendLine(" ${bold("record")} ${yellow(record.name)}")
}
for (alias in samtPackage.aliases) {
appendLine(" ${bold("alias")} ${yellow(alias.name)} = ${gray(alias.fullyResolvedType?.humanReadableName ?: "Unknown")}")
}
for (service in samtPackage.services) {
appendLine(" ${bold("service")} ${yellow(service.name)}")
}
for (provider in samtPackage.providers) {
appendLine(" ${bold("provider")} ${yellow(provider.name)}")
}
for (consumer in samtPackage.consumers) {
appendLine(" ${bold("consumer")} for ${yellow(consumer.provider.humanReadableName)}")
}

val childDumps: List<String> = samtPackage.subPackages.map { dump(it) }

childDumps.forEachIndexed { childIndex, child ->
var firstLine = true
child.lineSequence().forEach { line ->
if (line.isNotEmpty()) {
if (childIndex != childDumps.lastIndex) {
if (firstLine) {
append("${white("├─")}$line")
} else {
append("${white("")}$line")
}
} else {
if (firstLine) {
append("${white("└─")}$line")
} else {
append(" $line")
}
}

appendLine()
}

firstLine = false
}
}
}
}
54 changes: 41 additions & 13 deletions cli/src/test/kotlin/tools/samt/cli/ASTPrinterTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,21 @@ class ASTPrinterTest {
enum E { A, B, C }
alias B : E
@Description("This is a service")
service MyService {
testmethod(foo: A): B
}
provide MyEndpoint {
implements MyService
transport HTTP
}
consume MyEndpoint {
uses MyService
}
""".trimIndent())

val dump = ASTPrinter.dump(fileNode)
Expand Down Expand Up @@ -80,19 +91,36 @@ class ASTPrinterTest {
│ ├─IdentifierNode A <11:10>
│ ├─IdentifierNode B <11:13>
│ └─IdentifierNode C <11:16>
└─ServiceDeclarationNode <14:1>
├─IdentifierNode MyService <14:9>
├─RequestResponseOperationNode <15:3>
│ ├─IdentifierNode testmethod <15:3>
│ ├─OperationParameterNode <15:14>
│ │ ├─IdentifierNode foo <15:14>
│ │ └─BundleIdentifierNode A <15:19>
│ │ └─IdentifierNode A <15:19>
│ └─BundleIdentifierNode B <15:23>
│ └─IdentifierNode B <15:23>
└─AnnotationNode <13:1>
├─IdentifierNode Description <13:2>
└─StringNode "This is a service" <13:14>
├─TypeAliasNode <13:1>
│ ├─IdentifierNode B <13:7>
│ └─BundleIdentifierNode E <13:11>
│ └─IdentifierNode E <13:11>
├─ServiceDeclarationNode <16:1>
│ ├─IdentifierNode MyService <16:9>
│ ├─RequestResponseOperationNode <17:3>
│ │ ├─IdentifierNode testmethod <17:3>
│ │ ├─OperationParameterNode <17:14>
│ │ │ ├─IdentifierNode foo <17:14>
│ │ │ └─BundleIdentifierNode A <17:19>
│ │ │ └─IdentifierNode A <17:19>
│ │ └─BundleIdentifierNode B <17:23>
│ │ └─IdentifierNode B <17:23>
│ └─AnnotationNode <15:1>
│ ├─IdentifierNode Description <15:2>
│ └─StringNode "This is a service" <15:14>
├─ProviderDeclarationNode <20:1>
│ ├─IdentifierNode MyEndpoint <20:9>
│ ├─ProviderImplementsNode <21:3>
│ │ └─BundleIdentifierNode MyService <21:14>
│ │ └─IdentifierNode MyService <21:14>
│ └─ProviderTransportNode <22:3>
│ └─IdentifierNode HTTP <22:13>
└─ConsumerDeclarationNode <25:1>
├─BundleIdentifierNode MyEndpoint <25:9>
│ └─IdentifierNode MyEndpoint <25:9>
└─ConsumerUsesNode <26:3>
└─BundleIdentifierNode MyService <26:8>
└─IdentifierNode MyService <26:8>
""".trimIndent().trim(), dumpWithoutColorCodes.trimIndent().trim())
}

Expand Down
79 changes: 79 additions & 0 deletions cli/src/test/kotlin/tools/samt/cli/TypePrinterTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package tools.samt.cli

import tools.samt.common.DiagnosticController
import tools.samt.common.SourceFile
import tools.samt.lexer.Lexer
import tools.samt.parser.FileNode
import tools.samt.parser.Parser
import tools.samt.semantic.SemanticModelBuilder
import java.net.URI
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse

class TypePrinterTest {
@Test
fun `correctly formats an AST dump`() {
val stuffPackage = parse("""
package test.stuff
record A {
name: String (10..20, pattern("hehe"))
age: Int (0..150)
}
enum E { A, B, C }
@Description("This is a service")
service MyService {
testmethod(foo: A): E
}
provide MyEndpoint {
implements MyService
transport HTTP
}
""".trimIndent())
val consumerPackage = parse("""
package test.other.company
record Empty
consume test.stuff.MyEndpoint {
uses test.stuff.MyService
}
""".trimIndent())

val controller = DiagnosticController(URI("file:///tmp"))
val samtPackage = SemanticModelBuilder.build(listOf(stuffPackage, consumerPackage), controller)
assertFalse(controller.hasErrors())

val dump = TypePrinter.dump(samtPackage)
val dumpWithoutColorCodes = dump.replace(Regex("\u001B\\[[;\\d]*m"), "")

assertEquals("""
<root>
└─test
├─stuff
│ enum E
│ record A
│ service MyService
│ provider MyEndpoint
└─other
└─company
record Empty
consumer for MyEndpoint
""".trimIndent().trim(), dumpWithoutColorCodes.trimIndent().trim())
}

private fun parse(source: String): FileNode {
val filePath = URI("file:///tmp/ASTPrinterTest.samt")
val sourceFile = SourceFile(filePath, source)
val diagnosticController = DiagnosticController(URI("file:///tmp"))
val diagnosticContext = diagnosticController.getOrCreateContext(sourceFile)
val stream = Lexer.scan(source.reader(), diagnosticContext)
val fileTree = Parser.parse(sourceFile, stream, diagnosticContext)
assertFalse(diagnosticContext.hasErrors(), "Expected no errors, but had errors")
return fileTree
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ class SamtDeclarationLookup private constructor() : SamtSemanticLookup<Location,
this[operation.declaration.name.location] = operation
}

override fun markTypeAliasDeclaration(aliasType: AliasType) {
super.markTypeAliasDeclaration(aliasType)
this[aliasType.declaration.name.location] = aliasType
}

companion object {
fun analyze(fileNode: FileNode, samtPackage: Package) =
SamtDeclarationLookup().also { it.analyze(fileNode, samtPackage) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ abstract class SamtSemanticLookup<TKey, TValue> protected constructor() {
markTypeReference(type.valueType)
}

is AliasType,
is ConsumerType,
is EnumType,
is ProviderType,
Expand Down Expand Up @@ -61,12 +62,17 @@ abstract class SamtSemanticLookup<TKey, TValue> protected constructor() {
is EnumDeclarationNode -> markEnumDeclaration(samtPackage.getTypeByNode(statement))
is RecordDeclarationNode -> markRecordDeclaration(samtPackage.getTypeByNode(statement))
is ServiceDeclarationNode -> markServiceDeclaration(samtPackage.getTypeByNode(statement))
is TypeAliasNode -> Unit
is TypeAliasNode -> markTypeAliasDeclaration(samtPackage.getTypeByNode(statement))
is PackageDeclarationNode -> markPackageDeclaration(statement)
is ImportNode -> markImport(statement,samtPackage.typeByNode[statement] ?: UnknownType)
}
}

protected open fun markTypeAliasDeclaration(aliasType: AliasType) {
markAnnotations(aliasType.declaration.annotations)
markTypeReference(aliasType.aliasedType)
}

protected open fun markServiceDeclaration(serviceType: ServiceType) {
markAnnotations(serviceType.declaration.annotations)
for (operation in serviceType.operations) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class SamtSemanticTokens private constructor() : SamtSemanticLookup<Location, Sa
Metadata(TokenType.type, TokenModifier.defaultLibrary)
}

is AliasType -> this[location] = Metadata(TokenType.type)
is ProviderType -> this[location] = Metadata(TokenType.type)
is RecordType -> this[location] = Metadata(TokenType.`class`)
is ServiceType -> this[location] = Metadata(TokenType.`interface`)
Expand Down
10 changes: 8 additions & 2 deletions semantic/src/main/kotlin/tools/samt/semantic/Package.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ class Package(val name: String) {

val records: MutableList<RecordType> = mutableListOf()

@Suppress("MemberVisibilityCanBePrivate")
val enums: MutableList<EnumType> = mutableListOf() // Will be read in the future
val enums: MutableList<EnumType> = mutableListOf()
val services: MutableList<ServiceType> = mutableListOf()
val providers: MutableList<ProviderType> = mutableListOf()
val consumers: MutableList<ConsumerType> = mutableListOf()
val aliases: MutableList<AliasType> = mutableListOf()

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

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

operator fun plusAssign(alias: AliasType) {
aliases.add(alias)
types[alias.name] = alias
typeByNode[alias.declaration] = alias
}

operator fun contains(identifier: IdentifierNode): Boolean =
types.containsKey(identifier.name)

Expand Down
Loading

0 comments on commit f538149

Please sign in to comment.