Operators

_images/unicon.png

Index Unicon

Unicon operators

Unicon is an operator rich programming language, very rich. As Unicon is also an everything is an expression language, operators can be chained to provide very concise computational phrases. With the small price of requiring developers to understand the rules of Precedence and order of operations.

Precedence chart

Updated for Unicon and reformatted slightly from

https://www.cs.arizona.edu/icon/refernce/exprlist.htm#expressions [1]

Shown in order of decreasing precedence. Items in groups (as separated by empty lines) have equal precedence (left to right). [2]

For instance: exponentiation ^ is higher than addition +, so

a + b ^ c

parses as

a + (b ^ c)

Left to right associativity means

a ++ b -- c

parses as

(a ++ b) -- c
High Precedence Expressions

    (expr)                             # grouping
    {expr_1;expr_2;...}                  # compound
    x(expr_1,expr_2,...)                 # process argument list
    x{expr_1,expr_2,...}                 # process co-expression list
    [expr_1,expr_2,...]                  # list
    expr.F                             # field reference
    expr_1[expr_2]                       # subscript
    expr_1[expr_2,expr_3,...]             # multiple subscript
    expr_1[expr_2:expr_3]                 # section
    expr_1[expr_2+:expr_3]                # section
    expr_1[expr_2-:expr_3]                # section

Prefix Expressions

    not expr                           # success/failure reversal
    | expr                             # repeated alternation
    ! expr                             # element generation
    * expr                             # size
    + expr                             # numeric value
    - expr                             # negative
    . expr                             # value (dereference)
    / expr                             # null
    \ expr                             # non-null
    = expr                             # match and tab
    ? expr                             # random value
    ~ expr                             # cset complement
    @ expr                             # activation (&null transmitted)
    ^ expr                             # refresh
    .> expr                            # pattern cursor position assign

Infix (some postfix Unicon 13) Expressions

    expr_2 \ expr_1                      # limitation [2]
    expr_1 @ expr_2                      # transmission
    expr_1 ! expr_2                      # invocation
    expr_1 >@ expr_2                     # send to other out-box
    expr_1 >>@ expr_2                    # blocking send to other out-box
    expr_1 <@ expr_2                     # receive from other out-box
    expr_1 <<@ expr_2                    # blocking receive from other out-box

    expr_1 ^ expr_2                      # exponentiation
    expr >@                            # send to default out-box
    expr >>@                           # blocking send
    expr <@                            # receive
    expr <<@                           # blocking receive

    expr_1 * expr_2                      # multiplication
    expr_1 / expr_2                      # division
    expr_1 % expr_2                      # remainder
    expr_1 ** expr_2                     # cset or set intersection

    expr_1 + expr_2                      # addition
    expr_1 - expr_2                      # subtraction
    expr_1 ++ expr_2                     # cset or set union
    expr_1 -- expr_2                     # cset or set difference
    expr_1 -> expr_2                     # conditional pattern assignment
    expr_1 => expr_2                     # immediate pattern assignment

    expr_1 || expr_2                     # string concatenation
    expr_1 ||| expr_2                    # list concatenation

    expr_1 < expr_2                      # numeric comparison
    expr_1 <= expr_2                     # numeric comparison
    expr_1 = expr_2                      # numeric comparison
    expr_1 >= expr_2                     # numeric comparison
    expr_1 > expr_2                      # numeric comparison
    expr_1 ~= expr_2                     # numeric comparison
    expr_1 << expr_2                     # string comparison
    expr_1 <<= expr_2                    # string comparison
    expr_1 == expr_2                     # string comparison
    expr_1 >>= expr_2                    # string comparison
    expr_1 >> expr_2                     # string comparison
    expr_1 ~== expr_2                    # string comparison
    expr_1 === expr_2                    # value comparison
    expr_1 ~=== expr_2                   # value comparison

    expr_1 | expr_2                      # alternation
    expr_1 || expr_2                     # pattern concatenation

    expr_1 to expr_2 by expr_3            # step wise number generation
    expr_1 .| expr_2                     # pattern alternate

    expr_1 := expr_2                     # assignment
    expr_1 <- expr_2                     # reversible assignment
    expr_1 :=: expr_2                    # exchange
    expr_1 <-> expr_2                    # reversible exchange
    expr_1 op:= expr_2                   # (augmented assignments)

    expr_1 ?? expr_2                     # pattern match

    expr_1 ? expr_2                      # string scanning

    expr_1 & expr_2                      # conjunction

