Iconは、高水準のプログラミング言語であり、アプリケーションの開発やデータ処理、テキスト処理に適しています。Iconは、多くの人が使用するようになっており、特にUNIXやLinuxシステムでよく使用されています。
このチュートリアルでは、Iconの基本的な概念や文法について説明し、プログラムの作成方法や実行方法を学ぶことができます。また、実際に手を動かしながら学習できるように、多数のサンプルプログラムも提供しています。
このチュートリアルを完了することで、Iconを使用してプログラムを作成するために必要な基礎的な知識を身につけることができます。是非、このチュートリアルを通じてIconを学び、プログラミングの世界に足を踏み入れてみてください。
__TOC__
Iconは、文字列と構造の処理に多くの機能を持つ高水準プログラミング言語です。
Iconには、複数の結果を生成する可能性のある式、成功する結果を自動的に探す目標指向評価、文字列スキャンによる高度な概念レベルの文字列操作など、いくつかの新しい機能があります。
Iconは、高度な文字列処理と簡単なプログラミングという設計思想を重視しており、短く簡潔なプログラムを可能にします。
Iconでは、ストレージの割り当てとガベージコレクションが自動的に行われ、オブジェクトのサイズには制限がほとんどありません。
文字列、リスト、その他の構造はプログラムの実行時に作成され、プログラムを記述する際にそのサイズを知る必要はありません。
値は自動的に期待される型に変換され、たとえば入力として読み込んだ数字の文字列は、明示的な変換なしに数値計算に使用できます。
Iconには予約語を含む式ベースの構文があり、外見上、IconのプログラムはPascalやCのものに似ています。
Iconは、1977年にRalph Griswoldによって設計された高水準プログラミング言語で、SNOBOL, SL5およびALGOLに影響を受けています。
Iconは、構文的な柔軟性、文字列処理、抽象データ型など、さまざまな機能を提供しています。
Iconは、高レベルの文字列処理と、プログラミングの容易さと短く簡潔なプログラムを可能にする設計思想に重点を置いています。
Iconの主な特徴としては、以下のようなものがあります：
文字列処理 - 文字列処理：文字列を処理するための豊富な機能を備えており、パターンマッチング機能による文字列の便利な分析も可能です。
目標指向の評価 - Iconは、制御構造を用いて、評価とバックトラックを組み合わせています。
組み込むデータ構造 - 数値、文字列、リスト、文字、レコード、プロシージャ、連想検索が可能なテーブルなどのデータ構造が用意されています。
ポータビリティ Iconはポータブルな言語であり、様々なプラットフォームでコンパイルして実行することができます。
Iconは強力で汎用性の高い言語であり、様々なアプリケーションを作成することができます。初心者にも経験豊富なプログラマーにもおすすめです。
次のプログラムを、hello.icnの名前で保存してください。
hello.icn:
Iconのプログラムのエントリーポイントは main プロシージャです。
procedure main()          
   write("Hello world!");
end
コマンドラインから、次のようにタイプしてください。
$ icon hello.ico
次のように表示されます。
実行結果:
Hello world!
プロシージャの使い方を説明するために、先のプログラムを次のように分割しました。
hello2.icn:
Iconのプログラムのエントリーポイントは main プロシージャです。
procedure main()
    hello()
end
procedure hello()
    write("Hello world")
end
mainからのhelloの呼び出しが前方参照になっていることに、気がついたかもしれません。
Iconは、前方参照を処理系が自動的に解決します。
動的型付け言語のIconの変数には型はありません。
変数を予め宣言する必要は基本的にありません。
Hello world を少し書き換えてみます。
variable.icn:
procedure main()
   x := "Hello world!"           
   write(x);
   x := 42;
   write(x);
end
実行結果:
Hello world!
42
最初に、変数xには文字列"Hello world!"を代入しました。
Iconの代入は := です。
一度、xを表示した後、今度は整数42を代入しています。
何もなかったかのように、今度は42が表示されます。
このようにIconの変数には型はなく、値（＝オブジェクト）が型を持っています。
を使って宣言します。
procedure f()
   write("Begin");
   write(x);
   write("End");
end
procedure main()
   x := "Hello world!"           
   f();
end
実行結果:
Begin
End
プロシージャmainの中で変数xに代入した結果は、プロシージャfでは参照できていない事がわかります。
global x
procedure f()
   write("Begin");
   write(x);
   write("End");
end
procedure main()
   x := "Hello world!"           
   f();
end
実行結果:
Begin
Hello world!
End
今度は、プロシージャfから変数xを参照することが出来ました。
違いは、予約語globalを使って宣言したことです。
グローバル変数は、特定の用途では便利ですが以下のようなデメリットがあります。
名前の衝突: 大規模なプログラムでは、グローバル変数を使うと、同じ名前の変数が別の場所で誤って定義される可能性があります。これにより、プログラム全体でのデータの整合性が損なわれる可能性があります。
メモリ使用量: グローバル変数は、プログラム全体で共有されるため、その変数の値を保持するために必要なメモリ量が増加します。大量のグローバル変数を使用すると、プログラムの実行に必要なメモリが不足する可能性があります。
デバッグの難しさ: グローバル変数を使用すると、プログラムの動作が予測不能になる可能性があります。特に、グローバル変数がどこで設定され、どこで変更されているかを特定するのは困難です。したがって、バグの修正やテストの実行が困難になる可能性があります。
特に、偶発的にグローバル変数と同じ名前を踏んだときには、原因とかけ離れたところで障害が出るので、グローバル変数は多用すべきではありません。
Iconでは、グローバル変数と名前が重なってもローカル変数であることを宣言することが出来ます。
global x
procedure f()
   write("Begin");
   write(x);
   write("End");
end
procedure main()
   local x # ローカル変数を宣言
   x := "Hello world!"           
   f();
end
実行結果:
Begin
End
を使います。
procedure main()
   func();
   func();
   func();
   func();
end
procedure func()
   static i;
   initial i := 0;
   write("i = ", i);
   i +:= 1;
