ソフトウェア基礎 - sqlab.i.is.nagoya-u.ac.jphamaguti/lecture/... · •...
TRANSCRIPT
ソフトウェア基礎
実験概要:
簡単なテキスト・フォーマッタの作成を通して,ソフトウェア開発プロセスの各フェーズを理解・体験することにより,以下の能力を身につけることを目標とする.
• 現実世界を抽象化してシステムとして捉える
• 問題毎に適切な分析・設計手法とツールを選択・適用する
• テストケースを設計し,十分なテストを行う
• 必要十分な文書を作成する
実験スケジュール:
1,2日目 利用者の要求を分析し,要求仕様を作成する.3日目 モジュールの機能を考えてモジュール分割を行い,設計を行う.
4,5日目 モジュールの機能を実現するためのアルゴリズムとデータ構造を決定し,プログラミングを行う.
6,7日目 テストを行い,必要ならプログラムの修正を行う.マニュアルを作成する.
実験課題目次:
1 概略 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 解説 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3 課題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
A 成果物の書式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
B LEX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
C PostScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
最終更新 2018/3/1
著者 濱口 毅, 山本 晋一郎
1
目 次
1 概略 4
2 ソフトウェア開発プロセス 5
2.1 要求分析 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 設計 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3 コーディング . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4 テスト . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.5 運用・保守 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3 ソフトウェア開発技法 9
3.1 要求分析技法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.1.1 シンタックス . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.1.2 セマンティクス . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.2 状態遷移機械 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2.1 状態遷移機械 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2.2 状態遷移表と状態遷移図 . . . . . . . . . . . . . . . . . . . . . . . 12
3.2.3 状態遷移機械の例 . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2.4 Cによる実現 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2.5 LEXによる実現 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3 テスト技法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3.1 ブラックボックステスト . . . . . . . . . . . . . . . . . . . . . . . 16
3.3.2 ホワイトボックステスト . . . . . . . . . . . . . . . . . . . . . . . 16
3.3.3 回帰テスト . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4 実験課題 18
4.1 要求分析 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.1.1 要求の確認と実現可能性の検討 . . . . . . . . . . . . . . . . . . . 19
4.1.2 要求仕様書作成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.2 設計 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.3 コーディング . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.4 テスト . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.5 文書作成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5 レポート 21
5.1 上流工程レポート . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5.2 最終レポート . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
A 成果物の書式 23
A.1 要求仕様書の書式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
A.2 設計仕様書の書式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2
A.3 実現例 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
B LEX 33
B.1 LEXとは . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
B.2 記述方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
B.2.1 状態 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
B.2.2 パターン . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
B.2.3 パターン定義 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
B.2.4 アクション . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
B.2.5 サブルーチン部 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
B.3 記述例 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
B.4 使用方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
B.5 複数の LEX記述 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
C PostScript 40
C.1 PostScriptマクロ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
C.2 文字列 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
C.3 構造情報の付加 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
C.4 PostScript ファイルの作成 . . . . . . . . . . . . . . . . . . . . . . . . . . 42
C.5 日本語文字コード . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3
1 概略
ソフトウェア開発は一般に表 1のフェーズに分けることができる.
表 1: ソフトウェア開発フェーズと作業内容フェーズ 作業内容の概要
要求分析 利用者の要求を分析し,要求仕様書にまとめる設計 要求仕様をどのようにプログラム化するかを設計するコーディング 設計結果をソースコードに変換するテスト 実装したプログラムが利用者の要求を満足するかテストする
運用 システムを利用者サイトに移し,実用に供する保守 運用時に利用者から要求された機能変更を実施する
本実験では,簡単なテキスト・フォーマッタの作成を通して,各フェーズを理解・体験することにより.以下の能力を身につけることを目標とする.
• 現実世界を抽象化してシステムとして捉える
• 問題毎に適切な分析・設計手法とツールを選択・適用する
• テストケースを設計し,十分なテストを行う
• 必要十分な文書を作成するまた,システムを状態遷移機械として捉える手法と,状態遷移機械の仕様を実現に変
換するツール LEX[1]について学ぶ.実験は以下の段階からなる.
(1) 要求分析システムの実現に必要な技術を確認し,実現可能性の検討を行う.そして,自然言語で書かれた利用者の(不完全な)要求を分析し,システムを状態遷移機械として捉えて要求仕様を作成する.
(2) 設計システムをモジュールに分割し,各モジュールの仕様を決定する.
(3) コーディングモジュールの機能を実現するためのアルゴリズムとデータ構造を決定し,プログラミングを行う.
(4) テストテストを行い必要な場合はプログラムの修正を行う.あるいは,仕様の変更を行い,それをシステム設計以降のフェーズに反映させる.
(5) 文書作成プログラムのマニュアルを作成する.
4
2 ソフトウェア開発プロセス
ソフトウェア開発プロセスについて簡単に説明する.詳細は文献 [8, 3, 12, 14]を参照すること.ソフトウェアのライフサイクルを何らかのモデルにより抽象化したものをプロセスモデルという.ライフサイクルとは,要求に応じてソフトウェア・システムが作成されて稼働し,運用されていく中で保守が繰り返され,最終的にシステムが使用されなくなるまでの期間のことである.ライフサイクルでは表 1の各フェーズが繰り返される.ここではプロセスモデルのうち,古くから用いられている W.Royce によるウォーターフォールモデルについて説明する.図 1にウォーターフォールモデルの例を示す.このモデルでは,要求分析,設計,コー
要求分析
設計
コーディング
テスト
運用・保守
要求仕様書
設計仕様書
プログラム
テスト報告書
図 1: ウォーターフォールモデル
ディング,テスト,運用・保守のフェーズからなる.作業はフェーズの順にトップダウンに進めていく.各フェーズの終了時には,その工程の作業の成果をまとめたドキュメントやプログラムが成果物として作成される.各フェーズごとの成果物に対してレビューやテストを行い,基準を満たしていれば次のフェーズに進む.このモデルでは,仕様はシステム全体の大まかなことから順にトップダウンに決定されていく.開発の作業分担や進捗管理がしやすく,多人数による共同開発に向いている.一方で,ソフトウェア開発の最初の段階で作り込んだ誤りが最後の段階にならないと検出できないという欠点がある.この欠点を回避したプロセスモデルとしてスパイラルモデルなど,様々なモデルが提唱されている.
2.1 要求分析
ソフトウェア開発は,利用者が必要とするシステムの開発を企画し,文書化することから始まる.この段階では開発目的,主要な機能,適用効果,予算,開発期間などシステムの概要が決められる.一般に,この段階ではシステムの機能が完全に記述されているわけではなく,適用技術の情報が十分にそろっているわけでない.また,場合によっては内容に若干の矛盾を含むことがある.これは多くの場合,利用者が現状の問題点やシステムの適用範囲を正確に自覚していないためである.
5
要求分析はシステムの開発者と利用者の両者によって行われ,その結果は要求仕様書にまとめられる.要求分析では利用者によって出されたシステム要求を具体化して分析し,開発されるシステムが置かれる外部環境とその中での使われ方を明らかにし,システムの境界(システム化範囲)を確定する.要求分析フェーズでは,開発者と利用者が仕様の妥当性について確認し,合意を得る.
そのために,開発者はインタビューなどを通じて利用者の真の要求を引き出し,適用技術を提示して実現可能性を確認し,開発期間,開発費用を見積もる.要求仕様書には,システムが備えているべき特徴,機能,能力が記述される.要求分
析はシステムが何(what)を行うかを決定するフェーズであり,どうやって(how)実現するかを決定するフェーズではない.したがって,要求分析フェーズにおいて,具体的なアルゴリズムやデータ構造を詳細に決めて実現の細部を決定してはいけない.なぜなら,分析と設計を分離することにより,将来起こりうるハードウェアや実現方法の変更に対して柔軟に対応できるシステムを構築することができるからである.要求仕様は厳密に定める必要がある.あいまいな要求仕様は利用者の要求しているソ
フトウェアとは異なるものが作られる原因となる.厳密な要求仕様にするために,種々のモデル化技法を導入して,できるだけ形式化された図式を用いて要求仕様を表現することが必要である.要求仕様を記述するために多くの記法が提案されているが,事務系のシステムではデータフローダイアグラム(Data Flow Diagram: DFD)が用いられることが多い.また,オブジェクト指向方法論 [2, 13]では,DFDとオブジェクト図,状態遷移図などを併用した統一記法である UML(Unified Modeling Language)が用いられる.本実験では,対象システムを状態遷移機械として捉えるため,要求仕様の記述に状態
遷移図および状態遷移表を用いる.本実験で用いる要求仕様書の書式を付録A.1に示す.
2.2 設計
要求分析ではどのようなシステムを作るかを明確に定義した.設計フェーズでは,システムをいかにして(how)作るかを決定する.設計の要点は,規模が大きく複雑なシステムを,どのようなモジュールを組合わせて実現するかを決定することである.設計ではシステムについて全体から部分への分割を繰り返す.分割の最小単位はモジュールであり,ひとつの機能を持った独立性の高い最小の単位となる.そして,モジュールの組合わせでシステムを構成する.よって,モジュールの構成,個々のモジュールの機能,およびモジュール間の関係を決定することが主な作業である.モジュールが満たすべき多くの規範が提案されているが,以下の規範はその中でも基
本的なものである.
• 実現に関する事項が隠されていること.
• モジュール強度が強いこと.
• モジュール結合度が低いこと.実現に関する事項を隠すことを情報隠蔽という.これはプログラムの理解容易性と保
守性を向上させるために,モジュールを利用する側が知っておく必要のあるモジュールの
6
外部仕様と知る必要のない内部仕様は完全に分離すべきであるという考え方である.情報隠蔽のために以下の方針で設計を行う.
1. 各モジュールの機能,すなわち他のモジュールから呼び出されるときに必要となる外部仕様(インタフェース)をその機能の実現方式(インプリメンテーション)とは独立に決定する.
2. 各モジュールの実現方式に関する詳細な設計情報を他のモジュールから参照できないように隠しておく.
モジュール強度とはモジュール内の構成要素間の関係の強さの指標であり,モジュールの機能が一つに絞られているかの評価基準となる.以下に望ましくない順,すなわち弱い順にモジュール強度を列挙する.
1. 暗号的強度: 何の関連性もない複数の機能をモジュールにしたもの.
2. 論理的強度: 相互の関連性が希薄な複数の機能を持ち,制御により一つが選択実行されるもの.
3. 時間的強度: 機能的には相互に関連性があまりないが,実行順序に意味がある機能をまとめたもの.
4. 手順的強度: フローチャートなどで表現されたひとまとまりの手順をモジュールにしたもの.
5. 通信的強度: 共通のデータに対する関連する機能を実行順にまとめたもの
6. 情報的強度: 機能的強度を持つモジュールをいくつか集め,特定データへのアクセス機能を追加して一つにまとめたもの.
7. 機能的強度: 一つのモジュールがただ一つの機能を実現している.
例えばプログラムで用いられるすべのデータの初期設定を行うモジュールの強度は時間的強度である.モジュール結合度とは,モジュール間でやりとりされる情報の量と質に基づいたモジュー
ル間の関連性の尺度であり,モジュールの独立性の評価基準となる.結合度は低いほど望ましく,他のモジュールと無関係に,そのモジュールの内容をより容易に理解したり,そのモジュールをより安全に使うことができる.以下に望ましくない順,すなわち高い順にモジュール結合度を列挙する.
1. 内容結合: 一方のモジュールから他方のモジュールの内容にアクセスする形で情報を交換している.
2. 共通結合: 不特定多数のモジュールから共通にアクセスできるデータ領域があり,そこを介して二つのモジュールが情報を交換している.
7
3. 外部結合: 特定のモジュールだけが共通にアクセスできるデータ領域があり,そこを介して二つのモジュールが情報を交換している.
4. 制御結合: 呼び出されるモジュールの制御を決定するデータをパラメータとして受け渡す形で情報を交換している.
5. スタンプ結合: プライベートなデータ構造をパラメータとして受け渡す形で情報を交換している.
6. データ結合: スカラ型のデータをパラメータとして受け渡す形で情報を交換している.
ただし,結合度を低くするためにすべてのモジュールをデータ結合すると,多くのデータを引数として受け渡しすることになり実行効率が低下する.一方,効率を重視する場合も,特定のモジュールを介して共通のデータ領域にアクセスするようにし,なるべく外部結合を避けるべきである.設計の結果は設計仕様書にまとめられる.設計仕様書は以下の項目を含む.
• システム構成図システムを構成するサブシステム集合の静的関係
• モジュール構成図サブシステムを構成するモジュール集合の静的関係
• モジュール間関連図モジュール間の動的関係(呼び出し関係)
• モジュール外部仕様モジュールが外部から呼び出されるときのインタフェースと機能
• ファイル・データベース仕様ファイルやデータベースの論理的な構造
• ユーザインタフェース仕様画面表示形式や入力操作方法
本実験で作成するシステムは規模が小さいため,システム構成図,モジュール構成図は不要である.また,ファイル・データベース仕様,ユーザインタフェース仕様も不要である.本実験で用いる設計仕様書の書式を付録A.2に示す.
2.3 コーディング
設計仕様の各モジュールの外部仕様書を満たすソースプログラムをプログラミング言語を用いて記述する作業をコーディングと呼ぶ.成果物はソースプログラムである.コーディングにはモジュールの内部仕様,すなわちモジュールを実現するアルゴリズ
ムとデータ構造を決定する作業も含まれる.そのための手法は,構造的プログラミング
8
(structured programming),あるいは段階的詳細化と呼ばれる手法が用いられることが多い.これらに関しては文献 [11]の 1,2章の解説が詳しい.
2.4 テスト
テストとは仕様書で定めた通りにプログラムが機能するかどうかを調べ,仕様書とのくい違い(これをエラーと呼ぶ)を検出して報告することである [6].最初のテストはモジュール単位で行われる.これをモジュールテスト(単体テスト)と
呼ぶ.モジュールがその仕様書を満たしたふるまいをするか検査する.一般にモジュールは単体で動作しないため,モジュールテストを行うためにはテスト環境が必要となる.モジュールテストが完了したモジュール群に対して,プログラムテスト(統合テスト)
が行われる.ここでは,モジュールをモジュール構成図で定めるように結合したときに,インタフェースがモジュール外部仕様に合致しているかを検査する.システムが完全に統合できたら,システム全体のテストを行う必要がある.このテス
トをシステムテストと呼ぶ.システムテストでは,要求仕様書から想定した入力をもとに,プログラムを実行してみる.最後に,利用者が想定されるシステムの利用形態を反映したデータを用いて受け入れテストを行う.
2.5 運用・保守
保守の作業は大きく二つに分けることができる.ひとつは運用フェーズにおいて検出された障害に対して,システムを修正する作業である.これを残存エラー修復とよび,保守作業全体の 20%前後を占めるといわれている.残りの 80%は,システム運用後に生じた,新しい機能の追加,既存の機能の変更などの要求に応えてシステムを変更する作業である.ソフトウェアの開発コストは運用・保守も含めたライフサイクル全体で考えることが重要である.そのため,ライフサイクルの全てのフェーズで,拡張性や変更の容易さを意識して作業する必要がある.ただし,本実験では日程の関係から運用・保守のフェーズを含まない.
3 ソフトウェア開発技法
3.1 要求分析技法
要求分析とは,開発対象のシステムと外界との関係を明確に捉え,その関係をシステムの要求仕様書としてまとめる作業である.一般に,開発対象のシステムは,ハードウェアとソフトウェアから構成されるが,本実験では与えられたハードウェアを使用するため,設計対象をソフトウェアに限定する.また,外界とは,利用者,あるいは別のシステムである.要求仕様には以下の二つの側面がある.
(1) シンタックス: 入力および出力の形式
9
(2) セマンティクス: 入力と出力の間の関係
ただし,入力と出力の対応付けが比較的単純な規則に従っている場合は,(1)と (2)を無理に区別する必要はない.
3.1.1 シンタックス
以下では,シンタックス,すなわち対象システムが扱う入力と出力の形式を記述する方法を示す.入力のシンタックスを厳密に定めることにより,システムが扱う範囲を明確にすることができる.また,人間がシステムの出力を利用する場合は,かならずしも出力のシンタックスを厳密に定める必要はないが,あるシステムの出力が別のシステムの入力になる場合を考えると出力にも厳密さが要求される.システムはシンタックスで規定された入力のみを扱い,シンタックスで規定された形式のみを出力する.ただし,実際のシステムでは,扱う範囲を明確にするだけでは不十分であり,取り扱わない形式の入力に対して,入力が不適切な理由をエラーメッセージとして示すことが要求される.以下にシンタックスを記述する方法を示す.
入力および出力を列挙する入力と出力が有限の個数に限定されていて,比較的少ない種類に分類できる場合
は,それらを数えあげることができる.例えば,AとBの 2人で行われる “ じゃんけん”の勝敗を判定するシステムなどがこれの例である.この場合,入力は
{ 〈ぐー,ぐー 〉, 〈ぐー,ちょき 〉, 〈ぐー,ぱー 〉,〈ちょき,ぐー 〉, 〈ちょき,ちょき 〉, 〈ちょき,ぱー 〉,〈ぱー,ぐー 〉, 〈ぱー,ちょき 〉, 〈ぱー,ぱー 〉 }
の 9通り,出力は {Aが勝つ,Bが勝つ,引き分け }の 3通りである.
入力と出力の形式を規則で記述する入力と出力が無限にある場合,あるいは入力と出力の個数が限定されていても,
その種別が非常に多い場合は,それを数えあげることはできない(例えば,10人で行われる “じゃんけん”を考えてみよ).この場合は入力と出力の形式を何らかの規則を用いて記述する必要があり,これは一般に文法を定義することによって行われる.文法にはその表現能力によって階層が存在するが,もっとも簡単な文法として正
規文法がある.本実験で扱うテキスト・フォーマッタの入力はこの正規文法で記述可能である.文法の詳細については文献 [10]を参考にすること.また,正規文法は正規表現と 1対 1に対応するので,以下では表現が簡潔である正規表現を用いる.
3.1.2 セマンティクス
以下では,入力と出力の間の関係,すなわちシステムのセマンティクスを記述する方法を示す.セマンティクスを形式的に与えることは,一般的には容易ではない.また,形
10
式的に与えることが可能な場合も,記述が繁雑になったり,記述量が著しく増加してしまうことがある.そのため,本稿では形式性にはこだわらず理解の容易性を重視する.
入力および出力を列挙する入力と出力が有限の個数に限定されていて,比較的少ない種類に分類できる場合
は,両者の対応を直接示すことができる.例えば,2人で行われる “じゃんけん”の勝敗を判定するシステムなどがこれの例である.一般にシステムは n入力,m出力の関数と捉えることができる.よって,このシ
ステムの機能を 2入力・1出力の関数 f で表すと
f( 〈ぐー,ぐー 〉 ) = 〈引き分け 〉,f( 〈ぐー,ちょき 〉 ) = 〈Aの勝ち 〉,f( 〈ぐー,ぱー 〉 ) = 〈Bの勝ち 〉, . . .
となる.ただし,組合せの数が爆発的に増えるので,実際規模の問題に対してこの手法が使えることは少ない.
要求モデルの仕様を与える複雑な要求を正確に表現する方法として,計算モデルの仕様を与える方法があり,
各種の計算モデルが提案されている.例えば,信号処理システムを表現するために数学的なモデルが,言語処理系を表現するためには属性文法が,システムが行う操作に注目する場合は状態遷移機械やペトリネットが用いられる.
本実験で対象とするシステムは状態遷移機械として捉えるのが自然であるため,要求定義の主要な成果物は状態遷移図および状態遷移表である.
3.2 状態遷移機械
3.2.1 状態遷移機械
状態遷移機械は計算機械に関するもっとも古典的なモデルの一つである.状態遷移機械を用いてシステムを表現することは極めて自然であり, LEXに代表される過去の理論的,応用的な研究成果を用いることができるという点からも有利である.もちろん,状態遷移機械によって全ての計算ができるわけではないが,内部に変数をもつ拡張状態遷移機械を用いれば,かなり多くのシステムを自然にモデル化することができる.以下の状態遷移機械に関する記述は [10]から抜粋した.状態遷移機械は以下の 6項組によって表される.
• 状態の有限集合Q
• 入力記号の有限集合Σ
• 出力記号の有限集合Δ
11
• 状態遷移関数 δ
機械の現在の状態 p ∈ Qと入力 a ∈ Σの全ての組合せに対して,次の時点の状態q ∈ Qを δ(p, a) = qにより定める関数
• 出力関数 λ
機械の現在の状態 p ∈ Qと入力 a ∈ Σの全ての組合せに対して,次の状態に遷移する間に出力する出力記号 b ∈ Δを λ(p, a) = bにより定める関数
• 初期状態 q0
例えば,連続する 1を一つの 1にする状態遷移機械M1は,M1 = 〈Q1,Σ1,Δ1, δ1, λ1, q01〉と定義される.ただし,各項は以下のとおりである.
Q1 = {p, q},Σ1 = {0, 1},Δ1 = {0, 1}, q01 = p,
δ1(p, 0) = p, δ1(p, 1) = q, δ1(q, 0) = p, δ1(q, 1) = q,
λ1(p, 0) = 0, λ1(p, 1) = 1, λ1(q, 0) = 0, λ1(q, 1) = ε
3.2.2 状態遷移表と状態遷移図
前述の状態遷移機械M1は以下の状態遷移表を用いて表現することもできる.
入力現在の状態 0 1
⇒ p (p, 0) (q, 1)
q (p, 0) (q, ε)
ここで,⇒は初期状態を表し,δ(p, a) = q, λ(p, a) = bであるとき,現在の状態が pである行と入力 aの列の交点には,次の状態と出力の 2項組 (q, b)を記入する.前述の状態遷移機械M1は図 2の状態遷移図を用いて表現することもできる.状態遷移
qp
1 / 1
0 / 0
0 / 0 1 /ε
図 2: 状態遷移機械M1の状態遷移図
図において状態はその記号を角の丸い四角で囲って表す.また,δ(p, a) = q, λ(p, a) = b
であるとき,状態 pから状態 qへの遷移を表す矢印を引き,それに入出力を表すラベルa/bを付ける.状態遷移図は表に比べて直観的な把握が可能であるという利点を持つ.一方,状態遷移表は未定義な動作が存在しないかを確認することが容易であるため,適宜,二つの表現を使い分ける必要がある.
12
3.2.3 状態遷移機械の例
例として,JISコードのテキストファイル(ASCII文字と日本語の両者を含む)をEUC
に変換するプログラムを取り上げる.ASCIIは 7ビット (8進数で \000–\177の範囲)でアルファベットや数字などの文字を表すコードである.また,JISコードは 7ビットコード 2バイトで日本語の文字を表す.ASCII文字と区別するため,JISコードでは日本語の始まりを表す特別な 3バイトのシーケンス,ESC, ‘$’, ‘B’(\033\044\102)と,ASCIIの始まりを表すシーケンスESC, ‘(’, ‘B’(\033\050\102)を用いる.たとえば,文字列 “AB
漢字 ab”は次のように表される.
\101︸ ︷︷ ︸
‘A′
\102︸ ︷︷ ︸
‘B′
\033\044\102︸ ︷︷ ︸
日本語の始まり
\064\101︸ ︷︷ ︸
‘漢′
\073\172︸ ︷︷ ︸
‘字′
\033\050\102︸ ︷︷ ︸
ASCII の始まり
\141︸ ︷︷ ︸
‘a′
\142︸ ︷︷ ︸
‘b′
一方,EUCは特別なシーケンスを使わないかわりに,JISで用いられている 7ビットコードに 8ビット目の 1を加えた(8進数で 200を加えた)コードを用いる.EUCの文字コードは \200以上であり,ASCII 文字と区別できる.上の例の文字列をEUCで表すと以下のようになる.
\101︸ ︷︷ ︸
‘A′
\102︸ ︷︷ ︸
‘B′
\264\301︸ ︷︷ ︸
‘漢′
\273\372︸ ︷︷ ︸
‘字′
\141︸ ︷︷ ︸
‘a′
\142︸ ︷︷ ︸
‘b′
よって,この変換プログラムの機能は以下のように表現できる.
入力中にパターン\033\044\102が現れると,次にパターン\033\050\102が現れるまでのすべての文字の8ビット目を 1にする.このとき\033\044\102
と\033\050\102は除去する.
ただし,仕様を簡単にするために,\033\044\102と\033\050\102の間に改行やタブなどのコントロール文字は現れないとする.このプログラムの仕様を図 3の状態遷移図で表す.
ASCIIモード 日本語モード
japanese_esc/ε
ascii_esc/εjapanese_esc以外の入力
/そのまま出力
ascii_esc以外の入力/8ビット目を1にして出力
図 3: 変換プログラムの状態遷移図
以下では,比較のためにこのプログラムをCと LEXの両方で記述する.
3.2.4 Cによる実現
前述のプログラムをCで記述した例を図 4に示す.20行で日本語の始まりを表すシーケンスを検索し,発見した場合は,23–26行でモードを JAPANESEにしている.また,30–33
13
行はASCIIの始まりを表すシーケンスに関して同様の処理を行う.42行は文字 cの最上位ビットを 1にしながら出力する.ここで,’|’はビット単位の論理和を行う演算子である.また,関数 flush out()は途中までマッチしたが,最終的にはマッチしなかったシーケンス japanese esc[],あるいは ascii esc[]を出力する.
1 #include <stdio.h> 2 3 enum { 4 ASCII = 1, /* ASCII mode. */ 5 JAPANESE = 2 /* JAPANESE mode. */ 6 }; 7 8 char japanese_esc[] = "\033\044\102"; /* (ESC, ’$’, ’B’) JIS X 0208-1983. */ 9 char ascii_esc[] = "\033\050\102"; /* (ESC, ’(’, ’B’) ASCII. */ 10 11 void flush_out(int, int); 12 13 int main() 14 { 15 int mode = ASCII; /* Start in ASCII mode. */ 16 int index = 0; 17 int c; 18 19 while ((c = getchar()) != EOF) { 20 if (mode == ASCII && c == japanese_esc[index]) { 21 /* Search japanese_esc[] in ASCII mode. */ 22 index++; 23 if (japanese_esc[index] == ’\0’) { /* Move to JAPANESE mode. */ 24 mode = JAPANESE; 25 index = 0; 26 } 27 } else if (mode == JAPANESE && c == ascii_esc[index]) { 28 /* Search ascii_esc[] in JAPANESE mode. */ 29 index++; 30 if (ascii_esc[index] == ’\0’) { /* Move to ASCII mode. */ 31 mode = ASCII; 32 index = 0; 33 } 34 } else { 35 if (index != 0) { /* Flush out saved input. */ 36 flush_out(mode, index); 37 index = 0; 38 } 39 if (mode == ASCII) { 40 putchar(c); 41 } else { 42 putchar(0x80 | c); 43 } 44 } 45 } 46 if (index != 0) { /* Flush out saved input. */ 47 flush_out(mode, index); 48 } 49 50 return 0; 51 } 52 53 void flush_out(int mode, int index) 54 { 55 int i; 56 57 for (i = 0; i < index; i++) { 58 if (mode == ASCII) { 59 putchar(japanese_esc[i]); 60 } else { 61 putchar(ascii_esc[i]); 62 } 63 } 64 }
図 4: Cによるプログラム
3.2.5 LEXによる実現
前述のプログラムを LEXを用いて記述した例を図 5に示す.LEX は字句解析プログラムを生成するためのツールであるが,ここでは状態遷移機械を実現するツールとして用いる.ここで,
14
<XXX>yyy {
actions
}
は XXXモード (LEXでは状態をモードと呼ぶ)において,正規表現によって表されたパターン yyyが入力中に現れると,Cのブロック文で記述されたアクション actionsが実行されることを示す.
1 %{ 2 3 #include <stdio.h> 4 5 %} 6 7 JAPANESE_ESC (\033\044\102) 8 ASCII_ESC (\033\050\102) 9 10 %Start ASCII JAPANESE 11 12 %% 13 14 <ASCII>{JAPANESE_ESC} { /* Search japanese_esc[] in ASCII mode. */ 15 BEGIN(JAPANESE); /* Move to JAPANESE mode. */ 16 } 17 <ASCII>. { 18 putchar(yytext[0]); 19 } 20 21 <JAPANESE>{ASCII_ESC} { /* Search ascii_esc[] in JAPANESE mode. */ 22 BEGIN(ASCII); /* Move to ASCII mode. */ 23 } 24 <JAPANESE>. { 25 putchar(0x80 | yytext[0]); 26 } 27 28 <ASCII>\n { 29 putchar(yytext[0]); 30 } 31 32 %% 33 34 int main() 35 { 36 BEGIN(ASCII); /* Start in ASCII mode. */ 37 yylex(); 38 39 return 0; 40 }
図 5: LEXを用いた記述
一般に,アクションは副作用を持った文であり,モードの変化を起こしたり,出力を行う.純粋な状態遷移機械は状態のみによって計算状況を表すが,LEXではアクションに任意のCの文を記述することができるため,内部に変数を持つ拡張された状態遷移機械を実現していると考えることができる.図 5では,ASCIIと JAPANESEの 2つのモードがある.また,モードの遷移はマクロ
BEGIN()によって行う.例えば,BEGIN(ASCII)によってASCIIへ遷移する.JAPANESE ESC
と ASCII ESCは各々日本語とASCIIの開始を表すパターンを,“\n”は改行を “.”は改行以外の任意の文字を表すパターンである.また,配列 yytext[]にパターンにマッチした文字列が保存される.LEXに関する詳細な説明は付録Bを参照すること.
3.3 テスト技法
テストとは『エラーの発見を意図してシステムを実行すること』である [6].テストの目的はシステムの誤りを発見することであり,誤りをより多く発見できるテストが良い
15
テストである.テストケースとはテスト項目のことであり,テスト対象への入力データ(テストデータ)と予想される出力または結果からなる.思い込みを排除するため,テストケースを設計するときは,入力だけでなく予想される結果もテスト実行前に求めておくことが重要である.一般にシステムに入力可能なデータをすべて入力してテストを行うことは不可能であるため,効果的なテストケースを選択する必要がある.テスト技法は,ブラックボックステストとホワイトボックステストの二つに分けることができる.前者は,システムの内部構造に関係なく,要求仕様,あるいは他の文書から得たテストケースをもとにシステムを実行する.また,後者は,システムを構成する特定の部分を実行するために,システムの内部構造に基づいてテストケースを作成する.
3.3.1 ブラックボックステスト
一般にブラックボックステストでは,システムの入力仕様条件に着目してテストケースを設計する.ブラックボックステストの代表的な技法として同値分割と境界値分析がある.同値分割はあるデータに対して有効な値と無効な値を用いてテストケースを設計する技法である.ある一つの入力条件に対して注目し,その条件に対して有効な値の集合(以下,有効同値クラスと呼ぶ)と無効な値の集合(以下,無効同値クラスと呼ぶ)に分ける.そして,各同値クラスに対して,そのクラスを代表するテストケースを選択する.効果的な代表テストケースを選択することで有効性の高いテストを行うことができる.境界値分析はあるデータが範囲を持つとき,その境界値をもとにテストケースを設計する技法である.範囲内のデータから境界値と境界値から少しずれた値を選択する.同値分割で各同値クラスが範囲を持つ場合には境界値分析を用いて代表テストケースを選択する.テキストフォーマッタでは入力テキスト中の長い行に対して改行を行うが,例えばこ
の改行処理が 1回だけ起きるという条件に着目するなら,改行が 1回だけ起きるような入力の集合(有効同値クラス)とそれ以外の場合の入力の集合(無効同値クラス)に分けることができる.1行の文字数をn以下に整形して出力するという仕様の場合,改行が1回だけ起きるのは入力テキストの 1行の文字数が n+ 1から 2nの場合である.つまり,有効同値クラスは入力テキストの 1行の文字数が n + 1以上 2n以下の場合であり,それ以外は無効同値クラスである.各同値クラスは範囲を持つため,境界値分析によりそれぞれのクラスについて代表テストケースとして境界値と境界値から少しずれた値の文字数の行を選択することになる.行の長さについては,複数回改行処理が起きる場合と起きない場合,モジュールが扱
うことのできる最長の長さより短い場合と長い場合,などの条件についてもテストケースを設計する必要がある.
3.3.2 ホワイトボックステスト
ホワイトボックステストでは,システムの内部構造に基づいてテストケースを設計する.一般に,プログラム実行時には,最初に実行されるステートメントから最後に実行されるステートメントまでの実行経路は膨大な数がある.テストにおいてすべての実行経
16
路を実行することが理想であるが,ある程度の規模のシステムでは不可能である.また,やみくもに多くのテストケースを用意しても,必ずしも効果的ではない.そこで,実際にはすべての経路をテストする代わりに簡素化されたテスト網羅基準が用いられる.ホワイトボックステストでよく用いられるテスト網羅基準には以下のものがある.
全命令網羅 すべてのステートメントを 1回以上実行するような経路の集合を選ぶ
全分岐網羅 すべての分岐方向を 1回以上実行するような経路の集合を選ぶ
ここで,分岐方向とは条件文から分かれる経路の方向であり,if文では,条件式が成り立つ場合とそうでない場合の二つの方向がある.また,while文の場合も条件式が成り立つ場合とそうでない場合の二つの方向がある.たとえば以下のプログラムのテストケースを考える.
1 if (x > 1 && y == 0) {
2 z = z / x;
3 }
4 if (x == 2 || z > 1) {
5 z = z + 1;
6 }
このプログラムの流れ図は図 6のようになる.
x > 1and
y == 0
z = z / x
x == 2or
z > 1
z = z + 1
a
b
c
d
e
No
Yes
Yes
No
図 6: テスト対象プログラムの流れ図
このプログラムの全経路は,abd, abe, acd, aceである.このうち,経路 aceをたどるテストケースはすべての命令を実行することができ,命令網羅を満たす.このようなテストケースとして,{(x = 2, y = 0, z = 3)} がある.しかし,このテストケースでは一つめ
17
の条件分岐で,&& とすべきところを|| としてしまったような誤りを発見することはできない.全分岐網羅を満たすようなテストケースとしては {(x = 3, y = 0, z = 3), (x = 2, y =
1, z = 1)} がある.このテストケースは,経路 abe, acdをたどる.テスト網羅基準に基づくテストケースの生成は以下の手順で行われる.
1. あるテスト網羅基準を満たすように経路の最小集合を選ぶ
2. 各経路を通過するための経路条件を選ぶ
3. 各経路条件を満たすテストケースを求める
3.3.3 回帰テスト
テストフェーズでは発見された誤りの修正が行われるが,修正が不十分な場合や,新たに誤りを作り込んでしまう場合がある.そのためにすべてのテストケースを用いて再びテストを行うが,このテストを回帰テストと呼ぶ.回帰テストでは修正した箇所以外の部分についても副作用として誤りが発生しないか確かめる必要がある.回帰テストで問題が生じた場合はプログラムを修正して再びテストを行い,問題がなくなるまでこれを繰り返す.
4 実験課題
テキスト・フォーマッタに対する利用者の要求を図 7に与える.
日本語を含むテキストファイルをプリンタに出力するためのテキストフォーマッタがほしい.入力は,ファイルと標準入力の両方に対応すること.出力は PostScript 形式で標準出力に出す.各ページのへッダにファイル名,ページ番号,印刷した日付,ユーザ名を出力する.入力テキストの行に対応した行番号を出力すること.Cプログラムを印刷するときは,予約語を強調印刷する.
図 7: 利用者の要求
4.1 要求分析
図 7を分析しテキスト・フォーマッタの持つべき機能を定め,A.1の書式に従った要求仕様書を作成せよ.
18
4.1.1 要求の確認と実現可能性の検討
Web ページで与えるサンプル入力に対して,利用者の要求を満たす出力を PostScript
で記述せよ.出力イメージはプレビューアによる表示及びプリンタによる印刷で確認せよ.PostScript の詳細については付録Cを参照すること.出力イメージの作成を通してシステムの機能を定めよ.このとき,常識的にテキスト・
フォーマッタに要求される機能について図 7では特に言及されていない可能性が高い点に注意せよ.利用者にとってあたりまえの要求ほど文章化されていないことが多い.要求について不明な点があれば質問し,利用者の要求を正確に定義せよ.
4.1.2 要求仕様書作成
付録A.1の書式に従った要求仕様書を作成せよ.システムを状態遷移機械としてとらえ, 状態遷移図および状態遷移表を要求仕様書の概要の項目に示すこと.状態遷移機械は LEXを用いて実現することを意識し,LEXで実現可能な状態遷移機械を作ること.要求仕様書にはシステムの機能を詳細に記述せよ.要求仕様書には実現するテキストフォーマッタを用いて印刷される出力イメージを添付せよ.また,要求仕様書のレビューを行い,付録A.1で提示された,完全さ,テストの容易さ,簡潔さ等の基準項目について確認すること.
4.2 設計
システムのモジュール分割を行い,各モジュールの外部仕様を決定せよ.このとき,モジュールの満たすべき規範に留意すること.また,各モジュールの仕様に不整合が生じないように注意せよ.成果物として付録A.2の書式に従った設計仕様書を作成せよ.設計仕様書にはモジュール間の呼び出し関係を表したモジュール間関連図と各モジュールのモジュール外部仕様書を記述すること.
4.3 コーディング
設計仕様書に基づき,システムをCのプログラムとして実現せよ.モジュールごとにソースファイルを分割して記述すること.状態遷移機械はアクションを設計し,LEXを用いて実現せよ.成果物はソースプログラムである.ソースファイルはモジュールごとに分割し,コンパイルには make を利用すること.ソースコード管理ツールを利用してソースコードを管理すること.すべての定数(列挙定数またはマクロ),大域変数,静的変数,LEXのアクション,関数とその引数に適切なコメントを付けること.
4.4 テスト
以下の手順でテストを実施せよ.
19
1. ブラックボックステストに基づいてテストケースを設計する.
2. テスト実行を行い,問題が生じた場合はプログラムを修正する.
3. テストケースがホワイトボックステストの全分岐網羅を満たすかどうか調べ,満たさない場合は満たすようにテストケースを追加する.
4. 追加したテストケースに対してテスト実行を行い,問題が生じた場合はプログラムを修正する.
5. 回帰テストを行う.
ブラックボックステストについて,少なくとも以下の項目をテストするテストケースを含むこと.
• 1行の文字数
• 1ページの行数
• 長いファイル名
• タブ文字の処理
• エラーの発生
1行の文字数に関しては 3.3.1節を参考にしてテストケースを設計せよ.他の項目についても同様にテストケースを設計せよ.成果物としてテスト報告書を作成せよ.テスト報告書には,以下の項目を記述すること.
1. ブラックボックステストのテスト項目
2. ホワイトボックステストのテスト項目
3. テストフェーズ全体で発見できた誤りの総数
また,各テスト項目ごとに以下の事項を記載すること.
(1) テスト項目の説明ブラックボックステストの場合は着目した仕様と入力条件ホワイトボックステストの場合はテスト対象のコードの説明
(2) テストケースと予想される結果の説明ただしプログラムに対する入力をそのまま記述する必要はない
(3) テスト実行の結果
(4) 対処誤りが発見された場合は,それに対して行った対処を記述
20
4.5 文書作成
UNIXのオンラインマニュアルの書式に準拠したマニュアルを作成せよ.項目は man
コマンドの出力を参考にせよ.
5 レポート
本実験ではレポートを 2回提出する.提出法の詳細については実験時間に指示する.
5.1 上流工程レポート
要求分析,設計の結果に基づいてレポートを作成し提出せよ.レポートには下記の考察を記述すること.
(1) 実験で行ったモジュール分割が適切であったかどうか考察せよ.モジュールが満たすべき規範をどの程度満たしているかを述べよ.
(2) その他実験について考察せよ.
5.2 最終レポート
すべての実験課題終了後,実験結果に基づいてレポートを作成し提出せよ.レポートには下記の考察を記述すること.
(1) ソフトウェア開発のプロセスモデルについて調査し,実験の開発プロセスと比較せよ.
(2) 例としてとり上げたテキスト・フォーマッタをより実用的にするためにどのような機能が必要か考察せよ.
(3) 作成したシステムにそのような機能追加を行うことは容易か否か考察せよ.
• 容易な場合: 機能拡張の方法を説明せよ.
• 困難な場合: どのように設計すべきであったかについて考察せよ.
(4) その他実験について考察せよ.
参考文献
[1] Lesk M.E. and Schmidt E. Lex - a lexical analizer generator. Technical report, Bell
Laboratories, 1979.
[2] Rambaugh J. Object-Orented Modeling and Design. Pretice Hall, 1991.
21
[3] Fox C.J. Frakes W.B. and Nejmeh B.A. Object-Orented Modeling and Design. Pretice
Hall, 1991.
[4] Adobe Systems. PostScriptリファレンス・マニュアル. アスキー出版局, 1988.
[5] Adobe Systems. PostScriptチュートリアル&クックブック. アスキー出版局, 1989.
[6] Glenford J.Myers. ソフトウェア・テストの技法 第 2版. 近代科学社, 2006.
[7] 野口健一郎. ソフトウェアの論理的な設計法. 共立出版, 1990.
[8] 有沢誠. ソフトウェア工学. 岩波書店, 1988.
[9] 五月女健治. yacc/lex. 啓学出版, 1992.
[10] 横森貴 富田悦次. オートマトン・言語理論. 森北出版, 1992.
[11] 落水浩一郎. ソフトウェア工学実践の基礎. 日科技連出版社, 1993.
[12] 中所武司. ソフトウェア工学. 朝倉書店, 1997.
[13] G.Booch et al. The Unified Modeling Language User Guide. Addsion-Wesley, 1999.
[14] 松本啓之亮. ソフトウェア工学 オブジェクト指向・UML・プロジェクト管理. 森北出版, 2005.
22
A 成果物の書式
要求仕様書および設計仕様書の書式を示す.各成果物は各々以下の項目を含むこと.各書式については [3]を参考にした.
A.1 要求仕様書の書式
概要 システム概要を説明し,システムをモデル化技法を用いて表現する.本実験では,システムを状態遷移機械として捉えるので,状態遷移機械を状態遷移表と状態遷移図で表現する.
機能 システムが提供するサービス,動作について記述する.システムの機能については注意深く,正確に,細部に至るまで解説しなければならないため,通常はこの機能についての解説が要求仕様書の大部分を占める.
ユーザインタフェース仕様 コマンド名,使用方法,オプションについて記述する.最終的なマニュアルの元になる.
例外処理 予想される例外やエラー条件とこれらに対する対応について記述する.
設計のヒント 設計上の特徴,制限事項,注意点についてシステム設計者やプログラマに提示する.
コメント その他記載すべきことがあれば記述する.
添付資料 機能を補足する資料として出力イメージを添付する.
要求仕様書は以下の基準項目を満たす必要がある [3].
完全さ システムの機能,能力,制限事項,その他の性質の全てを,詳細にわたって記述する.次のフェーズであるシステム設計を行うのに十分な記述が行われているか確認すること.
テストの容易さ テストケースを作成できるように具体的に記述する必要がある.例えば“ファイルの印刷を適切な時間で終了すること”という仕様では,どのようなテストケースを作成すればよいか明確でない.“1Kバイトのファイルの印刷は 10秒で終了すること”のように記述すべきである.
簡潔さ 必要十分な内容のみが含まれていること.
次のページに例として JISコードのファイルを EUCに変換するプログラムの要求仕様書を与える.
23
要求仕様書
システム名: 日本語コード変換プログラム作成者: 名大太郎作成年月日: 2014年 4月 17日最終更新: 2014年 4月 24日Version: 1.2
概要:
入力データの日本語コードを JISコードからEUCへ変換する.システムは以下の状態遷移図と状態遷移表で表される状態遷移機械として捉えられる.
ASCIIモード 日本語モード
japanese_esc/ε
ascii_esc/εjapanese_esc以外の入力
/そのまま出力
ascii_esc以外の入力/8ビット目を1にして出力
次状態/出力状態 japanese esc ascii esc それ以外
⇒ ASCII 日本語 ε ASCII 入力文字列 ASCII 入力文字列
日本語 日本語 入力文字列 ASCII ε 日本語入力の最上位ビットを 1にしたもの
ただし,japanese esc は入力パターン \033\044\102を,ascii esc は \033\050\102を表す.
機能:
引数に指定されたファイルから入力を読み込み,JISコードをEUCに変換して出力する.引数が無い場合は標準入力からデータを読む.JISコード以外はそのまま出力する.コード変換は次のように行う.入力データにパターン\033\044\102が現れると,次にパターン\033\050\102が現れるまでの全ての入力文字の最上位ビットを 1にする.
ユーザインタフェース:コマンド名: jis2euc
使用方法: jis2euc [ファイル名]
オプション: なし
例外処理:
引数に指定されたファイルが存在しない場合はエラーメッセージを表示してプログラムを終了する.
設計のヒント:
入力データはすべて 7ビットの文字であり,最上位ビットが 1になることはない.
24
コメント:
特になし.
25
A.2 設計仕様書の書式
設計仕様書はモジュール間関連図とモジュールごとのモジュール外部仕様書からなる.
1. モジュール間関連図モジュール間の呼び出し関係を図で表現する.矢印には呼び出す関数をラベルとして記述する.
2. モジュール外部仕様書
概要 モジュールの概要を記述する.
機能・処理 モジュールの振舞いを詳細に説明する.他のモジュールを呼び出す場合,そのモジュールを呼び出す状況,関数名,引数,返り値を記述すること.また,他のモジュールから呼び出される場合は,呼び出し元のモジュール,呼び出される関数,受け取る引数,そのデータに対する処理,返す値を記述すること.
インタフェース モジュールを外部から呼び出すときの関数名と引数を記述する.インタフェースとなる関数は 1個のモジュールに複数存在してもよい.
入力と出力 モジュールがシステム外部との間で行う入力と出力を説明する.
前提 モジュールの前提とは,モジュールが正常に動作するための条件のことである.例えば,ファイル処理モジュールの前提とは,指定されたファイルは既にオープンされていて,かつ読み込み可能なことである.
エラー処理 モジュールで起こりうるエラーや例外条件,その原因,その対処方法などを記述する.
内部構造 大域変数,静的変数,関数,列挙定数,マクロ,ヘッダファイルなどのモジュールの内部構造のうち,機能に関わるものについて記述する.複数のモジュールで利用されるものは関連するすべてのモジュール外部仕様書に記述すること.
Caller 自分が呼び出すモジュールを記述する.
Callee 自分を呼び出すモジュールを記述する.
コメント その他記載すべきことがあれば記述する.
次のページに例としてA.1節の要求仕様に示されたシステムの設計仕様書を与える.
26
設計仕様書
モジュール間関連図
void codeconv(FILE *infile)
メインモジュール
コード変換モジュール
27
モジュール外部仕様書
メインモジュール
システム名: 日本語コード変換プログラムモジュール名: メインモジュール作成者: 名大太郎作成年月日: 2014年 5月 1日最終更新: 2014年 5月 1日Version: 1.2
概要:
メインモジュールはコード変換モジュールを呼び出す.
機能・処理:
コマンドライン引数を調べ,引数が指定されていた場合はそれを入力ファイル名としてファイルを開き,そのファイルポインタを引数としてコード変換モジュールの関数 codeconv
を呼び出す.コマンドライン引数が指定されていなかった場合は標準入力ファイルポインタを引数としてコード変換モジュールを呼び出す.変換モジュールの処理が終了したら OK EXIT を返し,処理を終了する.
インタフェース:
int main(int argc, char *argv[])
入力と出力:
入力ファイル名が入力される.
前提:
引数で指定された入力ファイルが存在する.
エラー処理:
引数で指定された入力ファイルが存在しなかった場合は標準エラー出力にエラーメッセージを出力して終了する.このとき返り値は ERR EXIT とする.
内部構造:
• 大域変数(型,名前,初期値)
• 静的変数(型,名前,初期値)
• 列挙定数(名前,値)OK EXIT 0
ERR EXIT 1
• ヘッダファイル(ファイル名,構成要素)
28
– jis2euc.h
関数 codeconv のプロトタイプ宣言,列挙定数 OK EXIT,ERR EXIT の定義を含む
Caller:
コード変換モジュール
Callee:
なし
コメント:
特になし.
29
コード変換モジュール
システム名: 日本語コード変換プログラムモジュール名: コード変換モジュール作成者: 名大太郎作成年月日: 2014年 5月 1日最終更新: 2014年 5月 8日Version: 1.3
概要:
コード変換モジュールはファイルから入力を読み込み,コード変換をして出力する.
機能・処理:
メインモジュールからファイルポインタを引数として関数 codeconv が呼び出された場合は.そのファイルポインタからデータを読み,JISコードをEUCに変換して標準出力に出力する.ただし,JISコード以外はそのまま出力する.コード変換は次のように行う.入力データにパターン\033\044\102が現れると,次にパターン\033\050\102が現れるまでの全ての入力文字の最上位ビットを 1にする.
インタフェース:
void codeconv(FILE *)
入力と出力:
ファイルポインタから文字列が入力される.標準出力に文字を出力する.
前提:
\033\044\102と\033\050\102の間に改行やタブなどの制御文字は現れない.
エラー処理:
内部構造:
• 大域変数(型,名前,初期値)
• 静的変数(型,名前,初期値)
• 関数(型,名前,パラメータ)void codeconv(FILE *)
• 列挙定数(名前,値)
• ヘッダファイル(ファイル名,構成要素)
– jis2euc.h
関数 codeconv のプロトタイプ宣言を含む
30
Caller:
なし
Callee:
メインモジュール
コメント:
LEXを用いて実現すること.
31
A.3 実現例
A.2節の設計仕様書の実現例を与える.
1 #include <stdio.h> 2 #include "jis2euc.h" 3 4 int main(int argc, char *argv[]) 5 { 6 FILE *input_file = stdin; /* Input file */ 7 8 if (argc > 1) { 9 if ((input_file = fopen(argv[1], "r")) == NULL) { 10 fprintf(stderr, "Can’t open %s.\n", argv[1]); 11 return(ERR_EXIT); 12 } 13 } 14 15 codeconv(input_file); 16 17 return(OK_EXIT); 18 }
図 8: メインモジュールのプログラム
1 %{ 2 3 #include <stdio.h> 4 #include "jis2euc.h" 5 6 %} 7 8 JAPANESE_ESC (\033\044\102) 9 ASCII_ESC (\033\050\102) 10 11 %Start ASCII JAPANESE 12 13 %% 14 15 <ASCII>{JAPANESE_ESC} { /* Search japanese_esc[] in ASCII mode. */ 16 BEGIN(JAPANESE); /* Move to JAPANESE mode. */ 17 } 18 <ASCII>. { 19 putchar(yytext[0]); 20 } 21 22 <JAPANESE>{ASCII_ESC} { /* Search ascii_esc[] in JAPANESE mode. */ 23 BEGIN(ASCII); /* Move to ASCII mode. */ 24 } 25 <JAPANESE>. { 26 putchar(0x80 | yytext[0]); 27 } 28 29 <ASCII>\n { 30 putchar(yytext[0]); 31 } 32 33 %% 34 35 void codeconv(FILE *infile) 36 { 37 yyin = infile; 38 39 BEGIN(ASCII); /* Start in ASCII mode. */ 40 yylex(); 41 }
図 9: コード変換モジュールのプログラム
1 enum { 2 OK_EXIT = 0, 3 ERR_EXIT = 1 4 }; 5 6 void codeconv(FILE *);
図 10: ヘッダファイル
32
B LEX
B.1 LEXとは
ファイルの内容を読み込んで処理するプログラムは,一文字ずつ読み込みながら処理することが多い.しかし,Cのソースプログラムのように文法にしたがって書かれたファイルを処理する場合には,連続した文字を一つの単語として認識してから,予約語の判定や識別子の判定などを行なう必要がある.LEXは,このような入力を単語単位で扱うプログラムの作成を支援する.LEXは字句解析プログラムを生成するツールである.LEXによって生成されたプログ
ラムは,ファイルからの入力を解析し,トークンと呼ばれる字句に分解する.LEXを用いて,トークンを入力単位として扱うプログラムを記述することで,記述が簡潔になり見通しの良いプログラムを作成することができる.LEXの記述は三つの部分から構成される.それぞれ,定義部(definition section),ルー
ル部(rule section), サブルーチン部(subroutine section)と呼ばれ,“%%”によって区切られる.この構成を図 11に示す.定義部ではモードの宣言,パターンの名前の定義,C
のプログラムの記述を行う.ルール部では,状態を表すモード,入力パターン,アクションの三つを記述する.サブルーチン部には必要な関数をCで記述する.定義部とサブルーチン部は省略が可能である.サブルーチン部を省略するときは二つ目の “%%”も省略可能である.
B.2 記述方法
LEXを使った例題として次のようなプログラムを考える.
Pascal で記述されたソースプログラムの単語の数を数える.ただし,コメントとコード内の単語はそれぞれ別々に数える.
なお,“:=”のような複数の記号からなる演算子も一つの単語とする.また,シングルクウォートで囲まれた文字列は単語に分解しないで,その文字列全体を一つの単語として数える.スペース,タブ,改行やPascalの中で意味を持たない記号は単語として扱わない.
B.2.1 状態
このプログラムには二つの状態が存在する.一つは「コードの処理(CODE)」,もう一つは「コメントの処理(COMMENT)」である.LEXでは状態をモードと呼び,その記述で用いるモードを定義部の中で次のように記述する.
%Start CODE COMMENT
また,入力のトークンを次の 4種類に分類して扱う.
WORD, WHITESPACE, BEGINCOMMENT, ENDCOMMENT
33
%{
#include <stdio.h>
%}
JAPANESE_ESC (\033\044\102)ASCII_ESC (\033\050\102)
%Start ASCII JAPANESE
%%
<ASCII>{JAPANESE_ESC} { /* Search japanese_esc[] in ASCII mode. */ BEGIN(JAPANESE); /* Move to JAPANESE mode. */}<ASCII>. { putchar(yytext[0]);}
<JAPANESE>{ASCII_ESC} { /* Search ascii_esc[] in JAPANESE mode. */ BEGIN(ASCII); /* Move to ASCII mode. */}<JAPANESE>. { putchar(0x80 | yytext[0]);}
<ASCII>\n { putchar(yytext[0]);}
%%
int main(){ BEGIN(ASCII); /* Start in ASCII mode. */ yylex();
return 0;}
定義部
ルール部
サブルーチン部
図 11: LEXの記述構成
WHITESPACEはスペース,タブ,改行を表し,BEGINCOMMENT, ENDCOMMENTはそれぞれコメントの開始と終了を表す.それ以外の単語は WORDに含まれる.作成するプログラムの状態遷移図を図 12に示す.図中の CodeWord, CommentWord は
単語数を格納する変数を意味し,各状態で WORDが入力されるたびに値が 1増やされる.また,BEGINCOMMENT または ENDCOMMENT が入力されると状態が変化する.
B.2.2 パターン
LEXでは,ルール部において状態遷移を次のように記述する.
<モード名> パターン アクション
CODE COMMENT
BEGINCOMMENT/
ENDCOMMENT/
WHITESPACE/
WORD/CommentWord++
WHITESPACE/
WORD/CodeWord++
図 12: 状態遷移図
34
パターンはトークンの形式を表し,アクションはCによる文である.モード名で指定された状態であるときに,パターンに適合したトークンが入力されれば,そのアクションが実行される.パターンは正規表現で記述する.以下では,正規表現の基本的な書き方について説明
する.最も簡単なパターンは特定の文字列の記述である.例えば,コメントの開始記号や終
了記号は,それぞれ “(*” と “*)” であり 1,これらを直接記述する.なお,正規表現では特別な意味を持つ記号があるため,実際に記述する際には "(*" とダブルクウォートで囲うようにする.あるいは,直前にバックスラッシュ “\” をつけて,\(\* と記述する.また,タブ,改行,バックスラッシュはそれぞれ次のように記述する.
\t, \n, \\
また, “\133” のようにバックスラッシュの後に 8進数の数字を記述すると,ASCIIコードでその数字に該当する文字として扱われる.アルファベットの中の一文字などのように,ある特定の文字集合の中の一文字を指定
することができる.例えば,WHITESPACEは次のように記述される.
[ \t\n]
また,アルファベットや一桁の数字の場合は範囲を指定することができる.例えば,小文字のアルファベットの集合の中の一文字を次のように記述する.
[a-z]
複数の範囲や文字を組み合わせて指定することも可能である.例えば,アルファベットと数字の集合の中の一文字は次のように記述する.
[0-9A-Za-z]
ある特定の文字以外の文字を指定するには,“[” の直後に補集合を表す “^” を付加する.例えば,アルファベット以外の一文字を表すには次のように記述する.
[^A-Za-z]
なお,任意の一文字を表す場合にはこのような表現を用いず,ピリオド “.” を記述する.ただしピリオドは改行文字にはマッチしない.パターンは複数のパターンを連結して記述することができる.例えば,“[a-zA-Z][a-zA-Z]”
と記述すると,2文字のアルファベットからなる文字列を意味する.しかし,これではパターンの任意回の繰り返しなどを表現できない.正規表現には次の 3つの演算子がある.
? 0回または 1回だけ出現する.* 0回以上出現する.+ 1回以上出現する.
1コメントの開始記号と終了記号に “{”,“}” を使用する場合もあるが,ここでは考えないことにする.
35
例えば,Pascalにおける識別子は,先頭がアルファベットで,その後に 0個以上のアルファベットまたは数字がつながったトークンである.これを正規表現で記述すると次のようになる.
[A-Za-z][0-9A-Za-z]*
複数のパターンを選択したい場合には,演算子 “|” を用いる.例えば,Pascalの演算子のうち 2つの記号から構成されるものは次のように記述する.
"<>"|":="|"<="|">="|".."
正規表現で用いられる演算子には優先順位が存在する.よって,複数のパターンを結合する際には優先順位を考慮する必要がある.ここで述べた演算子の優先順位は次の表の通りである.表の上の方が優先度が高い.
演算子 優先度
"a" \b \133 [c] .(ピリオド) 高い(a)
a? b* c+
ab
a|b 低い
優先順位を変更するには括弧を用いる.例えば, “ab*” と “(ab)*” では優先度の違いから,前者は “abbbbbb...” を,後者は “abababab...” を表す.LEX では入力が複数の正規表現にマッチする場合は,もっとも長いパターンにマッチ
する.同じ長さの場合は最初に現れたルールにマッチする.定義部に書かれたパターン定義の順番ではなく,ルール部の記述の順に依存する.
B.2.3 パターン定義
パターンをルール部の中に直接記述することができるが,パターンに名前をつけてその名前を参照するようにすることができる.名前をつけることで,記述を見やすくし,また意味がわかりやすくなる.パターンの名前は定義部で次のように定義する.
パターン名 パターン
例えば,WHITESPACEは次のように記述される.
WHITESPACE [ \t\n]
定義したパターン名は “{”, “}” で囲うことで参照することができる.例えば,Pascal
の演算子を表すパターン OPERATOR を次のように記述する.
OPERATOR ({COMPOUNDOP}|{SINGLEOP})
COMPOUNDOP ("<>"|[<>:]=|"..")
SINGLEOP ([\+\-\*/\.,:;=<>^\(\)\[\]])
36
ここでは,COMPOUNDOP, SINGLEOPが参照されている.なお,各パターンが括弧で囲まれているが,これは参照された際に演算子の優先順位がパターンに影響することを防ぐためである.影響がない場合でも囲うようにすることで,未然に間違いを防ぐことができる.
B.2.4 アクション
アクションには Cの文を記述する.基本的に,記述できる文は一つであり,複数記述する場合には “{”, “}” で囲う.また,アクションが必要ない場合は空文であるセミコロン “;” だけを書く.例えば,Pascalのプログラムには現れない文字が入力される場合にそれを無視するようにするには,次のように記述する.
<CODE>. ;
ただし,このルールは他のルールより後に記述する.後に記述することで,他のパターンに適合しなかった場合だけこのパターンに適合するようになる.アクションの中ではモードを変更することができる.変更するにはマクロ BEGIN() を
用いる.引数には遷移したいモードを書く.例えば,CODE から COMMENT に遷移したい場合は次のように記述する.
BEGIN(COMMENT);
B.2.5 サブルーチン部
サブルーチン部では,プログラムに必要な関数を記述する.この記述の中で,入力ファイルの設定や初期状態の設定を行なう.入力ファイルを設定するには,入力ファイルをオープンし,変数 yyin にそのファイルポインタを代入する.yyin を設定しない場合は,標準入力が入力ファイルになる.また,初期状態の設定はアクションと同様に BEGIN()
で指定する.字句解析を開始するには関数 yylex() を呼び出す.
B.3 記述例
例題の記述例を図 13に示す.この記述例では,二つの関数 ppwc(), main() が定義されている.関数 ppwc() は引数で指定されたファイルを入力ファイルに設定して字句解析を行ない,結果を引数で指定された変数に代入する.関数 main() はコマンドの引数を調べ,引数で指定されたファイルをオープンして ppwc() を呼び出す.引数にファイルが指定されない場合は標準入力を入力ファイルに設定する.この二つの関数は一つの関数で記述することが可能だが,関数 ppwc() に LEXの記述
に依存した部分をまとめてあるため,関数 main() を別のファイルに記述して分割コンパイルしたり,他のファイルで定義された関数から関数 ppwc() を呼び出すことが可能である.
37
1 %{ 2 /* 3 * ppwc: Pascal Program Word Counter, 4 * prduced by Appon Software Labo. 5 */ 6 7 #include <stdio.h> 8 9 int CodeWord = 0; /* for word count in CODE */ 10 int CommWord = 0; /* for word count in COMMENT */ 11 12 %} 13 14 WORD ({IDENTIFIER}|{NUMBER}|{STRING}|{OPERATOR}) 15 IDENTIFIER ([a-zA-Z][0-9a-zA-Z]*) 16 NUMBER ({INTEGER}(\.{INTEGER})?([Ee][\+\-]?{INTEGER})?) 17 INTEGER ([0-9]+) 18 STRING (’([^’]|’’)*’) 19 OPERATOR ({COMPOUNDOP}|{SINGLEOP}) 20 COMPOUNDOP ("<>"|[<>:]=|"..") 21 SINGLEOP [\+\-\*/\.,:;=<>^\(\)\[\]] 22 BEGINCOMMENT ("(*") 23 ENDCOMMENT ("*)") 24 WHITESPACE ([ \t\n]+) 25 26 %Start CODE COMMENT 27 28 %% 29 30 <CODE>{WORD} { /* Count words. */ 31 CodeWord++; 32 } 33 34 <CODE>{BEGINCOMMENT} { /* Enter to COMMENT mode. */ 35 BEGIN(COMMENT); 36 } 37 38 <CODE>{WHITESPACE} ; /* Skip white spaces. */ 39 <CODE>. ; /* Skip unexpected symbols. */ 40 41 42 <COMMENT>{WORD} { /* Count words. */ 43 CommWord++; 44 } 45 46 <COMMENT>{ENDCOMMENT} { /* Enter to CODE mode. */ 47 BEGIN(CODE); 48 } 49 50 <COMMENT>{WHITESPACE} ; /* Skip white spaces. */ 51 <COMMENT>. ; /* Skip unexpected symbols. */ 52 53 %% 54 55 void ppwc(FILE *file, int *code_words, int *comm_words) 56 { 57 yyin = file; /* Set input file */ 58 BEGIN(CODE); /* Initial mode. */ 59 yylex(); /* Call lexical analyzer. */ 60 61 *code_words = CodeWord; /* Return number of words in code. */ 62 *comm_words = CommWord; /* Return number of words in comment. */ 63 } 64 65 void main(int argc, char **argv) 66 { 67 FILE *input_file = stdin; /* Input file */ 68 int code_words; /* Number of words in code */ 69 int comm_words; /* Number of words in comments */ 70 71 if (argc > 1 && (input_file = fopen(*++argv, "r")) == NULL) { 72 fprintf(stderr, "Can’t open %s.\n", *argv); 73 exit(1); 74 } 75 76 ppwc(input_file, &code_words, &comm_words); 77 78 printf("words in code = %d\n", code_words); 79 printf("words in comment = %d\n", comm_words); 80 }
図 13: プログラム記述例
38
B.4 使用方法
LEXプログラムに対してコマンド lex を実行すると Cのプログラムが生成される.“-t” オプションを指定することで標準出力に出力することができ,リダイレクトを利用することで任意の名前のファイルに生成されたCプログラムを保存できる.生成されたプログラムは通常の Cのプログラムと同じようにコンパイルする.ただし,LEX用のライブラリをリンクするためにコンパイラの引数に “-ll” オプションをつける必要がある.ppwc.lというファイル名の LEXプログラムから,実行形式ファイル ppwcを作成する
例を以下に示す.
% lex -t ppwc.l > ppwc.c
% cc -o ppwc ppwc.c -ll
なお,LEXの記述が間違っていてもlexの実行時にエラーにならず,生成されたCプログラムをコンパイルしたときにエラーになることがあるので注意すること.
B.5 複数のLEX記述
LEXで生成されるCプログラムには yytext,yylex() のように yy というプレフィックスの付いた変数名や関数名が使用されている.一つのプログラムに複数の LEX記述から生成されたプログラムが含まれる場合はこれらの関数が二重定義にならないようにプレフィックスを変更する必要がある.このプレフィックスは -P オプションで指定することができる.
% lex -Pyy2 -t sample.l > sample.c
この例で生成されるプログラムでは変数名,関数名に yy2 というプレフィックスが付けられる.デフォルトのプレフィックスで生成されたプログラムの中で呼び出されている関数 yywrap()
は LEX で生成されたプログラムではなく,LEX のライブラリ(libl)で定義されている.そのため,上の例の場合 yy2wrap() の定義を別途記述する必要がある.ライブラリでマクロとして定義されている関数 yywrap() は常に 1を返す関数であり,これと同じ動作をする関数 yy2wrap() をサブルーチン部で定義してやればよい.
39
C PostScript
C.1 PostScriptマクロ
本実験で作成するテキスト・フォーマッタの出力として PostScript [4, 5] を用いる.PostScriptは印刷されるページにおける文章,図形,ディジタルイメージの体裁などについて記述するために用いられる,高機能,高水準,かつ出力デバイスに依存しない言語である.PostScript よって記述された印刷イメージは,PostScript 対応プリンタであればメーカー,機種を問わず印刷できる.一般に PostScript の記述は煩雑であるため,本実験では簡潔に記述するためのマクロが用意してある.マクロ定義のファイル名は prologue.ps
である.このマクロはあくまでも実現を容易にするために用意されたものであり,システム要
求の前提でない.利用者がこのマクロの存在を意識することなくテキストフォーマッタを使用できるように設計すること.例えば,テキストフォーマッタの出力に対して利用者自身が prologue.ps を付加する,あるいは prologue.ps を実行ディレクトリに置かなければいけない,といった仕様は望ましくない.ここでは,例を用いて本実験で出力として用いるマクロについて説明する.PostScript
は逆ポーランド記法に基づいた文法を採用しており,パラメータの後に命令を記述する.例えば,
Hello world.
みなさんこんにちは.
と書かれたページを印刷するには,次のような記述をする.
b
(Hello world.) C s n
(みなさんこんにちは.) K s n
f
b,s,n,fはマクロ命令,C,K はフォント名である.文字列は ‘(’ と ‘)’ で囲む.本実験で用意されているPostScriptのマクロ命令を以下に示す.
b ページの初期化を行う(begin)f 改ページを行う(formfeed)n 改行を行う(newline)
string font s 文字列 string をフォント font を用いて出力する(show string)
b によるページの初期化は各ページごとに行う.フォントは種類は以下の 3種類が用意されている.
C クーリエ(Courier, 英文用等幅フォント)B クーリエボールド(Courier-Bold, 英文用等幅太字フォント)K 明朝体(日本語フォント)
40
クーリエボールドは強調印字したいときに用いることができる.つまり上の例の記述では,ページの初期化を行い,“Hello world.” という文字列をクー
リエフォントで出力し,改行を行い,“みなさんこんにちは.”という文字列を日本語フォントで出力し,改行を行い,改ページを行う.また,PostScirpt では % 記号(文字列中に現れるものを除く)から行末まではコメン
トとして扱わる.
C.2 文字列
PostScript では文字列を ‘(’ と ‘)’ で囲んで記述する.文字列を複数行にわたって記述することもできるが,改行箇所に 1個の空白が出力される.この空白の出力を抑制したい場合は改行箇所にバックスラッシュを挿入する.PostScript の文字列ではバックスラッシュでエスケープされる以下の特殊文字が定め
られている.
\n 改行\r 復帰\t 水平タブ\b バックスペース\f フォームフィード\( 左かっこ\) 右かっこ\\ バックスラッシュ\ddd ASCIIが ddd(3桁の 8進数)である文字
例えば右かっこの出力は以下のように記述できる.
(\)) C s
また,右かっこを以下のようにASCIIで記述することもできる.
(\051) C s
1つの文字列内で左右のかっこが対になっている場合はバックスラッシュでエスケープしなくても出力されるが,エスケープしてもよい.改行,復帰,水平タブ,バックスペース,フォームフィードは 1文字分の空白として出
力されるため,これらの制御文字の出力には適さない.
41
C.3 構造情報の付加
PostScript 記述を入力として処理するユーティリティには構造情報を必要とするものが多い.ほとんどのプレビューアはページ番号情報の埋め込まれていないPostScript 記述を正しく扱うことができない.構造情報はコメントを利用し,%% で始まる行に記述される.ページ番号情報は以下のような記述を各ページの記述の最初に埋め込む.
%%Page: <label> <ordinal>
今回の実験では <label> と <ordinal> は両方ともページ番号を入れておけば構わない.本実験のマクロを利用する場合,ページ初期化コマンド b の直前に記述することになる.ページ番号情報を含む記述例を以下に示す.
%%Page: 1 1
b
(Hello world.) C s n
f
%%Page: 2 2
b
(みなさんこんにちは) K s n
f
C.4 PostScript ファイルの作成
C.1で紹介したマクロを利用するためには,マクロの定義を付加する必要がある.本実験で使用する PostScript マクロは “prologue.ps” という名前のファイルで定義されている.たとえば,マクロを用いた記述をテキストエディタで作成し,bar.ps というファイル名で保存した場合,以下のようにして “prologue.ps” と “bar.ps” を結合することができる.なお,“%”はプロンプトである.
% cat prologue.ps bar.ps > foo.ps
このようにしてできた “foo.ps” というファイルは通常の PostScriptプログラムであり,プレビューアで表示したり,PostScript プリンタで印刷することができる.
C.5 日本語文字コード
prologue.ps で定義されているマクロは,日本語文字列が EUC で記述されていることを想定している.
42