190 lines
5.6 KiB
JavaScript
190 lines
5.6 KiB
JavaScript
var DIALECTS = require('./dialects');
|
|
var Errors = require('./errors');
|
|
var LANGUAGE_PATTERN = /^\s*#\s*language\s*:\s*([a-zA-Z\-_]+)\s*$/;
|
|
|
|
module.exports = function TokenMatcher(defaultDialectName) {
|
|
defaultDialectName = defaultDialectName || 'en';
|
|
|
|
var dialect;
|
|
var dialectName;
|
|
var activeDocStringSeparator;
|
|
var indentToRemove;
|
|
|
|
function changeDialect(newDialectName, location) {
|
|
var newDialect = DIALECTS[newDialectName];
|
|
if(!newDialect) {
|
|
throw Errors.NoSuchLanguageException.create(newDialectName, location);
|
|
}
|
|
|
|
dialectName = newDialectName;
|
|
dialect = newDialect;
|
|
}
|
|
|
|
this.reset = function () {
|
|
if(dialectName != defaultDialectName) changeDialect(defaultDialectName);
|
|
activeDocStringSeparator = null;
|
|
indentToRemove = 0;
|
|
};
|
|
|
|
this.reset();
|
|
|
|
this.match_TagLine = function match_TagLine(token) {
|
|
if(token.line.startsWith('@')) {
|
|
setTokenMatched(token, 'TagLine', null, null, null, token.line.getTags());
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
this.match_FeatureLine = function match_FeatureLine(token) {
|
|
return matchTitleLine(token, 'FeatureLine', dialect.feature);
|
|
};
|
|
|
|
this.match_ScenarioLine = function match_ScenarioLine(token) {
|
|
return matchTitleLine(token, 'ScenarioLine', dialect.scenario);
|
|
};
|
|
|
|
this.match_ScenarioOutlineLine = function match_ScenarioOutlineLine(token) {
|
|
return matchTitleLine(token, 'ScenarioOutlineLine', dialect.scenarioOutline);
|
|
};
|
|
|
|
this.match_BackgroundLine = function match_BackgroundLine(token) {
|
|
return matchTitleLine(token, 'BackgroundLine', dialect.background);
|
|
};
|
|
|
|
this.match_ExamplesLine = function match_ExamplesLine(token) {
|
|
return matchTitleLine(token, 'ExamplesLine', dialect.examples);
|
|
};
|
|
|
|
this.match_TableRow = function match_TableRow(token) {
|
|
if (token.line.startsWith('|')) {
|
|
// TODO: indent
|
|
setTokenMatched(token, 'TableRow', null, null, null, token.line.getTableCells());
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
this.match_Empty = function match_Empty(token) {
|
|
if (token.line.isEmpty) {
|
|
setTokenMatched(token, 'Empty', null, null, 0);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
this.match_Comment = function match_Comment(token) {
|
|
if(token.line.startsWith('#')) {
|
|
var text = token.line.getLineText(0); //take the entire line, including leading space
|
|
setTokenMatched(token, 'Comment', text, null, 0);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
this.match_Language = function match_Language(token) {
|
|
var match;
|
|
if(match = token.line.trimmedLineText.match(LANGUAGE_PATTERN)) {
|
|
var newDialectName = match[1];
|
|
setTokenMatched(token, 'Language', newDialectName);
|
|
|
|
changeDialect(newDialectName, token.location);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
this.match_DocStringSeparator = function match_DocStringSeparator(token) {
|
|
return activeDocStringSeparator == null
|
|
?
|
|
// open
|
|
_match_DocStringSeparator(token, '"""', true) ||
|
|
_match_DocStringSeparator(token, '```', true)
|
|
:
|
|
// close
|
|
_match_DocStringSeparator(token, activeDocStringSeparator, false);
|
|
};
|
|
|
|
function _match_DocStringSeparator(token, separator, isOpen) {
|
|
if (token.line.startsWith(separator)) {
|
|
var contentType = null;
|
|
if (isOpen) {
|
|
contentType = token.line.getRestTrimmed(separator.length);
|
|
activeDocStringSeparator = separator;
|
|
indentToRemove = token.line.indent;
|
|
} else {
|
|
activeDocStringSeparator = null;
|
|
indentToRemove = 0;
|
|
}
|
|
|
|
// TODO: Use the separator as keyword. That's needed for pretty printing.
|
|
setTokenMatched(token, 'DocStringSeparator', contentType);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
this.match_EOF = function match_EOF(token) {
|
|
if(token.isEof) {
|
|
setTokenMatched(token, 'EOF');
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
this.match_StepLine = function match_StepLine(token) {
|
|
var keywords = []
|
|
.concat(dialect.given)
|
|
.concat(dialect.when)
|
|
.concat(dialect.then)
|
|
.concat(dialect.and)
|
|
.concat(dialect.but);
|
|
var length = keywords.length;
|
|
for(var i = 0, keyword; i < length; i++) {
|
|
var keyword = keywords[i];
|
|
|
|
if (token.line.startsWith(keyword)) {
|
|
var title = token.line.getRestTrimmed(keyword.length);
|
|
setTokenMatched(token, 'StepLine', title, keyword);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
this.match_Other = function match_Other(token) {
|
|
var text = token.line.getLineText(indentToRemove); //take the entire line, except removing DocString indents
|
|
setTokenMatched(token, 'Other', unescapeDocString(text), null, 0);
|
|
return true;
|
|
};
|
|
|
|
function matchTitleLine(token, tokenType, keywords) {
|
|
var length = keywords.length;
|
|
for(var i = 0, keyword; i < length; i++) {
|
|
var keyword = keywords[i];
|
|
|
|
if (token.line.startsWithTitleKeyword(keyword)) {
|
|
var title = token.line.getRestTrimmed(keyword.length + ':'.length);
|
|
setTokenMatched(token, tokenType, title, keyword);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function setTokenMatched(token, matchedType, text, keyword, indent, items) {
|
|
token.matchedType = matchedType;
|
|
token.matchedText = text;
|
|
token.matchedKeyword = keyword;
|
|
token.matchedIndent = (typeof indent === 'number') ? indent : (token.line == null ? 0 : token.line.indent);
|
|
token.matchedItems = items || [];
|
|
|
|
token.location.column = token.matchedIndent + 1;
|
|
token.matchedGherkinDialect = dialectName;
|
|
}
|
|
|
|
function unescapeDocString(text) {
|
|
return activeDocStringSeparator != null ? text.replace("\\\"\\\"\\\"", "\"\"\"") : text;
|
|
}
|
|
};
|