Prevent interpretation of quoted strings as keywords everywhere

This commit is contained in:
David Evans 2017-10-29 23:01:57 +00:00
parent 12f81b4f9d
commit c306b74b15
3 changed files with 64 additions and 43 deletions

View File

@ -217,14 +217,15 @@ define(['core/ArrayUtilities'], (array) => {
if(i === state.line.length - 1) { if(i === state.line.length - 1) {
state.completions = cmMakeCompletions(state, path); state.completions = cmMakeCompletions(state, path);
} }
const found = current.then[token] || current.then['']; const keywordToken = token.q ? '' : token.v;
const found = current.then[keywordToken] || current.then[''];
if(typeof found === 'number') { if(typeof found === 'number') {
path.length -= found; path.length -= found;
} else { } else {
path.push(found || CM_ERROR); path.push(found || CM_ERROR);
} }
current = array.last(path); current = array.last(path);
updateSuggestion(state, suggestions, token, current); updateSuggestion(state, suggestions, token.v, current);
}); });
if(eol) { if(eol) {
updateSuggestion(state, suggestions, '', {}); updateSuggestion(state, suggestions, '', {});
@ -233,8 +234,12 @@ define(['core/ArrayUtilities'], (array) => {
return current.type; return current.type;
} }
function getInitialValue(block) { function getInitialToken(block) {
return (block.baseToken || {}).v || ''; const baseToken = (block.baseToken || {});
return {
value: baseToken.v || '',
quoted: baseToken.q || false,
};
} }
return class Mode { return class Mode {
@ -247,6 +252,7 @@ define(['core/ArrayUtilities'], (array) => {
return { return {
currentType: -1, currentType: -1,
current: '', current: '',
currentQuoted: false,
knownAgent: [], knownAgent: [],
knownLabel: [], knownLabel: [],
beginCompletions: cmMakeCompletions({}, [CM_COMMANDS]), beginCompletions: cmMakeCompletions({}, [CM_COMMANDS]),
@ -274,7 +280,9 @@ define(['core/ArrayUtilities'], (array) => {
const block = this.tokenDefinitions[i]; const block = this.tokenDefinitions[i];
if(this._matchPattern(stream, block.start, true)) { if(this._matchPattern(stream, block.start, true)) {
state.currentType = i; state.currentType = i;
state.current = getInitialValue(block); const {value, quoted} = getInitialToken(block);
state.current = value;
state.currentQuoted = quoted;
return true; return true;
} }
} }
@ -294,7 +302,7 @@ define(['core/ArrayUtilities'], (array) => {
if(block.omit) { if(block.omit) {
return 'comment'; return 'comment';
} }
state.line.push(state.current); state.line.push({v: state.current, q: state.currentQuoted});
return cmCheckToken(state, stream.eol()); return cmCheckToken(state, stream.eol());
} }
@ -303,7 +311,7 @@ define(['core/ArrayUtilities'], (array) => {
if(block.omit) { if(block.omit) {
return 'comment'; return 'comment';
} }
state.line.push(state.current); state.line.push(({v: state.current, q: state.currentQuoted}));
const type = cmCheckToken(state, false); const type = cmCheckToken(state, false);
state.line.pop(); state.line.pop();
return type; return type;

View File

@ -163,7 +163,10 @@ define([
} }
} }
function joinLabel(line, begin, end) { function joinLabel(line, begin = 0, end = null) {
if(end === null) {
end = line.length;
}
if(end <= begin) { if(end <= begin) {
return ''; return '';
} }
@ -174,17 +177,20 @@ define([
return result; return result;
} }
function debugLine(line) { function tokenKeyword(token) {
return joinLabel(line, 0, line.length); if(!token || token.q) {
return null;
}
return token.v;
} }
function skipOver(line, start, skip, error = null) { function skipOver(line, start, skip, error = null) {
if(skip.some((token, i) => ( const pass = skip.every((expected, i) => (
!line[start + i] || tokenKeyword(line[start + i]) === expected
line[start + i].v !== token ));
))) { if(!pass) {
if(error) { if(error) {
throw new Error(error + ': ' + debugLine(line)); throw new Error(error + ': ' + joinLabel(line));
} else { } else {
return start; return start;
} }
@ -194,7 +200,7 @@ define([
function findToken(line, token, start = 0) { function findToken(line, token, start = 0) {
for(let i = start; i < line.length; ++ i) { for(let i = start; i < line.length; ++ i) {
if(line[i].v === token) { if(tokenKeyword(line[i]) === token) {
return i; return i;
} }
} }
@ -206,7 +212,8 @@ define([
let current = ''; let current = '';
let first = true; let first = true;
for(let i = start; i < end; ++ i) { for(let i = start; i < end; ++ i) {
if(line[i].v === ',') { const token = line[i];
if(tokenKeyword(token) === ',') {
if(current) { if(current) {
list.push({name: current}); list.push({name: current});
current = ''; current = '';
@ -214,11 +221,11 @@ define([
} }
} else { } else {
if(!first) { if(!first) {
current += line[i].s; current += token.s;
} else { } else {
first = false; first = false;
} }
current += line[i].v; current += token.v;
} }
} }
if(current) { if(current) {
@ -228,9 +235,10 @@ define([
} }
function readAgent(line, begin, end) { function readAgent(line, begin, end) {
if(line[begin].v === '+' || line[begin].v === '-') { const firstKeyword = tokenKeyword(line[begin]);
if(firstKeyword === '+' || firstKeyword === '-') {
return { return {
opt: line[begin].v, opt: firstKeyword,
name: joinLabel(line, begin + 1, end), name: joinLabel(line, begin + 1, end),
}; };
} else { } else {
@ -243,32 +251,33 @@ define([
const PARSERS = [ const PARSERS = [
(line, meta) => { // title (line, meta) => { // title
if(line[0].v !== 'title') { if(tokenKeyword(line[0]) !== 'title') {
return null; return null;
} }
meta.title = joinLabel(line, 1, line.length); meta.title = joinLabel(line, 1);
return true; return true;
}, },
(line, meta) => { // terminators (line, meta) => { // terminators
if(line[0].v !== 'terminators') { if(tokenKeyword(line[0]) !== 'terminators') {
return null; return null;
} }
if(TERMINATOR_TYPES.indexOf(line[1].v) === -1) { const type = tokenKeyword(line[1]);
throw new Error('Unknown termination: ' + debugLine(line)); if(TERMINATOR_TYPES.indexOf(type) === -1) {
throw new Error('Unknown termination: ' + joinLabel(line));
} }
meta.terminators = line[1].v; meta.terminators = type;
return true; return true;
}, },
(line) => { // block (line) => { // block
if(line[0].v === 'end' && line.length === 1) { if(tokenKeyword(line[0]) === 'end' && line.length === 1) {
return {type: 'block end'}; return {type: 'block end'};
} }
const type = BLOCK_TYPES[line[0].v]; const type = BLOCK_TYPES[tokenKeyword(line[0])];
if(!type) { if(!type) {
return null; return null;
} }
@ -280,12 +289,12 @@ define([
return { return {
type: type.type, type: type.type,
mode: type.mode, mode: type.mode,
label: joinLabel(line, skip, line.length), label: joinLabel(line, skip),
}; };
}, },
(line) => { // agent (line) => { // agent
const type = AGENT_MANIPULATION_TYPES[line[0].v]; const type = AGENT_MANIPULATION_TYPES[tokenKeyword(line[0])];
if(!type || line.length <= 1) { if(!type || line.length <= 1) {
return null; return null;
} }
@ -295,15 +304,15 @@ define([
}, },
(line) => { // async (line) => { // async
if(line[0].v !== 'simultaneously') { if(tokenKeyword(line[0]) !== 'simultaneously') {
return null; return null;
} }
if(array.last(line).v !== ':') { if(tokenKeyword(array.last(line)) !== ':') {
return null; return null;
} }
let target = ''; let target = '';
if(line.length > 2) { if(line.length > 2) {
if(line[1].v !== 'with') { if(tokenKeyword(line[1]) !== 'with') {
return null; return null;
} }
target = joinLabel(line, 2, line.length - 1); target = joinLabel(line, 2, line.length - 1);
@ -315,12 +324,12 @@ define([
}, },
(line) => { // note (line) => { // note
const mode = NOTE_TYPES[line[0].v]; const mode = NOTE_TYPES[tokenKeyword(line[0])];
const labelSplit = findToken(line, ':'); const labelSplit = findToken(line, ':');
if(!mode || labelSplit === -1) { if(!mode || labelSplit === -1) {
return null; return null;
} }
const type = mode.types[line[1].v]; const type = mode.types[tokenKeyword(line[1])];
if(!type) { if(!type) {
return null; return null;
} }
@ -333,14 +342,14 @@ define([
) { ) {
throw new Error( throw new Error(
'Invalid ' + mode.mode + 'Invalid ' + mode.mode +
': ' + debugLine(line) ': ' + joinLabel(line)
); );
} }
return { return {
type: type.type, type: type.type,
agents, agents,
mode: mode.mode, mode: mode.mode,
label: joinLabel(line, labelSplit + 1, line.length), label: joinLabel(line, labelSplit + 1),
}; };
}, },
@ -352,7 +361,7 @@ define([
let typeSplit = -1; let typeSplit = -1;
let options = null; let options = null;
for(let j = 0; j < line.length; ++ j) { for(let j = 0; j < line.length; ++ j) {
const opts = CONNECTION_TYPES[line[j].v]; const opts = CONNECTION_TYPES[tokenKeyword(line[j])];
if(opts) { if(opts) {
typeSplit = j; typeSplit = j;
options = opts; options = opts;
@ -368,12 +377,12 @@ define([
readAgent(line, 0, typeSplit), readAgent(line, 0, typeSplit),
readAgent(line, typeSplit + 1, labelSplit), readAgent(line, typeSplit + 1, labelSplit),
], ],
label: joinLabel(line, labelSplit + 1, line.length), label: joinLabel(line, labelSplit + 1),
}, options); }, options);
}, },
(line) => { // marker (line) => { // marker
if(line.length < 2 || array.last(line).v !== ':') { if(line.length < 2 || tokenKeyword(array.last(line)) !== ':') {
return null; return null;
} }
return { return {
@ -392,7 +401,7 @@ define([
} }
} }
if(!stage) { if(!stage) {
throw new Error('Unrecognised command: ' + debugLine(line)); throw new Error('Unrecognised command: ' + joinLabel(line));
} }
if(typeof stage === 'object') { if(typeof stage === 'object') {
stages.push(stage); stages.push(stage);
@ -439,7 +448,7 @@ define([
const lines = []; const lines = [];
let line = []; let line = [];
tokens.forEach((token) => { tokens.forEach((token) => {
if(token.v === '\n' && !token.q) { if(tokenKeyword(token) === '\n') {
if(line.length > 0) { if(line.length > 0) {
lines.push(line); lines.push(line);
line = []; line = [];

View File

@ -522,6 +522,10 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
]); ]);
}); });
it('rejects quoted keywords', () => {
expect(() => parser.parse('"repeat" until something')).toThrow();
});
it('rejects invalid inputs', () => { it('rejects invalid inputs', () => {
expect(() => parser.parse('huh')).toThrow(); expect(() => parser.parse('huh')).toThrow();
}); });