-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathPDDL.kt
151 lines (131 loc) · 4.86 KB
/
PDDL.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package com.softbankrobotics.pddlplanning
import com.softbankrobotics.pddlplanning.Fact
import com.softbankrobotics.pddlplanning.Goal
import com.softbankrobotics.pddlplanning.Instance
/** Checks that the character at the given position is '('. */
private fun assertOpenParenthesis(pddl: String, position: Int) {
assert(pddl[position] == '(') {
"parenthesis location $position does not point at a parenthesis, instead points at ${pddl[position]}"
}
}
/** Find first child expression. */
private fun firstChildExpression(pddl: String, position: Int): Int? {
assertOpenParenthesis(pddl, position)
pddl.substring(position).forEachIndexed { i, c ->
when (c) {
'(' -> if (i != 0) return position + i
')' -> return null
}
}
throw RuntimeException("malformed PDDL: parenthesis opened at $position is never closed")
}
/** Find next sibling expression. */
private fun nextSiblingExpression(pddl: String, position: Int): Int? {
assertOpenParenthesis(pddl, position)
var depth = 0
pddl.substring(position).forEachIndexed { i, c ->
when (c) {
'(' -> if (++depth == 1 && i != 0) return position + i
')' -> if (--depth < 0) return null
}
}
if (depth > 0)
throw RuntimeException("malformed PDDL: parenthesis opened at $position is never closed")
return null
}
/** Iterate using a breadth-first walk. */
private class BreadthFirstIterator(private val pddl: String, private var position: Int) :
Iterator<Int> {
private var next: Int? = null
private val nextChildren = mutableListOf<Int>()
init {
assertOpenParenthesis(pddl, position)
next = position
}
override fun hasNext(): Boolean {
return next != null
}
override fun next(): Int {
assert(hasNext())
val result = next!!
position = result
// prepare next
firstChildExpression(pddl, position)?.let { nextChildren.add(it) }
next = nextSiblingExpression(pddl, position)
if (next == null) {
if (nextChildren.isNotEmpty()) {
next = nextChildren.first()
nextChildren.removeAt(0)
}
}
return result
}
}
fun wordAt(pddl: String, position: Int): String {
assertOpenParenthesis(pddl, position)
val ahead = pddl.substring(position + 1).trim()
val endOfWord = ahead.indexOfFirst { c ->
c.isWhitespace() || c in listOf('(', ')')
}
return ahead.substring(0, endOfWord)
}
private fun expressionRangeAt(pddl: String, position: Int): IntRange {
assertOpenParenthesis(pddl, position)
var depth = 0
pddl.substring(position).forEachIndexed { i, c ->
when (c) {
'(' -> ++depth
')' -> if (--depth == 0) return IntRange(position, position + i)
}
}
throw RuntimeException("malformed PDDL: parenthesis opened at $position is never closed")
}
/** Splits PDDL content into a domain and a problem. */
fun splitDomainAndProblem(pddlContent: String): Pair<String, String> {
val domain = pddlContent.substringBeforeLast("(define ")
val problem = pddlContent.substring(domain.length)
return Pair(domain, problem)
}
/**
* Looks for an expression starting with the given word and returns the range of the expression.
*/
fun findExpressionWithWord(problem: String, word: String): IntRange? {
val trimmed = problem.trim()
val iterator = BreadthFirstIterator(trimmed, 0)
while (iterator.hasNext()) {
val position = iterator.next()
if (wordAt(trimmed, position) == word)
return expressionRangeAt(trimmed, position)
}
return null
}
fun replaceObjects(problem: String, instances: Iterable<Instance>): String {
val range = findExpressionWithWord(problem, ":objects")!!
var newObjects = "(:objects\n "
for (instance in instances)
newObjects += instance.declaration() + "\n "
newObjects += ")"
return problem.replaceRange(range, newObjects)
}
/** Replaces the :init section of a problem with the given facts. */
fun replaceInit(problem: String, facts: Iterable<Fact>): String {
val range = findExpressionWithWord(problem, ":init")!!
val newInit = "(:init\n${facts.joinToString("\n ")})"
return problem.replaceRange(range, newInit)
}
/**
* Replaces the :goal section of a problem with the given goals.
*/
fun replaceGoal(problem: String, goals: List<Goal>): String {
val range = findExpressionWithWord(problem, ":goal")
if (range == null) {
throw RuntimeException("no goal section found in PDDL problem")
} else {
val newGoal = "(:goal" + when {
goals.size > 1 -> "\n (and\n ${goals.joinToString("\n ")}\n ))"
goals.size == 1 -> "\n ${goals.joinToString("\n ")}\n )"
else -> ")"
}
return problem.replaceRange(range, newGoal)
}
}