💡

【No.4】実行部をつくる

2023/02/12に公開

はやく完成させる

「実行」といっても、ただ入力されたコードの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