3h-ast
Version:
A simple AST generator.
416 lines (353 loc) • 12.1 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.HA = {}));
}(this, (function (exports) { 'use strict';
var tokenizeDefaults = {
normalSymbols: new Set('()[]{}<>+-*/\\?~!@#$%^&=|,.:;'),
twoCharacterSymbols: new Set(['&&', '||', '??', '==', '>=', '<=', '!=', '<<', '>>', '++', '--', '**', '+=', '-=', '*=', '/=', '%=', '&=', '^=', '|=', '|>', '->', '=>', '..', '//', '/*', '*/']),
threeCharacterSymbols: new Set(['&&=', '||=', '??=', '===', '!==', '<<=', '>>=', '**=', '...', '>>>']),
fourCharacterSymbols: new Set(['>>>=']),
globSymbols: new Set(['"', "'", '`']),
numberCharacters: new Set('0123456789'),
extendedNumberCharacters: new Set('.ABCDEF'),
numberSuffixes: new Set(['D', 'B', 'O', 'H']),
spaceCharacters: new Set(' \r\n\t')
};
/** dts2md break */
/**
* Split source code into tokens
*/
var tokenize = function tokenize(source, options) {
var config = Object.assign({}, tokenizeDefaults, options);
var normalSymbols = config.normalSymbols,
twoCharacterSymbols = config.twoCharacterSymbols,
threeCharacterSymbols = config.threeCharacterSymbols,
fourCharacterSymbols = config.fourCharacterSymbols,
globSymbols = config.globSymbols,
numberCharacters = config.numberCharacters,
extendedNumberCharacters = config.extendedNumberCharacters,
numberSuffixes = config.numberSuffixes,
spaceCharacters = config.spaceCharacters;
var tokens = [];
var tokenBuffer = source[0];
var globFlag = '';
var state = 0
/* ANY */
;
var escapeFlag = false; // init state
if (globSymbols.has(tokenBuffer)) {
globFlag = tokenBuffer;
} else if (spaceCharacters.has(tokenBuffer)) {
state = 1
/* SPACE */
;
} else if (numberCharacters.has(tokenBuffer)) {
state = 2
/* NUMBER */
;
} else if (normalSymbols.has(tokenBuffer)) {
tokens.push(tokenBuffer);
tokenBuffer = '';
}
for (var i = 1; i < source.length; i++) {
var character = source[i];
if (globFlag) {
tokenBuffer += character;
if (escapeFlag) {
escapeFlag = false;
} else {
if (character === globFlag) {
tokens.push(tokenBuffer);
tokenBuffer = '';
globFlag = '';
state = 0
/* ANY */
;
} else {
escapeFlag = character === '\\';
}
}
continue;
}
if (globSymbols.has(character)) {
if (tokenBuffer) {
tokens.push(tokenBuffer);
}
tokenBuffer = character;
globFlag = character;
escapeFlag = false;
continue;
}
if ((state === 2
/* NUMBER */
|| state === 3
/* NUMBER_SUFFIX */
) && numberSuffixes.has(character)) {
tokenBuffer += character;
state = 3
/* NUMBER_SUFFIX */
;
continue;
}
if (state === 0
/* ANY */
&& !tokenBuffer && normalSymbols.has(character) && tokens.length) {
var lastToken = tokens[tokens.length - 1];
if (lastToken.length === 1 && twoCharacterSymbols.has(lastToken + character)) {
tokens[tokens.length - 1] += character;
continue;
}
if (lastToken.length === 2 && threeCharacterSymbols.has(lastToken + character)) {
tokens[tokens.length - 1] += character;
continue;
}
if (lastToken.length === 3 && fourCharacterSymbols.has(lastToken + character)) {
tokens[tokens.length - 1] += character;
continue;
}
}
var flag = 0
/* ANY */
;
if (spaceCharacters.has(character)) {
flag = 1
/* SPACE */
;
} else if (numberCharacters.has(character) || state === 2
/* NUMBER */
&& extendedNumberCharacters.has(character) && !tokenBuffer.includes('.')) {
flag = 2
/* NUMBER */
;
} else if (normalSymbols.has(character)) {
if (tokenBuffer) {
tokens.push(tokenBuffer);
}
tokens.push(character);
tokenBuffer = '';
state = 0
/* ANY */
;
continue;
}
if (state === flag) {
tokenBuffer += character;
} else if (state === 0
/* ANY */
&& flag === 2
/* NUMBER */
) {
tokenBuffer += character;
if (tokenBuffer.length > 1) {
// \w+\d
continue;
}
} else {
if (tokenBuffer) {
tokens.push(tokenBuffer);
}
tokenBuffer = character;
}
state = flag;
}
if (tokenBuffer) {
tokens.push(tokenBuffer);
}
return tokens;
};
/** dts2md break */
/**
* Default span symbols:
* '(' -> ')'
* '[' -> ']'
* '{' -> '}'
*/
var astDefaults = Object.assign({}, tokenizeDefaults, {
spanSymbols: new Map([['(', ')'], ['[', ']'], ['{', '}']]),
escapeCharacters: new Map([['t', '\t'], ['r', '\r'], ['n', '\n'], ['\n', ''], ['\\', '\\'], ['"', '"'], ["'", "'"], ['`', '`']])
});
/** dts2md break */
/**
* Convert tokens to AST nodes
*/
var token2ast = function token2ast(tokens, options, initOffset, initLine, initColumn, cursorStart, cursorStop) {
if (initOffset === void 0) {
initOffset = 0;
}
if (initLine === void 0) {
initLine = 1;
}
if (initColumn === void 0) {
initColumn = 1;
}
if (cursorStart === void 0) {
cursorStart = 0;
}
if (cursorStop === void 0) {
cursorStop = tokens.length;
}
var config = Object.assign({}, astDefaults, options);
var normalSymbols = config.normalSymbols,
twoCharacterSymbols = config.twoCharacterSymbols,
threeCharacterSymbols = config.threeCharacterSymbols,
fourCharacterSymbols = config.fourCharacterSymbols,
globSymbols = config.globSymbols,
numberCharacters = config.numberCharacters,
numberSuffixes = config.numberSuffixes,
spaceCharacters = config.spaceCharacters,
spanSymbols = config.spanSymbols,
escapeCharacters = config.escapeCharacters;
var result = new Array();
var offset = initOffset;
var line = initLine;
var column = initColumn;
for (var mainCursor = cursorStart; mainCursor < cursorStop; mainCursor++) {
var token = tokens[mainCursor];
if (spanSymbols.has(token)) {
var spanStopSymbol = spanSymbols.get(token);
var nested = 0;
var spanStopPosition = mainCursor + 1;
for (; spanStopPosition < tokens.length; spanStopPosition++) {
if (tokens[spanStopPosition] === token) {
nested++;
continue;
}
if (tokens[spanStopPosition] === spanStopSymbol) {
if (!nested) {
break;
}
nested--;
}
}
if (spanStopPosition === tokens.length) {
throw new SyntaxError("missing " + spanStopSymbol + " for " + token + " at line " + line + " column " + column);
}
if (spanStopPosition === mainCursor + 1) {
result.push({
type: 'span',
start: token,
stop: spanStopSymbol,
body: [],
offset: offset,
line: line,
column: column
}); // stop symbol
offset++;
} else {
var ast = token2ast(tokens, config, offset + 1, // +1 for start symbol
line, column + 1, mainCursor + 1, spanStopPosition);
var body = ast.ast;
result.push({
type: 'span',
start: token,
stop: spanStopSymbol,
body: body,
offset: offset,
line: line,
column: column
});
offset = ast.stopOffset;
line = ast.stopLine;
column = ast.stopColumn;
}
mainCursor = spanStopPosition; // mainCursor++ automatically goes here
} else if (normalSymbols.has(token) || twoCharacterSymbols.has(token) || threeCharacterSymbols.has(token) || fourCharacterSymbols.has(token)) {
result.push({
type: 'symbol',
value: token,
offset: offset,
line: line,
column: column
});
} else if (globSymbols.has(token[0])) {
var value = token[0];
var escapeFlag = false;
for (var i = 1; i < token.length - 1; i++) {
var character = token[i];
if (escapeFlag) {
if (!escapeCharacters.has(character)) {
if (character === '\r' && token[i + 1] === '\n') {
continue;
}
throw new SyntaxError("unknown escape character: \\" + character);
}
value += escapeCharacters.get(character);
escapeFlag = false;
} else {
if (character === '\\') {
escapeFlag = true;
continue;
}
value += character;
}
}
if (escapeFlag || token[token.length - 1] !== token[0] || token.length === 1) {
throw new SyntaxError("missing corresponding " + spanSymbols + " at line " + line + " column " + column);
}
value += token[0];
result.push({
type: 'glob',
value: value,
offset: offset,
line: line,
column: column
});
} else if (numberCharacters.has(token[0])) {
var suffixOffset = token.length - 1;
for (; suffixOffset >= 0; suffixOffset--) {
if (!numberSuffixes.has(token[suffixOffset])) {
break;
}
}
suffixOffset++;
result.push({
type: 'number',
value: token.slice(0, suffixOffset),
suffix: token.slice(suffixOffset),
offset: offset,
line: line,
column: column
});
} else if (!spaceCharacters.has(token[0])) {
result.push({
type: 'word',
value: token,
offset: offset,
line: line,
column: column
});
}
offset += token.length;
for (var _i = 0; _i < token.length; _i++) {
if (token[_i] === '\n') {
line++;
}
}
var lastNextLineIndex = token.lastIndexOf('\n');
if (~lastNextLineIndex) {
column = token.length - lastNextLineIndex;
} else {
column += token.length;
}
}
return {
ast: result,
stopOffset: offset,
stopLine: line,
stopColumn: column
};
};
/**
* Convert source code into AST nodes
*/
var parse = function parse(source, options) {
return token2ast(tokenize(source, options), options);
};
exports.astDefaults = astDefaults;
exports.parse = parse;
exports.token2ast = token2ast;
exports.tokenize = tokenize;
exports.tokenizeDefaults = tokenizeDefaults;
Object.defineProperty(exports, '__esModule', { value: true });
})));