|
INTERPRETER は、文法表現を構成するツリーの構造を提供します。通常、文法表現はテキスト文字列などで表現されますので、INTERPRETER を実際に応用する場合は、字句解析や、構文解析の技術と切り離すことができません。INTERPRETER を必要とする人は、JavaCCや、flex、bison
(※) などにも精通する必要があるでしょう。手前味噌ですが、JavaCCについては、こちらで特集していますので、参考にしてください。最初はとっつきにくいですが、これらのツールを使いこなせると、プログラミングの幅は大きく広がりますので知っていて損はないと思います。
ということで、ここでは、字句解析、構文解析を主題に扱いたいと思います。
『INTERPRETER でデータベースエンジンを作っちゃえ!』 (なげぇよ・・・)では、単純なテキストのデータベースファイルから、問い合わせ構文にマッチした行を抜き出して表示するデータベースエンジンを作ってみました。以下に実行例を示します。実装としては、かなり非効率なデータベース検索エンジンですが、まぁちょっとしたサンプルということで許してください。
|
$ ./InterpreterSample.exe query.txt db.txt
> Oota
Query: Oota
FOUND IN LINE[5] => yoshinori oota male 30 Yokohama-shi
FOUND IN LINE[10] => hisako oota female 27 yokohama-shi
FOUND IN LINE[14] => mariko oota female 12 yokohama-shi
FOUND IN LINE[15] => karashima yuko female 33 oota-ku
> Oota & Yoshinori
Query: (Oota & Yoshinori)
FOUND IN LINE[5] => yoshinori oota male 30 Yokohama-shi
> Oota & ( Yoshinori | Hisako )
Query: (Oota & (Yoshinori | Hisako))
FOUND IN LINE[5] => yoshinori oota male 30 Yokohama-shi
FOUND IN LINE[10] => hisako oota female 27 yokohama-shi
> end
【注意!】
※C++版では、字句解析ルーチンの処理を簡単にしたため、定義字句間に必ずスペースを入れてください。例えば、以下のような定義ファイルはNGです。
悪い例 ×Oota & (Yoshinori | (Hisako | Mariko))
"("、")"と文字列の間にスペースがない・・・ |
|
▼データベースファイル▼
db.txt
takasaki taro male 22 Takasaki-shi
shinagawa fumie female 20 Shinagawa-ku
tanimachi koji male 30 Bunkyo-ku
murai ken male 29 Akita-ken
yoshinori oota male 30 Yokohama-shi
kawamura yoko female 22 yokosuka-shi
yamada chisato female 30 Yokohama-shi
kaizuka ken male 38 atsugi-shi
edward haward male 32 kawasaki-shi
hisako oota female 27 yokohama-shi
kawashima kenji male 39 hamamatsu-shi
kurosaki kaori female 32 utsunomiya-shi
hamazaki airi female 33 niigata-shi
mariko oota female 12 yokohama-shi
karashima yuko female 33 oota-ku
jon kartis male 38 minato-ku
asaka den male 40 kawagoe-shi |
|
これぐらいの構文ですと、JavaCCや、flex、bisonを使うまでもありません。Javaの場合は、java.io.StreamTokenizer という便利な簡易字句解析エンジンがありますし、C++の場合は、STLライブラリの
istringstream を使うことで、字句解析エンジンに代用することが可能です。(という理由で字句間にスペースが必要なんです・・・^^;)
字句解析が解決すると、次は、渡された字句を構文に構築することを考えなければなりません。通常、構文解析はスタックを用いることで実現できます。基本的な処理の流れをマスターすれば、それほど難しいものではありません。次の構文を処理することを考えてみましょう。
|
例題
|
Oota & ( ( Hisa | Mari ) | Yoshi )
|
以下に、このクエリー構文の解析の流れを示します。解析処理は大きく四つのパターンに分かれます。
それらの処理の分類を、下図で、<P1>〜<P4>に分類しています。その中で、オペレータは青丸で表現し、その他は白丸で表現しています。また、Currentとは、現在処理対象としている
Expression のことです。この分類は解析処理において重要になります。それらの点に注意して、以下の処理の流れを追ってください。


