Coverage Summary for Class: RuleAnnotations (it.unibo.tuprolog.argumentation.core.runtime.theory)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| RuleAnnotations |
100%
(1/1)
|
100%
(5/5)
|
86.8%
(33/38)
|
100%
(30/30)
|
99.2%
(242/244)
|
package it.unibo.tuprolog.argumentation.core.runtime.theory
import it.unibo.tuprolog.core.Clause
import it.unibo.tuprolog.core.operators.OperatorSet
import it.unibo.tuprolog.parser.PrologLexer
import it.unibo.tuprolog.parser.sources.SourceText
import it.unibo.tuprolog.parser.tokens.TokenKind
import it.unibo.tuprolog.theory.Theory
import it.unibo.tuprolog.theory.parsing.parse
/**
* The identifiers a theory gives its clauses in comments: a clause followed, on the line of its full stop, by a
* comment of a single word (`h :- b. % r5`, the clause spanning one line or more) is tagged with it ([RULE]). Under
* prologStrictCompatibility a clause so tagged is the defeasible rule or premise of that identifier, the others
* strict rules and axioms; otherwise the tag changes nothing. The comments are read with the lexer of 2P-Kt, which
* keeps them, and the clauses are those its parser gives.
*/
object RuleAnnotations {
/** The tag of a clause with its identifier */
const val RULE = "it.unibo.tuprolog.argumentation.rule"
// A comment of a single word, the identifier of a rule
private val identifier = Regex("""[a-z][A-Za-z0-9_]*""")
/** [text] parsed with [operators], its clauses tagged with the identifiers of their comments */
fun parse(
text: String,
operators: OperatorSet,
): Theory = annotate(Theory.parse(text, operators), text)
/** The clauses of [theory], parsed from [text], tagged with the identifiers of their comments in [text] */
fun annotate(
theory: Theory,
text: String,
): Theory {
val identifiers = identifiers(text)
val clauses = theory.clauses.toList()
// the clauses are those of the full stops: if they are not (a theory not parsed from text), nothing is tagged
if (identifiers.all { it == null } || identifiers.size != clauses.size) return theory
return Theory.of(
theory.unificator,
clauses.mapIndexed { index, clause ->
identifiers[index]?.let { clause.replaceTags(clause.tags + (RULE to it)) as Clause } ?: clause
},
)
}
/** The identifier [clause] is tagged with, if any */
fun of(clause: Clause): String? = clause.tags[RULE] as? String
// The identifier of the comment after each full stop, in their order (null for a clause with none)
private fun identifiers(text: String): List<String?> {
val source = SourceText(text)
val tokens =
PrologLexer
.default()
.lex(source)
.materialize()
.tokens
.toList()
val identifiers = mutableListOf<String?>()
for (stop in tokens.indices) {
if (tokens[stop].kind != TokenKind.FULL_STOP) continue
var next = stop + 1
while (next < tokens.size && tokens[next].kind == TokenKind.WHITESPACE) next++
val comment = tokens.getOrNull(next)?.takeIf { it.kind == TokenKind.LINE_COMMENT }
identifiers +=
comment
?.takeIf { it.span.start.line == tokens[stop].span.start.line }
?.let { source.text(it.span.start.offset, it.span.endExclusive.offset).removePrefix("%").trim() }
?.takeIf { identifier.matches(it) }
}
return identifiers
}
}