💡
【No.4】実行部をつくる
はやく完成させる
「実行」といっても、ただ入力されたコードのPrimitiveCommand("GO","RIGHT","LEFT")が文字列のリストとして返ってくるだけです。
ややショボすぎる気はしますが、当面は「はやく完成させる」ことを目的にして、さっさと完成させてしまいます。
実装
構文解析で作成したクラスに、evalメソッドを追加しました。
特に大したことはしていないのですが、一応、下に実装を配置します。
AstNode
package parser;
import lexer.Lexer;
import java.util.*;
interface AstNode {
AstNode parse(Lexer lexer);
void eval(List<String> list);
}
ProgramNode
package parser;
import java.util.List;
import lexer.Lexer;
class ProgramNode implements AstNode{
private AstNode child;
@Override
public AstNode parse(Lexer lexer) {
lexer.nextToken();
child = new CommandList().parse(lexer);
return this;
}
@Override
public void eval(List<String> list) {
child.eval(list);
}
}
CommandList
package parser;
import lexer.Lexer;
import java.util.*;
public class CommandList implements AstNode{
private List<AstNode> children = new ArrayList<>();
@Override
public AstNode parse(Lexer lexer) {
while(! lexer.getToken().equals("END")) {
AstNode child = new Command().parse(lexer);
children.add(child);
lexer.nextToken();
}
return this;
}
@Override
public void eval(List<String> list) {
children.forEach(x -> x.eval(list));
}
}
Command
package parser;
import java.util.List;
import lexer.Lexer;
public class Command implements AstNode {
private AstNode child;
@Override
public AstNode parse(Lexer lexer) {
if(lexer.getToken().equals("REPEAT")) {
child = new RepeatCommand().parse(lexer);
}
else if(lexer.getToken().equals("GO")|
lexer.getToken().equals("RIGHT")|
lexer.getToken().equals("LEFT")
) {
child = new PrimitiveCommand().parse(lexer);
}
return this;
}
@Override
public void eval(List<String> list) {
child.eval(list);
}
}
RepeatCommand
package parser;
import java.util.List;
import lexer.Lexer;
public class RepeatCommand implements AstNode {
private int number;
private AstNode child;
@Override
public AstNode parse(Lexer lexer) {
lexer.nextToken();
number = Integer.parseInt(lexer.getToken());
lexer.nextToken();
child = new CommandList().parse(lexer);
return this;
}
@Override
public void eval(List<String> list) {
for(int i=0 ;i<number ;i++) {
child.eval(list);
}
}
}
PrimitiveCommand
package parser;
import java.util.List;
import lexer.Lexer;
public class PrimitiveCommand implements AstNode {
private String command;
@Override
public AstNode parse(Lexer lexer) {
command = lexer.getToken();
return this;
}
@Override
public void eval(List<String> list) {
list.add(command);
}
}
課題
今回&前回の課題(?)としては、テスト駆動で開発しなかったことです。
この構文解析器&実行部の特徴は、クラス間の結合度が高く、かつ各クラスが小さいことです。
この場合、単体テストなしで実装したほうが速そうな気がしたので、テスト駆動は実施しませんでした。
どのような条件ではテスト駆動が有利になり、逆に不利になるのか、すこし学習してみたいと思います。
あとがき
一応、処理系はこれで完成です。
ですが、これで終了なのではなく、ここからがスタートになります。
「始まりのおわり」(チャーチル)ですね。
Discussion