From 743bc00bab72d8063472525c94e19814d721fbcc Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Wed, 30 Aug 2023 03:14:53 -0700 Subject: [PATCH] Cleanup parser and extract common logic. --- .../gay/pizza/pork/frontend/FileFrontend.kt | 1 - .../kotlin/gay/pizza/pork/parse/Parser.kt | 195 ++++++++++-------- 2 files changed, 110 insertions(+), 86 deletions(-) diff --git a/src/main/kotlin/gay/pizza/pork/frontend/FileFrontend.kt b/src/main/kotlin/gay/pizza/pork/frontend/FileFrontend.kt index 5a64d8c..d3ecaac 100644 --- a/src/main/kotlin/gay/pizza/pork/frontend/FileFrontend.kt +++ b/src/main/kotlin/gay/pizza/pork/frontend/FileFrontend.kt @@ -1,6 +1,5 @@ package gay.pizza.pork.frontend -import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.parse.CharSource import gay.pizza.pork.parse.StringCharSource import java.nio.file.Path diff --git a/src/main/kotlin/gay/pizza/pork/parse/Parser.kt b/src/main/kotlin/gay/pizza/pork/parse/Parser.kt index f42de4b..1316d7b 100644 --- a/src/main/kotlin/gay/pizza/pork/parse/Parser.kt +++ b/src/main/kotlin/gay/pizza/pork/parse/Parser.kt @@ -6,75 +6,106 @@ import gay.pizza.pork.util.StringEscape class Parser(source: PeekableSource) { private val unsanitizedSource = source - private fun readIntLiteral(): IntLiteral { - val token = expect(TokenType.IntLiteral) - return IntLiteral(token.text.toInt()) + private fun readIntLiteral(): IntLiteral = + expect(TokenType.IntLiteral) { IntLiteral(it.text.toInt()) } + + private fun readStringLiteral(): StringLiteral = + expect(TokenType.StringLiteral) { + val content = StringEscape.unescape(StringEscape.unquote(it.text)) + StringLiteral(content) + } + + private fun readBooleanLiteral(): BooleanLiteral = + expect(TokenType.True, TokenType.False) { + BooleanLiteral(it.type == TokenType.True) + } + + private fun readListLiteral(): ListLiteral { + expect(TokenType.LeftBracket) + val items = collect(TokenType.RightBracket, TokenType.Comma) { + readExpression() + } + expect(TokenType.RightBracket) + return ListLiteral(items) } - private fun readSymbol(): Symbol { - val token = expect(TokenType.Symbol) - return Symbol(token.text) + private fun readSymbol(): Symbol = + expect(TokenType.Symbol) { Symbol(it.text) } + + private fun readSymbolCases(): Expression { + val symbol = readSymbol() + return if (next(TokenType.LeftParentheses)) { + val arguments = collect(TokenType.RightParentheses, TokenType.Comma) { + readExpression() + } + expect(TokenType.RightParentheses) + FunctionCall(symbol, arguments) + } else if (next(TokenType.Equals)) { + Define(symbol, readExpression()) + } else { + SymbolReference(symbol) + } } + private fun readLambda(): Lambda { + expect(TokenType.LeftCurly) + val arguments = mutableListOf() + while (!peek(TokenType.In)) { + val symbol = readSymbol() + arguments.add(symbol) + if (next(TokenType.Comma)) { + continue + } else { + break + } + } + expect(TokenType.In) + val items = collect(TokenType.RightCurly) { + readExpression() + } + expect(TokenType.RightCurly) + return Lambda(arguments, items) + } + + private fun readParentheses(): Parentheses { + expect(TokenType.LeftParentheses) + val expression = readExpression() + expect(TokenType.RightParentheses) + return Parentheses(expression) + } + + private fun readNegation(): PrefixOperation = + expect(TokenType.Negation) { + PrefixOperation(PrefixOperator.Negate, readExpression()) + } + private fun readIf(): If { expect(TokenType.If) val condition = readExpression() expect(TokenType.Then) val thenExpression = readExpression() var elseExpression: Expression? = null - if (peekType(TokenType.Else)) { - expect(TokenType.Else) + if (next(TokenType.Else)) { elseExpression = readExpression() } return If(condition, thenExpression, elseExpression) } - private fun readSymbolCases(): Expression { - val symbol = readSymbol() - return if (peekType(TokenType.LeftParentheses)) { - expect(TokenType.LeftParentheses) - val arguments = collectExpressions(TokenType.RightParentheses, TokenType.Comma) - expect(TokenType.RightParentheses) - FunctionCall(symbol, arguments) - } else if (peekType(TokenType.Equals)) { - expect(TokenType.Equals) - Define(symbol, readExpression()) - } else { - SymbolReference(symbol) - } - } - - fun readLambda(): Lambda { - expect(TokenType.LeftCurly) - val arguments = mutableListOf() - while (!peekType(TokenType.In)) { - val symbol = readSymbol() - arguments.add(symbol) - if (peekType(TokenType.Comma)) { - expect(TokenType.Comma) - continue - } else { - break - } - } - expect(TokenType.In) - val items = collectExpressions(TokenType.RightCurly) - expect(TokenType.RightCurly) - return Lambda(arguments, items) - } - fun readExpression(): Expression { val token = peek() val expression = when (token.type) { - TokenType.StringLiteral -> { - expect(TokenType.StringLiteral) - return StringLiteral(StringEscape.unescape(StringEscape.unquote(token.text))) - } - TokenType.IntLiteral -> { readIntLiteral() } + TokenType.StringLiteral -> { + readStringLiteral() + } + + TokenType.True, TokenType.False -> { + readBooleanLiteral() + } + TokenType.LeftBracket -> { readListLiteral() } @@ -88,39 +119,25 @@ class Parser(source: PeekableSource) { } TokenType.LeftParentheses -> { - expect(TokenType.LeftParentheses) - val expression = readExpression() - expect(TokenType.RightParentheses) - Parentheses(expression) - } - - TokenType.True -> { - expect(TokenType.True) - return BooleanLiteral(true) - } - - TokenType.False -> { - expect(TokenType.False) - return BooleanLiteral(false) + readParentheses() } TokenType.Negation -> { - expect(TokenType.Negation) - return PrefixOperation(PrefixOperator.Negate, readExpression()) + readNegation() } TokenType.If -> { - return readIf() + readIf() } else -> { throw RuntimeException( - "Failed to parse token: ${token.type} '${token.text}' as expression" + - " (index ${unsanitizedSource.currentIndex})") + "Failed to parse token: ${token.type} '${token.text}' as" + + " expression (index ${unsanitizedSource.currentIndex})") } } - if (peekType( + if (peek( TokenType.Plus, TokenType.Minus, TokenType.Multiply, @@ -146,44 +163,52 @@ class Parser(source: PeekableSource) { else -> throw RuntimeException("Unknown Infix Operator") } - fun readListLiteral(): ListLiteral { - expect(TokenType.LeftBracket) - val items = collectExpressions(TokenType.RightBracket, TokenType.Comma) - expect(TokenType.RightBracket) - return ListLiteral(items) - } - fun readProgram(): Program { - val items = collectExpressions(TokenType.EndOfFile) + val items = collect(TokenType.EndOfFile) { readExpression() } expect(TokenType.EndOfFile) return Program(items) } - private fun collectExpressions(peeking: TokenType, consuming: TokenType? = null): List { - val items = mutableListOf() - while (!peekType(peeking)) { - val expression = readExpression() - if (consuming != null && !peekType(peeking)) { - expect(consuming) + private fun collect( + peeking: TokenType, + consuming: TokenType? = null, + read: () -> T + ): List { + val items = mutableListOf() + while (!peek(peeking)) { + val expression = read() + if (consuming != null) { + next(consuming) } items.add(expression) } return items } - private fun peekType(vararg types: TokenType): Boolean { + private fun peek(vararg types: TokenType): Boolean { val token = peek() return types.contains(token.type) } - private fun expect(type: TokenType): Token { + 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 (token.type != type) { - throw RuntimeException("Expected token type '${type}' but got type ${token.type} '${token.text}'") + if (!types.contains(token.type)) { + throw RuntimeException("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()