Skip to content

Commit

Permalink
language: add binary operators
Browse files Browse the repository at this point in the history
- unary binary not
- infix bitwise and
- infix bitwise or
- infix bitwise exclusive or (xor)
  • Loading branch information
ScrelliCopter committed Sep 11, 2023
1 parent 24d2ff5 commit cdcbdb2
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 12 deletions.
12 changes: 12 additions & 0 deletions ast/src/main/ast/pork.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -196,6 +205,9 @@ types:
- name: UnaryMinus
values:
token: "-"
- name: BinaryNot
values:
token: "~"
PrefixOperation:
parent: Expression
values:
Expand Down
5 changes: 4 additions & 1 deletion ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ enum class InfixOperator(val token: String) {
Lesser("<"),
Greater(">"),
GreaterEqual(">="),
LesserEqual("<=")
LesserEqual("<="),
BinaryAnd("&"),
BinaryOr("|"),
BinaryExclusiveOr("^")
}
3 changes: 2 additions & 1 deletion ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ import kotlinx.serialization.Serializable
enum class PrefixOperator(val token: String) {
Negate("!"),
UnaryPlus("+"),
UnaryMinus("-")
UnaryMinus("-"),
BinaryNot("~")
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
}
!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}'")
}
Expand All @@ -91,7 +91,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
value,
convert = { it.toDouble() },
plus = { +it },
minus = { -it }
minus = { -it },
binaryNot = unaryFloatingPointTypeError("binary not")
)
}
is Float -> {
Expand All @@ -100,7 +101,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
value,
convert = { it.toFloat() },
plus = { +it },
minus = { -it }
minus = { -it },
binaryNot = unaryFloatingPointTypeError("binary not")
)
}
is Long -> {
Expand All @@ -109,7 +111,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
value,
convert = { it.toLong() },
plus = { +it },
minus = { -it }
minus = { -it },
binaryNot = { it.inv() }
)
}
is Int -> {
Expand All @@ -118,7 +121,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
value,
convert = { it.toInt() },
plus = { +it },
minus = { -it }
minus = { -it },
binaryNot = { it.inv() }
)
}
else -> throw RuntimeException("Unknown numeric type: ${value.javaClass.name}")
Expand Down Expand Up @@ -179,6 +183,9 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
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 },
Expand All @@ -198,6 +205,9 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
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 },
Expand All @@ -217,6 +227,9 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
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 },
Expand All @@ -236,6 +249,9 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
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 },
Expand All @@ -257,6 +273,9 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
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,
Expand All @@ -269,13 +288,16 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
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")
}
}

Expand All @@ -284,12 +306,14 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
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))
}
}

Expand Down Expand Up @@ -328,6 +352,10 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
}
}

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")
}
Expand Down
11 changes: 9 additions & 2 deletions parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class Parser(source: PeekableSource<Token>, 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())
}
}
Expand Down Expand Up @@ -148,7 +148,7 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
readParentheses()
}

TokenType.Negation, TokenType.Plus, TokenType.Minus -> {
TokenType.Negation, TokenType.Plus, TokenType.Minus, TokenType.Tilde -> {
readPrefixOperation()
}

Expand Down Expand Up @@ -192,6 +192,9 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
TokenType.Minus,
TokenType.Multiply,
TokenType.Divide,
TokenType.Ampersand,
TokenType.Pipe,
TokenType.Caret,
TokenType.Equality,
TokenType.Inequality,
TokenType.Mod,
Expand Down Expand Up @@ -311,6 +314,9 @@ class Parser(source: PeekableSource<Token>, 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
Expand All @@ -325,6 +331,7 @@ class Parser(source: PeekableSource<Token>, 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")
}

Expand Down
4 changes: 4 additions & 0 deletions parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down

0 comments on commit cdcbdb2

Please sign in to comment.