From cdcbdb2bdf3f1e95d34080e45fc2c97ace419dd2 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Mon, 11 Sep 2023 19:21:09 +1000 Subject: [PATCH] language: add binary operators - unary binary not - infix bitwise and - infix bitwise or - infix bitwise exclusive or (xor) --- ast/src/main/ast/pork.yml | 12 +++++ .../gay/pizza/pork/ast/InfixOperator.kt | 5 ++- .../gay/pizza/pork/ast/PrefixOperator.kt | 3 +- .../pizza/pork/evaluator/EvaluationVisitor.kt | 44 +++++++++++++++---- .../kotlin/gay/pizza/pork/parser/Parser.kt | 11 ++++- .../kotlin/gay/pizza/pork/parser/TokenType.kt | 4 ++ 6 files changed, 67 insertions(+), 12 deletions(-) diff --git a/ast/src/main/ast/pork.yml b/ast/src/main/ast/pork.yml index c8b1174..1d2436b 100644 --- a/ast/src/main/ast/pork.yml +++ b/ast/src/main/ast/pork.yml @@ -96,6 +96,15 @@ types: - name: LesserEqual values: token: "<=" + - name: BinaryAnd + values: + token: "&" + - name: BinaryOr + values: + token: "|" + - name: BinaryExclusiveOr + values: + token: "^" InfixOperation: parent: Expression values: @@ -196,6 +205,9 @@ types: - name: UnaryMinus values: token: "-" + - name: BinaryNot + values: + token: "~" PrefixOperation: parent: Expression values: diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperator.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperator.kt index 1901a62..6e105a3 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperator.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperator.kt @@ -18,5 +18,8 @@ enum class InfixOperator(val token: String) { Lesser("<"), Greater(">"), GreaterEqual(">="), - LesserEqual("<=") + LesserEqual("<="), + BinaryAnd("&"), + BinaryOr("|"), + BinaryExclusiveOr("^") } diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperator.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperator.kt index fa0e797..d6202c1 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperator.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperator.kt @@ -9,5 +9,6 @@ import kotlinx.serialization.Serializable enum class PrefixOperator(val token: String) { Negate("!"), UnaryPlus("+"), - UnaryMinus("-") + UnaryMinus("-"), + BinaryNot("~") } diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt index 976017e..ba3b21b 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt @@ -75,7 +75,7 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { } !value } - PrefixOperator.UnaryPlus, PrefixOperator.UnaryMinus -> { + PrefixOperator.UnaryPlus, PrefixOperator.UnaryMinus, PrefixOperator.BinaryNot -> { if (value !is Number) { throw RuntimeException("Numeric unary '${node.op.token}' illegal on non-numeric type '${value.javaClass.simpleName}'") } @@ -91,7 +91,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { value, convert = { it.toDouble() }, plus = { +it }, - minus = { -it } + minus = { -it }, + binaryNot = unaryFloatingPointTypeError("binary not") ) } is Float -> { @@ -100,7 +101,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { value, convert = { it.toFloat() }, plus = { +it }, - minus = { -it } + minus = { -it }, + binaryNot = unaryFloatingPointTypeError("binary not") ) } is Long -> { @@ -109,7 +111,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { value, convert = { it.toLong() }, plus = { +it }, - minus = { -it } + minus = { -it }, + binaryNot = { it.inv() } ) } is Int -> { @@ -118,7 +121,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { value, convert = { it.toInt() }, plus = { +it }, - minus = { -it } + minus = { -it }, + binaryNot = { it.inv() } ) } else -> throw RuntimeException("Unknown numeric type: ${value.javaClass.name}") @@ -179,6 +183,9 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { subtract = { a, b -> a - b }, multiply = { a, b -> a * b }, divide = { a, b -> a / b }, + binaryAnd = { _, _ -> floatingPointTypeError("binary and") }, + binaryOr = { _, _ -> floatingPointTypeError("binary or") }, + binaryExclusiveOr = { _, _ -> floatingPointTypeError("binary exclusive-or") }, euclideanModulo = { _, _ -> floatingPointTypeError("integer modulo") }, remainder = { _, _ -> floatingPointTypeError("integer remainder") }, lesser = { a, b -> a < b }, @@ -198,6 +205,9 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { subtract = { a, b -> a - b }, multiply = { a, b -> a * b }, divide = { a, b -> a / b }, + binaryAnd = { _, _ -> floatingPointTypeError("binary and") }, + binaryOr = { _, _ -> floatingPointTypeError("binary or") }, + binaryExclusiveOr = { _, _ -> floatingPointTypeError("binary exclusive-or") }, euclideanModulo = { _, _ -> floatingPointTypeError("integer modulo") }, remainder = { _, _ -> floatingPointTypeError("integer remainder") }, lesser = { a, b -> a < b }, @@ -217,6 +227,9 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { subtract = { a, b -> a - b }, multiply = { a, b -> a * b }, divide = { a, b -> a / b }, + binaryAnd = { a, b -> a and b }, + binaryOr = { a, b -> a or b }, + binaryExclusiveOr = { a, b -> a xor b }, euclideanModulo = { x, d -> (x % d).let { q -> if (q < 0) q + abs(d) else q } }, remainder = { x, d -> x % d }, lesser = { a, b -> a < b }, @@ -236,6 +249,9 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { subtract = { a, b -> a - b }, multiply = { a, b -> a * b }, divide = { a, b -> a / b }, + binaryAnd = { a, b -> a and b }, + binaryOr = { a, b -> a or b }, + binaryExclusiveOr = { a, b -> a xor b }, euclideanModulo = { x, d -> (x % d).let { q -> if (q < 0) q + abs(d) else q } }, remainder = { x, d -> x % d }, lesser = { a, b -> a < b }, @@ -257,6 +273,9 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { subtract: (T, T) -> T, multiply: (T, T) -> T, divide: (T, T) -> T, + binaryAnd: (T, T) -> T, + binaryOr: (T, T) -> T, + binaryExclusiveOr: (T, T) -> T, euclideanModulo: (T, T) -> T, remainder: (T, T) -> T, lesser: (T, T) -> Boolean, @@ -269,13 +288,16 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { InfixOperator.Minus -> subtract(convert(left), convert(right)) InfixOperator.Multiply -> multiply(convert(left), convert(right)) InfixOperator.Divide -> divide(convert(left), convert(right)) + InfixOperator.Equals, InfixOperator.NotEquals -> throw RuntimeException("Unable to handle operation $op") + InfixOperator.BinaryAnd -> binaryAnd(convert(left), convert(right)) + InfixOperator.BinaryOr -> binaryOr(convert(left), convert(right)) + InfixOperator.BinaryExclusiveOr -> binaryExclusiveOr(convert(left), convert(right)) InfixOperator.EuclideanModulo -> euclideanModulo(convert(left), convert(right)) InfixOperator.Remainder -> remainder(convert(left), convert(right)) InfixOperator.Lesser -> lesser(convert(left), convert(right)) InfixOperator.Greater -> greater(convert(left), convert(right)) InfixOperator.LesserEqual -> lesserEqual(convert(left), convert(right)) InfixOperator.GreaterEqual -> greaterEqual(convert(left), convert(right)) - else -> throw RuntimeException("Unable to handle operation $op") } } @@ -284,12 +306,14 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { value: Number, convert: (Number) -> T, plus: (T) -> T, - minus: (T) -> T + minus: (T) -> T, + binaryNot: (T) -> T ): Any { return when (op) { + PrefixOperator.Negate -> throw RuntimeException("Unable to handle operation $op") PrefixOperator.UnaryPlus -> plus(convert(value)) PrefixOperator.UnaryMinus -> minus(convert(value)) - else -> throw RuntimeException("Unable to handle operation $op") + PrefixOperator.BinaryNot -> binaryNot(convert(value)) } } @@ -328,6 +352,10 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { } } + private fun unaryFloatingPointTypeError(operation: String): Nothing { + throw RuntimeException("Can't perform $operation on a floating point type") + } + private fun floatingPointTypeError(operation: String): Nothing { throw RuntimeException("Can't perform $operation between floating point types") } diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt index c88adde..fd80993 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt @@ -83,7 +83,7 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { } private fun readPrefixOperation(): PrefixOperation = within { - expect(TokenType.Negation, TokenType.Plus, TokenType.Minus) { + expect(TokenType.Negation, TokenType.Plus, TokenType.Minus, TokenType.Tilde) { PrefixOperation(convertPrefixOperator(it), readExpression()) } } @@ -148,7 +148,7 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { readParentheses() } - TokenType.Negation, TokenType.Plus, TokenType.Minus -> { + TokenType.Negation, TokenType.Plus, TokenType.Minus, TokenType.Tilde -> { readPrefixOperation() } @@ -192,6 +192,9 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { TokenType.Minus, TokenType.Multiply, TokenType.Divide, + TokenType.Ampersand, + TokenType.Pipe, + TokenType.Caret, TokenType.Equality, TokenType.Inequality, TokenType.Mod, @@ -311,6 +314,9 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { TokenType.Minus -> InfixOperator.Minus TokenType.Multiply -> InfixOperator.Multiply TokenType.Divide -> InfixOperator.Divide + TokenType.Ampersand -> InfixOperator.BinaryAnd + TokenType.Pipe -> InfixOperator.BinaryOr + TokenType.Caret -> InfixOperator.BinaryExclusiveOr TokenType.Equality -> InfixOperator.Equals TokenType.Inequality -> InfixOperator.NotEquals TokenType.Mod -> InfixOperator.EuclideanModulo @@ -325,6 +331,7 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { private fun convertPrefixOperator(token: Token): PrefixOperator = when (token.type) { TokenType.Plus -> PrefixOperator.UnaryPlus TokenType.Minus -> PrefixOperator.UnaryMinus + TokenType.Tilde -> PrefixOperator.BinaryNot else -> throw RuntimeException("Unknown Prefix Operator") } diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt index 413a232..cc4b5a2 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt @@ -21,6 +21,10 @@ enum class TokenType(vararg properties: TokenTypeProperty) { Minus(SingleChar('-'), OperatorFamily, Promotion('-', MinusMinus)), Multiply(SingleChar('*'), OperatorFamily), Divide(SingleChar('/'), OperatorFamily), + Tilde(SingleChar('~'), OperatorFamily), + Ampersand(SingleChar('&'), OperatorFamily), + Pipe(SingleChar('|'), OperatorFamily), + Caret(SingleChar('^'), OperatorFamily), LesserEqual(OperatorFamily), GreaterEqual(OperatorFamily), Lesser(SingleChar('<'), OperatorFamily, Promotion('=', LesserEqual)),