×

[PR]この広告は3ヶ月以上更新がないため表示されています。
ホームページを更新後24時間以内に表示されなくなります。

C#デザインパターン  - Interpreter

ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。
解説らしい解説は載せていませんので、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』も合わせてお読みください。

※当ソースは、Visual C# 2008 Express Editionで動作を確認しています。
  Visual Studio 2005をお使いの方は、ブログの記事を残しておきますので、そちらをご覧ください。




■INode.cs
using System;

namespace InterpreterSample {
    public interface INode {
        void Parse(Context context);
        void Print();
        void Run(Turtle turtle);
    }
}



■ProgramNode.cs
using System;
using System.Drawing;

namespace InterpreterSample {
    // <program> ::= program <command list>
    public class ProgramNode : INode {
        private INode commandListNode;

        public void Parse(Context context) {
            context.SkipToken("program");
            commandListNode = new CommandListNode();
            commandListNode.Parse(context);
        }

        public override string ToString() {
            return "[program " + commandListNode + "]";
        }
        
        public void Print() {
            commandListNode.Print();
        }
        
        public void Run(Turtle turtle) {
            turtle.Graphics.Clear(Color.White);
            commandListNode.Run(turtle);
        }
    }
}


■CommandLineNode.cs
using System;
using System.Linq;
using System.Collections.Generic;

namespace InterpreterSample {
    // <command list> ::= <command>* end
    public class CommandListNode : INode {
        private List<INode> list = new List<INode>();
        public void Parse(Context context) {
            while (true) {
                if (context.CurrentToken() == null) {
                    throw new ParseException("Missing 'end'");
                } else if (context.CurrentToken().Equals("end")) {
                    context.SkipToken("end");
                    break;
                } else {
                    INode commandNode = new CommandNode();
                    commandNode.Parse(context);
                    list.Add(commandNode);
                }
            }
        }

        public override string ToString() {
            return list.Aggregate("", (s, node) => s + (node.ToString() + ' '));
        }
        
        public void Print() {
            list.ForEach(node => node.Print());
        }
        
        public void Run(Turtle turtle) {
            list.ForEach(node => node.Run(turtle));
        }
    }
}



■CommandNode.cs
using System;

namespace InterpreterSample {
    // <command> ::= <repeat command> | <primitive command>
    public class CommandNode : INode {
        private INode node;

        public void Parse(Context context) {
            if (context.CurrentToken().Equals("repeat")) {
                node = new RepeatCommandNode();
                node.Parse(context);
            } else {
                node = new PrimitiveCommandNode();
                node.Parse(context);
            }
        }

        public override string ToString() {
            return node.ToString();
        }
        
        public void Print() {
            node.Print();
        }
        
        public void Run(Turtle turtle) {
            node.Run(turtle);
        }
    }
}


■RepeatCommandNode.cs
using System;

namespace InterpreterSample {
    // <repeat command> ::= repeat <number> <command list>
    public class RepeatCommandNode : INode {
        private int number;
        private INode commandListNode;

        public void Parse(Context context) {
            context.SkipToken("repeat");
            number = context.CurrentNumber();
            context.NextToken();
            commandListNode = new CommandListNode();
            commandListNode.Parse(context);
        }

        public override string ToString() {
            return "[repeat " + number + " " + commandListNode.ToString() + "]";
        }
        
        public void Print() {
            for (int i = 0; i < number; i++) {
                commandListNode.Print();
            }
        }

        public void Run(Turtle turtle) {
            for (int i = 0; i < number; i++) {
                commandListNode.Run(turtle);
            }
        }
    }
}


■PrimitiveCommandNode.cs
using System;
using System.Drawing;

namespace InterpreterSample {
    // <primitive command> ::= go | right | left
    public class PrimitiveCommandNode : INode {
        private string name;

        public void Parse(Context context) {
            name = context.CurrentToken();
            context.SkipToken(name);
            if (!name.Equals("go") && !name.Equals("right") && !name.Equals("left")) {
                throw new ParseException(name + " is undefined");
            }
        }

        public override string ToString() {
            return name;
        }

        public void Print() {
            Console.WriteLine("do:" + name);
        }

        // Runメソッドは、画面描画のために独自に追加
        public void Run(Turtle turtle) {
            turtle.Run(name);
        }
    }
}



■Context.cs
using System;
using InterpreterSample;

namespace InterpreterSample {
    public class Context {
        private StringTokenizer tokenizer;
        private string currentToken;

        public Context(string text) {
            tokenizer = new StringTokenizer(text);
            NextToken();
        }

