Skip to content

Commit

Permalink
Rename TypesafeClause to TypesafePredicate, now includes context
Browse files Browse the repository at this point in the history
* Some significant changes to preserve usability of the DSL given the new contextual predicates/rules
* Moves a lot of unfinished/unused code to a new subproject, dialector-kt-experimental
* Adds many tests to improve coverage of rules and core Dialector elements
  • Loading branch information
tyler-hodgkins-wd authored and ty1824 committed Aug 9, 2023
1 parent 9ff9251 commit a6b79a8
Show file tree
Hide file tree
Showing 59 changed files with 738 additions and 391 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/pull-request-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ jobs:
if: success() || failure()
with:
name: Test Results
path: '**/test-results/test/*.xml'
path: '**/build/test-results/test/*.xml'
reporter: java-junit

- name: Coverage Report
uses: mi-kas/kover-report@v1
with:
path: 'dialector-kt/build/reports/kover/xml/report.xml'
path: 'build/reports/kover/report.xml'
title: Coverage Results
token: ${{ secrets.GITHUB_TOKEN }}
update-comment: true
Expand Down
43 changes: 33 additions & 10 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import java.time.LocalDateTime

plugins {
base
idea
kotlin("jvm") apply false
id("org.jetbrains.dokka")
Expand All @@ -13,30 +14,30 @@ plugins {
*/
fun getVersionTimestamp(): String = with(LocalDateTime.now()) {
year.toString() +
monthValue.toString().padStart(0, '0') +
dayOfMonth.toString().padStart(0, '0') +
hour.toString().padStart(0, '0') +
minute.toString().padStart(0, '0') +
second.toString().padStart(0, '0')
monthValue.toString().padStart(2, '0') +
dayOfMonth.toString().padStart(2, '0') +
hour.toString().padStart(2, '0') +
minute.toString().padStart(2, '0') +
second.toString().padStart(2, '0')
}

allprojects {
if (version.toString().isNullOrBlank()) {
if (version.toString().isBlank() || version.toString() == "unspecified") {
// If the version hasn't been specified, set it to a timestamped default
version = "LOCAL-${getVersionTimestamp()}"
} else if (version.toString().startsWith("v")) {
// TODO: Probably should do this before passing as a parameter
version = version.toString().drop(1)
}
if (project == rootProject) println("Using version for build: $version")
}

subprojects {
apply(plugin = "org.jmailen.kotlinter")

repositories {
mavenCentral()
}
}

subprojects {
apply(plugin = "org.jmailen.kotlinter")

// configure Kotlin to allow these opt-in features throughout the project
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
Expand All @@ -49,3 +50,25 @@ subprojects {
}
}
}

dependencies {
kover(project(":dialector-kt"))
kover(project(":inkt"))
}

koverReport {
filters {
excludes {
packages("dev.dialector.inkt.example")
}
}

defaults {
xml {
onCheck = true
}
html {
onCheck = true
}
}
}
29 changes: 29 additions & 0 deletions dialector-kt-experimental/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
plugins {
kotlin("jvm")
id("maven-publish")
signing
}

dependencies {
implementation(project(":dialector-kt"))
implementation(kotlin("reflect"))

testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("io.mockk:mockk:1.12.0")
}

kotlin {
explicitApiWarning()
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}

java {
withJavadocJar()
withSourcesJar()
}

tasks.withType<Test> {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,20 @@ public interface Origin

public interface ConstraintCreator

public interface SemanticRule<T : Node> {
public interface SemanticRule<T : Node, C : SemanticRuleContext> {
public val name: String
public val isValidFor: NodeClause<T>
public val rule: SemanticRuleContext.(node: T) -> Unit
public val isValidFor: NodeClause<T, C>
public val rule: C.(node: T) -> Unit

@Suppress("UNCHECKED_CAST")
public operator fun invoke(context: SemanticRuleContext, node: Node) {
if (isValidFor(node)) context.rule(node as T)
public operator fun invoke(node: Node, context: C) {
if (isValidFor(node, context)) context.rule(node as T)
}
}

public fun <T : Node> NodeClause<T>.evaluateSemantics(name: String, semantics: SemanticRuleContext.(node: T) -> Unit): SemanticRule<T> =
object : SemanticRule<T> {
context(RuleContextProvider<C>)
public fun <T : Node, C : SemanticRuleContext> NodeClause<T, C>.evaluateSemantics(name: String, semantics: C.(node: T) -> Unit): SemanticRule<T, C> =
object : SemanticRule<T, C> {
override val name = name
override val isValidFor = this@evaluateSemantics
override val rule = semantics
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package dev.dialector.semantic

import dev.dialector.semantic.type.Type
import dev.dialector.semantic.type.inference.new.VariableConstraintKind

public interface TypeVariable : Type, SemanticVariable {
public val id: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dev.dialector.semantic.type.inference

import assertAll
import dev.dialector.semantic.type.IdentityType
import dev.dialector.semantic.type.RuleContextProvider
import dev.dialector.semantic.type.Type
import dev.dialector.semantic.type.lattice.SimpleTypeLattice
import dev.dialector.semantic.type.lattice.TypeLattice
Expand All @@ -13,7 +14,7 @@ import io.mockk.mockk
import kotlin.test.Test
import kotlin.test.assertEquals

class InferenceEngineTest {
class InferenceEngineTest : RuleContextProvider<Nothing> {
private val booleanType = object : IdentityType("boolean") {}
private val stringType = object : IdentityType("string") {}
private val integerType = object : IdentityType("integer") {}
Expand Down
6 changes: 0 additions & 6 deletions dialector-kt/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ tasks.withType<Test> {
useJUnitPlatform()
}

kover {
xmlReport {
onCheck.set(true)
}
}

publishing {
repositories {
maven {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
package dev.dialector.diagnostic

import dev.dialector.semantic.SemanticModel
import dev.dialector.syntax.Node
import dev.dialector.syntax.NodeClause
import dev.dialector.syntax.SyntacticModel
import dev.dialector.syntax.NodePredicate

public interface DiagnosticEvaluationContext : SyntacticModel, SemanticModel {
public interface DiagnosticContext {
/**
* Registers a diagnostic for the given node.
*/
public fun diagnostic(message: String, node: Node)
}

/**
* A rule that defines [ModelDiagnostics] that should be produced for nodes matching a [NodeClause]
* A rule that defines [ModelDiagnostics] that should be produced for nodes matching a [NodePredicate]
*/
public interface DiagnosticRule<T : Node> {
public val isValidFor: NodeClause<T>
public val diagnostics: DiagnosticEvaluationContext.(node: T) -> Unit
public interface DiagnosticRule<T : Node, C : DiagnosticContext> {
public val isValidFor: NodePredicate<T, in C>
public val diagnostics: C.(node: T) -> Unit

public operator fun invoke(context: DiagnosticEvaluationContext, node: Node) {
public operator fun invoke(node: Node, context: C) {
@Suppress("UNCHECKED_CAST")
if (isValidFor(node)) context.diagnostics(node as T)
if (isValidFor(node, context)) context.diagnostics(node as T)
}
}

public infix fun <T : Node> NodeClause<T>.check(check: DiagnosticEvaluationContext.(node: T) -> Unit): DiagnosticRule<T> =
object : DiagnosticRule<T> {
override val isValidFor: NodeClause<T> = this@check
override val diagnostics: DiagnosticEvaluationContext.(node: T) -> Unit = check
/**
* Produces a [DiagnosticRule] from the given [NodePredicate] and checking function.
*
* @param T The node type being checked.
* @param C The [DiagnosticContext] type
*/
public infix fun <T : Node, C : DiagnosticContext> NodePredicate<T, in C>.check(
check: C.(node: T) -> Unit,
): DiagnosticRule<T, C> =
object : DiagnosticRule<T, C> {
override val isValidFor: NodePredicate<T, in C> = this@check
override val diagnostics: C.(node: T) -> Unit = check
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package dev.dialector.semantic.type

import dev.dialector.util.ClassifierPredicate
import dev.dialector.util.InstancePredicate
import dev.dialector.util.LogicalPredicate
import dev.dialector.util.TypesafePredicate
import kotlin.reflect.KClass

/**
* A clause that describes a type.
*/
public interface TypePredicate<T : Type, C> : TypesafePredicate<T, C>

/**
* A special TypeClause that matches against a specific Type.
*
* This specialization facilitates optimizations in TypeLattice implementation.
*/
public class TypeObjectPredicate<T : Type, C>(type: T) : TypePredicate<T, C>, InstancePredicate<T, C>(type)

/**
* A special TypeClause that matches against a specific Typeclass
*
* This specialization facilitates optimizations in TypeLattice implementation.
*/
public class TypeClassPredicate<T : Type, C>(
typeClass: KClass<T>,
) : TypePredicate<T, C>, ClassifierPredicate<T, C>(typeClass)

public class TypeLogicalPredicate<T : Type, C>(
typeClass: KClass<T>,
predicate: C.(T) -> Boolean,
) : TypePredicate<T, C>, LogicalPredicate<T, C>(typeClass, predicate)

/**
* Creates a TypeClause that matches against a specific Type.
*/
public fun <T : Type> type(type: T): TypePredicate<T, Any> = TypeObjectPredicate(type)

/**
* Creates a TypeClause that matches against a specific Typeclass.
*/
public inline fun <reified T : Type> typeClass(): TypePredicate<T, Any> = TypeClassPredicate(T::class)

/**
* Creates a TypeClause that matches types against a given predicate.
*/
public inline fun <reified T : Type, C> typeClause(
noinline predicate: C.(T) -> Boolean,
): TypePredicate<T, C> = TypeLogicalPredicate(T::class, predicate)
Loading

0 comments on commit a6b79a8

Please sign in to comment.