vipsi - operators
Author: kio@little-bat.de
Keywords: vipsi shell script interpreter programming

# vipsi - shell and script language

## the built-in words

### operators

#### operator precedence

vipsi evaluates expressions with proper operator hierarchy.

        2+3*4  ==  2+(3*4)

the exact listing of all operator precedences is like this:

    highest:    round brackets:     ( … )
                postfix:            ++  --  [ … ]  ( … )  .
                bit shifting:       >>  <<
                bit masking:        &  |  ^
                multiplication:     *  /  %
                addition:           +  -
                concatenation:      # ##
                comparision:        ==  !=  >=  <=  >  <
                boolean:            &&  ||
                triadic selector:   ? … : …
    lowest:     assignment:         =  +=  -=  *=  /=  %=  #=  >>=  <<=  &&=  ||=  ^=  &=  |=  ##=

assignments are right-oriented: if an expression is made up with multiple assignments, the rightmost assignment operator is evaluated first.

#### automatic type casting

operators which only take one type of argument try their best, to convert any type of argument to the required type. in addition, if a list is supplied to an operator which takes no lists, then the operation is applied to all items in the list.

    	123 + "456"     =   579                 numeric addition
    	123 # "456"     =   "123456"            text concatenation
    	123 + {4,5,6}   =   {127,128,129}       numeric addition, one argument is a list
    	{1,2,3}+{4,5,6} =   {5,7,9}             numeric addition, both arguments are lists
    	123 ## {4,5,6}  =   {123,4,5,6}         list concatenation

automatic type casting is not performed for comparison operators. there are ambiguities which can't be resolved. if values of different data type are compared then an error is generated. in addition, comparing NaNs (not-a-numer, e.g. the result of 0.0/0.0) is handled in accordance to IEEE, which in essence means, that if a=NaN then a!=a.

boolean operators `&&` and `||` and the triadic operator `?:` are not expanded for list arguments. pruning would become very tricky. also comparison operators are not expanded for lists as the result is typically expected to be a boolean value.

#### "++" pre and post increment

    num_value = [++variable|variable++]

pre- or post-increment numeric variable.
if the target variable is a text, then it is automatically casted to a number first.
if the target variable is a list, then the operation is performed on all items. recursively.

e.g.: `var i=0; do while i<10; put i++; loop.`

#### "--" pre and post decrement

    num_value = [--variable|variable--]

pre- or post-decrement numeric variable.
if the target variable is a text, then it is automatically casted to a number first.
if the target variable is a list, then the operation is performed on all items. recursively.

e.g.: `var i=10; do while i>0; put --i; loop.`

#### "!" boolean (logical) not

    num_value = !num_value

logical negation.
if the supplied value is a text, then it is automatically evaluated as a number.

e.g.: `if (!exists boo) log "variable boo is missing",nl then.`

#### "~" bitwise not (bit toggle)

    num_value = ~num_value

bitwise negation.
bitwise operators work on 32 bits. if you need less, you must mask the result.
if the supplied value is a text, then it is automatically evaluated as a number.

e.g.: `a = a & ~$20.`

#### "@" identifier from text

    identifier = @text_value

create identifier from text expression.

e.g.: `var t="a"; var a=1; put @t;`

#### "=" assignment

    variable = value

assign value to variable.
assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

note: though the assignment operator returns a result (the new value of the variable) this result is not automagically printed in interactive shell mode. this is done because assignments do not start with a special instruction which "eats" the result.

e.g.: `var a = 47; var x,y,z; x=y=z=47;`

#### "+=" add to target

    variable += num_value

add value to variable.
assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

if any of both arguments is a text, then it is evaluated as number.
if any of both is a list, then the operation is performed on all items. recursively.

e.g.: `var a=20,b; b = a += 33; a += {1,"2",3};`

#### "-=" subtract from target

    variable -= num_value

subtract value from variable.
assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

if any of both arguments is a text, then it is evaluated as number.
if any of both is a list, then the operation is performed on all items. recursively.