Low Precedence Expressions

    break [expr]                       # break from loop
    case expr of {                     # case selection
       expr_1 : expr_2
       ...
       [default: expr_3]
       }
    create expr                        # co-expression creation
    every expr_1 [do expr_2]             # iterate over generated values
    fail                               # failure of procedure
    if expr_1 then expr_2 [else expr_3]   # if-then-else
    next                               # go to top of loop
    repeat expr                        # loop
    return expr                        # return from procedure
    suspend expr_1 [do expr_2]           # suspension of procedure
    until expr_1 [do expr_2]             # until-loop
    while expr_1 [do expr_2]             # while-loop
[1]ProIcon was a commercial Macintosh Icon extension venture by Bright Forest and Catspaw. When ProIcon was taken off the market, materials were placed in the public domain for redistribution by the Icon project. Unicon has deep roots in computing for the public good.
[2](1, 2) The generator limitation expression is a rare Unicon infix operator that is evaluated right to left. The limit needs to be known before the first result is suspended by the generator. Like all nifty Unicon expressions, the limit can actually be a generator as well.

Unary operators

! (generate elements)

Generate elements.

! x : any*

Generate elements of x. Values produced depends on type of x.

For x of

Type ! produces
integer equivalent to (1 to x) for integer x
real digits from the real number (including .)
string one character substrings of string in order
file lines/records of resource
list elements of list
record field values of record
set elements of set, in undefined order
table values of table, in undefined order
variable fields that can be used for assignment

For record and Table, use key to retrieve the key fields, which can be used directly to get at the values.

#
# generate-elements, unary !
#
link ximage

procedure main()
    write("!3")
    every write(!3)

    write("\n!\"abc\"")
    every write(!"abc")

    write("\n![1,2,3]")
    every write(![1,2,3])

    write("\n!&ucase\\3")
    every write(!&ucase\3)

    write("\n!this file\\3")
    f := open(&file, "r")
    every write(!f\3)
    close(f)

    s := "abc"
    write("\nevery !s := 3  # with s starting as ", s)
    every !s := 3
    write("s now ", s)

    L := ["abc", "def", "ghi"]
    write("\nevery !L := \"xyz\"  # with L starting as ", ximage(L))
    every !L := "xyz"
    write(ximage(L))
end

examples/generate-elements.icn

!3
1
2
3

!"abc"
a
b
c

![1,2,3]
1
2
3

!&ucase\3
A
B
C

!this file\3
##-
# Author: Brian Tiffin
# Dedicated to the public domain

every !s := 3  # with s starting as abc
s now 333

every !L := "xyz"  # with L starting as L2 := list(3)
   L2[1] := "abc"
   L2[2] := "def"
   L2[3] := "ghi"
L2 := list(3,"xyz")

Note that !"abc" produced "a", "b", "c", while !s produced s[1], s[2], s[3], which can be assigned to, as can the element references of a List (arrays) variable (or other structured) type. The generate elements operator doesn’t just generate values, it generates the references to the values if applicable to the surrounding expression and the source, x.

Note

There may be times when multi-threading changes the state of x during generation, and it will be up the programmer to account for these times.


* (size)

Size of operator. *expr Returns the size of the expr.

*x : integer

  • integer, size of the value coerced to string
  • real, size of the value coerces to string
  • string, number of characters in string
  • cset, number of characters in cset
  • structure, number of elements in list, set, table, fields of record
  • co-expression, number of activations since last reset
  • thread, messages in in-box
#
# size-operator.icn, demonstrate size operator for various expressions
#
# tectonics: unicon -s size-operator.icn -x
#
link printf

record R(f1, f2, f3)
procedure main()

format := "%-12s %-20s is %2d %s\n" 
printf(format, "integer:", "*12", *12, "as string")

printf(format, "real:", "*12.12", *12.12, "as string")

printf(format, "string:", "*\"abc\"", *"abc", "")

printf(format, "cset:", "*'aabbcd'", *'aabbcd', "")

printf(format, "list:", "*[1,2,3]", *[1,2,3], "")
printf(format, "list:", "*[: 1 to 5 :]", *[: 1 to 5 :], "")

r1 := R(1,2,3)
printf(format, "record:", "*record(f1,f2,f3)", *r1, "")

printf(format, "keyword:", "*&file", *&file, "as name: " || &file)

db := open("unicon", "o", "", "")
printf(format, "odbc:", "*db of ODBC file", *db, "")
sql(db, "select * from contacts")
printf(format, "odbc:", "*fetch() of contacts", *fetch(db), "")
close(db)

coexp := create seq()\3
printf(format, "activations:", "*coexp", *coexp, "none from seq()\\3")
@coexp
printf(format, "activations:", "*coexp", *coexp, "after one")
every 1 to 6 do @coexp
printf(format, "activations:", "*coexp", *coexp, "after seven")
end