end
実行結果:
i = 0
i = 1
i = 2
i = 3
Iconの予約語( ''Reserved Words'' )は以下のとおりです。
これらは、変数や関数の名前（識別子）に使うことは出来ません。
break
by
case
create
default
do
else
end
every
fail
global
if
initial
invocable
link
local
next
not
of
procedure
record
repeat
return
static
suspend
then
to
until
while
Iconの演算子と区切子は以下のとおりです。
これらは１つ１つがトークンで、間に空白を含めることは出来ません。
!
%
%:=
&
&:=
(
)
+
++
++:=
+:
+:=
,
-
--
--:=
-:
-:=
.
/
/:=
<
<-
<->
<:=
<<
<<:=
<<=
<<=:=
<=
<=:=
>
>:=
>=
>=:=
>>
>>:=
>>=
>>=:=
?
?:=
@
@:=
[
\
]
^
^:=
{
|
||
||:=
|||
|||:=
}
~
~=
~=:=
~==
~==:=
~===
~===:=
Iconのキーワードは以下のとおりです。
Iconのリテラル( ''Literals'' )には、数値リテラル( ''Numeric literals'' )と引用符付きリテラル( ''Quotedliterals'' )の2つのカテゴリに分けられます。
数値リテラルは、さらに2つのカテゴリに分けられます：
整数リテラルには2つの形式があります。
数字リテラルは1つ以上の数字で構成されています。
数字リテラルの例
基数リテラルでは、数字の基数を指定することができます。
基数リテラルの書式
数字リテラル
数字リテラルの値は基数を指定し、2から36までの範囲でなければなりません。
数字指定
数字指定は数字と文字のシーケンスで構成され、aは10を表し、bは11を表し、zまで続きます。
数字指定内の大文字と小文字は同等です。
数字指定内の文字は基数より小さい値を表さなければなりません。
基数リテラルの例
実数リテラルには2つの形式があります：
10進数リテラルの書式
指数リテラルの書式
引用リテラルは2つのカテゴリに分けられます。
csetリテラルは、シングルクォート( ’ )で囲まれた文字列で構成されます。シングルクォートは、エスケープされていない限り、囲まれたクォート内に現れることはできません。エスケープシーケンスについては後述します。
文字列リテラルは、ダブルクォート( ” )で囲まれた文字列で構成されます。ダブルクォートは、エスケープされていない限り、囲まれたクォート内に現れることはできません。
エスケープシーケンスを使用することで、それ以外では不便または不可能な文字を文字列リテラルに含めることができます。エスケープシーケンスは、バックスラッシュに続く1つ以上の特殊な意味を持つ文字で構成されます。
エスケープシーケンスとそれに対応する文字は次の通りです。
\ddd のシーケンスは、dが8進数の0、1、...、7である文字に対応します。
バックスラッシュの後に続く文字が前述のリストに含まれていない場合、バックスラッシュは無視されます。したがって、\a は文字 "a" を表します。
Iconには次にような型があります。
ニーモニックは、Iconのリファレンスマニュアルでも広く使われている型の表現です。
整数( i : integer ) は、数値( N : numeric )の一種で、多倍長整数でありメモリがある限り大きな値を表現できます。
procedure main()
   write(2 ^ 1024)
end
179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216
整数シーケンス( integer sequence )は、型ではく演算子ですが、使い勝手は範囲型と理解して差し支えありません。
procedure main()
   s := [2, 3, 5, 7, 11]
   every i := 1 to *s do
      writes(s[i], " ")
   write()
   s := "Hello!"
   every i := 1 to *s do
      writes(s[i], " ")
   write()
end
2 3 5 7 11
H e l l o !
i := 1 to *s は、リストや文字列の要素の添字範囲を表すイディオムでChapelならば、.domainを使うところです。、*sは要素数を返す前置演算子*を適用しています。
この例は、次のように書くことが出来ます。
procedure main()
   s := [2, 3, 5, 7, 11]
   every writes(!s, " ")
   write()
   s := "Hello!"
   every writes(!s, " ")
   write()
end
前置演算子!は、リストや文字列をジェネレータ化します。
実数( r : real ) は、数値( N : numeric )の一種で、IEEE 754の倍精度浮動小数点数( float64 : 8-octet )です。
procedure main()
   every i := 305 to 310 do
      write("10.0 ^ ", i, " = ", 10.0 ^ i)