e.g.: `var a=33,b; b = a -= 20; a-={1,"2",3}`

#### "*=" multiply to target

    variable *= num_value

multiply variable with value.
assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

if any of both arguments is a text, then it is evaluated as number.
if any of both is a list, then the operation is performed on all items. recursively.

e.g.: `var a=5,b; b = a *= 4; a *= {1,2,"3"}`

#### "/=" divide target

    variable /= num_value

divide variable by value.
assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

if any of both arguments is a text, then it is evaluated as number.
if any of both is a list, then the operation is performed on all items. recursively.

e.g.: `var a=20,b; b = a /= 5; a /= {"1",2,3}`

#### "%=" divide and store remainder

    variable %= num_value

divide variable by value and store remainder.
assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

if any of both arguments is a text, then it is evaluated as number.
if any of both is a list, then the operation is performed on all items. recursively.

e.g.: `var a=20,b; b = a %= 6; a %= {1,"2",3}`

#### "#=" concatenate text to target

    variable #= text

append text to text.
assignment operators are right-oriented, that means: if multiple assignments are made in an expression, they are evaluated right-to-left.

if either operand is not text, it is converted to text first.
if one of both is a list, then the operation is performed on all items. recursively.
if both arguments are lists then the operation is applied to matching items.

    vipsi> var a="abc";   a #= "de";   put a;  abcde
    vipsi> var a={1,2,3}; a #= "X" ;   put a;  { "1X", "2X", "3X" }
    vipsi> var a="X";     a #= {1,2,3} put a;  { "X1", "X2", "X3" }
    vipsi> var a={1,2,3}; a #= {4,5};  put a;  { "14", "25", "3" }

#### "##=" concatenate list to target

    variable ##= list

append list to list.
assignment operators are right-oriented, that means: if multiple assignments are made in an expression, they are evaluated right-to-left.

if any of both arguments is not a list, then it is converted to a list first.

e.g.: `var a={2,4,8},b="abc"; a ##= "de"; b ##= a; a ##= {1,"2",3}`

note: operator `##=` concatenates two lists. if you wish to append a list as a single item to another list, you must put it into a list first:

    vipsi> var a={1,2,3}, b=a, i4="i4", i5={5,6}
    vipsi> a ##=  i4   = { 1, 2, 3, "i4" }
    vipsi> b ##= {i4}  = { 1, 2, 3, "i4" }
    vipsi> a ##=  i5   = { 1, 2, 3, "i4", 5, 6 }
    vipsi> b ##= {i5}  = { 1, 2, 3, "i4", { 5, 6 } }

#### ">>=" bitwise shift right target

    variable >>= num_value

shift value of numeric variable bitwise right. may yield a fractional number! right side operand may be negative but not fractional (will be forced to integer).
this works for any number of any size, even fractional numbers. the effect is that of dividing the variable by a multiple of 2, or, more techi, decrementing the floating point exponent by the given number.

assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

if any of both arguments is a text, then it is evaluated as number.
if any of both is a list, then the operation is performed on all items. recursively.

e.g.: `var a=20; a >>= 5; a >>= {"-1",2,3}`

#### "<<=" bitwise shift left target

    variable <<= num_value

shift value of numeric variable bitwise left. may yield a fractional number! right side operand may be negative but not fractional (will be forced to integer).
this works for any number of any size, even fractional numbers. the effect is that of multiplying the variable by a multiple of 2, or, more techi, incrementing the floating point exponent by the given number.

assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

if any of both arguments is a text, then it is evaluated as number.
if any of both is a list, then the operation is performed on all items. recursively.

e.g.: `var a=20; a <<= 0.05; a <<= {"-1",2,3}`

#### "&&=" boolean AND with target

    variable &&= num_value

calculate result of boolean AND and store result in variable. pruning not yet implemented. (0.8.3)
assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

if any of both arguments is a text, then it is evaluated as number.
list arguments are not allowed for boolean operators. pruning would become very tricky.

#### "||=" boolean OR with target

    variable ||= num_value

