From 72163a2aa6d52e5de73f934b6a644277b9d0848a Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sun, 20 Aug 2023 00:48:10 -0700 Subject: [PATCH] Implement token promotion. --- .../kotlin/gay/pizza/pork/ast/BooleanLiteral.kt | 2 +- src/main/kotlin/gay/pizza/pork/ast/Define.kt | 2 +- .../kotlin/gay/pizza/pork/ast/Expression.kt | 2 +- .../kotlin/gay/pizza/pork/ast/FunctionCall.kt | 2 +- src/main/kotlin/gay/pizza/pork/ast/If.kt | 2 +- .../kotlin/gay/pizza/pork/ast/InfixOperation.kt | 2 +- .../kotlin/gay/pizza/pork/ast/IntLiteral.kt | 2 +- src/main/kotlin/gay/pizza/pork/ast/Lambda.kt | 2 +- .../kotlin/gay/pizza/pork/ast/ListLiteral.kt | 2 +- src/main/kotlin/gay/pizza/pork/ast/Node.kt | 8 +++++--- .../kotlin/gay/pizza/pork/ast/Parentheses.kt | 2 +- src/main/kotlin/gay/pizza/pork/ast/Program.kt | 2 +- src/main/kotlin/gay/pizza/pork/ast/Symbol.kt | 2 +- .../gay/pizza/pork/ast/SymbolReference.kt | 2 +- .../kotlin/gay/pizza/pork/parse/PorkParser.kt | 15 ++------------- .../gay/pizza/pork/parse/PorkTokenizer.kt | 17 +++++++++++++++-- .../gay/pizza/pork/parse/TokenPromotion.kt | 3 +++ .../kotlin/gay/pizza/pork/parse/TokenType.kt | 5 +++-- 18 files changed, 41 insertions(+), 33 deletions(-) create mode 100644 src/main/kotlin/gay/pizza/pork/parse/TokenPromotion.kt diff --git a/src/main/kotlin/gay/pizza/pork/ast/BooleanLiteral.kt b/src/main/kotlin/gay/pizza/pork/ast/BooleanLiteral.kt index 3ac3c7d..244550a 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/BooleanLiteral.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/BooleanLiteral.kt @@ -1,5 +1,5 @@ package gay.pizza.pork.ast -class BooleanLiteral(val value: Boolean) : Expression { +class BooleanLiteral(val value: Boolean) : Expression() { override val type: NodeType = NodeType.BooleanLiteral } diff --git a/src/main/kotlin/gay/pizza/pork/ast/Define.kt b/src/main/kotlin/gay/pizza/pork/ast/Define.kt index 0975e68..3e96a4a 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/Define.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/Define.kt @@ -1,6 +1,6 @@ package gay.pizza.pork.ast -class Define(val symbol: Symbol, val value: Expression) : Expression { +class Define(val symbol: Symbol, val value: Expression) : Expression() { override val type: NodeType = NodeType.Define override fun visitChildren(visitor: Visitor): List = diff --git a/src/main/kotlin/gay/pizza/pork/ast/Expression.kt b/src/main/kotlin/gay/pizza/pork/ast/Expression.kt index c4f5387..916bf7d 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/Expression.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/Expression.kt @@ -1,3 +1,3 @@ package gay.pizza.pork.ast -interface Expression : Node +abstract class Expression : Node() diff --git a/src/main/kotlin/gay/pizza/pork/ast/FunctionCall.kt b/src/main/kotlin/gay/pizza/pork/ast/FunctionCall.kt index a2f0b90..e9baee6 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/FunctionCall.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/FunctionCall.kt @@ -1,6 +1,6 @@ package gay.pizza.pork.ast -class FunctionCall(val symbol: Symbol, val arguments: List) : Expression { +class FunctionCall(val symbol: Symbol, val arguments: List) : Expression() { override val type: NodeType = NodeType.FunctionCall override fun visitChildren(visitor: Visitor): List = diff --git a/src/main/kotlin/gay/pizza/pork/ast/If.kt b/src/main/kotlin/gay/pizza/pork/ast/If.kt index 3648561..3a8b98b 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/If.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/If.kt @@ -4,6 +4,6 @@ class If( val condition: Expression, val thenExpression: Expression, val elseExpression: Expression? = null -) : Expression { +) : Expression() { override val type: NodeType = NodeType.If } diff --git a/src/main/kotlin/gay/pizza/pork/ast/InfixOperation.kt b/src/main/kotlin/gay/pizza/pork/ast/InfixOperation.kt index d35f05a..9d2b74c 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/InfixOperation.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/InfixOperation.kt @@ -1,6 +1,6 @@ package gay.pizza.pork.ast -class InfixOperation(val left: Expression, val op: InfixOperator, val right: Expression) : Expression { +class InfixOperation(val left: Expression, val op: InfixOperator, val right: Expression) : Expression() { override val type: NodeType = NodeType.InfixOperation override fun visitChildren(visitor: Visitor): List = diff --git a/src/main/kotlin/gay/pizza/pork/ast/IntLiteral.kt b/src/main/kotlin/gay/pizza/pork/ast/IntLiteral.kt index ca27bd2..7175336 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/IntLiteral.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/IntLiteral.kt @@ -1,5 +1,5 @@ package gay.pizza.pork.ast -class IntLiteral(val value: Int) : Expression { +class IntLiteral(val value: Int) : Expression() { override val type: NodeType = NodeType.IntLiteral } diff --git a/src/main/kotlin/gay/pizza/pork/ast/Lambda.kt b/src/main/kotlin/gay/pizza/pork/ast/Lambda.kt index aa0be45..0b73601 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/Lambda.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/Lambda.kt @@ -1,6 +1,6 @@ package gay.pizza.pork.ast -class Lambda(val arguments: List, val expressions: List) : Expression { +class Lambda(val arguments: List, val expressions: List) : Expression() { override val type: NodeType = NodeType.Lambda override fun visitChildren(visitor: Visitor): List = diff --git a/src/main/kotlin/gay/pizza/pork/ast/ListLiteral.kt b/src/main/kotlin/gay/pizza/pork/ast/ListLiteral.kt index b8c467b..d0b094c 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/ListLiteral.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/ListLiteral.kt @@ -1,6 +1,6 @@ package gay.pizza.pork.ast -class ListLiteral(val items: List) : Expression { +class ListLiteral(val items: List) : Expression() { override val type: NodeType = NodeType.ListLiteral override fun visitChildren(visitor: Visitor): List = diff --git a/src/main/kotlin/gay/pizza/pork/ast/Node.kt b/src/main/kotlin/gay/pizza/pork/ast/Node.kt index 8e557f0..9919bb1 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/Node.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/Node.kt @@ -1,6 +1,8 @@ package gay.pizza.pork.ast -interface Node { - val type: NodeType - fun visitChildren(visitor: Visitor): List = emptyList() +abstract class Node { + abstract val type: NodeType + open fun visitChildren(visitor: Visitor): List = emptyList() + + override fun toString(): String = let { node -> buildString { Printer(this).visit(node) } } } diff --git a/src/main/kotlin/gay/pizza/pork/ast/Parentheses.kt b/src/main/kotlin/gay/pizza/pork/ast/Parentheses.kt index b2cf606..8b455dd 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/Parentheses.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/Parentheses.kt @@ -1,6 +1,6 @@ package gay.pizza.pork.ast -class Parentheses(val expression: Expression) : Expression { +class Parentheses(val expression: Expression) : Expression() { override val type: NodeType = NodeType.Parentheses override fun visitChildren(visitor: Visitor): List = diff --git a/src/main/kotlin/gay/pizza/pork/ast/Program.kt b/src/main/kotlin/gay/pizza/pork/ast/Program.kt index 5045708..bad04fc 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/Program.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/Program.kt @@ -1,6 +1,6 @@ package gay.pizza.pork.ast -class Program(val expressions: List) : Node { +class Program(val expressions: List) : Node() { override val type: NodeType = NodeType.Program override fun visitChildren(visitor: Visitor): List = diff --git a/src/main/kotlin/gay/pizza/pork/ast/Symbol.kt b/src/main/kotlin/gay/pizza/pork/ast/Symbol.kt index c0bd153..5c32a1c 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/Symbol.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/Symbol.kt @@ -1,5 +1,5 @@ package gay.pizza.pork.ast -class Symbol(val id: String) : Node { +class Symbol(val id: String) : Node() { override val type: NodeType = NodeType.Symbol } diff --git a/src/main/kotlin/gay/pizza/pork/ast/SymbolReference.kt b/src/main/kotlin/gay/pizza/pork/ast/SymbolReference.kt index 714ad5e..04e4e82 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/SymbolReference.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/SymbolReference.kt @@ -1,6 +1,6 @@ package gay.pizza.pork.ast -class SymbolReference(val symbol: Symbol) : Expression { +class SymbolReference(val symbol: Symbol) : Expression() { override val type: NodeType = NodeType.SymbolReference override fun visitChildren(visitor: Visitor): List = diff --git a/src/main/kotlin/gay/pizza/pork/parse/PorkParser.kt b/src/main/kotlin/gay/pizza/pork/parse/PorkParser.kt index 382b2b9..45bd7c6 100644 --- a/src/main/kotlin/gay/pizza/pork/parse/PorkParser.kt +++ b/src/main/kotlin/gay/pizza/pork/parse/PorkParser.kt @@ -109,18 +109,12 @@ class PorkParser(val source: PeekableSource) { } } - if (peekType(TokenType.Plus, TokenType.Minus, TokenType.Multiply, TokenType.Divide)) { + if (peekType(TokenType.Plus, TokenType.Minus, TokenType.Multiply, TokenType.Divide, TokenType.Equality)) { val infixToken = source.next() val infixOperator = convertInfixOperator(infixToken) return InfixOperation(expression, infixOperator, readExpression()) } - if (peekType(TokenType.Equals)) { - val twoWideInfix = source.next() - val secondToken = expect(twoWideInfix.type) - return InfixOperation(expression, convertWideInfixOperator(twoWideInfix, secondToken), readExpression()) - } - return expression } @@ -130,12 +124,7 @@ class PorkParser(val source: PeekableSource) { TokenType.Minus -> InfixOperator.Minus TokenType.Multiply -> InfixOperator.Multiply TokenType.Divide -> InfixOperator.Divide - else -> throw RuntimeException("Unknown Infix Operator") - } - - private fun convertWideInfixOperator(firstToken: Token, secondToken: Token): InfixOperator = - when (firstToken.type to secondToken.type) { - TokenType.Equals to TokenType.Equals -> InfixOperator.Equals + TokenType.Equality -> InfixOperator.Equals else -> throw RuntimeException("Unknown Infix Operator") } diff --git a/src/main/kotlin/gay/pizza/pork/parse/PorkTokenizer.kt b/src/main/kotlin/gay/pizza/pork/parse/PorkTokenizer.kt index 8dc871e..90271ff 100644 --- a/src/main/kotlin/gay/pizza/pork/parse/PorkTokenizer.kt +++ b/src/main/kotlin/gay/pizza/pork/parse/PorkTokenizer.kt @@ -54,10 +54,23 @@ class PorkTokenizer(val source: CharSource, val preserveWhitespace: Boolean = fa while (source.peek() != CharSource.NullChar) { tokenStart = source.currentIndex val char = source.next() + for (item in TokenType.SingleChars) { - if (item.char == char) { - return Token(item, char.toString()) + if (item.char != char) { + continue } + + var type = item + var text = item.char.toString() + for (promotion in item.promotions) { + if (source.peek() != promotion.nextChar) { + continue + } + val nextChar = source.next() + type = promotion.type + text += nextChar + } + return Token(type, text) } if (isWhitespace(char)) { diff --git a/src/main/kotlin/gay/pizza/pork/parse/TokenPromotion.kt b/src/main/kotlin/gay/pizza/pork/parse/TokenPromotion.kt new file mode 100644 index 0000000..4fb743c --- /dev/null +++ b/src/main/kotlin/gay/pizza/pork/parse/TokenPromotion.kt @@ -0,0 +1,3 @@ +package gay.pizza.pork.parse + +class TokenPromotion(val nextChar: Char, val type: TokenType) diff --git a/src/main/kotlin/gay/pizza/pork/parse/TokenType.kt b/src/main/kotlin/gay/pizza/pork/parse/TokenType.kt index 80a600d..3b6f443 100644 --- a/src/main/kotlin/gay/pizza/pork/parse/TokenType.kt +++ b/src/main/kotlin/gay/pizza/pork/parse/TokenType.kt @@ -1,9 +1,10 @@ package gay.pizza.pork.parse -enum class TokenType(val char: Char? = null, val keyword: String? = null) { +enum class TokenType(val char: Char? = null, val keyword: String? = null, val promotions: List = emptyList()) { Symbol, IntLiteral, - Equals(char = '='), + Equality, + Equals(char = '=', promotions = listOf(TokenPromotion('=', Equality))), Plus(char = '+'), Minus(char = '-'), Multiply(char = '*'),