end
10.0 ^ 305 = 1e+305
10.0 ^ 306 = 1e+306
10.0 ^ 307 = 1e+307
10.0 ^ 308 = 1e+308
10.0 ^ 309 = inf.0
10.0 ^ 310 = inf.0
文字列( s : string )は、文字のシーケンスで、構造型( #any structure type )の一種です。
procedure main()
   s1 := "Hello"
   s2 := "World"
   infix(![s1, s2], !["||", "<<", "<<=", ">>", ">>=", "==", "~=="], ![s1, s2])
end
procedure infix(s1, opr, s2)
   write(image(s1), " ", opr," ", image(s2), " = ", image(opr(s1, s2)))
end
"Hello" || "Hello" = "HelloHello"
"Hello" || "World" = "HelloWorld"
"Hello" << "World" = "World"
"Hello" <<= "Hello" = "Hello"
"Hello" <<= "World" = "World"
"Hello" >>= "Hello" = "Hello"
"Hello" == "Hello" = "Hello"
"Hello" ~== "World" = "World"
"World" || "Hello" = "WorldHello"
"World" || "World" = "WorldWorld"
"World" <<= "World" = "World"
"World" >> "Hello" = "Hello"
"World" >>= "Hello" = "Hello"
"World" >>= "World" = "World"
"World" == "World" = "World"
"World" ~== "Hello" = "Hello"
条件式を引数にした関数は、条件が不成立だった場合（失敗した場合）関数そのものがなかったことにされます。
文字集合( c : cset)は、文字の集合で順序はなく、構造型( #any structure type )の一種です。
procedure main()
   digit := '0123456789'
   alphabet := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
   infix(![digit,alphabet], !["**", "++", "--", "--"], ![digit,alphabet])
   prefix("~", ![digit,alphabet])
end
procedure infix(s1, opr, s2)
   write(image(s1), " ", opr," ", image(s2), " = ", image(opr(s1, s2)))
end
procedure prefix(opr, s1)
   write(opr," ", image(s1), " = ", image(opr(s1)))
end
&digits ** &digits = &digits
&digits ** &ucase = ''
&digits ++ &digits = &digits
&digits ++ &ucase = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
&digits -- &digits = ''
&digits -- &ucase = &digits
&digits -- &digits = ''
&digits -- &ucase = &digits
&ucase ** &digits = ''
&ucase ** &ucase = &ucase
&ucase ++ &digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
&ucase ++ &ucase = &ucase
&ucase -- &digits = &ucase
&ucase -- &ucase = ''
&ucase -- &digits = &ucase
&ucase -- &ucase = ''
~ &digits = '\x00\x01\x02\x03\x04\x05\x06\x07\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz~\d\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
ファイル( f : file)
procedure main()
   fn := "/etc/hosts"
   f := open(fn) | stop("cannot open " || fn)
   write(image(f), ": type = ", type(f))
   write()
   while write(read(f))
end
file(/etc/hosts): type = file
127.0.0.1	localhost
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
Null( n : null )は、&null だけを取り得る型です。
procedure main()
   x := &null
   write(image(x), ": type = ", type(x))
 
   ary := list(3)
   x := ary[2]
   write(image(x), ": type = ", type(x))
end
&null: type = null
&null: type = null
キーワード&nullのほか、未初期化のリストの要素も値として&nullを保持しているように見えます。
手続き( p : procedeure )
procedure main()
   every func := ![add2, sub2, mul2, div2, mod2] do
      write(image(func), ": type = ", type(func), "; func(17, 7) = ", func(17, 7))
end
procedure add2(a, b)
   return a + b
end
procedure sub2(a, b)
   return a / b
end
procedure mul2(a, b)
   return a * b
end
procedure div2(a, b)
   return a / b
end
procedure mod2(a, b)
   return a % b
end
procedure add2: type = procedure; func(17, 7) = 24
procedure sub2: type = procedure; func(17, 7) = 2
procedure mul2: type = procedure; func(17, 7) = 119
procedure div2: type = procedure; func(17, 7) = 2
procedure mod2: type = procedure; func(17, 7) = 3
全ての型( x : any type)
リスト( L : list )
procedure main()
   ary := [2,3,5,7,11]
   write(inspect(ary));
   
   ary := list(10, 1)
   write(inspect(ary));
   
   ary := list(10)
   write(inspect(ary));
   
   every i := 1 to *ary do ary[i] := i
   write(inspect(ary));
   
   mat := make_matrix(4, 0.0);
   write(inspect(mat))
end
procedure inspect(arg)
   result := "[ "
   every  elt := !arg do 
      if type(elt) == "list" then
         result ||:= inspect(elt) || ", "
      else 
         result ||:= image(elt) || ", "
   return result || "]"
end
procedure make_matrix(n, x)
   L := list(n)
   every !L := list(n, x)
   return L
end
[ 2, 3, 5, 7, 11, ]
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ]
[ &null, &null, &null, &null, &null, &null, &null, &null, &null, &null, ]
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ]
[ [ 0.0, 0.0, 0.0, 0.0, ], [ 0.0, 0.0, 0.0, 0.0, ], [ 0.0, 0.0, 0.0, 0.0, ], [ 0.0, 0.0, 0.0, 0.0, ], ]
まず、mainという手続きが定義されています。これはプログラムのエントリーポイントとなる部分です。
 
最初の行では、aryという変数に整数の配列 [2, 3, 5, 7, 11] を代入しています。そして、inspect(ary)を呼び出して、aryの内容を表示しています。inspectは後述します。
次に、aryに別の値を代入し、再びinspect(ary)を呼び出してその内容を表示しています。これは、list(n, x)を使用して長さがnで、全ての要素がxであるリストを生成しています。
さらに、aryに別の値を代入し、再びinspect(ary)を呼び出してその内容を表示しています。ここでは、list(n)を使用して長さがnで、全ての要素が初期値であるリストを生成しています。
次の行では、everyループを使用して、iを1からaryの要素数まで繰り返します。ループの中で、ary[i]にiの値を代入しています。このループにより、aryの各要素がインデックスの値になるようになります。
最後に、make_matrix(n, x)を呼び出して4x4の行列（要素が全て0.0）を生成し、その内容をinspectを用いて表示しています。
次に、inspectという手続きが定義されています。
 
inspectは、引数として渡されたオブジェクトを文字列形式で表示するための手続きです。
この手続きでは、resultという変数を初期化し、"[ "という文字列を代入しています。そして、everyループを使用してargの要素に順番にアクセスします。
ループ内では、各要素eltの型がlistであるかどうかをチェックしています。もしeltがリストであれば、再帰的にinspect(elt)を呼び出し、結果の文字列をresultに追加します。
それ以外の場合、eltの値を文字列に変換してresultに追加します。image(elt)はオブジェクトを文字列に変換する組み込み関数です。
最後に、resultに"]"を追加して、結果の文字列を返します。
最後に、make_matrixという手続きが定義されています。
 
make_matrixは、指定されたサイズnと初期値xで要素が全てxである行列を生成するための手続きです。
まず、長さがnのリストLを生成し、それぞれの要素を初期値xで初期化します。
この手続きでは、everyループを使用して、Lの各要素に対してlist(n, x)を適用し、要素をリストに置き換えています。
最後に、生成された行列Lを返します。
集合( S : set)
procedure main()
   ary := [2,3,5,7,11]
   every writes(image(!set(ary)) || ", ");
   write()
   
   ary := list(10, 1)
   every writes(image(!set(ary)) || ", ");
   write()
   
   ary := list(10)
   every writes(image(!set(ary)) || ", ");
   write()
   
   every i := 1 to *ary do ary[i] := i
   every writes(image(!set(ary)) || ", ");
   write()