calculate result of boolean OR and store result in variable. pruning not yet implemented. (0.8.3)
assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

if any of both arguments is a text, then it is evaluated as number.
list arguments are not allowed for boolean operators. pruning would become very tricky.

#### "&=" bitwise AND with target

    variable &= num_value

calculate result of bitwise AND and store result in variable.
bitwise operators work on 32 bits. if you need less, you must mask the result.
assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

if any of both arguments is a text, then it is evaluated as number.
if any of both is a list, then the operation is performed on all items. recursively.

#### "|=" bitwise OR with target

    variable |= num_value

calculate result of bitwise OR and store result in variable.
bitwise operators work on 32 bits. if you need less, you must mask the result.
assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

if any of both arguments is a text, then it is evaluated as number.
if any of both is a list, then the operation is performed on all items. recursively.

#### "^=" bitwise EXOR with target

    variable ^= num_value

calculate result of bitwise exclusive OR and store result in variable.
bitwise operators work on 32 bits. if you need less, you must mask the result.
assignment operators are right-oriented, that means: if multiple assignments are made in one expression, they are evaluated right-to-left.

if any of both arguments is a text, then it is evaluated as number.
if any of both is a list, then the operation is performed on all items. recursively.

#### "+" add

    value = num_value + num_value

1. operator: add two numerical values.
   add two numerical values or text values, which are converted to numbers, results in a number. if one operand is a list, the result is a list, where the operation was applied to all items. if both are lists, they should match in size and the operation is applied to all matching items.

   e.g.: `4+5 = 9; ({4,5}+3) = {7,8}; ({2,3})+{4,5} = {6,8}.`

   operator priority is below multiplication. text + text may some day result in text for high precision calculations.

2. optional number sign for positive number.
   note: preceding a text with operator + converts it to a number.

   e.g. `log +123, +$affe, +"0123", +"$affe";`

#### "-" subtract

    num_value = num_value - num_value

1. operator: subtract two numerical values.
   subtract two numerical values or text values, which are converted to numbers, results in a number. if one operand is a list, the result is a list, where the operation was applied to all items. if both are lists, they should match in size and the operation is applied to all matching items.

   e.g.: `9-5 = 4; ({4,5})-3 = {1,2}; ({4,5})-{2,3} = {2,2}.`

   operator priority is below multiplication. text - text may some day result in text for high precision calculations.

2. number sign for negative number.
   note: preceding a text with operator - converts it to a number.

   e.g. `log -123, -$affe, -"0123", -"$affe";`

#### "*" multiply

    num_value = num_value * num_value

1. operator: multiply two numerical values.
   multiply two numerical values or text values, which are converted to numbers, results in a number. if one operand is a list, the result is a list, where the operation was applied to all items. if both are lists, they should match in size and the operation is applied to all matching items.

   e.g.: `4*5 = 20; ({4,5})*3 = {12,15}; ({4,5})*{2,3} = {8,15}.`

   operator priority is above addition. text * text may some day result in text for high precision calculations.

2. wildcard in path pattern for dir().

   e.g. `var cafélogo = dir("./*.gif");`

3. flag for del statement to delete all variables in local scope.

   e.g.: `del *;`

4. flag in procedure parameter list to accept any number of arguments.

   e.g.: `proc zähle(a,*){ log "a=",a," argc=",count locals,nl };`

#### "/" divide

    num_value = num_value / num_value

operator: divide one numerical value by another.
divide two numerical values or text values, which are converted to numbers, results in a number. if one operand is a list, the result is a list, where the operation was applied to all items. if both are lists, they should match in size and the operation is applied to all matching items.

e.g.: `20/5 = 4; ({40,50})/5 = {8,10}; ({40,50})/{8,5} = {5,10}.`

operator priority is above addition. text / text may some day result in text for high precision calculations.

#### "%" remainder

    num_value = num_value % num_value

