mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 12:50:55 +00:00
Implement syntax highlighter.
This commit is contained in:
parent
95fe6e9abb
commit
e12b51e8a7
@ -53,6 +53,9 @@ fun main(args: Array<String>) {
|
||||
if (program != parsedAst) {
|
||||
throw RuntimeException("Equality of parsed AST from printer was not proven.")
|
||||
}
|
||||
|
||||
val highlighter = Highlighter(AnsiHighlightScheme())
|
||||
println(highlighter.highlight(stream).joinToString(""))
|
||||
}
|
||||
|
||||
fun tokenize(input: String): TokenStream =
|
||||
|
29
src/main/kotlin/gay/pizza/pork/parse/AnsiHighlightScheme.kt
Normal file
29
src/main/kotlin/gay/pizza/pork/parse/AnsiHighlightScheme.kt
Normal file
@ -0,0 +1,29 @@
|
||||
package gay.pizza.pork.parse
|
||||
|
||||
open class AnsiHighlightScheme : HighlightScheme {
|
||||
override fun highlight(token: Token): Highlight {
|
||||
val attributes = when (token.type.family) {
|
||||
TokenFamily.OperatorFamily -> operator()
|
||||
TokenFamily.KeywordFamily -> keyword()
|
||||
TokenFamily.SymbolFamily -> symbol()
|
||||
TokenFamily.CommentFamily -> comment()
|
||||
else -> null
|
||||
}
|
||||
|
||||
return if (attributes != null) {
|
||||
Highlight(token, ansi(attributes, token.text))
|
||||
} else Highlight(token)
|
||||
}
|
||||
|
||||
open fun operator(): AnsiAttributes = AnsiAttributes("32m")
|
||||
open fun symbol(): AnsiAttributes = AnsiAttributes("33m")
|
||||
open fun keyword(): AnsiAttributes = AnsiAttributes("35m")
|
||||
open fun comment(): AnsiAttributes = AnsiAttributes("37m")
|
||||
|
||||
private fun ansi(attributes: AnsiAttributes, text: String): String =
|
||||
"\u001b[${attributes.color}${text}\u001b[0m"
|
||||
|
||||
class AnsiAttributes(
|
||||
val color: String
|
||||
)
|
||||
}
|
5
src/main/kotlin/gay/pizza/pork/parse/Highlight.kt
Normal file
5
src/main/kotlin/gay/pizza/pork/parse/Highlight.kt
Normal file
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.parse
|
||||
|
||||
class Highlight(val token: Token, val text: String? = null) {
|
||||
override fun toString(): String = text ?: token.text
|
||||
}
|
5
src/main/kotlin/gay/pizza/pork/parse/HighlightScheme.kt
Normal file
5
src/main/kotlin/gay/pizza/pork/parse/HighlightScheme.kt
Normal file
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.parse
|
||||
|
||||
interface HighlightScheme {
|
||||
fun highlight(token: Token): Highlight
|
||||
}
|
@ -1,19 +1,6 @@
|
||||
package gay.pizza.pork.parse
|
||||
|
||||
abstract class Highlighter(source: TokenSource) : TokenProcessor(source) {
|
||||
override fun process(token: Token) {
|
||||
when {
|
||||
token.type.keyword != null -> {
|
||||
keyword(token)
|
||||
}
|
||||
token.type == TokenType.Symbol -> {
|
||||
symbol(token)
|
||||
}
|
||||
else -> other(token)
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun keyword(token: Token)
|
||||
abstract fun symbol(token: Token)
|
||||
abstract fun other(token: Token)
|
||||
}
|
||||
class Highlighter(val scheme: HighlightScheme) {
|
||||
fun highlight(stream: TokenStream): List<Highlight> =
|
||||
stream.tokens.map { scheme.highlight(it) }
|
||||
}
|
||||
|
10
src/main/kotlin/gay/pizza/pork/parse/TokenFamily.kt
Normal file
10
src/main/kotlin/gay/pizza/pork/parse/TokenFamily.kt
Normal file
@ -0,0 +1,10 @@
|
||||
package gay.pizza.pork.parse
|
||||
|
||||
enum class TokenFamily : TokenTypeProperty {
|
||||
OperatorFamily,
|
||||
KeywordFamily,
|
||||
SymbolFamily,
|
||||
NumericLiteralFamily,
|
||||
CommentFamily,
|
||||
OtherFamily
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package gay.pizza.pork.parse
|
||||
|
||||
abstract class TokenProcessor(val source: TokenSource) {
|
||||
fun processAll() {
|
||||
while (true) {
|
||||
val token = source.next()
|
||||
process(token)
|
||||
if (token.type == TokenType.EndOfFile) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun process(token: Token)
|
||||
}
|
@ -1,8 +1,5 @@
|
||||
package gay.pizza.pork.parse
|
||||
|
||||
class TokenStream(val tokens: List<Token>) {
|
||||
fun excludeAllWhitespace(): TokenStream =
|
||||
TokenStream(tokens.filter { it.type != TokenType.Whitespace })
|
||||
|
||||
override fun toString(): String = tokens.toString()
|
||||
}
|
||||
|
@ -1,39 +1,42 @@
|
||||
package gay.pizza.pork.parse
|
||||
|
||||
import gay.pizza.pork.parse.TokenTypeProperty.*
|
||||
import gay.pizza.pork.parse.TokenFamily.*
|
||||
|
||||
enum class TokenType(vararg properties: TokenTypeProperty) {
|
||||
Symbol,
|
||||
IntLiteral,
|
||||
Equality,
|
||||
Inequality,
|
||||
Symbol(SymbolFamily),
|
||||
IntLiteral(NumericLiteralFamily),
|
||||
Equality(OperatorFamily),
|
||||
Inequality(OperatorFamily),
|
||||
Equals(SingleChar('='), Promotion('=', Equality)),
|
||||
Plus(SingleChar('+')),
|
||||
Minus(SingleChar('-')),
|
||||
Multiply(SingleChar('*')),
|
||||
Divide(SingleChar('/')),
|
||||
Plus(SingleChar('+'), OperatorFamily),
|
||||
Minus(SingleChar('-'), OperatorFamily),
|
||||
Multiply(SingleChar('*'), OperatorFamily),
|
||||
Divide(SingleChar('/'), OperatorFamily),
|
||||
LeftCurly(SingleChar('{')),
|
||||
RightCurly(SingleChar('}')),
|
||||
LeftBracket(SingleChar('[')),
|
||||
RightBracket(SingleChar(']')),
|
||||
LeftParentheses(SingleChar('(')),
|
||||
RightParentheses(SingleChar(')')),
|
||||
Negation(SingleChar('!'), Promotion('=', Inequality)),
|
||||
Negation(SingleChar('!'), Promotion('=', Inequality), OperatorFamily),
|
||||
Comma(SingleChar(',')),
|
||||
False(Keyword("false")),
|
||||
True(Keyword("true")),
|
||||
In(Keyword("in")),
|
||||
If(Keyword("if")),
|
||||
Then(Keyword("then")),
|
||||
Else(Keyword("else")),
|
||||
False(Keyword("false"), KeywordFamily),
|
||||
True(Keyword("true"), KeywordFamily),
|
||||
In(Keyword("in"), KeywordFamily),
|
||||
If(Keyword("if"), KeywordFamily),
|
||||
Then(Keyword("then"), KeywordFamily),
|
||||
Else(Keyword("else"), KeywordFamily),
|
||||
Whitespace,
|
||||
BlockComment,
|
||||
LineComment,
|
||||
BlockComment(CommentFamily),
|
||||
LineComment(CommentFamily),
|
||||
EndOfFile;
|
||||
|
||||
val promotions: List<Promotion> = properties.filterIsInstance<Promotion>()
|
||||
val keyword: Keyword? = properties.filterIsInstance<Keyword>().singleOrNull()
|
||||
val singleChar: SingleChar? = properties.filterIsInstance<SingleChar>().singleOrNull()
|
||||
val family: TokenFamily =
|
||||
properties.filterIsInstance<TokenFamily>().singleOrNull() ?: OtherFamily
|
||||
|
||||
companion object {
|
||||
val Keywords = entries.filter { item -> item.keyword != null }
|
||||
|
@ -1,7 +1,7 @@
|
||||
package gay.pizza.pork.parse
|
||||
|
||||
sealed class TokenTypeProperty {
|
||||
class SingleChar(val char: Char) : TokenTypeProperty()
|
||||
class Promotion(val nextChar: Char, val type: TokenType) : TokenTypeProperty()
|
||||
class Keyword(val text: String) : TokenTypeProperty()
|
||||
interface TokenTypeProperty {
|
||||
class SingleChar(val char: Char) : TokenTypeProperty
|
||||
class Promotion(val nextChar: Char, val type: TokenType) : TokenTypeProperty
|
||||
class Keyword(val text: String) : TokenTypeProperty
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user