Cleanup parser and extract common logic.

This commit is contained in:
Alex Zenla 2023-08-30 03:14:53 -07:00
parent 7f0b9ca8e3
commit 743bc00bab
Signed by: alex
GPG Key ID: C0780728420EBFE5
2 changed files with 110 additions and 86 deletions

View File

@ -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

View File

@ -6,75 +6,106 @@ import gay.pizza.pork.util.StringEscape
class Parser(source: PeekableSource<Token>) {
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<Symbol>()
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<Symbol>()
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<Token>) {
}
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<Token>) {
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<Expression> {
val items = mutableListOf<Expression>()
while (!peekType(peeking)) {
val expression = readExpression()
if (consuming != null && !peekType(peeking)) {
expect(consuming)
private fun <T> collect(
peeking: TokenType,
consuming: TokenType? = null,
read: () -> T
): List<T> {
val items = mutableListOf<T>()
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 <T> expect(vararg types: TokenType, consume: (Token) -> T): T =
consume(expect(*types))
private fun next(): Token {
while (true) {
val token = unsanitizedSource.next()