以下にそれぞれのパターンの処理のまとめをしましょう。
|
P1
|
Token が文字列で、Current が OPERATOR でない場合、Current を削除て
terminalExpression を生成して置き換え |
if (Token == 文字列 && Current != OPERATOR)
{
____delete Current;
____Current = new terminalExpression(文字列)
} |
|
P1'
|
Token が文字列で、Current が OPERATOR の場合、terminalExpression
生成して Operatorの右辺に設定 |
if (Token == 文字列 && Current == OPERATOR)
{
____Current.addRightHandSide(new terminalExpression(文字列));
} |
|
P2
|
Token が OPERATOR の場合、Operator を生成して Current を左辺に設定し、生成したOperator
を Current に設定 |
if (Token == OPERATOR) {
____Operator = new Operator();
____Operator.addLeftHandSide(Current);
____Current = Operator;
} |
|
P3
|
Token が '(' の場合、Current をスタックに積み、bracketExpression を生成して Current に設定 |
if (Token == '(') {
____Stack.Push(Current);
____Current = new bracketExpression();
} |
|
P4
|
Tokenが ')' で、スタックから取り出した Expression が OPERATOR
でない場合、取り出した Expression を削除 |
if (Token == ')') {
____Expression = Stack.Pop();
____if (Expression != OPERATOR)
_delete Expression;
} |
|
P4'
|
Tokenが ')' で、スタックから取り出した Expression が OPERATOR
の場合、取り出した Expression の右辺に Current を設定し、取り出した Expression を Current に設定 |
if (Token == ')') {
____Expression = Stack.Pop();
____if (Expression == OPERATOR)
{
________Expression.addRightHandSide(Current);
________Current = Expression;
____}
} |
ふぅ、ちょっと長かったですが、これくらいの構文でないと、構文解析処理のパターンを把握できませんので、がんばっちゃいました。Token
に応じた処理を STRATEGY にして拡張性を担保してもよかったのですが、クラスが増えすぎて分かりにくくなってしまうので、やめておきます。
このように、構文解析処理にはある一定のパターンがあります。スクリプトなどの複雑な構文も、字句解析エンジンと、構文解析エンジンの助けを借りれば、基本的な処理の流れは、そう大差ありません。いったんマスターをしてしまうと、いろいろな場面で用いることができ、大変便利ですので、がんばってチャレンジしてみてください。
INTERPRETERに関しての話題は、先ほども紹介しましたが、簡単なJavaスクリプトライクなインタープリタの作成を、『jjtree(JavaCC)で、Visitorパターンをマスター』で取り上げていますので、興味のある方はどうぞ。また、『COMPOSITEでDOMもどきにチャレンジ!』で、XMLライク構文の解釈を扱っていますし、『VISITORで四則演算』では、ここで扱った構文解析とまったく同じルーチンで、足し算引き算をする演算器を作成していますので興味のある方は、そちらもどうぞ。
※ GNUのC,C++用、字句解析、構文解析エンジン。JavaCCのようなもの。というよりこっちが元祖。
■ 『INTERPRETER でデータベースエンジンを作っちゃえ!』の構造

