Skip to content

Commit d701d86

Browse files
committed
Allow number,duration,date/time capturing groups in skill yaml
1 parent 51e96f2 commit d701d86

File tree

6 files changed

+143
-40
lines changed

6 files changed

+143
-40
lines changed

app/src/main/kotlin/org/stypox/dicio/skills/timer/TimerSkill.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,10 @@ class TimerSkill(correspondingSkillInfo: SkillInfo, data: StandardRecognizerData
2525
override suspend fun generateOutput(ctx: SkillContext, inputData: Timer): SkillOutput {
2626
return when (inputData) {
2727
is Timer.Set -> {
28-
val duration = inputData.duration?.let {
29-
ctx.parserFormatter?.extractDuration(it)?.parseFirst()?.toJavaDuration()
30-
}
31-
if (duration == null) {
28+
if (inputData.duration == null) {
3229
TimerOutput.SetAskDuration { setTimer(ctx, it, inputData.name) }
3330
} else {
34-
setTimer(ctx, duration, inputData.name)
31+
setTimer(ctx, inputData.duration.toJavaDuration(), inputData.name)
3532
}
3633
}
3734
is Timer.Query -> {

sentences-compiler-plugin/src/main/kotlin/org/stypox/dicio/sentencesCompilerPlugin/data/CheckSentences.kt

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@ package org.stypox.dicio.sentencesCompilerPlugin.data
22

33
import org.stypox.dicio.sentencesCompilerPlugin.util.SentencesCompilerPluginException
44

5+
fun correspondingSentenceDefinition(
6+
parsedSkill: ParsedSkill,
7+
sentence: ParsedSentence
8+
): SentenceDefinition {
9+
return parsedSkill.sentenceDefinitions
10+
.firstOrNull { definition -> definition.id == sentence.id }
11+
?: throw SentencesCompilerPluginException(
12+
"BUG in sentences compiler plugin: could not find definition corresponding to " +
13+
"sentence id ${sentence.id} for skill ${parsedSkill.id}"
14+
)
15+
}
16+
517
fun checkSentences(parsedSkill: ParsedSkill) {
618
for (sentence in parsedSkill.languageToSentences.map { it.second }.flatten()) {
7-
val definition = parsedSkill.sentenceDefinitions
8-
.find { definition -> definition.id == sentence.id }
9-
if (definition == null) {
10-
throw SentencesCompilerPluginException(
11-
"BUG in sentences compiler plugin: could not find definition corresponding to " +
12-
"sentence id ${sentence.id} for skill ${parsedSkill.id}"
13-
)
14-
}
15-
19+
val definition = correspondingSentenceDefinition(parsedSkill, sentence)
1620
val expectedCapturingGroups = definition.captures.map { it.id }.toSet()
1721
val actualCapturingGroups = sentence.constructs.capturingGroupNames
1822
if (!expectedCapturingGroups.containsAll(actualCapturingGroups)) {

sentences-compiler-plugin/src/main/kotlin/org/stypox/dicio/sentencesCompilerPlugin/data/SkillDefinitionsFile.kt

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package org.stypox.dicio.sentencesCompilerPlugin.data
22

3+
import com.fasterxml.jackson.annotation.JsonProperty
4+
import com.fasterxml.jackson.annotation.JsonSubTypes
5+
import com.fasterxml.jackson.annotation.JsonTypeInfo
36
import com.fasterxml.jackson.annotation.JsonValue
47

58
data class SkillDefinitionsFile(
@@ -23,12 +26,56 @@ data class SentenceDefinition(
2326
val captures: List<CaptureDefinition> = listOf(),
2427
)
2528

26-
data class CaptureDefinition(
27-
val id: String,
28-
val type: CaptureType
29+
@JsonTypeInfo(
30+
use = JsonTypeInfo.Id.NAME,
31+
include = JsonTypeInfo.As.PROPERTY,
32+
property = "type"
33+
)
34+
@JsonSubTypes(
35+
JsonSubTypes.Type(value = CaptureDefinition.StringCapture::class, name = "string"),
36+
JsonSubTypes.Type(value = CaptureDefinition.NumberCapture::class, name = "number"),
37+
JsonSubTypes.Type(value = CaptureDefinition.DurationCapture::class, name = "duration"),
38+
JsonSubTypes.Type(value = CaptureDefinition.DateTimeCapture::class, name = "date_time"),
2939
)
40+
sealed interface CaptureDefinition {
41+
val id: String
42+
val weight: Float?
43+
44+
data class StringCapture(
45+
override val id: String,
46+
override val weight: Float?,
47+
) : CaptureDefinition
48+
49+
data class NumberCapture(
50+
override val id: String,
51+
override val weight: Float?,
52+
@param:JsonProperty("bonus_if_largest_possible")
53+
val bonusIfLargestPossible: Float?,
54+
@param:JsonProperty("short_scale")
55+
val shortScale: Boolean?,
56+
@param:JsonProperty("prefer_ordinal")
57+
val preferOrdinal: Boolean?,
58+
@param:JsonProperty("integer_only")
59+
val integerOnly: Boolean?,
60+
) : CaptureDefinition
61+
62+
data class DurationCapture(
63+
override val id: String,
64+
override val weight: Float?,
65+
@param:JsonProperty("bonus_if_largest_possible")
66+
val bonusIfLargestPossible: Float?,
67+
@param:JsonProperty("short_scale")
68+
val shortScale: Boolean?,
69+
) : CaptureDefinition
3070

31-
enum class CaptureType(@JsonValue val serializedValue: String) {
32-
STRING("string"),
33-
DURATION("duration")
71+
data class DateTimeCapture(
72+
override val id: String,
73+
override val weight: Float?,
74+
@param:JsonProperty("bonus_if_largest_possible")
75+
val bonusIfLargestPossible: Float?,
76+
@param:JsonProperty("short_scale")
77+
val shortScale: Boolean?,
78+
@param:JsonProperty("prefer_month_before_day")
79+
val preferMonthBeforeDay: Boolean?,
80+
) : CaptureDefinition
3481
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.stypox.dicio.sentencesCompilerPlugin.gen
2+
3+
import com.squareup.kotlinpoet.ClassName
4+
import com.squareup.kotlinpoet.TypeName
5+
import com.squareup.kotlinpoet.asTypeName
6+
import org.stypox.dicio.sentencesCompilerPlugin.data.CaptureDefinition
7+
8+
fun getTypeName(captureDefinition: CaptureDefinition): TypeName {
9+
return when (captureDefinition) {
10+
is CaptureDefinition.StringCapture -> String::class.asTypeName()
11+
is CaptureDefinition.NumberCapture -> ClassName("org.dicio.numbers.unit", "Number")
12+
is CaptureDefinition.DurationCapture -> ClassName("org.dicio.numbers.unit", "Duration")
13+
is CaptureDefinition.DateTimeCapture -> ClassName("java.time", "LocalDateTime")
14+
}
15+
}
16+
17+
fun getCapturingGroupClassName(captureDefinition: CaptureDefinition): TypeName {
18+
return when (captureDefinition) {
19+
is CaptureDefinition.StringCapture -> ClassName("org.dicio.skill.standard.construct", "CapturingConstruct")
20+
is CaptureDefinition.NumberCapture -> ClassName("org.dicio.skill.standard.construct", "NumberConstruct")
21+
is CaptureDefinition.DurationCapture -> ClassName("org.dicio.skill.standard.construct", "DurationConstruct")
22+
is CaptureDefinition.DateTimeCapture -> ClassName("org.dicio.skill.standard.construct", "DateTimeConstruct")
23+
}
24+
}
25+
26+
fun getParams(captureDefinition: CaptureDefinition): List<Triple<String, String, Any>> {
27+
val res = ArrayList<Triple<String, String, Any>>()
28+
captureDefinition.weight?.let { res.add(Triple("weight", "%Lf", it)) }
29+
when (captureDefinition) {
30+
is CaptureDefinition.StringCapture -> {}
31+
is CaptureDefinition.NumberCapture -> {
32+
captureDefinition.bonusIfLargestPossible?.let { res.add(Triple("bonusIfLargestPossible", "%Lf", it)) }
33+
captureDefinition.shortScale?.let { res.add(Triple("shortScale", "%L", it)) }
34+
captureDefinition.preferOrdinal?.let { res.add(Triple("preferOrdinal", "%L", it)) }
35+
captureDefinition.integerOnly?.let { res.add(Triple("integerOnly", "%L", it)) }
36+
}
37+
is CaptureDefinition.DurationCapture -> {
38+
captureDefinition.bonusIfLargestPossible?.let { res.add(Triple("bonusIfLargestPossible", "%Lf", it)) }
39+
captureDefinition.shortScale?.let { res.add(Triple("shortScale", "%L", it)) }
40+
}
41+
is CaptureDefinition.DateTimeCapture -> {
42+
captureDefinition.bonusIfLargestPossible?.let { res.add(Triple("bonusIfLargestPossible", "%Lf", it)) }
43+
captureDefinition.shortScale?.let { res.add(Triple("shortScale", "%L", it)) }
44+
captureDefinition.preferMonthBeforeDay?.let { res.add(Triple("preferMonthBeforeDay", "%L", it)) }
45+
}
46+
}
47+
return res
48+
}

sentences-compiler-plugin/src/main/kotlin/org/stypox/dicio/sentencesCompilerPlugin/gen/GenerateConstruct.kt

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,17 @@ import org.dicio.sentences_compiler.construct.OrList
99
import org.dicio.sentences_compiler.construct.SentenceConstructList
1010
import org.dicio.sentences_compiler.construct.Word
1111
import org.dicio.sentences_compiler.construct.WordWithVariations
12-
import org.dicio.sentences_compiler.util.StringNormalizer
13-
import org.dicio.sentences_compiler.util.StringNormalizer.nfkdNormalize
12+
import org.stypox.dicio.sentencesCompilerPlugin.data.SentenceDefinition
1413
import org.stypox.dicio.sentencesCompilerPlugin.util.SentencesCompilerPluginException
1514

16-
fun generateConstruct(construct: Construct): CodeBlock {
15+
fun generateConstruct(construct: Construct, sentence: SentenceDefinition): CodeBlock {
1716
return when (construct) {
1817
is Word -> generateWord(construct)
1918
is WordWithVariations -> generateWordWithVariations(construct)
20-
is OrList -> generateOrList(construct)
19+
is OrList -> generateOrList(construct, sentence)
2120
is OptionalConstruct -> generateOptionalConstruct()
22-
is CapturingGroup -> generateCapturingGroup(construct)
23-
is SentenceConstructList -> generateSentenceConstructList(construct)
21+
is CapturingGroup -> generateCapturingGroup(construct, sentence)
22+
is SentenceConstructList -> generateSentenceConstructList(construct, sentence)
2423
else -> throw SentencesCompilerPluginException(
2524
"Unexpected construct obtained from sentences compiler: type=${
2625
construct::class.simpleName
@@ -51,11 +50,11 @@ fun generateWordWithVariations(word: WordWithVariations): CodeBlock {
5150
)
5251
}
5352

54-
fun generateOrList(orList: OrList): CodeBlock {
53+
fun generateOrList(orList: OrList, sentence: SentenceDefinition): CodeBlock {
5554
return CodeBlock.of(
5655
"%T(listOf(${"%L,".repeat(orList.constructs.size)}))",
5756
ClassName("org.dicio.skill.standard.construct", "OrConstruct"),
58-
*orList.constructs.map(::generateConstruct).toTypedArray(),
57+
*orList.constructs.map { generateConstruct(it, sentence) }.toTypedArray(),
5958
)
6059
}
6160

@@ -66,19 +65,26 @@ fun generateOptionalConstruct(): CodeBlock {
6665
)
6766
}
6867

69-
fun generateCapturingGroup(capturingGroup: CapturingGroup): CodeBlock {
68+
fun generateCapturingGroup(capturingGroup: CapturingGroup, sentence: SentenceDefinition): CodeBlock {
69+
val captureDef = sentence.captures.firstOrNull { it.id == capturingGroup.name }
70+
?: throw SentencesCompilerPluginException("BUG in sentences compiler plugin: capturing " +
71+
"group named ${capturingGroup.name} not found among captures for sentence ${sentence.id}")
72+
val params = getParams(captureDef)
7073
return CodeBlock.of(
71-
"%T(%S)",
72-
ClassName("org.dicio.skill.standard.construct", "CapturingConstruct"),
74+
"%T(%S" + params.joinToString { (name, spec, _) -> ",$name=$spec" } + ")",
75+
getCapturingGroupClassName(captureDef),
7376
capturingGroup.name,
74-
// TODO allow specifying weight
77+
*params.map { (_, _, value) -> value }.toTypedArray()
7578
)
7679
}
7780

78-
fun generateSentenceConstructList(sentenceConstructList: SentenceConstructList): CodeBlock {
81+
fun generateSentenceConstructList(
82+
sentenceConstructList: SentenceConstructList,
83+
sentence: SentenceDefinition
84+
): CodeBlock {
7985
return CodeBlock.of(
8086
"%T(listOf(${"%L,".repeat(sentenceConstructList.constructs.size)}))",
8187
ClassName("org.dicio.skill.standard.construct", "CompositeConstruct"),
82-
*sentenceConstructList.constructs.map(::generateConstruct).toTypedArray(),
88+
*sentenceConstructList.constructs.map { generateConstruct(it, sentence) }.toTypedArray(),
8389
)
8490
}

sentences-compiler-plugin/src/main/kotlin/org/stypox/dicio/sentencesCompilerPlugin/gen/GenerateSkillSentencesKt.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import org.stypox.dicio.sentencesCompilerPlugin.util.CLASS_NAME
1919
import org.stypox.dicio.sentencesCompilerPlugin.util.FILE_COMMENT
2020
import org.stypox.dicio.sentencesCompilerPlugin.util.PACKAGE_NAME
2121
import java.io.File
22+
import org.stypox.dicio.sentencesCompilerPlugin.data.correspondingSentenceDefinition
2223

2324
fun generateSkillSentencesKt(parsedData: ParsedData, outputDirFile: File) {
2425
val baseObj = TypeSpec.objectBuilder(CLASS_NAME)
@@ -71,7 +72,6 @@ private fun generateSkillObject(skill: ParsedSkill): TypeSpec {
7172
}
7273

7374
private fun generateResultTypes(skill: ParsedSkill, superinterface: ClassName): List<TypeSpec> {
74-
val nullableStringType = String::class.asTypeName().copy(nullable = true)
7575
val resultTypes = ArrayList<TypeSpec>()
7676

7777
for (definition in skill.sentenceDefinitions) {
@@ -83,13 +83,13 @@ private fun generateResultTypes(skill: ParsedSkill, superinterface: ClassName):
8383
.primaryConstructor(
8484
FunSpec.constructorBuilder()
8585
.addParameters(definition.captures.map {
86-
ParameterSpec.builder(it.id.toCamelCase(), nullableStringType).build()
86+
ParameterSpec.builder(it.id.toCamelCase(), getTypeName(it).copy(nullable = true)).build()
8787
})
8888
.build()
8989
)
9090
.addProperties(definition.captures.map {
9191
PropertySpec
92-
.builder(it.id.toCamelCase(), nullableStringType)
92+
.builder(it.id.toCamelCase(), getTypeName(it).copy(nullable = true))
9393
.initializer(it.id.toCamelCase())
9494
.build()
9595
})
@@ -124,12 +124,12 @@ private fun generateResultFromMatchFunction(skill: ParsedSkill, returnType: Clas
124124
.repeat(definition.captures.size)
125125
})",
126126
definition.id,
127-
*definition.captures.flatMap { sequenceOf(String::class, it.id) }.toTypedArray()
127+
*definition.captures.flatMap { sequenceOf(getTypeName(it), it.id) }.toTypedArray()
128128
)
129129
}
130130
}
131131

132-
fromStandardResultFun.addStatement("else -> throw IllegalArgumentException(\"Unknown sentence id \$sentenceId\")")
132+
fromStandardResultFun.addStatement($$"""else -> throw IllegalArgumentException("Unknown sentence id $sentenceId")""")
133133
fromStandardResultFun.endControlFlow()
134134
return fromStandardResultFun.build()
135135
}
@@ -161,9 +161,10 @@ private fun generateLanguageToDataProperty(skill: ParsedSkill, resultType: Class
161161
skill.specificity.name,
162162
"::fromStandardScore",
163163
*sentences.flatMap { sentence ->
164+
val definition = correspondingSentenceDefinition(skill, sentence)
164165
sequenceOf(
165166
sentence.id,
166-
generateConstruct(sentence.constructs),
167+
generateConstruct(sentence.constructs, definition),
167168
)
168169
}.toTypedArray()
169170
)

0 commit comments

Comments
 (0)