Prevent interpretation of quoted strings as keywords everywhere
This commit is contained in:
parent
12f81b4f9d
commit
c306b74b15
|
@ -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;
|
||||||
|
|
|
@ -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 = [];
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue