mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 21:00:56 +00:00
Cleanup parser and extract common logic.
This commit is contained in:
parent
7f0b9ca8e3
commit
743bc00bab
@ -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
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user