build: move parser and tokenizer into separate modules

This commit is contained in:
Alex Zenla 2023-10-16 21:52:21 -07:00
parent 9338b01b48
commit 15f5f313cc
Signed by: alex
GPG Key ID: C0780728420EBFE5
57 changed files with 92 additions and 61 deletions

View File

@ -2,9 +2,9 @@ package gay.pizza.pork.ffi
import gay.pizza.pork.ast.gen.visit
import gay.pizza.pork.frontend.ContentSource
import gay.pizza.pork.parser.CharSource
import gay.pizza.pork.tokenizer.CharSource
import gay.pizza.pork.parser.Printer
import gay.pizza.pork.parser.StringCharSource
import gay.pizza.pork.tokenizer.StringCharSource
object JavaAutogenContentSource : ContentSource {
override fun loadAsCharSource(path: String): CharSource {

View File

@ -1,6 +1,6 @@
package gay.pizza.pork.frontend
import gay.pizza.pork.parser.CharSource
import gay.pizza.pork.tokenizer.CharSource
interface ContentSource {
fun loadAsCharSource(path: String): CharSource

View File

@ -3,8 +3,8 @@ package gay.pizza.pork.frontend
import gay.pizza.dough.fs.FsPath
import gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.dough.fs.readString
import gay.pizza.pork.parser.CharSource
import gay.pizza.pork.parser.StringCharSource
import gay.pizza.pork.tokenizer.CharSource
import gay.pizza.pork.tokenizer.StringCharSource
class FsContentSource(val root: FsPath) : ContentSource {
override fun loadAsCharSource(path: String): CharSource =

View File

@ -4,7 +4,7 @@ import gay.pizza.pork.ast.gen.CompilationUnit
import gay.pizza.pork.ast.gen.ImportDeclaration
import gay.pizza.pork.parser.DiscardNodeAttribution
import gay.pizza.pork.parser.Parser
import gay.pizza.pork.parser.Tokenizer
import gay.pizza.pork.tokenizer.Tokenizer
class World(val importSource: ImportSource) {
private val internalUnits = mutableMapOf<String, CompilationUnit>()

View File

@ -4,8 +4,8 @@ import gay.pizza.dough.fs.FsPath
import gay.pizza.dough.fs.readString
import gay.pizza.pork.frontend.ContentSource
import gay.pizza.pork.frontend.FsContentSource
import gay.pizza.pork.parser.CharSource
import gay.pizza.pork.parser.StringCharSource
import gay.pizza.pork.tokenizer.CharSource
import gay.pizza.pork.tokenizer.StringCharSource
class FileTool(val path: FsPath) : Tool() {
override fun createCharSource(): CharSource =

View File

@ -13,6 +13,7 @@ import gay.pizza.pork.frontend.DynamicImportSource
import gay.pizza.pork.frontend.World
import gay.pizza.pork.parser.*
import gay.pizza.pork.stdlib.PorkStdlib
import gay.pizza.pork.tokenizer.*
abstract class Tool {
abstract fun createCharSource(): CharSource

View File

@ -4,6 +4,7 @@ plugins {
dependencies {
api(project(":ast"))
api(project(":tokenizer"))
implementation(project(":common"))
}

View File

@ -1,5 +0,0 @@
package gay.pizza.pork.parser
class BadCharacterError(val char: Char, sourceIndex: SourceIndex, state: TokenizerState) : ParseError(
"Failed to produce token for '${char}' at $sourceIndex in state $state"
)

View File

@ -2,6 +2,7 @@ package gay.pizza.pork.parser
import gay.pizza.pork.ast.gen.Node
import gay.pizza.pork.ast.gen.NodeType
import gay.pizza.pork.tokenizer.Token
object DiscardNodeAttribution : NodeAttribution {
override fun push(token: Token) {}

View File

@ -1,5 +1,9 @@
package gay.pizza.pork.parser
import gay.pizza.pork.tokenizer.Token
import gay.pizza.pork.tokenizer.TokenSource
import gay.pizza.pork.tokenizer.TokenType
class LazySkippingTokenSource(val source: TokenSource, val skipping: Set<TokenType>) : ParserAwareTokenSource {
private var index = 0
override val currentIndex: Int

View File

@ -2,6 +2,7 @@ package gay.pizza.pork.parser
import gay.pizza.pork.ast.gen.Node
import gay.pizza.pork.ast.gen.NodeType
import gay.pizza.pork.tokenizer.Token
interface NodeAttribution {
fun push(token: Token)

View File

@ -1,6 +1,8 @@
package gay.pizza.pork.parser
import gay.pizza.pork.ast.gen.*
import gay.pizza.pork.tokenizer.TokenSource
import gay.pizza.pork.tokenizer.TokenType
class Parser(source: TokenSource, attribution: NodeAttribution) :
ParserBase(source, attribution) {

View File

@ -4,6 +4,7 @@ import gay.pizza.pork.ast.gen.Node
import gay.pizza.pork.ast.gen.NodeCoalescer
import gay.pizza.pork.ast.gen.data
import gay.pizza.pork.ast.gen.visit
import gay.pizza.pork.tokenizer.Token
data class ParserAttributes(val tokens: List<Token>) {
companion object {

View File

@ -1,3 +1,5 @@
package gay.pizza.pork.parser
import gay.pizza.pork.tokenizer.TokenSource
interface ParserAwareTokenSource : TokenSource

View File

@ -3,6 +3,7 @@ package gay.pizza.pork.parser
import gay.pizza.pork.ast.gen.Node
import gay.pizza.pork.ast.gen.NodeParser
import gay.pizza.pork.ast.gen.NodeType
import gay.pizza.pork.tokenizer.*
abstract class ParserBase(source: TokenSource, val attribution: NodeAttribution) : NodeParser {
val source: TokenSource = if (source is ParserAwareTokenSource) {

View File

@ -3,6 +3,8 @@ package gay.pizza.pork.parser
import gay.pizza.pork.ast.gen.InfixOperator
import gay.pizza.pork.ast.gen.PrefixOperator
import gay.pizza.pork.ast.gen.SuffixOperator
import gay.pizza.pork.tokenizer.Token
import gay.pizza.pork.tokenizer.TokenType
internal object ParserHelpers {
fun convertInfixOperator(token: Token): InfixOperator = when (token.type) {

View File

@ -3,6 +3,7 @@ package gay.pizza.pork.parser
import gay.pizza.pork.ast.gen.Node
import gay.pizza.pork.ast.gen.NodeType
import gay.pizza.pork.ast.gen.data
import gay.pizza.pork.tokenizer.Token
open class ParserNodeAttribution : NodeAttribution {
private val stack = mutableListOf<MutableList<Token>>()

View File

@ -4,6 +4,7 @@ includeBuild("buildext")
include(
":common",
":tokenizer",
":ast",
":parser",
":frontend",

View File

@ -1,8 +1,8 @@
package gay.pizza.pork.stdlib
import gay.pizza.pork.frontend.ContentSource
import gay.pizza.pork.parser.CharSource
import gay.pizza.pork.parser.StringCharSource
import gay.pizza.pork.tokenizer.CharSource
import gay.pizza.pork.tokenizer.StringCharSource
object PorkStdlib : ContentSource {
private val stdlibClass = PorkStdlib::class.java

View File

@ -4,7 +4,7 @@ import com.intellij.lang.BracePair
import com.intellij.lang.PairedBraceMatcher
import com.intellij.psi.PsiFile
import com.intellij.psi.tree.IElementType
import gay.pizza.pork.parser.TokenType
import gay.pizza.pork.tokenizer.TokenType
class PorkBraceMatcher : PairedBraceMatcher {
override fun getPairs(): Array<BracePair> = arrayOf(

View File

@ -3,7 +3,7 @@ package gay.pizza.pork.idea
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet
import gay.pizza.pork.ast.gen.NodeType
import gay.pizza.pork.parser.TokenType
import gay.pizza.pork.tokenizer.TokenType
object PorkElementTypes {
private val tokenTypeToElementType = mutableMapOf<TokenType, IElementType>()

View File

@ -3,10 +3,10 @@ package gay.pizza.pork.idea
import com.intellij.lexer.LexerBase
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.psi.tree.IElementType
import gay.pizza.pork.parser.BadCharacterError
import gay.pizza.pork.parser.StringCharSource
import gay.pizza.pork.parser.Tokenizer
import gay.pizza.pork.parser.UnterminatedTokenError
import gay.pizza.pork.tokenizer.BadCharacterError
import gay.pizza.pork.tokenizer.StringCharSource
import gay.pizza.pork.tokenizer.Tokenizer
import gay.pizza.pork.tokenizer.UnterminatedTokenError
import com.intellij.psi.TokenType as PsiTokenType
class PorkLexer : LexerBase() {

View File

@ -11,7 +11,7 @@ import com.intellij.psi.util.elementsAtOffsetUp
import gay.pizza.pork.idea.psi.gen.ArgumentSpecElement
import gay.pizza.pork.idea.psi.gen.FunctionCallElement
import gay.pizza.pork.idea.psi.gen.FunctionDefinitionElement
import gay.pizza.pork.parser.TokenType
import gay.pizza.pork.tokenizer.TokenType
@Suppress("UnstableApiUsage")
class PorkParameterInfoHandler : ParameterInfoHandler<FunctionCallElement, FunctionDefinitionElement> {

View File

@ -11,7 +11,7 @@ import com.intellij.psi.PsiFile
import com.intellij.psi.tree.IFileElementType
import com.intellij.psi.tree.TokenSet
import gay.pizza.pork.idea.psi.gen.PorkElementFactory
import gay.pizza.pork.parser.TokenType
import gay.pizza.pork.tokenizer.TokenType
class PorkParserDefinition : ParserDefinition {
val fileElementType = IFileElementType(PorkLanguage)

View File

@ -6,8 +6,8 @@ import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.fileTypes.SyntaxHighlighter
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase
import com.intellij.psi.tree.IElementType
import gay.pizza.pork.parser.TokenFamily
import gay.pizza.pork.parser.TokenType
import gay.pizza.pork.tokenizer.TokenFamily
import gay.pizza.pork.tokenizer.TokenType
object PorkSyntaxHighlighter : SyntaxHighlighter {
override fun getHighlightingLexer(): Lexer {

View File

@ -1,7 +1,10 @@
package gay.pizza.pork.idea
import com.intellij.lang.PsiBuilder
import gay.pizza.pork.parser.*
import gay.pizza.pork.parser.ParserAwareTokenSource
import gay.pizza.pork.tokenizer.SourceIndex
import gay.pizza.pork.tokenizer.Token
import gay.pizza.pork.tokenizer.TokenType
import com.intellij.psi.TokenType as PsiTokenType
@Suppress("UnstableApiUsage")

View File

@ -0,0 +1,7 @@
plugins {
id("gay.pizza.pork.module")
}
dependencies {
implementation(project(":common"))
}

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
open class AnsiHighlightScheme : HighlightScheme {
override fun highlight(token: Token): Highlight {

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.tokenizer
class BadCharacterError(char: Char, sourceIndex: SourceIndex, state: TokenizerState) : TokenizeError(
"Failed to produce token for '${char}' at $sourceIndex in state $state"
)

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
interface CharConsumer {
fun consume(type: TokenType, tokenizer: Tokenizer): String?

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
fun interface CharMatcher {
fun valid(char: Char, index: Int): Boolean

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
interface CharSource : PeekableSource<Char> {
fun peek(index: Int): Char

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
fun CharSource.readToString(): String = buildString {
while (peek() != CharSource.EndOfFile) {

View File

@ -1,6 +1,6 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
class ExpectedTokenError(got: Token, sourceIndex: SourceIndex, vararg expectedTypes: TokenType) : ParseError(
class ExpectedTokenError(got: Token, sourceIndex: SourceIndex, vararg expectedTypes: TokenType) : TokenizeError(
message(got, sourceIndex, expectedTypes)
) {
companion object {

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
class Highlight(val token: Token, val text: String? = null) {
override fun toString(): String = text ?: token.text

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
interface HighlightScheme {
fun highlight(token: Token): Highlight

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
class Highlighter(val scheme: HighlightScheme) {
fun highlight(stream: TokenStream): List<Highlight> =

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
@Suppress("CanBeParameter")
class MatchedCharConsumer(

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
interface PeekableSource<T> {
val currentIndex: Int

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
data class SourceIndex(val index: Int, val line: Int, val column: Int, val locationReliable: Boolean = true) {
companion object {

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
class SourceIndexCharSource(val delegate: CharSource) : CharSource by delegate {
private var currentLineIndex = 1

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
object StringCharConsumer : CharConsumer {
override fun consume(type: TokenType, tokenizer: Tokenizer): String {

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
class StringCharSource(
val input: CharSequence,

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
class Token(val type: TokenType, val sourceIndex: SourceIndex, val text: String) {
override fun toString(): String =

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
enum class TokenFamily : TokenTypeProperty {
OperatorFamily,

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
interface TokenSource : PeekableSource<Token> {
fun peekTypeAhead(ahead: Int): TokenType

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
class TokenStream(val tokens: List<Token>) {
override fun toString(): String = tokens.toString()

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
class TokenStreamSource(val stream: TokenStream) : TokenSource {
private var index = 0

View File

@ -1,10 +1,10 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
import gay.pizza.pork.parser.CharMatcher.*
import gay.pizza.pork.parser.MatchedCharConsumer.Options.AllowEofTermination
import gay.pizza.pork.parser.TokenTypeProperty.*
import gay.pizza.pork.parser.TokenFamily.*
import gay.pizza.pork.parser.TokenTypeProperty.AnyOf
import gay.pizza.pork.tokenizer.CharMatcher.*
import gay.pizza.pork.tokenizer.MatchedCharConsumer.Options.AllowEofTermination
import gay.pizza.pork.tokenizer.TokenTypeProperty.*
import gay.pizza.pork.tokenizer.TokenFamily.*
import gay.pizza.pork.tokenizer.TokenTypeProperty.AnyOf
enum class TokenType(vararg val properties: TokenTypeProperty) {
NumberLiteral(NumericLiteralFamily, CharMatch(CharMatcher.AnyOf(

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
interface TokenTypeProperty {
class SingleChar(val char: Char) : TokenTypeProperty

View File

@ -0,0 +1,3 @@
package gay.pizza.pork.tokenizer
abstract class TokenizeError(message: String) : RuntimeException(message)

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
class Tokenizer(source: CharSource) : TokenSource {
internal val source = SourceIndexCharSource(source)

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
enum class TokenizerState(vararg val transitions: Transition) {
Normal(Transition({ TokenType.Quote }) { StringLiteralStart }),

View File

@ -1,5 +1,5 @@
package gay.pizza.pork.parser
package gay.pizza.pork.tokenizer
class UnterminatedTokenError(what: String, sourceIndex: SourceIndex) : ParseError(
class UnterminatedTokenError(what: String, sourceIndex: SourceIndex) : TokenizeError(
"Unterminated $what at $sourceIndex"
)

View File

@ -7,7 +7,7 @@ import gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.dough.fs.createDirectories
import gay.pizza.dough.fs.exists
import gay.pizza.dough.fs.writeString
import gay.pizza.pork.parser.readToString
import gay.pizza.pork.tokenizer.readToString
import gay.pizza.pork.stdlib.PorkStdlib
class CopyStdlibCommand : CliktCommand(help = "Copy Stdlib", name = "copy-stdlib") {

View File

@ -4,7 +4,7 @@ import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.pork.minimal.FileTool
import gay.pizza.pork.parser.AnsiHighlightScheme
import gay.pizza.pork.tokenizer.AnsiHighlightScheme
class HighlightCommand : CliktCommand(help = "Syntax Highlighter", name = "highlight") {
val path by argument("file")

View File

@ -4,7 +4,7 @@ import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.pork.minimal.FileTool
import gay.pizza.pork.parser.TokenType
import gay.pizza.pork.tokenizer.TokenType
class TokenizeCommand : CliktCommand(help = "Tokenize Compilation Unit", name = "tokenize") {
val path by argument("file")