diff --git a/packages/lu/src/parser/lu/lu.js b/packages/lu/src/parser/lu/lu.js index 2d16e0dbc..aac99c1d6 100644 --- a/packages/lu/src/parser/lu/lu.js +++ b/packages/lu/src/parser/lu/lu.js @@ -4,8 +4,8 @@ */ const translateHelpers = require('./../lufile/translate-helpers') - const luOptions = require('./luOptions') +const luParser = require('./../lufile/luParser') class Lu { constructor(content, options = new luOptions){ @@ -22,6 +22,10 @@ class Lu { } } + parse(){ + return luParser.parse(this.content, undefined, {}); + } + async translate(translate_key, tgt_lang, translate_comments = false, translate_link_text = false, region = ''){ const translateSettings = new translateHelpers.translationSettings(); translateSettings.subscriptionKey = translate_key diff --git a/packages/lu/src/parser/lufile/luParser.js b/packages/lu/src/parser/lufile/luParser.js index 185e3e5d3..e1385c2f5 100644 --- a/packages/lu/src/parser/lufile/luParser.js +++ b/packages/lu/src/parser/lufile/luParser.js @@ -25,8 +25,73 @@ const defaultConfig = { enableComments: true // Temporarily enabled by default, cannot be configured } -class LUParser { +const objectFactory = (className) => { + const classes = { + NestedIntentSection, + SimpleIntentSection, + EntitySection, + NewEntitySection, + ImportSection, + ReferenceSection, + QnaSection, + ModelInfoSection + }; + return classes[className] +} + +const extractElementSections = (mapFunction, filterFunction, className, fileContext, content) => { + if (fileContext === undefined + || fileContext === null) { + return []; + } + + let entitySections = fileContext.paragraph() + .map(x => x[mapFunction]()); + + let filterToApply = (x => x !== undefined && x !== null); + if (filterFunction) { + filterToApply = (x => x && x[filterFunction]()); + } + + entitySections = entitySections.filter(filterToApply); + let entitySectionList = entitySections.map(x => { + const classToBuild = objectFactory(className) + return new classToBuild(x, content); + }); + + return entitySectionList; +} + +const buildSection = (strategyObject) => { + let builtSections = [] + let builtErrors = [] + + try { + let sectionToBuild = extractElementSections(...strategyObject.args); + sectionToBuild.forEach(section => builtErrors = builtErrors.concat(section.Errors)); + + if (strategyObject.postProcess) { + let postProcessArgs = [sectionToBuild].concat(strategyObject.postProcessArgs ? strategyObject.postProcessArgs : []); + let result = strategyObject.postProcess.apply(this, postProcessArgs); + builtSections = builtSections.concat(result.sections); + builtErrors = builtErrors.concat(result.errors) + } + builtSections = builtSections.concat(sectionToBuild); + } catch (err) { + builtErrors.push(BuildDiagnostic({ + message: `${strategyObject.message} ${err.message}` + })); + } + + return { + sections: builtSections, + errors: builtErrors, + } +} + + +class LUParser { /** * * @param {string} text @@ -63,134 +128,33 @@ class LUParser { let sections = []; let modelInfoSections = []; - try { - modelInfoSections = this.extractModelInfoSections(fileContent); - } catch (err) { - errors.push(BuildDiagnostic({ - message: `Error happened when parsing model information: ${err.message}` - })) - } + let result = buildSection({args:['modelInfoSection', undefined, 'ModelInfoSection', fileContent], message: 'Error happened when parsing model information:' }); + sections = sections.concat(result.sections); + errors = errors.concat(result.errors) if (modelInfoSections && modelInfoSections.length > 0 && !config.enableModelDescription) { errors.push(BuildDiagnostic({ message: `Do not support Model Description. Please make sure enableModelDescription is set to true.` })) } - modelInfoSections.forEach(section => errors = errors.concat(section.Errors)); - sections = sections.concat(modelInfoSections); - - try { - let isSectionEnabled = sectionEnabled === undefined ? this.isSectionEnabled(sections) : sectionEnabled; - - let nestedIntentSections = this.extractNestedIntentSections(fileContent, content); - nestedIntentSections.forEach(section => errors = errors.concat(section.Errors)); - if (isSectionEnabled) { - sections = sections.concat(nestedIntentSections); - } else { - nestedIntentSections.forEach(section => { - let emptyIntentSection = new SimpleIntentSection(); - emptyIntentSection.Name = section.Name; - emptyIntentSection.Id = `${emptyIntentSection.SectionType}_${emptyIntentSection.Name}` - - // get the end character index - // this is default value - // it will be reset in function extractSectionBody() - let endCharacter = section.Name.length + 2; - - const range = new Range(section.Range.Start, new Position(section.Range.Start.Line, endCharacter)) - emptyIntentSection.Range = range; - let errorMsg = `no utterances found for intent definition: "# ${emptyIntentSection.Name}"` - let error = BuildDiagnostic({ - message: errorMsg, - range: emptyIntentSection.Range, - severity: DiagnosticSeverity.WARN - }) - - errors.push(error); - sections.push(emptyIntentSection); - - section.SimpleIntentSections.forEach(subSection => { - sections.push(subSection); - errors = errors.concat(subSection.Errors); - }) - }); - } - } catch (err) { - errors.push(BuildDiagnostic({ - message: `Error happened when parsing nested intent section: ${err.message}` - })) - } - try { - let simpleIntentSections = this.extractSimpleIntentSections(fileContent, content); - simpleIntentSections.forEach(section => errors = errors.concat(section.Errors)); - sections = sections.concat(simpleIntentSections); - } catch (err) { - errors.push(BuildDiagnostic({ - message: `Error happened when parsing simple intent section: ${err.message}` - })) - } + let isSectionEnabled = sectionEnabled === undefined ? this.isSectionEnabled(sections) : sectionEnabled; - try { - let entitySections = this.extractEntitiesSections(fileContent); - entitySections.forEach(section => errors = errors.concat(section.Errors)); - sections = sections.concat(entitySections); - } catch (err) { - errors.push(BuildDiagnostic({ - message: `Error happened when parsing entities: ${err.message}` - })) - } + let strategies = [ + {args:['nestedIntentSection', undefined, 'NestedIntentSection', fileContent, content], message: 'Error happened when parsing nested intent section:', postProcess: this.filterNestedEntities, postProcessArgs: isSectionEnabled}, + {args:['simpleIntentSection', 'intentDefinition', 'SimpleIntentSection', fileContent, content], message: 'Error happened when parsing simple intent section:' }, + {args:['entitySection', 'entityDefinition', 'EntitySection', fileContent], message: 'Error happened when parsing entities:'}, + {args:['newEntitySection', 'newEntityDefinition', 'NewEntitySection', fileContent], message: 'Error happened when parsing new entities:', postProcess: this.filterPrebuiltEntities }, + {args:['importSection', undefined, 'ImportSection', fileContent], message: 'Error happened when parsing import section:'}, + {args:['referenceSection', undefined, 'ReferenceSection', fileContent], message: 'Error happened when parsing reference section:'}, + {args:['qnaSection', undefined, 'QnaSection', fileContent], message: 'Error happened when parsing qna section'} + ] - try { - let newEntitySections = this.extractNewEntitiesSections(fileContent); - const prebuilts = new Set(['age', 'datetimeV2', 'dimension', 'email', 'geographyV2', 'keyPhrase', 'money', 'number', 'ordinal', 'ordinalV2', - 'percentage', 'personName', 'phonenumber', 'temperature', 'url', 'datetime']); - newEntitySections.forEach(section =>{ - if (prebuilts.has(section.Name) && section.Type && section.Type !== 'prebuilt') { - section.Errors.push(BuildDiagnostic({ - message: `The model name ${section.Name} is reserved.`, - range: section.Range - })) - } - }); - - newEntitySections.forEach(section => errors = errors.concat(section.Errors)); - sections = sections.concat(newEntitySections); - } catch (err) { - errors.push(BuildDiagnostic({ - message: `Error happened when parsing new entities: ${err.message}` - })) - } - - try { - let importSections = this.extractImportSections(fileContent); - importSections.forEach(section => errors = errors.concat(section.Errors)); - sections = sections.concat(importSections); - } catch (err) { - errors.push(BuildDiagnostic({ - message: `Error happened when parsing import section: ${err.message}` - })) - } - - try { - let referenceSections = this.extractReferenceSections(fileContent); - referenceSections.forEach(section => errors = errors.concat(section.Errors)); - sections = sections.concat(referenceSections); - } catch (err) { - errors.push(BuildDiagnostic({ - message: `Error happened when parsing reference section: ${err.message}` - })) - } - - try { - let qnaSections = this.extractQnaSections(fileContent); - qnaSections.forEach(section => errors = errors.concat(section.Errors)); - sections = sections.concat(qnaSections); - } catch (err) { - errors.push(BuildDiagnostic({ - message: `Error happened when parsing qna section: ${err.message}` - })) - } + for(let i = 0; i < strategies.length; i++){ + result = buildSection(strategies[i]); + sections = sections.concat(result.sections); + errors = errors.concat(result.errors) + }; sections = this.reconstractIntentSections(sections) @@ -224,150 +188,60 @@ class LUParser { return { fileContent, errors }; } - /** - * @param {FileContext} fileContext - * @param {string} content - */ - static extractNestedIntentSections(fileContext, content) { - if (fileContext === undefined - || fileContext === null) { - return []; - } - - let nestedIntentSections = fileContext.paragraph() - .map(x => x.nestedIntentSection()) - .filter(x => x !== undefined && x !== null); - - let nestedIntentSectionList = nestedIntentSections.map(x => new NestedIntentSection(x, content)); - - return nestedIntentSectionList; - } - - /** - * @param {FileContext} fileContext - * @param {string} content - */ - static extractSimpleIntentSections(fileContext, content) { - if (fileContext === undefined - || fileContext === null) { - return []; - } - - let simpleIntentSections = fileContext.paragraph() - .map(x => x.simpleIntentSection()) - .filter(x => x && x.intentDefinition()); - - let simpleIntentSectionList = simpleIntentSections.map(x => new SimpleIntentSection(x, content)); - - return simpleIntentSectionList; - } - - /** - * @param {FileContext} fileContext - */ - static extractEntitiesSections(fileContext) { - if (fileContext === undefined - || fileContext === null) { - return []; - } - - let entitySections = fileContext.paragraph() - .map(x => x.entitySection()) - .filter(x => x && x.entityDefinition()); - - let entitySectionList = entitySections.map(x => new EntitySection(x)); - - return entitySectionList; - } - - /** - * @param {FileContext} fileContext - */ - static extractNewEntitiesSections(fileContext) { - if (fileContext === undefined - || fileContext === null) { - return []; - } - - let newEntitySections = fileContext.paragraph() - .map(x => x.newEntitySection()) - .filter(x => x && x.newEntityDefinition()); - - let newEntitySectionList = newEntitySections.map(x => new NewEntitySection(x)); - - return newEntitySectionList; - } - - /** - * @param {FileContext} fileContext - */ - static extractImportSections(fileContext) { - if (fileContext === undefined - || fileContext === null) { - return []; - } - - let importSections = fileContext.paragraph() - .map(x => x.importSection()) - .filter(x => x !== undefined && x !== null); - - let importSectionList = importSections.map(x => new ImportSection(x)); - - return importSectionList; - } - - /** - * @param {FileContext} fileContext - */ - static extractReferenceSections(fileContext) { - if (fileContext === undefined - || fileContext === null) { - return []; - } - - let referenceSections = fileContext.paragraph() - .map(x => x.referenceSection()) - .filter(x => x !== undefined && x !== null); - - let referenceSectionList = referenceSections.map(x => new ReferenceSection(x)); - - return referenceSectionList; + static filterPrebuiltEntities (newEntitySections) { + const prebuilts = new Set(['age', 'datetimeV2', 'dimension', 'email', 'geographyV2', 'keyPhrase', 'money', 'number', 'ordinal', 'ordinalV2', + 'percentage', 'personName', 'phonenumber', 'temperature', 'url', 'datetime']); + newEntitySections.forEach(section =>{ + if (prebuilts.has(section.Name) && section.Type && section.Type !== 'prebuilt') { + section.Errors.push(BuildDiagnostic({ + message: `The model name ${section.Name} is reserved.`, + range: section.Range + })) + } + }); + return {sections:[], errors:[]}; } - /** - * @param {FileContext} fileContext - */ - static extractQnaSections(fileContext) { - if (fileContext === undefined - || fileContext === null) { - return []; + static filterNestedEntities (nestedIntentSections, isSectionEnabled) { + let sections = []; + let errors = []; + if (isSectionEnabled) { + sections = sections.concat(nestedIntentSections); + } else { + nestedIntentSections.forEach(section => { + let emptyIntentSection = new SimpleIntentSection(); + emptyIntentSection.Name = section.Name; + emptyIntentSection.Id = `${emptyIntentSection.SectionType}_${emptyIntentSection.Name}` + + // get the end character index + // this is default value + // it will be reset in function extractSectionBody() + let endCharacter = section.Name.length + 2; + + const range = new Range(section.Range.Start, new Position(section.Range.Start.Line, endCharacter)) + emptyIntentSection.Range = range; + let errorMsg = `no utterances found for intent definition: "# ${emptyIntentSection.Name}"` + let error = BuildDiagnostic({ + message: errorMsg, + range: emptyIntentSection.Range, + severity: DiagnosticSeverity.WARN + }) + + errors.push(error); + sections.push(emptyIntentSection); + + section.SimpleIntentSections.forEach(subSection => { + sections.push(subSection); + errors = errors.concat(subSection.Errors); + }) + }); } - let qnaSections = fileContext.paragraph() - .map(x => x.qnaSection()) - .filter(x => x !== undefined && x !== null); - - let qnaSectionList = qnaSections.map(x => new QnaSection(x)); - - return qnaSectionList; - } - - /** - * @param {FileContext} fileContext - */ - static extractModelInfoSections(fileContext) { - if (fileContext === undefined - || fileContext === null) { - return []; + nestedIntentSections.splice(0, nestedIntentSections.length); + return { + sections, + errors } - - let modelInfoSections = fileContext.paragraph() - .map(x => x.modelInfoSection()) - .filter(x => x !== undefined && x !== null); - - let modelInfoSectionList = modelInfoSections.map(x => new ModelInfoSection(x)); - - return modelInfoSectionList; } /** diff --git a/packages/lu/src/parser/lufile/parseFileContents.js b/packages/lu/src/parser/lufile/parseFileContents.js index 841bc3739..c09e72cae 100644 --- a/packages/lu/src/parser/lufile/parseFileContents.js +++ b/packages/lu/src/parser/lufile/parseFileContents.js @@ -207,6 +207,23 @@ const parseFileContentsModule = { } }; +const throwDiagnosticError = function (diagnosticDetails) { + let diagnostic = { + message: diagnosticDetails.message, + } + if (diagnosticDetails.line) { + diagnostic['line'] = diagnosticDetails.line; + } + if (diagnosticDetails.range) { + diagnostic['range'] = diagnosticDetails.range; + } + + const error = BuildDiagnostic(diagnostic); + const errorCode = diagnosticDetails.errorCode ? diagnosticDetails.errorCode : retCode.errorCode.INVALID_INPUT; + + throw new exception(errorCode, error.toString(), [error]); +} + /** * Main parser code to parse current file contents into LUIS and QNA sections. * @param {parserObj} Object with that contains list of additional files to parse, parsed LUIS object and parsed QnA object @@ -323,10 +340,7 @@ const updateModelBasedOnNDepthEntities = function(utterances, entities) { let parentLabelled = utterance.entities.find(entityUtt => entityUtt.entity == parent); if (!parentLabelled) { const errorMsg = `Every child entity labelled in an utterance must have its parent labelled in that utterance. Parent "${parent}" for child "${entityInUtterance.entity}" is not labelled in utterance "${utterance.text}" for intent "${utterance.intent}".`; - const error = BuildDiagnostic({ - message: errorMsg - }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + throwDiagnosticError({message: errorMsg}); } else { isParentLabelled = true; } @@ -366,21 +380,16 @@ const validateNDepthEntities = function(collection, entitiesAndRoles, intentsCol if(child.instanceOf) { let baseEntityFound = entitiesAndRoles.find(i => i.name == child.instanceOf); if (!baseEntityFound) { - let errorMsg = `Invalid child entity definition found. No definition for "${child.instanceOf}" in child entity definition "${child.context.definition}".`; - const error = BuildDiagnostic({ - message: errorMsg, - line: child.context.line - }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + const errorMsg = `Invalid child entity definition found. No definition for "${child.instanceOf}" in child entity definition "${child.context.definition}".`; + throwDiagnosticError({message: errorMsg, line: child.context.line}); } // base type can only be a list or regex or prebuilt. if (![EntityTypeEnum.LIST, EntityTypeEnum.REGEX, EntityTypeEnum.PREBUILT, EntityTypeEnum.ML].includes(baseEntityFound.type)) { let errorMsg = `Invalid child entity definition found. "${child.instanceOf}" is of type "${baseEntityFound.type}" in child entity definition "${child.context.definition}". Child cannot be only be an instance of "${EntityTypeEnum.LIST}, ${EntityTypeEnum.REGEX} or ${EntityTypeEnum.PREBUILT}.`; - const error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, line: child.context.line }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } } @@ -399,11 +408,10 @@ const validateNDepthEntities = function(collection, entitiesAndRoles, intentsCol featureHandled = true; } else if (featureExists.type == EntityTypeEnum.PATTERNANY) { let errorMsg = `Invalid child entity definition found. "${feature}" is of type "${EntityTypeEnum.PATTERNANY}" in child entity definition "${child.context.definition}". Child cannot include a usesFeature of type "${EntityTypeEnum.PATTERNANY}".`; - const error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, line: child.context.line }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } else { child.features[idx] = new helperClass.modelToFeature(feature, featureProperties.entityFeatureToModel[featureExists.type]); featureHandled = true; @@ -417,11 +425,10 @@ const validateNDepthEntities = function(collection, entitiesAndRoles, intentsCol featureHandled = true; } else { let errorMsg = `Invalid child entity definition found. No definition found for "${feature}" in child entity definition "${child.context.definition}". Features must be defined before they can be added to a child.`; - const error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, line: child.context.line }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } } }) @@ -457,22 +464,20 @@ const validateFeatureAssignment = function(srcItemType, srcItemName, tgtFeatureT // can use everything as a feature except pattern.any if (tgtFeatureType === EntityTypeEnum.PATTERNANY) { let errorMsg = `'patternany' entity cannot be added as a feature. Invalid definition found for "@ ${srcItemType} ${srcItemName} usesFeature ${tgtFeatureName}"`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: range - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } break; default: // cannot have any features assigned let errorMsg = `Invalid definition found for "@ ${srcItemType} ${srcItemName} usesFeature ${tgtFeatureName}". usesFeature is only available for intent, ${plAllowedTypes.join(', ')}`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: range - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); - break; + }); + } } /** @@ -488,11 +493,10 @@ const addFeatures = function(tgtItem, feature, featureType, range, featureProper if (tgtItem.name === feature && !featureIsPhraseList) { // Item must be defined before being added as a feature. let errorMsg = `Source and target cannot be the same for usesFeature. e.g. x usesFeature x is invalid. "${tgtItem.name}" usesFeature "${feature}" is invalid.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: range - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } let featureToModelAlreadyDefined = (tgtItem.features || []).find(item => item.featureName == feature); let modelToFeatureAlreadyDefined = (tgtItem.features || []).find(item => item.modelName == feature); @@ -526,10 +530,9 @@ const addFeatures = function(tgtItem, feature, featureType, range, featureProper */ const parseFeatureSections = function(parsedContent, featuresToProcess, config) { if (!config.enableFeatures) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: 'Do not support Features. Please make sure enableFeatures is set to true.', }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } // We are only interested in extracting features and setting things up here. (featuresToProcess || []).forEach(section => { @@ -537,11 +540,10 @@ const parseFeatureSections = function(parsedContent, featuresToProcess, config) // Intents can only have features and nothing else. if (section.Roles) { let errorMsg = `Intents can only have usesFeature and nothing else. Invalid definition for "${section.Name}".`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: section.Range - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } if (!section.Features) return; // verify intent exists @@ -590,20 +592,18 @@ const parseFeatureSections = function(parsedContent, featuresToProcess, config) } else { // Item must be defined before being added as a feature. let errorMsg = `Features must be defined before assigned to an intent. No definition found for feature "${feature}" in usesFeature definition for intent "${section.Name}"`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: section.Range - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } }) } else { let errorMsg = `Features can only be added to intents that have a definition. Invalid feature definition found for intent "${section.Name}".`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: section.Range - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } } else { // handle as entity @@ -712,10 +712,9 @@ const updateDependencyList = function(type, parsedContent, dependencyList) { if (circularItemFound) { const errorMsg = `Circular dependency found for usesFeature. ${circularItemFound.value.map(v => v.feature ? v.feature : v).join(' -> ')}`; - const error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } }) @@ -748,10 +747,9 @@ const parseAndHandleImportSection = async function (parsedContent, luResource, c let luImports = luResource.Sections.filter(s => s.SectionType === SectionType.IMPORTSECTION); if (luImports && luImports.length > 0) { if (!config.enableExternalReferences) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: 'Do not support External References. Please make sure enableExternalReferences is set to true.' }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } let references = luResource.Sections.filter(s => s.SectionType === SectionType.REFERENCESECTION); @@ -770,22 +768,20 @@ const parseAndHandleImportSection = async function (parsedContent, luResource, c } catch (err) { // throw, invalid URI let errorMsg = `URI: "${linkValue}" appears to be invalid. Please double check the URI or re-try this parse when you are connected to the internet.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, - range: luImport.Range - }) - - throw (new exception(retCode.errorCode.INVALID_URI, error.toString(), [error])); + range: luImport.Range, + errorCode: retCode.errorCode.INVALID_URI + }); } if (response.status !== 200) { let errorMsg = `URI: "${linkValue}" appears to be invalid. Please double check the URI or re-try this parse when you are connected to the internet.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, - range: luImport.Range - }) - - throw (new exception(retCode.errorCode.INVALID_URI, error.toString(), [error])); + range: luImport.Range, + errorCode: retCode.errorCode.INVALID_URI + }); } let contentType = response.headers['content-type']; @@ -806,12 +802,10 @@ const parseAndHandleImportSection = async function (parsedContent, luResource, c linkValue = foundReference.Path } else { let errorMsg = `Cannot find reference "${linkValue}" when resolving import "${luImport.Description}${luImport.Path}".`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: luImport.Range - }) - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } } @@ -830,10 +824,9 @@ const validateImportSection = function (luResource, config) { let luImports = luResource.Sections.filter(s => s.SectionType === SectionType.IMPORTSECTION); if (luImports && luImports.length > 0) { if (!config.enableExternalReferences) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: 'Do not support External References. Please make sure enableExternalReferences is set to true.' }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } } } @@ -932,12 +925,10 @@ const parseAndHandleSimpleIntentSection = function (parsedContent, luResource, c let intentName = intent.Name; if (InvalidCharsInIntentOrEntityName.some(x => intentName.includes(x))) { let errorMsg = `Invalid intent line, intent name ${intentName} cannot contain any of the following characters: [<, >, *, %, &, :, \\, $]`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, line: intent.Range.Start.Line - }) - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } // insert only if the intent is not already present. @@ -954,12 +945,10 @@ const parseAndHandleSimpleIntentSection = function (parsedContent, luResource, c let reference = references.find(refer => refer.ReferenceId === referenceId) if (!reference) { let errorMsg = `Cannot find reference ${reference} when resolving utternace "${utteranceAndEntities.contextText}".`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: utteranceAndEntities.range - }) - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } utterance = `${utterance.slice(0, index)}(${reference.Path})` @@ -990,34 +979,29 @@ const parseAndHandleSimpleIntentSection = function (parsedContent, luResource, c let havePatternAnyEntity = entitiesFound.find(item => item.type == LUISObjNameEnum.PATTERNANYENTITY); if (havePatternAnyEntity !== undefined) { if (!config.enablePattern) { - const error = BuildDiagnostic({ - message: 'Do not support Pattern. Please make sure enablePattern is set to true.', - range: utteranceAndEntities.range + throwDiagnosticError({ + message: 'Do not support Pattern. Please make sure enablePattern is set to true.', + range: utteranceAndEntities.range }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } utterance = handleAtForPattern(utterance, entitiesFound, parsedContent.LUISJsonStructure.flatListOfEntityAndRoles); let mixedEntity = entitiesFound.filter(item => item.type != LUISObjNameEnum.PATTERNANYENTITY); if (mixedEntity.length !== 0) { let errorMsg = `Utterance "${utteranceAndEntities.contextText}" has mix of entites with labelled values and ones without. Please update utterance to either include labelled values for all entities or remove labelled values from all entities.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: utteranceAndEntities.range - }) - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } let prebuiltEntities = entitiesFound.filter(item => builtInTypes.consolidatedList.map(prebuiltEntity => prebuiltEntity.toLowerCase()).includes(item.entity.toLowerCase())); prebuiltEntities.forEach(prebuiltEntity => { if (parsedContent.LUISJsonStructure.prebuiltEntities.findIndex(e => e.name === prebuiltEntity.entity) < 0) { let errorMsg = `Pattern "${utteranceAndEntities.contextText}" has prebuilt entity ${prebuiltEntity.entity}. Please define it explicitly with @ prebuilt ${prebuiltEntity.entity}.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: utteranceAndEntities.range - }) - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } }) @@ -1079,12 +1063,10 @@ const parseAndHandleSimpleIntentSection = function (parsedContent, luResource, c ); if ((otherEntities || []).find(item => item.name == entity.entity) === undefined) { let errorMsg = `Utterance "${utterance}" has invalid reference to Phrase List entity "${nonAllowedPhrseListEntityInUtterance.name}". Phrase list entities cannot be given an explicit labelled value.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: utteranceAndEntities.range }); - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } } @@ -1096,11 +1078,10 @@ const parseAndHandleSimpleIntentSection = function (parsedContent, luResource, c let patternAnyExists = (parsedContent.LUISJsonStructure.patternAnyEntities || []).find(item => item.name == entity.entity); if (compositeExists === undefined && listExists === undefined && prebuiltExists === undefined && regexExists === undefined && patternAnyExists === undefined) { if (!config.enableMLEntities) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: 'Do not support ML entity. Please make sure enableMLEntities is set to true.', range: utteranceAndEntities.range }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } if (entity.role && entity.role !== '') { addItemOrRoleIfNotPresent(parsedContent.LUISJsonStructure, LUISObjNameEnum.ENTITIES, entity.entity, [entity.role.trim()]); @@ -1118,12 +1099,10 @@ const parseAndHandleSimpleIntentSection = function (parsedContent, luResource, c } else { if (!isChildEntity(entity, entitiesFound)) { let errorMsg = `${entity.entity} has been defined as a LIST entity type. It cannot be explicitly included in a labelled utterance unless the label includes a role.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: utteranceAndEntities.range }); - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } } } else if (prebuiltExists !== undefined) { @@ -1132,12 +1111,10 @@ const parseAndHandleSimpleIntentSection = function (parsedContent, luResource, c } else { if (!isChildEntity(entity, entitiesFound)) { let errorMsg = `${entity.entity} has been defined as a PREBUILT entity type. It cannot be explicitly included in a labelled utterance unless the label includes a role.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: utteranceAndEntities.range }); - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } } } else if (regexExists !== undefined) { @@ -1146,12 +1123,10 @@ const parseAndHandleSimpleIntentSection = function (parsedContent, luResource, c } else { if (!isChildEntity(entity, entitiesFound)) { let errorMsg = `${entity.entity} has been defined as a Regex entity type. It cannot be explicitly included in a labelled utterance unless the label includes a role.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: utteranceAndEntities.range }); - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } } } else if (patternAnyExists !== undefined) { @@ -1190,12 +1165,11 @@ const parseAndHandleSimpleIntentSection = function (parsedContent, luResource, c entitiesFound.forEach(item => { if (item.startPos > item.endPos) { let errorMsg = `No labelled value found for entity: "${item.entity}" in utterance: "${utteranceAndEntities.contextText}"`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, - range: utteranceAndEntities.range - }) - - throw (new exception(retCode.errorCode.MISSING_LABELLED_VALUE, error.toString(), [error])); + range: utteranceAndEntities.range, + errorCode: retCode.errorCode.MISSING_LABELLED_VALUE + }); } let utteranceEntity = new helperClass.utteranceEntity(item.entity, item.startPos, item.endPos); @@ -1234,11 +1208,10 @@ const parseAndHandleSimpleIntentSection = function (parsedContent, luResource, c let oldUtterance = expandUtterance(utterance, priorLabelFound); let newUtterance = expandUtterance(utterance, utteranceEntity); let errorMsg = `[Error] Duplicate overlapping labels found for entity '${priorLabelFound.name}' for Intent '${priorLabelFound.intent}'.\n 1. ${oldUtterance}\n 2. ${newUtterance}`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: utteranceAndEntities.range - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); default: // take the longest label if ((utteranceEntity.startPos >= priorLabelFound.startPos) && (utteranceEntity.endPos >= priorLabelFound.endPos)) { @@ -1320,11 +1293,10 @@ const validateAndGetRoles = function(parsedContent, roles, range, entityName, en let hasBadNonPLRoles = (roleFound.roles || []).filter(item => item.toLowerCase() !== PLCONSTS.INTERCHANGEABLE && item.toLowerCase() !== PLCONSTS.ENABLEDFORALLMODELS && item.toLowerCase() !== PLCONSTS.DISABLED && item.toLowerCase() !== PLCONSTS.DISABLEDFORALLMODELS); if (hasBadNonPLRoles.length !== 0) { let errorMsg = `Roles must be unique across entity types. Invalid role definition found "${entityName}". Prior definition - '@ ${roleFound.type} ${roleFound.name}${roleFound.roles.length > 0 ? ` hasRoles ${roleFound.roles.join(',')}` : ``}'`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: range - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } } }); @@ -1380,29 +1352,26 @@ const parseAndHandleEntityV2 = function (parsedContent, luResource, log, locale, let entityType = !entity.Type ? getEntityType(entity.Name, entities) : entity.Type; if (!entityType) { let errorMsg = `No type definition found for entity "${entityName}". Supported types are ${Object.values(EntityTypeEnum).join(', ')}. Note: Type names are case sensitive.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: entity.Range - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); }; if (entityType !== EntityTypeEnum.PHRASELIST && InvalidCharsInIntentOrEntityName.some(x => entityName.includes(x))) { let errorMsg = `Invalid entity line, entity name ${entityName} cannot contain any of the following characters: [<, >, *, %, &, :, \\, $]`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, line: entity.Range.Start.Line - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } if (entityType === entityName) { let errorMsg = `Entity name "${entityName}" cannot be the same as entity type "${entityType}"`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: entity.Range - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } let entityRoles = validateAndGetRoles(parsedContent, entity.Roles, entity.Range, entityName, entityType); let PAEntityRoles = RemoveDuplicatePatternAnyEntity(parsedContent, entityName, entityType, entity.Range); @@ -1414,11 +1383,10 @@ const parseAndHandleEntityV2 = function (parsedContent, luResource, log, locale, switch(entityType) { case EntityTypeEnum.ML: if (!config.enableMLEntities) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: 'Do not support ML entity. Please make sure enableMLEntities is set to true.', range: entity.Range }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } handleNDepthEntity(parsedContent, entityName, entityRoles, entity.ListBody, entity.Range); break; @@ -1497,11 +1465,10 @@ const handleNDepthEntity = function(parsedContent, entityName, entityRoles, enti let groupsFound = captureGroups.exec(child); if (!groupsFound) { let errorMsg = `Invalid child entity definition found for "${child.trim()}". Child definitions must start with '- @' and only include a type, name and optionally one or more usesFeature(s) definition.`; - const error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, line: defLine }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } let childEntityName = groupsFound.groups.entityName.replace(/^['"]/g, '').replace(/['"]$/g, ''); let childEntityType = groupsFound.groups.instanceOf.trim().replace(/^['"]/g, '').replace(/['"]$/g, ''); @@ -1520,11 +1487,10 @@ const handleNDepthEntity = function(parsedContent, entityName, entityRoles, enti entityIdxByLevel.reverse(); if (!currentParentEntity) { let errorMsg = `[ERROR] line ${defLine}: Invalid definition found for child "${child.trim()}". Parent of each child entity must be of type "${EntityTypeEnum.ML}".`; - const error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, line: defLine }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } let context = {line : defLine, definition: child.trim()}; if (groupsFound.groups.instanceOf.toLowerCase().trim() === EntityTypeEnum.SIMPLE) { @@ -1587,11 +1553,10 @@ const verifyUniqueEntityName = function(parsedContent, entityName, entityType, r }); if (entityFound !== undefined) { let errorMsg = `${matchType} Prior definition - '@ ${entityFound.type} ${entityFound.name}${entityFound.roles.length > 0 ? ` hasRoles ${entityFound.roles.join(',')}` : ``}'`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: range - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } } /** @@ -1602,11 +1567,10 @@ const verifyUniqueEntityName = function(parsedContent, entityName, entityType, r */ const handlePatternAny = function(parsedContent, entityName, entityRoles, range, config) { if (!config.enablePattern) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: 'Do not support Pattern. Please make sure enablePattern is set to true.', range: range }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } // check if this patternAny entity is already labelled in an utterance and or added as a simple entity. if so, throw an error. try { @@ -1650,11 +1614,10 @@ const RemoveDuplicatePatternAnyEntity = function(parsedContent, pEntityName, ent if (PAEntityFound !== undefined && PAIdx !== -1 && entityType != EntityTypeEnum.PATTERNANY) { if (entityType.toLowerCase().trim().includes('phraselist')) { let errorMsg = `Phrase lists cannot be used as an entity in a pattern "${pEntityName}"`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: range - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } entityRoles = (PAEntityFound.roles.length !== 0) ? PAEntityFound.roles : []; parsedContent.LUISJsonStructure.patternAnyEntities.splice(PAIdx, 1); @@ -1672,20 +1635,18 @@ const RemoveDuplicatePatternAnyEntity = function(parsedContent, pEntityName, ent */ const handlePhraseList = function(parsedContent, entityName, entityType, entityRoles, valuesList, range, config) { if (!config.enablePhraseLists) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: 'Do not support Phrase Lists. Please make sure enablePhraseLists is set to true.', range: range }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } // phraselist name can only contain letters (a-z, A-Z), numbers (0-9) and symbols @ # _ . , ^ \\ [ ] if (!/[a-zA-Z0-9@#_,.,^\\\[\]]+$/.test(entityName.toLowerCase().includes('interchangeable') ? entityName.split(/\(.*\)/g)[0] : entityName)) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: `Invalid phraselist line, phraselist name ${entityName} can only contain letters (a-z, A-Z), numbers (0-9) and symbols @ # _ . , ^ \\ [ ]`, line: range.Start.Line }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); }; let isPLEnabledForAllModels = undefined; @@ -1703,12 +1664,10 @@ const handlePhraseList = function(parsedContent, entityName, entityType, entityR entityName += item; } else { let errorMsg = `Phrase list entity ${entityName} has invalid role definition with roles = ${entityRoles.join(', ')}. Roles are not supported for Phrase Lists`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, context: range - }) - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } }) } @@ -1738,12 +1697,10 @@ const handlePhraseList = function(parsedContent, entityName, entityType, entityR if (entityType) { if (pLEntityExists.mode !== intc) { let errorMsg = `Phrase list: "${entityName}" has conflicting definitions. One marked interchangeable and another not interchangeable`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: range - }) - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } } let wordsSplit = pLEntityExists.words.split(','); @@ -1770,11 +1727,10 @@ const handlePhraseList = function(parsedContent, entityName, entityType, entityR */ const handlePrebuiltEntity = function(parsedContent, entityName, entityType, entityRoles, locale, log, range, config) { if (!config.enablePrebuiltEntities) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: 'Do not support Prebuilt entity. Please make sure enablePrebuiltEntities is set to true.', range: range }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } locale = locale ? locale.toLowerCase() : 'en-us'; @@ -1790,12 +1746,10 @@ const handlePrebuiltEntity = function(parsedContent, entityName, entityType, ent // verify if the requested entityType is available in the requested locale if (!builtInTypes.consolidatedList.map(item => item.toLowerCase()).includes(entityType.toLowerCase())) { let errorMsg = `Unknown PREBUILT entity '${entityType}'. Available pre-built types are ${builtInTypes.consolidatedList.join(',')}`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: range - }) - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } let prebuiltCheck = builtInTypes.perLocaleAvailability[locale][entityType]; if (prebuiltCheck === null) { @@ -1804,12 +1758,10 @@ const handlePrebuiltEntity = function(parsedContent, entityName, entityType, ent process.stdout.write(chalk.default.yellowBright(' Skipping this prebuilt entity..\n')); } else { let errorMsg = `PREBUILT entity '${entityType}' is not available for the requested locale '${locale}'`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, range: range - }) - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } } else if (prebuiltCheck && prebuiltCheck.includes('datetime')) { if (log) { @@ -1835,11 +1787,10 @@ const handlePrebuiltEntity = function(parsedContent, entityName, entityType, ent */ const handleComposite = function(parsedContent, entityName, entityType, entityRoles, range, inlineChildRequired, isEntityTypeDefinition, config) { if (!config.enableCompositeEntities) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: 'Do not support Composite entity. Please make sure enableCompositeEntities is set to true.', range: range }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } // remove simple entity definitions for composites but carry forward roles. @@ -1860,12 +1811,11 @@ const handleComposite = function(parsedContent, entityName, entityType, entityRo let childDefinition = entityType.trim().replace('[', '').replace(']', '').trim(); if (childDefinition.length === 0 && inlineChildRequired) { let errorMsg = `Composite entity: ${entityName} is missing child entity definitions. Child entities are denoted via [entity1, entity2] notation.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, - range: range + range: range, + errorCode: retCode.errorCode.INVALID_COMPOSITE_ENTITY }) - - throw (new exception(retCode.errorCode.INVALID_COMPOSITE_ENTITY, error.toString(), [error])); } // split the children based on ',' or ';' delimiter. Trim each child to remove white spaces. let compositeChildren = childDefinition !== "" ? childDefinition.split(new RegExp(/[,;]/g)).map(item => item.trim()) : []; @@ -1881,12 +1831,11 @@ const handleComposite = function(parsedContent, entityName, entityType, entityRo if (isEntityTypeDefinition) { if (compositeEntity.children.length !== 0 && JSON.stringify(compositeChildren.sort()) !== JSON.stringify(compositeEntity.children.sort())) { let errorMsg = `Composite entity: ${entityName} has multiple definition with different children. \n 1. ${compositeChildren.join(', ')}\n 2. ${compositeEntity.children.join(', ')}`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, - range: range - }) - - throw (new exception(retCode.errorCode.INVALID_COMPOSITE_ENTITY, error.toString(), [error])); + range: range, + errorCode: retCode.errorCode.INVALID_COMPOSITE_ENTITY + }); } } @@ -1907,11 +1856,10 @@ const handleComposite = function(parsedContent, entityName, entityType, entityRo */ const handleClosedList = function (parsedContent, entityName, listLines, entityRoles, range, config) { if (!config.enableListEntities) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: 'Do not support List entity. Please make sure enableListEntities is set to true.', range: range }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } // check if this list entity is already labelled in an utterance and or added as a simple entity. if so, throw an error. try { @@ -1954,12 +1902,11 @@ const handleClosedList = function (parsedContent, entityName, listLines, entityR item = item.trim(); if (!nvExists || !nvExists.list) { let errorMsg = `Closed list ${entityName} has synonyms list "${line}" without a normalized value.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, - range: range - }) - - throw (new exception(retCode.errorCode.SYNONYMS_NOT_A_LIST, error.toString(), [error])); + range: range, + errorCode: retCode.errorCode.SYNONYMS_NOT_A_LIST + }); } nvExists.list.push(item); }) @@ -1997,11 +1944,10 @@ const parseAndHandleEntitySection = function (parsedContent, luResource, log, lo if (entityType !== EntityTypeEnum.PHRASELIST && InvalidCharsInIntentOrEntityName.some(x => entityName.includes(x))) { let errorMsg = `Invalid entity line, entity name ${entityName} cannot contain any of the following characters: [<, >, *, %, &, :, \\, $]`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, line: entity.Range.Start.Line - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } let parsedRoleAndType = helpers.getRolesAndType(entityType); @@ -2034,12 +1980,11 @@ const parseAndHandleEntitySection = function (parsedContent, luResource, log, lo parsedContent.qnaAlterations.wordAlterations.push(new qnaAlterations().wordAlterations = {"alterations": alterationlist}); } else { let errorMsg = `QnA alteration section: "${alterationlist}" does not have list decoration. Prefix line with "-" or "+" or "*"`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, - range: entity.range - }) - - throw (new exception(retCode.errorCode.SYNONYMS_NOT_A_LIST, error.toString(), [error])); + range: entity.range, + errorCode: retCode.errorCode.SYNONYMS_NOT_A_LIST + }); } } else { // treat this as a LUIS list entity type @@ -2092,12 +2037,11 @@ const parseAndHandleEntitySection = function (parsedContent, luResource, log, lo handleRegExEntity(parsedContent, entityName, entityType, entityRoles, entity.Range, config); } else { let errorMsg = `RegEx entity: ${regExEntity.name} is missing trailing '/'. Regex patterns need to be enclosed in forward slashes. e.g. /[0-9]/`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, - range: entity.Range - }) - - throw (new exception(retCode.errorCode.INVALID_REGEX_ENTITY, error.toString(), [error])); + range: entity.Range, + errorCode: retCode.errorCode.INVALID_REGEX_ENTITY + }); } } else { // TODO: handle other entity types @@ -2115,11 +2059,10 @@ const parseAndHandleEntitySection = function (parsedContent, luResource, log, lo */ const handleRegExEntity = function(parsedContent, entityName, entityType, entityRoles, range, config) { if (!config.enableRegexEntities) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: 'Do not support Regex entity. Please make sure enableRegexEntities is set to true.', range: range }); - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } // check if this regex entity is already labelled in an utterance and or added as a simple entity. if so, throw an error. try { @@ -2136,12 +2079,11 @@ const handleRegExEntity = function(parsedContent, entityName, entityType, entity regex = entityType.slice(1, entityType.length - 1); if (regex === '') { let errorMsg = `RegEx entity: ${entityName} has empty regex pattern defined.`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, - range: range - }) - - throw (new exception(retCode.errorCode.INVALID_REGEX_ENTITY, error.toString(), [error])); + range: range, + errorCode: retCode.errorCode.INVALID_REGEX_ENTITY + }); } } @@ -2153,12 +2095,12 @@ const handleRegExEntity = function(parsedContent, entityName, entityType, entity // throw an error if the pattern is different for the same entity if (regExEntity.regexPattern !== '' && regex !== '' && regExEntity.regexPattern !== regex) { let errorMsg = `RegEx entity: ${regExEntity.name} has multiple regex patterns defined. \n 1. /${regex}/\n 2. /${regExEntity.regexPattern}/`; - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg, - range: range - }) - - throw (new exception(retCode.errorCode.INVALID_REGEX_ENTITY, error.toString(), [error])); + range: range, + errorCode: retCode.errorCode.INVALID_REGEX_ENTITY + }); + } else { // update roles addItemOrRoleIfNotPresent(parsedContent.LUISJsonStructure, LUISObjNameEnum.REGEX, regExEntity.name, entityRoles); @@ -2225,10 +2167,9 @@ const parseAndHandleModelInfoSection = function (parsedContent, luResource, log, let modelInfos = luResource.Sections.filter(s => s.SectionType === SectionType.MODELINFOSECTION); if (modelInfos && modelInfos.length > 0) { if (!config.enableModelDescription) { - const error = BuildDiagnostic({ + throwDiagnosticError({ message: `Do not support Model Description. Please make sure enableModelDescription is set to true.` - }) - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); + }); } for (const modelInfo of modelInfos) { @@ -2420,11 +2361,9 @@ const VerifyAndUpdateSimpleEntityCollection = function (parsedContent, entityNam } } else if (entityType !== 'Phrase List') { // Fix for # 1151. Phrase lists can have same name as other entities. let errorMsg = `'${entityType}' entity: "${entityName}" is added as a labelled entity in utterance "${entityExistsInUtteranceLabel.text}". ${entityType} cannot be added with explicit labelled values in utterances.` - let error = BuildDiagnostic({ + throwDiagnosticError({ message: errorMsg }); - - throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error])); } }); } diff --git a/packages/lu/test/fixtures/lu_raw_parse/lu_sections.json b/packages/lu/test/fixtures/lu_raw_parse/lu_sections.json new file mode 100644 index 000000000..d608b9774 --- /dev/null +++ b/packages/lu/test/fixtures/lu_raw_parse/lu_sections.json @@ -0,0 +1,1814 @@ +{ + "sections": [ + { + "Errors": [ + + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_Greeting", + "Body": "- hi\n- hello\n\n> ML ENTITY. Without an explicit entity definition, 'userName' defaults to 'ml' entity type.", + "UtteranceAndEntitiesMap": [ + { + "utterance": "hi", + "entities": [ + + ], + "errorMsgs": [ + + ], + "contextText": "- hi", + "range": { + "Start": { + "Line": 4, + "Character": 0 + }, + "End": { + "Line": 4, + "Character": 4 + } + } + }, + { + "utterance": "hello", + "entities": [ + + ], + "errorMsgs": [ + + ], + "contextText": "- hello", + "range": { + "Start": { + "Line": 5, + "Character": 0 + }, + "End": { + "Line": 5, + "Character": 7 + } + } + } + ], + "Entities": [ + + ], + "Name": "Greeting", + "IntentNameLine": "# Greeting", + "Range": { + "Start": { + "Line": 3, + "Character": 0 + }, + "End": { + "Line": 7, + "Character": 92 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_getUserName", + "Body": "- my name is {username=vishwac}\n\n> PREBUILT ENTITIES\n@ prebuilt number numOfGuests, age\n\n> COMPOSITE ENTITIES", + "UtteranceAndEntitiesMap": [ + { + "utterance": "my name is vishwac", + "entities": [ + { + "type": "entities", + "entity": "username", + "role": "", + "startPos": 11, + "endPos": 17 + } + ], + "errorMsgs": [ + + ], + "contextText": "- my name is {username=vishwac}", + "range": { + "Start": { + "Line": 9, + "Character": 0 + }, + "End": { + "Line": 9, + "Character": 31 + } + } + } + ], + "Entities": [ + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_number", + "Body": "", + "Name": "number", + "Type": "prebuilt", + "Roles": "numOfGuests, age", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 12, + "Character": 0 + }, + "End": { + "Line": 12, + "Character": 35 + } + } + } + ], + "Name": "getUserName", + "IntentNameLine": "# getUserName", + "Range": { + "Start": { + "Line": 8, + "Character": 0 + }, + "End": { + "Line": 14, + "Character": 20 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_setThermostat", + "Body": "> This utterance labels ‘thermostat to 72’ as composite entity deviceTemperature\n - Please set {deviceTemperature = thermostat to 72}\n> This is an example utterance that labels ‘owen’ as customDevice (ml entity) and wraps ‘owen to 72’ with the ‘deviceTemperature’ composite entity\n - Set {deviceTemperature = {customDevice = owen} to 72}\n\n> Define a composite entity ‘deviceTemperature’ that has device (list entity), customDevice (ml entity), temperature (pre-built entity) as children\n\n@ composite deviceTemperature = [device, customDevice, temperature]\n\n@ list device =\n\t- thermostat :\n\t\t- Thermostat\n\t\t- Heater\n\t\t- AC\n\t\t- Air conditioner\n\t- refrigerator :\n\t\t- Fridge\n \t- Cooler\n\n@ ml customDevice\n\n@ prebuilt temperature\n\n> REGULAR EXPRESSION ENTITY\n> from, to are roles to hrf-number.\n@ regex hrf-number from, to = /hrf-[0-9]{6}/\n\n> ROLES\n", + "UtteranceAndEntitiesMap": [ + { + "utterance": "Please set thermostat to 72", + "entities": [ + { + "type": "entities", + "entity": "deviceTemperature", + "role": "", + "startPos": 11, + "endPos": 26 + } + ], + "errorMsgs": [ + + ], + "contextText": "- Please set {deviceTemperature = thermostat to 72}", + "range": { + "Start": { + "Line": 17, + "Character": 4 + }, + "End": { + "Line": 17, + "Character": 55 + } + } + }, + { + "utterance": "Set owen to 72", + "entities": [ + { + "type": "entities", + "entity": "customDevice", + "role": "", + "startPos": 4, + "endPos": 7 + }, + { + "type": "entities", + "entity": "deviceTemperature", + "role": "", + "startPos": 4, + "endPos": 13 + } + ], + "errorMsgs": [ + + ], + "contextText": " - Set {deviceTemperature = {customDevice = owen} to 72}", + "range": { + "Start": { + "Line": 19, + "Character": 0 + }, + "End": { + "Line": 19, + "Character": 59 + } + } + } + ], + "Entities": [ + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_deviceTemperature", + "Body": "", + "Name": "deviceTemperature", + "Type": "composite", + "CompositeDefinition": "[device, customDevice, temperature]", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 23, + "Character": 0 + }, + "End": { + "Line": 23, + "Character": 68 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_device", + "Body": "", + "Name": "device", + "Type": "list", + "SynonymsList": [ + { + "NormalizedValue": "thermostat", + "Synonyms": [ + "Thermostat", + "Heater", + "AC", + "Air conditioner" + ] + }, + { + "NormalizedValue": "refrigerator", + "Synonyms": [ + "Fridge", + "Cooler" + ] + } + ], + "Range": { + "Start": { + "Line": 25, + "Character": 0 + }, + "End": { + "Line": 33, + "Character": 14 + } + } + }, + { + "Errors": [], + "SectionType": "newEntitySection", + "Id": "newEntitySection_customDevice", + "Body": "", + "Name": "customDevice", + "Type": "ml", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 35, + "Character": 0 + }, + "End": { + "Line": 35, + "Character": 18 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_temperature", + "Body": "", + "Name": "temperature", + "Type": "prebuilt", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 37, + "Character": 0 + }, + "End": { + "Line": 37, + "Character": 23 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_hrf-number", + "Body": "", + "Name": "hrf-number", + "Type": "regex", + "Roles": "from, to", + "RegexDefinition": "/hrf-[0-9]{6}/", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 41, + "Character": 0 + }, + "End": { + "Line": 41, + "Character": 45 + } + } + } + ], + "Name": "setThermostat", + "IntentNameLine": "# setThermostat", + "Range": { + "Start": { + "Line": 15, + "Character": 0 + }, + "End": { + "Line": 44, + "Character": 0 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_AskForUserName", + "Body": "- {userName:firstName=vishwac} {userName:lastName=kannan}\n- I'm {userName:firstName=vishwac}\n- my first name is {userName:firstName=vishwac}\n- {userName=vishwac} is my name\n\n> In patterns, you can use roles using the {\\:\\} notation", + "UtteranceAndEntitiesMap": [ + { + "utterance": "vishwac kannan", + "entities": [ + { + "type": "entities", + "entity": "userName", + "role": "firstName", + "startPos": 0, + "endPos": 6 + }, + { + "type": "entities", + "entity": "userName", + "role": "lastName", + "startPos": 8, + "endPos": 13 + } + ], + "errorMsgs": [ + + ], + "contextText": "- {userName:firstName=vishwac} {userName:lastName=kannan}", + "range": { + "Start": { + "Line": 46, + "Character": 0 + }, + "End": { + "Line": 46, + "Character": 57 + } + } + }, + { + "utterance": "I'm vishwac", + "entities": [ + { + "type": "entities", + "entity": "userName", + "role": "firstName", + "startPos": 4, + "endPos": 10 + } + ], + "errorMsgs": [ + + ], + "contextText": "- I'm {userName:firstName=vishwac}", + "range": { + "Start": { + "Line": 47, + "Character": 0 + }, + "End": { + "Line": 47, + "Character": 34 + } + } + }, + { + "utterance": "my first name is vishwac", + "entities": [ + { + "type": "entities", + "entity": "userName", + "role": "firstName", + "startPos": 17, + "endPos": 23 + } + ], + "errorMsgs": [ + + ], + "contextText": "- my first name is {userName:firstName=vishwac}", + "range": { + "Start": { + "Line": 48, + "Character": 0 + }, + "End": { + "Line": 48, + "Character": 47 + } + } + }, + { + "utterance": "vishwac is my name", + "entities": [ + { + "type": "entities", + "entity": "userName", + "role": "", + "startPos": 0, + "endPos": 6 + } + ], + "errorMsgs": [ + + ], + "contextText": "- {userName=vishwac} is my name", + "range": { + "Start": { + "Line": 49, + "Character": 0 + }, + "End": { + "Line": 49, + "Character": 31 + } + } + } + ], + "Entities": [ + + ], + "Name": "AskForUserName", + "IntentNameLine": "# AskForUserName", + "Range": { + "Start": { + "Line": 45, + "Character": 0 + }, + "End": { + "Line": 51, + "Character": 81 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_getUserNameRoles", + "Body": "- call me {name:userName}\n- I'm {name:userName}\n- my name is {name:userName}\n\n> roles can be specified for list entity types as well - in this case fromCity and toCity are added as roles to the 'city' list entity defined further below", + "UtteranceAndEntitiesMap": [ + { + "utterance": "call me {name:userName}", + "entities": [ + { + "type": "patternAnyEntities", + "entity": "name", + "role": "userName" + } + ], + "errorMsgs": [ + + ], + "contextText": "- call me {name:userName}", + "range": { + "Start": { + "Line": 53, + "Character": 0 + }, + "End": { + "Line": 53, + "Character": 25 + } + } + }, + { + "utterance": "I'm {name:userName}", + "entities": [ + { + "type": "patternAnyEntities", + "entity": "name", + "role": "userName" + } + ], + "errorMsgs": [ + + ], + "contextText": "- I'm {name:userName}", + "range": { + "Start": { + "Line": 54, + "Character": 0 + }, + "End": { + "Line": 54, + "Character": 21 + } + } + }, + { + "utterance": "my name is {name:userName}", + "entities": [ + { + "type": "patternAnyEntities", + "entity": "name", + "role": "userName" + } + ], + "errorMsgs": [ + + ], + "contextText": "- my name is {name:userName}", + "range": { + "Start": { + "Line": 55, + "Character": 0 + }, + "End": { + "Line": 55, + "Character": 28 + } + } + } + ], + "Entities": [ + + ], + "Name": "getUserNameRoles", + "IntentNameLine": "# getUserNameRoles", + "Range": { + "Start": { + "Line": 52, + "Character": 0 + }, + "End": { + "Line": 57, + "Character": 156 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_BookFlight", + "Body": "- book flight from {city:fromCity} to {city:toCity}\n- [can you] get me a flight from {city:fromCity} to {city:toCity}\n- get me a flight to {city:toCity}\n- I need to fly from {city:fromCity}\n\n$city:Seattle=\n- Seattle\n- Tacoma\n- SeaTac\n- SEA\n\n$city:Portland=\n- Portland\n- PDX\n\n> PATTERNS", + "UtteranceAndEntitiesMap": [ + { + "utterance": "book flight from {city:fromCity} to {city:toCity}", + "entities": [ + { + "type": "patternAnyEntities", + "entity": "city", + "role": "fromCity" + }, + { + "type": "patternAnyEntities", + "entity": "city", + "role": "toCity" + } + ], + "errorMsgs": [ + + ], + "contextText": "- book flight from {city:fromCity} to {city:toCity}", + "range": { + "Start": { + "Line": 59, + "Character": 0 + }, + "End": { + "Line": 59, + "Character": 51 + } + } + }, + { + "utterance": "[can you] get me a flight from {city:fromCity} to {city:toCity}", + "entities": [ + { + "type": "patternAnyEntities", + "entity": "city", + "role": "fromCity" + }, + { + "type": "patternAnyEntities", + "entity": "city", + "role": "toCity" + } + ], + "errorMsgs": [ + + ], + "contextText": "- [can you] get me a flight from {city:fromCity} to {city:toCity}", + "range": { + "Start": { + "Line": 60, + "Character": 0 + }, + "End": { + "Line": 60, + "Character": 65 + } + } + }, + { + "utterance": "get me a flight to {city:toCity}", + "entities": [ + { + "type": "patternAnyEntities", + "entity": "city", + "role": "toCity" + } + ], + "errorMsgs": [ + + ], + "contextText": "- get me a flight to {city:toCity}", + "range": { + "Start": { + "Line": 61, + "Character": 0 + }, + "End": { + "Line": 61, + "Character": 34 + } + } + }, + { + "utterance": "I need to fly from {city:fromCity}", + "entities": [ + { + "type": "patternAnyEntities", + "entity": "city", + "role": "fromCity" + } + ], + "errorMsgs": [ + + ], + "contextText": "- I need to fly from {city:fromCity}", + "range": { + "Start": { + "Line": 62, + "Character": 0 + }, + "End": { + "Line": 62, + "Character": 36 + } + } + } + ], + "Entities": [ + { + "Errors": [ + + ], + "SectionType": "entitySection", + "Id": "entitySection_city", + "Body": "", + "Name": "city", + "Type": "Seattle=", + "SynonymsOrPhraseList": [ + "Seattle", + "Tacoma", + "SeaTac", + "SEA" + ], + "Range": { + "Start": { + "Line": 64, + "Character": 0 + }, + "End": { + "Line": 68, + "Character": 6 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "entitySection", + "Id": "entitySection_city", + "Body": "", + "Name": "city", + "Type": "Portland=", + "SynonymsOrPhraseList": [ + "Portland", + "PDX" + ], + "Range": { + "Start": { + "Line": 70, + "Character": 0 + }, + "End": { + "Line": 72, + "Character": 6 + } + } + } + ], + "Name": "BookFlight", + "IntentNameLine": "# BookFlight", + "Range": { + "Start": { + "Line": 58, + "Character": 0 + }, + "End": { + "Line": 74, + "Character": 10 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_DeleteAlarm", + "Body": "- delete the {alarmTime} alarm\n\n> PHRASE LIST DEFINITION\n@ phraseList Want =\n - require\n\t- need\n\t- desire\n\t- know\n\n@ phraselist question(interchangeable) =\n - are you\n - you are\n\n\n@ phraselist abc(interchangeable) =\n - are you\n - you are\n\n@ abc disabled\n\n> TIE FEATURES TO SPECIFIC MODEL", + "UtteranceAndEntitiesMap": [ + { + "utterance": "delete the {alarmTime} alarm", + "entities": [ + { + "type": "patternAnyEntities", + "entity": "alarmTime", + "role": "" + } + ], + "errorMsgs": [ + + ], + "contextText": "- delete the {alarmTime} alarm", + "range": { + "Start": { + "Line": 76, + "Character": 0 + }, + "End": { + "Line": 76, + "Character": 30 + } + } + } + ], + "Entities": [ + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_Want", + "Body": "", + "Name": "Want", + "Type": "phraseList", + "ListBody": [ + " - require", + "\t- need", + "\t- desire", + "\t- know" + ], + "Range": { + "Start": { + "Line": 79, + "Character": 0 + }, + "End": { + "Line": 83, + "Character": 8 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_question(interchangeable)", + "Body": "", + "Name": "question(interchangeable)", + "Type": "phraselist", + "ListBody": [ + " - are you", + " - you are" + ], + "Range": { + "Start": { + "Line": 85, + "Character": 0 + }, + "End": { + "Line": 87, + "Character": 14 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_abc(interchangeable)", + "Body": "", + "Name": "abc(interchangeable)", + "Type": "phraselist", + "ListBody": [ + " - are you", + " - you are" + ], + "Range": { + "Start": { + "Line": 90, + "Character": 0 + }, + "End": { + "Line": 92, + "Character": 14 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_abc", + "Body": "", + "Name": "abc", + "Roles": "disabled", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 94, + "Character": 0 + }, + "End": { + "Line": 94, + "Character": 15 + } + } + } + ], + "Name": "DeleteAlarm", + "IntentNameLine": "# DeleteAlarm", + "Range": { + "Start": { + "Line": 75, + "Character": 0 + }, + "End": { + "Line": 96, + "Character": 32 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_getUserProfile", + "Body": "- my name is {@userName = vishwac}\n\n> phrase list definition\n@ phraselist PLCity(interchangeable) =\n - seattle\n - space needle\n - SEATAC\n - SEA\n> phrase list as feature to intent (also applicable to entities)\n@ intent getUserProfileIntent usesFeature PLCity\n\n> phrase list as a feature to an ml entity.\n@ ml myCity usesFeature PLCity\n\n@ regex regexZipcode = /[0-9]{5}/\n\n> phrase list as feature to n-depth entity with phrase list as a feature\n@ ml address2 fromAddress2, toAddress2\n@ address2 =\n - @ number 'door number'\n - @ ml streetName\n - @ ml location\n - @ ml city usesFeature PLCity\n - @ regexZipcode zipcode \n\n\n> ADD ENTITY OR INTENT AS FEATURE\n> entity definition - @ []\n\n@ prebuilt personName\n\n> entity definition with roles\n@ ml userN hasRoles fistN, lastN\n\n> add entity as a feature to another entity\n@ userN usesFeature personName\n\n> add entity as feature to an intent\n@ intent getUserNameIntent usesFeature personName\n\n> Intent definition", + "UtteranceAndEntitiesMap": [ + { + "utterance": "my name is vishwac", + "entities": [ + { + "type": "entities", + "entity": "@userName", + "role": "", + "startPos": 11, + "endPos": 17 + } + ], + "errorMsgs": [ + + ], + "contextText": "- my name is {@userName = vishwac}", + "range": { + "Start": { + "Line": 98, + "Character": 0 + }, + "End": { + "Line": 98, + "Character": 34 + } + } + } + ], + "Entities": [ + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_PLCity(interchangeable)", + "Body": "", + "Name": "PLCity(interchangeable)", + "Type": "phraselist", + "ListBody": [ + " - seattle", + " - space needle", + " - SEATAC", + " - SEA" + ], + "Range": { + "Start": { + "Line": 101, + "Character": 0 + }, + "End": { + "Line": 105, + "Character": 10 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_getUserProfileIntent", + "Body": "", + "Name": "getUserProfileIntent", + "Type": "intent", + "Features": "PLCity", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 107, + "Character": 0 + }, + "End": { + "Line": 107, + "Character": 49 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_myCity", + "Body": "", + "Name": "myCity", + "Type": "ml", + "Features": "PLCity", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 110, + "Character": 0 + }, + "End": { + "Line": 110, + "Character": 31 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_regexZipcode", + "Body": "", + "Name": "regexZipcode", + "Type": "regex", + "RegexDefinition": "/[0-9]{5}/", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 112, + "Character": 0 + }, + "End": { + "Line": 112, + "Character": 34 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_address2", + "Body": "", + "Name": "address2", + "Type": "ml", + "Roles": "fromAddress2, toAddress2", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 115, + "Character": 0 + }, + "End": { + "Line": 115, + "Character": 39 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_address2", + "Body": "", + "Name": "address2", + "ListBody": [ + " - @ number 'door number'", + " - @ ml streetName", + " - @ ml location", + " - @ ml city usesFeature PLCity", + " - @ regexZipcode zipcode " + ], + "Range": { + "Start": { + "Line": 116, + "Character": 0 + }, + "End": { + "Line": 121, + "Character": 34 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_personName", + "Body": "", + "Name": "personName", + "Type": "prebuilt", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 127, + "Character": 0 + }, + "End": { + "Line": 127, + "Character": 22 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_userN", + "Body": "", + "Name": "userN", + "Type": "ml", + "Roles": "fistN, lastN", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 130, + "Character": 0 + }, + "End": { + "Line": 130, + "Character": 33 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_userN", + "Body": "", + "Name": "userN", + "Features": "personName", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 133, + "Character": 0 + }, + "End": { + "Line": 133, + "Character": 31 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_getUserNameIntent", + "Body": "", + "Name": "getUserNameIntent", + "Type": "intent", + "Features": "personName", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 136, + "Character": 0 + }, + "End": { + "Line": 136, + "Character": 50 + } + } + } + ], + "Name": "getUserProfile", + "IntentNameLine": "# getUserProfile", + "Range": { + "Start": { + "Line": 97, + "Character": 0 + }, + "End": { + "Line": 138, + "Character": 19 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_getUserNameIntent", + "Body": "- utterances\n\n> multiple entities as a feature to a model\n@ intent getUserNameIntent usesFeature personName\n\n> intent as a feature to another intent\n@ intent getUserProfileI usesFeature getUserNameIntent\n", + "UtteranceAndEntitiesMap": [ + { + "utterance": "utterances", + "entities": [ + + ], + "errorMsgs": [ + + ], + "contextText": "- utterances", + "range": { + "Start": { + "Line": 140, + "Character": 0 + }, + "End": { + "Line": 140, + "Character": 12 + } + } + } + ], + "Entities": [ + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_getUserNameIntent", + "Body": "", + "Name": "getUserNameIntent", + "Type": "intent", + "Features": "personName", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 143, + "Character": 0 + }, + "End": { + "Line": 143, + "Character": 50 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_getUserProfileI", + "Body": "", + "Name": "getUserProfileI", + "Type": "intent", + "Features": "getUserNameIntent", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 146, + "Character": 0 + }, + "End": { + "Line": 146, + "Character": 55 + } + } + } + ], + "Name": "getUserNameIntent", + "IntentNameLine": "# getUserNameIntent", + "Range": { + "Start": { + "Line": 139, + "Character": 0 + }, + "End": { + "Line": 147, + "Character": 0 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_getUserProfileI", + "Body": "- utterances\n\n> MACHINE_LEARNED ENTITY WITH CHILDREN\n@ list listCity\n@ prebuilt number\n@ prebuilt geographyV2\n@ regex regexZipcode = /[0-9]{5}/\n@ ml address hasRoles fromAddress, toAddress\n@ address =\n - @ number 'door number'\n - @ ml streetName\n - @ ml location usesFeature geographyV2\n - @ listCity city\n - @ regexZipcode zipcode\n\n\n", + "UtteranceAndEntitiesMap": [ + { + "utterance": "utterances", + "entities": [ + + ], + "errorMsgs": [ + + ], + "contextText": "- utterances", + "range": { + "Start": { + "Line": 149, + "Character": 0 + }, + "End": { + "Line": 149, + "Character": 12 + } + } + } + ], + "Entities": [ + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_listCity", + "Body": "", + "Name": "listCity", + "Type": "list", + "SynonymsList": [ + + ], + "Range": { + "Start": { + "Line": 152, + "Character": 0 + }, + "End": { + "Line": 152, + "Character": 16 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_number", + "Body": "", + "Name": "number", + "Type": "prebuilt", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 153, + "Character": 0 + }, + "End": { + "Line": 153, + "Character": 18 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_geographyV2", + "Body": "", + "Name": "geographyV2", + "Type": "prebuilt", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 154, + "Character": 0 + }, + "End": { + "Line": 154, + "Character": 23 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_regexZipcode", + "Body": "", + "Name": "regexZipcode", + "Type": "regex", + "RegexDefinition": "/[0-9]{5}/", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 155, + "Character": 0 + }, + "End": { + "Line": 155, + "Character": 34 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_address", + "Body": "", + "Name": "address", + "Type": "ml", + "Roles": "fromAddress, toAddress", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 156, + "Character": 0 + }, + "End": { + "Line": 156, + "Character": 45 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_address", + "Body": "", + "Name": "address", + "ListBody": [ + " - @ number 'door number'", + " - @ ml streetName", + " - @ ml location usesFeature geographyV2", + " - @ listCity city", + " - @ regexZipcode zipcode" + ], + "Range": { + "Start": { + "Line": 157, + "Character": 0 + }, + "End": { + "Line": 162, + "Character": 33 + } + } + } + ], + "Name": "getUserProfileI", + "IntentNameLine": "# getUserProfileI", + "Range": { + "Start": { + "Line": 148, + "Character": 0 + }, + "End": { + "Line": 165, + "Character": 0 + } + } + }, + { + "Errors": [ + { + "Message": "no utterances found for intent definition: \"# getUserProfileIntent\"", + "Range": { + "Start": { + "Line": 166, + "Character": 0 + }, + "End": { + "Line": 166, + "Character": 22 + } + }, + "Severity": "WARN" + } + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_getUserProfileIntent", + "Body": "@ prebuilt number\n\n> LABELS", + "UtteranceAndEntitiesMap": [ + + ], + "Entities": [ + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_number", + "Body": "", + "Name": "number", + "Type": "prebuilt", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 167, + "Character": 0 + }, + "End": { + "Line": 167, + "Character": 18 + } + } + } + ], + "Name": "getUserProfileIntent", + "IntentNameLine": "# getUserProfileIntent", + "Range": { + "Start": { + "Line": 166, + "Character": 0 + }, + "End": { + "Line": 169, + "Character": 8 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_getUserP", + "Body": "- my name is vishwac and I'm 36 years old\n - my name is {@userProfile = vishwac and I'm 36 years old}\n - my name is {@fstN = vishwac} and I'm 36 years old\n - my name is vishwac and I'm {@userAge = 36} years old\n- i'm {@userProfile = {@fstN = vishwac}}\n\n@ ml userProfile\n - @personName fstN\n - @personName lstN\n\n@ prebuilt personName\n\n> EXTERNAL REFERENCES", + "UtteranceAndEntitiesMap": [ + { + "utterance": "my name is vishwac and I'm 36 years old", + "entities": [ + + ], + "errorMsgs": [ + + ], + "contextText": "- my name is vishwac and I'm 36 years old", + "range": { + "Start": { + "Line": 171, + "Character": 0 + }, + "End": { + "Line": 171, + "Character": 41 + } + } + }, + { + "utterance": "my name is vishwac and I'm 36 years old", + "entities": [ + { + "type": "entities", + "entity": "@userProfile", + "role": "", + "startPos": 11, + "endPos": 38 + } + ], + "errorMsgs": [ + + ], + "contextText": " - my name is {@userProfile = vishwac and I'm 36 years old}", + "range": { + "Start": { + "Line": 172, + "Character": 0 + }, + "End": { + "Line": 172, + "Character": 62 + } + } + }, + { + "utterance": "my name is vishwac and I'm 36 years old", + "entities": [ + { + "type": "entities", + "entity": "@fstN", + "role": "", + "startPos": 11, + "endPos": 17 + } + ], + "errorMsgs": [ + + ], + "contextText": " - my name is {@fstN = vishwac} and I'm 36 years old", + "range": { + "Start": { + "Line": 173, + "Character": 0 + }, + "End": { + "Line": 173, + "Character": 55 + } + } + }, + { + "utterance": "my name is vishwac and I'm 36 years old", + "entities": [ + { + "type": "entities", + "entity": "@userAge", + "role": "", + "startPos": 27, + "endPos": 28 + } + ], + "errorMsgs": [ + + ], + "contextText": " - my name is vishwac and I'm {@userAge = 36} years old", + "range": { + "Start": { + "Line": 174, + "Character": 0 + }, + "End": { + "Line": 174, + "Character": 58 + } + } + }, + { + "utterance": "i'm vishwac", + "entities": [ + { + "type": "entities", + "entity": "@fstN", + "role": "", + "startPos": 4, + "endPos": 10 + }, + { + "type": "entities", + "entity": "@userProfile", + "role": "", + "startPos": 4, + "endPos": 10 + } + ], + "errorMsgs": [ + + ], + "contextText": "- i'm {@userProfile = {@fstN = vishwac}}", + "range": { + "Start": { + "Line": 175, + "Character": 0 + }, + "End": { + "Line": 175, + "Character": 40 + } + } + } + ], + "Entities": [ + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_userProfile", + "Body": "", + "Name": "userProfile", + "Type": "ml", + "ListBody": [ + " - @personName fstN", + " - @personName lstN" + ], + "Range": { + "Start": { + "Line": 177, + "Character": 0 + }, + "End": { + "Line": 179, + "Character": 23 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "newEntitySection", + "Id": "newEntitySection_personName", + "Body": "", + "Name": "personName", + "Type": "prebuilt", + "ListBody": [ + + ], + "Range": { + "Start": { + "Line": 181, + "Character": 0 + }, + "End": { + "Line": 181, + "Character": 22 + } + } + } + ], + "Name": "getUserP", + "IntentNameLine": "# getUserP", + "Range": { + "Start": { + "Line": 170, + "Character": 0 + }, + "End": { + "Line": 183, + "Character": 21 + } + } + }, + { + "Errors": [ + + ], + "SectionType": "simpleIntentSection", + "Id": "simpleIntentSection_None", + "Body": "- [None uttearnces](./test_help.lu#Help)", + "UtteranceAndEntitiesMap": [ + { + "utterance": "[None uttearnces](./test_help.lu#Help)", + "entities": [ + + ], + "errorMsgs": [ + + ], + "contextText": "- [None uttearnces](./test_help.lu#Help)", + "range": { + "Start": { + "Line": 185, + "Character": 0 + }, + "End": { + "Line": 185, + "Character": 40 + } + }, + "references": { + "source": "./test_help.lu#Help" + } + } + ], + "Entities": [ + + ], + "Name": "None", + "IntentNameLine": "# None", + "Range": { + "Start": { + "Line": 184, + "Character": 0 + }, + "End": { + "Line": 185, + "Character": 40 + } + } + } + ], + "content": "> This is a comment and will be ignored.\n> INTENT\n# Greeting\n- hi\n- hello\n\n> ML ENTITY. Without an explicit entity definition, 'userName' defaults to 'ml' entity type.\n# getUserName\n- my name is {username=vishwac}\n\n> PREBUILT ENTITIES\n@ prebuilt number numOfGuests, age\n\n> COMPOSITE ENTITIES\n# setThermostat\n> This utterance labels ‘thermostat to 72’ as composite entity deviceTemperature\n - Please set {deviceTemperature = thermostat to 72}\n> This is an example utterance that labels ‘owen’ as customDevice (ml entity) and wraps ‘owen to 72’ with the ‘deviceTemperature’ composite entity\n - Set {deviceTemperature = {customDevice = owen} to 72}\n\n> Define a composite entity ‘deviceTemperature’ that has device (list entity), customDevice (ml entity), temperature (pre-built entity) as children\n\n@ composite deviceTemperature = [device, customDevice, temperature]\n\n@ list device =\n\t- thermostat :\n\t\t- Thermostat\n\t\t- Heater\n\t\t- AC\n\t\t- Air conditioner\n\t- refrigerator :\n\t\t- Fridge\n \t- Cooler\n\n@ ml customDevice\n\n@ prebuilt temperature\n\n> REGULAR EXPRESSION ENTITY\n> from, to are roles to hrf-number.\n@ regex hrf-number from, to = /hrf-[0-9]{6}/\n\n> ROLES\n\n# AskForUserName\n- {userName:firstName=vishwac} {userName:lastName=kannan}\n- I'm {userName:firstName=vishwac}\n- my first name is {userName:firstName=vishwac}\n- {userName=vishwac} is my name\n\n> In patterns, you can use roles using the {\\:\\} notation\n# getUserNameRoles\n- call me {name:userName}\n- I'm {name:userName}\n- my name is {name:userName}\n\n> roles can be specified for list entity types as well - in this case fromCity and toCity are added as roles to the 'city' list entity defined further below\n# BookFlight\n- book flight from {city:fromCity} to {city:toCity}\n- [can you] get me a flight from {city:fromCity} to {city:toCity}\n- get me a flight to {city:toCity}\n- I need to fly from {city:fromCity}\n\n$city:Seattle=\n- Seattle\n- Tacoma\n- SeaTac\n- SEA\n\n$city:Portland=\n- Portland\n- PDX\n\n> PATTERNS\n# DeleteAlarm\n- delete the {alarmTime} alarm\n\n> PHRASE LIST DEFINITION\n@ phraseList Want =\n - require\n\t- need\n\t- desire\n\t- know\n\n@ phraselist question(interchangeable) =\n - are you\n - you are\n\n\n@ phraselist abc(interchangeable) =\n - are you\n - you are\n\n@ abc disabled\n\n> TIE FEATURES TO SPECIFIC MODEL\n# getUserProfile\n- my name is {@userName = vishwac}\n\n> phrase list definition\n@ phraselist PLCity(interchangeable) =\n - seattle\n - space needle\n - SEATAC\n - SEA\n> phrase list as feature to intent (also applicable to entities)\n@ intent getUserProfileIntent usesFeature PLCity\n\n> phrase list as a feature to an ml entity.\n@ ml myCity usesFeature PLCity\n\n@ regex regexZipcode = /[0-9]{5}/\n\n> phrase list as feature to n-depth entity with phrase list as a feature\n@ ml address2 fromAddress2, toAddress2\n@ address2 =\n - @ number 'door number'\n - @ ml streetName\n - @ ml location\n - @ ml city usesFeature PLCity\n - @ regexZipcode zipcode \n\n\n> ADD ENTITY OR INTENT AS FEATURE\n> entity definition - @ []\n\n@ prebuilt personName\n\n> entity definition with roles\n@ ml userN hasRoles fistN, lastN\n\n> add entity as a feature to another entity\n@ userN usesFeature personName\n\n> add entity as feature to an intent\n@ intent getUserNameIntent usesFeature personName\n\n> Intent definition\n# getUserNameIntent\n- utterances\n\n> multiple entities as a feature to a model\n@ intent getUserNameIntent usesFeature personName\n\n> intent as a feature to another intent\n@ intent getUserProfileI usesFeature getUserNameIntent\n\n# getUserProfileI\n- utterances\n\n> MACHINE_LEARNED ENTITY WITH CHILDREN\n@ list listCity\n@ prebuilt number\n@ prebuilt geographyV2\n@ regex regexZipcode = /[0-9]{5}/\n@ ml address hasRoles fromAddress, toAddress\n@ address =\n - @ number 'door number'\n - @ ml streetName\n - @ ml location usesFeature geographyV2\n - @ listCity city\n - @ regexZipcode zipcode\n\n\n\n# getUserProfileIntent\n@ prebuilt number\n\n> LABELS\n# getUserP\n- my name is vishwac and I'm 36 years old\n - my name is {@userProfile = vishwac and I'm 36 years old}\n - my name is {@fstN = vishwac} and I'm 36 years old\n - my name is vishwac and I'm {@userAge = 36} years old\n- i'm {@userProfile = {@fstN = vishwac}}\n\n@ ml userProfile\n - @personName fstN\n - @personName lstN\n\n@ prebuilt personName\n\n> EXTERNAL REFERENCES\n# None\n- [None uttearnces](./test_help.lu#Help)", + "errors": [ + { + "Message": "no utterances found for intent definition: \"# getUserProfileIntent\"", + "Range": { + "Start": { + "Line": 166, + "Character": 0 + }, + "End": { + "Line": 166, + "Character": 22 + } + }, + "Severity": "WARN" + } + ] +} \ No newline at end of file diff --git a/packages/lu/test/fixtures/lu_raw_parse/lu_sections.lu b/packages/lu/test/fixtures/lu_raw_parse/lu_sections.lu new file mode 100644 index 000000000..df0c9d0b3 --- /dev/null +++ b/packages/lu/test/fixtures/lu_raw_parse/lu_sections.lu @@ -0,0 +1,185 @@ +> This is a comment and will be ignored. +> INTENT +# Greeting +- hi +- hello + +> ML ENTITY. Without an explicit entity definition, 'userName' defaults to 'ml' entity type. +# getUserName +- my name is {username=vishwac} + +> PREBUILT ENTITIES +@ prebuilt number numOfGuests, age + +> COMPOSITE ENTITIES +# setThermostat +> This utterance labels ‘thermostat to 72’ as composite entity deviceTemperature + - Please set {deviceTemperature = thermostat to 72} +> This is an example utterance that labels ‘owen’ as customDevice (ml entity) and wraps ‘owen to 72’ with the ‘deviceTemperature’ composite entity + - Set {deviceTemperature = {customDevice = owen} to 72} + +> Define a composite entity ‘deviceTemperature’ that has device (list entity), customDevice (ml entity), temperature (pre-built entity) as children + +@ composite deviceTemperature = [device, customDevice, temperature] + +@ list device = + - thermostat : + - Thermostat + - Heater + - AC + - Air conditioner + - refrigerator : + - Fridge + - Cooler + +@ ml customDevice + +@ prebuilt temperature + +> REGULAR EXPRESSION ENTITY +> from, to are roles to hrf-number. +@ regex hrf-number from, to = /hrf-[0-9]{6}/ + +> ROLES + +# AskForUserName +- {userName:firstName=vishwac} {userName:lastName=kannan} +- I'm {userName:firstName=vishwac} +- my first name is {userName:firstName=vishwac} +- {userName=vishwac} is my name + +> In patterns, you can use roles using the {\:\} notation +# getUserNameRoles +- call me {name:userName} +- I'm {name:userName} +- my name is {name:userName} + +> roles can be specified for list entity types as well - in this case fromCity and toCity are added as roles to the 'city' list entity defined further below +# BookFlight +- book flight from {city:fromCity} to {city:toCity} +- [can you] get me a flight from {city:fromCity} to {city:toCity} +- get me a flight to {city:toCity} +- I need to fly from {city:fromCity} + +$city:Seattle= +- Seattle +- Tacoma +- SeaTac +- SEA + +$city:Portland= +- Portland +- PDX + +> PATTERNS +# DeleteAlarm +- delete the {alarmTime} alarm + +> PHRASE LIST DEFINITION +@ phraseList Want = + - require + - need + - desire + - know + +@ phraselist question(interchangeable) = + - are you + - you are + + +@ phraselist abc(interchangeable) = + - are you + - you are + +@ abc disabled + +> TIE FEATURES TO SPECIFIC MODEL +# getUserProfile +- my name is {@userName = vishwac} + +> phrase list definition +@ phraselist PLCity(interchangeable) = + - seattle + - space needle + - SEATAC + - SEA +> phrase list as feature to intent (also applicable to entities) +@ intent getUserProfileIntent usesFeature PLCity + +> phrase list as a feature to an ml entity. +@ ml myCity usesFeature PLCity + +@ regex regexZipcode = /[0-9]{5}/ + +> phrase list as feature to n-depth entity with phrase list as a feature +@ ml address2 fromAddress2, toAddress2 +@ address2 = + - @ number 'door number' + - @ ml streetName + - @ ml location + - @ ml city usesFeature PLCity + - @ regexZipcode zipcode + + +> ADD ENTITY OR INTENT AS FEATURE +> entity definition - @ [] + +@ prebuilt personName + +> entity definition with roles +@ ml userN hasRoles fistN, lastN + +> add entity as a feature to another entity +@ userN usesFeature personName + +> add entity as feature to an intent +@ intent getUserNameIntent usesFeature personName + +> Intent definition +# getUserNameIntent +- utterances + +> multiple entities as a feature to a model +@ intent getUserNameIntent usesFeature personName + +> intent as a feature to another intent +@ intent getUserProfileI usesFeature getUserNameIntent + +# getUserProfileI +- utterances + +> MACHINE_LEARNED ENTITY WITH CHILDREN +@ list listCity +@ prebuilt number +@ prebuilt geographyV2 +@ regex regexZipcode = /[0-9]{5}/ +@ ml address hasRoles fromAddress, toAddress +@ address = + - @ number 'door number' + - @ ml streetName + - @ ml location usesFeature geographyV2 + - @ listCity city + - @ regexZipcode zipcode + + + +# getUserProfileIntent +@ prebuilt number + +> LABELS +# getUserP +- my name is vishwac and I'm 36 years old + - my name is {@userProfile = vishwac and I'm 36 years old} + - my name is {@fstN = vishwac} and I'm 36 years old + - my name is vishwac and I'm {@userAge = 36} years old +- i'm {@userProfile = {@fstN = vishwac}} + +@ ml userProfile + - @personName fstN + - @personName lstN + +@ prebuilt personName + +> EXTERNAL REFERENCES +# None +- [None uttearnces](./test_help.lu#Help) diff --git a/packages/lu/test/parser/lu/lu.test.js b/packages/lu/test/parser/lu/lu.test.js index 934ad4bef..07814e0a0 100644 --- a/packages/lu/test/parser/lu/lu.test.js +++ b/packages/lu/test/parser/lu/lu.test.js @@ -28,4 +28,17 @@ describe('LU instance', function() { await luInstance.translate('xxxxxxx', 'fr', true, false) compareLuFiles(luInstance.content, result) }); +}); + +describe('LU instance raw parsing', function() { + const response = require('./../../fixtures/lu_raw_parse/lu_sections.json') + + it('Parse LU instance content to raw parse', async () => { + let luContent = await fs.readFile(path.join(__dirname, './../../fixtures/lu_raw_parse/lu_sections.lu')) + const luInstance = new LU(luContent.toString()) + let result = luInstance.parse() + let sanitizedResult = JSON.stringify(result); + let sanitizedResponse = JSON.stringify(response); + compareLuFiles(JSON.parse(sanitizedResult), JSON.parse(sanitizedResponse)); + }); }); \ No newline at end of file