diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 7d5bf6871..4e0d54d81 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -191,7 +191,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex return when (parent) { is TypecastExpression -> InferredTypes.InferredType.known((parent as TypecastExpression).type) is Assignment -> (parent as Assignment).target.inferType(program) - else -> InferredTypes.InferredType.known(DataType.UBYTE) + else -> InferredTypes.InferredType.known(DataType.UBYTE) // note: don't use BOOL type here to avoid type errors later! Will be replaced anyway. } } @@ -214,7 +214,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex } } "&", "|", "^" -> if(leftDt istype DataType.BOOL) InferredTypes.knownFor(DataType.UBYTE) else leftDt - "and", "or", "xor", "not" -> InferredTypes.knownFor(DataType.UBYTE) + "and", "or", "xor", "not" -> InferredTypes.knownFor(DataType.UBYTE) // note: don't use BOOL type here to avoid type errors later! Will be replaced anyway. "<", ">", "<=", ">=", "==", "!=", "in" -> dynamicBooleanType() diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 6947c28f3..02aa21143 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -237,6 +237,20 @@ The signed integers integers are in the range -128..127 for bytes, and -32768..32767 for words. +Boolean values +^^^^^^^^^^^^^^ + +These values are only ``true`` or ``false``, or 1 or 0. An integer's "truthy" value (i.e. a number +converted to boolean) is ``false`` (0) when it is zero and ``true`` (1) for all other values. +Logical expressions, comparisons and some other code might compile more efficiently if +you explicitly use ``bool`` types instead of integers there because that will avoid this conversion. +In the end the compiler translates boolean variables to a byte that stores just 0 or 1. + +If you find that you need a whole bunch of boolean variables or perhaps even an array of them, +consider using bit masks in regular integer variables instead. +This saves a lot of memory and may be faster as well. + + Floating point numbers ^^^^^^^^^^^^^^^^^^^^^^ @@ -645,7 +659,9 @@ Arithmetic and Logical expressions Arithmetic expressions are expressions that calculate a numeric result (integer or floating point). Many common arithmetic operators can be used and follow the regular precedence rules. Logical expressions are expressions that calculate a boolean result: true or false -(which in reality are just a 1 or 0 integer value). +(which in reality are just a 1 or 0 integer value). When using variables of the type ``bool``, +logical expressions will compile more efficiently than when you're using regular integer type operands +(because these have to be converted to 0 or 1 every time) You can use parentheses to group parts of an expresion to change the precedence. Usually the normal precedence rules apply (``*`` goes before ``+`` etc.) but subexpressions @@ -775,10 +791,6 @@ sort(array) Miscellaneous ^^^^^^^^^^^^^ -boolean(x) - Returns a byte value representing the boolean (truthy) value of x, where x can be any numeric type. - This means it returns 0 (false) when x equals 0, and 1 otherwise. - cmp(x,y) Compare the integer value x to integer value y. Doesn't return a value or boolean result, only sets the processor's status bits! You can use a conditional jumps (``if_cc`` etcetera) to act on this. diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index fac90d00c..6bdef3717 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -300,7 +300,7 @@ type identifier type storage size example var declara =============== ======================= ================= ========================================= ``byte`` signed byte 1 byte = 8 bits ``byte myvar = -22`` ``ubyte`` unsigned byte 1 byte = 8 bits ``ubyte myvar = $8f``, ``ubyte c = 'a'`` --- boolean 1 byte = 8 bits ``byte myvar = true`` or ``byte myvar == false`` +``bool`` boolean 1 byte = 8 bits ``bool myvar = true`` or ``bool myvar == false`` The true and false are actually just aliases for the byte values 1 and 0. ``word`` signed word 2 bytes = 16 bits ``word myvar = -12345`` @@ -312,11 +312,13 @@ type identifier type storage size example var declara ``word[x]`` signed word array 2*x bytes ``word[4] myvar`` ``uword[x]`` unsigned word array 2*x bytes ``uword[4] myvar`` ``float[x]`` floating-point array 5*x bytes ``float[4] myvar`` +``bool[x]`` boolean array 5*x bytes ``bool[4] myvar`` note: consider using bit flags in a byte or word instead to save space ``byte[]`` signed byte array depends on value ``byte[] myvar = [1, 2, 3, 4]`` ``ubyte[]`` unsigned byte array depends on value ``ubyte[] myvar = [1, 2, 3, 4]`` ``word[]`` signed word array depends on value ``word[] myvar = [1, 2, 3, 4]`` ``uword[]`` unsigned word array depends on value ``uword[] myvar = [1, 2, 3, 4]`` ``float[]`` floating-point array depends on value ``float[] myvar = [1.1, 2.2, 3.3, 4.4]`` +``bool[]`` boolean array depends on value ``bool[] myvar = [true, false, true]`` note: consider using bit flags in a byte or word instead to save space ``str[]`` array with string ptrs 2*x bytes + strs ``str[] names = ["ally", "pete"]`` ``str`` string (petscii) varies ``str myvar = "hello."`` implicitly terminated by a 0-byte @@ -488,6 +490,11 @@ logical: ``not`` ``and`` ``or`` ``xor`` (which in reality is just a byte value of 1 or 0). Notice that the expression ``not x`` is equivalent to ``x==0``, and the compiler will treat it as such. + .. note:: + You can use regular integers directly in logical expressions but these have to be converted to + the boolean value 0 or 1 every time, resulting in larger and slower code. Consider using + the ``bool`` variable type instead, where this conversion doesn't need to occur. + .. note:: Unlike most other programming languages, there is no short-cirquit or McCarthy-evaluation for the logical ``and`` and ``or`` operators. This means that prog8 currently always evaluates diff --git a/docs/source/todo.rst b/docs/source/todo.rst index dc942e1c0..648361f95 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,13 @@ TODO For next release ^^^^^^^^^^^^^^^^ +- add compiler error when using boolean with operators <,<=,>,>= (== and != are ok!) +- add compiler error when using boolean on lhs of operators -,*,/,% (+ is ok!) +- add compiler error when using boolean on rhs operators /,% +- test various scenarios of using bool type vs byte actually produces tighter code + make unit test of them? + + ... diff --git a/examples/test.p8 b/examples/test.p8 index c85067ad3..2594bfa48 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -3,59 +3,25 @@ main { - sub value(word input) -> word { - return input-999 - } - - sub boolfunc(bool data) -> bool { - return not data - } + bool boolvalue1 = true + bool boolvalue2 = false + ubyte ubvalue1 = true + ubyte ubvalue2 = false sub start() { + if ubvalue1<44 or ubvalue1>99 + txt.print("0\n") - ubyte ubb + if boolvalue1 or boolvalue2 + txt.print("1\n") - if ubb and 10 - ubb++ + if boolvalue1 and boolvalue2 + txt.print("2\n") - - bool b1 = 1 - bool b2 = 0 - bool b3 = true - bool b4 = false - bool bb5 = -99 - bool bb6 = 123 - - txt.print_ub(b1) - txt.spc() - txt.print_ub(b2) - txt.spc() - txt.print_ub(b3) - txt.spc() - txt.print_ub(b4) - txt.spc() - txt.print_b(bb5) - txt.spc() - txt.print_b(bb6) - txt.nl() - - b1 = value(99) as bool - txt.print_ub(b1) ; should be 1 - txt.spc() - txt.print_ub(value(99) as ubyte) ; should be 124 - txt.spc() - txt.print_ub(value(99) as bool) ; should be 1 - txt.nl() - - txt.print_ub(boolfunc(true)) - txt.spc() - txt.print_ub(boolfunc(false)) - txt.nl() - - ubb = ubb != 0 - ubb++ - ubb = bb6 != 0 - txt.print_ub(ubb) - txt.nl() +; if ubvalue1 or ubvalu2 +; txt.print("3\n") +; +; if ubvalue1 and ubvalu2 +; txt.print("4\n") } }