Skip to content
This repository has been archived by the owner on Jan 15, 2025. It is now read-only.

Commit

Permalink
Fix expression visitor to make escape work in nested entity definition (
Browse files Browse the repository at this point in the history
#1106)

* fix expression lexer

* fix expression parser
  • Loading branch information
feich-ms authored Feb 5, 2021
1 parent c622c6f commit fe56306
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 46 deletions.
90 changes: 44 additions & 46 deletions packages/lu/src/parser/lufile/visitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,58 +95,56 @@ class Visitor {
let entityNameCapture = false;
let entityValueCapture = false;
let entityRoleCapture = false;
exp.split('').forEach(char => {
switch(char)
{
case '{':
let newEntity = {entityName : '', role : '', entityValue : undefined, parent : curEntity};
curList.push(newEntity);
curEntity = newEntity;
entityNameCapture = true;
entityRoleCapture = false;
entityValueCapture = false;
break;
case '}':
curEntity = curEntity.parent || undefined;
curList = curEntity != undefined ? curEntity.entityValue : splitString;
entityValueCapture = false;
entityRoleCapture = false;
entityNameCapture = false;
break;
case '=':
curEntity.entityValue = [];
curList = curEntity.entityValue;
let expChars = exp.split('');
let escapeChar = false;
expChars.forEach(function (char, index) {
if (char === '\\' && expChars.length > index + 1 && EscapeCharsInUtterance.includes(expChars[index + 1])) {
escapeChar = true;
} else if (char === '{' && !escapeChar) {
let newEntity = {entityName : '', role : '', entityValue : undefined, parent : curEntity};
curList.push(newEntity);
curEntity = newEntity;
entityNameCapture = true;
entityRoleCapture = false;
entityValueCapture = false;
} else if (char === '}' && !escapeChar) {
curEntity = curEntity.parent || undefined;
curList = curEntity != undefined ? curEntity.entityValue : splitString;
entityValueCapture = false;
entityRoleCapture = false;
entityNameCapture = false;
} else if (char === '=' && !entityValueCapture) {
curEntity.entityValue = [];
curList = curEntity.entityValue;
entityNameCapture = false;
entityValueCapture = true;
entityRoleCapture = false;
} else if (char === ':' && !entityRoleCapture) {
if (curEntity !== undefined && curEntity.entityName !== '' && entityNameCapture === true) {
entityRoleCapture = true;
entityNameCapture = false;
entityValueCapture = true;
entityRoleCapture = false;
break;
case ':':
if (curEntity !== undefined && curEntity.entityName !== '' && entityNameCapture === true) {
entityRoleCapture = true;
entityNameCapture = false;
entityValueCapture = false;
} else {
curList.push(char);
}
break;
default :
if (entityNameCapture) {
curEntity.entityName += char;
} else if (entityValueCapture) {
if (char === ' ') {
// we do not want leading spaces
if (curList.length !== 0) {
curList.push(char);
}
} else {
entityValueCapture = false;
} else {
curList.push(char);
}
} else {
escapeChar = false;
if (entityNameCapture) {
curEntity.entityName += char;
} else if (entityValueCapture) {
if (char === ' ') {
// we do not want leading spaces
if (curList.length !== 0) {
curList.push(char);
}
} else if (entityRoleCapture) {
curEntity.role += char;
} else {
curList.push(char);
}
break;
} else if (entityRoleCapture) {
curEntity.role += char;
} else {
curList.push(char);
}
}
});
return splitString;
Expand Down
58 changes: 58 additions & 0 deletions packages/lu/test/parser/lufile/parseFileContents.parseFile.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1147,4 +1147,62 @@ describe('parseFile correctly parses utterances', function () {
})
.catch((err) => done(err));
});

it("Correctly parses utterance that escape char \\ in nested entity definition", function (done) {
let testLU = `
# test
- {@Command={@Action={@BoldAction=bold}} \\{{@ActionTargetPhrase=directions from seattle to portland}\\}}`;
parseFile
.parseFile(testLU)
.then((res) => {
assert.equal(
res.LUISJsonStructure.utterances[0].text,
"bold {directions from seattle to portland}"
);
assert.equal(res.LUISJsonStructure.entities.length, 4);
assert.equal(res.LUISJsonStructure.entities[0].name, "BoldAction");
assert.equal(res.LUISJsonStructure.entities[1].name, "Action");
assert.equal(res.LUISJsonStructure.entities[2].name, "ActionTargetPhrase");
assert.equal(res.LUISJsonStructure.entities[3].name, "Command");
done();
})
.catch((err) => done(err));
});

it("Correctly parses utterance that with equal sign as entity value", function (done) {
let testLU = `
# test
- {@Command={@Action={@BoldAction=emphasise from}} {@ActionTargetRange={@ActionTargetStart=@username} {@ActionTargetSeparator=through} {@ActionTargetEnd==}}}`;
parseFile
.parseFile(testLU)
.then((res) => {
assert.equal(res.LUISJsonStructure.utterances[0].text, "emphasise from @username through =");
assert.equal(res.LUISJsonStructure.entities.length, 7);
assert.equal(res.LUISJsonStructure.entities[0].name, "BoldAction");
assert.equal(res.LUISJsonStructure.entities[1].name, "Action");
assert.equal(res.LUISJsonStructure.entities[2].name, "ActionTargetStart");
assert.equal(res.LUISJsonStructure.entities[3].name, "ActionTargetSeparator");
assert.equal(res.LUISJsonStructure.entities[4].name, "ActionTargetEnd");
assert.equal(res.LUISJsonStructure.entities[5].name, "ActionTargetRange");
assert.equal(res.LUISJsonStructure.entities[6].name, "Command");
done();
})
.catch((err) => done(err));
});

it("Correctly parses utterance that with colon sign in entity role", function (done) {
let testLU = `
# test
- {@city::startCity=Seattle}`;
parseFile
.parseFile(testLU)
.then((res) => {
assert.equal(res.LUISJsonStructure.utterances[0].text, "Seattle");
assert.equal(res.LUISJsonStructure.entities.length, 1);
assert.equal(res.LUISJsonStructure.entities[0].name, "city");
assert.equal(res.LUISJsonStructure.entities[0].roles[0], ":startCity");
done();
})
.catch((err) => done(err));
});
})

0 comments on commit fe56306

Please sign in to comment.