end
5, 3, 2, 7, 11, 
1, 
&null,  
5, 10, 4, 9, 3, 8, 2, 7, 1, 6, 
[TODO: insert(), delete(), S1 ** S2, S1 ++ S2, S1 -- S2]
テーブル( T : table)は、キーと対応する値のペアからなる順序付けされない構造型( #any structure type )の一種です。
これらのペアは要素と呼ばれます。
キーは一意であり、値にアクセスするための索引として機能します。
テーブルは、データの保持、検索、更新などの操作を効率的に行うために使用されます。
Iconのテーブルは、他のプログラミング言語の連想配列やマップに似た機能を提供します。しかし、Iconのテーブルはより柔軟であり、キーと値のペアのコレクションとして表現されます。キーは任意のデータ型であることができます（整数や文字列など）。さらに、テーブル内の要素の順序は重要ではありません。
Iconのテーブルは、次のような特徴を持っています：
要素の追加と削除: テーブルに新しいキーと値のペアを追加することができます。また、キーに対応する要素を削除することもできます。
値の参照と変更: テーブル内の特定のキーに対応する値にアクセスすることができます。また、既存のキーに新しい値を割り当てることで値を変更することもできます。
検索と存在確認: テーブル内に特定のキーが存在するかどうかを確認することができます。存在する場合は、そのキーに対応する値を取得できます。
サイズの管理: テーブル内の要素の数を管理することができます。要素の追加や削除によってテーブルのサイズが変化します。
テーブルは、データのグループ化や関連データの管理、高速な検索や更新など、さまざまな場面で有用です。Iconのテーブルは、高水準の抽象化と操作の容易さを提供し、プログラミングタスクを効率的に解決するための強力なツールとなっています。
procedure main()
   tbl := table(0)
   tbl["a"] := 1
   tbl["b"] := 2
   tbl["c"] := 3
   every x := key(tbl) do
      write(image(x), ":", image(tbl[x]))
end
"c":3
"a":1
"b":2
レコード( R : recode )は、複数のフィールド（属性）から成るデータ構造です。レコードは固定サイズであり、各フィールドは名前で参照されます。Iconのレコードは、他のプログラミング言語における構造体やレコード型に類似しています。
レコードはグローバルなスコープを持ち、プログラム全体で利用可能です。レコードの宣言は手続きの内部ではなく、プログラムのトップレベルで行われます。
レコードの宣言は以下のような形式を取ります：
record レコード名(フィールド1, フィールド2, ..., フィールドn)
ここで、レコード名はレコードの識別子であり、フィールド1からフィールドnまでは各フィールドの名前です。フィールド名は識別子のルールに従う必要があります。
レコードはレコードコンストラクタ関数を使用してインスタンス化されます。レコードコンストラクタ関数はレコード名に対応し、各フィールドに値を指定して呼び出されます。
レコードのフィールドは、ドット演算子を使用して参照および代入されます。例えば、レコード名.フィールド名の形式でフィールドにアクセスできます。
Iconのレコードは静的なデータ構造であり、フィールドの値は変更可能です。また、フィールドへのランダムなアクセスやフィールドの反復処理もサポートされています。
record point(x, y)
procedure main()
   pt := point(3, 4)
   write("type(pt) = ", type(pt))
   write("pt.x = ", pt.x)
   write("pt.y = ", pt.y)
end
type(pt) = point
pt.x = 3
pt.y = 4
数( N : numeric)は、整数( integer )または実数( real )です。
Co Expression( C : co-expression )
構造型( X : any structure type )は、レコード・リスト・集合またはテーブル (R, L, S, or T)のいずれかです。
すべてのIconのソースコードは、コンパイル前に前処理( ''Preprocessing'' )を通過します。プリプロセッサディレクティブは、プリプロセッサの動作を制御し、Iconコンパイラには渡されません。
プリプロセッサディレクティブの一般的な形式は次の通りです。
$ディレクティブ 引数 # コメント
まず、定数の定義が可能です。$defineディレクティブを使用して、シンボルを定義し、それに値を与えることができます。定義されたシンボルは、コンパイル前にその値に置き換えられます。
$define START 0
$define END 12
$define STEP 3
procedure main()
   every writes(START to END by STEP, " ")
end
0 3 6 9 12 
$define は、Cの #define と似ていますが、関数型のマクロの定義・展開には対応していません。
定義した定数の定義を取り消すことが出来ます。
$undef START
また、他のファイルをプログラムに挿入することもできます。$includeディレクティブを使用すると、指定したファイルの内容がその場所に挿入されます。
$include "example.icn"
さらに、定義された定数に応じてコードの含有または排除が可能です。定数の値に基づいて、特定のコードブロックを実行するかスキップするかを決定することができます。
$ifdef name 
   … name が定義されていると実行される
$else
   … name が定義されていないと実行される
$endif
前処理で、エラーや警告に使われる行番号（とファイル名）を変更することが出来ます。
この機能は、トランスパイルによって生成された Icon のコードの診断に使われることを意図しています。
$line line-number [ filename ]
Iconは、構文は比較的小さなセットですが、式と演算子は非常に多種多様です。
様々な式
これらは全て式で、式でないものはプロシージャ定義・レコード定義や変数の宣言など少数派です。
Iconには、以下の算術演算子が用意されています。
評価は、優先度が高い演算子から行われます。
 1 + 2 * 3
は、乗算のほうが優先度が高いので
 1 + ( 2 * 3 )
の順で評価されます。
優先度が同じ演算子は、結合方向に従って評価されます。
 1 - 2 + 3
の結合は、左から右なので
 ( 1 - 2 ) + 3
の順で評価されます。
また、
 2 ^ 3 ^ 4
の場合、累乗は右から左に結合するので
 2 ^ ( 3 ^ 4 )
の順で評価されます。
関数の引数は左から右に評価されます。もし引数の評価が失敗した場合、関数は呼び出されません。
一部の関数は、与えられた引数の集合に対して複数の結果を生成する場合があります。
もし引数が複数の値を生成する場合、関数は異なる引数値で繰り返し呼び出されることがあります。
[TODO:]
演算子は、通常は式の中で使われますが、関数的に呼び出すことも出来ます。
例えば
 3 + 4
は
 "+"(3, 4)
と
 演算子を表す文字列 ( 引数リスト )
の形式になります。
procedure main()
   write("3 * 4 = ", "*"(3, 4))
   write("- 127 = ", "-"(127))
   
   a := 42
   b := 73
   prefix("-", a)
   infix(![a, b], !["+", "-", "*", "/", "%"], ![a, b])
end
procedure infix(s1, opr, s2)
   write(image(s1), " ", opr," ", image(s2), " = ", image(opr(s1, s2)))
end
procedure prefix(opr, s1)
   write(opr," ", image(s1), " = ", image(opr(s1)))
end
3 * 4 = 12
- 127 = -127
- 42 = -42
42 + 42 = 84
42 + 73 = 115
42 - 42 = 0
42 - 73 = -31
42 * 42 = 1764
42 * 73 = 3066
42 / 42 = 1
42 / 73 = 0
42 % 42 = 0
42 % 73 = 42
73 + 42 = 115
73 + 73 = 146
73 - 42 = 31
73 - 73 = 0
73 * 42 = 3066
73 * 73 = 5329
73 / 42 = 1
73 / 73 = 1
73 % 42 = 31
73 % 73 = 0
下の表は、演算子を優先度順にグループ化し、結合方向も示しました。
Iconには、以下の制御構造があります。
Iconプログラミング言語における「Goal-directed evaluation（ゴール指向の評価）」は、条件式の成功や失敗を通じて制御構造を駆動する評価メカニズムの一部です。
Iconでは、条件式は成功すると結果を生成し、失敗すると結果を生成しません。例えば、比較演算子 x > y は、xがyよりも大きい場合に成功しyの値を返します。それ以外の場合は失敗します。
また、「x > y > z」は、yの値がxとzの間にある場合に成功し、zの値を返します。
このようにIconでは論理演算ではなく、成功すると値を返し、失敗すると失敗が上位に伝搬する仕組みを基本としています。
以下は、Iconの条件式を使用した例です。
procedure max(a, b)
   if a > b then
      return a           # a > b が成功したら、こちらを実行
   else       
      return b           # a > b が失敗すると、こちらを実行
end
procedure main()
    write(max(5, 10))    # 出力: 10
    write(max(8, 3))     # 出力: 8
end
この例では、max 関数は、2つの引数 a と b のうち、大きい方の値を返します。
条件式 a > b が成功する場合は、 a を返し、失敗する場合は b を返します。
Iconのifは以下のような構文を持ちます。
if 条件式 then 式A else 式B
式Aは条件式が成功した場合に実行され、式Aは条件が失敗した場合に実行されます。
number := 6
q := 3
if number < q then
	write("数値 ", number, " は、",q ,"未満です。")
else
	write("数値 ", number, " は、",q ,"以上です。")
実行結果:
数値 6 は、3以上です。
else を含んだ例です。
number := 6
if number < 10 then
	write("数値 ", number, " は、10未満です。")
実行結果:
数値 6 は、10未満です。
[else 式B]は省略できます。
number := 6
p := 3
q := 7
if number < p then
	write("数値 ", number, " は、",p ,"未満です。")
else if number < q then
	write("数値 ", number, " は、",q ,"未満です。")
else
	write("数値 ", number, " は、",q ,"以上です。")
実行結果:
数値 6 は、7未満です。
else if で複数の条件で分岐できます。
これは最初のelseに次のifが結合した結果です。
Iconのcase of文は、複数の値に対して条件分岐を行うために使用されます。
以下は、case of文の基本的な構文です。
 
case  of {
     : 
 : 
 : 
  ...
    default : 
}
: 条件分岐の対象となる値。
: と比較されるパターン。:の左側には、がマッチした場合に評価される式が書かれます。
: がマッチした場合に評価される式。
default: どのパターンにもマッチしない場合に評価される式。必ず最後に書かれます。
以下は、具体的な例です。
 
procedure print_weekday(day)
  case day of {
    "Monday" : write("This is Monday.")
    "Tuesday" : write("This is Tuesday.")
    "Wednesday" : write("This is Wednesday.")
    "Thursday" : write("This is Thursday.")
    "Friday" : write("This is Friday.")
    "Saturday" : write("This is Saturday.")
    "Sunday" : write("This is Sunday.")
    default : write("Unknown day.")
  }
end
この例では、dayに渡された文字列が曜日の名前と一致するかどうかを調べ、一致する場合にはその曜日を表示します。
一致しない場合には、「Unknown day.」と表示します。最後の_は、どの曜日の名前にも一致しなかった場合に評価される式を指定しています。
Iconにおけるwhileは、条件が満たされている限り、繰り返し実行する制御構造です。Iconでは真偽値（Boolean）ではなく、式の成功または失敗で条件を評価します。
procedure main()
   local i
   i := 0
   while i < 10 do {
      writes(i, " ")
      i +:= 1
   }
   write()
end
0 1 2 3 4 5 6 7 8 9 
Iconのuntil文は、条件が失敗している限り繰り返し処理を繰り返し、条件が成功するとループが終了します。
procedure main()
   local i
   i := 0
   until i >= 10 do {
      writes(i, " ")
      i +:= 1
   }
   write()
end
0 1 2 3 4 5 6 7 8 9 
Iconのrepeatは、続く式を永久に繰り返します。
procedure main()
   local i
   i := 0
   repeat {
      writes(i, " ")
      i +:= 1
      if i >= 10 then break
   }
   write()
end
0 1 2 3 4 5 6 7 8 9 
この例では[if i >= 10 then break]で脱出しています。
&は二項の論理積演算子です。
a & b
a が成功した場合、bが評価され成功したならbが式の値となり、失敗したなら失敗します。
やや禅問答のようですが、比較演算子と基本的には同じです。
|は二項の論理和演算子です。
a | b
a が失敗した場合、bが評価され成功したならbが式の値となり、失敗したなら失敗します。
&と|の使用例
procedure main()
   local ors, ands, mix, mix2, mix3
   ors := (1 | 9 | 3 | "abc" )
   ands := (1 & 9 & 3 & "abc" )
   mix := (1 | 9 & 3 )
   mix2 := ((1 | 9) & 3 )
   mix3 := (1 | (9 & 3) )
   if x := ors then write(x)
   if x := ands then write(x)
   if x := mix then write(x)
   if x := mix2 then write(x)
   if x := mix3 then write(x)
   write()
   write(ors)
   write(ands)
   write(mix)
   write(mix2)
   write(mix3)
end
1
abc
3
3
1
1
abc
3
3
1
if 変数 := 式 then 関数( 変数 )のように、if では条件式で代入を行うことが可能です。
この形式は関数( 式 )と等価で、式が「失敗」すると関数は実行されません。
+これは、論理演算子式だけではなく、式一般に言え、例えばfind( pattern, string )のように string から pattern を含む文字列を返す関数でも、 pattern が見つからなければ「失敗」し、find() を引数に含む関数は実行されません。このような仕組みをゴール指向の評価と呼び、ゴール指向の評価を多用するIconでは、while/forは余り使う必要がなく、次に説明するeveryが反復の主役になります。
大概のプログラミング言語で反復構造の解説のトリを務めるのは、forあるいはforeachですが、Iconにはforもforeachもなく、everyを使います。と言ってもeveryは単なる制御構造ではなく、パターンマッチングに基づくより高度な制御を実現することができます。
前述の while until や repeat の例と等価なループをeveryを書いてみましょう。
procedure main()
   local i
   every i := 0 to 9 do
      writes(i, " ")
   write()
end
0 1 2 3 4 5 6 7 8 9
0 to 9 は、to-by式を使ったジェネレータです。
everyはジェネレータから値を1つずつ取り出して変数iに代入し、その値を画面に出力するという操作を行います。
さらに、このようにも書けます。
procedure main()
   every writes(0 to 9, " ")
   write()
end
他の他のプログラミング言語には余り見ることが出来ない構文ですが、これで writes() が 0 to 9 の回数だけ繰り返されます。
to-by式は、0 to 9 by 2 のように省略可能なby 整数を使い増分を指定できます（省略された場合は 1 が仮定されます）。
procedure main()
   every writes(0 to 9 by 2, " ")
   write()
   every writes(9 to 0 by -1, " ")
   write()
end
0 2 4 6 8 
9 8 7 6 5 4 3 2 1 0 
breakは、repeatで示したとおりループを中断して脱出します。
また、ループも式であり値を持ちますが、break に値を渡すとループの式の値になります。
procedure main()
   local i, x
   i := 0
   x := repeat {
      writes(i, " ")
      i +:= 1
      if i >= 10 then break 123
   }
   write()
   write("x = ", image(x))
   
   x := 111
   i := 0
   x := while i < 10 do {
      writes(i, " ")
      i +:= 1
      if i >= 100 then break 99
   }
   write()
   write("x = ", image(x))
end
0 1 2 3 4 5 6 7 8 9 
x = 123
0 1 2 3 4 5 6 7 8 9 
x = 111
nextは、ループ内で使用される制御文の1つです。nextは、現在のループイテレーションをスキップして、次のイテレーションに進むために使用されます。
nextは通常、条件式を使用して特定の条件が満たされた場合にループイテレーションをスキップするために使用されます。
procedure main()
   every i := 1 to 10 do {
      if i % 2 = 0 then
         next
      writes(i, " ")
   }
end
1 3 5 7 9 
上記の例では、1から10までの数値を反復処理するループがあります。if i % 2 = 0 then nextの行によって、数値が偶数の場合にはnextが実行され、ループイテレーションがスキップされます。したがって、奇数の数値のみが出力されます。
nextは、while、until、repeat、everyなど、さまざまなループ構造で使用できます。また、
ネストされたループ内での使用も可能です。
return は、手続きの戻り値を返すことと、手続きを終了し呼び出し元の手続きに変えるという2つの機能があります。
procedure fib0(n) 
   if n < 2 then 
      return n 
   else
      return fib(n - 1) + fib(n - 2);
end
procedure fib1(n) 
   if n < 2 then 
      return n 
   return fib(n - 1) + fib(n - 2);
end
procedure fib2(n)
   local result
   
   if n < 2 then 
      result := n 
   else
      result := fib(n - 1) + fib(n - 2);
   return result
end
procedure fib3(n) 
   return if n < 2 then n else fib(n - 1) + fib(n - 2)
end
procedure fib(n)
   return 2 > n | fib(n - 1) + fib(n - 2)
end
procedure main()
   every writes(fib(1 to 10), " ")
end
1 1 2 3 5 8 13 21 34 55 
これはフィボナッチ数列を計算するためのいくつかの異なるプロシージャ（手続き）の実装です。フィボナッチ数列は、最初の2つの数字が0と1であり、それ以降の数は前の2つの数の和である数列です。
各プロシージャは引数として整数 n を受け取り、フィボナッチ数列の n 番目の値を返します。
以下に各プロシージャの解説を示します：
fib0 プロシージャ： このプロシージャは最も基本的な形式で、条件分岐を用いて再帰的にフィボナッチ数列を計算しています。n が2未満の場合は n を返し、それ以外の場合は n - 1 番目と n - 2 番目のフィボナッチ数列の和を返します。
fib1 プロシージャ： fib0 プロシージャと同様に、条件分岐を用いて再帰的にフィボナッチ数列を計算しています。異なる点は、if 文の else 節が省略されており、その場合には return 文だけが続くということです。
fib2 プロシージャ： このプロシージャは一時変数 result を導入しています。条件分岐によって再帰的にフィボナッチ数列を計算し、結果を result に代入します。最後に result を返します。
fib3 プロシージャ： fib1 プロシージャと同様に、条件分岐を用いて再帰的にフィボナッチ数列を計算しています。異なる点は、return 文が式として直接使用されていることです。条件式が真の場合には n を返し、偽の場合には fib(n - 1) + fib(n - 2) の結果を返します。
fib プロシージャ： このプロシージャでは、再帰呼び出しを利用してフィボナッチ数列を計算します。条件式 2 > n が真の場合は n を返し、偽の場合は fib(n - 1) + fib(n - 2) を返します。
main プロシージャ： fib プロシージャを呼び出して、1から10までのフィボナッチ数列を順番に出力しています。
これらのプロシージャは、異なる構文の使用や一時変数の導入など、同じ目的のためにいくつかの異なる方法でフィボナッチ数列を計算しています。
どのプロシージャを使用しても、正しいフィボナッチ数列の値が得られるはずです。
サスペンド( suspend )は、ジェネレータプロシージャで使います。
素数を返す関数を考えてみます。
procedure get_primes(n)
   local primes
   primes := []
   every i := 2 to n do {
      flag := 1 
      every j := 2 to *primes do {
         if i % primes[j] = 0 then {
            flag := 0
            break j
         }
       }
       if flag ~= 0 then
          put(primes, i)
   }
   return primes
end
procedure main()
   # 結果を表示します。
   every writes(!get_primes(100), " ")
   write()
end
2 3 4 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 
この関数をジェネレータに書き換えてみます。
procedure gen_primes()
   local primes
   primes := []
   every i := seq(2) do {
      flag := 1 
      every j := 2 to *primes do {
         if i % primes[j] = 0 then {
            flag := 0
            break j
         }
       }
       if flag ~= 0 then {
          put(primes, i)
          suspend i
       }
   }
end
procedure main()
   # 結果を表示します。
   every writes(gen_primes() \ 100, " ")
   write()
end
2 3 4 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 
まず「100までの素数」から「最初の100個の素数」に変えたので結果は異なります。
every i := seq(2) doのseq(2)が目を引きますが、2 to ∞と同じ意味で、これ自体がジェネレータです。
suspend iがここでの核心で、定義している手続きはここまで来ると return のように呼び出し元に返りますが、値を返すのではなくジェネレターを介し、返されたジェネレターはsuspend の引数を返します。
every writes(gen_primes() \ 100, " ")が定義したジェネレータの呼び出しでgen_primes() \ 100は生成を100個で打ち切っています。もし打ち切らないと無限に素数を生成してしまいます。
   every i := gen_primes() do
      if i > 100 then
         break
      else
         writes(i, " ")
   write()
とします。
Iconでは、式や関数は単一の値を返すだけでなく、呼び出されるたびに新しい値を返すことができます。これらはジェネレータと呼ばれ、Icon言語の重要な部分です。
ジェネレータは、式や関数の評価によって生成される"結果シーケンス"を返します。結果シーケンスには、式や関数によって生成できるすべての可能な値が含まれます。結果シーケンスが使い切られると、式や関数は失敗します。
Iconでは、どの手続きでも単一の値または複数の値を返すことができます。制御は、予約語 fail、returnおよびsuspendを使用して行います。これらの予約語がない手続きは、実行が手続きの終わりに到達するとキーワード&failを返します。
ジェネレータに変換するには、予約語suspendを使用します。これは、「この値を返し、再度呼び出されたときにこの点から実行を開始する」という意味で、C言語のstaticの概念とreturnの組み合わせのようなものです。ジェネレータを作成するには、whileループの中にsuspendステートメントを配置します。このように、数値のシーケンスを簡単に作成できます。
また、Iconには、オルタネーターと呼ばれる、ブール演算子のような機能もあります。これを使うと、条件分岐をシンプルに記述することができます。
Iconのオルタネータは、論理和（OR）演算子と同様に機能するもので、複数の値のリストを評価することができます。例えば、以下のように書くことができます。
if y < (x | 5) then write("y=", y)
この式は「もし y が x または 5 よりも小さければ ...」と読めますが、実際には、オルタネータは値をリストから取り出して、式を評価し、条件を満たす場合はその値を返します。この場合、最初に y < x を評価し、x が y よりも大きければ、その値を返して y の値を出力します。x が y よりも小さければ、次に y < 5 を評価します。もし y が x または 5 よりも小さければ、y の値を出力します。しかし、y が x および 5 よりも大きい場合、オルタネータは評価する値がなくなり、条件式が失敗します。その場合、if 文は失敗し、write 文は実行されません。
オルタネータは、Pythonのジェネレータ式のように、リストやシーケンスの要素を評価するための手軽な方法として使うことができます。また、オルタネータは、関数の引数を評価する前に、必ず成功するようにしてくれるため、簡潔なコードを書くことができます。例えば、以下のようなコードがあります。
write("y=", (x | 5) > y)
このコードは、先ほどの例と同じ結果を得ることができます。オルタネータを使うことで、条件を満たす値が存在する場合に限り、評価されるため、無駄な処理を避けることができます。
エラトステネスの篩を、若干 Icon らしく書いてみました。
エラトステネスの篩:
`eratosthenes` は、「エラトステネスの篩」を使用して、2 から `n` までの素数のリストを返します。
procedure eratosthenes(n)
   local sieve, i, result
   # `sieve` は、2 から `n` までのすべての素数候補を表しています。
   # 素数候補を全て暫定的に1とします。
   sieve := list(n, 1)
   every i := 2 to n do 
      if \ sieve[i] then
         every sieve[i + i to n by i] := &null
   # sieve の &null ではない要素の添字をリストで返す
   result := list()
   every i := 2 to *sieve do
      if \ sieve[i] then
         put(result, i)
   return result
end
procedure main()
   # 結果を表示します。
   every writes(!eratosthenes(100), " ")
   write()
end
実行結果:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 
最大公約数と最小公倍数を、若干 Icon らしく書いてみました。
最大公約数と最小公倍数:
gcd2は2つの整数mとnの最大公約数を計算します。
n == 0の場合はmを返し、それ以外の場合はユークリッドの互除法に基づいて最大公約数を計算します。
procedure gcd2(m, n)
   return if n == 0 then m else gcd2(n, m % n)
end
gcdは任意の長さの整数の配列の最大公約数を計算します。
procedure gcd(args)
   local result
   result := args[1]
   every result := gcd2(result, args[2 to *args])
   return result
end
lcm2は2つの整数mとnの最小公倍数を計算します。
procedure lcm2(m, n)
   return m * n / gcd2(m, n)
end
lcmは任意の長さの整数の配列の最小公倍数を計算します。
procedure lcm(args)
   local result
   result := args[1]
   every result := lcm2(result, args[2 to *args])
   return result
end
gcd2、gcd、lcm2、lcmの各関数を呼び出します。
procedure main()
   write("gcd(32, 48) => ", gcd2(32, 48))
   write("gcd(30, 72, 12) => ", gcd([30, 72, 12]));
   write("lcm2(30, 72) => ", lcm2(30, 72));
   write("lcm(30, 42, 72) => ", lcm([30, 42, 72]));
end
実行結果:
gcd2(30, 45) => 15
gcd(30, 72, 12) => 6
lcm2(30, 72) => 360
lcm(30, 42, 72) => 2520
※ 他のプログラミング言語の例では、可変長引数を使っていますが、Iconではリストを渡しました。
二分法を、若干 Icon らしく書いてみました。
二分法:
bisection は、関数 f が 0 に等しくなる実数 x を二分法で検索します。
low と high は、関数 f の値が 0 より小さい範囲の下限と上限です。
x は、現在の中点です。
fx は、関数 f の x の値です。
procedure bisection(low, high, f) 
  local x, fx
  # 中点を計算します。
  x := (low + high) / 2.0
  # f の x の値を計算します。
  fx := f(x)
  # fx が十分に小さい場合は、x を返します。
  if abs(fx) < +1.0e-10 then
    return x
  # 範囲を更新します。
  if fx < 0.0 then # fx が負の場合は、low を x に設定します。
    low := x
  else             # fx が正の場合は、high を x に設定します。
    high := x
  # 再帰的に呼び出し、関数 f が 0 に等しくなる x を見つけます。
  return bisection(low, high, f);
end
procedure format(x)
   local s, prec
   
   prec := 16
   x := real(x) | return image(x)
   s := ""
   if x < 0 then {
      s := "-"
      x := -x
   }
   x := string(integer(x * 10 ^ prec + 0.5))
   if *x <= prec then
      x := right(x, prec + 1, "0")
   return s || x[1:-prec] || "." || x[-prec:0]
end
procedure f1(x)
  return x - 1
end
  
procedure f2(x) 
  return x * x - 1 
end
procedure main()
  write(format(bisection(0, 3, f1)))
  write(format(bisection(0, 3, f2)))
end
実行結果:
0.9999999999417923 
1.0000000000291038
旧課程(-2012年度)高等学校数学B/数値計算とコンピューター#2分法の例を Icon に移植しました。
program ::= declaration
          | declaration program
declaration ::= "global" identifier-list
              | "invocable" "all"
              | "invocable" proc-list
              | "link" link-list
              | "procedure" IDENTIFIER "(" [ parameter-list ] ")" ";" [ locals ] [ "initial" expression ";" ] [ expression-sequence ] "end"
              | "record" IDENTIFIER "(" [ field-list ] ")"
identifier-list ::= IDENTIFIER
                  | IDENTIFIER "," identifier-list
proc-list ::= string-literal
            | string-literal "," proc-list
link-list ::= file-name
            | file-name "," link-list
file-name ::= IDENTIFIER
            | string-literal
parameter-list ::= identifier-list
                 |                     IDENTIFIER "[" "]"
                 | identifier-list "," IDENTIFIER "[" "]"
locals ::= local-specification identifier-list ";"
         | local-specification identifier-list ";" locals
local-specification ::= "local"
                      | "static"
field-list ::= field-name
             | field-name "," field-list
expression-sequence ::= [ expression ]
                      | [ expression ] ";" expression-sequence
expression ::= "(" [ expression ] ")"
             | "{" expression-sequence "}"
             | [ expression-list ]
             | expression "." field-name
             | expression [ expression-list ]
             | expression [ range-specification ]
             | [ expression ] "(" expression-list ")"
             | [ expression ] "{" expression-list "}"
             | "(" expression-list ")"
             | prefix-operator expression
             | expression infix-operator expression
             | expression "to" expression [ "by" expression ]
             | "create" expression
             | "return" [ expression ]
             | "suspend" [ expression ] [ do-clause ]
             | "fail"
             | "break" [ expression ]
             | "next"
             | "case" expression "of" "{" case-list "}"
             | "if" expression "then" expression [ "else" expression ]
             | "repeat" expression
             | "while" expression [ do-clause ]
             | "until" expression [ do-clause ]
             | "every" expression [ do-clause ]
             | IDENTIFIER
             | KEYWORD
             | literal
expression-list ::= expression
                  | expression "," expression-list
range-specification ::= expression ":" expression
                      | expression "+:" expression
                      | expression "–:" expression
do-clause ::= "do" expression
case-list ::= case-clause
            | case-clause ";" case-list
case-clause ::= expression ":" expression
              | "default" ":" expression
literal ::= numeric-literal
          | quoted-literal
numeric-literal ::= integer-literal
                  | real-literal
integer-literal ::= digit-literal
                  | radix-literal
digit-literal ::= digit
                | digit digit-literal
digit ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
radix-literal ::= radix radix-specification digit-specification
radix ::= "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
        | "10" | "11" | "12" | "13" | "14" | "15" | "16" | "17" | "18" | "19"
        | "20" | "21" | "22" | "23" | "24" | "25" | "26" | "27" | "28" | "29"
        | "30" | "31" | "32" | "33" | "34" | "35" | "36"
radix-specification ::= "r" | "R"
digit-specification ::= /0-9A-Za-z/+