Menu

#47 Improvements [r873]

Unstable_(example)
open
nobody
None
5
2017-04-02
2017-04-02
No

Hi Jim,

This is the list of improvements:

Error messages: Encapsulate variables names (or any symbol names) with quotes

BEFORE:

ERROR on line 1: Variable a has not been assigned a value.

AFTER: with quotes - more easy to read

ERROR on line 1: Variable 'a' has not been assigned a value.

Error messages: Fix variable name in error message

    print a

BEFORE: variable name is unknown

ERROR on line 1: Variable unknown has not been assigned a value.

AFTER: correct name

ERROR on line 1: Variable 'a' has not been assigned a value.
    a=0
    print b

BEFORE: wrong name. At line 2 it is b, not a.

ERROR on line 2: Variable a has not been assigned a value.

AFTER: correct variable name (b).

ERROR on line 2: Variable b has not been assigned a value.

Error messages: Distinguishing between a simple variable and an array’s element

    dim a(10)
    print a[1]

BEFORE:

ERROR on line 2: Variable a has not been assigned a value.

AFTER: easy to understand

ERROR on line 2: Element of array 'a' has not been assigned a value.

FIX: Correct variable name/number of array after assignment

    dim a(1)
    c=0
    d=0
    b=a
    print b[0]

BEFORE: wrong name, wrong varnum

ERROR on line 5: Variable a has not been assigned a value. (variable a!)

AFTER: perfect

ERROR on line 5: Element of array 'b' has not been assigned a value.

FIX: Errors in evaluation. Throw error for unassigned variable. Convert->getBool 10% speed up.

    if a then print 1

BEFORE: nothing
AFTER:throw error message

ERROR on line 1: Variable 'a' has not been assigned a value.

Improvement: Safe and correct increment or decrement a++, a--, ++a, --a, a[0]++, a[0]--, ++a[0], --a[0]. Speed up 340%

1. Avoid concatenation
    a="text"
    a++
    a++
    print a

BEFORE: Result - incrementation in fact made a concatenation

text11

AFTER: correct behaviour

ERROR on line 2: Unable to convert string to number.
2. Increment and decrement operations are safe (avoid integer overflow)
3. Speed up operations by 340%. Benchmark: a lot of a++:a++:a++… (100.560ms vs 29.520ms)

Times in benchmark are measured in a common BASIC256 running program, not just for routine.

Error messages: Add Unable to convert string to musical note

    sound "x",1000

BEFORE and AFTER messages:

ERROR on line 1: Unable to convert string to number.
ERROR on line 1: Unable to convert string to musical note.

FIX: Empty string is not number!

    a=""
    print isnumeric(a)
    a=a-5
    print a

BEFORE: isnumeric(a) print value 0 because an empty string is not a number, but why it is treat as 0 in arithmetic operation?

0 #empty string is indeed not a number
-5.0 #why is treat as 0?

AFTER: correct behaviour

0
ERROR on line 3: Unable to convert string to number.

Error messages: Chose the proper error code if there is no valid address for this label/function/subroutine

    call mysub("text")

BEFORE and AFTER messages

ERROR on line 1: No such label mysub.
ERROR on line 1: No such SUBROUTINE 'mysub'.
    print mysub("text")

BEFORE and AFTER messages:

ERROR on line 1: No such label mysub.
ERROR on line 1: No such FUNCTION 'mysub'.

Error messages: Store only first error from last operation

    print a/b

will print error: "ERROR on line 1: Division by zero."
but the first error is the most important to debug the problem:
AFTER:

ERROR on line 1: Variable 'b' has not been assigned a value.

FIX: Fill with unassigned variable or unassigned array’s element

    dim (a) fill b
    a fill b

BEFORE: nothing
AFTER: error message

ERROR on line 1: Variable 'b' has not been assigned a value.
    a={0,1,2,3,4}
    dim b(10)
    redim a(10) fill b[0]
    print a[9]

BEFORE and AFTER messages

ERROR on line 4: Variable a has not been assigned a value.
ERROR on line 3: Element of array 'b' has not been assigned a value.

FIX Safe negate number

    a=0x80000000
    print a
    print typeof(a)
    a=-a
    print a
    print typeof(a)

BEFORE: wrong output

-2147483648
1
-2147483648
1

AFTER: correct behaviour and output

-2147483648
1
2147483648.0
2

Improvement: Speedup 12% OP_ADD when FLOAT involved

