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
|
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
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user