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

Commit fe56306

Browse files
authored
Fix expression visitor to make escape work in nested entity definition (#1106)
* fix expression lexer * fix expression parser
1 parent c622c6f commit fe56306

File tree

2 files changed

+102
-46
lines changed

2 files changed

+102
-46
lines changed

packages/lu/src/parser/lufile/visitor.js

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -95,58 +95,56 @@ class Visitor {
9595
let entityNameCapture = false;
9696
let entityValueCapture = false;
9797
let entityRoleCapture = false;
98-
exp.split('').forEach(char => {
99-
switch(char)
100-
{
101-
case '{':
102-
let newEntity = {entityName : '', role : '', entityValue : undefined, parent : curEntity};
103-
curList.push(newEntity);
104-
curEntity = newEntity;
105-
entityNameCapture = true;
106-
entityRoleCapture = false;
107-
entityValueCapture = false;
108-
break;
109-
case '}':
110-
curEntity = curEntity.parent || undefined;
111-
curList = curEntity != undefined ? curEntity.entityValue : splitString;
112-
entityValueCapture = false;
113-
entityRoleCapture = false;
114-
entityNameCapture = false;
115-
break;
116-
case '=':
117-
curEntity.entityValue = [];
118-
curList = curEntity.entityValue;
98+
let expChars = exp.split('');
99+
let escapeChar = false;
100+
expChars.forEach(function (char, index) {
101+
if (char === '\\' && expChars.length > index + 1 && EscapeCharsInUtterance.includes(expChars[index + 1])) {
102+
escapeChar = true;
103+
} else if (char === '{' && !escapeChar) {
104+
let newEntity = {entityName : '', role : '', entityValue : undefined, parent : curEntity};
105+
curList.push(newEntity);
106+
curEntity = newEntity;
107+
entityNameCapture = true;
108+
entityRoleCapture = false;
109+
entityValueCapture = false;
110+
} else if (char === '}' && !escapeChar) {
111+
curEntity = curEntity.parent || undefined;
112+
curList = curEntity != undefined ? curEntity.entityValue : splitString;
113+
entityValueCapture = false;
114+
entityRoleCapture = false;
115+
entityNameCapture = false;
116+
} else if (char === '=' && !entityValueCapture) {
117+
curEntity.entityValue = [];
118+
curList = curEntity.entityValue;
119+
entityNameCapture = false;
120+
entityValueCapture = true;
121+
entityRoleCapture = false;
122+
} else if (char === ':' && !entityRoleCapture) {
123+
if (curEntity !== undefined && curEntity.entityName !== '' && entityNameCapture === true) {
124+
entityRoleCapture = true;
119125
entityNameCapture = false;
120-
entityValueCapture = true;
121-
entityRoleCapture = false;
122-
break;
123-
case ':':
124-
if (curEntity !== undefined && curEntity.entityName !== '' && entityNameCapture === true) {
125-
entityRoleCapture = true;
126-
entityNameCapture = false;
127-
entityValueCapture = false;
128-
} else {
129-
curList.push(char);
130-
}
131-
break;
132-
default :
133-
if (entityNameCapture) {
134-
curEntity.entityName += char;
135-
} else if (entityValueCapture) {
136-
if (char === ' ') {
137-
// we do not want leading spaces
138-
if (curList.length !== 0) {
139-
curList.push(char);
140-
}
141-
} else {
126+
entityValueCapture = false;
127+
} else {
128+
curList.push(char);
129+
}
130+
} else {
131+
escapeChar = false;
132+
if (entityNameCapture) {
133+
curEntity.entityName += char;
134+
} else if (entityValueCapture) {
135+
if (char === ' ') {
136+
// we do not want leading spaces
137+
if (curList.length !== 0) {
142138
curList.push(char);
143139
}
144-
} else if (entityRoleCapture) {
145-
curEntity.role += char;
146140
} else {
147141
curList.push(char);
148142
}
149-
break;
143+
} else if (entityRoleCapture) {
144+
curEntity.role += char;
145+
} else {
146+
curList.push(char);
147+
}
150148
}
151149
});
152150
return splitString;

packages/lu/test/parser/lufile/parseFileContents.parseFile.test.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,4 +1147,62 @@ describe('parseFile correctly parses utterances', function () {
11471147
})
11481148
.catch((err) => done(err));
11491149
});
1150+
1151+
it("Correctly parses utterance that escape char \\ in nested entity definition", function (done) {
1152+
let testLU = `
1153+
# test
1154+
- {@Command={@Action={@BoldAction=bold}} \\{{@ActionTargetPhrase=directions from seattle to portland}\\}}`;
1155+
parseFile
1156+
.parseFile(testLU)
1157+
.then((res) => {
1158+
assert.equal(
1159+
res.LUISJsonStructure.utterances[0].text,
1160+
"bold {directions from seattle to portland}"
1161+
);
1162+
assert.equal(res.LUISJsonStructure.entities.length, 4);
1163+
assert.equal(res.LUISJsonStructure.entities[0].name, "BoldAction");
1164+
assert.equal(res.LUISJsonStructure.entities[1].name, "Action");
1165+
assert.equal(res.LUISJsonStructure.entities[2].name, "ActionTargetPhrase");
1166+
assert.equal(res.LUISJsonStructure.entities[3].name, "Command");
1167+
done();
1168+
})
1169+
.catch((err) => done(err));
1170+
});
1171+
1172+
it("Correctly parses utterance that with equal sign as entity value", function (done) {
1173+
let testLU = `
1174+
# test
1175+
- {@Command={@Action={@BoldAction=emphasise from}} {@ActionTargetRange={@ActionTargetStart=@username} {@ActionTargetSeparator=through} {@ActionTargetEnd==}}}`;
1176+
parseFile
1177+
.parseFile(testLU)
1178+
.then((res) => {
1179+
assert.equal(res.LUISJsonStructure.utterances[0].text, "emphasise from @username through =");
1180+
assert.equal(res.LUISJsonStructure.entities.length, 7);
1181+
assert.equal(res.LUISJsonStructure.entities[0].name, "BoldAction");
1182+
assert.equal(res.LUISJsonStructure.entities[1].name, "Action");
1183+
assert.equal(res.LUISJsonStructure.entities[2].name, "ActionTargetStart");
1184+
assert.equal(res.LUISJsonStructure.entities[3].name, "ActionTargetSeparator");
1185+
assert.equal(res.LUISJsonStructure.entities[4].name, "ActionTargetEnd");
1186+
assert.equal(res.LUISJsonStructure.entities[5].name, "ActionTargetRange");
1187+
assert.equal(res.LUISJsonStructure.entities[6].name, "Command");
1188+
done();
1189+
})
1190+
.catch((err) => done(err));
1191+
});
1192+
1193+
it("Correctly parses utterance that with colon sign in entity role", function (done) {
1194+
let testLU = `
1195+
# test
1196+
- {@city::startCity=Seattle}`;
1197+
parseFile
1198+
.parseFile(testLU)
1199+
.then((res) => {
1200+
assert.equal(res.LUISJsonStructure.utterances[0].text, "Seattle");
1201+
assert.equal(res.LUISJsonStructure.entities.length, 1);
1202+
assert.equal(res.LUISJsonStructure.entities[0].name, "city");
1203+
assert.equal(res.LUISJsonStructure.entities[0].roles[0], ":startCity");
1204+
done();
1205+
})
1206+
.catch((err) => done(err));
1207+
});
11501208
})

0 commit comments

Comments
 (0)