残り時間 1.5 時間
たぶん出来ない。
定義
終端は、 /^/ で囲んで、 非終端は文字列で表現する。
var y = { add: [ ['mul'], ['add', /^\+/, 'mul'], ['add', /^-/, 'mul'] ], mul: [ ['una'], ['mul', /^\*/, 'una'], ['mul', /\//, 'una'] ], una:[ ['pri'], [/^\+/, 'una'], [/^-/, 'una'] ], pri: [/^1/], [/^\(/, 'add', /^\)/] ] };
これから、状態遷移図を作る
あー。ハッシュだとどれがstart かわからんな><
やっぱり $accept は手動で入れることにする。 $end は /^$/ で表現する。
var y = { $accept: [ ['add', /^$/] ], add: [ ['mul'], ['add', /^\+/, 'mul'], ['add', /^-/, 'mul'] ], mul: [ ['una'], ['mul', /^\*/, 'una'], ['mul', /\//, 'una'] ], una:[ ['pri'], [/^\+/, 'una'], [/^-/, 'una'] ], pri: [ [/^1/], [/^\(/, 'add', /^\)/] ] };
終端、非終端を抽出
var terms = []; var nterms = []; var tSeen = {}; var nSeen = {}; for (var n in y) { y[n].forEach(function(rule) { rule.forEach(function(t) { if (t instanceof RegExp) { if (tSeen[t] != true) { terms.push(t); tSeen[t] = true; } } else { if (nSeen[t] != true) { nterms.push(t); nSeen[t] = true; } } }); }); } console.log(terms, nterms); // 終端、非終端
ルールオブジェクトを作る
function Rule(nterm, data) { this.nterm = nterm; this.data = data; this.first = data[0]; }
さっきの過程で、 Rule も生成するようにする。
var terms = []; var nterms = []; var tSeen = {}; var nSeen = {}; var ntermRuleMap = {}; var rules = []; for (var n in y) { ntermRuleMap[n] = []; y[n].forEach(function(rule) { rule.forEach(function(t) { if (t instanceof RegExp) { if (tSeen[t] != true) { terms.push(t); tSeen[t] = true; } } else { if (nSeen[t] != true) { nterms.push(t); nSeen[t] = true; } } }); var r = new Rule(n, rule); rules.push(r); ntermRuleMap[n].push(r); }); } console.log(terms, nterms, rules, ntermRuleMap);
こんな感じ
非終端の最初に出現する非終端をまとめる
var ntermDeriveMap = {}; nterms.forEach(function(nterm) { ntermDeriveMap[nterm] = {}; ntermRuleMap[nterm].forEach(function(rule) { if (!(rule.first instanceof RegExp)) { ntermDeriveMap[nterm][rule.first] = true; } }); }); console.log(ntermDeriveMap);
よしよし
推移閉包を求める
nterms.forEach(function(nterm0) { for (var nterm1 in ntermDeriveMap[nterm0]) { nterms.forEach(function(nterm2) { if (ntermDeriveMap[nterm2][nterm0]) { ntermDeriveMap[nterm2][nterm1] = true; } }); } }); console.log(ntermDeriveMap);
おk
ルールを網羅的にしたようなものを作る
var ntermDeriveRuleMap = {}; nterms.forEach(function(nterm) { ntermDeriveRuleMap[nterm] = []; rules.forEach(function(rule) { if (ntermDeriveMap[nterm][rule.nterm]) { ntermDeriveRuleMap[nterm].push(rule); } }); }); console.log(ntermDeriveRuleMap);
おk
あと 30 分
・・・ごくり
状態を作る
State オブジェクトを定義しておく
function State() { this.mainRulesData = []; this.reductions = []; this.rulesAndPositions = []; this.rulesAndPositionsSeen = {}; this.termTransitions = {}; this.ntermTransitions = {}; this.rrConflict = false; this.srConflict = false; this.containReduction = false; } State.all = []; State.hash = {}; State.prototype.add = function(rule, position) { if (this.rulesAndPositionsSeen[rule.id + '-' + position]) { return; } this.rulesAndPositionsSeen[rule.id + '-' + position] = true; this.mainRulesData.push(rule.id + '-' + position); if (rule.data.length == position) { if (this.containReduction) { this.rrConflict = true; } this.containReduction = true; this.reductions.push(rule); } else if (this.containReduction) { this.srConflict = true; } this.rulesAndPositions.push([rule, position]); if (rule.data[position] != undefined && !(rule.data[position] instanceof RegExp)) { var nterm = rule.data[position]; var self = this; ntermDeriveRuleMap[nterm].forEach(function(rule) { if (!self.rulesAndPositionsSeen[rule.id + '-' + 0]) { self.rulesAndPositionsSeen[rule.id + '-' + 0] = true; self.rulesAndPositions.push([rule, 0]); } }); } }; State.prototype.createTransitions = function() { State.all.push(this); this.id = this.mainRulesData.sort().join(':'); State.hash[this.id] = this; console.log(State.hash); var termMap = {}; var ntermMap = {}; this.rulesAndPositions.forEach(function(ruleAndPositions) { var rule = ruleAndPositions[0]; var position = ruleAndPositions[1]; if (rule.data.length > position) { if (rule.data[position] instanceof RegExp) { termMap[rule.data[position]] = termMap[rule.data[position]] || []; termMap[rule.data[position]].push([rule, position + 1]); } else { ntermMap[rule.data[position]] = ntermMap[rule.data[position]] || []; ntermMap[rule.data[position]].push([rule, position + 1]); } } }); console.log(termMap, ntermMap); for (var n in termMap) { var state = State.hash[termMap[n].map(function(rp) { return rp[0].id + '-' + rp[1] }).sort().join(':')]; if (!state) { state = new State(); var rps = termMap[n]; rps.forEach(function(rp) { state.add(rp[0], rp[1]); }); state.createTransitions(); } this.termTransitions[n] = state; } for (var n in ntermMap) { var state = State.hash[ntermMap[n].map(function(rp) { return rp[0].id + '-' + rp[1] }).sort().join(':')]; if (!state) { state = new State(); var rps = ntermMap[n]; rps.forEach(function(rp) { state.add(rp[0], rp[1]); }); state.createTransitions(); } this.ntermTransitions[n] = state; } };
これで
var firstRule = ntermRuleMap['$accept'][0]; var firstState = new State(); firstState.add(firstRule, 0); firstState.createTransitions(); console.log(firstState);
とすれば、再帰的にすべての状態が作られるはず
console.log(State.all.length); // 22
ちゃんと 22 個の状態が出来た
ここで時間だ
先読み終端の検出は後で
ここまでの全ソース
<script >
var y = {
$accept: [
['add', /^$/]
],
add: [
['mul'],
['add', /^\+/, 'mul'],
['add', /^-/, 'mul']
],
mul: [
['una'],
['mul', /^\*/, 'una'],
['mul', /\//, 'una']
],
una:[
['pri'],
[/^\+/, 'una'],
[/^-/, 'una']
],
pri: [
[/^1/],
[/^\(/, 'add', /^\)/]
]
};
function Rule(nterm, data) {
this.nterm = nterm;
this.data = data;
this.first = data[0];
this.id = Rule.id++;
}
Rule.id = 0;
function State() {
this.mainRulesData = [];
this.reductions = [];
this.rulesAndPositions = [];
this.rulesAndPositionsSeen = {};
this.termTransitions = {};
this.ntermTransitions = {};
this.rrConflict = false;
this.srConflict = false;
this.containReduction = false;
}
State.all = [];
State.hash = {};
State.prototype.add = function(rule, position) {
if (this.rulesAndPositionsSeen[rule.id + '-' + position]) {
return;
}
this.rulesAndPositionsSeen[rule.id + '-' + position] = true;
this.mainRulesData.push(rule.id + '-' + position);
if (rule.data.length == position) {
if (this.containReduction) {
this.rrConflict = true;
}
this.containReduction = true;
this.reductions.push(rule);
}
else if (this.containReduction) {
this.srConflict = true;
}
this.rulesAndPositions.push([rule, position]);
if (rule.data[position] != undefined && !(rule.data[position] instanceof RegExp)) {
var nterm = rule.data[position];
var self = this;
ntermDeriveRuleMap[nterm].forEach(function(rule) {
if (!self.rulesAndPositionsSeen[rule.id + '-' + 0]) {
self.rulesAndPositionsSeen[rule.id + '-' + 0] = true;
self.rulesAndPositions.push([rule, 0]);
}
});
}
};
State.prototype.createTransitions = function() {
State.all.push(this);
this.id = this.mainRulesData.sort().join(':');
State.hash[this.id] = this;
console.log(State.hash);
var termMap = {};
var ntermMap = {};
this.rulesAndPositions.forEach(function(ruleAndPositions) {
var rule = ruleAndPositions[0];
var position = ruleAndPositions[1];
if (rule.data.length > position) {
if (rule.data[position] instanceof RegExp) {
termMap[rule.data[position]] = termMap[rule.data[position]] || [];
termMap[rule.data[position]].push([rule, position + 1]);
}
else {
ntermMap[rule.data[position]] = ntermMap[rule.data[position]] || [];
ntermMap[rule.data[position]].push([rule, position + 1]);
}
}
});
console.log(termMap, ntermMap);
for (var n in termMap) {
var state = State.hash[termMap[n].map(function(rp) { return rp[0].id + '-' + rp[1] }).sort().join(':')];
if (!state) {
state = new State();
var rps = termMap[n];
rps.forEach(function(rp) {
state.add(rp[0], rp[1]);
});
state.createTransitions();
}
this.termTransitions[n] = state;
}
for (var n in ntermMap) {
var state = State.hash[ntermMap[n].map(function(rp) { return rp[0].id + '-' + rp[1] }).sort().join(':')];
if (!state) {
state = new State();
var rps = ntermMap[n];
rps.forEach(function(rp) {
state.add(rp[0], rp[1]);
});
state.createTransitions();
}
this.ntermTransitions[n] = state;
}
};
var terms = [];
var nterms = [];
var tSeen = {};
var nSeen = {};
var ntermRuleMap = {};
var rules = [];
for (var n in y) {
if (nSeen[n] != true) {
nterms.push(n);
nSeen[n] = true;
}
ntermRuleMap[n] = [];
y[n].forEach(function(rule) {
rule.forEach(function(t) {
if (t instanceof RegExp) {
if (tSeen[t] != true) {
terms.push(t);
tSeen[t] = true;
}
}
});
var r = new Rule(n, rule);
rules.push(r);
ntermRuleMap[n].push(r);
});
}
console.log(terms, nterms, rules, ntermRuleMap);
var ntermDeriveMap = {};
nterms.forEach(function(nterm) {
ntermDeriveMap[nterm] = {};
ntermRuleMap[nterm].forEach(function(rule) {
if (!(rule.first instanceof RegExp)) {
ntermDeriveMap[nterm][rule.first] = true;
}
});
});
console.log(ntermDeriveMap);
nterms.forEach(function(nterm0) {
for (var nterm1 in ntermDeriveMap[nterm0]) {
nterms.forEach(function(nterm2) {
if (ntermDeriveMap[nterm2][nterm0]) {
ntermDeriveMap[nterm2][nterm1] = true;
}
});
}
ntermDeriveMap[nterm0][nterm0] = true;
});
console.log(ntermDeriveMap);
var ntermDeriveRuleMap = {};
nterms.forEach(function(nterm) {
ntermDeriveRuleMap[nterm] = [];
rules.forEach(function(rule) {
if (ntermDeriveMap[nterm][rule.nterm]) {
ntermDeriveRuleMap[nterm].push(rule);
}
});
});
console.log(ntermDeriveRuleMap);
var firstRule = ntermRuleMap['$accept'][0];
var firstState = new State();
firstState.add(firstRule, 0);
firstState.createTransitions();
console.log(firstState);
console.log(State.all.length);
</script>
続き
トークンの先読み。
State.all.forEach(function(state0) {
if (state0.srConflict || state0.ssConflict) {
State.all.forEach(function(state1) {
state0.reductions.forEach(function(red) {
if (state1.ntermTransitions[red[0].nterm]) {
for (var n in state1.ntermTransitions[red[0].nterm].termTransitions) {
if (!red[2][n]) {
red[1].push(eval(n)); // dirty >_<
red[2][n] = true;
}
}
}
});
});
}
});
console.log(firstState);できてるっぽい