簡単なタートルグラフィックスを描くミニ言語を作成してみましょう。ここでは、次のコマンドを使用できるようにします。
コマンド | 引数 | 解説 |
---|---|---|
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.