156 lines
2.9 KiB
JavaScript
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;
|
|
}
|
|
};
|
|
});
|