🦔

【No.7】変数と代入文の追加

2023/02/20に公開

ここでは、変数と代入文を追加します。

BNFの書き換え

まずは、BNFを書き換えますね。

<program> ::= PROGRAM <command list>
<command list> ::= <command>* END | <substitution> END
<substitution> ::= <variable> = <number>
<command> ::= <repeat command> | <primitive command>
<repeat command> ::= REPEAT <number> <command list>
<primitive command> ::= GO | RIGHT | LRFT

変数(variable)は、ENDやGOみたいな予約語ではない文字列とします。
アルファベットから始まり、数字も含められるものとします。
数値しか代入できないものになっています。

変更点

それでは、変更すべき点を列挙します。
・字句解析器・・・トークンのクラスを追加、字句解析のメソッドを変更
・構文解析器・・・代入文を追加

変数名と値を保持する「名前表」(記号表)は、構文解析のクラスに含めました。

実装

変更点の詳細です。

字句解析器

Token.java
package lexer;

public abstract class Token {

	public String getContents() {return "なし";}
	public int getNumber() {return 0;}
	
	//トークンの種類を判別
	public boolean isNumberToken() {return false;}
	public boolean isPrimitiveCommandToken(){return false;}
	public boolean isRepeatToken() {return false;}
	public boolean isEndProgramToken() {return false;}
	public boolean isStartProgramToken() {return false;}
	public boolean isVariableToken() {return false;}
	public boolean isEqualToken(){return false;}
}
VariableToken.java
package lexer;

public class VariableToken extends Token{
	
	private String variableName;
	
	public VariableToken(String code) {
		variableName = code;
	}
	
	@Override
	public boolean isVariableToken() {return true;}
	
	@Override
	public String getContents() {return variableName;}
}
EqualToken.java
package lexer;

public class EqualToken extends Token {
	
	@Override
	public boolean isEqualToken(){return true;}

}

字句解析のメソッドに追加しました。

Analyzer.java
class Analyzer{
   private void categorized() {	
     for(int i=0;i<tokens.length;i++) {
	String token = tokens[i];
	
	/*******************中略***************************/
	
	Pattern equalPattern = Pattern.compile("=");
	Matcher equalMach = equalPattern.matcher(token);
	if(equalMach.find()) {
		EqualToken pc =new EqualToken();
		categorizedTokens.add(pc);
		continue;
	}		
	Pattern variablePattern = Pattern.compile("[A-Za-z][A-Za-z0-9]*");
	Matcher variableMach = variablePattern.matcher(token);
	if(variableMach.find()) {
		VariableToken pc =new VariableToken(token);
		categorizedTokens.add(pc);
		continue;
	}
}

構文解析器

Parser.java
package parser;

import lexer.Lexer;
import java.util.*;

public class Parser {
	//名前表
	private Map<String,Integer> nameTable = new HashMap<>();
	
	
	public AstNode conduct(Lexer lexer) {
		AstNode root = new ProgramNode().parse(lexer);
		return root;
	}
	
	public List<String> doEval(AstNode root){		
		List<String> list = new ArrayList<>();
		root.eval(list,nameTable);
		return list;
	}
	
	public Map<String,Integer> getNameTable() {
		return nameTable;
	}
	
}
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) {
		
		if(lexer.getToken().isVariableToken()) {
			AstNode child = new Substitution().parse(lexer);
			children.add(child);
		}else {		
		while(! lexer.getToken().isEndProgramToken()) {			
			AstNode child = new Command().parse(lexer);
			children.add(child);
			lexer.nextToken();
		}
		}
		return this;
	}
	
	@Override
	public void eval(List<String> list,Map<String,Integer> nameTable) {
		children.forEach(x -> x.eval(list,nameTable));
	}
}
Substitution.java
package parser;

import java.util.*;

import lexer.Lexer;

public class Substitution implements AstNode{
	
	private Integer number;
	private String variableName;
	
	@Override
	public AstNode parse(Lexer lexer) {
		variableName = lexer.getToken().getContents();
		lexer.nextToken();
		lexer.nextToken();
		number = lexer.getToken().getNumber();
		return this;
	}
	
	@Override
	public void eval(List<String> list, Map<String,Integer> nameTable) {
		nameTable.put(variableName,number);
	}
}


課題

構文解析のクラスに、責務が集中してしまっています。
現時点では、以下の3つの責務が集まっています。
・構文解析
・実行
・名前表
ちょっとこれは対応が必要ですね。

Discussion