Operators¶
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://www2.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 () # grouping {;;...} # compound x(,,...) # process argument list x{,,...} # process co-expression list [,,...] # list .F # field reference [] # subscript [,,...] # multiple subscript [:] # section [+:] # section [-:] # section Prefix Expressions not # success/failure reversal | # repeated alternation ! # element generation * # size + # numeric value - # negative . # value (dereference) / # null \ # non-null = # match and tab ? # random value ~ # cset complement @ # activation (&null transmitted) ^ # refresh .> # pattern cursor position assign Infix (some postfix Unicon 13) Expressions \ # limitation [2] @ # transmission ! # invocation >@ # send to other out-box >>@ # blocking send to other out-box <@ # receive from other out-box <<@ # blocking receive from other out-box ^ # exponentiation >@ # send to default out-box >>@ # blocking send <@ # receive <<@ # blocking receive * # multiplication / # division % # remainder ** # cset or set intersection + # addition - # subtraction ++ # cset or set union -- # cset or set difference -> # conditional pattern assignment => # immediate pattern assignment || # string concatenation ||| # list concatenation < # numeric comparison <= # numeric comparison = # numeric comparison >= # numeric comparison > # numeric comparison ~= # numeric comparison << # string comparison <<= # string comparison == # string comparison >>= # string comparison >> # string comparison ~== # string comparison === # value comparison ~=== # value comparison | # alternation || # pattern concatenation to by # step wise number generation .| # pattern alternate := # assignment <- # reversible assignment :=: # exchange <-> # reversible exchange op:= # (augmented assignments) ?? # pattern match ? # string scanning & # conjunction Low Precedence Expressions break [] # break from loop case of { # case selection : ... [default: ] } create # co-expression creation every [do ] # iterate over generated values fail # failure of procedure if then [else ] # if-then-else next # go to top of loop repeat # loop return # return from procedure suspend [do ] # suspension of procedure until [do ] # until-loop while [do ] # 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
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
-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
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).
&:= (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.
? (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.
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
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