1. operator: remainder of division of two numerical values.
   divide two numerical values or text values, which are converted to numbers, results in a number. if one operand is a list, the result is a list, where the operation was applied to all items. if both are lists, they must match in size and the operation is applied to all matching items.

   e.g.: `21.5%2.5 = 1.5; ({33,21})%5 = {3,1}; ({36,27}/{5,10}) = {1,7}.`

   operator priority is above addition.
   note: text % text may some day result in text for high precision calculations.

2. flag for binary number literal.

   e.g.: `-%0101.1 = -5.5.`

#### "#" concatenate texts

    text_value = text_value # text_value

1. operator: concatenate two texts.
   concatenate two texts or numeric values, which are converted to text, results in a text. if one operand is a list, then the result is a list, where the operation was applied to all items. if both are lists, then the operation is applied to all matching items.

       vipsi> put {1,2,3} # "anton"  { "1anton", "2anton", "3anton" }
       vipsi> put "anton" # {1,2,3}  {"anton1","anton2","anton3"}
       vipsi> put "ant" # "0123.40"  "ant0123.40"
       vipsi> put "anton" # 0123.40  "anton123.4"
       vipsi> put 123.456 # 0123.40  "123.456123.4"
       vipsi> put {1,2,3} # "XYZ" ;  { "1XYZ", "2XYZ", "3XYZ" }
       vipsi> put "XYZ" # {1,2,3} ;  { "XYZ1", "XYZ2", "XYZ3" }
       vipsi> put {1,2,3} # {4,5} ;  { "14", "25", "3" }

   operator priority is between add/sub and comparisions:

           e.g.: a#b+c  =  a#(b+c)
           a#b>c  =  (a#b)>c.

2. Select stream for i/o instruction:
   open, openin, openout, close, put, get, read, write, ioctl, info, variables, words, pwd, dir and others.

   e.g.: `open #4, "hello.txt"; put#4,"hello world",nl; close#4;`

#### "##" concatenate lists

    list_value = list_value # list_value

operator: concatenate two lists.
concatenate two lists or text or numeric values which are converted to lists, results in a list:

    vipsi> ({1,2,3})##{4,5,6}  =  { 1,2,3,4,5,6 }
    vipsi> ({1,2,3})##"anton"  =  { 1,2,3,"anton" }
    vipsi> "anton" ## {1,2,3}  =  { "anton",1,2,3 }
    vipsi> "ant" ## "0123.40"  =  { "ant","0123.40" }
    vipsi> "anton" ## 0123.40  =  { "anton",123.4 }
    vipsi> 123.456 ## 0123.40  =  { 123.456,123.4 }

note: operator `##` concatenates two lists. if you wish to append a list as a single item to another list, you must put it into a list first:

    vipsi> var a={1,2,3} i4="i4", i5={5,6}
    vipsi> a ##  i4   = { 1, 2, 3, "i4" }
    vipsi> a ## {i4}  = { 1, 2, 3, "i4" }
    vipsi> a ##  i5   = { 1, 2, 3, 5, 6 }
    vipsi> a ## {i5}  = { 1, 2, 3, { 5, 6 } }

operator priority is between add/sub and comparisions:

        e.g.: a##b+c  =  a##(b+c)
        a##b>c  =  (a##b)>c.

#### > < >= <= != == compare

    number = value1 >  value2
    number = value1 <  value2
    number = value1 >= value2
    number = value1 <= value2
    number = value1 == value2
    number = value1 != value2

operator: compare two values. results in a number: 1 for true/yes or 0 for false/no.

numbers are compared numerically. NaN (not a number; e.g. the result of 0/0) is handled according to IEEE; that is, NaN's are unsortable and unequal to itself and only operator != returns true. (may be changed, because it makes things unsortable.)

text values are compared alphabetically by comparing character for character starting at index 1. the character code is compared, no semi-intelligent character mapping is applied.

lists are sorted by comparing matching items starting at index 1.

even procedures may be compared though it may not be evident how they are ordered.

comparing values of different type results in an error.

e.g.:

        if (a>55) put "a>55" then
    	if (b>"aaa") put "b>aaa" then
    	if a!=a put "a is not a number (NaN)" then