examples/size-operator.icn

integer:     *12                  is  2 as string
real:        *12.12               is  5 as string
string:      *"abc"               is  3 
cset:        *'aabbcd'            is  4 
list:        *[1,2,3]             is  3 
list:        *[: 1 to 5 :]        is  5 
record:      *record(f1,f2,f3)    is  3 
keyword:     *&file               is 17 as name: size-operator.icn
odbc:        *db of ODBC file     is -1 
odbc:        *fetch() of contacts is  3 
activations: *coexp               is  0 none from seq()\3
activations: *coexp               is  1 after one
activations: *coexp               is  3 after seven

+ (numeric identity)

Numeric identity, does not change the value other than to convert to a required numeric type.

+x is x as a number if possible.

+”123” returns integer 123, +”abc” raises a run-time conversion error.

#
# numeric-identity.icn, demonstrate the prefix plus operator
#
procedure main()
write(+-1)
write(+--1)
write(+++---1)

# the size of -1 is 2 as in "-1", identity does not change that
write(*+-1)

# the size of -0 is 1 as in "0", identity does not change that
write(*+-0)

write("type(\"123\"): " || type("123"))
write("type(+\"123\"): " || type(+"123"))

# raises a run-time error
write(+"abc")
end

examples/numeric-identity.icn

-1
1
-1
2
1
type("123"): string
type(+"123"): integer

Run-time error 102
File numeric-identity.icn; Line 29
numeric expected
offending value: "abc"
Traceback:
   main()
   {+"abc"} from line 29 in numeric-identity.icn

- (negate)

Negate. Reverses the sign of the given operand.

-x is the signed reverse of x as a number if possible.

-“-123” returns integer 123. —1 is -1. -“abc” raises a run-time conversion error.

#
# negate-operator.icn, demonstrate the prefix minus operator
#
procedure main()
write(-"-123")
write(-+1)
write(--1)
write(---1)
end

examples/negate-operator.icn

123
-1
1
-1

/ (null test)

Null test.

/x : x or &fail

Test expr for null. Succeeds and returns x if x is null.

This doesn’t just return the value &null, but a reference to the expression if appropriate, which can be set to a value. This is very useful for optional arguments and optional default values.

/var := "abc"

The null test operator will succeed and produce a reference to var if it is &null, and the surrounding assignment can then set a new value. If var is already set, the null test will fail and the assignment operation is not attempted. That is not the only use for the null test operator, but it the most common.


\ (not null test)

Not null test.

\ x : x or &fail

Test expr for nonnull. Succeeds and returns x if x is not null.

A handy operator when dealing with possibly unset values.

L |||:= [\var]

The nonnull test will return var, unless it is &null in which case the operator fails. Only values that have been set will be appended to the List (arrays) L in the expression shown above.


. (dereference)

Dereference.

. x : x

The dereference operator. It returns the value x.

Unless required by context, Unicon will pass references to variables (so they can be set to new values). The dereference operator returns the actual value of x even if the surrounding context may prefer a reference.


= (anchored or tab match)

Tab match or anchored pattern match. Historically this unary operator has been called tab-match. Even though it is extended for use with Unicon pattern types with different semantics, and not just string scanning, it will likely always be spoken as tab-match.

=x : string | pattern

Equivalent to tab(match(s)) for string s, or anchors a pattern match when x is a pattern, =p.

#
# tabmatch-operator.icn, tab match and anchored pattern match
#
procedure main()
subject := "The quick brown fox jumped over the lazy dog."
space := ' '

subject ? {
    ="The"
    =space
    ="quick"
    =space
    ="brown"
    =space
    animal := tab(upto(space))
}
write(animal)
end

examples/tabmatch-operator.icn

fox

| (repeated alternation)

Repeated generation.

| x : x*

The repeated alternation operator. |x generates results from evaluating the expression x over and over again, in an infinite loop.


? (random element)

Random element.

Returns a random element from x.

  • Number, returns a number from 0 to n - 1.
  • String, returns a random single character subscript of s.
  • List, returns a random element from the list.
  • Table, returns a random value from the table - not the key.

Todo

fill out all the random element types.


@ (activation)

Activate co-expression. Shown here as a unary operator, but it is actually a binary operator with an optional initial transmitted value.

When used in a unary prefix mode the default value &null is supplied to the co-expression.


~ (cset complement)

cset complement.

~x returns the cset consisting of all the characters not found in x.

Unicon csets include byte values from 0 to 255, and the complement will be limited to that range.

