Expressions¶
Unicon expressions¶
Everything in Unicon is an expression. Well just about everything, the source files are actually just text until processed, for instance.
Expressions can be chained in Unicon, and the overall multiple expression expression still counts as an expression in terms of the lexical syntax.
For example: The every reserved word is syntactically defined as
every expr1 [do expr2]
That can be
every 1 to 5 do writes(".")
Or it can be
every i := 1 to 5 do write(i)
The assignment expression, inserted in the every
still counts as
for every
, from a compiler syntax point of view. This is a powerfully
concise feature of Unicon and almost all programs take advantage of this
grouped expressions within an expression paradigm.
Expression blocks, contained within braces, also count as a single expression, in terms of the Unicon parser. Some operators, such as alternation and conjunction, and other syntax elements provide even more flexibility when forming expressions.
Success and Failure¶
All Unicon expressions produce a value, or fail. This is a very powerful computational paradigm. Procedures can return a full range of values, and need not worry about reserving sentinel values for error codes or out-of-band results. For errors, or simple end-of-file status, the procedure can simply fail. When error status codes are required, variables can be set.
Procedures always produce values, unless they fail. The default value for
return when no expression is given is &null.
Falling off the end of a procedure without a return
or suspend
signals
failure.
#
# default-return-value.icn, default expression for return, is &null
#
procedure main()
write(type(subproc()))
end
# this actually returns &null, not void/nothing
procedure subproc()
return
end
examples/default-return-value.icn
prompt$ unicon -s default-return-value.icn -x
null
There is no practical use for the concept of void in Unicon. But fear not,
the foreign function interface layer loadfunc, can include
wrappers that manage the C concept of void returns and transform things to
usable Unicon values, when necessary. &null is not void. The
closest thing to void
in Unicon is failure
.
Failure propagation¶
With goal-directed evaluation, Unicon expressions always strive to produce a result for surrounding expressions. Failure will propagate outwards when no result can be produced. When an inner expression fails, a surrounding expression will attempt any possible alternates, until it either succeeds or must also fail. This propagation will continue until a bound expression marker terminates any goal-directed backtracking.
null¶
The null value, represented by &null is the default value for unset variables or omitted arguments.
The null value is treated specially inside most expressions. Many expressions will cause a runtime error when a null value is dereferenced as part of a computation, for instance.
#
# null value runtime error
#
procedure main()
a := b + c
end
The sample above abends with a runtime error when the null values of b
and
c
are used in a calculation. See &error for ways of
controlling how Unicon handles runtime errors. Runtime errors can be converted
to expression failure, allowing programmer control of abend states.
prompt$ unicon -s runtime-null.icn -x
Run-time error 102
File runtime-null.icn; Line 12
numeric expected
offending value: &null
Traceback:
main()
{&null + &null} from line 12 in runtime-null.icn
Through this document, null
, and &null
are used interchangeably, null
is the value, represented by the keyword &null.
Precedence¶
It is very much worthwhile getting used to the precedence rules with Unicon, It is not as tricky as it may first seem, but the level of complexity of some Unicon expressions will be easier to read with a sound understanding of the order of precedence rules. When in doubt, use parentheses to control the evaluation order of complex expressions.
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. |
Variable scope¶
Unicon supports variables with the following scope:
- local
- global
- static
- package
All procedures are global in scope. Methods are local to the enclosing class.
Parameters to a procedure are local to the body of the procedure. local
can be used to create localized references within a procedure hiding any
global variables. Static local variables retain their values between
invocations of the enclosing procedure.
Packages add another layer to the scoping rules of Unicon, and act as namespace containers for all procedures belonging to a package.
Todo
add more details to package scoping
Semicolon insertion¶
Unicon expressions are separated by semicolons.
i := 3 + 3; write(i);
That code is the same as
i := 3 + 3;
write(i);
Which, during unicon
translation, is the same as
i := 3 + 3
write(i)
The lexer makes life easier, by automatically inserting semicolons at the end of a line if the last token could legally end an expression, and the first token of the next line is legal at the beginning of an expression. This has implications. Although automatic semicolon insertion is usually an invisible assistive feature, you need to be careful with expressions that span lines. They need to break at a point that avoids the automatic semicolon.
#
# semicolon.icn example of semicolon insertion issues
#
procedure main()
i := 3 +
3
write(i)
i := 3
+ 3
write(i)
end
prompt$ unicon -s semicolon.icn -x
6
3
The second result is equivalent to
i := 3; +3; write(i);
Unicon will gladly accept +3
as an expression, then discard the result,
unused.
In the sample, the first evaluation, ending with +
, which is not a legal
expression ending token, avoided the automatic semicolon and set i
to 6.
i := 3+3; write(i);
Discarding results is a feature of the Unicon implementation and happens all the time. All parameters to a function or procedure are evaluated, but only the required ones are dereferenced and passed. Conjugation evaluates , and produces the value from if succeeds, but the value of itself is discarded. Side effects may happen, but the result is discarded in terms of the surrounding context.
Just a thing to be wary of when splitting expressions across lines. The Unicon lexical analyzer is very flexible and does the right thing in the vast majority of cases, but white space is not the only concern when splitting expressions across line breaks. The computer will always do what it is told, which may not always match what a human intended.
Visible semicolon insertion¶
To verify how the compiler treats multi-line expressions, the preprocessor output can always be used to see where semicolons are inserted:
prompt$ unicon -E -s semicolon.icn
#line 0 "/tmp/uni10050241"
#line 0 "semicolon.icn"
procedure main();
i := 3 +
3;
write(i);
i := 3;
+ 3;
write(i);
end
Bound Expressions¶
Along with the order of precedence, and goal directed evaluation, bounded expressions can be used to control the level of backtracking. A lot of bounded expressions are implicit. Intuitive, but nearly invisible with the automatic semicolon insertion that occurs at the end of each line (or multi line expression as explained in automatic semicolon insertion).
Bound expressions block backtracking, a complex feature (for the Unicon language designers and compiler authors). This features allows goal directed evaluation to work, without rewinding all the way back to the main entry point every time an alternative is required.
#
# backtrack-bound.icn
#
procedure main()
# not found; a keeps the value 1, alternate not used
a := (1 | 2)
if find("h", "this") == a then
write("found at ", a)
else
write("not found: a is ", a)
# found; alternative is allowed when striving for the goal
if find("h", "this") == (a := (1 | 2)) then
write("found at ", a)
else
write("not found: a is ", a)
end
The first expressions is implicitly bound at the newline between the a
assignment and the if find
, in the first code fragment. Unicon will not
backtrack to reassign a
to 2
when attempting to satisfy the find
goal. This is a good thing. In the second fragment, backtracking is not
bound as part of the a
assignment, and find
is allowed to try 2
when striving for the goal.
not found: a is 1
found at 2
Curiousities¶
The available Unicon documentation, which includes the Icon document collection, is a vast treasure hoard of gems and jewels. The Unicon mailing list is also a source of learning and shared knowledge. Bruce Rennie asked about this one:
#
# suspended conjugation
#
procedure main()
every write(subproc())
end
procedure subproc()
every (suspend 1 to 3) & 4
end
That code produces:
1
2
3
Normally conjugation (the &
operator), returns when
and succeed.
In the above instance, the parenthesized suspend actually fails when resumed after the 3 is delivered. The conjugation then fails and the 4 is never used. The write eventually fails, and the every in main fails after the to generator write side effects.
#
# suspended conjugation
#
procedure main()
every write(subproc())
end
procedure subproc()
every suspend 1 to 3 & 4
end
That code will display the 4 three times, and only the 4s, the result of the conjugation which succeeds three times given then the to.
4
4
4
The key expression here is grouped as
every suspend ((1 to 3) & 4)
1 to 3
succeeds, and conjugation produces (the 4), when
succeeds.
Unicon Co-Expressions¶
Co-expressions are another ahead of its time feature of Icon/Unicon. They are usually used in the context of generators, allowing sequence results to be generated when needed.
Co-expressions are also a key part of the multi-tasking features built into the Unicon virtual machine. See Multi-tasking for more information on this aspect of Unicon programming potentials. Multiple programs can be loaded into a single instance of a running Unicon machine and task co-expression activation can make for some very interesting possibilities.
Todo
co-expression entries
User defined control structures¶
Todo
control structure examples
Index | Previous: Data Structures | Next: Operators