Improvement: NEW_LINE little speed up

Improvement: Calling subs or functions speed up at least 5 TIMES FASTER. For big programs it is even faster.

Benchmark: calling an empty subroutine just like

    subroutine xxx()
    end subroutine

Improvement: Faster OP_ARRAY2STACK

    dim d(50000)
    for f=0 to 49999
        d[f]=0
    next f

    t=msec
    for f=0 to 100
        dim x=d[]
        dim x=d[]
        dim x=d[]
        dim x=d[]
        dim x=d[]
        dim x=d[]
        dim x=d[]
        dim x=d[]
        dim x=d[]
        dim x=d[]
    next f

print msec-t

BEFORE: 4270ms
AFTER: 1440ms

Improvement: Faster OP_ARRAYFILL

    for f=0 to 100
        dim a(1000000) fill 1
        dim a(1000000) fill 1
        dim a(1000000) fill 1
        dim a(1000000) fill 1
        dim a(1000000) fill 1
        dim a(1000000) fill 1
        dim a(1000000) fill 1
        dim a(1000000) fill 1
        dim a(1000000) fill 1
        dim a(1000000) fill 1
    next f
    print msec

BEFORE: 48081ms
AFTER: 19030ms

Improvement: Faster OP_ARRAYLISTASSIGN

    for f=0 to 1000
    a={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1…1,1,1,1}
    next f
    print msec

BEFORE: 6800ms
AFTER: 3560ms

FIX: Case insensitive for variables names

BASIC programming language it is in fact a case insensitive language.
A kid should benefit from this advantage.
iscussion BASIC-256 Users

Improvement: Lazy evaluation or Short-circuit evaluation

In programming language theory, lazy evaluation is an evaluation strategy which delays the evaluation of an expression until its value is needed or skip it.
Performance increases by avoiding needless calculations, and error conditions in evaluating compound expressions.
Read more: https://en.wikipedia.org/wiki/Short-circuit_evaluation

Implementation:
The old method requests both expr to be evaluated before AND to take action.

expr1 AND expr2
    PUSH result expr1
    PUSH result expr2
    OP_AND              

Lazy evaluation will evaluate first expr1 and if it is FALSE then will be no reason to evaluate expr2 because the final result will be FALSE anyway.
Lazy evaluation for AND is like this:

    PUSH result expr2
    OP_LAZYIFFALSE jmp_address
    PUSH result expr1
    OP_AND              
    jmp_address:

OP_LAZYIFFALSE will pop the bool result from stack of the first expression evaluated (expr1).
If it is false, then push false value on stack and jump after AND statement (final result is false regardless the value of expr2).
If first expression evaluated is true only then it will evaluate the second expression (expr2).
OP_AND will expect now only the result2 on stack because we already know the result of expr1 (true). It will be redundant to push the first result back on stack.
In fact we could skip entirely the OP_AND because the result2 is in fact the result of TRUE AND expr2:
- If expr2==FALSE then "TRUE AND expr2" will be also FALSE.
- If expr2==TRUE then "TRUE AND expr2" will be also TRUE.
I chose to not skip OP_AND because we need to be sure that there is a BOOL value on stack. (In case of FALSE AND "string$" the stack will contain "string$" which is not a BOOL)

The pseudo code is:

    PUSH result expr1 (expr1 is evaluated)
    OP_LAZYIFFALSE {
        POP result1 from STACK as BOOL
        IF result1=FALSE THEN PUSH FALSE and JUMP jmp_address
        }
    PUSH result expr2 (expr2 is evaluated)
    OP_AND {
        ensure result2 from STACK is BOOL
        }
    jmp_address:
    ...

The same thing is happening with OP_OR.
The old method requests both expressions to be evaluated before OR to take action.
expr1 OR expr2

    PUSH result expr1
    PUSH result expr2
    OP_OR

Lazy evaluation will evaluate first expr1 and if it is TRUE, then there will be no reason to evaluate expr2 because the final result will be TRUE anyway.
Lazy evaluation for OR is like this:

    PUSH result expr2
    OP_LAZYIFTRUE jmp_address
    PUSH result expr1
    OP_OR             
    jmp_address:

OP_LAZYIFTRUE acts just like OP_LAZYIFFALSE, but jumps only if the first expression is true.
The pseudo code is:

    PUSH result expr1 (expr1 is evaluated)
    OP_LAZYIFTRUE {
        POP result1 from STACK as BOOL
        IF result1=TRUE THEN PUSH TRUE and JUMP jmp_address
        }
    PUSH result expr2 (expr2 is evaluated)
    OP_OR {
        ensure result2 from STACK is BOOL
        }
    jmp_address:
    ...

