mirror of
				https://github.com/GayPizzaSpecifications/pork.git
				synced 2025-11-04 09:59:39 +00:00 
			
		
		
		
	Cleanup parser and extract common logic.
This commit is contained in:
		@ -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()
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user