
簡単なタートルグラフィックスを描くミニ言語を作成してみましょう。ここでは、次のコマンドを使用できるようにします。
| コマンド | 引数 | 解説 |
|---|---|---|
| pen | あり | ペンの色を選択 |
| down | なし | ペンを下ろす |
| up | なし | ペンを上げる |
| north | あり | ペンを移動 |
| east | あり | ペンを移動 |
| south | あり | ペンを移動 |
| west | あり | ペンを移動 |
このコマンドを以下のように使用します。
east 100 // ペンを東へ 100 移動 south 100 // ペンを南へ 100 移動 down // ペンを下ろす pen 2 // ペンの色を選択 east 60 // ペンを東へ 60 移動(ペンを下ろしたので、キャンバスに線が描かれる) south 30 // ペンを南へ 30 移動 west 30 // ペンを西へ 30 移動 north 60 // ペンを北へ 60 移動 west 30 // ペンを西へ 30 移動 south 30 // ペンを南へ 30 移動
上記のコマンドを実行すると、キャンバスに次のような図形が描かれます。

サンプルプログラムは、次のようになります。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Memo1: TMemo;
Button1: TButton;
Bevel1: TBevel;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
procedure CanvasInit;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
procedure SelectPen(C: string; b: Integer);
procedure PenUp(C: string; b: Integer);
procedure PenDown(C: string; b: Integer);
procedure PenDir(C: string; b: Integer);
implementation
{$R *.dfm}
type
TDrawProc = procedure(C: string; B: Integer);
TCommand = record
Cmd: string; // コマンド文字
HasArg: Boolean; // 引数があるかどうか
DrawProc: TDrawProc; // 呼び出しルーチン
end;
const
ARG = true;
NO_ARG = false;
Cmds: array[0..6] of TCommand = (
(Cmd: 'pen'; HasArg: ARG; DrawProc: SelectPen),
(Cmd: 'up'; HasArg: NO_ARG; DrawProc: PenUp),
(Cmd: 'down'; HasArg: NO_ARG; DrawProc: PenDown),
(Cmd: 'north'; HasArg: ARG; DrawProc: PenDir),
(Cmd: 'east'; HasArg: ARG; DrawProc: PenDir),
(Cmd: 'south'; HasArg: ARG; DrawProc: PenDir),
(Cmd: 'west'; HasArg: ARG; DrawProc: PenDir)
);
var
PenPos: TPoint;
Drawing: Boolean;
procedure SelectPen(C: string; B: Integer);
begin
case B of
1: Form1.Image1.Canvas.Pen.Color := clBlack;
2: Form1.Image1.Canvas.Pen.Color := clRed;
3: Form1.Image1.Canvas.Pen.Color := clBlue;
4: Form1.Image1.Canvas.Pen.Color := clTeal;
else
Form1.Image1.Canvas.Pen.Color := clYellow;
end;
end;
procedure PenUp(C: string; b : Integer);
begin
Drawing := false;
end;
procedure PenDown(C: string; b: Integer);
begin
Drawing := true;
end;
procedure PenDir(C: string; B: Integer);
var
Diff: TPoint;
begin
if C = 'north' then begin
Diff.X := 0;
Diff.Y := -B;
end
else if C = 'east' then begin
Diff.X := B;
Diff.Y := 0;
end
else if C = 'south' then begin
Diff.X := 0;
Diff.Y := B;
end
else if C = 'west' then begin
Diff.X := -B;
Diff.Y := 0;
end
else Assert(false);
PenPos.X := PenPos.X+Diff.X;
PenPos.Y := PenPos.Y+Diff.Y;
if Drawing then
Form1.Image1.Canvas.LineTo(PenPos.X, PenPos.Y)
else
Form1.Image1.Canvas.MoveTo(PenPos.X, PenPos.Y);
end;
function FindCommand(C: string): TCommand;
var
i: Integer;
begin
for i := 0 to High(Cmds) do begin
if Cmds[i].Cmd = C then begin
Result := Cmds[i];
exit;
end;
end;
raise Exception.Create(Format('無効コマンド %s', [C]));
end;
procedure TForm1.Button1Click(Sender: TObject);
var
arg: Integer;
Cmd: TCommand;
i: Integer;
sl: TStringList;
begin
CanvasInit;
PenPos.X := 0; PenPos.Y := 0;
Drawing := false;
sl := TStringList.Create;
try
for i := 0 to Memo1.Lines.Count-1 do begin
if Memo1.Lines[i] = '' then continue;
sl.CommaText := Memo1.Lines[i];
Cmd := FindCommand(sl[0]);
if Cmd.HasArg then begin
if (sl.Count < 2) or not TryStrToInt(sl[1], arg) then
raise Exception.Create(Format('%s には引数が必要です', [sl[0]]));
end;
Cmd.DrawProc(sl[0], arg);
end;
finally
sl.Free;
end;
end;
procedure TForm1.CanvasInit;
begin
Image1.Canvas.FillRect(Image1.ClientRect);
Image1.Canvas.Pen.Color := clBlack;
end;
end.