#### "<<" shift left bitwise

    num_value = num_value << num_value

operator: shift left-side numeric value bitwise left right-side times. may yield a fractional number! right-side operand may be negative but not fractional (will be forced to integer).
this works for any number of any size, even fractional numbers. the effect is that of multiplying the variable by a multiple of 2, or, more techi, incrementing the floating point exponent by the given number.

if any of both arguments is a text, then it is evaluated as number.
if any of both is a list, then the operation is performed on all items. recursively.

e.g.: `8<<2 = 32.`

#### ">>" shift right bitwise

    num_value = num_value >> num_value

operator: shift left-side numeric value bitwise right right-side times. may yield a fractional number! right-side operand may be negative but not fractional (will be forced to integer).
this works for any number of any size, even fractional numbers. the effect is that of multiplying the variable by a multiple of 2, or, more techi, incrementing the floating point exponent by the given number.

if any of both arguments is a text, then it is evaluated as number.
if any of both is a list, then the operation is performed on all items. recursively.

e.g.: `32>>2 = 8.`

#### "&&" boolean (logical) "and"

    num_value = num_value && num_value

operator: calculate result of boolean AND and store result in variable.
pruning: if the result is sufficiently determined by the left-side operand, that is, if it is 0 and therefore the result must be 0, then the right-side operand is simply skipped and not executed at all.

if any of both arguments is a text, then it is evaluated as number.
list arguments are not allowed for boolean operators. pruning would become very tricky.

e.g.: `if (a==0&&a!=0) log "this can not happen" then.`

#### "||" boolean (logical) "or"

    num_value = num_value || num_value

operator: calculate result of boolean OR and store result in variable.
pruning: if the result is sufficiently determined by the left-side operand, that is, if it is 1 and therefore the result must be 1, then the right-side operand is simply skipped and not executed at all.

if any of both arguments is a text, then it is evaluated as number.
list arguments are not allowed for boolean operators. pruning would become very tricky.

e.g.: `if (a==0||a!=0) log "this is always true" then.`

#### "&" bitwise "and"

    num_value = num_value & num_value

operator: calculate bitwise AND.
bitwise operators work on 32 bits. if you need less, you must mask the result.

if any of both arguments is a text, then it is evaluated as number.
if one operand is a list, the result is a list, where the operation was applied to all items. recursively. if both are lists, they should match in size and the operation is applied to all matching items.

e.g.: `$FE & $0F = $0E.`

#### "|" bitwise "or"

    num_value = num_value | num_value

operator: calculate bitwise OR.
bitwise operators work on 32 bits. if you need less, you must mask the result.

if any of both arguments is a text, then it is evaluated as number.
if one operand is a list, the result is a list, where the operation was applied to all items. recursively. if both are lists, they should match in size and the operation is applied to all matching items.

e.g.: `$0E | $58 = $5E.`

#### "^" bitwise "exclusive or"

    num_value = num_value ^ num_value

operator: calculate bitwise XOR (exclusive OR).
bitwise operators work on 32 bits. if you need less, you must mask the result.

if any of both arguments is a text, then it is evaluated as number.
if one operand is a list, the result is a list, where the operation was applied to all items. recursively. if both are lists, they should match in size and the operation is applied to all matching items.

e.g.: `$0E ^ $5E = $50.`

#### "." named item selector

    variable = list_value . identifier

operator: select named item in list.

e.g.: `var a={1,2,3}; var a.b=4; rename a[2]="z"; log a.z,a.b;`

#### "... ? ... : ..." select operator (triadic)

    value = num_value1 ? value2 : value3

operator: select value 2 or 3 based on value 1.
pruning: depending on value 1 either the expression 2 or expression 3 is evaluated and the other is skipped and not executed at all.

e.g.: `return a>b ? a : b;`

the selector operator may be concatenated:

    var a=2;
    var Z.Z = a==1 ? "foobar" : a==2 ? "Schnerk" : a==4 ? "schubidoo" : "titanic";

in the above example "foobar", "schubidoo" and "titanic" are skipped and not executed due to pruning.