To check the results, I use a code from http://basic256.blogspot.ro/2010/12/rule-30.html

The results are:
Closed graphic window:
1700ms (Short-circuit evaluation)
2750ms (normal run)

Graphic window visible:
2400 ms (Short-circuit evaluation)
3400 ms (normal run)

Operators

First we must admit that no programming language is perfect to be taken as a model when it comes to order of operations. Read for example the "Neonatal C" section from https://www.bell-labs.com/usr/dmr/www/chist.html

What we agree for now is that the order of operations of BASIC256 is almost ok.
http://doc.basic256.org/doku.php?id=en:orderofoperators

% Mod operation - fix help page

First thing is to move % Mod operation into the group of [* / \ Multiplication, Division and Integer Division] because it is in fact a division. BASIC256 acts like this; it is just a mistake in help page:

%left '*' '/' B256MOD B256INTDIV

FIX: Concatenation (;)

A real issue is with the concatenation symbol ‘; ’(semicolon). It was introduced as an only concatenation operation. It means "stick those things together". The usage is very important for any user:

    a=10
    b=2
    PRINT "A (";a;") divided (/) by B (";b;") = ";a/b

This will print "A (10) divided (/) by B (2) = 5,0" Which is great. But if you try:

    a=10
    b=2
    PRINT "The sum of two numbers is = ";a+b

... the problem occur by showing this result "The sum of two numbers is = 102". This happen because concatenation symbol ; has the same precedence as + or -. In this case, instead of resolving (a+b) and after that to concatenate all segments separated by ;... it starts to do it from the first place.

%left '-' '+' B256SEMICOLON

B256SEMICOLON should have a lower precedence even lower than bitwise operations because the same thing is happening in this case:

    a=1
    b=2
    PRINT "A | B = ";a|b

Will ouput

ERROR on line 3: Unable to convert string to number.

Also its precedence should be higher than Comparison operators to allow comparison of strings resulted by concatenations:

%left '<' B256LTE '>' B256GTE '=' B256NE
%left B256BINARYOR B256AMP
%left '-' '+' B256SEMICOLON
%left '*' '/' B256MOD B256INTDIV
%nonassoc B256UMINUS B256BINARYNOT
%left '^'

Become:

%left '<' B256LTE '>' B256GTE '=' B256NE
%left B256SEMICOLON
%left B256BINARYOR B256AMP
%left '-' '+'
%left '*' '/' B256MOD B256INTDIV
%nonassoc B256UMINUS B256BINARYNOT
%left '^'

NEW: % Percentage

In mathematics, a percentage is a number or ratio expressed as a fraction of 100. It is denoted using the percent sign, "%". Example 20%, 55%... Percentage calculations are used in almost all calculations made with a pocket calculator.
It is very useful and it is in a human-readable format which is the purpose of BASIC.

Example

    vat = price_before_vat * 20%

Example

    input "How much do you weigh? (Kg) ", weigh
    print "You are  made of:"
    print "- Oxygen "; weigh * 65% ;" Kg"
    print "- Carbon "; weigh * 18.5% ;" Kg"
    print "- Hydrogen "; weigh * 9.5% ;" Kg"
    print "- Nitrogen "; weigh * 3.3% ;" Kg"
    print "- Calcium "; weigh * 1.4% ;" Kg"
    print "- Phosphorus "; weigh * 1.1% ;" Kg"
    print "- Other elements "; weigh - weigh * (65+18.5+9.5+3.3+1.4+1.1)% ;" Kg"
    print "Do you ever think about that?"

Example

    input "Sarah is buying a pair of jeans. Enter the original price: ", price
    input "The percentage discount is: ", percent
    print "How much will the discount be?"
    print "Simple: "; price; "$ * "; percent; "% = "; price * percent%; "$"

The implementation is simple, just add:

| expr B256MOD %prec B256UMINUS{
addIntOp(OP_PUSHINT, 100);
 addOp(OP_DIV);
}

FIX: NOT operator

Logical operators do not deal with basic elements (numbers/strings) but instead use true/false results obtained from comparisons. They are working with conditions that can be evaluated to a true or false value. So NOT should have a lower precedence, just after the comparison operators, without taking precedence of B256UMINUS.

