mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 05:10:55 +00:00
Implement frontend layer to simplify cli commands.
This commit is contained in:
parent
4ec53e1209
commit
c418694307
@ -10,5 +10,6 @@ sealed class Node {
|
|||||||
abstract val type: NodeType
|
abstract val type: NodeType
|
||||||
open fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> = emptyList()
|
open fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> = emptyList()
|
||||||
|
|
||||||
override fun toString(): String = let { node -> buildString { Printer(this).visit(node) } }
|
override fun toString(): String =
|
||||||
|
let { node -> buildString { Printer(this).visit(node) } }
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,9 @@ import com.github.ajalt.clikt.core.CliktCommand
|
|||||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||||
import com.github.ajalt.clikt.parameters.types.path
|
import com.github.ajalt.clikt.parameters.types.path
|
||||||
import gay.pizza.pork.ast.nodes.Node
|
import gay.pizza.pork.ast.nodes.Node
|
||||||
import gay.pizza.pork.parse.PorkParser
|
import gay.pizza.pork.frontend.FileFrontend
|
||||||
import gay.pizza.pork.parse.PorkTokenizer
|
|
||||||
import gay.pizza.pork.parse.StringCharSource
|
|
||||||
import gay.pizza.pork.parse.TokenStreamSource
|
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlin.io.path.readText
|
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
class AstCommand : CliktCommand(help = "Print AST", name = "ast") {
|
class AstCommand : CliktCommand(help = "Print AST", name = "ast") {
|
||||||
@ -23,9 +19,7 @@ class AstCommand : CliktCommand(help = "Print AST", name = "ast") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val content = path.readText()
|
val frontend = FileFrontend(path)
|
||||||
val tokenStream = PorkTokenizer(StringCharSource(content)).tokenize()
|
println(json.encodeToString(Node.serializer(), frontend.parse()))
|
||||||
val program = PorkParser(TokenStreamSource(tokenStream)).readProgram()
|
|
||||||
println(json.encodeToString(Node.serializer(), program))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,20 +4,13 @@ import com.github.ajalt.clikt.core.CliktCommand
|
|||||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||||
import com.github.ajalt.clikt.parameters.types.path
|
import com.github.ajalt.clikt.parameters.types.path
|
||||||
import gay.pizza.pork.compiler.KotlinCompiler
|
import gay.pizza.pork.compiler.KotlinCompiler
|
||||||
import gay.pizza.pork.parse.PorkParser
|
import gay.pizza.pork.frontend.FileFrontend
|
||||||
import gay.pizza.pork.parse.PorkTokenizer
|
|
||||||
import gay.pizza.pork.parse.StringCharSource
|
|
||||||
import gay.pizza.pork.parse.TokenStreamSource
|
|
||||||
import kotlin.io.path.readText
|
|
||||||
|
|
||||||
class GenerateKotlinCommand : CliktCommand(help = "Generate Kotlin Code", name = "generate-kotlin") {
|
class GenerateKotlinCommand : CliktCommand(help = "Generate Kotlin Code", name = "generate-kotlin") {
|
||||||
val path by argument("file").path(mustExist = true, canBeDir = false)
|
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val content = path.readText()
|
val frontend = FileFrontend(path)
|
||||||
val tokenStream = PorkTokenizer(StringCharSource(content)).tokenize()
|
println(frontend.visit(KotlinCompiler()))
|
||||||
val program = PorkParser(TokenStreamSource(tokenStream)).readProgram()
|
|
||||||
val compiler = KotlinCompiler()
|
|
||||||
println(compiler.visit(program))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,19 +3,14 @@ package gay.pizza.pork.cli
|
|||||||
import com.github.ajalt.clikt.core.CliktCommand
|
import com.github.ajalt.clikt.core.CliktCommand
|
||||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||||
import com.github.ajalt.clikt.parameters.types.path
|
import com.github.ajalt.clikt.parameters.types.path
|
||||||
|
import gay.pizza.pork.frontend.FileFrontend
|
||||||
import gay.pizza.pork.parse.AnsiHighlightScheme
|
import gay.pizza.pork.parse.AnsiHighlightScheme
|
||||||
import gay.pizza.pork.parse.Highlighter
|
|
||||||
import gay.pizza.pork.parse.PorkTokenizer
|
|
||||||
import gay.pizza.pork.parse.StringCharSource
|
|
||||||
import kotlin.io.path.readText
|
|
||||||
|
|
||||||
class HighlightCommand : CliktCommand(help = "Syntax Highlighter", name = "highlight") {
|
class HighlightCommand : CliktCommand(help = "Syntax Highlighter", name = "highlight") {
|
||||||
val path by argument("file").path(mustExist = true, canBeDir = false)
|
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val content = path.readText()
|
val frontend = FileFrontend(path)
|
||||||
val tokenStream = PorkTokenizer(StringCharSource(content)).tokenize()
|
print(frontend.highlight(AnsiHighlightScheme()).joinToString(""))
|
||||||
val highlighter = Highlighter(AnsiHighlightScheme())
|
|
||||||
print(highlighter.highlight(tokenStream).joinToString(""))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,20 +3,13 @@ package gay.pizza.pork.cli
|
|||||||
import com.github.ajalt.clikt.core.CliktCommand
|
import com.github.ajalt.clikt.core.CliktCommand
|
||||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||||
import com.github.ajalt.clikt.parameters.types.path
|
import com.github.ajalt.clikt.parameters.types.path
|
||||||
import gay.pizza.pork.ast.Printer
|
import gay.pizza.pork.frontend.FileFrontend
|
||||||
import gay.pizza.pork.parse.PorkParser
|
|
||||||
import gay.pizza.pork.parse.PorkTokenizer
|
|
||||||
import gay.pizza.pork.parse.StringCharSource
|
|
||||||
import gay.pizza.pork.parse.TokenStreamSource
|
|
||||||
import kotlin.io.path.readText
|
|
||||||
|
|
||||||
class ReprintCommand : CliktCommand(help = "Reprint Parsed Program", name = "reprint") {
|
class ReprintCommand : CliktCommand(help = "Reprint Parsed Program", name = "reprint") {
|
||||||
val path by argument("file").path(mustExist = true, canBeDir = false)
|
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val content = path.readText()
|
val frontend = FileFrontend(path)
|
||||||
val tokenStream = PorkTokenizer(StringCharSource(content)).tokenize()
|
print(frontend.reprint())
|
||||||
val program = PorkParser(TokenStreamSource(tokenStream)).readProgram()
|
|
||||||
print(buildString { Printer(this).visit(program) })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,29 +4,20 @@ import com.github.ajalt.clikt.core.CliktCommand
|
|||||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||||
import com.github.ajalt.clikt.parameters.types.path
|
import com.github.ajalt.clikt.parameters.types.path
|
||||||
import gay.pizza.pork.eval.CallableFunction
|
import gay.pizza.pork.eval.CallableFunction
|
||||||
import gay.pizza.pork.eval.PorkEvaluator
|
|
||||||
import gay.pizza.pork.eval.Scope
|
import gay.pizza.pork.eval.Scope
|
||||||
import gay.pizza.pork.parse.PorkParser
|
import gay.pizza.pork.frontend.FileFrontend
|
||||||
import gay.pizza.pork.parse.PorkTokenizer
|
|
||||||
import gay.pizza.pork.parse.StringCharSource
|
|
||||||
import gay.pizza.pork.parse.TokenStreamSource
|
|
||||||
import kotlin.io.path.readText
|
|
||||||
|
|
||||||
class RunCommand : CliktCommand(help = "Run Program", name = "run") {
|
class RunCommand : CliktCommand(help = "Run Program", name = "run") {
|
||||||
val path by argument("file").path(mustExist = true, canBeDir = false)
|
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val content = path.readText()
|
val frontend = FileFrontend(path)
|
||||||
val tokenStream = PorkTokenizer(StringCharSource(content)).tokenize()
|
|
||||||
val program = PorkParser(TokenStreamSource(tokenStream)).readProgram()
|
|
||||||
|
|
||||||
val scope = Scope()
|
val scope = Scope()
|
||||||
scope.define("println", CallableFunction { arguments ->
|
scope.define("println", CallableFunction { arguments ->
|
||||||
for (argument in arguments.values) {
|
for (argument in arguments.values) {
|
||||||
println(argument)
|
println(argument)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
val evaluator = PorkEvaluator(scope)
|
frontend.evaluate(scope)
|
||||||
evaluator.visit(program)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,16 +3,14 @@ package gay.pizza.pork.cli
|
|||||||
import com.github.ajalt.clikt.core.CliktCommand
|
import com.github.ajalt.clikt.core.CliktCommand
|
||||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||||
import com.github.ajalt.clikt.parameters.types.path
|
import com.github.ajalt.clikt.parameters.types.path
|
||||||
import gay.pizza.pork.parse.PorkTokenizer
|
import gay.pizza.pork.frontend.FileFrontend
|
||||||
import gay.pizza.pork.parse.StringCharSource
|
|
||||||
import kotlin.io.path.readText
|
|
||||||
|
|
||||||
class TokenizeCommand : CliktCommand(help = "Tokenize Program", name = "tokenize") {
|
class TokenizeCommand : CliktCommand(help = "Tokenize Program", name = "tokenize") {
|
||||||
val path by argument("file").path(mustExist = true, canBeDir = false)
|
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val content = path.readText()
|
val frontend = FileFrontend(path)
|
||||||
val tokenStream = PorkTokenizer(StringCharSource(content)).tokenize()
|
val tokenStream = frontend.tokenize()
|
||||||
for (token in tokenStream.tokens) {
|
for (token in tokenStream.tokens) {
|
||||||
println("${token.start} ${token.type.name} '${sanitize(token.text)}'")
|
println("${token.start} ${token.type.name} '${sanitize(token.text)}'")
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package gay.pizza.pork.eval
|
|||||||
import gay.pizza.pork.ast.*
|
import gay.pizza.pork.ast.*
|
||||||
import gay.pizza.pork.ast.nodes.*
|
import gay.pizza.pork.ast.nodes.*
|
||||||
|
|
||||||
class PorkEvaluator(root: Scope) : NodeVisitor<Any> {
|
class Evaluator(root: Scope) : NodeVisitor<Any> {
|
||||||
private var currentScope: Scope = root
|
private var currentScope: Scope = root
|
||||||
|
|
||||||
override fun visitDefine(node: Define): Any {
|
override fun visitDefine(node: Define): Any {
|
11
src/main/kotlin/gay/pizza/pork/frontend/FileFrontend.kt
Normal file
11
src/main/kotlin/gay/pizza/pork/frontend/FileFrontend.kt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package gay.pizza.pork.frontend
|
||||||
|
|
||||||
|
import gay.pizza.pork.ast.NodeVisitor
|
||||||
|
import gay.pizza.pork.parse.CharSource
|
||||||
|
import gay.pizza.pork.parse.StringCharSource
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.readText
|
||||||
|
|
||||||
|
class FileFrontend(val path: Path) : Frontend() {
|
||||||
|
override fun createCharSource(): CharSource = StringCharSource(path.readText())
|
||||||
|
}
|
28
src/main/kotlin/gay/pizza/pork/frontend/Frontend.kt
Normal file
28
src/main/kotlin/gay/pizza/pork/frontend/Frontend.kt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package gay.pizza.pork.frontend
|
||||||
|
|
||||||
|
import gay.pizza.pork.ast.NodeVisitor
|
||||||
|
import gay.pizza.pork.ast.Printer
|
||||||
|
import gay.pizza.pork.ast.nodes.Program
|
||||||
|
import gay.pizza.pork.eval.Evaluator
|
||||||
|
import gay.pizza.pork.eval.Scope
|
||||||
|
import gay.pizza.pork.parse.*
|
||||||
|
|
||||||
|
abstract class Frontend {
|
||||||
|
abstract fun createCharSource(): CharSource
|
||||||
|
|
||||||
|
fun tokenize(): TokenStream =
|
||||||
|
Tokenizer(createCharSource()).tokenize()
|
||||||
|
|
||||||
|
fun parse(): Program =
|
||||||
|
Parser(TokenStreamSource(tokenize())).readProgram()
|
||||||
|
|
||||||
|
fun highlight(scheme: HighlightScheme): List<Highlight> =
|
||||||
|
Highlighter(scheme).highlight(tokenize())
|
||||||
|
|
||||||
|
fun evaluate(scope: Scope = Scope()): Any =
|
||||||
|
visit(Evaluator(scope))
|
||||||
|
|
||||||
|
fun reprint(): String = buildString { visit(Printer(this)) }
|
||||||
|
|
||||||
|
fun <T> visit(visitor: NodeVisitor<T>): T = visitor.visit(parse())
|
||||||
|
}
|
@ -2,7 +2,7 @@ package gay.pizza.pork.parse
|
|||||||
|
|
||||||
import gay.pizza.pork.ast.nodes.*
|
import gay.pizza.pork.ast.nodes.*
|
||||||
|
|
||||||
class PorkParser(source: PeekableSource<Token>) {
|
class Parser(source: PeekableSource<Token>) {
|
||||||
private val unsanitizedSource = source
|
private val unsanitizedSource = source
|
||||||
|
|
||||||
private fun readIntLiteral(): IntLiteral {
|
private fun readIntLiteral(): IntLiteral {
|
@ -1,6 +1,6 @@
|
|||||||
package gay.pizza.pork.parse
|
package gay.pizza.pork.parse
|
||||||
|
|
||||||
class PorkTokenizer(val source: CharSource) {
|
class Tokenizer(val source: CharSource) {
|
||||||
private var tokenStart: Int = 0
|
private var tokenStart: Int = 0
|
||||||
|
|
||||||
private fun isSymbol(c: Char): Boolean =
|
private fun isSymbol(c: Char): Boolean =
|
Loading…
Reference in New Issue
Block a user