parser: rewrite to be able to parse any node type on demand

This commit is contained in:
Alex Zenla 2023-09-13 19:29:23 -07:00
parent b42ca92d9f
commit 2307fdc0ed
Signed by: alex
GPG Key ID: C0780728420EBFE5
9 changed files with 729 additions and 525 deletions

View 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
}

View File

@ -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}")
}

View File

@ -20,8 +20,11 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
}
writeNodeExtensions()
writeNodeType()
writeNodeVisitors()
writeNodeVisitor()
writeNodeCoalescer()
writeNodeVisitorExtensions()
writeNodeParser()
writeNodeParserExtensions()
}
private fun writeNodeType() {
@ -46,7 +49,7 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
write("NodeType.kt", KotlinWriter(enumClass))
}
private fun writeNodeVisitors() {
private fun writeNodeVisitor() {
val nodeVisitorInterface = KotlinClass(
pkg,
"NodeVisitor",
@ -72,7 +75,9 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
nodeVisitorInterface.functions.add(nodeVisitFunction)
}
write("NodeVisitor.kt", KotlinWriter(nodeVisitorInterface))
}
private fun writeNodeVisitorExtensions() {
val visitorExtensionSet = KotlinFunctionSet(pkg)
val visitAnyFunction = KotlinFunction(
"visit",
@ -128,6 +133,63 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
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() {
val coalescerClass = KotlinClass(
pkg,
@ -190,48 +252,7 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
}
if (role == AstTypeRole.RootNode) {
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)
addRootNodeDefinitions(kotlinClassLike)
} else if (role == AstTypeRole.AstNode) {
val typeMember = KotlinMember(
"type",
@ -282,111 +303,7 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
}
if (role == AstTypeRole.AstNode) {
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)
addAstNodeDefinitions(type, kotlinClassLike)
}
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))
}
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() {
val nodeExtensionSet = KotlinFunctionSet(pkg)
val dataFunction = KotlinFunction(

View File

@ -24,7 +24,7 @@ class World(val importSource: ImportSource) {
val tokenizer = Tokenizer(charSource)
val tokenStream = tokenizer.tokenize()
val parser = Parser(TokenStreamSource(tokenStream), DiscardNodeAttribution)
val unit = parser.readCompilationUnit()
val unit = parser.parseCompilationUnit()
internalUnits[stableKey] = unit
return unit
}

View File

@ -23,7 +23,7 @@ abstract class Tool {
Tokenizer(createCharSource()).tokenize()
fun parse(attribution: NodeAttribution = DiscardNodeAttribution): CompilationUnit =
Parser(TokenStreamSource(tokenize()), attribution).readCompilationUnit()
Parser(TokenStreamSource(tokenize()), attribution).parseCompilationUnit()
fun highlight(scheme: HighlightScheme): List<Highlight> =
Highlighter(scheme).highlight(tokenize())

View File

@ -2,214 +2,53 @@ package gay.pizza.pork.parser
import gay.pizza.pork.ast.*
@Suppress("SameParameterValue")
class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
private val unsanitizedSource = source
class Parser(source: TokenSource, attribution: NodeAttribution) :
ParserBase(source, attribution) {
private var storedSymbol: Symbol? = null
private var storedDefinitionModifiers: DefinitionModifiers? = null
private fun readNumberLiteral(): Expression = within {
expect(TokenType.NumberLiteral) {
if (it.text.contains(".")) {
DoubleLiteral(it.text.toDouble())
} 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")
}
override fun parseBlock(): Block = guarded {
expect(TokenType.LeftCurly)
val items = collect(TokenType.RightCurly) {
parseExpression()
}
expect(TokenType.RightCurly)
Block(items)
}
private fun readStringLiteral(): StringLiteral = within {
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 {
override fun parseExpression(): Expression {
val token = peek()
val expression = when (token.type) {
TokenType.NumberLiteral -> {
readNumberLiteral()
}
TokenType.StringLiteral -> {
readStringLiteral()
}
TokenType.True, TokenType.False -> {
readBooleanLiteral()
}
TokenType.LeftBracket -> {
readListLiteral()
}
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()
}
TokenType.NumberLiteral -> parseNumberLiteral()
TokenType.StringLiteral -> parseStringLiteral()
TokenType.True, TokenType.False -> parseBooleanLiteral()
TokenType.LeftBracket -> parseListLiteral()
TokenType.Let -> parseLetAssignment()
TokenType.Var -> parseVarAssignment()
TokenType.Symbol -> parseSymbolCases()
TokenType.LeftParentheses -> parseParentheses()
TokenType.Not, TokenType.Plus, TokenType.Minus, TokenType.Tilde ->
parsePrefixOperation()
TokenType.If -> parseIf()
TokenType.While -> parseWhile()
TokenType.For -> parseForIn()
TokenType.Break -> parseBreak()
TokenType.Continue -> parseContinue()
TokenType.None -> parseNoneLiteral()
else -> {
throw ParseError(
throw gay.pizza.pork.parser.ParseError(
"Failed to parse token: ${token.type} '${token.text}' as" +
" expression (index ${unsanitizedSource.currentIndex})"
" expression (index ${source.currentIndex})"
)
}
}
if (expression is SymbolReference && peek(TokenType.Equals)) {
return within {
return guarded {
attribution.adopt(expression)
expect(TokenType.Equals)
val value = readExpression()
val value = parseExpression()
SetAssignment(expression.symbol, value)
}
}
@ -234,31 +73,57 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
TokenType.Or
)
) {
within {
guarded {
val infixToken = next()
val infixOperator = convertInfixOperator(infixToken)
InfixOperation(expression, infixOperator, readExpression())
val infixOperator = ParserHelpers.convertInfixOperator(infixToken)
InfixOperation(expression, infixOperator, parseExpression())
}
} else expression
}
private fun readBlock(): Block = within {
expect(TokenType.LeftCurly)
val items = collect(TokenType.RightCurly) {
readExpression()
override fun parseBooleanLiteral(): BooleanLiteral = guarded {
if (next(TokenType.True)) {
BooleanLiteral(true)
} else if (next(TokenType.False)) {
BooleanLiteral(false)
} else {
throw ParseError("Expected ")
}
expect(TokenType.RightCurly)
Block(items)
}
private fun readImportDeclaration(): ImportDeclaration = within {
expect(TokenType.Import)
val form = readSymbolRaw()
val components = oneAndContinuedBy(TokenType.Dot) { readSymbolRaw() }
ImportDeclaration(form, components)
override fun parseBreak(): Break = guarded {
expect(TokenType.Break)
Break()
}
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)
while (true) {
val token = peek()
@ -273,12 +138,68 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
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)
val name = readSymbolRaw()
val name = parseSymbol()
expect(TokenType.LeftParentheses)
val arguments = collect(TokenType.RightParentheses, TokenType.Comma) {
val symbol = readSymbolRaw()
val symbol = parseSymbol()
var multiple = false
if (next(TokenType.DotDotDot)) {
multiple = true
@ -290,194 +211,167 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
var native: Native? = null
var block: Block? = null
if (peek(TokenType.Native)) {
native = readNative()
native = parseNative()
} else {
block = readBlock()
block = parseBlock()
}
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)
val name = readSymbolRaw()
val symbol = parseSymbol()
expect(TokenType.Equals)
val value = readExpression()
LetDefinition(modifiers, name, value)
val value = parseExpression()
LetAssignment(symbol, value)
}
private fun maybeReadDefinition(): Definition? {
val modifiers = readDefinitionModifiers()
val token = peek()
return when (token.type) {
TokenType.Func -> readFunctionDeclaration(modifiers)
TokenType.Let -> readLetDefinition(modifiers)
else -> null
override fun parseLetDefinition(): LetDefinition = guarded {
val definitionModifiers = storedDefinitionModifiers ?: parseDefinitionModifiers()
expect(TokenType.Let)
val name = parseSymbol()
expect(TokenType.Equals)
val value = parseExpression()
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 {
val definition = maybeReadDefinition()
if (definition != null) {
return definition
}
val token = peek()
throw ParseError(
"Failed to parse token: ${token.type} '${token.text}' as" +
" definition (index ${unsanitizedSource.currentIndex})"
)
override fun parseSetAssignment(): SetAssignment = guarded {
val symbol = storedSymbol ?: parseSymbol()
expect(TokenType.Equals)
val value = parseExpression()
SetAssignment(symbol, value)
}
fun readDeclaration(): Declaration {
val token = peek()
return when (token.type) {
TokenType.Import -> readImportDeclaration()
else -> throw ParseError(
"Failed to parse token: ${token.type} '${token.text}' as" +
" declaration (index ${unsanitizedSource.currentIndex})"
)
override fun parseStringLiteral(): StringLiteral = guarded {
expect(TokenType.StringLiteral) {
val content = StringEscape.unescape(StringEscape.unquote(it.text))
StringLiteral(content)
}
}
private 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 ParseError("Unknown Infix Operator")
override fun parseSuffixOperation(): SuffixOperation = guarded {
val reference = parseSymbolReference()
expect(TokenType.PlusPlus, TokenType.MinusMinus) {
SuffixOperation(ParserHelpers.convertSuffixOperator(it), reference)
}
}
private 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 ParseError("Unknown Prefix Operator")
}
private fun convertSuffixOperator(token: Token): SuffixOperator = when (token.type) {
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
private fun parseSymbolCases(): Expression = guarded {
val symbol = parseSymbol()
if (next(TokenType.LeftParentheses)) {
parseFunctionCall(symbol)
} else {
val reference = SymbolReference(symbol)
if (peek(TokenType.PlusPlus, TokenType.MinusMinus)) {
expect(TokenType.PlusPlus, TokenType.MinusMinus) {
SuffixOperation(ParserHelpers.convertSuffixOperator(it), reference)
}
declarations.add(readDeclaration())
} 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
} else reference
}
}
private fun peek(): Token {
while (true) {
val token = unsanitizedSource.peek()
if (ignoredByParser(token.type)) {
attribution.push(token)
unsanitizedSource.next()
continue
}
return token
}
override fun parseSymbol(): Symbol = guarded {
expect(TokenType.Symbol) { Symbol(it.text) }
}
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) {
TokenType.BlockComment -> true
TokenType.LineComment -> true
TokenType.Whitespace -> true
else -> false
override fun parseVarAssignment(): VarAssignment = guarded {
expect(TokenType.Var)
val symbol = parseSymbol()
expect(TokenType.Equals)
val value = parseExpression()
VarAssignment(symbol, value)
}
override fun parseWhile(): While = guarded {
expect(TokenType.While)
val condition = parseExpression()
val block = parseBlock()
While(condition, block)
}
}

View 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
}
}

View File

@ -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")
}
}

View File

@ -12,7 +12,7 @@ class PorkParser : PsiParser {
val source = PsiBuilderTokenSource(builder)
val parser = Parser(source, psiBuilderMarkAttribution)
try {
parser.within { parser.readCompilationUnit() }
parser.parseCompilationUnit()
} catch (_: ExitParser) {}
return builder.treeBuilt
}