■ 『INTERPRETER でデータベースエンジンを作っちゃえ!』 のJavaプログラム・コード
| □ Java2版 |
| InterpreterSample.java |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
import java.io.FileReader;
import java.io.BufferedReader;
public class InterpreterSample {
static private void error(){
System.err.println("java InterpreterSample [database file]");
System.exit(-1);
}
static public void main(String args[]) {
if (args.length != 1) {
error();
}
QueryParser parser = new QueryParser();
BufferedReader console = null;
RandomAccessFile db_file = null;
try {
console = new BufferedReader(new InputStreamReader(System.in));
db_file = new RandomAccessFile(args[0], "r");
while (true) {
// Qurey の生成
System.out.print("> ");
String input = console.readLine();
if (input.equals("end")) {
break;
}
Expression query = parser.parse(input);
if (query == null) {
continue;
}
System.out.println("Query: " + query.dump());
// 検索処理
int n = 0;
String line = null;
boolean found = false;
while ((line = db_file.readLine()) != null) {
++n;
if (query.Interpret(line)) {
System.out.print(" FOUND IN LINE[" + n + "]");
System.out.println(" => " + line);
found = true;
}
}
if (!found) {
System.out.println(" NOT FOUND");
}
// レコード位置を先頭に戻す
db_file.seek(0);
}
} catch (java.io.IOException e) {
e.printStackTrace();
System.err.println(e);
} finally {
try {
if (db_file != null) {
db_file.close();
}
} catch (java.io.IOException e) {
System.err.println(e);
}
}
}
}
|
| QueryParser.java |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
import java.io.StringReader;
import java.io.BufferedReader;
import java.io.StreamTokenizer;
import java.util.LinkedList;
public class QueryParser {
private Expression _cur = null;
private LinkedList _stk = new LinkedList();
public Expression parse(String data) throws java.io.IOException {
BufferedReader reader = new BufferedReader(new StringReader(data));
StreamTokenizer in = new StreamTokenizer(reader);
in.ordinaryChar('/');
try {
Expression exp = null;
_cur = new bracketExpression(); // Initial
while (in.nextToken() != StreamTokenizer.TT_EOF) {
switch (in.ttype) {
case '&':
_cur = new sequenceExpression(_cur);
break;
case '|':
_cur = new alternationExpression(_cur);
break;
case '(':
_stk.add(_cur);
_cur = new bracketExpression();
break;
case ')':
exp = (Expression)_stk.removeLast();
if (exp.isOperator()) {
exp.addRightHandSide(_cur);
_cur = exp;
}
break;
case StreamTokenizer.TT_WORD:
exp = new terminalExpression(in.sval);
if (_cur.isOperator()) {
_cur.addRightHandSide(exp);
} else {
_cur = exp;
}
}
}
} catch (IllegalStateException e) {
e.printStackTrace();
System.err.println(e);
}
if (_stk.size() != 0) {
System.err.println("parse error");
_stk.clear();
_cur = null;
}
return _cur;
}
}
|
| Expression.java |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
abstract public class Expression {
abstract public boolean Interpret(String str);
abstract void addRightHandSide(Expression exp) throws IllegalStateException;
abstract void addLeftHandSide(Expression exp) throws IllegalStateException;
abstract boolean isOperator();
abstract public String dump();
}
|
| terminalExpression.java |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
import java.util.StringTokenizer;
public class terminalExpression extends Expression {
private String _literal = null;
public terminalExpression(String str) {
_literal = str;
}
public boolean Interpret(String str) {
StringTokenizer st = new StringTokenizer(str);
while (st.hasMoreTokens()) {
String test = st.nextToken();
if (test.compareToIgnoreCase(_literal) == 0) {
return true;
}
}
return false;
}
void addRightHandSide(Expression exp) throws IllegalStateException {
throw new IllegalStateException("Invalid call terminalExpression::addRightHandSide");
}
void addLeftHandSide(Expression exp) throws IllegalStateException {
throw new IllegalStateException("Invalid call terminalExpression::addLeftHandSide");
}
boolean isOperator() {
return false;
}
public String dump() {
return _literal;
}
}
|
| bracketExpression.java |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
public class terminalExpression extends Expression {
public boolean Interpret(String str) {
return false;
}
void addRightHandSide(Expression exp) throws IllegalStateException {
throw new IllegalStateException("Invalid call bracketExpression::addRightHandSide");
}
void addLeftHandSide(Expression exp) throws IllegalStateException {
throw new IllegalStateException("Invalid call bracketExpression::addLeftHandSide");
}
boolean isOperator() {
return false;
}
public String dump() {
return '(';
}
}
|
| sequenceExpression.java |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
public class sequenceExpression extends Expression {
private Expression _lhs = null;
private Expression _rhs = null;
public sequenceExpression() { }
public sequenceExpression(Expression lhs) {
_lhs = lhs;
}
void addRightHandSide(Expression exp) {
_rhs = exp;
}
void addLeftHandSide(Expression exp) {
_lhs = exp;
}
boolean isOperator() {
return true;
}
public boolean Interpret(String str) {
return _lhs.Interpret(str) && _rhs.Interpret(str);
}
public String dump() {
return "(" + _lhs.dump() + " & " + _rhs.dump() + ")";
}
}
|
| alternationExpression.java |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
public class alternationExpression extends Expression {
private Expression _lhs = null;
private Expression _rhs = null;
public alternationExpression() { }
public alternationExpression(Expression lhs) {
_lhs = lhs;
}
void addRightHandSide(Expression exp) {
_rhs = exp;
}
void addLeftHandSide(Expression exp) {
_lhs = exp;
}
boolean isOperator() {
return true;
}
public boolean Interpret(String str) {
return _lhs.Interpret(str) || _rhs.Interpret(str);
}
public String dump() {
return "(" + _lhs.dump() + " | " + _rhs.dump() + ")";
}
}
|
■ 『INTERPRETER でお手軽データベースエンジンを作っちゃえ!』 のC++(Win32)プログラム・コード
| □ C++版 |
| InterpreterSample.cpp |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#include "Expression.h"
#include "QueryParser.h"
#include <fstream>
#include <iostream>
#define LINE_BUF 1024
void error() {
std::cerr << "./InterpreterSamle.exe [database file]" << std::endl;
exit(-1);
}
int main(int argc, char* argv[]) {
if (argc != 2) {
error();
}
using namespace std;
ifstream is(argv[1]);
if (!is) {
cerr << "database file not found: " << argv[1] << endl;
error();
}
QueryParser parser;
char buffer[LINE_BUF];
while (true) {
// Query の生成
cout << "> ";
cin.getline(buffer, LINE_BUF);
string input(buffer);
if (input == "end") {
break;
}
Expression* query = parser.parse(input);
if (query == NULL) {
cerr << "parse error!" << endl;
continue;
}
cout << "Query: " << query->dump() << endl;
// 検索処理
int n = 0;
bool found = false;
is.getline(buffer, LINE_BUF);
while (is.good()) {
++n;
string str(buffer);
if (query->Interpret(str)) {
cout << " FOUND IN LINE[" << n << "]";
cout << " => " << str << endl;
found = true;
}
is.getline(buffer, LINE_BUF);
}
is.clear(); // bad()状態をクリア good()状態に
is.seekg(0, ios_base::beg);
if (!found) {
cout << " NOT FOUND" << endl;
}
}
is.close();
}
|
| QueryParser |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#ifndef QUERYPARSER_HEADER_GUARD__
#define QUERYPARSER_HEADER_GUARD__
#include <string>
#include <stack>
class Expression;
class QueryParser {
public:
QueryParser();
~QueryParser();
Expression* parse(const std::string& line);
private:
void clearStack();
Expression* cur_;
std::stack<Expression*> stk_;
};
#endif // QUERYPARSER_HEADER_GUARD__
|
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#include "QueryParser.h"
#include "sequenceExpression.h"
#include "alternationExpression.h"
#include "terminalExpression.h"
#include "bracketExpression.h"
#include <fstream>
#include <sstream>
#include <iostream>
QueryParser::QueryParser() : cur_(NULL) { }
QueryParser::~QueryParser() {
cur_ = NULL;
clearStack();
}
Expression* QueryParser::parse(const std::string& line) {
using namespace std;
istringstream is(line);
string token;
try {
Expression* exp = NULL;
cur_ = new bracketExpression();
while (is >> token) {
if (token == "&") {
cur_ = new sequenceExpression(cur_);
} else if (token == "|") {
cur_ = new alternationExpression(cur_);
} else if (token == "(") {
stk_.push(cur_);
cur_ = new bracketExpression();
} else if (token == ")") {
exp = stk_.top();
if (exp->isOperator()) {
exp->addRightHandSide(cur_);
cur_ = exp;
} else {
delete exp;
}
stk_.pop();
} else {
exp = new terminalExpression(token);
if (cur_->isOperator()) {
cur_->addRightHandSide(exp);
} else {
delete cur_;
cur_ = exp;
}
}
}
} catch (invalid_argument& e) {
cerr << "exception: " << e.what() << endl;
clearStack();
if (cur_ != NULL) {
delete cur_;
cur_ = NULL;
}
}
if (!stk_.empty()) {
cerr << "parse error. " << endl;
clearStack();
if (cur_ != NULL) {
delete cur_;
cur_ = NULL;
}
}
return cur_;
}
void QueryParser::clearStack() {
while (!stk_.empty()) {
Expression* exp = stk_.top();
delete exp;
stk_.pop();
}
}
|
| Expression |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#ifndef EXPRESSION_HEADER_GUARD__
#define EXPRESSION_HEADER_GUARD__
#include <string>
#include <stdexcept>
class Expression {
public:
Expression();
virtual ~Expression();
virtual bool Interpret(const std::string& str) const = 0;
virtual std::string dump() const = 0;
protected:
friend class QueryParser;
virtual void addRightHandSide(Expression* exp) throw (std::invalid_argument) = 0;
virtual void addLeftHandSide(Expression* exp) throw (std::invalid_argument) = 0;
virtual bool isOperator() const = 0;
};
#endif // EXPRESSION_HEADER_GUARD__
|
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#include "Expression.h"
Expression::Expression() { }
Expression::~Expression() { }
|
| terminalExpression |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#ifndef TERMINALEXPRESSION_HEADER_GUARD__
#define TERMINALEXPRESSION_HEADER_GUARD__
#include "Expression.h"
class terminalExpression : public Expression {
public:
terminalExpression(const std::string& literal);
virtual ~terminalExpression();
virtual bool Interpret(const std::string& str) const;
virtual std::string dump() const;
protected:
virtual void addRightHandSide(Expression* exp) throw (std::invalid_argument);
virtual void addLeftHandSide(Expression* exp) throw (std::invalid_argument);
virtual bool isOperator() const;
bool cmp_ignorecase(const std::string& s0, const std::string& s1) const;
private:
std::string literal_;
};
#endif // TERMINALEXPRESSION_HEADER_GUARD__
|
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#include "terminalExpression.h"
#include <sstream>
#include <iostream>
terminalExpression::terminalExpression(const std::string& literal) :
literal_(literal) { }
terminalExpression::~terminalExpression() { }
bool terminalExpression::Interpret(const std::string& str) const {
using namespace std;
istringstream is(str);
string token;
while (is >> token) {
if (cmp_ignorecase(token,literal_)) {
return true;
}
}
return false;
}
void terminalExpression::addRightHandSide(Expression* exp) throw (std::invalid_argument) {
throw std::invalid_argument("invalid call terminalExpression::addRightHandSide()");
}
void terminalExpression::addLeftHandSide(Expression* exp) throw (std::invalid_argument) {
throw std::invalid_argument("invalid call terminalExpression::addLeftHandSide()");
}
bool terminalExpression::isOperator() const {
return false;
}
std::string terminalExpression::dump() const {
return literal_;
}
bool terminalExpression::cmp_ignorecase(const std::string& s0, const std::string& s1) const {
using namespace std;
string::const_iterator it0 = s0.begin();
string::const_iterator it1 = s1.begin();
while (it0 != s0.end() && it1 != s1.end()) {
if (toupper(*it0) != toupper(*it1)) {
return false;
}
++it0;
++it1;
}
return true;
}
|
| bracketExpression |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#ifndef BRACKETEXPRESSION_HEADER_GUARD__
#define BRACKETEXPRESSION_HEADER_GUARD__
#include "Expression.h"
class bracketExpression : public Expression {
public:
bracketExpression();
virtual ~bracketExpression();
virtual bool Interpret(const std::string& str) const;
virtual std::string dump() const;
protected:
virtual void addRightHandSide(Expression* exp) throw (std::invalid_argument);
virtual void addLeftHandSide(Expression* exp) throw (std::invalid_argument);
virtual bool isOperator() const;
};
#endif // BRACKETEXPRESSION_HEADER_GUARD__
|
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#include "bracketExpression.h"
bracketExpression::bracketExpression() { }
bracketExpression::~bracketExpression() { }
bool bracketExpression::Interpret(const std::string& str) const {
return false;
}
void bracketExpression::addRightHandSide(Expression* exp) throw (std::invalid_argument) {
throw std::invalid_argument("invalid call bracketExpression::addRightHandSide()");
}
void bracketExpression::addLeftHandSide(Expression* exp) throw (std::invalid_argument) {
throw std::invalid_argument("invalid call bracketExpression::addLeftHandSide()");
}
bool bracketExpression::isOperator() const {
return false;
}
std::string terminalExpression::dump() const {
return "(";
}
|
| sequenceExpression |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#ifndef SEQUENCEEXPRESSION_HEADER_GUARD__
#define SEQUENCEEXPRESSION_HEADER_GUARD__
#include "Expression.h"
class sequenceExpression : public Expression {
public:
sequenceExpression();
sequenceExpression(Expression* lhs);
virtual ~sequenceExpression();
virtual bool Interpret(const std::string& str) const;
virtual std::string dump() const;
protected:
friend class QueryParser;
virtual void addRightHandSide(Expression* exp) throw (std::invalid_argument);
virtual void addLeftHandSide(Expression* exp) throw (std::invalid_argument);
virtual bool isOperator() const;
private:
Expression* lhs_;
Expression* rhs_;
};
#endif // SEQUENCEEXPRESSION_HEADER_GUARD__
|
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#include "sequenceExpression.h"
sequenceExpression::sequenceExpression() : lhs_(NULL), rhs_(NULL) { }
sequenceExpression::sequenceExpression(Expression* lhs) : lhs_(lhs), rhs_(NULL) { }
sequenceExpression::~sequenceExpression() {
if (lhs_ != NULL) delete lhs_;
if (rhs_ != NULL) delete rhs_;
}
void sequenceExpression::addRightHandSide(Expression* exp) throw (std::invalid_argument) {
rhs_ = exp;
}
void sequenceExpression::addLeftHandSide(Expression* exp) throw (std::invalid_argument) {
lhs_ = exp;
}
bool sequenceExpression::isOperator() const {
return true;
}
bool sequenceExpression::Interpret(const std::string& str) const {
return lhs_->Interpret(str) && rhs_->Interpret(str);
}
std::string sequenceExpression::dump() const {
std::string tmp("");
tmp += "(";
tmp += lhs_->dump();
tmp += " & ";
tmp += rhs_->dump();
tmp += ")";
return tmp;
}
|
| alternationExpression |
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#define ALTERNATIONEXPRESSION_HEADER_GUARD__
#include "Expression.h"
class alternationExpression : public Expression {
public:
alternationExpression();
alternationExpression(Expression* lhs);
virtual ~alternationExpression();
virtual bool Interpret(const std::string& str) const;
virtual std::string dump() const;
protected:
friend class QueryParser;
virtual void addRightHandSide(Expression* exp) throw (std::invalid_argument);
virtual void addLeftHandSide(Expression* exp) throw (std::invalid_argument);
virtual bool isOperator() const;
private:
Expression* lhs_;
Expression* rhs_;
};
#endif // ALTERNATIONEXPRESSION_HEADER_GUARD__
|
// Copyright(C) 2004 Yoshinori Oota All rights reserved.
#include "alternationExpression.h"
alternationExpression::alternationExpression() : lhs_(NULL), rhs_(NULL) { }
alternationExpression::alternationExpression(Expression* lhs) : lhs_(lhs), rhs_(NULL) { }
alternationExpression::~alternationExpression() {
if (lhs_ != NULL) delete lhs_;
if (rhs_ != NULL) delete rhs_;
}
void alternationExpression::addRightHandSide(Expression* exp) throw (std::invalid_argument) {
rhs_ = exp;
}
void alternationExpression::addLeftHandSide(Expression* exp) throw (std::invalid_argument) {
lhs_ = exp;
}
bool sequenceExpression::isOperator() const {
return true;
}
bool alternationExpression::Interpret(const std::string& str) const {
return lhs_->Interpret(str) || rhs_->Interpret(str);
}
std::string alternationExpression::dump() const {
std::string tmp("");
tmp += "(";
tmp += lhs_->dump();
tmp += " | ";
tmp += rhs_->dump();
tmp += ")";
return tmp;
}
|
【補足】
・C++版では、本来はコピーコンストラクタ、代入演算子を定義すべきですが、コードが冗長になるため省いています。
|