
commonMain.agl.parser.InputFromString.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of agl-processor-jvm8 Show documentation
Show all versions of agl-processor-jvm8 Show documentation
Dynamic, scan-on-demand, parsing; when a regular expression is just not enough
The newest version!
/**
* Copyright (C) 2018 Dr. David H. Akehurst (http://dr.david.h.akehurst.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.akehurst.language.agl.parser
import net.akehurst.language.agl.agl.parser.SentenceDefault
import net.akehurst.language.agl.automaton.LookaheadSetPart
import net.akehurst.language.agl.runtime.graph.CompletedNodesStore
import net.akehurst.language.agl.runtime.structure.*
import net.akehurst.language.agl.sppt.CompleteTreeDataNode
import net.akehurst.language.api.parser.InputLocation
import net.akehurst.language.api.regex.RegexMatcher
import net.akehurst.language.api.sppt.Sentence
internal class InputFromString(
numTerminalRules: Int,
sentenceText: String
) {
companion object {
const val contextSize = 10
val END_OF_TEXT = 3.toChar().toString()
val EOL_PATTERN = Regex("\n", setOf(RegexOption.MULTILINE))
val LEAF_NONE = CompleteTreeDataNode(RuntimeRuleSet.UNDEFINED_RULE, -1, -1, -1, -1)
//TODO: write a scanner that counts eols as it goes, rather than scanning the text twice
fun eolPositions(text: String): List = EOL_PATTERN.findAll(text).map { it.range.first }.toList()
/*
fun locationFor(sentence: String, startPosition: Int, length: Int): InputLocation {
val before = sentence.substring(0, startPosition)
val line = before.count { it == '\n' } + 1
val column = startPosition - before.lastIndexOf('\n')
return InputLocation(startPosition, column, line, length)
}
*/
}
// private var lastlocationCachePosition = -1
// private val locationCache = mutableMapOf()
// seems faster to match literal with regex than substring and startsWith
private val isLookingAt_cache = hashMapOf, Boolean>()
var sentence: Sentence = SentenceDefault(sentenceText); private set
fun contextInText(position: Int): String {
val startIndex = maxOf(0, position - contextSize)
val endIndex = minOf(this.sentence.text.length, position + contextSize)
val forText = this.sentence.text.substring(startIndex, position)
val aftText = this.sentence.text.substring(position, endIndex)
val startOfLine = forText.lastIndexOfAny(listOf("\n", "\r"))
val s = if (-1 == startOfLine) {
0
} else {
startOfLine + 1
}
val forTextAfterLastEol = forText.substring(s)
val startOrStartOfLine = startIndex + startOfLine
val prefix = when {
startOfLine > 0 -> ""
startIndex > 0 -> "..."
else -> ""
}
val postFix = if (endIndex < this.sentence.text.length) "..." else ""
return "$prefix$forTextAfterLastEol^$aftText$postFix"
}
//internal val leaves: MutableMap = mutableMapOf()
// leaves[runtimeRule, position]
internal val leaves = CompletedNodesStore(numTerminalRules, sentenceText.length + 1)
fun reset() {
this.leaves.clear()
}
// used by SPPTParserDefault to build up the sentence
// internal fun append(value: String) {
// this.text += value
// }
// operator fun get(startPosition: Int, nextInputPosition: Int): String {
// return sentence.text.substring(startPosition, nextInputPosition)
// }
internal fun isStart(position: Int): Boolean {
// TODO what if we want t0 parse part of the text?, e.g. sub grammar
return 0 == position
}
internal fun isEnd(position: Int): Boolean {
// TODO what if we want t0 parse part of the text?, e.g. sub grammar
return position >= this.sentence.text.length
}
fun isLookingAt(position: Int, terminalRule: RuntimeRule): Boolean {
val r = isLookingAt_cache[Pair(position, terminalRule)] //TODO: make this configurable
return if (null != r) {
r
} else {
val rhs = terminalRule.rhs
val matched = when {
this.isEnd(position) -> if (terminalRule == RuntimeRuleSet.END_OF_TEXT) true else false //TODO: do we need this
rhs is RuntimeRuleRhsPattern -> rhs.regex.matchesAt(this.sentence.text, position)
rhs is RuntimeRuleRhsLiteral -> this.sentence.text.regionMatches(position, rhs.literalUnescaped, 0, rhs.literalUnescaped.length)
else -> error("Internal Error: not handled")
}
isLookingAt_cache[Pair(position, terminalRule)] = matched
matched
}
}
fun isLookingAtAnyOf(lh: LookaheadSetPart, position: Int): Boolean {
return when {
lh.includesRT -> error("lookahead must be real lookahead values,
© 2015 - 2025 Weber Informatics LLC | Privacy Policy