diff --git a/src/main/kotlin/gay/pizza/pork/main.kt b/src/main/kotlin/gay/pizza/pork/main.kt index 51fc83a..d8377aa 100644 --- a/src/main/kotlin/gay/pizza/pork/main.kt +++ b/src/main/kotlin/gay/pizza/pork/main.kt @@ -53,6 +53,9 @@ fun main(args: Array) { 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 = diff --git a/src/main/kotlin/gay/pizza/pork/parse/AnsiHighlightScheme.kt b/src/main/kotlin/gay/pizza/pork/parse/AnsiHighlightScheme.kt new file mode 100644 index 0000000..31818a6 --- /dev/null +++ b/src/main/kotlin/gay/pizza/pork/parse/AnsiHighlightScheme.kt @@ -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 + ) +} diff --git a/src/main/kotlin/gay/pizza/pork/parse/Highlight.kt b/src/main/kotlin/gay/pizza/pork/parse/Highlight.kt new file mode 100644 index 0000000..75fd20d --- /dev/null +++ b/src/main/kotlin/gay/pizza/pork/parse/Highlight.kt @@ -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 +} diff --git a/src/main/kotlin/gay/pizza/pork/parse/HighlightScheme.kt b/src/main/kotlin/gay/pizza/pork/parse/HighlightScheme.kt new file mode 100644 index 0000000..96ee358 --- /dev/null +++ b/src/main/kotlin/gay/pizza/pork/parse/HighlightScheme.kt @@ -0,0 +1,5 @@ +package gay.pizza.pork.parse + +interface HighlightScheme { + fun highlight(token: Token): Highlight +} diff --git a/src/main/kotlin/gay/pizza/pork/parse/Highlighter.kt b/src/main/kotlin/gay/pizza/pork/parse/Highlighter.kt index 1f5370d..cb8fc54 100644 --- a/src/main/kotlin/gay/pizza/pork/parse/Highlighter.kt +++ b/src/main/kotlin/gay/pizza/pork/parse/Highlighter.kt @@ -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) -} \ No newline at end of file +class Highlighter(val scheme: HighlightScheme) { + fun highlight(stream: TokenStream): List = + stream.tokens.map { scheme.highlight(it) } +} diff --git a/src/main/kotlin/gay/pizza/pork/parse/TokenFamily.kt b/src/main/kotlin/gay/pizza/pork/parse/TokenFamily.kt new file mode 100644 index 0000000..ec05ec0 --- /dev/null +++ b/src/main/kotlin/gay/pizza/pork/parse/TokenFamily.kt @@ -0,0 +1,10 @@ +package gay.pizza.pork.parse + +enum class TokenFamily : TokenTypeProperty { + OperatorFamily, + KeywordFamily, + SymbolFamily, + NumericLiteralFamily, + CommentFamily, + OtherFamily +} diff --git a/src/main/kotlin/gay/pizza/pork/parse/TokenProcessor.kt b/src/main/kotlin/gay/pizza/pork/parse/TokenProcessor.kt deleted file mode 100644 index 78f7099..0000000 --- a/src/main/kotlin/gay/pizza/pork/parse/TokenProcessor.kt +++ /dev/null @@ -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) -} diff --git a/src/main/kotlin/gay/pizza/pork/parse/TokenStream.kt b/src/main/kotlin/gay/pizza/pork/parse/TokenStream.kt index e415d66..b55fd5b 100644 --- a/src/main/kotlin/gay/pizza/pork/parse/TokenStream.kt +++ b/src/main/kotlin/gay/pizza/pork/parse/TokenStream.kt @@ -1,8 +1,5 @@ package gay.pizza.pork.parse class TokenStream(val tokens: List) { - fun excludeAllWhitespace(): TokenStream = - TokenStream(tokens.filter { it.type != TokenType.Whitespace }) - override fun toString(): String = tokens.toString() } diff --git a/src/main/kotlin/gay/pizza/pork/parse/TokenType.kt b/src/main/kotlin/gay/pizza/pork/parse/TokenType.kt index 0fcb721..1f00ad6 100644 --- a/src/main/kotlin/gay/pizza/pork/parse/TokenType.kt +++ b/src/main/kotlin/gay/pizza/pork/parse/TokenType.kt @@ -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 = properties.filterIsInstance() val keyword: Keyword? = properties.filterIsInstance().singleOrNull() val singleChar: SingleChar? = properties.filterIsInstance().singleOrNull() + val family: TokenFamily = + properties.filterIsInstance().singleOrNull() ?: OtherFamily companion object { val Keywords = entries.filter { item -> item.keyword != null } diff --git a/src/main/kotlin/gay/pizza/pork/parse/TokenTypeProperty.kt b/src/main/kotlin/gay/pizza/pork/parse/TokenTypeProperty.kt index f166143..1f22f60 100644 --- a/src/main/kotlin/gay/pizza/pork/parse/TokenTypeProperty.kt +++ b/src/main/kotlin/gay/pizza/pork/parse/TokenTypeProperty.kt @@ -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 }