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 package gay.pizza.pork.frontend
import gay.pizza.pork.ast.NodeVisitor
import gay.pizza.pork.parse.CharSource import gay.pizza.pork.parse.CharSource
import gay.pizza.pork.parse.StringCharSource import gay.pizza.pork.parse.StringCharSource
import java.nio.file.Path import java.nio.file.Path

View File

@ -6,75 +6,106 @@ import gay.pizza.pork.util.StringEscape
class Parser(source: PeekableSource<Token>) { class Parser(source: PeekableSource<Token>) {
private val unsanitizedSource = source private val unsanitizedSource = source
private fun readIntLiteral(): IntLiteral { private fun readIntLiteral(): IntLiteral =
val token = expect(TokenType.IntLiteral) expect(TokenType.IntLiteral) { IntLiteral(it.text.toInt()) }
return IntLiteral(token.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 { private fun readSymbol(): Symbol =
val token = expect(TokenType.Symbol) expect(TokenType.Symbol) { Symbol(it.text) }
return Symbol(token.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 { private fun readIf(): If {
expect(TokenType.If) expect(TokenType.If)
val condition = readExpression() val condition = readExpression()
expect(TokenType.Then) expect(TokenType.Then)
val thenExpression = readExpression() val thenExpression = readExpression()
var elseExpression: Expression? = null var elseExpression: Expression? = null
if (peekType(TokenType.Else)) { if (next(TokenType.Else)) {
expect(TokenType.Else)
elseExpression = readExpression() elseExpression = readExpression()
} }
return If(condition, thenExpression, elseExpression) 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 { fun readExpression(): Expression {
val token = peek() val token = peek()
val expression = when (token.type) { val expression = when (token.type) {
TokenType.StringLiteral -> {
expect(TokenType.StringLiteral)
return StringLiteral(StringEscape.unescape(StringEscape.unquote(token.text)))
}
TokenType.IntLiteral -> { TokenType.IntLiteral -> {
readIntLiteral() readIntLiteral()
} }
TokenType.StringLiteral -> {
readStringLiteral()
}
TokenType.True, TokenType.False -> {
readBooleanLiteral()
}
TokenType.LeftBracket -> { TokenType.LeftBracket -> {
readListLiteral() readListLiteral()
} }
@ -88,39 +119,25 @@ class Parser(source: PeekableSource<Token>) {
} }
TokenType.LeftParentheses -> { TokenType.LeftParentheses -> {
expect(TokenType.LeftParentheses) readParentheses()
val expression = readExpression()
expect(TokenType.RightParentheses)
Parentheses(expression)
}
TokenType.True -> {
expect(TokenType.True)
return BooleanLiteral(true)
}
TokenType.False -> {
expect(TokenType.False)
return BooleanLiteral(false)
} }
TokenType.Negation -> { TokenType.Negation -> {
expect(TokenType.Negation) readNegation()
return PrefixOperation(PrefixOperator.Negate, readExpression())
} }
TokenType.If -> { TokenType.If -> {
return readIf() readIf()
} }
else -> { else -> {
throw RuntimeException( throw RuntimeException(
"Failed to parse token: ${token.type} '${token.text}' as expression" + "Failed to parse token: ${token.type} '${token.text}' as" +
" (index ${unsanitizedSource.currentIndex})") " expression (index ${unsanitizedSource.currentIndex})")
} }
} }
if (peekType( if (peek(
TokenType.Plus, TokenType.Plus,
TokenType.Minus, TokenType.Minus,
TokenType.Multiply, TokenType.Multiply,
@ -146,44 +163,52 @@ class Parser(source: PeekableSource<Token>) {
else -> throw RuntimeException("Unknown Infix Operator") 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 { fun readProgram(): Program {
val items = collectExpressions(TokenType.EndOfFile) val items = collect(TokenType.EndOfFile) { readExpression() }
expect(TokenType.EndOfFile) expect(TokenType.EndOfFile)
return Program(items) return Program(items)
} }
private fun collectExpressions(peeking: TokenType, consuming: TokenType? = null): List<Expression> { private fun <T> collect(
val items = mutableListOf<Expression>() peeking: TokenType,
while (!peekType(peeking)) { consuming: TokenType? = null,
val expression = readExpression() read: () -> T
if (consuming != null && !peekType(peeking)) { ): List<T> {
expect(consuming) val items = mutableListOf<T>()
while (!peek(peeking)) {
val expression = read()
if (consuming != null) {
next(consuming)
} }
items.add(expression) items.add(expression)
} }
return items return items
} }
private fun peekType(vararg types: TokenType): Boolean { private fun peek(vararg types: TokenType): Boolean {
val token = peek() val token = peek()
return types.contains(token.type) 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() val token = next()
if (token.type != type) { if (!types.contains(token.type)) {
throw RuntimeException("Expected token type '${type}' but got type ${token.type} '${token.text}'") throw RuntimeException("Expected one of ${types.joinToString(", ")} " +
" but got type ${token.type} '${token.text}'")
} }
return token return token
} }
private fun <T> expect(vararg types: TokenType, consume: (Token) -> T): T =
consume(expect(*types))
private fun next(): Token { private fun next(): Token {
while (true) { while (true) {
val token = unsanitizedSource.next() val token = unsanitizedSource.next()