Example

    print 0>10       #print 0 (false) - correct
    print not (0>10) #print 1 (true) - correct
    print not 0>10   #print 0 - wrong! 

The last example show clear the wrong precedence of NOT. It is equivalent to print not (0) > 10. The fix is simple: to remove B256UMINUS precedence.

| B256NOT expr %prec B256UMINUS { addOp(OP_NOT); }
//should be
| B256NOT expr { addOp(OP_NOT); }

Logical operators precedence (should be none)

Logical operators are mainly used to control program flow. Usually, we will find them as part of an if, a while, an until or some other control statement.
The concept of logical operators is simple. They allow a program to make a decision based on multiple conditions. Each operand is considered a condition that can be evaluated to a true or false value. Then the value of the conditions is used to determine the overall value.

    IF a=1 and b>0 THEN ...

Most common logical operators in programming languages are:

NOT p (which is an unary negation because it takes only one variable)
p AND q - both conditions are true
p OR q - at least one condition is true
p XOR q - only one condition is true
p NOR q - neither p nor q is true / both conditions are false
…

(About “NOR” - if you like it as much as I do, then it would be nice to have it into BASIC language.)

http://math.stackexchange.com/questions/128504/boolean-algebra-operation-precedence

About Boolean algebra
There are multiple conventions about syntax, signs and about precedence too in Boolean algebra.
None of those are universally accepted.
In Boolean algebra, as a way of reducing the number of necessary parentheses in complex structures, one may introduce precedence rules. However, not all authors/schools/programming languages/compilers use the same order.
This is a paradox. Because the main goal for precedence rules was reducing the number of parentheses, but now every decent book of programming advise to use parentheses.

Shifting our attention to Boolean algebra, we discover that there are no precedence and associativity rules that are universal. This is particularly true when we start adding more operators (which are nothing but short cut notations for certain combinations of the three fundamental ones). Programming languages, by their nature, HAVE to have well-defined precedence and associativity rules, but they may vary significantly from one language to another. For this reason, it is best to write Boolean expressions with a liberal dose of parentheses.
https://www.allaboutcircuits.com/technical-articles/boolean-basics/

Using precedence in logical operators increase the work required to understand the code. It also request very advanced knowledge from the user. It is hard to understand the developer's intent.

IF a=0 or b=0 and c>0 THEN ...

Let’s keep in mind that BASIC is an acronym for Beginner's All-purpose Symbolic Instruction Code.
BASIC programming language is closer to human languages and further from machine languages. In contrast, assembly languages are considered low-level because they are very close to machine languages.
At least COMODORE BASIC or (the great) ZX SPECTRUM BASIC (as other BASIC’s or other programming languages) use no precedence for logical operators but evaluate expression from left to right, as any users expect to do. It would be strange to be otherwise. Especially as BASIC is suitable for complete beginners (kids).

Logical operators are in fact functions. AND, OR, XOR, NOR are just few functions from the 16 functions that results from using of two variables as input.
http://mathworld.wolfram.com/BooleanFunction.html
https://cs.fit.edu/~wds/classes/adm/Lectures/BooleanFunctions.pdf
http://www.plantation-productions.com/Webster/www.artofasm.com/DOS/pdf/ch02.pdf

There are 2^2n functions for n binary variables.
For 2 variables, n = 2, the number of possible BOOLEAN function is 16.
This is the table for the16 functions of two binary variables x & y.

Each function has a name. But just look at those functions:

#F1 - AND
#F6 - XOR
#F7 - OR
#F8 - NOR
#F14 - NAND

Those are the logical operators/functions that we choose before right? So, why should they have precedence? See? In the very background all are the same.

Trying to adopt one precedence variant for logical operators is not a bad thing. The real question is why to do that?

Programming languages are not Boolean algebra.
Even in programming logic gates the order is the same: in the order of connection (in the order they appear), the order you chose to connect.
Play with this: http://www.neuroproductions.be/logic-lab/index.php?id=75114 . It is equivalent to:

IF a OR b AND c XOR d THEN light the bulb

If we want another order, it is logically to use parentheses for that:
http://www.neuroproductions.be/logic-lab/index.php?id=75115

IF a OR (b AND c) XOR d THEN light the bulb

In conclusion, we must not have a precedence for the logical operators as the bitwise operators have no precedence too.

The final and correct order of operations should be:

Respectfully,
Florin Oprea

Discussion


Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.