parser: switch to char matcher interface

This commit is contained in:
Alex Zenla 2023-10-06 15:30:43 -07:00
parent 97283636bc
commit d36310e698
Signed by: alex
GPG Key ID: C0780728420EBFE5
7 changed files with 65 additions and 34 deletions

View File

@ -11,9 +11,14 @@ class IndentPrinter(
append(indentLevelText)
}
fun emitIndentedLine(line: String) {
emitIndent()
appendLine(line)
}
fun increaseIndent() {
indentLevel++
indentLevelText = indent.repeat(indentLevel)
indentLevelText += indent
}
fun decreaseIndent() {
@ -21,5 +26,11 @@ class IndentPrinter(
indentLevelText = indent.repeat(indentLevel)
}
inline fun indented(block: IndentPrinter.() -> Unit) {
increaseIndent()
block(this)
decreaseIndent()
}
override fun toString(): String = buffer.toString()
}

View File

@ -0,0 +1,26 @@
package gay.pizza.pork.parser
fun interface CharMatcher {
fun valid(char: Char, index: Int): Boolean
class AnyOf(vararg val filters: CharMatcher) : CharMatcher {
override fun valid(char: Char, index: Int): Boolean =
filters.any { it.valid(char, index) }
}
class MatchSingle(val char: Char) : CharMatcher {
override fun valid(char: Char, index: Int): Boolean =
char == this.char
}
class MatchRange(val charRange: CharRange) : CharMatcher {
override fun valid(char: Char, index: Int): Boolean =
charRange.contains(char)
}
class NotAtIndex(val index: Int, val matcher: CharMatcher) : CharMatcher {
override fun valid(char: Char, index: Int): Boolean {
return this.index != index && matcher.valid(char, index)
}
}
}

View File

@ -1,16 +1,21 @@
package gay.pizza.pork.parser
import gay.pizza.pork.parser.CharMatcher.*
import gay.pizza.pork.parser.TokenTypeProperty.*
import gay.pizza.pork.parser.TokenFamily.*
import gay.pizza.pork.parser.TokenTypeProperty.AnyOf
enum class TokenType(vararg properties: TokenTypeProperty) {
NumberLiteral(NumericLiteralFamily, CharIndexConsumer { it, index ->
(it in '0'..'9') || (index > 0 && it == '.') }),
Symbol(SymbolFamily, CharConsumer {
(it in 'a'..'z') ||
(it in 'A'..'Z') ||
(it == '_') ||
(it in '0' .. '9')}, KeywordUpgrader),
NumberLiteral(NumericLiteralFamily, CharConsumer(CharMatcher.AnyOf(
MatchRange('0'..'9'),
NotAtIndex(0, MatchSingle('.'))
))),
Symbol(SymbolFamily, CharConsumer(CharMatcher.AnyOf(
MatchRange('a'..'z'),
MatchRange('A'..'Z'),
MatchRange('0' .. '9'),
MatchSingle('_')
)), KeywordUpgrader),
StringLiteral(StringLiteralFamily),
Equality(OperatorFamily),
Inequality(ManyChars("!="), OperatorFamily),
@ -61,7 +66,12 @@ enum class TokenType(vararg properties: TokenTypeProperty) {
Native(ManyChars("native"), KeywordFamily),
Let(ManyChars("let"), KeywordFamily),
Var(ManyChars("var"), KeywordFamily),
Whitespace(CharConsumer { it == ' ' || it == '\r' || it == '\n' || it == '\t' }),
Whitespace(CharConsumer(CharMatcher.AnyOf(
MatchSingle(' '),
MatchSingle('\r'),
MatchSingle('\n'),
MatchSingle('\t')
))),
BlockComment(CommentFamily),
LineComment(CommentFamily),
EndOfFile;
@ -77,8 +87,6 @@ enum class TokenType(vararg properties: TokenTypeProperty) {
val family: TokenFamily =
properties.filterIsInstance<TokenFamily>().singleOrNull() ?: OtherFamily
val charConsumer: CharConsumer? = properties.filterIsInstance<CharConsumer>().singleOrNull()
val charIndexConsumer: CharIndexConsumer? =
properties.filterIsInstance<CharIndexConsumer>().singleOrNull()
val tokenUpgrader: TokenUpgrader? =
properties.filterIsInstance<TokenUpgrader>().singleOrNull()
@ -89,7 +97,7 @@ enum class TokenType(vararg properties: TokenTypeProperty) {
val ManyChars = entries.filter { item -> item.manyChars != null }
val SingleChars = entries.filter { item -> item.singleChar != null }
val CharConsumers = entries.filter { item ->
item.charConsumer != null || item.charIndexConsumer != null }
item.charConsumer != null }
val ParserIgnoredTypes: Array<TokenType> = arrayOf(
Whitespace,

View File

@ -5,8 +5,7 @@ interface TokenTypeProperty {
class Promotion(val nextChar: Char, val type: TokenType) : TokenTypeProperty
class ManyChars(val text: String) : TokenTypeProperty
class AnyOf(vararg val strings: String): TokenTypeProperty
class CharConsumer(val isValid: (Char) -> Boolean) : TokenTypeProperty
class CharIndexConsumer(val isValid: (Char, Int) -> Boolean) : TokenTypeProperty
open class CharConsumer(val matcher: CharMatcher) : TokenTypeProperty
open class TokenUpgrader(val maybeUpgrade: (Token) -> Token?) : TokenTypeProperty
object KeywordUpgrader : TokenUpgrader({ token ->

View File

@ -102,27 +102,14 @@ class Tokenizer(val source: CharSource) {
var index = 0
for (item in TokenType.CharConsumers) {
if (item.charConsumer != null) {
if (!item.charConsumer.isValid(char)) {
if (!item.charConsumer!!.matcher.valid(char, index)) {
continue
}
} else if (item.charIndexConsumer != null) {
if (!item.charIndexConsumer.isValid(char, index)) {
continue
}
} else {
throw ParseError("Unknown Char Consumer")
}
val text = buildString {
append(char)
while (
if (item.charConsumer != null)
item.charConsumer.isValid(source.peek())
else
item.charIndexConsumer!!.isValid(source.peek(), ++index)
) {
while (item.charConsumer.matcher.valid(source.peek(), ++index)) {
append(nextChar())
}
}
@ -168,5 +155,6 @@ class Tokenizer(val source: CharSource) {
return char
}
private fun currentSourceIndex(): SourceIndex = SourceIndex(source.currentIndex, currentLineIndex, currentLineColumn)
private fun currentSourceIndex(): SourceIndex =
SourceIndex(source.currentIndex, currentLineIndex, currentLineColumn)
}

View File

@ -1,7 +1,7 @@
import gay.pizza.pork.buildext.AstCodegenType
plugins {
id("org.jetbrains.intellij") version "1.15.0"
id("org.jetbrains.intellij") version "1.16.0"
id("gay.pizza.pork.module")
id("gay.pizza.pork.ast")
}

View File

@ -11,9 +11,8 @@ import gay.pizza.pork.idea.psi.gen.PorkElement
class PorkSymbolDeclaration(val element: PorkElement) : PsiSymbolDeclaration {
override fun getDeclaringElement(): PsiElement = element
override fun getRangeInDeclaringElement(): TextRange {
val textRangeOfSymbol = PorkElementHelpers.symbolElementOf(element)?.psi?.textRangeInParent
return PorkElementHelpers.symbolElementOf(element)?.psi?.textRangeInParent
?: throw RuntimeException("Unable to get symbol of element: $element")
return textRangeOfSymbol
}
override fun getSymbol(): Symbol = PorkElementHelpers.psiSymbolFor(element) ?: