/
Matcher.js
100 lines (81 loc) · 2.96 KB
/
Matcher.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
'use strict';
// --------------------------------------------------------------------
// Imports
// --------------------------------------------------------------------
const MatchState = require('./MatchState');
const pexprs = require('./pexprs');
// --------------------------------------------------------------------
// Private stuff
// --------------------------------------------------------------------
function Matcher(grammar) {
this.grammar = grammar;
this.memoTable = [];
this.input = '';
}
Matcher.prototype.getInput = function() {
return this.input;
};
Matcher.prototype.setInput = function(str) {
if (this.input !== str) {
this.replaceInputRange(0, this.input.length, str);
}
return this;
};
Matcher.prototype.replaceInputRange = function(startIdx, endIdx, str) {
const currentInput = this.input;
if (
startIdx < 0 ||
startIdx > currentInput.length ||
endIdx < 0 ||
endIdx > currentInput.length ||
startIdx > endIdx
) {
throw new Error('Invalid indices: ' + startIdx + ' and ' + endIdx);
}
// update input
this.input = currentInput.slice(0, startIdx) + str + currentInput.slice(endIdx);
// update memo table (similar to the above)
const restOfMemoTable = this.memoTable.slice(endIdx);
this.memoTable.length = startIdx;
for (let idx = 0; idx < str.length; idx++) {
this.memoTable.push(undefined);
}
restOfMemoTable.forEach(function(posInfo) {
this.memoTable.push(posInfo);
}, this);
// Invalidate memoRecs
for (let pos = 0; pos < startIdx; pos++) {
const posInfo = this.memoTable[pos];
if (posInfo) {
posInfo.clearObsoleteEntries(pos, startIdx);
}
}
return this;
};
Matcher.prototype.match = function(optStartApplicationStr) {
return this._match(this._getStartExpr(optStartApplicationStr), false);
};
Matcher.prototype.trace = function(optStartApplicationStr) {
return this._match(this._getStartExpr(optStartApplicationStr), true);
};
Matcher.prototype._match = function(startExpr, tracing, optPositionToRecordFailures) {
const state = new MatchState(this, startExpr, optPositionToRecordFailures);
return tracing ? state.getTrace() : state.getMatchResult();
};
/*
Returns the starting expression for this Matcher's associated grammar. If `optStartApplicationStr`
is specified, it is a string expressing a rule application in the grammar. If not specified, the
grammar's default start rule will be used.
*/
Matcher.prototype._getStartExpr = function(optStartApplicationStr) {
const applicationStr = optStartApplicationStr || this.grammar.defaultStartRule;
if (!applicationStr) {
throw new Error('Missing start rule argument -- the grammar has no default start rule.');
}
const startApp = this.grammar.parseApplication(applicationStr);
return new pexprs.Seq([startApp, pexprs.end]);
};
// --------------------------------------------------------------------
// Exports
// --------------------------------------------------------------------
module.exports = Matcher;