Oberon-2

Oberon-2 とは、プログラミング言語 Oberon を拡張し、オブジェクト指向的なコンセプトを取り入れた言語である。 1991年、チューリッヒ工科大学のニクラウス・ヴィルトと Hanspeter Mössenböck（ドイツ語版）（現リンツ大学（英語版）システムソフトウェア研究所）が開発した。Oberon-2 は Oberon の上位互換である。Oberon-2 は Object Oberon（Oberon にオブジェクト指向のコンセプトを導入した最初の試み）の再設計でもあった。 Oberon-2 は Oberon から限定されたリフレクションとインタフェースなどを持たない単一継承（型拡張）を受け継いでいるが、効果的な仮想メソッド（型束縛プロシージャ）を追加している。メソッド呼び出しは、C++のような仮想メソッドテーブルを使って実行時に確定する。 Smalltalk などの完全なオブジェクト指向言語に比べると、Oberon-2 の基本データ型はオブジェクトになっておらず、クラスもオブジェクトではなく、多くの操作がメソッドではないし、メッセージパッシングの概念もなく、ポリモーフィズムも限定的である（SmalltalkやRubyのようなダックタイピングがなく、Javaのようなインタフェースも定義できない）。オブジェクト/クラスレベルでのカプセル化もサポートしていないが、モジュールをその目的で使用することができる。 Oberon-2 のリフレクションはメタオブジェクト（英語版）を使わず、実行ファイル内に含まれる型記述子を単に読み、それが型やプロシージャを定義しているモジュールに渡される。その構造体の形式が言語レベルで渡されるなら（例えば Oberon System 3 がそうである）、ライブラリレベルでのリフレクションの実装が可能である。従って、言語コードを全く変えずにライブラリレベルでほとんど全てを実行することも可能である。実際、Oberon System 3 は言語レベルとライブラリレベルのリフレクションを多用している。 以下の Oberon-2 のコードは、単純なリストクラスを実装したものである。 MODULE ListClass; TYPE List* = POINTER TO ListNode; ListNode = RECORD value : INTEGER; next : List; END; PROCEDURE ( l : List ) Add* ( v : INTEGER ); BEGIN (* ... *) END Add; PROCEDURE ( l : List ) AddLast*( v : INTEGER ); BEGIN (* ... *) END AddLast; PROCEDURE ( l : List ) AddAt* ( i : INTEGER; v : INTEGER ); BEGIN (* ... *) END AddAt; PROCEDURE ( l : List ) Remove*; BEGIN (* ... *) END Remove; PROCEDURE ( l : List ) RemoveLast*; BEGIN (* ... *) END RemoveLast; PROCEDURE ( l : List ) RemoveAt* ( i : INTEGER ); BEGIN (* ... *) END RemoveAt; END ListClass. 型束縛プロシージャ プロシージャは、レコード型（あるいはポインタ型）に束縛することができる。オブジェクト指向的用語で言えば、インスタンスメソッドと等価である。 リードオンリーのエクスポート エクスポートされた変数やレコードのフィールドについて、参照だけにアクセスを制限することもできる。これは、visibility flag に "-" を付与することで示される。 Open arrays Oberon では正式なパラメータ型としてのみ宣言可能だったが、Oberon-2 ではポインタベースの型として宣言できる。 FOR 文 Pascal や Modula-2 にあった FOR 文は、Oberon では実装されなかった。それが、Oberon-2 で復活している。 Oberon-2 はオブジェクトの型について、動的なチェックを行う機構がいくつか用意されている。例えば、Bird オブジェクトのインスタンスとして Duck や Cuckoo があったとき、Oberon-2 では実行時にオブジェクトの実際の型に応じた処理が可能である。 従来的な方法としては、「型束縛システム」を使った方法がある。第二の方法として、WITH 文を使った方法がある。これは、変数の動的な派生型を直接チェックするものである。どちらの場合も、派生型を特定したら、プログラマはその派生型に適切な型束縛プロシージャや型束縛変数を使うことができる。以下に具体例を示す。 なお、Oberon-2 の WITH 文と Pascal や Modula-2 の WITH 文は無関係である。以下のメソッドではレコードのフィールドへのアクセスを略記しているが、Oberon や Oberon-2 では実際にはこのような記法は使わない。 型束縛 MODULE Birds; TYPE Bird* = RECORD sound* : ARRAY 10 OF CHAR; END; END Birds. (*-------------------------------------*) MODULE Ducks; IMPORT Birds; TYPE Duck* = RECORD(Birds.Bird) END; PROCEDURE makeSound*( VAR bird: Duck ); BEGIN COPY("Quack!", bird.sound); END makeSound; END Ducks. (*-------------------------------------*) MODULE Cuckoos; IMPORT Birds; TYPE Cuckoo* = RECORD(Birds.Bird) END; PROCEDURE makeSound*( VAR bird: Cuckoo ); BEGIN COPY("Cuckoo!", bird.sound); END makeSound; END Cuckoos. WITH 文 MODULE Test; IMPORT Out, Birds, Cuckooo, Ducks; TYPE SomeBird* = RECORD ( Birds.Bird ) END; VAR sb : SomeBird; VAR c  : Cuckoos.Cuckoo; VAR d  : Ducks.Duck; PROCEDURE setSound*( VAR bird : Birds.Bird ); BEGIN WITH bird : Cuckoos.Cuckoo DO bird.sound := "Cuckoo!"; | bird : Ducks.Duck DO bird.sound := "Quack!"; ELSE bird.sound := "Tweet!"; END; END setSound; PROCEDURE MakeSound* ( VAR b : Birds.Bird ); BEGIN Out.Ln; Out.String( b.sound ); Out.Ln; END MakeSound; BEGIN setSound(c); setSound(d); setSound(sb); MakeSound(c); MakeSound(d); MakeSound(sb); END Test. POINTER MODULE PointerBirds; IMPORT Out; TYPE BirdRec* = RECORD sound* : ARRAY 10 OF CHAR; END; DuckRec* = RECORD(BirdRec) END; CuckooRec* = RECORD(BirdRec) END; TYPE Bird = POINTER TO BirdRec; Cuckoo = POINTER TO CuckooRec; Duck = POINTER TO DuckRec; VAR pb : Bird; pc : Cuckoo; pd : Duck; PROCEDURE makeDuckSound*( bird : Duck ); BEGIN COPY("Quack!", bird.sound) END makeDuckSound; PROCEDURE makeCuckooSound*( bird : Cuckoo ); BEGIN COPY("Cuckoo!", bird.sound) END makeCuckooSound; PROCEDURE makeSound*( bird : Bird ); BEGIN WITH bird : Cuckoo DO makeCuckooSound(bird); | bird : Duck DO makeDuckSound(bird) ELSE COPY("Tweet!", bird.sound); END; END makeSound; BEGIN NEW(pc); NEW(pd); makeCuckooSound( pc ); makeDuckSound( pd ); Out.Ln;Out.String( pc^.sound );Out.Ln; Out.Ln;Out.String( pd^.sound );Out.Ln; makeSound( pc ); makeSound( pd ); Out.Ln;Out.String( pc^.sound );Out.Ln; Out.Ln;Out.String( pd^.sound );Out.Ln; (* -------------------------------------- *) (* Pass dynamic type to procedure *) pb := pd; makeDuckSound( pb(Duck) ); Out.Ln;Out.String( pb^.sound );Out.Ln; pb := pc; makeCuckooSound( pb(Cuckoo) ); Out.Ln;Out.String( pb^.sound );Out.Ln; (* -------------------------------------- *) makeSound(pb); Out.Ln;Out.String( pb^.sound );Out.Ln; pb := pd; makeSound(pb); Out.Ln;Out.String( pb^.sound );Out.Ln; (* -------------------------------------- *) NEW(pb); makeSound(pb); Out.Ln;Out.String( pb^.sound );Out.Ln; END PointerBirds. 第三の方法として、IS 演算子を使った方法もある。これは、等号（=）や不等号（>）などと同じ優先順位の関係演算子であるが、動的な型の検査を行う。しかし、上述の2つの方法とは異なり、検出した派生型によって処理を振り分けることはできない。 ALGOL - Pascal - Modula-2 - Oberon - Component Pascal の系列の言語は文法を単純化させてきたと言える。Oberon-2 はEBNFを使って下記のように33の構文生成規則で表せる（Mössenböck & Wirth, 1993）。 Module = MODULE ident ";" [ImportList] DeclSeq [BEGIN StatementSeq] END ident ".". ImportList = IMPORT [ident ":="] ident {"," [ident ":="] ident} ";". DeclSeq = { CONST {ConstDecl ";" } | TYPE {TypeDecl ";"} | VAR {VarDecl ";"}} {ProcDecl ";" | ForwardDecl ";"}. ConstDecl = IdentDef "=" ConstExpr. TypeDecl = IdentDef "=" Type. VarDecl = IdentList ":" Type. ProcDecl = PROCEDURE [Receiver] IdentDef [FormalPars] ";" DeclSeq [BEGIN StatementSeq] END ident. ForwardDecl = PROCEDURE "^" [Receiver] IdentDef [FormalPars]. FormalPars = "(" [FPSection {";" FPSection}] ")" [":" Qualident]. FPSection = [VAR] ident {"," ident} ":" Type. Receiver = "(" [VAR] ident ":" ident ")". Type = Qualident | ARRAY [ConstExpr {"," ConstExpr}] OF Type | RECORD ["("Qualident")"] FieldList {";" FieldList} END | POINTER TO Type | PROCEDURE [FormalPars]. FieldList = [IdentList ":" Type]. StatementSeq = Statement {";" Statement}. Statement = [ Designator ":=" Expr | Designator ["(" [ExprList] ")"] | IF Expr THEN StatementSeq {ELSIF Expr THEN StatementSeq} [ELSE StatementSeq] END | CASE Expr OF Case {"|" Case} [ELSE StatementSeq] END | WHILE Expr DO StatementSeq END | REPEAT StatementSeq UNTIL Expr | FOR ident ":=" Expr TO Expr [BY ConstExpr] DO StatementSeq END | LOOP StatementSeq END | WITH Guard DO StatementSeq {"|" Guard DO StatementSeq} [ELSE StatementSeq] END | EXIT | RETURN [Expr] ]. Case = [CaseLabels {"," CaseLabels} ":" StatementSeq]. CaseLabels = ConstExpr [".." ConstExpr]. Guard = Qualident ":" Qualident. ConstExpr = Expr. Expr = SimpleExpr [Relation SimpleExpr]. SimpleExpr = ["+" | "-"] Term {AddOp Term}. Term = Factor {MulOp Factor}. Factor = Designator ["(" [ExprList] ")"] | number | character | string | NIL | Set | "(" Expr ")" | " ~ " Factor. Set = "{" [Element {"," Element}] "}". Element = Expr [".." Expr]. Relation = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS. AddOp = "+" | "-" | OR. MulOp = " * " | "/" | DIV | MOD | "&". Designator = Qualident {"." ident | "[" ExprList "]" | "