mirror of
				https://github.com/GayPizzaSpecifications/pork.git
				synced 2025-11-04 09:59:39 +00:00 
			
		
		
		
	Implement syntax highlighter.
This commit is contained in:
		@ -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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user