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) {
 | 
					  if (program != parsedAst) {
 | 
				
			||||||
    throw RuntimeException("Equality of parsed AST from printer was not proven.")
 | 
					    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 =
 | 
					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
 | 
					package gay.pizza.pork.parse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
abstract class Highlighter(source: TokenSource) : TokenProcessor(source) {
 | 
					class Highlighter(val scheme: HighlightScheme) {
 | 
				
			||||||
  override fun process(token: Token) {
 | 
					  fun highlight(stream: TokenStream): List<Highlight> =
 | 
				
			||||||
    when {
 | 
					    stream.tokens.map { scheme.highlight(it) }
 | 
				
			||||||
      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)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										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
 | 
					package gay.pizza.pork.parse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TokenStream(val tokens: List<Token>) {
 | 
					class TokenStream(val tokens: List<Token>) {
 | 
				
			||||||
  fun excludeAllWhitespace(): TokenStream =
 | 
					 | 
				
			||||||
    TokenStream(tokens.filter { it.type != TokenType.Whitespace })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun toString(): String = tokens.toString()
 | 
					  override fun toString(): String = tokens.toString()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,39 +1,42 @@
 | 
				
			|||||||
package gay.pizza.pork.parse
 | 
					package gay.pizza.pork.parse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import gay.pizza.pork.parse.TokenTypeProperty.*
 | 
					import gay.pizza.pork.parse.TokenTypeProperty.*
 | 
				
			||||||
 | 
					import gay.pizza.pork.parse.TokenFamily.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum class TokenType(vararg properties: TokenTypeProperty) {
 | 
					enum class TokenType(vararg properties: TokenTypeProperty) {
 | 
				
			||||||
  Symbol,
 | 
					  Symbol(SymbolFamily),
 | 
				
			||||||
  IntLiteral,
 | 
					  IntLiteral(NumericLiteralFamily),
 | 
				
			||||||
  Equality,
 | 
					  Equality(OperatorFamily),
 | 
				
			||||||
  Inequality,
 | 
					  Inequality(OperatorFamily),
 | 
				
			||||||
  Equals(SingleChar('='), Promotion('=', Equality)),
 | 
					  Equals(SingleChar('='), Promotion('=', Equality)),
 | 
				
			||||||
  Plus(SingleChar('+')),
 | 
					  Plus(SingleChar('+'), OperatorFamily),
 | 
				
			||||||
  Minus(SingleChar('-')),
 | 
					  Minus(SingleChar('-'), OperatorFamily),
 | 
				
			||||||
  Multiply(SingleChar('*')),
 | 
					  Multiply(SingleChar('*'), OperatorFamily),
 | 
				
			||||||
  Divide(SingleChar('/')),
 | 
					  Divide(SingleChar('/'), OperatorFamily),
 | 
				
			||||||
  LeftCurly(SingleChar('{')),
 | 
					  LeftCurly(SingleChar('{')),
 | 
				
			||||||
  RightCurly(SingleChar('}')),
 | 
					  RightCurly(SingleChar('}')),
 | 
				
			||||||
  LeftBracket(SingleChar('[')),
 | 
					  LeftBracket(SingleChar('[')),
 | 
				
			||||||
  RightBracket(SingleChar(']')),
 | 
					  RightBracket(SingleChar(']')),
 | 
				
			||||||
  LeftParentheses(SingleChar('(')),
 | 
					  LeftParentheses(SingleChar('(')),
 | 
				
			||||||
  RightParentheses(SingleChar(')')),
 | 
					  RightParentheses(SingleChar(')')),
 | 
				
			||||||
  Negation(SingleChar('!'), Promotion('=', Inequality)),
 | 
					  Negation(SingleChar('!'), Promotion('=', Inequality), OperatorFamily),
 | 
				
			||||||
  Comma(SingleChar(',')),
 | 
					  Comma(SingleChar(',')),
 | 
				
			||||||
  False(Keyword("false")),
 | 
					  False(Keyword("false"), KeywordFamily),
 | 
				
			||||||
  True(Keyword("true")),
 | 
					  True(Keyword("true"), KeywordFamily),
 | 
				
			||||||
  In(Keyword("in")),
 | 
					  In(Keyword("in"), KeywordFamily),
 | 
				
			||||||
  If(Keyword("if")),
 | 
					  If(Keyword("if"), KeywordFamily),
 | 
				
			||||||
  Then(Keyword("then")),
 | 
					  Then(Keyword("then"), KeywordFamily),
 | 
				
			||||||
  Else(Keyword("else")),
 | 
					  Else(Keyword("else"), KeywordFamily),
 | 
				
			||||||
  Whitespace,
 | 
					  Whitespace,
 | 
				
			||||||
  BlockComment,
 | 
					  BlockComment(CommentFamily),
 | 
				
			||||||
  LineComment,
 | 
					  LineComment(CommentFamily),
 | 
				
			||||||
  EndOfFile;
 | 
					  EndOfFile;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  val promotions: List<Promotion> = properties.filterIsInstance<Promotion>()
 | 
					  val promotions: List<Promotion> = properties.filterIsInstance<Promotion>()
 | 
				
			||||||
  val keyword: Keyword? = properties.filterIsInstance<Keyword>().singleOrNull()
 | 
					  val keyword: Keyword? = properties.filterIsInstance<Keyword>().singleOrNull()
 | 
				
			||||||
  val singleChar: SingleChar? = properties.filterIsInstance<SingleChar>().singleOrNull()
 | 
					  val singleChar: SingleChar? = properties.filterIsInstance<SingleChar>().singleOrNull()
 | 
				
			||||||
 | 
					  val family: TokenFamily =
 | 
				
			||||||
 | 
					    properties.filterIsInstance<TokenFamily>().singleOrNull() ?: OtherFamily
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  companion object {
 | 
					  companion object {
 | 
				
			||||||
    val Keywords = entries.filter { item -> item.keyword != null }
 | 
					    val Keywords = entries.filter { item -> item.keyword != null }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
package gay.pizza.pork.parse
 | 
					package gay.pizza.pork.parse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sealed class TokenTypeProperty {
 | 
					interface TokenTypeProperty {
 | 
				
			||||||
  class SingleChar(val char: Char) : TokenTypeProperty()
 | 
					  class SingleChar(val char: Char) : TokenTypeProperty
 | 
				
			||||||
  class Promotion(val nextChar: Char, val type: TokenType) : TokenTypeProperty()
 | 
					  class Promotion(val nextChar: Char, val type: TokenType) : TokenTypeProperty
 | 
				
			||||||
  class Keyword(val text: String) : TokenTypeProperty()
 | 
					  class Keyword(val text: String) : TokenTypeProperty
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user