2006年04月26日

Interpreter ― 文法規則をクラスで表現する

  
ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。移植後のコードの公開に関しては、結城氏の了解を得ています。
※当ソースは、Visual C# 2005 Express Editionで動作を確認しています。



ParseExceptionインターフェース
using System;

namespace Gushwell.DesignPatterns {
[Serializable]
public class ParseException : System.Exception {
public ParseException(string msg)
: base(msg) {
}
}
}

Nodeクラス
using System;

namespace Gushwell.DesignPatterns {
public abstract class Node {
public abstract void Parse(Context context);
}
}



ProgramNodeクラス
using System;

namespace Gushwell.DesignPatterns {
// <program> ::= program <command list>
public class ProgramNode : Node {
private Node commandListNode;

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

public override string ToString() {
return "[program " + commandListNode + "]";
}
}
}



CommandListNodeクラス
using System;
using System.Collections.Generic;

namespace Gushwell.DesignPatterns {
// <command list> ::= <command>* end
public class CommandListNode : Node {
private List<Node> list = new List<Node>();
public override 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 {
Node commandNode = new CommandNode();
commandNode.Parse(context);
list.Add(commandNode);
}
}
}

public override string ToString() {
string s = "[";
for ( int i = 0; i < list.Count; i++ ) {
Node node = list[i];
s += node.ToString() + ( ( i == list.Count - 1 ) ? "" : "," );
}
return s + "]";
}
}
}



CommandNodeクラス
using System;

namespace Gushwell.DesignPatterns {
// <command> ::= <repeat command> | <primitive command>
public class CommandNode : Node {
private Node node;

public override 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();
}
}
}



<b>RepeatCommandNodeクラス</b>  
using System;

namespace Gushwell.DesignPatterns {
// <repeat command> ::= repeat <number> <command list>
public class RepeatCommandNode : Node {
private int number;
private Node commandListNode;

public override 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() + "]";
}
}
}



PrimitiveCommandNodeクラス
using System;

namespace Gushwell.DesignPatterns {
// <primitive command> ::= go | right | left
public class PrimitiveCommandNode : Node {
private string name;

public override 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;
}
}
}



StringTokenizerクラス
using System;
using System.Collections.Generic;
using System.Text;

namespace Gushwell.DesignPatterns {
class StringTokenizer {
private string[] strs;
private int index = -1;

public StringTokenizer(string text) {
this.strs = text.Split();
}
public string NextToken() {
return strs[++index];
}

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

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



Contextクラス
using System;

namespace Gushwell.DesignPatterns {
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;
}
}
}



Programクラス
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Gushwell.DesignPatterns {
class Program {
static void Main(string[] args) {
try {
StreamReader reader =
new StreamReader("program.txt", System.Text.Encoding.Default);
System.String text;
while ( ( text = reader.ReadLine() ) != null ) {
Console.WriteLine("text = \"" + text + "\"");
Node node = new ProgramNode();
node.Parse(new Context(text));
Console.WriteLine("node = " + node);

}
Console.ReadLine();
} catch ( System.Exception ex ) {
Console.Error.WriteLine(ex.StackTrace);
}
}
}
}



 

この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/gushwell/50439287