diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt new file mode 100644 index 0000000..b6816a6 --- /dev/null +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt @@ -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 +} diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParserExtensions.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParserExtensions.kt new file mode 100644 index 0000000..e2e5eb1 --- /dev/null +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParserExtensions.kt @@ -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}") + } diff --git a/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstCodegen.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstCodegen.kt index 1aa4a18..74655fd 100644 --- a/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstCodegen.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstCodegen.kt @@ -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", - open = true, - typeParameters = mutableListOf("T"), - parameters = mutableListOf( - KotlinParameter("visitor", "NodeVisitor") - ), - 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") - ), - 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", - typeParameters = mutableListOf("T"), - overridden = true, - parameters = mutableListOf( - KotlinParameter("visitor", "NodeVisitor") - ), - 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") - ), - 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", + open = true, + typeParameters = mutableListOf("T"), + parameters = mutableListOf( + KotlinParameter("visitor", "NodeVisitor") + ), + 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") + ), + 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", + typeParameters = mutableListOf("T"), + overridden = true, + parameters = mutableListOf( + KotlinParameter("visitor", "NodeVisitor") + ), + 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") + ), + 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( diff --git a/frontend/src/main/kotlin/gay/pizza/pork/frontend/World.kt b/frontend/src/main/kotlin/gay/pizza/pork/frontend/World.kt index 27a2c06..32fe291 100644 --- a/frontend/src/main/kotlin/gay/pizza/pork/frontend/World.kt +++ b/frontend/src/main/kotlin/gay/pizza/pork/frontend/World.kt @@ -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 } diff --git a/minimal/src/main/kotlin/gay/pizza/pork/minimal/Tool.kt b/minimal/src/main/kotlin/gay/pizza/pork/minimal/Tool.kt index 88eeab0..3d52759 100644 --- a/minimal/src/main/kotlin/gay/pizza/pork/minimal/Tool.kt +++ b/minimal/src/main/kotlin/gay/pizza/pork/minimal/Tool.kt @@ -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 = Highlighter(scheme).highlight(tokenize()) diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt index b0e3870..3532d73 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt @@ -2,214 +2,53 @@ package gay.pizza.pork.parser import gay.pizza.pork.ast.* -@Suppress("SameParameterValue") -class Parser(source: PeekableSource, 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, 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() + val definitions = mutableListOf() + 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, 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, 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() - val definitions = mutableListOf() - 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 collect( - peeking: TokenType, - consuming: TokenType? = null, - read: () -> T - ): List { - val items = mutableListOf() - while (!peek(peeking)) { - val item = read() - if (consuming != null) { - if (!peek(peeking)) { - expect(consuming) - } - } - items.add(item) - } - return items - } - - private fun oneAndContinuedBy(separator: TokenType, read: () -> T): List { - val items = mutableListOf() - 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 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 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) } } diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/ParserBase.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/ParserBase.kt new file mode 100644 index 0000000..ee7cdee --- /dev/null +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/ParserBase.kt @@ -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 guarded(block: () -> T): T = attribution.guarded(block) + + protected fun collect( + peeking: TokenType, + consuming: TokenType? = null, + read: () -> T + ): List { + val items = mutableListOf() + while (!peek(peeking)) { + val item = read() + if (consuming != null) { + if (!peek(peeking)) { + expect(consuming) + } + } + items.add(item) + } + return items + } + + protected fun oneAndContinuedBy(separator: TokenType, read: () -> T): List { + val items = mutableListOf() + 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 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 + } +} diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/ParserHelpers.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/ParserHelpers.kt new file mode 100644 index 0000000..c7c7377 --- /dev/null +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/ParserHelpers.kt @@ -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") + } +} diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PorkParser.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PorkParser.kt index 9a23cb5..38c7f96 100644 --- a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PorkParser.kt +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PorkParser.kt @@ -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 }