主要コンポネント
檜山正幸
Thu Sep 28 2006
1. コンポネント
- VSTLexerCmp : Pull型のレクサー
- VSTParserCmp : Push型のパーザー
- VSTProcessorCmp : テンプレート処理系
- VSTMainCmp : 全体をまとめる
/* VSTLexerCmp.java */
// VerySimpleTemplatesのレクサー・コンポネント
package sample;
import java.io.*;
/* レクサー(字句解析系)*/
public class VSTLexerCmp implements VSTLexerPI {
/* -- componet ports */
/* @Provide */
public final VSTLexerPI lexer;
/* @Require */
public void setInput(Reader reader) {
input = new PushbackReader(reader);
}
/* @Require */
public void setInput(InputStream stream) {
setInput(new InputStreamReader(stream));
}
/* -- end pors */
private PushbackReader input;
public VSTLexerCmp() {
lexer = this;
}
public VSTToken nextToken() throws IOException {
int c = input.read();
switch (c) {
case -1:
return new VSTToken(VSTToken.Kind.EOF, "<EOF>");
case '{':
int c2 = input.read(); // 1文字先読み
if (c2 == '{') // エスケープされた左波括弧
return new VSTToken(VSTToken.Kind.TEXT, "{");
if (c2 != -1) input.unread((char)c2); // 先読み分を戻す
return new VSTToken(VSTToken.Kind.L_BRACE, "<L_BRACE>");
case '}':
return new VSTToken(VSTToken.Kind.R_BRACE, "<R_BRACE>");
default:
input.unread((char)c);
return new VSTToken(VSTToken.Kind.TEXT, readText());
}
}
// EOF、または'{'か'}'が出現するまでテキストを読む
private String readText() throws IOException {
StringBuilder sb = new StringBuilder();
while (true) {
int c = input.read();
if (c == -1 ) {
break;
}
if (c == '{' || c == '}') {
input.unread((char)c);
break;
}
sb.append((char)c);
}
return sb.toString();
}
}
/* VSTParserCmp.java */
// VerySimpleTemplatesのパーザー・コンポネント
package sample;
import java.io.*;
/* パーザー */
class VSTParserCmp implements VSTParserPI {
/* -- component ports */
// @Provide
public final VSTParserPI parser;
// @Require(Occurence.OPTIONAL)
public VSTHandler handler;
// @Require
public VSTLexerPI lexer;
/* -- end ports */
public VSTParserCmp() {
parser = this;
handler = new PrintHandler(); // デフォルト
}
private void signalError(String msg) throws VSTParseException {
handler.error(msg);
throw new VSTParseException(msg);
}
public void parse() throws IOException, VSTParseException {
boolean withinPlaceholder = false; // 状態
StringBuilder placeholderContent = new StringBuilder(); // テキスト蓄積用
handler.startTemplate();
while (true) {
VSTToken tok = lexer.nextToken();
switch (tok.kind) {
case L_BRACE:
if (withinPlaceholder)
signalError("unexpected left-brace - nested placeholder.");
withinPlaceholder = true;
break;
case R_BRACE:
if (!withinPlaceholder) {
handler.text("}"); // 単なるテキスト扱い
} else { // withinPlaceholder
if (placeholderContent.length() == 0)
signalError("empty placeholder content.");
handler.placeholder(placeholderContent.toString());
withinPlaceholder = false;
// プレイスホルダー内容をクリア
placeholderContent.delete(0, placeholderContent.length());
}
break;
case TEXT:
if (withinPlaceholder) {
placeholderContent.append(tok.value); // 蓄積
} else {
handler.text(tok.value); // すぐにはき出す
}
break;
case EOF:
if (withinPlaceholder)
signalError("unexpected end-of-file - placeholder is not closed.");
handler.endTemplate();
return; // 脱出・終了
default:
assert false; // アリエナーイ
}
}
}
}
/* VSTProcessorCmp.java */
package sample;
import java.io.*;
import java.util.*;
import sample.model.*;
public class VSTProcessorCmp implements VSTProcessorPI {
/* -- component ports */
// @Provide
public final VSTProcessorPI processor;
// @Require
public List<Node> model;
// @Require
public Evaluator evaluator;
// @Require
public void setOutput(Writer writer) {
output = new PrintWriter(writer);
}
// @Require
public void setOutput(OutputStream stream) {
setOutput(new OutputStreamWriter(stream));
}
/* -- end ports */
private PrintWriter output;
public VSTProcessorCmp() {
processor = this;
}
public void process() {
for (Node node : model) {
if (node instanceof TextNode) {
output.print(node.value);
} else if (node instanceof PlaceholderNode) {
String val = evaluator.eval(node.value);
if (val != null) {
output.print(val);
} else {
output.print(node.value);
}
}
output.flush();
}
}
}
/* VSTMainCmp.java */
package sample;
import java.io.*;
import java.util.*;
import sample.model.*;
public class VSTMainCmp implements Startable {
/* -- component ports */
// @Provide
public final Startable starter;
// @Require(Occurence.OPTIONAL)
public String inputName = null;
// @Require(Occurence.OPTIONAL)
public String outputName = null;
// @Require
public Evaluator evaluator;
// @Require
public ExitReportHandler reportHandler;
/* -- end ports */
public VSTMainCmp() {
starter = this;
}
public void start() throws CannotStartException {
/* ========== 第一段階 ========== */
// inputの準備
Reader input = null;
if (inputName == null) {
input = new InputStreamReader(System.in);
} else {
try {
input = new FileReader(inputName);
} catch (Exception e) {
reportHandler.aborted(1, e.toString(), e);
return;
}
}
// parserの準備
VSTParserPI parser;
// resultの準備
Result<List<Node>> result;
// 生成
VSTLexerCmp lexerCmp = new VSTLexerCmp();
VSTParserCmp parserCmp = new VSTParserCmp();
ModelBuilder builder = new ModelBuilder();
// ワイヤリング
lexerCmp.setInput(input);
parserCmp.lexer = lexerCmp.lexer;
parserCmp.handler = builder.handler;
parser = parserCmp.parser;
result = builder.result;
// 実行
try {
parser.parse();
} catch (Exception e) {
reportHandler.aborted(1, e.toString(), e);
return;
}
// 結果取得
if (!result.isAvailable()) {
reportHandler.aborted(1, "Unknown error.", null);
return;
}
List<Node> model = null;
try {
model = result.result();
} catch (Exception e) {
reportHandler.aborted(1, e.toString(), e);
return;
}
/* ========== 第二段階 ========== */
// outputの準備
PrintStream output = null;
if (outputName == null) {
output = System.out;
} else {
try {
output = new PrintStream(new File(outputName));
} catch (Exception e) {
reportHandler.aborted(1, e.toString(), e);
return;
}
}
// processorの準備
VSTProcessorPI processor;
// 生成
VSTProcessorCmp processorCmp = new VSTProcessorCmp();
// ワイヤリング
processorCmp.model = model;
processorCmp.evaluator = evaluator;
processorCmp.setOutput(output);
processor = processorCmp.processor;
// 実行
try {
processor.process();
} catch (Exception e) {
reportHandler.aborted(1, e.toString(), e);
return;
}
// 後始末
try {
input.close();
output.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
reportHandler.finished(0);
return;
}
}