SequenceDiagram/scripts/sequence/Tokeniser.js

156 lines
2.9 KiB
JavaScript

define(['./CodeMirrorMode'], (CMMode) => {
'use strict';
function execAt(str, reg, i) {
reg.lastIndex = i;
return reg.exec(str);
}
function unescape(match) {
const c = match[1];
if(c === 'n') {
return '\n';
}
return match[1];
}
const TOKENS = [
{start: /#/y, end: /(?=\n)|$/y, omit: true},
{
start: /"/y,
end: /"/y,
escape: /\\(.)/y,
escapeWith: unescape,
baseToken: {q: true},
},
{
start: /'/y,
end: /'/y,
escape: /\\(.)/y,
escapeWith:
unescape,
baseToken: {q: true},
},
{start: /(?=[^ \t\r\n:+\-<>,])/y, end: /(?=[ \t\r\n:+\-<>,])|$/y},
{start: /(?=[+\-<>])/y, end: /(?=[^+\-<>])|$/y},
{start: /,/y, baseToken: {v: ','}},
{start: /:/y, baseToken: {v: ':'}},
{start: /\n/y, baseToken: {v: '\n'}},
];
function tokFindBegin(src, i) {
for(let j = 0; j < TOKENS.length; ++ j) {
const block = TOKENS[j];
const match = execAt(src, block.start, i);
if(match) {
return {
newBlock: block,
end: !block.end,
appendSpace: '',
appendValue: '',
skip: match[0].length,
};
}
}
return {
newBlock: null,
end: false,
appendSpace: src[i],
appendValue: '',
skip: 1,
};
}
function tokContinuePart(src, i, block) {
if(block.escape) {
const match = execAt(src, block.escape, i);
if(match) {
return {
newBlock: null,
end: false,
appendSpace: '',
appendValue: block.escapeWith(match),
skip: match[0].length,
};
}
}
const match = execAt(src, block.end, i);
if(match) {
return {
newBlock: null,
end: true,
appendSpace: '',
appendValue: '',
skip: match[0].length,
};
}
return {
newBlock: null,
end: false,
appendSpace: '',
appendValue: src[i],
skip: 1,
};
}
function tokAdvance(src, i, block) {
if(block) {
return tokContinuePart(src, i, block);
} else {
return tokFindBegin(src, i);
}
}
return class Tokeniser {
tokenise(src) {
const tokens = [];
let block = null;
let current = {s: '', v: '', q: false};
for(let i = 0; i <= src.length;) {
const advance = tokAdvance(src, i, block);
if(advance.newBlock) {
block = advance.newBlock;
Object.assign(current, block.baseToken);
}
current.s += advance.appendSpace;
current.v += advance.appendValue;
i += advance.skip;
if(advance.end) {
if(!block.omit) {
tokens.push(current);
}
current = {s: '', v: '', q: false};
block = null;
}
}
if(block) {
throw new Error('Unterminated block');
}
return tokens;
}
getCodeMirrorMode() {
return new CMMode(TOKENS);
}
splitLines(tokens) {
const lines = [];
let line = [];
tokens.forEach((token) => {
if(!token.q && token.v === '\n') {
if(line.length > 0) {
lines.push(line);
line = [];
}
} else {
line.push(token);
}
});
if(line.length > 0) {
lines.push(line);
}
return lines;
}
};
});