mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 21:00:56 +00:00
parser: rewrite to be able to parse any node type on demand
This commit is contained in:
parent
b42ca92d9f
commit
2307fdc0ed
66
ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt
Normal file
66
ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// GENERATED CODE FROM PORK AST CODEGEN
|
||||||
|
package gay.pizza.pork.ast
|
||||||
|
|
||||||
|
interface NodeParser {
|
||||||
|
fun parseBlock(): Block
|
||||||
|
|
||||||
|
fun parseExpression(): Expression
|
||||||
|
|
||||||
|
fun parseBooleanLiteral(): BooleanLiteral
|
||||||
|
|
||||||
|
fun parseBreak(): Break
|
||||||
|
|
||||||
|
fun parseCompilationUnit(): CompilationUnit
|
||||||
|
|
||||||
|
fun parseContinue(): Continue
|
||||||
|
|
||||||
|
fun parseDeclaration(): Declaration
|
||||||
|
|
||||||
|
fun parseDefinition(): Definition
|
||||||
|
|
||||||
|
fun parseDoubleLiteral(): DoubleLiteral
|
||||||
|
|
||||||
|
fun parseForIn(): ForIn
|
||||||
|
|
||||||
|
fun parseFunctionCall(): FunctionCall
|
||||||
|
|
||||||
|
fun parseFunctionDefinition(): FunctionDefinition
|
||||||
|
|
||||||
|
fun parseIf(): If
|
||||||
|
|
||||||
|
fun parseImportDeclaration(): ImportDeclaration
|
||||||
|
|
||||||
|
fun parseInfixOperation(): InfixOperation
|
||||||
|
|
||||||
|
fun parseIntegerLiteral(): IntegerLiteral
|
||||||
|
|
||||||
|
fun parseLetAssignment(): LetAssignment
|
||||||
|
|
||||||
|
fun parseLetDefinition(): LetDefinition
|
||||||
|
|
||||||
|
fun parseListLiteral(): ListLiteral
|
||||||
|
|
||||||
|
fun parseLongLiteral(): LongLiteral
|
||||||
|
|
||||||
|
fun parseNative(): Native
|
||||||
|
|
||||||
|
fun parseNoneLiteral(): NoneLiteral
|
||||||
|
|
||||||
|
fun parseParentheses(): Parentheses
|
||||||
|
|
||||||
|
fun parsePrefixOperation(): PrefixOperation
|
||||||
|
|
||||||
|
fun parseSetAssignment(): SetAssignment
|
||||||
|
|
||||||
|
fun parseStringLiteral(): StringLiteral
|
||||||
|
|
||||||
|
fun parseSuffixOperation(): SuffixOperation
|
||||||
|
|
||||||
|
fun parseSymbol(): Symbol
|
||||||
|
|
||||||
|
fun parseSymbolReference(): SymbolReference
|
||||||
|
|
||||||
|
fun parseVarAssignment(): VarAssignment
|
||||||
|
|
||||||
|
fun parseWhile(): While
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
// GENERATED CODE FROM PORK AST CODEGEN
|
||||||
|
package gay.pizza.pork.ast
|
||||||
|
|
||||||
|
fun NodeParser.parse(type: NodeType): Node =
|
||||||
|
when (type) {
|
||||||
|
NodeType.Expression -> parseExpression()
|
||||||
|
NodeType.Symbol -> parseSymbol()
|
||||||
|
NodeType.Declaration -> parseDeclaration()
|
||||||
|
NodeType.Definition -> parseDefinition()
|
||||||
|
NodeType.Block -> parseBlock()
|
||||||
|
NodeType.CompilationUnit -> parseCompilationUnit()
|
||||||
|
NodeType.LetAssignment -> parseLetAssignment()
|
||||||
|
NodeType.VarAssignment -> parseVarAssignment()
|
||||||
|
NodeType.SetAssignment -> parseSetAssignment()
|
||||||
|
NodeType.InfixOperation -> parseInfixOperation()
|
||||||
|
NodeType.BooleanLiteral -> parseBooleanLiteral()
|
||||||
|
NodeType.FunctionCall -> parseFunctionCall()
|
||||||
|
NodeType.FunctionDefinition -> parseFunctionDefinition()
|
||||||
|
NodeType.LetDefinition -> parseLetDefinition()
|
||||||
|
NodeType.If -> parseIf()
|
||||||
|
NodeType.ImportDeclaration -> parseImportDeclaration()
|
||||||
|
NodeType.IntegerLiteral -> parseIntegerLiteral()
|
||||||
|
NodeType.LongLiteral -> parseLongLiteral()
|
||||||
|
NodeType.DoubleLiteral -> parseDoubleLiteral()
|
||||||
|
NodeType.ListLiteral -> parseListLiteral()
|
||||||
|
NodeType.Parentheses -> parseParentheses()
|
||||||
|
NodeType.PrefixOperation -> parsePrefixOperation()
|
||||||
|
NodeType.SuffixOperation -> parseSuffixOperation()
|
||||||
|
NodeType.StringLiteral -> parseStringLiteral()
|
||||||
|
NodeType.SymbolReference -> parseSymbolReference()
|
||||||
|
NodeType.While -> parseWhile()
|
||||||
|
NodeType.ForIn -> parseForIn()
|
||||||
|
NodeType.Break -> parseBreak()
|
||||||
|
NodeType.Continue -> parseContinue()
|
||||||
|
NodeType.NoneLiteral -> parseNoneLiteral()
|
||||||
|
NodeType.Native -> parseNative()
|
||||||
|
else -> throw RuntimeException("Unable to automatically parse type: ${type.name}")
|
||||||
|
}
|
@ -20,8 +20,11 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
|||||||
}
|
}
|
||||||
writeNodeExtensions()
|
writeNodeExtensions()
|
||||||
writeNodeType()
|
writeNodeType()
|
||||||
writeNodeVisitors()
|
writeNodeVisitor()
|
||||||
writeNodeCoalescer()
|
writeNodeCoalescer()
|
||||||
|
writeNodeVisitorExtensions()
|
||||||
|
writeNodeParser()
|
||||||
|
writeNodeParserExtensions()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeNodeType() {
|
private fun writeNodeType() {
|
||||||
@ -46,7 +49,7 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
|||||||
write("NodeType.kt", KotlinWriter(enumClass))
|
write("NodeType.kt", KotlinWriter(enumClass))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeNodeVisitors() {
|
private fun writeNodeVisitor() {
|
||||||
val nodeVisitorInterface = KotlinClass(
|
val nodeVisitorInterface = KotlinClass(
|
||||||
pkg,
|
pkg,
|
||||||
"NodeVisitor",
|
"NodeVisitor",
|
||||||
@ -72,7 +75,9 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
|||||||
nodeVisitorInterface.functions.add(nodeVisitFunction)
|
nodeVisitorInterface.functions.add(nodeVisitFunction)
|
||||||
}
|
}
|
||||||
write("NodeVisitor.kt", KotlinWriter(nodeVisitorInterface))
|
write("NodeVisitor.kt", KotlinWriter(nodeVisitorInterface))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeNodeVisitorExtensions() {
|
||||||
val visitorExtensionSet = KotlinFunctionSet(pkg)
|
val visitorExtensionSet = KotlinFunctionSet(pkg)
|
||||||
val visitAnyFunction = KotlinFunction(
|
val visitAnyFunction = KotlinFunction(
|
||||||
"visit",
|
"visit",
|
||||||
@ -128,6 +133,63 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
|||||||
write("NodeVisitorExtensions.kt", KotlinWriter(visitorExtensionSet))
|
write("NodeVisitorExtensions.kt", KotlinWriter(visitorExtensionSet))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun writeNodeParserExtensions() {
|
||||||
|
val parserExtensionSet = KotlinFunctionSet(pkg)
|
||||||
|
val parseAnyFunction = KotlinFunction(
|
||||||
|
"parse",
|
||||||
|
extensionOf = "NodeParser",
|
||||||
|
returnType = "Node",
|
||||||
|
parameters = mutableListOf(
|
||||||
|
KotlinParameter("type", type = "NodeType")
|
||||||
|
),
|
||||||
|
isImmediateExpression = true
|
||||||
|
)
|
||||||
|
|
||||||
|
if (enableVisitAnyInline) {
|
||||||
|
parseAnyFunction.inline = true
|
||||||
|
parseAnyFunction.annotations.add("""@Suppress("NOTHING_TO_INLINE")""")
|
||||||
|
}
|
||||||
|
|
||||||
|
parseAnyFunction.body.add("when (type) {")
|
||||||
|
for (type in world.typeRegistry.types.filter {
|
||||||
|
world.typeRegistry.roleOfType(it) in arrayOf(
|
||||||
|
AstTypeRole.AstNode,
|
||||||
|
AstTypeRole.HierarchyNode
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
parseAnyFunction.body.add(" NodeType.${type.name} -> parse${type.name}()")
|
||||||
|
}
|
||||||
|
parseAnyFunction.body.add(" else -> throw RuntimeException(\"Unable to automatically parse type: \${type.name}\")")
|
||||||
|
parseAnyFunction.body.add("}")
|
||||||
|
parserExtensionSet.functions.add(parseAnyFunction)
|
||||||
|
write("NodeParserExtensions.kt", KotlinWriter(parserExtensionSet))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeNodeParser() {
|
||||||
|
val nodeParserInterface = KotlinClass(
|
||||||
|
pkg,
|
||||||
|
"NodeParser",
|
||||||
|
isInterface = true
|
||||||
|
)
|
||||||
|
|
||||||
|
for (type in world.typesInDependencyOrder()) {
|
||||||
|
val role = world.typeRegistry.roleOfType(type)
|
||||||
|
|
||||||
|
if (role !in arrayOf(AstTypeRole.AstNode, AstTypeRole.HierarchyNode)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val nodeParseFunction = KotlinFunction(
|
||||||
|
"parse${type.name}",
|
||||||
|
returnType = type.name,
|
||||||
|
parameters = mutableListOf(),
|
||||||
|
isInterfaceMethod = true
|
||||||
|
)
|
||||||
|
nodeParserInterface.functions.add(nodeParseFunction)
|
||||||
|
}
|
||||||
|
write("NodeParser.kt", KotlinWriter(nodeParserInterface))
|
||||||
|
}
|
||||||
|
|
||||||
private fun writeNodeCoalescer() {
|
private fun writeNodeCoalescer() {
|
||||||
val coalescerClass = KotlinClass(
|
val coalescerClass = KotlinClass(
|
||||||
pkg,
|
pkg,
|
||||||
@ -190,48 +252,7 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role == AstTypeRole.RootNode) {
|
if (role == AstTypeRole.RootNode) {
|
||||||
kotlinClassLike.imports.add("kotlinx.serialization.Transient")
|
addRootNodeDefinitions(kotlinClassLike)
|
||||||
val typeMember = KotlinMember(
|
|
||||||
"type",
|
|
||||||
"NodeType",
|
|
||||||
abstract = true
|
|
||||||
)
|
|
||||||
kotlinClassLike.members.add(typeMember)
|
|
||||||
val dataMember = KotlinMember(
|
|
||||||
"data",
|
|
||||||
"Any?",
|
|
||||||
value = "null",
|
|
||||||
mutable = true,
|
|
||||||
notInsideConstructor = true,
|
|
||||||
annotations = mutableListOf("@Transient")
|
|
||||||
)
|
|
||||||
kotlinClassLike.members.add(dataMember)
|
|
||||||
|
|
||||||
val abstractVisitChildrenFunction = KotlinFunction(
|
|
||||||
"visitChildren",
|
|
||||||
returnType = "List<T>",
|
|
||||||
open = true,
|
|
||||||
typeParameters = mutableListOf("T"),
|
|
||||||
parameters = mutableListOf(
|
|
||||||
KotlinParameter("visitor", "NodeVisitor<T>")
|
|
||||||
),
|
|
||||||
isImmediateExpression = true
|
|
||||||
)
|
|
||||||
abstractVisitChildrenFunction.body.add("emptyList()")
|
|
||||||
kotlinClassLike.functions.add(abstractVisitChildrenFunction)
|
|
||||||
|
|
||||||
val abstractVisitSelfFunction = KotlinFunction(
|
|
||||||
"visit",
|
|
||||||
returnType = "T",
|
|
||||||
open = true,
|
|
||||||
typeParameters = mutableListOf("T"),
|
|
||||||
parameters = mutableListOf(
|
|
||||||
KotlinParameter("visitor", "NodeVisitor<T>")
|
|
||||||
),
|
|
||||||
isImmediateExpression = true
|
|
||||||
)
|
|
||||||
abstractVisitSelfFunction.body.add("visitor.visit(this)")
|
|
||||||
kotlinClassLike.functions.add(abstractVisitSelfFunction)
|
|
||||||
} else if (role == AstTypeRole.AstNode) {
|
} else if (role == AstTypeRole.AstNode) {
|
||||||
val typeMember = KotlinMember(
|
val typeMember = KotlinMember(
|
||||||
"type",
|
"type",
|
||||||
@ -282,111 +303,7 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role == AstTypeRole.AstNode) {
|
if (role == AstTypeRole.AstNode) {
|
||||||
val visitChildrenFunction = KotlinFunction(
|
addAstNodeDefinitions(type, kotlinClassLike)
|
||||||
"visitChildren",
|
|
||||||
returnType = "List<T>",
|
|
||||||
typeParameters = mutableListOf("T"),
|
|
||||||
overridden = true,
|
|
||||||
parameters = mutableListOf(
|
|
||||||
KotlinParameter("visitor", "NodeVisitor<T>")
|
|
||||||
),
|
|
||||||
isImmediateExpression = true
|
|
||||||
)
|
|
||||||
val anyListMembers = type.values?.any {
|
|
||||||
it.typeRef.form == AstTypeRefForm.List
|
|
||||||
} ?: false
|
|
||||||
val elideVisitChildren: Boolean
|
|
||||||
if (anyListMembers) {
|
|
||||||
val visitParameters = (type.values?.mapNotNull {
|
|
||||||
if (it.typeRef.primitive != null) {
|
|
||||||
null
|
|
||||||
} else if (it.typeRef.type != null &&
|
|
||||||
!world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
|
||||||
null
|
|
||||||
} else if (it.typeRef.form == AstTypeRefForm.Single ||
|
|
||||||
it.typeRef.form == AstTypeRefForm.Nullable) {
|
|
||||||
"listOf(${it.name})"
|
|
||||||
} else {
|
|
||||||
it.name
|
|
||||||
}
|
|
||||||
} ?: emptyList()).joinToString(", ")
|
|
||||||
elideVisitChildren = visitParameters.isEmpty()
|
|
||||||
visitChildrenFunction.body.add("visitor.visitAll(${visitParameters})")
|
|
||||||
} else {
|
|
||||||
val visitParameters = (type.values?.mapNotNull {
|
|
||||||
if (it.typeRef.primitive != null) {
|
|
||||||
null
|
|
||||||
} else if (it.typeRef.type != null &&
|
|
||||||
!world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
it.name
|
|
||||||
}
|
|
||||||
} ?: emptyList()).joinToString(", ")
|
|
||||||
elideVisitChildren = visitParameters.isEmpty()
|
|
||||||
visitChildrenFunction.body.add("visitor.visitNodes(${visitParameters})")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!elideVisitChildren) {
|
|
||||||
kotlinClassLike.functions.add(visitChildrenFunction)
|
|
||||||
}
|
|
||||||
|
|
||||||
val visitSelfFunction = KotlinFunction(
|
|
||||||
"visit",
|
|
||||||
returnType = "T",
|
|
||||||
typeParameters = mutableListOf("T"),
|
|
||||||
overridden = true,
|
|
||||||
parameters = mutableListOf(
|
|
||||||
KotlinParameter("visitor", "NodeVisitor<T>")
|
|
||||||
),
|
|
||||||
isImmediateExpression = true
|
|
||||||
)
|
|
||||||
visitSelfFunction.body.add("visitor.visit${type.name}(this)")
|
|
||||||
kotlinClassLike.functions.add(visitSelfFunction)
|
|
||||||
|
|
||||||
val equalsAndHashCodeMembers = kotlinClassLike.members.map {
|
|
||||||
it.name
|
|
||||||
}.sortedBy { it == "type" }
|
|
||||||
val equalsFunction = KotlinFunction(
|
|
||||||
"equals",
|
|
||||||
returnType = "Boolean",
|
|
||||||
overridden = true
|
|
||||||
)
|
|
||||||
equalsFunction.parameters.add(KotlinParameter(
|
|
||||||
"other",
|
|
||||||
"Any?"
|
|
||||||
))
|
|
||||||
equalsFunction.body.add("if (other !is ${type.name}) return false")
|
|
||||||
var predicate = equalsAndHashCodeMembers.mapNotNull {
|
|
||||||
if (it == "type") null else "other.${it} == $it"
|
|
||||||
}.joinToString(" && ")
|
|
||||||
if (predicate.isEmpty()) {
|
|
||||||
predicate = "true"
|
|
||||||
}
|
|
||||||
equalsFunction.body.add("return $predicate")
|
|
||||||
kotlinClassLike.functions.add(equalsFunction)
|
|
||||||
|
|
||||||
val hashCodeFunction = KotlinFunction(
|
|
||||||
"hashCode",
|
|
||||||
returnType = "Int",
|
|
||||||
overridden = true
|
|
||||||
)
|
|
||||||
|
|
||||||
if (equalsAndHashCodeMembers.size == 1) {
|
|
||||||
val member = equalsAndHashCodeMembers.single()
|
|
||||||
hashCodeFunction.isImmediateExpression = true
|
|
||||||
hashCodeFunction.body.add("31 * ${member}.hashCode()")
|
|
||||||
} else {
|
|
||||||
for ((index, value) in equalsAndHashCodeMembers.withIndex()) {
|
|
||||||
if (index == 0) {
|
|
||||||
hashCodeFunction.body.add("var result = ${value}.hashCode()")
|
|
||||||
} else {
|
|
||||||
hashCodeFunction.body.add("result = 31 * result + ${value}.hashCode()")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hashCodeFunction.body.add("return result")
|
|
||||||
}
|
|
||||||
kotlinClassLike.functions.add(hashCodeFunction)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val serialName = kotlinClassLike.name[0].lowercase() +
|
val serialName = kotlinClassLike.name[0].lowercase() +
|
||||||
@ -399,6 +316,159 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
|||||||
write("${type.name}.kt", KotlinWriter(kotlinClassLike))
|
write("${type.name}.kt", KotlinWriter(kotlinClassLike))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun addRootNodeDefinitions(kotlinClassLike: KotlinClassLike) {
|
||||||
|
kotlinClassLike.imports.add("kotlinx.serialization.Transient")
|
||||||
|
val typeMember = KotlinMember(
|
||||||
|
"type",
|
||||||
|
"NodeType",
|
||||||
|
abstract = true
|
||||||
|
)
|
||||||
|
kotlinClassLike.members.add(typeMember)
|
||||||
|
val dataMember = KotlinMember(
|
||||||
|
"data",
|
||||||
|
"Any?",
|
||||||
|
value = "null",
|
||||||
|
mutable = true,
|
||||||
|
notInsideConstructor = true,
|
||||||
|
annotations = mutableListOf("@Transient")
|
||||||
|
)
|
||||||
|
kotlinClassLike.members.add(dataMember)
|
||||||
|
|
||||||
|
val abstractVisitChildrenFunction = KotlinFunction(
|
||||||
|
"visitChildren",
|
||||||
|
returnType = "List<T>",
|
||||||
|
open = true,
|
||||||
|
typeParameters = mutableListOf("T"),
|
||||||
|
parameters = mutableListOf(
|
||||||
|
KotlinParameter("visitor", "NodeVisitor<T>")
|
||||||
|
),
|
||||||
|
isImmediateExpression = true
|
||||||
|
)
|
||||||
|
abstractVisitChildrenFunction.body.add("emptyList()")
|
||||||
|
kotlinClassLike.functions.add(abstractVisitChildrenFunction)
|
||||||
|
|
||||||
|
val abstractVisitSelfFunction = KotlinFunction(
|
||||||
|
"visit",
|
||||||
|
returnType = "T",
|
||||||
|
open = true,
|
||||||
|
typeParameters = mutableListOf("T"),
|
||||||
|
parameters = mutableListOf(
|
||||||
|
KotlinParameter("visitor", "NodeVisitor<T>")
|
||||||
|
),
|
||||||
|
isImmediateExpression = true
|
||||||
|
)
|
||||||
|
abstractVisitSelfFunction.body.add("visitor.visit(this)")
|
||||||
|
kotlinClassLike.functions.add(abstractVisitSelfFunction)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addAstNodeDefinitions(type: AstType, kotlinClassLike: KotlinClassLike) {
|
||||||
|
val visitChildrenFunction = KotlinFunction(
|
||||||
|
"visitChildren",
|
||||||
|
returnType = "List<T>",
|
||||||
|
typeParameters = mutableListOf("T"),
|
||||||
|
overridden = true,
|
||||||
|
parameters = mutableListOf(
|
||||||
|
KotlinParameter("visitor", "NodeVisitor<T>")
|
||||||
|
),
|
||||||
|
isImmediateExpression = true
|
||||||
|
)
|
||||||
|
val anyListMembers = type.values?.any {
|
||||||
|
it.typeRef.form == AstTypeRefForm.List
|
||||||
|
} ?: false
|
||||||
|
val elideVisitChildren: Boolean
|
||||||
|
if (anyListMembers) {
|
||||||
|
val visitParameters = (type.values?.mapNotNull {
|
||||||
|
if (it.typeRef.primitive != null) {
|
||||||
|
null
|
||||||
|
} else if (it.typeRef.type != null &&
|
||||||
|
!world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
||||||
|
null
|
||||||
|
} else if (it.typeRef.form == AstTypeRefForm.Single ||
|
||||||
|
it.typeRef.form == AstTypeRefForm.Nullable) {
|
||||||
|
"listOf(${it.name})"
|
||||||
|
} else {
|
||||||
|
it.name
|
||||||
|
}
|
||||||
|
} ?: emptyList()).joinToString(", ")
|
||||||
|
elideVisitChildren = visitParameters.isEmpty()
|
||||||
|
visitChildrenFunction.body.add("visitor.visitAll(${visitParameters})")
|
||||||
|
} else {
|
||||||
|
val visitParameters = (type.values?.mapNotNull {
|
||||||
|
if (it.typeRef.primitive != null) {
|
||||||
|
null
|
||||||
|
} else if (it.typeRef.type != null &&
|
||||||
|
!world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
it.name
|
||||||
|
}
|
||||||
|
} ?: emptyList()).joinToString(", ")
|
||||||
|
elideVisitChildren = visitParameters.isEmpty()
|
||||||
|
visitChildrenFunction.body.add("visitor.visitNodes(${visitParameters})")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!elideVisitChildren) {
|
||||||
|
kotlinClassLike.functions.add(visitChildrenFunction)
|
||||||
|
}
|
||||||
|
|
||||||
|
val visitSelfFunction = KotlinFunction(
|
||||||
|
"visit",
|
||||||
|
returnType = "T",
|
||||||
|
typeParameters = mutableListOf("T"),
|
||||||
|
overridden = true,
|
||||||
|
parameters = mutableListOf(
|
||||||
|
KotlinParameter("visitor", "NodeVisitor<T>")
|
||||||
|
),
|
||||||
|
isImmediateExpression = true
|
||||||
|
)
|
||||||
|
visitSelfFunction.body.add("visitor.visit${type.name}(this)")
|
||||||
|
kotlinClassLike.functions.add(visitSelfFunction)
|
||||||
|
|
||||||
|
val equalsAndHashCodeMembers = kotlinClassLike.members.map {
|
||||||
|
it.name
|
||||||
|
}.sortedBy { it == "type" }
|
||||||
|
val equalsFunction = KotlinFunction(
|
||||||
|
"equals",
|
||||||
|
returnType = "Boolean",
|
||||||
|
overridden = true
|
||||||
|
)
|
||||||
|
equalsFunction.parameters.add(KotlinParameter(
|
||||||
|
"other",
|
||||||
|
"Any?"
|
||||||
|
))
|
||||||
|
equalsFunction.body.add("if (other !is ${type.name}) return false")
|
||||||
|
var predicate = equalsAndHashCodeMembers.mapNotNull {
|
||||||
|
if (it == "type") null else "other.${it} == $it"
|
||||||
|
}.joinToString(" && ")
|
||||||
|
if (predicate.isEmpty()) {
|
||||||
|
predicate = "true"
|
||||||
|
}
|
||||||
|
equalsFunction.body.add("return $predicate")
|
||||||
|
kotlinClassLike.functions.add(equalsFunction)
|
||||||
|
|
||||||
|
val hashCodeFunction = KotlinFunction(
|
||||||
|
"hashCode",
|
||||||
|
returnType = "Int",
|
||||||
|
overridden = true
|
||||||
|
)
|
||||||
|
|
||||||
|
if (equalsAndHashCodeMembers.size == 1) {
|
||||||
|
val member = equalsAndHashCodeMembers.single()
|
||||||
|
hashCodeFunction.isImmediateExpression = true
|
||||||
|
hashCodeFunction.body.add("31 * ${member}.hashCode()")
|
||||||
|
} else {
|
||||||
|
for ((index, value) in equalsAndHashCodeMembers.withIndex()) {
|
||||||
|
if (index == 0) {
|
||||||
|
hashCodeFunction.body.add("var result = ${value}.hashCode()")
|
||||||
|
} else {
|
||||||
|
hashCodeFunction.body.add("result = 31 * result + ${value}.hashCode()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hashCodeFunction.body.add("return result")
|
||||||
|
}
|
||||||
|
kotlinClassLike.functions.add(hashCodeFunction)
|
||||||
|
}
|
||||||
|
|
||||||
private fun writeNodeExtensions() {
|
private fun writeNodeExtensions() {
|
||||||
val nodeExtensionSet = KotlinFunctionSet(pkg)
|
val nodeExtensionSet = KotlinFunctionSet(pkg)
|
||||||
val dataFunction = KotlinFunction(
|
val dataFunction = KotlinFunction(
|
||||||
|
@ -24,7 +24,7 @@ class World(val importSource: ImportSource) {
|
|||||||
val tokenizer = Tokenizer(charSource)
|
val tokenizer = Tokenizer(charSource)
|
||||||
val tokenStream = tokenizer.tokenize()
|
val tokenStream = tokenizer.tokenize()
|
||||||
val parser = Parser(TokenStreamSource(tokenStream), DiscardNodeAttribution)
|
val parser = Parser(TokenStreamSource(tokenStream), DiscardNodeAttribution)
|
||||||
val unit = parser.readCompilationUnit()
|
val unit = parser.parseCompilationUnit()
|
||||||
internalUnits[stableKey] = unit
|
internalUnits[stableKey] = unit
|
||||||
return unit
|
return unit
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ abstract class Tool {
|
|||||||
Tokenizer(createCharSource()).tokenize()
|
Tokenizer(createCharSource()).tokenize()
|
||||||
|
|
||||||
fun parse(attribution: NodeAttribution = DiscardNodeAttribution): CompilationUnit =
|
fun parse(attribution: NodeAttribution = DiscardNodeAttribution): CompilationUnit =
|
||||||
Parser(TokenStreamSource(tokenize()), attribution).readCompilationUnit()
|
Parser(TokenStreamSource(tokenize()), attribution).parseCompilationUnit()
|
||||||
|
|
||||||
fun highlight(scheme: HighlightScheme): List<Highlight> =
|
fun highlight(scheme: HighlightScheme): List<Highlight> =
|
||||||
Highlighter(scheme).highlight(tokenize())
|
Highlighter(scheme).highlight(tokenize())
|
||||||
|
@ -2,214 +2,53 @@ package gay.pizza.pork.parser
|
|||||||
|
|
||||||
import gay.pizza.pork.ast.*
|
import gay.pizza.pork.ast.*
|
||||||
|
|
||||||
@Suppress("SameParameterValue")
|
class Parser(source: TokenSource, attribution: NodeAttribution) :
|
||||||
class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
ParserBase(source, attribution) {
|
||||||
private val unsanitizedSource = source
|
private var storedSymbol: Symbol? = null
|
||||||
|
private var storedDefinitionModifiers: DefinitionModifiers? = null
|
||||||
|
|
||||||
private fun readNumberLiteral(): Expression = within {
|
override fun parseBlock(): Block = guarded {
|
||||||
expect(TokenType.NumberLiteral) {
|
expect(TokenType.LeftCurly)
|
||||||
if (it.text.contains(".")) {
|
val items = collect(TokenType.RightCurly) {
|
||||||
DoubleLiteral(it.text.toDouble())
|
parseExpression()
|
||||||
} else {
|
|
||||||
val integer = it.text.toIntOrNull()
|
|
||||||
if (integer != null) {
|
|
||||||
return@expect IntegerLiteral(integer)
|
|
||||||
}
|
|
||||||
val long = it.text.toLongOrNull()
|
|
||||||
if (long != null) {
|
|
||||||
return@expect LongLiteral(long)
|
|
||||||
}
|
|
||||||
throw ParseError("Illegal integer value")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
expect(TokenType.RightCurly)
|
||||||
|
Block(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readStringLiteral(): StringLiteral = within {
|
override fun parseExpression(): Expression {
|
||||||
expect(TokenType.StringLiteral) {
|
|
||||||
val content = StringEscape.unescape(StringEscape.unquote(it.text))
|
|
||||||
StringLiteral(content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readBooleanLiteral(): BooleanLiteral = within {
|
|
||||||
expect(TokenType.True, TokenType.False) {
|
|
||||||
BooleanLiteral(it.type == TokenType.True)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readListLiteral(): ListLiteral = within {
|
|
||||||
expect(TokenType.LeftBracket)
|
|
||||||
val items = collect(TokenType.RightBracket, TokenType.Comma) {
|
|
||||||
readExpression()
|
|
||||||
}
|
|
||||||
expect(TokenType.RightBracket)
|
|
||||||
ListLiteral(items)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readLetAssignment(): LetAssignment = within {
|
|
||||||
expect(TokenType.Let)
|
|
||||||
val symbol = readSymbolRaw()
|
|
||||||
expect(TokenType.Equals)
|
|
||||||
val value = readExpression()
|
|
||||||
LetAssignment(symbol, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readVarAssignment(): VarAssignment = within {
|
|
||||||
expect(TokenType.Var)
|
|
||||||
val symbol = readSymbolRaw()
|
|
||||||
expect(TokenType.Equals)
|
|
||||||
val value = readExpression()
|
|
||||||
VarAssignment(symbol, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readSymbolRaw(): Symbol = within {
|
|
||||||
expect(TokenType.Symbol) { Symbol(it.text) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readSymbolCases(): Expression = within {
|
|
||||||
val symbol = readSymbolRaw()
|
|
||||||
if (next(TokenType.LeftParentheses)) {
|
|
||||||
val arguments = collect(TokenType.RightParentheses, TokenType.Comma) {
|
|
||||||
readExpression()
|
|
||||||
}
|
|
||||||
expect(TokenType.RightParentheses)
|
|
||||||
FunctionCall(symbol, arguments)
|
|
||||||
} else {
|
|
||||||
val reference = SymbolReference(symbol)
|
|
||||||
if (peek(TokenType.PlusPlus, TokenType.MinusMinus)) {
|
|
||||||
expect(TokenType.PlusPlus, TokenType.MinusMinus) {
|
|
||||||
SuffixOperation(convertSuffixOperator(it), reference)
|
|
||||||
}
|
|
||||||
} else reference
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readParentheses(): Parentheses = within {
|
|
||||||
expect(TokenType.LeftParentheses)
|
|
||||||
val expression = readExpression()
|
|
||||||
expect(TokenType.RightParentheses)
|
|
||||||
Parentheses(expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readPrefixOperation(): PrefixOperation = within {
|
|
||||||
expect(TokenType.Not, TokenType.Plus, TokenType.Minus, TokenType.Tilde) {
|
|
||||||
PrefixOperation(convertPrefixOperator(it), readExpression())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readIf(): If = within {
|
|
||||||
expect(TokenType.If)
|
|
||||||
val condition = readExpression()
|
|
||||||
val thenBlock = readBlock()
|
|
||||||
var elseBlock: Block? = null
|
|
||||||
if (next(TokenType.Else)) {
|
|
||||||
elseBlock = readBlock()
|
|
||||||
}
|
|
||||||
If(condition, thenBlock, elseBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readWhile(): While = within {
|
|
||||||
expect(TokenType.While)
|
|
||||||
val condition = readExpression()
|
|
||||||
val block = readBlock()
|
|
||||||
While(condition, block)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readForIn(): ForIn = within {
|
|
||||||
expect(TokenType.For)
|
|
||||||
val symbol = readSymbolRaw()
|
|
||||||
expect(TokenType.In)
|
|
||||||
val value = readExpression()
|
|
||||||
val block = readBlock()
|
|
||||||
ForIn(symbol, value, block)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readNative(): Native = within {
|
|
||||||
expect(TokenType.Native)
|
|
||||||
val form = readSymbolRaw()
|
|
||||||
val definition = readStringLiteral()
|
|
||||||
Native(form, definition)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readExpression(): Expression {
|
|
||||||
val token = peek()
|
val token = peek()
|
||||||
val expression = when (token.type) {
|
val expression = when (token.type) {
|
||||||
TokenType.NumberLiteral -> {
|
TokenType.NumberLiteral -> parseNumberLiteral()
|
||||||
readNumberLiteral()
|
TokenType.StringLiteral -> parseStringLiteral()
|
||||||
}
|
TokenType.True, TokenType.False -> parseBooleanLiteral()
|
||||||
|
TokenType.LeftBracket -> parseListLiteral()
|
||||||
TokenType.StringLiteral -> {
|
TokenType.Let -> parseLetAssignment()
|
||||||
readStringLiteral()
|
TokenType.Var -> parseVarAssignment()
|
||||||
}
|
TokenType.Symbol -> parseSymbolCases()
|
||||||
|
TokenType.LeftParentheses -> parseParentheses()
|
||||||
TokenType.True, TokenType.False -> {
|
TokenType.Not, TokenType.Plus, TokenType.Minus, TokenType.Tilde ->
|
||||||
readBooleanLiteral()
|
parsePrefixOperation()
|
||||||
}
|
TokenType.If -> parseIf()
|
||||||
|
TokenType.While -> parseWhile()
|
||||||
TokenType.LeftBracket -> {
|
TokenType.For -> parseForIn()
|
||||||
readListLiteral()
|
TokenType.Break -> parseBreak()
|
||||||
}
|
TokenType.Continue -> parseContinue()
|
||||||
|
TokenType.None -> parseNoneLiteral()
|
||||||
TokenType.Let -> {
|
|
||||||
readLetAssignment()
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType.Var -> {
|
|
||||||
readVarAssignment()
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType.Symbol -> {
|
|
||||||
readSymbolCases()
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType.LeftParentheses -> {
|
|
||||||
readParentheses()
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType.Not, TokenType.Plus, TokenType.Minus, TokenType.Tilde -> {
|
|
||||||
readPrefixOperation()
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType.If -> {
|
|
||||||
readIf()
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType.While -> {
|
|
||||||
readWhile()
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType.For -> {
|
|
||||||
readForIn()
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType.Break -> {
|
|
||||||
expect(TokenType.Break)
|
|
||||||
Break()
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType.Continue -> {
|
|
||||||
expect(TokenType.Continue)
|
|
||||||
Continue()
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType.None -> {
|
|
||||||
expect(TokenType.None)
|
|
||||||
NoneLiteral()
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
throw ParseError(
|
throw gay.pizza.pork.parser.ParseError(
|
||||||
"Failed to parse token: ${token.type} '${token.text}' as" +
|
"Failed to parse token: ${token.type} '${token.text}' as" +
|
||||||
" expression (index ${unsanitizedSource.currentIndex})"
|
" expression (index ${source.currentIndex})"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expression is SymbolReference && peek(TokenType.Equals)) {
|
if (expression is SymbolReference && peek(TokenType.Equals)) {
|
||||||
return within {
|
return guarded {
|
||||||
attribution.adopt(expression)
|
attribution.adopt(expression)
|
||||||
expect(TokenType.Equals)
|
expect(TokenType.Equals)
|
||||||
val value = readExpression()
|
val value = parseExpression()
|
||||||
SetAssignment(expression.symbol, value)
|
SetAssignment(expression.symbol, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,31 +73,57 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
|||||||
TokenType.Or
|
TokenType.Or
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
within {
|
guarded {
|
||||||
val infixToken = next()
|
val infixToken = next()
|
||||||
val infixOperator = convertInfixOperator(infixToken)
|
val infixOperator = ParserHelpers.convertInfixOperator(infixToken)
|
||||||
InfixOperation(expression, infixOperator, readExpression())
|
InfixOperation(expression, infixOperator, parseExpression())
|
||||||
}
|
}
|
||||||
} else expression
|
} else expression
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readBlock(): Block = within {
|
override fun parseBooleanLiteral(): BooleanLiteral = guarded {
|
||||||
expect(TokenType.LeftCurly)
|
if (next(TokenType.True)) {
|
||||||
val items = collect(TokenType.RightCurly) {
|
BooleanLiteral(true)
|
||||||
readExpression()
|
} else if (next(TokenType.False)) {
|
||||||
|
BooleanLiteral(false)
|
||||||
|
} else {
|
||||||
|
throw ParseError("Expected ")
|
||||||
}
|
}
|
||||||
expect(TokenType.RightCurly)
|
|
||||||
Block(items)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readImportDeclaration(): ImportDeclaration = within {
|
override fun parseBreak(): Break = guarded {
|
||||||
expect(TokenType.Import)
|
expect(TokenType.Break)
|
||||||
val form = readSymbolRaw()
|
Break()
|
||||||
val components = oneAndContinuedBy(TokenType.Dot) { readSymbolRaw() }
|
|
||||||
ImportDeclaration(form, components)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readDefinitionModifiers(): DefinitionModifiers {
|
override fun parseCompilationUnit(): CompilationUnit = guarded {
|
||||||
|
val declarations = mutableListOf<Declaration>()
|
||||||
|
val definitions = mutableListOf<Definition>()
|
||||||
|
var declarationAccepted = true
|
||||||
|
|
||||||
|
while (!peek(TokenType.EndOfFile)) {
|
||||||
|
if (declarationAccepted) {
|
||||||
|
val definition = maybeParseDefinition()
|
||||||
|
if (definition != null) {
|
||||||
|
declarationAccepted = false
|
||||||
|
definitions.add(definition)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
declarations.add(parseDeclaration())
|
||||||
|
} else {
|
||||||
|
definitions.add(parseDefinition())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationUnit(declarations, definitions)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseContinue(): Continue = guarded {
|
||||||
|
expect(TokenType.Continue)
|
||||||
|
Continue()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseDefinitionModifiers(): DefinitionModifiers {
|
||||||
val modifiers = DefinitionModifiers(export = false)
|
val modifiers = DefinitionModifiers(export = false)
|
||||||
while (true) {
|
while (true) {
|
||||||
val token = peek()
|
val token = peek()
|
||||||
@ -273,12 +138,68 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
|||||||
return modifiers
|
return modifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readFunctionDeclaration(modifiers: DefinitionModifiers): FunctionDefinition = within {
|
fun maybeParseDefinition(): Definition? {
|
||||||
|
try {
|
||||||
|
storedDefinitionModifiers = parseDefinitionModifiers()
|
||||||
|
val token = peek()
|
||||||
|
return when (token.type) {
|
||||||
|
TokenType.Func -> parseFunctionDefinition()
|
||||||
|
TokenType.Let -> parseLetDefinition()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
storedDefinitionModifiers = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseDeclaration(): Declaration {
|
||||||
|
val token = peek()
|
||||||
|
return when (token.type) {
|
||||||
|
TokenType.Import -> parseImportDeclaration()
|
||||||
|
else -> throw gay.pizza.pork.parser.ParseError(
|
||||||
|
"Failed to parse token: ${token.type} '${token.text}' as" +
|
||||||
|
" declaration (index ${source.currentIndex})"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseDefinition(): Definition {
|
||||||
|
return maybeParseDefinition() ?: throw ParseError("Unable to parse definition")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseDoubleLiteral(): DoubleLiteral = guarded {
|
||||||
|
DoubleLiteral(expect(TokenType.NumberLiteral).text.toDouble())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseForIn(): ForIn = guarded {
|
||||||
|
expect(TokenType.For)
|
||||||
|
val symbol = parseSymbol()
|
||||||
|
expect(TokenType.In)
|
||||||
|
val value = parseExpression()
|
||||||
|
val block = parseBlock()
|
||||||
|
ForIn(symbol, value, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseFunctionCall(): FunctionCall = guarded {
|
||||||
|
parseFunctionCall(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseFunctionCall(target: Symbol?): FunctionCall = guarded {
|
||||||
|
val symbol = target ?: parseSymbol()
|
||||||
|
val arguments = collect(TokenType.RightParentheses, TokenType.Comma) {
|
||||||
|
parseExpression()
|
||||||
|
}
|
||||||
|
expect(TokenType.RightParentheses)
|
||||||
|
FunctionCall(symbol, arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseFunctionDefinition(): FunctionDefinition = guarded {
|
||||||
|
val modifiers = storedDefinitionModifiers ?: parseDefinitionModifiers()
|
||||||
expect(TokenType.Func)
|
expect(TokenType.Func)
|
||||||
val name = readSymbolRaw()
|
val name = parseSymbol()
|
||||||
expect(TokenType.LeftParentheses)
|
expect(TokenType.LeftParentheses)
|
||||||
val arguments = collect(TokenType.RightParentheses, TokenType.Comma) {
|
val arguments = collect(TokenType.RightParentheses, TokenType.Comma) {
|
||||||
val symbol = readSymbolRaw()
|
val symbol = parseSymbol()
|
||||||
var multiple = false
|
var multiple = false
|
||||||
if (next(TokenType.DotDotDot)) {
|
if (next(TokenType.DotDotDot)) {
|
||||||
multiple = true
|
multiple = true
|
||||||
@ -290,194 +211,167 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
|||||||
var native: Native? = null
|
var native: Native? = null
|
||||||
var block: Block? = null
|
var block: Block? = null
|
||||||
if (peek(TokenType.Native)) {
|
if (peek(TokenType.Native)) {
|
||||||
native = readNative()
|
native = parseNative()
|
||||||
} else {
|
} else {
|
||||||
block = readBlock()
|
block = parseBlock()
|
||||||
}
|
}
|
||||||
FunctionDefinition(modifiers, name, arguments, block, native)
|
FunctionDefinition(modifiers, name, arguments, block, native)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readLetDefinition(modifiers: DefinitionModifiers): LetDefinition = within {
|
override fun parseIf(): If = guarded {
|
||||||
|
expect(TokenType.If)
|
||||||
|
val condition = parseExpression()
|
||||||
|
val thenBlock = parseBlock()
|
||||||
|
var elseBlock: Block? = null
|
||||||
|
if (next(TokenType.Else)) {
|
||||||
|
elseBlock = parseBlock()
|
||||||
|
}
|
||||||
|
If(condition, thenBlock, elseBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseImportDeclaration(): ImportDeclaration = guarded {
|
||||||
|
expect(TokenType.Import)
|
||||||
|
val form = parseSymbol()
|
||||||
|
val components = oneAndContinuedBy(TokenType.Dot) {
|
||||||
|
parseSymbol()
|
||||||
|
}
|
||||||
|
ImportDeclaration(form, components)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseInfixOperation(): InfixOperation = guarded {
|
||||||
|
val infixToken = next()
|
||||||
|
val infixOperator = ParserHelpers.convertInfixOperator(infixToken)
|
||||||
|
InfixOperation(parseExpression(), infixOperator, parseExpression())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseNumberLiteral(): Expression = guarded {
|
||||||
|
val token = peek()
|
||||||
|
if (token.type != TokenType.NumberLiteral) {
|
||||||
|
expect(TokenType.NumberLiteral)
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
token.text.contains(".") -> parseDoubleLiteral()
|
||||||
|
token.text.toIntOrNull() != null -> parseIntegerLiteral()
|
||||||
|
token.text.toLongOrNull() != null -> parseLongLiteral()
|
||||||
|
else -> throw ParseError("Invalid numeric literal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseIntegerLiteral(): IntegerLiteral = guarded {
|
||||||
|
IntegerLiteral(expect(TokenType.NumberLiteral).text.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseLetAssignment(): LetAssignment = guarded {
|
||||||
expect(TokenType.Let)
|
expect(TokenType.Let)
|
||||||
val name = readSymbolRaw()
|
val symbol = parseSymbol()
|
||||||
expect(TokenType.Equals)
|
expect(TokenType.Equals)
|
||||||
val value = readExpression()
|
val value = parseExpression()
|
||||||
LetDefinition(modifiers, name, value)
|
LetAssignment(symbol, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun maybeReadDefinition(): Definition? {
|
override fun parseLetDefinition(): LetDefinition = guarded {
|
||||||
val modifiers = readDefinitionModifiers()
|
val definitionModifiers = storedDefinitionModifiers ?: parseDefinitionModifiers()
|
||||||
val token = peek()
|
expect(TokenType.Let)
|
||||||
return when (token.type) {
|
val name = parseSymbol()
|
||||||
TokenType.Func -> readFunctionDeclaration(modifiers)
|
expect(TokenType.Equals)
|
||||||
TokenType.Let -> readLetDefinition(modifiers)
|
val value = parseExpression()
|
||||||
else -> null
|
LetDefinition(definitionModifiers, name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseListLiteral(): ListLiteral = guarded {
|
||||||
|
expect(TokenType.LeftBracket)
|
||||||
|
val items = collect(TokenType.RightBracket, TokenType.Comma) {
|
||||||
|
parseExpression()
|
||||||
|
}
|
||||||
|
expect(TokenType.RightBracket)
|
||||||
|
ListLiteral(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseLongLiteral(): LongLiteral = guarded {
|
||||||
|
LongLiteral(expect(TokenType.NumberLiteral).text.toLong())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseNative(): Native = guarded {
|
||||||
|
expect(TokenType.Native)
|
||||||
|
val form = parseSymbol()
|
||||||
|
val definition = parseStringLiteral()
|
||||||
|
Native(form, definition)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseNoneLiteral(): NoneLiteral = guarded {
|
||||||
|
expect(TokenType.None)
|
||||||
|
NoneLiteral()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseParentheses(): Parentheses = guarded {
|
||||||
|
expect(TokenType.LeftParentheses)
|
||||||
|
val expression = parseExpression()
|
||||||
|
expect(TokenType.RightParentheses)
|
||||||
|
Parentheses(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parsePrefixOperation(): PrefixOperation = guarded {
|
||||||
|
expect(TokenType.Not, TokenType.Plus, TokenType.Minus, TokenType.Tilde) {
|
||||||
|
PrefixOperation(ParserHelpers.convertPrefixOperator(it), parseExpression())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readDefinition(): Definition {
|
override fun parseSetAssignment(): SetAssignment = guarded {
|
||||||
val definition = maybeReadDefinition()
|
val symbol = storedSymbol ?: parseSymbol()
|
||||||
if (definition != null) {
|
expect(TokenType.Equals)
|
||||||
return definition
|
val value = parseExpression()
|
||||||
}
|
SetAssignment(symbol, value)
|
||||||
val token = peek()
|
|
||||||
throw ParseError(
|
|
||||||
"Failed to parse token: ${token.type} '${token.text}' as" +
|
|
||||||
" definition (index ${unsanitizedSource.currentIndex})"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readDeclaration(): Declaration {
|
override fun parseStringLiteral(): StringLiteral = guarded {
|
||||||
val token = peek()
|
expect(TokenType.StringLiteral) {
|
||||||
return when (token.type) {
|
val content = StringEscape.unescape(StringEscape.unquote(it.text))
|
||||||
TokenType.Import -> readImportDeclaration()
|
StringLiteral(content)
|
||||||
else -> throw ParseError(
|
|
||||||
"Failed to parse token: ${token.type} '${token.text}' as" +
|
|
||||||
" declaration (index ${unsanitizedSource.currentIndex})"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertInfixOperator(token: Token): InfixOperator = when (token.type) {
|
override fun parseSuffixOperation(): SuffixOperation = guarded {
|
||||||
TokenType.Plus -> InfixOperator.Plus
|
val reference = parseSymbolReference()
|
||||||
TokenType.Minus -> InfixOperator.Minus
|
expect(TokenType.PlusPlus, TokenType.MinusMinus) {
|
||||||
TokenType.Multiply -> InfixOperator.Multiply
|
SuffixOperation(ParserHelpers.convertSuffixOperator(it), reference)
|
||||||
TokenType.Divide -> InfixOperator.Divide
|
}
|
||||||
TokenType.Ampersand -> InfixOperator.BinaryAnd
|
|
||||||
TokenType.Pipe -> InfixOperator.BinaryOr
|
|
||||||
TokenType.Caret -> InfixOperator.BinaryExclusiveOr
|
|
||||||
TokenType.Equality -> InfixOperator.Equals
|
|
||||||
TokenType.Inequality -> InfixOperator.NotEquals
|
|
||||||
TokenType.Mod -> InfixOperator.EuclideanModulo
|
|
||||||
TokenType.Rem -> InfixOperator.Remainder
|
|
||||||
TokenType.Lesser -> InfixOperator.Lesser
|
|
||||||
TokenType.Greater -> InfixOperator.Greater
|
|
||||||
TokenType.LesserEqual -> InfixOperator.LesserEqual
|
|
||||||
TokenType.GreaterEqual -> InfixOperator.GreaterEqual
|
|
||||||
TokenType.And -> InfixOperator.BooleanAnd
|
|
||||||
TokenType.Or -> InfixOperator.BooleanOr
|
|
||||||
else -> throw ParseError("Unknown Infix Operator")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertPrefixOperator(token: Token): PrefixOperator = when (token.type) {
|
private fun parseSymbolCases(): Expression = guarded {
|
||||||
TokenType.Not -> PrefixOperator.BooleanNot
|
val symbol = parseSymbol()
|
||||||
TokenType.Plus -> PrefixOperator.UnaryPlus
|
if (next(TokenType.LeftParentheses)) {
|
||||||
TokenType.Minus -> PrefixOperator.UnaryMinus
|
parseFunctionCall(symbol)
|
||||||
TokenType.Tilde -> PrefixOperator.BinaryNot
|
} else {
|
||||||
else -> throw ParseError("Unknown Prefix Operator")
|
val reference = SymbolReference(symbol)
|
||||||
}
|
if (peek(TokenType.PlusPlus, TokenType.MinusMinus)) {
|
||||||
|
expect(TokenType.PlusPlus, TokenType.MinusMinus) {
|
||||||
private fun convertSuffixOperator(token: Token): SuffixOperator = when (token.type) {
|
SuffixOperation(ParserHelpers.convertSuffixOperator(it), reference)
|
||||||
TokenType.PlusPlus -> SuffixOperator.Increment
|
|
||||||
TokenType.MinusMinus -> SuffixOperator.Decrement
|
|
||||||
else -> throw ParseError("Unknown Suffix Operator")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readCompilationUnit(): CompilationUnit = within {
|
|
||||||
val declarations = mutableListOf<Declaration>()
|
|
||||||
val definitions = mutableListOf<Definition>()
|
|
||||||
var declarationAccepted = true
|
|
||||||
|
|
||||||
while (!peek(TokenType.EndOfFile)) {
|
|
||||||
if (declarationAccepted) {
|
|
||||||
val definition = maybeReadDefinition()
|
|
||||||
if (definition != null) {
|
|
||||||
declarationAccepted = false
|
|
||||||
definitions.add(definition)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
declarations.add(readDeclaration())
|
} else reference
|
||||||
} else {
|
|
||||||
definitions.add(readDefinition())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilationUnit(declarations, definitions)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> collect(
|
|
||||||
peeking: TokenType,
|
|
||||||
consuming: TokenType? = null,
|
|
||||||
read: () -> T
|
|
||||||
): List<T> {
|
|
||||||
val items = mutableListOf<T>()
|
|
||||||
while (!peek(peeking)) {
|
|
||||||
val item = read()
|
|
||||||
if (consuming != null) {
|
|
||||||
if (!peek(peeking)) {
|
|
||||||
expect(consuming)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
items.add(item)
|
|
||||||
}
|
|
||||||
return items
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> oneAndContinuedBy(separator: TokenType, read: () -> T): List<T> {
|
|
||||||
val items = mutableListOf<T>()
|
|
||||||
items.add(read())
|
|
||||||
while (peek(separator)) {
|
|
||||||
expect(separator)
|
|
||||||
items.add(read())
|
|
||||||
}
|
|
||||||
return items
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun peek(vararg types: TokenType): Boolean {
|
|
||||||
val token = peek()
|
|
||||||
return types.contains(token.type)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun next(type: TokenType): Boolean {
|
|
||||||
return if (peek(type)) {
|
|
||||||
expect(type)
|
|
||||||
true
|
|
||||||
} else false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun expect(vararg types: TokenType): Token {
|
|
||||||
val token = next()
|
|
||||||
if (!types.contains(token.type)) {
|
|
||||||
throw ParseError(
|
|
||||||
"Expected one of ${types.joinToString(", ")}" +
|
|
||||||
" but got type ${token.type} '${token.text}'"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T: Node> expect(vararg types: TokenType, consume: (Token) -> T): T =
|
|
||||||
consume(expect(*types))
|
|
||||||
|
|
||||||
private fun next(): Token {
|
|
||||||
while (true) {
|
|
||||||
val token = unsanitizedSource.next()
|
|
||||||
attribution.push(token)
|
|
||||||
if (ignoredByParser(token.type)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return token
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun peek(): Token {
|
override fun parseSymbol(): Symbol = guarded {
|
||||||
while (true) {
|
expect(TokenType.Symbol) { Symbol(it.text) }
|
||||||
val token = unsanitizedSource.peek()
|
|
||||||
if (ignoredByParser(token.type)) {
|
|
||||||
attribution.push(token)
|
|
||||||
unsanitizedSource.next()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T: Node> within(block: () -> T): T = attribution.guarded(block)
|
override fun parseSymbolReference(): SymbolReference = guarded {
|
||||||
|
SymbolReference(parseSymbol())
|
||||||
|
}
|
||||||
|
|
||||||
private fun ignoredByParser(type: TokenType): Boolean = when (type) {
|
override fun parseVarAssignment(): VarAssignment = guarded {
|
||||||
TokenType.BlockComment -> true
|
expect(TokenType.Var)
|
||||||
TokenType.LineComment -> true
|
val symbol = parseSymbol()
|
||||||
TokenType.Whitespace -> true
|
expect(TokenType.Equals)
|
||||||
else -> false
|
val value = parseExpression()
|
||||||
|
VarAssignment(symbol, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseWhile(): While = guarded {
|
||||||
|
expect(TokenType.While)
|
||||||
|
val condition = parseExpression()
|
||||||
|
val block = parseBlock()
|
||||||
|
While(condition, block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
94
parser/src/main/kotlin/gay/pizza/pork/parser/ParserBase.kt
Normal file
94
parser/src/main/kotlin/gay/pizza/pork/parser/ParserBase.kt
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package gay.pizza.pork.parser
|
||||||
|
|
||||||
|
import gay.pizza.pork.ast.Node
|
||||||
|
import gay.pizza.pork.ast.NodeParser
|
||||||
|
|
||||||
|
abstract class ParserBase(val source: TokenSource, val attribution: NodeAttribution) : NodeParser {
|
||||||
|
open class ParseError(error: String) : RuntimeException(error)
|
||||||
|
|
||||||
|
class ExpectedTokenError(got: Token, vararg expectedTypes: TokenType) : ParseError(
|
||||||
|
"Expected one of ${expectedTypes.joinToString(", ")}" +
|
||||||
|
" but got type ${got.type} '${got.text}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
protected fun <T: Node> guarded(block: () -> T): T = attribution.guarded(block)
|
||||||
|
|
||||||
|
protected fun <T> collect(
|
||||||
|
peeking: TokenType,
|
||||||
|
consuming: TokenType? = null,
|
||||||
|
read: () -> T
|
||||||
|
): List<T> {
|
||||||
|
val items = mutableListOf<T>()
|
||||||
|
while (!peek(peeking)) {
|
||||||
|
val item = read()
|
||||||
|
if (consuming != null) {
|
||||||
|
if (!peek(peeking)) {
|
||||||
|
expect(consuming)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items.add(item)
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun <T> oneAndContinuedBy(separator: TokenType, read: () -> T): List<T> {
|
||||||
|
val items = mutableListOf<T>()
|
||||||
|
items.add(read())
|
||||||
|
while (peek(separator)) {
|
||||||
|
expect(separator)
|
||||||
|
items.add(read())
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun peek(vararg types: TokenType): Boolean {
|
||||||
|
val token = peek()
|
||||||
|
return types.contains(token.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun next(type: TokenType): Boolean {
|
||||||
|
return if (peek(type)) {
|
||||||
|
expect(type)
|
||||||
|
true
|
||||||
|
} else false
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun expect(vararg types: TokenType): Token {
|
||||||
|
val token = next()
|
||||||
|
if (!types.contains(token.type)) {
|
||||||
|
throw ExpectedTokenError(token, *types)
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun <T: Node> expect(vararg types: TokenType, consume: (Token) -> T): T =
|
||||||
|
consume(expect(*types))
|
||||||
|
|
||||||
|
protected fun next(): Token {
|
||||||
|
while (true) {
|
||||||
|
val token = source.next()
|
||||||
|
if (ignoredByParser(token.type)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun peek(): Token {
|
||||||
|
while (true) {
|
||||||
|
val token = source.peek()
|
||||||
|
if (ignoredByParser(token.type)) {
|
||||||
|
source.next()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ignoredByParser(type: TokenType): Boolean = when (type) {
|
||||||
|
TokenType.BlockComment -> true
|
||||||
|
TokenType.LineComment -> true
|
||||||
|
TokenType.Whitespace -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package gay.pizza.pork.parser
|
||||||
|
|
||||||
|
import gay.pizza.pork.ast.InfixOperator
|
||||||
|
import gay.pizza.pork.ast.PrefixOperator
|
||||||
|
import gay.pizza.pork.ast.SuffixOperator
|
||||||
|
|
||||||
|
internal object ParserHelpers {
|
||||||
|
fun convertInfixOperator(token: Token): InfixOperator = when (token.type) {
|
||||||
|
TokenType.Plus -> InfixOperator.Plus
|
||||||
|
TokenType.Minus -> InfixOperator.Minus
|
||||||
|
TokenType.Multiply -> InfixOperator.Multiply
|
||||||
|
TokenType.Divide -> InfixOperator.Divide
|
||||||
|
TokenType.Ampersand -> InfixOperator.BinaryAnd
|
||||||
|
TokenType.Pipe -> InfixOperator.BinaryOr
|
||||||
|
TokenType.Caret -> InfixOperator.BinaryExclusiveOr
|
||||||
|
TokenType.Equality -> InfixOperator.Equals
|
||||||
|
TokenType.Inequality -> InfixOperator.NotEquals
|
||||||
|
TokenType.Mod -> InfixOperator.EuclideanModulo
|
||||||
|
TokenType.Rem -> InfixOperator.Remainder
|
||||||
|
TokenType.Lesser -> InfixOperator.Lesser
|
||||||
|
TokenType.Greater -> InfixOperator.Greater
|
||||||
|
TokenType.LesserEqual -> InfixOperator.LesserEqual
|
||||||
|
TokenType.GreaterEqual -> InfixOperator.GreaterEqual
|
||||||
|
TokenType.And -> InfixOperator.BooleanAnd
|
||||||
|
TokenType.Or -> InfixOperator.BooleanOr
|
||||||
|
else -> throw ParserBase.ParseError("Unknown Infix Operator")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertPrefixOperator(token: Token): PrefixOperator = when (token.type) {
|
||||||
|
TokenType.Not -> PrefixOperator.BooleanNot
|
||||||
|
TokenType.Plus -> PrefixOperator.UnaryPlus
|
||||||
|
TokenType.Minus -> PrefixOperator.UnaryMinus
|
||||||
|
TokenType.Tilde -> PrefixOperator.BinaryNot
|
||||||
|
else -> throw ParserBase.ParseError("Unknown Prefix Operator")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertSuffixOperator(token: Token): SuffixOperator = when (token.type) {
|
||||||
|
TokenType.PlusPlus -> SuffixOperator.Increment
|
||||||
|
TokenType.MinusMinus -> SuffixOperator.Decrement
|
||||||
|
else -> throw ParserBase.ParseError("Unknown Suffix Operator")
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ class PorkParser : PsiParser {
|
|||||||
val source = PsiBuilderTokenSource(builder)
|
val source = PsiBuilderTokenSource(builder)
|
||||||
val parser = Parser(source, psiBuilderMarkAttribution)
|
val parser = Parser(source, psiBuilderMarkAttribution)
|
||||||
try {
|
try {
|
||||||
parser.within { parser.readCompilationUnit() }
|
parser.parseCompilationUnit()
|
||||||
} catch (_: ExitParser) {}
|
} catch (_: ExitParser) {}
|
||||||
return builder.treeBuilt
|
return builder.treeBuilt
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user