        public virtual string NextToken() {
            if (tokenizer.HasMoreTokens()) {
                currentToken = tokenizer.NextToken();
            } else {
                currentToken = null;
            }
            return currentToken;
        }

        public virtual string CurrentToken() {
            return currentToken;
        }

        public virtual void SkipToken(string token) {
            if (!token.Equals(currentToken)) {
                throw new ParseException("Warning: " + token + " is expected, but " + currentToken + " is found.");
            }
            NextToken();
        }

        public virtual int CurrentNumber() {
            int number = 0;
            try {
                number = System.Int32.Parse(currentToken);
            } catch (System.FormatException e) {
                throw new ParseException("Warning: " + e);
            }
            return number;
        }
    }
}


■StringTokenizer.cs
using System;
using System.Collections.Generic;
using System.Text;

namespace InterpreterSample {
    class StringTokenizer {
        private string[] strs;
        private int index = -1;
        
        public StringTokenizer(string text) {
            this.strs = text.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
        }
        public string NextToken() {
            return strs[++index];
        }

        public string CurrentToken() {
            return strs[index];
        }

        public bool HasMoreTokens() {
            return (index + 1) < strs.Length;
        }
    }
}




■Turtle.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace InterpreterSample {

    // このクラスは完全に独自に実装(オリジナルとは異なります)
    public class Turtle : IDisposable {
        public Direction Direction { get; set; }
        public Point Current { get; set; }
        public Graphics Graphics { get; set; }
        public Pen Pen { get; set; }
        public bool IsDelay { get; set; }

        public Turtle() {
            Direction = Direction.Up;
            Pen = new Pen(Color.Red, 3);
            IsDelay = true;
        }

        public void Run(string cmdname) {
            switch (cmdname) {
                case "go":
                    Current = Go();
                    break;
                case "right":
                    Direction = ToRight(Direction);
                    break;
                case "left":
                    Direction = ToLeft(Direction);
                    break;
            }
        }

        private Point Go() {
            Point newpoint;
            switch (Direction) {
                case Direction.Up:
                    newpoint = new Point(Current.X, Current.Y - 15);
                    break;
                case Direction.Down:
                    newpoint = new Point(Current.X, Current.Y + 15);
                    break;
                case Direction.Right:
                    newpoint = new Point(Current.X + 15, Current.Y);
                    break;
                default: // case Direction.Left:
                    newpoint = new Point(Current.X - 15, Current.Y);
                    break;
            }
            Graphics.DrawLine(Pen, Current, newpoint);
            if (IsDelay)
                System.Threading.Thread.Sleep(30);
            return newpoint;
        }

        private static Direction ToLeft(Direction direction) {
            switch (direction) {
                case Direction.Up:
                    return Direction.Left;
                case Direction.Left:
                    return Direction.Down;
                case Direction.Down:
                    return Direction.Right;
                default: // case Direction.Right:
                    return Direction.Up;
            }
        }

        private static Direction ToRight(Direction direction) {
            switch (direction) {
                case Direction.Up:
                    return Direction.Right;
                case Direction.Left:
                    return Direction.Up;
                case Direction.Down:
                    return Direction.Left;
                default: // case Direction.Right:
                    return Direction.Down;
            }
        }


        // 手抜き
        public void Dispose() {
            Pen.Dispose();
            Graphics.Dispose();
        }
    }

    public enum Direction {
        Right,
        Left,
        Up,
        Down
    }
}




■ParseException.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace InterpreterSample {
    static class Program {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main() {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}



■Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace InterpreterSample {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private INode program;

        private void panel1_Paint(object sender, PaintEventArgs e) {
            if (program == null)
                return;
            using (Turtle turtle = new Turtle {
                Current = new Point(panel1.Width / 2, panel1.Height / 2),
                Graphics = panel1.CreateGraphics(),
                IsDelay = false,
            }) {
                program.Run(turtle);
            }
        }

        private void button1_Click_1(object sender, EventArgs e) {
            using (Turtle turtle = new Turtle {
                Current = new Point(panel1.Width / 2, panel1.Height / 2),
                Graphics = panel1.CreateGraphics(),
            }) {
                program = new ProgramNode();
                program.Parse(new Context(textBox1.Text));
                program.Run(turtle);
                if (isDebug)
                    program.Print();
            } 
        }

        private bool isDebug = false;

        private void button1_KeyUpDown(object sender, KeyEventArgs e) {
            isDebug = e.Shift;
        }
    }
}


■Program.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace InterpreterSample {
    static class Program {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main() {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}