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
This commit is contained in:
a dinosaur 2023-09-12 04:02:58 +10:00 committed by GitHub
parent 0aab45094a
commit a07e0fe672
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 65 additions and 11 deletions

View File

@ -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: "+"

View File

@ -19,6 +19,8 @@ enum class InfixOperator(val token: String) {
Greater(">"),
GreaterEqual(">="),
LesserEqual("<="),
BooleanAnd("and"),
BooleanOr("or"),
BinaryAnd("&"),
BinaryOr("|"),
BinaryExclusiveOr("^")

View File

@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable
@Serializable
@SerialName("prefixOperator")
enum class PrefixOperator(val token: String) {
Negate("!"),
BooleanNot("not"),
UnaryPlus("+"),
UnaryMinus("-"),
BinaryNot("~")

View File

@ -92,7 +92,7 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
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<Any> {
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<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.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<Any> {
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))

30
examples/boolean.pork Normal file
View File

@ -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("|------------------------------------------------------|")
}

View File

@ -83,7 +83,7 @@ class Parser(source: PeekableSource<Token>, 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<Token>, 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<Token>, 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<Token>, 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

View File

@ -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(',')),