Other Set data cannot really use a complement operator, as that would result in an unbounded infinite set.

#
# complement-operator.icn, demonstrate the cset complement operator
#
procedure main()
cs := 'ABCDEVWXYZabcdevwxyz'
write(*~cs)
write((~cs)[48+:60])
end

examples/complement-operator.icn

236
/0123456789:;<=>?@FGHIJKLMNOPQRSTU[\]^_`fghijklmnopqrstu{|}~

Binary Operators

Unicon includes a rich set of binary operators, and many can be augmented with assignment, making for a lengthy list of operator symbols.

Note

In terms of precedence rules, augmented assignment operators are equal in precedence to assignment, not the level of precedence for the operator being augmented.

For instance: a *:= b + c parses as a := a * (b + c)

The source expression: a := a * b + c parses as a := (a * b) + c

Some few binary operators have optional left hand side parameters, and those are also included in the Unary Operator list above for completeness (co-expression activation for instance).

& (conjunction)

Conjunction. x1 & x2 produces x2 if x1 succeeds.


&:= (augmented &)

Augmented conjunction. x1 &:= x2 produces x2 if x1 succeeds and assigns the result to x1. If x1 fails, no reassignment occurs.


| (alternation)

Alternation. x1 | x2 yields the result of x1 followed by x2, in that order. The alternation operator is a generator.


|| (concatenation)

Concatenation. x || y produces a new value that is the concatenation of the strings (or patterns) x and y.


||:= (augmented ||)

Augmented concatenation. x || y produces a new value that is the concatenation of the strings (or patterns) x and y, and assigns the result to x.


||| (list concatenation)

List concatenation operator.


|||:= (augmented |||)

Augmented list concatenation operator.


? (string scan)

One of the key features of Unicon. x1 ? x2 invokes the string scanner. The expression x1 is the &subject and x2 can be arbitrarily complex. See String Scanning.


?:= (augmented ?)

Augmented string scanning. The result of the scan is assigned to x1.


?? (pattern scan)

Pattern scan operator. Similar to string scanning, but expr is a Pattern not a general Unicon expression.


Operator idioms

The chaining capabilities inherent in the nature of Unicon expressions can lead to some very powerful programming techniques. Some of these techniques are common practise and become idioms, some are yet to be discovered and some few are just curiosities best left alone as they may be more confusing than separating the expression for the sake of clarity.

The order of operations Precedence rules can play a large role when building up expression chains.

For example, string scanning has a lower precedence than assignment:

result := subject ? {
    step1()
    step2()
}

In the code above, result is set to the original contents of subject, not the actual result produced by the scanning environment.

The above is equivalent to:

(result := subject) ? {
    step1()
    step2()
}

To get the scanning result, a rather odd looking order of operations control ends up as:

result := (subject ? {
    step1()
    step2()
})

The idiomatic Unicon expression uses augmented assignment:

subject ?:= {
    step1()
    step2()
}

With the downside that subject (the data referenced by the variable name used) is changed after scanning. Another option is to place the scan inside a procedure and use

suspend subject ? {
    step1()
    step2()
}

Or, get used to seeing parenthesis control around these often times rather complex operations. You will see the r := (s ? {code}) form relatively frequently in IPL listings.

Todo

more examples and clarification required.


Operator functions

Almost all Unicon operators can be used as function call expressions.

A few examples, summing a list of arguments and demonstrating the right hand side value given a condition test, along with some straight up arithmetic.

#
# opfuncs.icn, Calling operators as functions
#
# tectonics: unicon -s opfuncs.icn -x
#
link lists
procedure main()
    write("Multiply: ", "*"(3,5))
    write("Greater than (RHS): ", ">"(5,3))
    write("Less than (RHS): ", "<"(5,10))

    write("cset intersection: ", "**"('abcdefg', 'efghijk'))
    L := ['abcdefg', 'efghijk']
    write("cset union from list: ", "++"!L)


    result := 0
    L := [1,2,3,4,5,6,7,8,9]
    every result := "+"(result, !L)
    write("Sum of list: ", result)

    write("Section: ", limage("[:]"(L,3,6)))

    writes("ToBy: ")
    every writes("..."(1,9,2) || " ")
    write()
end

examples/opfuncs.icn

prompt$ unicon -s opfuncs.icn -x
Multiply: 15
Greater than (RHS): 3
Less than (RHS): 10
cset intersection: efg
cset union from list: abcdefghijk
Sum of list: 45
Section: [3,4,5]
ToBy: 1 3 5 7 9

Index | Previous: Expressions | Next: Reserved words