From a07e0fe672331f58bfccb03b06418bbbd150da28 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Tue, 12 Sep 2023 04:02:58 +1000 Subject: [PATCH] language: add boolean and/or operators, change negation syntax (#7) * language: add boolean and/or operators, change negation syntax * examples: simplify row builder using arrays --- ast/src/main/ast/pork.yml | 10 +++++-- .../gay/pizza/pork/ast/InfixOperator.kt | 2 ++ .../gay/pizza/pork/ast/PrefixOperator.kt | 2 +- .../pizza/pork/evaluator/EvaluationVisitor.kt | 14 +++++++-- examples/boolean.pork | 30 +++++++++++++++++++ .../kotlin/gay/pizza/pork/parser/Parser.kt | 11 +++++-- .../kotlin/gay/pizza/pork/parser/TokenType.kt | 7 +++-- 7 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 examples/boolean.pork diff --git a/ast/src/main/ast/pork.yml b/ast/src/main/ast/pork.yml index c725c00..d684c27 100644 --- a/ast/src/main/ast/pork.yml +++ b/ast/src/main/ast/pork.yml @@ -96,6 +96,12 @@ types: - name: LesserEqual values: token: "<=" + - name: BooleanAnd + values: + token: "and" + - name: BooleanOr + values: + token: "or" - name: BinaryAnd values: token: "&" @@ -196,9 +202,9 @@ types: - name: token type: String enums: - - name: Negate + - name: BooleanNot values: - token: "!" + token: "not" - name: UnaryPlus values: token: "+" 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 6e105a3..6e388f3 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperator.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperator.kt @@ -19,6 +19,8 @@ enum class InfixOperator(val token: String) { Greater(">"), GreaterEqual(">="), LesserEqual("<="), + BooleanAnd("and"), + BooleanOr("or"), 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 d6202c1..430b8e3 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperator.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperator.kt @@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable @Serializable @SerialName("prefixOperator") enum class PrefixOperator(val token: String) { - Negate("!"), + BooleanNot("not"), UnaryPlus("+"), 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 420415e..1e3c6b6 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt @@ -92,7 +92,7 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { override fun visitPrefixOperation(node: PrefixOperation): Any { val value = node.expression.visit(this) return when (node.op) { - PrefixOperator.Negate -> { + PrefixOperator.BooleanNot -> { if (value !is Boolean) { throw RuntimeException("Cannot negate a value which is not a boolean.") } @@ -192,6 +192,14 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { else -> {} } + if (left is Boolean && right is Boolean) { + when (node.op) { + InfixOperator.BooleanAnd -> return left && right + InfixOperator.BooleanOr -> return left || right + else -> {} + } + } + if (left !is Number || right !is Number) { throw RuntimeException("Failed to evaluate infix operation, bad types.") } @@ -311,7 +319,7 @@ 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.Equals, InfixOperator.NotEquals, InfixOperator.BooleanAnd, InfixOperator.BooleanOr -> 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)) @@ -333,7 +341,7 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { binaryNot: (T) -> T ): Any { return when (op) { - PrefixOperator.Negate -> throw RuntimeException("Unable to handle operation $op") + PrefixOperator.BooleanNot -> throw RuntimeException("Unable to handle operation $op") PrefixOperator.UnaryPlus -> plus(convert(value)) PrefixOperator.UnaryMinus -> minus(convert(value)) PrefixOperator.BinaryNot -> binaryNot(convert(value)) diff --git a/examples/boolean.pork b/examples/boolean.pork new file mode 100644 index 0000000..6f9b9e4 --- /dev/null +++ b/examples/boolean.pork @@ -0,0 +1,30 @@ +func buildRow(str, a, b, c, d) { + let checkSeparator = " " + print("|", str, "| ") + for i in [a, b, c, d] { + print(checkSeparator) + if a { print("✅|") } else { print("🚫|") } + } + println() +} + +export func main() { + println("| | a=🚫 b=🚫| a=✅b=🚫| a=🚫b=✅| a=✅b=✅|") + buildRow(" a == b", false == false, true == false, false == true, true == true) + buildRow("!a == b", (not false) == false, (not true) == false, (not false) == true, (not true) == true) + buildRow(" a == !b", false == (not false), true == (not false), false == (not true), true == (not true)) + buildRow("!a == !b", (not false) == (not false), (not true) == (not false), (not false) == (not true), (not true) == (not true)) + buildRow(" a != b", false != false, true != false, false != true, true != true) + buildRow("!a != b", (not false) != false, (not true) != false, (not false) != true, (not true) != true) + buildRow(" a != !b", false != (not false), true != (not false), false != (not true), true != (not true)) + buildRow("!a != !b", (not false) != (not false), (not true) != (not false), (not false) != (not true), (not true) != (not true)) + buildRow(" a && b", false and false, true and false, false and true, true and true) + buildRow("!a && b", (not false) and false, (not true) and false, (not false) and true, (not true) and true) + buildRow(" a && !b", false and (not false), true and (not false), false and (not true), true and (not true)) + buildRow("!a && !b", (not false) and (not false), (not true) and (not false), (not false) and (not true), (not true) and (not true)) + buildRow(" a || b", false or false, true or false, false or true, true or true) + buildRow("!a || b", (not false) or false, (not true) or false, (not false) or true, (not true) or true) + buildRow(" a || !b", false or (not false), true or (not false), false or (not true), true or (not true)) + buildRow("!a || !b", (not false) or (not false), (not true) or (not false), (not false) or (not true), (not true) or (not true)) + println("|------------------------------------------------------|") +} 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 9390831..7b257fd 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, TokenType.Tilde) { + expect(TokenType.Not, TokenType.Plus, TokenType.Minus, TokenType.Tilde) { PrefixOperation(convertPrefixOperator(it), readExpression()) } } @@ -157,7 +157,7 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { readParentheses() } - TokenType.Negation, TokenType.Plus, TokenType.Minus, TokenType.Tilde -> { + TokenType.Not, TokenType.Plus, TokenType.Minus, TokenType.Tilde -> { readPrefixOperation() } @@ -215,7 +215,9 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { TokenType.Lesser, TokenType.Greater, TokenType.LesserEqual, - TokenType.GreaterEqual + TokenType.GreaterEqual, + TokenType.And, + TokenType.Or ) ) { within { @@ -338,10 +340,13 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { TokenType.Greater -> InfixOperator.Greater TokenType.LesserEqual -> InfixOperator.LesserEqual TokenType.GreaterEqual -> InfixOperator.GreaterEqual + TokenType.And -> InfixOperator.BooleanAnd + TokenType.Or -> InfixOperator.BooleanOr else -> throw RuntimeException("Unknown Infix Operator") } private fun convertPrefixOperator(token: Token): PrefixOperator = when (token.type) { + TokenType.Not -> PrefixOperator.BooleanNot TokenType.Plus -> PrefixOperator.UnaryPlus TokenType.Minus -> PrefixOperator.UnaryMinus TokenType.Tilde -> PrefixOperator.BinaryNot 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 6e68058..b4ab0fa 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt @@ -13,7 +13,8 @@ enum class TokenType(vararg properties: TokenTypeProperty) { (it in '0' .. '9')}, KeywordUpgrader), StringLiteral(StringLiteralFamily), Equality(OperatorFamily), - Inequality(OperatorFamily), + Inequality(ManyChars("!="), OperatorFamily), + ExclaimationPoint(SingleChar('!'), Promotion('=', Inequality)), Equals(SingleChar('='), Promotion('=', Equality)), PlusPlus(ManyChars("++"), OperatorFamily), MinusMinus(ManyChars("--"), OperatorFamily), @@ -21,6 +22,8 @@ enum class TokenType(vararg properties: TokenTypeProperty) { Minus(SingleChar('-'), OperatorFamily, Promotion('-', MinusMinus)), Multiply(SingleChar('*'), OperatorFamily), Divide(SingleChar('/'), OperatorFamily), + And(ManyChars("and"), OperatorFamily), + Or(ManyChars("or"), OperatorFamily), Tilde(SingleChar('~'), OperatorFamily), Ampersand(SingleChar('&'), OperatorFamily), Pipe(SingleChar('|'), OperatorFamily), @@ -35,7 +38,7 @@ enum class TokenType(vararg properties: TokenTypeProperty) { RightBracket(SingleChar(']')), LeftParentheses(SingleChar('(')), RightParentheses(SingleChar(')')), - Negation(SingleChar('!'), Promotion('=', Inequality), OperatorFamily), + Not(ManyChars("not"), OperatorFamily), Mod(ManyChars("mod"), OperatorFamily), Rem(ManyChars("rem"), OperatorFamily), Comma(SingleChar(',')),